Compare commits
181 Commits
Author | SHA1 | Date | |
---|---|---|---|
c5fb915a6d | |||
a274b6d232 | |||
218a7fbf0f | |||
64e44846aa | |||
0d05540a6b | |||
9b3dde66bd | |||
a65eef3609 | |||
c4a2778af1 | |||
46bb2bac4e | |||
c6b4cc29b8 | |||
9a7ebb0f0a | |||
438d0ec6ec | |||
94e23b7f0f | |||
e6cd239dcb | |||
8d97ffba92 | |||
e46f4fb900 | |||
2b58204cb9 | |||
b91f061797 | |||
102ea607ab | |||
ba357ee99b | |||
546a4aa8c7 | |||
94cd23fe4b | |||
bc773f7d45 | |||
934475b959 | |||
665d528b87 | |||
865d7a7886 | |||
031cd6e771 | |||
6dbe96fca2 | |||
655a6da251 | |||
a100f182e1 | |||
0734122e75 | |||
4e284311e1 | |||
0cd28e402e | |||
260ed0fd5c | |||
ec82745c6d | |||
10e56fb0b9 | |||
5646bd3b5a | |||
6cb639317c | |||
db5c37c2fb | |||
a73a32f2ad | |||
44ac6fe4be | |||
1a24de3cd9 | |||
9f77531995 | |||
7b78338c76 | |||
021d7f9d1f | |||
338669b60f | |||
085d9185a9 | |||
1fd7a6eafd | |||
91b96e4b5d | |||
fc65d0f1f4 | |||
4eaf8794f6 | |||
fa8614891d | |||
c20a0a4a89 | |||
e66a245c78 | |||
eb7eb30814 | |||
57c3f2dd9e | |||
b772647137 | |||
dd2140d1d2 | |||
6cbd3446e5 | |||
b6d260bf2c | |||
53bb1522eb | |||
206e219467 | |||
8ee6204a42 | |||
803f1bea9e | |||
62419e94e1 | |||
d3c4d530b6 | |||
898407a0e9 | |||
66186c7354 | |||
36c769346c | |||
5f2bec75f5 | |||
7a9aaf9fe0 | |||
9ad90cea2e | |||
f2fec0b3b9 | |||
dae72d73d5 | |||
4a1eff40da | |||
d5bd82761a | |||
5cad8637cd | |||
607706ee2a | |||
2312ee27b7 | |||
4d2aa0b2c8 | |||
34450d6a13 | |||
1a6ece1312 | |||
e95f675e91 | |||
504ff37c47 | |||
41cdd03b1b | |||
e375173625 | |||
488a6b6496 | |||
5cdd2c3ee1 | |||
a0da6873c1 | |||
345d5737a2 | |||
f4d28dd3c3 | |||
c362081003 | |||
990a923463 | |||
56b781fcb8 | |||
e026f6efed | |||
e475da5fb4 | |||
c3026c67e9 | |||
103697fbdd | |||
cf17460b77 | |||
823a05c101 | |||
e5f95b97ce | |||
176eb762e3 | |||
15bd78c0e1 | |||
0f9d0c8c39 | |||
eefbdafc16 | |||
b0ecfeb294 | |||
48a8271b99 | |||
1604888254 | |||
4017f33447 | |||
f0527714db | |||
27d96f9b19 | |||
a5094fe873 | |||
1bd45283a9 | |||
a6dc0c37ba | |||
83434a89c7 | |||
b14c84bdad | |||
e98744871b | |||
c26ce93fc8 | |||
c856509759 | |||
5cb98ee29f | |||
bc29cd9848 | |||
502ab7f33f | |||
e0dba8840e | |||
4d13b4ada7 | |||
2a2e729f59 | |||
63cf94499b | |||
83a39468d5 | |||
9aba811cd0 | |||
e413409f9f | |||
e6a28fbb70 | |||
88acec5659 | |||
0f0d7f7a9a | |||
263f0d35d4 | |||
d713b96ad3 | |||
20285f0f98 | |||
f103c247b8 | |||
8e1807b4b7 | |||
f531e8d8ee | |||
78f860c672 | |||
0924518922 | |||
46d89619bd | |||
540749e4f1 | |||
3c5f01da89 | |||
70a79a8d25 | |||
0483c9eb27 | |||
61aad93f8d | |||
cd1aa26293 | |||
b656371142 | |||
9266edbf92 | |||
6335b1da47 | |||
8d5fc1ae48 | |||
67c30b8535 | |||
95651d7091 | |||
91b378aa43 | |||
ac7d9f5c3b | |||
b45d93a7dc | |||
6549305c9f | |||
6ea9eff844 | |||
e684fb421e | |||
8ba76c7a00 | |||
3d3eb966a4 | |||
491de52f17 | |||
69da2c52a4 | |||
9f6dffafda | |||
446de71c30 | |||
d47eaa423e | |||
e604ce83e9 | |||
ac250e9d84 | |||
617952c1e3 | |||
9f9e8c793b | |||
1f6594468d | |||
cc3cb35309 | |||
a923a6b5d1 | |||
68d1c23cfa | |||
8aa7da6be7 | |||
0be0dd5c6f | |||
f4ab9403a4 | |||
67ac4cf7ff | |||
002d3d9eac | |||
e1368962c1 | |||
4ae391e9fd |
@ -1,11 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "deferred_division"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default=["fixed_wide_traits"]
|
|
||||||
fixed_wide_traits=["dep:fixed_wide_traits"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
fixed_wide_traits = { version = "0.1.0", path = "../fixed_wide_traits", optional = true }
|
|
@ -1,8 +0,0 @@
|
|||||||
pub mod ratio;
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature="fixed_wide_traits")]
|
|
||||||
mod wide;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
@ -1,10 +0,0 @@
|
|||||||
#[derive(Clone,Copy,Debug,Hash)]
|
|
||||||
pub struct Ratio<Num,Den>{
|
|
||||||
pub(crate)num:Num,
|
|
||||||
pub(crate)den:Den,
|
|
||||||
}
|
|
||||||
//this trait is like a constructor for Ratio
|
|
||||||
pub trait DeferredDiv<Rhs=Self>{
|
|
||||||
type Output;
|
|
||||||
fn deferred_div(self,rhs:Rhs)->Self::Output;
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
use std::ops::{Add,Mul};
|
|
||||||
use crate::ratio::Ratio;
|
|
||||||
use fixed_wide_traits::wide::{WideMul,WideDiv};
|
|
||||||
|
|
||||||
impl<Num,Den:Copy> Ratio<Num,Den>
|
|
||||||
{
|
|
||||||
pub fn rational_add<T>(self,rhs:T)->Ratio<<Num as Add<<Den as Mul<T>>::Output>>::Output,Den>
|
|
||||||
where
|
|
||||||
Den:Mul<T>,
|
|
||||||
Num:Add<<Den as Mul<T>>::Output>,
|
|
||||||
{
|
|
||||||
Ratio{
|
|
||||||
num:self.num+self.den.mul(rhs),
|
|
||||||
den:self.den,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn wide_rational_add<T>(self,rhs:T)->Ratio<<Num as Add<<Den as WideMul<T>>::Output>>::Output,Den>
|
|
||||||
where
|
|
||||||
Den:WideMul<T>,
|
|
||||||
Num:Add<<Den as WideMul<T>>::Output>,
|
|
||||||
{
|
|
||||||
Ratio{
|
|
||||||
num:self.num+self.den.wide_mul(rhs),
|
|
||||||
den:self.den,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<Num,Den,T> WideMul<T> for Ratio<Num,Den>
|
|
||||||
where
|
|
||||||
Num:WideMul<T>,
|
|
||||||
{
|
|
||||||
type Output=Ratio<<Num as WideMul<T>>::Output,Den>;
|
|
||||||
fn wide_mul(self,rhs:T)->Ratio<<Num as WideMul<T>>::Output,Den>{
|
|
||||||
Ratio{
|
|
||||||
num:self.num.wide_mul(rhs),
|
|
||||||
den:self.den,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<Num,Den,T> WideDiv<T> for Ratio<Num,Den>
|
|
||||||
where
|
|
||||||
Den:WideMul<T>,
|
|
||||||
{
|
|
||||||
type Output=Ratio<Num,<Den as WideMul<T>>::Output>;
|
|
||||||
fn wide_div(self,rhs:T)->Ratio<Num,<Den as WideMul<T>>::Output>{
|
|
||||||
Ratio{
|
|
||||||
num:self.num,
|
|
||||||
den:self.den.wide_mul(rhs),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
31
fixed_wide/Cargo.lock
generated
31
fixed_wide/Cargo.lock
generated
@ -3,26 +3,33 @@
|
|||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bnum"
|
name = "arrayvec"
|
||||||
version = "0.11.0"
|
version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790"
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bnum"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50202def95bf36cb7d1d7a7962cea1c36a3f8ad42425e5d2b71d7acb8041b5b8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixed_wide"
|
name = "fixed_wide"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
"bnum",
|
"bnum",
|
||||||
"fixed_wide_traits",
|
"paste",
|
||||||
"typenum",
|
"ratio_ops",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixed_wide_traits"
|
name = "paste"
|
||||||
version = "0.1.0"
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "ratio_ops"
|
||||||
version = "1.17.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fixed_wide"
|
name = "fixed_wide"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
repository = "https://git.itzana.me/StrafesNET/fixed_wide_vectors"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "Fixed point numbers with optional widening Mul operator."
|
||||||
|
authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default=["fixed_wide_traits"]
|
default=[]
|
||||||
fixed_wide_traits=["dep:fixed_wide_traits"]
|
deferred-division=["dep:ratio_ops"]
|
||||||
|
wide-mul=[]
|
||||||
|
zeroes=["dep:arrayvec"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bnum = "0.11.0"
|
bnum = "0.12.0"
|
||||||
typenum = "1.17.0"
|
arrayvec = { version = "0.7.6", optional = true }
|
||||||
fixed_wide_traits = { version = "0.1.0", path = "../fixed_wide_traits", optional = true }
|
paste = "1.0.15"
|
||||||
|
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true }
|
||||||
|
@ -1,127 +1,848 @@
|
|||||||
use bnum::BInt;
|
use bnum::{BInt,cast::As};
|
||||||
use typenum::Unsigned;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,Hash)]
|
#[derive(Clone,Copy,Debug,Default,Hash)]
|
||||||
pub struct Fixed<const CHUNKS:usize,Frac>{
|
/// A Fixed point number for which multiply operations widen the bits in the output. (when the wide-mul feature is enabled)
|
||||||
pub(crate)bits:BInt<{CHUNKS}>,
|
/// N is the number of u64s to use
|
||||||
pub(crate)frac:PhantomData<Frac>,
|
/// F is the number of fractional bits (always N*32 lol)
|
||||||
|
pub struct Fixed<const N:usize,const F:usize>{
|
||||||
|
pub(crate)bits:BInt<{N}>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const CHUNKS:usize,Frac:Unsigned> Fixed<CHUNKS,Frac>{
|
impl<const N:usize,const F:usize> Fixed<N,F>{
|
||||||
pub const ZERO:Self=Self{bits:BInt::<CHUNKS>::ZERO,frac:PhantomData};
|
pub const MAX:Self=Self::from_bits(BInt::<N>::MAX);
|
||||||
pub const ONE:Self=Self{bits:BInt::<CHUNKS>::ONE.shl(Frac::U32),frac:PhantomData};
|
pub const MIN:Self=Self::from_bits(BInt::<N>::MIN);
|
||||||
pub const NEG_ONE:Self=Self{bits:BInt::<CHUNKS>::NEG_ONE.shl(Frac::U32),frac:PhantomData};
|
pub const ZERO:Self=Self::from_bits(BInt::<N>::ZERO);
|
||||||
|
pub const EPSILON:Self=Self::from_bits(BInt::<N>::ONE);
|
||||||
|
pub const NEG_EPSILON:Self=Self::from_bits(BInt::<N>::NEG_ONE);
|
||||||
|
pub const ONE:Self=Self::from_bits(BInt::<N>::ONE.shl(F as u32));
|
||||||
|
pub const TWO:Self=Self::from_bits(BInt::<N>::TWO.shl(F as u32));
|
||||||
|
pub const HALF:Self=Self::from_bits(BInt::<N>::ONE.shl(F as u32-1));
|
||||||
|
pub const NEG_ONE:Self=Self::from_bits(BInt::<N>::NEG_ONE.shl(F as u32));
|
||||||
|
pub const NEG_TWO:Self=Self::from_bits(BInt::<N>::NEG_TWO.shl(F as u32));
|
||||||
|
pub const NEG_HALF:Self=Self::from_bits(BInt::<N>::NEG_ONE.shl(F as u32-1));
|
||||||
}
|
}
|
||||||
|
impl<const N:usize,const F:usize> Fixed<N,F>{
|
||||||
impl<const CHUNKS:usize,FracDst:Unsigned,T> From<T> for Fixed<CHUNKS,FracDst>
|
#[inline]
|
||||||
where
|
pub const fn from_bits(bits:BInt::<N>)->Self{
|
||||||
BInt<CHUNKS>:From<T>
|
|
||||||
{
|
|
||||||
fn from(value:T)->Self{
|
|
||||||
Self{
|
Self{
|
||||||
bits:BInt::<{CHUNKS}>::from(value)<<FracDst::U32,
|
bits,
|
||||||
frac:PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
pub const fn to_bits(self)->BInt<N>{
|
||||||
|
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{
|
||||||
|
let &[digit]=self.to_bits().to_bits().digits();
|
||||||
|
digit as i64
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const CHUNKS:usize,Frac> PartialEq for Fixed<CHUNKS,Frac>{
|
macro_rules! impl_from {
|
||||||
|
($($from:ty),*)=>{
|
||||||
|
$(
|
||||||
|
impl<const N:usize,const F:usize> From<$from> for Fixed<N,F>{
|
||||||
|
#[inline]
|
||||||
|
fn from(value:$from)->Self{
|
||||||
|
Self::from_bits(BInt::<{N}>::from(value)<<F as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
impl_from!(
|
||||||
|
u8,u16,u32,u64,u128,usize,
|
||||||
|
i8,i16,i32,i64,i128,isize
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<const N:usize,const F:usize> PartialEq for Fixed<N,F>{
|
||||||
|
#[inline]
|
||||||
fn eq(&self,other:&Self)->bool{
|
fn eq(&self,other:&Self)->bool{
|
||||||
self.bits.eq(&other.bits)
|
self.bits.eq(&other.bits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<const CHUNKS:usize,Frac> Eq for Fixed<CHUNKS,Frac>{}
|
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 CHUNKS:usize,Frac> PartialOrd for Fixed<CHUNKS,Frac>{
|
impl<const N:usize,const F:usize> PartialOrd for Fixed<N,F>{
|
||||||
|
#[inline]
|
||||||
fn partial_cmp(&self,other:&Self)->Option<std::cmp::Ordering>{
|
fn partial_cmp(&self,other:&Self)->Option<std::cmp::Ordering>{
|
||||||
self.bits.partial_cmp(&other.bits)
|
self.bits.partial_cmp(&other.bits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<const CHUNKS:usize,Frac> Ord for Fixed<CHUNKS,Frac>{
|
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{
|
fn cmp(&self,other:&Self)->std::cmp::Ordering{
|
||||||
self.bits.cmp(&other.bits)
|
self.bits.cmp(&other.bits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const CHUNKS:usize,Frac> std::ops::Neg for Fixed<CHUNKS,Frac>{
|
impl<const N:usize,const F:usize> std::ops::Neg for Fixed<N,F>{
|
||||||
type Output=Self;
|
type Output=Self;
|
||||||
|
#[inline]
|
||||||
fn neg(self)->Self{
|
fn neg(self)->Self{
|
||||||
Self{
|
Self::from_bits(self.bits.neg())
|
||||||
bits:self.bits.neg(),
|
}
|
||||||
frac:PhantomData,
|
}
|
||||||
|
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_operator {
|
const fn signed_shift(lhs:u64,rhs:i32)->u64{
|
||||||
( $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
|
if rhs.is_negative(){
|
||||||
impl<const CHUNKS:usize,Frac> core::ops::$trait for $struct<CHUNKS,Frac>{
|
lhs>>-rhs
|
||||||
type Output = $output;
|
}else{
|
||||||
|
lhs<<rhs
|
||||||
fn $method(self, other: Self) -> Self::Output {
|
}
|
||||||
Self {
|
|
||||||
bits:self.bits.$method(other.bits),
|
|
||||||
frac:PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
macro_rules! impl_assign_operator {
|
macro_rules! impl_into_float {
|
||||||
( $struct: ident, $trait: ident, $method: ident ) => {
|
( $output: ty, $unsigned:ty, $exponent_bits:expr, $mantissa_bits:expr ) => {
|
||||||
impl<const CHUNKS:usize,Frac> core::ops::$trait for $struct<CHUNKS,Frac>{
|
impl<const N:usize,const F:usize> Into<$output> for Fixed<N,F>{
|
||||||
fn $method(&mut self, other: Self) {
|
#[inline]
|
||||||
self.bits.$method(other.bits);
|
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.saturating_sub(1)>>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);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn integer_decode_f32(f: f32) -> (u64, i16, bool) {
|
||||||
|
let bits: u32 = f.to_bits();
|
||||||
|
let sign: bool = bits & (1<<31) != 0;
|
||||||
|
let mut exponent: i16 = ((bits >> 23) & 0xff) as i16;
|
||||||
|
let mantissa = if exponent == 0 {
|
||||||
|
(bits & 0x7fffff) << 1
|
||||||
|
} else {
|
||||||
|
(bits & 0x7fffff) | 0x800000
|
||||||
|
};
|
||||||
|
// Exponent bias + mantissa shift
|
||||||
|
exponent -= 127 + 23;
|
||||||
|
(mantissa as u64, exponent, sign)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn integer_decode_f64(f: f64) -> (u64, i16, bool) {
|
||||||
|
let bits: u64 = f.to_bits();
|
||||||
|
let sign: bool = bits & (1u64<<63) != 0;
|
||||||
|
let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
|
||||||
|
let mantissa = if exponent == 0 {
|
||||||
|
(bits & 0xfffffffffffff) << 1
|
||||||
|
} else {
|
||||||
|
(bits & 0xfffffffffffff) | 0x10000000000000
|
||||||
|
};
|
||||||
|
// Exponent bias + mantissa shift
|
||||||
|
exponent -= 1023 + 52;
|
||||||
|
(mantissa, exponent, sign)
|
||||||
|
}
|
||||||
|
#[derive(Debug,Eq,PartialEq)]
|
||||||
|
pub enum FixedFromFloatError{
|
||||||
|
Nan,
|
||||||
|
Infinite,
|
||||||
|
Overflow,
|
||||||
|
Underflow,
|
||||||
|
}
|
||||||
|
impl FixedFromFloatError{
|
||||||
|
pub fn underflow_to_zero<const N:usize,const F:usize>(self)->Result<Fixed<N,F>,Self>{
|
||||||
|
match self{
|
||||||
|
FixedFromFloatError::Underflow=>Ok(Fixed::ZERO),
|
||||||
|
_=>Err(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
macro_rules! impl_from_float {
|
||||||
|
( $decode:ident, $input: ty, $mantissa_bits:expr ) => {
|
||||||
|
impl<const N:usize,const F:usize> TryFrom<$input> for Fixed<N,F>{
|
||||||
|
type Error=FixedFromFloatError;
|
||||||
|
#[inline]
|
||||||
|
fn try_from(value:$input)->Result<Self,Self::Error>{
|
||||||
|
const DIGIT_SHIFT:u32=6;
|
||||||
|
match value.classify(){
|
||||||
|
std::num::FpCategory::Nan=>Err(FixedFromFloatError::Nan),
|
||||||
|
std::num::FpCategory::Infinite=>Err(FixedFromFloatError::Infinite),
|
||||||
|
std::num::FpCategory::Zero=>Ok(Self::ZERO),
|
||||||
|
std::num::FpCategory::Subnormal
|
||||||
|
|std::num::FpCategory::Normal
|
||||||
|
=>{
|
||||||
|
let (m,e,s)=$decode(value);
|
||||||
|
let mut digits=[0u64;N];
|
||||||
|
let most_significant_bit=e as i32+$mantissa_bits as i32+F as i32;
|
||||||
|
if most_significant_bit<0{
|
||||||
|
return Err(FixedFromFloatError::Underflow);
|
||||||
|
}
|
||||||
|
let digit_index=most_significant_bit>>DIGIT_SHIFT;
|
||||||
|
let digit=digits.get_mut(digit_index as usize).ok_or(FixedFromFloatError::Overflow)?;
|
||||||
|
let take_bits=most_significant_bit-(digit_index<<DIGIT_SHIFT);
|
||||||
|
let rest_of_mantissa=-($mantissa_bits as i32-(take_bits as i32));
|
||||||
|
*digit=signed_shift(m,rest_of_mantissa);
|
||||||
|
if rest_of_mantissa<0&&digit_index!=0{
|
||||||
|
//we don't care if some float bits are partially truncated
|
||||||
|
if let Some(digit)=digits.get_mut((digit_index-1) as usize){
|
||||||
|
let take_bits=most_significant_bit-((digit_index-1)<<DIGIT_SHIFT);
|
||||||
|
let rest_of_mantissa=-($mantissa_bits as i32-(take_bits as i32));
|
||||||
|
*digit=signed_shift(m,rest_of_mantissa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let bits=BInt::from_bits(bnum::BUint::from_digits(digits));
|
||||||
|
Ok(if s{
|
||||||
|
Self::from_bits(bits.overflowing_neg().0)
|
||||||
|
}else{
|
||||||
|
Self::from_bits(bits)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl_from_float!(integer_decode_f32,f32,24);
|
||||||
|
impl_from_float!(integer_decode_f64,f64,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>{
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
|
||||||
|
where
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
|
||||||
|
where
|
||||||
|
BInt::<N>:From<U>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn $method(&mut self, other: U) {
|
||||||
|
self.bits.$method(BInt::<N>::from(other).shl(F as u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Impl arithmetic pperators
|
// Impl arithmetic pperators
|
||||||
impl_assign_operator!( Fixed, AddAssign, add_assign );
|
impl_additive_assign_operator!( Fixed, AddAssign, add_assign );
|
||||||
impl_operator!( Fixed, Add, add, Self );
|
impl_additive_operator!( Fixed, Add, add, Self );
|
||||||
impl_assign_operator!( Fixed, SubAssign, sub_assign );
|
impl_additive_assign_operator!( Fixed, SubAssign, sub_assign );
|
||||||
impl_operator!( Fixed, Sub, sub, Self );
|
impl_additive_operator!( Fixed, Sub, sub, Self );
|
||||||
impl_assign_operator!( Fixed, MulAssign, mul_assign );
|
impl_additive_assign_operator!( Fixed, RemAssign, rem_assign );
|
||||||
impl_operator!( Fixed, Mul, mul, Self );
|
impl_additive_operator!( Fixed, Rem, rem, Self );
|
||||||
impl_assign_operator!( Fixed, DivAssign, div_assign );
|
|
||||||
impl_operator!( Fixed, Div, div, Self );
|
|
||||||
impl_assign_operator!( Fixed, RemAssign, rem_assign );
|
|
||||||
impl_operator!( Fixed, Rem, rem, Self );
|
|
||||||
|
|
||||||
// Impl bitwise operators
|
// Impl bitwise operators
|
||||||
impl_assign_operator!( Fixed, BitAndAssign, bitand_assign );
|
impl_additive_assign_operator!( Fixed, BitAndAssign, bitand_assign );
|
||||||
impl_operator!( Fixed, BitAnd, bitand, Self );
|
impl_additive_operator!( Fixed, BitAnd, bitand, Self );
|
||||||
impl_assign_operator!( Fixed, BitOrAssign, bitor_assign );
|
impl_additive_assign_operator!( Fixed, BitOrAssign, bitor_assign );
|
||||||
impl_operator!( Fixed, BitOr, bitor, Self );
|
impl_additive_operator!( Fixed, BitOr, bitor, Self );
|
||||||
impl_assign_operator!( Fixed, BitXorAssign, bitxor_assign );
|
impl_additive_assign_operator!( Fixed, BitXorAssign, bitxor_assign );
|
||||||
impl_operator!( Fixed, BitXor, bitxor, Self );
|
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 ) => {
|
||||||
|
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, 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_())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[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.div_euclid(BInt::from(other)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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{
|
||||||
|
//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_euclid(rhs).as_())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(all(not(feature="wide-mul"),not(feature="deferred-division")))]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_multiplicative_operator {
|
||||||
|
( $struct: ident, $trait: ident, $method: ident, $inner_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.$inner_method(BInt::<N>::from(other)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! impl_multiplicative_assign_operator {
|
||||||
|
( $struct: ident, $trait: ident, $method: ident, $not_assign_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=self.bits.$not_assign_method(BInt::<N>::from(other));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! macro_repeated{
|
||||||
|
(
|
||||||
|
$macro:ident,
|
||||||
|
$any:tt,
|
||||||
|
$($repeated:tt),*
|
||||||
|
)=>{
|
||||||
|
$(
|
||||||
|
$macro!($any, $repeated);
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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, mul );
|
||||||
|
impl_multiplicative_operator!( Fixed, Mul, mul, mul, Self );
|
||||||
|
impl_multiplicative_assign_operator!( Fixed, DivAssign, div_assign, div_euclid );
|
||||||
|
impl_multiplicative_operator!( Fixed, Div, div, div_euclid, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[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()
|
||||||
|
}
|
||||||
|
}
|
||||||
macro_rules! impl_shift_operator {
|
macro_rules! impl_shift_operator {
|
||||||
( $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
|
( $struct: ident, $trait: ident, $method: ident, $output: ty ) => {
|
||||||
impl<const CHUNKS:usize,Frac> core::ops::$trait<u32> for $struct<CHUNKS,Frac>{
|
impl<const N:usize,const F:usize> core::ops::$trait<u32> for $struct<N,F>{
|
||||||
type Output = $output;
|
type Output = $output;
|
||||||
|
#[inline]
|
||||||
fn $method(self, other: u32) -> Self::Output {
|
fn $method(self, other: u32) -> Self::Output {
|
||||||
Self {
|
Self::from_bits(self.bits.$method(other))
|
||||||
bits:self.bits.$method(other),
|
}
|
||||||
frac:PhantomData,
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
macro_rules! impl_shift_assign_operator {
|
macro_rules! impl_shift_assign_operator {
|
||||||
( $struct: ident, $trait: ident, $method: ident ) => {
|
( $struct: ident, $trait: ident, $method: ident ) => {
|
||||||
impl<const CHUNKS:usize,Frac> core::ops::$trait<u32> for $struct<CHUNKS,Frac>{
|
impl<const N:usize,const F:usize> core::ops::$trait<u32> for $struct<N,F>{
|
||||||
fn $method(&mut self, other: u32) {
|
#[inline]
|
||||||
self.bits.$method(other);
|
fn $method(&mut self, other: u32) {
|
||||||
}
|
self.bits.$method(other);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
impl_shift_assign_operator!( Fixed, ShlAssign, shl_assign );
|
impl_shift_assign_operator!( Fixed, ShlAssign, shl_assign );
|
||||||
impl_shift_operator!( Fixed, Shl, shl, Self );
|
impl_shift_operator!( Fixed, Shl, shl, Self );
|
||||||
impl_shift_assign_operator!( Fixed, ShrAssign, shr_assign );
|
impl_shift_assign_operator!( Fixed, ShrAssign, shr_assign );
|
||||||
impl_shift_operator!( Fixed, Shr, shr, Self );
|
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"))]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
)=>{
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature="wide-mul")]
|
||||||
|
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,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)
|
||||||
|
);
|
||||||
|
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>]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)=>{
|
||||||
|
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:
|
||||||
|
//1. count "used" bits to the left of the decimal, not including the sign bit (so -1)
|
||||||
|
//2. divide by 2 via >>1 (sqrt-ish)
|
||||||
|
//3. add on fractional offset
|
||||||
|
//Voila
|
||||||
|
let used_bits=self.bits.bits() as i32-1-($n*32) as i32;
|
||||||
|
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>]();
|
||||||
|
//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))
|
||||||
|
};
|
||||||
|
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")
|
||||||
|
}else{
|
||||||
|
self.sqrt_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn sqrt_checked(self)->Option<Self>{
|
||||||
|
if self<Self::ZERO{
|
||||||
|
None
|
||||||
|
}else{
|
||||||
|
Some(self.sqrt_unchecked())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
use bnum::BInt;
|
|
||||||
use bnum::cast::As;
|
|
||||||
use typenum::{Sum,Unsigned};
|
|
||||||
use crate::fixed::Fixed;
|
|
||||||
use fixed_wide_traits::wide::WideMul;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
macro_rules! impl_wide_mul {
|
|
||||||
($lhs: expr,$rhs: expr) => {
|
|
||||||
impl<A,B> WideMul<Fixed<$rhs,B>> for Fixed<$lhs,A>
|
|
||||||
where
|
|
||||||
A:std::ops::Add<B>,
|
|
||||||
B:Unsigned,
|
|
||||||
{
|
|
||||||
type Output=Fixed<{$lhs+$rhs},Sum<A,B>>;
|
|
||||||
fn wide_mul(self,rhs:Fixed<$rhs,B>)->Self::Output{
|
|
||||||
Fixed{
|
|
||||||
bits:self.bits.as_::<BInt<{$lhs+$rhs}>>()*rhs.bits.as_::<BInt<{$lhs+$rhs}>>(),
|
|
||||||
frac:PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_wide_mul_all {
|
|
||||||
($(($x:expr, $y:expr)),*) => {
|
|
||||||
$(
|
|
||||||
impl_wide_mul!($x, $y);
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//const generics sidestepped wahoo
|
|
||||||
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)
|
|
||||||
);
|
|
@ -1,11 +1,8 @@
|
|||||||
pub mod fixed;
|
pub mod fixed;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub mod typenum{
|
#[cfg(feature="zeroes")]
|
||||||
pub use typenum::Unsigned;
|
pub mod zeroes;
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature="fixed_wide_traits")]
|
#[cfg(test)]
|
||||||
mod fixed_wide_traits;
|
mod tests;
|
||||||
#[cfg(feature="fixed_wide_traits")]
|
|
||||||
pub use ::fixed_wide_traits::wide;
|
|
||||||
|
218
fixed_wide/src/tests.rs
Normal file
218
fixed_wide/src/tests.rs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
use crate::types::I32F32;
|
||||||
|
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)<<16;
|
||||||
|
let f:f32=a.into();
|
||||||
|
assert_eq!(f,237946589723468975f32*2.0f32.powi(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)<<16;
|
||||||
|
let f:f64=a.into();
|
||||||
|
assert_eq!(f,237946589723468975f64*2.0f64.powi(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_f32(){
|
||||||
|
let a=I256F256::from(1)>>2;
|
||||||
|
let b:Result<I256F256,_>=0.25f32.try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
let a=I256F256::from(-1)>>2;
|
||||||
|
let b:Result<I256F256,_>=(-0.25f32).try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
let a=I256F256::from(0);
|
||||||
|
let b:Result<I256F256,_>=0.try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
let a=I256F256::from(0b101011110101001010101010000000000000000000000000000i64)<<16;
|
||||||
|
let b:Result<I256F256,_>=(0b101011110101001010101010000000000000000000000000000u64 as f32*2.0f32.powi(16)).try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
//I32F32::MAX into f32 is truncated into this value
|
||||||
|
let a=I32F32::raw(0b111111111111111111111111000000000000000000000000000000000000000i64);
|
||||||
|
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MAX).try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
//I32F32::MIN hits a special case since it's not representable as a positive signed integer
|
||||||
|
//TODO: don't return an overflow because this is technically possible
|
||||||
|
let a=I32F32::MIN;
|
||||||
|
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN).try_into();
|
||||||
|
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
|
||||||
|
//16 is within the 24 bits of float precision
|
||||||
|
let b:Result<I32F32,_>=Into::<f32>::into(-I32F32::MIN.fix_2()).try_into();
|
||||||
|
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
|
||||||
|
let b:Result<I32F32,_>=f32::MIN_POSITIVE.try_into();
|
||||||
|
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Underflow));
|
||||||
|
//test many cases
|
||||||
|
for i in 0..64{
|
||||||
|
let a=crate::fixed::Fixed::<2,64>::raw_digit(0b111111111111111111111111000000000000000000000000000000000000000i64)<<i;
|
||||||
|
let f:f32=a.into();
|
||||||
|
let b:Result<crate::fixed::Fixed<2,64>,_>=f.try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_f64(){
|
||||||
|
let a=I256F256::from(1)>>2;
|
||||||
|
let b:Result<I256F256,_>=0.25f64.try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
let a=I256F256::from(-1)>>2;
|
||||||
|
let b:Result<I256F256,_>=(-0.25f64).try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
let a=I256F256::from(0);
|
||||||
|
let b:Result<I256F256,_>=0.try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
let a=I256F256::from(0b101011110101001010101010000000000000000000000000000i64)<<16;
|
||||||
|
let b:Result<I256F256,_>=(0b101011110101001010101010000000000000000000000000000u64 as f64*2.0f64.powi(16)).try_into();
|
||||||
|
assert_eq!(b,Ok(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn you_can_shr_numbers(){
|
||||||
|
let a=I32F32::from(4);
|
||||||
|
assert_eq!(a>>1,I32F32::from(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wide_mul(){
|
||||||
|
let a=I32F32::ONE;
|
||||||
|
let aa=a.wide_mul_1_1(a);
|
||||||
|
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);
|
||||||
|
let b=I32F32::from(3);
|
||||||
|
|
||||||
|
let w1=a.wide_mul_1_1(b);
|
||||||
|
let w2=w1.wide_mul_2_2(w1);
|
||||||
|
let w3=w2.wide_mul_4_4(w2);
|
||||||
|
|
||||||
|
assert_eq!(w3,I256F256::from((3i128*2).pow(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bint(){
|
||||||
|
let a=I32F32::ONE;
|
||||||
|
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;
|
||||||
|
assert_eq!(a.sqrt(),I32F32::from(2));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_sqrt_zero(){
|
||||||
|
let a=I32F32::ZERO;
|
||||||
|
assert_eq!(a.sqrt(),I32F32::ZERO);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_sqrt_low(){
|
||||||
|
let a=I32F32::HALF;
|
||||||
|
let b=a.fixed_mul(a);
|
||||||
|
assert_eq!(b.sqrt(),a);
|
||||||
|
}
|
||||||
|
fn find_equiv_sqrt_via_f64(n:I32F32)->I32F32{
|
||||||
|
//GIMME THEM BITS BOY
|
||||||
|
let &[bits]=n.to_bits().to_bits().digits();
|
||||||
|
let ibits=bits as i64;
|
||||||
|
let f=(ibits as f64)/((1u64<<32) as f64);
|
||||||
|
let f_ans=f.sqrt();
|
||||||
|
let i=(f_ans*((1u64<<32) as f64)) as i64;
|
||||||
|
let r=I32F32::from_bits(bnum::BInt::<1>::from(i));
|
||||||
|
//mimic the behaviour of the algorithm,
|
||||||
|
//return the result if it truncates to the exact answer
|
||||||
|
if (r+I32F32::EPSILON).wide_mul_1_1(r+I32F32::EPSILON)==n.wide_mul_1_1(I32F32::ONE){
|
||||||
|
return r+I32F32::EPSILON;
|
||||||
|
}
|
||||||
|
if (r-I32F32::EPSILON).wide_mul_1_1(r-I32F32::EPSILON)==n.wide_mul_1_1(I32F32::ONE){
|
||||||
|
return r-I32F32::EPSILON;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
fn test_exact(n:I32F32){
|
||||||
|
assert_eq!(n.sqrt(),find_equiv_sqrt_via_f64(n));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_sqrt_exact(){
|
||||||
|
//43
|
||||||
|
for i in 0..((i64::MAX as f32).ln() as u32){
|
||||||
|
let n=I32F32::from_bits(bnum::BInt::<1>::from((i as f32).exp() as i64));
|
||||||
|
test_exact(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
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),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
pub type I32F32=crate::fixed::Fixed<1,typenum::consts::U32>;
|
pub type I32F32=crate::fixed::Fixed<1,32>;
|
||||||
pub type I64F64=crate::fixed::Fixed<2,typenum::consts::U64>;
|
pub type I64F64=crate::fixed::Fixed<2,64>;
|
||||||
pub type I128F128=crate::fixed::Fixed<4,typenum::consts::U128>;
|
pub type I128F128=crate::fixed::Fixed<4,128>;
|
||||||
pub type I256F256=crate::fixed::Fixed<8,typenum::consts::U256>;
|
pub type I256F256=crate::fixed::Fixed<8,256>;
|
||||||
|
53
fixed_wide/src/zeroes.rs
Normal file
53
fixed_wide/src/zeroes.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use crate::fixed::Fixed;
|
||||||
|
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
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>{
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
let radicand=a1*a1-a2*a0*4;
|
||||||
|
match radicand.cmp(&<Self as core::ops::Mul>::Output::ZERO){
|
||||||
|
Ordering::Greater=>{
|
||||||
|
paste::item!{
|
||||||
|
let planar_radicand=radicand.sqrt().[<fix_ $n>]();
|
||||||
|
}
|
||||||
|
//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)
|
||||||
|
},
|
||||||
|
Ordering::Equal=>ArrayVec::from_iter([(a1)/(a2*-2)]),
|
||||||
|
Ordering::Less=>ArrayVec::new_const(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn zeroes1(a0:Self,a1:Self)->ArrayVec<<Self as core::ops::Div>::Output,1>{
|
||||||
|
if a1==Self::ZERO{
|
||||||
|
ArrayVec::new_const()
|
||||||
|
}else{
|
||||||
|
ArrayVec::from_iter([(-a0)/(a1)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
impl_zeroes!(1);
|
||||||
|
impl_zeroes!(2);
|
||||||
|
impl_zeroes!(3);
|
||||||
|
impl_zeroes!(4);
|
||||||
|
//sqrt doubles twice!
|
||||||
|
//impl_zeroes!(5);
|
||||||
|
//impl_zeroes!(6);
|
||||||
|
//impl_zeroes!(7);
|
||||||
|
//impl_zeroes!(8);
|
63
fixed_wide_traits/Cargo.lock
generated
63
fixed_wide_traits/Cargo.lock
generated
@ -1,63 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "az"
|
|
||||||
version = "1.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bytemuck"
|
|
||||||
version = "1.17.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crunchy"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fixed"
|
|
||||||
version = "1.28.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a"
|
|
||||||
dependencies = [
|
|
||||||
"az",
|
|
||||||
"bytemuck",
|
|
||||||
"half",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fixed_wide_traits"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"fixed",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "half"
|
|
||||||
version = "2.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"crunchy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.17.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
|
@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fixed_wide_traits"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
fixed = "1.28.0"
|
|
||||||
typenum = "1.17.0"
|
|
@ -1,2 +0,0 @@
|
|||||||
pub mod wide;
|
|
||||||
pub mod narrow;
|
|
@ -1,57 +0,0 @@
|
|||||||
pub trait Narrow{
|
|
||||||
type Output;
|
|
||||||
fn narrow(self)->Self::Output;
|
|
||||||
}
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error{
|
|
||||||
Overflow,
|
|
||||||
Underflow,
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for Error{
|
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
|
||||||
write!(f,"{self:?}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl std::error::Error for Error{}
|
|
||||||
pub trait TryNarrow{
|
|
||||||
type Output;
|
|
||||||
fn try_narrow(self)->Result<Self::Output,Error>;
|
|
||||||
}
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
//TODO: use num_traits to do a blanket implementation (self<T::MIN as U)
|
|
||||||
impl TryNarrow for i16{
|
|
||||||
type Output=i8;
|
|
||||||
fn try_narrow(self)->Result<Self::Output,Error>{
|
|
||||||
if self<i8::MIN as i16{
|
|
||||||
return Err(Error::Underflow);
|
|
||||||
}
|
|
||||||
if (i8::MAX as i16)<self{
|
|
||||||
return Err(Error::Overflow);
|
|
||||||
}
|
|
||||||
Ok(self as i8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_i16_i8(){
|
|
||||||
assert!(matches!(257i16.try_narrow(),Err(Error::Overflow)));
|
|
||||||
assert!(matches!(64i16.try_narrow(),Ok(64i8)));
|
|
||||||
assert!(matches!((-257i16).try_narrow(),Err(Error::Underflow)));
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Narrow for fixed::FixedI16<typenum::consts::U8>{
|
|
||||||
type Output=i8;
|
|
||||||
fn narrow(self)->Self::Output{
|
|
||||||
(self.to_bits()>>8) as i8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_fixed_i16_i8(){
|
|
||||||
let a=fixed::FixedI16::<typenum::consts::U8>::from(5)/2;
|
|
||||||
assert_eq!(a.narrow(),2);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
pub trait WideMul<Rhs=Self>{
|
|
||||||
type Output;
|
|
||||||
fn wide_mul(self,rhs:Rhs)->Self::Output;
|
|
||||||
}
|
|
||||||
pub trait WideDiv<Rhs=Self>{
|
|
||||||
type Output;
|
|
||||||
fn wide_div(self,rhs:Rhs)->Self::Output;
|
|
||||||
}
|
|
||||||
pub trait WideDot<Rhs=Self>{
|
|
||||||
type Output;
|
|
||||||
fn wide_dot(self,rhs:Rhs)->Self::Output;
|
|
||||||
}
|
|
||||||
pub trait WideCross<Rhs=Self>{
|
|
||||||
type Output;
|
|
||||||
fn wide_cross(self,rhs:Rhs)->Self::Output;
|
|
||||||
}
|
|
1
fixed_wide_vectors/.gitignore
vendored
1
fixed_wide_vectors/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/target
|
|
@ -1,14 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fixed_wide_vectors"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default=["fixed_wide_traits"]
|
|
||||||
fixed_wide_traits=["dep:fixed_wide_traits"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
fixed_wide_traits = { version = "0.1.0", path = "../fixed_wide_traits", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
fixed_wide = { version = "0.1.0", path = "../fixed_wide" }
|
|
@ -1,17 +0,0 @@
|
|||||||
use std::ops::Add;
|
|
||||||
use fixed_wide_traits::wide::WideDot;
|
|
||||||
|
|
||||||
pub struct Affine<M,T>{
|
|
||||||
pub matrix:M,
|
|
||||||
pub offset:T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M:Copy,T:Copy> Affine<M,T>{
|
|
||||||
pub fn wide_transform<X>(&self,input:X)-><<M as WideDot<X>>::Output as Add<T>>::Output
|
|
||||||
where
|
|
||||||
M:WideDot<X>,
|
|
||||||
<M as WideDot<X>>::Output:Add<T>,
|
|
||||||
{
|
|
||||||
self.matrix.wide_dot(input)+self.offset
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
mod macros;
|
|
||||||
mod vector;
|
|
||||||
|
|
||||||
#[cfg(feature="fixed_wide_traits")]
|
|
||||||
pub mod affine;
|
|
||||||
|
|
||||||
pub use vector::Vector2;
|
|
||||||
pub use vector::Vector3;
|
|
||||||
pub use vector::Vector4;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
@ -1,281 +0,0 @@
|
|||||||
#[cfg(feature="fixed_wide_traits")]
|
|
||||||
pub mod wide;
|
|
||||||
|
|
||||||
// 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), + }, ( $($generic: 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 its values as a tuple.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use fixed_wide_vectors::Vector2;
|
|
||||||
///
|
|
||||||
/// let vec2 = Vector2::new(0, 0);
|
|
||||||
/// let tuple = vec2.to_tuple();
|
|
||||||
///
|
|
||||||
/// assert_eq!(tuple, (0, 0));
|
|
||||||
/// ```
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn to_tuple(self) -> ( $($generic), + ) {
|
|
||||||
( $(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> From<($($generic), +)> for $struct<T> {
|
|
||||||
fn from(from: ($($generic), +)) -> Self {
|
|
||||||
let ( $($field), + ) = from;
|
|
||||||
|
|
||||||
Self {
|
|
||||||
$( $field ), +
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() ), +
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<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_operator!( $struct { $($field), + }, AddAssign, add_assign );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, Add, add, Self );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, SubAssign, sub_assign );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, Sub, sub, Self );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, MulAssign, mul_assign );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, Mul, mul, Self );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, DivAssign, div_assign );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, Div, div, Self );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, RemAssign, rem_assign );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, Rem, rem, Self );
|
|
||||||
|
|
||||||
// Impl bitwise operators
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, BitAndAssign, bitand_assign );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, BitAnd, bitand, Self );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, BitOrAssign, bitor_assign );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, BitOr, bitor, Self );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, BitXorAssign, bitxor_assign );
|
|
||||||
$crate::impl_operator!( $struct { $($field), + }, BitXor, bitxor, Self );
|
|
||||||
|
|
||||||
// Impl floating-point based methods
|
|
||||||
$crate::impl_wide_operations!( $struct { $($field), + }, $size );
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[macro_export(local_inner_macros)]
|
|
||||||
macro_rules! impl_operator {
|
|
||||||
( $struct: ident { $($field: ident), + }, $trait: ident, $method: ident, $output: ty ) => {
|
|
||||||
impl<T: core::ops::$trait<Output = T>> core::ops::$trait<Self> 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 {
|
|
||||||
Self {
|
|
||||||
$( $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,43 +0,0 @@
|
|||||||
#[doc(hidden)]
|
|
||||||
#[macro_export(local_inner_macros)]
|
|
||||||
macro_rules! impl_wide_operations {
|
|
||||||
( $struct: ident { $($field: ident), + }, $size: expr ) => {
|
|
||||||
impl<U,T:Copy+fixed_wide_traits::wide::WideMul<Output=U>> fixed_wide_traits::wide::WideMul for $struct<T> {
|
|
||||||
type Output=$struct<U>;
|
|
||||||
#[inline]
|
|
||||||
fn wide_mul(self, rhs: Self) -> Self::Output {
|
|
||||||
$struct{
|
|
||||||
$( $field: self.$field.wide_mul(rhs.$field) ), +
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<U:std::ops::Add<Output=U>,T:Copy+fixed_wide_traits::wide::WideMul<Output=U>> $struct<T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn wide_dot(self, other: Self) -> U {
|
|
||||||
$crate::sum_repeating!(
|
|
||||||
$( + (self.$field.wide_mul(other.$field)) ) +
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn wide_length_squared(&self) -> U {
|
|
||||||
let squared = $struct {
|
|
||||||
$( $field: self.$field.wide_mul(self.$field) ), +
|
|
||||||
};
|
|
||||||
|
|
||||||
$crate::sum_repeating!(
|
|
||||||
$( + squared.$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) *
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
use fixed_wide_traits::wide::WideMul;
|
|
||||||
|
|
||||||
use crate::Vector3;
|
|
||||||
|
|
||||||
type Planar64=fixed_wide::types::I32F32;
|
|
||||||
//type Planar64Wide1=fixed::types::I64F64;
|
|
||||||
//type Planar64Wide2=fixed_wide::types::I128F128;
|
|
||||||
type Planar64Wide3=fixed_wide::types::I256F256;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn wide_int64() {
|
|
||||||
let a=Planar64::from(2);
|
|
||||||
let b=Planar64::from(3);
|
|
||||||
|
|
||||||
let w1=a.wide_mul(b);
|
|
||||||
let w2=w1.wide_mul(w1);
|
|
||||||
let w3=w2.wide_mul(w2);
|
|
||||||
|
|
||||||
assert_eq!(w3,Planar64Wide3::from((3i128*2).pow(4)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn you_can_add_numbers(){
|
|
||||||
let a=Planar64Wide3::from((3i128*2).pow(4));
|
|
||||||
assert_eq!(a+a,Planar64Wide3::from((3i128*2).pow(4)*2))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn you_can_shr_numbers(){
|
|
||||||
let a=Planar64::from(4);
|
|
||||||
assert_eq!(a>>1,Planar64::from(2))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn wide_vec3(){
|
|
||||||
let v=Vector3::from_value(Planar64::from(3));
|
|
||||||
let v1=v.wide_mul(v);
|
|
||||||
let v2=v1.wide_mul(v1);
|
|
||||||
let v3=v2.wide_mul(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(v);
|
|
||||||
let v2=v1.wide_mul(v1);
|
|
||||||
let v3=v2.wide_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.wide_mul(v);
|
|
||||||
let v2=v1.wide_mul(v1);
|
|
||||||
let v3=v2.wide_length_squared();
|
|
||||||
|
|
||||||
assert_eq!(v3,Planar64Wide3::from(3i128.pow(8)*3));
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
// 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 }, (T, T), 2);
|
|
||||||
crate::impl_vector!(Vector3 { x, y, z }, (T, T, T), 3);
|
|
||||||
crate::impl_vector!(Vector4 { x, y, z, w }, (T, T, T, T), 4);
|
|
@ -4,33 +4,33 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bnum"
|
name = "bnum"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790"
|
checksum = "50202def95bf36cb7d1d7a7962cea1c36a3f8ad42425e5d2b71d7acb8041b5b8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixed_wide"
|
name = "fixed_wide"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bnum",
|
"bnum",
|
||||||
"fixed_wide_traits",
|
"paste",
|
||||||
"typenum",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixed_wide_traits"
|
name = "linear_ops"
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fixed_wide_vectors"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fixed_wide",
|
"fixed_wide",
|
||||||
"fixed_wide_traits",
|
"paste",
|
||||||
|
"ratio_ops",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "paste"
|
||||||
version = "1.17.0"
|
version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ratio_ops"
|
||||||
|
version = "0.1.0"
|
22
linear_ops/Cargo.toml
Normal file
22
linear_ops/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "linear_ops"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
repository = "https://git.itzana.me/StrafesNET/fixed_wide_vectors"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "Vector/Matrix operations using trait bounds."
|
||||||
|
authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default=["named-fields","fixed-wide"]
|
||||||
|
named-fields=[]
|
||||||
|
fixed-wide=["dep:fixed_wide","dep:paste"]
|
||||||
|
deferred-division=["dep:ratio_ops"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true }
|
||||||
|
fixed_wide = { version = "0.1.0", path = "../fixed_wide", registry = "strafesnet", optional = true }
|
||||||
|
paste = { version = "1.0.15", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
fixed_wide = { version = "0.1.0", path = "../fixed_wide", registry = "strafesnet", features = ["wide-mul"] }
|
176
linear_ops/LICENSE-APACHE
Normal file
176
linear_ops/LICENSE-APACHE
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
23
linear_ops/LICENSE-MIT
Normal file
23
linear_ops/LICENSE-MIT
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any
|
||||||
|
person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without
|
||||||
|
limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software
|
||||||
|
is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice
|
||||||
|
shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
10
linear_ops/src/lib.rs
Normal file
10
linear_ops/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
mod macros;
|
||||||
|
pub mod types;
|
||||||
|
pub mod vector;
|
||||||
|
pub mod matrix;
|
||||||
|
|
||||||
|
#[cfg(feature="named-fields")]
|
||||||
|
mod named;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
1
linear_ops/src/macros/common.rs
Normal file
1
linear_ops/src/macros/common.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
79
linear_ops/src/macros/fixed_wide.rs
Normal file
79
linear_ops/src/macros/fixed_wide.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#[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>]())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
272
linear_ops/src/macros/matrix.rs
Normal file
272
linear_ops/src/macros/matrix.rs
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
#[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],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
linear_ops/src/macros/mod.rs
Normal file
20
linear_ops/src/macros/mod.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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{
|
||||||
|
(
|
||||||
|
$macro:ident,
|
||||||
|
$any:tt,
|
||||||
|
$($repeated:tt),*
|
||||||
|
)=>{
|
||||||
|
$(
|
||||||
|
$crate::$macro!($any, $repeated);
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
357
linear_ops/src/macros/vector.rs
Normal file
357
linear_ops/src/macros/vector.rs
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
#[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);
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[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,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
linear_ops/src/matrix.rs
Normal file
17
linear_ops/src/matrix.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
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;Y];X],
|
||||||
|
}
|
||||||
|
|
||||||
|
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!();
|
59
linear_ops/src/named.rs
Normal file
59
linear_ops/src/named.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
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)
|
||||||
|
)
|
||||||
|
);
|
96
linear_ops/src/tests/fixed_wide.rs
Normal file
96
linear_ops/src/tests/fixed_wide.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use crate::types::{Matrix3,Matrix3x2,Matrix3x4,Matrix4x2,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=Matrix3x4::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([
|
||||||
|
[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;
|
||||||
|
//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([
|
||||||
|
[Planar64Wide1::from(50),Planar64Wide1::from(60)],
|
||||||
|
[Planar64Wide1::from(114),Planar64Wide1::from(140)],
|
||||||
|
[Planar64Wide1::from(178),Planar64Wide1::from(220)],
|
||||||
|
]).transpose().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
|
||||||
|
);
|
||||||
|
}
|
6
linear_ops/src/tests/mod.rs
Normal file
6
linear_ops/src/tests/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
mod tests;
|
||||||
|
|
||||||
|
#[cfg(feature="named-fields")]
|
||||||
|
mod named;
|
||||||
|
|
||||||
|
mod fixed_wide;
|
30
linear_ops/src/tests/named.rs
Normal file
30
linear_ops/src/tests/named.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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);
|
||||||
|
}
|
59
linear_ops/src/tests/tests.rs
Normal file
59
linear_ops/src/tests/tests.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use crate::types::{Vector2,Vector3,Matrix3x4,Matrix4x2,Matrix3x2,Matrix2x3};
|
||||||
|
|
||||||
|
#[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=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([
|
||||||
|
[ 1.0, 2.0],
|
||||||
|
[ 3.0, 4.0],
|
||||||
|
[ 5.0, 6.0],
|
||||||
|
[ 7.0, 8.0],
|
||||||
|
]).transpose(); // | | |
|
||||||
|
let lhs=Matrix3x4::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;
|
||||||
|
//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([
|
||||||
|
[50.0,60.0],
|
||||||
|
[114.0,140.0],
|
||||||
|
[178.0,220.0],
|
||||||
|
]).transpose().array
|
||||||
|
);
|
||||||
|
}
|
18
linear_ops/src/types.rs
Normal file
18
linear_ops/src/types.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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>;
|
19
linear_ops/src/vector.rs
Normal file
19
linear_ops/src/vector.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/// 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!();
|
@ -3,12 +3,5 @@
|
|||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deferred_division"
|
name = "ratio_ops"
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"fixed_wide_traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fixed_wide_traits"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
10
ratio_ops/Cargo.toml
Normal file
10
ratio_ops/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "ratio_ops"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
repository = "https://git.itzana.me/StrafesNET/fixed_wide_vectors"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "Ratio operations using trait bounds for avoiding division like the plague."
|
||||||
|
authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
176
ratio_ops/LICENSE-APACHE
Normal file
176
ratio_ops/LICENSE-APACHE
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
23
ratio_ops/LICENSE-MIT
Normal file
23
ratio_ops/LICENSE-MIT
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any
|
||||||
|
person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without
|
||||||
|
limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software
|
||||||
|
is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice
|
||||||
|
shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
4
ratio_ops/src/lib.rs
Normal file
4
ratio_ops/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod ratio;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
297
ratio_ops/src/ratio.rs
Normal file
297
ratio_ops/src/ratio.rs
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
#[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<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialEq<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
|
||||||
|
where
|
||||||
|
LhsNum:Copy,
|
||||||
|
LhsDen:Copy,
|
||||||
|
RhsNum:Copy,
|
||||||
|
RhsDen:Copy,
|
||||||
|
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
||||||
|
RhsNum:core::ops::Mul<LhsDen,Output=U>,
|
||||||
|
T:PartialEq<U>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self,other:&Ratio<RhsNum,RhsDen>)->bool{
|
||||||
|
(self.num*other.den).eq(&(other.num*self.den))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Num,Den> Eq for Ratio<Num,Den> where Self:PartialEq{}
|
||||||
|
|
||||||
|
impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialOrd<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
|
||||||
|
where
|
||||||
|
LhsNum:Copy,
|
||||||
|
LhsDen:Copy,
|
||||||
|
RhsNum:Copy,
|
||||||
|
RhsDen:Copy,
|
||||||
|
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
||||||
|
RhsNum:core::ops::Mul<LhsDen,Output=U>,
|
||||||
|
T:PartialOrd<U>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self,other:&Ratio<RhsNum,RhsDen>)->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))
|
||||||
|
}
|
||||||
|
}
|
58
ratio_ops/src/tests.rs
Normal file
58
ratio_ops/src/tests.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
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);
|
||||||
|
// }
|
Loading…
Reference in New Issue
Block a user