/*TITLE arithmetic encoding routines, no assembly */

/****keyword-flag*** "%v %f %n" */
/* "12 12-May-92,18:01:12 ARENC.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 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 --; \
		} \
}


int start_encoding()
{
	low = 0;
	high = (unsigned)TOP_VALUE;
	bits_to_follow = 0;
	return(0);
}

int encode_symbol(unsigned symbol, unsigned oldch)
{
	long range;
	unsigned 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;

	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 = (unsigned char)((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 = (unsigned char)((current_pair & HIGH_MASK) >> HIGH_SHIFT);
		high_half_weight = translate[high_half];
		prev_cum += high_half_weight;
		} /* end updating */

	range = (long)(high-low)+1;
	high = low + (unsigned)((range * cum)/total_freq-1);
	low = low + (unsigned)((range * prev_cum)/total_freq);

	for (;;)
		{
		if (high < HALF)
			bit_plus_follow_0
		else if (low >= HALF)
			{
			bit_plus_follow_1
			low -= (unsigned)HALF;
			high -= (unsigned)HALF;
			}
		else if (low >= FIRST_QTR && high < THIRD_QTR)
			{
			bits_to_follow ++;
			low -= (unsigned)FIRST_QTR;
			high -= (unsigned)FIRST_QTR;
			}
		else
			break;

		low <<= 1;
		high <<= 1;
		high ++;
		} /* end of bit output loop */

	return 0;
}

int done_encoding()
{
	bits_to_follow ++;
	if (low < FIRST_QTR)
		bit_plus_follow_0
	else
		bit_plus_follow_1
	return(0);
}


int start_outputing_bits()
{
	buffer = 0;
	bits_to_go = 8;
	return(0);
}


int output_0()
{
	buffer >>= 1;

	bits_to_go --;
	if (bits_to_go == 0)
		{
		fputc(buffer,outfile);
		bits_to_go = 8;
		}
	return(0);
}


int output_1()
{
	buffer >>= 1;
	buffer |= 0x80;

	bits_to_go --;
	if (bits_to_go == 0)
		{
		fputc(buffer,outfile);
		bits_to_go = 8;
		}
	return(0);
}


int done_outputing_bits()
{
	fputc(buffer>>bits_to_go,outfile);
	return(0);
}


