Fix isZeroMask in SignedSecretWord
This commit is contained in:
parent
915f89fdd6
commit
a385acf2b8
|
@ -15,7 +15,7 @@ import
|
|||
|
||||
# ############################################################
|
||||
#
|
||||
# Primitives based on Bézout's identity
|
||||
# Primitives based on Bézout's identity
|
||||
#
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -78,7 +78,7 @@ func invMod2powK(M0: BaseType, k: static BaseType): BaseType =
|
|||
|
||||
# ###############################################################
|
||||
#
|
||||
# Modular inversion
|
||||
# Modular inversion
|
||||
#
|
||||
# ###############################################################
|
||||
|
||||
|
@ -136,17 +136,17 @@ func canonicalize(
|
|||
M: LimbsUnsaturated
|
||||
) =
|
||||
## Compute a = sign*a (mod M)
|
||||
##
|
||||
##
|
||||
## with a in range (-2*M, M)
|
||||
## result in range [0, M)
|
||||
|
||||
|
||||
const
|
||||
UnsatBitWidth = WordBitWidth - a.Excess
|
||||
Max = SignedSecretWord(MaxWord shr a.Excess)
|
||||
|
||||
|
||||
# Operate in registers
|
||||
var z = a
|
||||
|
||||
|
||||
# Add M if `z` is negative
|
||||
# -> range (-M, M)
|
||||
z.cadd(M, z.isNegMask())
|
||||
|
@ -170,7 +170,7 @@ func canonicalize(
|
|||
|
||||
proc partitionDivsteps(bits, wordBitWidth: int): tuple[totalIters, numChunks, chunkSize, cutoff: int] =
|
||||
# Given the field modulus number of bits
|
||||
# and the effective word size
|
||||
# and the effective word size
|
||||
# Returns:
|
||||
# - the total number of iterations that guarantees GCD convergence
|
||||
# - the number of chunks of divsteps to compute
|
||||
|
@ -178,7 +178,7 @@ proc partitionDivsteps(bits, wordBitWidth: int): tuple[totalIters, numChunks, ch
|
|||
# - a cutoff chunk,
|
||||
# before this chunk ID, the number of divsteps is "base number + 1"
|
||||
# afterward it's "base number"
|
||||
let totalIters =
|
||||
let totalIters =
|
||||
if bits == 256:
|
||||
# https://github.com/sipa/safegcd-bounds/tree/master/coq
|
||||
# For 256-bit inputs, 590 divsteps are sufficient with hddivstep variant (half-delta divstep)
|
||||
|
@ -203,12 +203,12 @@ func batchedDivsteps(
|
|||
k: static int
|
||||
): SignedSecretWord =
|
||||
## Bernstein-Yang half-delta (hdelta) batch of divsteps
|
||||
##
|
||||
##
|
||||
## Output:
|
||||
## - return hdelta for the next batch of divsteps
|
||||
## - mutate t, the transition matrix to apply `numIters` divsteps at once
|
||||
## t is scaled by 2ᵏ
|
||||
##
|
||||
##
|
||||
## Input:
|
||||
## - f0, bottom limb of f
|
||||
## - g0, bottom limb of g
|
||||
|
@ -281,13 +281,13 @@ func matVecMul_shr_k_mod_M[N, E: static int](
|
|||
invMod2powK: SecretWord
|
||||
) =
|
||||
## Compute
|
||||
##
|
||||
## [u v] [d]
|
||||
##
|
||||
## [u v] [d]
|
||||
## [q r]/2ᵏ.[e] mod M
|
||||
##
|
||||
## d, e will be in range (-2*modulus,modulus)
|
||||
## and output limbs in (-2ᵏ, 2ᵏ)
|
||||
|
||||
|
||||
static: doAssert k == WordBitWidth - E
|
||||
const Max = SignedSecretWord(MaxWord shr E)
|
||||
|
||||
|
@ -303,7 +303,7 @@ func matVecMul_shr_k_mod_M[N, E: static int](
|
|||
# Double-signed-word carries
|
||||
var cd, ce: DSWord
|
||||
|
||||
# First iteration of [u v] [d]
|
||||
# First iteration of [u v] [d]
|
||||
# [q r].[e]
|
||||
cd.ssumprodAccNoCarry(u, d[0], v, e[0])
|
||||
ce.ssumprodAccNoCarry(q, d[0], r, e[0])
|
||||
|
@ -317,7 +317,7 @@ func matVecMul_shr_k_mod_M[N, E: static int](
|
|||
md.cadd(v, sign_e)
|
||||
me.cadd(q, sign_d)
|
||||
me.cadd(r, sign_e)
|
||||
|
||||
|
||||
md = md - (SignedSecretWord(invMod2powK * SecretWord(cd.lo) + SecretWord(md)) and Max)
|
||||
me = me - (SignedSecretWord(invMod2powK * SecretWord(ce.lo) + SecretWord(me)) and Max)
|
||||
|
||||
|
@ -338,18 +338,18 @@ func matVecMul_shr_k_mod_M[N, E: static int](
|
|||
e[i-1] = ce.lo and Max
|
||||
cd.ashr(k)
|
||||
ce.ashr(k)
|
||||
|
||||
|
||||
d[N-1] = cd.lo
|
||||
e[N-1] = ce.lo
|
||||
|
||||
func matVecMul_shr_k[N, E: static int](
|
||||
t: TransitionMatrix,
|
||||
f, g: var LimbsUnsaturated[N, E],
|
||||
k: static int
|
||||
k: static int
|
||||
) =
|
||||
## Compute
|
||||
##
|
||||
## [u v] [f]
|
||||
##
|
||||
## [u v] [f]
|
||||
## [q r].[g] / 2ᵏ
|
||||
|
||||
static: doAssert k == WordBitWidth - E
|
||||
|
@ -363,8 +363,8 @@ func matVecMul_shr_k[N, E: static int](
|
|||
|
||||
# Double-signed-word carries
|
||||
var cf, cg: DSWord
|
||||
|
||||
# First iteration of [u v] [f]
|
||||
|
||||
# First iteration of [u v] [f]
|
||||
# [q r].[g]
|
||||
cf.ssumprodAccNoCarry(u, f[0], v, g[0])
|
||||
cg.ssumprodAccNoCarry(q, f[0], r, g[0])
|
||||
|
@ -383,7 +383,7 @@ func matVecMul_shr_k[N, E: static int](
|
|||
g[i-1] = cg.lo and Max
|
||||
cf.ashr(k)
|
||||
cg.ashr(k)
|
||||
|
||||
|
||||
f[N-1] = cf.lo
|
||||
g[N-1] = cg.lo
|
||||
|
||||
|
@ -414,11 +414,11 @@ func invmodImpl[N, E](
|
|||
# Compute transition matrix and next hdelta
|
||||
hdelta = t.batchedDivsteps(hdelta, f[0], g[0], numIters, k)
|
||||
# Apply the transition matrix
|
||||
# [u v] [d]
|
||||
# [u v] [d]
|
||||
# [q r]/2ᵏ.[e] mod M
|
||||
t.matVecMul_shr_k_mod_M(d, e, k, M, invMod2powK)
|
||||
# [u v] [f]
|
||||
# [q r]/2ᵏ.[g]
|
||||
# [u v] [f]
|
||||
# [q r]/2ᵏ.[g]
|
||||
t.matVecMul_shr_k(f, g, k)
|
||||
|
||||
d.canonicalize(signMask = f.isNegMask(), M)
|
||||
|
@ -453,12 +453,12 @@ func invmod*(
|
|||
F, M: static Limbs, bits: static int) =
|
||||
## Compute the scaled modular inverse of ``a`` modulo M
|
||||
## r ≡ F.a⁻¹ (mod M) (compile-time factor and modulus overload)
|
||||
##
|
||||
##
|
||||
## with F and M known at compile-time
|
||||
##
|
||||
## M MUST be odd, M does not need to be prime.
|
||||
## ``a`` MUST be less than M.
|
||||
|
||||
|
||||
const Excess = 2
|
||||
const k = WordBitWidth - Excess
|
||||
const NumUnsatWords = (bits + k - 1) div k
|
||||
|
@ -506,13 +506,13 @@ func batchedDivstepsSymbol(
|
|||
): tuple[hdelta, L: SignedSecretWord] =
|
||||
## Bernstein-Yang half-delta (hdelta) batch of divsteps
|
||||
## with Legendre symbol tracking
|
||||
##
|
||||
##
|
||||
## Output:
|
||||
## - return hdelta for the next batch of divsteps
|
||||
## - Returns the intermediate Legendre symbol
|
||||
## - mutate t, the transition matrix to apply `numIters` divsteps at once
|
||||
## t is scaled by 2ᵏ
|
||||
##
|
||||
##
|
||||
## Input:
|
||||
## - f0, bottom limb of f
|
||||
## - g0, bottom limb of g
|
||||
|
@ -618,20 +618,20 @@ func legendreImpl[N, E](
|
|||
numIters, k)
|
||||
else:
|
||||
(hdelta, L) = t.batchedDivstepsSymbol(hdelta, f[0], g[0], numIters, k)
|
||||
# [u v] [f]
|
||||
# [q r]/2ᵏ.[g]
|
||||
# [u v] [f]
|
||||
# [q r]/2ᵏ.[g]
|
||||
t.matVecMul_shr_k(f, g, k)
|
||||
accL = (accL + L) and SignedSecretWord(3)
|
||||
accL = (accL + ((accL.isOdd() xor f.isNeg()))) and SignedSecretWord(3)
|
||||
|
||||
accL = (accL + accL.isOdd()) and SignedSecretWord(3)
|
||||
accL = SignedSecretWord(1)-accL
|
||||
accL.csetZero(f.isZeroMask())
|
||||
accL.csetZero(not f.isZeroMask())
|
||||
return SecretWord(accL)
|
||||
|
||||
func legendre*(a, M: Limbs, bits: static int): SecretWord =
|
||||
## Compute the Legendre symbol
|
||||
##
|
||||
##
|
||||
## (a/p)ₗ ≡ a^((p-1)/2) ≡ 1 (mod p), iff a is a square
|
||||
## ≡ -1 (mod p), iff a is quadratic non-residue
|
||||
## ≡ 0 (mod p), iff a is 0
|
||||
|
@ -645,24 +645,24 @@ func legendre*(a, M: Limbs, bits: static int): SecretWord =
|
|||
|
||||
var a2 {.noInit.}: LimbsUnsaturated[NumUnsatWords, Excess]
|
||||
a2.fromPackedRepr(a)
|
||||
|
||||
|
||||
legendreImpl(a2, m2, k, bits)
|
||||
|
||||
func legendre*(a: Limbs, M: static Limbs, bits: static int): SecretWord =
|
||||
## Compute the Legendre symbol (compile-time modulus overload)
|
||||
##
|
||||
##
|
||||
## (a/p)ₗ ≡ a^((p-1)/2) ≡ 1 (mod p), iff a is a square
|
||||
## ≡ -1 (mod p), iff a is quadratic non-residue
|
||||
## ≡ 0 (mod p), iff a is 0
|
||||
|
||||
|
||||
const Excess = 2
|
||||
const k = WordBitWidth - Excess
|
||||
const NumUnsatWords = (bits + k - 1) div k
|
||||
|
||||
# Convert values to unsaturated repr
|
||||
const m2 = LimbsUnsaturated[NumUnsatWords, Excess].fromPackedRepr(M)
|
||||
|
||||
|
||||
var a2 {.noInit.}: LimbsUnsaturated[NumUnsatWords, Excess]
|
||||
a2.fromPackedRepr(a)
|
||||
|
||||
|
||||
legendreImpl(a2, m2, k, bits)
|
||||
|
|
|
@ -17,7 +17,7 @@ type
|
|||
## This allows efficient handling of carries and signs without intrinsics or assembly.
|
||||
#
|
||||
# Comparison with packed representation:
|
||||
#
|
||||
#
|
||||
# Packed representation
|
||||
# - pro: uses less words (important for multiplication which is O(n²) with n the number of words)
|
||||
# - pro: less "mental overhead" to keep track (clear/shift) excess bits
|
||||
|
@ -86,7 +86,7 @@ template `[]=`*(a: LimbsUnsaturated, idx: int, val: SignedSecretWord) =
|
|||
func fromPackedRepr*[LU, E, LP: static int](
|
||||
dst: var LimbsUnsaturated[LU, E],
|
||||
src: Limbs[LP]) =
|
||||
## Converts from an packed representation to an unsaturated representation
|
||||
## Converts from an packed representation to an unsaturated representation
|
||||
const UnsatBitWidth = WordBitWidth-E
|
||||
const Max = MaxWord shr E
|
||||
|
||||
|
@ -100,13 +100,13 @@ func fromPackedRepr*[LU, E, LP: static int](
|
|||
srcIdx, dstIdx = 0
|
||||
hi, lo = Zero
|
||||
accLen = 0
|
||||
|
||||
|
||||
while srcIdx < src.len:
|
||||
# Form a 2-word buffer (hi, lo)
|
||||
let w = if src_idx < src.len: src[srcIdx]
|
||||
else: Zero
|
||||
inc srcIdx
|
||||
|
||||
|
||||
if accLen == 0:
|
||||
lo = w and Max
|
||||
hi = w shr UnsatBitWidth
|
||||
|
@ -124,7 +124,7 @@ func fromPackedRepr*[LU, E, LP: static int](
|
|||
accLen -= s
|
||||
lo = ((lo shr s) or (hi shl (UnsatBitWidth - s))) and Max
|
||||
hi = hi shr s
|
||||
|
||||
|
||||
if dstIdx < dst.words.len:
|
||||
dst[dstIdx] = SignedSecretWord lo
|
||||
|
||||
|
@ -138,7 +138,7 @@ func fromPackedRepr*(T: type LimbsUnsaturated, src: Limbs): T =
|
|||
func fromUnsatRepr*[LU, E, LP: static int](
|
||||
dst: var Limbs[LP],
|
||||
src: LimbsUnsaturated[LU, E]) =
|
||||
## Converts from an packed representation to an unsaturated representation
|
||||
## Converts from an packed representation to an unsaturated representation
|
||||
const UnsatBitWidth = WordBitWidth-E
|
||||
|
||||
static:
|
||||
|
@ -165,7 +165,7 @@ func fromUnsatRepr*[LU, E, LP: static int](
|
|||
inc dstIdx
|
||||
accLen -= WordBitWidth
|
||||
acc = nextWord shr (UnsatBitWidth - accLen)
|
||||
|
||||
|
||||
if dst_idx < dst.len:
|
||||
dst[dst_idx] = acc
|
||||
|
||||
|
@ -280,9 +280,10 @@ func isOdd*(a: SignedSecretWord): SignedSecretWord {.inline.} =
|
|||
a and SignedSecretWord(1)
|
||||
|
||||
func isZeroMask*(a: SignedSecretWord): SignedSecretWord {.inline.} =
|
||||
## Produce the -1 mask if a is negative
|
||||
## Produce the -1 mask if a is 0
|
||||
## and 0 otherwise
|
||||
not SignedSecretWord(a.SecretWord().isZero())
|
||||
# In x86 assembly, we can use "neg" + "sbb"
|
||||
-SignedSecretWord(a.SecretWord().isZero())
|
||||
|
||||
func isNegMask*(a: SignedSecretWord): SignedSecretWord {.inline.} =
|
||||
## Produce the -1 mask if a is negative
|
||||
|
@ -295,7 +296,7 @@ func isOddMask*(a: SignedSecretWord): SignedSecretWord {.inline.} =
|
|||
-(a and SignedSecretWord(1))
|
||||
|
||||
func csetZero*(a: var SignedSecretWord, mask: SignedSecretWord) {.inline.} =
|
||||
## Conditionally set `a` to 0
|
||||
## Conditionally set `a` to 0
|
||||
## mask must be 0 (0x00000...0000) (kept as is)
|
||||
## or -1 (0xFFFF...FFFF) (zeroed)
|
||||
a = a and mask
|
||||
|
@ -303,7 +304,7 @@ func csetZero*(a: var SignedSecretWord, mask: SignedSecretWord) {.inline.} =
|
|||
func cneg*(
|
||||
a: SignedSecretWord,
|
||||
mask: SignedSecretWord): SignedSecretWord {.inline.} =
|
||||
## Conditionally negate `a`
|
||||
## Conditionally negate `a`
|
||||
## mask must be 0 (0x00000...0000) (no negation)
|
||||
## or -1 (0xFFFF...FFFF) (negation)
|
||||
(a xor mask) - mask
|
||||
|
@ -312,7 +313,7 @@ func cadd*(
|
|||
a: var SignedSecretWord,
|
||||
b: SignedSecretWord,
|
||||
mask: SignedSecretWord) {.inline.} =
|
||||
## Conditionally add `b` to `a`
|
||||
## Conditionally add `b` to `a`
|
||||
## mask must be 0 (0x00000...0000) (no addition)
|
||||
## or -1 (0xFFFF...FFFF) (addition)
|
||||
a = a + (b and mask)
|
||||
|
@ -321,7 +322,7 @@ func csub*(
|
|||
a: var SignedSecretWord,
|
||||
b: SignedSecretWord,
|
||||
mask: SignedSecretWord) {.inline.} =
|
||||
## Conditionally substract `b` from `a`
|
||||
## Conditionally substract `b` from `a`
|
||||
## mask must be 0 (0x00000...0000) (no substraction)
|
||||
## or -1 (0xFFFF...FFFF) (substraction)
|
||||
a = a - (b and mask)
|
||||
|
@ -335,7 +336,7 @@ func isZeroMask*(a: LimbsUnsaturated): SignedSecretWord {.inline.} =
|
|||
var accum = SignedSecretWord(0)
|
||||
for i in 0 ..< a.words.len:
|
||||
accum = accum or a.words[i]
|
||||
|
||||
|
||||
return accum.isZeroMask()
|
||||
|
||||
func isNeg*(a: LimbsUnsaturated): SignedSecretWord {.inline.} =
|
||||
|
@ -351,10 +352,10 @@ func isNegMask*(a: LimbsUnsaturated): SignedSecretWord {.inline.} =
|
|||
func cneg*(
|
||||
a: var LimbsUnsaturated,
|
||||
mask: SignedSecretWord) {.inline.} =
|
||||
## Conditionally negate `a`
|
||||
## Conditionally negate `a`
|
||||
## mask must be 0 (0x00000...0000) (no negation)
|
||||
## or -1 (0xFFFF...FFFF) (negation)
|
||||
##
|
||||
##
|
||||
## Carry propagation is deferred
|
||||
for i in 0 ..< a.words.len:
|
||||
a[i] = a[i].cneg(mask)
|
||||
|
@ -363,10 +364,10 @@ func cadd*(
|
|||
a: var LimbsUnsaturated,
|
||||
b: LimbsUnsaturated,
|
||||
mask: SignedSecretWord) {.inline.} =
|
||||
## Conditionally add `b` to `a`
|
||||
## Conditionally add `b` to `a`
|
||||
## mask must be 0 (0x00000...0000) (no addition)
|
||||
## or -1 (0xFFFF...FFFF) (addition)
|
||||
##
|
||||
##
|
||||
## Carry propagation is deferred
|
||||
for i in 0 ..< a.words.len:
|
||||
a[i].cadd(b[i], mask)
|
||||
|
|
|
@ -173,7 +173,7 @@ template `xor`*[T: Ct](x, y: CTBool[T]): CTBool[T] =
|
|||
|
||||
template cneg*[T: Ct](x: T, ctl: CTBool[T]): T =
|
||||
# Conditional negate if ctl is true
|
||||
(x xor -T(ctl)) + T(ctl)
|
||||
(x xor -T(ctl)) + T(ctl)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -215,6 +215,7 @@ template isNonZero*[T: Ct](x: T): CTBool[T] =
|
|||
isMsbSet(x_NZ or -x_NZ)
|
||||
|
||||
template isZero*[T: Ct](x: T): CTBool[T] =
|
||||
# In x86 assembly, we can use "neg" + "adc"
|
||||
not isNonZero(x)
|
||||
|
||||
# ############################################################
|
||||
|
|
Loading…
Reference in New Issue