1 module barcode.util;
2 
3 import std.exception;
4 import std.typecons : Flag;
5 import std.bitmanip : bitfields;
6 
7 public import std.typecons : tuple;
8 public import std.bitmanip : BitArray;
9 
10 alias AppendCheckSum = Flag!"appendCheckSum";
11 
12 ///
13 struct Bits(T=ulong)
14     if (is(T==ubyte) || is(T==ushort) || is(T==uint) || is(T==ulong))
15 {
16          static if (is(T==ubyte))  enum COUNTBITS = 3;
17     else static if (is(T==ushort)) enum COUNTBITS = 4;
18     else static if (is(T==uint))   enum COUNTBITS = 5;
19     else static if (is(T==ulong))  enum COUNTBITS = 6;
20 
21     enum VALUEBITS = T.sizeof * 8 - COUNTBITS;
22 
23     mixin(bitfields!(ubyte, "count", COUNTBITS,
24                          T, "value", VALUEBITS));
25 
26     alias value this;
27 
28 
29 pure nothrow @nogc @safe:
30 
31     ///
32     this(ulong cnt, ulong val)
33     {
34         count = cast(ubyte)cnt;
35         value = cast(T)val;
36     }
37 
38     ///
39     bool opIndex(size_t i) const
40     { return cast(bool)((value>>i)&1); }
41 
42     ///
43     bool opEquals(X)(auto ref const Bits!X v) const
44     { return v.value == value && v.count == count; }
45 
46     ///
47     bool opEquals(X)(X v) const
48     if (!is(X : Bits!U, U))
49     { return v == value; }
50 }
51 
52 @safe
53 unittest
54 {
55     static assert(bitsStr!"---".count == 3);
56     static assert(bitsStr!"--".count == 2);
57     static assert(bitsStr!"---" != bitsStr!"--");
58 }
59 
60 void addBits(ref BitArray ba, size_t value, int bits) @safe pure
61 {
62     enforce(bits <= size_t.sizeof*8, "so many bits");
63     enforce(bits >= 0, "bits must be more that 0");
64     addBits(ba, Bits!ulong(bits, value));
65 }
66 
67 @trusted // BitArray
68 unittest
69 {
70     BitArray ba;
71     ba.addBits(0b11000111010, 11);
72     const tst = BitArray([1,1,0,0,0,1,1,1,0,1,0]);
73     assert (ba == tst);
74 }
75 
76 void addBits(T)(ref BitArray ba, auto ref const(Bits!T) bits) @trusted pure
77 { foreach_reverse (i; 0 .. bits.count) ba ~= bits[i]; }
78 
79 @trusted // BitArray
80 unittest
81 {
82     BitArray ba;
83     ba.addBits(bitsStr!"##---###-#-");
84     auto tst = BitArray([1,1,0,0,0,1,1,1,0,1,0]);
85     assert (ba == tst);
86 }
87 
88 // for more readable bits writing
89 template bitsStr(string mask, char ONE='#')
90     if (mask.length <= 58)
91 {
92     static pure auto bitsImpl(T)() nothrow @nogc
93     {
94         ulong ret;
95         foreach (i; 0 .. mask.length)
96             ret |= cast(ulong)(mask[i] == ONE) << (mask.length - 1 - i);
97         return Bits!T(mask.length, ret);
98     }
99 
100          static if (mask.length < 6)  alias S = ubyte;
101     else static if (mask.length < 13) alias S = ushort;
102     else static if (mask.length < 28) alias S = uint;
103     else                              alias S = ulong;
104 
105     static if (mask.length >= 1) enum bitsStr = bitsImpl!S();
106     else static assert(0, "can't create 0 bits value");
107 }
108 
109 @safe
110 unittest
111 {
112     assert(1 == bitsStr!"#");
113     assert(0 == bitsStr!"-");
114     assert(0b1100011101011 == bitsStr!"##---###-#-##");
115     assert(bitsStr!"---".count == 3);
116 }
117 
118 auto getDict(alias F, T)(T[] arr)
119 {
120     alias frt = typeof(F(size_t(0), T.init));
121     alias KEY = typeof(frt.init[0]);
122     alias VAL = typeof(frt.init[1]);
123 
124     VAL[KEY] ret;
125     foreach (i, e; arr)
126     {
127         auto tmp = F(i, e);
128         ret[tmp[0]] = tmp[1];
129     }
130     return ret;
131 }
132 
133 @safe
134 unittest
135 {
136     static struct X { char ch; ushort mask; }
137     enum data = [ X('0', 0b001), X('1', 0b010), X('2', 0b100), ];
138 
139     {
140         enum ushort[char] t = getDict!((i,a) => tuple(a.ch, a.mask))(data);
141         static assert(t.keys.length == 3);
142         assert('0' in t);
143         assert(t['0'] == 0b001);
144         assert('1' in t);
145         assert(t['1'] == 0b010);
146         assert('2' in t);
147         assert(t['2'] == 0b100);
148     }
149 
150     {
151         enum ushort[char] t = getDict!((i,a) => tuple(a.ch, i))(data);
152         static assert(t.keys.length == 3);
153         assert('0' in t);
154         assert(t['0'] == 0);
155         assert('1' in t);
156         assert(t['1'] == 1);
157         assert('2' in t);
158         assert(t['2'] == 2);
159     }
160 
161     {
162         enum char[ubyte] t = getDict!((i,a) => tuple(cast(ubyte)i, a.ch))(data);
163         static assert(t.keys.length == 3);
164         assert(0 in t);
165         assert(t[0] == '0');
166         assert(1 in t);
167         assert(t[1] == '1');
168         assert(2 in t);
169         assert(t[2] == '2');
170     }
171 }