Skip to content

Field/Modular arithmetic design to support efficient arithmetic on any moduli (including even) #2

@mratsim

Description

@mratsim

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): Modular

With 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions