7 Commits

Author SHA1 Message Date
14cec421e0 fix test 2024-09-09 15:33:10 -07:00
fb9228b7fd silence warning 2024-09-09 15:33:07 -07:00
3f0900b0ec ratio stuff 2024-09-09 15:25:01 -07:00
658c73a033 div op 2024-09-09 15:25:01 -07:00
b98eba27e5 wide operators 2024-09-09 15:25:01 -07:00
503f5b6d22 comment impl zones 2024-09-09 15:25:01 -07:00
f22cd653df wide-mul is a crate feature
here goes another bottom-up rewrite
2024-09-09 15:25:01 -07:00
37 changed files with 515 additions and 1441 deletions

View File

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

View File

@@ -1,4 +0,0 @@
/// This type represents an absolute value, such as a coordinate for a world position.
/// This is called a euclidean point in Geometric Algebra.
/// The most appropriate type here is a fixed-point value.
pub struct Absolute<T>(T);

View File

@@ -1,2 +0,0 @@
pub mod abs;
pub mod rel;

View File

@@ -1,8 +0,0 @@
/// This type represents the difference between two absolute values.
/// This is called an ideal point in Geometric Algebra.
/// After you get your time delta or position delta,
/// implement your number-crunching logic using this type.
/// Once the calculation is complete, it can be added to an absolute value
/// with the effect of offsetting the value to a new absolute value.
/// The most appropriate type here is a floating point value.
pub struct Relative<T>(T);

9
fixed_wide/Cargo.lock generated
View File

@@ -10,9 +10,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "bnum"
version = "0.12.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50202def95bf36cb7d1d7a7962cea1c36a3f8ad42425e5d2b71d7acb8041b5b8"
checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790"
[[package]]
name = "fixed_wide"
@@ -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"

View File

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

View File

@@ -1,6 +1,6 @@
use bnum::{BInt,cast::As};
#[derive(Clone,Copy,Debug,Default,Hash)]
#[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)
@@ -33,88 +33,33 @@ 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)))
}
#[inline]
pub const fn is_zero(self)->bool{
self.bits.is_zero()
}
#[inline]
pub const fn is_negative(self)->bool{
self.bits.is_negative()
}
#[inline]
pub const fn is_positive(self)->bool{
self.bits.is_positive()
}
#[inline]
pub const fn abs(self)->Self{
Self::from_bits(self.bits.abs())
}
}
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)))
}
#[inline]
pub const fn to_raw(self)->i64{
self.to_bits().to_bits().digits()[0] as i64
}
}
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)
}
@@ -122,13 +67,11 @@ 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{
@@ -138,65 +81,6 @@ impl<const N:usize,const F:usize> std::iter::Sum for Fixed<N,F>{
}
}
const fn signed_shift(lhs:u64,rhs:i32)->u64{
if rhs.is_negative(){
lhs>>-rhs
}else{
lhs<<rhs
}
}
macro_rules! impl_into_float {
( $output: ty, $unsigned:ty, $exponent_bits:expr, $mantissa_bits:expr ) => {
impl<const N:usize,const F:usize> Into<$output> for Fixed<N,F>{
#[inline]
fn into(self)->$output{
const DIGIT_SHIFT:u32=6;//Log2[64]
// SBBB BBBB
// 1001 1110 0000 0000
let sign=if self.bits.is_negative(){(1 as $unsigned)<<(<$unsigned>::BITS-1)}else{0};
let unsigned=self.bits.unsigned_abs();
let most_significant_bit=unsigned.bits();
let exp=if unsigned.is_zero(){
0
}else{
let msb=most_significant_bit as $unsigned;
let _127=((1 as $unsigned)<<($exponent_bits-1))-1;
let msb_offset=msb+_127-1-F as $unsigned;
msb_offset<<($mantissa_bits-1)
};
let digits=unsigned.digits();
let digit_index=most_significant_bit>>DIGIT_SHIFT;
let digit=digits[digit_index as usize];
//How many bits does the mantissa take from this digit
let take_bits=most_significant_bit-(digit_index<<DIGIT_SHIFT);
let rest_of_mantissa=$mantissa_bits as i32-(take_bits as i32);
let mut unmasked_mant=signed_shift(digit,rest_of_mantissa) as $unsigned;
if 0<rest_of_mantissa&&digit_index!=0{
//take the next digit down and shove some of its bits onto the bottom of the mantissa
let digit=digits[digit_index as usize-1];
let take_bits=most_significant_bit-((digit_index-1)<<DIGIT_SHIFT);
let rest_of_mantissa=$mantissa_bits as i32-(take_bits as i32);
let unmasked_mant2=signed_shift(digit,rest_of_mantissa) as $unsigned;
unmasked_mant|=unmasked_mant2;
}
let mant=unmasked_mant&((1 as $unsigned)<<($mantissa_bits-1))-1;
let bits=sign|exp|mant;
<$output>::from_bits(bits)
}
}
}
}
impl_into_float!(f32,u32,8,24);
impl_into_float!(f64,u64,11,53);
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>{
@@ -207,7 +91,6 @@ macro_rules! impl_additive_operator {
}
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)
}
@@ -217,7 +100,6 @@ 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)))
}
@@ -227,7 +109,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);
}
@@ -236,7 +117,6 @@ 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));
}
@@ -268,7 +148,6 @@ macro_rules! impl_multiplicative_operator_not_const_generic {
( ($struct: ident, $trait: ident, $method: ident, $output: ty ), $width:expr ) => {
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)
@@ -280,7 +159,6 @@ macro_rules! impl_multiplicative_operator_not_const_generic {
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);
@@ -295,27 +173,15 @@ macro_rules! impl_multiply_operator_not_const_generic {
impl<const F:usize> $struct<$width,F>{
paste::item!{
#[inline]
pub fn [<fixed_ $method>](self, rhs: Self) -> Self {
let (low,high)=self.bits.unsigned_abs().widening_mul(rhs.bits.unsigned_abs());
let out:BInt::<{$width*2}>=unsafe{core::mem::transmute([low,high])};
if self.is_negative()==rhs.is_negative(){
Self::from_bits(out.shr(F as u32).as_())
}else{
-Self::from_bits(out.shr(F as u32).as_())
}
pub fn [<fixed_ $method>](self, other: Self) -> Self {
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_())
}
}
}
#[cfg(not(feature="wide-mul"))]
impl_multiplicative_operator_not_const_generic!(($struct, $trait, $method, $output ), $width);
#[cfg(feature="deferred-division")]
impl ratio_ops::ratio::Divide<i64> for Fixed<$width,{$width*32}>{
type Output=Self;
#[inline]
fn divide(self, other: i64)->Self::Output{
Self::from_bits(self.bits/BInt::from(other))
}
}
}
}
macro_rules! impl_divide_operator_not_const_generic {
@@ -331,18 +197,8 @@ macro_rules! impl_divide_operator_not_const_generic {
}
}
}
#[cfg(all(not(feature="wide-mul"),not(feature="deferred-division")))]
#[cfg(all(not(feature="wide-mul"),not(feature="ratio")))]
impl_multiplicative_operator_not_const_generic!(($struct, $trait, $method, $output ), $width);
#[cfg(all(not(feature="wide-mul"),feature="deferred-division"))]
impl<const F:usize> ratio_ops::ratio::Divide for $struct<$width,F>{
type Output = $output;
#[inline]
fn divide(self, other: Self) -> Self::Output {
paste::item!{
self.[<fixed_ $method>](other)
}
}
}
};
}
@@ -353,7 +209,7 @@ macro_rules! impl_multiplicative_operator {
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)))
}
@@ -366,7 +222,6 @@ macro_rules! impl_multiplicative_assign_operator {
where
BInt::<N>:From<U>+core::ops::$trait,
{
#[inline]
fn $method(&mut self, other: U) {
self.bits.$method(BInt::<N>::from(other));
}
@@ -400,25 +255,21 @@ 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")]
#[cfg(feature="ratio")]
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]
type Output=crate::ratio::Ratio<Fixed<LHS_N,LHS_F>,Fixed<RHS_N,RHS_F>>;
fn div(self, other: Fixed<RHS_N,RHS_F>)->Self::Output{
ratio_ops::ratio::Ratio::new(self,other)
}
}
#[cfg(feature="deferred-division")]
impl<const N:usize,const F:usize> ratio_ops::ratio::Parity for Fixed<N,F>{
fn parity(&self)->bool{
self.is_negative()
crate::ratio::Ratio::new(self,other)
}
}
// wide operators. The result width is the sum of the input widths, i.e. none of the multiplication
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))
}
@@ -428,7 +279,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);
}
@@ -440,40 +290,25 @@ 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
#[allow(unused_macros)]
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"))]
#[cfg(not(feature="ratio"))]
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)
}
}
}
#[cfg(feature="deferred-division")]
impl ratio_ops::ratio::Divide<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
type Output=Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>;
#[inline]
fn divide(self, other: Fixed<$rhs,{$rhs*32}>)->Self::Output{
paste::item!{
self.[<wide_div_ $lhs _ $rhs>](other)
}
}
}
}
}
@@ -488,7 +323,6 @@ macro_rules! impl_wide_not_const_generic{
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}>>();
@@ -497,7 +331,6 @@ macro_rules! impl_wide_not_const_generic{
/// 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);
@@ -510,54 +343,18 @@ macro_rules! impl_wide_not_const_generic{
impl_wide_operators!($lhs,$rhs);
};
}
macro_rules! impl_wide_same_size_not_const_generic{
(
(),
$width:expr
)=>{
impl Fixed<$width,{$width*32}>
{
paste::item!{
#[inline]
pub fn [<wide_mul_ $width _ $width>](self,rhs:Fixed<$width,{$width*32}>)->Fixed<{$width*2},{$width*2*32}>{
let (low,high)=self.bits.unsigned_abs().widening_mul(rhs.bits.unsigned_abs());
let out:BInt::<{$width*2}>=unsafe{core::mem::transmute([low,high])};
if self.is_negative()==rhs.is_negative(){
Fixed::from_bits(out)
}else{
// Normal neg is the cheapest negation operation
// And the inputs cannot reach the point where it matters
Fixed::from_bits(out.neg())
}
}
/// 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_ $width _ $width>](self,rhs:Fixed<$width,{$width*32}>)->Fixed<{$width*2},{$width*2*32}>{
// (lhs/2^LHS_FRAC)/(rhs/2^RHS_FRAC)
let lhs=self.bits.as_::<BInt<{$width*2}>>().shl($width*64);
let rhs=rhs.bits.as_::<BInt<{$width*2}>>();
Fixed::from_bits(lhs/rhs)
}
}
}
#[cfg(feature="wide-mul")]
impl_wide_operators!($width,$width);
};
}
//const generics sidestepped wahoo
macro_repeated!(
impl_wide_not_const_generic,(),
(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), (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), (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), (5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),
(1,5),(2,5),(3,5),(4,5), (6,5),(7,5),(8,5),(9,5),(10,5),(11,5),
(1,6),(2,6),(3,6),(4,6),(5,6), (7,6),(8,6),(9,6),(10,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7), (8,7),(9,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8), (9,8),
(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),
@@ -566,133 +363,21 @@ macro_repeated!(
(1,14),(2,14),
(1,15)
);
macro_repeated!(
impl_wide_same_size_not_const_generic,(),
1,2,3,4,5,6,7,8
);
pub trait Fix<Out>{
fn fix(self)->Out;
}
macro_rules! impl_fix_rhs_lt_lhs_not_const_generic{
(
(),
($lhs:expr,$rhs:expr)
)=>{
impl Fixed<$lhs,{$lhs*32}>
{
paste::item!{
#[inline]
pub fn [<fix_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{
Fixed::from_bits(bnum::cast::As::as_::<BInt::<$rhs>>(self.bits.shr(($lhs-$rhs)*32)))
}
}
}
impl Fix<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
fn fix(self)->Fixed<$rhs,{$rhs*32}>{
paste::item!{
self.[<fix_ $rhs>]()
}
}
}
impl<const SRC:usize,const F:usize> Fixed<SRC,F>{
pub fn resize_into<const DST:usize>(self)->Fixed<DST,F>{
Fixed::from_bits(self.bits.as_::<BInt<DST>>())
}
}
macro_rules! impl_fix_lhs_lt_rhs_not_const_generic{
(
(),
($lhs:expr,$rhs:expr)
)=>{
impl Fixed<$lhs,{$lhs*32}>
{
paste::item!{
#[inline]
pub fn [<fix_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{
Fixed::from_bits(bnum::cast::As::as_::<BInt::<$rhs>>(self.bits).shl(($rhs-$lhs)*32))
}
}
}
impl Fix<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
fn fix(self)->Fixed<$rhs,{$rhs*32}>{
paste::item!{
self.[<fix_ $rhs>]()
}
}
}
}
}
macro_rules! impl_fix_lhs_eq_rhs_not_const_generic{
(
(),
($lhs:expr,$rhs:expr)
)=>{
impl Fixed<$lhs,{$lhs*32}>
{
paste::item!{
#[inline]
pub fn [<fix_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{
self
}
}
}
impl Fix<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
fn fix(self)->Fixed<$rhs,{$rhs*32}>{
paste::item!{
self.[<fix_ $rhs>]()
}
}
}
}
}
// I LOVE NOT BEING ABLE TO USE CONST GENERICS
macro_repeated!(
impl_fix_rhs_lt_lhs_not_const_generic,(),
(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),(16,1),(17,1),
(3,2),(4,2),(5,2),(6,2),(7,2),(8,2),(9,2),(10,2),(11,2),(12,2),(13,2),(14,2),(15,2),(16,2),
(4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),(14,3),(15,3),(16,3),
(5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),(13,4),(14,4),(15,4),(16,4),
(6,5),(7,5),(8,5),(9,5),(10,5),(11,5),(12,5),(13,5),(14,5),(15,5),(16,5),
(7,6),(8,6),(9,6),(10,6),(11,6),(12,6),(13,6),(14,6),(15,6),(16,6),
(8,7),(9,7),(10,7),(11,7),(12,7),(13,7),(14,7),(15,7),(16,7),
(9,8),(10,8),(11,8),(12,8),(13,8),(14,8),(15,8),(16,8),
(10,9),(11,9),(12,9),(13,9),(14,9),(15,9),(16,9),
(11,10),(12,10),(13,10),(14,10),(15,10),(16,10),
(12,11),(13,11),(14,11),(15,11),(16,11),
(13,12),(14,12),(15,12),(16,12),
(14,13),(15,13),(16,13),
(15,14),(16,14),
(16,15)
);
macro_repeated!(
impl_fix_lhs_lt_rhs_not_const_generic,(),
(1,2),
(1,3),(2,3),
(1,4),(2,4),(3,4),
(1,5),(2,5),(3,5),(4,5),
(1,6),(2,6),(3,6),(4,6),(5,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),
(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),(8,9),
(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),(7,10),(8,10),(9,10),
(1,11),(2,11),(3,11),(4,11),(5,11),(6,11),(7,11),(8,11),(9,11),(10,11),
(1,12),(2,12),(3,12),(4,12),(5,12),(6,12),(7,12),(8,12),(9,12),(10,12),(11,12),
(1,13),(2,13),(3,13),(4,13),(5,13),(6,13),(7,13),(8,13),(9,13),(10,13),(11,13),(12,13),
(1,14),(2,14),(3,14),(4,14),(5,14),(6,14),(7,14),(8,14),(9,14),(10,14),(11,14),(12,14),(13,14),
(1,15),(2,15),(3,15),(4,15),(5,15),(6,15),(7,15),(8,15),(9,15),(10,15),(11,15),(12,15),(13,15),(14,15),
(1,16),(2,16),(3,16),(4,16),(5,16),(6,16),(7,16),(8,16),(9,16),(10,16),(11,16),(12,16),(13,16),(14,16),(15,16)
);
macro_repeated!(
impl_fix_lhs_eq_rhs_not_const_generic,(),
(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10),(11,11),(12,12),(13,13),(14,14),(15,15),(16,16)
);
macro_rules! impl_not_const_generic{
($n:expr,$_2n:expr)=>{
($n:expr)=>{
impl Fixed<{$n*2},{$n*2*32}>{
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:
@@ -704,15 +389,11 @@ macro_rules! impl_not_const_generic{
let max_shift=((used_bits>>1)+($n*32) as i32) as u32;
let mut result=Self::ZERO;
//resize self to match the wide mul output
let wide_self=self.[<fix_ $_2n>]();
//multiply by one to make the types match (hack)
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={
let mut bits=result.to_bits().to_bits();
bits.set_bit(shift,true);
Self::from_bits(BInt::from_bits(bits))
};
let new_result=result|Self::from_bits(BInt::from_bits(bnum::BUint::power_of_two(shift)));
if new_result.[<wide_mul_ $n _ $n>](new_result)<=wide_self{
result=new_result;
}
@@ -720,7 +401,6 @@ macro_rules! impl_not_const_generic{
result
}
}
#[inline]
pub fn sqrt(self)->Self{
if self<Self::ZERO{
panic!("Square root less than zero")
@@ -728,7 +408,6 @@ macro_rules! impl_not_const_generic{
self.sqrt_unchecked()
}
}
#[inline]
pub fn sqrt_checked(self)->Option<Self>{
if self<Self::ZERO{
None
@@ -739,11 +418,11 @@ macro_rules! impl_not_const_generic{
}
}
}
impl_not_const_generic!(1,2);
impl_not_const_generic!(2,4);
impl_not_const_generic!(3,6);
impl_not_const_generic!(4,8);
impl_not_const_generic!(5,10);
impl_not_const_generic!(6,12);
impl_not_const_generic!(7,14);
impl_not_const_generic!(8,16);
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);

View File

@@ -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
View 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}
}
}

View File

@@ -4,43 +4,13 @@ use crate::types::I256F256;
#[test]
fn you_can_add_numbers(){
let a=I256F256::from((3i128*2).pow(4));
assert_eq!(a+a,I256F256::from((3i128*2).pow(4)*2));
}
#[test]
fn to_f32(){
let a=I256F256::from(1)>>2;
let f:f32=a.into();
assert_eq!(f,0.25f32);
let f:f32=(-a).into();
assert_eq!(f,-0.25f32);
let a=I256F256::from(0);
let f:f32=(-a).into();
assert_eq!(f,0f32);
let a=I256F256::from(237946589723468975i64)<<32;
let f:f32=a.into();
assert_eq!(f,237946589723468975f32*2.0f32.powi(32));
}
#[test]
fn to_f64(){
let a=I256F256::from(1)>>2;
let f:f64=a.into();
assert_eq!(f,0.25f64);
let f:f64=(-a).into();
assert_eq!(f,-0.25f64);
let a=I256F256::from(0);
let f:f64=(-a).into();
assert_eq!(f,0f64);
let a=I256F256::from(237946589723468975i64)<<32;
let f:f64=a.into();
assert_eq!(f,237946589723468975f64*2.0f64.powi(32));
assert_eq!(a+a,I256F256::from((3i128*2).pow(4)*2))
}
#[test]
fn you_can_shr_numbers(){
let a=I32F32::from(4);
assert_eq!(a>>1,I32F32::from(2));
assert_eq!(a>>1,I32F32::from(2))
}
#[test]
@@ -82,13 +52,6 @@ fn test_bint(){
assert_eq!(a*2,I32F32::from(2));
}
#[test]
fn test_fix(){
assert_eq!(I32F32::ONE.fix_8(),I256F256::ONE);
assert_eq!(I32F32::ONE,I256F256::ONE.fix_1());
assert_eq!(I32F32::NEG_ONE.fix_8(),I256F256::NEG_ONE);
assert_eq!(I32F32::NEG_ONE,I256F256::NEG_ONE.fix_1());
}
#[test]
fn test_sqrt(){
let a=I32F32::ONE*4;
@@ -139,27 +102,3 @@ fn test_sqrt_max(){
let a=I32F32::MAX;
test_exact(a);
}
#[test]
#[cfg(all(feature="zeroes",not(feature="deferred-division")))]
fn test_zeroes_normal(){
// (x-1)*(x+1)
// x^2-1
let zeroes=I32F32::zeroes2(I32F32::NEG_ONE,I32F32::ZERO,I32F32::ONE);
assert_eq!(zeroes,arrayvec::ArrayVec::from_iter([I32F32::NEG_ONE,I32F32::ONE]));
let zeroes=I32F32::zeroes2(I32F32::NEG_ONE*3,I32F32::ONE*2,I32F32::ONE);
assert_eq!(zeroes,arrayvec::ArrayVec::from_iter([I32F32::NEG_ONE*3,I32F32::ONE]));
}
#[test]
#[cfg(all(feature="zeroes",feature="deferred-division"))]
fn test_zeroes_deferred_division(){
// (x-1)*(x+1)
// x^2-1
let zeroes=I32F32::zeroes2(I32F32::NEG_ONE,I32F32::ZERO,I32F32::ONE);
assert_eq!(
zeroes,
arrayvec::ArrayVec::from_iter([
ratio_ops::ratio::Ratio::new(I32F32::ONE*2,I32F32::NEG_ONE*2),
ratio_ops::ratio::Ratio::new(I32F32::ONE*2,I32F32::ONE*2),
])
);
}

View File

@@ -1,4 +1,5 @@
use crate::fixed::Fixed;
use crate::ratio::Ratio;
use arrayvec::ArrayVec;
use std::cmp::Ordering;
@@ -6,37 +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=>false,
Ordering::Less=>true,
};
let radicand=a1*a1-a2*a0*4;
match radicand.cmp(&<Self as core::ops::Mul>::Output::ZERO){
paste::item!{
let radicand=a1.[<wide_mul_ $n _ $n>](a1)-a2.[<wide_mul_ $n _ $n>](a0)*4;
}
match radicand.cmp(&Fixed::<{$n*2},{$n*2*32}>::ZERO){
Ordering::Greater=>{
paste::item!{
let planar_radicand=radicand.sqrt().[<fix_ $n>]();
}
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)])
}
}
}

View File

@@ -3,26 +3,32 @@
version = 3
[[package]]
name = "bnum"
version = "0.12.0"
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50202def95bf36cb7d1d7a7962cea1c36a3f8ad42425e5d2b71d7acb8041b5b8"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "bnum"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790"
[[package]]
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",
"paste",
"ratio_ops",
]
[[package]]
@@ -30,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"

View File

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

View File

@@ -0,0 +1,212 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_wide_vector_operations_2arg_not_const_generic {
(
(),
($lhs:expr, $rhs:expr)
) => {
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<{$lhs},{$lhs*32}>>{
paste::item!{
#[inline]
pub fn [<wide_mul_ $lhs _ $rhs>](self,rhs:Vector<N,fixed_wide::fixed::Fixed<{$rhs},{$rhs*32}>>)->Vector<N,fixed_wide::fixed::Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>>{
self.map_zip(rhs,|(a,b)|a.[<wide_mul_ $lhs _ $rhs>](b))
}
#[inline]
pub fn [<wide_dot_ $lhs _ $rhs>](self,rhs:Vector<N,fixed_wide::fixed::Fixed<{$rhs},{$rhs*32}>>)->fixed_wide::fixed::Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>{
self.array.into_iter().zip(rhs.array).map(|(a,b)|a.[<wide_mul_ $lhs _ $rhs>](b)).sum()
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_wide_vector_operations_1arg_not_const_generic {
(
(),
$n:expr
) => {
impl<const N:usize> Vector<N,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}>{
self.array.into_iter().map(|t|t.[<wide_mul_ $n _ $n>](t)).sum()
}
}
}
};
}
#[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 {
() => {
$crate::do_macro_8!(impl_wide_vector_operations_1arg_not_const_generic,());
$crate::do_macro_8x8!(impl_wide_vector_operations_2arg_not_const_generic,());
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_3_wide_cross {
(
(),
($lhs:expr, $rhs:expr)
)=>{
impl Vector<3,fixed_wide::fixed::Fixed<{$lhs},{$lhs*32}>>{
paste::item!{
#[inline]
pub fn [<wide_cross_ $lhs _ $rhs>](self,rhs:Vector<3,fixed_wide::fixed::Fixed<{$rhs},{$rhs*32}>>)->Vector<3,fixed_wide::fixed::Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>>{
Vector::new([
self.y.[<wide_mul_ $lhs _ $rhs>](rhs.z)-self.z.[<wide_mul_ $lhs _ $rhs>](rhs.y),
self.z.[<wide_mul_ $lhs _ $rhs>](rhs.x)-self.x.[<wide_mul_ $lhs _ $rhs>](rhs.z),
self.x.[<wide_mul_ $lhs _ $rhs>](rhs.y)-self.y.[<wide_mul_ $lhs _ $rhs>](rhs.x),
])
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_wide_3 {
()=>{
$crate::do_macro_8x8!(impl_vector_3_wide_cross,());
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! do_macro_4_dumb{
(
$macro:ident,
$any:tt
)=>{
$crate::macro_repeated!($macro, $any, (1,2),(2,4),(3,6),(4,8));
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_dot {
(
(),
($lhs: expr, $rhs: expr)
) => {
impl<const X:usize,const Y:usize> Matrix<X,Y,fixed_wide::fixed::Fixed<{$lhs},{$lhs*32}>>{
paste::item!{
#[inline]
pub fn [<wide_dot_ $lhs _ $rhs>]<const Z:usize>(self,rhs:Matrix<Z,X,fixed_wide::fixed::Fixed<{$rhs},{$rhs*32}>>)->Matrix<Z,Y,fixed_wide::fixed::Fixed<{$lhs+$rhs},{($lhs+$rhs)*32}>>{
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.[<wide_mul_ $lhs _ $rhs>](rhs_iter.next().unwrap())
).sum()
)
)
)
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_dot_8x8 {
() => {
$crate::do_macro_8x8!(impl_matrix_wide_dot,());
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_3x3_det_not_const_generic {
(
$n: expr,
$_2n: expr
)=>{
impl Matrix<3,3,fixed_wide::fixed::Fixed<$n,{$n*32}>>{
paste::item!{
pub fn [<wide_det_3x3_ $n>](self)->fixed_wide::fixed::Fixed<{$n*3},{$n*3*32}>{
//[<wide_dot_ $n _ $n*2>] will not compile, so the doubles are hardcoded above
self.x_axis.[<wide_dot_ $n _ $_2n>](self.y_axis.[<wide_cross_ $n _ $n>](self.z_axis))
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_3x3_det_not_const_generic_shim {
(
(),($n: expr,$_2n: expr)
)=>{
$crate::impl_matrix_wide_3x3_det_not_const_generic!($n,$_2n);
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_3x3_adjugate_not_const_generic {
(
(),
$n: expr
)=>{
impl Matrix<3,3,fixed_wide::fixed::Fixed<$n,{$n*32}>>{
paste::item!{
pub fn [<wide_adjugate_3x3_ $n>](self)->Matrix<3,3,fixed_wide::fixed::Fixed<{$n*2},{$n*2*32}>>{
Matrix::new([
[self.y_axis.y.[<wide_mul_ $n _ $n>](self.z_axis.z)-self.y_axis.z.[<wide_mul_ $n _ $n>](self.z_axis.y),self.x_axis.z.[<wide_mul_ $n _ $n>](self.z_axis.y)-self.x_axis.y.[<wide_mul_ $n _ $n>](self.z_axis.z),self.x_axis.y.[<wide_mul_ $n _ $n>](self.y_axis.z)-self.x_axis.z.[<wide_mul_ $n _ $n>](self.y_axis.y)],
[self.y_axis.z.[<wide_mul_ $n _ $n>](self.z_axis.x)-self.y_axis.x.[<wide_mul_ $n _ $n>](self.z_axis.z),self.x_axis.x.[<wide_mul_ $n _ $n>](self.z_axis.z)-self.x_axis.z.[<wide_mul_ $n _ $n>](self.z_axis.x),self.x_axis.z.[<wide_mul_ $n _ $n>](self.y_axis.x)-self.x_axis.x.[<wide_mul_ $n _ $n>](self.y_axis.z)],
[self.y_axis.x.[<wide_mul_ $n _ $n>](self.z_axis.y)-self.y_axis.y.[<wide_mul_ $n _ $n>](self.z_axis.x),self.x_axis.y.[<wide_mul_ $n _ $n>](self.z_axis.x)-self.x_axis.x.[<wide_mul_ $n _ $n>](self.z_axis.y),self.x_axis.x.[<wide_mul_ $n _ $n>](self.y_axis.y)-self.x_axis.y.[<wide_mul_ $n _ $n>](self.y_axis.x)],
])
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_wide_3x3 {
()=>{
$crate::do_macro_4_dumb!(impl_matrix_wide_3x3_det_not_const_generic_shim,());
$crate::do_macro_8!(impl_matrix_wide_3x3_adjugate_not_const_generic,());
}
}

View File

@@ -0,0 +1,155 @@
#[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()
)
)
)
}
}
impl<const X:usize,const Y:usize,T> Matrix<X,Y,T>
where
T:Copy
{
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>{
fn default()->Self{
Self::new(
core::array::from_fn(|_|core::array::from_fn(|_|Default::default()))
)
}
}
#[cfg(feature="fixed_wide")]
$crate::impl_matrix_wide_dot_8x8!();
}
}
#[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>>;
fn deref(&self)->&Self::Target{
unsafe{core::mem::transmute(&self.array)}
}
}
impl<T> core::ops::DerefMut for Matrix<$size_outer,$size_inner,T>{
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 {
()=>{
#[cfg(feature="fixed_wide")]
$crate::impl_matrix_wide_3x3!();
}
}

View File

@@ -1,10 +1,10 @@
#[cfg(feature="fixed_wide")]
pub mod fixed_wide;
pub mod common;
pub mod vector;
pub mod matrix;
#[cfg(feature="fixed-wide")]
pub mod fixed_wide;
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! macro_repeated{

View File

@@ -39,7 +39,6 @@ macro_rules! impl_vector {
}
impl<const N:usize,T:Default> Default for Vector<N,T>{
#[inline]
fn default()->Self{
Self::new(
core::array::from_fn(|_|Default::default())
@@ -47,17 +46,6 @@ macro_rules! impl_vector {
}
}
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{
@@ -102,7 +90,6 @@ macro_rules! impl_vector {
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)
@@ -110,47 +97,18 @@ macro_rules! impl_vector {
}
}
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!(MulAssign, mul_assign );
$crate::impl_vector_operator!(Mul, mul );
$crate::impl_vector_assign_operator!(DivAssign, div_assign );
$crate::impl_vector_operator!(Div, div );
$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 );
@@ -159,48 +117,9 @@ macro_rules! impl_vector {
$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);
// dedicated methods for this type
#[cfg(feature="fixed-wide")]
$crate::impl_fixed_wide_vector!();
}
}
#[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))
}
}
// Impl floating-point based methods
#[cfg(feature="fixed_wide")]
$crate::impl_wide_vector_operations!();
}
}
#[doc(hidden)]
@@ -209,14 +128,12 @@ 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))
}
@@ -225,30 +142,15 @@ macro_rules! impl_vector_operator {
}
#[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))
@@ -256,46 +158,6 @@ macro_rules! impl_vector_assign_operator {
}
}
}
#[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)]
@@ -319,13 +181,11 @@ 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)}
}
@@ -337,21 +197,7 @@ macro_rules! impl_vector_named_fields {
#[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,
])
}
}
#[cfg(feature="fixed_wide")]
$crate::impl_vector_wide_3!();
}
}

View File

@@ -1,8 +1,8 @@
use crate::vector::Vector;
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
pub struct Matrix<const X:usize,const Y:usize,T>{
pub(crate) array:[[T;Y];X],
pub(crate) array:[[T;X];Y],
}
crate::impl_matrix!();
@@ -13,5 +13,4 @@ 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!();

View File

@@ -1,4 +1,4 @@
use crate::types::{Matrix3,Matrix3x2,Matrix3x4,Matrix4x2,Vector3};
use crate::types::{Matrix3,Matrix2x3,Matrix4x3,Matrix2x4,Vector3};
type Planar64=fixed_wide::types::I32F32;
type Planar64Wide1=fixed_wide::types::I64F64;
@@ -8,9 +8,9 @@ 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;
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.array,Vector3::from_value(Planar64Wide3::from(3i128.pow(8))).array);
}
@@ -18,9 +18,9 @@ fn wide_vec3(){
#[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);
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));
}
@@ -28,42 +28,41 @@ fn wide_vec3_dot(){
#[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();
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=Matrix3x4::new([
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)],
]).transpose();
let rhs=Matrix4x2::new([
]);
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)],
]).transpose();
]);
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot=lhs*rhs;
let m_dot=lhs.wide_dot_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.array,
Matrix3x2::new([
Matrix2x3::new([
[Planar64Wide1::from(50),Planar64Wide1::from(60)],
[Planar64Wide1::from(114),Planar64Wide1::from(140)],
[Planar64Wide1::from(178),Planar64Wide1::from(220)],
]).transpose().array
]).array
);
}
#[test]
#[cfg(feature="named-fields")]
fn wide_matrix_det(){
let m=Matrix3::new([
[Planar64::from(1),Planar64::from(2),Planar64::from(3)],
@@ -72,11 +71,10 @@ fn wide_matrix_det(){
]);
// 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));
assert_eq!(m.wide_det_3x3_1(),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)],
@@ -86,7 +84,7 @@ fn wide_matrix_adjugate(){
// 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,
m.wide_adjugate_3x3_1().array,
Matrix3::new([
[Planar64Wide1::from(-11),Planar64Wide1::from(6),Planar64Wide1::from(-1)],
[Planar64Wide1::from(6),Planar64Wide1::from(-9),Planar64Wide1::from(5)],

View File

@@ -3,4 +3,5 @@ mod tests;
#[cfg(feature="named-fields")]
mod named;
#[cfg(feature="fixed_wide")]
mod fixed_wide;

View File

@@ -1,4 +1,4 @@
use crate::types::{Vector2,Vector3,Matrix3x4,Matrix4x2,Matrix3x2,Matrix2x3};
use crate::types::{Vector3,Matrix4x3,Matrix2x4,Matrix2x3};
#[test]
fn test_bool(){
@@ -8,52 +8,36 @@ fn test_bool(){
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=Matrix2x3::new([
[1,2,3],
[4,5,6],
]).transpose();
let v=Vector3::new([1,2,3]);
let transformed=m*v;
assert_eq!(transformed.array,Vector2::new([14,32]).array);
}
#[test]
fn matrix_dot(){
// All this code was written row major and I converted the lib to colum major
let rhs=Matrix4x2::new([
let rhs=Matrix2x4::new([
[ 1.0, 2.0],
[ 3.0, 4.0],
[ 5.0, 6.0],
[ 7.0, 8.0],
]).transpose(); // | | |
let lhs=Matrix3x4::new([ // | | |
]); // | | |
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],
]).transpose();
]);
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot=lhs*rhs;
let m_dot=lhs.dot(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,
Matrix3x2::new([
Matrix2x3::new([
[50.0,60.0],
[114.0,140.0],
[178.0,220.0],
]).transpose().array
]).array
);
}

View File

@@ -3,7 +3,7 @@
/// v.x += v.z;
/// println!("v.x={}",v.x);
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
pub struct Vector<const N:usize,T>{
pub(crate) array:[T;N],
}
@@ -15,5 +15,4 @@ crate::impl_vector_extend!(2);
crate::impl_vector_extend!(3);
//cross product
#[cfg(feature="named-fields")]
crate::impl_vector_3!();

View File

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

View File

@@ -1,79 +0,0 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_fixed_wide_vector_not_const_generic {
(
(),
$n:expr
) => {
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$n,{$n*32}>>{
#[inline]
pub fn length(self)-><fixed_wide::fixed::Fixed::<$n,{$n*32}> as core::ops::Mul>::Output{
self.length_squared().sqrt_unchecked()
}
#[inline]
pub fn with_length<U,V>(self,length:U)-><Vector<N,V> as core::ops::Div<<fixed_wide::fixed::Fixed::<$n,{$n*32}> as core::ops::Mul>::Output>>::Output
where
fixed_wide::fixed::Fixed<$n,{$n*32}>:core::ops::Mul<U,Output=V>,
U:Copy,
V:core::ops::Div<<fixed_wide::fixed::Fixed::<$n,{$n*32}> as core::ops::Mul>::Output>,
{
self*length/self.length()
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! macro_4 {
( $macro: ident, $any:tt ) => {
$crate::macro_repeated!($macro,$any,1,2,3,4);
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_fixed_wide_vector {
() => {
$crate::macro_4!(impl_fixed_wide_vector_not_const_generic,());
// I LOVE NOT BEING ABLE TO USE CONST GENERICS
$crate::macro_repeated!(
impl_fix_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),(16,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),(15,2),(16,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),(14,3),(15,3),(16,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),(13,4),(14,4),(15,4),(16,4),
(1,5),(2,5),(3,5),(4,5),(5,5),(6,5),(7,5),(8,5),(9,5),(10,5),(11,5),(12,5),(13,5),(14,5),(15,5),(16,5),
(1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6),(8,6),(9,6),(10,6),(11,6),(12,6),(13,6),(14,6),(15,6),(16,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),(7,7),(8,7),(9,7),(10,7),(11,7),(12,7),(13,7),(14,7),(15,7),(16,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),(8,8),(9,8),(10,8),(11,8),(12,8),(13,8),(14,8),(15,8),(16,8),
(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),(8,9),(9,9),(10,9),(11,9),(12,9),(13,9),(14,9),(15,9),(16,9),
(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),(7,10),(8,10),(9,10),(10,10),(11,10),(12,10),(13,10),(14,10),(15,10),(16,10),
(1,11),(2,11),(3,11),(4,11),(5,11),(6,11),(7,11),(8,11),(9,11),(10,11),(11,11),(12,11),(13,11),(14,11),(15,11),(16,11),
(1,12),(2,12),(3,12),(4,12),(5,12),(6,12),(7,12),(8,12),(9,12),(10,12),(11,12),(12,12),(13,12),(14,12),(15,12),(16,12),
(1,13),(2,13),(3,13),(4,13),(5,13),(6,13),(7,13),(8,13),(9,13),(10,13),(11,13),(12,13),(13,13),(14,13),(15,13),(16,13),
(1,14),(2,14),(3,14),(4,14),(5,14),(6,14),(7,14),(8,14),(9,14),(10,14),(11,14),(12,14),(13,14),(14,14),(15,14),(16,14),
(1,15),(2,15),(3,15),(4,15),(5,15),(6,15),(7,15),(8,15),(9,15),(10,15),(11,15),(12,15),(13,15),(14,15),(15,15),(16,15),
(1,16),(2,16),(3,16),(4,16),(5,16),(6,16),(7,16),(8,16),(9,16),(10,16),(11,16),(12,16),(13,16),(14,16),(15,16),(16,16)
);
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_fix_not_const_generic{
(
(),
($lhs:expr,$rhs:expr)
)=>{
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$lhs,{$lhs*32}>>
{
paste::item!{
#[inline]
pub fn [<fix_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{
self.map(|t|t.[<fix_ $rhs>]())
}
}
}
}
}

View File

@@ -1,272 +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;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{
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],
])
}
}
}
}

View File

@@ -1 +0,0 @@
/target

7
ratio_ops/Cargo.lock generated
View File

@@ -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"

View File

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

View File

@@ -1,4 +0,0 @@
pub mod ratio;
#[cfg(test)]
mod tests;

View File

@@ -1,294 +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);
/// Comparing two ratios needs to know the parity of the denominators
/// For signed integers this can be implemented with is_negative()
pub trait Parity{
fn parity(&self)->bool;
}
macro_rules! impl_parity_unsigned{
($($type:ty),*)=>{
$(
impl Parity for $type{
fn parity(&self)->bool{
false
}
}
)*
};
}
macro_rules! impl_parity_signed{
($($type:ty),*)=>{
$(
impl Parity for $type{
fn parity(&self)->bool{
self.is_negative()
}
}
)*
};
}
macro_rules! impl_parity_float{
($($type:ty),*)=>{
$(
impl Parity for $type{
fn parity(&self)->bool{
self.is_sign_negative()
}
}
)*
};
}
impl_parity_unsigned!(u8,u16,u32,u64,u128,usize);
impl_parity_signed!(i8,i16,i32,i64,i128,isize);
impl_parity_float!(f32,f64);
macro_rules! impl_ratio_ord_method{
($method:ident, $ratio_method:ident, $output:ty)=>{
impl<LhsNum,LhsDen:Parity> Ratio<LhsNum,LhsDen>{
#[inline]
pub fn $ratio_method<RhsNum,RhsDen:Parity,T>(self,rhs:Ratio<RhsNum,RhsDen>)->$output
where
LhsNum:core::ops::Mul<RhsDen,Output=T>,
LhsDen:core::ops::Mul<RhsNum,Output=T>,
T:Ord,
{
match self.den.parity()^rhs.den.parity(){
true=>(self.den*rhs.num).$method(&(self.num*rhs.den)),
false=>(self.num*rhs.den).$method(&(self.den*rhs.num)),
}
}
}
}
}
//PartialEq
impl_ratio_ord_method!(eq,eq_ratio,bool);
//PartialOrd
impl_ratio_ord_method!(lt,lt_ratio,bool);
impl_ratio_ord_method!(gt,gt_ratio,bool);
impl_ratio_ord_method!(le,le_ratio,bool);
impl_ratio_ord_method!(ge,ge_ratio,bool);
impl_ratio_ord_method!(partial_cmp,partial_cmp_ratio,Option<core::cmp::Ordering>);
//Ord
impl_ratio_ord_method!(cmp,cmp_ratio,core::cmp::Ordering);
/* 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);
// Only implement PartialEq<Self>
// Rust's operators aren't actually that good
impl<Num,Den,T> PartialEq for Ratio<Num,Den>
where
Num:Copy,
Den:Copy,
Num:core::ops::Mul<Den,Output=T>,
T:PartialEq,
{
#[inline]
fn eq(&self,&other:&Self)->bool{
(self.num*other.den).eq(&(other.num*self.den))
}
}
impl<Num,Den> Eq for Ratio<Num,Den>
where
Ratio<Num,Den>:PartialEq,
{}
impl<Num,Den,T> PartialOrd for Ratio<Num,Den>
where
Num:Copy,
Den:Copy,
Num:core::ops::Mul<Den,Output=T>,
T:PartialOrd,
{
#[inline]
fn partial_cmp(&self,&other:&Self)->Option<core::cmp::Ordering>{
(self.num*other.den).partial_cmp(&(other.num*self.den))
}
}
impl<Num,Den,T> Ord for Ratio<Num,Den>
where
Num:Copy,
Den:Copy,
Num:core::ops::Mul<Den,Output=T>,
T:Ord,
{
#[inline]
fn cmp(&self,other:&Self)->std::cmp::Ordering{
(self.num*other.den).cmp(&(other.num*self.den))
}
}

View File

@@ -1,58 +0,0 @@
use crate::ratio::Ratio;
macro_rules! test_op{
($ratio_op:ident,$op:ident,$a:expr,$b:expr,$c:expr,$d:expr)=>{
assert_eq!(
Ratio::new($a,$b).$ratio_op(Ratio::new($c,$d)),
(($a as f32)/($b as f32)).$op(&(($c as f32)/($d as f32)))
);
};
}
macro_rules! test_many_ops{
($ratio_op:ident,$op:ident)=>{
test_op!($ratio_op,$op,1,2,3,4);
test_op!($ratio_op,$op,1,2,-3,4);
test_op!($ratio_op,$op,-1,2,-3,4);
test_op!($ratio_op,$op,-1,-2,-3,4);
test_op!($ratio_op,$op,2,1,6,3);
test_op!($ratio_op,$op,-2,1,6,3);
test_op!($ratio_op,$op,2,-1,-6,3);
test_op!($ratio_op,$op,2,1,6,-3);
};
}
#[test]
fn test_lt(){
test_many_ops!(lt_ratio,lt);
}
#[test]
fn test_gt(){
test_many_ops!(gt_ratio,gt);
}
#[test]
fn test_le(){
test_many_ops!(le_ratio,le);
}
#[test]
fn test_ge(){
test_many_ops!(ge_ratio,ge);
}
#[test]
fn test_eq(){
test_many_ops!(eq_ratio,eq);
}
#[test]
fn test_partial_cmp(){
test_many_ops!(partial_cmp_ratio,partial_cmp);
}
// #[test]
// fn test_cmp(){
// test_many_ops!(cmp_ratio,cmp);
// }