/**********************************************************************
MPEG-4 Audio VM

This software module was originally developed by
  Y.B. Thomas Kim and S.H. Park (Samsung AIT)
and edited by
  Y.B. Thomas Kim (Samsung AIT) on 1997-11-06

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. Those intending to use this software module in hardware or
software products are advised that this use may infringe existing
patents. The original developer of this software module and his/her
company, the subsequent editors and their companies, and ISO/IEC have
no liability for use of this software module or modifications thereof
in an implementation. Copyright is not released for non MPEG-2
NBC/MPEG-4 Audio conforming products. The original developer retains
full right to use the code for his/her own purpose, assign or donate
the code to a third party and to inhibit third party from using the
code for non MPEG-2 NBC/MPEG-4 Audio conforming products. This
copyright notice must be included in all copies or derivative works.

Copyright (c) 1997.

**********************************************************************/
/* ARITHMETIC DECODING ALGORITHM. */

#include <stdio.h>
#include <math.h>
#include "sam_facoding.h"
#include "sam_dec.h"

/* CURRENT STATE OF THE ENCODING */

static code_value value;	/* Currently-seen code value         	*/
static code_value low, high; 	/* Ends of the current code region.	*/

static int	acode_btstr[16];
static int	read_bits;
static int	target_bits;
static int	ar_error;

static int get_acode_from_bs(int, int *, int, int);

/* START ENCODING A STREAM OF SYMBOLS. */
void sam_start_decoding(int ubits, int target)
{
	int	i;

	value = 0;
	low = 0;		/* full code range. */
	high = 0;

	read_bits = ubits;
	target_bits = target;

	for(i = 0; i < 16; i++)
		acode_btstr[i] = 0;

	get_acode_from_bs(16, acode_btstr, read_bits, target_bits);
	read_bits += 16;

	ar_error = 0;
}

/* DECODE THE NEXT SYMBOL. */
int sam_decode_symbol (
	int armodel[],
	int *symbol,
	double *estimated)
{
	long range;		/* Size of the current code region */
	int  cum;		/* Cumulative frequency calculated */
	int	 symbol_size, sym;
	int  res;
	double 	prob;
	
	if(ar_error) return -1;

	symbol_size = 0;

	while (1) {
		if (high < Half) {
			/* nothing */
		}
		else if (low >= Half ) {
			low -= Half;
			high -= Half;
			value -= Half;
		}
		else if (low >= First_qtr &&
			 high <Third_qtr) {
			low -= First_qtr;
			high -= First_qtr;
			value -= First_qtr;
		}
		else 
			break;

		low = 2*low;		/* Scale up code range. */
		high = 2*high+1;
		value = 2*value;
		
		if (acode_btstr[symbol_size]<0) {
			ar_error = -1;
			break;
		} else if (acode_btstr[symbol_size]) value += 1; /* Move in next input bit. */
		symbol_size++;
	}

	if(ar_error) return ar_error;

	range = (long)(high-low) + 1;
	cum = (((long)(value-low)+1)*16384-1)/range; /* Find cum freq */

	for (sym=0; armodel[sym]>cum; sym++) ;

	/* Narrow the code region to that allotted to this symbol. */ 
	if (sym>0) {
		high  = low + (range * armodel[sym-1])/16384 - 1;
	}
	low  = low + (range * armodel[sym])/16384;

	ar_error = get_acode_from_bs(symbol_size, acode_btstr, read_bits, target_bits);
	read_bits += symbol_size;

	if (sym>0)
		prob = (double)(armodel[sym-1]-armodel[sym])/16384;
	else
		prob = (double)(16384-armodel[sym])/16384;

	*estimated += -log(prob)/log((double)2.);

	*symbol = sym;

	return ar_error;
}

static int get_acode_from_bs(int btstr_len,
	int *btstr,
	int cur_frame_pos,
	int frame_len)
{
	int j, pos;

	pos = cur_frame_pos;

	/* shift bitstream which is unused */
	for (j=btstr_len; j<16; j++)
		btstr[j-btstr_len] = btstr[j];

	for (j=16-btstr_len; j<16; j++, pos++) {
	    if (pos < frame_len)
			btstr[j] = sam_GetBits(1);
	    else {
			btstr[j] = 0; 
			return 1;
		}
	}
	return 0;
}
