about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-04-05 22:00:50 -0700
committerbors <bors@rust-lang.org>2013-04-05 22:00:50 -0700
commitbdd2439529c23b4fcd2676d7427c45ca223385ce (patch)
treeb722a99b095de611361906d112ffa5b8d9c71fc2 /src/libstd
parentf678d63507a779d81810c342dd1bdc828fb983ba (diff)
parentc6949b3669d23a1694b964108f21d5200c985cb5 (diff)
downloadrust-bdd2439529c23b4fcd2676d7427c45ca223385ce.tar.gz
rust-bdd2439529c23b4fcd2676d7427c45ca223385ce.zip
auto merge of #5733 : dbaupp/rust/std-complex-rational, r=thestinger
This adds two generic data types, `Ratio` and `Cmplx` (and some aliases for useful instances, e.g. `Ratio<int>` and `Cmplx<f64>`), and basic arithmetic support, as well as `.to_str` (for both) and `.from_str` (for rational).

The complex number implementation doesn't solve #1284 other than getting something into the libraries, specifically it doesn't even try to address C interop. If the complex part of this gets merged, maybe it's worth closing that issue and reopening more specific issue(s) about the failings.

The implementations can be fleshed out when the numeric traits stabilise (and trait inheritance works).
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/num/bigint.rs (renamed from src/libstd/bigint.rs)0
-rw-r--r--src/libstd/num/complex.rs315
-rw-r--r--src/libstd/num/rational.rs511
-rw-r--r--src/libstd/std.rc5
4 files changed, 831 insertions, 0 deletions
diff --git a/src/libstd/bigint.rs b/src/libstd/num/bigint.rs
index 35b1a28a465..35b1a28a465 100644
--- a/src/libstd/bigint.rs
+++ b/src/libstd/num/bigint.rs
diff --git a/src/libstd/num/complex.rs b/src/libstd/num/complex.rs
new file mode 100644
index 00000000000..1e8fc0e6c2b
--- /dev/null
+++ b/src/libstd/num/complex.rs
@@ -0,0 +1,315 @@
+// Copyright 2013 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+//! Complex numbers.
+
+use core::num::{Zero,One,ToStrRadix};
+use core::prelude::*;
+
+// FIXME #1284: handle complex NaN & infinity etc. This
+// probably doesn't map to C's _Complex correctly.
+
+// FIXME #5734:: Need generic sin/cos for .to/from_polar().
+// FIXME #5735: Need generic sqrt to implement .norm().
+
+
+/// A complex number in Cartesian form.
+#[deriving(Eq,Clone)]
+pub struct Cmplx<T> {
+    re: T,
+    im: T
+}
+
+pub type Complex = Cmplx<float>;
+pub type Complex32 = Cmplx<f32>;
+pub type Complex64 = Cmplx<f64>;
+
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
+    Cmplx<T> {
+    /// Create a new Cmplx
+    #[inline]
+    pub fn new(re: T, im: T) -> Cmplx<T> {
+        Cmplx { re: re, im: im }
+    }
+
+    /**
+    Returns the square of the norm (since `T` doesn't necessarily
+    have a sqrt function), i.e. `re^2 + im^2`.
+    */
+    #[inline]
+    pub fn norm_sqr(&self) -> T {
+        self.re * self.re + self.im * self.im
+    }
+
+
+    /// Returns the complex conjugate. i.e. `re - i im`
+    #[inline]
+    pub fn conj(&self) -> Cmplx<T> {
+        Cmplx::new(self.re, -self.im)
+    }
+
+
+    /// Multiplies `self` by the scalar `t`.
+    #[inline]
+    pub fn scale(&self, t: T) -> Cmplx<T> {
+        Cmplx::new(self.re * t, self.im * t)
+    }
+
+    /// Divides `self` by the scalar `t`.
+    #[inline]
+    pub fn unscale(&self, t: T) -> Cmplx<T> {
+        Cmplx::new(self.re / t, self.im / t)
+    }
+
+    /// Returns `1/self`
+    #[inline]
+    pub fn inv(&self) -> Cmplx<T> {
+        let norm_sqr = self.norm_sqr();
+        Cmplx::new(self.re / norm_sqr,
+                    -self.im / norm_sqr)
+    }
+}
+
+/* arithmetic */
+// (a + i b) + (c + i d) == (a + c) + i (b + d)
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
+    Add<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
+    #[inline]
+    fn add(&self, other: &Cmplx<T>) -> Cmplx<T> {
+        Cmplx::new(self.re + other.re, self.im + other.im)
+    }
+}
+// (a + i b) - (c + i d) == (a - c) + i (b - d)
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
+    Sub<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
+    #[inline]
+    fn sub(&self, other: &Cmplx<T>) -> Cmplx<T> {
+        Cmplx::new(self.re - other.re, self.im - other.im)
+    }
+}
+// (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c)
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
+    Mul<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
+    #[inline]
+    fn mul(&self, other: &Cmplx<T>) -> Cmplx<T> {
+        Cmplx::new(self.re*other.re - self.im*other.im,
+                     self.re*other.im + self.im*other.re)
+    }
+}
+
+// (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d)
+//   == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)]
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
+    Div<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
+    #[inline]
+    fn div(&self, other: &Cmplx<T>) -> Cmplx<T> {
+        let norm_sqr = other.norm_sqr();
+        Cmplx::new((self.re*other.re + self.im*other.im) / norm_sqr,
+                     (self.im*other.re - self.re*other.im) / norm_sqr)
+    }
+}
+
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
+    Neg<Cmplx<T>> for Cmplx<T> {
+    #[inline]
+    fn neg(&self) -> Cmplx<T> {
+        Cmplx::new(-self.re, -self.im)
+    }
+}
+
+/* constants */
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T> + Zero>
+    Zero for Cmplx<T> {
+    #[inline]
+    fn zero() -> Cmplx<T> {
+        Cmplx::new(Zero::zero(), Zero::zero())
+    }
+}
+
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T> + Zero + One>
+    One for Cmplx<T> {
+    #[inline]
+    fn one() -> Cmplx<T> {
+        Cmplx::new(One::one(), Zero::zero())
+    }
+}
+
+/* string conversions */
+impl<T: ToStr + Zero + Ord + Neg<T>> ToStr for Cmplx<T> {
+    fn to_str(&self) -> ~str {
+        if self.im < Zero::zero() {
+            fmt!("%s-%si", self.re.to_str(), (-self.im).to_str())
+        } else {
+            fmt!("%s+%si", self.re.to_str(), self.im.to_str())
+        }
+    }
+}
+
+impl<T: ToStrRadix + Zero + Ord + Neg<T>> ToStrRadix for Cmplx<T> {
+    fn to_str_radix(&self, radix: uint) -> ~str {
+        if self.im < Zero::zero() {
+            fmt!("%s-%si", self.re.to_str_radix(radix), (-self.im).to_str_radix(radix))
+        } else {
+            fmt!("%s+%si", self.re.to_str_radix(radix), self.im.to_str_radix(radix))
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use core::prelude::*;
+    use super::*;
+    use core::num::{Zero,One};
+
+    pub static _0_0i : Complex = Cmplx { re: 0f, im: 0f };
+    pub static _1_0i : Complex = Cmplx { re: 1f, im: 0f };
+    pub static _1_1i : Complex = Cmplx { re: 1f, im: 1f };
+    pub static _0_1i : Complex = Cmplx { re: 0f, im: 1f };
+    pub static _neg1_1i : Complex = Cmplx { re: -1f, im: 1f };
+    pub static _05_05i : Complex = Cmplx { re: 0.5f, im: 0.5f };
+    pub static all_consts : [Complex, .. 5] = [_0_0i, _1_0i, _1_1i, _neg1_1i, _05_05i];
+
+    #[test]
+    fn test_consts() {
+        // check our constants are what Cmplx::new creates
+        fn test(c : Complex, r : float, i: float) {
+            assert_eq!(c, Cmplx::new(r,i));
+        }
+        test(_0_0i, 0f, 0f);
+        test(_1_0i, 1f, 0f);
+        test(_1_1i, 1f, 1f);
+        test(_neg1_1i, -1f, 1f);
+        test(_05_05i, 0.5f, 0.5f);
+
+        assert_eq!(_0_0i, Zero::zero());
+        assert_eq!(_1_0i, One::one());
+    }
+
+    #[test]
+    fn test_norm_sqr() {
+        fn test(c: Complex, ns: float) {
+            assert_eq!(c.norm_sqr(), ns);
+        }
+        test(_0_0i, 0f);
+        test(_1_0i, 1f);
+        test(_1_1i, 2f);
+        test(_neg1_1i, 2f);
+        test(_05_05i, 0.5f);
+    }
+
+    #[test]
+    fn test_scale_unscale() {
+        assert_eq!(_05_05i.scale(2f), _1_1i);
+        assert_eq!(_1_1i.unscale(2f), _05_05i);
+        for all_consts.each |&c| {
+            assert_eq!(c.scale(2f).unscale(2f), c);
+        }
+    }
+
+    #[test]
+    fn test_conj() {
+        for all_consts.each |&c| {
+            assert_eq!(c.conj(), Cmplx::new(c.re, -c.im));
+            assert_eq!(c.conj().conj(), c);
+        }
+    }
+
+    #[test]
+    fn test_inv() {
+        assert_eq!(_1_1i.inv(), _05_05i.conj());
+        assert_eq!(_1_0i.inv(), _1_0i.inv());
+    }
+
+    #[test]
+    #[should_fail]
+    #[ignore]
+    fn test_inv_zero() {
+        // FIXME #5736: should this really fail, or just NaN?
+        _0_0i.inv();
+    }
+
+
+    mod arith {
+        use super::*;
+        use super::super::*;
+        use core::num::Zero;
+
+        #[test]
+        fn test_add() {
+            assert_eq!(_05_05i + _05_05i, _1_1i);
+            assert_eq!(_0_1i + _1_0i, _1_1i);
+            assert_eq!(_1_0i + _neg1_1i, _0_1i);
+
+            for all_consts.each |&c| {
+                assert_eq!(_0_0i + c, c);
+                assert_eq!(c + _0_0i, c);
+            }
+        }
+
+        #[test]
+        fn test_sub() {
+            assert_eq!(_05_05i - _05_05i, _0_0i);
+            assert_eq!(_0_1i - _1_0i, _neg1_1i);
+            assert_eq!(_0_1i - _neg1_1i, _1_0i);
+
+            for all_consts.each |&c| {
+                assert_eq!(c - _0_0i, c);
+                assert_eq!(c - c, _0_0i);
+            }
+        }
+
+        #[test]
+        fn test_mul() {
+            assert_eq!(_05_05i * _05_05i, _0_1i.unscale(2f));
+            assert_eq!(_1_1i * _0_1i, _neg1_1i);
+
+            // i^2 & i^4
+            assert_eq!(_0_1i * _0_1i, -_1_0i);
+            assert_eq!(_0_1i * _0_1i * _0_1i * _0_1i, _1_0i);
+
+            for all_consts.each |&c| {
+                assert_eq!(c * _1_0i, c);
+                assert_eq!(_1_0i * c, c);
+            }
+        }
+        #[test]
+        fn test_div() {
+            assert_eq!(_neg1_1i / _0_1i, _1_1i);
+            for all_consts.each |&c| {
+                if c != Zero::zero() {
+                    assert_eq!(c / c, _1_0i);
+                }
+            }
+        }
+        #[test]
+        fn test_neg() {
+            assert_eq!(-_1_0i + _0_1i, _neg1_1i);
+            assert_eq!((-_0_1i) * _0_1i, _1_0i);
+            for all_consts.each |&c| {
+                assert_eq!(-(-c), c);
+            }
+        }
+    }
+
+    #[test]
+    fn test_to_str() {
+        fn test(c : Complex, s: ~str) {
+            assert_eq!(c.to_str(), s);
+        }
+        test(_0_0i, ~"0+0i");
+        test(_1_0i, ~"1+0i");
+        test(_0_1i, ~"0+1i");
+        test(_1_1i, ~"1+1i");
+        test(_neg1_1i, ~"-1+1i");
+        test(-_neg1_1i, ~"1-1i");
+        test(_05_05i, ~"0.5+0.5i");
+    }
+}
diff --git a/src/libstd/num/rational.rs b/src/libstd/num/rational.rs
new file mode 100644
index 00000000000..f15b382dcd3
--- /dev/null
+++ b/src/libstd/num/rational.rs
@@ -0,0 +1,511 @@
+// Copyright 2013 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+//! Rational numbers
+
+use core::num::{Zero,One,ToStrRadix,FromStrRadix,Round};
+use core::from_str::FromStr;
+use core::to_str::ToStr;
+use core::prelude::*;
+use core::cmp::TotalEq;
+use super::bigint::BigInt;
+
+/// Represents the ratio between 2 numbers.
+#[deriving(Clone)]
+pub struct Ratio<T> {
+    numer: T,
+    denom: T
+}
+
+/// Alias for a `Ratio` of machine-sized integers.
+pub type Rational = Ratio<int>;
+pub type Rational32 = Ratio<i32>;
+pub type Rational64 = Ratio<i64>;
+
+/// Alias for arbitrary precision rationals.
+pub type BigRational = Ratio<BigInt>;
+
+impl<T: Copy + Div<T,T> + Modulo<T,T> + Neg<T> + Zero + One + Ord + Eq>
+    Ratio<T> {
+    /// Create a ratio representing the integer `t`.
+    #[inline(always)]
+    pub fn from_integer(t: T) -> Ratio<T> {
+        Ratio::new_raw(t, One::one())
+    }
+
+    /// Create a ratio without checking for `denom == 0` or reducing.
+    #[inline(always)]
+    pub fn new_raw(numer: T, denom: T) -> Ratio<T> {
+        Ratio { numer: numer, denom: denom }
+    }
+
+    // Create a new Ratio. Fails if `denom == 0`.
+    #[inline(always)]
+    pub fn new(numer: T, denom: T) -> Ratio<T> {
+        if denom == Zero::zero() {
+            fail!(~"divide by 0");
+        }
+        let mut ret = Ratio::new_raw(numer, denom);
+        ret.reduce();
+        ret
+    }
+
+    /// Put self into lowest terms, with denom > 0.
+    fn reduce(&mut self) {
+        let mut g : T = gcd(self.numer, self.denom);
+
+        self.numer /= g;
+        self.denom /= g;
+
+        // keep denom positive!
+        if self.denom < Zero::zero() {
+            self.numer = -self.numer;
+            self.denom = -self.denom;
+        }
+    }
+    /// Return a `reduce`d copy of self.
+    fn reduced(&self) -> Ratio<T> {
+        let mut ret = copy *self;
+        ret.reduce();
+        ret
+    }
+}
+
+/**
+Compute the greatest common divisor of two numbers, via Euclid's algorithm.
+
+The result can be negative.
+*/
+#[inline]
+pub fn gcd_raw<T: Modulo<T,T> + Zero + Eq>(n: T, m: T) -> T {
+    let mut m = m, n = n;
+    while m != Zero::zero() {
+        let temp = m;
+        m = n % temp;
+        n = temp;
+    }
+    n
+}
+
+/**
+Compute the greatest common divisor of two numbers, via Euclid's algorithm.
+
+The result is always positive.
+*/
+#[inline]
+pub fn gcd<T: Modulo<T,T> + Neg<T> + Zero + Ord + Eq>(n: T, m: T) -> T {
+    let g = gcd_raw(n, m);
+    if g < Zero::zero() { -g }
+    else { g }
+}
+
+/* Comparisons */
+
+// comparing a/b and c/d is the same as comparing a*d and b*c, so we
+// abstract that pattern. The following macro takes a trait and either
+// a comma-separated list of "method name -> return value" or just
+// "method name" (return value is bool in that case)
+macro_rules! cmp_impl {
+    (impl $imp:ident, $($method:ident),+) => {
+        cmp_impl!(impl $imp, $($method -> bool),+)
+    };
+    // return something other than a Ratio<T>
+    (impl $imp:ident, $($method:ident -> $res:ty),+) => {
+        impl<T: Mul<T,T> + $imp> $imp for Ratio<T> {
+            $(
+                #[inline]
+                fn $method(&self, other: &Ratio<T>) -> $res {
+                    (self.numer * other.denom). $method (&(self.denom*other.numer))
+                }
+            )+
+        }
+    };
+}
+cmp_impl!(impl Eq, eq, ne)
+cmp_impl!(impl TotalEq, equals)
+cmp_impl!(impl Ord, lt, gt, le, ge)
+cmp_impl!(impl TotalOrd, cmp -> cmp::Ordering)
+
+/* Arithmetic */
+// a/b * c/d = (a*c)/(b*d)
+impl<T: Copy + Mul<T,T> + Div<T,T> + Modulo<T,T> + Neg<T> + Zero + One + Ord + Eq>
+    Mul<Ratio<T>,Ratio<T>> for Ratio<T> {
+    #[inline]
+    fn mul(&self, rhs: &Ratio<T>) -> Ratio<T> {
+        Ratio::new(self.numer * rhs.numer, self.denom * rhs.denom)
+    }
+}
+
+// (a/b) / (c/d) = (a*d)/(b*c)
+impl<T: Copy + Mul<T,T> + Div<T,T> + Modulo<T,T> + Neg<T> + Zero + One + Ord + Eq>
+    Div<Ratio<T>,Ratio<T>> for Ratio<T> {
+    #[inline]
+    fn div(&self, rhs: &Ratio<T>) -> Ratio<T> {
+        Ratio::new(self.numer * rhs.denom, self.denom * rhs.numer)
+    }
+}
+
+// Abstracts the a/b `op` c/d = (a*d `op` b*d) / (b*d) pattern
+macro_rules! arith_impl {
+    (impl $imp:ident, $method:ident) => {
+        impl<T: Copy +
+                Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Modulo<T,T> + Neg<T> +
+                Zero + One + Ord + Eq>
+            $imp<Ratio<T>,Ratio<T>> for Ratio<T> {
+            #[inline]
+            fn $method(&self, rhs: &Ratio<T>) -> Ratio<T> {
+                Ratio::new((self.numer * rhs.denom).$method(&(self.denom * rhs.numer)),
+                           self.denom * rhs.denom)
+            }
+        }
+    }
+}
+
+// a/b + c/d = (a*d + b*c)/(b*d
+arith_impl!(impl Add, add)
+
+// a/b - c/d = (a*d - b*c)/(b*d)
+arith_impl!(impl Sub, sub)
+
+// a/b % c/d = (a*d % b*c)/(b*d)
+arith_impl!(impl Modulo, modulo)
+
+impl<T: Copy + Div<T,T> + Modulo<T,T> + Neg<T> + Zero + One + Ord + Eq>
+    Neg<Ratio<T>> for Ratio<T> {
+    #[inline]
+    fn neg(&self) -> Ratio<T> {
+        Ratio::new_raw(-self.numer, self.denom)
+    }
+}
+
+/* Constants */
+impl<T: Copy + Div<T,T> + Modulo<T,T> + Neg<T> + Zero + One + Ord + Eq>
+    Zero for Ratio<T> {
+    #[inline]
+    fn zero() -> Ratio<T> {
+        Ratio::new_raw(Zero::zero(), One::one())
+    }
+}
+
+impl<T: Copy + Div<T,T> + Modulo<T,T> + Neg<T> + Zero + One + Ord + Eq>
+    One for Ratio<T> {
+    #[inline]
+    fn one() -> Ratio<T> {
+        Ratio::new_raw(One::one(), One::one())
+    }
+}
+
+/* Utils */
+impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Modulo<T,T> + Neg<T> +
+    Zero + One + Ord + Eq>
+    Round for Ratio<T> {
+    fn round(&self, mode: num::RoundMode) -> Ratio<T> {
+        match mode {
+            num::RoundUp => { self.ceil() }
+            num::RoundDown => { self.floor()}
+            num::RoundToZero => { Ratio::from_integer(self.numer / self.denom) }
+            num::RoundFromZero => {
+                if *self < Zero::zero() {
+                    Ratio::from_integer((self.numer - self.denom + One::one()) / self.denom)
+                } else {
+                    Ratio::from_integer((self.numer + self.denom - One::one()) / self.denom)
+                }
+            }
+        }
+    }
+
+    fn floor(&self) -> Ratio<T> {
+        if *self < Zero::zero() {
+            Ratio::from_integer((self.numer - self.denom + One::one()) / self.denom)
+        } else {
+            Ratio::from_integer(self.numer / self.denom)
+        }
+    }
+    fn ceil(&self) -> Ratio<T> {
+        if *self < Zero::zero() {
+            Ratio::from_integer(self.numer / self.denom)
+        } else {
+            Ratio::from_integer((self.numer + self.denom - One::one()) / self.denom)
+        }
+    }
+    fn fract(&self) -> Ratio<T> {
+        Ratio::new_raw(self.numer % self.denom, self.denom)
+    }
+}
+
+
+/* String conversions */
+impl<T: ToStr> ToStr for Ratio<T> {
+    /// Renders as `numer/denom`.
+    fn to_str(&self) -> ~str {
+        fmt!("%s/%s", self.numer.to_str(), self.denom.to_str())
+    }
+}
+impl<T: ToStrRadix> ToStrRadix for Ratio<T> {
+    /// Renders as `numer/denom` where the numbers are in base `radix`.
+    fn to_str_radix(&self, radix: uint) -> ~str {
+        fmt!("%s/%s", self.numer.to_str_radix(radix), self.denom.to_str_radix(radix))
+    }
+}
+
+impl<T: FromStr + Copy + Div<T,T> + Modulo<T,T> + Neg<T> + Zero + One + Ord + Eq>
+    FromStr for Ratio<T> {
+    /// Parses `numer/denom`.
+    fn from_str(s: &str) -> Option<Ratio<T>> {
+        let split = vec::build(|push| {
+            for str::each_splitn_char(s, '/', 1) |s| {
+                push(s.to_owned());
+            }
+        });
+        if split.len() < 2 { return None; }
+        do FromStr::from_str(split[0]).chain |a| {
+            do FromStr::from_str(split[1]).chain |b| {
+                Some(Ratio::new(a,b))
+            }
+        }
+    }
+}
+impl<T: FromStrRadix + Copy + Div<T,T> + Modulo<T,T> + Neg<T> + Zero + One + Ord + Eq>
+    FromStrRadix for Ratio<T> {
+    /// Parses `numer/denom` where the numbers are in base `radix`.
+    fn from_str_radix(s: &str, radix: uint) -> Option<Ratio<T>> {
+        let split = vec::build(|push| {
+            for str::each_splitn_char(s, '/', 1) |s| {
+                push(s.to_owned());
+            }
+        });
+        if split.len() < 2 { None }
+        else {
+            do FromStrRadix::from_str_radix(split[0], radix).chain |a| {
+                do FromStrRadix::from_str_radix(split[1], radix).chain |b| {
+                    Some(Ratio::new(a,b))
+                }
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use core::prelude::*;
+    use super::*;
+    use core::num::{Zero,One,FromStrRadix};
+    use core::from_str::FromStr;
+
+    pub static _0 : Rational = Ratio { numer: 0, denom: 1};
+    pub static _1 : Rational = Ratio { numer: 1, denom: 1};
+    pub static _2: Rational = Ratio { numer: 2, denom: 1};
+    pub static _1_2: Rational = Ratio { numer: 1, denom: 2};
+    pub static _3_2: Rational = Ratio { numer: 3, denom: 2};
+    pub static _neg1_2: Rational =  Ratio { numer: -1, denom: 2};
+
+    #[test]
+    fn test_gcd() {
+        assert_eq!(gcd(10,2),2);
+        assert_eq!(gcd(10,3),1);
+        assert_eq!(gcd(0,3),3);
+        assert_eq!(gcd(3,3),3);
+
+        assert_eq!(gcd(3,-3), 3);
+        assert_eq!(gcd(-6,3), 3);
+        assert_eq!(gcd(-4,-2), 2);
+    }
+
+    #[test]
+    fn test_test_constants() {
+        // check our constants are what Ratio::new etc. would make.
+        assert_eq!(_0, Zero::zero());
+        assert_eq!(_1, One::one());
+        assert_eq!(_2, Ratio::from_integer(2));
+        assert_eq!(_1_2, Ratio::new(1,2));
+        assert_eq!(_3_2, Ratio::new(3,2));
+        assert_eq!(_neg1_2, Ratio::new(-1,2));
+    }
+
+    #[test]
+    fn test_new_reduce() {
+        let one22 = Ratio::new(2i,2);
+
+        assert_eq!(one22, One::one());
+    }
+    #[test]
+    #[should_fail]
+    fn test_new_zero() {
+        let _a = Ratio::new(1,0);
+    }
+
+
+    #[test]
+    fn test_cmp() {
+        assert!(_0 == _0 && _1 == _1);
+        assert!(_0 != _1 && _1 != _0);
+        assert!(_0 < _1 && !(_1 < _0));
+        assert!(_1 > _0 && !(_0 > _1));
+
+        assert!(_0 <= _0 && _1 <= _1);
+        assert!(_0 <= _1 && !(_1 <= _0));
+
+        assert!(_0 >= _0 && _1 >= _1);
+        assert!(_1 >= _0 && !(_0 >= _1));
+    }
+
+
+    mod arith {
+        use super::*;
+        use super::super::*;
+
+
+        #[test]
+        fn test_add() {
+            assert_eq!(_1 + _1_2, _3_2);
+            assert_eq!(_1 + _1, _2);
+            assert_eq!(_1_2 + _3_2, _2);
+            assert_eq!(_1_2 + _neg1_2, _0);
+        }
+
+        #[test]
+        fn test_sub() {
+            assert_eq!(_1 - _1_2, _1_2);
+            assert_eq!(_3_2 - _1_2, _1);
+            assert_eq!(_1 - _neg1_2, _3_2);
+        }
+
+        #[test]
+        fn test_mul() {
+            assert_eq!(_1 * _1_2, _1_2);
+            assert_eq!(_1_2 * _3_2, Ratio::new(3,4));
+            assert_eq!(_1_2 * _neg1_2, Ratio::new(-1, 4));
+        }
+
+        #[test]
+        fn test_div() {
+            assert_eq!(_1 / _1_2, _2);
+            assert_eq!(_3_2 / _1_2, _1 + _2);
+            assert_eq!(_1 / _neg1_2, _neg1_2 + _neg1_2 + _neg1_2 + _neg1_2);
+        }
+
+        #[test]
+        fn test_modulo() {
+            assert_eq!(_3_2 % _1, _1_2);
+            assert_eq!(_2 % _neg1_2, _0);
+            assert_eq!(_1_2 % _2,  _1_2);
+        }
+
+        #[test]
+        fn test_neg() {
+            assert_eq!(-_0, _0);
+            assert_eq!(-_1_2, _neg1_2);
+            assert_eq!(-(-_1), _1);
+        }
+        #[test]
+        fn test_zero() {
+            assert_eq!(_0 + _0, _0);
+            assert_eq!(_0 * _0, _0);
+            assert_eq!(_0 * _1, _0);
+            assert_eq!(_0 / _neg1_2, _0);
+            assert_eq!(_0 - _0, _0);
+        }
+        #[test]
+        #[should_fail]
+        fn test_div_0() {
+            let _a =  _1 / _0;
+        }
+    }
+
+    #[test]
+    fn test_round() {
+        assert_eq!(_1_2.ceil(), _1);
+        assert_eq!(_1_2.floor(), _0);
+        assert_eq!(_1_2.round(num::RoundToZero), _0);
+        assert_eq!(_1_2.round(num::RoundFromZero), _1);
+
+        assert_eq!(_neg1_2.ceil(), _0);
+        assert_eq!(_neg1_2.floor(), -_1);
+        assert_eq!(_neg1_2.round(num::RoundToZero), _0);
+        assert_eq!(_neg1_2.round(num::RoundFromZero), -_1);
+
+        assert_eq!(_1.ceil(), _1);
+        assert_eq!(_1.floor(), _1);
+        assert_eq!(_1.round(num::RoundToZero), _1);
+        assert_eq!(_1.round(num::RoundFromZero), _1);
+    }
+
+    #[test]
+    fn test_fract() {
+        assert_eq!(_1.fract(), _0);
+        assert_eq!(_neg1_2.fract(), _neg1_2);
+        assert_eq!(_1_2.fract(), _1_2);
+        assert_eq!(_3_2.fract(), _1_2);
+    }
+
+    #[test]
+    fn test_to_from_str() {
+        fn test(r: Rational, s: ~str) {
+            assert_eq!(FromStr::from_str(s), Some(r));
+            assert_eq!(r.to_str(), s);
+        }
+        test(_1, ~"1/1");
+        test(_0, ~"0/1");
+        test(_1_2, ~"1/2");
+        test(_3_2, ~"3/2");
+        test(_2, ~"2/1");
+        test(_neg1_2, ~"-1/2");
+    }
+    #[test]
+    fn test_from_str_fail() {
+        fn test(s: &str) {
+            assert_eq!(FromStr::from_str::<Rational>(s), None);
+        }
+
+        for ["0 /1", "abc", "", "1/", "--1/2","3/2/1"].each |&s| {
+            test(s);
+        }
+    }
+
+    #[test]
+    fn test_to_from_str_radix() {
+        fn test(r: Rational, s: ~str, n: uint) {
+            assert_eq!(FromStrRadix::from_str_radix(s, n), Some(r));
+            assert_eq!(r.to_str_radix(n), s);
+        }
+        fn test3(r: Rational, s: ~str) { test(r, s, 3) }
+        fn test16(r: Rational, s: ~str) { test(r, s, 16) }
+
+        test3(_1, ~"1/1");
+        test3(_0, ~"0/1");
+        test3(_1_2, ~"1/2");
+        test3(_3_2, ~"10/2");
+        test3(_2, ~"2/1");
+        test3(_neg1_2, ~"-1/2");
+        test3(_neg1_2 / _2, ~"-1/11");
+
+        test16(_1, ~"1/1");
+        test16(_0, ~"0/1");
+        test16(_1_2, ~"1/2");
+        test16(_3_2, ~"3/2");
+        test16(_2, ~"2/1");
+        test16(_neg1_2, ~"-1/2");
+        test16(_neg1_2 / _2, ~"-1/4");
+        test16(Ratio::new(13,15), ~"d/f");
+        test16(_1_2*_1_2*_1_2*_1_2, ~"1/10");
+    }
+
+    #[test]
+    fn test_from_str_radix_fail() {
+        fn test(s: &str) {
+            assert_eq!(FromStrRadix::from_str_radix::<Rational>(s, 3), None);
+        }
+
+        for ["0 /1", "abc", "", "1/", "--1/2","3/2/1", "3/2"].each |&s| {
+            test(s);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/libstd/std.rc b/src/libstd/std.rc
index 74ef229a033..9aac8d23055 100644
--- a/src/libstd/std.rc
+++ b/src/libstd/std.rc
@@ -95,7 +95,12 @@ pub mod cmp;
 pub mod base64;
 pub mod rl;
 pub mod workcache;
+#[path="num/bigint.rs"]
 pub mod bigint;
+#[path="num/rational.rs"]
+pub mod rational;
+#[path="num/complex.rs"]
+pub mod complex;
 pub mod stats;
 pub mod semver;
 pub mod fileinput;