/
ring_automorphism.go
138 lines (100 loc) · 3.61 KB
/
ring_automorphism.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package ring
import (
"math/bits"
"unsafe"
"github.com/ldsec/lattigo/v2/utils"
)
// GenGaloisParams generates the generators for the Galois endomorphisms.
func GenGaloisParams(n, gen uint64) (galElRotCol []uint64) {
var m, mask uint64
m = n << 1
mask = m - 1
galElRotCol = make([]uint64, n>>1)
galElRotCol[0] = 1
for i := uint64(1); i < n>>1; i++ {
galElRotCol[i] = (galElRotCol[i-1] * gen) & mask
}
return
}
// PermuteNTTIndex computes the index table for PermuteNTT.
func (r *Ring) PermuteNTTIndex(galEl uint64) (index []uint64) {
var mask, tmp1, tmp2, logNthRoot uint64
logNthRoot = uint64(bits.Len64(r.NthRoot) - 2)
mask = r.NthRoot - 1
index = make([]uint64, r.N)
for i := uint64(0); i < uint64(r.N); i++ {
tmp1 = 2*utils.BitReverse64(i, logNthRoot) + 1
tmp2 = ((galEl * tmp1 & mask) - 1) >> 1
index[i] = utils.BitReverse64(tmp2, logNthRoot)
}
return
}
// PermuteNTT applies the Galois transform on a polynomial in the NTT domain.
// It maps the coefficients x^i to x^(gen*i)
// It must be noted that the result cannot be in-place.
func (r *Ring) PermuteNTT(polIn *Poly, gen uint64, polOut *Poly) {
r.PermuteNTTLvl(utils.MinInt(polIn.Level(), polOut.Level()), polIn, gen, polOut)
}
// PermuteNTTLvl applies the Galois transform on a polynomial in the NTT domain, up to a given level.
// It maps the coefficients x^i to x^(gen*i)
// It must be noted that the result cannot be in-place.
func (r *Ring) PermuteNTTLvl(level int, polIn *Poly, gen uint64, polOut *Poly) {
r.PermuteNTTWithIndexLvl(level, polIn, r.PermuteNTTIndex(gen), polOut)
}
// PermuteNTTWithIndexLvl applies the Galois transform on a polynomial in the NTT domain, up to a given level.
// It maps the coefficients x^i to x^(gen*i) using the PermuteNTTIndex table.
// It must be noted that the result cannot be in-place.
func (r *Ring) PermuteNTTWithIndexLvl(level int, polIn *Poly, index []uint64, polOut *Poly) {
for j := 0; j < r.N; j = j + 8 {
x := (*[8]uint64)(unsafe.Pointer(&index[j]))
for i := 0; i < level+1; i++ {
z := (*[8]uint64)(unsafe.Pointer(&polOut.Coeffs[i][j]))
y := polIn.Coeffs[i]
z[0] = y[x[0]]
z[1] = y[x[1]]
z[2] = y[x[2]]
z[3] = y[x[3]]
z[4] = y[x[4]]
z[5] = y[x[5]]
z[6] = y[x[6]]
z[7] = y[x[7]]
}
}
}
// PermuteNTTWithIndexAndAddNoModLvl applies the Galois transform on a polynomial in the NTT domain, up to a given level,
// and adds the result to the output polynomial without modular reduction.
// It maps the coefficients x^i to x^(gen*i) using the PermuteNTTIndex table.
// It must be noted that the result cannot be in-place.
func (r *Ring) PermuteNTTWithIndexAndAddNoModLvl(level int, polIn *Poly, index []uint64, polOut *Poly) {
for j := 0; j < r.N; j = j + 8 {
x := (*[8]uint64)(unsafe.Pointer(&index[j]))
for i := 0; i < level+1; i++ {
z := (*[8]uint64)(unsafe.Pointer(&polOut.Coeffs[i][j]))
y := polIn.Coeffs[i]
z[0] += y[x[0]]
z[1] += y[x[1]]
z[2] += y[x[2]]
z[3] += y[x[3]]
z[4] += y[x[4]]
z[5] += y[x[5]]
z[6] += y[x[6]]
z[7] += y[x[7]]
}
}
}
// Permute applies the Galois transform on a polynomial outside of the NTT domain.
// It maps the coefficients x^i to x^(gen*i)
// It must be noted that the result cannot be in-place.
func (r *Ring) Permute(polIn *Poly, gen uint64, polOut *Poly) {
var mask, index, indexRaw, logN, tmp uint64
mask = uint64(r.N - 1)
logN = uint64(bits.Len64(mask))
for i := uint64(0); i < uint64(r.N); i++ {
indexRaw = i * gen
index = indexRaw & mask
tmp = (indexRaw >> logN) & 1
for j, qi := range r.Modulus {
polOut.Coeffs[j][index] = polIn.Coeffs[j][i]*(tmp^1) | (qi-polIn.Coeffs[j][i])*tmp
}
}
}