mirror of
https://github.com/luxfi/lattice.git
synced 2026-09-24 00:25:44 +00:00
merge LP-107 Phase 3: ring/modular_reduction shim over math/ntt/canonical
This commit is contained in:
@@ -5,17 +5,22 @@ go 1.26.1
|
||||
require (
|
||||
github.com/ALTree/bigfloat v0.2.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/luxfi/math v2.0.0+incompatible
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/crypto v0.49.0
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90
|
||||
)
|
||||
|
||||
// LP-107 Phase 3: math has not been published yet. Point at the local
|
||||
// canonical-relocation branch on disk; the user replaces this with a
|
||||
// proper version pin after pushing.
|
||||
replace github.com/luxfi/math => /Users/z/work/lux/math/.claude/worktrees/lp-107-phase3
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -5,17 +5,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
|
||||
+44
-164
@@ -1,205 +1,85 @@
|
||||
// Copyright (c) 2026 Lux Industries Inc.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// Thin shim over github.com/luxfi/math/ntt/canonical.
|
||||
// LP-107 Phase 3 inverted the dependency: luxfi/math owns the
|
||||
// canonical Montgomery / Barrett body. This package re-exports it so
|
||||
// downstream consumers (luxfi/pulsar, luxfi/fhe, luxfi/threshold,
|
||||
// luxfi/multiparty, ...) see no source change.
|
||||
|
||||
package ring
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
|
||||
"github.com/luxfi/lattice/v7/utils/bignum"
|
||||
"github.com/luxfi/math/ntt/canonical"
|
||||
)
|
||||
|
||||
// MForm switches a to the Montgomery domain by computing
|
||||
// a*2^64 mod q.
|
||||
func MForm(a, q uint64, bredconstant [2]uint64) (r uint64) {
|
||||
// R = 2^128/q
|
||||
// b = a * 2^{64}
|
||||
// mhi = (b * (R % 2^{64})) / 2^{64}
|
||||
//
|
||||
// r = b - ((b * (R/2^{64}) + mhi) * q
|
||||
// = b - (b * R)/2^{64} * q
|
||||
// = b - n * q
|
||||
//
|
||||
// Since r is in [0, 2q] and q is in [0, 2^{63}-1]
|
||||
// we are ensured that r < 2^64, thus r = r mod 2^{64}
|
||||
// So we can work mod 2^{64} (i.e. with uint64)
|
||||
// and therefore:
|
||||
//
|
||||
// r = b - n * q mod 2^{64}
|
||||
// = a * 2^{64} - n * q mod 2^{64}
|
||||
// = - n * q mod 2^{64}
|
||||
//
|
||||
mhi, _ := bits.Mul64(a, bredconstant[1])
|
||||
r = -(a*bredconstant[0] + mhi) * q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
// MForm switches a to the Montgomery domain by computing a*2^64 mod q.
|
||||
func MForm(a, q uint64, bredconstant [2]uint64) uint64 {
|
||||
return canonical.MForm(a, q, bredconstant)
|
||||
}
|
||||
|
||||
// MFormLazy switches a to the Montgomery domain by computing
|
||||
// a*2^64 mod q in constant time.
|
||||
// The result is between 0 and 2*q-1.
|
||||
func MFormLazy(a, q uint64, bredconstant [2]uint64) (r uint64) {
|
||||
// See MForm for the implementation trick.
|
||||
mhi, _ := bits.Mul64(a, bredconstant[1])
|
||||
r = -(a*bredconstant[0] + mhi) * q
|
||||
return
|
||||
// a*2^64 mod q in constant time. Result in [0, 2q-1].
|
||||
func MFormLazy(a, q uint64, bredconstant [2]uint64) uint64 {
|
||||
return canonical.MFormLazy(a, q, bredconstant)
|
||||
}
|
||||
|
||||
// IMForm switches a from the Montgomery domain back to the
|
||||
// standard domain by computing a*(1/2^64) mod q.
|
||||
func IMForm(a, q, mredconstant uint64) (r uint64) {
|
||||
r, _ = bits.Mul64(a*mredconstant, q)
|
||||
r = q - r
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
// IMForm switches a from the Montgomery domain back to the standard
|
||||
// domain by computing a*(1/2^64) mod q.
|
||||
func IMForm(a, q, mredconstant uint64) uint64 {
|
||||
return canonical.IMForm(a, q, mredconstant)
|
||||
}
|
||||
|
||||
// IMFormLazy switches a from the Montgomery domain back to the
|
||||
// standard domain by computing a*(1/2^64) mod q in constant time.
|
||||
// The result is between 0 and 2*q-1.
|
||||
func IMFormLazy(a, q, mredconstant uint64) (r uint64) {
|
||||
r, _ = bits.Mul64(a*mredconstant, q)
|
||||
r = q - r
|
||||
return
|
||||
// Result in [0, 2q-1].
|
||||
func IMFormLazy(a, q, mredconstant uint64) uint64 {
|
||||
return canonical.IMFormLazy(a, q, mredconstant)
|
||||
}
|
||||
|
||||
// GenMRedConstant computes the constant mredconstant = (q^-1) mod 2^64 required for MRed.
|
||||
func GenMRedConstant(q uint64) (mredconstant uint64) {
|
||||
mredconstant = 1
|
||||
for i := 0; i < 63; i++ {
|
||||
mredconstant *= q
|
||||
q *= q
|
||||
}
|
||||
return mredconstant
|
||||
// GenMRedConstant computes the constant (q^-1) mod 2^64 used by MRed.
|
||||
func GenMRedConstant(q uint64) uint64 {
|
||||
return canonical.GenMRedConstant(q)
|
||||
}
|
||||
|
||||
// MRed computes x * y * (1/2^64) mod q.
|
||||
func MRed(x, y, q, mredconstant uint64) (r uint64) {
|
||||
mhi, mlo := bits.Mul64(x, y)
|
||||
hhi, _ := bits.Mul64(mlo*mredconstant, q)
|
||||
r = mhi - hhi + q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
func MRed(x, y, q, mredconstant uint64) uint64 {
|
||||
return canonical.MRed(x, y, q, mredconstant)
|
||||
}
|
||||
|
||||
// MRedLazy computes x * y * (1/2^64) mod q in constant time.
|
||||
// The result is between 0 and 2*q-1.
|
||||
func MRedLazy(x, y, q, mredconstant uint64) (r uint64) {
|
||||
ahi, alo := bits.Mul64(x, y)
|
||||
H, _ := bits.Mul64(alo*mredconstant, q)
|
||||
r = ahi - H + q
|
||||
return
|
||||
// Result in [0, 2q-1].
|
||||
func MRedLazy(x, y, q, mredconstant uint64) uint64 {
|
||||
return canonical.MRedLazy(x, y, q, mredconstant)
|
||||
}
|
||||
|
||||
// GenBRedConstant computes the constant for the BRed algorithm.
|
||||
// GenBRedConstant computes the constant pair for the BRed algorithm.
|
||||
// Returns ((2^128)/q)/(2^64) and (2^128)/q mod 2^64.
|
||||
func GenBRedConstant(q uint64) [2]uint64 {
|
||||
bigR := bignum.NewInt("0x100000000000000000000000000000000")
|
||||
bigR.Quo(bigR, bignum.NewInt(q))
|
||||
|
||||
mlo := bigR.Uint64()
|
||||
mhi := bigR.Rsh(bigR, 64).Uint64()
|
||||
|
||||
return [2]uint64{mhi, mlo}
|
||||
return canonical.GenBRedConstant(q)
|
||||
}
|
||||
|
||||
// BRedAdd computes a mod q.
|
||||
func BRedAdd(a, q uint64, bredconstant [2]uint64) (r uint64) {
|
||||
mhi, _ := bits.Mul64(a, bredconstant[0])
|
||||
r = a - mhi*q
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
return
|
||||
func BRedAdd(a, q uint64, bredconstant [2]uint64) uint64 {
|
||||
return canonical.BRedAdd(a, q, bredconstant)
|
||||
}
|
||||
|
||||
// BRedAddLazy computes a mod q in constant time.
|
||||
// The result is between 0 and 2*q-1.
|
||||
// BRedAddLazy computes a mod q in constant time. Result in [0, 2q-1].
|
||||
func BRedAddLazy(x, q uint64, bredconstant [2]uint64) uint64 {
|
||||
s0, _ := bits.Mul64(x, bredconstant[0])
|
||||
return x - s0*q
|
||||
return canonical.BRedAddLazy(x, q, bredconstant)
|
||||
}
|
||||
|
||||
// BRed computes x*y mod q.
|
||||
func BRed(x, y, q uint64, bredconstant [2]uint64) (r uint64) {
|
||||
|
||||
var mhi, mlo, lhi, hhi, hlo, s0, carry uint64
|
||||
|
||||
mhi, mlo = bits.Mul64(x, y)
|
||||
|
||||
// computes r = mhi * uhi + (mlo * uhi + mhi * ulo)<<64 + (mlo * ulo)) >> 128
|
||||
|
||||
r = mhi * bredconstant[0] // r = mhi * uhi
|
||||
|
||||
hhi, hlo = bits.Mul64(mlo, bredconstant[0]) // mlo * uhi
|
||||
|
||||
r += hhi
|
||||
|
||||
lhi, _ = bits.Mul64(mlo, bredconstant[1]) // mlo * ulo
|
||||
|
||||
s0, carry = bits.Add64(hlo, lhi, 0)
|
||||
|
||||
r += carry
|
||||
|
||||
hhi, hlo = bits.Mul64(mhi, bredconstant[1]) // mhi * ulo
|
||||
|
||||
r += hhi
|
||||
|
||||
_, carry = bits.Add64(hlo, s0, 0)
|
||||
|
||||
r += carry
|
||||
|
||||
r = mlo - r*q
|
||||
|
||||
if r >= q {
|
||||
r -= q
|
||||
}
|
||||
|
||||
return
|
||||
func BRed(x, y, q uint64, bredconstant [2]uint64) uint64 {
|
||||
return canonical.BRed(x, y, q, bredconstant)
|
||||
}
|
||||
|
||||
// BRedLazy computes x*y mod q in constant time.
|
||||
// The result is between 0 and 2*q-1.
|
||||
func BRedLazy(x, y, q uint64, bredconstant [2]uint64) (r uint64) {
|
||||
|
||||
var mhi, mlo, lhi, hhi, hlo, s0, carry uint64
|
||||
|
||||
mhi, mlo = bits.Mul64(x, y)
|
||||
|
||||
// computes r = mhi * uhi + (mlo * uhi + mhi * ulo)<<64 + (mlo * ulo)) >> 128
|
||||
|
||||
r = mhi * bredconstant[0] // r = mhi * uhi
|
||||
|
||||
hhi, hlo = bits.Mul64(mlo, bredconstant[0]) // mlo * uhi
|
||||
|
||||
r += hhi
|
||||
|
||||
lhi, _ = bits.Mul64(mlo, bredconstant[1]) // mlo * ulo
|
||||
|
||||
s0, carry = bits.Add64(hlo, lhi, 0)
|
||||
|
||||
r += carry
|
||||
|
||||
hhi, hlo = bits.Mul64(mhi, bredconstant[1]) // mhi * ulo
|
||||
|
||||
r += hhi
|
||||
|
||||
_, carry = bits.Add64(hlo, s0, 0)
|
||||
|
||||
r += carry
|
||||
|
||||
r = mlo - r*q
|
||||
|
||||
return
|
||||
// BRedLazy computes x*y mod q in constant time. Result in [0, 2q-1].
|
||||
func BRedLazy(x, y, q uint64, bredconstant [2]uint64) uint64 {
|
||||
return canonical.BRedLazy(x, y, q, bredconstant)
|
||||
}
|
||||
|
||||
// CRed reduce returns a mod q where a is between 0 and 2*q-1.
|
||||
// CRed reduces a in [0, 2q-1] to [0, q-1].
|
||||
func CRed(a, q uint64) uint64 {
|
||||
if a >= q {
|
||||
return a - q
|
||||
}
|
||||
return a
|
||||
return canonical.CRed(a, q)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user