1use crate::buffer::{Buffer, DType, Element, FloatWidth, Source, View, Width};
5use crate::domain::Range;
6use crate::error::{Error, Kind};
7use crate::gather::Writes;
8use crate::reduce::{Direction, Piece, Pieces, Seed, native_reduction};
9use crate::slice::Arg;
10use crate::{FUSED, Native, facts::Formula, recipe::NativeOps, tolerant::Banded};
11
12#[derive(Clone, Copy, Debug)]
14pub struct Settings { pub tolerance: crate::tolerant::Tolerance }
15impl Settings {
16 pub fn ops(self) -> crate::recipe::NativeOps<{ crate::FUSED }> { crate::recipe::NativeOps { tolerance: Some(self.tolerance.value()) } }
18}
19
20#[derive(Clone, Copy, PartialEq, Debug)]
22pub enum Projection {
23 AsIs,
25 Codes(crate::tolerant::Tolerance),
27 Wholes(crate::tolerant::Tolerance),
29}
30impl Projection {
31 fn wholes(self) -> Option<crate::tolerant::Tolerance> { match self { Self::AsIs => None, Self::Codes(t) | Self::Wholes(t) => Some(t) } }
33}
34
35pub enum WriteRoot<'a> { Read(crate::agreement::Read<'a>), Owned(Buffer) }
37impl WriteRoot<'_> {
38 fn initialize(self, dtype: DType, checked: bool, projection: Projection, output: &mut Buffer) -> Result<(), Error> {
40 match (self, projection) {
41 (Self::Read(read), Projection::AsIs) => read.read_into(0..read.plan.len(), dtype, checked, output),
42 (Self::Read(read), projection) => with_projected(&[read], None, projection, |reads| reads[0].read_into(0..read.plan.len(), dtype, checked, output)),
43 (Self::Owned(buffer), _) if buffer.dtype() == dtype => { *output = buffer; Ok(()) }
44 (Self::Owned(buffer), Projection::AsIs) => { *output = Buffer::cast_results(buffer.view(), dtype, checked)?; Ok(()) }
45 (Self::Owned(buffer), projection) => {
46 *output = with_projected_view(buffer.view(), projection, |view| Buffer::cast_results(view, dtype, checked))??;
47 Ok(())
48 }
49 }
50 }
51}
52
53#[derive(Clone)]
55pub struct Kernel {
56 variant: crate::domain::Variant, result: DType, boolean_inputs: bool, repair: bool, raising: bool, wholes: bool, policy: Option<crate::characters::Policy>,
57 definition: std::sync::Arc<dyn Application>
58}
59
60impl std::fmt::Debug for Kernel {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 f.debug_struct("Kernel").field("variant", &self.variant).field("arity", &self.arity()).finish()
63 }
64}
65
66trait Application: Send + Sync {
67 fn exact(&self, args: &[num_rational::BigRational]) -> Result<Option<num_rational::BigRational>, Error>;
68 fn has_exact(&self) -> bool;
69 fn integer(&self, variant: crate::domain::Variant, args: &[i64]) -> Result<i64, Error>;
70 fn real(&self, variant: crate::domain::Variant, args: &[f64], settings: Settings, repair: bool) -> Result<f64, Error>;
71 fn complex(&self, variant: crate::domain::Variant, args: &[num_complex::Complex64], settings: Settings) -> Result<num_complex::Complex64, Error>;
72 fn arity(&self) -> usize;
73 fn inputs(&self) -> &[crate::domain::Range];
74 fn ordered_kinds(&self, classes: [i32; 2]) -> Option<crate::characters::ByKinds>;
75 fn may_raise(&self, variant: crate::domain::Variant, ranges: &[crate::domain::Range]) -> bool;
76 fn partials(
77 &self, ops: &dyn crate::recipe::Ops<f64, V = crate::facts::Slot, B = crate::facts::Slot, I = crate::facts::Slot, W = crate::facts::Slot>,
78 x: &[crate::facts::Slot], result: crate::facts::Slot, needed: &[bool], output: &mut [Option<crate::facts::Slot>]);
79 fn range(&self, x: &[crate::domain::Range]) -> crate::domain::Range;
80 fn same(&self, x: Range) -> Option<Range>;
81 fn identity(&self) -> Option<f64>;
82 fn kept(&self, constants: &[Option<f64>]) -> Option<usize>;
83 fn reduction(&self, variant: crate::domain::Variant) -> Option<crate::reduce::Reduce>;
84 fn inverse_fold(&self) -> Option<crate::reduce::Reduce>;
85 fn truth_table(&self) -> Option<u8>;
86 fn product(
87 &self, variant: crate::domain::Variant, fold: Option<crate::reduce::Reduce>, boolean_inputs: bool, wanted: Option<DType>, settings: Settings
88 ) -> Option<NativeProduct>;
89 fn map(
91 &self, variant: crate::domain::Variant, inputs: Inputs<'_, '_>, range: std::ops::Range<usize>, settings: Settings, repair: bool, judged: bool,
92 output: &mut Output<'_, '_>) -> Result<(), Error>;
93 fn binary(&self, variant: crate::domain::Variant, settings: Settings, route: Route<'_>, output: &mut Buffer) -> Result<(), Error>;
95}
96
97struct Definition<K, const N: usize> { object: K, inputs: [crate::domain::Range; N] }
99
100impl<K: crate::facts::Formula<N>, const N: usize> Application for Definition<K, N> {
101 fn exact(&self, args: &[num_rational::BigRational]) -> Result<Option<num_rational::BigRational>, Error> {
102 if args.len() != N { return Err(Error::new(Kind::Length, "scalar operand count changed")); }
103 self.object.exact(std::array::from_fn(|i| &args[i]))
104 }
105 fn has_exact(&self) -> bool { K::HAS_EXACT }
106 fn integer(&self, variant: crate::domain::Variant, args: &[i64]) -> Result<i64, Error> {
107 let x: [i64; N] = args.try_into().map_err(|_| Error::new(Kind::Length, "scalar operand count changed"))?;
108 let width = match variant.dtype {
109 DType::Integer(w) => w, DType::Boolean => Width::U8,
110 _ => return Err(Error::new(Kind::Domain, "an integer scalar call needs an integer computing dtype"))
111 };
112 with_width!(width, I => {
113 let ops = crate::recipe::IntegerOps::<I>::new(variant);
114 scalar_result(integer_value(&self.object, variant, &ops, x.map(<I as crate::recipe::Overflowing>::low))).map(Into::into)
115 })
116 }
117 fn real(&self, variant: crate::domain::Variant, args: &[f64], settings: Settings, repair: bool) -> Result<f64, Error> {
118 let x: [f64; N] = args.try_into().map_err(|_| Error::new(Kind::Length, "scalar operand count changed"))?;
119 let ops = settings.ops();
120 match variant.dtype {
121 DType::Float(FloatWidth::F64) => scalar_result(real_value(&self.object, variant, &ops, x, repair)),
122 DType::Float(width) => {
123 let value = scalar_result(real_value(&self.object, variant, &ops, x.map(|x| x as f32), repair))?;
124 Ok(if width == FloatWidth::F16 { half::f16::from_f32(value).to_f64() } else { f64::from(value) })
125 }
126 _ => Err(Error::new(Kind::Domain, "a real scalar call needs a floating computing dtype")),
127 }
128 }
129 fn complex(&self, variant: crate::domain::Variant, args: &[num_complex::Complex64], settings: Settings) -> Result<num_complex::Complex64, Error> {
130 let x: [num_complex::Complex64; N] = args.try_into().map_err(|_| Error::new(Kind::Length, "scalar operand count changed"))?;
131 let ops = settings.ops();
132 match variant.dtype {
133 DType::Complex(crate::buffer::ComplexWidth::F64) => scalar_result(complex_value(&self.object, variant, &ops, x)),
134 DType::Complex(crate::buffer::ComplexWidth::F32) => {
135 let value = scalar_result(complex_value(&self.object, variant, &ops, x.map(|x| num_complex::Complex32::new(x.re as f32, x.im as f32))))?;
136 Ok(num_complex::Complex64::new(f64::from(value.re), f64::from(value.im)))
137 }
138 _ => Err(Error::new(Kind::Domain, "a complex scalar call needs a complex computing dtype")),
139 }
140 }
141 fn arity(&self) -> usize { N }
142 fn inputs(&self) -> &[crate::domain::Range] { &self.inputs }
143 fn may_raise(&self, variant: crate::domain::Variant, ranges: &[crate::domain::Range]) -> bool {
144 if variant.dtype == DType::Boolean && ranges.iter().all(|r| r.within(0.0, 1.0)) { return boolean_raises(&self.object, variant); }
145 if matches!(variant.dtype, DType::Complex(_)) || variant.dtype.is_integral() && !K::INTEGER_FORMULA { return true; }
146 let Ok(args) = ranges.try_into() else { return true; };
147 if matches!(variant.dtype, DType::Float(_)) && self.object.domain(variant.dtype, args).is_some() { return false; }
149 self.object.emit_valid::<f64, _>(variant, &crate::recipe::RangeOps(variant.dtype), args, self.object.range(args))
150 .is_some_and(|(_, valid)| valid != Some(true))
151 }
152 fn partials(
153 &self, ops: &dyn crate::recipe::Ops<f64, V = crate::facts::Slot, B = crate::facts::Slot, I = crate::facts::Slot, W = crate::facts::Slot>,
154 x: &[crate::facts::Slot], result: crate::facts::Slot, needed: &[bool], output: &mut [Option<crate::facts::Slot>]) {
155 let (Ok(x), Ok(needed)) = (x.try_into(), needed.try_into()) else { output.fill(None); return; };
156 output.copy_from_slice(&self.object.emit_partials(ops, x, result, needed));
157 }
158 fn range(&self, x: &[crate::domain::Range]) -> crate::domain::Range { x.try_into().map_or(crate::domain::Range::ANY, |x| self.object.range(x)) }
159 fn same(&self, x: Range) -> Option<Range> { self.object.same(x) }
160 fn ordered_kinds(&self, classes: [i32; 2]) -> Option<crate::characters::ByKinds> {
161 if N != 2 || self.object.character_rule() != Some(crate::characters::Rule::Ordered) { return None; }
162 Some(crate::characters::ByKinds::ordered(classes, self.object.ordered_kinds(classes)?, K::BOOLEAN))
163 }
164 fn identity(&self) -> Option<f64> { self.object.identity().last().copied().flatten() }
165 fn kept(&self, constants: &[Option<f64>]) -> Option<usize> {
166 let identity = self.object.identity();
167 if N != 2 || constants.len() != 2 { return None; }
168 [1, 0].into_iter().find(|&i| constants[i].is_some() && constants[i] == identity[i]).map(|i| 1 - i)
169 }
170 fn reduction(&self, variant: crate::domain::Variant) -> Option<crate::reduce::Reduce> { reduction(&self.object, variant) }
171 fn inverse_fold(&self) -> Option<crate::reduce::Reduce> { self.object.inverse_fold() }
172 fn truth_table(&self) -> Option<u8> { self.object.truth_table() }
173 fn product(
174 &self, variant: crate::domain::Variant, fold: Option<crate::reduce::Reduce>, boolean_inputs: bool, wanted: Option<DType>, settings: Settings
175 ) -> Option<NativeProduct> { product_for::<K, N>(&self.object, variant, fold, boolean_inputs, wanted, settings) }
176 fn map(
177 &self, variant: crate::domain::Variant, inputs: Inputs<'_, '_>, range: std::ops::Range<usize>, settings: Settings, repair: bool, judged: bool,
178 output: &mut Output<'_, '_>) -> Result<(), Error> { map_formula::<K, N>(&self.object, variant, inputs, range, settings, repair, judged, output) }
179 fn binary(&self, variant: crate::domain::Variant, settings: Settings, route: Route<'_>, output: &mut Buffer) -> Result<(), Error> {
180 binary_formula::<K, N>(&self.object, variant, settings, route, output)
181 }
182}
183
184impl Kernel {
185 pub fn resolve<K: crate::facts::Formula<N>, const N: usize>(object: K, variant: crate::domain::Variant, types: [DType; N]) -> Result<Self, Error> {
186 Self::try_resolve(object, variant, types)?.ok_or_else(|| Error::new(Kind::Domain, "function has no numerical body for these arguments"))
187 }
188
189 pub fn circle(code: i64, inverse: bool, real_part: bool, variant: crate::domain::Variant, types: [DType; 1]) -> Result<Option<Self>, Error> {
190 macro_rules! picked {
191 ($normal:expr, $real:expr) => {
192 if real_part && !matches!(variant.dtype, DType::Complex(_)) {
193 Self::try_resolve($real, variant, types)
194 } else {
195 Self::try_resolve($normal, variant, types)
196 }
197 };
198 }
199 let Some(mut kernel) = (match (code, inverse) {
200 (0, _) => picked!(crate::Sqrt1mx2, crate::RealSqrt1mx2), (1, false) => picked!(crate::Sin, crate::Sin),
201 (2, false) => picked!(crate::Cos, crate::Cos), (3, false) => picked!(crate::Tan, crate::Tan),
202 (4, false) => picked!(crate::Sqrt1px2, crate::Sqrt1px2), (5, false) => picked!(crate::Sinh, crate::Sinh),
203 (6, false) => picked!(crate::Cosh, crate::Cosh), (7, false) => picked!(crate::Tanh, crate::Tanh),
204 (1, true) => picked!(crate::Asin, crate::RealAsin), (2, true) => picked!(crate::Acos, crate::RealAcos),
205 (3, true) => picked!(crate::Atan, crate::Atan), (4, true) => picked!(crate::Sqrtx2m1, crate::RealSqrtx2m1),
206 (5, true) => picked!(crate::Asinh, crate::Asinh), (6, true) => picked!(crate::Acosh, crate::RealAcosh),
207 (7, true) => picked!(crate::Atanh, crate::RealAtanh), (8, inverse) => picked!(crate::Sqrtm1mx2(inverse), crate::RealSqrtm1mx2(inverse)),
208 _ => return Ok(None)
209 })?
210 else { return Ok(None); };
211 if real_part && let DType::Complex(width) = kernel.result { kernel.result = DType::Float(width.into()); }
212 Ok(Some(kernel))
213 }
214 pub fn new<K: crate::facts::Formula<N>, const N: usize>(object: K, variant: crate::domain::Variant) -> Self {
215 Self::typed(object, variant, [variant.dtype; N])
216 }
217 fn typed<K: crate::facts::Formula<N>, const N: usize>(object: K, variant: crate::domain::Variant, types: [DType; N]) -> Self {
219 let result = object.result_dtype(variant.dtype);
220 let definition: std::sync::Arc<dyn Application> = std::sync::Arc::new(Definition::<K, N> { object, inputs: types.map(Range::of_dtype) });
221 let raising = definition.may_raise(variant, definition.inputs());
222 let wholes = variant.dtype.is_integral() && types.iter().any(|d| matches!(d, DType::Float(_)));
223 Self { repair: true, raising, result, policy: None, boolean_inputs: types.iter().all(|&d| d == DType::Boolean), wholes, variant, definition }
224 }
225 pub fn try_resolve<K: crate::facts::Formula<N>, const N: usize>(
226 object: K, mut variant: crate::domain::Variant, types: [DType; N]) -> Result<Option<Self>, Error> {
227 use crate::characters::{ByKinds, Policy};
228 let characters = if N == 2 {
229 object.character_policy([types[0], types[1]])?
230 } else if N == 1 && types[0] == DType::Character && object.character_rule() == Some(crate::characters::Rule::Identity) {
231 Some(Policy::Codes { computing: DType::Integer(Width::I32), result: DType::Character })
232 } else {
233 if types.contains(&DType::Character) { return Err(Error::new(Kind::Domain, "function does not take character arguments")); }
234 None
235 };
236 let result = match characters {
237 Some(Policy::Codes { computing, result }) => { variant.dtype = computing; result }
238 Some(Policy::Kinds(kind)) => {
239 let result = match kind { ByKinds::Truth(_) => DType::Boolean, ByKinds::Left => types[0], ByKinds::Right => types[1] };
240 variant.dtype = if matches!(kind, ByKinds::Truth(_)) || result == DType::Character { DType::Integer(Width::I32) } else { result };
241 result
242 }
243 None => {
244 if K::BOOLEAN && variant.dtype.is_integral() {
245 let Some(dtype) = types.into_iter().try_fold(DType::Boolean, DType::common) else { return Ok(None); };
246 variant.dtype = dtype;
247 }
248 let Some(dtype) = object.computing_dtype(variant.dtype) else { return Ok(None); };
249 variant.dtype = dtype;
250 object.result_dtype(variant.dtype)
251 }
252 };
253 let mut kernel = Self::typed(object, variant, types);
254 kernel.result = result;
255 kernel.policy = characters;
256 Ok(Some(kernel))
257 }
258 pub fn of(op: crate::facts::Op, variant: crate::domain::Variant, types: &[DType]) -> Result<Option<Self>, Error> {
260 struct Resolve<'a>(crate::domain::Variant, &'a [DType]);
261 impl crate::facts::Visit for Resolve<'_> {
262 type Out = Result<Option<Kernel>, Error>;
263 fn visit<K: crate::facts::Formula<N>, const N: usize>(self, object: K) -> Self::Out {
264 let Ok(types) = self.1.try_into() else { return Ok(None) };
265 Kernel::try_resolve(object, self.0, types)
266 }
267 }
268 op.visit(types.len(), Resolve(variant, types))
269 }
270 pub fn slope(op: crate::facts::Op, side: usize, variant: crate::domain::Variant, types: &[DType]) -> Result<Option<Self>, Error> {
272 use crate::derivative::Slope;
273 struct Slopes<'a>(usize, crate::domain::Variant, &'a [DType]);
274 impl crate::facts::Visit for Slopes<'_> {
275 type Out = Result<Option<Kernel>, Error>;
276 fn visit<K: crate::facts::Formula<N>, const N: usize>(self, _: K) -> Self::Out { Ok(None) }
277 fn unary<K: crate::facts::Formula<1>>(self, object: K) -> Self::Out {
278 let Ok(types) = self.2.try_into() else { return Ok(None) };
279 Kernel::try_resolve(Slope(object), self.1, types)
280 }
281 fn binary<K: crate::facts::Formula<2>>(self, object: K) -> Self::Out {
282 let Ok(types) = self.2.try_into() else { return Ok(None) };
283 if self.0 == 0 { Kernel::try_resolve(Slope::<K, 0>(object), self.1, types) } else { Kernel::try_resolve(Slope::<K, 1>(object), self.1, types) }
284 }
285 }
286 op.visit(types.len().saturating_sub(1), Slopes(side, variant, types))
287 }
288 pub fn arity(&self) -> usize { self.definition.arity() }
289 pub fn result_dtype(&self) -> DType { self.result }
290 pub fn variant(&self) -> crate::domain::Variant { self.variant }
292 pub fn with_checked(mut self, checked: bool) -> Self {
294 self.variant.checked = checked;
295 self.raising = self.definition.may_raise(self.variant, self.definition.inputs());
296 self
297 }
298 pub fn input_policy(&self) -> Option<crate::characters::Policy> { self.policy }
299 pub fn selected_input(&self) -> Option<usize> {
300 use crate::characters::{ByKinds, Policy};
301 match self.policy { Some(Policy::Kinds(ByKinds::Left)) => Some(0), Some(Policy::Kinds(ByKinds::Right)) => Some(1), _ => None }
302 }
303 pub fn ordered(mut self, classes: &[Option<i32>], types: &[DType]) -> Self {
305 let [Some(left), Some(right)] = classes else { return self; };
306 if left == right { return self; }
307 let Some(policy) = self.definition.ordered_kinds([*left, *right]) else { return self; };
308 use crate::characters::{ByKinds, Policy};
309 self.result = match policy { ByKinds::Truth(_) => DType::Boolean, ByKinds::Left => types[0], ByKinds::Right => types[1] };
310 self.policy = Some(Policy::Kinds(policy));
311 self
312 }
313 pub fn exact(&self, args: &[num_rational::BigRational]) -> Result<Option<num_rational::BigRational>, Error> { self.definition.exact(args) }
314 pub fn has_exact(&self) -> bool { self.definition.has_exact() }
315 pub fn integers(&self, args: &[i64]) -> Result<i64, Error> { crate::error::checked(|| self.definition.integer(self.variant, args)) }
317 pub fn real(&self, args: &[f64], settings: Settings) -> Result<f64, Error> {
318 crate::error::checked(|| self.definition.real(self.variant, args, settings, self.repair))
319 }
320 pub fn complex(&self, args: &[num_complex::Complex64], settings: Settings) -> Result<num_complex::Complex64, Error> {
321 let value = crate::error::checked(|| self.definition.complex(self.variant, args, settings))?;
322 Ok(if matches!(self.result, DType::Complex(_)) { value } else { num_complex::Complex64::new(value.re, 0.0) })
323 }
324 fn kind_result<T: Copy>(&self, x: &[T], truth: impl FnOnce(bool) -> T) -> Option<T> {
325 use crate::characters::{ByKinds, Policy};
326 Some(match self.policy? {
327 Policy::Kinds(ByKinds::Truth(value)) => truth(value), Policy::Kinds(ByKinds::Left) => x[0], Policy::Kinds(ByKinds::Right) => x[1], _ => return None
328 })
329 }
330 pub fn may_raise(&self) -> bool { self.variant.checked && (self.result == DType::Character || self.wholes || self.raising) }
332 pub fn may_raise_on(&self, narrowed: &[Range]) -> bool {
334 self.variant.checked && (self.result == DType::Character || self.wholes || self.definition.may_raise(self.variant, &self.points(narrowed)))
335 }
336 pub fn range_on(&self, narrowed: &[Range], same: bool) -> Range {
338 let points = self.points(narrowed);
339 if same && let [x, y] = points.as_slice() && let Some(range) = self.definition.same(x.meet(*y)) { return self.bound(range); }
340 self.range(&points)
341 }
342 fn points(&self, narrowed: &[Range]) -> Vec<Range> { self.definition.inputs().iter().zip(narrowed).map(|(&range, &n)| range.meet(n)).collect() }
344 pub fn partials(
345 &self, ops: &dyn crate::recipe::Ops<f64, V = crate::facts::Slot, B = crate::facts::Slot, I = crate::facts::Slot, W = crate::facts::Slot>,
346 x: &[crate::facts::Slot], result: crate::facts::Slot, needed: &[bool], output: &mut [Option<crate::facts::Slot>]) {
347 if let Some(side) = self.kind_result(&[Some(0), Some(1)], |_| None) {
348 for (i, (&needed, output)) in needed.iter().zip(output).enumerate() { *output = needed.then(|| ops.number(u8::from(side == Some(i)) as f64)); }
349 return;
350 }
351 self.definition.partials(ops, x, result, needed, output)
352 }
353 pub fn range(&self, x: &[crate::domain::Range]) -> crate::domain::Range {
354 self.bound(self.kind_result(x, |value| Range::of(Some(u8::from(value) as f64))).unwrap_or_else(|| self.definition.range(x)))
355 }
356 fn bound(&self, range: Range) -> Range {
358 if matches!(self.variant.dtype, DType::Complex(_)) { return Range::ANY; }
359 let range = range.at_width(self.result);
360 let bounds = Range::of_dtype(self.result);
361 if self.variant.dtype.is_integral() && !self.variant.fails_on_overflow() && self.result.is_integral()
363 && (self.result == DType::Integer(Width::I64) || !range.within(bounds.lo, bounds.hi)) { bounds }
364 else { range }
365 }
366 pub fn identity(&self) -> Option<f64> { self.definition.identity() }
367 pub fn kept(&self, constants: &[Option<f64>]) -> Option<usize> { self.definition.kept(constants) }
369 pub fn fold(&self) -> Option<crate::reduce::Reduce> { self.definition.reduction(self.variant) }
371 fn fold_variant(&self, seed: &Seed<'_>) -> crate::domain::Variant {
373 let boolean = crate::domain::Variant { dtype: DType::Boolean, ..self.variant };
374 let counts = self.boolean_inputs && matches!(seed, Seed::Identity) && matches!(self.definition.reduction(boolean), Some(crate::reduce::Reduce::Sum));
375 if counts { boolean } else { self.variant }
376 }
377 pub fn inverse_fold(&self) -> Option<crate::reduce::Reduce> { self.definition.inverse_fold() }
379 fn alternated(&self, seed: Seed<'_>, direction: Direction) -> Option<crate::reduce::Reduce> {
381 if self.arity() == 2 && direction == Direction::Right && matches!(seed, Seed::Identity) { self.inverse_fold() } else { None }
382 }
383 pub fn truth_table(&self) -> Option<u8> { self.definition.truth_table() }
384 pub fn total_dtype(&self) -> Option<DType> { self.fold()?.total_dtype(self.variant) }
385 pub fn fold_dtype(&self, input: DType, len: usize, seed: Option<DType>) -> DType {
386 self.total_dtype().unwrap_or_else(|| match (len, seed) { (0, Some(seed)) => seed, (1, None) if input != DType::Boolean => input, _ => self.result })
387 }
388 pub fn scan_dtype(&self, seeded: bool) -> DType {
390 if seeded && self.result == DType::Boolean { return DType::Boolean; }
391 let carry = match self.variant.dtype { DType::Float(FloatWidth::F16) => DType::Float(FloatWidth::F32), dtype => dtype };
392 self.total_dtype().unwrap_or_else(|| carry.common(self.result).unwrap_or(self.result))
393 }
394 pub fn product_dtype(&self, fold: &Kernel, plan: &crate::linalg::ProductPlan) -> DType {
396 self.native_product(self.variant, fold, self.boolean_inputs, None, Settings { tolerance: crate::tolerant::Tolerance::EXACT })
397 .map_or_else(|| fold.fold_dtype(self.result, plan.contracted_len(), None), |p| p.result)
398 }
399 fn native_product(
400 &self, variant: crate::domain::Variant, fold: &Kernel, boolean_inputs: bool, wanted: Option<DType>, settings: Settings) -> Option<NativeProduct> {
401 if matches!(self.policy, Some(crate::characters::Policy::Kinds(_))) { return None; }
402 self.definition.product(variant, fold.fold(), boolean_inputs, wanted, settings)
403 }
404 fn projection(&self, settings: Settings) -> Projection {
406 match self.policy {
407 Some(crate::characters::Policy::Codes { .. }) => Projection::Codes(settings.tolerance),
408 None if self.wholes => Projection::Wholes(settings.tolerance), _ => Projection::AsIs
409 }
410 }
411 fn projected<R>(
413 &self, inputs: &[crate::agreement::Read<'_>], range: Option<std::ops::Range<usize>>, seed: Seed<'_>, settings: Settings,
414 call: impl FnOnce(&[crate::agreement::Read<'_>], Seed<'_>) -> Result<R, Error>) -> Result<R, Error> {
415 crate::error::checked(|| {
416 let projection = self.projection(settings);
417 if projection == Projection::AsIs { return call(inputs, seed); }
418 let mut all = inputs.to_vec();
419 all.extend(seed.read());
420 with_projected(&all, range, projection, |projected| {
421 let seed = match seed {
422 Seed::Initial(_) => Seed::Initial(projected[inputs.len()]), Seed::Tail(_) => Seed::Tail(projected[inputs.len()]),
423 Seed::Identity => Seed::Identity
424 };
425 call(&projected[..inputs.len()], seed)
426 })
427 })
428 }
429 pub fn map(&self, inputs: &[crate::agreement::Read<'_>], range: std::ops::Range<usize>, settings: Settings, output: &mut Buffer) -> Result<(), Error> {
430 output.clear();
431 self.mapping(Inputs::Mapped(inputs), range, settings, MapOutput::Buffer(output))
432 }
433
434 pub fn map_ready(&self, inputs: &[View<'_>], n: usize, settings: Settings, mut output: MapOutput<'_, '_>) -> Result<(), Error> {
436 if inputs.iter().any(|v| v.len() != 1 && v.len() != n) { output.fill(n); return Err(Error::new(Kind::Length, "ready kernel input length changed")); }
437 self.mapping(Inputs::Ready(inputs), 0..n, settings, output)
438 }
439
440 fn mapping(&self, inputs: Inputs<'_, '_>, range: std::ops::Range<usize>, settings: Settings, mut target: MapOutput<'_, '_>) -> Result<(), Error> {
441 use crate::characters::{ByKinds, Policy};
442 if inputs.len() != self.arity() { target.fill(range.len()); return Err(Error::new(Kind::Length, "kernel operand count changed")); }
443 if range.is_empty() { return Ok(()); }
444 let mut output = Output { target, dtype: self.result, checked: self.variant.fails_on_overflow() };
445 crate::error::checked(|| match self.policy {
446 Some(Policy::Kinds(ByKinds::Truth(value))) => map::constant(value, range.len(), &mut output),
447 Some(Policy::Kinds(side)) => map::copied(inputs.selected(usize::from(side == ByKinds::Right)), range, self.variant.checked, &mut output),
448 _ => {
449 let apply = |inputs: Inputs<'_, '_>, output: &mut Output<'_, '_>| {
450 self.definition.map(self.variant, inputs, range.clone(), settings, self.repair, self.may_raise(), output)
451 };
452 match inputs {
453 Inputs::Mapped(reads) => {
454 self.projected(reads, Some(range.clone()), Seed::Identity, settings, |reads, _| apply(Inputs::Mapped(reads), &mut output))
455 }
456 Inputs::Ready(_) if self.projection(settings) == Projection::AsIs => apply(inputs, &mut output),
457 Inputs::Ready(views) => {
458 let projected = match views.iter().map(|&v| projected_view(v, self.projection(settings))).collect::<Result<Vec<_>, _>>() {
459 Ok(projected) => projected, Err(error) => { output.target.fill(range.len()); return Err(error); }
460 };
461 let views: Vec<_> = projected.iter().map(|v| v.view()).collect();
462 apply(Inputs::Ready(&views), &mut output)
463 }
464 }
465 }
466 })
467 }
468 pub fn fold_pieces(
469 &self, dims: [usize; 3], seed: Seed<'_>, direction: Direction, settings: Settings, pieces: &Pieces<'_>, output: &mut Buffer) -> Result<(), Error> {
470 let dtype = output.dtype();
471 let variant = self.fold_variant(&seed);
472 let projection = self.projection(settings);
473 let projected = |range, f: &mut dyn FnMut(usize, Piece<'_>)| {
474 pieces(range, &mut |range, piece| match piece {
475 Piece::Items(view) => {
476 let n = view.len();
477 if let Err(error) = with_projected_view(view, projection, |view| f(range, Piece::Items(view))) {
479 crate::error::record(error);
480 f(range, Piece::Items(View::Integers(crate::buffer::Ints::I64(&vec![0; n]))));
481 }
482 }
483 Piece::Total(total) => f(range, Piece::Total(total)),
484 })
485 };
486 let pieces: &Pieces<'_> = if projection == Projection::AsIs { pieces } else { &projected };
487 self.projected(&[], None, seed, settings, |_, seed| {
488 if let Some(fold) = self.definition.reduction(variant) { return fold.run_pieces(variant, dims, seed, pieces, output); }
489 if let Some(fold) = self.alternated(seed, direction) && let Some(buffer) = fold.inverse_pieces(self.variant, dims, pieces)? {
490 *output = buffer;
491 return Ok(());
492 }
493 self.definition.binary(self.variant, settings, Route::Fold { dims, seed, direction, pieces }, output)
494 })?;
495 finish(output, dtype, self.variant)
496 }
497 pub fn scan(
498 &self, dims: [usize; 3], seed: Seed<'_>, direction: Direction, settings: Settings, input: crate::agreement::Read<'_>, output: &mut Buffer
499 ) -> Result<(), Error> {
500 let dtype = output.dtype();
501 let seeded = seed.read().is_some();
502 self.projected(&[input], None, seed, settings, |inputs, seed| {
503 if let Some(fold) = self.fold() { return fold.run_scan(self.variant, dims, seed, direction, inputs[0], output); }
504 if direction == Direction::Left && self.inverse_fold() == Some(crate::reduce::Reduce::Sum)
505 && crate::reduce::negated_scan(self.variant, dims, seed, inputs[0], output)? { return Ok(()); }
506 self.definition.binary(self.variant, settings, Route::Scan { dims, seed, direction, input: inputs[0] }, output)
507 })?;
508 let checked = self.variant.checked && !(seeded && dtype == DType::Boolean && self.result == DType::Boolean);
510 finish(output, dtype, crate::domain::Variant { checked, ..self.variant })
511 }
512 pub fn adjacent(
514 &self, dims: [usize; 3], seed: Option<crate::agreement::Read<'_>>, flip: bool, settings: Settings, input: crate::agreement::Read<'_>,
515 output: &mut Buffer) -> Result<(), Error> {
516 let dtype = output.dtype();
517 self.projected(&[input], None, seed.map_or(Seed::Identity, Seed::Initial), settings, |inputs, seed| {
518 let seed = seed.read();
519 self.definition.binary(self.variant, settings, Route::Adjacent { dims, seed, flip, input: inputs[0] }, output)
520 })?;
521 finish(output, dtype, self.variant)
522 }
523 pub fn fold_read(
525 &self, shape: &[usize], axis: Option<usize>, seed: Seed<'_>, direction: Direction, settings: Settings, input: crate::agreement::Read<'_>,
526 output: &mut Buffer) -> Result<Option<Vec<crate::gather::Steps>>, Error> {
527 let dtype = output.dtype();
528 let variant = self.fold_variant(&seed);
529 let order = self.projected(&[input], None, seed, settings, |inputs, seed| {
530 let input = inputs[0];
531 if let Some(fold) = self.definition.reduction(variant) { return fold.run_read(variant, shape, axis, seed, input, output); }
532 let flat = [crate::layout::count(shape)];
533 let (shape, axis) = axis.map_or((flat.as_slice(), 0), |axis| (shape, axis));
534 if let Some(fold) = self.alternated(seed, direction) && let Some(buffer) = fold.inverse_read(variant, shape, axis, input)? {
535 *output = buffer;
536 return Ok(None);
537 }
538 let pieces = |range: std::ops::Range<usize>, emit: &mut dyn FnMut(usize, Piece<'_>)| {
539 let (start, mut scratch) = (range.start, Buffer::with_capacity(variant.dtype, 0));
540 emit(start, Piece::Items(input.part_view(range, variant.dtype, variant.checked, &mut scratch)?));
541 Ok(())
542 };
543 self.definition
544 .binary(variant, settings, Route::Fold { dims: crate::layout::Lanes::new(shape, axis)?.dims(), seed, direction, pieces: &pieces }, output)
545 .map(|()| None)
546 })?;
547 finish(output, dtype, self.variant).map(|()| order)
548 }
549}
550
551pub fn choose(
552 selector: (crate::agreement::Read<'_>, DType), choices: &[crate::agreement::Read<'_>], range: std::ops::Range<usize>, dtype: DType, checked: bool,
553 output: &mut Buffer) -> Result<(), Error> {
554 let reads = || std::iter::once(selector.0).chain(choices.iter().copied());
555 let len = reads().map(|read| read.plan.len()).find(|&n| n != 1).unwrap_or(1);
556 if reads().any(|read| read.plan.len() != 1 && read.plan.len() != len) { return Err(Error::new(Kind::Length, "selection operands have different lengths")); }
557 if range.start > range.end || (len != 1 && range.end > len) { return Err(Error::new(Kind::Length, "selection range exceeds its operands")); }
558 if output.dtype() != dtype { *output = Buffer::with_capacity(dtype, range.len()); }
559 with_dtype!(dtype, T => choose_typed::<T>(selector, choices, range, checked, T::vec_mut(output).expect("selection output dtype")))
560}
561
562fn choose_typed<T: crate::buffer::Cast>(
563 selector: (crate::agreement::Read<'_>, DType), choices: &[crate::agreement::Read<'_>], range: std::ops::Range<usize>, checked: bool, output: &mut Vec<T>
564) -> Result<(), Error> {
565 output.clear();
566 let start = range.start;
567 crate::error::checked(|| {
568 crate::parallel::extended(output, range.len(), crate::CHUNK, &|range, slots| {
569 let part = |read: crate::agreement::Read<'_>| if read.plan.len() == 1 { 0..1 } else { start + range.start..start + range.end };
570 let mut storage: Vec<Vec<T>> = choices.iter().map(|_| Vec::new()).collect();
571 let args = choices.iter().zip(&mut storage).map(|(read, scratch)| read.part::<T>(part(*read), checked, scratch)).collect::<Result<Vec<_>, _>>();
572 if selector.1 == DType::Boolean && let Ok([Arg::Many(no), Arg::Many(yes)]) = args.as_deref() && no.len() == range.len() && yes.len() == range.len()
574 {
575 let mut bits = Vec::new();
576 if let Ok(Arg::Many(mask)) = selector.0.part::<bool>(part(selector.0), checked, &mut bits) && mask.len() == range.len() {
577 return unsafe {
578 slots.fill(range.len(), |out| {
579 for (((slot, &m), &n), &y) in out.iter_mut().zip(mask.iter()).zip(no.iter()).zip(yes.iter()) {
580 slot.write(core::hint::select_unpredictable(m, y, n));
581 }
582 true
583 })
584 };
585 }
586 }
587 let mut indices = Buffer::with_capacity(selector.1, 0);
588 let positions = selector.0.part_view(part(selector.0), selector.1, false, &mut indices)
589 .and_then(|view| crate::structure::positions(view, choices.len(), crate::tolerant::Tolerance::EXACT));
590 match positions.and_then(|positions| args.map(|args| (positions, args))) {
591 Ok((positions, args)) => unsafe {
592 slots.fill(range.len(), |out| {
593 for (i, slot) in out.iter_mut().enumerate() {
594 let position = positions[if positions.len() == 1 { 0 } else { i }];
595 let value = match args.get(position) {
596 Some(Arg::One(value)) => *value, Some(Arg::Many(values)) => values[if values.len() == 1 { 0 } else { i }], None => T::FILL
597 };
598 slot.write(value);
599 }
600 true
601 })
602 },
603 Err(error) => {
604 crate::error::record(error);
605 unsafe { slots.fill(range.len(), |out| { for slot in out { slot.write(T::FILL); } false }) }
606 }
607 }
608 });
609 Ok(())
610 })
611}
612
613const I64_LIMIT: f64 = 9_223_372_036_854_775_808.0;
615
616pub fn whole_numbers<F: crate::Float>(t: crate::tolerant::Tolerance, values: &[F]) -> Result<Vec<i64>, Error> {
618 crate::tolerant::wholes(t, values, I64_LIMIT).map(|(numbers, _)| numbers)
619 .ok_or_else(|| Error::new(Kind::Domain, "an integer computation's float argument must be a whole number"))
620}
621
622fn projected_view(view: View<'_>, projection: Projection) -> Result<crate::buffer::Items<'_>, Error> {
624 use crate::buffer::{IntBuf, Items};
625 let view = if matches!(projection, Projection::Codes(_)) { crate::characters::as_codes(view) } else { view };
626 let Some(t) = projection.wholes().filter(|_| matches!(view.dtype(), DType::Float(_))) else { return Ok(Items::Borrowed(view)) };
627 let mut floats = Vec::new();
628 <f64 as crate::buffer::Cast>::extend_cast(view, view.len(), true, &mut floats)?;
629 Ok(Items::Owned(Buffer::Integers(IntBuf::I64(whole_numbers(t, &floats)?))))
630}
631
632fn with_projected_view<R>(view: View<'_>, projection: Projection, f: impl FnOnce(View<'_>) -> R) -> Result<R, Error> {
633 Ok(f(projected_view(view, projection)?.view()))
634}
635
636fn with_projected<R>(
638 inputs: &[crate::agreement::Read<'_>], range: Option<std::ops::Range<usize>>, projection: Projection,
639 call: impl FnOnce(&[crate::agreement::Read<'_>]) -> Result<R, Error>) -> Result<R, Error> {
640 use crate::{agreement::{Binding, Read, ReadPlan}, buffer::{FloatBuf, IntBuf}};
641 let Some(t) = projection.wholes() else { return call(inputs) };
642 let codes = matches!(projection, Projection::Codes(_));
643 let sources: Vec<Vec<_>> = if codes { inputs.iter().map(|r| r.sources.iter().map(|s| s.as_codes()).collect()).collect() } else { Vec::new() };
644 let code_plans = if !codes { Vec::new() } else {
646 inputs.iter().map(|read| {
647 let mut characters = false;
648 read.plan.try_for_each_source(&mut |source, _| {
649 characters |= read.sources.get(source).is_some_and(|binding| binding.view.dtype() == DType::Character);
650 Ok::<_, Error>(())
651 })?;
652 Ok(read.plan.as_codes(characters))
653 }).collect::<Result<Vec<_>, Error>>()?
654 };
655 let mut projected: Vec<Read<'_>> =
656 if codes { sources.iter().zip(&code_plans).map(|(sources, plan)| Read { sources, plan }).collect() } else { inputs.to_vec() };
657 if projected.iter().all(|r| r.sources.iter().all(|s| !matches!(s.view.dtype(), DType::Float(_)))) { return call(&projected); }
658 let converted = projected.iter().map(|read| {
659 if !read.sources.iter().any(|s| matches!(s.view.dtype(), DType::Float(_))) { return Ok(None); }
660 let range = range.clone().unwrap_or(0..read.plan.len());
661 let mut floats = Buffer::with_capacity(DType::Float(FloatWidth::F64), range.len());
662 read.read_into(range.clone(), floats.dtype(), true, &mut floats)?;
663 let Buffer::Floats(FloatBuf::F64(values)) = &floats else { unreachable!("a read into 64-bit floats gives them") };
664 Ok(Some((range.start, Buffer::Integers(IntBuf::I64(whole_numbers(t, values)?)))))
665 }).collect::<Result<Vec<_>, Error>>()?;
666 let bindings = converted.iter().zip(inputs)
667 .map(|(buffer, read)| buffer.as_ref().map(|(start, b)| Binding::window(b.view(), *start, read.plan.len())).transpose()).collect::<Result<Vec<_>, _>>()?;
668 let plans: Vec<_> = inputs.iter().map(|r| ReadPlan::input(0, r.plan.len())).collect();
669 for (i, binding) in bindings.iter().enumerate() {
670 if let Some(binding) = binding { projected[i] = Read { sources: std::slice::from_ref(binding), plan: &plans[i] }; }
671 }
672 call(&projected)
673}
674
675macro_rules! with_binary_body {
676 ($object:expr, $variant:expr, $settings:expr, |$T:ident, $apply:ident, $compare:ident| $body:expr) => {{
677 let (object, variant, settings) = ($object, $variant, $settings);
678 let ops = settings.ops();
679 match variant.dtype {
680 DType::Boolean | DType::Integer(_) => {
681 let width = match variant.dtype { DType::Integer(w) => w, _ => Width::U8 };
682 with_width!(width, $T => {
683 let ops = crate::recipe::IntegerOps::<$T>::new(variant);
684 let $apply = { #[inline(always)] |x: $T, y: $T| integer_value(object, variant, &ops, std::array::from_fn(|i| if i == 0 { x } else { y })) };
685 let $compare = |_: $T, _: &[$T], _: bool, _: &mut Vec<bool>| None;
686 $body
687 })
688 },
689 DType::Float(FloatWidth::F16 | FloatWidth::F32) => {
690 type $T = f32;
691 let $apply = { #[inline(always)] move |x: $T, y: $T| real_value(object, variant, &ops, std::array::from_fn(|i| if i == 0 { x } else { y }), true) };
692 let $compare = comparison_writer(object, &ops);
693 $body
694 },
695 DType::Float(FloatWidth::F64) => {
696 type $T = f64;
697 let $apply = { #[inline(always)] move |x: $T, y: $T| real_value(object, variant, &ops, std::array::from_fn(|i| if i == 0 { x } else { y }), true) };
698 let $compare = comparison_writer(object, &ops);
699 $body
700 },
701 DType::Complex(w) => with_complex_width!(w, $T => {
702 let $apply = { #[inline(always)] move |x: $T, y: $T| complex_value(object, variant, &ops, std::array::from_fn(|i| if i == 0 { x } else { y })) };
703 let $compare = |_: $T, _: &[$T], _: bool, _: &mut Vec<bool>| None;
704 $body
705 }),
706 DType::Character => Err(Error::new(Kind::Domain, "a numerical body needs code-point inputs for characters")),
707 }
708 }};
709}
710
711fn comparison_writer<K: Formula<N>, F: Native + Banded, const N: usize>(
713 object: &K, ops: &NativeOps<{ FUSED }>) -> impl Fn(F, &[F], bool, &mut Vec<bool>) -> Option<bool> + Copy {
714 let bands = object.bands().and_then(|(t, left, right)| Some((crate::facts::run_tolerance::<F, _>(ops, t)?, left, right)));
715 move |carry, items, flipped, out| {
716 let (t, left, right) = bands?;
717 let (x, y) = (Arg::One(carry), Arg::Many(items));
718 let (x, y) = if flipped { (y, x) } else { (x, y) };
719 let (at, n) = (out.len(), items.len());
720 out.reserve(n);
721 let ok = crate::tolerant::against(t, x, y, n, &mut out.spare_capacity_mut()[..n], left, right, F::banded)?;
722 unsafe { out.set_len(at + n) };
724 Some(ok)
725 }
726}
727
728fn paired<T: Copy>(step: impl Fn(T, T) -> (T, Option<Kind>)) -> impl Fn(&[T], &[T], &mut [std::mem::MaybeUninit<T>]) -> Result<(), Kind> {
730 move |xs: &[T], ys: &[T], out: &mut [std::mem::MaybeUninit<T>]| {
731 let mut ok = true;
732 for ((&x, &y), slot) in xs.iter().zip(ys).zip(out) {
733 let (value, failed) = step(x, y);
734 slot.write(value);
735 ok &= failed.is_none();
736 }
737 if ok { Ok(()) } else { xs.iter().zip(ys).find_map(|(&x, &y)| step(x, y).1).map_or(Ok(()), Err) }
738 }
739}
740
741fn chained<T: Copy>(step: impl Fn(T, T) -> (T, Option<Kind>) + Copy) -> impl Fn(Option<T>, &[T], usize, &mut Vec<T>) -> Option<Kind> {
743 move |carry: Option<T>, data: &[T], len: usize, out: &mut Vec<T>| {
744 let fits = move |x, y| { let (t, failed) = step(x, y); (t, failed.is_none()) };
745 if crate::reduce::lanes_scan(data, len, out, move |x| carry.map_or((x, true), |c| fits(c, x)), fits) { return None; }
746 data.chunks(len).find_map(|lane| {
747 let (mut total, mut failure) = carry.map_or((lane[0], None), |c| step(c, lane[0]));
748 for &x in &lane[1..] {
749 if failure.is_some() { break; }
750 (total, failure) = step(total, x);
751 }
752 failure
753 })
754 }
755}
756
757fn folded<T: Copy>(step: impl Fn(T, T) -> (T, Option<Kind>)) -> impl Fn(&[T], T) -> (T, Option<Kind>) {
759 move |items: &[T], last: T| {
760 items.iter().rev().fold((last, None), |(total, failure), &x| {
761 let (t, f) = step(total, x);
762 (t, failure.or(f))
763 })
764 }
765}
766
767pub fn report_maps<'a>(plans: impl IntoIterator<Item = &'a crate::agreement::ReadPlan>, range: std::ops::Range<usize>) {
769 if crate::events::running() && plans.into_iter().any(|plan| plan.contiguous(range.clone()).is_none()) {
770 crate::events::taken(crate::events::Route::MappedKernel, range.len());
771 }
772}
773
774#[inline(always)]
776fn verdict<K: crate::facts::Formula<N>, F: crate::Float, O: crate::recipe::Ops<F, B = bool> + ?Sized, const N: usize>(
777 object: &K, variant: crate::domain::Variant, ops: &O, x: [O::V; N], result: O::V) -> Option<Kind> {
778 object.emit_valid(variant, ops, x, result).and_then(|(kind, valid)| (!valid).then_some(kind))
779}
780
781#[inline(always)]
783fn complex_valid<K: crate::facts::Formula<N>, F: crate::Native, const N: usize>(
784 ops: &crate::recipe::NativeOps<{ crate::FUSED }>, x: [num_complex::Complex<F>; N], result: num_complex::Complex<F>) -> bool {
785 !K::FAILS_NAN || crate::facts::nan_valid(ops, x.into_iter().flat_map(|x| [x.re, x.im]), [result.re, result.im])
786}
787
788#[inline(always)]
789fn real_value<K, F, const N: usize>(
790 object: &K, variant: crate::domain::Variant, ops: &crate::recipe::NativeOps<{ crate::FUSED }>, x: [F; N], repair: bool) -> (F, Option<Kind>) where
791 K: crate::facts::Formula<N>, F: crate::Native {
792 let result = if repair && object.emit_repair(ops, x) == Some(true) { crate::facts::Formula::repair_routine(object).map(|r| r.apply(x[0])) }
793 else { object.real(ops, x) };
794 let Some(result) = result else { return (F::zero(), variant.checked.then_some(Kind::Domain)); };
795 let error = if variant.checked { verdict(object, variant, ops, x, result) } else { None };
796 (result, error)
797}
798
799#[inline(always)]
800fn integer_value<K, I, const N: usize>(object: &K, variant: crate::domain::Variant, ops: &crate::recipe::IntegerOps<I>, x: [I; N]) -> (I, Option<Kind>) where
801 K: crate::facts::Formula<N>, I: crate::recipe::Overflowing {
802 let Some(result) = object.integer(ops, x) else { return (I::FILL, variant.checked.then_some(Kind::Domain)); };
803 let error = if variant.checked { verdict(object, variant, ops, x, result) } else { None };
804 (result, error)
805}
806
807fn boolean_raises<K: crate::facts::Formula<N>, const N: usize>(object: &K, variant: crate::domain::Variant) -> bool {
809 let ops = crate::recipe::IntegerOps::<u8>::new(variant);
810 (0..1usize << N).any(|bits| {
811 let (value, error) = integer_value(object, variant, &ops, std::array::from_fn(|i| ((bits >> i) & 1) as u8));
812 error.is_some() || !K::BOOLEAN && value > 1
813 })
814}
815
816#[inline(always)]
817fn complex_value<K, F, const N: usize>(
818 object: &K, variant: crate::domain::Variant, ops: &crate::recipe::NativeOps<{ crate::FUSED }>, x: [num_complex::Complex<F>; N]
819) -> (num_complex::Complex<F>, Option<Kind>) where K: crate::facts::Formula<N>, F: crate::Native {
820 let Some(result) = object.complex(ops, x) else { return (num_complex::Complex::new(F::zero(), F::zero()), variant.checked.then_some(Kind::Domain)); };
821 let valid = !variant.checked || complex_valid::<K, F, N>(ops, x, result);
822 (result, (!valid).then_some(Kind::Domain))
823}
824
825fn scalar_result<T>((value, error): (T, Option<Kind>)) -> Result<T, Error> {
826 match error { Some(kind) => Err(Error::new(kind, "numerical argument is outside the function's domain")), None => Ok(value) }
827}
828
829fn reduction<K: crate::facts::Formula<N>, const N: usize>(object: &K, variant: crate::domain::Variant) -> Option<crate::reduce::Reduce> {
831 if variant.dtype == DType::Boolean { object.truth().or_else(|| object.fold()) } else { object.fold() }
832}
833
834fn finish(output: &mut Buffer, dtype: DType, variant: crate::domain::Variant) -> Result<(), Error> {
836 if output.dtype() != dtype { *output = Buffer::cast_results(output.view(), dtype, variant.fails_on_overflow())?; }
837 Ok(())
838}
839
840mod at;
841mod binary;
842mod fold;
843mod map;
844mod product;
845pub use {at::*, map::MapOutput, product::*};
846use {binary::*, fold::*, map::{Inputs, Output, map_formula}};