Merge branch 'float2'

This commit is contained in:
Tiffany Bennett 2016-09-26 00:37:07 -04:00
commit efd3182f46
10 changed files with 446 additions and 110 deletions

View file

@ -4,7 +4,7 @@
use std::rc::Rc;
use std::fmt;
use gmp::mpq::Mpq;
use number::Num;
#[derive(Debug, Clone)]
pub enum SuffixOp {
@ -31,7 +31,7 @@ pub enum DateToken {
pub enum Expr {
Unit(String),
Quote(String),
Const(Mpq),
Const(Num),
Date(Vec<DateToken>),
Frac(Box<Expr>, Box<Expr>),
Mul(Vec<Expr>),

View file

@ -3,9 +3,8 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::collections::{BTreeMap, BTreeSet};
use number::{Dim, Number, Unit};
use number::{Dim, Number, Unit, Num};
use ast::{Expr, DatePattern};
use gmp::mpq::Mpq;
use search;
/// The evaluation context that contains unit definitions.
@ -157,8 +156,8 @@ impl Context {
let mut buf = vec![];
let mut recip = false;
let square = Number(Mpq::one(), value.1.clone()).root(2).ok();
let inverse = (&Number::one() / &Number(Mpq::one(), value.1.clone())).unwrap();
let square = Number(Num::one(), value.1.clone()).root(2).ok();
let inverse = (&Number::one() / &Number(Num::one(), value.1.clone())).unwrap();
if let Some(name) = self.quantities.get(&value.1) {
write!(buf, "{}", name).unwrap();
} else if let Some(name) = square.and_then(|square| self.quantities.get(&square.1)) {

View file

@ -8,7 +8,7 @@ use xml::EventReader;
use xml::reader::XmlEvent;
use ast::{Defs, Def, Expr};
use std::rc::Rc;
use gmp::mpq::Mpq;
use number::Num;
static URL: &'static str = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
@ -40,7 +40,7 @@ pub fn parse(f: File) -> Result<Defs, String> {
if let Ok(num) = ::number::Number::from_parts(integer, frac, None) {
out.push((currency.to_owned(), Rc::new(Def::Unit(
Expr::Mul(vec![
Expr::Frac(Box::new(Expr::Const(Mpq::one())),
Expr::Frac(Box::new(Expr::Const(Num::one())),
Box::new(Expr::Const(num))),
Expr::Unit("EUR".to_string())
]))), Some(format!("Sourced from European Central Bank."))));

View file

@ -6,7 +6,7 @@ use ast::{DatePattern, DateToken, show_datepattern};
use chrono::format::Parsed;
use chrono::{Weekday, DateTime, UTC, FixedOffset, Duration, Date};
use context::Context;
use number::{Number, Dim};
use number::{Number, Num, Dim};
use std::iter::Peekable;
pub fn parse_date<I>(
@ -292,24 +292,17 @@ pub fn try_decode(date: &[DateToken], context: &Context) -> Result<DateTime<Fixe
}
pub fn to_duration(num: &Number) -> Result<Duration, String> {
use gmp::mpq::Mpq;
use gmp::mpz::Mpz;
if num.1.len() != 1 || num.1.get("s") != Some(&1) {
return Err(format!("Expected seconds"))
}
let max = Mpq::ratio(&Mpz::from(i64::max_value() / 1000), &Mpz::one());
let max = Num::from(i64::max_value() / 1000);
if num.0.abs() > max {
return Err(format!("Implementation error: Number is out of range ({:?})", max))
}
let ms_div = Mpz::from(1_000);
let ms = &(&num.0.get_num() * &ms_div) / &num.0.get_den();
let ns_div = Mpz::from(1_000_000_000);
let ns = &num.0 - &Mpq::ratio(&ms, &ms_div);
let ns = &(&ns.get_num() * &ns_div) / &ns.get_den();
let ms: Option<i64> = (&ms).into();
let ns: Option<i64> = (&ns).into();
Ok(Duration::milliseconds(ms.unwrap()) + Duration::nanoseconds(ns.unwrap()))
let (ms, rem) = num.0.div_rem(&Num::from(1000));
let ns = &rem / &Num::from(1_000_000_000);
Ok(Duration::milliseconds(ms.to_int().unwrap()) +
Duration::nanoseconds(ns.to_int().unwrap()))
}
pub fn from_duration(duration: &Duration) -> Result<Number, String> {
@ -322,7 +315,7 @@ pub fn from_duration(duration: &Duration) -> Result<Number, String> {
let ns_div = Mpz::from(1_000_000_000);
let ms = Mpq::ratio(&Mpz::from(ms), &ms_div);
let ns = Mpq::ratio(&Mpz::from(ns), &ns_div);
Ok(Number::new_unit(&ms + &ns, Dim::new("s")))
Ok(Number::new_unit(Num::Mpq(&ms + &ns), Dim::new("s")))
}
pub fn now() -> DateTime<FixedOffset> {

View file

@ -3,8 +3,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::collections::BTreeMap;
use gmp::mpq::Mpq;
use number::{Number, Dim, NumberParts, pow};
use number::{Number, Num, Int, Dim, NumberParts, pow};
use date;
use ast::{Expr, SuffixOp, Query, Conversion};
use std::rc::Rc;
@ -17,7 +16,6 @@ use reply::{
};
use search;
use context::Context;
use gmp::mpz::Mpz;
impl Context {
/// Evaluates an expression to compute its value, *excluding* `->`
@ -93,19 +91,149 @@ impl Context {
}),
Expr::Equals(_, ref right) => self.eval(right),
Expr::Call(ref name, ref args) => {
let args = try!(args.iter().map(|x| self.eval(x)).collect::<Result<Vec<_>, _>>());
let args = try!(
args.iter()
.map(|x| self.eval(x))
.collect::<Result<Vec<_>, _>>());
macro_rules! func {
(fn $fname:ident($($name:ident : $ty:ident),*) $block:block) => {{
let mut iter = args.iter();
let mut count = 0;
$( count += 1; let _ = stringify!($name); )*;
$(
let $name = match iter.next() {
Some(&Value::$ty(ref v)) => v,
Some(x) => return Err(format!(
"Expected {}, got <{}>",
stringify!($ty), x.show(self))),
None => return Err(format!(
"Argument number mismatch for {}: \
Expected {}, got {}",
stringify!($fname), count, args.len()))
};
)*;
if iter.next().is_some() {
return Err(format!(
"Argument number mismatch for {}: \
Expected {}, got {}",
stringify!($fname), count, args.len()));
}
let res: Result<Value, String> = {
$block
};
res.map_err(|e| {
format!(
"{}: {}({})",
e, stringify!($fname),
args.iter()
.map(|x| x.show(self))
.collect::<Vec<_>>()
.join(", "))
})
}
}}
match &**name {
"sqrt" => {
if args.len() != 1 {
return Err(format!("Argument number mismatch for sqrt: expected 1, got {}", args.len()))
"sqrt" => func!(fn sqrt(num: Number) {
num.root(2).map(Value::Number)
}),
"exp" => func!(fn exp(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().exp()), num.1.clone())))
}),
"ln" => func!(fn ln(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().ln()), num.1.clone())))
}),
"log" => func!(fn log(num: Number, base: Number) {
if base.1.len() > 0 {
Err(format!(
"Base must be dimensionless"))
} else {
Ok(Value::Number(Number(
Num::Float(num.0.to_f64().log(base.0.to_f64())),
num.1.clone())))
}
match args[0] {
Value::Number(ref num) =>
num.root(2).map(Value::Number).map_err(|e| format!(
"{}: sqrt <{}>", e, num.show(self))),
ref x => Err(format!("Expected number, got <{}>", x.show(self)))
}),
"log2" => func!(fn log2(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().log2()), num.1.clone())))
}),
"log10" => func!(fn ln(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().log10()), num.1.clone())))
}),
"hypot" => func!(fn hypot(x: Number, y: Number) {
if x.1 != y.1 {
Err(format!(
"Arguments to hypot must have matching \
dimensionality"))
} else {
Ok(Value::Number(Number(
Num::Float(x.0.to_f64().hypot(y.0.to_f64())),
x.1.clone())))
}
},
}),
"sin" => func!(fn sin(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().sin()), num.1.clone())))
}),
"cos" => func!(fn cos(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().cos()), num.1.clone())))
}),
"tan" => func!(fn tan(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().tan()), num.1.clone())))
}),
"asin" => func!(fn asin(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().asin()), num.1.clone())))
}),
"acos" => func!(fn acos(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().acos()), num.1.clone())))
}),
"atan" => func!(fn atan(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().atan()), num.1.clone())))
}),
"atan2" => func!(fn atan2(x: Number, y: Number) {
if x.1 != y.1 {
Err(format!(
"Arguments to atan2 must have matching \
dimensionality"))
} else {
Ok(Value::Number(Number(
Num::Float(x.0.to_f64().atan2(y.0.to_f64())),
x.1.clone())))
}
}),
"sinh" => func!(fn sinh(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().sinh()), num.1.clone())))
}),
"cosh" => func!(fn cosh(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().cosh()), num.1.clone())))
}),
"tanh" => func!(fn tanh(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().tanh()), num.1.clone())))
}),
"asinh" => func!(fn asinh(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().asinh()), num.1.clone())))
}),
"acosh" => func!(fn acosh(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().acosh()), num.1.clone())))
}),
"atanh" => func!(fn atanh(num: Number) {
Ok(Value::Number(Number(Num::Float(
num.0.to_f64().atanh()), num.1.clone())))
}),
_ => Err(format!("Function not found: {}", name))
}
},
@ -113,13 +241,13 @@ impl Context {
}
}
pub fn eval_unit_name(&self, expr: &Expr) -> Result<(BTreeMap<String, isize>, Mpq), String> {
pub fn eval_unit_name(&self, expr: &Expr) -> Result<(BTreeMap<String, isize>, Num), String> {
match *expr {
Expr::Equals(ref left, ref _right) => match **left {
Expr::Unit(ref name) => {
let mut map = BTreeMap::new();
map.insert(name.clone(), 1);
Ok((map, Mpq::one()))
Ok((map, Num::one()))
},
ref x => Err(format!("Expected identifier, got {:?}", x))
},
@ -127,7 +255,7 @@ impl Context {
Expr::Unit(ref name) | Expr::Quote(ref name) => {
let mut map = BTreeMap::new();
map.insert(self.canonicalize(&**name).unwrap_or_else(|| name.clone()), 1);
Ok((map, Mpq::one()))
Ok((map, Num::one()))
},
Expr::Const(ref i) =>
Ok((BTreeMap::new(), i.clone())),
@ -156,7 +284,7 @@ impl Context {
if res.1.len() > 0 {
return Err(format!("Exponents must be dimensionless"))
}
let res: f64 = res.0.into();
let res = res.0.to_f64();
let (left, lv) = try!(self.eval_unit_name(left));
Ok((left.into_iter()
.filter_map(|(k, v)| {
@ -189,9 +317,9 @@ impl Context {
fn conformance_err(&self, top: &Number, bottom: &Number) -> ConformanceError {
let mut topu = top.clone();
topu.0 = Mpq::one();
topu.0 = Num::one();
let mut bottomu = bottom.clone();
bottomu.0 = Mpq::one();
bottomu.0 = Num::one();
let mut suggestions = vec![];
let diff = (&topu * &bottomu).unwrap();
if diff.1.len() == 0 {
@ -224,23 +352,24 @@ impl Context {
raw: &Number,
bottom: &Number,
bottom_name: BTreeMap<String, isize>,
bottom_const: Mpq,
bottom_const: Num,
base: u8
) -> ConversionReply {
let (exact, approx) = raw.numeric_value(base);
let bottom_name = bottom_name.into_iter().map(
|(a,b)| (Dim::new(&*a), b as i64)).collect();
let (num, den) = bottom_const.to_rational();
ConversionReply {
value: NumberParts {
exact_value: exact,
approx_value: approx,
factor: if bottom_const.get_num() != Mpz::one() {
Some(format!("{}", bottom_const.get_num()))
factor: if num != Int::one() {
Some(format!("{}", num))
} else {
None
},
divfactor: if bottom_const.get_den() != Mpz::one() {
Some(format!("{}", bottom_const.get_den()))
divfactor: if den != Int::one() {
Some(format!("{}", den))
} else {
None
},
@ -278,14 +407,12 @@ impl Context {
let mut out = vec![];
let len = units.len();
for (i, unit) in units.into_iter().enumerate() {
let res = &value / &unit.0;
let div = &res.get_num() / res.get_den();
let rem = &value - &(&unit.0 * &Mpq::ratio(&div, &Mpz::one()));
value = rem;
if i == len-1 {
out.push(res);
out.push(&value / &unit.0);
} else {
out.push(Mpq::ratio(&div, &Mpz::one()));
let (div, rem) = value.div_rem(&unit.0);
out.push(div);
value = rem;
}
}
Ok(list.into_iter().zip(out.into_iter()).map(|(name, value)| {
@ -479,7 +606,7 @@ impl Context {
name.insert(format!("°{}", $name), 1);
Ok(QueryReply::Conversion(self.show(
&res, &bottom,
name, Mpq::one(),
name, Num::one(),
10)))
}
}}

View file

@ -4,9 +4,8 @@
use std::collections::{BTreeMap, BinaryHeap};
use std::rc::Rc;
use number::{Number, Unit, Dim};
use number::{Number, Num, Unit, Dim};
use std::cmp;
use gmp::mpq::Mpq;
#[derive(PartialEq, Eq, Debug)]
pub struct Factors(pub usize, pub Vec<Rc<String>>);
@ -34,7 +33,7 @@ pub fn fast_decompose(value: &Number, quantities: &BTreeMap<Unit, String>) -> Un
continue 'outer
}
}
let num = Number(Mpq::one(), unit.clone());
let num = Number(Num::one(), unit.clone());
for &i in [-1, 1, 2].into_iter() {
let res = (value / &num.powi(i)).unwrap();
let score = res.complexity_score();
@ -46,7 +45,7 @@ pub fn fast_decompose(value: &Number, quantities: &BTreeMap<Unit, String>) -> Un
}
if let Some((name, unit, pow, score)) = best {
if score < value.complexity_score() {
let mut res = (value / &Number(Mpq::one(), unit.clone()).powi(pow)).unwrap().1;
let mut res = (value / &Number(Num::one(), unit.clone()).powi(pow)).unwrap().1;
res.insert(Dim::new(&**name), pow as i64);
return res
}
@ -64,7 +63,7 @@ pub fn factorize(value: &Number, quantities: &BTreeMap<Unit, Rc<String>>)
let mut candidates: BinaryHeap<Factors> = BinaryHeap::new();
let value_score = value.complexity_score();
for (unit, name) in quantities.iter().rev() {
let res = (value / &Number(Mpq::one(), unit.clone())).unwrap();
let res = (value / &Number(Num::one(), unit.clone())).unwrap();
//if res.1.len() >= value.1.len() {
let score = res.complexity_score();
// we are not making the unit any simpler

View file

@ -6,7 +6,7 @@ use std::str::Chars;
use std::iter::Peekable;
use std::rc::Rc;
use ast::*;
use gmp::mpq::Mpq;
use number::Num;
#[derive(Debug, Clone)]
pub enum Token {
@ -184,7 +184,7 @@ fn parse_term(mut iter: &mut Iter) -> Expr {
Token::Plus => Expr::Plus(Box::new(parse_term(iter))),
Token::Dash => Expr::Neg(Box::new(parse_term(iter))),
Token::Slash => Expr::Frac(
Box::new(Expr::Const(Mpq::one())),
Box::new(Expr::Const(Num::one())),
Box::new(parse_term(iter))),
Token::LPar => {
let res = parse_expr(iter);

View file

@ -3,8 +3,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::collections::{BTreeMap, BTreeSet};
use gmp::mpq::Mpq;
use number::Dim;
use number::{Dim, Num};
use ast::{Expr, Def, Defs};
use std::rc::Rc;
use value::Value;
@ -232,7 +231,7 @@ impl Context {
},
Def::Unit(ref expr) => match self.eval(expr) {
Ok(Value::Number(v)) => {
if v.0 == Mpq::one() && reverse.contains(&*name) {
if v.0 == Num::one() && reverse.contains(&*name) {
self.reverse.insert(v.1.clone(), name.clone());
}
self.definitions.insert(name.clone(), expr.clone());

View file

@ -11,9 +11,191 @@ use std::rc::Rc;
use std::fmt;
use std::borrow::Borrow;
use context::Context;
use std::cmp::Ordering;
pub type Int = Mpz;
/// Number type.
#[derive(Clone, PartialEq, Debug)]
pub enum Num {
/// Arbitrary-precision rational fraction.
Mpq(Mpq),
/// Machine floats.
Float(f64),
// /// Machine ints.
// Int(i64),
}
enum NumParity {
Mpq(Mpq, Mpq),
Float(f64, f64)
}
impl Num {
pub fn one() -> Num {
Num::Mpq(Mpq::one())
//Num::Int(1)
}
pub fn zero() -> Num {
Num::Mpq(Mpq::zero())
//Num::Int(0)
}
pub fn abs(&self) -> Num {
match *self {
Num::Mpq(ref mpq) => Num::Mpq(mpq.abs()),
Num::Float(f) => Num::Float(f.abs()),
}
}
fn parity(&self, other: &Num) -> NumParity {
match (self, other) {
(&Num::Float(left), right) =>
NumParity::Float(left, right.into()),
(left, &Num::Float(right)) =>
NumParity::Float(left.into(), right),
(&Num::Mpq(ref left), &Num::Mpq(ref right)) =>
NumParity::Mpq(left.clone(), right.clone()),
}
}
pub fn div_rem(&self, other: &Num) -> (Num, Num) {
match self.parity(other) {
NumParity::Mpq(left, right) => {
let div = &left / &right;
let floor = &div.get_num() / div.get_den();
let rem = &left - &(&right * &Mpq::ratio(&floor, &Mpz::one()));
(Num::Mpq(Mpq::ratio(&floor, &Mpz::one())), Num::Mpq(rem))
},
NumParity::Float(left, right) => {
(Num::Float(left / right), Num::Float(left % right))
},
}
}
pub fn to_rational(&self) -> (Int, Int) {
match *self {
Num::Mpq(ref mpq) => (mpq.get_num(), mpq.get_den()),
Num::Float(mut x) => {
let mut m = [
[1, 0],
[0, 1]
];
let maxden = 1_000_000;
// loop finding terms until denom gets too big
loop {
let ai = x as i64;
if m[1][0] * ai + m[1][1] > maxden {
break;
}
let mut t;
t = m[0][0] * ai + m[0][1];
m[0][1] = m[0][0];
m[0][0] = t;
t = m[1][0] * ai + m[1][1];
m[1][1] = m[1][0];
m[1][0] = t;
if x == ai as f64 {
break; // division by zero
}
x = 1.0/(x - ai as f64);
if x as i64 > i64::max_value() / 2 {
break; // representation failure
}
}
(Int::from(m[0][0]), Int::from(m[1][0]))
},
}
}
pub fn to_int(&self) -> Option<i64> {
match *self {
Num::Mpq(ref mpq) => (&(mpq.get_num() / mpq.get_den())).into(),
Num::Float(f) => if f.abs() < i64::max_value() as f64 {
Some(f as i64)
} else {
None
},
}
}
pub fn to_f64(&self) -> f64 {
self.into()
}
}
impl From<Mpq> for Num {
fn from(mpq: Mpq) -> Num {
Num::Mpq(mpq)
}
}
impl From<Mpz> for Num {
fn from(mpz: Mpz) -> Num {
Num::Mpq(Mpq::ratio(&mpz, &Mpz::one()))
}
}
impl From<i64> for Num {
fn from(i: i64) -> Num {
Num::from(Mpz::from(i))
}
}
impl<'a> Into<f64> for &'a Num {
fn into(self) -> f64 {
match *self {
Num::Mpq(ref mpq) => mpq.clone().into(),
Num::Float(f) => f,
}
}
}
impl PartialOrd for Num {
fn partial_cmp(&self, other: &Num) -> Option<Ordering> {
match self.parity(other) {
NumParity::Mpq(left, right) => left.partial_cmp(&right),
NumParity::Float(left, right) => left.partial_cmp(&right),
}
}
}
macro_rules! num_binop {
($what:ident, $func:ident) => {
impl<'a, 'b> $what<&'b Num> for &'a Num {
type Output = Num;
fn $func(self, other: &'b Num) -> Num {
match self.parity(other) {
NumParity::Mpq(left, right) =>
Num::Mpq(left.$func(&right)),
NumParity::Float(left, right) =>
Num::Float(left.$func(&right)),
}
}
}
}
}
num_binop!(Add, add);
num_binop!(Sub, sub);
num_binop!(Mul, mul);
num_binop!(Div, div);
impl<'a> Neg for &'a Num {
type Output = Num;
fn neg(self) -> Num {
match *self {
Num::Mpq(ref mpq) => Num::Mpq(-mpq),
Num::Float(f) => Num::Float(-f),
}
}
}
/// Number type
pub type Num = Mpq;
/// Alias for the primary representation of dimensionality.
pub type Unit = BTreeMap<Dim, i64>;
@ -22,7 +204,7 @@ pub type Unit = BTreeMap<Dim, i64>;
pub struct Dim(pub Rc<String>);
/// The basic representation of a number with a unit.
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq)]
pub struct Number(pub Num, pub Unit);
impl Borrow<str> for Dim {
@ -43,41 +225,48 @@ impl Dim {
}
}
fn one() -> Mpq {
Mpq::one()
}
fn zero() -> Mpq {
Mpq::zero()
}
pub fn pow(left: &Mpq, exp: i32) -> Mpq {
pub fn pow(left: &Num, exp: i32) -> Num {
if exp < 0 {
one() / pow(left, -exp)
&Num::one() / &pow(left, -exp)
} else {
let left = match *left {
Num::Mpq(ref left) => left,
Num::Float(f) => return Num::Float(f.powi(exp))
};
let num = left.get_num().pow(exp as u32);
let den = left.get_den().pow(exp as u32);
Mpq::ratio(&num, &den)
Num::Mpq(Mpq::ratio(&num, &den))
}
}
fn root(left: &Mpq, n: i32) -> Mpq {
fn root(left: &Num, n: i32) -> Num {
if n < 0 {
one() / root(left, -n)
&Num::one() / &root(left, -n)
} else {
let left = match *left {
Num::Mpq(ref left) => left,
Num::Float(f) => return Num::Float(f.powf(1.0 / n as f64))
};
let num = left.get_num().root(n as u32);
let den = left.get_den().root(n as u32);
Mpq::ratio(&num, &den)
Num::Mpq(Mpq::ratio(&num, &den))
}
}
pub fn to_string(rational: &Mpq, base: u8) -> (bool, String) {
pub fn to_string(rational: &Num, base: u8) -> (bool, String) {
use std::char::from_digit;
let sign = *rational < Mpq::zero();
let sign = *rational < Num::zero();
let rational = rational.abs();
let num = rational.get_num();
let den = rational.get_den();
let (num, den) = rational.to_rational();
let rational = match rational {
Num::Mpq(mpq) => mpq,
Num::Float(f) => {
let mut m = Mpq::one();
m.set_d(f);
m
},
};
let intdigits = (&num / &den).size_in_base(base) as u32;
let mut buf = String::new();
@ -85,10 +274,10 @@ pub fn to_string(rational: &Mpq, base: u8) -> (bool, String) {
buf.push('-');
}
let zero = Mpq::zero();
let one = Mpz::one();
let ten = Mpz::from(base as u64);
let one = Int::one();
let ten = Int::from(base as u64);
let ten_mpq = Mpq::ratio(&ten, &one);
let mut cursor = rational / Mpq::ratio(&ten.pow(intdigits), &one);
let mut cursor = &rational / &Mpq::ratio(&ten.pow(intdigits), &one);
let mut n = 0;
let mut only_zeros = true;
let mut zeros = 0;
@ -331,15 +520,15 @@ impl fmt::Display for NumberParts {
impl Number {
pub fn one() -> Number {
Number(one(), Unit::new())
Number(Num::one(), Unit::new())
}
pub fn one_unit(unit: Dim) -> Number {
Number::new_unit(one(), unit)
Number::new_unit(Num::one(), unit)
}
pub fn zero() -> Number {
Number(zero(), Unit::new())
Number(Num::zero(), Unit::new())
}
/// Creates a dimensionless value.
@ -354,7 +543,7 @@ impl Number {
Number(num, map)
}
pub fn from_parts(integer: &str, frac: Option<&str>, exp: Option<&str>) -> Result<Mpq, String> {
pub fn from_parts(integer: &str, frac: Option<&str>, exp: Option<&str>) -> Result<Num, String> {
use std::str::FromStr;
let num = Mpz::from_str_radix(integer, 10).unwrap();
@ -382,12 +571,12 @@ impl Number {
};
let num = &Mpq::ratio(&num, &Mpz::one()) + &frac;
let num = &num * &exp;
Ok(num)
Ok(Num::Mpq(num))
}
/// Computes the reciprocal (1/x) of the value.
pub fn invert(&self) -> Number {
Number(&one() / &self.0,
Number(&Num::one() / &self.0,
self.1.iter()
.map(|(k, &power)| (k.clone(), -power))
.collect::<Unit>())
@ -404,7 +593,7 @@ impl Number {
/// Computes the nth root of a value iff all of its units have
/// powers divisible by n.
pub fn root(&self, exp: i32) -> Result<Number, String> {
if self.0 < Mpq::zero() {
if self.0 < Num::zero() {
return Err(format!("Complex numbers are not implemented"))
}
let mut res = Unit::new();
@ -425,11 +614,8 @@ impl Number {
if exp.1.len() != 0 {
return Err(format!("Exponent must be dimensionless"))
}
let mut exp = exp.0.clone();
exp.canonicalize();
let num = exp.get_num();
let den = exp.get_den();
let one = Mpz::one();
let (num, den) = exp.0.to_rational();
let one = Int::one();
if den == one {
let exp: Option<i64> = (&num).into();
Ok(self.powi(exp.unwrap() as i32))
@ -442,14 +628,24 @@ impl Number {
}
pub fn numeric_value(&self, base: u8) -> (Option<String>, Option<String>) {
match to_string(&self.0, base) {
(true, v) => (Some(v), None),
(false, v) => if {self.0.get_den() > Mpz::from(1_000_000) ||
self.0.get_num() > Mpz::from(1_000_000_000u64)} {
(None, Some(v))
} else {
(Some(format!("{:?}", self.0)), Some(v))
}
match self.0 {
Num::Mpq(ref mpq) => {
let num = mpq.get_num();
let den = mpq.get_den();
match to_string(&self.0, base) {
(true, v) => (Some(v), None),
(false, v) => if {den > Mpz::from(1_000_000) ||
num > Mpz::from(1_000_000_000u64)} {
(None, Some(v))
} else {
(Some(format!("{}/{}", num, den)), Some(v))
}
}
},
Num::Float(_f) => {
(None, Some(to_string(&self.0, base).1))
},
}
}
@ -476,7 +672,7 @@ impl Number {
let orig = unit.iter().next().unwrap();
// kg special case
let (val, orig) = if &**(orig.0).0 == "kg" || &**(orig.0).0 == "kilogram" {
(&self.0 * &pow(&Mpq::ratio(&Mpz::from(1000), &Mpz::one()), (*orig.1) as i32),
(&self.0 * &pow(&Num::from(1000), (*orig.1) as i32),
(Dim::new("gram"), orig.1))
} else {
(self.0.clone(), (orig.0.clone(), orig.1))
@ -487,7 +683,7 @@ impl Number {
}
let abs = val.abs();
if { abs >= pow(&v.0, (*orig.1) as i32) &&
abs < pow(&(&v.0 * &Mpq::ratio(&Mpz::from(1000), &Mpz::one())),
abs < pow(&(&v.0 * &Num::from(1000)),
(*orig.1) as i32) } {
let res = &val / &pow(&v.0, (*orig.1) as i32);
// tonne special case
@ -641,7 +837,7 @@ impl<'a, 'b> Div<&'b Number> for &'a Number {
type Output = Option<Number>;
fn div(self, other: &Number) -> Self::Output {
if other.0 == zero() {
if other.0 == Num::zero() {
None
} else {
self * &other.invert()

View file

@ -7,6 +7,7 @@ use std::iter::Peekable;
use ast::*;
use gmp::mpz::Mpz;
use gmp::mpq::Mpq;
use number::Num;
#[derive(Debug, Clone)]
pub enum Token {
@ -423,6 +424,25 @@ pub type Iter<'a> = Peekable<TokenIterator<'a>>;
fn is_func(name: &str) -> bool {
match name {
"sqrt" => true,
"exp" => true,
"ln" => true,
"log" => true,
"log2" => true,
"log10" => true,
"hypot" => true,
"sin" => true,
"cos" => true,
"tan" => true,
"asin" => true,
"acos" => true,
"atan" => true,
"atan2" => true,
"sinh" => true,
"cosh" => true,
"tanh" => true,
"asinh" => true,
"acosh" => true,
"atanh" => true,
_ => false
}
}
@ -493,16 +513,19 @@ fn parse_term(mut iter: &mut Iter) -> Expr {
Token::Hex(num) =>
Mpz::from_str_radix(&*num, 16)
.map(|x| Mpq::ratio(&x, &Mpz::one()))
.map(Num::Mpq)
.map(Expr::Const)
.unwrap_or_else(|()| Expr::Error(format!("Failed to parse hex"))),
Token::Oct(num) =>
Mpz::from_str_radix(&*num, 8)
.map(|x| Mpq::ratio(&x, &Mpz::one()))
.map(Num::Mpq)
.map(Expr::Const)
.unwrap_or_else(|()| Expr::Error(format!("Failed to parse octal"))),
Token::Bin(num) =>
Mpz::from_str_radix(&*num, 2)
.map(|x| Mpq::ratio(&x, &Mpz::one()))
.map(Num::Mpq)
.map(Expr::Const)
.unwrap_or_else(|()| Expr::Error(format!("Failed to parse binary"))),
Token::Plus => Expr::Plus(Box::new(parse_term(iter))),