/***********************************************************************
  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 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 arithmetic coder
***********************************************************************/

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


/***********************************************************************
  Bit I/O
***********************************************************************/

#define putbit(a,h,b) 	{ a->ppat<<=1;				\
			  if (b) a->ppat|=1;			\
			  if (a->ppat&0x100) {			\
				putbyte(h,a->ppat&0xff);		\
				a->ppat=1;				\
			  }					\
			}


#define getbit(a,h,b) 	{ a->gpat<<=1;				\
			  if (!(a->gpat&0xff)) {			\
				a->gpat=getbyte(h);			\
				if (a->gpat&0x100) a->gpat=0x100;	\
				else {				\
					a->gpat<<=1;		\
					a->gpat|=1;		\
				}				\
			  }					\
			  b|=(a->gpat&0x100)>>8;			\
			}


/***********************************************************************
  Arithmetic encoding
***********************************************************************/

void ac_out(struct acoder *ac, struct haio *haio, U16B low, U16B high, U16B tot) {
    
    register U32B r;
    
    r=(U32B)(ac->h-ac->l)+1;
    ac->h=(U16B)(r*high/tot-1)+ac->l;
    ac->l+=(U16B)(r*low/tot);
    if (!((ac->h^ac->l)&0x8000)) {
	putbit(ac,haio,ac->l&0x8000);
	while(ac->s) {
	    --ac->s;
	    putbit(ac,haio,~ac->l&0x8000);
	}
	ac->l<<=1;
	ac->h<<=1;
	ac->h|=1;
	while (!((ac->h^ac->l)&0x8000)) {
	    putbit(ac,haio,ac->l&0x8000);
	    ac->l<<=1;
	    ac->h<<=1;
	    ac->h|=1;
	}
    }
    while ((ac->l&0x4000)&&!(ac->h&0x4000)) {
	++ac->s;
	ac->l<<=1;
	ac->l&=0x7fff;
	ac->h<<=1;
	ac->h|=0x8001;
    }
}

struct acoder *ac_init_encode(struct haio *haio) {

    struct acoder *ac;

	 if (ac = (struct acoder *)AllocVec(sizeof(struct acoder),0))
	 {
		 ac->h=0xffff;
		 ac->l=ac->s=0;
		 ac->ppat=1;
	 }

	 return ac;
}

void ac_end_encode(struct acoder *ac, struct haio *haio) {

    ++ac->s;
    putbit(ac,haio,ac->l&0x4000);
    while (ac->s--) {
	putbit(ac,haio,~ac->l&0x4000);
    }
    if (ac->ppat!=1) {
      while(!(ac->ppat&0x100)) ac->ppat<<=1;
      putbyte(haio,ac->ppat&0xff);
    }
    flush(haio);

	 FreeVec(ac);
}


/***********************************************************************
  Arithmetic decoding
***********************************************************************/

void ac_in(struct acoder *ac, struct haio *haio, U16B low, U16B high, U16B tot) {

    register U32B r;

    r=(U32B)(ac->h-ac->l)+1;
    ac->h=(U16B)(r*high/tot-1)+ac->l;
    ac->l+=(U16B)(r*low/tot);
    while (!((ac->h^ac->l)&0x8000)) {
	ac->l<<=1;
	ac->h<<=1;
	ac->h|=1;
	ac->v<<=1;
	getbit(ac,haio,ac->v);
    }
    while ((ac->l&0x4000)&&!(ac->h&0x4000)) {
	ac->l<<=1;
	ac->l&=0x7fff;
	ac->h<<=1;
	ac->h|=0x8001;
	ac->v<<=1;
	ac->v^=0x8000;
	getbit(ac,haio,ac->v);
    }
}

U16B ac_threshold_val(struct acoder *ac, struct haio *haio, U16B tot) {
	
    register U32B r;
    
    r=(U32B)(ac->h-ac->l)+1;
    return (U16B)((((U32B)(ac->v-ac->l)+1)*tot-1)/r);
}

struct acoder *ac_init_decode(struct haio *haio) {

    struct acoder *ac;

	 if (ac = (struct acoder *)AllocVec(sizeof(struct acoder),0))
	 {
		 ac->h=0xffff;
		 ac->l=0;
		 ac->gpat=0;
		 ac->v=getbyte(haio)<<8;
		 ac->v|=0xff&getbyte(haio);
	 }

	 return ac;
}

void ac_end_decode(struct acoder *ac, struct haio *haio) {

	flush(asc->haio);

	FreeVec(ac);
}

