// The numeric tower: one value that can be any of the four kinds.
///|
/// A number of any kind, as the tower arranges them.
///
/// Promotion goes `Int` → `Fraction` → `Float` → `Complex`. Comparison is
/// exact across the tower: an integer is never rounded to a float to be
/// compared with one.
pub(all) enum Number {
Int(@bigint.BigInt)
Float(Double)
Fraction(Fraction)
Complex(Complex)
}
///|
/// The value as a complex number.
fn as_complex(n : Number) -> Complex {
match n {
Int(v) => { re: big_to_double(v), im: 0.0, }
Float(f) => { re: f, im: 0.0, }
Fraction(f) => { re: fraction_to_double_or_inf(f), im: 0.0, }
Complex(z) => z
}
}
///|
/// The value as a double, saturating to an infinity when it does not fit.
fn big_to_double(v : @bigint.BigInt) -> Double {
let neg = v < 0N
let magnitude = if neg { -v } else { v }
ratio_to_double(neg, magnitude, 1N)
}
///|
/// The value as a fraction, `None` for a complex number with an imaginary part.
fn as_fraction(n : Number) -> Fraction? {
match n {
Int(v) => Some(Fraction::of_int(v))
Float(f) =>
if f.is_nan() || f.is_inf() {
None
} else {
let (neg, num, den) = double_ratio(f)
Some(make(if neg { -num } else { num }, den))
}
Fraction(f) => Some(f)
Complex(_) => None
}
}
///|
/// A fraction as a double, without raising: used only where the caller has
/// already accepted the saturation.
fn fraction_to_double_or_inf(f : Fraction) -> Double {
let neg = f.num < 0N
ratio_to_double(neg, if neg { -f.num } else { f.num }, f.den)
}
///|
/// `parse(text)`: read a Python number literal and keep the kind it spelled.
///
/// A `/` makes a fraction, a trailing `j` or `i` makes a complex number, a
/// point or an exponent or `inf`/`nan` makes a float, and anything else is an
/// integer in whatever base its prefix names.
pub fn parse(text : StringView) -> Number raise Refused {
let s = text.to_owned()
let n = s.length()
let mut start = 0
while start < n && is_space(s.get_char(start).unwrap()) {
start += 1
}
let mut end = n
while end > start && is_space(s.get_char(end - 1).unwrap()) {
end -= 1
}
if start == end {
malformed(start, "expected a number")
}
let body = slice(s, start, end)
let word = lowercase_ascii(body)
let kind = if word == "inf" ||
word == "infinity" ||
word == "nan" ||
word == "+inf" ||
word == "-inf" ||
word == "+infinity" ||
word == "-infinity" ||
word == "+nan" ||
word == "-nan" {
'f'
} else if is_prefixed_int(body) {
// A base prefix settles it: `0x1e` is thirty, not a float in disguise.
'i'
} else if ends_with_imaginary(body) || body.get_char(0) == Some('(') {
// A bracketed literal is complex, as `(1+2j)` is: nothing else is wrapped.
'c'
} else if body.contains_char('/') {
'r'
} else if body.contains_char('.') || has_exponent(body) {
'f'
} else {
'i'
}
match kind {
'c' =>
Number::Complex(
parse_complex(text) catch {
(err : Refused) => raise err
},
)
'r' =>
Number::Fraction(
parse_fraction(text) catch {
(err : Refused) => raise err
},
)
'f' =>
Number::Float(parse_float(text) catch { (err : Refused) => raise err })
_ =>
Number::Int(
parse_int(text, base=0) catch {
(err : Refused) => raise err
},
)
}
}
///|
/// Whether the literal starts with a base prefix, after an optional sign.
fn is_prefixed_int(body : String) -> Bool {
let mut i = 0
if body.get_char(0) == Some('+') || body.get_char(0) == Some('-') {
i = 1
}
if i + 1 >= body.length() || body.get_char(i) != Some('0') {
return false
}
match body.get_char(i + 1).unwrap().to_ascii_lowercase() {
'x' | 'o' | 'b' => true
_ => false
}
}
///|
/// Whether the literal ends in `j` or `i`, which makes it a complex number even
/// if it also contains an exponent: `1e5j` is one hundred thousand j.
fn ends_with_imaginary(body : String) -> Bool {
if body.length() == 0 {
return false
}
match body.get_char(body.length() - 1).unwrap().to_ascii_lowercase() {
'j' | 'i' => true
_ => false
}
}
///|
/// Whether the literal has a decimal exponent, which makes it a float.
fn has_exponent(body : String) -> Bool {
let mut i = 0
while i < body.length() {
match body.get_char(i).unwrap().to_ascii_lowercase() {
'e' => {
// `e` needs a digit on either side to be an exponent.
let before = i > 0 && is_digit(body.get_char(i - 1).unwrap())
let mut after = i + 1 < body.length() &&
(
is_digit(body.get_char(i + 1).unwrap()) ||
body.get_char(i + 1) == Some('+') ||
body.get_char(i + 1) == Some('-')
)
if after {
let mut j = i + 1
if body.get_char(j) == Some('+') || body.get_char(j) == Some('-') {
j += 1
}
after = j < body.length() && is_digit(body.get_char(j).unwrap())
}
if before && after {
return true
}
}
_ => ()
}
i += 1
}
false
}
///|
/// Promote two numbers to a common kind: 0 for integers, 1 for fractions,
/// 2 for floats, 3 for complex numbers.
fn rank(n : Number) -> Int32 {
match n {
Int(_) => 0
Fraction(_) => 1
Float(_) => 2
Complex(_) => 3
}
}
///|
/// `a + b`.
///|
/// The value as a double, saturating at infinities.
fn number_to_double(n : Number) -> Double {
match n {
Int(v) => big_to_double(v)
Float(f) => f
Fraction(f) => fraction_to_double_or_inf(f)
Complex(z) => z.re
}
}
///|
/// `a - b`.
///
/// Subtraction is not addition of a negation: `-0.0 - 0` is `-0.0`, while
/// `-0.0 + -0.0` is also `-0.0` but `-0.0 + 0.0` is `0.0`.
///|
/// `a * b`.
///|
/// `a / b`, with Python's result kinds: two integers divide to a float, a
/// fraction stays exact, and a complex number is complex.
pub fn Number::divide(self : Number, other : Number) -> Number raise Refused {
let level = if rank(self) > rank(other) { rank(self) } else { rank(other) }
match level {
0 =>
match (self, other) {
(Int(a), Int(b)) => {
if b == 0N {
raise ZeroDivision
}
let neg = (a < 0N) != (b < 0N)
let an = if a < 0N { -a } else { a }
let bn = if b < 0N { -b } else { b }
Number::Float(ratio_to_double(neg, an, bn))
}
_ => Number::Int(0N)
}
1 =>
match (as_fraction(self), as_fraction(other)) {
(Some(a), Some(b)) =>
Number::Fraction(a.divide(b) catch { (err : Refused) => raise err })
_ => Number::Int(0N)
}
2 => {
if number_to_double(other) == 0.0 {
raise ZeroDivision
}
Number::Float(number_to_double(self) / number_to_double(other))
}
_ =>
Number::Complex(
as_complex(self).divide(as_complex(other)) catch {
(err : Refused) => raise err
},
)
}
}
///|
/// `a // b`.
pub fn Number::floordiv(self : Number, other : Number) -> Number raise Refused {
match (self, other) {
(Int(a), Int(b)) =>
Number::Int(floordiv(a, b) catch { (err : Refused) => raise err })
(Complex(_), _) | (_, Complex(_)) =>
raise Domain(reason="complex numbers have no floor division")
_ => {
let divisor = number_to_double(other)
if divisor == 0.0 {
raise ZeroDivision
}
match (as_fraction(self), as_fraction(other)) {
(Some(a), Some(b)) => Number::Int(a.floor_divide_big(b))
_ => Number::Float(@math.floor(number_to_double(self) / divisor))
}
}
}
}
///|
/// `a % b`: the remainder that takes the sign of the divisor.
pub fn Number::floor_mod(self : Number, other : Number) -> Number raise Refused {
match (self, other) {
(Int(a), Int(b)) =>
Number::Int(floor_mod(a, b) catch { (err : Refused) => raise err })
(Complex(_), _) | (_, Complex(_)) =>
raise Domain(reason="complex numbers have no modulo")
_ => {
let divisor = number_to_double(other)
if divisor == 0.0 {
raise ZeroDivision
}
match (as_fraction(self), as_fraction(other)) {
(Some(a), Some(b)) => Number::Fraction(a.modulo(b))
_ => {
let dividend = number_to_double(self)
let mut r = dividend % divisor
if r != 0.0 && (r < 0.0) != (divisor < 0.0) {
r += divisor
}
Number::Float(r)
}
}
}
}
}
///|
/// `a ** b`.
pub fn Number::pow(self : Number, exponent : Number) -> Number raise Refused {
match (self, exponent) {
(Int(base), Int(e)) =>
if e >= 0N {
Number::Int(power(base, e) catch { (err : Refused) => raise err })
} else {
let den = base.pow(-e)
if den == 0N {
raise ZeroDivision
}
let neg = den < 0N
Number::Float(ratio_to_double(neg, if neg { -den } else { den }, 1N))
}
(Fraction(base), Int(e)) =>
Number::Fraction(base.pow(e) catch { (err : Refused) => raise err })
(Complex(base), _) =>
Number::Complex(
base.pow(as_complex(exponent)) catch {
(err : Refused) => raise err
},
)
(_, Complex(exp)) =>
Number::Complex(
as_complex(self).pow(exp) catch {
(err : Refused) => raise err
},
)
_ =>
Number::Float(
@math.pow(number_to_double(self), number_to_double(exponent)),
)
}
}
///|
/// `-a`.
///|
/// Compare two real numbers, exactly.
///
/// Raises `Domain` for a NaN operand and for a complex number with a non-zero
/// imaginary part, which Python refuses to order.
pub fn Number::compare_numeric(
self : Number,
other : Number,
) -> Int32 raise Refused {
match (self, other) {
(Complex(_), _) | (_, Complex(_)) =>
raise Domain(reason="complex numbers are not ordered")
(Int(a), Int(b)) => a.compare(b)
(Float(a), Float(b)) => compare_floats(a, b)
(Fraction(a), Fraction(b)) => a.compare(b)
(Int(a), Float(b)) => compare_int_float(a, b)
(Float(a), Int(b)) => -compare_int_float(b, a)
(Fraction(a), Float(b)) => compare_fraction_float(a, b)
(Float(a), Fraction(b)) => -compare_fraction_float(b, a)
_ => {
let left = as_exact_real(self)
let right = as_exact_real(other)
match (left, right) {
(Some(a), Some(b)) => a.compare(b)
_ => raise Domain(reason="these numbers are not ordered")
}
}
}
}
///|
/// A number as an exact fraction, `None` for a complex number that is not real.
fn as_exact_real(n : Number) -> Fraction? {
match n {
Complex(z) =>
if z.im == 0.0 && !z.re.is_nan() {
as_fraction(Number::Float(z.re))
} else {
None
}
_ => as_fraction(n)
}
}
///|
/// Order two doubles, refusing NaN.
fn compare_floats(a : Double, b : Double) -> Int32 raise Refused {
if a.is_nan() || b.is_nan() {
raise Domain(reason="NaN is not ordered")
}
if a < b {
-1
} else if a > b {
1
} else {
0
}
}
///|
/// Order an integer against a double without rounding the integer.
fn compare_int_float(a : @bigint.BigInt, b : Double) -> Int32 raise Refused {
if b.is_nan() {
raise Domain(reason="NaN is not ordered")
}
if b.is_inf() {
return if b > 0.0 { -1 } else { 1 }
}
let (neg, num, den) = double_ratio(b)
let scaled = if neg { -(a * den) } else { a * den }
scaled.compare(num)
}
///|
/// Order a fraction against a double without rounding either one.
fn compare_fraction_float(a : Fraction, b : Double) -> Int32 raise Refused {
if b.is_nan() {
raise Domain(reason="NaN is not ordered")
}
if b.is_inf() {
return if b > 0.0 { -1 } else { 1 }
}
let (neg, num, den) = double_ratio(b)
let other = if neg { -(num * a.den) } else { num * a.den }
(a.num * den).compare(other)
}
///|
/// `a == b`, across the tower: `1 == 1.0 == Fraction(1) == 1 + 0j`.
pub fn Number::equal_numeric(self : Number, other : Number) -> Bool {
match (self, other) {
(Int(a), Int(b)) => a == b
(Float(a), Float(b)) => a == b
(Fraction(a), Fraction(b)) => a == b
(Complex(a), Complex(b)) => a == b
(Complex(z), _) =>
z.im == 0.0 && Number::equal_numeric(Number::Float(z.re), other)
(_, Complex(z)) =>
z.im == 0.0 && Number::equal_numeric(self, Number::Float(z.re))
_ =>
match (as_exact_real(self), as_exact_real(other)) {
(Some(a), Some(b)) => a == b
_ => false
}
}
}
///|
/// The value as a double, or `Overflow` when it is infinite or not real.
pub fn Number::to_double(self : Number) -> Double raise Refused {
match self {
Complex(z) =>
if z.im != 0.0 {
raise Domain(
reason="cannot convert a non-real complex number to a float",
)
} else {
z.re
}
_ => {
let value = number_to_double(self)
if value.is_inf() {
raise Overflow(reason="number too large to convert to float")
}
value
}
}
}
///|
pub impl Show for Number with fn to_string(self) {
match self {
Int(v) => v.to_string()
Float(f) => repr_float(f)
Fraction(f) => f.to_string()
Complex(z) => repr_complex(z)
}
}
///|
pub impl Eq for Number with fn equal(self, other) {
self.equal_numeric(other)
}
///|
pub fn Number::add(self : Number, other : Number) -> Number {
self + other
}
///|
pub fn Number::sub(self : Number, other : Number) -> Number {
self - other
}
///|
pub fn Number::mul(self : Number, other : Number) -> Number {
self * other
}
///|
pub fn Number::neg(self : Number) -> Number {
-self
}
///|
/// Addition stays inside the higher of the two kinds, which is what Python's
/// tower does: an integer plus a fraction is a fraction, never a float.
pub impl Add for Number with fn add(self, other) {
let level = if rank(self) > rank(other) { rank(self) } else { rank(other) }
match level {
0 =>
match (self, other) {
(Int(a), Int(b)) => Number::Int(a + b)
_ => Number::Int(0N)
}
1 =>
match (as_fraction(self), as_fraction(other)) {
(Some(a), Some(b)) => Number::Fraction(a + b)
_ => Number::Int(0N)
}
2 => Number::Float(number_to_double(self) + number_to_double(other))
_ => Number::Complex(as_complex(self) + as_complex(other))
}
}
///|
/// Subtraction, spelled out instead of adding a negation: `-0.0 - 0` is
/// `-0.0` and `-0.0 + 0.0` is not.
pub impl Sub for Number with fn sub(self, other) {
let level = if rank(self) > rank(other) { rank(self) } else { rank(other) }
match level {
0 =>
match (self, other) {
(Int(a), Int(b)) => Number::Int(a - b)
_ => Number::Int(0N)
}
1 =>
match (as_fraction(self), as_fraction(other)) {
(Some(a), Some(b)) => Number::Fraction(a - b)
_ => Number::Int(0N)
}
2 => Number::Float(number_to_double(self) - number_to_double(other))
_ => Number::Complex(as_complex(self) - as_complex(other))
}
}
///|
pub impl Mul for Number with fn mul(self, other) {
let level = if rank(self) > rank(other) { rank(self) } else { rank(other) }
match level {
0 =>
match (self, other) {
(Int(a), Int(b)) => Number::Int(a * b)
_ => Number::Int(0N)
}
1 =>
match (as_fraction(self), as_fraction(other)) {
(Some(a), Some(b)) => Number::Fraction(a * b)
_ => Number::Int(0N)
}
2 => Number::Float(number_to_double(self) * number_to_double(other))
_ => Number::Complex(as_complex(self) * as_complex(other))
}
}
///|
pub impl Neg for Number with fn neg(self) {
match self {
Int(v) => Number::Int(-v)
Float(f) => Number::Float(-f)
Fraction(f) => Number::Fraction(-f)
Complex(z) => Number::Complex(-z)
}
}
///|
pub extend Number with Show::{to_string, output}
///|
pub extend Number with Eq::{equal, not_equal}