view vsb.c @ 0:e0bbaa717f41 draft default tip

lol J
author Jordi Gutiérrez Hermoso <jordigh@octave.org>
date Mon, 25 Nov 2013 11:56:30 -0500
parents
children
line wrap: on
line source

/* Copyright 1990-2011, Jsoftware Inc.  All rights reserved. */
/* License in license.txt.                                   */
/*                                                                         */
/* Verbs: s:                                                               */

#include "j.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */

#define BLACK           0
#define RED             1
#define ROOT            (jt->sbroot)
#define FILLFACTOR      (jt->sbfillfactor)
#define GAP             (jt->sbgap)  

static const  SBU       sentinel = {0,0,0,BLACK,0,0,0,IMIN,0,0};

/* #define TMP */
#ifdef TMP
#include <time.h>
static int              tmp_lr=0; 
static int              tmp_rr=0; 
static int              tmp_lt=0; 
static int              tmp_while=0; 
static int              tmp_node=0; 
static int              tmp_reorder=0; 
static int              tmp_moves=0; 
static int              tmp_imax=0; 
static int              tmp_rhit=0; 
static int              tmp_lhit=0;
static clock_t          clo;
static D                tickk=1.0/CLOCKS_PER_SEC;
#endif


/* implementation dependend declarations */
typedef enum {
    STATUS_OK,
    STATUS_MEM_EXHAUSTED,
    STATUS_DUPLICATE_KEY,
    STATUS_KEY_NOT_FOUND
} statusEnum;


#ifdef TMP
#define NODE(a)         (tmp_node++,a+jt->sbuv)
#else
#define NODE(a)         (a+jt->sbuv)
#endif

#define NODEM(a,b)      (jt->sbuv[a].b)
#define LEFT(a)         NODEM(a,left)
#define RIGHT(a)        NODEM(a,right)
#define ORDER(a)        NODEM(a,order)
#define INDEX(a)        NODEM(a,i)
#define LENGTH(a)       NODEM(a,n)
#define HASH(a)         NODEM(a,h)
#define COLOR(a)        NODEM(a,color)
#define DOWN(a)         NODEM(a,down)
#define UP(a)           NODEM(a,up)
#define PARENT(a)       NODEM(a,parent)
#define GRANDPARENT(a)  (PARENT(PARENT(a)))
#define ISLEFTCHILD(x)  (x ==  LEFT(PARENT(x)))
#define ISRIGHTCHILD(x) (x == RIGHT(PARENT(x)))
#define compLT(a,b)     Vcompare(jt,a,b)
#define compEQ(a,b)     ( a == b )



#ifdef TMP
static void showdepth(J jt, I node, int **ptr, I* size, I depth)
{
  if(LEFT(node) == 0) {
    (*ptr) = realloc((*ptr), sizeof(I)*((*size)+1));
    (*ptr)[(*size)++]=depth;
  }
  else             showdepth(jt,  LEFT(node), ptr, size, depth+1);

  if(RIGHT(node) == 0) {
    (*ptr) = realloc((*ptr), sizeof(I)*((*size)+1));
    (*ptr)[(*size)++]=depth;
  }
  else             showdepth(jt, RIGHT(node), ptr, size, depth+1);
}
#endif

static __inline int Vcompare(J jt,I a,I b){I m,n;SBU*u,*v;UC*s,*t;U2*p,*q;
#ifdef TMP
 tmp_lt++;
#endif
 u=a+jt->sbuv; m=u->n; s=(UC*)(jt->sbsv+u->i);
 v=b+jt->sbuv; n=v->n; t=(UC*)(jt->sbsv+v->i);
 switch((SBC2&u->flag?2:0)+(SBC2&v->flag?1:0)){
  case 0: {                                DO(MIN(m,n), if(*s!=*t)R *s<*t; ++s; ++t;);} break;
  case 1: {          q=(U2*)t;       n/=2; DO(MIN(m,n), if(*s!=*q)R *s<*q; ++s; ++q;);} break;
  case 2: {p=(U2*)s;           m/=2;       DO(MIN(m,n), if(*p!=*t)R *p<*t; ++p; ++t;);} break;
  case 3: {p=(U2*)s; q=(U2*)t; m/=2; n/=2; DO(MIN(m,n), if(*p!=*q)R *p<*q; ++p; ++q;);} 
 }
 R m<n;
}

static __inline void rotateLeft(J jt, I x) {I y;
#ifdef TMP
    tmp_lr++;
#endif
   /***************************
    *  rotate node x to left  *
    ***************************/
    y = RIGHT(x);
    if (RIGHT(x)= LEFT(y))    PARENT(LEFT(y)) = x;         /* establish x->right link  */
    if (y)                    PARENT(y)       = PARENT(x); /* establish y->parent link */
    if (PARENT(x) == 0)       ROOT            = y;
    else if (ISLEFTCHILD(x))  LEFT (PARENT(x))= y;
    else                      RIGHT(PARENT(x))= y;
    if (LEFT(y) = x)          PARENT(x)       = y;         /* link x and y             */
}


static __inline void rotateRight(J jt, I x) {I y;
#ifdef TMP
    tmp_rr++;
#endif
   /***************************
    *  rotate node x to right *
    ***************************/
    y = LEFT(x);
    if (LEFT(x)= RIGHT(y))    PARENT(RIGHT(y))= x;         /* establish x->left link   */
    if (y)                    PARENT(y)       = PARENT(x); /* establish y->parent link */
    if (PARENT(x) == 0)       ROOT            = y;
    else if (ISRIGHTCHILD(x)) RIGHT(PARENT(x))= y;
    else                      LEFT (PARENT(x))= y;
    if (RIGHT(y)= x)          PARENT(x)       = y;         /* link x and y             */
}

static __inline void insertFixup(J jt, I x) {B b;I y;

   /*************************************
    *  maintain Red-Black tree balance  *
    *  after inserting node x           *
    *************************************/

    /* check Red-Black properties                                  */
    /* the repositioning is necessary to propogate the rebalancing */
    while (x != ROOT && COLOR(PARENT(x)) == RED) {
#ifdef TMP
        tmp_while++;
#endif
        if (ISLEFTCHILD(PARENT(x))) {           /* we have a violation          */
            y = RIGHT(GRANDPARENT(x));          /* uncle                        */
            b = COLOR(y)==BLACK;                /* uncle is BLACK               */
            if (b && ISRIGHTCHILD(x)) {x=PARENT(x); rotateLeft(jt,x);}             
            COLOR(PARENT(x)) = BLACK;
            COLOR(GRANDPARENT(x)) = RED;
            if (b) rotateRight(jt, GRANDPARENT(x));
            else   {COLOR(y)=BLACK; x=GRANDPARENT(x);}
        }else {                                 /* mirror image of above code   */
            y = LEFT(GRANDPARENT(x));           /* uncle                        */
            b = COLOR(y)==BLACK;                /* uncle is BLACK               */
            if (b && ISLEFTCHILD(x)) {x=PARENT(x); rotateRight(jt,x);}
            COLOR(PARENT(x)) = BLACK;
            COLOR(GRANDPARENT(x)) = RED;
            if (b) rotateLeft(jt, GRANDPARENT(x));
            else   {COLOR(y)=BLACK; x=GRANDPARENT(x);}
    }}
    COLOR(ROOT) = BLACK;
}

static statusEnum insert(J jt, I key) {
    I current,dist,i,keep1,keep2,lorder,parent,rorder,to_the_left,to_the_right;SBU *x;
#ifdef TMP
    static I icount=0;
#endif

    if (key < 0) R STATUS_KEY_NOT_FOUND;

   /***********************************************
    *  allocate node for data and insert in tree  *
    ***********************************************/

    /* find future parent */
    current = ROOT;  /* jt-> root points to the int value of the root symbol */
    parent = to_the_left = to_the_right = 0;

    while (current != 0) {
        if (compEQ(key, current))return STATUS_DUPLICATE_KEY;
        parent = current;
        if(compLT(key, current)){to_the_right=current; current= LEFT(current);}
        else                    {to_the_left =current; current=RIGHT(current);}
    }

#ifdef TMP
    icount++;
    if (icount==10000&&0) {I corder,running;
     icount=running=corder=0;
     do {
      ORDER(running)=corder;
      corder+=FILLFACTOR;
      running=UP(running);
     } while(running);
    }
#endif

    /* get the new node */

    lorder = to_the_left  ? ORDER(to_the_left)  : 0;
    rorder = to_the_right ? ORDER(to_the_right) : lorder + 2 * FILLFACTOR;

    if(rorder-lorder<2) {    /*  if(rorder-lorder<(2*GAP)) { */
      i=0;
      /*parameter GAP is TWICE the difference in order numbers we want after re-ordering*/
      while(to_the_right&&to_the_left&&(ORDER(to_the_right)-ORDER(to_the_left))<(GAP*++i)){
       keep1=to_the_left;  to_the_left =DOWN(to_the_left);
       keep2=to_the_right; to_the_right=UP  (to_the_right);
      }

#ifdef TMP
      if(!to_the_left )UP(0)  =keep1,i++,tmp_lhit++ ;
      if(!to_the_right)DOWN(0)=keep2,i++,tmp_rhit++;
      tmp_imax=__max(i,tmp_imax);
      tmp_moves+=2*i;
      tmp_reorder++;
#else
      if(!to_the_left )UP(0)  =keep1,i++;
      if(!to_the_right)DOWN(0)=keep2,i++;
#endif

      lorder= to_the_left  ? ORDER(to_the_left ) : rorder-2*i*FILLFACTOR;
      rorder= to_the_right ? ORDER(to_the_right) : lorder+2*i*FILLFACTOR;
      dist = (rorder-lorder)/(2*i);

      while(--i) {
       to_the_left =UP  (to_the_left);  lorder+=dist; ORDER(to_the_left )=lorder;
       to_the_right=DOWN(to_the_right); rorder-=dist; ORDER(to_the_right)=rorder;
      }
    }

    x = NODE(key);
    x->parent= parent;
    x->left  = 0;
    x->right = 0;
    x->color = RED;
    x->order = (rorder+lorder)/2;
    x->up    = to_the_right; DOWN(to_the_right)=key;
    x->down  = to_the_left;  UP(to_the_left)   =key;

    /* insert node in tree */
    if     (0==parent)          ROOT         =key;
    else if(compLT(key, parent))LEFT(parent) =key;
    else                        RIGHT(parent)=key;

    insertFixup(jt, key);

    return STATUS_OK;
}

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */

static I jtsbextend(J jt,I n,C*s,UI h,I hi){A x;I c,*hv,j,p;SBU*v;
 c=jt->sbun;
 if(c==*AS(jt->sbu)){                   /* extend sbu unique symbols    */
  RZ(x=ext(1,jt->sbu)); jt->sbu=x; jt->sbuv=(SBU*)AV(x);
 }
 if(AN(jt->sbs)<n+jt->sbsn){            /* extend sbs strings           */
  GA(x,LIT,2*(n+jt->sbsn),1,0); MC(CAV(x),jt->sbsv,jt->sbsn);
  fa(jt->sbs); ra(x); jt->sbs=x; jt->sbsv=CAV(x);
 }
 if(AN(jt->sbh)<2*c){                   /* extend sbh hash table        */
  p=2*AN(jt->sbh); DO(64, if(p<=ptab[i]){p=ptab[i]; break;});
  RZ(x=apv(p,-1L,0L)); hv=AV(x); v=jt->sbuv;
  DO(c, j=v++->h%p; while(0<=hv[j])j=(1+j)%p; hv[j]=i;);
  fa(jt->sbh); ra(x); jt->sbh=x; jt->sbhv= AV(x);
  hi=h%p;                               /* new hi wrt new sbh size      */
  while(0<=hv[hi])hi=(1+hi)%p; 
 }
 R hi;
}

static SB jtsbinsert(J jt,B c2,I n,C*s,UI h,I hi){I c,m,p;SBU*u;
 c=jt->sbun;                            /* cardinality                  */
 m=jt->sbsn;                            /* existing # chars in sbs      */
 p=c2&&m%2;                             /* pad for alignment            */
 RE(hi=sbextend(n+p,s,h,hi));           /* extend global tables as req'd*/
 MC(SBSV(m+p),s,n);                     /* copy string into sbs         */
 u=SBUV(c); u->i=m+p; u->n=n; u->h=h;   /* index/length/hash            */
 u->flag=c2?SBC2:0;
 ASSERTSYS(STATUS_OK==insert(jt,c),"sbinsert");
 (jt->sbhv)[hi]=c;                      /* make sbh point to new symbol */
 ++jt->sbun;                            /* # unique symbols             */
 jt->sbsn+=n+p;                         /* # chars in sbs               */
 R(SB)c;
}    /* insert new symbol */

static SB jtsbprobe(J jt,B c2,I n,C*s){B b;C*t;I hi,hn,ui;SBU*u;UI h;
 h=c2?hic2(n,(UC*)s):hic(n,(UC*)s);
 hn=AN(jt->sbh);                        /* size of hast table           */
 hi=h%hn;                               /* index into hash table        */
 while(1){
  ui=(jt->sbhv)[hi];                    /* index into unique symbols    */
  if(0>ui)R sbinsert(c2,n,s,h,hi);      /* new symbol                   */
  u=SBUV(ui);
  if(h==u->h){                          /* old symbol, maybe            */
   t=SBSV(u->i);
   switch((c2?2:0)+(u->flag&SBC2?1:0)){
    case 1: if(n==u->n/2){C2*q=(C2*)t; b=1; DO(n,   if(s[i]!=q[i]){b=0; break;}); if(b)R(SB)ui;}
    case 2: if(n==u->n*2){C2*q=(C2*)s; b=1; DO(n/2, if(t[i]!=q[i]){b=0; break;}); if(b)R(SB)ui;} 
    case 3:
    case 0: if(n==u->n&&!memcmp(t,s,n))R(SB)ui; break;
  }}
  hi=(1+hi)%hn;                         /* next hash table index        */
}}   /* insert new symbol or get existing symbol */


static A jtsbunstr(J jt,I q,A w){A z;B c2;I i,j,m,wn;SB*zv;
 RZ(w);
 if(!AN(w))R vec(SBT,0L,0L);
 ASSERT(AT(w)&LIT+C2T,EVDOMAIN);
 ASSERT(1>=AR(w),EVRANK);
 c2=1&&AT(w)&C2T; wn=AN(w);
 if(c2){C2 c,*wv=(C2*)AV(w); 
  c=wv[q==-1?0:wn-1];
  m=0; DO(wn, if(c==wv[i])++m;);
  GA(z,SBT,m,1,0); zv=SBAV(z);
  if(q==-1){for(i=j=1;i<=wn;++i)if(c==wv[i]||i==wn){RE(*zv++=sbprobe(c2,2*(i-j),(C*)(j+wv))); j=i+1;}}
  else     {for(i=j=0;i< wn;++i)if(c==wv[i]       ){RE(*zv++=sbprobe(c2,2*(i-j),(C*)(j+wv))); j=i+1;}}
 }else{C c,*wv=CAV(w); 
  c=wv[q==-1?0:wn-1];
  m=0; DO(wn, if(c==wv[i])++m;);
  GA(z,SBT,m,1,0); zv=SBAV(z);
  if(q==-1){for(i=j=1;i<=wn;++i)if(c==wv[i]||i==wn){RE(*zv++=sbprobe(c2,i-j,j+wv)); j=i+1;}}
  else     {for(i=j=0;i< wn;++i)if(c==wv[i]       ){RE(*zv++=sbprobe(c2,i-j,j+wv)); j=i+1;}}
 }
 R z;
}    /* monad s: on leading (_1=q) or trailing (_2=q) character separated strings */

static A jtsbunlit(J jt,C cx,A w){A z;B c2;I i,m,wc,wr,*ws;SB*zv;
 RZ(w);
 ASSERT(!AN(w)||AT(w)&LIT+C2T,EVDOMAIN);
 ASSERT(1<AR(w),EVRANK);
 c2=1&&AT(w)&C2T; wr=AR(w); ws=AS(w); wc=ws[wr-1];
 RE(m=wc?AN(w)/wc:prod(wr-1,ws));
 GA(z,SBT,m,wr-1,ws); zv=SBAV(z);
 if(!wc)memset(zv,C0,m*sizeof(SB));
 else if(c2){C2 c=(C2)cx,*s,*wv=(C2*)AV(w);
  for(i=0;i<m;++i){
   s=wc+wv; DO(wc, if(c!=*--s)break;);   /* exclude trailing "blanks"    */
   RE(*zv++=sbprobe(c2,2*((c!=*s)+s-wv),(C*)wv));
   wv+=wc;
 }}else{C c=cx,*s,*wv=CAV(w);
  for(i=0;i<m;++i){
   s=wc+wv; DO(wc, if(c!=*--s)break;);   /* exclude trailing "blanks"    */
   RE(*zv++=sbprobe(c2,(c!=*s)+s-wv,wv));
   wv+=wc;
 }}
 R z;
}    /* each row of literal array w less the trailing "blanks" is a symbol */

static F1(jtsbunbox){A*wv,x,z;B c2;I i,m,n,wd;SB*zv;
 RZ(w);
 ASSERT(!AN(w)||BOX&AT(w),EVDOMAIN);
 m=AN(w); wv=AAV(w); wd=(I)w*ARELATIVE(w);
 GA(z,SBT,m,AR(w),AS(w)); zv=SBAV(z);
 for(i=0;i<m;++i){
  x=WVR(i); n=AN(x); c2=1&&AT(x)&C2T;
  ASSERT(!n||AT(x)&LIT+C2T,EVDOMAIN);
  ASSERT(1>=AR(x),EVRANK);
  RE(*zv++=sbprobe(c2,c2?n+n:n,CAV(x)));
 }
 R z;
}    /* each element of boxed array w is a string */

static F1(jtsbunind){A z;I j,n,*zv;
 RZ(z=cvt(INT,w));
 zv=AV(z); n=jt->sbun;
 DO(AN(w), j=*zv++; ASSERT(0<=j&&j<n,EVINDEX););
 AT(z)=SBT;
 R z;
}    /* w is a numeric array of symbol indices */

#ifdef TMP
F1(jtsb1){
 A abc;
 clo=clock();
 RZ(w);
 switch(AT(w)){
  default:  ASSERT(0,EVDOMAIN);
  case C2T:
  case LIT: abc=(1>=AR(w)?sbunstr(-1L,w):sbunlit(' ',w)); break;
  case BOX: abc=(sbunbox(w));
 }  
 clo-=clock();
 R abc;
}
#else
F1(jtsb1){
 RZ(w);
 switch(AT(w)){
  default:  ASSERT(0,EVDOMAIN);
  case C2T:
  case LIT: R 1>=AR(w)?sbunstr(-1L,w):sbunlit(' ',w);
  case BOX: R sbunbox(w);
}}   /* monad s: main control */
#endif


F1(jtsborder){A z;I n,*zv;SB*v;
 RZ(w);
 n=AN(w); v=SBAV(w);
 ASSERT(!n||SBT&AT(w),EVDOMAIN);
 GA(z,INT,n,AR(w),AS(w)); zv=AV(z);
 DO(n, *zv++=SBUV(*v++)->order;);
 R z;
}    /* order numbers for symbol array w */

static F1(jtsbbox){A z,*zv;C*s;I n;SB*v;SBU*u;
 RZ(w);
 n=AN(w); v=SBAV(w);
 ASSERT(!n||SBT&AT(w),EVDOMAIN);
 GA(z,BOX,n,AR(w),AS(w)); zv=AAV(z);
 DO(n, u=SBUV(*v++); s=SBSV(u->i); RZ(*zv++=SBC2&u->flag?vec(C2T,u->n/2,s):str(u->n,s)););
 R z;
}    /* boxed strings for symbol array w */

#define C2FSB(zv,u,q,m,c)  \
 {C*s=SBSV(u->i);I k=u->n;                                         \
  if(SBC2&u->flag){MC(zv,s,k); zv+=k/=2;}else DO(k, *zv++=*s++;);  \
  if(2==q)*zv++=c; else if(3==q)DO(m-k, *zv++=c;);                 \
 }

static A jtsbstr(J jt,I q,A w){A z;B c2=0;C c;I m,n;SB*v,*v0;SBU*u;
 RZ(w);
 m=n=AN(w); v=v0=SBAV(w); c=1==q?'`':C0;
 ASSERT(!n||SBT&AT(w),EVDOMAIN);
 DO(n, u=SBUV(*v++); if(u->flag&SBC2){c2=1; m+=u->n/2;}else m+=u->n;); 
 v=v0; 
 GA(z,c2?C2T:LIT,m,1,0);
 if(c2){C2*zv;
  zv=(C2*)AV(z); 
  if(1==q)*zv++=c;
  DO(n-1, u=SBUV(*v++); C2FSB(zv,u,2,0,c););
  if(n){  u=SBUV(*v++); C2FSB(zv,u,q,0,c);}
 }else{C*zv;
  zv=CAV(z); 
  if(1==q)*zv++=c;
  DO(n-1, u=SBUV(*v++); MC(zv,SBSV(u->i),u->n); zv+=u->n; *zv++=c;); 
  if(n){  u=SBUV(*v++); MC(zv,SBSV(u->i),u->n); zv+=u->n; if(2==q)*zv=c;}
 }
 R z;
}    /* leading (1=q) or trailing (2=q) separated string for symbol array w */

static A jtsblit(J jt,C c,A w){A z;B c2=0;I k,m=0,n;SB*v,*v0;SBU*u;
 RZ(w);
 n=AN(w); v=v0=SBAV(w);
 ASSERT(!n||SBT&AT(w),EVDOMAIN);
 DO(n, u=SBUV(*v++); k=u->n; if(u->flag&SBC2){c2=1; k/=2;} if(m<k)m=k;); 
 v=v0;
 GA(z,c2?C2T:LIT,n*m,1+AR(w),AS(w)); *(AR(w)+AS(z))=m;
 if(c2){C2*zv=(C2*)AV(z); DO(n, u=SBUV(*v++); C2FSB(zv,u,3,m,c););}
 else  {C*zv=CAV(z); memset(zv,c,n*m); DO(n, u=SBUV(*v++); MC(zv,SBSV(u->i),u->n); zv+=m;);}
 R z;
}    /* literal array for symbol array w padded with c */


static F1(jtsbhashstat){A z;I j,k,n,p,*zv;SBU*v;
 n=jt->sbun; v=jt->sbuv; p=AN(jt->sbh);
 GA(z,INT,n,1,0); zv=AV(z);
 DO(n, j=v++->h%p; k=1; while(i!=(jt->sbhv)[j]){j=(j+1)%p; ++k;} *zv++=k;);
 R z;
}    /* # queries in hash table for each unique symbol */

static A jtsbcheck1(J jt,A una,A sna,A u,A s,A h,A roota,A ff,A gp){PROLOG;A x,*xv,y;
     B b,*dnv,*lfv,*rtv,*upv;C*ptv,*sv;I c,f,g,hn,*hv,i,j,r,sn,un,*yv;SBU*uv,*v;
 RZ(una&&sna&&u&&s&&h);
 ASSERTD(!AR(una),"c atom");            /* cardinality   */
 ASSERTD(INT&AT(una),"c integer");
 c=*AV(una);
 ASSERTD(0<=c,"c non-negative");
 ASSERTD(!AR(sna),"sn atom");           /* string length */
 ASSERTD(INT&AT(sna),"sn integer");
 sn=*AV(sna); 
 ASSERTD(0<=sn,"sn non-negative");      /* root          */
 ASSERTD(!AR(roota),"root atom"); 
 ASSERTD(INT&AT(roota),"root integer");
 r=*AV(roota);
 ASSERTD(0<=r,"root non-negative");
 ASSERTD(r<c,"root bounded by c");
 ASSERTD(!AR(ff),"ff atom");            /* fill factor   */
 ASSERTD(INT&AT(ff),"ff integer");
 f=*AV(ff);
 ASSERTD(0<=f,"ff non-negative");
 ASSERTD(!AR(gp),"gap atom");           /* gap           */
 ASSERTD(INT&AT(gp),"gap integer");
 g=*AV(gp);
 ASSERTD(0<=g,"gap non-negative");
 ASSERTD(g<f,"gap bounded by ff");
 sv=CAV(s);
 un=*AS(u); uv=(SBU*)AV(u);
 hn= AN(h); hv=AV(h);
 ASSERTD(2==AR(u),"u matrix");
 ASSERTD(INT&AT(u),"u integer");
 ASSERTD(*(1+AS(u))==sizeof(SBU)/SZI,"u #columns");
 ASSERTD(c<=un,"c bounded by #u");
 ASSERTD(1==AR(s),"s vector");
 ASSERTD(LIT&AT(s),"s literal");
 ASSERTD(sn<=AN(s),"sn bounded by #s");
 ASSERTD(1==AR(h),"h vector");
 ASSERTD(INT&AT(h),"h integer");
 ASSERTD(c<=AN(h),"c bounded by #h");
 ASSERTD(equ(vec(INT,1L,&hn),factor(sc(hn))),"#h prime");
 b=0; DO(AN(h), j=hv[i]; if(-1==j)b=1; else ASSERTD(0<=j&&j<c,"h index"););
 ASSERTD(b,"h full");
 GA(x,B01,c,1,0); lfv=BAV(x); memset(lfv,C0,c);
 GA(x,B01,c,1,0); rtv=BAV(x); memset(rtv,C0,c);
 GA(x,B01,c,1,0); dnv=BAV(x); memset(dnv,C0,c);
 GA(x,B01,c,1,0); upv=BAV(x); memset(upv,C0,c);
 GA(x,LIT,c,1,0); ptv=CAV(x); memset(ptv,C0,c); ptv[0]=1;
 GA(x,BOX,c,1,0); xv=AAV(x); RZ(xv[0]=str(uv->n,sv+uv->i));
 GA(y,INT,c,1,0); yv= AV(y); yv[0]=uv->order;
 for(i=1,v=1+uv;i<c;++i,++v){B c2;I ord,vi,vn;UC*vc;UI k;
  c2=1&&v->flag&SBC2;
  vi=v->i;
  vn=v->n;
  vc=(UC*)(sv+vi);
  ASSERTD(0<=vi&&vi<=sn,"u index");
  ASSERTD(!(c2&&vi%2),"u index alignment");
  ASSERTD(0<=vn&&!(c2&&vn%2),"u length");
  ASSERTD(sn>=vi+vn,"u index/length");
  k=(c2?hic2:hic)(vn,vc);
  ASSERTD(k==v->h,"u hash");
  j=k%hn; while(i!=hv[j]&&0<=hv[j])j=(1+j)%hn;
  ASSERTD(i==hv[j],"u/h mismatch");
  ASSERTD(BLACK==v->color||RED==v->color,"u color");
  RZ(xv[i]=c2?vec(C2T,vn/2,vc):str(vn,vc));
  yv[i]=ord=v->order;
  j=v->parent; ASSERTD(    0<=j&&j<c&&2>=++ptv[j],"u parent");                        
  j=v->left;   ASSERTD(!j||0<=j&&j<c&&1>=++lfv[j]&&     ord>(j+uv)->order ,"u left"       );
  j=v->right;  ASSERTD(!j||0<=j&&j<c&&1>=++rtv[j]&&     ord<(j+uv)->order ,"u right"      );
  j=v->down;   ASSERTD(    0<=j&&j<c&&1>=++dnv[j]&&(!j||ord>(j+uv)->order),"u predecessor");
  j=v->up;     ASSERTD(    0<=j&&j<c&&1>=++upv[j]&&(!j||ord<(j+uv)->order),"u successor"  );
 }
 ASSERTD(equ(grade1(x),grade1(y)),"u order");
 EPILOG(one);
}

static F1(jtsbcheck){R sbcheck1(sc(jt->sbun),sc(jt->sbsn),jt->sbu,jt->sbs,jt->sbh,sc(ROOT),sc(FILLFACTOR),sc(GAP));}

static F1(jtsbsetdata){A h,s,u,*wv,x;I wd;
 RZ(w);
 ASSERTD(BOX&AT(w),"arg type");
 ASSERTD(1==AR(w), "arg rank");
 ASSERTD(8==AN(w), "arg length");
 wv=AAV(w); wd=(I)w*ARELATIVE(w);
 RZ(sbcheck1(WVR(0),WVR(1),WVR(2),WVR(3),WVR(4),WVR(5),WVR(6),WVR(7)));
 jt->sbun=*AV(WVR(0));
 jt->sbsn=*AV(WVR(1));
 RZ(x=ra(ca(WVR(2)))); u=jt->sbu; jt->sbu=x; jt->sbuv=(SBU*)AV(x);
 RZ(x=ra(ca(WVR(3)))); s=jt->sbs; jt->sbs=x; jt->sbsv=     CAV(x);
 RZ(x=ra(ca(WVR(4)))); h=jt->sbh; jt->sbh=x; jt->sbhv=      AV(x);
 ROOT      =*AV(WVR(5));
 FILLFACTOR=*AV(WVR(6));
 GAP       =*AV(WVR(7));
 fa(u); fa(s); fa(h);
 R one;
}

static F1(jtsbgetdata){A z,*zv;
 GA(z,BOX,8,1,0); zv=AAV(z);
 RZ(zv[0]=sc(jt->sbun));
 RZ(zv[1]=sc(jt->sbsn));
 RZ(zv[2]=ca(jt->sbu));
 RZ(zv[3]=ca(jt->sbs));
 RZ(zv[4]=ca(jt->sbh));
 RZ(zv[5]=sc(ROOT));
 RZ(zv[6]=sc(FILLFACTOR));
 RZ(zv[7]=sc(GAP));
 R z;
}

F2(jtsb2){A z;I j,k,n;
#ifdef TMP
 I*zv;
#endif
 RZ(a&&w);
 RE(j=i0(a)); n=AN(w);
 ASSERT(!(1<=j&&j<=7)||!n||SBT&AT(w),EVDOMAIN);
 switch(j){
  default:   ASSERT(0,EVDOMAIN);
  case 0:
   RE(k=i0(w));
   switch(k){
    default: ASSERT(0,EVDOMAIN);
    case 0:  R sc(jt->sbun);
    case 1:  R sc(jt->sbsn);
    case 2:  R ca(jt->sbu);
    case 3:  R ca(jt->sbs);
    case 4:  R ca(jt->sbh);
    case 5:  R sc(ROOT);
    case 6:  R sc(FILLFACTOR);
    case 7:  R sc(GAP);
    case 10: R sbgetdata(zero);
    case 11: R sbcheck(zero);
    case 12: R sbhashstat(zero);
   }
  case  1:   R sbstr(1L,w);
  case -1:   R sbunstr(-1L,w);
  case  2:   R sbstr(2L,w);
  case -2:   R sbunstr(-2L,w);
  case  3:   R sblit(C0,w);
  case -3:   R sbunlit(C0,w);
  case  4:   R sblit(' ',w);
  case -4:   R sbunlit(' ',w);
  case  5:   R sbbox(w);
  case -5:   R sbunbox(w);
  case  6:   RZ(z=ca(w)); AT(z)=INT; R z;
  case -6:   R sbunind(w);
  case  7:   R sborder(w);
  case 10:   R sbsetdata(w);
  case 16:   GAP = 4;                                       R sc(GAP);
  case 17:   GAP++;         ASSERT(FILLFACTOR>GAP,EVLIMIT); R sc(GAP);
  case 18:   GAP--;                                         R sc(GAP);
  case 19:   FILLFACTOR=1024;                               R sc(FILLFACTOR);
  case 20:   FILLFACTOR*=2;                                 R sc(FILLFACTOR);
  case 21:   FILLFACTOR/=2; ASSERT(FILLFACTOR>GAP,EVLIMIT); R sc(FILLFACTOR);
#ifdef TMP
  case 22:
    GA(z,INT,10,1,0); zv=AV(z);
    zv[0] = tmp_lr      = 0;
    zv[1] = tmp_rr      = 0;
    zv[2] = tmp_lt      = 0;
    zv[3] = tmp_while   = 0;
    zv[4] = tmp_node    = 0;
    zv[5] = tmp_reorder = 0;
    zv[6] = tmp_moves   = 0;
    zv[7] = tmp_imax    = 0;
    zv[8] = tmp_lhit;
    zv[9] = tmp_rhit;
    R z;
  case 23:
    GA(z,INT,10,1,0);
    zv[0] = tmp_lr;
    zv[1] = tmp_rr;
    zv[2] = tmp_lt;
    zv[3] = tmp_while;
    zv[4] = tmp_node;
    zv[5] = tmp_reorder;
    zv[6] = tmp_moves;
    zv[7] = tmp_imax;
    zv[8] = tmp_lhit;
    zv[9] = tmp_rhit;
    R z;
  case 24:   R sc((I)clo);
  case 25:   R scf(tickk);
#endif
}}


B jtsbtypeinit(J jt){A x;I c=sizeof(SBU)/SZI,s[2];
 s[0]=2000; s[1]=c;
 GA(x,LIT,20000,1,0);       jt->sbs=x; jt->sbsv=     CAV(x); jt->sbsn=0;
 RZ(x=apv(ptab[5],-1L,0L)); jt->sbh=x; jt->sbhv=      AV(x);
 GA(x,INT,*s*c,2,s);        jt->sbu=x; jt->sbuv=(SBU*)AV(x);
 GAP=15;                /* TWICE the difference in order numbers we want after re-ordering */
 FILLFACTOR=1024;
 ROOT=0;                /* initialize binary tree; initialize the empty symbol (used as fill) */
 jt->sbuv[0]=sentinel;
 jt->sbun=1;
 *jt->sbhv=0;
 R 1;
}    /* initialize global data for SBT datatype */