From 269092fb53b9b02cf35e8f810ed8e3f83cd299ce Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sun, 6 Jul 2025 19:20:20 +1000 Subject: [PATCH] Project is now named Celeste --- .gitignore | 2 + README | 24 + celeste.nimble | 10 + {imp => celeste}/__init__.py | 0 {imp => celeste}/attacks/__init__.py | 0 {imp => celeste}/attacks/paddingoracle.py | 2 +- {imp => celeste}/constants.py | 0 {imp => celeste}/crypto/__init__.py | 0 {imp => celeste}/crypto/cyphers.py | 2 +- {imp => celeste}/crypto/hash.py | 0 {imp => celeste}/crypto/rsa.py | 0 {imp => celeste}/extern/__init__.py | 0 {imp => celeste}/extern/primefac.py | 0 {imp => celeste}/math/__init__.py | 0 {imp => celeste}/math/crt.py | 0 {imp => celeste}/math/factor/__init__.py | 0 {imp => celeste}/math/factor/factordb.py | 0 .../math/factor/pftrialdivision.py | 0 {imp => celeste}/math/groups.py | 0 {imp => celeste}/math/numbers/__init__.py | 2 +- {imp => celeste}/math/numbers/functions.py | 0 {imp => celeste}/math/numbers/kinds.py | 0 {imp => celeste}/math/numbertheory.py | 0 {imp => celeste}/math/primes.py | 4 +- {imp => celeste}/math/primes/__init__.py | 0 {imp => celeste}/math/util.py | 0 {imp => celeste}/structs/__init__.py | 0 {imp => celeste}/structs/__result.py | 0 config.nims | 4 + ctf-solutions/bean_counter.py | 2 +- ctf-solutions/symmetry.py | 2 +- examples/cryptohack-ecboracle.py | 4 +- examples/local-ecboracle.py | 2 +- pyproject.toml | 3 +- research/aparith/README | 12 + research/aparith/aparith | Bin 0 -> 93104 bytes research/aparith/aparith.nimble | 14 + research/aparith/src/aparith.nim | 5 + research/aparith/src/bigints.git.nim | 1311 +++++++++++++++++ .../src/bigints.git/private/literals.nim | 17 + research/aparith/src/bigints.git/random.nim | 43 + research/aparith/src/speedtest_bigint | Bin 0 -> 177392 bytes research/aparith/src/speedtest_bigint.nim | 35 + src/imp.nim | 7 + src/imp/submodule.nim | 12 + 45 files changed, 1507 insertions(+), 12 deletions(-) create mode 100644 celeste.nimble rename {imp => celeste}/__init__.py (100%) rename {imp => celeste}/attacks/__init__.py (100%) rename {imp => celeste}/attacks/paddingoracle.py (98%) rename {imp => celeste}/constants.py (100%) rename {imp => celeste}/crypto/__init__.py (100%) rename {imp => celeste}/crypto/cyphers.py (98%) rename {imp => celeste}/crypto/hash.py (100%) rename {imp => celeste}/crypto/rsa.py (100%) rename {imp => celeste}/extern/__init__.py (100%) rename {imp => celeste}/extern/primefac.py (100%) rename {imp => celeste}/math/__init__.py (100%) rename {imp => celeste}/math/crt.py (100%) rename {imp => celeste}/math/factor/__init__.py (100%) rename {imp => celeste}/math/factor/factordb.py (100%) rename {imp => celeste}/math/factor/pftrialdivision.py (100%) rename {imp => celeste}/math/groups.py (100%) rename {imp => celeste}/math/numbers/__init__.py (98%) rename {imp => celeste}/math/numbers/functions.py (100%) rename {imp => celeste}/math/numbers/kinds.py (100%) rename {imp => celeste}/math/numbertheory.py (100%) rename {imp => celeste}/math/primes.py (93%) rename {imp => celeste}/math/primes/__init__.py (100%) rename {imp => celeste}/math/util.py (100%) rename {imp => celeste}/structs/__init__.py (100%) rename {imp => celeste}/structs/__result.py (100%) create mode 100644 config.nims create mode 100644 research/aparith/README create mode 100755 research/aparith/aparith create mode 100644 research/aparith/aparith.nimble create mode 100644 research/aparith/src/aparith.nim create mode 100644 research/aparith/src/bigints.git.nim create mode 100644 research/aparith/src/bigints.git/private/literals.nim create mode 100644 research/aparith/src/bigints.git/random.nim create mode 100755 research/aparith/src/speedtest_bigint create mode 100644 research/aparith/src/speedtest_bigint.nim create mode 100644 src/imp.nim create mode 100644 src/imp/submodule.nim diff --git a/.gitignore b/.gitignore index c18dd8d..8d0f132 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ __pycache__/ +nimble.develop +nimble.paths diff --git a/README b/README index 248d5eb..1303d8b 100644 --- a/README +++ b/README @@ -2,3 +2,27 @@ The "imbaud python library" (imp lib), or just imp for short! TODO: - define a getPrime function like PyCryptodome's +- rewrite nim-lang/bigints to implement features like Karatsuba multiplication, or even Toom-3 multiplication + + +PyCryptodome defines getPrime as follows: +```py +def getPrime(N, randfunc=None): + """Return a random N-bit prime number. + + N must be an integer larger than 1. + If randfunc is omitted, then :meth:`Random.get_random_bytes` is used. + """ + if randfunc is None: + randfunc = Random.get_random_bytes + + if N < 2: + raise ValueError("N must be larger than 1") + + while True: + number = getRandomNBitInteger(N, randfunc) | 1 + if isPrime(number, randfunc=randfunc): + break + return number +``` +in essence infinite random generation until a prime is found diff --git a/celeste.nimble b/celeste.nimble new file mode 100644 index 0000000..a3d4f31 --- /dev/null +++ b/celeste.nimble @@ -0,0 +1,10 @@ +# Package +version = "0.1.0" +author = "Emile Clark-Boman" +description = "Self contained framework for computational mathematics" +license = "MIT" +srcDir = "src" + + +# Dependencies +requires "nim >= 2.2.0" diff --git a/imp/__init__.py b/celeste/__init__.py similarity index 100% rename from imp/__init__.py rename to celeste/__init__.py diff --git a/imp/attacks/__init__.py b/celeste/attacks/__init__.py similarity index 100% rename from imp/attacks/__init__.py rename to celeste/attacks/__init__.py diff --git a/imp/attacks/paddingoracle.py b/celeste/attacks/paddingoracle.py similarity index 98% rename from imp/attacks/paddingoracle.py rename to celeste/attacks/paddingoracle.py index d9fd1a5..5529624 100644 --- a/imp/attacks/paddingoracle.py +++ b/celeste/attacks/paddingoracle.py @@ -1,7 +1,7 @@ from math import inf, ceil from typing import Callable -from imp.math.util import clamp_max +from celeste.math.util import clamp_max SPACER = b'\xff' # arbitrary spacing character diff --git a/imp/constants.py b/celeste/constants.py similarity index 100% rename from imp/constants.py rename to celeste/constants.py diff --git a/imp/crypto/__init__.py b/celeste/crypto/__init__.py similarity index 100% rename from imp/crypto/__init__.py rename to celeste/crypto/__init__.py diff --git a/imp/crypto/cyphers.py b/celeste/crypto/cyphers.py similarity index 98% rename from imp/crypto/cyphers.py rename to celeste/crypto/cyphers.py index b6896eb..2d0b504 100644 --- a/imp/crypto/cyphers.py +++ b/celeste/crypto/cyphers.py @@ -12,7 +12,7 @@ Terminology: substitutions to the entire message (ie vigenere) ''' -from imp.constants import ALPHA_LOWER, ALPHA_UPPER +from celeste.constants import ALPHA_LOWER, ALPHA_UPPER ''' Constant Declarations diff --git a/imp/crypto/hash.py b/celeste/crypto/hash.py similarity index 100% rename from imp/crypto/hash.py rename to celeste/crypto/hash.py diff --git a/imp/crypto/rsa.py b/celeste/crypto/rsa.py similarity index 100% rename from imp/crypto/rsa.py rename to celeste/crypto/rsa.py diff --git a/imp/extern/__init__.py b/celeste/extern/__init__.py similarity index 100% rename from imp/extern/__init__.py rename to celeste/extern/__init__.py diff --git a/imp/extern/primefac.py b/celeste/extern/primefac.py similarity index 100% rename from imp/extern/primefac.py rename to celeste/extern/primefac.py diff --git a/imp/math/__init__.py b/celeste/math/__init__.py similarity index 100% rename from imp/math/__init__.py rename to celeste/math/__init__.py diff --git a/imp/math/crt.py b/celeste/math/crt.py similarity index 100% rename from imp/math/crt.py rename to celeste/math/crt.py diff --git a/imp/math/factor/__init__.py b/celeste/math/factor/__init__.py similarity index 100% rename from imp/math/factor/__init__.py rename to celeste/math/factor/__init__.py diff --git a/imp/math/factor/factordb.py b/celeste/math/factor/factordb.py similarity index 100% rename from imp/math/factor/factordb.py rename to celeste/math/factor/factordb.py diff --git a/imp/math/factor/pftrialdivision.py b/celeste/math/factor/pftrialdivision.py similarity index 100% rename from imp/math/factor/pftrialdivision.py rename to celeste/math/factor/pftrialdivision.py diff --git a/imp/math/groups.py b/celeste/math/groups.py similarity index 100% rename from imp/math/groups.py rename to celeste/math/groups.py diff --git a/imp/math/numbers/__init__.py b/celeste/math/numbers/__init__.py similarity index 98% rename from imp/math/numbers/__init__.py rename to celeste/math/numbers/__init__.py index 22a0d21..83a0705 100644 --- a/imp/math/numbers/__init__.py +++ b/celeste/math/numbers/__init__.py @@ -6,7 +6,7 @@ Terminology: the "prime proper divisors of n". ''' -from imp.extern.primefac import primefac +from celeste.extern.primefac import primefac def factors(n: int) -> int: pfactors: list[tuple[int, int]] = [] diff --git a/imp/math/numbers/functions.py b/celeste/math/numbers/functions.py similarity index 100% rename from imp/math/numbers/functions.py rename to celeste/math/numbers/functions.py diff --git a/imp/math/numbers/kinds.py b/celeste/math/numbers/kinds.py similarity index 100% rename from imp/math/numbers/kinds.py rename to celeste/math/numbers/kinds.py diff --git a/imp/math/numbertheory.py b/celeste/math/numbertheory.py similarity index 100% rename from imp/math/numbertheory.py rename to celeste/math/numbertheory.py diff --git a/imp/math/primes.py b/celeste/math/primes.py similarity index 93% rename from imp/math/primes.py rename to celeste/math/primes.py index b130a0c..d657367 100644 --- a/imp/math/primes.py +++ b/celeste/math/primes.py @@ -1,7 +1,7 @@ from math import gcd, inf -from imp.math.numbers import bigomega, factors -from imp.extern.primefac import ( +from celeste.math.numbers import bigomega, factors +from celeste.extern.primefac import ( isprime, primegen as Primes, ) diff --git a/imp/math/primes/__init__.py b/celeste/math/primes/__init__.py similarity index 100% rename from imp/math/primes/__init__.py rename to celeste/math/primes/__init__.py diff --git a/imp/math/util.py b/celeste/math/util.py similarity index 100% rename from imp/math/util.py rename to celeste/math/util.py diff --git a/imp/structs/__init__.py b/celeste/structs/__init__.py similarity index 100% rename from imp/structs/__init__.py rename to celeste/structs/__init__.py diff --git a/imp/structs/__result.py b/celeste/structs/__result.py similarity index 100% rename from imp/structs/__result.py rename to celeste/structs/__result.py diff --git a/config.nims b/config.nims new file mode 100644 index 0000000..8ee48d2 --- /dev/null +++ b/config.nims @@ -0,0 +1,4 @@ +# begin Nimble config (version 2) +when withDir(thisDir(), system.fileExists("nimble.paths")): + include "nimble.paths" +# end Nimble config diff --git a/ctf-solutions/bean_counter.py b/ctf-solutions/bean_counter.py index 31a6219..f0b5634 100644 --- a/ctf-solutions/bean_counter.py +++ b/ctf-solutions/bean_counter.py @@ -8,7 +8,7 @@ from math import ceil from Crypto.Cipher import AES -from imp.math.util import xor_bytes +from celeste.math.util import xor_bytes class StepUpCounter(object): def __init__(self, step_up=False): diff --git a/ctf-solutions/symmetry.py b/ctf-solutions/symmetry.py index d12ad08..47d103e 100644 --- a/ctf-solutions/symmetry.py +++ b/ctf-solutions/symmetry.py @@ -5,7 +5,7 @@ Solution to https://cryptohack.org/courses/symmetric/symmetry/ import os import requests -from imp.math.util import xor_bytes, xor_str +from celeste.math.util import xor_bytes, xor_str URL = 'https://aes.cryptohack.org/symmetry' diff --git a/examples/cryptohack-ecboracle.py b/examples/cryptohack-ecboracle.py index d4ce7aa..86c69c3 100644 --- a/examples/cryptohack-ecboracle.py +++ b/examples/cryptohack-ecboracle.py @@ -1,7 +1,7 @@ import requests -from imp.constants import PRINTABLE -from imp.attacks import paddingoracle +from celeste.constants import PRINTABLE +from celeste.attacks import paddingoracle from Crypto.Util.Padding import pad diff --git a/examples/local-ecboracle.py b/examples/local-ecboracle.py index 6902349..d3aeda7 100644 --- a/examples/local-ecboracle.py +++ b/examples/local-ecboracle.py @@ -1,6 +1,6 @@ import string -from imp.attacks import paddingoracle +from celeste.attacks import paddingoracle from Crypto.Cipher import AES from Crypto.Util.Padding import pad diff --git a/pyproject.toml b/pyproject.toml index 2572c5a..fac77e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "imp" +name = "Celeste" version = "0.1.0" description = "" authors = ["Emile Clark-Boman "] @@ -7,7 +7,6 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" -pycryptodome = "^3.23.0" requests = "^2.32.4" diff --git a/research/aparith/README b/research/aparith/README new file mode 100644 index 0000000..eca4c93 --- /dev/null +++ b/research/aparith/README @@ -0,0 +1,12 @@ +Comparing the speeds of various Nim libraries for +arbitrary precision integers. + +Test Targets: +https://github.com/status-im/nim-stint +https://github.com/nim-lang/bigints +https://github.com/michaeljclark/bignum +https://github.com/FedeOmoto/bignum +https://github.com/fsh/integers + +Test algorithms: +https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm#Algorithm diff --git a/research/aparith/aparith b/research/aparith/aparith new file mode 100755 index 0000000000000000000000000000000000000000..c74e32c1f67bf5b9dbd41a0f6b41ed62f9f5f580 GIT binary patch literal 93104 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%0A2t9 ziQB}Id?ZZ71*qAX zlb;N6Iw(n@hNOv!r4cwJu|$TMF?bxyCp8Z;kBjUoOSFDDTIx491@(ASi;7UvRk~|l z83Th}ab<2vViJR1aY+$`&VaEpK{wPDFzDswm!#^Y=auRe6oGDTE~$hHIXZdjf);at z@KDPhn9l^P6sC8-r9 z40@oo91MCz`Jl!lgI;O|boE9?N)dv`pjVPqTny0{p9os_0nrCzr&Q)8=4K`{=%weE zfC)XYH4r1gI+OErb5rwRJ1L+Gb;zVZ>xV!~$6)8F1VGQN2!I^h!4QmQE=Vo1kTL@U zXlpk7p5zr!{VSmQA**u`I$-PFU?OH{`eEm0ynyP5tqX*yhppFz@j>gZL2FK7?&pA> zFT(*<2wTSlJAVddKUA6lbZ64H|Nrx0`eEnSBtR9w)-$8)huQDPz`y`Hy9TBocD_vq zR6lJ07HB;pDC}VN!`A4&X%{s2`_cod`>fuX{10nk2NWcS1NzocA)D1n{Z1mS`9`oUNbdI8jaSp31( zwKG5$oED=Uq=cTIR-oyJ?YAp|E<|^M2Q0)s^zhq;rhmt8NQ&9<8>0RVETSM>@cB4U zCYU+^J#&f~YB22FnH{pAP+?&B0bPFxRt!}OV}s~p3=9l>p!M${4rre(^jsvEepq^e zO2h4kiC+TkAx6>9@p(+(&PgVezGF`j_x7l1|#G&{iUhoyt((C~xl zzX3hp>IQTnD@+~8Y!HUI2S&d^(+}Gh`2woH0;CBE!}P&uP&9)=8fHIiKS=^KGjoKcHv^nFZ4iThDHx2r`v{0o{Id{h)QQAeFFlL16a4^rP?D0;vV5L8hTW3=)Rp z0I2;xKnE!?Fff4j{VzvKKd@v8I`<3acDQB+1_lr4Mw{8NgL9ygFngg~2GII{Z2BK3 RK~!==D424Pcj0Uh0{|D{RS5t9 literal 0 HcmV?d00001 diff --git a/research/aparith/aparith.nimble b/research/aparith/aparith.nimble new file mode 100644 index 0000000..8581321 --- /dev/null +++ b/research/aparith/aparith.nimble @@ -0,0 +1,14 @@ +# Package + +version = "1.0.0" +author = "Emile Clark-Boman" +description = "Arbitrary Precision Integer Test - BigInts" +license = "MIT" +srcDir = "src" +bin = @["aparith", "speedtest_bigint"] + + +# Dependencies + +requires "nim >= 2.2.0" +requires "bigints >= 1.0.0" diff --git a/research/aparith/src/aparith.nim b/research/aparith/src/aparith.nim new file mode 100644 index 0000000..862d40c --- /dev/null +++ b/research/aparith/src/aparith.nim @@ -0,0 +1,5 @@ +# This is just an example to get you started. A typical binary package +# uses this file as the main entry point of the application. + +when isMainModule: + echo("Hello, World!") diff --git a/research/aparith/src/bigints.git.nim b/research/aparith/src/bigints.git.nim new file mode 100644 index 0000000..b00d466 --- /dev/null +++ b/research/aparith/src/bigints.git.nim @@ -0,0 +1,1311 @@ +## Arbitrary precision integers. +## +## The bitwise operations behave as if negative numbers were represented in 2's complement. + +import std/[algorithm, bitops, math, options] + +type + BigInt* = object + ## An arbitrary precision integer. + # Invariants for `a: BigInt`: + # * if `a` is non-zero: `a.limbs[a.limbs.high] != 0` + # * if `a` is zero: `a.limbs.len <= 1` + limbs: seq[uint32] + isNegative: bool + + +# forward declarations +func succ*(a: BigInt, b: int = 1): BigInt +func inc*(a: var BigInt, b: int = 1) +func dec*(a: var BigInt, b: int = 1) + + +func normalize(a: var BigInt) = + for i in countdown(a.limbs.high, 0): + if a.limbs[i] > 0'u32: + a.limbs.setLen(i+1) + return + a.limbs.setLen(1) + +func initBigInt*(vals: sink seq[uint32], isNegative = false): BigInt = + ## Initializes a `BigInt` from a sequence of `uint32` values. + runnableExamples: + let a = @[10'u32, 2'u32].initBigInt + let b = 10 + 2 shl 32 + assert $a == $b + result.limbs = vals + result.isNegative = isNegative + normalize(result) + +func initBigInt*[T: int8|int16|int32](val: T): BigInt = + if val < 0: + result.limbs = @[(not val).uint32 + 1] # manual 2's complement (to avoid overflow) + result.isNegative = true + else: + result.limbs = @[val.uint32] + result.isNegative = false + +func initBigInt*[T: uint8|uint16|uint32](val: T): BigInt = + result.limbs = @[val.uint32] + +func initBigInt*(val: int64): BigInt = + var a = val.uint64 + if val < 0: + a = not a + 1 # 2's complement + result.isNegative = true + if a > uint32.high: + result.limbs = @[(a and uint32.high).uint32, (a shr 32).uint32] + else: + result.limbs = @[a.uint32] + +func initBigInt*(val: uint64): BigInt = + if val > uint32.high: + result.limbs = @[(val and uint32.high).uint32, (val shr 32).uint32] + else: + result.limbs = @[val.uint32] + +when sizeof(int) == 4: + template initBigInt*(val: int): BigInt = initBigInt(val.int32) + template initBigInt*(val: uint): BigInt = initBigInt(val.uint32) +else: + template initBigInt*(val: int): BigInt = initBigInt(val.int64) + template initBigInt*(val: uint): BigInt = initBigInt(val.uint64) + +func initBigInt*(val: BigInt): BigInt = + result = val + +const + zero = initBigInt(0) + one = initBigInt(1) + +func isZero(a: BigInt): bool {.inline.} = + a.limbs.len == 0 or (a.limbs.len == 1 and a.limbs[0] == 0) + +func abs*(a: BigInt): BigInt = + # Returns the absolute value of `a`. + runnableExamples: + assert abs(42.initBigInt) == 42.initBigInt + assert abs(-12.initBigInt) == 12.initBigInt + result = a + result.isNegative = false + +func unsignedCmp(a: BigInt, b: uint32): int64 = + # ignores the sign of `a` + # `a` and `b` are assumed to not be zero + result = int64(a.limbs.len) - 1 + if result != 0: return + result = int64(a.limbs[0]) - int64(b) + +func unsignedCmp(a: uint32, b: BigInt): int64 = -unsignedCmp(b, a) + +func unsignedCmp(a, b: BigInt): int64 = + # ignores the signs of `a` and `b` + # `a` and `b` are assumed to not be zero + result = int64(a.limbs.len) - int64(b.limbs.len) + if result != 0: return + for i in countdown(a.limbs.high, 0): + result = int64(a.limbs[i]) - int64(b.limbs[i]) + if result != 0: + return + +func cmp(a, b: BigInt): int64 = + ## Returns: + ## * a value less than zero, if `a < b` + ## * a value greater than zero, if `a > b` + ## * zero, if `a == b` + if a.isZero: + if b.isZero: + return 0 + elif b.isNegative: + return 1 + else: + return -1 + elif a.isNegative: + if b.isZero or not b.isNegative: + return -1 + else: + return unsignedCmp(b, a) + else: # a > 0 + if b.isZero or b.isNegative: + return 1 + else: + return unsignedCmp(a, b) + +func cmp(a: BigInt, b: int32): int64 = + ## Returns: + ## * a value less than zero, if `a < b` + ## * a value greater than zero, if `a > b` + ## * zero, if `a == b` + if a.isZero: + return -b.int64 + elif a.isNegative: + if b < 0: + return unsignedCmp((not b).uint32 + 1, a) + else: + return -1 + else: # a > 0 + if b <= 0: + return 1 + else: + return unsignedCmp(a, b.uint32) + +func cmp(a: int32, b: BigInt): int64 = -cmp(b, a) + +func `==`*(a, b: BigInt): bool = + ## Compares if two `BigInt` numbers are equal. + runnableExamples: + let + a = 5.initBigInt + b = 3.initBigInt + c = 2.initBigInt + assert a == b + c + assert b != c + cmp(a, b) == 0 + +func `<`*(a, b: BigInt): bool = + runnableExamples: + let + a = 5.initBigInt + b = 3.initBigInt + c = 2.initBigInt + assert b < a + assert b > c + cmp(a, b) < 0 + +func `<=`*(a, b: BigInt): bool = + runnableExamples: + let + a = 5.initBigInt + b = 3.initBigInt + c = 2.initBigInt + assert a <= b + c + assert c <= b + cmp(a, b) <= 0 + +func `==`(a: BigInt, b: int32): bool = cmp(a, b) == 0 +func `<`(a: BigInt, b: int32): bool = cmp(a, b) < 0 +func `<`(a: int32, b: BigInt): bool = cmp(a, b) < 0 + +template addParts(toAdd) = + tmp += toAdd + a.limbs[i] = uint32(tmp and uint32.high) + tmp = tmp shr 32 + +func unsignedAdditionInt(a: var BigInt, b: BigInt, c: uint32) = + let bl = b.limbs.len + a.limbs.setLen(bl) + + var tmp: uint64 = uint64(c) + for i in 0 ..< bl: + addParts(uint64(b.limbs[i])) + if tmp > 0'u64: + a.limbs.add(uint32(tmp)) + a.isNegative = false + +func unsignedAddition(a: var BigInt, b, c: BigInt) = + let + bl = b.limbs.len + cl = c.limbs.len + var m = min(bl, cl) + a.limbs.setLen(max(bl, cl)) + + var tmp = 0'u64 + for i in 0 ..< m: + addParts(uint64(b.limbs[i]) + uint64(c.limbs[i])) + if bl < cl: + for i in m ..< cl: + addParts(uint64(c.limbs[i])) + else: + for i in m ..< bl: + addParts(uint64(b.limbs[i])) + if tmp > 0'u64: + a.limbs.add(uint32(tmp)) + a.isNegative = false + +func negate(a: var BigInt) = + a.isNegative = not a.isNegative + +func `-`*(a: BigInt): BigInt = + ## Unary minus for `BigInt`. + runnableExamples: + let + a = 5.initBigInt + b = -10.initBigInt + assert (-a) == -5.initBigInt + assert (-b) == 10.initBigInt + result = a + negate(result) + +template realUnsignedSubtractionInt(a: var BigInt, b: BigInt, c: uint32) = + # b > c + let bl = b.limbs.len + a.limbs.setLen(bl) + + var tmp = int64(c) + for i in 0 ..< bl: + tmp = int64(uint32.high) + 1 + int64(b.limbs[i]) - tmp + a.limbs[i] = uint32(tmp and int64(uint32.high)) + tmp = 1 - (tmp shr 32) + a.isNegative = false + + normalize(a) + assert tmp == 0 + +template realUnsignedSubtraction(a: var BigInt, b, c: BigInt) = + # b > c + let + bl = b.limbs.len + cl = c.limbs.len + var m = min(bl, cl) + a.limbs.setLen(max(bl, cl)) + + var tmp = 0'i64 + for i in 0 ..< m: + tmp = int64(uint32.high) + 1 + int64(b.limbs[i]) - int64(c.limbs[i]) - tmp + a.limbs[i] = uint32(tmp and int64(uint32.high)) + tmp = 1 - (tmp shr 32) + if bl < cl: + for i in m ..< cl: + tmp = int64(uint32.high) + 1 - int64(c.limbs[i]) - tmp + a.limbs[i] = uint32(tmp and int64(uint32.high)) + tmp = 1 - (tmp shr 32) + a.isNegative = true + else: + for i in m ..< bl: + tmp = int64(uint32.high) + 1 + int64(b.limbs[i]) - tmp + a.limbs[i] = uint32(tmp and int64(uint32.high)) + tmp = 1 - (tmp shr 32) + a.isNegative = false + + normalize(a) + assert tmp == 0 + +func unsignedSubtractionInt(a: var BigInt, b: BigInt, c: uint32) = + # `b` is not zero + let cmpRes = unsignedCmp(b, c) + if cmpRes > 0: + realUnsignedSubtractionInt(a, b, c) + elif cmpRes < 0: + # `b` is only a single limb + a.limbs = @[c - b.limbs[0]] + a.isNegative = true + else: # b == c + a = zero + +func unsignedSubtraction(a: var BigInt, b, c: BigInt) = + let cmpRes = unsignedCmp(b, c) + if cmpRes > 0: + realUnsignedSubtraction(a, b, c) + elif cmpRes < 0: + realUnsignedSubtraction(a, c, b) + a.negate() + else: # b == c + a = zero + +func additionInt(a: var BigInt, b: BigInt, c: int32) = + # a = b + c + if b.isZero: + a = c.initBigInt + elif b.isNegative: + if c < 0: + unsignedAdditionInt(a, b, (not c).uint32 + 1) + else: + unsignedSubtractionInt(a, b, c.uint32) + a.negate() + else: + if c < 0: + unsignedSubtractionInt(a, b, (not c).uint32 + 1) + else: + unsignedAdditionInt(a, b, c.uint32) + +func addition(a: var BigInt, b, c: BigInt) = + # a = b + c + if b.isNegative: + if c.isNegative: + unsignedAddition(a, b, c) + a.isNegative = true + else: + unsignedSubtraction(a, c, b) + else: + if c.isNegative: + unsignedSubtraction(a, b, c) + else: + unsignedAddition(a, b, c) + +func `+`*(a, b: BigInt): BigInt = + ## Addition for `BigInt`s. + runnableExamples: + let + a = 5.initBigInt + b = 10.initBigInt + assert a + b == 15.initBigInt + assert (-a) + b == 5.initBigInt + assert a + (-b) == -5.initBigInt + addition(result, a, b) + +template `+=`*(a: var BigInt, b: BigInt) = + runnableExamples: + var a = 5.initBigInt + a += 2.initBigInt + assert a == 7.initBigInt + a = a + b + +func subtractionInt(a: var BigInt, b: BigInt, c: int32) = + # a = b - c + if b.isZero: + a = -c.initBigInt + elif b.isNegative: + if c < 0: + unsignedSubtractionInt(a, b, (not c).uint32 + 1) + else: + unsignedAdditionInt(a, b, c.uint32) + a.negate() + else: + if c < 0: + unsignedAdditionInt(a, b, (not c).uint32 + 1) + else: + unsignedSubtractionInt(a, b, c.uint32) + +func subtraction(a: var BigInt, b, c: BigInt) = + # a = b - c + if b.isNegative: + if c.isNegative: + unsignedSubtraction(a, c, b) + else: + unsignedAddition(a, b, c) + a.isNegative = true + else: + if c.isNegative: + unsignedAddition(a, b, c) + else: + unsignedSubtraction(a, b, c) + +func `-`*(a, b: BigInt): BigInt = + ## Subtraction for `BigInt`s. + runnableExamples: + let + a = 15.initBigInt + b = 10.initBigInt + assert a - b == 5.initBigInt + assert (-a) - b == -25.initBigInt + assert a - (-b) == 25.initBigInt + subtraction(result, a, b) + +template `-=`*(a: var BigInt, b: BigInt) = + runnableExamples: + var a = 5.initBigInt + a -= 2.initBigInt + assert a == 3.initBigInt + a = a - b + + +func unsignedMultiplication(a: var BigInt, b, c: BigInt) {.inline.} = + # always called with bl >= cl + let + bl = b.limbs.len + cl = c.limbs.len + a.limbs.setLen(bl + cl) + var tmp = 0'u64 + + for i in 0 ..< bl: + tmp += uint64(b.limbs[i]) * uint64(c.limbs[0]) + a.limbs[i] = uint32(tmp and uint32.high) + tmp = tmp shr 32 + + a.limbs[bl] = uint32(tmp) + + for j in 1 ..< cl: + tmp = 0'u64 + for i in 0 ..< bl: + tmp += uint64(a.limbs[j + i]) + uint64(b.limbs[i]) * uint64(c.limbs[j]) + a.limbs[j + i] = uint32(tmp and uint32.high) + tmp = tmp shr 32 + var pos = j + bl + while tmp > 0'u64: + tmp += uint64(a.limbs[pos]) + a.limbs[pos] = uint32(tmp and uint32.high) + tmp = tmp shr 32 + inc pos + normalize(a) + +func multiplication(a: var BigInt, b, c: BigInt) = + # a = b * c + if b.isZero or c.isZero: + a = zero + return + let + bl = b.limbs.len + cl = c.limbs.len + + if cl > bl: + unsignedMultiplication(a, c, b) + else: + unsignedMultiplication(a, b, c) + a.isNegative = b.isNegative xor c.isNegative + +func `*`*(a, b: BigInt): BigInt = + ## Multiplication for `BigInt`s. + runnableExamples: + let + a = 421.initBigInt + b = 200.initBigInt + assert a * b == 84200.initBigInt + multiplication(result, a, b) + +template `*=`*(a: var BigInt, b: BigInt) = + runnableExamples: + var a = 15.initBigInt + a *= 10.initBigInt + assert a == 150.initBigInt + a = a * b + +func pow*(x: BigInt, y: Natural): BigInt = + ## Computes `x` to the power of `y`. + var base = x + var exp = y + result = one + + # binary exponentiation + while exp > 0: + if (exp and 1) > 0: + result *= base + exp = exp shr 1 + base *= base + +func `shl`*(x: BigInt, y: Natural): BigInt = + ## Shifts a `BigInt` to the left. + runnableExamples: + let a = 24.initBigInt + assert a shl 1 == 48.initBigInt + assert a shl 2 == 96.initBigInt + + if x.isZero: + return x + + var carry = 0'u64 + let a = y div 32 + let b = uint32(y mod 32) + let mask = ((1'u64 shl b) - 1) shl (64 - b) + result.limbs.setLen(x.limbs.len + a) + result.isNegative = x.isNegative + + for i in countup(0, x.limbs.high): + let acc = (uint64(x.limbs[i]) shl 32) or carry + carry = (acc and mask) shr 32 + result.limbs[i + a] = uint32((acc shl b) shr 32) + + if carry > 0: + result.limbs.add(uint32(carry shr (32 - b))) + +func `shr`*(x: BigInt, y: Natural): BigInt = + ## Shifts a `BigInt` to the right (arithmetically). + runnableExamples: + let a = 24.initBigInt + assert a shr 1 == 12.initBigInt + assert a shr 2 == 6.initBigInt + + var carry = 0'u64 + let a = y div 32 + if a >= x.limbs.len: + return zero + let b = uint32(y mod 32) + let mask = (1'u32 shl b) - 1 + result.limbs.setLen(x.limbs.len - a) + result.isNegative = x.isNegative + + for i in countdown(x.limbs.high, a): + let acc = (carry shl 32) or x.limbs[i] + carry = acc and mask + result.limbs[i - a] = uint32(acc shr b) + + if result.isNegative: + var underflow = false + if carry > 0: + underflow = true + else: + for i in 0 .. a - 1: + if x.limbs[i] > 0: + underflow = true + break + + if underflow: + dec result + + if result.limbs.len > 1 and result.limbs[result.limbs.high] == 0: + # normalize + result.limbs.setLen(result.limbs.high) + + +# bitwise operations + +func invertIn(a: BigInt): BigInt {.inline.} = + result = a + result.isNegative = false + dec(result) + +func invertOut(a: var BigInt) {.inline.} = + inc(a) + a.isNegative = true + +func `not`*(a: BigInt): BigInt = + ## Bitwise `not` for `BigInt`s. + # 2's complement: -x = not x + 1 <=> not x = -x - 1 = -(x + 1) + result = succ(a) + negate(result) + +func `and`*(a, b: BigInt): BigInt = + ## Bitwise `and` for `BigInt`s. + if a.isNegative and not a.isZero: + if b.isNegative and not b.isZero: + # - and - + let a = invertIn(a) + let b = invertIn(b) + result.limbs.setLen(max(a.limbs.len, b.limbs.len)) + let m = min(a.limbs.len, b.limbs.len) + for i in 0 ..< m: + result.limbs[i] = a.limbs[i] or b.limbs[i] + for i in m ..< a.limbs.len: + result.limbs[i] = a.limbs[i] + for i in m ..< b.limbs.len: + result.limbs[i] = b.limbs[i] + invertOut(result) + else: + # - and + + let a = invertIn(a) + result = b + for i in 0 ..< min(a.limbs.len, b.limbs.len): + result.limbs[i] = (not a.limbs[i]) and b.limbs[i] + else: + if b.isNegative and not b.isZero: + # + and - + let b = invertIn(b) + result = a + for i in 0 ..< min(a.limbs.len, b.limbs.len): + result.limbs[i] = a.limbs[i] and (not b.limbs[i]) + else: + # + and + + result.limbs.setLen(min(a.limbs.len, b.limbs.len)) + for i in 0 ..< result.limbs.len: + result.limbs[i] = a.limbs[i] and b.limbs[i] + normalize(result) + +func `or`*(a, b: BigInt): BigInt = + ## Bitwise `or` for `BigInt`s. + if a.isNegative and not a.isZero: + if b.isNegative and not b.isZero: + # - or - + let a = invertIn(a) + let b = invertIn(b) + result.limbs.setLen(min(a.limbs.len, b.limbs.len)) + for i in 0 ..< result.limbs.len: + result.limbs[i] = a.limbs[i] and b.limbs[i] + invertOut(result) + else: + # - or + + let a = invertIn(a) + result = a + for i in 0 ..< min(a.limbs.len, b.limbs.len): + result.limbs[i] = a.limbs[i] and (not b.limbs[i]) + invertOut(result) + else: + if b.isNegative and not b.isZero: + # + or - + let b = invertIn(b) + result = b + for i in 0 ..< min(a.limbs.len, b.limbs.len): + result.limbs[i] = (not a.limbs[i]) and b.limbs[i] + invertOut(result) + else: + # + or + + result.limbs.setLen(max(a.limbs.len, b.limbs.len)) + let m = min(a.limbs.len, b.limbs.len) + for i in 0 ..< m: + result.limbs[i] = a.limbs[i] or b.limbs[i] + for i in m ..< a.limbs.len: + result.limbs[i] = a.limbs[i] + for i in m ..< b.limbs.len: + result.limbs[i] = b.limbs[i] + normalize(result) + +func `xor`*(a, b: BigInt): BigInt = + ## Bitwise `xor` for `BigInt`s. + if a.isNegative and not a.isZero: + if b.isNegative and not b.isZero: + # - xor - + let a = invertIn(a) + let b = invertIn(b) + result.limbs.setLen(max(a.limbs.len, b.limbs.len)) + let m = min(a.limbs.len, b.limbs.len) + for i in 0 ..< m: + result.limbs[i] = a.limbs[i] xor b.limbs[i] + for i in m ..< a.limbs.len: + result.limbs[i] = a.limbs[i] + for i in m ..< b.limbs.len: + result.limbs[i] = b.limbs[i] + else: + # - xor + + let a = invertIn(a) + result.limbs.setLen(max(a.limbs.len, b.limbs.len)) + let m = min(a.limbs.len, b.limbs.len) + for i in 0 ..< m: + result.limbs[i] = a.limbs[i] xor b.limbs[i] + for i in m ..< a.limbs.len: + result.limbs[i] = a.limbs[i] + for i in m ..< b.limbs.len: + result.limbs[i] = b.limbs[i] + invertOut(result) + else: + if b.isNegative and not b.isZero: + # + xor - + let b = invertIn(b) + result.limbs.setLen(max(a.limbs.len, b.limbs.len)) + let m = min(a.limbs.len, b.limbs.len) + for i in 0 ..< m: + result.limbs[i] = a.limbs[i] xor b.limbs[i] + for i in m ..< a.limbs.len: + result.limbs[i] = a.limbs[i] + for i in m ..< b.limbs.len: + result.limbs[i] = b.limbs[i] + invertOut(result) + else: + # + xor + + result.limbs.setLen(max(a.limbs.len, b.limbs.len)) + let m = min(a.limbs.len, b.limbs.len) + for i in 0 ..< m: + result.limbs[i] = a.limbs[i] xor b.limbs[i] + for i in m ..< a.limbs.len: + result.limbs[i] = a.limbs[i] + for i in m ..< b.limbs.len: + result.limbs[i] = b.limbs[i] + normalize(result) + + +func reset(a: var BigInt) = + ## Resets a `BigInt` back to the zero value. + a.limbs.setLen(1) + a.limbs[0] = 0 + a.isNegative = false + +func unsignedDivRem(q: var BigInt, r: var uint32, n: BigInt, d: uint32) = + q.limbs.setLen(n.limbs.len) + r = 0 + for i in countdown(n.limbs.high, 0): + let tmp = uint64(n.limbs[i]) + uint64(r) shl 32 + q.limbs[i] = uint32(tmp div d) + r = uint32(tmp mod d) + normalize(q) + +func bits(d: uint32): int = + const bitLengths = [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5] + var d = d + while d >= 32'u32: + result += 6 + d = d shr 6 + result += bitLengths[int(d)] + +# From Knuth and Python +func unsignedDivRem(q, r: var BigInt, n, d: BigInt) = + var + nn = n.limbs.len + dn = d.limbs.len + + if n.isZero: + q = zero + r = zero + elif nn < dn: + # n < d + q = zero + r = n + elif dn == 1: + var x: uint32 + unsignedDivRem(q, x, n, d.limbs[0]) + r.limbs = @[x] + r.isNegative = false + else: + assert nn >= dn and dn >= 2 + + # normalize + let ls = 32 - bits(d.limbs[d.limbs.high]) + r = d shl ls + q = n shl ls + if q.limbs.len > n.limbs.len or q.limbs[q.limbs.high] >= r.limbs[r.limbs.high]: + q.limbs.add(0'u32) + inc(nn) + + let k = nn - dn + assert k >= 0 + var a: BigInt + a.limbs.setLen(k) + let wm1 = r.limbs[r.limbs.high] + let wm2 = r.limbs[r.limbs.high-1] + var ak = k + + var zhi = zero + var z = zero + var qib = zero + var q1b = zero + + for v in countdown(k-1, 0): + # estimate quotient digit, may rarely overestimate by 1 + let vtop = q.limbs[v + dn] + assert vtop <= wm1 + let vv = (uint64(vtop) shl 32) or q.limbs[v+dn-1] + var q1 = vv div wm1 + var r1 = vv mod wm1 + + while (wm2 * q1) > ((r1 shl 32) or q.limbs[v+dn-2]): + dec q1 + r1 += wm1 + if r1 > uint32.high: + break + + assert q1 <= uint32.high + + q1b.limbs[0] = uint32(q1) + + # subtract + zhi.reset() + for i in 0 ..< dn: + z.reset() + z.limbs[0] = r.limbs[i] + z *= q1b + z.isNegative = true + z += zhi + var z1 = z + qib.limbs[0] = q.limbs[v+i] + z += qib + + if z < 0: + q.limbs[v+i] = not z.limbs[0] + 1 + else: + q.limbs[v+i] = z.limbs[0] + + if z.limbs.len > 1: + zhi.limbs[0] = z1.limbs[1] + if z1.limbs[0] > qib.limbs[0]: + zhi.limbs[0] += 1 + zhi.isNegative = true + elif z < 0: + zhi.limbs[0] = 1 + zhi.isNegative = true + else: + zhi.reset() + + # add back if was too large (rare branch) + if vtop.initBigInt + zhi < 0: + var carry = 0'u64 + for i in 0 ..< dn: + carry += q.limbs[v+i] + carry += r.limbs[i] + q.limbs[v+i] = uint32(carry and uint32.high) + carry = carry shr 32 + dec(q1) + + # store quotient digit + assert q1 <= uint32.high + dec(ak) + a.limbs[ak] = uint32(q1) + + # unshift remainder, we reuse w1 to store the result + q.limbs.setLen(dn) + r = q shr ls + + normalize(r) + q = a + normalize(q) + +func division(q, r: var BigInt, n, d: BigInt) = + # q = n div d + # r = n mod d + if d.isZero: + raise newException(DivByZeroDefect, "division by zero") + + unsignedDivRem(q, r, n, d) + + q.isNegative = n < 0 xor d < 0 + r.isNegative = n < 0 and r != 0 + + # divrem -> divmod + if (r < 0 and d > 0) or (r > 0 and d < 0): + r += d + q -= one + +func `div`*(a, b: BigInt): BigInt = + ## Computes the integer division of two `BigInt` numbers. + ## Raises a `DivByZeroDefect` if `b` is zero. + ## + ## If you also need the modulo (remainder), use the `divmod func <#divmod,BigInt,BigInt>`_. + runnableExamples: + let + a = 17.initBigInt + b = 5.initBigInt + assert a div b == 3.initBigInt + assert (-a) div b == -4.initBigInt + assert a div (-b) == -4.initBigInt + assert (-a) div (-b) == 3.initBigInt + var tmp: BigInt + division(result, tmp, a, b) + +func `mod`*(a, b: BigInt): BigInt = + ## Computes the integer modulo (remainder) of two `BigInt` numbers. + ## Raises a `DivByZeroDefect` if `b` is zero. + ## + ## If you also need an integer division, use the `divmod func <#divmod,BigInt,BigInt>`_. + runnableExamples: + let + a = 17.initBigInt + b = 5.initBigInt + assert a mod b == 2.initBigInt + assert (-a) mod b == 3.initBigInt + assert a mod (-b) == -3.initBigInt + assert (-a) mod (-b) == -2.initBigInt + var tmp: BigInt + division(tmp, result, a, b) + +func divmod*(a, b: BigInt): tuple[q, r: BigInt] = + ## Computes both the integer division and modulo (remainder) of two + ## `BigInt` numbers. + ## Raises a `DivByZeroDefect` if `b` is zero. + runnableExamples: + let + a = 17.initBigInt + b = 5.initBigInt + assert divmod(a, b) == (3.initBigInt, 2.initBigInt) + division(result.q, result.r, a, b) + +func countTrailingZeroBits(a: BigInt): int = + var count = 0 + for x in a.limbs: + if x == 0: + count += 32 + else: + return count + countTrailingZeroBits(x) + return count + +func gcd*(a, b: BigInt): BigInt = + ## Returns the greatest common divisor (GCD) of `a` and `b`. + runnableExamples: + assert gcd(54.initBigInt, 24.initBigInt) == 6.initBigInt + + # binary GCD algorithm + var + u = abs(a) + v = abs(b) + if u.isZero: + return v + elif v.isZero: + return u + let + i = countTrailingZeroBits(u) + j = countTrailingZeroBits(v) + k = min(i, j) + u = u shr i + v = v shr j + while true: + # u and v are odd + if u > v: + swap(u, v) + v -= u + if v.isZero: + return u shl k + v = v shr countTrailingZeroBits(v) + + +func toInt*[T: SomeInteger](x: BigInt): Option[T] = + ## Converts a `BigInt` number to an integer, if possible. + ## If the `BigInt` doesn't fit in a `T`, returns `none(T)`; + ## otherwise returns `some(x)`. + runnableExamples: + import std/options + let + a = 44.initBigInt + b = 130.initBigInt + assert toInt[int8](a) == some(44'i8) + assert toInt[int8](b) == none(int8) + assert toInt[uint8](b) == some(130'u8) + assert toInt[int](b) == some(130) + + if x.isZero: + return some(default(T)) # default(T) is 0 + when T is SomeSignedInt: + # T is signed + when sizeof(T) == 8: + if x.limbs.len > 2: + result = none(T) + elif x.limbs.len == 2: + if x.isNegative: + if x.limbs[1] > uint32(int32.high) + 1 or (x.limbs[1] == uint32(int32.high) + 1 and x.limbs[0] > 0): + result = none(T) + else: + let value = not T(x.limbs[1].uint64 shl 32 + x.limbs[0] - 1) + result = some(value) + else: + if x.limbs[1] > uint32(int32.high): + result = none(T) + else: + let value = T(x.limbs[1].uint64 shl 32 + x.limbs[0]) + result = some(value) + else: + if x.isNegative: + result = some(not T(x.limbs[0] - 1)) + else: + result = some(T(x.limbs[0])) + else: + if x.limbs.len > 1: + result = none(T) + else: + if x.isNegative: + if x.limbs[0] > uint32(T.high) + 1: + result = none(T) + else: + result = some(not T(x.limbs[0] - 1)) + else: + if x.limbs[0] > uint32(T.high): + result = none(T) + else: + result = some(T(x.limbs[0])) + else: + # T is unsigned + if x.isNegative: + return none(T) + when sizeof(T) == 8: + if x.limbs.len > 2: + result = none(T) + elif x.limbs.len == 2: + let value = T(x.limbs[1]) shl 32 + T(x.limbs[0]) + result = some(value) + else: + result = some(T(x.limbs[0])) + else: + if x.limbs.len > 1: + result = none(T) + elif x.limbs[0] > uint32(T.high): + result = none(T) + else: + result = some(T(x.limbs[0])) + +func calcSizes(): array[2..36, int] = + for i in 2..36: + var x = int64(i) + while x <= int64(uint32.high) + 1: + x *= i + result[i].inc + +const + digits = "0123456789abcdefghijklmnopqrstuvwxyz" + powers = {2'u8, 4, 8, 16, 32} + sizes = calcSizes() # `sizes[base]` is the maximum number of digits that fully fit in a `uint32` + +func toString*(a: BigInt, base: range[2..36] = 10): string = + ## Produces a string representation of a `BigInt` in a specified + ## `base`. + ## + ## Doesn't produce any prefixes (`0x`, `0b`, etc.). + runnableExamples: + let a = 55.initBigInt + assert toString(a) == "55" + assert toString(a, 2) == "110111" + assert toString(a, 16) == "37" + + if a.isZero: + return "0" + + let size = sizes[base] + if base.uint8 in powers: + let + bits = countTrailingZeroBits(base) # bits per digit + mask = (1'u32 shl bits) - 1 + totalBits = 32 * a.limbs.len - countLeadingZeroBits(a.limbs[a.limbs.high]) + result = newStringOfCap((totalBits + bits - 1) div bits + 1) + + var + acc = 0'u32 + accBits = 0 # the number of bits needed for acc + for x in a.limbs: + acc = acc or (x shl accBits) + accBits += 32 + while accBits >= bits: + result.add(digits[acc and mask]) + acc = acc shr bits + if accBits > 32: + acc = x shr (32 - (accBits - bits)) + accBits -= bits + if acc > 0: + result.add(digits[acc]) + else: + let + base = uint32(base) + d = base ^ size + var tmp = a + + tmp.isNegative = false + result = newStringOfCap(size * a.limbs.len + 1) # estimate the length of the result + + while tmp > 0: + var + c: uint32 + tmpCopy = tmp + unsignedDivRem(tmp, c, tmpCopy, d) + for i in 1..size: + result.add(digits[c mod base]) + c = c div base + + # normalize + var i = result.high + while i > 0 and result[i] == '0': + dec i + result.setLen(i+1) + + if a.isNegative: + result.add('-') + + result.reverse() + +func `$`*(a: BigInt): string = + ## String representation of a `BigInt` in base 10. + toString(a, 10) + +func parseDigit(c: char, base: uint32): uint32 {.inline.} = + result = case c + of '0'..'9': uint32(ord(c) - ord('0')) + of 'a'..'z': uint32(ord(c) - ord('a') + 10) + of 'A'..'Z': uint32(ord(c) - ord('A') + 10) + else: raise newException(ValueError, "Invalid input: " & c) + + if result >= base: + raise newException(ValueError, "Invalid input: " & c) + +func filterUnderscores(str: var string) {.inline.} = + var k = 0 # the amount of underscores + for i in 0 .. str.high: + let c = str[i] + if c == '_': + inc k + elif k > 0: + str[i - k] = c + str.setLen(str.len - k) + +func initBigInt*(str: string, base: range[2..36] = 10): BigInt = + ## Create a `BigInt` from a string. For invalid inputs, a `ValueError` exception is raised. + runnableExamples: + let + a = initBigInt("1234") + b = initBigInt("1234", base = 8) + assert a == 1234.initBigInt + assert b == 668.initBigInt + + if str.len == 0: + raise newException(ValueError, "Empty input") + + let size = sizes[base] + let base = base.uint32 + var first = 0 + var neg = false + + case str[0] + of '-': + if str.len == 1: + raise newException(ValueError, "Invalid input: " & str) + first = 1 + neg = true + of '+': + if str.len == 1: + raise newException(ValueError, "Invalid input: " & str) + first = 1 + else: + discard + if str[first] == '_': + raise newException(ValueError, "A number can not begin with _") + if str[^1] == '_': + raise newException(ValueError, "A number can not end with _") + + if base.uint8 in powers: + # base is a power of two, so each digit corresponds to a block of bits + let bits = countTrailingZeroBits(base) # bits per digit + var + acc = 0'u32 + accBits = 0 # the number of bits needed for acc + for i in countdown(str.high, first): + if str[i] != '_': + let digit = parseDigit(str[i], base) + acc = acc or (digit shl accBits) + accBits += bits + if accBits >= 32: + result.limbs.add(acc) + accBits -= 32 + acc = digit shr (bits - accBits) + if acc > 0: + result.limbs.add(acc) + result.normalize() + else: + var str = str + filterUnderscores(str) + let d = initBigInt(base ^ size) + for i in countup(first, str.high, size): + var num = 0'u32 # the accumulator in this block + if i + size <= str.len: + # iterator over a block of length `size`, so we can use `d` + for j in countup(i, i + size - 1): + if str[j] != '_': + let digit = parseDigit(str[j], base) + num = (num * base) + digit + unsignedAdditionInt(result, result * d, num) + else: + # iterator over a block smaller than `size`, so we have to compute `mul` + var mul = 1'u32 # the multiplication factor for num + for j in countup(i, min(i + size - 1, str.high)): + if str[j] != '_': + let digit = parseDigit(str[j], base) + num = (num * base) + digit + mul *= base + unsignedAdditionInt(result, result * initBigInt(mul), num) + + result.isNegative = neg + +when (NimMajor, NimMinor) >= (1, 5): + include bigints/private/literals + +func inc*(a: var BigInt, b: int = 1) = + ## Increase the value of a `BigInt` by the specified amount (default: 1). + runnableExamples: + var a = 15.initBigInt + inc a + assert a == 16.initBigInt + inc(a, 7) + assert a == 23.initBigInt + + if b in int32.low..int32.high: + var c = a + additionInt(a, c, b.int32) + else: + a += initBigInt(b) + +func dec*(a: var BigInt, b: int = 1) = + ## Decrease the value of a `BigInt` by the specified amount (default: 1). + runnableExamples: + var a = 15.initBigInt + dec a + assert a == 14.initBigInt + dec(a, 5) + assert a == 9.initBigInt + + if b in int32.low..int32.high: + var c = a + subtractionInt(a, c, b.int32) + else: + a -= initBigInt(b) + +func succ*(a: BigInt, b: int = 1): BigInt = + ## Returns the `b`-th successor of a `BigInt`. + result = a + inc(result, b) + +func pred*(a: BigInt, b: int = 1): BigInt = + ## Returns the `b`-th predecessor of a `BigInt`. + result = a + dec(result, b) + + +iterator countup*(a, b: BigInt, step: int32 = 1): BigInt = + ## Counts from `a` up to `b` (inclusive) with the given step count. + var res = a + while res <= b: + yield res + inc(res, step) + +iterator countdown*(a, b: BigInt, step: int32 = 1): BigInt = + ## Counts from `a` down to `b` (inclusive) with the given step count. + var res = a + while res >= b: + yield res + dec(res, step) + +iterator `..`*(a, b: BigInt): BigInt = + ## Counts from `a` up to `b` (inclusive). + var res = a + while res <= b: + yield res + inc res + +iterator `..<`*(a, b: BigInt): BigInt = + ## Counts from `a` up to `b` (exclusive). + var res = a + while res < b: + yield res + inc res + + +func modulo(a, modulus: BigInt): BigInt = + ## Like `mod`, but the result is always in the range `[0, modulus-1]`. + ## `modulus` should be greater than zero. + result = a mod modulus + if result < 0: + result += modulus + +func fastLog2*(a: BigInt): int = + ## Computes the logarithm in base 2 of `a`. + ## If `a` is negative, returns the logarithm of `abs(a)`. + ## If `a` is zero, returns -1. + if a.isZero: + return -1 + bitops.fastLog2(a.limbs[^1]) + 32*(a.limbs.high) + + +func invmod*(a, modulus: BigInt): BigInt = + ## Compute the modular inverse of `a` modulo `modulus`. + ## The return value is always in the range `[1, modulus-1]` + runnableExamples: + assert invmod(3.initBigInt, 7.initBigInt) == 5.initBigInt + + # extended Euclidean algorithm + if modulus.isZero: + raise newException(DivByZeroDefect, "modulus must be nonzero") + elif modulus.isNegative: + raise newException(ValueError, "modulus must be strictly positive") + elif a.isZero: + raise newException(DivByZeroDefect, "0 has no modular inverse") + else: + var + r0 = modulus + r1 = a.modulo(modulus) + t0 = zero + t1 = one + var rk, tk: BigInt # otherwise t1 is incorrectly inferred as cursor (https://github.com/nim-lang/Nim/issues/19457) + while r1 > 0: + let q = r0 div r1 + rk = r0 - q * r1 + tk = t0 - q * t1 + r0 = r1 + r1 = rk + t0 = t1 + t1 = tk + if r0 != one: + raise newException(ValueError, $a & " has no modular inverse modulo " & $modulus) + result = t0.modulo(modulus) + +func powmod*(base, exponent, modulus: BigInt): BigInt = + ## Compute modular exponentation of `base` with power `exponent` modulo `modulus`. + ## The return value is always in the range `[0, modulus-1]`. + runnableExamples: + assert powmod(2.initBigInt, 3.initBigInt, 7.initBigInt) == 1.initBigInt + if modulus.isZero: + raise newException(DivByZeroDefect, "modulus must be nonzero") + elif modulus.isNegative: + raise newException(ValueError, "modulus must be strictly positive") + elif modulus == 1: + return zero + else: + var + base = base + exponent = exponent + if exponent < 0: + base = invmod(base, modulus) + exponent = -exponent + var basePow = base.modulo(modulus) + result = one + while not exponent.isZero: + if (exponent.limbs[0] and 1) != 0: + result = (result * basePow) mod modulus + basePow = (basePow * basePow) mod modulus + exponent = exponent shr 1 diff --git a/research/aparith/src/bigints.git/private/literals.nim b/research/aparith/src/bigints.git/private/literals.nim new file mode 100644 index 0000000..c58d551 --- /dev/null +++ b/research/aparith/src/bigints.git/private/literals.nim @@ -0,0 +1,17 @@ +# This is an include file, do not import it directly. +# It is needed as a workaround for Nim's parser for versions <= 1.4. + +proc `'bi`*(s: string): BigInt = + ## Create a `BigInt` from a literal, using the suffix `'bi`. + runnableExamples: + let + a = 123'bi + b = 0xFF'bi + c = 0b1011'bi + assert $a == "123" + assert $b == "255" + assert $c == "11" + case s[0..min(s.high, 1)] + of "0x", "0X": initBigInt(s[2..s.high], base = 16) + of "0b", "0B": initBigInt(s[2..s.high], base = 2) + else: initBigInt(s) diff --git a/research/aparith/src/bigints.git/random.nim b/research/aparith/src/bigints.git/random.nim new file mode 100644 index 0000000..e4c7058 --- /dev/null +++ b/research/aparith/src/bigints.git/random.nim @@ -0,0 +1,43 @@ +import ../bigints +import std/sequtils +import std/options +import std/random + +func rand*(r: var Rand, x: Slice[BigInt]): BigInt = + ## Return a random `BigInt`, within the given range, using the given state. + assert(x.a <= x.b, "invalid range") + let + spread = x.b - x.a + # number of bits *not* including leading bit + nbits = spread.fastLog2 + # number of limbs to generate completely randomly + nFullLimbs = max(nbits div 32 - 1, 0) + # highest possible value of the top two limbs. + hi64Max = (spread shr (nFullLimbs*32)).toInt[:uint64].get() + while true: + # these limbs can be generated completely arbitrarily + var limbs = newSeqWith(nFullLimbs, r.rand(uint32.low..uint32.high)) + # generate the top two limbs more carefully. This all but guarantees + # that the entire number is in the correct range + let hi64 = r.rand(uint64.low..hi64Max) + limbs.add(cast[uint32](hi64)) + limbs.add(cast[uint32](hi64 shr 32)) + result = initBigInt(limbs) + if result <= spread: + break + result += x.a + +func rand*(r: var Rand, max: BigInt): BigInt = + ## Return a random non-negative `BigInt`, up to `max`, using the given state. + rand(r, 0.initBigInt..max) + +# backwards compatibility with 1.4 +when not defined(randState): + var state = initRand(777) + proc randState(): var Rand = state + +proc rand*(x: Slice[BigInt]): BigInt = rand(randState(), x) + ## Return a random `BigInt`, within the given range. + +proc rand*(max: BigInt): BigInt = rand(randState(), max) + ## Return a random `BigInt`, up to `max`. diff --git a/research/aparith/src/speedtest_bigint b/research/aparith/src/speedtest_bigint new file mode 100755 index 0000000000000000000000000000000000000000..ec55c9c0f073c74647a455f4d617796aefaf3878 GIT binary patch literal 177392 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 z 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

QoeXJA20x|;05VB4H9mw1(k;Obt<4l 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 literal 0 HcmV?d00001 diff --git a/research/aparith/src/speedtest_bigint.nim b/research/aparith/src/speedtest_bigint.nim new file mode 100644 index 0000000..8e2a268 --- /dev/null +++ b/research/aparith/src/speedtest_bigint.nim @@ -0,0 +1,35 @@ +import bigints +import std/[options, times] + +func g(x: BigInt, n: BigInt): BigInt {.inline.} = + result = (x*x + 1.initBigInt) mod n + +func pollardRho(n: BigInt): Option[BigInt] = + var + x: BigInt = 2.initBigInt + y: BigInt = n + d: BigInt = 1.initBigInt + + while d == 1.initBigInt: + x = g(x, n) + y = g(g(y, n), n) + d = gcd(abs(x - y), n) + + if d == n: + return none(BigInt) + result = some(d) + + +when isMainModule: + let + # num = 535006138814359.initBigInt + # num = 12.initBigInt + num = 976043389537.initBigInt * 270351207761773.initBigInt + time = cpuTime() + divisor = pollardRho(num) + elapsed = cpuTime() - time + echo "Time taken: ", elapsed + if divisor.isSome: + echo "Result: ", divisor.get() + else: + echo "Result: None(BigInt)" diff --git a/src/imp.nim b/src/imp.nim new file mode 100644 index 0000000..b7a2480 --- /dev/null +++ b/src/imp.nim @@ -0,0 +1,7 @@ +# This is just an example to get you started. A typical library package +# exports the main API in this file. Note that you cannot rename this file +# but you can remove it if you wish. + +proc add*(x, y: int): int = + ## Adds two numbers together. + return x + y diff --git a/src/imp/submodule.nim b/src/imp/submodule.nim new file mode 100644 index 0000000..638e90f --- /dev/null +++ b/src/imp/submodule.nim @@ -0,0 +1,12 @@ +# This is just an example to get you started. Users of your library will +# import this file by writing ``import celeste/submodule``. Feel free to rename or +# remove this file altogether. You may create additional modules alongside +# this file as required. + +type + Submodule* = object + name*: string + +proc initSubmodule*(): Submodule = + ## Initialises a new ``Submodule`` object. + Submodule(name: "Anonymous")