Skip to content

Commit 1e3f359

Browse files
authored
Implement support for binary literals (#368)
* Implement support for binary literals * Add basic test for BigInt fromBinaryString
1 parent 88b905a commit 1e3f359

File tree

3 files changed

+332
-0
lines changed

3 files changed

+332
-0
lines changed

source/mir/bignum/fixed.d

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,33 @@ struct UInt(size_t size)
223223
return view.fromHexStringImpl!(C, allowUnderscores)(str);
224224
}
225225

226+
///
227+
static UInt!size fromBinaryString(bool allowUnderscores = false)(scope const(char)[] str)
228+
{
229+
typeof(return) ret;
230+
if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str))
231+
return ret;
232+
version(D_Exceptions)
233+
{
234+
import mir.bignum.low_level_view: binaryStringException;
235+
throw binaryStringException;
236+
}
237+
else
238+
{
239+
import mir.bignum.low_level_view: binaryStringErrorMsg;
240+
assert(0, binaryStringErrorMsg);
241+
}
242+
}
243+
244+
/++
245+
+/
246+
bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
247+
@safe pure @nogc nothrow
248+
if (isSomeChar!C)
249+
{
250+
return view.fromBinaryStringImpl!(C, allowUnderscores)(str);
251+
}
252+
226253
/++
227254
+/
228255
auto opEquals(size_t rhsSize)(auto ref const UInt!rhsSize rhs) const

source/mir/bignum/integer.d

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,44 @@ struct BigInt(size_t maxSize64)
394394
return ret;
395395
}
396396

397+
/++
398+
+/
399+
static BigInt fromBinaryString(bool allowUnderscores = false)(scope const(char)[] str)
400+
@trusted pure
401+
{
402+
BigInt ret;
403+
if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str))
404+
return ret;
405+
version(D_Exceptions)
406+
throw binaryStringException;
407+
else
408+
assert(0, binaryStringErrorMsg);
409+
}
410+
411+
static if (maxSize64 == 3)
412+
///
413+
version(mir_bignum_test) @safe pure @nogc unittest
414+
{
415+
BigInt!4 integer = "-34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor
416+
assert(integer == BigInt!4.fromBinaryString("-100101100110001001110110010001110101010010101100000111000011011000010011000010111111000100111001011111001101101111101010100011000001000011000001110001110011010011001001011101010010010101101001010101111011101001111101110011101111110010011100000010110111000"));
417+
}
418+
419+
/++
420+
+/
421+
bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
422+
@safe pure @nogc nothrow
423+
if (isSomeChar!C)
424+
{
425+
auto work = BigIntView!size_t(data);
426+
auto ret = work.fromBinaryStringImpl!(C, allowUnderscores)(str);
427+
if (ret)
428+
{
429+
length = cast(uint)work.unsigned.coefficients.length;
430+
sign = work.sign;
431+
}
432+
return ret;
433+
}
434+
397435
///
398436
bool mulPow5(size_t degree)
399437
{

source/mir/bignum/low_level_view.d

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ private alias cop(string op : "+") = addu;
1919
private enum inverseSign(string op) = op == "+" ? "-" : "+";
2020

2121
package immutable hexStringErrorMsg = "Incorrect hex string for BigUIntView.fromHexString";
22+
package immutable binaryStringErrorMsg = "Incorrect binary string for BigUIntView.fromBinaryString";
2223
version (D_Exceptions)
2324
{
2425
package immutable hexStringException = new Exception(hexStringErrorMsg);
26+
package immutable binaryStringException = new Exception(binaryStringErrorMsg);
2527
}
2628

2729
/++
@@ -721,6 +723,144 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
721723
expectThrow("__abcd__efab_cdef__");
722724
expectThrow("__abcd__efab__cdef__");
723725
}
726+
727+
/++
728+
+/
729+
static BigUIntView fromBinaryString(C, bool allowUnderscores = false)(scope const(C)[] str)
730+
@trusted pure
731+
if (isSomeChar!C)
732+
{
733+
auto length = str.length / (W.sizeof * 8) + (str.length % (W.sizeof * 8) != 0);
734+
auto data = new Unqual!W[length];
735+
auto view = BigUIntView!(Unqual!W, endian)(data);
736+
if (view.fromBinaryStringImpl!(C, allowUnderscores)(str))
737+
return BigUIntView(cast(W[])view.coefficients);
738+
version(D_Exceptions)
739+
throw binaryStringException;
740+
else
741+
assert(0, binaryStringErrorMsg);
742+
}
743+
744+
static if (isMutable!W)
745+
/++
746+
+/
747+
bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
748+
@safe pure @nogc nothrow scope
749+
if (isSomeChar!C)
750+
{
751+
pragma(inline, false);
752+
import mir.utility: _expect;
753+
static if (allowUnderscores) {
754+
if (_expect(str.length == 0, false)) // can't tell how big the coeff array needs to be, rely on a runtime check
755+
return false;
756+
} else {
757+
if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 8, false))
758+
return false;
759+
}
760+
761+
leastSignificant = 0;
762+
auto work = topLeastSignificantPart(1);
763+
W current;
764+
size_t i, j;
765+
static if (allowUnderscores) bool recentUnderscore;
766+
767+
do
768+
{
769+
ubyte c;
770+
switch(str[$ - ++i])
771+
{
772+
case '0': c = 0x0; break;
773+
case '1': c = 0x1; break;
774+
static if (allowUnderscores)
775+
{
776+
case '_':
777+
if (recentUnderscore) return false;
778+
recentUnderscore = true;
779+
continue;
780+
}
781+
default: return false;
782+
}
783+
++j;
784+
static if (allowUnderscores) recentUnderscore = false;
785+
// how far do we need to shift to get to the top bit?
786+
enum s = W.sizeof * 8 - 1;
787+
// shift number to the top most bit
788+
W cc = cast(W)(W(c) << s);
789+
// shift unsigned right 1 bit
790+
current >>>= 1;
791+
// add number to top most bit of current var
792+
current |= cc;
793+
if (j % (W.sizeof * 8) == 0) // is this packed var full?
794+
{
795+
work.mostSignificant = current;
796+
current = 0;
797+
if (_expect(work.coefficients.length < coefficients.length, true))
798+
{
799+
work = topLeastSignificantPart(work.coefficients.length + 1);
800+
}
801+
else if (i < str.length) // if we've run out of coefficients before reaching the end of the string, error
802+
{
803+
return false;
804+
}
805+
}
806+
}
807+
while(i < str.length);
808+
809+
static if (allowUnderscores)
810+
{
811+
// check for a underscore at the beginning or the end
812+
if (recentUnderscore || str[$ - 1] == '_') return false;
813+
}
814+
815+
if (current)
816+
{
817+
current >>>= (W.sizeof * 8 - j % (W.sizeof * 8));
818+
work.mostSignificant = current;
819+
}
820+
821+
coefficients = coefficients[0 .. (j / (W.sizeof * 8) + (j % (W.sizeof * 8) != 0))];
822+
823+
return true;
824+
}
825+
826+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
827+
///
828+
version(mir_bignum_test)
829+
@safe pure
830+
unittest
831+
{
832+
auto view = BigUIntView!size_t.fromBinaryString!(char, true)("1111_0000_0101");
833+
assert(cast(ulong)view == 0b1111_0000_0101);
834+
}
835+
836+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
837+
///
838+
version(mir_bignum_test)
839+
@safe pure
840+
unittest
841+
{
842+
// Check that invalid underscores in hex literals throw an error.
843+
void expectThrow(const(char)[] input) {
844+
bool caught = false;
845+
try {
846+
auto view = BigUIntView!size_t.fromBinaryString!(char, true)(input);
847+
} catch (Exception e) {
848+
caught = true;
849+
}
850+
851+
assert(caught);
852+
}
853+
854+
expectThrow("abcd");
855+
expectThrow("0101__1011__0111");
856+
expectThrow("_0101_1011_0111");
857+
expectThrow("_0101_1011_0111_");
858+
expectThrow("_0101_1011_0111__");
859+
expectThrow("__0101_1011_0111_");
860+
expectThrow("__0101_1011_0111__");
861+
expectThrow("__0101__1011_0111__");
862+
expectThrow("__1011__0111__1011__");
863+
}
724864

725865
static if (isMutable!W && W.sizeof >= 4)
726866
/++
@@ -1502,6 +1642,53 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
15021642
return unsigned.fromHexStringImpl!(C, allowUnderscores)(str);
15031643
}
15041644

1645+
/++
1646+
+/
1647+
static BigIntView fromBinaryString(C, bool allowUnderscores = false)(scope const(C)[] str)
1648+
@trusted pure
1649+
if (isSomeChar!C)
1650+
{
1651+
auto length = str.length / (W.sizeof * 8) + (str.length % (W.sizeof * 8) != 0);
1652+
auto ret = BigIntView!(Unqual!W, endian)(new Unqual!W[length]);
1653+
if (ret.fromBinaryStringImpl!(C, allowUnderscores)(str))
1654+
return cast(BigIntView) ret;
1655+
version(D_Exceptions)
1656+
throw binaryStringException;
1657+
else
1658+
assert(0, binaryStringErrorMsg);
1659+
}
1660+
1661+
static if (isMutable!W)
1662+
/++
1663+
+/
1664+
bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
1665+
@safe pure @nogc nothrow
1666+
if (isSomeChar!C)
1667+
{
1668+
pragma(inline, false);
1669+
import mir.utility: _expect;
1670+
1671+
assert(unsigned.coefficients.length);
1672+
1673+
if (_expect(str.length == 0, false))
1674+
return false;
1675+
1676+
sign = false;
1677+
1678+
if (str[0] == '-')
1679+
{
1680+
sign = true;
1681+
str = str[1 .. $];
1682+
}
1683+
else
1684+
if (_expect(str[0] == '+', false))
1685+
{
1686+
str = str[1 .. $];
1687+
}
1688+
1689+
return unsigned.fromBinaryStringImpl!(C, allowUnderscores)(str);
1690+
}
1691+
15051692
///
15061693
T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const
15071694
if (isFloatingPoint!T && isMutable!T)
@@ -1522,6 +1709,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
15221709
assert(a == -0xa.fbbfae3cd0bp+124);
15231710
}
15241711

1712+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1713+
///
1714+
version(mir_bignum_test)
1715+
@safe pure
1716+
unittest
1717+
{
1718+
auto a = cast(double) BigIntView!size_t.fromBinaryString("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001010011101");
1719+
assert(a == -0xa.fbbfae3cd0bp+124);
1720+
}
1721+
15251722
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
15261723
///
15271724
version(mir_bignum_test)
@@ -1532,6 +1729,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
15321729
assert(a == -0xa.fbbfae3cd0bp+124);
15331730
}
15341731

1732+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1733+
///
1734+
version(mir_bignum_test)
1735+
@safe pure
1736+
unittest
1737+
{
1738+
auto a = cast(double) BigIntView!size_t.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_1001_1101");
1739+
assert(a == -0xa.fbbfae3cd0bp+124);
1740+
}
1741+
15351742
///
15361743
T opCast(T, bool nonZero = false)() scope const
15371744
if (is(T == long) || is(T == int))
@@ -1604,6 +1811,66 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
16041811
assert(cast(int) view == -0x22b0021d);
16051812
}
16061813

1814+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1815+
version(mir_bignum_test)
1816+
@safe pure
1817+
unittest
1818+
{
1819+
auto view = BigIntView!size_t.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
1820+
assert(cast(long) view == -0x14a1de7022b0021d);
1821+
assert(cast(int) view == -0x22b0021d);
1822+
}
1823+
1824+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1825+
version(mir_bignum_test)
1826+
@safe pure
1827+
unittest
1828+
{
1829+
auto view = BigIntView!size_t.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
1830+
assert(cast(long) view == -0x14a1de7022b0021d);
1831+
assert(cast(int) view == -0x22b0021d);
1832+
}
1833+
1834+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1835+
version(mir_bignum_test)
1836+
@safe pure
1837+
unittest
1838+
{
1839+
auto view = BigIntView!ushort.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
1840+
assert(cast(long) view == -0x14a1de7022b0021d);
1841+
assert(cast(int) view == -0x22b0021d);
1842+
}
1843+
1844+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1845+
version(mir_bignum_test)
1846+
@safe pure
1847+
unittest
1848+
{
1849+
auto view = BigIntView!ushort.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
1850+
assert(cast(long) view == -0x14a1de7022b0021d);
1851+
assert(cast(int) view == -0x22b0021d);
1852+
}
1853+
1854+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1855+
version(mir_bignum_test)
1856+
@safe pure
1857+
unittest
1858+
{
1859+
auto view = BigIntView!ubyte.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
1860+
assert(cast(long) view == -0x14a1de7022b0021d);
1861+
assert(cast(int) view == -0x22b0021d);
1862+
}
1863+
1864+
static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
1865+
version(mir_bignum_test)
1866+
@safe pure
1867+
unittest
1868+
{
1869+
auto view = BigIntView!ubyte.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
1870+
assert(cast(long) view == -0x14a1de7022b0021d);
1871+
assert(cast(int) view == -0x22b0021d);
1872+
}
1873+
16071874
/++
16081875
+/
16091876
T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const

0 commit comments

Comments
 (0)