Skip to content

Commit 2d36f2f

Browse files
authored
Bitops (#145)
* wip bitops * wip * wip * wip * wip * add nBitsToCount
1 parent 805a473 commit 2d36f2f

File tree

14 files changed

+1546
-205
lines changed

14 files changed

+1546
-205
lines changed

source/mir/algorithm/iteration.d

Lines changed: 599 additions & 79 deletions
Large diffs are not rendered by default.

source/mir/array/allocation.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ if (isIterable!Range && !isInfinite!Range && !isStaticArray!Range || isPointer!R
289289
int i;
290290
}
291291

292-
import std.meta: AliasSeq;
292+
alias AliasSeq(T...) = T;
293293
foreach (T; AliasSeq!(S, const S, immutable S))
294294
{
295295
auto arr = [T(1), T(2), T(3), T(4)];

source/mir/bitop.d

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
/++
2+
This module contains a collection of bit-level operations.
3+
4+
Authors: Ilya Yaroshenko, Phobos & LDC Authors (unittests, docs, conventions).
5+
+/
6+
module mir.bitop;
7+
8+
version(LDC)
9+
import ldc.intrinsics;
10+
11+
import mir.math.common: fastmath;
12+
13+
/// Right shift vallue for bit index to get element's index (5 for `uint`).
14+
enum uint bitElemShift(T : ubyte) = 3;
15+
/// ditto
16+
enum uint bitElemShift(T : byte) = 3;
17+
/// ditto
18+
enum uint bitElemShift(T : ushort) = 4;
19+
/// ditto
20+
enum uint bitElemShift(T : short) = 4;
21+
/// ditto
22+
enum uint bitElemShift(T : uint) = 5;
23+
/// ditto
24+
enum uint bitElemShift(T : int) = 5;
25+
/// ditto
26+
enum uint bitElemShift(T : ulong) = 6;
27+
/// ditto
28+
enum uint bitElemShift(T : long) = 6;
29+
static if (is(ucent))
30+
/// ditto
31+
enum uint bitElemShift(T : ucent) = 7;
32+
/// ditto
33+
static if (is(cent))
34+
enum uint bitElemShift(T : cent) = 7;
35+
36+
/// Bit mask for bit index to get element's bit shift (31 for uint).
37+
enum uint bitShiftMask(T : ubyte) = 7;
38+
/// ditto
39+
enum uint bitShiftMask(T : byte) = 7;
40+
/// ditto
41+
enum uint bitShiftMask(T : ushort) = 15;
42+
/// ditto
43+
enum uint bitShiftMask(T : short) = 15;
44+
/// ditto
45+
enum uint bitShiftMask(T : uint) = 31;
46+
/// ditto
47+
enum uint bitShiftMask(T : int) = 31;
48+
/// ditto
49+
enum uint bitShiftMask(T : ulong) = 63;
50+
/// ditto
51+
enum uint bitShiftMask(T : long) = 63;
52+
static if (is(ucent))
53+
/// ditto
54+
enum uint bitShiftMask(T : ucent) = 127;
55+
static if (is(cent))
56+
/// ditto
57+
enum uint bitShiftMask(T : cent) = 127;
58+
59+
// no effect on this function, but better for optimization of other @fastmath code that uses this
60+
@fastmath:
61+
62+
63+
/++
64+
+/
65+
T nTrailingBitsToCount(T)(T value, T popcnt)
66+
if (__traits(isUnsigned, T))
67+
{
68+
import std.traits;
69+
import mir.internal.utility: Iota;
70+
alias S = Signed!(CommonType!(int, T));
71+
S mask = S(-1) << T.sizeof * 4;
72+
foreach_reverse (s; Iota!(bitElemShift!T - 1))
73+
{{
74+
enum shift = 1 << s;
75+
if (S(popcnt) > S(ctpop(cast(T)(value & ~mask))))
76+
mask <<= shift;
77+
else
78+
mask >>= shift;
79+
}}
80+
return cttz(cast(T)mask) + (S(popcnt) != ctpop(cast(T)(value & ~mask)));
81+
}
82+
83+
///
84+
unittest
85+
{
86+
assert(nTrailingBitsToCount(0xF0u, 3u) == 7);
87+
assert(nTrailingBitsToCount(0xE00u, 3u) == 12);
88+
89+
foreach(uint i; 1 .. 32)
90+
assert(nTrailingBitsToCount(uint.max, i) == i);
91+
}
92+
93+
/++
94+
+/
95+
T nLeadingBitsToCount(T)(T value, T popcnt)
96+
if (__traits(isUnsigned, T))
97+
{
98+
import std.traits;
99+
import mir.internal.utility: Iota;
100+
alias S = Signed!(CommonType!(int, T));
101+
S mask = S(-1) << T.sizeof * 4;
102+
foreach_reverse (s; Iota!(bitElemShift!T - 1))
103+
{{
104+
enum shift = 1 << s;
105+
if (S(popcnt) > S(ctpop(cast(T)(value & mask))))
106+
mask >>= shift;
107+
else
108+
mask <<= shift;
109+
}}
110+
return ctlz(cast(T)~mask) + (S(popcnt) != ctpop(cast(T)(value & mask)));
111+
}
112+
113+
///
114+
unittest
115+
{
116+
assert(nLeadingBitsToCount(0xF0u, 3u) == 32 - 5);
117+
assert(nLeadingBitsToCount(0x700u, 3u) == 32 - 8);
118+
119+
foreach(uint i; 1 .. 32)
120+
assert(nLeadingBitsToCount(uint.max, i) == i);
121+
}
122+
123+
/++
124+
Tests the bit.
125+
Returns:
126+
A non-zero value if the bit was set, and a zero
127+
if it was clear.
128+
+/
129+
auto bt(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
130+
if (__traits(isUnsigned, T))
131+
{
132+
auto index = bitnum >> bitElemShift!T;
133+
auto mask = T(1) << (bitnum & bitShiftMask!T);
134+
return p[index] & mask;
135+
}
136+
137+
///
138+
@system pure unittest
139+
{
140+
size_t[2] array;
141+
142+
array[0] = 2;
143+
array[1] = 0x100;
144+
145+
assert(bt(array.ptr, 1));
146+
assert(array[0] == 2);
147+
assert(array[1] == 0x100);
148+
}
149+
150+
/++
151+
Tests and assign the bit.
152+
Returns:
153+
A non-zero value if the bit was set, and a zero if it was clear.
154+
+/
155+
auto bta(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum, bool value)
156+
if (__traits(isUnsigned, T))
157+
{
158+
auto index = bitnum >> bitElemShift!T;
159+
auto shift = bitnum & bitShiftMask!T;
160+
auto mask = T(1) << shift;
161+
static if (__traits(compiles, &p[size_t.init]))
162+
{
163+
auto qp = &p[index];
164+
auto q = *qp;
165+
auto ret = q & mask;
166+
*qp = cast(T)((q & ~mask) ^ (T(value) << shift));
167+
}
168+
else
169+
{
170+
auto q = p[index];
171+
auto ret = q & mask;
172+
p[index] = cast(T)((q & ~mask) ^ (T(value) << shift));
173+
}
174+
return ret;
175+
}
176+
177+
/++
178+
Tests and complements the bit.
179+
Returns:
180+
A non-zero value if the bit was set, and a zero if it was clear.
181+
+/
182+
auto btc(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
183+
if (__traits(isUnsigned, T))
184+
{
185+
auto index = bitnum >> bitElemShift!T;
186+
auto mask = T(1) << (bitnum & bitShiftMask!T);
187+
static if (__traits(compiles, &p[size_t.init]))
188+
{
189+
auto qp = &p[index];
190+
auto q = *qp;
191+
auto ret = q & mask;
192+
*qp = cast(T)(q ^ mask);
193+
}
194+
else
195+
{
196+
auto q = p[index];
197+
auto ret = q & mask;
198+
p[index] = cast(T)(q ^ mask);
199+
}
200+
return ret;
201+
}
202+
203+
/++
204+
Tests and resets (sets to 0) the bit.
205+
Returns:
206+
A non-zero value if the bit was set, and a zero if it was clear.
207+
+/
208+
auto btr(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
209+
if (__traits(isUnsigned, T))
210+
{
211+
auto index = bitnum >> bitElemShift!T;
212+
auto mask = T(1) << (bitnum & bitShiftMask!T);
213+
static if (__traits(compiles, &p[size_t.init]))
214+
{
215+
auto qp = &p[index];
216+
auto q = *qp;
217+
auto ret = q & mask;
218+
*qp = cast(T)(q & ~mask);
219+
}
220+
else
221+
{
222+
auto q = p[index];
223+
auto ret = q & mask;
224+
p[index] = cast(T)(q & ~mask);
225+
}
226+
return ret;
227+
}
228+
229+
/++
230+
Tests and sets the bit.
231+
Params:
232+
p = a non-NULL field / pointer to an array of unsigned integers.
233+
bitnum = a bit number, starting with bit 0 of p[0],
234+
and progressing. It addresses bits like the expression:
235+
---
236+
p[index / (T.sizeof*8)] & (1 << (index & ((T.sizeof*8) - 1)))
237+
---
238+
Returns:
239+
A non-zero value if the bit was set, and a zero if it was clear.
240+
+/
241+
auto bts(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
242+
if (__traits(isUnsigned, T))
243+
{
244+
auto index = bitnum >> bitElemShift!T;
245+
auto mask = T(1) << (bitnum & bitShiftMask!T);
246+
static if (__traits(compiles, &p[size_t.init]))
247+
{
248+
auto qp = &p[index];
249+
auto q = *qp;
250+
auto ret = q & mask;
251+
*qp = cast(T)(q | mask);
252+
}
253+
else
254+
{
255+
auto q = p[index];
256+
auto ret = q & mask;
257+
p[index] = cast(T)(q | mask);
258+
}
259+
return ret;
260+
}
261+
262+
///
263+
@system pure unittest
264+
{
265+
size_t[2] array;
266+
267+
array[0] = 2;
268+
array[1] = 0x100;
269+
270+
assert(btc(array.ptr, 35) == 0);
271+
if (size_t.sizeof == 8)
272+
{
273+
assert(array[0] == 0x8_0000_0002);
274+
assert(array[1] == 0x100);
275+
}
276+
else
277+
{
278+
assert(array[0] == 2);
279+
assert(array[1] == 0x108);
280+
}
281+
282+
assert(btc(array.ptr, 35));
283+
assert(array[0] == 2);
284+
assert(array[1] == 0x100);
285+
286+
assert(bts(array.ptr, 35) == 0);
287+
if (size_t.sizeof == 8)
288+
{
289+
assert(array[0] == 0x8_0000_0002);
290+
assert(array[1] == 0x100);
291+
}
292+
else
293+
{
294+
assert(array[0] == 2);
295+
assert(array[1] == 0x108);
296+
}
297+
298+
assert(btr(array.ptr, 35));
299+
assert(array[0] == 2);
300+
assert(array[1] == 0x100);
301+
}
302+
303+
/// The 'ctpop' family of intrinsics counts the number of bits set in a value.
304+
T ctpop(T)(T src)
305+
if (__traits(isUnsigned, T))
306+
{
307+
version(LDC) if (!__ctfe)
308+
return llvm_ctpop(src);
309+
import core.bitop: popcnt;
310+
return cast(T) popcnt(src);
311+
}
312+
313+
/++
314+
The 'ctlz' family of intrinsic functions counts the number of leading zeros in a variable.
315+
Result is undefined if the argument is zero.
316+
+/
317+
T ctlz(T)(T src)
318+
if (__traits(isUnsigned, T))
319+
{
320+
version(LDC) if (!__ctfe)
321+
return llvm_ctlz(src, true);
322+
import core.bitop: bsr;
323+
return cast(T)(T.sizeof * 8 - 1 - bsr(src));
324+
}
325+
326+
/++
327+
The 'cttz' family of intrinsic functions counts the number of trailing zeros.
328+
Result is undefined if the argument is zero.
329+
+/
330+
T cttz(T)(T src)
331+
if (__traits(isUnsigned, T))
332+
{
333+
version(LDC) if (!__ctfe)
334+
return llvm_cttz(src, true);
335+
import core.bitop: bsf;
336+
return cast(T) bsf(src);
337+
}

source/mir/internal/utility.d

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
///
22
module mir.internal.utility;
33

4-
import std.traits: Unqual, isFloatingPoint;
54
private alias AliasSeq(T...) = T;
65

76
///
@@ -19,7 +18,7 @@ template Iota(size_t i, size_t j)
1918

2019
///
2120
template realType(C)
22-
if (isFloatingPoint!C || isComplex!C)
21+
if (__traits(isFloating, C) || isComplex!C)
2322
{
2423
static if (isComplex!C)
2524
alias realType = typeof(Unqual!C.init.re);
@@ -28,10 +27,19 @@ template realType(C)
2827
}
2928

3029
///
31-
template isComplex(C)
32-
{
33-
enum bool isComplex
34-
= is(Unqual!C == creal)
35-
|| is(Unqual!C == cdouble)
36-
|| is(Unqual!C == cfloat);
37-
}
30+
enum isComplex(C : creal) = true;
31+
/// ditto
32+
enum isComplex(C : cdouble) = true;
33+
/// ditto
34+
enum isComplex(C : cfloat) = true;
35+
/// ditto
36+
enum isComplex(C) = false;
37+
38+
///
39+
enum isFloatingPoint(C : real) = true;
40+
/// ditto
41+
enum isFloatingPoint(C : double) = true;
42+
/// ditto
43+
enum isFloatingPoint(C : float) = true;
44+
/// ditto
45+
enum isFloatingPoint(C) = false;

0 commit comments

Comments
 (0)