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);
|
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()
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
'(' => {
|
'(' => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user