From ba63cba18d5f0a042fdcb4ab0d39b1bf822d4400 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 5 Apr 2013 17:36:24 +1100 Subject: [PATCH 1/4] libstd: move bigint to dedicated num directory --- src/libstd/{ => num}/bigint.rs | 0 src/libstd/std.rc | 1 + 2 files changed, 1 insertion(+) rename src/libstd/{ => num}/bigint.rs (100%) diff --git a/src/libstd/bigint.rs b/src/libstd/num/bigint.rs similarity index 100% rename from src/libstd/bigint.rs rename to src/libstd/num/bigint.rs diff --git a/src/libstd/std.rc b/src/libstd/std.rc index 74ef229a03353..30346d1b16b5a 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -95,6 +95,7 @@ pub mod cmp; pub mod base64; pub mod rl; pub mod workcache; +#[path="num/bigint.rs"] pub mod bigint; pub mod stats; pub mod semver; From 7b0401d774bac77bee279dff8641aba2b05cf9b8 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 5 Apr 2013 17:54:11 +1100 Subject: [PATCH 2/4] libstd: add basic rational numbers --- src/libstd/num/rational.rs | 511 +++++++++++++++++++++++++++++++++++++ src/libstd/std.rc | 2 + 2 files changed, 513 insertions(+) create mode 100644 src/libstd/num/rational.rs diff --git a/src/libstd/num/rational.rs b/src/libstd/num/rational.rs new file mode 100644 index 0000000000000..f15b382dcd351 --- /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 or the MIT license +// , 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 { + numer: T, + denom: T +} + +/// Alias for a `Ratio` of machine-sized integers. +pub type Rational = Ratio; +pub type Rational32 = Ratio; +pub type Rational64 = Ratio; + +/// Alias for arbitrary precision rationals. +pub type BigRational = Ratio; + +impl + Modulo + Neg + Zero + One + Ord + Eq> + Ratio { + /// Create a ratio representing the integer `t`. + #[inline(always)] + pub fn from_integer(t: T) -> Ratio { + 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 { + Ratio { numer: numer, denom: denom } + } + + // Create a new Ratio. Fails if `denom == 0`. + #[inline(always)] + pub fn new(numer: T, denom: T) -> Ratio { + 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 { + 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 + 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 + Neg + 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 + (impl $imp:ident, $($method:ident -> $res:ty),+) => { + impl + $imp> $imp for Ratio { + $( + #[inline] + fn $method(&self, other: &Ratio) -> $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 + Div + Modulo + Neg + Zero + One + Ord + Eq> + Mul,Ratio> for Ratio { + #[inline] + fn mul(&self, rhs: &Ratio) -> Ratio { + Ratio::new(self.numer * rhs.numer, self.denom * rhs.denom) + } +} + +// (a/b) / (c/d) = (a*d)/(b*c) +impl + Div + Modulo + Neg + Zero + One + Ord + Eq> + Div,Ratio> for Ratio { + #[inline] + fn div(&self, rhs: &Ratio) -> Ratio { + 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 + Sub + Mul + Div + Modulo + Neg + + Zero + One + Ord + Eq> + $imp,Ratio> for Ratio { + #[inline] + fn $method(&self, rhs: &Ratio) -> Ratio { + 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 + Modulo + Neg + Zero + One + Ord + Eq> + Neg> for Ratio { + #[inline] + fn neg(&self) -> Ratio { + Ratio::new_raw(-self.numer, self.denom) + } +} + +/* Constants */ +impl + Modulo + Neg + Zero + One + Ord + Eq> + Zero for Ratio { + #[inline] + fn zero() -> Ratio { + Ratio::new_raw(Zero::zero(), One::one()) + } +} + +impl + Modulo + Neg + Zero + One + Ord + Eq> + One for Ratio { + #[inline] + fn one() -> Ratio { + Ratio::new_raw(One::one(), One::one()) + } +} + +/* Utils */ +impl + Sub + Mul + Div + Modulo + Neg + + Zero + One + Ord + Eq> + Round for Ratio { + fn round(&self, mode: num::RoundMode) -> Ratio { + 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 { + 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 { + 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 { + Ratio::new_raw(self.numer % self.denom, self.denom) + } +} + + +/* String conversions */ +impl ToStr for Ratio { + /// Renders as `numer/denom`. + fn to_str(&self) -> ~str { + fmt!("%s/%s", self.numer.to_str(), self.denom.to_str()) + } +} +impl ToStrRadix for Ratio { + /// 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 + Modulo + Neg + Zero + One + Ord + Eq> + FromStr for Ratio { + /// Parses `numer/denom`. + fn from_str(s: &str) -> Option> { + 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 + Modulo + Neg + Zero + One + Ord + Eq> + FromStrRadix for Ratio { + /// Parses `numer/denom` where the numbers are in base `radix`. + fn from_str_radix(s: &str, radix: uint) -> Option> { + 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::(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::(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 30346d1b16b5a..656156b355f91 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -97,6 +97,8 @@ pub mod rl; pub mod workcache; #[path="num/bigint.rs"] pub mod bigint; +#[path="num/rational.rs"] +pub mod rational; pub mod stats; pub mod semver; pub mod fileinput; From 82d54602d7493f9a40a040d43ffe5be692582665 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 5 Apr 2013 18:18:19 +1100 Subject: [PATCH 3/4] libstd: add basic complex numbers --- src/libstd/num/complex.rs | 316 ++++++++++++++++++++++++++++++++++++++ src/libstd/std.rc | 2 + 2 files changed, 318 insertions(+) create mode 100644 src/libstd/num/complex.rs diff --git a/src/libstd/num/complex.rs b/src/libstd/num/complex.rs new file mode 100644 index 0000000000000..ceb1078d3f27b --- /dev/null +++ b/src/libstd/num/complex.rs @@ -0,0 +1,316 @@ +// 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 or the MIT license +// , 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. + +// XXX: Need generic sqrt to implement .norm(). Need generic sin/cos +// for .to/from_polar(). + + +/// A complex number in Cartesian form. +#[deriving(Eq,Clone)] +pub struct Cmplx { + re: T, + im: T +} + +pub type Complex = Cmplx; +pub type Complex32 = Cmplx; +pub type Complex64 = Cmplx; + +impl + Sub + Mul + Div + Neg> + Cmplx { + /// Create a new Cmplx + #[inline] + pub fn new(re: T, im: T) -> Cmplx { + 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 { + Cmplx::new(self.re, -self.im) + } + + + /// Multiplies `self` by the scalar `t`. + #[inline] + pub fn scale(&self, t: T) -> Cmplx { + Cmplx::new(self.re * t, self.im * t) + } + + /// Divides `self` by the scalar `t`. + #[inline] + pub fn unscale(&self, t: T) -> Cmplx { + Cmplx::new(self.re / t, self.im / t) + } + + /// Returns `1/self` + #[inline] + pub fn inv(&self) -> Cmplx { + 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 + Sub + Mul + Div + Neg> + Add, Cmplx> for Cmplx { + #[inline] + fn add(&self, other: &Cmplx) -> Cmplx { + Cmplx::new(self.re + other.re, self.im + other.im) + } +} +// (a + i b) - (c + i d) == (a - c) + i (b - d) +impl + Sub + Mul + Div + Neg> + Sub, Cmplx> for Cmplx { + #[inline] + fn sub(&self, other: &Cmplx) -> Cmplx { + 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 + Sub + Mul + Div + Neg> + Mul, Cmplx> for Cmplx { + #[inline] + fn mul(&self, other: &Cmplx) -> Cmplx { + 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 + Sub + Mul + Div + Neg> + Div, Cmplx> for Cmplx { + #[inline] + fn div(&self, other: &Cmplx) -> Cmplx { + 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 + Sub + Mul + Div + Neg> + Neg> for Cmplx { + #[inline] + fn neg(&self) -> Cmplx { + Cmplx::new(-self.re, -self.im) + } +} + +/* constants */ +impl + Sub + Mul + Div + Neg + Zero> + Zero for Cmplx { + #[inline] + fn zero() -> Cmplx { + Cmplx::new(Zero::zero(), Zero::zero()) + } +} + +impl + Sub + Mul + Div + Neg + Zero + One> + One for Cmplx { + #[inline] + fn one() -> Cmplx { + Cmplx::new(One::one(), Zero::zero()) + } +} + +/* string conversions */ +impl> ToStr for Cmplx { + 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> ToStrRadix for Cmplx { + 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() { + // XXX: 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/std.rc b/src/libstd/std.rc index 656156b355f91..9aac8d230558d 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -99,6 +99,8 @@ pub mod workcache; 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; From c6949b3669d23a1694b964108f21d5200c985cb5 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 5 Apr 2013 19:26:58 +1100 Subject: [PATCH 4/4] libstd: make complex.rs XXX's into issues and FIXME's --- src/libstd/num/complex.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libstd/num/complex.rs b/src/libstd/num/complex.rs index ceb1078d3f27b..1e8fc0e6c2baa 100644 --- a/src/libstd/num/complex.rs +++ b/src/libstd/num/complex.rs @@ -17,8 +17,8 @@ use core::prelude::*; // FIXME #1284: handle complex NaN & infinity etc. This // probably doesn't map to C's _Complex correctly. -// XXX: Need generic sqrt to implement .norm(). Need generic sin/cos -// for .to/from_polar(). +// FIXME #5734:: Need generic sin/cos for .to/from_polar(). +// FIXME #5735: Need generic sqrt to implement .norm(). /// A complex number in Cartesian form. @@ -75,7 +75,6 @@ impl + Sub + Mul + Div + Neg> let norm_sqr = self.norm_sqr(); Cmplx::new(self.re / norm_sqr, -self.im / norm_sqr) - } } @@ -233,7 +232,7 @@ mod test { #[should_fail] #[ignore] fn test_inv_zero() { - // XXX: should this really fail, or just NaN? + // FIXME #5736: should this really fail, or just NaN? _0_0i.inv(); }