1use std::collections::{HashMap, HashSet};
8use std::sync::Arc;
9
10use crate::array::{Array, Buf, Data, Layout, NearInt};
11use crate::complex::{self as cx, Cx};
12use crate::dtype::DType;
13use crate::error::{Error, ErrorKind, Result, Span};
14use crate::exact::{self, Ext, Rat};
15use crate::fmt::FmtOpts;
16use crate::frontend::{ComplexOrder, EncodeDigits, FloorRule, NearCount, NestedGrade, Rules};
17use crate::par;
18use crate::simd::multiversioned;
19
20pub const RANK_INF: i64 = i64::MAX;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum Agreement {
27 LeadingPrefix,
29 ExactOrScalar,
31}
32
33#[derive(Clone, Copy, Debug, PartialEq)]
43pub struct Tol {
44 pub ct: f64,
46 pub by_smaller: bool,
48 pub floor_rule: FloorRule,
51}
52
53impl Tol {
54 pub const EXACT: Tol = Tol { ct: 0.0, by_smaller: true, floor_rule: FloorRule::Shift };
56 pub const J: Tol =
58 Tol { ct: 5.684_341_886_080_802e-14, by_smaller: true, floor_rule: FloorRule::Shift };
59 pub const APL: Tol = Tol { ct: 1e-13, by_smaller: false, floor_rule: FloorRule::Shift };
61
62 #[inline(always)]
64 pub fn eq(self, a: f64, b: f64) -> bool {
65 if a == b {
66 return true;
67 }
68 let s = if self.by_smaller {
71 a.abs().min(b.abs())
72 } else {
73 a.abs().max(b.abs())
74 };
75 (a - b).abs() < self.ct * s
76 }
77
78 #[inline(always)]
84 pub fn is_j(self) -> bool {
85 self.by_smaller
86 }
87
88 #[inline(always)]
94 pub fn is_zero(self, y: f64) -> bool {
95 self.is_j() && y.abs() < self.ct
96 }
97
98 #[inline(always)]
100 pub fn lt(self, a: f64, b: f64) -> bool {
101 a < b && !self.eq(a, b)
102 }
103
104 #[inline(always)]
106 pub fn le(self, a: f64, b: f64) -> bool {
107 a <= b || self.eq(a, b)
108 }
109
110 #[inline]
114 pub fn eq_cx(self, a: Cx, b: Cx) -> bool {
115 if a == b {
116 return true;
117 }
118 let (ma, mb) = (cx::abs(a), cx::abs(b));
119 let s = if self.by_smaller { ma.min(mb) } else { ma.max(mb) };
120 cx::abs(cx::sub(a, b)) < self.ct * s
121 }
122
123 #[inline(always)]
134 pub fn floor(self, y: f64) -> f64 {
135 if self.is_j() {
136 let c = y.ceil();
137 if self.eq(y, c) { c } else { y.floor() }
138 } else if self.floor_rule == FloorRule::Shift {
139 (y + self.ct).floor()
140 } else {
141 let c = y.ceil();
146 if c - y <= self.ct * y.abs().max(1.0) { c } else { y.floor() }
147 }
148 }
149
150 #[inline(always)]
153 pub fn ceil(self, y: f64) -> f64 {
154 if self.is_j() {
155 let f = y.floor();
156 if self.eq(y, f) { f } else { y.ceil() }
157 } else if self.floor_rule == FloorRule::Shift {
158 (y - self.ct).ceil()
159 } else {
160 let f = y.floor();
161 if y - f <= self.ct * y.abs().max(1.0) { f } else { y.ceil() }
162 }
163 }
164
165 #[inline]
180 pub fn residue(self, x: f64, y: f64) -> f64 {
181 if self.is_j() && y.is_infinite() && x != 0.0 {
187 return f64::NAN;
188 }
189 if x.is_infinite() {
194 return if y == 0.0 || (y > 0.0) == (x > 0.0) { y } else { x };
195 }
196 if x == 0.0 {
197 return y;
198 }
199 if self.is_j() {
200 let p = x * self.floor(y / x);
201 return if self.eq(y, p) { 0.0 } else { y - p };
202 }
203 let q = y / x;
208 let c = q.ceil();
209 let gap = c - q;
210 let k = if gap <= self.ct || gap < self.ct * q.abs().max(c.abs()) { c } else { q.floor() };
211 let r = y - x * k;
212 if r.abs() < self.ct * x.abs() {
213 0.0
214 } else if r != 0.0 && (r < 0.0) != (x < 0.0) {
215 r + x
216 } else {
217 r
218 }
219 }
220
221 #[inline(always)]
231 pub fn mul(self, x: f64, y: f64) -> f64 {
232 if self.is_j() && (x == 0.0 || y == 0.0) && !(x.is_finite() && y.is_finite()) {
233 return 0.0;
234 }
235 x * y
236 }
237
238 #[inline(always)]
245 pub fn made_nan(self, r: f64, x: f64, y: f64) -> bool {
246 self.is_j() && r.is_nan() && !x.is_nan() && !y.is_nan()
247 }
248}
249
250pub(crate) fn j_number(v: f64) -> String {
253 if v.is_nan() {
254 "_.".to_string()
255 } else if v == f64::INFINITY {
256 "_".to_string()
257 } else if v == f64::NEG_INFINITY {
258 "__".to_string()
259 } else {
260 format!("{v}")
261 }
262}
263
264#[derive(Clone, Copy, Debug)]
269pub struct EvalCfg {
270 pub agreement: Agreement,
271 pub fmt: FmtOpts,
272 pub tol: Tol,
275 pub rules: Rules,
278}
279
280impl EvalCfg {
281 pub(crate) fn near(self) -> NearInt {
289 match self.rules.lang {
290 crate::Lang::J => NearInt::J,
291 crate::Lang::Apl => match self.rules.near_count {
292 NearCount::Absolute => NearInt::Apl,
293 NearCount::Tolerant => NearInt::Tolerant(self.rules.tol()),
294 },
295 }
296 }
297
298 pub(crate) fn pure<R>(self, f: impl FnOnce(&mut Ctx<'_>) -> R) -> R {
299 let mut sink = |_: &str| debug_assert!(false, "a pure verb wrote to the output sink");
300 let mut env = Env::new(Vec::new());
301 f(&mut Ctx { cfg: self, out: &mut sink, inp: None, env: &mut env, device: None })
302 }
303}
304
305pub const RECURSION_LIMIT: usize = 64;
314
315pub struct Env {
323 globals: HashMap<String, Array>,
324 frames: Vec<HashMap<String, Array>>,
325 running: Vec<std::sync::Arc<crate::ir::ExplicitDef>>,
328 verbs: HashMap<String, Verb>,
329 args: Vec<Array>,
330}
331
332impl Env {
333 pub fn new(args: Vec<Array>) -> Env {
334 Env {
335 globals: HashMap::new(),
336 frames: Vec::new(),
337 running: Vec::new(),
338 verbs: HashMap::new(),
339 args,
340 }
341 }
342
343 pub fn get(&self, name: &str) -> Option<Array> {
344 if let Some(frame) = self.frames.last() && let Some(v) = frame.get(name) {
345 return Some(v.clone());
346 }
347 if let Some(def) = self.running.last()
353 && !def.enclosing.is_empty()
354 {
355 for i in (0..self.frames.len().saturating_sub(1)).rev() {
356 if def.enclosing.contains(&self.running[i].id)
357 && let Some(v) = self.frames[i].get(name)
358 {
359 return Some(v.clone());
360 }
361 }
362 }
363 self.globals.get(name).cloned()
364 }
365
366 pub fn assign(&mut self, name: String, value: Array, scope: crate::ir::Scope) {
367 if scope == crate::ir::Scope::LocalDefault && self.get(&name).is_some() {
368 return;
369 }
370 let target = match (scope, self.frames.last_mut()) {
371 (crate::ir::Scope::Local | crate::ir::Scope::LocalDefault, Some(frame)) => frame,
372 _ => &mut self.globals,
373 };
374 target.insert(name, value);
375 }
376
377 pub fn define(&mut self, name: String, verb: Verb) {
378 self.verbs.insert(name, verb);
379 }
380
381 pub fn global(&self, name: &str) -> Option<Array> {
385 self.globals.get(name).cloned()
386 }
387
388 pub fn set_global(&mut self, name: String, value: Array) {
389 self.globals.insert(name, value);
390 }
391
392 pub fn unset_global(&mut self, name: &str) {
393 self.globals.remove(name);
394 }
395
396 pub fn undefine(&mut self, name: &str) {
397 self.verbs.remove(name);
398 }
399
400 pub fn verb(&self, name: &str) -> Option<&Verb> {
401 self.verbs.get(name)
402 }
403
404 pub fn arg(&self, i: usize) -> Result<Array> {
405 self.args
406 .get(i)
407 .cloned()
408 .ok_or_else(|| Error::internal("a parameter was read where none is bound"))
409 }
410
411 pub fn enter(
413 &mut self,
414 frame: HashMap<String, Array>,
415 def: std::sync::Arc<crate::ir::ExplicitDef>,
416 span: Span,
417 ) -> Result<()> {
418 if self.frames.len() >= RECURSION_LIMIT {
419 return Err(Error::new(
420 ErrorKind::Domain,
421 format!("explicit definitions called each other more than {RECURSION_LIMIT} deep"),
422 Some(span),
423 )
424 .note("a definition that recurses needs a case that stops"));
425 }
426 self.frames.push(frame);
427 self.running.push(def);
428 Ok(())
429 }
430
431 pub fn leave(&mut self) -> HashMap<String, Array> {
433 self.running.pop();
434 self.frames.pop().unwrap_or_default()
435 }
436
437 pub fn current_def(&self) -> Option<std::sync::Arc<crate::ir::ExplicitDef>> {
439 self.running.last().cloned()
440 }
441}
442
443pub type InputFn<'a> = Option<&'a mut dyn FnMut() -> Option<String>>;
451
452pub fn reborrow_input<'s, 'a: 's>(inp: &'s mut InputFn<'a>) -> InputFn<'s> {
456 match inp {
457 Some(f) => Some(&mut **f),
458 None => None,
459 }
460}
461
462pub struct Ctx<'a> {
464 pub cfg: EvalCfg,
465 pub out: &'a mut dyn FnMut(&str),
468 pub inp: InputFn<'a>,
472 pub env: &'a mut Env,
474 pub device: Option<&'a crate::device::Device>,
477}
478
479const MAX_NESTING: usize = 400;
488
489thread_local! {
490 static NESTING: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
491}
492
493pub(crate) fn check_nesting(depth: usize, span: Span) -> Result<()> {
495 if depth > MAX_NESTING {
496 return Err(Error::new(
497 ErrorKind::Limit,
498 format!("this program nests more than {MAX_NESTING} applications deep"),
499 Some(span),
500 ));
501 }
502 Ok(())
503}
504
505pub(crate) struct Nesting;
507
508impl Nesting {
509 pub(crate) fn enter(span: Span) -> Result<Nesting> {
511 let depth = NESTING.with(|c| {
512 let d = c.get() + 1;
513 c.set(d);
514 d
515 });
516 if depth > MAX_NESTING {
517 NESTING.with(|c| c.set(c.get() - 1));
518 return Err(Error::new(
519 ErrorKind::Limit,
520 format!("this program nests more than {MAX_NESTING} applications deep"),
521 Some(span),
522 ));
523 }
524 Ok(Nesting)
525 }
526}
527
528impl Drop for Nesting {
529 fn drop(&mut self) {
530 NESTING.with(|c| c.set(c.get().saturating_sub(1)));
531 }
532}
533
534impl Ctx<'_> {
535 fn with_tol<R>(&mut self, tol: Tol, f: impl FnOnce(&mut Ctx<'_>) -> R) -> R {
537 let cfg = EvalCfg { tol, ..self.cfg };
538 f(&mut Ctx {
539 cfg,
540 out: &mut *self.out,
541 inp: reborrow_input(&mut self.inp),
542 env: &mut *self.env,
543 device: self.device,
544 })
545 }
546
547 pub(crate) fn read_line(&mut self, span: Span) -> Result<String> {
553 let Some(read) = self.inp.as_deref_mut() else {
554 return Err(Error::new(
555 ErrorKind::Value,
556 "this expression reads input, and this run has no input source attached",
557 Some(span),
558 )
559 .note("attach one with Program::run_io (Rust), input= (Python), or jay_run_io (C)"));
560 };
561 read().ok_or_else(|| {
562 Error::new(ErrorKind::Value, "the input has ended: there is no line to read", Some(span))
563 })
564 }
565}
566
567#[derive(Clone, Copy, Debug, PartialEq, Eq)]
569pub enum ScalarMonad {
570 Conj,
572 Neg,
573 Signum,
574 Recip,
575 Sqrt,
576 Exp,
577 Abs,
578 Floor,
579 Ceil,
580 Not,
582 OneMinus,
584 Inc,
586 Dec,
588 Double,
590 Halve,
592 Square,
594 Ln,
596 Pi,
598 Factorial,
601 Imaginary,
603 Polar,
605}
606
607#[derive(Clone, Copy, Debug, PartialEq, Eq)]
609pub enum ScalarDyad {
610 Add,
611 Sub,
612 Mul,
613 DivJ,
615 DivApl,
617 Min,
618 Max,
619 Pow,
620 Residue,
622 Eq,
623 Ne,
624 Lt,
625 Le,
626 Gt,
627 Ge,
628 Lcm,
630 Gcd,
632 Log,
634 Root,
636 Circle,
640 Binomial,
643 MakeComplex,
645 PolarBy,
647}
648
649#[derive(Clone, Copy, Debug, PartialEq, Eq)]
651pub enum Enclose {
652 Always,
654 ExceptSimpleScalar,
656}
657
658#[derive(Clone, Copy, Debug, PartialEq, Eq)]
660pub enum MonadOp {
661 Scalar(ScalarMonad),
662 ShapeOf,
664 Tally,
666 Ravel,
668 RavelItems,
671 TransposeAxes,
673 Catalogue,
676 AtomicRep,
680 RazeIn,
683 Head,
685 Behead,
687 Tail,
689 Curtail,
691 Reverse,
693 Nub,
695 GradeUp { origin: i64 },
697 GradeDown { origin: i64 },
699 IotaJ,
701 IotaApl { origin: i64 },
703 Echo,
705 ReadStream,
709 TypeCode,
711 Same,
713 Format,
717 DecodeBits,
719 EncodeBits,
723 Itemize,
725 TableOf,
728 Enclose(Enclose),
730 Open,
734 Raze,
736 First,
739 Enlist,
741 Depth {
744 signed: bool,
747 },
748 Indices { origin: i64, boxed_coords: bool },
752 Steps,
754 ToExact,
757 NthPrime,
759 PrimeFactors,
761 MatrixInverse,
763 Roll { origin: i64, fixed: bool, float_at_zero: bool },
768 ComplexParts { polar: bool },
772 SelfClassify,
775 NubSieve,
778 Unicode { pass_chars: bool },
782 Symbols,
786 Sparse,
789 Words,
791 Nest,
794 LevelOf,
797 MapPaths,
800 PolyRoots,
804 PolyDeriv,
807 AnagramIndex,
809 CycleForm,
812 Split,
815 Execute { apl: bool },
820 Dense,
823 PrimeCount,
826 IndicesInverse,
829 NotYet(&'static str),
831 None,
833}
834
835#[derive(Clone, Copy, Debug, PartialEq, Eq)]
837pub enum DyadOp {
838 Scalar(ScalarDyad),
839 Reshape,
842 Take,
844 Drop,
846 Right,
848 Left,
850 Rotate,
852 RotateApl { last: bool },
857 AppendLeading,
859 AppendLast,
861 IndexOf { origin: i64, vector_left: bool },
865 MemberJ,
867 MemberApl,
869 From,
872 Match,
874 NotMatch,
876 GradeSelect { down: bool },
878 Copy,
881 Decode,
884 Encode,
888 CollateGrade { down: bool, origin: i64 },
891 TransposeJ,
894 TransposeApl,
897 DecodeApl,
900 EncodeApl,
903 Laminate,
905 Link,
908 Strand,
910 IntervalIndex { offset: i64, closed: bool },
914 IndexOfLast { origin: i64 },
916 MatrixDivide,
918 PartitionEnclose,
921 PartitionCounts,
925 Squad {
927 origin: i64,
928 leading: bool,
932 },
933 SelectAxis { axis: usize, rank: usize, origin: i64 },
937 Fetch,
939 PolyEval,
942 PolyIntegral,
945 FormatSpec,
948 FormatSpecJ,
953 ParseNumbers,
956 SequentialMachine,
958 TruthTable(u8),
962 ExactForm,
966 Deal { origin: i64, fixed: bool },
969 Boolean(BoolDyad),
972 Less,
974 Union,
976 Intersect,
978 AnagramFrom,
981 Permute,
984 FindSeq,
987 UnicodeForm,
990 PrimeMeta,
994 PrimeExponents,
997 SymbolForm,
1000 Pick { origin: i64 },
1002 Expand,
1005 WriteStream,
1009 SparseForm,
1015 NotYet(&'static str),
1016 None,
1017}
1018
1019#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1021pub enum BoolDyad {
1022 Nor,
1024 Nand,
1026}
1027
1028#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1031pub struct Prim {
1032 pub name: &'static str,
1033 pub monad: MonadOp,
1034 pub dyad: DyadOp,
1035 pub ranks: [i64; 3],
1036}
1037
1038#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1042pub enum WindowKind {
1043 Prefix,
1046 Suffix,
1049 Scan,
1053}
1054
1055#[derive(Clone, Debug, PartialEq, Eq)]
1057pub enum Power {
1058 Times(u64),
1060 Converge,
1062 Each(Vec<u64>),
1065 ConvergeTrace,
1067}
1068
1069const CONVERGE_LIMIT: usize = 1 << 20;
1071
1072pub type MemoCache = Arc<std::sync::Mutex<HashMap<Vec<u64>, Array>>>;
1076
1077#[derive(Clone, Debug)]
1082pub enum Operand {
1083 Func(Box<Verb>),
1084 Value(Box<Array>),
1085}
1086
1087impl Operand {
1088 pub fn name(&self) -> String {
1090 match self {
1091 Operand::Func(v) => v.name(),
1092 Operand::Value(_) => "n".to_string(),
1093 }
1094 }
1095
1096 fn is_value(&self) -> bool {
1097 matches!(self, Operand::Value(_))
1098 }
1099}
1100
1101#[derive(Debug)]
1109pub struct OpDef {
1110 pub readings: [std::result::Result<Verb, String>; 4],
1114}
1115
1116impl OpDef {
1117 pub fn uniform(v: Verb) -> OpDef {
1121 OpDef { readings: [Ok(v.clone()), Ok(v.clone()), Ok(v.clone()), Ok(v)] }
1122 }
1123
1124 pub fn pick(&self, alpha: &Operand, omega: Option<&Operand>) -> Result<&Verb> {
1126 let i = usize::from(alpha.is_value())
1127 | (usize::from(omega.is_some_and(Operand::is_value)) << 1);
1128 self.readings[i].as_ref().map_err(|msg| Error::new(ErrorKind::Parse, msg.clone(), None))
1129 }
1130
1131 fn bodies(&self) -> impl Iterator<Item = &Verb> {
1134 self.readings.iter().filter_map(|r| r.as_ref().ok())
1135 }
1136}
1137
1138#[derive(Clone, Debug)]
1142pub enum Verb {
1143 Prim(Prim),
1144 Rank(Box<Verb>, [i64; 3]),
1146 Reduce(Box<Verb>),
1148 NWise(Box<Verb>),
1153 Windowed(Box<Verb>, WindowKind),
1156 Commute(Box<Verb>),
1158 PowerN(Box<Verb>, Power),
1160 Fork(Box<Verb>, Box<Verb>, Box<Verb>),
1162 NounFork(Array, Box<Verb>, Box<Verb>),
1164 Hook(Box<Verb>, Box<Verb>),
1166 Atop(Box<Verb>, Box<Verb>),
1168 Compose(Box<Verb>, Box<Verb>),
1171 BondLeft(Array, Box<Verb>),
1174 BondRight(Box<Verb>, Array),
1176 Each(Box<Verb>, Enclose),
1179 UnderRavel(Box<Verb>),
1185 Fit(Box<Verb>, f64),
1187 Amend(Array),
1189 AmendVerb(Box<Verb>),
1192 ShiftFill(Array),
1195 Memo(Box<Verb>, MemoCache),
1199 Level { u: Box<Verb>, level: i64, spread: bool },
1203 Characteristics(Box<Verb>),
1206 Before(Box<Verb>, Box<Verb>),
1209 UserDerived { def: Arc<OpDef>, alpha: Operand, omega: Option<Operand> },
1213 KeyPairs(Box<Verb>),
1218 Key(Box<Verb>),
1221 Cut(Box<Verb>, i64),
1223 PowerV(Box<Verb>, Box<Verb>),
1225 PowerUntil(Box<Verb>, Box<Verb>),
1227 AlongAxis(Box<Verb>, usize),
1231 Explicit(Arc<crate::ir::ExplicitDef>),
1235 SelfRef,
1238 Named(String),
1241 WithObverse(Box<Verb>, Box<Verb>),
1244 Agenda(Vec<Verb>, Box<Verb>),
1247 Adverse(Box<Verb>, Box<Verb>),
1251 Hypergeometric { num: Vec<crate::complex::Cx>, den: Vec<crate::complex::Cx> },
1254 Beside(Box<Verb>, Box<Verb>),
1258 Stencil(Box<Verb>, Vec<i64>),
1262 Evoke(Vec<Verb>, i64),
1268 InnerProduct { u: Box<Verb>, v: Box<Verb>, apl: bool },
1275}
1276
1277impl Verb {
1278 pub fn ranks(&self) -> [i64; 3] {
1280 match self {
1281 Verb::Prim(p) => p.ranks,
1282 Verb::Rank(_, r) => *r,
1283 Verb::Windowed(_, WindowKind::Prefix | WindowKind::Suffix) => {
1287 [RANK_INF, 0, RANK_INF]
1288 }
1289 Verb::Each(..) => [0, 0, 0],
1290 Verb::Fit(v, _) => v.ranks(),
1291 Verb::Amend(_)
1294 | Verb::AmendVerb(_)
1295 | Verb::ShiftFill(_)
1296 | Verb::Level { .. }
1297 | Verb::Characteristics(_)
1298 | Verb::UserDerived { .. }
1299 | Verb::KeyPairs(_)
1300 | Verb::Key(_)
1301 | Verb::Cut(..)
1302 | Verb::PowerV(..)
1303 | Verb::PowerUntil(..)
1304 | Verb::AlongAxis(..) => [RANK_INF, RANK_INF, RANK_INF],
1305 Verb::Memo(v, _) => v.ranks(),
1306 Verb::WithObverse(v, _) | Verb::Adverse(v, _) => v.ranks(),
1307 Verb::Beside(..) => [RANK_INF, RANK_INF, RANK_INF],
1308 Verb::Hypergeometric { .. } => [0, 0, 0],
1310 Verb::InnerProduct { .. } => [2, RANK_INF, RANK_INF],
1313 _ => [RANK_INF, RANK_INF, RANK_INF],
1314 }
1315 }
1316
1317 pub fn name(&self) -> String {
1319 match self {
1320 Verb::Prim(p) => p.name.to_string(),
1321 Verb::Rank(v, r) => format!("{}\"{}", v.name(), rank_str(*r)),
1322 Verb::Reduce(v) | Verb::NWise(v) => format!("{}/", v.name()),
1323 Verb::Windowed(v, WindowKind::Suffix) => format!("{}\\.", v.name()),
1324 Verb::Windowed(v, _) => format!("{}\\", v.name()),
1325 Verb::Commute(v) => format!("{}~", v.name()),
1326 Verb::PowerN(v, Power::Converge) => format!("{}^:_", v.name()),
1327 Verb::PowerN(v, Power::Times(n)) => format!("{}^:{n}", v.name()),
1328 Verb::PowerN(v, Power::Each(_)) => format!("{}^:n", v.name()),
1329 Verb::PowerN(v, Power::ConvergeTrace) => format!("{}^:a:", v.name()),
1330 Verb::Fork(f, g, h) => format!("({} {} {})", f.name(), g.name(), h.name()),
1331 Verb::NounFork(_, g, h) => format!("(n {} {})", g.name(), h.name()),
1332 Verb::Hook(f, g) => format!("({} {})", f.name(), g.name()),
1333 Verb::Atop(f, g) => format!("({}@:{})", f.name(), g.name()),
1334 Verb::Compose(f, g) => format!("({}&:{})", f.name(), g.name()),
1335 Verb::BondLeft(_, v) => format!("(n&{})", v.name()),
1336 Verb::BondRight(v, _) => format!("({}&n)", v.name()),
1337 Verb::UnderRavel(v) => format!("({}&.,)", v.name()),
1338 Verb::Each(v, Enclose::Always) => format!("({}&.>)", v.name()),
1339 Verb::Each(v, _) => format!("({}¨)", v.name()),
1340 Verb::Fit(v, n) => format!("{}!.{n}", v.name()),
1341 Verb::Amend(_) => "(m})".to_string(),
1342 Verb::AmendVerb(v) => format!("({}}})", v.name()),
1343 Verb::ShiftFill(_) => "|.!.n".to_string(),
1344 Verb::Characteristics(v) => format!("{} b.", v.name()),
1345 Verb::Before(f, g) => format!("({}⍛{})", f.name(), g.name()),
1346 Verb::KeyPairs(v) => format!("{}⌸", v.name()),
1347 Verb::UserDerived { alpha, omega, .. } => match omega {
1348 Some(g) => format!("({} {{…}} {})", alpha.name(), g.name()),
1349 None => format!("({} {{…}})", alpha.name()),
1350 },
1351 Verb::Memo(v, _) => format!("{} M.", v.name()),
1352 Verb::Level { u, level, spread } => {
1353 format!("{} {} {level}", u.name(), if *spread { "S:" } else { "L:" })
1354 }
1355 Verb::Key(v) => format!("{}/.", v.name()),
1356 Verb::Cut(v, n) => format!("{};.{n}", v.name()),
1357 Verb::PowerV(v, w) => format!("{}^:{}", v.name(), w.name()),
1358 Verb::PowerUntil(v, w) => format!("{}⍣{}", v.name(), w.name()),
1359 Verb::AlongAxis(v, k) => format!("{}[{k}]", v.name()),
1360 Verb::Explicit(d) => d.name.clone(),
1361 Verb::SelfRef => "$:".to_string(),
1362 Verb::Named(n) => n.clone(),
1363 Verb::WithObverse(v, w) => format!("({}:.{})", v.name(), w.name()),
1364 Verb::Adverse(v, w) => format!("({}::{})", v.name(), w.name()),
1365 Verb::Beside(f, g) => format!("({}∘{})", f.name(), g.name()),
1366 Verb::Hypergeometric { num, den } => {
1367 format!("({} H. {})", cx_list(num), cx_list(den))
1368 }
1369 Verb::Agenda(vs, w) => {
1370 let names: Vec<String> = vs.iter().map(Verb::name).collect();
1371 format!("({}@.{})", names.join("`"), w.name())
1372 }
1373 Verb::Evoke(vs, n) => {
1374 let names: Vec<String> = vs.iter().map(Verb::name).collect();
1375 format!("({}`:{n})", names.join("`"))
1376 }
1377 Verb::Stencil(u, w) => {
1378 let sizes: Vec<String> = w.iter().map(i64::to_string).collect();
1379 format!("({}⌺{})", u.name(), sizes.join(" "))
1380 }
1381 Verb::InnerProduct { u, v, .. } => format!("({} . {})", u.name(), v.name()),
1382 }
1383 }
1384
1385 pub fn uses_tolerance(&self) -> bool {
1390 match self {
1391 Verb::Prim(p) => {
1392 matches!(
1393 p.monad,
1394 MonadOp::Scalar(ScalarMonad::Floor)
1395 | MonadOp::Scalar(ScalarMonad::Ceil)
1396 | MonadOp::Nub
1397 | MonadOp::GradeUp { .. }
1398 | MonadOp::GradeDown { .. }
1399 | MonadOp::EncodeBits
1400 ) || matches!(
1401 p.dyad,
1402 DyadOp::Scalar(
1403 ScalarDyad::Eq
1404 | ScalarDyad::Ne
1405 | ScalarDyad::Lt
1406 | ScalarDyad::Le
1407 | ScalarDyad::Gt
1408 | ScalarDyad::Ge
1409 | ScalarDyad::Residue
1410 | ScalarDyad::Gcd
1411 | ScalarDyad::Lcm
1412 ) | DyadOp::Match
1413 | DyadOp::GradeSelect { .. }
1414 | DyadOp::Encode
1415 | DyadOp::EncodeApl
1416 | DyadOp::NotMatch
1417 | DyadOp::MemberJ
1418 | DyadOp::MemberApl
1419 | DyadOp::IndexOf { .. }
1420 | DyadOp::IndexOfLast { .. }
1421 )
1422 }
1423 Verb::Rank(v, _)
1424 | Verb::Reduce(v)
1425 | Verb::NWise(v)
1426 | Verb::Windowed(v, _)
1427 | Verb::Commute(v)
1428 | Verb::PowerN(v, _)
1429 | Verb::BondLeft(_, v)
1430 | Verb::BondRight(v, _)
1431 | Verb::Each(v, _)
1432 | Verb::UnderRavel(v)
1433 | Verb::Fit(v, _)
1434 | Verb::Key(v)
1435 | Verb::Cut(v, _)
1436 | Verb::AlongAxis(v, _) => v.uses_tolerance(),
1437 Verb::PowerV(v, w) | Verb::PowerUntil(v, w) => {
1438 v.uses_tolerance() || w.uses_tolerance()
1439 }
1440 Verb::Amend(_)
1443 | Verb::AmendVerb(_)
1444 | Verb::ShiftFill(_)
1445 | Verb::Characteristics(_)
1446 | Verb::Explicit(_)
1447 | Verb::SelfRef
1448 | Verb::Named(_)
1449 | Verb::Hypergeometric { .. } => false,
1450 Verb::Memo(v, _) | Verb::Level { u: v, .. } => v.uses_tolerance(),
1451 Verb::WithObverse(v, _) => v.uses_tolerance(),
1452 Verb::Adverse(v, w) | Verb::Beside(v, w) | Verb::Before(v, w) => {
1453 v.uses_tolerance() || w.uses_tolerance()
1454 }
1455 Verb::KeyPairs(v) => v.uses_tolerance(),
1456 Verb::UserDerived { def, alpha, omega } => {
1457 let operand = |o: &Operand| match o {
1458 Operand::Func(v) => v.uses_tolerance(),
1459 Operand::Value(_) => false,
1460 };
1461 def.bodies().any(Verb::uses_tolerance)
1462 || operand(alpha)
1463 || omega.as_ref().is_some_and(operand)
1464 }
1465 Verb::Agenda(vs, w) => {
1466 w.uses_tolerance() || vs.iter().any(Verb::uses_tolerance)
1467 }
1468 Verb::Evoke(vs, _) => vs.iter().any(Verb::uses_tolerance),
1469 Verb::Stencil(u, _) => u.uses_tolerance(),
1470 Verb::InnerProduct { u, v, .. } => u.uses_tolerance() || v.uses_tolerance(),
1471 Verb::Fork(f, g, h) => {
1472 f.uses_tolerance() || g.uses_tolerance() || h.uses_tolerance()
1473 }
1474 Verb::NounFork(_, g, h)
1475 | Verb::Hook(g, h)
1476 | Verb::Atop(g, h)
1477 | Verb::Compose(g, h) => g.uses_tolerance() || h.uses_tolerance(),
1478 }
1479 }
1480
1481 pub fn is_pure(&self) -> bool {
1486 match self {
1487 Verb::Prim(p) => {
1490 !matches!(
1491 p.monad,
1492 MonadOp::Echo | MonadOp::Roll { .. } | MonadOp::ReadStream
1493 ) && !matches!(p.dyad, DyadOp::Deal { .. } | DyadOp::WriteStream)
1494 }
1495 Verb::Rank(v, _)
1496 | Verb::Reduce(v)
1497 | Verb::NWise(v)
1498 | Verb::Windowed(v, _)
1499 | Verb::Commute(v)
1500 | Verb::PowerN(v, _) => v.is_pure(),
1501 Verb::Fork(f, g, h) => f.is_pure() && g.is_pure() && h.is_pure(),
1502 Verb::NounFork(_, g, h)
1503 | Verb::Hook(g, h)
1504 | Verb::Atop(g, h)
1505 | Verb::Compose(g, h) => g.is_pure() && h.is_pure(),
1506 Verb::BondLeft(_, v)
1507 | Verb::BondRight(v, _)
1508 | Verb::Each(v, _)
1509 | Verb::UnderRavel(v)
1510 | Verb::Fit(v, _) => v.is_pure(),
1511 Verb::Key(v) | Verb::Cut(v, _) | Verb::AlongAxis(v, _) => v.is_pure(),
1512 Verb::Hypergeometric { .. } => true,
1513 Verb::PowerV(v, w) | Verb::PowerUntil(v, w) => v.is_pure() && w.is_pure(),
1514 Verb::WithObverse(v, _) => v.is_pure(),
1515 Verb::Adverse(v, w) | Verb::Beside(v, w) | Verb::Before(v, w) => {
1516 v.is_pure() && w.is_pure()
1517 }
1518 Verb::KeyPairs(v) => v.is_pure(),
1519 Verb::UserDerived { .. } => false,
1522 Verb::Agenda(vs, w) => w.is_pure() && vs.iter().all(Verb::is_pure),
1523 Verb::Evoke(vs, _) => vs.iter().all(Verb::is_pure),
1524 Verb::Stencil(u, _) => u.is_pure(),
1525 Verb::InnerProduct { u, v, .. } => u.is_pure() && v.is_pure(),
1526 Verb::Amend(_) | Verb::ShiftFill(_) | Verb::Characteristics(_) => true,
1527 Verb::AmendVerb(v) | Verb::Level { u: v, .. } => v.is_pure(),
1528 Verb::Memo(v, _) => v.is_pure(),
1532 Verb::Explicit(_) | Verb::SelfRef | Verb::Named(_) => false,
1537 }
1538 }
1539
1540 fn monad_reads_sparse(&self) -> bool {
1548 let Verb::Prim(p) = self else { return false };
1549 matches!(
1550 p.monad,
1551 MonadOp::Sparse
1552 | MonadOp::ShapeOf
1553 | MonadOp::Tally
1554 | MonadOp::TypeCode
1555 | MonadOp::Format
1556 | MonadOp::Echo
1557 )
1558 }
1559
1560 fn dyad_reads_sparse(&self) -> bool {
1563 matches!(self, Verb::Prim(p) if p.dyad == DyadOp::SparseForm)
1564 }
1565
1566 pub fn monad(&self, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1572 let _depth = Nesting::enter(span)?;
1573 let dense;
1576 let y = if y.is_sparse() && !self.monad_reads_sparse() {
1577 dense = y.densified();
1578 &dense
1579 } else {
1580 y
1581 };
1582 if y.is_row_major() {
1583 return self.monad_rows(y, ctx, span);
1584 }
1585 match self.monad_columns(y, ctx, span) {
1586 Some(r) => r,
1587 None => self.monad_rows(&y.to_row_major(), ctx, span),
1588 }
1589 }
1590
1591 fn monad_rows(&self, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1594 debug_assert!(y.is_row_major());
1595 match self {
1596 Verb::Prim(p) => {
1597 if let MonadOp::Scalar(op) = p.monad {
1600 return scalar_monad(op, y, ctx.cfg, span);
1601 }
1602 if p.monad == MonadOp::Open && is_mixed_simple(y) {
1606 return Ok(y.clone());
1607 }
1608 let frame_rank = y.rank() - effective_rank(p.ranks[0], y.rank());
1609 if frame_rank == 0 {
1610 return monad_op(p, y, ctx, span);
1611 }
1612 let frame = y.shape[..frame_rank].to_vec();
1613 let n: usize = frame.iter().product();
1614 if n == 0 {
1615 let cell = fill_cell(y, frame_rank, self.is_pure());
1616 return Ok(empty_frame(&frame, y.dtype(), cell, ctx, |cell, c| {
1617 monad_op(p, cell, c, span)
1618 }));
1619 }
1620 let cells = each_cell(n, y.count(), self.is_pure(), ctx, |i, c| {
1621 monad_op(p, &y.cell_at(frame_rank, i), c, span)
1622 })?;
1623 assemble(&frame, cells, span)
1624 }
1625 Verb::Rank(v, r) => {
1626 let frame_rank = y.rank() - effective_rank(r[0], y.rank());
1627 if frame_rank == 0 {
1628 return v.monad(y, ctx, span);
1631 }
1632 if let Some(a) = reduce_vector_cells(v, y, frame_rank) {
1635 return Ok(a);
1636 }
1637 let frame = y.shape[..frame_rank].to_vec();
1638 let n: usize = frame.iter().product();
1639 if n == 0 {
1640 let cell = fill_cell(y, frame_rank, self.is_pure());
1641 return Ok(empty_frame(&frame, y.dtype(), cell, ctx, |cell, c| {
1642 v.monad(cell, c, span)
1643 }));
1644 }
1645 let cells = each_cell(n, y.count(), self.is_pure(), ctx, |i, c| {
1646 v.monad(&y.cell_at(frame_rank, i), c, span)
1647 })?;
1648 assemble(&frame, cells, span)
1649 }
1650 Verb::Reduce(v) | Verb::NWise(v) => reduce(v, y, ctx, span),
1651 Verb::Windowed(v, kind) => {
1652 runs(v, y, *kind == WindowKind::Suffix, ctx, span)
1653 }
1654 Verb::Commute(v) => v.dyad(y, y, ctx, span),
1655 Verb::PowerN(v, p) => power(v, p.clone(), None, y, ctx, span),
1656 Verb::Fork(f, g, h) => {
1657 let l = f.monad(y, ctx, span)?;
1658 let r = h.monad(y, ctx, span)?;
1659 g.dyad(&l, &r, ctx, span)
1660 }
1661 Verb::NounFork(n, g, h) => {
1662 let r = h.monad(y, ctx, span)?;
1663 g.dyad(n, &r, ctx, span)
1664 }
1665 Verb::Hook(f, g) => {
1666 let r = g.monad(y, ctx, span)?;
1667 f.dyad(y, &r, ctx, span)
1668 }
1669 Verb::Atop(f, g) | Verb::Compose(f, g) => {
1670 let r = g.monad(y, ctx, span)?;
1671 f.monad(&r, ctx, span)
1672 }
1673 Verb::BondLeft(m, v) => v.dyad(m, y, ctx, span),
1674 Verb::BondRight(v, n) => v.dyad(y, n, ctx, span),
1675 Verb::Each(u, rule) => {
1676 let n = y.count();
1677 let cells = each_cell(n, n, self.is_pure(), ctx, |i, c| {
1678 let opened = open_cell(&atom(y, i));
1679 Ok(enclose(&u.monad(&opened, c, span)?, *rule))
1680 })?;
1681 assemble(&y.shape, cells, span)
1682 }
1683 Verb::UnderRavel(u) => {
1687 let flat = Array::new(vec![y.count()], y.data.clone());
1688 let r = u.monad(&flat, ctx, span)?;
1689 let shape = Array::from_i64(y.shape.iter().map(|&n| n as i64).collect());
1690 reshape(&shape, &r, false, false, ctx.cfg.near(), span)
1691 }
1692 Verb::Fit(v, n) => {
1693 let tol = Tol { ct: *n, ..ctx.cfg.tol };
1694 ctx.with_tol(tol, |c| v.monad(y, c, span))
1695 }
1696 Verb::Amend(m) => {
1698 if m.rank() != 0 || y.rank() > 1 {
1699 return Err(Error::new(
1700 ErrorKind::Rank,
1701 "selecting with m} takes one index into a list",
1702 Some(span),
1703 ));
1704 }
1705 from_index(m, y, ctx.cfg.near(), span)
1706 }
1707 Verb::AmendVerb(u) => {
1709 let m = u.monad(y, ctx, span)?;
1710 Verb::Amend(m).monad(y, ctx, span)
1711 }
1712 Verb::ShiftFill(fill) => {
1715 shift_fill(&Array::scalar_i64(-1), y, fill, ctx.cfg.near(), span)
1716 }
1717 Verb::Memo(u, cache) => memoised(u, cache, None, y, ctx, span),
1718 Verb::Characteristics(u) => characteristics(u, y, span),
1719 Verb::Before(f, g) => {
1720 let l = f.monad(y, ctx, span)?;
1721 g.dyad(&l, y, ctx, span)
1722 }
1723 Verb::KeyPairs(u) => key_pairs(u, y, None, ctx, span),
1724 Verb::UserDerived { def, alpha, omega } => {
1725 let body = def.pick(alpha, omega.as_ref())?.clone();
1726 with_operands(alpha, omega.as_ref(), ctx, |c| body.monad(y, c, span))
1727 }
1728 Verb::Level { u, level, spread } => {
1729 at_level(u, *level, *spread, y, ctx, span)
1730 }
1731 Verb::Key(u) => oblique(u, y, ctx, span),
1732 Verb::Cut(u, n) => cut(u, None, y, *n, ctx, span),
1733 Verb::PowerV(u, v) => power_v(u, v, None, y, ctx, span),
1734 Verb::PowerUntil(u, v) => power_until(u, v, y, ctx, span),
1735 Verb::AlongAxis(u, k) => along_axis(u, None, y, *k, ctx, span),
1736 Verb::Explicit(d) => crate::ir::call_explicit(d, None, y, ctx, span),
1737 Verb::SelfRef => {
1738 let d = self_ref(ctx, span)?;
1739 crate::ir::call_explicit(&d, None, y, ctx, span)
1740 }
1741 Verb::Named(n) => named_verb(ctx, n, span)?.monad(y, ctx, span),
1742 Verb::WithObverse(v, _) => v.monad(y, ctx, span),
1743 Verb::Adverse(v, w) => match v.monad(y, ctx, span) {
1744 Err(e) if e.kind != ErrorKind::NotYet => w.monad(y, ctx, span),
1745 other => other,
1746 },
1747 Verb::Beside(f, g) => {
1748 let r = g.monad(y, ctx, span)?;
1749 f.monad(&r, ctx, span)
1750 }
1751 Verb::Hypergeometric { num, den } => hypergeometric(num, den, y, span),
1752 Verb::Agenda(vs, w) => {
1753 agenda_pick(vs, w, None, y, ctx, span)?.monad(y, ctx, span)
1754 }
1755 Verb::Evoke(vs, n) => evoke(vs, *n, None, y, ctx, span),
1756 Verb::Stencil(u, w) => stencil(u, w, y, ctx, span),
1757 Verb::InnerProduct { u, v, apl } => determinant(u, v, *apl, y, ctx, span),
1758 }
1759 }
1760
1761 fn monad_columns(&self, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Option<Result<Array>> {
1770 debug_assert!(!y.is_row_major());
1771 match self {
1772 Verb::Prim(p) => match p.monad {
1773 MonadOp::Scalar(op) => Some(scalar_monad(op, y, ctx.cfg, span)),
1776 MonadOp::ShapeOf | MonadOp::Tally => Some(monad_op(p, y, ctx, span)),
1778 MonadOp::TransposeAxes => Some(Ok(transpose_axes(y))),
1782 _ => None,
1783 },
1784 Verb::Reduce(v) | Verb::NWise(v) => reduce_columns(v, y).map(Ok),
1787 Verb::Rank(v, r) => {
1790 if y.rank() != effective_rank(r[0], y.rank()) + 1 {
1791 return None;
1792 }
1793 reduce_rows_columns(v, y).map(Ok)
1794 }
1795 _ => None,
1796 }
1797 }
1798
1799 pub fn dyad(&self, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1805 let _depth = Nesting::enter(span)?;
1806 let (dense_x, dense_y);
1809 let x = if x.is_sparse() {
1810 dense_x = x.densified();
1811 &dense_x
1812 } else {
1813 x
1814 };
1815 let y = if y.is_sparse() && !self.dyad_reads_sparse() {
1816 dense_y = y.densified();
1817 &dense_y
1818 } else {
1819 y
1820 };
1821 if x.is_row_major() && y.is_row_major() {
1822 return self.dyad_rows(x, y, ctx, span);
1823 }
1824 if let Some(layout) = self.elementwise_layout(x, y) {
1825 return Ok(self.dyad_rows(x, y, ctx, span)?.with_layout(layout));
1826 }
1827 self.dyad_rows(&x.to_row_major(), &y.to_row_major(), ctx, span)
1828 }
1829
1830 fn elementwise_layout(&self, x: &Array, y: &Array) -> Option<Layout> {
1835 let Verb::Prim(p) = self else { return None };
1836 if !matches!(p.dyad, DyadOp::Scalar(_)) {
1837 return None;
1838 }
1839 if x.rank() == 0 {
1840 return Some(y.layout());
1841 }
1842 if y.rank() == 0 {
1843 return Some(x.layout());
1844 }
1845 (x.shape == y.shape && x.layout() == y.layout()).then(|| x.layout())
1846 }
1847
1848 fn dyad_rows(&self, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1852 match self {
1853 Verb::Prim(_) | Verb::Rank(_, _) | Verb::Each(..) => {
1854 self.dyad_ranked(x, y, ctx, span)
1855 }
1856 Verb::Windowed(_, WindowKind::Prefix | WindowKind::Suffix) => {
1859 self.dyad_ranked(x, y, ctx, span)
1860 }
1861 Verb::Windowed(_, WindowKind::Scan) => {
1862 Err(Error::not_yet("dyadic scan (x f\\ y)", span))
1863 }
1864 Verb::Commute(v) => v.dyad(y, x, ctx, span),
1865 Verb::PowerN(v, p) => power(v, p.clone(), Some(x), y, ctx, span),
1866 Verb::Reduce(v) => table(v, x, y, ctx, span),
1868 Verb::NWise(v) => nwise(v, x, y, ctx, span),
1870 Verb::Fork(f, g, h) => {
1871 let l = f.dyad(x, y, ctx, span)?;
1872 let r = h.dyad(x, y, ctx, span)?;
1873 g.dyad(&l, &r, ctx, span)
1874 }
1875 Verb::NounFork(n, g, h) => {
1876 let r = h.dyad(x, y, ctx, span)?;
1877 g.dyad(n, &r, ctx, span)
1878 }
1879 Verb::Hook(f, g) => {
1880 let r = g.monad(y, ctx, span)?;
1881 f.dyad(x, &r, ctx, span)
1882 }
1883 Verb::Atop(f, g) => {
1884 let r = g.dyad(x, y, ctx, span)?;
1885 f.monad(&r, ctx, span)
1886 }
1887 Verb::Compose(f, g) => {
1888 let l = g.monad(x, ctx, span)?;
1889 let r = g.monad(y, ctx, span)?;
1890 f.dyad(&l, &r, ctx, span)
1891 }
1892 Verb::Fit(v, n) => {
1893 let tol = Tol { ct: *n, ..ctx.cfg.tol };
1894 ctx.with_tol(tol, |c| v.dyad(x, y, c, span))
1895 }
1896 Verb::Amend(m) => amend(m, x, y, ctx.cfg.near(), span),
1897 Verb::AmendVerb(u) => {
1899 let m = u.dyad(x, y, ctx, span)?;
1900 amend(&m, x, y, ctx.cfg.near(), span)
1901 }
1902 Verb::ShiftFill(fill) => shift_fill(x, y, fill, ctx.cfg.near(), span),
1903 Verb::Memo(u, cache) => memoised(u, cache, Some(x), y, ctx, span),
1904 Verb::Characteristics(_) => {
1905 Err(Error::domain("u b. has no dyadic meaning", span))
1906 }
1907 Verb::Before(f, g) => {
1908 let l = f.monad(x, ctx, span)?;
1909 g.dyad(&l, y, ctx, span)
1910 }
1911 Verb::KeyPairs(u) => key_pairs(u, x, Some(y), ctx, span),
1912 Verb::UserDerived { def, alpha, omega } => {
1913 let body = def.pick(alpha, omega.as_ref())?.clone();
1914 with_operands(alpha, omega.as_ref(), ctx, |c| body.dyad(x, y, c, span))
1915 }
1916 Verb::Level { u, level, spread } => {
1917 at_level_dyad(u, *level, *spread, x, y, ctx, span)
1918 }
1919 Verb::Key(u) => key(u, x, y, ctx, span),
1920 Verb::Cut(u, n) => cut(u, Some(x), y, *n, ctx, span),
1921 Verb::PowerV(u, v) => power_v(u, v, Some(x), y, ctx, span),
1922 Verb::PowerUntil(..) => {
1923 Err(Error::not_yet("dyadic power with a function operand (x f⍣g y)", span))
1924 }
1925 Verb::AlongAxis(u, k) => along_axis(u, Some(x), y, *k, ctx, span),
1926 Verb::Explicit(d) => crate::ir::call_explicit(d, Some(x), y, ctx, span),
1927 Verb::SelfRef => {
1928 let d = self_ref(ctx, span)?;
1929 crate::ir::call_explicit(&d, Some(x), y, ctx, span)
1930 }
1931 Verb::Named(n) => named_verb(ctx, n, span)?.dyad(x, y, ctx, span),
1932 Verb::WithObverse(v, _) => v.dyad(x, y, ctx, span),
1933 Verb::Adverse(v, w) => match v.dyad(x, y, ctx, span) {
1934 Err(e) if e.kind != ErrorKind::NotYet => w.dyad(x, y, ctx, span),
1935 other => other,
1936 },
1937 Verb::Beside(f, g) => {
1938 let r = g.monad(y, ctx, span)?;
1939 f.dyad(x, &r, ctx, span)
1940 }
1941 Verb::Hypergeometric { .. } => {
1942 Err(Error::domain("m H. n has no dyadic meaning", span))
1943 }
1944 Verb::Agenda(vs, w) => {
1945 agenda_pick(vs, w, Some(x), y, ctx, span)?.dyad(x, y, ctx, span)
1946 }
1947 Verb::Evoke(vs, n) => evoke(vs, *n, Some(x), y, ctx, span),
1948 Verb::InnerProduct { u, v, apl } => inner_product(u, v, *apl, x, y, ctx, span),
1949 Verb::Stencil(..) => {
1950 Err(Error::domain("f⌺w has no dyadic meaning", span))
1951 }
1952 Verb::BondLeft(..) | Verb::BondRight(..) | Verb::UnderRavel(_) => {
1954 Err(Error::domain(format!("{} has no dyadic meaning", self.name()), span))
1955 }
1956 }
1957 }
1958
1959 fn dyad_ranked(&self, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1961 let ranks = self.ranks();
1962 let er_l = effective_rank(ranks[1], x.rank());
1963 let er_r = effective_rank(ranks[2], y.rank());
1964 if er_l == 0 && er_r == 0 {
1965 if let Some(op) = self.scalar_dyad_op() {
1968 return scalar_dyad(op, x, y, ctx.cfg, span);
1969 }
1970 }
1971 let fxl = x.rank() - er_l;
1972 let fyl = y.rank() - er_r;
1973 let p = agree(&x.shape[..fxl], &y.shape[..fyl], &x.shape, &y.shape, ctx.cfg.agreement, span)?;
1974 if p.frame.is_empty() {
1975 return self.dyad_cell(x, y, ctx, span);
1976 }
1977 if p.n == 0 {
1978 let right = fill_cell(y, fyl, self.is_pure());
1979 let cell = fill_cell(x, fxl, self.is_pure()).filter(|_| right.is_some());
1980 return Ok(empty_frame(&p.frame, y.dtype(), cell, ctx, |left, c| {
1981 let right = right.as_ref().expect("a left fill cell comes with a right one");
1982 self.dyad_cell(left, right, c, span)
1983 }));
1984 }
1985 let work = x.count().max(y.count());
1986 let cells = each_cell(p.n, work, self.is_pure(), ctx, |i, c| {
1987 let xc = x.cell_at(fxl, i / p.x_div);
1988 let yc = y.cell_at(fyl, i / p.y_div);
1989 self.dyad_cell(&xc, &yc, c, span)
1990 })?;
1991 assemble(&p.frame, cells, span)
1992 }
1993
1994 fn dyad_cell(&self, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1996 match self {
1997 Verb::Prim(p) if p.dyad == DyadOp::WriteStream => {
2000 stream_number(y, 2, "1!:2 writes", span)?;
2001 (ctx.out)(&format!("{}\n", crate::fmt::format_array(x, &ctx.cfg.fmt)));
2002 Ok(x.clone())
2003 }
2004 Verb::Prim(p) => dyad_op(p, x, y, ctx.cfg, span),
2005 Verb::Rank(v, _) => v.dyad(x, y, ctx, span),
2006 Verb::Windowed(v, WindowKind::Suffix) => outfix(v, x, y, ctx, span),
2008 Verb::Windowed(v, _) => infix(v, x, y, ctx, span),
2009 Verb::Each(u, rule) => {
2010 let r = u.dyad(&open_cell(x), &open_cell(y), ctx, span)?;
2011 Ok(enclose(&r, *rule))
2012 }
2013 _ => Err(Error::internal("dyad_cell on a verb without cell ranks")),
2014 }
2015 }
2016
2017 fn scalar_dyad_op(&self) -> Option<ScalarDyad> {
2020 match self {
2021 Verb::Prim(p) => match p.dyad {
2022 DyadOp::Scalar(op) => Some(op),
2023 _ => None,
2024 },
2025 Verb::Rank(v, _) => v.scalar_dyad_op(),
2026 _ => None,
2027 }
2028 }
2029}
2030
2031pub fn effective_rank(r: i64, arg_rank: usize) -> usize {
2034 if r >= 0 {
2035 (r as usize).min(arg_rank)
2036 } else {
2037 arg_rank.saturating_sub(r.unsigned_abs() as usize)
2038 }
2039}
2040
2041fn self_ref(ctx: &Ctx<'_>, span: Span) -> Result<Arc<crate::ir::ExplicitDef>> {
2051 ctx.env.current_def().ok_or_else(|| {
2052 Error::new(
2053 ErrorKind::Value,
2054 "self-reference outside an explicit definition",
2055 Some(span),
2056 )
2057 })
2058}
2059
2060fn named_verb(ctx: &Ctx<'_>, name: &str, span: Span) -> Result<Verb> {
2062 ctx.env.verb(name).cloned().ok_or_else(|| {
2063 Error::new(ErrorKind::Value, format!("undefined verb: {name}"), Some(span))
2064 })
2065}
2066
2067fn each_cell<F>(
2068 n: usize,
2069 work: usize,
2070 pure: bool,
2071 ctx: &mut Ctx<'_>,
2072 f: F,
2073) -> Result<Vec<Array>>
2074where
2075 F: Fn(usize, &mut Ctx<'_>) -> Result<Array> + Sync + Send,
2076{
2077 if pure && n > 1 && par::worth_it(work) {
2078 let cfg = ctx.cfg;
2079 return par::map_indexed(n, |i| cfg.pure(|c| f(i, c))).into_iter().collect();
2080 }
2081 (0..n).map(|i| f(i, ctx)).collect()
2082}
2083
2084fn one_rank(r: i64) -> String {
2087 if r == RANK_INF { "_".to_string() } else { r.to_string() }
2088}
2089
2090fn rank_str(r: [i64; 3]) -> String {
2093 if r[0] == r[1] && r[1] == r[2] {
2094 one_rank(r[0])
2095 } else {
2096 format!("{} {} {}", one_rank(r[0]), one_rank(r[1]), one_rank(r[2]))
2097 }
2098}
2099
2100fn show_shape(shape: &[usize]) -> String {
2102 if shape.is_empty() {
2103 return "(scalar)".to_string();
2104 }
2105 shape.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(" ")
2106}
2107
2108fn strides(shape: &[usize]) -> Vec<usize> {
2112 let mut s = vec![1usize; shape.len()];
2113 for k in (0..shape.len().saturating_sub(1)).rev() {
2114 s[k] = s[k + 1] * shape[k + 1];
2115 }
2116 s
2117}
2118
2119fn odometer(coord: &mut [usize], shape: &[usize]) {
2121 for k in (0..coord.len()).rev() {
2122 coord[k] += 1;
2123 if coord[k] < shape[k] {
2124 return;
2125 }
2126 coord[k] = 0;
2127 }
2128}
2129
2130fn push_elem(dst: &mut Data, src: &Data, i: usize) {
2132 match (dst, src) {
2133 (Data::Bool(a), Data::Bool(b)) => a.push(b[i]),
2134 (Data::I64(a), Data::I64(b)) => a.push(b[i]),
2135 (Data::Ext(a), Data::Ext(b)) => a.push(b[i].clone()),
2136 (Data::Rat(a), Data::Rat(b)) => a.push(b[i].clone()),
2137 (Data::F64(a), Data::F64(b)) => a.push(b[i]),
2138 (Data::Complex(a), Data::Complex(b)) => a.push(b[i]),
2139 (Data::Char(a), Data::Char(b)) => a.push(b[i]),
2140 (Data::Symbol(a), Data::Symbol(b)) => a.push(b[i]),
2141 (Data::Box(a), Data::Box(b)) => a.push(b[i].clone()),
2142 _ => debug_assert!(false, "push_elem across dtypes"),
2143 }
2144}
2145
2146fn fill_data(dtype: DType, n: usize) -> Data {
2148 let mut d = Data::empty(dtype);
2149 for _ in 0..n {
2150 d.push_fill();
2151 }
2152 d
2153}
2154
2155const FILL_CELL_LIMIT: usize = 1 << 20;
2157
2158fn fill_cell(y: &Array, frame_rank: usize, pure: bool) -> Option<Array> {
2169 if !pure {
2170 return None;
2171 }
2172 if y.shape[..frame_rank].iter().all(|&d| d != 0) {
2173 return Some(y.cell_at(frame_rank, 0));
2174 }
2175 let shape = y.shape[frame_rank..].to_vec();
2176 let n: usize = shape.iter().product();
2177 if n > FILL_CELL_LIMIT {
2178 return None;
2179 }
2180 let fill = y.proto().cloned();
2183 let mut data = Data::empty(y.dtype());
2184 for _ in 0..n {
2185 push_gap(&mut data, &fill);
2186 }
2187 Some(Array::new(shape, data))
2188}
2189
2190fn empty_frame(
2200 frame: &[usize],
2201 dtype: DType,
2202 cell: Option<Array>,
2203 ctx: &mut Ctx<'_>,
2204 run: impl FnOnce(&Array, &mut Ctx<'_>) -> Result<Array>,
2205) -> Array {
2206 let mut shape = frame.to_vec();
2207 if let Some(cell) = cell
2208 && let Ok(answer) = run(&cell, ctx)
2209 {
2210 shape.extend_from_slice(&answer.shape);
2211 return Array::new(shape, Data::empty(answer.dtype()));
2212 }
2213 Array::new(shape, Data::empty(dtype))
2214}
2215
2216struct Pairing {
2221 frame: Vec<usize>,
2222 n: usize,
2223 x_div: usize,
2224 y_div: usize,
2225}
2226
2227fn frame_mismatch(
2228 xs: &[usize],
2229 ys: &[usize],
2230 fx: &[usize],
2231 fy: &[usize],
2232 axis: usize,
2233 span: Span,
2234) -> Error {
2235 let kind = if fx.len() == 1 && fy.len() == 1 { ErrorKind::Length } else { ErrorKind::Shape };
2238 let note = if axis < fx.len() && axis < fy.len() {
2239 format!("frames first differ at axis {axis}: {} vs {}", fx[axis], fy[axis])
2240 } else {
2241 format!(
2242 "frames have different numbers of axes: {} vs {}, diverging at axis {axis}",
2243 fx.len(),
2244 fy.len()
2245 )
2246 };
2247 Error::new(
2248 kind,
2249 format!(
2250 "arguments do not agree: left shape {}, right shape {}",
2251 show_shape(xs),
2252 show_shape(ys)
2253 ),
2254 Some(span),
2255 )
2256 .note(note)
2257}
2258
2259fn agree(
2262 fx: &[usize],
2263 fy: &[usize],
2264 xs: &[usize],
2265 ys: &[usize],
2266 mode: Agreement,
2267 span: Span,
2268) -> Result<Pairing> {
2269 let common = fx.len().min(fy.len());
2270 match mode {
2271 Agreement::LeadingPrefix => {
2272 for i in 0..common {
2273 if fx[i] != fy[i] {
2274 return Err(frame_mismatch(xs, ys, fx, fy, i, span));
2275 }
2276 }
2277 let (long, short) = if fx.len() >= fy.len() { (fx, fy) } else { (fy, fx) };
2278 let n: usize = long.iter().product();
2279 let surplus: usize = long[short.len()..].iter().product();
2280 let (x_div, y_div) =
2281 if fx.len() >= fy.len() { (1, surplus.max(1)) } else { (surplus.max(1), 1) };
2282 Ok(Pairing { frame: long.to_vec(), n, x_div, y_div })
2283 }
2284 Agreement::ExactOrScalar => {
2285 if fx == fy {
2286 let n: usize = fx.iter().product();
2287 return Ok(Pairing { frame: fx.to_vec(), n, x_div: 1, y_div: 1 });
2288 }
2289 let one = |f: &[usize]| f.iter().product::<usize>() == 1;
2296 if fx.is_empty() || (one(fx) && !fy.is_empty()) {
2297 let n: usize = fy.iter().product();
2298 return Ok(Pairing { frame: fy.to_vec(), n, x_div: n.max(1), y_div: 1 });
2299 }
2300 if fy.is_empty() || one(fy) {
2301 let n: usize = fx.iter().product();
2302 return Ok(Pairing { frame: fx.to_vec(), n, x_div: 1, y_div: n.max(1) });
2303 }
2304 let axis = (0..common).find(|&i| fx[i] != fy[i]).unwrap_or(common);
2305 Err(frame_mismatch(xs, ys, fx, fy, axis, span))
2306 }
2307 }
2308}
2309
2310fn assemble_items(frame: &[usize], mut cells: Vec<Array>, span: Span) -> Result<Array> {
2319 let boxes = cells.iter().filter(|c| c.dtype() == DType::Box).count();
2320 if boxes > 0 && boxes < cells.len() {
2321 for c in &mut cells {
2322 if c.dtype() != DType::Box {
2323 *c = boxed_elements(c);
2324 }
2325 }
2326 }
2327 assemble(frame, cells, span)
2328}
2329
2330fn boxed_elements(a: &Array) -> Array {
2333 let row = a.to_row_major();
2334 let held: Vec<Array> = (0..row.count()).map(|i| atom(&row, i)).collect();
2335 Array::new(row.shape.clone(), Data::Box(held.into()))
2336}
2337
2338fn assemble(frame: &[usize], cells: Vec<Array>, span: Span) -> Result<Array> {
2345 if cells.is_empty() {
2346 return Ok(Array::new(frame.to_vec(), Data::empty(DType::I64)));
2349 }
2350 let cells: Vec<Array> =
2351 if cells.iter().all(Array::is_row_major) {
2352 cells
2353 } else {
2354 cells.iter().map(Array::to_row_major).collect()
2355 };
2356 let mut dt = cells.iter().find(|c| c.count() > 0).unwrap_or(&cells[0]).dtype();
2362 for c in &cells {
2363 if c.count() == 0 {
2364 continue;
2365 }
2366 dt = DType::promote(dt, c.dtype()).ok_or_else(|| {
2367 let boxed = dt == DType::Box || c.dtype() == DType::Box;
2368 let what = if boxed {
2369 "cannot frame boxed and unboxed results into one array"
2370 } else {
2371 "cannot frame character and numeric results into one array"
2372 };
2373 Error::new(ErrorKind::Type, what, Some(span))
2374 })?;
2375 }
2376 if cells.iter().all(|c| c.count() == 0) {
2377 for c in &cells {
2378 dt = DType::promote(dt, c.dtype()).unwrap_or(match (dt, c.dtype()) {
2379 (DType::Box, _) | (_, DType::Box) => DType::Box,
2380 _ => DType::Char,
2381 });
2382 }
2383 }
2384 let widen = |c: &Array| -> Result<Data> {
2385 if c.count() == 0 {
2386 return Ok(Data::empty(dt));
2387 }
2388 c.data.cast(dt).ok_or_else(|| Error::internal("unsupported widening while framing"))
2389 };
2390
2391 if cells[1..].iter().all(|c| c.shape == cells[0].shape) {
2392 let mut data = Data::empty(dt);
2393 for c in &cells {
2394 if c.dtype() == dt {
2395 data.extend_from(&c.data);
2396 } else {
2397 data.extend_from(&widen(c)?);
2398 }
2399 }
2400 let mut shape = frame.to_vec();
2401 shape.extend_from_slice(&cells[0].shape);
2402 return Ok(Array::new(shape, data));
2403 }
2404
2405 let crank = cells.iter().map(|c| c.rank()).max().unwrap_or(0);
2408 let padded: Vec<Vec<usize>> = cells
2409 .iter()
2410 .map(|c| {
2411 let mut s = vec![1usize; crank - c.rank()];
2412 s.extend_from_slice(&c.shape);
2413 s
2414 })
2415 .collect();
2416 let mut common = vec![0usize; crank];
2417 for s in &padded {
2418 for k in 0..crank {
2419 common[k] = common[k].max(s[k]);
2420 }
2421 }
2422 let cell_n: usize = common.iter().product();
2423 let mut data = Data::empty(dt);
2424 for (c, ps) in cells.iter().zip(&padded) {
2425 let cd = if c.dtype() == dt { c.data.clone() } else { widen(c)? };
2426 let st = strides(ps);
2427 let mut coord = vec![0usize; crank];
2428 for _ in 0..cell_n {
2429 let mut idx = 0usize;
2430 let mut inside = true;
2431 for k in 0..crank {
2432 if coord[k] >= ps[k] {
2433 inside = false;
2434 break;
2435 }
2436 idx += coord[k] * st[k];
2437 }
2438 if inside {
2439 push_elem(&mut data, &cd, idx);
2440 } else {
2441 data.push_fill();
2442 }
2443 odometer(&mut coord, &common);
2444 }
2445 }
2446 let mut shape = frame.to_vec();
2447 shape.extend_from_slice(&common);
2448 Ok(Array::new(shape, data))
2449}
2450
2451fn atom(a: &Array, i: usize) -> Array {
2456 debug_assert!(a.is_row_major(), "an atom out of a column-major buffer");
2457 Array::new(Vec::new(), a.data.slice(i, i + 1))
2458}
2459
2460fn enclose(y: &Array, rule: Enclose) -> Array {
2462 if rule == Enclose::ExceptSimpleScalar && y.rank() == 0 && y.dtype() != DType::Box {
2463 return y.clone();
2464 }
2465 Array::boxed(y.clone())
2466}
2467
2468fn open_cell(y: &Array) -> Array {
2476 match &y.data {
2477 Data::Box(v) if !v.is_empty() => v[0].to_row_major(),
2478 _ => y.clone(),
2479 }
2480}
2481
2482fn first(y: &Array) -> Array {
2485 if y.count() == 0 {
2486 if let Some(p) = y.proto() {
2489 return p.clone();
2490 }
2491 let mut d = Data::empty(y.dtype());
2492 d.push_fill();
2493 return open_cell(&Array::new(Vec::new(), d));
2494 }
2495 open_cell(&atom(y, 0))
2496}
2497
2498fn depth(y: &Array) -> i64 {
2500 match &y.data {
2501 Data::Box(v) => 1 + v.iter().map(depth).max().unwrap_or(0),
2502 _ => i64::from(y.rank() > 0),
2503 }
2504}
2505
2506fn uniform(y: &Array) -> bool {
2510 let Data::Box(v) = &y.data else { return true };
2511 let Some(head) = v.first() else { return true };
2512 let d = depth(head);
2513 v.iter().all(|b| depth(b) == d && uniform(b))
2514}
2515
2516fn leaves(a: &Array, out: &mut Vec<Array>) {
2522 let a = a.to_row_major();
2523 match &a.data {
2524 Data::Box(v) => {
2525 for b in v.iter() {
2526 leaves(b, out);
2527 }
2528 }
2529 _ => out.push(a),
2530 }
2531}
2532
2533fn enlist(y: &Array, _span: Span) -> Result<Array> {
2536 let mut parts = Vec::new();
2537 leaves(y, &mut parts);
2538 let mut dt = None;
2541 let mut mixing = false;
2542 for p in parts.iter().filter(|p| p.count() > 0) {
2543 dt = Some(match dt {
2544 None => p.dtype(),
2545 Some(t) => match DType::promote(t, p.dtype()) {
2546 Some(t) => t,
2547 None => {
2548 mixing = true;
2549 break;
2550 }
2551 },
2552 });
2553 }
2554 if mixing {
2555 let mut cells: Vec<Array> = Vec::new();
2556 for p in &parts {
2557 let p = p.to_row_major();
2558 cells.extend((0..p.count()).map(|i| atom(&p, i)));
2559 }
2560 return Ok(Array::new(vec![cells.len()], Data::Box(cells.into())));
2561 }
2562 let dt = dt.unwrap_or(DType::I64);
2563 let mut data = Data::empty(dt);
2564 for p in &parts {
2565 let cast = p.data.cast(dt).ok_or_else(|| Error::internal("unsupported widening in enlist"))?;
2566 data.extend_from(&cast);
2567 }
2568 Ok(Array::new(vec![data.len()], data))
2569}
2570
2571fn spread(a: &Array, shape: &[usize]) -> Array {
2573 let n: usize = shape.iter().product();
2574 let mut data = Data::empty(a.dtype());
2575 for _ in 0..n {
2576 push_elem(&mut data, &a.data, 0);
2577 }
2578 Array::new(shape.to_vec(), data)
2579}
2580
2581fn wider_shape(a: &[usize], b: &[usize]) -> Vec<usize> {
2584 let r = a.len().max(b.len());
2585 let pad = |s: &[usize]| {
2586 let mut v = vec![1usize; r - s.len()];
2587 v.extend_from_slice(s);
2588 v
2589 };
2590 let (pa, pb) = (pad(a), pad(b));
2591 (0..r).map(|k| pa[k].max(pb[k])).collect()
2592}
2593
2594fn raze(y: &Array, span: Span) -> Result<Array> {
2599 let opened: Vec<Array> = (0..y.count()).map(|i| open_cell(&atom(y, i))).collect();
2600 let mut common: Option<Vec<usize>> = None;
2601 for a in opened.iter().filter(|a| a.rank() > 0) {
2602 common = Some(match common {
2603 None => a.shape[1..].to_vec(),
2604 Some(c) => wider_shape(&c, &a.shape[1..]),
2605 });
2606 }
2607 let common = common.unwrap_or_default();
2608 let mut cells: Vec<Array> = Vec::new();
2609 for a in &opened {
2610 if a.rank() == 0 {
2611 cells.push(spread(a, &common));
2612 continue;
2613 }
2614 for i in 0..a.items() {
2615 cells.push(a.item(i));
2616 }
2617 }
2618 if cells.is_empty() {
2619 return Ok(Array::new(vec![0], Data::empty(DType::I64)));
2620 }
2621 let n = cells.len();
2622 assemble(&[n], cells, span)
2623}
2624
2625fn link(x: &Array, y: &Array, span: Span) -> Result<Array> {
2628 let head = Array::boxed(x.clone());
2629 let tail = if y.dtype() == DType::Box { y.clone() } else { Array::boxed(y.clone()) };
2630 catenate(&head, &tail, true, false, span)
2631}
2632
2633fn nest_like(a: &Array, other: &Array) -> Array {
2636 if a.dtype() == DType::Box || other.dtype() != DType::Box {
2637 return a.clone();
2638 }
2639 let cells: Vec<Array> = (0..a.count()).map(|i| atom(a, i)).collect();
2640 Array::new(a.shape.clone(), Data::Box(cells.into()))
2641}
2642
2643fn spread_scalars(a: &Array) -> Array {
2649 if a.dtype() == DType::Box {
2650 return a.clone();
2651 }
2652 let a = a.to_row_major();
2653 let cells: Vec<Array> = (0..a.count()).map(|i| atom(&a, i)).collect();
2654 Array::new(a.shape.clone(), Data::Box(cells.into()))
2655}
2656
2657fn holds_scalar_boxes(a: &Array) -> bool {
2660 match a.as_boxes() {
2661 Some(items) => {
2662 !items.is_empty() && items.iter().all(|b| b.rank() == 0 && b.dtype() != DType::Box)
2663 }
2664 None => false,
2665 }
2666}
2667
2668fn tightened_mixed(a: Array) -> Array {
2675 let common = match a.as_boxes() {
2676 Some(items) if holds_scalar_boxes(&a) => {
2677 let mut t = items[0].dtype();
2678 let mut ok = true;
2679 for b in &items[1..] {
2680 match DType::promote(t, b.dtype()) {
2681 Some(next) => t = next,
2682 None => {
2683 ok = false;
2684 break;
2685 }
2686 }
2687 }
2688 ok.then_some(t)
2689 }
2690 _ => None,
2691 };
2692 let Some(common) = common else { return a };
2693 let mut data = Data::empty(common);
2694 for b in a.as_boxes().expect("checked above") {
2695 match b.data.cast(common) {
2696 Some(widened) => push_elem(&mut data, &widened, 0),
2697 None => return a.clone(),
2698 }
2699 }
2700 Array::new(a.shape.clone(), data)
2701}
2702
2703fn align_mixed(x: &Array, y: &Array, apl: bool) -> (Array, Array) {
2708 if apl && holds_scalar_boxes(x) && y.dtype() != DType::Box {
2709 return (x.clone(), spread_scalars(y));
2710 }
2711 if apl && holds_scalar_boxes(y) && x.dtype() != DType::Box {
2712 return (spread_scalars(x), y.clone());
2713 }
2714 (x.clone(), y.clone())
2715}
2716
2717fn box_items(y: &Array) -> Array {
2719 if y.dtype() == DType::Box {
2720 return y.clone();
2721 }
2722 let n = y.items();
2723 let boxes: Vec<Array> = (0..n).map(|i| item_or_self(y, i)).collect();
2724 Array::new(vec![n], Data::Box(boxes.into()))
2725}
2726
2727fn strand(x: &Array, y: &Array, span: Span) -> Result<Array> {
2731 let item = enclose(x, Enclose::ExceptSimpleScalar);
2732 let one = |a: &Array| Array::new(vec![1], a.data.clone());
2733 if item.dtype() != DType::Box
2738 && y.dtype() != DType::Box
2739 && DType::promote(item.dtype(), y.dtype()).is_some()
2740 {
2741 return catenate(&one(&item), y, true, false, span);
2742 }
2743 let head = if item.dtype() == DType::Box { item } else { Array::boxed(item) };
2744 catenate(&one(&head), &box_items(y), true, false, span)
2745}
2746
2747fn char_arith(span: Span) -> Error {
2750 Error::new(ErrorKind::Type, "cannot do arithmetic on characters", Some(span))
2751}
2752
2753fn symbol_arith(span: Span) -> Error {
2754 Error::new(
2755 ErrorKind::Type,
2756 "cannot do arithmetic on symbols; `5 s:` gives their names back",
2757 Some(span),
2758 )
2759}
2760
2761fn box_arith(span: Span) -> Error {
2762 Error::new(
2763 ErrorKind::Type,
2764 "cannot do arithmetic on boxed values; open them first (J `>`, APL `⊃`)",
2765 Some(span),
2766 )
2767}
2768
2769fn wrong_type(d: DType, span: Span) -> Error {
2772 match d {
2773 DType::Box => box_arith(span),
2774 DType::Symbol => symbol_arith(span),
2775 _ => char_arith(span),
2776 }
2777}
2778
2779fn borrow_i64<'a>(d: &'a Data, tmp: &'a mut Vec<i64>) -> &'a [i64] {
2785 match d {
2786 Data::I64(v) => v,
2787 Data::Bool(v) => {
2788 *tmp = par::map(v, |&b| b as i64);
2789 &tmp[..]
2790 }
2791 _ => &[],
2793 }
2794}
2795
2796fn borrow_f64<'a>(d: &'a Data, tmp: &'a mut Vec<f64>) -> &'a [f64] {
2798 match d {
2799 Data::F64(v) => v,
2800 Data::I64(v) => {
2801 *tmp = par::map(v, |&x| x as f64);
2802 &tmp[..]
2803 }
2804 Data::Bool(v) => {
2805 *tmp = par::map(v, |&x| x as f64);
2806 &tmp[..]
2807 }
2808 Data::Ext(v) => {
2809 *tmp = par::map(v, exact::ext_to_f64);
2810 &tmp[..]
2811 }
2812 Data::Rat(v) => {
2813 *tmp = par::map(v, Rat::to_f64);
2814 &tmp[..]
2815 }
2816 _ => &[],
2817 }
2818}
2819
2820fn borrow_cx<'a>(d: &'a Data, tmp: &'a mut Vec<Cx>) -> &'a [Cx] {
2822 match d {
2823 Data::Complex(v) => v,
2824 Data::Ext(v) => {
2825 *tmp = par::map(v, |x| [exact::ext_to_f64(x), 0.0]);
2826 &tmp[..]
2827 }
2828 Data::Rat(v) => {
2829 *tmp = par::map(v, |x| [x.to_f64(), 0.0]);
2830 &tmp[..]
2831 }
2832 Data::F64(v) => {
2833 *tmp = par::map(v, |&x| [x, 0.0]);
2834 &tmp[..]
2835 }
2836 Data::I64(v) => {
2837 *tmp = par::map(v, |&x| [x as f64, 0.0]);
2838 &tmp[..]
2839 }
2840 Data::Bool(v) => {
2841 *tmp = v.iter().map(|&x| [x as f64, 0.0]).collect();
2842 &tmp[..]
2843 }
2844 _ => &[],
2845 }
2846}
2847
2848pub(crate) trait Widen<T>: Copy + Send + Sync {
2857 fn widen(self) -> T;
2858}
2859
2860macro_rules! widens {
2861 ($($from:ty => $to:ty : |$v:ident| $e:expr;)*) => {
2862 $(impl Widen<$to> for $from {
2863 #[inline(always)]
2864 fn widen(self) -> $to {
2865 let $v = self;
2866 $e
2867 }
2868 })*
2869 };
2870}
2871
2872widens! {
2873 u8 => i64: |v| v as i64;
2874 i64 => i64: |v| v;
2875 u8 => f64: |v| v as f64;
2876 i64 => f64: |v| v as f64;
2877 f64 => f64: |v| v;
2878 u8 => Cx: |v| [v as f64, 0.0];
2879 i64 => Cx: |v| [v as f64, 0.0];
2880 f64 => Cx: |v| [v, 0.0];
2881 Cx => Cx: |v| v;
2882}
2883
2884macro_rules! i64_source {
2887 ($d:expr, $tmp:ident, $s:ident, $body:expr) => {
2888 match $d {
2889 Data::I64(v) => {
2890 let $s: &[i64] = v;
2891 $body
2892 }
2893 Data::Bool(v) => {
2894 let $s: &[u8] = v;
2895 $body
2896 }
2897 other => {
2898 let $s: &[i64] = borrow_i64(other, &mut $tmp);
2899 $body
2900 }
2901 }
2902 };
2903}
2904
2905macro_rules! f64_source {
2908 ($d:expr, $tmp:ident, $s:ident, $body:expr) => {
2909 match $d {
2910 Data::F64(v) => {
2911 let $s: &[f64] = v;
2912 $body
2913 }
2914 Data::I64(v) => {
2915 let $s: &[i64] = v;
2916 $body
2917 }
2918 Data::Bool(v) => {
2919 let $s: &[u8] = v;
2920 $body
2921 }
2922 other => {
2923 let $s: &[f64] = borrow_f64(other, &mut $tmp);
2924 $body
2925 }
2926 }
2927 };
2928}
2929
2930macro_rules! cx_source {
2932 ($d:expr, $tmp:ident, $s:ident, $body:expr) => {
2933 match $d {
2934 Data::Complex(v) => {
2935 let $s: &[Cx] = v;
2936 $body
2937 }
2938 Data::F64(v) => {
2939 let $s: &[f64] = v;
2940 $body
2941 }
2942 Data::I64(v) => {
2943 let $s: &[i64] = v;
2944 $body
2945 }
2946 Data::Bool(v) => {
2947 let $s: &[u8] = v;
2948 $body
2949 }
2950 other => {
2951 let $s: &[Cx] = borrow_cx(other, &mut $tmp);
2952 $body
2953 }
2954 }
2955 };
2956}
2957
2958fn as_f64<'a>(d: &'a Data, tmp: &'a mut Vec<f64>, span: Span) -> Result<&'a [f64]> {
2960 if !d.dtype().is_numeric() {
2961 return Err(wrong_type(d.dtype(), span));
2962 }
2963 Ok(borrow_f64(d, tmp))
2964}
2965
2966fn arith_type(a: DType, b: DType, span: Span) -> Result<DType> {
2968 if a == DType::Box || b == DType::Box {
2969 return Err(box_arith(span));
2970 }
2971 if a == DType::Symbol || b == DType::Symbol {
2972 return Err(symbol_arith(span));
2973 }
2974 match DType::promote(a, b) {
2975 Some(DType::Char) => Err(char_arith(span)),
2976 None => Err(Error::new(
2977 ErrorKind::Type,
2978 "cannot mix character and numeric data",
2979 Some(span),
2980 )),
2981 Some(DType::Bool) => Ok(DType::I64),
2982 Some(t) => Ok(t),
2983 }
2984}
2985
2986#[allow(clippy::too_many_arguments)]
2999#[inline]
3000fn zip_chunk<A, B, U, F>(
3001 xs: &[A],
3002 xoff: usize,
3003 xdiv: usize,
3004 ys: &[B],
3005 yoff: usize,
3006 ydiv: usize,
3007 start: usize,
3008 out: &mut [U],
3009 mut f: F,
3010) -> bool
3011where
3012 A: Copy,
3013 B: Copy,
3014 F: FnMut(A, B, &mut U) -> bool,
3015{
3016 let len = out.len();
3017 if len == 0 {
3018 return true;
3019 }
3020 let last = start + len - 1;
3021 let one_x = xdiv > 1 && start / xdiv == last / xdiv;
3022 let one_y = ydiv > 1 && start / ydiv == last / ydiv;
3023 if xdiv == 1 && ydiv == 1 {
3024 let xc = &xs[xoff + start..xoff + start + len];
3025 let yc = &ys[yoff + start..yoff + start + len];
3026 for ((slot, &a), &b) in out.iter_mut().zip(xc).zip(yc) {
3027 if !f(a, b, slot) {
3028 return false;
3029 }
3030 }
3031 } else if xdiv == 1 && one_y {
3032 let b = ys[yoff + start / ydiv];
3033 let xc = &xs[xoff + start..xoff + start + len];
3034 for (slot, &a) in out.iter_mut().zip(xc) {
3035 if !f(a, b, slot) {
3036 return false;
3037 }
3038 }
3039 } else if one_x && ydiv == 1 {
3040 let a = xs[xoff + start / xdiv];
3041 let yc = &ys[yoff + start..yoff + start + len];
3042 for (slot, &b) in out.iter_mut().zip(yc) {
3043 if !f(a, b, slot) {
3044 return false;
3045 }
3046 }
3047 } else {
3048 for (k, slot) in out.iter_mut().enumerate() {
3049 let i = start + k;
3050 if !f(xs[xoff + i / xdiv], ys[yoff + i / ydiv], slot) {
3051 return false;
3052 }
3053 }
3054 }
3055 true
3056}
3057
3058const LANCZOS: [f64; 9] = [
3062 0.999_999_999_999_809_9,
3063 676.520_368_121_885_1,
3064 -1_259.139_216_722_402_8,
3065 771.323_428_777_653_1,
3066 -176.615_029_162_140_6,
3067 12.507_343_278_686_905,
3068 -0.138_571_095_265_720_12,
3069 9.984_369_578_019_572e-6,
3070 1.505_632_735_149_311_6e-7,
3071];
3072
3073fn gamma(x: f64) -> f64 {
3077 use std::f64::consts::PI;
3078 if x < 0.5 {
3079 return PI / ((PI * x).sin() * gamma(1.0 - x));
3081 }
3082 let z = x - 1.0;
3083 let mut a = LANCZOS[0];
3084 for (i, &c) in LANCZOS.iter().enumerate().skip(1) {
3085 a += c / (z + i as f64);
3086 }
3087 let t = z + 7.5;
3088 (2.0 * PI).sqrt() * t.powf(z + 0.5) * (-t).exp() * a
3089}
3090
3091fn factorial(y: f64) -> f64 {
3094 if y.fract() == 0.0 && y.abs() < 1e17 {
3095 let n = y as i64;
3096 if n < 0 {
3097 return if n % 2 == -1 { f64::INFINITY } else { f64::NEG_INFINITY };
3099 }
3100 if n > 170 {
3101 return f64::INFINITY;
3102 }
3103 let mut c = 1.0f64;
3104 for i in 2..=n {
3105 c *= i as f64;
3106 }
3107 return c;
3108 }
3109 gamma(y + 1.0)
3110}
3111
3112fn factorial_as(y: f64, tol: Tol) -> f64 {
3118 let r = factorial(y);
3119 if tol.is_j() && r.is_nan() && !y.is_nan() && y != f64::NEG_INFINITY {
3120 return f64::INFINITY;
3121 }
3122 r
3123}
3124
3125const BINOMIAL_PRODUCT_LIMIT: i64 = 4096;
3128
3129fn binomial_product(x: i64, y: f64) -> f64 {
3133 let mut c = 1.0f64;
3134 for i in 1..=x {
3135 c = c * (y - i as f64 + 1.0) / i as f64;
3136 if c == 0.0 {
3137 break;
3138 }
3139 }
3140 c
3141}
3142
3143fn binomial_i64(x: i64, y: i64) -> Option<i64> {
3147 if x < 0 {
3148 if y >= 0 || y < x {
3151 return Some(0);
3152 }
3153 let v = binomial_exact(-y - 1, -x - 1)?;
3154 return if (y - x) % 2 == 0 { Some(v) } else { v.checked_neg() };
3155 }
3156 binomial_exact(x, y)
3157}
3158
3159fn binomial_exact(x: i64, y: i64) -> Option<i64> {
3162 if x > BINOMIAL_PRODUCT_LIMIT {
3163 return None;
3164 }
3165 let mut c: i128 = 1;
3166 for i in 1..=x as i128 {
3167 c = c.checked_mul(y as i128 - i + 1)? / i;
3168 if c == 0 {
3169 break;
3170 }
3171 }
3172 i64::try_from(c).ok()
3173}
3174
3175fn binomial_at_infinity(x: f64, y: f64) -> Option<f64> {
3183 if x.is_infinite() && y.is_infinite() {
3184 return (x < 0.0 && y > 0.0).then_some(0.0);
3185 }
3186 if x.is_infinite() {
3187 return (!(y < 0.0 && y.fract() == 0.0)).then_some(0.0);
3190 }
3191 if x > 0.0 {
3192 Some(f64::INFINITY)
3193 } else if x == 0.0 {
3194 Some(1.0)
3195 } else {
3196 Some(0.0)
3197 }
3198}
3199
3200fn binomial(x: f64, y: f64) -> f64 {
3202 if x.is_nan() || y.is_nan() {
3203 return f64::NAN;
3206 }
3207 if x.is_infinite() || y.is_infinite() {
3208 return binomial_at_infinity(x, y).unwrap_or(f64::NAN);
3209 }
3210 if x.fract() == 0.0 && x.abs() < 1e17 {
3211 let xi = x as i64;
3212 if xi < 0 {
3213 if y.fract() == 0.0 && y < 0.0 && y >= x {
3214 let sign = if (y as i64 - xi) % 2 == 0 { 1.0 } else { -1.0 };
3215 return sign * binomial_product(-y as i64 - 1, -x - 1.0);
3216 }
3217 return 0.0;
3218 }
3219 if xi <= BINOMIAL_PRODUCT_LIMIT {
3220 return binomial_product(xi, y);
3221 }
3222 }
3223 gamma(y + 1.0) / (gamma(x + 1.0) * gamma(y - x + 1.0))
3224}
3225
3226#[inline]
3229fn i64_op(op: ScalarDyad, a: i64, b: i64) -> Option<i64> {
3230 use ScalarDyad::*;
3231 Some(match op {
3232 Add => a.checked_add(b)?,
3233 Sub => a.checked_sub(b)?,
3234 Mul => a.checked_mul(b)?,
3235 Min => a.min(b),
3236 Max => a.max(b),
3237 Residue => {
3238 if a == 0 {
3239 b
3240 } else {
3241 let mut r = b.wrapping_rem(a);
3243 if r != 0 && (r < 0) != (a < 0) {
3244 r += a;
3245 }
3246 r
3247 }
3248 }
3249 Pow => {
3250 if b < 0 {
3251 return None;
3252 }
3253 a.checked_pow(u32::try_from(b).ok()?)?
3254 }
3255 Binomial => binomial_i64(a, b)?,
3256 _ => return None,
3257 })
3258}
3259
3260#[inline]
3262fn f64_op(op: ScalarDyad, a: f64, b: f64, tol: Tol, span: Span) -> Result<f64> {
3263 use ScalarDyad::*;
3264 let r = match op {
3265 Add => a + b,
3266 Sub => a - b,
3267 Mul => tol.mul(a, b),
3268 Min => a.min(b),
3269 Max => a.max(b),
3270 DivJ => {
3271 if b == 0.0 {
3272 if a == 0.0 { 0.0 } else { f64::INFINITY.copysign(a) }
3273 } else {
3274 a / b
3275 }
3276 }
3277 DivApl => {
3278 if b == 0.0 {
3279 if a == 0.0 {
3280 1.0
3281 } else {
3282 return Err(Error::domain("division by zero", span));
3283 }
3284 } else {
3285 a / b
3286 }
3287 }
3288 Pow => {
3289 if a == 0.0 && b == 0.0 {
3290 1.0
3291 } else if a == 0.0 && b < 0.0 && !tol.is_j() {
3292 return Err(Error::domain("zero has no negative power", span));
3296 } else if a < 0.0 && b.is_infinite() {
3297 if a.abs() != 1.0 && (a.abs() > 1.0) == (b < 0.0) {
3302 0.0
3303 } else {
3304 return Err(Error::domain(
3305 "a negative base has no infinite power: the sign alternates",
3306 span,
3307 ));
3308 }
3309 } else {
3310 a.powf(b)
3311 }
3312 }
3313 Residue => tol.residue(a, b),
3314 Log => {
3315 if a < 0.0 || b < 0.0 {
3316 return Err(Error::not_yet("complex numbers", span));
3317 }
3318 let r = b.ln() / a.ln();
3319 if !tol.is_j() && !r.is_finite() {
3325 if r.is_nan() {
3326 return Ok(1.0);
3327 }
3328 return Err(Error::domain("this logarithm has no value", span));
3329 }
3330 r
3331 }
3332 Root => {
3333 if b < 0.0 {
3334 return Err(Error::not_yet("complex numbers", span));
3335 }
3336 b.powf(1.0 / a)
3337 }
3338 Circle => {
3341 let r = circle(a, b, span)?;
3342 if !tol.is_j() && !r.is_finite() && a.is_finite() && b.is_finite() {
3346 return Err(Error::domain("this circle function has no value", span));
3347 }
3348 r
3349 }
3350 Binomial => binomial(a, b),
3351 _ => return Err(Error::internal("non-arithmetic op in the float path")),
3352 };
3353 if tol.made_nan(r, a, b) {
3354 return Err(nan_error(op, a, b, span));
3355 }
3356 Ok(r)
3357}
3358
3359#[cold]
3362fn nan_error(op: ScalarDyad, a: f64, b: f64, span: Span) -> Error {
3363 Error::nan(
3364 format!(
3365 "`{} {} {}` has no value",
3366 j_number(a),
3367 crate::fuse::dyad_name(op),
3368 j_number(b)
3369 ),
3370 span,
3371 )
3372}
3373
3374#[inline]
3378fn escapes_reals(op: ScalarDyad, a: f64, b: f64) -> bool {
3379 use ScalarDyad::*;
3380 match op {
3381 Pow => a < 0.0 && b.is_finite() && b.fract() != 0.0,
3386 Log => a < 0.0 || b < 0.0,
3387 Root => b < 0.0,
3388 Circle => circle_escapes(a, b),
3389 _ => false,
3390 }
3391}
3392
3393#[inline]
3396fn circle_escapes(k: f64, y: f64) -> bool {
3397 if k.fract() != 0.0 {
3398 return false;
3399 }
3400 match k as i64 {
3401 0 | -1 | -2 | -7 => y.abs() > 1.0,
3402 -4 => y.abs() < 1.0,
3403 -6 => y < 1.0,
3404 8 | -8 | -11 | -12 => true,
3407 _ => false,
3408 }
3409}
3410
3411#[inline]
3421fn circle(k: f64, y: f64, span: Span) -> Result<f64> {
3422 if k.fract() != 0.0 {
3423 return Err(Error::domain("the circle function needs an integer left argument", span));
3424 }
3425 let complex = || Error::internal("a circle function left the reals on the real path");
3426 Ok(match k as i64 {
3427 0 => {
3428 if y.abs() > 1.0 {
3429 return Err(complex());
3430 }
3431 (1.0 - y * y).max(0.0).sqrt()
3432 }
3433 1 => y.sin(),
3434 2 => y.cos(),
3435 3 => y.tan(),
3436 4 => (1.0 + y * y).sqrt(),
3437 5 => y.sinh(),
3438 6 => y.cosh(),
3439 7 => y.tanh(),
3440 -1 => {
3441 if y.abs() > 1.0 {
3442 return Err(complex());
3443 }
3444 y.asin()
3445 }
3446 -2 => {
3447 if y.abs() > 1.0 {
3448 return Err(complex());
3449 }
3450 y.acos()
3451 }
3452 -3 => y.atan(),
3453 -4 => {
3454 if y.abs() < 1.0 {
3455 return Err(complex());
3456 }
3457 y.signum() * (y * y - 1.0).max(0.0).sqrt()
3459 }
3460 -5 => y.asinh(),
3461 -6 => {
3462 if y < 1.0 {
3463 return Err(complex());
3464 }
3465 y.acosh()
3466 }
3467 -7 => {
3468 if y.abs() > 1.0 {
3469 return Err(complex());
3470 }
3471 y.atanh()
3472 }
3473 9 | -9 | -10 => y,
3475 10 => y.abs(),
3476 11 => 0.0,
3477 12 => {
3478 if y < 0.0 {
3479 std::f64::consts::PI
3480 } else {
3481 0.0
3482 }
3483 }
3484 8 | -8 | -11 | -12 => return Err(complex()),
3485 _ => {
3486 return Err(Error::domain(
3487 "the circle functions run from _12 to 12",
3488 span,
3489 ));
3490 }
3491 })
3492}
3493
3494#[inline]
3496fn cx_op(op: ScalarDyad, a: Cx, b: Cx, span: Span) -> Result<Cx> {
3497 use ScalarDyad::*;
3498 Ok(match op {
3499 Add => cx::add(a, b),
3500 Sub => cx::sub(a, b),
3501 Mul => cx::mul(a, b),
3502 DivJ => cx::div(a, b),
3503 DivApl => {
3504 if b == cx::ZERO {
3505 if a == cx::ZERO {
3506 cx::ONE
3507 } else {
3508 return Err(Error::domain("division by zero", span));
3509 }
3510 } else {
3511 cx::div(a, b)
3512 }
3513 }
3514 Pow => cx::pow(a, b),
3515 Log => cx::log(a, b),
3516 Root => cx::root(a, b),
3517 Residue => cx::residue(a, b),
3518 Lcm => cx::lcm(a, b),
3519 Gcd => cx::gcd(a, b),
3520 MakeComplex => cx::add(a, cx::mul(cx::I, b)),
3521 PolarBy => cx::mul(a, cx::exp(cx::mul(cx::I, b))),
3522 Circle => {
3523 if a[1] != 0.0 || a[0].fract() != 0.0 {
3524 return Err(Error::domain(
3525 "the circle function needs an integer left argument",
3526 span,
3527 ));
3528 }
3529 cx::circle(a[0] as i64, b).ok_or_else(|| {
3530 Error::domain("the circle functions run from _12 to 12", span)
3531 })?
3532 }
3533 Min | Max => return Err(no_complex_order(span)),
3534 Binomial => {
3535 return Err(Error::not_yet("the binomial function on complex numbers", span));
3536 }
3537 Eq | Ne | Lt | Le | Gt | Ge => {
3538 return Err(Error::internal("a comparison in the complex arithmetic path"));
3539 }
3540 })
3541}
3542
3543fn no_complex_order(span: Span) -> Error {
3546 Error::new(
3547 ErrorKind::Domain,
3548 "complex numbers have no order; only equality (=, ~:) applies to them",
3549 Some(span),
3550 )
3551}
3552
3553#[allow(clippy::too_many_arguments)]
3554#[inline(always)]
3555fn dyad_cx_chunk_body<A: Widen<Cx>, B: Widen<Cx>>(
3556 op: ScalarDyad,
3557 xs: &[A],
3558 xoff: usize,
3559 xdiv: usize,
3560 ys: &[B],
3561 yoff: usize,
3562 ydiv: usize,
3563 start: usize,
3564 out: &mut [Cx],
3565 span: Span,
3566) -> Result<()> {
3567 use ScalarDyad::*;
3568 macro_rules! plain {
3571 ($step:expr) => {{
3572 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut Cx| {
3573 *slot = $step(a.widen(), b.widen());
3574 true
3575 });
3576 return Ok(());
3577 }};
3578 }
3579 match op {
3580 Add => plain!(cx::add),
3581 Sub => plain!(cx::sub),
3582 Mul => plain!(cx::mul),
3583 DivJ => plain!(cx::div),
3584 _ => {}
3585 }
3586 let mut err = None;
3587 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut Cx| {
3588 match cx_op(op, a.widen(), b.widen(), span) {
3589 Ok(v) => {
3590 *slot = v;
3591 true
3592 }
3593 Err(e) => {
3594 err = Some(e);
3595 false
3596 }
3597 }
3598 });
3599 match err {
3600 Some(e) => Err(e),
3601 None => Ok(()),
3602 }
3603}
3604
3605multiversioned! {
3606 #[allow(clippy::too_many_arguments)]
3609 fn dyad_cx_chunk[A: Widen<Cx>, B: Widen<Cx>](
3610 op: ScalarDyad,
3611 xs: &[A],
3612 xoff: usize,
3613 xdiv: usize,
3614 ys: &[B],
3615 yoff: usize,
3616 ydiv: usize,
3617 start: usize,
3618 out: &mut [Cx],
3619 span: Span,
3620 ) -> Result<()> = dyad_cx_chunk_body;
3621}
3622
3623#[allow(clippy::too_many_arguments)]
3624fn dyad_cx<A: Widen<Cx>, B: Widen<Cx>>(
3625 op: ScalarDyad,
3626 xs: &[A],
3627 xoff: usize,
3628 xdiv: usize,
3629 ys: &[B],
3630 yoff: usize,
3631 ydiv: usize,
3632 n: usize,
3633 span: Span,
3634) -> Result<Vec<Cx>> {
3635 par::try_fill(n, |start, part| {
3636 dyad_cx_chunk(op, xs, xoff, xdiv, ys, yoff, ydiv, start, part, span)
3637 })
3638}
3639
3640#[allow(clippy::too_many_arguments)]
3649fn complex_dyad_data(
3650 op: ScalarDyad,
3651 x: &Data,
3652 xoff: usize,
3653 xdiv: usize,
3654 y: &Data,
3655 yoff: usize,
3656 ydiv: usize,
3657 n: usize,
3658 span: Span,
3659) -> Result<Data> {
3660 let (mut tx, mut ty) = (Vec::new(), Vec::new());
3661 macro_rules! pass {
3662 ($xs:expr, $ys:expr) => {
3663 Data::Complex(dyad_cx(op, $xs, xoff, xdiv, $ys, yoff, ydiv, n, span)?.into())
3664 };
3665 }
3666 Ok(match (x, y) {
3667 (Data::Complex(a), _) => {
3668 let xs: &[Cx] = a;
3669 cx_source!(y, ty, ys, pass!(xs, ys))
3670 }
3671 (_, Data::Complex(b)) => {
3672 let ys: &[Cx] = b;
3673 cx_source!(x, tx, xs, pass!(xs, ys))
3674 }
3675 _ => pass!(borrow_cx(x, &mut tx), borrow_cx(y, &mut ty)),
3676 })
3677}
3678
3679fn circle_reads_a_part(x: &Data, xoff: usize, xdiv: usize, n: usize) -> bool {
3684 if x.dtype() == DType::Complex {
3685 return false;
3687 }
3688 let mut tmp = Vec::new();
3689 let xs = borrow_f64(x, &mut tmp);
3690 (0..n).all(|i| {
3691 let k = xs[xoff + i / xdiv];
3692 k.fract() == 0.0 && (9.0..=12.0).contains(&k)
3693 })
3694}
3695
3696#[allow(clippy::too_many_arguments)]
3699fn pass_leaves_reals(
3700 op: ScalarDyad,
3701 x: &Data,
3702 xoff: usize,
3703 xdiv: usize,
3704 y: &Data,
3705 yoff: usize,
3706 ydiv: usize,
3707 n: usize,
3708) -> bool {
3709 use ScalarDyad::*;
3710 if !matches!(op, Pow | Log | Root | Circle) {
3711 return false;
3712 }
3713 let (mut tx, mut ty) = (Vec::new(), Vec::new());
3714 let xs = borrow_f64(x, &mut tx);
3715 let ys = borrow_f64(y, &mut ty);
3716 (0..n).any(|i| escapes_reals(op, xs[xoff + i / xdiv], ys[yoff + i / ydiv]))
3717}
3718
3719#[allow(clippy::too_many_arguments)]
3720#[inline(always)]
3721fn dyad_i64_chunk_body<A: Widen<i64>, B: Widen<i64>>(
3722 op: ScalarDyad,
3723 xs: &[A],
3724 xoff: usize,
3725 xdiv: usize,
3726 ys: &[B],
3727 yoff: usize,
3728 ydiv: usize,
3729 start: usize,
3730 out: &mut [i64],
3731) -> bool {
3732 use ScalarDyad::*;
3733 macro_rules! overflowing {
3737 ($m:ident) => {{
3738 let mut over = false;
3739 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut i64| {
3740 let (v, o) = i64::$m(a.widen(), b.widen());
3741 *slot = v;
3742 over |= o;
3743 true
3744 });
3745 !over
3746 }};
3747 }
3748 macro_rules! plain {
3749 ($step:expr) => {{
3750 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut i64| {
3751 *slot = $step(a.widen(), b.widen());
3752 true
3753 })
3754 }};
3755 }
3756 match op {
3757 Add => overflowing!(overflowing_add),
3758 Sub => overflowing!(overflowing_sub),
3759 Mul => overflowing!(overflowing_mul),
3760 Min => plain!(i64::min),
3761 Max => plain!(i64::max),
3762 _ => zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut i64| {
3763 match i64_op(op, a.widen(), b.widen()) {
3764 Some(v) => {
3765 *slot = v;
3766 true
3767 }
3768 None => false,
3769 }
3770 }),
3771 }
3772}
3773
3774multiversioned! {
3775 #[allow(clippy::too_many_arguments)]
3782 fn dyad_i64_chunk[A: Widen<i64>, B: Widen<i64>](
3783 op: ScalarDyad,
3784 xs: &[A],
3785 xoff: usize,
3786 xdiv: usize,
3787 ys: &[B],
3788 yoff: usize,
3789 ydiv: usize,
3790 start: usize,
3791 out: &mut [i64],
3792 ) -> bool = dyad_i64_chunk_body;
3793}
3794
3795#[allow(clippy::too_many_arguments)]
3797fn dyad_i64<A: Widen<i64>, B: Widen<i64>>(
3798 op: ScalarDyad,
3799 xs: &[A],
3800 xoff: usize,
3801 xdiv: usize,
3802 ys: &[B],
3803 yoff: usize,
3804 ydiv: usize,
3805 n: usize,
3806) -> Option<Vec<i64>> {
3807 let (out, ok) = par::fill(n, |start, part| {
3808 dyad_i64_chunk(op, xs, xoff, xdiv, ys, yoff, ydiv, start, part)
3809 });
3810 ok.then_some(out)
3811}
3812
3813#[allow(clippy::too_many_arguments)]
3816fn int_dyad_data(
3817 op: ScalarDyad,
3818 x: &Data,
3819 xoff: usize,
3820 xdiv: usize,
3821 y: &Data,
3822 yoff: usize,
3823 ydiv: usize,
3824 n: usize,
3825) -> Option<Data> {
3826 let (mut tx, mut ty) = (Vec::new(), Vec::new());
3827 let out = i64_source!(x, tx, xs, {
3828 i64_source!(y, ty, ys, dyad_i64(op, xs, xoff, xdiv, ys, yoff, ydiv, n))
3829 })?;
3830 Some(Data::I64(out.into()))
3831}
3832
3833#[allow(clippy::too_many_arguments)]
3834#[inline(always)]
3835fn dyad_f64_chunk_body<A: Widen<f64>, B: Widen<f64>>(
3836 op: ScalarDyad,
3837 xs: &[A],
3838 xoff: usize,
3839 xdiv: usize,
3840 ys: &[B],
3841 yoff: usize,
3842 ydiv: usize,
3843 start: usize,
3844 out: &mut [f64],
3845 tol: Tol,
3846 span: Span,
3847) -> Result<()> {
3848 use ScalarDyad::*;
3849 macro_rules! plain {
3852 ($step:expr) => {{
3853 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut f64| {
3854 *slot = $step(a.widen(), b.widen());
3855 true
3856 });
3857 return Ok(());
3858 }};
3859 }
3860 macro_rules! plain_checked {
3867 ($step:expr) => {{
3868 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut f64| {
3869 *slot = $step(a.widen(), b.widen());
3870 true
3871 });
3872 if !(tol.is_j() && out.iter().any(|v| v.is_nan())) {
3873 return Ok(());
3874 }
3875 }};
3876 }
3877 match op {
3878 Add => plain_checked!(|a: f64, b: f64| a + b),
3879 Sub => plain_checked!(|a: f64, b: f64| a - b),
3880 Mul => plain_checked!(|a: f64, b: f64| a * b),
3881 Min => plain!(f64::min),
3882 Max => plain!(f64::max),
3883 _ => {}
3884 }
3885 let mut err = None;
3886 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut f64| {
3887 match f64_op(op, a.widen(), b.widen(), tol, span) {
3888 Ok(v) => {
3889 *slot = v;
3890 true
3891 }
3892 Err(e) => {
3893 err = Some(e);
3894 false
3895 }
3896 }
3897 });
3898 match err {
3899 Some(e) => Err(e),
3900 None => Ok(()),
3901 }
3902}
3903
3904multiversioned! {
3905 #[allow(clippy::too_many_arguments)]
3908 fn dyad_f64_chunk[A: Widen<f64>, B: Widen<f64>](
3909 op: ScalarDyad,
3910 xs: &[A],
3911 xoff: usize,
3912 xdiv: usize,
3913 ys: &[B],
3914 yoff: usize,
3915 ydiv: usize,
3916 start: usize,
3917 out: &mut [f64],
3918 tol: Tol,
3919 span: Span,
3920 ) -> Result<()> = dyad_f64_chunk_body;
3921}
3922
3923#[allow(clippy::too_many_arguments)]
3924fn dyad_f64<A: Widen<f64>, B: Widen<f64>>(
3925 op: ScalarDyad,
3926 xs: &[A],
3927 xoff: usize,
3928 xdiv: usize,
3929 ys: &[B],
3930 yoff: usize,
3931 ydiv: usize,
3932 n: usize,
3933 tol: Tol,
3934 span: Span,
3935) -> Result<Vec<f64>> {
3936 par::try_fill(n, |start, part| {
3937 dyad_f64_chunk(op, xs, xoff, xdiv, ys, yoff, ydiv, start, part, tol, span)
3938 })
3939}
3940
3941#[allow(clippy::too_many_arguments)]
3943fn float_dyad_data(
3944 op: ScalarDyad,
3945 x: &Data,
3946 xoff: usize,
3947 xdiv: usize,
3948 y: &Data,
3949 yoff: usize,
3950 ydiv: usize,
3951 n: usize,
3952 tol: Tol,
3953 span: Span,
3954) -> Result<Data> {
3955 let (mut tx, mut ty) = (Vec::new(), Vec::new());
3956 let out = f64_source!(x, tx, xs, {
3957 f64_source!(y, ty, ys, dyad_f64(op, xs, xoff, xdiv, ys, yoff, ydiv, n, tol, span)?)
3958 });
3959 Ok(Data::F64(out.into()))
3960}
3961
3962fn crossed_types(a: DType, b: DType) -> bool {
3966 let class = |d: DType| match d {
3967 DType::Box => 3,
3968 DType::Symbol => 2,
3969 DType::Char => 1,
3970 _ => 0,
3971 };
3972 class(a) != class(b)
3973}
3974
3975#[allow(clippy::too_many_arguments)]
3978fn symbol_min_max(
3979 op: ScalarDyad,
3980 x: &Data,
3981 xoff: usize,
3982 xdiv: usize,
3983 y: &Data,
3984 yoff: usize,
3985 ydiv: usize,
3986 n: usize,
3987 span: Span,
3988) -> Result<Data> {
3989 let (Data::Symbol(a), Data::Symbol(b)) = (x, y) else {
3990 return Err(symbol_arith(span));
3991 };
3992 let down = op == ScalarDyad::Min;
3993 let (out, _) = par::fill(n, |start, part: &mut [crate::symbol::Id]| {
3994 zip_chunk(a, xoff, xdiv, b, yoff, ydiv, start, part, |p, q, slot| {
3995 *slot = if crate::symbol::cmp(p, q).is_le() == down { p } else { q };
3996 true
3997 })
3998 });
3999 Ok(Data::Symbol(out.into()))
4000}
4001
4002#[allow(clippy::too_many_arguments)]
4003fn compare_data(
4004 op: ScalarDyad,
4005 x: &Data,
4006 xoff: usize,
4007 xdiv: usize,
4008 y: &Data,
4009 yoff: usize,
4010 ydiv: usize,
4011 n: usize,
4012 tol: Tol,
4013 span: Span,
4014) -> Result<Data> {
4015 use ScalarDyad::*;
4016 let (dx, dy) = (x.dtype(), y.dtype());
4017 let equality = matches!(op, Eq | Ne);
4018 let boxed = dx == DType::Box || dy == DType::Box;
4024 if equality && crossed_types(dx, dy) && (!boxed || tol.is_j()) {
4025 let unequal = op == Ne;
4026 return Ok(Data::Bool(vec![u8::from(unequal); n].into()));
4027 }
4028 if boxed {
4029 if !equality {
4032 return Err(box_arith(span));
4033 }
4034 let (Data::Box(a), Data::Box(b)) = (x, y) else {
4035 return Err(Error::not_yet("a scalar function inside a nested array", span));
4038 };
4039 let (out, _) = par::fill(n, |start, part: &mut [u8]| {
4040 for (k, slot) in part.iter_mut().enumerate() {
4041 let i = start + k;
4042 let e = arrays_match(&a[xoff + i / xdiv], &b[yoff + i / ydiv], tol);
4043 *slot = u8::from(if op == Eq { e } else { !e });
4044 }
4045 true
4046 });
4047 return Ok(Data::Bool(out.into()));
4048 }
4049 if dx == DType::Symbol || dy == DType::Symbol {
4050 if dx != dy {
4053 return Err(Error::new(
4054 ErrorKind::Type,
4055 "cannot compare a symbol with data that is not a symbol",
4056 Some(span),
4057 ));
4058 }
4059 let (Data::Symbol(a), Data::Symbol(b)) = (x, y) else {
4060 return Err(Error::internal("symbol comparison on non-symbol data"));
4061 };
4062 let (out, _) = par::fill(n, |start, part: &mut [u8]| {
4064 zip_chunk(a, xoff, xdiv, b, yoff, ydiv, start, part, |p, q, slot| {
4065 *slot = u8::from(match op {
4066 Eq => p == q,
4067 Ne => p != q,
4068 _ => {
4069 let o = crate::symbol::cmp(p, q);
4070 match op {
4071 Lt => o.is_lt(),
4072 Le => o.is_le(),
4073 Gt => o.is_gt(),
4074 _ => o.is_ge(),
4075 }
4076 }
4077 });
4078 true
4079 })
4080 });
4081 return Ok(Data::Bool(out.into()));
4082 }
4083 if dx == DType::Char || dy == DType::Char {
4084 if dx != dy {
4085 return Err(Error::new(
4086 ErrorKind::Type,
4087 "cannot compare character and numeric data",
4088 Some(span),
4089 ));
4090 }
4091 if !equality {
4092 return Err(Error::new(
4093 ErrorKind::Type,
4094 "cannot order character data; only equality applies",
4095 Some(span),
4096 ));
4097 }
4098 let (Data::Char(a), Data::Char(b)) = (x, y) else {
4099 return Err(Error::internal("character comparison on non-character data"));
4100 };
4101 let (out, _) = par::fill(n, |start, part: &mut [u8]| {
4102 zip_chunk(a, xoff, xdiv, b, yoff, ydiv, start, part, |p, q, slot| {
4103 let e = p == q;
4104 *slot = if op == Eq { e as u8 } else { !e as u8 };
4105 true
4106 })
4107 });
4108 return Ok(Data::Bool(out.into()));
4109 }
4110 if DType::promote(dx, dy).is_some_and(DType::is_exact)
4111 && let Some(d) = exact_compare_data(op, x, xoff, xdiv, y, yoff, ydiv, n)
4112 {
4113 return Ok(d);
4114 }
4115 if dx == DType::Complex || dy == DType::Complex {
4116 if !equality {
4117 return Err(no_complex_order(span));
4118 }
4119 let (mut tx, mut ty) = (Vec::new(), Vec::new());
4120 let out = cx_source!(x, tx, xs, {
4121 cx_source!(y, ty, ys, {
4122 par::fill(n, |start, part: &mut [u8]| {
4123 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, part, |a, b, slot| {
4124 let e = tol.eq_cx(a.widen(), b.widen());
4125 *slot = if op == Eq { e as u8 } else { !e as u8 };
4126 true
4127 })
4128 })
4129 .0
4130 })
4131 });
4132 return Ok(Data::Bool(out.into()));
4133 }
4134 let out = if DType::promote(dx, dy) == Some(DType::F64) {
4137 let (mut tx, mut ty) = (Vec::<f64>::new(), Vec::<f64>::new());
4138 f64_source!(x, tx, xs, {
4139 f64_source!(y, ty, ys, {
4140 par::fill(n, |start, part: &mut [u8]| {
4141 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, part, |a, b, slot| {
4142 *slot = tol_cmp(op, a.widen(), b.widen(), tol) as u8;
4143 true
4144 })
4145 })
4146 .0
4147 })
4148 })
4149 } else {
4150 let (mut tx, mut ty) = (Vec::<i64>::new(), Vec::<i64>::new());
4151 i64_source!(x, tx, xs, {
4152 i64_source!(y, ty, ys, {
4153 par::fill(n, |start, part: &mut [u8]| {
4154 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, part, |a, b, slot| {
4155 let (a, b): (i64, i64) = (a.widen(), b.widen());
4156 *slot = cmp_result(op, Some(i64::cmp(&a, &b))) as u8;
4157 true
4158 })
4159 })
4160 .0
4161 })
4162 })
4163 };
4164 Ok(Data::Bool(out.into()))
4165}
4166
4167#[inline(always)]
4169pub(crate) fn tol_cmp(op: ScalarDyad, a: f64, b: f64, tol: Tol) -> bool {
4170 use ScalarDyad::*;
4171 match op {
4172 Eq => tol.eq(a, b),
4173 Ne => !tol.eq(a, b),
4174 Lt => tol.lt(a, b),
4175 Le => tol.le(a, b),
4176 Gt => tol.lt(b, a),
4177 Ge => tol.le(b, a),
4178 _ => false,
4179 }
4180}
4181
4182#[inline]
4186pub(crate) fn tol_ord(a: f64, b: f64, tol: Tol) -> std::cmp::Ordering {
4187 use std::cmp::Ordering::Equal;
4188 if tol.ct != 0.0 && tol.eq(a, b) {
4189 return Equal;
4190 }
4191 a.partial_cmp(&b).unwrap_or(Equal)
4192}
4193
4194fn cmp_result(op: ScalarDyad, ord: Option<std::cmp::Ordering>) -> bool {
4196 use std::cmp::Ordering::*;
4197 use ScalarDyad::*;
4198 match ord {
4199 None => matches!(op, Ne),
4200 Some(o) => match op {
4201 Eq => o == Equal,
4202 Ne => o != Equal,
4203 Lt => o == Less,
4204 Le => o != Greater,
4205 Gt => o == Greater,
4206 Ge => o != Less,
4207 _ => false,
4208 },
4209 }
4210}
4211
4212fn gcd_i128(a: i128, b: i128) -> i128 {
4218 let (mut a, mut b) = (a.abs(), b.abs());
4219 while b != 0 {
4220 let t = a % b;
4221 a = b;
4222 b = t;
4223 }
4224 a
4225}
4226
4227fn signed_gcd_i128(a: i128, b: i128) -> i128 {
4231 match (a, b) {
4232 (0, _) => b,
4233 (_, 0) => a,
4234 _ => gcd_i128(a, b),
4235 }
4236}
4237
4238fn decimal_parts(v: f64) -> Option<(i128, u32)> {
4246 if !v.is_finite() {
4247 return None;
4248 }
4249 let text = format!("{v:e}");
4250 let (mantissa, exponent) = text.split_once('e')?;
4251 let exponent: i32 = exponent.parse().ok()?;
4252 let (whole, fraction) = mantissa.split_once('.').unwrap_or((mantissa, ""));
4253 if whole.trim_start_matches('-').len() + fraction.len() > WRITTEN_DIGITS {
4254 return None;
4255 }
4256 let mut digits: i128 = format!("{whole}{fraction}").parse().ok()?;
4257 let mut scale = fraction.len() as i32 - exponent;
4258 while scale < 0 {
4261 digits = digits.checked_mul(10)?;
4262 scale += 1;
4263 }
4264 (scale <= 34).then_some((digits, scale as u32))
4267}
4268
4269const WRITTEN_DIGITS: usize = 12;
4273
4274fn gcd_decimal(a: f64, b: f64) -> Option<f64> {
4279 let (pa, sa) = decimal_parts(a)?;
4280 let (pb, sb) = decimal_parts(b)?;
4281 let scale = sa.max(sb);
4282 let lift = |p: i128, s: u32| 10i128.checked_pow(scale - s).and_then(|k| p.checked_mul(k));
4283 let g = gcd_i128(lift(pa, sa)?, lift(pb, sb)?);
4284 format!("{g}e-{scale}").parse().ok()
4287}
4288
4289fn gcd_f64(a: f64, b: f64, tol: Tol) -> Option<f64> {
4297 let (mut a, mut b) = (a.abs(), b.abs());
4298 if !a.is_finite() || !b.is_finite() {
4299 return None;
4300 }
4301 let eps = tol.ct * a.max(b);
4302 for _ in 0..1000 {
4305 if b == 0.0 {
4306 return Some(a);
4307 }
4308 if a == 0.0 {
4309 return Some(b);
4310 }
4311 let q = a / b;
4316 let mut k = q.floor();
4317 if tol.eq(q, k + 1.0) {
4318 k += 1.0;
4319 }
4320 let mut r = a - b * k;
4321 if r <= eps || tol.eq(r, b) {
4322 r = 0.0;
4323 }
4324 a = b;
4325 b = r;
4326 }
4327 Some(a)
4328}
4329
4330#[allow(clippy::too_many_arguments)]
4334fn real_lcm_gcd(
4335 op: ScalarDyad,
4336 xs: &[f64],
4337 xoff: usize,
4338 xdiv: usize,
4339 ys: &[f64],
4340 yoff: usize,
4341 ydiv: usize,
4342 n: usize,
4343 tol: Tol,
4344 gnu: bool,
4345 span: Span,
4346) -> Result<Data> {
4347 let mut out = vec![0.0f64; n];
4348 let mut ok = true;
4349 let whole = |v: f64| {
4354 let w = v.round();
4355 if gnu && tol.eq(v, w) { w } else { v }
4356 };
4357 let vanishes = |v: f64, other: f64| gnu && v != 0.0 && v.abs() <= tol.ct * other.abs();
4360 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, 0, &mut out, |a, b, slot| {
4361 let (a, b) = (whole(a), whole(b));
4362 let (a, b) = (if vanishes(a, b) { 0.0 } else { a }, if vanishes(b, a) { 0.0 } else { b });
4363 let Some(g) = gcd_decimal(a, b).or_else(|| gcd_f64(a, b, tol)) else {
4364 ok = false;
4365 return false;
4366 };
4367 *slot = if op == ScalarDyad::Gcd {
4368 g
4369 } else if g == 0.0 {
4370 0.0
4371 } else {
4372 a / g * b
4373 };
4374 true
4375 });
4376 if !ok {
4377 return Err(Error::domain("LCM/GCD needs finite values", span));
4378 }
4379 Ok(Data::F64(out.into()))
4380}
4381
4382#[allow(clippy::too_many_arguments)]
4386fn lcm_gcd_data(
4387 op: ScalarDyad,
4388 x: &Data,
4389 xoff: usize,
4390 xdiv: usize,
4391 y: &Data,
4392 yoff: usize,
4393 ydiv: usize,
4394 n: usize,
4395 tol: Tol,
4396 rules: Rules,
4397 span: Span,
4398) -> Result<Data> {
4399 let gnu = rules.lang == crate::Lang::Apl
4402 && rules.gcd_rule == crate::frontend::GcdRule::Tolerant;
4403 let t = arith_type(x.dtype(), y.dtype(), span)?;
4404 if t == DType::Complex {
4405 return complex_dyad_data(op, x, xoff, xdiv, y, yoff, ydiv, n, span);
4407 }
4408 if t.is_exact()
4409 && let Some(d) = exact_dyad_data(op, t, x, xoff, xdiv, y, yoff, ydiv, n, span)?
4410 {
4411 return Ok(d);
4412 }
4413 let both_bool = x.dtype() == DType::Bool && y.dtype() == DType::Bool;
4414 let float = t == DType::F64;
4415 let (xs, ys) = if float {
4416 let (mut tx, mut ty) = (Vec::new(), Vec::new());
4417 let xf = borrow_f64(x, &mut tx);
4418 let yf = borrow_f64(y, &mut ty);
4419 let integral = |v: &[f64]| v.iter().all(|&a| a.fract() == 0.0 && fits_i64(a));
4420 if !integral(xf) || !integral(yf) {
4421 return real_lcm_gcd(op, xf, xoff, xdiv, yf, yoff, ydiv, n, tol, gnu, span);
4422 }
4423 (
4424 xf.iter().map(|&a| a as i64).collect::<Vec<_>>(),
4425 yf.iter().map(|&a| a as i64).collect::<Vec<_>>(),
4426 )
4427 } else {
4428 let (mut tx, mut ty) = (Vec::new(), Vec::new());
4429 (borrow_i64(x, &mut tx).to_vec(), borrow_i64(y, &mut ty).to_vec())
4430 };
4431 let (out, fits) = par::fill(n, |start, part: &mut [i128]| {
4434 let mut fits = true;
4435 zip_chunk(&xs, xoff, xdiv, &ys, yoff, ydiv, start, part, |a, b, slot| {
4436 let (a, b) = (a as i128, b as i128);
4437 let g = if gnu { signed_gcd_i128(a, b) } else { gcd_i128(a, b) };
4438 let v = if op == ScalarDyad::Gcd {
4439 g
4440 } else if g == 0 {
4441 0
4442 } else {
4443 a / g * b
4444 };
4445 fits &= i64::try_from(v).is_ok();
4446 *slot = v;
4447 true
4448 });
4449 fits
4450 });
4451 if !fits || float {
4452 return Ok(Data::F64(par::map(&out, |&v| v as f64).into()));
4453 }
4454 if both_bool {
4455 return Ok(Data::Bool(par::map(&out, |&v| v as u8).into()));
4456 }
4457 Ok(Data::I64(par::map(&out, |&v| v as i64).into()))
4458}
4459
4460fn to_rat_vec(d: &Data) -> Option<Vec<Rat>> {
4465 Some(match d {
4466 Data::Bool(v) => v.iter().map(|&b| Rat::from_int(Ext::from(b))).collect(),
4467 Data::I64(v) => v.iter().map(|&x| Rat::from_int(Ext::from(x))).collect(),
4468 Data::Ext(v) => v.iter().map(|x| Rat::from_int(x.clone())).collect(),
4469 Data::Rat(v) => v.to_vec(),
4470 Data::F64(_) | Data::Complex(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => {
4471 return None;
4472 }
4473 })
4474}
4475
4476fn rat_window(d: &Data, off: usize, div: usize, n: usize) -> Option<Vec<Rat>> {
4484 if n == 0 {
4485 return Some(Vec::new());
4486 }
4487 let end = off + (n - 1) / div + 1;
4488 if off == 0 && end == d.len() {
4489 return to_rat_vec(d);
4490 }
4491 to_rat_vec(&d.slice(off, end))
4492}
4493
4494fn exact_data(t: DType, out: Vec<Rat>) -> Data {
4502 if t == DType::Ext && out.iter().all(Rat::is_integer) {
4503 return Data::Ext(out.iter().map(|r| r.to_int().expect("whole")).collect());
4504 }
4505 Data::Rat(out.into())
4506}
4507
4508fn too_large(span: Span) -> Error {
4510 Error::domain(
4511 format!(
4512 "the exact result needs more than {} bits; use floats for a value this large",
4513 exact::MAX_BITS
4514 ),
4515 span,
4516 )
4517}
4518
4519fn exact_pow(a: &Rat, b: &Rat, span: Span) -> Result<Option<Rat>> {
4522 let Some(e) = b.to_int().as_ref().and_then(exact::ext_to_i64) else {
4523 return Ok(None);
4524 };
4525 if let Some(v) = a.pow(e) {
4526 return Ok(Some(v));
4527 }
4528 if a.is_zero() && e < 0 { Ok(None) } else { Err(too_large(span)) }
4530}
4531
4532#[allow(clippy::too_many_arguments)]
4536fn exact_dyad_data(
4537 op: ScalarDyad,
4538 t: DType,
4539 x: &Data,
4540 xoff: usize,
4541 xdiv: usize,
4542 y: &Data,
4543 yoff: usize,
4544 ydiv: usize,
4545 n: usize,
4546 span: Span,
4547) -> Result<Option<Data>> {
4548 use ScalarDyad::*;
4549 let (Some(xs), Some(ys)) = (rat_window(x, xoff, xdiv, n), rat_window(y, yoff, ydiv, n))
4550 else {
4551 return Ok(None);
4552 };
4553 let mut out = Vec::with_capacity(n);
4554 for i in 0..n {
4555 let a = &xs[i / xdiv];
4556 let b = &ys[i / ydiv];
4557 let v = match op {
4558 Add => a.add(b),
4559 Sub => a.sub(b),
4560 Mul => a.mul(b),
4561 DivJ | DivApl => match a.div(b) {
4563 Some(v) => v,
4564 None => return Ok(None),
4565 },
4566 Min => a.min(b).clone(),
4567 Max => a.max(b).clone(),
4568 Residue => exact::rat_residue(a, b),
4569 Gcd => exact::rat_gcd(a, b),
4570 Lcm => exact::rat_lcm(a, b),
4571 Pow => match exact_pow(a, b, span)? {
4572 Some(v) => v,
4573 None => return Ok(None),
4574 },
4575 Binomial => match (a.to_int(), b.to_int()) {
4576 (Some(k), Some(m)) => match exact::ext_binomial(&k, &m) {
4577 Some(v) => Rat::from_int(v),
4578 None => return Ok(None),
4579 },
4580 _ => return Ok(None),
4581 },
4582 Root if t == DType::Ext => {
4585 let (Some(k), Some(m)) = (a.to_int(), b.to_int()) else {
4586 return Ok(None);
4587 };
4588 let Some(k) = exact::ext_to_i64(&k).and_then(|k| u32::try_from(k).ok()) else {
4589 return Ok(None);
4590 };
4591 match exact::exact_root(k, &m) {
4592 Some(v) => Rat::from_int(v),
4593 None => return Ok(None),
4594 }
4595 }
4596 Root | Log | Circle | MakeComplex | PolarBy => return Ok(None),
4597 Eq | Ne | Lt | Le | Gt | Ge => return Ok(None),
4599 };
4600 out.push(v);
4601 }
4602 Ok(Some(exact_data(t, out)))
4603}
4604
4605fn exact_monad(op: ScalarMonad, y: &Array) -> Option<Array> {
4608 use ScalarMonad::*;
4609 let v = to_rat_vec(&y.data)?;
4610 let shape = y.shape.clone();
4611 if matches!(op, Floor | Ceil | Signum) {
4614 let out: Vec<Ext> = v
4615 .iter()
4616 .map(|r| match op {
4617 Floor => r.floor(),
4618 Ceil => r.ceil(),
4619 _ => r.signum(),
4620 })
4621 .collect();
4622 return Some(Array::new(shape, Data::Ext(out.into())).with_layout(y.layout()));
4623 }
4624 let two = Rat::from_int(Ext::from(2));
4625 let mut out = Vec::with_capacity(v.len());
4626 for r in &v {
4627 let value = match op {
4628 Conj => r.clone(),
4629 Neg => r.neg(),
4630 Abs => r.abs(),
4631 Recip => r.recip()?,
4632 Inc => r.add(&Rat::one()),
4633 Dec => r.sub(&Rat::one()),
4634 OneMinus => Rat::one().sub(r),
4635 Double => r.add(r),
4636 Halve => r.div(&two).expect("two is not zero"),
4637 Square => r.mul(r),
4638 Sqrt => r.sqrt()?,
4639 Factorial => Rat::from_int(r.to_int().as_ref().and_then(exact::ext_factorial)?),
4640 Exp | Ln | Pi | Imaginary | Polar | Not => return None,
4643 Floor | Ceil | Signum => unreachable!("handled above"),
4644 };
4645 out.push(value);
4646 }
4647 Some(Array::new(shape, exact_data(y.dtype(), out)).with_layout(y.layout()))
4648}
4649
4650fn to_exact(y: &Array, span: Span) -> Result<Array> {
4655 let data = match &y.data {
4656 Data::Ext(_) | Data::Rat(_) => return Ok(y.clone()),
4657 Data::Bool(v) => Data::Ext(v.iter().map(|&b| Ext::from(b)).collect()),
4658 Data::I64(v) => Data::Ext(v.iter().map(|&x| Ext::from(x)).collect()),
4659 Data::F64(v) => {
4660 let mut out = Vec::with_capacity(v.len());
4661 for &x in v.iter() {
4662 out.push(exact::f64_to_rat(x).ok_or_else(|| {
4663 Error::domain("an infinity has no exact value", span)
4664 })?);
4665 }
4666 exact_data(DType::Ext, out)
4667 }
4668 Data::Complex(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => {
4669 return Err(Error::domain(
4670 format!("x: needs real numbers, not {} data", y.dtype().name()),
4671 span,
4672 ));
4673 }
4674 };
4675 Ok(Array::new(y.shape.clone(), data).with_layout(y.layout()))
4676}
4677
4678fn from_exact(y: &Array) -> Array {
4681 let shape = y.shape.clone();
4682 match &y.data {
4683 Data::Ext(v) => match v.iter().map(exact::ext_to_i64).collect::<Option<Vec<i64>>>() {
4684 Some(out) => Array::new(shape, Data::I64(out.into())).with_layout(y.layout()),
4685 None => Array::new(shape, Data::F64(v.iter().map(exact::ext_to_f64).collect()))
4686 .with_layout(y.layout()),
4687 },
4688 Data::Rat(v) => Array::new(shape, Data::F64(v.iter().map(Rat::to_f64).collect()))
4689 .with_layout(y.layout()),
4690 _ => y.clone(),
4691 }
4692}
4693
4694fn exact_form(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
4696 match one_whole(x, "the form x: converts to", near, span)? {
4697 1 => {
4698 let e = to_exact(y, span)?;
4699 e.cast(DType::Rat).ok_or_else(|| Error::internal("an exact value has no rational form"))
4700 }
4701 2 => {
4702 let e = to_exact(y, span)?;
4703 let v = to_rat_vec(&e.data).ok_or_else(|| Error::internal("x: gave an inexact value"))?;
4704 let mut out = Vec::with_capacity(2 * v.len());
4705 for r in &v {
4706 out.push(r.numer().clone());
4707 out.push(r.denom().clone());
4708 }
4709 let mut shape = y.shape.clone();
4710 shape.push(2);
4711 Ok(Array::new(shape, Data::Ext(out.into())))
4712 }
4713 -1 => Ok(from_exact(y)),
4714 -2 => {
4716 if !y.dtype().is_numeric() {
4717 return Err(Error::domain(
4718 format!("x: needs real numbers, not {} data", y.dtype().name()),
4719 span,
4720 ));
4721 }
4722 Ok(y.clone())
4723 }
4724 n => Err(Error::domain(
4725 format!("x: converts to form 1, 2, _1 or _2, not {n}"),
4726 span,
4727 )),
4728 }
4729}
4730
4731#[allow(clippy::too_many_arguments)]
4735fn exact_compare_data(
4736 op: ScalarDyad,
4737 x: &Data,
4738 xoff: usize,
4739 xdiv: usize,
4740 y: &Data,
4741 yoff: usize,
4742 ydiv: usize,
4743 n: usize,
4744) -> Option<Data> {
4745 let (xs, ys) = (rat_window(x, xoff, xdiv, n)?, rat_window(y, yoff, ydiv, n)?);
4746 let out: Vec<u8> = (0..n)
4747 .map(|i| {
4748 let ord = xs[i / xdiv].cmp(&ys[i / ydiv]);
4749 cmp_result(op, Some(ord)) as u8
4750 })
4751 .collect();
4752 Some(Data::Bool(out.into()))
4753}
4754
4755#[allow(clippy::too_many_arguments)]
4759fn scalar_dyad_data(
4760 op: ScalarDyad,
4761 x: &Data,
4762 xoff: usize,
4763 xdiv: usize,
4764 y: &Data,
4765 yoff: usize,
4766 ydiv: usize,
4767 n: usize,
4768 tol: Tol,
4769 rules: Rules,
4770 span: Span,
4771) -> Result<Data> {
4772 use ScalarDyad::*;
4773 if x.dtype() == DType::Symbol || y.dtype() == DType::Symbol {
4774 match op {
4775 Eq | Ne | Lt | Le | Gt | Ge => {}
4777 Min | Max => {
4780 return symbol_min_max(op, x, xoff, xdiv, y, yoff, ydiv, n, span);
4781 }
4782 _ => return Err(symbol_arith(span)),
4783 }
4784 }
4785 if matches!(op, Eq | Ne | Lt | Le | Gt | Ge) {
4786 return compare_data(op, x, xoff, xdiv, y, yoff, ydiv, n, tol, span);
4787 }
4788 if matches!(op, Lcm | Gcd) {
4789 return lcm_gcd_data(op, x, xoff, xdiv, y, yoff, ydiv, n, tol, rules, span);
4790 }
4791 let t = arith_type(x.dtype(), y.dtype(), span)?;
4792 if t.is_exact()
4793 && let Some(d) = exact_dyad_data(op, t, x, xoff, xdiv, y, yoff, ydiv, n, span)?
4794 {
4795 return Ok(d);
4796 }
4797 if t == DType::I64 && !matches!(op, DivJ | DivApl | Log | Root | Circle) {
4799 if let Some(d) = int_dyad_data(op, x, xoff, xdiv, y, yoff, ydiv, n) {
4802 return Ok(d);
4803 }
4804 }
4806 if t == DType::Complex
4807 || matches!(op, MakeComplex | PolarBy)
4808 || pass_leaves_reals(op, x, xoff, xdiv, y, yoff, ydiv, n)
4809 {
4810 let data = complex_dyad_data(op, x, xoff, xdiv, y, yoff, ydiv, n, span)?;
4811 if op == Log
4815 && rules.lang == crate::Lang::Apl
4816 && let Data::Complex(v) = &data
4817 && v.iter().any(|z| !z[0].is_finite() || !z[1].is_finite())
4818 {
4819 return Err(Error::domain("this logarithm has no value", span));
4820 }
4821 if op == Circle && circle_reads_a_part(x, xoff, xdiv, n) && let Data::Complex(v) = &data {
4822 return Ok(Data::F64(v.iter().map(|z| z[0]).collect()));
4823 }
4824 return Ok(data);
4825 }
4826 float_dyad_data(op, x, xoff, xdiv, y, yoff, ydiv, n, tol, span)
4827}
4828
4829fn frame_pervaded(frame: Vec<usize>, cells: Vec<Array>, span: Span) -> Result<Array> {
4834 if cells.iter().all(|c| c.rank() == 0 && c.dtype() != DType::Box) {
4835 return assemble(&frame, cells, span);
4836 }
4837 let boxes: Vec<Array> = cells.into_iter().collect();
4838 Ok(Array::new(frame, Data::Box(boxes.into())))
4839}
4840
4841fn pervade_dyad(
4847 op: ScalarDyad,
4848 x: &Array,
4849 y: &Array,
4850 cfg: EvalCfg,
4851 span: Span,
4852) -> Result<Array> {
4853 let p = agree(&x.shape, &y.shape, &x.shape, &y.shape, cfg.agreement, span)?;
4854 if p.n == 0 {
4855 return Ok(Array::new(p.frame, Data::empty(DType::Box)));
4856 }
4857 let (xr, yr) = (x.to_row_major(), y.to_row_major());
4858 let mut cells = Vec::with_capacity(p.n);
4859 for i in 0..p.n {
4860 let a = open_cell(&atom(&xr, i / p.x_div));
4861 let b = open_cell(&atom(&yr, i / p.y_div));
4862 cells.push(scalar_dyad(op, &a, &b, cfg, span)?);
4863 }
4864 frame_pervaded(p.frame, cells, span)
4865}
4866
4867fn pervade_monad(op: ScalarMonad, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
4869 if y.count() == 0 {
4870 return Ok(Array::new(y.shape.clone(), Data::empty(DType::Box)));
4871 }
4872 let yr = y.to_row_major();
4873 let mut cells = Vec::with_capacity(y.count());
4874 for i in 0..y.count() {
4875 let a = open_cell(&atom(&yr, i));
4876 cells.push(scalar_monad(op, &a, cfg, span)?);
4877 }
4878 frame_pervaded(y.shape.clone(), cells, span)
4879}
4880
4881fn as_real(a: &Array) -> Option<Array> {
4885 if a.dtype() != DType::Complex {
4886 return Some(a.clone());
4887 }
4888 let real: Option<Vec<f64>> = a.to_f64_vec();
4889 Some(Array::new(a.shape.clone(), Data::F64(real?.into())))
4890}
4891
4892fn scalar_dyad(
4893 op: ScalarDyad,
4894 x: &Array,
4895 y: &Array,
4896 cfg: EvalCfg,
4897 span: Span,
4898) -> Result<Array> {
4899 if cfg.rules.lang == crate::Lang::Apl
4900 && (x.dtype() == DType::Box || y.dtype() == DType::Box)
4901 {
4902 return pervade_dyad(op, x, y, cfg, span);
4903 }
4904 if matches!(op, ScalarDyad::Min | ScalarDyad::Max | ScalarDyad::Lt
4910 | ScalarDyad::Le | ScalarDyad::Gt | ScalarDyad::Ge)
4911 && (x.dtype() == DType::Complex || y.dtype() == DType::Complex)
4912 && let (Some(a), Some(b)) = (as_real(x), as_real(y))
4913 {
4914 return scalar_dyad(op, &a, &b, cfg, span);
4915 }
4916 let p = agree(&x.shape, &y.shape, &x.shape, &y.shape, cfg.agreement, span)?;
4917 if p.n == 0 {
4921 return Ok(Array::new(p.frame, Data::empty(empty_result_type(x, y))));
4922 }
4923 let data = scalar_dyad_data(
4924 op,
4925 &x.data,
4926 0,
4927 p.x_div,
4928 &y.data,
4929 0,
4930 p.y_div,
4931 p.n,
4932 cfg.tol,
4933 cfg.rules,
4934 span,
4935 )?;
4936 Ok(Array::new(p.frame, data))
4937}
4938
4939fn empty_result_type(x: &Array, y: &Array) -> DType {
4942 for a in [x, y] {
4943 if a.dtype().is_numeric() {
4944 return a.dtype();
4945 }
4946 }
4947 DType::I64
4948}
4949
4950fn fits_i64(v: f64) -> bool {
4952 v.is_finite() && v >= i64::MIN as f64 && v < i64::MAX as f64
4953}
4954
4955fn monad_leaves_reals(op: ScalarMonad, d: &Data) -> bool {
4957 use ScalarMonad::*;
4958 match op {
4959 Imaginary | Polar => d.dtype().is_numeric(),
4961 Sqrt | Ln => match d {
4962 Data::I64(v) => par::any(v, |&x| x < 0),
4963 Data::F64(v) => par::any(v, |&x| x < 0.0),
4964 Data::Ext(v) => v.iter().any(|x| x.sign() == num_bigint::Sign::Minus),
4965 Data::Rat(v) => v.iter().any(|x| x < &Rat::zero()),
4966 _ => false,
4967 },
4968 _ => false,
4969 }
4970}
4971
4972fn complex_monad(op: ScalarMonad, y: &Array, span: Span) -> Result<Array> {
4974 use ScalarMonad::*;
4975 let mut tmp = Vec::new();
4976 let v = borrow_cx(&y.data, &mut tmp);
4977 if y.count() > 0 && v.is_empty() {
4978 return Err(wrong_type(y.dtype(), span));
4979 }
4980 let data = match op {
4981 Abs => Data::F64(par::map(v, |&z| cx::abs(z)).into()),
4983 Not => return Err(Error::domain("logical negation needs values of 0 or 1", span)),
4984 Factorial => {
4985 return Err(Error::not_yet("the factorial of a complex number", span));
4986 }
4987 _ => {
4988 let step: fn(Cx) -> Cx = match op {
4989 Conj => cx::conj,
4990 Neg => cx::neg,
4991 Signum => cx::signum,
4992 Recip => cx::recip,
4993 Sqrt => cx::sqrt,
4994 Exp => cx::exp,
4995 Ln => cx::ln,
4996 Floor => cx::floor,
4997 Ceil => cx::ceil,
4998 OneMinus => |z| cx::sub(cx::ONE, z),
4999 Inc => |z| cx::add(z, cx::ONE),
5000 Dec => |z| cx::sub(z, cx::ONE),
5001 Double => |z| cx::add(z, z),
5002 Halve => |z| [z[0] / 2.0, z[1] / 2.0],
5003 Square => |z| cx::mul(z, z),
5004 Pi => |z| [std::f64::consts::PI * z[0], std::f64::consts::PI * z[1]],
5005 Imaginary => |z| cx::mul(cx::I, z),
5006 Polar => |z| cx::exp(cx::mul(cx::I, z)),
5007 Abs | Not | Factorial => unreachable!("handled above"),
5008 };
5009 Data::Complex(par::map(v, |&z| step(z)).into())
5010 }
5011 };
5012 Ok(Array::new(y.shape.clone(), data).with_layout(y.layout()))
5013}
5014
5015fn scalar_monad(op: ScalarMonad, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
5017 use ScalarMonad::*;
5018 if cfg.rules.lang == crate::Lang::Apl && y.dtype() == DType::Box {
5019 return pervade_monad(op, y, cfg, span);
5020 }
5021 let tol = cfg.tol;
5022 let d = &y.data;
5023 if y.count() == 0 && !d.dtype().is_numeric() {
5026 return Ok(Array::new(y.shape.clone(), Data::empty(DType::I64)));
5027 }
5028 if d.dtype() == DType::Complex || monad_leaves_reals(op, d) {
5029 return complex_monad(op, y, span);
5030 }
5031 if d.dtype().is_exact() && let Some(a) = exact_monad(op, y) {
5032 return Ok(a);
5033 }
5034 let mut tmp = Vec::new();
5038 let data = match op {
5039 Conj if d.dtype().is_numeric() => d.clone(),
5041 Conj => return Err(wrong_type(d.dtype(), span)),
5042 Imaginary | Polar => return Err(Error::internal("a complex monad on the real path")),
5045 Neg => match d {
5046 Data::Bool(v) => Data::I64(par::map(v, |&b| -(b as i64)).into()),
5047 Data::I64(v) => match par::try_map(v, i64::checked_neg) {
5048 Some(out) => Data::I64(out.into()),
5049 None => Data::F64(par::map(v, |&x| -(x as f64)).into()),
5050 },
5051 Data::F64(v) => Data::F64(par::map(v, |&x| -x).into()),
5052 _ => return Err(wrong_type(d.dtype(), span)),
5053 },
5054 Signum => match d {
5055 Data::Bool(v) => Data::I64(par::map(v, |&b| b as i64).into()),
5056 Data::I64(v) => Data::I64(par::map(v, |&x| x.signum()).into()),
5057 Data::F64(v) => Data::F64(
5060 par::map(v, |&x| {
5061 if tol.is_zero(x) {
5062 0.0
5063 } else if x > 0.0 {
5064 1.0
5065 } else if x < 0.0 {
5066 -1.0
5067 } else {
5068 0.0
5069 }
5070 })
5071 .into(),
5072 ),
5073 _ => return Err(wrong_type(d.dtype(), span)),
5074 },
5075 Recip => {
5076 let v = as_f64(d, &mut tmp, span)?;
5081 if !tol.is_j() && par::any(v, |&x| x == 0.0) {
5082 return Err(Error::domain("zero has no reciprocal", span));
5083 }
5084 Data::F64(par::map(v, |&x| if x == 0.0 { f64::INFINITY } else { 1.0 / x }).into())
5085 }
5086 Sqrt => {
5087 let v = as_f64(d, &mut tmp, span)?;
5089 Data::F64(par::map(v, |&x| x.sqrt()).into())
5090 }
5091 Exp => {
5092 let v = as_f64(d, &mut tmp, span)?;
5093 Data::F64(par::map(v, |&x| x.exp()).into())
5094 }
5095 Abs => match d {
5096 Data::Bool(_) => d.clone(),
5097 Data::I64(v) => match par::try_map(v, i64::checked_abs) {
5098 Some(out) => Data::I64(out.into()),
5099 None => Data::F64(par::map(v, |&x| (x as f64).abs()).into()),
5100 },
5101 Data::F64(v) => Data::F64(par::map(v, |&x| x.abs()).into()),
5102 _ => return Err(wrong_type(d.dtype(), span)),
5103 },
5104 Floor | Ceil => match d {
5105 Data::Bool(v) => Data::I64(par::map(v, |&b| b as i64).into()),
5106 Data::I64(_) => d.clone(),
5107 Data::F64(v) => {
5108 let round = |x: f64| if op == Floor { tol.floor(x) } else { tol.ceil(x) };
5109 match par::try_map(v, |x| {
5111 let r = round(x);
5112 fits_i64(r).then_some(r as i64)
5113 }) {
5114 Some(out) => Data::I64(out.into()),
5115 None => Data::F64(par::map(v, |&x| round(x)).into()),
5116 }
5117 }
5118 _ => return Err(wrong_type(d.dtype(), span)),
5119 },
5120 Inc | Dec => {
5121 let step = if op == Inc { 1i64 } else { -1 };
5122 match d {
5123 Data::Bool(v) => Data::I64(par::map(v, |&b| b as i64 + step).into()),
5124 Data::I64(v) => match par::try_map(v, |x: i64| x.checked_add(step)) {
5125 Some(out) => Data::I64(out.into()),
5126 None => Data::F64(par::map(v, |&x| x as f64 + step as f64).into()),
5127 },
5128 Data::F64(v) => Data::F64(par::map(v, |&x| x + step as f64).into()),
5129 _ => return Err(wrong_type(d.dtype(), span)),
5130 }
5131 }
5132 Double | Square => match d {
5133 Data::Bool(v) => {
5134 Data::I64(par::map(v, |&b| if op == Double { 2 * b as i64 } else { b as i64 }).into())
5135 }
5136 Data::I64(v) => {
5137 let f = |x: i64| if op == Double { x.checked_mul(2) } else { x.checked_mul(x) };
5138 match par::try_map(v, f) {
5139 Some(out) => Data::I64(out.into()),
5140 None => Data::F64(
5141 par::map(v, |&x| {
5142 let x = x as f64;
5143 if op == Double { x + x } else { x * x }
5144 })
5145 .into(),
5146 ),
5147 }
5148 }
5149 Data::F64(v) => {
5150 Data::F64(par::map(v, |&x| if op == Double { x + x } else { x * x }).into())
5151 }
5152 _ => return Err(wrong_type(d.dtype(), span)),
5153 },
5154 Halve => {
5155 let v = as_f64(d, &mut tmp, span)?;
5156 Data::F64(par::map(v, |&x| x / 2.0).into())
5157 }
5158 Pi => {
5159 let v = as_f64(d, &mut tmp, span)?;
5160 Data::F64(par::map(v, |&x| std::f64::consts::PI * x).into())
5161 }
5162 Factorial => {
5163 let v = as_f64(d, &mut tmp, span)?;
5164 let out = par::map(v, |&x| factorial_as(x, tol));
5165 if tol.is_j() {
5166 if v.iter().zip(&out).any(|(&x, &r)| tol.made_nan(r, x, 0.0)) {
5170 return Err(Error::nan("`! __` has no value", span));
5171 }
5172 } else if par::any(&out, |v: &f64| !v.is_finite()) {
5173 return Err(Error::domain("this factorial has no value", span));
5177 }
5178 Data::F64(out.into())
5179 }
5180 Ln => {
5181 let v = as_f64(d, &mut tmp, span)?;
5183 if !tol.is_j() && par::any(v, |&x| x == 0.0) {
5187 return Err(Error::domain("zero has no logarithm", span));
5188 }
5189 Data::F64(par::map(v, |&x| x.ln()).into())
5190 }
5191 OneMinus => match d {
5192 Data::Bool(v) => Data::Bool(par::map(v, |&b| 1 - b).into()),
5193 Data::I64(v) => match par::try_map(v, |x: i64| 1i64.checked_sub(x)) {
5194 Some(out) => Data::I64(out.into()),
5195 None => Data::F64(par::map(v, |&x| 1.0 - x as f64).into()),
5196 },
5197 Data::F64(v) => Data::F64(par::map(v, |&x| 1.0 - x).into()),
5198 _ => return Err(wrong_type(d.dtype(), span)),
5199 },
5200 Not => {
5201 let bad = || Error::domain("logical negation needs values of 0 or 1", span);
5202 match d {
5203 Data::Bool(v) => Data::Bool(par::map(v, |&b| 1 - b).into()),
5204 Data::I64(v) => {
5205 let out = par::try_map(v, |x: i64| match x {
5206 0 => Some(1u8),
5207 1 => Some(0u8),
5208 _ => None,
5209 })
5210 .ok_or_else(bad)?;
5211 Data::Bool(out.into())
5212 }
5213 Data::F64(v) => {
5214 let out = par::try_map(v, |x: f64| {
5215 if x == 0.0 {
5216 Some(1u8)
5217 } else if x == 1.0 {
5218 Some(0u8)
5219 } else {
5220 None
5221 }
5222 })
5223 .ok_or_else(bad)?;
5224 Data::Bool(out.into())
5225 }
5226 _ => return Err(bad()),
5227 }
5228 }
5229 };
5230 Ok(Array::new(y.shape.clone(), data).with_layout(y.layout()))
5231}
5232
5233fn transpose_axes(y: &Array) -> Array {
5243 if y.rank() < 2 {
5244 return y.clone();
5245 }
5246 let out_shape: Vec<usize> = y.shape.iter().rev().copied().collect();
5247 let flipped = match y.layout() {
5248 Layout::RowMajor => Layout::ColMajor,
5249 Layout::ColMajor => Layout::RowMajor,
5250 };
5251 Array::new(out_shape, y.data.clone()).with_layout(flipped)
5252}
5253
5254fn iota_j(y: &Array, near: NearInt, span: Span) -> Result<Array> {
5257 if y.rank() > 1 {
5258 return Err(Error::new(
5259 ErrorKind::Rank,
5260 "index generator needs a scalar or vector argument",
5261 Some(span),
5262 ));
5263 }
5264 let dims = y
5265 .to_i64_vec_near(near)
5266 .ok_or_else(|| Error::domain("index generator needs integer lengths", span))?;
5267 let shape: Vec<usize> = dims.iter().map(|d| d.unsigned_abs() as usize).collect();
5268 let n = crate::limits::elements(&shape, span)?;
5269 let st = strides(&shape);
5270 let mut out = Vec::with_capacity(n);
5271 let mut coord = vec![0usize; shape.len()];
5272 for _ in 0..n {
5273 let mut v = 0usize;
5274 for k in 0..shape.len() {
5275 let c = if dims[k] < 0 { shape[k] - 1 - coord[k] } else { coord[k] };
5276 v += c * st[k];
5277 }
5278 out.push(v as i64);
5279 odometer(&mut coord, &shape);
5280 }
5281 let data = Data::I64(out.into());
5282 let data = if y.dtype() == DType::Ext {
5285 data.cast(DType::Ext).ok_or_else(|| Error::internal("integers have no extended form"))?
5286 } else {
5287 data
5288 };
5289 Ok(Array::new(shape, data))
5290}
5291
5292fn head(y: &Array) -> Array {
5294 if y.rank() == 0 {
5295 return y.clone();
5296 }
5297 if y.items() == 0 {
5298 let cell_shape = y.shape[1..].to_vec();
5299 let n: usize = cell_shape.iter().product();
5300 return Array::new(cell_shape, fill_data(y.dtype(), n));
5301 }
5302 y.item(0)
5303}
5304
5305fn behead(y: &Array, span: Span) -> Result<Array> {
5306 if y.rank() == 0 {
5307 return Err(Error::domain("cannot drop the first item of a scalar", span));
5308 }
5309 if y.items() == 0 {
5310 return Ok(y.clone());
5311 }
5312 let m = y.item_size();
5313 let mut shape = y.shape.clone();
5314 shape[0] -= 1;
5315 Ok(Array::new(shape, y.data.slice(m, y.count())))
5316}
5317
5318fn tail(y: &Array) -> Array {
5320 if y.rank() == 0 {
5321 return y.clone();
5322 }
5323 let n = y.items();
5324 if n == 0 {
5325 let cell_shape = y.shape[1..].to_vec();
5326 let m: usize = cell_shape.iter().product();
5327 return Array::new(cell_shape, fill_data(y.dtype(), m));
5328 }
5329 y.item(n - 1)
5330}
5331
5332fn curtail(y: &Array) -> Array {
5334 if y.rank() == 0 {
5335 return Array::empty(y.dtype());
5336 }
5337 let n = y.items();
5338 if n == 0 {
5339 return y.clone();
5340 }
5341 let m = y.item_size();
5342 let mut shape = y.shape.clone();
5343 shape[0] = n - 1;
5344 Array::new(shape, y.data.slice(0, (n - 1) * m))
5345}
5346
5347fn reverse(y: &Array) -> Array {
5349 if y.rank() == 0 {
5350 return y.clone();
5351 }
5352 let n = y.items();
5353 let m = y.item_size();
5354 let mut data = Data::empty(y.dtype());
5355 for i in (0..n).rev() {
5356 for k in 0..m {
5357 push_elem(&mut data, &y.data, i * m + k);
5358 }
5359 }
5360 Array::new(y.shape.clone(), data)
5361}
5362
5363fn rotate(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
5366 let counts = axis_counts(x, "rotate", near, span)?;
5367 if y.rank() == 0 {
5368 return Ok(y.clone());
5369 }
5370 if counts.len() > y.rank() {
5371 return Err(Error::new(
5372 ErrorKind::Length,
5373 format!(
5374 "rotate has {} amounts for an argument of rank {}",
5375 counts.len(),
5376 y.rank()
5377 ),
5378 Some(span),
5379 ));
5380 }
5381 let st = strides(&y.shape);
5382 let n = y.count();
5383 let r = y.rank();
5384 let mut data = Data::empty(y.dtype());
5385 let mut coord = vec![0usize; r];
5386 for _ in 0..n {
5387 let mut idx = 0usize;
5388 for k in 0..r {
5389 let len = y.shape[k] as i64;
5391 let s = counts.get(k).copied().unwrap_or(0).rem_euclid(len);
5395 idx += (coord[k] as i64 + s).rem_euclid(len) as usize * st[k];
5396 }
5397 push_elem(&mut data, &y.data, idx);
5398 odometer(&mut coord, &y.shape);
5399 }
5400 Ok(Array::new(y.shape.clone(), data))
5401}
5402
5403fn rotate_apl(x: &Array, y: &Array, last: bool, near: NearInt, span: Span) -> Result<Array> {
5414 let scalar_like = x.rank() == 0 || (x.rank() == 1 && x.count() == 1);
5415 if y.rank() == 0 {
5418 return if scalar_like {
5419 Ok(y.clone())
5420 } else {
5421 Err(Error::new(
5422 ErrorKind::Rank,
5423 format!(
5424 "rotate has a rank-{} left argument for a scalar, which needs a scalar",
5425 x.rank()
5426 ),
5427 Some(span),
5428 ))
5429 };
5430 }
5431 let axis = if last { y.rank() - 1 } else { 0 };
5432 let want: Vec<usize> =
5433 y.shape.iter().enumerate().filter(|&(k, _)| k != axis).map(|(_, &n)| n).collect();
5434 if !scalar_like {
5435 if x.rank() != want.len() {
5436 return Err(Error::new(
5437 ErrorKind::Rank,
5438 format!(
5439 "rotate has a rank-{} left argument for axis {axis} of {}, which needs rank {}",
5440 x.rank(),
5441 show_shape(&y.shape),
5442 want.len()
5443 ),
5444 Some(span),
5445 ));
5446 }
5447 if x.shape != want {
5448 return Err(Error::new(
5449 ErrorKind::Length,
5450 format!(
5451 "rotate has a {} left argument for axis {axis} of {}, which needs {}",
5452 show_shape(&x.shape),
5453 show_shape(&y.shape),
5454 show_shape(&want)
5455 ),
5456 Some(span),
5457 ));
5458 }
5459 }
5460 let counts = x
5461 .to_i64_vec_near(near)
5462 .ok_or_else(|| Error::domain("rotate needs integer lengths", span))?;
5463 let len = y.shape[axis] as i64;
5464 let n = y.count();
5465 if n == 0 {
5466 return Ok(y.clone());
5467 }
5468 let st = strides(&y.shape);
5469 let r = y.rank();
5470 let mut data = Data::empty(y.dtype());
5471 let mut coord = vec![0usize; r];
5472 for _ in 0..n {
5473 let mut which = 0usize;
5475 for (k, &c) in coord.iter().enumerate() {
5476 if k != axis {
5477 which = which * y.shape[k] + c;
5478 }
5479 }
5480 let s = if scalar_like { counts[0] } else { counts[which] };
5481 let s = s.rem_euclid(len);
5484 let mut idx = 0usize;
5485 for (k, &c) in coord.iter().enumerate() {
5486 let c = if k == axis { (c as i64 + s).rem_euclid(len) as usize } else { c };
5487 idx += c * st[k];
5488 }
5489 push_elem(&mut data, &y.data, idx);
5490 odometer(&mut coord, &y.shape);
5491 }
5492 Ok(Array::new(y.shape.clone(), data))
5493}
5494
5495fn elem_key(d: &Data, i: usize) -> u64 {
5498 match d {
5499 Data::Bool(v) => v[i] as u64,
5500 Data::I64(v) => v[i] as u64,
5501 Data::F64(v) => {
5502 let x = v[i];
5503 if x == 0.0 { 0 } else { x.to_bits() }
5504 }
5505 Data::Complex(v) => cx_key(v[i]),
5506 Data::Char(v) => v[i] as u64,
5507 Data::Symbol(v) => v[i] as u64,
5509 Data::Ext(_) | Data::Rat(_) | Data::Box(_) => 0,
5512 }
5513}
5514
5515fn num_key(d: &Data, i: usize) -> u64 {
5518 match d {
5519 Data::Bool(v) => (v[i] as f64).to_bits(),
5520 Data::I64(v) => (v[i] as f64).to_bits(),
5521 Data::F64(v) => {
5522 let x = v[i];
5523 if x == 0.0 { 0.0f64.to_bits() } else { x.to_bits() }
5524 }
5525 Data::Complex(v) => cx_key(v[i]),
5526 Data::Char(v) => v[i] as u64,
5527 Data::Symbol(v) => v[i] as u64,
5528 Data::Ext(_) | Data::Rat(_) | Data::Box(_) => 0,
5530 }
5531}
5532
5533fn cx_key(z: Cx) -> u64 {
5535 let bits = |x: f64| if x == 0.0 { 0u64 } else { x.to_bits() };
5536 bits(z[0]) ^ bits(z[1]).rotate_left(32)
5537}
5538
5539fn nub(y: &Array, tol: Tol) -> Array {
5541 if y.rank() == 0 {
5542 return Array::new(vec![1], y.data.clone());
5543 }
5544 let n = y.items();
5545 let m = y.item_size();
5546 let mut keep = Vec::new();
5547 if y.dtype() == DType::Box || y.dtype().is_exact() {
5548 for i in 0..n {
5551 if !keep.iter().any(|&j| arrays_match(&y.item(i), &y.item(j), tol)) {
5552 keep.push(i);
5553 }
5554 }
5555 } else if y.dtype() == DType::F64 && tol.ct != 0.0 {
5556 let mut tv = Vec::new();
5559 let v = borrow_f64(&y.data, &mut tv);
5560 for i in 0..n {
5561 if !keep.iter().any(|&j| (0..m).all(|k| tol.eq(v[i * m + k], v[j * m + k]))) {
5562 keep.push(i);
5563 }
5564 }
5565 } else {
5566 let mut seen: HashSet<Vec<u64>> = HashSet::with_capacity(n);
5567 for i in 0..n {
5568 let key: Vec<u64> = (0..m).map(|k| elem_key(&y.data, i * m + k)).collect();
5569 if seen.insert(key) {
5570 keep.push(i);
5571 }
5572 }
5573 }
5574 let mut data = Data::empty(y.dtype());
5575 for &i in &keep {
5576 for k in 0..m {
5577 push_elem(&mut data, &y.data, i * m + k);
5578 }
5579 }
5580 let mut shape = y.shape.clone();
5581 shape[0] = keep.len();
5582 Array::new(shape, data)
5583}
5584
5585#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5590enum Tao {
5591 J,
5592 Apl2,
5593 Dyalog,
5595}
5596
5597impl Tao {
5598 fn of(rules: Rules) -> Tao {
5599 match rules.lang {
5600 crate::Lang::J => Tao::J,
5601 crate::Lang::Apl => match rules.nested_grade {
5602 NestedGrade::Apl2 => Tao::Apl2,
5603 NestedGrade::TotalOrder => Tao::Dyalog,
5604 },
5605 }
5606 }
5607
5608 fn class(self, dt: DType) -> u8 {
5614 match self {
5615 Tao::J => match dt {
5616 DType::Symbol => 1,
5617 DType::Char => 2,
5618 DType::Box => 3,
5619 _ => 0,
5620 },
5621 Tao::Apl2 => match dt {
5622 DType::Char | DType::Symbol => 0,
5623 DType::Box => 2,
5624 _ => 1,
5625 },
5626 Tao::Dyalog => match dt {
5632 DType::Char | DType::Symbol => 2,
5633 DType::Box => 1,
5634 _ => 0,
5635 },
5636 }
5637 }
5638}
5639
5640#[derive(Clone, Copy, Debug)]
5648struct Grading {
5649 tao: Tao,
5650 tol: Tol,
5651}
5652
5653impl Grading {
5654 fn of(rules: Rules, tol: Tol) -> Grading {
5655 let tao = Tao::of(rules);
5656 let exact = tao == Tao::J || tao == Tao::Dyalog;
5660 Grading { tao, tol: if exact { Tol { ct: 0.0, ..tol } } else { tol } }
5661 }
5662
5663 fn class(self, dt: DType) -> u8 {
5664 self.tao.class(dt)
5665 }
5666}
5667
5668fn cmp_items_total(x: &Array, y: &Array, ord: Grading) -> std::cmp::Ordering {
5683 use std::cmp::Ordering::Equal;
5684 match ord.tao {
5685 Tao::Dyalog => cmp_items_dyalog(x, y, ord),
5686 Tao::J => {
5687 let class = |a: &Array| if a.count() == 0 { 0 } else { ord.class(a.dtype()) };
5688 class(x)
5689 .cmp(&class(y))
5690 .then_with(|| x.rank().cmp(&y.rank()))
5691 .then_with(|| x.shape.iter().rev().cmp(y.shape.iter().rev()))
5692 .then_with(|| cmp_atoms(x, y, ord))
5693 }
5694 Tao::Apl2 => x
5695 .rank()
5696 .cmp(&y.rank())
5697 .then_with(|| x.shape.iter().cmp(y.shape.iter()))
5698 .then_with(|| cmp_atoms(x, y, ord))
5699 .then_with(|| {
5700 if x.count() == 0 {
5701 ord.class(x.dtype()).cmp(&ord.class(y.dtype()))
5702 } else {
5703 Equal
5704 }
5705 }),
5706 }
5707}
5708
5709fn cmp_items_dyalog(x: &Array, y: &Array, ord: Grading) -> std::cmp::Ordering {
5724 use std::cmp::Ordering::{Equal, Greater, Less};
5725 if x.rank() == 0 && y.rank() == 0 && x.dtype() != DType::Box && y.dtype() != DType::Box {
5728 return cmp_atoms(x, y, ord);
5729 }
5730 let rank = x.rank().max(y.rank());
5731 let extend = |a: &Array| -> Vec<usize> {
5732 let mut s = vec![1usize; rank - a.rank()];
5733 s.extend_from_slice(&a.shape);
5734 s
5735 };
5736 let (sx, sy) = (extend(x), extend(y));
5737 let common: Vec<usize> = (0..rank).map(|k| sx[k].max(sy[k])).collect();
5738 let (xr, yr) = (x.to_row_major(), y.to_row_major());
5739 let (dx, dy) = (xr.row_major_data(), yr.row_major_data());
5740 let (stx, sty) = (strides(&sx), strides(&sy));
5741 let mut order = Equal;
5742 if !common.contains(&0) {
5743 let mut coord = vec![0usize; rank];
5744 loop {
5745 let inside = |s: &[usize]| (0..rank).all(|k| coord[k] < s[k]);
5746 let at = |st: &[usize]| -> usize { (0..rank).map(|k| coord[k] * st[k]).sum() };
5747 let here = match (inside(&sx), inside(&sy)) {
5748 (true, true) => {
5749 cmp_items_dyalog(&atom_array(dx, at(&stx)), &atom_array(dy, at(&sty)), ord)
5750 }
5751 (true, false) => Greater,
5753 (false, true) => Less,
5754 (false, false) => Equal,
5755 };
5756 if here != Equal {
5757 order = here;
5758 break;
5759 }
5760 odometer(&mut coord, &common);
5764 if coord.iter().all(|&c| c == 0) {
5765 break;
5766 }
5767 }
5768 }
5769 if order != Equal {
5770 return order;
5771 }
5772 match (proto_item(x), proto_item(y)) {
5775 (Some(px), Some(py)) => cmp_items_dyalog(&px, &py, ord),
5778 _ => ord.class(x.dtype()).cmp(&ord.class(y.dtype())),
5779 }
5780 .then_with(|| x.shape.iter().rev().cmp(y.shape.iter().rev()))
5781}
5782
5783fn proto_item(a: &Array) -> Option<Array> {
5789 if let Some(p) = a.proto() {
5790 return Some(p.clone());
5791 }
5792 match a.dtype() {
5793 DType::Box => None,
5794 dt => Some(Array::new(vec![], fill_data(dt, 1))),
5795 }
5796}
5797
5798fn atom_array(d: &Data, i: usize) -> Array {
5801 match d {
5802 Data::Box(v) => v[i].clone(),
5803 _ => {
5804 let mut one = Data::empty(d.dtype());
5805 push_elem(&mut one, d, i);
5806 Array::new(vec![], one)
5807 }
5808 }
5809}
5810
5811fn cmp_atoms(x: &Array, y: &Array, ord: Grading) -> std::cmp::Ordering {
5814 use std::cmp::Ordering::Equal;
5815 let n = x.count();
5816 if n == 0 {
5817 return Equal;
5818 }
5819 let (xr, yr) = (x.to_row_major(), y.to_row_major());
5820 let (dx, dy) = (xr.row_major_data(), yr.row_major_data());
5821 if matches!(dx, Data::Box(_)) || matches!(dy, Data::Box(_)) {
5822 return (0..n)
5823 .map(|i| cmp_items_total(&atom_array(dx, i), &atom_array(dy, i), ord))
5824 .find(|o| *o != Equal)
5825 .unwrap_or(Equal);
5826 }
5827 let classes = ord.class(dx.dtype()).cmp(&ord.class(dy.dtype()));
5829 if classes != Equal {
5830 return classes;
5831 }
5832 match (dx, dy) {
5833 (Data::Char(a), Data::Char(b)) => a[..n].cmp(&b[..n]),
5834 _ => cmp_numbers(dx, dy, n, ord.tol),
5835 }
5836}
5837
5838fn cmp_numbers(dx: &Data, dy: &Data, n: usize, tol: Tol) -> std::cmp::Ordering {
5842 use std::cmp::Ordering::Equal;
5843 let seek = |f: &dyn Fn(usize) -> std::cmp::Ordering| {
5844 (0..n).map(f).find(|o| *o != Equal).unwrap_or(Equal)
5845 };
5846 match DType::promote(dx.dtype(), dy.dtype()) {
5847 Some(DType::Complex) => {
5848 let (mut ta, mut tb) = (Vec::new(), Vec::new());
5849 let (a, b) = (borrow_cx(dx, &mut ta), borrow_cx(dy, &mut tb));
5850 seek(&|k| tol_ord(a[k][0], b[k][0], tol).then_with(|| tol_ord(a[k][1], b[k][1], tol)))
5851 }
5852 Some(DType::F64) => {
5853 let (mut ta, mut tb) = (Vec::new(), Vec::new());
5854 let (a, b) = (borrow_f64(dx, &mut ta), borrow_f64(dy, &mut tb));
5855 seek(&|k| tol_ord(a[k], b[k], tol))
5856 }
5857 Some(t) if t.is_exact() => match (to_rat_vec(dx), to_rat_vec(dy)) {
5858 (Some(a), Some(b)) => seek(&|k| a[k].cmp(&b[k])),
5859 _ => Equal,
5860 },
5861 None => Equal,
5863 Some(_) => {
5864 let (mut ta, mut tb) = (Vec::new(), Vec::new());
5865 let (a, b) = (borrow_i64(dx, &mut ta), borrow_i64(dy, &mut tb));
5866 seek(&|k| a[k].cmp(&b[k]))
5867 }
5868 }
5869}
5870
5871fn cmp_items(d: &Data, i: usize, j: usize, m: usize, ord: Grading) -> std::cmp::Ordering {
5875 use std::cmp::Ordering::Equal;
5876 let (a, b) = (i * m, j * m);
5877 let ord = |k: usize| match d {
5878 Data::Bool(v) => v[a + k].cmp(&v[b + k]),
5879 Data::I64(v) => v[a + k].cmp(&v[b + k]),
5880 Data::F64(v) => tol_ord(v[a + k], v[b + k], ord.tol),
5881 Data::Complex(v) => tol_ord(v[a + k][0], v[b + k][0], ord.tol)
5888 .then_with(|| tol_ord(v[a + k][1], v[b + k][1], ord.tol)),
5889 Data::Char(v) => v[a + k].cmp(&v[b + k]),
5890 Data::Symbol(v) => crate::symbol::cmp(v[a + k], v[b + k]),
5893 Data::Ext(v) => v[a + k].cmp(&v[b + k]),
5896 Data::Rat(v) => v[a + k].cmp(&v[b + k]),
5897 Data::Box(v) => cmp_items_total(&v[a + k], &v[b + k], ord),
5900 };
5901 (0..m).map(ord).find(|o| *o != Equal).unwrap_or(Equal)
5902}
5903
5904fn grade_order(y: &Array, down: bool, ord: Grading) -> Vec<usize> {
5906 if y.rank() == 0 {
5907 return vec![0];
5908 }
5909 let n = y.items();
5910 let m = y.item_size();
5911 let mut idx: Vec<usize> = (0..n).collect();
5912 if down {
5915 idx.sort_by(|&a, &b| cmp_items(&y.data, b, a, m, ord));
5916 } else {
5917 idx.sort_by(|&a, &b| cmp_items(&y.data, a, b, m, ord));
5918 }
5919 idx
5920}
5921
5922fn collate_grade(x: &Array, y: &Array, down: bool, origin: i64, span: Span) -> Result<Array> {
5927 let chars_of = |a: &Array| -> Result<Vec<char>> {
5928 match a.row_major_data() {
5929 Data::Char(v) => Ok(v.as_slice().to_vec()),
5930 _ => Err(Error::domain("a collating grade takes characters", span)),
5931 }
5932 };
5933 let (xs, ys) = (chars_of(x)?, chars_of(y)?);
5934 let xshape = if x.rank() == 0 { vec![1] } else { x.shape.clone() };
5935 let width = xshape.len();
5936 let absent: Vec<usize> = xshape.iter().rev().copied().collect();
5940 let mut keys: std::collections::HashMap<char, Vec<usize>> =
5941 std::collections::HashMap::new();
5942 let xst = strides(&xshape);
5943 for (i, &c) in xs.iter().enumerate() {
5944 keys.entry(c).or_insert_with(|| {
5945 (0..width).map(|a| (i / xst[a]) % xshape[a]).rev().collect()
5946 });
5947 }
5948 let key_of = |c: char| keys.get(&c).unwrap_or(&absent).clone();
5949 let n = if y.rank() == 0 { 1 } else { y.items() };
5950 let m = if n == 0 { 0 } else { ys.len() / n };
5951 let item_keys: Vec<Vec<usize>> = (0..n)
5952 .map(|i| ys[i * m..(i + 1) * m].iter().flat_map(|&c| key_of(c)).collect())
5953 .collect();
5954 let mut idx: Vec<usize> = (0..n).collect();
5955 if down {
5956 idx.sort_by(|&a, &b| item_keys[b].cmp(&item_keys[a]));
5957 } else {
5958 idx.sort_by(|&a, &b| item_keys[a].cmp(&item_keys[b]));
5959 }
5960 Ok(Array::from_i64(idx.into_iter().map(|i| origin + i as i64).collect()))
5961}
5962
5963fn atomic_rep(y: &Array, ctx: &Ctx<'_>, span: Span) -> Result<Array> {
5967 let name = match y.as_boxes() {
5968 Some([b]) if y.rank() == 0 => crate::gerund::text_of(b),
5969 _ => None,
5970 };
5971 let Some(name) = name else {
5972 return Err(Error::domain("5!:1 takes a boxed name", span));
5973 };
5974 if let Some(v) = ctx.env.verb(&name) {
5975 let ar = crate::gerund::verb_ar(v).ok_or_else(|| {
5976 Error::not_yet(
5977 format!("the atomic representation of {}", v.name()),
5978 span,
5979 )
5980 })?;
5981 return Ok(Array::boxed(ar.to_array()));
5982 }
5983 match ctx.env.get(&name) {
5984 Some(a) => Ok(Array::boxed(crate::gerund::Ar::Noun(a).to_array())),
5985 None => Err(Error::new(
5986 ErrorKind::Value,
5987 format!("undefined name: {name}"),
5988 Some(span),
5989 )),
5990 }
5991}
5992
5993fn catalogue(y: &Array, span: Span) -> Result<Array> {
5997 let items = if y.rank() == 0 { vec![y.clone()] } else { y.cells(1) };
5998 let opened: Vec<Array> = items
6000 .iter()
6001 .map(|it| match it.as_boxes() {
6002 Some(bs) if it.rank() == 0 => bs[0].clone(),
6003 _ => it.clone(),
6004 })
6005 .collect();
6006 let mut shape: Vec<usize> = Vec::new();
6007 for o in &opened {
6008 shape.extend_from_slice(&o.shape);
6009 }
6010 let total: usize = shape.iter().product();
6011 let mut out = Vec::with_capacity(total);
6012 let mut coord = vec![0usize; shape.len()];
6013 for _ in 0..total {
6014 let mut at = 0usize;
6015 let mut picks = Vec::with_capacity(opened.len());
6016 for o in &opened {
6017 let st = strides(&o.shape);
6018 let idx: usize = (0..o.rank()).map(|a| coord[at + a] * st[a]).sum();
6019 at += o.rank();
6020 let mut data = Data::empty(o.dtype());
6021 push_elem(&mut data, o.row_major_data(), idx);
6022 picks.push(Array::new(vec![], data));
6023 }
6024 out.push(assemble(&[picks.len()], picks, span)?);
6025 odometer(&mut coord, &shape);
6026 }
6027 Ok(Array::new(shape, Data::Box(out.into())))
6028}
6029
6030fn raze_in(y: &Array, tol: Tol, span: Span) -> Result<Array> {
6033 let all = raze(y, span)?;
6034 let n = if all.rank() == 0 { 1 } else { all.items() };
6035 let elements: Vec<Array> = (0..y.count())
6036 .map(|i| {
6037 let mut data = Data::empty(y.dtype());
6038 push_elem(&mut data, y.row_major_data(), i);
6039 let one = Array::new(vec![], data);
6040 match one.as_boxes() {
6041 Some(bs) => bs[0].clone(),
6042 None => one,
6043 }
6044 })
6045 .collect();
6046 let mut out = Vec::with_capacity(elements.len() * n);
6047 for e in &elements {
6048 let row = member_j(&all, e, tol);
6049 out.extend_from_slice(row.to_i64_vec().unwrap_or_default().as_slice());
6050 }
6051 let mut shape = y.shape.clone();
6052 shape.push(n);
6053 Ok(Array::new(shape, Data::Bool(out.into_iter().map(|v| v as u8).collect::<Vec<u8>>().into())))
6054}
6055
6056fn select_items(y: &Array, order: &[usize]) -> Array {
6058 let m = y.item_size();
6059 let mut data = Data::empty(y.dtype());
6060 for &i in order {
6061 for k in 0..m {
6062 push_elem(&mut data, &y.data, i * m + k);
6063 }
6064 }
6065 let mut shape = y.shape.clone();
6066 shape[0] = order.len();
6067 Array::new(shape, data)
6068}
6069
6070fn check_gradable(y: &Array, rules: Rules, span: Span) -> Result<()> {
6075 if y.dtype() == DType::Complex && rules.complex_order != ComplexOrder::RealThenImaginary {
6076 return Err(Error::not_yet("grading complex values by magnitude and angle", span));
6077 }
6078 Ok(())
6079}
6080
6081fn grade_select(
6085 x: &Array,
6086 y: &Array,
6087 down: bool,
6088 rules: Rules,
6089 tol: Tol,
6090 span: Span,
6091) -> Result<Array> {
6092 check_gradable(y, rules, span)?;
6093 let order = grade_order(y, down, Grading::of(rules, tol));
6094 if x.rank() == 0 {
6098 if let Some(&past) = order.iter().find(|&&i| i > 0) {
6099 return Err(Error::domain(
6100 format!("index {past} is out of range: the argument has 1 item"),
6101 span,
6102 ));
6103 }
6104 return Ok(select_items(&as_list(x), &order));
6107 }
6108 if let Some(&past) = order.iter().find(|&&i| i >= x.items()) {
6109 return Err(Error::domain(
6110 format!("index {past} is out of range: the argument has {} items", x.items()),
6111 span,
6112 ));
6113 }
6114 Ok(select_items(x, &order))
6115}
6116
6117pub(crate) fn arrays_match(x: &Array, y: &Array, tol: Tol) -> bool {
6120 if x.shape != y.shape {
6121 return false;
6122 }
6123 if x.layout() != y.layout() {
6126 return arrays_match(&x.to_row_major(), &y.to_row_major(), tol);
6127 }
6128 if x.count() == 0 {
6131 return true;
6132 }
6133 if let (Data::Box(a), Data::Box(b)) = (&x.data, &y.data) {
6134 return a.iter().zip(b.iter()).all(|(p, q)| arrays_match(p, q, tol));
6135 }
6136 let (dx, dy) = (x.dtype(), y.dtype());
6137 match DType::promote(dx, dy) {
6138 None => false,
6139 Some(DType::Char) => match (&x.data, &y.data) {
6140 (Data::Char(a), Data::Char(b)) => a.as_slice() == b.as_slice(),
6141 _ => false,
6142 },
6143 Some(DType::Symbol) => match (&x.data, &y.data) {
6146 (Data::Symbol(a), Data::Symbol(b)) => a.as_slice() == b.as_slice(),
6147 _ => false,
6148 },
6149 Some(DType::F64) => {
6150 let (mut ta, mut tb) = (Vec::new(), Vec::new());
6151 let a = borrow_f64(&x.data, &mut ta);
6152 let b = borrow_f64(&y.data, &mut tb);
6153 a.iter().zip(b).all(|(p, q)| tol.eq(*p, *q))
6154 }
6155 Some(DType::Complex) => {
6156 let (mut ta, mut tb) = (Vec::new(), Vec::new());
6157 let a = borrow_cx(&x.data, &mut ta);
6158 let b = borrow_cx(&y.data, &mut tb);
6159 a.iter().zip(b).all(|(p, q)| tol.eq_cx(*p, *q))
6160 }
6161 Some(t) if t.is_exact() => match (to_rat_vec(&x.data), to_rat_vec(&y.data)) {
6162 (Some(a), Some(b)) => a == b,
6163 _ => false,
6164 },
6165 Some(_) => {
6166 let (mut ta, mut tb) = (Vec::new(), Vec::new());
6167 let a = borrow_i64(&x.data, &mut ta);
6168 let b = borrow_i64(&y.data, &mut tb);
6169 a.iter().zip(b).all(|(p, q)| p == q)
6170 }
6171 }
6172}
6173
6174fn item_or_self(a: &Array, i: usize) -> Array {
6176 if a.rank() == 0 { a.clone() } else { a.item(i) }
6177}
6178
6179fn member_j(x: &Array, y: &Array, tol: Tol) -> Array {
6182 let cell_rank = y.rank().saturating_sub(1).min(x.rank());
6183 let frame_rank = x.rank() - cell_rank;
6184 let frame: Vec<usize> = x.shape[..frame_rank].to_vec();
6185 let nf: usize = frame.iter().product();
6186 let items = y.items();
6187 let mut out = Vec::with_capacity(nf);
6188 for i in 0..nf {
6189 let cell = x.cell_at(frame_rank, i);
6190 out.push((0..items).any(|j| arrays_match(&cell, &item_or_self(y, j), tol)) as u8);
6191 }
6192 Array::new(frame, Data::Bool(out.into()))
6193}
6194
6195fn member_apl(x: &Array, y: &Array, tol: Tol) -> Array {
6197 let n = x.count();
6198 if x.dtype() == DType::Box
6199 || y.dtype() == DType::Box
6200 || x.dtype().is_exact()
6201 || y.dtype().is_exact()
6202 {
6203 let opened = |a: &Array, i: usize| -> Array {
6209 let e = atom(a, i);
6210 match e.as_boxes() {
6211 Some([b]) if b.rank() == 0 && b.dtype() != DType::Box => b.clone(),
6212 _ => e,
6213 }
6214 };
6215 let out: Vec<u8> = (0..n)
6216 .map(|i| {
6217 let e = opened(x, i);
6218 u8::from((0..y.count()).any(|j| arrays_match(&e, &opened(y, j), tol)))
6219 })
6220 .collect();
6221 return Array::new(x.shape.clone(), Data::Bool(out.into()));
6222 }
6223 if x.dtype() != y.dtype()
6224 && [x.dtype(), y.dtype()].iter().any(|&d| matches!(d, DType::Char | DType::Symbol))
6225 {
6226 return Array::new(x.shape.clone(), Data::Bool(vec![0u8; n].into()));
6227 }
6228 if tol.ct != 0.0
6229 && (x.dtype() == DType::F64 || y.dtype() == DType::F64)
6230 && x.dtype() != DType::Char
6231 {
6232 let (mut tx, mut ty) = (Vec::new(), Vec::new());
6234 let xs = borrow_f64(&x.data, &mut tx);
6235 let ys = borrow_f64(&y.data, &mut ty);
6236 let out: Vec<u8> =
6237 xs.iter().map(|a| ys.iter().any(|b| tol.eq(*a, *b)) as u8).collect();
6238 return Array::new(x.shape.clone(), Data::Bool(out.into()));
6239 }
6240 let seen: HashSet<u64> = (0..y.count()).map(|i| num_key(&y.data, i)).collect();
6241 let out: Vec<u8> =
6242 (0..n).map(|i| seen.contains(&num_key(&x.data, i)) as u8).collect();
6243 Array::new(x.shape.clone(), Data::Bool(out.into()))
6244}
6245
6246fn index_of(
6252 x: &Array,
6253 y: &Array,
6254 origin: i64,
6255 vector_left: bool,
6256 tol: Tol,
6257 span: Span,
6258) -> Result<Array> {
6259 if vector_left && x.rank() != 1 {
6260 return Err(Error::new(
6261 ErrorKind::Rank,
6262 format!("⍳ looks up in a vector, and its left argument has rank {}", x.rank()),
6263 Some(span),
6264 ));
6265 }
6266 let cell_rank = x.rank().saturating_sub(1).min(y.rank());
6267 let frame_rank = y.rank() - cell_rank;
6268 let frame: Vec<usize> = y.shape[..frame_rank].to_vec();
6269 let nf: usize = frame.iter().product();
6270 let items = x.items();
6271 let mut out = Vec::with_capacity(nf);
6272 for i in 0..nf {
6273 let cell = y.cell_at(frame_rank, i);
6274 let at = (0..items)
6275 .find(|&j| arrays_match(&cell, &item_or_self(x, j), tol))
6276 .unwrap_or(items);
6277 out.push(origin + at as i64);
6278 }
6279 Ok(Array::new(frame, Data::I64(out.into())))
6280}
6281
6282fn from_index(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
6284 if let Some(spec) = x.as_boxes().and_then(<[Array]>::first) {
6287 let spec = index_spec(spec, y, near, span)?;
6288 return Ok(select_spec(&spec, y));
6289 }
6290 let idx = x
6291 .to_i64_vec_near(near)
6292 .ok_or_else(|| Error::domain("index must be an integer", span))?;
6293 let Some(&i) = idx.first() else {
6294 return Err(Error::internal("from_index with no index"));
6295 };
6296 let n = y.items() as i64;
6297 let k = if i < 0 { i + n } else { i };
6298 if k < 0 || k >= n {
6299 return Err(Error::domain(
6300 format!("index {i} is out of range: the argument has {n} items"),
6301 span,
6302 ));
6303 }
6304 Ok(item_or_self(y, k as usize))
6305}
6306
6307fn cat_promote(
6311 a: &Array,
6312 other: &Array,
6313 rank: usize,
6314 axis: usize,
6315 deep: bool,
6316 span: Span,
6317) -> Result<Array> {
6318 if a.rank() == rank {
6319 return Ok(a.clone());
6320 }
6321 if a.rank() == 0 {
6322 let mut shape =
6323 if other.rank() == rank { other.shape.clone() } else { vec![1usize; rank] };
6324 shape[axis] = 1;
6325 let n: usize = shape.iter().product();
6326 let mut data = Data::empty(a.dtype());
6327 for _ in 0..n {
6328 push_elem(&mut data, &a.data, 0);
6329 }
6330 return Ok(Array::new(shape, data));
6331 }
6332 if a.rank() + 1 == rank || (deep && a.rank() < rank) {
6337 let mut shape = a.shape.clone();
6338 for _ in a.rank()..rank {
6339 shape.insert(axis, 1);
6340 }
6341 return Ok(Array::new(shape, a.data.clone()));
6342 }
6343 Err(Error::new(
6344 ErrorKind::Rank,
6345 format!("cannot catenate rank {} with rank {}", a.rank(), other.rank()),
6346 Some(span),
6347 ))
6348}
6349
6350fn empty_type(x: &Array, y: &Array) -> Option<DType> {
6359 match (x.count() == 0, y.count() == 0) {
6360 (true, false) => Some(y.dtype()),
6361 (false, true) => Some(x.dtype()),
6362 (true, true) => Some(match (x.dtype(), y.dtype()) {
6363 (DType::Box, _) | (_, DType::Box) => DType::Box,
6364 (DType::Char, _) | (_, DType::Char) => DType::Char,
6365 (a, b) => DType::promote(a, b)?,
6366 }),
6367 (false, false) => None,
6368 }
6369}
6370
6371pub(crate) fn catenate(
6373 x: &Array,
6374 y: &Array,
6375 leading: bool,
6376 fill: bool,
6377 span: Span,
6378) -> Result<Array> {
6379 let rank = x.rank().max(y.rank()).max(1);
6380 let axis = if leading { 0 } else { rank - 1 };
6381 let deep = fill && leading;
6382 let xa = cat_promote(x, y, rank, axis, deep, span)?;
6383 let ya = cat_promote(y, x, rank, axis, deep, span)?;
6384 let (xa, ya) = match empty_type(&xa, &ya)
6390 .filter(|_| fill && DType::promote(xa.dtype(), ya.dtype()).is_none())
6391 {
6392 None => (xa, ya),
6393 Some(dt) => {
6394 let retype = |a: Array| {
6395 if a.count() == 0 && a.dtype() != dt {
6396 Array::new(a.shape.clone(), Data::empty(dt))
6397 } else {
6398 a
6399 }
6400 };
6401 (retype(xa), retype(ya))
6402 }
6403 };
6404 let mut ragged = false;
6408 let want: Vec<i64> = (0..rank)
6409 .map(|k| {
6410 ragged |= k != axis && xa.shape[k] != ya.shape[k];
6411 xa.shape[k].max(ya.shape[k]) as i64
6412 })
6413 .collect();
6414 if ragged && !fill {
6415 return Err(Error::new(
6416 ErrorKind::Length,
6417 format!(
6418 "cannot catenate: left shape {}, right shape {}",
6419 show_shape(&xa.shape),
6420 show_shape(&ya.shape)
6421 ),
6422 Some(span),
6423 ));
6424 }
6425 let (xa, ya) = if ragged {
6426 let fit = |a: &Array| -> Result<Array> {
6427 let mut to = want.clone();
6428 to[axis] = a.shape[axis] as i64;
6429 take(&Array::from_i64(to), a, false, false, NearInt::J, span)
6432 };
6433 (fit(&xa)?, fit(&ya)?)
6434 } else {
6435 (xa, ya)
6436 };
6437 let (xa, ya) = if !fill && (xa.dtype() == DType::Box) != (ya.dtype() == DType::Box) {
6441 (nest_like(&xa, &ya), nest_like(&ya, &xa))
6442 } else {
6443 (xa, ya)
6444 };
6445 let mixing = !fill
6449 && xa.dtype() != DType::Box
6450 && ya.dtype() != DType::Box
6451 && DType::promote(xa.dtype(), ya.dtype()).is_none();
6452 let (xa, ya) =
6453 if mixing { (spread_scalars(&xa), spread_scalars(&ya)) } else { (xa, ya) };
6454 let dt = DType::promote(xa.dtype(), ya.dtype())
6455 .ok_or_else(|| {
6456 let boxed = xa.dtype() == DType::Box || ya.dtype() == DType::Box;
6457 let what = if boxed {
6458 "cannot catenate boxed and unboxed data; box the other side first"
6459 } else {
6460 "cannot catenate character and numeric data"
6461 };
6462 Error::new(ErrorKind::Type, what, Some(span))
6463 })?;
6464 let widen = |a: &Array| -> Result<Data> {
6465 if a.dtype() == dt {
6466 Ok(a.data.clone())
6467 } else if a.count() == 0 {
6468 Ok(Data::empty(dt))
6472 } else {
6473 a.data.cast(dt).ok_or_else(|| Error::internal("unsupported widening in catenate"))
6474 }
6475 };
6476 let xd = widen(&xa)?;
6477 let yd = widen(&ya)?;
6478 let outer: usize = xa.shape[..axis].iter().product();
6479 let ix: usize = xa.shape[axis..].iter().product();
6480 let iy: usize = ya.shape[axis..].iter().product();
6481 let mut data = Data::empty(dt);
6482 for o in 0..outer {
6483 for k in 0..ix {
6484 push_elem(&mut data, &xd, o * ix + k);
6485 }
6486 for k in 0..iy {
6487 push_elem(&mut data, &yd, o * iy + k);
6488 }
6489 }
6490 let mut shape = xa.shape.clone();
6491 shape[axis] = xa.shape[axis] + ya.shape[axis];
6492 Ok(Array::new(shape, data))
6493}
6494
6495fn copy_items(x: &Array, y: &Array, apl: bool, near: NearInt, span: Span) -> Result<Array> {
6503 let counts = x
6504 .to_i64_vec_near(near)
6505 .ok_or_else(|| Error::domain("replication counts must be integers", span))?;
6506 if !apl && counts.iter().any(|&c| c < 0) {
6507 return Err(Error::domain("replication counts must be nonnegative", span));
6508 }
6509 let one_item = apl && x.rank() > 0 && y.rank() > 0 && y.items() == 1 && counts.len() != 1;
6513 let scalar_y = y.rank() == 0 || one_item;
6514 let m = y.item_size();
6515 let n = if x.rank() == 0 || !scalar_y { y.items() } else { counts.len() };
6516 let per = if x.rank() == 0 { vec![counts[0]; n] } else { counts };
6517 if per.len() != n {
6518 return Err(Error::new(
6519 ErrorKind::Length,
6520 format!("{} replication count(s) for {n} item(s)", per.len()),
6521 Some(span),
6522 ));
6523 }
6524 let items: u128 = per.iter().map(|&c| c.unsigned_abs() as u128).sum();
6527 let total = crate::limits::count(items * m.max(1) as u128, span)? / m.max(1);
6528 let fill = if apl { prototype_of(y) } else { None };
6529 let mut data = Data::empty(y.dtype());
6530 for (i, &c) in per.iter().enumerate() {
6531 let src = if scalar_y { 0 } else { i };
6533 for _ in 0..c.unsigned_abs() {
6534 for k in 0..m {
6535 if c < 0 {
6536 push_gap(&mut data, &fill);
6537 } else {
6538 push_elem(&mut data, &y.data, src * m + k);
6539 }
6540 }
6541 }
6542 }
6543 let mut shape = if y.rank() == 0 { vec![1] } else { y.shape.clone() };
6546 shape[0] = total;
6547 Ok(keep_proto(Array::new(shape, data), y, apl))
6548}
6549
6550fn format_chars(y: &Array, opts: &FmtOpts) -> Array {
6559 if y.is_sparse() {
6562 let text = crate::fmt::format_array(y, opts);
6563 let lines: Vec<&str> = text.lines().collect();
6564 let width = lines.iter().map(|l| l.chars().count()).max().unwrap_or(0);
6565 let mut chars: Vec<char> = Vec::with_capacity(lines.len() * width);
6566 for line in &lines {
6567 chars.extend(line.chars());
6568 chars.resize(chars.len() + width - line.chars().count(), ' ');
6569 }
6570 return Array::new(vec![lines.len(), width], Data::Char(chars.into()));
6571 }
6572 if y.dtype() == DType::Char {
6573 return y.clone();
6574 }
6575 if y.count() == 0 {
6577 return Array::new(y.shape.clone(), Data::empty(DType::Char));
6578 }
6579 let text = crate::fmt::format_array(y, opts);
6580 if y.dtype() == DType::Box {
6581 let lines = text.lines().filter(|l| !l.is_empty()).count();
6586 let rows: usize =
6587 if y.rank() == 0 { 1 } else { y.shape[..y.rank() - 1].iter().product() };
6588 if lines != rows {
6589 return text_planes(&text, &y.shape[..y.rank().saturating_sub(2)]);
6590 }
6591 }
6592 if y.rank() < 2 {
6593 let chars: Vec<char> = text.chars().collect();
6594 return Array::new(vec![chars.len()], Data::Char(chars.into()));
6595 }
6596 let lines: Vec<&str> = text.lines().filter(|l| !l.is_empty()).collect();
6599 let width = lines.iter().map(|l| l.chars().count()).max().unwrap_or(0);
6600 let mut chars: Vec<char> = Vec::with_capacity(lines.len() * width);
6601 for line in &lines {
6602 chars.extend(line.chars());
6603 chars.resize(chars.len() + width - line.chars().count(), ' ');
6604 }
6605 let mut shape = y.shape[..y.rank() - 1].to_vec();
6608 shape.push(width);
6609 debug_assert_eq!(lines.len(), shape[..shape.len() - 1].iter().product::<usize>());
6610 Array::new(shape, Data::Char(chars.into()))
6611}
6612
6613fn text_planes(text: &str, frame: &[usize]) -> Array {
6616 let lines: Vec<&str> = text.lines().filter(|l| !l.is_empty()).collect();
6617 let width = lines.iter().map(|l| l.chars().count()).max().unwrap_or(0);
6618 let planes: usize = frame.iter().product::<usize>().max(1);
6619 let per = lines.len() / planes;
6620 let mut chars: Vec<char> = Vec::with_capacity(lines.len() * width);
6621 for line in &lines {
6622 chars.extend(line.chars());
6623 chars.resize(chars.len() + width - line.chars().count(), ' ');
6624 }
6625 let mut shape = frame.to_vec();
6626 shape.push(per);
6627 shape.push(width);
6628 Array::new(shape, Data::Char(chars.into()))
6629}
6630
6631fn digits_of(a: &Array, what: &str, span: Span) -> Result<Vec<f64>> {
6633 a.to_f64_vec().ok_or_else(|| Error::domain(format!("{what} needs numeric data"), span))
6634}
6635
6636fn narrow(values: Vec<f64>, integral: bool) -> Data {
6640 if integral && values.iter().all(|&v| v.fract() == 0.0 && fits_i64(v)) {
6641 return Data::I64(values.iter().map(|&v| v as i64).collect::<Vec<_>>().into());
6642 }
6643 Data::F64(values.into())
6644}
6645
6646fn is_integral(a: &Array) -> bool {
6648 !matches!(a.dtype(), DType::F64 | DType::Rat | DType::Char | DType::Symbol)
6649}
6650
6651fn decode_exact(x: Option<&Array>, y: &Array) -> Option<Array> {
6657 let yr = y.to_row_major();
6658 let digits = to_rat_vec(&yr.data)?;
6659 let two = Rat::from_int(Ext::from(2));
6660 let mut digits = digits;
6661 let radix: Vec<Rat> = match x {
6662 None => vec![two; digits.len()],
6663 Some(x) => {
6664 let r = to_rat_vec(&x.to_row_major().data)?;
6665 if y.rank() == 0 && r.len() != 1 {
6670 digits = vec![digits[0].clone(); r.len()];
6671 }
6672 match r.len() {
6673 1 => vec![r[0].clone(); digits.len()],
6674 n if n == digits.len() => r,
6675 _ => return None,
6676 }
6677 }
6678 };
6679 let mut acc = Rat::from_int(Ext::from(0));
6680 for (d, b) in digits.iter().zip(&radix) {
6681 acc = acc.mul(b).add(d);
6682 }
6683 let exact_in = |a: &Array| matches!(a.dtype(), DType::Ext | DType::Rat);
6684 if exact_in(y) || x.is_some_and(exact_in) {
6685 return Some(Array::new(Vec::new(), exact_data(DType::Ext, vec![acc])));
6686 }
6687 let whole = acc.to_int()?;
6690 Some(Array::scalar_i64(exact::ext_to_i64(&whole)?))
6691}
6692
6693fn decode(x: Option<&Array>, y: &Array, tol: Tol, span: Span) -> Result<Array> {
6696 if let Some(exact) = decode_exact(x, y) {
6697 return Ok(exact);
6698 }
6699 let mut digits = digits_of(y, "decode", span)?;
6700 let radix: Vec<f64> = match x {
6701 None => vec![2.0; digits.len()],
6702 Some(x) => {
6703 let r = digits_of(x, "decode", span)?;
6704 if y.rank() == 0 && r.len() != 1 {
6707 digits = vec![digits[0]; r.len()];
6708 }
6709 match r.len() {
6710 1 => vec![r[0]; digits.len()],
6711 n if n == digits.len() => r,
6712 n => {
6713 return Err(Error::new(
6714 ErrorKind::Length,
6715 format!("{n} radices for {} digits", digits.len()),
6716 Some(span),
6717 ));
6718 }
6719 }
6720 }
6721 };
6722 let mut acc = 0.0f64;
6723 for (d, b) in digits.iter().zip(&radix) {
6724 acc = tol.mul(acc, *b) + d;
6728 }
6729 let integral = is_integral(y) && x.is_none_or(is_integral);
6730 Ok(Array::new(vec![], narrow(vec![acc], integral)))
6731}
6732
6733fn decode_apl(x: &Array, y: &Array, span: Span) -> Result<Array> {
6737 let empty = y.count() == 0;
6741 let mut digits = if empty { Vec::new() } else { digits_of(y, "decode", span)? };
6742 let radices = if empty { vec![0.0; x.count()] } else { digits_of(x, "decode", span)? };
6743 let mut k = if y.rank() == 0 { 1 } else { y.shape[0] };
6748 let mut n: usize = if y.rank() == 0 { 1 } else { y.shape[1..].iter().product() };
6749 let (rows, width) = match x.rank() {
6750 0 => (1usize, 0usize),
6751 r => (x.shape[..r - 1].iter().product(), x.shape[r - 1]),
6752 };
6753 if y.count() == 1 && width > 1 && width != k {
6758 digits = vec![digits[0]; width];
6759 k = width;
6760 n = 1;
6761 }
6762 if width > 1 && k != 0 && width != k {
6766 return Err(Error::new(
6767 ErrorKind::Length,
6768 format!("{width} radices for {k} digits"),
6769 Some(span),
6770 ));
6771 }
6772 let per_row = if x.rank() > 0 && width == 0 { 0 } else { k };
6776 let mut out = vec![0.0f64; rows * n];
6777 for i in 0..rows {
6778 for j in 0..n {
6779 let mut acc = 0.0f64;
6780 for d in 0..per_row {
6781 let b = if width <= 1 { radices[i * width] } else { radices[i * width + d] };
6782 acc = acc * b + digits[d * n + j];
6783 }
6784 out[i * n + j] = acc;
6785 }
6786 }
6787 let mut shape: Vec<usize> = if x.rank() == 0 {
6788 Vec::new()
6789 } else {
6790 x.shape[..x.rank() - 1].to_vec()
6791 };
6792 if y.rank() > 0 {
6793 shape.extend_from_slice(&y.shape[1..]);
6794 }
6795 let integral = is_integral(y) && (empty || is_integral(x));
6798 Ok(Array::new(shape, narrow(out, integral)))
6799}
6800
6801fn encode_apl(x: &Array, y: &Array, tol: Tol, span: Span) -> Result<Array> {
6804 let empty = y.count() == 0;
6807 let radices = if empty { vec![0.0; x.count()] } else { digits_of(x, "encode", span)? };
6808 let values = if empty { Vec::new() } else { digits_of(y, "encode", span)? };
6809 let k = if x.rank() == 0 { 1 } else { x.shape[0] };
6810 let frames = if k == 0 { 0 } else { radices.len() / k };
6811 let n = values.len();
6812 let mut out = vec![0.0f64; k * frames * n];
6813 let mut radix = vec![0.0f64; k];
6814 let mut cell = vec![0.0f64; k];
6815 for p in 0..frames {
6816 for (i, r) in radix.iter_mut().enumerate() {
6817 *r = radices[i * frames + p];
6818 }
6819 for (j, &v) in values.iter().enumerate() {
6820 encode_one(&radix, v, &mut cell, tol);
6821 for i in 0..k {
6822 out[(i * frames + p) * n + j] = cell[i];
6823 }
6824 }
6825 }
6826 let mut shape = x.shape.clone();
6827 shape.extend_from_slice(&y.shape);
6828 Ok(Array::new(shape, narrow(out, empty || (is_integral(x) && is_integral(y)))))
6829}
6830
6831fn bit_width(values: &[f64], span: Span) -> Result<usize> {
6834 if values.is_empty() {
6836 return Ok(0);
6837 }
6838 let mut m = 0.0f64;
6839 for &v in values {
6840 if !v.is_finite() {
6841 return Err(Error::domain("cannot encode an infinite value", span));
6842 }
6843 m = m.max(v.abs());
6844 }
6845 let whole = m.floor();
6846 if whole >= 1e15 {
6847 return Err(Error::domain("the value is too large to encode in binary", span));
6848 }
6849 let mut w = 1usize;
6850 let mut n = whole as i64;
6851 while n > 1 {
6852 n /= 2;
6853 w += 1;
6854 }
6855 Ok(w)
6856}
6857
6858fn encode_one(radix: &[f64], v: f64, out: &mut [f64], tol: Tol) {
6866 let mut rem = v;
6867 for i in (0..radix.len()).rev() {
6868 let b = radix[i];
6869 if b == 0.0 {
6870 out[i] = rem;
6871 rem = 0.0;
6872 } else {
6873 let r = tol.residue(b, rem);
6874 out[i] = r;
6875 rem = (rem - r) / b;
6876 }
6877 }
6878}
6879
6880fn encode(x: &Array, y: &Array, tol: Tol, span: Span) -> Result<Array> {
6884 let radix = digits_of(x, "encode", span)?;
6885 let values = digits_of(y, "encode", span)?;
6886 let k = radix.len();
6887 let n = values.len();
6888 let mut out = vec![0.0f64; k * n];
6889 let mut cell = vec![0.0f64; k];
6890 for (j, &v) in values.iter().enumerate() {
6891 encode_one(&radix, v, &mut cell, tol);
6892 if cell.iter().any(|&d| tol.made_nan(d, v, 0.0)) {
6895 return Err(Error::nan(
6896 format!("`{}` has no digits in this base", j_number(v)),
6897 span,
6898 ));
6899 }
6900 for i in 0..k {
6901 out[i * n + j] = cell[i];
6902 }
6903 }
6904 let mut shape = if x.rank() == 0 { Vec::new() } else { vec![k] };
6907 shape.extend_from_slice(&y.shape);
6908 Ok(Array::new(shape, narrow(out, is_integral(x) && is_integral(y))))
6909}
6910
6911fn encode_bits(y: &Array, tol: Tol, span: Span) -> Result<Array> {
6913 let values = digits_of(y, "encode", span)?;
6914 let k = bit_width(&values, span)?;
6915 let radix = vec![2.0; k];
6916 let mut out = vec![0.0f64; values.len() * k];
6917 for (j, &v) in values.iter().enumerate() {
6918 encode_one(&radix, v, &mut out[j * k..(j + 1) * k], tol);
6919 }
6920 let mut shape = y.shape.clone();
6921 shape.push(k);
6922 Ok(Array::new(shape, narrow(out, is_integral(y))))
6923}
6924
6925fn laminate(x: &Array, y: &Array, span: Span) -> Result<Array> {
6930 let spread = |a: &Array, other: &Array| -> Array {
6931 if a.rank() != 0 {
6932 return a.clone();
6933 }
6934 let shape = if other.rank() == 0 { vec![1] } else { other.shape.clone() };
6935 let n: usize = shape.iter().product();
6936 let mut data = Data::empty(a.dtype());
6937 for _ in 0..n {
6938 push_elem(&mut data, &a.data, 0);
6939 }
6940 Array::new(shape, data)
6941 };
6942 assemble(&[2], vec![spread(x, y), spread(y, x)], span)
6943}
6944
6945fn table_of(y: &Array) -> Array {
6947 let shape = match y.rank() {
6948 0 => vec![1, 1],
6949 _ => vec![y.items(), y.item_size()],
6950 };
6951 Array::new(shape, y.data.clone())
6952}
6953
6954fn item_dyad(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
6961 let r = u.dyad(&open_cell(x), &open_cell(y), ctx, span)?;
6962 Ok(enclose(&r, Enclose::ExceptSimpleScalar))
6963}
6964
6965fn outer_product(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
6973 let mut frame = x.shape.clone();
6974 frame.extend_from_slice(&y.shape);
6975 let (nx, ny) = (x.count(), y.count());
6976 let n = nx * ny;
6977 if n == 0 {
6978 return assemble(&frame, Vec::new(), span);
6979 }
6980 let (xr, yr) = (x.to_row_major(), y.to_row_major());
6981 let cells = each_cell(n, nx.max(ny).max(n), u.is_pure(), ctx, |i, c| {
6982 item_dyad(u, &atom(&xr, i / ny), &atom(&yr, i % ny), c, span)
6983 })?;
6984 assemble_items(&frame, cells, span)
6985}
6986
6987fn table(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
6994 if ctx.cfg.rules.lang == crate::Lang::Apl {
6995 return outer_product(u, x, y, ctx, span);
6996 }
6997 let ranks = u.ranks();
6998 let fxl = x.rank() - effective_rank(ranks[1], x.rank());
6999 let fyl = y.rank() - effective_rank(ranks[2], y.rank());
7000 let mut frame = x.shape[..fxl].to_vec();
7001 frame.extend_from_slice(&y.shape[..fyl]);
7002 let nx: usize = x.shape[..fxl].iter().product();
7003 let ny: usize = y.shape[..fyl].iter().product();
7004 let n = nx * ny;
7005 if n == 0 {
7006 return assemble(&frame, Vec::new(), span);
7007 }
7008 if frame.is_empty() {
7009 return u.dyad(x, y, ctx, span);
7010 }
7011 let work = x.count().max(y.count()).max(n);
7012 let cells = each_cell(n, work, u.is_pure(), ctx, |i, c| {
7013 u.dyad(&x.cell_at(fxl, i / ny), &y.cell_at(fyl, i % ny), c, span)
7014 })?;
7015 assemble(&frame, cells, span)
7016}
7017
7018pub(crate) fn with_origin(v: &Verb, origin: i64) -> Option<Verb> {
7025 match v {
7026 Verb::Prim(p) => {
7027 let mut out = *p;
7028 let mut changed = false;
7029 out.monad = match p.monad {
7030 MonadOp::GradeUp { .. } => {
7031 changed = true;
7032 MonadOp::GradeUp { origin }
7033 }
7034 MonadOp::GradeDown { .. } => {
7035 changed = true;
7036 MonadOp::GradeDown { origin }
7037 }
7038 MonadOp::IotaApl { .. } => {
7039 changed = true;
7040 MonadOp::IotaApl { origin }
7041 }
7042 MonadOp::Indices { boxed_coords, .. } => {
7043 changed = true;
7044 MonadOp::Indices { origin, boxed_coords }
7045 }
7046 MonadOp::Roll { fixed, float_at_zero, .. } => {
7047 changed = true;
7048 MonadOp::Roll { origin, fixed, float_at_zero }
7049 }
7050 other => other,
7051 };
7052 out.dyad = match p.dyad {
7053 DyadOp::IndexOf { vector_left, .. } => {
7054 changed = true;
7055 DyadOp::IndexOf { origin, vector_left }
7056 }
7057 DyadOp::IndexOfLast { .. } => {
7058 changed = true;
7059 DyadOp::IndexOfLast { origin }
7060 }
7061 DyadOp::CollateGrade { down, .. } => {
7062 changed = true;
7063 DyadOp::CollateGrade { down, origin }
7064 }
7065 DyadOp::Squad { leading, .. } => {
7066 changed = true;
7067 DyadOp::Squad { origin, leading }
7068 }
7069 DyadOp::Pick { .. } => {
7070 changed = true;
7071 DyadOp::Pick { origin }
7072 }
7073 DyadOp::SelectAxis { axis, rank, .. } => {
7074 changed = true;
7075 DyadOp::SelectAxis { axis, rank, origin }
7076 }
7077 DyadOp::Deal { fixed, .. } => {
7078 changed = true;
7079 DyadOp::Deal { origin, fixed }
7080 }
7081 other => other,
7082 };
7083 changed.then_some(Verb::Prim(out))
7084 }
7085 Verb::Rank(u, r) => Some(Verb::Rank(Box::new(with_origin(u, origin)?), *r)),
7086 Verb::Reduce(u) => Some(Verb::Reduce(Box::new(with_origin(u, origin)?))),
7087 Verb::NWise(u) => Some(Verb::NWise(Box::new(with_origin(u, origin)?))),
7088 Verb::Windowed(u, k) => Some(Verb::Windowed(Box::new(with_origin(u, origin)?), *k)),
7089 Verb::Commute(u) => Some(Verb::Commute(Box::new(with_origin(u, origin)?))),
7090 Verb::Each(u, e) => Some(Verb::Each(Box::new(with_origin(u, origin)?), *e)),
7091 Verb::Fit(u, n) => Some(Verb::Fit(Box::new(with_origin(u, origin)?), *n)),
7092 Verb::AlongAxis(u, k) => Some(Verb::AlongAxis(Box::new(with_origin(u, origin)?), *k)),
7093 _ => None,
7094 }
7095}
7096
7097fn scalar_dyad_of(v: &Verb) -> Option<ScalarDyad> {
7102 match v {
7103 Verb::Prim(p) => match p.dyad {
7104 DyadOp::Scalar(op) => Some(op),
7105 _ => None,
7106 },
7107 _ => None,
7108 }
7109}
7110
7111fn folds_with(u: &Verb, op: ScalarDyad) -> bool {
7114 matches!(u, Verb::Reduce(inner) if scalar_dyad_of(inner) == Some(op))
7115}
7116
7117fn inner_product(
7124 u: &Verb,
7125 v: &Verb,
7126 apl: bool,
7127 x: &Array,
7128 y: &Array,
7129 ctx: &mut Ctx<'_>,
7130 span: Span,
7131) -> Result<Array> {
7132 if let Some(a) = matrix_product(u, v, x, y, span) {
7133 return Ok(a);
7134 }
7135 if apl && scalar_dyad_of(v).is_none() {
7138 return apl_inner_product(u, v, x, y, ctx, span);
7139 }
7140 if !apl {
7141 return inner_cells(u, v, false, x, y, ctx, span);
7142 }
7143 let saved = ctx.cfg.agreement;
7149 ctx.cfg.agreement = Agreement::LeadingPrefix;
7150 let out = inner_cells(u, v, true, x, y, ctx, span);
7151 ctx.cfg.agreement = saved;
7152 out
7153}
7154
7155fn enclose_elements(a: &Array) -> Array {
7159 if a.dtype() == DType::Box { boxed_elements(a) } else { a.clone() }
7160}
7161
7162fn inner_fold(
7169 u: &Verb,
7170 apl: bool,
7171 inner: &Array,
7172 ctx: &mut Ctx<'_>,
7173 span: Span,
7174) -> Result<Array> {
7175 let folded = u.monad(inner, ctx, span)?;
7176 Ok(if apl { enclose_elements(&folded) } else { folded })
7177}
7178
7179fn inner_cells(
7182 u: &Verb,
7183 v: &Verb,
7184 apl: bool,
7185 x: &Array,
7186 y: &Array,
7187 ctx: &mut Ctx<'_>,
7188 span: Span,
7189) -> Result<Array> {
7190 let cell_rank = effective_rank(v.ranks()[1].max(1), x.rank());
7191 let frame_rank = x.rank() - cell_rank;
7192 if frame_rank == 0 {
7193 let inner = v.dyad(x, y, ctx, span)?;
7194 return inner_fold(u, apl, &inner, ctx, span);
7195 }
7196 let frame = x.shape[..frame_rank].to_vec();
7197 let n: usize = frame.iter().product();
7198 if n == 0 {
7199 return assemble(&frame, Vec::new(), span);
7200 }
7201 let work = x.count().max(y.count());
7202 let pure = u.is_pure() && v.is_pure();
7203 let cells = each_cell(n, work, pure, ctx, |i, c| {
7204 let inner = v.dyad(&x.cell_at(frame_rank, i), y, c, span)?;
7205 inner_fold(u, apl, &inner, c, span)
7206 })?;
7207 assemble(&frame, cells, span)
7208}
7209
7210fn apl_inner_product(
7215 u: &Verb,
7216 v: &Verb,
7217 x: &Array,
7218 y: &Array,
7219 ctx: &mut Ctx<'_>,
7220 span: Span,
7221) -> Result<Array> {
7222 let k = match (x.rank(), y.rank()) {
7225 (0, 0) => 1,
7226 (0, _) => y.shape[0],
7227 _ => x.shape[x.rank() - 1],
7228 };
7229 if x.rank() > 0 && y.rank() > 0 && x.shape[x.rank() - 1] != y.shape[0] {
7230 return Err(Error::new(
7231 ErrorKind::Length,
7232 format!("inner product over {} and {} elements", x.shape[x.rank() - 1], y.shape[0]),
7233 Some(span),
7234 ));
7235 }
7236 let lead: &[usize] = if x.rank() > 0 { &x.shape[..x.rank() - 1] } else { &[] };
7237 let trail: &[usize] = if y.rank() > 0 { &y.shape[1..] } else { &[] };
7238 let rows: usize = lead.iter().product();
7239 let cols: usize = trail.iter().product();
7240 let mut frame = lead.to_vec();
7241 frame.extend_from_slice(trail);
7242 let n = rows * cols;
7243 if n == 0 {
7244 return assemble(&frame, Vec::new(), span);
7245 }
7246 let vector = |d: &Data, at: &dyn Fn(usize) -> usize| {
7247 let mut out = Data::empty(d.dtype());
7248 for t in 0..k {
7249 out.push_from(d, at(t));
7250 }
7251 Array::new(vec![k], out)
7252 };
7253 let pure = u.is_pure() && v.is_pure();
7254 let cells = each_cell(n, x.count().max(y.count()), pure, ctx, |i, c| {
7255 let (r, col) = (i / cols, i % cols);
7256 let left = vector(&x.data, &|t| if x.rank() > 0 { r * k + t } else { 0 });
7257 let right = vector(&y.data, &|t| if y.rank() > 0 { t * cols + col } else { 0 });
7258 let inner = v.dyad(&left, &right, c, span)?;
7259 inner_fold(u, true, &inner, c, span)
7260 })?;
7261 assemble(&frame, cells, span)
7262}
7263
7264fn matrix_product(u: &Verb, v: &Verb, x: &Array, y: &Array, span: Span) -> Option<Array> {
7270 if !folds_with(u, ScalarDyad::Add) || scalar_dyad_of(v) != Some(ScalarDyad::Mul) {
7271 return None;
7272 }
7273 if x.rank() == 0 || y.rank() == 0 {
7274 return None;
7275 }
7276 let k = x.shape[x.rank() - 1];
7277 if k != y.shape[0] {
7278 return None;
7279 }
7280 let rows: usize = x.shape[..x.rank() - 1].iter().product();
7281 let cols: usize = y.shape[1..].iter().product();
7282 let mut shape = x.shape[..x.rank() - 1].to_vec();
7283 shape.extend_from_slice(&y.shape[1..]);
7284 if crate::limits::elements(&shape, span).is_err() {
7285 return None;
7286 }
7287 let whole = matches!(x.dtype(), DType::Bool | DType::I64)
7288 && matches!(y.dtype(), DType::Bool | DType::I64);
7289 if whole
7290 && let (Some(xs), Some(ys)) = (x.to_i64_vec(), y.to_i64_vec())
7291 && let Some(out) = matmul_whole(&xs, &ys, rows, k, cols)
7292 {
7293 return Some(Array::new(shape, Data::I64(out.into())));
7294 }
7295 let (xs, ys) = (x.to_f64_vec()?, y.to_f64_vec()?);
7296 let out = par::fill_rows(rows, cols, rows * k * cols, |r0, part| {
7297 matmul_f64(&xs, &ys, k, cols, r0, part);
7298 });
7299 Some(Array::new(shape, Data::F64(out.into())))
7300}
7301
7302const MATMUL_BLOCK: usize = 128;
7306
7307#[inline(always)]
7308fn matmul_f64_body(xs: &[f64], ys: &[f64], k: usize, n: usize, r0: usize, out: &mut [f64]) {
7309 if n == 0 {
7310 return;
7311 }
7312 let rows = out.len() / n;
7313 for k0 in (0..k).step_by(MATMUL_BLOCK) {
7314 let k1 = (k0 + MATMUL_BLOCK).min(k);
7315 for r in 0..rows {
7316 let left = &xs[(r0 + r) * k..(r0 + r + 1) * k];
7317 let dst = &mut out[r * n..(r + 1) * n];
7318 for (t, &a) in left.iter().enumerate().take(k1).skip(k0) {
7319 let row = &ys[t * n..(t + 1) * n];
7320 for (o, &b) in dst.iter_mut().zip(row) {
7321 *o += a * b;
7322 }
7323 }
7324 }
7325 }
7326}
7327
7328multiversioned! {
7329 fn matmul_f64(
7333 xs: &[f64],
7334 ys: &[f64],
7335 k: usize,
7336 n: usize,
7337 r0: usize,
7338 out: &mut [f64],
7339 ) -> () = matmul_f64_body;
7340}
7341
7342#[inline(always)]
7343fn matmul_i64_body(xs: &[i64], ys: &[i64], k: usize, n: usize, r0: usize, out: &mut [i64]) {
7344 if n == 0 {
7345 return;
7346 }
7347 let rows = out.len() / n;
7348 for k0 in (0..k).step_by(MATMUL_BLOCK) {
7349 let k1 = (k0 + MATMUL_BLOCK).min(k);
7350 for r in 0..rows {
7351 let left = &xs[(r0 + r) * k..(r0 + r + 1) * k];
7352 let dst = &mut out[r * n..(r + 1) * n];
7353 for (t, &a) in left.iter().enumerate().take(k1).skip(k0) {
7354 let row = &ys[t * n..(t + 1) * n];
7355 for (o, &b) in dst.iter_mut().zip(row) {
7356 *o = o.wrapping_add(a.wrapping_mul(b));
7357 }
7358 }
7359 }
7360 }
7361}
7362
7363multiversioned! {
7364 fn matmul_i64(
7368 xs: &[i64],
7369 ys: &[i64],
7370 k: usize,
7371 n: usize,
7372 r0: usize,
7373 out: &mut [i64],
7374 ) -> () = matmul_i64_body;
7375}
7376
7377fn matmul_whole(xs: &[i64], ys: &[i64], rows: usize, k: usize, n: usize) -> Option<Vec<i64>> {
7381 let bound = |v: &[i64]| v.iter().map(|&a| (a as i128).abs()).max().unwrap_or(0);
7386 if bound(xs).saturating_mul(bound(ys)).saturating_mul(k as i128) <= i64::MAX as i128 {
7387 return Some(par::fill_rows(rows, n, rows * k * n, |r0, part| {
7388 matmul_i64(xs, ys, k, n, r0, part);
7389 }));
7390 }
7391 let mut out = vec![0i64; rows * n];
7392 for r in 0..rows {
7393 let left = &xs[r * k..(r + 1) * k];
7394 let dst = &mut out[r * n..(r + 1) * n];
7395 for (t, &a) in left.iter().enumerate() {
7396 for (o, &b) in dst.iter_mut().zip(&ys[t * n..(t + 1) * n]) {
7397 *o = a.checked_mul(b).and_then(|p| o.checked_add(p))?;
7398 }
7399 }
7400 }
7401 Some(out)
7402}
7403
7404const DETERMINANT_MINORS_MAX: usize = 16;
7409
7410fn determinant(
7416 u: &Verb,
7417 v: &Verb,
7418 apl: bool,
7419 y: &Array,
7420 ctx: &mut Ctx<'_>,
7421 span: Span,
7422) -> Result<Array> {
7423 if apl {
7424 return Err(Error::domain("an inner product has no monadic meaning in APL", span));
7425 }
7426 if y.rank() > 2 {
7430 let frame = y.shape[..y.rank() - 2].to_vec();
7431 let n: usize = frame.iter().product();
7432 let pure = u.is_pure() && v.is_pure();
7433 let cells = each_cell(n, y.count(), pure, ctx, |i, c| {
7434 determinant(u, v, apl, &y.cell_at(y.rank() - 2, i), c, span)
7435 })?;
7436 return assemble(&frame, cells, span);
7437 }
7438 let rows = y.items();
7439 let cols = y.item_size();
7440 if folds_with(u, ScalarDyad::Sub)
7441 && scalar_dyad_of(v) == Some(ScalarDyad::Mul)
7442 && rows == cols
7443 && rows >= 3
7444 && matches!(y.dtype(), DType::Bool | DType::I64 | DType::F64)
7445 && let Some(values) = y.to_f64_vec()
7446 {
7447 return Ok(Array::scalar_f64(determinant_lu(values, rows)));
7448 }
7449 if rows > DETERMINANT_MINORS_MAX {
7450 return Err(Error::not_yet(
7451 format!(
7452 "a determinant of more than {DETERMINANT_MINORS_MAX} rows by minors \
7453 (only -/ . * over machine numbers has a direct method)"
7454 ),
7455 span,
7456 ));
7457 }
7458 let mut seen: HashMap<u64, Array> = HashMap::new();
7459 let all = if rows == 64 { u64::MAX } else { (1u64 << rows) - 1 };
7460 minors(u, v, y, cols, rows, all, &mut seen, ctx, span)
7461}
7462
7463#[allow(clippy::too_many_arguments)]
7467fn minors(
7468 u: &Verb,
7469 v: &Verb,
7470 y: &Array,
7471 cols: usize,
7472 rows: usize,
7473 left: u64,
7474 seen: &mut HashMap<u64, Array>,
7475 ctx: &mut Ctx<'_>,
7476 span: Span,
7477) -> Result<Array> {
7478 if let Some(a) = seen.get(&left) {
7479 return Ok(a.clone());
7480 }
7481 let column = rows - left.count_ones() as usize;
7484 let value = if column >= cols {
7485 let data = reduce_identity(v, 1, ctx.cfg.rules.lang).ok_or_else(|| {
7486 Error::not_yet(
7487 format!("the identity element of {} (a determinant with no columns)", v.name()),
7488 span,
7489 )
7490 })?;
7491 Array::new(Vec::new(), data)
7492 } else if left == 0 {
7493 u.monad(&Array::new(vec![0], Data::empty(DType::I64)), ctx, span)?
7494 } else {
7495 let mut terms = Vec::with_capacity(left.count_ones() as usize);
7496 for r in 0..rows {
7497 if left & (1 << r) == 0 {
7498 continue;
7499 }
7500 let minor = minors(u, v, y, cols, rows, left & !(1 << r), seen, ctx, span)?;
7501 let head = Array::new(Vec::new(), y.data.slice(r * cols + column, r * cols + column + 1));
7502 terms.push(v.dyad(&head, &minor, ctx, span)?);
7503 }
7504 let n = terms.len();
7505 u.monad(&assemble(&[n], terms, span)?, ctx, span)?
7506 };
7507 seen.insert(left, value.clone());
7508 Ok(value)
7509}
7510
7511fn determinant_lu(mut a: Vec<f64>, n: usize) -> f64 {
7516 let mut det = 1.0f64;
7517 for c in 0..n {
7518 let mut pivot = c;
7519 for r in c + 1..n {
7520 if a[r * n + c].abs() > a[pivot * n + c].abs() {
7521 pivot = r;
7522 }
7523 }
7524 if a[pivot * n + c] == 0.0 {
7525 return 0.0;
7526 }
7527 if pivot != c {
7528 for j in 0..n {
7529 a.swap(c * n + j, pivot * n + j);
7530 }
7531 det = -det;
7532 }
7533 let head = a[c * n + c];
7534 det *= head;
7535 for r in c + 1..n {
7536 let factor = a[r * n + c] / head;
7537 if factor == 0.0 {
7538 continue;
7539 }
7540 for j in c..n {
7541 a[r * n + j] -= factor * a[c * n + j];
7542 }
7543 }
7544 }
7545 det
7546}
7547
7548fn monad_op(p: &Prim, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
7550 let apl = ctx.cfg.rules.lang == crate::Lang::Apl;
7551 let out = monad_op_inner(p, y, ctx, span);
7552 if apl { out.map(tightened_mixed) } else { out }
7553}
7554
7555fn monad_op_inner(p: &Prim, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
7558 match p.monad {
7559 MonadOp::Scalar(op) => scalar_monad(op, y, ctx.cfg, span),
7560 MonadOp::ShapeOf => {
7561 Ok(carry_exact(Array::from_i64(y.shape.iter().map(|&n| n as i64).collect()), y))
7562 }
7563 MonadOp::Tally => Ok(carry_exact(Array::scalar_i64(y.items() as i64), y)),
7564 MonadOp::Ravel => Ok(Array::new(vec![y.count()], y.data.clone())),
7565 MonadOp::RavelItems => {
7570 let (items, width) = if y.rank() == 0 {
7571 (1usize, 1usize)
7572 } else {
7573 (y.shape[0], y.shape[1..].iter().product::<usize>())
7574 };
7575 Ok(Array::new(vec![items, width], y.to_row_major().data))
7576 }
7577 MonadOp::TransposeAxes => Ok(transpose_axes(y)),
7578 MonadOp::Head => Ok(head(y)),
7579 MonadOp::Behead => behead(y, span),
7580 MonadOp::Tail => Ok(tail(y)),
7581 MonadOp::Curtail => Ok(curtail(y)),
7582 MonadOp::Reverse => Ok(reverse(y)),
7583 MonadOp::Nub => Ok(nub(y, ctx.cfg.tol)),
7586 MonadOp::GradeUp { origin } | MonadOp::GradeDown { origin } => {
7587 check_gradable(y, ctx.cfg.rules, span)?;
7588 if ctx.cfg.rules.lang == crate::Lang::Apl && y.rank() == 0 {
7591 return Err(Error::domain("a grade needs an array, not a scalar", span));
7592 }
7593 let down = matches!(p.monad, MonadOp::GradeDown { .. });
7594 let order = grade_order(y, down, Grading::of(ctx.cfg.rules, ctx.cfg.tol));
7595 Ok(Array::from_i64(order.iter().map(|&i| origin + i as i64).collect()))
7596 }
7597 MonadOp::IotaJ => iota_j(y, ctx.cfg.near(), span),
7598 MonadOp::IotaApl { origin } => iota_apl(y, origin, ctx.cfg.near(), span),
7599 MonadOp::Echo => {
7600 (ctx.out)(&format!("{}\n", crate::fmt::format_array(y, &ctx.cfg.fmt)));
7601 Ok(Array::empty(DType::I64))
7602 }
7603 MonadOp::ReadStream => {
7604 stream_number(y, 1, "1!:1 reads", span)?;
7605 let line = ctx.read_line(span)?;
7606 Ok(Array::from_chars(line.chars().collect()))
7607 }
7608 MonadOp::TypeCode => Ok(Array::scalar_i64(type_code(y))),
7609 MonadOp::Sparse => crate::sparse::sparsify(y, span),
7610 MonadOp::Dense => Ok(y.densified()),
7611 MonadOp::PrimeCount => {
7612 let n = y
7613 .to_i64_vec_near(ctx.cfg.near())
7614 .ok_or_else(|| Error::domain("the prime count needs an integer", span))?;
7615 let v = n.first().copied().unwrap_or(0);
7616 Ok(carry_exact(Array::scalar_i64(primes_below(v, span)?), y))
7617 }
7618 MonadOp::IndicesInverse => indices_inverse(y, ctx.cfg.near(), span),
7619 MonadOp::Same => Ok(y.clone()),
7620 MonadOp::Format => Ok(format_chars(y, &ctx.cfg.fmt)),
7621 MonadOp::DecodeBits => decode(None, y, ctx.cfg.tol, span).map(|r| carry_exact(r, y)),
7622 MonadOp::EncodeBits => encode_bits(y, ctx.cfg.tol, span).map(|r| carry_exact(r, y)),
7623 MonadOp::Itemize => {
7624 let mut shape = vec![1usize];
7625 shape.extend_from_slice(&y.shape);
7626 Ok(Array::new(shape, y.data.clone()))
7627 }
7628 MonadOp::TableOf => Ok(table_of(y)),
7629 MonadOp::Enclose(rule) => Ok(enclose(y, rule)),
7630 MonadOp::Open => Ok(open_cell(y)),
7631 MonadOp::Raze => raze(y, span),
7632 MonadOp::Catalogue => catalogue(y, span),
7633 MonadOp::AtomicRep => atomic_rep(y, ctx, span),
7634 MonadOp::RazeIn => raze_in(y, ctx.cfg.tol, span),
7635 MonadOp::First => Ok(first(y)),
7636 MonadOp::Enlist => enlist(y, span),
7637 MonadOp::Depth { signed } => {
7638 let d = depth(y);
7639 Ok(Array::scalar_i64(if signed && d > 1 && !uniform(y) { -d } else { d }))
7640 }
7641 MonadOp::Indices { origin, boxed_coords } => {
7642 where_indices(y, origin, boxed_coords, ctx.cfg.near(), span)
7643 }
7644 MonadOp::Steps => steps(y, span),
7645 MonadOp::ToExact => to_exact(y, span),
7646 MonadOp::NthPrime => {
7647 let n = y
7648 .to_i64_vec_near(ctx.cfg.near())
7649 .ok_or_else(|| Error::domain("the prime index must be an integer", span))?;
7650 let v = n.first().copied().unwrap_or(0);
7651 Ok(carry_exact(Array::scalar_i64(nth_prime(v, span)?), y))
7652 }
7653 MonadOp::PrimeFactors => {
7654 let n = y
7655 .to_i64_vec_near(ctx.cfg.near())
7656 .ok_or_else(|| Error::domain("prime factors need an integer", span))?;
7657 let v = n.first().copied().unwrap_or(0);
7658 Ok(carry_exact(Array::from_i64(prime_factors(v, span)?), y))
7659 }
7660 MonadOp::MatrixInverse => matrix_inverse(y, span),
7661 MonadOp::Roll { origin, fixed, float_at_zero } => {
7662 roll(y, origin, fixed, float_at_zero, ctx.cfg.near(), span)
7663 }
7664 MonadOp::ComplexParts { polar } => complex_parts(y, polar, span),
7665 MonadOp::SelfClassify => Ok(self_classify(y, ctx.cfg.tol)),
7666 MonadOp::NubSieve => Ok(nub_sieve(y, ctx.cfg.tol, ctx.cfg.rules.lang)),
7667 MonadOp::Unicode { pass_chars } => unicode(y, pass_chars, ctx.cfg.near(), span),
7668 MonadOp::Symbols => to_symbols(y, span),
7669 MonadOp::Words => words(y, span),
7670 MonadOp::LevelOf => Ok(Array::scalar_i64(boxing_level(y))),
7671 MonadOp::MapPaths => Ok(map_paths(y)),
7672 MonadOp::Nest => Ok(nest(y)),
7673 MonadOp::PolyRoots => poly_roots(y, span),
7674 MonadOp::PolyDeriv => poly_deriv(y, span),
7675 MonadOp::AnagramIndex => anagram_index(y, ctx.cfg.rules, span),
7676 MonadOp::CycleForm => cycle_form(y, ctx.cfg.near(), span),
7677 MonadOp::Split => Ok(split_items(y)),
7678 MonadOp::Execute { apl } => execute(y, apl, ctx, span),
7679 MonadOp::NotYet(what) => Err(Error::not_yet(what, span)),
7680 MonadOp::None => {
7681 Err(Error::domain(format!("{} has no monadic meaning", p.name), span))
7682 }
7683 }
7684}
7685
7686fn complex_parts(y: &Array, polar: bool, span: Span) -> Result<Array> {
7690 let Some(v) = y.to_complex_vec() else {
7691 return Err(wrong_type(y.dtype(), span));
7692 };
7693 let z = v.first().copied().unwrap_or(cx::ZERO);
7694 let pair = if polar { vec![cx::abs(z), cx::arg(z)] } else { vec![z[0], z[1]] };
7695 Ok(Array::from_f64(pair))
7696}
7697
7698fn axis_counts(x: &Array, what: &str, near: NearInt, span: Span) -> Result<Vec<i64>> {
7699 if x.rank() > 1 {
7700 return Err(Error::new(
7701 ErrorKind::Rank,
7702 format!("{what} needs a scalar or vector left argument"),
7703 Some(span),
7704 ));
7705 }
7706 if x.count() == 0 {
7709 return Ok(Vec::new());
7710 }
7711 x.to_i64_vec_near(near)
7712 .ok_or_else(|| Error::domain(format!("{what} needs integer lengths"), span))
7713}
7714
7715fn reshape(
7726 x: &Array,
7727 y: &Array,
7728 by_items: bool,
7729 apl: bool,
7730 near: NearInt,
7731 span: Span,
7732) -> Result<Array> {
7733 let dims = axis_counts(x, "reshape", near, span)?;
7734 if dims.iter().any(|&d| d < 0) {
7735 return Err(Error::domain("reshape lengths must be nonnegative", span));
7736 }
7737 let mut shape: Vec<usize> = dims.iter().map(|&d| d as usize).collect();
7738 let (unit, src) = if by_items {
7740 let item_shape = if y.rank() == 0 { &[][..] } else { &y.shape[1..] };
7741 shape.extend_from_slice(item_shape);
7742 (item_shape.iter().product::<usize>(), y.items().max(usize::from(y.rank() == 0)))
7743 } else {
7744 (1, y.count())
7745 };
7746 let n = crate::limits::elements(&shape, span)?;
7747 let mut data = Data::empty(y.dtype());
7748 if n > 0 && src == 0 {
7749 if by_items {
7750 return Err(Error::new(ErrorKind::Length, "reshape of an empty array", Some(span)));
7751 }
7752 let fill = if apl { prototype_of(y) } else { None };
7753 let mut data = Data::empty(y.dtype());
7754 for _ in 0..n {
7755 push_gap(&mut data, &fill);
7756 }
7757 return Ok(Array::new(shape, data));
7758 }
7759 if y.is_row_major() && n <= unit.saturating_mul(src) && n <= y.data.len() {
7765 return Ok(keep_proto(Array::new(shape, y.data.slice(0, n)), y, apl));
7766 }
7767 for i in 0..n {
7768 push_elem(&mut data, &y.data, (i / unit) % src * unit + i % unit);
7769 }
7770 Ok(keep_proto(Array::new(shape, data), y, apl))
7771}
7772
7773fn leading_run(y: &Array, counts: &[i64], drop: bool) -> Option<Array> {
7777 if y.rank() == 0 || counts.is_empty() {
7778 return None;
7779 }
7780 let trailing_untouched = counts[1..].iter().enumerate().all(|(a, &c)| {
7784 if drop { c == 0 } else { c.unsigned_abs() as usize == y.shape[a + 1] }
7785 });
7786 if !trailing_untouched {
7787 return None;
7788 }
7789 let n = y.items();
7790 let k = counts[0];
7791 let a = k.unsigned_abs() as usize;
7792 let (lo, keep) = if drop {
7793 let a = a.min(n);
7794 if k >= 0 { (a, n - a) } else { (0, n - a) }
7795 } else {
7796 if a > n {
7798 return None;
7799 }
7800 if k >= 0 { (0, a) } else { (n - a, a) }
7801 };
7802 Some(section(y, lo, lo + keep))
7803}
7804
7805fn count_rank(verb: &str, counts: usize, rank: usize, span: Span) -> Error {
7810 Error::new(
7811 ErrorKind::Length,
7812 format!("{counts} {verb} counts for a rank-{rank} argument"),
7813 Some(span),
7814 )
7815}
7816
7817fn take(
7818 x: &Array,
7819 y: &Array,
7820 prototype_fill: bool,
7821 apl: bool,
7822 near: NearInt,
7823 span: Span,
7824) -> Result<Array> {
7825 let counts = axis_counts(x, "take", near, span)?;
7826 let fill = if prototype_fill { prototype_of(y) } else { None };
7830 let promoted;
7831 let base = if y.rank() == 0 {
7834 promoted = Array::new(vec![1; counts.len()], y.data.clone());
7835 &promoted
7836 } else {
7837 y
7838 };
7839 let wrong = if apl {
7841 counts.len() != base.rank()
7842 } else {
7843 counts.len() > base.rank() || (counts.is_empty() && base.rank() > 0)
7844 };
7845 if wrong {
7846 return Err(count_rank("take", counts.len(), base.rank(), span));
7847 }
7848 if let Some(run) = leading_run(base, &counts, false) {
7849 return Ok(keep_proto(run, base, prototype_fill));
7850 }
7851 let mut out_shape = base.shape.clone();
7852 for (a, &k) in counts.iter().enumerate() {
7853 out_shape[a] = k.unsigned_abs() as usize;
7854 }
7855 let n = crate::limits::elements(&out_shape, span)?;
7856 let st = strides(&base.shape);
7857 let mut data = Data::empty(base.dtype());
7858 let mut coord = vec![0usize; out_shape.len()];
7859 for _ in 0..n {
7860 let mut idx = 0usize;
7861 let mut inside = true;
7862 for a in 0..out_shape.len() {
7863 let len = base.shape[a] as i64;
7864 let c = coord[a] as i64;
7865 let s = match counts.get(a) {
7868 Some(&k) if k < 0 => c + len - k.unsigned_abs() as i64,
7869 _ => c,
7870 };
7871 if s < 0 || s >= len {
7872 inside = false;
7873 break;
7874 }
7875 idx += s as usize * st[a];
7876 }
7877 if inside {
7878 push_elem(&mut data, &base.data, idx);
7879 } else if let (Data::Box(v), Some(p)) = (&mut data, &fill) {
7880 v.push(p.clone());
7881 } else {
7882 data.push_fill();
7883 }
7884 odometer(&mut coord, &out_shape);
7885 }
7886 Ok(keep_proto(Array::new(out_shape, data), base, prototype_fill))
7887}
7888
7889fn prototype_of(y: &Array) -> Option<Array> {
7893 fn zeroed(a: &Array) -> Array {
7894 if let Some(items) = a.as_boxes() {
7895 let inner: Vec<Array> = items.iter().map(zeroed).collect();
7896 return Array::new(a.shape.clone(), Data::Box(inner.into()));
7897 }
7898 let dtype = match a.dtype() {
7899 DType::Char | DType::Symbol => a.dtype(),
7900 _ => DType::I64,
7901 };
7902 Array::new(a.shape.clone(), fill_data(dtype, a.count()))
7903 }
7904 match y.as_boxes()?.first() {
7905 Some(first) => Some(zeroed(first)),
7906 None => y.proto().cloned(),
7909 }
7910}
7911
7912fn keep_proto(out: Array, src: &Array, apl: bool) -> Array {
7918 if !apl || out.count() > 0 || out.dtype() != DType::Box {
7919 return out;
7920 }
7921 match prototype_of(src) {
7922 Some(p) => out.with_proto(p),
7923 None => out,
7924 }
7925}
7926
7927fn push_gap(data: &mut Data, fill: &Option<Array>) {
7930 match (data, fill) {
7931 (Data::Box(v), Some(p)) => v.push(p.clone()),
7932 (d, _) => d.push_fill(),
7933 }
7934}
7935
7936fn drop_(x: &Array, y: &Array, apl: bool, near: NearInt, span: Span) -> Result<Array> {
7937 let counts = axis_counts(x, "drop", near, span)?;
7938 let promoted;
7939 let base = if y.rank() == 0 {
7940 promoted = Array::new(vec![1; counts.len()], y.data.clone());
7941 &promoted
7942 } else {
7943 y
7944 };
7945 let wrong =
7946 if apl { counts.len() != base.rank() } else { counts.len() > base.rank() };
7947 if wrong {
7948 return Err(count_rank("drop", counts.len(), base.rank(), span));
7949 }
7950 if let Some(run) = leading_run(base, &counts, true) {
7951 return Ok(keep_proto(run, base, apl));
7952 }
7953 let mut out_shape = base.shape.clone();
7954 let mut offset = vec![0usize; base.rank()];
7955 for (a, &k) in counts.iter().enumerate() {
7956 let len = base.shape[a];
7957 let d = (k.unsigned_abs() as usize).min(len);
7958 out_shape[a] = len - d;
7959 if k > 0 {
7960 offset[a] = d;
7961 }
7962 }
7963 let n: usize = out_shape.iter().product();
7964 let st = strides(&base.shape);
7965 let mut data = Data::empty(base.dtype());
7966 let mut coord = vec![0usize; out_shape.len()];
7967 for _ in 0..n {
7968 let idx: usize = (0..out_shape.len()).map(|a| (coord[a] + offset[a]) * st[a]).sum();
7969 push_elem(&mut data, &base.data, idx);
7970 odometer(&mut coord, &out_shape);
7971 }
7972 Ok(keep_proto(Array::new(out_shape, data), base, apl))
7973}
7974
7975fn dyad_op(p: &Prim, x: &Array, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
7977 let apl = cfg.rules.lang == crate::Lang::Apl;
7978 let out = dyad_op_inner(p, x, y, cfg, span);
7979 if apl { out.map(tightened_mixed) } else { out }
7980}
7981
7982fn dyad_op_inner(p: &Prim, x: &Array, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
7985 let tol = cfg.tol;
7986 let apl = cfg.rules.lang == crate::Lang::Apl;
7987 match p.dyad {
7988 DyadOp::Scalar(op) => scalar_dyad(op, x, y, cfg, span),
7991 DyadOp::Reshape => {
7992 let apl = cfg.rules.lang == crate::Lang::Apl;
7993 reshape(x, y, cfg.agreement == Agreement::LeadingPrefix, apl, cfg.near(), span)
7994 }
7995 DyadOp::Take => {
7996 let apl = cfg.rules.lang == crate::Lang::Apl;
7997 take(x, y, cfg.agreement == Agreement::ExactOrScalar, apl, cfg.near(), span)
7998 }
7999 DyadOp::Drop => drop_(x, y, cfg.rules.lang == crate::Lang::Apl, cfg.near(), span),
8000 DyadOp::Right => Ok(y.clone()),
8001 DyadOp::Left => Ok(x.clone()),
8002 DyadOp::Rotate => rotate(x, y, cfg.near(), span),
8003 DyadOp::RotateApl { last } => rotate_apl(x, y, last, cfg.near(), span),
8004 DyadOp::AppendLeading => {
8007 catenate(x, y, true, cfg.agreement == Agreement::LeadingPrefix, span)
8008 }
8009 DyadOp::AppendLast => {
8010 catenate(x, y, false, cfg.agreement == Agreement::LeadingPrefix, span)
8011 }
8012 DyadOp::IndexOf { origin, vector_left } => {
8013 let (x, y) = align_mixed(x, y, apl);
8014 index_of(&x, &y, origin, vector_left, tol, span)
8015 }
8016 DyadOp::MemberJ => Ok(member_j(x, y, tol)),
8017 DyadOp::MemberApl => {
8018 let (x, y) = align_mixed(x, y, apl);
8019 Ok(member_apl(&x, &y, tol))
8020 }
8021 DyadOp::From => from_index(x, y, cfg.near(), span),
8022 DyadOp::Match => {
8023 let empties_differ = cfg.rules.lang == crate::Lang::Apl
8027 && x.count() == 0
8028 && y.count() == 0
8029 && (x.dtype() == DType::Char) != (y.dtype() == DType::Char);
8030 Ok(Array::scalar_bool(!empties_differ && arrays_match(x, y, tol)))
8031 }
8032 DyadOp::NotMatch => Ok(Array::scalar_bool(!arrays_match(x, y, tol))),
8033 DyadOp::GradeSelect { down } => grade_select(x, y, down, cfg.rules, cfg.tol, span),
8034 DyadOp::Copy => {
8035 copy_items(x, y, cfg.agreement == Agreement::ExactOrScalar, cfg.near(), span)
8036 }
8037 DyadOp::CollateGrade { down, origin } => collate_grade(x, y, down, origin, span),
8038 DyadOp::TransposeJ => transpose_j(x, y, cfg.near(), span),
8039 DyadOp::TransposeApl => transpose_apl(x, y, cfg.rules.origin, cfg.near(), span),
8040 DyadOp::DecodeApl => decode_apl(x, y, span).map(|r| carry_exact2(r, x, y)),
8041 DyadOp::EncodeApl => {
8042 let tol = match cfg.rules.encode_digits {
8045 EncodeDigits::Tolerant => cfg.tol,
8046 EncodeDigits::Exact => Tol { ct: 0.0, ..cfg.tol },
8047 };
8048 encode_apl(x, y, tol, span).map(|r| carry_exact2(r, x, y))
8049 }
8050 DyadOp::Decode => decode(Some(x), y, cfg.tol, span).map(|r| carry_exact2(r, x, y)),
8051 DyadOp::Encode => encode(x, y, cfg.tol, span).map(|r| carry_exact2(r, x, y)),
8052 DyadOp::Laminate => laminate(x, y, span),
8053 DyadOp::Link => link(x, y, span),
8054 DyadOp::Strand => strand(x, y, span),
8055 DyadOp::IntervalIndex { offset, closed } => {
8056 interval_index(x, y, offset, closed, tol, Grading::of(cfg.rules, tol), span)
8057 }
8058 DyadOp::IndexOfLast { origin } => Ok(index_of_last(x, y, origin, tol)),
8059 DyadOp::MatrixDivide => matrix_divide(x, y, span),
8060 DyadOp::PartitionEnclose => partition_enclose(x, y, cfg.near(), span),
8061 DyadOp::PartitionCounts => partition_counts(x, y, cfg.near(), span),
8062 DyadOp::Squad { origin, leading } => squad(x, y, origin, leading, cfg.near(), span),
8063 DyadOp::SelectAxis { axis, rank, origin } => {
8064 select_axis(x, y, axis, rank, origin, cfg.near(), span)
8065 }
8066 DyadOp::Fetch => fetch(x, y, cfg.near(), span),
8067 DyadOp::PolyEval => poly_eval(x, y, span),
8068 DyadOp::PolyIntegral => poly_integral(x, y, span),
8069 DyadOp::TruthTable(m) => truth_table(m, x, y, span),
8070 DyadOp::FormatSpec => format_spec(x, y, &cfg.fmt, span),
8071 DyadOp::FormatSpecJ => format_spec_j(x, y, &cfg.fmt, span),
8072 DyadOp::ParseNumbers => parse_numbers(x, y, span),
8073 DyadOp::SequentialMachine => sequential_machine(x, y, span),
8074 DyadOp::Deal { origin, fixed } => deal(x, y, origin, fixed, cfg.near(), span),
8075 DyadOp::ExactForm => exact_form(x, y, cfg.near(), span),
8076 DyadOp::Boolean(op) => bool_dyad(op, x, y, cfg, span),
8077 DyadOp::Less => {
8078 set_rank(cfg, "without", x, y, span)?;
8079 let (x, y) = align_mixed(x, y, apl);
8080 Ok(set_less(&x, &y, tol))
8081 }
8082 DyadOp::Union => {
8083 set_rank(cfg, "union", x, y, span)?;
8084 let (x, y) = align_mixed(x, y, apl);
8085 union_items(&x, &y, tol, span)
8086 }
8087 DyadOp::Intersect => {
8088 set_rank(cfg, "intersection", x, y, span)?;
8089 let (x, y) = align_mixed(x, y, apl);
8090 Ok(intersect_items(&x, &y, tol))
8091 }
8092 DyadOp::AnagramFrom => anagram_from(x, y, cfg.near(), span),
8093 DyadOp::Permute => permute(x, y, cfg.near(), span),
8094 DyadOp::FindSeq => {
8095 let (x, y) = align_mixed(x, y, apl);
8096 find_seq(&x, &y, tol, apl, span)
8097 }
8098 DyadOp::UnicodeForm => unicode_form(x, y, cfg.near(), span),
8099 DyadOp::SymbolForm => symbol_form(x, y, span),
8100 DyadOp::SparseForm => sparse_form(x, y, cfg.near(), span),
8101 DyadOp::PrimeMeta => prime_meta(x, y, cfg.near(), span).map(|r| carry_exact2(r, x, y)),
8102 DyadOp::PrimeExponents => {
8103 prime_exponents(x, y, cfg.near(), span).map(|r| carry_exact2(r, x, y))
8104 }
8105 DyadOp::Pick { origin } => pick(x, y, origin, cfg.near(), span),
8106 DyadOp::Expand => expand(x, y, cfg.rules.lang == crate::Lang::Apl, cfg.near(), span),
8107 DyadOp::WriteStream => Err(Error::internal("1!:2 reached the pure dyad dispatcher")),
8110 DyadOp::NotYet(what) => Err(Error::not_yet(what, span)),
8111 DyadOp::None => Err(Error::domain(format!("{} has no dyadic meaning", p.name), span)),
8112 }
8113}
8114
8115const APL_EXTREME: f64 = 1.7976e308;
8124
8125fn reduce_identity(v: &Verb, n: usize, lang: crate::Lang) -> Option<Data> {
8135 let Verb::Prim(p) = v else { return None };
8136 let DyadOp::Scalar(op) = p.dyad else { return None };
8137 let ints = |k: i64| Data::I64(vec![k; n].into());
8138 let bits = |k: u8| Data::Bool(vec![k; n].into());
8139 let extreme =
8140 |sign: f64| Data::F64(vec![sign * if lang == crate::Lang::Apl { APL_EXTREME } else { f64::INFINITY }; n].into());
8141 Some(match op {
8142 ScalarDyad::Add | ScalarDyad::Sub | ScalarDyad::Gcd | ScalarDyad::Residue => ints(0),
8143 ScalarDyad::Mul
8144 | ScalarDyad::DivJ
8145 | ScalarDyad::DivApl
8146 | ScalarDyad::Pow
8147 | ScalarDyad::Lcm
8148 | ScalarDyad::Root
8149 | ScalarDyad::Binomial => ints(1),
8150 ScalarDyad::Min => extreme(1.0),
8151 ScalarDyad::Max => extreme(-1.0),
8152 ScalarDyad::Eq | ScalarDyad::Le | ScalarDyad::Ge => bits(1),
8153 ScalarDyad::Ne | ScalarDyad::Lt | ScalarDyad::Gt => bits(0),
8154 ScalarDyad::MakeComplex | ScalarDyad::PolarBy => return None,
8157 ScalarDyad::Log | ScalarDyad::Circle => return None,
8160 })
8161}
8162
8163fn is_associative(op: ScalarDyad) -> bool {
8169 use ScalarDyad::*;
8170 matches!(op, Add | Mul | Min | Max)
8171}
8172
8173#[inline(always)]
8174fn fold_range_body<S, T, F>(
8175 v: &[S],
8176 m: usize,
8177 lo: usize,
8178 hi: usize,
8179 j0: usize,
8180 acc: &mut [T],
8181 step: &F,
8182) -> bool
8183where
8184 S: Widen<T>,
8185 T: Copy,
8186 F: Fn(T, T) -> (T, bool),
8187{
8188 let w = acc.len();
8189 let base = (hi - 1) * m + j0;
8190 for (slot, &x) in acc.iter_mut().zip(&v[base..base + w]) {
8191 *slot = x.widen();
8192 }
8193 let mut over = false;
8196 for i in (lo..hi - 1).rev() {
8197 let row = &v[i * m + j0..i * m + j0 + w];
8198 for (slot, &x) in acc.iter_mut().zip(row) {
8199 let (r, o) = step(x.widen(), *slot);
8200 *slot = r;
8201 over |= o;
8202 }
8203 }
8204 !over
8205}
8206
8207multiversioned! {
8208 #[allow(clippy::too_many_arguments)]
8209 fn fold_range_vectorised[S: Widen<T>, T: Copy, F: Fn(T, T) -> (T, bool)](
8210 v: &[S],
8211 m: usize,
8212 lo: usize,
8213 hi: usize,
8214 j0: usize,
8215 acc: &mut [T],
8216 step: &F,
8217 ) -> bool = fold_range_body;
8218}
8219
8220const VECTOR_COLUMNS: usize = 16;
8228
8229#[allow(clippy::too_many_arguments)]
8242#[inline]
8243fn fold_range<S, T, F>(
8244 v: &[S],
8245 m: usize,
8246 lo: usize,
8247 hi: usize,
8248 j0: usize,
8249 acc: &mut [T],
8250 step: &F,
8251) -> bool
8252where
8253 S: Widen<T>,
8254 T: Copy,
8255 F: Fn(T, T) -> (T, bool),
8256{
8257 if acc.len() < VECTOR_COLUMNS {
8258 fold_range_body(v, m, lo, hi, j0, acc, step)
8259 } else {
8260 fold_range_vectorised(v, m, lo, hi, j0, acc, step)
8261 }
8262}
8263
8264const FOLD_LANES: usize = 8;
8275
8276const MIN_LANE_WORK: usize = 8 * FOLD_LANES;
8281
8282#[inline(always)]
8287fn fold_lanes_body<S, T, F>(v: &[S], step: &F) -> Option<T>
8288where
8289 S: Widen<T>,
8290 T: Copy,
8291 F: Fn(T, T) -> (T, bool),
8292{
8293 let n = v.len();
8294 let mut over = false;
8295 if n < MIN_LANE_WORK {
8296 let mut acc = v[n - 1].widen();
8297 for &x in v[..n - 1].iter().rev() {
8298 let (r, o) = step(x.widen(), acc);
8299 acc = r;
8300 over |= o;
8301 }
8302 return (!over).then_some(acc);
8303 }
8304 let rows = n / FOLD_LANES;
8307 let head = n - rows * FOLD_LANES;
8308 let last = head + (rows - 1) * FOLD_LANES;
8309 let mut acc = [v[last].widen(); FOLD_LANES];
8310 for (slot, &x) in acc.iter_mut().zip(&v[last..last + FOLD_LANES]) {
8311 *slot = x.widen();
8312 }
8313 for r in (0..rows - 1).rev() {
8314 let row = &v[head + r * FOLD_LANES..head + (r + 1) * FOLD_LANES];
8315 for (slot, &x) in acc.iter_mut().zip(row) {
8316 let (r, o) = step(x.widen(), *slot);
8317 *slot = r;
8318 over |= o;
8319 }
8320 }
8321 let mut a = acc[FOLD_LANES - 1];
8322 for &x in acc[..FOLD_LANES - 1].iter().rev() {
8323 let (r, o) = step(x, a);
8324 a = r;
8325 over |= o;
8326 }
8327 for &x in v[..head].iter().rev() {
8328 let (r, o) = step(x.widen(), a);
8329 a = r;
8330 over |= o;
8331 }
8332 (!over).then_some(a)
8333}
8334
8335multiversioned! {
8336 fn fold_lanes_vectorised[S: Widen<T>, T: Copy, F: Fn(T, T) -> (T, bool)](
8337 v: &[S],
8338 step: &F,
8339 ) -> Option<T> = fold_lanes_body;
8340}
8341
8342#[inline]
8345fn fold_lanes<S, T, F>(v: &[S], step: &F) -> Option<T>
8346where
8347 S: Widen<T>,
8348 T: Copy,
8349 F: Fn(T, T) -> (T, bool),
8350{
8351 if v.len() < MIN_LANE_WORK {
8352 fold_lanes_body(v, step)
8353 } else {
8354 fold_lanes_vectorised(v, step)
8355 }
8356}
8357
8358fn fold_flat<S, T, F>(v: &[S], n: usize, assoc: bool, step: &F) -> Option<T>
8361where
8362 S: Widen<T>,
8363 T: Copy + Send + Sync,
8364 F: Fn(T, T) -> (T, bool) + Sync + Send,
8365{
8366 if assoc {
8367 return par::try_fold_chunks(
8368 &v[..n],
8369 |part| fold_lanes(part, step),
8370 |a, b| {
8371 let (r, o) = step(a, b);
8372 (!o).then_some(r)
8373 },
8374 );
8375 }
8376 let mut acc = v[n - 1].widen();
8377 let mut over = false;
8378 for &x in v[..n - 1].iter().rev() {
8379 let (r, o) = step(x.widen(), acc);
8380 acc = r;
8381 over |= o;
8382 }
8383 (!over).then_some(acc)
8384}
8385
8386fn fold_items<S, T, F>(v: &[S], n: usize, m: usize, assoc: bool, step: F) -> Option<Vec<T>>
8397where
8398 S: Widen<T>,
8399 T: Copy + Default + Send + Sync,
8400 F: Fn(T, T) -> (T, bool) + Sync + Send,
8401{
8402 if m >= par::WIDE_ITEM {
8403 let (out, ok) = par::fill_wide(m, n * m, |j0, acc: &mut [T]| {
8404 fold_range(v, m, 0, n, j0, acc, &step)
8405 });
8406 return ok.then_some(out);
8407 }
8408 if m == 1 {
8409 return fold_flat(v, n, assoc, &step).map(|x| vec![x]);
8410 }
8411 let chunks = if assoc { par::chunks(n, n * m) } else { 1 };
8412 if chunks < 2 {
8413 let mut acc = vec![T::default(); m];
8414 return fold_range(v, m, 0, n, 0, &mut acc, &step).then_some(acc);
8415 }
8416 let per = n.div_ceil(chunks);
8417 let parts = par::map_indexed(n.div_ceil(per), |c| {
8418 let mut acc = vec![T::default(); m];
8419 let ok = fold_range(v, m, c * per, ((c + 1) * per).min(n), 0, &mut acc, &step);
8420 ok.then_some(acc)
8421 });
8422 let mut it = parts.into_iter().rev();
8425 let mut acc = it.next()??;
8426 for part in it {
8427 let part = part?;
8428 let mut over = false;
8429 for (slot, &x) in acc.iter_mut().zip(&part) {
8430 let (r, o) = step(x, *slot);
8431 *slot = r;
8432 over |= o;
8433 }
8434 if over {
8435 return None;
8436 }
8437 }
8438 Some(acc)
8439}
8440
8441#[inline(always)]
8451fn block_f64(r: f64) -> (f64, bool) {
8452 (r, r.is_nan())
8453}
8454
8455fn fold_i64<S: Widen<i64>>(op: ScalarDyad, v: &[S], n: usize, m: usize) -> Option<Vec<i64>> {
8458 use ScalarDyad::*;
8459 let assoc = is_associative(op);
8460 match op {
8461 Add => fold_items(v, n, m, assoc, i64::overflowing_add),
8462 Sub => fold_items(v, n, m, assoc, i64::overflowing_sub),
8463 Mul => fold_items(v, n, m, assoc, i64::overflowing_mul),
8464 Min => fold_items(v, n, m, assoc, |a: i64, b: i64| (a.min(b), false)),
8465 Max => fold_items(v, n, m, assoc, |a: i64, b: i64| (a.max(b), false)),
8466 _ => None,
8467 }
8468}
8469
8470fn fold_cx(op: ScalarDyad, v: &[Cx], n: usize, m: usize) -> Option<Vec<Cx>> {
8471 use ScalarDyad::*;
8472 let assoc = is_associative(op);
8473 match op {
8474 Add => fold_items(v, n, m, assoc, |a: Cx, b: Cx| (cx::add(a, b), false)),
8475 Sub => fold_items(v, n, m, assoc, |a: Cx, b: Cx| (cx::sub(a, b), false)),
8476 Mul => fold_items(v, n, m, assoc, |a: Cx, b: Cx| (cx::mul(a, b), false)),
8477 _ => None,
8479 }
8480}
8481
8482fn fold_f64(op: ScalarDyad, v: &[f64], n: usize, m: usize) -> Option<Vec<f64>> {
8483 use ScalarDyad::*;
8484 let assoc = is_associative(op);
8485 match op {
8486 Add => fold_items(v, n, m, assoc, |a: f64, b: f64| block_f64(a + b)),
8487 Sub => fold_items(v, n, m, assoc, |a: f64, b: f64| block_f64(a - b)),
8488 Mul => fold_items(v, n, m, assoc, |a: f64, b: f64| block_f64(a * b)),
8489 Min => fold_items(v, n, m, assoc, |a: f64, b: f64| (a.min(b), false)),
8490 Max => fold_items(v, n, m, assoc, |a: f64, b: f64| (a.max(b), false)),
8491 _ => None,
8492 }
8493}
8494
8495fn reduce_typed(op: ScalarDyad, d: &Data, n: usize, m: usize) -> Option<Data> {
8499 use ScalarDyad::*;
8500 if !matches!(op, Add | Sub | Mul | Min | Max) {
8503 return None;
8504 }
8505 match d {
8506 Data::F64(v) => Some(Data::F64(fold_f64(op, v, n, m)?.into())),
8507 Data::Complex(v) => Some(Data::Complex(fold_cx(op, v, n, m)?.into())),
8508 Data::I64(v) => Some(Data::I64(fold_i64(op, v, n, m)?.into())),
8509 Data::Bool(v) => Some(Data::I64(fold_i64(op, v.as_slice(), n, m)?.into())),
8513 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
8516 }
8517}
8518
8519#[inline(always)]
8525fn fold_runs_body<S, T, F>(v: &[S], start: usize, m: usize, out: &mut [T], step: &F) -> bool
8526where
8527 S: Widen<T>,
8528 T: Copy,
8529 F: Fn(T, T) -> (T, bool),
8530{
8531 let mut over = false;
8532 for (k, slot) in out.iter_mut().enumerate() {
8533 let run = &v[(start + k) * m..(start + k + 1) * m];
8534 let mut acc = run[m - 1].widen();
8535 for &x in run[..m - 1].iter().rev() {
8536 let (r, o) = step(x.widen(), acc);
8537 acc = r;
8538 over |= o;
8539 }
8540 *slot = acc;
8541 }
8542 !over
8543}
8544
8545multiversioned! {
8546 fn fold_runs_vectorised[S: Widen<T>, T: Copy, F: Fn(T, T) -> (T, bool)](
8547 v: &[S],
8548 start: usize,
8549 m: usize,
8550 out: &mut [T],
8551 step: &F,
8552 ) -> bool = fold_runs_body;
8553}
8554
8555fn fold_runs<S, T, F>(v: &[S], n: usize, m: usize, step: F) -> Option<Vec<T>>
8558where
8559 S: Widen<T>,
8560 T: Copy + Default + Send + Sync,
8561 F: Fn(T, T) -> (T, bool) + Sync + Send,
8562{
8563 let wide = m >= VECTOR_COLUMNS;
8567 let (out, ok) = par::fill_wide(n, n * m, |start, part: &mut [T]| {
8568 if wide {
8569 fold_runs_vectorised(v, start, m, part, &step)
8570 } else {
8571 fold_runs_body(v, start, m, part, &step)
8572 }
8573 });
8574 ok.then_some(out)
8575}
8576
8577fn fold_runs_data(op: ScalarDyad, d: &Data, n: usize, m: usize) -> Option<Data> {
8578 use ScalarDyad::*;
8579 match d {
8580 Data::F64(v) => Some(Data::F64(
8581 match op {
8582 Add => fold_runs(v, n, m, |a: f64, b: f64| block_f64(a + b)),
8583 Sub => fold_runs(v, n, m, |a: f64, b: f64| block_f64(a - b)),
8584 Mul => fold_runs(v, n, m, |a: f64, b: f64| block_f64(a * b)),
8585 Min => fold_runs(v, n, m, |a: f64, b: f64| (a.min(b), false)),
8586 Max => fold_runs(v, n, m, |a: f64, b: f64| (a.max(b), false)),
8587 _ => None,
8588 }?
8589 .into(),
8590 )),
8591 Data::I64(v) => Some(Data::I64(fold_runs_i64(op, v.as_slice(), n, m)?.into())),
8592 Data::Complex(v) => Some(Data::Complex(
8594 match op {
8595 Add => fold_runs(v, n, m, |a: Cx, b: Cx| (cx::add(a, b), false)),
8596 Sub => fold_runs(v, n, m, |a: Cx, b: Cx| (cx::sub(a, b), false)),
8597 Mul => fold_runs(v, n, m, |a: Cx, b: Cx| (cx::mul(a, b), false)),
8598 _ => None,
8599 }?
8600 .into(),
8601 )),
8602 Data::Bool(v) => Some(Data::I64(fold_runs_i64(op, v.as_slice(), n, m)?.into())),
8604 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
8605 }
8606}
8607
8608fn fold_runs_i64<S: Widen<i64>>(op: ScalarDyad, v: &[S], n: usize, m: usize) -> Option<Vec<i64>> {
8610 use ScalarDyad::*;
8611 match op {
8612 Add => fold_runs(v, n, m, i64::overflowing_add),
8613 Sub => fold_runs(v, n, m, i64::overflowing_sub),
8614 Mul => fold_runs(v, n, m, i64::overflowing_mul),
8615 Min => fold_runs(v, n, m, |a: i64, b: i64| (a.min(b), false)),
8616 Max => fold_runs(v, n, m, |a: i64, b: i64| (a.max(b), false)),
8617 _ => None,
8618 }
8619}
8620
8621fn run_slices<T: Clone>(b: &Buf<T>, runs: usize, len: usize) -> Vec<&[T]> {
8637 if let Some(parts) = b.parts() && parts.len() == runs && parts.iter().all(|p| p.len() == len) {
8638 return parts.iter().map(Buf::as_slice).collect();
8639 }
8640 let flat = b.as_slice();
8641 (0..runs).map(|c| &flat[c * len..(c + 1) * len]).collect()
8642}
8643
8644fn fold_columns<S, T, F>(cols: &[&[S]], len: usize, assoc: bool, step: F) -> Option<Vec<T>>
8653where
8654 S: Widen<T>,
8655 T: Copy + Default + Send + Sync,
8656 F: Fn(T, T) -> (T, bool) + Sync + Send,
8657{
8658 if par::worth_it(len) {
8663 let mut out = Vec::with_capacity(cols.len());
8664 for c in cols {
8665 out.push(fold_flat(c, len, assoc, &step)?);
8666 }
8667 return Some(out);
8668 }
8669 let (out, ok) = par::fill_wide(cols.len(), cols.len() * len, |start, part: &mut [T]| {
8670 let mut ok = true;
8671 for (k, slot) in part.iter_mut().enumerate() {
8672 match fold_flat(cols[start + k], len, assoc, &step) {
8673 Some(v) => *slot = v,
8674 None => ok = false,
8675 }
8676 }
8677 ok
8678 });
8679 ok.then_some(out)
8680}
8681
8682fn fold_columns_data(op: ScalarDyad, d: &Data, runs: usize, len: usize) -> Option<Data> {
8684 use ScalarDyad::*;
8685 if !matches!(op, Add | Sub | Mul | Min | Max) {
8686 return None;
8687 }
8688 let assoc = is_associative(op);
8689 macro_rules! by {
8690 ($v:expr, $add:expr, $sub:expr, $mul:expr, $min:expr, $max:expr) => {{
8691 let cols = run_slices($v, runs, len);
8692 match op {
8693 Add => fold_columns(&cols, len, assoc, $add),
8694 Sub => fold_columns(&cols, len, assoc, $sub),
8695 Mul => fold_columns(&cols, len, assoc, $mul),
8696 Min => fold_columns(&cols, len, assoc, $min),
8697 Max => fold_columns(&cols, len, assoc, $max),
8698 _ => None,
8699 }?
8700 }};
8701 }
8702 match d {
8703 Data::F64(v) => Some(Data::F64(
8704 by!(
8705 v,
8706 |a: f64, b: f64| block_f64(a + b),
8707 |a: f64, b: f64| block_f64(a - b),
8708 |a: f64, b: f64| block_f64(a * b),
8709 |a: f64, b: f64| (a.min(b), false),
8710 |a: f64, b: f64| (a.max(b), false)
8711 )
8712 .into(),
8713 )),
8714 Data::I64(v) => Some(Data::I64(
8715 by!(
8716 v,
8717 i64::overflowing_add,
8718 i64::overflowing_sub,
8719 i64::overflowing_mul,
8720 |a: i64, b: i64| (a.min(b), false),
8721 |a: i64, b: i64| (a.max(b), false)
8722 )
8723 .into(),
8724 )),
8725 Data::Complex(v) => {
8726 if !matches!(op, Add | Sub | Mul) {
8727 return None;
8728 }
8729 Some(Data::Complex(
8730 by!(
8731 v,
8732 |a: Cx, b: Cx| (cx::add(a, b), false),
8733 |a: Cx, b: Cx| (cx::sub(a, b), false),
8734 |a: Cx, b: Cx| (cx::mul(a, b), false),
8735 |_: Cx, _: Cx| unreachable!("refused above"),
8736 |_: Cx, _: Cx| unreachable!("refused above")
8737 )
8738 .into(),
8739 ))
8740 }
8741 Data::Bool(v) => Some(Data::I64(
8745 by!(
8746 v,
8747 i64::overflowing_add,
8748 i64::overflowing_sub,
8749 i64::overflowing_mul,
8750 |a: i64, b: i64| (a.min(b), false),
8751 |a: i64, b: i64| (a.max(b), false)
8752 )
8753 .into(),
8754 )),
8755 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
8756 }
8757}
8758
8759fn reduce_columns(v: &Verb, y: &Array) -> Option<Array> {
8764 let Verb::Prim(p) = v else { return None };
8765 let DyadOp::Scalar(op) = p.dyad else { return None };
8766 if !y.dtype().is_numeric() {
8767 return None;
8768 }
8769 let n = y.shape[0];
8770 let m: usize = y.shape[1..].iter().product();
8771 if n == 0 || m == 0 {
8774 return None;
8775 }
8776 let shape = y.shape[1..].to_vec();
8777 if n == 1 {
8780 return Some(Array::col_major(shape, y.data.clone()));
8781 }
8782 let data = fold_columns_data(op, &y.data, m, n)?;
8783 Some(Array::col_major(shape, data))
8784}
8785
8786fn fold_across<S, T, F>(cols: &[&[S]], rows: usize, step: F) -> Option<Vec<T>>
8789where
8790 S: Widen<T>,
8791 T: Copy + Default + Send + Sync,
8792 F: Fn(T, T) -> (T, bool) + Sync + Send,
8793{
8794 let (last, rest) = cols.split_last()?;
8795 let (out, ok) = par::fill(rows, |start, part: &mut [T]| {
8796 let mut over = false;
8797 for (k, slot) in part.iter_mut().enumerate() {
8798 let i = start + k;
8799 let mut acc = last[i].widen();
8800 for c in rest.iter().rev() {
8801 let (r, o) = step(c[i].widen(), acc);
8802 acc = r;
8803 over |= o;
8804 }
8805 *slot = acc;
8806 }
8807 !over
8808 });
8809 ok.then_some(out)
8810}
8811
8812fn fold_across_data(op: ScalarDyad, d: &Data, rows: usize, cols: usize) -> Option<Data> {
8813 use ScalarDyad::*;
8814 if !matches!(op, Add | Sub | Mul | Min | Max) {
8815 return None;
8816 }
8817 macro_rules! by {
8818 ($v:expr, $add:expr, $sub:expr, $mul:expr, $min:expr, $max:expr) => {{
8819 let parts = run_slices($v, cols, rows);
8820 match op {
8821 Add => fold_across(&parts, rows, $add),
8822 Sub => fold_across(&parts, rows, $sub),
8823 Mul => fold_across(&parts, rows, $mul),
8824 Min => fold_across(&parts, rows, $min),
8825 Max => fold_across(&parts, rows, $max),
8826 _ => None,
8827 }?
8828 }};
8829 }
8830 match d {
8831 Data::F64(v) => Some(Data::F64(
8832 by!(
8833 v,
8834 |a: f64, b: f64| block_f64(a + b),
8835 |a: f64, b: f64| block_f64(a - b),
8836 |a: f64, b: f64| block_f64(a * b),
8837 |a: f64, b: f64| (a.min(b), false),
8838 |a: f64, b: f64| (a.max(b), false)
8839 )
8840 .into(),
8841 )),
8842 Data::I64(v) => Some(Data::I64(
8843 by!(
8844 v,
8845 i64::overflowing_add,
8846 i64::overflowing_sub,
8847 i64::overflowing_mul,
8848 |a: i64, b: i64| (a.min(b), false),
8849 |a: i64, b: i64| (a.max(b), false)
8850 )
8851 .into(),
8852 )),
8853 Data::Complex(v) => {
8854 if !matches!(op, Add | Sub | Mul) {
8855 return None;
8856 }
8857 Some(Data::Complex(
8858 by!(
8859 v,
8860 |a: Cx, b: Cx| (cx::add(a, b), false),
8861 |a: Cx, b: Cx| (cx::sub(a, b), false),
8862 |a: Cx, b: Cx| (cx::mul(a, b), false),
8863 |_: Cx, _: Cx| unreachable!("refused above"),
8864 |_: Cx, _: Cx| unreachable!("refused above")
8865 )
8866 .into(),
8867 ))
8868 }
8869 Data::Bool(v) => Some(Data::I64(
8871 by!(
8872 v,
8873 i64::overflowing_add,
8874 i64::overflowing_sub,
8875 i64::overflowing_mul,
8876 |a: i64, b: i64| (a.min(b), false),
8877 |a: i64, b: i64| (a.max(b), false)
8878 )
8879 .into(),
8880 )),
8881 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
8882 }
8883}
8884
8885fn reduce_rows_columns(u: &Verb, y: &Array) -> Option<Array> {
8888 let Verb::Reduce(inner) = u else { return None };
8889 let Verb::Prim(p) = &**inner else { return None };
8890 let DyadOp::Scalar(op) = p.dyad else { return None };
8891 if y.rank() != 2 || !y.dtype().is_numeric() {
8894 return None;
8895 }
8896 let (rows, cols) = (y.shape[0], y.shape[1]);
8897 if rows == 0 || cols == 0 {
8900 return None;
8901 }
8902 if cols == 1 {
8903 return Some(Array::new(vec![rows], y.data.clone()));
8905 }
8906 let data = fold_across_data(op, &y.data, rows, cols)?;
8907 Some(Array::new(vec![rows], data))
8908}
8909
8910fn reduce_vector_cells(u: &Verb, y: &Array, frame_rank: usize) -> Option<Array> {
8919 let Verb::Reduce(inner) = u else { return None };
8920 let Verb::Prim(p) = &**inner else { return None };
8921 let DyadOp::Scalar(op) = p.dyad else { return None };
8922 if y.rank() != frame_rank + 1 || !y.dtype().is_numeric() {
8925 return None;
8926 }
8927 let m = y.shape[frame_rank];
8928 if m == 0 {
8931 return None;
8932 }
8933 use ScalarDyad::{Add, Max, Min, Mul, Sub};
8934 if !matches!(op, Add | Sub | Mul | Min | Max) {
8935 return None;
8936 }
8937 let frame = y.shape[..frame_rank].to_vec();
8938 if m == 1 {
8939 return Some(Array::new(frame, y.data.clone()));
8942 }
8943 let n: usize = frame.iter().product();
8944 let data = fold_runs_data(op, &y.data, n, m)?;
8945 Some(Array::new(frame, data))
8946}
8947
8948fn reduce(v: &Verb, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
8950 if y.rank() == 0 {
8951 return Ok(y.clone());
8952 }
8953 let n = y.items();
8954 if n == 1 {
8955 return Ok(y.item(0));
8956 }
8957 let cell_shape = y.shape[1..].to_vec();
8958 let m: usize = cell_shape.iter().product();
8959 if n == 0 {
8960 if matches!(v, Verb::Prim(p) if matches!(p.dyad, DyadOp::AppendLeading | DyadOp::AppendLast))
8963 {
8964 return Ok(Array::new(vec![0], Data::empty(y.dtype())));
8965 }
8966 return match reduce_identity(v, m, ctx.cfg.rules.lang) {
8967 Some(d) => Ok(Array::new(cell_shape, d)),
8968 None => Err(Error::domain(
8969 format!("empty reduction has no identity for {}", v.name()),
8970 span,
8971 )),
8972 };
8973 }
8974 if y.dtype().is_numeric() && let Verb::Prim(p) = v && let DyadOp::Scalar(op) = p.dyad {
8975 if let Some(d) = reduce_typed(op, y.row_major_data(), n, m) {
8980 return Ok(Array::new(cell_shape, d));
8981 }
8982 let mut acc = y.data.slice((n - 1) * m, n * m);
8985 for i in (0..n - 1).rev() {
8986 acc =
8987 scalar_dyad_data(
8988 op,
8989 &y.data,
8990 i * m,
8991 1,
8992 &acc,
8993 0,
8994 1,
8995 m,
8996 ctx.cfg.tol,
8997 ctx.cfg.rules,
8998 span,
8999 )?;
9000 }
9001 return Ok(Array::new(cell_shape, acc));
9002 }
9003 if ctx.cfg.rules.lang == crate::Lang::Apl {
9004 return item_fold(v, y, ctx, span);
9005 }
9006 let mut acc = y.item(n - 1);
9007 for i in (0..n - 1).rev() {
9008 acc = v.dyad(&y.item(i), &acc, ctx, span)?;
9009 }
9010 Ok(acc)
9011}
9012
9013fn item_fold(v: &Verb, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
9024 let n = y.items();
9025 let frame = y.shape[1..].to_vec();
9026 let m: usize = frame.iter().product();
9027 if m == 0 {
9028 return assemble(&frame, Vec::new(), span);
9029 }
9030 let base = y.to_row_major();
9031 let cells = each_cell(m, base.count(), v.is_pure(), ctx, |p, c| {
9032 let mut acc = open_cell(&atom(&base, (n - 1) * m + p));
9033 for i in (0..n - 1).rev() {
9034 acc = v.dyad(&open_cell(&atom(&base, i * m + p)), &acc, c, span)?;
9035 }
9036 Ok(enclose(&acc, Enclose::ExceptSimpleScalar))
9037 })?;
9038 assemble_items(&frame, cells, span)
9039}
9040
9041fn folded_op(u: &Verb) -> Option<ScalarDyad> {
9048 let Verb::Reduce(inner) = u else { return None };
9049 let Verb::Prim(p) = &**inner else { return None };
9050 match p.dyad {
9051 DyadOp::Scalar(op) => Some(op),
9052 _ => None,
9053 }
9054}
9055
9056fn section(y: &Array, lo: usize, hi: usize) -> Array {
9058 let m = y.item_size();
9059 let mut shape = y.shape.clone();
9060 shape[0] = hi - lo;
9061 Array::new(shape, y.data.slice(lo * m, hi * m))
9062}
9063
9064fn as_items(y: &Array) -> Option<Array> {
9067 (y.rank() == 0).then(|| Array::new(vec![1], y.data.clone()))
9068}
9069
9070#[inline(always)]
9071fn scan_flat_body<S, T, F>(v: &[S], n: usize, m: usize, back: bool, step: F) -> Option<Vec<T>>
9072where
9073 S: Widen<T>,
9074 T: Copy + Default,
9075 F: Fn(T, T) -> (T, bool),
9076{
9077 if m == 1 {
9078 let mut out = vec![T::default(); n];
9081 let mut over = false;
9082 if back {
9083 let mut acc = v[n - 1].widen();
9084 out[n - 1] = acc;
9085 for (slot, &x) in out[..n - 1].iter_mut().zip(&v[..n - 1]).rev() {
9086 let (r, o) = step(x.widen(), acc);
9087 acc = r;
9088 over |= o;
9089 *slot = acc;
9090 }
9091 } else {
9092 let mut acc = v[0].widen();
9093 out[0] = acc;
9094 for (slot, &x) in out[1..n].iter_mut().zip(&v[1..n]) {
9095 let (r, o) = step(acc, x.widen());
9096 acc = r;
9097 over |= o;
9098 *slot = acc;
9099 }
9100 }
9101 return (!over).then_some(out);
9102 }
9103 let mut out = vec![T::default(); n * m];
9104 let mut acc = vec![T::default(); m];
9105 let mut over = false;
9106 if back {
9107 for (slot, &x) in acc.iter_mut().zip(&v[(n - 1) * m..n * m]) {
9108 *slot = x.widen();
9109 }
9110 out[(n - 1) * m..n * m].copy_from_slice(&acc);
9111 for i in (0..n - 1).rev() {
9112 for (j, slot) in acc.iter_mut().enumerate() {
9113 let (r, o) = step(v[i * m + j].widen(), *slot);
9114 *slot = r;
9115 over |= o;
9116 }
9117 out[i * m..i * m + m].copy_from_slice(&acc);
9118 }
9119 } else {
9120 for (slot, &x) in acc.iter_mut().zip(&v[..m]) {
9121 *slot = x.widen();
9122 }
9123 out[..m].copy_from_slice(&acc);
9124 for i in 1..n {
9125 for (j, slot) in acc.iter_mut().enumerate() {
9126 let (r, o) = step(*slot, v[i * m + j].widen());
9127 *slot = r;
9128 over |= o;
9129 }
9130 out[i * m..i * m + m].copy_from_slice(&acc);
9131 }
9132 }
9133 (!over).then_some(out)
9134}
9135
9136multiversioned! {
9137 fn scan_flat_vectorised[S: Widen<T>, T: Copy + Default, F: Fn(T, T) -> (T, bool)](
9138 v: &[S],
9139 n: usize,
9140 m: usize,
9141 back: bool,
9142 step: F,
9143 ) -> Option<Vec<T>> = scan_flat_body;
9144}
9145
9146fn scan_flat<S, T, F>(v: &[S], n: usize, m: usize, back: bool, step: F) -> Option<Vec<T>>
9158where
9159 S: Widen<T>,
9160 T: Copy + Default,
9161 F: Fn(T, T) -> (T, bool),
9162{
9163 if m < VECTOR_COLUMNS {
9164 scan_flat_body(v, n, m, back, step)
9165 } else {
9166 scan_flat_vectorised(v, n, m, back, step)
9167 }
9168}
9169
9170fn scan_i64<S: Widen<i64>>(
9171 op: ScalarDyad,
9172 v: &[S],
9173 n: usize,
9174 m: usize,
9175 back: bool,
9176) -> Option<Vec<i64>> {
9177 use ScalarDyad::*;
9178 match op {
9179 Add => scan_flat(v, n, m, back, i64::overflowing_add),
9180 Sub => scan_flat(v, n, m, back, i64::overflowing_sub),
9181 Mul => scan_flat(v, n, m, back, i64::overflowing_mul),
9182 Min => scan_flat(v, n, m, back, |a: i64, b: i64| (a.min(b), false)),
9183 Max => scan_flat(v, n, m, back, |a: i64, b: i64| (a.max(b), false)),
9184 _ => None,
9185 }
9186}
9187
9188fn scan_cx(op: ScalarDyad, v: &[Cx], n: usize, m: usize, back: bool) -> Option<Vec<Cx>> {
9189 use ScalarDyad::*;
9190 match op {
9191 Add => scan_flat(v, n, m, back, |a: Cx, b: Cx| (cx::add(a, b), false)),
9192 Sub => scan_flat(v, n, m, back, |a: Cx, b: Cx| (cx::sub(a, b), false)),
9193 Mul => scan_flat(v, n, m, back, |a: Cx, b: Cx| (cx::mul(a, b), false)),
9194 _ => None,
9195 }
9196}
9197
9198fn scan_f64<S: Widen<f64>>(
9199 op: ScalarDyad,
9200 v: &[S],
9201 n: usize,
9202 m: usize,
9203 back: bool,
9204) -> Option<Vec<f64>> {
9205 use ScalarDyad::*;
9206 match op {
9207 Add => scan_flat(v, n, m, back, |a: f64, b: f64| block_f64(a + b)),
9208 Sub => scan_flat(v, n, m, back, |a: f64, b: f64| block_f64(a - b)),
9209 Mul => scan_flat(v, n, m, back, |a: f64, b: f64| block_f64(a * b)),
9210 Min => scan_flat(v, n, m, back, |a: f64, b: f64| (a.min(b), false)),
9211 Max => scan_flat(v, n, m, back, |a: f64, b: f64| (a.max(b), false)),
9212 _ => None,
9213 }
9214}
9215
9216fn scan_typed(op: ScalarDyad, d: &Data, n: usize, m: usize, back: bool) -> Option<Data> {
9220 use ScalarDyad::*;
9221 if !matches!(op, Add | Sub | Mul | Min | Max) {
9222 return None;
9223 }
9224 fn ints<S: Widen<i64> + Widen<f64>>(
9228 op: ScalarDyad,
9229 v: &[S],
9230 n: usize,
9231 m: usize,
9232 back: bool,
9233 ) -> Data {
9234 match scan_i64(op, v, n, m, back) {
9235 Some(out) => Data::I64(out.into()),
9236 None => Data::F64(
9237 scan_f64(op, v, n, m, back).expect("the float scan cannot overflow").into(),
9238 ),
9239 }
9240 }
9241 match d {
9242 Data::F64(v) => Some(Data::F64(scan_f64(op, v.as_slice(), n, m, back)?.into())),
9243 Data::Complex(v) => Some(Data::Complex(scan_cx(op, v, n, m, back)?.into())),
9244 Data::I64(v) => Some(ints(op, v.as_slice(), n, m, back)),
9245 Data::Bool(v) => Some(ints(op, v.as_slice(), n, m, back)),
9246 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
9249 }
9250}
9251
9252fn affine_step(u: &Verb) -> Option<&Array> {
9259 fn prim(v: &Verb, want: DyadOp, ranks: [i64; 3]) -> bool {
9263 matches!(v, Verb::Prim(p) if p.dyad == want && p.ranks == ranks)
9264 }
9265 const ATOMS: [i64; 3] = [0, 0, 0];
9266 const WHOLE: [i64; 3] = [RANK_INF; 3];
9267 fn scaled(v: &Verb) -> Option<&Array> {
9269 let Verb::NounFork(c, g, h) = v else { return None };
9270 let noun = c.rank() == 0
9271 && matches!(c.dtype(), DType::Bool | DType::I64 | DType::F64 | DType::Complex);
9272 let tree = prim(g, DyadOp::Scalar(ScalarDyad::Mul), ATOMS)
9273 && prim(h, DyadOp::Right, WHOLE);
9274 (noun && tree).then_some(c)
9275 }
9276 let Verb::Fork(f, g, h) = u else { return None };
9277 if !prim(g, DyadOp::Scalar(ScalarDyad::Add), ATOMS) {
9278 return None;
9279 }
9280 if prim(f, DyadOp::Left, WHOLE) {
9281 scaled(h)
9282 } else if prim(h, DyadOp::Left, WHOLE) {
9283 scaled(f)
9284 } else {
9285 None
9286 }
9287}
9288
9289struct Ring<T> {
9292 add: fn(T, T) -> T,
9293 mul: fn(T, T) -> T,
9294 one: T,
9295 finite: fn(T) -> bool,
9296}
9297
9298fn affine_flat<T>(v: &[T], c: T, n: usize, m: usize, back: bool, r: &Ring<T>) -> Option<Vec<T>>
9304where
9305 T: Copy + Default,
9306{
9307 let (add, mul) = (r.add, r.mul);
9308 let mut out = vec![T::default(); n * m];
9309 if back {
9310 out[(n - 1) * m..].copy_from_slice(&v[(n - 1) * m..n * m]);
9311 for i in (0..n - 1).rev() {
9312 for j in 0..m {
9313 out[i * m + j] = add(v[i * m + j], mul(c, out[(i + 1) * m + j]));
9314 }
9315 }
9316 } else {
9317 out[..m].copy_from_slice(&v[..m]);
9318 let mut pow = r.one;
9319 for i in 1..n {
9320 pow = mul(pow, c);
9321 if !(r.finite)(pow) {
9322 return None;
9323 }
9324 for j in 0..m {
9325 out[i * m + j] = add(out[(i - 1) * m + j], mul(pow, v[i * m + j]));
9326 }
9327 }
9328 }
9329 Some(out)
9330}
9331
9332fn affine_scan(c: &Array, y: &Array, back: bool) -> Option<Data> {
9342 let (n, m) = (y.items(), y.item_size());
9343 let machine = |t: DType| matches!(t, DType::Bool | DType::I64 | DType::F64 | DType::Complex);
9344 if n == 0 || !machine(c.dtype()) || !machine(y.dtype()) {
9345 return None;
9346 }
9347 match DType::promote(c.dtype(), y.dtype())? {
9348 DType::F64 => {
9349 let (mut tc, mut tv) = (Vec::new(), Vec::new());
9350 let k = *borrow_f64(&c.data, &mut tc).first()?;
9351 let v = borrow_f64(y.row_major_data(), &mut tv);
9352 let r = Ring { add: |a, b| a + b, mul: |a, b| a * b, one: 1.0, finite: f64::is_finite };
9353 Some(Data::F64(affine_flat(v, k, n, m, back, &r)?.into()))
9354 }
9355 DType::Complex => {
9356 let (mut tc, mut tv) = (Vec::new(), Vec::new());
9357 let k = *borrow_cx(&c.data, &mut tc).first()?;
9358 let v = borrow_cx(y.row_major_data(), &mut tv);
9359 let finite = |z: Cx| z[0].is_finite() && z[1].is_finite();
9360 let r = Ring { add: cx::add, mul: cx::mul, one: [1.0, 0.0], finite };
9361 Some(Data::Complex(affine_flat(v, k, n, m, back, &r)?.into()))
9362 }
9363 _ => None,
9364 }
9365}
9366
9367fn window_fold<S, T, F>(v: &[S], n: usize, m: usize, w: usize, step: F) -> Option<Vec<T>>
9382where
9383 S: Widen<T>,
9384 T: Copy + Default + Send + Sync,
9385 F: Fn(T, T) -> (T, bool) + Sync + Send,
9386{
9387 debug_assert!(w >= 1 && n >= w);
9388 if m == 1 {
9389 return window_fold_flat(v, n, w, step);
9390 }
9391 let count = n - w + 1;
9392 let mut out = vec![T::default(); count * m];
9393 let mut pre = vec![T::default(); w * m];
9396 let mut suf = vec![T::default(); w * m];
9397 let mut prev = vec![T::default(); w * m];
9398 let mut over = false;
9399 for b in 0..n.div_ceil(w) {
9400 let bs = b * w;
9401 let be = ((b + 1) * w).min(n);
9402 for (slot, &x) in pre[..m].iter_mut().zip(&v[bs * m..bs * m + m]) {
9403 *slot = x.widen();
9404 }
9405 for i in 1..be - bs {
9406 let (o, p) = (i * m, (i - 1) * m);
9407 for j in 0..m {
9408 let (r, f) = step(pre[p + j], v[(bs + i) * m + j].widen());
9409 pre[o + j] = r;
9410 over |= f;
9411 }
9412 }
9413 for e in bs.max(w - 1)..be {
9416 let i = e + 1 - w;
9417 let (oo, po) = (i * m, (e - bs) * m);
9418 if i == bs {
9419 out[oo..oo + m].copy_from_slice(&pre[po..po + m]);
9420 } else {
9421 let so = (i + w - bs) * m;
9422 for j in 0..m {
9423 let (r, f) = step(prev[so + j], pre[po + j]);
9424 out[oo + j] = r;
9425 over |= f;
9426 }
9427 }
9428 }
9429 let last = be - 1 - bs;
9430 for (slot, &x) in suf[last * m..last * m + m]
9431 .iter_mut()
9432 .zip(&v[(be - 1) * m..be * m])
9433 {
9434 *slot = x.widen();
9435 }
9436 for i in (0..last).rev() {
9437 let (o, p) = (i * m, (i + 1) * m);
9438 for j in 0..m {
9439 let (r, f) = step(v[(bs + i) * m + j].widen(), suf[p + j]);
9440 suf[o + j] = r;
9441 over |= f;
9442 }
9443 }
9444 std::mem::swap(&mut prev, &mut suf);
9445 }
9446 (!over).then_some(out)
9447}
9448
9449fn window_fold_flat<S, T, F>(v: &[S], n: usize, w: usize, step: F) -> Option<Vec<T>>
9459where
9460 S: Widen<T>,
9461 T: Copy + Default + Send + Sync,
9462 F: Fn(T, T) -> (T, bool) + Sync + Send,
9463{
9464 let (out, ok) = par::fill(n - w + 1, |lo, part: &mut [T]| {
9465 window_fold_range(v, n, w, lo, part, &step)
9466 });
9467 ok.then_some(out)
9468}
9469
9470#[inline(always)]
9471fn window_fold_range_body<S, T, F>(
9472 v: &[S],
9473 n: usize,
9474 w: usize,
9475 lo: usize,
9476 out: &mut [T],
9477 step: &F,
9478) -> bool
9479where
9480 S: Widen<T>,
9481 T: Copy + Default,
9482 F: Fn(T, T) -> (T, bool),
9483{
9484 if out.is_empty() {
9485 return true;
9486 }
9487 let hi = lo + out.len();
9488 let mut pre = vec![T::default(); w];
9489 let mut suf = vec![T::default(); w];
9490 let mut prev = vec![T::default(); w];
9491 let mut over = false;
9492 let mut bs = lo / w * w;
9493 while bs < n && bs <= hi + w - 2 {
9495 let block = &v[bs..(bs + w).min(n)];
9496 let lb = block.len();
9497 let mut acc = block[0].widen();
9498 pre[0] = acc;
9499 for (slot, &x) in pre[1..lb].iter_mut().zip(&block[1..]) {
9500 let (r, o) = step(acc, x.widen());
9501 acc = r;
9502 over |= o;
9503 *slot = acc;
9504 }
9505 for e in bs.max(lo + w - 1)..(bs + lb).min(hi + w - 1) {
9510 let i = e + 1 - w;
9511 out[i - lo] = if i == bs {
9512 pre[e - bs]
9513 } else {
9514 let (r, o) = step(prev[i + w - bs], pre[e - bs]);
9515 over |= o;
9516 r
9517 };
9518 }
9519 let mut acc = block[lb - 1].widen();
9520 suf[lb - 1] = acc;
9521 for (slot, &x) in suf[..lb - 1].iter_mut().zip(&block[..lb - 1]).rev() {
9522 let (r, o) = step(x.widen(), acc);
9523 acc = r;
9524 over |= o;
9525 *slot = acc;
9526 }
9527 std::mem::swap(&mut prev, &mut suf);
9528 bs += w;
9529 }
9530 !over
9531}
9532
9533multiversioned! {
9534 fn window_fold_range[S: Widen<T>, T: Copy + Default, F: Fn(T, T) -> (T, bool)](
9539 v: &[S],
9540 n: usize,
9541 w: usize,
9542 lo: usize,
9543 out: &mut [T],
9544 step: &F,
9545 ) -> bool = window_fold_range_body;
9546}
9547
9548pub(crate) fn windows_into<S, T, F>(v: &[S], w: usize, lo: usize, out: &mut [T], step: &F) -> bool
9557where
9558 S: Widen<T>,
9559 T: Copy + Default,
9560 F: Fn(T, T) -> (T, bool),
9561{
9562 window_fold_range(v, v.len(), w, lo, out, step)
9563}
9564
9565fn window_i64<S: Widen<i64>>(
9566 op: ScalarDyad,
9567 v: &[S],
9568 n: usize,
9569 m: usize,
9570 w: usize,
9571) -> Option<Vec<i64>> {
9572 use ScalarDyad::*;
9573 match op {
9574 Add => window_fold(v, n, m, w, i64::overflowing_add),
9575 Mul => window_fold(v, n, m, w, i64::overflowing_mul),
9576 Min => window_fold(v, n, m, w, |a: i64, b: i64| (a.min(b), false)),
9577 Max => window_fold(v, n, m, w, |a: i64, b: i64| (a.max(b), false)),
9578 _ => None,
9579 }
9580}
9581
9582fn window_cx(op: ScalarDyad, v: &[Cx], n: usize, m: usize, w: usize) -> Option<Vec<Cx>> {
9583 use ScalarDyad::*;
9584 match op {
9585 Add => window_fold(v, n, m, w, |a: Cx, b: Cx| (cx::add(a, b), false)),
9586 Mul => window_fold(v, n, m, w, |a: Cx, b: Cx| (cx::mul(a, b), false)),
9587 _ => None,
9588 }
9589}
9590
9591fn window_f64<S: Widen<f64>>(
9592 op: ScalarDyad,
9593 v: &[S],
9594 n: usize,
9595 m: usize,
9596 w: usize,
9597) -> Option<Vec<f64>> {
9598 use ScalarDyad::*;
9599 match op {
9600 Add => window_fold(v, n, m, w, |a: f64, b: f64| block_f64(a + b)),
9601 Mul => window_fold(v, n, m, w, |a: f64, b: f64| block_f64(a * b)),
9602 Min => window_fold(v, n, m, w, |a: f64, b: f64| (a.min(b), false)),
9603 Max => window_fold(v, n, m, w, |a: f64, b: f64| (a.max(b), false)),
9604 _ => None,
9605 }
9606}
9607
9608fn window_typed(op: ScalarDyad, d: &Data, n: usize, m: usize, w: usize) -> Option<Data> {
9612 use ScalarDyad::*;
9613 if !matches!(op, Add | Mul | Min | Max) {
9614 return None;
9615 }
9616 fn ints<S: Widen<i64> + Widen<f64>>(
9619 op: ScalarDyad,
9620 v: &[S],
9621 n: usize,
9622 m: usize,
9623 w: usize,
9624 ) -> Data {
9625 match window_i64(op, v, n, m, w) {
9626 Some(out) => Data::I64(out.into()),
9627 None => {
9628 Data::F64(window_f64(op, v, n, m, w).expect("the float fold cannot overflow").into())
9629 }
9630 }
9631 }
9632 match d {
9633 Data::F64(v) => Some(Data::F64(window_f64(op, v.as_slice(), n, m, w)?.into())),
9634 Data::Complex(v) => Some(Data::Complex(window_cx(op, v, n, m, w)?.into())),
9635 Data::I64(v) => Some(ints(op, v.as_slice(), n, m, w)),
9636 Data::Bool(v) => Some(ints(op, v.as_slice(), n, m, w)),
9637 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
9640 }
9641}
9642
9643fn runs(u: &Verb, y: &Array, back: bool, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
9645 let promoted = as_items(y);
9646 let base = promoted.as_ref().unwrap_or(y);
9647 let n = base.items();
9648 let m = base.item_size();
9649 if n == 0 {
9655 if ctx.cfg.rules.lang == crate::Lang::Apl {
9656 return Ok(Array::new(base.shape.clone(), Data::empty(base.dtype())));
9657 }
9658 let cell = u.is_pure().then(|| base.clone());
9659 return Ok(empty_frame(&[0], base.dtype(), cell, ctx, |cell, c| u.monad(cell, c, span)));
9660 }
9661 if n > 0 && base.dtype().is_numeric() && let Some(op) = folded_op(u) {
9662 if (back || is_associative(op))
9665 && let Some(d) = scan_typed(op, base.row_major_data(), n, m, back)
9666 {
9667 return Ok(Array::new(base.shape.clone(), d));
9668 }
9669 }
9670 if n > 0 && let Verb::Reduce(inner) = u {
9671 if base.dtype().is_numeric()
9672 && let Some(c) = affine_step(inner)
9673 && let Some(d) = affine_scan(c, base, back)
9674 {
9675 return Ok(Array::new(base.shape.clone(), d));
9676 }
9677 if back && u.is_pure() {
9684 let mut acc = base.item(n - 1);
9685 let mut cells = Vec::with_capacity(n);
9686 cells.push(acc.clone());
9687 for i in (0..n - 1).rev() {
9688 acc = inner.dyad(&base.item(i), &acc, ctx, span)?;
9689 cells.push(acc.clone());
9690 }
9691 cells.reverse();
9692 return assemble(&[n], cells, span);
9693 }
9694 }
9695 let apl = ctx.cfg.rules.lang == crate::Lang::Apl;
9696 let cells = each_cell(n, n * m, u.is_pure(), ctx, |i, c| {
9697 let part = if back { section(base, i, n) } else { section(base, 0, i + 1) };
9698 u.monad(&part, c, span)
9699 })?;
9700 if apl { assemble_items(&[n], cells, span) } else { assemble(&[n], cells, span) }
9701}
9702
9703fn empty_windows(u: &Verb, y: &Array, w: usize, ctx: &mut Ctx<'_>, span: Span) -> Array {
9708 let m = y.item_size();
9709 if u.is_pure() && let Some(cells) = w.checked_mul(m).filter(|&s| s <= 1 << 20) {
9710 let mut shape = y.shape.clone();
9711 shape[0] = w;
9712 let probe = Array::new(shape, fill_data(y.dtype(), cells));
9713 if let Ok(cell) = u.monad(&probe, ctx, span) {
9714 let mut shape = vec![0usize];
9715 shape.extend_from_slice(&cell.shape);
9716 return Array::new(shape, Data::empty(cell.dtype()));
9717 }
9718 }
9719 Array::new(vec![0], Data::empty(DType::I64))
9720}
9721
9722fn window_size(x: &Array, near: NearInt, span: Span) -> Result<i64> {
9724 let v = x
9725 .to_i64_vec_near(near)
9726 .ok_or_else(|| Error::domain("the window size must be an integer", span))?;
9727 match v.as_slice() {
9728 [k] => Ok(*k),
9729 _ => Err(Error::new(
9730 ErrorKind::Length,
9731 "the window size must be a single number",
9732 Some(span),
9733 )),
9734 }
9735}
9736
9737fn infix(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
9745 let k = window_size(x, ctx.cfg.near(), span)?;
9746 let promoted = as_items(y);
9747 let base = promoted.as_ref().unwrap_or(y);
9748 let n = base.items();
9749 let m = base.item_size();
9750 if k < 0 {
9751 let w = k.unsigned_abs() as usize;
9752 let count = n.div_ceil(w);
9753 let cells = each_cell(count, n * m, u.is_pure(), ctx, |i, c| {
9754 u.monad(§ion(base, i * w, ((i + 1) * w).min(n)), c, span)
9755 })?;
9756 return assemble(&[count], cells, span);
9757 }
9758 let w = k as usize;
9759 if n < w {
9760 return Ok(empty_windows(u, base, w, ctx, span));
9761 }
9762 let count = n - w + 1;
9763 if w > 0 && base.dtype().is_numeric()
9764 && let Some(op) = folded_op(u) && let Some(d) = window_typed(op, &base.data, n, m, w)
9765 {
9766 let mut shape = base.shape.clone();
9767 shape[0] = count;
9768 return Ok(Array::new(shape, d));
9769 }
9770 let work = count.saturating_mul(w).saturating_mul(m);
9771 let cells = each_cell(count, work, u.is_pure(), ctx, |i, c| {
9772 u.monad(§ion(base, i, i + w), c, span)
9773 })?;
9774 assemble(&[count], cells, span)
9775}
9776
9777fn nwise(f: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
9790 if x.count() != 1 {
9794 return Err(Error::new(
9795 ErrorKind::Length,
9796 "the window size must be a single number",
9797 Some(span),
9798 ));
9799 }
9800 let k = window_size(x, ctx.cfg.near(), span)?;
9801 let promoted = as_items(y);
9802 if promoted.is_some() && k.unsigned_abs() == 1 {
9806 return Ok(y.clone());
9807 }
9808 let base = promoted.as_ref().unwrap_or(y).to_row_major();
9809 let base = &base;
9810 let n = base.items();
9811 let m = base.item_size();
9812 let w = k.unsigned_abs() as usize;
9813 if w > n + 1 {
9814 return Err(Error::domain(
9815 format!("a window of {w} does not fit an axis of {n}"),
9816 span,
9817 ));
9818 }
9819 let count = n + 1 - w;
9820 let fold = Verb::Reduce(Box::new(f.clone()));
9821 if count == 0 {
9822 return Ok(empty_windows(&fold, base, w, ctx, span));
9823 }
9824 if w > 0
9829 && base.dtype().is_numeric()
9830 && let Some(op) = scalar_dyad_of(f)
9831 && let Some(d) = window_typed(op, base.row_major_data(), n, m, w)
9832 {
9833 let mut shape = base.shape.clone();
9834 shape[0] = count;
9835 return Ok(Array::new(shape, d));
9836 }
9837 let work = count.saturating_mul(w.max(1)).saturating_mul(m);
9838 let cells = each_cell(count, work, f.is_pure(), ctx, |i, c| {
9839 let win = section(base, i, i + w);
9840 let win = if k < 0 { reverse(&win) } else { win };
9841 fold.monad(&win, c, span)
9842 })?;
9843 assemble(&[count], cells, span)
9844}
9845
9846fn power(
9849 u: &Verb,
9850 p: Power,
9851 x: Option<&Array>,
9852 y: &Array,
9853 ctx: &mut Ctx<'_>,
9854 span: Span,
9855) -> Result<Array> {
9856 let step = |acc: &Array, c: &mut Ctx<'_>| match x {
9857 Some(x) => u.dyad(x, acc, c, span),
9858 None => u.monad(acc, c, span),
9859 };
9860 match p {
9861 Power::Times(n) => {
9862 let mut acc = y.clone();
9863 for _ in 0..n {
9864 acc = step(&acc, ctx)?;
9865 }
9866 Ok(acc)
9867 }
9868 Power::Converge => {
9869 let mut acc = y.clone();
9870 for _ in 0..CONVERGE_LIMIT {
9871 let next = step(&acc, ctx)?;
9872 if arrays_match(&next, &acc, ctx.cfg.tol) {
9873 return Ok(next);
9874 }
9875 acc = next;
9876 }
9877 Err(Error::domain("the iteration did not converge", span))
9878 }
9879 Power::Each(ref counts) => {
9883 let mut acc = y.clone();
9884 let mut done = 0u64;
9885 let mut order: Vec<usize> = (0..counts.len()).collect();
9886 order.sort_by_key(|&i| counts[i]);
9887 let mut cells: Vec<Option<Array>> = vec![None; counts.len()];
9888 for i in order {
9889 while done < counts[i] {
9890 acc = step(&acc, ctx)?;
9891 done += 1;
9892 }
9893 cells[i] = Some(acc.clone());
9894 }
9895 let cells: Vec<Array> = cells.into_iter().map(|c| c.expect("every count filled")).collect();
9896 assemble(&[cells.len()], cells, span)
9897 }
9898 Power::ConvergeTrace => {
9899 let mut acc = y.clone();
9900 let mut cells = vec![acc.clone()];
9901 for _ in 0..CONVERGE_LIMIT {
9902 let next = step(&acc, ctx)?;
9903 if arrays_match(&next, &acc, ctx.cfg.tol) {
9904 return assemble(&[cells.len()], cells, span);
9905 }
9906 cells.push(next.clone());
9907 acc = next;
9908 }
9909 Err(Error::domain("the iteration did not converge", span))
9910 }
9911 }
9912}
9913
9914fn power_v(
9917 u: &Verb,
9918 v: &Verb,
9919 x: Option<&Array>,
9920 y: &Array,
9921 ctx: &mut Ctx<'_>,
9922 span: Span,
9923) -> Result<Array> {
9924 let count = match x {
9925 Some(x) => v.dyad(x, y, ctx, span)?,
9926 None => v.monad(y, ctx, span)?,
9927 };
9928 let n = count
9929 .to_i64_vec_near(ctx.cfg.near())
9930 .ok_or_else(|| Error::domain("the power count must be an integer", span))?;
9931 if n.len() != 1 {
9932 return Err(Error::not_yet("a list of power counts (u^:v with several)", span));
9933 }
9934 let n = n[0];
9935 if n < 0 {
9936 return Err(Error::not_yet("a negative power (the verb's inverse)", span));
9937 }
9938 power(u, Power::Times(n as u64), x, y, ctx, span)
9939}
9940
9941fn power_until(
9943 u: &Verb,
9944 test: &Verb,
9945 y: &Array,
9946 ctx: &mut Ctx<'_>,
9947 span: Span,
9948) -> Result<Array> {
9949 let mut acc = y.clone();
9950 for _ in 0..CONVERGE_LIMIT {
9951 let next = u.monad(&acc, ctx, span)?;
9952 let done = test.dyad(&next, &acc, ctx, span)?;
9953 let stop = done
9954 .to_f64_vec()
9955 .ok_or_else(|| Error::domain("the ⍣ test must answer with numbers", span))?;
9956 if !stop.is_empty() && stop.iter().all(|&v| v != 0.0) {
9957 return Ok(next);
9958 }
9959 acc = next;
9960 }
9961 Err(Error::domain("the iteration did not converge", span))
9962}
9963
9964fn along_axis(
9971 u: &Verb,
9972 x: Option<&Array>,
9973 y: &Array,
9974 k: usize,
9975 ctx: &mut Ctx<'_>,
9976 span: Span,
9977) -> Result<Array> {
9978 if k >= y.rank().max(1) {
9979 return Err(Error::new(
9980 ErrorKind::Rank,
9981 format!("axis {k} does not exist on an argument of rank {}", y.rank()),
9982 Some(span),
9983 ));
9984 }
9985 let moved = axis_to_front(y, k);
9986 let r = moved.rank();
9987 let out = match x {
9988 Some(x) => u.dyad(x, &moved, ctx, span)?,
9989 None => u.monad(&moved, ctx, span)?,
9990 };
9991 if out.rank() == r {
9992 return Ok(front_to_axis(&out, k));
9993 }
9994 Ok(out)
9995}
9996
9997fn where_indices(y: &Array, origin: i64, boxed: bool, near: NearInt, span: Span) -> Result<Array> {
10005 let counts = y
10006 .to_i64_vec_near(near)
10007 .ok_or_else(|| Error::domain("indices needs non-negative integers", span))?;
10008 if counts.iter().any(|&c| c < 0) {
10009 return Err(Error::domain("indices needs non-negative integers", span));
10010 }
10011 if !boxed || y.rank() < 2 {
10012 let mut out = Vec::new();
10013 for (i, &c) in counts.iter().enumerate() {
10014 for _ in 0..c {
10015 out.push(origin + i as i64);
10016 }
10017 }
10018 return Ok(Array::from_i64(out));
10019 }
10020 let r = y.rank();
10021 let mut coord = vec![0usize; r];
10022 let mut out: Vec<Array> = Vec::new();
10023 for &c in &counts {
10024 if c > 0 {
10025 let point =
10026 Array::from_i64(coord.iter().map(|&k| origin + k as i64).collect::<Vec<_>>());
10027 for _ in 0..c {
10028 out.push(point.clone());
10029 }
10030 }
10031 odometer(&mut coord, &y.shape);
10032 }
10033 Ok(Array::new(vec![out.len()], Data::Box(out.into())))
10034}
10035
10036fn indices_inverse(y: &Array, near: NearInt, span: Span) -> Result<Array> {
10040 if y.count() == 0 {
10041 return Ok(Array::empty(DType::I64));
10042 }
10043 let at = y
10044 .to_i64_vec_near(near)
10045 .ok_or_else(|| Error::domain("the obverse of indices needs integers", span))?;
10046 if at.iter().any(|&i| i < 0) {
10047 return Err(Error::domain("the obverse of indices needs non-negative integers", span));
10048 }
10049 let Some(&top) = at.iter().max() else {
10050 return Ok(Array::empty(DType::I64));
10051 };
10052 let mut counts = vec![0i64; top as usize + 1];
10053 for &i in &at {
10054 counts[i as usize] += 1;
10055 }
10056 Ok(Array::from_i64(counts))
10057}
10058
10059fn interval_index(
10065 x: &Array,
10066 y: &Array,
10067 offset: i64,
10068 closed: bool,
10069 tol: Tol,
10070 ord: Grading,
10071 span: Span,
10072) -> Result<Array> {
10073 if !x.dtype().is_numeric() || !y.dtype().is_numeric() {
10076 return ordered_interval_index(x, y, offset, closed, ord, span);
10077 }
10078 let bounds = x
10079 .to_f64_vec()
10080 .ok_or_else(|| Error::domain("interval index needs numeric bounds", span))?;
10081 let vals = y
10082 .to_f64_vec()
10083 .ok_or_else(|| Error::domain("interval index needs numeric values", span))?;
10084 let out: Vec<i64> = vals
10085 .iter()
10086 .map(|&v| {
10087 let count =
10090 bounds.iter().filter(|&&b| if closed { !tol.lt(v, b) } else { tol.lt(b, v) });
10091 offset + count.count() as i64
10092 })
10093 .collect();
10094 Ok(Array::new(y.shape.clone(), Data::I64(out.into())))
10095}
10096
10097fn ordered_interval_index(
10101 x: &Array,
10102 y: &Array,
10103 offset: i64,
10104 closed: bool,
10105 ord: Grading,
10106 span: Span,
10107) -> Result<Array> {
10108 let (xr, yr) = (x.to_row_major(), y.to_row_major());
10109 let (bounds, vals) = (&xr.data, &yr.data);
10110 let cmp = |i: usize, j: usize| -> Option<std::cmp::Ordering> {
10111 match (bounds, vals) {
10112 (Data::Char(p), Data::Char(q)) => Some(p[i].cmp(&q[j])),
10113 (Data::Symbol(p), Data::Symbol(q)) => Some(crate::symbol::cmp(p[i], q[j])),
10114 (Data::Box(p), Data::Box(q)) if ord.tao == Tao::J => {
10119 Some(cmp_items_total(&p[i], &q[j], ord))
10120 }
10121 _ => None,
10122 }
10123 };
10124 let mut out = Vec::with_capacity(y.count());
10125 for j in 0..y.count() {
10126 let mut count = 0i64;
10127 for i in 0..x.count() {
10128 let ord = cmp(i, j).ok_or_else(|| {
10129 Error::domain(
10130 format!(
10131 "interval index compares {} bounds with {} values",
10132 x.dtype().name(),
10133 y.dtype().name()
10134 ),
10135 span,
10136 )
10137 })?;
10138 count += i64::from(if closed { ord.is_le() } else { ord.is_lt() });
10140 }
10141 out.push(offset + count);
10142 }
10143 Ok(Array::new(y.shape.clone(), Data::I64(out.into())))
10144}
10145
10146fn steps(y: &Array, span: Span) -> Result<Array> {
10149 let vals = y.to_f64_vec().ok_or_else(|| Error::domain("steps needs a number", span))?;
10150 let v = match vals.first() {
10151 Some(&v) if v.is_finite() => v,
10152 _ => return Err(Error::domain("steps needs a finite number", span)),
10153 };
10154 let n = (2.0 * v.abs()).floor();
10155 if n > 1e7 {
10156 return Err(Error::domain("steps would produce too many items", span));
10157 }
10158 let n = n as i64 + 1;
10159 let step = if v < 0.0 { -1.0 } else { 1.0 };
10160 let start = -v;
10161 if v.fract() == 0.0 {
10162 let start = start as i64;
10163 let step = step as i64;
10164 return Ok(Array::from_i64((0..n).map(|k| start + k * step).collect()));
10165 }
10166 Ok(Array::from_f64((0..n).map(|k| start + k as f64 * step).collect()))
10167}
10168
10169fn index_of_last(x: &Array, y: &Array, origin: i64, tol: Tol) -> Array {
10171 let cell_rank = x.rank().saturating_sub(1).min(y.rank());
10172 let frame_rank = y.rank() - cell_rank;
10173 let frame: Vec<usize> = y.shape[..frame_rank].to_vec();
10174 let nf: usize = frame.iter().product();
10175 let items = x.items();
10176 let mut out = Vec::with_capacity(nf);
10177 for i in 0..nf {
10178 let cell = y.cell_at(frame_rank, i);
10179 let at = (0..items)
10180 .rev()
10181 .find(|&j| arrays_match(&cell, &item_or_self(x, j), tol))
10182 .unwrap_or(items);
10183 out.push(origin + at as i64);
10184 }
10185 Array::new(frame, Data::I64(out.into()))
10186}
10187
10188fn roll(
10195 y: &Array,
10196 origin: i64,
10197 fixed: bool,
10198 float_at_zero: bool,
10199 near: NearInt,
10200 span: Span,
10201) -> Result<Array> {
10202 let bounds = y
10203 .to_i64_vec_near(near)
10204 .ok_or_else(|| Error::domain("roll needs whole numbers", span))?;
10205 if bounds.iter().any(|&b| b < 0) {
10206 return Err(Error::domain("roll needs non-negative numbers", span));
10207 }
10208 if !float_at_zero && bounds.contains(&0) {
10209 return Err(Error::domain("? 0 has no value: the range is empty", span));
10210 }
10211 let any_zero = bounds.contains(&0);
10213 crate::rng::with(fixed, |g| {
10214 if any_zero {
10215 let out: Vec<f64> = bounds
10216 .iter()
10217 .map(|&b| {
10218 if b == 0 {
10219 g.unit()
10220 } else {
10221 (origin + g.below(b as u64) as i64) as f64
10222 }
10223 })
10224 .collect();
10225 return Ok(Array::new(y.shape.clone(), Data::F64(out.into())));
10226 }
10227 let out: Vec<i64> =
10228 bounds.iter().map(|&b| origin + g.below(b as u64) as i64).collect();
10229 Ok(Array::new(y.shape.clone(), Data::I64(out.into())))
10230 })
10231}
10232
10233fn deal(
10235 x: &Array,
10236 y: &Array,
10237 origin: i64,
10238 fixed: bool,
10239 near: NearInt,
10240 span: Span,
10241) -> Result<Array> {
10242 let want = one_whole(x, "the count dealt", near, span)?;
10243 let from = one_whole(y, "the range dealt from", near, span)?;
10244 if want < 0 || from < 0 {
10245 return Err(Error::domain("deal needs non-negative numbers", span));
10246 }
10247 if want > from {
10248 return Err(Error::domain(
10249 format!("cannot deal {want} distinct value(s) from {from}"),
10250 span,
10251 ));
10252 }
10253 if want == 0 {
10254 return Ok(Array::from_i64(Vec::new()));
10255 }
10256 let drawn = crate::rng::with(fixed, |g| g.deal(want as usize, from as u64));
10257 Ok(Array::from_i64(drawn.into_iter().map(|v| v + origin).collect()))
10258}
10259
10260fn one_whole(a: &Array, what: &str, near: NearInt, span: Span) -> Result<i64> {
10262 let v = a
10263 .to_i64_vec_near(near)
10264 .ok_or_else(|| Error::domain(format!("{what} must be a whole number"), span))?;
10265 match v[..] {
10266 [n] => Ok(n),
10267 _ => Err(Error::new(
10268 ErrorKind::Rank,
10269 format!("{what} must be one number"),
10270 Some(span),
10271 )),
10272 }
10273}
10274
10275fn nth_prime(n: i64, span: Span) -> Result<i64> {
10279 if n < 0 {
10280 return Err(Error::domain("the prime index must not be negative", span));
10281 }
10282 const LIMIT: i64 = 5_000_000;
10283 if n >= LIMIT {
10284 return Err(Error::domain(
10285 format!("prime index {n} is beyond the {LIMIT}th prime"),
10286 span,
10287 ));
10288 }
10289 let k = (n + 1) as f64;
10292 let bound = if n < 6 { 15.0 } else { k * (k.ln() + k.ln().ln()) };
10293 let bound = bound.ceil() as usize + 1;
10294 let mut sieve = vec![true; bound + 1];
10295 sieve[0] = false;
10296 if bound >= 1 {
10297 sieve[1] = false;
10298 }
10299 let mut p = 2usize;
10300 while p * p <= bound {
10301 if sieve[p] {
10302 let mut q = p * p;
10303 while q <= bound {
10304 sieve[q] = false;
10305 q += p;
10306 }
10307 }
10308 p += 1;
10309 }
10310 let mut seen = 0i64;
10311 for (v, &is_p) in sieve.iter().enumerate() {
10312 if is_p {
10313 if seen == n {
10314 return Ok(v as i64);
10315 }
10316 seen += 1;
10317 }
10318 }
10319 Err(Error::internal("the prime sieve was too small"))
10320}
10321
10322fn prime_factors(n: i64, span: Span) -> Result<Vec<i64>> {
10324 if n < 1 {
10325 return Err(Error::domain("prime factors need a positive integer", span));
10326 }
10327 let mut out = Vec::new();
10328 let mut m = n;
10329 let mut d = 2i64;
10330 while d.saturating_mul(d) <= m {
10331 while m % d == 0 {
10332 out.push(d);
10333 m /= d;
10334 }
10335 d += if d == 2 { 1 } else { 2 };
10336 }
10337 if m > 1 {
10338 out.push(m);
10339 }
10340 Ok(out)
10341}
10342
10343fn lstsq(a: &[f64], m: usize, n: usize, b: &[f64], k: usize) -> Option<Vec<f64>> {
10351 let mut r = a.to_vec();
10353 let mut c = b.to_vec();
10354 let at = |i: usize, j: usize, w: usize| i * w + j;
10355 let scale = a.iter().fold(0.0f64, |acc, v| acc.max(v.abs()));
10356 if scale == 0.0 {
10357 return None;
10358 }
10359 for j in 0..n {
10360 let norm = (j..m).map(|i| r[at(i, j, n)] * r[at(i, j, n)]).sum::<f64>().sqrt();
10362 if norm <= 1e-13 * scale {
10363 return None;
10364 }
10365 let alpha = if r[at(j, j, n)] > 0.0 { -norm } else { norm };
10366 let mut v = vec![0.0f64; m];
10367 for i in j..m {
10368 v[i] = r[at(i, j, n)];
10369 }
10370 v[j] -= alpha;
10371 let vnorm2: f64 = (j..m).map(|i| v[i] * v[i]).sum();
10372 if vnorm2 > 0.0 {
10373 for col in j..n {
10374 let dot: f64 = (j..m).map(|i| v[i] * r[at(i, col, n)]).sum();
10375 let f = 2.0 * dot / vnorm2;
10376 for i in j..m {
10377 r[at(i, col, n)] -= f * v[i];
10378 }
10379 }
10380 for col in 0..k {
10381 let dot: f64 = (j..m).map(|i| v[i] * c[at(i, col, k)]).sum();
10382 let f = 2.0 * dot / vnorm2;
10383 for i in j..m {
10384 c[at(i, col, k)] -= f * v[i];
10385 }
10386 }
10387 }
10388 }
10389 let mut x = vec![0.0f64; n * k];
10391 for col in 0..k {
10392 for i in (0..n).rev() {
10393 let mut acc = c[at(i, col, k)];
10394 for j in i + 1..n {
10395 acc -= r[at(i, j, n)] * x[at(j, col, k)];
10396 }
10397 let d = r[at(i, i, n)];
10398 if d.abs() <= 1e-13 * scale {
10399 return None;
10400 }
10401 x[at(i, col, k)] = acc / d;
10402 }
10403 }
10404 Some(x)
10405}
10406
10407fn as_matrix(a: &Array, span: Span) -> Result<(Vec<f64>, usize, usize)> {
10410 let v = a
10411 .to_f64_vec()
10412 .ok_or_else(|| Error::domain("matrix division needs numeric data", span))?;
10413 match a.rank() {
10414 0 => Ok((v, 1, 1)),
10415 1 => {
10416 let m = a.shape[0];
10417 Ok((v, m, 1))
10418 }
10419 2 => Ok((v, a.shape[0], a.shape[1])),
10420 _ => Err(Error::new(
10421 ErrorKind::Rank,
10422 "matrix division needs an argument of rank 2 or less",
10423 Some(span),
10424 )),
10425 }
10426}
10427
10428fn matrix_inverse(y: &Array, span: Span) -> Result<Array> {
10432 let (a, m, n) = as_matrix(y, span)?;
10433 if m < n {
10434 return Err(Error::new(
10435 ErrorKind::Length,
10436 format!("cannot invert a {m} by {n} matrix: it has more columns than rows"),
10437 Some(span),
10438 ));
10439 }
10440 let mut eye = vec![0.0f64; m * m];
10441 for i in 0..m {
10442 eye[i * m + i] = 1.0;
10443 }
10444 let x = lstsq(&a, m, n, &eye, m)
10445 .ok_or_else(|| Error::domain("the matrix is singular", span))?;
10446 let shape = if y.rank() == 2 { vec![n, m] } else { y.shape.clone() };
10449 Ok(Array::new(shape, Data::F64(x.into())))
10450}
10451
10452fn matrix_divide(x: &Array, y: &Array, span: Span) -> Result<Array> {
10454 let (a, m, n) = as_matrix(y, span)?;
10455 let (b, bm, k) = as_matrix(x, span)?;
10456 if bm != m {
10457 return Err(Error::new(
10458 ErrorKind::Length,
10459 format!("the system has {m} rows but the right-hand side has {bm}"),
10460 Some(span),
10461 ));
10462 }
10463 if m < n {
10464 return Err(Error::new(
10465 ErrorKind::Length,
10466 format!("the {m} by {n} system is underdetermined"),
10467 Some(span),
10468 ));
10469 }
10470 let sol = lstsq(&a, m, n, &b, k)
10471 .ok_or_else(|| Error::domain("the system is singular", span))?;
10472 let shape = if x.rank() == 2 { vec![n, k] } else { vec![n] };
10475 Ok(Array::new(shape, Data::F64(sol.into())))
10476}
10477
10478fn squad(x: &Array, y: &Array, origin: i64, leading: bool, near: NearInt, span: Span) -> Result<Array> {
10482 if x.rank() > 1 {
10483 return Err(Error::new(
10484 ErrorKind::Rank,
10485 "the index of ⌷ must be a scalar or a vector",
10486 Some(span),
10487 ));
10488 }
10489 let items: Vec<Array> = if x.rank() == 0 { vec![x.clone()] } else { x.cells(1) };
10494 let named = items.len();
10495 if named > y.rank() || (!leading && named != y.rank()) {
10496 return Err(Error::new(
10497 ErrorKind::Rank,
10498 format!("{} index(es) for an argument of rank {}", named, y.rank()),
10499 Some(span),
10500 ));
10501 }
10502 let mut specs = Vec::with_capacity(items.len());
10503 let mut shape = Vec::new();
10504 for (k, item) in items.iter().enumerate() {
10505 let spec = match item.as_boxes() {
10506 Some(bs) if item.rank() == 0 => bs[0].clone(),
10507 _ => item.clone(),
10508 };
10509 let idx = spec
10510 .to_i64_vec_near(near)
10511 .ok_or_else(|| Error::domain("index must be an integer", span))?;
10512 for &i in &idx {
10513 let j = i - origin;
10514 if j < 0 || j as usize >= y.shape[k] {
10515 return Err(Error::domain(
10516 format!("index {i} is out of range on axis {k}"),
10517 span,
10518 ));
10519 }
10520 }
10521 shape.extend_from_slice(&spec.shape);
10522 specs.push((spec.shape.clone(), idx));
10523 }
10524 for k in named..y.rank() {
10527 let n = y.shape[k];
10528 shape.push(n);
10529 specs.push((vec![n], (0..n as i64).map(|i| i + origin).collect()));
10530 }
10531 let y = y.to_row_major();
10532 let st = strides(&y.shape);
10533 let total: usize = shape.iter().product();
10534 let mut data = Data::empty(y.dtype());
10535 let mut coord = vec![0usize; shape.len()];
10536 for _ in 0..total {
10537 let mut at = 0usize;
10538 let mut used = 0usize;
10539 for (k, (sshape, idx)) in specs.iter().enumerate() {
10540 let sst = strides(sshape);
10541 let pick: usize = (0..sshape.len()).map(|a| coord[used + a] * sst[a]).sum();
10542 used += sshape.len();
10543 at += (idx[pick] - origin) as usize * st[k];
10544 }
10545 push_elem(&mut data, y.row_major_data(), at);
10546 odometer(&mut coord, &shape);
10547 }
10548 Ok(Array::new(shape, data))
10549}
10550
10551fn select_axis(
10558 x: &Array,
10559 y: &Array,
10560 axis: usize,
10561 rank: usize,
10562 origin: i64,
10563 near: NearInt,
10564 span: Span,
10565) -> Result<Array> {
10566 if rank != 0 && y.rank() != rank {
10567 return Err(Error::new(
10568 ErrorKind::Rank,
10569 format!("{rank} index slot(s) for an argument of rank {}", y.rank()),
10570 Some(span),
10571 ));
10572 }
10573 if axis >= y.rank() {
10574 return Err(Error::new(
10575 ErrorKind::Rank,
10576 format!("axis {axis} does not exist on an argument of rank {}", y.rank()),
10577 Some(span),
10578 ));
10579 }
10580 let idx = x
10581 .to_i64_vec_near(near)
10582 .ok_or_else(|| Error::domain("index must be an integer", span))?;
10583 let len = y.shape[axis];
10584 let mut picks = Vec::with_capacity(idx.len());
10585 for &i in &idx {
10586 let j = i - origin;
10587 if j < 0 || j as usize >= len {
10588 return Err(Error::domain(
10589 format!("index {i} is out of range: axis {axis} has {len} items"),
10590 span,
10591 ));
10592 }
10593 picks.push(j as usize);
10594 }
10595 let mut shape = Vec::with_capacity(y.rank() + x.rank());
10596 shape.extend_from_slice(&y.shape[..axis]);
10597 shape.extend_from_slice(&x.shape);
10598 shape.extend_from_slice(&y.shape[axis + 1..]);
10599 let outer: usize = y.shape[..axis].iter().product();
10600 let inner: usize = y.shape[axis + 1..].iter().product();
10601 let mut data = Data::empty(y.dtype());
10602 for o in 0..outer {
10603 for &p in &picks {
10604 let base = (o * len + p) * inner;
10605 for e in 0..inner {
10606 push_elem(&mut data, &y.data, base + e);
10607 }
10608 }
10609 }
10610 Ok(Array::new(shape, data))
10611}
10612
10613fn amend(m: &Array, x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
10617 if y.rank() == 0 {
10618 return Err(Error::new(ErrorKind::Rank, "cannot amend a scalar", Some(span)));
10619 }
10620 if let Some(spec) = m.as_boxes().and_then(<[Array]>::first) {
10622 let spec = index_spec(spec, y, near, span)?;
10623 return amend_spec(&spec, x, y, span);
10624 }
10625 let idx = m
10626 .to_i64_vec_near(near)
10627 .ok_or_else(|| Error::domain("amend indices must be integers", span))?;
10628 let items = y.items() as i64;
10629 let mut at = Vec::with_capacity(idx.len());
10630 for &i in &idx {
10631 let k = if i < 0 { i + items } else { i };
10632 if k < 0 || k >= items {
10633 return Err(Error::domain(
10634 format!("index {i} is out of range: the argument has {items} items"),
10635 span,
10636 ));
10637 }
10638 at.push(k as usize);
10639 }
10640 let cell = y.item_size();
10641 let per_index = if x.count() == cell {
10642 false
10643 } else if x.count() == cell * at.len() {
10644 true
10645 } else {
10646 return Err(Error::new(
10647 ErrorKind::Length,
10648 format!(
10649 "cannot amend {} item(s) of {} element(s) each with {} element(s)",
10650 at.len(),
10651 cell,
10652 x.count()
10653 ),
10654 Some(span),
10655 ));
10656 };
10657 let Some(t) = DType::promote(x.dtype(), y.dtype()) else {
10660 return Err(Error::new(
10661 ErrorKind::Type,
10662 "the replacement and the argument hold different kinds of value",
10663 Some(span),
10664 ));
10665 };
10666 let (Some(src), Some(base)) = (x.data.cast(t), y.data.cast(t)) else {
10667 return Err(Error::new(
10668 ErrorKind::Type,
10669 "the replacement and the argument hold different kinds of value",
10670 Some(span),
10671 ));
10672 };
10673 let mut data = Data::empty(t);
10675 let mut plan: Vec<Option<usize>> = vec![None; y.items()];
10676 for (n, &k) in at.iter().enumerate() {
10677 plan[k] = Some(if per_index { n } else { 0 });
10678 }
10679 for (i, slot) in plan.iter().enumerate() {
10680 match slot {
10681 Some(n) => {
10682 for e in 0..cell {
10683 push_elem(&mut data, &src, n * cell + e);
10684 }
10685 }
10686 None => {
10687 for e in 0..cell {
10688 push_elem(&mut data, &base, i * cell + e);
10689 }
10690 }
10691 }
10692 }
10693 Ok(Array::new(y.shape.clone(), data))
10694}
10695
10696fn fetch(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
10701 let steps: Vec<Array> = match x.as_boxes() {
10702 Some(bs) => bs.to_vec(),
10703 None => vec![x.clone()],
10704 };
10705 let mut cur = y.clone();
10706 for step in steps {
10707 let idx = if step.count() == 0 {
10710 Vec::new()
10711 } else {
10712 step.to_i64_vec_near(near)
10713 .ok_or_else(|| Error::domain("a fetch path holds integers", span))?
10714 };
10715 let base =
10717 if cur.rank() == 0 { Array::new(vec![1], cur.data.clone()) } else { cur.clone() };
10718 if idx.len() > base.rank() {
10719 return Err(Error::new(
10720 ErrorKind::Length,
10721 format!(
10722 "a path step of {} index(es) into a value of rank {}",
10723 idx.len(),
10724 cur.rank()
10725 ),
10726 Some(span),
10727 ));
10728 }
10729 let at = cell_index(&base, &idx, span)?;
10730 cur = open_cell(&base.cell_at(idx.len(), at));
10731 }
10732 Ok(cur)
10733}
10734
10735fn cell_index(y: &Array, idx: &[i64], span: Span) -> Result<usize> {
10737 let mut at = 0usize;
10738 for (k, &i) in idx.iter().enumerate() {
10739 let len = y.shape[k] as i64;
10740 let j = if i < 0 { i + len } else { i };
10741 if j < 0 || j >= len {
10742 return Err(Error::domain(
10743 format!("index {i} is out of range: axis {k} has {len} items"),
10744 span,
10745 ));
10746 }
10747 at = at * y.shape[k] + j as usize;
10748 }
10749 Ok(at)
10750}
10751
10752fn partition_counts(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
10768 if y.rank() == 0 {
10769 return Err(Error::new(
10770 ErrorKind::Rank,
10771 "partitioned enclose needs an array to partition",
10772 Some(span),
10773 ));
10774 }
10775 let counts = x
10776 .to_i64_vec_near(near)
10777 .ok_or_else(|| Error::domain("partition counts must be integers", span))?;
10778 if counts.iter().any(|&c| c < 0) {
10779 return Err(Error::domain("partition counts must not be negative", span));
10780 }
10781 let last = y.shape[y.rank() - 1];
10782 if counts.len() > last {
10786 return Err(Error::new(
10787 ErrorKind::Length,
10788 format!("{} count(s) for {} item(s)", counts.len(), last),
10789 Some(span),
10790 ));
10791 }
10792 let at = |i: usize| -> i64 {
10793 if x.rank() == 0 {
10794 counts.first().copied().unwrap_or(0)
10795 } else {
10796 counts.get(i).copied().unwrap_or(0)
10797 }
10798 };
10799 let mut groups: Vec<(usize, usize)> = Vec::new();
10802 for i in 0..last {
10803 for _ in 0..at(i) {
10804 groups.push((i, 0));
10805 }
10806 if let Some(g) = groups.last_mut() {
10807 g.1 += 1;
10808 }
10809 }
10810 let y = y.to_row_major();
10811 let rows = if last == 0 { 0 } else { y.count() / last };
10812 let lead = &y.shape[..y.rank() - 1];
10813 let parts: Vec<Array> = groups
10814 .iter()
10815 .map(|&(start, len)| {
10816 let mut d = Data::empty(y.dtype());
10817 for r in 0..rows {
10818 for c in start..start + len {
10819 push_elem(&mut d, y.row_major_data(), r * last + c);
10820 }
10821 }
10822 let mut shape = lead.to_vec();
10823 shape.push(len);
10824 Array::new(shape, d)
10825 })
10826 .collect();
10827 Ok(Array::new(vec![parts.len()], Data::Box(parts.into())))
10828}
10829
10830fn partition_enclose(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
10831 if y.rank() > 1 {
10834 let last = y.shape[y.rank() - 1];
10835 let rows = y.count() / last.max(1);
10836 let mut cells: Vec<Array> = Vec::new();
10837 let mut width = None;
10838 for r in 0..rows {
10839 let row = Array::new(vec![last], y.data.slice(r * last, (r + 1) * last));
10840 let parts = partition_enclose(x, &row, near, span)?;
10841 let n = parts.count();
10842 if *width.get_or_insert(n) != n {
10843 return Err(Error::internal("partitions of unequal count"));
10844 }
10845 match parts.data {
10846 Data::Box(v) => cells.extend(v.as_slice().iter().cloned()),
10847 _ => return Err(Error::internal("a partition is boxed")),
10848 }
10849 }
10850 let mut shape = y.shape[..y.rank() - 1].to_vec();
10851 shape.push(width.unwrap_or(0));
10852 return Ok(Array::new(shape, Data::Box(cells.into())));
10853 }
10854 if y.rank() == 0 {
10855 return Err(Error::new(
10856 ErrorKind::Rank,
10857 "partitioned enclose needs an array to partition",
10858 Some(span),
10859 ));
10860 }
10861 if x.count() == 0 && y.count() == 0 {
10866 return Ok(Array::new(vec![0], Data::Box(Vec::new().into())));
10867 }
10868 let mut flags = x
10869 .to_i64_vec_near(near)
10870 .ok_or_else(|| Error::domain("partition flags must be integers", span))?;
10871 if flags.iter().any(|&f| f < 0) {
10872 return Err(Error::domain("partition flags must not be negative", span));
10873 }
10874 if flags.len() == 1 && y.shape[0] != 1 {
10879 flags = vec![flags[0]; y.shape[0]];
10880 }
10881 if flags.len() != y.shape[0] {
10882 return Err(Error::new(
10883 ErrorKind::Length,
10884 format!("{} flag(s) for {} item(s)", flags.len(), y.shape[0]),
10885 Some(span),
10886 ));
10887 }
10888 let mut parts: Vec<Array> = Vec::new();
10889 let mut cur: Option<Data> = None;
10890 let mut prev = 0i64;
10891 for (i, &f) in flags.iter().enumerate() {
10892 if f > prev {
10893 if let Some(d) = cur.take() {
10894 parts.push(Array::new(vec![d.len()], d));
10895 }
10896 cur = Some(Data::empty(y.dtype()));
10897 }
10898 prev = f;
10899 if f == 0 {
10900 continue;
10901 }
10902 if let Some(d) = cur.as_mut() {
10903 push_elem(d, &y.data, i);
10904 }
10905 }
10906 if let Some(d) = cur.take() {
10907 parts.push(Array::new(vec![d.len()], d));
10908 }
10909 Ok(Array::new(vec![parts.len()], Data::Box(parts.into())))
10910}
10911
10912fn key(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
10915 let keys = if x.rank() == 0 { Array::new(vec![1], x.data.clone()) } else { x.clone() };
10916 let n = keys.items();
10917 if n != y.items() && !(y.rank() == 0 && n == 1) {
10918 return Err(Error::new(
10919 ErrorKind::Length,
10920 format!("{n} key(s) for {} item(s)", y.items()),
10921 Some(span),
10922 ));
10923 }
10924 let groups = group_positions(&keys, ctx.cfg.tol);
10925 let items = if y.rank() == 0 { Array::new(vec![1], y.data.clone()) } else { y.clone() };
10926 let mut cells = Vec::with_capacity(groups.len());
10927 for (_, at) in &groups {
10928 cells.push(u.monad(&select_items(&items, at), ctx, span)?);
10929 }
10930 assemble(&[groups.len()], cells, span)
10931}
10932
10933fn oblique(u: &Verb, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
10936 if y.rank() < 2 {
10937 let items = if y.rank() == 0 { Array::new(vec![1], y.data.clone()) } else { y.clone() };
10938 let n = items.items();
10939 let mut cells = Vec::with_capacity(n);
10940 for i in 0..n {
10941 cells.push(u.monad(&select_items(&items, &[i]), ctx, span)?);
10942 }
10943 return assemble(&[n], cells, span);
10944 }
10945 if y.rank() > 2 {
10946 return Err(Error::not_yet("oblique (u/.) on a rank-3 or higher argument", span));
10947 }
10948 let (rows, cols) = (y.shape[0], y.shape[1]);
10949 let mut cells = Vec::with_capacity(rows + cols - 1);
10950 for d in 0..rows + cols - 1 {
10951 let mut data = Data::empty(y.dtype());
10952 let mut len = 0usize;
10953 for i in 0..rows {
10954 if d >= i && d - i < cols {
10955 push_elem(&mut data, &y.data, i * cols + (d - i));
10956 len += 1;
10957 }
10958 }
10959 cells.push(u.monad(&Array::new(vec![len], data), ctx, span)?);
10960 }
10961 assemble(&[rows + cols - 1], cells, span)
10962}
10963
10964fn cut_ranges(frets: &[bool], mode: i64) -> Vec<(usize, usize)> {
10972 let n = frets.len();
10973 let mut out = Vec::new();
10974 if mode.abs() == 1 {
10975 let mut start: Option<usize> = None;
10976 for (i, &fret) in frets.iter().enumerate() {
10977 if fret {
10978 if let Some(s) = start {
10979 out.push((s, i));
10980 }
10981 start = Some(i);
10982 }
10983 }
10984 if let Some(s) = start {
10985 out.push((s, n));
10986 }
10987 if mode < 0 {
10988 return out.into_iter().map(|(s, e)| (s + 1, e)).collect();
10989 }
10990 } else {
10991 let mut start = 0usize;
10992 for (i, &fret) in frets.iter().enumerate() {
10993 if fret {
10994 out.push((start, i + 1));
10995 start = i + 1;
10996 }
10997 }
10998 if mode < 0 {
10999 return out.into_iter().map(|(s, e)| (s, e - 1)).collect();
11000 }
11001 }
11002 out
11003}
11004
11005fn cut(
11007 u: &Verb,
11008 x: Option<&Array>,
11009 y: &Array,
11010 mode: i64,
11011 ctx: &mut Ctx<'_>,
11012 span: Span,
11013) -> Result<Array> {
11014 if mode == 0 {
11015 let Some(x) = x else {
11016 return u.monad(&reverse_all_axes(y), ctx, span);
11017 };
11018 let (origin, size) = rectangle(x, span)?;
11019 let origin = origin.unwrap_or_else(|| vec![0; size.len()]);
11020 return u.monad(&subarray(y, &origin, &size, span)?, ctx, span);
11021 }
11022 if mode.abs() == 3 {
11023 let Some(x) = x else {
11024 return Err(Error::not_yet("monadic tessellation (u;.3 y)", span));
11025 };
11026 return tessellate(u, x, y, mode < 0, ctx, span);
11027 }
11028 if !matches!(mode, 1 | -1 | 2 | -2) {
11029 return Err(Error::not_yet(format!("cut (u;.{mode})"), span));
11030 }
11031 let items = if y.rank() == 0 { Array::new(vec![1], y.data.clone()) } else { y.clone() };
11032 let n = items.items();
11033 let tol = ctx.cfg.tol;
11034 let frets: Vec<bool> = match x {
11035 Some(x) => {
11036 let flags = x.to_i64_vec().ok_or_else(|| {
11037 if x.dtype() == DType::Box {
11038 Error::not_yet("per-axis cut frets (a boxed left argument)", span)
11042 } else {
11043 Error::domain("cut frets must be integers", span)
11044 }
11045 })?;
11046 if let Some(&bad) = flags.iter().find(|&&f| f != 0 && f != 1) {
11049 return Err(Error::domain(format!("{bad} is not a fret: a fret is 0 or 1"), span));
11050 }
11051 if x.rank() == 0 {
11054 vec![flags[0] != 0; n]
11055 } else if flags.is_empty() {
11056 Vec::new()
11058 } else {
11059 if flags.len() != n {
11060 return Err(Error::new(
11061 ErrorKind::Length,
11062 format!("{} fret(s) for {n} item(s)", flags.len()),
11063 Some(span),
11064 ));
11065 }
11066 flags.iter().map(|&f| f != 0).collect()
11067 }
11068 }
11069 None => {
11070 if n == 0 {
11072 Vec::new()
11073 } else {
11074 let at = if mode.abs() == 1 { 0 } else { n - 1 };
11075 let mark = items.item(at);
11076 (0..n).map(|i| arrays_match(&items.item(i), &mark, tol)).collect()
11077 }
11078 }
11079 };
11080 let empty_frets = matches!(x, Some(x) if x.rank() == 1 && x.count() == 0);
11087 let no_axis = matches!(x, Some(x) if x.rank() > 1 && x.count() == 0);
11088 let ranges = if empty_frets && n > 0 {
11089 vec![(0, n)]
11090 } else if empty_frets || no_axis {
11091 Vec::new()
11092 } else {
11093 cut_ranges(&frets, mode)
11094 };
11095 if ranges.is_empty() {
11099 let cell = u.is_pure().then(|| section(&items, 0, 0));
11100 return Ok(empty_frame(&[0], items.dtype(), cell, ctx, |cell, c| u.monad(cell, c, span)));
11101 }
11102 let mut cells = Vec::with_capacity(ranges.len());
11103 for (s, e) in &ranges {
11104 cells.push(u.monad(§ion(&items, *s, *e), ctx, span)?);
11105 }
11106 assemble(&[ranges.len()], cells, span)
11107}
11108
11109fn rectangle(x: &Array, span: Span) -> Result<(Option<Vec<i64>>, Vec<i64>)> {
11112 let values = x
11113 .to_i64_vec()
11114 .ok_or_else(|| Error::domain("a cut rectangle is whole numbers", span))?;
11115 match x.rank() {
11116 0 | 1 => Ok((None, values)),
11117 2 if x.shape[0] == 2 => {
11118 let n = x.shape[1];
11119 Ok((Some(values[..n].to_vec()), values[n..].to_vec()))
11120 }
11121 _ => Err(Error::new(
11122 ErrorKind::Rank,
11123 "a cut rectangle is a vector of sizes, or two rows of origins and sizes",
11124 Some(span),
11125 )),
11126 }
11127}
11128
11129fn subarray(y: &Array, origin: &[i64], size: &[i64], span: Span) -> Result<Array> {
11133 if origin.len() > y.rank() {
11134 return Err(Error::new(
11135 ErrorKind::Rank,
11136 format!("a cut of {} axis/axes into a rank-{} value", origin.len(), y.rank()),
11137 Some(span),
11138 ));
11139 }
11140 let r = y.rank();
11141 let st = strides(&y.shape);
11142 let mut shape = y.shape.clone();
11143 let mut start = vec![0i64; r];
11144 let mut step = vec![1i64; r];
11145 for k in 0..origin.len() {
11146 let want = u128::from(size[k].unsigned_abs());
11150 let from = if origin[k] < 0 { origin[k] + y.shape[k] as i64 } else { origin[k] };
11151 if from < 0 || u128::from(from.unsigned_abs()) + want > y.shape[k] as u128 {
11152 return Err(Error::domain(
11153 format!("a cut of {want} from {from} leaves axis {k} of {}", y.shape[k]),
11154 span,
11155 ));
11156 }
11157 let len = want as usize;
11158 shape[k] = len;
11159 if size[k] < 0 {
11160 start[k] = from + len as i64 - 1;
11161 step[k] = -1;
11162 } else {
11163 start[k] = from;
11164 }
11165 }
11166 Ok(gather(y, &shape, &start, &step, &st))
11167}
11168
11169fn gather(y: &Array, shape: &[usize], start: &[i64], step: &[i64], st: &[usize]) -> Array {
11171 let n: usize = shape.iter().product();
11172 let mut data = Data::empty(y.dtype());
11173 let mut coord = vec![0usize; shape.len()];
11174 for _ in 0..n {
11175 let idx: usize = (0..shape.len())
11176 .map(|k| (start[k] + step[k] * coord[k] as i64) as usize * st[k])
11177 .sum();
11178 push_elem(&mut data, &y.data, idx);
11179 odometer(&mut coord, shape);
11180 }
11181 Array::new(shape.to_vec(), data)
11182}
11183
11184fn tessellate(
11188 u: &Verb,
11189 x: &Array,
11190 y: &Array,
11191 complete: bool,
11192 ctx: &mut Ctx<'_>,
11193 span: Span,
11194) -> Result<Array> {
11195 let (movement, size) = rectangle(x, span)?;
11197 if size.iter().any(|&s| s < 0) && movement.is_none() {
11202 return Err(Error::not_yet(
11203 "a negative block size without a movement row (x u;.3 y)",
11204 span,
11205 ));
11206 }
11207 let movement = movement.unwrap_or_else(|| vec![1; size.len()]);
11208 if size.len() > y.rank() {
11209 return Err(Error::new(
11210 ErrorKind::Rank,
11211 format!("a tessellation of {} axis/axes into a rank-{} value", size.len(), y.rank()),
11212 Some(span),
11213 ));
11214 }
11215 let mut frame = Vec::with_capacity(size.len());
11219 for k in 0..size.len() {
11220 let (len, step) = (i128::from(y.shape[k] as i64), i128::from(movement[k]));
11221 let block = i128::from(size[k]).abs();
11222 if step <= 0 {
11223 return Err(Error::domain("a tessellation moves by a positive step", span));
11224 }
11225 let count = if complete {
11226 if len < block { 0 } else { (len - block) / step + 1 }
11227 } else {
11228 (len + step - 1) / step
11229 };
11230 frame.push(count as usize);
11231 }
11232 let total: usize = frame.iter().product();
11233 let mut cells = Vec::with_capacity(total);
11234 let mut coord = vec![0usize; frame.len()];
11235 for _ in 0..total {
11236 let origin: Vec<i64> = (0..frame.len()).map(|k| coord[k] as i64 * movement[k]).collect();
11237 let block: Vec<i64> = (0..frame.len())
11240 .map(|k| {
11241 let left = i128::from(y.shape[k] as i64 - origin[k]);
11242 let len = i128::from(size[k]).abs().min(left) as i64;
11243 if size[k] < 0 { -len } else { len }
11244 })
11245 .collect();
11246 cells.push(u.monad(&subarray(y, &origin, &block, span)?, ctx, span)?);
11247 odometer(&mut coord, &frame);
11248 }
11249 assemble(&frame, cells, span)
11250}
11251
11252fn reverse_all_axes(y: &Array) -> Array {
11254 if y.rank() == 0 {
11255 return y.clone();
11256 }
11257 let st = strides(&y.shape);
11258 let n = y.count();
11259 let r = y.rank();
11260 let mut data = Data::empty(y.dtype());
11261 let mut coord = vec![0usize; r];
11262 for _ in 0..n {
11263 let idx: usize = (0..r).map(|k| (y.shape[k] - 1 - coord[k]) * st[k]).sum();
11264 push_elem(&mut data, &y.data, idx);
11265 odometer(&mut coord, &y.shape);
11266 }
11267 Array::new(y.shape.clone(), data)
11268}
11269
11270fn axis_to_front(y: &Array, k: usize) -> Array {
11274 if k == 0 || y.rank() < 2 {
11275 return y.clone();
11276 }
11277 let r = y.rank();
11278 let src: Vec<usize> = std::iter::once(k).chain((0..r).filter(|&a| a != k)).collect();
11279 permute_axes(y, &src)
11280}
11281
11282fn front_to_axis(y: &Array, k: usize) -> Array {
11284 if k == 0 || y.rank() < 2 {
11285 return y.clone();
11286 }
11287 let r = y.rank();
11288 let mut src = Vec::with_capacity(r);
11291 for a in 0..r {
11292 src.push(match a.cmp(&k) {
11293 std::cmp::Ordering::Less => a + 1,
11294 std::cmp::Ordering::Equal => 0,
11295 std::cmp::Ordering::Greater => a,
11296 });
11297 }
11298 permute_axes(y, &src)
11299}
11300
11301fn transpose_to(y: &Array, dest: &[usize], span: Span) -> Result<Array> {
11306 let rank_out = dest.iter().copied().max().map_or(0, |m| m + 1);
11307 let mut out_shape = vec![usize::MAX; rank_out];
11308 for (a, &d) in dest.iter().enumerate() {
11309 out_shape[d] = out_shape[d].min(y.shape[a]);
11310 }
11311 if out_shape.contains(&usize::MAX) {
11312 return Err(Error::new(
11313 ErrorKind::Domain,
11314 "a transpose must name every axis of the result",
11315 Some(span),
11316 ));
11317 }
11318 let y = y.to_row_major();
11319 let st = strides(&y.shape);
11320 let n: usize = out_shape.iter().product();
11321 let mut data = Data::empty(y.dtype());
11322 let mut coord = vec![0usize; rank_out];
11323 for _ in 0..n {
11324 let idx: usize = dest.iter().enumerate().map(|(a, &d)| coord[d] * st[a]).sum();
11325 push_elem(&mut data, &y.data, idx);
11326 odometer(&mut coord, &out_shape);
11327 }
11328 Ok(Array::new(out_shape, data))
11329}
11330
11331fn transpose_apl(x: &Array, y: &Array, io: i64, near: NearInt, span: Span) -> Result<Array> {
11334 let axes = x
11335 .to_i64_vec_near(near)
11336 .ok_or_else(|| Error::domain("a transpose is given whole numbers", span))?;
11337 if axes.len() != y.rank() {
11338 return Err(Error::new(
11339 ErrorKind::Length,
11340 format!("{} axes for a rank-{} value", axes.len(), y.rank()),
11341 Some(span),
11342 ));
11343 }
11344 let mut dest = Vec::with_capacity(axes.len());
11345 for a in axes {
11346 let d = a - io;
11347 if d < 0 || d as usize >= y.rank() {
11348 return Err(Error::new(
11349 ErrorKind::Domain,
11350 format!("axis {a} is outside a rank-{} value", y.rank()),
11351 Some(span),
11352 ));
11353 }
11354 dest.push(d as usize);
11355 }
11356 transpose_to(y, &dest, span)
11357}
11358
11359fn transpose_j(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
11363 let groups: Vec<Vec<i64>> = match x.as_boxes() {
11364 Some(bs) => bs
11365 .iter()
11366 .map(|b| {
11367 b.to_i64_vec_near(near).ok_or_else(|| {
11368 Error::domain("a transpose is given whole numbers", span)
11369 })
11370 })
11371 .collect::<Result<Vec<_>>>()?,
11372 None => x
11373 .to_i64_vec_near(near)
11374 .ok_or_else(|| Error::domain("a transpose is given whole numbers", span))?
11375 .into_iter()
11376 .map(|a| vec![a])
11377 .collect(),
11378 };
11379 let r = y.rank();
11380 let mut group_of = vec![None; r];
11383 for (g, axes) in groups.iter().enumerate() {
11384 for &a in axes {
11385 let k = if a < 0 { a + r as i64 } else { a };
11386 if k < 0 || k as usize >= r {
11387 return Err(Error::new(
11388 ErrorKind::Domain,
11389 format!("axis {a} is outside a rank-{r} value"),
11390 Some(span),
11391 ));
11392 }
11393 if group_of[k as usize].is_some() {
11394 return Err(Error::new(
11395 ErrorKind::Domain,
11396 format!("axis {a} is named twice in a transpose"),
11397 Some(span),
11398 ));
11399 }
11400 group_of[k as usize] = Some(g);
11401 }
11402 }
11403 let leading = group_of.iter().filter(|g| g.is_none()).count();
11404 let mut dest = vec![0usize; r];
11405 let mut next = 0;
11406 for a in 0..r {
11407 match group_of[a] {
11408 None => {
11409 dest[a] = next;
11410 next += 1;
11411 }
11412 Some(g) => dest[a] = leading + g,
11413 }
11414 }
11415 transpose_to(y, &dest, span)
11416}
11417
11418fn permute_axes(y: &Array, src: &[usize]) -> Array {
11420 let st = strides(&y.shape);
11421 let out_shape: Vec<usize> = src.iter().map(|&a| y.shape[a]).collect();
11422 let n = y.count();
11423 let mut data = Data::empty(y.dtype());
11424 let mut coord = vec![0usize; src.len()];
11425 for _ in 0..n {
11426 let idx: usize = (0..src.len()).map(|a| coord[a] * st[src[a]]).sum();
11427 push_elem(&mut data, &y.data, idx);
11428 odometer(&mut coord, &out_shape);
11429 }
11430 Array::new(out_shape, data)
11431}
11432
11433struct Spec {
11437 width: usize,
11439 cells: Vec<Vec<usize>>,
11441 shape: Vec<usize>,
11444}
11445
11446fn axis_position(v: i64, len: usize, span: Span) -> Result<usize> {
11449 let p = if v < 0 { v + len as i64 } else { v };
11450 if p < 0 || p >= len as i64 {
11451 return Err(Error::domain(
11452 format!("index {v} is out of range: the axis has {len} element(s)"),
11453 span,
11454 ));
11455 }
11456 Ok(p as usize)
11457}
11458
11459fn index_spec(content: &Array, y: &Array, near: NearInt, span: Span) -> Result<Spec> {
11469 let too_deep = |n: usize| {
11470 Error::new(
11471 ErrorKind::Rank,
11472 format!("an index specification of {n} axis/axes into a rank-{} value", y.rank()),
11473 Some(span),
11474 )
11475 };
11476 if let Some(items) = content.as_boxes() {
11477 if items.len() > y.rank() {
11478 return Err(too_deep(items.len()));
11479 }
11480 let mut per_axis: Vec<Vec<usize>> = Vec::with_capacity(items.len());
11481 let mut shape: Vec<usize> = Vec::new();
11482 for (k, c) in items.iter().enumerate() {
11483 let len = y.shape[k];
11484 if c.as_boxes().is_some() {
11485 let inner = open_cell(c);
11486 let excluded = inner.to_i64_vec_near(near).ok_or_else(|| {
11487 Error::domain("an index complement holds integers", span)
11488 })?;
11489 let mut dropped = vec![false; len];
11490 for v in excluded {
11491 dropped[axis_position(v, len, span)?] = true;
11492 }
11493 let kept: Vec<usize> = (0..len).filter(|i| !dropped[*i]).collect();
11494 shape.push(kept.len());
11495 per_axis.push(kept);
11496 } else {
11497 let idx = c
11498 .to_i64_vec_near(near)
11499 .ok_or_else(|| Error::domain("an index holds integers", span))?;
11500 let mut positions = Vec::with_capacity(idx.len());
11501 for v in idx {
11502 positions.push(axis_position(v, len, span)?);
11503 }
11504 shape.extend_from_slice(&c.shape);
11505 per_axis.push(positions);
11506 }
11507 }
11508 let mut cells: Vec<Vec<usize>> = vec![Vec::new()];
11510 for positions in &per_axis {
11511 let mut next = Vec::with_capacity(cells.len() * positions.len());
11512 for prefix in &cells {
11513 for &p in positions {
11514 let mut cell = prefix.clone();
11515 cell.push(p);
11516 next.push(cell);
11517 }
11518 }
11519 cells = next;
11520 }
11521 return Ok(Spec { width: per_axis.len(), cells, shape });
11522 }
11523 let idx = content
11524 .to_i64_vec_near(near)
11525 .ok_or_else(|| Error::domain("an index specification holds integers", span))?;
11526 let rank = content.rank();
11527 let width = if rank == 0 { 1 } else { content.shape[rank - 1] };
11528 if width > y.rank() {
11529 return Err(too_deep(width));
11530 }
11531 let shape: Vec<usize> = if rank == 0 { Vec::new() } else { content.shape[..rank - 1].to_vec() };
11532 let count: usize = shape.iter().product();
11533 let mut cells: Vec<Vec<usize>> = Vec::new();
11534 if width == 0 {
11535 cells.resize(count, Vec::new());
11536 } else {
11537 for chunk in idx.chunks(width) {
11538 let mut cell = Vec::with_capacity(width);
11539 for (k, &v) in chunk.iter().enumerate() {
11540 cell.push(axis_position(v, y.shape[k], span)?);
11541 }
11542 cells.push(cell);
11543 }
11544 }
11545 Ok(Spec { width, cells, shape })
11546}
11547
11548fn spec_offset(st: &[usize], cell: &[usize]) -> usize {
11550 cell.iter().enumerate().map(|(k, &p)| p * st[k]).sum()
11551}
11552
11553fn select_spec(spec: &Spec, y: &Array) -> Array {
11555 let st = strides(&y.shape);
11556 let size: usize = y.shape[spec.width..].iter().product();
11557 let mut data = Data::empty(y.dtype());
11558 for cell in &spec.cells {
11559 let base = spec_offset(&st, cell);
11560 for e in 0..size {
11561 push_elem(&mut data, &y.data, base + e);
11562 }
11563 }
11564 let mut shape = spec.shape.clone();
11565 shape.extend_from_slice(&y.shape[spec.width..]);
11566 Array::new(shape, data)
11567}
11568
11569fn amend_spec(spec: &Spec, x: &Array, y: &Array, span: Span) -> Result<Array> {
11572 let size: usize = y.shape[spec.width..].iter().product();
11573 let per_cell = if x.count() == size {
11574 false
11575 } else if x.count() == size * spec.cells.len() {
11576 true
11577 } else {
11578 return Err(Error::new(
11579 ErrorKind::Length,
11580 format!(
11581 "cannot amend {} cell(s) of {size} element(s) each with {} element(s)",
11582 spec.cells.len(),
11583 x.count()
11584 ),
11585 Some(span),
11586 ));
11587 };
11588 let mismatch = || {
11589 Error::new(
11590 ErrorKind::Type,
11591 "the replacement and the argument hold different kinds of value",
11592 Some(span),
11593 )
11594 };
11595 let t = DType::promote(x.dtype(), y.dtype()).ok_or_else(mismatch)?;
11596 let (Some(src), Some(base)) = (x.data.cast(t), y.data.cast(t)) else {
11597 return Err(mismatch());
11598 };
11599 let st = strides(&y.shape);
11600 let mut plan: Vec<Option<usize>> = vec![None; y.count()];
11601 for (n, cell) in spec.cells.iter().enumerate() {
11602 let at = spec_offset(&st, cell);
11603 for e in 0..size {
11604 plan[at + e] = Some(if per_cell { n * size + e } else { e });
11605 }
11606 }
11607 let mut data = Data::empty(t);
11608 for (i, slot) in plan.iter().enumerate() {
11609 match slot {
11610 Some(n) => push_elem(&mut data, &src, *n),
11611 None => push_elem(&mut data, &base, i),
11612 }
11613 }
11614 Ok(Array::new(y.shape.clone(), data))
11615}
11616
11617fn map_paths(y: &Array) -> Array {
11626 fn coord_of(shape: &[usize], mut i: usize) -> Array {
11627 let mut out = vec![0i64; shape.len()];
11628 for k in (0..shape.len()).rev() {
11629 out[k] = (i % shape[k]) as i64;
11630 i /= shape[k];
11631 }
11632 Array::from_i64(out)
11633 }
11634 fn go(y: &Array, prefix: &[Array]) -> Array {
11635 let Some(boxes) = y.as_boxes() else {
11636 if prefix.is_empty() {
11637 return Array::new(vec![0], Data::I64(Vec::new().into()));
11638 }
11639 return Array::new(vec![prefix.len()], Data::Box(prefix.to_vec().into()));
11640 };
11641 let cells: Vec<Array> = boxes
11642 .iter()
11643 .enumerate()
11644 .map(|(i, b)| {
11645 let mut path = prefix.to_vec();
11646 path.push(coord_of(&y.shape, i));
11647 go(b, &path)
11648 })
11649 .collect();
11650 Array::new(y.shape.clone(), Data::Box(cells.into()))
11651 }
11652 go(y, &[])
11653}
11654
11655fn shift_fill(
11660 x: &Array,
11661 y: &Array,
11662 fill: &Array,
11663 near: NearInt,
11664 span: Span,
11665) -> Result<Array> {
11666 let counts = axis_counts(x, "shift", near, span)?;
11667 if y.rank() == 0 {
11668 return Ok(y.clone());
11669 }
11670 if counts.len() > y.rank() {
11671 return Err(Error::new(
11672 ErrorKind::Length,
11673 format!("shift has {} amounts for an argument of rank {}", counts.len(), y.rank()),
11674 Some(span),
11675 ));
11676 }
11677 if fill.count() != 1 {
11678 return Err(Error::new(ErrorKind::Length, "a fill is one atom", Some(span)));
11679 }
11680 let mismatch = || {
11681 Error::new(ErrorKind::Type, "the fill and the argument differ in kind", Some(span))
11682 };
11683 let t = DType::promote(y.dtype(), fill.dtype()).ok_or_else(mismatch)?;
11684 let (Some(base), Some(f)) = (y.data.cast(t), fill.data.cast(t)) else {
11685 return Err(mismatch());
11686 };
11687 let st = strides(&y.shape);
11688 let r = y.rank();
11689 let mut data = Data::empty(t);
11690 let mut coord = vec![0usize; r];
11691 for _ in 0..y.count() {
11692 let mut idx = 0usize;
11693 let mut vacated = false;
11694 for k in 0..r {
11695 let from = (coord[k] as i64).saturating_add(counts.get(k).copied().unwrap_or(0));
11699 if from < 0 || from >= y.shape[k] as i64 {
11700 vacated = true;
11701 break;
11702 }
11703 idx += from as usize * st[k];
11704 }
11705 if vacated {
11706 push_elem(&mut data, &f, 0);
11707 } else {
11708 push_elem(&mut data, &base, idx);
11709 }
11710 odometer(&mut coord, &y.shape);
11711 }
11712 Ok(Array::new(y.shape.clone(), data))
11713}
11714
11715fn memo_key(a: &Array, out: &mut Vec<u64>) -> bool {
11720 out.push(a.rank() as u64);
11721 out.extend(a.shape.iter().map(|&n| n as u64));
11722 out.push(a.dtype() as u64);
11723 match &a.data {
11724 Data::Ext(_) | Data::Rat(_) => false,
11725 Data::Box(items) => items.iter().all(|item| memo_key(item, out)),
11726 d => {
11727 for i in 0..d.len() {
11728 out.push(elem_key(d, i));
11729 }
11730 true
11731 }
11732 }
11733}
11734
11735fn memoised(
11737 u: &Verb,
11738 cache: &MemoCache,
11739 x: Option<&Array>,
11740 y: &Array,
11741 ctx: &mut Ctx<'_>,
11742 span: Span,
11743) -> Result<Array> {
11744 let apply = |ctx: &mut Ctx<'_>| match x {
11745 Some(x) => u.dyad(x, y, ctx, span),
11746 None => u.monad(y, ctx, span),
11747 };
11748 let mut key = vec![u64::from(x.is_some())];
11749 let keyed = x.is_none_or(|x| memo_key(x, &mut key)) && memo_key(y, &mut key);
11750 if !keyed {
11751 return apply(ctx);
11752 }
11753 if let Ok(map) = cache.lock() && let Some(hit) = map.get(&key) {
11754 return Ok(hit.clone());
11755 }
11756 let out = apply(ctx)?;
11757 if let Ok(mut map) = cache.lock() {
11758 map.insert(key, out.clone());
11759 }
11760 Ok(out)
11761}
11762
11763fn at_level(
11769 u: &Verb,
11770 level: i64,
11771 spread: bool,
11772 y: &Array,
11773 ctx: &mut Ctx<'_>,
11774 span: Span,
11775) -> Result<Array> {
11776 let n = if level < 0 { (boxing_level(y) + level).max(0) } else { level };
11778 if !spread {
11779 return map_level(u, n, y, ctx, span);
11780 }
11781 let mut cells = Vec::new();
11782 collect_level(u, n, y, ctx, span, &mut cells)?;
11783 let count = cells.len();
11784 assemble(&[count], cells, span)
11785}
11786
11787fn map_level(u: &Verb, n: i64, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
11788 let Some(boxes) = y.as_boxes().filter(|_| boxing_level(y) > n) else {
11789 return u.monad(y, ctx, span);
11790 };
11791 let boxes = boxes.to_vec();
11792 let mut cells = Vec::with_capacity(boxes.len());
11793 for b in &boxes {
11794 cells.push(map_level(u, n, b, ctx, span)?);
11795 }
11796 Ok(Array::new(y.shape.clone(), Data::Box(cells.into())))
11797}
11798
11799fn at_level_dyad(
11804 u: &Verb,
11805 level: i64,
11806 spread: bool,
11807 x: &Array,
11808 y: &Array,
11809 ctx: &mut Ctx<'_>,
11810 span: Span,
11811) -> Result<Array> {
11812 let depth = |a: &Array| if level < 0 { (boxing_level(a) + level).max(0) } else { level };
11815 let (nx, ny) = (depth(x), depth(y));
11816 if !spread {
11817 return map_level_dyad(u, nx, ny, x, y, ctx, span);
11818 }
11819 let mut cells = Vec::new();
11820 collect_level_dyad(u, nx, ny, x, y, ctx, span, &mut cells)?;
11821 let count = cells.len();
11822 assemble(&[count], cells, span)
11823}
11824
11825struct LevelPairs {
11827 left: Vec<Array>,
11828 right: Vec<Array>,
11829 shape: Vec<usize>,
11830}
11831
11832fn level_pairs(
11835 nx: i64,
11836 ny: i64,
11837 x: &Array,
11838 y: &Array,
11839 span: Span,
11840) -> Result<Option<LevelPairs>> {
11841 let bx = x.as_boxes().filter(|_| boxing_level(x) > nx);
11842 let by = y.as_boxes().filter(|_| boxing_level(y) > ny);
11843 Ok(match (bx, by) {
11844 (None, None) => None,
11845 (Some(bx), None) => {
11846 let n = bx.len();
11847 Some(LevelPairs {
11848 left: bx.to_vec(),
11849 right: vec![y.clone(); n],
11850 shape: x.shape.clone(),
11851 })
11852 }
11853 (None, Some(by)) => {
11854 let n = by.len();
11855 Some(LevelPairs {
11856 left: vec![x.clone(); n],
11857 right: by.to_vec(),
11858 shape: y.shape.clone(),
11859 })
11860 }
11861 (Some(bx), Some(by)) => {
11862 if x.shape != y.shape {
11863 return Err(Error::new(
11864 ErrorKind::Length,
11865 format!(
11866 "the levels do not agree: left shape {}, right shape {}",
11867 show_shape(&x.shape),
11868 show_shape(&y.shape)
11869 ),
11870 Some(span),
11871 ));
11872 }
11873 Some(LevelPairs { left: bx.to_vec(), right: by.to_vec(), shape: x.shape.clone() })
11874 }
11875 })
11876}
11877
11878fn map_level_dyad(
11879 u: &Verb,
11880 nx: i64,
11881 ny: i64,
11882 x: &Array,
11883 y: &Array,
11884 ctx: &mut Ctx<'_>,
11885 span: Span,
11886) -> Result<Array> {
11887 let Some(step) = level_pairs(nx, ny, x, y, span)? else {
11888 return u.dyad(x, y, ctx, span);
11889 };
11890 let mut cells = Vec::with_capacity(step.left.len());
11891 for (a, b) in step.left.iter().zip(step.right.iter()) {
11892 cells.push(map_level_dyad(u, nx, ny, a, b, ctx, span)?);
11893 }
11894 Ok(Array::new(step.shape, Data::Box(cells.into())))
11895}
11896
11897#[allow(clippy::too_many_arguments)]
11898fn collect_level_dyad(
11899 u: &Verb,
11900 nx: i64,
11901 ny: i64,
11902 x: &Array,
11903 y: &Array,
11904 ctx: &mut Ctx<'_>,
11905 span: Span,
11906 out: &mut Vec<Array>,
11907) -> Result<()> {
11908 let Some(step) = level_pairs(nx, ny, x, y, span)? else {
11909 out.push(u.dyad(x, y, ctx, span)?);
11910 return Ok(());
11911 };
11912 for (a, b) in step.left.iter().zip(step.right.iter()) {
11913 collect_level_dyad(u, nx, ny, a, b, ctx, span, out)?;
11914 }
11915 Ok(())
11916}
11917
11918fn collect_level(
11919 u: &Verb,
11920 n: i64,
11921 y: &Array,
11922 ctx: &mut Ctx<'_>,
11923 span: Span,
11924 out: &mut Vec<Array>,
11925) -> Result<()> {
11926 let Some(boxes) = y.as_boxes().filter(|_| boxing_level(y) > n) else {
11927 out.push(u.monad(y, ctx, span)?);
11928 return Ok(());
11929 };
11930 let boxes = boxes.to_vec();
11931 for b in &boxes {
11932 collect_level(u, n, b, ctx, span, out)?;
11933 }
11934 Ok(())
11935}
11936
11937fn poly_coeffs(y: &Array, span: Span) -> Result<Vec<Cx>> {
11941 let c = y
11942 .data
11943 .cast(DType::Complex)
11944 .ok_or_else(|| Error::domain("a polynomial's coefficients are numbers", span))?;
11945 match c {
11946 Data::Complex(v) => Ok(v.as_slice().to_vec()),
11947 _ => Err(Error::internal("coefficients did not cast to complex")),
11948 }
11949}
11950
11951fn poly_coeffs_relaxed(y: &Array, span: Span) -> Result<Vec<Cx>> {
11957 if y.count() == 0 {
11958 return Ok(Vec::new());
11959 }
11960 poly_coeffs(y, span)
11961}
11962
11963fn root_form_coeffs(parts: &[Array], span: Span) -> Result<Vec<Cx>> {
11966 let (multiplier, roots) = root_form(parts, span)?;
11967 let mut coeffs = vec![multiplier];
11968 for r in poly_coeffs_relaxed(roots, span)? {
11969 let mut next = vec![cx::ZERO; coeffs.len() + 1];
11970 for (k, &c) in coeffs.iter().enumerate() {
11971 next[k + 1] = cx::add(next[k + 1], c);
11972 next[k] = cx::sub(next[k], cx::mul(c, r));
11973 }
11974 coeffs = next;
11975 }
11976 Ok(coeffs)
11977}
11978
11979fn root_form(parts: &[Array], span: Span) -> Result<(Cx, &Array)> {
11983 match parts {
11984 [roots] => Ok((cx::ONE, roots)),
11985 [multiplier, roots] => Ok((
11986 poly_coeffs_relaxed(multiplier, span)?.first().copied().unwrap_or(cx::ONE),
11987 roots,
11988 )),
11989 _ => Err(Error::domain("the root form of a polynomial is `multiplier ; roots`", span)),
11990 }
11991}
11992
11993const HYPERGEOMETRIC_TERMS: usize = 1 << 16;
11997
11998fn cx_list(v: &[Cx]) -> String {
12000 v.iter()
12001 .map(|z| if z[1] == 0.0 { format!("{}", z[0]) } else { format!("{}j{}", z[0], z[1]) })
12002 .collect::<Vec<_>>()
12003 .join(" ")
12004}
12005
12006fn hypergeometric(num: &[Cx], den: &[Cx], y: &Array, span: Span) -> Result<Array> {
12014 let (num, den) = cancel_parameters(num, den);
12015 let at = poly_coeffs(y, span)?;
12016 let mut out = Vec::with_capacity(at.len());
12017 for z in &at {
12018 out.push(hypergeometric_at(&num, &den, *z, span)?);
12019 }
12020 let mut a = complex_or_real(out);
12021 a.shape = y.shape.clone();
12022 Ok(a)
12023}
12024
12025fn cancel_parameters(num: &[Cx], den: &[Cx]) -> (Vec<Cx>, Vec<Cx>) {
12028 let mut left: Vec<Cx> = Vec::with_capacity(num.len());
12029 let mut right: Vec<Cx> = den.to_vec();
12030 for a in num {
12031 match right.iter().position(|b| b == a) {
12032 Some(i) => {
12033 right.remove(i);
12034 }
12035 None => left.push(*a),
12036 }
12037 }
12038 (left, right)
12039}
12040
12041fn hypergeometric_at(num: &[Cx], den: &[Cx], z: Cx, span: Span) -> Result<Cx> {
12042 let real = |v: &[Cx]| v.iter().all(|c| c[1] == 0.0);
12046 if z[1] == 0.0 && real(num) && real(den) {
12047 let n: Vec<f64> = num.iter().map(|c| c[0]).collect();
12048 let d: Vec<f64> = den.iter().map(|c| c[0]).collect();
12049 return Ok([hypergeometric_real(&n, &d, z[0], span)?, 0.0]);
12050 }
12051 let mut sum = cx::ONE;
12052 let mut term = cx::ONE;
12053 for k in 0..HYPERGEOMETRIC_TERMS {
12054 let kk = [k as f64, 0.0];
12055 let mut ratio = z;
12056 for a in num {
12057 ratio = cx::mul(ratio, cx::add(*a, kk));
12058 }
12059 for b in den {
12060 ratio = cx::div(ratio, cx::add(*b, kk));
12061 }
12062 term = cx::div(cx::mul(term, ratio), [k as f64 + 1.0, 0.0]);
12063 if !term[0].is_finite() || !term[1].is_finite() {
12064 return Ok(term);
12067 }
12068 let before = sum;
12069 sum = cx::add(sum, term);
12070 if sum == before {
12072 return Ok(sum);
12073 }
12074 }
12075 Err(Error::domain(
12076 format!("the hypergeometric series did not converge within {HYPERGEOMETRIC_TERMS} terms"),
12077 span,
12078 ))
12079}
12080
12081fn hypergeometric_real(num: &[f64], den: &[f64], z: f64, span: Span) -> Result<f64> {
12082 let mut sum = 1.0f64;
12083 let mut term = 1.0f64;
12084 for k in 0..HYPERGEOMETRIC_TERMS {
12085 let kk = k as f64;
12086 let mut ratio = z;
12087 for a in num {
12088 ratio *= a + kk;
12089 }
12090 for b in den {
12091 ratio /= b + kk;
12092 }
12093 term = term * ratio / (kk + 1.0);
12094 if !term.is_finite() {
12095 return Ok(term);
12096 }
12097 let before = sum;
12098 sum += term;
12099 if sum == before {
12100 return Ok(sum);
12101 }
12102 }
12103 Err(Error::domain(
12104 format!("the hypergeometric series did not converge within {HYPERGEOMETRIC_TERMS} terms"),
12105 span,
12106 ))
12107}
12108
12109fn complex_or_real(values: Vec<Cx>) -> Array {
12111 if values.iter().all(|z| z[1] == 0.0) {
12112 return Array::from_f64(values.iter().map(|z| z[0]).collect());
12113 }
12114 Array::new(vec![values.len()], Data::Complex(values.into()))
12115}
12116
12117fn poly_eval(x: &Array, y: &Array, span: Span) -> Result<Array> {
12120 let at = poly_coeffs(y, span)?;
12121 let at = at.first().copied().unwrap_or(cx::ZERO);
12122 let value = match x.as_boxes() {
12123 Some(parts) => {
12124 let (mut v, roots) = root_form(parts, span)?;
12125 for r in poly_coeffs_relaxed(roots, span)? {
12126 v = cx::mul(v, cx::sub(at, r));
12127 }
12128 v
12129 }
12130 None => {
12131 let c = poly_coeffs_relaxed(x, span)?;
12132 let mut v = cx::ZERO;
12133 for &k in c.iter().rev() {
12134 v = cx::add(cx::mul(v, at), k);
12135 }
12136 v
12137 }
12138 };
12139 Ok(scalar_complex_or_real(value))
12140}
12141
12142fn scalar_complex_or_real(z: Cx) -> Array {
12143 if z[1] == 0.0 {
12144 return Array::scalar_f64(z[0]);
12145 }
12146 Array::new(vec![], Data::Complex(vec![z].into()))
12147}
12148
12149fn poly_roots(y: &Array, span: Span) -> Result<Array> {
12153 if let Some(parts) = y.as_boxes().filter(|p| !p.is_empty()) {
12154 return Ok(complex_or_real(root_form_coeffs(parts, span)?));
12155 }
12156 let mut c = poly_coeffs_relaxed(y, span)?;
12157 while c.len() > 1 && c[c.len() - 1] == cx::ZERO {
12158 c.pop();
12159 }
12160 if c.iter().all(|&k| k == cx::ZERO) {
12164 let pair = vec![Array::scalar_i64(0), Array::new(vec![0], Data::empty(DType::I64))];
12165 return Ok(Array::new(vec![2], Data::Box(pair.into())));
12166 }
12167 if c.len() < 2 {
12168 return Err(Error::domain("a polynomial's roots need a coefficient of x", span));
12169 }
12170 let lead = c[c.len() - 1];
12171 let monic: Vec<Cx> = c.iter().map(|&k| cx::div(k, lead)).collect();
12172 let roots = durand_kerner(&monic);
12173 let pair = vec![scalar_complex_or_real(lead), complex_or_real(roots)];
12174 Ok(Array::new(vec![2], Data::Box(pair.into())))
12175}
12176
12177fn durand_kerner(monic: &[Cx]) -> Vec<Cx> {
12184 let d = monic.len() - 1;
12185 let seed = [0.4, 0.9];
12186 let mut z: Vec<Cx> = Vec::with_capacity(d);
12187 let mut p = cx::ONE;
12188 for _ in 0..d {
12189 z.push(p);
12190 p = cx::mul(p, seed);
12191 }
12192 let value = |monic: &[Cx], at: Cx| {
12193 let mut v = cx::ZERO;
12194 for &k in monic.iter().rev() {
12195 v = cx::add(cx::mul(v, at), k);
12196 }
12197 v
12198 };
12199 for _ in 0..500 {
12200 let mut moved: f64 = 0.0;
12201 for i in 0..d {
12202 let mut denom = cx::ONE;
12203 for j in 0..d {
12204 if i != j {
12205 denom = cx::mul(denom, cx::sub(z[i], z[j]));
12206 }
12207 }
12208 if denom == cx::ZERO {
12209 continue;
12210 }
12211 let step = cx::div(value(monic, z[i]), denom);
12212 z[i] = cx::sub(z[i], step);
12213 moved = moved.max(step[0].hypot(step[1]));
12214 }
12215 if moved < 1e-15 {
12216 break;
12217 }
12218 }
12219 let mut z = polished_repeats(monic, z);
12220 for r in &mut z {
12222 if r[1].abs() < 1e-9 {
12223 r[1] = 0.0;
12224 }
12225 if r[0].abs() < 1e-12 {
12226 r[0] = 0.0;
12227 }
12228 }
12229 let coarse = |v: f64| -> f64 {
12235 if v == 0.0 || !v.is_finite() { v } else { format!("{v:.11e}").parse().unwrap_or(v) }
12236 };
12237 let mut keyed: Vec<([f64; 3], Cx)> =
12238 z.into_iter().map(|r| ([coarse(cx::abs(r)), coarse(r[0]), coarse(r[1])], r)).collect();
12239 keyed.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
12240 keyed.into_iter().map(|(_, r)| r).collect()
12241}
12242
12243fn polished_repeats(monic: &[Cx], z: Vec<Cx>) -> Vec<Cx> {
12258 let d = z.len();
12259 if d < 2 {
12260 return z;
12261 }
12262 let raw = coefficient_error(monic, &z);
12263 let scale = monic.iter().map(|&k| cx::abs(k)).fold(1.0f64, f64::max);
12264 let allowed = raw.max(1e-13 * scale);
12265 for reach in [1e-3, 1e-4, 1e-5, 1e-6, 1e-7] {
12266 let mut group: Vec<usize> = (0..d).collect();
12269 for i in 0..d {
12270 for j in 0..i {
12271 let apart = cx::abs(cx::sub(z[i], z[j]));
12272 let span = reach * (1.0 + cx::abs(z[i]).max(cx::abs(z[j])));
12273 if apart <= span {
12274 let (a, b) = (group[i], group[j]);
12275 let (keep, drop) = (a.min(b), a.max(b));
12276 for g in &mut group {
12277 if *g == drop {
12278 *g = keep;
12279 }
12280 }
12281 }
12282 }
12283 }
12284 let mut centre = vec![cx::ZERO; d];
12285 let mut size = vec![0usize; d];
12286 for i in 0..d {
12287 centre[group[i]] = cx::add(centre[group[i]], z[i]);
12288 size[group[i]] += 1;
12289 }
12290 if size.iter().all(|&n| n < 2) {
12291 return z;
12292 }
12293 let mut settled: Vec<Option<Cx>> = vec![None; d];
12294 for g in 0..d {
12295 if size[g] == 0 {
12296 continue;
12297 }
12298 let start = cx::div(centre[g], cx::from_real(size[g] as f64));
12299 settled[g] = Some(if size[g] < 2 {
12306 start
12307 } else {
12308 newton_at(&nth_derivative(monic, size[g] - 1), start)
12309 });
12310 }
12311 let out: Vec<Cx> = (0..d).map(|i| settled[group[i]].unwrap_or(z[i])).collect();
12312 if out.iter().all(|r| r[0].is_finite() && r[1].is_finite())
12313 && coefficient_error(monic, &out) <= allowed
12314 {
12315 return out;
12316 }
12317 }
12318 z
12319}
12320
12321fn newton_at(poly: &[Cx], start: Cx) -> Cx {
12323 let mut z = start;
12324 for _ in 0..40 {
12325 let (mut p, mut slope) = (cx::ZERO, cx::ZERO);
12326 for &k in poly.iter().rev() {
12327 slope = cx::add(cx::mul(slope, z), p);
12328 p = cx::add(cx::mul(p, z), k);
12329 }
12330 if slope == cx::ZERO {
12331 break;
12332 }
12333 let step = cx::div(p, slope);
12334 let next = cx::sub(z, step);
12335 if !next[0].is_finite() || !next[1].is_finite() {
12336 break;
12337 }
12338 z = next;
12339 if cx::abs(step) <= 1e-17 * (1.0 + cx::abs(z)) {
12340 break;
12341 }
12342 }
12343 z
12344}
12345
12346fn nth_derivative(c: &[Cx], k: usize) -> Vec<Cx> {
12348 let mut out = c.to_vec();
12349 for _ in 0..k {
12350 if out.len() < 2 {
12351 return vec![cx::ZERO];
12352 }
12353 out = out
12354 .iter()
12355 .enumerate()
12356 .skip(1)
12357 .map(|(i, &v)| cx::mul(v, cx::from_real(i as f64)))
12358 .collect();
12359 }
12360 out
12361}
12362
12363fn coefficient_error(monic: &[Cx], roots: &[Cx]) -> f64 {
12367 let mut built = vec![cx::ONE];
12368 for &r in roots {
12369 let mut next = vec![cx::ZERO; built.len() + 1];
12370 for (k, &c) in built.iter().enumerate() {
12371 next[k + 1] = cx::add(next[k + 1], c);
12372 next[k] = cx::sub(next[k], cx::mul(c, r));
12373 }
12374 built = next;
12375 }
12376 let mut worst: f64 = 0.0;
12377 for (k, &want) in monic.iter().enumerate() {
12378 let got = built.get(k).copied().unwrap_or(cx::ZERO);
12379 worst = worst.max(cx::abs(cx::sub(got, want)) / (1.0 + cx::abs(want)));
12380 }
12381 worst
12382}
12383
12384fn poly_deriv(y: &Array, span: Span) -> Result<Array> {
12387 let c = match y.as_boxes().filter(|p| !p.is_empty()) {
12390 Some(parts) => root_form_coeffs(parts, span)?,
12391 None => poly_coeffs_relaxed(y, span)?,
12392 };
12393 if c.len() < 2 {
12394 return Ok(Array::from_i64(vec![0]));
12395 }
12396 let out: Vec<Cx> =
12397 c.iter().enumerate().skip(1).map(|(k, &v)| cx::mul(v, cx::from_real(k as f64))).collect();
12398 Ok(narrow_numbers(complex_or_real(out)))
12399}
12400
12401fn poly_integral(x: &Array, y: &Array, span: Span) -> Result<Array> {
12403 let c = match y.as_boxes().filter(|p| !p.is_empty()) {
12407 Some(parts) => root_form_coeffs(parts, span)?,
12408 None => poly_coeffs(y, span)?,
12409 };
12410 let k = poly_coeffs(x, span)?;
12411 let mut out = vec![k.first().copied().unwrap_or(cx::ZERO)];
12412 for (i, &v) in c.iter().enumerate() {
12413 out.push(cx::div(v, cx::from_real((i + 1) as f64)));
12414 }
12415 Ok(narrow_numbers(complex_or_real(out)))
12416}
12417
12418fn narrow_numbers(a: Array) -> Array {
12422 let Data::F64(v) = &a.data else { return a };
12423 if v.iter().any(|x| !x.is_finite() || x.fract() != 0.0 || x.abs() > 9e15) {
12424 return a;
12425 }
12426 let values: Vec<i64> = v.iter().map(|&x| x as i64).collect();
12427 Array::new(a.shape, Data::I64(values.into()))
12428}
12429
12430fn characteristics(u: &Verb, y: &Array, span: Span) -> Result<Array> {
12434 let which = y.to_i64_vec().and_then(|v| v.first().copied());
12435 let chars = |s: String| Ok(Array::from_chars(s.chars().collect()));
12436 match which {
12437 Some(0) => {
12438 let ranks = u.ranks();
12439 Ok(Array::from_f64(
12440 ranks
12441 .iter()
12442 .map(|&r| if r == RANK_INF { f64::INFINITY } else { r as f64 })
12443 .collect(),
12444 ))
12445 }
12446 Some(-1) => match obverse(u) {
12450 Some(v) => chars(v.name()),
12451 None => Err(Error::not_yet(
12452 format!("the obverse of {} (no inverse is known)", u.name()),
12453 span,
12454 )),
12455 },
12456 Some(1) => match reduce_identity(u, 1, crate::Lang::J).as_ref().map(identity_spelling) {
12459 Some(s) => chars(s),
12460 None => Err(Error::not_yet(
12461 format!("the identity function of {} (u b. 1)", u.name()),
12462 span,
12463 )),
12464 },
12465 _ => Err(Error::not_yet("a verb characteristic other than 0, 1 and _1", span)),
12466 }
12467}
12468
12469fn identity_spelling(d: &Data) -> String {
12472 let one = Array::new(Vec::new(), d.slice(0, 1));
12473 let text = crate::fmt::format_array(&one, &crate::fmt::FmtOpts::J);
12474 format!("{} $~ }}.@$", text.trim())
12475}
12476
12477fn with_operands<R>(
12485 alpha: &Operand,
12486 omega: Option<&Operand>,
12487 ctx: &mut Ctx<'_>,
12488 f: impl FnOnce(&mut Ctx<'_>) -> Result<R>,
12489) -> Result<R> {
12490 let names = ["⍺⍺", "⍵⍵"];
12491 let operands = [Some(alpha), omega];
12492 let saved: Vec<(Option<Verb>, Option<Array>)> =
12493 names.iter().map(|n| (ctx.env.verb(n).cloned(), ctx.env.global(n))).collect();
12494 for (name, operand) in names.iter().zip(operands) {
12495 match operand {
12496 Some(Operand::Func(v)) => ctx.env.define((*name).to_string(), (**v).clone()),
12497 Some(Operand::Value(a)) => ctx.env.set_global((*name).to_string(), (**a).clone()),
12498 None => {}
12499 }
12500 }
12501 let out = f(ctx);
12502 for (name, (verb, value)) in names.iter().zip(saved) {
12503 match verb {
12504 Some(v) => ctx.env.define((*name).to_string(), v),
12505 None => ctx.env.undefine(name),
12506 }
12507 match value {
12508 Some(a) => ctx.env.set_global((*name).to_string(), a),
12509 None => ctx.env.unset_global(name),
12510 }
12511 }
12512 out
12513}
12514
12515fn is_mixed_simple(a: &Array) -> bool {
12519 let Some(items) = a.as_boxes() else { return false };
12520 if items.is_empty() || items.iter().any(|b| b.rank() != 0 || b.dtype() == DType::Box) {
12521 return false;
12522 }
12523 let mut common = Some(items[0].dtype());
12524 for b in &items[1..] {
12525 common = common.and_then(|t| DType::promote(t, b.dtype()));
12526 }
12527 common.is_none()
12528}
12529
12530fn nest(y: &Array) -> Array {
12533 if y.dtype() == DType::Box || y.rank() == 0 {
12534 return y.clone();
12535 }
12536 Array::boxed(y.clone())
12537}
12538
12539fn key_pairs(
12543 u: &Verb,
12544 keys: &Array,
12545 values: Option<&Array>,
12546 ctx: &mut Ctx<'_>,
12547 span: Span,
12548) -> Result<Array> {
12549 let base = if keys.rank() == 0 { Array::new(vec![1], keys.data.clone()) } else { keys.clone() };
12550 let n = base.items();
12551 if let Some(v) = values && v.items() != n {
12552 return Err(Error::new(
12553 ErrorKind::Length,
12554 format!("{n} key(s) for {} item(s)", v.items()),
12555 Some(span),
12556 ));
12557 }
12558 let groups = group_positions(&base, ctx.cfg.tol);
12559 let origin = ctx.cfg.rules.origin;
12560 let mut cells = Vec::with_capacity(groups.len());
12561 for (first, at) in &groups {
12562 let key = item_or_self(&base, *first);
12563 let group = match values {
12564 Some(v) => select_items(v, at),
12565 None => Array::from_i64(at.iter().map(|&i| origin + i as i64).collect()),
12566 };
12567 let monadic = matches!(u, Verb::Explicit(d) if d.left.is_none());
12570 cells.push(if monadic {
12571 u.monad(&group, ctx, span)?
12572 } else {
12573 u.dyad(&key, &group, ctx, span)?
12574 });
12575 }
12576 let count = cells.len();
12577 assemble(&[count], cells, span)
12578}
12579
12580fn group_positions(y: &Array, tol: Tol) -> Vec<(usize, Vec<usize>)> {
12583 let n = y.items();
12584 let m = y.item_size();
12585 let hashable = match y.dtype() {
12590 DType::Box | DType::Ext | DType::Rat => false,
12591 DType::F64 | DType::Complex => tol.ct == 0.0,
12592 _ => true,
12593 };
12594 if hashable {
12595 return if m == 1 {
12596 group_by_key(n, |i| elem_key(&y.data, i))
12597 } else {
12598 group_by_key(n, |i| (0..m).map(|k| elem_key(&y.data, i * m + k)).collect::<Vec<u64>>())
12599 };
12600 }
12601 let mut keys: Vec<Array> = Vec::new();
12602 let mut groups: Vec<(usize, Vec<usize>)> = Vec::new();
12603 for i in 0..n {
12604 let item = y.item(i);
12605 match keys.iter().position(|k| arrays_match(k, &item, tol)) {
12606 Some(at) => groups[at].1.push(i),
12607 None => {
12608 keys.push(item);
12609 groups.push((i, vec![i]));
12610 }
12611 }
12612 }
12613 groups
12614}
12615
12616fn group_by_key<K, F>(n: usize, key: F) -> Vec<(usize, Vec<usize>)>
12620where
12621 K: Eq + std::hash::Hash,
12622 F: Fn(usize) -> K,
12623{
12624 use std::collections::hash_map::Entry;
12625 let mut groups: Vec<(usize, Vec<usize>)> = Vec::new();
12626 let mut at: HashMap<K, usize, KeyHash> =
12627 HashMap::with_capacity_and_hasher(n.min(1 << 16), KeyHash);
12628 for i in 0..n {
12629 match at.entry(key(i)) {
12630 Entry::Occupied(e) => groups[*e.get()].1.push(i),
12631 Entry::Vacant(e) => {
12632 e.insert(groups.len());
12633 groups.push((i, vec![i]));
12634 }
12635 }
12636 }
12637 groups
12638}
12639
12640#[derive(Clone, Copy, Default)]
12646struct KeyHash;
12647
12648impl std::hash::BuildHasher for KeyHash {
12649 type Hasher = KeyHasher;
12650 fn build_hasher(&self) -> KeyHasher {
12651 KeyHasher(0)
12652 }
12653}
12654
12655struct KeyHasher(u64);
12656
12657impl std::hash::Hasher for KeyHasher {
12658 fn finish(&self) -> u64 {
12659 let mut x = self.0;
12660 x ^= x >> 33;
12661 x = x.wrapping_mul(0xff51_afd7_ed55_8ccd);
12662 x ^ (x >> 29)
12663 }
12664 fn write(&mut self, bytes: &[u8]) {
12665 for &b in bytes {
12666 self.write_u64(b as u64);
12667 }
12668 }
12669 fn write_u64(&mut self, n: u64) {
12670 self.0 = (self.0.rotate_left(5) ^ n).wrapping_mul(0x9e37_79b9_7f4a_7c15);
12671 }
12672 fn write_usize(&mut self, n: usize) {
12673 self.write_u64(n as u64);
12674 }
12675}
12676
12677fn format_spec(x: &Array, y: &Array, fmt: &FmtOpts, span: Span) -> Result<Array> {
12683 let spec = x
12684 .to_i64_vec()
12685 .ok_or_else(|| Error::domain("a format specification is whole numbers", span))?;
12686 if y.dtype() == DType::Box {
12687 return Err(Error::not_yet("format by specification of a nested array", span));
12688 }
12689 let cols = if y.rank() == 0 { 1 } else { y.shape[y.rank() - 1] };
12690 let rows = y.count() / cols.max(1);
12691 let pairs: Vec<(Option<i64>, i64)> = match spec.len() {
12693 1 => vec![(None, spec[0]); cols],
12694 2 => vec![(Some(spec[0]), spec[1]); cols],
12695 n if n == 2 * cols => spec.chunks(2).map(|c| (Some(c[0]), c[1])).collect(),
12696 n => {
12697 return Err(Error::new(
12698 ErrorKind::Length,
12699 format!("{n} specification value(s) for {cols} column(s)"),
12700 Some(span),
12701 ));
12702 }
12703 };
12704 if pairs.iter().any(|&(w, p)| w.is_some_and(|w| w < 0) || p < 0) {
12705 return Err(Error::domain("a format width and precision are nonnegative", span));
12706 }
12707 for &(w, p) in &pairs {
12711 crate::limits::count(w.unwrap_or(0) as u128, span)?;
12712 crate::limits::count(p as u128, span)?;
12713 }
12714 let numbers = y.to_f64_vec();
12715 let text = |i: usize, p: i64| -> String {
12716 match (&y.data, &numbers) {
12717 (Data::Char(v), _) => v[i].to_string(),
12718 (_, Some(v)) => {
12719 let s = format!("{:.*}", p as usize, v[i]);
12720 if v[i] < 0.0 { format!("{}{}", fmt.neg, &s[1..]) } else { s }
12721 }
12722 _ => String::new(),
12723 }
12724 };
12725 if y.dtype() != DType::Char && numbers.is_none() {
12726 return Err(Error::domain("format by specification takes numbers or characters", span));
12727 }
12728 let widths: Vec<usize> = pairs
12730 .iter()
12731 .enumerate()
12732 .map(|(c, &(w, p))| match w {
12733 Some(w) => w as usize,
12734 None => {
12735 (0..rows).map(|r| text(r * cols + c, p).chars().count()).max().unwrap_or(0) + 1
12736 }
12737 })
12738 .collect();
12739 let line = crate::limits::count(widths.iter().map(|&w| w as u128).sum(), span)?;
12740 let total = crate::limits::count(rows as u128 * line as u128, span)?;
12741 let mut out: Vec<char> = Vec::with_capacity(total);
12742 for r in 0..rows {
12743 for c in 0..cols {
12744 let s = text(r * cols + c, pairs[c].1);
12745 let len = s.chars().count();
12746 if len > widths[c] {
12747 return Err(Error::domain(
12748 format!("{s} does not fit a field {} wide", widths[c]),
12749 span,
12750 ));
12751 }
12752 out.extend(std::iter::repeat_n(' ', widths[c] - len));
12753 out.extend(s.chars());
12754 }
12755 }
12756 let mut shape = if y.rank() == 0 { Vec::new() } else { y.shape[..y.rank() - 1].to_vec() };
12757 shape.push(line);
12758 Ok(Array::new(shape, Data::Char(out.into())))
12759}
12760
12761fn sequential_machine(x: &Array, y: &Array, span: Span) -> Result<Array> {
12776 let Some(parts) = x.as_boxes() else {
12777 return Err(Error::domain("a sequential machine is a boxed description", span));
12778 };
12779 if x.rank() > 1 || !(2..=4).contains(&parts.len()) {
12780 return Err(Error::domain(
12781 "a sequential machine is 2 to 4 boxes: f ; s ; m ; ijrd",
12782 span,
12783 ));
12784 }
12785 let whole = |a: &Array, what: &str| -> Result<Vec<i64>> {
12786 a.to_i64_vec().ok_or_else(|| Error::domain(format!("{what} is whole numbers"), span))
12787 };
12788 let form = *whole(&parts[0], "a sequential machine's result form")?
12789 .first()
12790 .ok_or_else(|| Error::domain("a sequential machine needs a result form", span))?;
12791 if !(0..=5).contains(&form) {
12792 return Err(Error::domain(format!("{form} is not a result form of 0 to 5"), span));
12793 }
12794 let table = &parts[1];
12795 if table.rank() != 3 || table.shape[2] != 2 {
12796 return Err(Error::new(
12797 ErrorKind::Rank,
12798 "a sequential machine's transition table is shaped p q 2",
12799 Some(span),
12800 ));
12801 }
12802 let (states, classes) = (table.shape[0], table.shape[1]);
12803 let entries = whole(table, "a transition table")?;
12804 let map = parts.get(2).filter(|a| a.count() > 0);
12805 let start = match parts.get(3) {
12806 Some(a) => whole(a, "a sequential machine's starting values")?,
12807 None => Vec::new(),
12808 };
12809 let start = if start.is_empty() { vec![0, -1, 0, -1] } else { start };
12810 if start.len() != 4 {
12811 return Err(Error::new(
12812 ErrorKind::Length,
12813 "a sequential machine starts from four values: i j r d",
12814 Some(span),
12815 ));
12816 }
12817 let (mut i, mut word, mut state, ending) = (start[0], start[1], start[2], start[3]);
12818 let n = y.count() as i64;
12819
12820 let codes: Option<Vec<i64>> = match map {
12823 Some(m) => Some(whole(m, "a sequential machine's map")?),
12824 None => None,
12825 };
12826 let values: Vec<i64> = match (&y.data, &codes) {
12827 (Data::Char(v), Some(_)) => v.as_slice().iter().map(|&c| c as i64).collect(),
12828 (_, None) => y
12829 .to_i64_vec()
12830 .ok_or_else(|| Error::domain("a sequential machine over characters needs a map", span))?,
12831 _ => {
12832 return Err(Error::not_yet(
12833 "a sequential machine's map over a numeric argument (x's third box)",
12834 span,
12835 ));
12836 }
12837 };
12838 let class_at = |at: i64| -> Result<i64> {
12839 let raw = values[at as usize];
12840 let Some(m) = &codes else { return Ok(raw) };
12841 if raw < 0 || raw as usize >= m.len() {
12842 return Err(Error::new(
12843 ErrorKind::Domain,
12844 format!("{raw} is outside a map of {} entries", m.len()),
12845 Some(span),
12846 ));
12847 }
12848 Ok(m[raw as usize])
12849 };
12850
12851 let mut trace: Vec<i64> = Vec::new();
12852 let mut words: Vec<(i64, i64, i64)> = Vec::new();
12853 let mut emit = |word: i64, at: i64, place: i64| -> Result<()> {
12854 if word < 0 {
12855 return Err(Error::new(
12856 ErrorKind::Domain,
12857 "a sequential machine ended a word before one had begun",
12858 Some(span),
12859 ));
12860 }
12861 words.push((word, at - word, place));
12862 Ok(())
12863 };
12864 loop {
12865 let class = if i < n {
12866 class_at(i)?
12867 } else if ending >= 0 {
12868 ending
12869 } else {
12870 if word >= 0 {
12874 emit(word, i, classes as i64 * state)?;
12875 }
12876 break;
12877 };
12878 if state < 0 || state as usize >= states || class < 0 || class as usize >= classes {
12879 return Err(Error::new(
12880 ErrorKind::Domain,
12881 format!(
12882 "state {state} and class {class} are outside a {states} by {classes} table"
12883 ),
12884 Some(span),
12885 ));
12886 }
12887 let at = (state as usize * classes + class as usize) * 2;
12888 let (next, code) = (entries[at], entries[at + 1]);
12889 trace.extend_from_slice(&[i, word, state, class, next, code]);
12890 let place = class + classes as i64 * state;
12891 state = next;
12892 match code {
12893 0 => {}
12894 1 => word = i,
12895 2 => {
12896 emit(word, i, place)?;
12897 word = i;
12898 }
12899 3 => {
12900 emit(word, i, place)?;
12901 word = -1;
12902 }
12903 4 | 5 => {
12904 return Err(Error::not_yet(
12905 "a sequential machine's vector output (codes 4 and 5)",
12906 span,
12907 ));
12908 }
12909 6 => break,
12910 other => {
12911 return Err(Error::domain(
12912 format!("{other} is not a sequential machine output code"),
12913 span,
12914 ));
12915 }
12916 }
12917 if i >= n {
12918 break;
12919 }
12920 i += 1;
12921 }
12922 Ok(sequential_result(form, &words, &trace, y))
12923}
12924
12925fn sequential_result(form: i64, words: &[(i64, i64, i64)], trace: &[i64], y: &Array) -> Array {
12928 let piece = |&(at, len, _): &(i64, i64, i64)| {
12929 Array::new(vec![len as usize], y.data.slice(at as usize, (at + len) as usize))
12930 };
12931 match form {
12932 0 => Array::new(
12933 vec![words.len()],
12934 Data::Box(words.iter().map(piece).collect::<Vec<_>>().into()),
12935 ),
12936 1 => {
12937 let mut data = Data::empty(y.dtype());
12938 for w in words {
12939 data.extend_from(&piece(w).data);
12940 }
12941 let n = data.len();
12942 Array::new(vec![n], data)
12943 }
12944 2 => Array::new(
12945 vec![words.len(), 2],
12946 Data::I64(words.iter().flat_map(|&(at, len, _)| [at, len]).collect::<Vec<_>>().into()),
12947 ),
12948 3 => Array::from_i64(words.iter().map(|&(_, _, place)| place).collect()),
12949 4 => Array::new(
12950 vec![words.len(), 3],
12951 Data::I64(
12952 words
12953 .iter()
12954 .flat_map(|&(at, len, place)| [at, len, place])
12955 .collect::<Vec<_>>()
12956 .into(),
12957 ),
12958 ),
12959 _ => Array::new(vec![trace.len() / 6, 6], Data::I64(trace.to_vec().into())),
12960 }
12961}
12962
12963fn parse_numbers(x: &Array, y: &Array, span: Span) -> Result<Array> {
12968 if x.count() != 1 {
12969 return Err(Error::new(
12970 ErrorKind::Rank,
12971 "the stand-in for an unreadable word is one value",
12972 Some(span),
12973 ));
12974 }
12975 if y.count() == 0 {
12978 return Ok(Array::new(y.shape.clone(), Data::empty(DType::Bool)));
12979 }
12980 let Data::Char(text) = &y.data else {
12981 return Err(Error::domain("reading numbers from text needs characters", span));
12982 };
12983 let line: String = text.as_slice().iter().collect();
12984 crate::frontend::j::numbers_from_text(&line, x)
12985 .ok_or_else(|| Error::domain("the stand-in for an unreadable word is a number", span))
12986}
12987
12988fn format_field(value: f64, precision: usize, exponential: bool, neg: char) -> String {
12992 let sign = |s: String| match s.strip_prefix('-') {
12993 Some(rest) if rest.bytes().all(|b| !b.is_ascii_digit() || b == b'0') => rest.to_string(),
12996 Some(rest) => format!("{neg}{rest}"),
12997 None => s,
12998 };
12999 if !exponential {
13000 return sign(format!("{value:.precision$}"));
13001 }
13002 let text = format!("{value:.precision$e}");
13005 let (mantissa, exponent) = text.split_once('e').unwrap_or((text.as_str(), "0"));
13006 let exponent = match exponent.strip_prefix('-') {
13007 Some(rest) => format!("{neg}{rest}"),
13008 None => exponent.to_string(),
13009 };
13010 format!("{}e{exponent}", sign(mantissa.to_string()))
13011}
13012
13013fn format_spec_j(x: &Array, y: &Array, fmt: &FmtOpts, span: Span) -> Result<Array> {
13021 let Some(spec) = x.to_complex_vec() else {
13022 return Err(Error::domain("a format specification is numbers", span));
13023 };
13024 if y.dtype() == DType::Box {
13025 return Err(Error::domain("format by specification takes numbers", span));
13026 }
13027 let Some(values) = y.to_f64_vec() else {
13028 return Err(Error::domain("format by specification takes numbers", span));
13029 };
13030 let cols = if y.rank() == 0 { 1 } else { y.shape[y.rank() - 1] };
13031 let rows = if cols == 0 { 0 } else { y.count() / cols };
13032 let fields: Vec<[f64; 2]> = match spec.len() {
13033 1 => vec![spec[0]; cols],
13034 n if n == cols => spec,
13035 n => {
13036 return Err(Error::new(
13037 ErrorKind::Length,
13038 format!("{n} specification value(s) for {cols} column(s)"),
13039 Some(span),
13040 ));
13041 }
13042 };
13043 for &[w, d] in &fields {
13048 crate::limits::count(w.abs() as u128, span)?;
13049 crate::limits::count(d.max(0.0) as u128, span)?;
13050 }
13051 let text = |r: usize, c: usize| {
13052 let [w, d] = fields[c];
13053 let digits = if w == 0.0 { d.max(0.0) } else { d.max(0.0).min(w.abs()) };
13058 format_field(values[r * cols + c], digits as usize, w < 0.0, fmt.neg)
13059 };
13060 let widths: Vec<usize> = (0..cols)
13063 .map(|c| {
13064 let w = fields[c][0];
13065 if w != 0.0 {
13066 return w.abs() as usize;
13067 }
13068 let wide = (0..rows).map(|r| text(r, c).chars().count()).max().unwrap_or(0);
13069 wide + usize::from(c > 0)
13070 })
13071 .collect();
13072 let line = crate::limits::count(widths.iter().map(|&w| w as u128).sum(), span)?;
13073 let total = crate::limits::count(rows as u128 * line as u128, span)?;
13074 let mut out: Vec<char> = Vec::with_capacity(total);
13075 for r in 0..rows {
13076 for c in 0..cols {
13077 let s = text(r, c);
13078 let (lead, body) = match (fields[c][0] < 0.0, s.strip_prefix(fmt.neg)) {
13081 (false, _) => (String::new(), s.as_str()),
13082 (true, Some(rest)) => (fmt.neg.to_string(), rest),
13083 (true, None) => (" ".to_string(), s.as_str()),
13084 };
13085 let len = lead.chars().count() + body.chars().count();
13086 if len > widths[c] {
13087 out.extend(std::iter::repeat_n('*', widths[c]));
13088 continue;
13089 }
13090 if fields[c][0] < 0.0 {
13091 out.extend(lead.chars());
13092 out.extend(body.chars());
13093 out.extend(std::iter::repeat_n(' ', widths[c] - len));
13094 } else {
13095 out.extend(std::iter::repeat_n(' ', widths[c] - len));
13096 out.extend(body.chars());
13097 }
13098 }
13099 }
13100 let mut shape = if y.rank() == 0 { Vec::new() } else { y.shape[..y.rank() - 1].to_vec() };
13101 shape.push(line);
13102 Ok(Array::new(shape, Data::Char(out.into())))
13103}
13104
13105fn iota_apl(y: &Array, origin: i64, near: NearInt, span: Span) -> Result<Array> {
13109 if y.rank() > 1 {
13110 return Err(Error::new(
13111 ErrorKind::Rank,
13112 "the index generator takes a shape, which is a scalar or a vector",
13113 Some(span),
13114 ));
13115 }
13116 let dims = y
13117 .to_i64_vec_near(near)
13118 .ok_or_else(|| Error::domain("index generator needs an integer argument", span))?;
13119 if dims.iter().any(|&n| n < 0) {
13120 return Err(Error::domain("index generator needs nonnegative lengths", span));
13121 }
13122 if dims.len() <= 1 {
13123 let n = dims.first().copied().unwrap_or(0);
13124 crate::limits::count(n as u128, span)?;
13125 return Ok(Array::from_i64((0..n).map(|i| origin + i).collect()));
13126 }
13127 let shape: Vec<usize> = dims.iter().map(|&n| n as usize).collect();
13128 let total = crate::limits::elements(&shape, span)?;
13129 let mut cells = Vec::with_capacity(total);
13130 let mut coord = vec![0usize; shape.len()];
13131 for _ in 0..total {
13132 cells.push(Array::from_i64(coord.iter().map(|&c| origin + c as i64).collect()));
13133 odometer(&mut coord, &shape);
13134 }
13135 Ok(Array::new(shape, Data::Box(cells.into())))
13136}
13137
13138fn carry_exact(result: Array, y: &Array) -> Array {
13144 if !matches!(y.dtype(), DType::Ext | DType::Rat) {
13145 return result;
13146 }
13147 match result.data.cast(DType::Ext) {
13148 Some(data) => Array::new(result.shape, data),
13149 None => result,
13150 }
13151}
13152
13153fn carry_exact2(result: Array, x: &Array, y: &Array) -> Array {
13154 let widened = carry_exact(result, x);
13155 carry_exact(widened, y)
13156}
13157
13158fn truth_table(m: u8, x: &Array, y: &Array, span: Span) -> Result<Array> {
13161 let table = m & 15;
13162 let bit = |a: i64, b: i64| ((table >> (3 - (2 * a + b))) & 1) as i64;
13163 let xs = x
13164 .to_i64_vec()
13165 .ok_or_else(|| Error::domain("a boolean function takes integers", span))?;
13166 let ys = y
13167 .to_i64_vec()
13168 .ok_or_else(|| Error::domain("a boolean function takes integers", span))?;
13169 let (a, b) = (xs.first().copied().unwrap_or(0), ys.first().copied().unwrap_or(0));
13170 if m < 16 {
13171 if !(0..=1).contains(&a) || !(0..=1).contains(&b) {
13172 return Err(Error::domain(
13173 format!("{m} b. takes 0 and 1; {m} b. + 16 is the same function on every bit"),
13174 span,
13175 ));
13176 }
13177 return Ok(Array::scalar_bool(bit(a, b) != 0));
13178 }
13179 let mut out = 0i64;
13180 for k in 0..64 {
13181 if bit((a >> k) & 1, (b >> k) & 1) != 0 {
13182 out |= 1i64 << k;
13183 }
13184 }
13185 Ok(Array::scalar_i64(out))
13186}
13187
13188pub fn amend_at(
13193 base: &Array,
13194 slots: &[Option<Array>],
13195 value: &Array,
13196 origin: i64,
13197 near: NearInt,
13198 span: Span,
13199) -> Result<Array> {
13200 if slots.len() != base.rank() {
13201 return Err(Error::new(
13202 ErrorKind::Rank,
13203 format!(
13204 "indexed assignment needs one index per axis: {} slot(s) for a rank-{} value",
13205 slots.len(),
13206 base.rank()
13207 ),
13208 Some(span),
13209 ));
13210 }
13211 if !base.is_row_major() || !value.is_row_major() {
13214 let (b, v) = (base.to_row_major(), value.to_row_major());
13215 return amend_at(&b, slots, &v, origin, near, span);
13216 }
13217 let mut axes: Vec<Vec<usize>> = Vec::with_capacity(slots.len());
13219 let mut selected: Vec<usize> = Vec::new();
13220 for (k, slot) in slots.iter().enumerate() {
13221 let len = base.shape[k];
13222 let Some(idx) = slot else {
13223 axes.push((0..len).collect());
13224 selected.push(len);
13225 continue;
13226 };
13227 let Some(values) = idx.to_i64_vec_near(near) else {
13228 return Err(Error::new(
13229 ErrorKind::Type,
13230 "an index must be numeric",
13231 Some(span),
13232 ));
13233 };
13234 let mut positions = Vec::with_capacity(values.len());
13235 for v in values {
13236 let p = v - origin;
13237 if p < 0 || p as usize >= len {
13238 return Err(Error::new(
13239 ErrorKind::Domain,
13240 format!("index {v} is outside axis {k}, which has {len} element(s)"),
13241 Some(span),
13242 ));
13243 }
13244 positions.push(p as usize);
13245 }
13246 if idx.rank() > 0 {
13248 selected.push(positions.len());
13249 }
13250 axes.push(positions);
13251 }
13252 let count: usize = axes.iter().map(Vec::len).product();
13253 if value.rank() != 0 && (value.shape != selected || value.count() != count) {
13254 return Err(Error::new(
13255 ErrorKind::Shape,
13256 format!(
13257 "indexed assignment needs a scalar or a {} value, not a {} one",
13258 show_shape(&selected),
13259 show_shape(&value.shape)
13260 ),
13261 Some(span),
13262 ));
13263 }
13264 let dtype = DType::promote(base.dtype(), value.dtype()).ok_or_else(|| {
13267 Error::new(
13268 ErrorKind::Type,
13269 format!(
13270 "cannot put a {} value into a {} array",
13271 value.dtype().name(),
13272 base.dtype().name()
13273 ),
13274 Some(span),
13275 )
13276 })?;
13277 let mut out = base.cast(dtype).ok_or_else(|| Error::internal("promotion failed"))?;
13278 let src = value.cast(dtype).ok_or_else(|| Error::internal("promotion failed"))?;
13279 let strides = row_major_strides(&base.shape);
13280 let mut coords = vec![0usize; axes.len()];
13281 for n in 0..count {
13282 let mut rest = n;
13283 for k in (0..axes.len()).rev() {
13284 let len = axes[k].len();
13285 coords[k] = axes[k][rest % len];
13286 rest /= len;
13287 }
13288 let at: usize = coords.iter().zip(&strides).map(|(c, s)| c * s).sum();
13289 let from = if src.rank() == 0 { 0 } else { n };
13290 put_element(&mut out.data, at, &src.data, from);
13291 }
13292 Ok(out)
13293}
13294
13295fn row_major_strides(shape: &[usize]) -> Vec<usize> {
13296 let mut strides = vec![1usize; shape.len()];
13297 for k in (0..shape.len().saturating_sub(1)).rev() {
13298 strides[k] = strides[k + 1] * shape[k + 1];
13299 }
13300 strides
13301}
13302
13303fn put_element(dst: &mut Data, at: usize, src: &Data, from: usize) {
13305 match (dst, src) {
13306 (Data::Bool(d), Data::Bool(s)) => d.to_mut()[at] = s.as_slice()[from],
13307 (Data::I64(d), Data::I64(s)) => d.to_mut()[at] = s.as_slice()[from],
13308 (Data::Ext(d), Data::Ext(s)) => d.to_mut()[at] = s.as_slice()[from].clone(),
13309 (Data::Rat(d), Data::Rat(s)) => d.to_mut()[at] = s.as_slice()[from].clone(),
13310 (Data::F64(d), Data::F64(s)) => d.to_mut()[at] = s.as_slice()[from],
13311 (Data::Char(d), Data::Char(s)) => d.to_mut()[at] = s.as_slice()[from],
13312 (Data::Box(d), Data::Box(s)) => d.to_mut()[at] = s.as_slice()[from].clone(),
13313 _ => debug_assert!(false, "amend across types"),
13315 }
13316}
13317
13318fn agenda_pick(
13321 vs: &[Verb],
13322 w: &Verb,
13323 x: Option<&Array>,
13324 y: &Array,
13325 ctx: &mut Ctx<'_>,
13326 span: Span,
13327) -> Result<Verb> {
13328 let chosen = match x {
13329 None => w.monad(y, ctx, span)?,
13330 Some(x) => w.dyad(x, y, ctx, span)?,
13331 };
13332 let at = chosen
13333 .to_i64_vec_near(ctx.cfg.near())
13334 .and_then(|v| v.first().copied())
13335 .ok_or_else(|| Error::domain("an agenda index must be an integer", span))?;
13336 pick_gerund(vs, at, span)
13337}
13338
13339pub(crate) fn pick_gerund(vs: &[Verb], at: i64, span: Span) -> Result<Verb> {
13342 usize::try_from(at)
13343 .ok()
13344 .and_then(|k| vs.get(k))
13345 .cloned()
13346 .ok_or_else(|| {
13347 Error::domain(
13348 format!("agenda {at} is out of range: the gerund has {} verbs", vs.len()),
13349 span,
13350 )
13351 })
13352}
13353
13354fn evoke(
13359 vs: &[Verb],
13360 form: i64,
13361 x: Option<&Array>,
13362 y: &Array,
13363 ctx: &mut Ctx<'_>,
13364 span: Span,
13365) -> Result<Array> {
13366 if vs.is_empty() {
13367 return Err(Error::domain("an evoked gerund is empty", span));
13368 }
13369 if form == 0 {
13370 let mut cells = Vec::with_capacity(vs.len());
13371 for v in vs {
13372 cells.push(match x {
13373 None => v.monad(y, ctx, span)?,
13374 Some(x) => v.dyad(x, y, ctx, span)?,
13375 });
13376 }
13377 return assemble(&[vs.len()], cells, span);
13378 }
13379 if x.is_some() {
13380 return Err(Error::domain("m`:3 has no dyadic meaning", span));
13381 }
13382 let items = if y.rank() == 0 { vec![y.clone()] } else { y.cells(1) };
13383 let Some((last, rest)) = items.split_last() else {
13384 return Err(Error::domain("m`:3 needs an argument with items", span));
13385 };
13386 let mut acc = last.clone();
13387 for (i, item) in rest.iter().enumerate().rev() {
13388 acc = vs[i % vs.len()].dyad(item, &acc, ctx, span)?;
13389 }
13390 Ok(acc)
13391}
13392
13393fn stencil(u: &Verb, w: &[i64], y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
13398 if w.len() > y.rank() {
13399 return Err(Error::new(
13400 ErrorKind::Rank,
13401 format!("a stencil of {} axis/axes into a rank-{} value", w.len(), y.rank()),
13402 Some(span),
13403 ));
13404 }
13405 if w.iter().any(|&n| n <= 0) {
13406 return Err(Error::domain("a stencil window is a positive size", span));
13407 }
13408 let y = y.to_row_major();
13409 let k = w.len();
13410 let st = strides(&y.shape);
13411 let frame: Vec<usize> = y.shape[..k].to_vec();
13412 let mut wshape: Vec<usize> = w.iter().map(|&n| n as usize).collect();
13414 wshape.extend_from_slice(&y.shape[k..]);
13415 let inner: usize = y.shape[k..].iter().product();
13416 let total: usize = frame.iter().product();
13417 let mut cells = Vec::with_capacity(total);
13418 let mut at = vec![0usize; frame.len()];
13419 let mut coord = vec![0usize; k];
13420 for _ in 0..total {
13421 let mut data = Data::empty(y.dtype());
13422 coord.iter_mut().for_each(|c| *c = 0);
13423 let count: usize = w.iter().map(|&n| n as usize).product();
13424 for _ in 0..count {
13425 let mut base = 0usize;
13426 let mut inside = true;
13427 for a in 0..k {
13428 let off = at[a] as i64 + coord[a] as i64 - (w[a] - 1) / 2;
13429 if off < 0 || off >= y.shape[a] as i64 {
13430 inside = false;
13431 break;
13432 }
13433 base += off as usize * st[a];
13434 }
13435 for j in 0..inner {
13436 if inside {
13437 push_elem(&mut data, &y.data, base + j);
13438 } else {
13439 data.push_fill();
13440 }
13441 }
13442 odometer(&mut coord, &wshape[..k]);
13443 }
13444 cells.push(u.monad(&Array::new(wshape.clone(), data), ctx, span)?);
13445 odometer(&mut at, &frame);
13446 }
13447 assemble(&frame, cells, span)
13448}
13449
13450fn folds_eagerly(u: &Verb) -> bool {
13457 let Verb::Reduce(inner) = u else { return false };
13458 matches!(
13459 **inner,
13460 Verb::Prim(Prim {
13461 dyad: DyadOp::Scalar(
13462 ScalarDyad::Add
13463 | ScalarDyad::Mul
13464 | ScalarDyad::Min
13465 | ScalarDyad::Max
13466 | ScalarDyad::Gcd
13467 ),
13468 ..
13469 })
13470 )
13471}
13472
13473fn outfix(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
13477 let k = one_int(x, "an outfix width", ctx.cfg.near(), span)?;
13478 let n = y.items() as i64;
13479 let list = as_list(y);
13480 let starts: Vec<i64> = if k < 0 {
13488 let step = i128::from(k.unsigned_abs());
13489 let count = (i128::from(n) + step - 1) / step;
13490 (0..count).map(|i| (i * step) as i64).collect()
13491 } else {
13492 (0..=(n - k)).collect()
13493 };
13494 let width = k.unsigned_abs() as usize;
13495 if !list.dtype().is_numeric()
13505 && u.is_pure()
13506 && folds_eagerly(u)
13507 && n >= 1
13508 && (n >= 2 || !starts.is_empty())
13509 {
13510 let probe =
13516 if n == 1 { select_items(&list, &[0, 0]) } else { list.clone() };
13517 u.monad(&probe, ctx, span)?;
13518 }
13519 if starts.is_empty() {
13523 let cell = u.is_pure().then(|| select_items(&list, &[]));
13524 return Ok(empty_frame(&[0], list.dtype(), cell, ctx, |cell, c| u.monad(cell, c, span)));
13525 }
13526 let mut cells = Vec::with_capacity(starts.len());
13527 for start in starts {
13528 let start = start as usize;
13529 let keep: Vec<usize> =
13530 (0..n as usize).filter(|&i| i < start || i >= start + width).collect();
13531 cells.push(u.monad(&select_items(&list, &keep), ctx, span)?);
13532 }
13533 assemble(&[cells.len()], cells, span)
13534}
13535
13536pub(crate) fn obverse(v: &Verb) -> Option<Verb> {
13548 Some(match v {
13549 Verb::Prim(p) => prim_obverse(v, p)?,
13550 Verb::WithObverse(_, w) => (**w).clone(),
13552 Verb::Atop(f, g) => {
13554 Verb::Atop(Box::new(obverse(g)?), Box::new(obverse(f)?))
13555 }
13556 Verb::Compose(f, g) | Verb::Beside(f, g) => {
13557 Verb::Atop(Box::new(obverse(g)?), Box::new(obverse(f)?))
13558 }
13559 Verb::Rank(f, r) => Verb::Rank(Box::new(obverse(f)?), *r),
13560 Verb::Fit(f, n) => Verb::Fit(Box::new(obverse(f)?), *n),
13561 Verb::Each(f, rule) => Verb::Each(Box::new(obverse(f)?), *rule),
13564 Verb::Reduce(f) if is_dyad(f, DyadOp::Scalar(ScalarDyad::Mul)) => named("q:")?,
13567 Verb::Windowed(f, kind) => scan_obverse(f, *kind)?,
13570 Verb::PowerN(f, Power::Times(n)) => {
13572 Verb::PowerN(Box::new(obverse(f)?), Power::Times(*n))
13573 }
13574 Verb::BondLeft(m, f) => bond_obverse(m, f, true)?,
13575 Verb::BondRight(f, n) => bond_obverse(n, f, false)?,
13576 _ => return None,
13577 })
13578}
13579
13580fn named(spelling: &'static str) -> Option<Verb> {
13582 crate::frontend::j::verb_named(spelling)
13583}
13584
13585fn made(name: &'static str, monad: MonadOp, ranks: [i64; 3]) -> Verb {
13588 Verb::Prim(Prim { name, monad, dyad: DyadOp::None, ranks })
13589}
13590
13591fn is_dyad(v: &Verb, op: DyadOp) -> bool {
13592 matches!(v, Verb::Prim(p) if p.dyad == op)
13593}
13594
13595fn atop(f: Verb, g: Verb) -> Verb {
13596 Verb::Atop(Box::new(f), Box::new(g))
13597}
13598
13599fn prim_obverse(v: &Verb, p: &Prim) -> Option<Verb> {
13601 use ScalarMonad as SM;
13602 if matches!(
13609 p.monad,
13610 MonadOp::Scalar(SM::Conj | SM::Neg | SM::Recip | SM::OneMinus)
13611 | MonadOp::Reverse
13612 | MonadOp::TransposeAxes
13613 | MonadOp::GradeUp { .. }
13614 | MonadOp::CycleForm
13615 | MonadOp::MatrixInverse
13616 | MonadOp::PolyRoots
13617 | MonadOp::Same
13618 ) {
13619 return Some(v.clone());
13620 }
13621 if p.dyad == DyadOp::Copy {
13626 return Some(expand_verb());
13627 }
13628 let built = match p.monad {
13629 MonadOp::Scalar(SM::Imaginary) => atop(named("-")?, named("j.")?),
13632 MonadOp::Scalar(SM::Polar) => {
13635 atop(atop(named("-")?, named("j.")?), named("^.")?)
13636 }
13637 MonadOp::Scalar(SM::Pi) => Verb::BondLeft(
13640 Array::scalar_f64(std::f64::consts::FRAC_1_PI),
13641 Box::new(named("*")?),
13642 ),
13643 MonadOp::ComplexParts { polar } => Verb::Rank(
13646 Box::new(Verb::Reduce(Box::new(named(if polar { "r." } else { "j." })?))),
13647 [1, RANK_INF, RANK_INF],
13648 ),
13649 MonadOp::GradeDown { origin } => atop(
13651 Verb::Prim(Prim {
13652 name: "/:",
13653 monad: MonadOp::GradeUp { origin },
13654 dyad: DyadOp::GradeSelect { down: false },
13655 ranks: [RANK_INF, RANK_INF, RANK_INF],
13656 }),
13657 named("|.")?,
13658 ),
13659 MonadOp::PrimeFactors => {
13662 Verb::Rank(Box::new(Verb::Reduce(Box::new(named("*")?))), [1, RANK_INF, RANK_INF])
13663 }
13664 MonadOp::Words => atop(
13667 named("}:")?,
13668 atop(
13669 named(";")?,
13670 Verb::Each(
13671 Box::new(Verb::BondRight(
13672 Box::new(named(",")?),
13673 Array::from_chars(vec![' ']),
13674 )),
13675 Enclose::Always,
13676 ),
13677 ),
13678 ),
13679 MonadOp::ToExact => Verb::BondLeft(Array::scalar_i64(-1), Box::new(named("x:")?)),
13681 MonadOp::Unicode { .. } => {
13682 Verb::BondLeft(Array::scalar_i64(3), Box::new(named("u:")?))
13683 }
13684 MonadOp::Symbols => Verb::BondLeft(Array::scalar_i64(5), Box::new(named("s:")?)),
13685 MonadOp::NthPrime => made("p:^:_1", MonadOp::PrimeCount, [0, 0, 0]),
13687 MonadOp::Sparse => {
13688 made("$.^:_1", MonadOp::Dense, [RANK_INF, RANK_INF, RANK_INF])
13689 }
13690 MonadOp::Indices { origin: 0, boxed_coords: false } => {
13691 made("I.^:_1", MonadOp::IndicesInverse, [1, RANK_INF, RANK_INF])
13692 }
13693 MonadOp::Format if p.name == "⍕" => Verb::Prim(Prim {
13696 name: "⍎",
13697 monad: MonadOp::Execute { apl: true },
13698 dyad: DyadOp::None,
13699 ranks: [1, RANK_INF, RANK_INF],
13700 }),
13701 MonadOp::Format => named("\".")?,
13702 MonadOp::Execute { apl: true } => Verb::Prim(Prim {
13703 name: "⍕",
13704 monad: MonadOp::Format,
13705 dyad: DyadOp::FormatSpec,
13706 ranks: [RANK_INF, 1, RANK_INF],
13707 }),
13708 MonadOp::Execute { apl: false } => named("\":")?,
13709 _ => {
13710 let by_monad: Option<&'static str> = match p.monad {
13711 MonadOp::Scalar(SM::Exp) => Some("^."),
13712 MonadOp::Scalar(SM::Ln) => Some("^"),
13713 MonadOp::Scalar(SM::Sqrt) => Some("*:"),
13714 MonadOp::Scalar(SM::Square) => Some("%:"),
13715 MonadOp::Scalar(SM::Double) => Some("-:"),
13716 MonadOp::Scalar(SM::Halve) => Some("+:"),
13717 MonadOp::Scalar(SM::Inc) => Some("<:"),
13718 MonadOp::Scalar(SM::Dec) => Some(">:"),
13719 MonadOp::Enclose(_) => Some(">"),
13720 MonadOp::Open => Some("<"),
13721 MonadOp::DecodeBits => Some("#:"),
13722 MonadOp::EncodeBits => Some("#."),
13723 MonadOp::Itemize => Some("{."),
13724 MonadOp::Head => Some(",:"),
13725 _ => None,
13726 };
13727 named(by_monad?)?
13728 }
13729 };
13730 Some(built)
13731}
13732
13733fn expand_verb() -> Verb {
13735 Verb::Prim(Prim {
13736 name: "#^:_1",
13737 monad: MonadOp::None,
13738 dyad: DyadOp::Expand,
13739 ranks: [RANK_INF, 1, RANK_INF],
13740 })
13741}
13742
13743fn scan_obverse(f: &Verb, kind: WindowKind) -> Option<Verb> {
13751 use ScalarDyad as SD;
13752 let Verb::Reduce(inner) = f else { return None };
13753 let Verb::Prim(p) = &**inner else { return None };
13754 let DyadOp::Scalar(op) = p.dyad else { return None };
13755 let suffix = match kind {
13756 WindowKind::Prefix | WindowKind::Scan => false,
13757 WindowKind::Suffix => true,
13758 };
13759 let fill = match op {
13762 SD::Add | SD::Sub => 0.0,
13763 SD::Mul | SD::DivJ | SD::DivApl => 1.0,
13764 _ => return None,
13765 };
13766 let shift = Verb::ShiftFill(Array::scalar_f64(fill));
13767 let neighbour = if suffix {
13768 Verb::BondLeft(Array::scalar_i64(1), Box::new(shift))
13769 } else {
13770 shift
13771 };
13772 let step = match (op, suffix) {
13775 (SD::Add, false) | (SD::Sub, false) => named("-")?,
13776 (SD::Add, true) => named("-")?,
13777 (SD::Sub, true) => named("+")?,
13778 (SD::Mul, _) | (SD::DivJ | SD::DivApl, false) => named("%")?,
13779 (SD::DivJ | SD::DivApl, true) => named("*")?,
13780 _ => return None,
13781 };
13782 let differences = Verb::Hook(Box::new(step), Box::new(neighbour));
13783 let alternate = matches!((op, suffix), (SD::Sub, false) | (SD::DivJ | SD::DivApl, false));
13786 if !alternate {
13787 return Some(differences);
13788 }
13789 let signs = atop(
13790 Verb::BondRight(Box::new(named("$")?), Array::from_i64(vec![1, -1])),
13791 named("#")?,
13792 );
13793 let apply = if matches!(op, SD::Sub) { named("*")? } else { named("^")? };
13794 Some(Verb::Fork(Box::new(differences), Box::new(apply), Box::new(signs)))
13795}
13796
13797fn bond_obverse(n: &Array, f: &Verb, left: bool) -> Option<Verb> {
13801 if let Verb::Commute(g) = f {
13804 if !left {
13805 return bond_obverse(n, g, true);
13806 }
13807 return None;
13808 }
13809 if let Some(v) = structural_bond_obverse(n, f, left) {
13810 return Some(v);
13811 }
13812 let Verb::Prim(p) = f else { return None };
13813 let bond = |name: &'static str, arg: &Array| -> Option<Verb> {
13814 let g = named(name)?;
13815 Some(if left {
13816 Verb::BondLeft(arg.clone(), Box::new(g))
13817 } else {
13818 Verb::BondRight(Box::new(g), arg.clone())
13819 })
13820 };
13821 use ScalarDyad as SD;
13822 let DyadOp::Scalar(op) = p.dyad else { return None };
13823 if matches!(op, SD::Circle) {
13824 return left.then(|| Some(Verb::BondLeft(negated(n)?, Box::new(named("o.")?))))?;
13827 }
13828 match (op, left) {
13829 (SD::Sub | SD::DivJ | SD::DivApl, true) => bond(p.name, n),
13831 (SD::Add, _) => Some(Verb::BondRight(Box::new(named("-")?), n.clone())),
13835 (SD::Mul, _) => Some(Verb::BondRight(Box::new(named("%")?), n.clone())),
13836 (SD::Sub, false) => bond("+", n),
13837 (SD::DivJ | SD::DivApl, false) => bond("*", n),
13838 (SD::Pow, false) => Some(Verb::BondLeft(n.clone(), Box::new(named("%:")?))),
13840 (SD::Pow, true) => Some(Verb::BondLeft(n.clone(), Box::new(named("^.")?))),
13841 (SD::Log, true) => Some(Verb::BondLeft(n.clone(), Box::new(named("^")?))),
13845 (SD::Root, true) => Some(Verb::BondRight(Box::new(named("^")?), n.clone())),
13846 (SD::Log, false) => Some(Verb::BondRight(Box::new(named("%:")?), n.clone())),
13849 (SD::Root, false) => Some(Verb::BondRight(Box::new(named("^.")?), n.clone())),
13850 _ => None,
13851 }
13852}
13853
13854fn negated(n: &Array) -> Option<Array> {
13857 if let Some(v) = n.to_i64_vec() {
13858 let out: Vec<i64> = v.iter().map(|&k| -k).collect();
13859 return Some(Array::new(n.shape.clone(), Data::I64(out.into())));
13860 }
13861 let v = n.to_f64_vec()?;
13862 let out: Vec<f64> = v.iter().map(|&k| -k).collect();
13863 Some(Array::new(n.shape.clone(), Data::F64(out.into())))
13864}
13865
13866fn one_number(n: &Array) -> Option<f64> {
13869 match n.to_f64_vec()?[..] {
13870 [v] if n.rank() <= 1 => Some(v),
13871 _ => None,
13872 }
13873}
13874
13875fn structural_bond_obverse(n: &Array, f: &Verb, left: bool) -> Option<Verb> {
13879 let p = match f {
13883 Verb::Prim(p) => p,
13884 Verb::Rank(inner, _) => match &**inner {
13885 Verb::Prim(p) => p,
13886 _ => return None,
13887 },
13888 _ => return None,
13889 };
13890 match (p.dyad, left) {
13891 (DyadOp::Rotate | DyadOp::RotateApl { .. }, true) => {
13893 Some(Verb::BondLeft(negated(n)?, Box::new(f.clone())))
13894 }
13895 (DyadOp::Copy, true) => Some(Verb::BondLeft(n.clone(), Box::new(expand_verb()))),
13897 (DyadOp::AppendLeading | DyadOp::AppendLast, _) => {
13901 let items = if n.rank() == 0 { 1 } else { n.shape[0] } as i64;
13902 let count = if left { items } else { -items };
13903 Some(Verb::BondLeft(Array::scalar_i64(count), Box::new(named("}.")?)))
13904 }
13905 (DyadOp::Drop, true) => {
13909 let k = one_number(n)?;
13910 let size = Verb::Atop(
13911 Box::new(Verb::BondLeft(Array::scalar_f64(k.abs()), Box::new(named("+")?))),
13912 Box::new(named("#")?),
13913 );
13914 let width = if k >= 0.0 { atop(named("-")?, size) } else { size };
13915 Some(Verb::Hook(
13916 Box::new(Verb::Commute(Box::new(named("{.")?))),
13917 Box::new(width),
13918 ))
13919 }
13920 (DyadOp::Decode, true) => {
13923 let width = atop(
13924 Verb::BondRight(Box::new(named("$")?), n.clone()),
13925 atop(
13926 named(">:")?,
13927 atop(
13928 named("<.")?,
13929 atop(
13930 Verb::BondLeft(n.clone(), Box::new(named("^.")?)),
13931 atop(
13932 Verb::BondLeft(Array::scalar_i64(1), Box::new(named(">.")?)),
13933 atop(
13934 Verb::Reduce(Box::new(named(">.")?)),
13935 atop(named("|")?, named(",")?),
13936 ),
13937 ),
13938 ),
13939 ),
13940 ),
13941 );
13942 Some(Verb::Fork(Box::new(width), Box::new(named("#:")?), Box::new(named("]")?)))
13943 }
13944 (DyadOp::Encode, true) => Some(Verb::BondLeft(n.clone(), Box::new(named("#.")?))),
13946 (DyadOp::AnagramFrom | DyadOp::Permute, true) => {
13949 let spelling = if p.dyad == DyadOp::AnagramFrom { "A." } else { "C." };
13950 let inverse = atop(
13951 atop(named("/:")?, Verb::BondLeft(n.clone(), Box::new(named(spelling)?))),
13952 atop(named("i.")?, named("#")?),
13953 );
13954 Some(Verb::Fork(Box::new(inverse), Box::new(named("{")?), Box::new(named("]")?)))
13955 }
13956 _ => None,
13957 }
13958}
13959
13960fn self_classify(y: &Array, tol: Tol) -> Array {
13965 let items = if y.rank() == 0 { 1 } else { y.items() };
13966 let keys = nub(&as_list(y), tol);
13967 let rows = keys.items();
13968 let mut out = Vec::with_capacity(rows * items);
13969 for i in 0..rows {
13970 let key = item_or_self(&keys, i);
13971 for j in 0..items {
13972 out.push(arrays_match(&key, &item_or_self(y, j), tol) as u8);
13973 }
13974 }
13975 Array::new(vec![rows, items], Data::Bool(out.into()))
13976}
13977
13978fn nub_sieve(y: &Array, tol: Tol, lang: crate::Lang) -> Array {
13985 let by_element = lang == crate::Lang::Apl;
13986 let n = if by_element {
13987 y.count()
13988 } else if y.rank() == 0 {
13989 1
13990 } else {
13991 y.items()
13992 };
13993 let mut seen: Vec<Array> = Vec::new();
13994 let mut out = Vec::with_capacity(n);
13995 for i in 0..n {
13996 let cell = if by_element {
13997 Array::new(Vec::new(), y.data.slice(i, i + 1))
13998 } else {
13999 item_or_self(y, i)
14000 };
14001 let fresh = !seen.iter().any(|s| arrays_match(s, &cell, tol));
14002 if fresh {
14003 seen.push(cell);
14004 }
14005 out.push(fresh as u8);
14006 }
14007 let shape = if by_element { y.shape.clone() } else { vec![n] };
14008 Array::new(shape, Data::Bool(out.into()))
14009}
14010
14011fn as_list(y: &Array) -> Array {
14013 if y.rank() == 0 { Array::new(vec![1], y.data.clone()) } else { y.clone() }
14014}
14015
14016fn conforming_cells(y: &Array, item_rank: usize) -> Vec<Array> {
14021 let frame_rank = y.rank().saturating_sub(item_rank);
14022 let nf: usize = y.shape[..frame_rank].iter().product();
14023 (0..nf).map(|i| y.cell_at(frame_rank, i)).collect()
14024}
14025
14026fn item_marks(y: &Array, x: &Array, tol: Tol) -> Vec<bool> {
14028 let n = if y.rank() == 0 { 1 } else { y.items() };
14029 let item_rank = y.rank().saturating_sub(1);
14030 let against = conforming_cells(x, item_rank);
14031 (0..n)
14032 .map(|i| {
14033 let cell = item_or_self(y, i);
14034 against.iter().any(|c| arrays_match(&cell, c, tol))
14035 })
14036 .collect()
14037}
14038
14039fn set_less(x: &Array, y: &Array, tol: Tol) -> Array {
14041 let xs = as_list(x);
14042 let marks = item_marks(&xs, y, tol);
14043 let keep: Vec<usize> = (0..marks.len()).filter(|&i| !marks[i]).collect();
14044 select_items(&xs, &keep)
14045}
14046
14047fn set_rank(cfg: EvalCfg, what: &str, x: &Array, y: &Array, span: Span) -> Result<()> {
14051 if cfg.rules.lang == crate::Lang::Apl && (x.rank() > 1 || y.rank() > 1) {
14052 return Err(Error::new(
14053 ErrorKind::Rank,
14054 format!("{what} takes vectors, not rank {} and rank {}", x.rank(), y.rank()),
14055 Some(span),
14056 ));
14057 }
14058 Ok(())
14059}
14060
14061fn intersect_items(x: &Array, y: &Array, tol: Tol) -> Array {
14063 let xs = as_list(x);
14064 let marks = item_marks(&xs, y, tol);
14065 let keep: Vec<usize> = (0..marks.len()).filter(|&i| marks[i]).collect();
14066 select_items(&xs, &keep)
14067}
14068
14069fn union_items(x: &Array, y: &Array, tol: Tol, span: Span) -> Result<Array> {
14072 let xs = as_list(x);
14073 let ys = as_list(y);
14074 let marks = item_marks(&ys, &xs, tol);
14075 let mut extra: Vec<usize> = Vec::new();
14076 for (i, &seen) in marks.iter().enumerate() {
14077 if seen {
14078 continue;
14079 }
14080 let cell = item_or_self(&ys, i);
14081 if !extra.iter().any(|&j| arrays_match(&item_or_self(&ys, j), &cell, tol)) {
14082 extra.push(i);
14083 }
14084 }
14085 catenate(&xs, &select_items(&ys, &extra), true, false, span)
14086}
14087
14088fn find_seq(x: &Array, y: &Array, tol: Tol, apl: bool, span: Span) -> Result<Array> {
14098 let (xr, yr) = (x.rank(), y.rank());
14099 if !apl && xr == 0 && yr == 0 {
14103 let hit = arrays_match(x, y, tol);
14104 return Ok(Array::new(Vec::new(), Data::Bool(vec![u8::from(hit)].into())));
14105 }
14106 if apl && xr > yr {
14107 return Ok(Array::new(y.shape.clone(), Data::Bool(vec![0u8; y.count()].into())));
14109 }
14110 if !apl && xr.max(1) != yr {
14111 return Err(Error::new(
14112 ErrorKind::Rank,
14113 format!("a rank-{xr} pattern in a rank-{yr} argument"),
14114 Some(span),
14115 ));
14116 }
14117 let mut pattern = vec![1usize; yr];
14118 pattern[yr - xr..].copy_from_slice(&x.shape);
14119 let n = y.count();
14120 let mut out = vec![0u8; n];
14121 let (xrm, yrm) = (x.to_row_major(), y.to_row_major());
14122 let yst = strides(&y.shape);
14123 let cells: usize = pattern.iter().product();
14124 let mut at = vec![0usize; yr];
14125 for slot in out.iter_mut() {
14126 if (0..yr).all(|a| at[a] + pattern[a] <= y.shape[a]) {
14127 let mut off = vec![0usize; yr];
14128 let mut hit = true;
14129 for k in 0..cells {
14130 let i: usize = (0..yr).map(|a| (at[a] + off[a]) * yst[a]).sum();
14131 if !arrays_match(&atom(&xrm, k), &atom(&yrm, i), tol) {
14132 hit = false;
14133 break;
14134 }
14135 odometer(&mut off, &pattern);
14136 }
14137 *slot = hit as u8;
14138 }
14139 odometer(&mut at, &y.shape);
14140 }
14141 Ok(Array::new(y.shape.clone(), Data::Bool(out.into())))
14142}
14143
14144fn bool_dyad(op: BoolDyad, x: &Array, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
14148 let bit = |a: &Array| -> Result<u8> {
14149 match a.to_i64_vec().as_deref() {
14150 Some([0]) => Ok(0),
14151 Some([1]) => Ok(1),
14152 _ => Err(Error::domain("this verb reads values of 0 or 1", span)),
14153 }
14154 };
14155 let _ = cfg;
14156 let (a, b) = (bit(x)?, bit(y)?);
14157 let v = match op {
14158 BoolDyad::Nor => u8::from(a == 0 && b == 0),
14159 BoolDyad::Nand => u8::from(a == 0 || b == 0),
14160 };
14161 Ok(Array::new(vec![], Data::Bool(vec![v].into())))
14162}
14163
14164fn item_ranks(y: &Array, rules: Rules, span: Span) -> Result<Vec<usize>> {
14170 check_gradable(y, rules, span)?;
14171 if !y.dtype().is_numeric() {
14172 return Err(Error::domain("an anagram index needs numbers", span));
14173 }
14174 let order = grade_order(&as_list(y), false, Grading::of(rules, rules.tol()));
14175 let mut ranks = vec![0usize; order.len()];
14176 for (place, &i) in order.iter().enumerate() {
14177 ranks[i] = place;
14178 }
14179 Ok(ranks)
14180}
14181
14182fn anagram_index(y: &Array, rules: Rules, span: Span) -> Result<Array> {
14185 let ranks = item_ranks(y, rules, span)?;
14186 let n = ranks.len();
14187 let mut index: i128 = 0;
14188 for i in 0..n {
14189 let smaller = ranks[i + 1..].iter().filter(|&&r| r < ranks[i]).count() as i128;
14190 index = index
14191 .checked_mul((n - i) as i128)
14192 .and_then(|v| v.checked_add(smaller))
14193 .ok_or_else(|| Error::not_yet("an anagram index too large for an integer", span))?;
14194 }
14195 i64::try_from(index)
14196 .map(Array::scalar_i64)
14197 .map_err(|_| Error::not_yet("an anagram index too large for an integer", span))
14198}
14199
14200fn anagram_from(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14203 let ys = as_list(y);
14204 let n = ys.items();
14205 let mut total: i128 = 1;
14206 for k in 1..=n as i128 {
14207 total = total
14208 .checked_mul(k)
14209 .ok_or_else(|| Error::not_yet("permuting more items than an integer counts", span))?;
14210 }
14211 let out_of_range = |want: &dyn std::fmt::Display| {
14217 Error::domain(
14218 format!("permutation {want} is out of range: {n} items have {total} of them"),
14219 span,
14220 )
14221 };
14222 let mut at: i128 = match x.to_i64_vec_near(near) {
14223 Some(v) => {
14224 let want = i128::from(
14225 *v.first().ok_or_else(|| Error::internal("anagram with no index"))?,
14226 );
14227 let at = if want < 0 { want + total } else { want };
14228 if at < 0 || at >= total {
14229 return Err(out_of_range(&want));
14230 }
14231 at
14232 }
14233 None => {
14234 if n != 0 {
14235 return Err(Error::domain("an anagram index must be an integer", span));
14236 }
14237 let want = *x
14238 .to_f64_vec()
14239 .as_deref()
14240 .and_then(<[f64]>::first)
14241 .ok_or_else(|| Error::domain("an anagram index must be an integer", span))?;
14242 let at = if want < 0.0 { want + total as f64 } else { want };
14243 if !(0.0..total as f64).contains(&at) {
14244 return Err(out_of_range(&want));
14245 }
14246 at as i128
14247 }
14248 };
14249 let mut pool: Vec<usize> = (0..n).collect();
14252 let mut order = Vec::with_capacity(n);
14253 let mut fact = total;
14254 for i in 0..n {
14255 fact /= (n - i) as i128;
14256 let d = (at / fact) as usize;
14257 at %= fact;
14258 order.push(pool.remove(d));
14259 }
14260 Ok(select_items(&ys, &order))
14261}
14262
14263fn cycle_form(y: &Array, near: NearInt, span: Span) -> Result<Array> {
14269 if y.dtype() == DType::Box {
14270 let perm = cycles_to_direct(y, None, near, span)?;
14271 return Ok(Array::from_i64(perm.iter().map(|&i| i as i64).collect()));
14272 }
14273 let n = permutation_span(y, near, span)?;
14274 let perm = direct_permutation_of(y, n, near, span)?;
14275 let mut boxes: Vec<Array> = Vec::new();
14276 let mut done = vec![false; perm.len()];
14277 for start in 0..perm.len() {
14278 if done[start] {
14279 continue;
14280 }
14281 let mut cycle = Vec::new();
14282 let mut at = start;
14283 while !done[at] {
14284 done[at] = true;
14285 cycle.push(at);
14286 at = perm[at];
14287 }
14288 let top = cycle.iter().position(|&v| v == *cycle.iter().max().unwrap()).unwrap();
14291 cycle.rotate_left(top);
14292 boxes.push(Array::boxed(Array::from_i64(
14293 cycle.iter().map(|&i| i as i64).collect(),
14294 )));
14295 }
14296 boxes.sort_by_key(|b| b.as_boxes().map(|s| s[0].to_i64_vec().unwrap()[0]).unwrap_or(0));
14297 let n = boxes.len();
14298 let inner: Vec<Array> =
14299 boxes.into_iter().map(|b| b.as_boxes().unwrap()[0].clone()).collect();
14300 Ok(Array::new(vec![n], Data::Box(inner.into())))
14301}
14302
14303fn direct_permutation_of(y: &Array, n: usize, near: NearInt, span: Span) -> Result<Vec<usize>> {
14312 let v = y
14313 .to_i64_vec_near(near)
14314 .ok_or_else(|| Error::domain("a permutation is a list of integers", span))?;
14315 let mut seen = vec![false; n];
14316 let mut tail = Vec::with_capacity(v.len());
14317 for &i in &v {
14318 let k = usize::try_from(i).ok().filter(|&k| k < n && !seen[k]).ok_or_else(|| {
14319 Error::domain(format!("{i} does not belong to a permutation of {n} items"), span)
14320 })?;
14321 seen[k] = true;
14322 tail.push(k);
14323 }
14324 let mut out: Vec<usize> = (0..n).filter(|&k| !seen[k]).collect();
14325 out.append(&mut tail);
14326 Ok(out)
14327}
14328
14329fn permutation_span(y: &Array, near: NearInt, span: Span) -> Result<usize> {
14332 let v = y
14333 .to_i64_vec_near(near)
14334 .ok_or_else(|| Error::domain("a permutation is a list of integers", span))?;
14335 let top = v.iter().copied().max().unwrap_or(-1).saturating_add(1).max(0) as u128;
14336 Ok(crate::limits::count(top, span)?.max(v.len()))
14337}
14338
14339fn cycles_to_direct(
14351 y: &Array,
14352 within: Option<usize>,
14353 near: NearInt,
14354 span: Span,
14355) -> Result<Vec<usize>> {
14356 let boxes = y.as_boxes().ok_or_else(|| Error::internal("cycles from a simple array"))?;
14357 let mut cycles: Vec<Vec<usize>> = Vec::new();
14358 let mut top = 0usize;
14359 for b in boxes {
14360 let v = b
14361 .to_i64_vec_near(near)
14362 .ok_or_else(|| Error::domain("a cycle is a list of integers", span))?;
14363 let mut cycle = Vec::with_capacity(v.len());
14364 for &i in &v {
14365 let k = match within {
14366 Some(n) => {
14367 let at = if i < 0 { i.checked_add(n as i64) } else { Some(i) };
14368 usize::try_from(at.unwrap_or(-1))
14369 .ok()
14370 .filter(|&k| k < n)
14371 .ok_or_else(|| {
14372 Error::domain(
14373 format!("{i} is not an index into {n} item(s)"),
14374 span,
14375 )
14376 })?
14377 }
14378 None => {
14379 let k = usize::try_from(i)
14380 .map_err(|_| Error::domain(format!("{i} is not an index"), span))?;
14381 crate::limits::count(k as u128 + 1, span)?;
14382 k
14383 }
14384 };
14385 top = top.max(k + 1);
14386 cycle.push(k);
14387 }
14388 cycles.push(cycle);
14389 }
14390 let mut perm: Vec<usize> = (0..top).collect();
14391 for cycle in &cycles {
14392 for w in 0..cycle.len() {
14393 perm[cycle[w]] = cycle[(w + 1) % cycle.len()];
14395 }
14396 }
14397 Ok(perm)
14398}
14399
14400fn permute(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14406 let ys = as_list(y);
14407 let n = ys.items();
14408 if x.dtype() != DType::Box {
14409 let perm = direct_permutation_of(&as_list(x), n, near, span)?;
14410 return Ok(select_items(&ys, &perm));
14411 }
14412 let mut perm = cycles_to_direct(x, Some(n), near, span)?;
14413 perm.extend(perm.len()..n);
14415 Ok(select_items(&ys, &perm))
14416}
14417
14418fn unicode(y: &Array, pass_chars: bool, near: NearInt, span: Span) -> Result<Array> {
14424 if y.dtype() == DType::Char {
14425 if pass_chars {
14426 return Ok(y.clone());
14427 }
14428 return Ok(chars_to_codes(y));
14429 }
14430 codes_to_chars(y, near, span)
14431}
14432
14433fn chars_to_codes(y: &Array) -> Array {
14434 let Data::Char(v) = &y.data else { return y.clone() };
14435 Array::new(y.shape.clone(), Data::I64(v.iter().map(|&c| c as i64).collect()))
14436}
14437
14438fn codes_to_chars(y: &Array, near: NearInt, span: Span) -> Result<Array> {
14439 let v = y
14440 .to_i64_vec_near(near)
14441 .ok_or_else(|| Error::domain("a codepoint must be an integer", span))?;
14442 let mut out = Vec::with_capacity(v.len());
14443 for &c in &v {
14444 let ch = u32::try_from(c).ok().and_then(char::from_u32).ok_or_else(|| {
14445 Error::domain(format!("{c} is not a Unicode codepoint"), span)
14446 })?;
14447 out.push(ch);
14448 }
14449 Ok(Array::new(y.shape.clone(), Data::Char(out.into())))
14450}
14451
14452fn unicode_form(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14455 let form = x
14456 .to_i64_vec()
14457 .ok_or_else(|| Error::domain("a conversion form is an integer", span))?
14458 .first()
14459 .copied()
14460 .unwrap_or(0);
14461 match form {
14462 3 if y.dtype() == DType::Char => Ok(chars_to_codes(y)),
14463 3 => Err(Error::domain("form 3 converts characters to codepoints", span)),
14464 10 => codes_to_chars(y, near, span),
14465 n => Err(Error::not_yet(format!("the byte-oriented unicode form ({n} u:)"), span)),
14466 }
14467}
14468
14469fn to_symbols(y: &Array, span: Span) -> Result<Array> {
14480 if let Some(boxes) = y.as_boxes() {
14481 let mut ids = Vec::with_capacity(boxes.len());
14482 for b in boxes {
14483 if b.rank() > 1 {
14484 return Err(Error::new(
14485 ErrorKind::Rank,
14486 "a boxed symbol name is a character list",
14487 Some(span),
14488 ));
14489 }
14490 let row_major = b.to_row_major();
14491 let Data::Char(v) = &row_major.data else {
14492 if b.count() == 0 {
14493 ids.push(crate::symbol::EMPTY);
14494 continue;
14495 }
14496 return Err(Error::domain("a symbol is made from characters", span));
14497 };
14498 ids.push(crate::symbol::intern(&v.as_slice().iter().collect::<String>()));
14499 }
14500 return Ok(Array::new(y.shape.clone(), Data::Symbol(ids.into())));
14501 }
14502 let row_major = y.to_row_major();
14503 let Data::Char(v) = &row_major.data else {
14504 return Err(Error::domain(
14505 format!("s: makes symbols from characters, not {} data", y.dtype().name()),
14506 span,
14507 ));
14508 };
14509 let chars = v.as_slice();
14510 if y.rank() >= 2 {
14511 let width = y.shape[y.rank() - 1];
14512 let mut ids = Vec::with_capacity(chars.len() / width.max(1));
14513 for row in chars.chunks(width) {
14514 let name: String = row.iter().collect();
14515 ids.push(crate::symbol::intern(name.trim_end_matches(' ')));
14516 }
14517 return Ok(Array::new(y.shape[..y.rank() - 1].to_vec(), Data::Symbol(ids.into())));
14518 }
14519 let Some((&delim, rest)) = chars.split_first() else {
14520 return Ok(Array::new(vec![0], Data::empty(DType::Symbol)));
14521 };
14522 let mut ids = Vec::new();
14523 let mut name = String::new();
14524 for &c in rest {
14525 if c == delim {
14526 ids.push(crate::symbol::intern(&name));
14527 name.clear();
14528 } else {
14529 name.push(c);
14530 }
14531 }
14532 ids.push(crate::symbol::intern(&name));
14533 Ok(Array::new(vec![ids.len()], Data::Symbol(ids.into())))
14534}
14535
14536fn symbol_form(x: &Array, y: &Array, span: Span) -> Result<Array> {
14542 let form = x
14543 .to_i64_vec()
14544 .ok_or_else(|| Error::domain("a symbol form is an integer", span))?
14545 .first()
14546 .copied()
14547 .unwrap_or(0);
14548 if !matches!(form, 4 | 5) {
14549 return Err(Error::not_yet(format!("the symbol-table form ({form} s:)"), span));
14550 }
14551 let row_major = y.to_row_major();
14552 let Data::Symbol(ids) = &row_major.data else {
14553 return Err(Error::domain(
14554 format!("{form} s: reads symbols, not {} data", y.dtype().name()),
14555 span,
14556 ));
14557 };
14558 let names = crate::symbol::names(ids.as_slice());
14559 if form == 5 {
14560 let boxes: Vec<Array> =
14561 names.iter().map(|n| Array::from_chars(n.chars().collect())).collect();
14562 return Ok(Array::new(y.shape.clone(), Data::Box(boxes.into())));
14563 }
14564 let width = names.iter().map(|n| n.chars().count()).max().unwrap_or(0);
14565 let mut out: Vec<char> = Vec::with_capacity(names.len() * width);
14566 for n in &names {
14567 out.extend(n.chars());
14568 out.resize(out.len() + width - n.chars().count(), ' ');
14569 }
14570 let mut shape = y.shape.clone();
14571 shape.push(width);
14572 Ok(Array::new(shape, Data::Char(out.into())))
14573}
14574
14575fn sparse_form(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14586 if x.rank() != 0 {
14587 return Err(Error::new(ErrorKind::Rank, "a sparse form is one atom", Some(span)));
14588 }
14589 let form = x
14590 .to_i64_vec_near(near)
14591 .and_then(|v| v.first().copied())
14592 .ok_or_else(|| Error::domain("a sparse form is an integer", span))?;
14593 match form {
14594 0 if y.is_sparse() => return Ok(y.densified()),
14595 0 => return crate::sparse::sparsify(y, span),
14596 1 => return crate::sparse::create(y, span),
14597 2 => {
14598 let axes: Vec<i64> = match y.sparse_parts() {
14599 Some(s) => s.axes.iter().map(|&k| k as i64).collect(),
14600 None => (0..y.rank() as i64).collect(),
14601 };
14602 return Ok(Array::from_i64(axes));
14603 }
14604 _ => {}
14605 }
14606 let Some(s) = y.sparse_parts() else {
14607 return Err(Error::domain(
14608 format!("{form} $. reads a sparse array, and this one is dense"),
14609 span,
14610 ));
14611 };
14612 match form {
14613 -1 => Ok(crate::sparse::attributes(y, s)),
14614 3 => Ok(crate::sparse::fill_of(s)),
14615 4 => Ok(crate::sparse::indices_of(s)),
14616 5 => Ok(crate::sparse::values_of(y, s)),
14617 7 => Ok(Array::scalar_i64(s.entries as i64)),
14618 8 => Ok(crate::sparse::compress(y, s)),
14619 _ => Err(Error::domain(format!("{form} is not a sparse form"), span)),
14620 }
14621}
14622
14623fn boxing_level(y: &Array) -> i64 {
14625 match y.as_boxes() {
14626 None => 0,
14627 Some(bs) => 1 + bs.iter().map(boxing_level).max().unwrap_or(0),
14628 }
14629}
14630
14631fn split_items(y: &Array) -> Array {
14635 if y.rank() == 0 {
14636 return Array::boxed(y.clone());
14637 }
14638 let last = y.shape[y.rank() - 1];
14639 let outer: Vec<usize> = y.shape[..y.rank() - 1].to_vec();
14640 let n: usize = outer.iter().product();
14641 let mut boxes = Vec::with_capacity(n);
14642 for i in 0..n {
14643 let mut data = Data::empty(y.dtype());
14644 for k in 0..last {
14645 push_elem(&mut data, &y.data, i * last + k);
14646 }
14647 boxes.push(Array::new(vec![last], data));
14648 }
14649 Array::new(outer, Data::Box(boxes.into()))
14650}
14651
14652fn pick(x: &Array, y: &Array, origin: i64, near: NearInt, span: Span) -> Result<Array> {
14655 let xs = as_list(x);
14656 let mut cur = y.clone();
14657 for i in 0..xs.items() {
14658 let step = open_cell(&item_or_self(&xs, i));
14659 let idx = step
14660 .to_i64_vec_near(near)
14661 .ok_or_else(|| Error::domain("a pick path holds integers", span))?;
14662 let base =
14663 if cur.rank() == 0 { Array::new(vec![1], cur.data.clone()) } else { cur.clone() };
14664 if idx.len() > base.rank() {
14665 return Err(Error::new(
14666 ErrorKind::Length,
14667 format!(
14668 "a path step of {} index(es) into a value of rank {}",
14669 idx.len(),
14670 cur.rank()
14671 ),
14672 Some(span),
14673 ));
14674 }
14675 let zeroed: Vec<i64> = idx.iter().map(|&v| v - origin).collect();
14676 let at = cell_index(&base, &zeroed, span)?;
14677 cur = open_cell(&base.cell_at(idx.len(), at));
14678 }
14679 Ok(cur)
14680}
14681
14682fn prime_meta(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14687 let form = one_int(x, "a prime query", near, span)?;
14688 let n = one_int(y, "a prime query", near, span)?;
14689 match form {
14690 -1 => Ok(Array::scalar_i64(primes_below(n, span)?)),
14692 0 => Ok(Array::scalar_bool(!is_prime(n))),
14694 1 => Ok(Array::scalar_bool(is_prime(n))),
14695 2 | 3 => {
14697 let (ps, es) = factor_table(n, span)?;
14698 let k = ps.len();
14699 if form == 3 {
14700 return Ok(Array::from_i64(ps));
14701 }
14702 let mut all = ps;
14703 all.extend(es);
14704 Ok(Array::new(vec![2, k], Data::I64(all.into())))
14705 }
14706 4 => Ok(Array::scalar_i64(next_prime(n, span)?)),
14708 -4 => Ok(Array::scalar_i64(previous_prime(n, span)?)),
14709 other => Err(Error::domain(format!("{other} is not a prime query"), span)),
14710 }
14711}
14712
14713fn prime_exponents(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14716 let n = one_int(y, "prime exponents", near, span)?;
14717 let count = x.to_f64_vec().and_then(|v| v.first().copied()).unwrap_or(0.0);
14718 let (ps, es) = factor_table(n, span)?;
14719 if count == f64::NEG_INFINITY {
14720 let k = ps.len();
14721 let mut all = ps;
14722 all.extend(es);
14723 return Ok(Array::new(vec![2, k], Data::I64(all.into())));
14724 }
14725 let want = one_int(x, "prime exponents", near, span)?;
14726 if want < 0 {
14727 return Err(Error::not_yet(format!("the prime exponent form ({want} q:)"), span));
14728 }
14729 let mut out = Vec::with_capacity(want as usize);
14730 for i in 0..want {
14731 let p = nth_prime(i, span)?;
14732 out.push(ps.iter().position(|&q| q == p).map_or(0, |at| es[at]));
14733 }
14734 Ok(Array::from_i64(out))
14735}
14736
14737fn factor_table(n: i64, span: Span) -> Result<(Vec<i64>, Vec<i64>)> {
14739 let factors = prime_factors(n, span)?;
14740 let mut ps: Vec<i64> = Vec::new();
14741 let mut es: Vec<i64> = Vec::new();
14742 for f in factors {
14743 if ps.last() == Some(&f) {
14744 *es.last_mut().unwrap() += 1;
14745 } else {
14746 ps.push(f);
14747 es.push(1);
14748 }
14749 }
14750 Ok((ps, es))
14751}
14752
14753fn is_prime(n: i64) -> bool {
14754 if n < 2 {
14755 return false;
14756 }
14757 let mut d = 2i64;
14758 while d.saturating_mul(d) <= n {
14759 if n % d == 0 {
14760 return false;
14761 }
14762 d += 1;
14763 }
14764 true
14765}
14766
14767fn primes_below(n: i64, span: Span) -> Result<i64> {
14768 if n < 0 {
14769 return Err(Error::domain("counting the primes below a negative number", span));
14770 }
14771 Ok((2..n).filter(|&k| is_prime(k)).count() as i64)
14772}
14773
14774fn next_prime(n: i64, span: Span) -> Result<i64> {
14775 let mut k = n.checked_add(1).ok_or_else(|| Error::domain("no next prime", span))?;
14776 while !is_prime(k) {
14777 k = k.checked_add(1).ok_or_else(|| Error::domain("no next prime", span))?;
14778 }
14779 Ok(k)
14780}
14781
14782fn previous_prime(n: i64, span: Span) -> Result<i64> {
14783 let mut k = n - 1;
14784 while k >= 2 {
14785 if is_prime(k) {
14786 return Ok(k);
14787 }
14788 k -= 1;
14789 }
14790 Err(Error::domain(format!("there is no prime below {n}"), span))
14791}
14792
14793fn one_int(a: &Array, what: &str, near: NearInt, span: Span) -> Result<i64> {
14795 a.to_i64_vec_near(near)
14796 .and_then(|v| v.first().copied())
14797 .ok_or_else(|| Error::domain(format!("{what} needs an integer"), span))
14798}
14799
14800fn expand(x: &Array, y: &Array, apl: bool, near: NearInt, span: Span) -> Result<Array> {
14804 let mask = x
14805 .to_i64_vec_near(near)
14806 .ok_or_else(|| Error::domain("an expansion mask holds 0s and 1s", span))?;
14807 if mask.iter().any(|&b| b != 0 && b != 1) {
14808 return Err(Error::domain("an expansion mask holds 0s and 1s", span));
14809 }
14810 let ys = as_list(y);
14811 let taken = mask.iter().filter(|&&b| b == 1).count();
14812 let n = ys.items();
14813 let spread = n == 1 && taken != 1;
14815 if !spread && taken != n {
14816 return Err(Error::new(
14817 ErrorKind::Length,
14818 format!("an expansion mask taking {taken} item(s) over {n}"),
14819 Some(span),
14820 ));
14821 }
14822 let m = ys.item_size();
14823 let fill = if apl { prototype_of(&ys) } else { None };
14824 let mut data = Data::empty(ys.dtype());
14825 let mut at = 0usize;
14826 for &b in &mask {
14827 if b == 1 {
14828 let from = if spread { 0 } else { at };
14829 for k in 0..m {
14830 push_elem(&mut data, &ys.data, from * m + k);
14831 }
14832 at += 1;
14833 } else {
14834 for _ in 0..m {
14835 push_gap(&mut data, &fill);
14836 }
14837 }
14838 }
14839 let mut shape = ys.shape.clone();
14840 if shape.is_empty() {
14841 shape.push(mask.len());
14842 } else {
14843 shape[0] = mask.len();
14844 }
14845 Ok(keep_proto(Array::new(shape, data), &ys, apl))
14846}
14847
14848fn execute(y: &Array, apl: bool, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
14856 if y.count() == 0 {
14861 if apl {
14862 return execute_source("", apl, ctx, span);
14863 }
14864 return Ok(Array::new(y.shape.clone(), Data::empty(DType::Bool)));
14865 }
14866 let Data::Char(v) = &y.data else {
14867 return Err(Error::domain("execute reads a character list", span));
14868 };
14869 let src: String = v.iter().collect();
14870 execute_source(&src, apl, ctx, span)
14871}
14872
14873pub(crate) fn execute_source(
14876 src: &str,
14877 apl: bool,
14878 ctx: &mut Ctx<'_>,
14879 span: Span,
14880) -> Result<Array> {
14881 let lang = if apl { crate::Lang::Apl } else { crate::Lang::J };
14882 let dialect = ctx.cfg.rules.dialect();
14885 let nested = crate::compile(lang, src, &dialect).map_err(|e| nested_error(e, src, span))?;
14886 if !nested.params.is_empty() {
14887 return Err(Error::domain(
14888 "an executed string cannot take host data: `{name}` has nothing to bind to",
14889 span,
14890 ));
14891 }
14892 let mut rec = None;
14893 let (value, _) = crate::ir::run_block(&nested.stmts, None, ctx, &mut rec)
14894 .map_err(|e| nested_error(e, src, span))?;
14895 value.ok_or_else(|| Error::domain("the executed string yielded no value", span))
14896}
14897
14898fn stream_number(y: &Array, open: i64, what: &str, span: Span) -> Result<()> {
14905 let closed = || {
14906 Err(Error::sandbox(
14907 format!("{what} the standard stream {open} only; a file is outside the program"),
14908 span,
14909 ))
14910 };
14911 if matches!(y.data, Data::Box(_)) {
14912 return closed();
14913 }
14914 match y.to_i64_vec().as_deref() {
14915 Some([n]) if *n == open => Ok(()),
14916 Some([_]) => closed(),
14917 _ => Err(Error::domain(format!("{what} one stream number"), span)),
14918 }
14919}
14920
14921fn type_code(y: &Array) -> i64 {
14927 if y.is_sparse() {
14928 return 1024 * dense_type_code(y);
14929 }
14930 dense_type_code(y)
14931}
14932
14933fn dense_type_code(y: &Array) -> i64 {
14934 match y.dtype() {
14935 DType::Bool => 1,
14936 DType::Char => 2,
14937 DType::I64 => 4,
14938 DType::F64 => 8,
14939 DType::Complex => 16,
14940 DType::Box => 32,
14941 DType::Ext => 64,
14942 DType::Rat => 128,
14943 DType::Symbol => 65536,
14944 }
14945}
14946
14947fn nested_error(e: Error, src: &str, span: Span) -> Error {
14951 let inner = e.render(src);
14952 let mut out = Error::new(e.kind, format!("in the executed string: {}", e.msg), Some(span));
14953 out.notes.push(inner.trim_end().to_string());
14954 out
14955}
14956
14957fn words(y: &Array, span: Span) -> Result<Array> {
14963 if y.count() == 0 {
14966 return Ok(Array::new(vec![0], Data::Box(Vec::new().into())));
14967 }
14968 let Data::Char(v) = &y.data else {
14969 return Err(Error::domain("words reads a character list", span));
14970 };
14971 let src: Vec<char> = v.as_slice().to_vec();
14972 let n = src.len();
14973 let mut out: Vec<Array> = Vec::new();
14974 let mut i = 0usize;
14975 let numeric_start = |k: usize| -> bool {
14976 k < n && (src[k].is_ascii_digit() || src[k] == '_')
14977 };
14978 while i < n {
14979 let c = src[i];
14980 if c == ' ' || c == '\t' {
14981 i += 1;
14982 continue;
14983 }
14984 let start = i;
14985 if c == '\'' {
14986 i += 1;
14987 loop {
14988 if i >= n {
14989 return Err(Error::parse("a word list ends inside a string", span));
14990 }
14991 if src[i] == '\'' {
14992 i += 1;
14993 if i < n && src[i] == '\'' {
14994 i += 1;
14995 continue;
14996 }
14997 break;
14998 }
14999 i += 1;
15000 }
15001 } else if c.is_ascii_alphabetic() {
15002 while i < n && (src[i].is_ascii_alphanumeric() || src[i] == '_') {
15003 i += 1;
15004 }
15005 if i < n && (src[i] == '.' || src[i] == ':') {
15006 i += 1;
15007 }
15008 if src[start..i].iter().collect::<String>() == "NB." {
15010 while i < n && src[i] != '\n' {
15011 i += 1;
15012 }
15013 }
15014 } else if numeric_start(i) {
15015 loop {
15016 while i < n && (src[i].is_ascii_alphanumeric() || src[i] == '.' || src[i] == '_')
15017 {
15018 i += 1;
15019 }
15020 let mut j = i;
15022 while j < n && src[j] == ' ' {
15023 j += 1;
15024 }
15025 if j > i && numeric_start(j) {
15026 i = j;
15027 continue;
15028 }
15029 break;
15030 }
15031 } else {
15032 i += 1;
15033 while i < n && (src[i] == '.' || src[i] == ':') {
15034 i += 1;
15035 }
15036 }
15037 out.push(Array::from_chars(src[start..i].to_vec()));
15038 }
15039 let k = out.len();
15040 Ok(Array::new(vec![k], Data::Box(out.into())))
15041}
15042
15043#[cfg(test)]
15044mod tests {
15045 use super::*;
15046
15047 macro_rules! ctx {
15049 ($name:ident, $agreement:expr) => {
15050 let mut sink = |_: &str| {};
15051 let mut env = Env::new(Vec::new());
15052 #[allow(unused_mut)]
15053 let mut $name = Ctx {
15054 cfg: EvalCfg {
15055 agreement: $agreement,
15056 fmt: FmtOpts::J,
15057 tol: Tol::J,
15058 rules: crate::frontend::Dialect::default()
15061 .rules(if $agreement == Agreement::ExactOrScalar {
15062 crate::Lang::Apl
15063 } else {
15064 crate::Lang::J
15065 })
15066 .expect("the shipped dialect is implemented"),
15067 },
15068 out: &mut sink,
15069 inp: None,
15070 env: &mut env,
15071 device: None,
15072 };
15073 };
15074 ($name:ident) => {
15075 ctx!($name, Agreement::LeadingPrefix);
15076 };
15077 }
15078
15079 fn scalar_prim(name: &'static str, monad: MonadOp, dyad: DyadOp) -> Verb {
15080 Verb::Prim(Prim { name, monad, dyad, ranks: [0, 0, 0] })
15081 }
15082
15083 fn inf_prim(name: &'static str, monad: MonadOp, dyad: DyadOp) -> Verb {
15084 Verb::Prim(Prim { name, monad, dyad, ranks: [RANK_INF, RANK_INF, RANK_INF] })
15085 }
15086
15087 fn plus() -> Verb {
15088 scalar_prim("+", MonadOp::Scalar(ScalarMonad::Conj), DyadOp::Scalar(ScalarDyad::Add))
15089 }
15090 fn minus() -> Verb {
15091 scalar_prim("-", MonadOp::Scalar(ScalarMonad::Neg), DyadOp::Scalar(ScalarDyad::Sub))
15092 }
15093 fn times() -> Verb {
15094 scalar_prim("*", MonadOp::Scalar(ScalarMonad::Signum), DyadOp::Scalar(ScalarDyad::Mul))
15095 }
15096 fn pct() -> Verb {
15097 scalar_prim("%", MonadOp::Scalar(ScalarMonad::Recip), DyadOp::Scalar(ScalarDyad::DivJ))
15098 }
15099 fn div_apl() -> Verb {
15100 scalar_prim("÷", MonadOp::Scalar(ScalarMonad::Recip), DyadOp::Scalar(ScalarDyad::DivApl))
15101 }
15102 fn floor_v() -> Verb {
15103 scalar_prim("<.", MonadOp::Scalar(ScalarMonad::Floor), DyadOp::Scalar(ScalarDyad::Min))
15104 }
15105 fn ceil_v() -> Verb {
15106 scalar_prim(">.", MonadOp::Scalar(ScalarMonad::Ceil), DyadOp::Scalar(ScalarDyad::Max))
15107 }
15108 fn pow_v() -> Verb {
15109 scalar_prim("^", MonadOp::Scalar(ScalarMonad::Exp), DyadOp::Scalar(ScalarDyad::Pow))
15110 }
15111 fn residue_v() -> Verb {
15112 scalar_prim("|", MonadOp::Scalar(ScalarMonad::Abs), DyadOp::Scalar(ScalarDyad::Residue))
15113 }
15114 fn eq_v() -> Verb {
15115 scalar_prim("=", MonadOp::None, DyadOp::Scalar(ScalarDyad::Eq))
15116 }
15117 fn lt_v() -> Verb {
15118 scalar_prim("<", MonadOp::None, DyadOp::Scalar(ScalarDyad::Lt))
15119 }
15120 fn not_v() -> Verb {
15121 scalar_prim("-.", MonadOp::Scalar(ScalarMonad::Not), DyadOp::None)
15122 }
15123 fn sqrt_v() -> Verb {
15124 scalar_prim("%:", MonadOp::Scalar(ScalarMonad::Sqrt), DyadOp::NotYet("dyadic root"))
15125 }
15126 fn dollar() -> Verb {
15127 inf_prim("$", MonadOp::ShapeOf, DyadOp::Reshape)
15128 }
15129 fn pound() -> Verb {
15130 inf_prim("#", MonadOp::Tally, DyadOp::NotYet("copy"))
15131 }
15132 fn comma() -> Verb {
15133 inf_prim(",", MonadOp::Ravel, DyadOp::NotYet("append"))
15134 }
15135 fn transpose_v() -> Verb {
15136 inf_prim("|:", MonadOp::TransposeAxes, DyadOp::NotYet("dyadic transpose"))
15137 }
15138 fn head_v() -> Verb {
15139 inf_prim("{.", MonadOp::Head, DyadOp::Take)
15140 }
15141 fn behead_v() -> Verb {
15142 inf_prim("}.", MonadOp::Behead, DyadOp::Drop)
15143 }
15144 fn iota() -> Verb {
15145 inf_prim("i.", MonadOp::IotaJ, DyadOp::NotYet("index of"))
15146 }
15147 fn iota_apl(origin: i64) -> Verb {
15148 inf_prim("⍳", MonadOp::IotaApl { origin }, DyadOp::NotYet("index of"))
15149 }
15150 fn right_v() -> Verb {
15151 inf_prim("]", MonadOp::Same, DyadOp::Right)
15152 }
15153 fn echo_v() -> Verb {
15154 inf_prim("echo", MonadOp::Echo, DyadOp::None)
15155 }
15156
15157 fn b(v: Verb) -> Box<Verb> {
15158 Box::new(v)
15159 }
15160
15161 fn mat(rows: usize, cols: usize, v: Vec<i64>) -> Array {
15162 Array::new(vec![rows, cols], Data::I64(v.into()))
15163 }
15164
15165 fn ints(a: &Array) -> Vec<i64> {
15167 a.to_row_major().as_i64_slice().expect("integer result").to_vec()
15168 }
15169
15170 fn floats(a: &Array) -> Vec<f64> {
15171 a.to_row_major().as_f64_slice().expect("float result").to_vec()
15172 }
15173
15174 fn bools(a: &Array) -> Vec<u8> {
15175 match &a.to_row_major().data {
15176 Data::Bool(v) => v.to_vec(),
15177 other => panic!("expected boolean result, got {other:?}"),
15178 }
15179 }
15180
15181 fn sp() -> Span {
15182 Span::new(0, 1)
15183 }
15184
15185 fn close(a: f64, b: f64) -> bool {
15186 (a - b).abs() < 1e-9 || (a.is_infinite() && b.is_infinite() && a.signum() == b.signum())
15187 }
15188
15189 #[test]
15192 fn names_of_primitives_and_derived_verbs() {
15193 assert_eq!(plus().name(), "+");
15194 assert_eq!(Verb::Rank(b(plus()), [1, 1, 1]).name(), "+\"1");
15195 assert_eq!(Verb::Rank(b(plus()), [0, 1, RANK_INF]).name(), "+\"0 1 _");
15196 assert_eq!(Verb::Rank(b(plus()), [RANK_INF; 3]).name(), "+\"_");
15197 assert_eq!(Verb::Reduce(b(plus())).name(), "+/");
15198 assert_eq!(Verb::Rank(b(Verb::Reduce(b(plus()))), [1, 1, 1]).name(), "+/\"1");
15199 assert_eq!(Verb::Fork(b(plus()), b(minus()), b(times())).name(), "(+ - *)");
15200 assert_eq!(
15201 Verb::NounFork(Array::scalar_i64(1), b(plus()), b(minus())).name(),
15202 "(n + -)"
15203 );
15204 assert_eq!(Verb::Hook(b(plus()), b(minus())).name(), "(+ -)");
15205 assert_eq!(Verb::Atop(b(plus()), b(minus())).name(), "(+@:-)");
15206 assert_eq!(Verb::Compose(b(plus()), b(minus())).name(), "(+&:-)");
15207 assert_eq!(Verb::BondLeft(Array::scalar_i64(1), b(plus())).name(), "(n&+)");
15208 assert_eq!(Verb::BondRight(b(plus()), Array::scalar_i64(1)).name(), "(+&n)");
15209 }
15210
15211 #[test]
15212 fn composition_applies_the_right_verb_to_both_arguments() {
15213 ctx!(c);
15214 let v = Verb::Compose(b(plus()), b(times()));
15215 let r = v.monad(&Array::from_i64(vec![-2, 0, 3]), &mut c, sp()).unwrap();
15217 assert_eq!(ints(&r), vec![-1, 0, 1]);
15218 let r = v
15219 .dyad(&Array::scalar_i64(-5), &Array::scalar_i64(7), &mut c, sp())
15220 .unwrap();
15221 assert_eq!(ints(&r), vec![0]);
15222 let bond = Verb::BondLeft(Array::scalar_i64(10), b(minus()));
15224 let r = bond.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15225 assert_eq!(ints(&r), vec![9, 8]);
15226 let e = bond
15227 .dyad(&Array::scalar_i64(1), &Array::scalar_i64(2), &mut c, sp())
15228 .unwrap_err();
15229 assert_eq!(e.kind, ErrorKind::Domain);
15230 let bond = Verb::BondRight(b(minus()), Array::scalar_i64(10));
15231 let r = bond.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15232 assert_eq!(ints(&r), vec![-9, -8]);
15233 }
15234
15235 #[test]
15238 fn scalar_monad_covers_the_whole_buffer() {
15239 ctx!(c);
15240 let r = minus().monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp()).unwrap();
15241 assert_eq!(r.shape, vec![2, 3]);
15242 assert_eq!(ints(&r), vec![-1, -2, -3, -4, -5, -6]);
15243 }
15244
15245 #[test]
15246 fn leading_prefix_agreement_broadcasts_per_row() {
15247 ctx!(c);
15248 let x = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15249 let y = Array::from_i64(vec![10, 20]);
15250 let r = plus().dyad(&x, &y, &mut c, sp()).unwrap();
15251 assert_eq!(r.shape, vec![2, 3]);
15252 assert_eq!(ints(&r), vec![11, 12, 13, 24, 25, 26]);
15253 let r = plus().dyad(&y, &x, &mut c, sp()).unwrap();
15255 assert_eq!(ints(&r), vec![11, 12, 13, 24, 25, 26]);
15256 }
15257
15258 #[test]
15259 fn exact_or_scalar_rejects_a_prefix_frame() {
15260 ctx!(c, Agreement::ExactOrScalar);
15261 let x = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15262 let y = Array::from_i64(vec![10, 20]);
15263 let e = plus().dyad(&x, &y, &mut c, sp()).unwrap_err();
15264 assert_eq!(e.kind, ErrorKind::Shape);
15265 assert!(e.msg.contains("2 3"), "{}", e.msg);
15266 assert!(e.msg.contains("right shape 2"), "{}", e.msg);
15267 }
15268
15269 #[test]
15270 fn exact_or_scalar_accepts_equal_frames_and_scalars() {
15271 ctx!(c, Agreement::ExactOrScalar);
15272 let x = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15273 let r = plus().dyad(&x, &x, &mut c, sp()).unwrap();
15274 assert_eq!(ints(&r), vec![2, 4, 6, 8, 10, 12]);
15275 let r = plus().dyad(&Array::scalar_i64(10), &x, &mut c, sp()).unwrap();
15276 assert_eq!(r.shape, vec![2, 3]);
15277 assert_eq!(ints(&r), vec![11, 12, 13, 14, 15, 16]);
15278 let r = plus().dyad(&x, &Array::scalar_i64(10), &mut c, sp()).unwrap();
15279 assert_eq!(ints(&r), vec![11, 12, 13, 14, 15, 16]);
15280 }
15281
15282 #[test]
15283 fn vector_length_mismatch_is_a_length_error() {
15284 ctx!(c);
15285 let e = plus()
15286 .dyad(&Array::from_i64(vec![1, 2, 3]), &Array::from_i64(vec![1, 2, 3, 4, 5]), &mut c, sp())
15287 .unwrap_err();
15288 assert_eq!(e.kind, ErrorKind::Length);
15289 assert!(e.msg.contains("left shape 3"), "{}", e.msg);
15290 assert!(e.msg.contains("right shape 5"), "{}", e.msg);
15291 assert!(e.notes[0].contains("axis 0"), "{:?}", e.notes);
15292 }
15293
15294 #[test]
15295 fn diverging_matrix_frames_name_the_axis() {
15296 ctx!(c);
15297 let e = plus()
15298 .dyad(&mat(2, 3, vec![0; 6]), &mat(2, 4, vec![0; 8]), &mut c, sp())
15299 .unwrap_err();
15300 assert_eq!(e.kind, ErrorKind::Shape);
15301 assert!(e.notes[0].contains("axis 1"), "{:?}", e.notes);
15302 }
15303
15304 #[test]
15305 fn dyadic_rank_pairs_rows_with_the_whole_right_argument() {
15306 ctx!(c);
15307 let v = Verb::Rank(b(plus()), [0, 1, 1]);
15309 let r = v
15310 .dyad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &Array::from_i64(vec![10, 20, 30]), &mut c, sp())
15311 .unwrap();
15312 assert_eq!(r.shape, vec![2, 3]);
15313 assert_eq!(ints(&r), vec![11, 22, 33, 14, 25, 36]);
15314 }
15315
15316 #[test]
15317 fn surplus_frame_axes_repeat_the_shorter_frames_cells() {
15318 ctx!(c);
15319 let v = Verb::Rank(b(head_v()), [0, 0, 1]);
15322 let x = mat(2, 2, vec![1, 1, 2, 2]);
15323 let y = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15324 let r = v.dyad(&x, &y, &mut c, sp()).unwrap();
15325 assert_eq!(r.shape, vec![2, 2, 2]);
15326 assert_eq!(ints(&r), vec![1, 0, 1, 0, 4, 5, 4, 5]);
15327 }
15328
15329 #[test]
15330 fn an_empty_frame_pairs_its_single_cell_with_every_other_cell() {
15331 ctx!(c, Agreement::ExactOrScalar);
15332 let v = Verb::Rank(b(head_v()), [0, 0, 1]);
15334 let x = Array::from_i64(vec![1, 2]);
15335 let y = Array::from_i64(vec![7, 8, 9]);
15336 let r = v.dyad(&x, &y, &mut c, sp()).unwrap();
15337 assert_eq!(r.shape, vec![2, 2]);
15338 assert_eq!(ints(&r), vec![7, 0, 7, 8]);
15339 }
15340
15341 #[test]
15342 fn negative_rank_leaves_frame_axes() {
15343 ctx!(c);
15344 let v = Verb::Rank(b(dollar()), [-1, -1, -1]);
15346 let r = v.monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp()).unwrap();
15347 assert_eq!(r.shape, vec![2, 1]);
15348 assert_eq!(ints(&r), vec![3, 3]);
15349 }
15350
15351 #[test]
15352 fn effective_rank_clamps_and_counts_back() {
15353 assert_eq!(effective_rank(0, 3), 0);
15354 assert_eq!(effective_rank(2, 1), 1);
15355 assert_eq!(effective_rank(RANK_INF, 4), 4);
15356 assert_eq!(effective_rank(-1, 3), 2);
15357 assert_eq!(effective_rank(-5, 3), 0);
15358 }
15359
15360 #[test]
15363 fn reduction_folds_right_to_left() {
15364 ctx!(c);
15365 let r = Verb::Reduce(b(minus()))
15367 .monad(&Array::from_i64(vec![1, 2, 3]), &mut c, sp())
15368 .unwrap();
15369 assert!(r.shape.is_empty());
15370 assert_eq!(ints(&r), vec![2]);
15371 }
15372
15373 #[test]
15374 fn reduction_of_one_item_and_of_a_scalar() {
15375 ctx!(c);
15376 let r = Verb::Reduce(b(plus()))
15377 .monad(&Array::from_i64(vec![7]), &mut c, sp())
15378 .unwrap();
15379 assert!(r.shape.is_empty());
15380 assert_eq!(ints(&r), vec![7]);
15381 let r = Verb::Reduce(b(plus())).monad(&Array::scalar_i64(7), &mut c, sp()).unwrap();
15382 assert_eq!(ints(&r), vec![7]);
15383 }
15384
15385 #[test]
15386 fn reduction_runs_along_the_leading_axis() {
15387 ctx!(c);
15388 let r = Verb::Reduce(b(plus()))
15389 .monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp())
15390 .unwrap();
15391 assert_eq!(r.shape, vec![3]);
15392 assert_eq!(ints(&r), vec![5, 7, 9]);
15393 }
15394
15395 #[test]
15396 fn rank_wrapped_reduction_sums_the_last_axis() {
15397 ctx!(c);
15398 let v = Verb::Rank(b(Verb::Reduce(b(plus()))), [1, 1, 1]);
15399 let r = v.monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp()).unwrap();
15400 assert_eq!(r.shape, vec![2]);
15401 assert_eq!(ints(&r), vec![6, 15]);
15402 }
15403
15404 #[test]
15405 fn empty_reduction_uses_the_identity_cell() {
15406 ctx!(c);
15407 let empty = Array::new(vec![0, 2], Data::I64(vec![].into()));
15408 let r = Verb::Reduce(b(plus())).monad(&empty, &mut c, sp()).unwrap();
15409 assert_eq!(r.shape, vec![2]);
15410 assert_eq!(ints(&r), vec![0, 0]);
15411 let r = Verb::Reduce(b(times())).monad(&empty, &mut c, sp()).unwrap();
15412 assert_eq!(ints(&r), vec![1, 1]);
15413 let r = Verb::Reduce(b(floor_v())).monad(&empty, &mut c, sp()).unwrap();
15414 assert!(floats(&r).iter().all(|&x| x == f64::INFINITY));
15415 let r = Verb::Reduce(b(ceil_v())).monad(&empty, &mut c, sp()).unwrap();
15416 assert!(floats(&r).iter().all(|&x| x == f64::NEG_INFINITY));
15417 let r = Verb::Reduce(b(minus())).monad(&empty, &mut c, sp()).unwrap();
15420 assert_eq!(ints(&r), vec![0, 0]);
15421 let r = Verb::Reduce(b(pct())).monad(&empty, &mut c, sp()).unwrap();
15422 assert_eq!(ints(&r), vec![1, 1]);
15423 let r = Verb::Reduce(b(eq_v())).monad(&empty, &mut c, sp()).unwrap();
15424 assert_eq!(bools(&r), vec![1, 1]);
15425 let r = Verb::Reduce(b(plus()))
15427 .monad(&Array::empty(DType::I64), &mut c, sp())
15428 .unwrap();
15429 assert!(r.shape.is_empty());
15430 assert_eq!(ints(&r), vec![0]);
15431 }
15432
15433 #[test]
15434 fn empty_reduction_without_an_identity_is_a_domain_error() {
15435 ctx!(c);
15436 let v = Verb::Hook(b(plus()), b(minus()));
15440 let e = Verb::Reduce(b(v)).monad(&Array::empty(DType::I64), &mut c, sp()).unwrap_err();
15441 assert_eq!(e.kind, ErrorKind::Domain);
15442 assert!(e.msg.contains("identity"), "{}", e.msg);
15443 }
15444
15445 #[test]
15446 fn reduction_with_a_non_primitive_verb_uses_the_general_fold() {
15447 ctx!(c);
15448 let v = Verb::Reduce(b(Verb::Hook(b(plus()), b(minus()))));
15450 let r = v.monad(&Array::from_i64(vec![1, 2, 3]), &mut c, sp()).unwrap();
15451 assert_eq!(ints(&r), vec![2]);
15452 }
15453
15454 #[test]
15455 fn dyadic_reduction_is_the_table() {
15456 ctx!(c);
15457 let v = Verb::Reduce(b(plus()));
15460 let r = v
15461 .dyad(&Array::scalar_i64(2), &Array::from_i64(vec![1, 2, 3]), &mut c, sp())
15462 .unwrap();
15463 assert_eq!(r.shape, vec![3]);
15464 assert_eq!(ints(&r), vec![3, 4, 5]);
15465 let r = v
15468 .dyad(&Array::from_i64(vec![1, 2, 3]), &Array::from_i64(vec![10, 20]), &mut c, sp())
15469 .unwrap();
15470 assert_eq!(r.shape, vec![3, 2]);
15471 assert_eq!(ints(&r), vec![11, 21, 12, 22, 13, 23]);
15472 let cat = Verb::Reduce(b(inf_prim(",", MonadOp::Ravel, DyadOp::AppendLeading)));
15474 let r = cat
15475 .dyad(&Array::from_i64(vec![1, 2]), &Array::from_i64(vec![3, 4]), &mut c, sp())
15476 .unwrap();
15477 assert_eq!(r.shape, vec![4]);
15478 assert_eq!(ints(&r), vec![1, 2, 3, 4]);
15479 }
15480
15481 #[test]
15484 fn integer_overflow_promotes_the_whole_result_to_float() {
15485 ctx!(c);
15486 let r = plus()
15487 .dyad(&Array::from_i64(vec![1, i64::MAX]), &Array::scalar_i64(1), &mut c, sp())
15488 .unwrap();
15489 assert_eq!(r.dtype(), DType::F64);
15490 let v = floats(&r);
15491 assert!(close(v[0], 2.0));
15492 assert!(close(v[1], i64::MAX as f64 + 1.0));
15493 let r = plus()
15495 .dyad(&Array::from_i64(vec![1, 2]), &Array::scalar_i64(1), &mut c, sp())
15496 .unwrap();
15497 assert_eq!(r.dtype(), DType::I64);
15498 }
15499
15500 #[test]
15501 fn reduction_overflow_promotes_too() {
15502 ctx!(c);
15503 let r = Verb::Reduce(b(plus()))
15504 .monad(&Array::from_i64(vec![i64::MAX, i64::MAX]), &mut c, sp())
15505 .unwrap();
15506 assert_eq!(r.dtype(), DType::F64);
15507 assert!(close(floats(&r)[0], 2.0 * i64::MAX as f64));
15508 }
15509
15510 #[test]
15511 fn booleans_widen_to_integers_in_arithmetic() {
15512 ctx!(c);
15513 let bits = Array::new(vec![3], Data::Bool(vec![1, 0, 1].into()));
15514 let r = plus().dyad(&bits, &bits, &mut c, sp()).unwrap();
15515 assert_eq!(r.dtype(), DType::I64);
15516 assert_eq!(ints(&r), vec![2, 0, 2]);
15517 }
15518
15519 #[test]
15520 fn j_division_is_float_and_survives_zero() {
15521 ctx!(c);
15522 let r = pct()
15523 .dyad(&Array::from_i64(vec![1, -1, 0, 6]), &Array::from_i64(vec![0, 0, 0, 4]), &mut c, sp())
15524 .unwrap();
15525 let v = floats(&r);
15526 assert_eq!(v[0], f64::INFINITY);
15527 assert_eq!(v[1], f64::NEG_INFINITY);
15528 assert_eq!(v[2], 0.0);
15529 assert!(close(v[3], 1.5));
15530 }
15531
15532 #[test]
15533 fn apl_division_by_zero_is_a_domain_error_except_zero_by_zero() {
15534 ctx!(c, Agreement::ExactOrScalar);
15535 let r = div_apl()
15536 .dyad(&Array::scalar_i64(0), &Array::scalar_i64(0), &mut c, sp())
15537 .unwrap();
15538 assert!(close(floats(&r)[0], 1.0));
15539 let e = div_apl()
15540 .dyad(&Array::scalar_i64(1), &Array::scalar_i64(0), &mut c, sp())
15541 .unwrap_err();
15542 assert_eq!(e.kind, ErrorKind::Domain);
15543 assert!(e.msg.contains("division by zero"), "{}", e.msg);
15544 let r = div_apl()
15545 .dyad(&Array::scalar_i64(6), &Array::scalar_i64(4), &mut c, sp())
15546 .unwrap();
15547 assert!(close(floats(&r)[0], 1.5));
15548 }
15549
15550 #[test]
15551 fn reciprocal_of_zero_is_infinite() {
15552 ctx!(c);
15553 let r = pct().monad(&Array::from_i64(vec![0, 2]), &mut c, sp()).unwrap();
15554 let v = floats(&r);
15555 assert_eq!(v[0], f64::INFINITY);
15556 assert!(close(v[1], 0.5));
15557 }
15558
15559 #[test]
15560 fn residue_takes_the_sign_of_the_left_argument() {
15561 ctx!(c);
15562 let x = Array::from_i64(vec![3, 3, -3, -3, 0]);
15563 let y = Array::from_i64(vec![5, -5, 5, -5, 5]);
15564 let r = residue_v().dyad(&x, &y, &mut c, sp()).unwrap();
15565 assert_eq!(ints(&r), vec![2, 1, -1, -2, 5]);
15566 let r = residue_v()
15568 .dyad(&Array::from_f64(vec![2.5]), &Array::from_f64(vec![7.0]), &mut c, sp())
15569 .unwrap();
15570 assert!(close(floats(&r)[0], 2.0));
15571 }
15572
15573 #[test]
15574 fn power_stays_integral_when_it_can() {
15575 ctx!(c);
15576 let r = pow_v()
15577 .dyad(&Array::from_i64(vec![2, 0, 5]), &Array::from_i64(vec![10, 0, 1]), &mut c, sp())
15578 .unwrap();
15579 assert_eq!(r.dtype(), DType::I64);
15580 assert_eq!(ints(&r), vec![1024, 1, 5]);
15581 let r = pow_v()
15583 .dyad(&Array::from_i64(vec![2, 4]), &Array::from_i64(vec![-1, 2]), &mut c, sp())
15584 .unwrap();
15585 assert_eq!(r.dtype(), DType::F64);
15586 assert!(close(floats(&r)[0], 0.5));
15587 assert!(close(floats(&r)[1], 16.0));
15588 let r = pow_v()
15590 .dyad(&Array::scalar_i64(10), &Array::scalar_i64(30), &mut c, sp())
15591 .unwrap();
15592 assert_eq!(r.dtype(), DType::F64);
15593 }
15594
15595 #[test]
15596 fn comparisons_yield_booleans() {
15597 ctx!(c);
15598 let r = lt_v()
15599 .dyad(&Array::from_i64(vec![1, 2, 3]), &Array::scalar_i64(2), &mut c, sp())
15600 .unwrap();
15601 assert_eq!(bools(&r), vec![1, 0, 0]);
15602 let r = eq_v()
15603 .dyad(&Array::from_f64(vec![1.0, 2.0]), &Array::from_i64(vec![1, 3]), &mut c, sp())
15604 .unwrap();
15605 assert_eq!(bools(&r), vec![1, 0]);
15606 }
15607
15608 #[test]
15609 fn characters_compare_but_do_not_add() {
15610 ctx!(c);
15611 let a = Array::from_chars(vec!['a', 'b']);
15612 let bb = Array::from_chars(vec!['a', 'c']);
15613 assert_eq!(bools(&eq_v().dyad(&a, &bb, &mut c, sp()).unwrap()), vec![1, 0]);
15614 let e = plus().dyad(&a, &bb, &mut c, sp()).unwrap_err();
15615 assert_eq!(e.kind, ErrorKind::Type);
15616 assert!(e.msg.contains("characters"), "{}", e.msg);
15617 let e = lt_v().dyad(&a, &bb, &mut c, sp()).unwrap_err();
15618 assert_eq!(e.kind, ErrorKind::Type);
15619 let e = plus().dyad(&a, &Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap_err();
15620 assert_eq!(e.kind, ErrorKind::Type);
15621 assert!(e.msg.contains("character"), "{}", e.msg);
15622 let e = plus().monad(&a, &mut c, sp()).unwrap_err();
15623 assert_eq!(e.kind, ErrorKind::Type);
15624 }
15625
15626 #[test]
15627 fn floor_and_ceiling_return_integers_when_they_fit() {
15628 ctx!(c);
15629 let r = floor_v().monad(&Array::from_f64(vec![1.5, -1.5]), &mut c, sp()).unwrap();
15630 assert_eq!(r.dtype(), DType::I64);
15631 assert_eq!(ints(&r), vec![1, -2]);
15632 let r = ceil_v().monad(&Array::from_f64(vec![1.5, -1.5]), &mut c, sp()).unwrap();
15633 assert_eq!(ints(&r), vec![2, -1]);
15634 let r = floor_v().monad(&Array::from_f64(vec![1e30]), &mut c, sp()).unwrap();
15636 assert_eq!(r.dtype(), DType::F64);
15637 let r = floor_v().monad(&Array::from_i64(vec![3]), &mut c, sp()).unwrap();
15639 assert_eq!(ints(&r), vec![3]);
15640 }
15641
15642 #[test]
15643 fn logical_negation_needs_zero_or_one() {
15644 ctx!(c);
15645 let r = not_v().monad(&Array::from_i64(vec![0, 1]), &mut c, sp()).unwrap();
15646 assert_eq!(bools(&r), vec![1, 0]);
15647 let e = not_v().monad(&Array::from_i64(vec![2]), &mut c, sp()).unwrap_err();
15648 assert_eq!(e.kind, ErrorKind::Domain);
15649 }
15650
15651 #[test]
15652 fn signum_abs_and_negation_pick_their_types() {
15653 ctx!(c);
15654 let r = times().monad(&Array::from_i64(vec![-3, 0, 9]), &mut c, sp()).unwrap();
15655 assert_eq!(ints(&r), vec![-1, 0, 1]);
15656 let r = times().monad(&Array::from_f64(vec![-3.0, 0.0, 9.0]), &mut c, sp()).unwrap();
15657 assert_eq!(floats(&r), vec![-1.0, 0.0, 1.0]);
15658 let r = residue_v().monad(&Array::from_i64(vec![-3, 3]), &mut c, sp()).unwrap();
15659 assert_eq!(ints(&r), vec![3, 3]);
15660 let bits = Array::new(vec![2], Data::Bool(vec![0, 1].into()));
15661 let r = minus().monad(&bits, &mut c, sp()).unwrap();
15662 assert_eq!(r.dtype(), DType::I64);
15663 assert_eq!(ints(&r), vec![0, -1]);
15664 }
15665
15666 #[test]
15667 fn square_root_of_a_negative_number_is_complex() {
15668 ctx!(c);
15669 let r = sqrt_v().monad(&Array::from_i64(vec![9]), &mut c, sp()).unwrap();
15670 assert!(close(floats(&r)[0], 3.0));
15671 let r = sqrt_v().monad(&Array::from_i64(vec![-4]), &mut c, sp()).unwrap();
15672 assert_eq!(r.dtype(), DType::Complex);
15673 assert_eq!(r.as_complex_slice().expect("complex data"), &[[0.0, 2.0]]);
15674 }
15675
15676 #[test]
15679 fn shape_tally_and_ravel() {
15680 ctx!(c);
15681 let m = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15682 let r = dollar().monad(&m, &mut c, sp()).unwrap();
15683 assert_eq!(r.shape, vec![2]);
15684 assert_eq!(ints(&r), vec![2, 3]);
15685 let r = pound().monad(&m, &mut c, sp()).unwrap();
15686 assert!(r.shape.is_empty());
15687 assert_eq!(ints(&r), vec![2]);
15688 let r = pound().monad(&Array::scalar_i64(5), &mut c, sp()).unwrap();
15690 assert_eq!(ints(&r), vec![1]);
15691 let r = comma().monad(&m, &mut c, sp()).unwrap();
15692 assert_eq!(r.shape, vec![6]);
15693 assert_eq!(ints(&r), vec![1, 2, 3, 4, 5, 6]);
15694 }
15695
15696 #[test]
15697 fn transpose_reverses_the_axes() {
15698 ctx!(c);
15699 let r = transpose_v().monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp()).unwrap();
15700 assert_eq!(r.shape, vec![3, 2]);
15701 assert_eq!(ints(&r), vec![1, 4, 2, 5, 3, 6]);
15702 let a = Array::new(vec![2, 1, 3], Data::I64(vec![1, 2, 3, 4, 5, 6].into()));
15704 let r = transpose_v().monad(&a, &mut c, sp()).unwrap();
15705 assert_eq!(r.shape, vec![3, 1, 2]);
15706 assert_eq!(ints(&r), vec![1, 4, 2, 5, 3, 6]);
15707 let v = Array::from_i64(vec![1, 2]);
15709 assert_eq!(transpose_v().monad(&v, &mut c, sp()).unwrap(), v);
15710 }
15711
15712 #[test]
15713 fn head_and_behead() {
15714 ctx!(c);
15715 let m = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15716 let r = head_v().monad(&m, &mut c, sp()).unwrap();
15717 assert_eq!(r.shape, vec![3]);
15718 assert_eq!(ints(&r), vec![1, 2, 3]);
15719 let r = behead_v().monad(&m, &mut c, sp()).unwrap();
15720 assert_eq!(r.shape, vec![1, 3]);
15721 assert_eq!(ints(&r), vec![4, 5, 6]);
15722 let e = Array::new(vec![0, 2], Data::I64(vec![].into()));
15724 let r = head_v().monad(&e, &mut c, sp()).unwrap();
15725 assert_eq!(r.shape, vec![2]);
15726 assert_eq!(ints(&r), vec![0, 0]);
15727 assert_eq!(behead_v().monad(&e, &mut c, sp()).unwrap(), e);
15728 assert_eq!(head_v().monad(&Array::scalar_i64(5), &mut c, sp()).unwrap().shape, Vec::<usize>::new());
15729 let err = behead_v().monad(&Array::scalar_i64(5), &mut c, sp()).unwrap_err();
15730 assert_eq!(err.kind, ErrorKind::Domain);
15731 }
15732
15733 #[test]
15734 fn iota_fills_a_shape_and_reverses_negative_axes() {
15735 ctx!(c);
15736 let r = iota().monad(&Array::from_i64(vec![2, 3]), &mut c, sp()).unwrap();
15737 assert_eq!(r.shape, vec![2, 3]);
15738 assert_eq!(ints(&r), vec![0, 1, 2, 3, 4, 5]);
15739 let r = iota().monad(&Array::scalar_i64(3), &mut c, sp()).unwrap();
15741 assert_eq!(r.shape, vec![3]);
15742 assert_eq!(ints(&r), vec![0, 1, 2]);
15743 let r = iota().monad(&Array::scalar_i64(-3), &mut c, sp()).unwrap();
15745 assert_eq!(ints(&r), vec![2, 1, 0]);
15746 let r = iota().monad(&Array::from_i64(vec![2, -3]), &mut c, sp()).unwrap();
15747 assert_eq!(r.shape, vec![2, 3]);
15748 assert_eq!(ints(&r), vec![2, 1, 0, 5, 4, 3]);
15749 let r = iota().monad(&Array::from_i64(vec![-2, 3]), &mut c, sp()).unwrap();
15750 assert_eq!(ints(&r), vec![3, 4, 5, 0, 1, 2]);
15751 let r = iota().monad(&Array::scalar_i64(0), &mut c, sp()).unwrap();
15753 assert_eq!(r.shape, vec![0]);
15754 assert!(ints(&r).is_empty());
15755 let e = iota().monad(&Array::from_f64(vec![1.5]), &mut c, sp()).unwrap_err();
15757 assert_eq!(e.kind, ErrorKind::Domain);
15758 let e = iota().monad(&mat(1, 1, vec![1]), &mut c, sp()).unwrap_err();
15759 assert_eq!(e.kind, ErrorKind::Rank);
15760 }
15761
15762 #[test]
15763 fn apl_iota_starts_at_the_index_origin() {
15764 ctx!(c, Agreement::ExactOrScalar);
15765 let r = iota_apl(1).monad(&Array::scalar_i64(3), &mut c, sp()).unwrap();
15766 assert_eq!(ints(&r), vec![1, 2, 3]);
15767 let r = iota_apl(0).monad(&Array::scalar_i64(3), &mut c, sp()).unwrap();
15768 assert_eq!(ints(&r), vec![0, 1, 2]);
15769 let e = iota_apl(1).monad(&Array::scalar_i64(-1), &mut c, sp()).unwrap_err();
15770 assert_eq!(e.kind, ErrorKind::Domain);
15771 let r = iota_apl(1).monad(&Array::from_i64(vec![2, 3]), &mut c, sp()).unwrap();
15774 assert_eq!(r.shape, vec![2, 3]);
15775 assert_eq!(ints(&r.as_boxes().expect("boxed")[4]), vec![2, 2]);
15776 }
15777
15778 #[test]
15779 fn reshape_cycles_the_ravel() {
15780 ctx!(c);
15781 let r = dollar()
15782 .dyad(&Array::from_i64(vec![2, 3]), &Array::from_i64(vec![1, 2]), &mut c, sp())
15783 .unwrap();
15784 assert_eq!(r.shape, vec![2, 3]);
15785 assert_eq!(ints(&r), vec![1, 2, 1, 2, 1, 2]);
15786 let r = dollar()
15788 .dyad(&Array::scalar_i64(3), &Array::from_i64(vec![7]), &mut c, sp())
15789 .unwrap();
15790 assert_eq!(r.shape, vec![3]);
15791 assert_eq!(ints(&r), vec![7, 7, 7]);
15792 let r = dollar()
15794 .dyad(&Array::scalar_i64(2), &Array::from_chars(vec!['a', 'b', 'c']), &mut c, sp())
15795 .unwrap();
15796 assert_eq!(r.dtype(), DType::Char);
15797 let e = dollar()
15799 .dyad(&Array::scalar_i64(2), &Array::empty(DType::I64), &mut c, sp())
15800 .unwrap_err();
15801 assert_eq!(e.kind, ErrorKind::Length);
15802 assert!(e.msg.contains("empty"), "{}", e.msg);
15803 let r = dollar()
15805 .dyad(&Array::scalar_i64(0), &Array::empty(DType::I64), &mut c, sp())
15806 .unwrap();
15807 assert_eq!(r.shape, vec![0]);
15808 let e = dollar()
15809 .dyad(&Array::scalar_i64(-1), &Array::from_i64(vec![1]), &mut c, sp())
15810 .unwrap_err();
15811 assert_eq!(e.kind, ErrorKind::Domain);
15812 }
15813
15814 #[test]
15815 fn take_from_both_ends_and_beyond() {
15816 ctx!(c);
15817 let v = Array::from_i64(vec![1, 2, 3, 4]);
15818 let take = |x: Array, y: &Array, c: &mut Ctx<'_>| head_v().dyad(&x, y, c, sp()).unwrap();
15819 assert_eq!(ints(&take(Array::scalar_i64(2), &v, &mut c)), vec![1, 2]);
15820 assert_eq!(ints(&take(Array::scalar_i64(-2), &v, &mut c)), vec![3, 4]);
15821 let short = Array::from_i64(vec![1, 2, 3]);
15823 assert_eq!(ints(&take(Array::scalar_i64(6), &short, &mut c)), vec![1, 2, 3, 0, 0, 0]);
15824 assert_eq!(ints(&take(Array::scalar_i64(-6), &short, &mut c)), vec![0, 0, 0, 1, 2, 3]);
15826 let r = take(Array::scalar_i64(2), &Array::scalar_i64(5), &mut c);
15828 assert_eq!(r.shape, vec![2]);
15829 assert_eq!(ints(&r), vec![5, 0]);
15830 let m = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15832 let r = take(Array::scalar_i64(1), &m, &mut c);
15833 assert_eq!(r.shape, vec![1, 3]);
15834 assert_eq!(ints(&r), vec![1, 2, 3]);
15835 let r = take(Array::scalar_i64(-1), &m, &mut c);
15836 assert_eq!(ints(&r), vec![4, 5, 6]);
15837 let r = take(Array::from_i64(vec![2, 2]), &m, &mut c);
15838 assert_eq!(r.shape, vec![2, 2]);
15839 assert_eq!(ints(&r), vec![1, 2, 4, 5]);
15840 let r = take(Array::from_i64(vec![3, -2]), &m, &mut c);
15841 assert_eq!(r.shape, vec![3, 2]);
15842 assert_eq!(ints(&r), vec![2, 3, 5, 6, 0, 0]);
15843 let r = head_v()
15845 .dyad(&Array::scalar_i64(3), &Array::from_chars(vec!['a']), &mut c, sp())
15846 .unwrap();
15847 assert_eq!(r.data, Data::Char(vec!['a', ' ', ' '].into()));
15848 let e = head_v()
15851 .dyad(&Array::from_i64(vec![1, 1]), &Array::from_i64(vec![1, 2]), &mut c, sp())
15852 .unwrap_err();
15853 assert_eq!(e.kind, ErrorKind::Length);
15854 let r = head_v()
15855 .dyad(&Array::from_i64(vec![1, 2]), &Array::scalar_i64(5), &mut c, sp())
15856 .unwrap();
15857 assert_eq!(r.shape, vec![1, 2]);
15858 assert_eq!(ints(&r), vec![5, 0]);
15859 }
15860
15861 #[test]
15862 fn drop_from_both_ends_and_beyond() {
15863 ctx!(c);
15864 let v = Array::from_i64(vec![1, 2, 3]);
15865 let drop = |x: Array, y: &Array, c: &mut Ctx<'_>| behead_v().dyad(&x, y, c, sp()).unwrap();
15866 assert_eq!(ints(&drop(Array::scalar_i64(1), &v, &mut c)), vec![2, 3]);
15867 assert_eq!(ints(&drop(Array::scalar_i64(-1), &v, &mut c)), vec![1, 2]);
15868 let r = drop(Array::scalar_i64(5), &v, &mut c);
15870 assert_eq!(r.shape, vec![0]);
15871 assert!(ints(&r).is_empty());
15872 let m = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15873 let r = drop(Array::scalar_i64(1), &m, &mut c);
15874 assert_eq!(r.shape, vec![1, 3]);
15875 assert_eq!(ints(&r), vec![4, 5, 6]);
15876 let r = drop(Array::from_i64(vec![0, -1]), &m, &mut c);
15877 assert_eq!(r.shape, vec![2, 2]);
15878 assert_eq!(ints(&r), vec![1, 2, 4, 5]);
15879 }
15880
15881 #[test]
15884 fn cells_of_unequal_shapes_are_padded_with_fills() {
15885 ctx!(c);
15886 let v = Verb::Rank(b(iota()), [0, 0, 0]);
15888 let r = v.monad(&Array::from_i64(vec![1, 2, 3]), &mut c, sp()).unwrap();
15889 assert_eq!(r.shape, vec![3, 3]);
15890 assert_eq!(ints(&r), vec![0, 0, 0, 0, 1, 0, 0, 1, 2]);
15891 }
15892
15893 #[test]
15894 fn framing_aligns_lower_rank_cells_at_the_trailing_axes() {
15895 let cells = vec![Array::from_i64(vec![1, 2]), mat(2, 2, vec![1, 2, 3, 4])];
15896 let r = assemble(&[2], cells, sp()).unwrap();
15897 assert_eq!(r.shape, vec![2, 2, 2]);
15898 assert_eq!(ints(&r), vec![1, 2, 0, 0, 1, 2, 3, 4]);
15899 }
15900
15901 #[test]
15902 fn framing_promotes_cell_types() {
15903 let cells = vec![Array::from_i64(vec![1]), Array::from_f64(vec![2.5])];
15904 let r = assemble(&[2], cells, sp()).unwrap();
15905 assert_eq!(r.dtype(), DType::F64);
15906 assert_eq!(floats(&r), vec![1.0, 2.5]);
15907 let cells = vec![Array::from_i64(vec![1]), Array::from_chars(vec!['a'])];
15909 let e = assemble(&[2], cells, sp()).unwrap_err();
15910 assert_eq!(e.kind, ErrorKind::Type);
15911 }
15912
15913 #[test]
15914 fn framing_over_an_empty_frame_yields_an_empty_result() {
15915 let r = assemble(&[0], Vec::new(), sp()).unwrap();
15916 assert_eq!(r.shape, vec![0]);
15917 assert_eq!(r.count(), 0);
15918 }
15919
15920 #[test]
15923 fn fork_applies_both_tines() {
15924 ctx!(c);
15925 let v = Verb::Fork(b(Verb::Reduce(b(plus()))), b(pct()), b(pound()));
15927 let r = v.monad(&Array::from_i64(vec![1, 2, 3, 4]), &mut c, sp()).unwrap();
15928 assert!(close(floats(&r)[0], 2.5));
15929 let v = Verb::Fork(b(minus()), b(plus()), b(plus()));
15931 let r = v
15932 .dyad(&Array::from_i64(vec![5]), &Array::from_i64(vec![3]), &mut c, sp())
15933 .unwrap();
15934 assert_eq!(ints(&r), vec![10]);
15935 }
15936
15937 #[test]
15938 fn noun_fork_supplies_a_constant_left_argument() {
15939 ctx!(c);
15940 let v = Verb::NounFork(Array::scalar_i64(10), b(minus()), b(right_v()));
15941 let r = v.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15942 assert_eq!(ints(&r), vec![9, 8]);
15943 let r = v
15944 .dyad(&Array::scalar_i64(0), &Array::from_i64(vec![1, 2]), &mut c, sp())
15945 .unwrap();
15946 assert_eq!(ints(&r), vec![9, 8]);
15947 }
15948
15949 #[test]
15950 fn hook_reuses_its_right_argument() {
15951 ctx!(c);
15952 let v = Verb::Hook(b(plus()), b(minus()));
15954 let r = v.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15955 assert_eq!(ints(&r), vec![0, 0]);
15956 let r = v
15958 .dyad(&Array::from_i64(vec![10]), &Array::from_i64(vec![3]), &mut c, sp())
15959 .unwrap();
15960 assert_eq!(ints(&r), vec![7]);
15961 }
15962
15963 #[test]
15964 fn atop_composes() {
15965 ctx!(c);
15966 let v = Verb::Atop(b(minus()), b(plus()));
15967 let r = v.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15968 assert_eq!(ints(&r), vec![-1, -2]);
15969 let r = v
15970 .dyad(&Array::from_i64(vec![1]), &Array::from_i64(vec![2]), &mut c, sp())
15971 .unwrap();
15972 assert_eq!(ints(&r), vec![-3]);
15973 }
15974
15975 #[test]
15976 fn trains_apply_to_the_whole_argument() {
15977 assert_eq!(Verb::Hook(b(plus()), b(minus())).ranks(), [RANK_INF; 3]);
15979 assert_eq!(Verb::Reduce(b(plus())).ranks(), [RANK_INF; 3]);
15980 }
15981
15982 #[test]
15985 fn absent_and_unwritten_meanings_are_reported_differently() {
15986 ctx!(c);
15987 let e = eq_v().monad(&Array::scalar_i64(1), &mut c, sp()).unwrap_err();
15988 assert_eq!(e.kind, ErrorKind::Domain);
15989 assert!(e.msg.contains("no monadic meaning"), "{}", e.msg);
15990 let e = not_v()
15991 .dyad(&Array::scalar_i64(1), &Array::scalar_i64(1), &mut c, sp())
15992 .unwrap_err();
15993 assert_eq!(e.kind, ErrorKind::Domain);
15994 assert!(e.msg.contains("no dyadic meaning"), "{}", e.msg);
15995 let e = pound()
15996 .dyad(&Array::scalar_i64(1), &Array::scalar_i64(1), &mut c, sp())
15997 .unwrap_err();
15998 assert_eq!(e.kind, ErrorKind::NotYet);
15999 assert!(e.msg.contains("copy"), "{}", e.msg);
16000 let _ = echo_v();
16002 }
16003
16004 fn seq_par<T: Send>(f: impl Fn() -> T + Sync + Send) -> (T, T) {
16013 (par::with_threads(1, &f), par::with_threads(4, &f))
16014 }
16015
16016 fn noise(n: usize) -> Vec<f64> {
16018 let mut x = 0x2545_f491_4f6c_dd1du64;
16019 (0..n)
16020 .map(|_| {
16021 x ^= x << 13;
16022 x ^= x >> 7;
16023 x ^= x << 17;
16024 (x >> 11) as f64 / (1u64 << 53) as f64 - 0.5
16025 })
16026 .collect()
16027 }
16028
16029 fn f64_mat(rows: usize, cols: usize) -> Array {
16030 Array::new(vec![rows, cols], Data::F64(noise(rows * cols).into()))
16031 }
16032
16033 const BIG: usize = 200_000;
16035
16036 #[test]
16037 fn an_elementwise_dyad_splits_into_the_same_result() {
16038 let x = Array::from_f64(noise(BIG));
16039 let y = Array::from_f64(noise(BIG).iter().map(|v| v + 0.25).collect());
16040 let (one, many) = seq_par(|| {
16041 ctx!(c);
16042 times().dyad(&x, &y, &mut c, sp()).unwrap()
16043 });
16044 assert_eq!(floats(&one), floats(&many));
16045 let (one, many) = seq_par(|| {
16047 ctx!(c);
16048 plus().dyad(&Array::scalar_f64(0.5), &y, &mut c, sp()).unwrap()
16049 });
16050 assert_eq!(floats(&one), floats(&many));
16051 }
16052
16053 #[test]
16054 fn an_elementwise_dyad_that_overflows_widens_the_same_way() {
16055 let mut v = vec![1i64; BIG];
16058 v[BIG - 3] = i64::MAX;
16059 let x = Array::from_i64(v);
16060 let (one, many) = seq_par(|| {
16061 ctx!(c);
16062 plus().dyad(&x, &x, &mut c, sp()).unwrap()
16063 });
16064 assert_eq!(one.dtype(), DType::F64);
16065 assert_eq!(floats(&one), floats(&many));
16066 }
16067
16068 #[test]
16069 fn an_elementwise_monad_splits_into_the_same_result() {
16070 let y = Array::from_f64(noise(BIG));
16071 for v in [minus(), sqrt_v(), floor_v(), pct()] {
16072 let (one, many) = seq_par(|| {
16073 ctx!(c);
16074 v.monad(&Array::from_f64(y.as_f64_slice().unwrap().iter().map(|x| x.abs()).collect()), &mut c, sp())
16075 .unwrap()
16076 });
16077 assert_eq!(one.data, many.data, "{}", v.name());
16078 }
16079 }
16080
16081 #[test]
16082 fn monadic_cells_run_in_parallel_and_frame_in_order() {
16083 let y = f64_mat(400, 512);
16086 let v = Verb::Rank(b(Verb::Reduce(b(plus()))), [1, 1, 1]);
16087 let (one, many) = seq_par(|| {
16088 ctx!(c);
16089 v.monad(&y, &mut c, sp()).unwrap()
16090 });
16091 assert_eq!(one.shape, vec![400]);
16092 assert_eq!(floats(&one), floats(&many));
16093 }
16094
16095 #[test]
16096 fn dyadic_cells_run_in_parallel_and_frame_in_order() {
16097 let x = f64_mat(400, 512);
16098 let y = f64_mat(400, 512);
16099 let v = Verb::Rank(b(plus()), [1, 1, 1]);
16101 let (one, many) = seq_par(|| {
16102 ctx!(c);
16103 v.dyad(&x, &y, &mut c, sp()).unwrap()
16104 });
16105 assert_eq!(one.shape, vec![400, 512]);
16106 assert_eq!(floats(&one), floats(&many));
16107 }
16108
16109 #[test]
16110 fn a_verb_that_writes_output_is_not_pure() {
16111 assert!(plus().is_pure());
16112 assert!(Verb::Rank(b(Verb::Reduce(b(plus()))), [1, 1, 1]).is_pure());
16113 assert!(!echo_v().is_pure());
16114 assert!(!Verb::Rank(b(Verb::Atop(b(echo_v()), b(plus()))), [1, 1, 1]).is_pure());
16115 }
16116
16117 #[test]
16118 fn an_impure_verb_keeps_its_cells_in_order() {
16119 let y = Array::new(vec![16, 8192], Data::I64((0..16 * 8192).collect::<Vec<i64>>().into()));
16122 let v = Verb::Rank(b(Verb::Atop(b(echo_v()), b(head_v()))), [1, 1, 1]);
16123 let mut seen: Vec<i64> = Vec::new();
16124 let mut sink = |s: &str| {
16125 if let Some(first) = s.split_whitespace().next() && let Ok(n) = first.parse::<i64>() {
16126 seen.push(n);
16127 }
16128 };
16129 let mut env = Env::new(Vec::new());
16130 let mut c = Ctx {
16131 cfg: EvalCfg {
16132 agreement: Agreement::LeadingPrefix,
16133 fmt: FmtOpts::J,
16134 tol: Tol::J,
16135 rules: Rules::default(),
16136 },
16137 out: &mut sink,
16138 inp: None,
16139 env: &mut env,
16140 device: None,
16141 };
16142 v.monad(&y, &mut c, sp()).unwrap();
16143 assert_eq!(seen, (0..16).map(|i| i * 8192).collect::<Vec<i64>>());
16144 }
16145
16146 #[test]
16147 fn a_wide_item_reduce_folds_every_column_in_order() {
16148 let y = f64_mat(300, 512);
16151 for v in [plus(), minus(), floor_v()] {
16152 let (one, many) = seq_par(|| {
16153 ctx!(c);
16154 Verb::Reduce(b(v.clone())).monad(&y, &mut c, sp()).unwrap()
16155 });
16156 assert_eq!(one.shape, vec![512]);
16157 assert_eq!(floats(&one), floats(&many), "{}", v.name());
16158 }
16159 }
16160
16161 #[test]
16162 fn a_wide_item_integer_reduce_is_exact() {
16163 let n = 300;
16164 let m = 512;
16165 let y = Array::new(
16166 vec![n, m],
16167 Data::I64((0..(n * m) as i64).map(|i| i % 977 - 400).collect::<Vec<i64>>().into()),
16168 );
16169 let (one, many) = seq_par(|| {
16170 ctx!(c);
16171 Verb::Reduce(b(minus())).monad(&y, &mut c, sp()).unwrap()
16172 });
16173 assert_eq!(ints(&one), ints(&many));
16174 }
16175
16176 #[test]
16177 fn a_narrow_item_reduce_chunks_the_items() {
16178 let y = f64_mat(300_000, 8);
16182 let (one, many) = seq_par(|| {
16183 ctx!(c);
16184 Verb::Reduce(b(plus())).monad(&y, &mut c, sp()).unwrap()
16185 });
16186 assert_eq!(one.shape, vec![8]);
16187 for (p, q) in floats(&one).iter().zip(floats(&many)) {
16188 assert!((p - q).abs() <= 1e-12 * p.abs().max(1.0), "{p} vs {q}");
16189 }
16190 let ints_y = Array::new(
16191 vec![300_000, 8],
16192 Data::I64((0..300_000 * 8).map(|i| (i % 101) as i64 - 50).collect::<Vec<i64>>().into()),
16193 );
16194 let (one, many) = seq_par(|| {
16195 ctx!(c);
16196 Verb::Reduce(b(plus())).monad(&ints_y, &mut c, sp()).unwrap()
16197 });
16198 assert_eq!(ints(&one), ints(&many));
16199 }
16200
16201 #[test]
16202 fn a_vector_reduce_folds_the_flat_buffer() {
16203 let y = Array::from_f64(noise(BIG * 4));
16204 let (one, many) = seq_par(|| {
16205 ctx!(c);
16206 Verb::Reduce(b(plus())).monad(&y, &mut c, sp()).unwrap()
16207 });
16208 let (p, q) = (floats(&one)[0], floats(&many)[0]);
16209 assert!((p - q).abs() <= 1e-12 * p.abs().max(1.0), "{p} vs {q}");
16210
16211 let ints_y = Array::from_i64((0..BIG as i64 * 4).map(|i| i % 1009 - 500).collect());
16214 for v in [plus(), minus(), ceil_v()] {
16215 let (one, many) = seq_par(|| {
16216 ctx!(c);
16217 Verb::Reduce(b(v.clone())).monad(&ints_y, &mut c, sp()).unwrap()
16218 });
16219 assert_eq!(ints(&one), ints(&many), "{}", v.name());
16220 }
16221 }
16222
16223 #[test]
16224 fn a_reduce_that_overflows_falls_back_to_the_sequential_widening() {
16225 let mut v: Vec<i64> = vec![1; BIG];
16226 v[7] = i64::MAX;
16227 let y = Array::from_i64(v);
16228 let (one, many) = seq_par(|| {
16229 ctx!(c);
16230 Verb::Reduce(b(plus())).monad(&y, &mut c, sp()).unwrap()
16231 });
16232 assert_eq!(one.dtype(), DType::F64);
16233 assert_eq!(floats(&one), floats(&many));
16234 }
16235
16236 #[test]
16237 fn a_boolean_reduce_matches_the_sequential_promotion() {
16238 let n = BIG;
16239 let y = Array::new(
16240 vec![n],
16241 Data::Bool((0..n).map(|i| (i % 3 == 0) as u8).collect::<Vec<u8>>().into()),
16242 );
16243 let (one, many) = seq_par(|| {
16244 ctx!(c);
16245 Verb::Reduce(b(plus())).monad(&y, &mut c, sp()).unwrap()
16246 });
16247 assert_eq!(one.dtype(), DType::I64);
16248 assert_eq!(ints(&one), ints(&many));
16249 assert_eq!(ints(&one)[0], n.div_ceil(3) as i64);
16250 }
16251}