use std::ops::{Mul, Add, Div};
use libnum::{One, Zero, Float, FromPrimitive};
use std::fmt;
use std::iter::FromIterator;
use std::slice::{Iter, IterMut};
use std::vec::IntoIter;

use norm::{VectorNorm, VectorMetric};
use utils;

use super::Vector;

impl<T> Vector<T> {
    /// Constructor for Vector struct.
    ///
    /// Requires the vector data.
    ///
    /// # Examples
    ///
    /// ```
    /// use rulinalg::vector::Vector;
    ///
    /// let vec = Vector::new(vec![1.0,2.0,3.0,4.0]);
    /// ```
    pub fn new<U: Into<Vec<T>>>(data: U) -> Vector<T> {
        let our_data = data.into();
        let size = our_data.len();

        Vector {
            size: size,
            data: our_data,
        }
    }

    /// Constructor for Vector struct that takes a function `f`
    /// and constructs a new vector such that `V_i = f(i)`,
    /// where `i` is the index.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let v = Vector::from_fn(4, |x| x * 3);
    /// assert_eq!(v, vector![0, 3, 6, 9]);
    /// # }
    /// ```
    pub fn from_fn<F>(size: usize, mut f: F) -> Vector<T>
        where F: FnMut(usize) -> T {

        let data: Vec<T> = (0..size).into_iter().map(|x| f(x)).collect();

        Vector {
            size: size,
            data: data,
        }
    }

    /// Returns the size of the Vector.
    pub fn size(&self) -> usize {
        self.size
    }

    /// Returns a non-mutable reference to the underlying data.
    pub fn data(&self) -> &Vec<T> {
        &self.data
    }

    /// Returns a mutable slice of the underlying data.
    pub fn mut_data(&mut self) -> &mut [T] {
        &mut self.data
    }

    /// Consumes the Vector and returns the Vec of data.
    pub fn into_vec(self) -> Vec<T> {
        self.data
    }

    /// Returns an iterator over the Vector's data.
    pub fn iter(&self) -> Iter<T> {
        self.data.iter()
    }

    /// Returns an iterator over mutable references to the Vector's data.
    pub fn iter_mut(&mut self) -> IterMut<T> {
        self.mut_data().iter_mut()
    }

    /// Returns a pointer to the element at the given index, without doing
    /// bounds checking.
    pub unsafe fn get_unchecked(&self, index: usize) -> &T {
        self.data.get_unchecked(index)
    }

    /// Returns an unsafe mutable pointer to the element at the given index,
    /// without doing bounds checking.
    pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
        self.data.get_unchecked_mut(index)
    }

}

impl<T> Into<Vec<T>> for Vector<T> {
    fn into(self) -> Vec<T> {
        self.data
    }
}

impl<T> IntoIterator for Vector<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    fn into_iter(self) -> Self::IntoIter {
        self.data.into_iter()
    }
}

impl<'a, T> IntoIterator for &'a Vector<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<T> FromIterator<T> for Vector<T> {
    fn from_iter<I>(iter: I) -> Self where I: IntoIterator<Item=T> {
        let values: Vec<T> = iter.into_iter().collect();
        Vector::new(values)
    }
}

impl<T: fmt::Display> fmt::Display for Vector<T> {
    /// Displays the Vector.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "["));
        for (i, datum) in self.data.iter().enumerate() {
            match f.precision() {
                Some(places) => {
                    try!(write!(f, " {:.*}", places, datum));
                }
                None => {
                    try!(write!(f, " {}", datum));
                }
            }
            if i < self.data.len() - 1 {
                try!(write!(f, ","));
            }
        }
        write!(f, "]")
    }
}

impl<T: Clone> Clone for Vector<T> {
    /// Clones the Vector.
    fn clone(&self) -> Vector<T> {
        Vector {
            size: self.size,
            data: self.data.clone(),
        }
    }
}

impl<T: Copy> Vector<T> {
    /// Applies a function to each element in the vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    /// fn add_two(a: f64) -> f64 {
    ///     a + 2f64
    /// }
    ///
    /// let a = vector![0.; 4];
    ///
    /// let b = a.apply(&add_two);
    ///
    /// assert_eq!(b, vector![2.0; 4]);
    /// # }
    /// ```
    pub fn apply(mut self, f: &Fn(T) -> T) -> Vector<T> {
        for val in &mut self.data {
            *val = f(*val);
        }
        self
    }
}

impl<T: Copy + PartialOrd> Vector<T> {
    /// Find the argmax of the Vector.
    ///
    /// Returns the index of the largest value in the vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0,2.0,0.0,5.0];
    /// let b = a.argmax();
    /// assert_eq!(b.0, 3);
    /// assert_eq!(b.1, 5.0);
    /// # }
    /// ```
    pub fn argmax(&self) -> (usize, T) {
        utils::argmax(&self.data)
    }

    /// Find the argmin of the Vector.
    ///
    /// Returns the index of the smallest value in the vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0, 2.0, 0.0, 5.0];
    /// let b = a.argmin();
    /// assert_eq!(b.0, 2);
    /// assert_eq!(b.1, 0.0);
    /// # }
    /// ```
    pub fn argmin(&self) -> (usize, T) {
        utils::argmin(&self.data)
    }

    /// Select elements from the Vector and form a new Vector from them.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0, 2.0, 3.0, 4.0, 5.0];
    ///
    /// let a_lower = a.select(&[2, 3, 4]);
    ///
    /// // Prints [3,4,5]
    /// assert_eq!(a_lower, vector![3.0, 4.0, 5.0]);
    /// # }
    /// ```
    pub fn select(&self, idxs: &[usize]) -> Vector<T> {
        let mut new_data = Vec::with_capacity(idxs.len());

        for idx in idxs.into_iter() {
            new_data.push(self[*idx]);
        }

        Vector::new(new_data)
    }
}

impl<T: Clone + Zero> Vector<T> {
    /// Constructs Vector of all zeros.
    ///
    /// Requires the size of the vector.
    ///
    /// # Examples
    ///
    /// ```
    /// use rulinalg::vector::Vector;
    ///
    /// let vec = Vector::<f64>::zeros(10);
    /// ```
    pub fn zeros(size: usize) -> Vector<T> {
        Vector {
            size: size,
            data: vec![T::zero(); size],
        }
    }
}

impl<T: Clone + One> Vector<T> {
    /// Constructs Vector of all ones.
    ///
    /// Requires the size of the vector.
    ///
    /// # Examples
    ///
    /// ```
    /// use rulinalg::vector::Vector;
    ///
    /// let vec = Vector::<f64>::ones(10);
    /// ```
    pub fn ones(size: usize) -> Vector<T> {
        Vector {
            size: size,
            data: vec![T::one(); size],
        }
    }
}

impl<T: Copy + Zero + Mul<T, Output = T> + Add<T, Output = T>> Vector<T> {
    /// Compute dot product with specified Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0, 2.0, 3.0, 4.0];
    /// let b = vector![2.0; 4];
    ///
    /// let c = a.dot(&b);
    /// assert_eq!(c, 20.0);
    /// # }
    /// ```
    pub fn dot(&self, v: &Vector<T>) -> T {
        utils::dot(&self.data, &v.data)
    }
}

impl<T: Copy + Zero + Add<T, Output = T>> Vector<T> {
    /// The sum of the vector.
    ///
    /// Returns the sum of all elements in the vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0, 2.0, 3.0, 4.0];
    ///
    /// let c = a.sum();
    /// assert_eq!(c, 10.0);
    /// # }
    /// ```
    pub fn sum(&self) -> T {
        utils::unrolled_sum(&self.data[..])
    }
}

impl<T: Copy + Mul<T, Output = T>> Vector<T> {
    /// The elementwise product of two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0, 2.0, 3.0, 4.0];
    /// let b = vector![1.0, 2.0, 3.0, 4.0];
    ///
    /// let c = &a.elemul(&b);
    /// assert_eq!(c, &vector![1.0, 4.0, 9.0, 16.0]);
    /// # }
    /// ```
    pub fn elemul(&self, v: &Vector<T>) -> Vector<T> {
        assert_eq!(self.size, v.size);
        Vector::new(utils::ele_mul(&self.data, &v.data))
    }
}

impl<T: Copy + Div<T, Output = T>> Vector<T> {
    /// The elementwise division of two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0, 2.0, 3.0, 4.0];
    /// let b = vector![1.0, 2.0, 3.0, 4.0];
    ///
    /// let c = &a.elediv(&b);
    /// assert_eq!(c, &vector![1.0; 4]);
    /// # }
    /// ```
    pub fn elediv(&self, v: &Vector<T>) -> Vector<T> {
        assert_eq!(self.size, v.size);
        Vector::new(utils::ele_div(&self.data, &v.data))
    }
}

impl<T: Float> Vector<T> {
    /// Compute vector norm for vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    /// use rulinalg::norm::Euclidean;
    ///
    /// let a = vector![3.0, 4.0];
    /// let c = a.norm(Euclidean);
    ///
    /// assert_eq!(c, 5.0);
    /// # }
    /// ```
    pub fn norm<N: VectorNorm<T>>(&self, norm: N) -> T {
        norm.norm(self)
    }

    /// Compute metric distance between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    /// use rulinalg::norm::Euclidean;
    ///
    /// let a = vector![3.0, 4.0];
    /// let b = vector![0.0, 8.0];
    ///
    /// // Compute the square root of the sum of
    /// // elementwise squared-differences
    /// let c = a.metric(&b, Euclidean);
    /// assert_eq!(c, 5.0);
    /// # }
    /// ```
    pub fn metric<M: VectorMetric<T>>(&self, v: &Vector<T>, m: M) -> T {
        m.metric(self, v)
    }
}

impl<T: Float + FromPrimitive> Vector<T> {
    /// The mean of the vector.
    ///
    /// Returns the arithmetic mean of the vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0, 2.0, 3.0, 4.0];
    ///
    /// let c = a.mean();
    /// assert_eq!(c, 2.5);
    /// # }
    /// ```
    pub fn mean(&self) -> T {
        let sum = self.sum();
        sum / FromPrimitive::from_usize(self.size()).unwrap()
    }

    /// The variance of the vector.
    ///
    /// Returns the unbiased sample variance of the vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate rulinalg; fn main() {
    /// use rulinalg::vector::Vector;
    ///
    /// let a = vector![1.0, 2.0, 3.0, 4.0];
    ///
    /// let c = a.variance();
    /// assert_eq!(c, 5.0 / 3.0);
    /// # }
    /// ```
    pub fn variance(&self) -> T {
        let m = self.mean();
        let mut var = T::zero();

        for u in &self.data {
            var = var + (*u - m) * (*u - m);
        }

        var / FromPrimitive::from_usize(self.size() - 1).unwrap()
    }
}

#[cfg(test)]
mod tests {

    use super::super::Vector;
    use norm::Euclidean;

    #[test]
    fn test_display() {
        let v = vector![1, 2, 3, 4];
        assert_eq!(format!("{}", v), "[ 1, 2, 3, 4]");

        let v2 = vector![3.3, 4.0, 5.0, 6.0];
        assert_eq!(format!("{}", v2), "[ 3.3, 4, 5, 6]");
        assert_eq!(format!("{:.1}", v2), "[ 3.3, 4.0, 5.0, 6.0]");
    }

    #[test]
    fn test_equality() {
        let v = vector![1, 2, 3, 4];
        let v_redux = v.clone();
        assert_eq!(v, v_redux);
    }

    #[test]
    fn create_vector_new() {
        let a = vector![1.0; 12];

        assert_eq!(a.size(), 12);

        for i in 0..12 {
            assert_eq!(a[i], 1.0);
        }
    }

    #[test]
    fn create_vector_new_from_slice() {
        let data_vec: Vec<u32> = vec![1, 2, 3];
        let data_slice: &[u32] = &data_vec[..];
        let from_vec = Vector::new(data_vec.clone());
        let from_slice = Vector::new(data_slice);
        assert_eq!(from_vec, from_slice);
    }

    #[test]
    fn create_vector_from_fn() {
        let v1 = Vector::from_fn(3, |x| x + 1);
        assert_eq!(v1, vector![1, 2, 3]);

        let v2 = Vector::from_fn(3, |x| x as f64);
        assert_eq!(v2, vector![0., 1., 2.]);

        let mut z = 0;
        let v3 = Vector::from_fn(3, |x| { z += 1; x + z });
        assert_eq!(v3, vector![0 + 1, 1 + 2, 2 + 3]);

        let v4 = Vector::from_fn(3, move |x| x + 1);
        assert_eq!(v4, vector![1, 2, 3]);

        let v5 = Vector::from_fn(0, |x| x);
        assert_eq!(v5, Vector::new(vec![]));
    }

    #[test]
    fn create_vector_zeros() {
        let a = Vector::<f32>::zeros(7);

        assert_eq!(a.size(), 7);

        for i in 0..7 {
            assert_eq!(a[i], 0.0);
        }
    }

    #[test]
    fn vector_dot_product() {
        let a = vector![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
        let b = vector![3.0; 6];

        let c = a.dot(&b);

        assert_eq!(c, 63.0);
    }

    #[test]
    fn vector_euclidean_norm() {
        let a = vector![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
        let b = a.norm(Euclidean);

        assert_eq!(b, (1. + 4. + 9. + 16. + 25. + 36. as f32).sqrt());
    }

    #[test]
    fn vector_iteration() {
        let our_vec = vec![2i32, 7, 1, 8, 2, 8];
        let our_vector = Vector::new(our_vec.clone());
        let our_vector_again = our_vector.clone();

        // over Vector (consuming)
        let mut our_recovered_vec = Vec::new();
        for i in our_vector {
            our_recovered_vec.push(i);
        }
        assert_eq!(our_recovered_vec, our_vec);

        // over &Vector
        let mut our_refcovered_vec = Vec::new();
        for i in &our_vector_again {
            our_refcovered_vec.push(*i);
        }
        assert_eq!(our_refcovered_vec, our_vec);
    }

    #[test]
    fn vector_from_iter() {
        let v1: Vector<usize> = (2..5).collect();
        let exp1 = vector![2, 3, 4];
        assert_eq!(v1, exp1);

        let orig: Vec<f64> = vec![2., 3., 4.];
        let v2: Vector<f64> = orig.iter().map(|x| x + 1.).collect();
        let exp2 = vector![3., 4., 5.];
        assert_eq!(v2, exp2);
    }

    #[test]
    fn vector_get_unchecked() {
        let v1 = vector![1, 2, 3];
        unsafe {
            assert_eq!(v1.get_unchecked(1), &2);
        }

        let mut v2 = vector![1, 2, 3];

        unsafe {
            let elem = v2.get_unchecked_mut(1);
            *elem = 4;
        }
        assert_eq!(v2, vector![1, 4, 3]);
    }

    #[test]
    fn vector_mul_f32_elemwise() {
        let a = vector![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
        let b = vector![2.0, 3.0, 4.0, 5.0, 6.0, 7.0];

        let exp = vector![2.0, 6.0, 12.0, 20.0, 30.0, 42.0];

        // Allocating new memory
        let c = &a.elemul(&b);
        assert_eq!(c, &exp);

        // Allocating new memory
        let c = a.elemul(&b);
        assert_eq!(c, exp);
    }

    #[test]
    fn vector_mul_int_elemwise() {
        let a = vector![1, 2, 3, 4];
        let b = vector![2, 4, 6, 8];

        let exp = vector![2, 8, 18, 32];

        // Allocating new memory
        let c = &a.elemul(&b);
        assert_eq!(c, &exp);

        // Allocating new memory
        let c = a.elemul(&b);
        assert_eq!(c, exp);
    }

    #[test]
    fn vector_div_f32_elemwise() {
        let a = vector![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
        let b = vector![2.0, 3.0, 4.0, 5.0, 6.0, 7.0];

        let exp = vector![1. / 2., 2. / 3., 3. / 4., 4. / 5., 5. / 6., 6. / 7.];

        // Allocating new memory
        let c = &a.elediv(&b);
        assert_eq!(c, &exp);

        // Allocating new memory
        let c = a.elediv(&b);
        assert_eq!(c, exp);
    }

    #[test]
    fn vector_div_int_elemwise() {
        let a = vector![2, 4, 6, 8];
        let b = vector![2, 2, 3, 3];

        let exp = vector![1, 2, 2, 2];

        // Allocating new memory
        let c = &a.elediv(&b);
        assert_eq!(c, &exp);

        // Allocating new memory
        let c = a.elediv(&b);
        assert_eq!(c, exp);
    }
}
