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 T rangeIntersection(T)(T a0, T a1, T b0, T b1)
47 {
48 	import std.algorithm.comparison : min, max;
49 
50 	auto x0 = max(a0, b0);
51 	auto x1 = min(a1, b1);
52 	return x0 < x1 ? x1 - x0 : 0;
53 }
54 
55 unittest
56 {
57 	assert(rangeIntersection(0, 2, 1, 3) == 1);
58 	assert(rangeIntersection(0, 1, 2, 3) == 0);
59 }
60 
61 template unary(char op)
62 {
63 	T unary(T)(T value)
64 	{
65 		// Silence DMD 2.078.0 warning about integer promotion rules
66 		// https://dlang.org/changelog/2.078.0.html#fix16997
67 		static if ((op == '-' || op == '+' || op == '~') && is(T : int))
68 			alias CastT = int;
69 		else
70 			alias CastT = T;
71 		return mixin(`cast(T)` ~ op ~ `cast(CastT)value`);
72 	}
73 }
74 
75 /// Like the ~ operator, but without int-promotion.
76 alias flipBits = unary!'~';
77 
78 unittest
79 {
80 	ubyte b = 0x80;
81 	auto b2 = b.flipBits;
82 	assert(b2 == 0x7F);
83 	static assert(is(typeof(b2) == ubyte));
84 }
85 
86 T swapBytes(T)(T b)
87 {
88 	import core.bitop : bswap;
89 	static if (b.sizeof == 1)
90 		return b;
91 	else
92 	static if (b.sizeof == 2)
93 		return cast(T)((b >> 8) | (b << 8));
94 	else
95 	static if (b.sizeof == 4)
96 		return bswap(b);
97 	else
98 		static assert(false, "Don't know how to bswap " ~ T.stringof);
99 }
100 
101 bool isPowerOfTwo(T)(T x) { return (x & (x-1)) == 0; }
102 T roundUpToPowerOfTwo(T)(T x) { return nextPowerOfTwo(x-1); }
103 T nextPowerOfTwo(T)(T x)
104 {
105 	x |= x >>  1;
106 	x |= x >>  2;
107 	x |= x >>  4;
108 	static if (T.sizeof > 1)
109 		x |= x >>  8;
110 	static if (T.sizeof > 2)
111 		x |= x >> 16;
112 	static if (T.sizeof > 4)
113 		x |= x >> 32;
114 	return x + 1;
115 }
116 
117 /// Integer log2.
118 ubyte ilog2(T)(T n)
119 {
120 	ubyte result = 0;
121 	while (n >>= 1)
122 		result++;
123 	return result;
124 }
125 
126 unittest
127 {
128 	assert(ilog2(0) == 0);
129 	assert(ilog2(1) == 0);
130 	assert(ilog2(2) == 1);
131 	assert(ilog2(3) == 1);
132 	assert(ilog2(4) == 2);
133 }
134 
135 /// Returns the number of bits needed to
136 /// store a number up to n (inclusive).
137 ubyte bitsFor(T)(T n)
138 {
139 	return cast(ubyte)(ilog2(n)+1);
140 }
141 
142 unittest
143 {
144 	assert(bitsFor( int.max) == 31);
145 	assert(bitsFor(uint.max) == 32);
146 }
147 
148 /// Get the smallest built-in unsigned integer type
149 /// that can store this many bits of data.
150 template TypeForBits(uint bits)
151 {
152 	static if (bits <= 8)
153 		alias TypeForBits = ubyte;
154 	else
155 	static if (bits <= 16)
156 		alias TypeForBits = ushort;
157 	else
158 	static if (bits <= 32)
159 		alias TypeForBits = uint;
160 	else
161 	static if (bits <= 64)
162 		alias TypeForBits = ulong;
163 	else
164 		static assert(false, "No integer type big enough for " ~ bits.stringof ~ " bits");
165 }
166 
167 static assert(is(TypeForBits!7 == ubyte));
168 static assert(is(TypeForBits!8 == ubyte));
169 static assert(is(TypeForBits!9 == ushort));
170 static assert(is(TypeForBits!64 == ulong));
171 static assert(!is(TypeForBits!65));
172 
173 void minimize(T, Args...)(ref T v, Args args)
174 if (is(typeof({ import std.algorithm.comparison : min; v = min(v, args); })))
175 {
176 	import std.algorithm.comparison : min;
177 	v = min(v, args);
178 }
179 
180 void maximize(T, Args...)(ref T v, Args args)
181 if (is(typeof({ import std.algorithm.comparison : max; v = max(v, args); })))
182 {
183 	import std.algorithm.comparison : max;
184 	v = max(v, args);
185 }
186 
187 unittest
188 {
189 	int i = 5;
190 	i.minimize(2); assert(i == 2);
191 	i.minimize(5); assert(i == 2);
192 	i.maximize(5); assert(i == 5);
193 	i.maximize(2); assert(i == 5);
194 }