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