1 module des.math.basic.mathstruct; 2 3 import std..string; 4 import std.array; 5 import std.traits; 6 7 import des.math.util; 8 import des.math.basic.traits; 9 10 /// space 11 private enum SEP = " "; 12 13 /// 14 @property string BasicMathOp( string fields_str )() 15 if( isArrayAccessString( fields_str, SEP, true ) ) 16 { 17 auto fields = fields_str.split(SEP); 18 string bmctorname = "__basic_math_ctor"; 19 return format(q{ 20 import std.traits; 21 alias Unqual!(typeof(this)) self; 22 23 %1$s 24 %2$s 25 }, 26 basicMathCtor( fields, bmctorname ), 27 staticAsserts( fields ) 28 ) ~ format(q{ 29 30 auto opAdd( in self b ) const { return %1$s( %2$s ); } 31 auto opSub( in self b ) const { return %1$s( %3$s ); } 32 auto opMul( double b ) const { return %1$s( %4$s ); } 33 auto opDiv( double b ) const { return %1$s( %5$s ); } 34 35 auto opOpAssign(string op)( in self b ) 36 { mixin( "return this = this " ~ op ~ " b;" ); } 37 38 auto opOpAssign(string op)( double b ) 39 { mixin( "return this = this " ~ op ~ " b;" ); } 40 }, 41 bmctorname, 42 opSelf( fields, "+" ), 43 opSelf( fields, "-" ), 44 opEach( fields, "*" ), 45 opEach( fields, "/" ) 46 ); 47 } 48 49 /// 50 unittest 51 { 52 static struct Val 53 { 54 float v1 = 0; 55 double v2 = 0; 56 mixin( BasicMathOp!"v1 v2" ); 57 } 58 59 static assert( isAssignable!(Unqual!Val,Unqual!Val) ); 60 static assert( is( typeof(Val.init + Val.init) == Val ) ); 61 static assert( is( typeof(Val.init - Val.init) == Val ) ); 62 static assert( is( typeof( cast(Val)(Val.init * 0.5) ) ) ); 63 static assert( is( typeof( cast(Val)(Val.init / 0.5) ) ) ); 64 65 static assert( hasBasicMathOp!Val ); 66 67 auto p1 = Val( 1, 2 ); 68 auto p2 = Val( 2, 3 ); 69 70 assert( p1 + p2 == Val(3,5) ); 71 assert( p2 - p1 == Val(1,1) ); 72 assert( p1 * 3 == Val(3,6) ); 73 assert( p1 / 2 == Val(0.5,1) ); 74 75 static struct Comp 76 { 77 string str; 78 float val; 79 float time = 0; 80 mixin( BasicMathOp!"val" ); 81 } 82 83 static assert( hasBasicMathOp!Comp ); 84 85 auto c1 = Comp( "ololo", 10, 1.3 ); 86 auto c2 = Comp( "valav", 5, .8 ); 87 88 assert( c1 + c2 == Comp("ololo", 15, 1.3) ); 89 } 90 91 /// 92 unittest 93 { 94 static struct Val 95 { 96 float v1 = 0; 97 double v2 = 0; 98 mixin( BasicMathOp!"v1 v2" ); 99 } 100 101 auto p1 = Val( 1, 2 ); 102 auto p2 = Val( 2, 3 ); 103 104 auto p3 = p1 + p2; 105 p1 += p2; 106 assert( p1 == p3 ); 107 } 108 109 /// 110 unittest 111 { 112 static struct Vec 113 { 114 double x = 0, y = 0; 115 mixin( BasicMathOp!"x y" ); 116 } 117 118 static assert( hasBasicMathOp!Vec ); 119 120 static struct Point 121 { 122 Vec pos, vel; 123 this( in Vec p, in Vec v ) 124 { 125 pos = p; 126 vel = v; 127 } 128 mixin( BasicMathOp!"pos vel" ); 129 } 130 131 static assert( hasBasicMathOp!Vec ); 132 } 133 134 unittest 135 { 136 import des.math.linear.vector; 137 static assert( hasBasicMathOp!dvec3 ); 138 static assert( hasBasicMathOp!vec3 ); 139 140 static struct Point 141 { 142 vec3 pos, vel; 143 mixin( BasicMathOp!"pos vel" ); 144 } 145 146 static assert( hasBasicMathOp!Point ); 147 } 148 149 /// 150 unittest 151 { 152 static struct Vec { double x=0, y=0; } 153 static assert( !hasBasicMathOp!Vec ); 154 static struct Point 155 { 156 Vec pos, vel; 157 string str; 158 float val; 159 mixin( BasicMathOp!"pos.x pos.y vel.x vel.y val" ); 160 } 161 static assert( hasBasicMathOp!Point ); 162 auto a = Point( Vec(1,2), Vec(2,3), "hello", 3 ); 163 assert( a + a == Point( Vec(2,4), Vec(4,6), "hello", 6 ) ); 164 assert( a * 2 == Point( Vec(2,4), Vec(4,6), "hello", 6 ) ); 165 } 166 167 private 168 { 169 string opSelf( string[] fields, string op, string b="b" ) 170 { 171 string[] rb; 172 foreach( f; fields ) 173 rb ~= format( "%1$s %2$s %3$s.%1$s", f, op, b ); 174 return rb.join(", "); 175 } 176 177 unittest 178 { 179 assert( opSelf( "pnt rot".split, "+", "vv" ), "pnt + vv.pnt, rot + vv.rot" ); 180 assert( opSelf( "a b c".split, "*", "x" ), "a * x.a, b * x.b, c * x.c" ); 181 } 182 183 string opEach( string[] fields, string op, string b="b" ) 184 { 185 string[] rb; 186 foreach( f; fields ) 187 rb ~= format( "%1$s %2$s %3$s", f, op, b ); 188 return rb.join(","); 189 } 190 191 unittest 192 { 193 assert( opEach( "pnt rot".split, "+", "vv" ), "pnt + vv, rot + vv" ); 194 assert( opEach( "a b c".split, "*", "x" ), "a * x, b * x, c * x" ); 195 } 196 197 string basicMathCtor( string[] fields, string name ) 198 { 199 string[] args; 200 string cbody = "auto ret = cast(self)(this); "; 201 foreach( field; fields ) 202 { 203 auto arg = argName( field ); 204 args ~= format( "in typeof(%s) %s", field, arg ); 205 cbody ~= format( "ret.%1$s = %2$s; ", field, arg ); 206 } 207 cbody ~= "return ret;"; 208 return format( "self %s( %s ) const { %s }", name, args.join(", "), cbody ); 209 } 210 211 unittest 212 { 213 assert( basicMathCtor( ["p", "v"], "b" ) == 214 "self b( in typeof(p) arg_p, in typeof(v) arg_v ) const { auto ret = cast(self)(this); ret.p = arg_p; ret.v = arg_v; return ret; }" ); 215 } 216 217 string argName( string f ) pure { return format( "arg_%s", f.split(".").join("_") ); } 218 219 unittest 220 { 221 assert( argName( "pos" ) == "arg_pos" ); 222 assert( argName( "Pos" ) == "arg_Pos" ); 223 assert( argName( "Pos.x" ) == "arg_Pos_x" ); 224 assert( argName( "P.s.x" ) == "arg_P_s_x" ); 225 } 226 227 string staticAsserts( string[] fields ) 228 { 229 string ret; 230 foreach( f; fields ) 231 ret ~= format(`static assert( hasBasicMathOp!(typeof(%1$s)), "member '%1$s' hasn't basic math ops" );`, f ); 232 return ret; 233 } 234 }