1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // UNFINISHED! DO NOT USE!
20 // ////////////////////////////////////////////////////////////////////////// //
21 alias Fixnum
= FixnumN
!(int, 16);
22 enum Fixed(int v
) = Fixnum(v
);
23 enum Fixed(float v
) = Fixnum(cast(double)v
);
24 enum Fixed(double v
) = Fixnum(v
);
27 // ////////////////////////////////////////////////////////////////////////// //
28 struct FixnumN(T
=int, ubyte scl
=16) if ((is(T
== byte) ||
is(T
== int) ||
is(T
== long)) && scl
> 0 && scl
< T
.sizeof
*8-1) {
30 alias Me
= typeof(this);
33 enum mpl
= cast(T
)1<<scale
;
36 static FixnumN
make (T v
) nothrow @safe @nogc { FixnumN fixed
; fixed
.value
= v
; return fixed
; }
39 string
toString () const { import std
.format
: format
; return "%f".format(cast(double)value
/mpl
); }
41 public nothrow @safe @nogc:
42 // min and max represent the smallest and larget possible values respectively
43 enum min
= make(-T
.max
);
44 enum max
= make(T
.max
);
45 static if (is(T
== int) && scl
== 16) {
46 enum One
= make(1<<16);
47 enum Half
= make(1<<15);
48 enum Nan
= make(0x80000000);
49 enum Inf
= make(int.max
);
50 enum PI
= make(0x3243f);
51 enum PId2
= make(0x3243f>>1);
52 enum PIx2
= make(0x3243f<<1);
53 enum Sqrt2
= make(92682);
56 this (T v
) { pragma(inline
, true); value
= v
*mpl
; }
57 this (double v
) { pragma(inline
, true); import core
.math
: rndtol
; value
= cast(T
)rndtol(v
*mpl
); }
59 Me
opUnary(string op
:"++") () pure { pragma(inline
, true); value
+= mpl
; return this; }
60 Me
opUnary(string op
:"--") () pure { pragma(inline
, true); value
-= mpl
; return this; }
62 Me
opUnary(string op
) () const pure { pragma(inline
, true); mixin("return make("~op
~"value);"); }
64 bool opEquals (Me b
) const pure { pragma(inline
, true); return (value
== b
.value
); }
65 bool opEquals (T b
) const pure { pragma(inline
, true); return (value
== b
*mpl
); }
66 bool opEquals (double b
) const pure { pragma(inline
, true); return ((cast(double)value
/mpl
) == b
); }
68 int opCmp (const Me b
) const pure { pragma(inline
, true); return (value
< b
.value ?
-1 : value
> b
.value ?
1 : 0); }
69 int opCmp (const T b
) const pure { pragma(inline
, true); return (value
< b
*mpl ?
-1 : value
> b
*mpl ?
1 : 0); }
70 int opCmp (const double b
) const pure { pragma(inline
, true); return (value
< b
*mpl ?
-1 : value
> b
*mpl ?
1 : 0); }
72 void opAssign (Me v
) pure { pragma(inline
, true); value
= v
.value
; }
73 void opAssign (T v
) pure { pragma(inline
, true); value
= v
*mpl
; }
74 void opAssign (double v
) pure { pragma(inline
, true); value
= cast(T
)(v
*mpl
); }
76 void opOpAssign (string op
) (Me v
) pure if (op
== "+" || op
== "-") { pragma(inline
, true); mixin("value "~op
~"= v.value;"); }
77 void opOpAssign (string op
) (Me v
) pure if (op
== "*" || op
== "/") { pragma(inline
, true); mixin("value = cast(T)(cast(long)value"~op
~"cast(long)v.value/mpl);"); }
79 void opOpAssign (string op
) (T v
) pure if (op
== "+" || op
== "-") { pragma(inline
, true); mixin("value "~op
~"= v*mpl;"); }
80 void opOpAssign (string op
) (T v
) pure if (op
== "*" || op
== "/") { pragma(inline
, true); mixin("value "~op
~"= v;"); }
82 void opOpAssign (string op
) (double v
) pure if (op
== "+" || op
== "-") { pragma(inline
, true); import core
.math
: rndtol
; mixin("value "~op
~"= rndtol(v*mpl);"); }
83 void opOpAssign (string op
) (double v
) pure if (op
== "*" || op
== "/") { pragma(inline
, true); import core
.math
: rndtol
; mixin("value "~op
~"= rndtol(v);"); }
85 T
opCast(DT
) () const pure if (is(DT
== byte) ||
is(DT
== ubyte) ||
is(DT
== short) ||
is(DT
== ushort) ||
is(DT
== int) ||
is(DT
== uint) ||
is(DT
== long) ||
is(DT
== ulong)) { pragma(inline
, true); return cast(DT
)(value
/mpl
); }
86 T
opCast(DT
) () const pure if (is(DT
== float) ||
is(DT
== double)) { pragma(inline
, true); return cast(DT
)((cast(double)value
)/mpl
); }
87 T
opCast(DT
) () const pure if (is(DT
== bool)) { pragma(inline
, true); return value
!= 0; }
90 Me
opBinary(string op
) (Me b
) const pure if (op
== "+" || op
== "-") { pragma(inline
, true); mixin("return make(value"~op
~"b.value);"); }
91 Me
opBinary(string op
) (Me b
) const pure if (op
== "*" || op
== "/") { pragma(inline
, true); mixin("return make(cast(T)(cast(long)value"~op
~"cast(long)b.value/mpl));"); }
93 Me
opBinary(string op
) (T b
) const pure if (op
== "+" || op
== "-" || op
== "%") { pragma(inline
, true); mixin("return make(value"~op
~"(b*mpl));"); }
94 Me
opBinary(string op
) (T b
) const pure if (op
== "*" || op
== "/") { pragma(inline
, true); mixin("return make(value"~op
~"b);"); }
96 Me
opBinaryRight(string op
) (T b
) const pure if (op
== "+" || op
== "-" || op
== "%") { pragma(inline
, true); mixin("return make((b*mpl)"~op
~"value);"); }
97 Me
opBinaryRight(string op
) (T b
) const pure if (op
== "*") { pragma(inline
, true); return make(cast(T
)(b
*value
)); }
98 Me
opBinaryRight(string op
) (T b
) const pure if (op
== "/") { pragma(inline
, true); return make(cast(T
)(cast(long)(b
*mpl
*mpl
)/value
)); }
101 Me
opBinary(string op
) (double b
) const pure if (op
== "+" || op
== "-" || op
== "%") { pragma(inline
, true); import core
.math
: rndtol
; mixin("return make(value"~op
~"cast(T)rndtol(b*mpl));"); }
102 Me
opBinary(string op
) (double b
) const pure if (op
== "*" || op
== "/") { pragma(inline
, true); import core
.math
: rndtol
; mixin("return make(value"~op
~"cast(T)rndtol(b));"); }
104 Me
opBinaryRight(string op
) (double b
) const pure if (op
== "+" || op
== "-" || op
== "%") { pragma(inline
, true); import core
.math
: rndtol
; mixin("return make(cast(T)rndtol(b*mpl)"~op
~"value);"); }
105 Me
opBinaryRight(string op
) (double b
) const pure if (op
== "*") { pragma(inline
, true); import core
.math
: rndtol
; return make(cast(T
)(cast(T
)rndtol(b
)*value
)); }
106 Me
opBinaryRight(string op
) (double b
) const pure if (op
== "/") { pragma(inline
, true); import core
.math
: rndtol
; return make(cast(T
)(rndtol(b
*mpl
*mpl
)/value
)); }
108 @property Me
abs () const pure { pragma(inline
, true); return make(value
>= 0 ? value
: -value
); }
109 @property int sign () const pure { pragma(inline
, true); return (value
< 0 ?
-1 : value
> 0 ?
1 : 0); }
110 @property isnan () const pure { pragma(inline
, true); return (value
== T
.min
); }
112 static if (is(T
== int) && scl
== 16) {
113 static Me
cos (Me rads
) { pragma(inline
, true); Me res
; calcSinCosF16(rads
.value
, null, &res
.value
); return res
; }
114 static Me
sin (Me rads
) { pragma(inline
, true); Me res
; calcSinCosF16(rads
.value
, &res
.value
, null); return res
; }
115 static void sincos (Me rads
, ref Me s
, ref Me c
) { pragma(inline
, true); calcSinCosF16(rads
.value
, &s
.value
, &c
.value
); }
116 static Me
atan2 (Me y
, Me x
) { pragma(inline
, true); return make(calcAtan2F16(y
.value
, x
.value
)); }
117 static Me
atan (Me x
) { pragma(inline
, true); return make(calcAtanF16(x
.value
)); }
118 static Me
asin (Me x
) { pragma(inline
, true); return make(calcAsinF16(x
.value
)); }
122 // code taken from tbox
123 void calcSinCosF16 (int x
, int* s
, int* c
) @trusted {
124 // |angle| < 90 degrees
126 static void cordicRotation (int* x0
, int* y0
, int z0
) {
130 int x
= *x0
; // fixed30
131 int y
= *y0
; // fixed30
132 int xi
= 0; // fixed30
133 int yi
= 0; // fixed30
134 immutable(int)* patan2i
= tb_fixed16_cordic_atan2i_table
.ptr
;
155 // (x0, y0) = (k, 0), k = 0.607252935 => fixed30
156 int cos
= 0x26dd3b6a; // fixed30
157 int sin
= 0; // fixed30
159 /* scale to 65536 degrees from x radians: x * 65536 / (2 * pi)
182 int quadrant
= ang
>>30;
185 /* quadrant == 2, 3, |angle| < 90
187 * 100 => -100 + 180 => 80
188 * -200 => 200 + 180 => -20
190 if (quadrant
&0x2) ang
= -ang
+0x80000000;
193 cordicRotation(&cos
, &sin
, ang
);
196 if (s
!is null) *s
= sin
>>14; // fixed30->fixed16
199 if (quadrant
&0x2) cos
= -cos
;
200 *c
= cos
>>14; // fixed30->fixed16
204 // |angle| < 90 degrees
205 int cordicVectorAtan2 (int y0
, int x0
) @trusted {
213 immutable(int)* patan2i
= tb_fixed16_cordic_atan2i_table
.ptr
;
231 // slope angle: [-180, 180]
232 // the precision will be pool if x, y is too small
233 int calcAtan2F16 (int y
, int x
) {
234 if (!(x|y
)) return 0;
236 int xs
= tbGetSign(x
);
237 x
= (x
>= 0 ? x
: -x
); //tb_fixed30_abs(x);
239 int z
= cordicVectorAtan2(y
, x
);
240 // for quadrant: 2, 3
242 int zs
= tbGetSign(z
);
244 int pi
= tbSetSign(0x3243f/*TB_FIXED16_PI*/, zs
);
251 // the precision will be pool if x is too large.
252 int calcAtanF16 (int x
) {
254 return cordicVectorAtan2(x
, 1<<16/*TB_FIXED16_ONE*/);
257 int calcAsinF16 (int x
) {
258 // |angle| < 90 degrees
259 static int tb_fixed16_cordic_vector_asin (int m
) @trusted {
263 int x
= 0x18bde0bb; // k = 0.607252935
267 immutable(int)* patan2i
= tb_fixed16_cordic_atan2i_table
.ptr
;
286 int s
= tbGetSign(x
);
287 x
= (x
>= 0 ? x
: -1); //tb_fixed16_abs(x);
288 if (x
>= 1<<16/*TB_FIXED16_ONE*/) return tbSetSign((0x3243f/*TB_FIXED16_PI*/) >> 1, s
);
289 int z
= tb_fixed16_cordic_vector_asin(x
* 0x28be);
290 return tbSetSign(z
, ~s
);
293 // return -1 if x < 0, else return 0
294 int tbGetSign (int x
) pure nothrow @safe @nogc {
295 pragma(inline
, true);
296 int s
= (cast(int)(x
) >> 31);
297 //tb_assert((x < 0 && s == -1) || (x >= 0 && !s));
301 // if s == -1, return -x, else s must be 0, and return x.
302 int tbSetSign (int x
, int s
) pure nothrow @safe @nogc {
303 pragma(inline
, true);
304 //tb_assert(s == 0 || s == -1);
311 if (x == 1<<16/*TB_FIXED16_ONE*/) return 1<<16/*TB_FIXED16_ONE*/;
313 int s = tbGetSign(x);
315 x = (x >= 0 ? x : -x);
317 if (x <= 2) return (s < 0 ? -T.max : T.max); //return tbSetSign(TB_FIXED16_MAX, s);
320 int cl0 = cast(int)tb_bits_cl0_u32_be(x);
323 // compute 1 / x approximation (0.5 <= x < 1.0)
324 // (2.90625 (~2.914) - 2 * x) >> 1
327 // newton-raphson iteration:
328 // x = r * (2 - x * r) = ((r / 2) * (1 - x * r / 2)) * 4
329 r = ((0x10000 - ((x * r) >> 16)) * r) >> 15;
330 r = ((0x10000 - ((x * r) >> 16)) * r) >> (30 - cl0);
332 return tbSetSign(r, s);
337 __gshared
immutable int[30] tb_fixed16_cordic_atan2i_table
= [
338 0x20000000 // 45.000000
339 , 0x12e4051d // 26.565051
340 , 0x9fb385b // 14.036243
341 , 0x51111d4 // 7.125016
342 , 0x28b0d43 // 3.576334
343 , 0x145d7e1 // 1.789911
344 , 0xa2f61e // 0.895174
345 , 0x517c55 // 0.447614
346 , 0x28be53 // 0.223811
347 , 0x145f2e // 0.111906
348 , 0xa2f98 // 0.055953
349 , 0x517cc // 0.027976
350 , 0x28be6 // 0.013988
351 , 0x145f3 // 0.006994
372 // ////////////////////////////////////////////////////////////////////////// //
390 writeln("sin=", Fixnum.sin(Fixnum(0.3)));
391 writeln("dsin=", sin(0.3));
392 writeln("cos=", Fixnum.cos(Fixnum(0.5)));
393 writeln("dcos=", cos(0.5));