Skip to content

Commit bdd2439

Browse files
committed
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).
2 parents f678d63 + c6949b3 commit bdd2439

File tree

4 files changed

+831
-0
lines changed

4 files changed

+831
-0
lines changed
File renamed without changes.

src/libstd/num/complex.rs

+315
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
12+
//! Complex numbers.
13+
14+
use core::num::{Zero,One,ToStrRadix};
15+
use core::prelude::*;
16+
17+
// FIXME #1284: handle complex NaN & infinity etc. This
18+
// probably doesn't map to C's _Complex correctly.
19+
20+
// FIXME #5734:: Need generic sin/cos for .to/from_polar().
21+
// FIXME #5735: Need generic sqrt to implement .norm().
22+
23+
24+
/// A complex number in Cartesian form.
25+
#[deriving(Eq,Clone)]
26+
pub struct Cmplx<T> {
27+
re: T,
28+
im: T
29+
}
30+
31+
pub type Complex = Cmplx<float>;
32+
pub type Complex32 = Cmplx<f32>;
33+
pub type Complex64 = Cmplx<f64>;
34+
35+
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
36+
Cmplx<T> {
37+
/// Create a new Cmplx
38+
#[inline]
39+
pub fn new(re: T, im: T) -> Cmplx<T> {
40+
Cmplx { re: re, im: im }
41+
}
42+
43+
/**
44+
Returns the square of the norm (since `T` doesn't necessarily
45+
have a sqrt function), i.e. `re^2 + im^2`.
46+
*/
47+
#[inline]
48+
pub fn norm_sqr(&self) -> T {
49+
self.re * self.re + self.im * self.im
50+
}
51+
52+
53+
/// Returns the complex conjugate. i.e. `re - i im`
54+
#[inline]
55+
pub fn conj(&self) -> Cmplx<T> {
56+
Cmplx::new(self.re, -self.im)
57+
}
58+
59+
60+
/// Multiplies `self` by the scalar `t`.
61+
#[inline]
62+
pub fn scale(&self, t: T) -> Cmplx<T> {
63+
Cmplx::new(self.re * t, self.im * t)
64+
}
65+
66+
/// Divides `self` by the scalar `t`.
67+
#[inline]
68+
pub fn unscale(&self, t: T) -> Cmplx<T> {
69+
Cmplx::new(self.re / t, self.im / t)
70+
}
71+
72+
/// Returns `1/self`
73+
#[inline]
74+
pub fn inv(&self) -> Cmplx<T> {
75+
let norm_sqr = self.norm_sqr();
76+
Cmplx::new(self.re / norm_sqr,
77+
-self.im / norm_sqr)
78+
}
79+
}
80+
81+
/* arithmetic */
82+
// (a + i b) + (c + i d) == (a + c) + i (b + d)
83+
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
84+
Add<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
85+
#[inline]
86+
fn add(&self, other: &Cmplx<T>) -> Cmplx<T> {
87+
Cmplx::new(self.re + other.re, self.im + other.im)
88+
}
89+
}
90+
// (a + i b) - (c + i d) == (a - c) + i (b - d)
91+
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
92+
Sub<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
93+
#[inline]
94+
fn sub(&self, other: &Cmplx<T>) -> Cmplx<T> {
95+
Cmplx::new(self.re - other.re, self.im - other.im)
96+
}
97+
}
98+
// (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c)
99+
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
100+
Mul<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
101+
#[inline]
102+
fn mul(&self, other: &Cmplx<T>) -> Cmplx<T> {
103+
Cmplx::new(self.re*other.re - self.im*other.im,
104+
self.re*other.im + self.im*other.re)
105+
}
106+
}
107+
108+
// (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d)
109+
// == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)]
110+
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
111+
Div<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
112+
#[inline]
113+
fn div(&self, other: &Cmplx<T>) -> Cmplx<T> {
114+
let norm_sqr = other.norm_sqr();
115+
Cmplx::new((self.re*other.re + self.im*other.im) / norm_sqr,
116+
(self.im*other.re - self.re*other.im) / norm_sqr)
117+
}
118+
}
119+
120+
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
121+
Neg<Cmplx<T>> for Cmplx<T> {
122+
#[inline]
123+
fn neg(&self) -> Cmplx<T> {
124+
Cmplx::new(-self.re, -self.im)
125+
}
126+
}
127+
128+
/* constants */
129+
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T> + Zero>
130+
Zero for Cmplx<T> {
131+
#[inline]
132+
fn zero() -> Cmplx<T> {
133+
Cmplx::new(Zero::zero(), Zero::zero())
134+
}
135+
}
136+
137+
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T> + Zero + One>
138+
One for Cmplx<T> {
139+
#[inline]
140+
fn one() -> Cmplx<T> {
141+
Cmplx::new(One::one(), Zero::zero())
142+
}
143+
}
144+
145+
/* string conversions */
146+
impl<T: ToStr + Zero + Ord + Neg<T>> ToStr for Cmplx<T> {
147+
fn to_str(&self) -> ~str {
148+
if self.im < Zero::zero() {
149+
fmt!("%s-%si", self.re.to_str(), (-self.im).to_str())
150+
} else {
151+
fmt!("%s+%si", self.re.to_str(), self.im.to_str())
152+
}
153+
}
154+
}
155+
156+
impl<T: ToStrRadix + Zero + Ord + Neg<T>> ToStrRadix for Cmplx<T> {
157+
fn to_str_radix(&self, radix: uint) -> ~str {
158+
if self.im < Zero::zero() {
159+
fmt!("%s-%si", self.re.to_str_radix(radix), (-self.im).to_str_radix(radix))
160+
} else {
161+
fmt!("%s+%si", self.re.to_str_radix(radix), self.im.to_str_radix(radix))
162+
}
163+
}
164+
}
165+
166+
#[cfg(test)]
167+
mod test {
168+
use core::prelude::*;
169+
use super::*;
170+
use core::num::{Zero,One};
171+
172+
pub static _0_0i : Complex = Cmplx { re: 0f, im: 0f };
173+
pub static _1_0i : Complex = Cmplx { re: 1f, im: 0f };
174+
pub static _1_1i : Complex = Cmplx { re: 1f, im: 1f };
175+
pub static _0_1i : Complex = Cmplx { re: 0f, im: 1f };
176+
pub static _neg1_1i : Complex = Cmplx { re: -1f, im: 1f };
177+
pub static _05_05i : Complex = Cmplx { re: 0.5f, im: 0.5f };
178+
pub static all_consts : [Complex, .. 5] = [_0_0i, _1_0i, _1_1i, _neg1_1i, _05_05i];
179+
180+
#[test]
181+
fn test_consts() {
182+
// check our constants are what Cmplx::new creates
183+
fn test(c : Complex, r : float, i: float) {
184+
assert_eq!(c, Cmplx::new(r,i));
185+
}
186+
test(_0_0i, 0f, 0f);
187+
test(_1_0i, 1f, 0f);
188+
test(_1_1i, 1f, 1f);
189+
test(_neg1_1i, -1f, 1f);
190+
test(_05_05i, 0.5f, 0.5f);
191+
192+
assert_eq!(_0_0i, Zero::zero());
193+
assert_eq!(_1_0i, One::one());
194+
}
195+
196+
#[test]
197+
fn test_norm_sqr() {
198+
fn test(c: Complex, ns: float) {
199+
assert_eq!(c.norm_sqr(), ns);
200+
}
201+
test(_0_0i, 0f);
202+
test(_1_0i, 1f);
203+
test(_1_1i, 2f);
204+
test(_neg1_1i, 2f);
205+
test(_05_05i, 0.5f);
206+
}
207+
208+
#[test]
209+
fn test_scale_unscale() {
210+
assert_eq!(_05_05i.scale(2f), _1_1i);
211+
assert_eq!(_1_1i.unscale(2f), _05_05i);
212+
for all_consts.each |&c| {
213+
assert_eq!(c.scale(2f).unscale(2f), c);
214+
}
215+
}
216+
217+
#[test]
218+
fn test_conj() {
219+
for all_consts.each |&c| {
220+
assert_eq!(c.conj(), Cmplx::new(c.re, -c.im));
221+
assert_eq!(c.conj().conj(), c);
222+
}
223+
}
224+
225+
#[test]
226+
fn test_inv() {
227+
assert_eq!(_1_1i.inv(), _05_05i.conj());
228+
assert_eq!(_1_0i.inv(), _1_0i.inv());
229+
}
230+
231+
#[test]
232+
#[should_fail]
233+
#[ignore]
234+
fn test_inv_zero() {
235+
// FIXME #5736: should this really fail, or just NaN?
236+
_0_0i.inv();
237+
}
238+
239+
240+
mod arith {
241+
use super::*;
242+
use super::super::*;
243+
use core::num::Zero;
244+
245+
#[test]
246+
fn test_add() {
247+
assert_eq!(_05_05i + _05_05i, _1_1i);
248+
assert_eq!(_0_1i + _1_0i, _1_1i);
249+
assert_eq!(_1_0i + _neg1_1i, _0_1i);
250+
251+
for all_consts.each |&c| {
252+
assert_eq!(_0_0i + c, c);
253+
assert_eq!(c + _0_0i, c);
254+
}
255+
}
256+
257+
#[test]
258+
fn test_sub() {
259+
assert_eq!(_05_05i - _05_05i, _0_0i);
260+
assert_eq!(_0_1i - _1_0i, _neg1_1i);
261+
assert_eq!(_0_1i - _neg1_1i, _1_0i);
262+
263+
for all_consts.each |&c| {
264+
assert_eq!(c - _0_0i, c);
265+
assert_eq!(c - c, _0_0i);
266+
}
267+
}
268+
269+
#[test]
270+
fn test_mul() {
271+
assert_eq!(_05_05i * _05_05i, _0_1i.unscale(2f));
272+
assert_eq!(_1_1i * _0_1i, _neg1_1i);
273+
274+
// i^2 & i^4
275+
assert_eq!(_0_1i * _0_1i, -_1_0i);
276+
assert_eq!(_0_1i * _0_1i * _0_1i * _0_1i, _1_0i);
277+
278+
for all_consts.each |&c| {
279+
assert_eq!(c * _1_0i, c);
280+
assert_eq!(_1_0i * c, c);
281+
}
282+
}
283+
#[test]
284+
fn test_div() {
285+
assert_eq!(_neg1_1i / _0_1i, _1_1i);
286+
for all_consts.each |&c| {
287+
if c != Zero::zero() {
288+
assert_eq!(c / c, _1_0i);
289+
}
290+
}
291+
}
292+
#[test]
293+
fn test_neg() {
294+
assert_eq!(-_1_0i + _0_1i, _neg1_1i);
295+
assert_eq!((-_0_1i) * _0_1i, _1_0i);
296+
for all_consts.each |&c| {
297+
assert_eq!(-(-c), c);
298+
}
299+
}
300+
}
301+
302+
#[test]
303+
fn test_to_str() {
304+
fn test(c : Complex, s: ~str) {
305+
assert_eq!(c.to_str(), s);
306+
}
307+
test(_0_0i, ~"0+0i");
308+
test(_1_0i, ~"1+0i");
309+
test(_0_1i, ~"0+1i");
310+
test(_1_1i, ~"1+1i");
311+
test(_neg1_1i, ~"-1+1i");
312+
test(-_neg1_1i, ~"1-1i");
313+
test(_05_05i, ~"0.5+0.5i");
314+
}
315+
}

0 commit comments

Comments
 (0)