#include "../core.h" #include "../utils/each.h" #include "../utils/mut.h" #include "../utils/talloc.h" #include "../builtins.h" static Arr* take_impl(usz ria, B x) { // consumes x; returns v↑β₯Šπ•© without set shape; v is non-negative usz xia = IA(x); if (ria>xia) { B xf = getFillE(x); MAKE_MUT(r, ria); mut_init(r, el_or(TI(x,elType), selfElType(xf))); MUTG_INIT(r); mut_copyG(r, 0, x, 0, xia); mut_fillG(r, xia, xf, ria-xia); decG(x); if (r->fns->elType!=el_B) { dec(xf); return mut_fp(r); } return a(withFill(mut_fv(r), xf)); } else { return TI(x,slice)(x,0,ria); } } B m_vec1(B a) { if (isF64(a)) { i32 i = (i32)a.f; if (RARE(a.f != i)) { f64* rp; B r = m_f64arrv(&rp, 1); rp[0] = a.f; return r; } else if (q_ibit(i)) { u64* rp; B r = m_bitarrv(&rp, 1); rp[0] = i; return r; } else if (i == (i8 )i) { i8* rp; B r = m_i8arrv (&rp, 1); rp[0] = i; return r; } else if (i == (i16)i) { i16* rp; B r = m_i16arrv(&rp, 1); rp[0] = i; return r; } else { i32* rp; B r = m_i32arrv(&rp, 1); rp[0] = i; return r; } } if (isC32(a)) { u32 c = o2cG(a); if (LIKELY(c1) thrM("β₯Š: 𝕨 must have rank at most 1"); if (IA(w)>UR_MAX) thrM("β₯Š: Result rank too large"); nr = IA(w); sh = nr<=1? NULL : m_shArr(nr); if (TI(w,elType)==el_i32) { i32* wi = i32any_ptr(w); if (nr>1) for (i32 i = 0; i < nr; i++) sh->a[i] = wi[i]; bool bad=false, good=false; for (i32 i = 0; i < nr; i++) { if (wi[i]<0) thrF("β₯Š: 𝕨 contained %i", wi[i]); bad|= mulOn(nia, wi[i]); good|= wi[i]==0; } if (bad && !good) thrM("β₯Š: 𝕨 too large"); } else { SGetU(w) i32 unkPos = -1; i32 unkInd; bool bad=false, good=false; for (i32 i = 0; i < nr; i++) { B c = GetU(w, i); if (isF64(c)) { usz v = o2s(c); if (sh) sh->a[i] = v; bad|= mulOn(nia, v); good|= v==0; } else { if (isArr(c) || !isVal(c)) thrM("β₯Š: 𝕨 must consist of natural numbers or ∘ ⌊ ⌽ ↑"); if (unkPos!=-1) thrM("β₯Š: 𝕨 contained multiple computed axes"); unkPos = i; if (!isPrim(c)) thrM("β₯Š: 𝕨 must consist of natural numbers or ∘ ⌊ ⌽ ↑"); unkInd = ((i32)v(c)->flags) - 1; good|= xia==0 | unkInd==n_floor; } } if (bad && !good) thrM("β₯Š: 𝕨 too large"); if (unkPos!=-1) { if (unkInd!=n_atop & unkInd!=n_floor & unkInd!=n_reverse & unkInd!=n_take) thrM("β₯Š: 𝕨 must consist of natural numbers or ∘ ⌊ ⌽ ↑"); if (nia==0) thrM("β₯Š: Can't compute axis when the rest of the shape is empty"); i64 div = xia/nia; i64 mod = xia%nia; usz item; bool fill = false; if (unkInd == n_atop) { if (mod!=0) thrM("β₯Š: Shape must be exact when reshaping with ∘"); item = div; } else if (unkInd == n_floor) { item = div; } else if (unkInd == n_reverse) { item = mod? div+1 : div; } else if (unkInd == n_take) { item = mod? div+1 : div; fill = true; } else UD; if (sh) sh->a[unkPos] = item; nia = uszMul(nia, item); if (fill) { if (!isArr(x)) x = m_atomUnit(x); x = taga(arr_shVec(take_impl(nia, x))); xia = nia; } } } decG(w); } if (isArr(x)) { if (nia <= xia) { return truncReshape(x, xia, nia, nr, sh); } else { if (xia <= 1) { if (RARE(xia == 0)) thrM("β₯Š: Empty 𝕩 and non-empty result"); B n = IGet(x,0); decG(x); x = n; goto unit; } if (xia <= nia/2) x = any_squeeze(x); B xf = getFillQ(x); MAKE_MUT(m, nia); mut_init(m, TI(x,elType)); MUTG_INIT(m); i64 div = nia/xia; i64 mod = nia%xia; for (i64 i = 0; i < div; i++) mut_copyG(m, i*xia, x, 0, xia); mut_copyG(m, div*xia, x, 0, mod); decG(x); Arr* ra = mut_fp(m); arr_shSetU(ra, nr, sh); return withFill(taga(ra), xf); } } else { unit: Arr* r; #define FILL(E,T,V) T* rp; r = m_##E##arrp(&rp,nia); fill_words(rp, V, (u64)nia*sizeof(T)); if (isF64(x)) { i32 n = (i32)x.f; if (RARE(n!=x.f)) { FILL(f64,f64,x.u) } else if (n==(i8)n) { // memset can be faster than writing words u8 b = n; i8* rp; u64 nb = nia; if (b <= 1) { r = m_bitarrp((u64**)&rp,nia); nb = 8*BIT_N(nia); b=-b; } else { r = m_i8arrp ( &rp,nia); } memset(rp, b, nb); } else { if(n==(i16)n) { FILL(i16,i16,(u16)n*0x0001000100010001) } else { FILL(i32,i32,(u32)n*0x0000000100000001) } } } else if (isC32(x)) { u32 c = o2cG(x); if (c==(u8 )c) { u8* rp; r = m_c8arrp(&rp,nia); memset(rp, c, nia); } else if (c==(u16)c) { FILL(c16,u16,c*0x0001000100010001) } else { FILL(c32,u32,c*0x0000000100000001) } #undef FILL } else { B xf = asFill(inc(x)); r = m_fillarrp(nia); if (nia) incBy(x, nia-1); else dec(x); fill_words(fillarr_ptr(r), x.u, (u64)nia*8); fillarr_setFill(r, xf); } arr_shSetU(r,nr,sh); return taga(r); } } B pick_c1(B t, B x) { if (isAtm(x)) return x; if (RARE(IA(x)==0)) { thrM("βŠ‘: Argument cannot be empty"); // B r = getFillE(x); // dec(x); // return r; } B r = IGet(x, 0); decG(x); return r; } static NOINLINE void checkIndexList(B w, ur xr) { SGetU(w) usz ia = IA(w); for (usz i = 0; i < ia; i++) if (!isNum(GetU(w,i))) thrM("βŠ‘: 𝕨 contained list with mixed-type elements"); if (ia>xr+xr+10) { if (RNK(w)!=1) thrF("βŠ‘: Leaf arrays in 𝕨 must have rank 1 (element in 𝕨 has shape %H)", w); thrF("βŠ‘: Leaf array in 𝕨 too large (has shape %H)", w); } } static B recPick(B w, B x) { // doesn't consume assert(isArr(w) && isArr(x)); usz ia = IA(w); ur xr = RNK(x); usz* xsh = SH(x); switch(TI(w,elType)) { default: UD; case el_i8: { i8* wp = i8any_ptr (w); if(RNK(w)!=1)goto wrr; if (ia!=xr)goto wrl; usz c=0; for (usz i = 0; i < ia; i++) { c = c*xsh[i] + WRAP(wp[i], xsh[i], goto oob); }; return IGet(x,c); } case el_i16: { i16* wp = i16any_ptr(w); if(RNK(w)!=1)goto wrr; if (ia!=xr)goto wrl; usz c=0; for (usz i = 0; i < ia; i++) { c = c*xsh[i] + WRAP(wp[i], xsh[i], goto oob); }; return IGet(x,c); } case el_i32: { i32* wp = i32any_ptr(w); if(RNK(w)!=1)goto wrr; if (ia!=xr)goto wrl; usz c=0; for (usz i = 0; i < ia; i++) { c = c*xsh[i] + WRAP(wp[i], xsh[i], goto oob); }; return IGet(x,c); } case el_f64: { f64* wp = f64any_ptr(w); if(RNK(w)!=1)goto wrr; if (ia!=xr)goto wrl; usz c=0; for (usz i = 0; i < ia; i++) { i64 ws = (i64)wp[i]; if (wp[i]!=ws) thrM(ws==I64_MIN? "βŠ‘: 𝕨 contained value too large" : "βŠ‘: 𝕨 contained a non-integer"); c = c*xsh[i] + WRAP(ws, xsh[i], goto oob); }; return IGet(x,c); } case el_c8: case el_c16: case el_c32: case el_bit: case el_B: { if (ia==0) { if (xr!=0) thrM("βŠ‘: Empty array in 𝕨 must correspond to unit in 𝕩"); return IGet(x,0); } SGetU(w) if (isNum(GetU(w,0))) { if(RNK(w)!=1) goto wrr; if (ia!=xr) goto wrl; usz c=0; for (usz i = 0; i < ia; i++) { B cw = GetU(w,i); if (!isNum(cw)) thrM("βŠ‘: 𝕨 contained list with mixed-type elements"); c = c*xsh[i] + WRAP(o2i64(cw), xsh[i], goto oob); } return IGet(x,c); } else { M_HARR(r, ia); for(usz i=0; i=2); usz csz = arr_csz(x); usz* csh = xsh+1; for (usz i = 0; i < ria; i++) { usz len = post? cam-i : i; Arr* c = slice(x, post? i*csz : 0, len*csz); usz* sh = arr_shAlloc(c, xr); *(sh++) = len; shcpy(sh, csh, xr-1); HARR_ADD(r, i, taga(c)); } dec(x); } B rf = incG(HARR_O(r).a[post? cam : 0]); return withFill(HARR_FV(r), rf); } B take_c1(B t, B x) { return affixes(x, 0); } B drop_c1(B t, B x) { return affixes(x, 1); } extern B rt_take, rt_drop; B take_c2(B t, B w, B x) { if (isNum(w)) { if (!isArr(x)) x = m_atomUnit(x); i64 wv = o2i64(w); ur xr = RNK(x); usz csz = 1; usz* xsh; if (xr>1) { csz = arr_csz(x); xsh = SH(x); ptr_inc(shObjS(xsh)); // we'll look at it at the end and dec there } i64 t = wv*csz; // TODO error on overflow somehow Arr* a; if (t>=0) { a = take_impl(t, x); } else { t = -t; usz xia = IA(x); if (t>xia) { B xf = getFillE(x); MAKE_MUT(r, t); mut_init(r, el_or(TI(x,elType), selfElType(xf))); MUTG_INIT(r); mut_fillG(r, 0, xf, t-xia); mut_copyG(r, t-xia, x, 0, xia); decG(x); dec(xf); a = mut_fp(r); } else { a = TI(x,slice)(x,xia-t,t); } } if (xr<=1) { arr_shVec(a); } else { usz* rsh = arr_shAlloc(a, xr); // xr>1, don't have to worry about 0 rsh[0] = wv<0?-wv:wv; shcpy(rsh+1, xsh+1, xr-1); ptr_dec(shObjS(xsh)); } return taga(a); } return c2(rt_take, w, x); } B drop_c2(B t, B w, B x) { if (isNum(w) && isArr(x) && RNK(x)==1) { i64 v = o2i64(w); usz ia = IA(x); if (v<0) return -v>ia? slicev(x, 0, 0) : slicev(x, 0, v+ia); else return v>ia? slicev(x, 0, 0) : slicev(x, v, ia-v); } return c2(rt_drop, w, x); } B join_c1(B t, B x) { if (isAtm(x)) thrM("∾: Argument must be an array"); ur xr = RNK(x); usz xia = IA(x); if (xia==0) { B xf = getFillE(x); if (isAtm(xf)) { decA(xf); decG(x); if (!PROPER_FILLS && xr==1) return emptyHVec(); thrM("∾: Empty array 𝕩 cannot have an atom fill element"); } ur ir = RNK(xf); if (ir1) { usz* xsh = SH(x); for (usz i = 0; i < xr; i++) sh[i] = xsh[i]*fsh[i]; } shcpy(sh+xr, fsh+xr, ir-xr); } dec(xf); decG(x); return withFill(r.b, xff); } else if (xr==1) { SGetU(x) B x0 = GetU(x,0); B rf; if(SFNS_FILLS) rf = getFillQ(x0); ur rm = isAtm(x0) ? 0 : RNK(x0); // Maximum element rank seen ur rr = rm; // Result rank, or minimum possible so far ur rd = 0; // Difference of max and min lengths (0 or 1) usz* esh = NULL; usz cam = 1; // Result length if (rm) { esh = SH(x0); cam = *esh++; } else { rr++; } for (usz i = 1; i < xia; i++) { B c = GetU(x, i); ur cr = isAtm(c) ? 0 : RNK(c); if (cr == 0) { if (rm > 1) thrF("∾: Item ranks in a list can differ by at most one (contained ranks %i and %i)", 0, rm); rd=rm; cam++; } else { usz* csh = SH(c); ur cd = rm - cr; if (RARE(cd > rd)) { if ((ur)(cd+1-rd) > 2-rd) thrF("∾: Item ranks in a list can differ by at most one (contained ranks %i and %i)", rm-rd*(cr==rm), cr); if (cr > rr) { // Previous elements were cells esh--; if (cam != i * *esh) thrM("∾: Item trailing shapes must be equal"); rr=cr; cam=i; } rm = cr>rm ? cr : rm; rd = 1; } cam += cr < rm ? 1 : *csh++; if (!eqShPart(csh, esh, cr-1)) thrF("∾: Item trailing shapes must be equal (contained arrays with shapes %H and %H)", x0, c); } if (SFNS_FILLS && !noFill(rf)) rf = fill_or(rf, getFillQ(c)); } if (rm==0) thrM("∾: Some item rank must be equal or greater than rank of argument"); usz csz = shProd(esh, 0, rr-1); MAKE_MUT(r, cam*csz); usz ri = 0; for (usz i = 0; i < xia; i++) { B c = GetU(x, i); if (isArr(c)) { usz cia = IA(c); mut_copy(r, ri, c, 0, cia); ri+= cia; } else { mut_set(r, ri, inc(c)); ri++; } } assert(ri==cam*csz); Arr* ra = mut_fp(r); usz* sh = arr_shAlloc(ra, rr); if (sh) { sh[0] = cam; shcpy(sh+1, esh, rr-1); } decG(x); return SFNS_FILLS? qWithFill(taga(ra), rf) : taga(ra); } else if (xr==0) { return bqn_merge(x); } else { SGetU(x) B x0 = GetU(x,0); B rf; if(SFNS_FILLS) rf = getFillQ(x0); ur r0 = isAtm(x0) ? 0 : RNK(x0); usz xia = IA(x); usz* xsh = SH(x); usz tlen = 4*xr+2*r0; for (usz a=0; a1; ) st[a-1] = st[a]*xsh[a]; // Stride usz* tsh0 = st+xr; usz* tsh = tsh0+xr+r0; // Test shapes // Length buffer i is lp+lp[i] usz* lp = tsh+xr+r0; lp[0]=xr; for (usz a=1; ar1s) r1s=ll[i]; ur r1 = r1s; ur a0 = r1==r0; // Root has axis a if (tr < a0) thrM("∾: Ranks of argument items too small"); for (usz i=0; i1) thrF("∾: Item ranks along an axis can differ by at most one (contained ranks %i and %i along axis %i)", ll[i], r1, a); ll[i] = -1; } else { B c = GetU(x, i*step); ll[i] = SH(c)[r0-tr]; } } // Check shapes for (usz j=0; jxr?wr:xr; if (c==0) { HArr_p r = m_harrUv(2); r.a[0] = IGet(w,0); decG(w); r.a[1] = IGet(x,0); decG(x); return qWithFill(r.b, f); } if (c-wr > 1 || c-xr > 1) thrF("∾: Argument ranks must differ by 1 or less (%i≑=𝕨, %i≑=𝕩)", wr, xr); bool reusedW; B r = arr_join_inline(w, x, false, &reusedW); if (c==1) { if (RNK(r)==0) SRNK(r,1); } else { assert(c>1); ur rnk0 = RNK(r); ShArr* sh0 = shObj(r); usz wia; usz* wsh; if (wr==1 && reusedW) { wia = IA(w)-IA(x); wsh = &wia; } else { wsh = SH(w); // when wr>1, shape object won't be disturbed by arr_join_inline } usz* xsh = SH(x); SRNK(r, 0); // otherwise shape allocation failing may break things usz* rsh = arr_shAlloc(a(r), c); #if PRINT_JOIN_REUSE printf(reusedW? "reuse:1;" : "reuse:0;"); #endif for (i32 i = 1; i < c; i++) { usz s = xsh[i+xr-c]; if (RARE(wsh[i+wr-c] != s)) { B msg = make_fmt("∾: Lengths not matchable (%2H ≑ ≒𝕨, %H ≑ ≒𝕩)", wr, wsh, x); if (rnk0>1) decShObj(sh0); mm_free((Value*)shObjS(rsh)); arr_shVec(a(r)); thr(msg); } rsh[i] = s; } rsh[0] = (wr==c? wsh[0] : 1) + (xr==c? xsh[0] : 1); if (rnk0>1) decShObj(sh0); } decG(x); if (!reusedW) decG(w); return qWithFill(r, f); } B couple_c1(B t, B x) { if (isAtm(x)) return m_vec1(x); usz rr = RNK(x); usz ia = IA(x); Arr* r = TI(x,slice)(incG(x),0, ia); usz* sh = arr_shAlloc(r, rr+1); if (sh) { sh[0] = 1; shcpy(sh+1, SH(x), rr); } decG(x); return taga(r); } B couple_c2(B t, B w, B x) { if (isAtm(w)&isAtm(x)) return m_vec2(w, x); if (isAtm(w)) w = m_atomUnit(w); if (isAtm(x)) x = m_atomUnit(x); if (!eqShape(w, x)) thrF("≍: 𝕨 and 𝕩 must have equal shapes (%H ≑ ≒𝕨, %H ≑ ≒𝕩)", w, x); usz ia = IA(w); ur wr = RNK(w); MAKE_MUT(r, ia*2); mut_init(r, el_or(TI(w,elType), TI(x,elType))); MUTG_INIT(r); mut_copyG(r, 0, w, 0, ia); mut_copyG(r, ia, x, 0, ia); Arr* ra = mut_fp(r); usz* sh = arr_shAlloc(ra, wr+1); if (sh) { sh[0]=2; shcpy(sh+1, SH(w), wr); } if (!SFNS_FILLS) { decG(w); decG(x); return taga(ra); } B rf = fill_both(w, x); decG(w); decG(x); return qWithFill(taga(ra), rf); } static inline void shift_check(B w, B x) { ur wr = RNK(w); usz* wsh = SH(w); ur xr = RNK(x); usz* xsh = SH(x); if (wr+1!=xr & wr!=xr) thrF("shift: =𝕨 must be =𝕩 or Β―1+=𝕩 (%i≑=𝕨, %i≑=𝕩)", wr, xr); for (i32 i = 1; i < xr; i++) if (wsh[i+wr-xr] != xsh[i]) thrF("shift: Lengths not matchable (%H ≑ ≒𝕨, %H ≑ ≒𝕩)", w, x); } B shiftb_c1(B t, B x) { if (isAtm(x) || RNK(x)==0) thrM("Β»: Argument cannot be a scalar"); usz ia = IA(x); if (ia==0) return x; B xf = getFillE(x); usz csz = arr_csz(x); MAKE_MUT(r, ia); mut_init(r, el_or(TI(x,elType), selfElType(xf))); MUTG_INIT(r); mut_copyG(r, csz, x, 0, ia-csz); mut_fillG(r, 0, xf, csz); return qWithFill(mut_fcd(r, x), xf); } B shiftb_c2(B t, B w, B x) { if (isAtm(x) || RNK(x)==0) thrM("Β»: 𝕩 cannot be a scalar"); if (isAtm(w)) w = m_atomUnit(w); shift_check(w, x); B f = fill_both(w, x); usz wia = IA(w); usz xia = IA(x); MAKE_MUT(r, xia); mut_init(r, el_or(TI(w,elType), TI(x,elType))); MUTG_INIT(r); int mid = wia>4; c = (c&0x3333333333333333)<<2 | (c&0xcccccccccccccccc)>>2; c = (c&0x5555555555555555)<<1 | (c&0xaaaaaaaaaaaaaaaa)>>1; return c; } extern B rt_reverse; B reverse_c1(B t, B x) { if (isAtm(x) || RNK(x)==0) thrM("⌽: Argument cannot be a unit"); usz n = *SH(x); if (n==0) return x; u8 xl = cellWidthLog(x); u8 xt = arrNewType(TY(x)); if (xl<=6 && (xl>=3 || xl==0)) { void* xv = tyany_ptr(x); B r; switch(xl) { default: UD; break; case 0: { u64* rp; r = m_bitarrc(&rp, x); u64* xp=xv; usz g = BIT_N(n); usz e = g-1; for (usz i = 0; i < g; i++) rp[i] = bit_reverse(xp[e-i]); if (n&63) { u64 sh=(-n)&63; for (usz i=0; i>sh|rp[i+1]<<(64-sh); rp[e]>>=sh; } break; } case 3: { u8* xp=xv; u8* rp = m_tyarrc(&r, 1, x, xt); for (usz i=0; i= (u64)cam) { am%= (i64)cam; if(am<0) am+= cam; } am*= csz; MAKE_MUT(r, xia); mut_init(r, TI(x,elType)); MUTG_INIT(r); mut_copyG(r, 0, x, am, xia-am); mut_copyG(r, xia-am, x, 0, am); return withFill(mut_fcd(r, x), xf); } #ifdef __BMI2__ #include #endif extern B rt_transp; B transp_c1(B t, B x) { if (RARE(isAtm(x))) return m_atomUnit(x); ur xr = RNK(x); if (xr<=1) return x; usz ia = IA(x); usz* xsh = SH(x); usz h = xsh[0]; usz w = xsh[1] * shProd(xsh, 2, xr); Arr* r; usz xi = 0; u8 xe = TI(x,elType); if (h==2) { if (xe==el_B) { B* xp = arr_bptr(x); if (xp==NULL) { HArr* xa=cpyHArr(x); x=taga(xa); xp=xa->a; } B* x0 = xp; B* x1 = x0+w; HArr_p rp = m_harrUp(ia); for (usz i=0; ia; for (usz i=0; ia; } // TODO extract this to an inline function HArr_p p = m_harrUp(ia); for(usz y=0;ySH(x)[0]) thrF("π”½βŒΎ(nβŠΈβ†‘)𝕩: Cannot modify fill with Under (%l ≑ 𝕨, %H ≑ ≒𝕩)", wi, x); return takedrop_ucw(wi, o, am, x, xr); } B drop_ucw(B t, B o, B w, B x) { if (!isF64(w)) return def_fn_ucw(t, o, w, x); i64 wi = o2i64(w); u64 am = wi<0? -wi : wi; if (isAtm(x)) x = m_vec1(x); ur xr = RNK(x); if (xr==0) xr = 1; usz cam = SH(x)[0]; if (am>cam) am = cam; return takedrop_ucw(-wi, o, cam-am, x, xr); } static B shape_uc1_t(B r, usz ia) { if (!isArr(r) || RNK(r)!=1 || IA(r)!=ia) thrM("π”½βŒΎβ₯Š: 𝔽 changed the shape of the argument"); return r; } B shape_uc1(B t, B o, B x) { if (!isArr(x) || RNK(x)==0) { usz xia = isArr(x)? IA(x) : 1; return shape_c2(t, emptyIVec(), shape_uc1_t(c1(o, shape_c1(t, x)), xia)); } usz xia = IA(x); if (RNK(x)==1) return shape_uc1_t(c1(o, x), xia); ur xr = RNK(x); ShArr* sh = ptr_inc(shObj(x)); return truncReshape(shape_uc1_t(c1(o, shape_c1(t, x)), xia), xia, xia, xr, sh); } B select_ucw(B t, B o, B w, B x); B transp_uc1(B t, B o, B x) { return transp_im(t, c1(o, transp_c1(t, x))); } B reverse_uc1(B t, B o, B x) { return reverse_c1(t, c1(o, reverse_c1(t, x))); } NOINLINE B enclose_im(B t, B x) { if (isAtm(x) || RNK(x)!=0) thrM("<⁼: Argument wasn't a rank 0 array"); B r = IGet(x, 0); dec(x); return r; } B enclose_uc1(B t, B o, B x) { return enclose_im(t, c1(o, m_atomUnit(x))); } void sfns_init() { c(BFn,bi_pick)->uc1 = pick_uc1; c(BFn,bi_reverse)->uc1 = reverse_uc1; c(BFn,bi_pick)->ucw = pick_ucw; c(BFn,bi_select)->ucw = select_ucw; // TODO move to new init fn c(BFn,bi_shape)->uc1 = shape_uc1; c(BFn,bi_transp)->uc1 = transp_uc1; c(BFn,bi_transp)->im = transp_im; c(BFn,bi_take)->ucw = take_ucw; c(BFn,bi_drop)->ucw = drop_ucw; c(BFn,bi_lt)->im = enclose_im; c(BFn,bi_lt)->uc1 = enclose_uc1; }