/*TITLE arithmetic encoding routines, assembly multiply/divide*/

/****keyword-flag*** "%v %f %n" */
/* "2.15.1 13-Aug-94,15:51:28 ARENC1.C" */


#include "arith.h"
#include "model.h"
#include <stdio.h>

extern FILE *outfile;
int buffer;
int bits_to_go;
unsigned low,high;
unsigned bits_to_follow;


#define output_0\
{\
	buffer >>= 1;\
	bits_to_go --;\
	if (bits_to_go == 0)\
		{\
		fputc(buffer,outfile);\
		bits_to_go = 8;\
		}\
}


#define output_1\
{\
	buffer >>= 1;\
	buffer |= 0x80;\
	bits_to_go --;\
	if (bits_to_go == 0)\
		{\
		fputc(buffer,outfile);\
		bits_to_go = 8;\
		}\
}


#define bit_plus_follow_0 \
{ \
	output_0\
	while (bits_to_follow > 0) \
		{ \
		output_1\
		bits_to_follow --; \
		} \
}

#define bit_plus_follow_1 \
{ \
	output_1\
	while (bits_to_follow > 0) \
		{ \
		output_0\
		bits_to_follow --; \
		} \
}


start_encoding()
{
	low = 0;
	high = TOP_VALUE;
	bits_to_follow = 0;
	return(0);
}


encode_symbol(unsigned symbol, unsigned oldch)
{
	long range;
	int i;
	unsigned cum;
	unsigned prev_cum;
	unsigned char *freq_ptr;
	int current_pair;
	unsigned char high_half;
	unsigned high_half_weight;
	unsigned total_pair_weight;
	unsigned total_freq;
	FrequencyInfo *temp_freq_info;

	range = (long)(high-low)+1;
	temp_freq_info = frequency_info[oldch];
	freq_ptr = temp_freq_info->freq;
	total_freq = temp_freq_info->total_freq;
	prev_cum = 0;
	for (i = 0; i < symbol/2; i ++)
		{
		current_pair = *(freq_ptr++);
		total_pair_weight = both_weights[current_pair];
		prev_cum += total_pair_weight;
		}
	if (symbol % 2 == 0)  /* if even, prev cum is ok, update cum */
		{
		current_pair = *freq_ptr;
		high_half = (current_pair & HIGH_MASK) >> HIGH_SHIFT;
		high_half_weight = translate[high_half];
		cum = prev_cum + high_half_weight;
		}
	else /* if odd, update both */
		{
		current_pair = *freq_ptr;
		total_pair_weight = both_weights[current_pair];
		cum = prev_cum + total_pair_weight;
		high_half = (current_pair & HIGH_MASK) >> HIGH_SHIFT;
		high_half_weight = translate[high_half];
		prev_cum += high_half_weight;
		}

	if (range == 65536L)
		{
		high = low + (range * cum)/total_freq-1;
		low = low + (range * prev_cum)/total_freq;
		}
	else
		{
		asm mov bx,word ptr range;		/* get the range */
		asm mov cx,total_freq; /* get the total freq for two uses */
		asm mov si,low;		/* same for low value */
		asm mov ax,bx;		/* copy range for multiply */
		asm mul word ptr cum;	/* dx:ax = range * cum */
		asm div cx;			/* ax = (range * cum) / total_freq */
		asm dec ax;			/* ax = (range * cum) / total_freq - 1*/
		asm add ax,si;		/* ax = low + (range * cum) / total_freq - 1 */
		asm mov high,ax;		/* save the result in high */
		asm mov ax,bx;		/* get the range again */
		asm mul word ptr prev_cum; /* dx:ax = range * prev_cum */
		asm div cx;			/* ax = (range * prev_cum) / total_freq */
		asm add ax,si;		/* ax = low + (range * prev_cum) / total_freq */
		asm mov low,ax;		/* save the result in low */
		}

	for (;;)
		{
		if (high < HALF)
			bit_plus_follow_0
		else if (low >= HALF)
			{
			bit_plus_follow_1
			low -= HALF;
			high -= HALF;
			}
		else if (low >= FIRST_QTR && high < THIRD_QTR)
			{
			bits_to_follow ++;
			low -= FIRST_QTR;
			high -= FIRST_QTR;
			}
		else break;

		low <<= 1;
		high <<= 1;
		high ++;
		}
	return(0);
}


done_encoding()
{
	bits_to_follow ++;
	if (low < FIRST_QTR)
		bit_plus_follow_0
	else
		bit_plus_follow_1
	return(0);
}


start_outputing_bits()
{
	buffer = 0;
	bits_to_go = 8;
	return(0);
}


done_outputing_bits()
{
	fputc(buffer>>bits_to_go,outfile);
	return(0);
}


