|
| 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