#ifdef _WINDOWS
#include <windows.h>
#endif

#include        <stdio.h>
#include        <stdlib.h>

#include        "ccdef.h"
#include        "compcert.h"
#include        "gmem.h"

#define FIXED_MODEL                     1
#define ADAPTATIVE_MODEL        2
#define Code_value_bits     16
#define No_of_chars                 256
#define Max_frequency       16383
#define EOF_symbol              (No_of_chars+1)
#define No_of_symbols   (No_of_chars+1)

#define Top_value       (( (long) 1 << Code_value_bits) - 1)
#define First_qtr       (Top_value / 4 + 1)
#define Half            (2 * First_qtr)
#define Third_qtr       (3 * First_qtr)

typedef long    code_value;

static  code_value      value_dc;
static  code_value      low_dc, high_dc;
static  code_value      low_enc, high_enc;

static  long                bits_to_follow;
static  unsigned  int   buffer_in;
static  unsigned  int   bits_to_go_in;
static  unsigned  int   garbage_bits_in;
static  unsigned  int   buffer_out;
static  unsigned  int   bits_to_go_out;

static  int             Error;

unsigned int            char_to_index[No_of_chars];
unsigned char               index_to_char[No_of_symbols+1];
unsigned int            cum_freq[No_of_symbols+1];

unsigned char           *Memory;
static   unsigned       MemoSize;

unsigned int    FixedFreqA[No_of_symbols+1] =
{
        0,
  63, 260, 180, 216, 254,  63, 268,  95,  43,  62,  33,  28,  14,   6,  45,  38,

  33,  35,  13, 303,  41,  37,  41,   8,  15,  81,  25,   2,  15,  88,  20,   6,

/*      !    =    #    $    %    &    '    (    )    *    +    ,    -    .    /         */
 662,   3,  35,  18,  12,   3,  11,   3,  16,  12,   2,  34,  42,  32,  56,  31,

/* 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?  */
 300, 118,  42,  40,  21,  37,  63,  46,  10,  90,  33,   3,   1,   9,  15,   3,

/* @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O  */
  27, 158,  73, 122,  82,  31,  20,  14,  26,  56,   1,   2,  27,  20,  12,  31,

/* P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _  */
  64,   4,  65, 114, 106, 195,  10,  27,   3,  18,  25,   2,   3,   1,   2,  12,

/* `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o  */
  24, 271,  44, 171, 158, 439,  44,  50,  71, 308,   2,   5,  60,  73, 234, 280,

/* p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~       */
  76,  44, 274, 273, 358, 100,  36,  43,  12,  79,   2,   1,   1,   1,   1,   1,

  48,  48,  30,   1,   1,   5, 100,   5,   1,   3,   1,   1,   1,  50,   1,   1,
   1,   4,   2,  14,   1,   5,  12,   1,   4,   1,   1,   1,   1,   1,   1,   1,
   6,  14,   1,   1,  24,   1,   1,   4,   1,  11,   1,   2,  23,   1,   1,   4,
   1,   1,   1,   1,   5,   1,   1,  12,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   1,   1,   2,   1,   1,   1,   1,   1,   4,   1,   1,   1,   1,   1,   1,
   1,   2,   1,   2,   1,   1,   1,   1,   1,   1,   1,   4,   1,   1,   4,   1,
   1,   1,   1,  13,  12,   1,   1,   1,   1,   1,   1,   2,   1,  13,   1,   1,
   5,   1,   2,   1,  12,   1,   1,   1,  24,   2,   1,   1,   1,   4,   1, 308,
 250
};

unsigned int    AdaptativeFreqA[No_of_symbols+1] =
{
        0,
  12,  24,  19,  18,  19,   6,  20,   7,   4,   5,   3,   3,   2,   2,   4,   4,

   3,   4,   2,  23,   4,   4,   4,   2,   2,   7,   3,   1,   2,   7,   3,   1,

/*      !    =    #    $    %    &    '    (    )    *    +    ,    -    .    /         */
  49,   1,   3,   2,   2,   2,   2,   1,   2,   2,   1,   3,   4,   3,   5,   3,

/* 0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?  */
  25,   9,   3,   3,   2,   3,   5,   4,   2,   7,   3,   1,   1,   1,   2,   1,

/* @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    O  */
   3,  14,   6,  10,   7,   3,   3,   2,   3,   5,   1,   1,   3,   2,   1,   3,

/* P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _  */
   5,   2,   6,   9,   9,  15,   2,   3,   1,   2,   3,   2,   1,   1,   1,   2,

/* `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o  */
   2,  20,   4,  13,  12,  32,   4,   5,   6,  24,   1,   1,   5,   7,  18,  21,

/* p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~       */
   6,   4,  20,  21,  27,   8,   3,   4,   2,   7,   1,   1,   1,   1,   1,   1,

   4,   9,   3,   1,   1,   1,   8,   1,   1,   3,   1,   1,   1,   5,   1,   1,
   1,   2,   1,   2,   1,   1,   2,   1,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   2,   1,   1,   3,   1,   1,   1,   1,   2,   1,   1,   2,   1,   1,   1,
   1,   1,   1,   1,   1,   1,   1,   2,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   1,   1,   2,   2,   1,   1,   1,   1,   1,   1,   1,   1,   2,   1,   2,
   1,   1,   1,   1,   2,   1,   1,   1,   2,   1,   1,   1,   1,   1,   1,  24,
  25
};

unsigned int    freq[No_of_symbols+1];

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

void    start_model(int ModelType)

{
        int    i;


        for (i = 0; i < No_of_chars; i++)
        {
                char_to_index[i] = i+1;
                index_to_char[i+1] = (BYTE)i;
        }

        if (ModelType == FIXED_MODEL)
        {
                for (i = 0; i <= No_of_symbols; i++)
                {
                        freq[i] = FixedFreqA[i];
                }

                cum_freq[No_of_symbols] = 0;
                for(i = No_of_symbols; i > 0; i--)
                {
                        cum_freq[i-1] = cum_freq[i] + freq[i];
                }
        }

        if (ModelType == ADAPTATIVE_MODEL)
        {
                for (i = 0; i <= No_of_symbols; i++)
                {
                        freq[i] = AdaptativeFreqA[i];
                }

                cum_freq[No_of_symbols] = 0;
                for(i = No_of_symbols; i > 0; i--)
                {
                        cum_freq[i-1] = cum_freq[i] + freq[i];
                }
        }
}


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

void    update_model(int ModelType, unsigned int symbol)

{
        unsigned int    i;
        unsigned int    cum;
        unsigned int    ch_i;
        unsigned int    ch_symbol;


        if (ModelType == ADAPTATIVE_MODEL)
        {
                if (cum_freq[0] == Max_frequency)
                {
                        cum = 0;
                        
                        for (i = No_of_symbols + 1; i != 0; /**/)
                        {
                                --i;
                                freq[i] = (freq[i] + 1) / 2;
                                cum_freq[i] = cum;
                                cum += freq[i];
                        }
                }

                for (i = symbol; freq[i] == freq[i-1]; i--)
                {
                }

                if (i < symbol)
                {
                        ch_i = index_to_char[i];
                        ch_symbol = index_to_char[symbol];
                        index_to_char[i] = (BYTE)ch_symbol;
                        index_to_char[symbol] = (BYTE)ch_i;
                        char_to_index[ch_i] = symbol;
                        char_to_index[ch_symbol] = i;
                }

                freq[i] += 1;

                while (i > 0)
                {
                        i -= 1;
                        cum_freq[i] += 1;
                }
        }
}

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

void    start_outputing_bits(void)

{
        buffer_out = 0;
        bits_to_go_out = 8;
}


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

int     output_bit(unsigned short int   *pOutCount, unsigned int bit)
{
        buffer_out >>= 1;
        if (bit)
        {
                buffer_out |= 0x80;
        }

        bits_to_go_out -= 1;
        if (bits_to_go_out == 0)
        {
                if (*pOutCount == MemoSize)
                {
                        return RV_COMPRESSION_FAILED;
                }

                Memory[(*pOutCount)++] = (BYTE)buffer_out;
                bits_to_go_out = 8;
        }

        return RV_SUCCESS;
}

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

int done_outputing_bits(unsigned short int      *pOutCount)
{
        if (*pOutCount == MemoSize)
        {
                return RV_COMPRESSION_FAILED;
        }

        Memory[(*pOutCount)++] = buffer_out>>bits_to_go_out;

        return RV_SUCCESS;
}

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

void    start_inputing_bits(void)
{
        bits_to_go_in   = 0;
        garbage_bits_in = 0;
        Error           = RV_SUCCESS;
}

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

unsigned  int   input_bit(BLOC InBloc, unsigned short int *pusInCount)

{
        unsigned  int   t;

        if (bits_to_go_in == 0)
        {
                if (*pusInCount < InBloc.usLen)
                {
                        buffer_in = InBloc.pData[(*pusInCount)++];
                }
                else
                {
                        buffer_in = 0x00;
                }

            if (*pusInCount == InBloc.usLen)
                {
                        garbage_bits_in += 1;
                if (garbage_bits_in > Code_value_bits - 2)
                        {
                                Error = RV_INVALID_DATA;
                        }
                }

                bits_to_go_in = 8;
        }

        t = buffer_in & 1;
        buffer_in >>= 1;
        bits_to_go_in -= 1;

        return (t);
}


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

int bit_plus_follow(unsigned short int  *pOutCount, unsigned  int bit)

{
        if (output_bit(pOutCount, bit) != RV_SUCCESS)
        {
                return RV_COMPRESSION_FAILED;
        }
        while (bits_to_follow > 0)
        {
                if (output_bit(pOutCount, !bit) != RV_SUCCESS)
                {
                        return RV_COMPRESSION_FAILED;
                }
                bits_to_follow -= 1;
        }

        return RV_SUCCESS;
}

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

void    start_encoding(void)

{
        low_enc        = 0;
        high_enc       = Top_value;
        bits_to_follow = 0;
}

/******************************************************************************/
int     encode_symbol(unsigned short int        *pOutCount,
                                                  unsigned  int symbol,
                                                  unsigned  int cum_freq[]
                                                 )

{
        long    range;


        range = (long) (high_enc-low_enc) + 1;
        high_enc = low_enc + (range * cum_freq[symbol-1]) / cum_freq[0] - 1;
        low_enc = low_enc + (range * cum_freq[symbol]) / cum_freq[0];

        for ( ; ; )
        {
                if (high_enc < Half)
                {
                        if (bit_plus_follow(pOutCount, 0) != RV_SUCCESS)
                        {
                                return RV_COMPRESSION_FAILED;
                        }
                }
                else if (low_enc >= Half)
                {
                        if (bit_plus_follow(pOutCount, 1) != RV_SUCCESS)
                        {
                                return RV_COMPRESSION_FAILED;
                        }
                        low_enc -= Half;
                        high_enc -= Half;
                }
                else if ((low_enc >= First_qtr) && (high_enc < Third_qtr))
                {
                        bits_to_follow += 1;
                        low_enc -= First_qtr;
                        high_enc -= First_qtr;
                }
                else
                {
                        break;
                }

                low_enc = 2 * low_enc;
                high_enc = 2 * high_enc + 1;
        }

        return RV_SUCCESS;
}


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

int done_encoding(unsigned short int    *pOutCount)

{
        bits_to_follow += 1;
        if (low_enc < First_qtr)
        {
                if (bit_plus_follow(pOutCount, 0) != RV_SUCCESS)
                {
                        return RV_COMPRESSION_FAILED;
                }
        }
        else
        {
                if (bit_plus_follow(pOutCount, 1) != RV_SUCCESS)
                {
                        return RV_COMPRESSION_FAILED;
                }
        }

        return RV_SUCCESS;
}

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

void    start_decoding(BLOC InBloc, unsigned short int *pusInCount)

{
        unsigned  int   i;


        value_dc = 0;

        for (i = 1; i <= Code_value_bits; i++)
        {
                value_dc = 2 * value_dc + input_bit(InBloc, pusInCount);
                if (Error != RV_SUCCESS)
                {
                        break;
                }
        }

        low_dc = 0;
        high_dc = Top_value;
}


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

unsigned  int   decode_symbol(BLOC InBloc,
                                                                          unsigned short int *pusInCount,
                                                                          unsigned  int cum_freq[]
                                                                         )

{
        long    range;
        unsigned  int   cum;
        unsigned  int   symbol;


        range = (long) (high_dc-low_dc) + 1;
        cum = (((long) (value_dc-low_dc) + 1) * cum_freq[0] - 1) / range;

        for (symbol = 1; cum_freq[symbol] > cum; symbol++)
        {
        }

        high_dc = low_dc + (range * cum_freq[symbol-1]) / cum_freq[0] - 1;
        low_dc = low_dc + (range * cum_freq[symbol]) / cum_freq[0];

        for ( ; ; )
        {
                if (high_dc < Half)
                {
                }
                else if (low_dc >= Half)
                {
                        value_dc -= Half;
                        low_dc -= Half;
                        high_dc -= Half;
                }
                else if ((low_dc >= First_qtr) && (high_dc < Third_qtr))
                {
                        value_dc -= First_qtr;
                        low_dc -= First_qtr;
                        high_dc -= First_qtr;
                }
                else
                {
                        break;
                }

                low_dc = 2 * low_dc;
                high_dc = 2 * high_dc + 1;
                value_dc = 2 * value_dc + input_bit(InBloc, pusInCount);
                if (Error != RV_SUCCESS)
                {
                        break;
                }
        }

        return (symbol);
}

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

int AcAd8_Encode(BLOC *pInBloc, BLOC *pOutBloc)
{
        int                     i, ch;
        unsigned int        symbol;
        unsigned short int      usInCount = 0;
        unsigned short int      usOutCount = 0;

        MemoSize = pInBloc->usLen;
        Memory   = GMEM_Alloc (MemoSize);

        if (Memory == NULL)
        {
                return (RV_MALLOC_FAILED);
        }

        start_model(ADAPTATIVE_MODEL);
        start_outputing_bits();
        start_encoding();

        for (usInCount = 0; usInCount < pInBloc->usLen; usInCount++)
        {
           ch = pInBloc->pData[usInCount];
           symbol = char_to_index[ch];
           if (encode_symbol(&usOutCount, symbol, cum_freq) != RV_SUCCESS)
           {
                   goto err;
           }
           update_model(ADAPTATIVE_MODEL, symbol);
        }

        if (encode_symbol(&usOutCount, EOF_symbol, cum_freq) != RV_SUCCESS)
        {
                goto err;
        }
        if (done_encoding(&usOutCount) != RV_SUCCESS)
        {
                goto err;
        }
        if (done_outputing_bits(&usOutCount) != RV_SUCCESS)
        {
                goto err;
        }

        pOutBloc->usLen = usOutCount;
        pOutBloc->pData = GMEM_Alloc(pOutBloc->usLen);

        if (pOutBloc->pData != NULL)
        {
           for (i=0; i < pOutBloc->usLen; i++)
           {
                   pOutBloc->pData[i] = Memory[i];
           }

           GMEM_Free(Memory);
           return (RV_SUCCESS);
        }
        else
        {
           pOutBloc->usLen = 0;
           pOutBloc->pData = NULL;

           GMEM_Free(Memory);
           return (RV_MALLOC_FAILED);
        }

err:
        GMEM_Free(Memory);

        pOutBloc->usLen = pInBloc->usLen;
        pOutBloc->pData = GMEM_Alloc(pOutBloc->usLen);

        if (pOutBloc->pData != NULL)
        {
           for (i=0; i < pOutBloc->usLen; i++)
           {
                   pOutBloc->pData[i] = pInBloc->pData[i];
           }

           return (RV_COMPRESSION_FAILED);
        }
        else
        {
           pOutBloc->usLen = 0;
           pOutBloc->pData = NULL;

           return (RV_MALLOC_FAILED);
        }
}

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

int AcFx8_Encode(BLOC *pInBloc, BLOC *pOutBloc)
{
        int                     i, ch;
        unsigned int        symbol;
        unsigned short int      usInCount = 0;
        unsigned short int      usOutCount = 0;

        MemoSize = pInBloc->usLen;
        Memory   = GMEM_Alloc (MemoSize);

        if (Memory == NULL)
        {
                return (RV_MALLOC_FAILED);
        }

        start_model(FIXED_MODEL);
        start_outputing_bits();
        start_encoding();

        for (usInCount = 0; usInCount < pInBloc->usLen; usInCount++)
        {
           ch = pInBloc->pData[usInCount];
           symbol = char_to_index[ch];
           if (encode_symbol(&usOutCount, symbol, cum_freq) != RV_SUCCESS)
           {
                   goto err;
           }
           update_model(FIXED_MODEL, symbol);
        }

        if (encode_symbol(&usOutCount, EOF_symbol, cum_freq))
        {
                goto err;
        }
        if (done_encoding(&usOutCount) != RV_SUCCESS)
        {
                goto err;
        }
        if (done_outputing_bits(&usOutCount) != RV_SUCCESS)
        {
                goto err;
        }

        pOutBloc->usLen = usOutCount;
        pOutBloc->pData = GMEM_Alloc(pOutBloc->usLen);

        if (pOutBloc->pData != NULL)
        {
           for (i=0; i < pOutBloc->usLen; i++)
           {
                   pOutBloc->pData[i] = Memory[i];
           }

           GMEM_Free(Memory);
           return (RV_SUCCESS);
        }
        else
        {
           pOutBloc->usLen = 0;
           pOutBloc->pData = NULL;

           GMEM_Free(Memory);
           return (RV_MALLOC_FAILED);
        }

err:
        GMEM_Free(Memory);

        pOutBloc->usLen = pInBloc->usLen;
        pOutBloc->pData = GMEM_Alloc(pOutBloc->usLen);

        if (pOutBloc->pData != NULL)
        {
           for (i=0; i < pOutBloc->usLen; i++)
           {
                   pOutBloc->pData[i] = pInBloc->pData[i];
           }

           return (RV_COMPRESSION_FAILED);
        }
        else
        {
           pOutBloc->usLen = 0;
           pOutBloc->pData = NULL;

           return (RV_MALLOC_FAILED);
        }
}


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

int AcAd8_Decode(BLOC *pInBloc, BLOC *pOutBloc)
{
        unsigned  int   ch;
        unsigned  int   symbol;
        unsigned short int      usInCount = 0;
        unsigned short int      usOutCount = 0;

        MemoSize = 2*pInBloc->usLen;
        Memory   = GMEM_Alloc(MemoSize);
        if (Memory == NULL)
        {
                pOutBloc->pData = NULL;
                pOutBloc->usLen = 0;
                return (RV_MALLOC_FAILED);
        }

        start_model(ADAPTATIVE_MODEL);
        start_inputing_bits();
        start_decoding(*pInBloc, &usInCount);

        while(1)
        {
                symbol = decode_symbol(*pInBloc, &usInCount, cum_freq);
            if ((symbol == EOF_symbol) || (Error != RV_SUCCESS))
                {
                   break;
                }

                ch = index_to_char[symbol];

                if (usOutCount >= MemoSize)
                {
                        Memory = GMEM_ReAlloc (Memory, 2*MemoSize);
                        if (Memory == NULL)
                        {
                                pOutBloc->pData = NULL;
                                pOutBloc->usLen = 0;
                                return (RV_MALLOC_FAILED);
                        }
                        MemoSize = MemoSize*2;
                }

                Memory[usOutCount++] = (BYTE)ch;
                update_model(ADAPTATIVE_MODEL, symbol);
        }

        if (Error != RV_SUCCESS)
        {
                GMEM_Free (Memory);
                pOutBloc->pData = NULL;
                pOutBloc->usLen = 0;
                return (Error);
        }

        pOutBloc->pData = GMEM_Alloc(usOutCount);
        if (pOutBloc->pData == NULL)
        {
                GMEM_Free (Memory);
                pOutBloc->usLen = 0;
                return (RV_MALLOC_FAILED);
        }

        pOutBloc->usLen = usOutCount;
        memcpy(pOutBloc->pData, Memory, usOutCount);
        GMEM_Free (Memory);

        return (RV_SUCCESS);
}

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

int AcFx8_Decode(BLOC *pInBloc, BLOC *pOutBloc)
{
        unsigned  int   i, ch;
        unsigned  int   symbol;
        unsigned short int      usInCount = 0;
        unsigned short int      usOutCount = 0;

        MemoSize = 2*pInBloc->usLen;
        Memory   = GMEM_Alloc(MemoSize);
        if (Memory == NULL)
        {
                pOutBloc->pData = NULL;
                pOutBloc->usLen = 0;
                return (RV_MALLOC_FAILED);
        }

        start_model(FIXED_MODEL);
        start_inputing_bits();
        start_decoding(*pInBloc, &usInCount);

        while(1)
        {
                symbol = decode_symbol(*pInBloc, &usInCount, cum_freq);
            if ((symbol == EOF_symbol) || (Error != RV_SUCCESS))
                {
                   break;
                }

                ch = index_to_char[symbol];

                if (usOutCount >= MemoSize)
                {
                        Memory = GMEM_ReAlloc (Memory, 2*MemoSize);
                        if (Memory == NULL)
                        {
                                pOutBloc->pData = NULL;
                                pOutBloc->usLen = 0;
                                return (RV_MALLOC_FAILED);
                        }
                        MemoSize = MemoSize*2;
                }

                Memory[usOutCount++] = (BYTE)ch;
                update_model(FIXED_MODEL, symbol);
        }

        if (Error != RV_SUCCESS)
        {
                GMEM_Free (Memory);
                pOutBloc->pData = NULL;
                pOutBloc->usLen = 0;
                return (Error);
        }

        pOutBloc->pData = GMEM_Alloc(usOutCount);
        if (pOutBloc->pData == NULL)
        {
                GMEM_Free (Memory);
                pOutBloc->usLen = 0;
                return (RV_MALLOC_FAILED);
        }

        pOutBloc->usLen = usOutCount;
        for (i=0; i<pOutBloc->usLen; i++)
        {
                pOutBloc->pData[i] = Memory[i];
        }
        GMEM_Free (Memory);

        return (RV_SUCCESS);
}