#include "Portable.h"
#include "LZMAEncoder.h"

#include "BinTree2Main.h"

using namespace NCompression;
using namespace NArithmetic;

#define RETURN_E_OUTOFMEMORY_IF_FALSE(x) { if (!(x)) return E_OUTOFMEMORY; }

namespace NCompress {
namespace NLZMA {

BYTE g_FastPos[1024];

class CFastPosInit
{
public:
  CFastPosInit()
  {
    int c = 0;
    const int kFastSlots = 20;
    c = 0;
    for (BYTE aSlotFast = 0; aSlotFast < kFastSlots; aSlotFast++)
    {
      UINT32 k = (1 << kDistDirectBits[aSlotFast]);
      for (UINT32 j = 0; j < k; j++, c++)
        g_FastPos[c] = aSlotFast;
    }
  }
} g_FastPosInit;

const int kDefaultDictionaryLogSize = 20;
const int kNumFastBytesDefault = 0x20;

CEncoder::CEncoder():
  m_DictionarySize(1 << kDefaultDictionaryLogSize),
  m_DictionarySizePrev(UINT32(-1)),
  m_NumFastBytes(kNumFastBytesDefault),
  m_NumFastBytesPrev(UINT32(-1)),
  m_DistTableSize(kDefaultDictionaryLogSize * 2),
  m_PosStateBits(2),
  m_PosStateMask(4 - 1),
  m_LiteralPosStateBits(0),
  m_LiteralContextBits(3)
{
  m_MaxMode = false;
  m_FastMode = false;
  m_PosAlignEncoder.Create(kNumAlignBits);
  for(int i = 0; i < kNumPosModels; i++)
    m_PosEncoders[i].Create(kDistDirectBits[kStartPosModelIndex + i]);
}

HRESULT CEncoder::Create()
{
  if (m_DictionarySize == m_DictionarySizePrev && m_NumFastBytesPrev == m_NumFastBytes)
    return S_OK;
  RETURN_IF_NOT_S_OK(m_MatchFinder.Create(m_DictionarySize, kNumOpts, m_NumFastBytes, 
      kMatchMaxLen - m_NumFastBytes));
  m_DictionarySizePrev = m_DictionarySize;
  m_NumFastBytesPrev = m_NumFastBytes;
  m_LiteralEncoder.Create(m_LiteralPosStateBits, m_LiteralContextBits);
  m_LenEncoder.Create(1 << m_PosStateBits);
  m_RepMatchLenEncoder.Create(1 << m_PosStateBits);
  return S_OK;
}

HRESULT CEncoder::SetEncoderAlgorithm(UINT32 A) {
  UINT32 aMaximize = A;
  if (aMaximize > 2)
    return E_INVALIDARG;

  m_FastMode = (aMaximize == 0);
  m_MaxMode = (aMaximize >= 2);

  return S_OK;
}

HRESULT CEncoder::SetEncoderNumFastBytes(UINT32 A) {
  UINT32 aNumFastBytes = A;
  if(aNumFastBytes < 2 || aNumFastBytes > kMatchMaxLen)
    return E_INVALIDARG;

  m_NumFastBytes = aNumFastBytes;

  return S_OK;
}

HRESULT CEncoder::SetDictionarySize(UINT32 aDictionarySize)
{
  if (aDictionarySize > UINT32(1 << kDicLogSizeMax))
    return E_INVALIDARG;
  m_DictionarySize = aDictionarySize;
  UINT32 aDicLogSize;
  for(aDicLogSize = 0; aDicLogSize < kDicLogSizeMax; aDicLogSize++)
    if (aDictionarySize <= (UINT32(1) << aDicLogSize))
      break;
  m_DistTableSize = aDicLogSize * 2;
  return S_OK;
}

HRESULT CEncoder::SetLiteralProperties(UINT32 aLiteralPosStateBits, UINT32 aLiteralContextBits)
{
  if (aLiteralPosStateBits > kNumLitPosStatesBitsEncodingMax)
    return E_INVALIDARG;
  if (aLiteralContextBits > kNumLitContextBitsMax)
    return E_INVALIDARG;
  m_LiteralPosStateBits = aLiteralPosStateBits;
  m_LiteralContextBits = aLiteralContextBits;
  return S_OK;
}

HRESULT CEncoder::SetPosBitsProperties(UINT32 aNumPosStateBits)
{
  if (aNumPosStateBits > NLength::kNumPosStatesBitsEncodingMax)
    return E_INVALIDARG;
  m_PosStateBits = aNumPosStateBits;
  m_PosStateMask = (1 << m_PosStateBits) - 1;
  return S_OK;
}


HRESULT CEncoder::WriteCoderProperties(ISequentialOutStream *anOutStream)
{ 
  BYTE aByte = (m_PosStateBits * 5 + m_LiteralPosStateBits) * 9 + m_LiteralContextBits;
  UINT32 aProcessedSize;
  HRESULT aResult = anOutStream->Write(&aByte, sizeof(aByte), &aProcessedSize);
  if (aResult != S_OK)
     return aResult;
  if (aProcessedSize != sizeof(aByte))
     return E_FAIL;
  aResult = anOutStream->Write(&m_DictionarySize, sizeof(m_DictionarySize), &aProcessedSize);
    if (aResult != S_OK)
     return aResult;
  if (aProcessedSize != sizeof(m_DictionarySize))
     return E_FAIL;
  return S_OK;
}

HRESULT CEncoder::Init(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream)
{
  CBaseCoder::Init();

  RETURN_IF_NOT_S_OK(m_MatchFinder.Init(anInStream));
  m_RangeEncoder.Init(anOutStream);

  int i;
  for(i = 0; i < kNumStates; i++)
  {
    for (UINT32 j = 0; j <= m_PosStateMask; j++)
    {
      m_MainChoiceEncoders[i][j].Init();
      m_MatchRepShortChoiceEncoders[i][j].Init();
    }
    m_MatchChoiceEncoders[i].Init();
    m_MatchRepChoiceEncoders[i].Init();
    m_MatchRep1ChoiceEncoders[i].Init();
    m_MatchRep2ChoiceEncoders[i].Init();
  }

  m_LiteralEncoder.Init();

  // m_RepMatchLenEncoder.Init();
  
  for(i = 0; i < kNumLenToPosStates; i++)
    m_PosSlotEncoder[i].Init();

  for(i = 0; i < kNumPosModels; i++)
    m_PosEncoders[i].Init();

  m_LenEncoder.Init();
  m_RepMatchLenEncoder.Init();

  m_PosAlignEncoder.Init();

  m_LongestMatchWasFound = false;
  m_OptimumEndIndex = 0;
  m_OptimumCurrentIndex = 0;
  m_AdditionalOffset = 0;

  return S_OK;
}

void CEncoder::MovePos(UINT32 aNum)
{
  for (;aNum > 0; aNum--)
  {
    m_MatchFinder.DummyLongestMatch();
    HRESULT aResult = m_MatchFinder.MovePos();
    if (aResult != S_OK)
      throw aResult;
    m_AdditionalOffset++;
  }
}

UINT32 CEncoder::Backward(UINT32 &aBackRes, UINT32 aCur)
{
  m_OptimumEndIndex = aCur;
  UINT32 aPosMem = m_Optimum[aCur].PosPrev;
  UINT32 aBackMem = m_Optimum[aCur].BackPrev;
  do
  {
    if (m_Optimum[aCur].Prev1IsChar)
    {
      m_Optimum[aPosMem].MakeAsChar();
      m_Optimum[aPosMem].PosPrev = aPosMem - 1;
      if (m_Optimum[aCur].Prev2)
      {
        m_Optimum[aPosMem - 1].Prev1IsChar = false;
        m_Optimum[aPosMem - 1].PosPrev = m_Optimum[aCur].PosPrev2;
        m_Optimum[aPosMem - 1].BackPrev = m_Optimum[aCur].BackPrev2;
      }
    }
    UINT32 aPosPrev = aPosMem;
    UINT32 aBackCur = aBackMem;

    aBackMem = m_Optimum[aPosPrev].BackPrev;
    aPosMem = m_Optimum[aPosPrev].PosPrev;

    m_Optimum[aPosPrev].BackPrev = aBackCur;
    m_Optimum[aPosPrev].PosPrev = aCur;
    aCur = aPosPrev;
  }
  while(aCur > 0);
  aBackRes = m_Optimum[0].BackPrev;
  m_OptimumCurrentIndex  = m_Optimum[0].PosPrev;
  return m_OptimumCurrentIndex; 
}

/*
inline UINT32 GetMatchLen(const BYTE *aData, UINT32 aBack, UINT32 aLimit)
{  
  aBack++;
  for(UINT32 i = 0; i < aLimit && aData[i] == aData[i - aBack]; i++);
  return i;
}
*/

UINT32 CEncoder::GetOptimum(UINT32 &aBackRes, UINT32 aPosition)
{
  if(m_OptimumEndIndex != m_OptimumCurrentIndex)
  {
    UINT32 aLen = m_Optimum[m_OptimumCurrentIndex].PosPrev - m_OptimumCurrentIndex;
    aBackRes = m_Optimum[m_OptimumCurrentIndex].BackPrev;
    m_OptimumCurrentIndex = m_Optimum[m_OptimumCurrentIndex].PosPrev;
    return aLen;
  }
  m_OptimumCurrentIndex = 0;
  m_OptimumEndIndex = 0; // test it;
  
  UINT32 aLenMain;
  if (!m_LongestMatchWasFound)
    aLenMain = ReadMatchDistances();
  else
  {
    aLenMain = m_LongestMatchLength;
    m_LongestMatchWasFound = false;
  }


  UINT32 aReps[kNumRepDistances];
  UINT32 aRepLens[kNumRepDistances];
  UINT32 RepMaxIndex = 0;
  int i;
  for(i = 0; i < kNumRepDistances; i++)
  {
    aReps[i] = m_RepDistances[i];
    aRepLens[i] = m_MatchFinder.GetMatchLen(0 - 1, aReps[i], kMatchMaxLen);
    if (i == 0 || aRepLens[i] > aRepLens[RepMaxIndex])
      RepMaxIndex = i;
  }
  if(aRepLens[RepMaxIndex] > m_NumFastBytes)
  {
    aBackRes = RepMaxIndex;
    MovePos(aRepLens[RepMaxIndex] - 1);
    return aRepLens[RepMaxIndex];
  }

  if(aLenMain > m_NumFastBytes)
  {
    UINT32 aBackMain = (aLenMain < m_NumFastBytes) ? m_MatchDistances[aLenMain] :
        m_MatchDistances[m_NumFastBytes];
    aBackRes = aBackMain + kNumRepDistances; 
    MovePos(aLenMain - 1);
    return aLenMain;
  }
  BYTE aCurrentByte = m_MatchFinder.GetIndexByte(0 - 1);

  m_Optimum[0].State = m_State;

  BYTE aMatchByte;
  
  aMatchByte = m_MatchFinder.GetIndexByte(0 - m_RepDistances[0] - 1 - 1);

  UINT32 aPosState = (aPosition & m_PosStateMask);

  m_Optimum[1].Price = m_MainChoiceEncoders[m_State.m_Index][aPosState].GetPrice(kMainChoiceLiteralIndex) + 
      m_LiteralEncoder.GetPrice(aPosition, m_PreviousByte, m_PeviousIsMatch, aMatchByte, aCurrentByte);
  m_Optimum[1].MakeAsChar();

  m_Optimum[1].PosPrev = 0;

  for (i = 0; i < kNumRepDistances; i++)
    m_Optimum[0].Backs[i] = aReps[i];

  UINT32 aMatchPrice = m_MainChoiceEncoders[m_State.m_Index][aPosState].GetPrice(kMainChoiceMatchIndex);
  UINT32 aRepMatchPrice = aMatchPrice + 
      m_MatchChoiceEncoders[m_State.m_Index].GetPrice(kMatchChoiceRepetitionIndex);

  if(aMatchByte == aCurrentByte)
  {
    UINT32 aShortRepPrice = aRepMatchPrice + GetRepLen1Price(m_State, aPosState);
    if(aShortRepPrice < m_Optimum[1].Price)
    {
      m_Optimum[1].Price = aShortRepPrice;
      m_Optimum[1].MakeAsShortRep();
    }
  }
  if(aLenMain < 2)
  {
    aBackRes = m_Optimum[1].BackPrev;
    return 1;
  }

  
  UINT32 aNormalMatchPrice = aMatchPrice + 
      m_MatchChoiceEncoders[m_State.m_Index].GetPrice(kMatchChoiceDistanceIndex);

  if (aLenMain <= aRepLens[RepMaxIndex])
    aLenMain = 0;

  UINT32 aLen;
  for(aLen = 2; aLen <= aLenMain; aLen++)
  {
    m_Optimum[aLen].PosPrev = 0;
    m_Optimum[aLen].BackPrev = m_MatchDistances[aLen] + kNumRepDistances;
    m_Optimum[aLen].Price = aNormalMatchPrice + 
        GetPosLenPrice(m_MatchDistances[aLen], aLen, aPosState);
    m_Optimum[aLen].Prev1IsChar = false;
  }

  if (aLenMain < aRepLens[RepMaxIndex])
    aLenMain = aRepLens[RepMaxIndex];

  for (; aLen <= aLenMain; aLen++)
    m_Optimum[aLen].Price = kIfinityPrice;

  for(i = 0; i < kNumRepDistances; i++)
  {
    unsigned aRepLen = aRepLens[i];
    for(UINT32 aLenTest = 2; aLenTest <= aRepLen; aLenTest++)
    {
      UINT32 aCurAndLenPrice = aRepMatchPrice + GetRepPrice(i, aLenTest, m_State, aPosState);
      COptimal &anOptimum = m_Optimum[aLenTest];
      if (aCurAndLenPrice < anOptimum.Price) 
      {
        anOptimum.Price = aCurAndLenPrice;
        anOptimum.PosPrev = 0;
        anOptimum.BackPrev = i;
        anOptimum.Prev1IsChar = false;
      }
    }
  }

  UINT32 aCur = 0;
  UINT32 aLenEnd = aLenMain;

  while(true)
  {
    aCur++;
    if(aCur == aLenEnd)  
      return Backward(aBackRes, aCur);
    aPosition++;
    UINT32 aPosPrev = m_Optimum[aCur].PosPrev;
    CState aState;
    if (m_Optimum[aCur].Prev1IsChar)
    {
      aPosPrev--;
      if (m_Optimum[aCur].Prev2)
      {
        aState = m_Optimum[m_Optimum[aCur].PosPrev2].State;
        if (m_Optimum[aCur].BackPrev2 < kNumRepDistances)
          aState.UpdateRep();
        else
          aState.UpdateMatch();
      }
      else
        aState = m_Optimum[aPosPrev].State;
      aState.UpdateChar();
    }
    else
      aState = m_Optimum[aPosPrev].State;
    bool aPrevWasMatch;
    if (aPosPrev == aCur - 1)
    {
      if (m_Optimum[aCur].IsShortRep())
      {
        aPrevWasMatch = true;
        aState.UpdateShortRep();
      }
      else
      {
        aPrevWasMatch = false;
        aState.UpdateChar();
      }
      /*
      if (m_Optimum[aCur].Prev1IsChar)
        for(int i = 0; i < kNumRepDistances; i++)
          aReps[i] = m_Optimum[aPosPrev].Backs[i];
      */
    }
    else
    {
      aPrevWasMatch = true;
      UINT32 aPos;
      if (m_Optimum[aCur].Prev1IsChar && m_Optimum[aCur].Prev2)
      {
        aPosPrev = m_Optimum[aCur].PosPrev2;
        aPos = m_Optimum[aCur].BackPrev2;
        aState.UpdateRep();
      }
      else
      {
        aPos = m_Optimum[aCur].BackPrev;
        if (aPos < kNumRepDistances)
          aState.UpdateRep();
        else
          aState.UpdateMatch();
      }
      if (aPos < kNumRepDistances)
      {
        aReps[0] = m_Optimum[aPosPrev].Backs[aPos];
    		UINT32 i;
        for(i = 1; i <= aPos; i++)
          aReps[i] = m_Optimum[aPosPrev].Backs[i - 1];
        for(; i < kNumRepDistances; i++)
          aReps[i] = m_Optimum[aPosPrev].Backs[i];
      }
      else
      {
        aReps[0] = (aPos - kNumRepDistances);
        for(UINT32 i = 1; i < kNumRepDistances; i++)
          aReps[i] = m_Optimum[aPosPrev].Backs[i - 1];
      }
    }
    m_Optimum[aCur].State = aState;
    for(UINT32 i = 0; i < kNumRepDistances; i++)
      m_Optimum[aCur].Backs[i] = aReps[i];
    UINT32 aNewLen = ReadMatchDistances();
    if(aNewLen > m_NumFastBytes)
    {
      m_LongestMatchLength = aNewLen;
      m_LongestMatchWasFound = true;
      return Backward(aBackRes, aCur);
    }
    UINT32 aCurPrice = m_Optimum[aCur].Price; 
    // BYTE aCurrentByte  = m_MatchFinder.GetIndexByte(0 - 1);
    // BYTE aMatchByte = m_MatchFinder.GetIndexByte(0 - aReps[0] - 1 - 1);
    const BYTE *aData = m_MatchFinder.GetPointerToCurrentPos() - 1;
    BYTE aCurrentByte = *aData;
    BYTE aMatchByte = aData[0 - aReps[0] - 1];

    UINT32 aPosState = (aPosition & m_PosStateMask);

    UINT32 aCurAnd1Price = aCurPrice +
        m_MainChoiceEncoders[aState.m_Index][aPosState].GetPrice(kMainChoiceLiteralIndex) +
        m_LiteralEncoder.GetPrice(aPosition, aData[-1], aPrevWasMatch, aMatchByte, aCurrentByte);

    COptimal &aNextOptimum = m_Optimum[aCur + 1];

    bool aNextIsChar = false;
    if (aCurAnd1Price < aNextOptimum.Price) 
    {
      aNextOptimum.Price = aCurAnd1Price;
      aNextOptimum.PosPrev = aCur;
      aNextOptimum.MakeAsChar();
      aNextIsChar = true;
    }

    UINT32 aMatchPrice = aCurPrice + m_MainChoiceEncoders[aState.m_Index][aPosState].GetPrice(kMainChoiceMatchIndex);
    UINT32 aRepMatchPrice = aMatchPrice + m_MatchChoiceEncoders[aState.m_Index].GetPrice(kMatchChoiceRepetitionIndex);
    
    if(aMatchByte == aCurrentByte &&
        !(aNextOptimum.PosPrev < aCur && aNextOptimum.BackPrev == 0))
    {
      UINT32 aShortRepPrice = aRepMatchPrice + GetRepLen1Price(aState, aPosState);
      if(aShortRepPrice <= aNextOptimum.Price)
      {
        aNextOptimum.Price = aShortRepPrice;
        aNextOptimum.PosPrev = aCur;
        aNextOptimum.MakeAsShortRep();
        // aNextIsChar = false;
      }
    }
    /*
    if(aNewLen == 2 && m_MatchDistances[2] >= kDistLimit2) // test it maybe set 2000 ?
      continue;
    */

    UINT32 aNumAvailableBytes = m_MatchFinder.GetNumAvailableBytes() + 1;
    aNumAvailableBytes = MyMin(kNumOpts - 1 - aCur, aNumAvailableBytes);

    if (aNumAvailableBytes < 2)
      continue;
    if (aNumAvailableBytes > m_NumFastBytes)
      aNumAvailableBytes = m_NumFastBytes;
    if (aNumAvailableBytes >= 3 && !aNextIsChar)
    {
      UINT32 aBackOffset = aReps[0] + 1;
      UINT32 aTemp;
      for (aTemp = 1; aTemp < aNumAvailableBytes; aTemp++)
        if (aData[aTemp] != aData[aTemp - aBackOffset])
          break;
      UINT32 aLenTest2 = aTemp - 1;
      if (aLenTest2 >= 2)
      {
        CState aState2 = aState;
        aState2.UpdateChar();
        UINT32 aPosStateNext = (aPosition + 1) & m_PosStateMask;
        UINT32 aNextRepMatchPrice = aCurAnd1Price + 
            m_MainChoiceEncoders[aState2.m_Index][aPosStateNext].GetPrice(kMainChoiceMatchIndex) +
            m_MatchChoiceEncoders[aState2.m_Index].GetPrice(kMatchChoiceRepetitionIndex);
        // for (; aLenTest2 >= 2; aLenTest2--)
        {
          while(aLenEnd < aCur + 1 + aLenTest2)
            m_Optimum[++aLenEnd].Price = kIfinityPrice;
          UINT32 aCurAndLenPrice = aNextRepMatchPrice + GetRepPrice(
              0, aLenTest2, aState2, aPosStateNext);
          COptimal &anOptimum = m_Optimum[aCur + 1 + aLenTest2];
          if (aCurAndLenPrice < anOptimum.Price) 
          {
            anOptimum.Price = aCurAndLenPrice;
            anOptimum.PosPrev = aCur + 1;
            anOptimum.BackPrev = 0;
            anOptimum.Prev1IsChar = true;
            anOptimum.Prev2 = false;
          }
        }
      }
    }
    for(UINT32 aRepIndex = 0; aRepIndex < kNumRepDistances; aRepIndex++)
    {
      // UINT32 aRepLen = m_MatchFinder.GetMatchLen(0 - 1, aReps[aRepIndex], aNewLen); // test it;
      UINT32 aBackOffset = aReps[aRepIndex] + 1;
      UINT32 aLenTest;
      for (aLenTest = 0; aLenTest < aNumAvailableBytes; aLenTest++)
        if (aData[aLenTest] != aData[aLenTest - aBackOffset])
          break;
      for(; aLenTest >= 2; aLenTest--)
      {
        while(aLenEnd < aCur + aLenTest)
          m_Optimum[++aLenEnd].Price = kIfinityPrice;
        UINT32 aCurAndLenPrice = aRepMatchPrice + GetRepPrice(aRepIndex, aLenTest, aState, aPosState);
        COptimal &anOptimum = m_Optimum[aCur + aLenTest];
        if (aCurAndLenPrice < anOptimum.Price) 
        {
          anOptimum.Price = aCurAndLenPrice;
          anOptimum.PosPrev = aCur;
          anOptimum.BackPrev = aRepIndex;
          anOptimum.Prev1IsChar = false;
        }

        /*
        if (m_MaxMode)
        {
          UINT32 aTemp;
          for (aTemp = aLenTest + 1; aTemp < aNumAvailableBytes; aTemp++)
            if (aData[aTemp] != aData[aTemp - aBackOffset])
              break;
          UINT32 aLenTest2 = aTemp - (aLenTest + 1);
          if (aLenTest2 >= 2)
          {
            CState aState2 = aState;
            aState2.UpdateRep();
            UINT32 aPosStateNext = (aPosition + aLenTest) & m_PosStateMask;
            UINT32 aCurAndLenCharPrice = aCurAndLenPrice + 
                m_MainChoiceEncoders[aState2.m_Index][aPosStateNext].GetPrice(kMainChoiceLiteralIndex) +
                m_LiteralEncoder.GetPrice(aPosition + aLenTest, aData[aLenTest - 1], 
                true, aData[aLenTest - aBackOffset], aData[aLenTest]);
            aState2.UpdateChar();
            aPosStateNext = (aPosition + aLenTest + 1) & m_PosStateMask;
            UINT32 aNextMatchPrice = aCurAndLenCharPrice + m_MainChoiceEncoders[aState2.m_Index][aPosStateNext].GetPrice(kMainChoiceMatchIndex);
            UINT32 aNextRepMatchPrice = aNextMatchPrice + m_MatchChoiceEncoders[aState2.m_Index].GetPrice(kMatchChoiceRepetitionIndex);
            
            // for(; aLenTest2 >= 2; aLenTest2--)
            {
              UINT32 anOffset = aLenTest + 1 + aLenTest2;
              while(aLenEnd < aCur + anOffset)
                m_Optimum[++aLenEnd].Price = kIfinityPrice;
              UINT32 aCurAndLenPrice = aNextRepMatchPrice + GetRepPrice(
                  0, aLenTest2, aState2, aPosStateNext);
              COptimal &anOptimum = m_Optimum[aCur + anOffset];
              if (aCurAndLenPrice < anOptimum.Price) 
              {
                anOptimum.Price = aCurAndLenPrice;
                anOptimum.PosPrev = aCur + aLenTest + 1;
                anOptimum.BackPrev = 0;
                anOptimum.Prev1IsChar = true;
                anOptimum.Prev2 = true;
                anOptimum.PosPrev2 = aCur;
                anOptimum.BackPrev2 = aRepIndex;
              }
            }
          }
        }
        */
      }
    }
    
    //    for(UINT32 aLenTest = 2; aLenTest <= aNewLen; aLenTest++)
    if (aNewLen > aNumAvailableBytes)
      aNewLen = aNumAvailableBytes;
    if (aNewLen >= 2)
    {
      if (aNewLen == 2 && m_MatchDistances[2] >= 0x80)
        continue;
      UINT32 aNormalMatchPrice = aMatchPrice + 
        m_MatchChoiceEncoders[aState.m_Index].GetPrice(kMatchChoiceDistanceIndex);
      while(aLenEnd < aCur + aNewLen)
        m_Optimum[++aLenEnd].Price = kIfinityPrice;

      for(UINT32 aLenTest = aNewLen; aLenTest >= 2; aLenTest--)
      {
        UINT32 aCurBack = m_MatchDistances[aLenTest];
        UINT32 aCurAndLenPrice = aNormalMatchPrice + GetPosLenPrice(aCurBack, aLenTest, aPosState);
        COptimal &anOptimum = m_Optimum[aCur + aLenTest];
        if (aCurAndLenPrice < anOptimum.Price) 
        {
          anOptimum.Price = aCurAndLenPrice;
          anOptimum.PosPrev = aCur;
          anOptimum.BackPrev = aCurBack + kNumRepDistances;
          anOptimum.Prev1IsChar = false;
        }

        if (m_MaxMode)
        {
          UINT32 aBackOffset = aCurBack + 1;
          UINT32 aTemp;
          for (aTemp = aLenTest + 1; aTemp < aNumAvailableBytes; aTemp++)
            if (aData[aTemp] != aData[aTemp - aBackOffset])
              break;
          UINT32 aLenTest2 = aTemp - (aLenTest + 1);
          if (aLenTest2 >= 2)
          {
            CState aState2 = aState;
            aState2.UpdateMatch();
            UINT32 aPosStateNext = (aPosition + aLenTest) & m_PosStateMask;
            UINT32 aCurAndLenCharPrice = aCurAndLenPrice + 
                m_MainChoiceEncoders[aState2.m_Index][aPosStateNext].GetPrice(kMainChoiceLiteralIndex) +
                m_LiteralEncoder.GetPrice(aPosition + aLenTest, aData[aLenTest - 1], 
                true, aData[aLenTest - aBackOffset], aData[aLenTest]);
            aState2.UpdateChar();
            aPosStateNext = (aPosition + aLenTest + 1) & m_PosStateMask;
            UINT32 aNextMatchPrice = aCurAndLenCharPrice + m_MainChoiceEncoders[aState2.m_Index][aPosStateNext].GetPrice(kMainChoiceMatchIndex);
            UINT32 aNextRepMatchPrice = aNextMatchPrice + m_MatchChoiceEncoders[aState2.m_Index].GetPrice(kMatchChoiceRepetitionIndex);
            
            // for(; aLenTest2 >= 2; aLenTest2--)
            {
              UINT32 anOffset = aLenTest + 1 + aLenTest2;
              while(aLenEnd < aCur + anOffset)
                m_Optimum[++aLenEnd].Price = kIfinityPrice;
              UINT32 aCurAndLenPrice = aNextRepMatchPrice + GetRepPrice(
                  0, aLenTest2, aState2, aPosStateNext);
              COptimal &anOptimum = m_Optimum[aCur + anOffset];
              if (aCurAndLenPrice < anOptimum.Price) 
              {
                anOptimum.Price = aCurAndLenPrice;
                anOptimum.PosPrev = aCur + aLenTest + 1;
                anOptimum.BackPrev = 0;
                anOptimum.Prev1IsChar = true;
                anOptimum.Prev2 = true;
                anOptimum.PosPrev2 = aCur;
                anOptimum.BackPrev2 = aCurBack + kNumRepDistances;
              }
            }
          }
        }
      }
    }
  }
}

static bool inline ChangePair(UINT32 aSmall, UINT32 aBig)
{
  const int kDif = 7;
  return (aSmall < (UINT32(1) << (32-kDif)) && aBig >= (aSmall << kDif));
}


UINT32 CEncoder::GetOptimumFast(UINT32 &aBackRes, UINT32 aPosition)
{
  UINT32 aLenMain;
  if (!m_LongestMatchWasFound)
    aLenMain = ReadMatchDistances();
  else
  {
    aLenMain = m_LongestMatchLength;
    m_LongestMatchWasFound = false;
  }
  UINT32 aRepLens[kNumRepDistances];
  UINT32 RepMaxIndex = 0;
  for(int i = 0; i < kNumRepDistances; i++)
  {
    aRepLens[i] = m_MatchFinder.GetMatchLen(0 - 1, m_RepDistances[i], kMatchMaxLen);
    if (i == 0 || aRepLens[i] > aRepLens[RepMaxIndex])
      RepMaxIndex = i;
  }
  if(aRepLens[RepMaxIndex] >= m_NumFastBytes)
  {
    aBackRes = RepMaxIndex;
    MovePos(aRepLens[RepMaxIndex] - 1);
    return aRepLens[RepMaxIndex];
  }
  if(aLenMain >= m_NumFastBytes)
  {
    aBackRes = m_MatchDistances[m_NumFastBytes] + kNumRepDistances; 
    MovePos(aLenMain - 1);
    return aLenMain;
  }
  while (aLenMain > 2)
  {
    if (!ChangePair(m_MatchDistances[aLenMain - 1], 
        m_MatchDistances[aLenMain]))
      break;
    aLenMain--;
  }
  if (aLenMain == 2 && m_MatchDistances[2] >= 0x80)
    aLenMain = 1;

  UINT32 aBackMain = m_MatchDistances[aLenMain];
  if (aRepLens[RepMaxIndex] >= 2)
  {
    if (aRepLens[RepMaxIndex] + 1 >= aLenMain || 
        aRepLens[RepMaxIndex] + 2 >= aLenMain && (aBackMain > (1<<12)))
    {
      aBackRes = RepMaxIndex;
      MovePos(aRepLens[RepMaxIndex] - 1);
      return aRepLens[RepMaxIndex];
    }
  }
  

  if (aLenMain >= 2)
  {
    m_LongestMatchLength = ReadMatchDistances();
    if (m_LongestMatchLength >= 2 &&
      (
        (m_LongestMatchLength >= aLenMain && 
          m_MatchDistances[aLenMain] < aBackMain) || 
        m_LongestMatchLength == aLenMain + 1 && 
          !ChangePair(aBackMain, m_MatchDistances[m_LongestMatchLength]) ||
        m_LongestMatchLength > aLenMain + 1 ||
        m_LongestMatchLength + 1 >= aLenMain && 
          ChangePair(m_MatchDistances[aLenMain - 1], aBackMain)
      )
      )
    {
      m_LongestMatchWasFound = true;
      aBackRes = UINT32(-1);
      return 1;
    }
    for(int i = 0; i < kNumRepDistances; i++)
    {
      UINT32 aRepLen = m_MatchFinder.GetMatchLen(0 - 1, m_RepDistances[i], kMatchMaxLen);
      if (aRepLen >= 2 && aRepLen + 1 >= aLenMain)
      {
        m_LongestMatchWasFound = true;
        aBackRes = UINT32(-1);
        return 1;
      }
    }
    aBackRes = aBackMain + kNumRepDistances; 
    MovePos(aLenMain - 2);
    return aLenMain;
  }
  aBackRes = UINT32(-1);
  return 1;
}

HRESULT CEncoder::Flush()
{
  m_RangeEncoder.FlushData();
  return m_RangeEncoder.FlushStream();
}

HRESULT CEncoder::CodeReal(ISequentialInStream *anInStream,
      ISequentialOutStream *anOutStream, 
      const UINT64 *anInSize)
{
  RETURN_IF_NOT_S_OK(Create());
  Init(anInStream, anOutStream);

  if (m_MatchFinder.GetNumAvailableBytes() == 0)
    return Flush();

  if (!m_FastMode)
  {
    FillPosSlotPrices();
    FillDistancesPrices();
    FillAlignPrices();
  }

  m_LenEncoder.SetTableSize(m_NumFastBytes);
  m_LenEncoder.UpdateTables();
  m_RepMatchLenEncoder.SetTableSize(m_NumFastBytes);
  m_RepMatchLenEncoder.UpdateTables();

  UINT64 aLastPosSlotFillingPos = 0;

  UINT64 aNowPos64 = 0;
  ReadMatchDistances();
  UINT32 aPosState = UINT32(aNowPos64) & m_PosStateMask;
  m_MainChoiceEncoders[m_State.m_Index][aPosState].Encode(&m_RangeEncoder, kMainChoiceLiteralIndex);
  m_State.UpdateChar();
  BYTE aByte = m_MatchFinder.GetIndexByte(0 - m_AdditionalOffset);
  m_LiteralEncoder.Encode(&m_RangeEncoder, UINT32(aNowPos64), m_PreviousByte, false, 0, aByte);
  m_PreviousByte = aByte;
  m_AdditionalOffset--;
  aNowPos64++;
  if (m_MatchFinder.GetNumAvailableBytes() == 0)
    return Flush();
  while(true)
  {
    UINT32 aPos;
    UINT32 aPosState = UINT32(aNowPos64) & m_PosStateMask;

    UINT32 aLen;
    if (m_FastMode)
      aLen = GetOptimumFast(aPos, UINT32(aNowPos64));
    else
      aLen = GetOptimum(aPos, UINT32(aNowPos64));

    if(aLen == 1 && aPos == (-1))
    {
      m_MainChoiceEncoders[m_State.m_Index][aPosState].Encode(&m_RangeEncoder, kMainChoiceLiteralIndex);
      m_State.UpdateChar();
      BYTE aMatchByte;
      if(m_PeviousIsMatch)
        aMatchByte = m_MatchFinder.GetIndexByte(0 - m_RepDistances[0] - 1 - m_AdditionalOffset);
      BYTE aByte = m_MatchFinder.GetIndexByte(0 - m_AdditionalOffset);
      m_LiteralEncoder.Encode(&m_RangeEncoder, UINT32(aNowPos64), m_PreviousByte, m_PeviousIsMatch, aMatchByte, aByte);
      m_PreviousByte = aByte;
      m_PeviousIsMatch = false;
    }
    else
    {
      m_PeviousIsMatch = true;
      m_MainChoiceEncoders[m_State.m_Index][aPosState].Encode(&m_RangeEncoder, kMainChoiceMatchIndex);
      if(aPos < kNumRepDistances)
      {
        m_MatchChoiceEncoders[m_State.m_Index].Encode(&m_RangeEncoder, kMatchChoiceRepetitionIndex);
        if(aPos == 0)
        {
          m_MatchRepChoiceEncoders[m_State.m_Index].Encode(&m_RangeEncoder, 0);
          if(aLen == 1)
            m_MatchRepShortChoiceEncoders[m_State.m_Index][aPosState].Encode(&m_RangeEncoder, 0);
          else
            m_MatchRepShortChoiceEncoders[m_State.m_Index][aPosState].Encode(&m_RangeEncoder, 1);
        }
        else
        {
          m_MatchRepChoiceEncoders[m_State.m_Index].Encode(&m_RangeEncoder, 1);
          if (aPos == 1)
            m_MatchRep1ChoiceEncoders[m_State.m_Index].Encode(&m_RangeEncoder, 0);
          else
          {
            m_MatchRep1ChoiceEncoders[m_State.m_Index].Encode(&m_RangeEncoder, 1);
            m_MatchRep2ChoiceEncoders[m_State.m_Index].Encode(&m_RangeEncoder, aPos - 2);
          }
        }
        if (aLen == 1)
          m_State.UpdateShortRep();
        else
        {
          m_RepMatchLenEncoder.Encode(&m_RangeEncoder, aLen - kMatchMinLen, aPosState);
          m_State.UpdateRep();
        }


        UINT32 aDistance = m_RepDistances[aPos];
        if (aPos != 0)
        {
          for(UINT32 i = aPos; i >= 1; i--)
            m_RepDistances[i] = m_RepDistances[i - 1];
          m_RepDistances[0] = aDistance;
        }
      }
      else
      {
        m_MatchChoiceEncoders[m_State.m_Index].Encode(&m_RangeEncoder, kMatchChoiceDistanceIndex);
        m_State.UpdateMatch();
        m_LenEncoder.Encode(&m_RangeEncoder, aLen - kMatchMinLen, aPosState);
        aPos -= kNumRepDistances;
        UINT32 aPosSlot = GetPosSlot(aPos);
        UINT32 aLenToPosState = GetLenToPosState(aLen);
        m_PosSlotEncoder[aLenToPosState].Encode(&m_RangeEncoder, aPosSlot);
        
        UINT32 aFooterBits = kDistDirectBits[aPosSlot];
        UINT32 aPosReduced = aPos - kDistStart[aPosSlot];
        if (aPosSlot >= kStartPosModelIndex)
        {
          if (aPosSlot < kEndPosModelIndex)
            m_PosEncoders[aPosSlot - kStartPosModelIndex].Encode(&m_RangeEncoder, aPosReduced);
          else
          {
            m_RangeEncoder.EncodeDirectBits(aPosReduced >> kNumAlignBits, aFooterBits - kNumAlignBits);
            m_PosAlignEncoder.Encode(&m_RangeEncoder, aPosReduced & kAlignMask);
            if (!m_FastMode)
              if (--m_AlignPriceCount == 0)
                FillAlignPrices();
          }
        }
        UINT32 aDistance = aPos;
        for(UINT32 i = kNumRepDistances - 1; i >= 1; i--)
          m_RepDistances[i] = m_RepDistances[i - 1];
        m_RepDistances[0] = aDistance;
      }
      m_PreviousByte = m_MatchFinder.GetIndexByte(aLen - 1 - m_AdditionalOffset);
    }
    m_AdditionalOffset -= aLen;
    aNowPos64 += aLen;
    if (!m_FastMode)
      if (aNowPos64 - aLastPosSlotFillingPos >= (1 << 9))
      {
        FillPosSlotPrices();
        FillDistancesPrices();
        aLastPosSlotFillingPos = aNowPos64;
      }
    if (m_AdditionalOffset == 0 && m_MatchFinder.GetNumAvailableBytes() == 0)
      return Flush();
  }
}

HRESULT CEncoder::Code(ISequentialInStream *anInStream, ISequentialOutStream *anOutStream, const UINT64 *anInSize)
{
  try {
    return CodeReal(anInStream, anOutStream, anInSize);
  } catch (HRESULT& e) {
    return e;
  } catch (...) {
    return E_FAIL;
  }
}
  
void CEncoder::FillPosSlotPrices()
{
  for (int aLenToPosState = 0; aLenToPosState < kNumLenToPosStates; aLenToPosState++)
  {
    UINT32 aPosSlot;
    for (aPosSlot = 0; aPosSlot < kEndPosModelIndex && aPosSlot < m_DistTableSize; aPosSlot++)
      m_PosSlotPrices[aLenToPosState][aPosSlot] = m_PosSlotEncoder[aLenToPosState].GetPrice(aPosSlot);
    for (; aPosSlot < m_DistTableSize; aPosSlot++)
      m_PosSlotPrices[aLenToPosState][aPosSlot] = m_PosSlotEncoder[aLenToPosState].GetPrice(aPosSlot) + 
          ((kDistDirectBits[aPosSlot] - kNumAlignBits) << kNumBitPriceShiftBits);
  }
}

void CEncoder::FillDistancesPrices()
{
  for (int aLenToPosState = 0; aLenToPosState < kNumLenToPosStates; aLenToPosState++)
  {
    UINT32 i;
    for (i = 0; i < kStartPosModelIndex; i++)
      m_DistancesPrices[aLenToPosState][i] = m_PosSlotPrices[aLenToPosState][i];
    for (; i < kNumFullDistances; i++)
    { 
      UINT32 aPosSlot = GetPosSlot(i);
      m_DistancesPrices[aLenToPosState][i] = m_PosSlotPrices[aLenToPosState][aPosSlot] +
          m_PosEncoders[aPosSlot - kStartPosModelIndex].GetPrice(i - kDistStart[aPosSlot]);
    }
  }
}

void CEncoder::FillAlignPrices()
{
  for (int i = 0; i < kAlignTableSize; i++)
    m_AlignPrices[i] = m_PosAlignEncoder.GetPrice(i);
  m_AlignPriceCount = kAlignTableSize;
}

}}
