- Overview
- API Documentation
- License
- Requirements and Integration
- Implementation Notes
- Calculation Samples
Overview
This repository provides integer types of arbitrary width implemented
in 100% pure Swift. The underlying representation is in base 2^64, using Array<UInt64>
.
This module is handy when you need an integer type that's wider than UIntMax
, but
you don't want to add The GNU Multiple Precision Arithmetic Library
as a dependency.
Two big integer types are included: BigUInt
and BigInt
,
the latter being the signed variant.
Both of these are Swift structs with copy-on-write value semantics, and they can be used much
like any other integer type.
The library provides implementations for some of the most frequently useful functions on
big integers, including
-
All functionality from
Comparable
andHashable
-
The full set of arithmetic operators:
+
,-
,*
,/
,%
,+=
,-=
,*=
,/=
,%=
- Addition and subtraction have variants that allow for
shifting the digits of the second operand on the fly. - Unsigned subtraction will trap when the result would be negative.
(There are variants that return an overflow flag.) - Multiplication uses brute force for numbers up to 1024 digits, then switches to Karatsuba's recursive method.
(This limit is configurable, seeBigUInt.directMultiplicationLimit
.) - A fused multiply-add method is also available, along with other special-case variants.
- Division uses Knuth's Algorithm D, with its 3/2 digits wide quotient approximation.
It will trap when the divisor is zero. BigUInt.divide
returns the quotient and
remainder at once; this is faster than calculating them separately.
- Addition and subtraction have variants that allow for
-
Bitwise operators:
~
,|
,&
,^
,|=
,&=
,^=
, plus the following read-only properties:bitWidth
: the minimum number of bits required to store the integer,trailingZeroBitCount
: the number of trailing zero bits in the binary representation,leadingZeroBitCount
: the number of leading zero bits (when the last digit isn't full),
-
Shift operators:
>>
,<<
,>>=
,<<=
-
Methods to convert
NSData
to big integers and vice versa. -
Support for generating random integers of specified maximum width or magnitude.
-
Radix conversion to/from
String
s and big integers up to base 36 (using repeated divisions).- Big integers use this to implement
StringLiteralConvertible
(in base 10).
- Big integers use this to implement
-
sqrt(n)
: The square root of an integer (using Newton's method). -
BigUInt.gcd(n, m)
: The greatest common divisor of two integers (Stein's algorithm). -
base.power(exponent, modulus)
: Modular exponentiation (right-to-left binary method).
Vanilla exponentiation is also available. -
n.inverse(modulus)
: Multiplicative inverse in modulo arithmetic (extended Euclidean algorithm). -
n.isPrime()
: Miller–Rabin primality test.
The implementations are intended to be reasonably efficient, but they are unlikely to be
competitive with GMP at all, even when I happened to implement an algorithm with same asymptotic
behavior as GMP. (I haven't performed a comparison benchmark, though.)
The library has 100% unit test coverage. Sadly this does not imply that there are no bugs
in it.
API Documentation
Generated API docs are available at https://attaswift.github.io/BigInt/.
License
BigInt can be used, distributed and modified under the MIT license.
Requirements and Integration
BigInt 4.0.0 requires Swift 4.2 (The last version with support for Swift 3.x was BigInt 2.1.0.
The last version with support for Swift 2 was BigInt 1.3.0.)
Swift Version | last BigInt Version |
---|---|
3.x | 2.1.0 |
4.0 | 3.1.0 |
4.2 | 4.0.0 |
5.x | 5.3.0 |
BigInt deploys to macOS 10.10, iOS 9, watchOS 2 and tvOS 9.
It has been tested on the latest OS releases only---however, as the module uses very few platform-provided APIs,
there should be very few issues with earlier versions.
BigInt uses no APIs specific to Apple platforms, so
it should be easy to port it to other operating systems.
Setup instructions:
-
Swift Package Manager:
Although the Package Manager is still in its infancy, BigInt provides experimental support for it.
Add this to the dependency section of yourPackage.swift
manifest: -
CocoaPods: Put this in your
Podfile
: -
Carthage: Put this in your
Cartfile
:github "attaswift/BigInt" ~> 5.3
Implementation notes
BigUInt
is a MutableCollectionType
of its 64-bit digits, with the least significant
digit at index 0. As a convenience, BigUInt
allows you to subscript it with indexes at
or above its count
. The subscript operator returns 0 for out-of-bound get
s and
automatically extends the array on out-of-bound set
s. This makes memory management simpler.
BigInt
is just a tiny wrapper around a BigUInt
[absolute value][magnitude] and a
sign bit, both of which are accessible as public read-write properties.
Why is there no generic BigInt<Digit>
type?
The types provided by BigInt
are not parametric—this is very much intentional, as
Swift generics cost us dearly at runtime in this use case. In every approach I tried,
making arbitrary-precision arithmetic operations work with a generic Digit
type parameter
resulted in code that was literally ten times slower. If you can make the algorithms generic
without such a huge performance hit, please enlighten me!
This is an area that I plan to investigate more, as it would be useful to have generic
implementations for arbitrary-width arithmetic operations. (Polynomial division and decimal bases
are two examples.) The library already implements double-digit multiplication and division as
extension methods on a protocol with an associated type requirement; this has not measurably affected
performance. Unfortunately, the same is not true for BigUInt
's methods.
Of course, as a last resort, we could just duplicate the code to create a separate
generic variant that was slower but more flexible.
Calculation Samples
Obligatory Factorial Demo
It is easy to use BigInt
to calculate the factorial function for any integer:
Well, I guess that's all right, but it's not very interesting. Let's try something more useful.
RSA Cryptography
The BigInt
module provides all necessary parts to implement an (overly)
simple RSA cryptography system.
Let's start with a simple function that generates a random n-bit prime. The module
includes a function to generate random integers of a specific size, and also an
isPrime
method that performs the Miller–Rabin primality test. These are all we need:
Cool! Now that we have two large primes, we can produce an RSA public/private keypair
out of them.
In RSA, modular exponentiation is used to encrypt (and decrypt) messages.
Let's try out our new keypair by converting a string into UTF-8, interpreting
the resulting binary representation as a big integer, and encrypting it with the
public key. BigUInt
has an initializer that takes an NSData
, so this is pretty
easy to do:
Well, it looks encrypted all right, but can we get the original message back?
In theory, encrypting the cyphertext with the private key returns the original message.
Let's see:
Yay! This is truly terrific, but please don't use this example code in an actual
cryptography system. RSA has lots of subtle (and some not so subtle) complications
that we ignored to keep this example short.
Calculating the Digits of π
Another fun activity to try with BigInt
s is to generate the digits of π.
Let's try implementing Jeremy Gibbon's spigot algorithm.
This is a rather slow algorithm as π-generators go, but it makes up for it with its grooviness
factor: it's remarkably short, it only uses (big) integer arithmetic, and every iteration
produces a single new digit in the base-10 representation of π. This naturally leads to an
implementation as an infinite GeneratorType
:
Well, that was surprisingly easy. But does it work? Of course it does!
Now go and have some fun with big integers on your own!