From de47dbc5dd3a0964ed7aeb88854ba62e67a49d11 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Jun 2021 21:11:08 +0100 Subject: [PATCH 1/6] Make number generic on i32 --- agb/src/number.rs | 187 ++++++++++++++++++++++++++++++--------------- agb/src/syscall.rs | 6 +- 2 files changed, 130 insertions(+), 63 deletions(-) diff --git a/agb/src/number.rs b/agb/src/number.rs index d129b153..f94c2252 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -1,12 +1,63 @@ use core::{ + cmp::{Eq, Ord, PartialEq, PartialOrd}, 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 struct Num(i32); +pub trait FixedWidthInteger: + Sized + + Copy + + PartialOrd + + Ord + + PartialEq + + Eq + + Shl + + Shr + + Add + + Sub + + Not + + BitAnd + + Neg + + Rem + + Div + + Mul + + From + + Debug + + Display +{ + fn zero() -> Self; + fn one() -> Self; + fn ten() -> Self; + fn abs(self) -> Self; +} -pub fn change_base(num: Num) -> Num { +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); + +pub fn change_base( + num: Num, +) -> Num { if N < M { Num(num.0 << (M - N)) } else { @@ -14,15 +65,16 @@ pub fn change_base(num: Num) -> Num { } } -impl From for Num { - fn from(value: i32) -> Self { +impl From for Num { + fn from(value: I) -> Self { Num(value << N) } } -impl Add for Num +impl Add for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { type Output = Self; fn add(self, rhs: T) -> Self::Output { @@ -30,18 +82,20 @@ where } } -impl AddAssign for Num +impl AddAssign for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { fn add_assign(&mut self, rhs: T) { self.0 = (*self + rhs.into()).0 } } -impl Sub for Num +impl Sub for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { type Output = Self; fn sub(self, rhs: T) -> Self::Output { @@ -49,18 +103,20 @@ where } } -impl SubAssign for Num +impl SubAssign for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { fn sub_assign(&mut self, rhs: T) { self.0 = (*self - rhs.into()).0 } } -impl Mul for Num +impl Mul for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { type Output = Self; fn mul(self, rhs: T) -> Self::Output { @@ -68,18 +124,20 @@ where } } -impl MulAssign for Num +impl MulAssign for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { fn mul_assign(&mut self, rhs: T) { self.0 = (*self * rhs.into()).0 } } -impl Div for Num +impl Div for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { type Output = Self; fn div(self, rhs: T) -> Self::Output { @@ -87,18 +145,20 @@ where } } -impl DivAssign for Num +impl DivAssign for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { fn div_assign(&mut self, rhs: T) { self.0 = (*self / rhs.into()).0 } } -impl Rem for Num +impl Rem for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { type Output = Self; fn rem(self, modulus: T) -> Self::Output { @@ -106,45 +166,38 @@ where } } -impl RemAssign for Num +impl RemAssign for Num where - T: Into>, + I: FixedWidthInteger, + T: Into>, { fn rem_assign(&mut self, modulus: T) { self.0 = (*self % modulus).0 } } -impl Neg for Num { +impl Neg for Num { type Output = Self; fn neg(self) -> Self::Output { Num(-self.0) } } -impl Num { - pub const fn max() -> Self { - Num(i32::MAX) - } - - pub const fn min() -> Self { - Num(i32::MIN) - } - - pub const fn from_raw(n: i32) -> Self { +impl Num { + pub fn from_raw(n: I) -> Self { Num(n) } - pub const fn to_raw(&self) -> i32 { + pub fn to_raw(&self) -> I { self.0 } - pub const fn trunc(&self) -> i32 { - let fractional_part = self.0 & ((1 << N) - 1); + pub fn int(&self) -> I { + let fractional_part = self.0 & ((I::one() << N) - I::one()); let self_as_int = self.0 >> N; - if self_as_int < 0 && fractional_part != 0 { - self_as_int + 1 + if self_as_int < I::zero() && fractional_part != I::zero() { + self_as_int + I::one() } else { self_as_int } @@ -152,8 +205,8 @@ impl Num { pub fn rem_euclid(&self, rhs: Self) -> Self { let r = *self % rhs; - if r < 0.into() { - if rhs < 0.into() { + if r < I::zero().into() { + if rhs < I::zero().into() { r - rhs } else { r + rhs @@ -163,31 +216,38 @@ impl Num { } } - pub const fn floor(&self) -> i32 { + pub fn floor(&self) -> I { self.0 >> N } - pub const fn abs(self) -> Self { + pub fn abs(self) -> Self { Num(self.0.abs()) } /// domain of [0, 1]. /// see https://github.com/tarcieri/micromath/blob/24584465b48ff4e87cffb709c7848664db896b4f/src/float/cos.rs#L226 pub fn cos(self) -> Self { - let one: Self = 1.into(); + let one: Self = I::one().into(); let mut x = self; - x -= one / 4 + (x + one / 4).floor(); - x *= (x.abs() - one / 2) * 16; - x += x * (x.abs() - 1) * 9 / 40; + let four: I = 4.into(); + let two: I = 2.into(); + 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 } pub fn sin(self) -> Self { - let one: Self = 1.into(); - (self - one / 4).cos() + let one: Self = I::one().into(); + 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) } } @@ -320,29 +380,32 @@ fn test_rem_euclid_is_always_positive_and_sensible(_gba: &mut super::Gba) { } } -impl Display for Num { +impl Display for Num { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let integral = self.0 >> N; - let mask: u32 = (1 << N) - 1; + let mask: I = (I::one() << N) - I::one(); write!(f, "{}", integral)?; - let mut fractional = self.0 as u32 & mask; - if fractional & mask != 0 { + let mut fractional = self.0 & mask; + if fractional & mask != I::zero() { write!(f, ".")?; } - while fractional & mask != 0 { - fractional *= 10; + + while fractional & mask != I::zero() { + fractional = fractional * I::ten(); write!(f, "{}", (fractional & !mask) >> N)?; - fractional &= mask; + fractional = fractional & mask; } Ok(()) } } -impl Debug for Num { +impl Debug for Num { 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::(), N, self) } } diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index b30cb6a4..7474eaae 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -106,7 +106,11 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 { result } -pub fn affine_matrix(x_scale: Num<8>, y_scale: Num<8>, rotation: u8) -> AffineMatrixAttributes { +pub fn affine_matrix( + x_scale: Num, + y_scale: Num, + rotation: u8, +) -> AffineMatrixAttributes { let mut result = AffineMatrixAttributes { p_a: 0, p_b: 0, From 508f33facd5a59a061e89cebe3c5d6835fe3e016 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Jun 2021 21:17:24 +0100 Subject: [PATCH 2/6] Add support for all the integer widths --- agb/src/number.rs | 102 +++++++++++++++++++++++++++------------------ agb/src/syscall.rs | 14 +++---- 2 files changed, 68 insertions(+), 48 deletions(-) diff --git a/agb/src/number.rs b/agb/src/number.rs index f94c2252..1ff3ab0e 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -7,7 +7,7 @@ use core::{ }, }; -pub trait FixedWidthInteger: +pub trait FixedWidthUnsignedInteger: Sized + Copy + PartialOrd @@ -20,7 +20,6 @@ pub trait FixedWidthInteger: + Sub + Not + BitAnd - + Neg + Rem + Div + Mul @@ -31,31 +30,50 @@ pub trait FixedWidthInteger: fn zero() -> Self; fn one() -> Self; fn ten() -> Self; - fn abs(self) -> Self; } -impl FixedWidthInteger for i32 { - fn zero() -> Self { - 0 - } - - fn one() -> Self { - 1 - } - - fn ten() -> Self { - 10 - } - - fn abs(self) -> Self { - self.abs() - } +pub trait FixedWidthSignedInteger: FixedWidthUnsignedInteger + Neg { + fn fixed_abs(self) -> Self; } +macro_rules! fixed_width_unsigned_integer_impl { + ($T: ty) => { + impl FixedWidthUnsignedInteger for $T { + fn zero() -> Self { + 0 + } + fn one() -> Self { + 1 + } + fn ten() -> Self { + 10 + } + } + }; +} + +macro_rules! fixed_width_signed_integer_impl { + ($T: ty) => { + impl FixedWidthSignedInteger for $T { + fn fixed_abs(self) -> Self { + self.abs() + } + } + }; +} + +fixed_width_unsigned_integer_impl!(i16); +fixed_width_unsigned_integer_impl!(u16); +fixed_width_unsigned_integer_impl!(i32); +fixed_width_unsigned_integer_impl!(u32); + +fixed_width_signed_integer_impl!(i16); +fixed_width_signed_integer_impl!(i32); + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct Num(I); +pub struct Num(I); -pub fn change_base( +pub fn change_base( num: Num, ) -> Num { if N < M { @@ -65,7 +83,7 @@ pub fn change_base( } } -impl From for Num { +impl From for Num { fn from(value: I) -> Self { Num(value << N) } @@ -73,7 +91,7 @@ impl From for Num { impl Add for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { type Output = Self; @@ -84,7 +102,7 @@ where impl AddAssign for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { fn add_assign(&mut self, rhs: T) { @@ -94,7 +112,7 @@ where impl Sub for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { type Output = Self; @@ -105,7 +123,7 @@ where impl SubAssign for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { fn sub_assign(&mut self, rhs: T) { @@ -115,7 +133,7 @@ where impl Mul for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { type Output = Self; @@ -126,7 +144,7 @@ where impl MulAssign for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { fn mul_assign(&mut self, rhs: T) { @@ -136,7 +154,7 @@ where impl Div for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { type Output = Self; @@ -147,7 +165,7 @@ where impl DivAssign for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { fn div_assign(&mut self, rhs: T) { @@ -157,7 +175,7 @@ where impl Rem for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { type Output = Self; @@ -168,7 +186,7 @@ where impl RemAssign for Num where - I: FixedWidthInteger, + I: FixedWidthUnsignedInteger, T: Into>, { fn rem_assign(&mut self, modulus: T) { @@ -176,14 +194,14 @@ where } } -impl Neg for Num { +impl Neg for Num { type Output = Self; fn neg(self) -> Self::Output { Num(-self.0) } } -impl Num { +impl Num { pub fn from_raw(n: I) -> Self { Num(n) } @@ -220,8 +238,14 @@ impl Num { self.0 >> N } + pub fn new(integral: I) -> Self { + Self(integral << N) + } +} + +impl Num { pub fn abs(self) -> Self { - Num(self.0.abs()) + Num(self.0.fixed_abs()) } /// domain of [0, 1]. @@ -246,10 +270,6 @@ impl Num { let four: I = 4.into(); (self - one / four).cos() } - - pub fn new(integral: I) -> Self { - Self(integral << N) - } } #[test_case] @@ -380,7 +400,7 @@ fn test_rem_euclid_is_always_positive_and_sensible(_gba: &mut super::Gba) { } } -impl Display for Num { +impl Display for Num { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let integral = self.0 >> N; let mask: I = (I::one() << N) - I::one(); @@ -402,7 +422,7 @@ impl Display for Num { } } -impl Debug for Num { +impl Debug for Num { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use core::any::type_name; diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index 7474eaae..49e1fc53 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -107,8 +107,8 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 { } pub fn affine_matrix( - x_scale: Num, - y_scale: Num, + x_scale: Num, + y_scale: Num, rotation: u8, ) -> AffineMatrixAttributes { let mut result = AffineMatrixAttributes { @@ -129,8 +129,8 @@ pub fn affine_matrix( let rotation_for_input = (rotation as u16) << 8; let input = Input { - y_scale: x_scale.to_raw() as i16, - x_scale: y_scale.to_raw() as i16, + y_scale: x_scale.to_raw(), + x_scale: y_scale.to_raw(), rotation: rotation_for_input, }; @@ -149,9 +149,9 @@ pub fn affine_matrix( #[test_case] fn affine(_gba: &mut crate::Gba) { // expect identity matrix - let one: Num<8> = 1.into(); + let one: Num = 1.into(); let aff = affine_matrix(one, one, 0); - assert_eq!(aff.p_a, one.to_raw() as i16); - assert_eq!(aff.p_d, one.to_raw() as i16); + assert_eq!(aff.p_a, one.to_raw()); + assert_eq!(aff.p_d, one.to_raw()); } From 174517fbb18cac6fd5ca1a1968fd6b0c6327f2b1 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Jun 2021 21:18:42 +0100 Subject: [PATCH 3/6] Fix tests --- agb/src/number.rs | 51 +++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/agb/src/number.rs b/agb/src/number.rs index 1ff3ab0e..7ddf66cb 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -210,7 +210,7 @@ impl Num { self.0 } - pub fn int(&self) -> I { + pub fn trunc(&self) -> I { let fractional_part = self.0 & ((I::one() << N) - I::one()); let self_as_int = self.0 >> N; @@ -275,16 +275,16 @@ impl Num { #[test_case] fn test_numbers(_gba: &mut super::Gba) { // test addition - let n: Num<8> = 1.into(); + let n: Num = 1.into(); assert_eq!(n + 2, 3.into(), "testing that 1 + 2 == 3"); // test multiplication - let n: Num<8> = 5.into(); + let n: Num = 5.into(); assert_eq!(n * 3, 15.into(), "testing that 5 * 3 == 15"); // test division - let n: Num<8> = 30.into(); - let p: Num<8> = 3.into(); + let n: Num = 30.into(); + let p: Num = 3.into(); assert_eq!(n / 20, p / 2, "testing that 30 / 20 == 3 / 2"); assert_ne!(n, p, "testing that 30 != 3"); @@ -292,20 +292,20 @@ fn test_numbers(_gba: &mut super::Gba) { #[test_case] fn test_division_by_one(_gba: &mut super::Gba) { - let one: Num<8> = 1.into(); + let one: Num = 1.into(); for i in -40..40 { - let n: Num<8> = i.into(); + let n: Num = i.into(); assert_eq!(n / one, n); } } #[test_case] fn test_division_and_multiplication_by_16(_gba: &mut super::Gba) { - let sixteen: Num<8> = 16.into(); + let sixteen: Num = 16.into(); for i in -40..40 { - let n: Num<8> = i.into(); + let n: Num = i.into(); let m = n / sixteen; assert_eq!(m * sixteen, n); @@ -314,12 +314,12 @@ fn test_division_and_multiplication_by_16(_gba: &mut super::Gba) { #[test_case] fn test_division_by_2_and_15(_gba: &mut super::Gba) { - let two: Num<8> = 2.into(); - let fifteen: Num<8> = 15.into(); - let thirty: Num<8> = 30.into(); + let two: Num = 2.into(); + let fifteen: Num = 15.into(); + let thirty: Num = 30.into(); for i in -128..128 { - let n: Num<8> = i.into(); + let n: Num = i.into(); assert_eq!(n / two / fifteen, n / thirty); assert_eq!(n / fifteen / two, n / thirty); @@ -328,8 +328,8 @@ fn test_division_by_2_and_15(_gba: &mut super::Gba) { #[test_case] fn test_change_base(_gba: &mut super::Gba) { - let two: Num<9> = 2.into(); - let three: Num<4> = 3.into(); + let two: Num = 2.into(); + let three: Num = 3.into(); assert_eq!(two + change_base(three), 5.into()); assert_eq!(three + change_base(two), 5.into()); @@ -344,7 +344,7 @@ fn test_rem_returns_sensible_values_for_integers(_gba: &mut super::Gba) { } let i_rem_j_normally = i % j; - let i_fixnum: Num<8> = i.into(); + let i_fixnum: Num = i.into(); assert_eq!(i_fixnum % j, i_rem_j_normally.into()); } @@ -353,7 +353,7 @@ fn test_rem_returns_sensible_values_for_integers(_gba: &mut super::Gba) { #[test_case] fn test_rem_returns_sensible_values_for_non_integers(_gba: &mut super::Gba) { - let one: Num<8> = 1.into(); + let one: Num = 1.into(); let third = one / 3; for i in -50..50 { @@ -363,10 +363,10 @@ fn test_rem_returns_sensible_values_for_non_integers(_gba: &mut super::Gba) { } // full calculation in the normal way - let x: Num<8> = third + i; - let y: Num<8> = j.into(); + let x: Num = third + i; + let y: Num = j.into(); - let truncated_division: Num<8> = (x / y).trunc().into(); + let truncated_division: Num = (x / y).trunc().into(); let remainder = x - truncated_division * y; @@ -377,7 +377,7 @@ fn test_rem_returns_sensible_values_for_non_integers(_gba: &mut super::Gba) { #[test_case] fn test_rem_euclid_is_always_positive_and_sensible(_gba: &mut super::Gba) { - let one: Num<8> = 1.into(); + let one: Num = 1.into(); let third = one / 3; for i in -50..50 { @@ -386,13 +386,8 @@ fn test_rem_euclid_is_always_positive_and_sensible(_gba: &mut super::Gba) { continue; } - // full calculation in the normal way - let x: Num<8> = third + i; - let y: Num<8> = j.into(); - - let truncated_division: Num<8> = (x / y).trunc().into(); - - let remainder = x - truncated_division * y; + let x: Num = third + i; + let y: Num = j.into(); let rem_euclid = x.rem_euclid(y); assert!(rem_euclid > 0.into()); From 54e28f5dd49b7695340a00f1f8a4a3e46f396aa8 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Jun 2021 21:43:54 +0100 Subject: [PATCH 4/6] Add a Number type for i32 --- agb/src/number.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agb/src/number.rs b/agb/src/number.rs index 7ddf66cb..46522e2b 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -73,6 +73,8 @@ fixed_width_signed_integer_impl!(i32); #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Num(I); +pub type Number = Num; + pub fn change_base( num: Num, ) -> Num { From aa39b2b6ad79e179489f80d6334b070536b2b9d5 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Jun 2021 21:49:36 +0100 Subject: [PATCH 5/6] Make to_raw take self by value rather than reference --- agb/src/number.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/number.rs b/agb/src/number.rs index 46522e2b..588e7f75 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -208,7 +208,7 @@ impl Num { Num(n) } - pub fn to_raw(&self) -> I { + pub fn to_raw(self) -> I { self.0 } From 3357a4b69d91fab01e0e7d11c7ff7894c7cb1087 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 5 Jun 2021 21:58:32 +0100 Subject: [PATCH 6/6] Put brackets around nine / forty to ensure that we don't divide where we don't have to --- agb/src/number.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/number.rs b/agb/src/number.rs index 588e7f75..8be2b96a 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -263,7 +263,7 @@ impl Num { x -= one / four + (x + one / four).floor(); x *= (x.abs() - one / two) * sixteen; - x += x * (x.abs() - one) * nine / forty; + x += x * (x.abs() - one) * (nine / forty); x }