use crate::complex::Complex;
use crate::{AbortSignal, Backend, BlasResult, CheckedBlasResult, Problem, Scalar};
use std::sync::atomic::Ordering;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ZeroStatus {
Zero,
NonZero,
Unknown,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ScalarSign {
Negative,
Zero,
Positive,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ScalarMagnitudeBits {
pub msd: i32,
pub exact_msd: bool,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ScalarFacts {
pub sign: Option<ScalarSign>,
pub zero: ZeroStatus,
pub exact_rational: bool,
pub magnitude: Option<ScalarMagnitudeBits>,
}
pub(crate) fn two<B: Backend>() -> Scalar<B> {
2.into()
}
#[inline(always)]
pub(crate) fn with_abort<B: Backend>(mut value: Scalar<B>, signal: &AbortSignal) -> Scalar<B> {
crate::trace_dispatch!("hyperlattice", "abort", "attach-owned-scalar");
value.abort(signal.clone());
value
}
#[inline(always)]
pub(crate) fn clone_with_abort<B: Backend>(value: &Scalar<B>, signal: &AbortSignal) -> Scalar<B> {
crate::trace_dispatch!("hyperlattice", "abort", "clone-and-attach");
with_abort(value.clone(), signal)
}
#[inline(always)]
pub fn zero_status<B: Backend>(value: &Scalar<B>) -> ZeroStatus {
crate::trace_dispatch!("hyperlattice", "zero_status", "scalar-query");
value.zero_status()
}
#[inline(always)]
pub fn zero_status_with_abort<B: Backend>(value: &Scalar<B>, signal: &AbortSignal) -> ZeroStatus {
let status = zero_status(value);
if status != ZeroStatus::Unknown || !signal.load(Ordering::Relaxed) {
crate::trace_dispatch!("hyperlattice", "zero_status_abort", "no-clone-fast-path");
return status;
}
crate::trace_dispatch!(
"hyperlattice",
"zero_status_abort",
"clone-with-active-abort"
);
zero_status(&clone_with_abort(value, signal))
}
#[inline(always)]
pub(crate) fn reject_definite_zero<B: Backend>(value: &Scalar<B>) -> BlasResult<()> {
if value.definitely_zero() {
crate::trace_dispatch!("hyperlattice", "zero_guard", "definite-zero-rejected");
Err(Problem::DivideByZero)
} else {
crate::trace_dispatch!("hyperlattice", "zero_guard", "not-definitely-zero");
Ok(())
}
}
#[inline(always)]
pub(crate) fn require_known_nonzero<B: Backend>(value: &Scalar<B>) -> CheckedBlasResult<()> {
match zero_status(value) {
ZeroStatus::Zero => {
crate::trace_dispatch!("hyperlattice", "zero_guard", "checked-zero-rejected");
Err(Problem::DivideByZero)
}
ZeroStatus::NonZero => {
crate::trace_dispatch!("hyperlattice", "zero_guard", "checked-nonzero");
Ok(())
}
ZeroStatus::Unknown => {
crate::trace_dispatch!("hyperlattice", "zero_guard", "checked-unknown-rejected");
Err(Problem::UnknownZero)
}
}
}
#[inline(always)]
pub(crate) fn require_known_nonzero_with_abort<B: Backend>(
value: &Scalar<B>,
signal: &AbortSignal,
) -> CheckedBlasResult<()> {
match zero_status_with_abort(value, signal) {
ZeroStatus::Zero => Err(Problem::DivideByZero),
ZeroStatus::NonZero => Ok(()),
ZeroStatus::Unknown => Err(Problem::UnknownZero),
}
}
pub fn zero() -> Scalar {
crate::trace_dispatch!("hyperlattice", "free_function", "zero");
Scalar::zero()
}
pub fn one() -> Scalar {
crate::trace_dispatch!("hyperlattice", "free_function", "one");
Scalar::one()
}
pub fn e() -> Scalar {
crate::trace_dispatch!("hyperlattice", "free_function", "e");
Scalar::e()
}
pub fn pi() -> Scalar {
crate::trace_dispatch!("hyperlattice", "free_function", "pi");
Scalar::pi()
}
pub fn tau() -> Scalar {
crate::trace_dispatch!("hyperlattice", "free_function", "tau");
Scalar::tau()
}
pub fn i() -> Complex {
Complex::i()
}
pub fn reciprocal<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "reciprocal-owned");
value.inverse()
}
pub fn reciprocal_ref<B: Backend>(value: &Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "reciprocal-ref");
value.inverse_ref()
}
pub fn reciprocal_checked<B: Backend>(value: Scalar<B>) -> CheckedBlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "reciprocal-checked-owned");
require_known_nonzero(&value)?;
value.inverse()
}
pub fn reciprocal_ref_checked<B: Backend>(value: &Scalar<B>) -> CheckedBlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "reciprocal-checked-ref");
require_known_nonzero(value)?;
value.inverse_ref()
}
pub fn reciprocal_checked_with_abort<B: Backend>(
value: Scalar<B>,
signal: &AbortSignal,
) -> CheckedBlasResult<Scalar<B>> {
crate::trace_dispatch!(
"hyperlattice",
"free_function",
"reciprocal-checked-with-abort"
);
let value = with_abort(value, signal);
require_known_nonzero_with_abort(&value, signal)?;
value.inverse()
}
pub fn pow<B: Backend>(base: Scalar<B>, exponent: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "pow");
base.pow(exponent)
}
pub fn powi<B: Backend>(base: Scalar<B>, exponent: i64) -> BlasResult<Scalar<B>> {
if exponent == 0 {
if base.definitely_zero() {
crate::trace_dispatch!("hyperlattice", "powi", "zero-to-zero-domain-error");
return Err(Problem::NotANumber);
}
crate::trace_dispatch!("hyperlattice", "powi", "exponent-zero-one");
return Ok(Scalar::<B>::one());
}
let exp = exponent.unsigned_abs();
let positive = match (B::SPECIALIZE_SCALAR_POWI, exp) {
(_, 1) => {
crate::trace_dispatch!("hyperlattice", "powi", "exponent-one");
base
}
(true, 2) => {
crate::trace_dispatch!("hyperlattice", "powi", "specialized-square");
base.clone() * base
}
(true, 3) => {
crate::trace_dispatch!("hyperlattice", "powi", "specialized-cube");
let square = base.clone() * base.clone();
square * base
}
(true, 4) => {
crate::trace_dispatch!("hyperlattice", "powi", "specialized-fourth");
let square = base.clone() * base;
square.clone() * square
}
(true, 5) => {
crate::trace_dispatch!("hyperlattice", "powi", "specialized-fifth");
let square = base.clone() * base.clone();
let fourth = square.clone() * square;
fourth * base
}
_ => {
crate::trace_dispatch!("hyperlattice", "powi", "generic-squaring");
powi_by_squaring(base, exp)
}
};
if exponent < 0 {
crate::trace_dispatch!("hyperlattice", "powi", "negative-inverse");
positive.inverse()
} else {
Ok(positive)
}
}
fn powi_by_squaring<B: Backend>(base: Scalar<B>, exponent: u64) -> Scalar<B> {
let mut exp = exponent;
let mut result = None;
let mut factor = base;
while exp > 0 {
if exp & 1 == 1 {
result = Some(match result {
Some(result) => result * factor.clone(),
None => factor.clone(),
});
}
exp >>= 1;
if exp > 0 {
factor = factor.clone() * factor;
}
}
result.expect("non-zero exponent sets at least one result bit")
}
pub fn exp<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "exp");
value.exp()
}
pub fn ln<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "ln");
value.ln()
}
pub fn log10<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "log10");
value.log10()
}
pub fn log10_with_abort<B: Backend>(
value: Scalar<B>,
signal: &AbortSignal,
) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "log10-with-abort");
with_abort(value, signal).log10()
}
pub fn sqrt<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "sqrt");
value.sqrt()
}
pub fn sin<B: Backend>(value: Scalar<B>) -> Scalar<B> {
crate::trace_dispatch!("hyperlattice", "free_function", "sin");
value.sin()
}
pub fn cos<B: Backend>(value: Scalar<B>) -> Scalar<B> {
crate::trace_dispatch!("hyperlattice", "free_function", "cos");
value.cos()
}
pub fn tan<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "tan");
value.tan()
}
pub fn sinh<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "sinh-exp-formula");
let positive = value.clone().exp()?;
let negative = (-value).exp()?;
(positive - negative) / two()
}
pub fn cosh<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "cosh-exp-formula");
let positive = value.clone().exp()?;
let negative = (-value).exp()?;
(positive + negative) / two()
}
pub fn tanh<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "tanh-exp-formula");
let positive = value.clone().exp()?;
let negative = (-value).exp()?;
(positive.clone() - negative.clone()) / (positive + negative)
}
pub fn asin<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "asin");
value.asin()
}
pub fn asin_with_abort<B: Backend>(
value: Scalar<B>,
signal: &AbortSignal,
) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "asin-with-abort");
with_abort(value, signal).asin()
}
pub fn acos<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "acos");
value.acos()
}
pub fn acos_with_abort<B: Backend>(
value: Scalar<B>,
signal: &AbortSignal,
) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "acos-with-abort");
with_abort(value, signal).acos()
}
pub fn atan<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "atan");
value.atan()
}
pub fn atan_with_abort<B: Backend>(
value: Scalar<B>,
signal: &AbortSignal,
) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "atan-with-abort");
with_abort(value, signal).atan()
}
pub fn asinh<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "asinh");
value.asinh()
}
pub fn asinh_with_abort<B: Backend>(
value: Scalar<B>,
signal: &AbortSignal,
) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "asinh-with-abort");
with_abort(value, signal).asinh()
}
pub fn acosh<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "acosh");
value.acosh()
}
pub fn acosh_with_abort<B: Backend>(
value: Scalar<B>,
signal: &AbortSignal,
) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "acosh-with-abort");
with_abort(value, signal).acosh()
}
pub fn atanh<B: Backend>(value: Scalar<B>) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "atanh");
value.atanh()
}
pub fn atanh_with_abort<B: Backend>(
value: Scalar<B>,
signal: &AbortSignal,
) -> BlasResult<Scalar<B>> {
crate::trace_dispatch!("hyperlattice", "free_function", "atanh-with-abort");
with_abort(value, signal).atanh()
}