Files
2026-05-27 23:03:00 +08:00

246 lines
5.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build (!amd64 && !arm64) || generic
// +build !amd64,!arm64 generic
package bn256
/*
判断进位的规则
设B=2^w, w = 64 or 32.
假设a op b = c + carry·B.
注:若carry =0 or 1, 则a + b + carry是否产生进位和carry无关。
因为若a+b+carry当carry=0不产生进位,当carry=1产生进位,当且仅当
a+b = B-1, 则a,b中必然有一个数(不妨设为a)的某个非最高比特为0,则
a'=a+carry, a'与a的最高比特相同。因此a'+b 与a+b的进位相同。因此
我们只需要考虑carry为0的情况。
# Add
carry如下:
a[w-1] 0 0 0 1 1 1
b[w-1] 0 1 1 0 0 1
c[w-1] x 0 1 0 1 x
carry 0 1 0 1 0 1
carry>0 <=> a或b都为1(第w-1bit) 或 ab其中一个为1,但c为0.
# Sub
carry如下:
a[w-1] 0 0 0 1 1 1
b[w-1] 0 0 1 0 1 1
c[w-1] 1 0 x x 0 1
carry 1 0 1 0 0 1
carry = (b&^a | (b|^a)&c) >> 63
carry>0 <=> a为0b为1 或 (a,b)!=(1,0)并且c=1.
*/
// gfpCarry compute (a, head) mod p, input (a,head) < 2p
//
// 先计算 (b,carry) = a - p
//
// carry head ret
// 0(a>p) 0 b
// 0 1 b(此情形下,(a,head) > 2p, 不应出现。此时,应再调用一次gfpCarry)
// 1(a<p) 0 a
// 1 1 b
//
// so, carry &^ head = 1, return a, otherwise return b
func gfpCarry(a *gfP, head uint64) {
b := &gfP{}
var carry uint64
for i, pi := range p2 {
ai := a[i]
bi := ai - pi - carry
b[i] = bi
carry = (pi&^ai | (pi|^ai)&bi) >> 63
}
carry = carry &^ head
// If b is negative, then return a.
// Else return b.
carry = -carry
ncarry := ^carry
for i := 0; i < 4; i++ {
a[i] = (a[i] & carry) | (b[i] & ncarry)
}
}
// gfpNeg set c = -a, input a < p
func gfpNeg(c, a *gfP) {
var carry uint64
for i, pi := range p2 {
ai := a[i]
ci := pi - ai - carry
c[i] = ci
carry = (ai&^pi | (ai|^pi)&ci) >> 63
}
// FIXME: carry?
gfpCarry(c, 0)
}
// gfpAdd set c = a+b
func gfpAdd(c, a, b *gfP) {
var carry uint64
for i, ai := range a {
bi := b[i]
ci := ai + bi + carry
c[i] = ci
carry = (ai&bi | (ai|bi)&^ci) >> 63
}
gfpCarry(c, carry)
}
func gfpSub(c, a, b *gfP) {
t := &gfP{}
// t = p-b
var carry uint64
for i, pi := range p2 {
bi := b[i]
ti := pi - bi - carry
t[i] = ti
carry = (bi&^pi | (bi|^pi)&ti) >> 63
}
// c = a+t
carry = 0
for i, ai := range a {
ti := t[i]
ci := ai + ti + carry
c[i] = ci
carry = (ai&ti | (ai|ti)&^ci) >> 63
}
gfpCarry(c, carry)
}
// mul returns the multiplication of a*b. a,b are no restrictions.
func mul(a, b [4]uint64) [8]uint64 {
const (
mask16 uint64 = 0x0000ffff
mask32 uint64 = 0xffffffff
)
// Let B = 2^16, then
// buff = buff[0] + buff[1]*B + ... + buff[31]*B^31
var buff [32]uint64
for i, ai := range a {
a0, a1, a2, a3 := ai&mask16, (ai>>16)&mask16, (ai>>32)&mask16, ai>>48
for j, bj := range b {
// compute ai * bj and save to buff[4*(i+j):]
// (a0 + a1*B + a2*B^2 + a3*B^3) * (b0 + b2*B^2)
// = a0*b0 + a1*b0*B + (a2*b0 + a0*b2)*B^2 + (a1*b2 + a3*b0)*B^3 + a2*b2*B^4 + a3*b2*B^5
b0, b2 := bj&mask32, bj>>32
off := 4 * (i + j)
buff[off+0] += a0 * b0
buff[off+1] += a1 * b0
buff[off+2] += a2*b0 + a0*b2
buff[off+3] += a3*b0 + a1*b2
buff[off+4] += a2 * b2
buff[off+5] += a3 * b2
}
}
// buff:
// 0 1 2 3 | 4 5 6 7 | 8 9 10 11 | 12 13 14 15
// 外循环对将1,2,3加到0上
// 内循环处理0,4,8,12...
for i := uint(1); i < 4; i++ {
shift := 16 * i
var head, carry uint64
for j := uint(0); j < 8; j++ {
block := 4 * j
xi := buff[block]
yi := (buff[block+i] << shift) + head
zi := xi + yi + carry
buff[block] = zi
carry = (xi&yi | (xi|yi)&^zi) >> 63
head = buff[block+i] >> (64 - shift)
}
}
return [8]uint64{buff[0], buff[4], buff[8], buff[12], buff[16], buff[20], buff[24], buff[28]}
}
// halfMul returns a*b mod R, where R = 2^256.
func halfMul(a, b [4]uint64) [4]uint64 {
const (
mask16 uint64 = 0x0000ffff
mask32 uint64 = 0xffffffff
)
var buff [18]uint64
for i, ai := range a {
a0, a1, a2, a3 := ai&mask16, (ai>>16)&mask16, (ai>>32)&mask16, ai>>48
for j, bj := range b {
if i+j > 3 {
break
}
b0, b2 := bj&mask32, bj>>32
off := 4 * (i + j)
buff[off+0] += a0 * b0
buff[off+1] += a1 * b0
buff[off+2] += a2*b0 + a0*b2
buff[off+3] += a3*b0 + a1*b2
buff[off+4] += a2 * b2
buff[off+5] += a3 * b2
}
}
for i := uint(1); i < 4; i++ {
shift := 16 * i
var head, carry uint64
for j := uint(0); j < 4; j++ {
block := 4 * j
xi := buff[block]
yi := (buff[block+i] << shift) + head
zi := xi + yi + carry
buff[block] = zi
carry = (xi&yi | (xi|yi)&^zi) >> 63
head = buff[block+i] >> (64 - shift)
}
}
return [4]uint64{buff[0], buff[4], buff[8], buff[12]}
}
// gfpMul implements the Montgomery multiplication of a*b, i.e.,
// c = a*b*R^{-1} mod p
//
// Let T = a*b = T_h*R + T_l, then
//
// a*b = T_h*R + T_l
// = T_h*R + T_l + (T_l*np mod R)*P mod P
// (For np*P = -1 mod R, so T_l + (T_l*np mod R)*P = 0 mod R.)
// = higher parts of T + (T_l*np mod R)*P
func gfpMul(c, a, b *gfP) {
T := mul(*a, *b)
m := halfMul([4]uint64{T[0], T[1], T[2], T[3]}, np) // m = T_l *np mod R
t := mul([4]uint64{m[0], m[1], m[2], m[3]}, p2) // t = (T_l*np mod R)*P
// (T, carry) = a*b and (c, carry) = a*b/R
// T[0:4] must be 0.
var carry uint64
for i, Ti := range T {
ti := t[i]
zi := Ti + ti + carry
T[i] = zi
carry = (Ti&ti | (Ti|ti)&^zi) >> 63
}
*c = gfP{T[4], T[5], T[6], T[7]}
// TODO: can c >= p?
gfpCarry(c, carry)
}