bigint.h
BigInteger: signed integers of any size.
#include <sonore/bigint.h>- 1type
On this page
Design notes
What it is for
RSAKey and Primes in crypto.h are built on it, and those are the reason it exists: a key pair is two numbers hundreds of digits long, and the only operation that matters on them is "this to the power of that, modulo the other", which does not fit in any machine word. Beyond that it is the reference's arbitrary-precision integer -- construction from machine integers, text in bases 2, 8, 10 and 16, and little-endian bytes; the four arithmetic operations; gcd and modular inverse; bit access -- so code written against that one has the same names to reach for here.
Not the licence check's integer
license.h carries its own minimal integer for its one modular exponentiation. That stays separate on purpose: what a licence check accepts must not move because somebody made this class faster. The tests cross-check the two on random inputs, which is the only way they are allowed to be related.
Representation
Sign and magnitude. The magnitude is base-2^32 limbs, least significant first, with no zero limb at the top; zero is an empty vector and is never negative, so there is one representation of every value and == can compare the vectors directly. 32-bit limbs with 64-bit products, because every compiler this SDK targets multiplies 32x32->64 natively -- the 64-bit limb alternative needs __int128 on one compiler and _umul128 on another, and neither exists on all of them.
Semantics that follow the reference rather than C++
- Division truncates toward zero and the remainder takes the dividend's sign. That much IS C++'s rule for int.
- Division by zero gives a zero quotient and a zero remainder instead of trapping. A caller that might divide by zero has to check first, as it would for any other answer it cannot use.
- The bit operations -- & | ^, the shifts, setBit and friends -- act on the MAGNITUDE and keep the left operand's sign. So -5 >> 1 is -2, not -3, and -6 & 3 is -2. This is not two's complement: there is no infinite run of ones to the left of a negative number here.
- toMemoryBlock is the magnitude, little-endian, in the fewest bytes that hold it; the sign is not written.
Cost
Schoolbook multiplication and Knuth's Algorithm D for division: both quadratic. At RSA sizes -- 16 to 64 limbs -- that is close to the best there is; Karatsuba's crossover sits in the tens of limbs, so it would earn a modest fraction at the top of that range and cost a second multiplier to keep correct. A 1024-bit key pair is generated in well under a second. exponentModulo normalises the modulus once and reduces into reused buffers, so its loop does not allocate after the first square.
NOT constant-time: how long exponentModulo takes depends on the bits of the exponent. Signing a licence file on a build machine is fine; a server that decrypts whatever it is sent, and lets the sender time it, is not.
Allocates. Never on the audio thread.
Types 1
An integer with as many bits as it needs.
A value type: copy it, compare it, use it as an int. Machine integers convert implicitly, so n % 7 == 0 and x + 1 read the way they would for an int.