-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathalgorithms.go
113 lines (76 loc) · 2.73 KB
/
algorithms.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
package ckks
import (
"math/bits"
)
// PowerOf2 computes op^(2^logPow2), consuming logPow2 levels, and returns the result on opOut. Providing an evaluation
// key is necessary when logPow2 > 1.
func (eval *evaluator) PowerOf2(op *Ciphertext, logPow2 int, opOut *Ciphertext) {
if logPow2 == 0 {
if op != opOut {
opOut.Copy(op.El())
}
} else {
eval.MulRelin(op.El(), op.El(), opOut)
if err := eval.Rescale(opOut, eval.scale, opOut); err != nil {
panic(err)
}
for i := 1; i < logPow2; i++ {
eval.MulRelin(opOut.El(), opOut.El(), opOut)
if err := eval.Rescale(opOut, eval.scale, opOut); err != nil {
panic(err)
}
}
}
}
// PowerNew computes op^degree, consuming log(degree) levels, and returns the result on a new element. Providing an evaluation
// key is necessary when degree > 2.
func (eval *evaluator) PowerNew(op *Ciphertext, degree int) (opOut *Ciphertext) {
opOut = NewCiphertext(eval.params, 1, op.Level(), op.Scale())
eval.Power(op, degree, opOut)
return
}
// Power computes op^degree, consuming log(degree) levels, and returns the result on opOut. Providing an evaluation
// key is necessary when degree > 2.
func (eval *evaluator) Power(op *Ciphertext, degree int, opOut *Ciphertext) {
if degree < 1 {
panic("eval.Power -> degree cannot be smaller than 1")
}
tmpct0 := op.CopyNew()
var logDegree, po2Degree int
logDegree = bits.Len64(uint64(degree)) - 1
po2Degree = 1 << logDegree
eval.PowerOf2(tmpct0.Ciphertext(), logDegree, opOut)
degree -= po2Degree
for degree > 0 {
logDegree = bits.Len64(uint64(degree)) - 1
po2Degree = 1 << logDegree
tmp := NewCiphertext(eval.params, 1, tmpct0.Level(), tmpct0.Scale())
eval.PowerOf2(tmpct0.Ciphertext(), logDegree, tmp)
eval.MulRelin(opOut.El(), tmp.El(), opOut)
if err := eval.Rescale(opOut, eval.scale, opOut); err != nil {
panic(err)
}
degree -= po2Degree
}
}
// InverseNew computes 1/op and returns the result on a new element, iterating for n steps and consuming n levels. The algorithm requires the encrypted values to be in the range
// [-1.5 - 1.5i, 1.5 + 1.5i] or the result will be wrong. Each iteration increases the precision.
func (eval *evaluator) InverseNew(op *Ciphertext, steps int) (opOut *Ciphertext) {
cbar := eval.NegNew(op)
eval.AddConst(cbar, 1, cbar)
tmp := eval.AddConstNew(cbar, 1)
opOut = tmp.CopyNew().Ciphertext()
for i := 1; i < steps; i++ {
eval.MulRelin(cbar.El(), cbar.El(), cbar.Ciphertext())
if err := eval.Rescale(cbar, eval.scale, cbar); err != nil {
panic(err)
}
tmp = eval.AddConstNew(cbar, 1)
eval.MulRelin(tmp.El(), opOut.El(), tmp.Ciphertext())
if err := eval.Rescale(tmp, eval.scale, tmp); err != nil {
panic(err)
}
opOut = tmp.CopyNew().Ciphertext()
}
return opOut
}