labrador/core/
inner_product.rs1use std::{
2 borrow::Borrow,
3 ops::{Add, Mul},
4};
5
6pub fn compute_linear_combination<E, B, C>(elements: &[E], challenges: &[C]) -> B
51where
52 E: Borrow<B>,
53 for<'a> &'a B: Mul<&'a C, Output = B>,
54 for<'a> &'a B: Add<&'a B, Output = B>,
55{
56 debug_assert_eq!(
57 elements.len(),
58 challenges.len(),
59 "vectors must be the same length"
60 );
61 debug_assert!(!elements.is_empty(), "`elements` must not be empty");
62
63 let mut zipped_iter = elements.iter().zip(challenges.iter());
64 let (e0, c0) = zipped_iter.next().unwrap();
66 let init = e0.borrow() * c0;
67
68 zipped_iter.fold(init, |acc, (elem, c)| &acc + &(elem.borrow() * c))
69}
70
71#[cfg(test)]
72mod tests {
73 use rand::rng;
74
75 use crate::ring::{rq::Rq, rq_matrix::RqMatrix, rq_vector::RqVector, zq::Zq};
76
77 use super::*;
78
79 #[test]
80 #[should_panic]
81 fn test_inputs_with_different_lengths_panic() {
82 let vector_a = RqVector::random(&mut rng(), 100);
83 let vector_b = RqVector::random(&mut rng(), 90);
84 let _ = compute_linear_combination(vector_a.get_elements(), vector_b.get_elements());
85 }
86
87 #[test]
88 fn test_zq_type_inputs() {
89 let poly_a = Rq::random(&mut rng());
90 let poly_b = Rq::random(&mut rng());
91
92 let result =
93 compute_linear_combination(poly_a.get_coefficients(), poly_b.get_coefficients());
94
95 let mut expected = Zq::ZERO;
96 for i in 0..poly_a.get_coefficients().len() {
97 expected += poly_a.get_coefficients()[i] * poly_b.get_coefficients()[i];
98 }
99 assert_eq!(result, expected);
100 }
101
102 #[test]
103 fn test_rq_type_inputs() {
104 let poly_vec_a = RqVector::random(&mut rng(), 100);
105 let poly_vec_b = RqVector::random(&mut rng(), 100);
106
107 let result =
108 compute_linear_combination(poly_vec_a.get_elements(), poly_vec_b.get_elements());
109
110 let mut expected = Rq::zero();
111 for i in 0..poly_vec_a.get_elements().len() {
112 expected = &expected + &(&poly_vec_a.get_elements()[i] * &poly_vec_b.get_elements()[i]);
113 }
114 assert_eq!(result, expected);
115 }
116
117 #[test]
118 fn test_zq_rq_type_inputs() {
119 let poly_a = Rq::random(&mut rng());
120 let poly_vec_b = RqVector::random(&mut rng(), 64);
121
122 let result =
123 compute_linear_combination(poly_vec_b.get_elements(), poly_a.get_coefficients());
124
125 let mut expected = Rq::zero();
126 for i in 0..poly_a.get_coefficients().len() {
127 expected = &expected + &(&poly_vec_b.get_elements()[i] * &poly_a.get_coefficients()[i]);
128 }
129 assert_eq!(result, expected);
130 }
131
132 #[test]
133 fn test_zq_rqvector_type_inputs() {
134 let poly_a = Rq::random(&mut rng());
135 let poly_vec_b = RqMatrix::random(&mut rng(), 64, 100);
136
137 let result =
138 compute_linear_combination(poly_vec_b.get_elements(), poly_a.get_coefficients());
139
140 let mut expected = RqVector::zero(100);
141 for i in 0..poly_a.get_coefficients().len() {
142 expected = &expected + &(&poly_vec_b.get_elements()[i] * poly_a.get_coefficients()[i]);
143 }
144 assert_eq!(result, expected);
145 }
146
147 #[test]
148 fn test_rq_rqvector_type_inputs() {
149 let poly_vec_a = RqVector::random(&mut rng(), 80);
150 let poly_vec_b = RqMatrix::random(&mut rng(), 80, 100);
151
152 let result =
153 compute_linear_combination(poly_vec_b.get_elements(), poly_vec_a.get_elements());
154
155 let mut expected = RqVector::zero(100);
156 for i in 0..poly_vec_a.get_elements().len() {
157 expected = &expected + &(&poly_vec_b.get_elements()[i] * &poly_vec_a.get_elements()[i]);
158 }
159 assert_eq!(result, expected);
160 }
161}