nim-stint/stint/modular_arithmetic.nim

127 lines
2.8 KiB
Nim
Raw Normal View History

# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
2019-10-25 10:50:55 +00:00
import ./intops, private/datatypes
2022-02-24 19:09:53 +00:00
func addmod_internal(a, b, m: StUint): StUint {.inline.}=
## Modular addition
## ⚠⚠ Assume a < m and b < m
2019-03-14 02:43:51 +00:00
doAssert a < m
doAssert b < m
# We don't do a_m + b_m directly to avoid overflows
let b_from_m = m - b
if a >= b_from_m:
return a - b_from_m
return m - b_from_m + a
2022-02-24 19:09:53 +00:00
func submod_internal(a, b, m: StUint): StUint {.inline.}=
## Modular substraction
## ⚠⚠ Assume a < m and b < m
2019-03-14 02:43:51 +00:00
doAssert a < m
doAssert b < m
# We don't do a_m - b_m directly to avoid underflows
if a >= b:
return a - b
return m - b + a
2022-02-24 19:09:53 +00:00
func doublemod_internal(a, m: StUint): StUint {.inline.}=
## Double a modulo m. Assume a < m
## Internal proc - used in mulmod
2019-03-14 02:43:51 +00:00
doAssert a < m
result = a
if a >= m - a:
result -= m
result += a
2022-02-24 19:09:53 +00:00
func mulmod_internal(a, b, m: StUint): StUint {.inline.}=
## Does (a * b) mod m. Assume a < m and b < m
## Internal proc - used in powmod
2019-03-14 02:43:51 +00:00
doAssert a < m
doAssert b < m
var (a, b) = (a, b)
if b > a:
swap(a, b)
while not b.isZero:
if b.isOdd:
result = result.addmod_internal(a, m)
a = doublemod_internal(a, m)
b = b shr 1
2022-02-24 19:09:53 +00:00
func powmod_internal(a, b, m: StUint): StUint {.inline.}=
## Compute ``(a ^ b) mod m``, assume a < m
## Internal proc
2019-03-14 02:43:51 +00:00
doAssert a < m
var (a, b) = (a, b)
result = one(type a)
while not b.isZero:
if b.isOdd:
result = result.mulmod_internal(a, m)
b = b shr 1
a = mulmod_internal(a, a, m)
2022-02-24 19:09:53 +00:00
func addmod*(a, b, m: StUint): StUint =
## Modular addition
let a_m = if a < m: a
else: a mod m
let b_m = if b < m: b
else: b mod m
result = addmod_internal(a_m, b_m, m)
2022-02-24 19:09:53 +00:00
func submod*(a, b, m: StUint): StUint =
## Modular substraction
let a_m = if a < m: a
else: a mod m
let b_m = if b < m: b
else: b mod m
result = submod_internal(a_m, b_m, m)
2022-02-24 19:09:53 +00:00
func mulmod*(a, b, m: StUint): StUint =
## Modular multiplication
let a_m = if a < m: a
else: a mod m
let b_m = if b < m: b
else: b mod m
result = mulmod_internal(a_m, b_m, m)
2022-02-24 19:09:53 +00:00
func powmod*(a, b, m: StUint): StUint =
## Modular exponentiation
2019-10-25 10:50:55 +00:00
when nimvm:
doAssert false, "cannot use powmod at compile-time"
else:
# we need this ugly branch
# because of nim-lang/Nim#12517
discard
let a_m = if a < m: a
else: a mod m
result = powmod_internal(a_m, b, m)