use crate::scalar::{ExactFieldScalar, Poly, Scalar};
use std::fmt;
#[derive(Clone)]
pub struct RationalFunction<S: ExactFieldScalar> {
num: Poly<S>,
den: Poly<S>,
}
impl<S: ExactFieldScalar> RationalFunction<S> {
fn from_polys(num: Poly<S>, den: Poly<S>) -> Self {
assert!(!den.is_zero(), "RationalFunction: zero denominator");
if num.is_zero() {
return RationalFunction {
num: Poly::zero(),
den: Poly::one(),
};
}
let gcd = num.gcd(&den);
let (num, den) = if gcd == Poly::one() {
(num, den)
} else {
let (nq, nr) = num.divrem(&gcd);
let (dq, dr) = den.divrem(&gcd);
debug_assert!(nr.is_zero() && dr.is_zero(), "gcd must divide both");
(nq, dq)
};
let lead_inv = den
.leading()
.unwrap()
.inv()
.expect("a field's nonzero leading coefficient inverts");
RationalFunction {
num: num.scale(&lead_inv),
den: den.scale(&lead_inv),
}
}
pub fn new(num: Vec<S>, den: Vec<S>) -> Self {
RationalFunction::from_polys(Poly::new(num), Poly::new(den))
}
pub fn from_poly(p: Poly<S>) -> Self {
RationalFunction::from_polys(p, Poly::one())
}
pub fn from_base(s: S) -> Self {
RationalFunction::from_poly(Poly::constant(s))
}
pub fn t() -> Self {
RationalFunction::from_poly(Poly::t())
}
pub fn num(&self) -> &Poly<S> {
&self.num
}
pub fn den(&self) -> &Poly<S> {
&self.den
}
}
impl<S: ExactFieldScalar> PartialEq for RationalFunction<S> {
fn eq(&self, other: &Self) -> bool {
self.num.mul(&other.den) == other.num.mul(&self.den)
}
}
impl<S: ExactFieldScalar> fmt::Display for RationalFunction<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.den == Poly::one() {
write!(f, "{}", self.num)
} else {
write!(f, "({})/({})", self.num, self.den)
}
}
}
impl<S: ExactFieldScalar> fmt::Debug for RationalFunction<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<S: ExactFieldScalar> Scalar for RationalFunction<S> {
fn zero() -> Self {
RationalFunction {
num: Poly::zero(),
den: Poly::one(),
}
}
fn one() -> Self {
RationalFunction {
num: Poly::one(),
den: Poly::one(),
}
}
fn add(&self, rhs: &Self) -> Self {
let num = self.num.mul(&rhs.den).add(&rhs.num.mul(&self.den));
let den = self.den.mul(&rhs.den);
RationalFunction::from_polys(num, den)
}
fn neg(&self) -> Self {
RationalFunction {
num: self.num.neg(),
den: self.den.clone(),
}
}
fn mul(&self, rhs: &Self) -> Self {
RationalFunction::from_polys(self.num.mul(&rhs.num), self.den.mul(&rhs.den))
}
fn characteristic() -> u128 {
S::characteristic()
}
fn inv(&self) -> Option<Self> {
if self.num.is_zero() {
return None;
}
Some(RationalFunction::from_polys(
self.den.clone(),
self.num.clone(),
))
}
fn is_zero(&self) -> bool {
self.num.is_zero()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::Fp;
type F = RationalFunction<Fp<5>>;
fn rf(num: &[i128], den: &[i128]) -> F {
RationalFunction::new(
num.iter().map(|&n| Fp::<5>::from_int(n)).collect(),
den.iter().map(|&n| Fp::<5>::from_int(n)).collect(),
)
}
#[test]
fn is_an_exact_field() {
let samples = [
F::t(),
F::from_base(Fp::<5>::from_int(2)),
rf(&[1, 1], &[1]), rf(&[1], &[0, 1]), rf(&[2, 0, 1], &[1, 1]), ];
for x in &samples {
let xi = x.inv().expect("nonzero inverts in a field");
assert_eq!(x.mul(&xi), F::one(), "x·x⁻¹ ≠ 1 for {x:?}");
}
assert_eq!(F::zero().inv(), None);
assert_eq!(F::characteristic(), 5);
}
#[test]
fn cross_multiplication_equality() {
assert_eq!(rf(&[0, 1], &[0, 1]), F::one());
assert_eq!(rf(&[0, 2], &[2]), F::t());
assert_ne!(F::t(), F::one());
}
#[test]
fn fractions_are_gcd_reduced_and_denominator_monic() {
let x = rf(&[2, 3, 1], &[2, 2]);
assert_eq!(x.den(), &Poly::one());
assert_eq!(
x.num(),
&Poly::new(vec![Fp::<5>::from_int(1), Fp::<5>::from_int(3)])
);
}
#[test]
fn ring_axioms_on_a_sample() {
let es = [
F::zero(),
F::one(),
F::t(),
F::from_base(Fp::<5>::from_int(3)),
rf(&[1, 1], &[1]), rf(&[1], &[0, 1]), ];
for a in &es {
assert_eq!(a.add(&F::zero()), *a);
assert_eq!(a.add(&a.neg()), F::zero());
assert_eq!(a.mul(&F::one()), *a);
for b in &es {
assert_eq!(a.add(b), b.add(a));
assert_eq!(a.mul(b), b.mul(a));
for d in &es {
assert_eq!(a.add(b).add(d), a.add(&b.add(d)));
assert_eq!(a.mul(b).mul(d), a.mul(&b.mul(d)));
assert_eq!(a.mul(&b.add(d)), a.mul(b).add(&a.mul(d)));
}
}
}
}
#[test]
fn display_v4_uses_paren_fraction() {
let frac = rf(&[1], &[0, 1]); assert_eq!(frac.to_string(), "(1)/(t)");
assert_eq!(rf(&[1, 2], &[1]).to_string(), "2⋅t + 1");
}
#[test]
fn num_den_accessors_expose_polys_for_the_forms_layer() {
let x = rf(&[0, 1], &[1, 1]); assert_eq!(
x.num(),
&Poly::new(vec![Fp::<5>::from_int(0), Fp::<5>::from_int(1)])
);
assert_eq!(
x.den(),
&Poly::new(vec![Fp::<5>::from_int(1), Fp::<5>::from_int(1)])
);
}
}