Skip to content

Commit 3b01bac

Browse files
committed
clean up polynomial arithmetic and degree
1 parent 0b66bfa commit 3b01bac

File tree

2 files changed

+102
-113
lines changed

2 files changed

+102
-113
lines changed

src/polynomial_ring/poly.rs

Lines changed: 58 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,7 @@ use std::{
1010
};
1111

1212
/// Trait for arithmetic for univariate polynomials in Fp[X]
13-
pub trait Poly:
14-
Index<usize>
15-
+ IndexMut<usize>
16-
+ Sized
17-
+ Neg<Output = Self>
18-
+ Add<Output = Self>
19-
+ AddAssign
20-
+ Sub<Output = Self>
21-
+ SubAssign
22-
+ Mul<Output = Self>
23-
+ MulAssign
24-
+ Display
25-
{
13+
pub trait Poly: Index<usize> + IndexMut<usize> + Sized + Display {
2614
// TODO
2715
}
2816

@@ -47,14 +35,28 @@ impl<Fp: FpTrait> Polynomial<Fp> {
4735
self.coeffs.len()
4836
}
4937

50-
/// The degree of the polynomial. TODO: should we trim trailing zeros? If so, how often?
51-
/// We return None for the zero polynomial (instead of -inf which is a bit too big for my
52-
/// computer...)
53-
fn degree(&self) -> Option<usize> {
54-
if self.coeffs.is_empty() {
38+
/// Truncate leading zeros from the polynomial
39+
fn truncate_leading_zeros(&mut self) {
40+
// find the index of the first non-zero element
41+
let mut i = self.len() - 1;
42+
while i > 0 && self.coeffs[i].is_zero() == u32::MAX {
43+
i -= 1;
44+
}
45+
self.coeffs.truncate(i);
46+
}
47+
48+
/// Return the degree of the polynomial.
49+
// TODO: should we trim trailing zeros and mutate while computing the degree or do the following
50+
// and allow trailing zeros to remain.
51+
pub fn degree(&self) -> Option<usize> {
52+
let mut i = self.len() - 1;
53+
while i > 0 && self.coeffs[i].is_zero() == u32::MAX {
54+
i -= 1;
55+
}
56+
if i == 0 && self.coeffs[0].is_zero() == u32::MAX {
5557
None
5658
} else {
57-
Some(self.len() - 1)
59+
Some(i)
5860
}
5961
}
6062

@@ -103,30 +105,17 @@ impl<Fp: FpTrait> Polynomial<Fp> {
103105
is_zero
104106
}
105107

106-
/// Set self to it's negative.
107-
fn set_neg(&mut self) {
108-
for x in self.coeffs.iter_mut() {
109-
x.set_neg();
110-
}
111-
}
112-
113-
/// Set self <- self + other
114-
// TODO: should we assume other has length smaller or equal to
115-
// self, or dynamically extend?
116-
fn set_add(&mut self, other: &Self) {
117-
let k = self.len().min(other.len());
118-
for i in 0..k {
119-
self.coeffs[i] += other[i];
108+
/// Compute f <-- f + g, assumes that the length of f and g are the same.
109+
fn add_into(f: &mut [Fp], g: &[Fp]) {
110+
for i in 0..g.len() {
111+
f[i] += g[i];
120112
}
121113
}
122114

123-
/// Set self <- self - other
124-
// TODO: should we assume other has length smaller or equal to
125-
// self, or dynamically extend?
126-
fn set_sub(&mut self, other: &Self) {
127-
let k = self.len().min(other.len());
128-
for i in 0..k {
129-
self.coeffs[i] -= other[i];
115+
/// Compute f <-- f - g, assumes that the length of f and g are the same.
116+
fn sub_into(f: &mut [Fp], g: &[Fp]) {
117+
for i in 0..g.len() {
118+
f[i] -= g[i];
130119
}
131120
}
132121

@@ -143,6 +132,29 @@ impl<Fp: FpTrait> Polynomial<Fp> {
143132
}
144133
}
145134

135+
/// Set self to it's negative.
136+
fn set_neg(&mut self) {
137+
for x in self.coeffs.iter_mut() {
138+
x.set_neg();
139+
}
140+
}
141+
142+
/// Set self <- self + other
143+
fn set_add(&mut self, other: &Self) {
144+
if self.len() < other.len() {
145+
self.coeffs.resize(other.len(), Fp::ZERO);
146+
}
147+
Self::add_into(&mut self.coeffs, &other.coeffs);
148+
}
149+
150+
/// Set self <- self - other
151+
fn set_sub(&mut self, other: &Self) {
152+
if self.len() < other.len() {
153+
self.coeffs.resize(other.len(), Fp::ZERO);
154+
}
155+
Self::sub_into(&mut self.coeffs, &other.coeffs);
156+
}
157+
146158
/// Set self <- self * other
147159
fn set_mul(&mut self, other: &Self) {
148160
let mut fg_coeffs = vec![Fp::ZERO; self.len() + other.len() - 1];
@@ -179,6 +191,7 @@ impl<Fp: FpTrait> Polynomial<Fp> {
179191
}
180192

181193
/// Evaluate a polynomial at a value `a`
194+
// TODO: is this over engineered?
182195
pub fn evaluate(&self, a: &Fp) -> Fp {
183196
// Handle degree 0 and 1 cases early.
184197
if self.len() == 0 {
@@ -251,29 +264,18 @@ impl<Fp: FpTrait> IndexMut<usize> for Polynomial<Fp> {
251264
}
252265
}
253266

254-
impl<Fp: FpTrait> Neg for Polynomial<Fp> {
267+
impl<Fp: FpTrait> Neg for &Polynomial<Fp> {
255268
type Output = Polynomial<Fp>;
256269

257270
#[inline(always)]
258271
fn neg(self) -> Polynomial<Fp> {
259-
let mut r = self;
272+
let mut r = self.clone();
260273
r.set_neg();
261274
r
262275
}
263276
}
264277

265-
impl<Fp: FpTrait> Add for Polynomial<Fp> {
266-
type Output = Polynomial<Fp>;
267-
268-
#[inline(always)]
269-
fn add(self, other: Polynomial<Fp>) -> Polynomial<Fp> {
270-
let mut r = self;
271-
r.set_add(&other);
272-
r
273-
}
274-
}
275-
276-
impl<Fp: FpTrait> Add for &Polynomial<Fp> {
278+
impl<Fp: FpTrait> Add<&Polynomial<Fp>> for &Polynomial<Fp> {
277279
type Output = Polynomial<Fp>;
278280

279281
#[inline(always)]
@@ -284,32 +286,14 @@ impl<Fp: FpTrait> Add for &Polynomial<Fp> {
284286
}
285287
}
286288

287-
impl<Fp: FpTrait> AddAssign for Polynomial<Fp> {
288-
#[inline(always)]
289-
fn add_assign(&mut self, other: Polynomial<Fp>) {
290-
self.set_add(&other);
291-
}
292-
}
293-
294289
impl<Fp: FpTrait> AddAssign<&Polynomial<Fp>> for Polynomial<Fp> {
295290
#[inline(always)]
296291
fn add_assign(&mut self, other: &Polynomial<Fp>) {
297292
self.set_add(other);
298293
}
299294
}
300295

301-
impl<Fp: FpTrait> Sub for Polynomial<Fp> {
302-
type Output = Polynomial<Fp>;
303-
304-
#[inline(always)]
305-
fn sub(self, other: Polynomial<Fp>) -> Polynomial<Fp> {
306-
let mut r = self;
307-
r.set_sub(&other);
308-
r
309-
}
310-
}
311-
312-
impl<Fp: FpTrait> Sub for &Polynomial<Fp> {
296+
impl<Fp: FpTrait> Sub<&Polynomial<Fp>> for &Polynomial<Fp> {
313297
type Output = Polynomial<Fp>;
314298

315299
#[inline(always)]
@@ -320,32 +304,14 @@ impl<Fp: FpTrait> Sub for &Polynomial<Fp> {
320304
}
321305
}
322306

323-
impl<Fp: FpTrait> SubAssign for Polynomial<Fp> {
324-
#[inline(always)]
325-
fn sub_assign(&mut self, other: Polynomial<Fp>) {
326-
self.set_sub(&other);
327-
}
328-
}
329-
330307
impl<Fp: FpTrait> SubAssign<&Polynomial<Fp>> for Polynomial<Fp> {
331308
#[inline(always)]
332309
fn sub_assign(&mut self, other: &Polynomial<Fp>) {
333310
self.set_sub(other);
334311
}
335312
}
336313

337-
impl<Fp: FpTrait> Mul for Polynomial<Fp> {
338-
type Output = Polynomial<Fp>;
339-
340-
#[inline(always)]
341-
fn mul(self, other: Polynomial<Fp>) -> Polynomial<Fp> {
342-
let mut r = self;
343-
r.set_mul(&other);
344-
r
345-
}
346-
}
347-
348-
impl<Fp: FpTrait> Mul for &Polynomial<Fp> {
314+
impl<Fp: FpTrait> Mul<&Polynomial<Fp>> for &Polynomial<Fp> {
349315
type Output = Polynomial<Fp>;
350316

351317
#[inline(always)]
@@ -356,13 +322,6 @@ impl<Fp: FpTrait> Mul for &Polynomial<Fp> {
356322
}
357323
}
358324

359-
impl<Fp: FpTrait> MulAssign for Polynomial<Fp> {
360-
#[inline(always)]
361-
fn mul_assign(&mut self, other: Polynomial<Fp>) {
362-
self.set_mul(&other);
363-
}
364-
}
365-
366325
impl<Fp: FpTrait> MulAssign<&Polynomial<Fp>> for Polynomial<Fp> {
367326
#[inline(always)]
368327
fn mul_assign(&mut self, other: &Polynomial<Fp>) {

tests/test_poly.rs

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,44 @@ mod test_polynomial_arithmetic {
1010
fn test_addition() {
1111
let mut rng = DRNG::from_seed("polynomial_addition".as_bytes());
1212

13-
let f = PR::rand(&mut rng, 5);
13+
let f = PR::rand(&mut rng, 4);
1414
let g = PR::rand(&mut rng, 5);
15-
let h = PR::rand(&mut rng, 5);
15+
let h = PR::rand(&mut rng, 6);
1616

1717
let t1 = &(&f + &g) + &h;
1818
let t2 = &f + &(&g + &h);
1919

2020
assert!(t1.equals(&t2) == u32::MAX);
2121

2222
let mut t1 = f.clone();
23-
t1 += g;
24-
t1 += h;
23+
t1 += &g;
24+
t1 += &h;
2525
assert!(t1.equals(&t2) == u32::MAX);
2626

2727
let zero = PR::new_from_ele(&Fp::ZERO);
2828
let t1 = &f + &zero;
2929
assert!(t1.equals(&f) == u32::MAX);
30+
31+
let f_neg = -&f;
32+
assert!((&f + &f_neg).is_zero() == u32::MAX);
3033
}
3134

3235
#[test]
3336
fn test_subtraction() {
3437
let mut rng = DRNG::from_seed("polynomial_subtraction".as_bytes());
3538

36-
let f = PR::rand(&mut rng, 5);
39+
let f = PR::rand(&mut rng, 4);
3740
let g = PR::rand(&mut rng, 5);
38-
let h = PR::rand(&mut rng, 5);
41+
let h = PR::rand(&mut rng, 6);
3942

4043
let t1 = &(&f - &g) - &h;
4144
let t2 = &f - &(&g + &h);
4245

4346
assert!(t1.equals(&t2) == u32::MAX);
4447

4548
let mut t1 = f.clone();
46-
t1 -= g;
47-
t1 -= h;
49+
t1 -= &g;
50+
t1 -= &h;
4851
assert!(t1.equals(&t2) == u32::MAX);
4952

5053
let zero = &f - &f;
@@ -55,26 +58,26 @@ mod test_polynomial_arithmetic {
5558
fn test_multiplication() {
5659
let mut rng = DRNG::from_seed("polynomial_multiplication".as_bytes());
5760

58-
let f = PR::rand(&mut rng, 5);
61+
let f = PR::rand(&mut rng, 4);
5962
let g = PR::rand(&mut rng, 5);
60-
let h = PR::rand(&mut rng, 5);
63+
let h = PR::rand(&mut rng, 6);
6164

6265
let t1 = &(&f * &g) * &h;
6366
let t2 = &f * &(&g * &h);
6467

6568
assert!(t1.equals(&t2) == u32::MAX);
6669

6770
let mut t1 = f.clone();
68-
t1 *= g;
69-
t1 *= h;
71+
t1 *= &g;
72+
t1 *= &h;
7073
assert!(t1.equals(&t2) == u32::MAX);
7174

7275
let one = PR::new_from_ele(&Fp::ONE);
73-
t1 *= one;
76+
t1 *= &one;
7477
assert!(t1.equals(&t2) == u32::MAX);
7578

7679
let zero = PR::new_from_ele(&Fp::ZERO);
77-
t1 *= zero;
80+
t1 *= &zero;
7881
assert!(t1.is_zero() == u32::MAX);
7982
}
8083

@@ -159,4 +162,31 @@ mod test_polynomial_arithmetic {
159162

160163
assert!(ea.equals(&test_ea) == u32::MAX);
161164
}
165+
166+
#[test]
167+
fn test_degree() {
168+
let f = PR::new_from_slice(&[Fp::ZERO]);
169+
assert!(f.degree() == None);
170+
171+
let f = PR::new_from_slice(&[Fp::ONE]);
172+
assert!(f.degree().unwrap() == 0);
173+
174+
let f = PR::new_from_slice(&[Fp::ZERO, Fp::ONE]);
175+
assert!(f.degree().unwrap() == 1);
176+
177+
let f = PR::new_from_slice(&[Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ONE, Fp::ZERO]);
178+
assert!(f.degree().unwrap() == 4);
179+
180+
let f = PR::new_from_slice(&[Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ZERO]);
181+
assert!(f.degree() == None);
182+
183+
let f = PR::new_from_slice(&[Fp::ONE, Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ZERO]);
184+
assert!(f.degree().unwrap() == 0);
185+
186+
let f = PR::new_from_slice(&[Fp::ZERO, Fp::ONE, Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ZERO]);
187+
assert!(f.degree().unwrap() == 1);
188+
189+
let f = PR::new_from_slice(&[Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ZERO, Fp::ONE, Fp::ZERO]);
190+
assert!(f.degree().unwrap() == 4);
191+
}
162192
}

0 commit comments

Comments
 (0)