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::{
17 ComplexOrder, EncodeDigits, FloorRule, InnerEach, NearCount, NestedGrade, Rules,
18};
19use crate::par;
20use crate::simd::multiversioned;
21
22pub const RANK_INF: i64 = i64::MAX;
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum Agreement {
29 LeadingPrefix,
31 ExactOrScalar,
33}
34
35#[derive(Clone, Copy, Debug, PartialEq)]
45pub struct Tol {
46 pub ct: f64,
48 pub by_smaller: bool,
50 pub floor_rule: FloorRule,
53}
54
55impl Tol {
56 pub const EXACT: Tol = Tol { ct: 0.0, by_smaller: true, floor_rule: FloorRule::Shift };
58 pub const J: Tol =
60 Tol { ct: 5.684_341_886_080_802e-14, by_smaller: true, floor_rule: FloorRule::Shift };
61 pub const APL: Tol = Tol { ct: 1e-13, by_smaller: false, floor_rule: FloorRule::Shift };
63
64 #[inline(always)]
66 pub fn eq(self, a: f64, b: f64) -> bool {
67 if a == b {
68 return true;
69 }
70 let s = if self.by_smaller {
73 a.abs().min(b.abs())
74 } else {
75 a.abs().max(b.abs())
76 };
77 (a - b).abs() < self.ct * s
78 }
79
80 #[inline(always)]
86 pub fn is_j(self) -> bool {
87 self.by_smaller
88 }
89
90 #[inline(always)]
96 pub fn is_zero(self, y: f64) -> bool {
97 self.is_j() && y.abs() < self.ct
98 }
99
100 #[inline(always)]
102 pub fn lt(self, a: f64, b: f64) -> bool {
103 a < b && !self.eq(a, b)
104 }
105
106 #[inline(always)]
108 pub fn le(self, a: f64, b: f64) -> bool {
109 a <= b || self.eq(a, b)
110 }
111
112 #[inline]
116 pub fn eq_cx(self, a: Cx, b: Cx) -> bool {
117 if a == b {
118 return true;
119 }
120 let (ma, mb) = (cx::abs(a), cx::abs(b));
121 let s = if self.by_smaller { ma.min(mb) } else { ma.max(mb) };
122 cx::abs(cx::sub(a, b)) < self.ct * s
123 }
124
125 #[inline(always)]
136 pub fn floor(self, y: f64) -> f64 {
137 if self.is_j() {
138 let c = y.ceil();
139 if self.eq(y, c) { c } else { y.floor() }
140 } else if self.floor_rule == FloorRule::Shift {
141 (y + self.ct).floor()
142 } else {
143 let c = y.ceil();
148 if c - y <= self.ct * y.abs().max(1.0) { c } else { y.floor() }
149 }
150 }
151
152 #[inline(always)]
155 pub fn ceil(self, y: f64) -> f64 {
156 if self.is_j() {
157 let f = y.floor();
158 if self.eq(y, f) { f } else { y.ceil() }
159 } else if self.floor_rule == FloorRule::Shift {
160 (y - self.ct).ceil()
161 } else {
162 let f = y.floor();
163 if y - f <= self.ct * y.abs().max(1.0) { f } else { y.ceil() }
164 }
165 }
166
167 #[inline]
182 pub fn residue(self, x: f64, y: f64) -> f64 {
183 if self.is_j() && y.is_infinite() && x != 0.0 {
189 return f64::NAN;
190 }
191 if x.is_infinite() {
196 return if y == 0.0 || (y > 0.0) == (x > 0.0) { y } else { x };
197 }
198 if x == 0.0 {
199 return y;
200 }
201 if self.is_j() {
202 let p = x * self.floor(y / x);
203 return if self.eq(y, p) { 0.0 } else { y - p };
204 }
205 let q = y / x;
210 let c = q.ceil();
211 let gap = c - q;
212 let k = if gap <= self.ct || gap < self.ct * q.abs().max(c.abs()) { c } else { q.floor() };
213 let r = y - x * k;
214 if r.abs() < self.ct * x.abs() {
215 0.0
216 } else if r != 0.0 && (r < 0.0) != (x < 0.0) {
217 r + x
218 } else {
219 r
220 }
221 }
222
223 #[inline(always)]
233 pub fn mul(self, x: f64, y: f64) -> f64 {
234 if self.is_j() && (x == 0.0 || y == 0.0) && !(x.is_finite() && y.is_finite()) {
235 return 0.0;
236 }
237 x * y
238 }
239
240 #[inline(always)]
247 pub fn made_nan(self, r: f64, x: f64, y: f64) -> bool {
248 self.is_j() && r.is_nan() && !x.is_nan() && !y.is_nan()
249 }
250}
251
252pub(crate) fn j_number(v: f64) -> String {
255 if v.is_nan() {
256 "_.".to_string()
257 } else if v == f64::INFINITY {
258 "_".to_string()
259 } else if v == f64::NEG_INFINITY {
260 "__".to_string()
261 } else {
262 format!("{v}")
263 }
264}
265
266#[derive(Clone, Copy, Debug)]
271pub struct EvalCfg {
272 pub agreement: Agreement,
273 pub fmt: FmtOpts,
274 pub tol: Tol,
277 pub rules: Rules,
280}
281
282impl EvalCfg {
283 pub(crate) fn near(self) -> NearInt {
291 match self.rules.lang {
292 crate::Lang::J => NearInt::J,
293 crate::Lang::Apl => match self.rules.near_count {
294 NearCount::Absolute => NearInt::Apl,
295 NearCount::Tolerant => NearInt::Tolerant(self.rules.tol()),
296 },
297 }
298 }
299
300 pub(crate) fn pure<R>(self, f: impl FnOnce(&mut Ctx<'_>) -> R) -> R {
301 let mut sink = |_: &str| debug_assert!(false, "a pure verb wrote to the output sink");
302 let mut env = Env::new(Vec::new());
303 f(&mut Ctx { cfg: self, out: &mut sink, inp: None, env: &mut env, device: None, shy: false })
304 }
305}
306
307pub const RECURSION_LIMIT: usize = 64;
316
317pub struct Env {
325 globals: HashMap<String, Array>,
326 frames: Vec<HashMap<String, Array>>,
327 running: Vec<std::sync::Arc<crate::ir::ExplicitDef>>,
330 verbs: HashMap<String, Verb>,
331 args: Vec<Array>,
332}
333
334impl Env {
335 pub fn new(args: Vec<Array>) -> Env {
336 Env {
337 globals: HashMap::new(),
338 frames: Vec::new(),
339 running: Vec::new(),
340 verbs: HashMap::new(),
341 args,
342 }
343 }
344
345 pub fn get(&self, name: &str) -> Option<Array> {
346 if let Some(frame) = self.frames.last() && let Some(v) = frame.get(name) {
347 return Some(v.clone());
348 }
349 if let Some(def) = self.running.last()
355 && !def.enclosing.is_empty()
356 {
357 for i in (0..self.frames.len().saturating_sub(1)).rev() {
358 if def.enclosing.contains(&self.running[i].id)
359 && let Some(v) = self.frames[i].get(name)
360 {
361 return Some(v.clone());
362 }
363 }
364 }
365 self.globals.get(name).cloned()
366 }
367
368 pub fn assign(&mut self, name: String, value: Array, scope: crate::ir::Scope) {
369 if scope == crate::ir::Scope::LocalDefault && self.get(&name).is_some() {
370 return;
371 }
372 let target = match (scope, self.frames.last_mut()) {
373 (crate::ir::Scope::Local | crate::ir::Scope::LocalDefault, Some(frame)) => frame,
374 _ => &mut self.globals,
375 };
376 target.insert(name, value);
377 }
378
379 pub fn define(&mut self, name: String, verb: Verb) {
380 self.verbs.insert(name, verb);
381 }
382
383 pub fn global(&self, name: &str) -> Option<Array> {
387 self.globals.get(name).cloned()
388 }
389
390 pub fn set_global(&mut self, name: String, value: Array) {
391 self.globals.insert(name, value);
392 }
393
394 pub fn unset_global(&mut self, name: &str) {
395 self.globals.remove(name);
396 }
397
398 pub fn undefine(&mut self, name: &str) {
399 self.verbs.remove(name);
400 }
401
402 pub fn verb(&self, name: &str) -> Option<&Verb> {
403 self.verbs.get(name)
404 }
405
406 pub fn arg(&self, i: usize) -> Result<Array> {
407 self.args
408 .get(i)
409 .cloned()
410 .ok_or_else(|| Error::internal("a parameter was read where none is bound"))
411 }
412
413 pub fn enter(
415 &mut self,
416 frame: HashMap<String, Array>,
417 def: std::sync::Arc<crate::ir::ExplicitDef>,
418 span: Span,
419 ) -> Result<()> {
420 if self.frames.len() >= RECURSION_LIMIT {
421 return Err(Error::new(
422 ErrorKind::Domain,
423 format!("explicit definitions called each other more than {RECURSION_LIMIT} deep"),
424 Some(span),
425 )
426 .note("a definition that recurses needs a case that stops"));
427 }
428 self.frames.push(frame);
429 self.running.push(def);
430 Ok(())
431 }
432
433 pub fn leave(&mut self) -> HashMap<String, Array> {
435 self.running.pop();
436 self.frames.pop().unwrap_or_default()
437 }
438
439 pub fn current_def(&self) -> Option<std::sync::Arc<crate::ir::ExplicitDef>> {
441 self.running.last().cloned()
442 }
443}
444
445pub type InputFn<'a> = Option<&'a mut dyn FnMut() -> Option<String>>;
453
454pub fn reborrow_input<'s, 'a: 's>(inp: &'s mut InputFn<'a>) -> InputFn<'s> {
458 match inp {
459 Some(f) => Some(&mut **f),
460 None => None,
461 }
462}
463
464pub struct Ctx<'a> {
466 pub cfg: EvalCfg,
467 pub out: &'a mut dyn FnMut(&str),
470 pub inp: InputFn<'a>,
474 pub env: &'a mut Env,
476 pub device: Option<&'a crate::device::Device>,
479 pub shy: bool,
486}
487
488const MAX_NESTING: usize = 400;
497
498thread_local! {
499 static NESTING: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
500}
501
502pub(crate) fn check_nesting(depth: usize, span: Span) -> Result<()> {
504 if depth > MAX_NESTING {
505 return Err(Error::new(
506 ErrorKind::Limit,
507 format!("this program nests more than {MAX_NESTING} applications deep"),
508 Some(span),
509 ));
510 }
511 Ok(())
512}
513
514pub(crate) struct Nesting;
516
517impl Nesting {
518 pub(crate) fn enter(span: Span) -> Result<Nesting> {
520 let depth = NESTING.with(|c| {
521 let d = c.get() + 1;
522 c.set(d);
523 d
524 });
525 if depth > MAX_NESTING {
526 NESTING.with(|c| c.set(c.get() - 1));
527 return Err(Error::new(
528 ErrorKind::Limit,
529 format!("this program nests more than {MAX_NESTING} applications deep"),
530 Some(span),
531 ));
532 }
533 Ok(Nesting)
534 }
535}
536
537impl Drop for Nesting {
538 fn drop(&mut self) {
539 NESTING.with(|c| c.set(c.get().saturating_sub(1)));
540 }
541}
542
543impl Ctx<'_> {
544 fn with_tol<R>(&mut self, tol: Tol, f: impl FnOnce(&mut Ctx<'_>) -> R) -> R {
546 let cfg = EvalCfg { tol, ..self.cfg };
547 f(&mut Ctx {
548 cfg,
549 out: &mut *self.out,
550 inp: reborrow_input(&mut self.inp),
551 env: &mut *self.env,
552 device: self.device,
553 shy: self.shy,
554 })
555 }
556
557 pub(crate) fn read_line(&mut self, span: Span) -> Result<String> {
563 let Some(read) = self.inp.as_deref_mut() else {
564 return Err(Error::new(
565 ErrorKind::Value,
566 "this expression reads input, and this run has no input source attached",
567 Some(span),
568 )
569 .note("attach one with Program::run_io (Rust), input= (Python), or jay_run_io (C)"));
570 };
571 read().ok_or_else(|| {
572 Error::new(ErrorKind::Value, "the input has ended: there is no line to read", Some(span))
573 })
574 }
575}
576
577#[derive(Clone, Copy, Debug, PartialEq, Eq)]
579pub enum ScalarMonad {
580 Conj,
582 Neg,
583 Signum,
584 Recip,
585 Sqrt,
586 Exp,
587 Abs,
588 Floor,
589 Ceil,
590 Not,
592 OneMinus,
594 Inc,
596 Dec,
598 Double,
600 Halve,
602 Square,
604 Ln,
606 Pi,
608 Factorial,
611 Imaginary,
613 Polar,
615}
616
617#[derive(Clone, Copy, Debug, PartialEq, Eq)]
619pub enum ScalarDyad {
620 Add,
621 Sub,
622 Mul,
623 DivJ,
625 DivApl,
627 Min,
628 Max,
629 Pow,
630 Residue,
632 Eq,
633 Ne,
634 Lt,
635 Le,
636 Gt,
637 Ge,
638 Lcm,
640 Gcd,
642 Log,
644 Root,
646 Circle,
650 Binomial,
653 MakeComplex,
655 PolarBy,
657}
658
659#[derive(Clone, Copy, Debug, PartialEq, Eq)]
661pub enum Enclose {
662 Always,
664 ExceptSimpleScalar,
666}
667
668#[derive(Clone, Copy, Debug, PartialEq, Eq)]
670pub enum MonadOp {
671 Scalar(ScalarMonad),
672 ShapeOf,
674 Tally,
676 Ravel,
678 RavelItems,
681 TransposeAxes,
683 Catalogue,
686 AtomicRep,
690 RazeIn,
693 Head,
695 Behead,
697 Tail,
699 Curtail,
701 Reverse,
703 Nub,
705 GradeUp { origin: i64 },
707 GradeDown { origin: i64 },
709 IotaJ,
711 IotaApl { origin: i64 },
713 Echo,
715 ReadStream,
719 TypeCode,
721 Same,
723 Format,
727 DecodeBits,
729 EncodeBits,
733 Itemize,
735 TableOf,
738 Enclose(Enclose),
740 Open,
744 Raze,
746 First,
749 Enlist,
751 Depth {
754 signed: bool,
757 },
758 Indices { origin: i64, boxed_coords: bool },
762 Steps,
764 ToExact,
767 NthPrime,
769 PrimeFactors,
771 MatrixInverse,
773 Roll { origin: i64, fixed: bool, float_at_zero: bool },
778 ComplexParts { polar: bool },
782 SelfClassify,
785 NubSieve,
788 Unicode { pass_chars: bool },
792 Symbols,
796 Sparse,
799 Words,
801 Nest,
804 LevelOf,
807 MapPaths,
810 PolyRoots,
814 PolyDeriv,
817 AnagramIndex,
819 CycleForm,
822 Split,
825 Execute { apl: bool },
830 Dense,
833 PrimeCount,
836 IndicesInverse,
839 NotYet(&'static str),
841 None,
843}
844
845#[derive(Clone, Copy, Debug, PartialEq, Eq)]
847pub enum DyadOp {
848 Scalar(ScalarDyad),
849 Reshape,
852 Take,
854 Drop,
856 Right,
858 Left,
860 Rotate,
862 RotateApl { last: bool },
867 AppendLeading,
869 AppendLast,
871 IndexOf { origin: i64, vector_left: bool },
875 MemberJ,
877 MemberApl,
879 From,
882 Match,
884 NotMatch,
886 GradeSelect { down: bool },
888 Copy,
891 Decode,
894 Encode,
898 CollateGrade { down: bool, origin: i64 },
901 TransposeJ,
904 TransposeApl,
907 DecodeApl,
910 EncodeApl,
913 Laminate,
915 Link,
918 Strand,
920 IntervalIndex { offset: i64, closed: bool },
924 IndexOfLast { origin: i64 },
926 MatrixDivide,
928 PartitionEnclose,
931 PartitionCounts,
935 Squad {
937 origin: i64,
938 leading: bool,
942 },
943 SelectAxis { axis: usize, rank: usize, origin: i64 },
947 Fetch,
949 PolyEval,
952 PolyIntegral,
955 FormatSpec,
958 FormatSpecJ,
963 ParseNumbers,
966 SequentialMachine,
968 TruthTable(u8),
972 ExactForm,
976 Deal { origin: i64, fixed: bool },
979 Boolean(BoolDyad),
982 Less,
984 Union,
986 Intersect,
988 AnagramFrom,
991 Permute,
994 FindSeq,
997 UnicodeForm,
1000 PrimeMeta,
1004 PrimeExponents,
1007 SymbolForm,
1010 Pick { origin: i64 },
1012 Expand,
1015 WriteStream,
1019 SparseForm,
1025 NotYet(&'static str),
1026 None,
1027}
1028
1029#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1031pub enum BoolDyad {
1032 Nor,
1034 Nand,
1036}
1037
1038#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1041pub struct Prim {
1042 pub name: &'static str,
1043 pub monad: MonadOp,
1044 pub dyad: DyadOp,
1045 pub ranks: [i64; 3],
1046}
1047
1048#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1052pub enum WindowKind {
1053 Prefix,
1056 Suffix,
1059 Scan,
1063}
1064
1065#[derive(Clone, Debug, PartialEq, Eq)]
1067pub enum Power {
1068 Times(u64),
1070 Converge,
1072 Each(Vec<u64>),
1075 ConvergeTrace,
1077}
1078
1079const CONVERGE_LIMIT: usize = 1 << 20;
1081
1082pub type MemoCache = Arc<std::sync::Mutex<HashMap<Vec<u64>, Array>>>;
1086
1087#[derive(Clone, Debug)]
1092pub enum Operand {
1093 Func(Box<Verb>),
1094 Value(Box<Array>),
1095}
1096
1097impl Operand {
1098 pub fn name(&self) -> String {
1100 match self {
1101 Operand::Func(v) => v.name(),
1102 Operand::Value(_) => "n".to_string(),
1103 }
1104 }
1105
1106 fn is_value(&self) -> bool {
1107 matches!(self, Operand::Value(_))
1108 }
1109}
1110
1111#[derive(Debug)]
1119pub struct OpDef {
1120 pub readings: [std::result::Result<Verb, String>; 4],
1124}
1125
1126impl OpDef {
1127 pub fn uniform(v: Verb) -> OpDef {
1131 OpDef { readings: [Ok(v.clone()), Ok(v.clone()), Ok(v.clone()), Ok(v)] }
1132 }
1133
1134 pub fn pick(&self, alpha: &Operand, omega: Option<&Operand>) -> Result<&Verb> {
1136 let i = usize::from(alpha.is_value())
1137 | (usize::from(omega.is_some_and(Operand::is_value)) << 1);
1138 self.readings[i].as_ref().map_err(|msg| Error::new(ErrorKind::Parse, msg.clone(), None))
1139 }
1140
1141 fn bodies(&self) -> impl Iterator<Item = &Verb> {
1144 self.readings.iter().filter_map(|r| r.as_ref().ok())
1145 }
1146}
1147
1148#[derive(Clone, Debug)]
1152pub enum Verb {
1153 Prim(Prim),
1154 Rank(Box<Verb>, [i64; 3]),
1156 Reduce(Box<Verb>),
1158 NWise(Box<Verb>),
1163 Windowed(Box<Verb>, WindowKind),
1166 Commute(Box<Verb>),
1168 PowerN(Box<Verb>, Power),
1170 Fork(Box<Verb>, Box<Verb>, Box<Verb>),
1172 NounFork(Array, Box<Verb>, Box<Verb>),
1174 Hook(Box<Verb>, Box<Verb>),
1176 Atop(Box<Verb>, Box<Verb>),
1178 Compose(Box<Verb>, Box<Verb>),
1181 BondLeft(Array, Box<Verb>),
1184 BondRight(Box<Verb>, Array),
1186 Each(Box<Verb>, Enclose),
1189 UnderRavel(Box<Verb>),
1195 Fit(Box<Verb>, f64),
1197 Amend(Array),
1199 AmendVerb(Box<Verb>),
1202 ShiftFill(Array),
1205 Memo(Box<Verb>, MemoCache),
1209 Level { u: Box<Verb>, level: i64, spread: bool },
1213 Characteristics(Box<Verb>),
1216 Before(Box<Verb>, Box<Verb>),
1219 UserDerived { def: Arc<OpDef>, alpha: Operand, omega: Option<Operand> },
1223 KeyPairs(Box<Verb>),
1228 Key(Box<Verb>),
1231 Cut(Box<Verb>, i64),
1233 PowerV(Box<Verb>, Box<Verb>),
1235 PowerUntil(Box<Verb>, Box<Verb>),
1237 AlongAxis(Box<Verb>, usize),
1241 Explicit(Arc<crate::ir::ExplicitDef>),
1245 SelfRef,
1248 Named(String),
1251 WithObverse(Box<Verb>, Box<Verb>),
1254 Agenda(Vec<Verb>, Box<Verb>),
1257 Adverse(Box<Verb>, Box<Verb>),
1261 Hypergeometric { num: Vec<crate::complex::Cx>, den: Vec<crate::complex::Cx> },
1264 Beside(Box<Verb>, Box<Verb>),
1268 Stencil(Box<Verb>, Vec<i64>),
1272 Evoke(Vec<Verb>, i64),
1278 InnerProduct { u: Box<Verb>, v: Box<Verb>, apl: bool },
1285}
1286
1287impl Verb {
1288 pub fn ranks(&self) -> [i64; 3] {
1290 match self {
1291 Verb::Prim(p) => p.ranks,
1292 Verb::Rank(_, r) => *r,
1293 Verb::Windowed(_, WindowKind::Prefix | WindowKind::Suffix) => {
1297 [RANK_INF, 0, RANK_INF]
1298 }
1299 Verb::Each(..) => [0, 0, 0],
1300 Verb::Fit(v, _) => v.ranks(),
1301 Verb::Amend(_)
1304 | Verb::AmendVerb(_)
1305 | Verb::ShiftFill(_)
1306 | Verb::Level { .. }
1307 | Verb::Characteristics(_)
1308 | Verb::UserDerived { .. }
1309 | Verb::KeyPairs(_)
1310 | Verb::Key(_)
1311 | Verb::Cut(..)
1312 | Verb::PowerV(..)
1313 | Verb::PowerUntil(..)
1314 | Verb::AlongAxis(..) => [RANK_INF, RANK_INF, RANK_INF],
1315 Verb::Memo(v, _) => v.ranks(),
1316 Verb::WithObverse(v, _) | Verb::Adverse(v, _) => v.ranks(),
1317 Verb::Beside(..) => [RANK_INF, RANK_INF, RANK_INF],
1318 Verb::Hypergeometric { .. } => [0, 0, 0],
1320 Verb::InnerProduct { .. } => [2, RANK_INF, RANK_INF],
1323 _ => [RANK_INF, RANK_INF, RANK_INF],
1324 }
1325 }
1326
1327 pub fn name(&self) -> String {
1329 match self {
1330 Verb::Prim(p) => p.name.to_string(),
1331 Verb::Rank(v, r) => format!("{}\"{}", v.name(), rank_str(*r)),
1332 Verb::Reduce(v) | Verb::NWise(v) => format!("{}/", v.name()),
1333 Verb::Windowed(v, WindowKind::Suffix) => format!("{}\\.", v.name()),
1334 Verb::Windowed(v, _) => format!("{}\\", v.name()),
1335 Verb::Commute(v) => format!("{}~", v.name()),
1336 Verb::PowerN(v, Power::Converge) => format!("{}^:_", v.name()),
1337 Verb::PowerN(v, Power::Times(n)) => format!("{}^:{n}", v.name()),
1338 Verb::PowerN(v, Power::Each(_)) => format!("{}^:n", v.name()),
1339 Verb::PowerN(v, Power::ConvergeTrace) => format!("{}^:a:", v.name()),
1340 Verb::Fork(f, g, h) => format!("({} {} {})", f.name(), g.name(), h.name()),
1341 Verb::NounFork(_, g, h) => format!("(n {} {})", g.name(), h.name()),
1342 Verb::Hook(f, g) => format!("({} {})", f.name(), g.name()),
1343 Verb::Atop(f, g) => format!("({}@:{})", f.name(), g.name()),
1344 Verb::Compose(f, g) => format!("({}&:{})", f.name(), g.name()),
1345 Verb::BondLeft(_, v) => format!("(n&{})", v.name()),
1346 Verb::BondRight(v, _) => format!("({}&n)", v.name()),
1347 Verb::UnderRavel(v) => format!("({}&.,)", v.name()),
1348 Verb::Each(v, Enclose::Always) => format!("({}&.>)", v.name()),
1349 Verb::Each(v, _) => format!("({}¨)", v.name()),
1350 Verb::Fit(v, n) => format!("{}!.{n}", v.name()),
1351 Verb::Amend(_) => "(m})".to_string(),
1352 Verb::AmendVerb(v) => format!("({}}})", v.name()),
1353 Verb::ShiftFill(_) => "|.!.n".to_string(),
1354 Verb::Characteristics(v) => format!("{} b.", v.name()),
1355 Verb::Before(f, g) => format!("({}⍛{})", f.name(), g.name()),
1356 Verb::KeyPairs(v) => format!("{}⌸", v.name()),
1357 Verb::UserDerived { alpha, omega, .. } => match omega {
1358 Some(g) => format!("({} {{…}} {})", alpha.name(), g.name()),
1359 None => format!("({} {{…}})", alpha.name()),
1360 },
1361 Verb::Memo(v, _) => format!("{} M.", v.name()),
1362 Verb::Level { u, level, spread } => {
1363 format!("{} {} {level}", u.name(), if *spread { "S:" } else { "L:" })
1364 }
1365 Verb::Key(v) => format!("{}/.", v.name()),
1366 Verb::Cut(v, n) => format!("{};.{n}", v.name()),
1367 Verb::PowerV(v, w) => format!("{}^:{}", v.name(), w.name()),
1368 Verb::PowerUntil(v, w) => format!("{}⍣{}", v.name(), w.name()),
1369 Verb::AlongAxis(v, k) => format!("{}[{k}]", v.name()),
1370 Verb::Explicit(d) => d.name.clone(),
1371 Verb::SelfRef => "$:".to_string(),
1372 Verb::Named(n) => n.clone(),
1373 Verb::WithObverse(v, w) => format!("({}:.{})", v.name(), w.name()),
1374 Verb::Adverse(v, w) => format!("({}::{})", v.name(), w.name()),
1375 Verb::Beside(f, g) => format!("({}∘{})", f.name(), g.name()),
1376 Verb::Hypergeometric { num, den } => {
1377 format!("({} H. {})", cx_list(num), cx_list(den))
1378 }
1379 Verb::Agenda(vs, w) => {
1380 let names: Vec<String> = vs.iter().map(Verb::name).collect();
1381 format!("({}@.{})", names.join("`"), w.name())
1382 }
1383 Verb::Evoke(vs, n) => {
1384 let names: Vec<String> = vs.iter().map(Verb::name).collect();
1385 format!("({}`:{n})", names.join("`"))
1386 }
1387 Verb::Stencil(u, w) => {
1388 let sizes: Vec<String> = w.iter().map(i64::to_string).collect();
1389 format!("({}⌺{})", u.name(), sizes.join(" "))
1390 }
1391 Verb::InnerProduct { u, v, .. } => format!("({} . {})", u.name(), v.name()),
1392 }
1393 }
1394
1395 pub fn uses_tolerance(&self) -> bool {
1400 match self {
1401 Verb::Prim(p) => {
1402 matches!(
1403 p.monad,
1404 MonadOp::Scalar(ScalarMonad::Floor)
1405 | MonadOp::Scalar(ScalarMonad::Ceil)
1406 | MonadOp::Nub
1407 | MonadOp::GradeUp { .. }
1408 | MonadOp::GradeDown { .. }
1409 | MonadOp::EncodeBits
1410 ) || matches!(
1411 p.dyad,
1412 DyadOp::Scalar(
1413 ScalarDyad::Eq
1414 | ScalarDyad::Ne
1415 | ScalarDyad::Lt
1416 | ScalarDyad::Le
1417 | ScalarDyad::Gt
1418 | ScalarDyad::Ge
1419 | ScalarDyad::Residue
1420 | ScalarDyad::Gcd
1421 | ScalarDyad::Lcm
1422 ) | DyadOp::Match
1423 | DyadOp::GradeSelect { .. }
1424 | DyadOp::Encode
1425 | DyadOp::EncodeApl
1426 | DyadOp::NotMatch
1427 | DyadOp::MemberJ
1428 | DyadOp::MemberApl
1429 | DyadOp::IndexOf { .. }
1430 | DyadOp::IndexOfLast { .. }
1431 )
1432 }
1433 Verb::Rank(v, _)
1434 | Verb::Reduce(v)
1435 | Verb::NWise(v)
1436 | Verb::Windowed(v, _)
1437 | Verb::Commute(v)
1438 | Verb::PowerN(v, _)
1439 | Verb::BondLeft(_, v)
1440 | Verb::BondRight(v, _)
1441 | Verb::Each(v, _)
1442 | Verb::UnderRavel(v)
1443 | Verb::Fit(v, _)
1444 | Verb::Key(v)
1445 | Verb::Cut(v, _)
1446 | Verb::AlongAxis(v, _) => v.uses_tolerance(),
1447 Verb::PowerV(v, w) | Verb::PowerUntil(v, w) => {
1448 v.uses_tolerance() || w.uses_tolerance()
1449 }
1450 Verb::Amend(_)
1453 | Verb::AmendVerb(_)
1454 | Verb::ShiftFill(_)
1455 | Verb::Characteristics(_)
1456 | Verb::Explicit(_)
1457 | Verb::SelfRef
1458 | Verb::Named(_)
1459 | Verb::Hypergeometric { .. } => false,
1460 Verb::Memo(v, _) | Verb::Level { u: v, .. } => v.uses_tolerance(),
1461 Verb::WithObverse(v, _) => v.uses_tolerance(),
1462 Verb::Adverse(v, w) | Verb::Beside(v, w) | Verb::Before(v, w) => {
1463 v.uses_tolerance() || w.uses_tolerance()
1464 }
1465 Verb::KeyPairs(v) => v.uses_tolerance(),
1466 Verb::UserDerived { def, alpha, omega } => {
1467 let operand = |o: &Operand| match o {
1468 Operand::Func(v) => v.uses_tolerance(),
1469 Operand::Value(_) => false,
1470 };
1471 def.bodies().any(Verb::uses_tolerance)
1472 || operand(alpha)
1473 || omega.as_ref().is_some_and(operand)
1474 }
1475 Verb::Agenda(vs, w) => {
1476 w.uses_tolerance() || vs.iter().any(Verb::uses_tolerance)
1477 }
1478 Verb::Evoke(vs, _) => vs.iter().any(Verb::uses_tolerance),
1479 Verb::Stencil(u, _) => u.uses_tolerance(),
1480 Verb::InnerProduct { u, v, .. } => u.uses_tolerance() || v.uses_tolerance(),
1481 Verb::Fork(f, g, h) => {
1482 f.uses_tolerance() || g.uses_tolerance() || h.uses_tolerance()
1483 }
1484 Verb::NounFork(_, g, h)
1485 | Verb::Hook(g, h)
1486 | Verb::Atop(g, h)
1487 | Verb::Compose(g, h) => g.uses_tolerance() || h.uses_tolerance(),
1488 }
1489 }
1490
1491 pub fn is_pure(&self) -> bool {
1496 match self {
1497 Verb::Prim(p) => {
1500 !matches!(
1501 p.monad,
1502 MonadOp::Echo | MonadOp::Roll { .. } | MonadOp::ReadStream
1503 ) && !matches!(p.dyad, DyadOp::Deal { .. } | DyadOp::WriteStream)
1504 }
1505 Verb::Rank(v, _)
1506 | Verb::Reduce(v)
1507 | Verb::NWise(v)
1508 | Verb::Windowed(v, _)
1509 | Verb::Commute(v)
1510 | Verb::PowerN(v, _) => v.is_pure(),
1511 Verb::Fork(f, g, h) => f.is_pure() && g.is_pure() && h.is_pure(),
1512 Verb::NounFork(_, g, h)
1513 | Verb::Hook(g, h)
1514 | Verb::Atop(g, h)
1515 | Verb::Compose(g, h) => g.is_pure() && h.is_pure(),
1516 Verb::BondLeft(_, v)
1517 | Verb::BondRight(v, _)
1518 | Verb::Each(v, _)
1519 | Verb::UnderRavel(v)
1520 | Verb::Fit(v, _) => v.is_pure(),
1521 Verb::Key(v) | Verb::Cut(v, _) | Verb::AlongAxis(v, _) => v.is_pure(),
1522 Verb::Hypergeometric { .. } => true,
1523 Verb::PowerV(v, w) | Verb::PowerUntil(v, w) => v.is_pure() && w.is_pure(),
1524 Verb::WithObverse(v, _) => v.is_pure(),
1525 Verb::Adverse(v, w) | Verb::Beside(v, w) | Verb::Before(v, w) => {
1526 v.is_pure() && w.is_pure()
1527 }
1528 Verb::KeyPairs(v) => v.is_pure(),
1529 Verb::UserDerived { .. } => false,
1532 Verb::Agenda(vs, w) => w.is_pure() && vs.iter().all(Verb::is_pure),
1533 Verb::Evoke(vs, _) => vs.iter().all(Verb::is_pure),
1534 Verb::Stencil(u, _) => u.is_pure(),
1535 Verb::InnerProduct { u, v, .. } => u.is_pure() && v.is_pure(),
1536 Verb::Amend(_) | Verb::ShiftFill(_) | Verb::Characteristics(_) => true,
1537 Verb::AmendVerb(v) | Verb::Level { u: v, .. } => v.is_pure(),
1538 Verb::Memo(v, _) => v.is_pure(),
1542 Verb::Explicit(_) | Verb::SelfRef | Verb::Named(_) => false,
1547 }
1548 }
1549
1550 fn monad_reads_sparse(&self) -> bool {
1558 let Verb::Prim(p) = self else { return false };
1559 matches!(
1560 p.monad,
1561 MonadOp::Sparse
1562 | MonadOp::ShapeOf
1563 | MonadOp::Tally
1564 | MonadOp::TypeCode
1565 | MonadOp::Format
1566 | MonadOp::Echo
1567 )
1568 }
1569
1570 fn dyad_reads_sparse(&self) -> bool {
1573 matches!(self, Verb::Prim(p) if p.dyad == DyadOp::SparseForm)
1574 }
1575
1576 pub fn monad(&self, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1582 let _depth = Nesting::enter(span)?;
1583 ctx.shy = false;
1589 let dense;
1592 let y = if y.is_sparse() && !self.monad_reads_sparse() {
1593 dense = y.densified();
1594 &dense
1595 } else {
1596 y
1597 };
1598 if y.is_row_major() {
1599 return self.monad_rows(y, ctx, span);
1600 }
1601 match self.monad_columns(y, ctx, span) {
1602 Some(r) => r,
1603 None => self.monad_rows(&y.to_row_major(), ctx, span),
1604 }
1605 }
1606
1607 fn monad_rows(&self, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1610 debug_assert!(y.is_row_major());
1611 match self {
1612 Verb::Prim(p) => {
1613 if let MonadOp::Scalar(op) = p.monad {
1616 return scalar_monad(op, y, ctx.cfg, span);
1617 }
1618 if p.monad == MonadOp::Open && is_mixed_simple(y) {
1622 return Ok(y.clone());
1623 }
1624 let frame_rank = y.rank() - effective_rank(p.ranks[0], y.rank());
1625 if frame_rank == 0 {
1626 return monad_op(p, y, ctx, span);
1627 }
1628 let frame = y.shape[..frame_rank].to_vec();
1629 let n: usize = frame.iter().product();
1630 if n == 0 {
1631 let cell = fill_cell(y, frame_rank, self.is_pure());
1632 return Ok(empty_frame(&frame, y.dtype(), cell, ctx, |cell, c| {
1633 monad_op(p, cell, c, span)
1634 }));
1635 }
1636 let cells = each_cell(n, y.count(), self.is_pure(), ctx, |i, c| {
1637 monad_op(p, &y.cell_at(frame_rank, i), c, span)
1638 })?;
1639 assemble(&frame, cells, span)
1640 }
1641 Verb::Rank(v, r) => {
1642 let frame_rank = y.rank() - effective_rank(r[0], y.rank());
1643 if frame_rank == 0 {
1644 return v.monad(y, ctx, span);
1647 }
1648 if let Some(a) = reduce_vector_cells(v, y, frame_rank) {
1651 return Ok(a);
1652 }
1653 let frame = y.shape[..frame_rank].to_vec();
1654 let n: usize = frame.iter().product();
1655 if n == 0 {
1656 let cell = fill_cell(y, frame_rank, self.is_pure());
1657 return Ok(empty_frame(&frame, y.dtype(), cell, ctx, |cell, c| {
1658 v.monad(cell, c, span)
1659 }));
1660 }
1661 let cells = each_cell(n, y.count(), self.is_pure(), ctx, |i, c| {
1662 v.monad(&y.cell_at(frame_rank, i), c, span)
1663 })?;
1664 assemble(&frame, cells, span)
1665 }
1666 Verb::Reduce(v) | Verb::NWise(v) => reduce(v, y, ctx, span),
1667 Verb::Windowed(v, kind) => {
1668 runs(v, y, *kind == WindowKind::Suffix, ctx, span)
1669 }
1670 Verb::Commute(v) => v.dyad(y, y, ctx, span),
1671 Verb::PowerN(v, p) => power(v, p.clone(), None, y, ctx, span),
1672 Verb::Fork(f, g, h) => {
1673 let l = f.monad(y, ctx, span)?;
1674 let r = h.monad(y, ctx, span)?;
1675 g.dyad(&l, &r, ctx, span)
1676 }
1677 Verb::NounFork(n, g, h) => {
1678 let r = h.monad(y, ctx, span)?;
1679 g.dyad(n, &r, ctx, span)
1680 }
1681 Verb::Hook(f, g) => {
1682 let r = g.monad(y, ctx, span)?;
1683 f.dyad(y, &r, ctx, span)
1684 }
1685 Verb::Atop(f, g) | Verb::Compose(f, g) => {
1686 let r = g.monad(y, ctx, span)?;
1687 f.monad(&r, ctx, span)
1688 }
1689 Verb::BondLeft(m, v) => v.dyad(m, y, ctx, span),
1690 Verb::BondRight(v, n) => v.dyad(y, n, ctx, span),
1691 Verb::Each(u, rule) => {
1692 let n = y.count();
1693 let cells = each_cell(n, n, self.is_pure(), ctx, |i, c| {
1694 let opened = open_cell(&atom(y, i));
1695 Ok(enclose(&u.monad(&opened, c, span)?, *rule))
1696 })?;
1697 assemble(&y.shape, cells, span)
1698 }
1699 Verb::UnderRavel(u) => {
1703 let flat = Array::new(vec![y.count()], y.data.clone());
1704 let r = u.monad(&flat, ctx, span)?;
1705 let shape = Array::from_i64(y.shape.iter().map(|&n| n as i64).collect());
1706 reshape(&shape, &r, false, false, ctx.cfg.near(), span)
1707 }
1708 Verb::Fit(v, n) => {
1709 let tol = Tol { ct: *n, ..ctx.cfg.tol };
1710 ctx.with_tol(tol, |c| v.monad(y, c, span))
1711 }
1712 Verb::Amend(m) => {
1714 if m.rank() != 0 || y.rank() > 1 {
1715 return Err(Error::new(
1716 ErrorKind::Rank,
1717 "selecting with m} takes one index into a list",
1718 Some(span),
1719 ));
1720 }
1721 from_index(m, y, ctx.cfg.near(), span)
1722 }
1723 Verb::AmendVerb(u) => {
1725 let m = u.monad(y, ctx, span)?;
1726 Verb::Amend(m).monad(y, ctx, span)
1727 }
1728 Verb::ShiftFill(fill) => {
1731 shift_fill(&Array::scalar_i64(-1), y, fill, ctx.cfg.near(), span)
1732 }
1733 Verb::Memo(u, cache) => memoised(u, cache, None, y, ctx, span),
1734 Verb::Characteristics(u) => characteristics(u, y, span),
1735 Verb::Before(f, g) => {
1736 let l = f.monad(y, ctx, span)?;
1737 g.dyad(&l, y, ctx, span)
1738 }
1739 Verb::KeyPairs(u) => key_pairs(u, y, None, ctx, span),
1740 Verb::UserDerived { def, alpha, omega } => {
1741 let body = def.pick(alpha, omega.as_ref())?.clone();
1742 with_operands(alpha, omega.as_ref(), ctx, |c| body.monad(y, c, span))
1743 }
1744 Verb::Level { u, level, spread } => {
1745 at_level(u, *level, *spread, y, ctx, span)
1746 }
1747 Verb::Key(u) => oblique(u, y, ctx, span),
1748 Verb::Cut(u, n) => cut(u, None, y, *n, ctx, span),
1749 Verb::PowerV(u, v) => power_v(u, v, None, y, ctx, span),
1750 Verb::PowerUntil(u, v) => power_until(u, v, y, ctx, span),
1751 Verb::AlongAxis(u, k) => along_axis(u, None, y, *k, ctx, span),
1752 Verb::Explicit(d) => crate::ir::call_explicit(d, None, y, ctx, span),
1753 Verb::SelfRef => {
1754 let d = self_ref(ctx, span)?;
1755 crate::ir::call_explicit(&d, None, y, ctx, span)
1756 }
1757 Verb::Named(n) => named_verb(ctx, n, span)?.monad(y, ctx, span),
1758 Verb::WithObverse(v, _) => v.monad(y, ctx, span),
1759 Verb::Adverse(v, w) => match v.monad(y, ctx, span) {
1760 Err(e) if e.kind != ErrorKind::NotYet => w.monad(y, ctx, span),
1761 other => other,
1762 },
1763 Verb::Beside(f, g) => {
1764 let r = g.monad(y, ctx, span)?;
1765 f.monad(&r, ctx, span)
1766 }
1767 Verb::Hypergeometric { num, den } => hypergeometric(num, den, y, span),
1768 Verb::Agenda(vs, w) => {
1769 agenda_pick(vs, w, None, y, ctx, span)?.monad(y, ctx, span)
1770 }
1771 Verb::Evoke(vs, n) => evoke(vs, *n, None, y, ctx, span),
1772 Verb::Stencil(u, w) => stencil(u, w, y, ctx, span),
1773 Verb::InnerProduct { u, v, apl } => determinant(u, v, *apl, y, ctx, span),
1774 }
1775 }
1776
1777 fn monad_columns(&self, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Option<Result<Array>> {
1786 debug_assert!(!y.is_row_major());
1787 match self {
1788 Verb::Prim(p) => match p.monad {
1789 MonadOp::Scalar(op) => Some(scalar_monad(op, y, ctx.cfg, span)),
1792 MonadOp::ShapeOf | MonadOp::Tally => Some(monad_op(p, y, ctx, span)),
1794 MonadOp::TransposeAxes => Some(Ok(transpose_axes(y))),
1798 _ => None,
1799 },
1800 Verb::Reduce(v) | Verb::NWise(v) => reduce_columns(v, y).map(Ok),
1803 Verb::Rank(v, r) => {
1806 if y.rank() != effective_rank(r[0], y.rank()) + 1 {
1807 return None;
1808 }
1809 reduce_rows_columns(v, y).map(Ok)
1810 }
1811 _ => None,
1812 }
1813 }
1814
1815 pub fn dyad(&self, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1821 let _depth = Nesting::enter(span)?;
1822 ctx.shy = false;
1824 let (dense_x, dense_y);
1827 let x = if x.is_sparse() {
1828 dense_x = x.densified();
1829 &dense_x
1830 } else {
1831 x
1832 };
1833 let y = if y.is_sparse() && !self.dyad_reads_sparse() {
1834 dense_y = y.densified();
1835 &dense_y
1836 } else {
1837 y
1838 };
1839 if x.is_row_major() && y.is_row_major() {
1840 return self.dyad_rows(x, y, ctx, span);
1841 }
1842 if let Some(layout) = self.elementwise_layout(x, y) {
1843 return Ok(self.dyad_rows(x, y, ctx, span)?.with_layout(layout));
1844 }
1845 self.dyad_rows(&x.to_row_major(), &y.to_row_major(), ctx, span)
1846 }
1847
1848 fn elementwise_layout(&self, x: &Array, y: &Array) -> Option<Layout> {
1853 let Verb::Prim(p) = self else { return None };
1854 if !matches!(p.dyad, DyadOp::Scalar(_)) {
1855 return None;
1856 }
1857 if x.rank() == 0 {
1858 return Some(y.layout());
1859 }
1860 if y.rank() == 0 {
1861 return Some(x.layout());
1862 }
1863 (x.shape == y.shape && x.layout() == y.layout()).then(|| x.layout())
1864 }
1865
1866 fn dyad_rows(&self, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1870 match self {
1871 Verb::Prim(_) | Verb::Rank(_, _) | Verb::Each(..) => {
1872 self.dyad_ranked(x, y, ctx, span)
1873 }
1874 Verb::Windowed(_, WindowKind::Prefix | WindowKind::Suffix) => {
1877 self.dyad_ranked(x, y, ctx, span)
1878 }
1879 Verb::Windowed(_, WindowKind::Scan) => {
1880 Err(Error::not_yet("dyadic scan (x f\\ y)", span))
1881 }
1882 Verb::Commute(v) => v.dyad(y, x, ctx, span),
1883 Verb::PowerN(v, p) => power(v, p.clone(), Some(x), y, ctx, span),
1884 Verb::Reduce(v) => table(v, x, y, ctx, span),
1886 Verb::NWise(v) => nwise(v, x, y, ctx, span),
1888 Verb::Fork(f, g, h) => {
1889 let l = f.dyad(x, y, ctx, span)?;
1890 let r = h.dyad(x, y, ctx, span)?;
1891 g.dyad(&l, &r, ctx, span)
1892 }
1893 Verb::NounFork(n, g, h) => {
1894 let r = h.dyad(x, y, ctx, span)?;
1895 g.dyad(n, &r, ctx, span)
1896 }
1897 Verb::Hook(f, g) => {
1898 let r = g.monad(y, ctx, span)?;
1899 f.dyad(x, &r, ctx, span)
1900 }
1901 Verb::Atop(f, g) => {
1902 let r = g.dyad(x, y, ctx, span)?;
1903 f.monad(&r, ctx, span)
1904 }
1905 Verb::Compose(f, g) => {
1906 let l = g.monad(x, ctx, span)?;
1907 let r = g.monad(y, ctx, span)?;
1908 f.dyad(&l, &r, ctx, span)
1909 }
1910 Verb::Fit(v, n) => {
1911 let tol = Tol { ct: *n, ..ctx.cfg.tol };
1912 ctx.with_tol(tol, |c| v.dyad(x, y, c, span))
1913 }
1914 Verb::Amend(m) => amend(m, x, y, ctx.cfg.near(), span),
1915 Verb::AmendVerb(u) => {
1917 let m = u.dyad(x, y, ctx, span)?;
1918 amend(&m, x, y, ctx.cfg.near(), span)
1919 }
1920 Verb::ShiftFill(fill) => shift_fill(x, y, fill, ctx.cfg.near(), span),
1921 Verb::Memo(u, cache) => memoised(u, cache, Some(x), y, ctx, span),
1922 Verb::Characteristics(_) => {
1923 Err(Error::domain("u b. has no dyadic meaning", span))
1924 }
1925 Verb::Before(f, g) => {
1926 let l = f.monad(x, ctx, span)?;
1927 g.dyad(&l, y, ctx, span)
1928 }
1929 Verb::KeyPairs(u) => key_pairs(u, x, Some(y), ctx, span),
1930 Verb::UserDerived { def, alpha, omega } => {
1931 let body = def.pick(alpha, omega.as_ref())?.clone();
1932 with_operands(alpha, omega.as_ref(), ctx, |c| body.dyad(x, y, c, span))
1933 }
1934 Verb::Level { u, level, spread } => {
1935 at_level_dyad(u, *level, *spread, x, y, ctx, span)
1936 }
1937 Verb::Key(u) => key(u, x, y, ctx, span),
1938 Verb::Cut(u, n) => cut(u, Some(x), y, *n, ctx, span),
1939 Verb::PowerV(u, v) => power_v(u, v, Some(x), y, ctx, span),
1940 Verb::PowerUntil(..) => {
1941 Err(Error::not_yet("dyadic power with a function operand (x f⍣g y)", span))
1942 }
1943 Verb::AlongAxis(u, k) => along_axis(u, Some(x), y, *k, ctx, span),
1944 Verb::Explicit(d) => crate::ir::call_explicit(d, Some(x), y, ctx, span),
1945 Verb::SelfRef => {
1946 let d = self_ref(ctx, span)?;
1947 crate::ir::call_explicit(&d, Some(x), y, ctx, span)
1948 }
1949 Verb::Named(n) => named_verb(ctx, n, span)?.dyad(x, y, ctx, span),
1950 Verb::WithObverse(v, _) => v.dyad(x, y, ctx, span),
1951 Verb::Adverse(v, w) => match v.dyad(x, y, ctx, span) {
1952 Err(e) if e.kind != ErrorKind::NotYet => w.dyad(x, y, ctx, span),
1953 other => other,
1954 },
1955 Verb::Beside(f, g) => {
1956 let r = g.monad(y, ctx, span)?;
1957 f.dyad(x, &r, ctx, span)
1958 }
1959 Verb::Hypergeometric { .. } => {
1960 Err(Error::domain("m H. n has no dyadic meaning", span))
1961 }
1962 Verb::Agenda(vs, w) => {
1963 agenda_pick(vs, w, Some(x), y, ctx, span)?.dyad(x, y, ctx, span)
1964 }
1965 Verb::Evoke(vs, n) => evoke(vs, *n, Some(x), y, ctx, span),
1966 Verb::InnerProduct { u, v, apl } => inner_product(u, v, *apl, x, y, ctx, span),
1967 Verb::Stencil(..) => {
1968 Err(Error::domain("f⌺w has no dyadic meaning", span))
1969 }
1970 Verb::BondLeft(..) | Verb::BondRight(..) | Verb::UnderRavel(_) => {
1972 Err(Error::domain(format!("{} has no dyadic meaning", self.name()), span))
1973 }
1974 }
1975 }
1976
1977 fn dyad_ranked(&self, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
1979 let ranks = self.ranks();
1980 let er_l = effective_rank(ranks[1], x.rank());
1981 let er_r = effective_rank(ranks[2], y.rank());
1982 if er_l == 0 && er_r == 0 {
1983 if let Some(op) = self.scalar_dyad_op() {
1986 return scalar_dyad(op, x, y, ctx.cfg, span);
1987 }
1988 }
1989 let fxl = x.rank() - er_l;
1990 let fyl = y.rank() - er_r;
1991 let p = agree(&x.shape[..fxl], &y.shape[..fyl], &x.shape, &y.shape, ctx.cfg.agreement, span)?;
1992 if p.frame.is_empty() {
1993 return self.dyad_cell(x, y, ctx, span);
1994 }
1995 if p.n == 0 {
1996 let right = fill_cell(y, fyl, self.is_pure());
1997 let cell = fill_cell(x, fxl, self.is_pure()).filter(|_| right.is_some());
1998 return Ok(empty_frame(&p.frame, y.dtype(), cell, ctx, |left, c| {
1999 let right = right.as_ref().expect("a left fill cell comes with a right one");
2000 self.dyad_cell(left, right, c, span)
2001 }));
2002 }
2003 let work = x.count().max(y.count());
2004 let cells = each_cell(p.n, work, self.is_pure(), ctx, |i, c| {
2005 let xc = x.cell_at(fxl, i / p.x_div);
2006 let yc = y.cell_at(fyl, i / p.y_div);
2007 self.dyad_cell(&xc, &yc, c, span)
2008 })?;
2009 assemble(&p.frame, cells, span)
2010 }
2011
2012 fn dyad_cell(&self, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
2014 match self {
2015 Verb::Prim(p) if p.dyad == DyadOp::WriteStream => {
2018 stream_number(y, 2, "1!:2 writes", span)?;
2019 (ctx.out)(&format!("{}\n", crate::fmt::format_array(x, &ctx.cfg.fmt)));
2020 Ok(x.clone())
2021 }
2022 Verb::Prim(p) => dyad_op(p, x, y, ctx.cfg, span),
2023 Verb::Rank(v, _) => v.dyad(x, y, ctx, span),
2024 Verb::Windowed(v, WindowKind::Suffix) => outfix(v, x, y, ctx, span),
2026 Verb::Windowed(v, _) => infix(v, x, y, ctx, span),
2027 Verb::Each(u, rule) => {
2028 let r = u.dyad(&open_cell(x), &open_cell(y), ctx, span)?;
2029 Ok(enclose(&r, *rule))
2030 }
2031 _ => Err(Error::internal("dyad_cell on a verb without cell ranks")),
2032 }
2033 }
2034
2035 fn scalar_dyad_op(&self) -> Option<ScalarDyad> {
2038 match self {
2039 Verb::Prim(p) => match p.dyad {
2040 DyadOp::Scalar(op) => Some(op),
2041 _ => None,
2042 },
2043 Verb::Rank(v, _) => v.scalar_dyad_op(),
2044 _ => None,
2045 }
2046 }
2047}
2048
2049pub fn effective_rank(r: i64, arg_rank: usize) -> usize {
2052 if r >= 0 {
2053 (r as usize).min(arg_rank)
2054 } else {
2055 arg_rank.saturating_sub(r.unsigned_abs() as usize)
2056 }
2057}
2058
2059fn self_ref(ctx: &Ctx<'_>, span: Span) -> Result<Arc<crate::ir::ExplicitDef>> {
2069 ctx.env.current_def().ok_or_else(|| {
2070 Error::new(
2071 ErrorKind::Value,
2072 "self-reference outside an explicit definition",
2073 Some(span),
2074 )
2075 })
2076}
2077
2078fn named_verb(ctx: &Ctx<'_>, name: &str, span: Span) -> Result<Verb> {
2080 ctx.env.verb(name).cloned().ok_or_else(|| {
2081 Error::new(ErrorKind::Value, format!("undefined verb: {name}"), Some(span))
2082 })
2083}
2084
2085fn each_cell<F>(
2086 n: usize,
2087 work: usize,
2088 pure: bool,
2089 ctx: &mut Ctx<'_>,
2090 f: F,
2091) -> Result<Vec<Array>>
2092where
2093 F: Fn(usize, &mut Ctx<'_>) -> Result<Array> + Sync + Send,
2094{
2095 if pure && n > 1 && par::worth_it(work) {
2096 let cfg = ctx.cfg;
2097 return par::map_indexed(n, |i| cfg.pure(|c| f(i, c))).into_iter().collect();
2098 }
2099 (0..n).map(|i| f(i, ctx)).collect()
2100}
2101
2102fn one_rank(r: i64) -> String {
2105 if r == RANK_INF { "_".to_string() } else { r.to_string() }
2106}
2107
2108fn rank_str(r: [i64; 3]) -> String {
2111 if r[0] == r[1] && r[1] == r[2] {
2112 one_rank(r[0])
2113 } else {
2114 format!("{} {} {}", one_rank(r[0]), one_rank(r[1]), one_rank(r[2]))
2115 }
2116}
2117
2118fn show_shape(shape: &[usize]) -> String {
2120 if shape.is_empty() {
2121 return "(scalar)".to_string();
2122 }
2123 shape.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(" ")
2124}
2125
2126fn strides(shape: &[usize]) -> Vec<usize> {
2130 let mut s = vec![1usize; shape.len()];
2131 for k in (0..shape.len().saturating_sub(1)).rev() {
2132 s[k] = s[k + 1] * shape[k + 1];
2133 }
2134 s
2135}
2136
2137fn odometer(coord: &mut [usize], shape: &[usize]) {
2139 for k in (0..coord.len()).rev() {
2140 coord[k] += 1;
2141 if coord[k] < shape[k] {
2142 return;
2143 }
2144 coord[k] = 0;
2145 }
2146}
2147
2148fn push_elem(dst: &mut Data, src: &Data, i: usize) {
2150 match (dst, src) {
2151 (Data::Bool(a), Data::Bool(b)) => a.push(b[i]),
2152 (Data::I64(a), Data::I64(b)) => a.push(b[i]),
2153 (Data::Ext(a), Data::Ext(b)) => a.push(b[i].clone()),
2154 (Data::Rat(a), Data::Rat(b)) => a.push(b[i].clone()),
2155 (Data::F64(a), Data::F64(b)) => a.push(b[i]),
2156 (Data::Complex(a), Data::Complex(b)) => a.push(b[i]),
2157 (Data::Char(a), Data::Char(b)) => a.push(b[i]),
2158 (Data::Symbol(a), Data::Symbol(b)) => a.push(b[i]),
2159 (Data::Box(a), Data::Box(b)) => a.push(b[i].clone()),
2160 _ => debug_assert!(false, "push_elem across dtypes"),
2161 }
2162}
2163
2164fn fill_data(dtype: DType, n: usize) -> Data {
2166 let mut d = Data::empty(dtype);
2167 for _ in 0..n {
2168 d.push_fill();
2169 }
2170 d
2171}
2172
2173const FILL_CELL_LIMIT: usize = 1 << 20;
2175
2176fn fill_cell(y: &Array, frame_rank: usize, pure: bool) -> Option<Array> {
2187 if !pure {
2188 return None;
2189 }
2190 if y.shape[..frame_rank].iter().all(|&d| d != 0) {
2191 return Some(y.cell_at(frame_rank, 0));
2192 }
2193 let shape = y.shape[frame_rank..].to_vec();
2194 let n: usize = shape.iter().product();
2195 if n > FILL_CELL_LIMIT {
2196 return None;
2197 }
2198 let fill = y.proto().cloned();
2201 let mut data = Data::empty(y.dtype());
2202 for _ in 0..n {
2203 push_gap(&mut data, &fill);
2204 }
2205 Some(Array::new(shape, data))
2206}
2207
2208fn empty_frame(
2218 frame: &[usize],
2219 dtype: DType,
2220 cell: Option<Array>,
2221 ctx: &mut Ctx<'_>,
2222 run: impl FnOnce(&Array, &mut Ctx<'_>) -> Result<Array>,
2223) -> Array {
2224 let mut shape = frame.to_vec();
2225 if let Some(cell) = cell
2226 && let Ok(answer) = run(&cell, ctx)
2227 {
2228 shape.extend_from_slice(&answer.shape);
2229 return Array::new(shape, Data::empty(answer.dtype()));
2230 }
2231 Array::new(shape, Data::empty(dtype))
2232}
2233
2234struct Pairing {
2239 frame: Vec<usize>,
2240 n: usize,
2241 x_div: usize,
2242 y_div: usize,
2243}
2244
2245fn frame_mismatch(
2246 xs: &[usize],
2247 ys: &[usize],
2248 fx: &[usize],
2249 fy: &[usize],
2250 axis: usize,
2251 span: Span,
2252) -> Error {
2253 let kind = if fx.len() == 1 && fy.len() == 1 { ErrorKind::Length } else { ErrorKind::Shape };
2256 let note = if axis < fx.len() && axis < fy.len() {
2257 format!("frames first differ at axis {axis}: {} vs {}", fx[axis], fy[axis])
2258 } else {
2259 format!(
2260 "frames have different numbers of axes: {} vs {}, diverging at axis {axis}",
2261 fx.len(),
2262 fy.len()
2263 )
2264 };
2265 Error::new(
2266 kind,
2267 format!(
2268 "arguments do not agree: left shape {}, right shape {}",
2269 show_shape(xs),
2270 show_shape(ys)
2271 ),
2272 Some(span),
2273 )
2274 .note(note)
2275}
2276
2277fn agree(
2280 fx: &[usize],
2281 fy: &[usize],
2282 xs: &[usize],
2283 ys: &[usize],
2284 mode: Agreement,
2285 span: Span,
2286) -> Result<Pairing> {
2287 let common = fx.len().min(fy.len());
2288 match mode {
2289 Agreement::LeadingPrefix => {
2290 for i in 0..common {
2291 if fx[i] != fy[i] {
2292 return Err(frame_mismatch(xs, ys, fx, fy, i, span));
2293 }
2294 }
2295 let (long, short) = if fx.len() >= fy.len() { (fx, fy) } else { (fy, fx) };
2296 let n: usize = long.iter().product();
2297 let surplus: usize = long[short.len()..].iter().product();
2298 let (x_div, y_div) =
2299 if fx.len() >= fy.len() { (1, surplus.max(1)) } else { (surplus.max(1), 1) };
2300 Ok(Pairing { frame: long.to_vec(), n, x_div, y_div })
2301 }
2302 Agreement::ExactOrScalar => {
2303 if fx == fy {
2304 let n: usize = fx.iter().product();
2305 return Ok(Pairing { frame: fx.to_vec(), n, x_div: 1, y_div: 1 });
2306 }
2307 let one = |f: &[usize]| f.iter().product::<usize>() == 1;
2314 if fx.is_empty() || (one(fx) && !fy.is_empty()) {
2315 let n: usize = fy.iter().product();
2316 return Ok(Pairing { frame: fy.to_vec(), n, x_div: n.max(1), y_div: 1 });
2317 }
2318 if fy.is_empty() || one(fy) {
2319 let n: usize = fx.iter().product();
2320 return Ok(Pairing { frame: fx.to_vec(), n, x_div: 1, y_div: n.max(1) });
2321 }
2322 let axis = (0..common).find(|&i| fx[i] != fy[i]).unwrap_or(common);
2323 Err(frame_mismatch(xs, ys, fx, fy, axis, span))
2324 }
2325 }
2326}
2327
2328fn assemble_items(frame: &[usize], mut cells: Vec<Array>, span: Span) -> Result<Array> {
2337 let boxes = cells.iter().filter(|c| c.dtype() == DType::Box).count();
2338 if boxes > 0 && boxes < cells.len() {
2339 for c in &mut cells {
2340 if c.dtype() != DType::Box {
2341 *c = boxed_elements(c);
2342 }
2343 }
2344 }
2345 assemble(frame, cells, span)
2346}
2347
2348fn boxed_elements(a: &Array) -> Array {
2351 let row = a.to_row_major();
2352 let held: Vec<Array> = (0..row.count()).map(|i| atom(&row, i)).collect();
2353 Array::new(row.shape.clone(), Data::Box(held.into()))
2354}
2355
2356fn assemble(frame: &[usize], cells: Vec<Array>, span: Span) -> Result<Array> {
2363 if cells.is_empty() {
2364 return Ok(Array::new(frame.to_vec(), Data::empty(DType::I64)));
2367 }
2368 let cells: Vec<Array> =
2369 if cells.iter().all(Array::is_row_major) {
2370 cells
2371 } else {
2372 cells.iter().map(Array::to_row_major).collect()
2373 };
2374 let mut dt = cells.iter().find(|c| c.count() > 0).unwrap_or(&cells[0]).dtype();
2380 for c in &cells {
2381 if c.count() == 0 {
2382 continue;
2383 }
2384 dt = DType::promote(dt, c.dtype()).ok_or_else(|| {
2385 let boxed = dt == DType::Box || c.dtype() == DType::Box;
2386 let what = if boxed {
2387 "cannot frame boxed and unboxed results into one array"
2388 } else {
2389 "cannot frame character and numeric results into one array"
2390 };
2391 Error::new(ErrorKind::Type, what, Some(span))
2392 })?;
2393 }
2394 if cells.iter().all(|c| c.count() == 0) {
2395 for c in &cells {
2396 dt = DType::promote(dt, c.dtype()).unwrap_or(match (dt, c.dtype()) {
2397 (DType::Box, _) | (_, DType::Box) => DType::Box,
2398 _ => DType::Char,
2399 });
2400 }
2401 }
2402 let widen = |c: &Array| -> Result<Data> {
2403 if c.count() == 0 {
2404 return Ok(Data::empty(dt));
2405 }
2406 c.data.cast(dt).ok_or_else(|| Error::internal("unsupported widening while framing"))
2407 };
2408
2409 if cells[1..].iter().all(|c| c.shape == cells[0].shape) {
2410 let mut data = Data::empty(dt);
2411 for c in &cells {
2412 if c.dtype() == dt {
2413 data.extend_from(&c.data);
2414 } else {
2415 data.extend_from(&widen(c)?);
2416 }
2417 }
2418 let mut shape = frame.to_vec();
2419 shape.extend_from_slice(&cells[0].shape);
2420 return Ok(Array::new(shape, data));
2421 }
2422
2423 let crank = cells.iter().map(|c| c.rank()).max().unwrap_or(0);
2426 let padded: Vec<Vec<usize>> = cells
2427 .iter()
2428 .map(|c| {
2429 let mut s = vec![1usize; crank - c.rank()];
2430 s.extend_from_slice(&c.shape);
2431 s
2432 })
2433 .collect();
2434 let mut common = vec![0usize; crank];
2435 for s in &padded {
2436 for k in 0..crank {
2437 common[k] = common[k].max(s[k]);
2438 }
2439 }
2440 let cell_n: usize = common.iter().product();
2441 let mut data = Data::empty(dt);
2442 for (c, ps) in cells.iter().zip(&padded) {
2443 let cd = if c.dtype() == dt { c.data.clone() } else { widen(c)? };
2444 let st = strides(ps);
2445 let mut coord = vec![0usize; crank];
2446 for _ in 0..cell_n {
2447 let mut idx = 0usize;
2448 let mut inside = true;
2449 for k in 0..crank {
2450 if coord[k] >= ps[k] {
2451 inside = false;
2452 break;
2453 }
2454 idx += coord[k] * st[k];
2455 }
2456 if inside {
2457 push_elem(&mut data, &cd, idx);
2458 } else {
2459 data.push_fill();
2460 }
2461 odometer(&mut coord, &common);
2462 }
2463 }
2464 let mut shape = frame.to_vec();
2465 shape.extend_from_slice(&common);
2466 Ok(Array::new(shape, data))
2467}
2468
2469fn atom(a: &Array, i: usize) -> Array {
2474 debug_assert!(a.is_row_major(), "an atom out of a column-major buffer");
2475 Array::new(Vec::new(), a.data.slice(i, i + 1))
2476}
2477
2478fn enclose(y: &Array, rule: Enclose) -> Array {
2480 if rule == Enclose::ExceptSimpleScalar && y.rank() == 0 && y.dtype() != DType::Box {
2481 return y.clone();
2482 }
2483 Array::boxed(y.clone())
2484}
2485
2486pub(crate) fn open_cell(y: &Array) -> Array {
2494 match &y.data {
2495 Data::Box(v) if !v.is_empty() => v[0].to_row_major(),
2496 _ => y.clone(),
2497 }
2498}
2499
2500fn first(y: &Array) -> Array {
2503 if y.count() == 0 {
2504 if let Some(p) = y.proto() {
2507 return p.clone();
2508 }
2509 let mut d = Data::empty(y.dtype());
2510 d.push_fill();
2511 return open_cell(&Array::new(Vec::new(), d));
2512 }
2513 open_cell(&atom(y, 0))
2514}
2515
2516fn depth(y: &Array) -> i64 {
2518 match &y.data {
2519 Data::Box(v) => 1 + v.iter().map(depth).max().unwrap_or(0),
2520 _ => i64::from(y.rank() > 0),
2521 }
2522}
2523
2524fn uniform(y: &Array) -> bool {
2528 let Data::Box(v) = &y.data else { return true };
2529 let Some(head) = v.first() else { return true };
2530 let d = depth(head);
2531 v.iter().all(|b| depth(b) == d && uniform(b))
2532}
2533
2534fn leaves(a: &Array, out: &mut Vec<Array>) {
2540 let a = a.to_row_major();
2541 match &a.data {
2542 Data::Box(v) => {
2543 for b in v.iter() {
2544 leaves(b, out);
2545 }
2546 }
2547 _ => out.push(a),
2548 }
2549}
2550
2551fn enlist(y: &Array, _span: Span) -> Result<Array> {
2554 let mut parts = Vec::new();
2555 leaves(y, &mut parts);
2556 let mut dt = None;
2559 let mut mixing = false;
2560 for p in parts.iter().filter(|p| p.count() > 0) {
2561 dt = Some(match dt {
2562 None => p.dtype(),
2563 Some(t) => match DType::promote(t, p.dtype()) {
2564 Some(t) => t,
2565 None => {
2566 mixing = true;
2567 break;
2568 }
2569 },
2570 });
2571 }
2572 if mixing {
2573 let mut cells: Vec<Array> = Vec::new();
2574 for p in &parts {
2575 let p = p.to_row_major();
2576 cells.extend((0..p.count()).map(|i| atom(&p, i)));
2577 }
2578 return Ok(Array::new(vec![cells.len()], Data::Box(cells.into())));
2579 }
2580 let dt = dt.unwrap_or(DType::I64);
2581 let mut data = Data::empty(dt);
2582 for p in &parts {
2583 let cast = p.data.cast(dt).ok_or_else(|| Error::internal("unsupported widening in enlist"))?;
2584 data.extend_from(&cast);
2585 }
2586 Ok(Array::new(vec![data.len()], data))
2587}
2588
2589fn spread(a: &Array, shape: &[usize]) -> Array {
2591 let n: usize = shape.iter().product();
2592 let mut data = Data::empty(a.dtype());
2593 for _ in 0..n {
2594 push_elem(&mut data, &a.data, 0);
2595 }
2596 Array::new(shape.to_vec(), data)
2597}
2598
2599fn wider_shape(a: &[usize], b: &[usize]) -> Vec<usize> {
2602 let r = a.len().max(b.len());
2603 let pad = |s: &[usize]| {
2604 let mut v = vec![1usize; r - s.len()];
2605 v.extend_from_slice(s);
2606 v
2607 };
2608 let (pa, pb) = (pad(a), pad(b));
2609 (0..r).map(|k| pa[k].max(pb[k])).collect()
2610}
2611
2612fn raze(y: &Array, span: Span) -> Result<Array> {
2617 let opened: Vec<Array> = (0..y.count()).map(|i| open_cell(&atom(y, i))).collect();
2618 let mut common: Option<Vec<usize>> = None;
2619 for a in opened.iter().filter(|a| a.rank() > 0) {
2620 common = Some(match common {
2621 None => a.shape[1..].to_vec(),
2622 Some(c) => wider_shape(&c, &a.shape[1..]),
2623 });
2624 }
2625 let common = common.unwrap_or_default();
2626 let mut cells: Vec<Array> = Vec::new();
2627 for a in &opened {
2628 if a.rank() == 0 {
2629 cells.push(spread(a, &common));
2630 continue;
2631 }
2632 for i in 0..a.items() {
2633 cells.push(a.item(i));
2634 }
2635 }
2636 if cells.is_empty() {
2637 return Ok(Array::new(vec![0], Data::empty(DType::I64)));
2638 }
2639 let n = cells.len();
2640 assemble(&[n], cells, span)
2641}
2642
2643fn link(x: &Array, y: &Array, span: Span) -> Result<Array> {
2646 let head = Array::boxed(x.clone());
2647 let tail = if y.dtype() == DType::Box { y.clone() } else { Array::boxed(y.clone()) };
2648 catenate(&head, &tail, true, false, span)
2649}
2650
2651fn nest_like(a: &Array, other: &Array) -> Array {
2654 if a.dtype() == DType::Box || other.dtype() != DType::Box {
2655 return a.clone();
2656 }
2657 let cells: Vec<Array> = (0..a.count()).map(|i| atom(a, i)).collect();
2658 Array::new(a.shape.clone(), Data::Box(cells.into()))
2659}
2660
2661fn spread_scalars(a: &Array) -> Array {
2667 if a.dtype() == DType::Box {
2668 return a.clone();
2669 }
2670 let a = a.to_row_major();
2671 let cells: Vec<Array> = (0..a.count()).map(|i| atom(&a, i)).collect();
2672 Array::new(a.shape.clone(), Data::Box(cells.into()))
2673}
2674
2675fn holds_scalar_boxes(a: &Array) -> bool {
2678 match a.as_boxes() {
2679 Some(items) => {
2680 !items.is_empty() && items.iter().all(|b| b.rank() == 0 && b.dtype() != DType::Box)
2681 }
2682 None => false,
2683 }
2684}
2685
2686fn tightened_mixed(a: Array) -> Array {
2693 let common = match a.as_boxes() {
2694 Some(items) if holds_scalar_boxes(&a) => {
2695 let mut t = items[0].dtype();
2696 let mut ok = true;
2697 for b in &items[1..] {
2698 match DType::promote(t, b.dtype()) {
2699 Some(next) => t = next,
2700 None => {
2701 ok = false;
2702 break;
2703 }
2704 }
2705 }
2706 ok.then_some(t)
2707 }
2708 _ => None,
2709 };
2710 let Some(common) = common else { return a };
2711 let mut data = Data::empty(common);
2712 for b in a.as_boxes().expect("checked above") {
2713 match b.data.cast(common) {
2714 Some(widened) => push_elem(&mut data, &widened, 0),
2715 None => return a.clone(),
2716 }
2717 }
2718 Array::new(a.shape.clone(), data)
2719}
2720
2721fn align_mixed(x: &Array, y: &Array, apl: bool) -> (Array, Array) {
2726 if apl && holds_scalar_boxes(x) && y.dtype() != DType::Box {
2727 return (x.clone(), spread_scalars(y));
2728 }
2729 if apl && holds_scalar_boxes(y) && x.dtype() != DType::Box {
2730 return (spread_scalars(x), y.clone());
2731 }
2732 (x.clone(), y.clone())
2733}
2734
2735fn box_items(y: &Array) -> Array {
2737 if y.dtype() == DType::Box {
2738 return y.clone();
2739 }
2740 let n = y.items();
2741 let boxes: Vec<Array> = (0..n).map(|i| item_or_self(y, i)).collect();
2742 Array::new(vec![n], Data::Box(boxes.into()))
2743}
2744
2745fn strand(x: &Array, y: &Array, span: Span) -> Result<Array> {
2749 let item = enclose(x, Enclose::ExceptSimpleScalar);
2750 let one = |a: &Array| Array::new(vec![1], a.data.clone());
2751 if item.dtype() != DType::Box
2756 && y.dtype() != DType::Box
2757 && DType::promote(item.dtype(), y.dtype()).is_some()
2758 {
2759 return catenate(&one(&item), y, true, false, span);
2760 }
2761 let head = if item.dtype() == DType::Box { item } else { Array::boxed(item) };
2762 catenate(&one(&head), &box_items(y), true, false, span)
2763}
2764
2765fn char_arith(span: Span) -> Error {
2768 Error::new(ErrorKind::Type, "cannot do arithmetic on characters", Some(span))
2769}
2770
2771fn symbol_arith(span: Span) -> Error {
2772 Error::new(
2773 ErrorKind::Type,
2774 "cannot do arithmetic on symbols; `5 s:` gives their names back",
2775 Some(span),
2776 )
2777}
2778
2779fn box_arith(span: Span) -> Error {
2780 Error::new(
2781 ErrorKind::Type,
2782 "cannot do arithmetic on boxed values; open them first (J `>`, APL `⊃`)",
2783 Some(span),
2784 )
2785}
2786
2787fn wrong_type(d: DType, span: Span) -> Error {
2790 match d {
2791 DType::Box => box_arith(span),
2792 DType::Symbol => symbol_arith(span),
2793 _ => char_arith(span),
2794 }
2795}
2796
2797fn borrow_i64<'a>(d: &'a Data, tmp: &'a mut Vec<i64>) -> &'a [i64] {
2803 match d {
2804 Data::I64(v) => v,
2805 Data::Bool(v) => {
2806 *tmp = par::map(v, |&b| b as i64);
2807 &tmp[..]
2808 }
2809 _ => &[],
2811 }
2812}
2813
2814fn borrow_f64<'a>(d: &'a Data, tmp: &'a mut Vec<f64>) -> &'a [f64] {
2816 match d {
2817 Data::F64(v) => v,
2818 Data::I64(v) => {
2819 *tmp = par::map(v, |&x| x as f64);
2820 &tmp[..]
2821 }
2822 Data::Bool(v) => {
2823 *tmp = par::map(v, |&x| x as f64);
2824 &tmp[..]
2825 }
2826 Data::Ext(v) => {
2827 *tmp = par::map(v, exact::ext_to_f64);
2828 &tmp[..]
2829 }
2830 Data::Rat(v) => {
2831 *tmp = par::map(v, Rat::to_f64);
2832 &tmp[..]
2833 }
2834 _ => &[],
2835 }
2836}
2837
2838fn borrow_cx<'a>(d: &'a Data, tmp: &'a mut Vec<Cx>) -> &'a [Cx] {
2840 match d {
2841 Data::Complex(v) => v,
2842 Data::Ext(v) => {
2843 *tmp = par::map(v, |x| [exact::ext_to_f64(x), 0.0]);
2844 &tmp[..]
2845 }
2846 Data::Rat(v) => {
2847 *tmp = par::map(v, |x| [x.to_f64(), 0.0]);
2848 &tmp[..]
2849 }
2850 Data::F64(v) => {
2851 *tmp = par::map(v, |&x| [x, 0.0]);
2852 &tmp[..]
2853 }
2854 Data::I64(v) => {
2855 *tmp = par::map(v, |&x| [x as f64, 0.0]);
2856 &tmp[..]
2857 }
2858 Data::Bool(v) => {
2859 *tmp = v.iter().map(|&x| [x as f64, 0.0]).collect();
2860 &tmp[..]
2861 }
2862 _ => &[],
2863 }
2864}
2865
2866pub(crate) trait Widen<T>: Copy + Send + Sync {
2875 fn widen(self) -> T;
2876}
2877
2878macro_rules! widens {
2879 ($($from:ty => $to:ty : |$v:ident| $e:expr;)*) => {
2880 $(impl Widen<$to> for $from {
2881 #[inline(always)]
2882 fn widen(self) -> $to {
2883 let $v = self;
2884 $e
2885 }
2886 })*
2887 };
2888}
2889
2890widens! {
2891 u8 => i64: |v| v as i64;
2892 i64 => i64: |v| v;
2893 u8 => f64: |v| v as f64;
2894 i64 => f64: |v| v as f64;
2895 f64 => f64: |v| v;
2896 u8 => Cx: |v| [v as f64, 0.0];
2897 i64 => Cx: |v| [v as f64, 0.0];
2898 f64 => Cx: |v| [v, 0.0];
2899 Cx => Cx: |v| v;
2900}
2901
2902macro_rules! i64_source {
2905 ($d:expr, $tmp:ident, $s:ident, $body:expr) => {
2906 match $d {
2907 Data::I64(v) => {
2908 let $s: &[i64] = v;
2909 $body
2910 }
2911 Data::Bool(v) => {
2912 let $s: &[u8] = v;
2913 $body
2914 }
2915 other => {
2916 let $s: &[i64] = borrow_i64(other, &mut $tmp);
2917 $body
2918 }
2919 }
2920 };
2921}
2922
2923macro_rules! f64_source {
2926 ($d:expr, $tmp:ident, $s:ident, $body:expr) => {
2927 match $d {
2928 Data::F64(v) => {
2929 let $s: &[f64] = v;
2930 $body
2931 }
2932 Data::I64(v) => {
2933 let $s: &[i64] = v;
2934 $body
2935 }
2936 Data::Bool(v) => {
2937 let $s: &[u8] = v;
2938 $body
2939 }
2940 other => {
2941 let $s: &[f64] = borrow_f64(other, &mut $tmp);
2942 $body
2943 }
2944 }
2945 };
2946}
2947
2948macro_rules! cx_source {
2950 ($d:expr, $tmp:ident, $s:ident, $body:expr) => {
2951 match $d {
2952 Data::Complex(v) => {
2953 let $s: &[Cx] = v;
2954 $body
2955 }
2956 Data::F64(v) => {
2957 let $s: &[f64] = v;
2958 $body
2959 }
2960 Data::I64(v) => {
2961 let $s: &[i64] = v;
2962 $body
2963 }
2964 Data::Bool(v) => {
2965 let $s: &[u8] = v;
2966 $body
2967 }
2968 other => {
2969 let $s: &[Cx] = borrow_cx(other, &mut $tmp);
2970 $body
2971 }
2972 }
2973 };
2974}
2975
2976fn as_f64<'a>(d: &'a Data, tmp: &'a mut Vec<f64>, span: Span) -> Result<&'a [f64]> {
2978 if !d.dtype().is_numeric() {
2979 return Err(wrong_type(d.dtype(), span));
2980 }
2981 Ok(borrow_f64(d, tmp))
2982}
2983
2984fn arith_type(a: DType, b: DType, span: Span) -> Result<DType> {
2986 if a == DType::Box || b == DType::Box {
2987 return Err(box_arith(span));
2988 }
2989 if a == DType::Symbol || b == DType::Symbol {
2990 return Err(symbol_arith(span));
2991 }
2992 match DType::promote(a, b) {
2993 Some(DType::Char) => Err(char_arith(span)),
2994 None => Err(Error::new(
2995 ErrorKind::Type,
2996 "cannot mix character and numeric data",
2997 Some(span),
2998 )),
2999 Some(DType::Bool) => Ok(DType::I64),
3000 Some(t) => Ok(t),
3001 }
3002}
3003
3004#[allow(clippy::too_many_arguments)]
3017#[inline]
3018fn zip_chunk<A, B, U, F>(
3019 xs: &[A],
3020 xoff: usize,
3021 xdiv: usize,
3022 ys: &[B],
3023 yoff: usize,
3024 ydiv: usize,
3025 start: usize,
3026 out: &mut [U],
3027 mut f: F,
3028) -> bool
3029where
3030 A: Copy,
3031 B: Copy,
3032 F: FnMut(A, B, &mut U) -> bool,
3033{
3034 let len = out.len();
3035 if len == 0 {
3036 return true;
3037 }
3038 let last = start + len - 1;
3039 let one_x = xdiv > 1 && start / xdiv == last / xdiv;
3040 let one_y = ydiv > 1 && start / ydiv == last / ydiv;
3041 if xdiv == 1 && ydiv == 1 {
3042 let xc = &xs[xoff + start..xoff + start + len];
3043 let yc = &ys[yoff + start..yoff + start + len];
3044 for ((slot, &a), &b) in out.iter_mut().zip(xc).zip(yc) {
3045 if !f(a, b, slot) {
3046 return false;
3047 }
3048 }
3049 } else if xdiv == 1 && one_y {
3050 let b = ys[yoff + start / ydiv];
3051 let xc = &xs[xoff + start..xoff + start + len];
3052 for (slot, &a) in out.iter_mut().zip(xc) {
3053 if !f(a, b, slot) {
3054 return false;
3055 }
3056 }
3057 } else if one_x && ydiv == 1 {
3058 let a = xs[xoff + start / xdiv];
3059 let yc = &ys[yoff + start..yoff + start + len];
3060 for (slot, &b) in out.iter_mut().zip(yc) {
3061 if !f(a, b, slot) {
3062 return false;
3063 }
3064 }
3065 } else {
3066 for (k, slot) in out.iter_mut().enumerate() {
3067 let i = start + k;
3068 if !f(xs[xoff + i / xdiv], ys[yoff + i / ydiv], slot) {
3069 return false;
3070 }
3071 }
3072 }
3073 true
3074}
3075
3076const LANCZOS: [f64; 9] = [
3080 0.999_999_999_999_809_9,
3081 676.520_368_121_885_1,
3082 -1_259.139_216_722_402_8,
3083 771.323_428_777_653_1,
3084 -176.615_029_162_140_6,
3085 12.507_343_278_686_905,
3086 -0.138_571_095_265_720_12,
3087 9.984_369_578_019_572e-6,
3088 1.505_632_735_149_311_6e-7,
3089];
3090
3091fn gamma(x: f64) -> f64 {
3095 use std::f64::consts::PI;
3096 if x < 0.5 {
3097 return PI / ((PI * x).sin() * gamma(1.0 - x));
3099 }
3100 let z = x - 1.0;
3101 let mut a = LANCZOS[0];
3102 for (i, &c) in LANCZOS.iter().enumerate().skip(1) {
3103 a += c / (z + i as f64);
3104 }
3105 let t = z + 7.5;
3106 (2.0 * PI).sqrt() * t.powf(z + 0.5) * (-t).exp() * a
3107}
3108
3109fn factorial(y: f64) -> f64 {
3112 if y.fract() == 0.0 && y.abs() < 1e17 {
3113 let n = y as i64;
3114 if n < 0 {
3115 return if n % 2 == -1 { f64::INFINITY } else { f64::NEG_INFINITY };
3117 }
3118 if n > 170 {
3119 return f64::INFINITY;
3120 }
3121 let mut c = 1.0f64;
3122 for i in 2..=n {
3123 c *= i as f64;
3124 }
3125 return c;
3126 }
3127 gamma(y + 1.0)
3128}
3129
3130fn factorial_as(y: f64, tol: Tol) -> f64 {
3136 let r = factorial(y);
3137 if tol.is_j() && r.is_nan() && !y.is_nan() && y != f64::NEG_INFINITY {
3138 return f64::INFINITY;
3139 }
3140 r
3141}
3142
3143const BINOMIAL_PRODUCT_LIMIT: i64 = 4096;
3146
3147fn binomial_product(x: i64, y: f64) -> f64 {
3151 let mut c = 1.0f64;
3152 for i in 1..=x {
3153 c = c * (y - i as f64 + 1.0) / i as f64;
3154 if c == 0.0 {
3155 break;
3156 }
3157 }
3158 c
3159}
3160
3161fn binomial_i64(x: i64, y: i64) -> Option<i64> {
3165 if x < 0 {
3166 if y >= 0 || y < x {
3169 return Some(0);
3170 }
3171 let v = binomial_exact(-y - 1, -x - 1)?;
3172 return if (y - x) % 2 == 0 { Some(v) } else { v.checked_neg() };
3173 }
3174 binomial_exact(x, y)
3175}
3176
3177fn binomial_exact(x: i64, y: i64) -> Option<i64> {
3180 if x > BINOMIAL_PRODUCT_LIMIT {
3181 return None;
3182 }
3183 let mut c: i128 = 1;
3184 for i in 1..=x as i128 {
3185 c = c.checked_mul(y as i128 - i + 1)? / i;
3186 if c == 0 {
3187 break;
3188 }
3189 }
3190 i64::try_from(c).ok()
3191}
3192
3193fn binomial_at_infinity(x: f64, y: f64) -> Option<f64> {
3201 if x.is_infinite() && y.is_infinite() {
3202 return (x < 0.0 && y > 0.0).then_some(0.0);
3203 }
3204 if x.is_infinite() {
3205 return (!(y < 0.0 && y.fract() == 0.0)).then_some(0.0);
3208 }
3209 if x > 0.0 {
3210 Some(f64::INFINITY)
3211 } else if x == 0.0 {
3212 Some(1.0)
3213 } else {
3214 Some(0.0)
3215 }
3216}
3217
3218fn binomial(x: f64, y: f64) -> f64 {
3220 if x.is_nan() || y.is_nan() {
3221 return f64::NAN;
3224 }
3225 if x.is_infinite() || y.is_infinite() {
3226 return binomial_at_infinity(x, y).unwrap_or(f64::NAN);
3227 }
3228 if x.fract() == 0.0 && x.abs() < 1e17 {
3229 let xi = x as i64;
3230 if xi < 0 {
3231 if y.fract() == 0.0 && y < 0.0 && y >= x {
3232 let sign = if (y as i64 - xi) % 2 == 0 { 1.0 } else { -1.0 };
3233 return sign * binomial_product(-y as i64 - 1, -x - 1.0);
3234 }
3235 return 0.0;
3236 }
3237 if xi <= BINOMIAL_PRODUCT_LIMIT {
3238 return binomial_product(xi, y);
3239 }
3240 }
3241 gamma(y + 1.0) / (gamma(x + 1.0) * gamma(y - x + 1.0))
3242}
3243
3244#[inline]
3247fn i64_op(op: ScalarDyad, a: i64, b: i64) -> Option<i64> {
3248 use ScalarDyad::*;
3249 Some(match op {
3250 Add => a.checked_add(b)?,
3251 Sub => a.checked_sub(b)?,
3252 Mul => a.checked_mul(b)?,
3253 Min => a.min(b),
3254 Max => a.max(b),
3255 Residue => {
3256 if a == 0 {
3257 b
3258 } else {
3259 let mut r = b.wrapping_rem(a);
3261 if r != 0 && (r < 0) != (a < 0) {
3262 r += a;
3263 }
3264 r
3265 }
3266 }
3267 Pow => {
3268 if b < 0 {
3269 return None;
3270 }
3271 a.checked_pow(u32::try_from(b).ok()?)?
3272 }
3273 Binomial => binomial_i64(a, b)?,
3274 _ => return None,
3275 })
3276}
3277
3278#[inline]
3280fn f64_op(op: ScalarDyad, a: f64, b: f64, tol: Tol, span: Span) -> Result<f64> {
3281 use ScalarDyad::*;
3282 let r = match op {
3283 Add => a + b,
3284 Sub => a - b,
3285 Mul => tol.mul(a, b),
3286 Min => a.min(b),
3287 Max => a.max(b),
3288 DivJ => {
3289 if b == 0.0 {
3290 if a == 0.0 { 0.0 } else { f64::INFINITY.copysign(a) }
3291 } else {
3292 a / b
3293 }
3294 }
3295 DivApl => {
3296 if b == 0.0 {
3297 if a == 0.0 {
3298 1.0
3299 } else {
3300 return Err(Error::domain("division by zero", span));
3301 }
3302 } else {
3303 a / b
3304 }
3305 }
3306 Pow => {
3307 if a == 0.0 && b == 0.0 {
3308 1.0
3309 } else if a == 0.0 && b < 0.0 && !tol.is_j() {
3310 return Err(Error::domain("zero has no negative power", span));
3314 } else if a < 0.0 && b.is_infinite() {
3315 if a.abs() != 1.0 && (a.abs() > 1.0) == (b < 0.0) {
3320 0.0
3321 } else {
3322 return Err(Error::domain(
3323 "a negative base has no infinite power: the sign alternates",
3324 span,
3325 ));
3326 }
3327 } else {
3328 a.powf(b)
3329 }
3330 }
3331 Residue => tol.residue(a, b),
3332 Log => {
3333 if a < 0.0 || b < 0.0 {
3334 return Err(Error::not_yet("complex numbers", span));
3335 }
3336 let r = b.ln() / a.ln();
3337 if !tol.is_j() && !r.is_finite() {
3343 if r.is_nan() {
3344 return Ok(1.0);
3345 }
3346 return Err(Error::domain("this logarithm has no value", span));
3347 }
3348 r
3349 }
3350 Root => {
3351 if b < 0.0 {
3352 return Err(Error::not_yet("complex numbers", span));
3353 }
3354 b.powf(1.0 / a)
3355 }
3356 Circle => {
3359 let r = circle(a, b, span)?;
3360 if !tol.is_j() && !r.is_finite() && a.is_finite() && b.is_finite() {
3364 return Err(Error::domain("this circle function has no value", span));
3365 }
3366 r
3367 }
3368 Binomial => binomial(a, b),
3369 _ => return Err(Error::internal("non-arithmetic op in the float path")),
3370 };
3371 if tol.made_nan(r, a, b) {
3372 return Err(nan_error(op, a, b, span));
3373 }
3374 Ok(r)
3375}
3376
3377#[cold]
3380fn nan_error(op: ScalarDyad, a: f64, b: f64, span: Span) -> Error {
3381 Error::nan(
3382 format!(
3383 "`{} {} {}` has no value",
3384 j_number(a),
3385 crate::fuse::dyad_name(op),
3386 j_number(b)
3387 ),
3388 span,
3389 )
3390}
3391
3392#[inline]
3396fn escapes_reals(op: ScalarDyad, a: f64, b: f64) -> bool {
3397 use ScalarDyad::*;
3398 match op {
3399 Pow => a < 0.0 && b.is_finite() && b.fract() != 0.0,
3404 Log => a < 0.0 || b < 0.0,
3405 Root => b < 0.0,
3406 Circle => circle_escapes(a, b),
3407 _ => false,
3408 }
3409}
3410
3411#[inline]
3414fn circle_escapes(k: f64, y: f64) -> bool {
3415 if k.fract() != 0.0 {
3416 return false;
3417 }
3418 match k as i64 {
3419 0 | -1 | -2 | -7 => y.abs() > 1.0,
3420 -4 => y.abs() < 1.0,
3421 -6 => y < 1.0,
3422 8 | -8 | -11 | -12 => true,
3425 _ => false,
3426 }
3427}
3428
3429#[inline]
3439fn circle(k: f64, y: f64, span: Span) -> Result<f64> {
3440 if k.fract() != 0.0 {
3441 return Err(Error::domain("the circle function needs an integer left argument", span));
3442 }
3443 let complex = || Error::internal("a circle function left the reals on the real path");
3444 Ok(match k as i64 {
3445 0 => {
3446 if y.abs() > 1.0 {
3447 return Err(complex());
3448 }
3449 (1.0 - y * y).max(0.0).sqrt()
3450 }
3451 1 => y.sin(),
3452 2 => y.cos(),
3453 3 => y.tan(),
3454 4 => (1.0 + y * y).sqrt(),
3455 5 => y.sinh(),
3456 6 => y.cosh(),
3457 7 => y.tanh(),
3458 -1 => {
3459 if y.abs() > 1.0 {
3460 return Err(complex());
3461 }
3462 y.asin()
3463 }
3464 -2 => {
3465 if y.abs() > 1.0 {
3466 return Err(complex());
3467 }
3468 y.acos()
3469 }
3470 -3 => y.atan(),
3471 -4 => {
3472 if y.abs() < 1.0 {
3473 return Err(complex());
3474 }
3475 y.signum() * (y * y - 1.0).max(0.0).sqrt()
3477 }
3478 -5 => y.asinh(),
3479 -6 => {
3480 if y < 1.0 {
3481 return Err(complex());
3482 }
3483 y.acosh()
3484 }
3485 -7 => {
3486 if y.abs() > 1.0 {
3487 return Err(complex());
3488 }
3489 y.atanh()
3490 }
3491 9 | -9 | -10 => y,
3493 10 => y.abs(),
3494 11 => 0.0,
3495 12 => {
3496 if y < 0.0 {
3497 std::f64::consts::PI
3498 } else {
3499 0.0
3500 }
3501 }
3502 8 | -8 | -11 | -12 => return Err(complex()),
3503 _ => {
3504 return Err(Error::domain(
3505 "the circle functions run from _12 to 12",
3506 span,
3507 ));
3508 }
3509 })
3510}
3511
3512#[inline]
3514fn cx_op(op: ScalarDyad, a: Cx, b: Cx, span: Span) -> Result<Cx> {
3515 use ScalarDyad::*;
3516 Ok(match op {
3517 Add => cx::add(a, b),
3518 Sub => cx::sub(a, b),
3519 Mul => cx::mul(a, b),
3520 DivJ => cx::div(a, b),
3521 DivApl => {
3522 if b == cx::ZERO {
3523 if a == cx::ZERO {
3524 cx::ONE
3525 } else {
3526 return Err(Error::domain("division by zero", span));
3527 }
3528 } else {
3529 cx::div(a, b)
3530 }
3531 }
3532 Pow => cx::pow(a, b),
3533 Log => cx::log(a, b),
3534 Root => cx::root(a, b),
3535 Residue => cx::residue(a, b),
3536 Lcm => cx::lcm(a, b),
3537 Gcd => cx::gcd(a, b),
3538 MakeComplex => cx::add(a, cx::mul(cx::I, b)),
3539 PolarBy => cx::mul(a, cx::exp(cx::mul(cx::I, b))),
3540 Circle => {
3541 if a[1] != 0.0 || a[0].fract() != 0.0 {
3542 return Err(Error::domain(
3543 "the circle function needs an integer left argument",
3544 span,
3545 ));
3546 }
3547 cx::circle(a[0] as i64, b).ok_or_else(|| {
3548 Error::domain("the circle functions run from _12 to 12", span)
3549 })?
3550 }
3551 Min | Max => return Err(no_complex_order(span)),
3552 Binomial => {
3553 return Err(Error::not_yet("the binomial function on complex numbers", span));
3554 }
3555 Eq | Ne | Lt | Le | Gt | Ge => {
3556 return Err(Error::internal("a comparison in the complex arithmetic path"));
3557 }
3558 })
3559}
3560
3561fn no_complex_order(span: Span) -> Error {
3564 Error::new(
3565 ErrorKind::Domain,
3566 "complex numbers have no order; only equality (=, ~:) applies to them",
3567 Some(span),
3568 )
3569}
3570
3571#[allow(clippy::too_many_arguments)]
3572#[inline(always)]
3573fn dyad_cx_chunk_body<A: Widen<Cx>, B: Widen<Cx>>(
3574 op: ScalarDyad,
3575 xs: &[A],
3576 xoff: usize,
3577 xdiv: usize,
3578 ys: &[B],
3579 yoff: usize,
3580 ydiv: usize,
3581 start: usize,
3582 out: &mut [Cx],
3583 span: Span,
3584) -> Result<()> {
3585 use ScalarDyad::*;
3586 macro_rules! plain {
3589 ($step:expr) => {{
3590 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut Cx| {
3591 *slot = $step(a.widen(), b.widen());
3592 true
3593 });
3594 return Ok(());
3595 }};
3596 }
3597 match op {
3598 Add => plain!(cx::add),
3599 Sub => plain!(cx::sub),
3600 Mul => plain!(cx::mul),
3601 DivJ => plain!(cx::div),
3602 _ => {}
3603 }
3604 let mut err = None;
3605 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut Cx| {
3606 match cx_op(op, a.widen(), b.widen(), span) {
3607 Ok(v) => {
3608 *slot = v;
3609 true
3610 }
3611 Err(e) => {
3612 err = Some(e);
3613 false
3614 }
3615 }
3616 });
3617 match err {
3618 Some(e) => Err(e),
3619 None => Ok(()),
3620 }
3621}
3622
3623multiversioned! {
3624 #[allow(clippy::too_many_arguments)]
3627 fn dyad_cx_chunk[A: Widen<Cx>, B: Widen<Cx>](
3628 op: ScalarDyad,
3629 xs: &[A],
3630 xoff: usize,
3631 xdiv: usize,
3632 ys: &[B],
3633 yoff: usize,
3634 ydiv: usize,
3635 start: usize,
3636 out: &mut [Cx],
3637 span: Span,
3638 ) -> Result<()> = dyad_cx_chunk_body;
3639}
3640
3641#[allow(clippy::too_many_arguments)]
3642fn dyad_cx<A: Widen<Cx>, B: Widen<Cx>>(
3643 op: ScalarDyad,
3644 xs: &[A],
3645 xoff: usize,
3646 xdiv: usize,
3647 ys: &[B],
3648 yoff: usize,
3649 ydiv: usize,
3650 n: usize,
3651 span: Span,
3652) -> Result<Vec<Cx>> {
3653 par::try_fill(n, |start, part| {
3654 dyad_cx_chunk(op, xs, xoff, xdiv, ys, yoff, ydiv, start, part, span)
3655 })
3656}
3657
3658#[allow(clippy::too_many_arguments)]
3667fn complex_dyad_data(
3668 op: ScalarDyad,
3669 x: &Data,
3670 xoff: usize,
3671 xdiv: usize,
3672 y: &Data,
3673 yoff: usize,
3674 ydiv: usize,
3675 n: usize,
3676 span: Span,
3677) -> Result<Data> {
3678 let (mut tx, mut ty) = (Vec::new(), Vec::new());
3679 macro_rules! pass {
3680 ($xs:expr, $ys:expr) => {
3681 Data::Complex(dyad_cx(op, $xs, xoff, xdiv, $ys, yoff, ydiv, n, span)?.into())
3682 };
3683 }
3684 Ok(match (x, y) {
3685 (Data::Complex(a), _) => {
3686 let xs: &[Cx] = a;
3687 cx_source!(y, ty, ys, pass!(xs, ys))
3688 }
3689 (_, Data::Complex(b)) => {
3690 let ys: &[Cx] = b;
3691 cx_source!(x, tx, xs, pass!(xs, ys))
3692 }
3693 _ => pass!(borrow_cx(x, &mut tx), borrow_cx(y, &mut ty)),
3694 })
3695}
3696
3697fn circle_reads_a_part(x: &Data, xoff: usize, xdiv: usize, n: usize) -> bool {
3702 if x.dtype() == DType::Complex {
3703 return false;
3705 }
3706 let mut tmp = Vec::new();
3707 let xs = borrow_f64(x, &mut tmp);
3708 (0..n).all(|i| {
3709 let k = xs[xoff + i / xdiv];
3710 k.fract() == 0.0 && (9.0..=12.0).contains(&k)
3711 })
3712}
3713
3714#[allow(clippy::too_many_arguments)]
3717fn pass_leaves_reals(
3718 op: ScalarDyad,
3719 x: &Data,
3720 xoff: usize,
3721 xdiv: usize,
3722 y: &Data,
3723 yoff: usize,
3724 ydiv: usize,
3725 n: usize,
3726) -> bool {
3727 use ScalarDyad::*;
3728 if !matches!(op, Pow | Log | Root | Circle) {
3729 return false;
3730 }
3731 let (mut tx, mut ty) = (Vec::new(), Vec::new());
3732 let xs = borrow_f64(x, &mut tx);
3733 let ys = borrow_f64(y, &mut ty);
3734 (0..n).any(|i| escapes_reals(op, xs[xoff + i / xdiv], ys[yoff + i / ydiv]))
3735}
3736
3737#[allow(clippy::too_many_arguments)]
3738#[inline(always)]
3739fn dyad_i64_chunk_body<A: Widen<i64>, B: Widen<i64>>(
3740 op: ScalarDyad,
3741 xs: &[A],
3742 xoff: usize,
3743 xdiv: usize,
3744 ys: &[B],
3745 yoff: usize,
3746 ydiv: usize,
3747 start: usize,
3748 out: &mut [i64],
3749) -> bool {
3750 use ScalarDyad::*;
3751 macro_rules! overflowing {
3755 ($m:ident) => {{
3756 let mut over = false;
3757 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut i64| {
3758 let (v, o) = i64::$m(a.widen(), b.widen());
3759 *slot = v;
3760 over |= o;
3761 true
3762 });
3763 !over
3764 }};
3765 }
3766 macro_rules! plain {
3767 ($step:expr) => {{
3768 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut i64| {
3769 *slot = $step(a.widen(), b.widen());
3770 true
3771 })
3772 }};
3773 }
3774 match op {
3775 Add => overflowing!(overflowing_add),
3776 Sub => overflowing!(overflowing_sub),
3777 Mul => overflowing!(overflowing_mul),
3778 Min => plain!(i64::min),
3779 Max => plain!(i64::max),
3780 _ => zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut i64| {
3781 match i64_op(op, a.widen(), b.widen()) {
3782 Some(v) => {
3783 *slot = v;
3784 true
3785 }
3786 None => false,
3787 }
3788 }),
3789 }
3790}
3791
3792multiversioned! {
3793 #[allow(clippy::too_many_arguments)]
3800 fn dyad_i64_chunk[A: Widen<i64>, B: Widen<i64>](
3801 op: ScalarDyad,
3802 xs: &[A],
3803 xoff: usize,
3804 xdiv: usize,
3805 ys: &[B],
3806 yoff: usize,
3807 ydiv: usize,
3808 start: usize,
3809 out: &mut [i64],
3810 ) -> bool = dyad_i64_chunk_body;
3811}
3812
3813#[allow(clippy::too_many_arguments)]
3815fn dyad_i64<A: Widen<i64>, B: Widen<i64>>(
3816 op: ScalarDyad,
3817 xs: &[A],
3818 xoff: usize,
3819 xdiv: usize,
3820 ys: &[B],
3821 yoff: usize,
3822 ydiv: usize,
3823 n: usize,
3824) -> Option<Vec<i64>> {
3825 let (out, ok) = par::fill(n, |start, part| {
3826 dyad_i64_chunk(op, xs, xoff, xdiv, ys, yoff, ydiv, start, part)
3827 });
3828 ok.then_some(out)
3829}
3830
3831#[allow(clippy::too_many_arguments)]
3834fn int_dyad_data(
3835 op: ScalarDyad,
3836 x: &Data,
3837 xoff: usize,
3838 xdiv: usize,
3839 y: &Data,
3840 yoff: usize,
3841 ydiv: usize,
3842 n: usize,
3843) -> Option<Data> {
3844 let (mut tx, mut ty) = (Vec::new(), Vec::new());
3845 let out = i64_source!(x, tx, xs, {
3846 i64_source!(y, ty, ys, dyad_i64(op, xs, xoff, xdiv, ys, yoff, ydiv, n))
3847 })?;
3848 Some(Data::I64(out.into()))
3849}
3850
3851#[allow(clippy::too_many_arguments)]
3852#[inline(always)]
3853fn dyad_f64_chunk_body<A: Widen<f64>, B: Widen<f64>>(
3854 op: ScalarDyad,
3855 xs: &[A],
3856 xoff: usize,
3857 xdiv: usize,
3858 ys: &[B],
3859 yoff: usize,
3860 ydiv: usize,
3861 start: usize,
3862 out: &mut [f64],
3863 tol: Tol,
3864 span: Span,
3865) -> Result<()> {
3866 use ScalarDyad::*;
3867 macro_rules! plain {
3870 ($step:expr) => {{
3871 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut f64| {
3872 *slot = $step(a.widen(), b.widen());
3873 true
3874 });
3875 return Ok(());
3876 }};
3877 }
3878 macro_rules! plain_checked {
3885 ($step:expr) => {{
3886 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut f64| {
3887 *slot = $step(a.widen(), b.widen());
3888 true
3889 });
3890 if !(tol.is_j() && out.iter().any(|v| v.is_nan())) {
3891 return Ok(());
3892 }
3893 }};
3894 }
3895 match op {
3896 Add => plain_checked!(|a: f64, b: f64| a + b),
3897 Sub => plain_checked!(|a: f64, b: f64| a - b),
3898 Mul => plain_checked!(|a: f64, b: f64| a * b),
3899 Min => plain!(f64::min),
3900 Max => plain!(f64::max),
3901 _ => {}
3902 }
3903 let mut err = None;
3904 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, out, |a, b, slot: &mut f64| {
3905 match f64_op(op, a.widen(), b.widen(), tol, span) {
3906 Ok(v) => {
3907 *slot = v;
3908 true
3909 }
3910 Err(e) => {
3911 err = Some(e);
3912 false
3913 }
3914 }
3915 });
3916 match err {
3917 Some(e) => Err(e),
3918 None => Ok(()),
3919 }
3920}
3921
3922multiversioned! {
3923 #[allow(clippy::too_many_arguments)]
3926 fn dyad_f64_chunk[A: Widen<f64>, B: Widen<f64>](
3927 op: ScalarDyad,
3928 xs: &[A],
3929 xoff: usize,
3930 xdiv: usize,
3931 ys: &[B],
3932 yoff: usize,
3933 ydiv: usize,
3934 start: usize,
3935 out: &mut [f64],
3936 tol: Tol,
3937 span: Span,
3938 ) -> Result<()> = dyad_f64_chunk_body;
3939}
3940
3941#[allow(clippy::too_many_arguments)]
3942fn dyad_f64<A: Widen<f64>, B: Widen<f64>>(
3943 op: ScalarDyad,
3944 xs: &[A],
3945 xoff: usize,
3946 xdiv: usize,
3947 ys: &[B],
3948 yoff: usize,
3949 ydiv: usize,
3950 n: usize,
3951 tol: Tol,
3952 span: Span,
3953) -> Result<Vec<f64>> {
3954 par::try_fill(n, |start, part| {
3955 dyad_f64_chunk(op, xs, xoff, xdiv, ys, yoff, ydiv, start, part, tol, span)
3956 })
3957}
3958
3959#[allow(clippy::too_many_arguments)]
3961fn float_dyad_data(
3962 op: ScalarDyad,
3963 x: &Data,
3964 xoff: usize,
3965 xdiv: usize,
3966 y: &Data,
3967 yoff: usize,
3968 ydiv: usize,
3969 n: usize,
3970 tol: Tol,
3971 span: Span,
3972) -> Result<Data> {
3973 let (mut tx, mut ty) = (Vec::new(), Vec::new());
3974 let out = f64_source!(x, tx, xs, {
3975 f64_source!(y, ty, ys, dyad_f64(op, xs, xoff, xdiv, ys, yoff, ydiv, n, tol, span)?)
3976 });
3977 Ok(Data::F64(out.into()))
3978}
3979
3980fn crossed_types(a: DType, b: DType) -> bool {
3984 let class = |d: DType| match d {
3985 DType::Box => 3,
3986 DType::Symbol => 2,
3987 DType::Char => 1,
3988 _ => 0,
3989 };
3990 class(a) != class(b)
3991}
3992
3993#[allow(clippy::too_many_arguments)]
3996fn symbol_min_max(
3997 op: ScalarDyad,
3998 x: &Data,
3999 xoff: usize,
4000 xdiv: usize,
4001 y: &Data,
4002 yoff: usize,
4003 ydiv: usize,
4004 n: usize,
4005 span: Span,
4006) -> Result<Data> {
4007 let (Data::Symbol(a), Data::Symbol(b)) = (x, y) else {
4008 return Err(symbol_arith(span));
4009 };
4010 let down = op == ScalarDyad::Min;
4011 let (out, _) = par::fill(n, |start, part: &mut [crate::symbol::Id]| {
4012 zip_chunk(a, xoff, xdiv, b, yoff, ydiv, start, part, |p, q, slot| {
4013 *slot = if crate::symbol::cmp(p, q).is_le() == down { p } else { q };
4014 true
4015 })
4016 });
4017 Ok(Data::Symbol(out.into()))
4018}
4019
4020#[allow(clippy::too_many_arguments)]
4021fn compare_data(
4022 op: ScalarDyad,
4023 x: &Data,
4024 xoff: usize,
4025 xdiv: usize,
4026 y: &Data,
4027 yoff: usize,
4028 ydiv: usize,
4029 n: usize,
4030 tol: Tol,
4031 span: Span,
4032) -> Result<Data> {
4033 use ScalarDyad::*;
4034 let (dx, dy) = (x.dtype(), y.dtype());
4035 let equality = matches!(op, Eq | Ne);
4036 let boxed = dx == DType::Box || dy == DType::Box;
4042 if equality && crossed_types(dx, dy) && (!boxed || tol.is_j()) {
4043 let unequal = op == Ne;
4044 return Ok(Data::Bool(vec![u8::from(unequal); n].into()));
4045 }
4046 if boxed {
4047 if !equality {
4050 return Err(box_arith(span));
4051 }
4052 let (Data::Box(a), Data::Box(b)) = (x, y) else {
4053 return Err(Error::not_yet("a scalar function inside a nested array", span));
4056 };
4057 let (out, _) = par::fill(n, |start, part: &mut [u8]| {
4058 for (k, slot) in part.iter_mut().enumerate() {
4059 let i = start + k;
4060 let e = arrays_match(&a[xoff + i / xdiv], &b[yoff + i / ydiv], tol);
4061 *slot = u8::from(if op == Eq { e } else { !e });
4062 }
4063 true
4064 });
4065 return Ok(Data::Bool(out.into()));
4066 }
4067 if dx == DType::Symbol || dy == DType::Symbol {
4068 if dx != dy {
4071 return Err(Error::new(
4072 ErrorKind::Type,
4073 "cannot compare a symbol with data that is not a symbol",
4074 Some(span),
4075 ));
4076 }
4077 let (Data::Symbol(a), Data::Symbol(b)) = (x, y) else {
4078 return Err(Error::internal("symbol comparison on non-symbol data"));
4079 };
4080 let (out, _) = par::fill(n, |start, part: &mut [u8]| {
4082 zip_chunk(a, xoff, xdiv, b, yoff, ydiv, start, part, |p, q, slot| {
4083 *slot = u8::from(match op {
4084 Eq => p == q,
4085 Ne => p != q,
4086 _ => {
4087 let o = crate::symbol::cmp(p, q);
4088 match op {
4089 Lt => o.is_lt(),
4090 Le => o.is_le(),
4091 Gt => o.is_gt(),
4092 _ => o.is_ge(),
4093 }
4094 }
4095 });
4096 true
4097 })
4098 });
4099 return Ok(Data::Bool(out.into()));
4100 }
4101 if dx == DType::Char || dy == DType::Char {
4102 if dx != dy {
4103 return Err(Error::new(
4104 ErrorKind::Type,
4105 "cannot compare character and numeric data",
4106 Some(span),
4107 ));
4108 }
4109 if !equality {
4110 return Err(Error::new(
4111 ErrorKind::Type,
4112 "cannot order character data; only equality applies",
4113 Some(span),
4114 ));
4115 }
4116 let (Data::Char(a), Data::Char(b)) = (x, y) else {
4117 return Err(Error::internal("character comparison on non-character data"));
4118 };
4119 let (out, _) = par::fill(n, |start, part: &mut [u8]| {
4120 zip_chunk(a, xoff, xdiv, b, yoff, ydiv, start, part, |p, q, slot| {
4121 let e = p == q;
4122 *slot = if op == Eq { e as u8 } else { !e as u8 };
4123 true
4124 })
4125 });
4126 return Ok(Data::Bool(out.into()));
4127 }
4128 if DType::promote(dx, dy).is_some_and(DType::is_exact)
4129 && let Some(d) = exact_compare_data(op, x, xoff, xdiv, y, yoff, ydiv, n)
4130 {
4131 return Ok(d);
4132 }
4133 if dx == DType::Complex || dy == DType::Complex {
4134 if !equality {
4135 return Err(no_complex_order(span));
4136 }
4137 let (mut tx, mut ty) = (Vec::new(), Vec::new());
4138 let out = cx_source!(x, tx, xs, {
4139 cx_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 let e = tol.eq_cx(a.widen(), b.widen());
4143 *slot = if op == Eq { e as u8 } else { !e as u8 };
4144 true
4145 })
4146 })
4147 .0
4148 })
4149 });
4150 return Ok(Data::Bool(out.into()));
4151 }
4152 let out = if DType::promote(dx, dy) == Some(DType::F64) {
4155 let (mut tx, mut ty) = (Vec::<f64>::new(), Vec::<f64>::new());
4156 f64_source!(x, tx, xs, {
4157 f64_source!(y, ty, ys, {
4158 par::fill(n, |start, part: &mut [u8]| {
4159 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, part, |a, b, slot| {
4160 *slot = tol_cmp(op, a.widen(), b.widen(), tol) as u8;
4161 true
4162 })
4163 })
4164 .0
4165 })
4166 })
4167 } else {
4168 let (mut tx, mut ty) = (Vec::<i64>::new(), Vec::<i64>::new());
4169 i64_source!(x, tx, xs, {
4170 i64_source!(y, ty, ys, {
4171 par::fill(n, |start, part: &mut [u8]| {
4172 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, start, part, |a, b, slot| {
4173 let (a, b): (i64, i64) = (a.widen(), b.widen());
4174 *slot = cmp_result(op, Some(i64::cmp(&a, &b))) as u8;
4175 true
4176 })
4177 })
4178 .0
4179 })
4180 })
4181 };
4182 Ok(Data::Bool(out.into()))
4183}
4184
4185#[inline(always)]
4187pub(crate) fn tol_cmp(op: ScalarDyad, a: f64, b: f64, tol: Tol) -> bool {
4188 use ScalarDyad::*;
4189 match op {
4190 Eq => tol.eq(a, b),
4191 Ne => !tol.eq(a, b),
4192 Lt => tol.lt(a, b),
4193 Le => tol.le(a, b),
4194 Gt => tol.lt(b, a),
4195 Ge => tol.le(b, a),
4196 _ => false,
4197 }
4198}
4199
4200#[inline]
4204pub(crate) fn tol_ord(a: f64, b: f64, tol: Tol) -> std::cmp::Ordering {
4205 use std::cmp::Ordering::Equal;
4206 if tol.ct != 0.0 && tol.eq(a, b) {
4207 return Equal;
4208 }
4209 a.partial_cmp(&b).unwrap_or(Equal)
4210}
4211
4212fn cmp_result(op: ScalarDyad, ord: Option<std::cmp::Ordering>) -> bool {
4214 use std::cmp::Ordering::*;
4215 use ScalarDyad::*;
4216 match ord {
4217 None => matches!(op, Ne),
4218 Some(o) => match op {
4219 Eq => o == Equal,
4220 Ne => o != Equal,
4221 Lt => o == Less,
4222 Le => o != Greater,
4223 Gt => o == Greater,
4224 Ge => o != Less,
4225 _ => false,
4226 },
4227 }
4228}
4229
4230fn gcd_i128(a: i128, b: i128) -> i128 {
4236 let (mut a, mut b) = (a.abs(), b.abs());
4237 while b != 0 {
4238 let t = a % b;
4239 a = b;
4240 b = t;
4241 }
4242 a
4243}
4244
4245fn signed_gcd_i128(a: i128, b: i128) -> i128 {
4249 match (a, b) {
4250 (0, _) => b,
4251 (_, 0) => a,
4252 _ => gcd_i128(a, b),
4253 }
4254}
4255
4256fn decimal_parts(v: f64) -> Option<(i128, u32)> {
4264 if !v.is_finite() {
4265 return None;
4266 }
4267 let text = format!("{v:e}");
4268 let (mantissa, exponent) = text.split_once('e')?;
4269 let exponent: i32 = exponent.parse().ok()?;
4270 let (whole, fraction) = mantissa.split_once('.').unwrap_or((mantissa, ""));
4271 if whole.trim_start_matches('-').len() + fraction.len() > WRITTEN_DIGITS {
4272 return None;
4273 }
4274 let mut digits: i128 = format!("{whole}{fraction}").parse().ok()?;
4275 let mut scale = fraction.len() as i32 - exponent;
4276 while scale < 0 {
4279 digits = digits.checked_mul(10)?;
4280 scale += 1;
4281 }
4282 (scale <= 34).then_some((digits, scale as u32))
4285}
4286
4287const WRITTEN_DIGITS: usize = 12;
4291
4292fn gcd_decimal(a: f64, b: f64) -> Option<f64> {
4297 let (pa, sa) = decimal_parts(a)?;
4298 let (pb, sb) = decimal_parts(b)?;
4299 let scale = sa.max(sb);
4300 let lift = |p: i128, s: u32| 10i128.checked_pow(scale - s).and_then(|k| p.checked_mul(k));
4301 let g = gcd_i128(lift(pa, sa)?, lift(pb, sb)?);
4302 format!("{g}e-{scale}").parse().ok()
4305}
4306
4307fn gcd_f64(a: f64, b: f64, tol: Tol) -> Option<f64> {
4315 let (mut a, mut b) = (a.abs(), b.abs());
4316 if !a.is_finite() || !b.is_finite() {
4317 return None;
4318 }
4319 let eps = tol.ct * a.max(b);
4320 for _ in 0..1000 {
4323 if b == 0.0 {
4324 return Some(a);
4325 }
4326 if a == 0.0 {
4327 return Some(b);
4328 }
4329 let q = a / b;
4334 let mut k = q.floor();
4335 if tol.eq(q, k + 1.0) {
4336 k += 1.0;
4337 }
4338 let mut r = a - b * k;
4339 if r <= eps || tol.eq(r, b) {
4340 r = 0.0;
4341 }
4342 a = b;
4343 b = r;
4344 }
4345 Some(a)
4346}
4347
4348#[allow(clippy::too_many_arguments)]
4352fn real_lcm_gcd(
4353 op: ScalarDyad,
4354 xs: &[f64],
4355 xoff: usize,
4356 xdiv: usize,
4357 ys: &[f64],
4358 yoff: usize,
4359 ydiv: usize,
4360 n: usize,
4361 tol: Tol,
4362 gnu: bool,
4363 span: Span,
4364) -> Result<Data> {
4365 let mut out = vec![0.0f64; n];
4366 let mut ok = true;
4367 let whole = |v: f64| {
4372 let w = v.round();
4373 if gnu && tol.eq(v, w) { w } else { v }
4374 };
4375 let vanishes = |v: f64, other: f64| gnu && v != 0.0 && v.abs() <= tol.ct * other.abs();
4378 zip_chunk(xs, xoff, xdiv, ys, yoff, ydiv, 0, &mut out, |a, b, slot| {
4379 let (a, b) = (whole(a), whole(b));
4380 let (a, b) = (if vanishes(a, b) { 0.0 } else { a }, if vanishes(b, a) { 0.0 } else { b });
4381 let Some(g) = gcd_decimal(a, b).or_else(|| gcd_f64(a, b, tol)) else {
4382 ok = false;
4383 return false;
4384 };
4385 *slot = if op == ScalarDyad::Gcd {
4386 g
4387 } else if g == 0.0 {
4388 0.0
4389 } else {
4390 a / g * b
4391 };
4392 true
4393 });
4394 if !ok {
4395 return Err(Error::domain("LCM/GCD needs finite values", span));
4396 }
4397 Ok(Data::F64(out.into()))
4398}
4399
4400#[allow(clippy::too_many_arguments)]
4404fn lcm_gcd_data(
4405 op: ScalarDyad,
4406 x: &Data,
4407 xoff: usize,
4408 xdiv: usize,
4409 y: &Data,
4410 yoff: usize,
4411 ydiv: usize,
4412 n: usize,
4413 tol: Tol,
4414 rules: Rules,
4415 span: Span,
4416) -> Result<Data> {
4417 let gnu = rules.lang == crate::Lang::Apl
4420 && rules.gcd_rule == crate::frontend::GcdRule::Tolerant;
4421 let t = arith_type(x.dtype(), y.dtype(), span)?;
4422 if t == DType::Complex {
4423 return complex_dyad_data(op, x, xoff, xdiv, y, yoff, ydiv, n, span);
4425 }
4426 if t.is_exact()
4427 && let Some(d) = exact_dyad_data(op, t, x, xoff, xdiv, y, yoff, ydiv, n, span)?
4428 {
4429 return Ok(d);
4430 }
4431 let both_bool = x.dtype() == DType::Bool && y.dtype() == DType::Bool;
4432 let float = t == DType::F64;
4433 let (xs, ys) = if float {
4434 let (mut tx, mut ty) = (Vec::new(), Vec::new());
4435 let xf = borrow_f64(x, &mut tx);
4436 let yf = borrow_f64(y, &mut ty);
4437 let integral = |v: &[f64]| v.iter().all(|&a| a.fract() == 0.0 && fits_i64(a));
4438 if !integral(xf) || !integral(yf) {
4439 return real_lcm_gcd(op, xf, xoff, xdiv, yf, yoff, ydiv, n, tol, gnu, span);
4440 }
4441 (
4442 xf.iter().map(|&a| a as i64).collect::<Vec<_>>(),
4443 yf.iter().map(|&a| a as i64).collect::<Vec<_>>(),
4444 )
4445 } else {
4446 let (mut tx, mut ty) = (Vec::new(), Vec::new());
4447 (borrow_i64(x, &mut tx).to_vec(), borrow_i64(y, &mut ty).to_vec())
4448 };
4449 let (out, fits) = par::fill(n, |start, part: &mut [i128]| {
4452 let mut fits = true;
4453 zip_chunk(&xs, xoff, xdiv, &ys, yoff, ydiv, start, part, |a, b, slot| {
4454 let (a, b) = (a as i128, b as i128);
4455 let g = if gnu { signed_gcd_i128(a, b) } else { gcd_i128(a, b) };
4456 let v = if op == ScalarDyad::Gcd {
4457 g
4458 } else if g == 0 {
4459 0
4460 } else {
4461 a / g * b
4462 };
4463 fits &= i64::try_from(v).is_ok();
4464 *slot = v;
4465 true
4466 });
4467 fits
4468 });
4469 if !fits || float {
4470 return Ok(Data::F64(par::map(&out, |&v| v as f64).into()));
4471 }
4472 if both_bool {
4473 return Ok(Data::Bool(par::map(&out, |&v| v as u8).into()));
4474 }
4475 Ok(Data::I64(par::map(&out, |&v| v as i64).into()))
4476}
4477
4478fn to_rat_vec(d: &Data) -> Option<Vec<Rat>> {
4483 Some(match d {
4484 Data::Bool(v) => v.iter().map(|&b| Rat::from_int(Ext::from(b))).collect(),
4485 Data::I64(v) => v.iter().map(|&x| Rat::from_int(Ext::from(x))).collect(),
4486 Data::Ext(v) => v.iter().map(|x| Rat::from_int(x.clone())).collect(),
4487 Data::Rat(v) => v.to_vec(),
4488 Data::F64(_) | Data::Complex(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => {
4489 return None;
4490 }
4491 })
4492}
4493
4494fn rat_window(d: &Data, off: usize, div: usize, n: usize) -> Option<Vec<Rat>> {
4502 if n == 0 {
4503 return Some(Vec::new());
4504 }
4505 let end = off + (n - 1) / div + 1;
4506 if off == 0 && end == d.len() {
4507 return to_rat_vec(d);
4508 }
4509 to_rat_vec(&d.slice(off, end))
4510}
4511
4512fn exact_data(t: DType, out: Vec<Rat>) -> Data {
4520 if t == DType::Ext && out.iter().all(Rat::is_integer) {
4521 return Data::Ext(out.iter().map(|r| r.to_int().expect("whole")).collect());
4522 }
4523 Data::Rat(out.into())
4524}
4525
4526fn too_large(span: Span) -> Error {
4528 Error::domain(
4529 format!(
4530 "the exact result needs more than {} bits; use floats for a value this large",
4531 exact::MAX_BITS
4532 ),
4533 span,
4534 )
4535}
4536
4537fn exact_pow(a: &Rat, b: &Rat, span: Span) -> Result<Option<Rat>> {
4540 let Some(e) = b.to_int().as_ref().and_then(exact::ext_to_i64) else {
4541 return Ok(None);
4542 };
4543 if let Some(v) = a.pow(e) {
4544 return Ok(Some(v));
4545 }
4546 if a.is_zero() && e < 0 { Ok(None) } else { Err(too_large(span)) }
4548}
4549
4550#[allow(clippy::too_many_arguments)]
4554fn exact_dyad_data(
4555 op: ScalarDyad,
4556 t: DType,
4557 x: &Data,
4558 xoff: usize,
4559 xdiv: usize,
4560 y: &Data,
4561 yoff: usize,
4562 ydiv: usize,
4563 n: usize,
4564 span: Span,
4565) -> Result<Option<Data>> {
4566 use ScalarDyad::*;
4567 let (Some(xs), Some(ys)) = (rat_window(x, xoff, xdiv, n), rat_window(y, yoff, ydiv, n))
4568 else {
4569 return Ok(None);
4570 };
4571 let mut out = Vec::with_capacity(n);
4572 for i in 0..n {
4573 let a = &xs[i / xdiv];
4574 let b = &ys[i / ydiv];
4575 let v = match op {
4576 Add => a.add(b),
4577 Sub => a.sub(b),
4578 Mul => a.mul(b),
4579 DivJ | DivApl => match a.div(b) {
4581 Some(v) => v,
4582 None => return Ok(None),
4583 },
4584 Min => a.min(b).clone(),
4585 Max => a.max(b).clone(),
4586 Residue => exact::rat_residue(a, b),
4587 Gcd => exact::rat_gcd(a, b),
4588 Lcm => exact::rat_lcm(a, b),
4589 Pow => match exact_pow(a, b, span)? {
4590 Some(v) => v,
4591 None => return Ok(None),
4592 },
4593 Binomial => match (a.to_int(), b.to_int()) {
4594 (Some(k), Some(m)) => match exact::ext_binomial(&k, &m) {
4595 Some(v) => Rat::from_int(v),
4596 None => return Ok(None),
4597 },
4598 _ => return Ok(None),
4599 },
4600 Root if t == DType::Ext => {
4603 let (Some(k), Some(m)) = (a.to_int(), b.to_int()) else {
4604 return Ok(None);
4605 };
4606 let Some(k) = exact::ext_to_i64(&k).and_then(|k| u32::try_from(k).ok()) else {
4607 return Ok(None);
4608 };
4609 match exact::exact_root(k, &m) {
4610 Some(v) => Rat::from_int(v),
4611 None => return Ok(None),
4612 }
4613 }
4614 Root | Log | Circle | MakeComplex | PolarBy => return Ok(None),
4615 Eq | Ne | Lt | Le | Gt | Ge => return Ok(None),
4617 };
4618 out.push(v);
4619 }
4620 Ok(Some(exact_data(t, out)))
4621}
4622
4623fn exact_monad(op: ScalarMonad, y: &Array) -> Option<Array> {
4626 use ScalarMonad::*;
4627 let v = to_rat_vec(&y.data)?;
4628 let shape = y.shape.clone();
4629 if matches!(op, Floor | Ceil | Signum) {
4632 let out: Vec<Ext> = v
4633 .iter()
4634 .map(|r| match op {
4635 Floor => r.floor(),
4636 Ceil => r.ceil(),
4637 _ => r.signum(),
4638 })
4639 .collect();
4640 return Some(Array::new(shape, Data::Ext(out.into())).with_layout(y.layout()));
4641 }
4642 let two = Rat::from_int(Ext::from(2));
4643 let mut out = Vec::with_capacity(v.len());
4644 for r in &v {
4645 let value = match op {
4646 Conj => r.clone(),
4647 Neg => r.neg(),
4648 Abs => r.abs(),
4649 Recip => r.recip()?,
4650 Inc => r.add(&Rat::one()),
4651 Dec => r.sub(&Rat::one()),
4652 OneMinus => Rat::one().sub(r),
4653 Double => r.add(r),
4654 Halve => r.div(&two).expect("two is not zero"),
4655 Square => r.mul(r),
4656 Sqrt => r.sqrt()?,
4657 Factorial => Rat::from_int(r.to_int().as_ref().and_then(exact::ext_factorial)?),
4658 Exp | Ln | Pi | Imaginary | Polar | Not => return None,
4661 Floor | Ceil | Signum => unreachable!("handled above"),
4662 };
4663 out.push(value);
4664 }
4665 Some(Array::new(shape, exact_data(y.dtype(), out)).with_layout(y.layout()))
4666}
4667
4668fn to_exact(y: &Array, span: Span) -> Result<Array> {
4673 let data = match &y.data {
4674 Data::Ext(_) | Data::Rat(_) => return Ok(y.clone()),
4675 Data::Bool(v) => Data::Ext(v.iter().map(|&b| Ext::from(b)).collect()),
4676 Data::I64(v) => Data::Ext(v.iter().map(|&x| Ext::from(x)).collect()),
4677 Data::F64(v) => {
4678 let mut out = Vec::with_capacity(v.len());
4679 for &x in v.iter() {
4680 out.push(exact::f64_to_rat(x).ok_or_else(|| {
4681 Error::domain("an infinity has no exact value", span)
4682 })?);
4683 }
4684 exact_data(DType::Ext, out)
4685 }
4686 Data::Complex(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => {
4687 return Err(Error::domain(
4688 format!("x: needs real numbers, not {} data", y.dtype().name()),
4689 span,
4690 ));
4691 }
4692 };
4693 Ok(Array::new(y.shape.clone(), data).with_layout(y.layout()))
4694}
4695
4696fn from_exact(y: &Array) -> Array {
4699 let shape = y.shape.clone();
4700 match &y.data {
4701 Data::Ext(v) => match v.iter().map(exact::ext_to_i64).collect::<Option<Vec<i64>>>() {
4702 Some(out) => Array::new(shape, Data::I64(out.into())).with_layout(y.layout()),
4703 None => Array::new(shape, Data::F64(v.iter().map(exact::ext_to_f64).collect()))
4704 .with_layout(y.layout()),
4705 },
4706 Data::Rat(v) => Array::new(shape, Data::F64(v.iter().map(Rat::to_f64).collect()))
4707 .with_layout(y.layout()),
4708 _ => y.clone(),
4709 }
4710}
4711
4712fn exact_form(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
4714 match one_whole(x, "the form x: converts to", near, span)? {
4715 1 => {
4716 let e = to_exact(y, span)?;
4717 e.cast(DType::Rat).ok_or_else(|| Error::internal("an exact value has no rational form"))
4718 }
4719 2 => {
4720 let e = to_exact(y, span)?;
4721 let v = to_rat_vec(&e.data).ok_or_else(|| Error::internal("x: gave an inexact value"))?;
4722 let mut out = Vec::with_capacity(2 * v.len());
4723 for r in &v {
4724 out.push(r.numer().clone());
4725 out.push(r.denom().clone());
4726 }
4727 let mut shape = y.shape.clone();
4728 shape.push(2);
4729 Ok(Array::new(shape, Data::Ext(out.into())))
4730 }
4731 -1 => Ok(from_exact(y)),
4732 -2 => {
4734 if !y.dtype().is_numeric() {
4735 return Err(Error::domain(
4736 format!("x: needs real numbers, not {} data", y.dtype().name()),
4737 span,
4738 ));
4739 }
4740 Ok(y.clone())
4741 }
4742 n => Err(Error::domain(
4743 format!("x: converts to form 1, 2, _1 or _2, not {n}"),
4744 span,
4745 )),
4746 }
4747}
4748
4749#[allow(clippy::too_many_arguments)]
4753fn exact_compare_data(
4754 op: ScalarDyad,
4755 x: &Data,
4756 xoff: usize,
4757 xdiv: usize,
4758 y: &Data,
4759 yoff: usize,
4760 ydiv: usize,
4761 n: usize,
4762) -> Option<Data> {
4763 let (xs, ys) = (rat_window(x, xoff, xdiv, n)?, rat_window(y, yoff, ydiv, n)?);
4764 let out: Vec<u8> = (0..n)
4765 .map(|i| {
4766 let ord = xs[i / xdiv].cmp(&ys[i / ydiv]);
4767 cmp_result(op, Some(ord)) as u8
4768 })
4769 .collect();
4770 Some(Data::Bool(out.into()))
4771}
4772
4773#[allow(clippy::too_many_arguments)]
4777fn scalar_dyad_data(
4778 op: ScalarDyad,
4779 x: &Data,
4780 xoff: usize,
4781 xdiv: usize,
4782 y: &Data,
4783 yoff: usize,
4784 ydiv: usize,
4785 n: usize,
4786 tol: Tol,
4787 rules: Rules,
4788 span: Span,
4789) -> Result<Data> {
4790 use ScalarDyad::*;
4791 if x.dtype() == DType::Symbol || y.dtype() == DType::Symbol {
4792 match op {
4793 Eq | Ne | Lt | Le | Gt | Ge => {}
4795 Min | Max => {
4798 return symbol_min_max(op, x, xoff, xdiv, y, yoff, ydiv, n, span);
4799 }
4800 _ => return Err(symbol_arith(span)),
4801 }
4802 }
4803 if matches!(op, Eq | Ne | Lt | Le | Gt | Ge) {
4804 return compare_data(op, x, xoff, xdiv, y, yoff, ydiv, n, tol, span);
4805 }
4806 if matches!(op, Lcm | Gcd) {
4807 return lcm_gcd_data(op, x, xoff, xdiv, y, yoff, ydiv, n, tol, rules, span);
4808 }
4809 let t = arith_type(x.dtype(), y.dtype(), span)?;
4810 if t.is_exact()
4811 && let Some(d) = exact_dyad_data(op, t, x, xoff, xdiv, y, yoff, ydiv, n, span)?
4812 {
4813 return Ok(d);
4814 }
4815 if t == DType::I64 && !matches!(op, DivJ | DivApl | Log | Root | Circle) {
4817 if let Some(d) = int_dyad_data(op, x, xoff, xdiv, y, yoff, ydiv, n) {
4820 return Ok(d);
4821 }
4822 }
4824 if t == DType::Complex
4825 || matches!(op, MakeComplex | PolarBy)
4826 || pass_leaves_reals(op, x, xoff, xdiv, y, yoff, ydiv, n)
4827 {
4828 let data = complex_dyad_data(op, x, xoff, xdiv, y, yoff, ydiv, n, span)?;
4829 if op == Log
4833 && rules.lang == crate::Lang::Apl
4834 && let Data::Complex(v) = &data
4835 && v.iter().any(|z| !z[0].is_finite() || !z[1].is_finite())
4836 {
4837 return Err(Error::domain("this logarithm has no value", span));
4838 }
4839 if op == Circle && circle_reads_a_part(x, xoff, xdiv, n) && let Data::Complex(v) = &data {
4840 return Ok(Data::F64(v.iter().map(|z| z[0]).collect()));
4841 }
4842 return Ok(data);
4843 }
4844 float_dyad_data(op, x, xoff, xdiv, y, yoff, ydiv, n, tol, span)
4845}
4846
4847fn frame_pervaded(frame: Vec<usize>, cells: Vec<Array>, span: Span) -> Result<Array> {
4852 if cells.iter().all(|c| c.rank() == 0 && c.dtype() != DType::Box) {
4853 return assemble(&frame, cells, span);
4854 }
4855 let boxes: Vec<Array> = cells.into_iter().collect();
4856 Ok(Array::new(frame, Data::Box(boxes.into())))
4857}
4858
4859fn pervade_dyad(
4865 op: ScalarDyad,
4866 x: &Array,
4867 y: &Array,
4868 cfg: EvalCfg,
4869 span: Span,
4870) -> Result<Array> {
4871 let p = agree(&x.shape, &y.shape, &x.shape, &y.shape, cfg.agreement, span)?;
4872 if p.n == 0 {
4873 return Ok(Array::new(p.frame, Data::empty(DType::Box)));
4874 }
4875 let (xr, yr) = (x.to_row_major(), y.to_row_major());
4876 let mut cells = Vec::with_capacity(p.n);
4877 for i in 0..p.n {
4878 let a = open_cell(&atom(&xr, i / p.x_div));
4879 let b = open_cell(&atom(&yr, i / p.y_div));
4880 cells.push(scalar_dyad(op, &a, &b, cfg, span)?);
4881 }
4882 frame_pervaded(p.frame, cells, span)
4883}
4884
4885fn pervade_monad(op: ScalarMonad, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
4887 if y.count() == 0 {
4888 return Ok(Array::new(y.shape.clone(), Data::empty(DType::Box)));
4889 }
4890 let yr = y.to_row_major();
4891 let mut cells = Vec::with_capacity(y.count());
4892 for i in 0..y.count() {
4893 let a = open_cell(&atom(&yr, i));
4894 cells.push(scalar_monad(op, &a, cfg, span)?);
4895 }
4896 frame_pervaded(y.shape.clone(), cells, span)
4897}
4898
4899fn as_real(a: &Array) -> Option<Array> {
4903 if a.dtype() != DType::Complex {
4904 return Some(a.clone());
4905 }
4906 let real: Option<Vec<f64>> = a.to_f64_vec();
4907 Some(Array::new(a.shape.clone(), Data::F64(real?.into())))
4908}
4909
4910fn scalar_dyad(
4911 op: ScalarDyad,
4912 x: &Array,
4913 y: &Array,
4914 cfg: EvalCfg,
4915 span: Span,
4916) -> Result<Array> {
4917 if cfg.rules.lang == crate::Lang::Apl
4918 && (x.dtype() == DType::Box || y.dtype() == DType::Box)
4919 {
4920 return pervade_dyad(op, x, y, cfg, span);
4921 }
4922 if matches!(op, ScalarDyad::Min | ScalarDyad::Max | ScalarDyad::Lt
4928 | ScalarDyad::Le | ScalarDyad::Gt | ScalarDyad::Ge)
4929 && (x.dtype() == DType::Complex || y.dtype() == DType::Complex)
4930 && let (Some(a), Some(b)) = (as_real(x), as_real(y))
4931 {
4932 return scalar_dyad(op, &a, &b, cfg, span);
4933 }
4934 let p = agree(&x.shape, &y.shape, &x.shape, &y.shape, cfg.agreement, span)?;
4935 if p.n == 0 {
4939 return Ok(Array::new(p.frame, Data::empty(empty_result_type(x, y))));
4940 }
4941 let data = scalar_dyad_data(
4942 op,
4943 &x.data,
4944 0,
4945 p.x_div,
4946 &y.data,
4947 0,
4948 p.y_div,
4949 p.n,
4950 cfg.tol,
4951 cfg.rules,
4952 span,
4953 )?;
4954 Ok(Array::new(p.frame, data))
4955}
4956
4957fn empty_result_type(x: &Array, y: &Array) -> DType {
4960 for a in [x, y] {
4961 if a.dtype().is_numeric() {
4962 return a.dtype();
4963 }
4964 }
4965 DType::I64
4966}
4967
4968fn fits_i64(v: f64) -> bool {
4970 v.is_finite() && v >= i64::MIN as f64 && v < i64::MAX as f64
4971}
4972
4973fn monad_leaves_reals(op: ScalarMonad, d: &Data) -> bool {
4975 use ScalarMonad::*;
4976 match op {
4977 Imaginary | Polar => d.dtype().is_numeric(),
4979 Sqrt | Ln => match d {
4980 Data::I64(v) => par::any(v, |&x| x < 0),
4981 Data::F64(v) => par::any(v, |&x| x < 0.0),
4982 Data::Ext(v) => v.iter().any(|x| x.sign() == num_bigint::Sign::Minus),
4983 Data::Rat(v) => v.iter().any(|x| x < &Rat::zero()),
4984 _ => false,
4985 },
4986 _ => false,
4987 }
4988}
4989
4990fn complex_monad(op: ScalarMonad, y: &Array, span: Span) -> Result<Array> {
4992 use ScalarMonad::*;
4993 let mut tmp = Vec::new();
4994 let v = borrow_cx(&y.data, &mut tmp);
4995 if y.count() > 0 && v.is_empty() {
4996 return Err(wrong_type(y.dtype(), span));
4997 }
4998 let data = match op {
4999 Abs => Data::F64(par::map(v, |&z| cx::abs(z)).into()),
5001 Not => return Err(Error::domain("logical negation needs values of 0 or 1", span)),
5002 Factorial => {
5003 return Err(Error::not_yet("the factorial of a complex number", span));
5004 }
5005 _ => {
5006 let step: fn(Cx) -> Cx = match op {
5007 Conj => cx::conj,
5008 Neg => cx::neg,
5009 Signum => cx::signum,
5010 Recip => cx::recip,
5011 Sqrt => cx::sqrt,
5012 Exp => cx::exp,
5013 Ln => cx::ln,
5014 Floor => cx::floor,
5015 Ceil => cx::ceil,
5016 OneMinus => |z| cx::sub(cx::ONE, z),
5017 Inc => |z| cx::add(z, cx::ONE),
5018 Dec => |z| cx::sub(z, cx::ONE),
5019 Double => |z| cx::add(z, z),
5020 Halve => |z| [z[0] / 2.0, z[1] / 2.0],
5021 Square => |z| cx::mul(z, z),
5022 Pi => |z| [std::f64::consts::PI * z[0], std::f64::consts::PI * z[1]],
5023 Imaginary => |z| cx::mul(cx::I, z),
5024 Polar => |z| cx::exp(cx::mul(cx::I, z)),
5025 Abs | Not | Factorial => unreachable!("handled above"),
5026 };
5027 Data::Complex(par::map(v, |&z| step(z)).into())
5028 }
5029 };
5030 Ok(Array::new(y.shape.clone(), data).with_layout(y.layout()))
5031}
5032
5033fn scalar_monad(op: ScalarMonad, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
5035 use ScalarMonad::*;
5036 if cfg.rules.lang == crate::Lang::Apl && y.dtype() == DType::Box {
5037 return pervade_monad(op, y, cfg, span);
5038 }
5039 let tol = cfg.tol;
5040 let d = &y.data;
5041 if y.count() == 0 && !d.dtype().is_numeric() {
5044 return Ok(Array::new(y.shape.clone(), Data::empty(DType::I64)));
5045 }
5046 if d.dtype() == DType::Complex || monad_leaves_reals(op, d) {
5047 return complex_monad(op, y, span);
5048 }
5049 if d.dtype().is_exact() && let Some(a) = exact_monad(op, y) {
5050 return Ok(a);
5051 }
5052 let mut tmp = Vec::new();
5056 let data = match op {
5057 Conj if d.dtype().is_numeric() => d.clone(),
5059 Conj => return Err(wrong_type(d.dtype(), span)),
5060 Imaginary | Polar => return Err(Error::internal("a complex monad on the real path")),
5063 Neg => match d {
5064 Data::Bool(v) => Data::I64(par::map(v, |&b| -(b as i64)).into()),
5065 Data::I64(v) => match par::try_map(v, i64::checked_neg) {
5066 Some(out) => Data::I64(out.into()),
5067 None => Data::F64(par::map(v, |&x| -(x as f64)).into()),
5068 },
5069 Data::F64(v) => Data::F64(par::map(v, |&x| -x).into()),
5070 _ => return Err(wrong_type(d.dtype(), span)),
5071 },
5072 Signum => match d {
5073 Data::Bool(v) => Data::I64(par::map(v, |&b| b as i64).into()),
5074 Data::I64(v) => Data::I64(par::map(v, |&x| x.signum()).into()),
5075 Data::F64(v) => Data::F64(
5078 par::map(v, |&x| {
5079 if tol.is_zero(x) {
5080 0.0
5081 } else if x > 0.0 {
5082 1.0
5083 } else if x < 0.0 {
5084 -1.0
5085 } else {
5086 0.0
5087 }
5088 })
5089 .into(),
5090 ),
5091 _ => return Err(wrong_type(d.dtype(), span)),
5092 },
5093 Recip => {
5094 let v = as_f64(d, &mut tmp, span)?;
5099 if !tol.is_j() && par::any(v, |&x| x == 0.0) {
5100 return Err(Error::domain("zero has no reciprocal", span));
5101 }
5102 Data::F64(par::map(v, |&x| if x == 0.0 { f64::INFINITY } else { 1.0 / x }).into())
5103 }
5104 Sqrt => {
5105 let v = as_f64(d, &mut tmp, span)?;
5107 Data::F64(par::map(v, |&x| x.sqrt()).into())
5108 }
5109 Exp => {
5110 let v = as_f64(d, &mut tmp, span)?;
5111 Data::F64(par::map(v, |&x| x.exp()).into())
5112 }
5113 Abs => match d {
5114 Data::Bool(_) => d.clone(),
5115 Data::I64(v) => match par::try_map(v, i64::checked_abs) {
5116 Some(out) => Data::I64(out.into()),
5117 None => Data::F64(par::map(v, |&x| (x as f64).abs()).into()),
5118 },
5119 Data::F64(v) => Data::F64(par::map(v, |&x| x.abs()).into()),
5120 _ => return Err(wrong_type(d.dtype(), span)),
5121 },
5122 Floor | Ceil => match d {
5123 Data::Bool(v) => Data::I64(par::map(v, |&b| b as i64).into()),
5124 Data::I64(_) => d.clone(),
5125 Data::F64(v) => {
5126 let round = |x: f64| if op == Floor { tol.floor(x) } else { tol.ceil(x) };
5127 match par::try_map(v, |x| {
5129 let r = round(x);
5130 fits_i64(r).then_some(r as i64)
5131 }) {
5132 Some(out) => Data::I64(out.into()),
5133 None => Data::F64(par::map(v, |&x| round(x)).into()),
5134 }
5135 }
5136 _ => return Err(wrong_type(d.dtype(), span)),
5137 },
5138 Inc | Dec => {
5139 let step = if op == Inc { 1i64 } else { -1 };
5140 match d {
5141 Data::Bool(v) => Data::I64(par::map(v, |&b| b as i64 + step).into()),
5142 Data::I64(v) => match par::try_map(v, |x: i64| x.checked_add(step)) {
5143 Some(out) => Data::I64(out.into()),
5144 None => Data::F64(par::map(v, |&x| x as f64 + step as f64).into()),
5145 },
5146 Data::F64(v) => Data::F64(par::map(v, |&x| x + step as f64).into()),
5147 _ => return Err(wrong_type(d.dtype(), span)),
5148 }
5149 }
5150 Double | Square => match d {
5151 Data::Bool(v) => {
5152 Data::I64(par::map(v, |&b| if op == Double { 2 * b as i64 } else { b as i64 }).into())
5153 }
5154 Data::I64(v) => {
5155 let f = |x: i64| if op == Double { x.checked_mul(2) } else { x.checked_mul(x) };
5156 match par::try_map(v, f) {
5157 Some(out) => Data::I64(out.into()),
5158 None => Data::F64(
5159 par::map(v, |&x| {
5160 let x = x as f64;
5161 if op == Double { x + x } else { x * x }
5162 })
5163 .into(),
5164 ),
5165 }
5166 }
5167 Data::F64(v) => {
5168 Data::F64(par::map(v, |&x| if op == Double { x + x } else { x * x }).into())
5169 }
5170 _ => return Err(wrong_type(d.dtype(), span)),
5171 },
5172 Halve => {
5173 let v = as_f64(d, &mut tmp, span)?;
5174 Data::F64(par::map(v, |&x| x / 2.0).into())
5175 }
5176 Pi => {
5177 let v = as_f64(d, &mut tmp, span)?;
5178 Data::F64(par::map(v, |&x| std::f64::consts::PI * x).into())
5179 }
5180 Factorial => {
5181 let v = as_f64(d, &mut tmp, span)?;
5182 let out = par::map(v, |&x| factorial_as(x, tol));
5183 if tol.is_j() {
5184 if v.iter().zip(&out).any(|(&x, &r)| tol.made_nan(r, x, 0.0)) {
5188 return Err(Error::nan("`! __` has no value", span));
5189 }
5190 } else if par::any(&out, |v: &f64| !v.is_finite()) {
5191 return Err(Error::domain("this factorial has no value", span));
5195 }
5196 Data::F64(out.into())
5197 }
5198 Ln => {
5199 let v = as_f64(d, &mut tmp, span)?;
5201 if !tol.is_j() && par::any(v, |&x| x == 0.0) {
5205 return Err(Error::domain("zero has no logarithm", span));
5206 }
5207 Data::F64(par::map(v, |&x| x.ln()).into())
5208 }
5209 OneMinus => match d {
5210 Data::Bool(v) => Data::Bool(par::map(v, |&b| 1 - b).into()),
5211 Data::I64(v) => match par::try_map(v, |x: i64| 1i64.checked_sub(x)) {
5212 Some(out) => Data::I64(out.into()),
5213 None => Data::F64(par::map(v, |&x| 1.0 - x as f64).into()),
5214 },
5215 Data::F64(v) => Data::F64(par::map(v, |&x| 1.0 - x).into()),
5216 _ => return Err(wrong_type(d.dtype(), span)),
5217 },
5218 Not => {
5219 let bad = || Error::domain("logical negation needs values of 0 or 1", span);
5220 match d {
5221 Data::Bool(v) => Data::Bool(par::map(v, |&b| 1 - b).into()),
5222 Data::I64(v) => {
5223 let out = par::try_map(v, |x: i64| match x {
5224 0 => Some(1u8),
5225 1 => Some(0u8),
5226 _ => None,
5227 })
5228 .ok_or_else(bad)?;
5229 Data::Bool(out.into())
5230 }
5231 Data::F64(v) => {
5232 let out = par::try_map(v, |x: f64| {
5233 if x == 0.0 {
5234 Some(1u8)
5235 } else if x == 1.0 {
5236 Some(0u8)
5237 } else {
5238 None
5239 }
5240 })
5241 .ok_or_else(bad)?;
5242 Data::Bool(out.into())
5243 }
5244 _ => return Err(bad()),
5245 }
5246 }
5247 };
5248 Ok(Array::new(y.shape.clone(), data).with_layout(y.layout()))
5249}
5250
5251fn transpose_axes(y: &Array) -> Array {
5261 if y.rank() < 2 {
5262 return y.clone();
5263 }
5264 let out_shape: Vec<usize> = y.shape.iter().rev().copied().collect();
5265 let flipped = match y.layout() {
5266 Layout::RowMajor => Layout::ColMajor,
5267 Layout::ColMajor => Layout::RowMajor,
5268 };
5269 Array::new(out_shape, y.data.clone()).with_layout(flipped)
5270}
5271
5272fn iota_j(y: &Array, near: NearInt, span: Span) -> Result<Array> {
5275 if y.rank() > 1 {
5276 return Err(Error::new(
5277 ErrorKind::Rank,
5278 "index generator needs a scalar or vector argument",
5279 Some(span),
5280 ));
5281 }
5282 let dims = y
5283 .to_i64_vec_near(near)
5284 .ok_or_else(|| Error::domain("index generator needs integer lengths", span))?;
5285 let shape: Vec<usize> = dims.iter().map(|d| d.unsigned_abs() as usize).collect();
5286 let n = crate::limits::elements(&shape, span)?;
5287 let st = strides(&shape);
5288 let mut out = Vec::with_capacity(n);
5289 let mut coord = vec![0usize; shape.len()];
5290 for _ in 0..n {
5291 let mut v = 0usize;
5292 for k in 0..shape.len() {
5293 let c = if dims[k] < 0 { shape[k] - 1 - coord[k] } else { coord[k] };
5294 v += c * st[k];
5295 }
5296 out.push(v as i64);
5297 odometer(&mut coord, &shape);
5298 }
5299 let data = Data::I64(out.into());
5300 let data = if y.dtype() == DType::Ext {
5303 data.cast(DType::Ext).ok_or_else(|| Error::internal("integers have no extended form"))?
5304 } else {
5305 data
5306 };
5307 Ok(Array::new(shape, data))
5308}
5309
5310fn head(y: &Array) -> Array {
5312 if y.rank() == 0 {
5313 return y.clone();
5314 }
5315 if y.items() == 0 {
5316 let cell_shape = y.shape[1..].to_vec();
5317 let n: usize = cell_shape.iter().product();
5318 return Array::new(cell_shape, fill_data(y.dtype(), n));
5319 }
5320 y.item(0)
5321}
5322
5323fn behead(y: &Array, span: Span) -> Result<Array> {
5324 if y.rank() == 0 {
5325 return Err(Error::domain("cannot drop the first item of a scalar", span));
5326 }
5327 if y.items() == 0 {
5328 return Ok(y.clone());
5329 }
5330 let m = y.item_size();
5331 let mut shape = y.shape.clone();
5332 shape[0] -= 1;
5333 Ok(Array::new(shape, y.data.slice(m, y.count())))
5334}
5335
5336fn tail(y: &Array) -> Array {
5338 if y.rank() == 0 {
5339 return y.clone();
5340 }
5341 let n = y.items();
5342 if n == 0 {
5343 let cell_shape = y.shape[1..].to_vec();
5344 let m: usize = cell_shape.iter().product();
5345 return Array::new(cell_shape, fill_data(y.dtype(), m));
5346 }
5347 y.item(n - 1)
5348}
5349
5350fn curtail(y: &Array) -> Array {
5352 if y.rank() == 0 {
5353 return Array::empty(y.dtype());
5354 }
5355 let n = y.items();
5356 if n == 0 {
5357 return y.clone();
5358 }
5359 let m = y.item_size();
5360 let mut shape = y.shape.clone();
5361 shape[0] = n - 1;
5362 Array::new(shape, y.data.slice(0, (n - 1) * m))
5363}
5364
5365fn reverse(y: &Array) -> Array {
5367 if y.rank() == 0 {
5368 return y.clone();
5369 }
5370 let n = y.items();
5371 let m = y.item_size();
5372 let mut data = Data::empty(y.dtype());
5373 for i in (0..n).rev() {
5374 for k in 0..m {
5375 push_elem(&mut data, &y.data, i * m + k);
5376 }
5377 }
5378 Array::new(y.shape.clone(), data)
5379}
5380
5381fn rotate(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
5384 let counts = axis_counts(x, "rotate", near, span)?;
5385 if y.rank() == 0 {
5386 return Ok(y.clone());
5387 }
5388 if counts.len() > y.rank() {
5389 return Err(Error::new(
5390 ErrorKind::Length,
5391 format!(
5392 "rotate has {} amounts for an argument of rank {}",
5393 counts.len(),
5394 y.rank()
5395 ),
5396 Some(span),
5397 ));
5398 }
5399 let st = strides(&y.shape);
5400 let n = y.count();
5401 let r = y.rank();
5402 let mut data = Data::empty(y.dtype());
5403 let mut coord = vec![0usize; r];
5404 for _ in 0..n {
5405 let mut idx = 0usize;
5406 for k in 0..r {
5407 let len = y.shape[k] as i64;
5409 let s = counts.get(k).copied().unwrap_or(0).rem_euclid(len);
5413 idx += (coord[k] as i64 + s).rem_euclid(len) as usize * st[k];
5414 }
5415 push_elem(&mut data, &y.data, idx);
5416 odometer(&mut coord, &y.shape);
5417 }
5418 Ok(Array::new(y.shape.clone(), data))
5419}
5420
5421fn rotate_apl(x: &Array, y: &Array, last: bool, near: NearInt, span: Span) -> Result<Array> {
5432 let scalar_like = x.rank() == 0 || (x.rank() == 1 && x.count() == 1);
5433 if y.rank() == 0 {
5436 return if scalar_like {
5437 Ok(y.clone())
5438 } else {
5439 Err(Error::new(
5440 ErrorKind::Rank,
5441 format!(
5442 "rotate has a rank-{} left argument for a scalar, which needs a scalar",
5443 x.rank()
5444 ),
5445 Some(span),
5446 ))
5447 };
5448 }
5449 let axis = if last { y.rank() - 1 } else { 0 };
5450 let want: Vec<usize> =
5451 y.shape.iter().enumerate().filter(|&(k, _)| k != axis).map(|(_, &n)| n).collect();
5452 if !scalar_like {
5453 if x.rank() != want.len() {
5454 return Err(Error::new(
5455 ErrorKind::Rank,
5456 format!(
5457 "rotate has a rank-{} left argument for axis {axis} of {}, which needs rank {}",
5458 x.rank(),
5459 show_shape(&y.shape),
5460 want.len()
5461 ),
5462 Some(span),
5463 ));
5464 }
5465 if x.shape != want {
5466 return Err(Error::new(
5467 ErrorKind::Length,
5468 format!(
5469 "rotate has a {} left argument for axis {axis} of {}, which needs {}",
5470 show_shape(&x.shape),
5471 show_shape(&y.shape),
5472 show_shape(&want)
5473 ),
5474 Some(span),
5475 ));
5476 }
5477 }
5478 let counts = x
5479 .to_i64_vec_near(near)
5480 .ok_or_else(|| Error::domain("rotate needs integer lengths", span))?;
5481 let len = y.shape[axis] as i64;
5482 let n = y.count();
5483 if n == 0 {
5484 return Ok(y.clone());
5485 }
5486 let st = strides(&y.shape);
5487 let r = y.rank();
5488 let mut data = Data::empty(y.dtype());
5489 let mut coord = vec![0usize; r];
5490 for _ in 0..n {
5491 let mut which = 0usize;
5493 for (k, &c) in coord.iter().enumerate() {
5494 if k != axis {
5495 which = which * y.shape[k] + c;
5496 }
5497 }
5498 let s = if scalar_like { counts[0] } else { counts[which] };
5499 let s = s.rem_euclid(len);
5502 let mut idx = 0usize;
5503 for (k, &c) in coord.iter().enumerate() {
5504 let c = if k == axis { (c as i64 + s).rem_euclid(len) as usize } else { c };
5505 idx += c * st[k];
5506 }
5507 push_elem(&mut data, &y.data, idx);
5508 odometer(&mut coord, &y.shape);
5509 }
5510 Ok(Array::new(y.shape.clone(), data))
5511}
5512
5513fn elem_key(d: &Data, i: usize) -> u64 {
5516 match d {
5517 Data::Bool(v) => v[i] as u64,
5518 Data::I64(v) => v[i] as u64,
5519 Data::F64(v) => {
5520 let x = v[i];
5521 if x == 0.0 { 0 } else { x.to_bits() }
5522 }
5523 Data::Complex(v) => cx_key(v[i]),
5524 Data::Char(v) => v[i] as u64,
5525 Data::Symbol(v) => v[i] as u64,
5527 Data::Ext(_) | Data::Rat(_) | Data::Box(_) => 0,
5530 }
5531}
5532
5533fn num_key(d: &Data, i: usize) -> u64 {
5536 match d {
5537 Data::Bool(v) => (v[i] as f64).to_bits(),
5538 Data::I64(v) => (v[i] as f64).to_bits(),
5539 Data::F64(v) => {
5540 let x = v[i];
5541 if x == 0.0 { 0.0f64.to_bits() } else { x.to_bits() }
5542 }
5543 Data::Complex(v) => cx_key(v[i]),
5544 Data::Char(v) => v[i] as u64,
5545 Data::Symbol(v) => v[i] as u64,
5546 Data::Ext(_) | Data::Rat(_) | Data::Box(_) => 0,
5548 }
5549}
5550
5551fn cx_key(z: Cx) -> u64 {
5553 let bits = |x: f64| if x == 0.0 { 0u64 } else { x.to_bits() };
5554 bits(z[0]) ^ bits(z[1]).rotate_left(32)
5555}
5556
5557fn nub(y: &Array, tol: Tol) -> Array {
5559 if y.rank() == 0 {
5560 return Array::new(vec![1], y.data.clone());
5561 }
5562 let n = y.items();
5563 let m = y.item_size();
5564 let mut keep = Vec::new();
5565 if y.dtype() == DType::Box || y.dtype().is_exact() {
5566 for i in 0..n {
5569 if !keep.iter().any(|&j| arrays_match(&y.item(i), &y.item(j), tol)) {
5570 keep.push(i);
5571 }
5572 }
5573 } else if y.dtype() == DType::F64 && tol.ct != 0.0 {
5574 let mut tv = Vec::new();
5577 let v = borrow_f64(&y.data, &mut tv);
5578 for i in 0..n {
5579 if !keep.iter().any(|&j| (0..m).all(|k| tol.eq(v[i * m + k], v[j * m + k]))) {
5580 keep.push(i);
5581 }
5582 }
5583 } else {
5584 let mut seen: HashSet<Vec<u64>> = HashSet::with_capacity(n);
5585 for i in 0..n {
5586 let key: Vec<u64> = (0..m).map(|k| elem_key(&y.data, i * m + k)).collect();
5587 if seen.insert(key) {
5588 keep.push(i);
5589 }
5590 }
5591 }
5592 let mut data = Data::empty(y.dtype());
5593 for &i in &keep {
5594 for k in 0..m {
5595 push_elem(&mut data, &y.data, i * m + k);
5596 }
5597 }
5598 let mut shape = y.shape.clone();
5599 shape[0] = keep.len();
5600 Array::new(shape, data)
5601}
5602
5603#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5608enum Tao {
5609 J,
5610 Apl2,
5611 Dyalog,
5613}
5614
5615impl Tao {
5616 fn of(rules: Rules) -> Tao {
5617 match rules.lang {
5618 crate::Lang::J => Tao::J,
5619 crate::Lang::Apl => match rules.nested_grade {
5620 NestedGrade::Apl2 => Tao::Apl2,
5621 NestedGrade::TotalOrder => Tao::Dyalog,
5622 },
5623 }
5624 }
5625
5626 fn class(self, dt: DType) -> u8 {
5632 match self {
5633 Tao::J => match dt {
5634 DType::Symbol => 1,
5635 DType::Char => 2,
5636 DType::Box => 3,
5637 _ => 0,
5638 },
5639 Tao::Apl2 => match dt {
5640 DType::Char | DType::Symbol => 0,
5641 DType::Box => 2,
5642 _ => 1,
5643 },
5644 Tao::Dyalog => match dt {
5650 DType::Char | DType::Symbol => 2,
5651 DType::Box => 1,
5652 _ => 0,
5653 },
5654 }
5655 }
5656}
5657
5658#[derive(Clone, Copy, Debug)]
5666struct Grading {
5667 tao: Tao,
5668 tol: Tol,
5669}
5670
5671impl Grading {
5672 fn of(rules: Rules, tol: Tol) -> Grading {
5673 let tao = Tao::of(rules);
5674 let exact = tao == Tao::J || tao == Tao::Dyalog;
5678 Grading { tao, tol: if exact { Tol { ct: 0.0, ..tol } } else { tol } }
5679 }
5680
5681 fn class(self, dt: DType) -> u8 {
5682 self.tao.class(dt)
5683 }
5684}
5685
5686fn cmp_items_total(x: &Array, y: &Array, ord: Grading) -> std::cmp::Ordering {
5701 use std::cmp::Ordering::Equal;
5702 match ord.tao {
5703 Tao::Dyalog => cmp_items_dyalog(x, y, ord),
5704 Tao::J => {
5705 let class = |a: &Array| if a.count() == 0 { 0 } else { ord.class(a.dtype()) };
5706 class(x)
5707 .cmp(&class(y))
5708 .then_with(|| x.rank().cmp(&y.rank()))
5709 .then_with(|| x.shape.iter().rev().cmp(y.shape.iter().rev()))
5710 .then_with(|| cmp_atoms(x, y, ord))
5711 }
5712 Tao::Apl2 => x
5713 .rank()
5714 .cmp(&y.rank())
5715 .then_with(|| x.shape.iter().cmp(y.shape.iter()))
5716 .then_with(|| cmp_atoms(x, y, ord))
5717 .then_with(|| {
5718 if x.count() == 0 {
5719 ord.class(x.dtype()).cmp(&ord.class(y.dtype()))
5720 } else {
5721 Equal
5722 }
5723 }),
5724 }
5725}
5726
5727fn cmp_items_dyalog(x: &Array, y: &Array, ord: Grading) -> std::cmp::Ordering {
5742 use std::cmp::Ordering::{Equal, Greater, Less};
5743 if x.rank() == 0 && y.rank() == 0 && x.dtype() != DType::Box && y.dtype() != DType::Box {
5746 return cmp_atoms(x, y, ord);
5747 }
5748 let rank = x.rank().max(y.rank());
5749 let extend = |a: &Array| -> Vec<usize> {
5750 let mut s = vec![1usize; rank - a.rank()];
5751 s.extend_from_slice(&a.shape);
5752 s
5753 };
5754 let (sx, sy) = (extend(x), extend(y));
5755 let common: Vec<usize> = (0..rank).map(|k| sx[k].max(sy[k])).collect();
5756 let (xr, yr) = (x.to_row_major(), y.to_row_major());
5757 let (dx, dy) = (xr.row_major_data(), yr.row_major_data());
5758 let (stx, sty) = (strides(&sx), strides(&sy));
5759 let mut order = Equal;
5760 if !common.contains(&0) {
5761 let mut coord = vec![0usize; rank];
5762 loop {
5763 let inside = |s: &[usize]| (0..rank).all(|k| coord[k] < s[k]);
5764 let at = |st: &[usize]| -> usize { (0..rank).map(|k| coord[k] * st[k]).sum() };
5765 let here = match (inside(&sx), inside(&sy)) {
5766 (true, true) => {
5767 cmp_items_dyalog(&atom_array(dx, at(&stx)), &atom_array(dy, at(&sty)), ord)
5768 }
5769 (true, false) => Greater,
5771 (false, true) => Less,
5772 (false, false) => Equal,
5773 };
5774 if here != Equal {
5775 order = here;
5776 break;
5777 }
5778 odometer(&mut coord, &common);
5782 if coord.iter().all(|&c| c == 0) {
5783 break;
5784 }
5785 }
5786 }
5787 if order != Equal {
5788 return order;
5789 }
5790 match (proto_item(x), proto_item(y)) {
5793 (Some(px), Some(py)) => cmp_items_dyalog(&px, &py, ord),
5796 _ => ord.class(x.dtype()).cmp(&ord.class(y.dtype())),
5797 }
5798 .then_with(|| x.shape.iter().rev().cmp(y.shape.iter().rev()))
5799}
5800
5801fn proto_item(a: &Array) -> Option<Array> {
5807 if let Some(p) = a.proto() {
5808 return Some(p.clone());
5809 }
5810 match a.dtype() {
5811 DType::Box => None,
5812 dt => Some(Array::new(vec![], fill_data(dt, 1))),
5813 }
5814}
5815
5816fn atom_array(d: &Data, i: usize) -> Array {
5819 match d {
5820 Data::Box(v) => v[i].clone(),
5821 _ => {
5822 let mut one = Data::empty(d.dtype());
5823 push_elem(&mut one, d, i);
5824 Array::new(vec![], one)
5825 }
5826 }
5827}
5828
5829fn cmp_atoms(x: &Array, y: &Array, ord: Grading) -> std::cmp::Ordering {
5832 use std::cmp::Ordering::Equal;
5833 let n = x.count();
5834 if n == 0 {
5835 return Equal;
5836 }
5837 let (xr, yr) = (x.to_row_major(), y.to_row_major());
5838 let (dx, dy) = (xr.row_major_data(), yr.row_major_data());
5839 if matches!(dx, Data::Box(_)) || matches!(dy, Data::Box(_)) {
5840 return (0..n)
5841 .map(|i| cmp_items_total(&atom_array(dx, i), &atom_array(dy, i), ord))
5842 .find(|o| *o != Equal)
5843 .unwrap_or(Equal);
5844 }
5845 let classes = ord.class(dx.dtype()).cmp(&ord.class(dy.dtype()));
5847 if classes != Equal {
5848 return classes;
5849 }
5850 match (dx, dy) {
5851 (Data::Char(a), Data::Char(b)) => a[..n].cmp(&b[..n]),
5852 _ => cmp_numbers(dx, dy, n, ord.tol),
5853 }
5854}
5855
5856fn cmp_numbers(dx: &Data, dy: &Data, n: usize, tol: Tol) -> std::cmp::Ordering {
5860 use std::cmp::Ordering::Equal;
5861 let seek = |f: &dyn Fn(usize) -> std::cmp::Ordering| {
5862 (0..n).map(f).find(|o| *o != Equal).unwrap_or(Equal)
5863 };
5864 match DType::promote(dx.dtype(), dy.dtype()) {
5865 Some(DType::Complex) => {
5866 let (mut ta, mut tb) = (Vec::new(), Vec::new());
5867 let (a, b) = (borrow_cx(dx, &mut ta), borrow_cx(dy, &mut tb));
5868 seek(&|k| tol_ord(a[k][0], b[k][0], tol).then_with(|| tol_ord(a[k][1], b[k][1], tol)))
5869 }
5870 Some(DType::F64) => {
5871 let (mut ta, mut tb) = (Vec::new(), Vec::new());
5872 let (a, b) = (borrow_f64(dx, &mut ta), borrow_f64(dy, &mut tb));
5873 seek(&|k| tol_ord(a[k], b[k], tol))
5874 }
5875 Some(t) if t.is_exact() => match (to_rat_vec(dx), to_rat_vec(dy)) {
5876 (Some(a), Some(b)) => seek(&|k| a[k].cmp(&b[k])),
5877 _ => Equal,
5878 },
5879 None => Equal,
5881 Some(_) => {
5882 let (mut ta, mut tb) = (Vec::new(), Vec::new());
5883 let (a, b) = (borrow_i64(dx, &mut ta), borrow_i64(dy, &mut tb));
5884 seek(&|k| a[k].cmp(&b[k]))
5885 }
5886 }
5887}
5888
5889fn cmp_items(d: &Data, i: usize, j: usize, m: usize, ord: Grading) -> std::cmp::Ordering {
5893 use std::cmp::Ordering::Equal;
5894 let (a, b) = (i * m, j * m);
5895 let ord = |k: usize| match d {
5896 Data::Bool(v) => v[a + k].cmp(&v[b + k]),
5897 Data::I64(v) => v[a + k].cmp(&v[b + k]),
5898 Data::F64(v) => tol_ord(v[a + k], v[b + k], ord.tol),
5899 Data::Complex(v) => tol_ord(v[a + k][0], v[b + k][0], ord.tol)
5906 .then_with(|| tol_ord(v[a + k][1], v[b + k][1], ord.tol)),
5907 Data::Char(v) => v[a + k].cmp(&v[b + k]),
5908 Data::Symbol(v) => crate::symbol::cmp(v[a + k], v[b + k]),
5911 Data::Ext(v) => v[a + k].cmp(&v[b + k]),
5914 Data::Rat(v) => v[a + k].cmp(&v[b + k]),
5915 Data::Box(v) => cmp_items_total(&v[a + k], &v[b + k], ord),
5918 };
5919 (0..m).map(ord).find(|o| *o != Equal).unwrap_or(Equal)
5920}
5921
5922fn grade_order(y: &Array, down: bool, ord: Grading) -> Vec<usize> {
5924 if y.rank() == 0 {
5925 return vec![0];
5926 }
5927 let n = y.items();
5928 let m = y.item_size();
5929 let mut idx: Vec<usize> = (0..n).collect();
5930 if down {
5933 idx.sort_by(|&a, &b| cmp_items(&y.data, b, a, m, ord));
5934 } else {
5935 idx.sort_by(|&a, &b| cmp_items(&y.data, a, b, m, ord));
5936 }
5937 idx
5938}
5939
5940fn collate_grade(x: &Array, y: &Array, down: bool, origin: i64, span: Span) -> Result<Array> {
5945 let chars_of = |a: &Array| -> Result<Vec<char>> {
5946 match a.row_major_data() {
5947 Data::Char(v) => Ok(v.as_slice().to_vec()),
5948 _ => Err(Error::domain("a collating grade takes characters", span)),
5949 }
5950 };
5951 let (xs, ys) = (chars_of(x)?, chars_of(y)?);
5952 let xshape = if x.rank() == 0 { vec![1] } else { x.shape.clone() };
5953 let width = xshape.len();
5954 let absent: Vec<usize> = xshape.iter().rev().copied().collect();
5958 let mut keys: std::collections::HashMap<char, Vec<usize>> =
5959 std::collections::HashMap::new();
5960 let xst = strides(&xshape);
5961 for (i, &c) in xs.iter().enumerate() {
5962 keys.entry(c).or_insert_with(|| {
5963 (0..width).map(|a| (i / xst[a]) % xshape[a]).rev().collect()
5964 });
5965 }
5966 let key_of = |c: char| keys.get(&c).unwrap_or(&absent).clone();
5967 let n = if y.rank() == 0 { 1 } else { y.items() };
5968 let m = if n == 0 { 0 } else { ys.len() / n };
5969 let item_keys: Vec<Vec<usize>> = (0..n)
5970 .map(|i| ys[i * m..(i + 1) * m].iter().flat_map(|&c| key_of(c)).collect())
5971 .collect();
5972 let mut idx: Vec<usize> = (0..n).collect();
5973 if down {
5974 idx.sort_by(|&a, &b| item_keys[b].cmp(&item_keys[a]));
5975 } else {
5976 idx.sort_by(|&a, &b| item_keys[a].cmp(&item_keys[b]));
5977 }
5978 Ok(Array::from_i64(idx.into_iter().map(|i| origin + i as i64).collect()))
5979}
5980
5981fn atomic_rep(y: &Array, ctx: &Ctx<'_>, span: Span) -> Result<Array> {
5985 let name = match y.as_boxes() {
5986 Some([b]) if y.rank() == 0 => crate::gerund::text_of(b),
5987 _ => None,
5988 };
5989 let Some(name) = name else {
5990 return Err(Error::domain("5!:1 takes a boxed name", span));
5991 };
5992 if let Some(v) = ctx.env.verb(&name) {
5993 let ar = crate::gerund::verb_ar(v).ok_or_else(|| {
5994 Error::not_yet(
5995 format!("the atomic representation of {}", v.name()),
5996 span,
5997 )
5998 })?;
5999 return Ok(Array::boxed(ar.to_array()));
6000 }
6001 match ctx.env.get(&name) {
6002 Some(a) => Ok(Array::boxed(crate::gerund::Ar::Noun(a).to_array())),
6003 None => Err(Error::new(
6004 ErrorKind::Value,
6005 format!("undefined name: {name}"),
6006 Some(span),
6007 )),
6008 }
6009}
6010
6011fn catalogue(y: &Array, span: Span) -> Result<Array> {
6015 let items = if y.rank() == 0 { vec![y.clone()] } else { y.cells(1) };
6016 let opened: Vec<Array> = items
6018 .iter()
6019 .map(|it| match it.as_boxes() {
6020 Some(bs) if it.rank() == 0 => bs[0].clone(),
6021 _ => it.clone(),
6022 })
6023 .collect();
6024 let mut shape: Vec<usize> = Vec::new();
6025 for o in &opened {
6026 shape.extend_from_slice(&o.shape);
6027 }
6028 let total: usize = shape.iter().product();
6029 let mut out = Vec::with_capacity(total);
6030 let mut coord = vec![0usize; shape.len()];
6031 for _ in 0..total {
6032 let mut at = 0usize;
6033 let mut picks = Vec::with_capacity(opened.len());
6034 for o in &opened {
6035 let st = strides(&o.shape);
6036 let idx: usize = (0..o.rank()).map(|a| coord[at + a] * st[a]).sum();
6037 at += o.rank();
6038 let mut data = Data::empty(o.dtype());
6039 push_elem(&mut data, o.row_major_data(), idx);
6040 picks.push(Array::new(vec![], data));
6041 }
6042 out.push(assemble(&[picks.len()], picks, span)?);
6043 odometer(&mut coord, &shape);
6044 }
6045 Ok(Array::new(shape, Data::Box(out.into())))
6046}
6047
6048fn raze_in(y: &Array, tol: Tol, span: Span) -> Result<Array> {
6051 let all = raze(y, span)?;
6052 let n = if all.rank() == 0 { 1 } else { all.items() };
6053 let elements: Vec<Array> = (0..y.count())
6054 .map(|i| {
6055 let mut data = Data::empty(y.dtype());
6056 push_elem(&mut data, y.row_major_data(), i);
6057 let one = Array::new(vec![], data);
6058 match one.as_boxes() {
6059 Some(bs) => bs[0].clone(),
6060 None => one,
6061 }
6062 })
6063 .collect();
6064 let mut out = Vec::with_capacity(elements.len() * n);
6065 for e in &elements {
6066 let row = member_j(&all, e, tol);
6067 out.extend_from_slice(row.to_i64_vec().unwrap_or_default().as_slice());
6068 }
6069 let mut shape = y.shape.clone();
6070 shape.push(n);
6071 Ok(Array::new(shape, Data::Bool(out.into_iter().map(|v| v as u8).collect::<Vec<u8>>().into())))
6072}
6073
6074fn select_items(y: &Array, order: &[usize]) -> Array {
6076 let m = y.item_size();
6077 let mut data = Data::empty(y.dtype());
6078 for &i in order {
6079 for k in 0..m {
6080 push_elem(&mut data, &y.data, i * m + k);
6081 }
6082 }
6083 let mut shape = y.shape.clone();
6084 shape[0] = order.len();
6085 Array::new(shape, data)
6086}
6087
6088fn check_gradable(y: &Array, rules: Rules, span: Span) -> Result<()> {
6093 if y.dtype() == DType::Complex && rules.complex_order != ComplexOrder::RealThenImaginary {
6094 return Err(Error::not_yet("grading complex values by magnitude and angle", span));
6095 }
6096 Ok(())
6097}
6098
6099fn grade_select(
6103 x: &Array,
6104 y: &Array,
6105 down: bool,
6106 rules: Rules,
6107 tol: Tol,
6108 span: Span,
6109) -> Result<Array> {
6110 check_gradable(y, rules, span)?;
6111 let order = grade_order(y, down, Grading::of(rules, tol));
6112 if x.rank() == 0 {
6116 if let Some(&past) = order.iter().find(|&&i| i > 0) {
6117 return Err(Error::domain(
6118 format!("index {past} is out of range: the argument has 1 item"),
6119 span,
6120 ));
6121 }
6122 return Ok(select_items(&as_list(x), &order));
6125 }
6126 if let Some(&past) = order.iter().find(|&&i| i >= x.items()) {
6127 return Err(Error::domain(
6128 format!("index {past} is out of range: the argument has {} items", x.items()),
6129 span,
6130 ));
6131 }
6132 Ok(select_items(x, &order))
6133}
6134
6135pub(crate) fn arrays_match(x: &Array, y: &Array, tol: Tol) -> bool {
6138 if x.shape != y.shape {
6139 return false;
6140 }
6141 if x.layout() != y.layout() {
6144 return arrays_match(&x.to_row_major(), &y.to_row_major(), tol);
6145 }
6146 if x.count() == 0 {
6149 return true;
6150 }
6151 if let (Data::Box(a), Data::Box(b)) = (&x.data, &y.data) {
6152 return a.iter().zip(b.iter()).all(|(p, q)| arrays_match(p, q, tol));
6153 }
6154 let (dx, dy) = (x.dtype(), y.dtype());
6155 match DType::promote(dx, dy) {
6156 None => false,
6157 Some(DType::Char) => match (&x.data, &y.data) {
6158 (Data::Char(a), Data::Char(b)) => a.as_slice() == b.as_slice(),
6159 _ => false,
6160 },
6161 Some(DType::Symbol) => match (&x.data, &y.data) {
6164 (Data::Symbol(a), Data::Symbol(b)) => a.as_slice() == b.as_slice(),
6165 _ => false,
6166 },
6167 Some(DType::F64) => {
6168 let (mut ta, mut tb) = (Vec::new(), Vec::new());
6169 let a = borrow_f64(&x.data, &mut ta);
6170 let b = borrow_f64(&y.data, &mut tb);
6171 a.iter().zip(b).all(|(p, q)| tol.eq(*p, *q))
6172 }
6173 Some(DType::Complex) => {
6174 let (mut ta, mut tb) = (Vec::new(), Vec::new());
6175 let a = borrow_cx(&x.data, &mut ta);
6176 let b = borrow_cx(&y.data, &mut tb);
6177 a.iter().zip(b).all(|(p, q)| tol.eq_cx(*p, *q))
6178 }
6179 Some(t) if t.is_exact() => match (to_rat_vec(&x.data), to_rat_vec(&y.data)) {
6180 (Some(a), Some(b)) => a == b,
6181 _ => false,
6182 },
6183 Some(_) => {
6184 let (mut ta, mut tb) = (Vec::new(), Vec::new());
6185 let a = borrow_i64(&x.data, &mut ta);
6186 let b = borrow_i64(&y.data, &mut tb);
6187 a.iter().zip(b).all(|(p, q)| p == q)
6188 }
6189 }
6190}
6191
6192fn item_or_self(a: &Array, i: usize) -> Array {
6194 if a.rank() == 0 { a.clone() } else { a.item(i) }
6195}
6196
6197fn member_j(x: &Array, y: &Array, tol: Tol) -> Array {
6200 let cell_rank = y.rank().saturating_sub(1).min(x.rank());
6201 let frame_rank = x.rank() - cell_rank;
6202 let frame: Vec<usize> = x.shape[..frame_rank].to_vec();
6203 let nf: usize = frame.iter().product();
6204 let items = y.items();
6205 let mut out = Vec::with_capacity(nf);
6206 for i in 0..nf {
6207 let cell = x.cell_at(frame_rank, i);
6208 out.push((0..items).any(|j| arrays_match(&cell, &item_or_self(y, j), tol)) as u8);
6209 }
6210 Array::new(frame, Data::Bool(out.into()))
6211}
6212
6213fn member_apl(x: &Array, y: &Array, tol: Tol) -> Array {
6215 let n = x.count();
6216 if x.dtype() == DType::Box
6217 || y.dtype() == DType::Box
6218 || x.dtype().is_exact()
6219 || y.dtype().is_exact()
6220 {
6221 let opened = |a: &Array, i: usize| -> Array {
6227 let e = atom(a, i);
6228 match e.as_boxes() {
6229 Some([b]) if b.rank() == 0 && b.dtype() != DType::Box => b.clone(),
6230 _ => e,
6231 }
6232 };
6233 let out: Vec<u8> = (0..n)
6234 .map(|i| {
6235 let e = opened(x, i);
6236 u8::from((0..y.count()).any(|j| arrays_match(&e, &opened(y, j), tol)))
6237 })
6238 .collect();
6239 return Array::new(x.shape.clone(), Data::Bool(out.into()));
6240 }
6241 if x.dtype() != y.dtype()
6242 && [x.dtype(), y.dtype()].iter().any(|&d| matches!(d, DType::Char | DType::Symbol))
6243 {
6244 return Array::new(x.shape.clone(), Data::Bool(vec![0u8; n].into()));
6245 }
6246 if tol.ct != 0.0
6247 && (x.dtype() == DType::F64 || y.dtype() == DType::F64)
6248 && x.dtype() != DType::Char
6249 {
6250 let (mut tx, mut ty) = (Vec::new(), Vec::new());
6252 let xs = borrow_f64(&x.data, &mut tx);
6253 let ys = borrow_f64(&y.data, &mut ty);
6254 let out: Vec<u8> =
6255 xs.iter().map(|a| ys.iter().any(|b| tol.eq(*a, *b)) as u8).collect();
6256 return Array::new(x.shape.clone(), Data::Bool(out.into()));
6257 }
6258 let seen: HashSet<u64> = (0..y.count()).map(|i| num_key(&y.data, i)).collect();
6259 let out: Vec<u8> =
6260 (0..n).map(|i| seen.contains(&num_key(&x.data, i)) as u8).collect();
6261 Array::new(x.shape.clone(), Data::Bool(out.into()))
6262}
6263
6264fn index_of(
6270 x: &Array,
6271 y: &Array,
6272 origin: i64,
6273 vector_left: bool,
6274 tol: Tol,
6275 span: Span,
6276) -> Result<Array> {
6277 if vector_left && x.rank() != 1 {
6278 return Err(Error::new(
6279 ErrorKind::Rank,
6280 format!("⍳ looks up in a vector, and its left argument has rank {}", x.rank()),
6281 Some(span),
6282 ));
6283 }
6284 let cell_rank = x.rank().saturating_sub(1).min(y.rank());
6285 let frame_rank = y.rank() - cell_rank;
6286 let frame: Vec<usize> = y.shape[..frame_rank].to_vec();
6287 let nf: usize = frame.iter().product();
6288 let items = x.items();
6289 let mut out = Vec::with_capacity(nf);
6290 for i in 0..nf {
6291 let cell = y.cell_at(frame_rank, i);
6292 let at = (0..items)
6293 .find(|&j| arrays_match(&cell, &item_or_self(x, j), tol))
6294 .unwrap_or(items);
6295 out.push(origin + at as i64);
6296 }
6297 Ok(Array::new(frame, Data::I64(out.into())))
6298}
6299
6300fn from_index(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
6302 if let Some(spec) = x.as_boxes().and_then(<[Array]>::first) {
6305 let spec = index_spec(spec, y, near, span)?;
6306 return Ok(select_spec(&spec, y));
6307 }
6308 let idx = x
6309 .to_i64_vec_near(near)
6310 .ok_or_else(|| Error::domain("index must be an integer", span))?;
6311 let Some(&i) = idx.first() else {
6312 return Err(Error::internal("from_index with no index"));
6313 };
6314 let n = y.items() as i64;
6315 let k = if i < 0 { i + n } else { i };
6316 if k < 0 || k >= n {
6317 return Err(Error::domain(
6318 format!("index {i} is out of range: the argument has {n} items"),
6319 span,
6320 ));
6321 }
6322 Ok(item_or_self(y, k as usize))
6323}
6324
6325fn cat_promote(
6329 a: &Array,
6330 other: &Array,
6331 rank: usize,
6332 axis: usize,
6333 deep: bool,
6334 span: Span,
6335) -> Result<Array> {
6336 if a.rank() == rank {
6337 return Ok(a.clone());
6338 }
6339 if a.rank() == 0 {
6340 let mut shape =
6341 if other.rank() == rank { other.shape.clone() } else { vec![1usize; rank] };
6342 shape[axis] = 1;
6343 let n: usize = shape.iter().product();
6344 let mut data = Data::empty(a.dtype());
6345 for _ in 0..n {
6346 push_elem(&mut data, &a.data, 0);
6347 }
6348 return Ok(Array::new(shape, data));
6349 }
6350 if a.rank() + 1 == rank || (deep && a.rank() < rank) {
6355 let mut shape = a.shape.clone();
6356 for _ in a.rank()..rank {
6357 shape.insert(axis, 1);
6358 }
6359 return Ok(Array::new(shape, a.data.clone()));
6360 }
6361 Err(Error::new(
6362 ErrorKind::Rank,
6363 format!("cannot catenate rank {} with rank {}", a.rank(), other.rank()),
6364 Some(span),
6365 ))
6366}
6367
6368fn empty_type(x: &Array, y: &Array) -> Option<DType> {
6377 match (x.count() == 0, y.count() == 0) {
6378 (true, false) => Some(y.dtype()),
6379 (false, true) => Some(x.dtype()),
6380 (true, true) => Some(match (x.dtype(), y.dtype()) {
6381 (DType::Box, _) | (_, DType::Box) => DType::Box,
6382 (DType::Char, _) | (_, DType::Char) => DType::Char,
6383 (a, b) => DType::promote(a, b)?,
6384 }),
6385 (false, false) => None,
6386 }
6387}
6388
6389pub(crate) fn catenate(
6391 x: &Array,
6392 y: &Array,
6393 leading: bool,
6394 fill: bool,
6395 span: Span,
6396) -> Result<Array> {
6397 let rank = x.rank().max(y.rank()).max(1);
6398 let axis = if leading { 0 } else { rank - 1 };
6399 let deep = fill && leading;
6400 let xa = cat_promote(x, y, rank, axis, deep, span)?;
6401 let ya = cat_promote(y, x, rank, axis, deep, span)?;
6402 let (xa, ya) = match empty_type(&xa, &ya)
6408 .filter(|_| fill && DType::promote(xa.dtype(), ya.dtype()).is_none())
6409 {
6410 None => (xa, ya),
6411 Some(dt) => {
6412 let retype = |a: Array| {
6413 if a.count() == 0 && a.dtype() != dt {
6414 Array::new(a.shape.clone(), Data::empty(dt))
6415 } else {
6416 a
6417 }
6418 };
6419 (retype(xa), retype(ya))
6420 }
6421 };
6422 let mut ragged = false;
6426 let want: Vec<i64> = (0..rank)
6427 .map(|k| {
6428 ragged |= k != axis && xa.shape[k] != ya.shape[k];
6429 xa.shape[k].max(ya.shape[k]) as i64
6430 })
6431 .collect();
6432 if ragged && !fill {
6433 return Err(Error::new(
6434 ErrorKind::Length,
6435 format!(
6436 "cannot catenate: left shape {}, right shape {}",
6437 show_shape(&xa.shape),
6438 show_shape(&ya.shape)
6439 ),
6440 Some(span),
6441 ));
6442 }
6443 let (xa, ya) = if ragged {
6444 let fit = |a: &Array| -> Result<Array> {
6445 let mut to = want.clone();
6446 to[axis] = a.shape[axis] as i64;
6447 take(&Array::from_i64(to), a, false, false, NearInt::J, span)
6450 };
6451 (fit(&xa)?, fit(&ya)?)
6452 } else {
6453 (xa, ya)
6454 };
6455 let (xa, ya) = if !fill && (xa.dtype() == DType::Box) != (ya.dtype() == DType::Box) {
6459 (nest_like(&xa, &ya), nest_like(&ya, &xa))
6460 } else {
6461 (xa, ya)
6462 };
6463 let mixing = !fill
6467 && xa.dtype() != DType::Box
6468 && ya.dtype() != DType::Box
6469 && DType::promote(xa.dtype(), ya.dtype()).is_none();
6470 let (xa, ya) =
6471 if mixing { (spread_scalars(&xa), spread_scalars(&ya)) } else { (xa, ya) };
6472 let dt = DType::promote(xa.dtype(), ya.dtype())
6473 .ok_or_else(|| {
6474 let boxed = xa.dtype() == DType::Box || ya.dtype() == DType::Box;
6475 let what = if boxed {
6476 "cannot catenate boxed and unboxed data; box the other side first"
6477 } else {
6478 "cannot catenate character and numeric data"
6479 };
6480 Error::new(ErrorKind::Type, what, Some(span))
6481 })?;
6482 let widen = |a: &Array| -> Result<Data> {
6483 if a.dtype() == dt {
6484 Ok(a.data.clone())
6485 } else if a.count() == 0 {
6486 Ok(Data::empty(dt))
6490 } else {
6491 a.data.cast(dt).ok_or_else(|| Error::internal("unsupported widening in catenate"))
6492 }
6493 };
6494 let xd = widen(&xa)?;
6495 let yd = widen(&ya)?;
6496 let outer: usize = xa.shape[..axis].iter().product();
6497 let ix: usize = xa.shape[axis..].iter().product();
6498 let iy: usize = ya.shape[axis..].iter().product();
6499 let mut data = Data::empty(dt);
6500 for o in 0..outer {
6501 for k in 0..ix {
6502 push_elem(&mut data, &xd, o * ix + k);
6503 }
6504 for k in 0..iy {
6505 push_elem(&mut data, &yd, o * iy + k);
6506 }
6507 }
6508 let mut shape = xa.shape.clone();
6509 shape[axis] = xa.shape[axis] + ya.shape[axis];
6510 Ok(Array::new(shape, data))
6511}
6512
6513fn copy_items(x: &Array, y: &Array, apl: bool, near: NearInt, span: Span) -> Result<Array> {
6521 let counts = x
6522 .to_i64_vec_near(near)
6523 .ok_or_else(|| Error::domain("replication counts must be integers", span))?;
6524 if !apl && counts.iter().any(|&c| c < 0) {
6525 return Err(Error::domain("replication counts must be nonnegative", span));
6526 }
6527 let one_item = apl && x.rank() > 0 && y.rank() > 0 && y.items() == 1 && counts.len() != 1;
6531 let scalar_y = y.rank() == 0 || one_item;
6532 let m = y.item_size();
6533 let n = if x.rank() == 0 || !scalar_y { y.items() } else { counts.len() };
6534 let per = if x.rank() == 0 { vec![counts[0]; n] } else { counts };
6535 if per.len() != n {
6536 return Err(Error::new(
6537 ErrorKind::Length,
6538 format!("{} replication count(s) for {n} item(s)", per.len()),
6539 Some(span),
6540 ));
6541 }
6542 let items: u128 = per.iter().map(|&c| c.unsigned_abs() as u128).sum();
6545 let total = crate::limits::count(items * m.max(1) as u128, span)? / m.max(1);
6546 let fill = if apl { prototype_of(y) } else { None };
6547 let mut data = Data::empty(y.dtype());
6548 for (i, &c) in per.iter().enumerate() {
6549 let src = if scalar_y { 0 } else { i };
6551 for _ in 0..c.unsigned_abs() {
6552 for k in 0..m {
6553 if c < 0 {
6554 push_gap(&mut data, &fill);
6555 } else {
6556 push_elem(&mut data, &y.data, src * m + k);
6557 }
6558 }
6559 }
6560 }
6561 let mut shape = if y.rank() == 0 { vec![1] } else { y.shape.clone() };
6564 shape[0] = total;
6565 Ok(keep_proto(Array::new(shape, data), y, apl))
6566}
6567
6568fn format_chars(y: &Array, opts: &FmtOpts) -> Array {
6577 if y.is_sparse() {
6580 let text = crate::fmt::format_array(y, opts);
6581 let lines: Vec<&str> = text.lines().collect();
6582 let width = lines.iter().map(|l| l.chars().count()).max().unwrap_or(0);
6583 let mut chars: Vec<char> = Vec::with_capacity(lines.len() * width);
6584 for line in &lines {
6585 chars.extend(line.chars());
6586 chars.resize(chars.len() + width - line.chars().count(), ' ');
6587 }
6588 return Array::new(vec![lines.len(), width], Data::Char(chars.into()));
6589 }
6590 if y.dtype() == DType::Char {
6591 return y.clone();
6592 }
6593 if y.count() == 0 {
6595 return Array::new(y.shape.clone(), Data::empty(DType::Char));
6596 }
6597 let text = crate::fmt::format_array(y, opts);
6598 if y.dtype() == DType::Box {
6599 let lines = text.lines().filter(|l| !l.is_empty()).count();
6604 let rows: usize =
6605 if y.rank() == 0 { 1 } else { y.shape[..y.rank() - 1].iter().product() };
6606 if lines != rows {
6607 return text_planes(&text, &y.shape[..y.rank().saturating_sub(2)]);
6608 }
6609 }
6610 if y.rank() < 2 {
6611 let chars: Vec<char> = text.chars().collect();
6612 return Array::new(vec![chars.len()], Data::Char(chars.into()));
6613 }
6614 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 mut chars: Vec<char> = Vec::with_capacity(lines.len() * width);
6619 for line in &lines {
6620 chars.extend(line.chars());
6621 chars.resize(chars.len() + width - line.chars().count(), ' ');
6622 }
6623 let mut shape = y.shape[..y.rank() - 1].to_vec();
6626 shape.push(width);
6627 debug_assert_eq!(lines.len(), shape[..shape.len() - 1].iter().product::<usize>());
6628 Array::new(shape, Data::Char(chars.into()))
6629}
6630
6631fn text_planes(text: &str, frame: &[usize]) -> Array {
6634 let lines: Vec<&str> = text.lines().filter(|l| !l.is_empty()).collect();
6635 let width = lines.iter().map(|l| l.chars().count()).max().unwrap_or(0);
6636 let planes: usize = frame.iter().product::<usize>().max(1);
6637 let per = lines.len() / planes;
6638 let mut chars: Vec<char> = Vec::with_capacity(lines.len() * width);
6639 for line in &lines {
6640 chars.extend(line.chars());
6641 chars.resize(chars.len() + width - line.chars().count(), ' ');
6642 }
6643 let mut shape = frame.to_vec();
6644 shape.push(per);
6645 shape.push(width);
6646 Array::new(shape, Data::Char(chars.into()))
6647}
6648
6649fn digits_of(a: &Array, what: &str, span: Span) -> Result<Vec<f64>> {
6651 a.to_f64_vec().ok_or_else(|| Error::domain(format!("{what} needs numeric data"), span))
6652}
6653
6654fn narrow(values: Vec<f64>, integral: bool) -> Data {
6658 if integral && values.iter().all(|&v| v.fract() == 0.0 && fits_i64(v)) {
6659 return Data::I64(values.iter().map(|&v| v as i64).collect::<Vec<_>>().into());
6660 }
6661 Data::F64(values.into())
6662}
6663
6664fn is_integral(a: &Array) -> bool {
6666 !matches!(a.dtype(), DType::F64 | DType::Rat | DType::Char | DType::Symbol)
6667}
6668
6669fn decode_exact(x: Option<&Array>, y: &Array) -> Option<Array> {
6675 let yr = y.to_row_major();
6676 let digits = to_rat_vec(&yr.data)?;
6677 let two = Rat::from_int(Ext::from(2));
6678 let mut digits = digits;
6679 let radix: Vec<Rat> = match x {
6680 None => vec![two; digits.len()],
6681 Some(x) => {
6682 let r = to_rat_vec(&x.to_row_major().data)?;
6683 if y.rank() == 0 && r.len() != 1 {
6688 digits = vec![digits[0].clone(); r.len()];
6689 }
6690 match r.len() {
6691 1 => vec![r[0].clone(); digits.len()],
6692 n if n == digits.len() => r,
6693 _ => return None,
6694 }
6695 }
6696 };
6697 let mut acc = Rat::from_int(Ext::from(0));
6698 for (d, b) in digits.iter().zip(&radix) {
6699 acc = acc.mul(b).add(d);
6700 }
6701 let exact_in = |a: &Array| matches!(a.dtype(), DType::Ext | DType::Rat);
6702 if exact_in(y) || x.is_some_and(exact_in) {
6703 return Some(Array::new(Vec::new(), exact_data(DType::Ext, vec![acc])));
6704 }
6705 let whole = acc.to_int()?;
6708 Some(Array::scalar_i64(exact::ext_to_i64(&whole)?))
6709}
6710
6711fn decode(x: Option<&Array>, y: &Array, tol: Tol, span: Span) -> Result<Array> {
6714 if let Some(exact) = decode_exact(x, y) {
6715 return Ok(exact);
6716 }
6717 let mut digits = digits_of(y, "decode", span)?;
6718 let radix: Vec<f64> = match x {
6719 None => vec![2.0; digits.len()],
6720 Some(x) => {
6721 let r = digits_of(x, "decode", span)?;
6722 if y.rank() == 0 && r.len() != 1 {
6725 digits = vec![digits[0]; r.len()];
6726 }
6727 match r.len() {
6728 1 => vec![r[0]; digits.len()],
6729 n if n == digits.len() => r,
6730 n => {
6731 return Err(Error::new(
6732 ErrorKind::Length,
6733 format!("{n} radices for {} digits", digits.len()),
6734 Some(span),
6735 ));
6736 }
6737 }
6738 }
6739 };
6740 let mut acc = 0.0f64;
6741 for (d, b) in digits.iter().zip(&radix) {
6742 acc = tol.mul(acc, *b) + d;
6746 }
6747 let integral = is_integral(y) && x.is_none_or(is_integral);
6748 Ok(Array::new(vec![], narrow(vec![acc], integral)))
6749}
6750
6751fn decode_apl(x: &Array, y: &Array, span: Span) -> Result<Array> {
6755 let empty = y.count() == 0;
6759 let mut digits = if empty { Vec::new() } else { digits_of(y, "decode", span)? };
6760 let radices = if empty { vec![0.0; x.count()] } else { digits_of(x, "decode", span)? };
6761 let mut k = if y.rank() == 0 { 1 } else { y.shape[0] };
6766 let mut n: usize = if y.rank() == 0 { 1 } else { y.shape[1..].iter().product() };
6767 let (rows, width) = match x.rank() {
6768 0 => (1usize, 0usize),
6769 r => (x.shape[..r - 1].iter().product(), x.shape[r - 1]),
6770 };
6771 if y.count() == 1 && width > 1 && width != k {
6776 digits = vec![digits[0]; width];
6777 k = width;
6778 n = 1;
6779 }
6780 if width > 1 && k != 0 && width != k {
6784 return Err(Error::new(
6785 ErrorKind::Length,
6786 format!("{width} radices for {k} digits"),
6787 Some(span),
6788 ));
6789 }
6790 let per_row = if x.rank() > 0 && width == 0 { 0 } else { k };
6794 let mut out = vec![0.0f64; rows * n];
6795 for i in 0..rows {
6796 for j in 0..n {
6797 let mut acc = 0.0f64;
6798 for d in 0..per_row {
6799 let b = if width <= 1 { radices[i * width] } else { radices[i * width + d] };
6800 acc = acc * b + digits[d * n + j];
6801 }
6802 out[i * n + j] = acc;
6803 }
6804 }
6805 let mut shape: Vec<usize> = if x.rank() == 0 {
6806 Vec::new()
6807 } else {
6808 x.shape[..x.rank() - 1].to_vec()
6809 };
6810 if y.rank() > 0 {
6811 shape.extend_from_slice(&y.shape[1..]);
6812 }
6813 let integral = is_integral(y) && (empty || is_integral(x));
6816 Ok(Array::new(shape, narrow(out, integral)))
6817}
6818
6819fn encode_apl(x: &Array, y: &Array, tol: Tol, span: Span) -> Result<Array> {
6822 let empty = y.count() == 0;
6825 let radices = if empty { vec![0.0; x.count()] } else { digits_of(x, "encode", span)? };
6826 let values = if empty { Vec::new() } else { digits_of(y, "encode", span)? };
6827 let k = if x.rank() == 0 { 1 } else { x.shape[0] };
6828 let frames = if k == 0 { 0 } else { radices.len() / k };
6829 let n = values.len();
6830 let mut out = vec![0.0f64; k * frames * n];
6831 let mut radix = vec![0.0f64; k];
6832 let mut cell = vec![0.0f64; k];
6833 for p in 0..frames {
6834 for (i, r) in radix.iter_mut().enumerate() {
6835 *r = radices[i * frames + p];
6836 }
6837 for (j, &v) in values.iter().enumerate() {
6838 encode_one(&radix, v, &mut cell, tol);
6839 for i in 0..k {
6840 out[(i * frames + p) * n + j] = cell[i];
6841 }
6842 }
6843 }
6844 let mut shape = x.shape.clone();
6845 shape.extend_from_slice(&y.shape);
6846 Ok(Array::new(shape, narrow(out, empty || (is_integral(x) && is_integral(y)))))
6847}
6848
6849fn bit_width(values: &[f64], span: Span) -> Result<usize> {
6852 if values.is_empty() {
6854 return Ok(0);
6855 }
6856 let mut m = 0.0f64;
6857 for &v in values {
6858 if !v.is_finite() {
6859 return Err(Error::domain("cannot encode an infinite value", span));
6860 }
6861 m = m.max(v.abs());
6862 }
6863 let whole = m.floor();
6864 if whole >= 1e15 {
6865 return Err(Error::domain("the value is too large to encode in binary", span));
6866 }
6867 let mut w = 1usize;
6868 let mut n = whole as i64;
6869 while n > 1 {
6870 n /= 2;
6871 w += 1;
6872 }
6873 Ok(w)
6874}
6875
6876fn encode_one(radix: &[f64], v: f64, out: &mut [f64], tol: Tol) {
6884 let mut rem = v;
6885 for i in (0..radix.len()).rev() {
6886 let b = radix[i];
6887 if b == 0.0 {
6888 out[i] = rem;
6889 rem = 0.0;
6890 } else {
6891 let r = tol.residue(b, rem);
6892 out[i] = r;
6893 rem = (rem - r) / b;
6894 }
6895 }
6896}
6897
6898fn encode(x: &Array, y: &Array, tol: Tol, span: Span) -> Result<Array> {
6902 let radix = digits_of(x, "encode", span)?;
6903 let values = digits_of(y, "encode", span)?;
6904 let k = radix.len();
6905 let n = values.len();
6906 let mut out = vec![0.0f64; k * n];
6907 let mut cell = vec![0.0f64; k];
6908 for (j, &v) in values.iter().enumerate() {
6909 encode_one(&radix, v, &mut cell, tol);
6910 if cell.iter().any(|&d| tol.made_nan(d, v, 0.0)) {
6913 return Err(Error::nan(
6914 format!("`{}` has no digits in this base", j_number(v)),
6915 span,
6916 ));
6917 }
6918 for i in 0..k {
6919 out[i * n + j] = cell[i];
6920 }
6921 }
6922 let mut shape = if x.rank() == 0 { Vec::new() } else { vec![k] };
6925 shape.extend_from_slice(&y.shape);
6926 Ok(Array::new(shape, narrow(out, is_integral(x) && is_integral(y))))
6927}
6928
6929fn encode_bits(y: &Array, tol: Tol, span: Span) -> Result<Array> {
6931 let values = digits_of(y, "encode", span)?;
6932 let k = bit_width(&values, span)?;
6933 let radix = vec![2.0; k];
6934 let mut out = vec![0.0f64; values.len() * k];
6935 for (j, &v) in values.iter().enumerate() {
6936 encode_one(&radix, v, &mut out[j * k..(j + 1) * k], tol);
6937 }
6938 let mut shape = y.shape.clone();
6939 shape.push(k);
6940 Ok(Array::new(shape, narrow(out, is_integral(y))))
6941}
6942
6943fn laminate(x: &Array, y: &Array, span: Span) -> Result<Array> {
6948 let spread = |a: &Array, other: &Array| -> Array {
6949 if a.rank() != 0 {
6950 return a.clone();
6951 }
6952 let shape = if other.rank() == 0 { vec![1] } else { other.shape.clone() };
6953 let n: usize = shape.iter().product();
6954 let mut data = Data::empty(a.dtype());
6955 for _ in 0..n {
6956 push_elem(&mut data, &a.data, 0);
6957 }
6958 Array::new(shape, data)
6959 };
6960 assemble(&[2], vec![spread(x, y), spread(y, x)], span)
6961}
6962
6963fn table_of(y: &Array) -> Array {
6965 let shape = match y.rank() {
6966 0 => vec![1, 1],
6967 _ => vec![y.items(), y.item_size()],
6968 };
6969 Array::new(shape, y.data.clone())
6970}
6971
6972fn item_dyad(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
6979 let r = u.dyad(&open_cell(x), &open_cell(y), ctx, span)?;
6980 Ok(enclose(&r, Enclose::ExceptSimpleScalar))
6981}
6982
6983fn outer_product(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
6991 let mut frame = x.shape.clone();
6992 frame.extend_from_slice(&y.shape);
6993 let (nx, ny) = (x.count(), y.count());
6994 let n = nx * ny;
6995 if n == 0 {
6996 return assemble(&frame, Vec::new(), span);
6997 }
6998 let (xr, yr) = (x.to_row_major(), y.to_row_major());
6999 let cells = each_cell(n, nx.max(ny).max(n), u.is_pure(), ctx, |i, c| {
7000 item_dyad(u, &atom(&xr, i / ny), &atom(&yr, i % ny), c, span)
7001 })?;
7002 assemble_items(&frame, cells, span)
7003}
7004
7005fn table(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
7012 if ctx.cfg.rules.lang == crate::Lang::Apl {
7013 return outer_product(u, x, y, ctx, span);
7014 }
7015 let ranks = u.ranks();
7016 let fxl = x.rank() - effective_rank(ranks[1], x.rank());
7017 let fyl = y.rank() - effective_rank(ranks[2], y.rank());
7018 let mut frame = x.shape[..fxl].to_vec();
7019 frame.extend_from_slice(&y.shape[..fyl]);
7020 let nx: usize = x.shape[..fxl].iter().product();
7021 let ny: usize = y.shape[..fyl].iter().product();
7022 let n = nx * ny;
7023 if n == 0 {
7024 return assemble(&frame, Vec::new(), span);
7025 }
7026 if frame.is_empty() {
7027 return u.dyad(x, y, ctx, span);
7028 }
7029 let work = x.count().max(y.count()).max(n);
7030 let cells = each_cell(n, work, u.is_pure(), ctx, |i, c| {
7031 u.dyad(&x.cell_at(fxl, i / ny), &y.cell_at(fyl, i % ny), c, span)
7032 })?;
7033 assemble(&frame, cells, span)
7034}
7035
7036pub(crate) fn with_origin(v: &Verb, origin: i64) -> Option<Verb> {
7043 match v {
7044 Verb::Prim(p) => {
7045 let mut out = *p;
7046 let mut changed = false;
7047 out.monad = match p.monad {
7048 MonadOp::GradeUp { .. } => {
7049 changed = true;
7050 MonadOp::GradeUp { origin }
7051 }
7052 MonadOp::GradeDown { .. } => {
7053 changed = true;
7054 MonadOp::GradeDown { origin }
7055 }
7056 MonadOp::IotaApl { .. } => {
7057 changed = true;
7058 MonadOp::IotaApl { origin }
7059 }
7060 MonadOp::Indices { boxed_coords, .. } => {
7061 changed = true;
7062 MonadOp::Indices { origin, boxed_coords }
7063 }
7064 MonadOp::Roll { fixed, float_at_zero, .. } => {
7065 changed = true;
7066 MonadOp::Roll { origin, fixed, float_at_zero }
7067 }
7068 other => other,
7069 };
7070 out.dyad = match p.dyad {
7071 DyadOp::IndexOf { vector_left, .. } => {
7072 changed = true;
7073 DyadOp::IndexOf { origin, vector_left }
7074 }
7075 DyadOp::IndexOfLast { .. } => {
7076 changed = true;
7077 DyadOp::IndexOfLast { origin }
7078 }
7079 DyadOp::CollateGrade { down, .. } => {
7080 changed = true;
7081 DyadOp::CollateGrade { down, origin }
7082 }
7083 DyadOp::Squad { leading, .. } => {
7084 changed = true;
7085 DyadOp::Squad { origin, leading }
7086 }
7087 DyadOp::Pick { .. } => {
7088 changed = true;
7089 DyadOp::Pick { origin }
7090 }
7091 DyadOp::SelectAxis { axis, rank, .. } => {
7092 changed = true;
7093 DyadOp::SelectAxis { axis, rank, origin }
7094 }
7095 DyadOp::Deal { fixed, .. } => {
7096 changed = true;
7097 DyadOp::Deal { origin, fixed }
7098 }
7099 other => other,
7100 };
7101 changed.then_some(Verb::Prim(out))
7102 }
7103 Verb::Rank(u, r) => Some(Verb::Rank(Box::new(with_origin(u, origin)?), *r)),
7104 Verb::Reduce(u) => Some(Verb::Reduce(Box::new(with_origin(u, origin)?))),
7105 Verb::NWise(u) => Some(Verb::NWise(Box::new(with_origin(u, origin)?))),
7106 Verb::Windowed(u, k) => Some(Verb::Windowed(Box::new(with_origin(u, origin)?), *k)),
7107 Verb::Commute(u) => Some(Verb::Commute(Box::new(with_origin(u, origin)?))),
7108 Verb::Each(u, e) => Some(Verb::Each(Box::new(with_origin(u, origin)?), *e)),
7109 Verb::Fit(u, n) => Some(Verb::Fit(Box::new(with_origin(u, origin)?), *n)),
7110 Verb::AlongAxis(u, k) => Some(Verb::AlongAxis(Box::new(with_origin(u, origin)?), *k)),
7111 _ => None,
7112 }
7113}
7114
7115fn scalar_dyad_of(v: &Verb) -> Option<ScalarDyad> {
7120 match v {
7121 Verb::Prim(p) => match p.dyad {
7122 DyadOp::Scalar(op) => Some(op),
7123 _ => None,
7124 },
7125 _ => None,
7126 }
7127}
7128
7129fn folds_with(u: &Verb, op: ScalarDyad) -> bool {
7132 matches!(u, Verb::Reduce(inner) if scalar_dyad_of(inner) == Some(op))
7133}
7134
7135fn inner_product(
7142 u: &Verb,
7143 v: &Verb,
7144 apl: bool,
7145 x: &Array,
7146 y: &Array,
7147 ctx: &mut Ctx<'_>,
7148 span: Span,
7149) -> Result<Array> {
7150 if let Some(a) = matrix_product(u, v, x, y, span) {
7151 return Ok(a);
7152 }
7153 if apl && scalar_dyad_of(v).is_none() {
7156 return apl_inner_product(u, v, x, y, ctx, span);
7157 }
7158 if !apl {
7159 return inner_cells(u, v, false, x, y, ctx, span);
7160 }
7161 let saved = ctx.cfg.agreement;
7167 ctx.cfg.agreement = Agreement::LeadingPrefix;
7168 let out = inner_cells(u, v, true, x, y, ctx, span);
7169 ctx.cfg.agreement = saved;
7170 out
7171}
7172
7173fn enclose_elements(a: &Array) -> Array {
7177 if a.dtype() == DType::Box { boxed_elements(a) } else { a.clone() }
7178}
7179
7180fn inner_fold(
7188 u: &Verb,
7189 apl: bool,
7190 inner: &Array,
7191 ctx: &mut Ctx<'_>,
7192 span: Span,
7193) -> Result<Array> {
7194 let folded = u.monad(inner, ctx, span)?;
7195 Ok(if apl && each_on_fold(ctx) { enclose_elements(&folded) } else { folded })
7196}
7197
7198fn each_on_fold(ctx: &Ctx<'_>) -> bool {
7200 ctx.cfg.rules.inner_each == InnerEach::OnFold
7201}
7202
7203fn inner_cells(
7206 u: &Verb,
7207 v: &Verb,
7208 apl: bool,
7209 x: &Array,
7210 y: &Array,
7211 ctx: &mut Ctx<'_>,
7212 span: Span,
7213) -> Result<Array> {
7214 let cell_rank = effective_rank(v.ranks()[1].max(1), x.rank());
7215 let frame_rank = x.rank() - cell_rank;
7216 if frame_rank == 0 {
7217 let inner = v.dyad(x, y, ctx, span)?;
7218 return inner_fold(u, apl, &inner, ctx, span);
7219 }
7220 let frame = x.shape[..frame_rank].to_vec();
7221 let n: usize = frame.iter().product();
7222 if n == 0 {
7223 return assemble(&frame, Vec::new(), span);
7224 }
7225 let work = x.count().max(y.count());
7226 let pure = u.is_pure() && v.is_pure();
7227 let items = apl && !each_on_fold(ctx);
7228 let cells = each_cell(n, work, pure, ctx, |i, c| {
7229 let inner = v.dyad(&x.cell_at(frame_rank, i), y, c, span)?;
7230 inner_fold(u, apl, &inner, c, span)
7231 })?;
7232 if items { assemble_items(&frame, cells, span) } else { assemble(&frame, cells, span) }
7233}
7234
7235fn apl_inner_product(
7244 u: &Verb,
7245 v: &Verb,
7246 x: &Array,
7247 y: &Array,
7248 ctx: &mut Ctx<'_>,
7249 span: Span,
7250) -> Result<Array> {
7251 let k = match (x.rank(), y.rank()) {
7254 (0, 0) => 1,
7255 (0, _) => y.shape[0],
7256 _ => x.shape[x.rank() - 1],
7257 };
7258 if x.rank() > 0 && y.rank() > 0 && x.shape[x.rank() - 1] != y.shape[0] {
7259 return Err(Error::new(
7260 ErrorKind::Length,
7261 format!("inner product over {} and {} elements", x.shape[x.rank() - 1], y.shape[0]),
7262 Some(span),
7263 ));
7264 }
7265 let lead: &[usize] = if x.rank() > 0 { &x.shape[..x.rank() - 1] } else { &[] };
7266 let trail: &[usize] = if y.rank() > 0 { &y.shape[1..] } else { &[] };
7267 let rows: usize = lead.iter().product();
7268 let cols: usize = trail.iter().product();
7269 let mut frame = lead.to_vec();
7270 frame.extend_from_slice(trail);
7271 let n = rows * cols;
7272 if n == 0 {
7273 return assemble(&frame, Vec::new(), span);
7274 }
7275 let vector = |d: &Data, at: &dyn Fn(usize) -> usize| {
7276 let mut out = Data::empty(d.dtype());
7277 for t in 0..k {
7278 out.push_from(d, at(t));
7279 }
7280 Array::new(vec![k], out)
7281 };
7282 let pure = u.is_pure() && v.is_pure();
7283 let on_fold = each_on_fold(ctx);
7284 let paired = Verb::Each(Box::new(v.clone()), Enclose::ExceptSimpleScalar);
7285 let cells = each_cell(n, x.count().max(y.count()), pure, ctx, |i, c| {
7286 let (r, col) = (i / cols, i % cols);
7287 let left = vector(&x.data, &|t| if x.rank() > 0 { r * k + t } else { 0 });
7288 let right = vector(&y.data, &|t| if y.rank() > 0 { t * cols + col } else { 0 });
7289 let inner = if on_fold {
7290 v.dyad(&left, &right, c, span)?
7291 } else {
7292 paired.dyad(&left, &right, c, span)?
7293 };
7294 inner_fold(u, true, &inner, c, span)
7295 })?;
7296 if on_fold { assemble(&frame, cells, span) } else { assemble_items(&frame, cells, span) }
7297}
7298
7299fn matrix_product(u: &Verb, v: &Verb, x: &Array, y: &Array, span: Span) -> Option<Array> {
7305 if !folds_with(u, ScalarDyad::Add) || scalar_dyad_of(v) != Some(ScalarDyad::Mul) {
7306 return None;
7307 }
7308 if x.rank() == 0 || y.rank() == 0 {
7309 return None;
7310 }
7311 let k = x.shape[x.rank() - 1];
7312 if k != y.shape[0] {
7313 return None;
7314 }
7315 let rows: usize = x.shape[..x.rank() - 1].iter().product();
7316 let cols: usize = y.shape[1..].iter().product();
7317 let mut shape = x.shape[..x.rank() - 1].to_vec();
7318 shape.extend_from_slice(&y.shape[1..]);
7319 if crate::limits::elements(&shape, span).is_err() {
7320 return None;
7321 }
7322 let whole = matches!(x.dtype(), DType::Bool | DType::I64)
7323 && matches!(y.dtype(), DType::Bool | DType::I64);
7324 if whole
7325 && let (Some(xs), Some(ys)) = (x.to_i64_vec(), y.to_i64_vec())
7326 && let Some(out) = matmul_whole(&xs, &ys, rows, k, cols)
7327 {
7328 return Some(Array::new(shape, Data::I64(out.into())));
7329 }
7330 let (xs, ys) = (x.to_f64_vec()?, y.to_f64_vec()?);
7331 let out = par::fill_rows(rows, cols, rows * k * cols, |r0, part| {
7332 matmul_f64(&xs, &ys, k, cols, r0, part);
7333 });
7334 Some(Array::new(shape, Data::F64(out.into())))
7335}
7336
7337const MATMUL_BLOCK: usize = 128;
7341
7342#[inline(always)]
7343fn matmul_f64_body(xs: &[f64], ys: &[f64], k: usize, n: usize, r0: usize, out: &mut [f64]) {
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 += a * b;
7357 }
7358 }
7359 }
7360 }
7361}
7362
7363multiversioned! {
7364 fn matmul_f64(
7368 xs: &[f64],
7369 ys: &[f64],
7370 k: usize,
7371 n: usize,
7372 r0: usize,
7373 out: &mut [f64],
7374 ) -> () = matmul_f64_body;
7375}
7376
7377#[inline(always)]
7378fn matmul_i64_body(xs: &[i64], ys: &[i64], k: usize, n: usize, r0: usize, out: &mut [i64]) {
7379 if n == 0 {
7380 return;
7381 }
7382 let rows = out.len() / n;
7383 for k0 in (0..k).step_by(MATMUL_BLOCK) {
7384 let k1 = (k0 + MATMUL_BLOCK).min(k);
7385 for r in 0..rows {
7386 let left = &xs[(r0 + r) * k..(r0 + r + 1) * k];
7387 let dst = &mut out[r * n..(r + 1) * n];
7388 for (t, &a) in left.iter().enumerate().take(k1).skip(k0) {
7389 let row = &ys[t * n..(t + 1) * n];
7390 for (o, &b) in dst.iter_mut().zip(row) {
7391 *o = o.wrapping_add(a.wrapping_mul(b));
7392 }
7393 }
7394 }
7395 }
7396}
7397
7398multiversioned! {
7399 fn matmul_i64(
7403 xs: &[i64],
7404 ys: &[i64],
7405 k: usize,
7406 n: usize,
7407 r0: usize,
7408 out: &mut [i64],
7409 ) -> () = matmul_i64_body;
7410}
7411
7412fn matmul_whole(xs: &[i64], ys: &[i64], rows: usize, k: usize, n: usize) -> Option<Vec<i64>> {
7416 let bound = |v: &[i64]| v.iter().map(|&a| (a as i128).abs()).max().unwrap_or(0);
7421 if bound(xs).saturating_mul(bound(ys)).saturating_mul(k as i128) <= i64::MAX as i128 {
7422 return Some(par::fill_rows(rows, n, rows * k * n, |r0, part| {
7423 matmul_i64(xs, ys, k, n, r0, part);
7424 }));
7425 }
7426 let mut out = vec![0i64; rows * n];
7427 for r in 0..rows {
7428 let left = &xs[r * k..(r + 1) * k];
7429 let dst = &mut out[r * n..(r + 1) * n];
7430 for (t, &a) in left.iter().enumerate() {
7431 for (o, &b) in dst.iter_mut().zip(&ys[t * n..(t + 1) * n]) {
7432 *o = a.checked_mul(b).and_then(|p| o.checked_add(p))?;
7433 }
7434 }
7435 }
7436 Some(out)
7437}
7438
7439const DETERMINANT_MINORS_MAX: usize = 16;
7444
7445fn determinant(
7451 u: &Verb,
7452 v: &Verb,
7453 apl: bool,
7454 y: &Array,
7455 ctx: &mut Ctx<'_>,
7456 span: Span,
7457) -> Result<Array> {
7458 if apl {
7459 return Err(Error::domain("an inner product has no monadic meaning in APL", span));
7460 }
7461 if y.rank() > 2 {
7465 let frame = y.shape[..y.rank() - 2].to_vec();
7466 let n: usize = frame.iter().product();
7467 let pure = u.is_pure() && v.is_pure();
7468 let cells = each_cell(n, y.count(), pure, ctx, |i, c| {
7469 determinant(u, v, apl, &y.cell_at(y.rank() - 2, i), c, span)
7470 })?;
7471 return assemble(&frame, cells, span);
7472 }
7473 let rows = y.items();
7474 let cols = y.item_size();
7475 if folds_with(u, ScalarDyad::Sub)
7476 && scalar_dyad_of(v) == Some(ScalarDyad::Mul)
7477 && rows == cols
7478 && rows >= 3
7479 && matches!(y.dtype(), DType::Bool | DType::I64 | DType::F64)
7480 && let Some(values) = y.to_f64_vec()
7481 {
7482 return Ok(Array::scalar_f64(determinant_lu(values, rows)));
7483 }
7484 if rows > DETERMINANT_MINORS_MAX {
7485 return Err(Error::not_yet(
7486 format!(
7487 "a determinant of more than {DETERMINANT_MINORS_MAX} rows by minors \
7488 (only -/ . * over machine numbers has a direct method)"
7489 ),
7490 span,
7491 ));
7492 }
7493 let mut seen: HashMap<u64, Array> = HashMap::new();
7494 let all = if rows == 64 { u64::MAX } else { (1u64 << rows) - 1 };
7495 minors(u, v, y, cols, rows, all, &mut seen, ctx, span)
7496}
7497
7498#[allow(clippy::too_many_arguments)]
7502fn minors(
7503 u: &Verb,
7504 v: &Verb,
7505 y: &Array,
7506 cols: usize,
7507 rows: usize,
7508 left: u64,
7509 seen: &mut HashMap<u64, Array>,
7510 ctx: &mut Ctx<'_>,
7511 span: Span,
7512) -> Result<Array> {
7513 if let Some(a) = seen.get(&left) {
7514 return Ok(a.clone());
7515 }
7516 let column = rows - left.count_ones() as usize;
7519 let value = if column >= cols {
7520 let data = reduce_identity(v, 1, ctx.cfg.rules.lang).ok_or_else(|| {
7521 Error::not_yet(
7522 format!("the identity element of {} (a determinant with no columns)", v.name()),
7523 span,
7524 )
7525 })?;
7526 Array::new(Vec::new(), data)
7527 } else if left == 0 {
7528 u.monad(&Array::new(vec![0], Data::empty(DType::I64)), ctx, span)?
7529 } else {
7530 let mut terms = Vec::with_capacity(left.count_ones() as usize);
7531 for r in 0..rows {
7532 if left & (1 << r) == 0 {
7533 continue;
7534 }
7535 let minor = minors(u, v, y, cols, rows, left & !(1 << r), seen, ctx, span)?;
7536 let head = Array::new(Vec::new(), y.data.slice(r * cols + column, r * cols + column + 1));
7537 terms.push(v.dyad(&head, &minor, ctx, span)?);
7538 }
7539 let n = terms.len();
7540 u.monad(&assemble(&[n], terms, span)?, ctx, span)?
7541 };
7542 seen.insert(left, value.clone());
7543 Ok(value)
7544}
7545
7546fn determinant_lu(mut a: Vec<f64>, n: usize) -> f64 {
7551 let mut det = 1.0f64;
7552 for c in 0..n {
7553 let mut pivot = c;
7554 for r in c + 1..n {
7555 if a[r * n + c].abs() > a[pivot * n + c].abs() {
7556 pivot = r;
7557 }
7558 }
7559 if a[pivot * n + c] == 0.0 {
7560 return 0.0;
7561 }
7562 if pivot != c {
7563 for j in 0..n {
7564 a.swap(c * n + j, pivot * n + j);
7565 }
7566 det = -det;
7567 }
7568 let head = a[c * n + c];
7569 det *= head;
7570 for r in c + 1..n {
7571 let factor = a[r * n + c] / head;
7572 if factor == 0.0 {
7573 continue;
7574 }
7575 for j in c..n {
7576 a[r * n + j] -= factor * a[c * n + j];
7577 }
7578 }
7579 }
7580 det
7581}
7582
7583fn monad_op(p: &Prim, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
7585 let apl = ctx.cfg.rules.lang == crate::Lang::Apl;
7586 let out = monad_op_inner(p, y, ctx, span);
7587 if apl { out.map(tightened_mixed) } else { out }
7588}
7589
7590fn monad_op_inner(p: &Prim, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
7593 match p.monad {
7594 MonadOp::Scalar(op) => scalar_monad(op, y, ctx.cfg, span),
7595 MonadOp::ShapeOf => {
7596 Ok(carry_exact(Array::from_i64(y.shape.iter().map(|&n| n as i64).collect()), y))
7597 }
7598 MonadOp::Tally => Ok(carry_exact(Array::scalar_i64(y.items() as i64), y)),
7599 MonadOp::Ravel => Ok(Array::new(vec![y.count()], y.data.clone())),
7600 MonadOp::RavelItems => {
7605 let (items, width) = if y.rank() == 0 {
7606 (1usize, 1usize)
7607 } else {
7608 (y.shape[0], y.shape[1..].iter().product::<usize>())
7609 };
7610 Ok(Array::new(vec![items, width], y.to_row_major().data))
7611 }
7612 MonadOp::TransposeAxes => Ok(transpose_axes(y)),
7613 MonadOp::Head => Ok(head(y)),
7614 MonadOp::Behead => behead(y, span),
7615 MonadOp::Tail => Ok(tail(y)),
7616 MonadOp::Curtail => Ok(curtail(y)),
7617 MonadOp::Reverse => Ok(reverse(y)),
7618 MonadOp::Nub => Ok(nub(y, ctx.cfg.tol)),
7621 MonadOp::GradeUp { origin } | MonadOp::GradeDown { origin } => {
7622 check_gradable(y, ctx.cfg.rules, span)?;
7623 if ctx.cfg.rules.lang == crate::Lang::Apl && y.rank() == 0 {
7626 return Err(Error::domain("a grade needs an array, not a scalar", span));
7627 }
7628 let down = matches!(p.monad, MonadOp::GradeDown { .. });
7629 let order = grade_order(y, down, Grading::of(ctx.cfg.rules, ctx.cfg.tol));
7630 Ok(Array::from_i64(order.iter().map(|&i| origin + i as i64).collect()))
7631 }
7632 MonadOp::IotaJ => iota_j(y, ctx.cfg.near(), span),
7633 MonadOp::IotaApl { origin } => iota_apl(y, origin, ctx.cfg.near(), span),
7634 MonadOp::Echo => {
7635 (ctx.out)(&format!("{}\n", crate::fmt::format_array(y, &ctx.cfg.fmt)));
7636 Ok(Array::empty(DType::I64))
7637 }
7638 MonadOp::ReadStream => {
7639 stream_number(y, 1, "1!:1 reads", span)?;
7640 let line = ctx.read_line(span)?;
7641 Ok(Array::from_chars(line.chars().collect()))
7642 }
7643 MonadOp::TypeCode => Ok(Array::scalar_i64(type_code(y))),
7644 MonadOp::Sparse => crate::sparse::sparsify(y, span),
7645 MonadOp::Dense => Ok(y.densified()),
7646 MonadOp::PrimeCount => {
7647 let n = y
7648 .to_i64_vec_near(ctx.cfg.near())
7649 .ok_or_else(|| Error::domain("the prime count needs an integer", span))?;
7650 let v = n.first().copied().unwrap_or(0);
7651 Ok(carry_exact(Array::scalar_i64(primes_below(v, span)?), y))
7652 }
7653 MonadOp::IndicesInverse => indices_inverse(y, ctx.cfg.near(), span),
7654 MonadOp::Same => Ok(y.clone()),
7655 MonadOp::Format => Ok(format_chars(y, &ctx.cfg.fmt)),
7656 MonadOp::DecodeBits => decode(None, y, ctx.cfg.tol, span).map(|r| carry_exact(r, y)),
7657 MonadOp::EncodeBits => encode_bits(y, ctx.cfg.tol, span).map(|r| carry_exact(r, y)),
7658 MonadOp::Itemize => {
7659 let mut shape = vec![1usize];
7660 shape.extend_from_slice(&y.shape);
7661 Ok(Array::new(shape, y.data.clone()))
7662 }
7663 MonadOp::TableOf => Ok(table_of(y)),
7664 MonadOp::Enclose(rule) => Ok(enclose(y, rule)),
7665 MonadOp::Open => Ok(open_cell(y)),
7666 MonadOp::Raze => raze(y, span),
7667 MonadOp::Catalogue => catalogue(y, span),
7668 MonadOp::AtomicRep => atomic_rep(y, ctx, span),
7669 MonadOp::RazeIn => raze_in(y, ctx.cfg.tol, span),
7670 MonadOp::First => Ok(first(y)),
7671 MonadOp::Enlist => enlist(y, span),
7672 MonadOp::Depth { signed } => {
7673 let d = depth(y);
7674 Ok(Array::scalar_i64(if signed && d > 1 && !uniform(y) { -d } else { d }))
7675 }
7676 MonadOp::Indices { origin, boxed_coords } => {
7677 where_indices(y, origin, boxed_coords, ctx.cfg.near(), span)
7678 }
7679 MonadOp::Steps => steps(y, span),
7680 MonadOp::ToExact => to_exact(y, span),
7681 MonadOp::NthPrime => {
7682 let n = y
7683 .to_i64_vec_near(ctx.cfg.near())
7684 .ok_or_else(|| Error::domain("the prime index must be an integer", span))?;
7685 let v = n.first().copied().unwrap_or(0);
7686 Ok(carry_exact(Array::scalar_i64(nth_prime(v, span)?), y))
7687 }
7688 MonadOp::PrimeFactors => {
7689 let n = y
7690 .to_i64_vec_near(ctx.cfg.near())
7691 .ok_or_else(|| Error::domain("prime factors need an integer", span))?;
7692 let v = n.first().copied().unwrap_or(0);
7693 Ok(carry_exact(Array::from_i64(prime_factors(v, span)?), y))
7694 }
7695 MonadOp::MatrixInverse => matrix_inverse(y, span),
7696 MonadOp::Roll { origin, fixed, float_at_zero } => {
7697 roll(y, origin, fixed, float_at_zero, ctx.cfg.near(), span)
7698 }
7699 MonadOp::ComplexParts { polar } => complex_parts(y, polar, span),
7700 MonadOp::SelfClassify => Ok(self_classify(y, ctx.cfg.tol)),
7701 MonadOp::NubSieve => Ok(nub_sieve(y, ctx.cfg.tol, ctx.cfg.rules.lang)),
7702 MonadOp::Unicode { pass_chars } => unicode(y, pass_chars, ctx.cfg.near(), span),
7703 MonadOp::Symbols => to_symbols(y, span),
7704 MonadOp::Words => words(y, span),
7705 MonadOp::LevelOf => Ok(Array::scalar_i64(boxing_level(y))),
7706 MonadOp::MapPaths => Ok(map_paths(y)),
7707 MonadOp::Nest => Ok(nest(y)),
7708 MonadOp::PolyRoots => poly_roots(y, span),
7709 MonadOp::PolyDeriv => poly_deriv(y, span),
7710 MonadOp::AnagramIndex => anagram_index(y, ctx.cfg.rules, span),
7711 MonadOp::CycleForm => cycle_form(y, ctx.cfg.near(), span),
7712 MonadOp::Split => Ok(split_items(y)),
7713 MonadOp::Execute { apl } => execute(y, apl, ctx, span),
7714 MonadOp::NotYet(what) => Err(Error::not_yet(what, span)),
7715 MonadOp::None => {
7716 Err(Error::domain(format!("{} has no monadic meaning", p.name), span))
7717 }
7718 }
7719}
7720
7721fn complex_parts(y: &Array, polar: bool, span: Span) -> Result<Array> {
7725 let Some(v) = y.to_complex_vec() else {
7726 return Err(wrong_type(y.dtype(), span));
7727 };
7728 let z = v.first().copied().unwrap_or(cx::ZERO);
7729 let pair = if polar { vec![cx::abs(z), cx::arg(z)] } else { vec![z[0], z[1]] };
7730 Ok(Array::from_f64(pair))
7731}
7732
7733fn axis_counts(x: &Array, what: &str, near: NearInt, span: Span) -> Result<Vec<i64>> {
7734 if x.rank() > 1 {
7735 return Err(Error::new(
7736 ErrorKind::Rank,
7737 format!("{what} needs a scalar or vector left argument"),
7738 Some(span),
7739 ));
7740 }
7741 if x.count() == 0 {
7744 return Ok(Vec::new());
7745 }
7746 x.to_i64_vec_near(near)
7747 .ok_or_else(|| Error::domain(format!("{what} needs integer lengths"), span))
7748}
7749
7750fn reshape(
7761 x: &Array,
7762 y: &Array,
7763 by_items: bool,
7764 apl: bool,
7765 near: NearInt,
7766 span: Span,
7767) -> Result<Array> {
7768 let dims = axis_counts(x, "reshape", near, span)?;
7769 if dims.iter().any(|&d| d < 0) {
7770 return Err(Error::domain("reshape lengths must be nonnegative", span));
7771 }
7772 let mut shape: Vec<usize> = dims.iter().map(|&d| d as usize).collect();
7773 let (unit, src) = if by_items {
7775 let item_shape = if y.rank() == 0 { &[][..] } else { &y.shape[1..] };
7776 shape.extend_from_slice(item_shape);
7777 (item_shape.iter().product::<usize>(), y.items().max(usize::from(y.rank() == 0)))
7778 } else {
7779 (1, y.count())
7780 };
7781 let n = crate::limits::elements(&shape, span)?;
7782 let mut data = Data::empty(y.dtype());
7783 if n > 0 && src == 0 {
7784 if by_items {
7785 return Err(Error::new(ErrorKind::Length, "reshape of an empty array", Some(span)));
7786 }
7787 let fill = if apl { prototype_of(y) } else { None };
7788 let mut data = Data::empty(y.dtype());
7789 for _ in 0..n {
7790 push_gap(&mut data, &fill);
7791 }
7792 return Ok(Array::new(shape, data));
7793 }
7794 if y.is_row_major() && n <= unit.saturating_mul(src) && n <= y.data.len() {
7800 return Ok(keep_proto(Array::new(shape, y.data.slice(0, n)), y, apl));
7801 }
7802 for i in 0..n {
7803 push_elem(&mut data, &y.data, (i / unit) % src * unit + i % unit);
7804 }
7805 Ok(keep_proto(Array::new(shape, data), y, apl))
7806}
7807
7808fn leading_run(y: &Array, counts: &[i64], drop: bool) -> Option<Array> {
7812 if y.rank() == 0 || counts.is_empty() {
7813 return None;
7814 }
7815 let trailing_untouched = counts[1..].iter().enumerate().all(|(a, &c)| {
7819 if drop { c == 0 } else { c.unsigned_abs() as usize == y.shape[a + 1] }
7820 });
7821 if !trailing_untouched {
7822 return None;
7823 }
7824 let n = y.items();
7825 let k = counts[0];
7826 let a = k.unsigned_abs() as usize;
7827 let (lo, keep) = if drop {
7828 let a = a.min(n);
7829 if k >= 0 { (a, n - a) } else { (0, n - a) }
7830 } else {
7831 if a > n {
7833 return None;
7834 }
7835 if k >= 0 { (0, a) } else { (n - a, a) }
7836 };
7837 Some(section(y, lo, lo + keep))
7838}
7839
7840fn count_rank(verb: &str, counts: usize, rank: usize, span: Span) -> Error {
7845 Error::new(
7846 ErrorKind::Length,
7847 format!("{counts} {verb} counts for a rank-{rank} argument"),
7848 Some(span),
7849 )
7850}
7851
7852fn take(
7853 x: &Array,
7854 y: &Array,
7855 prototype_fill: bool,
7856 apl: bool,
7857 near: NearInt,
7858 span: Span,
7859) -> Result<Array> {
7860 let counts = axis_counts(x, "take", near, span)?;
7861 let fill = if prototype_fill { prototype_of(y) } else { None };
7865 let promoted;
7866 let base = if y.rank() == 0 {
7869 promoted = Array::new(vec![1; counts.len()], y.data.clone());
7870 &promoted
7871 } else {
7872 y
7873 };
7874 let wrong = if apl {
7876 counts.len() != base.rank()
7877 } else {
7878 counts.len() > base.rank() || (counts.is_empty() && base.rank() > 0)
7879 };
7880 if wrong {
7881 return Err(count_rank("take", counts.len(), base.rank(), span));
7882 }
7883 if let Some(run) = leading_run(base, &counts, false) {
7884 return Ok(keep_proto(run, base, prototype_fill));
7885 }
7886 let mut out_shape = base.shape.clone();
7887 for (a, &k) in counts.iter().enumerate() {
7888 out_shape[a] = k.unsigned_abs() as usize;
7889 }
7890 let n = crate::limits::elements(&out_shape, span)?;
7891 let st = strides(&base.shape);
7892 let mut data = Data::empty(base.dtype());
7893 let mut coord = vec![0usize; out_shape.len()];
7894 for _ in 0..n {
7895 let mut idx = 0usize;
7896 let mut inside = true;
7897 for a in 0..out_shape.len() {
7898 let len = base.shape[a] as i64;
7899 let c = coord[a] as i64;
7900 let s = match counts.get(a) {
7903 Some(&k) if k < 0 => c + len - k.unsigned_abs() as i64,
7904 _ => c,
7905 };
7906 if s < 0 || s >= len {
7907 inside = false;
7908 break;
7909 }
7910 idx += s as usize * st[a];
7911 }
7912 if inside {
7913 push_elem(&mut data, &base.data, idx);
7914 } else if let (Data::Box(v), Some(p)) = (&mut data, &fill) {
7915 v.push(p.clone());
7916 } else {
7917 data.push_fill();
7918 }
7919 odometer(&mut coord, &out_shape);
7920 }
7921 Ok(keep_proto(Array::new(out_shape, data), base, prototype_fill))
7922}
7923
7924fn prototype_of(y: &Array) -> Option<Array> {
7928 fn zeroed(a: &Array) -> Array {
7929 if let Some(items) = a.as_boxes() {
7930 let inner: Vec<Array> = items.iter().map(zeroed).collect();
7931 return Array::new(a.shape.clone(), Data::Box(inner.into()));
7932 }
7933 let dtype = match a.dtype() {
7934 DType::Char | DType::Symbol => a.dtype(),
7935 _ => DType::I64,
7936 };
7937 Array::new(a.shape.clone(), fill_data(dtype, a.count()))
7938 }
7939 match y.as_boxes()?.first() {
7940 Some(first) => Some(zeroed(first)),
7941 None => y.proto().cloned(),
7944 }
7945}
7946
7947fn keep_proto(out: Array, src: &Array, apl: bool) -> Array {
7953 if !apl || out.count() > 0 || out.dtype() != DType::Box {
7954 return out;
7955 }
7956 match prototype_of(src) {
7957 Some(p) => out.with_proto(p),
7958 None => out,
7959 }
7960}
7961
7962fn push_gap(data: &mut Data, fill: &Option<Array>) {
7965 match (data, fill) {
7966 (Data::Box(v), Some(p)) => v.push(p.clone()),
7967 (d, _) => d.push_fill(),
7968 }
7969}
7970
7971fn drop_(x: &Array, y: &Array, apl: bool, near: NearInt, span: Span) -> Result<Array> {
7972 let counts = axis_counts(x, "drop", near, span)?;
7973 let promoted;
7974 let base = if y.rank() == 0 {
7975 promoted = Array::new(vec![1; counts.len()], y.data.clone());
7976 &promoted
7977 } else {
7978 y
7979 };
7980 let wrong =
7981 if apl { counts.len() != base.rank() } else { counts.len() > base.rank() };
7982 if wrong {
7983 return Err(count_rank("drop", counts.len(), base.rank(), span));
7984 }
7985 if let Some(run) = leading_run(base, &counts, true) {
7986 return Ok(keep_proto(run, base, apl));
7987 }
7988 let mut out_shape = base.shape.clone();
7989 let mut offset = vec![0usize; base.rank()];
7990 for (a, &k) in counts.iter().enumerate() {
7991 let len = base.shape[a];
7992 let d = (k.unsigned_abs() as usize).min(len);
7993 out_shape[a] = len - d;
7994 if k > 0 {
7995 offset[a] = d;
7996 }
7997 }
7998 let n: usize = out_shape.iter().product();
7999 let st = strides(&base.shape);
8000 let mut data = Data::empty(base.dtype());
8001 let mut coord = vec![0usize; out_shape.len()];
8002 for _ in 0..n {
8003 let idx: usize = (0..out_shape.len()).map(|a| (coord[a] + offset[a]) * st[a]).sum();
8004 push_elem(&mut data, &base.data, idx);
8005 odometer(&mut coord, &out_shape);
8006 }
8007 Ok(keep_proto(Array::new(out_shape, data), base, apl))
8008}
8009
8010fn dyad_op(p: &Prim, x: &Array, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
8012 let apl = cfg.rules.lang == crate::Lang::Apl;
8013 let out = dyad_op_inner(p, x, y, cfg, span);
8014 if apl { out.map(tightened_mixed) } else { out }
8015}
8016
8017fn dyad_op_inner(p: &Prim, x: &Array, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
8020 let tol = cfg.tol;
8021 let apl = cfg.rules.lang == crate::Lang::Apl;
8022 match p.dyad {
8023 DyadOp::Scalar(op) => scalar_dyad(op, x, y, cfg, span),
8026 DyadOp::Reshape => {
8027 let apl = cfg.rules.lang == crate::Lang::Apl;
8028 reshape(x, y, cfg.agreement == Agreement::LeadingPrefix, apl, cfg.near(), span)
8029 }
8030 DyadOp::Take => {
8031 let apl = cfg.rules.lang == crate::Lang::Apl;
8032 take(x, y, cfg.agreement == Agreement::ExactOrScalar, apl, cfg.near(), span)
8033 }
8034 DyadOp::Drop => drop_(x, y, cfg.rules.lang == crate::Lang::Apl, cfg.near(), span),
8035 DyadOp::Right => Ok(y.clone()),
8036 DyadOp::Left => Ok(x.clone()),
8037 DyadOp::Rotate => rotate(x, y, cfg.near(), span),
8038 DyadOp::RotateApl { last } => rotate_apl(x, y, last, cfg.near(), span),
8039 DyadOp::AppendLeading => {
8042 catenate(x, y, true, cfg.agreement == Agreement::LeadingPrefix, span)
8043 }
8044 DyadOp::AppendLast => {
8045 catenate(x, y, false, cfg.agreement == Agreement::LeadingPrefix, span)
8046 }
8047 DyadOp::IndexOf { origin, vector_left } => {
8048 let (x, y) = align_mixed(x, y, apl);
8049 index_of(&x, &y, origin, vector_left, tol, span)
8050 }
8051 DyadOp::MemberJ => Ok(member_j(x, y, tol)),
8052 DyadOp::MemberApl => {
8053 let (x, y) = align_mixed(x, y, apl);
8054 Ok(member_apl(&x, &y, tol))
8055 }
8056 DyadOp::From => from_index(x, y, cfg.near(), span),
8057 DyadOp::Match => {
8058 let empties_differ = cfg.rules.lang == crate::Lang::Apl
8062 && x.count() == 0
8063 && y.count() == 0
8064 && (x.dtype() == DType::Char) != (y.dtype() == DType::Char);
8065 Ok(Array::scalar_bool(!empties_differ && arrays_match(x, y, tol)))
8066 }
8067 DyadOp::NotMatch => Ok(Array::scalar_bool(!arrays_match(x, y, tol))),
8068 DyadOp::GradeSelect { down } => grade_select(x, y, down, cfg.rules, cfg.tol, span),
8069 DyadOp::Copy => {
8070 copy_items(x, y, cfg.agreement == Agreement::ExactOrScalar, cfg.near(), span)
8071 }
8072 DyadOp::CollateGrade { down, origin } => collate_grade(x, y, down, origin, span),
8073 DyadOp::TransposeJ => transpose_j(x, y, cfg.near(), span),
8074 DyadOp::TransposeApl => transpose_apl(x, y, cfg.rules.origin, cfg.near(), span),
8075 DyadOp::DecodeApl => decode_apl(x, y, span).map(|r| carry_exact2(r, x, y)),
8076 DyadOp::EncodeApl => {
8077 let tol = match cfg.rules.encode_digits {
8080 EncodeDigits::Tolerant => cfg.tol,
8081 EncodeDigits::Exact => Tol { ct: 0.0, ..cfg.tol },
8082 };
8083 encode_apl(x, y, tol, span).map(|r| carry_exact2(r, x, y))
8084 }
8085 DyadOp::Decode => decode(Some(x), y, cfg.tol, span).map(|r| carry_exact2(r, x, y)),
8086 DyadOp::Encode => encode(x, y, cfg.tol, span).map(|r| carry_exact2(r, x, y)),
8087 DyadOp::Laminate => laminate(x, y, span),
8088 DyadOp::Link => link(x, y, span),
8089 DyadOp::Strand => strand(x, y, span),
8090 DyadOp::IntervalIndex { offset, closed } => {
8091 interval_index(x, y, offset, closed, tol, Grading::of(cfg.rules, tol), span)
8092 }
8093 DyadOp::IndexOfLast { origin } => Ok(index_of_last(x, y, origin, tol)),
8094 DyadOp::MatrixDivide => matrix_divide(x, y, span),
8095 DyadOp::PartitionEnclose => partition_enclose(x, y, cfg.near(), span),
8096 DyadOp::PartitionCounts => partition_counts(x, y, cfg.near(), span),
8097 DyadOp::Squad { origin, leading } => squad(x, y, origin, leading, cfg.near(), span),
8098 DyadOp::SelectAxis { axis, rank, origin } => {
8099 select_axis(x, y, axis, rank, origin, cfg.near(), span)
8100 }
8101 DyadOp::Fetch => fetch(x, y, cfg.near(), span),
8102 DyadOp::PolyEval => poly_eval(x, y, span),
8103 DyadOp::PolyIntegral => poly_integral(x, y, span),
8104 DyadOp::TruthTable(m) => truth_table(m, x, y, span),
8105 DyadOp::FormatSpec => format_spec(x, y, &cfg.fmt, span),
8106 DyadOp::FormatSpecJ => format_spec_j(x, y, &cfg.fmt, span),
8107 DyadOp::ParseNumbers => parse_numbers(x, y, span),
8108 DyadOp::SequentialMachine => sequential_machine(x, y, span),
8109 DyadOp::Deal { origin, fixed } => deal(x, y, origin, fixed, cfg.near(), span),
8110 DyadOp::ExactForm => exact_form(x, y, cfg.near(), span),
8111 DyadOp::Boolean(op) => bool_dyad(op, x, y, cfg, span),
8112 DyadOp::Less => {
8113 set_rank(cfg, "without", x, y, span)?;
8114 let (x, y) = align_mixed(x, y, apl);
8115 Ok(set_less(&x, &y, tol))
8116 }
8117 DyadOp::Union => {
8118 set_rank(cfg, "union", x, y, span)?;
8119 let (x, y) = align_mixed(x, y, apl);
8120 union_items(&x, &y, tol, span)
8121 }
8122 DyadOp::Intersect => {
8123 set_rank(cfg, "intersection", x, y, span)?;
8124 let (x, y) = align_mixed(x, y, apl);
8125 Ok(intersect_items(&x, &y, tol))
8126 }
8127 DyadOp::AnagramFrom => anagram_from(x, y, cfg.near(), span),
8128 DyadOp::Permute => permute(x, y, cfg.near(), span),
8129 DyadOp::FindSeq => {
8130 let (x, y) = align_mixed(x, y, apl);
8131 find_seq(&x, &y, tol, apl, span)
8132 }
8133 DyadOp::UnicodeForm => unicode_form(x, y, cfg.near(), span),
8134 DyadOp::SymbolForm => symbol_form(x, y, span),
8135 DyadOp::SparseForm => sparse_form(x, y, cfg.near(), span),
8136 DyadOp::PrimeMeta => prime_meta(x, y, cfg.near(), span).map(|r| carry_exact2(r, x, y)),
8137 DyadOp::PrimeExponents => {
8138 prime_exponents(x, y, cfg.near(), span).map(|r| carry_exact2(r, x, y))
8139 }
8140 DyadOp::Pick { origin } => pick(x, y, origin, cfg.near(), span),
8141 DyadOp::Expand => expand(x, y, cfg.rules.lang == crate::Lang::Apl, cfg.near(), span),
8142 DyadOp::WriteStream => Err(Error::internal("1!:2 reached the pure dyad dispatcher")),
8145 DyadOp::NotYet(what) => Err(Error::not_yet(what, span)),
8146 DyadOp::None => Err(Error::domain(format!("{} has no dyadic meaning", p.name), span)),
8147 }
8148}
8149
8150const APL_EXTREME: f64 = 1.7976e308;
8159
8160fn reduce_identity(v: &Verb, n: usize, lang: crate::Lang) -> Option<Data> {
8170 let Verb::Prim(p) = v else { return None };
8171 let DyadOp::Scalar(op) = p.dyad else { return None };
8172 let ints = |k: i64| Data::I64(vec![k; n].into());
8173 let bits = |k: u8| Data::Bool(vec![k; n].into());
8174 let extreme =
8175 |sign: f64| Data::F64(vec![sign * if lang == crate::Lang::Apl { APL_EXTREME } else { f64::INFINITY }; n].into());
8176 Some(match op {
8177 ScalarDyad::Add | ScalarDyad::Sub | ScalarDyad::Gcd | ScalarDyad::Residue => ints(0),
8178 ScalarDyad::Mul
8179 | ScalarDyad::DivJ
8180 | ScalarDyad::DivApl
8181 | ScalarDyad::Pow
8182 | ScalarDyad::Lcm
8183 | ScalarDyad::Root
8184 | ScalarDyad::Binomial => ints(1),
8185 ScalarDyad::Min => extreme(1.0),
8186 ScalarDyad::Max => extreme(-1.0),
8187 ScalarDyad::Eq | ScalarDyad::Le | ScalarDyad::Ge => bits(1),
8188 ScalarDyad::Ne | ScalarDyad::Lt | ScalarDyad::Gt => bits(0),
8189 ScalarDyad::MakeComplex | ScalarDyad::PolarBy => return None,
8192 ScalarDyad::Log | ScalarDyad::Circle => return None,
8195 })
8196}
8197
8198fn is_associative(op: ScalarDyad) -> bool {
8204 use ScalarDyad::*;
8205 matches!(op, Add | Mul | Min | Max)
8206}
8207
8208#[inline(always)]
8209fn fold_range_body<S, T, F>(
8210 v: &[S],
8211 m: usize,
8212 lo: usize,
8213 hi: usize,
8214 j0: usize,
8215 acc: &mut [T],
8216 step: &F,
8217) -> bool
8218where
8219 S: Widen<T>,
8220 T: Copy,
8221 F: Fn(T, T) -> (T, bool),
8222{
8223 let w = acc.len();
8224 let base = (hi - 1) * m + j0;
8225 for (slot, &x) in acc.iter_mut().zip(&v[base..base + w]) {
8226 *slot = x.widen();
8227 }
8228 let mut over = false;
8231 for i in (lo..hi - 1).rev() {
8232 let row = &v[i * m + j0..i * m + j0 + w];
8233 for (slot, &x) in acc.iter_mut().zip(row) {
8234 let (r, o) = step(x.widen(), *slot);
8235 *slot = r;
8236 over |= o;
8237 }
8238 }
8239 !over
8240}
8241
8242multiversioned! {
8243 #[allow(clippy::too_many_arguments)]
8244 fn fold_range_vectorised[S: Widen<T>, T: Copy, F: Fn(T, T) -> (T, bool)](
8245 v: &[S],
8246 m: usize,
8247 lo: usize,
8248 hi: usize,
8249 j0: usize,
8250 acc: &mut [T],
8251 step: &F,
8252 ) -> bool = fold_range_body;
8253}
8254
8255const VECTOR_COLUMNS: usize = 16;
8263
8264#[allow(clippy::too_many_arguments)]
8277#[inline]
8278fn fold_range<S, T, F>(
8279 v: &[S],
8280 m: usize,
8281 lo: usize,
8282 hi: usize,
8283 j0: usize,
8284 acc: &mut [T],
8285 step: &F,
8286) -> bool
8287where
8288 S: Widen<T>,
8289 T: Copy,
8290 F: Fn(T, T) -> (T, bool),
8291{
8292 if acc.len() < VECTOR_COLUMNS {
8293 fold_range_body(v, m, lo, hi, j0, acc, step)
8294 } else {
8295 fold_range_vectorised(v, m, lo, hi, j0, acc, step)
8296 }
8297}
8298
8299const FOLD_LANES: usize = 8;
8310
8311const MIN_LANE_WORK: usize = 8 * FOLD_LANES;
8316
8317#[inline(always)]
8322fn fold_lanes_body<S, T, F>(v: &[S], step: &F) -> Option<T>
8323where
8324 S: Widen<T>,
8325 T: Copy,
8326 F: Fn(T, T) -> (T, bool),
8327{
8328 let n = v.len();
8329 let mut over = false;
8330 if n < MIN_LANE_WORK {
8331 let mut acc = v[n - 1].widen();
8332 for &x in v[..n - 1].iter().rev() {
8333 let (r, o) = step(x.widen(), acc);
8334 acc = r;
8335 over |= o;
8336 }
8337 return (!over).then_some(acc);
8338 }
8339 let rows = n / FOLD_LANES;
8342 let head = n - rows * FOLD_LANES;
8343 let last = head + (rows - 1) * FOLD_LANES;
8344 let mut acc = [v[last].widen(); FOLD_LANES];
8345 for (slot, &x) in acc.iter_mut().zip(&v[last..last + FOLD_LANES]) {
8346 *slot = x.widen();
8347 }
8348 for r in (0..rows - 1).rev() {
8349 let row = &v[head + r * FOLD_LANES..head + (r + 1) * FOLD_LANES];
8350 for (slot, &x) in acc.iter_mut().zip(row) {
8351 let (r, o) = step(x.widen(), *slot);
8352 *slot = r;
8353 over |= o;
8354 }
8355 }
8356 let mut a = acc[FOLD_LANES - 1];
8357 for &x in acc[..FOLD_LANES - 1].iter().rev() {
8358 let (r, o) = step(x, a);
8359 a = r;
8360 over |= o;
8361 }
8362 for &x in v[..head].iter().rev() {
8363 let (r, o) = step(x.widen(), a);
8364 a = r;
8365 over |= o;
8366 }
8367 (!over).then_some(a)
8368}
8369
8370multiversioned! {
8371 fn fold_lanes_vectorised[S: Widen<T>, T: Copy, F: Fn(T, T) -> (T, bool)](
8372 v: &[S],
8373 step: &F,
8374 ) -> Option<T> = fold_lanes_body;
8375}
8376
8377#[inline]
8380fn fold_lanes<S, T, F>(v: &[S], step: &F) -> Option<T>
8381where
8382 S: Widen<T>,
8383 T: Copy,
8384 F: Fn(T, T) -> (T, bool),
8385{
8386 if v.len() < MIN_LANE_WORK {
8387 fold_lanes_body(v, step)
8388 } else {
8389 fold_lanes_vectorised(v, step)
8390 }
8391}
8392
8393fn fold_flat<S, T, F>(v: &[S], n: usize, assoc: bool, step: &F) -> Option<T>
8396where
8397 S: Widen<T>,
8398 T: Copy + Send + Sync,
8399 F: Fn(T, T) -> (T, bool) + Sync + Send,
8400{
8401 if assoc {
8402 return par::try_fold_chunks(
8403 &v[..n],
8404 |part| fold_lanes(part, step),
8405 |a, b| {
8406 let (r, o) = step(a, b);
8407 (!o).then_some(r)
8408 },
8409 );
8410 }
8411 let mut acc = v[n - 1].widen();
8412 let mut over = false;
8413 for &x in v[..n - 1].iter().rev() {
8414 let (r, o) = step(x.widen(), acc);
8415 acc = r;
8416 over |= o;
8417 }
8418 (!over).then_some(acc)
8419}
8420
8421fn fold_items<S, T, F>(v: &[S], n: usize, m: usize, assoc: bool, step: F) -> Option<Vec<T>>
8432where
8433 S: Widen<T>,
8434 T: Copy + Default + Send + Sync,
8435 F: Fn(T, T) -> (T, bool) + Sync + Send,
8436{
8437 if m >= par::WIDE_ITEM {
8438 let (out, ok) = par::fill_wide(m, n * m, |j0, acc: &mut [T]| {
8439 fold_range(v, m, 0, n, j0, acc, &step)
8440 });
8441 return ok.then_some(out);
8442 }
8443 if m == 1 {
8444 return fold_flat(v, n, assoc, &step).map(|x| vec![x]);
8445 }
8446 let chunks = if assoc { par::chunks(n, n * m) } else { 1 };
8447 if chunks < 2 {
8448 let mut acc = vec![T::default(); m];
8449 return fold_range(v, m, 0, n, 0, &mut acc, &step).then_some(acc);
8450 }
8451 let per = n.div_ceil(chunks);
8452 let parts = par::map_indexed(n.div_ceil(per), |c| {
8453 let mut acc = vec![T::default(); m];
8454 let ok = fold_range(v, m, c * per, ((c + 1) * per).min(n), 0, &mut acc, &step);
8455 ok.then_some(acc)
8456 });
8457 let mut it = parts.into_iter().rev();
8460 let mut acc = it.next()??;
8461 for part in it {
8462 let part = part?;
8463 let mut over = false;
8464 for (slot, &x) in acc.iter_mut().zip(&part) {
8465 let (r, o) = step(x, *slot);
8466 *slot = r;
8467 over |= o;
8468 }
8469 if over {
8470 return None;
8471 }
8472 }
8473 Some(acc)
8474}
8475
8476#[inline(always)]
8486fn block_f64(r: f64) -> (f64, bool) {
8487 (r, r.is_nan())
8488}
8489
8490fn fold_i64<S: Widen<i64>>(op: ScalarDyad, v: &[S], n: usize, m: usize) -> Option<Vec<i64>> {
8493 use ScalarDyad::*;
8494 let assoc = is_associative(op);
8495 match op {
8496 Add => fold_items(v, n, m, assoc, i64::overflowing_add),
8497 Sub => fold_items(v, n, m, assoc, i64::overflowing_sub),
8498 Mul => fold_items(v, n, m, assoc, i64::overflowing_mul),
8499 Min => fold_items(v, n, m, assoc, |a: i64, b: i64| (a.min(b), false)),
8500 Max => fold_items(v, n, m, assoc, |a: i64, b: i64| (a.max(b), false)),
8501 _ => None,
8502 }
8503}
8504
8505fn fold_cx(op: ScalarDyad, v: &[Cx], n: usize, m: usize) -> Option<Vec<Cx>> {
8506 use ScalarDyad::*;
8507 let assoc = is_associative(op);
8508 match op {
8509 Add => fold_items(v, n, m, assoc, |a: Cx, b: Cx| (cx::add(a, b), false)),
8510 Sub => fold_items(v, n, m, assoc, |a: Cx, b: Cx| (cx::sub(a, b), false)),
8511 Mul => fold_items(v, n, m, assoc, |a: Cx, b: Cx| (cx::mul(a, b), false)),
8512 _ => None,
8514 }
8515}
8516
8517fn fold_f64(op: ScalarDyad, v: &[f64], n: usize, m: usize) -> Option<Vec<f64>> {
8518 use ScalarDyad::*;
8519 let assoc = is_associative(op);
8520 match op {
8521 Add => fold_items(v, n, m, assoc, |a: f64, b: f64| block_f64(a + b)),
8522 Sub => fold_items(v, n, m, assoc, |a: f64, b: f64| block_f64(a - b)),
8523 Mul => fold_items(v, n, m, assoc, |a: f64, b: f64| block_f64(a * b)),
8524 Min => fold_items(v, n, m, assoc, |a: f64, b: f64| (a.min(b), false)),
8525 Max => fold_items(v, n, m, assoc, |a: f64, b: f64| (a.max(b), false)),
8526 _ => None,
8527 }
8528}
8529
8530fn reduce_typed(op: ScalarDyad, d: &Data, n: usize, m: usize) -> Option<Data> {
8534 use ScalarDyad::*;
8535 if !matches!(op, Add | Sub | Mul | Min | Max) {
8538 return None;
8539 }
8540 match d {
8541 Data::F64(v) => Some(Data::F64(fold_f64(op, v, n, m)?.into())),
8542 Data::Complex(v) => Some(Data::Complex(fold_cx(op, v, n, m)?.into())),
8543 Data::I64(v) => Some(Data::I64(fold_i64(op, v, n, m)?.into())),
8544 Data::Bool(v) => Some(Data::I64(fold_i64(op, v.as_slice(), n, m)?.into())),
8548 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
8551 }
8552}
8553
8554#[inline(always)]
8560fn fold_runs_body<S, T, F>(v: &[S], start: usize, m: usize, out: &mut [T], step: &F) -> bool
8561where
8562 S: Widen<T>,
8563 T: Copy,
8564 F: Fn(T, T) -> (T, bool),
8565{
8566 let mut over = false;
8567 for (k, slot) in out.iter_mut().enumerate() {
8568 let run = &v[(start + k) * m..(start + k + 1) * m];
8569 let mut acc = run[m - 1].widen();
8570 for &x in run[..m - 1].iter().rev() {
8571 let (r, o) = step(x.widen(), acc);
8572 acc = r;
8573 over |= o;
8574 }
8575 *slot = acc;
8576 }
8577 !over
8578}
8579
8580multiversioned! {
8581 fn fold_runs_vectorised[S: Widen<T>, T: Copy, F: Fn(T, T) -> (T, bool)](
8582 v: &[S],
8583 start: usize,
8584 m: usize,
8585 out: &mut [T],
8586 step: &F,
8587 ) -> bool = fold_runs_body;
8588}
8589
8590fn fold_runs<S, T, F>(v: &[S], n: usize, m: usize, step: F) -> Option<Vec<T>>
8593where
8594 S: Widen<T>,
8595 T: Copy + Default + Send + Sync,
8596 F: Fn(T, T) -> (T, bool) + Sync + Send,
8597{
8598 let wide = m >= VECTOR_COLUMNS;
8602 let (out, ok) = par::fill_wide(n, n * m, |start, part: &mut [T]| {
8603 if wide {
8604 fold_runs_vectorised(v, start, m, part, &step)
8605 } else {
8606 fold_runs_body(v, start, m, part, &step)
8607 }
8608 });
8609 ok.then_some(out)
8610}
8611
8612fn fold_runs_data(op: ScalarDyad, d: &Data, n: usize, m: usize) -> Option<Data> {
8613 use ScalarDyad::*;
8614 match d {
8615 Data::F64(v) => Some(Data::F64(
8616 match op {
8617 Add => fold_runs(v, n, m, |a: f64, b: f64| block_f64(a + b)),
8618 Sub => fold_runs(v, n, m, |a: f64, b: f64| block_f64(a - b)),
8619 Mul => fold_runs(v, n, m, |a: f64, b: f64| block_f64(a * b)),
8620 Min => fold_runs(v, n, m, |a: f64, b: f64| (a.min(b), false)),
8621 Max => fold_runs(v, n, m, |a: f64, b: f64| (a.max(b), false)),
8622 _ => None,
8623 }?
8624 .into(),
8625 )),
8626 Data::I64(v) => Some(Data::I64(fold_runs_i64(op, v.as_slice(), n, m)?.into())),
8627 Data::Complex(v) => Some(Data::Complex(
8629 match op {
8630 Add => fold_runs(v, n, m, |a: Cx, b: Cx| (cx::add(a, b), false)),
8631 Sub => fold_runs(v, n, m, |a: Cx, b: Cx| (cx::sub(a, b), false)),
8632 Mul => fold_runs(v, n, m, |a: Cx, b: Cx| (cx::mul(a, b), false)),
8633 _ => None,
8634 }?
8635 .into(),
8636 )),
8637 Data::Bool(v) => Some(Data::I64(fold_runs_i64(op, v.as_slice(), n, m)?.into())),
8639 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
8640 }
8641}
8642
8643fn fold_runs_i64<S: Widen<i64>>(op: ScalarDyad, v: &[S], n: usize, m: usize) -> Option<Vec<i64>> {
8645 use ScalarDyad::*;
8646 match op {
8647 Add => fold_runs(v, n, m, i64::overflowing_add),
8648 Sub => fold_runs(v, n, m, i64::overflowing_sub),
8649 Mul => fold_runs(v, n, m, i64::overflowing_mul),
8650 Min => fold_runs(v, n, m, |a: i64, b: i64| (a.min(b), false)),
8651 Max => fold_runs(v, n, m, |a: i64, b: i64| (a.max(b), false)),
8652 _ => None,
8653 }
8654}
8655
8656fn run_slices<T: Clone>(b: &Buf<T>, runs: usize, len: usize) -> Vec<&[T]> {
8672 if let Some(parts) = b.parts() && parts.len() == runs && parts.iter().all(|p| p.len() == len) {
8673 return parts.iter().map(Buf::as_slice).collect();
8674 }
8675 let flat = b.as_slice();
8676 (0..runs).map(|c| &flat[c * len..(c + 1) * len]).collect()
8677}
8678
8679fn fold_columns<S, T, F>(cols: &[&[S]], len: usize, assoc: bool, step: F) -> Option<Vec<T>>
8688where
8689 S: Widen<T>,
8690 T: Copy + Default + Send + Sync,
8691 F: Fn(T, T) -> (T, bool) + Sync + Send,
8692{
8693 if par::worth_it(len) {
8698 let mut out = Vec::with_capacity(cols.len());
8699 for c in cols {
8700 out.push(fold_flat(c, len, assoc, &step)?);
8701 }
8702 return Some(out);
8703 }
8704 let (out, ok) = par::fill_wide(cols.len(), cols.len() * len, |start, part: &mut [T]| {
8705 let mut ok = true;
8706 for (k, slot) in part.iter_mut().enumerate() {
8707 match fold_flat(cols[start + k], len, assoc, &step) {
8708 Some(v) => *slot = v,
8709 None => ok = false,
8710 }
8711 }
8712 ok
8713 });
8714 ok.then_some(out)
8715}
8716
8717fn fold_columns_data(op: ScalarDyad, d: &Data, runs: usize, len: usize) -> Option<Data> {
8719 use ScalarDyad::*;
8720 if !matches!(op, Add | Sub | Mul | Min | Max) {
8721 return None;
8722 }
8723 let assoc = is_associative(op);
8724 macro_rules! by {
8725 ($v:expr, $add:expr, $sub:expr, $mul:expr, $min:expr, $max:expr) => {{
8726 let cols = run_slices($v, runs, len);
8727 match op {
8728 Add => fold_columns(&cols, len, assoc, $add),
8729 Sub => fold_columns(&cols, len, assoc, $sub),
8730 Mul => fold_columns(&cols, len, assoc, $mul),
8731 Min => fold_columns(&cols, len, assoc, $min),
8732 Max => fold_columns(&cols, len, assoc, $max),
8733 _ => None,
8734 }?
8735 }};
8736 }
8737 match d {
8738 Data::F64(v) => Some(Data::F64(
8739 by!(
8740 v,
8741 |a: f64, b: f64| block_f64(a + b),
8742 |a: f64, b: f64| block_f64(a - b),
8743 |a: f64, b: f64| block_f64(a * b),
8744 |a: f64, b: f64| (a.min(b), false),
8745 |a: f64, b: f64| (a.max(b), false)
8746 )
8747 .into(),
8748 )),
8749 Data::I64(v) => Some(Data::I64(
8750 by!(
8751 v,
8752 i64::overflowing_add,
8753 i64::overflowing_sub,
8754 i64::overflowing_mul,
8755 |a: i64, b: i64| (a.min(b), false),
8756 |a: i64, b: i64| (a.max(b), false)
8757 )
8758 .into(),
8759 )),
8760 Data::Complex(v) => {
8761 if !matches!(op, Add | Sub | Mul) {
8762 return None;
8763 }
8764 Some(Data::Complex(
8765 by!(
8766 v,
8767 |a: Cx, b: Cx| (cx::add(a, b), false),
8768 |a: Cx, b: Cx| (cx::sub(a, b), false),
8769 |a: Cx, b: Cx| (cx::mul(a, b), false),
8770 |_: Cx, _: Cx| unreachable!("refused above"),
8771 |_: Cx, _: Cx| unreachable!("refused above")
8772 )
8773 .into(),
8774 ))
8775 }
8776 Data::Bool(v) => Some(Data::I64(
8780 by!(
8781 v,
8782 i64::overflowing_add,
8783 i64::overflowing_sub,
8784 i64::overflowing_mul,
8785 |a: i64, b: i64| (a.min(b), false),
8786 |a: i64, b: i64| (a.max(b), false)
8787 )
8788 .into(),
8789 )),
8790 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
8791 }
8792}
8793
8794fn reduce_columns(v: &Verb, y: &Array) -> Option<Array> {
8799 let Verb::Prim(p) = v else { return None };
8800 let DyadOp::Scalar(op) = p.dyad else { return None };
8801 if !y.dtype().is_numeric() {
8802 return None;
8803 }
8804 let n = y.shape[0];
8805 let m: usize = y.shape[1..].iter().product();
8806 if n == 0 || m == 0 {
8809 return None;
8810 }
8811 let shape = y.shape[1..].to_vec();
8812 if n == 1 {
8815 return Some(Array::col_major(shape, y.data.clone()));
8816 }
8817 let data = fold_columns_data(op, &y.data, m, n)?;
8818 Some(Array::col_major(shape, data))
8819}
8820
8821fn fold_across<S, T, F>(cols: &[&[S]], rows: usize, step: F) -> Option<Vec<T>>
8824where
8825 S: Widen<T>,
8826 T: Copy + Default + Send + Sync,
8827 F: Fn(T, T) -> (T, bool) + Sync + Send,
8828{
8829 let (last, rest) = cols.split_last()?;
8830 let (out, ok) = par::fill(rows, |start, part: &mut [T]| {
8831 let mut over = false;
8832 for (k, slot) in part.iter_mut().enumerate() {
8833 let i = start + k;
8834 let mut acc = last[i].widen();
8835 for c in rest.iter().rev() {
8836 let (r, o) = step(c[i].widen(), acc);
8837 acc = r;
8838 over |= o;
8839 }
8840 *slot = acc;
8841 }
8842 !over
8843 });
8844 ok.then_some(out)
8845}
8846
8847fn fold_across_data(op: ScalarDyad, d: &Data, rows: usize, cols: usize) -> Option<Data> {
8848 use ScalarDyad::*;
8849 if !matches!(op, Add | Sub | Mul | Min | Max) {
8850 return None;
8851 }
8852 macro_rules! by {
8853 ($v:expr, $add:expr, $sub:expr, $mul:expr, $min:expr, $max:expr) => {{
8854 let parts = run_slices($v, cols, rows);
8855 match op {
8856 Add => fold_across(&parts, rows, $add),
8857 Sub => fold_across(&parts, rows, $sub),
8858 Mul => fold_across(&parts, rows, $mul),
8859 Min => fold_across(&parts, rows, $min),
8860 Max => fold_across(&parts, rows, $max),
8861 _ => None,
8862 }?
8863 }};
8864 }
8865 match d {
8866 Data::F64(v) => Some(Data::F64(
8867 by!(
8868 v,
8869 |a: f64, b: f64| block_f64(a + b),
8870 |a: f64, b: f64| block_f64(a - b),
8871 |a: f64, b: f64| block_f64(a * b),
8872 |a: f64, b: f64| (a.min(b), false),
8873 |a: f64, b: f64| (a.max(b), false)
8874 )
8875 .into(),
8876 )),
8877 Data::I64(v) => Some(Data::I64(
8878 by!(
8879 v,
8880 i64::overflowing_add,
8881 i64::overflowing_sub,
8882 i64::overflowing_mul,
8883 |a: i64, b: i64| (a.min(b), false),
8884 |a: i64, b: i64| (a.max(b), false)
8885 )
8886 .into(),
8887 )),
8888 Data::Complex(v) => {
8889 if !matches!(op, Add | Sub | Mul) {
8890 return None;
8891 }
8892 Some(Data::Complex(
8893 by!(
8894 v,
8895 |a: Cx, b: Cx| (cx::add(a, b), false),
8896 |a: Cx, b: Cx| (cx::sub(a, b), false),
8897 |a: Cx, b: Cx| (cx::mul(a, b), false),
8898 |_: Cx, _: Cx| unreachable!("refused above"),
8899 |_: Cx, _: Cx| unreachable!("refused above")
8900 )
8901 .into(),
8902 ))
8903 }
8904 Data::Bool(v) => Some(Data::I64(
8906 by!(
8907 v,
8908 i64::overflowing_add,
8909 i64::overflowing_sub,
8910 i64::overflowing_mul,
8911 |a: i64, b: i64| (a.min(b), false),
8912 |a: i64, b: i64| (a.max(b), false)
8913 )
8914 .into(),
8915 )),
8916 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
8917 }
8918}
8919
8920fn reduce_rows_columns(u: &Verb, y: &Array) -> Option<Array> {
8923 let Verb::Reduce(inner) = u else { return None };
8924 let Verb::Prim(p) = &**inner else { return None };
8925 let DyadOp::Scalar(op) = p.dyad else { return None };
8926 if y.rank() != 2 || !y.dtype().is_numeric() {
8929 return None;
8930 }
8931 let (rows, cols) = (y.shape[0], y.shape[1]);
8932 if rows == 0 || cols == 0 {
8935 return None;
8936 }
8937 if cols == 1 {
8938 return Some(Array::new(vec![rows], y.data.clone()));
8940 }
8941 let data = fold_across_data(op, &y.data, rows, cols)?;
8942 Some(Array::new(vec![rows], data))
8943}
8944
8945fn reduce_vector_cells(u: &Verb, y: &Array, frame_rank: usize) -> Option<Array> {
8954 let Verb::Reduce(inner) = u else { return None };
8955 let Verb::Prim(p) = &**inner else { return None };
8956 let DyadOp::Scalar(op) = p.dyad else { return None };
8957 if y.rank() != frame_rank + 1 || !y.dtype().is_numeric() {
8960 return None;
8961 }
8962 let m = y.shape[frame_rank];
8963 if m == 0 {
8966 return None;
8967 }
8968 use ScalarDyad::{Add, Max, Min, Mul, Sub};
8969 if !matches!(op, Add | Sub | Mul | Min | Max) {
8970 return None;
8971 }
8972 let frame = y.shape[..frame_rank].to_vec();
8973 if m == 1 {
8974 return Some(Array::new(frame, y.data.clone()));
8977 }
8978 let n: usize = frame.iter().product();
8979 let data = fold_runs_data(op, &y.data, n, m)?;
8980 Some(Array::new(frame, data))
8981}
8982
8983fn reduce(v: &Verb, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
8985 if y.rank() == 0 {
8986 return Ok(y.clone());
8987 }
8988 let n = y.items();
8989 if n == 1 {
8990 return Ok(y.item(0));
8991 }
8992 let cell_shape = y.shape[1..].to_vec();
8993 let m: usize = cell_shape.iter().product();
8994 if n == 0 {
8995 if matches!(v, Verb::Prim(p) if matches!(p.dyad, DyadOp::AppendLeading | DyadOp::AppendLast))
8998 {
8999 return Ok(Array::new(vec![0], Data::empty(y.dtype())));
9000 }
9001 return match reduce_identity(v, m, ctx.cfg.rules.lang) {
9002 Some(d) => Ok(Array::new(cell_shape, d)),
9003 None => Err(Error::domain(
9004 format!("empty reduction has no identity for {}", v.name()),
9005 span,
9006 )),
9007 };
9008 }
9009 if y.dtype().is_numeric() && let Verb::Prim(p) = v && let DyadOp::Scalar(op) = p.dyad {
9010 if let Some(d) = reduce_typed(op, y.row_major_data(), n, m) {
9015 return Ok(Array::new(cell_shape, d));
9016 }
9017 let mut acc = y.data.slice((n - 1) * m, n * m);
9020 for i in (0..n - 1).rev() {
9021 acc =
9022 scalar_dyad_data(
9023 op,
9024 &y.data,
9025 i * m,
9026 1,
9027 &acc,
9028 0,
9029 1,
9030 m,
9031 ctx.cfg.tol,
9032 ctx.cfg.rules,
9033 span,
9034 )?;
9035 }
9036 return Ok(Array::new(cell_shape, acc));
9037 }
9038 if ctx.cfg.rules.lang == crate::Lang::Apl {
9039 return item_fold(v, y, ctx, span);
9040 }
9041 let mut acc = y.item(n - 1);
9042 for i in (0..n - 1).rev() {
9043 acc = v.dyad(&y.item(i), &acc, ctx, span)?;
9044 }
9045 Ok(acc)
9046}
9047
9048fn item_fold(v: &Verb, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
9059 let n = y.items();
9060 let frame = y.shape[1..].to_vec();
9061 let m: usize = frame.iter().product();
9062 if m == 0 {
9063 return assemble(&frame, Vec::new(), span);
9064 }
9065 let base = y.to_row_major();
9066 let cells = each_cell(m, base.count(), v.is_pure(), ctx, |p, c| {
9067 let mut acc = open_cell(&atom(&base, (n - 1) * m + p));
9068 for i in (0..n - 1).rev() {
9069 acc = v.dyad(&open_cell(&atom(&base, i * m + p)), &acc, c, span)?;
9070 }
9071 Ok(enclose(&acc, Enclose::ExceptSimpleScalar))
9072 })?;
9073 assemble_items(&frame, cells, span)
9074}
9075
9076fn folded_op(u: &Verb) -> Option<ScalarDyad> {
9083 let Verb::Reduce(inner) = u else { return None };
9084 let Verb::Prim(p) = &**inner else { return None };
9085 match p.dyad {
9086 DyadOp::Scalar(op) => Some(op),
9087 _ => None,
9088 }
9089}
9090
9091fn section(y: &Array, lo: usize, hi: usize) -> Array {
9093 let m = y.item_size();
9094 let mut shape = y.shape.clone();
9095 shape[0] = hi - lo;
9096 Array::new(shape, y.data.slice(lo * m, hi * m))
9097}
9098
9099fn as_items(y: &Array) -> Option<Array> {
9102 (y.rank() == 0).then(|| Array::new(vec![1], y.data.clone()))
9103}
9104
9105#[inline(always)]
9106fn scan_flat_body<S, T, F>(v: &[S], n: usize, m: usize, back: bool, step: F) -> Option<Vec<T>>
9107where
9108 S: Widen<T>,
9109 T: Copy + Default,
9110 F: Fn(T, T) -> (T, bool),
9111{
9112 if m == 1 {
9113 let mut out = vec![T::default(); n];
9116 let mut over = false;
9117 if back {
9118 let mut acc = v[n - 1].widen();
9119 out[n - 1] = acc;
9120 for (slot, &x) in out[..n - 1].iter_mut().zip(&v[..n - 1]).rev() {
9121 let (r, o) = step(x.widen(), acc);
9122 acc = r;
9123 over |= o;
9124 *slot = acc;
9125 }
9126 } else {
9127 let mut acc = v[0].widen();
9128 out[0] = acc;
9129 for (slot, &x) in out[1..n].iter_mut().zip(&v[1..n]) {
9130 let (r, o) = step(acc, x.widen());
9131 acc = r;
9132 over |= o;
9133 *slot = acc;
9134 }
9135 }
9136 return (!over).then_some(out);
9137 }
9138 let mut out = vec![T::default(); n * m];
9139 let mut acc = vec![T::default(); m];
9140 let mut over = false;
9141 if back {
9142 for (slot, &x) in acc.iter_mut().zip(&v[(n - 1) * m..n * m]) {
9143 *slot = x.widen();
9144 }
9145 out[(n - 1) * m..n * m].copy_from_slice(&acc);
9146 for i in (0..n - 1).rev() {
9147 for (j, slot) in acc.iter_mut().enumerate() {
9148 let (r, o) = step(v[i * m + j].widen(), *slot);
9149 *slot = r;
9150 over |= o;
9151 }
9152 out[i * m..i * m + m].copy_from_slice(&acc);
9153 }
9154 } else {
9155 for (slot, &x) in acc.iter_mut().zip(&v[..m]) {
9156 *slot = x.widen();
9157 }
9158 out[..m].copy_from_slice(&acc);
9159 for i in 1..n {
9160 for (j, slot) in acc.iter_mut().enumerate() {
9161 let (r, o) = step(*slot, v[i * m + j].widen());
9162 *slot = r;
9163 over |= o;
9164 }
9165 out[i * m..i * m + m].copy_from_slice(&acc);
9166 }
9167 }
9168 (!over).then_some(out)
9169}
9170
9171multiversioned! {
9172 fn scan_flat_vectorised[S: Widen<T>, T: Copy + Default, F: Fn(T, T) -> (T, bool)](
9173 v: &[S],
9174 n: usize,
9175 m: usize,
9176 back: bool,
9177 step: F,
9178 ) -> Option<Vec<T>> = scan_flat_body;
9179}
9180
9181fn scan_flat<S, T, F>(v: &[S], n: usize, m: usize, back: bool, step: F) -> Option<Vec<T>>
9193where
9194 S: Widen<T>,
9195 T: Copy + Default,
9196 F: Fn(T, T) -> (T, bool),
9197{
9198 if m < VECTOR_COLUMNS {
9199 scan_flat_body(v, n, m, back, step)
9200 } else {
9201 scan_flat_vectorised(v, n, m, back, step)
9202 }
9203}
9204
9205fn scan_i64<S: Widen<i64>>(
9206 op: ScalarDyad,
9207 v: &[S],
9208 n: usize,
9209 m: usize,
9210 back: bool,
9211) -> Option<Vec<i64>> {
9212 use ScalarDyad::*;
9213 match op {
9214 Add => scan_flat(v, n, m, back, i64::overflowing_add),
9215 Sub => scan_flat(v, n, m, back, i64::overflowing_sub),
9216 Mul => scan_flat(v, n, m, back, i64::overflowing_mul),
9217 Min => scan_flat(v, n, m, back, |a: i64, b: i64| (a.min(b), false)),
9218 Max => scan_flat(v, n, m, back, |a: i64, b: i64| (a.max(b), false)),
9219 _ => None,
9220 }
9221}
9222
9223fn scan_cx(op: ScalarDyad, v: &[Cx], n: usize, m: usize, back: bool) -> Option<Vec<Cx>> {
9224 use ScalarDyad::*;
9225 match op {
9226 Add => scan_flat(v, n, m, back, |a: Cx, b: Cx| (cx::add(a, b), false)),
9227 Sub => scan_flat(v, n, m, back, |a: Cx, b: Cx| (cx::sub(a, b), false)),
9228 Mul => scan_flat(v, n, m, back, |a: Cx, b: Cx| (cx::mul(a, b), false)),
9229 _ => None,
9230 }
9231}
9232
9233fn scan_f64<S: Widen<f64>>(
9234 op: ScalarDyad,
9235 v: &[S],
9236 n: usize,
9237 m: usize,
9238 back: bool,
9239) -> Option<Vec<f64>> {
9240 use ScalarDyad::*;
9241 match op {
9242 Add => scan_flat(v, n, m, back, |a: f64, b: f64| block_f64(a + b)),
9243 Sub => scan_flat(v, n, m, back, |a: f64, b: f64| block_f64(a - b)),
9244 Mul => scan_flat(v, n, m, back, |a: f64, b: f64| block_f64(a * b)),
9245 Min => scan_flat(v, n, m, back, |a: f64, b: f64| (a.min(b), false)),
9246 Max => scan_flat(v, n, m, back, |a: f64, b: f64| (a.max(b), false)),
9247 _ => None,
9248 }
9249}
9250
9251fn scan_typed(op: ScalarDyad, d: &Data, n: usize, m: usize, back: bool) -> Option<Data> {
9255 use ScalarDyad::*;
9256 if !matches!(op, Add | Sub | Mul | Min | Max) {
9257 return None;
9258 }
9259 fn ints<S: Widen<i64> + Widen<f64>>(
9263 op: ScalarDyad,
9264 v: &[S],
9265 n: usize,
9266 m: usize,
9267 back: bool,
9268 ) -> Data {
9269 match scan_i64(op, v, n, m, back) {
9270 Some(out) => Data::I64(out.into()),
9271 None => Data::F64(
9272 scan_f64(op, v, n, m, back).expect("the float scan cannot overflow").into(),
9273 ),
9274 }
9275 }
9276 match d {
9277 Data::F64(v) => Some(Data::F64(scan_f64(op, v.as_slice(), n, m, back)?.into())),
9278 Data::Complex(v) => Some(Data::Complex(scan_cx(op, v, n, m, back)?.into())),
9279 Data::I64(v) => Some(ints(op, v.as_slice(), n, m, back)),
9280 Data::Bool(v) => Some(ints(op, v.as_slice(), n, m, back)),
9281 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
9284 }
9285}
9286
9287fn affine_step(u: &Verb) -> Option<&Array> {
9294 fn prim(v: &Verb, want: DyadOp, ranks: [i64; 3]) -> bool {
9298 matches!(v, Verb::Prim(p) if p.dyad == want && p.ranks == ranks)
9299 }
9300 const ATOMS: [i64; 3] = [0, 0, 0];
9301 const WHOLE: [i64; 3] = [RANK_INF; 3];
9302 fn scaled(v: &Verb) -> Option<&Array> {
9304 let Verb::NounFork(c, g, h) = v else { return None };
9305 let noun = c.rank() == 0
9306 && matches!(c.dtype(), DType::Bool | DType::I64 | DType::F64 | DType::Complex);
9307 let tree = prim(g, DyadOp::Scalar(ScalarDyad::Mul), ATOMS)
9308 && prim(h, DyadOp::Right, WHOLE);
9309 (noun && tree).then_some(c)
9310 }
9311 let Verb::Fork(f, g, h) = u else { return None };
9312 if !prim(g, DyadOp::Scalar(ScalarDyad::Add), ATOMS) {
9313 return None;
9314 }
9315 if prim(f, DyadOp::Left, WHOLE) {
9316 scaled(h)
9317 } else if prim(h, DyadOp::Left, WHOLE) {
9318 scaled(f)
9319 } else {
9320 None
9321 }
9322}
9323
9324struct Ring<T> {
9327 add: fn(T, T) -> T,
9328 mul: fn(T, T) -> T,
9329 one: T,
9330 finite: fn(T) -> bool,
9331}
9332
9333fn affine_flat<T>(v: &[T], c: T, n: usize, m: usize, back: bool, r: &Ring<T>) -> Option<Vec<T>>
9339where
9340 T: Copy + Default,
9341{
9342 let (add, mul) = (r.add, r.mul);
9343 let mut out = vec![T::default(); n * m];
9344 if back {
9345 out[(n - 1) * m..].copy_from_slice(&v[(n - 1) * m..n * m]);
9346 for i in (0..n - 1).rev() {
9347 for j in 0..m {
9348 out[i * m + j] = add(v[i * m + j], mul(c, out[(i + 1) * m + j]));
9349 }
9350 }
9351 } else {
9352 out[..m].copy_from_slice(&v[..m]);
9353 let mut pow = r.one;
9354 for i in 1..n {
9355 pow = mul(pow, c);
9356 if !(r.finite)(pow) {
9357 return None;
9358 }
9359 for j in 0..m {
9360 out[i * m + j] = add(out[(i - 1) * m + j], mul(pow, v[i * m + j]));
9361 }
9362 }
9363 }
9364 Some(out)
9365}
9366
9367fn affine_scan(c: &Array, y: &Array, back: bool) -> Option<Data> {
9377 let (n, m) = (y.items(), y.item_size());
9378 let machine = |t: DType| matches!(t, DType::Bool | DType::I64 | DType::F64 | DType::Complex);
9379 if n == 0 || !machine(c.dtype()) || !machine(y.dtype()) {
9380 return None;
9381 }
9382 match DType::promote(c.dtype(), y.dtype())? {
9383 DType::F64 => {
9384 let (mut tc, mut tv) = (Vec::new(), Vec::new());
9385 let k = *borrow_f64(&c.data, &mut tc).first()?;
9386 let v = borrow_f64(y.row_major_data(), &mut tv);
9387 let r = Ring { add: |a, b| a + b, mul: |a, b| a * b, one: 1.0, finite: f64::is_finite };
9388 Some(Data::F64(affine_flat(v, k, n, m, back, &r)?.into()))
9389 }
9390 DType::Complex => {
9391 let (mut tc, mut tv) = (Vec::new(), Vec::new());
9392 let k = *borrow_cx(&c.data, &mut tc).first()?;
9393 let v = borrow_cx(y.row_major_data(), &mut tv);
9394 let finite = |z: Cx| z[0].is_finite() && z[1].is_finite();
9395 let r = Ring { add: cx::add, mul: cx::mul, one: [1.0, 0.0], finite };
9396 Some(Data::Complex(affine_flat(v, k, n, m, back, &r)?.into()))
9397 }
9398 _ => None,
9399 }
9400}
9401
9402fn window_fold<S, T, F>(v: &[S], n: usize, m: usize, w: usize, step: F) -> Option<Vec<T>>
9417where
9418 S: Widen<T>,
9419 T: Copy + Default + Send + Sync,
9420 F: Fn(T, T) -> (T, bool) + Sync + Send,
9421{
9422 debug_assert!(w >= 1 && n >= w);
9423 if m == 1 {
9424 return window_fold_flat(v, n, w, step);
9425 }
9426 let count = n - w + 1;
9427 let mut out = vec![T::default(); count * m];
9428 let mut pre = vec![T::default(); w * m];
9431 let mut suf = vec![T::default(); w * m];
9432 let mut prev = vec![T::default(); w * m];
9433 let mut over = false;
9434 for b in 0..n.div_ceil(w) {
9435 let bs = b * w;
9436 let be = ((b + 1) * w).min(n);
9437 for (slot, &x) in pre[..m].iter_mut().zip(&v[bs * m..bs * m + m]) {
9438 *slot = x.widen();
9439 }
9440 for i in 1..be - bs {
9441 let (o, p) = (i * m, (i - 1) * m);
9442 for j in 0..m {
9443 let (r, f) = step(pre[p + j], v[(bs + i) * m + j].widen());
9444 pre[o + j] = r;
9445 over |= f;
9446 }
9447 }
9448 for e in bs.max(w - 1)..be {
9451 let i = e + 1 - w;
9452 let (oo, po) = (i * m, (e - bs) * m);
9453 if i == bs {
9454 out[oo..oo + m].copy_from_slice(&pre[po..po + m]);
9455 } else {
9456 let so = (i + w - bs) * m;
9457 for j in 0..m {
9458 let (r, f) = step(prev[so + j], pre[po + j]);
9459 out[oo + j] = r;
9460 over |= f;
9461 }
9462 }
9463 }
9464 let last = be - 1 - bs;
9465 for (slot, &x) in suf[last * m..last * m + m]
9466 .iter_mut()
9467 .zip(&v[(be - 1) * m..be * m])
9468 {
9469 *slot = x.widen();
9470 }
9471 for i in (0..last).rev() {
9472 let (o, p) = (i * m, (i + 1) * m);
9473 for j in 0..m {
9474 let (r, f) = step(v[(bs + i) * m + j].widen(), suf[p + j]);
9475 suf[o + j] = r;
9476 over |= f;
9477 }
9478 }
9479 std::mem::swap(&mut prev, &mut suf);
9480 }
9481 (!over).then_some(out)
9482}
9483
9484fn window_fold_flat<S, T, F>(v: &[S], n: usize, w: usize, step: F) -> Option<Vec<T>>
9494where
9495 S: Widen<T>,
9496 T: Copy + Default + Send + Sync,
9497 F: Fn(T, T) -> (T, bool) + Sync + Send,
9498{
9499 let (out, ok) = par::fill(n - w + 1, |lo, part: &mut [T]| {
9500 window_fold_range(v, n, w, lo, part, &step)
9501 });
9502 ok.then_some(out)
9503}
9504
9505#[inline(always)]
9506fn window_fold_range_body<S, T, F>(
9507 v: &[S],
9508 n: usize,
9509 w: usize,
9510 lo: usize,
9511 out: &mut [T],
9512 step: &F,
9513) -> bool
9514where
9515 S: Widen<T>,
9516 T: Copy + Default,
9517 F: Fn(T, T) -> (T, bool),
9518{
9519 if out.is_empty() {
9520 return true;
9521 }
9522 let hi = lo + out.len();
9523 let mut pre = vec![T::default(); w];
9524 let mut suf = vec![T::default(); w];
9525 let mut prev = vec![T::default(); w];
9526 let mut over = false;
9527 let mut bs = lo / w * w;
9528 while bs < n && bs <= hi + w - 2 {
9530 let block = &v[bs..(bs + w).min(n)];
9531 let lb = block.len();
9532 let mut acc = block[0].widen();
9533 pre[0] = acc;
9534 for (slot, &x) in pre[1..lb].iter_mut().zip(&block[1..]) {
9535 let (r, o) = step(acc, x.widen());
9536 acc = r;
9537 over |= o;
9538 *slot = acc;
9539 }
9540 for e in bs.max(lo + w - 1)..(bs + lb).min(hi + w - 1) {
9545 let i = e + 1 - w;
9546 out[i - lo] = if i == bs {
9547 pre[e - bs]
9548 } else {
9549 let (r, o) = step(prev[i + w - bs], pre[e - bs]);
9550 over |= o;
9551 r
9552 };
9553 }
9554 let mut acc = block[lb - 1].widen();
9555 suf[lb - 1] = acc;
9556 for (slot, &x) in suf[..lb - 1].iter_mut().zip(&block[..lb - 1]).rev() {
9557 let (r, o) = step(x.widen(), acc);
9558 acc = r;
9559 over |= o;
9560 *slot = acc;
9561 }
9562 std::mem::swap(&mut prev, &mut suf);
9563 bs += w;
9564 }
9565 !over
9566}
9567
9568multiversioned! {
9569 fn window_fold_range[S: Widen<T>, T: Copy + Default, F: Fn(T, T) -> (T, bool)](
9574 v: &[S],
9575 n: usize,
9576 w: usize,
9577 lo: usize,
9578 out: &mut [T],
9579 step: &F,
9580 ) -> bool = window_fold_range_body;
9581}
9582
9583pub(crate) fn windows_into<S, T, F>(v: &[S], w: usize, lo: usize, out: &mut [T], step: &F) -> bool
9592where
9593 S: Widen<T>,
9594 T: Copy + Default,
9595 F: Fn(T, T) -> (T, bool),
9596{
9597 window_fold_range(v, v.len(), w, lo, out, step)
9598}
9599
9600fn window_i64<S: Widen<i64>>(
9601 op: ScalarDyad,
9602 v: &[S],
9603 n: usize,
9604 m: usize,
9605 w: usize,
9606) -> Option<Vec<i64>> {
9607 use ScalarDyad::*;
9608 match op {
9609 Add => window_fold(v, n, m, w, i64::overflowing_add),
9610 Mul => window_fold(v, n, m, w, i64::overflowing_mul),
9611 Min => window_fold(v, n, m, w, |a: i64, b: i64| (a.min(b), false)),
9612 Max => window_fold(v, n, m, w, |a: i64, b: i64| (a.max(b), false)),
9613 _ => None,
9614 }
9615}
9616
9617fn window_cx(op: ScalarDyad, v: &[Cx], n: usize, m: usize, w: usize) -> Option<Vec<Cx>> {
9618 use ScalarDyad::*;
9619 match op {
9620 Add => window_fold(v, n, m, w, |a: Cx, b: Cx| (cx::add(a, b), false)),
9621 Mul => window_fold(v, n, m, w, |a: Cx, b: Cx| (cx::mul(a, b), false)),
9622 _ => None,
9623 }
9624}
9625
9626fn window_f64<S: Widen<f64>>(
9627 op: ScalarDyad,
9628 v: &[S],
9629 n: usize,
9630 m: usize,
9631 w: usize,
9632) -> Option<Vec<f64>> {
9633 use ScalarDyad::*;
9634 match op {
9635 Add => window_fold(v, n, m, w, |a: f64, b: f64| block_f64(a + b)),
9636 Mul => window_fold(v, n, m, w, |a: f64, b: f64| block_f64(a * b)),
9637 Min => window_fold(v, n, m, w, |a: f64, b: f64| (a.min(b), false)),
9638 Max => window_fold(v, n, m, w, |a: f64, b: f64| (a.max(b), false)),
9639 _ => None,
9640 }
9641}
9642
9643fn window_typed(op: ScalarDyad, d: &Data, n: usize, m: usize, w: usize) -> Option<Data> {
9647 use ScalarDyad::*;
9648 if !matches!(op, Add | Mul | Min | Max) {
9649 return None;
9650 }
9651 fn ints<S: Widen<i64> + Widen<f64>>(
9654 op: ScalarDyad,
9655 v: &[S],
9656 n: usize,
9657 m: usize,
9658 w: usize,
9659 ) -> Data {
9660 match window_i64(op, v, n, m, w) {
9661 Some(out) => Data::I64(out.into()),
9662 None => {
9663 Data::F64(window_f64(op, v, n, m, w).expect("the float fold cannot overflow").into())
9664 }
9665 }
9666 }
9667 match d {
9668 Data::F64(v) => Some(Data::F64(window_f64(op, v.as_slice(), n, m, w)?.into())),
9669 Data::Complex(v) => Some(Data::Complex(window_cx(op, v, n, m, w)?.into())),
9670 Data::I64(v) => Some(ints(op, v.as_slice(), n, m, w)),
9671 Data::Bool(v) => Some(ints(op, v.as_slice(), n, m, w)),
9672 Data::Ext(_) | Data::Rat(_) | Data::Char(_) | Data::Symbol(_) | Data::Box(_) => None,
9675 }
9676}
9677
9678fn runs(u: &Verb, y: &Array, back: bool, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
9680 let promoted = as_items(y);
9681 let base = promoted.as_ref().unwrap_or(y);
9682 let n = base.items();
9683 let m = base.item_size();
9684 if n == 0 {
9690 if ctx.cfg.rules.lang == crate::Lang::Apl {
9691 return Ok(Array::new(base.shape.clone(), Data::empty(base.dtype())));
9692 }
9693 let cell = u.is_pure().then(|| base.clone());
9694 return Ok(empty_frame(&[0], base.dtype(), cell, ctx, |cell, c| u.monad(cell, c, span)));
9695 }
9696 if n > 0 && base.dtype().is_numeric() && let Some(op) = folded_op(u) {
9697 if (back || is_associative(op))
9700 && let Some(d) = scan_typed(op, base.row_major_data(), n, m, back)
9701 {
9702 return Ok(Array::new(base.shape.clone(), d));
9703 }
9704 }
9705 if n > 0 && let Verb::Reduce(inner) = u {
9706 if base.dtype().is_numeric()
9707 && let Some(c) = affine_step(inner)
9708 && let Some(d) = affine_scan(c, base, back)
9709 {
9710 return Ok(Array::new(base.shape.clone(), d));
9711 }
9712 if back && u.is_pure() {
9719 let mut acc = base.item(n - 1);
9720 let mut cells = Vec::with_capacity(n);
9721 cells.push(acc.clone());
9722 for i in (0..n - 1).rev() {
9723 acc = inner.dyad(&base.item(i), &acc, ctx, span)?;
9724 cells.push(acc.clone());
9725 }
9726 cells.reverse();
9727 return assemble(&[n], cells, span);
9728 }
9729 }
9730 let apl = ctx.cfg.rules.lang == crate::Lang::Apl;
9731 let cells = each_cell(n, n * m, u.is_pure(), ctx, |i, c| {
9732 let part = if back { section(base, i, n) } else { section(base, 0, i + 1) };
9733 u.monad(&part, c, span)
9734 })?;
9735 if apl { assemble_items(&[n], cells, span) } else { assemble(&[n], cells, span) }
9736}
9737
9738fn empty_windows(u: &Verb, y: &Array, w: usize, ctx: &mut Ctx<'_>, span: Span) -> Array {
9743 let m = y.item_size();
9744 if u.is_pure() && let Some(cells) = w.checked_mul(m).filter(|&s| s <= 1 << 20) {
9745 let mut shape = y.shape.clone();
9746 shape[0] = w;
9747 let probe = Array::new(shape, fill_data(y.dtype(), cells));
9748 if let Ok(cell) = u.monad(&probe, ctx, span) {
9749 let mut shape = vec![0usize];
9750 shape.extend_from_slice(&cell.shape);
9751 return Array::new(shape, Data::empty(cell.dtype()));
9752 }
9753 }
9754 Array::new(vec![0], Data::empty(DType::I64))
9755}
9756
9757fn window_size(x: &Array, near: NearInt, span: Span) -> Result<i64> {
9759 let v = x
9760 .to_i64_vec_near(near)
9761 .ok_or_else(|| Error::domain("the window size must be an integer", span))?;
9762 match v.as_slice() {
9763 [k] => Ok(*k),
9764 _ => Err(Error::new(
9765 ErrorKind::Length,
9766 "the window size must be a single number",
9767 Some(span),
9768 )),
9769 }
9770}
9771
9772fn infix(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
9780 let k = window_size(x, ctx.cfg.near(), span)?;
9781 let promoted = as_items(y);
9782 let base = promoted.as_ref().unwrap_or(y);
9783 let n = base.items();
9784 let m = base.item_size();
9785 if k < 0 {
9786 let w = k.unsigned_abs() as usize;
9787 let count = n.div_ceil(w);
9788 let cells = each_cell(count, n * m, u.is_pure(), ctx, |i, c| {
9789 u.monad(§ion(base, i * w, ((i + 1) * w).min(n)), c, span)
9790 })?;
9791 return assemble(&[count], cells, span);
9792 }
9793 let w = k as usize;
9794 if n < w {
9795 return Ok(empty_windows(u, base, w, ctx, span));
9796 }
9797 let count = n - w + 1;
9798 if w > 0 && base.dtype().is_numeric()
9799 && let Some(op) = folded_op(u) && let Some(d) = window_typed(op, &base.data, n, m, w)
9800 {
9801 let mut shape = base.shape.clone();
9802 shape[0] = count;
9803 return Ok(Array::new(shape, d));
9804 }
9805 let work = count.saturating_mul(w).saturating_mul(m);
9806 let cells = each_cell(count, work, u.is_pure(), ctx, |i, c| {
9807 u.monad(§ion(base, i, i + w), c, span)
9808 })?;
9809 assemble(&[count], cells, span)
9810}
9811
9812fn nwise(f: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
9825 if x.count() != 1 {
9829 return Err(Error::new(
9830 ErrorKind::Length,
9831 "the window size must be a single number",
9832 Some(span),
9833 ));
9834 }
9835 let k = window_size(x, ctx.cfg.near(), span)?;
9836 let promoted = as_items(y);
9837 if promoted.is_some() && k.unsigned_abs() == 1 {
9841 return Ok(y.clone());
9842 }
9843 let base = promoted.as_ref().unwrap_or(y).to_row_major();
9844 let base = &base;
9845 let n = base.items();
9846 let m = base.item_size();
9847 let w = k.unsigned_abs() as usize;
9848 if w > n + 1 {
9849 return Err(Error::domain(
9850 format!("a window of {w} does not fit an axis of {n}"),
9851 span,
9852 ));
9853 }
9854 let count = n + 1 - w;
9855 let fold = Verb::Reduce(Box::new(f.clone()));
9856 if count == 0 {
9857 return Ok(empty_windows(&fold, base, w, ctx, span));
9858 }
9859 if w > 0
9864 && base.dtype().is_numeric()
9865 && let Some(op) = scalar_dyad_of(f)
9866 && let Some(d) = window_typed(op, base.row_major_data(), n, m, w)
9867 {
9868 let mut shape = base.shape.clone();
9869 shape[0] = count;
9870 return Ok(Array::new(shape, d));
9871 }
9872 let work = count.saturating_mul(w.max(1)).saturating_mul(m);
9873 let cells = each_cell(count, work, f.is_pure(), ctx, |i, c| {
9874 let win = section(base, i, i + w);
9875 let win = if k < 0 { reverse(&win) } else { win };
9876 fold.monad(&win, c, span)
9877 })?;
9878 assemble(&[count], cells, span)
9879}
9880
9881fn power(
9884 u: &Verb,
9885 p: Power,
9886 x: Option<&Array>,
9887 y: &Array,
9888 ctx: &mut Ctx<'_>,
9889 span: Span,
9890) -> Result<Array> {
9891 let step = |acc: &Array, c: &mut Ctx<'_>| match x {
9892 Some(x) => u.dyad(x, acc, c, span),
9893 None => u.monad(acc, c, span),
9894 };
9895 match p {
9896 Power::Times(n) => {
9897 let mut acc = y.clone();
9898 for _ in 0..n {
9899 acc = step(&acc, ctx)?;
9900 }
9901 Ok(acc)
9902 }
9903 Power::Converge => {
9904 let mut acc = y.clone();
9905 for _ in 0..CONVERGE_LIMIT {
9906 let next = step(&acc, ctx)?;
9907 if arrays_match(&next, &acc, ctx.cfg.tol) {
9908 return Ok(next);
9909 }
9910 acc = next;
9911 }
9912 Err(Error::domain("the iteration did not converge", span))
9913 }
9914 Power::Each(ref counts) => {
9918 let mut acc = y.clone();
9919 let mut done = 0u64;
9920 let mut order: Vec<usize> = (0..counts.len()).collect();
9921 order.sort_by_key(|&i| counts[i]);
9922 let mut cells: Vec<Option<Array>> = vec![None; counts.len()];
9923 for i in order {
9924 while done < counts[i] {
9925 acc = step(&acc, ctx)?;
9926 done += 1;
9927 }
9928 cells[i] = Some(acc.clone());
9929 }
9930 let cells: Vec<Array> = cells.into_iter().map(|c| c.expect("every count filled")).collect();
9931 assemble(&[cells.len()], cells, span)
9932 }
9933 Power::ConvergeTrace => {
9934 let mut acc = y.clone();
9935 let mut cells = vec![acc.clone()];
9936 for _ in 0..CONVERGE_LIMIT {
9937 let next = step(&acc, ctx)?;
9938 if arrays_match(&next, &acc, ctx.cfg.tol) {
9939 return assemble(&[cells.len()], cells, span);
9940 }
9941 cells.push(next.clone());
9942 acc = next;
9943 }
9944 Err(Error::domain("the iteration did not converge", span))
9945 }
9946 }
9947}
9948
9949fn power_v(
9952 u: &Verb,
9953 v: &Verb,
9954 x: Option<&Array>,
9955 y: &Array,
9956 ctx: &mut Ctx<'_>,
9957 span: Span,
9958) -> Result<Array> {
9959 let count = match x {
9960 Some(x) => v.dyad(x, y, ctx, span)?,
9961 None => v.monad(y, ctx, span)?,
9962 };
9963 let n = count
9964 .to_i64_vec_near(ctx.cfg.near())
9965 .ok_or_else(|| Error::domain("the power count must be an integer", span))?;
9966 if n.len() != 1 {
9967 return Err(Error::not_yet("a list of power counts (u^:v with several)", span));
9968 }
9969 let n = n[0];
9970 if n < 0 {
9971 return Err(Error::not_yet("a negative power (the verb's inverse)", span));
9972 }
9973 power(u, Power::Times(n as u64), x, y, ctx, span)
9974}
9975
9976fn power_until(
9978 u: &Verb,
9979 test: &Verb,
9980 y: &Array,
9981 ctx: &mut Ctx<'_>,
9982 span: Span,
9983) -> Result<Array> {
9984 let mut acc = y.clone();
9985 for _ in 0..CONVERGE_LIMIT {
9986 let next = u.monad(&acc, ctx, span)?;
9987 let done = test.dyad(&next, &acc, ctx, span)?;
9988 let stop = done
9989 .to_f64_vec()
9990 .ok_or_else(|| Error::domain("the ⍣ test must answer with numbers", span))?;
9991 if !stop.is_empty() && stop.iter().all(|&v| v != 0.0) {
9992 return Ok(next);
9993 }
9994 acc = next;
9995 }
9996 Err(Error::domain("the iteration did not converge", span))
9997}
9998
9999fn along_axis(
10006 u: &Verb,
10007 x: Option<&Array>,
10008 y: &Array,
10009 k: usize,
10010 ctx: &mut Ctx<'_>,
10011 span: Span,
10012) -> Result<Array> {
10013 if k >= y.rank().max(1) {
10014 return Err(Error::new(
10015 ErrorKind::Rank,
10016 format!("axis {k} does not exist on an argument of rank {}", y.rank()),
10017 Some(span),
10018 ));
10019 }
10020 let moved = axis_to_front(y, k);
10021 let r = moved.rank();
10022 let out = match x {
10023 Some(x) => u.dyad(x, &moved, ctx, span)?,
10024 None => u.monad(&moved, ctx, span)?,
10025 };
10026 if out.rank() == r {
10027 return Ok(front_to_axis(&out, k));
10028 }
10029 Ok(out)
10030}
10031
10032fn where_indices(y: &Array, origin: i64, boxed: bool, near: NearInt, span: Span) -> Result<Array> {
10040 let counts = y
10041 .to_i64_vec_near(near)
10042 .ok_or_else(|| Error::domain("indices needs non-negative integers", span))?;
10043 if counts.iter().any(|&c| c < 0) {
10044 return Err(Error::domain("indices needs non-negative integers", span));
10045 }
10046 if !boxed || y.rank() < 2 {
10047 let mut out = Vec::new();
10048 for (i, &c) in counts.iter().enumerate() {
10049 for _ in 0..c {
10050 out.push(origin + i as i64);
10051 }
10052 }
10053 return Ok(Array::from_i64(out));
10054 }
10055 let r = y.rank();
10056 let mut coord = vec![0usize; r];
10057 let mut out: Vec<Array> = Vec::new();
10058 for &c in &counts {
10059 if c > 0 {
10060 let point =
10061 Array::from_i64(coord.iter().map(|&k| origin + k as i64).collect::<Vec<_>>());
10062 for _ in 0..c {
10063 out.push(point.clone());
10064 }
10065 }
10066 odometer(&mut coord, &y.shape);
10067 }
10068 Ok(Array::new(vec![out.len()], Data::Box(out.into())))
10069}
10070
10071fn indices_inverse(y: &Array, near: NearInt, span: Span) -> Result<Array> {
10075 if y.count() == 0 {
10076 return Ok(Array::empty(DType::I64));
10077 }
10078 let at = y
10079 .to_i64_vec_near(near)
10080 .ok_or_else(|| Error::domain("the obverse of indices needs integers", span))?;
10081 if at.iter().any(|&i| i < 0) {
10082 return Err(Error::domain("the obverse of indices needs non-negative integers", span));
10083 }
10084 let Some(&top) = at.iter().max() else {
10085 return Ok(Array::empty(DType::I64));
10086 };
10087 let mut counts = vec![0i64; top as usize + 1];
10088 for &i in &at {
10089 counts[i as usize] += 1;
10090 }
10091 Ok(Array::from_i64(counts))
10092}
10093
10094fn interval_index(
10100 x: &Array,
10101 y: &Array,
10102 offset: i64,
10103 closed: bool,
10104 tol: Tol,
10105 ord: Grading,
10106 span: Span,
10107) -> Result<Array> {
10108 if !x.dtype().is_numeric() || !y.dtype().is_numeric() {
10111 return ordered_interval_index(x, y, offset, closed, ord, span);
10112 }
10113 let bounds = x
10114 .to_f64_vec()
10115 .ok_or_else(|| Error::domain("interval index needs numeric bounds", span))?;
10116 let vals = y
10117 .to_f64_vec()
10118 .ok_or_else(|| Error::domain("interval index needs numeric values", span))?;
10119 let out: Vec<i64> = vals
10120 .iter()
10121 .map(|&v| {
10122 let count =
10125 bounds.iter().filter(|&&b| if closed { !tol.lt(v, b) } else { tol.lt(b, v) });
10126 offset + count.count() as i64
10127 })
10128 .collect();
10129 Ok(Array::new(y.shape.clone(), Data::I64(out.into())))
10130}
10131
10132fn ordered_interval_index(
10136 x: &Array,
10137 y: &Array,
10138 offset: i64,
10139 closed: bool,
10140 ord: Grading,
10141 span: Span,
10142) -> Result<Array> {
10143 let (xr, yr) = (x.to_row_major(), y.to_row_major());
10144 let (bounds, vals) = (&xr.data, &yr.data);
10145 let cmp = |i: usize, j: usize| -> Option<std::cmp::Ordering> {
10146 match (bounds, vals) {
10147 (Data::Char(p), Data::Char(q)) => Some(p[i].cmp(&q[j])),
10148 (Data::Symbol(p), Data::Symbol(q)) => Some(crate::symbol::cmp(p[i], q[j])),
10149 (Data::Box(p), Data::Box(q)) if ord.tao == Tao::J => {
10154 Some(cmp_items_total(&p[i], &q[j], ord))
10155 }
10156 _ => None,
10157 }
10158 };
10159 let mut out = Vec::with_capacity(y.count());
10160 for j in 0..y.count() {
10161 let mut count = 0i64;
10162 for i in 0..x.count() {
10163 let ord = cmp(i, j).ok_or_else(|| {
10164 Error::domain(
10165 format!(
10166 "interval index compares {} bounds with {} values",
10167 x.dtype().name(),
10168 y.dtype().name()
10169 ),
10170 span,
10171 )
10172 })?;
10173 count += i64::from(if closed { ord.is_le() } else { ord.is_lt() });
10175 }
10176 out.push(offset + count);
10177 }
10178 Ok(Array::new(y.shape.clone(), Data::I64(out.into())))
10179}
10180
10181fn steps(y: &Array, span: Span) -> Result<Array> {
10184 let vals = y.to_f64_vec().ok_or_else(|| Error::domain("steps needs a number", span))?;
10185 let v = match vals.first() {
10186 Some(&v) if v.is_finite() => v,
10187 _ => return Err(Error::domain("steps needs a finite number", span)),
10188 };
10189 let n = (2.0 * v.abs()).floor();
10190 if n > 1e7 {
10191 return Err(Error::domain("steps would produce too many items", span));
10192 }
10193 let n = n as i64 + 1;
10194 let step = if v < 0.0 { -1.0 } else { 1.0 };
10195 let start = -v;
10196 if v.fract() == 0.0 {
10197 let start = start as i64;
10198 let step = step as i64;
10199 return Ok(Array::from_i64((0..n).map(|k| start + k * step).collect()));
10200 }
10201 Ok(Array::from_f64((0..n).map(|k| start + k as f64 * step).collect()))
10202}
10203
10204fn index_of_last(x: &Array, y: &Array, origin: i64, tol: Tol) -> Array {
10206 let cell_rank = x.rank().saturating_sub(1).min(y.rank());
10207 let frame_rank = y.rank() - cell_rank;
10208 let frame: Vec<usize> = y.shape[..frame_rank].to_vec();
10209 let nf: usize = frame.iter().product();
10210 let items = x.items();
10211 let mut out = Vec::with_capacity(nf);
10212 for i in 0..nf {
10213 let cell = y.cell_at(frame_rank, i);
10214 let at = (0..items)
10215 .rev()
10216 .find(|&j| arrays_match(&cell, &item_or_self(x, j), tol))
10217 .unwrap_or(items);
10218 out.push(origin + at as i64);
10219 }
10220 Array::new(frame, Data::I64(out.into()))
10221}
10222
10223fn roll(
10230 y: &Array,
10231 origin: i64,
10232 fixed: bool,
10233 float_at_zero: bool,
10234 near: NearInt,
10235 span: Span,
10236) -> Result<Array> {
10237 let bounds = y
10238 .to_i64_vec_near(near)
10239 .ok_or_else(|| Error::domain("roll needs whole numbers", span))?;
10240 if bounds.iter().any(|&b| b < 0) {
10241 return Err(Error::domain("roll needs non-negative numbers", span));
10242 }
10243 if !float_at_zero && bounds.contains(&0) {
10244 return Err(Error::domain("? 0 has no value: the range is empty", span));
10245 }
10246 let any_zero = bounds.contains(&0);
10248 crate::rng::with(fixed, |g| {
10249 if any_zero {
10250 let out: Vec<f64> = bounds
10251 .iter()
10252 .map(|&b| {
10253 if b == 0 {
10254 g.unit()
10255 } else {
10256 (origin + g.below(b as u64) as i64) as f64
10257 }
10258 })
10259 .collect();
10260 return Ok(Array::new(y.shape.clone(), Data::F64(out.into())));
10261 }
10262 let out: Vec<i64> =
10263 bounds.iter().map(|&b| origin + g.below(b as u64) as i64).collect();
10264 Ok(Array::new(y.shape.clone(), Data::I64(out.into())))
10265 })
10266}
10267
10268fn deal(
10270 x: &Array,
10271 y: &Array,
10272 origin: i64,
10273 fixed: bool,
10274 near: NearInt,
10275 span: Span,
10276) -> Result<Array> {
10277 let want = one_whole(x, "the count dealt", near, span)?;
10278 let from = one_whole(y, "the range dealt from", near, span)?;
10279 if want < 0 || from < 0 {
10280 return Err(Error::domain("deal needs non-negative numbers", span));
10281 }
10282 if want > from {
10283 return Err(Error::domain(
10284 format!("cannot deal {want} distinct value(s) from {from}"),
10285 span,
10286 ));
10287 }
10288 if want == 0 {
10289 return Ok(Array::from_i64(Vec::new()));
10290 }
10291 let drawn = crate::rng::with(fixed, |g| g.deal(want as usize, from as u64));
10292 Ok(Array::from_i64(drawn.into_iter().map(|v| v + origin).collect()))
10293}
10294
10295fn one_whole(a: &Array, what: &str, near: NearInt, span: Span) -> Result<i64> {
10297 let v = a
10298 .to_i64_vec_near(near)
10299 .ok_or_else(|| Error::domain(format!("{what} must be a whole number"), span))?;
10300 match v[..] {
10301 [n] => Ok(n),
10302 _ => Err(Error::new(
10303 ErrorKind::Rank,
10304 format!("{what} must be one number"),
10305 Some(span),
10306 )),
10307 }
10308}
10309
10310fn nth_prime(n: i64, span: Span) -> Result<i64> {
10314 if n < 0 {
10315 return Err(Error::domain("the prime index must not be negative", span));
10316 }
10317 const LIMIT: i64 = 5_000_000;
10318 if n >= LIMIT {
10319 return Err(Error::domain(
10320 format!("prime index {n} is beyond the {LIMIT}th prime"),
10321 span,
10322 ));
10323 }
10324 let k = (n + 1) as f64;
10327 let bound = if n < 6 { 15.0 } else { k * (k.ln() + k.ln().ln()) };
10328 let bound = bound.ceil() as usize + 1;
10329 let mut sieve = vec![true; bound + 1];
10330 sieve[0] = false;
10331 if bound >= 1 {
10332 sieve[1] = false;
10333 }
10334 let mut p = 2usize;
10335 while p * p <= bound {
10336 if sieve[p] {
10337 let mut q = p * p;
10338 while q <= bound {
10339 sieve[q] = false;
10340 q += p;
10341 }
10342 }
10343 p += 1;
10344 }
10345 let mut seen = 0i64;
10346 for (v, &is_p) in sieve.iter().enumerate() {
10347 if is_p {
10348 if seen == n {
10349 return Ok(v as i64);
10350 }
10351 seen += 1;
10352 }
10353 }
10354 Err(Error::internal("the prime sieve was too small"))
10355}
10356
10357fn prime_factors(n: i64, span: Span) -> Result<Vec<i64>> {
10359 if n < 1 {
10360 return Err(Error::domain("prime factors need a positive integer", span));
10361 }
10362 let mut out = Vec::new();
10363 let mut m = n;
10364 let mut d = 2i64;
10365 while d.saturating_mul(d) <= m {
10366 while m % d == 0 {
10367 out.push(d);
10368 m /= d;
10369 }
10370 d += if d == 2 { 1 } else { 2 };
10371 }
10372 if m > 1 {
10373 out.push(m);
10374 }
10375 Ok(out)
10376}
10377
10378fn lstsq(a: &[f64], m: usize, n: usize, b: &[f64], k: usize) -> Option<Vec<f64>> {
10386 let mut r = a.to_vec();
10388 let mut c = b.to_vec();
10389 let at = |i: usize, j: usize, w: usize| i * w + j;
10390 let scale = a.iter().fold(0.0f64, |acc, v| acc.max(v.abs()));
10391 if scale == 0.0 {
10392 return None;
10393 }
10394 for j in 0..n {
10395 let norm = (j..m).map(|i| r[at(i, j, n)] * r[at(i, j, n)]).sum::<f64>().sqrt();
10397 if norm <= 1e-13 * scale {
10398 return None;
10399 }
10400 let alpha = if r[at(j, j, n)] > 0.0 { -norm } else { norm };
10401 let mut v = vec![0.0f64; m];
10402 for i in j..m {
10403 v[i] = r[at(i, j, n)];
10404 }
10405 v[j] -= alpha;
10406 let vnorm2: f64 = (j..m).map(|i| v[i] * v[i]).sum();
10407 if vnorm2 > 0.0 {
10408 for col in j..n {
10409 let dot: f64 = (j..m).map(|i| v[i] * r[at(i, col, n)]).sum();
10410 let f = 2.0 * dot / vnorm2;
10411 for i in j..m {
10412 r[at(i, col, n)] -= f * v[i];
10413 }
10414 }
10415 for col in 0..k {
10416 let dot: f64 = (j..m).map(|i| v[i] * c[at(i, col, k)]).sum();
10417 let f = 2.0 * dot / vnorm2;
10418 for i in j..m {
10419 c[at(i, col, k)] -= f * v[i];
10420 }
10421 }
10422 }
10423 }
10424 let mut x = vec![0.0f64; n * k];
10426 for col in 0..k {
10427 for i in (0..n).rev() {
10428 let mut acc = c[at(i, col, k)];
10429 for j in i + 1..n {
10430 acc -= r[at(i, j, n)] * x[at(j, col, k)];
10431 }
10432 let d = r[at(i, i, n)];
10433 if d.abs() <= 1e-13 * scale {
10434 return None;
10435 }
10436 x[at(i, col, k)] = acc / d;
10437 }
10438 }
10439 Some(x)
10440}
10441
10442fn as_matrix(a: &Array, span: Span) -> Result<(Vec<f64>, usize, usize)> {
10445 let v = a
10446 .to_f64_vec()
10447 .ok_or_else(|| Error::domain("matrix division needs numeric data", span))?;
10448 match a.rank() {
10449 0 => Ok((v, 1, 1)),
10450 1 => {
10451 let m = a.shape[0];
10452 Ok((v, m, 1))
10453 }
10454 2 => Ok((v, a.shape[0], a.shape[1])),
10455 _ => Err(Error::new(
10456 ErrorKind::Rank,
10457 "matrix division needs an argument of rank 2 or less",
10458 Some(span),
10459 )),
10460 }
10461}
10462
10463fn matrix_inverse(y: &Array, span: Span) -> Result<Array> {
10467 let (a, m, n) = as_matrix(y, span)?;
10468 if m < n {
10469 return Err(Error::new(
10470 ErrorKind::Length,
10471 format!("cannot invert a {m} by {n} matrix: it has more columns than rows"),
10472 Some(span),
10473 ));
10474 }
10475 let mut eye = vec![0.0f64; m * m];
10476 for i in 0..m {
10477 eye[i * m + i] = 1.0;
10478 }
10479 let x = lstsq(&a, m, n, &eye, m)
10480 .ok_or_else(|| Error::domain("the matrix is singular", span))?;
10481 let shape = if y.rank() == 2 { vec![n, m] } else { y.shape.clone() };
10484 Ok(Array::new(shape, Data::F64(x.into())))
10485}
10486
10487fn matrix_divide(x: &Array, y: &Array, span: Span) -> Result<Array> {
10489 let (a, m, n) = as_matrix(y, span)?;
10490 let (b, bm, k) = as_matrix(x, span)?;
10491 if bm != m {
10492 return Err(Error::new(
10493 ErrorKind::Length,
10494 format!("the system has {m} rows but the right-hand side has {bm}"),
10495 Some(span),
10496 ));
10497 }
10498 if m < n {
10499 return Err(Error::new(
10500 ErrorKind::Length,
10501 format!("the {m} by {n} system is underdetermined"),
10502 Some(span),
10503 ));
10504 }
10505 let sol = lstsq(&a, m, n, &b, k)
10506 .ok_or_else(|| Error::domain("the system is singular", span))?;
10507 let shape = if x.rank() == 2 { vec![n, k] } else { vec![n] };
10510 Ok(Array::new(shape, Data::F64(sol.into())))
10511}
10512
10513fn squad(x: &Array, y: &Array, origin: i64, leading: bool, near: NearInt, span: Span) -> Result<Array> {
10517 if x.rank() > 1 {
10518 return Err(Error::new(
10519 ErrorKind::Rank,
10520 "the index of ⌷ must be a scalar or a vector",
10521 Some(span),
10522 ));
10523 }
10524 let items: Vec<Array> = if x.rank() == 0 { vec![x.clone()] } else { x.cells(1) };
10529 let named = items.len();
10530 if named > y.rank() || (!leading && named != y.rank()) {
10531 return Err(Error::new(
10532 ErrorKind::Rank,
10533 format!("{} index(es) for an argument of rank {}", named, y.rank()),
10534 Some(span),
10535 ));
10536 }
10537 let mut specs = Vec::with_capacity(items.len());
10538 let mut shape = Vec::new();
10539 for (k, item) in items.iter().enumerate() {
10540 let spec = match item.as_boxes() {
10541 Some(bs) if item.rank() == 0 => bs[0].clone(),
10542 _ => item.clone(),
10543 };
10544 let idx = spec
10545 .to_i64_vec_near(near)
10546 .ok_or_else(|| Error::domain("index must be an integer", span))?;
10547 for &i in &idx {
10548 let j = i - origin;
10549 if j < 0 || j as usize >= y.shape[k] {
10550 return Err(Error::domain(
10551 format!("index {i} is out of range on axis {k}"),
10552 span,
10553 ));
10554 }
10555 }
10556 shape.extend_from_slice(&spec.shape);
10557 specs.push((spec.shape.clone(), idx));
10558 }
10559 for k in named..y.rank() {
10562 let n = y.shape[k];
10563 shape.push(n);
10564 specs.push((vec![n], (0..n as i64).map(|i| i + origin).collect()));
10565 }
10566 let y = y.to_row_major();
10567 let st = strides(&y.shape);
10568 let total: usize = shape.iter().product();
10569 let mut data = Data::empty(y.dtype());
10570 let mut coord = vec![0usize; shape.len()];
10571 for _ in 0..total {
10572 let mut at = 0usize;
10573 let mut used = 0usize;
10574 for (k, (sshape, idx)) in specs.iter().enumerate() {
10575 let sst = strides(sshape);
10576 let pick: usize = (0..sshape.len()).map(|a| coord[used + a] * sst[a]).sum();
10577 used += sshape.len();
10578 at += (idx[pick] - origin) as usize * st[k];
10579 }
10580 push_elem(&mut data, y.row_major_data(), at);
10581 odometer(&mut coord, &shape);
10582 }
10583 Ok(Array::new(shape, data))
10584}
10585
10586fn select_axis(
10593 x: &Array,
10594 y: &Array,
10595 axis: usize,
10596 rank: usize,
10597 origin: i64,
10598 near: NearInt,
10599 span: Span,
10600) -> Result<Array> {
10601 if rank != 0 && y.rank() != rank {
10602 return Err(Error::new(
10603 ErrorKind::Rank,
10604 format!("{rank} index slot(s) for an argument of rank {}", y.rank()),
10605 Some(span),
10606 ));
10607 }
10608 if axis >= y.rank() {
10609 return Err(Error::new(
10610 ErrorKind::Rank,
10611 format!("axis {axis} does not exist on an argument of rank {}", y.rank()),
10612 Some(span),
10613 ));
10614 }
10615 let idx = x
10616 .to_i64_vec_near(near)
10617 .ok_or_else(|| Error::domain("index must be an integer", span))?;
10618 let len = y.shape[axis];
10619 let mut picks = Vec::with_capacity(idx.len());
10620 for &i in &idx {
10621 let j = i - origin;
10622 if j < 0 || j as usize >= len {
10623 return Err(Error::domain(
10624 format!("index {i} is out of range: axis {axis} has {len} items"),
10625 span,
10626 ));
10627 }
10628 picks.push(j as usize);
10629 }
10630 let mut shape = Vec::with_capacity(y.rank() + x.rank());
10631 shape.extend_from_slice(&y.shape[..axis]);
10632 shape.extend_from_slice(&x.shape);
10633 shape.extend_from_slice(&y.shape[axis + 1..]);
10634 let outer: usize = y.shape[..axis].iter().product();
10635 let inner: usize = y.shape[axis + 1..].iter().product();
10636 let mut data = Data::empty(y.dtype());
10637 for o in 0..outer {
10638 for &p in &picks {
10639 let base = (o * len + p) * inner;
10640 for e in 0..inner {
10641 push_elem(&mut data, &y.data, base + e);
10642 }
10643 }
10644 }
10645 Ok(Array::new(shape, data))
10646}
10647
10648fn amend(m: &Array, x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
10652 if y.rank() == 0 {
10653 return Err(Error::new(ErrorKind::Rank, "cannot amend a scalar", Some(span)));
10654 }
10655 if let Some(spec) = m.as_boxes().and_then(<[Array]>::first) {
10657 let spec = index_spec(spec, y, near, span)?;
10658 return amend_spec(&spec, x, y, span);
10659 }
10660 let idx = m
10661 .to_i64_vec_near(near)
10662 .ok_or_else(|| Error::domain("amend indices must be integers", span))?;
10663 let items = y.items() as i64;
10664 let mut at = Vec::with_capacity(idx.len());
10665 for &i in &idx {
10666 let k = if i < 0 { i + items } else { i };
10667 if k < 0 || k >= items {
10668 return Err(Error::domain(
10669 format!("index {i} is out of range: the argument has {items} items"),
10670 span,
10671 ));
10672 }
10673 at.push(k as usize);
10674 }
10675 let cell = y.item_size();
10676 let per_index = if x.count() == cell {
10677 false
10678 } else if x.count() == cell * at.len() {
10679 true
10680 } else {
10681 return Err(Error::new(
10682 ErrorKind::Length,
10683 format!(
10684 "cannot amend {} item(s) of {} element(s) each with {} element(s)",
10685 at.len(),
10686 cell,
10687 x.count()
10688 ),
10689 Some(span),
10690 ));
10691 };
10692 let Some(t) = DType::promote(x.dtype(), y.dtype()) else {
10695 return Err(Error::new(
10696 ErrorKind::Type,
10697 "the replacement and the argument hold different kinds of value",
10698 Some(span),
10699 ));
10700 };
10701 let (Some(src), Some(base)) = (x.data.cast(t), y.data.cast(t)) else {
10702 return Err(Error::new(
10703 ErrorKind::Type,
10704 "the replacement and the argument hold different kinds of value",
10705 Some(span),
10706 ));
10707 };
10708 let mut data = Data::empty(t);
10710 let mut plan: Vec<Option<usize>> = vec![None; y.items()];
10711 for (n, &k) in at.iter().enumerate() {
10712 plan[k] = Some(if per_index { n } else { 0 });
10713 }
10714 for (i, slot) in plan.iter().enumerate() {
10715 match slot {
10716 Some(n) => {
10717 for e in 0..cell {
10718 push_elem(&mut data, &src, n * cell + e);
10719 }
10720 }
10721 None => {
10722 for e in 0..cell {
10723 push_elem(&mut data, &base, i * cell + e);
10724 }
10725 }
10726 }
10727 }
10728 Ok(Array::new(y.shape.clone(), data))
10729}
10730
10731fn fetch(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
10736 let steps: Vec<Array> = match x.as_boxes() {
10737 Some(bs) => bs.to_vec(),
10738 None => vec![x.clone()],
10739 };
10740 let mut cur = y.clone();
10741 for step in steps {
10742 let idx = if step.count() == 0 {
10745 Vec::new()
10746 } else {
10747 step.to_i64_vec_near(near)
10748 .ok_or_else(|| Error::domain("a fetch path holds integers", span))?
10749 };
10750 let base =
10752 if cur.rank() == 0 { Array::new(vec![1], cur.data.clone()) } else { cur.clone() };
10753 if idx.len() > base.rank() {
10754 return Err(Error::new(
10755 ErrorKind::Length,
10756 format!(
10757 "a path step of {} index(es) into a value of rank {}",
10758 idx.len(),
10759 cur.rank()
10760 ),
10761 Some(span),
10762 ));
10763 }
10764 let at = cell_index(&base, &idx, span)?;
10765 cur = open_cell(&base.cell_at(idx.len(), at));
10766 }
10767 Ok(cur)
10768}
10769
10770fn cell_index(y: &Array, idx: &[i64], span: Span) -> Result<usize> {
10772 let mut at = 0usize;
10773 for (k, &i) in idx.iter().enumerate() {
10774 let len = y.shape[k] as i64;
10775 let j = if i < 0 { i + len } else { i };
10776 if j < 0 || j >= len {
10777 return Err(Error::domain(
10778 format!("index {i} is out of range: axis {k} has {len} items"),
10779 span,
10780 ));
10781 }
10782 at = at * y.shape[k] + j as usize;
10783 }
10784 Ok(at)
10785}
10786
10787fn partition_counts(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
10803 if y.rank() == 0 {
10804 return Err(Error::new(
10805 ErrorKind::Rank,
10806 "partitioned enclose needs an array to partition",
10807 Some(span),
10808 ));
10809 }
10810 let counts = x
10811 .to_i64_vec_near(near)
10812 .ok_or_else(|| Error::domain("partition counts must be integers", span))?;
10813 if counts.iter().any(|&c| c < 0) {
10814 return Err(Error::domain("partition counts must not be negative", span));
10815 }
10816 let last = y.shape[y.rank() - 1];
10817 if counts.len() > last {
10821 return Err(Error::new(
10822 ErrorKind::Length,
10823 format!("{} count(s) for {} item(s)", counts.len(), last),
10824 Some(span),
10825 ));
10826 }
10827 let at = |i: usize| -> i64 {
10828 if x.rank() == 0 {
10829 counts.first().copied().unwrap_or(0)
10830 } else {
10831 counts.get(i).copied().unwrap_or(0)
10832 }
10833 };
10834 let mut groups: Vec<(usize, usize)> = Vec::new();
10837 for i in 0..last {
10838 for _ in 0..at(i) {
10839 groups.push((i, 0));
10840 }
10841 if let Some(g) = groups.last_mut() {
10842 g.1 += 1;
10843 }
10844 }
10845 let y = y.to_row_major();
10846 let rows = if last == 0 { 0 } else { y.count() / last };
10847 let lead = &y.shape[..y.rank() - 1];
10848 let parts: Vec<Array> = groups
10849 .iter()
10850 .map(|&(start, len)| {
10851 let mut d = Data::empty(y.dtype());
10852 for r in 0..rows {
10853 for c in start..start + len {
10854 push_elem(&mut d, y.row_major_data(), r * last + c);
10855 }
10856 }
10857 let mut shape = lead.to_vec();
10858 shape.push(len);
10859 Array::new(shape, d)
10860 })
10861 .collect();
10862 Ok(Array::new(vec![parts.len()], Data::Box(parts.into())))
10863}
10864
10865fn partition_enclose(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
10866 if y.rank() > 1 {
10869 let last = y.shape[y.rank() - 1];
10870 let rows = y.count() / last.max(1);
10871 let mut cells: Vec<Array> = Vec::new();
10872 let mut width = None;
10873 for r in 0..rows {
10874 let row = Array::new(vec![last], y.data.slice(r * last, (r + 1) * last));
10875 let parts = partition_enclose(x, &row, near, span)?;
10876 let n = parts.count();
10877 if *width.get_or_insert(n) != n {
10878 return Err(Error::internal("partitions of unequal count"));
10879 }
10880 match parts.data {
10881 Data::Box(v) => cells.extend(v.as_slice().iter().cloned()),
10882 _ => return Err(Error::internal("a partition is boxed")),
10883 }
10884 }
10885 let mut shape = y.shape[..y.rank() - 1].to_vec();
10886 shape.push(width.unwrap_or(0));
10887 return Ok(Array::new(shape, Data::Box(cells.into())));
10888 }
10889 if y.rank() == 0 {
10890 return Err(Error::new(
10891 ErrorKind::Rank,
10892 "partitioned enclose needs an array to partition",
10893 Some(span),
10894 ));
10895 }
10896 if x.count() == 0 && y.count() == 0 {
10901 return Ok(Array::new(vec![0], Data::Box(Vec::new().into())));
10902 }
10903 let mut flags = x
10904 .to_i64_vec_near(near)
10905 .ok_or_else(|| Error::domain("partition flags must be integers", span))?;
10906 if flags.iter().any(|&f| f < 0) {
10907 return Err(Error::domain("partition flags must not be negative", span));
10908 }
10909 if flags.len() == 1 && y.shape[0] != 1 {
10914 flags = vec![flags[0]; y.shape[0]];
10915 }
10916 if flags.len() != y.shape[0] {
10917 return Err(Error::new(
10918 ErrorKind::Length,
10919 format!("{} flag(s) for {} item(s)", flags.len(), y.shape[0]),
10920 Some(span),
10921 ));
10922 }
10923 let mut parts: Vec<Array> = Vec::new();
10924 let mut cur: Option<Data> = None;
10925 let mut prev = 0i64;
10926 for (i, &f) in flags.iter().enumerate() {
10927 if f > prev {
10928 if let Some(d) = cur.take() {
10929 parts.push(Array::new(vec![d.len()], d));
10930 }
10931 cur = Some(Data::empty(y.dtype()));
10932 }
10933 prev = f;
10934 if f == 0 {
10935 continue;
10936 }
10937 if let Some(d) = cur.as_mut() {
10938 push_elem(d, &y.data, i);
10939 }
10940 }
10941 if let Some(d) = cur.take() {
10942 parts.push(Array::new(vec![d.len()], d));
10943 }
10944 Ok(Array::new(vec![parts.len()], Data::Box(parts.into())))
10945}
10946
10947fn key(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
10950 let keys = if x.rank() == 0 { Array::new(vec![1], x.data.clone()) } else { x.clone() };
10951 let n = keys.items();
10952 if n != y.items() && !(y.rank() == 0 && n == 1) {
10953 return Err(Error::new(
10954 ErrorKind::Length,
10955 format!("{n} key(s) for {} item(s)", y.items()),
10956 Some(span),
10957 ));
10958 }
10959 let groups = group_positions(&keys, ctx.cfg.tol);
10960 let items = if y.rank() == 0 { Array::new(vec![1], y.data.clone()) } else { y.clone() };
10961 let mut cells = Vec::with_capacity(groups.len());
10962 for (_, at) in &groups {
10963 cells.push(u.monad(&select_items(&items, at), ctx, span)?);
10964 }
10965 assemble(&[groups.len()], cells, span)
10966}
10967
10968fn oblique(u: &Verb, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
10971 if y.rank() < 2 {
10972 let items = if y.rank() == 0 { Array::new(vec![1], y.data.clone()) } else { y.clone() };
10973 let n = items.items();
10974 let mut cells = Vec::with_capacity(n);
10975 for i in 0..n {
10976 cells.push(u.monad(&select_items(&items, &[i]), ctx, span)?);
10977 }
10978 return assemble(&[n], cells, span);
10979 }
10980 if y.rank() > 2 {
10981 return Err(Error::not_yet("oblique (u/.) on a rank-3 or higher argument", span));
10982 }
10983 let (rows, cols) = (y.shape[0], y.shape[1]);
10984 let mut cells = Vec::with_capacity(rows + cols - 1);
10985 for d in 0..rows + cols - 1 {
10986 let mut data = Data::empty(y.dtype());
10987 let mut len = 0usize;
10988 for i in 0..rows {
10989 if d >= i && d - i < cols {
10990 push_elem(&mut data, &y.data, i * cols + (d - i));
10991 len += 1;
10992 }
10993 }
10994 cells.push(u.monad(&Array::new(vec![len], data), ctx, span)?);
10995 }
10996 assemble(&[rows + cols - 1], cells, span)
10997}
10998
10999fn cut_ranges(frets: &[bool], mode: i64) -> Vec<(usize, usize)> {
11007 let n = frets.len();
11008 let mut out = Vec::new();
11009 if mode.abs() == 1 {
11010 let mut start: Option<usize> = None;
11011 for (i, &fret) in frets.iter().enumerate() {
11012 if fret {
11013 if let Some(s) = start {
11014 out.push((s, i));
11015 }
11016 start = Some(i);
11017 }
11018 }
11019 if let Some(s) = start {
11020 out.push((s, n));
11021 }
11022 if mode < 0 {
11023 return out.into_iter().map(|(s, e)| (s + 1, e)).collect();
11024 }
11025 } else {
11026 let mut start = 0usize;
11027 for (i, &fret) in frets.iter().enumerate() {
11028 if fret {
11029 out.push((start, i + 1));
11030 start = i + 1;
11031 }
11032 }
11033 if mode < 0 {
11034 return out.into_iter().map(|(s, e)| (s, e - 1)).collect();
11035 }
11036 }
11037 out
11038}
11039
11040fn cut(
11042 u: &Verb,
11043 x: Option<&Array>,
11044 y: &Array,
11045 mode: i64,
11046 ctx: &mut Ctx<'_>,
11047 span: Span,
11048) -> Result<Array> {
11049 if mode == 0 {
11050 let Some(x) = x else {
11051 return u.monad(&reverse_all_axes(y), ctx, span);
11052 };
11053 let (origin, size) = rectangle(x, span)?;
11054 let origin = origin.unwrap_or_else(|| vec![0; size.len()]);
11055 return u.monad(&subarray(y, &origin, &size, span)?, ctx, span);
11056 }
11057 if mode.abs() == 3 {
11058 let Some(x) = x else {
11059 return Err(Error::not_yet("monadic tessellation (u;.3 y)", span));
11060 };
11061 return tessellate(u, x, y, mode < 0, ctx, span);
11062 }
11063 if !matches!(mode, 1 | -1 | 2 | -2) {
11064 return Err(Error::not_yet(format!("cut (u;.{mode})"), span));
11065 }
11066 let items = if y.rank() == 0 { Array::new(vec![1], y.data.clone()) } else { y.clone() };
11067 let n = items.items();
11068 let tol = ctx.cfg.tol;
11069 let frets: Vec<bool> = match x {
11070 Some(x) => {
11071 let flags = x.to_i64_vec().ok_or_else(|| {
11072 if x.dtype() == DType::Box {
11073 Error::not_yet("per-axis cut frets (a boxed left argument)", span)
11077 } else {
11078 Error::domain("cut frets must be integers", span)
11079 }
11080 })?;
11081 if let Some(&bad) = flags.iter().find(|&&f| f != 0 && f != 1) {
11084 return Err(Error::domain(format!("{bad} is not a fret: a fret is 0 or 1"), span));
11085 }
11086 if x.rank() == 0 {
11089 vec![flags[0] != 0; n]
11090 } else if flags.is_empty() {
11091 Vec::new()
11093 } else {
11094 if flags.len() != n {
11095 return Err(Error::new(
11096 ErrorKind::Length,
11097 format!("{} fret(s) for {n} item(s)", flags.len()),
11098 Some(span),
11099 ));
11100 }
11101 flags.iter().map(|&f| f != 0).collect()
11102 }
11103 }
11104 None => {
11105 if n == 0 {
11107 Vec::new()
11108 } else {
11109 let at = if mode.abs() == 1 { 0 } else { n - 1 };
11110 let mark = items.item(at);
11111 (0..n).map(|i| arrays_match(&items.item(i), &mark, tol)).collect()
11112 }
11113 }
11114 };
11115 let empty_frets = matches!(x, Some(x) if x.rank() == 1 && x.count() == 0);
11122 let no_axis = matches!(x, Some(x) if x.rank() > 1 && x.count() == 0);
11123 let ranges = if empty_frets && n > 0 {
11124 vec![(0, n)]
11125 } else if empty_frets || no_axis {
11126 Vec::new()
11127 } else {
11128 cut_ranges(&frets, mode)
11129 };
11130 if ranges.is_empty() {
11134 let cell = u.is_pure().then(|| section(&items, 0, 0));
11135 return Ok(empty_frame(&[0], items.dtype(), cell, ctx, |cell, c| u.monad(cell, c, span)));
11136 }
11137 let mut cells = Vec::with_capacity(ranges.len());
11138 for (s, e) in &ranges {
11139 cells.push(u.monad(§ion(&items, *s, *e), ctx, span)?);
11140 }
11141 assemble(&[ranges.len()], cells, span)
11142}
11143
11144fn rectangle(x: &Array, span: Span) -> Result<(Option<Vec<i64>>, Vec<i64>)> {
11147 let values = x
11148 .to_i64_vec()
11149 .ok_or_else(|| Error::domain("a cut rectangle is whole numbers", span))?;
11150 match x.rank() {
11151 0 | 1 => Ok((None, values)),
11152 2 if x.shape[0] == 2 => {
11153 let n = x.shape[1];
11154 Ok((Some(values[..n].to_vec()), values[n..].to_vec()))
11155 }
11156 _ => Err(Error::new(
11157 ErrorKind::Rank,
11158 "a cut rectangle is a vector of sizes, or two rows of origins and sizes",
11159 Some(span),
11160 )),
11161 }
11162}
11163
11164fn subarray(y: &Array, origin: &[i64], size: &[i64], span: Span) -> Result<Array> {
11168 if origin.len() > y.rank() {
11169 return Err(Error::new(
11170 ErrorKind::Rank,
11171 format!("a cut of {} axis/axes into a rank-{} value", origin.len(), y.rank()),
11172 Some(span),
11173 ));
11174 }
11175 let r = y.rank();
11176 let st = strides(&y.shape);
11177 let mut shape = y.shape.clone();
11178 let mut start = vec![0i64; r];
11179 let mut step = vec![1i64; r];
11180 for k in 0..origin.len() {
11181 let want = u128::from(size[k].unsigned_abs());
11185 let from = if origin[k] < 0 { origin[k] + y.shape[k] as i64 } else { origin[k] };
11186 if from < 0 || u128::from(from.unsigned_abs()) + want > y.shape[k] as u128 {
11187 return Err(Error::domain(
11188 format!("a cut of {want} from {from} leaves axis {k} of {}", y.shape[k]),
11189 span,
11190 ));
11191 }
11192 let len = want as usize;
11193 shape[k] = len;
11194 if size[k] < 0 {
11195 start[k] = from + len as i64 - 1;
11196 step[k] = -1;
11197 } else {
11198 start[k] = from;
11199 }
11200 }
11201 Ok(gather(y, &shape, &start, &step, &st))
11202}
11203
11204fn gather(y: &Array, shape: &[usize], start: &[i64], step: &[i64], st: &[usize]) -> Array {
11206 let n: usize = shape.iter().product();
11207 let mut data = Data::empty(y.dtype());
11208 let mut coord = vec![0usize; shape.len()];
11209 for _ in 0..n {
11210 let idx: usize = (0..shape.len())
11211 .map(|k| (start[k] + step[k] * coord[k] as i64) as usize * st[k])
11212 .sum();
11213 push_elem(&mut data, &y.data, idx);
11214 odometer(&mut coord, shape);
11215 }
11216 Array::new(shape.to_vec(), data)
11217}
11218
11219fn tessellate(
11223 u: &Verb,
11224 x: &Array,
11225 y: &Array,
11226 complete: bool,
11227 ctx: &mut Ctx<'_>,
11228 span: Span,
11229) -> Result<Array> {
11230 let (movement, size) = rectangle(x, span)?;
11232 if size.iter().any(|&s| s < 0) && movement.is_none() {
11237 return Err(Error::not_yet(
11238 "a negative block size without a movement row (x u;.3 y)",
11239 span,
11240 ));
11241 }
11242 let movement = movement.unwrap_or_else(|| vec![1; size.len()]);
11243 if size.len() > y.rank() {
11244 return Err(Error::new(
11245 ErrorKind::Rank,
11246 format!("a tessellation of {} axis/axes into a rank-{} value", size.len(), y.rank()),
11247 Some(span),
11248 ));
11249 }
11250 let mut frame = Vec::with_capacity(size.len());
11254 for k in 0..size.len() {
11255 let (len, step) = (i128::from(y.shape[k] as i64), i128::from(movement[k]));
11256 let block = i128::from(size[k]).abs();
11257 if step <= 0 {
11258 return Err(Error::domain("a tessellation moves by a positive step", span));
11259 }
11260 let count = if complete {
11261 if len < block { 0 } else { (len - block) / step + 1 }
11262 } else {
11263 (len + step - 1) / step
11264 };
11265 frame.push(count as usize);
11266 }
11267 let total: usize = frame.iter().product();
11268 let mut cells = Vec::with_capacity(total);
11269 let mut coord = vec![0usize; frame.len()];
11270 for _ in 0..total {
11271 let origin: Vec<i64> = (0..frame.len()).map(|k| coord[k] as i64 * movement[k]).collect();
11272 let block: Vec<i64> = (0..frame.len())
11275 .map(|k| {
11276 let left = i128::from(y.shape[k] as i64 - origin[k]);
11277 let len = i128::from(size[k]).abs().min(left) as i64;
11278 if size[k] < 0 { -len } else { len }
11279 })
11280 .collect();
11281 cells.push(u.monad(&subarray(y, &origin, &block, span)?, ctx, span)?);
11282 odometer(&mut coord, &frame);
11283 }
11284 assemble(&frame, cells, span)
11285}
11286
11287fn reverse_all_axes(y: &Array) -> Array {
11289 if y.rank() == 0 {
11290 return y.clone();
11291 }
11292 let st = strides(&y.shape);
11293 let n = y.count();
11294 let r = y.rank();
11295 let mut data = Data::empty(y.dtype());
11296 let mut coord = vec![0usize; r];
11297 for _ in 0..n {
11298 let idx: usize = (0..r).map(|k| (y.shape[k] - 1 - coord[k]) * st[k]).sum();
11299 push_elem(&mut data, &y.data, idx);
11300 odometer(&mut coord, &y.shape);
11301 }
11302 Array::new(y.shape.clone(), data)
11303}
11304
11305fn axis_to_front(y: &Array, k: usize) -> Array {
11309 if k == 0 || y.rank() < 2 {
11310 return y.clone();
11311 }
11312 let r = y.rank();
11313 let src: Vec<usize> = std::iter::once(k).chain((0..r).filter(|&a| a != k)).collect();
11314 permute_axes(y, &src)
11315}
11316
11317fn front_to_axis(y: &Array, k: usize) -> Array {
11319 if k == 0 || y.rank() < 2 {
11320 return y.clone();
11321 }
11322 let r = y.rank();
11323 let mut src = Vec::with_capacity(r);
11326 for a in 0..r {
11327 src.push(match a.cmp(&k) {
11328 std::cmp::Ordering::Less => a + 1,
11329 std::cmp::Ordering::Equal => 0,
11330 std::cmp::Ordering::Greater => a,
11331 });
11332 }
11333 permute_axes(y, &src)
11334}
11335
11336fn transpose_to(y: &Array, dest: &[usize], span: Span) -> Result<Array> {
11341 let rank_out = dest.iter().copied().max().map_or(0, |m| m + 1);
11342 let mut out_shape = vec![usize::MAX; rank_out];
11343 for (a, &d) in dest.iter().enumerate() {
11344 out_shape[d] = out_shape[d].min(y.shape[a]);
11345 }
11346 if out_shape.contains(&usize::MAX) {
11347 return Err(Error::new(
11348 ErrorKind::Domain,
11349 "a transpose must name every axis of the result",
11350 Some(span),
11351 ));
11352 }
11353 let y = y.to_row_major();
11354 let st = strides(&y.shape);
11355 let n: usize = out_shape.iter().product();
11356 let mut data = Data::empty(y.dtype());
11357 let mut coord = vec![0usize; rank_out];
11358 for _ in 0..n {
11359 let idx: usize = dest.iter().enumerate().map(|(a, &d)| coord[d] * st[a]).sum();
11360 push_elem(&mut data, &y.data, idx);
11361 odometer(&mut coord, &out_shape);
11362 }
11363 Ok(Array::new(out_shape, data))
11364}
11365
11366fn transpose_apl(x: &Array, y: &Array, io: i64, near: NearInt, span: Span) -> Result<Array> {
11369 let axes = x
11370 .to_i64_vec_near(near)
11371 .ok_or_else(|| Error::domain("a transpose is given whole numbers", span))?;
11372 if axes.len() != y.rank() {
11373 return Err(Error::new(
11374 ErrorKind::Length,
11375 format!("{} axes for a rank-{} value", axes.len(), y.rank()),
11376 Some(span),
11377 ));
11378 }
11379 let mut dest = Vec::with_capacity(axes.len());
11380 for a in axes {
11381 let d = a - io;
11382 if d < 0 || d as usize >= y.rank() {
11383 return Err(Error::new(
11384 ErrorKind::Domain,
11385 format!("axis {a} is outside a rank-{} value", y.rank()),
11386 Some(span),
11387 ));
11388 }
11389 dest.push(d as usize);
11390 }
11391 transpose_to(y, &dest, span)
11392}
11393
11394fn transpose_j(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
11398 let groups: Vec<Vec<i64>> = match x.as_boxes() {
11399 Some(bs) => bs
11400 .iter()
11401 .map(|b| {
11402 b.to_i64_vec_near(near).ok_or_else(|| {
11403 Error::domain("a transpose is given whole numbers", span)
11404 })
11405 })
11406 .collect::<Result<Vec<_>>>()?,
11407 None => x
11408 .to_i64_vec_near(near)
11409 .ok_or_else(|| Error::domain("a transpose is given whole numbers", span))?
11410 .into_iter()
11411 .map(|a| vec![a])
11412 .collect(),
11413 };
11414 let r = y.rank();
11415 let mut group_of = vec![None; r];
11418 for (g, axes) in groups.iter().enumerate() {
11419 for &a in axes {
11420 let k = if a < 0 { a + r as i64 } else { a };
11421 if k < 0 || k as usize >= r {
11422 return Err(Error::new(
11423 ErrorKind::Domain,
11424 format!("axis {a} is outside a rank-{r} value"),
11425 Some(span),
11426 ));
11427 }
11428 if group_of[k as usize].is_some() {
11429 return Err(Error::new(
11430 ErrorKind::Domain,
11431 format!("axis {a} is named twice in a transpose"),
11432 Some(span),
11433 ));
11434 }
11435 group_of[k as usize] = Some(g);
11436 }
11437 }
11438 let leading = group_of.iter().filter(|g| g.is_none()).count();
11439 let mut dest = vec![0usize; r];
11440 let mut next = 0;
11441 for a in 0..r {
11442 match group_of[a] {
11443 None => {
11444 dest[a] = next;
11445 next += 1;
11446 }
11447 Some(g) => dest[a] = leading + g,
11448 }
11449 }
11450 transpose_to(y, &dest, span)
11451}
11452
11453fn permute_axes(y: &Array, src: &[usize]) -> Array {
11455 let st = strides(&y.shape);
11456 let out_shape: Vec<usize> = src.iter().map(|&a| y.shape[a]).collect();
11457 let n = y.count();
11458 let mut data = Data::empty(y.dtype());
11459 let mut coord = vec![0usize; src.len()];
11460 for _ in 0..n {
11461 let idx: usize = (0..src.len()).map(|a| coord[a] * st[src[a]]).sum();
11462 push_elem(&mut data, &y.data, idx);
11463 odometer(&mut coord, &out_shape);
11464 }
11465 Array::new(out_shape, data)
11466}
11467
11468struct Spec {
11472 width: usize,
11474 cells: Vec<Vec<usize>>,
11476 shape: Vec<usize>,
11479}
11480
11481fn axis_position(v: i64, len: usize, span: Span) -> Result<usize> {
11484 let p = if v < 0 { v + len as i64 } else { v };
11485 if p < 0 || p >= len as i64 {
11486 return Err(Error::domain(
11487 format!("index {v} is out of range: the axis has {len} element(s)"),
11488 span,
11489 ));
11490 }
11491 Ok(p as usize)
11492}
11493
11494fn index_spec(content: &Array, y: &Array, near: NearInt, span: Span) -> Result<Spec> {
11504 let too_deep = |n: usize| {
11505 Error::new(
11506 ErrorKind::Rank,
11507 format!("an index specification of {n} axis/axes into a rank-{} value", y.rank()),
11508 Some(span),
11509 )
11510 };
11511 if let Some(items) = content.as_boxes() {
11512 if items.len() > y.rank() {
11513 return Err(too_deep(items.len()));
11514 }
11515 let mut per_axis: Vec<Vec<usize>> = Vec::with_capacity(items.len());
11516 let mut shape: Vec<usize> = Vec::new();
11517 for (k, c) in items.iter().enumerate() {
11518 let len = y.shape[k];
11519 if c.as_boxes().is_some() {
11520 let inner = open_cell(c);
11521 let excluded = inner.to_i64_vec_near(near).ok_or_else(|| {
11522 Error::domain("an index complement holds integers", span)
11523 })?;
11524 let mut dropped = vec![false; len];
11525 for v in excluded {
11526 dropped[axis_position(v, len, span)?] = true;
11527 }
11528 let kept: Vec<usize> = (0..len).filter(|i| !dropped[*i]).collect();
11529 shape.push(kept.len());
11530 per_axis.push(kept);
11531 } else {
11532 let idx = c
11533 .to_i64_vec_near(near)
11534 .ok_or_else(|| Error::domain("an index holds integers", span))?;
11535 let mut positions = Vec::with_capacity(idx.len());
11536 for v in idx {
11537 positions.push(axis_position(v, len, span)?);
11538 }
11539 shape.extend_from_slice(&c.shape);
11540 per_axis.push(positions);
11541 }
11542 }
11543 let mut cells: Vec<Vec<usize>> = vec![Vec::new()];
11545 for positions in &per_axis {
11546 let mut next = Vec::with_capacity(cells.len() * positions.len());
11547 for prefix in &cells {
11548 for &p in positions {
11549 let mut cell = prefix.clone();
11550 cell.push(p);
11551 next.push(cell);
11552 }
11553 }
11554 cells = next;
11555 }
11556 return Ok(Spec { width: per_axis.len(), cells, shape });
11557 }
11558 let idx = content
11559 .to_i64_vec_near(near)
11560 .ok_or_else(|| Error::domain("an index specification holds integers", span))?;
11561 let rank = content.rank();
11562 let width = if rank == 0 { 1 } else { content.shape[rank - 1] };
11563 if width > y.rank() {
11564 return Err(too_deep(width));
11565 }
11566 let shape: Vec<usize> = if rank == 0 { Vec::new() } else { content.shape[..rank - 1].to_vec() };
11567 let count: usize = shape.iter().product();
11568 let mut cells: Vec<Vec<usize>> = Vec::new();
11569 if width == 0 {
11570 cells.resize(count, Vec::new());
11571 } else {
11572 for chunk in idx.chunks(width) {
11573 let mut cell = Vec::with_capacity(width);
11574 for (k, &v) in chunk.iter().enumerate() {
11575 cell.push(axis_position(v, y.shape[k], span)?);
11576 }
11577 cells.push(cell);
11578 }
11579 }
11580 Ok(Spec { width, cells, shape })
11581}
11582
11583fn spec_offset(st: &[usize], cell: &[usize]) -> usize {
11585 cell.iter().enumerate().map(|(k, &p)| p * st[k]).sum()
11586}
11587
11588fn select_spec(spec: &Spec, y: &Array) -> Array {
11590 let st = strides(&y.shape);
11591 let size: usize = y.shape[spec.width..].iter().product();
11592 let mut data = Data::empty(y.dtype());
11593 for cell in &spec.cells {
11594 let base = spec_offset(&st, cell);
11595 for e in 0..size {
11596 push_elem(&mut data, &y.data, base + e);
11597 }
11598 }
11599 let mut shape = spec.shape.clone();
11600 shape.extend_from_slice(&y.shape[spec.width..]);
11601 Array::new(shape, data)
11602}
11603
11604fn amend_spec(spec: &Spec, x: &Array, y: &Array, span: Span) -> Result<Array> {
11607 let size: usize = y.shape[spec.width..].iter().product();
11608 let per_cell = if x.count() == size {
11609 false
11610 } else if x.count() == size * spec.cells.len() {
11611 true
11612 } else {
11613 return Err(Error::new(
11614 ErrorKind::Length,
11615 format!(
11616 "cannot amend {} cell(s) of {size} element(s) each with {} element(s)",
11617 spec.cells.len(),
11618 x.count()
11619 ),
11620 Some(span),
11621 ));
11622 };
11623 let mismatch = || {
11624 Error::new(
11625 ErrorKind::Type,
11626 "the replacement and the argument hold different kinds of value",
11627 Some(span),
11628 )
11629 };
11630 let t = DType::promote(x.dtype(), y.dtype()).ok_or_else(mismatch)?;
11631 let (Some(src), Some(base)) = (x.data.cast(t), y.data.cast(t)) else {
11632 return Err(mismatch());
11633 };
11634 let st = strides(&y.shape);
11635 let mut plan: Vec<Option<usize>> = vec![None; y.count()];
11636 for (n, cell) in spec.cells.iter().enumerate() {
11637 let at = spec_offset(&st, cell);
11638 for e in 0..size {
11639 plan[at + e] = Some(if per_cell { n * size + e } else { e });
11640 }
11641 }
11642 let mut data = Data::empty(t);
11643 for (i, slot) in plan.iter().enumerate() {
11644 match slot {
11645 Some(n) => push_elem(&mut data, &src, *n),
11646 None => push_elem(&mut data, &base, i),
11647 }
11648 }
11649 Ok(Array::new(y.shape.clone(), data))
11650}
11651
11652fn map_paths(y: &Array) -> Array {
11661 fn coord_of(shape: &[usize], mut i: usize) -> Array {
11662 let mut out = vec![0i64; shape.len()];
11663 for k in (0..shape.len()).rev() {
11664 out[k] = (i % shape[k]) as i64;
11665 i /= shape[k];
11666 }
11667 Array::from_i64(out)
11668 }
11669 fn go(y: &Array, prefix: &[Array]) -> Array {
11670 let Some(boxes) = y.as_boxes() else {
11671 if prefix.is_empty() {
11672 return Array::new(vec![0], Data::I64(Vec::new().into()));
11673 }
11674 return Array::new(vec![prefix.len()], Data::Box(prefix.to_vec().into()));
11675 };
11676 let cells: Vec<Array> = boxes
11677 .iter()
11678 .enumerate()
11679 .map(|(i, b)| {
11680 let mut path = prefix.to_vec();
11681 path.push(coord_of(&y.shape, i));
11682 go(b, &path)
11683 })
11684 .collect();
11685 Array::new(y.shape.clone(), Data::Box(cells.into()))
11686 }
11687 go(y, &[])
11688}
11689
11690fn shift_fill(
11695 x: &Array,
11696 y: &Array,
11697 fill: &Array,
11698 near: NearInt,
11699 span: Span,
11700) -> Result<Array> {
11701 let counts = axis_counts(x, "shift", near, span)?;
11702 if y.rank() == 0 {
11703 return Ok(y.clone());
11704 }
11705 if counts.len() > y.rank() {
11706 return Err(Error::new(
11707 ErrorKind::Length,
11708 format!("shift has {} amounts for an argument of rank {}", counts.len(), y.rank()),
11709 Some(span),
11710 ));
11711 }
11712 if fill.count() != 1 {
11713 return Err(Error::new(ErrorKind::Length, "a fill is one atom", Some(span)));
11714 }
11715 let mismatch = || {
11716 Error::new(ErrorKind::Type, "the fill and the argument differ in kind", Some(span))
11717 };
11718 let t = DType::promote(y.dtype(), fill.dtype()).ok_or_else(mismatch)?;
11719 let (Some(base), Some(f)) = (y.data.cast(t), fill.data.cast(t)) else {
11720 return Err(mismatch());
11721 };
11722 let st = strides(&y.shape);
11723 let r = y.rank();
11724 let mut data = Data::empty(t);
11725 let mut coord = vec![0usize; r];
11726 for _ in 0..y.count() {
11727 let mut idx = 0usize;
11728 let mut vacated = false;
11729 for k in 0..r {
11730 let from = (coord[k] as i64).saturating_add(counts.get(k).copied().unwrap_or(0));
11734 if from < 0 || from >= y.shape[k] as i64 {
11735 vacated = true;
11736 break;
11737 }
11738 idx += from as usize * st[k];
11739 }
11740 if vacated {
11741 push_elem(&mut data, &f, 0);
11742 } else {
11743 push_elem(&mut data, &base, idx);
11744 }
11745 odometer(&mut coord, &y.shape);
11746 }
11747 Ok(Array::new(y.shape.clone(), data))
11748}
11749
11750fn memo_key(a: &Array, out: &mut Vec<u64>) -> bool {
11755 out.push(a.rank() as u64);
11756 out.extend(a.shape.iter().map(|&n| n as u64));
11757 out.push(a.dtype() as u64);
11758 match &a.data {
11759 Data::Ext(_) | Data::Rat(_) => false,
11760 Data::Box(items) => items.iter().all(|item| memo_key(item, out)),
11761 d => {
11762 for i in 0..d.len() {
11763 out.push(elem_key(d, i));
11764 }
11765 true
11766 }
11767 }
11768}
11769
11770fn memoised(
11772 u: &Verb,
11773 cache: &MemoCache,
11774 x: Option<&Array>,
11775 y: &Array,
11776 ctx: &mut Ctx<'_>,
11777 span: Span,
11778) -> Result<Array> {
11779 let apply = |ctx: &mut Ctx<'_>| match x {
11780 Some(x) => u.dyad(x, y, ctx, span),
11781 None => u.monad(y, ctx, span),
11782 };
11783 let mut key = vec![u64::from(x.is_some())];
11784 let keyed = x.is_none_or(|x| memo_key(x, &mut key)) && memo_key(y, &mut key);
11785 if !keyed {
11786 return apply(ctx);
11787 }
11788 if let Ok(map) = cache.lock() && let Some(hit) = map.get(&key) {
11789 return Ok(hit.clone());
11790 }
11791 let out = apply(ctx)?;
11792 if let Ok(mut map) = cache.lock() {
11793 map.insert(key, out.clone());
11794 }
11795 Ok(out)
11796}
11797
11798fn at_level(
11804 u: &Verb,
11805 level: i64,
11806 spread: bool,
11807 y: &Array,
11808 ctx: &mut Ctx<'_>,
11809 span: Span,
11810) -> Result<Array> {
11811 let n = if level < 0 { (boxing_level(y) + level).max(0) } else { level };
11813 if !spread {
11814 return map_level(u, n, y, ctx, span);
11815 }
11816 let mut cells = Vec::new();
11817 collect_level(u, n, y, ctx, span, &mut cells)?;
11818 let count = cells.len();
11819 assemble(&[count], cells, span)
11820}
11821
11822fn map_level(u: &Verb, n: i64, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
11823 let Some(boxes) = y.as_boxes().filter(|_| boxing_level(y) > n) else {
11824 return u.monad(y, ctx, span);
11825 };
11826 let boxes = boxes.to_vec();
11827 let mut cells = Vec::with_capacity(boxes.len());
11828 for b in &boxes {
11829 cells.push(map_level(u, n, b, ctx, span)?);
11830 }
11831 Ok(Array::new(y.shape.clone(), Data::Box(cells.into())))
11832}
11833
11834fn at_level_dyad(
11839 u: &Verb,
11840 level: i64,
11841 spread: bool,
11842 x: &Array,
11843 y: &Array,
11844 ctx: &mut Ctx<'_>,
11845 span: Span,
11846) -> Result<Array> {
11847 let depth = |a: &Array| if level < 0 { (boxing_level(a) + level).max(0) } else { level };
11850 let (nx, ny) = (depth(x), depth(y));
11851 if !spread {
11852 return map_level_dyad(u, nx, ny, x, y, ctx, span);
11853 }
11854 let mut cells = Vec::new();
11855 collect_level_dyad(u, nx, ny, x, y, ctx, span, &mut cells)?;
11856 let count = cells.len();
11857 assemble(&[count], cells, span)
11858}
11859
11860struct LevelPairs {
11862 left: Vec<Array>,
11863 right: Vec<Array>,
11864 shape: Vec<usize>,
11865}
11866
11867fn level_pairs(
11870 nx: i64,
11871 ny: i64,
11872 x: &Array,
11873 y: &Array,
11874 span: Span,
11875) -> Result<Option<LevelPairs>> {
11876 let bx = x.as_boxes().filter(|_| boxing_level(x) > nx);
11877 let by = y.as_boxes().filter(|_| boxing_level(y) > ny);
11878 Ok(match (bx, by) {
11879 (None, None) => None,
11880 (Some(bx), None) => {
11881 let n = bx.len();
11882 Some(LevelPairs {
11883 left: bx.to_vec(),
11884 right: vec![y.clone(); n],
11885 shape: x.shape.clone(),
11886 })
11887 }
11888 (None, Some(by)) => {
11889 let n = by.len();
11890 Some(LevelPairs {
11891 left: vec![x.clone(); n],
11892 right: by.to_vec(),
11893 shape: y.shape.clone(),
11894 })
11895 }
11896 (Some(bx), Some(by)) => {
11897 if x.shape != y.shape {
11898 return Err(Error::new(
11899 ErrorKind::Length,
11900 format!(
11901 "the levels do not agree: left shape {}, right shape {}",
11902 show_shape(&x.shape),
11903 show_shape(&y.shape)
11904 ),
11905 Some(span),
11906 ));
11907 }
11908 Some(LevelPairs { left: bx.to_vec(), right: by.to_vec(), shape: x.shape.clone() })
11909 }
11910 })
11911}
11912
11913fn map_level_dyad(
11914 u: &Verb,
11915 nx: i64,
11916 ny: i64,
11917 x: &Array,
11918 y: &Array,
11919 ctx: &mut Ctx<'_>,
11920 span: Span,
11921) -> Result<Array> {
11922 let Some(step) = level_pairs(nx, ny, x, y, span)? else {
11923 return u.dyad(x, y, ctx, span);
11924 };
11925 let mut cells = Vec::with_capacity(step.left.len());
11926 for (a, b) in step.left.iter().zip(step.right.iter()) {
11927 cells.push(map_level_dyad(u, nx, ny, a, b, ctx, span)?);
11928 }
11929 Ok(Array::new(step.shape, Data::Box(cells.into())))
11930}
11931
11932#[allow(clippy::too_many_arguments)]
11933fn collect_level_dyad(
11934 u: &Verb,
11935 nx: i64,
11936 ny: i64,
11937 x: &Array,
11938 y: &Array,
11939 ctx: &mut Ctx<'_>,
11940 span: Span,
11941 out: &mut Vec<Array>,
11942) -> Result<()> {
11943 let Some(step) = level_pairs(nx, ny, x, y, span)? else {
11944 out.push(u.dyad(x, y, ctx, span)?);
11945 return Ok(());
11946 };
11947 for (a, b) in step.left.iter().zip(step.right.iter()) {
11948 collect_level_dyad(u, nx, ny, a, b, ctx, span, out)?;
11949 }
11950 Ok(())
11951}
11952
11953fn collect_level(
11954 u: &Verb,
11955 n: i64,
11956 y: &Array,
11957 ctx: &mut Ctx<'_>,
11958 span: Span,
11959 out: &mut Vec<Array>,
11960) -> Result<()> {
11961 let Some(boxes) = y.as_boxes().filter(|_| boxing_level(y) > n) else {
11962 out.push(u.monad(y, ctx, span)?);
11963 return Ok(());
11964 };
11965 let boxes = boxes.to_vec();
11966 for b in &boxes {
11967 collect_level(u, n, b, ctx, span, out)?;
11968 }
11969 Ok(())
11970}
11971
11972fn poly_coeffs(y: &Array, span: Span) -> Result<Vec<Cx>> {
11976 let c = y
11977 .data
11978 .cast(DType::Complex)
11979 .ok_or_else(|| Error::domain("a polynomial's coefficients are numbers", span))?;
11980 match c {
11981 Data::Complex(v) => Ok(v.as_slice().to_vec()),
11982 _ => Err(Error::internal("coefficients did not cast to complex")),
11983 }
11984}
11985
11986fn poly_coeffs_relaxed(y: &Array, span: Span) -> Result<Vec<Cx>> {
11992 if y.count() == 0 {
11993 return Ok(Vec::new());
11994 }
11995 poly_coeffs(y, span)
11996}
11997
11998fn root_form_coeffs(parts: &[Array], span: Span) -> Result<Vec<Cx>> {
12001 let (multiplier, roots) = root_form(parts, span)?;
12002 let mut coeffs = vec![multiplier];
12003 for r in poly_coeffs_relaxed(roots, span)? {
12004 let mut next = vec![cx::ZERO; coeffs.len() + 1];
12005 for (k, &c) in coeffs.iter().enumerate() {
12006 next[k + 1] = cx::add(next[k + 1], c);
12007 next[k] = cx::sub(next[k], cx::mul(c, r));
12008 }
12009 coeffs = next;
12010 }
12011 Ok(coeffs)
12012}
12013
12014fn root_form(parts: &[Array], span: Span) -> Result<(Cx, &Array)> {
12018 match parts {
12019 [roots] => Ok((cx::ONE, roots)),
12020 [multiplier, roots] => Ok((
12021 poly_coeffs_relaxed(multiplier, span)?.first().copied().unwrap_or(cx::ONE),
12022 roots,
12023 )),
12024 _ => Err(Error::domain("the root form of a polynomial is `multiplier ; roots`", span)),
12025 }
12026}
12027
12028const HYPERGEOMETRIC_TERMS: usize = 1 << 16;
12032
12033fn cx_list(v: &[Cx]) -> String {
12035 v.iter()
12036 .map(|z| if z[1] == 0.0 { format!("{}", z[0]) } else { format!("{}j{}", z[0], z[1]) })
12037 .collect::<Vec<_>>()
12038 .join(" ")
12039}
12040
12041fn hypergeometric(num: &[Cx], den: &[Cx], y: &Array, span: Span) -> Result<Array> {
12049 let (num, den) = cancel_parameters(num, den);
12050 let at = poly_coeffs(y, span)?;
12051 let mut out = Vec::with_capacity(at.len());
12052 for z in &at {
12053 out.push(hypergeometric_at(&num, &den, *z, span)?);
12054 }
12055 let mut a = complex_or_real(out);
12056 a.shape = y.shape.clone();
12057 Ok(a)
12058}
12059
12060fn cancel_parameters(num: &[Cx], den: &[Cx]) -> (Vec<Cx>, Vec<Cx>) {
12063 let mut left: Vec<Cx> = Vec::with_capacity(num.len());
12064 let mut right: Vec<Cx> = den.to_vec();
12065 for a in num {
12066 match right.iter().position(|b| b == a) {
12067 Some(i) => {
12068 right.remove(i);
12069 }
12070 None => left.push(*a),
12071 }
12072 }
12073 (left, right)
12074}
12075
12076fn hypergeometric_at(num: &[Cx], den: &[Cx], z: Cx, span: Span) -> Result<Cx> {
12077 let real = |v: &[Cx]| v.iter().all(|c| c[1] == 0.0);
12081 if z[1] == 0.0 && real(num) && real(den) {
12082 let n: Vec<f64> = num.iter().map(|c| c[0]).collect();
12083 let d: Vec<f64> = den.iter().map(|c| c[0]).collect();
12084 return Ok([hypergeometric_real(&n, &d, z[0], span)?, 0.0]);
12085 }
12086 let mut sum = cx::ONE;
12087 let mut term = cx::ONE;
12088 for k in 0..HYPERGEOMETRIC_TERMS {
12089 let kk = [k as f64, 0.0];
12090 let mut ratio = z;
12091 for a in num {
12092 ratio = cx::mul(ratio, cx::add(*a, kk));
12093 }
12094 for b in den {
12095 ratio = cx::div(ratio, cx::add(*b, kk));
12096 }
12097 term = cx::div(cx::mul(term, ratio), [k as f64 + 1.0, 0.0]);
12098 if !term[0].is_finite() || !term[1].is_finite() {
12099 return Ok(term);
12102 }
12103 let before = sum;
12104 sum = cx::add(sum, term);
12105 if sum == before {
12107 return Ok(sum);
12108 }
12109 }
12110 Err(Error::domain(
12111 format!("the hypergeometric series did not converge within {HYPERGEOMETRIC_TERMS} terms"),
12112 span,
12113 ))
12114}
12115
12116fn hypergeometric_real(num: &[f64], den: &[f64], z: f64, span: Span) -> Result<f64> {
12117 let mut sum = 1.0f64;
12118 let mut term = 1.0f64;
12119 for k in 0..HYPERGEOMETRIC_TERMS {
12120 let kk = k as f64;
12121 let mut ratio = z;
12122 for a in num {
12123 ratio *= a + kk;
12124 }
12125 for b in den {
12126 ratio /= b + kk;
12127 }
12128 term = term * ratio / (kk + 1.0);
12129 if !term.is_finite() {
12130 return Ok(term);
12131 }
12132 let before = sum;
12133 sum += term;
12134 if sum == before {
12135 return Ok(sum);
12136 }
12137 }
12138 Err(Error::domain(
12139 format!("the hypergeometric series did not converge within {HYPERGEOMETRIC_TERMS} terms"),
12140 span,
12141 ))
12142}
12143
12144fn complex_or_real(values: Vec<Cx>) -> Array {
12146 if values.iter().all(|z| z[1] == 0.0) {
12147 return Array::from_f64(values.iter().map(|z| z[0]).collect());
12148 }
12149 Array::new(vec![values.len()], Data::Complex(values.into()))
12150}
12151
12152fn poly_eval(x: &Array, y: &Array, span: Span) -> Result<Array> {
12155 let at = poly_coeffs(y, span)?;
12156 let at = at.first().copied().unwrap_or(cx::ZERO);
12157 let value = match x.as_boxes() {
12158 Some(parts) => {
12159 let (mut v, roots) = root_form(parts, span)?;
12160 for r in poly_coeffs_relaxed(roots, span)? {
12161 v = cx::mul(v, cx::sub(at, r));
12162 }
12163 v
12164 }
12165 None => {
12166 let c = poly_coeffs_relaxed(x, span)?;
12167 let mut v = cx::ZERO;
12168 for &k in c.iter().rev() {
12169 v = cx::add(cx::mul(v, at), k);
12170 }
12171 v
12172 }
12173 };
12174 Ok(scalar_complex_or_real(value))
12175}
12176
12177fn scalar_complex_or_real(z: Cx) -> Array {
12178 if z[1] == 0.0 {
12179 return Array::scalar_f64(z[0]);
12180 }
12181 Array::new(vec![], Data::Complex(vec![z].into()))
12182}
12183
12184fn poly_roots(y: &Array, span: Span) -> Result<Array> {
12188 if let Some(parts) = y.as_boxes().filter(|p| !p.is_empty()) {
12189 return Ok(complex_or_real(root_form_coeffs(parts, span)?));
12190 }
12191 let mut c = poly_coeffs_relaxed(y, span)?;
12192 while c.len() > 1 && c[c.len() - 1] == cx::ZERO {
12193 c.pop();
12194 }
12195 if c.iter().all(|&k| k == cx::ZERO) {
12199 let pair = vec![Array::scalar_i64(0), Array::new(vec![0], Data::empty(DType::I64))];
12200 return Ok(Array::new(vec![2], Data::Box(pair.into())));
12201 }
12202 if c.len() < 2 {
12203 return Err(Error::domain("a polynomial's roots need a coefficient of x", span));
12204 }
12205 let lead = c[c.len() - 1];
12206 let monic: Vec<Cx> = c.iter().map(|&k| cx::div(k, lead)).collect();
12207 let roots = durand_kerner(&monic);
12208 let pair = vec![scalar_complex_or_real(lead), complex_or_real(roots)];
12209 Ok(Array::new(vec![2], Data::Box(pair.into())))
12210}
12211
12212fn durand_kerner(monic: &[Cx]) -> Vec<Cx> {
12219 let d = monic.len() - 1;
12220 let seed = [0.4, 0.9];
12221 let mut z: Vec<Cx> = Vec::with_capacity(d);
12222 let mut p = cx::ONE;
12223 for _ in 0..d {
12224 z.push(p);
12225 p = cx::mul(p, seed);
12226 }
12227 let value = |monic: &[Cx], at: Cx| {
12228 let mut v = cx::ZERO;
12229 for &k in monic.iter().rev() {
12230 v = cx::add(cx::mul(v, at), k);
12231 }
12232 v
12233 };
12234 for _ in 0..500 {
12235 let mut moved: f64 = 0.0;
12236 for i in 0..d {
12237 let mut denom = cx::ONE;
12238 for j in 0..d {
12239 if i != j {
12240 denom = cx::mul(denom, cx::sub(z[i], z[j]));
12241 }
12242 }
12243 if denom == cx::ZERO {
12244 continue;
12245 }
12246 let step = cx::div(value(monic, z[i]), denom);
12247 z[i] = cx::sub(z[i], step);
12248 moved = moved.max(step[0].hypot(step[1]));
12249 }
12250 if moved < 1e-15 {
12251 break;
12252 }
12253 }
12254 let mut z = polished_repeats(monic, z);
12255 for r in &mut z {
12257 if r[1].abs() < 1e-9 {
12258 r[1] = 0.0;
12259 }
12260 if r[0].abs() < 1e-12 {
12261 r[0] = 0.0;
12262 }
12263 }
12264 let coarse = |v: f64| -> f64 {
12270 if v == 0.0 || !v.is_finite() { v } else { format!("{v:.11e}").parse().unwrap_or(v) }
12271 };
12272 let mut keyed: Vec<([f64; 3], Cx)> =
12273 z.into_iter().map(|r| ([coarse(cx::abs(r)), coarse(r[0]), coarse(r[1])], r)).collect();
12274 keyed.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
12275 keyed.into_iter().map(|(_, r)| r).collect()
12276}
12277
12278fn polished_repeats(monic: &[Cx], z: Vec<Cx>) -> Vec<Cx> {
12293 let d = z.len();
12294 if d < 2 {
12295 return z;
12296 }
12297 let raw = coefficient_error(monic, &z);
12298 let scale = monic.iter().map(|&k| cx::abs(k)).fold(1.0f64, f64::max);
12299 let allowed = raw.max(1e-13 * scale);
12300 for reach in [1e-3, 1e-4, 1e-5, 1e-6, 1e-7] {
12301 let mut group: Vec<usize> = (0..d).collect();
12304 for i in 0..d {
12305 for j in 0..i {
12306 let apart = cx::abs(cx::sub(z[i], z[j]));
12307 let span = reach * (1.0 + cx::abs(z[i]).max(cx::abs(z[j])));
12308 if apart <= span {
12309 let (a, b) = (group[i], group[j]);
12310 let (keep, drop) = (a.min(b), a.max(b));
12311 for g in &mut group {
12312 if *g == drop {
12313 *g = keep;
12314 }
12315 }
12316 }
12317 }
12318 }
12319 let mut centre = vec![cx::ZERO; d];
12320 let mut size = vec![0usize; d];
12321 for i in 0..d {
12322 centre[group[i]] = cx::add(centre[group[i]], z[i]);
12323 size[group[i]] += 1;
12324 }
12325 if size.iter().all(|&n| n < 2) {
12326 return z;
12327 }
12328 let mut settled: Vec<Option<Cx>> = vec![None; d];
12329 for g in 0..d {
12330 if size[g] == 0 {
12331 continue;
12332 }
12333 let start = cx::div(centre[g], cx::from_real(size[g] as f64));
12334 settled[g] = Some(if size[g] < 2 {
12341 start
12342 } else {
12343 newton_at(&nth_derivative(monic, size[g] - 1), start)
12344 });
12345 }
12346 let out: Vec<Cx> = (0..d).map(|i| settled[group[i]].unwrap_or(z[i])).collect();
12347 if out.iter().all(|r| r[0].is_finite() && r[1].is_finite())
12348 && coefficient_error(monic, &out) <= allowed
12349 {
12350 return out;
12351 }
12352 }
12353 z
12354}
12355
12356fn newton_at(poly: &[Cx], start: Cx) -> Cx {
12358 let mut z = start;
12359 for _ in 0..40 {
12360 let (mut p, mut slope) = (cx::ZERO, cx::ZERO);
12361 for &k in poly.iter().rev() {
12362 slope = cx::add(cx::mul(slope, z), p);
12363 p = cx::add(cx::mul(p, z), k);
12364 }
12365 if slope == cx::ZERO {
12366 break;
12367 }
12368 let step = cx::div(p, slope);
12369 let next = cx::sub(z, step);
12370 if !next[0].is_finite() || !next[1].is_finite() {
12371 break;
12372 }
12373 z = next;
12374 if cx::abs(step) <= 1e-17 * (1.0 + cx::abs(z)) {
12375 break;
12376 }
12377 }
12378 z
12379}
12380
12381fn nth_derivative(c: &[Cx], k: usize) -> Vec<Cx> {
12383 let mut out = c.to_vec();
12384 for _ in 0..k {
12385 if out.len() < 2 {
12386 return vec![cx::ZERO];
12387 }
12388 out = out
12389 .iter()
12390 .enumerate()
12391 .skip(1)
12392 .map(|(i, &v)| cx::mul(v, cx::from_real(i as f64)))
12393 .collect();
12394 }
12395 out
12396}
12397
12398fn coefficient_error(monic: &[Cx], roots: &[Cx]) -> f64 {
12402 let mut built = vec![cx::ONE];
12403 for &r in roots {
12404 let mut next = vec![cx::ZERO; built.len() + 1];
12405 for (k, &c) in built.iter().enumerate() {
12406 next[k + 1] = cx::add(next[k + 1], c);
12407 next[k] = cx::sub(next[k], cx::mul(c, r));
12408 }
12409 built = next;
12410 }
12411 let mut worst: f64 = 0.0;
12412 for (k, &want) in monic.iter().enumerate() {
12413 let got = built.get(k).copied().unwrap_or(cx::ZERO);
12414 worst = worst.max(cx::abs(cx::sub(got, want)) / (1.0 + cx::abs(want)));
12415 }
12416 worst
12417}
12418
12419fn poly_deriv(y: &Array, span: Span) -> Result<Array> {
12422 let c = match y.as_boxes().filter(|p| !p.is_empty()) {
12425 Some(parts) => root_form_coeffs(parts, span)?,
12426 None => poly_coeffs_relaxed(y, span)?,
12427 };
12428 if c.len() < 2 {
12429 return Ok(Array::from_i64(vec![0]));
12430 }
12431 let out: Vec<Cx> =
12432 c.iter().enumerate().skip(1).map(|(k, &v)| cx::mul(v, cx::from_real(k as f64))).collect();
12433 Ok(narrow_numbers(complex_or_real(out)))
12434}
12435
12436fn poly_integral(x: &Array, y: &Array, span: Span) -> Result<Array> {
12438 let c = match y.as_boxes().filter(|p| !p.is_empty()) {
12442 Some(parts) => root_form_coeffs(parts, span)?,
12443 None => poly_coeffs(y, span)?,
12444 };
12445 let k = poly_coeffs(x, span)?;
12446 let mut out = vec![k.first().copied().unwrap_or(cx::ZERO)];
12447 for (i, &v) in c.iter().enumerate() {
12448 out.push(cx::div(v, cx::from_real((i + 1) as f64)));
12449 }
12450 Ok(narrow_numbers(complex_or_real(out)))
12451}
12452
12453fn narrow_numbers(a: Array) -> Array {
12457 let Data::F64(v) = &a.data else { return a };
12458 if v.iter().any(|x| !x.is_finite() || x.fract() != 0.0 || x.abs() > 9e15) {
12459 return a;
12460 }
12461 let values: Vec<i64> = v.iter().map(|&x| x as i64).collect();
12462 Array::new(a.shape, Data::I64(values.into()))
12463}
12464
12465fn characteristics(u: &Verb, y: &Array, span: Span) -> Result<Array> {
12469 let which = y.to_i64_vec().and_then(|v| v.first().copied());
12470 let chars = |s: String| Ok(Array::from_chars(s.chars().collect()));
12471 match which {
12472 Some(0) => {
12473 let ranks = u.ranks();
12474 Ok(Array::from_f64(
12475 ranks
12476 .iter()
12477 .map(|&r| if r == RANK_INF { f64::INFINITY } else { r as f64 })
12478 .collect(),
12479 ))
12480 }
12481 Some(-1) => match obverse(u) {
12485 Some(v) => chars(v.name()),
12486 None => Err(Error::not_yet(
12487 format!("the obverse of {} (no inverse is known)", u.name()),
12488 span,
12489 )),
12490 },
12491 Some(1) => match reduce_identity(u, 1, crate::Lang::J).as_ref().map(identity_spelling) {
12494 Some(s) => chars(s),
12495 None => Err(Error::not_yet(
12496 format!("the identity function of {} (u b. 1)", u.name()),
12497 span,
12498 )),
12499 },
12500 _ => Err(Error::not_yet("a verb characteristic other than 0, 1 and _1", span)),
12501 }
12502}
12503
12504fn identity_spelling(d: &Data) -> String {
12507 let one = Array::new(Vec::new(), d.slice(0, 1));
12508 let text = crate::fmt::format_array(&one, &crate::fmt::FmtOpts::J);
12509 format!("{} $~ }}.@$", text.trim())
12510}
12511
12512fn with_operands<R>(
12520 alpha: &Operand,
12521 omega: Option<&Operand>,
12522 ctx: &mut Ctx<'_>,
12523 f: impl FnOnce(&mut Ctx<'_>) -> Result<R>,
12524) -> Result<R> {
12525 let names = ["⍺⍺", "⍵⍵"];
12526 let operands = [Some(alpha), omega];
12527 let saved: Vec<(Option<Verb>, Option<Array>)> =
12528 names.iter().map(|n| (ctx.env.verb(n).cloned(), ctx.env.global(n))).collect();
12529 for (name, operand) in names.iter().zip(operands) {
12530 match operand {
12531 Some(Operand::Func(v)) => ctx.env.define((*name).to_string(), (**v).clone()),
12532 Some(Operand::Value(a)) => ctx.env.set_global((*name).to_string(), (**a).clone()),
12533 None => {}
12534 }
12535 }
12536 let out = f(ctx);
12537 for (name, (verb, value)) in names.iter().zip(saved) {
12538 match verb {
12539 Some(v) => ctx.env.define((*name).to_string(), v),
12540 None => ctx.env.undefine(name),
12541 }
12542 match value {
12543 Some(a) => ctx.env.set_global((*name).to_string(), a),
12544 None => ctx.env.unset_global(name),
12545 }
12546 }
12547 out
12548}
12549
12550fn is_mixed_simple(a: &Array) -> bool {
12554 let Some(items) = a.as_boxes() else { return false };
12555 if items.is_empty() || items.iter().any(|b| b.rank() != 0 || b.dtype() == DType::Box) {
12556 return false;
12557 }
12558 let mut common = Some(items[0].dtype());
12559 for b in &items[1..] {
12560 common = common.and_then(|t| DType::promote(t, b.dtype()));
12561 }
12562 common.is_none()
12563}
12564
12565fn nest(y: &Array) -> Array {
12568 if y.dtype() == DType::Box || y.rank() == 0 {
12569 return y.clone();
12570 }
12571 Array::boxed(y.clone())
12572}
12573
12574fn key_pairs(
12578 u: &Verb,
12579 keys: &Array,
12580 values: Option<&Array>,
12581 ctx: &mut Ctx<'_>,
12582 span: Span,
12583) -> Result<Array> {
12584 let base = if keys.rank() == 0 { Array::new(vec![1], keys.data.clone()) } else { keys.clone() };
12585 let n = base.items();
12586 if let Some(v) = values && v.items() != n {
12587 return Err(Error::new(
12588 ErrorKind::Length,
12589 format!("{n} key(s) for {} item(s)", v.items()),
12590 Some(span),
12591 ));
12592 }
12593 let groups = group_positions(&base, ctx.cfg.tol);
12594 let origin = ctx.cfg.rules.origin;
12595 let mut cells = Vec::with_capacity(groups.len());
12596 for (first, at) in &groups {
12597 let key = item_or_self(&base, *first);
12598 let group = match values {
12599 Some(v) => select_items(v, at),
12600 None => Array::from_i64(at.iter().map(|&i| origin + i as i64).collect()),
12601 };
12602 let monadic = matches!(u, Verb::Explicit(d) if d.left.is_none());
12605 cells.push(if monadic {
12606 u.monad(&group, ctx, span)?
12607 } else {
12608 u.dyad(&key, &group, ctx, span)?
12609 });
12610 }
12611 let count = cells.len();
12612 assemble(&[count], cells, span)
12613}
12614
12615fn group_positions(y: &Array, tol: Tol) -> Vec<(usize, Vec<usize>)> {
12618 let n = y.items();
12619 let m = y.item_size();
12620 let hashable = match y.dtype() {
12625 DType::Box | DType::Ext | DType::Rat => false,
12626 DType::F64 | DType::Complex => tol.ct == 0.0,
12627 _ => true,
12628 };
12629 if hashable {
12630 return if m == 1 {
12631 group_by_key(n, |i| elem_key(&y.data, i))
12632 } else {
12633 group_by_key(n, |i| (0..m).map(|k| elem_key(&y.data, i * m + k)).collect::<Vec<u64>>())
12634 };
12635 }
12636 let mut keys: Vec<Array> = Vec::new();
12637 let mut groups: Vec<(usize, Vec<usize>)> = Vec::new();
12638 for i in 0..n {
12639 let item = y.item(i);
12640 match keys.iter().position(|k| arrays_match(k, &item, tol)) {
12641 Some(at) => groups[at].1.push(i),
12642 None => {
12643 keys.push(item);
12644 groups.push((i, vec![i]));
12645 }
12646 }
12647 }
12648 groups
12649}
12650
12651fn group_by_key<K, F>(n: usize, key: F) -> Vec<(usize, Vec<usize>)>
12655where
12656 K: Eq + std::hash::Hash,
12657 F: Fn(usize) -> K,
12658{
12659 use std::collections::hash_map::Entry;
12660 let mut groups: Vec<(usize, Vec<usize>)> = Vec::new();
12661 let mut at: HashMap<K, usize, KeyHash> =
12662 HashMap::with_capacity_and_hasher(n.min(1 << 16), KeyHash);
12663 for i in 0..n {
12664 match at.entry(key(i)) {
12665 Entry::Occupied(e) => groups[*e.get()].1.push(i),
12666 Entry::Vacant(e) => {
12667 e.insert(groups.len());
12668 groups.push((i, vec![i]));
12669 }
12670 }
12671 }
12672 groups
12673}
12674
12675#[derive(Clone, Copy, Default)]
12681struct KeyHash;
12682
12683impl std::hash::BuildHasher for KeyHash {
12684 type Hasher = KeyHasher;
12685 fn build_hasher(&self) -> KeyHasher {
12686 KeyHasher(0)
12687 }
12688}
12689
12690struct KeyHasher(u64);
12691
12692impl std::hash::Hasher for KeyHasher {
12693 fn finish(&self) -> u64 {
12694 let mut x = self.0;
12695 x ^= x >> 33;
12696 x = x.wrapping_mul(0xff51_afd7_ed55_8ccd);
12697 x ^ (x >> 29)
12698 }
12699 fn write(&mut self, bytes: &[u8]) {
12700 for &b in bytes {
12701 self.write_u64(b as u64);
12702 }
12703 }
12704 fn write_u64(&mut self, n: u64) {
12705 self.0 = (self.0.rotate_left(5) ^ n).wrapping_mul(0x9e37_79b9_7f4a_7c15);
12706 }
12707 fn write_usize(&mut self, n: usize) {
12708 self.write_u64(n as u64);
12709 }
12710}
12711
12712fn format_spec(x: &Array, y: &Array, fmt: &FmtOpts, span: Span) -> Result<Array> {
12718 let spec = x
12719 .to_i64_vec()
12720 .ok_or_else(|| Error::domain("a format specification is whole numbers", span))?;
12721 if y.dtype() == DType::Box {
12722 return Err(Error::not_yet("format by specification of a nested array", span));
12723 }
12724 let cols = if y.rank() == 0 { 1 } else { y.shape[y.rank() - 1] };
12725 let rows = y.count() / cols.max(1);
12726 let pairs: Vec<(Option<i64>, i64)> = match spec.len() {
12728 1 => vec![(None, spec[0]); cols],
12729 2 => vec![(Some(spec[0]), spec[1]); cols],
12730 n if n == 2 * cols => spec.chunks(2).map(|c| (Some(c[0]), c[1])).collect(),
12731 n => {
12732 return Err(Error::new(
12733 ErrorKind::Length,
12734 format!("{n} specification value(s) for {cols} column(s)"),
12735 Some(span),
12736 ));
12737 }
12738 };
12739 if pairs.iter().any(|&(w, p)| w.is_some_and(|w| w < 0) || p < 0) {
12740 return Err(Error::domain("a format width and precision are nonnegative", span));
12741 }
12742 for &(w, p) in &pairs {
12746 crate::limits::count(w.unwrap_or(0) as u128, span)?;
12747 crate::limits::count(p as u128, span)?;
12748 }
12749 let numbers = y.to_f64_vec();
12750 let text = |i: usize, p: i64| -> String {
12751 match (&y.data, &numbers) {
12752 (Data::Char(v), _) => v[i].to_string(),
12753 (_, Some(v)) => {
12754 let s = format!("{:.*}", p as usize, v[i]);
12755 if v[i] < 0.0 { format!("{}{}", fmt.neg, &s[1..]) } else { s }
12756 }
12757 _ => String::new(),
12758 }
12759 };
12760 if y.dtype() != DType::Char && numbers.is_none() {
12761 return Err(Error::domain("format by specification takes numbers or characters", span));
12762 }
12763 let widths: Vec<usize> = pairs
12765 .iter()
12766 .enumerate()
12767 .map(|(c, &(w, p))| match w {
12768 Some(w) => w as usize,
12769 None => {
12770 (0..rows).map(|r| text(r * cols + c, p).chars().count()).max().unwrap_or(0) + 1
12771 }
12772 })
12773 .collect();
12774 let line = crate::limits::count(widths.iter().map(|&w| w as u128).sum(), span)?;
12775 let total = crate::limits::count(rows as u128 * line as u128, span)?;
12776 let mut out: Vec<char> = Vec::with_capacity(total);
12777 for r in 0..rows {
12778 for c in 0..cols {
12779 let s = text(r * cols + c, pairs[c].1);
12780 let len = s.chars().count();
12781 if len > widths[c] {
12782 return Err(Error::domain(
12783 format!("{s} does not fit a field {} wide", widths[c]),
12784 span,
12785 ));
12786 }
12787 out.extend(std::iter::repeat_n(' ', widths[c] - len));
12788 out.extend(s.chars());
12789 }
12790 }
12791 let mut shape = if y.rank() == 0 { Vec::new() } else { y.shape[..y.rank() - 1].to_vec() };
12792 shape.push(line);
12793 Ok(Array::new(shape, Data::Char(out.into())))
12794}
12795
12796fn sequential_machine(x: &Array, y: &Array, span: Span) -> Result<Array> {
12811 let Some(parts) = x.as_boxes() else {
12812 return Err(Error::domain("a sequential machine is a boxed description", span));
12813 };
12814 if x.rank() > 1 || !(2..=4).contains(&parts.len()) {
12815 return Err(Error::domain(
12816 "a sequential machine is 2 to 4 boxes: f ; s ; m ; ijrd",
12817 span,
12818 ));
12819 }
12820 let whole = |a: &Array, what: &str| -> Result<Vec<i64>> {
12821 a.to_i64_vec().ok_or_else(|| Error::domain(format!("{what} is whole numbers"), span))
12822 };
12823 let form = *whole(&parts[0], "a sequential machine's result form")?
12824 .first()
12825 .ok_or_else(|| Error::domain("a sequential machine needs a result form", span))?;
12826 if !(0..=5).contains(&form) {
12827 return Err(Error::domain(format!("{form} is not a result form of 0 to 5"), span));
12828 }
12829 let table = &parts[1];
12830 if table.rank() != 3 || table.shape[2] != 2 {
12831 return Err(Error::new(
12832 ErrorKind::Rank,
12833 "a sequential machine's transition table is shaped p q 2",
12834 Some(span),
12835 ));
12836 }
12837 let (states, classes) = (table.shape[0], table.shape[1]);
12838 let entries = whole(table, "a transition table")?;
12839 let map = parts.get(2).filter(|a| a.count() > 0);
12840 let start = match parts.get(3) {
12841 Some(a) => whole(a, "a sequential machine's starting values")?,
12842 None => Vec::new(),
12843 };
12844 let start = if start.is_empty() { vec![0, -1, 0, -1] } else { start };
12845 if start.len() != 4 {
12846 return Err(Error::new(
12847 ErrorKind::Length,
12848 "a sequential machine starts from four values: i j r d",
12849 Some(span),
12850 ));
12851 }
12852 let (mut i, mut word, mut state, ending) = (start[0], start[1], start[2], start[3]);
12853 let n = y.count() as i64;
12854
12855 let codes: Option<Vec<i64>> = match map {
12858 Some(m) => Some(whole(m, "a sequential machine's map")?),
12859 None => None,
12860 };
12861 let values: Vec<i64> = match (&y.data, &codes) {
12862 (Data::Char(v), Some(_)) => v.as_slice().iter().map(|&c| c as i64).collect(),
12863 (_, None) => y
12864 .to_i64_vec()
12865 .ok_or_else(|| Error::domain("a sequential machine over characters needs a map", span))?,
12866 _ => {
12867 return Err(Error::not_yet(
12868 "a sequential machine's map over a numeric argument (x's third box)",
12869 span,
12870 ));
12871 }
12872 };
12873 let class_at = |at: i64| -> Result<i64> {
12874 let raw = values[at as usize];
12875 let Some(m) = &codes else { return Ok(raw) };
12876 if raw < 0 || raw as usize >= m.len() {
12877 return Err(Error::new(
12878 ErrorKind::Domain,
12879 format!("{raw} is outside a map of {} entries", m.len()),
12880 Some(span),
12881 ));
12882 }
12883 Ok(m[raw as usize])
12884 };
12885
12886 let mut trace: Vec<i64> = Vec::new();
12887 let mut words: Vec<(i64, i64, i64)> = Vec::new();
12888 let mut emit = |word: i64, at: i64, place: i64| -> Result<()> {
12889 if word < 0 {
12890 return Err(Error::new(
12891 ErrorKind::Domain,
12892 "a sequential machine ended a word before one had begun",
12893 Some(span),
12894 ));
12895 }
12896 words.push((word, at - word, place));
12897 Ok(())
12898 };
12899 loop {
12900 let class = if i < n {
12901 class_at(i)?
12902 } else if ending >= 0 {
12903 ending
12904 } else {
12905 if word >= 0 {
12909 emit(word, i, classes as i64 * state)?;
12910 }
12911 break;
12912 };
12913 if state < 0 || state as usize >= states || class < 0 || class as usize >= classes {
12914 return Err(Error::new(
12915 ErrorKind::Domain,
12916 format!(
12917 "state {state} and class {class} are outside a {states} by {classes} table"
12918 ),
12919 Some(span),
12920 ));
12921 }
12922 let at = (state as usize * classes + class as usize) * 2;
12923 let (next, code) = (entries[at], entries[at + 1]);
12924 trace.extend_from_slice(&[i, word, state, class, next, code]);
12925 let place = class + classes as i64 * state;
12926 state = next;
12927 match code {
12928 0 => {}
12929 1 => word = i,
12930 2 => {
12931 emit(word, i, place)?;
12932 word = i;
12933 }
12934 3 => {
12935 emit(word, i, place)?;
12936 word = -1;
12937 }
12938 4 | 5 => {
12939 return Err(Error::not_yet(
12940 "a sequential machine's vector output (codes 4 and 5)",
12941 span,
12942 ));
12943 }
12944 6 => break,
12945 other => {
12946 return Err(Error::domain(
12947 format!("{other} is not a sequential machine output code"),
12948 span,
12949 ));
12950 }
12951 }
12952 if i >= n {
12953 break;
12954 }
12955 i += 1;
12956 }
12957 Ok(sequential_result(form, &words, &trace, y))
12958}
12959
12960fn sequential_result(form: i64, words: &[(i64, i64, i64)], trace: &[i64], y: &Array) -> Array {
12963 let piece = |&(at, len, _): &(i64, i64, i64)| {
12964 Array::new(vec![len as usize], y.data.slice(at as usize, (at + len) as usize))
12965 };
12966 match form {
12967 0 => Array::new(
12968 vec![words.len()],
12969 Data::Box(words.iter().map(piece).collect::<Vec<_>>().into()),
12970 ),
12971 1 => {
12972 let mut data = Data::empty(y.dtype());
12973 for w in words {
12974 data.extend_from(&piece(w).data);
12975 }
12976 let n = data.len();
12977 Array::new(vec![n], data)
12978 }
12979 2 => Array::new(
12980 vec![words.len(), 2],
12981 Data::I64(words.iter().flat_map(|&(at, len, _)| [at, len]).collect::<Vec<_>>().into()),
12982 ),
12983 3 => Array::from_i64(words.iter().map(|&(_, _, place)| place).collect()),
12984 4 => Array::new(
12985 vec![words.len(), 3],
12986 Data::I64(
12987 words
12988 .iter()
12989 .flat_map(|&(at, len, place)| [at, len, place])
12990 .collect::<Vec<_>>()
12991 .into(),
12992 ),
12993 ),
12994 _ => Array::new(vec![trace.len() / 6, 6], Data::I64(trace.to_vec().into())),
12995 }
12996}
12997
12998fn parse_numbers(x: &Array, y: &Array, span: Span) -> Result<Array> {
13003 if x.count() != 1 {
13004 return Err(Error::new(
13005 ErrorKind::Rank,
13006 "the stand-in for an unreadable word is one value",
13007 Some(span),
13008 ));
13009 }
13010 if y.count() == 0 {
13013 return Ok(Array::new(y.shape.clone(), Data::empty(DType::Bool)));
13014 }
13015 let Data::Char(text) = &y.data else {
13016 return Err(Error::domain("reading numbers from text needs characters", span));
13017 };
13018 let line: String = text.as_slice().iter().collect();
13019 crate::frontend::j::numbers_from_text(&line, x)
13020 .ok_or_else(|| Error::domain("the stand-in for an unreadable word is a number", span))
13021}
13022
13023fn format_field(value: f64, precision: usize, exponential: bool, neg: char) -> String {
13027 let sign = |s: String| match s.strip_prefix('-') {
13028 Some(rest) if rest.bytes().all(|b| !b.is_ascii_digit() || b == b'0') => rest.to_string(),
13031 Some(rest) => format!("{neg}{rest}"),
13032 None => s,
13033 };
13034 if !exponential {
13035 return sign(format!("{value:.precision$}"));
13036 }
13037 let text = format!("{value:.precision$e}");
13040 let (mantissa, exponent) = text.split_once('e').unwrap_or((text.as_str(), "0"));
13041 let exponent = match exponent.strip_prefix('-') {
13042 Some(rest) => format!("{neg}{rest}"),
13043 None => exponent.to_string(),
13044 };
13045 format!("{}e{exponent}", sign(mantissa.to_string()))
13046}
13047
13048fn format_spec_j(x: &Array, y: &Array, fmt: &FmtOpts, span: Span) -> Result<Array> {
13056 let Some(spec) = x.to_complex_vec() else {
13057 return Err(Error::domain("a format specification is numbers", span));
13058 };
13059 if y.dtype() == DType::Box {
13060 return Err(Error::domain("format by specification takes numbers", span));
13061 }
13062 let Some(values) = y.to_f64_vec() else {
13063 return Err(Error::domain("format by specification takes numbers", span));
13064 };
13065 let cols = if y.rank() == 0 { 1 } else { y.shape[y.rank() - 1] };
13066 let rows = if cols == 0 { 0 } else { y.count() / cols };
13067 let fields: Vec<[f64; 2]> = match spec.len() {
13068 1 => vec![spec[0]; cols],
13069 n if n == cols => spec,
13070 n => {
13071 return Err(Error::new(
13072 ErrorKind::Length,
13073 format!("{n} specification value(s) for {cols} column(s)"),
13074 Some(span),
13075 ));
13076 }
13077 };
13078 for &[w, d] in &fields {
13083 crate::limits::count(w.abs() as u128, span)?;
13084 crate::limits::count(d.max(0.0) as u128, span)?;
13085 }
13086 let text = |r: usize, c: usize| {
13087 let [w, d] = fields[c];
13088 let digits = if w == 0.0 { d.max(0.0) } else { d.max(0.0).min(w.abs()) };
13093 format_field(values[r * cols + c], digits as usize, w < 0.0, fmt.neg)
13094 };
13095 let widths: Vec<usize> = (0..cols)
13098 .map(|c| {
13099 let w = fields[c][0];
13100 if w != 0.0 {
13101 return w.abs() as usize;
13102 }
13103 let wide = (0..rows).map(|r| text(r, c).chars().count()).max().unwrap_or(0);
13104 wide + usize::from(c > 0)
13105 })
13106 .collect();
13107 let line = crate::limits::count(widths.iter().map(|&w| w as u128).sum(), span)?;
13108 let total = crate::limits::count(rows as u128 * line as u128, span)?;
13109 let mut out: Vec<char> = Vec::with_capacity(total);
13110 for r in 0..rows {
13111 for c in 0..cols {
13112 let s = text(r, c);
13113 let (lead, body) = match (fields[c][0] < 0.0, s.strip_prefix(fmt.neg)) {
13116 (false, _) => (String::new(), s.as_str()),
13117 (true, Some(rest)) => (fmt.neg.to_string(), rest),
13118 (true, None) => (" ".to_string(), s.as_str()),
13119 };
13120 let len = lead.chars().count() + body.chars().count();
13121 if len > widths[c] {
13122 out.extend(std::iter::repeat_n('*', widths[c]));
13123 continue;
13124 }
13125 if fields[c][0] < 0.0 {
13126 out.extend(lead.chars());
13127 out.extend(body.chars());
13128 out.extend(std::iter::repeat_n(' ', widths[c] - len));
13129 } else {
13130 out.extend(std::iter::repeat_n(' ', widths[c] - len));
13131 out.extend(body.chars());
13132 }
13133 }
13134 }
13135 let mut shape = if y.rank() == 0 { Vec::new() } else { y.shape[..y.rank() - 1].to_vec() };
13136 shape.push(line);
13137 Ok(Array::new(shape, Data::Char(out.into())))
13138}
13139
13140fn iota_apl(y: &Array, origin: i64, near: NearInt, span: Span) -> Result<Array> {
13144 if y.rank() > 1 {
13145 return Err(Error::new(
13146 ErrorKind::Rank,
13147 "the index generator takes a shape, which is a scalar or a vector",
13148 Some(span),
13149 ));
13150 }
13151 let dims = y
13152 .to_i64_vec_near(near)
13153 .ok_or_else(|| Error::domain("index generator needs an integer argument", span))?;
13154 if dims.iter().any(|&n| n < 0) {
13155 return Err(Error::domain("index generator needs nonnegative lengths", span));
13156 }
13157 if dims.len() <= 1 {
13158 let n = dims.first().copied().unwrap_or(0);
13159 crate::limits::count(n as u128, span)?;
13160 return Ok(Array::from_i64((0..n).map(|i| origin + i).collect()));
13161 }
13162 let shape: Vec<usize> = dims.iter().map(|&n| n as usize).collect();
13163 let total = crate::limits::elements(&shape, span)?;
13164 let mut cells = Vec::with_capacity(total);
13165 let mut coord = vec![0usize; shape.len()];
13166 for _ in 0..total {
13167 cells.push(Array::from_i64(coord.iter().map(|&c| origin + c as i64).collect()));
13168 odometer(&mut coord, &shape);
13169 }
13170 Ok(Array::new(shape, Data::Box(cells.into())))
13171}
13172
13173fn carry_exact(result: Array, y: &Array) -> Array {
13179 if !matches!(y.dtype(), DType::Ext | DType::Rat) {
13180 return result;
13181 }
13182 match result.data.cast(DType::Ext) {
13183 Some(data) => Array::new(result.shape, data),
13184 None => result,
13185 }
13186}
13187
13188fn carry_exact2(result: Array, x: &Array, y: &Array) -> Array {
13189 let widened = carry_exact(result, x);
13190 carry_exact(widened, y)
13191}
13192
13193fn truth_table(m: u8, x: &Array, y: &Array, span: Span) -> Result<Array> {
13196 let table = m & 15;
13197 let bit = |a: i64, b: i64| ((table >> (3 - (2 * a + b))) & 1) as i64;
13198 let xs = x
13199 .to_i64_vec()
13200 .ok_or_else(|| Error::domain("a boolean function takes integers", span))?;
13201 let ys = y
13202 .to_i64_vec()
13203 .ok_or_else(|| Error::domain("a boolean function takes integers", span))?;
13204 let (a, b) = (xs.first().copied().unwrap_or(0), ys.first().copied().unwrap_or(0));
13205 if m < 16 {
13206 if !(0..=1).contains(&a) || !(0..=1).contains(&b) {
13207 return Err(Error::domain(
13208 format!("{m} b. takes 0 and 1; {m} b. + 16 is the same function on every bit"),
13209 span,
13210 ));
13211 }
13212 return Ok(Array::scalar_bool(bit(a, b) != 0));
13213 }
13214 let mut out = 0i64;
13215 for k in 0..64 {
13216 if bit((a >> k) & 1, (b >> k) & 1) != 0 {
13217 out |= 1i64 << k;
13218 }
13219 }
13220 Ok(Array::scalar_i64(out))
13221}
13222
13223pub fn amend_at(
13228 base: &Array,
13229 slots: &[Option<Array>],
13230 value: &Array,
13231 origin: i64,
13232 near: NearInt,
13233 span: Span,
13234) -> Result<Array> {
13235 if slots.len() != base.rank() {
13236 return Err(Error::new(
13237 ErrorKind::Rank,
13238 format!(
13239 "indexed assignment needs one index per axis: {} slot(s) for a rank-{} value",
13240 slots.len(),
13241 base.rank()
13242 ),
13243 Some(span),
13244 ));
13245 }
13246 if !base.is_row_major() || !value.is_row_major() {
13249 let (b, v) = (base.to_row_major(), value.to_row_major());
13250 return amend_at(&b, slots, &v, origin, near, span);
13251 }
13252 let mut axes: Vec<Vec<usize>> = Vec::with_capacity(slots.len());
13254 let mut selected: Vec<usize> = Vec::new();
13255 for (k, slot) in slots.iter().enumerate() {
13256 let len = base.shape[k];
13257 let Some(idx) = slot else {
13258 axes.push((0..len).collect());
13259 selected.push(len);
13260 continue;
13261 };
13262 let Some(values) = idx.to_i64_vec_near(near) else {
13263 return Err(Error::new(
13264 ErrorKind::Type,
13265 "an index must be numeric",
13266 Some(span),
13267 ));
13268 };
13269 let mut positions = Vec::with_capacity(values.len());
13270 for v in values {
13271 let p = v - origin;
13272 if p < 0 || p as usize >= len {
13273 return Err(Error::new(
13274 ErrorKind::Domain,
13275 format!("index {v} is outside axis {k}, which has {len} element(s)"),
13276 Some(span),
13277 ));
13278 }
13279 positions.push(p as usize);
13280 }
13281 if idx.rank() > 0 {
13283 selected.push(positions.len());
13284 }
13285 axes.push(positions);
13286 }
13287 let count: usize = axes.iter().map(Vec::len).product();
13288 if value.rank() != 0 && (value.shape != selected || value.count() != count) {
13289 return Err(Error::new(
13290 ErrorKind::Shape,
13291 format!(
13292 "indexed assignment needs a scalar or a {} value, not a {} one",
13293 show_shape(&selected),
13294 show_shape(&value.shape)
13295 ),
13296 Some(span),
13297 ));
13298 }
13299 let dtype = DType::promote(base.dtype(), value.dtype()).ok_or_else(|| {
13302 Error::new(
13303 ErrorKind::Type,
13304 format!(
13305 "cannot put a {} value into a {} array",
13306 value.dtype().name(),
13307 base.dtype().name()
13308 ),
13309 Some(span),
13310 )
13311 })?;
13312 let mut out = base.cast(dtype).ok_or_else(|| Error::internal("promotion failed"))?;
13313 let src = value.cast(dtype).ok_or_else(|| Error::internal("promotion failed"))?;
13314 let strides = row_major_strides(&base.shape);
13315 let mut coords = vec![0usize; axes.len()];
13316 for n in 0..count {
13317 let mut rest = n;
13318 for k in (0..axes.len()).rev() {
13319 let len = axes[k].len();
13320 coords[k] = axes[k][rest % len];
13321 rest /= len;
13322 }
13323 let at: usize = coords.iter().zip(&strides).map(|(c, s)| c * s).sum();
13324 let from = if src.rank() == 0 { 0 } else { n };
13325 put_element(&mut out.data, at, &src.data, from);
13326 }
13327 Ok(out)
13328}
13329
13330fn row_major_strides(shape: &[usize]) -> Vec<usize> {
13331 let mut strides = vec![1usize; shape.len()];
13332 for k in (0..shape.len().saturating_sub(1)).rev() {
13333 strides[k] = strides[k + 1] * shape[k + 1];
13334 }
13335 strides
13336}
13337
13338fn put_element(dst: &mut Data, at: usize, src: &Data, from: usize) {
13340 match (dst, src) {
13341 (Data::Bool(d), Data::Bool(s)) => d.to_mut()[at] = s.as_slice()[from],
13342 (Data::I64(d), Data::I64(s)) => d.to_mut()[at] = s.as_slice()[from],
13343 (Data::Ext(d), Data::Ext(s)) => d.to_mut()[at] = s.as_slice()[from].clone(),
13344 (Data::Rat(d), Data::Rat(s)) => d.to_mut()[at] = s.as_slice()[from].clone(),
13345 (Data::F64(d), Data::F64(s)) => d.to_mut()[at] = s.as_slice()[from],
13346 (Data::Char(d), Data::Char(s)) => d.to_mut()[at] = s.as_slice()[from],
13347 (Data::Box(d), Data::Box(s)) => d.to_mut()[at] = s.as_slice()[from].clone(),
13348 _ => debug_assert!(false, "amend across types"),
13350 }
13351}
13352
13353fn agenda_pick(
13356 vs: &[Verb],
13357 w: &Verb,
13358 x: Option<&Array>,
13359 y: &Array,
13360 ctx: &mut Ctx<'_>,
13361 span: Span,
13362) -> Result<Verb> {
13363 let chosen = match x {
13364 None => w.monad(y, ctx, span)?,
13365 Some(x) => w.dyad(x, y, ctx, span)?,
13366 };
13367 let at = chosen
13368 .to_i64_vec_near(ctx.cfg.near())
13369 .and_then(|v| v.first().copied())
13370 .ok_or_else(|| Error::domain("an agenda index must be an integer", span))?;
13371 pick_gerund(vs, at, span)
13372}
13373
13374pub(crate) fn pick_gerund(vs: &[Verb], at: i64, span: Span) -> Result<Verb> {
13377 usize::try_from(at)
13378 .ok()
13379 .and_then(|k| vs.get(k))
13380 .cloned()
13381 .ok_or_else(|| {
13382 Error::domain(
13383 format!("agenda {at} is out of range: the gerund has {} verbs", vs.len()),
13384 span,
13385 )
13386 })
13387}
13388
13389fn evoke(
13394 vs: &[Verb],
13395 form: i64,
13396 x: Option<&Array>,
13397 y: &Array,
13398 ctx: &mut Ctx<'_>,
13399 span: Span,
13400) -> Result<Array> {
13401 if vs.is_empty() {
13402 return Err(Error::domain("an evoked gerund is empty", span));
13403 }
13404 if form == 0 {
13405 let mut cells = Vec::with_capacity(vs.len());
13406 for v in vs {
13407 cells.push(match x {
13408 None => v.monad(y, ctx, span)?,
13409 Some(x) => v.dyad(x, y, ctx, span)?,
13410 });
13411 }
13412 return assemble(&[vs.len()], cells, span);
13413 }
13414 if x.is_some() {
13415 return Err(Error::domain("m`:3 has no dyadic meaning", span));
13416 }
13417 let items = if y.rank() == 0 { vec![y.clone()] } else { y.cells(1) };
13418 let Some((last, rest)) = items.split_last() else {
13419 return Err(Error::domain("m`:3 needs an argument with items", span));
13420 };
13421 let mut acc = last.clone();
13422 for (i, item) in rest.iter().enumerate().rev() {
13423 acc = vs[i % vs.len()].dyad(item, &acc, ctx, span)?;
13424 }
13425 Ok(acc)
13426}
13427
13428fn stencil(u: &Verb, w: &[i64], y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
13433 if w.len() > y.rank() {
13434 return Err(Error::new(
13435 ErrorKind::Rank,
13436 format!("a stencil of {} axis/axes into a rank-{} value", w.len(), y.rank()),
13437 Some(span),
13438 ));
13439 }
13440 if w.iter().any(|&n| n <= 0) {
13441 return Err(Error::domain("a stencil window is a positive size", span));
13442 }
13443 let y = y.to_row_major();
13444 let k = w.len();
13445 let st = strides(&y.shape);
13446 let frame: Vec<usize> = y.shape[..k].to_vec();
13447 let mut wshape: Vec<usize> = w.iter().map(|&n| n as usize).collect();
13449 wshape.extend_from_slice(&y.shape[k..]);
13450 let inner: usize = y.shape[k..].iter().product();
13451 let total: usize = frame.iter().product();
13452 let mut cells = Vec::with_capacity(total);
13453 let mut at = vec![0usize; frame.len()];
13454 let mut coord = vec![0usize; k];
13455 for _ in 0..total {
13456 let mut data = Data::empty(y.dtype());
13457 coord.iter_mut().for_each(|c| *c = 0);
13458 let count: usize = w.iter().map(|&n| n as usize).product();
13459 for _ in 0..count {
13460 let mut base = 0usize;
13461 let mut inside = true;
13462 for a in 0..k {
13463 let off = at[a] as i64 + coord[a] as i64 - (w[a] - 1) / 2;
13464 if off < 0 || off >= y.shape[a] as i64 {
13465 inside = false;
13466 break;
13467 }
13468 base += off as usize * st[a];
13469 }
13470 for j in 0..inner {
13471 if inside {
13472 push_elem(&mut data, &y.data, base + j);
13473 } else {
13474 data.push_fill();
13475 }
13476 }
13477 odometer(&mut coord, &wshape[..k]);
13478 }
13479 cells.push(u.monad(&Array::new(wshape.clone(), data), ctx, span)?);
13480 odometer(&mut at, &frame);
13481 }
13482 assemble(&frame, cells, span)
13483}
13484
13485fn folds_eagerly(u: &Verb) -> bool {
13492 let Verb::Reduce(inner) = u else { return false };
13493 matches!(
13494 **inner,
13495 Verb::Prim(Prim {
13496 dyad: DyadOp::Scalar(
13497 ScalarDyad::Add
13498 | ScalarDyad::Mul
13499 | ScalarDyad::Min
13500 | ScalarDyad::Max
13501 | ScalarDyad::Gcd
13502 ),
13503 ..
13504 })
13505 )
13506}
13507
13508fn outfix(u: &Verb, x: &Array, y: &Array, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
13512 let k = one_int(x, "an outfix width", ctx.cfg.near(), span)?;
13513 let n = y.items() as i64;
13514 let list = as_list(y);
13515 let starts: Vec<i64> = if k < 0 {
13523 let step = i128::from(k.unsigned_abs());
13524 let count = (i128::from(n) + step - 1) / step;
13525 (0..count).map(|i| (i * step) as i64).collect()
13526 } else {
13527 (0..=(n - k)).collect()
13528 };
13529 let width = k.unsigned_abs() as usize;
13530 if !list.dtype().is_numeric()
13540 && u.is_pure()
13541 && folds_eagerly(u)
13542 && n >= 1
13543 && (n >= 2 || !starts.is_empty())
13544 {
13545 let probe =
13551 if n == 1 { select_items(&list, &[0, 0]) } else { list.clone() };
13552 u.monad(&probe, ctx, span)?;
13553 }
13554 if starts.is_empty() {
13558 let cell = u.is_pure().then(|| select_items(&list, &[]));
13559 return Ok(empty_frame(&[0], list.dtype(), cell, ctx, |cell, c| u.monad(cell, c, span)));
13560 }
13561 let mut cells = Vec::with_capacity(starts.len());
13562 for start in starts {
13563 let start = start as usize;
13564 let keep: Vec<usize> =
13565 (0..n as usize).filter(|&i| i < start || i >= start + width).collect();
13566 cells.push(u.monad(&select_items(&list, &keep), ctx, span)?);
13567 }
13568 assemble(&[cells.len()], cells, span)
13569}
13570
13571pub(crate) fn obverse(v: &Verb) -> Option<Verb> {
13583 Some(match v {
13584 Verb::Prim(p) => prim_obverse(v, p)?,
13585 Verb::WithObverse(_, w) => (**w).clone(),
13587 Verb::Atop(f, g) => {
13589 Verb::Atop(Box::new(obverse(g)?), Box::new(obverse(f)?))
13590 }
13591 Verb::Compose(f, g) | Verb::Beside(f, g) => {
13592 Verb::Atop(Box::new(obverse(g)?), Box::new(obverse(f)?))
13593 }
13594 Verb::Rank(f, r) => Verb::Rank(Box::new(obverse(f)?), *r),
13595 Verb::Fit(f, n) => Verb::Fit(Box::new(obverse(f)?), *n),
13596 Verb::Each(f, rule) => Verb::Each(Box::new(obverse(f)?), *rule),
13599 Verb::Reduce(f) if is_dyad(f, DyadOp::Scalar(ScalarDyad::Mul)) => named("q:")?,
13602 Verb::Windowed(f, kind) => scan_obverse(f, *kind)?,
13605 Verb::PowerN(f, Power::Times(n)) => {
13607 Verb::PowerN(Box::new(obverse(f)?), Power::Times(*n))
13608 }
13609 Verb::BondLeft(m, f) => bond_obverse(m, f, true)?,
13610 Verb::BondRight(f, n) => bond_obverse(n, f, false)?,
13611 _ => return None,
13612 })
13613}
13614
13615fn named(spelling: &'static str) -> Option<Verb> {
13617 crate::frontend::j::verb_named(spelling)
13618}
13619
13620fn made(name: &'static str, monad: MonadOp, ranks: [i64; 3]) -> Verb {
13623 Verb::Prim(Prim { name, monad, dyad: DyadOp::None, ranks })
13624}
13625
13626fn is_dyad(v: &Verb, op: DyadOp) -> bool {
13627 matches!(v, Verb::Prim(p) if p.dyad == op)
13628}
13629
13630fn atop(f: Verb, g: Verb) -> Verb {
13631 Verb::Atop(Box::new(f), Box::new(g))
13632}
13633
13634fn prim_obverse(v: &Verb, p: &Prim) -> Option<Verb> {
13636 use ScalarMonad as SM;
13637 if matches!(
13644 p.monad,
13645 MonadOp::Scalar(SM::Conj | SM::Neg | SM::Recip | SM::OneMinus)
13646 | MonadOp::Reverse
13647 | MonadOp::TransposeAxes
13648 | MonadOp::GradeUp { .. }
13649 | MonadOp::CycleForm
13650 | MonadOp::MatrixInverse
13651 | MonadOp::PolyRoots
13652 | MonadOp::Same
13653 ) {
13654 return Some(v.clone());
13655 }
13656 if p.dyad == DyadOp::Copy {
13661 return Some(expand_verb());
13662 }
13663 let built = match p.monad {
13664 MonadOp::Scalar(SM::Imaginary) => atop(named("-")?, named("j.")?),
13667 MonadOp::Scalar(SM::Polar) => {
13670 atop(atop(named("-")?, named("j.")?), named("^.")?)
13671 }
13672 MonadOp::Scalar(SM::Pi) => Verb::BondLeft(
13675 Array::scalar_f64(std::f64::consts::FRAC_1_PI),
13676 Box::new(named("*")?),
13677 ),
13678 MonadOp::ComplexParts { polar } => Verb::Rank(
13681 Box::new(Verb::Reduce(Box::new(named(if polar { "r." } else { "j." })?))),
13682 [1, RANK_INF, RANK_INF],
13683 ),
13684 MonadOp::GradeDown { origin } => atop(
13686 Verb::Prim(Prim {
13687 name: "/:",
13688 monad: MonadOp::GradeUp { origin },
13689 dyad: DyadOp::GradeSelect { down: false },
13690 ranks: [RANK_INF, RANK_INF, RANK_INF],
13691 }),
13692 named("|.")?,
13693 ),
13694 MonadOp::PrimeFactors => {
13697 Verb::Rank(Box::new(Verb::Reduce(Box::new(named("*")?))), [1, RANK_INF, RANK_INF])
13698 }
13699 MonadOp::Words => atop(
13702 named("}:")?,
13703 atop(
13704 named(";")?,
13705 Verb::Each(
13706 Box::new(Verb::BondRight(
13707 Box::new(named(",")?),
13708 Array::from_chars(vec![' ']),
13709 )),
13710 Enclose::Always,
13711 ),
13712 ),
13713 ),
13714 MonadOp::ToExact => Verb::BondLeft(Array::scalar_i64(-1), Box::new(named("x:")?)),
13716 MonadOp::Unicode { .. } => {
13717 Verb::BondLeft(Array::scalar_i64(3), Box::new(named("u:")?))
13718 }
13719 MonadOp::Symbols => Verb::BondLeft(Array::scalar_i64(5), Box::new(named("s:")?)),
13720 MonadOp::NthPrime => made("p:^:_1", MonadOp::PrimeCount, [0, 0, 0]),
13722 MonadOp::Sparse => {
13723 made("$.^:_1", MonadOp::Dense, [RANK_INF, RANK_INF, RANK_INF])
13724 }
13725 MonadOp::Indices { origin: 0, boxed_coords: false } => {
13726 made("I.^:_1", MonadOp::IndicesInverse, [1, RANK_INF, RANK_INF])
13727 }
13728 MonadOp::Format if p.name == "⍕" => Verb::Prim(Prim {
13731 name: "⍎",
13732 monad: MonadOp::Execute { apl: true },
13733 dyad: DyadOp::None,
13734 ranks: [1, RANK_INF, RANK_INF],
13735 }),
13736 MonadOp::Format => named("\".")?,
13737 MonadOp::Execute { apl: true } => Verb::Prim(Prim {
13738 name: "⍕",
13739 monad: MonadOp::Format,
13740 dyad: DyadOp::FormatSpec,
13741 ranks: [RANK_INF, 1, RANK_INF],
13742 }),
13743 MonadOp::Execute { apl: false } => named("\":")?,
13744 _ => {
13745 let by_monad: Option<&'static str> = match p.monad {
13746 MonadOp::Scalar(SM::Exp) => Some("^."),
13747 MonadOp::Scalar(SM::Ln) => Some("^"),
13748 MonadOp::Scalar(SM::Sqrt) => Some("*:"),
13749 MonadOp::Scalar(SM::Square) => Some("%:"),
13750 MonadOp::Scalar(SM::Double) => Some("-:"),
13751 MonadOp::Scalar(SM::Halve) => Some("+:"),
13752 MonadOp::Scalar(SM::Inc) => Some("<:"),
13753 MonadOp::Scalar(SM::Dec) => Some(">:"),
13754 MonadOp::Enclose(_) => Some(">"),
13755 MonadOp::Open => Some("<"),
13756 MonadOp::DecodeBits => Some("#:"),
13757 MonadOp::EncodeBits => Some("#."),
13758 MonadOp::Itemize => Some("{."),
13759 MonadOp::Head => Some(",:"),
13760 _ => None,
13761 };
13762 named(by_monad?)?
13763 }
13764 };
13765 Some(built)
13766}
13767
13768fn expand_verb() -> Verb {
13770 Verb::Prim(Prim {
13771 name: "#^:_1",
13772 monad: MonadOp::None,
13773 dyad: DyadOp::Expand,
13774 ranks: [RANK_INF, 1, RANK_INF],
13775 })
13776}
13777
13778fn scan_obverse(f: &Verb, kind: WindowKind) -> Option<Verb> {
13786 use ScalarDyad as SD;
13787 let Verb::Reduce(inner) = f else { return None };
13788 let Verb::Prim(p) = &**inner else { return None };
13789 let DyadOp::Scalar(op) = p.dyad else { return None };
13790 let suffix = match kind {
13791 WindowKind::Prefix | WindowKind::Scan => false,
13792 WindowKind::Suffix => true,
13793 };
13794 let fill = match op {
13797 SD::Add | SD::Sub => 0.0,
13798 SD::Mul | SD::DivJ | SD::DivApl => 1.0,
13799 _ => return None,
13800 };
13801 let shift = Verb::ShiftFill(Array::scalar_f64(fill));
13802 let neighbour = if suffix {
13803 Verb::BondLeft(Array::scalar_i64(1), Box::new(shift))
13804 } else {
13805 shift
13806 };
13807 let step = match (op, suffix) {
13810 (SD::Add, false) | (SD::Sub, false) => named("-")?,
13811 (SD::Add, true) => named("-")?,
13812 (SD::Sub, true) => named("+")?,
13813 (SD::Mul, _) | (SD::DivJ | SD::DivApl, false) => named("%")?,
13814 (SD::DivJ | SD::DivApl, true) => named("*")?,
13815 _ => return None,
13816 };
13817 let differences = Verb::Hook(Box::new(step), Box::new(neighbour));
13818 let alternate = matches!((op, suffix), (SD::Sub, false) | (SD::DivJ | SD::DivApl, false));
13821 if !alternate {
13822 return Some(differences);
13823 }
13824 let signs = atop(
13825 Verb::BondRight(Box::new(named("$")?), Array::from_i64(vec![1, -1])),
13826 named("#")?,
13827 );
13828 let apply = if matches!(op, SD::Sub) { named("*")? } else { named("^")? };
13829 Some(Verb::Fork(Box::new(differences), Box::new(apply), Box::new(signs)))
13830}
13831
13832fn bond_obverse(n: &Array, f: &Verb, left: bool) -> Option<Verb> {
13836 if let Verb::Commute(g) = f {
13839 if !left {
13840 return bond_obverse(n, g, true);
13841 }
13842 return None;
13843 }
13844 if let Some(v) = structural_bond_obverse(n, f, left) {
13845 return Some(v);
13846 }
13847 let Verb::Prim(p) = f else { return None };
13848 let bond = |name: &'static str, arg: &Array| -> Option<Verb> {
13849 let g = named(name)?;
13850 Some(if left {
13851 Verb::BondLeft(arg.clone(), Box::new(g))
13852 } else {
13853 Verb::BondRight(Box::new(g), arg.clone())
13854 })
13855 };
13856 use ScalarDyad as SD;
13857 let DyadOp::Scalar(op) = p.dyad else { return None };
13858 if matches!(op, SD::Circle) {
13859 return left.then(|| Some(Verb::BondLeft(negated(n)?, Box::new(named("o.")?))))?;
13862 }
13863 match (op, left) {
13864 (SD::Sub | SD::DivJ | SD::DivApl, true) => bond(p.name, n),
13866 (SD::Add, _) => Some(Verb::BondRight(Box::new(named("-")?), n.clone())),
13870 (SD::Mul, _) => Some(Verb::BondRight(Box::new(named("%")?), n.clone())),
13871 (SD::Sub, false) => bond("+", n),
13872 (SD::DivJ | SD::DivApl, false) => bond("*", n),
13873 (SD::Pow, false) => Some(Verb::BondLeft(n.clone(), Box::new(named("%:")?))),
13875 (SD::Pow, true) => Some(Verb::BondLeft(n.clone(), Box::new(named("^.")?))),
13876 (SD::Log, true) => Some(Verb::BondLeft(n.clone(), Box::new(named("^")?))),
13880 (SD::Root, true) => Some(Verb::BondRight(Box::new(named("^")?), n.clone())),
13881 (SD::Log, false) => Some(Verb::BondRight(Box::new(named("%:")?), n.clone())),
13884 (SD::Root, false) => Some(Verb::BondRight(Box::new(named("^.")?), n.clone())),
13885 _ => None,
13886 }
13887}
13888
13889fn negated(n: &Array) -> Option<Array> {
13892 if let Some(v) = n.to_i64_vec() {
13893 let out: Vec<i64> = v.iter().map(|&k| -k).collect();
13894 return Some(Array::new(n.shape.clone(), Data::I64(out.into())));
13895 }
13896 let v = n.to_f64_vec()?;
13897 let out: Vec<f64> = v.iter().map(|&k| -k).collect();
13898 Some(Array::new(n.shape.clone(), Data::F64(out.into())))
13899}
13900
13901fn one_number(n: &Array) -> Option<f64> {
13904 match n.to_f64_vec()?[..] {
13905 [v] if n.rank() <= 1 => Some(v),
13906 _ => None,
13907 }
13908}
13909
13910fn structural_bond_obverse(n: &Array, f: &Verb, left: bool) -> Option<Verb> {
13914 let p = match f {
13918 Verb::Prim(p) => p,
13919 Verb::Rank(inner, _) => match &**inner {
13920 Verb::Prim(p) => p,
13921 _ => return None,
13922 },
13923 _ => return None,
13924 };
13925 match (p.dyad, left) {
13926 (DyadOp::Rotate | DyadOp::RotateApl { .. }, true) => {
13928 Some(Verb::BondLeft(negated(n)?, Box::new(f.clone())))
13929 }
13930 (DyadOp::Copy, true) => Some(Verb::BondLeft(n.clone(), Box::new(expand_verb()))),
13932 (DyadOp::AppendLeading | DyadOp::AppendLast, _) => {
13936 let items = if n.rank() == 0 { 1 } else { n.shape[0] } as i64;
13937 let count = if left { items } else { -items };
13938 Some(Verb::BondLeft(Array::scalar_i64(count), Box::new(named("}.")?)))
13939 }
13940 (DyadOp::Drop, true) => {
13944 let k = one_number(n)?;
13945 let size = Verb::Atop(
13946 Box::new(Verb::BondLeft(Array::scalar_f64(k.abs()), Box::new(named("+")?))),
13947 Box::new(named("#")?),
13948 );
13949 let width = if k >= 0.0 { atop(named("-")?, size) } else { size };
13950 Some(Verb::Hook(
13951 Box::new(Verb::Commute(Box::new(named("{.")?))),
13952 Box::new(width),
13953 ))
13954 }
13955 (DyadOp::Decode, true) => {
13958 let width = atop(
13959 Verb::BondRight(Box::new(named("$")?), n.clone()),
13960 atop(
13961 named(">:")?,
13962 atop(
13963 named("<.")?,
13964 atop(
13965 Verb::BondLeft(n.clone(), Box::new(named("^.")?)),
13966 atop(
13967 Verb::BondLeft(Array::scalar_i64(1), Box::new(named(">.")?)),
13968 atop(
13969 Verb::Reduce(Box::new(named(">.")?)),
13970 atop(named("|")?, named(",")?),
13971 ),
13972 ),
13973 ),
13974 ),
13975 ),
13976 );
13977 Some(Verb::Fork(Box::new(width), Box::new(named("#:")?), Box::new(named("]")?)))
13978 }
13979 (DyadOp::Encode, true) => Some(Verb::BondLeft(n.clone(), Box::new(named("#.")?))),
13981 (DyadOp::AnagramFrom | DyadOp::Permute, true) => {
13984 let spelling = if p.dyad == DyadOp::AnagramFrom { "A." } else { "C." };
13985 let inverse = atop(
13986 atop(named("/:")?, Verb::BondLeft(n.clone(), Box::new(named(spelling)?))),
13987 atop(named("i.")?, named("#")?),
13988 );
13989 Some(Verb::Fork(Box::new(inverse), Box::new(named("{")?), Box::new(named("]")?)))
13990 }
13991 _ => None,
13992 }
13993}
13994
13995fn self_classify(y: &Array, tol: Tol) -> Array {
14000 let items = if y.rank() == 0 { 1 } else { y.items() };
14001 let keys = nub(&as_list(y), tol);
14002 let rows = keys.items();
14003 let mut out = Vec::with_capacity(rows * items);
14004 for i in 0..rows {
14005 let key = item_or_self(&keys, i);
14006 for j in 0..items {
14007 out.push(arrays_match(&key, &item_or_self(y, j), tol) as u8);
14008 }
14009 }
14010 Array::new(vec![rows, items], Data::Bool(out.into()))
14011}
14012
14013fn nub_sieve(y: &Array, tol: Tol, lang: crate::Lang) -> Array {
14020 let by_element = lang == crate::Lang::Apl;
14021 let n = if by_element {
14022 y.count()
14023 } else if y.rank() == 0 {
14024 1
14025 } else {
14026 y.items()
14027 };
14028 let mut seen: Vec<Array> = Vec::new();
14029 let mut out = Vec::with_capacity(n);
14030 for i in 0..n {
14031 let cell = if by_element {
14032 Array::new(Vec::new(), y.data.slice(i, i + 1))
14033 } else {
14034 item_or_self(y, i)
14035 };
14036 let fresh = !seen.iter().any(|s| arrays_match(s, &cell, tol));
14037 if fresh {
14038 seen.push(cell);
14039 }
14040 out.push(fresh as u8);
14041 }
14042 let shape = if by_element { y.shape.clone() } else { vec![n] };
14043 Array::new(shape, Data::Bool(out.into()))
14044}
14045
14046fn as_list(y: &Array) -> Array {
14048 if y.rank() == 0 { Array::new(vec![1], y.data.clone()) } else { y.clone() }
14049}
14050
14051fn conforming_cells(y: &Array, item_rank: usize) -> Vec<Array> {
14056 let frame_rank = y.rank().saturating_sub(item_rank);
14057 let nf: usize = y.shape[..frame_rank].iter().product();
14058 (0..nf).map(|i| y.cell_at(frame_rank, i)).collect()
14059}
14060
14061fn item_marks(y: &Array, x: &Array, tol: Tol) -> Vec<bool> {
14063 let n = if y.rank() == 0 { 1 } else { y.items() };
14064 let item_rank = y.rank().saturating_sub(1);
14065 let against = conforming_cells(x, item_rank);
14066 (0..n)
14067 .map(|i| {
14068 let cell = item_or_self(y, i);
14069 against.iter().any(|c| arrays_match(&cell, c, tol))
14070 })
14071 .collect()
14072}
14073
14074fn set_less(x: &Array, y: &Array, tol: Tol) -> Array {
14076 let xs = as_list(x);
14077 let marks = item_marks(&xs, y, tol);
14078 let keep: Vec<usize> = (0..marks.len()).filter(|&i| !marks[i]).collect();
14079 select_items(&xs, &keep)
14080}
14081
14082fn set_rank(cfg: EvalCfg, what: &str, x: &Array, y: &Array, span: Span) -> Result<()> {
14086 if cfg.rules.lang == crate::Lang::Apl && (x.rank() > 1 || y.rank() > 1) {
14087 return Err(Error::new(
14088 ErrorKind::Rank,
14089 format!("{what} takes vectors, not rank {} and rank {}", x.rank(), y.rank()),
14090 Some(span),
14091 ));
14092 }
14093 Ok(())
14094}
14095
14096fn intersect_items(x: &Array, y: &Array, tol: Tol) -> Array {
14098 let xs = as_list(x);
14099 let marks = item_marks(&xs, y, tol);
14100 let keep: Vec<usize> = (0..marks.len()).filter(|&i| marks[i]).collect();
14101 select_items(&xs, &keep)
14102}
14103
14104fn union_items(x: &Array, y: &Array, tol: Tol, span: Span) -> Result<Array> {
14107 let xs = as_list(x);
14108 let ys = as_list(y);
14109 let marks = item_marks(&ys, &xs, tol);
14110 let mut extra: Vec<usize> = Vec::new();
14111 for (i, &seen) in marks.iter().enumerate() {
14112 if seen {
14113 continue;
14114 }
14115 let cell = item_or_self(&ys, i);
14116 if !extra.iter().any(|&j| arrays_match(&item_or_self(&ys, j), &cell, tol)) {
14117 extra.push(i);
14118 }
14119 }
14120 catenate(&xs, &select_items(&ys, &extra), true, false, span)
14121}
14122
14123fn find_seq(x: &Array, y: &Array, tol: Tol, apl: bool, span: Span) -> Result<Array> {
14133 let (xr, yr) = (x.rank(), y.rank());
14134 if !apl && xr == 0 && yr == 0 {
14138 let hit = arrays_match(x, y, tol);
14139 return Ok(Array::new(Vec::new(), Data::Bool(vec![u8::from(hit)].into())));
14140 }
14141 if apl && xr > yr {
14142 return Ok(Array::new(y.shape.clone(), Data::Bool(vec![0u8; y.count()].into())));
14144 }
14145 if !apl && xr.max(1) != yr {
14146 return Err(Error::new(
14147 ErrorKind::Rank,
14148 format!("a rank-{xr} pattern in a rank-{yr} argument"),
14149 Some(span),
14150 ));
14151 }
14152 let mut pattern = vec![1usize; yr];
14153 pattern[yr - xr..].copy_from_slice(&x.shape);
14154 let n = y.count();
14155 let mut out = vec![0u8; n];
14156 let (xrm, yrm) = (x.to_row_major(), y.to_row_major());
14157 let yst = strides(&y.shape);
14158 let cells: usize = pattern.iter().product();
14159 let mut at = vec![0usize; yr];
14160 for slot in out.iter_mut() {
14161 if (0..yr).all(|a| at[a] + pattern[a] <= y.shape[a]) {
14162 let mut off = vec![0usize; yr];
14163 let mut hit = true;
14164 for k in 0..cells {
14165 let i: usize = (0..yr).map(|a| (at[a] + off[a]) * yst[a]).sum();
14166 if !arrays_match(&atom(&xrm, k), &atom(&yrm, i), tol) {
14167 hit = false;
14168 break;
14169 }
14170 odometer(&mut off, &pattern);
14171 }
14172 *slot = hit as u8;
14173 }
14174 odometer(&mut at, &y.shape);
14175 }
14176 Ok(Array::new(y.shape.clone(), Data::Bool(out.into())))
14177}
14178
14179fn bool_dyad(op: BoolDyad, x: &Array, y: &Array, cfg: EvalCfg, span: Span) -> Result<Array> {
14183 let bit = |a: &Array| -> Result<u8> {
14184 match a.to_i64_vec().as_deref() {
14185 Some([0]) => Ok(0),
14186 Some([1]) => Ok(1),
14187 _ => Err(Error::domain("this verb reads values of 0 or 1", span)),
14188 }
14189 };
14190 let _ = cfg;
14191 let (a, b) = (bit(x)?, bit(y)?);
14192 let v = match op {
14193 BoolDyad::Nor => u8::from(a == 0 && b == 0),
14194 BoolDyad::Nand => u8::from(a == 0 || b == 0),
14195 };
14196 Ok(Array::new(vec![], Data::Bool(vec![v].into())))
14197}
14198
14199fn item_ranks(y: &Array, rules: Rules, span: Span) -> Result<Vec<usize>> {
14205 check_gradable(y, rules, span)?;
14206 if !y.dtype().is_numeric() {
14207 return Err(Error::domain("an anagram index needs numbers", span));
14208 }
14209 let order = grade_order(&as_list(y), false, Grading::of(rules, rules.tol()));
14210 let mut ranks = vec![0usize; order.len()];
14211 for (place, &i) in order.iter().enumerate() {
14212 ranks[i] = place;
14213 }
14214 Ok(ranks)
14215}
14216
14217fn anagram_index(y: &Array, rules: Rules, span: Span) -> Result<Array> {
14220 let ranks = item_ranks(y, rules, span)?;
14221 let n = ranks.len();
14222 let mut index: i128 = 0;
14223 for i in 0..n {
14224 let smaller = ranks[i + 1..].iter().filter(|&&r| r < ranks[i]).count() as i128;
14225 index = index
14226 .checked_mul((n - i) as i128)
14227 .and_then(|v| v.checked_add(smaller))
14228 .ok_or_else(|| Error::not_yet("an anagram index too large for an integer", span))?;
14229 }
14230 i64::try_from(index)
14231 .map(Array::scalar_i64)
14232 .map_err(|_| Error::not_yet("an anagram index too large for an integer", span))
14233}
14234
14235fn anagram_from(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14238 let ys = as_list(y);
14239 let n = ys.items();
14240 let mut total: i128 = 1;
14241 for k in 1..=n as i128 {
14242 total = total
14243 .checked_mul(k)
14244 .ok_or_else(|| Error::not_yet("permuting more items than an integer counts", span))?;
14245 }
14246 let out_of_range = |want: &dyn std::fmt::Display| {
14252 Error::domain(
14253 format!("permutation {want} is out of range: {n} items have {total} of them"),
14254 span,
14255 )
14256 };
14257 let mut at: i128 = match x.to_i64_vec_near(near) {
14258 Some(v) => {
14259 let want = i128::from(
14260 *v.first().ok_or_else(|| Error::internal("anagram with no index"))?,
14261 );
14262 let at = if want < 0 { want + total } else { want };
14263 if at < 0 || at >= total {
14264 return Err(out_of_range(&want));
14265 }
14266 at
14267 }
14268 None => {
14269 if n != 0 {
14270 return Err(Error::domain("an anagram index must be an integer", span));
14271 }
14272 let want = *x
14273 .to_f64_vec()
14274 .as_deref()
14275 .and_then(<[f64]>::first)
14276 .ok_or_else(|| Error::domain("an anagram index must be an integer", span))?;
14277 let at = if want < 0.0 { want + total as f64 } else { want };
14278 if !(0.0..total as f64).contains(&at) {
14279 return Err(out_of_range(&want));
14280 }
14281 at as i128
14282 }
14283 };
14284 let mut pool: Vec<usize> = (0..n).collect();
14287 let mut order = Vec::with_capacity(n);
14288 let mut fact = total;
14289 for i in 0..n {
14290 fact /= (n - i) as i128;
14291 let d = (at / fact) as usize;
14292 at %= fact;
14293 order.push(pool.remove(d));
14294 }
14295 Ok(select_items(&ys, &order))
14296}
14297
14298fn cycle_form(y: &Array, near: NearInt, span: Span) -> Result<Array> {
14304 if y.dtype() == DType::Box {
14305 let perm = cycles_to_direct(y, None, near, span)?;
14306 return Ok(Array::from_i64(perm.iter().map(|&i| i as i64).collect()));
14307 }
14308 let n = permutation_span(y, near, span)?;
14309 let perm = direct_permutation_of(y, n, near, span)?;
14310 let mut boxes: Vec<Array> = Vec::new();
14311 let mut done = vec![false; perm.len()];
14312 for start in 0..perm.len() {
14313 if done[start] {
14314 continue;
14315 }
14316 let mut cycle = Vec::new();
14317 let mut at = start;
14318 while !done[at] {
14319 done[at] = true;
14320 cycle.push(at);
14321 at = perm[at];
14322 }
14323 let top = cycle.iter().position(|&v| v == *cycle.iter().max().unwrap()).unwrap();
14326 cycle.rotate_left(top);
14327 boxes.push(Array::boxed(Array::from_i64(
14328 cycle.iter().map(|&i| i as i64).collect(),
14329 )));
14330 }
14331 boxes.sort_by_key(|b| b.as_boxes().map(|s| s[0].to_i64_vec().unwrap()[0]).unwrap_or(0));
14332 let n = boxes.len();
14333 let inner: Vec<Array> =
14334 boxes.into_iter().map(|b| b.as_boxes().unwrap()[0].clone()).collect();
14335 Ok(Array::new(vec![n], Data::Box(inner.into())))
14336}
14337
14338fn direct_permutation_of(y: &Array, n: usize, near: NearInt, span: Span) -> Result<Vec<usize>> {
14347 let v = y
14348 .to_i64_vec_near(near)
14349 .ok_or_else(|| Error::domain("a permutation is a list of integers", span))?;
14350 let mut seen = vec![false; n];
14351 let mut tail = Vec::with_capacity(v.len());
14352 for &i in &v {
14353 let k = usize::try_from(i).ok().filter(|&k| k < n && !seen[k]).ok_or_else(|| {
14354 Error::domain(format!("{i} does not belong to a permutation of {n} items"), span)
14355 })?;
14356 seen[k] = true;
14357 tail.push(k);
14358 }
14359 let mut out: Vec<usize> = (0..n).filter(|&k| !seen[k]).collect();
14360 out.append(&mut tail);
14361 Ok(out)
14362}
14363
14364fn permutation_span(y: &Array, near: NearInt, span: Span) -> Result<usize> {
14367 let v = y
14368 .to_i64_vec_near(near)
14369 .ok_or_else(|| Error::domain("a permutation is a list of integers", span))?;
14370 let top = v.iter().copied().max().unwrap_or(-1).saturating_add(1).max(0) as u128;
14371 Ok(crate::limits::count(top, span)?.max(v.len()))
14372}
14373
14374fn cycles_to_direct(
14386 y: &Array,
14387 within: Option<usize>,
14388 near: NearInt,
14389 span: Span,
14390) -> Result<Vec<usize>> {
14391 let boxes = y.as_boxes().ok_or_else(|| Error::internal("cycles from a simple array"))?;
14392 let mut cycles: Vec<Vec<usize>> = Vec::new();
14393 let mut top = 0usize;
14394 for b in boxes {
14395 let v = b
14396 .to_i64_vec_near(near)
14397 .ok_or_else(|| Error::domain("a cycle is a list of integers", span))?;
14398 let mut cycle = Vec::with_capacity(v.len());
14399 for &i in &v {
14400 let k = match within {
14401 Some(n) => {
14402 let at = if i < 0 { i.checked_add(n as i64) } else { Some(i) };
14403 usize::try_from(at.unwrap_or(-1))
14404 .ok()
14405 .filter(|&k| k < n)
14406 .ok_or_else(|| {
14407 Error::domain(
14408 format!("{i} is not an index into {n} item(s)"),
14409 span,
14410 )
14411 })?
14412 }
14413 None => {
14414 let k = usize::try_from(i)
14415 .map_err(|_| Error::domain(format!("{i} is not an index"), span))?;
14416 crate::limits::count(k as u128 + 1, span)?;
14417 k
14418 }
14419 };
14420 top = top.max(k + 1);
14421 cycle.push(k);
14422 }
14423 cycles.push(cycle);
14424 }
14425 let mut perm: Vec<usize> = (0..top).collect();
14426 for cycle in &cycles {
14427 for w in 0..cycle.len() {
14428 perm[cycle[w]] = cycle[(w + 1) % cycle.len()];
14430 }
14431 }
14432 Ok(perm)
14433}
14434
14435fn permute(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14441 let ys = as_list(y);
14442 let n = ys.items();
14443 if x.dtype() != DType::Box {
14444 let perm = direct_permutation_of(&as_list(x), n, near, span)?;
14445 return Ok(select_items(&ys, &perm));
14446 }
14447 let mut perm = cycles_to_direct(x, Some(n), near, span)?;
14448 perm.extend(perm.len()..n);
14450 Ok(select_items(&ys, &perm))
14451}
14452
14453fn unicode(y: &Array, pass_chars: bool, near: NearInt, span: Span) -> Result<Array> {
14459 if y.dtype() == DType::Char {
14460 if pass_chars {
14461 return Ok(y.clone());
14462 }
14463 return Ok(chars_to_codes(y));
14464 }
14465 codes_to_chars(y, near, span)
14466}
14467
14468fn chars_to_codes(y: &Array) -> Array {
14469 let Data::Char(v) = &y.data else { return y.clone() };
14470 Array::new(y.shape.clone(), Data::I64(v.iter().map(|&c| c as i64).collect()))
14471}
14472
14473fn codes_to_chars(y: &Array, near: NearInt, span: Span) -> Result<Array> {
14474 let v = y
14475 .to_i64_vec_near(near)
14476 .ok_or_else(|| Error::domain("a codepoint must be an integer", span))?;
14477 let mut out = Vec::with_capacity(v.len());
14478 for &c in &v {
14479 let ch = u32::try_from(c).ok().and_then(char::from_u32).ok_or_else(|| {
14480 Error::domain(format!("{c} is not a Unicode codepoint"), span)
14481 })?;
14482 out.push(ch);
14483 }
14484 Ok(Array::new(y.shape.clone(), Data::Char(out.into())))
14485}
14486
14487fn unicode_form(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14490 let form = x
14491 .to_i64_vec()
14492 .ok_or_else(|| Error::domain("a conversion form is an integer", span))?
14493 .first()
14494 .copied()
14495 .unwrap_or(0);
14496 match form {
14497 3 if y.dtype() == DType::Char => Ok(chars_to_codes(y)),
14498 3 => Err(Error::domain("form 3 converts characters to codepoints", span)),
14499 10 => codes_to_chars(y, near, span),
14500 n => Err(Error::not_yet(format!("the byte-oriented unicode form ({n} u:)"), span)),
14501 }
14502}
14503
14504fn to_symbols(y: &Array, span: Span) -> Result<Array> {
14515 if let Some(boxes) = y.as_boxes() {
14516 let mut ids = Vec::with_capacity(boxes.len());
14517 for b in boxes {
14518 if b.rank() > 1 {
14519 return Err(Error::new(
14520 ErrorKind::Rank,
14521 "a boxed symbol name is a character list",
14522 Some(span),
14523 ));
14524 }
14525 let row_major = b.to_row_major();
14526 let Data::Char(v) = &row_major.data else {
14527 if b.count() == 0 {
14528 ids.push(crate::symbol::EMPTY);
14529 continue;
14530 }
14531 return Err(Error::domain("a symbol is made from characters", span));
14532 };
14533 ids.push(crate::symbol::intern(&v.as_slice().iter().collect::<String>()));
14534 }
14535 return Ok(Array::new(y.shape.clone(), Data::Symbol(ids.into())));
14536 }
14537 let row_major = y.to_row_major();
14538 let Data::Char(v) = &row_major.data else {
14539 return Err(Error::domain(
14540 format!("s: makes symbols from characters, not {} data", y.dtype().name()),
14541 span,
14542 ));
14543 };
14544 let chars = v.as_slice();
14545 if y.rank() >= 2 {
14546 let width = y.shape[y.rank() - 1];
14547 let mut ids = Vec::with_capacity(chars.len() / width.max(1));
14548 for row in chars.chunks(width) {
14549 let name: String = row.iter().collect();
14550 ids.push(crate::symbol::intern(name.trim_end_matches(' ')));
14551 }
14552 return Ok(Array::new(y.shape[..y.rank() - 1].to_vec(), Data::Symbol(ids.into())));
14553 }
14554 let Some((&delim, rest)) = chars.split_first() else {
14555 return Ok(Array::new(vec![0], Data::empty(DType::Symbol)));
14556 };
14557 let mut ids = Vec::new();
14558 let mut name = String::new();
14559 for &c in rest {
14560 if c == delim {
14561 ids.push(crate::symbol::intern(&name));
14562 name.clear();
14563 } else {
14564 name.push(c);
14565 }
14566 }
14567 ids.push(crate::symbol::intern(&name));
14568 Ok(Array::new(vec![ids.len()], Data::Symbol(ids.into())))
14569}
14570
14571fn symbol_form(x: &Array, y: &Array, span: Span) -> Result<Array> {
14577 let form = x
14578 .to_i64_vec()
14579 .ok_or_else(|| Error::domain("a symbol form is an integer", span))?
14580 .first()
14581 .copied()
14582 .unwrap_or(0);
14583 if !matches!(form, 4 | 5) {
14584 return Err(Error::not_yet(format!("the symbol-table form ({form} s:)"), span));
14585 }
14586 let row_major = y.to_row_major();
14587 let Data::Symbol(ids) = &row_major.data else {
14588 return Err(Error::domain(
14589 format!("{form} s: reads symbols, not {} data", y.dtype().name()),
14590 span,
14591 ));
14592 };
14593 let names = crate::symbol::names(ids.as_slice());
14594 if form == 5 {
14595 let boxes: Vec<Array> =
14596 names.iter().map(|n| Array::from_chars(n.chars().collect())).collect();
14597 return Ok(Array::new(y.shape.clone(), Data::Box(boxes.into())));
14598 }
14599 let width = names.iter().map(|n| n.chars().count()).max().unwrap_or(0);
14600 let mut out: Vec<char> = Vec::with_capacity(names.len() * width);
14601 for n in &names {
14602 out.extend(n.chars());
14603 out.resize(out.len() + width - n.chars().count(), ' ');
14604 }
14605 let mut shape = y.shape.clone();
14606 shape.push(width);
14607 Ok(Array::new(shape, Data::Char(out.into())))
14608}
14609
14610fn sparse_form(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14621 if x.rank() != 0 {
14622 return Err(Error::new(ErrorKind::Rank, "a sparse form is one atom", Some(span)));
14623 }
14624 let form = x
14625 .to_i64_vec_near(near)
14626 .and_then(|v| v.first().copied())
14627 .ok_or_else(|| Error::domain("a sparse form is an integer", span))?;
14628 match form {
14629 0 if y.is_sparse() => return Ok(y.densified()),
14630 0 => return crate::sparse::sparsify(y, span),
14631 1 => return crate::sparse::create(y, span),
14632 2 => {
14633 let axes: Vec<i64> = match y.sparse_parts() {
14634 Some(s) => s.axes.iter().map(|&k| k as i64).collect(),
14635 None => (0..y.rank() as i64).collect(),
14636 };
14637 return Ok(Array::from_i64(axes));
14638 }
14639 _ => {}
14640 }
14641 let Some(s) = y.sparse_parts() else {
14642 return Err(Error::domain(
14643 format!("{form} $. reads a sparse array, and this one is dense"),
14644 span,
14645 ));
14646 };
14647 match form {
14648 -1 => Ok(crate::sparse::attributes(y, s)),
14649 3 => Ok(crate::sparse::fill_of(s)),
14650 4 => Ok(crate::sparse::indices_of(s)),
14651 5 => Ok(crate::sparse::values_of(y, s)),
14652 7 => Ok(Array::scalar_i64(s.entries as i64)),
14653 8 => Ok(crate::sparse::compress(y, s)),
14654 _ => Err(Error::domain(format!("{form} is not a sparse form"), span)),
14655 }
14656}
14657
14658fn boxing_level(y: &Array) -> i64 {
14660 match y.as_boxes() {
14661 None => 0,
14662 Some(bs) => 1 + bs.iter().map(boxing_level).max().unwrap_or(0),
14663 }
14664}
14665
14666fn split_items(y: &Array) -> Array {
14670 if y.rank() == 0 {
14671 return Array::boxed(y.clone());
14672 }
14673 let last = y.shape[y.rank() - 1];
14674 let outer: Vec<usize> = y.shape[..y.rank() - 1].to_vec();
14675 let n: usize = outer.iter().product();
14676 let mut boxes = Vec::with_capacity(n);
14677 for i in 0..n {
14678 let mut data = Data::empty(y.dtype());
14679 for k in 0..last {
14680 push_elem(&mut data, &y.data, i * last + k);
14681 }
14682 boxes.push(Array::new(vec![last], data));
14683 }
14684 Array::new(outer, Data::Box(boxes.into()))
14685}
14686
14687fn pick(x: &Array, y: &Array, origin: i64, near: NearInt, span: Span) -> Result<Array> {
14690 let xs = as_list(x);
14691 let mut cur = y.clone();
14692 for i in 0..xs.items() {
14693 let step = open_cell(&item_or_self(&xs, i));
14694 let idx = step
14695 .to_i64_vec_near(near)
14696 .ok_or_else(|| Error::domain("a pick path holds integers", span))?;
14697 let base =
14698 if cur.rank() == 0 { Array::new(vec![1], cur.data.clone()) } else { cur.clone() };
14699 if idx.len() > base.rank() {
14700 return Err(Error::new(
14701 ErrorKind::Length,
14702 format!(
14703 "a path step of {} index(es) into a value of rank {}",
14704 idx.len(),
14705 cur.rank()
14706 ),
14707 Some(span),
14708 ));
14709 }
14710 let zeroed: Vec<i64> = idx.iter().map(|&v| v - origin).collect();
14711 let at = cell_index(&base, &zeroed, span)?;
14712 cur = open_cell(&base.cell_at(idx.len(), at));
14713 }
14714 Ok(cur)
14715}
14716
14717fn prime_meta(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14722 let form = one_int(x, "a prime query", near, span)?;
14723 let n = one_int(y, "a prime query", near, span)?;
14724 match form {
14725 -1 => Ok(Array::scalar_i64(primes_below(n, span)?)),
14727 0 => Ok(Array::scalar_bool(!is_prime(n))),
14729 1 => Ok(Array::scalar_bool(is_prime(n))),
14730 2 | 3 => {
14732 let (ps, es) = factor_table(n, span)?;
14733 let k = ps.len();
14734 if form == 3 {
14735 return Ok(Array::from_i64(ps));
14736 }
14737 let mut all = ps;
14738 all.extend(es);
14739 Ok(Array::new(vec![2, k], Data::I64(all.into())))
14740 }
14741 4 => Ok(Array::scalar_i64(next_prime(n, span)?)),
14743 -4 => Ok(Array::scalar_i64(previous_prime(n, span)?)),
14744 other => Err(Error::domain(format!("{other} is not a prime query"), span)),
14745 }
14746}
14747
14748fn prime_exponents(x: &Array, y: &Array, near: NearInt, span: Span) -> Result<Array> {
14751 let n = one_int(y, "prime exponents", near, span)?;
14752 let count = x.to_f64_vec().and_then(|v| v.first().copied()).unwrap_or(0.0);
14753 let (ps, es) = factor_table(n, span)?;
14754 if count == f64::NEG_INFINITY {
14755 let k = ps.len();
14756 let mut all = ps;
14757 all.extend(es);
14758 return Ok(Array::new(vec![2, k], Data::I64(all.into())));
14759 }
14760 let want = one_int(x, "prime exponents", near, span)?;
14761 if want < 0 {
14762 return Err(Error::not_yet(format!("the prime exponent form ({want} q:)"), span));
14763 }
14764 let mut out = Vec::with_capacity(want as usize);
14765 for i in 0..want {
14766 let p = nth_prime(i, span)?;
14767 out.push(ps.iter().position(|&q| q == p).map_or(0, |at| es[at]));
14768 }
14769 Ok(Array::from_i64(out))
14770}
14771
14772fn factor_table(n: i64, span: Span) -> Result<(Vec<i64>, Vec<i64>)> {
14774 let factors = prime_factors(n, span)?;
14775 let mut ps: Vec<i64> = Vec::new();
14776 let mut es: Vec<i64> = Vec::new();
14777 for f in factors {
14778 if ps.last() == Some(&f) {
14779 *es.last_mut().unwrap() += 1;
14780 } else {
14781 ps.push(f);
14782 es.push(1);
14783 }
14784 }
14785 Ok((ps, es))
14786}
14787
14788fn is_prime(n: i64) -> bool {
14789 if n < 2 {
14790 return false;
14791 }
14792 let mut d = 2i64;
14793 while d.saturating_mul(d) <= n {
14794 if n % d == 0 {
14795 return false;
14796 }
14797 d += 1;
14798 }
14799 true
14800}
14801
14802fn primes_below(n: i64, span: Span) -> Result<i64> {
14803 if n < 0 {
14804 return Err(Error::domain("counting the primes below a negative number", span));
14805 }
14806 Ok((2..n).filter(|&k| is_prime(k)).count() as i64)
14807}
14808
14809fn next_prime(n: i64, span: Span) -> Result<i64> {
14810 let mut k = n.checked_add(1).ok_or_else(|| Error::domain("no next prime", span))?;
14811 while !is_prime(k) {
14812 k = k.checked_add(1).ok_or_else(|| Error::domain("no next prime", span))?;
14813 }
14814 Ok(k)
14815}
14816
14817fn previous_prime(n: i64, span: Span) -> Result<i64> {
14818 let mut k = n - 1;
14819 while k >= 2 {
14820 if is_prime(k) {
14821 return Ok(k);
14822 }
14823 k -= 1;
14824 }
14825 Err(Error::domain(format!("there is no prime below {n}"), span))
14826}
14827
14828fn one_int(a: &Array, what: &str, near: NearInt, span: Span) -> Result<i64> {
14830 a.to_i64_vec_near(near)
14831 .and_then(|v| v.first().copied())
14832 .ok_or_else(|| Error::domain(format!("{what} needs an integer"), span))
14833}
14834
14835fn expand(x: &Array, y: &Array, apl: bool, near: NearInt, span: Span) -> Result<Array> {
14839 let mask = x
14840 .to_i64_vec_near(near)
14841 .ok_or_else(|| Error::domain("an expansion mask holds 0s and 1s", span))?;
14842 if mask.iter().any(|&b| b != 0 && b != 1) {
14843 return Err(Error::domain("an expansion mask holds 0s and 1s", span));
14844 }
14845 let ys = as_list(y);
14846 let taken = mask.iter().filter(|&&b| b == 1).count();
14847 let n = ys.items();
14848 let spread = n == 1 && taken != 1;
14850 if !spread && taken != n {
14851 return Err(Error::new(
14852 ErrorKind::Length,
14853 format!("an expansion mask taking {taken} item(s) over {n}"),
14854 Some(span),
14855 ));
14856 }
14857 let m = ys.item_size();
14858 let fill = if apl { prototype_of(&ys) } else { None };
14859 let mut data = Data::empty(ys.dtype());
14860 let mut at = 0usize;
14861 for &b in &mask {
14862 if b == 1 {
14863 let from = if spread { 0 } else { at };
14864 for k in 0..m {
14865 push_elem(&mut data, &ys.data, from * m + k);
14866 }
14867 at += 1;
14868 } else {
14869 for _ in 0..m {
14870 push_gap(&mut data, &fill);
14871 }
14872 }
14873 }
14874 let mut shape = ys.shape.clone();
14875 if shape.is_empty() {
14876 shape.push(mask.len());
14877 } else {
14878 shape[0] = mask.len();
14879 }
14880 Ok(keep_proto(Array::new(shape, data), &ys, apl))
14881}
14882
14883fn execute(y: &Array, apl: bool, ctx: &mut Ctx<'_>, span: Span) -> Result<Array> {
14891 if y.count() == 0 {
14896 if apl {
14897 return execute_source("", apl, ctx, span);
14898 }
14899 return Ok(Array::new(y.shape.clone(), Data::empty(DType::Bool)));
14900 }
14901 let Data::Char(v) = &y.data else {
14902 return Err(Error::domain("execute reads a character list", span));
14903 };
14904 let src: String = v.iter().collect();
14905 execute_source(&src, apl, ctx, span)
14906}
14907
14908pub(crate) fn execute_source(
14911 src: &str,
14912 apl: bool,
14913 ctx: &mut Ctx<'_>,
14914 span: Span,
14915) -> Result<Array> {
14916 let lang = if apl { crate::Lang::Apl } else { crate::Lang::J };
14917 let dialect = ctx.cfg.rules.dialect();
14920 let nested = crate::compile(lang, src, &dialect).map_err(|e| nested_error(e, src, span))?;
14921 if !nested.params.is_empty() {
14922 return Err(Error::domain(
14923 "an executed string cannot take host data: `{name}` has nothing to bind to",
14924 span,
14925 ));
14926 }
14927 let mut rec = None;
14928 let (value, _) = crate::ir::run_block(&nested.stmts, None, ctx, &mut rec)
14929 .map_err(|e| nested_error(e, src, span))?;
14930 value.ok_or_else(|| Error::domain("the executed string yielded no value", span))
14931}
14932
14933fn stream_number(y: &Array, open: i64, what: &str, span: Span) -> Result<()> {
14940 let closed = || {
14941 Err(Error::sandbox(
14942 format!("{what} the standard stream {open} only; a file is outside the program"),
14943 span,
14944 ))
14945 };
14946 if matches!(y.data, Data::Box(_)) {
14947 return closed();
14948 }
14949 match y.to_i64_vec().as_deref() {
14950 Some([n]) if *n == open => Ok(()),
14951 Some([_]) => closed(),
14952 _ => Err(Error::domain(format!("{what} one stream number"), span)),
14953 }
14954}
14955
14956fn type_code(y: &Array) -> i64 {
14962 if y.is_sparse() {
14963 return 1024 * dense_type_code(y);
14964 }
14965 dense_type_code(y)
14966}
14967
14968fn dense_type_code(y: &Array) -> i64 {
14969 match y.dtype() {
14970 DType::Bool => 1,
14971 DType::Char => 2,
14972 DType::I64 => 4,
14973 DType::F64 => 8,
14974 DType::Complex => 16,
14975 DType::Box => 32,
14976 DType::Ext => 64,
14977 DType::Rat => 128,
14978 DType::Symbol => 65536,
14979 }
14980}
14981
14982fn nested_error(e: Error, src: &str, span: Span) -> Error {
14986 let inner = e.render(src);
14987 let mut out = Error::new(e.kind, format!("in the executed string: {}", e.msg), Some(span));
14988 out.notes.push(inner.trim_end().to_string());
14989 out
14990}
14991
14992fn words(y: &Array, span: Span) -> Result<Array> {
14998 if y.count() == 0 {
15001 return Ok(Array::new(vec![0], Data::Box(Vec::new().into())));
15002 }
15003 let Data::Char(v) = &y.data else {
15004 return Err(Error::domain("words reads a character list", span));
15005 };
15006 let src: Vec<char> = v.as_slice().to_vec();
15007 let n = src.len();
15008 let mut out: Vec<Array> = Vec::new();
15009 let mut i = 0usize;
15010 let numeric_start = |k: usize| -> bool {
15011 k < n && (src[k].is_ascii_digit() || src[k] == '_')
15012 };
15013 while i < n {
15014 let c = src[i];
15015 if c == ' ' || c == '\t' {
15016 i += 1;
15017 continue;
15018 }
15019 let start = i;
15020 if c == '\'' {
15021 i += 1;
15022 loop {
15023 if i >= n {
15024 return Err(Error::parse("a word list ends inside a string", span));
15025 }
15026 if src[i] == '\'' {
15027 i += 1;
15028 if i < n && src[i] == '\'' {
15029 i += 1;
15030 continue;
15031 }
15032 break;
15033 }
15034 i += 1;
15035 }
15036 } else if c.is_ascii_alphabetic() {
15037 while i < n && (src[i].is_ascii_alphanumeric() || src[i] == '_') {
15038 i += 1;
15039 }
15040 if i < n && (src[i] == '.' || src[i] == ':') {
15041 i += 1;
15042 }
15043 if src[start..i].iter().collect::<String>() == "NB." {
15045 while i < n && src[i] != '\n' {
15046 i += 1;
15047 }
15048 }
15049 } else if numeric_start(i) {
15050 loop {
15051 while i < n && (src[i].is_ascii_alphanumeric() || src[i] == '.' || src[i] == '_')
15052 {
15053 i += 1;
15054 }
15055 let mut j = i;
15057 while j < n && src[j] == ' ' {
15058 j += 1;
15059 }
15060 if j > i && numeric_start(j) {
15061 i = j;
15062 continue;
15063 }
15064 break;
15065 }
15066 } else {
15067 i += 1;
15068 while i < n && (src[i] == '.' || src[i] == ':') {
15069 i += 1;
15070 }
15071 }
15072 out.push(Array::from_chars(src[start..i].to_vec()));
15073 }
15074 let k = out.len();
15075 Ok(Array::new(vec![k], Data::Box(out.into())))
15076}
15077
15078#[cfg(test)]
15079mod tests {
15080 use super::*;
15081
15082 macro_rules! ctx {
15084 ($name:ident, $agreement:expr) => {
15085 let mut sink = |_: &str| {};
15086 let mut env = Env::new(Vec::new());
15087 #[allow(unused_mut)]
15088 let mut $name = Ctx {
15089 cfg: EvalCfg {
15090 agreement: $agreement,
15091 fmt: FmtOpts::J,
15092 tol: Tol::J,
15093 rules: crate::frontend::Dialect::default()
15096 .rules(if $agreement == Agreement::ExactOrScalar {
15097 crate::Lang::Apl
15098 } else {
15099 crate::Lang::J
15100 })
15101 .expect("the shipped dialect is implemented"),
15102 },
15103 out: &mut sink,
15104 inp: None,
15105 env: &mut env,
15106 device: None,
15107 shy: false,
15108 };
15109 };
15110 ($name:ident) => {
15111 ctx!($name, Agreement::LeadingPrefix);
15112 };
15113 }
15114
15115 fn scalar_prim(name: &'static str, monad: MonadOp, dyad: DyadOp) -> Verb {
15116 Verb::Prim(Prim { name, monad, dyad, ranks: [0, 0, 0] })
15117 }
15118
15119 fn inf_prim(name: &'static str, monad: MonadOp, dyad: DyadOp) -> Verb {
15120 Verb::Prim(Prim { name, monad, dyad, ranks: [RANK_INF, RANK_INF, RANK_INF] })
15121 }
15122
15123 fn plus() -> Verb {
15124 scalar_prim("+", MonadOp::Scalar(ScalarMonad::Conj), DyadOp::Scalar(ScalarDyad::Add))
15125 }
15126 fn minus() -> Verb {
15127 scalar_prim("-", MonadOp::Scalar(ScalarMonad::Neg), DyadOp::Scalar(ScalarDyad::Sub))
15128 }
15129 fn times() -> Verb {
15130 scalar_prim("*", MonadOp::Scalar(ScalarMonad::Signum), DyadOp::Scalar(ScalarDyad::Mul))
15131 }
15132 fn pct() -> Verb {
15133 scalar_prim("%", MonadOp::Scalar(ScalarMonad::Recip), DyadOp::Scalar(ScalarDyad::DivJ))
15134 }
15135 fn div_apl() -> Verb {
15136 scalar_prim("÷", MonadOp::Scalar(ScalarMonad::Recip), DyadOp::Scalar(ScalarDyad::DivApl))
15137 }
15138 fn floor_v() -> Verb {
15139 scalar_prim("<.", MonadOp::Scalar(ScalarMonad::Floor), DyadOp::Scalar(ScalarDyad::Min))
15140 }
15141 fn ceil_v() -> Verb {
15142 scalar_prim(">.", MonadOp::Scalar(ScalarMonad::Ceil), DyadOp::Scalar(ScalarDyad::Max))
15143 }
15144 fn pow_v() -> Verb {
15145 scalar_prim("^", MonadOp::Scalar(ScalarMonad::Exp), DyadOp::Scalar(ScalarDyad::Pow))
15146 }
15147 fn residue_v() -> Verb {
15148 scalar_prim("|", MonadOp::Scalar(ScalarMonad::Abs), DyadOp::Scalar(ScalarDyad::Residue))
15149 }
15150 fn eq_v() -> Verb {
15151 scalar_prim("=", MonadOp::None, DyadOp::Scalar(ScalarDyad::Eq))
15152 }
15153 fn lt_v() -> Verb {
15154 scalar_prim("<", MonadOp::None, DyadOp::Scalar(ScalarDyad::Lt))
15155 }
15156 fn not_v() -> Verb {
15157 scalar_prim("-.", MonadOp::Scalar(ScalarMonad::Not), DyadOp::None)
15158 }
15159 fn sqrt_v() -> Verb {
15160 scalar_prim("%:", MonadOp::Scalar(ScalarMonad::Sqrt), DyadOp::NotYet("dyadic root"))
15161 }
15162 fn dollar() -> Verb {
15163 inf_prim("$", MonadOp::ShapeOf, DyadOp::Reshape)
15164 }
15165 fn pound() -> Verb {
15166 inf_prim("#", MonadOp::Tally, DyadOp::NotYet("copy"))
15167 }
15168 fn comma() -> Verb {
15169 inf_prim(",", MonadOp::Ravel, DyadOp::NotYet("append"))
15170 }
15171 fn transpose_v() -> Verb {
15172 inf_prim("|:", MonadOp::TransposeAxes, DyadOp::NotYet("dyadic transpose"))
15173 }
15174 fn head_v() -> Verb {
15175 inf_prim("{.", MonadOp::Head, DyadOp::Take)
15176 }
15177 fn behead_v() -> Verb {
15178 inf_prim("}.", MonadOp::Behead, DyadOp::Drop)
15179 }
15180 fn iota() -> Verb {
15181 inf_prim("i.", MonadOp::IotaJ, DyadOp::NotYet("index of"))
15182 }
15183 fn iota_apl(origin: i64) -> Verb {
15184 inf_prim("⍳", MonadOp::IotaApl { origin }, DyadOp::NotYet("index of"))
15185 }
15186 fn right_v() -> Verb {
15187 inf_prim("]", MonadOp::Same, DyadOp::Right)
15188 }
15189 fn echo_v() -> Verb {
15190 inf_prim("echo", MonadOp::Echo, DyadOp::None)
15191 }
15192
15193 fn b(v: Verb) -> Box<Verb> {
15194 Box::new(v)
15195 }
15196
15197 fn mat(rows: usize, cols: usize, v: Vec<i64>) -> Array {
15198 Array::new(vec![rows, cols], Data::I64(v.into()))
15199 }
15200
15201 fn ints(a: &Array) -> Vec<i64> {
15203 a.to_row_major().as_i64_slice().expect("integer result").to_vec()
15204 }
15205
15206 fn floats(a: &Array) -> Vec<f64> {
15207 a.to_row_major().as_f64_slice().expect("float result").to_vec()
15208 }
15209
15210 fn bools(a: &Array) -> Vec<u8> {
15211 match &a.to_row_major().data {
15212 Data::Bool(v) => v.to_vec(),
15213 other => panic!("expected boolean result, got {other:?}"),
15214 }
15215 }
15216
15217 fn sp() -> Span {
15218 Span::new(0, 1)
15219 }
15220
15221 fn close(a: f64, b: f64) -> bool {
15222 (a - b).abs() < 1e-9 || (a.is_infinite() && b.is_infinite() && a.signum() == b.signum())
15223 }
15224
15225 #[test]
15228 fn names_of_primitives_and_derived_verbs() {
15229 assert_eq!(plus().name(), "+");
15230 assert_eq!(Verb::Rank(b(plus()), [1, 1, 1]).name(), "+\"1");
15231 assert_eq!(Verb::Rank(b(plus()), [0, 1, RANK_INF]).name(), "+\"0 1 _");
15232 assert_eq!(Verb::Rank(b(plus()), [RANK_INF; 3]).name(), "+\"_");
15233 assert_eq!(Verb::Reduce(b(plus())).name(), "+/");
15234 assert_eq!(Verb::Rank(b(Verb::Reduce(b(plus()))), [1, 1, 1]).name(), "+/\"1");
15235 assert_eq!(Verb::Fork(b(plus()), b(minus()), b(times())).name(), "(+ - *)");
15236 assert_eq!(
15237 Verb::NounFork(Array::scalar_i64(1), b(plus()), b(minus())).name(),
15238 "(n + -)"
15239 );
15240 assert_eq!(Verb::Hook(b(plus()), b(minus())).name(), "(+ -)");
15241 assert_eq!(Verb::Atop(b(plus()), b(minus())).name(), "(+@:-)");
15242 assert_eq!(Verb::Compose(b(plus()), b(minus())).name(), "(+&:-)");
15243 assert_eq!(Verb::BondLeft(Array::scalar_i64(1), b(plus())).name(), "(n&+)");
15244 assert_eq!(Verb::BondRight(b(plus()), Array::scalar_i64(1)).name(), "(+&n)");
15245 }
15246
15247 #[test]
15248 fn composition_applies_the_right_verb_to_both_arguments() {
15249 ctx!(c);
15250 let v = Verb::Compose(b(plus()), b(times()));
15251 let r = v.monad(&Array::from_i64(vec![-2, 0, 3]), &mut c, sp()).unwrap();
15253 assert_eq!(ints(&r), vec![-1, 0, 1]);
15254 let r = v
15255 .dyad(&Array::scalar_i64(-5), &Array::scalar_i64(7), &mut c, sp())
15256 .unwrap();
15257 assert_eq!(ints(&r), vec![0]);
15258 let bond = Verb::BondLeft(Array::scalar_i64(10), b(minus()));
15260 let r = bond.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15261 assert_eq!(ints(&r), vec![9, 8]);
15262 let e = bond
15263 .dyad(&Array::scalar_i64(1), &Array::scalar_i64(2), &mut c, sp())
15264 .unwrap_err();
15265 assert_eq!(e.kind, ErrorKind::Domain);
15266 let bond = Verb::BondRight(b(minus()), Array::scalar_i64(10));
15267 let r = bond.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15268 assert_eq!(ints(&r), vec![-9, -8]);
15269 }
15270
15271 #[test]
15274 fn scalar_monad_covers_the_whole_buffer() {
15275 ctx!(c);
15276 let r = minus().monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp()).unwrap();
15277 assert_eq!(r.shape, vec![2, 3]);
15278 assert_eq!(ints(&r), vec![-1, -2, -3, -4, -5, -6]);
15279 }
15280
15281 #[test]
15282 fn leading_prefix_agreement_broadcasts_per_row() {
15283 ctx!(c);
15284 let x = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15285 let y = Array::from_i64(vec![10, 20]);
15286 let r = plus().dyad(&x, &y, &mut c, sp()).unwrap();
15287 assert_eq!(r.shape, vec![2, 3]);
15288 assert_eq!(ints(&r), vec![11, 12, 13, 24, 25, 26]);
15289 let r = plus().dyad(&y, &x, &mut c, sp()).unwrap();
15291 assert_eq!(ints(&r), vec![11, 12, 13, 24, 25, 26]);
15292 }
15293
15294 #[test]
15295 fn exact_or_scalar_rejects_a_prefix_frame() {
15296 ctx!(c, Agreement::ExactOrScalar);
15297 let x = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15298 let y = Array::from_i64(vec![10, 20]);
15299 let e = plus().dyad(&x, &y, &mut c, sp()).unwrap_err();
15300 assert_eq!(e.kind, ErrorKind::Shape);
15301 assert!(e.msg.contains("2 3"), "{}", e.msg);
15302 assert!(e.msg.contains("right shape 2"), "{}", e.msg);
15303 }
15304
15305 #[test]
15306 fn exact_or_scalar_accepts_equal_frames_and_scalars() {
15307 ctx!(c, Agreement::ExactOrScalar);
15308 let x = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15309 let r = plus().dyad(&x, &x, &mut c, sp()).unwrap();
15310 assert_eq!(ints(&r), vec![2, 4, 6, 8, 10, 12]);
15311 let r = plus().dyad(&Array::scalar_i64(10), &x, &mut c, sp()).unwrap();
15312 assert_eq!(r.shape, vec![2, 3]);
15313 assert_eq!(ints(&r), vec![11, 12, 13, 14, 15, 16]);
15314 let r = plus().dyad(&x, &Array::scalar_i64(10), &mut c, sp()).unwrap();
15315 assert_eq!(ints(&r), vec![11, 12, 13, 14, 15, 16]);
15316 }
15317
15318 #[test]
15319 fn vector_length_mismatch_is_a_length_error() {
15320 ctx!(c);
15321 let e = plus()
15322 .dyad(&Array::from_i64(vec![1, 2, 3]), &Array::from_i64(vec![1, 2, 3, 4, 5]), &mut c, sp())
15323 .unwrap_err();
15324 assert_eq!(e.kind, ErrorKind::Length);
15325 assert!(e.msg.contains("left shape 3"), "{}", e.msg);
15326 assert!(e.msg.contains("right shape 5"), "{}", e.msg);
15327 assert!(e.notes[0].contains("axis 0"), "{:?}", e.notes);
15328 }
15329
15330 #[test]
15331 fn diverging_matrix_frames_name_the_axis() {
15332 ctx!(c);
15333 let e = plus()
15334 .dyad(&mat(2, 3, vec![0; 6]), &mat(2, 4, vec![0; 8]), &mut c, sp())
15335 .unwrap_err();
15336 assert_eq!(e.kind, ErrorKind::Shape);
15337 assert!(e.notes[0].contains("axis 1"), "{:?}", e.notes);
15338 }
15339
15340 #[test]
15341 fn dyadic_rank_pairs_rows_with_the_whole_right_argument() {
15342 ctx!(c);
15343 let v = Verb::Rank(b(plus()), [0, 1, 1]);
15345 let r = v
15346 .dyad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &Array::from_i64(vec![10, 20, 30]), &mut c, sp())
15347 .unwrap();
15348 assert_eq!(r.shape, vec![2, 3]);
15349 assert_eq!(ints(&r), vec![11, 22, 33, 14, 25, 36]);
15350 }
15351
15352 #[test]
15353 fn surplus_frame_axes_repeat_the_shorter_frames_cells() {
15354 ctx!(c);
15355 let v = Verb::Rank(b(head_v()), [0, 0, 1]);
15358 let x = mat(2, 2, vec![1, 1, 2, 2]);
15359 let y = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15360 let r = v.dyad(&x, &y, &mut c, sp()).unwrap();
15361 assert_eq!(r.shape, vec![2, 2, 2]);
15362 assert_eq!(ints(&r), vec![1, 0, 1, 0, 4, 5, 4, 5]);
15363 }
15364
15365 #[test]
15366 fn an_empty_frame_pairs_its_single_cell_with_every_other_cell() {
15367 ctx!(c, Agreement::ExactOrScalar);
15368 let v = Verb::Rank(b(head_v()), [0, 0, 1]);
15370 let x = Array::from_i64(vec![1, 2]);
15371 let y = Array::from_i64(vec![7, 8, 9]);
15372 let r = v.dyad(&x, &y, &mut c, sp()).unwrap();
15373 assert_eq!(r.shape, vec![2, 2]);
15374 assert_eq!(ints(&r), vec![7, 0, 7, 8]);
15375 }
15376
15377 #[test]
15378 fn negative_rank_leaves_frame_axes() {
15379 ctx!(c);
15380 let v = Verb::Rank(b(dollar()), [-1, -1, -1]);
15382 let r = v.monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp()).unwrap();
15383 assert_eq!(r.shape, vec![2, 1]);
15384 assert_eq!(ints(&r), vec![3, 3]);
15385 }
15386
15387 #[test]
15388 fn effective_rank_clamps_and_counts_back() {
15389 assert_eq!(effective_rank(0, 3), 0);
15390 assert_eq!(effective_rank(2, 1), 1);
15391 assert_eq!(effective_rank(RANK_INF, 4), 4);
15392 assert_eq!(effective_rank(-1, 3), 2);
15393 assert_eq!(effective_rank(-5, 3), 0);
15394 }
15395
15396 #[test]
15399 fn reduction_folds_right_to_left() {
15400 ctx!(c);
15401 let r = Verb::Reduce(b(minus()))
15403 .monad(&Array::from_i64(vec![1, 2, 3]), &mut c, sp())
15404 .unwrap();
15405 assert!(r.shape.is_empty());
15406 assert_eq!(ints(&r), vec![2]);
15407 }
15408
15409 #[test]
15410 fn reduction_of_one_item_and_of_a_scalar() {
15411 ctx!(c);
15412 let r = Verb::Reduce(b(plus()))
15413 .monad(&Array::from_i64(vec![7]), &mut c, sp())
15414 .unwrap();
15415 assert!(r.shape.is_empty());
15416 assert_eq!(ints(&r), vec![7]);
15417 let r = Verb::Reduce(b(plus())).monad(&Array::scalar_i64(7), &mut c, sp()).unwrap();
15418 assert_eq!(ints(&r), vec![7]);
15419 }
15420
15421 #[test]
15422 fn reduction_runs_along_the_leading_axis() {
15423 ctx!(c);
15424 let r = Verb::Reduce(b(plus()))
15425 .monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp())
15426 .unwrap();
15427 assert_eq!(r.shape, vec![3]);
15428 assert_eq!(ints(&r), vec![5, 7, 9]);
15429 }
15430
15431 #[test]
15432 fn rank_wrapped_reduction_sums_the_last_axis() {
15433 ctx!(c);
15434 let v = Verb::Rank(b(Verb::Reduce(b(plus()))), [1, 1, 1]);
15435 let r = v.monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp()).unwrap();
15436 assert_eq!(r.shape, vec![2]);
15437 assert_eq!(ints(&r), vec![6, 15]);
15438 }
15439
15440 #[test]
15441 fn empty_reduction_uses_the_identity_cell() {
15442 ctx!(c);
15443 let empty = Array::new(vec![0, 2], Data::I64(vec![].into()));
15444 let r = Verb::Reduce(b(plus())).monad(&empty, &mut c, sp()).unwrap();
15445 assert_eq!(r.shape, vec![2]);
15446 assert_eq!(ints(&r), vec![0, 0]);
15447 let r = Verb::Reduce(b(times())).monad(&empty, &mut c, sp()).unwrap();
15448 assert_eq!(ints(&r), vec![1, 1]);
15449 let r = Verb::Reduce(b(floor_v())).monad(&empty, &mut c, sp()).unwrap();
15450 assert!(floats(&r).iter().all(|&x| x == f64::INFINITY));
15451 let r = Verb::Reduce(b(ceil_v())).monad(&empty, &mut c, sp()).unwrap();
15452 assert!(floats(&r).iter().all(|&x| x == f64::NEG_INFINITY));
15453 let r = Verb::Reduce(b(minus())).monad(&empty, &mut c, sp()).unwrap();
15456 assert_eq!(ints(&r), vec![0, 0]);
15457 let r = Verb::Reduce(b(pct())).monad(&empty, &mut c, sp()).unwrap();
15458 assert_eq!(ints(&r), vec![1, 1]);
15459 let r = Verb::Reduce(b(eq_v())).monad(&empty, &mut c, sp()).unwrap();
15460 assert_eq!(bools(&r), vec![1, 1]);
15461 let r = Verb::Reduce(b(plus()))
15463 .monad(&Array::empty(DType::I64), &mut c, sp())
15464 .unwrap();
15465 assert!(r.shape.is_empty());
15466 assert_eq!(ints(&r), vec![0]);
15467 }
15468
15469 #[test]
15470 fn empty_reduction_without_an_identity_is_a_domain_error() {
15471 ctx!(c);
15472 let v = Verb::Hook(b(plus()), b(minus()));
15476 let e = Verb::Reduce(b(v)).monad(&Array::empty(DType::I64), &mut c, sp()).unwrap_err();
15477 assert_eq!(e.kind, ErrorKind::Domain);
15478 assert!(e.msg.contains("identity"), "{}", e.msg);
15479 }
15480
15481 #[test]
15482 fn reduction_with_a_non_primitive_verb_uses_the_general_fold() {
15483 ctx!(c);
15484 let v = Verb::Reduce(b(Verb::Hook(b(plus()), b(minus()))));
15486 let r = v.monad(&Array::from_i64(vec![1, 2, 3]), &mut c, sp()).unwrap();
15487 assert_eq!(ints(&r), vec![2]);
15488 }
15489
15490 #[test]
15491 fn dyadic_reduction_is_the_table() {
15492 ctx!(c);
15493 let v = Verb::Reduce(b(plus()));
15496 let r = v
15497 .dyad(&Array::scalar_i64(2), &Array::from_i64(vec![1, 2, 3]), &mut c, sp())
15498 .unwrap();
15499 assert_eq!(r.shape, vec![3]);
15500 assert_eq!(ints(&r), vec![3, 4, 5]);
15501 let r = v
15504 .dyad(&Array::from_i64(vec![1, 2, 3]), &Array::from_i64(vec![10, 20]), &mut c, sp())
15505 .unwrap();
15506 assert_eq!(r.shape, vec![3, 2]);
15507 assert_eq!(ints(&r), vec![11, 21, 12, 22, 13, 23]);
15508 let cat = Verb::Reduce(b(inf_prim(",", MonadOp::Ravel, DyadOp::AppendLeading)));
15510 let r = cat
15511 .dyad(&Array::from_i64(vec![1, 2]), &Array::from_i64(vec![3, 4]), &mut c, sp())
15512 .unwrap();
15513 assert_eq!(r.shape, vec![4]);
15514 assert_eq!(ints(&r), vec![1, 2, 3, 4]);
15515 }
15516
15517 #[test]
15520 fn integer_overflow_promotes_the_whole_result_to_float() {
15521 ctx!(c);
15522 let r = plus()
15523 .dyad(&Array::from_i64(vec![1, i64::MAX]), &Array::scalar_i64(1), &mut c, sp())
15524 .unwrap();
15525 assert_eq!(r.dtype(), DType::F64);
15526 let v = floats(&r);
15527 assert!(close(v[0], 2.0));
15528 assert!(close(v[1], i64::MAX as f64 + 1.0));
15529 let r = plus()
15531 .dyad(&Array::from_i64(vec![1, 2]), &Array::scalar_i64(1), &mut c, sp())
15532 .unwrap();
15533 assert_eq!(r.dtype(), DType::I64);
15534 }
15535
15536 #[test]
15537 fn reduction_overflow_promotes_too() {
15538 ctx!(c);
15539 let r = Verb::Reduce(b(plus()))
15540 .monad(&Array::from_i64(vec![i64::MAX, i64::MAX]), &mut c, sp())
15541 .unwrap();
15542 assert_eq!(r.dtype(), DType::F64);
15543 assert!(close(floats(&r)[0], 2.0 * i64::MAX as f64));
15544 }
15545
15546 #[test]
15547 fn booleans_widen_to_integers_in_arithmetic() {
15548 ctx!(c);
15549 let bits = Array::new(vec![3], Data::Bool(vec![1, 0, 1].into()));
15550 let r = plus().dyad(&bits, &bits, &mut c, sp()).unwrap();
15551 assert_eq!(r.dtype(), DType::I64);
15552 assert_eq!(ints(&r), vec![2, 0, 2]);
15553 }
15554
15555 #[test]
15556 fn j_division_is_float_and_survives_zero() {
15557 ctx!(c);
15558 let r = pct()
15559 .dyad(&Array::from_i64(vec![1, -1, 0, 6]), &Array::from_i64(vec![0, 0, 0, 4]), &mut c, sp())
15560 .unwrap();
15561 let v = floats(&r);
15562 assert_eq!(v[0], f64::INFINITY);
15563 assert_eq!(v[1], f64::NEG_INFINITY);
15564 assert_eq!(v[2], 0.0);
15565 assert!(close(v[3], 1.5));
15566 }
15567
15568 #[test]
15569 fn apl_division_by_zero_is_a_domain_error_except_zero_by_zero() {
15570 ctx!(c, Agreement::ExactOrScalar);
15571 let r = div_apl()
15572 .dyad(&Array::scalar_i64(0), &Array::scalar_i64(0), &mut c, sp())
15573 .unwrap();
15574 assert!(close(floats(&r)[0], 1.0));
15575 let e = div_apl()
15576 .dyad(&Array::scalar_i64(1), &Array::scalar_i64(0), &mut c, sp())
15577 .unwrap_err();
15578 assert_eq!(e.kind, ErrorKind::Domain);
15579 assert!(e.msg.contains("division by zero"), "{}", e.msg);
15580 let r = div_apl()
15581 .dyad(&Array::scalar_i64(6), &Array::scalar_i64(4), &mut c, sp())
15582 .unwrap();
15583 assert!(close(floats(&r)[0], 1.5));
15584 }
15585
15586 #[test]
15587 fn reciprocal_of_zero_is_infinite() {
15588 ctx!(c);
15589 let r = pct().monad(&Array::from_i64(vec![0, 2]), &mut c, sp()).unwrap();
15590 let v = floats(&r);
15591 assert_eq!(v[0], f64::INFINITY);
15592 assert!(close(v[1], 0.5));
15593 }
15594
15595 #[test]
15596 fn residue_takes_the_sign_of_the_left_argument() {
15597 ctx!(c);
15598 let x = Array::from_i64(vec![3, 3, -3, -3, 0]);
15599 let y = Array::from_i64(vec![5, -5, 5, -5, 5]);
15600 let r = residue_v().dyad(&x, &y, &mut c, sp()).unwrap();
15601 assert_eq!(ints(&r), vec![2, 1, -1, -2, 5]);
15602 let r = residue_v()
15604 .dyad(&Array::from_f64(vec![2.5]), &Array::from_f64(vec![7.0]), &mut c, sp())
15605 .unwrap();
15606 assert!(close(floats(&r)[0], 2.0));
15607 }
15608
15609 #[test]
15610 fn power_stays_integral_when_it_can() {
15611 ctx!(c);
15612 let r = pow_v()
15613 .dyad(&Array::from_i64(vec![2, 0, 5]), &Array::from_i64(vec![10, 0, 1]), &mut c, sp())
15614 .unwrap();
15615 assert_eq!(r.dtype(), DType::I64);
15616 assert_eq!(ints(&r), vec![1024, 1, 5]);
15617 let r = pow_v()
15619 .dyad(&Array::from_i64(vec![2, 4]), &Array::from_i64(vec![-1, 2]), &mut c, sp())
15620 .unwrap();
15621 assert_eq!(r.dtype(), DType::F64);
15622 assert!(close(floats(&r)[0], 0.5));
15623 assert!(close(floats(&r)[1], 16.0));
15624 let r = pow_v()
15626 .dyad(&Array::scalar_i64(10), &Array::scalar_i64(30), &mut c, sp())
15627 .unwrap();
15628 assert_eq!(r.dtype(), DType::F64);
15629 }
15630
15631 #[test]
15632 fn comparisons_yield_booleans() {
15633 ctx!(c);
15634 let r = lt_v()
15635 .dyad(&Array::from_i64(vec![1, 2, 3]), &Array::scalar_i64(2), &mut c, sp())
15636 .unwrap();
15637 assert_eq!(bools(&r), vec![1, 0, 0]);
15638 let r = eq_v()
15639 .dyad(&Array::from_f64(vec![1.0, 2.0]), &Array::from_i64(vec![1, 3]), &mut c, sp())
15640 .unwrap();
15641 assert_eq!(bools(&r), vec![1, 0]);
15642 }
15643
15644 #[test]
15645 fn characters_compare_but_do_not_add() {
15646 ctx!(c);
15647 let a = Array::from_chars(vec!['a', 'b']);
15648 let bb = Array::from_chars(vec!['a', 'c']);
15649 assert_eq!(bools(&eq_v().dyad(&a, &bb, &mut c, sp()).unwrap()), vec![1, 0]);
15650 let e = plus().dyad(&a, &bb, &mut c, sp()).unwrap_err();
15651 assert_eq!(e.kind, ErrorKind::Type);
15652 assert!(e.msg.contains("characters"), "{}", e.msg);
15653 let e = lt_v().dyad(&a, &bb, &mut c, sp()).unwrap_err();
15654 assert_eq!(e.kind, ErrorKind::Type);
15655 let e = plus().dyad(&a, &Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap_err();
15656 assert_eq!(e.kind, ErrorKind::Type);
15657 assert!(e.msg.contains("character"), "{}", e.msg);
15658 let e = plus().monad(&a, &mut c, sp()).unwrap_err();
15659 assert_eq!(e.kind, ErrorKind::Type);
15660 }
15661
15662 #[test]
15663 fn floor_and_ceiling_return_integers_when_they_fit() {
15664 ctx!(c);
15665 let r = floor_v().monad(&Array::from_f64(vec![1.5, -1.5]), &mut c, sp()).unwrap();
15666 assert_eq!(r.dtype(), DType::I64);
15667 assert_eq!(ints(&r), vec![1, -2]);
15668 let r = ceil_v().monad(&Array::from_f64(vec![1.5, -1.5]), &mut c, sp()).unwrap();
15669 assert_eq!(ints(&r), vec![2, -1]);
15670 let r = floor_v().monad(&Array::from_f64(vec![1e30]), &mut c, sp()).unwrap();
15672 assert_eq!(r.dtype(), DType::F64);
15673 let r = floor_v().monad(&Array::from_i64(vec![3]), &mut c, sp()).unwrap();
15675 assert_eq!(ints(&r), vec![3]);
15676 }
15677
15678 #[test]
15679 fn logical_negation_needs_zero_or_one() {
15680 ctx!(c);
15681 let r = not_v().monad(&Array::from_i64(vec![0, 1]), &mut c, sp()).unwrap();
15682 assert_eq!(bools(&r), vec![1, 0]);
15683 let e = not_v().monad(&Array::from_i64(vec![2]), &mut c, sp()).unwrap_err();
15684 assert_eq!(e.kind, ErrorKind::Domain);
15685 }
15686
15687 #[test]
15688 fn signum_abs_and_negation_pick_their_types() {
15689 ctx!(c);
15690 let r = times().monad(&Array::from_i64(vec![-3, 0, 9]), &mut c, sp()).unwrap();
15691 assert_eq!(ints(&r), vec![-1, 0, 1]);
15692 let r = times().monad(&Array::from_f64(vec![-3.0, 0.0, 9.0]), &mut c, sp()).unwrap();
15693 assert_eq!(floats(&r), vec![-1.0, 0.0, 1.0]);
15694 let r = residue_v().monad(&Array::from_i64(vec![-3, 3]), &mut c, sp()).unwrap();
15695 assert_eq!(ints(&r), vec![3, 3]);
15696 let bits = Array::new(vec![2], Data::Bool(vec![0, 1].into()));
15697 let r = minus().monad(&bits, &mut c, sp()).unwrap();
15698 assert_eq!(r.dtype(), DType::I64);
15699 assert_eq!(ints(&r), vec![0, -1]);
15700 }
15701
15702 #[test]
15703 fn square_root_of_a_negative_number_is_complex() {
15704 ctx!(c);
15705 let r = sqrt_v().monad(&Array::from_i64(vec![9]), &mut c, sp()).unwrap();
15706 assert!(close(floats(&r)[0], 3.0));
15707 let r = sqrt_v().monad(&Array::from_i64(vec![-4]), &mut c, sp()).unwrap();
15708 assert_eq!(r.dtype(), DType::Complex);
15709 assert_eq!(r.as_complex_slice().expect("complex data"), &[[0.0, 2.0]]);
15710 }
15711
15712 #[test]
15715 fn shape_tally_and_ravel() {
15716 ctx!(c);
15717 let m = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15718 let r = dollar().monad(&m, &mut c, sp()).unwrap();
15719 assert_eq!(r.shape, vec![2]);
15720 assert_eq!(ints(&r), vec![2, 3]);
15721 let r = pound().monad(&m, &mut c, sp()).unwrap();
15722 assert!(r.shape.is_empty());
15723 assert_eq!(ints(&r), vec![2]);
15724 let r = pound().monad(&Array::scalar_i64(5), &mut c, sp()).unwrap();
15726 assert_eq!(ints(&r), vec![1]);
15727 let r = comma().monad(&m, &mut c, sp()).unwrap();
15728 assert_eq!(r.shape, vec![6]);
15729 assert_eq!(ints(&r), vec![1, 2, 3, 4, 5, 6]);
15730 }
15731
15732 #[test]
15733 fn transpose_reverses_the_axes() {
15734 ctx!(c);
15735 let r = transpose_v().monad(&mat(2, 3, vec![1, 2, 3, 4, 5, 6]), &mut c, sp()).unwrap();
15736 assert_eq!(r.shape, vec![3, 2]);
15737 assert_eq!(ints(&r), vec![1, 4, 2, 5, 3, 6]);
15738 let a = Array::new(vec![2, 1, 3], Data::I64(vec![1, 2, 3, 4, 5, 6].into()));
15740 let r = transpose_v().monad(&a, &mut c, sp()).unwrap();
15741 assert_eq!(r.shape, vec![3, 1, 2]);
15742 assert_eq!(ints(&r), vec![1, 4, 2, 5, 3, 6]);
15743 let v = Array::from_i64(vec![1, 2]);
15745 assert_eq!(transpose_v().monad(&v, &mut c, sp()).unwrap(), v);
15746 }
15747
15748 #[test]
15749 fn head_and_behead() {
15750 ctx!(c);
15751 let m = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15752 let r = head_v().monad(&m, &mut c, sp()).unwrap();
15753 assert_eq!(r.shape, vec![3]);
15754 assert_eq!(ints(&r), vec![1, 2, 3]);
15755 let r = behead_v().monad(&m, &mut c, sp()).unwrap();
15756 assert_eq!(r.shape, vec![1, 3]);
15757 assert_eq!(ints(&r), vec![4, 5, 6]);
15758 let e = Array::new(vec![0, 2], Data::I64(vec![].into()));
15760 let r = head_v().monad(&e, &mut c, sp()).unwrap();
15761 assert_eq!(r.shape, vec![2]);
15762 assert_eq!(ints(&r), vec![0, 0]);
15763 assert_eq!(behead_v().monad(&e, &mut c, sp()).unwrap(), e);
15764 assert_eq!(head_v().monad(&Array::scalar_i64(5), &mut c, sp()).unwrap().shape, Vec::<usize>::new());
15765 let err = behead_v().monad(&Array::scalar_i64(5), &mut c, sp()).unwrap_err();
15766 assert_eq!(err.kind, ErrorKind::Domain);
15767 }
15768
15769 #[test]
15770 fn iota_fills_a_shape_and_reverses_negative_axes() {
15771 ctx!(c);
15772 let r = iota().monad(&Array::from_i64(vec![2, 3]), &mut c, sp()).unwrap();
15773 assert_eq!(r.shape, vec![2, 3]);
15774 assert_eq!(ints(&r), vec![0, 1, 2, 3, 4, 5]);
15775 let r = iota().monad(&Array::scalar_i64(3), &mut c, sp()).unwrap();
15777 assert_eq!(r.shape, vec![3]);
15778 assert_eq!(ints(&r), vec![0, 1, 2]);
15779 let r = iota().monad(&Array::scalar_i64(-3), &mut c, sp()).unwrap();
15781 assert_eq!(ints(&r), vec![2, 1, 0]);
15782 let r = iota().monad(&Array::from_i64(vec![2, -3]), &mut c, sp()).unwrap();
15783 assert_eq!(r.shape, vec![2, 3]);
15784 assert_eq!(ints(&r), vec![2, 1, 0, 5, 4, 3]);
15785 let r = iota().monad(&Array::from_i64(vec![-2, 3]), &mut c, sp()).unwrap();
15786 assert_eq!(ints(&r), vec![3, 4, 5, 0, 1, 2]);
15787 let r = iota().monad(&Array::scalar_i64(0), &mut c, sp()).unwrap();
15789 assert_eq!(r.shape, vec![0]);
15790 assert!(ints(&r).is_empty());
15791 let e = iota().monad(&Array::from_f64(vec![1.5]), &mut c, sp()).unwrap_err();
15793 assert_eq!(e.kind, ErrorKind::Domain);
15794 let e = iota().monad(&mat(1, 1, vec![1]), &mut c, sp()).unwrap_err();
15795 assert_eq!(e.kind, ErrorKind::Rank);
15796 }
15797
15798 #[test]
15799 fn apl_iota_starts_at_the_index_origin() {
15800 ctx!(c, Agreement::ExactOrScalar);
15801 let r = iota_apl(1).monad(&Array::scalar_i64(3), &mut c, sp()).unwrap();
15802 assert_eq!(ints(&r), vec![1, 2, 3]);
15803 let r = iota_apl(0).monad(&Array::scalar_i64(3), &mut c, sp()).unwrap();
15804 assert_eq!(ints(&r), vec![0, 1, 2]);
15805 let e = iota_apl(1).monad(&Array::scalar_i64(-1), &mut c, sp()).unwrap_err();
15806 assert_eq!(e.kind, ErrorKind::Domain);
15807 let r = iota_apl(1).monad(&Array::from_i64(vec![2, 3]), &mut c, sp()).unwrap();
15810 assert_eq!(r.shape, vec![2, 3]);
15811 assert_eq!(ints(&r.as_boxes().expect("boxed")[4]), vec![2, 2]);
15812 }
15813
15814 #[test]
15815 fn reshape_cycles_the_ravel() {
15816 ctx!(c);
15817 let r = dollar()
15818 .dyad(&Array::from_i64(vec![2, 3]), &Array::from_i64(vec![1, 2]), &mut c, sp())
15819 .unwrap();
15820 assert_eq!(r.shape, vec![2, 3]);
15821 assert_eq!(ints(&r), vec![1, 2, 1, 2, 1, 2]);
15822 let r = dollar()
15824 .dyad(&Array::scalar_i64(3), &Array::from_i64(vec![7]), &mut c, sp())
15825 .unwrap();
15826 assert_eq!(r.shape, vec![3]);
15827 assert_eq!(ints(&r), vec![7, 7, 7]);
15828 let r = dollar()
15830 .dyad(&Array::scalar_i64(2), &Array::from_chars(vec!['a', 'b', 'c']), &mut c, sp())
15831 .unwrap();
15832 assert_eq!(r.dtype(), DType::Char);
15833 let e = dollar()
15835 .dyad(&Array::scalar_i64(2), &Array::empty(DType::I64), &mut c, sp())
15836 .unwrap_err();
15837 assert_eq!(e.kind, ErrorKind::Length);
15838 assert!(e.msg.contains("empty"), "{}", e.msg);
15839 let r = dollar()
15841 .dyad(&Array::scalar_i64(0), &Array::empty(DType::I64), &mut c, sp())
15842 .unwrap();
15843 assert_eq!(r.shape, vec![0]);
15844 let e = dollar()
15845 .dyad(&Array::scalar_i64(-1), &Array::from_i64(vec![1]), &mut c, sp())
15846 .unwrap_err();
15847 assert_eq!(e.kind, ErrorKind::Domain);
15848 }
15849
15850 #[test]
15851 fn take_from_both_ends_and_beyond() {
15852 ctx!(c);
15853 let v = Array::from_i64(vec![1, 2, 3, 4]);
15854 let take = |x: Array, y: &Array, c: &mut Ctx<'_>| head_v().dyad(&x, y, c, sp()).unwrap();
15855 assert_eq!(ints(&take(Array::scalar_i64(2), &v, &mut c)), vec![1, 2]);
15856 assert_eq!(ints(&take(Array::scalar_i64(-2), &v, &mut c)), vec![3, 4]);
15857 let short = Array::from_i64(vec![1, 2, 3]);
15859 assert_eq!(ints(&take(Array::scalar_i64(6), &short, &mut c)), vec![1, 2, 3, 0, 0, 0]);
15860 assert_eq!(ints(&take(Array::scalar_i64(-6), &short, &mut c)), vec![0, 0, 0, 1, 2, 3]);
15862 let r = take(Array::scalar_i64(2), &Array::scalar_i64(5), &mut c);
15864 assert_eq!(r.shape, vec![2]);
15865 assert_eq!(ints(&r), vec![5, 0]);
15866 let m = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15868 let r = take(Array::scalar_i64(1), &m, &mut c);
15869 assert_eq!(r.shape, vec![1, 3]);
15870 assert_eq!(ints(&r), vec![1, 2, 3]);
15871 let r = take(Array::scalar_i64(-1), &m, &mut c);
15872 assert_eq!(ints(&r), vec![4, 5, 6]);
15873 let r = take(Array::from_i64(vec![2, 2]), &m, &mut c);
15874 assert_eq!(r.shape, vec![2, 2]);
15875 assert_eq!(ints(&r), vec![1, 2, 4, 5]);
15876 let r = take(Array::from_i64(vec![3, -2]), &m, &mut c);
15877 assert_eq!(r.shape, vec![3, 2]);
15878 assert_eq!(ints(&r), vec![2, 3, 5, 6, 0, 0]);
15879 let r = head_v()
15881 .dyad(&Array::scalar_i64(3), &Array::from_chars(vec!['a']), &mut c, sp())
15882 .unwrap();
15883 assert_eq!(r.data, Data::Char(vec!['a', ' ', ' '].into()));
15884 let e = head_v()
15887 .dyad(&Array::from_i64(vec![1, 1]), &Array::from_i64(vec![1, 2]), &mut c, sp())
15888 .unwrap_err();
15889 assert_eq!(e.kind, ErrorKind::Length);
15890 let r = head_v()
15891 .dyad(&Array::from_i64(vec![1, 2]), &Array::scalar_i64(5), &mut c, sp())
15892 .unwrap();
15893 assert_eq!(r.shape, vec![1, 2]);
15894 assert_eq!(ints(&r), vec![5, 0]);
15895 }
15896
15897 #[test]
15898 fn drop_from_both_ends_and_beyond() {
15899 ctx!(c);
15900 let v = Array::from_i64(vec![1, 2, 3]);
15901 let drop = |x: Array, y: &Array, c: &mut Ctx<'_>| behead_v().dyad(&x, y, c, sp()).unwrap();
15902 assert_eq!(ints(&drop(Array::scalar_i64(1), &v, &mut c)), vec![2, 3]);
15903 assert_eq!(ints(&drop(Array::scalar_i64(-1), &v, &mut c)), vec![1, 2]);
15904 let r = drop(Array::scalar_i64(5), &v, &mut c);
15906 assert_eq!(r.shape, vec![0]);
15907 assert!(ints(&r).is_empty());
15908 let m = mat(2, 3, vec![1, 2, 3, 4, 5, 6]);
15909 let r = drop(Array::scalar_i64(1), &m, &mut c);
15910 assert_eq!(r.shape, vec![1, 3]);
15911 assert_eq!(ints(&r), vec![4, 5, 6]);
15912 let r = drop(Array::from_i64(vec![0, -1]), &m, &mut c);
15913 assert_eq!(r.shape, vec![2, 2]);
15914 assert_eq!(ints(&r), vec![1, 2, 4, 5]);
15915 }
15916
15917 #[test]
15920 fn cells_of_unequal_shapes_are_padded_with_fills() {
15921 ctx!(c);
15922 let v = Verb::Rank(b(iota()), [0, 0, 0]);
15924 let r = v.monad(&Array::from_i64(vec![1, 2, 3]), &mut c, sp()).unwrap();
15925 assert_eq!(r.shape, vec![3, 3]);
15926 assert_eq!(ints(&r), vec![0, 0, 0, 0, 1, 0, 0, 1, 2]);
15927 }
15928
15929 #[test]
15930 fn framing_aligns_lower_rank_cells_at_the_trailing_axes() {
15931 let cells = vec![Array::from_i64(vec![1, 2]), mat(2, 2, vec![1, 2, 3, 4])];
15932 let r = assemble(&[2], cells, sp()).unwrap();
15933 assert_eq!(r.shape, vec![2, 2, 2]);
15934 assert_eq!(ints(&r), vec![1, 2, 0, 0, 1, 2, 3, 4]);
15935 }
15936
15937 #[test]
15938 fn framing_promotes_cell_types() {
15939 let cells = vec![Array::from_i64(vec![1]), Array::from_f64(vec![2.5])];
15940 let r = assemble(&[2], cells, sp()).unwrap();
15941 assert_eq!(r.dtype(), DType::F64);
15942 assert_eq!(floats(&r), vec![1.0, 2.5]);
15943 let cells = vec![Array::from_i64(vec![1]), Array::from_chars(vec!['a'])];
15945 let e = assemble(&[2], cells, sp()).unwrap_err();
15946 assert_eq!(e.kind, ErrorKind::Type);
15947 }
15948
15949 #[test]
15950 fn framing_over_an_empty_frame_yields_an_empty_result() {
15951 let r = assemble(&[0], Vec::new(), sp()).unwrap();
15952 assert_eq!(r.shape, vec![0]);
15953 assert_eq!(r.count(), 0);
15954 }
15955
15956 #[test]
15959 fn fork_applies_both_tines() {
15960 ctx!(c);
15961 let v = Verb::Fork(b(Verb::Reduce(b(plus()))), b(pct()), b(pound()));
15963 let r = v.monad(&Array::from_i64(vec![1, 2, 3, 4]), &mut c, sp()).unwrap();
15964 assert!(close(floats(&r)[0], 2.5));
15965 let v = Verb::Fork(b(minus()), b(plus()), b(plus()));
15967 let r = v
15968 .dyad(&Array::from_i64(vec![5]), &Array::from_i64(vec![3]), &mut c, sp())
15969 .unwrap();
15970 assert_eq!(ints(&r), vec![10]);
15971 }
15972
15973 #[test]
15974 fn noun_fork_supplies_a_constant_left_argument() {
15975 ctx!(c);
15976 let v = Verb::NounFork(Array::scalar_i64(10), b(minus()), b(right_v()));
15977 let r = v.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15978 assert_eq!(ints(&r), vec![9, 8]);
15979 let r = v
15980 .dyad(&Array::scalar_i64(0), &Array::from_i64(vec![1, 2]), &mut c, sp())
15981 .unwrap();
15982 assert_eq!(ints(&r), vec![9, 8]);
15983 }
15984
15985 #[test]
15986 fn hook_reuses_its_right_argument() {
15987 ctx!(c);
15988 let v = Verb::Hook(b(plus()), b(minus()));
15990 let r = v.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
15991 assert_eq!(ints(&r), vec![0, 0]);
15992 let r = v
15994 .dyad(&Array::from_i64(vec![10]), &Array::from_i64(vec![3]), &mut c, sp())
15995 .unwrap();
15996 assert_eq!(ints(&r), vec![7]);
15997 }
15998
15999 #[test]
16000 fn atop_composes() {
16001 ctx!(c);
16002 let v = Verb::Atop(b(minus()), b(plus()));
16003 let r = v.monad(&Array::from_i64(vec![1, 2]), &mut c, sp()).unwrap();
16004 assert_eq!(ints(&r), vec![-1, -2]);
16005 let r = v
16006 .dyad(&Array::from_i64(vec![1]), &Array::from_i64(vec![2]), &mut c, sp())
16007 .unwrap();
16008 assert_eq!(ints(&r), vec![-3]);
16009 }
16010
16011 #[test]
16012 fn trains_apply_to_the_whole_argument() {
16013 assert_eq!(Verb::Hook(b(plus()), b(minus())).ranks(), [RANK_INF; 3]);
16015 assert_eq!(Verb::Reduce(b(plus())).ranks(), [RANK_INF; 3]);
16016 }
16017
16018 #[test]
16021 fn absent_and_unwritten_meanings_are_reported_differently() {
16022 ctx!(c);
16023 let e = eq_v().monad(&Array::scalar_i64(1), &mut c, sp()).unwrap_err();
16024 assert_eq!(e.kind, ErrorKind::Domain);
16025 assert!(e.msg.contains("no monadic meaning"), "{}", e.msg);
16026 let e = not_v()
16027 .dyad(&Array::scalar_i64(1), &Array::scalar_i64(1), &mut c, sp())
16028 .unwrap_err();
16029 assert_eq!(e.kind, ErrorKind::Domain);
16030 assert!(e.msg.contains("no dyadic meaning"), "{}", e.msg);
16031 let e = pound()
16032 .dyad(&Array::scalar_i64(1), &Array::scalar_i64(1), &mut c, sp())
16033 .unwrap_err();
16034 assert_eq!(e.kind, ErrorKind::NotYet);
16035 assert!(e.msg.contains("copy"), "{}", e.msg);
16036 let _ = echo_v();
16038 }
16039
16040 fn seq_par<T: Send>(f: impl Fn() -> T + Sync + Send) -> (T, T) {
16049 (par::with_threads(1, &f), par::with_threads(4, &f))
16050 }
16051
16052 fn noise(n: usize) -> Vec<f64> {
16054 let mut x = 0x2545_f491_4f6c_dd1du64;
16055 (0..n)
16056 .map(|_| {
16057 x ^= x << 13;
16058 x ^= x >> 7;
16059 x ^= x << 17;
16060 (x >> 11) as f64 / (1u64 << 53) as f64 - 0.5
16061 })
16062 .collect()
16063 }
16064
16065 fn f64_mat(rows: usize, cols: usize) -> Array {
16066 Array::new(vec![rows, cols], Data::F64(noise(rows * cols).into()))
16067 }
16068
16069 const BIG: usize = 200_000;
16071
16072 #[test]
16073 fn an_elementwise_dyad_splits_into_the_same_result() {
16074 let x = Array::from_f64(noise(BIG));
16075 let y = Array::from_f64(noise(BIG).iter().map(|v| v + 0.25).collect());
16076 let (one, many) = seq_par(|| {
16077 ctx!(c);
16078 times().dyad(&x, &y, &mut c, sp()).unwrap()
16079 });
16080 assert_eq!(floats(&one), floats(&many));
16081 let (one, many) = seq_par(|| {
16083 ctx!(c);
16084 plus().dyad(&Array::scalar_f64(0.5), &y, &mut c, sp()).unwrap()
16085 });
16086 assert_eq!(floats(&one), floats(&many));
16087 }
16088
16089 #[test]
16090 fn an_elementwise_dyad_that_overflows_widens_the_same_way() {
16091 let mut v = vec![1i64; BIG];
16094 v[BIG - 3] = i64::MAX;
16095 let x = Array::from_i64(v);
16096 let (one, many) = seq_par(|| {
16097 ctx!(c);
16098 plus().dyad(&x, &x, &mut c, sp()).unwrap()
16099 });
16100 assert_eq!(one.dtype(), DType::F64);
16101 assert_eq!(floats(&one), floats(&many));
16102 }
16103
16104 #[test]
16105 fn an_elementwise_monad_splits_into_the_same_result() {
16106 let y = Array::from_f64(noise(BIG));
16107 for v in [minus(), sqrt_v(), floor_v(), pct()] {
16108 let (one, many) = seq_par(|| {
16109 ctx!(c);
16110 v.monad(&Array::from_f64(y.as_f64_slice().unwrap().iter().map(|x| x.abs()).collect()), &mut c, sp())
16111 .unwrap()
16112 });
16113 assert_eq!(one.data, many.data, "{}", v.name());
16114 }
16115 }
16116
16117 #[test]
16118 fn monadic_cells_run_in_parallel_and_frame_in_order() {
16119 let y = f64_mat(400, 512);
16122 let v = Verb::Rank(b(Verb::Reduce(b(plus()))), [1, 1, 1]);
16123 let (one, many) = seq_par(|| {
16124 ctx!(c);
16125 v.monad(&y, &mut c, sp()).unwrap()
16126 });
16127 assert_eq!(one.shape, vec![400]);
16128 assert_eq!(floats(&one), floats(&many));
16129 }
16130
16131 #[test]
16132 fn dyadic_cells_run_in_parallel_and_frame_in_order() {
16133 let x = f64_mat(400, 512);
16134 let y = f64_mat(400, 512);
16135 let v = Verb::Rank(b(plus()), [1, 1, 1]);
16137 let (one, many) = seq_par(|| {
16138 ctx!(c);
16139 v.dyad(&x, &y, &mut c, sp()).unwrap()
16140 });
16141 assert_eq!(one.shape, vec![400, 512]);
16142 assert_eq!(floats(&one), floats(&many));
16143 }
16144
16145 #[test]
16146 fn a_verb_that_writes_output_is_not_pure() {
16147 assert!(plus().is_pure());
16148 assert!(Verb::Rank(b(Verb::Reduce(b(plus()))), [1, 1, 1]).is_pure());
16149 assert!(!echo_v().is_pure());
16150 assert!(!Verb::Rank(b(Verb::Atop(b(echo_v()), b(plus()))), [1, 1, 1]).is_pure());
16151 }
16152
16153 #[test]
16154 fn an_impure_verb_keeps_its_cells_in_order() {
16155 let y = Array::new(vec![16, 8192], Data::I64((0..16 * 8192).collect::<Vec<i64>>().into()));
16158 let v = Verb::Rank(b(Verb::Atop(b(echo_v()), b(head_v()))), [1, 1, 1]);
16159 let mut seen: Vec<i64> = Vec::new();
16160 let mut sink = |s: &str| {
16161 if let Some(first) = s.split_whitespace().next() && let Ok(n) = first.parse::<i64>() {
16162 seen.push(n);
16163 }
16164 };
16165 let mut env = Env::new(Vec::new());
16166 let mut c = Ctx {
16167 cfg: EvalCfg {
16168 agreement: Agreement::LeadingPrefix,
16169 fmt: FmtOpts::J,
16170 tol: Tol::J,
16171 rules: Rules::default(),
16172 },
16173 out: &mut sink,
16174 inp: None,
16175 env: &mut env,
16176 device: None,
16177 shy: false,
16178 };
16179 v.monad(&y, &mut c, sp()).unwrap();
16180 assert_eq!(seen, (0..16).map(|i| i * 8192).collect::<Vec<i64>>());
16181 }
16182
16183 #[test]
16184 fn a_wide_item_reduce_folds_every_column_in_order() {
16185 let y = f64_mat(300, 512);
16188 for v in [plus(), minus(), floor_v()] {
16189 let (one, many) = seq_par(|| {
16190 ctx!(c);
16191 Verb::Reduce(b(v.clone())).monad(&y, &mut c, sp()).unwrap()
16192 });
16193 assert_eq!(one.shape, vec![512]);
16194 assert_eq!(floats(&one), floats(&many), "{}", v.name());
16195 }
16196 }
16197
16198 #[test]
16199 fn a_wide_item_integer_reduce_is_exact() {
16200 let n = 300;
16201 let m = 512;
16202 let y = Array::new(
16203 vec![n, m],
16204 Data::I64((0..(n * m) as i64).map(|i| i % 977 - 400).collect::<Vec<i64>>().into()),
16205 );
16206 let (one, many) = seq_par(|| {
16207 ctx!(c);
16208 Verb::Reduce(b(minus())).monad(&y, &mut c, sp()).unwrap()
16209 });
16210 assert_eq!(ints(&one), ints(&many));
16211 }
16212
16213 #[test]
16214 fn a_narrow_item_reduce_chunks_the_items() {
16215 let y = f64_mat(300_000, 8);
16219 let (one, many) = seq_par(|| {
16220 ctx!(c);
16221 Verb::Reduce(b(plus())).monad(&y, &mut c, sp()).unwrap()
16222 });
16223 assert_eq!(one.shape, vec![8]);
16224 for (p, q) in floats(&one).iter().zip(floats(&many)) {
16225 assert!((p - q).abs() <= 1e-12 * p.abs().max(1.0), "{p} vs {q}");
16226 }
16227 let ints_y = Array::new(
16228 vec![300_000, 8],
16229 Data::I64((0..300_000 * 8).map(|i| (i % 101) as i64 - 50).collect::<Vec<i64>>().into()),
16230 );
16231 let (one, many) = seq_par(|| {
16232 ctx!(c);
16233 Verb::Reduce(b(plus())).monad(&ints_y, &mut c, sp()).unwrap()
16234 });
16235 assert_eq!(ints(&one), ints(&many));
16236 }
16237
16238 #[test]
16239 fn a_vector_reduce_folds_the_flat_buffer() {
16240 let y = Array::from_f64(noise(BIG * 4));
16241 let (one, many) = seq_par(|| {
16242 ctx!(c);
16243 Verb::Reduce(b(plus())).monad(&y, &mut c, sp()).unwrap()
16244 });
16245 let (p, q) = (floats(&one)[0], floats(&many)[0]);
16246 assert!((p - q).abs() <= 1e-12 * p.abs().max(1.0), "{p} vs {q}");
16247
16248 let ints_y = Array::from_i64((0..BIG as i64 * 4).map(|i| i % 1009 - 500).collect());
16251 for v in [plus(), minus(), ceil_v()] {
16252 let (one, many) = seq_par(|| {
16253 ctx!(c);
16254 Verb::Reduce(b(v.clone())).monad(&ints_y, &mut c, sp()).unwrap()
16255 });
16256 assert_eq!(ints(&one), ints(&many), "{}", v.name());
16257 }
16258 }
16259
16260 #[test]
16261 fn a_reduce_that_overflows_falls_back_to_the_sequential_widening() {
16262 let mut v: Vec<i64> = vec![1; BIG];
16263 v[7] = i64::MAX;
16264 let y = Array::from_i64(v);
16265 let (one, many) = seq_par(|| {
16266 ctx!(c);
16267 Verb::Reduce(b(plus())).monad(&y, &mut c, sp()).unwrap()
16268 });
16269 assert_eq!(one.dtype(), DType::F64);
16270 assert_eq!(floats(&one), floats(&many));
16271 }
16272
16273 #[test]
16274 fn a_boolean_reduce_matches_the_sequential_promotion() {
16275 let n = BIG;
16276 let y = Array::new(
16277 vec![n],
16278 Data::Bool((0..n).map(|i| (i % 3 == 0) as u8).collect::<Vec<u8>>().into()),
16279 );
16280 let (one, many) = seq_par(|| {
16281 ctx!(c);
16282 Verb::Reduce(b(plus())).monad(&y, &mut c, sp()).unwrap()
16283 });
16284 assert_eq!(one.dtype(), DType::I64);
16285 assert_eq!(ints(&one), ints(&many));
16286 assert_eq!(ints(&one)[0], n.div_ceil(3) as i64);
16287 }
16288}