
/*!
 *************************************************************************************
 * \file biariencode.c
 *
 * \brief
 *   Routines for binary arithmetic encoding.
 *
 *   This modified implementation of the M Coder is based on JVT-U084 
 *   with the choice of M_BITS = 16.
 *
 * \author
 *    Main contributors (see contributors.h for copyright, address and affiliation details)
 *    - Detlev Marpe                    <marpe@hhi.de>
 *    - Gabi Blaettermann
 *    - Gunnar Marten
 *************************************************************************************
 */

#include "global.h"
#include "biariencode.h"

//added by zhb
const unsigned short lgprobaddvl[64][2] = {
{  4096, 4096},{  4493, 3744},{  4735, 3552},{  5120, 3232},
{  5374, 3072},{  5632, 2880},{  5973, 2688},{  6271, 2528},
{  6689, 2304},{  7042, 2144},{  7424, 1984},{  7425, 1984},
{  7939, 1792},{  7869, 1824},{  8534, 1600},{  8832, 1504},
{  9086, 1440},{  9087, 1440},{  9857, 1248},{  9598, 1312},
{ 10240, 1152},{ 10430, 1120},{ 10563, 1088},{ 10885, 1024},
{ 10898, 1024},{ 11480,  928},{ 11486,  928},{ 12011,  832},
{ 12481,  768},{ 12483,  768},{ 12928,  704},{ 13242,  672},
{ 13503,  640},{ 14053,  576},{ 14051,  576},{ 14048,  576},
{ 14402,  544},{ 14720,  512},{ 14721,  512},{ 15135,  480},
{ 15904,  416},{ 15518,  448},{ 15936,  416},{ 16385,  384},
{ 16415,  384},{ 16385,  384},{ 17438,  320},{ 17408,  320},
{ 17409,  320},{ 17411,  320},{ 17425,  320},{ 17426,  320},
{ 18704,  256},{ 18706,  256},{ 18707,  256},{ 18717,  256},
{ 18718,  256},{ 18716,  256},{ 19518,  224},{ 19519,  224},
{ 19517,  224},{ 19519,  224},{ 22780,  128},{ 22780,  128}};
//{  4192, 4032},{  4461, 3776},{  4730, 3552},{  4999, 3328},
//{  5364, 3072},{  5633, 2880},{  5934, 2720},{  6267, 2528},
//{  6792, 2272},{  7061, 2144},{  7426, 1986},{  7439, 1984},
//{  7964, 1792},{  8041, 1760},{  8630, 1568},{  8835, 1504},
//{  9072, 1440},{  9213, 1408},{  9994, 1216},{  9975, 1216},
//{ 10244, 1152},{ 10737, 1056},{ 10910, 1024},{ 11050,  992},
//{ 11287,  960},{ 12228,  800},{ 12017,  832},{ 12254,  800},
//{ 12939,  704},{ 13208,  672},{ 13189,  672},{ 13778,  608},
//{ 14079,  576},{ 14380,  544},{ 14745,  512},{ 14726,  512},
//{ 15507,  448},{ 15488,  448},{ 15501,  448},{ 16378,  384},
//{ 16391,  384},{ 16372,  384},{ 17409,  320},{ 17422,  320},
//{ 18043,  288},{ 17416,  320},{ 18037,  288},{ 18690,  256},
//{ 18703,  256},{ 19484,  224},{ 19465,  224},{ 19478,  224},
//{ 20355,  192},{ 20368,  192},{ 20381,  192},{ 21482,  160},
//{ 21495,  160},{ 21476,  160},{ 22769,  128},{ 22782,  128},
//{ 22763,  128},{ 22776,  128},{ 22757,  128},{ 24434,   96}};
const unsigned short subinterval_division[128] = {
0xfffe, 0xfe9e, 0xfd3e, 0xfbde, 0xfa82, 0xf928, 0xf7d0, 0xf67a,
0xf524, 0xf3d2, 0xf280, 0xf132, 0xefe4, 0xee98, 0xed4e, 0xec06,
0xeac0, 0xe97c, 0xe838, 0xe6f8, 0xe5b8, 0xe47a, 0xe33e, 0xe204,
0xe0cc, 0xdf96, 0xde60, 0xdd2c, 0xdbfa, 0xdaca, 0xd99c, 0xd870,
0xd744, 0xd61a, 0xd4f2, 0xd3cc, 0xd2a8, 0xd184, 0xd062, 0xcf42,
0xce24, 0xcd06, 0xcbec, 0xcad2, 0xc9b8, 0xc8a2, 0xc78c, 0xc678,
0xc566, 0xc456, 0xc346, 0xc238, 0xc12c, 0xc020, 0xbf16, 0xbe0e,
0xbd08, 0xbc02, 0xbafe, 0xb9fc, 0xb8fa, 0xb7fa, 0xb6fc, 0xb600,
0xb504, 0xb40a, 0xb310, 0xb21a, 0xb122, 0xb02e, 0xaf3a, 0xae48,
0xad58, 0xac68, 0xab7a, 0xaa8c, 0xa9a0, 0xa8b6, 0xa7cc, 0xa6e4,
0xa5fe, 0xa518, 0xa434, 0xa352, 0xa270, 0xa18e, 0xa0b0, 0x9fd2,
0x9ef4, 0x9e18, 0x9d3e, 0x9c64, 0x9b8c, 0x9ab6, 0x99e0, 0x990a,
0x9836, 0x9764, 0x9694, 0x95c2, 0x94f4, 0x9426, 0x935a, 0x928e,
0x91c2, 0x90fa, 0x9030, 0x8f6a, 0x8ea4, 0x8dde, 0x8d1a, 0x8c56,
0x8b94, 0x8ad4, 0x8a14, 0x8954, 0x8898, 0x87da, 0x871e, 0x8664,
0x85aa, 0x84f0, 0x843a, 0x8382, 0x82cc, 0x8218, 0x8164, 0x80b0};

#define dvsn_interval(x) ((subinterval_division[127&((x)>>5)]<<8)>>((x)>>12))

// Range table for LPS
static const byte renorm_table_32[32]={6,5,4,4,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};


void reset_pic_bin_count(VideoParameters *p_Vid)
{
  p_Vid->pic_bin_count = 0;
}

int get_pic_bin_count(VideoParameters *p_Vid)
{
  return p_Vid->pic_bin_count;
}

/*!
 ************************************************************************
 * \brief
 *    Allocates memory for the EncodingEnvironment struct
 ************************************************************************
 */
EncodingEnvironmentPtr arienco_create_encoding_environment(void)
{
  EncodingEnvironmentPtr eep;

  if ( (eep = (EncodingEnvironmentPtr) calloc(1,sizeof(EncodingEnvironment))) == NULL)
    no_mem_exit("arienco_create_encoding_environment: eep");

  return eep;
}

/*!
 ************************************************************************
 * \brief
 *    Frees memory of the EncodingEnvironment struct
 ************************************************************************
 */
void arienco_delete_encoding_environment(EncodingEnvironmentPtr eep)
{
  if (eep == NULL)
  {
    snprintf(errortext, ET_SIZE, "Error freeing eep (NULL pointer)");
    error (errortext, 200);
  }
  else
    free(eep);
}

#define smblcnt(eep) (eep->E*8+eep->C)
void encode_bitstream(EncodingEnvironmentPtr parth)
{
	unsigned int isbigthan32bit = ((0xffffffff-parth->upvalue)<parth->low);
	parth->upvalue += parth->low;
	if(isbigthan32bit)
	{
		parth->Ecodestrm[(*parth->Ecodestrm_len)++] = parth->backbyte+1;
		parth->bytes255--;
		if(parth->bytes255)
		{
			do
			{
				parth->Ecodestrm[(*parth->Ecodestrm_len)++] = 0;
				parth->bytes255--;
			}
			while(parth->bytes255);
		}
	}
	parth->currpos += 8;
	if(parth->currpos==32)
	{
		parth->currpos -= 8;
		if( (parth->upvalue&0xffffff00)!=0xffffff00 )
		{
			if(parth->bytes255)
			{
				parth->Ecodestrm[(*parth->Ecodestrm_len)++] = parth->backbyte;
				parth->bytes255--;
				if(parth->bytes255)
				{
					do
					{
						parth->Ecodestrm[(*parth->Ecodestrm_len)++] = 0xff;
						parth->bytes255--;
					}
					while(parth->bytes255);
				}
			}
			if( (parth->upvalue&0xffff00)!=0xffff00 )
			{
				parth->Ecodestrm[(*parth->Ecodestrm_len)++] = (parth->upvalue>>24);
				parth->upvalue &= 0xffffff;

				if( (parth->upvalue&0xff00)!=0xff00 )
				{
					parth->Ecodestrm[(*parth->Ecodestrm_len)++] = (parth->upvalue>>16);
					parth->upvalue &= 0xffff;
				}
				else
				{
					parth->backbyte = parth->upvalue>>16;
					parth->bytes255 = 1;
					parth->upvalue &= 0xffff;
				}
			}
			else
			{
				parth->backbyte = parth->upvalue>>24;
				parth->bytes255 = 2;
				parth->upvalue &= 0xffff;
			}
		}
		else
		{
			if(parth->bytes255)
			{
				parth->bytes255 += 2;
				parth->upvalue &= 0xffff;
			}
			else
			{
				parth->bytes255 = 2;
				parth->backbyte = parth->upvalue>>24;
				parth->upvalue &= 0xffff;
			}
		}
	}
	else
	{
		if( (parth->upvalue&0xff00)==0xff00 )
		{
			parth->bytes255 = 1;
			parth->backbyte = parth->upvalue>>16;
		}
		else
		{
			parth->Ecodestrm[(*parth->Ecodestrm_len)++] = (parth->upvalue>>16);
		}
		parth->upvalue &= 0xffff;
	}
	parth->upvalue <<= 16;

	while(parth->C > 7)
    {
        parth->C-=8;
        ++(parth->E);
    }
}

void encode_btrmend(EncodingEnvironmentPtr parth)
{
	unsigned int isbigthan32bit = ((0xffffffff-parth->upvalue)<parth->low);
	unsigned int uprng = parth->endhigh+parth->upvalue, valuewrite;
	int ii, endbit;

	parth->upvalue += parth->low;
	if(isbigthan32bit)
	{
		parth->Ecodestrm[(*parth->Ecodestrm_len)++] = parth->backbyte+1;
		parth->bytes255--;
		if(parth->bytes255)
		{
			do
			{
				parth->Ecodestrm[(*parth->Ecodestrm_len)++] = 0;
				parth->bytes255--;
			}
			while(parth->bytes255);
		}
	}
//printf("lownow %6x  uprng %6x\n",parth->low,uprng);
	if(parth->bytes255)
	{
		parth->Ecodestrm[(*parth->Ecodestrm_len)++] = parth->backbyte;
		parth->bytes255--;
		if(parth->bytes255)
		{
			do
			{
				parth->Ecodestrm[(*parth->Ecodestrm_len)++] = 0xff;
				parth->bytes255--;
			}
			while(parth->bytes255);
		}
	}
	if(uprng < parth->upvalue)
	{
		for(ii=23; ii>=0; ii--)
		{
			unsigned int valcur = (parth->upvalue|(1<<ii)) & (~((1<<ii)-1));
			if( valcur >= parth->upvalue)
			{
				valuewrite = valcur;
				break;
			}
		}
	}
	else
	{
		for(ii=23; ii>=0; ii--)
		{
			unsigned int valcur = (parth->upvalue|(1<<ii)) & (~((1<<ii)-1));
			unsigned int addvalff = 0;
			if(ii>=16)
				addvalff = 0xffff;
			else if(ii>=8)
				addvalff = 0xff;
			if( (valcur>=parth->upvalue) && (valcur+addvalff<uprng) )
			{
				valuewrite = valcur;
				break;
			}
		}
	}
	if(parth->currpos==24)
	{
		parth->Ecodestrm[(*parth->Ecodestrm_len)++] = (valuewrite>>24);
	}
	parth->Ecodestrm[(*parth->Ecodestrm_len)++] = (valuewrite>>16);

	if(ii<16)
	{
		parth->Ecodestrm[(*parth->Ecodestrm_len)++] = (valuewrite>>8);
	}
	if(ii<8)
	{
		parth->Ecodestrm[(*parth->Ecodestrm_len)++] = (valuewrite>>0);
	}
	
	while(parth->C > 7)
    {
        parth->C-=8;
        ++(parth->E);
    }
}
void renorm_logprob(EncodingEnvironmentPtr parth)
{
	int logprb = parth->rngprob>>5;
	int sel = logprb&127, shft = logprb>>7, rsft = 7-shft;

	unsigned int val22 = subinterval_division[sel]>>shft, valact = val22&0xff00, val33;
	int diff2;
	val22 &= 0xff;
	diff2 = ((val22+(1<<rsft))>>(1+rsft))+1;

	logprb += diff2;
	val33 = subinterval_division[logprb&127]>>(logprb>>7);
	if(val33 > valact)
		diff2 += ((diff2+shft+1)>>2);
	else if(sel<30)
		diff2 -= (diff2>>3);

	parth->rngprob += (diff2<<5);
//printf("sel %d  shft %3x  valact %4x valff %2x diff2 %2x cur %4x\n",
//sel,shft,valact,val22,diff2,subNtrvlDvsn[(parth->rngprob>>5)&127]>>(parth->rngprob>>12));
}
void encode_lowerrange(EncodingEnvironmentPtr parth, int prbadd)
{
	int rngprob = parth->rngprob;
	if(rngprob+prbadd >= 0x10000)
	{
		parth->rngprob = rngprob+prbadd-0x10000;
		encode_bitstream(parth);
		parth->low = 0;
		renorm_logprob(parth);
	}
	else
		parth->rngprob += prbadd;
}
void encode_higherrange(EncodingEnvironmentPtr parth, int prbadd0, int prbadd1)//highpart:prbadd0  lowpart:prbadd1
{
	int rngprob = parth->rngprob;
	parth->low += dvsn_interval(prbadd1+rngprob);
	if(rngprob+prbadd0 >= 0x10000)
	{
		parth->rngprob = rngprob+prbadd0-0x10000;
		encode_bitstream(parth);
		parth->low = 0;
		renorm_logprob(parth);
	}
	else
		parth->rngprob += prbadd0;
}
/*!
************************************************************************
* \brief
*    Initializes the EncodingEnvironment E and C values to zero
************************************************************************
*/
void arienco_reset_EC(EncodingEnvironmentPtr eep)
{
  eep->E = 0;
  eep->C = 0;
}

/*!
 ************************************************************************
 * \brief
 *    Initializes the EncodingEnvironment for the arithmetic coder
 ************************************************************************
 */
void arienco_start_encoding(EncodingEnvironmentPtr eep,
                            unsigned char *code_buffer,
                            int *code_len )
{
  eep->Ecodestrm = code_buffer;
  eep->Ecodestrm_len = code_len;
  eep->Ebits_to_go = 8;

    //added by zhb
    eep->low = 0;
	eep->rngprob = 0;
	eep->bytes255 = eep->upvalue = eep->backbyte = 0; 
	eep->currpos=16;
symblcntnow=0;
}

/*!
************************************************************************
* \brief
*    add slice bin number to picture bin counter
*    should be only used when slice is terminated
************************************************************************
*/
void set_pic_bin_count(VideoParameters *p_Vid, EncodingEnvironmentPtr eep)
{
  p_Vid->pic_bin_count += (eep->E << 3) + eep->C; // no of processed bins
}

/*!
 ************************************************************************
 * \brief
 *    Terminates the arithmetic codeword, writes stop bit and stuffing bytes (if any)
 ************************************************************************
 */
void arienco_done_encoding(Macroblock *currMB, EncodingEnvironmentPtr eep)
{
  BitCounter *mbBits = &currMB->bits;

  //added by zhb
  encode_btrmend(eep);
  mbBits->mb_stuffing += 8-(eep->rngprob>>12);
  if(eep->rngprob>=0x7000)
	  mbBits->mb_stuffing += 8;

  eep->Ebits_to_go = 8;
}


/*!
 ************************************************************************
 * \brief
 *    Actually arithmetic encoding of one binary symbol by using
 *    the probability estimate of its associated context model
 ************************************************************************
 */
void biari_encode_symbol(EncodingEnvironmentPtr eep, int symbol, BiContextTypePtr bi_ct )
{
static const byte AC_next_state_MPS_64[64] =
{
  1,2,3,4,5,6,7,8,9,10,
  11,12,13,14,15,16,17,18,19,20,
  21,22,23,24,25,26,27,28,29,30,
  31,32,33,34,35,36,37,38,39,40,
  41,42,43,44,45,46,47,48,49,50,
  51,52,53,54,55,56,57,58,59,60,
  61,62,62,63
};

static const byte AC_next_state_LPS_64[64] =
{
  0, 0, 1, 2, 2, 4, 4, 5, 6, 7,
  8, 9, 9,11,11,12,13,13,15,15,
  16,16,18,18,19,19,21,21,22,22,
  23,24,24,25,26,26,27,27,28,29,
  29,30,30,30,31,32,32,33,33,33,
  34,34,35,35,35,36,36,36,37,37,
  37,38,38,63
};

  ++(eep->C);
  bi_ct->count += eep->p_Vid->cabac_encoding;

  /* covers all cases where code does not bother to shift down symbol to be 
  * either 0 or 1, e.g. in some cases for cbp, mb_Type etc the code simply 
  * masks off the bit position and passes in the resulting value */
  //symbol = (short) (symbol != 0);
  //added by zhb
  if ((symbol != 0) == bi_ct->MPS)  //MPS
  {
	  encode_lowerrange(eep, lgprobaddvl[bi_ct->state][1]);

	  bi_ct->state = AC_next_state_MPS_64[bi_ct->state]; // next state
  }
  else         //LPS
  {
	encode_higherrange(eep, lgprobaddvl[bi_ct->state][0], lgprobaddvl[bi_ct->state][1]);

	if (!bi_ct->state)
	  bi_ct->MPS ^= 0x01;               // switch MPS if necessary
	bi_ct->state = AC_next_state_LPS_64[bi_ct->state]; // next state
  }
symblcntnow++;
//if(currpocforcabac==176&&smblcnt(eep)>=148000)printf("smbl %d rngprob %6x low %5x\n",symbol,eep->rngprob,eep->low);
}

/*!
 ************************************************************************
 * \brief
 *    Arithmetic encoding of one binary symbol assuming 
 *    a fixed prob. distribution with p(symbol) = 0.5
 ************************************************************************
 */
void biari_encode_symbol_eq_prob(EncodingEnvironmentPtr eep, int symbol)
{
  ++(eep->C);

  //added by zhb
  if(symbol == 0)
	  encode_lowerrange(eep, 4096);
  else
	  encode_higherrange(eep, 4096, 4096);
symblcntnow++;
//if(currpocforcabac==176&&smblcnt(eep)>=148000)printf("eqsl %d rngprob %6x low %5x\n",symbol,eep->rngprob,eep->low);
}

/*!
 ************************************************************************
 * \brief
 *    Arithmetic encoding for last symbol before termination
 ************************************************************************
 */
void biari_encode_symbol_final(EncodingEnvironmentPtr eep, int symbol)
{
  ++(eep->C);

  //added by zhb
  if(symbol == 0)
  {
	  //encode_lowerrange(eep, 96);
	  eep->rngprob += 32;
	  eep->rngprob &= (~31);

	    if(eep->rngprob >= 0x10000)
		{
			eep->rngprob -= 0x10000;
			encode_bitstream(eep);
			eep->low = 0;
			renorm_logprob(eep);
		}
  }
  else
  {
	  eep->endhigh = eep->low + dvsn_interval(eep->rngprob);
	  eep->low += dvsn_interval(eep->rngprob+32);
//printf("low %10x  endhigh %10x\n",eep->low,eep->endhigh);
	  //encode_higherrange(eep, 24434, 96);
  }
symblcntnow++;
//if(currpocforcabac==176&&smblcnt(eep)>=148000)printf("edbl %d rngprob %6x low %5x\n",symbol,eep->rngprob,eep->low);
//if(currpocforcabac==176&&symbol==1)scanf("%d",&symbol);
}

/*!
 ************************************************************************
 * \brief
 *    Initializes a given context with some pre-defined probability state
 ************************************************************************
 */
void biari_init_context (int qp, BiContextTypePtr ctx, const char* ini)
{
  int pstate = ((ini[0]* qp) >> 4) + ini[1];

  if ( pstate >= 64 )
  {
    pstate = imin(126, pstate);
    ctx->state = (uint8) (pstate - 64);
    ctx->MPS   = 1;
  }
  else
  {
    pstate = imax(1, pstate);
    ctx->state = (uint8) (63 - pstate);
    ctx->MPS   = 0;
  }

  ctx->count = 0;
}

