//
// WordBitCompress.cc
//
// BitStream: put and get bits into a buffer
//           *tagging:  add tags to keep track of the position of data 
//                      inside the bitstream for debuging purposes.
//           *freezing: saves current position. further inserts in the BitStream
//                      aren't really done. This way you can try different
//                      compression algorithms and chose the best.
//
// Compressor: BitStream with extended compression fuctionalities
//
//
// Part of the ht://Dig package   <http://www.htdig.org/>
// Copyright (c) 1999-2004 The ht://Dig Group
// For copyright details, see the file COPYING in your distribution
// or the GNU Library General Public License (LGPL) version 2 or later
// <http://www.gnu.org/copyleft/lgpl.html>
//
// $Id: WordBitCompress.cc,v 1.5 2004/05/28 13:15:26 lha Exp $
//

#ifdef HAVE_CONFIG_H
#include "htconfig.h"
#endif /* HAVE_CONFIG_H */

#include <stdlib.h>

#include"WordBitCompress.h"

// ******** HtVector_byte (implementation)
#define GType byte
#define HtVectorGType HtVector_byte
#include "HtVectorGenericCode.h"

// ******** HtVector_charptr (implementation)
#define GType charptr
#define HtVectorGType HtVector_charptr
#include "HtVectorGenericCode.h"



// **************************************************
// *************** misc functions *******************
// **************************************************

// return a temporary string that merges a name and a number
char *
label_str(const char *s,int n)
{
    static char buff[1000];
    sprintf(buff,"%s%d",s,n);
    return buff;
}

// display n bits of value v
void
show_bits(int v,int n/*=16*/)
{
    int i;
    if(n>0)
    {
	for(i=0;i<n;i++)
	{
	    printf("%c",( v&(1<<(n-i-1)) ? '1':'0' ) );
	}
    }
    else
    {
	n=-n;
	for(i=0;i<n;i++)
	{
	    printf("%c",( v&(1<<(i)) ? '1':'0' ) );
	}
    }
}



// duplicate an array of unsigned int's
unsigned int *
duplicate(unsigned int *v,int n)
{
    unsigned int *res=new unsigned int[n];
    CHECK_MEM(res);
    memcpy((void *)res,(void *)v,n*sizeof(unsigned int));
    return(res);
}

// quick sort compare function (for unsigned int's)
int
qsort_uint_cmp(const void *a,const void *b)
{
//      printf("%12u %12u",*((unsigned int *)a),*((unsigned int *)b));
    if((*((unsigned int *)a)) > (*((unsigned int *)b))) return 1;
    else
    if((*((unsigned int *)a)) < (*((unsigned int *)b))) return -1;
    else
    return 0;
//      return 
//  	(*((unsigned int *)a)) -
//  	(*((unsigned int *)b))   ;
}
// quick sort an array of unsigned int's
void
qsort_uint(unsigned int *v,int n)
{
    qsort((void *)v,(unsigned int)n,sizeof(unsigned int),&qsort_uint_cmp);
}

// log in base 2 of v
// log2(0) -> -1
// log2(1) ->  0
// log2(2) ->  1
// log2(4) ->  2
// ...
// log2(8) ->  3
// log2(7) ->  2
int
log2(unsigned int v)
{
    int res;
    for(res=-1;v;res++){v>>=1;}
    return(res);
}




// **************************************************
// *************** VlengthCoder   *******************
// **************************************************
//
// Compress values into a bitstream based on their probability distribution
// The probability distribution is reduced to a number of intervals.
// Each  interval (generally)  has the same probability of occuring
// values are then coded by:  interval_number position_inside_interval
// this can be seen as modified version of shanon-fanno encoding
//
// Here are some aproximate calculation for estimating final coded size: 
//
// n number of entries to code
// nbits maximum size in bits of entries to code
//
// SUM_interval_bit_sizes -> depends on probability dist
// total_size = table_size + coded_size
// table_size = 2^nlev * NBITS_NBITS_VAL
// coded_size = n * (nlev + SUM_interval_bit_sizes / 2^nlev )
//
// example1: flat probability distribution :
// SUM_interval_bit_sizes = 2^nlev * log2( 2^nbits / 2^nlev) = 2^nlev * ( nbits - nlev )
// => coded_size = n * ( nlev + nbits - nlev ) = n*nbits !!
// => coded_size is the same as if we used no compression
//    this is normal, because it is not possible to compress random data
//
// example2: probability all focused in first interval except for one entry
// SUM_interval_bit_sizes  = 1 + nbits
// the computations above are not valid because of integer roundofs
// => coded_size would actually be = n *  1 + nbits 
// (but the code needs a few cleanups to obtain this value) 
//
class VlengthCoder
{
    int nbits;// min number of bits to code all entries
    int nlev;// split proba into 2^nlev parts
    int nintervals;// number of intervals

    int *intervals;
    unsigned int *intervalsizes; // speedup
    unsigned int *lboundaries; // speedup
    BitStream &bs;

//      inline unsigned int intervalsize(int i)
//  	{
//  	    unsigned int res=((intervals[i] > 0 ? pow2(intervals[i]-1) : 0));
//  	    if(intervalsizes[i]!=res){errr("intervalsizes");}
//  	    return res;
//  	}
    inline unsigned int intervalsize0(int i){return((intervals[i] > 0 ? pow2(intervals[i]-1) : 0));}

public:
    int verbose;

    // find interval where value v resides
    // fast version, this one recursively splits initial interval
    inline int find_interval2(const unsigned int v,unsigned int &lboundary)
    {
	int i0=0;
	int i1=nintervals;
	int i;
	for(;;)
	{
	    if(i1==i0+1){break;}
	    i=(i0+i1)>>1;
	    lboundary=lboundaries[i];
//  	    if(verbose)printf("considering i0:%3d i1:%3d : i:%3d  v:%12u lboundary:%12u (%12u - %12u)\n",i0,i1,i,v,lboundary,lboundaries[i0],lboundaries[i1]);
	    if(v<lboundary){i1=i;continue;}
	    else           {i0=i;continue;}
	    
	}

	lboundary=lboundaries[i0];
//  	i=i0;
//    	unsigned int sboundary=lboundary+intervalsizes[i];
//    	if(!( (lboundary!=sboundary && v>=lboundary && v<sboundary) || 
//    	    (lboundary==sboundary && v==lboundary)                   ))
//    	{
//    	    printf("interval fd:i0:%3d i1:%3d : i:%3d  v:%12u lboundary:%12u (%12u - %12u)\n",i0,i1,i,v,lboundary,lboundaries[i0],lboundaries[i1]);
//    	    errr("bad interval");
//    	}
	return i0;
    }

    // find interval where value v resides
    // slow version, this tries every interval
    inline int find_interval(const unsigned int v,unsigned int &lboundary)
    {
	// SPEED CRITICAL SECTION
	register int i;
	register unsigned int sboundary=0;
	lboundary=0;
	for(i=0;i<nintervals-1;i++)
	{
//  	    if(i>=nintervals){errr("code argh!");}
	    sboundary=lboundary+intervalsizes[i];
//    	    printf("nintervals:%3d i:%3d : %12u ...  %12u  : %12u\n",nintervals,i,lboundary,sboundary,v);
	    if( (lboundary!=sboundary && v>=lboundary && v<sboundary) || 
		(lboundary==sboundary && v==lboundary)                   ){break;}
	    lboundary=sboundary;
	}

	return i;
    }

    // compress and insert a value into the bitstream
    inline void code(unsigned int v)
    {
	unsigned int lboundary=0;
	// SPEED CRITICAL SECTION
	int i;
//  	i=find_interval(v,lboundary);
  	i=find_interval2(v,lboundary);
	// were in the i'th interval;
  	bs.put_uint(i,nlev,"int");// store interval
	const int bitsremaining=(intervals[i]>0 ? intervals[i]-1 : 0);
//  	if(verbose>1)printf("v:%6d interval:%2d (%5d - %5d) bitsremaining:%2d ",v,i,lboundary,sboundary,bitsremaining);
	v-=lboundary;
//  	if(verbose>1)printf("remain:%6d  totalbits:%2d\n",v,bitsremaining+nlev);
    	bs.put_uint(v,bitsremaining,"rem");
    }
    // get and uncompress  a value from  the bitstream
    inline unsigned int get()
    {
	// SPEED CRITICAL SECTION
	int i=bs.get_uint(nlev,"int");// get interval
//  	if(verbose>1)printf("get:interval:%2d ",i);
	const int bitsremaining=(intervals[i]>0 ? intervals[i]-1 : 0);
//  	if(verbose>1)printf("bitsremain:%2d ",bitsremaining);
	unsigned int v=bs.get_uint(bitsremaining,"rem");
//  	if(verbose>1)printf("v0:%3d ",v);
//  	unsigned int lboundary=0;
	v+=lboundaries[i];
//	for(int j=0;j<i;j++){lboundary+=intervalsizes[j];}
//  	v+=lboundary;
//  	if(verbose>1)printf("lboundary:%5d v:%5d \n",lboundaries[i],v);
	return(v);
    }


    //  insert the packed probability distrbution into the bitstream
    void code_begin();
    //  get the packed probability distrbution from the bitstream
    void get_begin();

    void make_lboundaries();

    VlengthCoder(BitStream &nbs,int nverbose=0);
    
    ~VlengthCoder()
    {
	delete [] lboundaries;
	delete [] intervals;
	delete [] intervalsizes;
    }

    // create VlengthCoder and its probability distrbution from an array of values
    VlengthCoder(unsigned int *vals,int n,BitStream &nbs,int nverbose=0);
};

void 
VlengthCoder::code_begin()
{
    int i;
    bs.add_tag("VlengthCoder:Header");
    bs.put_uint(nbits,NBITS_NBITS_VAL,"nbits");
    bs.put_uint(nlev,5,"nlev");
    for(i=0;i<nintervals;i++)
    {
	bs.put_uint(intervals[i],NBITS_NBITS_VAL,label_str("interval",i));
    }
}
void 
VlengthCoder::get_begin()
{
    int i;
    nbits=bs.get_uint(NBITS_NBITS_VAL,"nbits");
    if(verbose>1)printf("get_begin nbits:%d\n",nbits);
    nlev=bs.get_uint(5,"nlev");
    if(verbose>1)printf("get_begin nlev:%d\n",nlev);
    nintervals=pow2(nlev);

    intervals=new int [nintervals];
    CHECK_MEM(intervals);
    intervalsizes=new unsigned int [nintervals];
    CHECK_MEM(intervalsizes);
    lboundaries=new unsigned int [nintervals+1];
    CHECK_MEM(lboundaries);

    for(i=0;i<nintervals;i++)
    {
	intervals[i]=bs.get_uint(NBITS_NBITS_VAL,label_str("interval",i));
	intervalsizes[i]=intervalsize0(i);
	if(verbose>1)printf("get_begin intervals:%2d:%2d\n",i,intervals[i]);
    }
    make_lboundaries();
}
void 
VlengthCoder::make_lboundaries()
{
    unsigned int lboundary=0;
    for(int j=0;j<=nintervals;j++)
    {
	lboundaries[j]=lboundary;
	if(j<nintervals){lboundary+=intervalsizes[j];}
    }
}

VlengthCoder::VlengthCoder(BitStream &nbs,int nverbose/*=0*/):bs(nbs)
{
    verbose=nverbose;
    nbits=0;
    nlev=0;
    nintervals=0;
    intervals=NULL;
}

int debug_test_nlev=-1;

VlengthCoder::VlengthCoder(unsigned int *vals,int n,BitStream &nbs,int nverbose/*=0*/):bs(nbs)
{
    verbose=nverbose;
    unsigned int *sorted=duplicate(vals,n);
    qsort_uint(sorted,n);

    nbits=num_bits(HtMaxMin::max_v(vals,n));

    // **** heuristics to determine best nlev
    // force table size to be less than 1/10 of the maximum coded size
    nlev=num_bits((n*nbits)/(10*NBITS_NBITS_VAL));
    // sanity
    if(nlev>=nbits){nlev=nbits-1;}
    // nlev at least 1
    if(nlev<1){nlev=1;}

    if(debug_test_nlev>=0){nlev=debug_test_nlev;}
    nintervals=pow2(nlev);
    int i;

    intervals=new int [nintervals];
    CHECK_MEM(intervals);
    intervalsizes=new unsigned int [nintervals];
    CHECK_MEM(intervalsizes);
    lboundaries=new unsigned int [nintervals+1];
    CHECK_MEM(lboundaries);

    if(verbose>1)printf("nbits:%d nlev:%d nintervals:%d \n",nbits,nlev,nintervals);

    if(verbose>10)
    {
	printf("vals;\n");
	for(i=0;i<n;i++)
	{
	    printf("%12u  ",vals[i]);
	}
	printf("\nsorted:\n");
	for(i=0;i<n;i++)
	{
	    printf("%12u  ",sorted[i]);
	}
	printf("\n");
    }

    // find split boundaires
    unsigned int lboundary=0;
    unsigned int boundary;
    for(i=0;i<nintervals-1;i++)
    {
	boundary=sorted[(n*(i+1))/nintervals];
	intervals[i]=1+log2(boundary-lboundary);
	intervalsizes[i]=intervalsize0(i);
	if(0 || verbose>1)printf("intnum%02d  begin:%5u end:%5u len:%5u (code:%2d)  real upper boundary: real:%5u\n",i,lboundary,intervalsizes[i]+lboundary,intervalsizes[i],intervals[i],boundary);
	lboundary+=intervalsizes[i];
    }
    boundary=sorted[n-1];
    intervals[i]=1+log2(boundary-lboundary)+1;
    intervalsizes[i]=intervalsize0(i);
    if(0 || verbose>1)printf("intnum%02d  begin:%5u end:%5u len:%5u (code:%2d)  real upper boundary: real:%5u\n",i,lboundary,intervalsizes[i]+lboundary,intervalsizes[i],intervals[i],boundary);
    if(0 || verbose>1)printf("\n");

    make_lboundaries();

    int SUM_interval_bit_sizes=0;
    for(i=0;i<nintervals;i++)
    {
	SUM_interval_bit_sizes+=intervals[i];
    }
    if(verbose)printf("SUM_interval_bit_sizes:%d\n",SUM_interval_bit_sizes);
    delete [] sorted;
}


// **************************************************
// *************** BitStream  ***********************
// **************************************************

void 
BitStream::put_zone(byte *vals,int n,const char *tag)
{
    add_tag(tag);
    for(int i=0;i<(n+7)/8;i++){put_uint(vals[i],TMin(8,n-8*i),NULL);}
}
void 
BitStream::get_zone(byte *vals,int n,const char *tag)
{
    check_tag(tag);
    for(int i=0;i<(n+7)/8;i++){vals[i]=get_uint(TMin(8,n-8*i));}
}

void 
BitStream::put_uint(unsigned int v,int n,const char *tag/*="NOTAG"*/)
{
    // SPEED CRITICAL SECTION
    if(freezeon){bitpos+=n;return;}
    add_tag(tag);
    
    if(!n){return;}

    // 1)
    int bpos0= bitpos & 0x07;
//      printf("bpos0:%3d bitpos:%5d:%5d  n:%4d  val:%x\n",bpos0,bitpos,buff.size()*8,n,v);
    if(bpos0 + n <8)
    {
//    	printf("simple case:");
//  	::show_bits(v,n);
//  	printf("\n");
	// simplest case it all fits
	buff.back()|=v<<bpos0;
	bitpos+=n;
	if(! (bitpos & 0x07) )
	{buff.push_back(0);}// new byte
	return;
    }
    else
    {
	const int ncentral=((bpos0 + n)>>3)-1;
	// put first
	buff.back()|=((v & 0xff)<<bpos0) & 0xff;
	const int nbitsinfirstbyte=8-bpos0;

//  	printf("normal case :(%x:%x)",((v & 0xff)<<bpos0) & 0xff,buff.back());
//  	::show_bits(((v & 0xff)<<bpos0) & 0xff,-8);
//  	printf(" ");


	v>>=nbitsinfirstbyte;
//  	printf(" (v:%x)",v);
	// put central
	for(int i=ncentral;i;i--)
	{
	    buff.push_back(0);
	    buff.back()= v & 0xff ;
//  	    ::show_bits(v & 0xff,-8);
//  	    printf(" ");
	    v>>=8;	    
	}
	// put last
	const int nbitsremaining=n-(  (ncentral<<3)+nbitsinfirstbyte );
	if(nbitsremaining)
	{
	    buff.push_back(0);
	    buff.back()=v &  (pow2(nbitsremaining+1)-1);

//  	    printf(" (v:%x:%x)",v &  (pow2(nbitsremaining+1)-1),buff.back());
//  	    ::show_bits(v &  (pow2(nbitsremaining+1)-1),-nbitsremaining);
//  	    printf("\n");
	}
	if(!(nbitsremaining & 0x07)){buff.push_back(0);}
	bitpos+=n;
//  	printf("nbitsinfirstbyte:%d ncentral:%d  nbitsremaining:%d\n",nbitsinfirstbyte,ncentral,nbitsremaining);

    }
//      printf("cuurent put order:");
//      for(i=0;i<n;i++)
//      {
//  	printf("%c",((v0& pow2(i) ? '1':'0')));
//      }
//      printf("\n");
}




unsigned int 
BitStream::get_uint(int n,const char *tag/*=NULL*/)
{
    // SPEED CRITICAL SECTION
    if(check_tag(tag)==NOTOK){errr("BitStream::get(int) check_tag failed");}    
    if(!n){return 0;}

    unsigned int res=0;

    // 1)
    int bpos0= bitpos & 0x07;

//      printf("bpos0:%3d bitpos:%5d  n:%4d %s\n",bpos0,bitpos,n,tag);
//      printf("input:\n");
//      for(int j=0;j<(bpos0+n+7)/8;j++){printf("%x",buff[bitpos/8+j]);}
//      printf("\n");

    if(bpos0 + n <8)
    {
	// simplest case it all fits
	res=(buff[bitpos>>3]>>bpos0) & (pow2(n)-1);
	bitpos+=n;
//      	printf("simple case:res:%x\n",res);
	return res;
    }
    else
    {
	int bytepos=bitpos>>3;
	const int ncentral=((bpos0 + n)>>3)-1;
	// put first
	res=(buff[bytepos]>>bpos0) & 0xff;
//        	printf("normal case:res0:%x\n",res);

	const int nbitsinfirstbyte=8-bpos0;

	bytepos++;
	// put central
	if(ncentral)
	{
	    unsigned int v=0;
	    for(int i=ncentral-1;i>=0;i--)
	    {
		v|=buff[bytepos+i]&0xff;
		if(i)v<<=8;
//    		printf("       resC%d:v:%x\n",i,v);
	    }
	    bytepos+=ncentral;
	    res|=v<<nbitsinfirstbyte;
//    	    printf("       :resC:%x\n",res);
	}
	// put last
	const int nbitsremaining=n-(  (ncentral<<3)+nbitsinfirstbyte );
	if(nbitsremaining)
	{
	    res|=((unsigned int)(buff[bytepos] &  (pow2(nbitsremaining)-1) )) << (nbitsinfirstbyte +((bytepos-(bitpos>>3)-1)<<3));
//    	    printf("       :resR:%x  buff[%d]:%x  %d\n",res,bytepos,buff[bytepos],
//    		   (nbitsinfirstbyte +((bytepos-(bitpos>>3)-1)<<3)));
	}

	bitpos+=n;
//      	printf("nbitsinfirstbyte:%d ncentral:%d  nbitsremaining:%d\n",nbitsinfirstbyte,ncentral,nbitsremaining);
	return res;
    }
}
#ifdef NOTDEF
unsigned int 
BitStream::get(int n,const char *tag/*=NULL*/)
{	
    if(check_tag(tag)==NOTOK){errr("BitStream::get(int) check_tag failed");}
    unsigned int res=0;
    for(int i=0;i<n;i++)
    {
	if(get()){res|=pow2(i);}
    }
    return(res);
}
#endif
void 
BitStream::freeze()
{
    freeze_stack.push_back(bitpos);
    freezeon=1;
}

int 
BitStream::unfreeze()
{
    int size0=bitpos;
    bitpos=freeze_stack.back();
    freeze_stack.pop_back();
    size0-=bitpos;
    if(freeze_stack.size()==0){freezeon=0;}
    return(size0);
}
void 
BitStream::add_tag1(const char *tag)
{
    if(!use_tags){return;}
    if(freezeon){return;}
    if(!tag){return;}
    tags.push_back(strdup(tag));
    tagpos.push_back(bitpos);
}

int 
BitStream::check_tag1(const char *tag,int pos/*=-1*/)
{
    if(!use_tags){return OK;}
    if(!tag){return OK;}
    int found=-1;
    int ok=0;
    if(pos==-1){pos=bitpos;}
    for(int i=0;i<tags.size();i++)
    {
	if(!strcmp(tags[i],tag))
	{
	    found=tagpos[i];
	    if(tagpos[i]==pos){ok=1;break;}
	}
    }
    if(!ok)
    {
	show();
	if(found>=0)
	{
	    printf("ERROR:BitStream:bitpos:%4d:check_tag: found tag %s at %d expected it at %d\n",bitpos,tag,found,pos);
	}
	else
	{
	    printf("ERROR:BitStream:bitpos:%4d:check_tag:  tag %s not found, expected it at %d\n",bitpos,tag,pos);
	}
	return(NOTOK);
    }
    return(OK);
}

int 
BitStream::find_tag(const char *tag)
{
    int i;
    for(i=0;i<tags.size() && strcmp(tag,tags[i]);i++);
    if(i==tags.size()){return -1;}
    else{return i;}
}
int 
BitStream::find_tag(int pos,int posaftertag/*=1*/)
{
    int i;
    for(i=0;i<tags.size() && tagpos[i]<pos;i++);
    if(i==tags.size()){return -1;}
    if(!posaftertag){return i;}
    for(;tagpos[i]>pos && i>=0;i--);
    return(i);	
}

void 
BitStream::show_bits(int a,int n)
{
    for(int b=a;b<a+n;b++)
    {
	printf("%c",(buff[b/8] & (1<<(b%8)) ? '1' : '0'));
    }
}
void 
BitStream::show(int a/*=0*/,int n/*=-1*/)
{
    int all=(n<0 ? 1 : 0);
    if(n<0){n=bitpos-a;}
    int i;

    if(all)
    {
	printf("BitStream::Show: ntags:%d size:%4d buffsize:%6d ::: ",tags.size(),size(),buffsize());
//  	    for(i=0;i<tags.size();i++){printf("tag:%d:%s:pos:%d\n",i,tags[i],tagpos[i]);}
    }

    int t=find_tag(a,0);
    if(t<0){show_bits(a,n);return;}
    for(i=a;i<a+n;i++)
    {
	for(;t<tags.size() && tagpos[t]<i+1;t++)
	{
	    printf("# %s:%03d:%03d #",tags[t],tagpos[t],n);
	}
	show_bits(i,1);
    }
    if(all){printf("\n");}

}
byte *
BitStream::get_data()
{
    byte *res=(byte *)malloc(buff.size());
    CHECK_MEM(res);
    for(int i=0;i<buff.size();i++){res[i]=buff[i];}
    return(res);
}
void 
BitStream::set_data(const byte *nbuff,int nbits)
{
    if(buff.size()!=1 || bitpos!=0)
    {
	printf("BitStream:set_data: size:%d bitpos:%d\n",buff.size(),bitpos);
	errr("BitStream::set_data: valid only if BitStream is empty");
    }
    buff[0] = nbuff[0];
    for(int i=1;i<(nbits+7)/8;i++){buff.push_back(nbuff[i]);}
    bitpos=nbits;
}



// **************************************************
// *************** Compressor ***********************
// **************************************************


void 
Compressor::put_uint_vl(unsigned int v,int maxn,const char *tag/*="NOTAG"*/)
{
    int nbits=num_bits(v);
    put_uint(nbits,num_bits(maxn),tag);
    if(nbits){put_uint(v,nbits,(char *)NULL);}
}
unsigned int 
Compressor::get_uint_vl(int maxn,const char *tag/*=NULL*/)
{
    int nbits=get_uint(num_bits(maxn),tag);
    if(!nbits){return 0;}
    else{return(get_uint(nbits,(char *)NULL));}
}

int 
Compressor::put_vals(unsigned int *vals,int n,const char *tag)
{
    int cpos=bitpos;
    add_tag(tag);
    if(n>=pow2(NBITS_NVALS)){errr("Compressor::put(uint *,nvals) : overflow: nvals>2^16");}
    put_uint_vl(n,NBITS_NVALS,"size");
    if(n==0){return NBITS_NVALS;}

    int sdecr=2;
    int sfixed=1;

    int nbits=num_bits(HtMaxMin::max_v(vals,n));
    if(verbose)printf("*********************put_vals:n:%3d nbits:%3d\n",n,nbits);

    int i;
    if(verbose)
    {
	printf("TTT:n:%3d nbits:%3d\n",n,nbits);
	for(i=1;i<7;i++)
	{
	    debug_test_nlev=i;
	    printf("trying nlev:%3d\n",debug_test_nlev);
	    freeze();
	    put_decr(vals,n);
	    int fndsz=unfreeze();
	    printf("TTT:nlev:%2d try size:%4d\n",i,fndsz);
	}
	debug_test_nlev=-1;
    }

    if(n>15 && nbits>3)
    {
	freeze();
	put_decr(vals,n);
	sdecr=unfreeze();

	freeze();
	put_fixedbitl(vals,n);
	sfixed=unfreeze();
    }

    if(verbose)printf("put_vals:n:%3d sdecr:%6d sfixed:%6d rap:%f\n",n,sdecr,sfixed,sdecr/(float)sfixed);
    if(sdecr<sfixed)
    {
	if(verbose)printf("put_vals: comptyp:0\n");
	put_uint(0,2,"put_valsCompType");
	put_decr(vals,n);
    }
    else
    {
	if(verbose)printf("put_vals: comptyp:1\n");
	put_uint(1,2,"put_valsCompType");
	put_fixedbitl(vals,n);
    }

    if(verbose)printf("------------------------------put_vals over\n");

    return(bitpos-cpos);
}

int 
Compressor::get_vals(unsigned int **pres,const char *tag/*="BADTAG!"*/)
{
    if(check_tag(tag)==NOTOK){errr("Compressor::get_vals(unsigned int): check_tag failed");}
    int n=get_uint_vl(NBITS_NVALS);
    if(verbose>1)printf("get_vals n:%d\n",n);
    if(!n){*pres=NULL;return 0;}

    if(verbose)printf("get_vals: n:%3d\n",n);
    unsigned int *res=new unsigned int[n];
    CHECK_MEM(res);


    int comptype=get_uint(2,"put_valsCompType");
    if(verbose)printf("get_vals:comptype:%d\n",comptype);
    switch(comptype)
    {
    case 0:    get_decr(res,n);
	break;
    case 1:    get_fixedbitl(res,n);
	break;
    default:   errr("Compressor::get_vals invalid comptype");break;
    }
//      get_fixedbitl(res,n);
//      get_decr(res,n);

    *pres=res;
    return(n);
}


int 
Compressor::put_fixedbitl(byte *vals,int n,const char *tag)
{
    int cpos=bitpos;
    int i,j;
    add_tag(tag);

    put_uint_vl(n,NBITS_NVALS,"size");
    if(n==0){return 0;}

    byte maxv=vals[0];
    for(i=1;i<n;i++)
    {
	byte v=vals[i];
	if(v>maxv){maxv=v;}
    }
    int nbits=num_bits(maxv);
    if(n>=pow2(NBITS_NVALS)){errr("Compressor::put_fixedbitl(byte *) : overflow: nvals>2^16");}
    put_uint(nbits,NBITS_NBITS_CHARVAL,"nbits");
    add_tag("data");
    for(i=0;i<n;i++)
    {
	byte v=vals[i];
	for(j=0;j<nbits;j++) {put(v&pow2(j));}
    }	
    return(bitpos-cpos);
}
void
Compressor::put_fixedbitl(unsigned int *vals,int n)
{
    int nbits=num_bits(HtMaxMin::max_v(vals,n));

    put_uint_vl(nbits,NBITS_NBITS_VAL,"nbits");
    add_tag("data");
    if(verbose)printf("put_fixedbitl:nbits:%4d nvals:%6d\n",nbits,n);
    for(int i=0;i<n;i++)
    {
	put_uint(vals[i],nbits,NULL);
    }	
}

void
Compressor::get_fixedbitl(unsigned int *res,int n)
{
    int nbits=get_uint_vl(NBITS_NBITS_VAL);
    if(verbose)printf("get_fixedbitl(uint):n%3d nbits:%2d\n",n,nbits);
    int i;
    for(i=0;i<n;i++)
    {
	res[i]=get_uint(nbits);
    }
}
int 
Compressor::get_fixedbitl(byte **pres,const char *tag/*="BADTAG!"*/)
{
    if(check_tag(tag)==NOTOK){errr("Compressor::get_fixedbitl(byte *): check_tag failed");}
    int n=get_uint_vl(NBITS_NVALS);
    if(!n){*pres=NULL;return 0;}
    int nbits=get_uint(NBITS_NBITS_CHARVAL);
    if(verbose)printf("get_fixedbitl(byte):n%3d nbits:%2d\n",n,nbits);
    int i;
    byte *res=new byte[n];
    CHECK_MEM(res);
    for(i=0;i<n;i++)
    {
	res[i]=get_uint(nbits);
    }
    *pres=res;
    return(n);
}

void
Compressor::put_decr(unsigned int *vals,int n)
{
    VlengthCoder coder(vals,n,*this,verbose);
    coder.code_begin();
    int i;
    for(i=0;i<n;i++){coder.code(vals[i]);}
}
void
Compressor::get_decr(unsigned int *res,int n)
{
    VlengthCoder coder(*this,verbose);
    coder.get_begin();
    int i;
    for(i=0;i<n;i++)
    {
	res[i]=coder.get();
	if(verbose>1){printf("get_decr:got:%8d\n",res[i]);}
    }
}
