// Compress/RangeCoder/RangeCoderBit.h



#ifndef __COMPRESS_RANGECODER_BIT_H

#define __COMPRESS_RANGECODER_BIT_H



#include "RangeCoder.h"



namespace NCompress {

namespace NRangeCoder {



const int kNumBitModelTotalBits  = 11;

const UInt32 kBitModelTotal = (1 << kNumBitModelTotalBits);



const int kNumMoveReducingBits = 2;



const int kNumBitPriceShiftBits = 6;

const UInt32 kBitPrice = 1 << kNumBitPriceShiftBits;



class CPriceTables

{

public:

  static UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits];

  static void Init();

  CPriceTables();

};



template <int numMoveBits>

class CBitModel

{

public:

  UInt32 Prob;

  void UpdateModel(UInt32 symbol)

  {

    /*

    Prob -= (Prob + ((symbol - 1) & ((1 << numMoveBits) - 1))) >> numMoveBits;

    Prob += (1 - symbol) << (kNumBitModelTotalBits - numMoveBits);

    */

    if (symbol == 0)

      Prob += (kBitModelTotal - Prob) >> numMoveBits;

    else

      Prob -= (Prob) >> numMoveBits;

  }

public:

  void Init() { Prob = kBitModelTotal / 2; }

};



template <int numMoveBits>

class CBitEncoder: public CBitModel<numMoveBits>

{

public:

  void Encode(CEncoder *encoder, UInt32 symbol)

  {

    /*

    encoder->EncodeBit(this->Prob, kNumBitModelTotalBits, symbol);

    this->UpdateModel(symbol);

    */

    UInt32 newBound = (encoder->Range >> kNumBitModelTotalBits) * this->Prob;

    if (symbol == 0)

    {

      encoder->Range = newBound;

      this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits;

    }

    else

    {

      encoder->Low += newBound;

      encoder->Range -= newBound;

      this->Prob -= (this->Prob) >> numMoveBits;

    }

    if (encoder->Range < kTopValue)

    {

      encoder->Range <<= 8;

      encoder->ShiftLow();

    }

  }

  UInt32 GetPrice(UInt32 symbol) const

  {

    return CPriceTables::ProbPrices[

      (((this->Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits];

  }

  UInt32 GetPrice0() const { return CPriceTables::ProbPrices[this->Prob >> kNumMoveReducingBits]; }

  UInt32 GetPrice1() const { return CPriceTables::ProbPrices[(kBitModelTotal - this->Prob) >> kNumMoveReducingBits]; }

};





template <int numMoveBits>

class CBitDecoder: public CBitModel<numMoveBits>

{

public:

  UInt32 Decode(CDecoder *decoder)

  {

    UInt32 newBound = (decoder->Range >> kNumBitModelTotalBits) * this->Prob;

    if (decoder->Code < newBound)

    {

      decoder->Range = newBound;

      this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits;

      if (decoder->Range < kTopValue)

      {

        decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte();

        decoder->Range <<= 8;

      }

      return 0;

    }

    else

    {

      decoder->Range -= newBound;

      decoder->Code -= newBound;

      this->Prob -= (this->Prob) >> numMoveBits;

      if (decoder->Range < kTopValue)

      {

        decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte();

        decoder->Range <<= 8;

      }

      return 1;

    }

  }

};



}}



#endif

