implement small int optimizations

This allows for numbers within 18 digits of precision (the vast
majority of numbers) to be represented by just 2 i64s rather than 2 heap
allocated BigInts. In the future these optimizations may become more
aggressive and granular, but for now `grass` is already more than an order of
magnitude faster than `dart-sass`.
This commit is contained in:
ConnorSkees 2020-05-19 23:23:04 -04:00
parent cf987abb53
commit 8520048b26
4 changed files with 370 additions and 89 deletions

View File

@ -362,16 +362,16 @@ impl Color {
let repr = repr(&val, &val, &val, &alpha); let repr = repr(&val, &val, &val, &alpha);
return Color::new_hsla(val.clone(), val.clone(), val, alpha, hsla, repr); return Color::new_hsla(val.clone(), val.clone(), val, alpha, hsla, repr);
} }
let temporary_1 = if luminance < Number::ratio(1, 2) { let temporary_1 = if luminance < Number::machine_ratio(1, 2) {
luminance.clone() * (Number::one() + saturation) luminance.clone() * (Number::one() + saturation)
} else { } else {
luminance.clone() + saturation.clone() - luminance.clone() * saturation luminance.clone() + saturation.clone() - luminance.clone() * saturation
}; };
let temporary_2 = Number::from(2) * luminance - temporary_1.clone(); let temporary_2 = Number::from(2) * luminance - temporary_1.clone();
hue /= Number::from(360); hue /= Number::from(360);
let mut temporary_r = hue.clone() + Number::ratio(1, 3); let mut temporary_r = hue.clone() + Number::machine_ratio(1, 3);
let mut temporary_g = hue.clone(); let mut temporary_g = hue.clone();
let mut temporary_b = hue - Number::ratio(1, 3); let mut temporary_b = hue - Number::machine_ratio(1, 3);
macro_rules! clamp_temp { macro_rules! clamp_temp {
($temp:ident) => { ($temp:ident) => {
@ -397,7 +397,7 @@ impl Color {
} else if Number::from(3) * $temp.clone() < Number::from(2) { } else if Number::from(3) * $temp.clone() < Number::from(2) {
$temp2.clone() $temp2.clone()
+ ($temp1.clone() - $temp2.clone()) + ($temp1.clone() - $temp2.clone())
* (Number::ratio(2, 3) - $temp) * (Number::machine_ratio(2, 3) - $temp)
* Number::from(6) * Number::from(6)
} else { } else {
$temp2.clone() $temp2.clone()

View File

@ -16,18 +16,18 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy<
let mut from_in = HashMap::new(); let mut from_in = HashMap::new();
from_in.insert("in", Number::one()); from_in.insert("in", Number::one());
from_in.insert("cm", Number::one() / Number::from(2.54)); from_in.insert("cm", Number::one() / Number::from(2.54));
from_in.insert("pc", Number::ratio(1, 6)); from_in.insert("pc", Number::machine_ratio(1, 6));
from_in.insert("mm", Number::one() / Number::from(25.4)); from_in.insert("mm", Number::one() / Number::from(25.4));
from_in.insert("q", Number::one() / Number::from(101.6)); from_in.insert("q", Number::one() / Number::from(101.6));
from_in.insert("pt", Number::ratio(1, 72)); from_in.insert("pt", Number::machine_ratio(1, 72));
from_in.insert("px", Number::ratio(1, 96)); from_in.insert("px", Number::machine_ratio(1, 96));
let mut from_cm = HashMap::new(); let mut from_cm = HashMap::new();
from_cm.insert("in", Number::from(2.54)); from_cm.insert("in", Number::from(2.54));
from_cm.insert("cm", Number::one()); from_cm.insert("cm", Number::one());
from_cm.insert("pc", Number::from(2.54) / Number::from(6)); from_cm.insert("pc", Number::from(2.54) / Number::from(6));
from_cm.insert("mm", Number::ratio(1, 10)); from_cm.insert("mm", Number::machine_ratio(1, 10));
from_cm.insert("q", Number::ratio(1, 40)); from_cm.insert("q", Number::machine_ratio(1, 40));
from_cm.insert("pt", Number::from(2.54) / Number::from(72)); from_cm.insert("pt", Number::from(2.54) / Number::from(72));
from_cm.insert("px", Number::from(2.54) / Number::from(96)); from_cm.insert("px", Number::from(2.54) / Number::from(96));
@ -37,15 +37,15 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy<
from_pc.insert("pc", Number::one()); from_pc.insert("pc", Number::one());
from_pc.insert("mm", Number::from(6) / Number::from(25.4)); from_pc.insert("mm", Number::from(6) / Number::from(25.4));
from_pc.insert("q", Number::from(6) / Number::from(101.6)); from_pc.insert("q", Number::from(6) / Number::from(101.6));
from_pc.insert("pt", Number::ratio(1, 12)); from_pc.insert("pt", Number::machine_ratio(1, 12));
from_pc.insert("px", Number::ratio(1, 16)); from_pc.insert("px", Number::machine_ratio(1, 16));
let mut from_mm = HashMap::new(); let mut from_mm = HashMap::new();
from_mm.insert("in", Number::from(25.4)); from_mm.insert("in", Number::from(25.4));
from_mm.insert("cm", Number::from(10)); from_mm.insert("cm", Number::from(10));
from_mm.insert("pc", Number::from(25.4) / Number::from(6)); from_mm.insert("pc", Number::from(25.4) / Number::from(6));
from_mm.insert("mm", Number::one()); from_mm.insert("mm", Number::one());
from_mm.insert("q", Number::ratio(1, 4)); from_mm.insert("q", Number::machine_ratio(1, 4));
from_mm.insert("pt", Number::from(25.4) / Number::from(72)); from_mm.insert("pt", Number::from(25.4) / Number::from(72));
from_mm.insert("px", Number::from(25.4) / Number::from(96)); from_mm.insert("px", Number::from(25.4) / Number::from(96));
@ -65,7 +65,7 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy<
from_pt.insert("mm", Number::from(72) / Number::from(25.4)); from_pt.insert("mm", Number::from(72) / Number::from(25.4));
from_pt.insert("q", Number::from(72) / Number::from(101.6)); from_pt.insert("q", Number::from(72) / Number::from(101.6));
from_pt.insert("pt", Number::one()); from_pt.insert("pt", Number::one());
from_pt.insert("px", Number::ratio(3, 4)); from_pt.insert("px", Number::machine_ratio(3, 4));
let mut from_px = HashMap::new(); let mut from_px = HashMap::new();
from_px.insert("in", Number::from(96)); from_px.insert("in", Number::from(96));
@ -73,17 +73,17 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy<
from_px.insert("pc", Number::from(16)); from_px.insert("pc", Number::from(16));
from_px.insert("mm", Number::from(96) / Number::from(25.4)); from_px.insert("mm", Number::from(96) / Number::from(25.4));
from_px.insert("q", Number::from(96) / Number::from(101.6)); from_px.insert("q", Number::from(96) / Number::from(101.6));
from_px.insert("pt", Number::ratio(4, 3)); from_px.insert("pt", Number::machine_ratio(4, 3));
from_px.insert("px", Number::one()); from_px.insert("px", Number::one());
let mut from_deg = HashMap::new(); let mut from_deg = HashMap::new();
from_deg.insert("deg", Number::one()); from_deg.insert("deg", Number::one());
from_deg.insert("grad", Number::ratio(9, 10)); from_deg.insert("grad", Number::machine_ratio(9, 10));
from_deg.insert("rad", Number::from(180) / Number::from(PI)); from_deg.insert("rad", Number::from(180) / Number::from(PI));
from_deg.insert("turn", Number::from(360)); from_deg.insert("turn", Number::from(360));
let mut from_grad = HashMap::new(); let mut from_grad = HashMap::new();
from_grad.insert("deg", Number::ratio(10, 9)); from_grad.insert("deg", Number::machine_ratio(10, 9));
from_grad.insert("grad", Number::one()); from_grad.insert("grad", Number::one());
from_grad.insert("rad", Number::from(200) / Number::from(PI)); from_grad.insert("rad", Number::from(200) / Number::from(PI));
from_grad.insert("turn", Number::from(400)); from_grad.insert("turn", Number::from(400));
@ -95,14 +95,14 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy<
from_rad.insert("turn", Number::from(2.0 * PI)); from_rad.insert("turn", Number::from(2.0 * PI));
let mut from_turn = HashMap::new(); let mut from_turn = HashMap::new();
from_turn.insert("deg", Number::ratio(1, 360)); from_turn.insert("deg", Number::machine_ratio(1, 360));
from_turn.insert("grad", Number::ratio(1, 400)); from_turn.insert("grad", Number::machine_ratio(1, 400));
from_turn.insert("rad", Number::one() / Number::from(2.0 * PI)); from_turn.insert("rad", Number::one() / Number::from(2.0 * PI));
from_turn.insert("turn", Number::one()); from_turn.insert("turn", Number::one());
let mut from_s = HashMap::new(); let mut from_s = HashMap::new();
from_s.insert("s", Number::one()); from_s.insert("s", Number::one());
from_s.insert("ms", Number::ratio(1, 1000)); from_s.insert("ms", Number::machine_ratio(1, 1000));
let mut from_ms = HashMap::new(); let mut from_ms = HashMap::new();
from_ms.insert("s", Number::from(1000)); from_ms.insert("s", Number::from(1000));
@ -113,7 +113,7 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy<
from_hz.insert("kHz", Number::from(1000)); from_hz.insert("kHz", Number::from(1000));
let mut from_khz = HashMap::new(); let mut from_khz = HashMap::new();
from_khz.insert("Hz", Number::ratio(1, 1000)); from_khz.insert("Hz", Number::machine_ratio(1, 1000));
from_khz.insert("kHz", Number::one()); from_khz.insert("kHz", Number::one());
let mut from_dpi = HashMap::new(); let mut from_dpi = HashMap::new();
@ -127,7 +127,7 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy<
from_dpcm.insert("dppx", Number::from(96) / Number::from(2.54)); from_dpcm.insert("dppx", Number::from(96) / Number::from(2.54));
let mut from_dppx = HashMap::new(); let mut from_dppx = HashMap::new();
from_dppx.insert("dpi", Number::ratio(1, 96)); from_dppx.insert("dpi", Number::machine_ratio(1, 96));
from_dppx.insert("dpcm", Number::from(2.54) / Number::from(96)); from_dppx.insert("dpcm", Number::from(2.54) / Number::from(96));
from_dppx.insert("dppx", Number::one()); from_dppx.insert("dppx", Number::one());

View File

@ -1,61 +1,90 @@
use std::convert::From; use std::cmp::Ordering;
use std::convert::{From, TryFrom};
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
use std::mem;
use std::ops::{ use std::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
}; };
use num_bigint::BigInt; use num_bigint::BigInt;
use num_rational::BigRational; use num_rational::{BigRational, Rational64};
use num_traits::{Num, One, Signed, Zero}; use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Num, One, Signed, Zero};
use crate::error::SassError; use crate::error::SassError;
const PRECISION: usize = 10; const PRECISION: usize = 10;
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Clone, Eq, PartialEq, Ord)]
pub(crate) struct Number { pub(crate) enum Number {
val: BigRational, Machine(Rational64),
Big(BigRational),
} }
impl Number { impl Number {
pub const fn new(val: BigRational) -> Number { pub const fn new_machine(val: Rational64) -> Number {
Number { val } Number::Machine(val)
}
pub const fn new_big(val: BigRational) -> Number {
Number::Big(val)
} }
pub fn to_integer(&self) -> BigInt { pub fn to_integer(&self) -> BigInt {
self.val.to_integer() match self {
Self::Machine(val) => BigInt::from(val.to_integer()),
Self::Big(val) => val.to_integer(),
}
} }
pub fn ratio<A: Into<BigInt>, B: Into<BigInt>>(a: A, b: B) -> Self { pub fn machine_ratio<A: Into<i64>, B: Into<i64>>(a: A, b: B) -> Self {
Number::new(BigRational::new(a.into(), b.into())) Number::new_machine(Rational64::new(a.into(), b.into()))
}
#[allow(dead_code)]
pub fn big_ratio<A: Into<BigInt>, B: Into<BigInt>>(a: A, b: B) -> Self {
Number::new_big(BigRational::new(a.into(), b.into()))
} }
pub fn round(&self) -> Self { pub fn round(&self) -> Self {
Number { match self {
val: self.val.round(), Self::Machine(val) => Self::Machine(val.round()),
Self::Big(val) => Self::Big(val.round()),
} }
} }
pub fn ceil(&self) -> Self { pub fn ceil(&self) -> Self {
Number { match self {
val: self.val.ceil(), Self::Machine(val) => Self::Machine(val.ceil()),
Self::Big(val) => Self::Big(val.ceil()),
} }
} }
pub fn floor(&self) -> Self { pub fn floor(&self) -> Self {
Number { match self {
val: self.val.floor(), Self::Machine(val) => Self::Machine(val.floor()),
Self::Big(val) => Self::Big(val.floor()),
} }
} }
pub fn abs(&self) -> Self { pub fn abs(&self) -> Self {
Number { match self {
val: self.val.abs(), Self::Machine(val) => Self::Machine(val.abs()),
Self::Big(val) => Self::Big(val.abs()),
} }
} }
pub fn is_decimal(&self) -> bool { pub fn is_decimal(&self) -> bool {
!self.val.denom().is_one() match self {
Self::Machine(v) => !v.is_integer(),
Self::Big(v) => !v.is_integer(),
}
}
pub fn fract(&mut self) -> Number {
match self {
Self::Machine(v) => Number::new_machine(v.fract()),
Self::Big(v) => Number::new_big(v.fract()),
}
} }
pub fn clamp<A: Into<Number> + Zero, B: Into<Number>>(self, min: A, max: B) -> Self { pub fn clamp<A: Into<Number> + Zero, B: Into<Number>>(self, min: A, max: B) -> Self {
@ -77,23 +106,35 @@ impl Number {
} }
} }
impl Default for Number {
fn default() -> Self {
Self::zero()
}
}
impl Zero for Number { impl Zero for Number {
fn zero() -> Self { fn zero() -> Self {
Number::from(0) Number::new_machine(Rational64::from_integer(0))
} }
fn is_zero(&self) -> bool { fn is_zero(&self) -> bool {
self.val.is_zero() match self {
Self::Machine(v) => v.is_zero(),
Self::Big(v) => v.is_zero(),
}
} }
} }
impl One for Number { impl One for Number {
fn one() -> Self { fn one() -> Self {
Number::from(1) Number::new_machine(Rational64::from_integer(1))
} }
fn is_one(&self) -> bool { fn is_one(&self) -> bool {
self.val.is_one() match self {
Self::Machine(v) => v.is_one(),
Self::Big(v) => v.is_one(),
}
} }
} }
@ -109,10 +150,8 @@ impl Signed for Number {
self.abs() self.abs()
} }
fn abs_sub(&self, other: &Self) -> Self { fn abs_sub(&self, _: &Self) -> Self {
Number { todo!()
val: self.val.abs_sub(&other.val),
}
} }
fn signum(&self) -> Self { fn signum(&self) -> Self {
@ -126,11 +165,17 @@ impl Signed for Number {
} }
fn is_positive(&self) -> bool { fn is_positive(&self) -> bool {
self.val.is_positive() match self {
Self::Machine(v) => v.is_positive(),
Self::Big(v) => v.is_positive(),
}
} }
fn is_negative(&self) -> bool { fn is_negative(&self) -> bool {
self.val.is_negative() match self {
Self::Machine(v) => v.is_negative(),
Self::Big(v) => v.is_negative(),
}
} }
} }
@ -138,29 +183,44 @@ macro_rules! from_integer {
($ty:ty) => { ($ty:ty) => {
impl From<$ty> for Number { impl From<$ty> for Number {
fn from(b: $ty) -> Self { fn from(b: $ty) -> Self {
Number { if let Ok(v) = i64::try_from(b) {
val: BigRational::from_integer(BigInt::from(b)), Number::Machine(Rational64::from_integer(v))
} else {
Number::Big(BigRational::from_integer(BigInt::from(b)))
} }
} }
} }
}; };
} }
// todo: implement std::convertTryFrom instead macro_rules! from_smaller_integer {
impl From<f64> for Number { ($ty:ty) => {
fn from(b: f64) -> Self { impl From<$ty> for Number {
Number { fn from(val: $ty) -> Self {
val: BigRational::from_float(b).unwrap(), Number::new_machine(Rational64::from_integer(val as i64))
}
} }
};
}
impl From<i64> for Number {
fn from(val: i64) -> Self {
Number::new_machine(Rational64::from_integer(val))
}
}
// todo: implement std::convertTryFrom instead
impl From<f64> for Number {
fn from(b: f64) -> Self {
Number::Big(BigRational::from_float(b).unwrap())
} }
} }
from_integer!(u16);
from_integer!(usize); from_integer!(usize);
from_integer!(isize); from_integer!(isize);
from_integer!(i32); from_smaller_integer!(i32);
from_integer!(u32); from_smaller_integer!(u32);
from_integer!(u8); from_smaller_integer!(u8);
impl fmt::Debug for Number { impl fmt::Debug for Number {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -170,13 +230,13 @@ impl fmt::Debug for Number {
impl Display for Number { impl Display for Number {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut whole = self.val.to_integer().abs(); let mut whole = self.to_integer().abs();
let has_decimal = !self.val.denom().is_one(); let has_decimal = self.is_decimal();
let mut frac = self.val.fract().abs(); let mut frac = self.abs().fract();
let mut dec = String::with_capacity(if has_decimal { PRECISION + 1 } else { 0 }); let mut dec = String::with_capacity(if has_decimal { PRECISION + 1 } else { 0 });
if has_decimal { if has_decimal {
for _ in 0..(PRECISION - 1) { for _ in 0..(PRECISION - 1) {
frac *= BigInt::from(10); frac = frac * Self::from(10);
write!(dec, "{}", frac.to_integer())?; write!(dec, "{}", frac.to_integer())?;
frac = frac.fract(); frac = frac.fract();
if frac.is_zero() { if frac.is_zero() {
@ -184,7 +244,7 @@ impl Display for Number {
} }
} }
if !frac.is_zero() { if !frac.is_zero() {
let end = (frac * BigInt::from(10)).round().to_integer(); let end = (frac * Self::from(10)).round().to_integer();
if end == BigInt::from(10) { if end == BigInt::from(10) {
loop { loop {
match dec.pop() { match dec.pop() {
@ -216,7 +276,7 @@ impl Display for Number {
} }
} }
if self.val.is_negative() && (!whole.is_zero() || !dec.is_empty()) { if self.is_negative() && (!whole.is_zero() || !dec.is_empty()) {
f.write_char('-')?; f.write_char('-')?;
} }
write!(f, "{}", whole)?; write!(f, "{}", whole)?;
@ -228,12 +288,67 @@ impl Display for Number {
} }
} }
impl PartialOrd for Number {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self {
Self::Machine(val1) => match other {
Self::Machine(val2) => val1.partial_cmp(val2),
Self::Big(val2) => {
let tuple: (i64, i64) = (*val1).into();
BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1))
.partial_cmp(val2)
}
},
Self::Big(val1) => match other {
Self::Machine(val2) => {
let tuple: (i64, i64) = (*val2).into();
val1.partial_cmp(&BigRational::new_raw(
BigInt::from(tuple.0),
BigInt::from(tuple.1),
))
}
Self::Big(val2) => val1.partial_cmp(val2),
},
}
}
}
impl Add for Number { impl Add for Number {
type Output = Self; type Output = Self;
fn add(self, other: Self) -> Self { fn add(self, other: Self) -> Self {
Number { match self {
val: self.val + other.val, Self::Machine(val1) => match other {
Self::Machine(val2) => match val1.checked_add(&val2) {
Some(v) => Self::Machine(v),
None => {
let tuple1: (i64, i64) = val1.into();
let tuple2: (i64, i64) = val2.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1))
+ BigRational::new_raw(
BigInt::from(tuple2.0),
BigInt::from(tuple2.1),
),
)
}
},
Self::Big(val2) => {
let tuple: (i64, i64) = val1.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) + val2,
)
}
},
Self::Big(val1) => match other {
Self::Big(val2) => Self::Big(val1 + val2),
Self::Machine(val2) => {
let tuple: (i64, i64) = val2.into();
Self::Big(
val1 + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)),
)
}
},
} }
} }
} }
@ -242,15 +357,46 @@ impl Add<&Self> for Number {
type Output = Self; type Output = Self;
fn add(self, other: &Self) -> Self { fn add(self, other: &Self) -> Self {
Number { match self {
val: self.val + &other.val, Self::Machine(val1) => match other {
Self::Machine(val2) => match val1.checked_add(val2) {
Some(v) => Self::Machine(v),
None => {
let tuple1: (i64, i64) = val1.into();
let tuple2: (i64, i64) = (*val2).into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1))
+ BigRational::new_raw(
BigInt::from(tuple2.0),
BigInt::from(tuple2.1),
),
)
}
},
Self::Big(val2) => {
let tuple: (i64, i64) = val1.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) + val2,
)
}
},
Self::Big(val1) => match other {
Self::Big(val2) => Self::Big(val1 + val2),
Self::Machine(val2) => {
let tuple: (i64, i64) = (*val2).into();
Self::Big(
val1 + BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)),
)
}
},
} }
} }
} }
impl AddAssign for Number { impl AddAssign for Number {
fn add_assign(&mut self, other: Self) { fn add_assign(&mut self, other: Self) {
self.val += other.val let tmp = mem::take(self);
*self = tmp + other;
} }
} }
@ -258,15 +404,46 @@ impl Sub for Number {
type Output = Self; type Output = Self;
fn sub(self, other: Self) -> Self { fn sub(self, other: Self) -> Self {
Number { match self {
val: self.val - other.val, Self::Machine(val1) => match other {
Self::Machine(val2) => match val1.checked_sub(&val2) {
Some(v) => Self::Machine(v),
None => {
let tuple1: (i64, i64) = val1.into();
let tuple2: (i64, i64) = val2.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1))
- BigRational::new_raw(
BigInt::from(tuple2.0),
BigInt::from(tuple2.1),
),
)
}
},
Self::Big(val2) => {
let tuple: (i64, i64) = val1.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) - val2,
)
}
},
Self::Big(val1) => match other {
Self::Big(val2) => Self::Big(val1 - val2),
Self::Machine(val2) => {
let tuple: (i64, i64) = val2.into();
Self::Big(
val1 - BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)),
)
}
},
} }
} }
} }
impl SubAssign for Number { impl SubAssign for Number {
fn sub_assign(&mut self, other: Self) { fn sub_assign(&mut self, other: Self) {
self.val -= other.val let tmp = mem::take(self);
*self = tmp - other;
} }
} }
@ -274,15 +451,46 @@ impl Mul for Number {
type Output = Self; type Output = Self;
fn mul(self, other: Self) -> Self { fn mul(self, other: Self) -> Self {
Number { match self {
val: self.val * other.val, Self::Machine(val1) => match other {
Self::Machine(val2) => match val1.checked_mul(&val2) {
Some(v) => Self::Machine(v),
None => {
let tuple1: (i64, i64) = val1.into();
let tuple2: (i64, i64) = val2.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1))
* BigRational::new_raw(
BigInt::from(tuple2.0),
BigInt::from(tuple2.1),
),
)
}
},
Self::Big(val2) => {
let tuple: (i64, i64) = val1.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) * val2,
)
}
},
Self::Big(val1) => match other {
Self::Big(val2) => Self::Big(val1 * val2),
Self::Machine(val2) => {
let tuple: (i64, i64) = val2.into();
Self::Big(
val1 * BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)),
)
}
},
} }
} }
} }
impl MulAssign for Number { impl MulAssign for Number {
fn mul_assign(&mut self, other: Self) { fn mul_assign(&mut self, other: Self) {
self.val *= other.val let tmp = mem::take(self);
*self = tmp * other;
} }
} }
@ -290,15 +498,46 @@ impl Div for Number {
type Output = Self; type Output = Self;
fn div(self, other: Self) -> Self { fn div(self, other: Self) -> Self {
Number { match self {
val: self.val / other.val, Self::Machine(val1) => match other {
Self::Machine(val2) => match val1.checked_div(&val2) {
Some(v) => Self::Machine(v),
None => {
let tuple1: (i64, i64) = val1.into();
let tuple2: (i64, i64) = val2.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1))
/ BigRational::new_raw(
BigInt::from(tuple2.0),
BigInt::from(tuple2.1),
),
)
}
},
Self::Big(val2) => {
let tuple: (i64, i64) = val1.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) / val2,
)
}
},
Self::Big(val1) => match other {
Self::Big(val2) => Self::Big(val1 / val2),
Self::Machine(val2) => {
let tuple: (i64, i64) = val2.into();
Self::Big(
val1 / BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)),
)
}
},
} }
} }
} }
impl DivAssign for Number { impl DivAssign for Number {
fn div_assign(&mut self, other: Self) { fn div_assign(&mut self, other: Self) {
self.val /= other.val let tmp = mem::take(self);
*self = tmp / other;
} }
} }
@ -306,15 +545,41 @@ impl Rem for Number {
type Output = Self; type Output = Self;
fn rem(self, other: Self) -> Self { fn rem(self, other: Self) -> Self {
Number { match self {
val: self.val % other.val, Self::Machine(val1) => match other {
// todo: checked_rem for ratio?
Self::Machine(val2) => {
let tuple1: (i64, i64) = val1.into();
let tuple2: (i64, i64) = val2.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple1.0), BigInt::from(tuple1.1))
% BigRational::new_raw(BigInt::from(tuple2.0), BigInt::from(tuple2.1)),
)
}
Self::Big(val2) => {
let tuple: (i64, i64) = val1.into();
Self::Big(
BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)) % val2,
)
}
},
Self::Big(val1) => match other {
Self::Big(val2) => Self::Big(val1 % val2),
Self::Machine(val2) => {
let tuple: (i64, i64) = val2.into();
Self::Big(
val1 % BigRational::new_raw(BigInt::from(tuple.0), BigInt::from(tuple.1)),
)
}
},
} }
} }
} }
impl RemAssign for Number { impl RemAssign for Number {
fn rem_assign(&mut self, other: Self) { fn rem_assign(&mut self, other: Self) {
self.val %= other.val let tmp = mem::take(self);
*self = tmp % other;
} }
} }
@ -322,6 +587,9 @@ impl Neg for Number {
type Output = Self; type Output = Self;
fn neg(self) -> Self { fn neg(self) -> Self {
Number { val: -self.val } match self {
Self::Machine(v) => Self::Machine(-v),
Self::Big(v) => Self::Big(-v),
}
} }
} }

View File

@ -2,7 +2,7 @@ use std::iter::Iterator;
use std::mem; use std::mem;
use num_bigint::BigInt; use num_bigint::BigInt;
use num_rational::BigRational; use num_rational::{BigRational, Rational64};
use num_traits::{pow, One, ToPrimitive}; use num_traits::{pow, One, ToPrimitive};
use codemap::{Span, Spanned}; use codemap::{Span, Spanned};
@ -668,14 +668,27 @@ impl Value {
}; };
let n = if val.dec_len == 0 { let n = if val.dec_len == 0 {
if val.num.len() <= 18 && val.times_ten.is_empty() {
let n = Rational64::new_raw(val.num.parse::<i64>().unwrap(), 1i64);
return Some(Ok(IntermediateValue::Value(
Value::Dimension(Number::new_machine(n), unit).span(span),
)));
}
BigRational::new_raw(val.num.parse::<BigInt>().unwrap(), BigInt::one()) BigRational::new_raw(val.num.parse::<BigInt>().unwrap(), BigInt::one())
} else { } else {
if val.num.len() <= 18 && val.times_ten.is_empty() {
let n =
Rational64::new(val.num.parse::<i64>().unwrap(), pow(10, val.dec_len));
return Some(Ok(IntermediateValue::Value(
Value::Dimension(Number::new_machine(n), unit).span(span),
)));
}
BigRational::new(val.num.parse().unwrap(), pow(BigInt::from(10), val.dec_len)) BigRational::new(val.num.parse().unwrap(), pow(BigInt::from(10), val.dec_len))
}; };
if val.times_ten.is_empty() { if val.times_ten.is_empty() {
return Some(Ok(IntermediateValue::Value( return Some(Ok(IntermediateValue::Value(
Value::Dimension(Number::new(n), unit).span(span), Value::Dimension(Number::new_big(n), unit).span(span),
))); )));
} }
@ -700,7 +713,7 @@ impl Value {
}; };
IntermediateValue::Value( IntermediateValue::Value(
Value::Dimension(Number::new(n * times_ten), unit).span(span), Value::Dimension(Number::new_big(n * times_ten), unit).span(span),
) )
} }
'(' => { '(' => {