ckks_engine/arithmetic.rs
1use crate::ckks::CKKSEncryptor;
2use crate::polynomial::Polynomial;
3use crate::utils::{mod_reduce};
4
5impl CKKSEncryptor {
6
7 // Function to perform homomorphic addition on two encrypted polynomials (ciphertexts)
8 pub fn homomorphic_add(&self, cipher1: &Polynomial, cipher2: &Polynomial) -> Polynomial {
9
10 // Add the two polynomials (ciphertexts). Assuming the ciphertexts have the same scaling factor
11 let result = cipher1.add(cipher2);
12
13 // Perform modular reduction to ensure the result fits within the modulus
14 let reduced_result = mod_reduce(&result, self.params.modulus);
15
16 // Return the reduced result as the final homomorphic addition result
17 reduced_result
18 }
19
20 // Function to perform homomorphic subtraction on two encrypted polynomials (ciphertexts)
21 pub fn homomorphic_subtract(&self, cipher1: &Polynomial, cipher2: &Polynomial) -> Polynomial {
22
23 // Subtract the second polynomial (cipher2) from the first (cipher1)
24 let result = cipher1.subtract(cipher2);
25
26 // Perform modular reduction to ensure the result fits within the modulus
27 let reduced_result = mod_reduce(&result, self.params.modulus);
28
29 // Return the reduced result as the final homomorphic subtraction result
30 reduced_result
31 }
32
33 // Function to perform homomorphic multiplication on two encrypted polynomials (ciphertexts)
34 pub fn homomorphic_multiply(&self, cipher1: &Polynomial, cipher2: &Polynomial) -> Polynomial {
35
36 // Multiply the two polynomials (ciphertexts). The result size is determined by the degree of the polynomials
37 let result = cipher1.multiply(cipher2);
38
39 // Perform modular reduction to ensure the result fits within the modulus
40 let reduced_result = mod_reduce(&result, self.params.modulus);
41
42 // Return the reduced result as the final homomorphic multiplication result
43 reduced_result
44 }
45
46 // Function to perform homomorphic negation on an encrypted polynomial (ciphertext)
47 pub fn homomorphic_negation(&self, cipher1: &Polynomial) -> Polynomial {
48
49 // Negate the coefficients of the polynomial (ciphertext)
50 let negated_poly = cipher1.negation();
51
52 // Perform modular reduction to ensure the negated result fits within the modulus
53 let reduced_result = mod_reduce(&negated_poly, self.params.modulus);
54
55 // Return the reduced result as the final homomorphic negation result
56 reduced_result
57 }
58
59 // Function to perform homomorphic ceil on encrypted polynomials (ciphertexts)
60 pub fn homomorphic_ceil(&self, cipher: &Polynomial) -> Polynomial {
61 // This function will operate on encrypted coefficients
62 let ceil_poly: Vec<i64> = cipher.coeffs.iter().map(|&c| {
63 let scaled_value = (c as f64) / 1e7; // scale down
64 let ceil_value = scaled_value.ceil() as i64; // apply ceil
65 (ceil_value as i64) * (1e7 as i64) // scale up back after ceil
66 }).collect();
67
68 // Return the new polynomial with ceil applied on encrypted data
69 let ceil_polynomial = Polynomial::new(ceil_poly);
70
71 // Perform modular reduction to ensure the result fits within the modulus
72 let reduced_result = mod_reduce(&ceil_polynomial, self.params.modulus);
73
74 // Return the reduced result
75 reduced_result
76 }
77
78 // Function to perform homomorphic floor on encrypted polynomials (ciphertexts)
79 pub fn homomorphic_floor(&self, cipher: &Polynomial) -> Polynomial {
80 // This function will operate on encrypted coefficients
81 let floor_poly: Vec<i64> = cipher.coeffs.iter().map(|&c| {
82 let scaled_value = (c as f64) / 1e7; // scale down
83 let floor_value = scaled_value.floor() as i64; // apply floor
84 (floor_value as i64) * (1e7 as i64) // scale up back after floor
85 }).collect();
86
87 // Return the new polynomial with floor applied on encrypted data
88 let floor_polynomial = Polynomial::new(floor_poly);
89
90 // Perform modular reduction to ensure the result fits within the modulus
91 let reduced_result = mod_reduce(&floor_polynomial, self.params.modulus);
92
93 // Return the reduced result
94 reduced_result
95 }
96
97
98 // Function to perform homomorphic round on encrypted polynomials (ciphertexts)
99 pub fn homomorphic_round(&self, cipher: &Polynomial) -> Polynomial {
100 // Operate on encrypted coefficients
101 let round_poly: Vec<i64> = cipher.coeffs.iter().map(|&c| {
102 let scaled_value = (c as f64) / 1e7; // Scale down
103 let rounded_value = scaled_value.round() as i64; // Apply round
104 (rounded_value as i64) * (1e7 as i64) // Scale up back after rounding
105 }).collect();
106
107 // Create a new polynomial with rounded coefficients
108 let rounded_polynomial = Polynomial::new(round_poly);
109
110 // Perform modular reduction to ensure the result fits within the modulus
111 let reduced_result = mod_reduce(&rounded_polynomial, self.params.modulus);
112
113 // Return the reduced result
114 reduced_result
115 }
116
117 pub fn homomorphic_truncate(&self, cipher: &Polynomial) -> Polynomial {
118 let scale: i64 = 10_000_000; // Defining scaling factor as integer (1e7)
119
120 // Truncate each coefficient by performing integer division and scaling back up
121 let truncate_poly: Vec<i64> = cipher.coeffs.iter().map(|&c| (c / scale) * scale).collect();
122
123 let truncated_polynomial = Polynomial::new(truncate_poly);
124
125 let reduced_result = mod_reduce(&truncated_polynomial, self.params.modulus);
126 reduced_result
127 }
128
129 pub fn homomorphic_reciprocal(&self, cipher: &Polynomial, iterations: u32) -> Polynomial {
130 let scale: i64 = 10_000_000; // Define scaling factor as integer (1e7)
131
132 // Initialize the reciprocal with a closer initial guess
133 let mut reciprocal = Polynomial::new(vec![scale / 2]); // Represents 0.5
134
135 for _i in 0..iterations {
136 // Step 1: Compute c * x_n / scale
137 let temp = self.homomorphic_multiply(cipher, &reciprocal);
138 let temp_coeff = temp.coeffs[0];
139
140 // Step 2: Compute 2 * scale - temp_coeff
141 let two_scale = scale * 2;
142 let updated_coeff = two_scale - temp_coeff;
143
144 // Step 3: Multiply the updated_coeff with the current reciprocal
145 let updated_poly = Polynomial::new(vec![updated_coeff]);
146 let multiplied = self.homomorphic_multiply(&updated_poly, &reciprocal);
147
148 // Step 4: Update the reciprocal
149 reciprocal = multiplied;
150 }
151
152 reciprocal
153 }
154
155 pub fn homomorphic_divide(&self, cipher1: &Polynomial, cipher2: &Polynomial) -> Polynomial {
156 let scaling_factor = 1e7; // Use a scaling factor for precision
157
158 // Use the divide function from the Polynomial struct
159 let result_poly = cipher1.divide(cipher2, scaling_factor);
160
161 // Apply modular reduction to keep coefficients within the bounds of the modulus
162 let reduced_result = mod_reduce(&result_poly, self.params.modulus);
163
164 reduced_result // Return the final homomorphic division result
165 }
166
167
168
169 // Function to perform homomorphic exponentiation on an encrypted polynomial (ciphertext)
170 pub fn homomorphic_exponentiation(&self, cipher: &Polynomial, exponent: u32) -> Polynomial {
171 if exponent == 0 {
172 // Return polynomial representing 1 (scaled by 1e7)
173 return Polynomial::new(vec![10000000]);
174 }
175
176 if exponent == 1 {
177 return cipher.clone();
178 }
179
180 // Initialize the result with the original ciphertext
181 let mut result = cipher.clone();
182
183 // Perform repeated multiplication
184 for _ in 1..exponent {
185 // Multiply the result by cipher polynomial
186 let temp = self.homomorphic_multiply(&result, cipher);
187 result = temp;
188 }
189 // Perform modular reduction to ensure the result fits within the modulus
190 let reduced_result = mod_reduce(&result, self.params.modulus);
191
192 reduced_result
193 }
194
195 pub fn homomorphic_divide_with_constant(&self, cipher: &Polynomial, constant: i64) -> Polynomial {
196 // Gracefully handle the case when the constant is zero
197 if constant == 0 {
198 return cipher.clone(); // Return the original polynomial as is
199 }
200
201 // Scale the constant to match the ciphertext's scale
202 let scaling_factor = 10_000_000; // Assuming 1e7 scaling factor
203 let scaled_constant = constant * scaling_factor;
204
205 // Compute the reciprocal of the scaled constant
206 let reciprocal = scaling_factor / scaled_constant; // This is effectively 1/constant in scaled form
207
208 // Multiply the ciphertext by the reciprocal
209 let scaled_reciprocal_poly = Polynomial::new(vec![reciprocal]);
210 let result = self.homomorphic_multiply(cipher, &scaled_reciprocal_poly);
211
212 // Perform modular reduction to ensure the result fits within the modulus
213 let reduced_result = mod_reduce(&result, self.params.modulus);
214
215 reduced_result
216 }
217
218
219}