simba-0.8.1/.cargo_vcs_info.json0000644000000001360000000000100121350ustar { "git": { "sha1": "aa2b0d6d8031bfbb48e40f2c1a9fabccce7efbfa" }, "path_in_vcs": "" }simba-0.8.1/.github/FUNDING.yml000064400000000000000000000013260072674642500141340ustar 00000000000000# These are supported funding model platforms github: [ "dimforge" ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] simba-0.8.1/.github/Xargo.toml000064400000000000000000000001650072674642500142740ustar 00000000000000[target.x86_64-unknown-linux-gnu.dependencies] alloc = {} [target.thumbv7em-none-eabihf.dependencies] alloc = {}simba-0.8.1/.github/workflows/simba-ci-build.yml000064400000000000000000000042730072674642500176640ustar 00000000000000name: Simba CI build on: push: branches: [ master ] pull_request: branches: [ master ] env: CARGO_TERM_COLOR: always jobs: check-fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Check formatting run: cargo fmt -- --check build-native: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build --no-default-feature run: cargo build --no-default-features; - name: Build libm only run: cargo build --no-default-features --features libm; - name: Build (default features) run: cargo build; - name: Build all features except cuda,libm run: cargo build --features wide,rkyv-serialize,serde_serialize,partial_fixed_point_support; build-wasm: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: rustup target add wasm32-unknown-unknown - name: build run: cargo build --verbose --target wasm32-unknown-unknown; build-no-std: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install latest nightly uses: actions-rs/toolchain@v1 with: toolchain: nightly override: true - name: install xargo run: cp .github/Xargo.toml .; rustup component add rust-src; cargo install -f xargo; - name: build x86_64-unknown-linux-gnu run: xargo build --verbose --no-default-features --target=x86_64-unknown-linux-gnu; - name: build x86_64-unknown-linux-gnu --features libm run: xargo build --verbose --no-default-features --features libm --target=x86_64-unknown-linux-gnu; build-cuda: runs-on: ubuntu-latest steps: - uses: Jimver/cuda-toolkit@v0.2.8 - name: Install nightly-2021-12-04 uses: actions-rs/toolchain@v1 with: toolchain: nightly-2021-12-04 override: true - uses: actions/checkout@v2 - run: rustup target add nvptx64-nvidia-cuda - run: cargo build --no-default-features --features cuda - run: cargo build --no-default-features --features cuda --target=nvptx64-nvidia-cuda env: CUDA_ARCH: "350"simba-0.8.1/.gitignore000064400000000000000000000001200072674642500127360ustar 00000000000000*.swp *.html doc lib TODO target/ Cargo.lock *.orig *.swo site/ .vscode/ .idea/ simba-0.8.1/CHANGELOG000064400000000000000000000102020072674642500121620ustar 00000000000000## Release v0.8.1 (04 Apr. 2023) - Add implementation of `rkyv` serialization/deserialization to the `Wide*` wrapper types. ## Release v0.8.0 (14 Jan. 2023) - When `rkyv` support is enabled, the archived version of `AutoSimd` and `AutoBoolSimd` are themselves. The `ArchivedAutoSimd` and `ArchivedAutoBoolSimd` types are no longer generated. ## Release v0.7.3 (30 Oct. 2022) - Cuda: implement DeviceCopy for AutoSimd - Implement `Distribution` (for random number generation) for SIMD types. ## Release v0.7.2 (30 July 2022) - Add the optional types `WideF64x4` and `WideBoolF64x4` for 4-lanes SIMD 64-bits float operations based on the `wide` crate. - Add the `rkyv-serialize` feature to enable `rkyv` serialization/deserialization of the `AutoSimd` types. - Fix build with only the `libm` feature enabled. ## Release v0.7.1 (27 Jan. 2022) - Add the optional types `WideF32x8` and `WideBoolF32x8` for 8-lanes SIMD float operations. ## Release v0.7.0 (02 Jan. 2022) - Remove the `Bounded` requirement from `RealField`. Replace it by methods returning `Option` so that they can still be implemented by unbounded types (by returning `None`). - The `ComplexField` trait derives from `FromPrimitive` again. We can actually keep this because all its methods return `Option`, meaning that it could be implemented by any type. - Add the `cuda` cargo feature that enables support for [Rust-Cuda](https://github.com/Rust-GPU/Rust-CUDA) by generating the relevant cuda intrinsics for float operations when running on a cuda kernel. ## Release v0.6.0 - Replace all the `Copy` trait bounds by `Clone`, allowing more types to fulfill the requirements of `ComplexField` and `RealField`. - The `ComplexField` trait no longer derive from `FromPrimitive`. ## Release v0.5.0 - Update the `num-complex` 0.4 dependency. ## Release v0.4.0 - Update the `rand` 0.8 dependency. - Add `SimdComplexField::simd_horizontal_sum` and `SimdComplexField::simd_horizontal_product` for computing the sum or product of all the lanes of a single SIMD value. ## Release v0.3.0 This release includes a change that is not technically a breaking change because it won't cause existing code to not compile anymore. However it is an important semantic change to justify a major release: - The arguments of `copysign` are now swapped: `a.copysign(b)` will copy the sign of `b` into `a` (the previous behavior copied the sign of `a` into `b`). This change was needed to align with the argument order used by the standard library, thus avoiding hard-to-debug errors due to mismatching conventions. - The arguments of `simd_copysign` are now swapped too. This release also update the `num-complex` dependency to its latest version 0.3. ## Release v0.2.4 - Make `Fixed.to_bits` and `Fixed::from_bits` const-fn. - Make our `Fixed` wrapper type `repr(transparent)`. ## Release v0.2.3 - Add `.to_bits` to our fixed-point number newtype. - Add `::from_bits` to our fixed-point number newtype. - Add `Ord` implementation for our fixed-point number newtype. - Add `Mul, Div, Rem, MulAssign, DivAssign, RemAssign` implementation for our fixed-point number newtype, where `int` is the underlying integer used by the fixed-point number. ## Release v0.2.2 - Fix a compilation error when building simba with out `std` and with the `libm` feature. ## Release v0.2.1 - Add SIMD types named, e.g. `AutoF32x4`, based on auto-vectorization only. They don't call any SIMD intrinsics and let the compiler do the vectorization, if it can. ## Release v0.2.0 - The use of `libm` in `#[no-std]` environments is now opt-in by enabling the `libm` feature. - If the `libm` is not enabled in a `#[no-std]` environment, then no `RealField` or `ComplexField` impls will be provided for floats. - Add the `libm_force` cargo feature that forces the use of `libm`, even when we don't target `no-std`. - Add `copysign` to copy the sign from one number to another. - Add `simd_horizontal_min`, `simd_horizontal_max` to compute the min/max among the lanes of a single SIMD number. - Wrap all SIMD bools from `packed_simd` into our own `Simd<_>` newtype.simba-0.8.1/Cargo.toml0000644000000042650000000000100101420ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # 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. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "simba" version = "0.8.1" authors = ["sebcrozet "] description = "SIMD algebra for Rust" documentation = "https://docs.rs/simba" readme = "README.md" keywords = [ "algebra", "simd", "math", ] categories = [ "science", "mathematics", "wasm", "no-std", ] license = "Apache-2.0" repository = "https://github.com/dimforge/simba" [package.metadata.docs.rs] all-features = true [dependencies.approx] version = "0.5" default-features = false [dependencies.cordic] version = "0.1" optional = true [dependencies.cuda_std] version = "0.2" optional = true [dependencies.cust_core] version = "0.1" optional = true [dependencies.decimal] version = "2.0" optional = true default-features = false [dependencies.fixed] version = "1" optional = true [dependencies.libm_force] version = "0.2" optional = true package = "libm" [dependencies.num-complex] version = "0.4" default-features = false [dependencies.num-traits] version = "0.2.11" default-features = false [dependencies.packed_simd] version = "0.3" features = ["into_bits"] optional = true package = "packed_simd_2" [dependencies.paste] version = "1.0" [dependencies.rand] version = "0.8" optional = true [dependencies.rkyv] version = "0.7" optional = true [dependencies.serde] version = "1" optional = true default-features = false [dependencies.wide] version = "0.7" optional = true default-features = false [features] cuda = [ "cuda_std", "cust_core", ] default = ["std"] libm = ["num-traits/libm"] partial_fixed_point_support = [ "fixed", "cordic", ] rkyv-serialize = ["rkyv"] serde_serialize = [ "serde", "fixed/serde", ] std = ["wide/std"] [badges.maintenance] status = "actively-developed" simba-0.8.1/Cargo.toml.orig000064400000000000000000000032350072674642500136470ustar 00000000000000[package] name = "simba" version = "0.8.1" authors = ["sebcrozet "] description = "SIMD algebra for Rust" keywords = ["algebra", "simd", "math"] readme = "README.md" repository = "https://github.com/dimforge/simba" categories = [ "science", "mathematics", "wasm", "no-std" ] documentation = "https://docs.rs/simba" license = "Apache-2.0" edition = "2018" [badges] maintenance = { status = "actively-developed" } [features] default = [ "std" ] std = ["wide/std"] partial_fixed_point_support = [ "fixed", "cordic" ] serde_serialize = [ "serde", "fixed/serde" ] rkyv-serialize = [ "rkyv" ] libm = [ "num-traits/libm" ] cuda = [ "cuda_std", "cust_core" ] [dependencies] num-traits = { version = "0.2.11", default-features = false } approx = { version = "0.5", default-features = false } decimal = { version = "2.0", default-features = false, optional = true } num-complex = { version = "0.4", default-features = false } packed_simd = { package = "packed_simd_2", version = "0.3", features = [ "into_bits" ], optional = true } wide = { version = "0.7", default-features = false, optional = true } fixed = { version = "1", optional = true } cordic = { version = "0.1", optional = true } paste = "1.0" rand = { version = "0.8", optional = true } serde = { version = "1", default-features = false, optional = true } rkyv = { version = "0.7", optional = true } libm_force = { package = "libm", version = "0.2", optional = true } cuda_std = { version = "0.2", optional = true } cust_core = { version = "0.1", optional = true } [package.metadata.docs.rs] all-features = true simba-0.8.1/LICENSE000064400000000000000000000261230072674642500117660ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2020 Sébastien Crozet Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. simba-0.8.1/README.md000064400000000000000000000026540072674642500122430ustar 00000000000000# Simba - SIMD algebra for Rust

simba logo

Build status crates.io

Documentation | Forum

-----

SIMD algebra for the Rust programming language.

----- ## Acknowledgements Simba is supported by our platinum sponsors:

And our gold sponsors:

simba-0.8.1/rustfmt.toml000064400000000000000000000001170072674642500133550ustar 00000000000000# unstable_features = true # indent_style = "Block" # where_single_line = true simba-0.8.1/src/lib.rs000064400000000000000000000040310072674642500126560ustar 00000000000000/*! __Simba__ is a crate defining a set of trait for writing code that can be generic with regard to the number of lanes of the numeric input value. Those traits are implemented by `f32`, `u32`, `i16`, `bool` as well as SIMD types like `f32x4, u32x8, i16x2`, etc. One example of use-case applied by the [nalgebra crate](https://nalgebra.org) is to define generic methods like vector normalization that will work for `Vector3` as well as `Vector3`. This makes it easier leverage the power of [SIMD Array-of-Struct-of-Array (AoSoA)](https://www.rustsim.org/blog/2020/03/23/simd-aosoa-in-nalgebra/) with less code duplication. ## Cargo features Two cargo features can be optionally enabled: - With the __`packed_simd`__ feature enabled, the `simba::simd` module will export several SIMD types like `f32x2`, `f64x4`, `i32i8`, `u16i16`, etc. There types are wrappers around the SIMD types from the [__packed_simd__ crate](https://docs.rs/packed_simd). This requires a nightly compiler. - With the __`wide`__ feature enabled, the `simba::simd` module will export the `WideF32x4` and `WideBoolF32x4` types. The types are wrappers around the `wide::f32x4` type from the [__wide__ crate](https://docs.rs/wide). This will work with both a stable or nightly compiler. If none of those features are enabled, __simba__ will still define all the scalar and SIMD traits. However, the SIMD traits won't be implemented for any SIMD types. Therefore it is recommended to: - Use the `packed_simd` feature if you want more features, and can afford to use a nightly compiler. - Use the `wide` feature if you only need 4-lanes 32-bits floats, and can't afford to use a nightly compiler. */ #![deny(non_camel_case_types)] #![deny(unused_parens)] #![deny(non_upper_case_globals)] #![deny(unused_results)] #![deny(missing_docs)] // FIXME: should be denied #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] extern crate core as std; extern crate num_traits as num; #[macro_use] pub mod scalar; pub mod simd; simba-0.8.1/src/scalar/complex.rs000064400000000000000000001132640072674642500150350ustar 00000000000000use num::{One, Signed, Zero}; use num_traits::FromPrimitive; use std::any::Any; use std::fmt::{Debug, Display}; use std::ops::Neg; use std::{f32, f64}; use crate::scalar::{Field, RealField, SubsetOf, SupersetOf}; #[cfg(all( any(target_arch = "nvptx", target_arch = "nvptx64"), not(feature = "std"), not(feature = "libm_force"), feature = "cuda" ))] use cuda_std::GpuFloat; #[cfg(all(not(feature = "std"), not(feature = "libm_force"), feature = "libm"))] use num::Float; //#[cfg(feature = "decimal")] //use decimal::d128; macro_rules! complex_trait_methods( ($RealField: ident $(, $prefix: ident)*) => { paste::item! { /// Builds a pure-real complex number from the given value. fn [](re: Self::$RealField) -> Self; /// The real part of this complex number. fn [<$($prefix)* real>](self) -> Self::$RealField; /// The imaginary part of this complex number. fn [<$($prefix)* imaginary>](self) -> Self::$RealField; /// The modulus of this complex number. fn [<$($prefix)* modulus>](self) -> Self::$RealField; /// The squared modulus of this complex number. fn [<$($prefix)* modulus_squared>](self) -> Self::$RealField; /// The argument of this complex number. fn [<$($prefix)* argument>](self) -> Self::$RealField; /// The sum of the absolute value of this complex number's real and imaginary part. fn [<$($prefix)* norm1>](self) -> Self::$RealField; /// Multiplies this complex number by `factor`. fn [<$($prefix)* scale>](self, factor: Self::$RealField) -> Self; /// Divides this complex number by `factor`. fn [<$($prefix)* unscale>](self, factor: Self::$RealField) -> Self; /// The polar form of this complex number: (modulus, arg) fn [<$($prefix)* to_polar>](self) -> (Self::$RealField, Self::$RealField) { (self.clone().[<$($prefix)* modulus>](), self.[<$($prefix)* argument>]()) } /// The exponential form of this complex number: (modulus, e^{i arg}) fn [<$($prefix)* to_exp>](self) -> (Self::$RealField, Self) { let m = self.clone().[<$($prefix)* modulus>](); if !m.is_zero() { (m.clone(), self.[<$($prefix)* unscale>](m)) } else { (Self::$RealField::zero(), Self::one()) } } /// The exponential part of this complex number: `self / self.modulus()` fn [<$($prefix)* signum>](self) -> Self { self.[<$($prefix)* to_exp>]().1 } fn [<$($prefix)* floor>](self) -> Self; fn [<$($prefix)* ceil>](self) -> Self; fn [<$($prefix)* round>](self) -> Self; fn [<$($prefix)* trunc>](self) -> Self; fn [<$($prefix)* fract>](self) -> Self; fn [<$($prefix)* mul_add>](self, a: Self, b: Self) -> Self; /// The absolute value of this complex number: `self / self.signum()`. /// /// This is equivalent to `self.modulus()`. fn [<$($prefix)* abs>](self) -> Self::$RealField; /// Computes (self.conjugate() * self + other.conjugate() * other).sqrt() fn [<$($prefix)* hypot>](self, other: Self) -> Self::$RealField; fn [<$($prefix)* recip>](self) -> Self; fn [<$($prefix)* conjugate>](self) -> Self; fn [<$($prefix)* sin>](self) -> Self; fn [<$($prefix)* cos>](self) -> Self; fn [<$($prefix)* sin_cos>](self) -> (Self, Self); #[inline] fn [<$($prefix)* sinh_cosh>](self) -> (Self, Self) { (self.clone().[<$($prefix)* sinh>](), self.[<$($prefix)* cosh>]()) } fn [<$($prefix)* tan>](self) -> Self; fn [<$($prefix)* asin>](self) -> Self; fn [<$($prefix)* acos>](self) -> Self; fn [<$($prefix)* atan>](self) -> Self; fn [<$($prefix)* sinh>](self) -> Self; fn [<$($prefix)* cosh>](self) -> Self; fn [<$($prefix)* tanh>](self) -> Self; fn [<$($prefix)* asinh>](self) -> Self; fn [<$($prefix)* acosh>](self) -> Self; fn [<$($prefix)* atanh>](self) -> Self; /// Cardinal sine #[inline] fn [<$($prefix)* sinc>](self) -> Self { if self.is_zero() { Self::one() } else { self.clone().[<$($prefix)* sin>]() / self } } #[inline] fn [<$($prefix)* sinhc>](self) -> Self { if self.is_zero() { Self::one() } else { self.clone().[<$($prefix)* sinh>]() / self } } /// Cardinal cos #[inline] fn [<$($prefix)* cosc>](self) -> Self { if self.is_zero() { Self::one() } else { self.clone().[<$($prefix)* cos>]() / self } } #[inline] fn [<$($prefix)* coshc>](self) -> Self { if self.is_zero() { Self::one() } else { self.clone().[<$($prefix)* cosh>]() / self } } fn [<$($prefix)* log>](self, base: Self::$RealField) -> Self; fn [<$($prefix)* log2>](self) -> Self; fn [<$($prefix)* log10>](self) -> Self; fn [<$($prefix)* ln>](self) -> Self; fn [<$($prefix)* ln_1p>](self) -> Self; fn [<$($prefix)* sqrt>](self) -> Self; fn [<$($prefix)* exp>](self) -> Self; fn [<$($prefix)* exp2>](self) -> Self; fn [<$($prefix)* exp_m1>](self) -> Self; fn [<$($prefix)* powi>](self, n: i32) -> Self; fn [<$($prefix)* powf>](self, n: Self::$RealField) -> Self; fn [<$($prefix)* powc>](self, n: Self) -> Self; fn [<$($prefix)* cbrt>](self) -> Self; } } ); /// Trait shared by all complex fields and its subfields (like real numbers). /// /// Complex numbers are equipped with functions that are commonly used on complex numbers and reals. /// The results of those functions only have to be approximately equal to the actual theoretical values. // FIXME: SubsetOf should be removed when specialization will be supported by rustc. This will // allow a blanket impl: impl SubsetOf for T { ... } #[allow(missing_docs)] pub trait ComplexField: SubsetOf + SupersetOf + FromPrimitive + Field + Neg + Clone // + MeetSemilattice // + JoinSemilattice + Send + Sync + Any + 'static + Debug + Display { type RealField: RealField; complex_trait_methods!(RealField); fn is_finite(&self) -> bool; fn try_sqrt(self) -> Option; } #[cfg(not(feature = "libm_force"))] macro_rules! impl_complex( ($($T:ty, $M:ident, $libm: ident);*) => ($( impl ComplexField for $T { type RealField = $T; #[inline] fn from_real(re: Self::RealField) -> Self { re } #[inline] fn real(self) -> Self::RealField { self } #[inline] fn imaginary(self) -> Self::RealField { Self::zero() } #[inline] fn norm1(self) -> Self::RealField { $libm::abs(self) } #[inline] fn modulus(self) -> Self::RealField { $libm::abs(self) } #[inline] fn modulus_squared(self) -> Self::RealField { self * self } #[inline] fn argument(self) -> Self::RealField { if self >= Self::zero() { Self::zero() } else { Self::pi() } } #[inline] fn to_exp(self) -> (Self, Self) { if self >= Self::zero() { (self, Self::one()) } else { (-self, -Self::one()) } } #[inline] fn recip(self) -> Self { $M::recip(self) } #[inline] fn conjugate(self) -> Self { self } #[inline] fn scale(self, factor: Self::RealField) -> Self { self * factor } #[inline] fn unscale(self, factor: Self::RealField) -> Self { self / factor } #[inline] fn floor(self) -> Self { $libm::floor(self) } #[inline] fn ceil(self) -> Self { $libm::ceil(self) } #[inline] fn round(self) -> Self { $libm::round(self) } #[inline] fn trunc(self) -> Self { $libm::trunc(self) } #[inline] fn fract(self) -> Self { $libm::fract(self) } #[inline] fn abs(self) -> Self { $libm::abs(self) } #[inline] fn signum(self) -> Self { Signed::signum(&self) } #[inline] fn mul_add(self, a: Self, b: Self) -> Self { $libm::mul_add(self, a, b) } #[cfg(feature = "std")] #[inline] fn powi(self, n: i32) -> Self { self.powi(n) } #[cfg(not(feature = "std"))] #[inline] fn powi(self, n: i32) -> Self { // FIXME: is there a more accurate solution? $libm::powf(self, n as $T) } #[inline] fn powf(self, n: Self) -> Self { $libm::powf(self, n) } #[inline] fn powc(self, n: Self) -> Self { // Same as powf. $libm::powf(self, n) } #[inline] fn sqrt(self) -> Self { $libm::sqrt(self) } #[inline] fn try_sqrt(self) -> Option { if self >= Self::zero() { Some($libm::sqrt(self)) } else { None } } #[inline] fn exp(self) -> Self { $libm::exp(self) } #[inline] fn exp2(self) -> Self { $libm::exp2(self) } #[inline] fn exp_m1(self) -> Self { $libm::exp_m1(self) } #[inline] fn ln_1p(self) -> Self { $libm::ln_1p(self) } #[inline] fn ln(self) -> Self { $libm::ln(self) } #[inline] fn log(self, base: Self) -> Self { $libm::log(self, base) } #[inline] fn log2(self) -> Self { $libm::log2(self) } #[inline] fn log10(self) -> Self { $libm::log10(self) } #[inline] fn cbrt(self) -> Self { $libm::cbrt(self) } #[inline] fn hypot(self, other: Self) -> Self::RealField { $libm::hypot(self, other) } #[inline] fn sin(self) -> Self { $libm::sin(self) } #[inline] fn cos(self) -> Self { $libm::cos(self) } #[inline] fn tan(self) -> Self { $libm::tan(self) } #[inline] fn asin(self) -> Self { $libm::asin(self) } #[inline] fn acos(self) -> Self { $libm::acos(self) } #[inline] fn atan(self) -> Self { $libm::atan(self) } #[inline] fn sin_cos(self) -> (Self, Self) { $libm::sin_cos(self) } // #[inline] // fn exp_m1(self) -> Self { // $libm::exp_m1(self) // } // // #[inline] // fn ln_1p(self) -> Self { // $libm::ln_1p(self) // } // #[inline] fn sinh(self) -> Self { $libm::sinh(self) } #[inline] fn cosh(self) -> Self { $libm::cosh(self) } #[inline] fn tanh(self) -> Self { $libm::tanh(self) } #[inline] fn asinh(self) -> Self { $libm::asinh(self) } #[inline] fn acosh(self) -> Self { $libm::acosh(self) } #[inline] fn atanh(self) -> Self { $libm::atanh(self) } #[inline] fn is_finite(&self) -> bool { $M::is_finite(*self) } } )*) ); #[cfg(all( not(target_arch = "nvptx"), not(target_arch = "nvptx64"), not(feature = "std"), not(feature = "libm_force"), feature = "libm" ))] impl_complex!( f32, f32, Float; f64, f64, Float ); #[cfg(all(feature = "std", not(feature = "libm_force")))] impl_complex!( f32,f32,f32; f64,f64,f64 ); #[cfg(all( any(target_arch = "nvptx", target_arch = "nvptx64"), not(feature = "std"), not(feature = "libm_force"), feature = "cuda" ))] impl_complex!( f32, f32, GpuFloat; f64, f64, GpuFloat ); #[cfg(feature = "libm_force")] impl ComplexField for f32 { type RealField = f32; #[inline] fn from_real(re: Self::RealField) -> Self { re } #[inline] fn real(self) -> Self::RealField { self } #[inline] fn imaginary(self) -> Self::RealField { Self::zero() } #[inline] fn norm1(self) -> Self::RealField { libm_force::fabsf(self) } #[inline] fn modulus(self) -> Self::RealField { libm_force::fabsf(self) } #[inline] fn modulus_squared(self) -> Self::RealField { self * self } #[inline] fn argument(self) -> Self::RealField { if self >= Self::zero() { Self::zero() } else { Self::pi() } } #[inline] fn to_exp(self) -> (Self, Self) { if self >= Self::zero() { (self, Self::one()) } else { (-self, -Self::one()) } } #[inline] fn recip(self) -> Self { f32::recip(self) } #[inline] fn conjugate(self) -> Self { self } #[inline] fn scale(self, factor: Self::RealField) -> Self { self * factor } #[inline] fn unscale(self, factor: Self::RealField) -> Self { self / factor } #[inline] fn floor(self) -> Self { libm_force::floorf(self) } #[inline] fn ceil(self) -> Self { libm_force::ceilf(self) } #[inline] fn round(self) -> Self { libm_force::roundf(self) } #[inline] fn trunc(self) -> Self { libm_force::truncf(self) } #[inline] fn fract(self) -> Self { self - libm_force::truncf(self) } #[inline] fn abs(self) -> Self { libm_force::fabsf(self) } #[inline] fn signum(self) -> Self { Signed::signum(&self) } #[inline] fn mul_add(self, a: Self, b: Self) -> Self { libm_force::fmaf(self, a, b) } #[inline] fn powi(self, n: i32) -> Self { // TODO: implement a more accurate/efficient solution? libm_force::powf(self, n as f32) } #[inline] fn powf(self, n: Self) -> Self { libm_force::powf(self, n) } #[inline] fn powc(self, n: Self) -> Self { // Same as powf. libm_force::powf(self, n) } #[inline] fn sqrt(self) -> Self { libm_force::sqrtf(self) } #[inline] fn try_sqrt(self) -> Option { if self >= Self::zero() { Some(libm_force::sqrtf(self)) } else { None } } #[inline] fn exp(self) -> Self { libm_force::expf(self) } #[inline] fn exp2(self) -> Self { libm_force::exp2f(self) } #[inline] fn exp_m1(self) -> Self { libm_force::expm1f(self) } #[inline] fn ln_1p(self) -> Self { libm_force::log1pf(self) } #[inline] fn ln(self) -> Self { libm_force::logf(self) } #[inline] fn log(self, base: Self) -> Self { libm_force::logf(self) / libm_force::logf(base) } #[inline] fn log2(self) -> Self { libm_force::log2f(self) } #[inline] fn log10(self) -> Self { libm_force::log10f(self) } #[inline] fn cbrt(self) -> Self { libm_force::cbrtf(self) } #[inline] fn hypot(self, other: Self) -> Self::RealField { libm_force::hypotf(self, other) } #[inline] fn sin(self) -> Self { libm_force::sinf(self) } #[inline] fn cos(self) -> Self { libm_force::cosf(self) } #[inline] fn tan(self) -> Self { libm_force::tanf(self) } #[inline] fn asin(self) -> Self { libm_force::asinf(self) } #[inline] fn acos(self) -> Self { libm_force::acosf(self) } #[inline] fn atan(self) -> Self { libm_force::atanf(self) } #[inline] fn sin_cos(self) -> (Self, Self) { libm_force::sincosf(self) } // #[inline] // fn exp_m1(self) -> Self { // libm_force::exp_m1(self) // } // // #[inline] // fn ln_1p(self) -> Self { // libm_force::ln_1p(self) // } // #[inline] fn sinh(self) -> Self { libm_force::sinhf(self) } #[inline] fn cosh(self) -> Self { libm_force::coshf(self) } #[inline] fn tanh(self) -> Self { libm_force::tanhf(self) } #[inline] fn asinh(self) -> Self { libm_force::asinhf(self) } #[inline] fn acosh(self) -> Self { libm_force::acoshf(self) } #[inline] fn atanh(self) -> Self { libm_force::atanhf(self) } #[inline] fn is_finite(&self) -> bool { f32::is_finite(*self) } } #[cfg(feature = "libm_force")] impl ComplexField for f64 { type RealField = f64; #[inline] fn from_real(re: Self::RealField) -> Self { re } #[inline] fn real(self) -> Self::RealField { self } #[inline] fn imaginary(self) -> Self::RealField { Self::zero() } #[inline] fn norm1(self) -> Self::RealField { libm_force::fabs(self) } #[inline] fn modulus(self) -> Self::RealField { libm_force::fabs(self) } #[inline] fn modulus_squared(self) -> Self::RealField { self * self } #[inline] fn argument(self) -> Self::RealField { if self >= Self::zero() { Self::zero() } else { Self::pi() } } #[inline] fn to_exp(self) -> (Self, Self) { if self >= Self::zero() { (self, Self::one()) } else { (-self, -Self::one()) } } #[inline] fn recip(self) -> Self { f64::recip(self) } #[inline] fn conjugate(self) -> Self { self } #[inline] fn scale(self, factor: Self::RealField) -> Self { self * factor } #[inline] fn unscale(self, factor: Self::RealField) -> Self { self / factor } #[inline] fn floor(self) -> Self { libm_force::floor(self) } #[inline] fn ceil(self) -> Self { libm_force::ceil(self) } #[inline] fn round(self) -> Self { libm_force::round(self) } #[inline] fn trunc(self) -> Self { libm_force::trunc(self) } #[inline] fn fract(self) -> Self { self - libm_force::trunc(self) } #[inline] fn abs(self) -> Self { libm_force::fabs(self) } #[inline] fn signum(self) -> Self { Signed::signum(&self) } #[inline] fn mul_add(self, a: Self, b: Self) -> Self { libm_force::fma(self, a, b) } #[inline] fn powi(self, n: i32) -> Self { // TODO: implement a more accurate solution? libm_force::pow(self, n as f64) } #[inline] fn powf(self, n: Self) -> Self { libm_force::pow(self, n) } #[inline] fn powc(self, n: Self) -> Self { // Same as powf. libm_force::pow(self, n) } #[inline] fn sqrt(self) -> Self { libm_force::sqrt(self) } #[inline] fn try_sqrt(self) -> Option { if self >= Self::zero() { Some(libm_force::sqrt(self)) } else { None } } #[inline] fn exp(self) -> Self { libm_force::exp(self) } #[inline] fn exp2(self) -> Self { libm_force::exp2(self) } #[inline] fn exp_m1(self) -> Self { libm_force::expm1(self) } #[inline] fn ln_1p(self) -> Self { libm_force::log1p(self) } #[inline] fn ln(self) -> Self { libm_force::log(self) } #[inline] fn log(self, base: Self) -> Self { libm_force::log(self) / libm_force::log(base) } #[inline] fn log2(self) -> Self { libm_force::log2(self) } #[inline] fn log10(self) -> Self { libm_force::log10(self) } #[inline] fn cbrt(self) -> Self { libm_force::cbrt(self) } #[inline] fn hypot(self, other: Self) -> Self::RealField { libm_force::hypot(self, other) } #[inline] fn sin(self) -> Self { libm_force::sin(self) } #[inline] fn cos(self) -> Self { libm_force::cos(self) } #[inline] fn tan(self) -> Self { libm_force::tan(self) } #[inline] fn asin(self) -> Self { libm_force::asin(self) } #[inline] fn acos(self) -> Self { libm_force::acos(self) } #[inline] fn atan(self) -> Self { libm_force::atan(self) } #[inline] fn sin_cos(self) -> (Self, Self) { libm_force::sincos(self) } // #[inline] // fn exp_m1(self) -> Self { // libm_force::exp_m1(self) // } // // #[inline] // fn ln_1p(self) -> Self { // libm_force::ln_1p(self) // } // #[inline] fn sinh(self) -> Self { libm_force::sinh(self) } #[inline] fn cosh(self) -> Self { libm_force::cosh(self) } #[inline] fn tanh(self) -> Self { libm_force::tanh(self) } #[inline] fn asinh(self) -> Self { libm_force::asinh(self) } #[inline] fn acosh(self) -> Self { libm_force::acosh(self) } #[inline] fn atanh(self) -> Self { libm_force::atanh(self) } #[inline] fn is_finite(&self) -> bool { f64::is_finite(*self) } } //#[cfg(feature = "decimal")] //impl_real!(d128, d128, d128); // NOTE: all those impls have been copied-pasted to `simd_impl.rs` to implement // SimdComplexField for Complex. impl ComplexField for num_complex::Complex { type RealField = N; #[inline] fn from_real(re: Self::RealField) -> Self { Self::new(re, Self::RealField::zero()) } #[inline] fn real(self) -> Self::RealField { self.re } #[inline] fn imaginary(self) -> Self::RealField { self.im } #[inline] fn argument(self) -> Self::RealField { self.im.atan2(self.re) } #[inline] fn modulus(self) -> Self::RealField { self.re.hypot(self.im) } #[inline] fn modulus_squared(self) -> Self::RealField { self.re.clone() * self.re + self.im.clone() * self.im } #[inline] fn norm1(self) -> Self::RealField { self.re.abs() + self.im.abs() } #[inline] fn recip(self) -> Self { Self::one() / self } #[inline] fn conjugate(self) -> Self { self.conj() } #[inline] fn scale(self, factor: Self::RealField) -> Self { self * factor } #[inline] fn unscale(self, factor: Self::RealField) -> Self { self / factor } #[inline] fn floor(self) -> Self { Self::new(self.re.floor(), self.im.floor()) } #[inline] fn ceil(self) -> Self { Self::new(self.re.ceil(), self.im.ceil()) } #[inline] fn round(self) -> Self { Self::new(self.re.round(), self.im.round()) } #[inline] fn trunc(self) -> Self { Self::new(self.re.trunc(), self.im.trunc()) } #[inline] fn fract(self) -> Self { Self::new(self.re.fract(), self.im.fract()) } #[inline] fn mul_add(self, a: Self, b: Self) -> Self { self * a + b } #[inline] fn abs(self) -> Self::RealField { self.modulus() } #[inline] fn exp2(self) -> Self { let _2 = N::one() + N::one(); num_complex::Complex::new(_2, N::zero()).powc(self) } #[inline] fn exp_m1(self) -> Self { self.exp() - Self::one() } #[inline] fn ln_1p(self) -> Self { (Self::one() + self).ln() } #[inline] fn log2(self) -> Self { let _2 = N::one() + N::one(); self.log(_2) } #[inline] fn log10(self) -> Self { let _10 = N::from_subset(&10.0f64); self.log(_10) } #[inline] fn cbrt(self) -> Self { let one_third = N::from_subset(&(1.0 / 3.0)); self.powf(one_third) } #[inline] fn powi(self, n: i32) -> Self { // FIXME: is there a more accurate solution? let n = N::from_subset(&(n as f64)); self.powf(n) } #[inline] fn is_finite(&self) -> bool { self.re.is_finite() && self.im.is_finite() } /* * * * Unfortunately we are forced to copy-paste all * those impls from https://github.com/rust-num/num-complex/blob/master/src/lib.rs * to avoid requiring `std`. * * */ /// Computes `e^(self)`, where `e` is the base of the natural logarithm. #[inline] 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) } /// Computes the principal value of natural logarithm of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0]`, continuous from above. /// /// The branch satisfies `-π ≤ arg(ln(z)) ≤ π`. #[inline] fn ln(self) -> Self { // formula: ln(z) = ln|z| + i*arg(z) let (r, theta) = self.to_polar(); Self::new(r.ln(), theta) } /// Computes the principal value of the square root of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0)`, continuous from above. /// /// The branch satisfies `-π/2 ≤ arg(sqrt(z)) ≤ π/2`. #[inline] fn sqrt(self) -> Self { // formula: sqrt(r e^(it)) = sqrt(r) e^(it/2) let two = N::one() + N::one(); let (r, theta) = self.to_polar(); complex_from_polar(r.sqrt(), theta / two) } #[inline] fn try_sqrt(self) -> Option { Some(self.sqrt()) } #[inline] fn hypot(self, b: Self) -> Self::RealField { (self.modulus_squared() + b.modulus_squared()).sqrt() } /// Raises `self` to a floating point power. #[inline] fn powf(self, exp: Self::RealField) -> 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.clone()), theta * exp) } /// Returns the logarithm of `self` with respect to an arbitrary base. #[inline] fn log(self, base: N) -> 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(); Self::new(r.log(base.clone()), theta / base.ln()) } /// Raises `self` to a complex power. #[inline] 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) // = ρ^c e^(−d θ) e^(i c θ) ρ^(i d) // = p^c e^(−d θ) (cos(c θ) // + i sin(c θ)) (cos(d ln(ρ)) + i sin(d ln(ρ))) // = p^c e^(−d θ) ( // cos(c θ) cos(d ln(ρ)) − sin(c θ) sin(d ln(ρ)) // + i(cos(c θ) sin(d ln(ρ)) + sin(c θ) cos(d ln(ρ)))) // = 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( r.clone().powf(exp.re.clone()) * (-exp.im.clone() * theta.clone()).exp(), exp.re * theta + exp.im * r.ln(), ) } /* /// Raises a floating point number to the complex power `self`. #[inline] 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)) Self::from_polar(&base.powf(self.re), &(self.im * base.ln())) } */ /// Computes the sine of `self`. #[inline] fn sin(self) -> Self { // formula: sin(a + bi) = sin(a)cosh(b) + i*cos(a)sinh(b) Self::new( self.re.clone().sin() * self.im.clone().cosh(), self.re.cos() * self.im.sinh(), ) } /// Computes the cosine of `self`. #[inline] fn cos(self) -> Self { // formula: cos(a + bi) = cos(a)cosh(b) - i*sin(a)sinh(b) Self::new( self.re.clone().cos() * self.im.clone().cosh(), -self.re.sin() * self.im.sinh(), ) } #[inline] fn sin_cos(self) -> (Self, Self) { let (rsin, rcos) = self.re.sin_cos(); let (isinh, icosh) = self.im.sinh_cosh(); let sin = Self::new(rsin.clone() * icosh.clone(), rcos.clone() * isinh.clone()); let cos = Self::new(rcos * icosh, -rsin * isinh); (sin, cos) } /// Computes the tangent of `self`. #[inline] fn tan(self) -> Self { // formula: tan(a + bi) = (sin(2a) + i*sinh(2b))/(cos(2a) + cosh(2b)) let (two_re, two_im) = (self.re.clone() + self.re, self.im.clone() + self.im); Self::new(two_re.clone().sin(), two_im.clone().sinh()).unscale(two_re.cos() + two_im.cosh()) } /// Computes the principal value of the inverse sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Re(asin(z)) ≤ π/2`. #[inline] fn asin(self) -> Self { // formula: arcsin(z) = -i ln(sqrt(1-z^2) + iz) let i = Self::i(); -i.clone() * ((Self::one() - self.clone() * self.clone()).sqrt() + i * self).ln() } /// Computes the principal value of the inverse cosine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `0 ≤ Re(acos(z)) ≤ π`. #[inline] fn acos(self) -> Self { // formula: arccos(z) = -i ln(i sqrt(1-z^2) + z) let i = Self::i(); -i.clone() * (i * (Self::one() - self.clone() * self.clone()).sqrt() + self).ln() } /// Computes the principal value of the inverse tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i]`, continuous from the left. /// * `[i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Re(atan(z)) ≤ π/2`. #[inline] fn atan(self) -> Self { // formula: arctan(z) = (ln(1+iz) - ln(1-iz))/(2i) let i = Self::i(); let one = Self::one(); let two = one.clone() + one.clone(); if self == i { return Self::new(N::zero(), N::one() / N::zero()); } else if self == -i.clone() { return Self::new(N::zero(), -N::one() / N::zero()); } ((one.clone() + i.clone() * self.clone()).ln() - (one - i.clone() * self).ln()) / (two * i) } /// Computes the hyperbolic sine of `self`. #[inline] fn sinh(self) -> Self { // formula: sinh(a + bi) = sinh(a)cos(b) + i*cosh(a)sin(b) Self::new( self.re.clone().sinh() * self.im.clone().cos(), self.re.cosh() * self.im.sin(), ) } /// Computes the hyperbolic cosine of `self`. #[inline] fn cosh(self) -> Self { // formula: cosh(a + bi) = cosh(a)cos(b) + i*sinh(a)sin(b) Self::new( self.re.clone().cosh() * self.im.clone().cos(), self.re.sinh() * self.im.sin(), ) } #[inline] fn sinh_cosh(self) -> (Self, Self) { let (rsinh, rcosh) = self.re.sinh_cosh(); let (isin, icos) = self.im.sin_cos(); let sin = Self::new(rsinh.clone() * icos.clone(), rcosh.clone() * isin.clone()); let cos = Self::new(rcosh * icos, rsinh * isin); (sin, cos) } /// Computes the hyperbolic tangent of `self`. #[inline] fn tanh(self) -> Self { // formula: tanh(a + bi) = (sinh(2a) + i*sin(2b))/(cosh(2a) + cos(2b)) let (two_re, two_im) = (self.re.clone() + self.re, self.im.clone() + self.im); Self::new(two_re.clone().sinh(), two_im.clone().sin()).unscale(two_re.cosh() + two_im.cos()) } /// Computes the principal value of inverse hyperbolic sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i)`, continuous from the left. /// * `(i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Im(asinh(z)) ≤ π/2`. #[inline] fn asinh(self) -> Self { // formula: arcsinh(z) = ln(z + sqrt(1+z^2)) let one = Self::one(); (self.clone() + (one + self.clone() * self).sqrt()).ln() } /// Computes the principal value of inverse hyperbolic cosine of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 1)`, continuous from above. /// /// The branch satisfies `-π ≤ Im(acosh(z)) ≤ π` and `0 ≤ Re(acosh(z)) < ∞`. #[inline] fn acosh(self) -> Self { // formula: arccosh(z) = 2 ln(sqrt((z+1)/2) + sqrt((z-1)/2)) let one = Self::one(); let two = one.clone() + one.clone(); two.clone() * (((self.clone() + one.clone()) / two.clone()).sqrt() + ((self - one) / two).sqrt()) .ln() } /// Computes the principal value of inverse hyperbolic tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1]`, continuous from above. /// * `[1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Im(atanh(z)) ≤ π/2`. #[inline] fn atanh(self) -> Self { // formula: arctanh(z) = (ln(1+z) - ln(1-z))/2 let one = Self::one(); let two = one.clone() + one.clone(); if self == one { return Self::new(N::one() / N::zero(), N::zero()); } else if self == -one.clone() { return Self::new(-N::one() / N::zero(), N::zero()); } ((one.clone() + self.clone()).ln() - (one - self).ln()) / two } } #[inline] fn complex_from_polar(r: N, theta: N) -> num_complex::Complex { num_complex::Complex::new(r.clone() * theta.clone().cos(), r * theta.sin()) } simba-0.8.1/src/scalar/field.rs000064400000000000000000000032730072674642500144470ustar 00000000000000use crate::simd::SimdValue; use num::NumAssign; pub use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}; /// Trait __alias__ for `Add` and `AddAssign` with result of type `Self`. pub trait ClosedAdd: Sized + Add + AddAssign {} /// Trait __alias__ for `Sub` and `SubAssign` with result of type `Self`. pub trait ClosedSub: Sized + Sub + SubAssign {} /// Trait __alias__ for `Mul` and `MulAssign` with result of type `Self`. pub trait ClosedMul: Sized + Mul + MulAssign {} /// Trait __alias__ for `Div` and `DivAssign` with result of type `Self`. pub trait ClosedDiv: Sized + Div + DivAssign {} /// Trait __alias__ for `Neg` with result of type `Self`. pub trait ClosedNeg: Sized + Neg {} impl ClosedAdd for T where T: Add + AddAssign {} impl ClosedSub for T where T: Sub + SubAssign {} impl ClosedMul for T where T: Mul + MulAssign {} impl ClosedDiv for T where T: Div + DivAssign {} impl ClosedNeg for T where T: Neg {} /// Trait implemented by fields, i.e., complex numbers and floats. pub trait Field: SimdValue + NumAssign + ClosedNeg {} impl Field for num_complex::Complex {} macro_rules! impl_field( ($($t: ty),*) => {$( impl Field for $t {} )*} ); impl_field!(f32, f64); //#[cfg(feature = "decimal")] //impl_field!(decimal::d128); simba-0.8.1/src/scalar/fixed_impl.rs000064400000000000000000001033310072674642500155000ustar 00000000000000#![allow(missing_docs)] //! Implementation of traits form fixed-point numbers. use crate::scalar::{ComplexField, Field, RealField, SubsetOf}; use crate::simd::{PrimitiveSimdValue, SimdValue}; use fixed::traits::ToFixed; use fixed::types::extra::{ IsLessOrEqual, LeEqU16, LeEqU32, LeEqU64, LeEqU8, True, Unsigned, U12, U13, U14, U15, U28, U29, U30, U31, U4, U5, U6, U60, U61, U62, U63, U7, }; use num::{Bounded, FromPrimitive, Num, One, Signed, Zero}; #[cfg(feature = "serde_serialize")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, }; macro_rules! impl_fixed_type( ($($FixedI: ident, $Int: ident, $LeEqDim: ident, $LeEqDim1: ident, $LeEqDim2: ident, $LeEqDim3: ident, $LeEqDim4: ident;)*) => {$( #[derive(Copy, Clone)] #[repr(transparent)] /// Signed fixed-point number with a generic number of bits for the fractional part. pub struct $FixedI(pub fixed::$FixedI); impl $FixedI { /// Creates a fixed-point number from another number. #[inline(always)] pub fn from_num(val: N) -> Self { $FixedI(fixed::$FixedI::from_num(val)) } } impl $FixedI { /// Creates a fixed-point number that has a bitwise representation identical to the given integer. #[inline(always)] pub const fn from_bits(bits: $Int) -> Self { $FixedI(fixed::$FixedI::from_bits(bits)) } /// Creates an integer that has a bitwise representation identical to the given fixed-point number. #[inline(always)] pub const fn to_bits(self) -> $Int { self.0.to_bits() } } impl PartialEq for $FixedI { #[inline(always)] fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl Eq for $FixedI {} impl PartialOrd for $FixedI { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(&other.0) } } impl Ord for $FixedI { #[inline(always)] fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } } impl Hash for $FixedI { fn hash(&self, h: &mut H) { self.0.hash(h); } } #[cfg(feature = "rand")] impl rand::distributions::Distribution<$FixedI> for rand::distributions::Standard { #[inline] fn sample<'a, G: rand::Rng + ?Sized>(&self, rng: &mut G) -> $FixedI { let bits = rng.gen(); $FixedI(fixed::$FixedI::from_bits(bits)) } } #[cfg(feature = "rand")] impl rand::distributions::Distribution<$FixedI> for rand::distributions::OpenClosed01 { #[inline] fn sample<'a, G: rand::Rng + ?Sized>(&self, rng: &mut G) -> $FixedI { let val: f64 = rng.gen(); $FixedI(fixed::$FixedI::from_num(val)) } } impl PrimitiveSimdValue for $FixedI {} impl SimdValue for $FixedI { type Element = Self; type SimdBool = bool; #[inline(always)] fn lanes() -> usize { 1 } #[inline(always)] fn splat(val: Self::Element) -> Self { val } #[inline(always)] fn extract(&self, _: usize) -> Self::Element { *self } #[inline(always)] unsafe fn extract_unchecked(&self, _: usize) -> Self::Element { *self } #[inline(always)] fn replace(&mut self, _: usize, val: Self::Element) { *self = val } #[inline(always)] unsafe fn replace_unchecked(&mut self, _: usize, val: Self::Element) { *self = val } #[inline(always)] fn select(self, cond: Self::SimdBool, other: Self) -> Self { if cond { self } else { other } } } impl Mul for $FixedI { type Output = Self; #[inline(always)] fn mul(self, rhs: Self) -> Self { Self(self.0 * rhs.0) } } impl Mul<$Int> for $FixedI { type Output = Self; #[inline(always)] fn mul(self, rhs: $Int) -> Self { Self(self.0 * rhs) } } impl Div for $FixedI { type Output = Self; #[inline(always)] fn div(self, rhs: Self) -> Self { Self(self.0 / rhs.0) } } impl Div<$Int> for $FixedI { type Output = Self; #[inline(always)] fn div(self, rhs: $Int) -> Self { Self(self.0 / rhs) } } impl Rem for $FixedI { type Output = Self; #[inline(always)] fn rem(self, rhs: Self) -> Self { Self(self.0 % rhs.0) } } impl Rem<$Int> for $FixedI { type Output = Self; #[inline(always)] fn rem(self, rhs: $Int) -> Self { Self(self.0 % rhs) } } impl Add for $FixedI { type Output = Self; #[inline(always)] fn add(self, rhs: Self) -> Self { Self(self.0 + rhs.0) } } impl Sub for $FixedI { type Output = Self; #[inline(always)] fn sub(self, rhs: Self) -> Self { Self(self.0 - rhs.0) } } impl Neg for $FixedI { type Output = Self; #[inline(always)] fn neg(self) -> Self { Self(-self.0) } } impl MulAssign for $FixedI { #[inline(always)] fn mul_assign(&mut self, rhs: Self) { self.0 *= rhs.0 } } impl MulAssign<$Int> for $FixedI { #[inline(always)] fn mul_assign(&mut self, rhs: $Int) { self.0 *= rhs } } impl DivAssign for $FixedI { #[inline(always)] fn div_assign(&mut self, rhs: Self) { self.0 /= rhs.0 } } impl DivAssign<$Int> for $FixedI { #[inline(always)] fn div_assign(&mut self, rhs: $Int) { self.0 /= rhs } } impl RemAssign for $FixedI { #[inline(always)] fn rem_assign(&mut self, rhs: Self) { self.0 %= rhs.0 } } impl RemAssign<$Int> for $FixedI { #[inline(always)] fn rem_assign(&mut self, rhs: $Int) { self.0 %= rhs } } impl AddAssign for $FixedI { #[inline(always)] fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 } } impl SubAssign for $FixedI { #[inline(always)] fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 } } impl Zero for $FixedI { #[inline(always)] fn zero() -> Self { Self(fixed::$FixedI::from_num(0)) } #[inline(always)] fn is_zero(&self) -> bool { self.0 == Self::zero().0 } } impl One for $FixedI { #[inline(always)] fn one() -> Self { Self(fixed::$FixedI::from_num(1)) } } impl Num for $FixedI { type FromStrRadixErr = (); fn from_str_radix(_str: &str, _radix: u32) -> Result { unimplemented!() } } impl Field for $FixedI {} impl SubsetOf<$FixedI> for f64 { #[inline] fn to_superset(&self) -> $FixedI { $FixedI(fixed::$FixedI::from_num(*self)) } #[inline] fn from_superset(element: &$FixedI) -> Option { Some(Self::from_superset_unchecked(element)) } #[inline] fn from_superset_unchecked(element: &$FixedI) -> Self { element.0.to_num::() } #[inline] fn is_in_subset(_: &$FixedI) -> bool { true } } impl SubsetOf<$FixedI> for $FixedI { #[inline] fn to_superset(&self) -> $FixedI { *self } #[inline] fn from_superset(element: &$FixedI) -> Option { Some(*element) } #[inline] fn from_superset_unchecked(element: &$FixedI) -> Self { *element } #[inline] fn is_in_subset(_: &$FixedI) -> bool { true } } impl approx::AbsDiffEq for $FixedI { type Epsilon = Self; fn default_epsilon() -> Self::Epsilon { Self(fixed::$FixedI::from_bits(0b01)) } fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { // This is the impl used in the approx crate. if self > other { (*self - *other) <= epsilon } else { (*other - *self) <= epsilon } } } impl approx::RelativeEq for $FixedI { fn default_max_relative() -> Self::Epsilon { use approx::AbsDiffEq; Self::default_epsilon() } fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { // This is the impl used in the approx crate. let abs_diff = (*self - *other).abs(); if abs_diff <= epsilon { return true; } let abs_self = self.abs(); let abs_other = other.abs(); let largest = if abs_other > abs_self { abs_other } else { abs_self }; abs_diff <= largest * max_relative } } impl approx::UlpsEq for $FixedI { fn default_max_ulps() -> u32 { 4 } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { use approx::AbsDiffEq; if self.abs_diff_eq(other, epsilon) { return true; } if self.signum() != other.signum() { return false; } let bits1 = self.0.to_bits(); let bits2 = other.0.to_bits(); if bits1 > bits2 { (bits1 - bits2) <= max_ulps as $Int } else { (bits2 - bits1) <= max_ulps as $Int } } } impl std::fmt::Debug for $FixedI { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { self.0.fmt(f) } } impl std::fmt::Display for $FixedI { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { self.0.fmt(f) } } impl Bounded for $FixedI { #[inline] fn min_value() -> Self { Self(fixed::$FixedI::MIN) } #[inline] fn max_value() -> Self { Self(fixed::$FixedI::MAX) } } impl FromPrimitive for $FixedI { fn from_i64(n: i64) -> Option { n.checked_to_fixed().map(Self) } fn from_u64(n: u64) -> Option { n.checked_to_fixed().map(Self) } fn from_isize(n: isize) -> Option { n.checked_to_fixed().map(Self) } fn from_i8(n: i8) -> Option { n.checked_to_fixed().map(Self) } fn from_i16(n: i16) -> Option { n.checked_to_fixed().map(Self) } fn from_i32(n: i32) -> Option { n.checked_to_fixed().map(Self) } fn from_usize(n: usize) -> Option { n.checked_to_fixed().map(Self) } fn from_u8(n: u8) -> Option { n.checked_to_fixed().map(Self) } fn from_u16(n: u16) -> Option { n.checked_to_fixed().map(Self) } fn from_u32(n: u32) -> Option { n.checked_to_fixed().map(Self) } fn from_f32(n: f32) -> Option { n.checked_to_fixed().map(Self) } fn from_f64(n: f64) -> Option { n.checked_to_fixed().map(Self) } } impl Signed for $FixedI { fn abs(&self) -> Self { Self(self.0.abs()) } fn abs_sub(&self, other: &Self) -> Self { self.abs() - *other } fn signum(&self) -> Self { Self(self.0.signum()) } fn is_positive(&self) -> bool { self.0 >= Self::zero().0 } fn is_negative(&self) -> bool { self.0 <= Self::zero().0 } } impl ComplexField for $FixedI where Fract: Unsigned + $LeEqDim + IsLessOrEqual<$LeEqDim1, Output = True> + IsLessOrEqual<$LeEqDim2, Output = True> + IsLessOrEqual<$LeEqDim3, Output = True> + IsLessOrEqual<$LeEqDim4, Output = True> { type RealField = Self; #[inline] fn from_real(re: Self::RealField) -> Self { re } #[inline] fn real(self) -> Self::RealField { self } #[inline] fn imaginary(self) -> Self::RealField { Self::zero() } #[inline] fn norm1(self) -> Self::RealField { self.abs() } #[inline] fn modulus(self) -> Self::RealField { self.abs() } #[inline] fn modulus_squared(self) -> Self::RealField { self * self } #[inline] fn argument(self) -> Self::RealField { if self >= Self::zero() { Self::zero() } else { Self::pi() } } #[inline] fn to_exp(self) -> (Self, Self) { if self >= Self::zero() { (self, Self::one()) } else { (-self, -Self::one()) } } #[inline] fn recip(self) -> Self { Self::one() / self } #[inline] fn conjugate(self) -> Self { self } #[inline] fn scale(self, factor: Self::RealField) -> Self { self * factor } #[inline] fn unscale(self, factor: Self::RealField) -> Self { self / factor } #[inline] fn floor(self) -> Self { Self(self.0.floor()) } #[inline] fn ceil(self) -> Self { Self(self.0.ceil()) } #[inline] fn round(self) -> Self { Self(self.0.round()) } #[inline] fn trunc(self) -> Self { Self(self.0.int()) } #[inline] fn fract(self) -> Self { Self(self.0.frac()) } #[inline] fn abs(self) -> Self { Self(self.0.abs()) } #[inline] fn signum(self) -> Self { Self(self.0.signum()) } #[inline] fn mul_add(self, a: Self, b: Self) -> Self { self * a + b } #[cfg(feature = "std")] #[inline] fn powi(self, _n: i32) -> Self { unimplemented!() } #[cfg(not(feature = "std"))] #[inline] fn powi(self, n: i32) -> Self { unimplemented!() } #[inline] fn powf(self, _n: Self) -> Self { unimplemented!() } #[inline] fn powc(self, _n: Self) -> Self { unimplemented!() } #[inline] fn sqrt(self) -> Self { Self(cordic::sqrt(self.0)) } #[inline] fn try_sqrt(self) -> Option { if self >= Self::zero() { Some(self.sqrt()) } else { None } } #[inline] fn exp(self) -> Self { Self(cordic::exp(self.0)) } #[inline] fn exp2(self) -> Self { unimplemented!() } #[inline] fn exp_m1(self) -> Self { unimplemented!() } #[inline] fn ln_1p(self) -> Self { unimplemented!() } #[inline] fn ln(self) -> Self { unimplemented!() } #[inline] fn log(self, _base: Self) -> Self { unimplemented!() } #[inline] fn log2(self) -> Self { unimplemented!() } #[inline] fn log10(self) -> Self { unimplemented!() } #[inline] fn cbrt(self) -> Self { unimplemented!() } #[inline] fn hypot(self, _other: Self) -> Self::RealField { unimplemented!() } #[inline] fn sin(self) -> Self { Self(cordic::sin(self.0)) } #[inline] fn cos(self) -> Self { Self(cordic::cos(self.0)) } #[inline] fn tan(self) -> Self { Self(cordic::tan(self.0)) } #[inline] fn asin(self) -> Self { Self(cordic::asin(self.0)) } #[inline] fn acos(self) -> Self { Self(cordic::acos(self.0)) } #[inline] fn atan(self) -> Self { Self(cordic::atan(self.0)) } #[inline] fn sin_cos(self) -> (Self, Self) { let (sin, cos) = cordic::sin_cos(self.0); (Self(sin), Self(cos)) } #[inline] fn sinh(self) -> Self { unimplemented!() } #[inline] fn cosh(self) -> Self { unimplemented!() } #[inline] fn tanh(self) -> Self { unimplemented!() } #[inline] fn asinh(self) -> Self { unimplemented!() } #[inline] fn acosh(self) -> Self { unimplemented!() } #[inline] fn atanh(self) -> Self { unimplemented!() } #[inline] fn is_finite(&self) -> bool { true } } impl RealField for $FixedI where Fract: Unsigned + $LeEqDim + IsLessOrEqual<$LeEqDim1, Output = True> + IsLessOrEqual<$LeEqDim2, Output = True> + IsLessOrEqual<$LeEqDim3, Output = True> + IsLessOrEqual<$LeEqDim4, Output = True> { #[inline] fn is_sign_positive(&self) -> bool { self.0.is_positive() } #[inline] fn is_sign_negative(&self) -> bool { self.0.is_negative() } #[inline] fn copysign(self, sign: Self) -> Self { if sign >= Self::zero() { self.abs() } else { -self.abs() } } #[inline] fn max(self, other: Self) -> Self { if self >= other { self } else { other } } #[inline] fn min(self, other: Self) -> Self { if self < other { self } else { other } } #[inline] fn clamp(self, min: Self, max: Self) -> Self { if self < min { min } else if self > max { max } else { self } } #[inline] fn atan2(self, other: Self) -> Self { Self(cordic::atan2(self.0, other.0)) } #[inline] fn min_value() -> Option { Some(Bounded::min_value()) } #[inline] fn max_value() -> Option { Some(Bounded::max_value()) } /// Archimedes' constant. #[inline] fn pi() -> Self { Self(fixed::$FixedI::PI) } /// 2.0 * pi. #[inline] fn two_pi() -> Self { Self::pi() + Self::pi() } /// pi / 2.0. #[inline] fn frac_pi_2() -> Self { Self(fixed::$FixedI::FRAC_PI_2) } /// pi / 3.0. #[inline] fn frac_pi_3() -> Self { Self(fixed::$FixedI::FRAC_PI_3) } /// pi / 4.0. #[inline] fn frac_pi_4() -> Self { Self(fixed::$FixedI::FRAC_PI_4) } /// pi / 6.0. #[inline] fn frac_pi_6() -> Self { Self(fixed::$FixedI::FRAC_PI_6) } /// pi / 8.0. #[inline] fn frac_pi_8() -> Self { Self(fixed::$FixedI::FRAC_PI_8) } /// 1.0 / pi. #[inline] fn frac_1_pi() -> Self { Self(fixed::$FixedI::FRAC_1_PI) } /// 2.0 / pi. #[inline] fn frac_2_pi() -> Self { Self(fixed::$FixedI::FRAC_2_PI) } /// 2.0 / sqrt(pi). #[inline] fn frac_2_sqrt_pi() -> Self { Self(fixed::$FixedI::FRAC_2_SQRT_PI) } /// Euler's number. #[inline] fn e() -> Self { Self(fixed::$FixedI::E) } /// log2(e). #[inline] fn log2_e() -> Self { Self(fixed::$FixedI::LOG2_E) } /// log10(e). #[inline] fn log10_e() -> Self { Self(fixed::$FixedI::LOG10_E) } /// ln(2.0). #[inline] fn ln_2() -> Self { Self(fixed::$FixedI::LN_2) } /// ln(10.0). #[inline] fn ln_10() -> Self { Self(fixed::$FixedI::LN_10) } } #[cfg(feature = "serde_serialize")] impl Serialize for $FixedI { fn serialize(&self, serializer: S) -> Result { self.0.serialize(serializer) } } #[cfg(feature = "serde_serialize")] impl<'de, Fract: $LeEqDim> Deserialize<'de> for $FixedI { fn deserialize>(deserializer: D) -> Result { fixed::$FixedI::deserialize(deserializer).map($FixedI) } } )*} ); impl_fixed_type!( FixedI8, i8, LeEqU8, U7, U6, U5, U4; FixedI16, i16, LeEqU16, U15, U14, U13, U12; FixedI32, i32, LeEqU32, U31, U30, U29, U28; FixedI64, i64, LeEqU64, U63, U62, U61, U60; ); pub type FixedI8F0 = FixedI8; pub type FixedI7F1 = FixedI8; pub type FixedI6F2 = FixedI8; pub type FixedI5F3 = FixedI8; pub type FixedI4F4 = FixedI8; pub type FixedI3F5 = FixedI8; pub type FixedI16F0 = FixedI16; pub type FixedI15F1 = FixedI16; pub type FixedI14F2 = FixedI16; pub type FixedI13F3 = FixedI16; pub type FixedI12F4 = FixedI16; pub type FixedI11F5 = FixedI16; pub type FixedI10F6 = FixedI16; pub type FixedI9F7 = FixedI16; pub type FixedI8F8 = FixedI16; pub type FixedI7F9 = FixedI16; pub type FixedI6F10 = FixedI16; pub type FixedI5F11 = FixedI16; pub type FixedI4F12 = FixedI16; pub type FixedI3F13 = FixedI16; pub type FixedI32F0 = FixedI32; pub type FixedI31F1 = FixedI32; pub type FixedI30F2 = FixedI32; pub type FixedI29F3 = FixedI32; pub type FixedI28F4 = FixedI32; pub type FixedI27F5 = FixedI32; pub type FixedI26F6 = FixedI32; pub type FixedI25F7 = FixedI32; pub type FixedI24F8 = FixedI32; pub type FixedI23F9 = FixedI32; pub type FixedI22F10 = FixedI32; pub type FixedI21F11 = FixedI32; pub type FixedI20F12 = FixedI32; pub type FixedI19F13 = FixedI32; pub type FixedI18F14 = FixedI32; pub type FixedI17F15 = FixedI32; pub type FixedI16F16 = FixedI32; pub type FixedI15F17 = FixedI32; pub type FixedI14F18 = FixedI32; pub type FixedI13F19 = FixedI32; pub type FixedI12F20 = FixedI32; pub type FixedI11F21 = FixedI32; pub type FixedI10F22 = FixedI32; pub type FixedI9F23 = FixedI32; pub type FixedI8F24 = FixedI32; pub type FixedI7F25 = FixedI32; pub type FixedI6F26 = FixedI32; pub type FixedI5F27 = FixedI32; pub type FixedI4F28 = FixedI32; pub type FixedI3F29 = FixedI32; pub type FixedI64F0 = FixedI64; pub type FixedI63F1 = FixedI64; pub type FixedI62F2 = FixedI64; pub type FixedI61F3 = FixedI64; pub type FixedI60F4 = FixedI64; pub type FixedI59F5 = FixedI64; pub type FixedI58F6 = FixedI64; pub type FixedI57F7 = FixedI64; pub type FixedI56F8 = FixedI64; pub type FixedI55F9 = FixedI64; pub type FixedI54F10 = FixedI64; pub type FixedI53F11 = FixedI64; pub type FixedI52F12 = FixedI64; pub type FixedI51F13 = FixedI64; pub type FixedI50F14 = FixedI64; pub type FixedI49F15 = FixedI64; pub type FixedI48F16 = FixedI64; pub type FixedI47F17 = FixedI64; pub type FixedI46F18 = FixedI64; pub type FixedI45F19 = FixedI64; pub type FixedI44F20 = FixedI64; pub type FixedI43F21 = FixedI64; pub type FixedI42F22 = FixedI64; pub type FixedI41F23 = FixedI64; pub type FixedI40F24 = FixedI64; pub type FixedI39F25 = FixedI64; pub type FixedI38F26 = FixedI64; pub type FixedI37F27 = FixedI64; pub type FixedI36F28 = FixedI64; pub type FixedI35F29 = FixedI64; pub type FixedI34F30 = FixedI64; pub type FixedI33F31 = FixedI64; pub type FixedI32F32 = FixedI64; pub type FixedI31F33 = FixedI64; pub type FixedI30F34 = FixedI64; pub type FixedI29F35 = FixedI64; pub type FixedI28F36 = FixedI64; pub type FixedI27F37 = FixedI64; pub type FixedI26F38 = FixedI64; pub type FixedI25F39 = FixedI64; pub type FixedI24F40 = FixedI64; pub type FixedI23F41 = FixedI64; pub type FixedI22F42 = FixedI64; pub type FixedI21F43 = FixedI64; pub type FixedI20F44 = FixedI64; pub type FixedI19F45 = FixedI64; pub type FixedI18F46 = FixedI64; pub type FixedI17F47 = FixedI64; pub type FixedI16F48 = FixedI64; pub type FixedI15F49 = FixedI64; pub type FixedI14F50 = FixedI64; pub type FixedI13F51 = FixedI64; pub type FixedI12F52 = FixedI64; pub type FixedI11F53 = FixedI64; pub type FixedI10F54 = FixedI64; pub type FixedI9F55 = FixedI64; pub type FixedI8F56 = FixedI64; pub type FixedI7F57 = FixedI64; pub type FixedI6F58 = FixedI64; pub type FixedI5F59 = FixedI64; pub type FixedI4F60 = FixedI64; simba-0.8.1/src/scalar/mod.rs000064400000000000000000000007040072674642500141370ustar 00000000000000//! Traits implemented by scalar, non-SIMD, types. pub use self::complex::ComplexField; pub use self::field::{ClosedAdd, ClosedDiv, ClosedMul, ClosedNeg, ClosedSub, Field}; #[cfg(feature = "partial_fixed_point_support")] pub use self::fixed_impl::*; pub use self::real::RealField; pub use self::subset::{SubsetOf, SupersetOf}; mod real; #[macro_use] mod complex; mod field; #[cfg(feature = "partial_fixed_point_support")] mod fixed_impl; mod subset; simba-0.8.1/src/scalar/real.rs000064400000000000000000000154240072674642500143100ustar 00000000000000use num::Signed; use std::{f32, f64}; use approx::{RelativeEq, UlpsEq}; use crate::scalar::ComplexField; #[cfg(all( any(target_arch = "nvptx", target_arch = "nvptx64"), not(feature = "std"), not(feature = "libm_force"), feature = "cuda" ))] use cuda_std::GpuFloat; #[cfg(all(not(feature = "std"), not(feature = "libm_force"), feature = "libm"))] use num::Float; //#[cfg(feature = "decimal")] //use decimal::d128; /// Trait shared by all reals. #[allow(missing_docs)] pub trait RealField: ComplexField + RelativeEq + UlpsEq + Signed + PartialOrd { /// Is the sign of this real number positive? fn is_sign_positive(&self) -> bool; /// Is the sign of this real number negative? fn is_sign_negative(&self) -> bool; /// Copies the sign of `sign` to `self`. /// /// - Returns `self.simd_abs()` if `sign` is positive or positive-zero. /// - Returns `-self.simd_abs()` if `sign` is negative or negative-zero. fn copysign(self, sign: Self) -> Self; fn max(self, other: Self) -> Self; fn min(self, other: Self) -> Self; fn clamp(self, min: Self, max: Self) -> Self; fn atan2(self, other: Self) -> Self; /// The smallest finite positive value representable using this type. fn min_value() -> Option; /// The largest finite positive value representable using this type. fn max_value() -> Option; fn pi() -> Self; fn two_pi() -> Self; fn frac_pi_2() -> Self; fn frac_pi_3() -> Self; fn frac_pi_4() -> Self; fn frac_pi_6() -> Self; fn frac_pi_8() -> Self; fn frac_1_pi() -> Self; fn frac_2_pi() -> Self; fn frac_2_sqrt_pi() -> Self; fn e() -> Self; fn log2_e() -> Self; fn log10_e() -> Self; fn ln_2() -> Self; fn ln_10() -> Self; } macro_rules! impl_real( ($($T:ty, $M:ident, $cpysgn_mod: ident, $atan_mod: ident);*) => ($( impl RealField for $T { #[inline] fn is_sign_positive(&self) -> bool { $M::is_sign_positive(*self) } #[inline] fn is_sign_negative(&self) -> bool { $M::is_sign_negative(*self) } #[inline(always)] fn copysign(self, sign: Self) -> Self { $cpysgn_mod::copysign(self, sign) } #[inline] fn max(self, other: Self) -> Self { $M::max(self, other) } #[inline] fn min(self, other: Self) -> Self { $M::min(self, other) } #[inline] fn clamp(self, min: Self, max: Self) -> Self { if self < min { min } else if self > max { max } else { self } } #[inline] fn atan2(self, other: Self) -> Self { $atan_mod::atan2(self, other) } /// The smallest finite positive value representable using this type. #[inline] fn min_value() -> Option { Some($M::MIN) } /// The largest finite positive value representable using this type. #[inline] fn max_value() -> Option { Some($M::MAX) } /// Archimedes' constant. #[inline] fn pi() -> Self { $M::consts::PI } /// 2.0 * pi. #[inline] fn two_pi() -> Self { $M::consts::PI + $M::consts::PI } /// pi / 2.0. #[inline] fn frac_pi_2() -> Self { $M::consts::FRAC_PI_2 } /// pi / 3.0. #[inline] fn frac_pi_3() -> Self { $M::consts::FRAC_PI_3 } /// pi / 4.0. #[inline] fn frac_pi_4() -> Self { $M::consts::FRAC_PI_4 } /// pi / 6.0. #[inline] fn frac_pi_6() -> Self { $M::consts::FRAC_PI_6 } /// pi / 8.0. #[inline] fn frac_pi_8() -> Self { $M::consts::FRAC_PI_8 } /// 1.0 / pi. #[inline] fn frac_1_pi() -> Self { $M::consts::FRAC_1_PI } /// 2.0 / pi. #[inline] fn frac_2_pi() -> Self { $M::consts::FRAC_2_PI } /// 2.0 / sqrt(pi). #[inline] fn frac_2_sqrt_pi() -> Self { $M::consts::FRAC_2_SQRT_PI } /// Euler's number. #[inline] fn e() -> Self { $M::consts::E } /// log2(e). #[inline] fn log2_e() -> Self { $M::consts::LOG2_E } /// log10(e). #[inline] fn log10_e() -> Self { $M::consts::LOG10_E } /// ln(2.0). #[inline] fn ln_2() -> Self { $M::consts::LN_2 } /// ln(10.0). #[inline] fn ln_10() -> Self { $M::consts::LN_10 } } )*) ); #[cfg(all( not(target_arch = "nvptx"), not(target_arch = "nvptx64"), not(feature = "std"), not(feature = "libm_force"), feature = "libm" ))] impl_real!(f32, f32, Float, Float; f64, f64, Float, Float); #[cfg(all(feature = "std", not(feature = "libm_force")))] impl_real!(f32, f32, f32, f32; f64, f64, f64, f64); #[cfg(all( any(target_arch = "nvptx", target_arch = "nvptx64"), not(feature = "std"), not(feature = "libm_force"), feature = "cuda" ))] impl_real!( f32, f32, GpuFloat, GpuFloat; f64, f64, GpuFloat, GpuFloat ); #[cfg(feature = "libm_force")] impl_real!(f32, f32, libm_force_f32, libm_force_f32; f64, f64, libm_force, libm_force); // We use this dummy module to remove the 'f' suffix at the end of // each libm functions to make our generic Real/ComplexField impl // macros work. #[cfg(feature = "libm_force")] mod libm_force_f32 { #[inline(always)] pub fn atan2(y: f32, x: f32) -> f32 { libm_force::atan2f(y, x) } #[inline(always)] pub fn copysign(x: f32, y: f32) -> f32 { libm_force::copysignf(x, y) } } //#[cfg(feature = "decimal")] //impl_real!(d128, d128, d128); simba-0.8.1/src/scalar/subset.rs000064400000000000000000000170160072674642500146710ustar 00000000000000#[cfg(feature = "decimal")] use decimal::d128; use num::Zero; use num_complex::Complex; /// Nested sets and conversions between them (using an injective mapping). Useful to work with /// substructures. In generic code, it is preferable to use `SupersetOf` as trait bound whenever /// possible instead of `SubsetOf` (because SupersetOf is automatically implemented whenever /// `SubsetOf` is). /// /// The notion of "nested sets" is very broad and applies to what the types are _supposed to /// represent_, independently from their actual implementation details and limitations. For /// example: /// * f32 and f64 are both supposed to represent reals and are thus considered equal (even if in /// practice f64 has more elements). /// * u32 and i8 are respectively supposed to represent natural and relative numbers. Thus, u32 is /// a subset of i8. /// * A quaternion and a 3x3 orthogonal matrix with unit determinant are both sets of rotations. /// They can thus be considered equal. /// /// In other words, implementation details due to machine limitations are ignored (otherwise we /// could not even, e.g., convert a u64 to an i64). If considering those limitations are /// important, other crates allowing you to query the limitations of given types should be used. pub trait SubsetOf: Sized { /// The inclusion map: converts `self` to the equivalent element of its superset. fn to_superset(&self) -> T; /// The inverse inclusion map: attempts to construct `self` from the equivalent element of its /// superset. /// /// Must return `None` if `element` has no equivalent in `Self`. fn from_superset(element: &T) -> Option { if Self::is_in_subset(element) { Some(Self::from_superset_unchecked(element)) } else { None } } /// Use with care! Same as `self.to_superset` but without any property checks. Always succeeds. fn from_superset_unchecked(element: &T) -> Self; /// Checks if `element` is actually part of the subset `Self` (and can be converted to it). fn is_in_subset(element: &T) -> bool; } /// Nested sets and conversions between them. Useful to work with substructures. It is preferable /// to implement the `SubsetOf` trait instead of `SupersetOf` whenever possible (because /// `SupersetOf` is automatically implemented whenever `SubsetOf` is). /// /// The notion of "nested sets" is very broad and applies to what the types are _supposed to /// represent_, independently from their actual implementation details and limitations. For /// example: /// * f32 and f64 are both supposed to represent reals and are thus considered equal (even if in /// practice f64 has more elements). /// * u32 and i8 are respectively supposed to represent natural and relative numbers. Thus, i8 is /// a superset of u32. /// * A quaternion and a 3x3 orthogonal matrix with unit determinant are both sets of rotations. /// They can thus be considered equal. /// /// In other words, implementation details due to machine limitations are ignored (otherwise we /// could not even, e.g., convert a u64 to an i64). If considering those limitations are /// important, other crates allowing you to query the limitations of given types should be used. pub trait SupersetOf: Sized { /// The inverse inclusion map: attempts to construct `self` from the equivalent element of its /// superset. /// /// Must return `None` if `element` has no equivalent in `Self`. fn to_subset(&self) -> Option { if self.is_in_subset() { Some(self.to_subset_unchecked()) } else { None } } /// Checks if `self` is actually part of its subset `T` (and can be converted to it). fn is_in_subset(&self) -> bool; /// Use with care! Same as `self.to_subset` but without any property checks. Always succeeds. fn to_subset_unchecked(&self) -> T; /// The inclusion map: converts `self` to the equivalent element of its superset. fn from_subset(element: &T) -> Self; } impl, SP> SupersetOf for SP { #[inline] fn to_subset(&self) -> Option { SS::from_superset(self) } #[inline] fn is_in_subset(&self) -> bool { SS::is_in_subset(self) } #[inline] fn to_subset_unchecked(&self) -> SS { SS::from_superset_unchecked(self) } #[inline] fn from_subset(element: &SS) -> Self { element.to_superset() } } macro_rules! impl_subset( ($($subset: ty as $( $superset: ty),+ );* $(;)*) => { $($( impl SubsetOf<$superset> for $subset { #[inline] fn to_superset(&self) -> $superset { *self as $superset } #[inline] fn from_superset_unchecked(element: &$superset) -> $subset { *element as $subset } #[inline] fn is_in_subset(_: &$superset) -> bool { true } } )+)* } ); impl_subset!( u8 as u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64; u16 as u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64; u32 as u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64; u64 as u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64; u128 as u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64; usize as u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64; i8 as i8, i16, i32, i64, i128, isize, f32, f64; i16 as i8, i16, i32, i64, i128, isize, f32, f64; i32 as i8, i16, i32, i64, i128, isize, f32, f64; i64 as i8, i16, i32, i64, i128, isize, f32, f64; i128 as i8, i16, i32, i64, i128, isize, f32, f64; isize as i8, i16, i32, i64, i128, isize, f32, f64; f32 as f32, f64; f64 as f32, f64; ); //#[cfg(feature = "decimal")] //impl_subset!( // u8 as d128; // u16 as d128; // u32 as d128; // u64 as d128; // usize as d128; // // i8 as d128; // i16 as d128; // i32 as d128; // i64 as d128; // isize as d128; // // f32 as d128; // f64 as d128; // d128 as d128; //); impl> SubsetOf> for Complex { #[inline] fn to_superset(&self) -> Complex { Complex { re: N2::from_subset(&self.re), im: N2::from_subset(&self.im), } } #[inline] fn from_superset_unchecked(element: &Complex) -> Complex { Complex { re: element.re.to_subset_unchecked(), im: element.im.to_subset_unchecked(), } } #[inline] fn is_in_subset(c: &Complex) -> bool { c.re.is_in_subset() && c.im.is_in_subset() } } macro_rules! impl_scalar_subset_of_complex( ($($t: ident),*) => {$( impl> SubsetOf> for $t { #[inline] fn to_superset(&self) -> Complex { Complex { re: N2::from_subset(self), im: N2::zero() } } #[inline] fn from_superset_unchecked(element: &Complex) -> $t { element.re.to_subset_unchecked() } #[inline] fn is_in_subset(c: &Complex) -> bool { c.re.is_in_subset() && c.im.is_zero() } } )*} ); impl_scalar_subset_of_complex!( u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64 ); #[cfg(feature = "decimal")] impl_scalar_subset_of_complex!(d128); simba-0.8.1/src/simd/auto_simd_impl.rs000064400000000000000000001641770072674642500160730ustar 00000000000000#![allow(missing_docs)] #![allow(non_camel_case_types)] // For the simd type aliases. //! SIMD values based on auto-vectorization. use crate::scalar::{Field, SubsetOf, SupersetOf}; use crate::simd::{ PrimitiveSimdValue, SimdBool, SimdComplexField, SimdPartialOrd, SimdRealField, SimdSigned, SimdValue, }; use approx::AbsDiffEq; #[cfg(feature = "decimal")] use decimal::d128; use num::{FromPrimitive, Num, One, Zero}; use std::{ fmt, ops::{ Add, AddAssign, BitAnd, BitOr, BitXor, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Sub, SubAssign, }, }; // This is a hack to allow use to reuse `_0` as integers or as identifier, // depending on whether or not `ident_to_value` has been called in scope. // This helps writing macros that define both `::new` and `From([T; lanes()])`. macro_rules! ident_to_value( () => { const _0: usize = 0; const _1: usize = 1; const _2: usize = 2; const _3: usize = 3; const _4: usize = 4; const _5: usize = 5; const _6: usize = 6; const _7: usize = 7; const _8: usize = 8; const _9: usize = 9; const _10: usize = 10; const _11: usize = 11; const _12: usize = 12; const _13: usize = 13; const _14: usize = 14; const _15: usize = 15; const _16: usize = 16; const _17: usize = 17; const _18: usize = 18; const _19: usize = 19; const _20: usize = 20; const _21: usize = 21; const _22: usize = 22; const _23: usize = 23; const _24: usize = 24; const _25: usize = 25; const _26: usize = 26; const _27: usize = 27; const _28: usize = 28; const _29: usize = 29; const _30: usize = 30; const _31: usize = 31; const _32: usize = 32; const _33: usize = 33; const _34: usize = 34; const _35: usize = 35; const _36: usize = 36; const _37: usize = 37; const _38: usize = 38; const _39: usize = 39; const _40: usize = 40; const _41: usize = 41; const _42: usize = 42; const _43: usize = 43; const _44: usize = 44; const _45: usize = 45; const _46: usize = 46; const _47: usize = 47; const _48: usize = 48; const _49: usize = 49; const _50: usize = 50; const _51: usize = 51; const _52: usize = 52; const _53: usize = 53; const _54: usize = 54; const _55: usize = 55; const _56: usize = 56; const _57: usize = 57; const _58: usize = 58; const _59: usize = 59; const _60: usize = 60; const _61: usize = 61; const _62: usize = 62; const _63: usize = 63; } ); /// A SIMD structure that implements all the relevant traits from `num` an `simba`. /// /// This is needed to overcome the orphan rules. #[repr(align(16))] #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr( feature = "rkyv", derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize), archive(as = "Self", bound(archive = "N: rkyv::Archive")) )] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] pub struct AutoSimd(pub N); /// A SIMD boolean structure that implements all the relevant traits from `num` an `simba`. /// /// This is needed to overcome the orphan rules. #[repr(align(16))] #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr( feature = "rkyv", derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize), archive(as = "Self", bound(archive = "N: rkyv::Archive")) )] pub struct AutoBoolSimd(pub N); macro_rules! impl_bool_simd( ($($t: ty, $lanes: expr, $($i: ident),*;)*) => {$( impl_simd_value!($t, bool, $lanes, AutoSimd<$t> $(, $i)*;); impl From<[bool; $lanes]> for AutoSimd<$t> { #[inline(always)] fn from(vals: [bool; $lanes]) -> Self { Self(vals) } } impl Not for AutoSimd<$t> { type Output = Self; #[inline] fn not(self) -> Self { self.map(|x| !x) } } impl BitAnd> for AutoSimd<$t> { type Output = Self; fn bitand(self, rhs: Self) -> Self { self.zip_map(rhs, |x, y| x & y) } } impl BitOr> for AutoSimd<$t> { type Output = Self; fn bitor(self, rhs: Self) -> Self { self.zip_map(rhs, |x, y| x | y) } } impl BitXor> for AutoSimd<$t> { type Output = Self; fn bitxor(self, rhs: Self) -> Self { self.zip_map(rhs, |x, y| x ^ y) } } impl SimdBool for AutoSimd<$t> { #[inline(always)] fn bitmask(self) -> u64 { ident_to_value!(); 0u64 $( | ((self.0[$i] as u64) << $i) )* } #[inline(always)] fn and(self) -> bool { ident_to_value!(); true $( && self.0[$i] )* } #[inline(always)] fn or(self) -> bool { ident_to_value!(); false $( || self.0[$i] )* } #[inline(always)] fn xor(self) -> bool { ident_to_value!(); false $( ^ self.0[$i] )* } #[inline(always)] fn all(self) -> bool { self.and() } #[inline(always)] fn any(self) -> bool { self.or() } #[inline(always)] fn none(self) -> bool { !self.any() } #[inline(always)] fn if_else>( self, if_value: impl FnOnce() -> Res, else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_value(); a.select(self, b) } #[inline(always)] fn if_else2>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_if.1(); let c = else_value(); let cond_a = self; let cond_b = else_if.0(); a.select(cond_a, b.select(cond_b, c)) } #[inline(always)] fn if_else3>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_if.1(); let c = else_else_if.1(); let d = else_value(); let cond_a = self; let cond_b = else_if.0(); let cond_c = else_else_if.0(); a.select(cond_a, b.select(cond_b, c.select(cond_c, d))) } } )*} ); macro_rules! impl_scalar_subset_of_simd( ($($t: ty),*) => {$( impl SubsetOf> for $t where AutoSimd: SimdValue + Copy, as SimdValue>::Element: SupersetOf<$t> + PartialEq, { #[inline(always)] fn to_superset(&self) -> AutoSimd { AutoSimd::::splat( as SimdValue>::Element::from_subset(self)) } #[inline(always)] fn from_superset_unchecked(element: &AutoSimd) -> $t { element.extract(0).to_subset_unchecked() } #[inline(always)] fn is_in_subset(c: &AutoSimd) -> bool { let elt0 = c.extract(0); elt0.is_in_subset() && (1..AutoSimd::::lanes()).all(|i| c.extract(i) == elt0) } } )*} ); impl_scalar_subset_of_simd!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64); #[cfg(feature = "decimal")] impl_scalar_subset_of_simd!(d128); macro_rules! impl_simd_value( ($($t: ty, $elt: ty, $lanes: expr, $bool: ty, $($i: ident),*;)*) => ($( impl ArrTransform for AutoSimd<$t> { #[inline(always)] fn map(self, f: impl Fn(Self::Element) -> Self::Element) -> Self { ident_to_value!(); Self([$(f(self.0[$i])),*]) } #[inline(always)] fn zip_map(self, other: Self, f: impl Fn(Self::Element, Self::Element) -> Self::Element) -> Self { ident_to_value!(); Self([$(f(self.0[$i], other.0[$i])),*]) } #[inline(always)] fn zip_zip_map(self, b: Self, c: Self, f: impl Fn(Self::Element, Self::Element, Self::Element) -> Self::Element) -> Self { ident_to_value!(); Self([$(f(self.0[$i], b.0[$i], c.0[$i])),*]) } #[inline(always)] fn map_bool(self, f: impl Fn(Self::Element) -> bool) -> Self::SimdBool { ident_to_value!(); AutoSimd([$(f(self.0[$i])),*]) } #[inline(always)] fn zip_map_bool(self, other: Self, f: impl Fn(Self::Element, Self::Element) -> bool) -> Self::SimdBool { ident_to_value!(); AutoSimd([$(f(self.0[$i], other.0[$i])),*]) } } impl fmt::Display for AutoSimd<$t> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if Self::lanes() == 1 { return self.extract(0).fmt(f); } write!(f, "({}", self.extract(0))?; for i in 1..Self::lanes() { write!(f, ", {}", self.extract(i))?; } write!(f, ")") } } impl AutoSimd<$t> { pub fn new($($i: $elt),*) -> Self { AutoSimd([$($i),*]) } } impl PrimitiveSimdValue for AutoSimd<$t> {} impl SimdValue for AutoSimd<$t> { type Element = $elt; type SimdBool = $bool; #[inline(always)] fn lanes() -> usize { $lanes } #[inline(always)] fn splat(val: Self::Element) -> Self { AutoSimd([val; $lanes]) } #[inline(always)] fn extract(&self, i: usize) -> Self::Element { self.0[i] } #[inline(always)] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { *self.0.get_unchecked(i) } #[inline(always)] fn replace(&mut self, i: usize, val: Self::Element) { self.0[i] = val } #[inline(always)] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { *self.0.get_unchecked_mut(i) = val } #[inline(always)] fn select(self, cond: Self::SimdBool, other: Self) -> Self { ident_to_value!(); Self([ $(if cond.0[$i] { self.0[$i] } else { other.0[$i] }),* ]) } } )*) ); macro_rules! impl_uint_simd( ($($t: ty, $elt: ty, $lanes: expr, $bool: ty, $($i: ident),*;)*) => ($( impl_simd_value!($t, $elt, $lanes, $bool $(, $i)*;); impl From<[$elt; $lanes]> for AutoSimd<$t> { #[inline(always)] fn from(vals: [$elt; $lanes]) -> Self { AutoSimd(vals) } } impl From> for [$elt; $lanes] { #[inline(always)] fn from(val: AutoSimd<$t>) -> [$elt; $lanes] { val.0 } } impl SubsetOf> for AutoSimd<$t> { #[inline(always)] fn to_superset(&self) -> Self { *self } #[inline(always)] fn from_superset(element: &Self) -> Option { Some(*element) } #[inline(always)] fn from_superset_unchecked(element: &Self) -> Self { *element } #[inline(always)] fn is_in_subset(_: &Self) -> bool { true } } impl Num for AutoSimd<$t> { type FromStrRadixErr = <$elt as Num>::FromStrRadixErr; #[inline(always)] fn from_str_radix(str: &str, radix: u32) -> Result { <$elt>::from_str_radix(str, radix).map(Self::splat) } } impl FromPrimitive for AutoSimd<$t> { #[inline(always)] fn from_i64(n: i64) -> Option { <$elt>::from_i64(n).map(Self::splat) } #[inline(always)] fn from_u64(n: u64) -> Option { <$elt>::from_u64(n).map(Self::splat) } #[inline(always)] fn from_isize(n: isize) -> Option { <$elt>::from_isize(n).map(Self::splat) } #[inline(always)] fn from_i8(n: i8) -> Option { <$elt>::from_i8(n).map(Self::splat) } #[inline(always)] fn from_i16(n: i16) -> Option { <$elt>::from_i16(n).map(Self::splat) } #[inline(always)] fn from_i32(n: i32) -> Option { <$elt>::from_i32(n).map(Self::splat) } #[inline(always)] fn from_usize(n: usize) -> Option { <$elt>::from_usize(n).map(Self::splat) } #[inline(always)] fn from_u8(n: u8) -> Option { <$elt>::from_u8(n).map(Self::splat) } #[inline(always)] fn from_u16(n: u16) -> Option { <$elt>::from_u16(n).map(Self::splat) } #[inline(always)] fn from_u32(n: u32) -> Option { <$elt>::from_u32(n).map(Self::splat) } #[inline(always)] fn from_f32(n: f32) -> Option { <$elt>::from_f32(n).map(Self::splat) } #[inline(always)] fn from_f64(n: f64) -> Option { <$elt>::from_f64(n).map(Self::splat) } } impl Zero for AutoSimd<$t> { #[inline(always)] fn zero() -> Self { AutoSimd([<$elt>::zero(); $lanes]) } #[inline(always)] fn is_zero(&self) -> bool { *self == Self::zero() } } impl One for AutoSimd<$t> { #[inline(always)] fn one() -> Self { AutoSimd([<$elt>::one(); $lanes]) } } impl Add> for AutoSimd<$t> { type Output = Self; #[inline(always)] fn add(self, rhs: Self) -> Self { self.zip_map(rhs, |x, y| x + y) } } impl Sub> for AutoSimd<$t> { type Output = Self; #[inline(always)] fn sub(self, rhs: Self) -> Self { self.zip_map(rhs, |x, y| x - y) } } impl Mul> for AutoSimd<$t> { type Output = Self; #[inline(always)] fn mul(self, rhs: Self) -> Self { self.zip_map(rhs, |x, y| x * y) } } impl Div> for AutoSimd<$t> { type Output = Self; #[inline(always)] fn div(self, rhs: Self) -> Self { self.zip_map(rhs, |x, y| x / y) } } impl Rem> for AutoSimd<$t> { type Output = Self; #[inline(always)] fn rem(self, rhs: Self) -> Self { self.zip_map(rhs, |x, y| x % y) } } impl AddAssign> for AutoSimd<$t> { #[inline(always)] fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } } impl SubAssign> for AutoSimd<$t> { #[inline(always)] fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } } impl DivAssign> for AutoSimd<$t> { #[inline(always)] fn div_assign(&mut self, rhs: Self) { *self = *self / rhs; } } impl MulAssign> for AutoSimd<$t> { #[inline(always)] fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; } } impl RemAssign> for AutoSimd<$t> { #[inline(always)] fn rem_assign(&mut self, rhs: Self) { *self = *self % rhs; } } impl SimdPartialOrd for AutoSimd<$t> { #[inline(always)] fn simd_gt(self, other: Self) -> Self::SimdBool { self.zip_map_bool(other, |x, y| x.simd_gt(y)) } #[inline(always)] fn simd_lt(self, other: Self) -> Self::SimdBool { self.zip_map_bool(other, |x, y| x.simd_lt(y)) } #[inline(always)] fn simd_ge(self, other: Self) -> Self::SimdBool { self.zip_map_bool(other, |x, y| x.simd_ge(y)) } #[inline(always)] fn simd_le(self, other: Self) -> Self::SimdBool { self.zip_map_bool(other, |x, y| x.simd_le(y)) } #[inline(always)] fn simd_eq(self, other: Self) -> Self::SimdBool { self.zip_map_bool(other, |x, y| x.simd_eq(y)) } #[inline(always)] fn simd_ne(self, other: Self) -> Self::SimdBool { self.zip_map_bool(other, |x, y| x.simd_ne(y)) } #[inline(always)] fn simd_max(self, other: Self) -> Self { self.zip_map(other, |x, y| x.simd_max(y)) } #[inline(always)] fn simd_min(self, other: Self) -> Self { self.zip_map(other, |x, y| x.simd_min(y)) } #[inline(always)] fn simd_clamp(self, min: Self, max: Self) -> Self { self.simd_max(min).simd_min(max) } #[inline(always)] fn simd_horizontal_min(self) -> Self::Element { ident_to_value!(); self.0[0] $(.simd_min(self.0[$i]))* } #[inline(always)] fn simd_horizontal_max(self) -> Self::Element { ident_to_value!(); self.0[0] $(.simd_max(self.0[$i]))* } } // impl MeetSemilattice for AutoSimd<$t> { // #[inline(always)] // fn meet(&self, other: &Self) -> Self { // AutoSimd(self.0.min(other.0)) // } // } // // impl JoinSemilattice for AutoSimd<$t> { // #[inline(always)] // fn join(&self, other: &Self) -> Self { // AutoSimd(self.0.max(other.0)) // } // } )*) ); macro_rules! impl_int_simd( ($($t: ty, $elt: ty, $lanes: expr, $bool: ty, $($i: ident),*;)*) => ($( impl_uint_simd!($t, $elt, $lanes, $bool $(, $i)*;); impl Neg for AutoSimd<$t> { type Output = Self; #[inline(always)] fn neg(self) -> Self { self.map(|x| -x) } } )*) ); macro_rules! impl_float_simd( ($($t: ty, $elt: ty, $lanes: expr, $int: ty, $bool: ty, $($i: ident),*;)*) => ($( impl_int_simd!($t, $elt, $lanes, $bool $(, $i)*;); // FIXME: this should be part of impl_int_simd // but those methods do not seem to be implemented // by packed_simd for integers. impl SimdSigned for AutoSimd<$t> { #[inline(always)] fn simd_abs(&self) -> Self { self.map(|x| x.simd_abs()) } #[inline(always)] fn simd_abs_sub(&self, other: &Self) -> Self { self.zip_map(*other, |x, y| x.simd_abs_sub(&y)) } #[inline(always)] fn simd_signum(&self) -> Self { self.map(|x| x.simd_signum()) } #[inline(always)] fn is_simd_positive(&self) -> Self::SimdBool { self.map_bool(|x| x.is_simd_positive()) } #[inline(always)] fn is_simd_negative(&self) -> Self::SimdBool { self.map_bool(|x| x.is_simd_negative()) } } impl Field for AutoSimd<$t> {} #[cfg(any(feature = "std", feature = "libm", feature = "libm_force", all(any(target_arch = "nvptx", target_arch = "nvptx64"), feature = "cuda")))] impl SimdRealField for AutoSimd<$t> { #[inline(always)] fn simd_atan2(self, other: Self) -> Self { self.zip_map(other, |x, y| x.simd_atan2(y)) } #[inline(always)] fn simd_copysign(self, sign: Self) -> Self { self.zip_map(sign, |me, sgn| me.simd_copysign(sgn)) } #[inline(always)] fn simd_default_epsilon() -> Self { Self::splat(<$elt>::default_epsilon()) } #[inline(always)] fn simd_pi() -> Self { Self::splat(<$elt>::simd_pi()) } #[inline(always)] fn simd_two_pi() -> Self { Self::splat(<$elt>::simd_two_pi()) } #[inline(always)] fn simd_frac_pi_2() -> Self { Self::splat(<$elt>::simd_frac_pi_2()) } #[inline(always)] fn simd_frac_pi_3() -> Self { Self::splat(<$elt>::simd_frac_pi_3()) } #[inline(always)] fn simd_frac_pi_4() -> Self { Self::splat(<$elt>::simd_frac_pi_4()) } #[inline(always)] fn simd_frac_pi_6() -> Self { Self::splat(<$elt>::simd_frac_pi_6()) } #[inline(always)] fn simd_frac_pi_8() -> Self { Self::splat(<$elt>::simd_frac_pi_8()) } #[inline(always)] fn simd_frac_1_pi() -> Self { Self::splat(<$elt>::simd_frac_1_pi()) } #[inline(always)] fn simd_frac_2_pi() -> Self { Self::splat(<$elt>::simd_frac_2_pi()) } #[inline(always)] fn simd_frac_2_sqrt_pi() -> Self { Self::splat(<$elt>::simd_frac_2_sqrt_pi()) } #[inline(always)] fn simd_e() -> Self { Self::splat(<$elt>::simd_e()) } #[inline(always)] fn simd_log2_e() -> Self { Self::splat(<$elt>::simd_log2_e()) } #[inline(always)] fn simd_log10_e() -> Self { Self::splat(<$elt>::simd_log10_e() ) } #[inline(always)] fn simd_ln_2() -> Self { Self::splat(<$elt>::simd_ln_2()) } #[inline(always)] fn simd_ln_10() -> Self { Self::splat(<$elt>::simd_ln_10()) } } #[cfg(any(feature = "std", feature = "libm", feature = "libm_force", all(any(target_arch = "nvptx", target_arch = "nvptx64"), feature = "cuda")))] impl SimdComplexField for AutoSimd<$t> { type SimdRealField = Self; #[inline(always)] fn simd_horizontal_sum(self) -> Self::Element { self.0.iter().sum() } #[inline(always)] fn simd_horizontal_product(self) -> Self::Element { self.0.iter().product() } #[inline(always)] fn from_simd_real(re: Self::SimdRealField) -> Self { re } #[inline(always)] fn simd_real(self) -> Self::SimdRealField { self } #[inline(always)] fn simd_imaginary(self) -> Self::SimdRealField { Self::zero() } #[inline(always)] fn simd_norm1(self) -> Self::SimdRealField { self.map(|x| x.simd_norm1()) } #[inline(always)] fn simd_modulus(self) -> Self::SimdRealField { self.map(|x| x.simd_modulus()) } #[inline(always)] fn simd_modulus_squared(self) -> Self::SimdRealField { self.map(|x| x.simd_modulus_squared()) } #[inline(always)] fn simd_argument(self) -> Self::SimdRealField { self.map(|x| x.simd_argument()) } #[inline(always)] fn simd_to_exp(self) -> (Self::SimdRealField, Self) { let ge = self.simd_ge(Self::one()); let exp = Self::one().select(ge, -Self::one()); (self * exp, exp) } #[inline(always)] fn simd_recip(self) -> Self { self.map(|x| x.simd_recip()) } #[inline(always)] fn simd_conjugate(self) -> Self { self.map(|x| x.simd_conjugate()) } #[inline(always)] fn simd_scale(self, factor: Self::SimdRealField) -> Self { self.zip_map(factor, |x, y| x.simd_scale(y)) } #[inline(always)] fn simd_unscale(self, factor: Self::SimdRealField) -> Self { self.zip_map(factor, |x, y| x.simd_unscale(y)) } #[inline(always)] fn simd_floor(self) -> Self { self.map(|e| e.simd_floor()) } #[inline(always)] fn simd_ceil(self) -> Self { self.map(|e| e.simd_ceil()) } #[inline(always)] fn simd_round(self) -> Self { self.map(|e| e.simd_round()) } #[inline(always)] fn simd_trunc(self) -> Self { self.map(|e| e.simd_trunc()) } #[inline(always)] fn simd_fract(self) -> Self { self.map(|e| e.simd_fract()) } #[inline(always)] fn simd_abs(self) -> Self { self.map(|e| e.simd_abs()) } #[inline(always)] fn simd_signum(self) -> Self { self.map(|e| e.simd_signum()) } #[inline(always)] fn simd_mul_add(self, a: Self, b: Self) -> Self { self.zip_zip_map(a, b, |x, y, z| x.simd_mul_add(y, z)) } #[inline(always)] fn simd_powi(self, n: i32) -> Self { self.map(|e| e.simd_powi(n)) } #[inline(always)] fn simd_powf(self, n: Self) -> Self { self.zip_map(n, |x, y| x.simd_powf(y)) } #[inline(always)] fn simd_powc(self, n: Self) -> Self { self.zip_map(n, |x, y| x.simd_powc(y)) } #[inline(always)] fn simd_sqrt(self) -> Self { self.map(|x| x.simd_sqrt()) } #[inline(always)] fn simd_exp(self) -> Self { self.map(|x| x.simd_exp()) } #[inline(always)] fn simd_exp2(self) -> Self { self.map(|x| x.simd_exp2()) } #[inline(always)] fn simd_exp_m1(self) -> Self { self.map(|x| x.simd_exp_m1()) } #[inline(always)] fn simd_ln_1p(self) -> Self { self.map(|x| x.simd_ln_1p()) } #[inline(always)] fn simd_ln(self) -> Self { self.map(|x| x.simd_ln()) } #[inline(always)] fn simd_log(self, base: Self) -> Self { self.zip_map(base, |x, y| x.simd_log(y)) } #[inline(always)] fn simd_log2(self) -> Self { self.map(|x| x.simd_log2()) } #[inline(always)] fn simd_log10(self) -> Self { self.map(|x| x.simd_log10()) } #[inline(always)] fn simd_cbrt(self) -> Self { self.map(|x| x.simd_cbrt()) } #[inline(always)] fn simd_hypot(self, other: Self) -> Self::SimdRealField { self.zip_map(other, |x, y| x.simd_hypot(y)) } #[inline(always)] fn simd_sin(self) -> Self { self.map(|x| x.simd_sin()) } #[inline(always)] fn simd_cos(self) -> Self { self.map(|x| x.simd_cos()) } #[inline(always)] fn simd_tan(self) -> Self { self.map(|x| x.simd_tan()) } #[inline(always)] fn simd_asin(self) -> Self { self.map(|x| x.simd_asin()) } #[inline(always)] fn simd_acos(self) -> Self { self.map(|x| x.simd_acos()) } #[inline(always)] fn simd_atan(self) -> Self { self.map(|x| x.simd_atan()) } #[inline(always)] fn simd_sin_cos(self) -> (Self, Self) { (self.simd_sin(), self.simd_cos()) } // #[inline(always] // fn simd_exp_m1(self) -> Self { // $libm::exp_m1(self) // } // // #[inline(always] // fn simd_ln_1p(self) -> Self { // $libm::ln_1p(self) // } // #[inline(always)] fn simd_sinh(self) -> Self { self.map(|x| x.simd_sinh()) } #[inline(always)] fn simd_cosh(self) -> Self { self.map(|x| x.simd_cosh()) } #[inline(always)] fn simd_tanh(self) -> Self { self.map(|x| x.simd_tanh()) } #[inline(always)] fn simd_asinh(self) -> Self { self.map(|x| x.simd_asinh()) } #[inline(always)] fn simd_acosh(self) -> Self { self.map(|x| x.simd_acosh()) } #[inline(always)] fn simd_atanh(self) -> Self { self.map(|x| x.simd_atanh()) } } // NOTE: most of the impls in there are copy-paste from the implementation of // ComplexField for num_complex::Complex. Unfortunately, we can't reuse the implementations // so easily. #[cfg(any(feature = "std", feature = "libm", feature = "libm_force", all(any(target_arch = "nvptx", target_arch = "nvptx64"), feature = "cuda")))] impl SimdComplexField for num_complex::Complex> { type SimdRealField = AutoSimd<$t>; #[inline(always)] fn simd_horizontal_sum(self) -> Self::Element { num_complex::Complex::new(self.re.simd_horizontal_sum(), self.im.simd_horizontal_sum()) } #[inline(always)] fn simd_horizontal_product(self) -> Self::Element { let mut prod = self.extract(0); for ii in 1..$lanes { prod = prod * self.extract(ii) } prod } #[inline] fn from_simd_real(re: Self::SimdRealField) -> Self { Self::new(re, Self::SimdRealField::zero()) } #[inline] fn simd_real(self) -> Self::SimdRealField { self.re } #[inline] fn simd_imaginary(self) -> Self::SimdRealField { self.im } #[inline] fn simd_argument(self) -> Self::SimdRealField { self.im.simd_atan2(self.re) } #[inline] fn simd_modulus(self) -> Self::SimdRealField { self.re.simd_hypot(self.im) } #[inline] fn simd_modulus_squared(self) -> Self::SimdRealField { self.re * self.re + self.im * self.im } #[inline] fn simd_norm1(self) -> Self::SimdRealField { self.re.simd_abs() + self.im.simd_abs() } #[inline] fn simd_recip(self) -> Self { Self::one() / self } #[inline] fn simd_conjugate(self) -> Self { self.conj() } #[inline] fn simd_scale(self, factor: Self::SimdRealField) -> Self { self * factor } #[inline] fn simd_unscale(self, factor: Self::SimdRealField) -> Self { self / factor } #[inline] fn simd_floor(self) -> Self { Self::new(self.re.simd_floor(), self.im.simd_floor()) } #[inline] fn simd_ceil(self) -> Self { Self::new(self.re.simd_ceil(), self.im.simd_ceil()) } #[inline] fn simd_round(self) -> Self { Self::new(self.re.simd_round(), self.im.simd_round()) } #[inline] fn simd_trunc(self) -> Self { Self::new(self.re.simd_trunc(), self.im.simd_trunc()) } #[inline] fn simd_fract(self) -> Self { Self::new(self.re.simd_fract(), self.im.simd_fract()) } #[inline] fn simd_mul_add(self, a: Self, b: Self) -> Self { self * a + b } #[inline] fn simd_abs(self) -> Self::SimdRealField { self.simd_modulus() } #[inline] fn simd_exp2(self) -> Self { let _2 = AutoSimd::<$t>::one() + AutoSimd::<$t>::one(); num_complex::Complex::new(_2, AutoSimd::<$t>::zero()).simd_powc(self) } #[inline] fn simd_exp_m1(self) -> Self { self.simd_exp() - Self::one() } #[inline] fn simd_ln_1p(self) -> Self { (Self::one() + self).simd_ln() } #[inline] fn simd_log2(self) -> Self { let _2 = AutoSimd::<$t>::one() + AutoSimd::<$t>::one(); self.simd_log(_2) } #[inline] fn simd_log10(self) -> Self { let _10 = AutoSimd::<$t>::from_subset(&10.0f64); self.simd_log(_10) } #[inline] fn simd_cbrt(self) -> Self { let one_third = AutoSimd::<$t>::from_subset(&(1.0 / 3.0)); self.simd_powf(one_third) } #[inline] fn simd_powi(self, n: i32) -> Self { // FIXME: is there a more accurate solution? let n = AutoSimd::<$t>::from_subset(&(n as f64)); self.simd_powf(n) } /* * * * Unfortunately we are forced to copy-paste all * those impls from https://github.com/rust-num/num-complex/blob/master/src/lib.rs * to avoid requiring `std`. * * */ /// Computes `e^(self)`, where `e` is the base of the natural logarithm. #[inline] fn simd_exp(self) -> Self { // formula: e^(a + bi) = e^a (cos(b) + i*sin(b)) // = from_polar(e^a, b) simd_complex_from_polar(self.re.simd_exp(), self.im) } /// Computes the principal value of natural logarithm of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0]`, continuous from above. /// /// The branch satisfies `-π ≤ arg(ln(z)) ≤ π`. #[inline] fn simd_ln(self) -> Self { // formula: ln(z) = ln|z| + i*arg(z) let (r, theta) = self.simd_to_polar(); Self::new(r.simd_ln(), theta) } /// Computes the principal value of the square root of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0)`, continuous from above. /// /// The branch satisfies `-π/2 ≤ arg(sqrt(z)) ≤ π/2`. #[inline] fn simd_sqrt(self) -> Self { // formula: sqrt(r e^(it)) = sqrt(r) e^(it/2) let two = AutoSimd::<$t>::one() + AutoSimd::<$t>::one(); let (r, theta) = self.simd_to_polar(); simd_complex_from_polar(r.simd_sqrt(), theta / two) } #[inline] fn simd_hypot(self, b: Self) -> Self::SimdRealField { (self.simd_modulus_squared() + b.simd_modulus_squared()).simd_sqrt() } /// Raises `self` to a floating point power. #[inline] fn simd_powf(self, exp: Self::SimdRealField) -> Self { // formula: x^y = (ρ e^(i θ))^y = ρ^y e^(i θ y) // = from_polar(ρ^y, θ y) let (r, theta) = self.simd_to_polar(); simd_complex_from_polar(r.simd_powf(exp), theta * exp) } /// Returns the logarithm of `self` with respect to an arbitrary base. #[inline] fn simd_log(self, base: AutoSimd<$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.simd_to_polar(); Self::new(r.simd_log(base), theta / base.simd_ln()) } /// Raises `self` to a complex power. #[inline] fn simd_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) // = ρ^c e^(−d θ) e^(i c θ) ρ^(i d) // = p^c e^(−d θ) (cos(c θ) // + i sin(c θ)) (cos(d ln(ρ)) + i sin(d ln(ρ))) // = p^c e^(−d θ) ( // cos(c θ) cos(d ln(ρ)) − sin(c θ) sin(d ln(ρ)) // + i(cos(c θ) sin(d ln(ρ)) + sin(c θ) cos(d ln(ρ)))) // = 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.simd_to_polar(); simd_complex_from_polar( r.simd_powf(exp.re) * (-exp.im * theta).simd_exp(), exp.re * theta + exp.im * r.simd_ln(), ) } /* /// Raises a floating point number to the complex power `self`. #[inline] fn simd_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)) Self::from_polar(&base.powf(self.re), &(self.im * base.ln())) } */ /// Computes the sine of `self`. #[inline] fn simd_sin(self) -> Self { // formula: sin(a + bi) = sin(a)cosh(b) + i*cos(a)sinh(b) Self::new( self.re.simd_sin() * self.im.simd_cosh(), self.re.simd_cos() * self.im.simd_sinh(), ) } /// Computes the cosine of `self`. #[inline] fn simd_cos(self) -> Self { // formula: cos(a + bi) = cos(a)cosh(b) - i*sin(a)sinh(b) Self::new( self.re.simd_cos() * self.im.simd_cosh(), -self.re.simd_sin() * self.im.simd_sinh(), ) } #[inline] fn simd_sin_cos(self) -> (Self, Self) { let (rsin, rcos) = self.re.simd_sin_cos(); let (isinh, icosh) = self.im.simd_sinh_cosh(); let sin = Self::new(rsin * icosh, rcos * isinh); let cos = Self::new(rcos * icosh, -rsin * isinh); (sin, cos) } /// Computes the tangent of `self`. #[inline] fn simd_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); Self::new(two_re.simd_sin(), two_im.simd_sinh()).unscale(two_re.simd_cos() + two_im.simd_cosh()) } /// Computes the principal value of the inverse sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Re(asin(z)) ≤ π/2`. #[inline] fn simd_asin(self) -> Self { // formula: arcsin(z) = -i ln(sqrt(1-z^2) + iz) let i = Self::i(); -i * ((Self::one() - self * self).simd_sqrt() + i * self).simd_ln() } /// Computes the principal value of the inverse cosine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `0 ≤ Re(acos(z)) ≤ π`. #[inline] fn simd_acos(self) -> Self { // formula: arccos(z) = -i ln(i sqrt(1-z^2) + z) let i = Self::i(); -i * (i * (Self::one() - self * self).simd_sqrt() + self).simd_ln() } /// Computes the principal value of the inverse tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i]`, continuous from the left. /// * `[i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Re(atan(z)) ≤ π/2`. #[inline] fn simd_atan(self) -> Self { // formula: arctan(z) = (ln(1+iz) - ln(1-iz))/(2i) let i = Self::i(); let one = Self::one(); let two = one + one; if self == i { return Self::new(AutoSimd::<$t>::zero(), AutoSimd::<$t>::one() / AutoSimd::<$t>::zero()); } else if self == -i { return Self::new(AutoSimd::<$t>::zero(), -AutoSimd::<$t>::one() / AutoSimd::<$t>::zero()); } ((one + i * self).simd_ln() - (one - i * self).simd_ln()) / (two * i) } /// Computes the hyperbolic sine of `self`. #[inline] fn simd_sinh(self) -> Self { // formula: sinh(a + bi) = sinh(a)cos(b) + i*cosh(a)sin(b) Self::new( self.re.simd_sinh() * self.im.simd_cos(), self.re.simd_cosh() * self.im.simd_sin(), ) } /// Computes the hyperbolic cosine of `self`. #[inline] fn simd_cosh(self) -> Self { // formula: cosh(a + bi) = cosh(a)cos(b) + i*sinh(a)sin(b) Self::new( self.re.simd_cosh() * self.im.simd_cos(), self.re.simd_sinh() * self.im.simd_sin(), ) } #[inline] fn simd_sinh_cosh(self) -> (Self, Self) { let (rsinh, rcosh) = self.re.simd_sinh_cosh(); let (isin, icos) = self.im.simd_sin_cos(); let sin = Self::new(rsinh * icos, rcosh * isin); let cos = Self::new(rcosh * icos, rsinh * isin); (sin, cos) } /// Computes the hyperbolic tangent of `self`. #[inline] fn simd_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); Self::new(two_re.simd_sinh(), two_im.simd_sin()).unscale(two_re.simd_cosh() + two_im.simd_cos()) } /// Computes the principal value of inverse hyperbolic sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i)`, continuous from the left. /// * `(i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Im(asinh(z)) ≤ π/2`. #[inline] fn simd_asinh(self) -> Self { // formula: arcsinh(z) = ln(z + sqrt(1+z^2)) let one = Self::one(); (self + (one + self * self).simd_sqrt()).simd_ln() } /// Computes the principal value of inverse hyperbolic cosine of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 1)`, continuous from above. /// /// The branch satisfies `-π ≤ Im(acosh(z)) ≤ π` and `0 ≤ Re(acosh(z)) < ∞`. #[inline] fn simd_acosh(self) -> Self { // formula: arccosh(z) = 2 ln(sqrt((z+1)/2) + sqrt((z-1)/2)) let one = Self::one(); let two = one + one; two * (((self + one) / two).simd_sqrt() + ((self - one) / two).simd_sqrt()).simd_ln() } /// Computes the principal value of inverse hyperbolic tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1]`, continuous from above. /// * `[1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Im(atanh(z)) ≤ π/2`. #[inline] fn simd_atanh(self) -> Self { // formula: arctanh(z) = (ln(1+z) - ln(1-z))/2 let one = Self::one(); let two = one + one; if self == one { return Self::new(AutoSimd::<$t>::one() / AutoSimd::<$t>::zero(), AutoSimd::<$t>::zero()); } else if self == -one { return Self::new(-AutoSimd::<$t>::one() / AutoSimd::<$t>::zero(), AutoSimd::<$t>::zero()); } ((one + self).simd_ln() - (one - self).simd_ln()) / two } } )*) ); #[inline] fn simd_complex_from_polar(r: N, theta: N) -> num_complex::Complex { num_complex::Complex::new(r.clone() * theta.clone().simd_cos(), r * theta.simd_sin()) } impl_float_simd!( [f32; 2], f32, 2, [i32; 2], AutoBoolx2, _0, _1; [f32; 4], f32, 4, [i32; 4], AutoBoolx4, _0, _1, _2, _3; [f32; 8], f32, 8, [i32; 8], AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [f32; 16], f32, 16, [i32; 16], AutoBoolx16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [f64; 2], f64, 2, [i64; 2], AutoBoolx2, _0, _1; [f64; 4], f64, 4, [i64; 4], AutoBoolx4, _0, _1, _2, _3; [f64; 8], f64, 8, [i64; 8], AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; ); impl_int_simd!( [i128; 1], i128, 1, AutoBoolx1, _0; [i128; 2], i128, 2, AutoBoolx2, _0, _1; [i128; 4], i128, 4, AutoBoolx4, _0, _1, _2, _3; [i16; 2], i16, 2, AutoBoolx2, _0, _1; [i16; 4], i16, 4, AutoBoolx4, _0, _1, _2, _3; [i16; 8], i16, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [i16; 16], i16, 16, AutoBoolx16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [i16; 32], i16, 32, AutoBoolx32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; [i32; 2], i32, 2, AutoBoolx2, _0, _1; [i32; 4], i32, 4, AutoBoolx4, _0, _1, _2, _3; [i32; 8], i32, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [i32; 16], i32, 16, AutoBoolx16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [i64; 2], i64, 2, AutoBoolx2, _0, _1; [i64; 4], i64, 4, AutoBoolx4, _0, _1, _2, _3; [i64; 8], i64, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [i8; 2], i8, 2, AutoBoolx2, _0, _1; [i8; 4], i8, 4, AutoBoolx4, _0, _1, _2, _3; [i8; 8], i8, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [i8; 16], i8, 16, AutoBoolx16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [i8; 32], i8, 32, AutoBoolx32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; // [i8; 64], i8, 64, AutoBoolx64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; [isize; 2], isize, 2, AutoBoolx2, _0, _1; [isize; 4], isize, 4, AutoBoolx4, _0, _1, _2, _3; [isize; 8], isize, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; ); impl_uint_simd!( [u128; 1], u128, 1, AutoBoolx1, _0; [u128; 2], u128, 2, AutoBoolx2, _0, _1; [u128; 4], u128, 4, AutoBoolx4, _0, _1, _2, _3; [u16; 2], u16, 2, AutoBoolx2, _0, _1; [u16; 4], u16, 4, AutoBoolx4, _0, _1, _2, _3; [u16; 8], u16, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [u16; 16], u16, 16, AutoBoolx16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [u16; 32], u16, 32, AutoBoolx32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; [u32; 2], u32, 2, AutoBoolx2, _0, _1; [u32; 4], u32, 4, AutoBoolx4, _0, _1, _2, _3; [u32; 8], u32, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [u32; 16], u32, 16, AutoBoolx16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [u64; 2], u64, 2, AutoBoolx2, _0, _1; [u64; 4], u64, 4, AutoBoolx4, _0, _1, _2, _3; [u64; 8], u64, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [u8; 2], u8, 2, AutoBoolx2, _0, _1; [u8; 4], u8, 4, AutoBoolx4, _0, _1, _2, _3; [u8; 8], u8, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; [u8; 16], u8, 16, AutoBoolx16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [u8; 32], u8, 32, AutoBoolx32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; // [u8; 64], u8, 64, AutoBoolx64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; [usize; 2], usize, 2, AutoBoolx2, _0, _1; [usize; 4], usize, 4, AutoBoolx4, _0, _1, _2, _3; [usize; 8], usize, 8, AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; ); impl_bool_simd!( [bool; 1], 1, _0; [bool; 2], 2, _0, _1; [bool; 4], 4, _0, _1, _2, _3; [bool; 8], 8, _0, _1, _2, _3, _4, _5, _6, _7; [bool; 16], 16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [bool; 32], 32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; // [bool; 64], 64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; ); // // NOTE: the following does not work because of the orphan rules. // //macro_rules! impl_simd_complex_from( // ($($t: ty, $elt: ty $(, $i: expr)*;)*) => ($( // impl From<[num_complex::Complex<$elt>; $lanes]> for num_complex::Complex> { // #[inline(always)] // fn from(vals: [num_complex::Complex<$elt>; $lanes]) -> Self { // num_complex::Complex { // re: <$t>::from([$(vals[$i].re),*]), // im: <$t>::from([$(vals[$i].im),*]), // } // } // } // )*) //); // //impl_simd_complex_from!( // [f32; 2], f32, 0, 1; // [f32; 4], f32, 0, 1, 2, 3; // [f32; 8], f32, 0, 1, 2, 3, 4, 5, 6, 7; // [f32; 16], f32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15; //); ////////////////////////////////////////// // Aliases // ////////////////////////////////////////// pub type AutoF32x2 = AutoSimd<[f32; 2]>; pub type AutoF32x4 = AutoSimd<[f32; 4]>; pub type AutoF32x8 = AutoSimd<[f32; 8]>; pub type AutoF32x16 = AutoSimd<[f32; 16]>; pub type AutoF64x2 = AutoSimd<[f64; 2]>; pub type AutoF64x4 = AutoSimd<[f64; 4]>; pub type AutoF64x8 = AutoSimd<[f64; 8]>; pub type AutoI128x1 = AutoSimd<[i128; 1]>; pub type AutoI128x2 = AutoSimd<[i128; 2]>; pub type AutoI128x4 = AutoSimd<[i128; 4]>; pub type AutoI16x2 = AutoSimd<[i16; 2]>; pub type AutoI16x4 = AutoSimd<[i16; 4]>; pub type AutoI16x8 = AutoSimd<[i16; 8]>; pub type AutoI16x16 = AutoSimd<[i16; 16]>; pub type AutoI16x32 = AutoSimd<[i16; 32]>; pub type AutoI32x2 = AutoSimd<[i32; 2]>; pub type AutoI32x4 = AutoSimd<[i32; 4]>; pub type AutoI32x8 = AutoSimd<[i32; 8]>; pub type AutoI32x16 = AutoSimd<[i32; 16]>; pub type AutoI64x2 = AutoSimd<[i64; 2]>; pub type AutoI64x4 = AutoSimd<[i64; 4]>; pub type AutoI64x8 = AutoSimd<[i64; 8]>; pub type AutoI8x2 = AutoSimd<[i8; 2]>; pub type AutoI8x4 = AutoSimd<[i8; 4]>; pub type AutoI8x8 = AutoSimd<[i8; 8]>; pub type AutoI8x16 = AutoSimd<[i8; 16]>; pub type AutoI8x32 = AutoSimd<[i8; 32]>; // pub type AutoI8x64 = AutoSimd<[i8; 64]>; pub type AutoIsizex2 = AutoSimd<[isize; 2]>; pub type AutoIsizex4 = AutoSimd<[isize; 4]>; pub type AutoIsizex8 = AutoSimd<[isize; 8]>; pub type AutoU128x1 = AutoSimd<[u128; 1]>; pub type AutoU128x2 = AutoSimd<[u128; 2]>; pub type AutoU128x4 = AutoSimd<[u128; 4]>; pub type AutoU16x2 = AutoSimd<[u16; 2]>; pub type AutoU16x4 = AutoSimd<[u16; 4]>; pub type AutoU16x8 = AutoSimd<[u16; 8]>; pub type AutoU16x16 = AutoSimd<[u16; 16]>; pub type AutoU16x32 = AutoSimd<[u16; 32]>; pub type AutoU32x2 = AutoSimd<[u32; 2]>; pub type AutoU32x4 = AutoSimd<[u32; 4]>; pub type AutoU32x8 = AutoSimd<[u32; 8]>; pub type AutoU32x16 = AutoSimd<[u32; 16]>; pub type AutoU64x2 = AutoSimd<[u64; 2]>; pub type AutoU64x4 = AutoSimd<[u64; 4]>; pub type AutoU64x8 = AutoSimd<[u64; 8]>; pub type AutoU8x2 = AutoSimd<[u8; 2]>; pub type AutoU8x4 = AutoSimd<[u8; 4]>; pub type AutoU8x8 = AutoSimd<[u8; 8]>; pub type AutoU8x16 = AutoSimd<[u8; 16]>; pub type AutoU8x32 = AutoSimd<[u8; 32]>; // pub type AutoU8x64 = AutoSimd<[u8; 64]>; pub type AutoUsizex2 = AutoSimd<[usize; 2]>; pub type AutoUsizex4 = AutoSimd<[usize; 4]>; pub type AutoUsizex8 = AutoSimd<[usize; 8]>; pub type AutoBoolx1 = AutoSimd<[bool; 1]>; pub type AutoBoolx16 = AutoSimd<[bool; 16]>; pub type AutoBoolx2 = AutoSimd<[bool; 2]>; pub type AutoBoolx32 = AutoSimd<[bool; 32]>; pub type AutoBoolx4 = AutoSimd<[bool; 4]>; // pub type AutoBoolx64 = AutoSimd<[bool; 64]>; pub type AutoBoolx8 = AutoSimd<[bool; 8]>; /* * Helper trait to transform an array. */ trait ArrTransform: SimdValue { fn map(self, f: impl Fn(Self::Element) -> Self::Element) -> Self; fn zip_map( self, other: Self, f: impl Fn(Self::Element, Self::Element) -> Self::Element, ) -> Self; fn zip_zip_map( self, b: Self, c: Self, f: impl Fn(Self::Element, Self::Element, Self::Element) -> Self::Element, ) -> Self; fn map_bool(self, f: impl Fn(Self::Element) -> bool) -> Self::SimdBool; fn zip_map_bool( self, other: Self, f: impl Fn(Self::Element, Self::Element) -> bool, ) -> Self::SimdBool; } simba-0.8.1/src/simd/mod.rs000064400000000000000000000015660072674642500136350ustar 00000000000000//! Traits implemented by SIMD types and non-SIMD types. pub use self::auto_simd_impl::*; #[cfg(feature = "packed_simd")] pub use self::packed_simd_impl::*; pub use self::simd_bool::SimdBool; pub use self::simd_complex::SimdComplexField; pub use self::simd_option::SimdOption; pub use self::simd_partial_ord::SimdPartialOrd; pub use self::simd_real::SimdRealField; pub use self::simd_signed::SimdSigned; pub use self::simd_value::{PrimitiveSimdValue, SimdValue}; #[cfg(feature = "wide")] pub use self::wide_simd_impl::{ WideBoolF32x4, WideBoolF32x8, WideBoolF64x4, WideF32x4, WideF32x8, WideF64x4, }; mod auto_simd_impl; #[cfg(feature = "packed_simd")] mod packed_simd_impl; #[cfg(feature = "rand")] mod rand_impl; mod simd_bool; mod simd_complex; mod simd_option; mod simd_partial_ord; mod simd_real; mod simd_signed; mod simd_value; #[cfg(feature = "wide")] mod wide_simd_impl; simba-0.8.1/src/simd/packed_simd_impl.rs000064400000000000000000001613120072674642500163360ustar 00000000000000#![allow(missing_docs)] #![allow(non_camel_case_types)] // For the simd type aliases. //! Traits for SIMD values. use crate::scalar::{ComplexField, Field, SubsetOf, SupersetOf}; use crate::simd::{ PrimitiveSimdValue, SimdBool, SimdComplexField, SimdPartialOrd, SimdRealField, SimdSigned, SimdValue, }; use approx::AbsDiffEq; #[cfg(feature = "decimal")] use decimal::d128; use num::{FromPrimitive, Num, One, Zero}; use std::{ fmt, ops::{ Add, AddAssign, BitAnd, BitOr, BitXor, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Sub, SubAssign, }, }; // This is a hack to allow use to reuse `_0` as integers or as identifier, // depending on whether or not `ident_to_value` has been called in scope. // This helps writing macros that define both `::new` and `From([T; lanes()])`. macro_rules! ident_to_value( () => { const _0: usize = 0; const _1: usize = 1; const _2: usize = 2; const _3: usize = 3; const _4: usize = 4; const _5: usize = 5; const _6: usize = 6; const _7: usize = 7; const _8: usize = 8; const _9: usize = 9; const _10: usize = 10; const _11: usize = 11; const _12: usize = 12; const _13: usize = 13; const _14: usize = 14; const _15: usize = 15; const _16: usize = 16; const _17: usize = 17; const _18: usize = 18; const _19: usize = 19; const _20: usize = 20; const _21: usize = 21; const _22: usize = 22; const _23: usize = 23; const _24: usize = 24; const _25: usize = 25; const _26: usize = 26; const _27: usize = 27; const _28: usize = 28; const _29: usize = 29; const _30: usize = 30; const _31: usize = 31; const _32: usize = 32; const _33: usize = 33; const _34: usize = 34; const _35: usize = 35; const _36: usize = 36; const _37: usize = 37; const _38: usize = 38; const _39: usize = 39; const _40: usize = 40; const _41: usize = 41; const _42: usize = 42; const _43: usize = 43; const _44: usize = 44; const _45: usize = 45; const _46: usize = 46; const _47: usize = 47; const _48: usize = 48; const _49: usize = 49; const _50: usize = 50; const _51: usize = 51; const _52: usize = 52; const _53: usize = 53; const _54: usize = 54; const _55: usize = 55; const _56: usize = 56; const _57: usize = 57; const _58: usize = 58; const _59: usize = 59; const _60: usize = 60; const _61: usize = 61; const _62: usize = 62; const _63: usize = 63; } ); /// An Simd structure that implements all the relevant traits from `num` an `simba`. /// /// This is needed to overcome the orphan rules. #[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Simd(pub N); macro_rules! impl_bool_simd( ($($t: ty, $($i: ident),*;)*) => {$( impl_simd_value!($t, bool, Simd<$t> $(, $i)*;); impl From<[bool; <$t>::lanes()]> for Simd<$t> { #[inline(always)] fn from(vals: [bool; <$t>::lanes()]) -> Self { ident_to_value!(); Simd(<$t>::new($(vals[$i]),*)) } } impl Not for Simd<$t> { type Output = Self; #[inline] fn not(self) -> Self { Self(!self.0) } } impl BitAnd> for Simd<$t> { type Output = Self; fn bitand(self, rhs: Self) -> Self { Simd(self.0.bitand(rhs.0)) } } impl BitOr> for Simd<$t> { type Output = Self; fn bitor(self, rhs: Self) -> Self { Simd(self.0.bitor(rhs.0)) } } impl BitXor> for Simd<$t> { type Output = Self; fn bitxor(self, rhs: Self) -> Self { Simd(self.0.bitxor(rhs.0)) } } impl SimdBool for Simd<$t> { #[inline(always)] fn bitmask(self) -> u64 { self.0.bitmask() as u64 } #[inline(always)] fn and(self) -> bool { self.0.and() } #[inline(always)] fn or(self) -> bool { self.0.or() } #[inline(always)] fn xor(self) -> bool { self.0.xor() } #[inline(always)] fn all(self) -> bool { self.0.all() } #[inline(always)] fn any(self) -> bool { self.0.any() } #[inline(always)] fn none(self) -> bool { self.0.none() } #[inline(always)] fn if_else>( self, if_value: impl FnOnce() -> Res, else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_value(); a.select(self, b) } #[inline(always)] fn if_else2>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_if.1(); let c = else_value(); let cond_a = self; let cond_b = else_if.0(); a.select(cond_a, b.select(cond_b, c)) } #[inline(always)] fn if_else3>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_if.1(); let c = else_else_if.1(); let d = else_value(); let cond_a = self; let cond_b = else_if.0(); let cond_c = else_else_if.0(); a.select(cond_a, b.select(cond_b, c.select(cond_c, d))) } } )*} ); macro_rules! impl_scalar_subset_of_simd( ($($t: ty),*) => {$( impl SubsetOf> for $t where Simd: SimdValue + Copy, as SimdValue>::Element: SupersetOf<$t> + PartialEq, { #[inline(always)] fn to_superset(&self) -> Simd { Simd::::splat( as SimdValue>::Element::from_subset(self)) } #[inline(always)] fn from_superset_unchecked(element: &Simd) -> $t { element.extract(0).to_subset_unchecked() } #[inline(always)] fn is_in_subset(c: &Simd) -> bool { let elt0 = c.extract(0); elt0.is_in_subset() && (1..Simd::::lanes()).all(|i| c.extract(i) == elt0) } } )*} ); impl_scalar_subset_of_simd!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64); #[cfg(feature = "decimal")] impl_scalar_subset_of_simd!(d128); macro_rules! impl_simd_value( ($($t: ty, $elt: ty, $bool: ty, $($i: ident),*;)*) => ($( impl fmt::Display for Simd<$t> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if Self::lanes() == 1 { return self.extract(0).fmt(f); } write!(f, "({}", self.extract(0))?; for i in 1..Self::lanes() { write!(f, ", {}", self.extract(i))?; } write!(f, ")") } } impl Simd<$t> { #[inline] pub fn new($($i: $elt),*) -> Self { Simd(<$t>::new($($i),*)) } } impl PrimitiveSimdValue for Simd<$t> {} impl SimdValue for Simd<$t> { type Element = $elt; type SimdBool = $bool; #[inline(always)] fn lanes() -> usize { <$t>::lanes() } #[inline(always)] fn splat(val: Self::Element) -> Self { Simd(<$t>::splat(val)) } #[inline(always)] fn extract(&self, i: usize) -> Self::Element { <$t>::extract(self.0, i) } #[inline(always)] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { <$t>::extract_unchecked(self.0, i) } #[inline(always)] fn replace(&mut self, i: usize, val: Self::Element) { *self = Simd(<$t>::replace(self.0, i, val)) } #[inline(always)] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { *self = Simd(<$t>::replace_unchecked(self.0, i, val)) } #[inline(always)] fn select(self, cond: Self::SimdBool, other: Self) -> Self { Self(cond.0.select(self.0, other.0)) } } )*) ); macro_rules! impl_uint_simd( ($($t: ty, $elt: ty, $bool: ty, $($i: ident),*;)*) => ($( impl_simd_value!($t, $elt, $bool $(, $i)*;); impl Simd<$t> { /// Instantiates a new vector with the values of the `slice`. /// /// # Panics /// /// If `slice.len() < Self::lanes()`. #[inline] pub fn from_slice_unaligned(slice: &[$elt]) -> Self { Simd(<$t>::from_slice_unaligned(slice)) } } impl From<[$elt; <$t>::lanes()]> for Simd<$t> { #[inline(always)] fn from(vals: [$elt; <$t>::lanes()]) -> Self { Simd(<$t>::from(vals)) } } impl From> for [$elt; <$t>::lanes()] { #[inline(always)] fn from(val: Simd<$t>) -> [$elt; <$t>::lanes()] { let mut res = [<$elt>::zero(); <$t>::lanes()]; val.0.write_to_slice_unaligned(&mut res[..]); res } } impl SubsetOf> for Simd<$t> { #[inline(always)] fn to_superset(&self) -> Self { *self } #[inline(always)] fn from_superset(element: &Self) -> Option { Some(*element) } #[inline(always)] fn from_superset_unchecked(element: &Self) -> Self { *element } #[inline(always)] fn is_in_subset(_: &Self) -> bool { true } } impl Num for Simd<$t> { type FromStrRadixErr = <$elt as Num>::FromStrRadixErr; #[inline(always)] fn from_str_radix(str: &str, radix: u32) -> Result { <$elt>::from_str_radix(str, radix).map(Self::splat) } } impl FromPrimitive for Simd<$t> { #[inline(always)] fn from_i64(n: i64) -> Option { <$elt>::from_i64(n).map(Self::splat) } #[inline(always)] fn from_u64(n: u64) -> Option { <$elt>::from_u64(n).map(Self::splat) } #[inline(always)] fn from_isize(n: isize) -> Option { <$elt>::from_isize(n).map(Self::splat) } #[inline(always)] fn from_i8(n: i8) -> Option { <$elt>::from_i8(n).map(Self::splat) } #[inline(always)] fn from_i16(n: i16) -> Option { <$elt>::from_i16(n).map(Self::splat) } #[inline(always)] fn from_i32(n: i32) -> Option { <$elt>::from_i32(n).map(Self::splat) } #[inline(always)] fn from_usize(n: usize) -> Option { <$elt>::from_usize(n).map(Self::splat) } #[inline(always)] fn from_u8(n: u8) -> Option { <$elt>::from_u8(n).map(Self::splat) } #[inline(always)] fn from_u16(n: u16) -> Option { <$elt>::from_u16(n).map(Self::splat) } #[inline(always)] fn from_u32(n: u32) -> Option { <$elt>::from_u32(n).map(Self::splat) } #[inline(always)] fn from_f32(n: f32) -> Option { <$elt>::from_f32(n).map(Self::splat) } #[inline(always)] fn from_f64(n: f64) -> Option { <$elt>::from_f64(n).map(Self::splat) } } impl Zero for Simd<$t> { #[inline(always)] fn zero() -> Self { Simd(<$t>::splat(<$elt>::zero())) } #[inline(always)] fn is_zero(&self) -> bool { *self == Self::zero() } } impl One for Simd<$t> { #[inline(always)] fn one() -> Self { Simd(<$t>::splat(<$elt>::one())) } } impl Add> for Simd<$t> { type Output = Self; #[inline(always)] fn add(self, rhs: Self) -> Self { Self(self.0 + rhs.0) } } impl Sub> for Simd<$t> { type Output = Self; #[inline(always)] fn sub(self, rhs: Self) -> Self { Self(self.0 - rhs.0) } } impl Mul> for Simd<$t> { type Output = Self; #[inline(always)] fn mul(self, rhs: Self) -> Self { Self(self.0 * rhs.0) } } impl Div> for Simd<$t> { type Output = Self; #[inline(always)] fn div(self, rhs: Self) -> Self { Self(self.0 / rhs.0) } } impl Rem> for Simd<$t> { type Output = Self; #[inline(always)] fn rem(self, rhs: Self) -> Self { Self(self.0 % rhs.0) } } impl AddAssign> for Simd<$t> { #[inline(always)] fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 } } impl SubAssign> for Simd<$t> { #[inline(always)] fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 } } impl DivAssign> for Simd<$t> { #[inline(always)] fn div_assign(&mut self, rhs: Self) { self.0 /= rhs.0 } } impl MulAssign> for Simd<$t> { #[inline(always)] fn mul_assign(&mut self, rhs: Self) { self.0 *= rhs.0 } } impl RemAssign> for Simd<$t> { #[inline(always)] fn rem_assign(&mut self, rhs: Self) { self.0 %= rhs.0 } } impl SimdPartialOrd for Simd<$t> { #[inline(always)] fn simd_gt(self, other: Self) -> Self::SimdBool { Simd(self.0.gt(other.0)) } #[inline(always)] fn simd_lt(self, other: Self) -> Self::SimdBool { Simd(self.0.lt(other.0)) } #[inline(always)] fn simd_ge(self, other: Self) -> Self::SimdBool { Simd(self.0.ge(other.0)) } #[inline(always)] fn simd_le(self, other: Self) -> Self::SimdBool { Simd(self.0.le(other.0)) } #[inline(always)] fn simd_eq(self, other: Self) -> Self::SimdBool { Simd(self.0.eq(other.0)) } #[inline(always)] fn simd_ne(self, other: Self) -> Self::SimdBool { Simd(self.0.ne(other.0)) } #[inline(always)] fn simd_max(self, other: Self) -> Self { Simd(self.0.max(other.0)) } #[inline(always)] fn simd_min(self, other: Self) -> Self { Simd(self.0.min(other.0)) } #[inline(always)] fn simd_clamp(self, min: Self, max: Self) -> Self { self.simd_max(min).simd_min(max) } #[inline(always)] fn simd_horizontal_min(self) -> Self::Element { self.0.min_element() } #[inline(always)] fn simd_horizontal_max(self) -> Self::Element { self.0.max_element() } } // impl MeetSemilattice for Simd<$t> { // #[inline(always)] // fn meet(&self, other: &Self) -> Self { // Simd(self.0.min(other.0)) // } // } // // impl JoinSemilattice for Simd<$t> { // #[inline(always)] // fn join(&self, other: &Self) -> Self { // Simd(self.0.max(other.0)) // } // } )*) ); macro_rules! impl_int_simd( ($($t: ty, $elt: ty, $bool: ty, $($i: ident),*;)*) => ($( impl_uint_simd!($t, $elt, $bool $(, $i)*;); impl Neg for Simd<$t> { type Output = Self; #[inline(always)] fn neg(self) -> Self { Self(-self.0) } } )*) ); macro_rules! impl_float_simd( ($($t: ty, $elt: ty, $int: ty, $bool: ty, $($i: ident),*;)*) => ($( impl_int_simd!($t, $elt, $bool $(, $i)*;); // FIXME: this should be part of impl_int_simd // but those methods do not seem to be implemented // by packed_simd for integers. impl SimdSigned for Simd<$t> { #[inline(always)] fn simd_abs(&self) -> Self { Simd(self.0.abs()) } #[inline(always)] fn simd_abs_sub(&self, other: &Self) -> Self { Simd((self.0 - other.0).max(Self::zero().0)) } #[inline(always)] fn simd_signum(&self) -> Self { // NOTE: is there a more efficient way of doing this? let zero = Self::zero().0; let one = Self::one().0; let gt = self.0.gt(zero); let lt = self.0.lt(zero); Simd(lt.select(-one, gt.select(one, zero))) } #[inline(always)] fn is_simd_positive(&self) -> Self::SimdBool { self.simd_gt(Self::zero()) } #[inline(always)] fn is_simd_negative(&self) -> Self::SimdBool { self.simd_lt(Self::zero()) } } impl Field for Simd<$t> {} impl SimdRealField for Simd<$t> { #[inline(always)] fn simd_atan2(self, other: Self) -> Self { self.zip_map_lanes(other, |a, b| a.atan2(b)) } #[inline(always)] fn simd_copysign(self, sign: Self) -> Self { use packed_simd::FromBits; let sign_bits = <$int>::from_bits(<$t>::splat(-0.0)); let ref_bits = <$int>::from_bits(sign.0); let self_bits = <$int>::from_bits(self.0); let result = <$t>::from_bits((sign_bits & ref_bits) | ((!sign_bits) & self_bits)); Simd(result) } #[inline(always)] fn simd_default_epsilon() -> Self { Self::splat(<$elt>::default_epsilon()) } #[inline(always)] fn simd_pi() -> Self { Simd(<$t>::PI) } #[inline(always)] fn simd_two_pi() -> Self { Simd(<$t>::PI + <$t>::PI) } #[inline(always)] fn simd_frac_pi_2() -> Self { Simd(<$t>::FRAC_PI_2) } #[inline(always)] fn simd_frac_pi_3() -> Self { Simd(<$t>::FRAC_PI_3) } #[inline(always)] fn simd_frac_pi_4() -> Self { Simd(<$t>::FRAC_PI_4) } #[inline(always)] fn simd_frac_pi_6() -> Self { Simd(<$t>::FRAC_PI_6) } #[inline(always)] fn simd_frac_pi_8() -> Self { Simd(<$t>::FRAC_PI_8) } #[inline(always)] fn simd_frac_1_pi() -> Self { Simd(<$t>::FRAC_1_PI) } #[inline(always)] fn simd_frac_2_pi() -> Self { Simd(<$t>::FRAC_2_PI) } #[inline(always)] fn simd_frac_2_sqrt_pi() -> Self { Simd(<$t>::FRAC_2_SQRT_PI) } #[inline(always)] fn simd_e() -> Self { Simd(<$t>::E) } #[inline(always)] fn simd_log2_e() -> Self { Simd(<$t>::LOG2_E) } #[inline(always)] fn simd_log10_e() -> Self { Simd(<$t>::LOG10_E) } #[inline(always)] fn simd_ln_2() -> Self { Simd(<$t>::LN_2) } #[inline(always)] fn simd_ln_10() -> Self { Simd(<$t>::LN_10) } } impl SimdComplexField for Simd<$t> { type SimdRealField = Self; #[inline(always)] fn simd_horizontal_sum(self) -> Self::Element { self.0.sum() } #[inline(always)] fn simd_horizontal_product(self) -> Self::Element { self.0.product() } #[inline(always)] fn from_simd_real(re: Self::SimdRealField) -> Self { re } #[inline(always)] fn simd_real(self) -> Self::SimdRealField { self } #[inline(always)] fn simd_imaginary(self) -> Self::SimdRealField { Self::zero() } #[inline(always)] fn simd_norm1(self) -> Self::SimdRealField { Simd(self.0.abs()) } #[inline(always)] fn simd_modulus(self) -> Self::SimdRealField { Simd(self.0.abs()) } #[inline(always)] fn simd_modulus_squared(self) -> Self::SimdRealField { self * self } #[inline(always)] fn simd_argument(self) -> Self::SimdRealField { self.map_lanes(|e| e.argument()) } #[inline(always)] fn simd_to_exp(self) -> (Self::SimdRealField, Self) { let ge = self.0.ge(Self::one().0); let exp = ge.select(Self::one().0, -Self::one().0); (Simd(self.0 * exp), Simd(exp)) } #[inline(always)] fn simd_recip(self) -> Self { Self::one() / self } #[inline(always)] fn simd_conjugate(self) -> Self { self } #[inline(always)] fn simd_scale(self, factor: Self::SimdRealField) -> Self { Simd(self.0 * factor.0) } #[inline(always)] fn simd_unscale(self, factor: Self::SimdRealField) -> Self { Simd(self.0 / factor.0) } #[inline(always)] fn simd_floor(self) -> Self { self.map_lanes(|e| e.floor()) } #[inline(always)] fn simd_ceil(self) -> Self { self.map_lanes(|e| e.ceil()) } #[inline(always)] fn simd_round(self) -> Self { self.map_lanes(|e| e.round()) } #[inline(always)] fn simd_trunc(self) -> Self { self.map_lanes(|e| e.trunc()) } #[inline(always)] fn simd_fract(self) -> Self { self.map_lanes(|e| e.fract()) } #[inline(always)] fn simd_abs(self) -> Self { Simd(self.0.abs()) } #[inline(always)] fn simd_signum(self) -> Self { self.map_lanes(|e| e.signum()) } #[inline(always)] fn simd_mul_add(self, a: Self, b: Self) -> Self { Simd(self.0.mul_add(a.0, b.0)) } #[inline(always)] fn simd_powi(self, n: i32) -> Self { Simd(self.0.powf(<$t>::splat(n as $elt))) } #[inline(always)] fn simd_powf(self, n: Self) -> Self { Simd(self.0.powf(n.0)) } #[inline(always)] fn simd_powc(self, n: Self) -> Self { Simd(self.0.powf(n.0)) } #[inline(always)] fn simd_sqrt(self) -> Self { Simd(self.0.sqrt()) } #[inline(always)] fn simd_exp(self) -> Self { Simd(self.0.exp()) } #[inline(always)] fn simd_exp2(self) -> Self { self.map_lanes(|e| e.exp2()) } #[inline(always)] fn simd_exp_m1(self) -> Self { self.map_lanes(|e| e.exp_m1()) } #[inline(always)] fn simd_ln_1p(self) -> Self { self.map_lanes(|e| e.ln_1p()) } #[inline(always)] fn simd_ln(self) -> Self { Simd(self.0.ln()) } #[inline(always)] fn simd_log(self, base: Self) -> Self { self.zip_map_lanes(base, |e, b| e.log(b)) } #[inline(always)] fn simd_log2(self) -> Self { self.map_lanes(|e| e.log2()) } #[inline(always)] fn simd_log10(self) -> Self { self.map_lanes(|e| e.log10()) } #[inline(always)] fn simd_cbrt(self) -> Self { self.map_lanes(|e| e.cbrt()) } #[inline(always)] fn simd_hypot(self, other: Self) -> Self::SimdRealField { self.zip_map_lanes(other, |e, o| e.hypot(o)) } #[inline(always)] fn simd_sin(self) -> Self { Simd(self.0.sin()) } #[inline(always)] fn simd_cos(self) -> Self { Simd(self.0.cos()) } #[inline(always)] fn simd_tan(self) -> Self { self.map_lanes(|e| e.tan()) } #[inline(always)] fn simd_asin(self) -> Self { self.map_lanes(|e| e.asin()) } #[inline(always)] fn simd_acos(self) -> Self { self.map_lanes(|e| e.acos()) } #[inline(always)] fn simd_atan(self) -> Self { self.map_lanes(|e| e.atan()) } #[inline(always)] fn simd_sin_cos(self) -> (Self, Self) { (self.simd_sin(), self.simd_cos()) } // #[inline(always] // fn simd_exp_m1(self) -> Self { // $libm::exp_m1(self) // } // // #[inline(always] // fn simd_ln_1p(self) -> Self { // $libm::ln_1p(self) // } // #[inline(always)] fn simd_sinh(self) -> Self { self.map_lanes(|e| e.sinh()) } #[inline(always)] fn simd_cosh(self) -> Self { self.map_lanes(|e| e.cosh()) } #[inline(always)] fn simd_tanh(self) -> Self { self.map_lanes(|e| e.tanh()) } #[inline(always)] fn simd_asinh(self) -> Self { self.map_lanes(|e| e.asinh()) } #[inline(always)] fn simd_acosh(self) -> Self { self.map_lanes(|e| e.acosh()) } #[inline(always)] fn simd_atanh(self) -> Self { self.map_lanes(|e| e.atanh()) } } // NOTE: most of the impls in there are copy-paste from the implementation of // ComplexField for num_complex::Complex. Unfortunately, we can't reuse the implementations // so easily. impl SimdComplexField for num_complex::Complex> { type SimdRealField = Simd<$t>; #[inline(always)] fn simd_horizontal_sum(self) -> Self::Element { num_complex::Complex::new(self.re.simd_horizontal_sum(), self.im.simd_horizontal_sum()) } #[inline(always)] fn simd_horizontal_product(self) -> Self::Element { let mut prod = self.extract(0); for ii in 1..Self::lanes() { prod = prod * self.extract(ii) } prod } #[inline] fn from_simd_real(re: Self::SimdRealField) -> Self { Self::new(re, Self::SimdRealField::zero()) } #[inline] fn simd_real(self) -> Self::SimdRealField { self.re } #[inline] fn simd_imaginary(self) -> Self::SimdRealField { self.im } #[inline] fn simd_argument(self) -> Self::SimdRealField { self.im.simd_atan2(self.re) } #[inline] fn simd_modulus(self) -> Self::SimdRealField { self.re.simd_hypot(self.im) } #[inline] fn simd_modulus_squared(self) -> Self::SimdRealField { self.re * self.re + self.im * self.im } #[inline] fn simd_norm1(self) -> Self::SimdRealField { self.re.simd_abs() + self.im.simd_abs() } #[inline] fn simd_recip(self) -> Self { Self::one() / self } #[inline] fn simd_conjugate(self) -> Self { self.conj() } #[inline] fn simd_scale(self, factor: Self::SimdRealField) -> Self { self * factor } #[inline] fn simd_unscale(self, factor: Self::SimdRealField) -> Self { self / factor } #[inline] fn simd_floor(self) -> Self { Self::new(self.re.simd_floor(), self.im.simd_floor()) } #[inline] fn simd_ceil(self) -> Self { Self::new(self.re.simd_ceil(), self.im.simd_ceil()) } #[inline] fn simd_round(self) -> Self { Self::new(self.re.simd_round(), self.im.simd_round()) } #[inline] fn simd_trunc(self) -> Self { Self::new(self.re.simd_trunc(), self.im.simd_trunc()) } #[inline] fn simd_fract(self) -> Self { Self::new(self.re.simd_fract(), self.im.simd_fract()) } #[inline] fn simd_mul_add(self, a: Self, b: Self) -> Self { self * a + b } #[inline] fn simd_abs(self) -> Self::SimdRealField { self.simd_modulus() } #[inline] fn simd_exp2(self) -> Self { let _2 = Simd::<$t>::one() + Simd::<$t>::one(); num_complex::Complex::new(_2, Simd::<$t>::zero()).simd_powc(self) } #[inline] fn simd_exp_m1(self) -> Self { self.simd_exp() - Self::one() } #[inline] fn simd_ln_1p(self) -> Self { (Self::one() + self).simd_ln() } #[inline] fn simd_log2(self) -> Self { let _2 = Simd::<$t>::one() + Simd::<$t>::one(); self.simd_log(_2) } #[inline] fn simd_log10(self) -> Self { let _10 = Simd::<$t>::from_subset(&10.0f64); self.simd_log(_10) } #[inline] fn simd_cbrt(self) -> Self { let one_third = Simd::<$t>::from_subset(&(1.0 / 3.0)); self.simd_powf(one_third) } #[inline] fn simd_powi(self, n: i32) -> Self { // FIXME: is there a more accurate solution? let n = Simd::<$t>::from_subset(&(n as f64)); self.simd_powf(n) } /* * * * Unfortunately we are forced to copy-paste all * those impls from https://github.com/rust-num/num-complex/blob/master/src/lib.rs * to avoid requiring `std`. * * */ /// Computes `e^(self)`, where `e` is the base of the natural logarithm. #[inline] fn simd_exp(self) -> Self { // formula: e^(a + bi) = e^a (cos(b) + i*sin(b)) // = from_polar(e^a, b) simd_complex_from_polar(self.re.simd_exp(), self.im) } /// Computes the principal value of natural logarithm of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0]`, continuous from above. /// /// The branch satisfies `-π ≤ arg(ln(z)) ≤ π`. #[inline] fn simd_ln(self) -> Self { // formula: ln(z) = ln|z| + i*arg(z) let (r, theta) = self.simd_to_polar(); Self::new(r.simd_ln(), theta) } /// Computes the principal value of the square root of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0)`, continuous from above. /// /// The branch satisfies `-π/2 ≤ arg(sqrt(z)) ≤ π/2`. #[inline] fn simd_sqrt(self) -> Self { // formula: sqrt(r e^(it)) = sqrt(r) e^(it/2) let two = Simd::<$t>::one() + Simd::<$t>::one(); let (r, theta) = self.simd_to_polar(); simd_complex_from_polar(r.simd_sqrt(), theta / two) } #[inline] fn simd_hypot(self, b: Self) -> Self::SimdRealField { (self.simd_modulus_squared() + b.simd_modulus_squared()).simd_sqrt() } /// Raises `self` to a floating point power. #[inline] fn simd_powf(self, exp: Self::SimdRealField) -> Self { // formula: x^y = (ρ e^(i θ))^y = ρ^y e^(i θ y) // = from_polar(ρ^y, θ y) let (r, theta) = self.simd_to_polar(); simd_complex_from_polar(r.simd_powf(exp), theta * exp) } /// Returns the logarithm of `self` with respect to an arbitrary base. #[inline] fn simd_log(self, base: Simd<$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.simd_to_polar(); Self::new(r.simd_log(base), theta / base.simd_ln()) } /// Raises `self` to a complex power. #[inline] fn simd_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) // = ρ^c e^(−d θ) e^(i c θ) ρ^(i d) // = p^c e^(−d θ) (cos(c θ) // + i sin(c θ)) (cos(d ln(ρ)) + i sin(d ln(ρ))) // = p^c e^(−d θ) ( // cos(c θ) cos(d ln(ρ)) − sin(c θ) sin(d ln(ρ)) // + i(cos(c θ) sin(d ln(ρ)) + sin(c θ) cos(d ln(ρ)))) // = 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.simd_to_polar(); simd_complex_from_polar( r.simd_powf(exp.re) * (-exp.im * theta).simd_exp(), exp.re * theta + exp.im * r.simd_ln(), ) } /* /// Raises a floating point number to the complex power `self`. #[inline] fn simd_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)) Self::from_polar(&base.powf(self.re), &(self.im * base.ln())) } */ /// Computes the sine of `self`. #[inline] fn simd_sin(self) -> Self { // formula: sin(a + bi) = sin(a)cosh(b) + i*cos(a)sinh(b) Self::new( self.re.simd_sin() * self.im.simd_cosh(), self.re.simd_cos() * self.im.simd_sinh(), ) } /// Computes the cosine of `self`. #[inline] fn simd_cos(self) -> Self { // formula: cos(a + bi) = cos(a)cosh(b) - i*sin(a)sinh(b) Self::new( self.re.simd_cos() * self.im.simd_cosh(), -self.re.simd_sin() * self.im.simd_sinh(), ) } #[inline] fn simd_sin_cos(self) -> (Self, Self) { let (rsin, rcos) = self.re.simd_sin_cos(); let (isinh, icosh) = self.im.simd_sinh_cosh(); let sin = Self::new(rsin * icosh, rcos * isinh); let cos = Self::new(rcos * icosh, -rsin * isinh); (sin, cos) } /// Computes the tangent of `self`. #[inline] fn simd_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); Self::new(two_re.simd_sin(), two_im.simd_sinh()).unscale(two_re.simd_cos() + two_im.simd_cosh()) } /// Computes the principal value of the inverse sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Re(asin(z)) ≤ π/2`. #[inline] fn simd_asin(self) -> Self { // formula: arcsin(z) = -i ln(sqrt(1-z^2) + iz) let i = Self::i(); -i * ((Self::one() - self * self).simd_sqrt() + i * self).simd_ln() } /// Computes the principal value of the inverse cosine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `0 ≤ Re(acos(z)) ≤ π`. #[inline] fn simd_acos(self) -> Self { // formula: arccos(z) = -i ln(i sqrt(1-z^2) + z) let i = Self::i(); -i * (i * (Self::one() - self * self).simd_sqrt() + self).simd_ln() } /// Computes the principal value of the inverse tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i]`, continuous from the left. /// * `[i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Re(atan(z)) ≤ π/2`. #[inline] fn simd_atan(self) -> Self { // formula: arctan(z) = (ln(1+iz) - ln(1-iz))/(2i) let i = Self::i(); let one = Self::one(); let two = one + one; if self == i { return Self::new(Simd::<$t>::zero(), Simd::<$t>::one() / Simd::<$t>::zero()); } else if self == -i { return Self::new(Simd::<$t>::zero(), -Simd::<$t>::one() / Simd::<$t>::zero()); } ((one + i * self).simd_ln() - (one - i * self).simd_ln()) / (two * i) } /// Computes the hyperbolic sine of `self`. #[inline] fn simd_sinh(self) -> Self { // formula: sinh(a + bi) = sinh(a)cos(b) + i*cosh(a)sin(b) Self::new( self.re.simd_sinh() * self.im.simd_cos(), self.re.simd_cosh() * self.im.simd_sin(), ) } /// Computes the hyperbolic cosine of `self`. #[inline] fn simd_cosh(self) -> Self { // formula: cosh(a + bi) = cosh(a)cos(b) + i*sinh(a)sin(b) Self::new( self.re.simd_cosh() * self.im.simd_cos(), self.re.simd_sinh() * self.im.simd_sin(), ) } #[inline] fn simd_sinh_cosh(self) -> (Self, Self) { let (rsinh, rcosh) = self.re.simd_sinh_cosh(); let (isin, icos) = self.im.simd_sin_cos(); let sin = Self::new(rsinh * icos, rcosh * isin); let cos = Self::new(rcosh * icos, rsinh * isin); (sin, cos) } /// Computes the hyperbolic tangent of `self`. #[inline] fn simd_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); Self::new(two_re.simd_sinh(), two_im.simd_sin()).unscale(two_re.simd_cosh() + two_im.simd_cos()) } /// Computes the principal value of inverse hyperbolic sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i)`, continuous from the left. /// * `(i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Im(asinh(z)) ≤ π/2`. #[inline] fn simd_asinh(self) -> Self { // formula: arcsinh(z) = ln(z + sqrt(1+z^2)) let one = Self::one(); (self + (one + self * self).simd_sqrt()).simd_ln() } /// Computes the principal value of inverse hyperbolic cosine of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 1)`, continuous from above. /// /// The branch satisfies `-π ≤ Im(acosh(z)) ≤ π` and `0 ≤ Re(acosh(z)) < ∞`. #[inline] fn simd_acosh(self) -> Self { // formula: arccosh(z) = 2 ln(sqrt((z+1)/2) + sqrt((z-1)/2)) let one = Self::one(); let two = one + one; two * (((self + one) / two).simd_sqrt() + ((self - one) / two).simd_sqrt()).simd_ln() } /// Computes the principal value of inverse hyperbolic tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1]`, continuous from above. /// * `[1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Im(atanh(z)) ≤ π/2`. #[inline] fn simd_atanh(self) -> Self { // formula: arctanh(z) = (ln(1+z) - ln(1-z))/2 let one = Self::one(); let two = one + one; if self == one { return Self::new(Simd::<$t>::one() / Simd::<$t>::zero(), Simd::<$t>::zero()); } else if self == -one { return Self::new(-Simd::<$t>::one() / Simd::<$t>::zero(), Simd::<$t>::zero()); } ((one + self).simd_ln() - (one - self).simd_ln()) / two } } )*) ); #[inline] fn simd_complex_from_polar(r: N, theta: N) -> num_complex::Complex { num_complex::Complex::new(r.clone() * theta.clone().simd_cos(), r * theta.simd_sin()) } impl_float_simd!( packed_simd::f32x2, f32, packed_simd::i32x2, m32x2, _0, _1; packed_simd::f32x4, f32, packed_simd::i32x4, m32x4, _0, _1, _2, _3; packed_simd::f32x8, f32, packed_simd::i32x8, m32x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::f32x16, f32, packed_simd::i32x16, m32x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::f64x2, f64, packed_simd::i64x2, m64x2, _0, _1; packed_simd::f64x4, f64, packed_simd::i64x4, m64x4, _0, _1, _2, _3; packed_simd::f64x8, f64, packed_simd::i64x8, m64x8, _0, _1, _2, _3, _4, _5, _6, _7; ); impl_int_simd!( packed_simd::i128x1, i128, m128x1, _0; packed_simd::i128x2, i128, m128x2, _0, _1; packed_simd::i128x4, i128, m128x4, _0, _1, _2, _3; packed_simd::i16x2, i16, m16x2, _0, _1; packed_simd::i16x4, i16, m16x4, _0, _1, _2, _3; packed_simd::i16x8, i16, m16x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i16x16, i16, m16x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::i16x32, i16, m16x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::i32x2, i32, m32x2, _0, _1; packed_simd::i32x4, i32, m32x4, _0, _1, _2, _3; packed_simd::i32x8, i32, m32x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i32x16, i32, m32x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::i64x2, i64, m64x2, _0, _1; packed_simd::i64x4, i64, m64x4, _0, _1, _2, _3; packed_simd::i64x8, i64, m64x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i8x2, i8, m8x2, _0, _1; packed_simd::i8x4, i8, m8x4, _0, _1, _2, _3; packed_simd::i8x8, i8, m8x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i8x16, i8, m8x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::i8x32, i8, m8x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::i8x64, i8, m8x64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; packed_simd::isizex2, isize, msizex2, _0, _1; packed_simd::isizex4, isize, msizex4, _0, _1, _2, _3; packed_simd::isizex8, isize, msizex8, _0, _1, _2, _3, _4, _5, _6, _7; ); impl_uint_simd!( packed_simd::u128x1, u128, m128x1, _0; packed_simd::u128x2, u128, m128x2, _0, _1; packed_simd::u128x4, u128, m128x4, _0, _1, _2, _3; packed_simd::u16x2, u16, m16x2, _0, _1; packed_simd::u16x4, u16, m16x4, _0, _1, _2, _3; packed_simd::u16x8, u16, m16x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u16x16, u16, m16x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::u16x32, u16, m16x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::u32x2, u32, m32x2, _0, _1; packed_simd::u32x4, u32, m32x4, _0, _1, _2, _3; packed_simd::u32x8, u32, m32x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u32x16, u32, m32x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::u64x2, u64, m64x2, _0, _1; packed_simd::u64x4, u64, m64x4, _0, _1, _2, _3; packed_simd::u64x8, u64, m64x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u8x2, u8, m8x2, _0, _1; packed_simd::u8x4, u8, m8x4, _0, _1, _2, _3; packed_simd::u8x8, u8, m8x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u8x16, u8, m8x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::u8x32, u8, m8x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::u8x64, u8, m8x64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; packed_simd::usizex2, usize, msizex2, _0, _1; packed_simd::usizex4, usize, msizex4, _0, _1, _2, _3; packed_simd::usizex8, usize, msizex8, _0, _1, _2, _3, _4, _5, _6, _7; ); impl_bool_simd!( packed_simd::m128x1, _0; packed_simd::m128x2, _0, _1; packed_simd::m128x4, _0, _1, _2, _3; packed_simd::m16x2, _0, _1; packed_simd::m16x4, _0, _1, _2, _3; packed_simd::m16x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m16x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::m16x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::m32x2, _0, _1; packed_simd::m32x4, _0, _1, _2, _3; packed_simd::m32x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m32x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::m64x2, _0, _1; packed_simd::m64x4, _0, _1, _2, _3; packed_simd::m64x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m8x2, _0, _1; packed_simd::m8x4, _0, _1, _2, _3; packed_simd::m8x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m8x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::m8x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::m8x64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; packed_simd::msizex2, _0, _1; packed_simd::msizex4, _0, _1, _2, _3; packed_simd::msizex8, _0, _1, _2, _3, _4, _5, _6, _7; ); // // NOTE: the following does not work because of the orphan rules. // //macro_rules! impl_simd_complex_from( // ($($t: ty, $elt: ty $(, $i: expr)*;)*) => ($( // impl From<[num_complex::Complex<$elt>; <$t>::lanes()]> for num_complex::Complex> { // #[inline(always)] // fn from(vals: [num_complex::Complex<$elt>; <$t>::lanes()]) -> Self { // num_complex::Complex { // re: <$t>::from([$(vals[$i].re),*]), // im: <$t>::from([$(vals[$i].im),*]), // } // } // } // )*) //); // //impl_simd_complex_from!( // packed_simd::f32x2, f32, 0, 1; // packed_simd::f32x4, f32, 0, 1, 2, 3; // packed_simd::f32x8, f32, 0, 1, 2, 3, 4, 5, 6, 7; // packed_simd::f32x16, f32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15; //); ////////////////////////////////////////// // Aliases // ////////////////////////////////////////// pub type f32x2 = Simd; pub type f32x4 = Simd; pub type f32x8 = Simd; pub type f32x16 = Simd; pub type f64x2 = Simd; pub type f64x4 = Simd; pub type f64x8 = Simd; pub type i128x1 = Simd; pub type i128x2 = Simd; pub type i128x4 = Simd; pub type i16x2 = Simd; pub type i16x4 = Simd; pub type i16x8 = Simd; pub type i16x16 = Simd; pub type i16x32 = Simd; pub type i32x2 = Simd; pub type i32x4 = Simd; pub type i32x8 = Simd; pub type i32x16 = Simd; pub type i64x2 = Simd; pub type i64x4 = Simd; pub type i64x8 = Simd; pub type i8x2 = Simd; pub type i8x4 = Simd; pub type i8x8 = Simd; pub type i8x16 = Simd; pub type i8x32 = Simd; pub type i8x64 = Simd; pub type isizex2 = Simd; pub type isizex4 = Simd; pub type isizex8 = Simd; pub type u128x1 = Simd; pub type u128x2 = Simd; pub type u128x4 = Simd; pub type u16x2 = Simd; pub type u16x4 = Simd; pub type u16x8 = Simd; pub type u16x16 = Simd; pub type u16x32 = Simd; pub type u32x2 = Simd; pub type u32x4 = Simd; pub type u32x8 = Simd; pub type u32x16 = Simd; pub type u64x2 = Simd; pub type u64x4 = Simd; pub type u64x8 = Simd; pub type u8x2 = Simd; pub type u8x4 = Simd; pub type u8x8 = Simd; pub type u8x16 = Simd; pub type u8x32 = Simd; pub type u8x64 = Simd; pub type usizex2 = Simd; pub type usizex4 = Simd; pub type usizex8 = Simd; pub type m128x1 = Simd; pub type m128x2 = Simd; pub type m128x4 = Simd; pub type m16x16 = Simd; pub type m16x2 = Simd; pub type m16x32 = Simd; pub type m16x4 = Simd; pub type m16x8 = Simd; pub type m32x16 = Simd; pub type m32x2 = Simd; pub type m32x4 = Simd; pub type m32x8 = Simd; pub type m64x2 = Simd; pub type m64x4 = Simd; pub type m64x8 = Simd; pub type m8x16 = Simd; pub type m8x2 = Simd; pub type m8x32 = Simd; pub type m8x4 = Simd; pub type m8x64 = Simd; pub type m8x8 = Simd; pub type msizex2 = Simd; pub type msizex4 = Simd; pub type msizex8 = Simd; simba-0.8.1/src/simd/rand_impl.rs000064400000000000000000000265030072674642500150210ustar 00000000000000use crate::simd::*; // Given two token streams in the format `ignore_snd!([first_token_tree], [second])` will simply // expand to the first one. This is usefull in order to allow the repetition of some statement // according to some repetition variable, without using the repetition variables. macro_rules! ignore_snd( ([$($fst: tt)*], [$($snd: tt)*]) => ($($fst)*) ); macro_rules! impl_rand_auto_simd( ($($t: ty, $($i: ident),*;)*) => ($( impl rand::distributions::Distribution> for rand::distributions::Standard { #[inline(always)] fn sample(&self, rng: &mut R) -> AutoSimd<$t> { AutoSimd::<$t>::new($( ignore_snd!([self.sample(rng)], [$i]) ),*) } } )*) ); impl_rand_auto_simd!( [f32; 2], _0, _1; [f32; 4], _0, _1, _2, _3; [f32; 8], _0, _1, _2, _3, _4, _5, _6, _7; [f32; 16], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [f64; 2], _0, _1; [f64; 4], _0, _1, _2, _3; [f64; 8], _0, _1, _2, _3, _4, _5, _6, _7; [i128; 1], _0; [i128; 2], _0, _1; [i128; 4], _0, _1, _2, _3; [i16; 2], _0, _1; [i16; 4], _0, _1, _2, _3; [i16; 8], _0, _1, _2, _3, _4, _5, _6, _7; [i16; 16], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [i16; 32], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; [i32; 2], _0, _1; [i32; 4], _0, _1, _2, _3; [i32; 8], _0, _1, _2, _3, _4, _5, _6, _7; [i32; 16], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [i64; 2], _0, _1; [i64; 4], _0, _1, _2, _3; [i64; 8], _0, _1, _2, _3, _4, _5, _6, _7; [i8; 2], _0, _1; [i8; 4], _0, _1, _2, _3; [i8; 8], _0, _1, _2, _3, _4, _5, _6, _7; [i8; 16], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [i8; 32], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; // [i8; 64], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; [isize; 2], _0, _1; [isize; 4], _0, _1, _2, _3; [isize; 8], _0, _1, _2, _3, _4, _5, _6, _7; [u128; 1], _0; [u128; 2], _0, _1; [u128; 4], _0, _1, _2, _3; [u16; 2], _0, _1; [u16; 4], _0, _1, _2, _3; [u16; 8], _0, _1, _2, _3, _4, _5, _6, _7; [u16; 16], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [u16; 32], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; [u32; 2], _0, _1; [u32; 4], _0, _1, _2, _3; [u32; 8], _0, _1, _2, _3, _4, _5, _6, _7; [u32; 16], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [u64; 2], _0, _1; [u64; 4], _0, _1, _2, _3; [u64; 8], _0, _1, _2, _3, _4, _5, _6, _7; [u8; 2], _0, _1; [u8; 4], _0, _1, _2, _3; [u8; 8], _0, _1, _2, _3, _4, _5, _6, _7; [u8; 16], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [u8; 32], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; // [u8; 64], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; [usize; 2], _0, _1; [usize; 4], _0, _1, _2, _3; [usize; 8], _0, _1, _2, _3, _4, _5, _6, _7; [bool; 1], _0; [bool; 2], _0, _1; [bool; 4], _0, _1, _2, _3; [bool; 8], _0, _1, _2, _3, _4, _5, _6, _7; [bool; 16], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; [bool; 32], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; // [bool; 64], _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; ); #[cfg(feature = "wide")] macro_rules! impl_rand_wide_simd( ($($t: ident, $wrapped: ty, $arr: ty;)*) => ($( impl rand::distributions::Distribution<$t> for rand::distributions::Standard { #[inline(always)] fn sample(&self, rng: &mut R) -> $t { $t(<$wrapped as From<$arr>>::from(self.sample(rng))) } } )*) ); #[cfg(feature = "wide")] impl_rand_wide_simd!( WideF32x4, wide::f32x4, [f32; 4]; WideF64x4, wide::f64x4, [f64; 4]; WideF32x8, wide::f32x8, [f32; 8]; ); #[cfg(feature = "packed_simd")] macro_rules! impl_rand_packed_simd( ($($wrapped: ty, $($i: ident),*;)*) => ($( impl rand::distributions::Distribution> for rand::distributions::Standard { #[inline(always)] fn sample(&self, rng: &mut R) -> Simd<$wrapped> { Simd(<$wrapped>::new($( ignore_snd!([self.sample(rng)], [$i]) ),*)) } } )*) ); #[cfg(feature = "packed_simd")] impl_rand_packed_simd!( packed_simd::f32x2, _0, _1; packed_simd::f32x4, _0, _1, _2, _3; packed_simd::f32x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::f32x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::f64x2, _0, _1; packed_simd::f64x4, _0, _1, _2, _3; packed_simd::f64x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i128x1, _0; packed_simd::i128x2, _0, _1; packed_simd::i128x4, _0, _1, _2, _3; packed_simd::i16x2, _0, _1; packed_simd::i16x4, _0, _1, _2, _3; packed_simd::i16x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i16x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::i16x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::i32x2, _0, _1; packed_simd::i32x4, _0, _1, _2, _3; packed_simd::i32x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i32x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::i64x2, _0, _1; packed_simd::i64x4, _0, _1, _2, _3; packed_simd::i64x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i8x2, _0, _1; packed_simd::i8x4, _0, _1, _2, _3; packed_simd::i8x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::i8x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::i8x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::i8x64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; packed_simd::isizex2, _0, _1; packed_simd::isizex4, _0, _1, _2, _3; packed_simd::isizex8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u128x1, _0; packed_simd::u128x2, _0, _1; packed_simd::u128x4, _0, _1, _2, _3; packed_simd::u16x2, _0, _1; packed_simd::u16x4, _0, _1, _2, _3; packed_simd::u16x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u16x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::u16x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::u32x2, _0, _1; packed_simd::u32x4, _0, _1, _2, _3; packed_simd::u32x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u32x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::u64x2, _0, _1; packed_simd::u64x4, _0, _1, _2, _3; packed_simd::u64x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u8x2, _0, _1; packed_simd::u8x4, _0, _1, _2, _3; packed_simd::u8x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::u8x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::u8x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::u8x64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; packed_simd::usizex2, _0, _1; packed_simd::usizex4, _0, _1, _2, _3; packed_simd::usizex8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m128x1, _0; packed_simd::m128x2, _0, _1; packed_simd::m128x4, _0, _1, _2, _3; packed_simd::m16x2, _0, _1; packed_simd::m16x4, _0, _1, _2, _3; packed_simd::m16x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m16x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::m16x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::m32x2, _0, _1; packed_simd::m32x4, _0, _1, _2, _3; packed_simd::m32x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m32x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::m64x2, _0, _1; packed_simd::m64x4, _0, _1, _2, _3; packed_simd::m64x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m8x2, _0, _1; packed_simd::m8x4, _0, _1, _2, _3; packed_simd::m8x8, _0, _1, _2, _3, _4, _5, _6, _7; packed_simd::m8x16, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15; packed_simd::m8x32, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31; packed_simd::m8x64, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63; packed_simd::msizex2, _0, _1; packed_simd::msizex4, _0, _1, _2, _3; packed_simd::msizex8, _0, _1, _2, _3, _4, _5, _6, _7; ); simba-0.8.1/src/simd/simd_bool.rs000064400000000000000000000127470072674642500150300ustar 00000000000000use crate::simd::SimdValue; use std::ops::{BitAnd, BitOr, BitXor, Not}; /// Lane-wise generalization of `bool` for SIMD booleans. /// /// This trait implemented by `bool` as well as SIMD boolean types like `packed_simd::m32x4`. /// It is designed to abstract the behavior of booleans so it can work with multi-lane boolean /// values in an AoSoA setting. pub trait SimdBool: Copy + BitAnd + BitOr + BitXor + Not { /// A bit mask representing the boolean state of each lanes of `self`. /// /// The `i-th` bit of the result is `1` iff. the `i-th` lane of `self` is `true`. fn bitmask(self) -> u64; /// Lane-wise bitwise and of the vector elements. fn and(self) -> bool; /// Lane-wise bitwise or of the vector elements. fn or(self) -> bool; /// Lane-wise bitwise xor of the vector elements. fn xor(self) -> bool; /// Are all vector lanes true? fn all(self) -> bool; /// Is any vector lane true? fn any(self) -> bool; /// Are all vector lanes false? fn none(self) -> bool; /// Merges the value of `if_value()` and `else_value()` depending on the lanes of `self`. /// /// - For each lane of `self` containing `1`, the result will contain the corresponding lane of `if_value()`. /// - For each lane of `self` containing `0`, the result will contain the corresponding lane of `else_value()`. /// /// The implementor of this trait is free to choose on what cases `if_value` and `else_value` are actually /// called. fn if_else>( self, if_value: impl FnOnce() -> Res, else_value: impl FnOnce() -> Res, ) -> Res; /// Merges the value of `if_value()` and `else_if.1()` and `else_value()` depending on the lanes of `self` and `else_if.0()`. /// /// - For each lane of `self` containing `1`, the result will contain the corresponding lane of `if_value()`. /// - For each lane of `self` containing `0` but with a corresponding lane of `else_if.0()` containing `1`, the result will contain the corresponding lane of `else_if.1()`. /// - For each lane of `self` containing `0` but with a corresponding lane of `else_if.0()` containing `0`, the result will contain the corresponding lane of `else_value()`. /// /// The implementor of this trait is free to choose on what cases any of those closures are implemented. fn if_else2>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res; /// Merges the value of `if_value()` and `else_if.1()` and `else_else_if.1()` and `else_value()` depending on the lanes of `self` and `else_if.0()` and `else_else_if.0()`. /// /// - For each lane of `self` containing `1`, the result will contain the corresponding lane of `if_value()`. /// - For each lane of `self` containing `0` but with a corresponding lane of `else_if.0()` containing `1`, the result will contain the corresponding lane of `else_if.1()`. /// - For each lane of `self` containing `0` and `else_if.0()` containing `0` and `else_else_if.0()` containing `1`, the result will contain the corresponding lane of `else_else_if.1()`. /// - Other lanes will contain the corresponding lane of `else_value()`. /// /// The implementor of this trait is free to choose on what cases any of those closures are implemented. fn if_else3>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res; } impl SimdBool for bool { #[inline(always)] fn bitmask(self) -> u64 { self as u64 } #[inline(always)] fn and(self) -> bool { self } #[inline(always)] fn or(self) -> bool { self } #[inline(always)] fn xor(self) -> bool { self } #[inline(always)] fn all(self) -> bool { self } #[inline(always)] fn any(self) -> bool { self } #[inline(always)] fn none(self) -> bool { !self } #[inline(always)] fn if_else>( self, if_value: impl FnOnce() -> Res, else_value: impl FnOnce() -> Res, ) -> Res { if self { if_value() } else { else_value() } } #[inline(always)] fn if_else2>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res { if self { if_value() } else if else_if.0() { else_if.1() } else { else_value() } } #[inline(always)] fn if_else3>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res { if self { if_value() } else if else_if.0() { else_if.1() } else if else_else_if.0() { else_else_if.1() } else { else_value() } } } simba-0.8.1/src/simd/simd_complex.rs000064400000000000000000000145560072674642500155440ustar 00000000000000use num::{NumAssignOps, NumOps, Zero}; use std::any::Any; use std::f64; use std::fmt::Debug; use std::ops::Neg; use crate::scalar::{ComplexField, Field, SubsetOf, SupersetOf}; use crate::simd::{SimdRealField, SimdValue}; /// Lane-wise generalisation of `ComplexField` for SIMD complex fields. /// /// Each lane of an SIMD complex field should contain one complex field. #[allow(missing_docs)] pub trait SimdComplexField: SubsetOf + SupersetOf + Field + Clone + Neg // + MeetSemilattice // + JoinSemilattice + Send + Sync + Any + 'static + Debug + NumAssignOps + NumOps + PartialEq { /// Type of the coefficients of a complex number. type SimdRealField: SimdRealField::SimdBool>; complex_trait_methods!(SimdRealField, simd_); /// Computes the sum of all the lanes of `self`. fn simd_horizontal_sum(self) -> Self::Element; /// Computes the product of all the lanes of `self`. fn simd_horizontal_product(self) -> Self::Element; } // Blanket impl: ComplexField => SimdComplexField impl SimdComplexField for T { type SimdRealField = T::RealField; #[inline(always)] fn from_simd_real(re: Self::SimdRealField) -> Self { Self::from_real(re) } #[inline(always)] fn simd_real(self) -> Self::SimdRealField { self.real() } #[inline(always)] fn simd_imaginary(self) -> Self::SimdRealField { self.imaginary() } #[inline(always)] fn simd_modulus(self) -> Self::SimdRealField { self.modulus() } #[inline(always)] fn simd_modulus_squared(self) -> Self::SimdRealField { self.modulus_squared() } #[inline(always)] fn simd_argument(self) -> Self::SimdRealField { self.argument() } #[inline(always)] fn simd_norm1(self) -> Self::SimdRealField { self.norm1() } #[inline(always)] fn simd_scale(self, factor: Self::SimdRealField) -> Self { self.scale(factor) } #[inline(always)] fn simd_unscale(self, factor: Self::SimdRealField) -> Self { self.unscale(factor) } #[inline(always)] fn simd_to_polar(self) -> (Self::SimdRealField, Self::SimdRealField) { self.to_polar() } #[inline(always)] fn simd_to_exp(self) -> (Self::SimdRealField, Self) { self.to_exp() } #[inline(always)] fn simd_signum(self) -> Self { self.signum() } #[inline(always)] fn simd_floor(self) -> Self { self.floor() } #[inline(always)] fn simd_ceil(self) -> Self { self.ceil() } #[inline(always)] fn simd_round(self) -> Self { self.round() } #[inline(always)] fn simd_trunc(self) -> Self { self.trunc() } #[inline(always)] fn simd_fract(self) -> Self { self.fract() } #[inline(always)] fn simd_mul_add(self, a: Self, b: Self) -> Self { self.mul_add(a, b) } #[inline(always)] fn simd_abs(self) -> Self::SimdRealField { self.abs() } #[inline(always)] fn simd_hypot(self, other: Self) -> Self::SimdRealField { self.hypot(other) } #[inline(always)] fn simd_recip(self) -> Self { self.recip() } #[inline(always)] fn simd_conjugate(self) -> Self { self.conjugate() } #[inline(always)] fn simd_sin(self) -> Self { self.sin() } #[inline(always)] fn simd_cos(self) -> Self { self.cos() } #[inline(always)] fn simd_sin_cos(self) -> (Self, Self) { self.sin_cos() } #[inline(always)] fn simd_sinh_cosh(self) -> (Self, Self) { self.sinh_cosh() } #[inline(always)] fn simd_tan(self) -> Self { self.tan() } #[inline(always)] fn simd_asin(self) -> Self { self.asin() } #[inline(always)] fn simd_acos(self) -> Self { self.acos() } #[inline(always)] fn simd_atan(self) -> Self { self.atan() } #[inline(always)] fn simd_sinh(self) -> Self { self.sinh() } #[inline(always)] fn simd_cosh(self) -> Self { self.cosh() } #[inline(always)] fn simd_tanh(self) -> Self { self.tanh() } #[inline(always)] fn simd_asinh(self) -> Self { self.asinh() } #[inline(always)] fn simd_acosh(self) -> Self { self.acosh() } #[inline(always)] fn simd_atanh(self) -> Self { self.atanh() } #[inline(always)] fn simd_sinc(self) -> Self { self.sinc() } #[inline(always)] fn simd_sinhc(self) -> Self { self.sinhc() } #[inline(always)] fn simd_cosc(self) -> Self { self.cosc() } #[inline(always)] fn simd_coshc(self) -> Self { self.coshc() } #[inline(always)] fn simd_log(self, base: Self::SimdRealField) -> Self { self.log(base) } #[inline(always)] fn simd_log2(self) -> Self { self.log2() } #[inline(always)] fn simd_log10(self) -> Self { self.log10() } #[inline(always)] fn simd_ln(self) -> Self { self.ln() } #[inline(always)] fn simd_ln_1p(self) -> Self { self.ln_1p() } #[inline(always)] fn simd_sqrt(self) -> Self { self.sqrt() } #[inline(always)] fn simd_exp(self) -> Self { self.exp() } #[inline(always)] fn simd_exp2(self) -> Self { self.exp2() } #[inline(always)] fn simd_exp_m1(self) -> Self { self.exp_m1() } #[inline(always)] fn simd_powi(self, n: i32) -> Self { self.powi(n) } #[inline(always)] fn simd_powf(self, n: Self::SimdRealField) -> Self { self.powf(n) } #[inline(always)] fn simd_powc(self, n: Self) -> Self { self.powc(n) } #[inline(always)] fn simd_cbrt(self) -> Self { self.cbrt() } #[inline(always)] fn simd_horizontal_sum(self) -> Self::Element { self } #[inline(always)] fn simd_horizontal_product(self) -> Self::Element { self } } simba-0.8.1/src/simd/simd_option.rs000064400000000000000000000047270072674642500154040ustar 00000000000000use crate::simd::{SimdBool, SimdValue}; //pub trait SimdOption { // type SimdValue: SimdValue; // // fn simd_unwrap(self) -> Self::SimdValue; // fn simd_unwrap_or(self, other: impl FnOnce() -> Self::SimdValue) -> Self::SimdValue; //} //impl SimdOption for Option { // type SimdValue = T; // // #[inline(always)] // fn simd_unwrap(self) -> T { // self.unwrap() // } // // #[inline(always)] // fn simd_unwrap_or(self, other: impl FnOnce() -> Self::SimdValue) -> Self::SimdValue { // self.unwrap_or_else(other) // } //} /// Generalization of Option for SIMD computation. pub struct SimdOption { val: V, mask: V::SimdBool, } impl SimdOption { /// Creates a new SIMD option by combining a value and a mask indicating what lane of the value is valid. pub fn new(val: V, mask: V::SimdBool) -> Self { SimdOption { val, mask } } /// Return the underlying SIMD boolean mask. pub fn mask(&self) -> V::SimdBool { self.mask } /// Return the underlying unfiltered value. pub fn value(&self) -> &V { &self.val } /// Converts this SIMD option to a strandard Option. /// /// If all the bits of `self.mask` are 1, then this returns `Some(self.value())`. /// If any bit of `self.mask` is 0, then this returns `None`. pub fn option(self) -> Option { if self.mask.all() { Some(self.val) } else { None } } /// Retrieve the underlying value if all the bits of `self.mask` are 1. /// /// Panics if any of the bits of `self.mask` is 0. #[inline] pub fn simd_unwrap(self) -> V { assert!( self.mask.all(), "Attempt to unwrap an SIMD value with at least one false lane." ); self.val } /// Merges the value of `self` with the value of `other`. /// /// Each lane of the result with a corresponding bit mask set to 1 will be filled with the corresponding lanes of `self.value()`. /// The lanes of the result with a corresponding bit mask set to 0 will be filled with the corresponding lanes of `other()`. /// /// The function in `other` should not do any side-effect. Indeed, implementors of this trait are free to decide in what /// cases `other` is called or not. #[inline(always)] pub fn simd_unwrap_or(self, other: impl FnOnce() -> V) -> V { self.val.select(self.mask, other()) } } simba-0.8.1/src/simd/simd_partial_ord.rs000064400000000000000000000051660072674642500163720ustar 00000000000000use crate::simd::SimdValue; /// Lane-wise generalization of the standard `PartialOrd` for SIMD values. pub trait SimdPartialOrd: SimdValue { /// Lanewise _greater than_ `>` comparison. fn simd_gt(self, other: Self) -> Self::SimdBool; /// Lanewise _less than_ `<` comparison. fn simd_lt(self, other: Self) -> Self::SimdBool; /// Lanewise _greater or equal_ `>=` comparison. fn simd_ge(self, other: Self) -> Self::SimdBool; /// Lanewise _less or equal_ `<=` comparison. fn simd_le(self, other: Self) -> Self::SimdBool; /// Lanewise _equal_ `==` comparison. fn simd_eq(self, other: Self) -> Self::SimdBool; /// Lanewise _not equal_ `!=` comparison. fn simd_ne(self, other: Self) -> Self::SimdBool; /// Lanewise max value. fn simd_max(self, other: Self) -> Self; /// Lanewise min value. fn simd_min(self, other: Self) -> Self; /// Clamps each lane of `self` between the corresponding lane of `min` and `max`. fn simd_clamp(self, min: Self, max: Self) -> Self; /// The min value among all lanes of `self`. fn simd_horizontal_min(self) -> Self::Element; /// The max value among all lanes of `self`. fn simd_horizontal_max(self) -> Self::Element; } impl> SimdPartialOrd for T { #[inline(always)] fn simd_gt(self, other: Self) -> Self::SimdBool { self > other } #[inline(always)] fn simd_lt(self, other: Self) -> Self::SimdBool { self < other } #[inline(always)] fn simd_ge(self, other: Self) -> Self::SimdBool { self >= other } #[inline(always)] fn simd_le(self, other: Self) -> Self::SimdBool { self <= other } #[inline(always)] fn simd_eq(self, other: Self) -> Self::SimdBool { self == other } #[inline(always)] fn simd_ne(self, other: Self) -> Self::SimdBool { self != other } #[inline(always)] fn simd_max(self, other: Self) -> Self { if self >= other { self } else { other } } #[inline(always)] fn simd_min(self, other: Self) -> Self { if self <= other { self } else { other } } #[inline(always)] fn simd_clamp(self, min: Self, max: Self) -> Self { if self < min { min } else if self > max { max } else { self } } #[inline(always)] fn simd_horizontal_min(self) -> Self::Element { self } #[inline(always)] fn simd_horizontal_max(self) -> Self::Element { self } } simba-0.8.1/src/simd/simd_real.rs000064400000000000000000000055630072674642500150160ustar 00000000000000use crate::scalar::RealField; use crate::simd::{SimdComplexField, SimdPartialOrd, SimdSigned}; /// Lanewise generalization of `RealField` for SIMD reals. /// /// Each lane of an SIMD real field should contain one real field. /// This is implemented by scalar reals like `f32` and `f64` as well as SIMD reals like `packed_simd::f32x4`. #[allow(missing_docs)] pub trait SimdRealField: SimdPartialOrd + SimdSigned + SimdComplexField { /// Copies the sign of `sign` to `self`. /// /// - Returns `self.simd_abs()` if `sign` is positive or positive-zero. /// - Returns `-self.simd_abs()` if `sign` is negative or negative-zero. fn simd_copysign(self, sign: Self) -> Self; fn simd_atan2(self, other: Self) -> Self; fn simd_default_epsilon() -> Self; fn simd_pi() -> Self; fn simd_two_pi() -> Self; fn simd_frac_pi_2() -> Self; fn simd_frac_pi_3() -> Self; fn simd_frac_pi_4() -> Self; fn simd_frac_pi_6() -> Self; fn simd_frac_pi_8() -> Self; fn simd_frac_1_pi() -> Self; fn simd_frac_2_pi() -> Self; fn simd_frac_2_sqrt_pi() -> Self; fn simd_e() -> Self; fn simd_log2_e() -> Self; fn simd_log10_e() -> Self; fn simd_ln_2() -> Self; fn simd_ln_10() -> Self; } // Blanket impl RealField => SimdRealField impl SimdRealField for T { #[inline(always)] fn simd_atan2(self, other: Self) -> Self { self.atan2(other) } #[inline(always)] fn simd_default_epsilon() -> Self { Self::default_epsilon() } #[inline(always)] fn simd_copysign(self, sign: Self) -> Self { self.copysign(sign) } #[inline(always)] fn simd_pi() -> Self { Self::pi() } #[inline(always)] fn simd_two_pi() -> Self { Self::two_pi() } #[inline(always)] fn simd_frac_pi_2() -> Self { Self::frac_pi_2() } #[inline(always)] fn simd_frac_pi_3() -> Self { Self::frac_pi_3() } #[inline(always)] fn simd_frac_pi_4() -> Self { Self::frac_pi_4() } #[inline(always)] fn simd_frac_pi_6() -> Self { Self::frac_pi_6() } #[inline(always)] fn simd_frac_pi_8() -> Self { Self::frac_pi_8() } #[inline(always)] fn simd_frac_1_pi() -> Self { Self::frac_1_pi() } #[inline(always)] fn simd_frac_2_pi() -> Self { Self::frac_2_pi() } #[inline(always)] fn simd_frac_2_sqrt_pi() -> Self { Self::frac_2_sqrt_pi() } #[inline(always)] fn simd_e() -> Self { Self::e() } #[inline(always)] fn simd_log2_e() -> Self { Self::log2_e() } #[inline(always)] fn simd_log10_e() -> Self { Self::log10_e() } #[inline(always)] fn simd_ln_2() -> Self { Self::ln_2() } #[inline(always)] fn simd_ln_10() -> Self { Self::ln_10() } } simba-0.8.1/src/simd/simd_signed.rs000064400000000000000000000026050072674642500153360ustar 00000000000000use crate::simd::SimdValue; use num::Signed; /// A lane-wise generalization of [`num::Signed`](https://rust-num.github.io/num/num/trait.Signed.html) for SIMD values. pub trait SimdSigned: SimdValue { /// The absolute value of each lane of `self`. fn simd_abs(&self) -> Self; /// The absolute difference of each lane of `self`. /// /// For each lane, this zero if the lane of self is less than or equal to the corresponding lane of other /// otherwise the difference between the lane of self and the lane of other is returned. fn simd_abs_sub(&self, other: &Self) -> Self; /// The signum of each lane of `Self`. fn simd_signum(&self) -> Self; /// Tests which lane is positive. fn is_simd_positive(&self) -> Self::SimdBool; /// Tests which lane is negative. fn is_simd_negative(&self) -> Self::SimdBool; } impl> SimdSigned for T { #[inline(always)] fn simd_abs(&self) -> Self { self.abs() } #[inline(always)] fn simd_abs_sub(&self, other: &Self) -> Self { self.abs_sub(other) } #[inline(always)] fn simd_signum(&self) -> Self { self.signum() } #[inline(always)] fn is_simd_positive(&self) -> Self::SimdBool { self.is_positive() } #[inline(always)] fn is_simd_negative(&self) -> Self::SimdBool { self.is_negative() } } simba-0.8.1/src/simd/simd_value.rs000064400000000000000000000140170072674642500152010ustar 00000000000000use crate::simd::SimdBool; /// Base trait for every SIMD types. pub trait SimdValue: Sized { /// The type of the elements of each lane of this SIMD value. type Element: SimdValue; /// Type of the result of comparing two SIMD values like `self`. type SimdBool: SimdBool; /// The number of lanes of this SIMD value. fn lanes() -> usize; /// Initializes an SIMD value with each lanes set to `val`. fn splat(val: Self::Element) -> Self; /// Extracts the i-th lane of `self`. /// /// Panics if `i >= Self::lanes()`. fn extract(&self, i: usize) -> Self::Element; /// Extracts the i-th lane of `self` without bound-checking. unsafe fn extract_unchecked(&self, i: usize) -> Self::Element; /// Replaces the i-th lane of `self` by `val`. /// /// Panics if `i >= Self::lanes()`. fn replace(&mut self, i: usize, val: Self::Element); /// Replaces the i-th lane of `self` by `val` without bound-checking. unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element); /// Merges `self` and `other` depending on the lanes of `cond`. /// /// For each lane of `cond` with bits set to 1, the result's will contain the value of the lane of `self`. /// For each lane of `cond` with bits set to 0, the result's will contain the value of the lane of `other`. fn select(self, cond: Self::SimdBool, other: Self) -> Self; /// Applies a function to each lane of `self`. /// /// Note that, while convenient, this method can be extremely slow as this /// requires to extract each lane of `self` and then combine them again into /// a new SIMD value. #[inline(always)] fn map_lanes(self, f: impl Fn(Self::Element) -> Self::Element) -> Self where Self: Clone, { let mut result = self.clone(); for i in 0..Self::lanes() { unsafe { result.replace_unchecked(i, f(self.extract_unchecked(i))) } } result } /// Applies a function to each lane of `self` paired with the corresponding lane of `b`. /// /// Note that, while convenient, this method can be extremely slow as this /// requires to extract each lane of `self` and then combine them again into /// a new SIMD value. #[inline(always)] fn zip_map_lanes( self, b: Self, f: impl Fn(Self::Element, Self::Element) -> Self::Element, ) -> Self where Self: Clone, { let mut result = self.clone(); for i in 0..Self::lanes() { unsafe { let a = self.extract_unchecked(i); let b = b.extract_unchecked(i); result.replace_unchecked(i, f(a, b)) } } result } } /// Marker trait implemented by SIMD and non-SIMD primitive numeric values. /// /// This trait is useful for some disambiguations when writing blanked impls. /// This is implemented by all unsigned integer, integer, float, and complex types, as /// with only one lane, i.e., `f32`, `f64`, `u32`, `i64`, etc. as well as SIMD types like /// `f32x4, i32x8`, etc.. pub trait PrimitiveSimdValue: Copy + SimdValue {} impl SimdValue for num_complex::Complex { type Element = num_complex::Complex; type SimdBool = N::SimdBool; #[inline(always)] fn lanes() -> usize { N::lanes() } #[inline(always)] fn splat(val: Self::Element) -> Self { num_complex::Complex { re: N::splat(val.re), im: N::splat(val.im), } } #[inline(always)] fn extract(&self, i: usize) -> Self::Element { num_complex::Complex { re: self.re.extract(i), im: self.im.extract(i), } } #[inline(always)] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { num_complex::Complex { re: self.re.extract_unchecked(i), im: self.im.extract_unchecked(i), } } #[inline(always)] fn replace(&mut self, i: usize, val: Self::Element) { self.re.replace(i, val.re); self.im.replace(i, val.im); } #[inline(always)] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.re.replace_unchecked(i, val.re); self.im.replace_unchecked(i, val.im); } #[inline(always)] fn select(self, cond: Self::SimdBool, other: Self) -> Self { num_complex::Complex { re: self.re.select(cond, other.re), im: self.im.select(cond, other.im), } } } impl PrimitiveSimdValue for num_complex::Complex {} macro_rules! impl_primitive_simd_value_for_scalar( ($($t: ty),*) => {$( impl PrimitiveSimdValue for $t {} impl SimdValue for $t { type Element = $t; type SimdBool = bool; #[inline(always)] fn lanes() -> usize { 1 } #[inline(always)] fn splat(val: Self::Element) -> Self { val } #[inline(always)] fn extract(&self, _: usize) -> Self::Element { *self } #[inline(always)] unsafe fn extract_unchecked(&self, _: usize) -> Self::Element { *self } #[inline(always)] fn replace(&mut self, _: usize, val: Self::Element) { *self = val } #[inline(always)] unsafe fn replace_unchecked(&mut self, _: usize, val: Self::Element) { *self = val } #[inline(always)] fn select(self, cond: Self::SimdBool, other: Self) -> Self { if cond { self } else { other } } } )*} ); impl_primitive_simd_value_for_scalar!( bool, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64 ); #[cfg(feature = "decimal")] impl_primitive_simd_value_for_scalar!(decimal::d128); simba-0.8.1/src/simd/wide_simd_impl.rs000064400000000000000000001437070072674642500160470ustar 00000000000000#![allow(missing_docs)] #![allow(non_camel_case_types)] // For the simd type aliases. //! Traits for SIMD values. use crate::scalar::{ComplexField, Field, SubsetOf, SupersetOf}; use crate::simd::{ PrimitiveSimdValue, SimdBool, SimdComplexField, SimdPartialOrd, SimdRealField, SimdSigned, SimdValue, }; use approx::AbsDiffEq; use num::{FromPrimitive, Num, One, Zero}; use num_traits::Bounded; use std::{ cmp::PartialEq, ops::{ Add, AddAssign, BitAnd, BitOr, BitXor, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Sub, SubAssign, }, }; use wide::{CmpEq, CmpGe, CmpGt, CmpLe, CmpLt, CmpNe}; #[cfg(feature = "rkyv")] macro_rules! impl_rkyv { ($type:ty, $array:ty) => { impl rkyv::Archive for $type { type Archived = $array; type Resolver = (); #[inline] unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { out.write((*self).into_arr()); } } impl rkyv::Serialize for $type { #[inline] fn serialize(&self, _: &mut S) -> Result { Ok(()) } } impl rkyv::Deserialize<$type, D> for rkyv::Archived<$type> { #[inline] fn deserialize(&self, _: &mut D) -> Result<$type, D::Error> { Ok(<$type>::from_arr(*self)) } } }; } /// A wrapper type of `wide::f32x4` that implements all the relevant traits from `num` and `simba`. /// /// This is needed to overcome the orphan rules. #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct WideF32x4(pub wide::f32x4); #[cfg(feature = "rkyv")] impl_rkyv!(WideF32x4, [f32; 4]); /// An SIMD boolean structure associated to `wide::f32x4` that implements all the relevant traits from `simba`. /// /// This is needed to overcome the orphan rules. #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct WideBoolF32x4(pub wide::f32x4); #[cfg(feature = "rkyv")] impl_rkyv!(WideBoolF32x4, [f32; 4]); /// A wrapper type of `wide::f32x8` that implements all the relevant traits from `num` and `simba`. /// /// This is needed to overcome the orphan rules. #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct WideF32x8(pub wide::f32x8); #[cfg(feature = "rkyv")] impl_rkyv!(WideF32x8, [f32; 8]); /// An SIMD boolean structure associated to `wide::f32x8` that implements all the relevant traits from `simba`. /// /// This is needed to overcome the orphan rules. #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct WideBoolF32x8(pub wide::f32x8); #[cfg(feature = "rkyv")] impl_rkyv!(WideBoolF32x8, [f32; 8]); /// A wrapper type of `wide::f64x4` that implements all the relevant traits from `num` and `simba`. /// /// This is needed to overcome the orphan rules. #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct WideF64x4(pub wide::f64x4); #[cfg(feature = "rkyv")] impl_rkyv!(WideF64x4, [f64; 4]); /// An SIMD boolean structure associated to `wide::f64x4` that implements all the relevant traits from `simba`. /// /// This is needed to overcome the orphan rules. #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct WideBoolF64x4(pub wide::f64x4); #[cfg(feature = "rkyv")] impl_rkyv!(WideBoolF64x4, [f64; 4]); macro_rules! impl_wide_f32( ($f32: ident, $f32xX: ident, $WideF32xX: ident, $WideBoolF32xX: ident, $lanes: expr; $($ii: expr),+) => { impl PrimitiveSimdValue for $WideF32xX {} impl PrimitiveSimdValue for $WideBoolF32xX {} impl $WideF32xX { #[inline(always)] fn into_arr(self) -> [$f32; $lanes] { self.0.into() } #[inline(always)] fn from_arr(arr: [$f32; $lanes]) -> Self { Self(arr.into()) } #[inline(always)] fn map(self, f: impl Fn($f32) -> $f32) -> Self { let arr = self.into_arr(); Self::from([f(arr[0]), $(f(arr[$ii])),+]) } #[inline(always)] fn zip_map(self, rhs: Self, f: impl Fn($f32, $f32) -> $f32) -> Self { let arr = self.into_arr(); let rhs = rhs.into_arr(); Self::from([ f(arr[0], rhs[0]), $(f(arr[$ii], rhs[$ii])),+ ]) } } impl $WideBoolF32xX { fn from_arr(arr: [$f32; $lanes]) -> Self { Self(arr.into()) } fn into_arr(self) -> [$f32; $lanes] { self.0.into() } } impl SimdValue for $WideF32xX { type Element = $f32; type SimdBool = $WideBoolF32xX; #[inline(always)] fn lanes() -> usize { $lanes } #[inline(always)] fn splat(val: Self::Element) -> Self { $WideF32xX(wide::$f32xX::from(val)) } #[inline(always)] fn extract(&self, i: usize) -> Self::Element { self.into_arr()[i] } #[inline(always)] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { *self.into_arr().get_unchecked(i) } #[inline(always)] fn replace(&mut self, i: usize, val: Self::Element) { let mut arr = self.into_arr(); arr[i] = val; *self = Self::from(arr); } #[inline(always)] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { let mut arr = self.into_arr(); *arr.get_unchecked_mut(i) = val; *self = Self::from(arr); } #[inline(always)] fn select(self, cond: Self::SimdBool, other: Self) -> Self { $WideF32xX(cond.0.blend(self.0, other.0)) } } impl SimdValue for $WideBoolF32xX { type Element = bool; type SimdBool = Self; #[inline(always)] fn lanes() -> usize { $lanes } #[inline(always)] fn splat(val: bool) -> Self { let results = [ $WideBoolF32xX(wide::$f32xX::ZERO), $WideBoolF32xX(!wide::$f32xX::ZERO), ]; results[val as usize] } #[inline(always)] fn extract(&self, i: usize) -> Self::Element { self.into_arr()[i] != 0.0 } #[inline(always)] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { *self.into_arr().get_unchecked(i) != 0.0 } #[inline(always)] fn replace(&mut self, i: usize, val: Self::Element) { let vals = [0.0, <$f32>::from_bits(Bounded::max_value())]; let mut arr = self.into_arr(); arr[i] = vals[val as usize]; *self = Self::from_arr(arr); } #[inline(always)] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { let vals = [0.0, <$f32>::from_bits(Bounded::max_value())]; let mut arr = self.into_arr(); *arr.get_unchecked_mut(i) = vals[val as usize]; *self = Self::from_arr(arr); } #[inline(always)] fn select(self, cond: Self::SimdBool, other: Self) -> Self { $WideBoolF32xX(cond.0.blend(self.0, other.0)) } } impl PartialEq for $WideF32xX { #[inline] fn eq(&self, rhs: &Self) -> bool { self.0 == rhs.0 } } impl PartialEq for $WideBoolF32xX { #[inline] fn eq(&self, rhs: &Self) -> bool { self.0 == rhs.0 } } impl Not for $WideBoolF32xX { type Output = Self; #[inline] fn not(self) -> Self { Self(!self.0) } } impl BitXor for $WideBoolF32xX { type Output = Self; #[inline] fn bitxor(self, rhs: Self) -> Self { Self(self.0 ^ rhs.0) } } impl BitOr for $WideBoolF32xX { type Output = Self; #[inline] fn bitor(self, rhs: Self) -> Self { Self(self.0 | rhs.0) } } impl BitAnd for $WideBoolF32xX { type Output = Self; #[inline] fn bitand(self, rhs: Self) -> Self { Self(self.0 & rhs.0) } } impl SimdBool for $WideBoolF32xX { #[inline(always)] fn bitmask(self) -> u64 { let arr = self.into_arr(); (((arr[0] != 0.0) as u64) << 0) $(| (((arr[$ii] != 0.0) as u64) << $ii))* } #[inline(always)] fn and(self) -> bool { let arr = self.into_arr(); (arr[0].to_bits() $(& arr[$ii].to_bits())*) != 0 } #[inline(always)] fn or(self) -> bool { let arr = self.into_arr(); (arr[0].to_bits() $(| arr[$ii].to_bits())*) != 0 } #[inline(always)] fn xor(self) -> bool { let arr = self.into_arr(); (arr[0].to_bits() $(^ arr[$ii].to_bits())*) != 0 } #[inline(always)] fn all(self) -> bool { self == Self(!wide::$f32xX::ZERO) } #[inline(always)] fn any(self) -> bool { self != Self(wide::$f32xX::ZERO) } #[inline(always)] fn none(self) -> bool { self == Self(wide::$f32xX::ZERO) } #[inline(always)] fn if_else>( self, if_value: impl FnOnce() -> Res, else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_value(); a.select(self, b) } #[inline(always)] fn if_else2>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_if.1(); let c = else_value(); let cond_a = self; let cond_b = else_if.0(); a.select(cond_a, b.select(cond_b, c)) } #[inline(always)] fn if_else3>( self, if_value: impl FnOnce() -> Res, else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_else_if: (impl FnOnce() -> Self, impl FnOnce() -> Res), else_value: impl FnOnce() -> Res, ) -> Res { let a = if_value(); let b = else_if.1(); let c = else_else_if.1(); let d = else_value(); let cond_a = self; let cond_b = else_if.0(); let cond_c = else_else_if.0(); a.select(cond_a, b.select(cond_b, c.select(cond_c, d))) } } impl From<[$f32; $lanes]> for $WideF32xX { #[inline(always)] fn from(vals: [$f32; $lanes]) -> Self { $WideF32xX(wide::$f32xX::from(vals)) } } impl From<$WideF32xX> for [$f32; $lanes] { #[inline(always)] fn from(val: $WideF32xX) -> [$f32; $lanes] { val.0.into() } } impl SubsetOf<$WideF32xX> for $WideF32xX { #[inline(always)] fn to_superset(&self) -> Self { *self } #[inline(always)] fn from_superset(element: &Self) -> Option { Some(*element) } #[inline(always)] fn from_superset_unchecked(element: &Self) -> Self { *element } #[inline(always)] fn is_in_subset(_: &Self) -> bool { true } } impl From<[bool; $lanes]> for $WideBoolF32xX { #[inline(always)] fn from(vals: [bool; $lanes]) -> Self { let bits = [0.0, <$f32>::from_bits(Bounded::max_value())]; $WideBoolF32xX(wide::$f32xX::from([ bits[vals[0] as usize], $(bits[vals[$ii] as usize]),* ])) } } impl SubsetOf<$WideBoolF32xX> for $WideBoolF32xX { #[inline(always)] fn to_superset(&self) -> Self { *self } #[inline(always)] fn from_superset(element: &Self) -> Option { Some(*element) } #[inline(always)] fn from_superset_unchecked(element: &Self) -> Self { *element } #[inline(always)] fn is_in_subset(_: &Self) -> bool { true } } impl Num for $WideF32xX { type FromStrRadixErr = <$f32 as Num>::FromStrRadixErr; #[inline(always)] fn from_str_radix(str: &str, radix: u32) -> Result { <$f32>::from_str_radix(str, radix).map(Self::splat) } } impl FromPrimitive for $WideF32xX { #[inline(always)] fn from_i64(n: i64) -> Option { <$f32>::from_i64(n).map(Self::splat) } #[inline(always)] fn from_u64(n: u64) -> Option { <$f32>::from_u64(n).map(Self::splat) } #[inline(always)] fn from_isize(n: isize) -> Option { <$f32>::from_isize(n).map(Self::splat) } #[inline(always)] fn from_i8(n: i8) -> Option { <$f32>::from_i8(n).map(Self::splat) } #[inline(always)] fn from_i16(n: i16) -> Option { <$f32>::from_i16(n).map(Self::splat) } #[inline(always)] fn from_i32(n: i32) -> Option { <$f32>::from_i32(n).map(Self::splat) } #[inline(always)] fn from_usize(n: usize) -> Option { <$f32>::from_usize(n).map(Self::splat) } #[inline(always)] fn from_u8(n: u8) -> Option { <$f32>::from_u8(n).map(Self::splat) } #[inline(always)] fn from_u16(n: u16) -> Option { <$f32>::from_u16(n).map(Self::splat) } #[inline(always)] fn from_u32(n: u32) -> Option { <$f32>::from_u32(n).map(Self::splat) } #[inline(always)] fn from_f32(n: f32) -> Option { <$f32>::from_f32(n).map(Self::splat) } #[inline(always)] fn from_f64(n: f64) -> Option { <$f32>::from_f64(n).map(Self::splat) } } impl Zero for $WideF32xX { #[inline(always)] fn zero() -> Self { <$WideF32xX>::splat(<$f32>::zero()) } #[inline(always)] fn is_zero(&self) -> bool { *self == Self::zero() } } impl One for $WideF32xX { #[inline(always)] fn one() -> Self { <$WideF32xX>::splat(<$f32>::one()) } } impl Add<$WideF32xX> for $WideF32xX { type Output = Self; #[inline(always)] fn add(self, rhs: Self) -> Self { Self(self.0 + rhs.0) } } impl Sub<$WideF32xX> for $WideF32xX { type Output = Self; #[inline(always)] fn sub(self, rhs: Self) -> Self { Self(self.0 - rhs.0) } } impl Mul<$WideF32xX> for $WideF32xX { type Output = Self; #[inline(always)] fn mul(self, rhs: Self) -> Self { Self(self.0 * rhs.0) } } impl Div<$WideF32xX> for $WideF32xX { type Output = Self; #[inline(always)] fn div(self, rhs: Self) -> Self { Self(self.0 / rhs.0) } } impl Rem<$WideF32xX> for $WideF32xX { type Output = Self; #[inline(always)] fn rem(self, rhs: Self) -> Self { self.zip_map(rhs, |a, b| a % b) } } impl AddAssign<$WideF32xX> for $WideF32xX { #[inline(always)] fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 } } impl SubAssign<$WideF32xX> for $WideF32xX { #[inline(always)] fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 } } impl DivAssign<$WideF32xX> for $WideF32xX { #[inline(always)] fn div_assign(&mut self, rhs: Self) { self.0 /= rhs.0 } } impl MulAssign<$WideF32xX> for $WideF32xX { #[inline(always)] fn mul_assign(&mut self, rhs: Self) { self.0 *= rhs.0 } } impl RemAssign<$WideF32xX> for $WideF32xX { #[inline(always)] fn rem_assign(&mut self, rhs: Self) { *self = *self % rhs; } } impl SimdPartialOrd for $WideF32xX { #[inline(always)] fn simd_gt(self, other: Self) -> Self::SimdBool { $WideBoolF32xX(self.0.cmp_gt(other.0)) } #[inline(always)] fn simd_lt(self, other: Self) -> Self::SimdBool { $WideBoolF32xX(self.0.cmp_lt(other.0)) } #[inline(always)] fn simd_ge(self, other: Self) -> Self::SimdBool { $WideBoolF32xX(self.0.cmp_ge(other.0)) } #[inline(always)] fn simd_le(self, other: Self) -> Self::SimdBool { $WideBoolF32xX(self.0.cmp_le(other.0)) } #[inline(always)] fn simd_eq(self, other: Self) -> Self::SimdBool { $WideBoolF32xX(self.0.cmp_eq(other.0)) } #[inline(always)] fn simd_ne(self, other: Self) -> Self::SimdBool { $WideBoolF32xX(self.0.cmp_ne(other.0)) } #[inline(always)] fn simd_max(self, other: Self) -> Self { $WideF32xX(self.0.max(other.0)) } #[inline(always)] fn simd_min(self, other: Self) -> Self { $WideF32xX(self.0.min(other.0)) } #[inline(always)] fn simd_clamp(self, min: Self, max: Self) -> Self { self.simd_min(max).simd_max(min) } #[inline(always)] fn simd_horizontal_min(self) -> Self::Element { let arr = self.into_arr(); arr[0]$(.min(arr[$ii]))* } #[inline(always)] fn simd_horizontal_max(self) -> Self::Element { let arr = self.into_arr(); arr[0]$(.max(arr[$ii]))* } } impl Neg for $WideF32xX { type Output = Self; #[inline(always)] fn neg(self) -> Self { Self(-self.0) } } impl SimdSigned for $WideF32xX { #[inline(always)] fn simd_abs(&self) -> Self { $WideF32xX(self.0.abs()) } #[inline(always)] fn simd_abs_sub(&self, other: &Self) -> Self { $WideF32xX((self.0 - other.0).max(Self::zero().0)) } #[inline(always)] fn simd_signum(&self) -> Self { // FIXME: is there a more efficient way? self.map(|x| x.signum()) } #[inline(always)] fn is_simd_positive(&self) -> Self::SimdBool { self.simd_gt(Self::zero()) } #[inline(always)] fn is_simd_negative(&self) -> Self::SimdBool { self.simd_lt(Self::zero()) } } impl Field for $WideF32xX {} impl SimdRealField for $WideF32xX { #[inline(always)] fn simd_atan2(self, other: Self) -> Self { self.zip_map_lanes(other, |a, b| a.atan2(b)) } #[inline(always)] fn simd_copysign(self, sign: Self) -> Self { let neg_zero = wide::$f32xX::from(-0.0); $WideF32xX((neg_zero & sign.0) | ((!neg_zero) & self.0)) } #[inline(always)] fn simd_default_epsilon() -> Self { Self::splat(<$f32>::default_epsilon()) } #[inline(always)] fn simd_pi() -> Self { $WideF32xX(wide::$f32xX::PI) } #[inline(always)] fn simd_two_pi() -> Self { $WideF32xX(wide::$f32xX::PI + wide::$f32xX::PI) } #[inline(always)] fn simd_frac_pi_2() -> Self { $WideF32xX(wide::$f32xX::FRAC_PI_2) } #[inline(always)] fn simd_frac_pi_3() -> Self { $WideF32xX(wide::$f32xX::FRAC_PI_3) } #[inline(always)] fn simd_frac_pi_4() -> Self { $WideF32xX(wide::$f32xX::FRAC_PI_4) } #[inline(always)] fn simd_frac_pi_6() -> Self { $WideF32xX(wide::$f32xX::FRAC_PI_6) } #[inline(always)] fn simd_frac_pi_8() -> Self { $WideF32xX(wide::$f32xX::FRAC_PI_8) } #[inline(always)] fn simd_frac_1_pi() -> Self { $WideF32xX(wide::$f32xX::FRAC_1_PI) } #[inline(always)] fn simd_frac_2_pi() -> Self { $WideF32xX(wide::$f32xX::FRAC_2_PI) } #[inline(always)] fn simd_frac_2_sqrt_pi() -> Self { $WideF32xX(wide::$f32xX::FRAC_2_SQRT_PI) } #[inline(always)] fn simd_e() -> Self { $WideF32xX(wide::$f32xX::E) } #[inline(always)] fn simd_log2_e() -> Self { $WideF32xX(wide::$f32xX::LOG2_E) } #[inline(always)] fn simd_log10_e() -> Self { $WideF32xX(wide::$f32xX::LOG10_E) } #[inline(always)] fn simd_ln_2() -> Self { $WideF32xX(wide::$f32xX::LN_2) } #[inline(always)] fn simd_ln_10() -> Self { $WideF32xX(wide::$f32xX::LN_10) } } impl SimdComplexField for $WideF32xX { type SimdRealField = Self; #[inline(always)] fn simd_horizontal_sum(self) -> Self::Element { self.0.reduce_add() } #[inline(always)] fn simd_horizontal_product(self) -> Self::Element { self.extract(0) $(* self.extract($ii))* } #[inline(always)] fn from_simd_real(re: Self::SimdRealField) -> Self { re } #[inline(always)] fn simd_real(self) -> Self::SimdRealField { self } #[inline(always)] fn simd_imaginary(self) -> Self::SimdRealField { Self::zero() } #[inline(always)] fn simd_norm1(self) -> Self::SimdRealField { $WideF32xX(self.0.abs()) } #[inline(always)] fn simd_modulus(self) -> Self::SimdRealField { $WideF32xX(self.0.abs()) } #[inline(always)] fn simd_modulus_squared(self) -> Self::SimdRealField { self * self } #[inline(always)] fn simd_argument(self) -> Self::SimdRealField { self.map_lanes(|e| e.argument()) } #[inline(always)] fn simd_to_exp(self) -> (Self::SimdRealField, Self) { let ge = self.0.cmp_ge(Self::one().0); let exp = ge.blend(Self::one().0, -Self::one().0); ($WideF32xX(self.0 * exp), $WideF32xX(exp)) } #[inline(always)] fn simd_recip(self) -> Self { Self::one() / self } #[inline(always)] fn simd_conjugate(self) -> Self { self } #[inline(always)] fn simd_scale(self, factor: Self::SimdRealField) -> Self { $WideF32xX(self.0 * factor.0) } #[inline(always)] fn simd_unscale(self, factor: Self::SimdRealField) -> Self { $WideF32xX(self.0 / factor.0) } #[inline(always)] fn simd_floor(self) -> Self { self.map_lanes(|e| e.floor()) } #[inline(always)] fn simd_ceil(self) -> Self { self.map_lanes(|e| e.ceil()) } #[inline(always)] fn simd_round(self) -> Self { self.map_lanes(|e| e.round()) } #[inline(always)] fn simd_trunc(self) -> Self { self.map_lanes(|e| e.trunc()) } #[inline(always)] fn simd_fract(self) -> Self { self.map_lanes(|e| e.fract()) } #[inline(always)] fn simd_abs(self) -> Self { $WideF32xX(self.0.abs()) } #[inline(always)] fn simd_signum(self) -> Self { self.map_lanes(|e| e.signum()) } #[inline(always)] fn simd_mul_add(self, a: Self, b: Self) -> Self { $WideF32xX(self.0.mul_add(a.0, b.0)) } #[inline(always)] fn simd_powi(self, n: i32) -> Self { self.map_lanes(|e| e.powi(n)) } #[inline(always)] fn simd_powf(self, n: Self) -> Self { self.zip_map_lanes(n, |e, n| e.powf(n)) } #[inline(always)] fn simd_powc(self, n: Self) -> Self { self.zip_map_lanes(n, |e, n| e.powf(n)) } #[inline(always)] fn simd_sqrt(self) -> Self { $WideF32xX(self.0.sqrt()) } #[inline(always)] fn simd_exp(self) -> Self { self.map_lanes(|e| e.exp()) } #[inline(always)] fn simd_exp2(self) -> Self { self.map_lanes(|e| e.exp2()) } #[inline(always)] fn simd_exp_m1(self) -> Self { self.map_lanes(|e| e.exp_m1()) } #[inline(always)] fn simd_ln_1p(self) -> Self { self.map_lanes(|e| e.ln_1p()) } #[inline(always)] fn simd_ln(self) -> Self { self.map_lanes(|e| e.ln()) } #[inline(always)] fn simd_log(self, base: Self) -> Self { self.zip_map_lanes(base, |e, b| e.log(b)) } #[inline(always)] fn simd_log2(self) -> Self { self.map_lanes(|e| e.log2()) } #[inline(always)] fn simd_log10(self) -> Self { self.map_lanes(|e| e.log10()) } #[inline(always)] fn simd_cbrt(self) -> Self { self.map_lanes(|e| e.cbrt()) } #[inline(always)] fn simd_hypot(self, other: Self) -> Self::SimdRealField { self.zip_map_lanes(other, |e, o| e.hypot(o)) } #[inline(always)] fn simd_sin(self) -> Self { $WideF32xX(self.0.sin()) } #[inline(always)] fn simd_cos(self) -> Self { $WideF32xX(self.0.cos()) } #[inline(always)] fn simd_tan(self) -> Self { self.map_lanes(|e| e.tan()) } #[inline(always)] fn simd_asin(self) -> Self { self.map_lanes(|e| e.asin()) } #[inline(always)] fn simd_acos(self) -> Self { self.map_lanes(|e| e.acos()) } #[inline(always)] fn simd_atan(self) -> Self { self.map_lanes(|e| e.atan()) } #[inline(always)] fn simd_sin_cos(self) -> (Self, Self) { (self.simd_sin(), self.simd_cos()) } // #[inline(always] // fn simd_exp_m1(self) -> Self { // $libm::exp_m1(self) // } // // #[inline(always] // fn simd_ln_1p(self) -> Self { // $libm::ln_1p(self) // } // #[inline(always)] fn simd_sinh(self) -> Self { self.map_lanes(|e| e.sinh()) } #[inline(always)] fn simd_cosh(self) -> Self { self.map_lanes(|e| e.cosh()) } #[inline(always)] fn simd_tanh(self) -> Self { self.map_lanes(|e| e.tanh()) } #[inline(always)] fn simd_asinh(self) -> Self { self.map_lanes(|e| e.asinh()) } #[inline(always)] fn simd_acosh(self) -> Self { self.map_lanes(|e| e.acosh()) } #[inline(always)] fn simd_atanh(self) -> Self { self.map_lanes(|e| e.atanh()) } } // NOTE: most of the impls in there are copy-paste from the implementation of // ComplexField for num_complex::Complex. Unfortunately, we can't reuse the implementations // so easily. impl SimdComplexField for num_complex::Complex<$WideF32xX> { type SimdRealField = $WideF32xX; #[inline(always)] fn simd_horizontal_sum(self) -> Self::Element { num_complex::Complex::new(self.re.simd_horizontal_sum(), self.im.simd_horizontal_sum()) } #[inline(always)] fn simd_horizontal_product(self) -> Self::Element { let mut prod = self.extract(0); for ii in 1..Self::lanes() { prod = prod * self.extract(ii) } prod } #[inline] fn from_simd_real(re: Self::SimdRealField) -> Self { Self::new(re, Self::SimdRealField::zero()) } #[inline] fn simd_real(self) -> Self::SimdRealField { self.re } #[inline] fn simd_imaginary(self) -> Self::SimdRealField { self.im } #[inline] fn simd_argument(self) -> Self::SimdRealField { self.im.simd_atan2(self.re) } #[inline] fn simd_modulus(self) -> Self::SimdRealField { self.re.simd_hypot(self.im) } #[inline] fn simd_modulus_squared(self) -> Self::SimdRealField { self.re * self.re + self.im * self.im } #[inline] fn simd_norm1(self) -> Self::SimdRealField { self.re.simd_abs() + self.im.simd_abs() } #[inline] fn simd_recip(self) -> Self { Self::one() / self } #[inline] fn simd_conjugate(self) -> Self { self.conj() } #[inline] fn simd_scale(self, factor: Self::SimdRealField) -> Self { self * factor } #[inline] fn simd_unscale(self, factor: Self::SimdRealField) -> Self { self / factor } #[inline] fn simd_floor(self) -> Self { Self::new(self.re.simd_floor(), self.im.simd_floor()) } #[inline] fn simd_ceil(self) -> Self { Self::new(self.re.simd_ceil(), self.im.simd_ceil()) } #[inline] fn simd_round(self) -> Self { Self::new(self.re.simd_round(), self.im.simd_round()) } #[inline] fn simd_trunc(self) -> Self { Self::new(self.re.simd_trunc(), self.im.simd_trunc()) } #[inline] fn simd_fract(self) -> Self { Self::new(self.re.simd_fract(), self.im.simd_fract()) } #[inline] fn simd_mul_add(self, a: Self, b: Self) -> Self { self * a + b } #[inline] fn simd_abs(self) -> Self::SimdRealField { self.simd_modulus() } #[inline] fn simd_exp2(self) -> Self { let _2 = <$WideF32xX>::one() + <$WideF32xX>::one(); num_complex::Complex::new(_2, <$WideF32xX>::zero()).simd_powc(self) } #[inline] fn simd_exp_m1(self) -> Self { self.simd_exp() - Self::one() } #[inline] fn simd_ln_1p(self) -> Self { (Self::one() + self).simd_ln() } #[inline] fn simd_log2(self) -> Self { let _2 = <$WideF32xX>::one() + <$WideF32xX>::one(); self.simd_log(_2) } #[inline] fn simd_log10(self) -> Self { let _10 = <$WideF32xX>::from_subset(&10.0f64); self.simd_log(_10) } #[inline] fn simd_cbrt(self) -> Self { let one_third = <$WideF32xX>::from_subset(&(1.0 / 3.0)); self.simd_powf(one_third) } #[inline] fn simd_powi(self, n: i32) -> Self { // FIXME: is there a more accurate solution? let n = <$WideF32xX>::from_subset(&(n as f64)); self.simd_powf(n) } /* * * * Unfortunately we are forced to copy-paste all * those impls from https://github.com/rust-num/num-complex/blob/master/src/lib.rs * to avoid requiring `std`. * * */ /// Computes `e^(self)`, where `e` is the base of the natural logarithm. #[inline] fn simd_exp(self) -> Self { // formula: e^(a + bi) = e^a (cos(b) + i*sin(b)) // = from_polar(e^a, b) simd_complex_from_polar(self.re.simd_exp(), self.im) } /// Computes the principal value of natural logarithm of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0]`, continuous from above. /// /// The branch satisfies `-π ≤ arg(ln(z)) ≤ π`. #[inline] fn simd_ln(self) -> Self { // formula: ln(z) = ln|z| + i*arg(z) let (r, theta) = self.simd_to_polar(); Self::new(r.simd_ln(), theta) } /// Computes the principal value of the square root of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0)`, continuous from above. /// /// The branch satisfies `-π/2 ≤ arg(sqrt(z)) ≤ π/2`. #[inline] fn simd_sqrt(self) -> Self { // formula: sqrt(r e^(it)) = sqrt(r) e^(it/2) let two = <$WideF32xX>::one() + <$WideF32xX>::one(); let (r, theta) = self.simd_to_polar(); simd_complex_from_polar(r.simd_sqrt(), theta / two) } #[inline] fn simd_hypot(self, b: Self) -> Self::SimdRealField { (self.simd_modulus_squared() + b.simd_modulus_squared()).simd_sqrt() } /// Raises `self` to a floating point power. #[inline] fn simd_powf(self, exp: Self::SimdRealField) -> Self { // formula: x^y = (ρ e^(i θ))^y = ρ^y e^(i θ y) // = from_polar(ρ^y, θ y) let (r, theta) = self.simd_to_polar(); simd_complex_from_polar(r.simd_powf(exp), theta * exp) } /// Returns the logarithm of `self` with respect to an arbitrary base. #[inline] fn simd_log(self, base: $WideF32xX) -> 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.simd_to_polar(); Self::new(r.simd_log(base), theta / base.simd_ln()) } /// Raises `self` to a complex power. #[inline] fn simd_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) // = ρ^c e^(−d θ) e^(i c θ) ρ^(i d) // = p^c e^(−d θ) (cos(c θ) // + i sin(c θ)) (cos(d ln(ρ)) + i sin(d ln(ρ))) // = p^c e^(−d θ) ( // cos(c θ) cos(d ln(ρ)) − sin(c θ) sin(d ln(ρ)) // + i(cos(c θ) sin(d ln(ρ)) + sin(c θ) cos(d ln(ρ)))) // = 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.simd_to_polar(); simd_complex_from_polar( r.simd_powf(exp.re) * (-exp.im * theta).simd_exp(), exp.re * theta + exp.im * r.simd_ln(), ) } /* /// Raises a floating point number to the complex power `self`. #[inline] fn simd_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)) Self::from_polar(&base.powf(self.re), &(self.im * base.ln())) } */ /// Computes the sine of `self`. #[inline] fn simd_sin(self) -> Self { // formula: sin(a + bi) = sin(a)cosh(b) + i*cos(a)sinh(b) Self::new( self.re.simd_sin() * self.im.simd_cosh(), self.re.simd_cos() * self.im.simd_sinh(), ) } /// Computes the cosine of `self`. #[inline] fn simd_cos(self) -> Self { // formula: cos(a + bi) = cos(a)cosh(b) - i*sin(a)sinh(b) Self::new( self.re.simd_cos() * self.im.simd_cosh(), -self.re.simd_sin() * self.im.simd_sinh(), ) } #[inline] fn simd_sin_cos(self) -> (Self, Self) { let (rsin, rcos) = self.re.simd_sin_cos(); let (isinh, icosh) = self.im.simd_sinh_cosh(); let sin = Self::new(rsin * icosh, rcos * isinh); let cos = Self::new(rcos * icosh, -rsin * isinh); (sin, cos) } /// Computes the tangent of `self`. #[inline] fn simd_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); Self::new(two_re.simd_sin(), two_im.simd_sinh()) .unscale(two_re.simd_cos() + two_im.simd_cosh()) } /// Computes the principal value of the inverse sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Re(asin(z)) ≤ π/2`. #[inline] fn simd_asin(self) -> Self { // formula: arcsin(z) = -i ln(sqrt(1-z^2) + iz) let i = Self::i(); -i * ((Self::one() - self * self).simd_sqrt() + i * self).simd_ln() } /// Computes the principal value of the inverse cosine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `0 ≤ Re(acos(z)) ≤ π`. #[inline] fn simd_acos(self) -> Self { // formula: arccos(z) = -i ln(i sqrt(1-z^2) + z) let i = Self::i(); -i * (i * (Self::one() - self * self).simd_sqrt() + self).simd_ln() } /// Computes the principal value of the inverse tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i]`, continuous from the left. /// * `[i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Re(atan(z)) ≤ π/2`. #[inline] fn simd_atan(self) -> Self { // formula: arctan(z) = (ln(1+iz) - ln(1-iz))/(2i) let i = Self::i(); let one = Self::one(); let two = one + one; if self == i { return Self::new(<$WideF32xX>::zero(), <$WideF32xX>::one() / <$WideF32xX>::zero()); } else if self == -i { return Self::new(<$WideF32xX>::zero(), -<$WideF32xX>::one() / <$WideF32xX>::zero()); } ((one + i * self).simd_ln() - (one - i * self).simd_ln()) / (two * i) } /// Computes the hyperbolic sine of `self`. #[inline] fn simd_sinh(self) -> Self { // formula: sinh(a + bi) = sinh(a)cos(b) + i*cosh(a)sin(b) Self::new( self.re.simd_sinh() * self.im.simd_cos(), self.re.simd_cosh() * self.im.simd_sin(), ) } /// Computes the hyperbolic cosine of `self`. #[inline] fn simd_cosh(self) -> Self { // formula: cosh(a + bi) = cosh(a)cos(b) + i*sinh(a)sin(b) Self::new( self.re.simd_cosh() * self.im.simd_cos(), self.re.simd_sinh() * self.im.simd_sin(), ) } #[inline] fn simd_sinh_cosh(self) -> (Self, Self) { let (rsinh, rcosh) = self.re.simd_sinh_cosh(); let (isin, icos) = self.im.simd_sin_cos(); let sin = Self::new(rsinh * icos, rcosh * isin); let cos = Self::new(rcosh * icos, rsinh * isin); (sin, cos) } /// Computes the hyperbolic tangent of `self`. #[inline] fn simd_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); Self::new(two_re.simd_sinh(), two_im.simd_sin()) .unscale(two_re.simd_cosh() + two_im.simd_cos()) } /// Computes the principal value of inverse hyperbolic sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i)`, continuous from the left. /// * `(i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Im(asinh(z)) ≤ π/2`. #[inline] fn simd_asinh(self) -> Self { // formula: arcsinh(z) = ln(z + sqrt(1+z^2)) let one = Self::one(); (self + (one + self * self).simd_sqrt()).simd_ln() } /// Computes the principal value of inverse hyperbolic cosine of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 1)`, continuous from above. /// /// The branch satisfies `-π ≤ Im(acosh(z)) ≤ π` and `0 ≤ Re(acosh(z)) < ∞`. #[inline] fn simd_acosh(self) -> Self { // formula: arccosh(z) = 2 ln(sqrt((z+1)/2) + sqrt((z-1)/2)) let one = Self::one(); let two = one + one; two * (((self + one) / two).simd_sqrt() + ((self - one) / two).simd_sqrt()).simd_ln() } /// Computes the principal value of inverse hyperbolic tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1]`, continuous from above. /// * `[1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Im(atanh(z)) ≤ π/2`. #[inline] fn simd_atanh(self) -> Self { // formula: arctanh(z) = (ln(1+z) - ln(1-z))/2 let one = Self::one(); let two = one + one; if self == one { return Self::new(<$WideF32xX>::one() / <$WideF32xX>::zero(), <$WideF32xX>::zero()); } else if self == -one { return Self::new(-<$WideF32xX>::one() / <$WideF32xX>::zero(), <$WideF32xX>::zero()); } ((one + self).simd_ln() - (one - self).simd_ln()) / two } } } ); macro_rules! impl_scalar_subset_of_simd( ($WideF32xX: ty, $f32: ty, $lanes: expr; $($t: ty),*) => {$( impl SubsetOf<$WideF32xX> for $t { #[inline(always)] fn to_superset(&self) -> $WideF32xX { <$WideF32xX>::splat(<$f32>::from_subset(self)) } #[inline(always)] fn from_superset_unchecked(element: &$WideF32xX) -> $t { element.extract(0).to_subset_unchecked() } #[inline(always)] fn is_in_subset(c: &$WideF32xX) -> bool { let elt0 = c.extract(0); <$t as SubsetOf<$f32>>::is_in_subset(&elt0) && (1..$lanes).all(|i| c.extract(i) == elt0) } } )*} ); impl_scalar_subset_of_simd!(WideF32x4, f32, 4; u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64); impl_scalar_subset_of_simd!(WideF64x4, f64, 4; u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64); //#[cfg(feature = "decimal")] //impl_scalar_subset_of_simd!(WideF32x4, 4; d128); impl_scalar_subset_of_simd!(WideF32x8, f32, 8; u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64); //#[cfg(feature = "decimal")] //impl_scalar_subset_of_simd!(WideF32x8, 8; d128); // NOTE: don’t include the 0 for the indices because they are taken care // for explicitly in the macro (it’s simpler that way). impl_wide_f32!(f32, f32x4, WideF32x4, WideBoolF32x4, 4; 1, 2, 3); impl_wide_f32!(f64, f64x4, WideF64x4, WideBoolF64x4, 4; 1, 2, 3); impl_wide_f32!(f32, f32x8, WideF32x8, WideBoolF32x8, 8; 1, 2, 3, 4, 5, 6, 7); #[inline] fn simd_complex_from_polar(r: N, theta: N) -> num_complex::Complex { num_complex::Complex::new(r.clone() * theta.clone().simd_cos(), r * theta.simd_sin()) }