# -*- mode: snippet -*-
# name: minimum bounding circle
# contributor: Nakamura Kenko
# key: minimumboundingcircle
# --

pub mod minimum_bounding_circle {
    pub fn make_circle(ps: &[Point<f64>]) -> (Point<f64>, f64) {
        let n = ps.len();
        assert!(n >= 2);

        let mut c = make_circle2(ps[0], ps[1]);
        for i in 2..n {
            if is_included(ps[i], c) {
                continue;
            }
            c = make_circle2(ps[0], ps[i]);
            for j in 1..i {
                if is_included(ps[j], c) {
                    continue;
                }
                c = make_circle2(ps[i], ps[j]);
                for k in 0..j {
                    if is_included(ps[k], c) {
                        continue;
                    }
                    c = make_circle3(ps[i], ps[j], ps[k]);
                }
            }
        }
        c
    }

    fn make_circle3(a: Point<f64>, b: Point<f64>, c: Point<f64>) -> (Point<f64>, f64) {
        let ea = (b - c).norm();
        let eb = (c - a).norm();
        let ec = (a - b).norm();
        let s = (b - a).det(&(c - a));

        let center = (a * ea * (eb + ec - ea) + b * eb * (ec + ea - eb) + c * ec * (ea + eb - ec))
            / (s * s * 4.0);
        let r2 = (center - a).norm();
        (center, r2)
    }

    fn make_circle2(a: Point<f64>, b: Point<f64>) -> (Point<f64>, f64) {
        let c = (a + b) / 2.0;
        let r2 = (a - c).norm();
        (c, r2)
    }

    fn is_included(a: Point<f64>, circle: (Point<f64>, f64)) -> bool {
        let (center, r2) = circle;
        (a - center).norm() <= r2
    }

    pub struct Point<T> {
        pub x: T,
        pub y: T,
    }

    impl<T> Copy for Point<T> where T: Copy {}

    impl<T> Clone for Point<T>
    where
        T: Clone,
    {
        fn clone(&self) -> Point<T> {
            Point {
                x: self.x.clone(),
                y: self.y.clone(),
            }
        }
    }

    impl<T> std::ops::Sub for Point<T>
    where
        T: std::ops::Sub<Output = T>,
    {
        type Output = Point<T>;
        fn sub(self, other: Point<T>) -> Point<T> {
            Point {
                x: self.x - other.x,
                y: self.y - other.y,
            }
        }
    }

    impl<T> std::ops::Mul<T> for Point<T>
    where
        T: Copy + std::ops::Mul<Output = T>,
    {
        type Output = Point<T>;
        fn mul(self, rhs: T) -> Point<T> {
            Point {
                x: self.x * rhs,
                y: self.y * rhs,
            }
        }
    }

    impl<T> std::ops::Div<T> for Point<T>
    where
        T: Copy + std::ops::Div<Output = T>,
    {
        type Output = Point<T>;
        fn div(self, rhs: T) -> Point<T> {
            Point {
                x: self.x / rhs,
                y: self.y / rhs,
            }
        }
    }

    impl<T> std::ops::Add<Point<T>> for Point<T>
    where
        T: std::ops::Add<Output = T>,
    {
        type Output = Point<T>;

        fn add(self, rhs: Point<T>) -> Point<T> {
            Point {
                x: self.x + rhs.x,
                y: self.y + rhs.y,
            }
        }
    }

    impl<T> Point<T>
    where
        T: Copy + std::ops::Mul<Output = T> + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
    {
        pub fn det(&self, other: &Point<T>) -> T {
            self.x * other.y - self.y * other.x
        }
        pub fn norm(&self) -> T {
            self.x * self.x + self.y * self.y
        }
    }
}

