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 }