1 /** Additions to $(STDMODULE _math).
2 
3 Copyright: Denis Shelomovskij 2011-2012
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Denis Shelomovskij
8 */
9 module unstd.math;
10 
11 
12 public import std.math;
13 
14 import core.bitop: bsr;
15 
16 
17 @safe pure nothrow @nogc:
18 
19 
20 /**
21 Returns $(D true) iff $(D n) is a power of 2.
22 
23 Preconditions:
24 $(D n) must be non-zero.
25 */
26 bool isPowerOf2(in size_t n)
27 in { assert(n > 0); }
28 body { return !((n - 1) & n); }
29 
30 unittest
31 {
32 	static assert( isPowerOf2(1));
33 	static assert( isPowerOf2(2));
34 	static assert(!isPowerOf2(3));
35 	static assert( isPowerOf2(4));
36 	static assert(!isPowerOf2(5));
37 }
38 
39 
40 /**
41 Returns largest power of 2 which <= $(D n).
42 
43 Preconditions:
44 $(D n) must be non-zero.
45 */
46 size_t roundDownToPowerOf2(in size_t n)
47 in { assert(n > 0); }
48 body { return 1 << bsr(n); }
49 
50 unittest
51 {
52 	alias down = roundDownToPowerOf2;
53 	static assert(!__traits(compiles, { enum e = down(0); }));
54 	static assert(down(1) == 1);
55 	static assert(down(2) == 2 && down(3) == 2);
56 	static assert(down(4) == 4 && down(5) == 4);
57 	static assert(down(6) == 4 && down(7) == 4);
58 	static assert(down(8) == 8 && down(9) == 8);
59 }
60 
61 
62 /**
63 Returns smallest power of 2 which >= $(D n).
64 
65 Preconditions:
66 $(D n) must be non-zero.
67 */
68 size_t roundUpToPowerOf2(in size_t n)
69 in { assert(n > 0); }
70 body { return 1 << (bsr(n) + !isPowerOf2(n)); }
71 
72 unittest
73 {
74 	alias up = roundUpToPowerOf2;
75 	static assert(!__traits(compiles, { enum e = up(0); }));
76 	static assert(up(1) == 1);
77 	static assert(up(2) == 2);
78 	static assert(up(3) == 4 && up(4) == 4);
79 	static assert(up(5) == 8 && up(6) == 8);
80 	static assert(up(7) == 8 && up(8) == 8);
81 }
82 
83 
84 /**
85 Returns the base-2 logarithm of largest power of 2 which <= $(D n).
86 
87 Preconditions:
88 $(D n) must be non-zero.
89 */
90 ubyte log2RoundedDown(in size_t n)
91 in { assert(n > 0); }
92 body { return cast(ubyte) bsr(n); }
93 
94 unittest
95 {
96 	static assert(log2RoundedDown(1) == 0);
97 	static assert(log2RoundedDown(1023) == 9);
98 	static assert(log2RoundedDown(1024) == 10);
99 	static assert(log2RoundedDown(1025) == 10);
100 }
101 
102 
103 /**
104 Returns the base-2 logarithm of smallest power of 2 which >= $(D n).
105 
106 Preconditions:
107 $(D n) must be non-zero.
108 */
109 ubyte log2RoundedUp(in size_t n)
110 in { assert(n > 0); }
111 body { return cast(ubyte) (bsr(n) + !isPowerOf2(n)); }
112 
113 unittest
114 {
115 	static assert(log2RoundedUp(1) == 0);
116 	static assert(log2RoundedUp(1023) == 10);
117 	static assert(log2RoundedUp(1024) == 10);
118 	static assert(log2RoundedUp(1025) == 11);
119 }
120 
121 
122 /**
123 Aligns $(D n) up or down.
124 
125 Preconditions:
126 $(D alignment) must be power of 2.
127 */
128 size_t alignDown(in size_t alignment, in size_t n)
129 in { assert(isPowerOf2(alignment)); }
130 body
131 {
132 	return n & ~(alignment - 1); // alignment - 1: 0b11, 0b111, ...
133 }
134 
135 /// ditto
136 size_t alignDown(size_t alignment)(in size_t n) if(isPowerOf2(alignment))
137 { return .alignDown(alignment, n); }
138 
139 /// ditto
140 size_t alignUp(in size_t alignment, in size_t n)
141 in { assert(isPowerOf2(alignment)); }
142 body
143 {
144 	return alignDown(alignment, n + alignment - 1);	
145 }
146 
147 /// ditto
148 size_t alignUp(size_t alignment)(in size_t n) if(isPowerOf2(alignment))
149 { return .alignUp(alignment, n); }
150 
151 /**
152 Aligns $(D n) up or down.
153 
154 Preconditions:
155 $(D alignment) must be power of 2.
156 */
157 bool isAligned(in size_t alignment, in size_t n)
158 in { assert(isPowerOf2(alignment)); }
159 body
160 {
161 	return !(n & (alignment - 1)); // alignment - 1: 0b11, 0b111, ...
162 }
163 
164 /// ditto
165 bool isAligned(size_t alignment)(in size_t n) if(isPowerOf2(alignment))
166 { return .isAligned(alignment, n); }
167 
168 unittest
169 {
170 	import unstd.generictuple;
171 
172 	foreach(f; GenericTuple!(alignDown, alignUp, isAligned))
173 	{
174 		static assert(!__traits(compiles, f!0(1)));
175 		static assert(!__traits(compiles, { enum e = f(0, 1); }));
176 	}
177 
178 	foreach(n; iotaTuple!5)
179 	{
180 		foreach(f; GenericTuple!(alignDown, alignUp))
181 		{
182 			static assert(f!1(n) == n);
183 			static assert(f(1, n) == n);
184 		}
185 		static assert(isAligned!1(n));
186 		static assert(isAligned(1, n));
187 	}
188 
189 	foreach(alignment; expressionTuple!(2, 4, 8, 16))
190 	{
191 		static assert(!__traits(compiles, alignDown!(alignment + 1)(1)));
192 		static assert(!__traits(compiles, alignUp!(alignment + 1)(1)));
193 		
194 		alias down = alignDown!alignment;
195 		alias up = alignUp!alignment;
196 		alias aligned = isAligned!alignment;
197 		static assert(down(0) == 0 && up(0) == 0 && aligned(0));
198 
199 		static assert(down(1) == 0);
200 		static assert(down(alignment - 1) == 0);
201 		static assert(down(alignment) == alignment);
202 		static assert(down(alignment + 1) == alignment);
203 
204 		static assert(up(1) == alignment);
205 		static assert(up(alignment - 1) == alignment);
206 		static assert(up(alignment) == alignment);
207 		static assert(up(alignment + 1) == alignment * 2);
208 
209 		static assert(!aligned(1));
210 		static assert(!aligned(alignment - 1));
211 		static assert( aligned(alignment));
212 		static assert(!aligned(alignment + 1));
213 	}
214 
215 	static assert(alignDown!2(size_t.max) == size_t.max - 1);
216 	static assert(alignDown!16(size_t.max) == size_t.max - 15);
217 	static assert(alignUp!2(size_t.max - 1) == size_t.max - 1);
218 	static assert(alignUp!2(size_t.max) == 0);
219 	static assert(!isAligned!2(size_t.max));
220 	static assert( isAligned!16(size_t.max - 15));
221 }