/*******************************************************************************
*   モジュール名称      :Application Common Compression関数定義               *
*   モジュールラベル    :Common App                                           *
*   タスク区分          :Common Functions                                     *
*   機能                :MWをラップする共通関数定義を行う                     *
*   使用上の注意        :特になし                                             *
*   作成日・作成者      :2012/3/22     Y.K.                                   *
*******************************************************************************/
#include 	"dpu_api.h"
#include 	"dpu_api_proto.h"
#include 	"app_core.h"
#define 	d_END_OF_STREAM 	256

// **** Local Prototype ****
int		MathCompressFile   ( unsigned char uc_data[], unsigned char uc_data2[], int i_size, MATH * );
void	MathExpandFile     ( unsigned char uc_data[], unsigned char uc_data2[], int i_size, MATH * );


// *** ARRAY in MATH ***
//	short        	totals       [258];		// 00000204
//	unsigned int	counts       [256];		// 00000400
//	unsigned char	scaled_counts[256];		// 00000100	

// ===========================================================================
// int 	app_CompMath( unsigned char uc_data[], int i_size )				**** 数値圧縮 ****
//		[Input/Output]	unsigned char	uc_data [0x1000 max]	Input Data --> Output Data
//		[return]	int											Data size	>0: compressed  
//																			0:  non-compressed (larger than input)
//																			-1: non-compressed (error)
// ===========================================================================
int 	app_CompMath( unsigned char uc_data[], unsigned char uc_data2[], int i_size, MATH * math )
{
	// **********************************************
	// COMPRESS
	// **********************************************
	math->out_size = 0;
    math->rack             = 0;
    math->mask             = 0x80;

	// **** COMPRESS! ****
	if ( MathCompressFile  ( uc_data, uc_data2, i_size, math ) ) 	return(-1);
	if ( math->mask != 0x80 ) {
		uc_data2[ math->out_size ] = (unsigned char)math->rack;
		math->out_size ++;
	}
	return ( math->out_size );
}


// *******************************************************************
// ****************** COMPRESS: General ******************************
// *******************************************************************
void MathOutputComps        		( unsigned int, int, unsigned char uc_data2[], MATH * );
void MathOutputComp         		( int,               unsigned char uc_data2[], MATH * );
//
//void MathBuild_model        		( short         * , unsigned char uc_data[] , unsigned char uc_data2[], int, MATH * );
//void MathCount_bytes        		( unsigned int  * , unsigned char uc_data[] , int );
void MathScale_counts       		( unsigned int  * , unsigned char * );
void MathBuild_totals       		( unsigned char * , short         * );
void MathOutput_counts     			( unsigned char * , unsigned char uc_data2[], MATH * );
//void MathInitialize_arithmetic_encoder	( MATH * );
void MathFlush_arithmetic_encoder	( unsigned char uc_data2[], MATH * );
void MathConvert_int_to_symbol  	( int , short  * ,          MATH *);
void MathEncode_symbol           	( unsigned char uc_data2[], MATH * );

// ****************************************************************
// ****************************************************************

int  MathCompressFile( unsigned char uc_data[], unsigned char uc_data2[], int i_size, MATH *math )
{
  int             i;
  int             c;

  // MathBuild_model( math->totals, uc_data, uc_data2, i_size, math );  
  //	MathCount_bytes  ( math->counts, uc_data, i_size );
  for ( i = 0 ; i < 256    ; i++ )  math->counts[ i ] = 0;
  for ( i = 0 ; i < i_size ; i++ )  math->counts[ (uc_data[ i ]) ]++;
  //
  MathScale_counts ( math->counts, math->scaled_counts );
  MathOutput_counts( math->scaled_counts , uc_data2, math );
  MathBuild_totals ( math->scaled_counts , math->totals );
  //
  // MathInitialize_arithmetic_encoder( math );
  math->low            = 0;
  math->high           = 0xffff;
  math->underflow_bits = 0;
  //

  for ( i = 0 ; i < i_size; i++ ) { 
    c = (int)(uc_data[i]);
    MathConvert_int_to_symbol( c, math->totals, math );    
    MathEncode_symbol( uc_data2, math );
    if (math->out_size > i_size - 10)  return(-1);   // ** Compressed-size > Input-size **
  }

  MathConvert_int_to_symbol( d_END_OF_STREAM, math->totals, math );
  MathEncode_symbol( uc_data2, math );
  MathFlush_arithmetic_encoder( uc_data2, math );
  MathOutputComps( 0L, 16, uc_data2, math );

  return( 0 );
}

// ****************************************************************
// ****************************************************************

void  MathOutputComp( int  bit, unsigned char uc_data2[], MATH *math )
{
  unsigned char m;

  m = math->mask;
  if ( bit )  math->rack |= m;
  m >>= 1;
  if ( m == 0 ) {
    uc_data2[ math->out_size ] = (unsigned char)math->rack;
    math->out_size ++;
    math->rack = 0;
    m = 0x80;
  }

  math->mask = m;
}


void  MathOutputComps( unsigned int code, int count, unsigned char uc_data2[], MATH *math )
{
  unsigned int mask;
  unsigned char m;

  m = math->mask;

  mask = 1L << ( count - 1 );
  while ( mask != 0) {
    if ( mask & code )  math->rack |= m;
    m >>= 1;
    if ( m == 0 ) {
      uc_data2[ math->out_size ] = (unsigned char)math->rack;
      math->out_size ++;
      math->rack = 0;
      m = 0x80;
    }
    mask >>= 1;
  }

  math->mask = m;
}

// ****************************************************************
// ****************************************************************
/*
void MathBuild_model( short * totals, unsigned char uc_data[], unsigned char uc_data2[], int i_size, MATH *math )
{
  MathCount_bytes  ( math->counts, uc_data, i_size );
  MathScale_counts ( math->counts, math->scaled_counts );
  MathOutput_counts( math->scaled_counts , uc_data2, math );
  MathBuild_totals ( math->scaled_counts , totals );
}
*/

/*
void MathCount_bytes( unsigned int counts[], unsigned char uc_data[], int i_size )
{
  int i;

  for ( i = 0 ; i < 256    ; i++ )  counts[ i ] = 0;
  for ( i = 0 ; i < i_size ; i++ )  counts[ (uc_data[ i ]) ]++;
}
*/

void MathScale_counts( unsigned int counts[], unsigned char scaled_counts[])
{
  int           i;
  unsigned int max_count;
  unsigned int  total;
  unsigned int scale;
  
  max_count = 0;
  for ( i = 0 ; i < 256 ; i++ )
    if ( counts[ i ] > max_count )  max_count = counts[ i ];
  scale = max_count / 256;
  scale = scale + 1;
  for ( i = 0 ; i < 256 ; i++ ) {
    scaled_counts[ i ] = (unsigned char ) ( counts[ i ] / scale );
    if ( scaled_counts[ i ] == 0 && counts[ i ] != 0 )
      scaled_counts[ i ] = 1;
  }

  total = 1;
  for ( i = 0 ; i < 256 ; i++ )  total += scaled_counts[ i ];
  if ( total > ( 32767 - 256 ) ) scale = 4;
  else if ( total > 16383 )      scale = 2;
  else                           return;
  for ( i = 0 ; i < 256 ; i++ )  scaled_counts[ i ] /= scale;
}

void MathOutput_counts( unsigned char scaled_counts[], unsigned char uc_data2[], MATH *math )
{
  int first;
  int last;
  int next;
  int i;
  int j;
  
  first = 0;
  while ( first < 255 && scaled_counts[ first ] == 0 )  first++;

  j = math->out_size;
  for ( ; first < 256 ; first = next ) {
    last = first + 1;
    for ( ; ; ) {
      for ( ; last < 256 ; last++ )
	if ( scaled_counts[ last ] == 0 )  break;
      last--;
      for ( next = last + 1; next < 256 ; next++ )
	if ( scaled_counts[ next ] != 0 )  break;
      if ( next > 255 )       	       break;
      if ( ( next - last ) > 3 )	   break;
      last = next;
    };

    uc_data2[ j ] = (unsigned char)first;  j ++;
    uc_data2[ j ] = (unsigned char)last;   j ++;
    for ( i = first ; i <= last ; i++ ) {
      uc_data2[ j ] = scaled_counts[ i ];  j ++;
    }
  }
  uc_data2[ j ] = 0;                       j ++;
  math->out_size = j;
}

void MathBuild_totals( unsigned char scaled_counts[], short totals[] )
{
  int i;

  totals[ 0 ] = 0;
  for ( i = 0 ; i < d_END_OF_STREAM ; i++ ) 
    totals[ i+1 ] = totals[ i ] + scaled_counts[ i ];
  totals[ d_END_OF_STREAM + 1 ] = totals[ d_END_OF_STREAM ] + 1;
}

// ****************************************************************
// ****************************************************************
/*
void MathInitialize_arithmetic_encoder( MATH *math)
{
  math->low            = 0;
  math->high           = 0xffff;
  math->underflow_bits = 0;
}
*/

void MathFlush_arithmetic_encoder( unsigned char uc_data2[], MATH *math )
{
  int  i;
  unsigned short l;

  l = math->low;

  MathOutputComp( l & 0x4000, uc_data2, math );
  i = math->underflow_bits + 1;
  while ( i > 0 ) {
    i--;
    MathOutputComp( ‾l & 0x4000, uc_data2, math );
  }
  math->underflow_bits = i;
}

void MathConvert_int_to_symbol( int c, short * totals, MATH *math )
{
  math->scale      = totals[ d_END_OF_STREAM + 1 ];
  math->low_count  = totals[ c ];
  math->high_count = totals[ c + 1 ];
}

void MathEncode_symbol( unsigned char uc_data2[], MATH *math )
{
  int range;
  unsigned short l;  
  unsigned short h; 
  int            u;

  h = math->high;
  l = math->low;
  u = math->underflow_bits;

  range = (int) ( h-l ) + 1;
  h = l + (unsigned short)
    (( range * math->high_count ) / math->scale - 1 );
  l = l + (unsigned short)
    (( range * math->low_count )  / math->scale );
  for ( ; ; ) {
    if ( ( h & 0x8000 ) == ( l & 0x8000 ) ) {
      MathOutputComp( h & 0x8000, uc_data2, math );
      while ( u > 0 ) {
		MathOutputComp( ‾h & 0x8000, uc_data2, math );
		u--;
      }
    }
    else if ( ( l & 0x4000 ) && !( h & 0x4000 )) {
      u  += 1;
      l  &= 0x3fff;
      h  |= 0x4000;
    } else {
      math->high = h;
      math->low  = l;
      math->underflow_bits = u;      
      return ;
    }
    l <<= 1;
    h <<= 1;
    h |= 1;
  }
  math->high = h;
  math->low  = l;
  math->underflow_bits = u;      
}



/*
// ===========================================================================
// int 	app_ExpdMath( unsigned char uc_data[], int i_size )				**** 数値圧縮 ****
//		[Input/Output]	unsigned char	uc_data [0x1000 max]	Input Data --> Output Data
//		[return]	int											Data size	>0: compressed  
//																			0:  non-compressed (larger than input)
//																			-1: non-compressed (error)
// ===========================================================================
int 	app_ExpdMath( unsigned char uc_data[], unsigned char uc_data2[], int i_size, MATH * math )
{
	math->out_size = 0;
	math->rack = 0;
  	math->mask = 0x80;

	// **** EXPAND! ****
  	MathExpandFile        ( uc_data, uc_data2, i_size, math );

	return ( math->out_size );
}


// *******************************************************************
// ****************** EXPAND: General ********************************
// *******************************************************************
int          	MathInputComp                    ( int * ,            unsigned char uc_data[], MATH * );
unsigned int 	MathInputComps                   ( int , int * ,      unsigned char uc_data[], MATH * );

void  			MathInput_counts                 ( int * , short  * , unsigned char uc_data[], MATH * );
void  			MathInitialize_arithmetic_decoder( int * ,            unsigned char uc_data[], MATH * );
void  			MathRemove_symbol_from_stream    ( int * , unsigned char uc_data[], MATH * );
void  			MathGet_symbol_scale             ( short * , MATH *);
int   			MathConvert_symbol_to_int        ( int count, short * , MATH *);
short 			MathGet_current_count            ( );

         
void MathExpandFile( unsigned char uc_data[], unsigned char uc_data2[], int i_size, MATH *math )
{
  int    c;
  int    count;
  int    out_position = 0;
  int    in_position  = 0;

  MathInput_counts( &in_position , math->totals,   uc_data, math );
  MathInitialize_arithmetic_decoder( &in_position, uc_data, math );
  for ( ; ; ) {
    MathGet_symbol_scale( math->totals , math );
    count = MathGet_current_count( );
    c = MathConvert_symbol_to_int( count, math->totals, math );
    if ( c == d_END_OF_STREAM )
      break;

    if (in_position >= i_size) { 
      math->out_size = 0;  break;
    }

    MathRemove_symbol_from_stream( &in_position, uc_data, math );
    uc_data2[ out_position ] = (unsigned char)c;
    out_position++;
  }
  math->out_size = out_position;
}


// *******************************************************************
// *******************************************************************


int MathInputComp( int * i, unsigned char uc_data[], MATH *math )
{
  int value;
  
  if ( math->mask == 0x80 ) {
    math->rack = (int)(uc_data[ *i ]);
    (*i)++;
  }
  value = math->rack & math->mask;
  math->mask >>= 1;
  if ( math->mask == 0 )
    math->mask = 0x80;
  return( value ? 1 : 0 );
}

unsigned int MathInputComps( int bit_count, int * i, unsigned char uc_data[], MATH *math )
{
  unsigned int mask;
  unsigned int return_value;
  
  mask = 1L << ( bit_count - 1 );
  return_value = 0;
  while ( mask != 0) {
    if ( math->mask == 0x80 ) {
      math->rack = (int)(uc_data[ *i ]);
      (*i)++;
    }
    if ( math->rack & math->mask )
      return_value |= mask;
    mask >>= 1;
    math->mask >>= 1;
    if ( math->mask == 0 )
      math->mask = 0x80;
  }
  return( return_value );
}


// *******************************************************************
// *******************************************************************


void MathInput_counts( int * in_position, short * totals, unsigned char uc_data[], MATH *math )
{
  int first;
  int last;
  int i;
  int c;
  
  for ( i = 0 ; i < 256 ; i++ )
    math->scaled_counts[ i ] = 0;
  first = (int)(uc_data[ *in_position ]);     (*in_position) ++;
  last  = (int)(uc_data[ *in_position ]);     (*in_position) ++;
  for ( ; ; ) {
    for ( i = first ; i <= last ; i++ ) {
      c = (int)(uc_data[ *in_position ]);     (*in_position) ++;
      math->scaled_counts[ i ] = (unsigned char) c;
    }
    first = (int)(uc_data[ *in_position ]);   (*in_position) ++;
    if ( first == 0 )   break;
    last  = (int)(uc_data[ *in_position ]);   (*in_position) ++;
  }
  MathBuild_totals( math->scaled_counts , totals);
}

void MathGet_symbol_scale( short * totals, MATH *math)
{
    math->scale = totals[ d_END_OF_STREAM + 1 ];
}

int MathConvert_symbol_to_int( int count, short * totals, MATH *math )
{
    int c;

    for ( c = d_END_OF_STREAM ; count < totals[ c ] ; c-- )
	;
    math->high_count = totals[ c + 1 ];
    math->low_count = totals[ c ];
    return( c );
}

short MathGet_current_count( MATH *math)
{
    int   range;
    short count;

    range = (int) ( math->high - math->low ) + 1;
    count = (short)
      ((((int) ( math->code - math->low ) + 1 ) * math->scale-1 ) / range );
    return( count );
}

void MathInitialize_arithmetic_decoder( int * in_position, unsigned char uc_data[], MATH *math )
{
  int i;
  
  math->code = 0;
  for ( i = 0 ; i < 16 ; i++ ) {
    math->code <<= 1;
    math->code += MathInputComp( in_position, uc_data, math );
  }
  math->low = 0;
  math->high = 0xffff;
}

void MathRemove_symbol_from_stream( int * in_position, unsigned char uc_data[], MATH *math )
{
  int range;
  
  range = (int)( math->high - math->low ) + 1;
  math->high = math->low + (unsigned short)
    (( range * math->high_count ) / math->scale - 1 );
  math->low  = math->low + (unsigned short)
    (( range * math->low_count ) / math->scale );
  for ( ; ; ) {
    if ( ( math->high & 0x8000 ) == ( math->low & 0x8000 ) ) {
    }
    else if ((math->low & 0x4000) == 0x4000  && (math->high & 0x4000) == 0 ) {
      math->code ^= 0x4000;
      math->low   &= 0x3fff;
      math->high  |= 0x4000;
    } else
      return;
    math->low <<= 1;
    math->high <<= 1;
    math->high |= 1;
    math->code <<= 1;
    math->code += MathInputComp( in_position, uc_data, math );
  }
}
// ************************** End of ARITH.C ****************************
// ************************** End of ARITH.C ****************************
// ************************** End of ARITH.C ****************************
*/