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}