Documentation
use core::ops::{Add, Div, Mul, Neg, Sub};

use num_traits::{One, Zero};

use super::inv;

/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::mul(&mut m, &mat3::new_identity(), &mat3::new_identity());
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn mul<'out, T>(out: &'out mut [T; 9], a: &[T; 9], b: &[T; 9]) -> &'out mut [T; 9]
where
    T: Add<T, Output = T>,
    for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
    let a11 = &a[0];
    let a12 = &a[3];
    let a13 = &a[6];
    let a21 = &a[1];
    let a22 = &a[4];
    let a23 = &a[7];
    let a31 = &a[2];
    let a32 = &a[5];
    let a33 = &a[8];

    let b11 = &b[0];
    let b12 = &b[3];
    let b13 = &b[6];
    let b21 = &b[1];
    let b22 = &b[4];
    let b23 = &b[7];
    let b31 = &b[2];
    let b32 = &b[5];
    let b33 = &b[8];

    out[0] = a11 * b11 + a21 * b12 + a31 * b13;
    out[3] = a12 * b11 + a22 * b12 + a32 * b13;
    out[6] = a13 * b11 + a23 * b12 + a33 * b13;

    out[1] = a11 * b21 + a21 * b22 + a31 * b23;
    out[4] = a12 * b21 + a22 * b22 + a32 * b23;
    out[7] = a13 * b21 + a23 * b22 + a33 * b23;

    out[2] = a11 * b31 + a21 * b32 + a31 * b33;
    out[5] = a12 * b31 + a22 * b32 + a32 * b33;
    out[8] = a13 * b31 + a23 * b32 + a33 * b33;
    out
}
/// rmul_mut(A, B) does A = B * A
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::rmul_mut(&mut m, &mat3::new_identity());
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn rmul_mut<'out, T>(out: &'out mut [T; 9], m: &[T; 9]) -> &'out mut [T; 9]
where
    T: Clone + Add<T, Output = T>,
    for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
    let tmp = out.clone();
    mul(out, m, &tmp)
}
/// lmul_mut(A, B) does A = A * B
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::lmul_mut(&mut m, &mat3::new_identity());
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn lmul_mut<'out, T>(out: &'out mut [T; 9], m: &[T; 9]) -> &'out mut [T; 9]
where
    T: Clone + Add<T, Output = T>,
    for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
    let tmp = out.clone();
    mul(out, &tmp, m)
}
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::smul(&mut m, &mat3::new_identity(), &1_f32);
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn smul<'out, T>(out: &'out mut [T; 9], m: &[T; 9], s: &T) -> &'out mut [T; 9]
where
    T: Add<T, Output = T>,
    for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
    out[0] = &m[0] * s;
    out[1] = &m[1] * s;
    out[2] = &m[2] * s;
    out[3] = &m[3] * s;
    out[4] = &m[4] * s;
    out[5] = &m[5] * s;
    out[6] = &m[6] * s;
    out[7] = &m[7] * s;
    out[8] = &m[8] * s;
    out
}
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::smul_mut(&mut m, &1_f32);
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn smul_mut<'out, T>(out: &'out mut [T; 9], s: &T) -> &'out mut [T; 9]
where
    T: Clone + Add<T, Output = T>,
    for<'a, 'b> &'a T: Mul<&'b T, Output = T>,
{
    let tmp = out.clone();
    smul(out, &tmp, s)
}
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::div(&mut m, &mat3::new_identity(), &mat3::new_identity());
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn div<'out, T>(out: &'out mut [T; 9], a: &[T; 9], b: &[T; 9]) -> &'out mut [T; 9]
where
    T: Clone + One + Zero + Sub<T, Output = T>,
    for<'a, 'b> &'a T: Neg<Output = T> + Mul<&'b T, Output = T> + Div<&'b T, Output = T>,
{
    let mut inv_b = b.clone();
    inv(&mut inv_b, b);
    mul(out, a, &inv_b)
}
/// rdiv_mut(A, B) does A = B / A
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::rdiv_mut(&mut m, &mat3::new_identity());
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn rdiv_mut<'out, T>(out: &'out mut [T; 9], m: &[T; 9]) -> &'out mut [T; 9]
where
    T: Clone + One + Zero + Sub<T, Output = T>,
    for<'a, 'b> &'a T: Neg<Output = T> + Mul<&'b T, Output = T> + Div<&'b T, Output = T>,
{
    let tmp = out.clone();
    div(out, m, &tmp)
}
/// ldiv_mut(A, B) does A = A / B
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::ldiv_mut(&mut m, &mat3::new_identity());
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn ldiv_mut<'out, T>(out: &'out mut [T; 9], m: &[T; 9]) -> &'out mut [T; 9]
where
    T: Clone + One + Zero + Sub<T, Output = T>,
    for<'a, 'b> &'a T: Neg<Output = T> + Mul<&'b T, Output = T> + Div<&'b T, Output = T>,
{
    let tmp = out.clone();
    div(out, &tmp, m)
}
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::sdiv(&mut m, &mat3::new_identity(), &1_f32);
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn sdiv<'out, T>(out: &'out mut [T; 9], m: &[T; 9], s: &T) -> &'out mut [T; 9]
where
    T: One + Zero + Add<T, Output = T>,
    for<'a, 'b> &'a T: Div<&'b T, Output = T> + Mul<&'b T, Output = T>,
{
    let inv_s = if s.is_zero() {
        T::zero()
    } else {
        &T::one() / s
    };
    smul(out, m, &inv_s)
}
/// # Example
/// ```
/// let mut m = mat3::new_identity::<f32>();
/// mat3::sdiv_mut(&mut m, &1_f32);
/// assert_eq!(m, mat3::new_identity::<f32>());
/// ```
#[inline]
pub fn sdiv_mut<'out, T>(out: &'out mut [T; 9], s: &T) -> &'out mut [T; 9]
where
    T: Clone + One + Zero + Add<T, Output = T>,
    for<'a, 'b> &'a T: Div<&'b T, Output = T> + Mul<&'b T, Output = T>,
{
    let tmp = out.clone();
    sdiv(out, &tmp, s)
}