********************************************************************
function: utility functions for loading .vqh and .vqd files
- last mod: $Id: bookutil.c,v 1.10 2000/02/16 16:18:33 xiphmont Exp $
+ last mod: $Id: bookutil.c,v 1.11 2000/02/21 01:12:53 xiphmont Exp $
********************************************************************/
return(b);
}
+
+/* the new version! we have ptr0[0] distinct trees in the auxiliary
+ encoding structure. Find the best match in each one, choosing the
+ best global match */
+
int codebook_entry(codebook *b,double *val){
const static_codebook *c=b->c;
encode_aux *t=c->encode_tree;
- int ptr=0,k;
-
+ int trees=t->ptr0[0];
+
/*{
brute force
double this,best=_dist(c->dim,val,b->valuelist);
}*/
double *n=alloca(c->dim*sizeof(double));
-
- while(1){
- double C=0.;
- double *p=b->valuelist+t->p[ptr];
- double *q=b->valuelist+t->q[ptr];
-
- for(k=0;k<c->dim;k++){
- n[k]=p[k]-q[k];
- C-=(p[k]+q[k])*n[k];
+ double bestmetric;
+ double best=-1;
+ while(trees>0){
+ int ptr=t->ptr0[--trees],k;
+
+ while(1){
+ double C=0.;
+ double *p=b->valuelist+t->p[ptr];
+ double *q=b->valuelist+t->q[ptr];
+
+ for(k=0;k<c->dim;k++){
+ n[k]=p[k]-q[k];
+ C-=(p[k]+q[k])*n[k];
+ }
+ C/=2.;
+
+ for(k=0;k<c->dim;k++)
+ C+=n[k]*val[k];
+
+ if(C>0.) /* in A */
+ ptr= -t->ptr0[ptr];
+ else /* in B */
+ ptr= -t->ptr1[ptr];
+ if(ptr<=0)break;
+ }
+
+ {
+ double dist=0.;
+ for(k=0;k<c->dim;k++){
+ double one=val[k]-(b->valuelist-ptr*c->dim)[k];
+ dist+=one*one;
+ }
+ if(best==-1 || dist<bestmetric){
+ best=-ptr;
+ bestmetric=dist;
+ }
}
- C/=2.;
-
- for(k=0;k<c->dim;k++)
- C+=n[k]*val[k];
-
- if(C>0.) /* in A */
- ptr= -t->ptr0[ptr];
- else /* in B */
- ptr= -t->ptr1[ptr];
- if(ptr<=0)break;
}
- return(-ptr);
+
+ return(best);
}
/* 24 bit float (not IEEE; nonnormalized mantissa +
}
}
+void build_tree_from_lengths(int vals, long *hist, long *lengths){
+ int i,j;
+ long *membership=malloc(vals*sizeof(long));
+
+ for(i=0;i<vals;i++)membership[i]=i;
+
+ /* find codeword lengths */
+ /* much more elegant means exist. Brute force n^2, minimum thought */
+ for(i=vals;i>1;i--){
+ int first=-1,second=-1;
+ long least=-1;
+
+ spinnit("building... ",i);
+
+ /* find the two nodes to join */
+ for(j=0;j<vals;j++)
+ if(least==-1 || hist[j]<least){
+ least=hist[j];
+ first=membership[j];
+ }
+ least=-1;
+ for(j=0;j<vals;j++)
+ if((least==-1 || hist[j]<least) && membership[j]!=first){
+ least=hist[j];
+ second=membership[j];
+ }
+ if(first==-1 || second==-1){
+ fprintf(stderr,"huffman fault; no free branch\n");
+ exit(1);
+ }
+
+ /* join them */
+ least=hist[first]+hist[second];
+ for(j=0;j<vals;j++)
+ if(membership[j]==first || membership[j]==second){
+ membership[j]=first;
+ hist[j]=least;
+ lengths[j]++;
+ }
+ }
+ for(i=0;i<vals-1;i++)
+ if(membership[i]!=membership[i+1]){
+ fprintf(stderr,"huffman fault; failed to build single tree\n");
+ exit(1);
+ }
+
+ free(membership);
+}
********************************************************************
function: utility functions for loading .vqh and .vqd files
- last mod: $Id: bookutil.h,v 1.3 2000/01/10 10:42:02 xiphmont Exp $
+ last mod: $Id: bookutil.h,v 1.4 2000/02/21 01:12:54 xiphmont Exp $
********************************************************************/
extern void spinnit(char *s,int n);
extern long float24_pack(double val);
extern double float24_unpack(long val);
+extern void build_tree_from_lengths(int vals, long *hist, long *lengths);
#endif
********************************************************************
function: generic euclidian distance metric for VQ codebooks
- last mod: $Id: genericdata.c,v 1.3 2000/02/16 16:18:35 xiphmont Exp $
+ last mod: $Id: genericdata.c,v 1.4 2000/02/21 01:12:55 xiphmont Exp $
********************************************************************/
int vqext_aux=0;
quant_meta q={0,0,0,0}; /* non sequence data; each scalar
independent */
-double vqext_mindist=0.;
void vqext_quantize(vqgen *v,quant_meta *q){
vqgen_quantize(v,q);
********************************************************************
function: metrics and quantization code for LSP VQ codebooks
- last mod: $Id: lspdata.c,v 1.10 2000/02/16 16:18:36 xiphmont Exp $
+ last mod: $Id: lspdata.c,v 1.11 2000/02/21 01:12:56 xiphmont Exp $
********************************************************************/
char *vqext_booktype="LSPdata";
quant_meta q={0,0,0,1}; /* set sequence data */
int vqext_aux=1;
-double vqext_mindist=0.;
void vqext_quantize(vqgen *v,quant_meta *q){
vqgen_quantize(v,q);
********************************************************************
function: metrics and quantization code for residue VQ codebooks
- last mod: $Id: residuedata.c,v 1.1 2000/02/16 16:18:38 xiphmont Exp $
+ last mod: $Id: residuedata.c,v 1.2 2000/02/21 01:12:57 xiphmont Exp $
********************************************************************/
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
+#include <string.h>
#include "vqgen.h"
#include "bookutil.h"
#include "vqext.h"
quant_meta q={0,0,0,0}; /* set sequence data */
int vqext_aux=0;
-double vqext_mindist=.7; /* minimum desired cell radius */
+static double *quant_save=NULL;
/* LSP training metric. We weight error proportional to distance
*between* LSP vector values. The idea of this metric is not to set
}
/* quantize aligned on unit boundaries */
-void vqext_quantize(vqgen *v,quant_meta *q){
- int i,j,k;
- double min,max,delta=1.;
- int vals=(1<<q->quant);
-
- min=max=_now(v,0)[0];
- for(i=0;i<v->entries;i++)
- for(j=0;j<v->elements;j++){
- if(max<_now(v,i)[j])max=_now(v,i)[j];
- if(min>_now(v,i)[j])min=_now(v,i)[j];
- }
- /* roll the delta */
+static double _delta(double min,double max,int bits){
+ double delta=1.;
+ int vals=(1<<bits);
while(1){
double qmax=rint(max/delta);
double qmin=rint(min/delta);
delta*=.5;
}else{
min=qmin*delta;
- q->min=float24_pack(qmin*delta);
- q->delta=float24_pack(delta);
break;
}
}
+ return(delta);
+}
- for(j=0;j<v->entries;j++){
- for(k=0;k<v->elements;k++){
+void vqext_quantize(vqgen *v,quant_meta *q){
+ int i,j,k;
+ long dim=v->elements;
+ long n=v->entries;
+ double min,vals,delta=1.;
+ double *test=alloca(sizeof(double)*dim);
+ int moved=0;
+
+ min=-((1<<q->quant)/2-1);
+ vals=1<<q->quant;
+ q->min=float24_pack(rint(min/delta)*delta);
+ q->delta=float24_pack(delta);
+
+ /* allow movement only to unoccupied coordinates on the coarse grid */
+ for(j=0;j<n;j++){
+ for(k=0;k<dim;k++){
double val=_now(v,j)[k];
- double now=rint((val-min)/delta);
- _now(v,j)[k]=now;
+ val=rint((val-min)/delta);
+ if(val<0)val=0;
+ if(val>=vals)val=vals-1;
+ test[k]=val;
+ }
+ /* allow move only if unoccupied */
+ if(quant_save){
+ for(k=0;k<n;k++)
+ if(j!=k && memcmp(test,quant_save+dim*k,dim*sizeof(double))==0)
+ break;
+ if(k==n){
+ if(memcmp(test,quant_save+dim*j,dim*sizeof(double)))
+ moved++;
+ memcpy(quant_save+dim*j,test,sizeof(double)*dim);
+ }
+ }else{
+ memcpy(_now(v,j),test,sizeof(double)*dim);
}
}
+
+ if(quant_save){
+ memcpy(_now(v,0),quant_save,sizeof(double)*dim*n);
+ fprintf(stderr,"cells shifted this iteration: %d\n",moved);
+ }
}
/* this should probably go to a x^6/4 error metric */
vqgen_addpoint(v,b+start,NULL);
}
+/* need to reseed because of the coarse quantization we tend to use on
+ residuals (which causes lots & lots of dupes) */
void vqext_preprocess(vqgen *v){
+ long i,j,k,l,min,max;
+ double *test=alloca(sizeof(double)*v->elements);
+
+ vqext_quantize(v,&q);
+ vqgen_unquantize(v,&q);
+
+ /* if there are any dupes, reseed */
+ for(k=0;k<v->entries;k++){
+ for(l=0;l<k;l++){
+ if(memcmp(_now(v,k),_now(v,l),sizeof(double)*v->elements)==0)
+ break;
+ }
+ if(l<k)break;
+ }
+
+ if(k<v->entries){
+ fprintf(stderr,"reseeding with quantization....\n");
+ min=-((1<<q.quant)/2-1);
+ max=min+(1<<q.quant)-1;
+
+ /* seed the inputs to input points, but points on unit boundaries,
+ ignoring quantbits for now, making sure each seed is unique */
+
+ for(i=0,j=0;i<v->points && j<v->entries;i++){
+ for(k=0;k<v->elements;k++){
+ test[k]=rint(_point(v,i)[k]);
+ if(test[k]<min)test[k]=min;
+ if(test[k]>max)test[k]=max;
+ }
+
+ for(l=0;l<j;l++){
+ for(k=0;k<v->elements;k++)
+ if(test[k]!=_now(v,l)[k])
+ break;
+ if(k==v->elements)break;
+ }
+ if(l==j){
+ memcpy(_now(v,j),test,v->elements*sizeof(double));
+ j++;
+ }
+ }
+
+ if(j<v->elements){
+ fprintf(stderr,"Not enough unique entries after prequantization\n");
+ exit(1);
+ }
+ }
+ vqext_quantize(v,&q);
+ quant_save=malloc(sizeof(double)*v->elements*v->entries);
+ memcpy(quant_save,_now(v,0),sizeof(double)*v->elements*v->entries);
+ vqgen_unquantize(v,&q);
+
}
********************************************************************
function: utility main for training codebooks
- last mod: $Id: train.c,v 1.15 2000/02/16 16:18:38 xiphmont Exp $
+ last mod: $Id: train.c,v 1.16 2000/02/21 01:12:58 xiphmont Exp $
********************************************************************/
"options: -p[arams] <entries,dim,quant>\n"
" -s[ubvector] <start[,num]>\n"
" -e[rror] <desired_error>\n"
- " -i[terations] <maxiterations>\n\n"
+ " -i[terations] <maxiterations>\n"
+ " -d[istance] desired minimum cell radius from midpoint\n\n"
"examples:\n"
" train a new codebook to 1%% tolerance on datafile 'foo':\n"
" xxxvqtrain book -p 256,6,8 -e .01 foo\n"
int entries=-1,dim=-1;
int start=0,num=-1;
- double desired=.05;
+ double desired=.05,mindist=0.;
int iter=1000;
FILE *out=NULL;
exit(1);
}
- vqgen_init(&v,dim,vqext_aux,entries,vqext_mindist,
+ vqgen_init(&v,dim,vqext_aux,entries,mindist,
vqext_metric,vqext_weight);
init=1;
if(sscanf(argv[1],"%lf",&desired)!=1)
goto syner;
break;
+ case 'd':
+ if(sscanf(argv[1],"%lf",&mindist)!=1)
+ goto syner;
+ break;
case 'i':
if(sscanf(argv[1],"%d",&iter)!=1)
goto syner;
fprintf(stderr,"-p required when training a new set\n");
exit(1);
}
- vqgen_init(&v,dim,vqext_aux,entries,vqext_mindist,
+ vqgen_init(&v,dim,vqext_aux,entries,mindist,
vqext_metric,vqext_weight);
init=1;
}
********************************************************************
function: prototypes for extermal metrics specific to data type
- last mod: $Id: vqext.h,v 1.7 2000/02/16 16:18:39 xiphmont Exp $
+ last mod: $Id: vqext.h,v 1.8 2000/02/21 01:12:59 xiphmont Exp $
********************************************************************/
extern char *vqext_booktype;
extern quant_meta q;
extern int vqext_aux;
-extern double vqext_mindist;
extern double vqext_metric(vqgen *v,double *e, double *p);
extern double *vqext_weight(vqgen *v,double *p);
********************************************************************
function: train a VQ codebook
- last mod: $Id: vqgen.c,v 1.29 2000/02/16 16:18:40 xiphmont Exp $
+ last mod: $Id: vqgen.c,v 1.30 2000/02/21 01:13:00 xiphmont Exp $
********************************************************************/
#endif
}
- fprintf(stderr,"cell diameter: %.03g::%.03g::%.03g (%d unused/%d dup)\n",
+ fprintf(stderr,"cell diameter: %.03g::%.03g::%.03g (%ld unused/%ld dup)\n",
min,mean/acc,max,unused,dup);
#ifdef NOISY
********************************************************************
function: build a VQ codebook
- last mod: $Id: vqgen.h,v 1.11 2000/02/16 16:18:41 xiphmont Exp $
+ last mod: $Id: vqgen.h,v 1.12 2000/02/21 01:13:01 xiphmont Exp $
********************************************************************/
extern double vqgen_iterate(vqgen *v);
extern void vqgen_unquantize(vqgen *v,quant_meta *q);
extern void vqgen_quantize(vqgen *v,quant_meta *q);
+extern void vqgen_cellmetric(vqgen *v);
#endif
********************************************************************
function: build a VQ codebook and the encoding decision 'tree'
- last mod: $Id: vqsplit.c,v 1.16 2000/02/16 16:18:42 xiphmont Exp $
+ last mod: $Id: vqsplit.c,v 1.17 2000/02/21 01:13:02 xiphmont Exp $
********************************************************************/
}
void vqsp_book(vqgen *v, codebook *b, long *quantlist){
- long *entryindex=malloc(sizeof(long)*v->entries);
- long *pointindex=malloc(sizeof(long)*v->points);
long i,j;
static_codebook *c=(static_codebook *)b->c;
encode_aux *t;
}
}
- fprintf(stderr,"Building a book with %d unique entries...\n",v->entries);
+ fprintf(stderr,"Building a book with %ld unique entries...\n",v->entries);
+
+ /* We don't always generate a single tree; it tends to be large. We
+ trade off by splitting the cell and point sets into n totally
+ non-overlapping subsets (where the decision tree tends to have
+ substantial overlap in hyperplane splits) and generating a tree
+ for each. Because the tree depth is O(n) o(lg(n)), the smaller
+ data set will get us much better storage usage for worst cases
+ (and we will run toward worst case) */
- for(i=0;i<v->entries;i++)entryindex[i]=i;
- for(i=0;i<v->points;i++)pointindex[i]=i;
-
- c->dim=v->elements;
- c->entries=v->entries;
- c->lengthlist=calloc(c->entries,sizeof(long));
-
- t->alloc=4096;
- t->ptr0=malloc(sizeof(long)*t->alloc);
- t->ptr1=malloc(sizeof(long)*t->alloc);
- t->p=malloc(sizeof(long)*t->alloc);
- t->q=malloc(sizeof(long)*t->alloc);
-
- /* generate the encoding decision heirarchy */
{
- long pointsofar=0;
- fprintf(stderr,"Total leaves: %d \n",
- lp_split(v,b,entryindex,v->entries,
- pointindex,v->points,0,&pointsofar));
- }
+ /* split data set... */
+ int trees=v->entries/(32+log(v->entries)/log(2)*2)+1; /* a guess */
+ long *entrymap=malloc(v->entries*sizeof(long *));
+ long **entryindex=malloc(trees*sizeof(long *));
+ long *entries=calloc(trees,sizeof(long));
+ long *pointmap=malloc(v->points*sizeof(long));
+ long desired=(v->entries/trees)+1;
+ int booknum;
+ long pointssofar=0;
+
+ for(i=0;i<trees;i++)entryindex[i]=calloc(desired,sizeof(long));
- free(entryindex);
- free(pointindex);
+ fprintf(stderr,"Prepartitioning into %d trees...\n",trees);
- fprintf(stderr,"Paring and rerouting redundant branches:\n");
- /* The tree is likely big and redundant. Pare and reroute branches */
- {
- int changedflag=1;
+ /* map points to cells */
+ for(i=0;i<v->points;i++){
+ double *ppt=_point(v,i);
+ double firstmetric=_dist(v,_now(v,0),ppt);
+ long firstentry=0;
+
+ if(!(i&0xff))spinnit("parititoning... ",v->points-i);
- while(changedflag){
- changedflag=0;
+ for(j=0;j<v->entries;j++){
+ double thismetric=_dist(v,_now(v,j),ppt);
+ if(thismetric<firstmetric){
+ firstmetric=thismetric;
+ firstentry=j;
+ }
+ }
+ pointmap[i]=firstentry;
+ }
- fprintf(stderr,"\t...");
- /* span the tree node by node; list unique decision nodes and
- short circuit redundant branches */
+ /* this could be more effective; long, narrow slices are probably
+ better than these porous blobs */
- for(i=0;i<t->aux;){
- int k;
+ for(j=0;j<v->entries;j++){
+ int choice=-1;
+ for(i=0;i<trees;i++){
+ if(entries[i]<desired){ /* still has open space */
+ choice=i;
+ break;
+ }
+ }
- /* check list of unique decisions */
- for(j=0;j<i;j++)
- if(_node_eq(t,i,j))break;
+ entryindex[choice][entries[choice]++]=j;
+ entrymap[j]=choice;
+ }
- if(j<i){
- /* a redundant entry; find all higher nodes referencing it and
- short circuit them to the previously noted unique entry */
- changedflag=1;
- for(k=0;k<t->aux;k++){
- if(t->ptr0[k]==-i)t->ptr0[k]=-j;
- if(t->ptr1[k]==-i)t->ptr1[k]=-j;
- }
+
+ /* we overload the static codebook ptrs just a bit... the first
+ <tree> ptr0s just point to the starting point in the struct for
+ that tree. The first ptr0 points to the first tree at offset
+ <tree>, and thus can be used as the tree count. Clever but
+ totally opaque :-( . */
+
+ t->alloc=4096;
+ t->ptr0=malloc(sizeof(long)*t->alloc);
+ t->ptr1=malloc(sizeof(long)*t->alloc);
+ t->p=malloc(sizeof(long)*t->alloc);
+ t->q=malloc(sizeof(long)*t->alloc);
+ t->aux=trees;
+ c->dim=v->elements;
+ c->entries=v->entries;
+ c->lengthlist=calloc(c->entries,sizeof(long));
+
+ for(booknum=0;booknum<trees;booknum++){
+ long points=0;
+ long *pointindex;
+
+ t->ptr0[booknum]=t->aux;
+ for(i=0;i<v->points;i++)
+ if(entrymap[pointmap[i]]==booknum)points++;
+ pointindex=malloc(points*sizeof(long));
+ points=0;
+ for(i=0;i<v->points;i++)
+ if(entrymap[pointmap[i]]==booknum)pointindex[points++]=i;
+
+ /* generate the encoding decision heirarchy */
+ fprintf(stderr,"Leaves added: %d \n",
+ lp_split(v,b,entryindex[booknum],entries[booknum],
+ pointindex,points,0,&pointssofar));
+
+ free(pointindex);
+ fprintf(stderr,"Paring/rerouting redundant branches... ");
- /* Now, we need to fill in the hole from this redundant
- entry in the listing. Insert the last entry in the list.
- Fix the forward pointers to that last entry */
- t->aux--;
- t->ptr0[i]=t->ptr0[t->aux];
- t->ptr1[i]=t->ptr1[t->aux];
- t->p[i]=t->p[t->aux];
- t->q[i]=t->q[t->aux];
- for(k=0;k<t->aux;k++){
- if(t->ptr0[k]==-t->aux)t->ptr0[k]=-i;
- if(t->ptr1[k]==-t->aux)t->ptr1[k]=-i;
+ /* The tree is likely big and redundant. Pare and reroute branches */
+ {
+ int changedflag=1;
+
+ while(changedflag){
+ changedflag=0;
+
+ /* span the tree node by node; list unique decision nodes and
+ short circuit redundant branches */
+
+ for(i=t->ptr0[booknum];i<t->aux;){
+ int k;
+
+ /* check list of unique decisions */
+ for(j=t->ptr0[booknum];j<i;j++)
+ if(_node_eq(t,i,j))break;
+
+ if(j<i){
+ /* a redundant entry; find all higher nodes referencing it and
+ short circuit them to the previously noted unique entry */
+ changedflag=1;
+ for(k=0;k<t->aux;k++){
+ if(t->ptr0[k]==-i)t->ptr0[k]=-j;
+ if(t->ptr1[k]==-i)t->ptr1[k]=-j;
+ }
+
+ /* Now, we need to fill in the hole from this redundant
+ entry in the listing. Insert the last entry in the list.
+ Fix the forward pointers to that last entry */
+ t->aux--;
+ t->ptr0[i]=t->ptr0[t->aux];
+ t->ptr1[i]=t->ptr1[t->aux];
+ t->p[i]=t->p[t->aux];
+ t->q[i]=t->q[t->aux];
+ for(k=0;k<t->aux;k++){
+ if(t->ptr0[k]==-t->aux)t->ptr0[k]=-i;
+ if(t->ptr1[k]==-t->aux)t->ptr1[k]=-i;
+ }
+ /* hole plugged */
+
+ }else
+ i++;
}
- /* hole plugged */
-
- }else
- i++;
+
+ fprintf(stderr,"\rParing/rerouting redundant branches... "
+ "%ld remaining ",t->aux);
+ }
+ fprintf(stderr,"\n");
}
-
- fprintf(stderr," %ld remaining\n",t->aux);
}
}
probability count */
{
long *probability=malloc(c->entries*sizeof(long));
- long *membership=malloc(c->entries*sizeof(long));
-
for(i=0;i<c->entries;i++)probability[i]=1; /* trivial guard */
-
b->valuelist=v->entrylist; /* temporary for vqenc_entry; replaced later */
- for(i=0;i<v->points;i++){
- int ret=codebook_entry(b,v->pointlist+i*v->elements);
- probability[ret]++;
- }
- for(i=0;i<v->entries;i++)membership[i]=i;
-
- /* find codeword lengths */
- /* much more elegant means exist. Brute force n^2, minimum thought */
- for(i=v->entries;i>1;i--){
- int first=-1,second=-1;
- long least=v->points+1;
-
- /* find the two nodes to join */
- for(j=0;j<v->entries;j++)
- if(probability[j]<least){
- least=probability[j];
- first=membership[j];
- }
- least=v->points+1;
- for(j=0;j<v->entries;j++)
- if(probability[j]<least && membership[j]!=first){
- least=probability[j];
- second=membership[j];
- }
- if(first==-1 || second==-1){
- fprintf(stderr,"huffman fault; no free branch\n");
- exit(1);
- }
- /* join them */
- least=probability[first]+probability[second];
- for(j=0;j<v->entries;j++)
- if(membership[j]==first || membership[j]==second){
- membership[j]=first;
- probability[j]=least;
- c->lengthlist[j]++;
- }
- }
- for(i=0;i<v->entries-1;i++)
- if(membership[i]!=membership[i+1]){
- fprintf(stderr,"huffman fault; failed to build single tree\n");
- exit(1);
- }
-
- /* unneccessary metric */
- memset(probability,0,sizeof(long)*v->entries);
for(i=0;i<v->points;i++){
int ret=codebook_entry(b,v->pointlist+i*v->elements);
probability[ret]++;
+ if(!(i&0xff))spinnit("counting hits... ",v->points-i);
}
+
+ build_tree_from_lengths(c->entries,probability,c->lengthlist);
free(probability);
- free(membership);
}
/* Sort the entries by codeword length, short to long (eases
/* rearrange storage; ptr0/1 first as it needs a reverse index */
/* n and c stay unchanged */
for(i=0;i<c->entries;i++)revindex[index[i]]=i;
- for(i=0;i<t->aux;i++){
+ for(i=t->ptr0[0];i<t->aux;i++){
+ if(!(i&0x3f))spinnit("sorting... ",t->aux-i);
+
if(t->ptr0[i]>=0)t->ptr0[i]=revindex[t->ptr0[i]];
if(t->ptr1[i]>=0)t->ptr1[i]=revindex[t->ptr1[i]];
t->p[i]=revindex[t->p[i]];
free(wordlen);
}
- fprintf(stderr,"Done.\n\n");
+ fprintf(stderr,"Done. \n\n");
}