// Decompiled with JetBrains decompiler
// Type: SevenZip.Compression.LZMA.Encoder
// Assembly: P4CTEMP, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 7BE4E538-8555-4C2E-974B-99E556F5462C
// Assembly location: C:\Users\Administrateur\Downloads\Virusshare-00000-msil\Trojan-Ransom.Win32.Gimemo.ayt-624a52079bf1703bcd3bcc9d2d3716b6126fd05655e25289d19142f9aae02eb5.exe

using SevenZip.Compression.LZ;
using SevenZip.Compression.RangeCoder;
using System;
using System.IO;

namespace SevenZip.Compression.LZMA
{
  public class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties
  {
    private const uint kIfinityPrice = 268435455;
    private const int kDefaultDictionaryLogSize = 22;
    private const uint kNumFastBytesDefault = 32;
    private const uint kNumLenSpecSymbols = 16;
    private const uint kNumOpts = 4096;
    private const int kPropSize = 5;
    private static byte[] g_FastPos = new byte[2048];
    private Base.State _state = new Base.State();
    private byte _previousByte;
    private uint[] _repDistances = new uint[new IntPtr(4)];
    private Encoder.Optimal[] _optimum = new Encoder.Optimal[new IntPtr(4096)];
    private IMatchFinder _matchFinder;
    private SevenZip.Compression.RangeCoder.Encoder _rangeEncoder = new SevenZip.Compression.RangeCoder.Encoder();
    private BitEncoder[] _isMatch = new BitEncoder[new IntPtr(192)];
    private BitEncoder[] _isRep = new BitEncoder[new IntPtr(12)];
    private BitEncoder[] _isRepG0 = new BitEncoder[new IntPtr(12)];
    private BitEncoder[] _isRepG1 = new BitEncoder[new IntPtr(12)];
    private BitEncoder[] _isRepG2 = new BitEncoder[new IntPtr(12)];
    private BitEncoder[] _isRep0Long = new BitEncoder[new IntPtr(192)];
    private BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[new IntPtr(4)];
    private BitEncoder[] _posEncoders = new BitEncoder[new IntPtr(114)];
    private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(4);
    private Encoder.LenPriceTableEncoder _lenEncoder = new Encoder.LenPriceTableEncoder();
    private Encoder.LenPriceTableEncoder _repMatchLenEncoder = new Encoder.LenPriceTableEncoder();
    private Encoder.LiteralEncoder _literalEncoder = new Encoder.LiteralEncoder();
    private uint[] _matchDistances = new uint[new IntPtr(548)];
    private uint _numFastBytes = 32;
    private uint _longestMatchLength;
    private uint _numDistancePairs;
    private uint _additionalOffset;
    private uint _optimumEndIndex;
    private uint _optimumCurrentIndex;
    private bool _longestMatchWasFound;
    private uint[] _posSlotPrices = new uint[256];
    private uint[] _distancesPrices = new uint[new IntPtr(512)];
    private uint[] _alignPrices = new uint[new IntPtr(16)];
    private uint _alignPriceCount;
    private uint _distTableSize = 44;
    private int _posStateBits = 2;
    private uint _posStateMask = 3;
    private int _numLiteralPosStateBits;
    private int _numLiteralContextBits = 3;
    private uint _dictionarySize = 4194304;
    private uint _dictionarySizePrev = uint.MaxValue;
    private uint _numFastBytesPrev = uint.MaxValue;
    private long nowPos64;
    private bool _finished;
    private Stream _inStream;
    private Encoder.EMatchFinderType _matchFinderType = Encoder.EMatchFinderType.BT4;
    private bool _writeEndMark;
    private bool _needReleaseMFStream;
    private uint[] reps = new uint[new IntPtr(4)];
    private uint[] repLens = new uint[new IntPtr(4)];
    private byte[] properties = new byte[5];
    private uint[] tempPrices = new uint[new IntPtr(128)];
    private uint _matchPriceCount;
    private static string[] kMatchFinderIDs = new string[2]
    {
      "BT2",
      "BT4"
    };
    private uint _trainSize;

    static Encoder()
    {
      int index1 = 2;
      Encoder.g_FastPos[0] = (byte) 0;
      Encoder.g_FastPos[1] = (byte) 1;
      for (byte index2 = 2; index2 < (byte) 22; ++index2)
      {
        uint num1 = (uint) (1 << ((int) index2 >> 1) - 1);
        uint num2 = 0;
        while (num2 < num1)
        {
          Encoder.g_FastPos[index1] = index2;
          ++num2;
          ++index1;
        }
      }
    }

    private static uint GetPosSlot(uint pos)
    {
      if (pos < 2048U)
        return (uint) Encoder.g_FastPos[(IntPtr) pos];
      return pos < 2097152U ? (uint) Encoder.g_FastPos[(IntPtr) (pos >> 10)] + 20U : (uint) Encoder.g_FastPos[(IntPtr) (pos >> 20)] + 40U;
    }

    private static uint GetPosSlot2(uint pos)
    {
      if (pos < 131072U)
        return (uint) Encoder.g_FastPos[(IntPtr) (pos >> 6)] + 12U;
      return pos < 134217728U ? (uint) Encoder.g_FastPos[(IntPtr) (pos >> 16)] + 32U : (uint) Encoder.g_FastPos[(IntPtr) (pos >> 26)] + 52U;
    }

    private void BaseInit()
    {
      this._state.Init();
      this._previousByte = (byte) 0;
      for (uint index = 0; index < 4U; ++index)
        this._repDistances[(IntPtr) index] = 0U;
    }

    private void Create()
    {
      if (this._matchFinder == null)
      {
        BinTree binTree = new BinTree();
        int numHashBytes = 4;
        if (this._matchFinderType == Encoder.EMatchFinderType.BT2)
          numHashBytes = 2;
        binTree.SetType(numHashBytes);
        this._matchFinder = (IMatchFinder) binTree;
      }
      this._literalEncoder.Create(this._numLiteralPosStateBits, this._numLiteralContextBits);
      if ((int) this._dictionarySize == (int) this._dictionarySizePrev && (int) this._numFastBytesPrev == (int) this._numFastBytes)
        return;
      this._matchFinder.Create(this._dictionarySize, 4096U, this._numFastBytes, 274U);
      this._dictionarySizePrev = this._dictionarySize;
      this._numFastBytesPrev = this._numFastBytes;
    }

    public Encoder()
    {
      for (int index = 0; index < 4096; ++index)
        this._optimum[index] = new Encoder.Optimal();
      for (int index = 0; index < 4; ++index)
        this._posSlotEncoder[index] = new BitTreeEncoder(6);
    }

    private void SetWriteEndMarkerMode(bool writeEndMarker) => this._writeEndMark = writeEndMarker;

    private void Init()
    {
      this.BaseInit();
      this._rangeEncoder.Init();
      for (uint index1 = 0; index1 < 12U; ++index1)
      {
        for (uint index2 = 0; index2 <= this._posStateMask; ++index2)
        {
          uint index3 = (index1 << 4) + index2;
          this._isMatch[(IntPtr) index3].Init();
          this._isRep0Long[(IntPtr) index3].Init();
        }
        this._isRep[(IntPtr) index1].Init();
        this._isRepG0[(IntPtr) index1].Init();
        this._isRepG1[(IntPtr) index1].Init();
        this._isRepG2[(IntPtr) index1].Init();
      }
      this._literalEncoder.Init();
      for (uint index = 0; index < 4U; ++index)
        this._posSlotEncoder[(IntPtr) index].Init();
      for (uint index = 0; index < 114U; ++index)
        this._posEncoders[(IntPtr) index].Init();
      this._lenEncoder.Init((uint) (1 << this._posStateBits));
      this._repMatchLenEncoder.Init((uint) (1 << this._posStateBits));
      this._posAlignEncoder.Init();
      this._longestMatchWasFound = false;
      this._optimumEndIndex = 0U;
      this._optimumCurrentIndex = 0U;
      this._additionalOffset = 0U;
    }

    private void ReadMatchDistances(out uint lenRes, out uint numDistancePairs)
    {
      lenRes = 0U;
      numDistancePairs = this._matchFinder.GetMatches(this._matchDistances);
      if (numDistancePairs > 0U)
      {
        lenRes = this._matchDistances[(IntPtr) (numDistancePairs - 2U)];
        if ((int) lenRes == (int) this._numFastBytes)
          lenRes += this._matchFinder.GetMatchLen((int) lenRes - 1, this._matchDistances[(IntPtr) (numDistancePairs - 1U)], 273U - lenRes);
      }
      ++this._additionalOffset;
    }

    private void MovePos(uint num)
    {
      if (num <= 0U)
        return;
      this._matchFinder.Skip(num);
      this._additionalOffset += num;
    }

    private uint GetRepLen1Price(Base.State state, uint posState) => this._isRepG0[(IntPtr) state.Index].GetPrice0() + this._isRep0Long[(IntPtr) ((state.Index << 4) + posState)].GetPrice0();

    private uint GetPureRepPrice(uint repIndex, Base.State state, uint posState)
    {
      uint pureRepPrice;
      if (repIndex == 0U)
      {
        pureRepPrice = this._isRepG0[(IntPtr) state.Index].GetPrice0() + this._isRep0Long[(IntPtr) ((state.Index << 4) + posState)].GetPrice1();
      }
      else
      {
        uint price1 = this._isRepG0[(IntPtr) state.Index].GetPrice1();
        pureRepPrice = repIndex != 1U ? price1 + this._isRepG1[(IntPtr) state.Index].GetPrice1() + this._isRepG2[(IntPtr) state.Index].GetPrice(repIndex - 2U) : price1 + this._isRepG1[(IntPtr) state.Index].GetPrice0();
      }
      return pureRepPrice;
    }

    private uint GetRepPrice(uint repIndex, uint len, Base.State state, uint posState) => this._repMatchLenEncoder.GetPrice(len - 2U, posState) + this.GetPureRepPrice(repIndex, state, posState);

    private uint GetPosLenPrice(uint pos, uint len, uint posState)
    {
      uint lenToPosState = Base.GetLenToPosState(len);
      return (pos >= 128U ? this._posSlotPrices[(IntPtr) ((lenToPosState << 6) + Encoder.GetPosSlot2(pos))] + this._alignPrices[(IntPtr) (pos & 15U)] : this._distancesPrices[(IntPtr) (lenToPosState * 128U + pos)]) + this._lenEncoder.GetPrice(len - 2U, posState);
    }

    private uint Backward(out uint backRes, uint cur)
    {
      this._optimumEndIndex = cur;
      uint posPrev = this._optimum[(IntPtr) cur].PosPrev;
      uint backPrev = this._optimum[(IntPtr) cur].BackPrev;
      do
      {
        if (this._optimum[(IntPtr) cur].Prev1IsChar)
        {
          this._optimum[(IntPtr) posPrev].MakeAsChar();
          this._optimum[(IntPtr) posPrev].PosPrev = posPrev - 1U;
          if (this._optimum[(IntPtr) cur].Prev2)
          {
            this._optimum[(IntPtr) (posPrev - 1U)].Prev1IsChar = false;
            this._optimum[(IntPtr) (posPrev - 1U)].PosPrev = this._optimum[(IntPtr) cur].PosPrev2;
            this._optimum[(IntPtr) (posPrev - 1U)].BackPrev = this._optimum[(IntPtr) cur].BackPrev2;
          }
        }
        uint index = posPrev;
        uint num = backPrev;
        backPrev = this._optimum[(IntPtr) index].BackPrev;
        posPrev = this._optimum[(IntPtr) index].PosPrev;
        this._optimum[(IntPtr) index].BackPrev = num;
        this._optimum[(IntPtr) index].PosPrev = cur;
        cur = index;
      }
      while (cur > 0U);
      backRes = this._optimum[0].BackPrev;
      this._optimumCurrentIndex = this._optimum[0].PosPrev;
      return this._optimumCurrentIndex;
    }

    private uint GetOptimum(uint position, out uint backRes)
    {
      if ((int) this._optimumEndIndex != (int) this._optimumCurrentIndex)
      {
        uint optimum = this._optimum[(IntPtr) this._optimumCurrentIndex].PosPrev - this._optimumCurrentIndex;
        backRes = this._optimum[(IntPtr) this._optimumCurrentIndex].BackPrev;
        this._optimumCurrentIndex = this._optimum[(IntPtr) this._optimumCurrentIndex].PosPrev;
        return optimum;
      }
      this._optimumCurrentIndex = this._optimumEndIndex = 0U;
      uint lenRes1;
      uint numDistancePairs;
      if (!this._longestMatchWasFound)
      {
        this.ReadMatchDistances(out lenRes1, out numDistancePairs);
      }
      else
      {
        lenRes1 = this._longestMatchLength;
        numDistancePairs = this._numDistancePairs;
        this._longestMatchWasFound = false;
      }
      uint num1 = this._matchFinder.GetNumAvailableBytes() + 1U;
      if (num1 < 2U)
      {
        backRes = uint.MaxValue;
        return 1;
      }
      if (num1 > 273U)
        ;
      uint index1 = 0;
      for (uint index2 = 0; index2 < 4U; ++index2)
      {
        this.reps[(IntPtr) index2] = this._repDistances[(IntPtr) index2];
        this.repLens[(IntPtr) index2] = this._matchFinder.GetMatchLen(-1, this.reps[(IntPtr) index2], 273U);
        if (this.repLens[(IntPtr) index2] > this.repLens[(IntPtr) index1])
          index1 = index2;
      }
      if (this.repLens[(IntPtr) index1] >= this._numFastBytes)
      {
        backRes = index1;
        uint repLen = this.repLens[(IntPtr) index1];
        this.MovePos(repLen - 1U);
        return repLen;
      }
      if (lenRes1 >= this._numFastBytes)
      {
        backRes = this._matchDistances[(IntPtr) (numDistancePairs - 1U)] + 4U;
        this.MovePos(lenRes1 - 1U);
        return lenRes1;
      }
      byte indexByte1 = this._matchFinder.GetIndexByte(-1);
      byte indexByte2 = this._matchFinder.GetIndexByte(-(int) this._repDistances[0] - 1 - 1);
      if (lenRes1 < 2U && (int) indexByte1 != (int) indexByte2 && this.repLens[(IntPtr) index1] < 2U)
      {
        backRes = uint.MaxValue;
        return 1;
      }
      this._optimum[0].State = this._state;
      uint posState1 = position & this._posStateMask;
      Encoder.Optimal optimal1 = this._optimum[1];
      int price0 = (int) this._isMatch[(IntPtr) ((this._state.Index << 4) + posState1)].GetPrice0();
      Encoder.LiteralEncoder.Encoder2 subCoder = this._literalEncoder.GetSubCoder(position, this._previousByte);
      int price1 = (int) subCoder.GetPrice(!this._state.IsCharState(), indexByte2, indexByte1);
      int num2 = price0 + price1;
      optimal1.Price = (uint) num2;
      this._optimum[1].MakeAsChar();
      uint price1_1 = this._isMatch[(IntPtr) ((this._state.Index << 4) + posState1)].GetPrice1();
      uint num3 = price1_1 + this._isRep[(IntPtr) this._state.Index].GetPrice1();
      if ((int) indexByte2 == (int) indexByte1)
      {
        uint num4 = num3 + this.GetRepLen1Price(this._state, posState1);
        if (num4 < this._optimum[1].Price)
        {
          this._optimum[1].Price = num4;
          this._optimum[1].MakeAsShortRep();
        }
      }
      uint num5 = lenRes1 >= this.repLens[(IntPtr) index1] ? lenRes1 : this.repLens[(IntPtr) index1];
      if (num5 < 2U)
      {
        backRes = this._optimum[1].BackPrev;
        return 1;
      }
      this._optimum[1].PosPrev = 0U;
      this._optimum[0].Backs0 = this.reps[0];
      this._optimum[0].Backs1 = this.reps[1];
      this._optimum[0].Backs2 = this.reps[2];
      this._optimum[0].Backs3 = this.reps[3];
      uint num6 = num5;
      do
      {
        this._optimum[(IntPtr) num6--].Price = 268435455U;
      }
      while (num6 >= 2U);
      for (uint repIndex = 0; repIndex < 4U; ++repIndex)
      {
        uint repLen = this.repLens[(IntPtr) repIndex];
        if (repLen >= 2U)
        {
          uint num7 = num3 + this.GetPureRepPrice(repIndex, this._state, posState1);
          do
          {
            uint num8 = num7 + this._repMatchLenEncoder.GetPrice(repLen - 2U, posState1);
            Encoder.Optimal optimal2 = this._optimum[(IntPtr) repLen];
            if (num8 < optimal2.Price)
            {
              optimal2.Price = num8;
              optimal2.PosPrev = 0U;
              optimal2.BackPrev = repIndex;
              optimal2.Prev1IsChar = false;
            }
          }
          while (--repLen >= 2U);
        }
      }
      uint num9 = price1_1 + this._isRep[(IntPtr) this._state.Index].GetPrice0();
      uint len = this.repLens[0] >= 2U ? this.repLens[0] + 1U : 2U;
      if (len <= lenRes1)
      {
        uint index3 = 0;
        while (len > this._matchDistances[(IntPtr) index3])
          index3 += 2U;
        while (true)
        {
          uint matchDistance = this._matchDistances[(IntPtr) (index3 + 1U)];
          uint num10 = num9 + this.GetPosLenPrice(matchDistance, len, posState1);
          Encoder.Optimal optimal3 = this._optimum[(IntPtr) len];
          if (num10 < optimal3.Price)
          {
            optimal3.Price = num10;
            optimal3.PosPrev = 0U;
            optimal3.BackPrev = matchDistance + 4U;
            optimal3.Prev1IsChar = false;
          }
          if ((int) len == (int) this._matchDistances[(IntPtr) index3])
          {
            index3 += 2U;
            if ((int) index3 == (int) numDistancePairs)
              break;
          }
          ++len;
        }
      }
      uint cur = 0;
label_45:
      uint lenRes2;
      Base.State state1;
      uint posState2;
      uint num11;
      uint num12;
      uint num13;
      do
      {
        byte indexByte3;
        byte indexByte4;
        uint num14;
        bool flag;
        uint num15;
        uint limit1;
        do
        {
          ++cur;
          if ((int) cur == (int) num5)
            return this.Backward(out backRes, cur);
          this.ReadMatchDistances(out lenRes2, out numDistancePairs);
          if (lenRes2 >= this._numFastBytes)
          {
            this._numDistancePairs = numDistancePairs;
            this._longestMatchLength = lenRes2;
            this._longestMatchWasFound = true;
            return this.Backward(out backRes, cur);
          }
          ++position;
          uint index4 = this._optimum[(IntPtr) cur].PosPrev;
          if (this._optimum[(IntPtr) cur].Prev1IsChar)
          {
            --index4;
            if (this._optimum[(IntPtr) cur].Prev2)
            {
              state1 = this._optimum[(IntPtr) this._optimum[(IntPtr) cur].PosPrev2].State;
              if (this._optimum[(IntPtr) cur].BackPrev2 < 4U)
                state1.UpdateRep();
              else
                state1.UpdateMatch();
            }
            else
              state1 = this._optimum[(IntPtr) index4].State;
            state1.UpdateChar();
          }
          else
            state1 = this._optimum[(IntPtr) index4].State;
          if ((int) index4 == (int) cur - 1)
          {
            if (this._optimum[(IntPtr) cur].IsShortRep())
              state1.UpdateShortRep();
            else
              state1.UpdateChar();
          }
          else
          {
            uint num16;
            if (this._optimum[(IntPtr) cur].Prev1IsChar && this._optimum[(IntPtr) cur].Prev2)
            {
              index4 = this._optimum[(IntPtr) cur].PosPrev2;
              num16 = this._optimum[(IntPtr) cur].BackPrev2;
              state1.UpdateRep();
            }
            else
            {
              num16 = this._optimum[(IntPtr) cur].BackPrev;
              if (num16 < 4U)
                state1.UpdateRep();
              else
                state1.UpdateMatch();
            }
            Encoder.Optimal optimal4 = this._optimum[(IntPtr) index4];
            if (num16 < 4U)
            {
              switch (num16)
              {
                case 0:
                  this.reps[0] = optimal4.Backs0;
                  this.reps[1] = optimal4.Backs1;
                  this.reps[2] = optimal4.Backs2;
                  this.reps[3] = optimal4.Backs3;
                  break;
                case 1:
                  this.reps[0] = optimal4.Backs1;
                  this.reps[1] = optimal4.Backs0;
                  this.reps[2] = optimal4.Backs2;
                  this.reps[3] = optimal4.Backs3;
                  break;
                case 2:
                  this.reps[0] = optimal4.Backs2;
                  this.reps[1] = optimal4.Backs0;
                  this.reps[2] = optimal4.Backs1;
                  this.reps[3] = optimal4.Backs3;
                  break;
                default:
                  this.reps[0] = optimal4.Backs3;
                  this.reps[1] = optimal4.Backs0;
                  this.reps[2] = optimal4.Backs1;
                  this.reps[3] = optimal4.Backs2;
                  break;
              }
            }
            else
            {
              this.reps[0] = num16 - 4U;
              this.reps[1] = optimal4.Backs0;
              this.reps[2] = optimal4.Backs1;
              this.reps[3] = optimal4.Backs2;
            }
          }
          this._optimum[(IntPtr) cur].State = state1;
          this._optimum[(IntPtr) cur].Backs0 = this.reps[0];
          this._optimum[(IntPtr) cur].Backs1 = this.reps[1];
          this._optimum[(IntPtr) cur].Backs2 = this.reps[2];
          this._optimum[(IntPtr) cur].Backs3 = this.reps[3];
          uint price2 = this._optimum[(IntPtr) cur].Price;
          indexByte3 = this._matchFinder.GetIndexByte(-1);
          indexByte4 = this._matchFinder.GetIndexByte(-(int) this.reps[0] - 1 - 1);
          posState2 = position & this._posStateMask;
          int num17 = (int) price2 + (int) this._isMatch[(IntPtr) ((state1.Index << 4) + posState2)].GetPrice0();
          subCoder = this._literalEncoder.GetSubCoder(position, this._matchFinder.GetIndexByte(-2));
          int price3 = (int) subCoder.GetPrice(!state1.IsCharState(), indexByte4, indexByte3);
          num14 = (uint) (num17 + price3);
          Encoder.Optimal optimal5 = this._optimum[(IntPtr) (cur + 1U)];
          flag = false;
          if (num14 < optimal5.Price)
          {
            optimal5.Price = num14;
            optimal5.PosPrev = cur;
            optimal5.MakeAsChar();
            flag = true;
          }
          num11 = price2 + this._isMatch[(IntPtr) ((state1.Index << 4) + posState2)].GetPrice1();
          num15 = num11 + this._isRep[(IntPtr) state1.Index].GetPrice1();
          if ((int) indexByte4 == (int) indexByte3 && (optimal5.PosPrev >= cur || optimal5.BackPrev != 0U))
          {
            uint num18 = num15 + this.GetRepLen1Price(state1, posState2);
            if (num18 <= optimal5.Price)
            {
              optimal5.Price = num18;
              optimal5.PosPrev = cur;
              optimal5.MakeAsShortRep();
              flag = true;
            }
          }
          uint val2 = this._matchFinder.GetNumAvailableBytes() + 1U;
          num12 = Math.Min(4095U - cur, val2);
          limit1 = num12;
        }
        while (limit1 < 2U);
        if (limit1 > this._numFastBytes)
          limit1 = this._numFastBytes;
        if (!flag && (int) indexByte4 != (int) indexByte3)
        {
          uint matchLen = this._matchFinder.GetMatchLen(0, this.reps[0], Math.Min(num12 - 1U, this._numFastBytes));
          if (matchLen >= 2U)
          {
            Base.State state2 = state1;
            state2.UpdateChar();
            uint posState3 = position + 1U & this._posStateMask;
            uint num19 = num14 + this._isMatch[(IntPtr) ((state2.Index << 4) + posState3)].GetPrice1() + this._isRep[(IntPtr) state2.Index].GetPrice1();
            uint index5 = cur + 1U + matchLen;
            while (num5 < index5)
              this._optimum[(IntPtr) ++num5].Price = 268435455U;
            uint num20 = num19 + this.GetRepPrice(0U, matchLen, state2, posState3);
            Encoder.Optimal optimal6 = this._optimum[(IntPtr) index5];
            if (num20 < optimal6.Price)
            {
              optimal6.Price = num20;
              optimal6.PosPrev = cur + 1U;
              optimal6.BackPrev = 0U;
              optimal6.Prev1IsChar = true;
              optimal6.Prev2 = false;
            }
          }
        }
        num13 = 2U;
        for (uint repIndex = 0; repIndex < 4U; ++repIndex)
        {
          uint matchLen1 = this._matchFinder.GetMatchLen(-1, this.reps[(IntPtr) repIndex], limit1);
          if (matchLen1 >= 2U)
          {
            uint num21 = matchLen1;
            do
            {
              while (num5 < cur + matchLen1)
                this._optimum[(IntPtr) ++num5].Price = 268435455U;
              uint num22 = num15 + this.GetRepPrice(repIndex, matchLen1, state1, posState2);
              Encoder.Optimal optimal7 = this._optimum[(IntPtr) (cur + matchLen1)];
              if (num22 < optimal7.Price)
              {
                optimal7.Price = num22;
                optimal7.PosPrev = cur;
                optimal7.BackPrev = repIndex;
                optimal7.Prev1IsChar = false;
              }
            }
            while (--matchLen1 >= 2U);
            uint num23 = num21;
            if (repIndex == 0U)
              num13 = num23 + 1U;
            if (num23 < num12)
            {
              uint limit2 = Math.Min(num12 - 1U - num23, this._numFastBytes);
              uint matchLen2 = this._matchFinder.GetMatchLen((int) num23, this.reps[(IntPtr) repIndex], limit2);
              if (matchLen2 >= 2U)
              {
                Base.State state3 = state1;
                state3.UpdateRep();
                uint num24 = position + num23 & this._posStateMask;
                int num25 = (int) num15 + (int) this.GetRepPrice(repIndex, num23, state1, posState2) + (int) this._isMatch[(IntPtr) ((state3.Index << 4) + num24)].GetPrice0();
                subCoder = this._literalEncoder.GetSubCoder(position + num23, this._matchFinder.GetIndexByte((int) num23 - 1 - 1));
                int price4 = (int) subCoder.GetPrice(true, this._matchFinder.GetIndexByte((int) num23 - 1 - ((int) this.reps[(IntPtr) repIndex] + 1)), this._matchFinder.GetIndexByte((int) num23 - 1));
                uint num26 = (uint) (num25 + price4);
                state3.UpdateChar();
                uint posState4 = (uint) ((int) position + (int) num23 + 1) & this._posStateMask;
                uint num27 = num26 + this._isMatch[(IntPtr) ((state3.Index << 4) + posState4)].GetPrice1() + this._isRep[(IntPtr) state3.Index].GetPrice1();
                uint num28 = num23 + 1U + matchLen2;
                while (num5 < cur + num28)
                  this._optimum[(IntPtr) ++num5].Price = 268435455U;
                uint num29 = num27 + this.GetRepPrice(0U, matchLen2, state3, posState4);
                Encoder.Optimal optimal8 = this._optimum[(IntPtr) (cur + num28)];
                if (num29 < optimal8.Price)
                {
                  optimal8.Price = num29;
                  optimal8.PosPrev = (uint) ((int) cur + (int) num23 + 1);
                  optimal8.BackPrev = 0U;
                  optimal8.Prev1IsChar = true;
                  optimal8.Prev2 = true;
                  optimal8.PosPrev2 = cur;
                  optimal8.BackPrev2 = repIndex;
                }
              }
            }
          }
        }
        if (lenRes2 > limit1)
        {
          lenRes2 = limit1;
          uint index6 = 0;
          while (lenRes2 > this._matchDistances[(IntPtr) index6])
            index6 += 2U;
          this._matchDistances[(IntPtr) index6] = lenRes2;
          numDistancePairs = index6 + 2U;
        }
      }
      while (lenRes2 < num13);
      uint num30 = num11 + this._isRep[(IntPtr) state1.Index].GetPrice0();
      while (num5 < cur + lenRes2)
        this._optimum[(IntPtr) ++num5].Price = 268435455U;
      uint index7 = 0;
      while (num13 > this._matchDistances[(IntPtr) index7])
        index7 += 2U;
      uint num31 = num13;
      while (true)
      {
        uint matchDistance = this._matchDistances[(IntPtr) (index7 + 1U)];
        uint num32 = num30 + this.GetPosLenPrice(matchDistance, num31, posState2);
        Encoder.Optimal optimal9 = this._optimum[(IntPtr) (cur + num31)];
        if (num32 < optimal9.Price)
        {
          optimal9.Price = num32;
          optimal9.PosPrev = cur;
          optimal9.BackPrev = matchDistance + 4U;
          optimal9.Prev1IsChar = false;
        }
        if ((int) num31 == (int) this._matchDistances[(IntPtr) index7])
        {
          if (num31 < num12)
          {
            uint limit = Math.Min(num12 - 1U - num31, this._numFastBytes);
            uint matchLen = this._matchFinder.GetMatchLen((int) num31, matchDistance, limit);
            if (matchLen >= 2U)
            {
              Base.State state4 = state1;
              state4.UpdateMatch();
              uint num33 = position + num31 & this._posStateMask;
              int num34 = (int) num32 + (int) this._isMatch[(IntPtr) ((state4.Index << 4) + num33)].GetPrice0();
              subCoder = this._literalEncoder.GetSubCoder(position + num31, this._matchFinder.GetIndexByte((int) num31 - 1 - 1));
              int price5 = (int) subCoder.GetPrice(true, this._matchFinder.GetIndexByte((int) num31 - ((int) matchDistance + 1) - 1), this._matchFinder.GetIndexByte((int) num31 - 1));
              uint num35 = (uint) (num34 + price5);
              state4.UpdateChar();
              uint posState5 = (uint) ((int) position + (int) num31 + 1) & this._posStateMask;
              uint num36 = num35 + this._isMatch[(IntPtr) ((state4.Index << 4) + posState5)].GetPrice1() + this._isRep[(IntPtr) state4.Index].GetPrice1();
              uint num37 = num31 + 1U + matchLen;
              while (num5 < cur + num37)
                this._optimum[(IntPtr) ++num5].Price = 268435455U;
              uint num38 = num36 + this.GetRepPrice(0U, matchLen, state4, posState5);
              Encoder.Optimal optimal10 = this._optimum[(IntPtr) (cur + num37)];
              if (num38 < optimal10.Price)
              {
                optimal10.Price = num38;
                optimal10.PosPrev = (uint) ((int) cur + (int) num31 + 1);
                optimal10.BackPrev = 0U;
                optimal10.Prev1IsChar = true;
                optimal10.Prev2 = true;
                optimal10.PosPrev2 = cur;
                optimal10.BackPrev2 = matchDistance + 4U;
              }
            }
          }
          index7 += 2U;
          if ((int) index7 == (int) numDistancePairs)
            goto label_45;
        }
        ++num31;
      }
    }

    private bool ChangePair(uint smallDist, uint bigDist) => smallDist < 33554432U && bigDist >= smallDist << 7;

    private void WriteEndMarker(uint posState)
    {
      if (!this._writeEndMark)
        return;
      this._isMatch[(IntPtr) ((this._state.Index << 4) + posState)].Encode(this._rangeEncoder, 1U);
      this._isRep[(IntPtr) this._state.Index].Encode(this._rangeEncoder, 0U);
      this._state.UpdateMatch();
      uint len = 2;
      this._lenEncoder.Encode(this._rangeEncoder, len - 2U, posState);
      uint symbol = 63;
      this._posSlotEncoder[(IntPtr) Base.GetLenToPosState(len)].Encode(this._rangeEncoder, symbol);
      int num1 = 30;
      uint num2 = (uint) ((1 << num1) - 1);
      this._rangeEncoder.EncodeDirectBits(num2 >> 4, num1 - 4);
      this._posAlignEncoder.ReverseEncode(this._rangeEncoder, num2 & 15U);
    }

    private void Flush(uint nowPos)
    {
      this.ReleaseMFStream();
      this.WriteEndMarker(nowPos & this._posStateMask);
      this._rangeEncoder.FlushData();
      this._rangeEncoder.FlushStream();
    }

    public void CodeOneBlock(out long inSize, out long outSize, out bool finished)
    {
      inSize = 0L;
      outSize = 0L;
      finished = true;
      if (this._inStream != null)
      {
        this._matchFinder.SetStream(this._inStream);
        this._matchFinder.Init();
        this._needReleaseMFStream = true;
        this._inStream = (Stream) null;
        if (this._trainSize > 0U)
          this._matchFinder.Skip(this._trainSize);
      }
      if (this._finished)
        return;
      this._finished = true;
      long nowPos64 = this.nowPos64;
      if (this.nowPos64 == 0L)
      {
        if (this._matchFinder.GetNumAvailableBytes() == 0U)
        {
          this.Flush((uint) this.nowPos64);
          return;
        }
        this.ReadMatchDistances(out uint _, out uint _);
        this._isMatch[(IntPtr) ((this._state.Index << 4) + ((uint) this.nowPos64 & this._posStateMask))].Encode(this._rangeEncoder, 0U);
        this._state.UpdateChar();
        byte indexByte = this._matchFinder.GetIndexByte(-(int) this._additionalOffset);
        this._literalEncoder.GetSubCoder((uint) this.nowPos64, this._previousByte).Encode(this._rangeEncoder, indexByte);
        this._previousByte = indexByte;
        --this._additionalOffset;
        ++this.nowPos64;
      }
      if (this._matchFinder.GetNumAvailableBytes() == 0U)
      {
        this.Flush((uint) this.nowPos64);
      }
      else
      {
        do
        {
          do
          {
            uint backRes;
            uint optimum = this.GetOptimum((uint) this.nowPos64, out backRes);
            uint posState = (uint) this.nowPos64 & this._posStateMask;
            uint index1 = (this._state.Index << 4) + posState;
            if (optimum == 1U && backRes == uint.MaxValue)
            {
              this._isMatch[(IntPtr) index1].Encode(this._rangeEncoder, 0U);
              byte indexByte1 = this._matchFinder.GetIndexByte(-(int) this._additionalOffset);
              Encoder.LiteralEncoder.Encoder2 subCoder = this._literalEncoder.GetSubCoder((uint) this.nowPos64, this._previousByte);
              if (!this._state.IsCharState())
              {
                byte indexByte2 = this._matchFinder.GetIndexByte(-(int) this._repDistances[0] - 1 - (int) this._additionalOffset);
                subCoder.EncodeMatched(this._rangeEncoder, indexByte2, indexByte1);
              }
              else
                subCoder.Encode(this._rangeEncoder, indexByte1);
              this._previousByte = indexByte1;
              this._state.UpdateChar();
            }
            else
            {
              this._isMatch[(IntPtr) index1].Encode(this._rangeEncoder, 1U);
              if (backRes < 4U)
              {
                this._isRep[(IntPtr) this._state.Index].Encode(this._rangeEncoder, 1U);
                if (backRes == 0U)
                {
                  this._isRepG0[(IntPtr) this._state.Index].Encode(this._rangeEncoder, 0U);
                  if (optimum == 1U)
                    this._isRep0Long[(IntPtr) index1].Encode(this._rangeEncoder, 0U);
                  else
                    this._isRep0Long[(IntPtr) index1].Encode(this._rangeEncoder, 1U);
                }
                else
                {
                  this._isRepG0[(IntPtr) this._state.Index].Encode(this._rangeEncoder, 1U);
                  if (backRes == 1U)
                  {
                    this._isRepG1[(IntPtr) this._state.Index].Encode(this._rangeEncoder, 0U);
                  }
                  else
                  {
                    this._isRepG1[(IntPtr) this._state.Index].Encode(this._rangeEncoder, 1U);
                    this._isRepG2[(IntPtr) this._state.Index].Encode(this._rangeEncoder, backRes - 2U);
                  }
                }
                if (optimum == 1U)
                {
                  this._state.UpdateShortRep();
                }
                else
                {
                  this._repMatchLenEncoder.Encode(this._rangeEncoder, optimum - 2U, posState);
                  this._state.UpdateRep();
                }
                uint repDistance = this._repDistances[(IntPtr) backRes];
                if (backRes != 0U)
                {
                  for (uint index2 = backRes; index2 >= 1U; --index2)
                    this._repDistances[(IntPtr) index2] = this._repDistances[(IntPtr) (index2 - 1U)];
                  this._repDistances[0] = repDistance;
                }
              }
              else
              {
                this._isRep[(IntPtr) this._state.Index].Encode(this._rangeEncoder, 0U);
                this._state.UpdateMatch();
                this._lenEncoder.Encode(this._rangeEncoder, optimum - 2U, posState);
                backRes -= 4U;
                uint posSlot = Encoder.GetPosSlot(backRes);
                this._posSlotEncoder[(IntPtr) Base.GetLenToPosState(optimum)].Encode(this._rangeEncoder, posSlot);
                if (posSlot >= 4U)
                {
                  int NumBitLevels = (int) (posSlot >> 1) - 1;
                  uint num = (uint) ((2 | (int) posSlot & 1) << NumBitLevels);
                  uint symbol = backRes - num;
                  if (posSlot < 14U)
                  {
                    BitTreeEncoder.ReverseEncode(this._posEncoders, (uint) ((int) num - (int) posSlot - 1), this._rangeEncoder, NumBitLevels, symbol);
                  }
                  else
                  {
                    this._rangeEncoder.EncodeDirectBits(symbol >> 4, NumBitLevels - 4);
                    this._posAlignEncoder.ReverseEncode(this._rangeEncoder, symbol & 15U);
                    ++this._alignPriceCount;
                  }
                }
                uint num1 = backRes;
                for (uint index3 = 3; index3 >= 1U; --index3)
                  this._repDistances[(IntPtr) index3] = this._repDistances[(IntPtr) (index3 - 1U)];
                this._repDistances[0] = num1;
                ++this._matchPriceCount;
              }
              this._previousByte = this._matchFinder.GetIndexByte((int) optimum - 1 - (int) this._additionalOffset);
            }
            this._additionalOffset -= optimum;
            this.nowPos64 += (long) optimum;
          }
          while (this._additionalOffset != 0U);
          if (this._matchPriceCount >= 128U)
            this.FillDistancesPrices();
          if (this._alignPriceCount >= 16U)
            this.FillAlignPrices();
          inSize = this.nowPos64;
          outSize = this._rangeEncoder.GetProcessedSizeAdd();
          if (this._matchFinder.GetNumAvailableBytes() == 0U)
          {
            this.Flush((uint) this.nowPos64);
            return;
          }
        }
        while (this.nowPos64 - nowPos64 < 4096L);
        this._finished = false;
        finished = false;
      }
    }

    private void ReleaseMFStream()
    {
      if (this._matchFinder == null || !this._needReleaseMFStream)
        return;
      this._matchFinder.ReleaseStream();
      this._needReleaseMFStream = false;
    }

    private void SetOutStream(Stream outStream) => this._rangeEncoder.SetStream(outStream);

    private void ReleaseOutStream() => this._rangeEncoder.ReleaseStream();

    private void ReleaseStreams()
    {
      this.ReleaseMFStream();
      this.ReleaseOutStream();
    }

    private void SetStreams(Stream inStream, Stream outStream, long inSize, long outSize)
    {
      this._inStream = inStream;
      this._finished = false;
      this.Create();
      this.SetOutStream(outStream);
      this.Init();
      this.FillDistancesPrices();
      this.FillAlignPrices();
      this._lenEncoder.SetTableSize((uint) ((int) this._numFastBytes + 1 - 2));
      this._lenEncoder.UpdateTables((uint) (1 << this._posStateBits));
      this._repMatchLenEncoder.SetTableSize((uint) ((int) this._numFastBytes + 1 - 2));
      this._repMatchLenEncoder.UpdateTables((uint) (1 << this._posStateBits));
      this.nowPos64 = 0L;
    }

    public void Code(
      Stream inStream,
      Stream outStream,
      long inSize,
      long outSize,
      ICodeProgress progress)
    {
      this._needReleaseMFStream = false;
      try
      {
        this.SetStreams(inStream, outStream, inSize, outSize);
        while (true)
        {
          long inSize1;
          long outSize1;
          do
          {
            bool finished;
            this.CodeOneBlock(out inSize1, out outSize1, out finished);
            if (finished)
              goto label_3;
          }
          while (progress == null);
          progress.SetProgress(inSize1, outSize1);
        }
label_3:;
      }
      finally
      {
        this.ReleaseStreams();
      }
    }

    public void WriteCoderProperties(Stream outStream)
    {
      this.properties[0] = (byte) ((this._posStateBits * 5 + this._numLiteralPosStateBits) * 9 + this._numLiteralContextBits);
      for (int index = 0; index < 4; ++index)
        this.properties[1 + index] = (byte) (this._dictionarySize >> 8 * index & (uint) byte.MaxValue);
      outStream.Write(this.properties, 0, 5);
    }

    private void FillDistancesPrices()
    {
      for (uint pos = 4; pos < 128U; ++pos)
      {
        uint posSlot = Encoder.GetPosSlot(pos);
        int NumBitLevels = (int) (posSlot >> 1) - 1;
        uint num = (uint) ((2 | (int) posSlot & 1) << NumBitLevels);
        this.tempPrices[(IntPtr) pos] = BitTreeEncoder.ReverseGetPrice(this._posEncoders, (uint) ((int) num - (int) posSlot - 1), NumBitLevels, pos - num);
      }
      for (uint index1 = 0; index1 < 4U; ++index1)
      {
        BitTreeEncoder bitTreeEncoder = this._posSlotEncoder[(IntPtr) index1];
        uint num1 = index1 << 6;
        for (uint symbol = 0; symbol < this._distTableSize; ++symbol)
          this._posSlotPrices[(IntPtr) (num1 + symbol)] = bitTreeEncoder.GetPrice(symbol);
        for (uint index2 = 14; index2 < this._distTableSize; ++index2)
          this._posSlotPrices[(IntPtr) (num1 + index2)] += (uint) ((int) (index2 >> 1) - 1 - 4 << 6);
        uint num2 = index1 * 128U;
        uint pos;
        for (pos = 0U; pos < 4U; ++pos)
          this._distancesPrices[(IntPtr) (num2 + pos)] = this._posSlotPrices[(IntPtr) (num1 + pos)];
        for (; pos < 128U; ++pos)
          this._distancesPrices[(IntPtr) (num2 + pos)] = this._posSlotPrices[(IntPtr) (num1 + Encoder.GetPosSlot(pos))] + this.tempPrices[(IntPtr) pos];
      }
      this._matchPriceCount = 0U;
    }

    private void FillAlignPrices()
    {
      for (uint symbol = 0; symbol < 16U; ++symbol)
        this._alignPrices[(IntPtr) symbol] = this._posAlignEncoder.ReverseGetPrice(symbol);
      this._alignPriceCount = 0U;
    }

    private static int FindMatchFinder(string s)
    {
      for (int matchFinder = 0; matchFinder < Encoder.kMatchFinderIDs.Length; ++matchFinder)
      {
        if (s == Encoder.kMatchFinderIDs[matchFinder])
          return matchFinder;
      }
      return -1;
    }

    public void SetCoderProperties(CoderPropID[] propIDs, object[] properties)
    {
      for (uint index = 0; (long) index < (long) properties.Length; ++index)
      {
        object property = properties[(IntPtr) index];
        switch (propIDs[(IntPtr) index])
        {
          case CoderPropID.DictionarySize:
            if (!(property is int num1))
              throw new InvalidParamException();
            this._dictionarySize = num1 >= 1 && num1 <= 1073741824 ? (uint) num1 : throw new InvalidParamException();
            int num2 = 0;
            while (num2 < 30 && (long) num1 > (long) (uint) (1 << num2))
              ++num2;
            this._distTableSize = (uint) (num2 * 2);
            continue;
          case CoderPropID.PosStateBits:
            if (!(property is int num3))
              throw new InvalidParamException();
            this._posStateBits = num3 >= 0 && num3 <= 4 ? num3 : throw new InvalidParamException();
            this._posStateMask = (uint) ((1 << this._posStateBits) - 1);
            continue;
          case CoderPropID.LitContextBits:
            if (!(property is int num4))
              throw new InvalidParamException();
            this._numLiteralContextBits = num4 >= 0 && num4 <= 8 ? num4 : throw new InvalidParamException();
            continue;
          case CoderPropID.LitPosBits:
            if (!(property is int num5))
              throw new InvalidParamException();
            this._numLiteralPosStateBits = num5 >= 0 && num5 <= 4 ? num5 : throw new InvalidParamException();
            continue;
          case CoderPropID.NumFastBytes:
            if (!(property is int num6))
              throw new InvalidParamException();
            this._numFastBytes = num6 >= 5 && num6 <= 273 ? (uint) num6 : throw new InvalidParamException();
            continue;
          case CoderPropID.MatchFinder:
            if (!(property is string))
              throw new InvalidParamException();
            Encoder.EMatchFinderType matchFinderType = this._matchFinderType;
            int matchFinder = Encoder.FindMatchFinder(((string) property).ToUpper());
            this._matchFinderType = matchFinder >= 0 ? (Encoder.EMatchFinderType) matchFinder : throw new InvalidParamException();
            if (this._matchFinder != null && matchFinderType != this._matchFinderType)
            {
              this._dictionarySizePrev = uint.MaxValue;
              this._matchFinder = (IMatchFinder) null;
              continue;
            }
            continue;
          case CoderPropID.Algorithm:
            continue;
          case CoderPropID.EndMarker:
            if (!(property is bool writeEndMarker))
              throw new InvalidParamException();
            this.SetWriteEndMarkerMode(writeEndMarker);
            continue;
          default:
            throw new InvalidParamException();
        }
      }
    }

    public void SetTrainSize(uint trainSize) => this._trainSize = trainSize;

    private enum EMatchFinderType
    {
      BT2,
      BT4,
    }

    private class LiteralEncoder
    {
      private Encoder.LiteralEncoder.Encoder2[] m_Coders;
      private int m_NumPrevBits;
      private int m_NumPosBits;
      private uint m_PosMask;

      public void Create(int numPosBits, int numPrevBits)
      {
        if (this.m_Coders != null && this.m_NumPrevBits == numPrevBits && this.m_NumPosBits == numPosBits)
          return;
        this.m_NumPosBits = numPosBits;
        this.m_PosMask = (uint) ((1 << numPosBits) - 1);
        this.m_NumPrevBits = numPrevBits;
        uint length = (uint) (1 << this.m_NumPrevBits + this.m_NumPosBits);
        this.m_Coders = new Encoder.LiteralEncoder.Encoder2[(IntPtr) length];
        for (uint index = 0; index < length; ++index)
          this.m_Coders[(IntPtr) index].Create();
      }

      public void Init()
      {
        uint num = (uint) (1 << this.m_NumPrevBits + this.m_NumPosBits);
        for (uint index = 0; index < num; ++index)
          this.m_Coders[(IntPtr) index].Init();
      }

      public Encoder.LiteralEncoder.Encoder2 GetSubCoder(uint pos, byte prevByte) => this.m_Coders[(IntPtr) (uint) ((((int) pos & (int) this.m_PosMask) << this.m_NumPrevBits) + ((int) prevByte >> 8 - this.m_NumPrevBits))];

      public struct Encoder2
      {
        private BitEncoder[] m_Encoders;

        public void Create() => this.m_Encoders = new BitEncoder[768];

        public void Init()
        {
          for (int index = 0; index < 768; ++index)
            this.m_Encoders[index].Init();
        }

        public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte symbol)
        {
          uint index1 = 1;
          for (int index2 = 7; index2 >= 0; --index2)
          {
            uint symbol1 = (uint) ((int) symbol >> index2 & 1);
            this.m_Encoders[(IntPtr) index1].Encode(rangeEncoder, symbol1);
            index1 = index1 << 1 | symbol1;
          }
        }

        public void EncodeMatched(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
        {
          uint num1 = 1;
          bool flag = true;
          for (int index1 = 7; index1 >= 0; --index1)
          {
            uint symbol1 = (uint) ((int) symbol >> index1 & 1);
            uint index2 = num1;
            if (flag)
            {
              uint num2 = (uint) ((int) matchByte >> index1 & 1);
              index2 += (uint) (1 + (int) num2 << 8);
              flag = (int) num2 == (int) symbol1;
            }
            this.m_Encoders[(IntPtr) index2].Encode(rangeEncoder, symbol1);
            num1 = num1 << 1 | symbol1;
          }
        }

        public uint GetPrice(bool matchMode, byte matchByte, byte symbol)
        {
          uint price = 0;
          uint index = 1;
          int num1 = 7;
          if (matchMode)
          {
            for (; num1 >= 0; --num1)
            {
              uint num2 = (uint) ((int) matchByte >> num1 & 1);
              uint symbol1 = (uint) ((int) symbol >> num1 & 1);
              price += this.m_Encoders[(IntPtr) ((uint) (1 + (int) num2 << 8) + index)].GetPrice(symbol1);
              index = index << 1 | symbol1;
              if ((int) num2 != (int) symbol1)
              {
                --num1;
                break;
              }
            }
          }
          for (; num1 >= 0; --num1)
          {
            uint symbol2 = (uint) ((int) symbol >> num1 & 1);
            price += this.m_Encoders[(IntPtr) index].GetPrice(symbol2);
            index = index << 1 | symbol2;
          }
          return price;
        }
      }
    }

    private class LenEncoder
    {
      private BitEncoder _choice = new BitEncoder();
      private BitEncoder _choice2 = new BitEncoder();
      private BitTreeEncoder[] _lowCoder = new BitTreeEncoder[new IntPtr(16)];
      private BitTreeEncoder[] _midCoder = new BitTreeEncoder[new IntPtr(16)];
      private BitTreeEncoder _highCoder = new BitTreeEncoder(8);

      public LenEncoder()
      {
        for (uint index = 0; index < 16U; ++index)
        {
          this._lowCoder[(IntPtr) index] = new BitTreeEncoder(3);
          this._midCoder[(IntPtr) index] = new BitTreeEncoder(3);
        }
      }

      public void Init(uint numPosStates)
      {
        this._choice.Init();
        this._choice2.Init();
        for (uint index = 0; index < numPosStates; ++index)
        {
          this._lowCoder[(IntPtr) index].Init();
          this._midCoder[(IntPtr) index].Init();
        }
        this._highCoder.Init();
      }

      public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, uint symbol, uint posState)
      {
        if (symbol < 8U)
        {
          this._choice.Encode(rangeEncoder, 0U);
          this._lowCoder[(IntPtr) posState].Encode(rangeEncoder, symbol);
        }
        else
        {
          symbol -= 8U;
          this._choice.Encode(rangeEncoder, 1U);
          if (symbol < 8U)
          {
            this._choice2.Encode(rangeEncoder, 0U);
            this._midCoder[(IntPtr) posState].Encode(rangeEncoder, symbol);
          }
          else
          {
            this._choice2.Encode(rangeEncoder, 1U);
            this._highCoder.Encode(rangeEncoder, symbol - 8U);
          }
        }
      }

      public void SetPrices(uint posState, uint numSymbols, uint[] prices, uint st)
      {
        uint price0 = this._choice.GetPrice0();
        uint price1 = this._choice.GetPrice1();
        uint num1 = price1 + this._choice2.GetPrice0();
        uint num2 = price1 + this._choice2.GetPrice1();
        uint symbol;
        for (symbol = 0U; symbol < 8U; ++symbol)
        {
          if (symbol >= numSymbols)
            return;
          prices[(IntPtr) (st + symbol)] = price0 + this._lowCoder[(IntPtr) posState].GetPrice(symbol);
        }
        for (; symbol < 16U; ++symbol)
        {
          if (symbol >= numSymbols)
            return;
          prices[(IntPtr) (st + symbol)] = num1 + this._midCoder[(IntPtr) posState].GetPrice(symbol - 8U);
        }
        for (; symbol < numSymbols; ++symbol)
          prices[(IntPtr) (st + symbol)] = num2 + this._highCoder.GetPrice((uint) ((int) symbol - 8 - 8));
      }
    }

    private class LenPriceTableEncoder : Encoder.LenEncoder
    {
      private uint[] _prices = new uint[new IntPtr(4352)];
      private uint _tableSize;
      private uint[] _counters = new uint[new IntPtr(16)];

      public void SetTableSize(uint tableSize) => this._tableSize = tableSize;

      public uint GetPrice(uint symbol, uint posState) => this._prices[(IntPtr) (posState * 272U + symbol)];

      private void UpdateTable(uint posState)
      {
        this.SetPrices(posState, this._tableSize, this._prices, posState * 272U);
        this._counters[(IntPtr) posState] = this._tableSize;
      }

      public void UpdateTables(uint numPosStates)
      {
        for (uint posState = 0; posState < numPosStates; ++posState)
          this.UpdateTable(posState);
      }

      public new void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, uint symbol, uint posState)
      {
        base.Encode(rangeEncoder, symbol, posState);
        if (--this._counters[(IntPtr) posState] != 0U)
          return;
        this.UpdateTable(posState);
      }
    }

    private class Optimal
    {
      public Base.State State;
      public bool Prev1IsChar;
      public bool Prev2;
      public uint PosPrev2;
      public uint BackPrev2;
      public uint Price;
      public uint PosPrev;
      public uint BackPrev;
      public uint Backs0;
      public uint Backs1;
      public uint Backs2;
      public uint Backs3;

      public void MakeAsChar()
      {
        this.BackPrev = uint.MaxValue;
        this.Prev1IsChar = false;
      }

      public void MakeAsShortRep()
      {
        this.BackPrev = 0U;
        this.Prev1IsChar = false;
      }

      public bool IsShortRep() => this.BackPrev == 0U;
    }
  }
}
