/***********************************************************************
  This file is part of HA, a general purpose file archiver.
  Copyright (C) 1995 Harri Hirvola

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License hsc->as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
************************************************************************
	HA HSC method
***********************************************************************/

#include <proto/exec.h>
#include "ha.h"
#include "haio.h"
#include "acoder.h"
#include "hsc.h"

 
/***********************************************************************
	Finite context model
***********************************************************************/

#define HASH(s,l,h)	{				          \
			  h=0;                                    \
			  if (l) h=hsc->hrt[s[0]];                     \
			  if (l>1) h=hsc->hrt[(s[1]+h)&(HTLEN-1)];     \
			  if (l>2) h=hsc->hrt[(s[2]+h)&(HTLEN-1)];     \
			  if (l>3) h=hsc->hrt[(s[3]+h)&(HTLEN-1)];     \
			}                                                     

#define move_context(c) hsc->curcon[3]=hsc->curcon[2],hsc->curcon[2]=hsc->curcon[1], \
			hsc->curcon[1]=hsc->curcon[0],hsc->curcon[0]=c

static  void release_cfblocks(struct hsc *hsc) {
	
    register U16B i,j,d;
    
    do {
	do if (++hsc->nrel==NUMCON) hsc->nrel=0; while (hsc->nb[hsc->nrel]==NIL);
	for (i=0;i<=hsc->usp;++i) if ((hsc->cps[i]&0x7fff)==hsc->nrel) break;
    } while (i<=hsc->usp);	
    for (i=hsc->nb[hsc->nrel],d=hsc->fa[hsc->nrel];i!=NIL;i=hsc->nb[i]) if (hsc->fa[i]<d) d=hsc->fa[i];
    ++d;
    if (hsc->fa[hsc->nrel]<d) {
	for (i=hsc->nb[hsc->nrel];hsc->fa[i]<d && hsc->nb[i]!=NIL;i=hsc->nb[i]);
	hsc->fa[hsc->nrel]=hsc->fa[i];
	hsc->fc[hsc->nrel]=hsc->fc[i];
	j=hsc->nb[i];
	hsc->nb[i]=hsc->fcfbl;
	hsc->fcfbl=hsc->nb[hsc->nrel];
	if ((hsc->nb[hsc->nrel]=j)==NIL) {
	    hsc->cc[hsc->nrel]=0;
	    hsc->fe[hsc->nrel]=(hsc->ft[hsc->nrel]=hsc->fa[hsc->nrel])<ESCTH?1:0;
	    return;
	}
    }
    hsc->fe[hsc->nrel]=(hsc->ft[hsc->nrel]=hsc->fa[hsc->nrel]/=d)<ESCTH?1:0;
    hsc->cc[hsc->nrel]=0;
    for (j=hsc->nrel,i=hsc->nb[j];i!=NIL;) {
	if (hsc->fa[i]<d) {
	    hsc->nb[j]=hsc->nb[i];
	    hsc->nb[i]=hsc->fcfbl;
	    hsc->fcfbl=i;
	    i=hsc->nb[j];
	}
	else {
	    ++hsc->cc[hsc->nrel];
	    hsc->ft[hsc->nrel]+=hsc->fa[i]/=d;
	    if (hsc->fa[i]<ESCTH) hsc->fe[hsc->nrel]++;
	    j=i;
	    i=hsc->nb[i];
	}
    }
}

static  U16B make_context(struct hsc *hsc, unsigned char conlen, S16B c) {

    register S16B i;
    register U16B nc,fp;
    
    nc=hsc->ell;
    hsc->ell=hsc->elp[nc];
    hsc->elp[hsc->elf]=nc;
    hsc->eln[nc]=hsc->elf;
    hsc->elf=nc;
    if (hsc->cl[nc]!=0xff) {
	if (hsc->cl[nc]==MAXCLEN && --hsc->dropcnt==0) hsc->maxclen=MAXCLEN-1;
	HASH(hsc->con[nc],hsc->cl[nc],i);
	if (hsc->ht[i]==nc) hsc->ht[i]=hsc->hp[nc];
	else {
	    for (i=hsc->ht[i];hsc->hp[i]!=nc;i=hsc->hp[i]);
	    hsc->hp[i]=hsc->hp[nc];
	}
	if (hsc->nb[nc]!=NIL) {
	    for (fp=hsc->nb[nc];hsc->nb[fp]!=NIL;fp=hsc->nb[fp]);
	    hsc->nb[fp]=hsc->fcfbl;
	    hsc->fcfbl=hsc->nb[nc];
	}
    }
    hsc->nb[nc]=NIL;
    hsc->fe[nc]=hsc->ft[nc]=hsc->fa[nc]=1;
    hsc->fc[nc]=c;
    hsc->rfm[nc]=RFMINI;
    hsc->cc[nc]=0;
    hsc->cl[nc]=conlen;
    hsc->con[nc][0]=hsc->curcon[0];
    hsc->con[nc][1]=hsc->curcon[1];
    hsc->con[nc][2]=hsc->curcon[2];
    hsc->con[nc][3]=hsc->curcon[3];
    HASH(hsc->curcon,conlen,i);
    hsc->hp[nc]=hsc->ht[i];
    hsc->ht[i]=nc;
    return nc;
}

static  void el_movefront(struct hsc *hsc, U16B cp) {

    if (cp==hsc->elf) return;
    if (cp==hsc->ell) hsc->ell=hsc->elp[cp];
    else {
	hsc->elp[hsc->eln[cp]]=hsc->elp[cp];
	hsc->eln[hsc->elp[cp]]=hsc->eln[cp];
    }	
    hsc->elp[hsc->elf]=cp;
    hsc->eln[cp]=hsc->elf;
    hsc->elf=cp;
}

static void  add_model(struct hsc *hsc, S16B c) {

    register U16B i;
    register S16B cp;
    
    while (hsc->usp!=0) {
	i=hsc->as[--hsc->usp];
	cp=hsc->cps[hsc->usp];
	if (cp&0x8000) {
	    cp&=0x7fff;
	    if (hsc->fcfbl==NIL) release_cfblocks(hsc);
	    hsc->nb[i]=hsc->fcfbl;
	    i=hsc->nb[i];
	    hsc->fcfbl=hsc->nb[hsc->fcfbl];
	    hsc->nb[i]=NIL;
	    hsc->fa[i]=1;			
	    hsc->fc[i]=c;
	    ++hsc->cc[cp];
	    ++hsc->fe[cp];
	}
	else if (++hsc->fa[i]==ESCTH) --hsc->fe[cp];
	if ((hsc->fa[i]<<1)<++hsc->ft[cp]/(hsc->cc[cp]+1)) --hsc->rfm[cp];
	else if (hsc->rfm[cp]<RFMINI) ++hsc->rfm[cp];
	if (!hsc->rfm[cp] || hsc->ft[cp]>=MAXTVAL) {
	    ++hsc->rfm[cp];
	    hsc->fe[cp]=hsc->ft[cp]=0;
	    for (i=cp;i!=NIL;i=hsc->nb[i]) {
		if (hsc->fa[i]>1) {
		    hsc->ft[cp]+=hsc->fa[i]>>=1;
		    if (hsc->fa[i]<ESCTH) ++hsc->fe[cp];
		}
		else {
		    ++hsc->ft[cp];
		    ++hsc->fe[cp];
		}
	    }
	}
    }
}

static  U16B find_next(struct hsc *hsc) {

    register S16B i,k;
    register U16B cp;
    
    for (i=hsc->cslen-1;i>=0;--i) {
	k=hsc->hs[i];
	for (cp=hsc->ht[k];cp!=NIL;cp=hsc->hp[cp]) {
	    if (hsc->cl[cp]==i) {
		switch (i) {
		  case 4:
		    if (hsc->curcon[3]!=hsc->con[cp][3]) break;
		  case 3:
		    if (hsc->curcon[2]!=hsc->con[cp][2]) break;
		  case 2:
		    if (hsc->curcon[1]!=hsc->con[cp][1]) break;
		  case 1:
		    if (hsc->curcon[0]!=hsc->con[cp][0]) break;
		  case 0:
		    hsc->cslen=i;
		    return cp;
		}
	    }	
	}
    }
    return NIL;
}

static  U16B find_longest(struct hsc *hsc) {

    hsc->hs[1]=hsc->hrt[hsc->curcon[0]];	
    hsc->hs[2]=hsc->hrt[(hsc->curcon[1]+hsc->hs[1])&(HTLEN-1)]; 
    hsc->hs[3]=hsc->hrt[(hsc->curcon[2]+hsc->hs[2])&(HTLEN-1)]; 
    hsc->hs[4]=hsc->hrt[(hsc->curcon[3]+hsc->hs[3])&(HTLEN-1)]; 
    hsc->usp=0;
    while(hsc->cmsp) hsc->cmask[hsc->cmstack[--hsc->cmsp]]=0;
    hsc->cslen=MAXCLEN+1;
    return find_next(hsc);
}

static U16B adj_escape_prob(struct hsc *hsc, U16B esc, U16B cp) {

    if (hsc->ft[cp]==1) return hsc->iec[hsc->cl[cp]]>=(IECLIM>>1)?2:1;
    if (hsc->cc[cp]==255) return 1;
    if (hsc->cc[cp] && ((hsc->cc[cp]+1)<<1)>=hsc->ft[cp]) {
	esc=(S16B)((S32B)esc*((hsc->cc[cp]+1)<<1)/hsc->ft[cp]);
	if (hsc->cc[cp]+1==hsc->ft[cp]) esc+=(hsc->cc[cp]+1)>>1;
    }
    return esc?esc:1;
}


static  S16B code_first(struct hsc *hsc, U16B cp, S16B c) {

    register U16B i;
    register S16B sum,cf,tot,esc;
    
    sum=cf=0;
    for (i=cp;i!=NIL;i=hsc->nb[i]) {
	if (hsc->fc[i]==c) {
	    cf=hsc->fa[i];
	    hsc->as[0]=i;
	    break;
	}
	sum+=hsc->fa[i];
    }	
    tot=hsc->ft[cp];
    esc=adj_escape_prob(hsc,hsc->fe[cp],cp);
    if (hsc->nec>=NECLIM) {
	if (tot<=NECTLIM && hsc->nec==NECMAX) {
	    tot<<=2;
	    sum<<=2;
	    cf<<=2;
	}
	else {
	    tot<<=1;
	    sum<<=1;
	    cf<<=1;
	}
    }
    hsc->usp=1;
    if (cf==0) {
	ac_out(hsc->ac,hsc->haio,tot,tot+esc,tot+esc);
	for (i=cp;i!=NIL;sum=i,i=hsc->nb[i]) {
	    hsc->cmstack[hsc->cmsp++]=hsc->fc[i];
	    hsc->cmask[hsc->fc[i]]=1;
	}
	hsc->as[0]=sum;  /* sum holds last i ! */
	hsc->nec=0;
	if (hsc->ft[cp]==1 && hsc->iec[hsc->cl[cp]]<IECLIM) ++hsc->iec[hsc->cl[cp]];
	hsc->cps[0]=0x8000|cp; 
	return 0;
    }
    ac_out(hsc->ac,hsc->haio,sum,sum+cf,tot+esc);
    if (hsc->nec<NECMAX) ++hsc->nec;
    if (hsc->ft[cp]==1 && hsc->iec[hsc->cl[cp]]) --hsc->iec[hsc->cl[cp]];
    hsc->cps[0]=cp;
    return 1;
}


static  S16B code_rest(struct hsc *hsc, U16B cp, S16B c) {

    register U16B i;
    register S16B sum,cf,tot,esc;
    
    tot=sum=cf=esc=0;
    for (i=cp;i!=NIL;i=hsc->nb[i]) {
	if (!hsc->cmask[hsc->fc[i]]) {
	    if (hsc->fa[i]<ESCTH) ++esc;
	    if (cf==0 && hsc->fc[i]==c) {
		sum=tot;
		cf=hsc->fa[i];
		hsc->as[hsc->usp]=i;
	    }
	    tot+=hsc->fa[i];
	}
    }	
    esc=adj_escape_prob(hsc,esc,cp);
    if (cf==0) {
	ac_out(hsc->ac,hsc->haio,tot,tot+esc,tot+esc);
	for (i=cp;i!=NIL;sum=i,i=hsc->nb[i]) {
	    if (!hsc->cmask[hsc->fc[i]]) {
		hsc->cmstack[hsc->cmsp++]=hsc->fc[i];
		hsc->cmask[hsc->fc[i]]=1;
	    }
	}
	hsc->as[hsc->usp]=sum;  /* sum holds last i ! */
	if (hsc->ft[cp]==1 && hsc->iec[hsc->cl[cp]]<IECLIM) ++hsc->iec[hsc->cl[cp]];
	hsc->cps[hsc->usp++]=0x8000|cp; 
	return 0;
    }
    ac_out(hsc->ac,hsc->haio,sum,sum+cf,tot+esc);
    ++hsc->nec;   /* must add test used in code_first() if NECMAX<5 ! */
    if (hsc->ft[cp]==1 && hsc->iec[hsc->cl[cp]]) --hsc->iec[hsc->cl[cp]];
    hsc->cps[hsc->usp++]=cp;
    return 1;
}

static  void code_new(struct hsc *hsc, S16B c) {
    
    register S16B i;
    register U16B sum,tot;
    
    sum=0;
    tot=257-hsc->cmsp;
    for (i=0;i<c;++i) sum+=1-hsc->cmask[i];
    ac_out(hsc->ac,hsc->haio,sum,sum+1,tot);
}

static  S16B decode_first(struct hsc *hsc, U16B cp) {
    
    register U16B c;
    register U16B tv;
    register U16B i;
    register S16B sum,tot,esc,cf;
    register unsigned char sv;

    esc=adj_escape_prob(hsc,hsc->fe[cp],cp);
    tot=hsc->ft[cp];
    if (hsc->nec>=NECLIM) {
	if (tot<=NECTLIM && hsc->nec==NECMAX) sv=2;
	else sv=1;
	tot<<=sv;
	tv=ac_threshold_val(hsc->ac,hsc->haio,tot+esc)>>sv;
	for (c=cp,sum=0;;c=hsc->nb[c]) {
	    if (c==NIL) break;
	    if (sum+hsc->fa[c]<=tv) sum+=hsc->fa[c];
	    else {
		cf=hsc->fa[c]<<sv;
		break;
	    }
	}
	sum<<=sv;
    }
    else {
	tv=ac_threshold_val(hsc->ac,hsc->haio,tot+esc);
	for (c=cp,sum=0;;c=hsc->nb[c]) {
	    if (c==NIL) break;
	    if (sum+hsc->fa[c]<=tv) sum+=hsc->fa[c];
	    else {
		cf=hsc->fa[c];
		break;
	    }
	}
    }
    hsc->usp=1;
    if (c!=NIL) {
	ac_in(hsc->ac,hsc->haio,sum,sum+cf,tot+esc);
	if (hsc->ft[cp]==1 && hsc->iec[hsc->cl[cp]]) --hsc->iec[hsc->cl[cp]];
	hsc->as[0]=c;
	hsc->cps[0]=cp;
	c=hsc->fc[c];
	if (hsc->nec<NECMAX) ++hsc->nec;
    }
    else {
	ac_in(hsc->ac,hsc->haio,tot,tot+esc,tot+esc);
	if (hsc->ft[cp]==1 && hsc->iec[hsc->cl[cp]]<IECLIM) ++hsc->iec[hsc->cl[cp]];
	for (i=cp;i!=NIL;sum=i,i=hsc->nb[i]) {
	    hsc->cmstack[hsc->cmsp++]=hsc->fc[i];
	    hsc->cmask[hsc->fc[i]]=1;
	}
	hsc->cps[0]=0x8000|cp;
	hsc->as[0]=sum;
	c=ESC;
	hsc->nec=0;
    }
    return c;
}

static  S16B decode_rest(struct hsc *hsc, U16B cp) {

    register U16B c;
    register U16B tv;
    register U16B i;
    register S16B sum,tot,esc,cf;
    
    esc=tot=0;
    for (i=cp;i!=NIL;i=hsc->nb[i]) {
	if (!hsc->cmask[hsc->fc[i]]) {
	    tot+=hsc->fa[i];
	    if (hsc->fa[i]<ESCTH) ++esc;
	}
    }
    esc=adj_escape_prob(hsc,esc,cp);
    tv=ac_threshold_val(hsc->ac,hsc->haio,tot+esc);
    for (c=cp,sum=0;;c=hsc->nb[c]) {
	if (c==NIL) break;
	if (!hsc->cmask[hsc->fc[c]]) {
	    if (sum+hsc->fa[c]<=tv) sum+=hsc->fa[c];
	    else {
		cf=hsc->fa[c];
		break;
	    }
	}
    }
    if (c!=NIL) {
	ac_in(hsc->ac,hsc->haio,sum,sum+cf,tot+esc);
	if (hsc->ft[cp]==1 && hsc->iec[hsc->cl[cp]]) --hsc->iec[hsc->cl[cp]];
	hsc->as[hsc->usp]=c;
	hsc->cps[hsc->usp++]=cp;
	c=hsc->fc[c];
	++hsc->nec;  /* must add test used in code_first() if NECMAX<5 ! */
    }
    else {
	ac_in(hsc->ac,hsc->haio,tot,tot+esc,tot+esc);
	if (hsc->ft[cp]==1 && hsc->iec[hsc->cl[cp]]<IECLIM) ++hsc->iec[hsc->cl[cp]];
	for (i=cp;i!=NIL;sum=i,i=hsc->nb[i]) {
	    if (!hsc->cmask[hsc->fc[i]]) {
		hsc->cmstack[hsc->cmsp++]=hsc->fc[i];
		hsc->cmask[hsc->fc[i]]=1;
	    }
	}
	hsc->cps[hsc->usp]=0x8000|cp;
	hsc->as[hsc->usp++]=sum;		/* sum holds last i !! */
	c=ESC;
    }
    return c;
}

static  S16B decode_new(struct hsc *hsc) {
    
    register S16B c;
    register U16B tv,sum,tot;
    
    tot=257-hsc->cmsp;
    tv=ac_threshold_val(hsc->ac,hsc->haio,tot);
    for (c=sum=0;c<256;++c) {
	if (hsc->cmask[c]) continue;
	if (sum+1<=tv) ++sum;
	else break;
    }
    ac_in(hsc->ac,hsc->haio,sum,sum+1,tot);	
    return c;
}

/***********************************************************************
	Reset routine
***********************************************************************/

static void hsc_reset(struct hsc *hsc) {
	
	register S16B i;
	S32B z,l,h,t;

	 hsc->maxclen=MAXCLEN;		
	 hsc->iec[0]=(IECLIM>>1);
	 for (i=1;i<=MAXCLEN;++i) hsc->iec[i]=(IECLIM>>1)-1;
	 hsc->dropcnt=NUMCON/4;
	 hsc->nec=0;
	 hsc->nrel=0;
	 hsc->hs[0]=0;
	 for (i=0;i<HTLEN;++i) hsc->ht[i]=NIL;	
	 for (i=0;i<NUMCON;++i) {
	hsc->eln[i]=i+1;
	hsc->elp[i]=i-1;
	hsc->cl[i]=0xff;
	hsc->nb[i]=NIL;
	 }
	 hsc->elf=0;
	 hsc->ell=NUMCON-1;
	 for (i=NUMCON;i<NUMCFB-1;++i) hsc->nb[i]=i+1;
	 hsc->nb[i]=NIL;
	 hsc->fcfbl=NUMCON;
	 hsc->curcon[3]=hsc->curcon[2]=hsc->curcon[1]=hsc->curcon[0]=0;
	 hsc->cmsp=0;
	 for (i=0;i<256;++i) hsc->cmask[i]=0;		
	 for (z=10,i=0;i<HTLEN;++i) {
	h=z/(2147483647L/16807L);
	l=z%(2147483647L/16807L);		
	if ((t=16807L*l-(2147483647L%16807L)*h)>0) z=t;
	else z=t+2147483647L;
	hsc->hrt[i]=(U16B)z&(HTLEN-1);
	 }
}

/***********************************************************************
	Setup routine
***********************************************************************/

struct hsc *hsc_setup(void) {
	
	struct hsc *hsc;

	if (hsc = (struct hsc *)AllocVec(sizeof(struct hsc),0))
	{
		 hsc->ht=AllocVec(HTLEN*sizeof(*hsc->ht),0);		
		 hsc->hp=AllocVec(NUMCON*sizeof(*hsc->hp),0);
		 hsc->elp=AllocVec(NUMCON*sizeof(*hsc->elp),0);
		 hsc->eln=AllocVec(NUMCON*sizeof(*hsc->eln),0);
		 hsc->cl=AllocVec(NUMCON*sizeof(*hsc->cl),0);
		 hsc->cc=AllocVec(NUMCON*sizeof(*hsc->cc),0);
		 hsc->ft=AllocVec(NUMCON*sizeof(*hsc->ft),0);
		 hsc->fe=AllocVec(NUMCON*sizeof(*hsc->fe),0);
		 hsc->rfm=AllocVec(NUMCON*sizeof(*hsc->rfm),0);
		 hsc->con=AllocVec(NUMCON*sizeof(*hsc->con),0);
		 hsc->fc=AllocVec(NUMCFB*sizeof(*hsc->fc),0);
		 hsc->fa=AllocVec(NUMCFB*sizeof(*hsc->fa),0);
		 hsc->nb=AllocVec(NUMCFB*sizeof(*hsc->nb),0);
		 if (hsc->hp==NULL || hsc->elp==NULL || hsc->eln==NULL ||
		hsc->cl==NULL || hsc->rfm==NULL || hsc->con==NULL ||
		hsc->cc==NULL || hsc->ft==NULL || hsc->fe==NULL ||
		hsc->fc==NULL || hsc->fa==NULL || hsc->nb==NULL || hsc->ht==NULL) {
		hsc_cleanup(hsc);
		return NULL;
		 }
	}

	return hsc;
}

/***********************************************************************
	Cleanup routine
***********************************************************************/

void hsc_cleanup(struct hsc *hsc) {
    
    if (hsc->ht!=NULL) FreeVec(hsc->ht);
    if (hsc->fc!=NULL) FreeVec(hsc->fc);
    if (hsc->fa!=NULL) FreeVec(hsc->fa);
    if (hsc->ft!=NULL) FreeVec(hsc->ft);
    if (hsc->fe!=NULL) FreeVec(hsc->fe);
    if (hsc->nb!=NULL) FreeVec(hsc->nb);
    if (hsc->hp!=NULL) FreeVec(hsc->hp);
    if (hsc->elp!=NULL) FreeVec(hsc->elp);
    if (hsc->eln!=NULL) FreeVec(hsc->eln);
    if (hsc->cl!=NULL) FreeVec(hsc->cl);
    if (hsc->cc!=NULL) FreeVec(hsc->cc);
    if (hsc->rfm!=NULL) FreeVec(hsc->rfm);
    if (hsc->con!=NULL) FreeVec(hsc->con);

	 FreeVec(hsc);
}

/***********************************************************************
	Encoding
***********************************************************************/

#define code_byte(h,cp,c) (hsc->cmsp?code_rest(h,cp,c):code_first(h,cp,c))

U32B hsc_pack(struct hsc *hsc, unsigned char *ibuf, U32B ilen, unsigned char *obuf, U32B olen) {

    S16B c;
    U16B cp;
    unsigned char ncmax,ncmin;
    U32B plen=0;

	hsc_reset(hsc);    
	if (hsc->haio = init_io(ibuf,ilen,obuf,olen))
	{
    if (hsc->ac = ac_init_encode(hsc->haio))
	 {
    for (;(c=getbyte(hsc->haio))>=0;) {
	cp=find_longest(hsc);
	ncmin=cp==NIL?0:hsc->cl[cp]+1;
	ncmax=hsc->maxclen+1;
	for(;;) {
	    if (cp==NIL) {
		code_new(hsc,c);
		break;
	    }			
	    if (code_byte(hsc,cp,c)) {
		el_movefront(hsc,cp);
		break;
	    }		
	    cp=find_next(hsc);
	}
	add_model(hsc,c);
	while (ncmax>ncmin) make_context(hsc,--ncmax,c);
	move_context(c);
    }
    cp=find_longest(hsc);
    while (cp!=NIL) {
	code_byte(hsc,cp,ESC);
	cp=find_next(hsc);
    }
    code_new(hsc,ESC);
    ac_end_encode(hsc->ac,hsc->haio);
	 }
    plen=end_io(hsc->haio);
	}
    return plen;
}

/***********************************************************************
	Decoding
***********************************************************************/

#define decode_byte(h,cp) (hsc->cmsp?decode_rest(h,cp):decode_first(h,cp))

U32B hsc_unpack(struct hsc *hsc, unsigned char *ibuf, U32B ilen, unsigned char *obuf, U32B olen) {

    S16B c;
    U16B cp;
    unsigned char ncmax,ncmin;
    U32B ulen=0;
    
	hsc_reset(hsc);    
	if (hsc->haio = init_io(ibuf,ilen,obuf,olen))
	{
    if (hsc->ac = ac_init_decode(hsc->haio))
	 {
    for (;;) {
	cp=find_longest(hsc); 
	ncmin=cp==NIL?0:hsc->cl[cp]+1;
	ncmax=hsc->maxclen+1;
	for(;;) {
	    if (cp==NIL) {
		c=decode_new(hsc);
		break;
	    }			
	    if ((c=decode_byte(hsc,cp))!=ESC) {
		el_movefront(hsc,cp);
		break;
	    }		
	    cp=find_next(hsc);
	}
	if (c==ESC) break;
	add_model(hsc,c);
	while (ncmax>ncmin) make_context(hsc,--ncmax,c);
	putbyte(hsc->haio,c);
	move_context(c);
    }
    ac_end_decode(hsc->ac,hsc->haio);
	 }
    ulen=end_io(hsc->haio);
	}
    return ulen;
}

