4 Commits

Author SHA1 Message Date
c51dc6098c matrix: directly implement dot product to avoid a copy 2024-09-04 12:04:08 -07:00
6aba246453 umm the macro 2024-09-04 12:04:08 -07:00
f7ae745bef probably wrong stuff 2024-09-03 12:53:42 -07:00
1f1568ebe9 wip mat mul 2024-09-03 12:53:42 -07:00
38 changed files with 1116 additions and 1432 deletions

5
fixed_wide/Cargo.lock generated

@ -21,7 +21,6 @@ dependencies = [
"arrayvec",
"bnum",
"paste",
"ratio_ops",
]
[[package]]
@ -29,7 +28,3 @@ name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "ratio_ops"
version = "0.1.0"

@ -4,13 +4,11 @@ version = "0.1.0"
edition = "2021"
[features]
default=[]
deferred-division=["dep:ratio_ops"]
wide-mul=[]
zeroes=["dep:arrayvec"]
default=["zeroes"]
ratio=[]
zeroes=["ratio","dep:arrayvec"]
[dependencies]
bnum = "0.11.0"
arrayvec = { version = "0.7.6", optional = true }
paste = "1.0.15"
ratio_ops = { path = "../ratio_ops", optional = true }

@ -1,7 +1,6 @@
use bnum::{BInt,cast::As};
#[derive(Clone,Copy,Debug,Hash)]
/// A Fixed point number for which multiply operations widen the bits in the output. (when the wide-mul feature is enabled)
/// N is the number of u64s to use
/// F is the number of fractional bits (always N*32 lol)
pub struct Fixed<const N:usize,const F:usize>{
@ -33,17 +32,6 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
self.bits
}
#[inline]
pub const fn raw_digit(value:i64)->Self{
let mut digits=[0u64;N];
digits[0]=value.abs() as u64;
//sign bit
digits[N-1]|=(value&i64::MIN) as u64;
Self::from_bits(BInt::from_bits(bnum::BUint::from_digits(digits)))
}
}
impl<const F:usize> Fixed<1,F>{
/// My old code called this function everywhere so let's provide it
#[inline]
pub const fn raw(value:i64)->Self{
Self::from_bits(BInt::from_bits(bnum::BUint::from_digit(value as u64)))
}
@ -53,48 +41,24 @@ impl<const N:usize,const F:usize,T> From<T> for Fixed<N,F>
where
BInt<N>:From<T>
{
#[inline]
fn from(value:T)->Self{
Self::from_bits(BInt::<{N}>::from(value)<<F as u32)
}
}
impl<const N:usize,const F:usize> PartialEq for Fixed<N,F>{
#[inline]
fn eq(&self,other:&Self)->bool{
self.bits.eq(&other.bits)
}
}
impl<const N:usize,const F:usize,T> PartialEq<T> for Fixed<N,F>
where
T:Copy,
BInt::<N>:From<T>,
{
#[inline]
fn eq(&self,&other:&T)->bool{
self.bits.eq(&other.into())
}
}
impl<const N:usize,const F:usize> Eq for Fixed<N,F>{}
impl<const N:usize,const F:usize> PartialOrd for Fixed<N,F>{
#[inline]
fn partial_cmp(&self,other:&Self)->Option<std::cmp::Ordering>{
self.bits.partial_cmp(&other.bits)
}
}
impl<const N:usize,const F:usize,T> PartialOrd<T> for Fixed<N,F>
where
T:Copy,
BInt::<N>:From<T>,
{
#[inline]
fn partial_cmp(&self,&other:&T)->Option<std::cmp::Ordering>{
self.bits.partial_cmp(&other.into())
}
}
impl<const N:usize,const F:usize> Ord for Fixed<N,F>{
#[inline]
fn cmp(&self,other:&Self)->std::cmp::Ordering{
self.bits.cmp(&other.bits)
}
@ -102,69 +66,18 @@ impl<const N:usize,const F:usize> Ord for Fixed<N,F>{
impl<const N:usize,const F:usize> std::ops::Neg for Fixed<N,F>{
type Output=Self;
#[inline]
fn neg(self)->Self{
Self::from_bits(self.bits.neg())
}
}
impl<const N:usize,const F:usize> std::iter::Sum for Fixed<N,F>{
#[inline]
fn sum<I:Iterator<Item=Self>>(iter:I)->Self{
let mut sum=Self::ZERO;
for elem in iter{
sum+=elem;
}
sum
}
}
macro_rules! impl_into_float {
( $output: ty ) => {
impl<const N:usize,const F:usize> Into<$output> for Fixed<N,F>{
#[inline]
fn into(self)->$output{
let mut total=0.0;
let bits=self.bits.to_bits();
let digits=bits.digits();
for (i,digit) in digits[0..N-1].iter().enumerate(){
// (i*64-F) as i32 will interpret the highest order bit as a sign bit but whatever
total+=(*digit as $output)*(2.0 as $output).powi((i*64-F) as i32);
}
//most significant digit holds the sign bit
//assume we are using a number with at least 1 digit...
total+=((*digits.last().unwrap() as i64).abs() as $output)*(2.0 as $output).powi(((N-1)*64-F) as i32);
if self.bits.is_negative(){
total=-total;
}
total
}
}
}
}
impl_into_float!(f32);
impl_into_float!(f64);
impl<const N:usize,const F:usize> core::fmt::Display for Fixed<N,F>{
#[inline]
fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{
let float:f32=(*self).into();
core::write!(f,"{:.3}",float)
}
}
macro_rules! impl_additive_operator {
( $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
impl<const N:usize,const F:usize> $struct<N,F>{
#[inline]
pub const fn $method(self, other: Self) -> Self {
Self::from_bits(self.bits.$method(other.bits))
}
}
impl<const N:usize,const F:usize> core::ops::$trait for $struct<N,F>{
type Output = $output;
#[inline]
fn $method(self, other: Self) -> Self::Output {
self.$method(other)
Self::from_bits(self.bits.$method(other.bits))
}
}
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
@ -172,7 +85,7 @@ macro_rules! impl_additive_operator {
BInt::<N>:From<U>,
{
type Output = $output;
#[inline]
fn $method(self, other: U) -> Self::Output {
Self::from_bits(self.bits.$method(BInt::<N>::from(other).shl(F as u32)))
}
@ -182,7 +95,6 @@ macro_rules! impl_additive_operator {
macro_rules! impl_additive_assign_operator {
( $struct: ident, $trait: ident, $method: ident ) => {
impl<const N:usize,const F:usize> core::ops::$trait for $struct<N,F>{
#[inline]
fn $method(&mut self, other: Self) {
self.bits.$method(other.bits);
}
@ -191,9 +103,8 @@ macro_rules! impl_additive_assign_operator {
where
BInt::<N>:From<U>,
{
#[inline]
fn $method(&mut self, other: U) {
self.bits.$method(BInt::<N>::from(other).shl(F as u32));
self.bits.$method(BInt::<N>::from(other)<<F as u32);
}
}
};
@ -215,91 +126,75 @@ impl_additive_operator!( Fixed, BitOr, bitor, Self );
impl_additive_assign_operator!( Fixed, BitXorAssign, bitxor_assign );
impl_additive_operator!( Fixed, BitXor, bitxor, Self );
// non-wide operators. The result is the same width as the inputs.
// This macro is not used in the default configuration.
#[allow(unused_macros)]
macro_rules! impl_multiplicative_operator_not_const_generic {
( ($struct: ident, $trait: ident, $method: ident, $output: ty ), $width:expr ) => {
macro_rules! impl_multiply_operator_const {
( $width:expr, $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
impl<const F:usize> core::ops::$trait for $struct<$width,F>{
type Output = $output;
#[inline]
fn $method(self, other: Self) -> Self::Output {
paste::item!{
self.[<fixed_ $method>](other)
}
}
}
};
}
macro_rules! impl_multiplicative_assign_operator_not_const_generic {
( ($struct: ident, $trait: ident, $method: ident, $non_assign_method: ident ), $width:expr ) => {
impl<const F:usize> core::ops::$trait for $struct<$width,F>{
#[inline]
fn $method(&mut self, other: Self) {
paste::item!{
*self=self.[<fixed_ $non_assign_method>](other);
}
}
}
};
}
macro_rules! impl_multiply_operator_not_const_generic {
( ($struct: ident, $trait: ident, $method: ident, $output: ty ), $width:expr ) => {
impl<const F:usize> $struct<$width,F>{
paste::item!{
#[inline]
pub fn [<fixed_ $method>](self, other: Self) -> Self {
fn $method(self, other: Self) -> Self::Output {
//this can be done better but that is a job for later
let lhs=self.bits.as_::<BInt::<{$width*2}>>();
let rhs=other.bits.as_::<BInt::<{$width*2}>>();
Self::from_bits(lhs.mul(rhs).shr(F as u32).as_())
}
}
};
}
macro_rules! impl_multiply_assign_operator_const {
( $width:expr, $struct: ident, $trait: ident, $method: ident ) => {
impl<const F:usize> core::ops::$trait for $struct<$width,F>{
fn $method(&mut self, other: Self) {
self.bits.$method(other.bits);
}
}
#[cfg(not(feature="wide-mul"))]
impl_multiplicative_operator_not_const_generic!(($struct, $trait, $method, $output ), $width);
}
};
}
macro_rules! impl_divide_operator_not_const_generic {
( ($struct: ident, $trait: ident, $method: ident, $output: ty ), $width:expr ) => {
impl<const F:usize> $struct<$width,F>{
paste::item!{
#[inline]
pub fn [<fixed_ $method>](self, other: Self) -> Self {
macro_rules! impl_divide_operator_const {
( $width:expr, $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
impl<const F:usize> core::ops::$trait for $struct<$width,F>{
type Output = $output;
fn $method(self, other: Self) -> Self::Output {
//this can be done better but that is a job for later
//this only needs to be $width+F as u32/64+1 but MUH CONST GENERICS!!!!!
let lhs=self.bits.as_::<BInt::<{$width*2}>>().shl(F as u32);
let rhs=other.bits.as_::<BInt::<{$width*2}>>();
Self::from_bits(lhs.div(rhs).as_())
}
}
};
}
macro_rules! impl_divide_assign_operator_const {
( $width:expr, $struct: ident, $trait: ident, $method: ident ) => {
impl<const F:usize> core::ops::$trait for $struct<$width,F>{
fn $method(&mut self, other: Self) {
self.bits.$method(other.bits);
}
}
#[cfg(all(not(feature="wide-mul"),not(feature="deferred-division")))]
impl_multiplicative_operator_not_const_generic!(($struct, $trait, $method, $output ), $width);
};
}
macro_rules! impl_multiplicative_operator {
macro_rules! impl_multiplicatave_operator {
( $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
where
BInt::<N>:From<U>+core::ops::$trait,
{
type Output = $output;
#[inline]
fn $method(self, other: U) -> Self::Output {
Self::from_bits(self.bits.$method(BInt::<N>::from(other)))
}
}
};
}
macro_rules! impl_multiplicative_assign_operator {
macro_rules! impl_multiplicatave_assign_operator {
( $struct: ident, $trait: ident, $method: ident ) => {
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
where
BInt::<N>:From<U>+core::ops::$trait,
{
#[inline]
fn $method(&mut self, other: U) {
self.bits.$method(BInt::<N>::from(other));
}
@ -307,46 +202,61 @@ macro_rules! impl_multiplicative_assign_operator {
};
}
macro_rules! macro_repeated{
(
$macro:ident,
$any:tt,
$($repeated:tt),*
)=>{
$(
$macro!($any, $repeated);
)*
};
macro_rules! impl_operator_16 {
( $macro: ident, $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
$macro!(1,$struct,$trait,$method,$output);
$macro!(2,$struct,$trait,$method,$output);
$macro!(3,$struct,$trait,$method,$output);
$macro!(4,$struct,$trait,$method,$output);
$macro!(5,$struct,$trait,$method,$output);
$macro!(6,$struct,$trait,$method,$output);
$macro!(7,$struct,$trait,$method,$output);
$macro!(8,$struct,$trait,$method,$output);
$macro!(9,$struct,$trait,$method,$output);
$macro!(10,$struct,$trait,$method,$output);
$macro!(11,$struct,$trait,$method,$output);
$macro!(12,$struct,$trait,$method,$output);
$macro!(13,$struct,$trait,$method,$output);
$macro!(14,$struct,$trait,$method,$output);
$macro!(15,$struct,$trait,$method,$output);
$macro!(16,$struct,$trait,$method,$output);
}
}
macro_rules! macro_16 {
( $macro: ident, $any:tt ) => {
macro_repeated!($macro,$any,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
macro_rules! impl_assign_operator_16 {
( $macro: ident, $struct: ident, $trait: ident, $method: ident ) => {
$macro!(1,$struct,$trait,$method);
$macro!(2,$struct,$trait,$method);
$macro!(3,$struct,$trait,$method);
$macro!(4,$struct,$trait,$method);
$macro!(5,$struct,$trait,$method);
$macro!(6,$struct,$trait,$method);
$macro!(7,$struct,$trait,$method);
$macro!(8,$struct,$trait,$method);
$macro!(9,$struct,$trait,$method);
$macro!(10,$struct,$trait,$method);
$macro!(11,$struct,$trait,$method);
$macro!(12,$struct,$trait,$method);
$macro!(13,$struct,$trait,$method);
$macro!(14,$struct,$trait,$method);
$macro!(15,$struct,$trait,$method);
$macro!(16,$struct,$trait,$method);
}
}
macro_16!( impl_multiplicative_assign_operator_not_const_generic, (Fixed, MulAssign, mul_assign, mul) );
macro_16!( impl_multiply_operator_not_const_generic, (Fixed, Mul, mul, Self) );
macro_16!( impl_multiplicative_assign_operator_not_const_generic, (Fixed, DivAssign, div_assign, div) );
macro_16!( impl_divide_operator_not_const_generic, (Fixed, Div, div, Self) );
impl_multiplicative_assign_operator!( Fixed, MulAssign, mul_assign );
impl_multiplicative_operator!( Fixed, Mul, mul, Self );
impl_multiplicative_assign_operator!( Fixed, DivAssign, div_assign );
impl_multiplicative_operator!( Fixed, Div, div, Self );
#[cfg(feature="deferred-division")]
impl<const LHS_N:usize,const LHS_F:usize,const RHS_N:usize,const RHS_F:usize> core::ops::Div<Fixed<RHS_N,RHS_F>> for Fixed<LHS_N,LHS_F>{
type Output=ratio_ops::ratio::Ratio<Fixed<LHS_N,LHS_F>,Fixed<RHS_N,RHS_F>>;
#[inline]
fn div(self, other: Fixed<RHS_N,RHS_F>)->Self::Output{
ratio_ops::ratio::Ratio::new(self,other)
}
}
impl_assign_operator_16!( impl_multiply_assign_operator_const, Fixed, MulAssign, mul_assign );
impl_operator_16!( impl_multiply_operator_const, Fixed, Mul, mul, Self );
impl_assign_operator_16!( impl_divide_assign_operator_const, Fixed, DivAssign, div_assign );
impl_operator_16!( impl_divide_operator_const, Fixed, Div, div, Self );
impl_multiplicatave_assign_operator!( Fixed, MulAssign, mul_assign );
impl_multiplicatave_operator!( Fixed, Mul, mul, Self );
impl_multiplicatave_assign_operator!( Fixed, DivAssign, div_assign );
impl_multiplicatave_operator!( Fixed, Div, div, Self );
macro_rules! impl_shift_operator {
( $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
impl<const N:usize,const F:usize> core::ops::$trait<u32> for $struct<N,F>{
type Output = $output;
#[inline]
fn $method(self, other: u32) -> Self::Output {
Self::from_bits(self.bits.$method(other))
}
@ -356,7 +266,6 @@ macro_rules! impl_shift_operator {
macro_rules! impl_shift_assign_operator {
( $struct: ident, $trait: ident, $method: ident ) => {
impl<const N:usize,const F:usize> core::ops::$trait<u32> for $struct<N,F>{
#[inline]
fn $method(&mut self, other: u32) {
self.bits.$method(other);
}
@ -368,103 +277,56 @@ impl_shift_operator!( Fixed, Shl, shl, Self );
impl_shift_assign_operator!( Fixed, ShrAssign, shr_assign );
impl_shift_operator!( Fixed, Shr, shr, Self );
// wide operators. The result width is the sum of the input widths, i.e. none of the multiplication
macro_rules! impl_wide_operators{
($lhs:expr,$rhs:expr)=>{
impl core::ops::Mul<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
type Output=Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>;
#[inline]
fn mul(self, other: Fixed<$rhs,{$rhs*32}>)->Self::Output{
paste::item!{
self.[<wide_mul_ $lhs _ $rhs>](other)
}
}
}
#[cfg(not(feature="deferred-division"))]
impl core::ops::Div<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
type Output=Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>;
#[inline]
fn div(self, other: Fixed<$rhs,{$rhs*32}>)->Self::Output{
paste::item!{
self.[<wide_div_ $lhs _ $rhs>](other)
}
}
}
}
}
// WIDE MUL: multiply into a wider type
// let a = I32F32::ONE;
// let b:I64F64 = a.wide_mul(a);
macro_rules! impl_wide_not_const_generic{
(
(),
($lhs:expr,$rhs:expr)
)=>{
macro_rules! impl_wide_mul{
($lhs:expr,$rhs:expr)=>{
impl Fixed<$lhs,{$lhs*32}>
{
paste::item!{
#[inline]
pub fn [<wide_mul_ $lhs _ $rhs>](self,rhs:Fixed<$rhs,{$rhs*32}>)->Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>{
let lhs=self.bits.as_::<BInt<{$lhs+$rhs}>>();
let rhs=rhs.bits.as_::<BInt<{$lhs+$rhs}>>();
Fixed::from_bits(lhs*rhs)
}
/// This operation cannot represent the fraction exactly,
/// but it shapes the output to have precision for the
/// largest and smallest possible fractions.
#[inline]
pub fn [<wide_div_ $lhs _ $rhs>](self,rhs:Fixed<$rhs,{$rhs*32}>)->Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>{
// (lhs/2^LHS_FRAC)/(rhs/2^RHS_FRAC)
let lhs=self.bits.as_::<BInt<{$lhs+$rhs}>>().shl($rhs*64);
let rhs=rhs.bits.as_::<BInt<{$lhs+$rhs}>>();
Fixed::from_bits(lhs/rhs)
Fixed::from_bits(self.bits.as_::<BInt<{$lhs+$rhs}>>()*rhs.bits.as_::<BInt<{$lhs+$rhs}>>())
}
}
}
#[cfg(feature="wide-mul")]
impl_wide_operators!($lhs,$rhs);
};
}
macro_rules! impl_wide_mul_all{
($(($x:expr, $y:expr)),*)=>{
$(
impl_wide_mul!($x, $y);
)*
};
}
//const generics sidestepped wahoo
macro_repeated!(
impl_wide_not_const_generic,(),
(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),
(1,2),(2,2),(3,2),(4,2),(5,2),(6,2),(7,2),(8,2),(9,2),(10,2),(11,2),(12,2),(13,2),(14,2),
(1,3),(2,3),(3,3),(4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),
(1,4),(2,4),(3,4),(4,4),(5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),
(1,5),(2,5),(3,5),(4,5),(5,5),(6,5),(7,5),(8,5),(9,5),(10,5),(11,5),
(1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6),(8,6),(9,6),(10,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),(7,7),(8,7),(9,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),(8,8),
(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),
(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),
(1,11),(2,11),(3,11),(4,11),(5,11),
(1,12),(2,12),(3,12),(4,12),
(1,13),(2,13),(3,13),
(1,14),(2,14),
(1,15)
impl_wide_mul_all!(
(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),
(1,2),(2,2),(3,2),(4,2),(5,2),(6,2),(7,2),(8,2),
(1,3),(2,3),(3,3),(4,3),(5,3),(6,3),(7,3),(8,3),
(1,4),(2,4),(3,4),(4,4),(5,4),(6,4),(7,4),(8,4),
(1,5),(2,5),(3,5),(4,5),(5,5),(6,5),(7,5),(8,5),
(1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6),(8,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),(7,7),(8,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),(8,8)
);
impl<const SRC:usize,const F:usize> Fixed<SRC,F>{
#[inline]
pub fn resize_into<const DST:usize>(self)->Fixed<DST,F>{
Fixed::from_bits(self.bits.as_::<BInt<DST>>())
}
}
macro_rules! impl_not_const_generic{
macro_rules! impl_const{
($n:expr)=>{
impl Fixed<{$n*2},{$n*2*32}>{
#[inline]
pub fn halve_precision(self)->Fixed<$n,{$n*32}>{
Fixed::from_bits(bnum::cast::As::as_(self.bits.shr($n*32)))
}
}
impl Fixed<$n,{$n*32}>{
paste::item!{
#[inline]
pub fn sqrt_unchecked(self)->Self{
//1<<max_shift must be the minimum power of two which when squared is greater than self
//calculating max_shift:
@ -477,19 +339,17 @@ macro_rules! impl_not_const_generic{
let mut result=Self::ZERO;
//multiply by one to make the types match (hack)
//TODO: use resize method
let wide_self:<Self as core::ops::Mul>::Output=self*Self::ONE;
let wide_self=self.[<wide_mul_ $n _ $n>](Self::ONE);
//descend down the bits and check if flipping each bit would push the square over the input value
for shift in (0..=max_shift).rev(){
let new_result=result|Self::from_bits(BInt::from_bits(bnum::BUint::power_of_two(shift)));
if new_result*new_result<=wide_self{
if new_result.[<wide_mul_ $n _ $n>](new_result)<=wide_self{
result=new_result;
}
}
result
}
}
#[inline]
pub fn sqrt(self)->Self{
if self<Self::ZERO{
panic!("Square root less than zero")
@ -497,7 +357,6 @@ macro_rules! impl_not_const_generic{
self.sqrt_unchecked()
}
}
#[inline]
pub fn sqrt_checked(self)->Option<Self>{
if self<Self::ZERO{
None
@ -508,11 +367,11 @@ macro_rules! impl_not_const_generic{
}
}
}
impl_not_const_generic!(1);
impl_not_const_generic!(2);
impl_not_const_generic!(3);
impl_not_const_generic!(4);
impl_not_const_generic!(5);
impl_not_const_generic!(6);
impl_not_const_generic!(7);
impl_not_const_generic!(8);
impl_const!(1);
impl_const!(2);
impl_const!(3);
impl_const!(4);
impl_const!(5);
impl_const!(6);
impl_const!(7);
impl_const!(8);

@ -3,6 +3,8 @@ pub mod types;
#[cfg(feature="zeroes")]
pub mod zeroes;
#[cfg(feature="ratio")]
pub mod ratio;
#[cfg(test)]
mod tests;

10
fixed_wide/src/ratio.rs Normal file

@ -0,0 +1,10 @@
#[derive(Clone,Copy,Debug,Hash)]
pub struct Ratio<Num,Den>{
pub(crate)num:Num,
pub(crate)den:Den,
}
impl<Num,Den> Ratio<Num,Den>{
pub const fn new(num:Num,den:Den)->Self{
Self{num,den}
}
}

@ -20,20 +20,6 @@ fn test_wide_mul(){
assert_eq!(aa,crate::types::I64F64::ONE);
}
#[test]
fn test_wide_div(){
let a=I32F32::ONE*4;
let b=I32F32::ONE*2;
let wide_a=a.wide_mul_1_1(I32F32::ONE);
let wide_b=b.wide_mul_1_1(I32F32::ONE);
let ab=a.wide_div_1_1(b);
assert_eq!(ab,crate::types::I64F64::ONE*2);
let wab=wide_a.wide_div_2_1(b);
assert_eq!(wab,crate::fixed::Fixed::<3,96>::ONE*2);
let awb=a.wide_div_1_2(wide_b);
assert_eq!(awb,crate::fixed::Fixed::<3,96>::ONE*2);
}
#[test]
fn test_wide_mul_repeated() {
let a=I32F32::from(2);
@ -65,7 +51,7 @@ fn test_sqrt_zero(){
#[test]
fn test_sqrt_low(){
let a=I32F32::HALF;
let b=a.fixed_mul(a);
let b=a*a;
assert_eq!(b.sqrt(),a);
}
fn find_equiv_sqrt_via_f64(n:I32F32)->I32F32{

@ -1,4 +1,5 @@
use crate::fixed::Fixed;
use crate::ratio::Ratio;
use arrayvec::ArrayVec;
use std::cmp::Ordering;
@ -6,38 +7,36 @@ macro_rules! impl_zeroes{
($n:expr)=>{
impl Fixed<$n,{$n*32}>{
#[inline]
pub fn zeroes2(a0:Self,a1:Self,a2:Self)->ArrayVec<<Self as core::ops::Div>::Output,2>{
pub fn zeroes2(a0:Self,a1:Self,a2:Self)->ArrayVec<Ratio<Self,Self>,2>{
let a2pos=match a2.cmp(&Self::ZERO){
Ordering::Greater=>true,
Ordering::Equal=>return ArrayVec::from_iter(Self::zeroes1(a0,a1).into_iter()),
Ordering::Less=>true,
};
paste::item!{
let radicand=a1*a1-a2*a0*4;
let radicand=a1.[<wide_mul_ $n _ $n>](a1)-a2.[<wide_mul_ $n _ $n>](a0)*4;
}
match radicand.cmp(&<Self as core::ops::Mul>::Output::ZERO){
match radicand.cmp(&Fixed::<{$n*2},{$n*2*32}>::ZERO){
Ordering::Greater=>{
//TODO: use resize method
let planar_radicand:Self=radicand.sqrt().halve_precision();
let planar_radicand=radicand.sqrt().halve_precision();
//sort roots ascending and avoid taking the difference of large numbers
let zeroes=match (a2pos,Self::ZERO<a1){
(true, true )=>[(-a1-planar_radicand)/(a2*2),(a0*2)/(-a1-planar_radicand)],
(true, false)=>[(a0*2)/(-a1+planar_radicand),(-a1+planar_radicand)/(a2*2)],
(false,true )=>[(a0*2)/(-a1-planar_radicand),(-a1-planar_radicand)/(a2*2)],
(false,false)=>[(-a1+planar_radicand)/(a2*2),(a0*2)/(-a1+planar_radicand)],
};
ArrayVec::from_iter(zeroes)
match (a2pos,Self::ZERO<a1){
(true, true )=>[Ratio::new(-a1-planar_radicand,a2*2),Ratio::new(a0*2,-a1-planar_radicand)].into(),
(true, false)=>[Ratio::new(a0*2,-a1+planar_radicand),Ratio::new(-a1+planar_radicand,a2*2)].into(),
(false,true )=>[Ratio::new(a0*2,-a1-planar_radicand),Ratio::new(-a1-planar_radicand,a2*2)].into(),
(false,false)=>[Ratio::new(-a1+planar_radicand,a2*2),Ratio::new(a0*2,-a1+planar_radicand)].into(),
}
},
Ordering::Equal=>ArrayVec::from_iter([(a1)/(a2*-2)]),
Ordering::Equal=>ArrayVec::from_iter([Ratio::new(a1,a2*-2)]),
Ordering::Less=>ArrayVec::new_const(),
}
}
#[inline]
pub fn zeroes1(a0:Self,a1:Self)->ArrayVec<<Self as core::ops::Div>::Output,1>{
pub fn zeroes1(a0:Self,a1:Self)->ArrayVec<Ratio<Self,Self>,1>{
if a1==Self::ZERO{
ArrayVec::new_const()
}else{
ArrayVec::from_iter([(-a0)/(a1)])
ArrayVec::from_iter([Ratio::new(-a0,a1)])
}
}
}

@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "bnum"
version = "0.11.0"
@ -12,16 +18,17 @@ checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790"
name = "fixed_wide"
version = "0.1.0"
dependencies = [
"arrayvec",
"bnum",
"paste",
]
[[package]]
name = "linear_ops"
name = "fixed_wide_vectors"
version = "0.1.0"
dependencies = [
"fixed_wide",
"ratio_ops",
"paste",
]
[[package]]
@ -29,7 +36,3 @@ name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "ratio_ops"
version = "0.1.0"

@ -0,0 +1,12 @@
[package]
name = "fixed_wide_vectors"
version = "0.1.0"
edition = "2021"
[features]
default=["fixed_wide"]
fixed_wide=["dep:fixed_wide"]
[dependencies]
fixed_wide = { version = "0.1.0", path = "../fixed_wide", optional = true }
paste = "1.0.15"

@ -0,0 +1,14 @@
mod macros;
mod vector;
mod matrix;
pub use vector::Vector2;
pub use vector::Vector3;
pub use vector::Vector4;
pub use matrix::Matrix2;
pub use matrix::Matrix3;
pub use matrix::Matrix4;
#[cfg(test)]
mod tests;

@ -0,0 +1,138 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_common {
( $struct: ident { $($field: ident), + }, $size: expr ) => {
impl<T> $struct<T> {
/// Constructs a new vector with the specified values for each field.
///
/// # Example
///
/// ```
/// use fixed_wide_vectors::Vector2;
///
/// let vec2 = Vector2::new(0, 0);
///
/// assert_eq!(vec2.x, 0);
/// assert_eq!(vec2.y, 0);
/// ```
#[inline(always)]
pub const fn new( $($field: T), + ) -> Self {
Self {
$( $field ), +
}
}
/// Consumes the vector and returns its values as an array.
///
/// # Example
///
/// ```
/// use fixed_wide_vectors::Vector2;
///
/// let vec2 = Vector2::new(0, 0);
/// let array = vec2.to_array();
///
/// assert_eq!(array, [0, 0]);
/// ```
#[inline(always)]
pub fn to_array(self) -> [T; $size] {
[ $(self.$field), + ]
}
/// Consumes the vector and returns a new vector with the given function applied on each field.
///
/// # Example
///
/// ```
/// use fixed_wide_vectors::Vector2;
///
/// let vec2 = Vector2::new(1, 2)
/// .map(|i| i * 2);
///
/// assert_eq!(vec2, Vector2::new(2, 4));
/// ```
#[inline]
pub fn map<F, U>(self, f: F) -> $struct<U>
where
F: Fn(T) -> U
{
$struct {
$( $field: f(self.$field) ), +
}
}
}
impl<T: Copy> $struct<T> {
/// Constructs a vector using the given `value` as the value for all of its fields.
///
/// # Example
///
/// ```
/// use fixed_wide_vectors::Vector2;
///
/// let vec2 = Vector2::from_value(0);
///
/// assert_eq!(vec2, Vector2::new(0, 0));
/// ```
#[inline(always)]
pub const fn from_value(value: T) -> Self {
Self {
$( $field: value ), +
}
}
}
impl<T> From<[T; $size]> for $struct<T> {
fn from(from: [T; $size]) -> Self {
let mut iterator = from.into_iter();
Self {
// SAFETY: We know the size of `from` so `iterator.next()` is always `Some(..)`
$( $field: unsafe { iterator.next().unwrap_unchecked() } ), +
}
}
}
impl<T: core::fmt::Debug> core::fmt::Debug for $struct<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let identifier = core::stringify!($struct);
f.debug_struct(identifier)
$( .field( core::stringify!($field), &self.$field ) ) +
.finish()
}
}
impl<T: PartialEq> PartialEq for $struct<T> {
fn eq(&self, other: &Self) -> bool {
$( self.$field == other.$field ) && +
}
}
impl<T: Eq> Eq for $struct<T> { }
impl<T: core::hash::Hash> core::hash::Hash for $struct<T> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
$( self.$field.hash(state); ) +
}
}
impl<T: Clone> Clone for $struct<T> {
fn clone(&self) -> Self {
Self {
$( $field: self.$field.clone() ), +
}
}
}
impl<T: Copy> Copy for $struct<T> { }
impl<T: Default> Default for $struct<T> {
fn default() -> Self {
Self {
$( $field: T::default() ), +
}
}
}
}
}

@ -0,0 +1,274 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_wide_vector_operations_2arg_not_const_generic {
(
($struct: ident { $($field: ident), + }, $size: expr),
($lhs:expr, $rhs:expr)
) => {
impl $struct<fixed_wide::fixed::Fixed<{$lhs},{$lhs*32}>>{
paste::item!{
#[inline]
pub fn [<wide_mul_ $lhs _ $rhs>](self,rhs:$struct<fixed_wide::fixed::Fixed<{$rhs},{$rhs*32}>>)->$struct<fixed_wide::fixed::Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>>{
$struct{
$( $field: self.$field.[<wide_mul_ $lhs _ $rhs>](rhs.$field) ), +
}
}
#[inline]
pub fn [<wide_dot_ $lhs _ $rhs>](self,rhs:$struct<fixed_wide::fixed::Fixed<{$rhs},{$rhs*32}>>)->fixed_wide::fixed::Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>{
$crate::sum_repeating!(
$( + (self.$field.[<wide_mul_ $lhs _ $rhs>](rhs.$field)) ) +
)
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_wide_vector_operations_1arg_not_const_generic {
(
($struct: ident { $($field: ident), + }, $size: expr),
$n:expr
) => {
impl $struct<fixed_wide::fixed::Fixed<{$n},{$n*32}>>{
paste::item!{
#[inline]
pub fn wide_length_squared(&self)->fixed_wide::fixed::Fixed<{$n*2},{$n*2*32}>{
$crate::sum_repeating!(
$( + self.$field.[<wide_mul_ $n _ $n>](self.$field) ) +
)
}
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! do_macro_8x8{
(
$macro:ident,
$any:tt
)=>{
$crate::macro_repeated!($macro, $any,
(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),
(1,2),(2,2),(3,2),(4,2),(5,2),(6,2),(7,2),(8,2),
(1,3),(2,3),(3,3),(4,3),(5,3),(6,3),(7,3),(8,3),
(1,4),(2,4),(3,4),(4,4),(5,4),(6,4),(7,4),(8,4),
(1,5),(2,5),(3,5),(4,5),(5,5),(6,5),(7,5),(8,5),
(1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6),(8,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),(7,7),(8,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),(8,8)
);
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! do_macro_8{
(
$macro:ident,
$any:tt
)=>{
$crate::macro_repeated!($macro, $any, 1,2,3,4,5,6,7,8);
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_wide_vector_operations {
( $struct: ident { $($field: ident), + }, $size: expr ) => {
$crate::do_macro_8!(impl_wide_vector_operations_1arg_not_const_generic,($struct { $($field), + }, $size));
$crate::do_macro_8x8!(impl_wide_vector_operations_2arg_not_const_generic,($struct { $($field), + }, $size));
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_mul_transpose_helper {
(
$lhs_axis:expr, $wide_mul:ident, $rhs:ident,
($struct: ident { $($field: ident), + }),
($from_struct: ident { $($from_field: ident), + }),
$static_field: ident
) => {
$crate::sum_repeating!(
$( + $lhs_axis.$field.$wide_mul($rhs.$from_field.$static_field) ) +
)
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_mul_inner {
(
// MatY<VecX>.MatX<VecZ> = MatY<VecZ>
$lhs:ident, $lhs_field_outer:ident, $wide_mul:ident, $rhs:ident,
$struct_inner_thru: tt, //VecX
($struct_inner: ident { $($field_inner: ident), + }), //VecX
($rhs_struct_inner: ident { $($rhs_field_inner: ident), + }), //VecZ
$rhs_outer: tt //MatX
) => {
$rhs_struct_inner {
$(
//directly dot product to avoid a copy
$rhs_field_inner: $crate::impl_matrix_wide_mul_transpose_helper!{
//lhs_axis
$lhs.$lhs_field_outer,$wide_mul,$rhs,
$struct_inner_thru, //VecZ
$rhs_outer, //MatX
$rhs_field_inner
}
), +
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_mul_outer {
(
// MatY<VecX>.MatX<VecZ> = MatY<VecZ>
$lhs:ident, $wide_mul:ident, $rhs:ident,
//result matrix shape
($struct_outer: ident { $($field_outer: ident), + }),//MatY
$rhs_struct_inner: tt,//VecZ
//inner loop shape
$struct_inner: tt,//VecX
$rhs_matrix: tt//MatX
) => {
$struct_outer {
$(
$field_outer: $crate::impl_matrix_wide_mul_inner!{
$lhs, $field_outer, $wide_mul, $rhs,
$struct_inner, //VecX
$struct_inner, //VecX
$rhs_struct_inner, //VecZ
$rhs_matrix //MatX
}
), +
}
}
}
// Notes:
// Mat3<Vec2>.dot(Vec2) -> Vec3
// lhs.dot(rhs) -> out
// lhs = Mat3<Vec4>
// rhs = Mat4<Vec2>
// out = Mat3<Vec2>
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
// how to matrix multiply:
// RHS TRANSPOSE
// Mat4<Vec2> -> Mat2<Vec4>
// rhs_t = Mat2<Vec4>
// inner loop:
// out[y][x] = lhs[y].dot(rhs_t[x])
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_mul {
(
($struct_outer: ident { $($field_outer: ident), + }, $vector_outer: ident { $($vector_field_outer: ident), + }, $size_outer: expr),
($struct_inner: ident { $($field_inner: ident), + }, $matrix_inner: ident { $($matrix_field_inner: ident), + }, $size_inner: expr),
($rhs_struct_inner: ident { $($rhs_field_inner: ident), + }, $rhs_matrix_inner: ident { $($rhs_matrix_field_inner: ident), + }, $rhs_size_inner: expr),
($lhs: expr, $rhs: expr)
) => {
impl $struct_outer<$struct_inner<fixed_wide::fixed::Fixed<{$lhs},{$lhs*32}>>>{
paste::item!{
#[inline]
pub fn [<wide_dot_ $size_outer x $size_inner _ $size_inner x $rhs_size_inner _ $lhs _ $rhs>](self,rhs:$matrix_inner<$rhs_struct_inner<fixed_wide::fixed::Fixed<{$rhs},{$rhs*32}>>>)->$struct_outer<$rhs_struct_inner<fixed_wide::fixed::Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>>>{
$crate::impl_matrix_wide_mul_outer!(
//constituent idents
self,[<wide_mul_ $lhs _ $rhs>],rhs,
//result matrix shape
($struct_outer { $($field_outer), + }),
($rhs_struct_inner { $($rhs_field_inner), + }),
//inner loop shape
($struct_inner { $($field_inner), + }),
($matrix_inner { $($matrix_field_inner), + })
)
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_mul_shim {
(
($outer_info:tt,$inner_info:tt,$rhs_info:tt),
($lhs: expr, $rhs: expr)
) => {
$crate::impl_matrix_wide_mul!($outer_info,$inner_info,$rhs_info,($lhs,$rhs));
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_mul_8x8 {
(
($outer_info:tt,$inner_info:tt),
$rhs_info:tt
) => {
$crate::do_macro_8x8!(impl_matrix_wide_mul_shim,($outer_info,$inner_info,$rhs_info));
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_mul_repeat_rhs {
(
($outer_info:tt,($($rhs_info:tt),+)),
$inner_info:tt
) => {
$crate::macro_repeated!(impl_matrix_wide_mul_8x8,($outer_info,$inner_info),$($rhs_info),+);
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_wide_matrix_operations_2arg_not_const_generic {
(
$lhs: expr, $rhs: expr,
($struct_outer: ident { $($field_outer: ident), + }, $vector_outer: ident { $($vector_field_outer: ident), + }, $size_outer: expr),
($struct_inner: ident { $($field_inner: ident), + }, $matrix_inner: ident { $($matrix_field_inner: ident), + }, $size_inner: expr)
) => {
/* TODO: nasty determinant macro
impl<U:std::ops::Add<Output=U>,T:Copy+fixed_wide_traits::wide::WideMul<Output=U>> $struct<T> {
#[inline]
pub fn wide_dot(&self) -> U {
$crate::sum_repeating!(
$( + self.$field.wide_mul(self.$field) ) +
)
}
}
*/
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_wide_matrix_operations_1arg_not_const_generic {
(
$n: expr,
($struct_outer: ident { $($field_outer: ident), + }, $vector_outer: ident { $($vector_field_outer: ident), + }, $size_outer: expr),
) => {
/* TODO: nasty determinant macro
impl<U:std::ops::Add<Output=U>,T:Copy+fixed_wide_traits::wide::WideMul<Output=U>> $struct<T> {
#[inline]
pub fn wide_det(&self) -> U {
$crate::sum_repeating!(
$( + self.$field.wide_mul(self.$field) ) +
)
}
}
*/
};
}
// HACK: Allows us to sum repeating tokens in macros.
// See: https://stackoverflow.com/a/60187870/17452730
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! sum_repeating {
( + $($item: tt) * ) => {
$($item) *
};
}

@ -0,0 +1,192 @@
// Stolen from https://github.com/c1m50c/fixed-vectors (MIT license)
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix {
(
($struct_outer: ident { $($field_outer: ident), + }, $vector_outer: ident { $($vector_field_outer: ident), + }, $size_outer: expr)
) => {
$crate::impl_common!($struct_outer { $($field_outer), + }, $size_outer);
impl<U> $struct_outer<U> {
#[inline(always)]
pub fn to_vector(self) -> $vector_outer<U> {
$vector_outer {
$(
$vector_field_outer: self.$field_outer
), +
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_shim {
(
(),
$matrix_info:tt
) => {
$crate::impl_matrix!($matrix_info);
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrices {
(
($($matrix_info:tt),+),
$vector_infos:tt
) => {
$crate::macro_repeated!(impl_matrix_shim,(),$($matrix_info),+);
$crate::macro_repeated!(impl_matrix_inner_shim,$vector_infos,$($matrix_info),+);
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_inner_shim {
(
($($vector_info:tt),+),
$matrix_info:tt
) => {
$crate::macro_repeated!(impl_matrix_inner,$matrix_info,$($vector_info),+);
#[cfg(feature="fixed_wide")]
$crate::macro_repeated!(impl_matrix_wide_mul_repeat_rhs,($matrix_info,($($vector_info),+)),$($vector_info),+);
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_inner {
(
($struct_outer: ident { $($field_outer: ident), + }, $vector_outer: ident { $($vector_field_outer: ident), + }, $size_outer: expr),
($struct_inner: ident { $($field_inner: ident), + }, $matrix_inner: ident { $($matrix_field_inner: ident), + }, $size_inner: expr)
) => {
impl<T> $struct_outer<$struct_inner<T>> {
#[inline(always)]
pub fn to_array_2d(self) -> [[T; $size_inner]; $size_outer] {
[ $(self.$field_outer.to_array()), + ]
}
#[inline]
pub fn map_2d<F, U>(self, f: F) -> $struct_outer<$struct_inner<U>>
where
F: Fn(T) -> U
{
$crate::matrix_map2d_outer!{f,self,($struct_outer { $($field_outer), + }),($struct_inner { $($field_inner), + })}
}
#[inline]
pub fn transpose(self) -> $matrix_inner<$vector_outer<T>>{
$crate::matrix_transpose_outer!{self,
($matrix_inner { $($matrix_field_inner), + }),($struct_inner { $($field_inner), + }),
($vector_outer { $($vector_field_outer), + }),($struct_outer { $($field_outer), + })
}
}
}
impl<T: Copy> $struct_outer<$struct_inner<T>> {
#[inline(always)]
pub const fn from_value_2d(value: T) -> Self {
Self {
$( $field_outer: $struct_inner::from_value(value) ), +
}
}
//TODO: diagonal
}
// Impl floating-point based methods
//#[cfg(feature="fixed_wide_traits")]
//$crate::impl_wide_matrix_operations!( ($struct_outer { $($field_outer), + }, $size_outer), ($struct_inner, $size_inner), $fields_inner );
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! matrix_map2d_outer {
( $f:ident, $value:ident, ($struct_outer: ident { $($field_outer: ident), + }), $unparsed_inner:tt ) => {
$struct_outer {
$(
$field_outer: $crate::matrix_map2d_inner!{$f,$value,$field_outer,$unparsed_inner}
), +
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! matrix_map2d_inner {
( $f:ident, $value:ident, $field_outer:ident, ($struct_inner: ident { $($field_inner: ident), + }) ) => {
$struct_inner {
$(
$field_inner: $f($value.$field_outer.$field_inner)
), +
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! matrix_transpose_outer {
(
$value:ident,
($struct_outer: ident { $($field_outer: ident), + }),
($old_outer: ident { $($old_field_outer: ident), + }),
$fields_inner:tt,
$old_fields_inner:tt
) => {
$struct_outer {
$(
$field_outer: $crate::matrix_transpose_inner!{$value,$old_field_outer,$fields_inner,$old_fields_inner}
), +
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! matrix_transpose_inner {
( $value:ident, $field_outer:ident,
($struct_inner: ident { $($field_inner: ident), + }),
($old_struct_inner: ident { $($old_field_inner: ident), + })
) => {
$struct_inner {
$(
$field_inner: $value.$old_field_inner.$field_outer
), +
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_operator {
( $struct: ident { $($field: ident), + }, $trait: ident, $method: ident, $output: ty ) => {
impl<T:core::ops::$trait<Output=T>> core::ops::$trait for $struct<T> {
type Output = $output;
fn $method(self, other: Self) -> Self::Output {
Self {
$( $field: self.$field.$method(other.$field) ), +
}
}
}
impl<T:core::ops::$trait<Output=T>+Copy> core::ops::$trait<T> for $struct<T>{
type Output = $output;
fn $method(self, other: T) -> Self::Output {
$struct {
$( $field: self.$field.$method(other) ), +
}
}
}
};
( $struct: ident { $($field: ident), + }, $trait: ident, $method: ident ) => {
impl<T: core::ops::$trait> core::ops::$trait for $struct<T> {
fn $method(&mut self, other: Self) {
$( self.$field.$method(other.$field) ); +
}
}
impl<T: core::ops::$trait + Copy> core::ops::$trait<T> for $struct<T> {
fn $method(&mut self, other: T) {
$( self.$field.$method(other) ); +
}
}
};
}

@ -1,3 +1,6 @@
#[cfg(feature="fixed_wide")]
pub mod fixed_wide;
pub mod common;
pub mod vector;
pub mod matrix;

@ -0,0 +1,146 @@
// Stolen from https://github.com/c1m50c/fixed-vectors (MIT license)
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector {
( $struct: ident { $($field: ident), + }, $size: expr ) => {
$crate::impl_common!($struct { $($field), + }, $size);
impl<T: Ord> $struct<T> {
pub fn min(self, rhs: Self) -> $struct<T> {
$struct{
$( $field: self.$field.min(rhs.$field) ), +
}
}
pub fn max(self, rhs: Self) -> $struct<T> {
$struct{
$( $field: self.$field.max(rhs.$field) ), +
}
}
pub fn cmp(self, rhs: Self) -> $struct<core::cmp::Ordering> {
$struct{
$( $field: self.$field.cmp(&rhs.$field) ), +
}
}
pub fn lt(self, rhs: Self) -> $struct<bool> {
$struct{
$( $field: self.$field.lt(&rhs.$field) ), +
}
}
pub fn gt(self, rhs: Self) -> $struct<bool> {
$struct{
$( $field: self.$field.gt(&rhs.$field) ), +
}
}
pub fn ge(self, rhs: Self) -> $struct<bool> {
$struct{
$( $field: self.$field.ge(&rhs.$field) ), +
}
}
pub fn le(self, rhs: Self) -> $struct<bool> {
$struct{
$( $field: self.$field.le(&rhs.$field) ), +
}
}
}
impl $struct<bool>{
pub fn all(&self)->bool{
const ALL:[bool;$size]=[true;$size];
core::matches!(self.to_array(),ALL)
}
pub fn any(&self)->bool{
$( self.$field )|| +
}
}
impl<T: core::ops::Neg<Output = T>> core::ops::Neg for $struct<T> {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
$( $field: -self.$field ), +
}
}
}
// Impl arithmetic pperators
$crate::impl_vector_operator!( $struct { $($field), + }, AddAssign, add_assign );
$crate::impl_vector_operator!( $struct { $($field), + }, Add, add, Self );
$crate::impl_vector_operator!( $struct { $($field), + }, SubAssign, sub_assign );
$crate::impl_vector_operator!( $struct { $($field), + }, Sub, sub, Self );
$crate::impl_vector_operator!( $struct { $($field), + }, MulAssign, mul_assign );
$crate::impl_vector_operator!( $struct { $($field), + }, Mul, mul, Self );
$crate::impl_vector_operator!( $struct { $($field), + }, DivAssign, div_assign );
$crate::impl_vector_operator!( $struct { $($field), + }, Div, div, Self );
$crate::impl_vector_operator!( $struct { $($field), + }, RemAssign, rem_assign );
$crate::impl_vector_operator!( $struct { $($field), + }, Rem, rem, Self );
// Impl bitwise operators
$crate::impl_vector_operator!( $struct { $($field), + }, BitAndAssign, bitand_assign );
$crate::impl_vector_operator!( $struct { $($field), + }, BitAnd, bitand, Self );
$crate::impl_vector_operator!( $struct { $($field), + }, BitOrAssign, bitor_assign );
$crate::impl_vector_operator!( $struct { $($field), + }, BitOr, bitor, Self );
$crate::impl_vector_operator!( $struct { $($field), + }, BitXorAssign, bitxor_assign );
$crate::impl_vector_operator!( $struct { $($field), + }, BitXor, bitxor, Self );
// Impl floating-point based methods
#[cfg(feature="fixed_wide")]
$crate::impl_wide_vector_operations!( $struct { $($field), + }, $size );
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_extend {
( $struct: ident { $($field: ident), + }, $struct_extended: ident, $field_extended: ident ) => {
impl<T> $struct<T> {
#[inline(always)]
pub fn extend(self,value:T) -> $struct_extended<T> {
$struct_extended {
$( $field:self.$field, ) +
$field_extended:value
}
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_operator {
( $struct: ident { $($field: ident), + }, $trait: ident, $method: ident, $output: ty ) => {
impl<T:core::ops::$trait<Output=T>> core::ops::$trait for $struct<T> {
type Output = $output;
fn $method(self, other: Self) -> Self::Output {
Self {
$( $field: self.$field.$method(other.$field) ), +
}
}
}
impl<T:core::ops::$trait<Output=T>+Copy> core::ops::$trait<T> for $struct<T>{
type Output = $output;
fn $method(self, other: T) -> Self::Output {
$struct {
$( $field: self.$field.$method(other) ), +
}
}
}
};
( $struct: ident { $($field: ident), + }, $trait: ident, $method: ident ) => {
impl<T: core::ops::$trait> core::ops::$trait for $struct<T> {
fn $method(&mut self, other: Self) {
$( self.$field.$method(other.$field) ); +
}
}
impl<T: core::ops::$trait + Copy> core::ops::$trait<T> for $struct<T> {
fn $method(&mut self, other: T) {
$( self.$field.$method(other) ); +
}
}
};
}

@ -0,0 +1,36 @@
use crate::{Vector2,Vector3,Vector4};
pub struct Matrix2<T> {
pub x_axis: T,
pub y_axis: T,
}
pub struct Matrix3<T> {
pub x_axis: T,
pub y_axis: T,
pub z_axis: T,
}
pub struct Matrix4<T> {
pub x_axis: T,
pub y_axis: T,
pub z_axis: T,
pub w_axis: T,
}
crate::impl_extend!(Matrix2 { x_axis, y_axis }, Matrix3, z_axis);
crate::impl_extend!(Matrix3 { x_axis, y_axis, z_axis }, Matrix4, w_axis);
//TODO: extend vertically
crate::impl_matrices!(
//outer struct and equivalent vector
(
(Matrix2 { x_axis, y_axis }, Vector2 { x, y }, 2),
(Matrix3 { x_axis, y_axis, z_axis }, Vector3 { x, y, z }, 3),
(Matrix4 { x_axis, y_axis, z_axis, w_axis }, Vector4 { x, y, z, w }, 4)
),
//inner struct and equivalent matrix
(
(Vector2 { x, y }, Matrix2 { x_axis, y_axis }, 2),
(Vector3 { x, y, z }, Matrix3 { x_axis, y_axis, z_axis }, 3),
(Vector4 { x, y, z, w }, Matrix4 { x_axis, y_axis, z_axis, w_axis }, 4)
)
);

@ -0,0 +1,63 @@
use crate::{Vector2,Vector3,Vector4,Matrix3,Matrix4};
type Planar64=fixed_wide::types::I32F32;
type Planar64Wide1=fixed_wide::types::I64F64;
//type Planar64Wide2=fixed_wide::types::I128F128;
type Planar64Wide3=fixed_wide::types::I256F256;
#[test]
fn wide_vec3(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v.wide_mul_1_1(v);
let v2=v1.wide_mul_2_2(v1);
let v3=v2.wide_mul_4_4(v2);
assert_eq!(v3,Vector3::from_value(Planar64Wide3::from(3i128.pow(8))));
}
#[test]
fn wide_vec3_dot(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v.wide_mul_1_1(v);
let v2=v1.wide_mul_2_2(v1);
let v3=v2.wide_dot_4_4(v2);
assert_eq!(v3,Planar64Wide3::from(3i128.pow(8)*3));
}
#[test]
fn wide_vec3_length_squared(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v.wide_mul_1_1(v);
let v2=v1.wide_mul_2_2(v1);
let v3=v2.wide_length_squared();
assert_eq!(v3,Planar64Wide3::from(3i128.pow(8)*3));
}
#[test]
fn wide_matrix_dot(){
let lhs=Matrix3::from([
Vector4::from([Planar64::from(1),Planar64::from(2),Planar64::from(3),Planar64::from(4)]),
Vector4::from([Planar64::from(5),Planar64::from(6),Planar64::from(7),Planar64::from(8)]),
Vector4::from([Planar64::from(9),Planar64::from(10),Planar64::from(11),Planar64::from(12)]),
]);
let rhs=Matrix4::from([
Vector2::from([Planar64::from(1),Planar64::from(2)]),
Vector2::from([Planar64::from(3),Planar64::from(4)]),
Vector2::from([Planar64::from(5),Planar64::from(6)]),
Vector2::from([Planar64::from(7),Planar64::from(8)]),
]);
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot=lhs.wide_dot_3x4_4x2_1_1(rhs);
//In[1]:= {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} . {{1, 2}, {3, 4}, {5, 6}, {7, 8}}
//Out[1]= {{50, 60}, {114, 140}, {178, 220}}
assert_eq!(
m_dot,
Matrix3::from([
Vector2::from([Planar64Wide1::from(50),Planar64Wide1::from(60)]),
Vector2::from([Planar64Wide1::from(114),Planar64Wide1::from(140)]),
Vector2::from([Planar64Wide1::from(178),Planar64Wide1::from(220)]),
])
);
}

@ -0,0 +1,5 @@
mod tests;
#[cfg(feature="fixed_wide")]
mod fixed_wide;

@ -0,0 +1,81 @@
// Stolen from https://github.com/c1m50c/fixed-vectors (MIT license)
/// Vector for holding two-dimensional values.
///
/// # Example
///
/// ```
/// use fixed_wide_vectors::Vector2;
///
/// let mut vec2 = Vector2::new(1, 2);
/// vec2 += Vector2::new(1, 2);
///
/// assert_eq!(vec2.x, 2);
/// assert_eq!(vec2.y, 4);
/// ```
pub struct Vector2<T> {
pub x: T,
pub y: T,
}
/// Vector for holding three-dimensional values.
///
/// # Example
///
/// ```
/// use fixed_wide_vectors::Vector3;
///
/// let mut vec3 = Vector3::new(1, 2, 3);
/// vec3 += Vector3::new(1, 2, 3);
///
/// assert_eq!(vec3.x, 2);
/// assert_eq!(vec3.y, 4);
/// assert_eq!(vec3.z, 6);
/// ```
pub struct Vector3<T> {
pub x: T,
pub y: T,
pub z: T,
}
/// Vector for holding four-dimensional values.
///
/// # Example
///
/// ```
/// use fixed_wide_vectors::Vector4;
///
/// let mut vec4 = Vector4::new(1, 2, 3, 4);
/// vec4 += Vector4::new(1, 2, 3, 4);
///
/// assert_eq!(vec4.x, 2);
/// assert_eq!(vec4.y, 4);
/// assert_eq!(vec4.z, 6);
/// assert_eq!(vec4.w, 8);
/// ```
pub struct Vector4<T> {
pub x: T,
pub y: T,
pub z: T,
pub w: T,
}
crate::impl_vector!(Vector2 { x, y }, 2);
crate::impl_vector!(Vector3 { x, y, z }, 3);
crate::impl_vector!(Vector4 { x, y, z, w }, 4);
crate::impl_extend!(Vector2 { x, y }, Vector3, z);
crate::impl_extend!(Vector3 { x, y, z }, Vector4, w);
crate::impl_matrix_inner!((Vector2 { x, y }, Vector2 { x, y }, 2), (Vector2 { x, y }, Vector2 { x, y }, 2) );
crate::impl_matrix_inner!((Vector2 { x, y }, Vector2 { x, y }, 2), (Vector3 { x, y, z }, Vector3 { x, y, z }, 3) );
crate::impl_matrix_inner!((Vector2 { x, y }, Vector2 { x, y }, 2), (Vector4 { x, y, z, w }, Vector4 { x, y, z, w }, 4) );
crate::impl_matrix_inner!((Vector3 { x, y, z }, Vector3 { x, y, z }, 3), (Vector2 { x, y }, Vector2 { x, y }, 2) );
crate::impl_matrix_inner!((Vector3 { x, y, z }, Vector3 { x, y, z }, 3), (Vector3 { x, y, z }, Vector3 { x, y, z }, 3) );
crate::impl_matrix_inner!((Vector3 { x, y, z }, Vector3 { x, y, z }, 3), (Vector4 { x, y, z, w }, Vector4 { x, y, z, w }, 4) );
crate::impl_matrix_inner!((Vector4 { x, y, z, w }, Vector4 { x, y, z, w }, 4), (Vector2 { x, y }, Vector2 { x, y }, 2) );
crate::impl_matrix_inner!((Vector4 { x, y, z, w }, Vector4 { x, y, z, w }, 4), (Vector3 { x, y, z }, Vector3 { x, y, z }, 3) );
crate::impl_matrix_inner!((Vector4 { x, y, z, w }, Vector4 { x, y, z, w }, 4), (Vector4 { x, y, z, w }, Vector4 { x, y, z, w }, 4) );

@ -1,15 +0,0 @@
[package]
name = "linear_ops"
version = "0.1.0"
edition = "2021"
[features]
default=["named-fields"]
named-fields=[]
deferred-division=["dep:ratio_ops"]
[dependencies]
ratio_ops = { path = "../ratio_ops", optional = true }
[dev-dependencies]
fixed_wide = { version = "0.1.0", path = "../fixed_wide", features = ["wide-mul"] }

@ -1,10 +0,0 @@
mod macros;
pub mod types;
pub mod vector;
pub mod matrix;
#[cfg(feature="named-fields")]
mod named;
#[cfg(test)]
mod tests;

@ -1,258 +0,0 @@
#[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;X];Y])->Self{
Self{array}
}
#[inline(always)]
pub fn to_array(self)->[[T;X];Y]{
self.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]
// MatY<VecX>.MatX<VecZ> = MatY<VecZ>
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=rhs.array.map(|axis|axis.into_iter().cycle());
Matrix::new(
self.array.map(|axis|
core::array::from_fn(|_|
// axis dot product with transposed rhs array
axis.iter().zip(
array_of_iterators.iter_mut()
).map(|(&lhs_value,rhs_iter)|
lhs_value*rhs_iter.next().unwrap()
).sum()
)
)
)
}
#[inline]
// MatY<VecX>.VecX = VecY
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,
{
Vector::new(
self.array.map(|axis|
Vector::new(axis).dot(rhs)
)
)
}
}
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;X];Y])
}
}
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 row in &self.array[0..Y]{
core::write!(f,"\n")?;
for elem in &row[0..X-1]{
core::write!(f,"{}, ",elem)?;
}
// assume we will be using matrices of size 1x1 or greater
core::write!(f,"{}",row.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_row(self,value:Vector<$x,T>)->Matrix<$x,{$y+1},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_column(self,value:Vector<$y,T>)->Matrix<{$x+1},$y,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{
unsafe{core::mem::transmute(&self.array)}
}
}
impl<T> core::ops::DerefMut for Matrix<$size_outer,$size_inner,T>{
#[inline]
fn deref_mut(&mut self)->&mut Self::Target{
unsafe{core::mem::transmute(&mut self.array)}
}
}
}
}
#[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],
])
}
}
}
}

@ -1,353 +0,0 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector {
() => {
impl<const N:usize,T> Vector<N,T>{
#[inline(always)]
pub const fn new(array:[T;N])->Self{
Self{array}
}
#[inline(always)]
pub fn to_array(self)->[T;N]{
self.array
}
#[inline]
pub fn map<F,U>(self,f:F)->Vector<N,U>
where
F:Fn(T)->U
{
Vector::new(
self.array.map(f)
)
}
#[inline]
pub fn map_zip<F,U,V>(self,other:Vector<N,U>,f:F)->Vector<N,V>
where
F:Fn((T,U))->V,
{
let mut iter=self.array.into_iter().zip(other.array);
Vector::new(
core::array::from_fn(|_|f(iter.next().unwrap())),
)
}
}
impl<const N:usize,T:Copy> Vector<N,T>{
#[inline(always)]
pub const fn from_value(value:T)->Self{
Self::new([value;N])
}
}
impl<const N:usize,T:Default> Default for Vector<N,T>{
#[inline]
fn default()->Self{
Self::new(
core::array::from_fn(|_|Default::default())
)
}
}
impl<const N:usize,T:core::fmt::Display> core::fmt::Display for Vector<N,T>{
#[inline]
fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{
for elem in &self.array[0..N-1]{
core::write!(f,"{}, ",elem)?;
}
// assume we will be using vectors of length 1 or greater
core::write!(f,"{}",self.array.last().unwrap())
}
}
impl<const N:usize,T:Ord> Vector<N,T>{
#[inline]
pub fn min(self,rhs:Self)->Self{
self.map_zip(rhs,|(a,b)|a.min(b))
}
#[inline]
pub fn max(self,rhs:Self)->Self{
self.map_zip(rhs,|(a,b)|a.max(b))
}
#[inline]
pub fn cmp(self,rhs:Self)->Vector<N,core::cmp::Ordering>{
self.map_zip(rhs,|(a,b)|a.cmp(&b))
}
#[inline]
pub fn lt(self,rhs:Self)->Vector<N,bool>{
self.map_zip(rhs,|(a,b)|a.lt(&b))
}
#[inline]
pub fn gt(self,rhs:Self)->Vector<N,bool>{
self.map_zip(rhs,|(a,b)|a.gt(&b))
}
#[inline]
pub fn ge(self,rhs:Self)->Vector<N,bool>{
self.map_zip(rhs,|(a,b)|a.ge(&b))
}
#[inline]
pub fn le(self,rhs:Self)->Vector<N,bool>{
self.map_zip(rhs,|(a,b)|a.le(&b))
}
}
impl<const N:usize> Vector<N,bool>{
#[inline]
pub fn all(&self)->bool{
self.array==[true;N]
}
#[inline]
pub fn any(&self)->bool{
self.array!=[false;N]
}
}
impl<const N:usize,T:core::ops::Neg<Output=V>,V> core::ops::Neg for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn neg(self)->Self::Output{
Vector::new(
self.array.map(|t|-t)
)
}
}
impl<const N:usize,T> Vector<N,T>
{
#[inline]
pub fn dot<U,V>(self,rhs:Vector<N,U>)->V
where
T:core::ops::Mul<U,Output=V>,
V:core::iter::Sum,
{
self.array.into_iter().zip(rhs.array).map(|(a,b)|a*b).sum()
}
}
impl<const N:usize,T,V> Vector<N,T>
where
T:core::ops::Mul<Output=V>+Copy,
V:core::iter::Sum,
{
#[inline]
pub fn length_squared(self)->V{
self.array.into_iter().map(|t|t*t).sum()
}
}
// Impl arithmetic operators
$crate::impl_vector_assign_operator!(AddAssign, add_assign );
$crate::impl_vector_operator!(Add, add );
$crate::impl_vector_assign_operator!(SubAssign, sub_assign );
$crate::impl_vector_operator!(Sub, sub );
$crate::impl_vector_assign_operator!(RemAssign, rem_assign );
$crate::impl_vector_operator!(Rem, rem );
// mul and div are special, usually you multiply by a scalar
// and implementing both vec*vec and vec*scalar is conflicting implementations Q_Q
$crate::impl_vector_assign_operator_scalar!(MulAssign, mul_assign );
$crate::impl_vector_operator_scalar!(Mul, mul );
$crate::impl_vector_assign_operator_scalar!(DivAssign, div_assign );
#[cfg(not(feature="deferred-division"))]
$crate::impl_vector_operator_scalar!(Div, div );
#[cfg(feature="deferred-division")]
$crate::impl_vector_deferred_division!();
// Impl bitwise operators
$crate::impl_vector_assign_operator!(BitAndAssign, bitand_assign );
$crate::impl_vector_operator!(BitAnd, bitand );
$crate::impl_vector_assign_operator!(BitOrAssign, bitor_assign );
$crate::impl_vector_operator!(BitOr, bitor );
$crate::impl_vector_assign_operator!(BitXorAssign, bitxor_assign );
$crate::impl_vector_operator!(BitXor, bitxor );
// Impl shift operators
$crate::impl_vector_shift_assign_operator!(ShlAssign, shl_assign);
$crate::impl_vector_shift_operator!(Shl, shl);
$crate::impl_vector_shift_assign_operator!(ShrAssign, shr_assign);
$crate::impl_vector_shift_operator!(Shr, shr);
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_deferred_division {
() => {
impl<const N:usize,T:ratio_ops::ratio::Divide<U,Output=V>,U:Copy,V> ratio_ops::ratio::Divide<U> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn divide(self,rhs:U)->Self::Output{
self.map(|t|t.divide(rhs))
}
}
impl<const N:usize,T,U> core::ops::Div<U> for Vector<N,T>{
type Output=ratio_ops::ratio::Ratio<Vector<N,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_vector_operator_scalar {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U,Output=V>,U:Copy,V> core::ops::$trait<U> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn $method(self,rhs:U)->Self::Output{
self.map(|t|t.$method(rhs))
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_operator {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U,Output=V>,U,V> core::ops::$trait<Vector<N,U>> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn $method(self,rhs:Vector<N,U>)->Self::Output{
self.map_zip(rhs,|(a,b)|a.$method(b))
}
}
impl<const N:usize,T:core::ops::$trait<i64,Output=T>> core::ops::$trait<i64> for Vector<N,T>{
type Output=Self;
#[inline]
fn $method(self,rhs:i64)->Self::Output{
self.map(|t|t.$method(rhs))
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_assign_operator_scalar {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U>,U:Copy> core::ops::$trait<U> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:U){
self.array.iter_mut()
.for_each(|t|t.$method(rhs))
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_assign_operator {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U>,U> core::ops::$trait<Vector<N,U>> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:Vector<N,U>){
self.array.iter_mut().zip(rhs.array)
.for_each(|(a,b)|a.$method(b))
}
}
impl<const N:usize,T:core::ops::$trait<i64>> core::ops::$trait<i64> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:i64){
self.array.iter_mut()
.for_each(|t|t.$method(rhs))
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_shift_operator {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U,Output=V>,U,V> core::ops::$trait<Vector<N,U>> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn $method(self,rhs:Vector<N,U>)->Self::Output{
self.map_zip(rhs,|(a,b)|a.$method(b))
}
}
impl<const N:usize,T:core::ops::$trait<u32,Output=V>,V> core::ops::$trait<u32> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn $method(self,rhs:u32)->Self::Output{
self.map(|t|t.$method(rhs))
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_shift_assign_operator {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U>,U> core::ops::$trait<Vector<N,U>> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:Vector<N,U>){
self.array.iter_mut().zip(rhs.array)
.for_each(|(a,b)|a.$method(b))
}
}
impl<const N:usize,T:core::ops::$trait<u32>> core::ops::$trait<u32> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:u32){
self.array.iter_mut()
.for_each(|t|t.$method(rhs))
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_extend {
( $size: expr ) => {
impl<T> Vector<$size,T>{
#[inline]
pub fn extend(self,value:T)->Vector<{$size+1},T>{
let mut iter=self.array.into_iter().chain(core::iter::once(value));
Vector::new(
core::array::from_fn(|_|iter.next().unwrap()),
)
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_named_fields {
( $struct:ident, $size: expr ) => {
impl<T> core::ops::Deref for Vector<$size,T>{
type Target=$struct<T>;
#[inline]
fn deref(&self)->&Self::Target{
unsafe{core::mem::transmute(&self.array)}
}
}
impl<T> core::ops::DerefMut for Vector<$size,T>{
#[inline]
fn deref_mut(&mut self)->&mut Self::Target{
unsafe{core::mem::transmute(&mut self.array)}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_3 {
()=>{
impl<T> Vector<3,T>
{
#[inline]
pub fn cross<U,V>(self,rhs:Vector<3,U>)->Vector<3,<V as core::ops::Sub>::Output>
where
T:core::ops::Mul<U,Output=V>+Copy,
U:Copy,
V:core::ops::Sub,
{
Vector::new([
self.y*rhs.z-self.z*rhs.y,
self.z*rhs.x-self.x*rhs.z,
self.x*rhs.y-self.y*rhs.x,
])
}
}
}
}

@ -1,17 +0,0 @@
use crate::vector::Vector;
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
pub struct Matrix<const X:usize,const Y:usize,T>{
pub(crate) array:[[T;X];Y],
}
crate::impl_matrix!();
crate::impl_matrix_extend!(2,2);
crate::impl_matrix_extend!(2,3);
crate::impl_matrix_extend!(3,2);
crate::impl_matrix_extend!(3,3);
//Special case 3x3 matrix operations because I cba to write macros for the arbitrary cases
#[cfg(feature="named-fields")]
crate::impl_matrix_3x3!();

@ -1,59 +0,0 @@
use crate::vector::Vector;
use crate::matrix::Matrix;
#[repr(C)]
pub struct Vector2<T> {
pub x: T,
pub y: T,
}
#[repr(C)]
pub struct Vector3<T> {
pub x: T,
pub y: T,
pub z: T,
}
#[repr(C)]
pub struct Vector4<T> {
pub x: T,
pub y: T,
pub z: T,
pub w: T,
}
crate::impl_vector_named_fields!(Vector2, 2);
crate::impl_vector_named_fields!(Vector3, 3);
crate::impl_vector_named_fields!(Vector4, 4);
#[repr(C)]
pub struct Matrix2<T> {
pub x_axis: T,
pub y_axis: T,
}
#[repr(C)]
pub struct Matrix3<T> {
pub x_axis: T,
pub y_axis: T,
pub z_axis: T,
}
#[repr(C)]
pub struct Matrix4<T> {
pub x_axis: T,
pub y_axis: T,
pub z_axis: T,
pub w_axis: T,
}
crate::impl_matrix_named_fields!(
//outer struct
(
(Matrix2, 2),
(Matrix3, 3),
(Matrix4, 4)
),
//inner struct
(
(2),
(3),
(4)
)
);

@ -1,96 +0,0 @@
use crate::types::{Matrix3,Matrix2x3,Matrix4x3,Matrix2x4,Vector3};
type Planar64=fixed_wide::types::I32F32;
type Planar64Wide1=fixed_wide::types::I64F64;
//type Planar64Wide2=fixed_wide::types::I128F128;
type Planar64Wide3=fixed_wide::types::I256F256;
#[test]
fn wide_vec3(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v*v.x;
let v2=v1*v1.y;
let v3=v2*v2.z;
assert_eq!(v3.array,Vector3::from_value(Planar64Wide3::from(3i128.pow(8))).array);
}
#[test]
fn wide_vec3_dot(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v*v.x;
let v2=v1*v1.y;
let v3=v2.dot(v2);
assert_eq!(v3,Planar64Wide3::from(3i128.pow(8)*3));
}
#[test]
fn wide_vec3_length_squared(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v*v.x;
let v2=v1*v1.y;
let v3=v2.length_squared();
assert_eq!(v3,Planar64Wide3::from(3i128.pow(8)*3));
}
#[test]
fn wide_matrix_dot(){
let lhs=Matrix4x3::new([
[Planar64::from(1),Planar64::from(2),Planar64::from(3),Planar64::from(4)],
[Planar64::from(5),Planar64::from(6),Planar64::from(7),Planar64::from(8)],
[Planar64::from(9),Planar64::from(10),Planar64::from(11),Planar64::from(12)],
]);
let rhs=Matrix2x4::new([
[Planar64::from(1),Planar64::from(2)],
[Planar64::from(3),Planar64::from(4)],
[Planar64::from(5),Planar64::from(6)],
[Planar64::from(7),Planar64::from(8)],
]);
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot=lhs*rhs;
//In[1]:= {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} . {{1, 2}, {3, 4}, {5, 6}, {7, 8}}
//Out[1]= {{50, 60}, {114, 140}, {178, 220}}
assert_eq!(
m_dot.array,
Matrix2x3::new([
[Planar64Wide1::from(50),Planar64Wide1::from(60)],
[Planar64Wide1::from(114),Planar64Wide1::from(140)],
[Planar64Wide1::from(178),Planar64Wide1::from(220)],
]).array
);
}
#[test]
#[cfg(feature="named-fields")]
fn wide_matrix_det(){
let m=Matrix3::new([
[Planar64::from(1),Planar64::from(2),Planar64::from(3)],
[Planar64::from(4),Planar64::from(5),Planar64::from(7)],
[Planar64::from(6),Planar64::from(8),Planar64::from(9)],
]);
// In[2]:= Det[{{1, 2, 3}, {4, 5, 7}, {6, 8, 9}}]
// Out[2]= 7
assert_eq!(m.det(),fixed_wide::fixed::Fixed::<3,96>::from(7));
}
#[test]
#[cfg(feature="named-fields")]
fn wide_matrix_adjugate(){
let m=Matrix3::new([
[Planar64::from(1),Planar64::from(2),Planar64::from(3)],
[Planar64::from(4),Planar64::from(5),Planar64::from(7)],
[Planar64::from(6),Planar64::from(8),Planar64::from(9)],
]);
// In[6]:= Adjugate[{{1, 2, 3}, {4, 5, 7}, {6, 8, 9}}]
// Out[6]= {{-11, 6, -1}, {6, -9, 5}, {2, 4, -3}}
assert_eq!(
m.adjugate().array,
Matrix3::new([
[Planar64Wide1::from(-11),Planar64Wide1::from(6),Planar64Wide1::from(-1)],
[Planar64Wide1::from(6),Planar64Wide1::from(-9),Planar64Wide1::from(5)],
[Planar64Wide1::from(2),Planar64Wide1::from(4),Planar64Wide1::from(-3)],
]).array
);
}

@ -1,6 +0,0 @@
mod tests;
#[cfg(feature="named-fields")]
mod named;
mod fixed_wide;

@ -1,30 +0,0 @@
use crate::types::{Vector3,Matrix3};
#[test]
fn test_vector(){
let mut v=Vector3::new([1,2,3]);
assert_eq!(v.x,1);
assert_eq!(v.y,2);
assert_eq!(v.z,3);
v.x=5;
assert_eq!(v.x,5);
v.y*=v.x;
assert_eq!(v.y,10);
}
#[test]
fn test_matrix(){
let mut v=Matrix3::from_value(2);
assert_eq!(v.x_axis.x,2);
assert_eq!(v.y_axis.y,2);
assert_eq!(v.z_axis.z,2);
v.x_axis.x=5;
assert_eq!(v.x_axis.x,5);
v.y_axis.z*=v.x_axis.x;
assert_eq!(v.y_axis.z,10);
}

@ -1,59 +0,0 @@
use crate::types::{Vector2,Vector3,Matrix4x3,Matrix2x4,Matrix2x3,Matrix3x2};
#[test]
fn test_bool(){
assert_eq!(Vector3::new([false,false,false]).any(),false);
assert_eq!(Vector3::new([false,false,true]).any(),true);
assert_eq!(Vector3::new([false,false,true]).all(),false);
assert_eq!(Vector3::new([true,true,true]).all(),true);
}
#[test]
fn test_length_squared(){
assert_eq!(Vector3::new([1,2,3]).length_squared(),14);
}
#[test]
fn test_arithmetic(){
let a=Vector3::new([1,2,3]);
assert_eq!((a+a*2).array,Vector3::new([1*3,2*3,3*3]).array);
}
#[test]
fn matrix_transform_vector(){
let m=Matrix3x2::new([
[1,2,3],
[4,5,6],
]);
let v=Vector3::new([1,2,3]);
let transformed=m*v;
assert_eq!(transformed.array,Vector2::new([14,32]).array);
}
#[test]
fn matrix_dot(){
let rhs=Matrix2x4::new([
[ 1.0, 2.0],
[ 3.0, 4.0],
[ 5.0, 6.0],
[ 7.0, 8.0],
]); // | | |
let lhs=Matrix4x3::new([ // | | |
[1.0, 2.0, 3.0, 4.0],// [ 50.0, 60.0],
[5.0, 6.0, 7.0, 8.0],// [114.0,140.0],
[9.0,10.0,11.0,12.0],// [178.0,220.0],
]);
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot=lhs*rhs;
//In[1]:= {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} . {{1, 2}, {3, 4}, {5, 6}, {7, 8}}
//Out[1]= {{50, 60}, {114, 140}, {178, 220}}
assert_eq!(
m_dot.array,
Matrix2x3::new([
[50.0,60.0],
[114.0,140.0],
[178.0,220.0],
]).array
);
}

@ -1,18 +0,0 @@
use crate::vector::Vector;
use crate::matrix::Matrix;
pub type Vector2<T>=Vector<2,T>;
pub type Vector3<T>=Vector<3,T>;
pub type Vector4<T>=Vector<4,T>;
pub type Matrix2<T>=Matrix<2,2,T>;
pub type Matrix2x3<T>=Matrix<2,3,T>;
pub type Matrix2x4<T>=Matrix<2,4,T>;
pub type Matrix3x2<T>=Matrix<3,2,T>;
pub type Matrix3<T>=Matrix<3,3,T>;
pub type Matrix3x4<T>=Matrix<3,4,T>;
pub type Matrix4x2<T>=Matrix<4,2,T>;
pub type Matrix4x3<T>=Matrix<4,3,T>;
pub type Matrix4<T>=Matrix<4,4,T>;

@ -1,19 +0,0 @@
/// An array-backed vector type. Named fields are made accessible via the Deref/DerefMut traits which are implmented for 2-4 dimensions.
/// let mut v = Vector::new([1.0,2.0,3.0]);
/// v.x += v.z;
/// println!("v.x={}",v.x);
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
pub struct Vector<const N:usize,T>{
pub(crate) array:[T;N],
}
crate::impl_vector!();
// Needs const generics for generic case
crate::impl_vector_extend!(2);
crate::impl_vector_extend!(3);
//cross product
#[cfg(feature="named-fields")]
crate::impl_vector_3!();

@ -1 +0,0 @@
/target

7
ratio_ops/Cargo.lock generated

@ -1,7 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ratio_ops"
version = "0.1.0"

@ -1,6 +0,0 @@
[package]
name = "ratio_ops"
version = "0.1.0"
edition = "2021"
[dependencies]

@ -1 +0,0 @@
pub mod ratio;

@ -1,177 +0,0 @@
#[derive(Clone,Copy,Debug,Hash)]
pub struct Ratio<Num,Den>{
pub num:Num,
pub den:Den,
}
impl<Num,Den> Ratio<Num,Den>{
#[inline(always)]
pub const fn new(num:Num,den:Den)->Self{
Self{num,den}
}
}
/// The actual divide implementation, Div is replaced with a Ratio constructor
pub trait Divide<Rhs=Self>{
type Output;
fn divide(self,rhs:Rhs)->Self::Output;
}
impl<Num,Den> Ratio<Num,Den>
where
Num:Divide<Den>,
{
#[inline]
pub fn divide(self)-><Num as Divide<Den>>::Output{
self.num.divide(self.den)
}
}
//take care to use the ratio methods to avoid nested ratios
impl<LhsNum,LhsDen> Ratio<LhsNum,LhsDen>{
#[inline]
pub fn mul_ratio<RhsNum,RhsDen>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsNum as core::ops::Mul<RhsNum>>::Output,<LhsDen as core::ops::Mul<RhsDen>>::Output>
where
LhsNum:core::ops::Mul<RhsNum>,
LhsDen:core::ops::Mul<RhsDen>,
{
Ratio::new(self.num*rhs.num,self.den*rhs.den)
}
#[inline]
pub fn div_ratio<RhsNum,RhsDen>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsNum as core::ops::Mul<RhsDen>>::Output,<LhsDen as core::ops::Mul<RhsNum>>::Output>
where
LhsNum:core::ops::Mul<RhsDen>,
LhsDen:core::ops::Mul<RhsNum>,
{
Ratio::new(self.num*rhs.den,self.den*rhs.num)
}
}
macro_rules! impl_ratio_method {
($trait:ident, $method:ident, $ratio_method:ident) => {
impl<LhsNum,LhsDen> Ratio<LhsNum,LhsDen>{
#[inline]
pub fn $ratio_method<RhsNum,RhsDen,LhsCrossMul,RhsCrossMul>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsCrossMul as core::ops::$trait<RhsCrossMul>>::Output,<LhsDen as core::ops::Mul<RhsDen>>::Output>
where
LhsNum:core::ops::Mul<RhsDen,Output=LhsCrossMul>,
LhsDen:core::ops::Mul<RhsNum,Output=RhsCrossMul>,
LhsDen:core::ops::Mul<RhsDen>,
LhsDen:Copy,
RhsDen:Copy,
LhsCrossMul:core::ops::$trait<RhsCrossMul>,
{
Ratio::new((self.num*rhs.den).$method(self.den*rhs.num),self.den*rhs.den)
}
}
};
}
impl_ratio_method!(Add,add,add_ratio);
impl_ratio_method!(Sub,sub,sub_ratio);
impl_ratio_method!(Rem,rem,rem_ratio);
/* generic rhs mul is not possible!
impl<Lhs,RhsNum,RhsDen> core::ops::Mul<Ratio<RhsNum,RhsDen>> for Lhs
where
Lhs:core::ops::Mul<RhsNum>,
{
type Output=Ratio<<Lhs as core::ops::Mul<RhsNum>>::Output,RhsDen>;
#[inline]
fn mul(self,rhs:Ratio<RhsNum,RhsDen>)->Self::Output{
Ratio::new(self*rhs.num,rhs.den)
}
}
*/
//operators
impl<LhsNum,LhsDen> core::ops::Neg for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::Neg,
{
type Output=Ratio<<LhsNum as core::ops::Neg>::Output,LhsDen>;
#[inline]
fn neg(self)->Self::Output{
Ratio::new(-self.num,self.den)
}
}
impl<LhsNum,LhsDen,Rhs> core::ops::Mul<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::Mul<Rhs>,
{
type Output=Ratio<<LhsNum as core::ops::Mul<Rhs>>::Output,LhsDen>;
#[inline]
fn mul(self,rhs:Rhs)->Self::Output{
Ratio::new(self.num*rhs,self.den)
}
}
impl<LhsNum,LhsDen,Rhs> core::ops::Div<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsDen:core::ops::Mul<Rhs>,
{
type Output=Ratio<LhsNum,<LhsDen as core::ops::Mul<Rhs>>::Output>;
#[inline]
fn div(self,rhs:Rhs)->Self::Output{
Ratio::new(self.num,self.den*rhs)
}
}
macro_rules! impl_ratio_operator {
($trait:ident, $method:ident) => {
impl<LhsNum,LhsDen,Rhs,Intermediate> core::ops::$trait<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::$trait<Intermediate>,
LhsDen:Copy,
Rhs:core::ops::Mul<LhsDen,Output=Intermediate>,
{
type Output=Ratio<<LhsNum as core::ops::$trait<Intermediate>>::Output,LhsDen>;
#[inline]
fn $method(self,rhs:Rhs)->Self::Output{
Ratio::new(self.num.$method(rhs*self.den),self.den)
}
}
};
}
impl_ratio_operator!(Add,add);
impl_ratio_operator!(Sub,sub);
impl_ratio_operator!(Rem,rem);
//assign operators
impl<LhsNum,LhsDen,Rhs> core::ops::MulAssign<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::MulAssign<Rhs>,
{
#[inline]
fn mul_assign(&mut self,rhs:Rhs){
self.num*=rhs;
}
}
impl<LhsNum,LhsDen,Rhs> core::ops::DivAssign<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsDen:core::ops::MulAssign<Rhs>,
{
#[inline]
fn div_assign(&mut self,rhs:Rhs){
self.den*=rhs;
}
}
macro_rules! impl_ratio_assign_operator {
($trait:ident, $method:ident) => {
impl<LhsNum,LhsDen,Rhs> core::ops::$trait<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::$trait,
LhsDen:Copy,
Rhs:core::ops::Mul<LhsDen,Output=LhsNum>,
{
#[inline]
fn $method(&mut self,rhs:Rhs){
self.num.$method(rhs*self.den)
}
}
};
}
impl_ratio_assign_operator!(AddAssign,add_assign);
impl_ratio_assign_operator!(SubAssign,sub_assign);
impl_ratio_assign_operator!(RemAssign,rem_assign);