Documentation
use std::ops::{Add, AddAssign};

use crate::arr::{
    core::{adaptative_result_shape, dim_expansion_for_shape, plain_index},
    shape_iterator::ShapeIterator,
    vec::core::ArrVec,
};

impl<T> ArrVec<T>
where
    T: Default + Clone + Copy + Add<Output = T>,
{
    fn add_to_left(lhs: &mut Self, rhs: &Self) {
        let out_shape = adaptative_result_shape(&lhs.shape, &rhs.shape);
        assert_eq!(lhs.shape, out_shape);
        let rhs_expanded_shape = dim_expansion_for_shape(&rhs.shape, out_shape.len());
        for index in ShapeIterator::new(&out_shape) {
            let out_i = plain_index(&out_shape, &out_shape, &index);
            let lhs_i = plain_index(&out_shape, &out_shape, &index);
            let rhs_i = plain_index(&rhs_expanded_shape, &out_shape, &index);
            lhs.buff[out_i] = rhs.buff[rhs_i] + lhs.buff[lhs_i];
        }
    }

    fn add_to_new(lhs: &Self, rhs: &Self) -> Self {
        let out_shape = adaptative_result_shape(&lhs.shape, &rhs.shape);
        let mut out = ArrVec::<T>::new(T::default(), out_shape);
        Self::add_to_out(lhs, rhs, &mut out);
        out
    }

    fn add_to_out(lhs: &Self, rhs: &Self, out: &mut Self) {
        let lhs_expanded_shape = dim_expansion_for_shape(&lhs.shape, out.shape.len());
        let rhs_expanded_shape = dim_expansion_for_shape(&rhs.shape, out.shape.len());
        for index in ShapeIterator::new(&out.shape) {
            let out_i = plain_index(&out.shape, &out.shape, &index);
            let lhs_i = plain_index(&lhs_expanded_shape, &out.shape, &index);
            let rhs_i = plain_index(&rhs_expanded_shape, &out.shape, &index);
            out.buff[out_i] = rhs.buff[rhs_i] + lhs.buff[lhs_i];
        }
    }
}

impl<T> Add for &ArrVec<T>
where
    T: Default + Clone + Copy + Add<Output = T>,
{
    type Output = ArrVec<T>;

    fn add(self, rhs: Self) -> Self::Output {
        ArrVec::<T>::add_to_new(self, rhs)
    }
}

impl<T> Add for ArrVec<T>
where
    T: Default + Clone + Copy + Add<Output = T>,
{
    type Output = ArrVec<T>;

    fn add(self, rhs: Self) -> Self::Output {
        ArrVec::<T>::add_to_new(&self, &rhs)
    }
}

impl<T> AddAssign for ArrVec<T>
where
    T: Default + Clone + Copy + Add<Output = T>,
{
    fn add_assign(&mut self, rhs: Self) {
        ArrVec::<T>::add_to_left(self, &rhs);
    }
}

impl<T> AddAssign<&ArrVec<T>> for ArrVec<T>
where
    T: Default + Clone + Copy + Add<Output = T>,
{
    fn add_assign(&mut self, rhs: &Self) {
        ArrVec::<T>::add_to_left(self, rhs);
    }
}