diff --git a/src/builtins/cells.c b/src/builtins/cells.c index 00e55267..e4632510 100644 --- a/src/builtins/cells.c +++ b/src/builtins/cells.c @@ -9,6 +9,7 @@ B shape_c2(B, B, B); B transp_c2(B, B, B); B fold_rows(Md1D* d, B x); // from fold.c B fold_rows_bit(Md1D* d, B x); // from fold.c +B scan_rows_bit(Md1D* d, B x); // from scan.c B takedrop_highrank(bool take, B w, B x); // from sfns.c B try_interleave_cells(B w, B x, ur xr, ur xk, usz* xsh); // from transpose.c @@ -461,6 +462,17 @@ B for_cells_c1(B f, u32 xr, u32 cr, u32 k, B x, u32 chr) { // F⎉cr x, with arr if (m <= 64 && m < sh[0]) return fold_rows(fd, x); } } + if (rtid==n_scan) { + if (cr==0) goto base; + usz *sh = SH(x); usz m = sh[k]; + if (m<=1 || IA(x)==0) return x; + if (!isFun(fd->f)) goto base; + u8 frtid = v(fd->f)->flags-1; + if (frtid==n_rtack) return x; + if (k==1 && xr==2 && isPervasiveDyExt(fd->f) && TI(x,elType)==el_bit) { + B r = scan_rows_bit(fd, x); if (!q_N(r)) return r; + } + } } else if (TY(f) == t_md2D) { Md2D* fd = c(Md2D,f); u8 rtid = fd->m2->flags-1; diff --git a/src/builtins/scan.c b/src/builtins/scan.c index f798881d..a059c387 100644 --- a/src/builtins/scan.c +++ b/src/builtins/scan.c @@ -329,3 +329,20 @@ B scan_c2(Md1D* d, B w, B x) { B f = d->f; decG(x); return withFill(r.b, wf); } + +B scan_rows_bit(Md1D* fd, B x) { + assert(isArr(x) && RNK(x)==2 && TI(x,elType)==el_bit); + #if SINGELI + if (!v(fd->f)->flags) return bi_N; + u8 rtid = v(fd->f)->flags-1; + if (rtid==n_and|rtid==n_or) { + usz *sh = SH(x); usz n = sh[0]; usz m = sh[1]; + u64* xp = bitarr_ptr(x); + u64* rp; B r = m_bitarrc(&rp, x); + if (rtid==n_and) si_scan_rows_and(xp, rp, n, m); + else si_scan_rows_or (xp, rp, n, m); + decG(x); return r; + } + #endif + return bi_N; +} diff --git a/src/singeli/src/scan.singeli b/src/singeli/src/scan.singeli index aec9976a..3858c748 100644 --- a/src/singeli/src/scan.singeli +++ b/src/singeli/src/scan.singeli @@ -295,3 +295,86 @@ export{'si_scan_plus_i32_i32', plus_scanC{i32, i32}} export{'si_scan_plus_i16_f64', plus_scanG{i16, f64}} export{'si_scan_plus_i32_f64', plus_scanG{i32, f64}} + + + +# Row-wise boolean scan +fn scan_rows_andor{id}(src:*u64, dst:*u64, n:usz, l:usz) : void = { + def qand = not id + assert{l > 0} + nw := cdiv{n*l, 64} + def res_m1{x,c,m} = { # result word with carry c, popc{m}<=1 + if (qand) x &~ ((x+c) & (x+m)) + else x | ((-x-c) &~ (x-m)) + } + if (l < 64) { + if ((l & (l-1)) == 0) { + if (l == 2) { + @for (r in dst, x in src over nw) { + r = (if (qand) x & (x<<1 | 64w2b01) else x | (x<<1 & 64w2b10)) + } + } else { + m:u64 = (~u64~~0) / ((u64~~1 << l)-1) + t := m << (l-1) + @for (r in dst, x in src over nw) { + r = (if (qand) x &~ ((t&x) ^ ((x&~t) + m)) + else x | ~((t&~x) ^ ((x|t) - m))) + } + } + # could use for l>=8; not much faster and takes up space + # def rowwise{T} = @for (r in *T~~dst, x in *T~~src over (64/width{T})*nw) r = x &~ (x+1) + } else { + d:usz = 64 % l + m:u64 = (~u64~~0 >> d) / ((u64~~1 << l)-1) + m = m<> 1 else ~(x | m) >> 1 ) + a:= (if (qand) s + (x&(m|c)) else s + ((m|c) &~ x)) + r = (if (qand) s ^ a else ~(s ^ a) ) + c = a >> 63 + m = m>>d | m<<(l-d) + } + } + } else if (l < 160) { + q:usz = 0 # distance to next row boundary + c:u64 = id # carry + @for (r in dst, x in src over nw) { + b:= q<64 # whether there's a boundary + p:= q%64 # its position + q-= 64 - (l &- b) + r = res_m1{x, c, promote{u64, b} << p} + c = r >> 63 + } + } else { + ib:usz = 0 # row bit index + wn:usz = 0 # starting word of next row + c:u64 = id # carry + def word{bit} = bit * ((1<<64) - 1) + @for (i to n) { + iw:= wn + r := res_m1{load{src, iw}, c, u64~~1 << (ib%64)} + store{dst, iw, r}; ++iw + ib+= l; wn = ib/64 + c = r>>63 + if (c != id) while (iw < wn) { + x:= load{src, iw} + if (x != word{not id}) { + c = id + store{dst, iw, if (qand) x &~ (x+1) else x | -x}; ++iw + goto{'shortcut'} + } + store{dst, iw, x}; ++iw + } + setlabel{'shortcut'} + @for (r in dst over _ from iw to wn) r = word{id} + } + if (ib%64 != 0) { + x:= load{src, wn} + store{dst, wn, if (qand) x &~ (x+c) else x | (-x-c)} + } + } +} + +export{'si_scan_rows_and', scan_rows_andor{0}} +export{'si_scan_rows_or', scan_rows_andor{1}}