/***********************************************************************
  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
***********************************************************************/

/***********************************************************************
This file contains some small changes made by Nico de Vries (AIP-NL)
allowing it to be compiled with Borland C++ 3.1.
***********************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include "ha.h"
#include "haio.h"
#include "acoder.h"

static U16B h,l,v;
static S16B s;
static S16B gpat,ppat;

/***********************************************************************
  Bit I/O
***********************************************************************/

#define putbit(b) 	{ ppat<<=1;				\
			  if (b) ppat|=1;			\
			  if (ppat&0x100) {			\
				putbyte(ppat&0xff);		\
				ppat=1;				\
			  }					\
			}


#define getbit(b) 	{ gpat<<=1;				\
			  if (!(gpat&0xff)) {			\
				gpat=getbyte();			\
				if (gpat&0x100) gpat=0x100;	\
				else {				\
					gpat<<=1;		\
					gpat|=1;		\
				}				\
			  }					\
			  b|=(gpat&0x100)>>8;			\
			}


/***********************************************************************
  Arithmetic encoding
***********************************************************************/

#ifndef __BORLANDC__

void ac_out(U16B low, U16B high, U16B tot) {
    
    register U32B r;
    
    r=(U32B)(h-l)+1;
    h=(U16B)(r*high/tot-1)+l;
    l+=(U16B)(r*low/tot);
    if (!((h^l)&0x8000)) {
	putbit(l&0x8000);
	while(s) {
	    --s;
	    putbit(~l&0x8000);
	}
	l<<=1;
	h<<=1;
	h|=1;
	while (!((h^l)&0x8000)) {
	    putbit(l&0x8000);
	    l<<=1;
	    h<<=1;
	    h|=1;
	}
    }
    while ((l&0x4000)&&!(h&0x4000)) {
	++s;
	l<<=1;
	l&=0x7fff;
	h<<=1;
	h|=0x8001;
    }
}

#else

void ac_out(U16B low, U16B high, U16B tot) {

    asm {
	mov ax,h
	mov bx,l
	sub ax,bx
	add ax,1
	mov cx,ax
        jc  aco0
	mul high
        jmp aco1
    }
aco0:
    asm {
        mov dx,high
    }
aco1:
    asm {
        cmp dx,tot
	je  aco2
        div tot
        sub ax,1
        jmp aco3
    }
aco2:
    asm {
	mov ax,0xffff
    }
aco3:
    asm {
        add ax,bx
	mov h,ax
	mov ax,cx
	cmp ax,0
        je  aco4
        mul low
        jmp aco5
    }
aco4:
    asm {
        mov dx,low
    }
aco5:
    asm {
	div tot
	add l,ax
    }
    if (!((h^l)&0x8000)) {
	putbit(l&0x8000);
	while(s) {
	    --s;
	    putbit(~l&0x8000);
	}
	l<<=1;
	h<<=1;
	h|=1;
	while (!((h^l)&0x8000)) {
	    putbit(l&0x8000);
	    l<<=1;
	    h<<=1;
	    h|=1;
	}
    }
    while ((l&0x4000)&&!(h&0x4000)) {
	++s;
	l<<=1;
	l&=0x7fff;
	h<<=1;
	h|=0x8001;
    }
}

#endif

void ac_init_encode(void) {

    h=0xffff;
    l=s=0;
    ppat=1;
}

void ac_end_encode(void) {

    ++s;
    putbit(l&0x4000);
    while (s--) {
	putbit(~l&0x4000);
    }
    if (ppat==1) {
	flush();
	return;
    }
    while(!(ppat&0x100)) ppat<<=1;
    putbyte(ppat&0xff);
    flush();
}


/***********************************************************************
  Arithmetic decoding
***********************************************************************/

#ifndef __BORLANDC__

void ac_in(U16B low, U16B high, U16B tot) {

    register U32B r;

    r=(U32B)(h-l)+1;
    h=(U16B)(r*high/tot-1)+l;
    l+=(U16B)(r*low/tot);
    while (!((h^l)&0x8000)) {
	l<<=1;
	h<<=1;
	h|=1;
	v<<=1;
	getbit(v);
    }
    while ((l&0x4000)&&!(h&0x4000)) {
	l<<=1;
	l&=0x7fff;
	h<<=1;
	h|=0x8001;
	v<<=1;
	v^=0x8000;
	getbit(v);
    }
}

U16B ac_threshold_val(U16B tot) {
	
    register U32B r;
    
    r=(U32B)(h-l)+1;
    return (U16B)((((U32B)(v-l)+1)*tot-1)/r);
}

#else

void ac_in(U16B low, U16B high, U16B tot) {

    asm {
	mov ax,h
	mov bx,l
	sub ax,bx
	add ax,1
	mov cx,ax
        jc  aci0
	mul high
        jmp aci1
    }
aci0:
    asm {
        mov dx,high
    }
aci1:
    asm {
        cmp dx,tot
	je  aci2
        div tot
        sub ax,1
        jmp aci3
    }
aci2:
    asm {
	mov ax,0xffff
    }
aci3:
    asm {
        add ax,bx
	mov h,ax
	mov ax,cx
	cmp ax,0
        je  aci4
        mul low
        jmp aci5
    }
aci4:
    asm {
        mov dx,low
    }
aci5:
    asm {
	div tot
	add l,ax
    }
    while (!((h^l)&0x8000)) {
	l<<=1;
	h<<=1;
	h|=1;
	v<<=1;
	getbit(v);
    }
    while ((l&0x4000)&&!(h&0x4000)) {
	l<<=1;
	l&=0x7fff;
	h<<=1;
	h|=0x8001;
	v<<=1;
	v^=0x8000;
	getbit(v);
    }
}

U16B ac_threshold_val(U16B tot) {

    U16B rv;

    asm {
	mov ax,h
	sub ax,l
	add ax,1
	jc  atv1
	mov cx,ax
	mov ax,v
	sub ax,l
	inc ax
	mul [tot]
	sub ax,1
	sbb dx,0
	div cx
	mov rv,ax
	jmp atv3
    }
atv1:
    asm {
	mov ax,v
	sub ax,l
	add ax,1
	jc  atv2
	mul [tot]
	sub ax,1
	sbb dx,0
	mov rv,dx
	jmp atv3
    }
atv2:
    asm {
	mov ax,tot
	dec ax
	mov rv,dx
    }
atv3:
    return rv;
}

#endif

void ac_init_decode(void) {

    h=0xffff;
    l=0;
    gpat=0;
    v=getbyte()<<8;
    v|=0xff&getbyte();
}

