diff -Nru rust-num-complex-0.2.1/bors.toml rust-num-complex-0.2.3/bors.toml --- rust-num-complex-0.2.1/bors.toml 2018-03-06 22:47:15.000000000 +0000 +++ rust-num-complex-0.2.3/bors.toml 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -status = [ - "continuous-integration/travis-ci/push", -] diff -Nru rust-num-complex-0.2.1/build.rs rust-num-complex-0.2.3/build.rs --- rust-num-complex-0.2.1/build.rs 2018-05-22 01:10:39.000000000 +0000 +++ rust-num-complex-0.2.3/build.rs 2019-05-22 00:31:04.000000000 +0000 @@ -1,35 +1,14 @@ +extern crate autocfg; + use std::env; -use std::io::Write; -use std::process::{Command, Stdio}; fn main() { - if probe("fn main() { 0i128; }") { + let ac = autocfg::new(); + if ac.probe_type("i128") { println!("cargo:rustc-cfg=has_i128"); } else if env::var_os("CARGO_FEATURE_I128").is_some() { panic!("i128 support was not detected!"); } -} - -/// Test if a code snippet can be compiled -fn probe(code: &str) -> bool { - let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); - let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR"); - - let mut child = Command::new(rustc) - .arg("--out-dir") - .arg(out_dir) - .arg("--emit=obj") - .arg("-") - .stdin(Stdio::piped()) - .spawn() - .expect("rustc probe"); - - child - .stdin - .as_mut() - .expect("rustc stdin") - .write_all(code.as_bytes()) - .expect("write rustc stdin"); - child.wait().expect("rustc probe").success() + autocfg::rerun_path(file!()); } diff -Nru rust-num-complex-0.2.1/Cargo.toml rust-num-complex-0.2.3/Cargo.toml --- rust-num-complex-0.2.1/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 +++ rust-num-complex-0.2.3/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 @@ -3,7 +3,7 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies +# to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're @@ -12,9 +12,10 @@ [package] name = "num-complex" -version = "0.2.1" +version = "0.2.3" authors = ["The Rust Project Developers"] build = "build.rs" +exclude = ["/ci/*", "/.travis.yml", "/bors.toml"] description = "Complex numbers implementation for Rust" homepage = "https://github.com/rust-num/num-complex" documentation = "https://docs.rs/num-complex" @@ -26,7 +27,7 @@ [package.metadata.docs.rs] features = ["std", "serde", "rand"] [dependencies.num-traits] -version = "0.2.4" +version = "0.2.7" default-features = false [dependencies.rand] @@ -38,6 +39,8 @@ version = "1.0" optional = true default-features = false +[build-dependencies.autocfg] +version = "0.1.3" [features] default = ["std"] diff -Nru rust-num-complex-0.2.1/Cargo.toml.orig rust-num-complex-0.2.3/Cargo.toml.orig --- rust-num-complex-0.2.1/Cargo.toml.orig 2018-10-08 23:47:39.000000000 +0000 +++ rust-num-complex-0.2.3/Cargo.toml.orig 2019-06-11 23:01:05.000000000 +0000 @@ -8,9 +8,10 @@ license = "MIT/Apache-2.0" name = "num-complex" repository = "https://github.com/rust-num/num-complex" -version = "0.2.1" +version = "0.2.3" readme = "README.md" build = "build.rs" +exclude = ["/ci/*", "/.travis.yml", "/bors.toml"] [package.metadata.docs.rs] features = ["std", "serde", "rand"] @@ -18,7 +19,7 @@ [dependencies] [dependencies.num-traits] -version = "0.2.4" +version = "0.2.7" default-features = false [dependencies.serde] @@ -35,3 +36,6 @@ default = ["std"] i128 = ["num-traits/i128"] std = ["num-traits/std"] + +[build-dependencies] +autocfg = "0.1.3" diff -Nru rust-num-complex-0.2.1/.cargo_vcs_info.json rust-num-complex-0.2.3/.cargo_vcs_info.json --- rust-num-complex-0.2.1/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 +++ rust-num-complex-0.2.3/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "dc2157f99981e454e610ec3572738e0597aa4e23" + } +} diff -Nru rust-num-complex-0.2.1/ci/rustup.sh rust-num-complex-0.2.3/ci/rustup.sh --- rust-num-complex-0.2.1/ci/rustup.sh 2018-05-22 01:10:39.000000000 +0000 +++ rust-num-complex-0.2.3/ci/rustup.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -#!/bin/sh -# Use rustup to locally run the same suite of tests as .travis.yml. -# (You should first install/update all versions listed below.) - -set -ex - -export TRAVIS_RUST_VERSION -for TRAVIS_RUST_VERSION in 1.15.0 1.22.0 1.26.0 stable beta nightly; do - run="rustup run $TRAVIS_RUST_VERSION" - $run cargo build --verbose - $run $PWD/ci/test_full.sh -done diff -Nru rust-num-complex-0.2.1/ci/test_full.sh rust-num-complex-0.2.3/ci/test_full.sh --- rust-num-complex-0.2.1/ci/test_full.sh 2018-05-25 00:22:37.000000000 +0000 +++ rust-num-complex-0.2.3/ci/test_full.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -#!/bin/bash - -set -ex - -echo Testing num-complex on rustc ${TRAVIS_RUST_VERSION} - -FEATURES="std serde" -if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable|1.26.0|1.22.0)$ ]]; then - FEATURES="$FEATURES rand" -fi -if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable|1.26.0)$ ]]; then - FEATURES="$FEATURES i128" -fi - -# num-complex should build and test everywhere. -cargo build --verbose -cargo test --verbose - -# It should build with minimal features too. -cargo build --no-default-features -cargo test --no-default-features - -# Each isolated feature should also work everywhere. -for feature in $FEATURES; do - cargo build --verbose --no-default-features --features="$feature" - cargo test --verbose --no-default-features --features="$feature" -done - -# test all supported features together -cargo build --features="$FEATURES" -cargo test --features="$FEATURES" diff -Nru rust-num-complex-0.2.1/debian/cargo-checksum.json rust-num-complex-0.2.3/debian/cargo-checksum.json --- rust-num-complex-0.2.1/debian/cargo-checksum.json 2018-10-20 14:02:28.000000000 +0000 +++ rust-num-complex-0.2.3/debian/cargo-checksum.json 2019-12-01 09:59:28.000000000 +0000 @@ -1 +1 @@ -{"package":"107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8","files":{}} +{"package":"fcb0cf31fb3ff77e6d2a6ebd6800df7fdcd106f2ad89113c9130bcd07f93dffc","files":{}} diff -Nru rust-num-complex-0.2.1/debian/changelog rust-num-complex-0.2.3/debian/changelog --- rust-num-complex-0.2.1/debian/changelog 2018-10-20 14:02:28.000000000 +0000 +++ rust-num-complex-0.2.3/debian/changelog 2019-12-01 09:59:28.000000000 +0000 @@ -1,3 +1,9 @@ +rust-num-complex (0.2.3-1) unstable; urgency=medium + + * Package num-complex 0.2.3 from crates.io using debcargo 2.4.0 + + -- Sylvestre Ledru Sun, 01 Dec 2019 10:59:28 +0100 + rust-num-complex (0.2.1-1) unstable; urgency=medium * Package num-complex 0.2.1 from crates.io using debcargo 2.2.7 diff -Nru rust-num-complex-0.2.1/debian/control rust-num-complex-0.2.3/debian/control --- rust-num-complex-0.2.1/debian/control 2018-10-20 14:02:28.000000000 +0000 +++ rust-num-complex-0.2.3/debian/control 2019-12-01 09:59:28.000000000 +0000 @@ -2,12 +2,13 @@ Section: rust Priority: optional Build-Depends: debhelper (>= 11), - dh-cargo (>= 10), + dh-cargo (>= 18), cargo:native , rustc:native , libstd-rust-dev , - librust-num-traits-0.2+std-dev (>= 0.2.4~~) , - librust-num-traits-0.2-dev (>= 0.2.4~~) + librust-autocfg-0.1+default-dev (>= 0.1.3-~~) , + librust-num-traits-0.2+std-dev (>= 0.2.7-~~) , + librust-num-traits-0.2-dev (>= 0.2.7-~~) Maintainer: Debian Rust Maintainers Uploaders: Sylvestre Ledru @@ -21,7 +22,8 @@ Multi-Arch: same Depends: ${misc:Depends}, - librust-num-traits-0.2-dev (>= 0.2.4~~) + librust-autocfg-0.1+default-dev (>= 0.1.3-~~), + librust-num-traits-0.2-dev (>= 0.2.7-~~) Recommends: librust-num-complex+std-dev (= ${binary:Version}) Suggests: @@ -31,7 +33,7 @@ Provides: librust-num-complex-0-dev (= ${binary:Version}), librust-num-complex-0.2-dev (= ${binary:Version}), - librust-num-complex-0.2.1-dev (= ${binary:Version}) + librust-num-complex-0.2.3-dev (= ${binary:Version}) Description: Complex numbers implementation for Rust - Rust source code This package contains the source for the Rust num-complex crate, packaged by debcargo for use with cargo and dh-cargo. @@ -42,13 +44,13 @@ Depends: ${misc:Depends}, librust-num-complex-dev (= ${binary:Version}), - librust-num-traits-0.2+i128-dev (>= 0.2.4~~) + librust-num-traits-0.2+i128-dev (>= 0.2.7-~~) Provides: librust-num-complex-0+i128-dev (= ${binary:Version}), librust-num-complex-0.2+i128-dev (= ${binary:Version}), - librust-num-complex-0.2.1+i128-dev (= ${binary:Version}) + librust-num-complex-0.2.3+i128-dev (= ${binary:Version}) Description: Complex numbers implementation for Rust - feature "i128" - This metapackage enables feature i128 for the Rust num-complex crate, by + This metapackage enables feature "i128" for the Rust num-complex crate, by pulling in any additional dependencies needed by that feature. Package: librust-num-complex+rand-dev @@ -61,9 +63,9 @@ Provides: librust-num-complex-0+rand-dev (= ${binary:Version}), librust-num-complex-0.2+rand-dev (= ${binary:Version}), - librust-num-complex-0.2.1+rand-dev (= ${binary:Version}) + librust-num-complex-0.2.3+rand-dev (= ${binary:Version}) Description: Complex numbers implementation for Rust - feature "rand" - This metapackage enables feature rand for the Rust num-complex crate, by + This metapackage enables feature "rand" for the Rust num-complex crate, by pulling in any additional dependencies needed by that feature. Package: librust-num-complex+serde-dev @@ -76,9 +78,9 @@ Provides: librust-num-complex-0+serde-dev (= ${binary:Version}), librust-num-complex-0.2+serde-dev (= ${binary:Version}), - librust-num-complex-0.2.1+serde-dev (= ${binary:Version}) + librust-num-complex-0.2.3+serde-dev (= ${binary:Version}) Description: Complex numbers implementation for Rust - feature "serde" - This metapackage enables feature serde for the Rust num-complex crate, by + This metapackage enables feature "serde" for the Rust num-complex crate, by pulling in any additional dependencies needed by that feature. Package: librust-num-complex+std-dev @@ -87,15 +89,17 @@ Depends: ${misc:Depends}, librust-num-complex-dev (= ${binary:Version}), - librust-num-traits-0.2+std-dev (>= 0.2.4~~) + librust-num-traits-0.2+std-dev (>= 0.2.7-~~) Provides: librust-num-complex+default-dev (= ${binary:Version}), librust-num-complex-0+std-dev (= ${binary:Version}), librust-num-complex-0+default-dev (= ${binary:Version}), librust-num-complex-0.2+std-dev (= ${binary:Version}), librust-num-complex-0.2+default-dev (= ${binary:Version}), - librust-num-complex-0.2.1+std-dev (= ${binary:Version}), - librust-num-complex-0.2.1+default-dev (= ${binary:Version}) -Description: Complex numbers implementation for Rust - feature "std" - This metapackage enables feature std for the Rust num-complex crate, by pulling - in any additional dependencies needed by that feature. + librust-num-complex-0.2.3+std-dev (= ${binary:Version}), + librust-num-complex-0.2.3+default-dev (= ${binary:Version}) +Description: Complex numbers implementation for Rust - feature "std" and 1 more + This metapackage enables feature "std" for the Rust num-complex crate, by + pulling in any additional dependencies needed by that feature. + . + Additionally, this package also provides the "default" feature. diff -Nru rust-num-complex-0.2.1/debian/copyright.debcargo.hint rust-num-complex-0.2.3/debian/copyright.debcargo.hint --- rust-num-complex-0.2.1/debian/copyright.debcargo.hint 2018-10-20 14:02:28.000000000 +0000 +++ rust-num-complex-0.2.3/debian/copyright.debcargo.hint 2019-12-01 09:59:28.000000000 +0000 @@ -28,8 +28,8 @@ Files: debian/* Copyright: - 2018 Debian Rust Maintainers - 2018 Sylvestre Ledru + 2018-2019 Debian Rust Maintainers + 2018-2019 Sylvestre Ledru License: MIT or Apache-2.0 License: Apache-2.0 diff -Nru rust-num-complex-0.2.1/debian/rules rust-num-complex-0.2.3/debian/rules --- rust-num-complex-0.2.1/debian/rules 2018-10-20 14:02:28.000000000 +0000 +++ rust-num-complex-0.2.3/debian/rules 2019-12-01 09:59:28.000000000 +0000 @@ -1,3 +1,6 @@ #!/usr/bin/make -f %: dh $@ --buildsystem cargo + +override_dh_auto_test: + dh_auto_test -- test --all diff -Nru rust-num-complex-0.2.1/debian/tests/control rust-num-complex-0.2.3/debian/tests/control --- rust-num-complex-0.2.1/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ rust-num-complex-0.2.3/debian/tests/control 2019-12-01 09:59:28.000000000 +0000 @@ -0,0 +1,23 @@ +Test-Command: /usr/share/cargo/bin/cargo-auto-test num-complex 0.2.3 --all-targets --all-features +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test num-complex 0.2.3 --all-targets --no-default-features +Depends: dh-cargo (>= 18), librust-num-complex-dev +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test num-complex 0.2.3 --all-targets --features i128 +Depends: dh-cargo (>= 18), librust-num-complex+i128-dev +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test num-complex 0.2.3 --all-targets --features rand +Depends: dh-cargo (>= 18), librust-num-complex+rand-dev +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test num-complex 0.2.3 --all-targets --features serde +Depends: dh-cargo (>= 18), librust-num-complex+serde-dev +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test num-complex 0.2.3 --all-targets --features std +Depends: dh-cargo (>= 18), librust-num-complex+std-dev +Restrictions: allow-stderr, skip-not-installable diff -Nru rust-num-complex-0.2.1/RELEASES.md rust-num-complex-0.2.3/RELEASES.md --- rust-num-complex-0.2.1/RELEASES.md 2018-10-08 23:52:00.000000000 +0000 +++ rust-num-complex-0.2.3/RELEASES.md 2019-06-11 23:00:38.000000000 +0000 @@ -1,3 +1,30 @@ +# Release 0.2.3 (2019-06-11) + +- [`Complex::sqrt()` is now more accurate for negative reals][60]. +- [`Complex::cbrt()` computes the principal cube root][61]. + +**Contributors**: @cuviper + +[60]: https://github.com/rust-num/num-complex/pull/60 +[61]: https://github.com/rust-num/num-complex/pull/61 + +# Release 0.2.2 (2019-06-10) + +- [`Complex::l1_norm()` computes the Manhattan distance from the origin][43]. +- [`Complex::fdiv()` and `finv()` use floating-point for inversion][41], which + may avoid overflows for some inputs, at the cost of trigonometric rounding. +- [`Complex` now implements `num_traits::MulAdd` and `MulAddAssign`][44]. +- [`Complex` now implements `Zero::set_zero` and `One::set_one`][57]. +- [`Complex` now implements `num_traits::Pow` and adds `powi` and `powu`][56]. + +**Contributors**: @adamnemecek, @cuviper, @ignatenkobrain, @Schultzer + +[41]: https://github.com/rust-num/num-complex/pull/41 +[43]: https://github.com/rust-num/num-complex/pull/43 +[44]: https://github.com/rust-num/num-complex/pull/44 +[56]: https://github.com/rust-num/num-complex/pull/56 +[57]: https://github.com/rust-num/num-complex/pull/57 + # Release 0.2.1 (2018-10-08) - [`Complex` now implements `ToPrimitive`, `FromPrimitive`, `AsPrimitive`, and `NumCast`][33]. diff -Nru rust-num-complex-0.2.1/src/lib.rs rust-num-complex-0.2.3/src/lib.rs --- rust-num-complex-0.2.1/src/lib.rs 2018-10-03 18:45:39.000000000 +0000 +++ rust-num-complex-0.2.3/src/lib.rs 2019-06-11 22:55:15.000000000 +0000 @@ -38,13 +38,15 @@ #[cfg(feature = "std")] use std::error::Error; -use traits::{Inv, Num, One, Zero}; +use traits::{Inv, MulAdd, Num, One, Pow, Signed, Zero}; #[cfg(feature = "std")] use traits::float::Float; use traits::float::FloatCore; mod cast; +mod pow; + #[cfg(feature = "rand")] mod crand; #[cfg(feature = "rand")] @@ -93,13 +95,13 @@ impl Complex { /// Create a new Complex #[inline] - pub fn new(re: T, im: T) -> Complex { + pub fn new(re: T, im: T) -> Self { Complex { re: re, im: im } } /// Returns imaginary unit #[inline] - pub fn i() -> Complex { + pub fn i() -> Self { Self::new(T::zero(), T::one()) } @@ -112,33 +114,55 @@ /// Multiplies `self` by the scalar `t`. #[inline] - pub fn scale(&self, t: T) -> Complex { - Complex::new(self.re.clone() * t.clone(), self.im.clone() * t) + pub fn scale(&self, t: T) -> Self { + Self::new(self.re.clone() * t.clone(), self.im.clone() * t) } /// Divides `self` by the scalar `t`. #[inline] - pub fn unscale(&self, t: T) -> Complex { - Complex::new(self.re.clone() / t.clone(), self.im.clone() / t) + pub fn unscale(&self, t: T) -> Self { + Self::new(self.re.clone() / t.clone(), self.im.clone() / t) + } + + /// Raises `self` to an unsigned integer power. + #[inline] + pub fn powu(&self, exp: u32) -> Self { + Pow::pow(self, exp) } } impl> Complex { /// Returns the complex conjugate. i.e. `re - i im` #[inline] - pub fn conj(&self) -> Complex { - Complex::new(self.re.clone(), -self.im.clone()) + pub fn conj(&self) -> Self { + Self::new(self.re.clone(), -self.im.clone()) } /// Returns `1/self` #[inline] - pub fn inv(&self) -> Complex { + pub fn inv(&self) -> Self { let norm_sqr = self.norm_sqr(); - Complex::new( + Self::new( self.re.clone() / norm_sqr.clone(), -self.im.clone() / norm_sqr, ) } + + /// Raises `self` to a signed integer power. + #[inline] + pub fn powi(&self, exp: i32) -> Self { + Pow::pow(self, exp) + } +} + +impl Complex { + /// Returns the L1 norm `|re| + |im|` -- the [Manhattan distance] from the origin. + /// + /// [Manhattan distance]: https://en.wikipedia.org/wiki/Taxicab_geometry + #[inline] + pub fn l1_norm(&self) -> T { + self.re.abs() + self.im.abs() + } } #[cfg(feature = "std")] @@ -161,16 +185,16 @@ } /// Convert a polar representation into a complex number. #[inline] - pub fn from_polar(r: &T, theta: &T) -> Complex { - Complex::new(*r * theta.cos(), *r * theta.sin()) + pub fn from_polar(r: &T, theta: &T) -> Self { + Self::new(*r * theta.cos(), *r * theta.sin()) } /// Computes `e^(self)`, where `e` is the base of the natural logarithm. #[inline] - pub fn exp(&self) -> Complex { + pub fn exp(&self) -> Self { // formula: e^(a + bi) = e^a (cos(b) + i*sin(b)) // = from_polar(e^a, b) - Complex::from_polar(&self.re.exp(), &self.im) + Self::from_polar(&self.re.exp(), &self.im) } /// Computes the principal value of natural logarithm of `self`. @@ -181,10 +205,10 @@ /// /// The branch satisfies `-π ≤ arg(ln(z)) ≤ π`. #[inline] - pub fn ln(&self) -> Complex { + pub fn ln(&self) -> Self { // formula: ln(z) = ln|z| + i*arg(z) let (r, theta) = self.to_polar(); - Complex::new(r.ln(), theta) + Self::new(r.ln(), theta) } /// Computes the principal value of the square root of `self`. @@ -195,35 +219,117 @@ /// /// The branch satisfies `-π/2 ≤ arg(sqrt(z)) ≤ π/2`. #[inline] - pub fn sqrt(&self) -> Complex { - // formula: sqrt(r e^(it)) = sqrt(r) e^(it/2) - let two = T::one() + T::one(); - let (r, theta) = self.to_polar(); - Complex::from_polar(&(r.sqrt()), &(theta / two)) + pub fn sqrt(&self) -> Self { + if self.im.is_zero() { + if self.re.is_sign_positive() { + // simple positive real √r, and copy `im` for its sign + Self::new(self.re.sqrt(), self.im) + } else { + // √(r e^(iπ)) = √r e^(iπ/2) = i√r + // √(r e^(-iπ)) = √r e^(-iπ/2) = -i√r + let re = T::zero(); + let im = (-self.re).sqrt(); + if self.im.is_sign_positive() { + Self::new(re, im) + } else { + Self::new(re, -im) + } + } + } else if self.re.is_zero() { + // √(r e^(iπ/2)) = √r e^(iπ/4) = √(r/2) + i√(r/2) + // √(r e^(-iπ/2)) = √r e^(-iπ/4) = √(r/2) - i√(r/2) + let one = T::one(); + let two = one + one; + let x = (self.im.abs() / two).sqrt(); + if self.im.is_sign_positive() { + Self::new(x, x) + } else { + Self::new(x, -x) + } + } else { + // formula: sqrt(r e^(it)) = sqrt(r) e^(it/2) + let one = T::one(); + let two = one + one; + let (r, theta) = self.to_polar(); + Self::from_polar(&(r.sqrt()), &(theta / two)) + } + } + + /// Computes the principal value of the cube root of `self`. + /// + /// This function has one branch cut: + /// + /// * `(-∞, 0)`, continuous from above. + /// + /// The branch satisfies `-π/3 ≤ arg(cbrt(z)) ≤ π/3`. + /// + /// Note that this does not match the usual result for the cube root of + /// negative real numbers. For example, the real cube root of `-8` is `-2`, + /// but the principal complex cube root of `-8` is `1 + i√3`. + #[inline] + pub fn cbrt(&self) -> Self { + if self.im.is_zero() { + if self.re.is_sign_positive() { + // simple positive real ∛r, and copy `im` for its sign + Self::new(self.re.cbrt(), self.im) + } else { + // ∛(r e^(iπ)) = ∛r e^(iπ/3) = ∛r/2 + i∛r√3/2 + // ∛(r e^(-iπ)) = ∛r e^(-iπ/3) = ∛r/2 - i∛r√3/2 + let one = T::one(); + let two = one + one; + let three = two + one; + let re = (-self.re).cbrt() / two; + let im = three.sqrt() * re; + if self.im.is_sign_positive() { + Self::new(re, im) + } else { + Self::new(re, -im) + } + } + } else if self.re.is_zero() { + // ∛(r e^(iπ/2)) = ∛r e^(iπ/6) = ∛r√3/2 + i∛r/2 + // ∛(r e^(-iπ/2)) = ∛r e^(-iπ/6) = ∛r√3/2 - i∛r/2 + let one = T::one(); + let two = one + one; + let three = two + one; + let im = self.im.abs().cbrt() / two; + let re = three.sqrt() * im; + if self.im.is_sign_positive() { + Self::new(re, im) + } else { + Self::new(re, -im) + } + } else { + // formula: cbrt(r e^(it)) = cbrt(r) e^(it/3) + let one = T::one(); + let three = one + one + one; + let (r, theta) = self.to_polar(); + Self::from_polar(&(r.cbrt()), &(theta / three)) + } } /// Raises `self` to a floating point power. #[inline] - pub fn powf(&self, exp: T) -> Complex { + pub fn powf(&self, exp: T) -> Self { // formula: x^y = (ρ e^(i θ))^y = ρ^y e^(i θ y) // = from_polar(ρ^y, θ y) let (r, theta) = self.to_polar(); - Complex::from_polar(&r.powf(exp), &(theta * exp)) + Self::from_polar(&r.powf(exp), &(theta * exp)) } /// Returns the logarithm of `self` with respect to an arbitrary base. #[inline] - pub fn log(&self, base: T) -> Complex { + pub fn log(&self, base: T) -> Self { // formula: log_y(x) = log_y(ρ e^(i θ)) // = log_y(ρ) + log_y(e^(i θ)) = log_y(ρ) + ln(e^(i θ)) / ln(y) // = log_y(ρ) + i θ / ln(y) let (r, theta) = self.to_polar(); - Complex::new(r.log(base), theta / base.ln()) + Self::new(r.log(base), theta / base.ln()) } /// Raises `self` to a complex power. #[inline] - pub fn powc(&self, exp: Complex) -> Complex { + pub fn powc(&self, exp: Self) -> Self { // formula: x^y = (a + i b)^(c + i d) // = (ρ e^(i θ))^c (ρ e^(i θ))^(i d) // where ρ=|x| and θ=arg(x) @@ -236,7 +342,7 @@ // = p^c e^(−d θ) (cos(c θ + d ln(ρ)) + i sin(c θ + d ln(ρ))) // = from_polar(p^c e^(−d θ), c θ + d ln(ρ)) let (r, theta) = self.to_polar(); - Complex::from_polar( + Self::from_polar( &(r.powf(exp.re) * (-exp.im * theta).exp()), &(exp.re * theta + exp.im * r.ln()), ) @@ -244,17 +350,17 @@ /// Raises a floating point number to the complex power `self`. #[inline] - pub fn expf(&self, base: T) -> Complex { + pub fn expf(&self, base: T) -> Self { // formula: x^(a+bi) = x^a x^bi = x^a e^(b ln(x) i) // = from_polar(x^a, b ln(x)) - Complex::from_polar(&base.powf(self.re), &(self.im * base.ln())) + Self::from_polar(&base.powf(self.re), &(self.im * base.ln())) } /// Computes the sine of `self`. #[inline] - pub fn sin(&self) -> Complex { + pub fn sin(&self) -> Self { // formula: sin(a + bi) = sin(a)cosh(b) + i*cos(a)sinh(b) - Complex::new( + Self::new( self.re.sin() * self.im.cosh(), self.re.cos() * self.im.sinh(), ) @@ -262,9 +368,9 @@ /// Computes the cosine of `self`. #[inline] - pub fn cos(&self) -> Complex { + pub fn cos(&self) -> Self { // formula: cos(a + bi) = cos(a)cosh(b) - i*sin(a)sinh(b) - Complex::new( + Self::new( self.re.cos() * self.im.cosh(), -self.re.sin() * self.im.sinh(), ) @@ -272,10 +378,10 @@ /// Computes the tangent of `self`. #[inline] - pub fn tan(&self) -> Complex { + pub fn tan(&self) -> Self { // formula: tan(a + bi) = (sin(2a) + i*sinh(2b))/(cos(2a) + cosh(2b)) let (two_re, two_im) = (self.re + self.re, self.im + self.im); - Complex::new(two_re.sin(), two_im.sinh()).unscale(two_re.cos() + two_im.cosh()) + Self::new(two_re.sin(), two_im.sinh()).unscale(two_re.cos() + two_im.cosh()) } /// Computes the principal value of the inverse sine of `self`. @@ -287,10 +393,10 @@ /// /// The branch satisfies `-π/2 ≤ Re(asin(z)) ≤ π/2`. #[inline] - pub fn asin(&self) -> Complex { + pub fn asin(&self) -> Self { // formula: arcsin(z) = -i ln(sqrt(1-z^2) + iz) - let i = Complex::::i(); - -i * ((Complex::::one() - self * self).sqrt() + i * self).ln() + let i = Self::i(); + -i * ((Self::one() - self * self).sqrt() + i * self).ln() } /// Computes the principal value of the inverse cosine of `self`. @@ -302,10 +408,10 @@ /// /// The branch satisfies `0 ≤ Re(acos(z)) ≤ π`. #[inline] - pub fn acos(&self) -> Complex { + pub fn acos(&self) -> Self { // formula: arccos(z) = -i ln(i sqrt(1-z^2) + z) - let i = Complex::::i(); - -i * (i * (Complex::::one() - self * self).sqrt() + self).ln() + let i = Self::i(); + -i * (i * (Self::one() - self * self).sqrt() + self).ln() } /// Computes the principal value of the inverse tangent of `self`. @@ -317,24 +423,24 @@ /// /// The branch satisfies `-π/2 ≤ Re(atan(z)) ≤ π/2`. #[inline] - pub fn atan(&self) -> Complex { + pub fn atan(&self) -> Self { // formula: arctan(z) = (ln(1+iz) - ln(1-iz))/(2i) - let i = Complex::::i(); - let one = Complex::::one(); + let i = Self::i(); + let one = Self::one(); let two = one + one; if *self == i { - return Complex::new(T::zero(), T::infinity()); + return Self::new(T::zero(), T::infinity()); } else if *self == -i { - return Complex::new(T::zero(), -T::infinity()); + return Self::new(T::zero(), -T::infinity()); } ((one + i * self).ln() - (one - i * self).ln()) / (two * i) } /// Computes the hyperbolic sine of `self`. #[inline] - pub fn sinh(&self) -> Complex { + pub fn sinh(&self) -> Self { // formula: sinh(a + bi) = sinh(a)cos(b) + i*cosh(a)sin(b) - Complex::new( + Self::new( self.re.sinh() * self.im.cos(), self.re.cosh() * self.im.sin(), ) @@ -342,9 +448,9 @@ /// Computes the hyperbolic cosine of `self`. #[inline] - pub fn cosh(&self) -> Complex { + pub fn cosh(&self) -> Self { // formula: cosh(a + bi) = cosh(a)cos(b) + i*sinh(a)sin(b) - Complex::new( + Self::new( self.re.cosh() * self.im.cos(), self.re.sinh() * self.im.sin(), ) @@ -352,10 +458,10 @@ /// Computes the hyperbolic tangent of `self`. #[inline] - pub fn tanh(&self) -> Complex { + pub fn tanh(&self) -> Self { // formula: tanh(a + bi) = (sinh(2a) + i*sin(2b))/(cosh(2a) + cos(2b)) let (two_re, two_im) = (self.re + self.re, self.im + self.im); - Complex::new(two_re.sinh(), two_im.sin()).unscale(two_re.cosh() + two_im.cos()) + Self::new(two_re.sinh(), two_im.sin()).unscale(two_re.cosh() + two_im.cos()) } /// Computes the principal value of inverse hyperbolic sine of `self`. @@ -367,9 +473,9 @@ /// /// The branch satisfies `-π/2 ≤ Im(asinh(z)) ≤ π/2`. #[inline] - pub fn asinh(&self) -> Complex { + pub fn asinh(&self) -> Self { // formula: arcsinh(z) = ln(z + sqrt(1+z^2)) - let one = Complex::::one(); + let one = Self::one(); (self + (one + self * self).sqrt()).ln() } @@ -381,9 +487,9 @@ /// /// The branch satisfies `-π ≤ Im(acosh(z)) ≤ π` and `0 ≤ Re(acosh(z)) < ∞`. #[inline] - pub fn acosh(&self) -> Complex { + pub fn acosh(&self) -> Self { // formula: arccosh(z) = 2 ln(sqrt((z+1)/2) + sqrt((z-1)/2)) - let one = Complex::one(); + let one = Self::one(); let two = one + one; two * (((self + one) / two).sqrt() + ((self - one) / two).sqrt()).ln() } @@ -397,17 +503,73 @@ /// /// The branch satisfies `-π/2 ≤ Im(atanh(z)) ≤ π/2`. #[inline] - pub fn atanh(&self) -> Complex { + pub fn atanh(&self) -> Self { // formula: arctanh(z) = (ln(1+z) - ln(1-z))/2 - let one = Complex::one(); + let one = Self::one(); let two = one + one; if *self == one { - return Complex::new(T::infinity(), T::zero()); + return Self::new(T::infinity(), T::zero()); } else if *self == -one { - return Complex::new(-T::infinity(), T::zero()); + return Self::new(-T::infinity(), T::zero()); } ((one + self).ln() - (one - self).ln()) / two } + + /// Returns `1/self` using floating-point operations. + /// + /// This may be more accurate than the generic `self.inv()` in cases + /// where `self.norm_sqr()` would overflow to ∞ or underflow to 0. + /// + /// # Examples + /// + /// ``` + /// use num_complex::Complex64; + /// let c = Complex64::new(1e300, 1e300); + /// + /// // The generic `inv()` will overflow. + /// assert!(!c.inv().is_normal()); + /// + /// // But we can do better for `Float` types. + /// let inv = c.finv(); + /// assert!(inv.is_normal()); + /// println!("{:e}", inv); + /// + /// let expected = Complex64::new(5e-301, -5e-301); + /// assert!((inv - expected).norm() < 1e-315); + /// ``` + #[inline] + pub fn finv(&self) -> Complex { + let norm = self.norm(); + self.conj() / norm / norm + } + + /// Returns `self/other` using floating-point operations. + /// + /// This may be more accurate than the generic `Div` implementation in cases + /// where `other.norm_sqr()` would overflow to ∞ or underflow to 0. + /// + /// # Examples + /// + /// ``` + /// use num_complex::Complex64; + /// let a = Complex64::new(2.0, 3.0); + /// let b = Complex64::new(1e300, 1e300); + /// + /// // Generic division will overflow. + /// assert!(!(a / b).is_normal()); + /// + /// // But we can do better for `Float` types. + /// let quotient = a.fdiv(b); + /// assert!(quotient.is_normal()); + /// println!("{:e}", quotient); + /// + /// let expected = Complex64::new(2.5e-300, 5e-301); + /// assert!((quotient - expected).norm() < 1e-315); + /// ``` + #[inline] + pub fn fdiv(&self, other: Complex) -> Complex { + self * other.finv() + } } impl Complex { @@ -438,17 +600,14 @@ impl From for Complex { #[inline] - fn from(re: T) -> Complex { - Complex { - re: re, - im: T::zero(), - } + fn from(re: T) -> Self { + Self::new(re, T::zero()) } } impl<'a, T: Clone + Num> From<&'a T> for Complex { #[inline] - fn from(re: &T) -> Complex { + fn from(re: &T) -> Self { From::from(re.clone()) } } @@ -459,7 +618,7 @@ type Output = Complex; #[inline] - fn $method(self, other: &Complex) -> Complex { + fn $method(self, other: &Complex) -> Self::Output { self.clone().$method(other.clone()) } } @@ -472,7 +631,7 @@ type Output = Complex; #[inline] - fn $method(self, other: Complex) -> Complex { + fn $method(self, other: Complex) -> Self::Output { self.clone().$method(other) } } @@ -485,7 +644,7 @@ type Output = Complex; #[inline] - fn $method(self, other: &Complex) -> Complex { + fn $method(self, other: &Complex) -> Self::Output { self.$method(other.clone()) } } @@ -505,11 +664,11 @@ // (a + i b) + (c + i d) == (a + c) + i (b + d) impl Add> for Complex { - type Output = Complex; + type Output = Self; #[inline] - fn add(self, other: Complex) -> Complex { - Complex::new(self.re + other.re, self.im + other.im) + fn add(self, other: Self) -> Self::Output { + Self::Output::new(self.re + other.re, self.im + other.im) } } @@ -517,11 +676,11 @@ // (a + i b) - (c + i d) == (a - c) + i (b - d) impl Sub> for Complex { - type Output = Complex; + type Output = Self; #[inline] - fn sub(self, other: Complex) -> Complex { - Complex::new(self.re - other.re, self.im - other.im) + fn sub(self, other: Self) -> Self::Output { + Self::Output::new(self.re - other.re, self.im - other.im) } } @@ -529,29 +688,50 @@ // (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c) impl Mul> for Complex { - type Output = Complex; + type Output = Self; #[inline] - fn mul(self, other: Complex) -> Complex { + fn mul(self, other: Self) -> Self::Output { let re = self.re.clone() * other.re.clone() - self.im.clone() * other.im.clone(); let im = self.re * other.im + self.im * other.re; + Self::Output::new(re, im) + } +} + +// (a + i b) * (c + i d) + (e + i f) == ((a*c + e) - b*d) + i (a*d + (b*c + f)) +impl> MulAdd> for Complex { + type Output = Complex; + + #[inline] + fn mul_add(self, other: Complex, add: Complex) -> Complex { + let re = self.re.clone().mul_add(other.re.clone(), add.re) + - (self.im.clone() * other.im.clone()); // FIXME: use mulsub when available in rust + let im = self.re.mul_add(other.im, self.im.mul_add(other.re, add.im)); Complex::new(re, im) } } +impl<'a, 'b, T: Clone + Num + MulAdd> MulAdd<&'b Complex> for &'a Complex { + type Output = Complex; + + #[inline] + fn mul_add(self, other: &Complex, add: &Complex) -> Complex { + self.clone().mul_add(other.clone(), add.clone()) + } +} forward_all_binop!(impl Div, div); // (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 Div> for Complex { - type Output = Complex; + type Output = Self; #[inline] - fn div(self, other: Complex) -> Complex { + fn div(self, other: Self) -> Self::Output { let norm_sqr = other.norm_sqr(); let re = self.re.clone() * other.re.clone() + self.im.clone() * other.im.clone(); let im = self.im * other.re - self.re * other.im; - Complex::new(re / norm_sqr.clone(), im / norm_sqr) + Self::Output::new(re / norm_sqr.clone(), im / norm_sqr) } } @@ -560,15 +740,15 @@ // Attempts to identify the gaussian integer whose product with `modulus` // is closest to `self`. impl Rem> for Complex { - type Output = Complex; + type Output = Self; #[inline] - fn rem(self, modulus: Complex) -> Self { + fn rem(self, modulus: Self) -> Self::Output { let Complex { re, im } = self.clone() / modulus.clone(); // This is the gaussian integer corresponding to the true ratio // rounded towards zero. let (re0, im0) = (re.clone() - re % T::one(), im.clone() - im % T::one()); - self - modulus * Complex::new(re0, im0) + self - modulus * Self::Output::new(re0, im0) } } @@ -577,38 +757,60 @@ mod opassign { use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; - use traits::NumAssign; + use traits::{MulAddAssign, NumAssign}; use Complex; impl AddAssign for Complex { - fn add_assign(&mut self, other: Complex) { + fn add_assign(&mut self, other: Self) { self.re += other.re; self.im += other.im; } } impl SubAssign for Complex { - fn sub_assign(&mut self, other: Complex) { + fn sub_assign(&mut self, other: Self) { self.re -= other.re; self.im -= other.im; } } impl MulAssign for Complex { - fn mul_assign(&mut self, other: Complex) { + fn mul_assign(&mut self, other: Self) { *self = self.clone() * other; } } + // (a + i b) * (c + i d) + (e + i f) == ((a*c + e) - b*d) + i (b*c + (a*d + f)) + impl MulAddAssign for Complex { + fn mul_add_assign(&mut self, other: Complex, add: Complex) { + let a = self.re.clone(); + + self.re.mul_add_assign(other.re.clone(), add.re); // (a*c + e) + self.re -= self.im.clone() * other.im.clone(); // ((a*c + e) - b*d) + + let mut adf = a; + adf.mul_add_assign(other.im, add.im); // (a*d + f) + self.im.mul_add_assign(other.re, adf); // (b*c + (a*d + f)) + } + } + + impl<'a, 'b, T: Clone + NumAssign + MulAddAssign> MulAddAssign<&'a Complex, &'b Complex> + for Complex + { + fn mul_add_assign(&mut self, other: &Complex, add: &Complex) { + self.mul_add_assign(other.clone(), add.clone()); + } + } + impl DivAssign for Complex { - fn div_assign(&mut self, other: Complex) { + fn div_assign(&mut self, other: Self) { *self = self.clone() / other; } } impl RemAssign for Complex { - fn rem_assign(&mut self, other: Complex) { + fn rem_assign(&mut self, other: Self) { *self = self.clone() % other; } } @@ -649,7 +851,7 @@ (impl $imp:ident, $method:ident) => { impl<'a, T: Clone + NumAssign> $imp<&'a Complex> for Complex { #[inline] - fn $method(&mut self, other: &Complex) { + fn $method(&mut self, other: &Self) { self.$method(other.clone()) } } @@ -669,7 +871,7 @@ impl<'a, T: Clone + NumAssign> RemAssign<&'a Complex> for Complex { #[inline] - fn rem_assign(&mut self, other: &Complex) { + fn rem_assign(&mut self, other: &Self) { self.rem_assign(other.clone()) } } @@ -682,11 +884,11 @@ } impl> Neg for Complex { - type Output = Complex; + type Output = Self; #[inline] - fn neg(self) -> Complex { - Complex::new(-self.re, -self.im) + fn neg(self) -> Self::Output { + Self::Output::new(-self.re, -self.im) } } @@ -694,16 +896,16 @@ type Output = Complex; #[inline] - fn neg(self) -> Complex { + fn neg(self) -> Self::Output { -self.clone() } } impl> Inv for Complex { - type Output = Complex; + type Output = Self; #[inline] - fn inv(self) -> Complex { + fn inv(self) -> Self::Output { (&self).inv() } } @@ -712,7 +914,7 @@ type Output = Complex; #[inline] - fn inv(self) -> Complex { + fn inv(self) -> Self::Output { self.inv() } } @@ -723,7 +925,7 @@ type Output = Complex; #[inline] - fn $method(self, other: &T) -> Complex { + fn $method(self, other: &T) -> Self::Output { self.$method(other.clone()) } } @@ -731,7 +933,7 @@ type Output = Complex; #[inline] - fn $method(self, other: T) -> Complex { + fn $method(self, other: T) -> Self::Output { self.clone().$method(other) } } @@ -739,7 +941,7 @@ type Output = Complex; #[inline] - fn $method(self, other: &T) -> Complex { + fn $method(self, other: &T) -> Self::Output { self.clone().$method(other.clone()) } } @@ -782,8 +984,8 @@ type Output = Complex<$real>; #[inline] - fn add(self, other: Complex<$real>) -> Complex<$real> { - Complex::new(self + other.re, other.im) + fn add(self, other: Complex<$real>) -> Self::Output { + Self::Output::new(self + other.re, other.im) } } @@ -791,8 +993,8 @@ type Output = Complex<$real>; #[inline] - fn sub(self, other: Complex<$real>) -> Complex<$real> { - Complex::new(self - other.re, $real::zero() - other.im) + fn sub(self, other: Complex<$real>) -> Self::Output { + Self::Output::new(self - other.re, $real::zero() - other.im) } } @@ -800,8 +1002,8 @@ type Output = Complex<$real>; #[inline] - fn mul(self, other: Complex<$real>) -> Complex<$real> { - Complex::new(self * other.re, self * other.im) + fn mul(self, other: Complex<$real>) -> Self::Output { + Self::Output::new(self * other.re, self * other.im) } } @@ -809,11 +1011,11 @@ type Output = Complex<$real>; #[inline] - fn div(self, other: Complex<$real>) -> Complex<$real> { + fn div(self, other: Complex<$real>) -> Self::Output { // a / (c + i d) == [a * (c - i d)] / (c*c + d*d) let norm_sqr = other.norm_sqr(); - Complex::new(self * other.re / norm_sqr.clone(), - $real::zero() - self * other.im / norm_sqr) + Self::Output::new(self * other.re / norm_sqr.clone(), + $real::zero() - self * other.im / norm_sqr) } } @@ -821,8 +1023,8 @@ type Output = Complex<$real>; #[inline] - fn rem(self, other: Complex<$real>) -> Complex<$real> { - Complex::new(self, Self::zero()) % other + fn rem(self, other: Complex<$real>) -> Self::Output { + Self::Output::new(self, Self::zero()) % other } } )* @@ -833,8 +1035,8 @@ type Output = Complex; #[inline] - fn add(self, other: T) -> Complex { - Complex::new(self.re + other, self.im) + fn add(self, other: T) -> Self::Output { + Self::Output::new(self.re + other, self.im) } } @@ -842,8 +1044,8 @@ type Output = Complex; #[inline] - fn sub(self, other: T) -> Complex { - Complex::new(self.re - other, self.im) + fn sub(self, other: T) -> Self::Output { + Self::Output::new(self.re - other, self.im) } } @@ -851,17 +1053,17 @@ type Output = Complex; #[inline] - fn mul(self, other: T) -> Complex { - Complex::new(self.re * other.clone(), self.im * other) + fn mul(self, other: T) -> Self::Output { + Self::Output::new(self.re * other.clone(), self.im * other) } } impl Div for Complex { - type Output = Complex; + type Output = Self; #[inline] - fn div(self, other: T) -> Complex { - Complex::new(self.re / other.clone(), self.im / other) + fn div(self, other: T) -> Self::Output { + Self::Output::new(self.re / other.clone(), self.im / other) } } @@ -869,8 +1071,8 @@ type Output = Complex; #[inline] - fn rem(self, other: T) -> Complex { - Complex::new(self.re % other.clone(), self.im % other) + fn rem(self, other: T) -> Self::Output { + Self::Output::new(self.re % other.clone(), self.im % other) } } @@ -882,26 +1084,38 @@ /* constants */ impl Zero for Complex { #[inline] - fn zero() -> Complex { - Complex::new(Zero::zero(), Zero::zero()) + fn zero() -> Self { + Self::new(Zero::zero(), Zero::zero()) } #[inline] fn is_zero(&self) -> bool { self.re.is_zero() && self.im.is_zero() } + + #[inline] + fn set_zero(&mut self) { + self.re.set_zero(); + self.im.set_zero(); + } } impl One for Complex { #[inline] - fn one() -> Complex { - Complex::new(One::one(), Zero::zero()) + fn one() -> Self { + Self::new(One::one(), Zero::zero()) } #[inline] fn is_one(&self) -> bool { self.re.is_one() && self.im.is_zero() } + + #[inline] + fn set_one(&mut self) { + self.re.set_one(); + self.im.set_zero(); + } } macro_rules! write_complex { @@ -1058,6 +1272,7 @@ } } +#[allow(deprecated)] // `trim_left_matches` and `trim_right_matches` since 1.33 fn from_str_generic(s: &str, from: F) -> Result, ParseComplexError> where F: Fn(&str) -> Result, @@ -1238,7 +1453,7 @@ D: serde::Deserializer<'de>, { let (re, im) = try!(serde::Deserialize::deserialize(deserializer)); - Ok(Complex::new(re, im)) + Ok(Self::new(re, im)) } } @@ -1369,10 +1584,40 @@ assert!(_0_0i.inv().is_nan()); } + #[test] + fn test_l1_norm() { + assert_eq!(_0_0i.l1_norm(), 0.0); + assert_eq!(_1_0i.l1_norm(), 1.0); + assert_eq!(_1_1i.l1_norm(), 2.0); + assert_eq!(_0_1i.l1_norm(), 1.0); + assert_eq!(_neg1_1i.l1_norm(), 2.0); + assert_eq!(_05_05i.l1_norm(), 1.0); + assert_eq!(_4_2i.l1_norm(), 6.0); + } + + #[test] + fn test_pow() { + for c in all_consts.iter() { + assert_eq!(c.powi(0), _1_0i); + let mut pos = _1_0i; + let mut neg = _1_0i; + for i in 1i32..20 { + pos *= c; + assert_eq!(pos, c.powi(i)); + if c.is_zero() { + assert!(c.powi(-i).is_nan()); + } else { + neg /= c; + assert_eq!(neg, c.powi(-i)); + } + } + } + } + #[cfg(feature = "std")] mod float { use super::*; - use traits::Float; + use traits::{Float, Pow}; #[test] #[cfg_attr(target_arch = "x86", ignore)] @@ -1417,7 +1662,11 @@ fn close_to_tol(a: Complex64, b: Complex64, tol: f64) -> bool { // returns true if a and b are reasonably close - (a == b) || (a - b).norm() < tol + let close = (a == b) || (a - b).norm() < tol; + if !close { + println!("{:?} != {:?}", a, b); + } + close } #[test] @@ -1474,9 +1723,11 @@ #[test] fn test_powf() { - let c = Complex::new(2.0, -1.0); - let r = c.powf(3.5); - assert!(close_to_tol(r, Complex::new(-0.8684746, -16.695934), 1e-5)); + let c = Complex64::new(2.0, -1.0); + let expected = Complex64::new(-0.8684746, -16.695934); + assert!(close_to_tol(c.powf(3.5), expected, 1e-5)); + assert!(close_to_tol(Pow::pow(c, 3.5_f64), expected, 1e-5)); + assert!(close_to_tol(Pow::pow(c, 3.5_f32), expected, 1e-5)); } #[test] @@ -1513,8 +1764,8 @@ assert!(close(c.conj().sqrt(), c.sqrt().conj())); // for this branch, -pi/2 <= arg(sqrt(z)) <= pi/2 assert!( - -f64::consts::PI / 2.0 <= c.sqrt().arg() - && c.sqrt().arg() <= f64::consts::PI / 2.0 + -f64::consts::FRAC_PI_2 <= c.sqrt().arg() + && c.sqrt().arg() <= f64::consts::FRAC_PI_2 ); // sqrt(z) * sqrt(z) = z assert!(close(c.sqrt() * c.sqrt(), c)); @@ -1522,6 +1773,102 @@ } #[test] + fn test_sqrt_real() { + for n in (0..100).map(f64::from) { + // √(n² + 0i) = n + 0i + let n2 = n * n; + assert_eq!(Complex64::new(n2, 0.0).sqrt(), Complex64::new(n, 0.0)); + // √(-n² + 0i) = 0 + ni + assert_eq!(Complex64::new(-n2, 0.0).sqrt(), Complex64::new(0.0, n)); + // √(-n² - 0i) = 0 - ni + assert_eq!(Complex64::new(-n2, -0.0).sqrt(), Complex64::new(0.0, -n)); + } + } + + #[test] + fn test_sqrt_imag() { + for n in (0..100).map(f64::from) { + // √(0 + n²i) = n e^(iπ/4) + let n2 = n * n; + assert!(close( + Complex64::new(0.0, n2).sqrt(), + Complex64::from_polar(&n, &(f64::consts::FRAC_PI_4)) + )); + // √(0 - n²i) = n e^(-iπ/4) + assert!(close( + Complex64::new(0.0, -n2).sqrt(), + Complex64::from_polar(&n, &(-f64::consts::FRAC_PI_4)) + )); + } + } + + #[test] + fn test_cbrt() { + assert!(close(_0_0i.cbrt(), _0_0i)); + assert!(close(_1_0i.cbrt(), _1_0i)); + assert!(close( + Complex::new(-1.0, 0.0).cbrt(), + Complex::new(0.5, 0.75.sqrt()) + )); + assert!(close( + Complex::new(-1.0, -0.0).cbrt(), + Complex::new(0.5, -0.75.sqrt()) + )); + assert!(close(_0_1i.cbrt(), Complex::new(0.75.sqrt(), 0.5))); + assert!(close(_0_1i.conj().cbrt(), Complex::new(0.75.sqrt(), -0.5))); + for &c in all_consts.iter() { + // cbrt(conj(z() = conj(cbrt(z)) + assert!(close(c.conj().cbrt(), c.cbrt().conj())); + // for this branch, -pi/3 <= arg(cbrt(z)) <= pi/3 + assert!( + -f64::consts::FRAC_PI_3 <= c.cbrt().arg() + && c.cbrt().arg() <= f64::consts::FRAC_PI_3 + ); + // cbrt(z) * cbrt(z) cbrt(z) = z + assert!(close(c.cbrt() * c.cbrt() * c.cbrt(), c)); + } + } + + #[test] + fn test_cbrt_real() { + for n in (0..100).map(f64::from) { + // ∛(n³ + 0i) = n + 0i + let n3 = n * n * n; + assert!(close( + Complex64::new(n3, 0.0).cbrt(), + Complex64::new(n, 0.0) + )); + // ∛(-n³ + 0i) = n e^(iπ/3) + assert!(close( + Complex64::new(-n3, 0.0).cbrt(), + Complex64::from_polar(&n, &(f64::consts::FRAC_PI_3)) + )); + // ∛(-n³ - 0i) = n e^(-iπ/3) + assert!(close( + Complex64::new(-n3, -0.0).cbrt(), + Complex64::from_polar(&n, &(-f64::consts::FRAC_PI_3)) + )); + } + } + + #[test] + fn test_cbrt_imag() { + for n in (0..100).map(f64::from) { + // ∛(0 + n³i) = n e^(iπ/6) + let n3 = n * n * n; + assert!(close( + Complex64::new(0.0, n3).cbrt(), + Complex64::from_polar(&n, &(f64::consts::FRAC_PI_6)) + )); + // ∛(0 - n³i) = n e^(-iπ/6) + assert!(close( + Complex64::new(0.0, -n3).cbrt(), + Complex64::from_polar(&n, &(-f64::consts::FRAC_PI_6)) + )); + } + } + + #[test] fn test_sin() { assert!(close(_0_0i.sin(), _0_0i)); assert!(close(_1_0i.scale(f64::consts::PI * 2.0).sin(), _0_0i)); @@ -1885,7 +2232,7 @@ mod complex_arithmetic { use super::{_05_05i, _0_0i, _0_1i, _1_0i, _1_1i, _4_2i, _neg1_1i, all_consts}; - use traits::Zero; + use traits::{MulAdd, MulAddAssign, Zero}; #[test] fn test_add() { @@ -1927,6 +2274,65 @@ } #[test] + #[cfg(feature = "std")] + fn test_mul_add_float() { + assert_eq!(_05_05i.mul_add(_05_05i, _0_0i), _05_05i * _05_05i + _0_0i); + assert_eq!(_05_05i * _05_05i + _0_0i, _05_05i.mul_add(_05_05i, _0_0i)); + assert_eq!(_0_1i.mul_add(_0_1i, _0_1i), _neg1_1i); + assert_eq!(_1_0i.mul_add(_1_0i, _1_0i), _1_0i * _1_0i + _1_0i); + assert_eq!(_1_0i * _1_0i + _1_0i, _1_0i.mul_add(_1_0i, _1_0i)); + + let mut x = _1_0i; + x.mul_add_assign(_1_0i, _1_0i); + assert_eq!(x, _1_0i * _1_0i + _1_0i); + + for &a in &all_consts { + for &b in &all_consts { + for &c in &all_consts { + let abc = a * b + c; + assert_eq!(a.mul_add(b, c), abc); + let mut x = a; + x.mul_add_assign(b, c); + assert_eq!(x, abc); + } + } + } + } + + #[test] + fn test_mul_add() { + use super::Complex; + const _0_0i: Complex = Complex { re: 0, im: 0 }; + const _1_0i: Complex = Complex { re: 1, im: 0 }; + const _1_1i: Complex = Complex { re: 1, im: 1 }; + const _0_1i: Complex = Complex { re: 0, im: 1 }; + const _neg1_1i: Complex = Complex { re: -1, im: 1 }; + const all_consts: [Complex; 5] = [_0_0i, _1_0i, _1_1i, _0_1i, _neg1_1i]; + + assert_eq!(_1_0i.mul_add(_1_0i, _0_0i), _1_0i * _1_0i + _0_0i); + assert_eq!(_1_0i * _1_0i + _0_0i, _1_0i.mul_add(_1_0i, _0_0i)); + assert_eq!(_0_1i.mul_add(_0_1i, _0_1i), _neg1_1i); + assert_eq!(_1_0i.mul_add(_1_0i, _1_0i), _1_0i * _1_0i + _1_0i); + assert_eq!(_1_0i * _1_0i + _1_0i, _1_0i.mul_add(_1_0i, _1_0i)); + + let mut x = _1_0i; + x.mul_add_assign(_1_0i, _1_0i); + assert_eq!(x, _1_0i * _1_0i + _1_0i); + + for &a in &all_consts { + for &b in &all_consts { + for &c in &all_consts { + let abc = a * b + c; + assert_eq!(a.mul_add(b, c), abc); + let mut x = a; + x.mul_add_assign(b, c); + assert_eq!(x, abc); + } + } + } + } + + #[test] fn test_div() { test_op!(_neg1_1i / _0_1i, _1_1i); for &c in all_consts.iter() { @@ -2207,4 +2613,30 @@ assert_eq!(v.iter().product::(), _0_1i); assert_eq!(v.into_iter().product::(), _0_1i); } + + #[test] + fn test_zero() { + let zero = Complex64::zero(); + assert!(zero.is_zero()); + + let mut c = Complex::new(1.23, 4.56); + assert!(!c.is_zero()); + assert_eq!(&c + &zero, c); + + c.set_zero(); + assert!(c.is_zero()); + } + + #[test] + fn test_one() { + let one = Complex64::one(); + assert!(one.is_one()); + + let mut c = Complex::new(1.23, 4.56); + assert!(!c.is_one()); + assert_eq!(&c * &one, c); + + c.set_one(); + assert!(c.is_one()); + } } diff -Nru rust-num-complex-0.2.1/src/pow.rs rust-num-complex-0.2.3/src/pow.rs --- rust-num-complex-0.2.1/src/pow.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-num-complex-0.2.3/src/pow.rs 2019-06-06 18:31:51.000000000 +0000 @@ -0,0 +1,187 @@ +use super::Complex; + +use core::ops::Neg; +#[cfg(feature = "std")] +use traits::Float; +use traits::{Num, One, Pow}; + +macro_rules! pow_impl { + ($U:ty, $S:ty) => { + impl<'a, T: Clone + Num> Pow<$U> for &'a Complex { + type Output = Complex; + + #[inline] + fn pow(self, mut exp: $U) -> Self::Output { + if exp == 0 { + return Complex::one(); + } + let mut base = self.clone(); + + while exp & 1 == 0 { + base = base.clone() * base; + exp >>= 1; + } + + if exp == 1 { + return base; + } + + let mut acc = base.clone(); + while exp > 1 { + exp >>= 1; + base = base.clone() * base; + if exp & 1 == 1 { + acc = acc * base.clone(); + } + } + acc + } + } + + impl<'a, 'b, T: Clone + Num> Pow<&'b $U> for &'a Complex { + type Output = Complex; + + #[inline] + fn pow(self, exp: &$U) -> Self::Output { + self.pow(*exp) + } + } + + impl<'a, T: Clone + Num + Neg> Pow<$S> for &'a Complex { + type Output = Complex; + + #[inline] + fn pow(self, exp: $S) -> Self::Output { + if exp < 0 { + Pow::pow(&self.inv(), exp.wrapping_neg() as $U) + } else { + Pow::pow(self, exp as $U) + } + } + } + + impl<'a, 'b, T: Clone + Num + Neg> Pow<&'b $S> for &'a Complex { + type Output = Complex; + + #[inline] + fn pow(self, exp: &$S) -> Self::Output { + self.pow(*exp) + } + } + }; +} + +pow_impl!(u8, i8); +pow_impl!(u16, i16); +pow_impl!(u32, i32); +pow_impl!(u64, i64); +pow_impl!(usize, isize); +#[cfg(has_i128)] +pow_impl!(u128, i128); + +// Note: we can't add `impl Pow for Complex` because new blanket impls are a +// breaking change. Someone could already have their own `F` and `impl Pow for Complex` +// which would conflict. We can't even do this in a new semantic version, because we have to +// gate it on the "std" feature, and features can't add breaking changes either. + +macro_rules! powf_impl { + ($F:ty) => { + #[cfg(feature = "std")] + impl<'a, T: Float> Pow<$F> for &'a Complex + where + $F: Into, + { + type Output = Complex; + + #[inline] + fn pow(self, exp: $F) -> Self::Output { + self.powf(exp.into()) + } + } + + #[cfg(feature = "std")] + impl<'a, 'b, T: Float> Pow<&'b $F> for &'a Complex + where + $F: Into, + { + type Output = Complex; + + #[inline] + fn pow(self, &exp: &$F) -> Self::Output { + self.powf(exp.into()) + } + } + + #[cfg(feature = "std")] + impl Pow<$F> for Complex + where + $F: Into, + { + type Output = Complex; + + #[inline] + fn pow(self, exp: $F) -> Self::Output { + self.powf(exp.into()) + } + } + + #[cfg(feature = "std")] + impl<'b, T: Float> Pow<&'b $F> for Complex + where + $F: Into, + { + type Output = Complex; + + #[inline] + fn pow(self, &exp: &$F) -> Self::Output { + self.powf(exp.into()) + } + } + }; +} + +powf_impl!(f32); +powf_impl!(f64); + +// These blanket impls are OK, because both the target type and the trait parameter would be +// foreign to anyone else trying to implement something that would overlap, raising E0117. + +#[cfg(feature = "std")] +impl<'a, T: Float> Pow> for &'a Complex { + type Output = Complex; + + #[inline] + fn pow(self, exp: Complex) -> Self::Output { + self.powc(exp) + } +} + +#[cfg(feature = "std")] +impl<'a, 'b, T: Float> Pow<&'b Complex> for &'a Complex { + type Output = Complex; + + #[inline] + fn pow(self, &exp: &'b Complex) -> Self::Output { + self.powc(exp) + } +} + +#[cfg(feature = "std")] +impl Pow> for Complex { + type Output = Complex; + + #[inline] + fn pow(self, exp: Complex) -> Self::Output { + self.powc(exp) + } +} + +#[cfg(feature = "std")] +impl<'b, T: Float> Pow<&'b Complex> for Complex { + type Output = Complex; + + #[inline] + fn pow(self, &exp: &'b Complex) -> Self::Output { + self.powc(exp) + } +} diff -Nru rust-num-complex-0.2.1/.travis.yml rust-num-complex-0.2.3/.travis.yml --- rust-num-complex-0.2.1/.travis.yml 2018-05-22 01:10:39.000000000 +0000 +++ rust-num-complex-0.2.3/.travis.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -language: rust -rust: - - 1.15.0 - - 1.22.0 - - 1.26.0 - - stable - - beta - - nightly -sudo: false -script: - - cargo build --verbose - - ./ci/test_full.sh -notifications: - email: - on_success: never -branches: - only: - - master - - next - - staging - - trying