// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! An interface for numeric types use core::cmp::{Ord, Eq}; use option::{None, Option, Some}; use char; use str; use kinds::Copy; use vec; pub trait Num { // FIXME: Trait composition. (#2616) pure fn add(&self, other: &Self) -> Self; pure fn sub(&self, other: &Self) -> Self; pure fn mul(&self, other: &Self) -> Self; pure fn div(&self, other: &Self) -> Self; pure fn modulo(&self, other: &Self) -> Self; pure fn neg(&self) -> Self; pure fn to_int(&self) -> int; static pure fn from_int(n: int) -> Self; } pub trait IntConvertible { pure fn to_int(&self) -> int; static pure fn from_int(n: int) -> Self; } pub trait Zero { static pure fn zero() -> Self; } pub trait One { static pure fn one() -> Self; } pub trait Round { pure fn round(&self, mode: RoundMode) -> self; pure fn floor(&self) -> self; pure fn ceil(&self) -> self; pure fn fract(&self) -> self; } pub enum RoundMode { RoundDown, RoundUp, RoundToZero, RoundFromZero } pub trait ToStrRadix { pub pure fn to_str_radix(&self, radix: uint) -> ~str; } pub trait FromStrRadix { static pub pure fn from_str_radix(str: &str, radix: uint) -> Option; } // Generic math functions: /// Dynamically calculates the value `inf` (`1/0`). /// Can fail on integer types. #[inline(always)] pub pure fn infinity() -> T { let _0: T = Zero::zero(); let _1: T = One::one(); _1 / _0 } /// Dynamically calculates the value `-inf` (`-1/0`). /// Can fail on integer types. #[inline(always)] pub pure fn neg_infinity() -> T { let _0: T = Zero::zero(); let _1: T = One::one(); - _1 / _0 } /// Dynamically calculates the value `NaN` (`0/0`). /// Can fail on integer types. #[inline(always)] pub pure fn NaN() -> T { let _0: T = Zero::zero(); _0 / _0 } /// Returns `true` if `num` has the value `inf` (`1/0`). /// Can fail on integer types. #[inline(always)] pub pure fn is_infinity(num: &T) -> bool { (*num) == (infinity::()) } /// Returns `true` if `num` has the value `-inf` (`-1/0`). /// Can fail on integer types. #[inline(always)] pub pure fn is_neg_infinity(num: &T) -> bool { (*num) == (neg_infinity::()) } /// Returns `true` if `num` has the value `NaN` (is not equal to itself). #[inline(always)] pub pure fn is_NaN(num: &T) -> bool { (*num) != (*num) } /// Returns `true` if `num` has the value `-0` (`1/num == -1/0`). /// Can fail on integer types. #[inline(always)] pub pure fn is_neg_zero(num: &T) -> bool { let _1: T = One::one(); let _0: T = Zero::zero(); *num == _0 && is_neg_infinity(&(_1 / *num)) } /** * Calculates a power to a given radix, optimized for uint `pow` and `radix`. * * Returns `radix^pow` as `T`. * * Note: * Also returns `1` for `0^0`, despite that technically being an * undefined number. The reason for this is twofold: * - If code written to use this function cares about that special case, it's * probably going to catch it before making the call. * - If code written to use this function doesn't care about it, it's * probably assuming that `x^0` always equals `1`. */ pub pure fn pow_with_uint(radix: uint, pow: uint) -> T { let _0: T = Zero::zero(); let _1: T = One::one(); if pow == 0u { return _1; } if radix == 0u { return _0; } let mut my_pow = pow; let mut total = _1; let mut multiplier = Num::from_int(radix as int); while (my_pow > 0u) { if my_pow % 2u == 1u { total *= multiplier; } my_pow /= 2u; multiplier *= multiplier; } total } pub enum ExponentFormat { ExpNone, ExpDec, ExpBin } pub enum SignificantDigits { DigAll, DigMax(uint), DigExact(uint) } pub enum SignFormat { SignNone, SignNeg, SignAll } /** * Converts a number to its string representation as a byte vector. * This is meant to be a common base implementation for all numeric string * conversion functions like `to_str()` or `to_str_radix()`. * * # Arguments * - `num` - The number to convert. Accepts any number that * implements the numeric traits. * - `radix` - Base to use. Accepts only the values 2-36. * - `special` - Whether to attempt to compare to special values like * `inf` or `NaN`. Also needed to detect negative 0. * Can fail if it doesn't match `num`s type * (see safety note). * - `negative_zero` - Whether to treat the special value `-0` as * `-0` or as `+0`. * - `sign` - How to emit the sign. Options are: * - `SignNone`: No sign at all. Basically emits `abs(num)`. * - `SignNeg`: Only `-` on negative values. * - `SignAll`: Both `+` on positive, and `-` on negative numbers. * - `digits` - The amount of digits to use for emitting the * fractional part, if any. Options are: * - `DigAll`: All calculatable digits. Beware of bignums or * fractions! * - `DigMax(uint)`: Maximum N digits, truncating any trailing zeros. * - `DigExact(uint)`: Exactly N digits. * * # Return value * A tuple containing the byte vector, and a boolean flag indicating * whether it represents a special value like `inf`, `-inf`, `NaN` or not. * It returns a tuple because there can be ambiguity between a special value * and a number representation at higher bases. * * # Failure * - Fails if `radix` < 2 or `radix` > 36. * - Fails on wrong value for `special` (see safety note). * * # Safety note * The function detects the special values `inf`, `-inf` and `NaN` by * dynamically comparing `num` to `1 / 0`, `-1 / 0` and `0 / 0` * (each of type T) if `special` is `true`. This will fail on integer types * with a 'divide by zero'. Likewise, it will fail if `num` **is** one of * those special values, and `special` is `false`, because then the * algorithm just does normal calculations on them. */ pub pure fn to_str_bytes_common( num: &T, radix: uint, special: bool, negative_zero: bool, sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) { if radix as int < 2 { fail fmt!("to_str_bytes_common: radix %? to low, \ must lie in the range [2, 36]", radix); } else if radix as int > 36 { fail fmt!("to_str_bytes_common: radix %? to high, \ must lie in the range [2, 36]", radix); } let _0: T = Zero::zero(); let _1: T = One::one(); if special { if is_NaN(num) { return (str::to_bytes("NaN"), true); } else if is_infinity(num){ return match sign { SignAll => (str::to_bytes("+inf"), true), _ => (str::to_bytes("inf"), true) } } else if is_neg_infinity(num) { return match sign { SignNone => (str::to_bytes("inf"), true), _ => (str::to_bytes("-inf"), true), } } } let neg = *num < _0 || (negative_zero && *num == _0 && special && is_neg_zero(num)); let mut buf: ~[u8] = ~[]; let radix_gen = Num::from_int::(radix as int); let mut deccum; // First emit the non-fractional part, looping at least once to make // sure at least a `0` gets emitted. deccum = num.round(RoundToZero); loop { // Calculate the absolute value of each digit instead of only // doing it once for the whole number because a // representable negative number doesn't necessary have an // representable additive inverse of the same type // (See twos complement). But we assume that for the // numbers [-35 .. 0] we always have [0 .. 35]. let current_digit_signed = deccum % radix_gen; let current_digit = if current_digit_signed < _0 { -current_digit_signed } else { current_digit_signed }; // Decrease the deccumulator one digit at a time deccum /= radix_gen; deccum = deccum.round(RoundToZero); unsafe { // FIXME: Pureness workaround (#4568) buf.push(char::from_digit(current_digit.to_int() as uint, radix) .unwrap() as u8); } // No more digits to calculate for the non-fractional part -> break if deccum == _0 { break; } } // If limited digits, calculate one digit more for rounding. let (limit_digits, digit_count, exact) = match digits { DigAll => (false, 0u, false), DigMax(count) => (true, count+1, false), DigExact(count) => (true, count+1, true) }; // Decide what sign to put in front match sign { SignNeg | SignAll if neg => { unsafe { // FIXME: Pureness workaround (#4568) buf.push('-' as u8); } } SignAll => { unsafe { // FIXME: Pureness workaround (#4568) buf.push('+' as u8); } } _ => () } unsafe { // FIXME: Pureness workaround (#4568) vec::reverse(buf); } // Remember start of the fractional digits. // Points one beyond end of buf if none get generated, // or at the '.' otherwise. let start_fractional_digits = buf.len(); // Now emit the fractional part, if any deccum = num.fract(); if deccum != _0 || (limit_digits && exact && digit_count > 0) { unsafe { // FIXME: Pureness workaround (#4568) buf.push('.' as u8); } let mut dig = 0u; // calculate new digits while // - there is no limit and there are digits left // - or there is a limit, it's not reached yet and // - it's exact // - or it's a maximum, and there are still digits left while (!limit_digits && deccum != _0) || (limit_digits && dig < digit_count && ( exact || (!exact && deccum != _0) ) ) { // Shift first fractional digit into the integer part deccum *= radix_gen; // Calculate the absolute value of each digit. // See note in first loop. let current_digit_signed = deccum.round(RoundToZero); let current_digit = if current_digit_signed < _0 { -current_digit_signed } else { current_digit_signed }; unsafe { // FIXME: Pureness workaround (#4568) buf.push(char::from_digit( current_digit.to_int() as uint, radix).unwrap() as u8); } // Decrease the deccumulator one fractional digit at a time deccum = deccum.fract(); dig += 1u; } // If digits are limited, and that limit has been reached, // cut off the one extra digit, and depending on its value // round the remaining ones. if limit_digits && dig == digit_count { let ascii2value = |chr: u8| { char::to_digit(chr as char, radix).unwrap() as uint }; let value2ascii = |val: uint| { char::from_digit(val, radix).unwrap() as u8 }; unsafe { // FIXME: Pureness workaround (#4568) let extra_digit = ascii2value(buf.pop()); if extra_digit >= radix / 2 { // -> need to round let mut i: int = buf.len() as int - 1; loop { // If reached left end of number, have to // insert additional digit: if i < 0 || buf[i] == '-' as u8 || buf[i] == '+' as u8 { buf.insert((i + 1) as uint, value2ascii(1)); break; } // Skip the '.' if buf[i] == '.' as u8 { i -= 1; loop; } // Either increment the digit, // or set to 0 if max and carry the 1. let current_digit = ascii2value(buf[i]); if current_digit < (radix - 1) { buf[i] = value2ascii(current_digit+1); break; } else { buf[i] = value2ascii(0); i -= 1; } } } } } } // if number of digits is not exact, remove all trailing '0's up to // and including the '.' if !exact { let buf_max_i = buf.len() - 1; // index to truncate from let mut i = buf_max_i; // discover trailing zeros of fractional part while i > start_fractional_digits && buf[i] == '0' as u8 { i -= 1; } // Only attempt to truncate digits if buf has fractional digits if i >= start_fractional_digits { // If buf ends with '.', cut that too. if buf[i] == '.' as u8 { i -= 1 } // only resize buf if we actually remove digits if i < buf_max_i { buf = buf.slice(0, i + 1); } } } (buf, false) } /** * Converts a number to its string representation. This is a wrapper for * `to_str_bytes_common()`, for details see there. */ #[inline(always)] pub pure fn to_str_common( num: &T, radix: uint, special: bool, negative_zero: bool, sign: SignFormat, digits: SignificantDigits) -> (~str, bool) { let (bytes, special) = to_str_bytes_common(num, radix, special, negative_zero, sign, digits); (str::from_bytes(bytes), special) } // Some constants for from_str_bytes_common's input validation, // they define minimum radix values for which the character is a valid digit. priv const DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u; priv const DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; priv const DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; /** * Parses a byte slice as a number. This is meant to * be a common base implementation for all numeric string conversion * functions like `from_str()` or `from_str_radix()`. * * # Arguments * - `buf` - The byte slice to parse. * - `radix` - Which base to parse the number as. Accepts 2-36. * - `negative` - Whether to accept negative numbers. * - `fractional` - Whether to accept numbers with fractional parts. * - `special` - Whether to accept special values like `inf` * and `NaN`. Can conflict with `radix`, see Failure. * - `exponent` - Which exponent format to accept. Options are: * - `ExpNone`: No Exponent, accepts just plain numbers like `42` or * `-8.2`. * - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or * `8.2E-2`. The exponent string itself is always base 10. * Can conflict with `radix`, see Failure. * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or * `FFp128`. The exponent string itself is always base 10. * Can conflict with `radix`, see Failure. * - `empty_zero` - Whether to accept a empty `buf` as a 0 or not. * * # Return value * Returns `Some(n)` if `buf` parses to a number n without overflowing, and * `None` otherwise, depending on the constraints set by the remaining * arguments. * * # Failure * - Fails if `radix` < 2 or `radix` > 36. * - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict * between digit and exponent sign `'e'`. * - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict * between digit and exponent sign `'p'`. * - Fails if `radix` > 18 and `special == true` due to conflict * between digit and lowest first character in `inf` and `NaN`, the `'i'`. * * # Possible improvements * - Could accept option to allow ignoring underscores, allowing for numbers * formated like `FF_AE_FF_FF`. */ pub pure fn from_str_bytes_common( buf: &[u8], radix: uint, negative: bool, fractional: bool, special: bool, exponent: ExponentFormat, empty_zero: bool ) -> Option { match exponent { ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' => fail fmt!("from_str_bytes_common: radix %? incompatible with \ use of 'e' as decimal exponent", radix), ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' => fail fmt!("from_str_bytes_common: radix %? incompatible with \ use of 'p' as binary exponent", radix), _ if special && radix >= DIGIT_I_RADIX // first digit of 'inf' => fail fmt!("from_str_bytes_common: radix %? incompatible with \ special values 'inf' and 'NaN'", radix), _ if radix as int < 2 => fail fmt!("from_str_bytes_common: radix %? to low, \ must lie in the range [2, 36]", radix), _ if radix as int > 36 => fail fmt!("from_str_bytes_common: radix %? to high, \ must lie in the range [2, 36]", radix), _ => () } let _0: T = Zero::zero(); let _1: T = One::one(); let radix_gen: T = Num::from_int(radix as int); let len = buf.len(); if len == 0 { if empty_zero { return Some(_0); } else { return None; } } if special { if buf == str::to_bytes("inf") || buf == str::to_bytes("+inf") { return Some(infinity()); } else if buf == str::to_bytes("-inf") { if negative { return Some(neg_infinity()); } else { return None; } } else if buf == str::to_bytes("NaN") { return Some(NaN()); } } let (start, accum_positive) = match buf[0] { '-' as u8 if !negative => return None, '-' as u8 => (1u, false), '+' as u8 => (1u, true), _ => (0u, true) }; // Initialize accumulator with signed zero for floating point parsing to // work let mut accum = if accum_positive { _0 } else { -_1 * _0}; let mut last_accum = accum; // Necessary to detect overflow let mut i = start; let mut exp_found = false; // Parse integer part of number while i < len { let c = buf[i] as char; match char::to_digit(c, radix) { Some(digit) => { // shift accum one digit left accum *= radix_gen; // add/subtract current digit depending on sign if accum_positive { accum += Num::from_int(digit as int); } else { accum -= Num::from_int(digit as int); } // Detect overflow by comparing to last value if accum_positive && accum < last_accum { return None; } if !accum_positive && accum > last_accum { return None; } last_accum = accum; } None => match c { 'e' | 'E' | 'p' | 'P' => { exp_found = true; break; // start of exponent } '.' if fractional => { i += 1u; // skip the '.' break; // start of fractional part } _ => return None // invalid number } } i += 1u; } // Parse fractional part of number // Skip if already reached start of exponent if !exp_found { let mut power = _1; while i < len { let c = buf[i] as char; match char::to_digit(c, radix) { Some(digit) => { // Decrease power one order of magnitude power /= radix_gen; // add/subtract current digit depending on sign if accum_positive { accum += Num::from_int::(digit as int) * power; } else { accum -= Num::from_int::(digit as int) * power; } // Detect overflow by comparing to last value if accum_positive && accum < last_accum { return None; } if !accum_positive && accum > last_accum { return None; } last_accum = accum; } None => match c { 'e' | 'E' | 'p' | 'P' => { exp_found = true; break; // start of exponent } _ => return None // invalid number } } i += 1u; } } // Special case: buf not empty, but does not contain any digit in front // of the exponent sign -> number is empty string if i == start { if empty_zero { return Some(_0); } else { return None; } } let mut multiplier = _1; if exp_found { let c = buf[i] as char; let base = match (c, exponent) { ('e', ExpDec) | ('E', ExpDec) => 10u, ('p', ExpBin) | ('P', ExpBin) => 2u, _ => return None // char doesn't fit given exponent format }; // parse remaining bytes as decimal integer, // skipping the exponent char let exp: Option = from_str_bytes_common( buf.view(i+1, len), 10, true, false, false, ExpNone, false); match exp { Some(exp_pow) => { multiplier = if exp_pow < 0 { _1 / pow_with_uint::(base, (-exp_pow.to_int()) as uint) } else { pow_with_uint::(base, exp_pow.to_int() as uint) } } None => return None // invalid exponent -> invalid number } } Some(accum * multiplier) } /** * Parses a string as a number. This is a wrapper for * `from_str_bytes_common()`, for details see there. */ #[inline(always)] pub pure fn from_str_common( buf: &str, radix: uint, negative: bool, fractional: bool, special: bool, exponent: ExponentFormat, empty_zero: bool ) -> Option { from_str_bytes_common(str::to_bytes(buf), radix, negative, fractional, special, exponent, empty_zero) }