1 /**
2  * Number stuff
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.math;
15 
16 typeof(Ta+Tb+Tc) bound(Ta, Tb, Tc)(Ta a, Tb b, Tc c) { return a<b?b:a>c?c:a; }
17 bool between(T)(T point, T a, T b) { return a <= point && point <= b; } /// Assumes points are sorted (was there a faster way?)
18 auto sqr(T)(T x) { return x*x; }
19 
20 void sort2(T)(ref T x, ref T y) { if (x > y) { T z=x; x=y; y=z; } }
21 
22 T itpl(T, U)(T low, T high, U r, U rLow, U rHigh)
23 {
24 	import std.traits : Signed;
25 	return cast(T)(low + (cast(Signed!T)high-cast(Signed!T)low) * (cast(Signed!U)r - cast(Signed!U)rLow) / (cast(Signed!U)rHigh - cast(Signed!U)rLow));
26 }
27 
28 byte sign(T)(T x) { return x<0 ? -1 : x>0 ? 1 : 0; }
29 
30 int compare(T)(T a, T b)
31 {
32 	return a<b ? -1 : a>b ? 1 : 0;
33 }
34 
35 auto op(string OP, T...)(T args)
36 {
37 	auto result = args[0];
38 	foreach (arg; args[1..$])
39 		mixin("result" ~ OP ~ "=arg;");
40 	return result;
41 }
42 
43 auto sum(T...)(T args) { return op!"+"(args); }
44 auto average(T...)(T args) { return sum(args) / args.length; }
45 
46 template binary(string op)
47 {
48 	auto binary(A, B)(auto ref A a, auto ref B b) { return mixin(`a` ~ op ~ `b`); }
49 }
50 alias eq = binary!"==";
51 alias ne = binary!"!=";
52 alias lt = binary!"<";
53 alias gt = binary!">";
54 alias le = binary!"<=";
55 alias ge = binary!">=";
56 
57 T rangeIntersection(T)(T a0, T a1, T b0, T b1)
58 {
59 	import std.algorithm.comparison : min, max;
60 
61 	auto x0 = max(a0, b0);
62 	auto x1 = min(a1, b1);
63 	return x0 < x1 ? x1 - x0 : 0;
64 }
65 
66 unittest
67 {
68 	assert(rangeIntersection(0, 2, 1, 3) == 1);
69 	assert(rangeIntersection(0, 1, 2, 3) == 0);
70 }
71 
72 template unary(char op)
73 {
74 	T unary(T)(T value)
75 	{
76 		// Silence DMD 2.078.0 warning about integer promotion rules
77 		// https://dlang.org/changelog/2.078.0.html#fix16997
78 		static if ((op == '-' || op == '+' || op == '~') && is(T : int))
79 			alias CastT = int;
80 		else
81 			alias CastT = T;
82 		return mixin(`cast(T)` ~ op ~ `cast(CastT)value`);
83 	}
84 }
85 
86 /// Like the ~ operator, but without int-promotion.
87 alias flipBits = unary!'~';
88 
89 unittest
90 {
91 	ubyte b = 0x80;
92 	auto b2 = b.flipBits;
93 	assert(b2 == 0x7F);
94 	static assert(is(typeof(b2) == ubyte));
95 }
96 
97 T swapBytes(T)(T b)
98 {
99 	import core.bitop : bswap;
100 	static if (b.sizeof == 1)
101 		return b;
102 	else
103 	static if (b.sizeof == 2)
104 		return cast(T)((b >> 8) | (b << 8));
105 	else
106 	static if (b.sizeof == 4)
107 		return bswap(b);
108 	else
109 		static assert(false, "Don't know how to bswap " ~ T.stringof);
110 }
111 
112 bool isPowerOfTwo(T)(T x) { return (x & (x-1)) == 0; }
113 T roundUpToPowerOfTwo(T)(T x) { return nextPowerOfTwo(x-1); }
114 T nextPowerOfTwo(T)(T x)
115 {
116 	x |= x >>  1;
117 	x |= x >>  2;
118 	x |= x >>  4;
119 	static if (T.sizeof > 1)
120 		x |= x >>  8;
121 	static if (T.sizeof > 2)
122 		x |= x >> 16;
123 	static if (T.sizeof > 4)
124 		x |= x >> 32;
125 	return x + 1;
126 }
127 
128 /// Integer log2.
129 ubyte ilog2(T)(T n)
130 {
131 	ubyte result = 0;
132 	while (n >>= 1)
133 		result++;
134 	return result;
135 }
136 
137 unittest
138 {
139 	assert(ilog2(0) == 0);
140 	assert(ilog2(1) == 0);
141 	assert(ilog2(2) == 1);
142 	assert(ilog2(3) == 1);
143 	assert(ilog2(4) == 2);
144 }
145 
146 /// Returns the number of bits needed to
147 /// store a number up to n (inclusive).
148 ubyte bitsFor(T)(T n)
149 {
150 	return cast(ubyte)(ilog2(n)+1);
151 }
152 
153 unittest
154 {
155 	assert(bitsFor( int.max) == 31);
156 	assert(bitsFor(uint.max) == 32);
157 }
158 
159 /// Get the smallest built-in unsigned integer type
160 /// that can store this many bits of data.
161 template TypeForBits(uint bits)
162 {
163 	static if (bits <= 8)
164 		alias TypeForBits = ubyte;
165 	else
166 	static if (bits <= 16)
167 		alias TypeForBits = ushort;
168 	else
169 	static if (bits <= 32)
170 		alias TypeForBits = uint;
171 	else
172 	static if (bits <= 64)
173 		alias TypeForBits = ulong;
174 	else
175 		static assert(false, "No integer type big enough for " ~ bits.stringof ~ " bits");
176 }
177 
178 static assert(is(TypeForBits!7 == ubyte));
179 static assert(is(TypeForBits!8 == ubyte));
180 static assert(is(TypeForBits!9 == ushort));
181 static assert(is(TypeForBits!64 == ulong));
182 static assert(!is(TypeForBits!65));
183 
184 void minimize(T, Args...)(ref T v, Args args)
185 if (is(typeof({ import std.algorithm.comparison : min; v = min(v, args); })))
186 {
187 	import std.algorithm.comparison : min;
188 	v = min(v, args);
189 }
190 
191 void maximize(T, Args...)(ref T v, Args args)
192 if (is(typeof({ import std.algorithm.comparison : max; v = max(v, args); })))
193 {
194 	import std.algorithm.comparison : max;
195 	v = max(v, args);
196 }
197 
198 unittest
199 {
200 	int i = 5;
201 	i.minimize(2); assert(i == 2);
202 	i.minimize(5); assert(i == 2);
203 	i.maximize(5); assert(i == 5);
204 	i.maximize(2); assert(i == 5);
205 }