arnaucube
arnaucube/testudoPublic Self-hosted repository
testudo/src/unipoly.rs
src/unipoly.rsRaw
  1use crate::poseidon_transcript::{PoseidonTranscript, TranscriptWriter};
  2use ark_crypto_primitives::sponge::Absorb;
  3use ark_ff::{Field, PrimeField};
  4use ark_serialize::*;
  5// ax^2 + bx + c stored as vec![c,b,a]
  6// ax^3 + bx^2 + cx + d stored as vec![d,c,b,a]
  7#[derive(Debug, CanonicalDeserialize, CanonicalSerialize, Clone)]
  8pub struct UniPoly<F: Field> {
  9  pub coeffs: Vec<F>,
 10  // pub coeffs_fq: Vec<Fq>,
 11}
 12
 13// ax^2 + bx + c stored as vec![c,a]
 14// ax^3 + bx^2 + cx + d stored as vec![d,b,a]
 15#[derive(CanonicalSerialize, CanonicalDeserialize, Debug)]
 16pub struct CompressedUniPoly<F: Field> {
 17  pub coeffs_except_linear_term: Vec<F>,
 18}
 19
 20impl<F: Field> UniPoly<F> {
 21  pub fn from_evals(evals: &[F]) -> Self {
 22    // we only support degree-2 or degree-3 univariate polynomials
 23    assert!(evals.len() == 3 || evals.len() == 4);
 24    let coeffs = if evals.len() == 3 {
 25      // ax^2 + bx + c
 26      let two_inv = F::from(2 as u8).inverse().unwrap();
 27
 28      let c = evals[0];
 29      let a = two_inv * (evals[2] - evals[1] - evals[1] + c);
 30      let b = evals[1] - c - a;
 31      vec![c, b, a]
 32    } else {
 33      // ax^3 + bx^2 + cx + d
 34      let two_inv = F::from(2 as u8).inverse().unwrap();
 35      let six_inv = F::from(6 as u8).inverse().unwrap();
 36
 37      let d = evals[0];
 38      let a = six_inv
 39        * (evals[3] - evals[2] - evals[2] - evals[2] + evals[1] + evals[1] + evals[1] - evals[0]);
 40      let b = two_inv
 41        * (evals[0] + evals[0] - evals[1] - evals[1] - evals[1] - evals[1] - evals[1]
 42          + evals[2]
 43          + evals[2]
 44          + evals[2]
 45          + evals[2]
 46          - evals[3]);
 47      let c = evals[1] - d - a - b;
 48      vec![d, c, b, a]
 49    };
 50
 51    UniPoly { coeffs }
 52  }
 53
 54  pub fn degree(&self) -> usize {
 55    self.coeffs.len() - 1
 56  }
 57
 58  pub fn eval_at_zero(&self) -> F {
 59    self.coeffs[0]
 60  }
 61
 62  pub fn eval_at_one(&self) -> F {
 63    (0..self.coeffs.len()).map(|i| self.coeffs[i]).sum()
 64  }
 65
 66  pub fn evaluate(&self, r: &F) -> F {
 67    let mut eval = self.coeffs[0];
 68    let mut power = *r;
 69    for i in 1..self.coeffs.len() {
 70      eval += power * self.coeffs[i];
 71      power *= r;
 72    }
 73    eval
 74  }
 75  // pub fn compress(&self) -> CompressedUniPoly<F> {
 76  //   let coeffs_except_linear_term = [&self.coeffs[..1], &self.coeffs[2..]].concat();
 77  //   assert_eq!(coeffs_except_linear_term.len() + 1, self.coeffs.len());
 78  //   CompressedUniPoly {
 79  //     coeffs_except_linear_term,
 80  //   }
 81  // }
 82}
 83
 84// impl<F: PrimeField> CompressedUniPoly<F> {
 85//   // we require eval(0) + eval(1) = hint, so we can solve for the linear term as:
 86//   // linear_term = hint - 2 * constant_term - deg2 term - deg3 term
 87//   pub fn decompress(&self, hint: &F) -> UniPoly<F> {
 88//     let mut linear_term =
 89//       (*hint) - self.coeffs_except_linear_term[0] - self.coeffs_except_linear_term[0];
 90//     for i in 1..self.coeffs_except_linear_term.len() {
 91//       linear_term -= self.coeffs_except_linear_term[i];
 92//     }
 93
 94//     let mut coeffs = vec![self.coeffs_except_linear_term[0], linear_term];
 95//     coeffs.extend(&self.coeffs_except_linear_term[1..]);
 96//     assert_eq!(self.coeffs_except_linear_term.len() + 1, coeffs.len());
 97//     UniPoly { coeffs }
 98//   }
 99// }
100
101impl<F: PrimeField + Absorb> TranscriptWriter<F> for UniPoly<F> {
102  fn write_to_transcript(&self, transcript: &mut PoseidonTranscript<F>) {
103    // transcript.append_message(label, b"UniPoly_begin");
104    for i in 0..self.coeffs.len() {
105      transcript.append_scalar(b"", &self.coeffs[i]);
106    }
107    // transcript.append_message(label, b"UniPoly_end");
108  }
109}
110
111#[cfg(test)]
112mod tests {
113
114  use ark_ff::One;
115
116  use super::*;
117
118  type F = ark_bls12_377::Fr;
119
120  #[test]
121  fn test_from_evals_quad() {
122    // polynomial is 2x^2 + 3x + 1
123    let e0 = F::one();
124    let e1 = F::from(6 as u8);
125    let e2 = F::from(15 as u8);
126    let evals = vec![e0, e1, e2];
127    let poly = UniPoly::from_evals(&evals);
128
129    assert_eq!(poly.eval_at_zero(), e0);
130    assert_eq!(poly.eval_at_one(), e1);
131    assert_eq!(poly.coeffs.len(), 3);
132    assert_eq!(poly.coeffs[0], F::one());
133    assert_eq!(poly.coeffs[1], F::from(3 as u8));
134    assert_eq!(poly.coeffs[2], F::from(2 as u8));
135
136    // let hint = e0 + e1;
137    // // let compressed_poly = poly.compress();
138    // // let decompressed_poly = compressed_poly.decompress(&hint);
139    // for i in 0..poly.coeffs.len() {
140    //   assert_eq!(poly.coeffs[i], poly.coeffs[i]);
141    // }
142
143    let e3 = F::from(28 as u8);
144    assert_eq!(poly.evaluate(&F::from(3 as u8)), e3);
145  }
146
147  #[test]
148  fn test_from_evals_cubic() {
149    // polynomial is x^3 + 2x^2 + 3x + 1
150    let e0 = F::one();
151    let e1 = F::from(7);
152    let e2 = F::from(23);
153    let e3 = F::from(55);
154    let evals = vec![e0, e1, e2, e3];
155    let poly = UniPoly::from_evals(&evals);
156
157    assert_eq!(poly.eval_at_zero(), e0);
158    assert_eq!(poly.eval_at_one(), e1);
159    assert_eq!(poly.coeffs.len(), 4);
160    assert_eq!(poly.coeffs[0], F::one());
161    assert_eq!(poly.coeffs[1], F::from(3));
162    assert_eq!(poly.coeffs[2], F::from(2));
163    assert_eq!(poly.coeffs[3], F::from(1));
164
165    // let hint = e0 + e1;
166    // let compressed_poly = poly.compress();
167    // let decompressed_poly = compressed_poly.decompress(&hint);
168    // for i in 0..decompressed_poly.coeffs.len() {
169    //   assert_eq!(decompressed_poly.coeffs[i], poly.coeffs[i]);
170    // }
171
172    let e4 = F::from(109);
173    assert_eq!(poly.evaluate(&F::from(4)), e4);
174  }
175}