/
repacking.go
119 lines (89 loc) · 3.7 KB
/
repacking.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
package pde
import (
"fmt"
"math/bits"
"github.com/tuneinsight/lattigo/v5/core/rlwe"
"github.com/tuneinsight/lattigo/v5/ring"
)
type RepackEvaluator struct {
*RepackEvaluationKeySet
Evaluators map[int]*rlwe.Evaluator
XPow2NTT map[int][]ring.Poly
}
func NewRepackEvaluator(evk *RepackEvaluationKeySet) *RepackEvaluator {
Evaluators := map[int]*rlwe.Evaluator{}
XPow2NTT := map[int][]ring.Poly{}
minLogN := evk.MinLogN()
maxLogN := evk.MaxLogN()
levelQ := evk.Parameters[minLogN].GetRLWEParameters().MaxLevel()
for i := minLogN; i < maxLogN+1; i++ {
pi := evk.Parameters[i].GetRLWEParameters()
Evaluators[i] = rlwe.NewEvaluator(pi, evk.RepackKeys[i])
XPow2NTT[i] = GenXPow2NTT(pi.RingQ().AtLevel(levelQ), pi.LogN(), false)
}
return &RepackEvaluator{
RepackEvaluationKeySet: evk,
Evaluators: Evaluators,
XPow2NTT: XPow2NTT,
}
}
func (eval RepackEvaluator) Pack(cts map[int]*rlwe.Ciphertext) (ct *rlwe.Ciphertext, err error) {
return eval.Evaluators[LogNPack].Pack(cts, LogNPack, true)
}
// Merge merges two ciphertexts of degree N/2 into a ciphertext of degre N:
// ctN[X] = ctEvenNHalf[Y] + X * ctOddNHalf[Y] where Y = X^2.
func (eval RepackEvaluator) Merge(ctEvenNHalf, ctOddNHalf, ctN *rlwe.Ciphertext) (err error) {
if eval.MinLogN() == eval.MaxLogN() {
return fmt.Errorf("method is not supported when eval.MinLogN() == eval.MaxLogN()")
}
if ctEvenNHalf == nil {
return fmt.Errorf("ctEvenNHalf cannot be nil")
}
if bits.Len64(uint64(len(ctEvenNHalf.Value[0].Coeffs[0])-1)) >= eval.MaxLogN() {
return fmt.Errorf("ctEvenNHalf.LogN() must be smaller than eval.MaxLogN()")
}
if bits.Len64(uint64(len(ctN.Value[0].Coeffs[0])-1)) != bits.Len64(uint64(len(ctEvenNHalf.Value[0].Coeffs[0])-1))+1 {
return fmt.Errorf("ctN.LogN() must be equal to ctEvenNHalf.LogN()+1")
}
if ctOddNHalf != nil {
if bits.Len64(uint64(len(ctEvenNHalf.Value[0].Coeffs[0])-1)) != bits.Len64(uint64(len(ctOddNHalf.Value[0].Coeffs[0])-1)) {
return fmt.Errorf("ctEvenNHalf.LogN() and ctOddNHalf.LogN() must be equal")
}
}
LogN := bits.Len64(uint64(len(ctN.Value[0].Coeffs[0]) - 1))
evalN := eval.Evaluators[LogN]
evkNHalfToN := eval.RingSwitchingKeys[LogN-1][LogN]
r := eval.Parameters[LogN].GetRLWEParameters().RingQ().AtLevel(ctN.Level())
ctTmp := rlwe.NewCiphertext(eval.Parameters[LogN], 1, ctN.Level())
if ctEvenNHalf != nil {
*ctN.MetaData = *ctEvenNHalf.MetaData
rlwe.SwitchCiphertextRingDegreeNTT(ctEvenNHalf.El(), r, ctN.El())
if ctOddNHalf != nil {
rlwe.SwitchCiphertextRingDegreeNTT(ctOddNHalf.El(), r, ctTmp.El())
r.MulCoeffsMontgomeryThenAdd(ctTmp.Value[0], eval.XPow2NTT[LogN][0], ctN.Value[0])
r.MulCoeffsMontgomeryThenAdd(ctTmp.Value[1], eval.XPow2NTT[LogN][0], ctN.Value[1])
}
}
// SkNHalf -> SkN
if err = evalN.ApplyEvaluationKey(ctN, evkNHalfToN, ctN); err != nil {
return fmt.Errorf("evalN.ApplyEvaluationKey(ctN, evkNToNHalf, ctN): %w", err)
}
ctN.LogDimensions.Cols++
return
}
// MergeNew merges two ciphertexts of degree N/2 into a ciphertext of degre N:
// ctN[X] = ctEvenNHalf[Y] + X * ctOddNHalf[Y] where Y = X^2.
func (eval RepackEvaluator) MergeNew(ctEvenNHalf, ctOddNHalf *rlwe.Ciphertext) (ctN *rlwe.Ciphertext, err error) {
if eval.MinLogN() == eval.MaxLogN() {
return nil, fmt.Errorf("method is not supported when eval.MinLogN() == eval.MaxLogN()")
}
if ctEvenNHalf == nil {
return nil, fmt.Errorf("ctEvenNHalf cannot be nil")
}
LogN := bits.Len64(uint64(len(ctEvenNHalf.Value[0].Coeffs[0]) - 1))
if LogN >= eval.MaxLogN() {
return nil, fmt.Errorf("ctEvenNHalf.LogN() must be smaller than eval.MaxLogN()")
}
ctN = rlwe.NewCiphertext(eval.Parameters[LogN+1], 1, ctEvenNHalf.Level())
return ctN, eval.Merge(ctEvenNHalf, ctOddNHalf, ctN)
}