extern crate alga;
#[macro_use]
extern crate alga_derive;
#[macro_use]
extern crate approx;
extern crate quickcheck;

use std::fmt::{Display, Error, Formatter};

use alga::general::wrapper::Wrapper as W;
use alga::general::*;

use approx::{AbsDiffEq, RelativeEq, UlpsEq};

use quickcheck::{Arbitrary, Gen};

#[derive(Alga, PartialEq, Clone, Debug)]
#[alga_traits(GroupAbelian(Additive), Where = "Scalar: AbstractField")]
#[alga_quickcheck(check(Rational))]
struct Vec2<Scalar> {
    x: Scalar,
    y: Scalar,
}

impl<Scalar: AbstractField + Arbitrary> Arbitrary for Vec2<Scalar> {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        Vec2::new(Scalar::arbitrary(g), Scalar::arbitrary(g))
    }

    fn shrink(&self) -> Box<Iterator<Item = Self>> {
        Box::new(
            self.x
                .shrink()
                .zip(self.y.shrink())
                .map(|(x, y)| Vec2::new(x, y)),
        )
    }
}

impl<Scalar: AbstractField> Vec2<Scalar> {
    fn new(x: Scalar, y: Scalar) -> Vec2<Scalar> {
        Vec2 { x: x, y: y }
    }
}

impl<Scalar: AbstractField + Display> Display for Vec2<Scalar> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        fmt.write_fmt(format_args!("({}, {})", self.x, self.y))
    }
}

impl<Scalar: AbstractField + AbsDiffEq> AbsDiffEq for Vec2<Scalar>
where
    Scalar::Epsilon: Clone,
{
    type Epsilon = Scalar::Epsilon;

    fn default_epsilon() -> Self::Epsilon {
        Scalar::default_epsilon()
    }

    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
        self.x.abs_diff_eq(&other.x, epsilon.clone()) && self.y.abs_diff_eq(&other.y, epsilon)
    }
}

impl<Scalar: AbstractField + RelativeEq> RelativeEq for Vec2<Scalar>
where
    Scalar::Epsilon: Clone,
{
    fn default_max_relative() -> Self::Epsilon {
        Scalar::default_max_relative()
    }

    fn relative_eq(
        &self,
        other: &Self,
        epsilon: Self::Epsilon,
        max_relative: Self::Epsilon,
    ) -> bool {
        self.x
            .relative_eq(&other.x, epsilon.clone(), max_relative.clone())
            && self.y.relative_eq(&other.y, epsilon, max_relative)
    }
}

impl<Scalar: AbstractField + UlpsEq> UlpsEq for Vec2<Scalar>
where
    Scalar::Epsilon: Clone,
{
    fn default_max_ulps() -> u32 {
        Scalar::default_max_ulps()
    }

    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
        self.x.ulps_eq(&other.x, epsilon.clone(), max_ulps)
            && self.y.ulps_eq(&other.y, epsilon, max_ulps)
    }
}

impl<Scalar: AbstractField> AbstractMagma<Additive> for Vec2<Scalar> {
    fn operate(&self, lhs: &Self) -> Self {
        Vec2::new(self.x.op(Additive, &lhs.x), self.y.op(Additive, &lhs.y))
    }
}

impl<Scalar: AbstractField> TwoSidedInverse<Additive> for Vec2<Scalar> {
    fn two_sided_inverse(&self) -> Self {
        Vec2::new(
            TwoSidedInverse::<Additive>::two_sided_inverse(&self.x),
            TwoSidedInverse::<Additive>::two_sided_inverse(&self.y),
        )
    }
}

impl<Scalar: AbstractField> Identity<Additive> for Vec2<Scalar> {
    fn identity() -> Self {
        Vec2 {
            x: Identity::<Additive>::identity(),
            y: Identity::<Additive>::identity(),
        }
    }
}

impl<Scalar: AbstractField> AbstractModule for Vec2<Scalar> {
    type AbstractRing = Scalar;
    fn multiply_by(&self, r: Self::AbstractRing) -> Self {
        self.op(Multiplicative, &Vec2::new(r.clone(), r))
    }
}

impl<Scalar: AbstractField> AbstractMagma<Multiplicative> for Vec2<Scalar> {
    fn operate(&self, lhs: &Self) -> Self {
        Vec2::new(
            self.x.op(Multiplicative, &lhs.x),
            self.y.op(Multiplicative, &lhs.y),
        )
    }
}

impl<Scalar: AbstractField> Identity<Multiplicative> for Vec2<Scalar> {
    fn identity() -> Self {
        Vec2 {
            x: Identity::<Multiplicative>::identity(),
            y: Identity::<Multiplicative>::identity(),
        }
    }
}

fn gcd<T: AbstractRingCommutative + PartialOrd>(a: T, b: T) -> T {
    let (mut a, mut b) = (W::<_, _, Multiplicative>::new(a), W::new(b));
    let zero = W::new(Identity::<Additive>::identity());
    if a < zero {
        a = -a;
    }
    if b < zero {
        b = -b;
    }
    if a == zero {
        if b == zero {
            return zero.val;
        }
        return b.val;
    }
    if b == zero {
        return a.val;
    }
    while a != b {
        if a > b {
            a = a - b.clone();
        } else {
            b = b - a.clone();
        }
    }
    a.val
}

#[test]
fn gcd_works() {
    assert_eq!(2, gcd(8, 6));
    assert_eq!(2, gcd(6, 8));
    assert_eq!(3, gcd(15, 6));
    assert_eq!(3, gcd(6, 15));
    assert_eq!(1, gcd(17, 12345));
    assert_eq!(1, gcd(42312, 17));
    assert_eq!(5, gcd(15, -35));
    assert_eq!(5, gcd(-15, 35));
    assert_eq!(5, gcd(-15, -35));
}

#[derive(Alga, Clone, Debug)]
#[alga_traits(Field(Additive, Multiplicative))]
#[alga_quickcheck]
struct Rational {
    a: i64,
    b: i64,
}

impl Arbitrary for Rational {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let mut div = 0;
        while div == 0 {
            div = i64::arbitrary(g);
        }
        Rational::new(i64::arbitrary(g), div)
    }

    fn shrink(&self) -> Box<Iterator<Item = Self>> {
        RationalShrinker::new(self.clone())
    }
}

struct RationalShrinker {
    x: Rational,
    i: Rational,
}

impl RationalShrinker {
    pub fn new(x: Rational) -> Box<Iterator<Item = Rational>> {
        if x.a == 0 {
            quickcheck::empty_shrinker()
        } else {
            let shrinker = RationalShrinker {
                x: x.clone(),
                i: Rational::new(x.a, x.b * 2),
            };
            let items = vec![Rational::new(0, 1)];
            Box::new(items.into_iter().chain(shrinker))
        }
    }
}

impl Iterator for RationalShrinker {
    type Item = Rational;
    fn next(&mut self) -> Option<Self::Item> {
        let next = Rational::new(
            (self.x.a * self.i.b) - (self.i.a * self.x.b),
            self.x.b * self.i.b,
        );
        if next.a * self.x.b < self.x.a * next.b {
            let result = Some(next);
            self.i = Rational::new(self.i.a, self.i.b * 2);
            result
        } else {
            None
        }
    }
}

impl Rational {
    fn new(mut a: i64, mut b: i64) -> Self {
        assert!(b != 0);
        if b < 0 {
            b = -b;
            a = -a;
        }
        if a == 0 {
            Rational::whole(0)
        } else {
            let gcd = gcd(a, b);
            Rational {
                a: a / gcd,
                b: b / gcd,
            }
        }
    }

    fn whole(n: i64) -> Self {
        Rational { a: n, b: 1 }
    }
}

impl Display for Rational {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        if self.b == 1 {
            fmt.write_fmt(format_args!("{}", self.a))
        } else {
            fmt.write_fmt(format_args!("{}⁄{}", self.a, self.b))
        }
    }
}

impl PartialEq for Rational {
    fn eq(&self, lhs: &Self) -> bool {
        self.a * lhs.b == lhs.a * self.b
    }
}

impl AbsDiffEq for Rational {
    type Epsilon = f64;

    fn default_epsilon() -> Self::Epsilon {
        ::std::f64::EPSILON
    }

    fn abs_diff_eq(&self, other: &Self, epsilon: f64) -> bool {
        let us = self.a as f64 / self.b as f64;
        let them = other.a as f64 / other.b as f64;
        us.abs_diff_eq(&them, epsilon)
    }
}

impl RelativeEq for Rational {
    fn default_max_relative() -> Self::Epsilon {
        ::std::f64::EPSILON
    }

    fn relative_eq(
        &self,
        other: &Self,
        epsilon: Self::Epsilon,
        max_relative: Self::Epsilon,
    ) -> bool {
        let us = self.a as f64 / self.b as f64;
        let them = other.a as f64 / other.b as f64;
        us.relative_eq(&them, epsilon, max_relative)
    }
}

impl UlpsEq for Rational {
    fn default_max_ulps() -> u32 {
        4
    }

    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
        let us = self.a as f64 / self.b as f64;
        let them = other.a as f64 / other.b as f64;
        us.ulps_eq(&them, epsilon, max_ulps)
    }
}

impl AbstractMagma<Additive> for Rational {
    fn operate(&self, lhs: &Self) -> Self {
        let a = self.a * lhs.b + lhs.a * self.b;
        let b = self.b * lhs.b;
        let gcd = gcd(a, b);
        Rational::new(a / gcd, b / gcd)
    }
}

impl TwoSidedInverse<Additive> for Rational {
    fn two_sided_inverse(&self) -> Self {
        Rational::new(-self.a, self.b)
    }
}

impl Identity<Additive> for Rational {
    fn identity() -> Self {
        Rational::whole(0)
    }
}

impl AbstractMagma<Multiplicative> for Rational {
    fn operate(&self, lhs: &Self) -> Self {
        let a = self.a * lhs.a;
        let b = self.b * lhs.b;
        let gcd = gcd(a, b);
        Rational::new(a / gcd, b / gcd)
    }
}

impl TwoSidedInverse<Multiplicative> for Rational {
    fn two_sided_inverse(&self) -> Self {
        if self.a == 0 {
            self.clone()
        } else {
            Rational::new(self.b, self.a)
        }
    }
}

impl Identity<Multiplicative> for Rational {
    fn identity() -> Self {
        Rational::whole(1)
    }
}

fn main() {
    let vec = || {
        W::<_, Additive, Multiplicative>::new(Vec2::new(Rational::new(1, 2), Rational::whole(3)))
    };
    let vec2 = || W::new(Vec2::new(Rational::whole(5), Rational::new(11, 7)));
    let vec3 = || W::new(Vec2::new(Rational::new(7, 11), Rational::whole(17)));

    let vec4 = (vec() * vec2()) + (vec() * vec3());
    let vec5 = vec() * (vec2() + vec3());
    if relative_eq!(vec4, vec5) {
        println!("{} == {}", vec4, vec5);
    } else {
        println!("{} != {}", vec4, vec5);
    }
}
