1use crate::buffer::{DType, FloatWidth, Source};
3use crate::domain::{Range, Variant};
4use crate::float::sealed::Core;
5use crate::pow::parity;
6use crate::recipe::{NativeOps, Ops};
7use crate::reduce::{Maximum, Minimum, Product, Reduce, Reduction};
8use crate::tolerant::{Residue, Tolerance, Tolerant, TolerantComplex, lcm_from_gcd};
9use crate::{Arg, Betainc, BetaincAt, Binary, FUSED, Flipped, Float, Native, Unary};
10use num_complex::Complex;
11use num_traits::{One, Signed, Zero};
12use std::f64::consts::PI;
13
14const UNIT: (f64, f64) = (-1.0, 1.0);
16
17fn exact(lo: f64, hi: f64) -> Range { Range { lo: if lo.is_nan() { f64::NEG_INFINITY } else { lo }, hi: if hi.is_nan() { f64::INFINITY } else { hi } } }
19fn increasing(x: Range, f: impl Fn(f64) -> f64) -> Range { exact(f(x.lo), f(x.hi)).loose() }
21fn decreasing(x: Range, f: impl Fn(f64) -> f64) -> Range { exact(f(x.hi), f(x.lo)).loose() }
23fn even(x: Range, f: impl Fn(f64) -> f64) -> Range { increasing(x.magnitude(), f) }
25fn corners(x: Range, y: Range, op: impl Fn(f64, f64) -> f64) -> Range {
27 let side = |v: f64, inward: f64| if v == 0.0 { inward } else { v.signum() };
28 let mut r = Range { lo: f64::INFINITY, hi: f64::NEG_INFINITY };
29 for (a, da) in [(x.lo, 1.0), (x.hi, -1.0)] {
30 for (b, db) in [(y.lo, 1.0), (y.hi, -1.0)] {
31 let v = op(a, b);
32 for v in if v.is_nan() { [0.0, f64::INFINITY * side(a, da) * side(b, db)] } else { [v, v] } { r = r.join(Range { lo: v, hi: v }); }
33 }
34 }
35 r
36}
37fn quotient(x: Range, y: Range) -> Range { if y.excludes(0.0) { corners(x, y, |a, b| a / b) } else { Range::ANY } }
39fn pi_times(x: Range) -> Range { corners(x, Range::of(Some(PI)), |a, b| a * b).loose() }
41fn signs<const N: usize>(args: [Range; N]) -> Range { if args.iter().all(|a| a.nonnegative()) { Range::NONNEGATIVE } else { Range::ANY } }
43fn inside<const N: usize>(args: [Range; N], domain: [Range; N]) -> Option<[Range; N]> {
45 args.iter().zip(&domain).all(|(a, d)| a.within(d.lo, d.hi)).then_some(domain)
46}
47fn root_domain(n: Range, y: Range) -> Option<[Range; 2]> {
49 let tiny = f64::from_bits(1);
50 match n.constant() {
51 Some(c) if y.lo < 0.0 && parity(c).1 => inside([n, y], [n, Range::ANY]),
52 _ => inside([n, y], [if n.lo > 0.0 { exact(tiny, f64::MAX) } else { exact(f64::MIN, -tiny) }, Range::NONNEGATIVE]),
53 }
54}
55
56pub trait Formula<const N: usize>: Clone + Send + Sync + 'static {
58 const BOOLEAN: bool = false;
59 const FAILS_NAN: bool = false;
61 const HAS_EXACT: bool = false;
63 const INTEGER_FORMULA: bool = false;
65 const MAP_ONLY: bool = false;
67 fn result_dtype(&self, input: DType) -> DType { if Self::BOOLEAN { DType::Boolean } else { input } }
68 fn computing_dtype(&self, input: DType) -> Option<DType> { input.computes_in(crate::buffer::Width::I64, input.numpy_float(), |d| self.supports(d)) }
69 fn character_rule(&self) -> Option<crate::characters::Rule> { None }
70 fn ordered_kinds(&self, [x, y]: [i32; 2]) -> Option<i32> {
71 let ops = crate::recipe::IntegerOps::<i32>::new(Variant::checked(DType::Integer(crate::buffer::Width::I32)));
72 self.integer(&ops, std::array::from_fn(|i| if i == 0 { x } else { y }))
73 }
74 fn character_policy(&self, types: [DType; 2]) -> Result<Option<crate::characters::Policy>, crate::error::Error> {
75 crate::characters::policy(self.character_rule(), types, self.result_dtype(DType::Integer(crate::buffer::Width::I32)), |x| self.ordered_kinds(x))
76 }
77 fn exact(&self, _args: [&num_rational::BigRational; N]) -> Result<Option<num_rational::BigRational>, crate::error::Error> { Ok(None) }
78 fn range(&self, _args: [Range; N]) -> Range { Range::ANY }
79 fn domain(&self, _dtype: DType, _args: [Range; N]) -> Option<[Range; N]> { None }
81 fn identity(&self) -> [Option<f64>; N] { [None; N] }
82 fn fold(&self) -> Option<Reduce> { None }
83 fn inverse_fold(&self) -> Option<Reduce> { None }
84 fn truth_table(&self) -> Option<u8> { None }
86 fn truth(&self) -> Option<Reduce> {
87 match self.truth_table()? { 8 => Some(Reduce::Min), 14 => Some(Reduce::Max), 6 => Some(Reduce::Xor), 9 => Some(Reduce::Xnor), _ => None }
88 }
89 fn same(&self, _arg: Range) -> Option<Range> { None }
91 fn reads(&self) -> Reads { Reads::Items }
92 fn supports(&self, _dtype: DType) -> bool { false }
93 fn emit<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, _o: &O, _x: [O::V; N]) -> Option<O::V> { None }
94 fn emit_test<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, _o: &O, _x: [O::V; N]) -> Option<O::B> { None }
95 fn emit_against<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, _o: &O, _c: F, _v: O::V, _left: bool) -> Option<O::B> { None }
97 fn bands(&self) -> Option<(crate::tolerant::Tolerance, crate::tolerant::Band, crate::tolerant::Band)> { None }
99 fn emit_repair<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, _o: &O, _x: [O::V; N]) -> Option<O::B> { None }
100 fn repair_routine(&self) -> Option<Repair> { None }
101 fn emit_valid<F: crate::Float, O: Ops<F> + ?Sized>(&self, _variant: Variant, o: &O, x: [O::V; N], result: O::V) -> Option<(crate::error::Kind, O::B)> {
102 Self::FAILS_NAN.then(|| (crate::error::Kind::Domain, nan_valid(o, x, [result])))
103 }
104 #[inline]
105 fn real<F: crate::float::Native>(&self, ops: &crate::recipe::NativeOps<{ crate::FUSED }>, x: [F; N]) -> Option<F> {
106 if const { Self::BOOLEAN } { self.emit_test(ops, x).map(|x| if x { F::one() } else { F::zero() }) } else { self.emit(ops, x) }
107 }
108 fn integer<I: crate::recipe::Overflowing>(&self, _ops: &crate::recipe::IntegerOps<I>, _x: [I; N]) -> Option<I> { None }
109 fn complex<F: crate::float::Native>(
110 &self, _ops: &crate::recipe::NativeOps<{ crate::FUSED }>, _x: [num_complex::Complex<F>; N]) -> Option<num_complex::Complex<F>> { None }
111 const REAL_COMPLEX: bool = false;
113 fn real_complex<F: crate::float::Native>(&self, _ops: &crate::recipe::NativeOps<{ crate::FUSED }>, _x: [F; N]) -> Option<num_complex::Complex<F>> { None }
115 fn native_slices(&self) -> Option<NativeSlices<Self, N>> { None }
116 fn emit_partials<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, _o: &O, _x: [O::V; N], _result: O::V, _needed: [bool; N]) -> [Option<O::V>; N] {
117 [None; N]
118 }
119}
120
121pub trait Visit: Sized {
123 type Out;
124 fn visit<K: Formula<N>, const N: usize>(self, object: K) -> Self::Out;
125 fn unary<K: Formula<1>>(self, object: K) -> Self::Out { self.visit(object) }
126 fn binary<K: Formula<2>>(self, object: K) -> Self::Out { self.visit(object) }
127}
128
129macro_rules! ops {
131 (unary [$($u:ident)*] binary [$($b:ident)*] reduce [$($r:ident)*] ternary [$($t:ident)*] held [$($h:ident($ty:ty) => $how:ident $make:expr),* $(,)?]) => {
132 #[derive(Clone, Copy, Debug)]
134 pub enum Op { $($u,)* $($b,)* $($r,)* $($t,)* $($h($ty),)* PiTimes, Bitwise(u8), BetaincAt(f64, f64), FlippedBinom }
135 impl Op {
136 pub fn visit<V: Visit>(self, arity: usize, v: V) -> V::Out {
138 match self {
139 $(Op::$u => v.unary(crate::$u),)* $(Op::$b => v.binary(crate::$b),)* $(Op::$r => v.binary(crate::reduce::$r),)* $(Op::$t => v.visit(crate::$t),)*
140 $(Op::$h(x) => v.$how($make(x)),)*
141 Op::PiTimes if arity == 1 => v.unary(crate::PiTimes), Op::PiTimes => v.binary(crate::PiTimes),
142 Op::Bitwise(table) if arity == 1 => v.unary(crate::Bitwise(table)), Op::Bitwise(table) => v.binary(crate::Bitwise(table)),
143 Op::BetaincAt(a, b) => v.unary(BetaincAt(a, b)), Op::FlippedBinom => v.binary(Flipped(crate::Binom)),
144 }
145 }
146 }
147 };
148}
149ops! {
150 unary [Conj Negate Sign Reciprocal Abs Exp Ln Log2 Log10 Log1p Expm1 Cis Sqrt Cbrt RealSqrt RealLn Factorial Gamma Lgamma Digamma LambertW Erf Erfc Erfinv Erfcinv
151 Not Isnan Isfinite Isinf Real Imag Angle Trunc Rint Sin Cos Tan Asin Acos Atan Sinh Cosh Tanh Asinh Acosh Atanh Sqrt1mx2 Sqrt1px2 Sqrtx2m1]
152 binary [Add Subtract Multiply Divide MathishDivide Pow Root RealPow Logb MathishLogb Nand Nor Hypot Atan2 Binom BinomDn BinomDk Polygamma Beta Gammainc Gammaincc
153 Gammaincinv LeftShift RightShift]
154 reduce [Minimum Maximum Xor Xnor]
155 ternary [Clip Betainc Betaincinv]
156 held [
157 Floor(Tolerance) => unary crate::tolerant::Floor, Ceil(Tolerance) => unary crate::tolerant::Ceil, Residue(Tolerance) => binary crate::tolerant::Residue,
158 FlippedResidue(Tolerance) => binary |t| Flipped(crate::tolerant::Residue(t)), FloorDivide(Tolerance) => binary crate::tolerant::FloorDivide,
159 MathishFloorDivide(Tolerance) => binary crate::tolerant::MathishFloorDivide, Gcd(Tolerance) => binary crate::tolerant::Gcd, Lcm(Tolerance) => binary crate::tolerant::Lcm,
160 Equal(Tolerance) => binary crate::tolerant::Equal, NotEqual(Tolerance) => binary crate::tolerant::NotEqual, Less(Tolerance) => binary crate::tolerant::Less,
161 LessEqual(Tolerance) => binary crate::tolerant::LessEqual, Greater(Tolerance) => binary crate::tolerant::Greater,
162 GreaterEqual(Tolerance) => binary crate::tolerant::GreaterEqual, Scale(f64) => unary crate::Scale, Round(crate::Round) => unary std::convert::identity,
163 Pdf(crate::distribution::Distribution) => unary crate::distribution::Pdf, Cdf(crate::distribution::Distribution) => unary crate::distribution::Cdf,
164 Quantile(crate::distribution::Distribution) => unary crate::distribution::Quantile,
165 ]
166}
167impl Op {
168 pub fn computing_dtype(self, arity: usize, input: DType) -> Option<DType> {
170 struct Of(DType);
171 impl Visit for Of {
172 type Out = Option<DType>;
173 fn visit<K: Formula<N>, const N: usize>(self, object: K) -> Option<DType> { object.computing_dtype(self.0) }
174 }
175 self.visit(arity, Of(input))
176 }
177 pub fn supports(self, arity: usize, dtype: DType) -> bool {
179 struct Of(DType);
180 impl Visit for Of {
181 type Out = bool;
182 fn visit<K: Formula<N>, const N: usize>(self, object: K) -> bool { object.supports(self.0) }
183 }
184 self.visit(arity, Of(dtype))
185 }
186}
187pub struct NativeSlices<K, const N: usize> {
189 pub(crate) single: Option<Slice<K, f32, N>>, pub(crate) double: Option<Slice<K, f64, N>>, pub(crate) byte: Option<Slice<K, u8, N>>,
190 pub(crate) short: Option<Slice<K, i16, N>>, pub(crate) int: Option<Slice<K, i32, N>>, pub(crate) long: Option<Slice<K, i64, N>>,
191 pub(crate) complex_single: Option<Slice<K, num_complex::Complex32, N, ()>>, pub(crate) complex_double: Option<Slice<K, num_complex::Complex64, N, ()>>,
192 pub(crate) real_complex_single: Option<Slice<K, f32, N, (), num_complex::Complex32>>, pub(crate) real_complex_double: Option<Slice<K, f64, N, (), num_complex::Complex64>>
193}
194pub type Slice<K, T, const N: usize, R = bool, U = T> = fn(&K, &NativeOps<{ FUSED }>, [crate::Arg<'_, T>; N], usize, &mut [std::mem::MaybeUninit<U>]) -> R;
196impl<K, const N: usize> NativeSlices<K, N> {
197 pub fn single(&self) -> Option<Slice<K, f32, N>> { self.single }
198 pub fn double(&self) -> Option<Slice<K, f64, N>> { self.double }
199 pub fn byte(&self) -> Option<Slice<K, u8, N>> { self.byte }
200 pub fn short(&self) -> Option<Slice<K, i16, N>> { self.short }
201 pub fn int(&self) -> Option<Slice<K, i32, N>> { self.int }
202 pub fn long(&self) -> Option<Slice<K, i64, N>> { self.long }
203 pub fn complex_single(&self) -> Option<Slice<K, num_complex::Complex32, N, ()>> { self.complex_single }
204 pub fn complex_double(&self) -> Option<Slice<K, num_complex::Complex64, N, ()>> { self.complex_double }
205 pub fn real_complex_single(&self) -> Option<Slice<K, f32, N, (), num_complex::Complex32>> { self.real_complex_single }
206 pub fn real_complex_double(&self) -> Option<Slice<K, f64, N, (), num_complex::Complex64>> { self.real_complex_double }
207
208 const NONE: Self = Self { single: None, double: None, byte: None, short: None, int: None, long: None, complex_single: None, complex_double: None,
209 real_complex_single: None, real_complex_double: None };
210 fn with_complex(self, single: Slice<K, num_complex::Complex32, N, ()>, double: Slice<K, num_complex::Complex64, N, ()>) -> Self {
211 Self { complex_single: Some(single), complex_double: Some(double), ..self }
212 }
213 fn with_real_complex(self, single: Slice<K, f32, N, (), num_complex::Complex32>, double: Slice<K, f64, N, (), num_complex::Complex64>) -> Self {
214 Self { real_complex_single: Some(single), real_complex_double: Some(double), ..self }
215 }
216}
217impl<K> NativeSlices<K, 1> where K: Unary<f32, Out = f32> + Unary<f64, Out = f64> {
218 pub(crate) fn unary() -> Self {
219 Self { single: Some(|k, _, x, n, out| Unary::<f32>::slice(k, x, n, out)), double: Some(|k, _, x, n, out| Unary::<f64>::slice(k, x, n, out)),
220 ..Self::NONE }
221 }
222}
223impl NativeSlices<crate::Pow, 2> {
224 fn power() -> Self {
225 Self { single: Some(|k, _, x, n, out| crate::pow::power_slice::<f32>(k, x, n, out)),
226 double: Some(|k, _, x, n, out| crate::pow::power_slice::<f64>(k, x, n, out)), ..Self::NONE }
227 }
228}
229impl NativeSlices<crate::Root, 2> {
230 fn root() -> Self {
231 Self { single: Some(|k, _, x, n, out| crate::pow::root_slice::<f32>(k, x, n, out)),
232 double: Some(|k, _, x, n, out| crate::pow::root_slice::<f64>(k, x, n, out)), ..Self::NONE }
233 }
234}
235impl NativeSlices<Residue, 2> {
236 fn residue() -> Self {
237 Self { single: Some(|k, o, x, n, out| Binary::<f32>::slice(&Residue(native_tolerance(o.tolerance, k.0)), x, n, out)),
238 double: Some(|k, o, x, n, out| Binary::<f64>::slice(&Residue(native_tolerance(o.tolerance, k.0)), x, n, out)),
239 byte: Some(|k, _, x, n, out| Binary::<u8>::slice(k, x, n, out)), short: Some(|k, _, x, n, out| Binary::<i16>::slice(k, x, n, out)),
240 int: Some(|k, _, x, n, out| Binary::<i32>::slice(k, x, n, out)), long: Some(|k, _, x, n, out| Binary::<i64>::slice(k, x, n, out)), ..Self::NONE }
241 }
242}
243
244impl NativeSlices<crate::Logb, 2> {
245 fn logarithm() -> Self {
246 Self { single: Some(|_, o, x, n, out| logarithm_slice::<f32, false>(o, x, n, out)),
247 double: Some(|_, o, x, n, out| logarithm_slice::<f64, false>(o, x, n, out)), ..Self::NONE }
248 }
249}
250impl NativeSlices<crate::MathishLogb, 2> {
251 fn mathish_logarithm() -> Self {
252 Self { single: Some(|_, o, x, n, out| logarithm_slice::<f32, true>(o, x, n, out)),
253 double: Some(|_, o, x, n, out| logarithm_slice::<f64, true>(o, x, n, out)), ..Self::NONE }
254 }
255}
256
257#[derive(Clone, Copy, Debug)]
258pub struct Repair { pub single: extern "C" fn(f32) -> f32, pub double: extern "C" fn(f64) -> f64 }
259
260#[inline(always)]
261pub fn nan_valid<F: crate::Float, O: Ops<F> + ?Sized>(o: &O, inputs: impl IntoIterator<Item = O::V>, results: impl IntoIterator<Item = O::V>) -> O::B {
262 let mut valid = o.equal(o.number(F::zero()), o.number(F::zero()));
263 for result in results { valid = o.and(valid, o.equal(result, result)); }
264 for input in inputs { valid = o.or(valid, o.not(o.equal(input, input))); }
265 valid
266}
267
268fn logarithm_valid<F: Float, O: Ops<F> + ?Sized>(o: &O, x: [O::V; 2], result: O::V) -> Option<(crate::error::Kind, O::B)> {
269 let zero = o.number(F::zero());
270 let nonnegative = o.and(o.less_equal(zero, x[0]), o.less_equal(zero, x[1]));
271 Some((crate::error::Kind::Domain, o.or(nonnegative, nan_valid(o, x, [result]))))
272}
273
274fn logarithm_slice<F: Native, const MATHISH: bool>(ops: &NativeOps<{ FUSED }>, args: [Arg<'_, F>; 2], n: usize, out: &mut [std::mem::MaybeUninit<F>]) -> bool {
275 crate::ln::logb_many::<F, MATHISH>(args, n, out) || {
276 let results = unsafe { crate::slice::written(&mut out[..n]) };
278 crate::slice::judged(&args, results, |x, r| logarithm_valid(ops, x, r).unwrap().1)
279 }
280}
281
282impl Repair {
283 pub fn apply<F: crate::Float>(self, x: F) -> F { F::narrow(if F::Working::WIDE { (self.double)(x.wide()) } else { (self.single)(x.wide() as f32) as f64 }) }
284}
285
286#[derive(Clone, Copy, Debug, PartialEq, Eq)]
287pub enum Reads { Items, Whole }
288
289#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
290pub struct Slot(pub u32);
291
292#[inline(always)]
294pub fn formula_at<K: Formula<N>, F: crate::Float, const N: usize>(k: &K, x: [F; N]) -> F {
295 let o = crate::recipe::NativeOps::<{ crate::FUSED }>::DEFAULT;
296 let x = x.map(F::working);
297 let result = if k.emit_repair(&o, x) == Some(true) { k.repair_routine().expect("a repair predicate has its routine").apply(x[0]) }
298 else { k.real(&o, x).expect("a real body that gives every result") };
299 F::from_working(result)
300}
301
302pub fn real_slice<K: Formula<N>, F: Native + Source<O>, O: Float, const N: usize>(
304 object: &K, ops: &NativeOps<{ FUSED }>, args: [Arg<'_, F>; N], n: usize, out: &mut [std::mem::MaybeUninit<O>]) -> bool {
305 let body = |x| object.real(ops, x).expect("a resolved object has a real body");
306 if let Some(repair) = object.repair_routine() {
307 let needs = |x| object.emit_repair(ops, x).unwrap_or(false);
308 crate::slice::two_pass(args, n, out, |x| (!needs(x)).then(|| body(x)), |x| if needs(x) { repair.apply(x[0]) } else { body(x) })
309 } else { crate::slice::each(args, n, out, #[inline(always)] |x| Source::<O>::read(body(x))) }
310}
311
312#[inline(always)]
314pub fn truth_bit<const N: usize>(table: u8, x: [bool; N]) -> bool { (table >> x.iter().fold(0, |at, &b| 2 * at + u8::from(b))) & 1 != 0 }
315
316macro_rules! objects {
318 (@partials 1, slope |$o:tt, $x:tt, $r:tt| $slope:expr) => {
319 #[inline(always)]
320 fn emit_partials<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, $o: &O, [$x]: [O::V; 1], $r: O::V, [need]: [bool; 1]) -> [Option<O::V>; 1] {
321 [need.then(|| $slope)]
322 }
323 };
324 (@partials 2, slopes |$o:tt, $x:tt, $y:tt, $r:tt| [$dx:expr, $dy:expr]) => {
325 #[inline(always)]
326 fn emit_partials<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, $o: &O, [$x, $y]: [O::V; 2], $r: O::V, [nx, ny]: [bool; 2]) -> [Option<O::V>; 2] {
327 [nx.then(|| $dx), ny.then(|| $dy)]
328 }
329 };
330 (@partials 2, shared $slopes:ident) => {
331 #[inline(always)]
332 fn emit_partials<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, o: &O, x: [O::V; 2], r: O::V, need: [bool; 2]) -> [Option<O::V>; 2] {
333 crate::derivative::$slopes(o, x, r, need)
334 }
335 };
336 (@partials $n:tt,) => {};
337 (@present $body:expr) => { true };
338 (@test_output $body:expr) => { const BOOLEAN: bool = true; };
339 (@output $dtype:ident, real) => { match $dtype { DType::Complex(w) => DType::Float(w.into()), _ => $dtype } };
340 (@output $dtype:ident, integer) => { match $dtype { DType::Boolean => DType::Integer(crate::buffer::Width::I64), _ => $dtype } };
341 (@output $dtype:ident, complex) => {
342 match $dtype.inexact(crate::buffer::FloatWidth::F64) {
343 Some((DType::Float(w), _)) => DType::Complex(w.into()), Some((d, _)) => d, None => $dtype,
344 }
345 };
346 (@computing real) => {};
347 (@computing integer) => {};
348 (@computing complex) => { fn computing_dtype(&self, input: DType) -> Option<DType> { let dtype = input.complex(); self.supports(dtype).then_some(dtype) } };
349 (@checks nan) => { const FAILS_NAN: bool = true; };
350 (@only bits) => { true };
351 (@boolean bits) => { false };
352 (@boolean arithmetic) => { false };
353 (@boolean $kind:ident) => { true };
354 (@integers $n:tt, bits) => { objects!(@integers $n, formula); };
355 (@only formula) => { false };
356 (@only arithmetic) => { false };
357 (@integers $n:tt, arithmetic) => { objects!(@integers $n, formula); };
358 (@only only) => { true };
359 (@integers $n:tt, only) => { objects!(@integers $n, formula); };
360 (@integers $n:tt, formula) => {
361 const INTEGER_FORMULA: bool = true;
362 #[inline(always)]
363 fn integer<I: crate::recipe::Overflowing>(&self, o: &crate::recipe::IntegerOps<I>, x: [I; $n]) -> Option<I> {
364 if const { Self::BOOLEAN } { self.emit_test(o, x).map(|x| I::low(i64::from(x))) } else { self.emit(o, x) }
365 }
366 };
367 ($n:tt: $($object:ty {
368 $(range $args:pat => $range:expr;)? $(domain $domain_dtype:pat_param, $targs:pat => $domain:expr;)? $(identity $identity:expr;)? $(fold $fold:ident;)? $(inverse_fold $inverse_fold:ident;)? $(truth $truth:ident;)? $(table $table:expr;)?
369 $(output $output:ident;)? $(characters $rule:ident;)? $(same |$sx:pat_param| $same:expr;)? $(formula |$k:pat_param, $o:tt, $fargs:tt| $formula:expr;)? $(test |$tk:pat_param, $to:tt, $targs2:tt| $test:expr;)? $(against |$ak:pat_param| $atol:expr;)?
370 $(slope |$so:tt, $sx1:tt, $sr:tt| $slope:expr;)? $(slopes |$s2o:tt, $s2x:tt, $s2y:tt, $s2r:tt| [$dx:expr, $dy:expr];)? $(shared $shared:ident;)?
371 $(repair $repair:expr;)?
372 $(integers $integer:ident;)?
373 $(native_integer |$nik:pat_param, $nio:tt, $nix:tt| $native_integer:expr;)?
374 $(native_complex |$nfk:pat_param, $nfo:tt, $nfx:tt| $native_complex:expr;)?
375 $(complex_slices $complex_slices:ident;)?
376 $(real_complex_slices $real_complex_slices:ident;)?
377 $(native_real |$nrk:pat_param, $nro:tt, $nrx:tt| $native_real:expr;)?
378 $(real_complex |$rck:pat_param, $rco:tt, $rcx:tt| $real_complex:expr;)?
379 $(slices $slices:ident;)?
380 $(exact |$ek:pat_param, $ex:tt| $exact:expr;)?
381 $(checks $checks:ident;)?
382 $(valid |$vk:pat_param, $vv:tt, $vo:tt, $vx:tt, $vr:tt| $valid:expr;)?
383 })+) => {$(
384 impl Formula<$n> for $object {
385 $(fn exact(&self, $ex: [&num_rational::BigRational; $n]) -> Result<Option<num_rational::BigRational>, crate::error::Error> { let $ek = self; $exact } const HAS_EXACT: bool = true;)?
386 $(fn character_rule(&self) -> Option<crate::characters::Rule> { Some(crate::characters::Rule::$rule) })?
387 $(fn result_dtype(&self, dtype: DType) -> DType { objects!(@output dtype, $output) } objects!(@computing $output);)?
388 $(objects!(@test_output $test);)?
389 $(fn range(&self, $args: [Range; $n]) -> Range { $range })?
390 $(fn domain(&self, $domain_dtype: DType, $targs: [Range; $n]) -> Option<[Range; $n]> { $domain })?
391 $(fn identity(&self) -> [Option<f64>; $n] { $identity })?
392 $(fn fold(&self) -> Option<crate::reduce::Reduce> { Some(crate::reduce::Reduce::$fold) })?
393 $(fn inverse_fold(&self) -> Option<crate::reduce::Reduce> { Some(crate::reduce::Reduce::$inverse_fold) })?
394 $(fn truth(&self) -> Option<crate::reduce::Reduce> { Some(crate::reduce::Reduce::$truth) })?
395 $(fn truth_table(&self) -> Option<u8> { Some($table) })?
396 $(fn same(&self, $sx: Range) -> Option<Range> { Some($same) })?
397 fn supports(&self, dtype: DType) -> bool {
398 match dtype {
399 DType::Boolean => (false $(|| objects!(@present $integer))? $(|| objects!(@present $native_integer))?) $(&& objects!(@boolean $integer))?,
400 DType::Integer(_) => false $(|| objects!(@present $integer))? $(|| objects!(@present $native_integer))?,
401 DType::Float(_) => (false $(|| objects!(@present $formula))? $(|| objects!(@present $test))? $(|| objects!(@present $native_real))?) $(&& !objects!(@only $integer))?,
402 DType::Complex(_) => false $(|| objects!(@present $native_complex))?,
403 DType::Character => false,
404 }
405 }
406 $(#[inline(always)] fn emit<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, $o: &O, $fargs: [O::V; $n]) -> Option<O::V> { let $k = self; $formula })?
407 $(#[inline(always)] fn emit_test<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, $to: &O, $targs2: [O::V; $n]) -> Option<O::B> { let $tk = self; $test })?
408 $(#[inline(always)] fn emit_against<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, o: &O, c: F, v: O::V, left: bool) -> Option<O::B> { let $ak = self; against(*self, $atol, o, c, v, left) }
409 fn bands(&self) -> Option<(crate::tolerant::Tolerance, crate::tolerant::Band, crate::tolerant::Band)> { let $ak = self; let (left, right) = <Self as crate::tolerant::Banding>::BANDS; Some(($atol, left, right)) })?
410 $(fn emit_repair<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(&self, o: &O, x: [O::V; $n]) -> Option<O::B> { Some(($repair).predicate(o, x[0])) }
411 fn repair_routine(&self) -> Option<Repair> { Some(($repair).routine()) })?
412 objects!(@partials $n, $(slope |$so, $sx1, $sr| $slope)? $(slopes |$s2o, $s2x, $s2y, $s2r| [$dx, $dy])? $(shared $shared)?);
413 $(objects!(@integers $n, $integer);)?
414 $(objects!(@checks $checks);)?
415 $(#[inline(always)] fn emit_valid<F: crate::Float, O: Ops<F> + ?Sized>(&self, $vv: Variant, $vo: &O, $vx: [O::V; $n], $vr: O::V) -> Option<(crate::error::Kind, O::B)> { let $vk = self; $valid })?
416 $(#[inline(always)] fn integer<I: crate::recipe::Overflowing>(&self, $nio: &crate::recipe::IntegerOps<I>, $nix: [I; $n]) -> Option<I> { let $nik = self; $native_integer })?
417 $(#[inline(always)] fn complex<F: crate::float::Native>(&self, $nfo: &crate::recipe::NativeOps<{ crate::FUSED }>, $nfx: [num_complex::Complex<F>; $n]) -> Option<num_complex::Complex<F>> { let $nfk = self; $native_complex })?
418 $(#[inline(always)] fn real<F: crate::float::Native>(&self, $nro: &crate::recipe::NativeOps<{ crate::FUSED }>, $nrx: [F; $n]) -> Option<F> { let $nrk = self; $native_real })?
419 $(const REAL_COMPLEX: bool = true; #[inline(always)] fn real_complex<F: crate::float::Native>(&self, $rco: &crate::recipe::NativeOps<{ crate::FUSED }>, $rcx: [F; $n]) -> Option<num_complex::Complex<F>> { let $rck = self; $real_complex })?
420 fn native_slices(&self) -> Option<NativeSlices<Self, $n>> {
421 None $(.or_else(|| Some(NativeSlices::$slices())))?
422 $(.or_else(|| Some(NativeSlices::NONE)).map(|s| s.with_complex(crate::complex::bulk::$complex_slices, crate::complex::bulk::$complex_slices)))?
423 $(.or_else(|| Some(NativeSlices::NONE)).map(|s| s.with_real_complex(crate::complex::bulk::$real_complex_slices, crate::complex::bulk::$real_complex_slices)))?
424 }
425 }
426 )+};
427}
428
429objects!(1:
430 crate::Abs { range [x] => x.magnitude(); output real; formula |_, o, [x]| Some(o.magnitude(x)); slope |o, x, _| o.sign(x); integers formula; native_complex |_, _, [x]| Some(num_complex::Complex::new(crate::complex::modulus::<F, { crate::FUSED }>(x.re, x.im), F::zero())); exact |_, [x]| Ok(Some(x.abs())); valid |_, v, o, [x], _| negation_valid(v, o, x, true); }
431 crate::Negate { range [x] => Range { lo: -x.hi, hi: -x.lo }; formula |_, o, [x]| Some(o.negate(x)); slope |o, _, _| o.number(-F::one()); integers arithmetic; native_complex |_, _, [x]| Some(-x); exact |_, [x]| Ok(Some(-x)); valid |_, v, o, [x], _| negation_valid(v, o, x, false); }
432 crate::Conj { range [x] => x; characters Identity; formula |_, _, [x]| Some(x); slope |o, _, _| o.number(F::one()); integers formula; native_complex |_, _, [x]| Some(x.conj()); exact |_, [x]| Ok(Some(x.clone())); }
433 crate::Real { range [x] => x; output real; formula |_, _, [x]| Some(x); slope |o, _, _| o.number(F::one()); integers formula; native_complex |_, _, [x]| Some(num_complex::Complex::new(x.re, F::zero())); exact |_, [x]| Ok(Some(x.clone())); }
434 crate::Imag { range _ => exact(0.0, 0.0); output real; formula |_, o, [_]| Some(o.number(F::zero())); slope |o, _, _| o.number(F::zero()); integers formula; native_complex |_, _, [x]| Some(num_complex::Complex::new(x.im, F::zero())); exact |_, [_]| Ok(Some(num_rational::BigRational::zero())); }
435 crate::Angle { range _ => exact(-PI, PI); output real; slope |o, _, _| o.number(F::zero()); native_complex |_, _, [x]| Some(num_complex::Complex::new(crate::arc::atan2::<F, { crate::FUSED }>(x.im, x.re), F::zero())); native_real |_, _, [x]| Some(crate::arc::real_angle(x)); }
436 crate::Isnan { test |_, o, [x]| Some(o.not(o.equal(x, x))); integers formula; native_complex |_, _, [x]| Some(num_complex::Complex::new(if x.re.is_nan() || x.im.is_nan() { F::one() } else { F::zero() }, F::zero())); }
437 crate::Isfinite { test |_, o, [x]| Some(if o.integral() { o.equal(x, x) } else { o.equal(o.subtract(x, x), o.number(F::zero())) }); integers formula; native_complex |_, _, [x]| Some(num_complex::Complex::new(if x.re.is_finite() && x.im.is_finite() { F::one() } else { F::zero() }, F::zero())); }
438 crate::Isinf { test |_, o, [x]| Some(if o.integral() { o.not(o.equal(x, x)) } else { o.equal(o.magnitude(x), o.number(F::infinity())) }); integers formula; native_complex |_, _, [x]| Some(num_complex::Complex::new(if x.re.is_infinite() || x.im.is_infinite() { F::one() } else { F::zero() }, F::zero())); }
439 crate::Trunc { native_integer |_, _, [x]| Some(x); native_real |_, _, [x]| Some(x.trunc()); }
440 crate::Rint { native_integer |_, _, [x]| Some(x); native_complex |_, _, [x]| Some(num_complex::Complex::new(x.re.round_ties_even(), x.im.round_ties_even())); native_real |_, _, [x]| Some(x.round_ties_even()); }
441 crate::Scale { formula |k, o, [x]| Some(o.multiply(x, o.number(F::narrow(k.0)))); }
442 crate::Round { native_integer |k, o, [x]| { let wide = crate::parts::rounded_wide(Into::<i64>::into(x).into(), k.scale); Some(o.result(crate::integer::overflowed(o.variant, i64::try_from(wide).ok(), || wide as i64))) }; native_complex |k, _, [x]| Some(num_complex::Complex::new(crate::parts::rounded(x.re, k.scale), crate::parts::rounded(x.im, k.scale))); native_real |k, _, [x]| Some(crate::parts::rounded(x, k.scale)); }
443 crate::Sqrt { range [x] => exact(x.lo.max(0.0).sqrt(), x.hi.sqrt()); domain _, [x] => inside([x], [Range::NONNEGATIVE]); formula |_, o, [x]| Some(o.sqrt(x)); slope |o, _, r| o.reciprocal(o.add(r, r)); native_complex |_, _, [x]| Some(crate::complex::sqrt::<F, { crate::FUSED }>(x)); complex_slices sqrt; real_complex |_, o, [x]| { let r = o.sqrt(o.magnitude(x)); Some(if x < F::zero() { num_complex::Complex::new(F::zero(), r) } else { num_complex::Complex::new(r, F::zero()) }) }; checks nan; }
444 crate::Cbrt { range [x] => increasing(x, f64::cbrt); formula |_, o, [x]| Some(crate::pow::cbrt_formula(o, x)); slope |o, _, r| o.reciprocal(o.multiply(o.number(F::narrow(3.0)), o.multiply(r, r))); }
445 crate::Exp { range [x] => increasing(x, f64::exp).meet(Range::NONNEGATIVE); formula |_, o, [x]| Some(crate::exp::formula(o, x)); slope |_, _, r| r; native_complex |_, _, [x]| Some(crate::complex::exp::<F, { crate::FUSED }>(x)); complex_slices exp; }
446 crate::Expm1 { range [x] => increasing(x, f64::exp_m1); formula |_, o, [x]| Some(crate::exp::minus_one_formula(o, x, o.number(F::zero()))); slope |o, _, r| o.add(r, o.number(F::one())); native_complex |_, _, [x]| Some(crate::complex::exp::<F, { crate::FUSED }>(x) - Complex::new(F::one(), F::zero())); }
447 crate::Ln { range [x] => increasing(x, |v| if v < 0.0 { f64::NEG_INFINITY } else { v.ln() }); domain _, [x] => inside([x], [Range::NONNEGATIVE]); formula |_, o, [x]| Some(crate::ln::formula(o, x)); slope |o, x, _| o.reciprocal(x); native_complex |_, _, [x]| Some(crate::complex::ln::<F, { crate::FUSED }>(x)); complex_slices ln; real_complex |_, o, [x]| Some(num_complex::Complex::new(crate::ln::formula(o, o.magnitude(x)), if x < F::zero() { F::narrow(PI) } else { F::zero() })); checks nan; }
448 crate::Log2 { range [x] => increasing(x, f64::log2); domain _, [x] => inside([x], [Range::NONNEGATIVE]); formula |_, o, [x]| Some(crate::ln::log_with(o, x, crate::ln::base2(o))); slope |o, x, _| o.divide(o.number(F::narrow(std::f64::consts::LOG2_E)), x); slices unary; checks nan; }
449 crate::Log10 { range [x] => increasing(x, f64::log10); domain _, [x] => inside([x], [Range::NONNEGATIVE]); formula |_, o, [x]| Some(crate::ln::log_with(o, x, crate::ln::base10(o))); slope |o, x, _| o.divide(o.number(F::narrow(std::f64::consts::LOG10_E)), x); slices unary; checks nan; }
450 crate::RealLn { range [x] => increasing(x.magnitude(), f64::ln); output real; formula |_, o, [x]| Some(crate::ln::formula(o, o.magnitude(x))); slope |o, x, _| o.reciprocal(x); native_complex |_, _, [x]| Some(crate::complex::ln::<F, { crate::FUSED }>(x)); }
451 crate::RealSqrt {
452 range [x] => exact(x.lo.max(0.0).sqrt(), x.hi.max(0.0).sqrt()); output real;
453 formula |_, o, [x]| Some(crate::recipe::real_sqrt(o, x));
454 slope |o, x, r| {
455 let (zero, inside) = (o.number(F::zero()), o.less(o.number(F::zero()), x));
456 let r = o.select(inside, r, o.number(F::one()));
458 o.select(inside, o.reciprocal(o.add(r, r)), zero)
459 };
460 native_complex |_, _, [x]| Some(crate::complex::sqrt::<F, { crate::FUSED }>(x));
461 }
462 crate::RealSqrt1mx2 {
463 range [x] => decreasing(x.magnitude(), |v| (1.0 - v.min(1.0) * v.min(1.0)).sqrt()).meet(Range::NONNEGATIVE); output real;
464 slope |o, x, r| {
466 let (one, inside) = (o.number(F::one()), o.less(o.magnitude(x), o.number(F::one())));
467 o.select(inside, o.negate(o.divide(x, o.select(inside, r, one))), o.number(F::zero()))
468 };
469 native_complex |_, _, [x]| Some(crate::complex::sqrt1mx2::<F, { crate::FUSED }>(x)); native_real |_, _, [x]| Some(if x.abs() > F::one() { F::zero() } else { crate::sqrts::sqrt1mx2::<F, { crate::FUSED }>(x) });
470 }
471 crate::RealSqrtx2m1 {
472 range [x] => increasing(x, |v| if v.abs() < 1.0 { 0.0 } else { v.signum() * (v * v - 1.0).sqrt() }); output real;
473 slope |o, x, r| {
475 let (one, outside) = (o.number(F::one()), o.less(o.number(F::one()), o.magnitude(x)));
476 o.select(outside, o.divide(o.magnitude(x), o.select(outside, o.magnitude(r), one)), o.number(F::zero()))
477 };
478 native_complex |_, _, [x]| Some(crate::complex::sqrtx2m1::<F, { crate::FUSED }>(x)); native_real |_, _, [x]| Some(if x.abs() < F::one() { F::zero() } else { x.signum() * crate::sqrts::sqrtx2m1::<F, { crate::FUSED }>(x.abs()) });
479 }
480 crate::RealAsin {
481 range [x] => increasing(x, |v| v.clamp(UNIT.0, UNIT.1).asin()); output real;
482 slope |o, x, _| {
484 let inside = o.less(o.magnitude(x), o.number(F::one()));
485 o.select(inside, o.reciprocal(o.sqrt1mx2(o.select(inside, x, o.number(F::zero())))), o.number(F::zero()))
486 };
487 native_complex |_, _, [x]| Some(crate::complex::asin::<F, { crate::FUSED }>(x)); native_real |_, _, [x]| Some(crate::arc::asin::<F, { crate::FUSED }>(if x > F::one() { F::one() } else if x < -F::one() { -F::one() } else { x }));
488 }
489 crate::RealAcos {
490 range [x] => decreasing(x, |v| v.clamp(UNIT.0, UNIT.1).acos()).meet(Range::NONNEGATIVE); output real;
491 slope |o, x, _| {
492 let inside = o.less(o.magnitude(x), o.number(F::one()));
493 o.select(inside, o.negate(o.reciprocal(o.sqrt1mx2(o.select(inside, x, o.number(F::zero()))))), o.number(F::zero()))
494 };
495 native_complex |_, _, [x]| Some(crate::complex::acos::<F, { crate::FUSED }>(x)); native_real |_, _, [x]| Some(crate::arc::acos::<F, { crate::FUSED }>(if x > F::one() { F::one() } else if x < -F::one() { -F::one() } else { x }));
496 }
497 crate::RealAcosh {
498 range [x] => even(x, |v| v.abs().max(1.0).acosh()).meet(Range::NONNEGATIVE); output real;
499 slope |o, x, r| {
501 let (one, outside) = (o.number(F::one()), o.less(o.number(F::one()), o.magnitude(x)));
502 o.select(outside, o.divide(o.sign(x), o.select(outside, o.sinh(r), one)), o.number(F::zero()))
503 };
504 native_complex |_, _, [x]| Some(crate::complex::acosh::<F, { crate::FUSED }>(x)); native_real |_, _, [x]| Some(crate::hyperbolic::acosh::<F, { crate::FUSED }>(if x.abs() < F::one() { F::one() } else { x.abs() }));
505 }
506 crate::RealSqrtm1mx2 { range _ => exact(0.0, 0.0); output real; slope |o, _, _| o.number(F::zero()); native_complex |k, _, [x]| Some(crate::complex::sqrtm1mx2::<F, { crate::FUSED }>(x, k.0)); native_real |_, _, [x]| Some(if x.is_nan() { x } else { F::zero() }); }
507 crate::sqrts::Sqrtm1mx2 { output complex; native_complex |k, _, [x]| Some(crate::complex::sqrtm1mx2::<F, { crate::FUSED }>(x, k.0)); }
508 crate::Log1p { range [x] => increasing(x, |v| if v < -1.0 { f64::NEG_INFINITY } else { v.ln_1p() }); domain _, [x] => inside([x], [exact(-1.0, f64::INFINITY)]); formula |_, o, [x]| Some(crate::ln::log1p_formula(o, x)); slope |o, x, _| o.reciprocal(o.add(o.number(F::one()), x)); native_complex |_, _, [x]| Some(crate::complex::ln1p::<F, { crate::FUSED }>(x)); checks nan; }
509 crate::Sinh { range [x] => increasing(x, f64::sinh); formula |_, o, [x]| Some(crate::hyperbolic::sinh_formula(o, x)); slope |o, x, _| o.cosh(x); native_complex |_, _, [x]| Some(crate::complex::sinh::<F, { crate::FUSED }>(x)); complex_slices sinh; }
510 crate::Cosh { range [x] => even(x, f64::cosh).meet(Range::NONNEGATIVE); formula |_, o, [x]| Some(crate::hyperbolic::cosh_formula(o, x)); slope |o, x, _| o.sinh(x); native_complex |_, _, [x]| Some(crate::complex::cosh::<F, { crate::FUSED }>(x)); complex_slices cosh; }
511 crate::Tanh { range [x] => increasing(x, f64::tanh); formula |_, o, [x]| Some(crate::hyperbolic::tanh_formula(o, x)); slope |o, _, r| o.subtract(o.number(F::one()), o.multiply(r, r)); native_complex |_, _, [x]| Some(crate::complex::tanh::<F, { crate::FUSED }>(x)); complex_slices tanh; }
512 crate::Asinh { range [x] => increasing(x, f64::asinh); slope |o, x, _| o.reciprocal(o.sqrt1px2(x)); native_complex |_, _, [x]| Some(crate::complex::asinh::<F, { crate::FUSED }>(x)); complex_slices asinh; native_real |_, _, [x]| Some(crate::hyperbolic::asinh::<F, { crate::FUSED }>(x)); }
513 crate::Acosh { range [x] => increasing(x, |v| v.max(1.0).acosh()).meet(Range::NONNEGATIVE); domain _, [x] => inside([x], [exact(1.0, f64::INFINITY)]); slope |o, _, r| o.reciprocal(o.sinh(r)); native_complex |_, _, [x]| Some(crate::complex::acosh::<F, { crate::FUSED }>(x)); complex_slices acosh; native_real |_, _, [x]| Some(crate::hyperbolic::acosh::<F, { crate::FUSED }>(x)); checks nan; }
515 crate::Atanh { range [x] => increasing(x, |v| v.clamp(UNIT.0, UNIT.1).atanh()); domain _, [x] => inside([x], [exact(UNIT.0, UNIT.1)]); slope |o, x, _| o.reciprocal(o.subtract(o.number(F::one()), o.multiply(x, x))); native_complex |_, _, [x]| Some(crate::complex::atanh::<F, { crate::FUSED }>(x)); complex_slices atanh; native_real |_, _, [x]| Some(crate::hyperbolic::atanh::<F, { crate::FUSED }>(x)); checks nan; }
516 crate::RealAtanh { output real; slope |o, x, _| o.reciprocal(o.subtract(o.number(F::one()), o.multiply(x, x))); native_complex |_, _, [x]| Some(crate::complex::atanh::<F, { crate::FUSED }>(x)); native_real |_, _, [x]| Some(crate::hyperbolic::atanh::<F, { crate::FUSED }>(if x.abs() > F::one() { F::one()/x } else { x })); }
517 crate::Sin { range _ => exact(UNIT.0, UNIT.1).loose(); formula |_, o, [x]| Some(crate::trig::formula(o, x, crate::trig::Kind::Sin)); slope |o, x, _| o.cos(x); repair crate::trig::Kind::Sin; native_complex |_, _, [x]| Some(crate::complex::sin::<F, { crate::FUSED }>(x)); complex_slices sin; }
518 crate::Cos { range _ => exact(UNIT.0, UNIT.1).loose(); formula |_, o, [x]| Some(crate::trig::formula(o, x, crate::trig::Kind::Cos)); slope |o, x, _| o.negate(o.sin(x)); repair crate::trig::Kind::Cos; native_complex |_, _, [x]| Some(crate::complex::cos::<F, { crate::FUSED }>(x)); complex_slices cos; }
519 crate::Tan { formula |_, o, [x]| Some(crate::trig::formula(o, x, crate::trig::Kind::Tan)); slope |o, _, r| o.add(o.number(F::one()), o.multiply(r, r)); repair crate::trig::Kind::Tan; native_complex |_, _, [x]| Some(crate::complex::tan::<F, { crate::FUSED }>(x)); complex_slices tan; }
520 crate::Asin { range [x] => increasing(x, |v| v.clamp(UNIT.0, UNIT.1).asin()); domain _, [x] => inside([x], [exact(UNIT.0, UNIT.1)]); slope |o, x, _| o.reciprocal(o.sqrt1mx2(x)); native_complex |_, _, [x]| Some(crate::complex::asin::<F, { crate::FUSED }>(x)); complex_slices asin; native_real |_, _, [x]| Some(crate::arc::asin::<F, { crate::FUSED }>(x)); checks nan; }
521 crate::Acos { range [x] => decreasing(x, |v| v.clamp(UNIT.0, UNIT.1).acos()).meet(Range::NONNEGATIVE); domain _, [x] => inside([x], [exact(UNIT.0, UNIT.1)]); slope |o, x, _| o.negate(o.reciprocal(o.sqrt1mx2(x))); native_complex |_, _, [x]| Some(crate::complex::acos::<F, { crate::FUSED }>(x)); complex_slices acos; native_real |_, _, [x]| Some(crate::arc::acos::<F, { crate::FUSED }>(x)); checks nan; }
522 crate::Atan { range [x] => increasing(x, f64::atan); slope |o, x, _| o.reciprocal(o.add(o.number(F::one()), o.multiply(x, x))); native_complex |_, _, [x]| Some(crate::complex::atan::<F, { crate::FUSED }>(x)); complex_slices atan; native_real |_, _, [x]| Some(crate::arc::atan::<F, { crate::FUSED }>(x)); }
523 crate::Sqrt1mx2 { range [x] => decreasing(x.magnitude(), |v| (1.0 - v.min(1.0) * v.min(1.0)).sqrt()).meet(Range::NONNEGATIVE); domain _, [x] => inside([x], [exact(UNIT.0, UNIT.1)]); slope |o, x, r| o.negate(o.divide(x, r)); native_complex |_, _, [x]| Some(crate::complex::sqrt1mx2::<F, { crate::FUSED }>(x)); complex_slices sqrt1mx2; native_real |_, _, [x]| Some(crate::sqrts::sqrt1mx2::<F, { crate::FUSED }>(x)); }
524 crate::Sqrt1px2 { range [x] => even(x, |v| (1.0 + v * v).sqrt()).meet(Range::NONNEGATIVE); slope |o, x, r| o.divide(x, r); native_complex |_, _, [x]| Some(crate::complex::sqrt1px2::<F, { crate::FUSED }>(x)); complex_slices sqrt1px2; native_real |_, _, [x]| Some(crate::sqrts::sqrt1px2::<F, { crate::FUSED }>(x)); }
525 crate::Sqrtx2m1 {
526 range [x] => increasing(x, |v| v.signum() * (v * v - 1.0).max(0.0).sqrt());
527 domain _, [x] => inside([x], [if x.lo >= 1.0 { exact(1.0, f64::INFINITY) } else { exact(f64::NEG_INFINITY, -1.0) }]);
528 slope |o, x, r| o.divide(x, r);
529 native_complex |_, _, [x]| Some(crate::complex::sqrtx2m1::<F, { crate::FUSED }>(x));
530 complex_slices sqrtx2m1;
531 native_real |_, _, [x]| Some(crate::sqrts::sqrtx2m1::<F, { crate::FUSED }>(x)); checks nan;
532 }
533 crate::Reciprocal { range [x] => quotient(Range::of(Some(1.0)), x).loose(); formula |_, o, [x]| Some(if o.integral() { crate::recipe::integer_floor_divide(o, o.number(F::one()), x) } else { crate::recipe::reciprocal(o, x) }); slope |o, _, r| o.negate(o.multiply(r, r)); integers arithmetic; native_complex |_, _, [x]| Some(crate::complex::divide::<F>(num_complex::Complex::new(F::one(), F::zero()), x)); exact |_, [x]| crate::exact::divide(&num_rational::BigRational::one(), x, false); }
534 crate::PiTimes { range [x] => pi_times(x); formula |_, o, [x]| Some(crate::recipe::pi_times(o, x)); slope |o, _, _| o.number(F::narrow(std::f64::consts::PI)); native_complex |_, _, [x]| Some(x * F::narrow(std::f64::consts::PI)); }
535 crate::Factorial {
537 range args => signs(args); output integer;
538 slope |o, x, r| o.multiply(r, o.digamma(o.add(x, o.number(F::one()))));
539 native_integer |_, o, [x]| {
540 let n: i64 = x.into();
541 if n < 0 { return Some(if o.variant.fails_on_overflow() { o.result(i64::MAX) } else { I::limit(1) }); }
543 let (p, valid) = (2..=n.min(66)).fold((1, true), |(p, valid), k| {
545 let (q, fits) = <Product as Reduction<i64>>::chain(p, k);
546 (q, valid && fits)
547 });
548 if o.variant.fails_on_overflow() && !valid { (crate::error::Kind::Overflow.reporter())(); }
549 Some(o.result(p))
550 };
551 native_complex |_, _, [x]| Some(crate::complex::factorial::<F, { crate::FUSED }>(x));
552 native_real |_, _, [x]| Some(crate::special::factorial::<F, { crate::FUSED }>(x));
553 exact |_, [x]| crate::exact::factorial(x);
554 }
555 crate::Gamma {
556 range args => signs(args); slope |o, x, r| o.multiply(r, o.digamma(x));
557 native_complex |_, _, [x]| Some(crate::complex::gamma::<F, { crate::FUSED }>(x));
558 native_real |_, _, [x]| Some(crate::special::gamma::<F, { crate::FUSED }>(x)); checks nan;
559 }
560 crate::Sign { formula |_, o, [x]| Some(crate::recipe::sign(o, x)); slope |o, _, _| o.number(F::zero()); integers arithmetic; native_complex |_, _, [x]| Some(crate::complex::sign::<F, { crate::FUSED }>(x)); exact |_, [x]| Ok(Some(x.signum())); }
561 crate::tolerant::Floor { formula |k, o, [x]| Some(crate::with_tolerance!(k.0, |t| t.floor_in(o, x))); slope |o, _, _| o.number(F::zero()); integers formula; native_complex |k, o, [x]| Some(crate::tolerant::TolerantComplex::complex_floor(native_tolerance(o.tolerance, k.0), x)); exact |_, [x]| Ok(Some(x.floor())); }
562 crate::tolerant::Ceil { formula |k, o, [x]| Some(crate::with_tolerance!(k.0, |t| t.ceil_in(o, x))); slope |o, _, _| o.number(F::zero()); integers formula; native_complex |k, o, [x]| Some(crate::tolerant::TolerantComplex::complex_ceil(native_tolerance(o.tolerance, k.0), x)); exact |_, [x]| Ok(Some(x.ceil())); }
563 crate::Not { table 1; test |_, o, [x]| Some(o.equal(x, o.number(F::zero()))); integers formula; valid |_, v, o, x, _| truth_valid(v, o, x); }
564 crate::Cis { output complex; native_complex |_, _, [x]| Some(crate::complex::exp::<F, { crate::FUSED }>(num_complex::Complex::new(-x.im, x.re))); complex_slices cis; real_complex_slices real_cis; real_complex |_, o, [x]| (!crate::trig::Kind::Cos.predicate(o, x)).then(|| { let (c, s) = crate::complex::quarter(x).unwrap_or_else(|| (crate::trig::formula(o, x, crate::trig::Kind::Cos), crate::trig::formula(o, x, crate::trig::Kind::Sin))); num_complex::Complex::new(c, s) }); }
565 crate::LambertW { domain dtype, [x] => crate::lambert::real_domain(dtype, x); native_complex |_, _, [x]| Some(crate::lambert::complex(x)); native_real |_, _, [x]| Some(crate::lambert::lambertw::<F, { crate::FUSED }>(x)); checks nan; }
566 crate::Lgamma { slope |o, x, _| o.digamma(x); native_complex |_, _, [x]| Some(crate::complex::lgamma::<F, { crate::FUSED }>(x)); native_real |_, _, [x]| Some(crate::special::lgamma::<F, { crate::FUSED }>(x)); checks nan; }
567 crate::Digamma { slope |o, x, _| o.polygamma(o.number(F::one()), x); native_real |_, _, [x]| Some(crate::special::digamma::<F, { crate::FUSED }>(x)); }
569 crate::Erf { slope |o, x, _| o.multiply(o.number(F::narrow(std::f64::consts::FRAC_2_SQRT_PI)), o.exp(o.negate(o.multiply(x, x)))); native_real |_, _, [x]| Some(crate::special::erf::<F, { crate::FUSED }>(x)); }
571 crate::Erfc { slope |o, x, _| o.multiply(o.number(F::narrow(-std::f64::consts::FRAC_2_SQRT_PI)), o.exp(o.negate(o.multiply(x, x)))); native_real |_, _, [x]| Some(crate::special::erfc::<F, { crate::FUSED }>(x)); slices unary; }
572 crate::Erfinv { slope |o, _, r| o.multiply(o.number(F::narrow(std::f64::consts::FRAC_2_SQRT_PI.recip())), o.exp(o.multiply(r, r))); native_real |_, _, [x]| Some(crate::special::erfinv::<F, { crate::FUSED }>(x)); slices unary; checks nan; }
574 crate::Erfcinv { slope |o, _, r| o.multiply(o.number(F::narrow(-std::f64::consts::FRAC_2_SQRT_PI.recip())), o.exp(o.multiply(r, r))); native_real |_, _, [x]| Some(crate::special::erfcinv::<F, { crate::FUSED }>(x)); slices unary; checks nan; }
575);
576objects!(2:
577 crate::Add { range [x, y] => exact(x.lo + y.lo, x.hi + y.hi); identity [Some(0.0), Some(0.0)]; fold Sum; truth Sum; characters Shift; formula |_, o, [x, y]| Some(o.add(x, y)); slopes |o, _, _, _| [o.number(F::one()), o.number(F::one())]; integers arithmetic; native_complex |_, _, [x, y]| Some(x+y); exact |_, [x, y]| Ok(Some(x + y)); valid |_, v, o, [x, y], _| arithmetic_valid(v, o, crate::recipe::Binary::Add, x, y); }
578 crate::Subtract { range [x, y] => exact(x.lo - y.hi, x.hi - y.lo); identity [None, Some(0.0)]; inverse_fold Sum; characters Difference; formula |_, o, [x, y]| Some(o.subtract(x, y)); slopes |o, _, _, _| [o.number(F::one()), o.number(-F::one())]; integers arithmetic; native_complex |_, _, [x, y]| Some(x-y); exact |_, [x, y]| Ok(Some(x - y)); valid |_, v, o, [x, y], _| arithmetic_valid(v, o, crate::recipe::Binary::Subtract, x, y); }
579 crate::Multiply { range [x, y] => corners(x, y, |a, b| a * b); identity [Some(1.0), Some(1.0)]; fold Product; table 8; same |x| x.square(); formula |_, o, [x, y]| Some(o.multiply(x, y)); slopes |_, x, y, _| [y, x]; integers arithmetic; native_complex |_, _, [x, y]| Some(x*y); exact |_, [x, y]| Ok(Some(x * y)); valid |_, v, o, [x, y], _| arithmetic_valid(v, o, crate::recipe::Binary::Multiply, x, y); }
580 crate::Divide { range [x, y] => quotient(x, y); identity [None, Some(1.0)]; inverse_fold Product; formula |_, o, [x, y]| Some(o.divide(x, y)); slopes |o, _, y, r| [o.reciprocal(y), o.negate(o.divide(r, y))]; native_complex |_, _, [x, y]| Some(crate::complex::divide::<F>(x, y)); exact |_, [x, y]| crate::exact::divide(x, y, false); }
581 crate::MathishDivide {
583 range [x, y] => quotient(x, y); identity [None, Some(1.0)]; inverse_fold Product;
584 formula |_, o, [x, y]| Some(match o.constant(y) { Some(d) if d != F::zero() => o.divide(x, y), _ => crate::recipe::mathish_divide(o, x, y) });
585 slopes |o, _, y, r| [o.reciprocal(y), o.negate(o.divide(r, y))];
586 native_complex |_, _, [x, y]| Some(if crate::arith::both_zero(x, y) { x } else { crate::complex::divide::<F>(x, y) });
587 exact |_, [x, y]| crate::exact::divide(x, y, true);
588 }
589 crate::PiTimes { range [x, y] => pi_times(quotient(x, y)); formula |_, o, [x, y]| Some(crate::recipe::pi_ratio(o, x, y)); slopes |o, _, y, r| [crate::recipe::pi_ratio(o, o.number(F::one()), y), o.negate(o.divide(r, y))]; native_complex |_, _, [x, y]| Some(crate::complex::divide::<F>(x, y) * F::narrow(std::f64::consts::PI)); }
591 Maximum {
593 range [x, y] => Range { lo: x.lo.max(y.lo), hi: x.hi.max(y.hi) }; identity [Some(f64::NEG_INFINITY), Some(f64::NEG_INFINITY)]; fold Max; table 14; characters Ordered;
594 formula |_, o, [x, y]| Some(o.larger(x, y)); slopes |o, x, y, _| [o.one_where(o.less_equal(y, x)), o.one_where(o.less(x, y))]; integers formula; exact |_, [x, y]| Ok(Some(x.max(y).clone()));
595 }
596 Minimum {
597 range [x, y] => Range { lo: x.lo.min(y.lo), hi: x.hi.min(y.hi) }; identity [Some(f64::INFINITY), Some(f64::INFINITY)]; fold Min; table 8; characters Ordered;
598 formula |_, o, [x, y]| Some(o.smaller(x, y)); slopes |o, x, y, _| [o.one_where(o.less_equal(x, y)), o.one_where(o.less(y, x))]; integers formula; exact |_, [x, y]| Ok(Some(x.min(y).clone()));
599 }
600 crate::Hypot { range [x, y] => { let (a, b) = (x.magnitude(), y.magnitude()); exact(a.lo.max(b.lo), a.hi.hypot(b.hi)).loose() }; native_real |_, _, [x, y]| Some(crate::pow::hypot::<F, { crate::FUSED }>(x, y)); }
601 crate::Atan2 { range _ => exact(-PI, PI).loose(); native_real |_, _, [y, x]| Some(crate::arc::atan2::<F, { crate::FUSED }>(y, x)); }
602 crate::Beta { range args => signs(args); slopes |o, a, b, r| [o.multiply(r, o.subtract(o.digamma(a), o.digamma(o.add(a, b)))), o.multiply(r, o.subtract(o.digamma(b), o.digamma(o.add(a, b))))]; native_real |_, _, [a, b]| Some(crate::special::beta::<F, { crate::FUSED }>(a, b)); checks nan; }
604 crate::Pow {
606 range [x, y] => powered(x, y); domain _, [x, y] => if whole(y) { Some([x, y]) } else { inside([x, y], [Range::NONNEGATIVE, Range::ANY]) };
607 identity [None, Some(1.0)]; formula |_, o, [x, y]| Some(crate::pow::formula(o, x, y)); shared power;
608 native_integer |_, o, [x, y]| Some(I::low(crate::integer::native_power(o.variant, x.into(), y.into())));
609 native_complex |_, _, [x, y]| Some(crate::complex::pow::<F, { crate::FUSED }>(x, y)); complex_slices pow; slices power; exact |_, [x, y]| crate::exact::power(x, y); checks nan;
610 valid |_, v, o, x, r| power_valid(v, o, x, r);
611 }
612 crate::RealPow { range [x, _] => signs([x]); identity [None, Some(1.0)]; output real; formula |_, o, [x, y]| Some(crate::pow::real_formula(o, x, y)); shared real_power; native_complex |_, _, [x, y]| Some(crate::complex::pow::<F, { crate::FUSED }>(x, y)); }
613 crate::Root { range [_, y] => signs([y]); domain _, [n, y] => root_domain(n, y); formula |_, o, [n, y]| crate::pow::root_formula(o, n, y); slopes |o, x, y, r| [o.negate(o.divide(o.multiply(r, o.ln(y)), o.multiply(x, x))), o.divide(r, o.multiply(x, y))]; native_complex |_, _, [n, y]| Some(crate::complex::nth_root::<F, { crate::FUSED }>(n, y)); native_real |_, _, [n, y]| Some(crate::pow::root::<F, { crate::FUSED }>(n, y)); slices root; exact |_, [n, y]| { if num_traits::Zero::is_zero(n) { return Err(crate::error::Error::new(crate::error::Kind::Domain, "root degree is zero")); } crate::exact::power(y, &n.recip()) }; checks nan; }
615 crate::Logb { domain _, [x, y] => inside([x, y], [Range::NONNEGATIVE; 2]); shared logb; native_complex |_, _, [b, x]| Some(crate::complex::divide(crate::complex::ln::<F, { crate::FUSED }>(x), crate::complex::ln::<F, { crate::FUSED }>(b))); native_real |_, _, [b, x]| Some(crate::ln::logb::<F, { crate::FUSED }>(b, x)); slices logarithm; valid |_, _, o, x, r| logarithm_valid(o, x, r); }
616 crate::MathishLogb { domain _, [x, y] => inside([x, y], [Range::NONNEGATIVE; 2]); shared logb; native_complex |_, _, [b, x]| { let one = num_complex::Complex::new(F::one(), F::zero()); Some(if b == one && x == one { num_complex::Complex::new(F::zero(), F::zero()) } else { crate::complex::divide(crate::complex::ln::<F, { crate::FUSED }>(x), crate::complex::ln::<F, { crate::FUSED }>(b)) }) }; native_real |_, _, [b, x]| Some(crate::ln::logb_mode::<F, true>(b, x)); slices mathish_logarithm; valid |_, _, o, x, r| logarithm_valid(o, x, r); }
617 crate::tolerant::Residue {
619 identity [Some(0.0), None];
620 formula |k, o, [x, y]| if o.integral() { masked(o, x, y) } else { (!k.0.is_exact()).then(|| k.0.residue_in(o, x, y)) };
621 slopes |o, x, y, r| [{
622 let at_zero = o.equal(x, o.number(F::zero()));
623 o.negate(o.multiply(o.one_where(o.not(at_zero)), o.divide(o.subtract(y, r), o.add(x, o.one_where(at_zero)))))
624 }, o.number(F::one())];
625 native_integer |_, _, [x, y]| Some(I::low(crate::integer::residue(x.into(), y.into())));
626 native_complex |k, o, [x, y]| Some(crate::tolerant::TolerantComplex::complex_residue(native_tolerance(o.tolerance, k.0), x, y));
627 native_real |k, o, [x, y]| {
628 let t = native_tolerance(o.tolerance, k.0);
629 Some(if t.is_exact() { crate::tolerant::exact_residue(x, y) } else { crate::tolerant::Tolerant::residue(t, x, y) })
630 };
631 slices residue;
632 exact |_, [x, y]| Ok(Some(crate::exact::residue(x, y)));
633 }
634 crate::Binom {
636 identity [None, Some(1.0)];
637 slopes |o, n, k, _| [o.binom_dn(n, k), o.binom_dk(n, k)];
638 native_integer |_, o, [n, k]| {
639 let (n, k) = (n.into(), k.into());
640 let value = crate::integer::overflowed(o.variant, crate::integer::binom(n, k), || crate::integer::binom_wrapped(n, k).unwrap_or(0));
641 Some(o.result(value))
642 };
643 native_complex |_, _, [n, k]| Some(crate::complex::binom::<F, { crate::FUSED }>(n, k));
644 native_real |_, _, [n, k]| Some(crate::special::binom::<F, { crate::FUSED }>(n, k));
645 exact |_, [n, k]| {
646 if !k.is_integer() { return Ok(None); }
647 let k = num_traits::ToPrimitive::to_i64(k).ok_or_else(|| crate::error::Error::new(crate::error::Kind::Limit, "exact binomial count is too large"))?;
648 crate::exact::binom(n, k).map(Some)
649 }; checks nan;
650 }
651 crate::tolerant::FloorDivide {
652 identity [None, Some(1.0)];
653 formula |k, o, [x, y]| Some(if o.integral() { crate::recipe::integer_floor_divide(o, x, y) }
654 else { crate::with_tolerance!(k.0, |t| t.floor_in(o, o.divide(x, y))) });
655 slopes |o, _, _, _| [o.number(F::zero()), o.number(F::zero())];
656 integers arithmetic;
657 exact |_, [x, y]| crate::exact::floor_divide(x, y, false);
658 valid |_, v, o, [x, y], _| arithmetic_valid(v, o, crate::recipe::Binary::Divide, x, y);
659 }
660 crate::tolerant::MathishFloorDivide {
661 identity [None, Some(1.0)];
662 formula |k, o, [x, y]| Some(if o.integral() { crate::recipe::integer_floor_divide(o, x, y) }
663 else { crate::with_tolerance!(k.0, |t| t.floor_in(o, crate::recipe::mathish_divide(o, x, y))) });
664 slopes |o, _, _, _| [o.number(F::zero()), o.number(F::zero())];
665 integers arithmetic;
666 exact |_, [x, y]| crate::exact::floor_divide(x, y, true);
667 valid |_, v, o, [x, y], _| arithmetic_valid(v, o, crate::recipe::Binary::Divide, x, y);
668 }
669 crate::tolerant::Gcd {
670 identity [Some(0.0), Some(0.0)]; table 14;
671 native_integer |_, o, [x, y]| { let (x, y) = (x.into(), y.into()); Some(o.result(crate::integer::overflowed(o.variant, crate::integer::gcd(x, y), || crate::integer::gcd_wrapped(x, y)))) };
672 native_complex |k, o, [x, y]| Some(native_tolerance(o.tolerance, k.0).complex_gcd(x, y));
673 native_real |k, o, [x, y]| Some(native_tolerance(o.tolerance, k.0).gcd(x, y));
674 exact |_, [x, y]| Ok(Some(crate::exact::gcd(x, y))); checks nan;
675 }
676 crate::tolerant::Lcm {
677 identity [Some(1.0), Some(1.0)]; table 8;
678 native_integer |_, o, [x, y]| { let (x, y) = (x.into(), y.into()); Some(o.result(crate::integer::overflowed(o.variant, crate::integer::lcm(x, y), || crate::integer::lcm_wrapped(x, y)))) };
679 native_complex |k, o, [x, y]| Some(lcm_from_gcd(x, y, native_tolerance(o.tolerance, k.0).complex_gcd(x, y)));
680 native_real |k, o, [x, y]| Some(lcm_from_gcd(x, y, native_tolerance(o.tolerance, k.0).gcd(x, y)));
681 exact |_, [x, y]| Ok(Some(crate::exact::lcm(x, y))); checks nan;
682 }
683 crate::Nand { table 7; test |_, o, [x, y]| Some(o.not(o.and(boolean_one(o, x), boolean_one(o, y)))); integers formula; valid |_, v, o, x, _| truth_valid(v, o, x); }
684 crate::Nor { table 1; test |_, o, [x, y]| Some(o.and(o.equal(x, o.number(F::zero())), o.equal(y, o.number(F::zero())))); integers formula; valid |_, v, o, x, _| truth_valid(v, o, x); }
685 crate::BitAnd { fold And; table 8; formula |_, o, [x, y]| Some(o.from_bits(o.integer_op(crate::recipe::Integer::And, o.to_bits(x), o.to_bits(y)))); integers only; }
686 crate::BitOr { fold Or; table 14; formula |_, o, [x, y]| Some(o.from_bits(o.integer_op(crate::recipe::Integer::Or, o.to_bits(x), o.to_bits(y)))); integers only; }
687 crate::LeftShift { formula |_, o, [x, y]| Some(o.from_bits(o.integer_op(crate::recipe::Integer::ShiftLeft, o.to_bits(x), o.to_integer(y)))); integers bits; }
688 crate::RightShift {
689 formula |_, o, [x, y]| Some(o.from_bits(o.integer_op(crate::recipe::Integer::SignedShiftRight, o.to_bits(x), o.to_integer(y))));
690 integers bits;
691 }
692 crate::reduce::Xor { fold Xor; table 6; test |_, o, [x, y]| Some(o.not(o.equal(x, y))); integers only; valid |_, v, o, x, _| truth_valid(v, o, x); }
693 crate::reduce::Xnor { fold Xnor; table 9; test |_, o, [x, y]| Some(o.equal(x, y)); integers only; valid |_, v, o, x, _| truth_valid(v, o, x); }
694 crate::tolerant::Equal {
695 identity [Some(1.0), Some(1.0)]; table 9; characters Ordered;
696 test |k, o, [x, y]| Some(tested(*k, k.0, o, x, y)); against |k| k.0; integers formula;
697 native_complex |k, o, [x, y]| Some(if native_tolerance(o.tolerance, k.0).complex_equal(x, y) { Complex::one() } else { Complex::zero() });
698 exact |_, [x, y]| Ok(Some(crate::exact::boolean(x == y)));
699 }
700 crate::tolerant::NotEqual {
701 identity [Some(0.0), Some(0.0)]; table 6; characters Ordered;
702 test |k, o, [x, y]| Some(tested(*k, k.0, o, x, y)); against |k| k.0; integers formula;
703 native_complex |k, o, [x, y]| Some(if !native_tolerance(o.tolerance, k.0).complex_equal(x, y) { Complex::one() } else { Complex::zero() });
704 exact |_, [x, y]| Ok(Some(crate::exact::boolean(x != y)));
705 }
706 crate::tolerant::Less { identity [Some(0.0), None]; table 2; characters Ordered; test |k, o, [x, y]| Some(tested(*k, k.0, o, x, y)); against |k| k.0; integers formula; exact |_, [x, y]| Ok(Some(crate::exact::boolean(x < y))); }
707 crate::tolerant::LessEqual { identity [Some(1.0), None]; table 11; characters Ordered; test |k, o, [x, y]| Some(tested(*k, k.0, o, x, y)); against |k| k.0; integers formula; exact |_, [x, y]| Ok(Some(crate::exact::boolean(x <= y))); }
708 crate::tolerant::Greater { identity [None, Some(0.0)]; table 4; characters Ordered; test |k, o, [x, y]| Some(tested(*k, k.0, o, x, y)); against |k| k.0; integers formula; exact |_, [x, y]| Ok(Some(crate::exact::boolean(x > y))); }
709 crate::tolerant::GreaterEqual { identity [None, Some(1.0)]; table 13; characters Ordered; test |k, o, [x, y]| Some(tested(*k, k.0, o, x, y)); against |k| k.0; integers formula; exact |_, [x, y]| Ok(Some(crate::exact::boolean(x >= y))); }
710 crate::BinomDk { shared binom_dk_slopes; native_real |_, _, [n, k]| Some(crate::special::binom_dk::<F, { crate::FUSED }>(n, k)); checks nan; }
711 crate::BinomDn { shared binom_dn_slopes; native_real |_, _, [n, k]| Some(crate::special::binom_dn::<F, { crate::FUSED }>(n, k)); checks nan; }
712 crate::Gammainc { shared gammainc_slopes; native_real |_, _, [a, x]| Some(crate::special::gammainc::<F, { crate::FUSED }>(a, x)); checks nan; }
713 crate::Gammaincc { native_real |_, _, [a, x]| Some(crate::special::gammaincc::<F, { crate::FUSED }>(a, x)); checks nan; }
714 crate::Gammaincinv { native_real |_, _, [a, p]| Some(crate::special::gammaincinv::<F, { crate::FUSED }>(a, p)); checks nan; }
715 crate::Polygamma { shared polygamma_slopes; native_real |_, _, [n, x]| Some(crate::special::polygamma::<F, { crate::FUSED }>(n, x)); checks nan; }
716);
717objects!(3:
718 crate::Clip { formula |_, o, [x, lo, hi]| Some(o.smaller(hi, o.larger(lo, x))); integers formula; }
719 crate::Choose { formula |_, o, [x, y, mask]| Some(o.select(o.not(o.equal(mask, o.number(F::zero()))), x, y)); integers formula; native_complex |_, _, [x, y, mask]| Some(if mask.re != F::zero() { x } else { y }); }
720 crate::Betainc {
721 range _ => exact(0.0, 1.0);
722 native_real |_, _, [a, b, x]| Some(crate::special::betainc::<F, { crate::FUSED }>(a, b, x)); checks nan;
723 }
724 crate::Betaincinv {
725 range _ => exact(0.0, 1.0);
726 native_real |_, _, [a, b, p]| Some(crate::special::betaincinv::<F, { crate::FUSED }>(a, b, p)); checks nan;
727 }
728);
729
730impl BetaincAt {
731 fn parameters(&self, width: FloatWidth) -> [f64; 2] { with_float_width!(width, T => [self.0, self.1].map(|x| <T as Float>::Working::narrow(x).wide())) }
732}
733
734impl Formula<1> for BetaincAt {
735 fn supports(&self, dtype: DType) -> bool { Betainc.supports(dtype) }
736 fn range(&self, [x]: [Range; 1]) -> Range { Betainc.range([Range::of(Some(self.0)), Range::of(Some(self.1)), x]) }
737 fn real<F: Native>(&self, o: &NativeOps<{ FUSED }>, [x]: [F; 1]) -> Option<F> {
738 let [a, b] = self.parameters(Ops::<F>::width(o));
739 Betainc.real(o, [F::narrow(a), F::narrow(b), x])
740 }
741 fn emit_partials<F: Float, O: Ops<F> + ?Sized>(&self, o: &O, [x]: [O::V; 1], _: O::V, [need]: [bool; 1]) -> [Option<O::V>; 1] {
742 [need.then(|| {
743 let ([a, b], one) = (self.parameters(o.width()), o.number(F::one()));
744 let n = |v: f64| o.number(F::narrow(v));
745 let mut e = o.negate(o.subtract(o.add(o.lgamma(n(a)), o.lgamma(n(b))), o.lgamma(n(a + b))));
746 if a != 1.0 { e = o.add(e, o.multiply(n(a - 1.0), o.ln(x))); }
747 if b != 1.0 { e = o.add(e, o.multiply(n(b - 1.0), o.ln(o.subtract(one, x)))); }
748 o.exp(e)
749 })]
750 }
751}
752
753fn whole(y: Range) -> bool { y.constant().is_some_and(|k| k.fract() == 0.0) }
755
756fn powered(x: Range, y: Range) -> Range { if y.constant().is_some_and(|k| matches!(parity(k), (true, false))) { Range::NONNEGATIVE } else { signs([x]) } }
758
759fn masked<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V, y: O::V) -> Option<O::V> {
761 let d = o.constant(x)?.wide();
762 let mask = crate::integer::mask(d as i64).filter(|_| d.fract() == 0.0)?;
763 Some(o.bit_and(y, o.number(F::narrow(mask as f64))))
764}
765
766#[inline]
767fn native_tolerance(value: Option<f64>, fallback: Tolerance) -> Tolerance { value.and_then(Tolerance::new).unwrap_or(fallback) }
768
769pub fn run_tolerance<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, t: crate::tolerant::Tolerance) -> Option<crate::tolerant::Tolerance> {
771 crate::tolerant::Tolerance::new(o.constant(o.tolerance(F::narrow(t.value())))?.wide())
772}
773fn against<K: crate::tolerant::Banding, F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(
775 k: K, t: crate::tolerant::Tolerance, o: &O, c: F, v: O::V, left: bool) -> Option<O::B> {
776 crate::tolerant::against_steps(k, run_tolerance(o, t)?, o, c, v, left)
777}
778
779#[inline]
781fn tested<K, F, O>(k: K, t: crate::tolerant::Tolerance, o: &O, x: O::V, y: O::V) -> O::B where K: crate::tolerant::Stepped + crate::tolerant::Banding + Copy,
782 F: crate::Float, O: crate::recipe::Ops<F> + ?Sized {
783 let side = match (o.constant(x), o.constant(y)) {
784 (Some(_), Some(_)) => None, (None, Some(c)) => Some((c, x, true)), (Some(c), None) => Some((c, y, false)), _ => None
785 };
786 side.and_then(|(c, v, left)| against(k, t, o, c, v, left)).unwrap_or_else(|| k.steps::<F, O>(o, x, y))
787}
788
789impl Formula<1> for crate::Bitwise {
791 const HAS_EXACT: bool = true;
792 fn exact(&self, [y]: [&num_rational::BigRational; 1]) -> Result<Option<num_rational::BigRational>, crate::error::Error> {
793 <Self as Formula<2>>::exact(self, [&num_rational::BigRational::zero(), y])
794 }
795 const INTEGER_FORMULA: bool = true;
796 fn supports(&self, dtype: DType) -> bool { <Self as Formula<2>>::supports(self, dtype) }
797 fn emit<F: crate::Float, O: Ops<F> + ?Sized>(&self, o: &O, [y]: [O::V; 1]) -> Option<O::V> { <Self as Formula<2>>::emit(self, o, [o.number(F::zero()), y]) }
798 fn integer<I: crate::recipe::Overflowing>(&self, o: &crate::recipe::IntegerOps<I>, [y]: [I; 1]) -> Option<I> { <Self as Formula<1>>::emit(self, o, [y]) }
799}
800impl Formula<2> for crate::Bitwise {
801 fn fold(&self) -> Option<crate::reduce::Reduce> {
802 use crate::reduce::Reduce::*;
803 match self.0 { 8 => Some(And), 14 => Some(Or), 6 => Some(Xor), 9 => Some(Xnor), _ => None }
804 }
805 const HAS_EXACT: bool = true;
806 fn exact(&self, [x, y]: [&num_rational::BigRational; 2]) -> Result<Option<num_rational::BigRational>, crate::error::Error> {
807 if !x.is_integer() || !y.is_integer() { return Err(crate::error::Error::new(crate::error::Kind::Domain, "bitwise needs whole numbers")); }
808 Ok(Some(num_rational::BigRational::from_integer(crate::bitwise(self.0, x.to_integer(), y.to_integer()))))
809 }
810 fn truth_table(&self) -> Option<u8> { Some(self.0 as u8) }
811 const INTEGER_FORMULA: bool = true;
812 fn supports(&self, dtype: DType) -> bool { dtype.is_integral() && (dtype != DType::Integer(crate::buffer::Width::U8) || self.0 & 1 == 0) }
813 fn emit<F: crate::Float, O: Ops<F> + ?Sized>(&self, o: &O, [x, y]: [O::V; 2]) -> Option<O::V> {
814 use crate::recipe::Integer;
815 o.integral().then(|| {
816 o.from_bits(crate::arith::bitwise_with(
817 self.0, o.to_bits(x), o.to_bits(y), o.integer(-1), |a, b| o.integer_op(Integer::And, a, b), |a, b| o.integer_op(Integer::Xor, a, b)))
818 })
819 }
820 fn integer<I: crate::recipe::Overflowing>(&self, o: &crate::recipe::IntegerOps<I>, x: [I; 2]) -> Option<I> { <Self as Formula<2>>::emit(self, o, x) }
821}
822
823impl<K: Formula<2>> Formula<2> for Flipped<K> {
824 fn truth_table(&self) -> Option<u8> { self.0.truth_table().map(|t| (t & 9) | (t & 2) << 1 | (t & 4) >> 1) }
825 fn character_policy(&self, [x, y]: [DType; 2]) -> Result<Option<crate::characters::Policy>, crate::error::Error> {
826 use crate::characters::{ByKinds, Policy};
827 self.0.character_policy([y, x]).map(|p| {
828 p.map(|p| match p {
829 Policy::Kinds(ByKinds::Left) => Policy::Kinds(ByKinds::Right), Policy::Kinds(ByKinds::Right) => Policy::Kinds(ByKinds::Left), p => p
830 })
831 })
832 }
833 fn exact(&self, [x, y]: [&num_rational::BigRational; 2]) -> Result<Option<num_rational::BigRational>, crate::error::Error> { self.0.exact([y, x]) }
834 const HAS_EXACT: bool = K::HAS_EXACT;
835 fn character_rule(&self) -> Option<crate::characters::Rule> { self.0.character_rule() }
836 fn result_dtype(&self, input: DType) -> DType { self.0.result_dtype(input) }
837 fn range(&self, [x, y]: [Range; 2]) -> Range { self.0.range([y, x]) }
838 fn same(&self, x: Range) -> Option<Range> { self.0.same(x) }
839 fn domain(&self, dtype: DType, [x, y]: [Range; 2]) -> Option<[Range; 2]> { self.0.domain(dtype, [y, x]).map(|[y, x]| [x, y]) }
840 fn identity(&self) -> [Option<f64>; 2] { let [x, y] = self.0.identity(); [y, x] }
841 const INTEGER_FORMULA: bool = K::INTEGER_FORMULA;
842 fn supports(&self, dtype: DType) -> bool { self.0.supports(dtype) }
843 const BOOLEAN: bool = K::BOOLEAN;
844 const FAILS_NAN: bool = K::FAILS_NAN;
845 #[inline]
846 fn integer<I: crate::recipe::Overflowing>(&self, o: &crate::recipe::IntegerOps<I>, [x, y]: [I; 2]) -> Option<I> { self.0.integer(o, [y, x]) }
847 #[inline]
848 fn real<F: crate::float::Native>(&self, o: &crate::recipe::NativeOps<{ crate::FUSED }>, [x, y]: [F; 2]) -> Option<F> { self.0.real(o, [y, x]) }
849 #[inline]
850 fn complex<F: crate::float::Native>(
851 &self, o: &crate::recipe::NativeOps<{ crate::FUSED }>, [x, y]: [num_complex::Complex<F>; 2]) -> Option<num_complex::Complex<F>> {
852 self.0.complex(o, [y, x])
853 }
854 fn emit_valid<F: crate::Float, O: Ops<F> + ?Sized>(&self, v: Variant, o: &O, [x, y]: [O::V; 2], r: O::V) -> Option<(crate::error::Kind, O::B)> {
855 self.0.emit_valid(v, o, [y, x], r)
856 }
857 fn emit<F: crate::Float, O: Ops<F> + ?Sized>(&self, o: &O, [x, y]: [O::V; 2]) -> Option<O::V> { self.0.emit(o, [y, x]) }
858 fn emit_test<F: crate::Float, O: Ops<F> + ?Sized>(&self, o: &O, [x, y]: [O::V; 2]) -> Option<O::B> { self.0.emit_test(o, [y, x]) }
859 fn emit_partials<F: crate::Float, O: Ops<F> + ?Sized>(&self, o: &O, [x, y]: [O::V; 2], r: O::V, [nx, ny]: [bool; 2]) -> [Option<O::V>; 2] {
860 let [dy, dx] = self.0.emit_partials(o, [y, x], r, [ny, nx]);
861 [dx, dy]
862 }
863}
864
865impl<K: Formula<1>> Formula<2> for crate::derivative::Slope<K> {
866 const MAP_ONLY: bool = true;
867 fn supports(&self, dtype: DType) -> bool { matches!(dtype, DType::Float(_)) }
868 fn emit<F: crate::Float, O: Ops<F> + ?Sized>(&self, o: &O, [x, r]: [O::V; 2]) -> Option<O::V> {
869 Some(self.0.emit_partials(o, [x], r, [true])[0].unwrap_or_else(|| o.number(F::narrow(f64::NAN))))
870 }
871}
872
873impl<K: Formula<2>, const SIDE: usize> Formula<3> for crate::derivative::Slope<K, SIDE> {
874 const MAP_ONLY: bool = true;
875 fn supports(&self, dtype: DType) -> bool { matches!(dtype, DType::Float(_)) }
876 fn emit<F: crate::Float, O: Ops<F> + ?Sized>(&self, o: &O, [x, y, r]: [O::V; 3]) -> Option<O::V> {
877 Some(self.0.emit_partials(o, [x, y], r, std::array::from_fn(|i| i == SIDE))[SIDE].unwrap_or_else(|| o.number(F::narrow(f64::NAN))))
878 }
879}
880
881#[inline(always)]
882fn arithmetic_valid<F: crate::Float, O: Ops<F> + ?Sized>(
883 variant: Variant, o: &O, op: crate::recipe::Binary, x: O::V, y: O::V) -> Option<(crate::error::Kind, O::B)> {
884 (variant.fails_on_overflow() && variant.dtype.is_integral()).then(|| (crate::error::Kind::Overflow, o.not(o.overflow(op, x, y))))
885}
886
887#[inline(always)]
888fn negation_valid<F: crate::Float, O: Ops<F> + ?Sized>(variant: Variant, o: &O, x: O::V, magnitude: bool) -> Option<(crate::error::Kind, O::B)> {
889 let (kind, valid) = arithmetic_valid(variant, o, crate::recipe::Binary::Subtract, o.number(F::zero()), x)?;
890 Some((kind, if magnitude { o.or(o.less_equal(o.number(F::zero()), x), valid) } else { valid }))
891}
892
893#[inline]
895fn boolean_one<F: crate::Float, O: Ops<F> + ?Sized>(o: &O, x: O::V) -> O::B {
896 let one = o.number(F::one());
897 crate::recipe::equal(o, x, one, |o| crate::recipe::close(o, o.tolerance(F::narrow(Tolerance::DEFAULT.value())), x, one))
898}
899
900#[inline(always)]
901fn truth_valid<F: crate::Float, O: Ops<F> + ?Sized, const N: usize>(variant: Variant, o: &O, args: [O::V; N]) -> Option<(crate::error::Kind, O::B)> {
902 variant.checked.then(|| {
903 let valid = args.into_iter()
904 .fold(o.equal(o.number(F::zero()), o.number(F::zero())), |valid, x| o.and(valid, o.or(o.equal(x, o.number(F::zero())), boolean_one(o, x))));
905 (crate::error::Kind::Domain, valid)
906 })
907}
908
909#[inline(always)]
910fn power_valid<F: Float, O: Ops<F> + ?Sized>(variant: Variant, o: &O, [x, y]: [O::V; 2], result: O::V) -> Option<(crate::error::Kind, O::B)> {
911 (variant.checked && !o.integral()).then(|| (crate::error::Kind::Domain, nan_valid(o, [x, y], [result])))
912}