From 124d0945914cc04e59e9063dce0bf56be43581cc Mon Sep 17 00:00:00 2001 From: dzaima Date: Fri, 23 May 2025 22:51:28 +0300 Subject: [PATCH] =?UTF-8?q?fast=20c=C2=A8=E2=8C=BE(is=E2=8A=B8=E2=8A=8F)li?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/select.c | 136 +++++++++++++++++++++++++++++++++++++++++- src/builtins/slash.c | 3 +- test/cases/under.bqn | 10 ++++ 3 files changed, 145 insertions(+), 4 deletions(-) diff --git a/src/builtins/select.c b/src/builtins/select.c index 24604316..28737a28 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -70,6 +70,7 @@ #include "../utils/talloc.h" #include "../utils/mut.h" #include "../utils/calls.h" +#include "../builtins.h" #if SINGELI #define SINGELI_FILE select @@ -124,6 +125,53 @@ FORCE_INLINE void cf_call(CFRes f, void* r, ux rs, void* x, ux xs) { f.fn(r, rs, x, xs, f.data); } +extern INIT_GLOBAL u8 reuseElType[t_COUNT]; +typedef struct { B obj; void* data; } DirectArr; +static NOINLINE DirectArr toFillArr(B x, B fill) { + usz ia = IA(x); + Arr* r = arr_shCopy(m_fillarrp(ia), x); + fillarr_setFill(r, fill); + B* rp = fillarrv_ptr(r); + COPY_TO(rp, el_B, 0, x, 0, ia); + NOGC_E; + decG(x); + return (DirectArr){taga(r), rp}; +} +static void* reusableArr_ptr(Arr* t, u8 el) { + return el==el_B? (void*)harrv_ptr(t) : tyarrv_ptr((TyArr*)t); +} +static DirectArr toEltypeArr(B x, u8 re) { // consumes x; returns an array with eltype==re, with same shape/elements/fill as x, and its data pointer + assert(isArr(x)); + if (reusable(x) && re==reuseElType[TY(x)]) { + x = REUSE(x); + return (DirectArr){x, reusableArr_ptr(a(x), re)}; + } + + Arr* tyarr; + switch (re) { default: UD; + case el_bit: tyarr = cpyBitArr(x); goto tyarr; + case el_i8: tyarr = cpyI8Arr(x); goto tyarr; + case el_i16: tyarr = cpyI16Arr(x); goto tyarr; + case el_i32: tyarr = cpyI32Arr(x); goto tyarr; + case el_f64: tyarr = cpyF64Arr(x); goto tyarr; + case el_c8: tyarr = cpyC8Arr(x); goto tyarr; + case el_c16: tyarr = cpyC16Arr(x); goto tyarr; + case el_c32: tyarr = cpyC32Arr(x); goto tyarr; + case el_B:; + B fill = getFillR(x); + if (noFill(fill)) { + Arr* r = cpyHArr(x); + return (DirectArr){taga(r), harrv_ptr(r)}; + } + return toFillArr(x, fill); + } + + tyarr: + return (DirectArr){taga(tyarr), tyarrv_ptr((TyArr*) tyarr)}; +} + + + extern GLOBAL B rt_select; B select_c1(B t, B x) { if (isAtm(x)) thrM("⊏𝕩: 𝕩 cannot be an atom"); @@ -441,7 +489,6 @@ 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 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 @@ -971,6 +1018,53 @@ B select_rows_B(B x, ux csz, ux cam, B inds) { // consumes inds,x; ⥊ inds⊸ return select_cells_base(inds, x, csz, cam); } + + +SHOULD_INLINE i64 i64get_i32(void* xp, ux i, bool* bad) { + return ((i32*)xp)[i]; +} +SHOULD_INLINE i64 i64get_f64(void* xp, ux i, bool* bad) { + f64 f = ((f64*)xp)[i]; + if (q_fi64(f)) return (i64)f; + *bad = true; + return 0; +} + +SHOULD_INLINE bool select_each_impl(DirectArr r, u8 re, B c, ux xn, void* wp, usz wia, i64 (*getW)(void*, ux, bool*)) { + #define GETW ({ bool bad=false; i64 wc = getW(wp, i, &bad); if (bad) goto bad; WRAP(wc, xn, goto bad); }) + u64 uval; + switch (re) { default: UD; + case el_bit:; + bool cb = o2bG(c); + if (cb) for (ux i = 0; i < wia; i++) bitp_set(r.data, GETW, true); + else for (ux i = 0; i < wia; i++) bitp_set(r.data, GETW, false); + return true; + case el_i8: uval = o2iG(c); goto do_u8; + case el_i16: uval = o2iG(c); goto do_u16; + case el_i32: uval = o2iG(c); goto do_u32; + case el_f64: uval = r_f64u(o2fG(c)); goto do_u64; + case el_c8: uval = o2cG(c); goto do_u8; + case el_c16: uval = o2cG(c); goto do_u16; + case el_c32: uval = o2cG(c); goto do_u32; + case el_B: + for (ux i = 0; i < wia; i++) { + B* p = (B*)r.data + GETW; + dec(*p); + *p = c; + } + return true; + } + UD; + + do_u8: for (ux i = 0; i < wia; i++) ((u8 *)r.data)[GETW] = uval; return true; + do_u16: for (ux i = 0; i < wia; i++) ((u16*)r.data)[GETW] = uval; return true; + do_u32: for (ux i = 0; i < wia; i++) ((u32*)r.data)[GETW] = uval; return true; + do_u64: for (ux i = 0; i < wia; i++) ((u64*)r.data)[GETW] = uval; return true; + #undef GETW + + bad: return false; +} + B select_ucw(B t, B o, B w, B x) { if (RARE(isAtm(x))) { def: return def_fn_ucw(t, o, w, x); } u8 we; @@ -981,7 +1075,7 @@ B select_ucw(B t, B o, B w, B x) { assert(elNum(we)); } else { we = TI(w,elType); - if (!elInt(we) && IA(w)!=0) { + if (!elInt(we)) { w = squeeze_numTry(w, &we); if (!elNum(we)) goto def; } @@ -993,11 +1087,49 @@ B select_ucw(B t, B o, B w, B x) { usz xn = *SH(x); i64 buf[2]; if (wia!=0 && (!getRange_fns[we](tyany_ptr(w), buf, wia) || buf[0]<-(i64)xn || buf[1]>=xn)) { + bad: C2(select, w, x); fatal("select_ucw expected to error"); } rep = incG(o); + } else if (isFun(o) && TY(o)==t_md1D && RNK(x)==1) { + Md1D* od = c(Md1D,o); + if (PRTID(od->m1) != n_each) goto notConstEach; + B c; + if (!toConstant(od->f, &c)) goto notConstEach; + + u8 ce = selfElType(c); + u8 xe = TI(x,elType); + u8 re = el_or(ce,xe); + + DirectArr r = toEltypeArr(x, re); + if (isVal(c)) { + if (wia==0) decG(c); // TODO could return x; fills? + else incByG(c, wia-1); + } + + usz xn = *SH(r.obj); + bool ok; + if (elInt(we)) { + w = toI32Any(w); we = el_i32; + i32* wp = i32any_ptr(w); + ok = select_each_impl(r, re, c, xn, wp, wia, i64get_i32); + } else { + // annoying amount of code for el_f64 𝕨 vs ≤el_i32, but as el_f64 only should apply to ≥2⋆31-element arrays it shouldn't matter much + assert(we==el_f64); + f64* wp = f64any_ptr(w); + ok = select_each_impl(r, re, c, xn, wp, wia, i64get_f64); + } + + if (ok) { + decG(w); + return r.obj; + } else { + x = r.obj; + goto bad; + } } else { + notConstEach:; rep = c1(o, C2(select, incG(w), incG(x))); } diff --git a/src/builtins/slash.c b/src/builtins/slash.c index b8b0a394..7a9be56a 100644 --- a/src/builtins/slash.c +++ b/src/builtins/slash.c @@ -1093,9 +1093,8 @@ B slash_ucw(B t, B o, B w, B x) { Md1D* od = c(Md1D,o); if (PRTID(od->m1) != n_each) goto notConstEach; - B f = od->f; B c; - if (!toConstant(f, &c)) goto notConstEach; + if (!toConstant(od->f, &c)) goto notConstEach; if (we != el_bit) { // relies on compatible(c,c) always being true diff --git a/test/cases/under.bqn b/test/cases/under.bqn index 9c461ede..cbb390fe 100644 --- a/test/cases/under.bqn +++ b/test/cases/under.bqn @@ -74,6 +74,7 @@ n←500 ⋄ a←↕n ⋄ i←(-n)+↕2×n ⋄ r←⌽(2×n)⥊a ⋄ ! (⌽a) ≡ !"𝕨⊏𝕩: Indexing out-of-bounds (10∊𝕨, 10≡≠𝕩)" % %USE evar ⋄ "ab" {𝕨⌾(9‿10⊸⊏)𝕩}_evar 10⥊"foo" !"𝕨⊏𝕩: Indexing out-of-bounds (4.611686018427388e18∊𝕨, 3≡≠𝕩)" % %USE eqerr ⋄ {𝕩⌾((3⥊2⋆62)⊸⊏) "abc"}_eqerr ⟨"abc", {𝕊: "abc"}⟩ !"𝕨⊏𝕩: Indexing out-of-bounds (2∊𝕨, 2≡≠𝕩)" % %USE evar ⋄ (3‿50⥊1‿0‿1) {𝕨⌾(0‿2⊸⊏) 𝕩}_evar 2‿50⥊↕100 +!"𝕨⊏𝕩: Indexing out-of-bounds (2∊𝕨, 2≡≠𝕩)" % %USE evar ⋄ 0‿2 {1¨⌾(𝕨⊸⊏) 𝕩}_evar 2‿50⥊0 %USE eqvar ⋄ (2‿0⥊0) {𝕨⌾(0‿1⊸⊏) 𝕩}_eqvar 10‿0⥊2 %% 10‿0⥊0 %USE eqerr ⋄ ! ⊑ (<⋈⁼ ⍷{1⊣𝕩⌾((3⥊2⋆63)⊸⊏) "abc"}⎊{𝕊: •CurrentError@}¨ ⟨"abc", {𝕊: "abc"}⟩) ∊ "Integer out of range: 9.223372036854776e18"‿"𝕨⊏𝕩: Indexing out-of-bounds (9.223372036854776e18∊𝕨, 3≡≠𝕩)" !"Expected integer, got 1.1" % %USE eqerr ⋄ {𝕩⌾((3⥊1.1)⊸⊏) ↕3}_eqerr ⟨↕3, {1+𝕩}⟩ @@ -117,8 +118,17 @@ n←500 ⋄ a←↕n ⋄ i←(-n)+↕2×n ⋄ r←⌽(2×n)⥊a ⋄ ! (⌽a) ≡ %USE eqvar ⋄ %USE nz ⋄ 0‿0 {(0 InsNZ 2⥊0)⌾(𝕨⊸⊏)𝕩}_eqvar 3⥊3 %% 0‿3‿3 %USE eqvar ⋄ "CD" {𝕨¨⌾(2‿3‿2‿3‿4⊸⊏) 𝕩}_eqvar 10⥊<"ab" %% "ab"‿"ab"‿"CD"‿"CD"‿"CD"‿"ab"‿"ab"‿"ab"‿"ab"‿"ab" %USE eqvar ⋄ "CD" {𝕨¨⌾(2‿3‿2‿1⊸⊏) 𝕩}_eqvar 5‿2⥊<"ab" %% ≍˜˘ "ab"‿"CD"‿"CD"‿"CD"‿"ab" +%USE eqvar ⋄ 2‿3‿2‿1 {!˙¨⌾(𝕨⊸⊏) 𝕩}_eqvar 5‿2⥊<"ab" %% ≍˜˘ "ab"‿!‿!‿!‿"ab" %USE eqvar ⋄ 2‿3‿2‿1 {( 32⊸+⊸≍˘ "AB"‿"CD"‿"AB"‿"EF")⌾(𝕨⊸⊏) 𝕩}_eqvar 5‿2⥊<"?" %% 5‿2⥊⟨"?","?","ef","EF","ab","AB","cd","CD","?","?"⟩ %USE eqvar ⋄ 2‿3‿2‿1 {{𝕊: 32⊸+⊸≍˘ "AB"‿"CD"‿"AB"‿"EF"}⌾(𝕨⊸⊏) 𝕩}_eqvar 5‿2⥊<"?" %% 5‿2⥊⟨"?","?","ef","EF","ab","AB","cd","CD","?","?"⟩ +%USE eqvar ⋄ 2‿3‿2‿1 {(0‿1⊸+˘ 10‿20‿10‿30)⌾(𝕨⊸⊏) 𝕩}_eqvar 5‿2⥊↕10 %% 5‿2⥊0‿1‿30‿31‿10‿11‿20‿21‿8‿9 +%USE eqvar ⋄ 2‿3‿2‿1 {0¨⌾(𝕨⊸⊏) 𝕩}_eqvar 5‿2⥊1 %% 5‿2⥊1‿1‿0‿0‿0‿0‿0‿0‿1‿1 +%USE eqvar ⋄ 2‿3‿2‿1 {0¨⌾(𝕨⊸⊏) 𝕩}_eqvar 5‿1⥊1 %% ≍˘1‿0‿0‿0‿1 +%USE eqvar ⋄ 2‿3‿2‿1 {0¨⌾(𝕨⊸⊏) 𝕩}_eqvar 2≠↕10 %% 1‿0‿0‿0‿1‿1‿1‿1‿1‿1 +%USE eqvar ⋄ 2‿3‿2‿1 {!˙¨⌾(𝕨⊸⊏) 𝕩}_eqvar 2≠↕10 %% 1‿!‿!‿!‿1‿1‿1‿1‿1‿1 +%USE eqvar ⋄ [1‿2,3‿1,1‿8] {99¨⌾(𝕨⊸⊏) 𝕩}_eqvar ↕10 %% 0‿99‿99‿99‿4‿5‿6‿7‿99‿9 +%USE eqvar ⋄ 2‿3‿2‿1 {'?'¨⌾(𝕨⊸⊏) 𝕩}_eqvar "abcdefgh" %% "a???efgh" +%USE eqvar ⋄ 2‿3‿2‿1 {'𝕨'¨⌾(𝕨⊸⊏) 𝕩}_eqvar "abcdefgh" %% "a𝕨𝕨𝕨efgh" # ⥊