mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
Merge pull request #43 from gwilymk/add-rem-and-rem-euclid
Add rem and rem euclid implementations for fixnum
This commit is contained in:
commit
e2925eb917
|
@ -1,9 +1,9 @@
|
||||||
use core::{
|
use core::{
|
||||||
fmt::Display,
|
fmt::{Debug, Display},
|
||||||
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
|
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Num<const N: usize>(i32);
|
pub struct Num<const N: usize>(i32);
|
||||||
|
|
||||||
pub fn change_base<const N: usize, const M: usize>(num: Num<N>) -> Num<M> {
|
pub fn change_base<const N: usize, const M: usize>(num: Num<N>) -> Num<M> {
|
||||||
|
@ -96,6 +96,25 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> Rem<T> for Num<N>
|
||||||
|
where
|
||||||
|
T: Into<Num<N>>,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
fn rem(self, modulus: T) -> Self::Output {
|
||||||
|
Num(self.0 % modulus.into().0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> RemAssign<T> for Num<N>
|
||||||
|
where
|
||||||
|
T: Into<Num<N>>,
|
||||||
|
{
|
||||||
|
fn rem_assign(&mut self, modulus: T) {
|
||||||
|
self.0 = (*self % modulus).0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<const N: usize> Neg for Num<N> {
|
impl<const N: usize> Neg for Num<N> {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
|
@ -107,12 +126,33 @@ impl<const N: usize> Num<N> {
|
||||||
pub fn max() -> Self {
|
pub fn max() -> Self {
|
||||||
Num(i32::MAX)
|
Num(i32::MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn min() -> Self {
|
pub fn min() -> Self {
|
||||||
Num(i32::MIN)
|
Num(i32::MIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn int(&self) -> i32 {
|
pub fn int(&self) -> i32 {
|
||||||
self.0 >> N
|
let fractional_part = self.0 & ((1 << N) - 1);
|
||||||
|
let self_as_int = self.0 >> N;
|
||||||
|
|
||||||
|
if self_as_int < 0 && fractional_part != 0 {
|
||||||
|
self_as_int + 1
|
||||||
|
} else {
|
||||||
|
self_as_int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rem_euclid(&self, rhs: Self) -> Self {
|
||||||
|
let r = *self % rhs;
|
||||||
|
if r < 0.into() {
|
||||||
|
if rhs < 0.into() {
|
||||||
|
r - rhs
|
||||||
|
} else {
|
||||||
|
r + rhs
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(integral: i32) -> Self {
|
pub fn new(integral: i32) -> Self {
|
||||||
|
@ -183,6 +223,71 @@ fn test_change_base(_gba: &mut super::Gba) {
|
||||||
assert_eq!(three + change_base(two), 5.into());
|
assert_eq!(three + change_base(two), 5.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn test_rem_returns_sensible_values_for_integers(_gba: &mut super::Gba) {
|
||||||
|
for i in -50..50 {
|
||||||
|
for j in -50..50 {
|
||||||
|
if j == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i_rem_j_normally = i % j;
|
||||||
|
let i_fixnum: Num<8> = i.into();
|
||||||
|
|
||||||
|
assert_eq!(i_fixnum % j, i_rem_j_normally.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn test_rem_returns_sensible_values_for_non_integers(_gba: &mut super::Gba) {
|
||||||
|
let one: Num<8> = 1.into();
|
||||||
|
let third = one / 3;
|
||||||
|
|
||||||
|
for i in -50..50 {
|
||||||
|
for j in -50..50 {
|
||||||
|
if j == 0 {
|
||||||
|
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).int().into();
|
||||||
|
|
||||||
|
let remainder = x - truncated_division * y;
|
||||||
|
|
||||||
|
assert_eq!(x % y, remainder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn test_rem_euclid_is_always_positive_and_sensible(_gba: &mut super::Gba) {
|
||||||
|
let one: Num<8> = 1.into();
|
||||||
|
let third = one / 3;
|
||||||
|
|
||||||
|
for i in -50..50 {
|
||||||
|
for j in -50..50 {
|
||||||
|
if j == 0 {
|
||||||
|
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).int().into();
|
||||||
|
|
||||||
|
let remainder = x - truncated_division * y;
|
||||||
|
|
||||||
|
let rem_euclid = x.rem_euclid(y);
|
||||||
|
assert!(rem_euclid > 0.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<const N: usize> Display for Num<N> {
|
impl<const N: usize> Display for Num<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;
|
||||||
|
@ -203,3 +308,9 @@ impl<const N: usize> Display for Num<N> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Debug for Num<N> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "Num<{}>({})", N, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue