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