#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix {
	() => {
		impl<const X:usize,const Y:usize,T> Matrix<X,Y,T>{
			#[inline(always)]
			pub const fn new(array:[[T;Y];X])->Self{
				Self{array}
			}
			#[inline(always)]
			pub fn to_array(self)->[[T;Y];X]{
				self.array
			}
			#[inline]
			pub fn from_cols(cols:[Vector<Y,T>;X])->Self
			{
				Matrix::new(
					cols.map(|col|col.array),
				)
			}
			#[inline]
			pub fn map<F,U>(self,f:F)->Matrix<X,Y,U>
			where
				F:Fn(T)->U
			{
				Matrix::new(
					self.array.map(|inner|inner.map(&f)),
				)
			}
			#[inline]
			pub fn transpose(self)->Matrix<Y,X,T>{
				//how did I think of this
				let mut array_of_iterators=self.array.map(|axis|axis.into_iter());
				Matrix::new(
					core::array::from_fn(|_|
						array_of_iterators.each_mut().map(|iter|
							iter.next().unwrap()
						)
					)
				)
			}
			#[inline]
			// old (list of rows)    MatY<VecX>.MatX<VecZ> = MatY<VecZ>
			// new (list of columns) MatX<VecY>.MatZ<VecX> = MatZ<VecY>
			pub fn dot<const Z:usize,U,V>(self,rhs:Matrix<Z,X,U>)->Matrix<Z,Y,V>
			where
				T:core::ops::Mul<U,Output=V>+Copy,
				V:core::iter::Sum,
				U:Copy,
			{
				let mut array_of_iterators=self.array.map(|axis|axis.into_iter().cycle());
				Matrix{
					array:rhs.array.map(|rhs_axis|
						core::array::from_fn(|_|
							array_of_iterators
								.iter_mut()
								.zip(rhs_axis.iter())
								.map(|(lhs_iter,&rhs_value)|
									lhs_iter.next().unwrap()*rhs_value
								).sum()
						)
					)
				}
			}
			#[inline]
			// MatX<VecY>.VecY = VecX
			pub fn transform_vector<U,V>(self,rhs:Vector<X,U>)->Vector<Y,V>
			where
				T:core::ops::Mul<U,Output=V>,
				V:core::iter::Sum,
				U:Copy,
			{
				let mut array_of_iterators=self.array.map(|axis|axis.into_iter());
				Vector::new(
					core::array::from_fn(|_|
						array_of_iterators
							.iter_mut()
							.zip(rhs.array.iter())
							.map(|(lhs_iter,&rhs_value)|
								lhs_iter.next().unwrap()*rhs_value
							).sum()
					)
				)
			}
		}
		impl<const X:usize,const Y:usize,T> Matrix<X,Y,T>
		where
			T:Copy
		{
			#[inline(always)]
			pub const fn from_value(value:T)->Self{
				Self::new([[value;Y];X])
			}
		}

		impl<const X:usize,const Y:usize,T:Default> Default for Matrix<X,Y,T>{
			#[inline]
			fn default()->Self{
				Self::new(
					core::array::from_fn(|_|core::array::from_fn(|_|Default::default()))
				)
			}
		}

		impl<const X:usize,const Y:usize,T:core::fmt::Display> core::fmt::Display for Matrix<X,Y,T>{
			#[inline]
			fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{
				for col in &self.array[0..X]{
					core::write!(f,"\n")?;
					for elem in &col[0..Y-1]{
						core::write!(f,"{}, ",elem)?;
					}
					// assume we will be using matrices of size 1x1 or greater
					core::write!(f,"{}",col.last().unwrap())?;
				}
				Ok(())
			}
		}

		impl<const X:usize,const Y:usize,const Z:usize,T,U,V> core::ops::Mul<Matrix<Z,X,U>> for Matrix<X,Y,T>
		where
			T:core::ops::Mul<U,Output=V>+Copy,
			V:core::iter::Sum,
			U:Copy,
		{
			type Output=Matrix<Z,Y,V>;
			#[inline]
			fn mul(self,rhs:Matrix<Z,X,U>)->Self::Output{
				self.dot(rhs)
			}
		}
		impl<const X:usize,const Y:usize,T,U,V> core::ops::Mul<Vector<X,U>> for Matrix<X,Y,T>
		where
			T:core::ops::Mul<U,Output=V>,
			V:core::iter::Sum,
			U:Copy,
		{
			type Output=Vector<Y,V>;
			#[inline]
			fn mul(self,rhs:Vector<X,U>)->Self::Output{
				self.transform_vector(rhs)
			}
		}
		#[cfg(feature="deferred-division")]
		$crate::impl_matrix_deferred_division!();
	}
}

#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_deferred_division {
	() => {
		impl<const X:usize,const Y:usize,T:ratio_ops::ratio::Divide<U,Output=V>,U:Copy,V> ratio_ops::ratio::Divide<U> for Matrix<X,Y,T>{
			type Output=Matrix<X,Y,V>;
			#[inline]
			fn divide(self,rhs:U)->Self::Output{
				self.map(|t|t.divide(rhs))
			}
		}
		impl<const X:usize,const Y:usize,T,U> core::ops::Div<U> for Matrix<X,Y,T>{
			type Output=ratio_ops::ratio::Ratio<Matrix<X,Y,T>,U>;
			#[inline]
			fn div(self,rhs:U)->Self::Output{
				ratio_ops::ratio::Ratio::new(self,rhs)
			}
		}
	}
}

#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_extend {
	( $x: expr, $y: expr ) => {
		impl<T> Matrix<$x,$y,T>{
			#[inline]
			pub fn extend_column(self,value:Vector<$y,T>)->Matrix<{$x+1},$y,T>{
				let mut iter=self.array.into_iter().chain(core::iter::once(value.array));
				Matrix::new(
					core::array::from_fn(|_|iter.next().unwrap()),
				)
			}
			#[inline]
			pub fn extend_row(self,value:Vector<$x,T>)->Matrix<$x,{$y+1},T>{
				let mut iter_rows=value.array.into_iter();
				Matrix::new(
					self.array.map(|axis|{
						let mut elements_iter=axis.into_iter().chain(core::iter::once(iter_rows.next().unwrap()));
						core::array::from_fn(|_|elements_iter.next().unwrap())
					})
				)
			}
		}
	}
}

#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_named_fields_shape {
	(
		($struct_outer:ident, $size_outer: expr),
		($size_inner: expr)
	) => {
		impl<T> core::ops::Deref for Matrix<$size_outer,$size_inner,T>{
			type Target=$struct_outer<Vector<$size_inner,T>>;
			#[inline]
			fn deref(&self)->&Self::Target{
				// This cast is valid because Matrix has #[repr(transparent)]
				let ptr:*const [[T;$size_inner];$size_outer]=&self.array;
				let ptr=ptr as *const Self::Target;
				// SAFETY: this pointer is non-null because it comes from a reference
				unsafe{&*ptr}
			}
		}
		impl<T> core::ops::DerefMut for Matrix<$size_outer,$size_inner,T>{
			#[inline]
			fn deref_mut(&mut self)->&mut Self::Target{
				// This cast is valid because Matrix has #[repr(transparent)]
				let ptr:*mut [[T;$size_inner];$size_outer]=&mut self.array;
				let ptr=ptr as *mut Self::Target;
				// SAFETY: this pointer is non-null because it comes from a reference
				unsafe{&mut*ptr}
			}
		}
	}
}

#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_named_fields_shape_shim {
	(
		($($vector_info:tt),+),
		$matrix_info:tt
	) => {
		$crate::macro_repeated!(impl_matrix_named_fields_shape,$matrix_info,$($vector_info),+);
	}
}

#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_named_fields {
	(
		($($matrix_info:tt),+),
		$vector_infos:tt
	) => {
		$crate::macro_repeated!(impl_matrix_named_fields_shape_shim,$vector_infos,$($matrix_info),+);
	}
}

#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_3x3 {
	()=>{
		impl<T,T2,T3> Matrix<3,3,T>
		where
			//cross
			T:core::ops::Mul<T,Output=T2>+Copy,
			T2:core::ops::Sub,
			//dot
			T:core::ops::Mul<<T2 as core::ops::Sub>::Output,Output=T3>,
			T3:core::iter::Sum,
		{
			pub fn det(self)->T3{
				self.x_axis.dot(self.y_axis.cross(self.z_axis))
			}
		}
		impl<T,T2> Matrix<3,3,T>
		where
			T:core::ops::Mul<T,Output=T2>+Copy,
			T2:core::ops::Sub,
		{
			pub fn adjugate(self)->Matrix<3,3,<T2 as core::ops::Sub>::Output>{
				Matrix::new([
					[self.y_axis.y*self.z_axis.z-self.y_axis.z*self.z_axis.y,self.x_axis.z*self.z_axis.y-self.x_axis.y*self.z_axis.z,self.x_axis.y*self.y_axis.z-self.x_axis.z*self.y_axis.y],
					[self.y_axis.z*self.z_axis.x-self.y_axis.x*self.z_axis.z,self.x_axis.x*self.z_axis.z-self.x_axis.z*self.z_axis.x,self.x_axis.z*self.y_axis.x-self.x_axis.x*self.y_axis.z],
					[self.y_axis.x*self.z_axis.y-self.y_axis.y*self.z_axis.x,self.x_axis.y*self.z_axis.x-self.x_axis.x*self.z_axis.y,self.x_axis.x*self.y_axis.y-self.x_axis.y*self.y_axis.x],
				])
			}
		}
	}
}
