summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordzaima <dzaimagit@gmail.com>2024-05-20 01:27:10 +0300
committerGitHub <noreply@github.com>2024-05-20 01:27:10 +0300
commitf02f882801be3341d2b807d74648ce543e72a650 (patch)
treee2dec8c6286dc3c94850bbba3bdd4b0ee755c113
parent4f898f38d22ebec96965f7293ed4e40bd24c152d (diff)
parent44c8da7dc76a0fd4b38146a6d974586269d63707 (diff)
Merge pull request #109 from ap29600/highrank_select_ucw
native path in `select_ucw` for high rank `𝕩`
-rw-r--r--src/builtins/compare.c7
-rw-r--r--src/builtins/select.c117
-rw-r--r--src/builtins/sfns.c4
-rw-r--r--src/singeli/src/equal.singeli1
-rw-r--r--src/singeli/src/mask.singeli1
-rw-r--r--src/utils/calls.h3
-rw-r--r--test/cases/perf.bqn7
-rw-r--r--test/cases/under.bqn24
8 files changed, 114 insertions, 50 deletions
diff --git a/src/builtins/compare.c b/src/builtins/compare.c
index a8b5268f..f55831cf 100644
--- a/src/builtins/compare.c
+++ b/src/builtins/compare.c
@@ -107,13 +107,14 @@ u8 const eqFnData[] = { // for the main diagonal, amount to shift length by; oth
#else
#define F(X) equal_##X
bool F(1_1)(void* w, void* x, u64 l, u64 d) {
+ assert(l>0);
u64* wp = w; u64* xp = x;
usz q = l/64;
for (usz i=0; i<q; i++) if (wp[i] != xp[i]) return false;
usz r = (-l)%64; return r==0 || (wp[q]^xp[q])<<r == 0;
}
#define DEF_EQ_U1(N, T) \
- bool F(1_##N)(void* w, void* x, u64 l, u64 d) { \
+ bool F(1_##N)(void* w, void* x, u64 l, u64 d) { assert(l>0); \
if (d!=0) { void* t=w; w=x; x=t; } \
u64* wp = w; T* xp = x; \
for (usz i=0; i<l; i++) if (bitp_get(wp,i)!=xp[i]) return false; \
@@ -127,7 +128,7 @@ u8 const eqFnData[] = { // for the main diagonal, amount to shift length by; oth
#define DEF_EQ_I(NAME, S, T, INIT) \
bool F(NAME)(void* w, void* x, u64 l, u64 d) { \
- INIT \
+ assert(l>0); INIT \
S* wp = w; T* xp = x; \
for (usz i=0; i<l; i++) if (wp[i]!=xp[i]) return false; \
return true; \
@@ -143,7 +144,7 @@ u8 const eqFnData[] = { // for the main diagonal, amount to shift length by; oth
#undef DEF_EQ_I
#undef DEF_EQ
#endif
-bool notEq(void* a, void* b, u64 l, u64 data) { return false; }
+bool notEq(void* a, void* b, u64 l, u64 data) { assert(l>0); return false; }
INIT_GLOBAL EqFn eqFns[] = {
F(1_1), F(1_8), F(1_16), F(1_32), F(1_f64), notEq, notEq, notEq,
F(1_8), F(8_8), F(s8_16), F(s8_32), F(s8_f64), notEq, notEq, notEq,
diff --git a/src/builtins/select.c b/src/builtins/select.c
index 3665dbaa..f8b8e536 100644
--- a/src/builtins/select.c
+++ b/src/builtins/select.c
@@ -372,24 +372,27 @@ B select_c2(B t, B w, B x) {
extern INIT_GLOBAL u8 reuseElType[t_COUNT];
-B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊) x, assumes w is a typed (elNum) list of valid indices, only el_f64 if strictly necessary
+B select_replace(u32 chr, B w, B x, B rep, usz wia, usz cam, usz csz) { // consumes all; (⥊rep)⌾(⥊w⊏cam‿csz⥊⊢) x; assumes csz>0, that w is a typed (elNum) list of valid indices (squeeze already attempted on el_f64), and that rep has the proper element count
+ assert(csz > 0);
#if CHECK_VALID
- TALLOC(bool, set, xia);
- bool sparse = wia < xia/64;
- if (!sparse) for (i64 i = 0; i < xia; i++) set[i] = false;
+ TALLOC(bool, set, cam);
+ bool sparse = wia < cam/64;
+ if (!sparse) for (i64 i = 0; i < cam; i++) set[i] = false;
#define SPARSE_INIT(WI) \
- if (sparse) for (usz i = 0; i < wia; i++) { \
- i64 cw = WI; if (RARE(cw<0)) cw+= (i64)xia; set[cw] = false; \
+ if (sparse) for (usz i = 0; i < wia; i++) { \
+ i64 cw = WI; if (RARE(cw<0)) cw+= (i64)cam; set[cw] = false; \
}
- #define EQ(F) if (set[cw] && (F)) thrF("𝔽⌾(a⊸%c): Incompatible result elements", chr); set[cw] = true;
+ #define EQ(ITER,F) if (set[cw]) ITER if (F) thrF("𝔽⌾(a⊸%c): Incompatible result elements", chr); set[cw] = true;
+ #define EQ1(F) EQ(,F)
#define FREE_CHECK TFREE(set)
#else
#define SPARSE_INIT(GET)
- #define EQ(F)
+ #define EQ(ITER,F)
+ #define EQ1(F)
#define FREE_CHECK
#endif
- #define READ_W(N,I) i64 N = (i64)wp[I]; if (RARE(N<0)) N+= (i64)xia
+ #define READ_W(N,I) i64 N = (i64)wp[I]; if (RARE(N<0)) N+= (i64)cam
u8 we = TI(w,elType); assert(elNum(we));
u8 xe = TI(x,elType);
u8 re = el_or(xe, TI(rep,elType));
@@ -399,16 +402,25 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊
f64* wp = f64any_ptr(w);
SPARSE_INIT((i64)wp[i])
- MAKE_MUT(r, xia);
+ MAKE_MUT(r, cam*csz);
mut_init_copy(r, x, re);
NOGC_E;
MUTG_INIT(r); SGet(rep)
- for (usz i = 0; i < wia; i++) {
- READ_W(cw, i);
- B cn = Get(rep, i);
- EQ(!equal(mut_getU(r, cw), cn));
- mut_rm(r, cw);
- mut_setG(r, cw, cn);
+ if (csz==1) {
+ for (usz i = 0; i < wia; i++) {
+ READ_W(cw, i);
+ B cn = Get(rep, i);
+ EQ1(!equal(mut_getU(r, cw), cn));
+ mut_rm(r, cw);
+ mut_setG(r, cw, cn);
+ }
+ } else {
+ for (usz i = 0; i < wia; i++) {
+ READ_W(cw, i);
+ EQ(for (usz j = 0; j < csz; j++), !equal(mut_getU(r, cw*csz + j), Get(rep, i*csz + j)));
+ for (usz j = 0; j < csz; j++) mut_rm(r, cw*csz + j);
+ mut_copyG(r, cw*csz, rep, i*csz, csz);
+ }
}
ra = mut_fp(r);
goto dec_ret_ra;
@@ -419,7 +431,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊
i32* wp = i32any_ptr(w);
SPARSE_INIT(wp[i])
bool reuse = reusable(x) && re==reuseElType[TY(x)];
- SLOWIF(!reuse && xia>100 && wia<xia/50) SLOW2("⌾(𝕨⊸⊏)𝕩 or ⌾(𝕨⊸⊑)𝕩 because not reusable", w, x);
+ SLOWIF(!reuse && cam>100 && wia<cam/50) SLOW2("⌾(𝕨⊸⊏)𝕩 or ⌾(𝕨⊸⊑)𝕩 because not reusable", w, x);
switch (re) { default: UD;
case el_i8: rep = toI8Any(rep); ra = reuse? a(REUSE(x)) : cpyI8Arr(x); goto do_u8;
case el_c8: rep = toC8Any(rep); ra = reuse? a(REUSE(x)) : cpyC8Arr(x); goto do_u8;
@@ -432,11 +444,19 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊
TyArr* na = toBitArr(rep); rep = taga(na);
u64* np = bitarrv_ptr(na);
u64* rp = (void*)((TyArr*)ra)->a;
- for (usz i = 0; i < wia; i++) {
- READ_W(cw, i);
- bool cn = bitp_get(np, i);
- EQ(cn != bitp_get(rp, cw));
- bitp_set(rp, cw, cn);
+ if (csz==1) {
+ for (usz i = 0; i < wia; i++) {
+ READ_W(cw, i);
+ bool cn = bitp_get(np, i);
+ EQ1(cn != bitp_get(rp, cw));
+ bitp_set(rp, cw, cn);
+ }
+ } else {
+ for (usz i = 0; i < wia; i++) {
+ READ_W(cw, i);
+ EQ(for (usz j = 0; j < csz; j++), bitp_get(np, i*csz + j) != bitp_get(rp, cw*csz + j));
+ COPY_TO(rp, el_bit, cw*csz, rep, i*csz, csz);
+ }
}
goto dec_ret_ra;
}
@@ -444,27 +464,37 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊
ra = reuse? a(REUSE(x)) : cpyHArr(x);
B* rp = harrP_parts((HArr*)ra).a;
SGet(rep)
- for (usz i = 0; i < wia; i++) {
- READ_W(cw, i);
- B cn = Get(rep, i);
- EQ(!equal(cn,rp[cw]));
- dec(rp[cw]);
- rp[cw] = cn;
+ if (csz==1) {
+ for (usz i = 0; i < wia; i++) {
+ READ_W(cw, i);
+ B cn = Get(rep, i);
+ EQ1(!equal(cn,rp[cw]));
+ dec(rp[cw]);
+ rp[cw] = cn;
+ }
+ } else {
+ for (usz i = 0; i < wia; i++) {
+ READ_W(cw, i);
+ EQ(for (usz j = 0; j < csz; j++), !equal(Get(rep, i*csz + j), rp[cw*csz + j]));
+ for (usz j = 0; j < csz; j++) dec(rp[cw*csz + j]);
+ COPY_TO(rp, el_B, cw*csz, rep, i*csz, csz);
+ }
}
goto dec_ret_ra;
}
}
#define IMPL(T) do { \
+ if (csz!=1) goto do_tycell; \
T* rp = (void*)((TyArr*)ra)->a; \
T* np = tyany_ptr(rep); \
for (usz i = 0; i < wia; i++) { \
READ_W(cw, i); \
T cn = np[i]; \
- EQ(cn != rp[cw]); \
+ EQ1(cn != rp[cw]); \
rp[cw] = cn; \
} \
- goto dec_ret_ra; \
+ goto dec_ret_ra; \
} while(0)
do_u8: IMPL(u8);
@@ -473,6 +503,18 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊
do_u64: IMPL(u64);
#undef IMPL
+ do_tycell:;
+ u8 cwidth = csz * elWidth(re);
+ u8* rp = (u8*) ((TyArr*)ra)->a;
+ u8* np = tyany_ptr(rep);
+ EqFnObj eq = EQFN_GET(re,re);
+ for (usz i = 0; i < wia; i++) {
+ READ_W(cw, i);
+ EQ1(!EQFN_CALL(eq, rp + cw*cwidth, np + i*cwidth, csz));
+ COPY_TO(rp, re, cw*csz, rep, i*csz, csz);
+ }
+ goto dec_ret_ra;
+
dec_ret_ra:;
@@ -482,11 +524,12 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊
#undef SPARSE_INIT
#undef EQ
+ #undef EQ1
#undef FREE_CHECK
}
B select_ucw(B t, B o, B w, B x) {
- if (isAtm(x) || RNK(x)!=1 || isAtm(w)) { def: return def_fn_ucw(t, o, w, x); }
+ if (isAtm(x) || isAtm(w)) { def: return def_fn_ucw(t, o, w, x); }
usz xia = IA(x);
usz wia = IA(w);
u8 we = TI(w,elType);
@@ -495,13 +538,19 @@ B select_ucw(B t, B o, B w, B x) {
if (!elNum(we)) goto def;
}
B rep;
- if (isArr(o)) {
+ if (isArr(o) && RNK(x)>0) {
i64 buf[2];
if (wia!=0 && (!getRange_fns[we](tyany_ptr(w), buf, wia) || buf[0]<-(i64)xia || buf[1]>=xia)) thrF("𝔽⌾(a⊸⊏)𝕩: Indexing out-of-bounds (%l∊a, %H≡≢𝕩)", buf[1]>=xia?buf[1]:buf[0], x);
rep = incG(o);
} else {
rep = c1(o, C2(select, incG(w), incG(x)));
}
- if (isAtm(rep) || !eqShape(w, rep)) thrF("𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected %H, got %H)", w, rep);
- return select_replace(U'⊏', w, x, rep, wia, xia);
+ usz xr = RNK(x);
+ usz wr = RNK(w);
+ usz rr = RNK(rep);
+ bool ok = !isAtm(rep) && xr+wr==rr+1 && eqShPart(SH(w),SH(rep),wr) && eqShPart(SH(x)+1,SH(rep)+wr,xr-1);
+ if (!ok) thrF("𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (%H ≡ shape of a, %2H ≡ shape of ⊏𝕩, %H ≡ shape of result of 𝔽)", w, xr-1, SH(x)+1, rep);
+ usz csz = arr_csz(x);
+ if (csz == 0) { decG(rep); decG(w); return x; }
+ return select_replace(U'⊏', w, x, rep, wia, *SH(x), csz);
}
diff --git a/src/builtins/sfns.c b/src/builtins/sfns.c
index 0a9109a5..952b27f9 100644
--- a/src/builtins/sfns.c
+++ b/src/builtins/sfns.c
@@ -1302,7 +1302,7 @@ B pick_uc1(B t, B o, B x) {
-B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia);
+B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcia);
B select_ucw(B t, B o, B w, B x);
B select_c2(B,B,B);
B pick_ucw(B t, B o, B w, B x) {
@@ -1329,7 +1329,7 @@ B pick_ucw(B t, B o, B w, B x) {
w = num_squeeze(mut_fcd(r, w));
B rep = isArr(o)? incG(o) : c1(o, C2(select, incG(w), C1(shape, incG(x))));
if (isAtm(rep) || !eqShape(w, rep)) thrF("𝔽⌾(a⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected %H, got %H)", w, rep);
- return select_replace(U'⊑', w, x, rep, wia, xia);
+ return select_replace(U'⊑', w, x, rep, wia, xia, 1);
}
decG(w);
}
diff --git a/src/singeli/src/equal.singeli b/src/singeli/src/equal.singeli
index b80eb02d..3bdd7ff2 100644
--- a/src/singeli/src/equal.singeli
+++ b/src/singeli/src/equal.singeli
@@ -15,6 +15,7 @@ fn equal{W, X}(w:*void, x:*void, l:u64, d:u64) : u1 = {
def vw = arch_defvw
def bulk = vw / width{X}
if (W!=X) if (d!=0) swap{w,x}
+ assert{l>0}
if (W==u1) {
if (X==u1) { # bitarr ≡ bitarr
diff --git a/src/singeli/src/mask.singeli b/src/singeli/src/mask.singeli
index d9979b7d..adc575e2 100644
--- a/src/singeli/src/mask.singeli
+++ b/src/singeli/src/mask.singeli
@@ -103,6 +103,7 @@ def maskedLoop{bulk} = maskedLoop{bulk,0}
def maskedLoopPositive{bulk}{vars,begin==0,end:L,iter} = {
+ assert{end > 0}
i:L = 0
while(i < (end-1)/bulk) {
mlExec{i, iter, vars, bulk, maskNone}
diff --git a/src/utils/calls.h b/src/utils/calls.h
index d38cf376..137b8810 100644
--- a/src/utils/calls.h
+++ b/src/utils/calls.h
@@ -26,14 +26,13 @@ CMP_DEF(le, AS);
#define CMP_AA_IMM(FN, ELT, WHERE, WP, XP, LEN) CMP_AA_CALL(CMP_AA_FN(FN, ELT), WHERE, WP, XP, LEN)
#define CMP_AS_IMM(FN, ELT, WHERE, WP, X, LEN) CMP_AS_CALL(CMP_AS_FN(FN, ELT), WHERE, WP, X, LEN)
-// Check if the l elements starting at a and b match
typedef bool (*EqFn)(void* a, void* b, u64 l, u64 data);
extern INIT_GLOBAL EqFn eqFns[];
extern u8 const eqFnData[];
#define EQFN_INDEX(W_ELT, X_ELT) ((W_ELT)*8 + (X_ELT))
typedef struct { EqFn fn; u8 data; } EqFnObj;
#define EQFN_GET(W_ELT, X_ELT) ({ u8 eqfn_i_ = EQFN_INDEX(W_ELT, X_ELT); (EqFnObj){.fn=eqFns[eqfn_i_], .data=eqFnData[eqfn_i_]}; })
-#define EQFN_CALL(FN, W, X, L) (FN).fn(W, X, L, (FN).data)
+#define EQFN_CALL(FN, W, X, L) (FN).fn(W, X, L, (FN).data) // check if L elements starting at a and b match; assumes L≥1
typedef bool (*RangeFn)(void* xp, i64* res, u64 len); // writes min,max in res, assumes len≥1; returns 0 and leaves res undefined if either any (floor(x)≠x or abs>2⋆53), or (x≠(i64)x)
extern INIT_GLOBAL RangeFn getRange_fns[el_f64+1]; // limited to ≤el_f64
diff --git a/test/cases/perf.bqn b/test/cases/perf.bqn
index fec72971..0a1ca625 100644
--- a/test/cases/perf.bqn
+++ b/test/cases/perf.bqn
@@ -29,11 +29,16 @@
# in-place ⌾⊑
%USE fastone ⋄ a←5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾⊑↩}_fastone 4 ⋄ ! (10↑a) ≡ (5+n)∾6+↕9
-# in-place ⌾(l⊸⊏)
+# in-place ⌾(l⊸⊏) list
%USE fastone ⋄ a←⋈¨5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! (⋈¨5‿6‿7‿8‿9‿1000000‿1000001‿1000002‿1000003‿1000004+0‿n‿0‿n‿0‿0‿0‿0‿0‿n) ≡ (5↑a)∾¯5↑a #%NDEBUG
%USE fastone ⋄ a←5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! ( 5‿6‿7‿8‿9‿1000000‿1000001‿1000002‿1000003‿1000004+0‿n‿0‿n‿0‿0‿0‿0‿0‿n) ≡ (5↑a)∾¯5↑a #%NDEBUG
%USE fastone ⋄ a←5+1e6⥊@+↕10000 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! (@+5‿6‿7‿8‿9‿10000‿10001‿10002‿10003‿10004 +0‿n‿0‿n‿0‿0‿0‿0‿0‿n) ≡ (5↑a)∾¯5↑a #%NDEBUG
+# in-place ⌾(l⊸⊏) highrank
+%USE fastone ⋄ a←∘‿2‿2⥊⋈¨5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! a ≡ (n×⟨1,3,¯1+≠a⟩∊˜↕≠a) + ∘‿2‿2⥊⋈¨5+↕1e6 #%NDEBUG
+%USE fastone ⋄ a←∘‿2‿2⥊5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! a ≡ (n×⟨1,3,¯1+≠a⟩∊˜↕≠a) + ∘‿2‿2⥊5+↕1e6 #%NDEBUG
+%USE fastone ⋄ a←∘‿2‿2⥊5+1e6⥊@+↕10000 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! a ≡ (n×⟨1,3,¯1+≠a⟩∊˜↕≠a) + ∘‿2‿2⥊5+1e6⥊@+↕10000 #%NDEBUG
+
# in-place ∾⟜atom & ∾⟜list
%USE fastone ⋄ %USE tvar2 ⋄ {𝕊v: j←<⍟(×≡) v ⋄ {a←𝕩 ⋄ n←{𝕊: a∾↩j}_fastone 4 ⋄ !( n +≠𝕩)≡≠a ⋄ !a≡v¨a}_tvar2_"f" 1e6 ⥊<𝕩}¨ ⟨1, 1‿2, 'a'⟩
%USE fastone ⋄ %USE tvar2 ⋄ {𝕊v: j←10⥊<v ⋄ {a←𝕩 ⋄ n←{𝕊: a∾↩j}_fastone 4 ⋄ !((10×n)+≠𝕩)≡≠a ⋄ !a≡v¨a}_tvar2_"f" 1e6 ⥊<𝕩}¨ ⟨1, 1‿2, 'a'⟩
diff --git a/test/cases/under.bqn b/test/cases/under.bqn
index e3b8fff1..45c8d4c9 100644
--- a/test/cases/under.bqn
+++ b/test/cases/under.bqn
@@ -1,20 +1,17 @@
%DEF var V←•internal.Variation ⋄ LV←•internal.ListVariations ⋄ CLR←•internal.ClearRefs
%DEF tvar %USE var ⋄ _tvar ← {F _𝕣 x: (CLR@) ⊢ {F 𝕩 V x}¨ LV 𝕩; w F _𝕣 x: (CLR@) ⊢ (LV 𝕨) {(𝕨 V w) F 𝕩 V x}⌜ LV 𝕩}
+%DEF eqvar %USE tvar ⋄ _eqvar ← {r ← 𝕨 𝔽 _tvar 𝕩 ⋄ !∘≡⟜(⊑r)¨ r ⋄ ⊑r}
%DEF evar %USE tvar ⋄ _evar ← {ok←{⇐} ⋄ r←⥊𝕨 ok∘𝔽⎊{𝕊: •CurrentError@} _tvar 𝕩 ⋄ {⊑ok∊r? !"TEST FAIL: variation didn't error"; ("TEST FAIL: varying error messages"⊸⋈ ! 1=≠)⍷r ⋄ !⊑r}}
# ¨ & ˘
# !% ⊢⌾(⊢¨) 4 # TODO enable
# !% ⊢⌾(⊢˘) 4 # TODO enable
-# ⊏ & ⊑
-!"𝔽⌾(a⊸⊑): Incompatible result elements" % 10‿20⌾(⟨1‿2,1‿2⟩⊸⊑) 4‿5⥊↕9
-!"𝔽⌾(a⊸⊏): Incompatible result elements" % 10‿20⌾(3‿3⊸⊏) ↕10
-!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨2⟩, got ⟨3⟩)" % 10‿20‿30⌾(2‿3⊸⊏) ↕10
-!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨2⟩, got ⟨3⟩)" % 1⊸∾⌾(2‿3⊸⊏) ↕10
-!"𝔽⌾(a⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨2⟩, got ⟨3⟩)" % 10‿20‿30⌾(⟨1‿2,2‿1⟩⊸⊑) 4‿4⥊↕16
-!"𝔽⌾(a⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨2⟩, got ⟨3⟩)" % 1⊸∾⌾(⟨1‿2,2‿1⟩⊸⊑) 4‿4⥊↕16
# ⊑
+!"𝔽⌾(a⊸⊑): Incompatible result elements" % 10‿20⌾(⟨1‿2,1‿2⟩⊸⊑) 4‿5⥊↕9
+!"𝔽⌾(a⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨2⟩, got ⟨3⟩)" % 10‿20‿30⌾(⟨1‿2,2‿1⟩⊸⊑) 4‿4⥊↕16
+!"𝔽⌾(a⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨2⟩, got ⟨3⟩)" % 1⊸∾⌾(⟨1‿2,2‿1⟩⊸⊑) 4‿4⥊↕16
!"𝔽⌾(n⊸⊑)𝕩: reading out-of-bounds (n≡¯10, 3≡≠𝕩)" % 1⊸+⌾(¯10⊸⊑) ↕3
!"⊑: 𝕨 contained list with mixed-type elements" % 'a'⌾(1‿'a'⊸⊑) 4‿5⥊↕9
!"⊑: Indexing out-of-bounds (index 1‿5 in array of shape 4‿5)" % 'a'⌾(1‿5⊸⊑) 4‿5⥊↕9
@@ -33,6 +30,8 @@ a←10‿10‿10⥊↕1000 ⋄ ! a ≡ a⌾((↕≢a)⊸⊑) a
# ⊏
n←500 ⋄ a←↕n ⋄ i←(-n)+↕2×n ⋄ r←⌽(2×n)⥊a ⋄ ! (⌽a) ≡ r⌾(i⊸⊏) a
+!"⊏: 𝕩 cannot be a unit" % 0‿0⌾(0‿0⊸⊏) <0
+!"⊏: 𝕩 cannot be a unit" % %USE evar ⋄ (↕0) {⊢⌾(𝕨⊸⊏)𝕩}_evar <0
!"𝔽⌾(a⊸⊏)𝕩: Indexing out-of-bounds (¯11∊a, ⟨10⟩≡≢𝕩)" % %USE evar ⋄ 5‿¯11 {0‿1⌾(𝕨⊸⊏)𝕩}_evar 10⥊1‿0‿1
!"𝔽⌾(a⊸⊏)𝕩: Indexing out-of-bounds (10∊a, ⟨10⟩≡≢𝕩)" % %USE evar ⋄ 9‿10 {0‿1⌾(𝕨⊸⊏)𝕩}_evar 10⥊1‿0‿1
!"𝔽⌾(a⊸⊏)𝕩: Indexing out-of-bounds (10∊a, ⟨10⟩≡≢𝕩)" % %USE evar ⋄ "ab" {𝕨⌾(9‿10⊸⊏)𝕩}_evar 10⥊"foo"
@@ -43,7 +42,10 @@ n←500 ⋄ a←↕n ⋄ i←(-n)+↕2×n ⋄ r←⌽(2×n)⥊a ⋄ ! (⌽a) ≡
100⊸+⌾(1‿2‿¯4⊸⊏) <¨↕10 %% <¨ 0‿101‿102‿3‿4‿5‿106‿7‿8‿9
100⊸+⌾(1‿2‿¯4⊸⊏) ↕10 %% 0‿101‿102‿3‿4‿5‿106‿7‿8‿9
⟨10⊸+⌾(1‿2⊸⊏) 0↓a←↕4, a⟩ %% ⟨0‿11‿12‿3,0‿1‿2‿3⟩
-%USE tvar ⋄ !∘≡¨⟜⊏⊸⊢⟜⊑ 0‿0‿1‿1 {𝕨⌾(1‿2‿4‿5⊸⊏) 𝕩} _tvar 1‿0‿1‿0‿1‿0‿1‿1‿0 %% 1‿0‿0‿0‿1‿1‿1‿1‿0
+%USE eqvar ⋄ 0‿0‿1‿1 {𝕨⌾(1‿2‿4‿5⊸⊏) 𝕩} _eqvar 1‿0‿1‿0‿1‿0‿1‿1‿0 %% 1‿0‿0‿0‿1‿1‿1‿1‿0
+!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (⟨2⟩ ≡ shape of a, ⟨⟩ ≡ shape of ⊏𝕩, ⟨3⟩ ≡ shape of result of 𝔽)" % 10‿20‿30⌾(2‿3⊸⊏) ↕10
+!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (⟨2⟩ ≡ shape of a, ⟨⟩ ≡ shape of ⊏𝕩, ⟨3⟩ ≡ shape of result of 𝔽)" % 1⊸∾⌾(2‿3⊸⊏) ↕10
+!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (⟨3⟩ ≡ shape of a, ⟨10⟩ ≡ shape of ⊏𝕩, 2‿10 ≡ shape of result of 𝔽)" % %USE evar ⋄ 1⊸↓⌾(2‿3‿4⊸⊏)_evar 10‿10⥊0
!"𝔽⌾(a⊸⊏): Incompatible result elements" % 3‿4⌾(1‿1⊸⊏) ↕10
!"𝔽⌾(a⊸⊏): Incompatible result elements" % 3‿4⌾(1‿¯9⊸⊏) ↕10
!"𝔽⌾(a⊸⊏): Incompatible result elements" % %USE evar ⋄ 3‿4 {𝕨⌾(1‿¯9⊸⊏) 𝕩}_evar ↕10
@@ -52,10 +54,16 @@ n←500 ⋄ a←↕n ⋄ i←(-n)+↕2×n ⋄ r←⌽(2×n)⥊a ⋄ ! (⌽a) ≡
!"𝔽⌾(a⊸⊏): Incompatible result elements" % 3‿4‿5⌾(1‿1‿2⊸⊏) <¨↕10
!"𝔽⌾(a⊸⊏): Incompatible result elements" % 3‿4‿5⌾(1‿1‿2⊸⊏) a←<¨↕10 ⋄ •internal.Keep a
!"𝔽⌾(a⊸⊏): Incompatible result elements" % 3‿4⌾(("Ah"•internal.Variation 1‿1)⊸⊏) <¨↕10
+!"𝔽⌾(a⊸⊏): Incompatible result elements" % %USE evar ⋄ (27≠↕30) {𝕨⌾((30⥊↕10)⊸⊏) 𝕩} _evar 100⥊1
+!"𝔽⌾(a⊸⊏): Incompatible result elements" % %USE evar ⋄ !∘≡¨⟜⊏⊸⊢⟜⊑ {{¬⌾(31‿4⊸⊑)𝕩}∘•internal.Keep⌾((∾10⥊<↕≠𝕩)⊸⊏) 𝕩} _evar 10‿5⥊1‿1‿0‿0‿1‿1
+%USE eqvar ⋄ {¬⌾((∾10⥊<↕≠𝕩)⊸⊏) 𝕩} _eqvar 10‿5⥊1‿1‿0‿0‿1‿1 %% 10‿5⥊0‿0‿1‿1‿0‿0
20‿20⌾(1‿1⊸⊏) ↕10 %% 0‿20∾2+↕8
100⊸+⌾(1‿2‿¯1⊸⊏) ↕5 %% 0‿101‿102‿3‿104
100⊸+⌾(1‿2‿¯1⊸⊏) <¨↕5 %% <¨ 0‿101‿102‿3‿104
1⊸+⌾(((100⥊↕10)∾10/↕10)⊸⊏) ↕10 %% 1+↕10
+%USE eqvar ⋄ 1⊸+⌾((2/↕5)⊸⊏) _eqvar ↕10 %% <⟜5⊸+ ↕10
+%USE eqvar ⋄ 1⊸+⌾((2/↕5)⊸⊏) _eqvar ≍˘ ↕10 %% ≍˘ <⟜5⊸+ ↕10
+%USE eqvar ⋄ •internal.Keep⌾((2/↕5)⊸⊏) _eqvar 10‿0‿4⥊0 %% 10‿0‿4⥊0
# ⥊