pub trait Policy: core::fmt::Debug + Clone + Copy + PartialEq + Eq + PartialOrd + Ord + core::hash::Hash {
const POLICY: PolicyParameters;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u32)]
pub enum PrecisionPolicy {
Worst = 0,
Medium = 1,
Average = 2,
Best = 3,
Reference = 9,
}
impl PrecisionPolicy {
pub const fn eq(self, other: PrecisionPolicy) -> bool {
(self as u32) == (other as u32)
}
pub const fn gt(self, other: PrecisionPolicy) -> bool {
(self as u32) > (other as u32)
}
pub const fn ge(self, other: PrecisionPolicy) -> bool {
(self as u32) >= (other as u32)
}
pub const fn lt(self, other: PrecisionPolicy) -> bool {
(self as u32) < (other as u32)
}
pub const fn le(self, other: PrecisionPolicy) -> bool {
(self as u32) <= (other as u32)
}
#[inline(always)]
pub const fn tolerance(self) -> crate::LargeInt {
match self {
PrecisionPolicy::Worst => 100_000,
PrecisionPolicy::Medium => 10_000,
PrecisionPolicy::Average => 100,
PrecisionPolicy::Best => 20,
PrecisionPolicy::Reference => 8,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DenormalBehavior {
Ignore,
FlushToZero,
Crush,
Preserve,
}
impl DenormalBehavior {
const fn preserve_any(a: Self, b: Self) -> Self {
match (a, b) {
(DenormalBehavior::Preserve, _) | (_, DenormalBehavior::Preserve) => DenormalBehavior::Preserve,
_ => a,
}
}
const fn select_default(crush: bool) -> Self {
#[cfg(feature = "preserve_denormals")]
return DenormalBehavior::Preserve;
#[cfg(feature = "ignore_denormals")]
return DenormalBehavior::Ignore;
if crush {
DenormalBehavior::Crush
} else {
DenormalBehavior::FlushToZero
}
}
}
pub struct PolicyParameters {
pub check_overflow: bool,
pub unroll_loops: bool,
pub precision: PrecisionPolicy,
pub avoid_branching: bool,
pub max_iterations: usize,
pub use_compensation: bool,
pub denormal_behavior: DenormalBehavior,
}
impl PolicyParameters {
#[inline(always)]
pub const fn avoid_precision_branches(self) -> bool {
self.avoid_branching && self.precision.le(PrecisionPolicy::Worst)
}
}
pub mod policies {
use core::marker::PhantomData;
use super::{DenormalBehavior, Policy, PolicyParameters, PrecisionPolicy};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ExtraPrecision<P: Policy>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LessPrecision<P: Policy>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UseCompensation<P: Policy, const USE_COMPENSATION: bool>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CheckOverflow<P: Policy, const CHECK_OVERFLOW: bool>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnrollLoops<P: Policy, const UNROLL_LOOPS: bool>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AvoidBranching<P: Policy, const AVOID_BRANCHING: bool>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PreserveDenormals<P: Policy>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MaxIterations<P: Policy, const MAX_ITERATIONS: usize>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WorstPrecision<P: Policy>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MediumPrecision<P: Policy>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AveragePrecision<P: Policy>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BestPrecision<P: Policy>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ReferencePrecision<P: Policy>(PhantomData<P>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CmpLessPrecision<A: Policy, B: Policy>(PhantomData<(A, B)>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UltraPerformance;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HighPerformance;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Performance;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Precision;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Size;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Reference;
const fn extra_precision(p: PrecisionPolicy) -> PrecisionPolicy {
match p {
PrecisionPolicy::Worst => PrecisionPolicy::Medium,
PrecisionPolicy::Medium => PrecisionPolicy::Average,
PrecisionPolicy::Average => PrecisionPolicy::Best,
PrecisionPolicy::Best => PrecisionPolicy::Reference,
PrecisionPolicy::Reference => PrecisionPolicy::Reference, }
}
const fn less_precision(p: PrecisionPolicy) -> PrecisionPolicy {
match p {
PrecisionPolicy::Reference => PrecisionPolicy::Best,
PrecisionPolicy::Best => PrecisionPolicy::Average,
PrecisionPolicy::Average => PrecisionPolicy::Medium,
PrecisionPolicy::Medium => PrecisionPolicy::Worst,
PrecisionPolicy::Worst => PrecisionPolicy::Worst, }
}
impl<P: Policy> Policy for ExtraPrecision<P> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: extra_precision(P::POLICY.precision),
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: P::POLICY.precision.ge(PrecisionPolicy::Average),
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy> Policy for LessPrecision<P> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: less_precision(P::POLICY.precision),
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: P::POLICY.precision.gt(PrecisionPolicy::Average),
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy, const USE_COMPENSATION: bool> Policy for UseCompensation<P, USE_COMPENSATION> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: P::POLICY.precision,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: USE_COMPENSATION,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy, const CHECK_OVERFLOW: bool> Policy for CheckOverflow<P, CHECK_OVERFLOW> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: CHECK_OVERFLOW,
unroll_loops: P::POLICY.unroll_loops,
precision: P::POLICY.precision,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: P::POLICY.use_compensation,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy, const UNROLL_LOOPS: bool> Policy for UnrollLoops<P, UNROLL_LOOPS> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: UNROLL_LOOPS,
precision: P::POLICY.precision,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: P::POLICY.use_compensation,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy, const AVOID_BRANCHING: bool> Policy for AvoidBranching<P, AVOID_BRANCHING> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: P::POLICY.precision,
avoid_branching: AVOID_BRANCHING,
max_iterations: P::POLICY.max_iterations,
use_compensation: P::POLICY.use_compensation,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy, const MAX_ITERATIONS: usize> Policy for MaxIterations<P, MAX_ITERATIONS> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: P::POLICY.precision,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: MAX_ITERATIONS,
use_compensation: P::POLICY.use_compensation,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy> Policy for PreserveDenormals<P> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: P::POLICY.precision,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: P::POLICY.use_compensation,
denormal_behavior: DenormalBehavior::Preserve,
};
}
impl<P: Policy> Policy for WorstPrecision<P> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: PrecisionPolicy::Worst,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: false,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy> Policy for MediumPrecision<P> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: PrecisionPolicy::Medium,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: false,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy> Policy for AveragePrecision<P> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: PrecisionPolicy::Average,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: false,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy> Policy for BestPrecision<P> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: PrecisionPolicy::Best,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: true,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<P: Policy> Policy for ReferencePrecision<P> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: P::POLICY.check_overflow,
unroll_loops: P::POLICY.unroll_loops,
precision: PrecisionPolicy::Reference,
avoid_branching: P::POLICY.avoid_branching,
max_iterations: P::POLICY.max_iterations,
use_compensation: true,
denormal_behavior: P::POLICY.denormal_behavior,
};
}
impl<A: Policy, B: Policy> Policy for CmpLessPrecision<A, B> {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: A::POLICY.check_overflow,
unroll_loops: A::POLICY.unroll_loops,
precision: if B::POLICY.precision.lt(A::POLICY.precision) {
B::POLICY.precision
} else {
A::POLICY.precision
},
avoid_branching: A::POLICY.avoid_branching,
max_iterations: A::POLICY.max_iterations,
use_compensation: A::POLICY.use_compensation && B::POLICY.use_compensation,
denormal_behavior: DenormalBehavior::preserve_any(A::POLICY.denormal_behavior, B::POLICY.denormal_behavior),
};
}
impl Policy for UltraPerformance {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: false,
unroll_loops: true,
precision: PrecisionPolicy::Worst,
avoid_branching: true,
max_iterations: 1000,
use_compensation: false,
denormal_behavior: DenormalBehavior::select_default(true),
};
}
impl Policy for HighPerformance {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: false,
unroll_loops: true,
precision: PrecisionPolicy::Medium,
avoid_branching: false,
max_iterations: 10000,
use_compensation: false,
denormal_behavior: DenormalBehavior::select_default(true),
};
}
impl Policy for Performance {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: true,
unroll_loops: true,
precision: PrecisionPolicy::Average,
avoid_branching: false,
max_iterations: 10000,
use_compensation: false,
denormal_behavior: DenormalBehavior::select_default(false),
};
}
impl Policy for Precision {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: true,
unroll_loops: true,
precision: PrecisionPolicy::Best,
avoid_branching: false,
max_iterations: 50000,
use_compensation: true,
denormal_behavior: DenormalBehavior::select_default(false),
};
}
impl Policy for Size {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: true,
unroll_loops: false,
precision: PrecisionPolicy::Average,
avoid_branching: false,
max_iterations: 10000,
use_compensation: false,
denormal_behavior: DenormalBehavior::select_default(true),
};
}
impl Policy for Reference {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: true,
unroll_loops: true,
precision: PrecisionPolicy::Reference,
avoid_branching: false,
max_iterations: 100000,
use_compensation: true,
denormal_behavior: DenormalBehavior::select_default(false),
};
}
}
use policies::*;
#[cfg(all(feature = "spirv", target_arch = "spirv"))]
pub struct GpuDefault;
#[cfg(all(feature = "spirv", target_arch = "spirv"))]
impl Policy for GpuDefault {
const POLICY: PolicyParameters = PolicyParameters {
check_overflow: true,
unroll_loops: true,
precision: PrecisionPolicy::Average,
avoid_branching: true,
max_iterations: 10000,
use_compensation: false,
denormal_behavior: DenormalBehavior::select_default(true),
};
}
pub type DefaultPolicy = cfg_select! {
feature = "strict_ieee754" => Precision,
all(feature = "wasm", any(target_arch = "wasm32", target_arch = "wasm64")) => Size,
all(feature = "spirv", target_arch = "spirv") => GpuDefault,
_ => Performance,
};