use std::fmt::Debug;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign};
pub trait Group: Debug + Clone + PartialEq + Sized + Mul<Self> + MulAssign<Self> + Mul<Output = Self> + Div<Self> + DivAssign<Self> + Div<Output = Self> {
fn identity() -> Self;
fn inverse(&self) -> Self;
}
pub trait Ring: Debug + Clone + PartialEq + Sized + Add<Self> + AddAssign<Self> + Neg + Sub<Self> + SubAssign<Self> + Mul<Self> + MulAssign<Self> + Mul<Output = Self> + Add<Output = Self> + Neg<Output = Self> + Sub<Output = Self> {
fn one() -> Self;
fn zero() -> Self;
fn is_zero(&self) -> bool;
fn power(&self, n: i64) -> Self;
}
pub trait PoRing: Ring + PartialOrd { }
pub trait OrderedRing: PoRing + Ord { }
pub trait Field: Ring + Div + DivAssign + Div<Output = Self> {
fn inverse(&self) -> Self;
}
pub trait PoField: Field + PartialOrd { }
pub trait OrderedField: PoField + Ord { }
pub trait InnerProductSpace<R: Ring> {
fn inner_product(&self, other: Self) -> R;
}
pub trait NormSpace {
type NormType: PartialOrd;
fn norm(&self) -> Self::NormType;
}
pub trait EuclideanDomain: Ring + Div + DivAssign + Rem + RemAssign {
type SizeType: Ord;
fn euc_size(&self) -> Self::SizeType;
fn quotient_and_remainder(&self, divisor: &Self) -> (Self, Self);
}
pub fn ext_gcd<R: EuclideanDomain>(a: R, b: R) -> (R, R, R) {
if a == R::zero() {
return (b, R::zero(), R::one())
}
let (q, r) = b.quotient_and_remainder(&a);
let (g, x1, y1) = ext_gcd(r, a);
let x = y1 - q * x1.clone();
(g, x, x1)
}
pub fn gcd<R: EuclideanDomain>(a: &R, b: &R) -> R {
if a.is_zero() {
b.clone()
} else if b.is_zero() {
a.clone()
} else if a.euc_size() < b.euc_size() {
gcd(b, a)
} else {
let (_, r) = a.quotient_and_remainder(b);
gcd(b, &r)
}
}