uCBQN/src/builtins/selfsearch.c

548 lines
25 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Self-search: Mark Firsts (∊), Occurrence Count (⊒), Classify (⊐), Deduplicate (⍷)
// Except for trivial cases, ⍷ is implemented as ∊⊸/
// Other functions use adaptations of the same set of methods
// Boolean cases (1-bit cells) all use special code, including ⍷
// COULD vectorize boolean ⊒ with +`
// Sorted flags: start with r0⌾⊑»⊸≠𝕩 (r0←0 for ⊐, 1 otherwise)
// ∊: ⊢; ⊐: +`; ⊒: ↕∘≠⊸(⊣-⌈`∘×)
// COULD determine ⊒ result type by direct comparisons on 𝕩
// Brute force or all-pairs comparison for small lengths
// Branchless, not vectorized (+´∧` structure for ⊐)
// COULD use direct all-pairs filter, not ∊⊸/, for short ⍷
// Full-size table lookups for 1- and 2-byte 𝕩
// 2-byte table can be "sparse" initialized with an extra pass over 𝕩
// 4-byte ⊐ can use a small-range lookup table
// COULD add small-range 4-byte tables for ∊ and ⊒
// Radix-assisted lookups are fallbacks for 4-byte ∊ and ⊒
// COULD do radix-assisted ⊐ as ⍷⊸⊐ or similar
// Specializes on constant top 1/2 bytes, but hashes make this rare
// Cells with 2≤n≤63 bits, ¬n∊8‿16‿32:
// Pad to the nearest nice size (8, 16, 32, 64 bits)
// Specialized 4-byte and 8-byte hash tables
// In-place resizing by factor of 4 based on measured collisions
// Max collisions ensures bounded time spent here before giving up
// First element used as sentinel (not good for ⊒)
// COULD prefetch when table gets larger
// Generic hash table for other cases
// Resizing is pretty expensive here
#include "../core.h"
#include "../utils/hash.h"
#include "../utils/talloc.h"
#include "../utils/calls.h"
#include "../builtins.h"
extern B shape_c1(B, B);
extern B slash_c2(B, B, B);
extern B ud_c1(B, B);
extern B sub_c2(B, B, B);
extern B mul_c2(B, B, B);
extern B scan_add_bool(B x, u64 ia);
extern B scan_max_num(B x, u8 xe, u64 ia);
// These hashes are stored in tables and must be invertible!
#if defined(__SSE4_2__)
#include <x86intrin.h>
static inline u32 hash32(u32 x) { return _mm_crc32_u32(0x973afb51, x); }
#else
// Murmur3
static inline u32 hash32(u32 x) {
x ^= x >> 16; x *= 0x85ebca6b;
x ^= x >> 13; x *= 0xc2b2ae35;
x ^= x >> 16;
return x;
}
#endif
static inline u64 hash64(u64 x) {
x ^= x >> 33; x *= 0xff51afd7ed558ccd;
x ^= x >> 33; x *= 0xc4ceb9fe1a85ec53;
x ^= x >> 33;
return x;
}
static inline bool use_sorted(B x, u8 logw) {
if (!FL_HAS(x, fl_asc|fl_dsc)) return 0;
if (logw==6) return TI(x, elType) == el_f64;
return 3<=logw & logw<=5;
}
static inline B shift_ne(B x, usz n, u8 lw, bool r0) { // consumes x
u64* rp; B r = m_bitarrv(&rp, n);
u8* xp = tyany_ptr(x);
u8 lb = lw - 3;
CMP_AA_IMM(ne, el_i8+lb, rp, xp-(1<<lb), xp, n);
bitp_set(rp, 0, r0);
decG(x); return r;
}
static bool canCompare64_norm(B x, usz n) {
u8 e = TI(x,elType);
if (e == el_B) return 0;
if (e == el_f64) {
f64* pf = f64any_ptr(x);
u64* pu = (u64*)pf;
for (usz i = 0; i < n; i++) {
f64 f = pf[i];
if (f!=f) return 0;
if (pu[i] == m_f64(-0.0).u) return 0;
}
}
return 1;
}
static bool shouldWidenBitarr(B x, usz csz) { // assumes cells won't anymore have sizes of 0, 8, or 16 bits
u8 xe = TI(x,elType);
ux bcsz = ((ux)csz)<<elWidthLogBits(xe);
assert(csz!=0 && bcsz!=1 && bcsz!=8 && bcsz!=16);
if (bcsz<64 && bcsz!=32) { assert(xe!=el_B); return true; } // not el_B because csz>0 → csz*sizeof(B) >= 64
return false;
}
#define GRADE_UD(U,D) U
#include "radix.h"
u8 radix_offsets_2_u32(usz* c0, u32* v0, usz n) {
usz rx = 256;
usz* c1 = c0 + rx;
// Count keys
for (usz j=0; j<2*rx+1; j++) c0[j] = 0;
for (usz i=0; i<n; i++) { u32 v=v0[i]; (c0+1)[(u8)(v>>16)]++; (c1+1)[(u8)(v>>24)]++; }
u32 v=v0[0];
// Inclusive prefix sum; note c offsets above
if ((c1+1)[(u8)(v>>24)] < n) { c1[0]-=n; RADIX_SUM_2_u32; return 2; }
if ((c0+1)[(u8)(v>>16)] < n) { RADIX_SUM_1_u32; return 1; }
return 0;
}
#undef GRADE_UD
#define RADIX_LOOKUP_32(INIT, SETTAB) \
u8 bytes = radix_offsets_2_u32(c0, v0, n); \
usz tim = tn/(64/sizeof(*tab)); /* sparse table init max */ \
if (bytes==0) { \
if (n<tim) for (usz i=0; i< n; i++) tab[(u16)v0[i]]=INIT; \
else for (usz j=0; j<tn; j++) tab[j]=INIT; \
for (usz i=0; i<n; i++) { u32 j=(u16)v0[i]; r0[i]=tab[j]; tab[j]SETTAB; } \
} else { \
if (bytes==1) { v1=v2; r1=r2; } \
for (usz i=0; i<n; i++) { u32 v=v0[i]; u8 k=k0[i]=(u8)(v>>16); usz c=c0[k]++; v1[c]=v; } \
if (bytes==1) { \
/* Table lookup, getting radix boundaries from c0 */ \
for (usz i=0; i<n; ) { \
usz l=c0[(u8)(v1[i]>>16)]; \
if (l-i < tim) for (usz ii=i; ii<l; ii++) tab[(u16)v1[ii]]=INIT; \
else for (usz j=0; j<tn; j++) tab[j]=INIT; \
for (; i<l; i++) { u32 j=(u16)v1[i]; r1[i]=tab[j]; tab[j]SETTAB; } \
} \
} else { \
/* Radix move */ \
for (usz i=0; i<n; i++) { u32 v=v1[i]; u8 k=k1[i]=(u8)(v>>24); usz c=c1[k]++; v2[c]=v; } \
/* Table lookup */ \
u32 tv=v2[0]>>16; v2[n]=~v2[n-1]; \
for (usz l=0, i=0; l<n; ) { \
for (; ; l++) { u32 v=v2[l], t0=tv; tv=v>>16; if (tv!=t0) break; tab[(u16)v]=INIT; } \
for (; i<l; i++) { u32 j=(u16)v2[i]; r2[i]=tab[j]; tab[j]SETTAB; } \
} \
/* Radix unmove; back up c0 to account for increments in radix step */ \
*--c1=0; for (usz i=0; i<n; i++) { r1[i]=r2[c1[k1[i]]++]; } \
} \
*--c0=0; for (usz i=0; i<n; i++) { r0[i]=r1[c0[k0[i]]++]; } \
} \
decG(x); TFREE(alloc);
static NOINLINE void memset32(u32* p, u32 v, usz l) { for (usz i=0; i<l; i++) p[i]=v; }
static NOINLINE void memset64(u64* p, u64 v, usz l) { for (usz i=0; i<l; i++) p[i]=v; }
// Resizing hash table, with fallback
#define SELFHASHTAB(T, W, RAD, STOP, RES0, RESULT, RESWRITE, THRESHMUL, THRESH, AUXSIZE, AUXINIT, AUXEXTEND, AUXMOVE) \
usz log = 64 - CLZ(n); \
usz msl = (64 - CLZ(n+n/2)) + 1; if (RAD && msl>20) msl=20; \
usz sh = W - (msl<14? msl : 12+(msl&1)); /* Shift to fit to table */ \
usz sz = 1 << (W - sh); /* Initial size */ \
usz msz = 1ull << msl; /* Max sz */ \
usz b = 64; /* Block size */ \
/* Resize or abort if more than 1/2^thresh collisions/element */ \
usz thresh = THRESH; \
/* Filling e slots past the end requires e*(e+1)/2 collisions, so */ \
/* n entries with <2 each can fill <sqrt(4*n) */ \
usz ext = n<=b? n : b + (1ull << (log/2 + 1)); \
TALLOC(u8, halloc, (msz+ext)*(sizeof(T)+AUXSIZE)); \
T* hash = (T*)halloc + msz-sz; \
T x0 = hash##W(xp[0]); rp[0] = RES0; \
memset##W(hash, x0, sz+ext); \
AUXINIT \
usz cc = 0; /* Collision counter */ \
usz i=1; while (1) { \
usz e = n-i>b? i+b : n; \
for (; i < e; i++) { \
T h = hash##W(xp[i]); T j0 = h>>sh, j = j0; T k; \
while (k=hash[j], k!=h & k!=x0) j++; \
cc += j-j0; \
RESWRITE \
} \
if (i == n) break; \
i64 dc = (i64)cc - ((THRESHMUL*i)>>thresh); \
if (dc >= 0) { \
if (sz == msz || (RAD && i < n/2 && sz >= 1<<18)) break; /*Abort*/ \
/* Avoid resizing if close to the end */ \
if (cc<STOP && (n-i)*dc < (i*((i64)n+i))>>(5+log-(W-sh))) continue;\
/* Resize hash, factor of 4 */ \
usz m = 2; \
usz dif = sz*((1<<m)-1); \
sh -= m; sz <<= m; \
hash -= dif; \
usz j = 0; \
cc = 0; \
memset##W(hash, x0, dif); \
AUXEXTEND \
for (j = dif; j < sz + ext; j++) { \
T h = hash[j]; if (h==x0) continue; hash[j] = x0; \
T k0 = h>>sh, k = k0; while (hash[k]!=x0) k++; \
cc += k-k0; \
hash[k] = h; AUXMOVE \
} \
if (cc >= STOP) break; \
thresh = THRESH; \
} \
} \
TFREE(halloc); \
if (i==n) { decG(x); return RESULT; }
#define SELFHASHTAB_VAL(T, W, RAD, STOP, RES0, RESULT, RESWRITE, THRESHMUL, THRESH, INIT) \
SELFHASHTAB(T, W, RAD, STOP, RES0, RESULT, RESWRITE, THRESHMUL, THRESH, \
/*AUXSIZE*/sizeof(u32), \
/* AUXINIT */ \
u32* val = (u32*)(hash+sz+ext) + msz-sz; \
memset32(val, 0, sz+ext); \
INIT , \
/*AUXEXTEND*/val -= dif; memset32(val, 0, dif); , \
/*AUXMOVE*/u32 v = val[j]; val[j] = 0; val[k] = v;)
B memberOf_c1(B t, B x) {
if (isAtm(x) || RNK(x)==0) thrM("∊: Argument cannot have rank 0");
u64 n = *SH(x);
if (n<=1) { decG(x); return n ? taga(arr_shVec(allOnes(1))) : emptyIVec(); }
usz csz = arr_csz(x);
u8 lw = cellWidthLog(x);
void* xv = tyany_ptr(x);
if (lw==0 || csz==0) {
usz i = csz==0? n : bit_find(xv, n, 1 &~ *(u64*)xv); decG(x);
B r = taga(arr_shVec(allZeroes(n)));
u64* rp = tyany_ptr(r);
rp[0]=1; if (i<n) bitp_set(rp, i, 1);
return r;
}
if (use_sorted(x, lw)) {
return shift_ne(x, n, lw, 1);
}
#define BRUTE(T) \
i##T* xp = xv; \
u64 rv = 1; \
for (usz i=1; i<n; i++) { \
bool c=1; i##T xi=xp[i]; \
PLAINLOOP for (usz j=0; j<i; j++) c &= xi!=xp[j]; \
rv |= c<<i; \
} \
decG(x); u64* rp; B r = m_bitarrv(&rp, n); rp[0] = rv; \
return r;
#define LOOKUP(T) \
usz tn = 1<<T; \
u##T* xp = (u##T*)xv; \
i8* rp; B r = m_i8arrv(&rp, n); \
TALLOC(u8, tab, tn); \
if (T>8 && n<tn/64) for (usz i=0; i<n; i++) tab[xp[i]]=1; \
else for (usz j=0; j<tn; j++) tab[j]=1; \
for (usz i=0; i<n; i++) { u##T j=xp[i]; rp[i]=tab[j]; tab[j]=0; } \
decG(x); TFREE(tab); \
return taga(cpyBitArr(r))
if (lw == 3) { if (n<8) { BRUTE(8); } else { LOOKUP(8); } }
if (lw == 4) { if (n<8) { BRUTE(16); } else { LOOKUP(16); } }
#undef LOOKUP
#define HASHTAB(T, W, RAD, STOP, THRESH) T* xp = (T*)xv; SELFHASHTAB( \
T, W, RAD, STOP, \
1, taga(cpyBitArr(r)), hash[j]=h; rp[i]=k!=h;, \
1, THRESH, 0,,,)
if (lw == 5) {
if (n<12) { BRUTE(32); }
i8* rp; B r = m_i8arrv(&rp, n);
HASHTAB(u32, 32, 1, n/2, sz==msz? 1 : sz>=(1<<15)? 3 : 5)
// Radix-assisted lookup when hash table gives up
usz rx = 256, tn = 1<<16; // Radix; table length
u32* v0 = (u32*)xv;
i8* r0 = rp;
TALLOC(u8, alloc, 6*n+(4+(tn>3*n?tn:3*n)+(2*rx+1)*sizeof(usz)));
// timeline
// Allocations len count radix hash deradix bytes layout:
usz *c0 = (usz*)(alloc)+1; // rx [+++................] c0 rx #
usz *c1 = (usz*)(c0+rx); // rx [++................] c1 rx #
u8 *k0 = (u8 *)(c1+rx); // n [+.............] k0 n ##
u32 *v2 = (u32*)(k0+n); // n+1 [+.......] v2 4*n+4 ########
u8 *k1 = (u8 *)(v2+n+1); // n [+............] k1 n ##
u32 *v1 = (u32*)(k1); // n [+-] v1 4*n ########
u8 *r2 = (u8 *)(v2); // n [+.....] r2 n ##
u8 *r1 = (u8 *)(k1+n); // n [+..] r1 n ##
u8 *tab= (u8 *)(r1); // tn [+] tab tn #####
RADIX_LOOKUP_32(1, =0)
return taga(cpyBitArr(r));
}
if (lw == 6 && canCompare64_norm(x, n)) {
if (n<20) { BRUTE(64); }
i8* rp; B r = m_i8arrv(&rp, n);
HASHTAB(u64, 64, 0, n, sz==msz? 0 : sz>=(1<<18)? 0 : sz>=(1<<14)? 3 : 5)
decG(r); // Fall through
}
#undef HASHTAB
#undef BRUTE
if (RNK(x)>1) {
if (shouldWidenBitarr(x, csz)) return C1(memberOf, widenBitArr(x, 1));
x = toCells(x);
}
u64* rp; B r = m_bitarrv(&rp, n);
H_Sb* set = m_Sb(64);
SGetU(x)
for (usz i = 0; i < n; i++) bitp_set(rp, i, !ins_Sb(&set, GetU(x,i)));
free_Sb(set); decG(x);
return r;
}
B count_c1(B t, B x) {
if (isAtm(x) || RNK(x)==0) thrM("⊒: Argument cannot have rank 0");
u64 n = *SH(x);
if (n<=1) { decG(x); return n ? taga(arr_shVec(allZeroes(1))) : emptyIVec(); }
if (n>(usz)I32_MAX+1) thrM("⊒: Argument length >2⋆31 not supported");
usz csz = arr_csz(x);
if (csz==0) { decG(x); return C1(ud, m_f64(n)); }
u8 lw = cellWidthLog(x);
if (lw==0) {
u64* xp = bitarr_ptr(x);
B r;
#define COUNT_BOOL(T) \
T* rp; r = m_##T##arrv(&rp, n); \
usz n1 = 0; \
for (usz i=0; i<n; ) { \
u64 bb = xp[i/64]; \
for (usz e=n-i<64?n:i+64; i<e; i++) { \
bool b = bb&1; bb>>=1; \
rp[i] = b? n1 : i-n1; \
n1 += b; \
} \
}
if (n <= 128) { COUNT_BOOL(i8) }
else if (n <= 1<<15) { COUNT_BOOL(i16) }
else { COUNT_BOOL(i32) }
decG(x); return r;
#undef COUNT_BOOL
}
if (use_sorted(x, lw) && n>16 && (lw>4 || n<1<<16)) { // ↕∘≠(⊣-⌈`∘×)∊
B c = shift_ne(x, n, lw, 1);
B i = C1(ud, m_f64(n));
B m = C2(mul, c, inc(i));
return C2(sub, i, scan_max_num(m, TI(m,elType), n));
}
void* xv = tyany_ptr(x);
#define BRUTE(T) \
i##T* xp = xv; \
i8* rp; B r = m_i8arrv(&rp, n); rp[0]=0; \
for (usz i=1; i<n; i++) { \
usz c=0; i##T xi=xp[i]; \
PLAINLOOP for (usz j=0; j<i; j++) c += xi==xp[j]; \
rp[i] = c; \
} \
decG(x); return r;
#define LOOKUP(T) \
usz tn = 1<<T; \
u##T* xp = (u##T*)xv; \
i32* rp; B r = m_i32arrv(&rp, n); \
TALLOC(i32, tab, tn); \
if (T>8 && n<tn/16) for (usz i=0; i<n; i++) tab[xp[i]]=0; \
else for (usz j=0; j<tn; j++) tab[j]=0; \
for (usz i=0; i<n; i++) rp[i]=tab[xp[i]]++; \
decG(x); TFREE(tab); \
return num_squeeze(r)
if (lw==3) { if (n<12) { BRUTE(8); } else { LOOKUP(8); } }
if (lw==4) { if (n<12) { BRUTE(16); } else { LOOKUP(16); } }
#undef LOOKUP
#define HASHTAB(T, W, RAD, STOP, THRESH) T* xp = (T*)xv; SELFHASHTAB_VAL( \
T, W, RAD, STOP, \
/*RES0*/0, /*RETURN*/num_squeeze(r), \
/* RESWRITE */ \
bool e0=h==x0; rp[i]=val[j]+(ctr0&-(u32)e0); \
hash[j]=h; val[j]+=!e0; ctr0+=e0; , \
/*THRESHMUL*/1, THRESH, \
/*INIT*/u32 ctr0 = 1;)
if (lw==5) {
if (n<20) { BRUTE(32); }
i32* rp; B r = m_i32arrv(&rp, n);
HASHTAB(u32, 32, 1, n/2, sz==msz? 1 : sz>=(1<<14)? 3 : 5)
// Radix-assisted lookup
usz rx = 256, tn = 1<<16; // Radix; table length
u32* v0 = (u32*)xv;
i32* r0 = rp;
TALLOC(u8, alloc, 6*n+(4+4*(tn>n?tn:n)+(2*rx+1)*sizeof(usz)));
// timeline
// Allocations len count radix hash deradix bytes layout:
usz *c0 = (usz*)(alloc)+1; // rx [+++................] c0 rx #
usz *c1 = (usz*)(c0+rx); // rx [++................] c1 rx #
u8 *k0 = (u8 *)(c1+rx); // n [+.............] k0 n ##
u8 *k1 = (u8 *)(k0+n); // n [+............] k1 n ##
u32 *v2 = (u32*)(k1+n); // n+1 [+....-] v2 4*n ########
u32 *v1 = (u32*)(v2+n+1); // n [+..] v1 4*n ########
u32 *r2 = (u32*)v2; // n [+.....] r2 4*n ########
u32 *r1 = (u32*)v1; // n [+..] r1 4*n ########
u32 *tab= (u32*)v1; // tn [+] tab 4*tn ###########
RADIX_LOOKUP_32(0, ++)
return num_squeeze(r);
}
if (lw == 6 && canCompare64_norm(x, n)) {
if (n<20) { BRUTE(64); }
i32* rp; B r = m_i32arrv(&rp, n);
HASHTAB(u64, 64, 0, n, sz==msz? 0 : sz>=(1<<18)? 0 : sz>=(1<<14)? 3 : 5)
decG(r); // Fall through
}
#undef HASHTAB
#undef BRUTE
if (RNK(x)>1) {
if (shouldWidenBitarr(x, csz)) return C1(count, widenBitArr(x, 1));
x = toCells(x);
}
i32* rp; B r = m_i32arrv(&rp, n);
H_b2i* map = m_b2i(64);
SGetU(x)
for (usz i = 0; i < n; i++) {
bool had; u64 p = mk_b2i(&map, GetU(x,i), &had);
rp[i] = had? ++map->a[p].val : (map->a[p].val = 0);
}
decG(x); free_b2i(map);
return num_squeeze(r);
}
static B reduceI32WidthBelow(B r, usz after) {
return after<=2? taga(cpyBitArr(r)) : after<=I8_MAX+1? taga(cpyI8Arr(r)) : after<=I16_MAX+1? taga(cpyI16Arr(r)) : r;
}
B indexOf_c1(B t, B x) {
if (isAtm(x) || RNK(x)==0) thrM("⊐: 𝕩 cannot have rank 0");
u64 n = *SH(x);
if (n<=1) { zeroRes: decG(x); return n? taga(arr_shVec(allZeroes(n))) : emptyIVec(); }
if (n>(usz)I32_MAX+1) thrM("⊐: Argument length >2⋆31 not supported");
usz csz = arr_csz(x);
if (csz==0) goto zeroRes;
u8 lw = cellWidthLog(x);
void* xv = tyany_ptr(x);
if (lw == 0) {
B r = 1&*(u64*)xv ? bit_negate(x) : x;
return C1(shape, r);
}
if (use_sorted(x, lw) && n>8) {
return scan_add_bool(shift_ne(x, n, lw, 0), n);
}
#define BRUTE(T) \
i##T* xp = xv; \
i8* rp; B r = m_i8arrv(&rp, n); rp[0]=0; \
TALLOC(i##T, uniq, n); uniq[0]=xp[0]; \
for (usz i=1, u=1; i<n; i++) { \
bool c=1; usz s=0; i##T xi=uniq[u]=xp[i]; \
for (usz j=0; j<u; j++) s += (c &= xi!=uniq[j]); \
rp[i]=s; u+=u==s; \
} \
decG(x); TFREE(uniq); return r;
#define DOTAB(T) /* leaves 1+max in u */ \
i32 u=0; \
for (usz i=0; i<n; i++) { \
T j=xp[i]; i32 t=tab[j]; \
if (t==n) tab[j]=u++; \
rp[i]=tab[j]; \
}
#define LOOKUP(T) \
usz tn = 1<<T; \
u##T* xp = (u##T*)xv; \
i32* rp; B r = m_i32arrv(&rp, n); \
TALLOC(i32, tab, tn); \
if (T>8 && n<tn/16) for (usz i=0; i<n; i++) tab[xp[i]]=n; \
else for (usz j=0; j<tn; j++) tab[j]=n; \
DOTAB(u##T) \
decG(x); TFREE(tab); \
return reduceI32WidthBelow(r, u)
if (lw==3) { if (n<12) { BRUTE(8); } else { LOOKUP(8); } }
if (lw==4) { if (n<12) { BRUTE(16); } else { LOOKUP(16); } }
#undef LOOKUP
#define HASHTAB(T, W, THRESH) SELFHASHTAB_VAL(T, W, 0, 2*n, \
/*RES0*/0, /*RETURN*/reduceI32WidthBelow(r, ctr), \
/* RESWRITE */ \
if (k!=h) { val[j]=ctr++; hash[j]=h; } rp[i]=val[j]; , \
/*THRESHMUL*/2, THRESH, \
/*INIT*/u32 ctr = 1;)
if (lw==5) {
if (n<12) { BRUTE(32); }
B r;
i32* rp; r = m_i32arrv(&rp, n);
i32* xp = tyany_ptr(x);
i32 min=I32_MAX, max=I32_MIN;
for (usz i = 0; i < n; i++) {
i32 c = xp[i];
if (c<min) min = c;
if (c>max) max = c;
}
i64 dst = 1 + (max-(i64)min);
if (dst<n*5 || dst<50) {
TALLOC(i32, tmp, dst); i32* tab = tmp-min;
for (i64 i = 0; i < dst; i++) tmp[i] = n;
DOTAB(i32)
TFREE(tmp);
decG(x);
return reduceI32WidthBelow(r, u);
}
HASHTAB(u32, 32, sz==msz? 0 : sz>=(1<<18)? 1 : sz>=(1<<14)? 4 : 6)
decG(r); // Fall through
}
if (lw==6 && canCompare64_norm(x, n)) {
if (n<16) { BRUTE(64); }
i32* rp; B r = m_i32arrv(&rp, n);
u64* xp = tyany_ptr(x);
HASHTAB(u64, 64, sz==msz? 0 : sz>=(1<<17)? 1 : sz>=(1<<13)? 4 : 6)
decG(r); // Fall through
}
#undef HASHTAB
#undef BRUTE
#undef DOTAB
if (RNK(x)>1) {
if (shouldWidenBitarr(x, csz)) return C1(indexOf, widenBitArr(x, 1));
x = toCells(x);
}
i32* rp; B r = m_i32arrv(&rp, n);
H_b2i* map = m_b2i(64);
SGetU(x)
i32 ctr = 0;
for (usz i = 0; i < n; i++) {
bool had; u64 p = mk_b2i(&map, GetU(x,i), &had);
if (had) rp[i] = map->a[p].val;
else rp[i] = map->a[p].val = ctr++;
}
free_b2i(map); decG(x);
return reduceI32WidthBelow(r, ctr);
}
B find_c1(B t, B x) {
if (isAtm(x) || RNK(x)==0) thrM("⍷: Argument cannot have rank 0");
usz n = *SH(x);
if (n<=1) return x;
if (TI(x,elType)==el_bit && RNK(x)==1) {
u64* xp = bitarr_ptr(x);
u64 x0 = 1 & *xp;
usz i = bit_find(xp, n, !x0); decG(x);
u64* rp; B r = m_bitarrv(&rp, 1 + (i<n));
rp[0] = 2 ^ -x0;
return r;
}
return C2(slash, C1(memberOf, inc(x)), x);
}