ckks_engine/
polynomial.rs

1use std::panic;
2
3#[derive(Debug, Clone)]
4pub struct Polynomial {
5    pub coeffs: Vec<i64>,  // Coefficients for the polynomial
6}
7
8impl Polynomial {
9    // Constructor to create a new Polynomial with given coefficients
10    pub fn new(coeffs: Vec<i64>) -> Self {
11        Polynomial { coeffs }
12    }
13
14    //wrote this only for homomorphic truncate - we haven't used this anywhere apart from it
15    pub fn decode(&self) -> Vec<i64> {
16        self.coeffs.iter().map(|&c| {
17            let real = (c as f64) / 10_000_000.0;
18            real.round() as i64 // Round to the nearest integer
19        }).collect()
20    }
21
22    // Polynomial addition
23    pub fn add(&self, other: &Polynomial) -> Polynomial {
24        // Determine the maximum length of the two polynomials
25        let len = self.coeffs.len().max(other.coeffs.len());
26        let mut result = vec![0; len];  // Initialize result vector with zeros
27
28        // Add coefficients of both polynomials
29        for i in 0..len {
30            let a = if i < self.coeffs.len() { self.coeffs[i] } else { 0 };  // Get coefficient from self or 0 if out of bounds
31            let b = if i < other.coeffs.len() { other.coeffs[i] } else { 0 }; // Get coefficient from other or 0 if out of bounds
32            result[i] = a + b;  // Add coefficients
33        }
34
35        Polynomial::new(result)  // Return new polynomial as the result
36    }
37
38    // Polynomial subtraction
39    pub fn subtract(&self, other: &Polynomial) -> Polynomial {
40        // Determine the maximum length of the two polynomials
41        let len = self.coeffs.len().max(other.coeffs.len());
42        let mut result = vec![0; len];  // Initialize result vector with zeros
43
44        // Subtract coefficients of the second polynomial from the first
45        for i in 0..len {
46            let a = if i < self.coeffs.len() { self.coeffs[i] } else { 0 };  // Get coefficient from self or 0 if out of bounds
47            let b = if i < other.coeffs.len() { other.coeffs[i] } else { 0 }; // Get coefficient from other or 0 if out of bounds
48            result[i] = a - b;  // Subtract coefficients
49        }
50
51        Polynomial::new(result)  // Return new polynomial as the result
52    }
53
54    // Polynomial multiplication
55    pub fn multiply(&self, other: &Polynomial) -> Polynomial {
56        // Determine size for the resulting polynomial
57        let result_size = self.coeffs.len().max(other.coeffs.len());
58        let mut result_coeffs = vec![0.0; result_size]; // Initialize result coefficients with f64 for scaling
59
60        // Multiply matching coefficients of both polynomials
61        let min_len = self.coeffs.len().min(other.coeffs.len());
62        for i in 0..min_len {
63            result_coeffs[i] = (self.coeffs[i] as f64 * other.coeffs[i] as f64) / 1e7; // Scale and store the product
64        }
65        // Create a new polynomial with rounded coefficients
66        Polynomial::new(result_coeffs.iter().map(|&x| x.round() as i64).collect())
67    }
68
69    // Polynomial negation
70    pub fn negation(&self) -> Polynomial {
71        // Negate each coefficient of the polynomial
72        let negated_coeffs: Vec<f64> = self.coeffs.iter().map(|&c| -(c as f64)).collect();
73        // Create a new polynomial with rounded negated coefficients
74        Polynomial::new(negated_coeffs.iter().map(|&x| x.round() as i64).collect())
75    }
76
77    pub fn divide(&self, divisor: &Polynomial, scaling_factor: f64) -> Polynomial {
78        let mut result_coeffs = Vec::new();
79
80        // Handle scalar division if divisor has only one coefficient
81        if divisor.coeffs.len() == 1 {
82            let scalar = divisor.coeffs[0];
83            if scalar == 0 {
84                eprintln!("Division by zero encountered in scalar divisor. Skipping computation.");
85                return Polynomial::new(vec![]); // Return an empty polynomial
86            }
87
88            for a in &self.coeffs {
89                let result = panic::catch_unwind(|| {
90                    let scaled_result = (*a as f64 / scalar as f64) * scaling_factor;
91                    scaled_result.round() as i64
92                });
93
94                match result {
95                    Ok(value) => result_coeffs.push(value),
96                    Err(_) => {
97                        eprintln!("Panic occurred during scalar division. Skipping coefficient.");
98                        result_coeffs.push(0); // Default value on panic
99                    }
100                }
101            }
102        } else {
103            // Handle polynomial division
104            for i in 0..std::cmp::max(self.coeffs.len(), divisor.coeffs.len()) {
105                let a = self.coeffs.get(i).copied().unwrap_or(0); // Default to 0 if self.coeffs is shorter
106                let b = divisor.coeffs.get(i).copied().unwrap_or(0); // Default to 0 for missing divisor terms
107
108                if b == 0 {
109                    eprintln!(
110                        "Division by zero encountered at index {}. Defaulting to 0.",
111                        i
112                    );
113                    result_coeffs.push(0);
114                    continue;
115                }
116
117                let result = panic::catch_unwind(|| {
118                    let scaled_result = (a as f64 / b as f64) * scaling_factor;
119                    scaled_result.round() as i64
120                });
121
122                match result {
123                    Ok(value) => result_coeffs.push(value),
124                    Err(_) => {
125                        eprintln!("Panic occurred during polynomial division at index {}. Defaulting to 0.", i);
126                        result_coeffs.push(0); // Default value on panic
127                    }
128                }
129            }
130        }
131
132        // Return a new polynomial with the resulting coefficients after division
133        Polynomial::new(result_coeffs)
134    }
135}