From 7f28308e44a4858b6ebe9d4c9944bb65c12cd71e Mon Sep 17 00:00:00 2001 From: Andrea Piseri Date: Sat, 18 May 2024 12:12:17 +0200 Subject: [PATCH 01/10] =?UTF-8?q?native=20path=20in=20`select=5Fucw`=20for?= =?UTF-8?q?=20high=20rank=20`=F0=9D=95=A9`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `𝔽⌾(a⊸⊏)𝕩` now does not need to go through the self-hosted runtime if `1<=𝕩`. Instead the `select_replace` helper function is parametrized over the length of `𝕩` (`xl`) and item amount of the cell of `𝕩` (`xcia`). - The `EQ` macro is modified to not immediately mark the cell as populated, so that multiple replacements can be done on the cell on the first assignment to it. - The `DONE_CW` macro is invoked to mark the current cell as populated when every element in it has been assigned. - A loop over the cell contents is introduced to copy the elements in `𝕩`. This should be fine as it is an easily predictable jump, but a performance regression is possible and a separate code path could be introduced in the future. The change introduces more extensive checking on the shape of `𝔽`'s result, as for high rank `𝕩` the requirement should be `(≢𝔽a⊏𝕩)≡(≢a)∾1↓≢𝕩`. The old behaviour of `select_replace` is recovered by passing `xl=xia` and `xcia=1` in the implementation of `pick_ucw`. --- src/builtins/select.c | 86 ++++++++++++++++++++++++++----------------- src/builtins/sfns.c | 4 +- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index 3665dbaa..70f0400d 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -372,24 +372,26 @@ 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 xl, usz xcia) { // rep⌾(w⊏⥊) x, assumes w is a typed (elNum) list of valid indices, only el_f64 if strictly necessary #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, xl); + bool sparse = wia < xl/64; + if (!sparse) for (i64 i = 0; i < xl; 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)xl; set[cw] = false; \ } - #define EQ(F) if (set[cw] && (F)) thrF("𝔽⌾(a⊸%c): Incompatible result elements", chr); set[cw] = true; + #define EQ(F) if (set[cw] && (F)) thrF("𝔽⌾(a⊸%c): Incompatible result elements", chr); + #define DONE_CW set[cw] = true; #define FREE_CHECK TFREE(set) #else #define SPARSE_INIT(GET) #define EQ(F) + #define DONE_CW #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)xl u8 we = TI(w,elType); assert(elNum(we)); u8 xe = TI(x,elType); u8 re = el_or(xe, TI(rep,elType)); @@ -399,16 +401,19 @@ 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, xl * xcia); 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); + for (usz j = 0; j < xcia; j++) { + B cn = Get(rep, i * xcia + j); + EQ(!equal(mut_getU(r, cw * xcia + j), cn)); + mut_rm(r, cw * xcia + j); + mut_setG(r, cw * xcia + j, cn); + } + DONE_CW; } ra = mut_fp(r); goto dec_ret_ra; @@ -419,7 +424,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 && wia100 && wiaa; 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); + for (usz j = 0; j < xcia; j++) { + bool cn = bitp_get(np, i * xcia + j); + EQ(cn != bitp_get(rp, cw * xcia + j)); + bitp_set(rp, cw * xcia + j, cn); + } + DONE_CW; } goto dec_ret_ra; } @@ -446,24 +454,30 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊ 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; + for (usz j = 0; j < xcia; j++) { + B cn = Get(rep, i * xcia + j); + EQ(!equal(cn,rp[cw * xcia + j])); + dec(rp[cw * xcia + j]); + rp[cw * xcia + j] = cn; + } + DONE_CW; } goto dec_ret_ra; } } - #define IMPL(T) do { \ - 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]); \ - rp[cw] = cn; \ - } \ + #define IMPL(T) do { \ + T* rp = (void*)((TyArr*)ra)->a; \ + T* np = tyany_ptr(rep); \ + for (usz i = 0; i < wia; i++) { \ + READ_W(cw, i); \ + for (usz j = 0; j < xcia; j++) { \ + T cn = np[i * xcia + j]; \ + EQ(cn != rp[cw * xcia + j]); \ + rp[cw * xcia + j] = cn; \ + } \ + DONE_CW; \ + } \ goto dec_ret_ra; \ } while(0) @@ -482,11 +496,12 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xia) { // rep⌾(w⊏⥊ #undef SPARSE_INIT #undef EQ + #undef DONE_CW #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); @@ -502,6 +517,11 @@ B select_ucw(B t, B o, B w, B x) { } 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 ia = shProd(SH(x), 1, RNK(x)); + return select_replace(U'⊏', w, x, rep, wia, SH(x)[0], ia); } 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); } From 06808414da9a99aab0222dd93863786aeae9f322 Mon Sep 17 00:00:00 2001 From: Andrea Piseri Date: Sat, 18 May 2024 21:45:18 +0200 Subject: [PATCH 02/10] Separate code path for `cxsz=1`, fix out of bounds read for `RNK(x)==0` --- src/builtins/select.c | 123 ++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 40 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index 70f0400d..96d4e588 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -372,7 +372,7 @@ 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 xl, usz xcia) { // 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 xl, usz xcsz) { // rep⌾(w⊏⥊) x, assumes w is a typed (elNum) list of valid indices, only el_f64 if strictly necessary #if CHECK_VALID TALLOC(bool, set, xl); bool sparse = wia < xl/64; @@ -401,19 +401,30 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcia) { // rep f64* wp = f64any_ptr(w); SPARSE_INIT((i64)wp[i]) - MAKE_MUT(r, xl * xcia); + MAKE_MUT(r, xl * xcsz); mut_init_copy(r, x, re); NOGC_E; MUTG_INIT(r); SGet(rep) - for (usz i = 0; i < wia; i++) { - READ_W(cw, i); - for (usz j = 0; j < xcia; j++) { - B cn = Get(rep, i * xcia + j); - EQ(!equal(mut_getU(r, cw * xcia + j), cn)); - mut_rm(r, cw * xcia + j); - mut_setG(r, cw * xcia + j, cn); + if (xcsz==1) { + 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); + DONE_CW; + } + } else { + for (usz i = 0; i < wia; i++) { + READ_W(cw, i); + for (usz j = 0; j < xcsz; j++) { + B cn = Get(rep, i * xcsz + j); + EQ(!equal(mut_getU(r, cw * xcsz + j), cn)); + mut_rm(r, cw * xcsz + j); + mut_setG(r, cw * xcsz + j, cn); + } + DONE_CW; } - DONE_CW; } ra = mut_fp(r); goto dec_ret_ra; @@ -437,14 +448,24 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcia) { // rep 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); - for (usz j = 0; j < xcia; j++) { - bool cn = bitp_get(np, i * xcia + j); - EQ(cn != bitp_get(rp, cw * xcia + j)); - bitp_set(rp, cw * xcia + j, cn); + if (xcsz==1) { + 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); + DONE_CW; + } + } else { + for (usz i = 0; i < wia; i++) { + READ_W(cw, i); + for (usz j = 0; j < xcsz; j++) { + bool cn = bitp_get(np, i * xcsz + j); + EQ(cn != bitp_get(rp, cw * xcsz + j)); + bitp_set(rp, cw * xcsz + j, cn); + } + DONE_CW; } - DONE_CW; } goto dec_ret_ra; } @@ -452,32 +473,54 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcia) { // rep 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); - for (usz j = 0; j < xcia; j++) { - B cn = Get(rep, i * xcia + j); - EQ(!equal(cn,rp[cw * xcia + j])); - dec(rp[cw * xcia + j]); - rp[cw * xcia + j] = cn; + if (xcsz==1) + { + 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; + DONE_CW; + } + } else { + for (usz i = 0; i < wia; i++) { + READ_W(cw, i); + for (usz j = 0; j < xcsz; j++) { + B cn = Get(rep, i * xcsz + j); + EQ(!equal(cn,rp[cw * xcsz + j])); + dec(rp[cw * xcsz + j]); + rp[cw * xcsz + j] = cn; + } + DONE_CW; } - DONE_CW; } goto dec_ret_ra; } } - #define IMPL(T) do { \ - T* rp = (void*)((TyArr*)ra)->a; \ - T* np = tyany_ptr(rep); \ - for (usz i = 0; i < wia; i++) { \ - READ_W(cw, i); \ - for (usz j = 0; j < xcia; j++) { \ - T cn = np[i * xcia + j]; \ - EQ(cn != rp[cw * xcia + j]); \ - rp[cw * xcia + j] = cn; \ - } \ - DONE_CW; \ - } \ + #define IMPL(T) do { \ + T* rp = (void*)((TyArr*)ra)->a; \ + T* np = tyany_ptr(rep); \ + if (xcsz==1) { \ + for (usz i = 0; i < wia; i++) { \ + READ_W(cw, i); \ + T cn = np[i]; \ + EQ(cn != rp[cw]); \ + rp[cw] = cn; \ + DONE_CW; \ + } \ + } else { \ + for (usz i = 0; i < wia; i++) { \ + READ_W(cw, i); \ + for (usz j = 0; j < xcsz; j++) { \ + T cn = np[i * xcsz + j]; \ + EQ(cn != rp[cw * xcsz + j]); \ + rp[cw * xcsz + j] = cn; \ + } \ + DONE_CW; \ + } \ + } \ goto dec_ret_ra; \ } while(0) @@ -501,7 +544,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcia) { // rep } B select_ucw(B t, B o, B w, B x) { - if (isAtm(x) || isAtm(w)) { def: return def_fn_ucw(t, o, w, x); } + if (isAtm(x) || RNK(x)==0 || isAtm(w)) { def: return def_fn_ucw(t, o, w, x); } usz xia = IA(x); usz wia = IA(w); u8 we = TI(w,elType); @@ -522,6 +565,6 @@ B select_ucw(B t, B o, B w, B x) { 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 ia = shProd(SH(x), 1, RNK(x)); - return select_replace(U'⊏', w, x, rep, wia, SH(x)[0], ia); + usz xcsz = arr_csz(x); + return select_replace(U'⊏', w, x, rep, wia, SH(x)[0], xcsz); } From 93188a9f90d04fe636b2af919dd3a28e4cce4d01 Mon Sep 17 00:00:00 2001 From: Andrea Piseri Date: Sat, 18 May 2024 23:56:13 +0200 Subject: [PATCH 03/10] Use `COPY_TO` and `EQFN` in the typed array code path --- src/builtins/select.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index 96d4e588..2b5c8078 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -499,7 +499,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep } } - #define IMPL(T) do { \ + #define IMPL(T) do { \ T* rp = (void*)((TyArr*)ra)->a; \ T* np = tyany_ptr(rep); \ if (xcsz==1) { \ @@ -511,14 +511,12 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep DONE_CW; \ } \ } else { \ + EqFnObj eq = EQFN_GET(re,re); \ for (usz i = 0; i < wia; i++) { \ READ_W(cw, i); \ - for (usz j = 0; j < xcsz; j++) { \ - T cn = np[i * xcsz + j]; \ - EQ(cn != rp[cw * xcsz + j]); \ - rp[cw * xcsz + j] = cn; \ - } \ - DONE_CW; \ + EQ(!EQFN_CALL(eq,rp+cw*xcsz,np+i*xcsz,xcsz)); \ + COPY_TO(rp,re,cw*xcsz,rep,i*xcsz,xcsz); \ + DONE_CW; \ } \ } \ goto dec_ret_ra; \ From 3738d0d65d4624fbd53765359aaa0619cb1382e2 Mon Sep 17 00:00:00 2001 From: Andrea Piseri Date: Sat, 18 May 2024 23:58:11 +0200 Subject: [PATCH 04/10] Formatting --- src/builtins/select.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index 2b5c8078..d464feab 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -412,7 +412,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep EQ(!equal(mut_getU(r, cw), cn)); mut_rm(r, cw); mut_setG(r, cw, cn); - DONE_CW; + DONE_CW; } } else { for (usz i = 0; i < wia; i++) { @@ -500,25 +500,25 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep } #define IMPL(T) do { \ - T* rp = (void*)((TyArr*)ra)->a; \ - T* np = tyany_ptr(rep); \ - if (xcsz==1) { \ - for (usz i = 0; i < wia; i++) { \ - READ_W(cw, i); \ - T cn = np[i]; \ - EQ(cn != rp[cw]); \ - rp[cw] = cn; \ - DONE_CW; \ - } \ - } else { \ - EqFnObj eq = EQFN_GET(re,re); \ - for (usz i = 0; i < wia; i++) { \ - READ_W(cw, i); \ - EQ(!EQFN_CALL(eq,rp+cw*xcsz,np+i*xcsz,xcsz)); \ - COPY_TO(rp,re,cw*xcsz,rep,i*xcsz,xcsz); \ - DONE_CW; \ - } \ - } \ + T* rp = (void*)((TyArr*)ra)->a; \ + T* np = tyany_ptr(rep); \ + if (xcsz==1) { \ + for (usz i = 0; i < wia; i++) { \ + READ_W(cw, i); \ + T cn = np[i]; \ + EQ(cn != rp[cw]); \ + rp[cw] = cn; \ + DONE_CW; \ + } \ + } else { \ + EqFnObj eq = EQFN_GET(re,re); \ + for (usz i = 0; i < wia; i++) { \ + READ_W(cw, i); \ + EQ(!EQFN_CALL(eq,rp+cw*xcsz,np+i*xcsz,xcsz)); \ + COPY_TO(rp,re,cw*xcsz,rep,i*xcsz,xcsz); \ + DONE_CW; \ + } \ + } \ goto dec_ret_ra; \ } while(0) From e66443d19f31a4a478c1102d2dd41d41e9a2cab0 Mon Sep 17 00:00:00 2001 From: Andrea Piseri Date: Sun, 19 May 2024 00:07:58 +0200 Subject: [PATCH 05/10] update expected error messages in test/cases/under.bqn --- test/cases/under.bqn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/under.bqn b/test/cases/under.bqn index e3b8fff1..17210ee8 100644 --- a/test/cases/under.bqn +++ b/test/cases/under.bqn @@ -8,8 +8,8 @@ # ⊏ & ⊑ !"𝔽⌾(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 (⟨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 (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 From 99a23a97d8853995db76baa3ee9a85fdd1fd6f64 Mon Sep 17 00:00:00 2001 From: Andrea Piseri Date: Sun, 19 May 2024 00:10:28 +0200 Subject: [PATCH 06/10] Fix typo in the error message --- src/builtins/select.c | 2 +- test/cases/under.bqn | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index d464feab..23c9043b 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -562,7 +562,7 @@ B select_ucw(B t, B o, B w, B 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); + 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 xcsz = arr_csz(x); return select_replace(U'⊏', w, x, rep, wia, SH(x)[0], xcsz); } diff --git a/test/cases/under.bqn b/test/cases/under.bqn index 17210ee8..b8034f9f 100644 --- a/test/cases/under.bqn +++ b/test/cases/under.bqn @@ -8,8 +8,8 @@ # ⊏ & ⊑ !"𝔽⌾(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 (⟨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 (⟨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 (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 From 9f6151d92d6bdb9de396d1f7c9a765902bb5a776 Mon Sep 17 00:00:00 2001 From: dzaima Date: Sun, 19 May 2024 02:05:33 +0300 Subject: [PATCH 07/10] =?UTF-8?q?expand=20=E2=8C=BE(a=E2=8A=B8=E2=8A=8F)?= =?UTF-8?q?=20tests,=20stylistic=20changes/cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/select.c | 35 +++++++++++++++++------------------ test/cases/perf.bqn | 7 ++++++- test/cases/under.bqn | 18 +++++++++++------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index 23c9043b..ac5c290b 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -372,7 +372,7 @@ 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 xl, usz xcsz) { // 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 xl, usz xcsz) { // rep⌾(w⊏xl‿xcsz⥊⊢) x, assumes w is a typed (elNum) list of valid indices, only el_f64 if strictly necessary #if CHECK_VALID TALLOC(bool, set, xl); bool sparse = wia < xl/64; @@ -401,7 +401,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep f64* wp = f64any_ptr(w); SPARSE_INIT((i64)wp[i]) - MAKE_MUT(r, xl * xcsz); + MAKE_MUT(r, xl*xcsz); mut_init_copy(r, x, re); NOGC_E; MUTG_INIT(r); SGet(rep) @@ -418,10 +418,10 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep for (usz i = 0; i < wia; i++) { READ_W(cw, i); for (usz j = 0; j < xcsz; j++) { - B cn = Get(rep, i * xcsz + j); - EQ(!equal(mut_getU(r, cw * xcsz + j), cn)); - mut_rm(r, cw * xcsz + j); - mut_setG(r, cw * xcsz + j, cn); + B cn = Get(rep, i*xcsz + j); + EQ(!equal(mut_getU(r, cw*xcsz + j), cn)); + mut_rm(r, cw*xcsz + j); + mut_setG(r, cw*xcsz + j, cn); } DONE_CW; } @@ -460,9 +460,9 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep for (usz i = 0; i < wia; i++) { READ_W(cw, i); for (usz j = 0; j < xcsz; j++) { - bool cn = bitp_get(np, i * xcsz + j); - EQ(cn != bitp_get(rp, cw * xcsz + j)); - bitp_set(rp, cw * xcsz + j, cn); + bool cn = bitp_get(np, i*xcsz + j); + EQ(cn != bitp_get(rp, cw*xcsz + j)); + bitp_set(rp, cw*xcsz + j, cn); } DONE_CW; } @@ -487,10 +487,10 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep for (usz i = 0; i < wia; i++) { READ_W(cw, i); for (usz j = 0; j < xcsz; j++) { - B cn = Get(rep, i * xcsz + j); - EQ(!equal(cn,rp[cw * xcsz + j])); - dec(rp[cw * xcsz + j]); - rp[cw * xcsz + j] = cn; + B cn = Get(rep, i*xcsz + j); + EQ(!equal(cn,rp[cw*xcsz + j])); + dec(rp[cw*xcsz + j]); + rp[cw*xcsz + j] = cn; } DONE_CW; } @@ -519,7 +519,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep DONE_CW; \ } \ } \ - goto dec_ret_ra; \ + goto dec_ret_ra; \ } while(0) do_u8: IMPL(u8); @@ -542,7 +542,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep } B select_ucw(B t, B o, B w, B x) { - if (isAtm(x) || RNK(x)==0 || 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); @@ -551,7 +551,7 @@ 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); @@ -563,6 +563,5 @@ B select_ucw(B t, B o, B w, B x) { 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 xcsz = arr_csz(x); - return select_replace(U'⊏', w, x, rep, wia, SH(x)[0], xcsz); + return select_replace(U'⊏', w, x, rep, wia, *SH(x), arr_csz(x)); } 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⥊ Date: Sun, 19 May 2024 16:46:56 +0200 Subject: [PATCH 08/10] Clean up collision checking, use vector copy functions --- src/builtins/select.c | 55 +++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index ac5c290b..3e00d1e0 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -381,13 +381,13 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep if (sparse) for (usz i = 0; i < wia; i++) { \ i64 cw = WI; if (RARE(cw<0)) cw+= (i64)xl; set[cw] = false; \ } - #define EQ(F) if (set[cw] && (F)) thrF("𝔽⌾(a⊸%c): Incompatible result elements", chr); - #define DONE_CW 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 DONE_CW + #define EQ(ITER,F) + #define EQ1(F) #define FREE_CHECK #endif @@ -409,21 +409,16 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep for (usz i = 0; i < wia; i++) { READ_W(cw, i); B cn = Get(rep, i); - EQ(!equal(mut_getU(r, cw), cn)); + EQ1(!equal(mut_getU(r, cw), cn)); mut_rm(r, cw); mut_setG(r, cw, cn); - DONE_CW; } } else { for (usz i = 0; i < wia; i++) { READ_W(cw, i); - for (usz j = 0; j < xcsz; j++) { - B cn = Get(rep, i*xcsz + j); - EQ(!equal(mut_getU(r, cw*xcsz + j), cn)); - mut_rm(r, cw*xcsz + j); - mut_setG(r, cw*xcsz + j, cn); - } - DONE_CW; + EQ(for (usz j = 0; j < xcsz; j++),!equal(mut_getU(r, cw*xcsz + j), Get(rep, i*xcsz + j))); + for (usz j = 0; j < xcsz; j++) mut_rm(r, cw*xcsz + j); + mut_copyG(r, cw*xcsz, rep, i*xcsz, xcsz); } } ra = mut_fp(r); @@ -452,19 +447,14 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep for (usz i = 0; i < wia; i++) { READ_W(cw, i); bool cn = bitp_get(np, i); - EQ(cn != bitp_get(rp, cw)); + EQ1(cn != bitp_get(rp, cw)); bitp_set(rp, cw, cn); - DONE_CW; } } else { for (usz i = 0; i < wia; i++) { READ_W(cw, i); - for (usz j = 0; j < xcsz; j++) { - bool cn = bitp_get(np, i*xcsz + j); - EQ(cn != bitp_get(rp, cw*xcsz + j)); - bitp_set(rp, cw*xcsz + j, cn); - } - DONE_CW; + EQ(for (usz j = 0; j < xcsz; j++), bitp_get(np, i*xcsz + j) != bitp_get(rp, cw*xcsz + j)); + COPY_TO(rp, el_bit, cw*xcsz, rep, i*xcsz, xcsz); } } goto dec_ret_ra; @@ -478,21 +468,16 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep for (usz i = 0; i < wia; i++) { READ_W(cw, i); B cn = Get(rep, i); - EQ(!equal(cn,rp[cw])); + EQ1(!equal(cn,rp[cw])); dec(rp[cw]); rp[cw] = cn; - DONE_CW; } } else { for (usz i = 0; i < wia; i++) { READ_W(cw, i); - for (usz j = 0; j < xcsz; j++) { - B cn = Get(rep, i*xcsz + j); - EQ(!equal(cn,rp[cw*xcsz + j])); - dec(rp[cw*xcsz + j]); - rp[cw*xcsz + j] = cn; - } - DONE_CW; + EQ(for (usz j = 0; j < xcsz; j++), !equal(Get(rep, i*xcsz + j), rp[cw*xcsz + j])); + for (usz j = 0; j < xcsz; j++) dec(rp[cw*xcsz + j]); + COPY_TO(rp, el_B, cw*xcsz, rep, i*xcsz, xcsz); } } goto dec_ret_ra; @@ -506,17 +491,15 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // 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; \ - DONE_CW; \ } \ } else { \ EqFnObj eq = EQFN_GET(re,re); \ for (usz i = 0; i < wia; i++) { \ READ_W(cw, i); \ - EQ(!EQFN_CALL(eq,rp+cw*xcsz,np+i*xcsz,xcsz)); \ - COPY_TO(rp,re,cw*xcsz,rep,i*xcsz,xcsz); \ - DONE_CW; \ + EQ1(!EQFN_CALL(eq, rp+cw*xcsz, np+i*xcsz, xcsz)); \ + COPY_TO(rp, re, cw*xcsz, rep, i*xcsz, xcsz); \ } \ } \ goto dec_ret_ra; \ @@ -537,7 +520,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep #undef SPARSE_INIT #undef EQ - #undef DONE_CW + #undef EQ1 #undef FREE_CHECK } From 52fb05fcd47364dede4b408f1b28836c44ba585a Mon Sep 17 00:00:00 2001 From: dzaima Date: Mon, 20 May 2024 01:03:45 +0300 Subject: [PATCH 09/10] =?UTF-8?q?unify=20=E2=8C=BE(a=E2=8A=B8=E2=8A=8F)=20?= =?UTF-8?q?typed=20array=20paths,=20fix=20csz=3D=3D0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/select.c | 86 +++++++++++++++++++++++-------------------- test/cases/under.bqn | 8 +++- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index 3e00d1e0..f8b8e536 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -372,14 +372,15 @@ 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 xl, usz xcsz) { // rep⌾(w⊏xl‿xcsz⥊⊢) 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, xl); - bool sparse = wia < xl/64; - if (!sparse) for (i64 i = 0; i < xl; 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)xl; set[cw] = false; \ + i64 cw = WI; if (RARE(cw<0)) cw+= (i64)cam; set[cw] = false; \ } #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) @@ -391,7 +392,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep #define FREE_CHECK #endif - #define READ_W(N,I) i64 N = (i64)wp[I]; if (RARE(N<0)) N+= (i64)xl + #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)); @@ -401,11 +402,11 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep f64* wp = f64any_ptr(w); SPARSE_INIT((i64)wp[i]) - MAKE_MUT(r, xl*xcsz); + MAKE_MUT(r, cam*csz); mut_init_copy(r, x, re); NOGC_E; MUTG_INIT(r); SGet(rep) - if (xcsz==1) { + if (csz==1) { for (usz i = 0; i < wia; i++) { READ_W(cw, i); B cn = Get(rep, i); @@ -416,9 +417,9 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep } else { for (usz i = 0; i < wia; i++) { READ_W(cw, i); - EQ(for (usz j = 0; j < xcsz; j++),!equal(mut_getU(r, cw*xcsz + j), Get(rep, i*xcsz + j))); - for (usz j = 0; j < xcsz; j++) mut_rm(r, cw*xcsz + j); - mut_copyG(r, cw*xcsz, rep, i*xcsz, xcsz); + 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); @@ -430,7 +431,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep i32* wp = i32any_ptr(w); SPARSE_INIT(wp[i]) bool reuse = reusable(x) && re==reuseElType[TY(x)]; - SLOWIF(!reuse && xl>100 && wia100 && wiaa; - if (xcsz==1) { + if (csz==1) { for (usz i = 0; i < wia; i++) { READ_W(cw, i); bool cn = bitp_get(np, i); @@ -453,8 +454,8 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep } else { for (usz i = 0; i < wia; i++) { READ_W(cw, i); - EQ(for (usz j = 0; j < xcsz; j++), bitp_get(np, i*xcsz + j) != bitp_get(rp, cw*xcsz + j)); - COPY_TO(rp, el_bit, cw*xcsz, rep, i*xcsz, xcsz); + 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; @@ -463,8 +464,7 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep ra = reuse? a(REUSE(x)) : cpyHArr(x); B* rp = harrP_parts((HArr*)ra).a; SGet(rep) - if (xcsz==1) - { + if (csz==1) { for (usz i = 0; i < wia; i++) { READ_W(cw, i); B cn = Get(rep, i); @@ -475,34 +475,26 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep } else { for (usz i = 0; i < wia; i++) { READ_W(cw, i); - EQ(for (usz j = 0; j < xcsz; j++), !equal(Get(rep, i*xcsz + j), rp[cw*xcsz + j])); - for (usz j = 0; j < xcsz; j++) dec(rp[cw*xcsz + j]); - COPY_TO(rp, el_B, cw*xcsz, rep, i*xcsz, xcsz); + 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 { \ - T* rp = (void*)((TyArr*)ra)->a; \ - T* np = tyany_ptr(rep); \ - if (xcsz==1) { \ - for (usz i = 0; i < wia; i++) { \ - READ_W(cw, i); \ - T cn = np[i]; \ - EQ1(cn != rp[cw]); \ - rp[cw] = cn; \ - } \ - } else { \ - EqFnObj eq = EQFN_GET(re,re); \ - for (usz i = 0; i < wia; i++) { \ - READ_W(cw, i); \ - EQ1(!EQFN_CALL(eq, rp+cw*xcsz, np+i*xcsz, xcsz)); \ - COPY_TO(rp, re, cw*xcsz, rep, i*xcsz, xcsz); \ - } \ - } \ - 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]; \ + EQ1(cn != rp[cw]); \ + rp[cw] = cn; \ + } \ + goto dec_ret_ra; \ } while(0) do_u8: IMPL(u8); @@ -511,6 +503,18 @@ B select_replace(u32 chr, B w, B x, B rep, usz wia, usz xl, usz xcsz) { // rep 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:; @@ -546,5 +550,7 @@ B select_ucw(B t, B o, B w, B x) { 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); - return select_replace(U'⊏', w, x, rep, wia, *SH(x), arr_csz(x)); + 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/test/cases/under.bqn b/test/cases/under.bqn index 20837e31..45c8d4c9 100644 --- a/test/cases/under.bqn +++ b/test/cases/under.bqn @@ -1,5 +1,6 @@ %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 @@ -41,7 +42,7 @@ 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 @@ -55,11 +56,14 @@ n←500 ⋄ a←↕n ⋄ i←(-n)+↕2×n ⋄ r←⌽(2×n)⥊a ⋄ ! (⌽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 tvar ⋄ !∘≡¨⟜⊏⊸⊢⟜⊑ {¬⌾((∾10⥊<↕≠𝕩)⊸⊏) 𝕩} _tvar 10‿5⥊1‿1‿0‿0‿1‿1 %% 10‿5⥊0‿0‿1‿1‿0‿0 +%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 # ⥊ From 44c8da7dc76a0fd4b38146a6d974586269d63707 Mon Sep 17 00:00:00 2001 From: dzaima Date: Mon, 20 May 2024 00:46:02 +0300 Subject: [PATCH 10/10] add checks for l>0 on equal() impls some Singeli `fn equal` paths already assumed such, this just makes the requirement properly checked for, benefited from, and documented --- src/builtins/compare.c | 7 ++++--- src/singeli/src/equal.singeli | 1 + src/singeli/src/mask.singeli | 1 + src/utils/calls.h | 3 +-- 4 files changed, 7 insertions(+), 5 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; i0); \ if (d!=0) { void* t=w; w=x; x=t; } \ u64* wp = w; T* xp = x; \ for (usz i=0; i0); INIT \ S* wp = w; T* xp = x; \ for (usz i=0; i0); 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/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