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:
parent
cf987abb53
commit
8520048b26
@ -362,16 +362,16 @@ impl Color {
|
||||
let repr = repr(&val, &val, &val, &alpha);
|
||||
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)
|
||||
} else {
|
||||
luminance.clone() + saturation.clone() - luminance.clone() * saturation
|
||||
};
|
||||
let temporary_2 = Number::from(2) * luminance - temporary_1.clone();
|
||||
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_b = hue - Number::ratio(1, 3);
|
||||
let mut temporary_b = hue - Number::machine_ratio(1, 3);
|
||||
|
||||
macro_rules! clamp_temp {
|
||||
($temp:ident) => {
|
||||
@ -397,7 +397,7 @@ impl Color {
|
||||
} else if Number::from(3) * $temp.clone() < Number::from(2) {
|
||||
$temp2.clone()
|
||||
+ ($temp1.clone() - $temp2.clone())
|
||||
* (Number::ratio(2, 3) - $temp)
|
||||
* (Number::machine_ratio(2, 3) - $temp)
|
||||
* Number::from(6)
|
||||
} else {
|
||||
$temp2.clone()
|
||||
|
@ -16,18 +16,18 @@ pub(crate) static UNIT_CONVERSION_TABLE: Lazy<
|
||||
let mut from_in = HashMap::new();
|
||||
from_in.insert("in", Number::one());
|
||||
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("q", Number::one() / Number::from(101.6));
|
||||
from_in.insert("pt", Number::ratio(1, 72));
|
||||
from_in.insert("px", Number::ratio(1, 96));
|
||||
from_in.insert("pt", Number::machine_ratio(1, 72));
|
||||
from_in.insert("px", Number::machine_ratio(1, 96));
|
||||
|
||||
let mut from_cm = HashMap::new();
|
||||
from_cm.insert("in", Number::from(2.54));
|
||||
from_cm.insert("cm", Number::one());
|
||||
from_cm.insert("pc", Number::from(2.54) / Number::from(6));
|
||||
from_cm.insert("mm", Number::ratio(1, 10));
|
||||
from_cm.insert("q", Number::ratio(1, 40));
|
||||
from_cm.insert("mm", Number::machine_ratio(1, 10));
|
||||
from_cm.insert("q", Number::machine_ratio(1, 40));
|
||||
from_cm.insert("pt", Number::from(2.54) / Number::from(72));
|
||||
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("mm", Number::from(6) / Number::from(25.4));
|
||||
from_pc.insert("q", Number::from(6) / Number::from(101.6));
|
||||
from_pc.insert("pt", Number::ratio(1, 12));
|
||||
from_pc.insert("px", Number::ratio(1, 16));
|
||||
from_pc.insert("pt", Number::machine_ratio(1, 12));
|
||||
from_pc.insert("px", Number::machine_ratio(1, 16));
|
||||
|
||||
let mut from_mm = HashMap::new();
|
||||
from_mm.insert("in", Number::from(25.4));
|
||||
from_mm.insert("cm", Number::from(10));
|
||||
from_mm.insert("pc", Number::from(25.4) / Number::from(6));
|
||||
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("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("q", Number::from(72) / Number::from(101.6));
|
||||
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();
|
||||
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("mm", Number::from(96) / Number::from(25.4));
|
||||
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());
|
||||
|
||||
let mut from_deg = HashMap::new();
|
||||
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("turn", Number::from(360));
|
||||
|
||||
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("rad", Number::from(200) / Number::from(PI));
|
||||
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));
|
||||
|
||||
let mut from_turn = HashMap::new();
|
||||
from_turn.insert("deg", Number::ratio(1, 360));
|
||||
from_turn.insert("grad", Number::ratio(1, 400));
|
||||
from_turn.insert("deg", Number::machine_ratio(1, 360));
|
||||
from_turn.insert("grad", Number::machine_ratio(1, 400));
|
||||
from_turn.insert("rad", Number::one() / Number::from(2.0 * PI));
|
||||
from_turn.insert("turn", Number::one());
|
||||
|
||||
let mut from_s = HashMap::new();
|
||||
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();
|
||||
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));
|
||||
|
||||
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());
|
||||
|
||||
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));
|
||||
|
||||
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("dppx", Number::one());
|
||||
|
||||
|
@ -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::mem;
|
||||
use std::ops::{
|
||||
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
|
||||
};
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use num_rational::BigRational;
|
||||
use num_traits::{Num, One, Signed, Zero};
|
||||
use num_rational::{BigRational, Rational64};
|
||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Num, One, Signed, Zero};
|
||||
|
||||
use crate::error::SassError;
|
||||
|
||||
const PRECISION: usize = 10;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub(crate) struct Number {
|
||||
val: BigRational,
|
||||
#[derive(Clone, Eq, PartialEq, Ord)]
|
||||
pub(crate) enum Number {
|
||||
Machine(Rational64),
|
||||
Big(BigRational),
|
||||
}
|
||||
|
||||
impl Number {
|
||||
pub const fn new(val: BigRational) -> Number {
|
||||
Number { val }
|
||||
pub const fn new_machine(val: Rational64) -> Number {
|
||||
Number::Machine(val)
|
||||
}
|
||||
|
||||
pub const fn new_big(val: BigRational) -> Number {
|
||||
Number::Big(val)
|
||||
}
|
||||
|
||||
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 {
|
||||
Number::new(BigRational::new(a.into(), b.into()))
|
||||
pub fn machine_ratio<A: Into<i64>, B: Into<i64>>(a: A, b: B) -> Self {
|
||||
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 {
|
||||
Number {
|
||||
val: self.val.round(),
|
||||
match self {
|
||||
Self::Machine(val) => Self::Machine(val.round()),
|
||||
Self::Big(val) => Self::Big(val.round()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ceil(&self) -> Self {
|
||||
Number {
|
||||
val: self.val.ceil(),
|
||||
match self {
|
||||
Self::Machine(val) => Self::Machine(val.ceil()),
|
||||
Self::Big(val) => Self::Big(val.ceil()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floor(&self) -> Self {
|
||||
Number {
|
||||
val: self.val.floor(),
|
||||
match self {
|
||||
Self::Machine(val) => Self::Machine(val.floor()),
|
||||
Self::Big(val) => Self::Big(val.floor()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn abs(&self) -> Self {
|
||||
Number {
|
||||
val: self.val.abs(),
|
||||
match self {
|
||||
Self::Machine(val) => Self::Machine(val.abs()),
|
||||
Self::Big(val) => Self::Big(val.abs()),
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -77,23 +106,35 @@ impl Number {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Number {
|
||||
fn default() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl Zero for Number {
|
||||
fn zero() -> Self {
|
||||
Number::from(0)
|
||||
Number::new_machine(Rational64::from_integer(0))
|
||||
}
|
||||
|
||||
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 {
|
||||
fn one() -> Self {
|
||||
Number::from(1)
|
||||
Number::new_machine(Rational64::from_integer(1))
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
fn abs_sub(&self, other: &Self) -> Self {
|
||||
Number {
|
||||
val: self.val.abs_sub(&other.val),
|
||||
}
|
||||
fn abs_sub(&self, _: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn signum(&self) -> Self {
|
||||
@ -126,11 +165,17 @@ impl Signed for Number {
|
||||
}
|
||||
|
||||
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 {
|
||||
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) => {
|
||||
impl From<$ty> for Number {
|
||||
fn from(b: $ty) -> Self {
|
||||
Number {
|
||||
val: BigRational::from_integer(BigInt::from(b)),
|
||||
if let Ok(v) = i64::try_from(b) {
|
||||
Number::Machine(Rational64::from_integer(v))
|
||||
} else {
|
||||
Number::Big(BigRational::from_integer(BigInt::from(b)))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// todo: implement std::convertTryFrom instead
|
||||
impl From<f64> for Number {
|
||||
fn from(b: f64) -> Self {
|
||||
Number {
|
||||
val: BigRational::from_float(b).unwrap(),
|
||||
macro_rules! from_smaller_integer {
|
||||
($ty:ty) => {
|
||||
impl From<$ty> for Number {
|
||||
fn from(val: $ty) -> Self {
|
||||
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!(isize);
|
||||
from_integer!(i32);
|
||||
from_integer!(u32);
|
||||
from_integer!(u8);
|
||||
from_smaller_integer!(i32);
|
||||
from_smaller_integer!(u32);
|
||||
from_smaller_integer!(u8);
|
||||
|
||||
impl fmt::Debug for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@ -170,13 +230,13 @@ impl fmt::Debug for Number {
|
||||
|
||||
impl Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut whole = self.val.to_integer().abs();
|
||||
let has_decimal = !self.val.denom().is_one();
|
||||
let mut frac = self.val.fract().abs();
|
||||
let mut whole = self.to_integer().abs();
|
||||
let has_decimal = self.is_decimal();
|
||||
let mut frac = self.abs().fract();
|
||||
let mut dec = String::with_capacity(if has_decimal { PRECISION + 1 } else { 0 });
|
||||
if has_decimal {
|
||||
for _ in 0..(PRECISION - 1) {
|
||||
frac *= BigInt::from(10);
|
||||
frac = frac * Self::from(10);
|
||||
write!(dec, "{}", frac.to_integer())?;
|
||||
frac = frac.fract();
|
||||
if frac.is_zero() {
|
||||
@ -184,7 +244,7 @@ impl Display for Number {
|
||||
}
|
||||
}
|
||||
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) {
|
||||
loop {
|
||||
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('-')?;
|
||||
}
|
||||
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 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
Number {
|
||||
val: self.val + other.val,
|
||||
match self {
|
||||
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;
|
||||
|
||||
fn add(self, other: &Self) -> Self {
|
||||
Number {
|
||||
val: self.val + &other.val,
|
||||
match self {
|
||||
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 {
|
||||
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;
|
||||
|
||||
fn sub(self, other: Self) -> Self {
|
||||
Number {
|
||||
val: self.val - other.val,
|
||||
match self {
|
||||
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 {
|
||||
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;
|
||||
|
||||
fn mul(self, other: Self) -> Self {
|
||||
Number {
|
||||
val: self.val * other.val,
|
||||
match self {
|
||||
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 {
|
||||
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;
|
||||
|
||||
fn div(self, other: Self) -> Self {
|
||||
Number {
|
||||
val: self.val / other.val,
|
||||
match self {
|
||||
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 {
|
||||
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;
|
||||
|
||||
fn rem(self, other: Self) -> Self {
|
||||
Number {
|
||||
val: self.val % other.val,
|
||||
match self {
|
||||
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 {
|
||||
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;
|
||||
|
||||
fn neg(self) -> Self {
|
||||
Number { val: -self.val }
|
||||
match self {
|
||||
Self::Machine(v) => Self::Machine(-v),
|
||||
Self::Big(v) => Self::Big(-v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::iter::Iterator;
|
||||
use std::mem;
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use num_rational::BigRational;
|
||||
use num_rational::{BigRational, Rational64};
|
||||
use num_traits::{pow, One, ToPrimitive};
|
||||
|
||||
use codemap::{Span, Spanned};
|
||||
@ -668,14 +668,27 @@ impl Value {
|
||||
};
|
||||
|
||||
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())
|
||||
} 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))
|
||||
};
|
||||
|
||||
if val.times_ten.is_empty() {
|
||||
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(
|
||||
Value::Dimension(Number::new(n * times_ten), unit).span(span),
|
||||
Value::Dimension(Number::new_big(n * times_ten), unit).span(span),
|
||||
)
|
||||
}
|
||||
'(' => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user