Make number generic on i32

This commit is contained in:
Gwilym Kuiper 2021-06-05 21:11:08 +01:00
parent ec87adceb2
commit de47dbc5dd
2 changed files with 130 additions and 63 deletions

View file

@ -1,12 +1,63 @@
use core::{ use core::{
cmp::{Eq, Ord, PartialEq, PartialOrd},
fmt::{Debug, Display}, fmt::{Debug, Display},
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, ops::{
Add, AddAssign, BitAnd, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, Shr,
Sub, SubAssign,
},
}; };
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub trait FixedWidthInteger:
pub struct Num<const N: usize>(i32); Sized
+ Copy
+ PartialOrd
+ Ord
+ PartialEq
+ Eq
+ Shl<usize, Output = Self>
+ Shr<usize, Output = Self>
+ Add<Output = Self>
+ Sub<Output = Self>
+ Not<Output = Self>
+ BitAnd<Output = Self>
+ Neg<Output = Self>
+ Rem<Output = Self>
+ Div<Output = Self>
+ Mul<Output = Self>
+ From<u8>
+ Debug
+ Display
{
fn zero() -> Self;
fn one() -> Self;
fn ten() -> Self;
fn abs(self) -> Self;
}
pub fn change_base<const N: usize, const M: usize>(num: Num<N>) -> Num<M> { impl FixedWidthInteger for i32 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
fn ten() -> Self {
10
}
fn abs(self) -> Self {
self.abs()
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Num<I: FixedWidthInteger, const N: usize>(I);
pub fn change_base<I: FixedWidthInteger, const N: usize, const M: usize>(
num: Num<I, N>,
) -> Num<I, M> {
if N < M { if N < M {
Num(num.0 << (M - N)) Num(num.0 << (M - N))
} else { } else {
@ -14,15 +65,16 @@ pub fn change_base<const N: usize, const M: usize>(num: Num<N>) -> Num<M> {
} }
} }
impl<const N: usize> From<i32> for Num<N> { impl<I: FixedWidthInteger, const N: usize> From<I> for Num<I, N> {
fn from(value: i32) -> Self { fn from(value: I) -> Self {
Num(value << N) Num(value << N)
} }
} }
impl<T, const N: usize> Add<T> for Num<N> impl<I, T, const N: usize> Add<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
type Output = Self; type Output = Self;
fn add(self, rhs: T) -> Self::Output { fn add(self, rhs: T) -> Self::Output {
@ -30,18 +82,20 @@ where
} }
} }
impl<T, const N: usize> AddAssign<T> for Num<N> impl<I, T, const N: usize> AddAssign<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
fn add_assign(&mut self, rhs: T) { fn add_assign(&mut self, rhs: T) {
self.0 = (*self + rhs.into()).0 self.0 = (*self + rhs.into()).0
} }
} }
impl<T, const N: usize> Sub<T> for Num<N> impl<I, T, const N: usize> Sub<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
type Output = Self; type Output = Self;
fn sub(self, rhs: T) -> Self::Output { fn sub(self, rhs: T) -> Self::Output {
@ -49,18 +103,20 @@ where
} }
} }
impl<T, const N: usize> SubAssign<T> for Num<N> impl<I, T, const N: usize> SubAssign<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
fn sub_assign(&mut self, rhs: T) { fn sub_assign(&mut self, rhs: T) {
self.0 = (*self - rhs.into()).0 self.0 = (*self - rhs.into()).0
} }
} }
impl<T, const N: usize> Mul<T> for Num<N> impl<I, T, const N: usize> Mul<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
type Output = Self; type Output = Self;
fn mul(self, rhs: T) -> Self::Output { fn mul(self, rhs: T) -> Self::Output {
@ -68,18 +124,20 @@ where
} }
} }
impl<T, const N: usize> MulAssign<T> for Num<N> impl<I, T, const N: usize> MulAssign<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
fn mul_assign(&mut self, rhs: T) { fn mul_assign(&mut self, rhs: T) {
self.0 = (*self * rhs.into()).0 self.0 = (*self * rhs.into()).0
} }
} }
impl<T, const N: usize> Div<T> for Num<N> impl<I, T, const N: usize> Div<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
type Output = Self; type Output = Self;
fn div(self, rhs: T) -> Self::Output { fn div(self, rhs: T) -> Self::Output {
@ -87,18 +145,20 @@ where
} }
} }
impl<T, const N: usize> DivAssign<T> for Num<N> impl<I, T, const N: usize> DivAssign<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
fn div_assign(&mut self, rhs: T) { fn div_assign(&mut self, rhs: T) {
self.0 = (*self / rhs.into()).0 self.0 = (*self / rhs.into()).0
} }
} }
impl<T, const N: usize> Rem<T> for Num<N> impl<I, T, const N: usize> Rem<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
type Output = Self; type Output = Self;
fn rem(self, modulus: T) -> Self::Output { fn rem(self, modulus: T) -> Self::Output {
@ -106,45 +166,38 @@ where
} }
} }
impl<T, const N: usize> RemAssign<T> for Num<N> impl<I, T, const N: usize> RemAssign<T> for Num<I, N>
where where
T: Into<Num<N>>, I: FixedWidthInteger,
T: Into<Num<I, N>>,
{ {
fn rem_assign(&mut self, modulus: T) { fn rem_assign(&mut self, modulus: T) {
self.0 = (*self % modulus).0 self.0 = (*self % modulus).0
} }
} }
impl<const N: usize> Neg for Num<N> { impl<I: FixedWidthInteger, const N: usize> Neg for Num<I, N> {
type Output = Self; type Output = Self;
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
Num(-self.0) Num(-self.0)
} }
} }
impl<const N: usize> Num<N> { impl<I: FixedWidthInteger, const N: usize> Num<I, N> {
pub const fn max() -> Self { pub fn from_raw(n: I) -> Self {
Num(i32::MAX)
}
pub const fn min() -> Self {
Num(i32::MIN)
}
pub const fn from_raw(n: i32) -> Self {
Num(n) Num(n)
} }
pub const fn to_raw(&self) -> i32 { pub fn to_raw(&self) -> I {
self.0 self.0
} }
pub const fn trunc(&self) -> i32 { pub fn int(&self) -> I {
let fractional_part = self.0 & ((1 << N) - 1); let fractional_part = self.0 & ((I::one() << N) - I::one());
let self_as_int = self.0 >> N; let self_as_int = self.0 >> N;
if self_as_int < 0 && fractional_part != 0 { if self_as_int < I::zero() && fractional_part != I::zero() {
self_as_int + 1 self_as_int + I::one()
} else { } else {
self_as_int self_as_int
} }
@ -152,8 +205,8 @@ impl<const N: usize> Num<N> {
pub fn rem_euclid(&self, rhs: Self) -> Self { pub fn rem_euclid(&self, rhs: Self) -> Self {
let r = *self % rhs; let r = *self % rhs;
if r < 0.into() { if r < I::zero().into() {
if rhs < 0.into() { if rhs < I::zero().into() {
r - rhs r - rhs
} else { } else {
r + rhs r + rhs
@ -163,31 +216,38 @@ impl<const N: usize> Num<N> {
} }
} }
pub const fn floor(&self) -> i32 { pub fn floor(&self) -> I {
self.0 >> N self.0 >> N
} }
pub const fn abs(self) -> Self { pub fn abs(self) -> Self {
Num(self.0.abs()) Num(self.0.abs())
} }
/// domain of [0, 1]. /// domain of [0, 1].
/// see https://github.com/tarcieri/micromath/blob/24584465b48ff4e87cffb709c7848664db896b4f/src/float/cos.rs#L226 /// see https://github.com/tarcieri/micromath/blob/24584465b48ff4e87cffb709c7848664db896b4f/src/float/cos.rs#L226
pub fn cos(self) -> Self { pub fn cos(self) -> Self {
let one: Self = 1.into(); let one: Self = I::one().into();
let mut x = self; let mut x = self;
x -= one / 4 + (x + one / 4).floor(); let four: I = 4.into();
x *= (x.abs() - one / 2) * 16; let two: I = 2.into();
x += x * (x.abs() - 1) * 9 / 40; let sixteen: I = 16.into();
let nine: I = 9.into();
let forty: I = 40.into();
x -= one / four + (x + one / four).floor();
x *= (x.abs() - one / two) * sixteen;
x += x * (x.abs() - one) * nine / forty;
x x
} }
pub fn sin(self) -> Self { pub fn sin(self) -> Self {
let one: Self = 1.into(); let one: Self = I::one().into();
(self - one / 4).cos() let four: I = 4.into();
(self - one / four).cos()
} }
pub const fn new(integral: i32) -> Self { pub fn new(integral: I) -> Self {
Self(integral << N) Self(integral << N)
} }
} }
@ -320,29 +380,32 @@ fn test_rem_euclid_is_always_positive_and_sensible(_gba: &mut super::Gba) {
} }
} }
impl<const N: usize> Display for Num<N> { impl<I: FixedWidthInteger, const N: usize> Display for Num<I, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let integral = self.0 >> N; let integral = self.0 >> N;
let mask: u32 = (1 << N) - 1; let mask: I = (I::one() << N) - I::one();
write!(f, "{}", integral)?; write!(f, "{}", integral)?;
let mut fractional = self.0 as u32 & mask; let mut fractional = self.0 & mask;
if fractional & mask != 0 { if fractional & mask != I::zero() {
write!(f, ".")?; write!(f, ".")?;
} }
while fractional & mask != 0 {
fractional *= 10; while fractional & mask != I::zero() {
fractional = fractional * I::ten();
write!(f, "{}", (fractional & !mask) >> N)?; write!(f, "{}", (fractional & !mask) >> N)?;
fractional &= mask; fractional = fractional & mask;
} }
Ok(()) Ok(())
} }
} }
impl<const N: usize> Debug for Num<N> { impl<I: FixedWidthInteger, const N: usize> Debug for Num<I, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Num<{}>({})", N, self) use core::any::type_name;
write!(f, "Num<{}, {}>({})", type_name::<I>(), N, self)
} }
} }

View file

@ -106,7 +106,11 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 {
result result
} }
pub fn affine_matrix(x_scale: Num<8>, y_scale: Num<8>, rotation: u8) -> AffineMatrixAttributes { pub fn affine_matrix(
x_scale: Num<i32, 8>,
y_scale: Num<i32, 8>,
rotation: u8,
) -> AffineMatrixAttributes {
let mut result = AffineMatrixAttributes { let mut result = AffineMatrixAttributes {
p_a: 0, p_a: 0,
p_b: 0, p_b: 0,