mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 16:44:33 +00:00
Trying out stack_fraction
This commit is contained in:
parent
13c9d41b95
commit
ae1a650b4d
6 changed files with 599 additions and 0 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -204,6 +204,7 @@ dependencies = [
|
||||||
"im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,6 +10,7 @@ petgraph = { version = "0.4.5", optional = true }
|
||||||
combine = "3.8.1"
|
combine = "3.8.1"
|
||||||
im-rc = "13.0.0"
|
im-rc = "13.0.0"
|
||||||
fraction = "0.6.2"
|
fraction = "0.6.2"
|
||||||
|
num = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
|
|
10
NOTICE
Normal file
10
NOTICE
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Notice of copyrights and license use. Thank you to everyone who has contributed
|
||||||
|
to this project, either directly or indirectly!
|
||||||
|
|
||||||
|
## Licensed from Apache 2.0 projects
|
||||||
|
|
||||||
|
Source code from the following projects has been included inline in this project,
|
||||||
|
licensed under the Apache License, version 2.0. A full copy of the License
|
||||||
|
can be found here: http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
1. The num-integer crate: https://docs.rs/num-integer/0.1.41/src/num_integer/
|
|
@ -2,6 +2,7 @@ pub mod expr;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod parse_state;
|
pub mod parse_state;
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
|
pub mod stack_fraction;
|
||||||
// mod ena;
|
// mod ena;
|
||||||
|
|
||||||
// #[macro_use]
|
// #[macro_use]
|
||||||
|
@ -12,5 +13,6 @@ extern crate dogged;
|
||||||
|
|
||||||
extern crate im_rc;
|
extern crate im_rc;
|
||||||
extern crate fraction;
|
extern crate fraction;
|
||||||
|
extern crate num;
|
||||||
|
|
||||||
#[macro_use] extern crate combine;
|
#[macro_use] extern crate combine;
|
||||||
|
|
584
src/stack_fraction.rs
Normal file
584
src/stack_fraction.rs
Normal file
|
@ -0,0 +1,584 @@
|
||||||
|
use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::ops::Neg;
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
use std::f64;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Fraction {
|
||||||
|
numerator: i64,
|
||||||
|
denominator: i64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fraction {
|
||||||
|
/// Returns a new Fraction unless the given denominator was zero,
|
||||||
|
/// in which case returns None.
|
||||||
|
///
|
||||||
|
/// For a more efficient (but astronomically less safe) alternative,
|
||||||
|
/// see new_prereduced_with_positive_denominator.
|
||||||
|
pub fn new(numerator: i64, denominator: i64) -> Option<Fraction> {
|
||||||
|
if denominator.is_positive() {
|
||||||
|
Some(Fraction { numerator, denominator })
|
||||||
|
} else if denominator == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Denominator may never be negative. This lets us avoid -0,
|
||||||
|
// get the sign merely by returning the numerator's sign, etc.
|
||||||
|
Some(Fraction { numerator: -numerator, denominator: -denominator })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Doesn't do any checks in release builds, but panics in debug builds if
|
||||||
|
/// the denominator is not positive.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_from_positive_denominator(numerator: i64, denominator: i64) -> Fraction {
|
||||||
|
assert!(denominator > 0);
|
||||||
|
|
||||||
|
Fraction { numerator, denominator }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Reduces the fraction in place.
|
||||||
|
pub fn reduce(&mut self) {
|
||||||
|
let common_divisor = gcd(self.numerator, self.denominator);
|
||||||
|
|
||||||
|
self.numerator = self.numerator / common_divisor;
|
||||||
|
self.denominator = self.denominator / common_divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Reduces the fraction, then returns the numerator.
|
||||||
|
pub fn reduced_numerator(&mut self) -> i64 {
|
||||||
|
self.reduce();
|
||||||
|
self.numerator
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Reduces the fraction, then returns the denominator.
|
||||||
|
pub fn reduced_denominator(&mut self) -> i64 {
|
||||||
|
self.reduce();
|
||||||
|
self.denominator
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Reduces the fraction, then returns a tuple of (numerator, denominator).
|
||||||
|
pub fn reduced(&mut self) -> ( i64, i64 ) {
|
||||||
|
self.reduce();
|
||||||
|
( self.numerator, self.denominator )
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Reduces the fraction, then returns true iff the denominator is 1.
|
||||||
|
pub fn is_integer(&mut self) -> bool {
|
||||||
|
self.reduce();
|
||||||
|
self.denominator == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Returns true iff the numerator is zero, without reducing first.
|
||||||
|
/// This is more efficient than getting the reduced numerator and then
|
||||||
|
/// comparing it to 0, because that has to execute the reduce operation.
|
||||||
|
pub fn is_zero(self) -> bool {
|
||||||
|
self.numerator == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add two fractions, returning None on overflow. Note: overflow can occur on more than just large numerators!
|
||||||
|
/// This requires mutable references because it may decide to reduce
|
||||||
|
/// the fractions mid-operation, and that reduction should persist to avoid having to redo
|
||||||
|
/// that calculation later.
|
||||||
|
pub fn checked_add(&mut self, other: &mut Self) -> Option<Self> {
|
||||||
|
if self.denominator == other.denominator {
|
||||||
|
match self.numerator.checked_add(other.numerator) {
|
||||||
|
// Happy path - we get to skip calculating a common denominator!
|
||||||
|
Some(numerator) => Some(Fraction { numerator, denominator: self.denominator }),
|
||||||
|
None => self.reducing_checked_add(other)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let common_denom: i64 = self.denominator * other.denominator;
|
||||||
|
|
||||||
|
(common_denom / self.denominator).checked_mul(self.numerator)
|
||||||
|
.and_then(|self_numer| {
|
||||||
|
(common_denom / other.denominator).checked_mul(other.numerator)
|
||||||
|
.and_then(|other_numer| {
|
||||||
|
self_numer.checked_add(other_numer)
|
||||||
|
.map(|numerator| Fraction { numerator, denominator: common_denom })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// Something overflowed - try reducing the inputs first.
|
||||||
|
.or_else(|| self.reducing_checked_add(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add two fractions. This requires mutable references because it may decide to reduce
|
||||||
|
/// the fractions mid-operation, and that reduction should persist to avoid having to redo
|
||||||
|
/// that calculation later.
|
||||||
|
pub fn add(&mut self, other: &mut Self) -> Self {
|
||||||
|
if self.denominator == other.denominator {
|
||||||
|
match self.numerator.checked_add(other.numerator) {
|
||||||
|
// Happy path - we get to skip calculating a common denominator!
|
||||||
|
Some(numerator) => Fraction { numerator, denominator: self.denominator },
|
||||||
|
None => self.reducing_add(other)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let common_denom: i64 = self.denominator * other.denominator;
|
||||||
|
|
||||||
|
(common_denom / self.denominator).checked_mul(self.numerator)
|
||||||
|
.and_then(|self_numer| {
|
||||||
|
(common_denom / other.denominator).checked_mul(other.numerator)
|
||||||
|
.and_then(|other_numer| {
|
||||||
|
self_numer.checked_add(other_numer)
|
||||||
|
.map(|numerator| Fraction { numerator, denominator: common_denom })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// Something overflowed - try reducing the inputs first.
|
||||||
|
.unwrap_or_else(|| self.reducing_add(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add while sacrificing performance to avoid overflow.
|
||||||
|
/// This should only be used as a fallback after an overflow was caught in a higher-perf arithmetic operation.
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reducing_add(&mut self, other: &mut Self) -> Self {
|
||||||
|
self.reduce();
|
||||||
|
other.reduce();
|
||||||
|
|
||||||
|
let denominator = lcm(self.denominator, other.denominator);
|
||||||
|
let numerator =
|
||||||
|
(self.numerator * (denominator / self.denominator))
|
||||||
|
+ (other.numerator * (denominator / other.denominator));
|
||||||
|
|
||||||
|
Fraction { numerator, denominator }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add while sacrificing performance to avoid overflow.
|
||||||
|
/// This should only be used as a fallback after an overflow was caught in a higher-perf arithmetic operation.
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reducing_checked_add(&mut self, other: &mut Self) -> Option<Self> {
|
||||||
|
self.reduce();
|
||||||
|
other.reduce();
|
||||||
|
|
||||||
|
let denominator = lcm(self.denominator, other.denominator);
|
||||||
|
|
||||||
|
(denominator / self.denominator).checked_mul(self.numerator).and_then(|self_numerator|
|
||||||
|
(denominator / other.denominator).checked_mul(other.numerator).and_then(|other_numerator|
|
||||||
|
self_numerator.checked_add(other_numerator).map(|numerator|
|
||||||
|
Fraction { numerator, denominator }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract two fractions, returning None on overflow. Note: overflow can occur on more than just large numerators!
|
||||||
|
/// This requires mutable references because it may decide to reduce
|
||||||
|
/// the fractions mid-operation, and that reduction should persist to avoid having to redo
|
||||||
|
/// that calculation later.
|
||||||
|
pub fn checked_sub(&mut self, other: &mut Self) -> Option<Self> {
|
||||||
|
if self.denominator == other.denominator {
|
||||||
|
match self.numerator.checked_sub(other.numerator) {
|
||||||
|
// Happy path - we get to skip calculating a common denominator!
|
||||||
|
Some(numerator) => Some(Fraction { numerator, denominator: self.denominator }),
|
||||||
|
None => self.reducing_checked_sub(other)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let common_denom: i64 = self.denominator * other.denominator;
|
||||||
|
|
||||||
|
(common_denom / self.denominator).checked_mul(self.numerator)
|
||||||
|
.and_then(|self_numer| {
|
||||||
|
(common_denom / other.denominator).checked_mul(other.numerator)
|
||||||
|
.and_then(|other_numer| {
|
||||||
|
self_numer.checked_sub(other_numer)
|
||||||
|
.map(|numerator| Fraction { numerator, denominator: common_denom })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// Something overflowed - try reducing the inputs first.
|
||||||
|
.or_else(|| self.reducing_checked_sub(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract two fractions. This requires mutable references because it may decide to reduce
|
||||||
|
/// the fractions mid-operation, and that reduction should persist to avoid having to redo
|
||||||
|
/// that calculation later.
|
||||||
|
pub fn sub(&mut self, other: &mut Self) -> Self {
|
||||||
|
if self.denominator == other.denominator {
|
||||||
|
match self.numerator.checked_sub(other.numerator) {
|
||||||
|
// Happy path - we get to skip calculating a common denominator!
|
||||||
|
Some(numerator) => Fraction { numerator, denominator: self.denominator },
|
||||||
|
None => self.reducing_sub(other)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let common_denom: i64 = self.denominator * other.denominator;
|
||||||
|
|
||||||
|
(common_denom / self.denominator).checked_mul(self.numerator)
|
||||||
|
.and_then(|self_numer| {
|
||||||
|
(common_denom / other.denominator).checked_mul(other.numerator)
|
||||||
|
.and_then(|other_numer| {
|
||||||
|
self_numer.checked_sub(other_numer)
|
||||||
|
.map(|numerator| Fraction { numerator, denominator: common_denom })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// Something overflowed - try reducing the inputs first.
|
||||||
|
.unwrap_or_else(|| self.reducing_sub(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract while sacrificing performance to avoid overflow.
|
||||||
|
/// This should only be used as a fallback after an overflow was caught in a higher-perf arithmetic operation.
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reducing_sub(&mut self, other: &mut Self) -> Self {
|
||||||
|
self.reduce();
|
||||||
|
other.reduce();
|
||||||
|
|
||||||
|
let denominator = lcm(self.denominator, other.denominator);
|
||||||
|
let numerator =
|
||||||
|
(self.numerator * (denominator / self.denominator))
|
||||||
|
- (other.numerator * (denominator / other.denominator));
|
||||||
|
|
||||||
|
Fraction { numerator, denominator }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract while sacrificing performance to avoid overflow.
|
||||||
|
/// This should only be used as a fallback after an overflow was caught in a higher-perf arithmetic operation.
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reducing_checked_sub(&mut self, other: &mut Self) -> Option<Self> {
|
||||||
|
self.reduce();
|
||||||
|
other.reduce();
|
||||||
|
|
||||||
|
let denominator = lcm(self.denominator, other.denominator);
|
||||||
|
|
||||||
|
(denominator / self.denominator).checked_mul(self.numerator).and_then(|self_numerator|
|
||||||
|
(denominator / other.denominator).checked_mul(other.numerator).and_then(|other_numerator|
|
||||||
|
self_numerator.checked_sub(other_numerator).map(|numerator|
|
||||||
|
Fraction { numerator, denominator }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply two fractions, returning None on overflow. Note: overflow can occur on more than just large numerators!
|
||||||
|
/// This requires mutable references because it may decide to reduce
|
||||||
|
/// the fractions mid-operation, and that reduction should persist to avoid having to redo
|
||||||
|
/// that calculation later.
|
||||||
|
pub fn checked_mul(&mut self, other: &mut Self) -> Option<Self> {
|
||||||
|
match self.numerator.checked_mul(other.numerator) {
|
||||||
|
Some(numerator) => {
|
||||||
|
// Common denominator is valuable. If we have it, try to preserve it!
|
||||||
|
if self.denominator == other.denominator
|
||||||
|
// See if the denominator is evenly divisible by the new numerator.
|
||||||
|
// If it is, we can "pre-reduce" to the original denominator!
|
||||||
|
&& numerator.checked_rem(self.denominator)
|
||||||
|
.map(|rem| rem == 0)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
// TODO There's probably an optimization opportunity here. Check the
|
||||||
|
// generated instructions - there might be a way to use a division-with-remainder
|
||||||
|
// instruction, grab the remainder value out of the register, then
|
||||||
|
// do the test, and if it passes, grab the existing result of the division
|
||||||
|
// out of the other register without issuing a second division instruction.
|
||||||
|
Some(Fraction {
|
||||||
|
numerator: numerator / self.denominator,
|
||||||
|
denominator: self.denominator
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
match self.denominator.checked_mul(other.denominator) {
|
||||||
|
Some(denominator) => Some(Fraction { numerator, denominator }),
|
||||||
|
|
||||||
|
// Denominator overflowed. See if reducing the inputs helps!
|
||||||
|
None => self.reducing_checked_mul(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Numerator overflowed. See if reducing the inputs helps!
|
||||||
|
None => self.reducing_checked_mul(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply two fractions. This requires mutable references because it may decide to reduce
|
||||||
|
/// the fractions mid-operation, and that reduction should persist to avoid having to redo
|
||||||
|
/// that calculation later.
|
||||||
|
pub fn mul(&mut self, other: &mut Self) -> Self {
|
||||||
|
match self.numerator.checked_mul(other.numerator) {
|
||||||
|
Some(numerator) => {
|
||||||
|
// Common denominator is valuable. If we have it, try to preserve it!
|
||||||
|
if self.denominator == other.denominator
|
||||||
|
// See if the denominator is evenly divisible by the new numerator.
|
||||||
|
// If it is, we can "pre-reduce" to the original denominator!
|
||||||
|
&& numerator.checked_rem(self.denominator)
|
||||||
|
.map(|rem| rem == 0)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
// TODO There's probably an optimization opportunity here. Check the
|
||||||
|
// generated instructions - there might be a way to use a division-with-remainder
|
||||||
|
// instruction, grab the remainder value out of the register, then
|
||||||
|
// do the test, and if it passes, grab the existing result of the division
|
||||||
|
// out of the other register without issuing a second division instruction.
|
||||||
|
Fraction {
|
||||||
|
numerator: numerator / self.denominator,
|
||||||
|
denominator: self.denominator
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match self.denominator.checked_mul(other.denominator) {
|
||||||
|
Some(denominator) => Fraction { numerator, denominator },
|
||||||
|
|
||||||
|
// Denominator overflowed. See if reducing the inputs helps!
|
||||||
|
None => self.reducing_mul(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Numerator overflowed. See if reducing the inputs helps!
|
||||||
|
None => self.reducing_mul(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This requires mutable references because it may decide to reduce
|
||||||
|
/// the fractions mid-operation, and that reduction should persist to avoid having to redo
|
||||||
|
/// that calculation later.
|
||||||
|
pub fn checked_div(&mut self, other: &mut Self) -> Option<Self> {
|
||||||
|
// We're going to multiply by the reciprocal of `other`, so if its numerator
|
||||||
|
// was 0, then the resulting fraction will have 0 for a denominator, so we're done.
|
||||||
|
if other.numerator == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.numerator.checked_mul(other.denominator) {
|
||||||
|
Some(numerator) => {
|
||||||
|
match self.denominator.checked_mul(other.numerator) {
|
||||||
|
Some(denominator) => Some(Fraction { numerator, denominator }),
|
||||||
|
// Denominator overflowed. See if reducing the inputs helps!
|
||||||
|
None => self.reducing_checked_div(other)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Numerator overflowed. See if reducing the inputs helps!
|
||||||
|
None => self.reducing_checked_div(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Divide two fractions, panicking if given a 0 denominator.
|
||||||
|
/// This requires mutable references because it may decide to reduce
|
||||||
|
/// the fractions mid-operation, and that reduction should persist to avoid having to redo
|
||||||
|
/// that calculation later.
|
||||||
|
pub fn div_or_panic(&mut self, other: &mut Self) -> Self {
|
||||||
|
// We're going to multiply by the reciprocal of `other`, so if its numerator
|
||||||
|
// was 0, then the resulting fraction will have 0 for a denominator, so we're done.
|
||||||
|
if other.numerator == 0 {
|
||||||
|
panic!("Division by zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.numerator.checked_mul(other.denominator) {
|
||||||
|
Some(numerator) => {
|
||||||
|
match self.denominator.checked_mul(other.numerator) {
|
||||||
|
Some(denominator) => Fraction { numerator, denominator },
|
||||||
|
// Denominator overflowed. See if reducing the inputs helps!
|
||||||
|
None => self.reducing_div(other)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Numerator overflowed. See if reducing the inputs helps!
|
||||||
|
None => self.reducing_div(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply while sacrificing performance to avoid overflow.
|
||||||
|
/// This should only be used as a fallback after an overflow was caught in a higher-perf arithmetic operation.
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reducing_mul(&mut self, other: &mut Self) -> Self {
|
||||||
|
self.reduce();
|
||||||
|
other.reduce();
|
||||||
|
|
||||||
|
// Preserving common denominator is out the window at this point.
|
||||||
|
let numerator = self.numerator * other.numerator;
|
||||||
|
let denominator = self.denominator * other.denominator;
|
||||||
|
|
||||||
|
Fraction { numerator, denominator }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply while sacrificing performance to avoid overflow.
|
||||||
|
/// This should only be used as a fallback after an overflow was caught in a higher-perf arithmetic operation.
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reducing_checked_mul(&mut self, other: &mut Self) -> Option<Self> {
|
||||||
|
self.reduce();
|
||||||
|
other.reduce();
|
||||||
|
|
||||||
|
// Preserving common denominator is out the window at this point.
|
||||||
|
self.numerator.checked_mul(other.numerator)
|
||||||
|
.and_then(|numerator|
|
||||||
|
self.denominator.checked_mul(other.denominator)
|
||||||
|
.map(|denominator| Fraction { numerator, denominator })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Divide while sacrificing performance to avoid overflow.
|
||||||
|
/// This should only be used as a fallback after an overflow was caught in a higher-perf arithmetic operation.
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reducing_checked_div(&mut self, other: &mut Self) -> Option<Self> {
|
||||||
|
// This should have been verified already by this point!
|
||||||
|
assert_ne!(other.numerator, 0);
|
||||||
|
|
||||||
|
self.reduce();
|
||||||
|
other.reduce();
|
||||||
|
|
||||||
|
self.numerator.checked_mul(other.denominator)
|
||||||
|
.and_then(|numerator|
|
||||||
|
self.denominator.checked_mul(other.numerator)
|
||||||
|
.map(|denominator| Fraction { numerator, denominator })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Divide while sacrificing performance to avoid overflow.
|
||||||
|
/// This should only be used as a fallback after an overflow was caught in a higher-perf arithmetic operation.
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reducing_div(&mut self, other: &mut Self) -> Self {
|
||||||
|
// This should have been verified already by this point!
|
||||||
|
assert_ne!(other.numerator, 0);
|
||||||
|
|
||||||
|
self.reduce();
|
||||||
|
other.reduce();
|
||||||
|
|
||||||
|
let numerator = self.numerator * other.denominator;
|
||||||
|
let denominator = self.denominator * other.numerator;
|
||||||
|
|
||||||
|
Fraction { numerator, denominator }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reduced_eq(&self, other: &Self) -> bool {
|
||||||
|
let mut reduced_self = self.clone();
|
||||||
|
let mut reduced_other = other.clone();
|
||||||
|
|
||||||
|
reduced_self.reduce();
|
||||||
|
reduced_other.reduce();
|
||||||
|
|
||||||
|
let denominator = lcm(reduced_self.denominator, reduced_other.denominator);
|
||||||
|
let self_numerator = reduced_self.numerator * (denominator / reduced_self.denominator);
|
||||||
|
let other_numerator = reduced_other.numerator * (denominator / reduced_other.denominator);
|
||||||
|
|
||||||
|
self_numerator == other_numerator
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)] // We don't want to inline this because it should be almost never invoked in practice.
|
||||||
|
fn reduced_cmp(&self, other: &Self) -> Ordering {
|
||||||
|
let mut reduced_self = self.clone();
|
||||||
|
let mut reduced_other = other.clone();
|
||||||
|
|
||||||
|
reduced_self.reduce();
|
||||||
|
reduced_other.reduce();
|
||||||
|
|
||||||
|
let denominator = lcm(reduced_self.denominator, reduced_other.denominator);
|
||||||
|
let self_numerator = reduced_self.numerator * (denominator / reduced_self.denominator);
|
||||||
|
let other_numerator = reduced_other.numerator * (denominator / reduced_other.denominator);
|
||||||
|
|
||||||
|
self_numerator.cmp(&other_numerator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Fraction {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.denominator == other.denominator {
|
||||||
|
self.numerator == other.numerator
|
||||||
|
} else {
|
||||||
|
let common_denom: i64 = self.denominator * other.denominator;
|
||||||
|
|
||||||
|
(common_denom / self.denominator).checked_mul(self.numerator)
|
||||||
|
.and_then(|self_numer| {
|
||||||
|
(common_denom / other.denominator).checked_mul(other.numerator)
|
||||||
|
.map(|other_numer| {
|
||||||
|
self_numer == other_numer
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// Something overflowed - try reducing the inputs first.
|
||||||
|
.unwrap_or_else(|| self.reduced_eq(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Fraction {}
|
||||||
|
impl PartialOrd for Fraction {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Fraction {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
if self.denominator == other.denominator {
|
||||||
|
self.numerator.cmp(&other.numerator)
|
||||||
|
} else {
|
||||||
|
let common_denom: i64 = self.denominator * other.denominator;
|
||||||
|
|
||||||
|
(common_denom / self.denominator).checked_mul(self.numerator)
|
||||||
|
.and_then(|self_numer| {
|
||||||
|
(common_denom / other.denominator).checked_mul(other.numerator)
|
||||||
|
.map(|other_numer| {
|
||||||
|
self_numer.cmp(&other_numer)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// Something overflowed - try reducing the inputs first.
|
||||||
|
.unwrap_or_else(|| self.reduced_cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for Fraction {
|
||||||
|
type Output = Fraction;
|
||||||
|
|
||||||
|
fn neg(self) -> Self {
|
||||||
|
Fraction { numerator: -self.numerator, denominator: self.denominator }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function was adapted from v0.1.39 of the num-integer crate. Licensed under the
|
||||||
|
/// Apache License, version 2.0. A full copy of the License can be found here:
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// The original code can be found at:
|
||||||
|
/// https://docs.rs/num-integer/0.1.41/src/num_integer/lib.rs.html#456-500
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Calculates the Greatest Common Divisor (GCD) of the number and
|
||||||
|
/// `other`. The result is always positive.
|
||||||
|
#[inline]
|
||||||
|
fn gcd(me: i64, other: i64) -> i64 {
|
||||||
|
// Use Stein's algorithm
|
||||||
|
let mut m = me;
|
||||||
|
let mut n = other;
|
||||||
|
if m == 0 || n == 0 { return (m | n).abs() }
|
||||||
|
|
||||||
|
// find common factors of 2
|
||||||
|
let shift = (m | n).trailing_zeros();
|
||||||
|
|
||||||
|
// The algorithm needs positive numbers, but the minimum value
|
||||||
|
// can't be represented as a positive one.
|
||||||
|
// It's also a power of two, so the gcd can be
|
||||||
|
// calculated by bitshifting in that case
|
||||||
|
|
||||||
|
// Assuming two's complement, the number created by the shift
|
||||||
|
// is positive for all numbers except gcd = abs(min value)
|
||||||
|
// The call to .abs() causes a panic in debug mode
|
||||||
|
if m == i64::min_value() || n == i64::min_value() {
|
||||||
|
return ((1 << shift) as i64).abs()
|
||||||
|
}
|
||||||
|
// guaranteed to be positive now, rest like unsigned algorithm
|
||||||
|
m = m.abs();
|
||||||
|
n = n.abs();
|
||||||
|
|
||||||
|
// divide n and m by 2 until odd
|
||||||
|
// m inside loop
|
||||||
|
n >>= n.trailing_zeros();
|
||||||
|
|
||||||
|
while m != 0 {
|
||||||
|
m >>= m.trailing_zeros();
|
||||||
|
if n > m { mem::swap(&mut n, &mut m) }
|
||||||
|
m -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
n << shift
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lowest common multiple
|
||||||
|
fn lcm(me: i64, other: i64) -> i64 {
|
||||||
|
me * (other / gcd(me, other))
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ mod test_eval {
|
||||||
use roc::eval::eval;
|
use roc::eval::eval;
|
||||||
use roc::eval::Evaluated;
|
use roc::eval::Evaluated;
|
||||||
use fraction::Fraction;
|
use fraction::Fraction;
|
||||||
|
use roc::stack_fraction;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_plus_one() {
|
fn one_plus_one() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue