use crate::{
BitMatrix,
BitStore,
BitVector,
Unsigned,
};
use std::{
fmt::{
self,
Write,
},
ops::{
Add,
AddAssign,
Index,
Mul,
MulAssign,
Sub,
SubAssign,
},
};
#[doc = include_str!("../docs/polynomial.md")]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct BitPolynomial<Word: Unsigned = usize> {
coeffs: BitVector<Word>,
}
impl<Word: Unsigned> BitPolynomial<Word> {
#[must_use]
#[inline]
pub fn new() -> Self { Self { coeffs: BitVector::new() } }
#[must_use]
#[inline]
pub fn zero() -> Self { Self { coeffs: BitVector::new() } }
#[must_use]
#[inline]
pub fn one() -> Self { Self { coeffs: BitVector::ones(1) } }
#[must_use]
#[inline]
pub fn constant(val: bool) -> Self { Self { coeffs: BitVector::constant(val, 1) } }
#[must_use]
#[inline]
pub fn zeros(n: usize) -> Self { Self { coeffs: BitVector::zeros(n + 1) } }
#[must_use]
#[inline]
pub fn ones(n: usize) -> Self { Self { coeffs: BitVector::ones(n + 1) } }
#[must_use]
#[inline]
pub fn x_to_the(n: usize) -> Self {
let mut coeffs = BitVector::zeros(n + 1);
coeffs.set(n, true);
Self { coeffs }
}
#[must_use]
#[inline]
pub fn from_coefficients(coeffs: BitVector<Word>) -> Self { Self { coeffs } }
#[must_use]
#[inline]
pub fn from_fn(n: usize, f: impl Fn(usize) -> bool) -> Self { Self { coeffs: BitVector::from_fn(n + 1, f) } }
}
impl<Word: Unsigned> BitPolynomial<Word> {
#[must_use]
pub fn random(n: usize) -> Self {
let mut coeffs = BitVector::random(n + 1);
if n > 0 {
coeffs.set(n, true);
}
Self { coeffs }
}
#[must_use]
pub fn random_seeded(n: usize, seed: u64) -> Self {
let mut coeffs = BitVector::random_seeded(n + 1, seed);
if n > 0 {
coeffs.set(n, true);
}
Self { coeffs }
}
}
impl<Word: Unsigned> BitPolynomial<Word> {
#[must_use]
#[inline]
pub fn degree(&self) -> usize { self.coeffs.last_set().unwrap_or(0) }
#[must_use]
#[inline]
pub fn is_zero(&self) -> bool { self.coeffs.none() }
#[must_use]
#[inline]
pub fn is_non_zero(&self) -> bool { self.coeffs.any() }
#[must_use]
#[inline]
pub fn is_one(&self) -> bool { self.degree() == 0 && self.coeffs.len() >= 1 && self.coeffs[0] }
#[must_use]
#[inline]
pub fn is_constant(&self) -> bool { self.degree() == 0 }
#[must_use]
#[inline]
pub fn is_monic(&self) -> bool { self.coeffs.trailing_zeros() == 0 }
#[must_use]
#[inline]
pub fn len(&self) -> usize { self.coeffs.len() }
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool { self.coeffs.is_empty() }
}
impl<Word: Unsigned> BitPolynomial<Word> {
#[must_use]
#[inline]
pub fn coefficients(&self) -> &BitVector<Word> { &self.coeffs }
#[must_use]
#[inline]
pub fn coefficients_mut(&mut self) -> &mut BitVector<Word> { &mut self.coeffs }
#[must_use]
#[inline]
pub fn coeff(&self, i: usize) -> bool { self.coeffs[i] }
#[inline]
pub fn set_coeff(&mut self, i: usize, value: bool) -> &mut Self {
self.coeffs.set(i, value);
self
}
#[inline]
pub fn clear(&mut self) -> &mut Self {
self.coeffs.clear();
self
}
#[inline]
pub fn resize(&mut self, n: usize) -> &mut Self {
self.coeffs.resize(n);
self
}
#[inline]
pub fn shrink_to_fit(&mut self) -> &mut Self {
self.make_monic();
self.coeffs.shrink_to_fit();
self
}
#[inline]
pub fn make_monic(&mut self) {
if self.is_non_zero() {
self.coeffs.resize(self.degree() + 1);
}
}
}
impl<Word: Unsigned> BitPolynomial<Word> {
pub fn sub_polynomial_into(&self, d: usize, dst: &mut BitPolynomial<Word>) {
dst.resize((d + 1).min(self.coeffs.len()));
if d == 0 {
*dst = BitPolynomial::<Word>::constant(self.coeffs.get(0));
}
else if d + 1 >= self.coeffs.len() {
*dst = self.clone();
}
else {
dst.coeffs.copy_store(&self.coeffs.slice(0..d + 1));
}
}
pub fn sub_polynomial(&self, d: usize) -> BitPolynomial<Word> {
let mut dst = BitPolynomial::<Word>::zero();
self.sub_polynomial_into(d, &mut dst);
dst
}
pub fn split_into(&self, d: usize, lo: &mut BitPolynomial<Word>, hi: &mut BitPolynomial<Word>) {
let len = self.coeffs.len();
let split = (d + 1).min(len);
let hi_len = len.saturating_sub(d + 1);
lo.resize(split);
hi.resize(hi_len);
if split > 0 {
lo.coeffs.copy_store(&self.coeffs.slice(0..split));
}
if hi_len > 0 {
hi.coeffs.copy_store(&self.coeffs.slice(d + 1..len));
}
}
pub fn split(&self, d: usize) -> (BitPolynomial<Word>, BitPolynomial<Word>) {
let mut lo = BitPolynomial::<Word>::zero();
let mut hi = BitPolynomial::<Word>::zero();
self.split_into(d, &mut lo, &mut hi);
(lo, hi)
}
}
impl<Word: Unsigned> BitPolynomial<Word> {
pub fn plus_eq(&mut self, rhs: &BitPolynomial<Word>) {
if rhs.is_zero() {
return;
}
if self.is_zero() {
self.coeffs = rhs.coeffs.clone();
return;
}
if self.len() < rhs.degree() + 1 {
self.coeffs.resize(rhs.degree() + 1);
}
let monic_words = if rhs.is_monic() { rhs.degree() / Word::UBITS + 1 } else { 0 };
for i in 0..monic_words {
let rhs_word = rhs.coeffs.word(i);
let self_word = self.coeffs.word(i);
self.coeffs.set_word(i, rhs_word ^ self_word);
}
}
#[must_use]
pub fn plus(&self, rhs: &BitPolynomial<Word>) -> BitPolynomial<Word> {
let mut result = self.clone();
result.plus_eq(rhs);
result
}
pub fn minus_eq(&mut self, rhs: &BitPolynomial<Word>) { self.plus_eq(rhs) }
#[must_use]
pub fn minus(&self, rhs: &BitPolynomial<Word>) -> BitPolynomial<Word> {
let mut result = self.clone();
result.plus_eq(rhs);
result
}
pub fn square_into(&self, dst: &mut BitPolynomial<Word>) {
if self.is_constant() {
*dst = self.clone();
return;
}
self.coeffs.riffled_into(&mut dst.coeffs);
}
#[inline]
#[must_use]
pub fn squared(&self) -> Self {
let mut dst = BitPolynomial::new();
self.square_into(&mut dst);
dst
}
pub fn times_x_to_the(&mut self, n: usize) -> &mut Self {
let new_degree = self.degree() + n;
let new_len = new_degree + 1;
if self.coeffs.len() < new_len {
self.coeffs.resize(new_len);
}
self.coeffs >>= n;
self
}
#[must_use]
pub fn convolved_with(&self, rhs: &BitPolynomial<Word>) -> Self {
if self.is_zero() || rhs.is_zero() {
return BitPolynomial::zero();
}
if self.is_one() {
return rhs.clone();
}
if rhs.is_one() {
return self.clone();
}
Self { coeffs: self.coeffs.convolved_with(&rhs.coeffs) }
}
}
impl<Word: Unsigned> BitPolynomial<Word> {
#[must_use]
pub fn eval_bool(&self, x: bool) -> bool {
if self.is_zero() {
return false;
}
if !x {
return self.coeff(0);
}
let monic_words = if self.is_monic() { self.degree() / Word::UBITS + 1 } else { 0 };
let mut sum = Word::ZERO;
for i in 0..monic_words {
sum ^= self.coeffs.word(i);
}
sum.count_ones() % 2 == 1
}
#[must_use]
pub fn eval_matrix(&self, mat: &BitMatrix<Word>) -> BitMatrix<Word> {
assert!(mat.is_square(), "BitMatrix must be square not {}x{}", mat.rows(), mat.cols());
if self.is_zero() {
return BitMatrix::zeros(mat.rows(), mat.cols());
}
let mut result = BitMatrix::identity(mat.rows());
let mut d = self.degree();
while d > 0 {
result = result.dot_matrix(mat);
if self.coeffs[d - 1] {
result.add_identity();
}
d -= 1;
}
result
}
}
impl<Word: Unsigned> BitPolynomial<Word> {
#[must_use]
pub fn to_full_string(&self) -> String { self.to_full_string_with_var("x") }
#[must_use]
pub fn to_string_with_var(&self, var: &str) -> String {
if self.is_zero() {
return "0".to_string();
}
let mut result = String::new();
let mut first_term = true;
for i in 0..=self.degree() {
if self.coeffs[i] {
if i == 0 {
result.push('1');
}
else {
if !first_term {
result.push_str(" + ");
}
if i == 1 {
result.push_str(var);
}
else {
write!(result, "{var}^{i}").unwrap();
}
}
first_term = false;
}
}
result
}
#[must_use]
pub fn to_full_string_with_var(&self, var: &str) -> String {
if self.coeffs.is_empty() {
return "0".to_string();
}
let mut result = String::new();
if self.coeffs[0] {
result.push('1');
}
else {
result.push('0');
}
for i in 1..self.len() {
result.push_str(" + ");
let c = if self.coeffs[i] { "" } else { "0" };
if i == 1 {
write!(result, "{c}{var}").unwrap();
}
else {
write!(result, "{c}{var}^{i}").unwrap();
}
}
result
}
}
impl<Word: Unsigned> BitPolynomial<Word> {
#[must_use]
pub fn reduce_x_to_the(&self, n: usize) -> Self { self.reduce_x_to_power(n, false) }
#[must_use]
pub fn reduce_x_to_the_2_to_the(&self, n: usize) -> Self { self.reduce_x_to_power(n, true) }
#[allow(clippy::many_single_char_names)]
#[must_use]
pub fn reduce_x_to_power(&self, n: usize, n_is_exponent: bool) -> Self {
assert!(!self.is_zero(), " ... mod P(x) is undefined if P(x) := 0");
if self.is_one() {
return Self::zero();
}
if n == 0 && !n_is_exponent {
return Self::one();
}
let d = self.degree();
if d == 1 {
return Self::constant(self.coeff(0));
}
let p: BitVector<Word> = self.coeffs.slice(0..d).into();
let times_x_step = |q: &mut BitVector<Word>| {
let add_p = q[d - 1];
*q >>= 1;
if add_p {
*q ^= &p;
}
};
let mut power_mod = Vec::<BitVector<Word>>::with_capacity(d);
power_mod.push(p.clone());
for i in 1..d {
let mut q = power_mod[i - 1].clone();
times_x_step(&mut q);
power_mod.push(q);
}
let mut s = BitVector::zeros(2 * d);
let mut h = BitVector::zeros(d);
let mut square_step = |q: &mut BitVector<Word>| {
q.riffled_into(&mut s);
s.split_at_into(d, q, &mut h);
if let Some(h_first) = h.first_set() {
let h_last = h.last_set().unwrap();
for i in (h_first..=h_last).step_by(2) {
if h[i] {
*q ^= &power_mod[i];
}
}
}
};
if n_is_exponent {
let mut r = BitVector::zeros(d);
r.set(1, true);
for _ in 0..n {
square_step(&mut r);
}
return Self::from_coefficients(r);
}
if n < d {
return BitPolynomial::x_to_the(n);
}
if n == d {
return Self::from_coefficients(p);
}
let mut n_bit = n.prev_power_of_two();
let mut r = BitVector::zeros(d);
r.set(1, true);
n_bit >>= 1;
while n_bit > 0 {
square_step(&mut r);
if (n & n_bit) != 0 {
times_x_step(&mut r);
}
n_bit >>= 1;
}
Self::from_coefficients(r)
}
}
impl<Word: Unsigned> Default for BitPolynomial<Word> {
#[inline]
fn default() -> Self { Self::new() }
}
impl<Word: Unsigned> Index<usize> for BitPolynomial<Word> {
type Output = bool;
#[inline]
fn index(&self, i: usize) -> &Self::Output { if self.coeffs[i] { &true } else { &false } }
}
impl<Word: Unsigned> fmt::Display for BitPolynomial<Word> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{}", self.to_full_string_with_var("x"))
}
else {
write!(f, "{}", self.to_string_with_var("x"))
}
}
}
impl<Word: Unsigned> fmt::Debug for BitPolynomial<Word> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_full_string_with_var("x")) }
}
impl<Word: Unsigned> AddAssign<&BitPolynomial<Word>> for BitPolynomial<Word> {
#[inline]
fn add_assign(&mut self, rhs: &BitPolynomial<Word>) { self.plus_eq(rhs); }
}
impl<Word: Unsigned> AddAssign<BitPolynomial<Word>> for BitPolynomial<Word> {
#[inline]
fn add_assign(&mut self, rhs: BitPolynomial<Word>) { self.plus_eq(&rhs); }
}
impl<Word: Unsigned> SubAssign<&BitPolynomial<Word>> for BitPolynomial<Word> {
#[inline]
fn sub_assign(&mut self, rhs: &BitPolynomial<Word>) { self.plus_eq(rhs); }
}
impl<Word: Unsigned> SubAssign<BitPolynomial<Word>> for BitPolynomial<Word> {
#[inline]
fn sub_assign(&mut self, rhs: BitPolynomial<Word>) { self.plus_eq(&rhs); }
}
impl<Word: Unsigned> MulAssign<&BitPolynomial<Word>> for BitPolynomial<Word> {
#[inline]
fn mul_assign(&mut self, rhs: &BitPolynomial<Word>) {
let result = self.convolved_with(rhs);
*self = result;
}
}
impl<Word: Unsigned> MulAssign<BitPolynomial<Word>> for BitPolynomial<Word> {
#[inline]
fn mul_assign(&mut self, rhs: BitPolynomial<Word>) {
let result = self.convolved_with(&rhs);
*self = result;
}
}
impl<Word: Unsigned> Add<&BitPolynomial<Word>> for &BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn add(self, rhs: &BitPolynomial<Word>) -> Self::Output { self.plus(rhs) }
}
impl<Word: Unsigned> Add<&BitPolynomial<Word>> for BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn add(self, rhs: &BitPolynomial<Word>) -> Self::Output { self.plus(rhs) }
}
impl<Word: Unsigned> Add<BitPolynomial<Word>> for &BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn add(self, rhs: BitPolynomial<Word>) -> Self::Output { self.plus(&rhs) }
}
impl<Word: Unsigned> Add<BitPolynomial<Word>> for BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn add(self, rhs: BitPolynomial<Word>) -> Self::Output { self.plus(&rhs) }
}
impl<Word: Unsigned> Sub<&BitPolynomial<Word>> for &BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn sub(self, rhs: &BitPolynomial<Word>) -> Self::Output { self.plus(rhs) }
}
impl<Word: Unsigned> Sub<&BitPolynomial<Word>> for BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn sub(self, rhs: &BitPolynomial<Word>) -> Self::Output { self.plus(rhs) }
}
impl<Word: Unsigned> Sub<BitPolynomial<Word>> for &BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn sub(self, rhs: BitPolynomial<Word>) -> Self::Output { self.plus(&rhs) }
}
impl<Word: Unsigned> Sub<BitPolynomial<Word>> for BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn sub(self, rhs: BitPolynomial<Word>) -> Self::Output { self.plus(&rhs) }
}
impl<Word: Unsigned> Mul<&BitPolynomial<Word>> for &BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn mul(self, rhs: &BitPolynomial<Word>) -> Self::Output { self.convolved_with(rhs) }
}
impl<Word: Unsigned> Mul<&BitPolynomial<Word>> for BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn mul(self, rhs: &BitPolynomial<Word>) -> Self::Output { self.convolved_with(rhs) }
}
impl<Word: Unsigned> Mul<BitPolynomial<Word>> for &BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn mul(self, rhs: BitPolynomial<Word>) -> Self::Output { self.convolved_with(&rhs) }
}
impl<Word: Unsigned> Mul<BitPolynomial<Word>> for BitPolynomial<Word> {
type Output = BitPolynomial<Word>;
#[inline]
fn mul(self, rhs: BitPolynomial<Word>) -> Self::Output { self.convolved_with(&rhs) }
}
#[cfg(feature = "unstable")]
impl<Word: Unsigned> Fn<(bool,)> for BitPolynomial<Word> {
extern "rust-call" fn call(&self, args: (bool,)) -> Self::Output { self.eval_bool(args.0) }
}
#[cfg(feature = "unstable")]
impl<Word: Unsigned> FnMut<(bool,)> for BitPolynomial<Word> {
extern "rust-call" fn call_mut(&mut self, args: (bool,)) -> Self::Output { self.eval_bool(args.0) }
}
#[cfg(feature = "unstable")]
impl<Word: Unsigned> FnOnce<(bool,)> for BitPolynomial<Word> {
type Output = bool;
extern "rust-call" fn call_once(self, args: (bool,)) -> Self::Output { self.eval_bool(args.0) }
}
#[cfg(feature = "unstable")]
impl<Word: Unsigned> Fn<(&BitMatrix<Word>,)> for BitPolynomial<Word> {
extern "rust-call" fn call(&self, args: (&BitMatrix<Word>,)) -> Self::Output { self.eval_matrix(args.0) }
}
#[cfg(feature = "unstable")]
impl<Word: Unsigned> FnMut<(&BitMatrix<Word>,)> for BitPolynomial<Word> {
extern "rust-call" fn call_mut(&mut self, args: (&BitMatrix<Word>,)) -> Self::Output { self.eval_matrix(args.0) }
}
#[cfg(feature = "unstable")]
impl<Word: Unsigned> FnOnce<(&BitMatrix<Word>,)> for BitPolynomial<Word> {
type Output = BitMatrix<Word>;
extern "rust-call" fn call_once(self, args: (&BitMatrix<Word>,)) -> Self::Output { self.eval_matrix(args.0) }
}