-
Notifications
You must be signed in to change notification settings - Fork 0
Description
edited: Field == Modular arithmetic for prime modulus only
Some ideas of modular arithmetic design.
Many algorithms are only applicable to prime moduli (Legendre symbol, Fermat's Little Theorem, Square roots ...) or odd moduli (Jacobi symbol, GCD and Modular inversion via extended Euclid, Montgomery arithmetic). There are also specialized routines for binary extension fields (mod 2ᵏ) and ternary extension fields (mod 3ᵏ) but there is nothing for generic even moduli.
What we can do is represent generic modulus on a dual base power-of-two 2ᵏ and odd similar to the Residue Number System representation. The results of all operations can be recombined using the Chinese Remainder Theorem.
Concretely we would have
when sizeof(int) == 8 and not defined(Megalo32):
type Word* = uint64
else:
type Word* = uint32
type
BigInt* = object
## Public BigInt repr
raw: RawInt
isNeg: bool
RawInt = object
## Backend BigInt repr, unsigned
limbs: seq[Word]
ModKind = enum
kPrime
kOdd
kBinary
kTernary
kGeneric
PrimeModular* = object
## element (mod p) with p prime
mres: RawInt ## Montgomery Residue repr
OddModular* = object
## element (mod m) with m odd
mres: RawInt ## Montgomery Residue repr
BinaryModular* = object
## element (mod 2ᵏ)
raw: RawInt
TernaryModular* = object
## element (mod 3ᵏ)
raw: RawInt
Modular* = object
## element (mod m)
oddBase: OddModular
binBase: BinModular
PrimeModulus* = object
# Context object for prime fields
raw: RawInt
m0ninv: Word ## 1/p₀ (mod 2⁶⁴), assuming 64-bit word
# other metadata ...
OddModulus* = object
# Context object for odd modular arithmetic
raw: RawInt
m0ninv: Word ## 1/p₀ (mod 2⁶⁴), assuming 64-bit word
# other metadata ...
BinaryModulus* = object
# Context object for binary extension fields
# m = 2ᵏ
k: int
# other metadata ...
TernaryModulus* = object
# Context object for ternary extension fields
# m = 3ᵏ
k: int
# other metadata ...
Modulus* = object
# Context object for arithmetic with any modulus
# Alternatively, use an object variant
oddBase: OddModulus
binBase: BinaryModulus
Operations would then have the form:
func sum*(r: var Modular, a, b, Modular, m: Modulus)
func add*(a, b: Modular, m: Modulus): ModularWith the convention for sum for operation that modify a buffer and add for out-of-place functions.
Alternatively we can have the Modulus object be carried by Modular elements to allow convenient operators:
type
PrimeModular* = object
## element (mod p) with p prime
mres: RawInt ## Montgomery Residue repr
modulus: PrimeModulus
PrimeModulus = ref object
# Context object for prime fields
raw: RawInt
m0ninv: Word ## 1/p₀ (mod 2⁶⁴), assuming 64-bit word
# other metadata ...
func sum*(r: var Modular, a, b, Modular)
func `+`*(a, b: Modular): Modular
func `+=`*(a: var Modular, b: Modular)This would require making it a ref object.