7zip使用的压缩算法,C#实现的LZMA压缩算法单文件版

http://www.oschina.net/code/snippet_222150_7935

 

 http://115.com/file/dpxi8m6v#  代码下载

using System;
using System.IO;
using System.Collections;

namespace SevenZip.CommandLineParser
{
 public enum SwitchType
 {
  Simple,
  PostMinus,
  LimitedPostString,
  UnLimitedPostString,
  PostChar
 }

 public class SwitchForm
 {
  public string IDString;
  public SwitchType Type;
  public bool Multi;
  public int MinLen;
  public int MaxLen;
  public string PostCharSet;

  public SwitchForm(string idString, SwitchType type, bool multi,
   int minLen, int maxLen, string postCharSet)
  {
   IDString = idString;
   Type = type;
   Multi = multi;
   MinLen = minLen;
   MaxLen = maxLen;
   PostCharSet = postCharSet;
  }
  public SwitchForm(string idString, SwitchType type, bool multi, int minLen):
   this(idString, type, multi, minLen, 0, "")
  {
  }
  public SwitchForm(string idString, SwitchType type, bool multi):
   this(idString, type, multi, 0)
  {
  }
 }

 public class SwitchResult
 {
  public bool ThereIs;
  public bool WithMinus;
  public ArrayList PostStrings = new ArrayList();
  public int PostCharIndex;
  public SwitchResult()
  {
   ThereIs = false;
  }
 }

 public class Parser
 {
  public ArrayList NonSwitchStrings = new ArrayList();
  SwitchResult[] _switches;

  public Parser(int numSwitches)
  {
   _switches = new SwitchResult[numSwitches];
   for (int i = 0; i < numSwitches; i++)
    _switches[i] = new SwitchResult();
  }

  bool ParseString(string srcString, SwitchForm[] switchForms)
  {
   int len = srcString.Length;
   if (len == 0)
    return false;
   int pos = 0;
   if (!IsItSwitchChar(srcString[pos]))
    return false;
   while (pos < len)
   {
    if (IsItSwitchChar(srcString[pos]))
     pos++;
    const int kNoLen = -1;
    int matchedSwitchIndex = 0;
    int maxLen = kNoLen;
    for (int switchIndex = 0; switchIndex < _switches.Length; switchIndex++)
    {
     int switchLen = switchForms[switchIndex].IDString.Length;
     if (switchLen <= maxLen || pos + switchLen > len)
      continue;
     if (String.Compare(switchForms[switchIndex].IDString, 0,
       srcString, pos, switchLen, true) == 0)
     {
      matchedSwitchIndex = switchIndex;
      maxLen = switchLen;
     }
    }
    if (maxLen == kNoLen)
     throw new Exception("maxLen == kNoLen");
    SwitchResult matchedSwitch = _switches[matchedSwitchIndex];
    SwitchForm switchForm = switchForms[matchedSwitchIndex];
    if ((!switchForm.Multi) && matchedSwitch.ThereIs)
     throw new Exception("switch must be single");
    matchedSwitch.ThereIs = true;
    pos += maxLen;
    int tailSize = len - pos;
    SwitchType type = switchForm.Type;
    switch (type)
    {
     case SwitchType.PostMinus:
      {
       if (tailSize == 0)
        matchedSwitch.WithMinus = false;
       else
       {
        matchedSwitch.WithMinus = (srcString[pos] == kSwitchMinus);
        if (matchedSwitch.WithMinus)
         pos++;
       }
       break;
      }
     case SwitchType.PostChar:
      {
       if (tailSize < switchForm.MinLen)
        throw new Exception("switch is not full");
       string charSet = switchForm.PostCharSet;
       const int kEmptyCharValue = -1;
       if (tailSize == 0)
        matchedSwitch.PostCharIndex = kEmptyCharValue;
       else
       {
        int index = charSet.IndexOf(srcString[pos]);
        if (index < 0)
         matchedSwitch.PostCharIndex = kEmptyCharValue;
        else
        {
         matchedSwitch.PostCharIndex = index;
         pos++;
        }
       }
       break;
      }
     case SwitchType.LimitedPostString:
     case SwitchType.UnLimitedPostString:
      {
       int minLen = switchForm.MinLen;
       if (tailSize < minLen)
        throw new Exception("switch is not full");
       if (type == SwitchType.UnLimitedPostString)
       {
        matchedSwitch.PostStrings.Add(srcString.Substring(pos));
        return true;
       }
       String stringSwitch = srcString.Substring(pos, minLen);
       pos += minLen;
       for (int i = minLen; i < switchForm.MaxLen && pos < len; i++, pos++)
       {
        char c = srcString[pos];
        if (IsItSwitchChar(c))
         break;
        stringSwitch += c;
       }
       matchedSwitch.PostStrings.Add(stringSwitch);
       break;
      }
    }
   }
   return true;

  }

  public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings)
  {
   int numCommandStrings = commandStrings.Length;
   bool stopSwitch = false;
   for (int i = 0; i < numCommandStrings; i++)
   {
    string s = commandStrings[i];
    if (stopSwitch)
     NonSwitchStrings.Add(s);
    else
     if (s == kStopSwitchParsing)
     stopSwitch = true;
    else
     if (!ParseString(s, switchForms))
     NonSwitchStrings.Add(s);
   }
  }

  public SwitchResult this[int index] { get { return _switches[index]; } }

  public static int ParseCommand(CommandForm[] commandForms, string commandString,
   out string postString)
  {
   for (int i = 0; i < commandForms.Length; i++)
   {
    string id = commandForms[i].IDString;
    if (commandForms[i].PostStringMode)
    {
     if (commandString.IndexOf(id) == 0)
     {
      postString = commandString.Substring(id.Length);
      return i;
     }
    }
    else
     if (commandString == id)
    {
     postString = "";
     return i;
    }
   }
   postString = "";
   return -1;
  }

  static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms,
   string commandString, ArrayList indices)
  {
   indices.Clear();
   int numUsedChars = 0;
   for (int i = 0; i < numForms; i++)
   {
    CommandSubCharsSet charsSet = forms[i];
    int currentIndex = -1;
    int len = charsSet.Chars.Length;
    for (int j = 0; j < len; j++)
    {
     char c = charsSet.Chars[j];
     int newIndex = commandString.IndexOf(c);
     if (newIndex >= 0)
     {
      if (currentIndex >= 0)
       return false;
      if (commandString.IndexOf(c, newIndex + 1) >= 0)
       return false;
      currentIndex = j;
      numUsedChars++;
     }
    }
    if (currentIndex == -1 && !charsSet.EmptyAllowed)
     return false;
    indices.Add(currentIndex);
   }
   return (numUsedChars == commandString.Length);
  }
  const char kSwitchID1 = '-';
  const char kSwitchID2 = '/';

  const char kSwitchMinus = '-';
  const string kStopSwitchParsing = "--";

  static bool IsItSwitchChar(char c)
  {
   return (c == kSwitchID1 || c == kSwitchID2);
  }
 }

 public class CommandForm
 {
  public string IDString = "";
  public bool PostStringMode = false;
  public CommandForm(string idString, bool postStringMode)
  {
   IDString = idString;
   PostStringMode = postStringMode;
  }
 }

 class CommandSubCharsSet
 {
  public string Chars = "";
  public bool EmptyAllowed = false;
 }
}

namespace SevenZip
{
 class CRC
 {
  public static readonly uint[] Table;

  static CRC()
  {
   Table = new uint[256];
   const uint kPoly = 0xEDB88320;
   for (uint i = 0; i < 256; i++)
   {
    uint r = i;
    for (int j = 0; j < 8; j++)
     if ((r & 1) != 0)
      r = (r >> 1) ^ kPoly;
     else
      r >>= 1;
    Table[i] = r;
   }
  }

  uint _value = 0xFFFFFFFF;

  public void Init() { _value = 0xFFFFFFFF; }

  public void UpdateByte(byte b)
  {
   _value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8);
  }

  public void Update(byte[] data, uint offset, uint size)
  {
   for (uint i = 0; i < size; i++)
    _value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8);
  }

  public uint GetDigest() { return _value ^ 0xFFFFFFFF; }

  static uint CalculateDigest(byte[] data, uint offset, uint size)
  {
   CRC crc = new CRC();
   // crc.Init();
   crc.Update(data, offset, size);
   return crc.GetDigest();
  }

  static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size)
  {
   return (CalculateDigest(data, offset, size) == digest);
  }
 }
}

namespace SevenZip.Compression.LZ
{
 interface IInWindowStream
 {
  void SetStream(System.IO.Stream inStream);
  void Init();
  void ReleaseStream();
  Byte GetIndexByte(Int32 index);
  UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit);
  UInt32 GetNumAvailableBytes();
 }

 interface IMatchFinder : IInWindowStream
 {
  void Create(UInt32 historySize, UInt32 keepAddBufferBefore,
    UInt32 matchMaxLen, UInt32 keepAddBufferAfter);
  UInt32 GetMatches(UInt32[] distances);
  void Skip(UInt32 num);
 }
}
namespace SevenZip.Compression.LZ
{
 public class InWindow
 {
  public Byte[] _bufferBase = null; // pointer to buffer with data
  System.IO.Stream _stream;
  UInt32 _posLimit; // offset (from _buffer) of first byte when new block reading must be done
  bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream

  UInt32 _pointerToLastSafePosition;

  public UInt32 _bufferOffset;

  public UInt32 _blockSize; // Size of Allocated memory block
  public UInt32 _pos; // offset (from _buffer) of curent byte
  UInt32 _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos
  UInt32 _keepSizeAfter; // how many BYTEs must be kept buffer after _pos
  public UInt32 _streamPos; // offset (from _buffer) of first not read byte from Stream

  public void MoveBlock()
  {
   UInt32 offset = (UInt32)(_bufferOffset) + _pos - _keepSizeBefore;
   // we need one additional byte, since MovePos moves on 1 byte.
   if (offset > 0)
    offset--;
   
   UInt32 numBytes = (UInt32)(_bufferOffset) + _streamPos - offset;

   // check negative offset ????
   for (UInt32 i = 0; i < numBytes; i++)
    _bufferBase[i] = _bufferBase[offset + i];
   _bufferOffset -= offset;
  }

  public virtual void ReadBlock()
  {
   if (_streamEndWasReached)
    return;
   while (true)
   {
    int size = (int)((0 - _bufferOffset) + _blockSize - _streamPos);
    if (size == 0)
     return;
    int numReadBytes = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size);
    if (numReadBytes == 0)
    {
     _posLimit = _streamPos;
     UInt32 pointerToPostion = _bufferOffset + _posLimit;
     if (pointerToPostion > _pointerToLastSafePosition)
      _posLimit = (UInt32)(_pointerToLastSafePosition - _bufferOffset);

     _streamEndWasReached = true;
     return;
    }
    _streamPos += (UInt32)numReadBytes;
    if (_streamPos >= _pos + _keepSizeAfter)
     _posLimit = _streamPos - _keepSizeAfter;
   }
  }

  void Free() { _bufferBase = null; }

  public void Create(UInt32 keepSizeBefore, UInt32 keepSizeAfter, UInt32 keepSizeReserv)
  {
   _keepSizeBefore = keepSizeBefore;
   _keepSizeAfter = keepSizeAfter;
   UInt32 blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv;
   if (_bufferBase == null || _blockSize != blockSize)
   {
    Free();
    _blockSize = blockSize;
    _bufferBase = new Byte[_blockSize];
   }
   _pointerToLastSafePosition = _blockSize - keepSizeAfter;
  }

  public void SetStream(System.IO.Stream stream) { _stream = stream; }
  public void ReleaseStream() { _stream = null; }

  public void Init()
  {
   _bufferOffset = 0;
   _pos = 0;
   _streamPos = 0;
   _streamEndWasReached = false;
   ReadBlock();
  }

  public void MovePos()
  {
   _pos++;
   if (_pos > _posLimit)
   {
    UInt32 pointerToPostion = _bufferOffset + _pos;
    if (pointerToPostion > _pointerToLastSafePosition)
     MoveBlock();
    ReadBlock();
   }
  }

  public Byte GetIndexByte(Int32 index) { return _bufferBase[_bufferOffset + _pos + index]; }

  // index + limit have not to exceed _keepSizeAfter;
  public UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit)
  {
   if (_streamEndWasReached)
    if ((_pos + index) + limit > _streamPos)
     limit = _streamPos - (UInt32)(_pos + index);
   distance++;
   // Byte *pby = _buffer + (size_t)_pos + index;
   UInt32 pby = _bufferOffset + _pos + (UInt32)index;

   UInt32 i;
   for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++);
   return i;
  }

  public UInt32 GetNumAvailableBytes() { return _streamPos - _pos; }

  public void ReduceOffsets(Int32 subValue)
  {
   _bufferOffset += (UInt32)subValue;
   _posLimit -= (UInt32)subValue;
   _pos -= (UInt32)subValue;
   _streamPos -= (UInt32)subValue;
  }
 }
}
// LzOutWindow.cs

namespace SevenZip.Compression.LZ
{
 public class OutWindow
 {
  byte[] _buffer = null;
  uint _pos;
  uint _windowSize = 0;
  uint _streamPos;
  System.IO.Stream _stream;

  public uint TrainSize = 0;

  public void Create(uint windowSize)
  {
   if (_windowSize != windowSize)
   {
    // System.GC.Collect();
    _buffer = new byte[windowSize];
   }
   _windowSize = windowSize;
   _pos = 0;
   _streamPos = 0;
  }

  public void Init(System.IO.Stream stream, bool solid)
  {
   ReleaseStream();
   _stream = stream;
   if (!solid)
   {
    _streamPos = 0;
    _pos = 0;
    TrainSize = 0;
   }
  }
 
  public bool Train(System.IO.Stream stream)
  {
   long len = stream.Length;
   uint size = (len < _windowSize) ? (uint)len : _windowSize;
   TrainSize = size;
   stream.Position = len - size;
   _streamPos = _pos = 0;
   while (size > 0)
   {
    uint curSize = _windowSize - _pos;
    if (size < curSize)
     curSize = size;
    int numReadBytes = stream.Read(_buffer, (int)_pos, (int)curSize);
    if (numReadBytes == 0)
     return false;
    size -= (uint)numReadBytes;
    _pos += (uint)numReadBytes;
    _streamPos += (uint)numReadBytes;
    if (_pos == _windowSize)
     _streamPos = _pos = 0;
   }
   return true;
  }

  public void ReleaseStream()
  {
   Flush();
   _stream = null;
  }

  public void Flush()
  {
   uint size = _pos - _streamPos;
   if (size == 0)
    return;
   _stream.Write(_buffer, (int)_streamPos, (int)size);
   if (_pos >= _windowSize)
    _pos = 0;
   _streamPos = _pos;
  }

  public void CopyBlock(uint distance, uint len)
  {
   uint pos = _pos - distance - 1;
   if (pos >= _windowSize)
    pos += _windowSize;
   for (; len > 0; len--)
   {
    if (pos >= _windowSize)
     pos = 0;
    _buffer[_pos++] = _buffer[pos++];
    if (_pos >= _windowSize)
     Flush();
   }
  }

  public void PutByte(byte b)
  {
   _buffer[_pos++] = b;
   if (_pos >= _windowSize)
    Flush();
  }

  public byte GetByte(uint distance)
  {
   uint pos = _pos - distance - 1;
   if (pos >= _windowSize)
    pos += _windowSize;
   return _buffer[pos];
  }
 }
}
// LzmaBase.cs

namespace SevenZip.Compression.LZMA
{
 internal abstract class Base
 {
  public const uint kNumRepDistances = 4;
  public const uint kNumStates = 12;

  // static byte []kLiteralNextStates  = {0, 0, 0, 0, 1, 2, 3, 4,  5,  6,   4, 5};
  // static byte []kMatchNextStates    = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10};
  // static byte []kRepNextStates      = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11};
  // static byte []kShortRepNextStates = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11};

  public struct State
  {
   public uint Index;
   public void Init() { Index = 0; }
   public void UpdateChar()
   {
    if (Index < 4) Index = 0;
    else if (Index < 10) Index -= 3;
    else Index -= 6;
   }
   public void UpdateMatch() { Index = (uint)(Index < 7 ? 7 : 10); }
   public void UpdateRep() { Index = (uint)(Index < 7 ? 8 : 11); }
   public void UpdateShortRep() { Index = (uint)(Index < 7 ? 9 : 11); }
   public bool IsCharState() { return Index < 7; }
  }

  public const int kNumPosSlotBits = 6;
  public const int kDicLogSizeMin = 0;
  // public const int kDicLogSizeMax = 30;
  // public const uint kDistTableSizeMax = kDicLogSizeMax * 2;

  public const int kNumLenToPosStatesBits = 2; // it's for speed optimization
  public const uint kNumLenToPosStates = 1 << kNumLenToPosStatesBits;

  public const uint kMatchMinLen = 2;

  public static uint GetLenToPosState(uint len)
  {
   len -= kMatchMinLen;
   if (len < kNumLenToPosStates)
    return len;
   return (uint)(kNumLenToPosStates - 1);
  }

  public const int kNumAlignBits = 4;
  public const uint kAlignTableSize = 1 << kNumAlignBits;
  public const uint kAlignMask = (kAlignTableSize - 1);

  public const uint kStartPosModelIndex = 4;
  public const uint kEndPosModelIndex = 14;
  public const uint kNumPosModels = kEndPosModelIndex - kStartPosModelIndex;

  public const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2);

  public const uint kNumLitPosStatesBitsEncodingMax = 4;
  public const uint kNumLitContextBitsMax = 8;

  public const int kNumPosStatesBitsMax = 4;
  public const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax);
  public const int kNumPosStatesBitsEncodingMax = 4;
  public const uint kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax);

  public const int kNumLowLenBits = 3;
  public const int kNumMidLenBits = 3;
  public const int kNumHighLenBits = 8;
  public const uint kNumLowLenSymbols = 1 << kNumLowLenBits;
  public const uint kNumMidLenSymbols = 1 << kNumMidLenBits;
  public const uint kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols +
    (1 << kNumHighLenBits);
  public const uint kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1;
 }
}
namespace SevenZip.Compression.RangeCoder
{
 class Encoder
 {
  public const uint kTopValue = (1 << 24);

  System.IO.Stream Stream;

  public UInt64 Low;
  public uint Range;
  uint _cacheSize;
  byte _cache;

  long StartPosition;

  public void SetStream(System.IO.Stream stream)
  {
   Stream = stream;
  }

  public void ReleaseStream()
  {
   Stream = null;
  }

  public void Init()
  {
   StartPosition = Stream.Position;

   Low = 0;
   Range = 0xFFFFFFFF;
   _cacheSize = 1;
   _cache = 0;
  }

  public void FlushData()
  {
   for (int i = 0; i < 5; i++)
    ShiftLow();
  }

  public void FlushStream()
  {
   Stream.Flush();
  }

  public void CloseStream()
  {
   Stream.Close();
  }

  public void Encode(uint start, uint size, uint total)
  {
   Low += start * (Range /= total);
   Range *= size;
   while (Range < kTopValue)
   {
    Range <<= 8;
    ShiftLow();
   }
  }

  public void ShiftLow()
  {
   if ((uint)Low < (uint)0xFF000000 || (uint)(Low >> 32) == 1)
   {
    byte temp = _cache;
    do
    {
     Stream.WriteByte((byte)(temp + (Low >> 32)));
     temp = 0xFF;
    }
    while (--_cacheSize != 0);
    _cache = (byte)(((uint)Low) >> 24);
   }
   _cacheSize++;
   Low = ((uint)Low) << 8;
  }

  public void EncodeDirectBits(uint v, int numTotalBits)
  {
   for (int i = numTotalBits - 1; i >= 0; i--)
   {
    Range >>= 1;
    if (((v >> i) & 1) == 1)
     Low += Range;
    if (Range < kTopValue)
    {
     Range <<= 8;
     ShiftLow();
    }
   }
  }

  public void EncodeBit(uint size0, int numTotalBits, uint symbol)
  {
   uint newBound = (Range >> numTotalBits) * size0;
   if (symbol == 0)
    Range = newBound;
   else
   {
    Low += newBound;
    Range -= newBound;
   }
   while (Range < kTopValue)
   {
    Range <<= 8;
    ShiftLow();
   }
  }

  public long GetProcessedSizeAdd()
  {
   return _cacheSize +
    Stream.Position - StartPosition + 4;
   // (long)Stream.GetProcessedSize();
  }
 }

 class Decoder
 {
  public const uint kTopValue = (1 << 24);
  public uint Range;
  public uint Code;
  // public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16);
  public System.IO.Stream Stream;

  public void Init(System.IO.Stream stream)
  {
   // Stream.Init(stream);
   Stream = stream;

   Code = 0;
   Range = 0xFFFFFFFF;
   for (int i = 0; i < 5; i++)
    Code = (Code << 8) | (byte)Stream.ReadByte();
  }

  public void ReleaseStream()
  {
   // Stream.ReleaseStream();
   Stream = null;
  }

  public void CloseStream()
  {
   Stream.Close();
  }

  public void Normalize()
  {
   while (Range < kTopValue)
   {
    Code = (Code << 8) | (byte)Stream.ReadByte();
    Range <<= 8;
   }
  }

  public void Normalize2()
  {
   if (Range < kTopValue)
   {
    Code = (Code << 8) | (byte)Stream.ReadByte();
    Range <<= 8;
   }
  }

  public uint GetThreshold(uint total)
  {
   return Code / (Range /= total);
  }

  public void Decode(uint start, uint size, uint total)
  {
   Code -= start * Range;
   Range *= size;
   Normalize();
  }

  public uint DecodeDirectBits(int numTotalBits)
  {
   uint range = Range;
   uint code = Code;
   uint result = 0;
   for (int i = numTotalBits; i > 0; i--)
   {
    range >>= 1;
    /*
    result <<= 1;
    if (code >= range)
    {
     code -= range;
     result |= 1;
    }
    */
    uint t = (code - range) >> 31;
    code -= range & (t - 1);
    result = (result << 1) | (1 - t);

    if (range < kTopValue)
    {
     code = (code << 8) | (byte)Stream.ReadByte();
     range <<= 8;
    }
   }
   Range = range;
   Code = code;
   return result;
  }

  public uint DecodeBit(uint size0, int numTotalBits)
  {
   uint newBound = (Range >> numTotalBits) * size0;
   uint symbol;
   if (Code < newBound)
   {
    symbol = 0;
    Range = newBound;
   }
   else
   {
    symbol = 1;
    Code -= newBound;
    Range -= newBound;
   }
   Normalize();
   return symbol;
  }

  // ulong GetProcessedSize() {return Stream.GetProcessedSize(); }
 }
}
namespace SevenZip.Compression.RangeCoder
{
 struct BitEncoder
 {
  public const int kNumBitModelTotalBits = 11;
  public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
  const int kNumMoveBits = 5;
  const int kNumMoveReducingBits = 2;
  public const int kNumBitPriceShiftBits = 6;

  uint Prob;

  public void Init() { Prob = kBitModelTotal >> 1; }

  public void UpdateModel(uint symbol)
  {
   if (symbol == 0)
    Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
   else
    Prob -= (Prob) >> kNumMoveBits;
  }

  public void Encode(Encoder encoder, uint symbol)
  {
   // encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol);
   // UpdateModel(symbol);
   uint newBound = (encoder.Range >> kNumBitModelTotalBits) * Prob;
   if (symbol == 0)
   {
    encoder.Range = newBound;
    Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
   }
   else
   {
    encoder.Low += newBound;
    encoder.Range -= newBound;
    Prob -= (Prob) >> kNumMoveBits;
   }
   if (encoder.Range < Encoder.kTopValue)
   {
    encoder.Range <<= 8;
    encoder.ShiftLow();
   }
  }

  private static UInt32[] ProbPrices = new UInt32[kBitModelTotal >> kNumMoveReducingBits];

  static BitEncoder()
  {
   const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits);
   for (int i = kNumBits - 1; i >= 0; i--)
   {
    UInt32 start = (UInt32)1 << (kNumBits - i - 1);
    UInt32 end = (UInt32)1 << (kNumBits - i);
    for (UInt32 j = start; j < end; j++)
     ProbPrices[j] = ((UInt32)i << kNumBitPriceShiftBits) +
      (((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1));
   }
  }

  public uint GetPrice(uint symbol)
  {
   return ProbPrices[(((Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits];
  }
  public uint GetPrice0() { return ProbPrices[Prob >> kNumMoveReducingBits]; }
  public uint GetPrice1() { return ProbPrices[(kBitModelTotal - Prob) >> kNumMoveReducingBits]; }
 }

 struct BitDecoder
 {
  public const int kNumBitModelTotalBits = 11;
  public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
  const int kNumMoveBits = 5;

  uint Prob;

  public void UpdateModel(int numMoveBits, uint symbol)
  {
   if (symbol == 0)
    Prob += (kBitModelTotal - Prob) >> numMoveBits;
   else
    Prob -= (Prob) >> numMoveBits;
  }

  public void Init() { Prob = kBitModelTotal >> 1; }

  public uint Decode(RangeCoder.Decoder rangeDecoder)
  {
   uint newBound = (uint)(rangeDecoder.Range >> kNumBitModelTotalBits) * (uint)Prob;
   if (rangeDecoder.Code < newBound)
   {
    rangeDecoder.Range = newBound;
    Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
    if (rangeDecoder.Range < Decoder.kTopValue)
    {
     rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
     rangeDecoder.Range <<= 8;
    }
    return 0;
   }
   else
   {
    rangeDecoder.Range -= newBound;
    rangeDecoder.Code -= newBound;
    Prob -= (Prob) >> kNumMoveBits;
    if (rangeDecoder.Range < Decoder.kTopValue)
    {
     rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
     rangeDecoder.Range <<= 8;
    }
    return 1;
   }
  }
 }
}
namespace SevenZip.Compression.RangeCoder
{
 struct BitTreeEncoder
 {
  BitEncoder[] Models;
  int NumBitLevels;

  public BitTreeEncoder(int numBitLevels)
  {
   NumBitLevels = numBitLevels;
   Models = new BitEncoder[1 << numBitLevels];
  }

  public void Init()
  {
   for (uint i = 1; i < (1 << NumBitLevels); i++)
    Models[i].Init();
  }

  public void Encode(Encoder rangeEncoder, UInt32 symbol)
  {
   UInt32 m = 1;
   for (int bitIndex = NumBitLevels; bitIndex > 0; )
   {
    bitIndex--;
    UInt32 bit = (symbol >> bitIndex) & 1;
    Models[m].Encode(rangeEncoder, bit);
    m = (m << 1) | bit;
   }
  }

  public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol)
  {
   UInt32 m = 1;
   for (UInt32 i = 0; i < NumBitLevels; i++)
   {
    UInt32 bit = symbol & 1;
    Models[m].Encode(rangeEncoder, bit);
    m = (m << 1) | bit;
    symbol >>= 1;
   }
  }

  public UInt32 GetPrice(UInt32 symbol)
  {
   UInt32 price = 0;
   UInt32 m = 1;
   for (int bitIndex = NumBitLevels; bitIndex > 0; )
   {
    bitIndex--;
    UInt32 bit = (symbol >> bitIndex) & 1;
    price += Models[m].GetPrice(bit);
    m = (m << 1) + bit;
   }
   return price;
  }

  public UInt32 ReverseGetPrice(UInt32 symbol)
  {
   UInt32 price = 0;
   UInt32 m = 1;
   for (int i = NumBitLevels; i > 0; i--)
   {
    UInt32 bit = symbol & 1;
    symbol >>= 1;
    price += Models[m].GetPrice(bit);
    m = (m << 1) | bit;
   }
   return price;
  }

  public static UInt32 ReverseGetPrice(BitEncoder[] Models, UInt32 startIndex,
   int NumBitLevels, UInt32 symbol)
  {
   UInt32 price = 0;
   UInt32 m = 1;
   for (int i = NumBitLevels; i > 0; i--)
   {
    UInt32 bit = symbol & 1;
    symbol >>= 1;
    price += Models[startIndex + m].GetPrice(bit);
    m = (m << 1) | bit;
   }
   return price;
  }

  public static void ReverseEncode(BitEncoder[] Models, UInt32 startIndex,
   Encoder rangeEncoder, int NumBitLevels, UInt32 symbol)
  {
   UInt32 m = 1;
   for (int i = 0; i < NumBitLevels; i++)
   {
    UInt32 bit = symbol & 1;
    Models[startIndex + m].Encode(rangeEncoder, bit);
    m = (m << 1) | bit;
    symbol >>= 1;
   }
  }
 }

 struct BitTreeDecoder
 {
  BitDecoder[] Models;
  int NumBitLevels;

  public BitTreeDecoder(int numBitLevels)
  {
   NumBitLevels = numBitLevels;
   Models = new BitDecoder[1 << numBitLevels];
  }

  public void Init()
  {
   for (uint i = 1; i < (1 << NumBitLevels); i++)
    Models[i].Init();
  }

  public uint Decode(RangeCoder.Decoder rangeDecoder)
  {
   uint m = 1;
   for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--)
    m = (m << 1) + Models[m].Decode(rangeDecoder);
   return m - ((uint)1 << NumBitLevels);
  }

  public uint ReverseDecode(RangeCoder.Decoder rangeDecoder)
  {
   uint m = 1;
   uint symbol = 0;
   for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
   {
    uint bit = Models[m].Decode(rangeDecoder);
    m <<= 1;
    m += bit;
    symbol |= (bit << bitIndex);
   }
   return symbol;
  }

  public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex,
   RangeCoder.Decoder rangeDecoder, int NumBitLevels)
  {
   uint m = 1;
   uint symbol = 0;
   for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
   {
    uint bit = Models[startIndex + m].Decode(rangeDecoder);
    m <<= 1;
    m += bit;
    symbol |= (bit << bitIndex);
   }
   return symbol;
  }
 }
}
namespace SevenZip
{
 /// <summary>
 /// The exception that is thrown when an error in input stream occurs during decoding.
 /// </summary>
 class DataErrorException : ApplicationException
 {
  public DataErrorException(): base("Data Error") { }
 }

 /// <summary>
 /// The exception that is thrown when the value of an argument is outside the allowable range.
 /// </summary>
 class InvalidParamException : ApplicationException
 {
  public InvalidParamException(): base("Invalid Parameter") { }
 }

 public interface ICodeProgress
 {
  /// <summary>
  /// Callback progress.
  /// </summary>
  /// <param name="inSize">
  /// input size. -1 if unknown.
  /// </param>
  /// <param name="outSize">
  /// output size. -1 if unknown.
  /// </param>
  void SetProgress(Int64 inSize, Int64 outSize);
 };

 public interface ICoder
 {
  /// <summary>
  /// Codes streams.
  /// </summary>
  /// <param name="inStream">
  /// input Stream.
  /// </param>
  /// <param name="outStream">
  /// output Stream.
  /// </param>
  /// <param name="inSize">
  /// input Size. -1 if unknown.
  /// </param>
  /// <param name="outSize">
  /// output Size. -1 if unknown.
  /// </param>
  /// <param name="progress">
  /// callback progress reference.
  /// </param>
  /// <exception cref="SevenZip.DataErrorException">
  /// if input stream is not valid
  /// </exception>
  void Code(System.IO.Stream inStream, System.IO.Stream outStream,
   Int64 inSize, Int64 outSize, ICodeProgress progress);
 };

 /*
 public interface ICoder2
 {
   void Code(ISequentialInStream []inStreams,
    const UInt64 []inSizes,
    ISequentialOutStream []outStreams,
    UInt64 []outSizes,
    ICodeProgress progress);
 };
  */

 /// <summary>
 /// Provides the fields that represent properties idenitifiers for compressing.
 /// </summary>
 public enum CoderPropID
 {
  /// <summary>
  /// Specifies default property.
  /// </summary>
  DefaultProp = 0,
  /// <summary>
  /// Specifies size of dictionary.
  /// </summary>
  DictionarySize,
  /// <summary>
  /// Specifies size of memory for PPM*.
  /// </summary>
  UsedMemorySize,
  /// <summary>
  /// Specifies order for PPM methods.
  /// </summary>
  Order,
  /// <summary>
  /// Specifies Block Size.
  /// </summary>
  BlockSize,
  /// <summary>
  /// Specifies number of postion state bits for LZMA (0 <= x <= 4).
  /// </summary>
  PosStateBits,
  /// <summary>
  /// Specifies number of literal context bits for LZMA (0 <= x <= 8).
  /// </summary>
  LitContextBits,
  /// <summary>
  /// Specifies number of literal position bits for LZMA (0 <= x <= 4).
  /// </summary>
  LitPosBits,
  /// <summary>
  /// Specifies number of fast bytes for LZ*.
  /// </summary>
  NumFastBytes,
  /// <summary>
  /// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B".
  /// </summary>
  MatchFinder,
  /// <summary>
  /// Specifies the number of match finder cyckes.
  /// </summary>
  MatchFinderCycles,
  /// <summary>
  /// Specifies number of passes.
  /// </summary>
  NumPasses,
  /// <summary>
  /// Specifies number of algorithm.
  /// </summary>
  Algorithm,
  /// <summary>
  /// Specifies the number of threads.
  /// </summary>
  NumThreads,
  /// <summary>
  /// Specifies mode with end marker.
  /// </summary>
  EndMarker
 };


 public interface ISetCoderProperties
 {
  void SetCoderProperties(CoderPropID[] propIDs, object[] properties);
 };

 public interface IWriteCoderProperties
 {
  void WriteCoderProperties(System.IO.Stream outStream);
 }

 public interface ISetDecoderProperties
 {
  void SetDecoderProperties(byte[] properties);
 }
}

namespace SevenZip.Compression.LZ
{
 public class BinTree : InWindow, IMatchFinder
 {
  UInt32 _cyclicBufferPos;
  UInt32 _cyclicBufferSize = 0;
  UInt32 _matchMaxLen;

  UInt32[] _son;
  UInt32[] _hash;

  UInt32 _cutValue = 0xFF;
  UInt32 _hashMask;
  UInt32 _hashSizeSum = 0;

  bool HASH_ARRAY = true;

  const UInt32 kHash2Size = 1 << 10;
  const UInt32 kHash3Size = 1 << 16;
  const UInt32 kBT2HashSize = 1 << 16;
  const UInt32 kStartMaxLen = 1;
  const UInt32 kHash3Offset = kHash2Size;
  const UInt32 kEmptyHashValue = 0;
  const UInt32 kMaxValForNormalize = ((UInt32)1 << 31) - 1;
 
  UInt32 kNumHashDirectBytes = 0;
  UInt32 kMinMatchCheck = 4;
  UInt32 kFixHashSize = kHash2Size + kHash3Size;
  
  public void SetType(int numHashBytes)
  {
   HASH_ARRAY = (numHashBytes > 2);
   if (HASH_ARRAY)
   {
    kNumHashDirectBytes = 0;
    kMinMatchCheck = 4;
    kFixHashSize = kHash2Size + kHash3Size;
   }
   else
   {
    kNumHashDirectBytes = 2;
    kMinMatchCheck = 2 + 1;
    kFixHashSize = 0;
   }
  }

  public new void SetStream(System.IO.Stream stream) { base.SetStream(stream); }
  public new void ReleaseStream() { base.ReleaseStream(); }
  
  public new void Init()
  {
   base.Init();
   for (UInt32 i = 0; i < _hashSizeSum; i++)
    _hash[i] = kEmptyHashValue;
   _cyclicBufferPos = 0;
   ReduceOffsets(-1);
  }

  public new void MovePos()
  {
   if (++_cyclicBufferPos >= _cyclicBufferSize)
    _cyclicBufferPos = 0;
   base.MovePos();
   if (_pos == kMaxValForNormalize)
    Normalize();
  }

  public new Byte GetIndexByte(Int32 index) { return base.GetIndexByte(index); }

  public new UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit)
  { return base.GetMatchLen(index, distance, limit); }

  public new UInt32 GetNumAvailableBytes() { return base.GetNumAvailableBytes(); }

  public void Create(UInt32 historySize, UInt32 keepAddBufferBefore,
    UInt32 matchMaxLen, UInt32 keepAddBufferAfter)
  {
   if (historySize > kMaxValForNormalize - 256)
    throw new Exception();
   _cutValue = 16 + (matchMaxLen >> 1);
    
   UInt32 windowReservSize = (historySize + keepAddBufferBefore +
     matchMaxLen + keepAddBufferAfter) / 2 + 256;

   base.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize);

   _matchMaxLen = matchMaxLen;

   UInt32 cyclicBufferSize = historySize + 1;
   if (_cyclicBufferSize != cyclicBufferSize)
    _son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2];

   UInt32 hs = kBT2HashSize;

   if (HASH_ARRAY)
   {
    hs = historySize - 1;
    hs |= (hs >> 1);
    hs |= (hs >> 2);
    hs |= (hs >> 4);
    hs |= (hs >> 8);
    hs >>= 1;
    hs |= 0xFFFF;
    if (hs > (1 << 24))
     hs >>= 1;
    _hashMask = hs;
    hs++;
    hs += kFixHashSize;
   }
   if (hs != _hashSizeSum)
    _hash = new UInt32[_hashSizeSum = hs];
  }

  public UInt32 GetMatches(UInt32[] distances)
  {
   UInt32 lenLimit;
   if (_pos + _matchMaxLen <= _streamPos)
    lenLimit = _matchMaxLen;
   else
   {
    lenLimit = _streamPos - _pos;
    if (lenLimit < kMinMatchCheck)
    {
     MovePos();
     return 0;
    }
   }

   UInt32 offset = 0;
   UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
   UInt32 cur = _bufferOffset + _pos;
   UInt32 maxLen = kStartMaxLen; // to avoid items for len < hashSize;
   UInt32 hashValue, hash2Value = 0, hash3Value = 0;

   if (HASH_ARRAY)
   {
    UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
    hash2Value = temp & (kHash2Size - 1);
    temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8);
    hash3Value = temp & (kHash3Size - 1);
    hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
   }
   else
    hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8);

   UInt32 curMatch = _hash[kFixHashSize + hashValue];
   if (HASH_ARRAY)
   {
    UInt32 curMatch2 = _hash[hash2Value];
    UInt32 curMatch3 = _hash[kHash3Offset + hash3Value];
    _hash[hash2Value] = _pos;
    _hash[kHash3Offset + hash3Value] = _pos;
    if (curMatch2 > matchMinPos)
     if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur])
     {
      distances[offset++] = maxLen = 2;
      distances[offset++] = _pos - curMatch2 - 1;
     }
    if (curMatch3 > matchMinPos)
     if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur])
     {
      if (curMatch3 == curMatch2)
       offset -= 2;
      distances[offset++] = maxLen = 3;
      distances[offset++] = _pos - curMatch3 - 1;
      curMatch2 = curMatch3;
     }
    if (offset != 0 && curMatch2 == curMatch)
    {
     offset -= 2;
     maxLen = kStartMaxLen;
    }
   }

   _hash[kFixHashSize + hashValue] = _pos;

   UInt32 ptr0 = (_cyclicBufferPos << 1) + 1;
   UInt32 ptr1 = (_cyclicBufferPos << 1);

   UInt32 len0, len1;
   len0 = len1 = kNumHashDirectBytes;
   
   if (kNumHashDirectBytes != 0)
   {
    if (curMatch > matchMinPos)
    {
     if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] !=
       _bufferBase[cur + kNumHashDirectBytes])
     {
      distances[offset++] = maxLen = kNumHashDirectBytes;
      distances[offset++] = _pos - curMatch - 1;
     }
    }
   }
   
   UInt32 count = _cutValue;
   
   while(true)
   {
    if(curMatch <= matchMinPos || count-- == 0)
    {
     _son[ptr0] = _son[ptr1] = kEmptyHashValue;
     break;
    }
    UInt32 delta = _pos - curMatch;
    UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ?
       (_cyclicBufferPos - delta) :
       (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;

    UInt32 pby1 = _bufferOffset + curMatch;
    UInt32 len = Math.Min(len0, len1);
    if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
    {
     while(++len != lenLimit)
      if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
       break;
     if (maxLen < len)
     {
      distances[offset++] = maxLen = len;
      distances[offset++] = delta - 1;
      if (len == lenLimit)
      {
       _son[ptr1] = _son[cyclicPos];
       _son[ptr0] = _son[cyclicPos + 1];
       break;
      }
     }
    }
    if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
    {
     _son[ptr1] = curMatch;
     ptr1 = cyclicPos + 1;
     curMatch = _son[ptr1];
     len1 = len;
    }
    else
    {
     _son[ptr0] = curMatch;
     ptr0 = cyclicPos;
     curMatch = _son[ptr0];
     len0 = len;
    }
   }
   MovePos();
   return offset;
  }

  public void Skip(UInt32 num)
  {
   do
   {
    UInt32 lenLimit;
    if (_pos + _matchMaxLen <= _streamPos)
     lenLimit = _matchMaxLen;
    else
    {
     lenLimit = _streamPos - _pos;
     if (lenLimit < kMinMatchCheck)
     {
      MovePos();
      continue;
     }
    }

    UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
    UInt32 cur = _bufferOffset + _pos;

    UInt32 hashValue;

    if (HASH_ARRAY)
    {
     UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
     UInt32 hash2Value = temp & (kHash2Size - 1);
     _hash[hash2Value] = _pos;
     temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8);
     UInt32 hash3Value = temp & (kHash3Size - 1);
     _hash[kHash3Offset + hash3Value] = _pos;
     hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
    }
    else
     hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8);

    UInt32 curMatch = _hash[kFixHashSize + hashValue];
    _hash[kFixHashSize + hashValue] = _pos;

    UInt32 ptr0 = (_cyclicBufferPos << 1) + 1;
    UInt32 ptr1 = (_cyclicBufferPos << 1);

    UInt32 len0, len1;
    len0 = len1 = kNumHashDirectBytes;

    UInt32 count = _cutValue;
    while (true)
    {
     if (curMatch <= matchMinPos || count-- == 0)
     {
      _son[ptr0] = _son[ptr1] = kEmptyHashValue;
      break;
     }

     UInt32 delta = _pos - curMatch;
     UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ?
        (_cyclicBufferPos - delta) :
        (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;

     UInt32 pby1 = _bufferOffset + curMatch;
     UInt32 len = Math.Min(len0, len1);
     if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
     {
      while (++len != lenLimit)
       if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
        break;
      if (len == lenLimit)
      {
       _son[ptr1] = _son[cyclicPos];
       _son[ptr0] = _son[cyclicPos + 1];
       break;
      }
     }
     if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
     {
      _son[ptr1] = curMatch;
      ptr1 = cyclicPos + 1;
      curMatch = _son[ptr1];
      len1 = len;
     }
     else
     {
      _son[ptr0] = curMatch;
      ptr0 = cyclicPos;
      curMatch = _son[ptr0];
      len0 = len;
     }
    }
    MovePos();
   }
   while (--num != 0);
  }

  void NormalizeLinks(UInt32[] items, UInt32 numItems, UInt32 subValue)
  {
   for (UInt32 i = 0; i < numItems; i++)
   {
    UInt32 value = items[i];
    if (value <= subValue)
     value = kEmptyHashValue;
    else
     value -= subValue;
    items[i] = value;
   }
  }

  void Normalize()
  {
   UInt32 subValue = _pos - _cyclicBufferSize;
   NormalizeLinks(_son, _cyclicBufferSize * 2, subValue);
   NormalizeLinks(_hash, _hashSizeSum, subValue);
   ReduceOffsets((Int32)subValue);
  }

  public void SetCutValue(UInt32 cutValue) { _cutValue = cutValue; }
 }
}
namespace SevenZip.Compression.LZMA
{
 using RangeCoder;

 public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream
 {
  class LenDecoder
  {
   BitDecoder m_Choice = new BitDecoder();
   BitDecoder m_Choice2 = new BitDecoder();
   BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
   BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
   BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits);
   uint m_NumPosStates = 0;

   public void Create(uint numPosStates)
   {
    for (uint posState = m_NumPosStates; posState < numPosStates; posState++)
    {
     m_LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits);
     m_MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits);
    }
    m_NumPosStates = numPosStates;
   }

   public void Init()
   {
    m_Choice.Init();
    for (uint posState = 0; posState < m_NumPosStates; posState++)
    {
     m_LowCoder[posState].Init();
     m_MidCoder[posState].Init();
    }
    m_Choice2.Init();
    m_HighCoder.Init();
   }

   public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState)
   {
    if (m_Choice.Decode(rangeDecoder) == 0)
     return m_LowCoder[posState].Decode(rangeDecoder);
    else
    {
     uint symbol = Base.kNumLowLenSymbols;
     if (m_Choice2.Decode(rangeDecoder) == 0)
      symbol += m_MidCoder[posState].Decode(rangeDecoder);
     else
     {
      symbol += Base.kNumMidLenSymbols;
      symbol += m_HighCoder.Decode(rangeDecoder);
     }
     return symbol;
    }
   }
  }

  class LiteralDecoder
  {
   struct Decoder2
   {
    BitDecoder[] m_Decoders;
    public void Create() { m_Decoders = new BitDecoder[0x300]; }
    public void Init() { for (int i = 0; i < 0x300; i++) m_Decoders[i].Init(); }

    public byte DecodeNormal(RangeCoder.Decoder rangeDecoder)
    {
     uint symbol = 1;
     do
      symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder);
     while (symbol < 0x100);
     return (byte)symbol;
    }

    public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte)
    {
     uint symbol = 1;
     do
     {
      uint matchBit = (uint)(matchByte >> 7) & 1;
      matchByte <<= 1;
      uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder);
      symbol = (symbol << 1) | bit;
      if (matchBit != bit)
      {
       while (symbol < 0x100)
        symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder);
       break;
      }
     }
     while (symbol < 0x100);
     return (byte)symbol;
    }
   }

   Decoder2[] m_Coders;
   int m_NumPrevBits;
   int m_NumPosBits;
   uint m_PosMask;

   public void Create(int numPosBits, int numPrevBits)
   {
    if (m_Coders != null && m_NumPrevBits == numPrevBits &&
     m_NumPosBits == numPosBits)
     return;
    m_NumPosBits = numPosBits;
    m_PosMask = ((uint)1 << numPosBits) - 1;
    m_NumPrevBits = numPrevBits;
    uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
    m_Coders = new Decoder2[numStates];
    for (uint i = 0; i < numStates; i++)
     m_Coders[i].Create();
   }

   public void Init()
   {
    uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
    for (uint i = 0; i < numStates; i++)
     m_Coders[i].Init();
   }

   uint GetState(uint pos, byte prevByte)
   { return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits)); }

   public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte)
   { return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); }

   public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte)
   { return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); }
  };

  LZ.OutWindow m_OutWindow = new LZ.OutWindow();
  RangeCoder.Decoder m_RangeDecoder = new RangeCoder.Decoder();

  BitDecoder[] m_IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
  BitDecoder[] m_IsRepDecoders = new BitDecoder[Base.kNumStates];
  BitDecoder[] m_IsRepG0Decoders = new BitDecoder[Base.kNumStates];
  BitDecoder[] m_IsRepG1Decoders = new BitDecoder[Base.kNumStates];
  BitDecoder[] m_IsRepG2Decoders = new BitDecoder[Base.kNumStates];
  BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax];

  BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates];
  BitDecoder[] m_PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex];

  BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits);

  LenDecoder m_LenDecoder = new LenDecoder();
  LenDecoder m_RepLenDecoder = new LenDecoder();

  LiteralDecoder m_LiteralDecoder = new LiteralDecoder();

  uint m_DictionarySize;
  uint m_DictionarySizeCheck;

  uint m_PosStateMask;

  public Decoder()
  {
   m_DictionarySize = 0xFFFFFFFF;
   for (int i = 0; i < Base.kNumLenToPosStates; i++)
    m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits);
  }

  void SetDictionarySize(uint dictionarySize)
  {
   if (m_DictionarySize != dictionarySize)
   {
    m_DictionarySize = dictionarySize;
    m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1);
    uint blockSize = Math.Max(m_DictionarySizeCheck, (1 << 12));
    m_OutWindow.Create(blockSize);
   }
  }

  void SetLiteralProperties(int lp, int lc)
  {
   if (lp > 8)
    throw new InvalidParamException();
   if (lc > 8)
    throw new InvalidParamException();
   m_LiteralDecoder.Create(lp, lc);
  }

  void SetPosBitsProperties(int pb)
  {
   if (pb > Base.kNumPosStatesBitsMax)
    throw new InvalidParamException();
   uint numPosStates = (uint)1 << pb;
   m_LenDecoder.Create(numPosStates);
   m_RepLenDecoder.Create(numPosStates);
   m_PosStateMask = numPosStates - 1;
  }

  bool _solid = false;
  void Init(System.IO.Stream inStream, System.IO.Stream outStream)
  {
   m_RangeDecoder.Init(inStream);
   m_OutWindow.Init(outStream, _solid);

   uint i;
   for (i = 0; i < Base.kNumStates; i++)
   {
    for (uint j = 0; j <= m_PosStateMask; j++)
    {
     uint index = (i << Base.kNumPosStatesBitsMax) + j;
     m_IsMatchDecoders[index].Init();
     m_IsRep0LongDecoders[index].Init();
    }
    m_IsRepDecoders[i].Init();
    m_IsRepG0Decoders[i].Init();
    m_IsRepG1Decoders[i].Init();
    m_IsRepG2Decoders[i].Init();
   }

   m_LiteralDecoder.Init();
   for (i = 0; i < Base.kNumLenToPosStates; i++)
    m_PosSlotDecoder[i].Init();
   // m_PosSpecDecoder.Init();
   for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++)
    m_PosDecoders[i].Init();

   m_LenDecoder.Init();
   m_RepLenDecoder.Init();
   m_PosAlignDecoder.Init();
  }

  public void Code(System.IO.Stream inStream, System.IO.Stream outStream,
   Int64 inSize, Int64 outSize, ICodeProgress progress)
  {
   Init(inStream, outStream);

   Base.State state = new Base.State();
   state.Init();
   uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0;

   UInt64 nowPos64 = 0;
   UInt64 outSize64 = (UInt64)outSize;
   if (nowPos64 < outSize64)
   {
    if (m_IsMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(m_RangeDecoder) != 0)
     throw new DataErrorException();
    state.UpdateChar();
    byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0);
    m_OutWindow.PutByte(b);
    nowPos64++;
   }
   while (nowPos64 < outSize64)
   {
    // UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64);
     // while(nowPos64 < next)
    {
     uint posState = (uint)nowPos64 & m_PosStateMask;
     if (m_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0)
     {
      byte b;
      byte prevByte = m_OutWindow.GetByte(0);
      if (!state.IsCharState())
       b = m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder,
        (uint)nowPos64, prevByte, m_OutWindow.GetByte(rep0));
      else
       b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)nowPos64, prevByte);
      m_OutWindow.PutByte(b);
      state.UpdateChar();
      nowPos64++;
     }
     else
     {
      uint len;
      if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1)
      {
       if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0)
       {
        if (m_IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0)
        {
         state.UpdateShortRep();
         m_OutWindow.PutByte(m_OutWindow.GetByte(rep0));
         nowPos64++;
         continue;
        }
       }
       else
       {
        UInt32 distance;
        if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0)
        {
         distance = rep1;
        }
        else
        {
         if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0)
          distance = rep2;
         else
         {
          distance = rep3;
          rep3 = rep2;
         }
         rep2 = rep1;
        }
        rep1 = rep0;
        rep0 = distance;
       }
       len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen;
       state.UpdateRep();
      }
      else
      {
       rep3 = rep2;
       rep2 = rep1;
       rep1 = rep0;
       len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState);
       state.UpdateMatch();
       uint posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder);
       if (posSlot >= Base.kStartPosModelIndex)
       {
        int numDirectBits = (int)((posSlot >> 1) - 1);
        rep0 = ((2 | (posSlot & 1)) << numDirectBits);
        if (posSlot < Base.kEndPosModelIndex)
         rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders,
           rep0 - posSlot - 1, m_RangeDecoder, numDirectBits);
        else
        {
         rep0 += (m_RangeDecoder.DecodeDirectBits(
          numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits);
         rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
        }
       }
       else
        rep0 = posSlot;
      }
      if (rep0 >= m_OutWindow.TrainSize + nowPos64 || rep0 >= m_DictionarySizeCheck)
      {
       if (rep0 == 0xFFFFFFFF)
        break;
       throw new DataErrorException();
      }
      m_OutWindow.CopyBlock(rep0, len);
      nowPos64 += len;
     }
    }
   }
   m_OutWindow.Flush();
   m_OutWindow.ReleaseStream();
   m_RangeDecoder.ReleaseStream();
  }

  public void SetDecoderProperties(byte[] properties)
  {
   if (properties.Length < 5)
    throw new InvalidParamException();
   int lc = properties[0] % 9;
   int remainder = properties[0] / 9;
   int lp = remainder % 5;
   int pb = remainder / 5;
   if (pb > Base.kNumPosStatesBitsMax)
    throw new InvalidParamException();
   UInt32 dictionarySize = 0;
   for (int i = 0; i < 4; i++)
    dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8);
   SetDictionarySize(dictionarySize);
   SetLiteralProperties(lp, lc);
   SetPosBitsProperties(pb);
  }

  public bool Train(System.IO.Stream stream)
  {
   _solid = true;
   return m_OutWindow.Train(stream);
  }

  /*
  public override bool CanRead { get { return true; }}
  public override bool CanWrite { get { return true; }}
  public override bool CanSeek { get { return true; }}
  public override long Length { get { return 0; }}
  public override long Position
  {
   get { return 0; }
   set { }
  }
  public override void Flush() { }
  public override int Read(byte[] buffer, int offset, int count)
  {
   return 0;
  }
  public override void Write(byte[] buffer, int offset, int count)
  {
  }
  public override long Seek(long offset, System.IO.SeekOrigin origin)
  {
   return 0;
  }
  public override void SetLength(long value) {}
  */
 }
}
namespace SevenZip.Compression.LZMA
{
 using RangeCoder;

 public class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties
 {
  enum EMatchFinderType
  {
   BT2,
   BT4,
  };

  const UInt32 kIfinityPrice = 0xFFFFFFF;

  static Byte[] g_FastPos = new Byte[1 << 11];

  static Encoder()
  {
   const Byte kFastSlots = 22;
   int c = 2;
   g_FastPos[0] = 0;
   g_FastPos[1] = 1;
   for (Byte slotFast = 2; slotFast < kFastSlots; slotFast++)
   {
    UInt32 k = ((UInt32)1 << ((slotFast >> 1) - 1));
    for (UInt32 j = 0; j < k; j++, c++)
     g_FastPos[c] = slotFast;
   }
  }

  static UInt32 GetPosSlot(UInt32 pos)
  {
   if (pos < (1 << 11))
    return g_FastPos[pos];
   if (pos < (1 << 21))
    return (UInt32)(g_FastPos[pos >> 10] + 20);
   return (UInt32)(g_FastPos[pos >> 20] + 40);
  }

  static UInt32 GetPosSlot2(UInt32 pos)
  {
   if (pos < (1 << 17))
    return (UInt32)(g_FastPos[pos >> 6] + 12);
   if (pos < (1 << 27))
    return (UInt32)(g_FastPos[pos >> 16] + 32);
   return (UInt32)(g_FastPos[pos >> 26] + 52);
  }

  Base.State _state = new Base.State();
  Byte _previousByte;
  UInt32[] _repDistances = new UInt32[Base.kNumRepDistances];

  void BaseInit()
  {
   _state.Init();
   _previousByte = 0;
   for (UInt32 i = 0; i < Base.kNumRepDistances; i++)
    _repDistances[i] = 0;
  }

  const int kDefaultDictionaryLogSize = 22;
  const UInt32 kNumFastBytesDefault = 0x20;

  class LiteralEncoder
  {
   public struct Encoder2
   {
    BitEncoder[] m_Encoders;

    public void Create() { m_Encoders = new BitEncoder[0x300]; }

    public void Init() { for (int i = 0; i < 0x300; i++) m_Encoders[i].Init(); }

    public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol)
    {
     uint context = 1;
     for (int i = 7; i >= 0; i--)
     {
      uint bit = (uint)((symbol >> i) & 1);
      m_Encoders[context].Encode(rangeEncoder, bit);
      context = (context << 1) | bit;
     }
    }

    public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
    {
     uint context = 1;
     bool same = true;
     for (int i = 7; i >= 0; i--)
     {
      uint bit = (uint)((symbol >> i) & 1);
      uint state = context;
      if (same)
      {
       uint matchBit = (uint)((matchByte >> i) & 1);
       state += ((1 + matchBit) << 8);
       same = (matchBit == bit);
      }
      m_Encoders[state].Encode(rangeEncoder, bit);
      context = (context << 1) | bit;
     }
    }

    public uint GetPrice(bool matchMode, byte matchByte, byte symbol)
    {
     uint price = 0;
     uint context = 1;
     int i = 7;
     if (matchMode)
     {
      for (; i >= 0; i--)
      {
       uint matchBit = (uint)(matchByte >> i) & 1;
       uint bit = (uint)(symbol >> i) & 1;
       price += m_Encoders[((1 + matchBit) << 8) + context].GetPrice(bit);
       context = (context << 1) | bit;
       if (matchBit != bit)
       {
        i--;
        break;
       }
      }
     }
     for (; i >= 0; i--)
     {
      uint bit = (uint)(symbol >> i) & 1;
      price += m_Encoders[context].GetPrice(bit);
      context = (context << 1) | bit;
     }
     return price;
    }
   }

   Encoder2[] m_Coders;
   int m_NumPrevBits;
   int m_NumPosBits;
   uint m_PosMask;

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

   public void Init()
   {
    uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
    for (uint i = 0; i < numStates; i++)
     m_Coders[i].Init();
   }

   public Encoder2 GetSubCoder(UInt32 pos, Byte prevByte)
   { return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits))]; }
  }

  class LenEncoder
  {
   RangeCoder.BitEncoder _choice = new RangeCoder.BitEncoder();
   RangeCoder.BitEncoder _choice2 = new RangeCoder.BitEncoder();
   RangeCoder.BitTreeEncoder[] _lowCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax];
   RangeCoder.BitTreeEncoder[] _midCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax];
   RangeCoder.BitTreeEncoder _highCoder = new RangeCoder.BitTreeEncoder(Base.kNumHighLenBits);

   public LenEncoder()
   {
    for (UInt32 posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++)
    {
     _lowCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumLowLenBits);
     _midCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumMidLenBits);
    }
   }

   public void Init(UInt32 numPosStates)
   {
    _choice.Init();
    _choice2.Init();
    for (UInt32 posState = 0; posState < numPosStates; posState++)
    {
     _lowCoder[posState].Init();
     _midCoder[posState].Init();
    }
    _highCoder.Init();
   }

   public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
   {
    if (symbol < Base.kNumLowLenSymbols)
    {
     _choice.Encode(rangeEncoder, 0);
     _lowCoder[posState].Encode(rangeEncoder, symbol);
    }
    else
    {
     symbol -= Base.kNumLowLenSymbols;
     _choice.Encode(rangeEncoder, 1);
     if (symbol < Base.kNumMidLenSymbols)
     {
      _choice2.Encode(rangeEncoder, 0);
      _midCoder[posState].Encode(rangeEncoder, symbol);
     }
     else
     {
      _choice2.Encode(rangeEncoder, 1);
      _highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols);
     }
    }
   }

   public void SetPrices(UInt32 posState, UInt32 numSymbols, UInt32[] prices, UInt32 st)
   {
    UInt32 a0 = _choice.GetPrice0();
    UInt32 a1 = _choice.GetPrice1();
    UInt32 b0 = a1 + _choice2.GetPrice0();
    UInt32 b1 = a1 + _choice2.GetPrice1();
    UInt32 i = 0;
    for (i = 0; i < Base.kNumLowLenSymbols; i++)
    {
     if (i >= numSymbols)
      return;
     prices[st + i] = a0 + _lowCoder[posState].GetPrice(i);
    }
    for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++)
    {
     if (i >= numSymbols)
      return;
     prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols);
    }
    for (; i < numSymbols; i++)
     prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols);
   }
  };

  const UInt32 kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols;

  class LenPriceTableEncoder : LenEncoder
  {
   UInt32[] _prices = new UInt32[Base.kNumLenSymbols << Base.kNumPosStatesBitsEncodingMax];
   UInt32 _tableSize;
   UInt32[] _counters = new UInt32[Base.kNumPosStatesEncodingMax];

   public void SetTableSize(UInt32 tableSize) { _tableSize = tableSize; }

   public UInt32 GetPrice(UInt32 symbol, UInt32 posState)
   {
    return _prices[posState * Base.kNumLenSymbols + symbol];
   }

   void UpdateTable(UInt32 posState)
   {
    SetPrices(posState, _tableSize, _prices, posState * Base.kNumLenSymbols);
    _counters[posState] = _tableSize;
   }

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

   public new void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
   {
    base.Encode(rangeEncoder, symbol, posState);
    if (--_counters[posState] == 0)
     UpdateTable(posState);
   }
  }

  const UInt32 kNumOpts = 1 << 12;
  class Optimal
  {
   public Base.State State;

   public bool Prev1IsChar;
   public bool Prev2;

   public UInt32 PosPrev2;
   public UInt32 BackPrev2;

   public UInt32 Price;
   public UInt32 PosPrev;
   public UInt32 BackPrev;

   public UInt32 Backs0;
   public UInt32 Backs1;
   public UInt32 Backs2;
   public UInt32 Backs3;

   public void MakeAsChar() { BackPrev = 0xFFFFFFFF; Prev1IsChar = false; }
   public void MakeAsShortRep() { BackPrev = 0; ; Prev1IsChar = false; }
   public bool IsShortRep() { return (BackPrev == 0); }
  };
  Optimal[] _optimum = new Optimal[kNumOpts];
  LZ.IMatchFinder _matchFinder = null;
  RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder();

  RangeCoder.BitEncoder[] _isMatch = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
  RangeCoder.BitEncoder[] _isRep = new RangeCoder.BitEncoder[Base.kNumStates];
  RangeCoder.BitEncoder[] _isRepG0 = new RangeCoder.BitEncoder[Base.kNumStates];
  RangeCoder.BitEncoder[] _isRepG1 = new RangeCoder.BitEncoder[Base.kNumStates];
  RangeCoder.BitEncoder[] _isRepG2 = new RangeCoder.BitEncoder[Base.kNumStates];
  RangeCoder.BitEncoder[] _isRep0Long = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax];

  RangeCoder.BitTreeEncoder[] _posSlotEncoder = new RangeCoder.BitTreeEncoder[Base.kNumLenToPosStates];
  
  RangeCoder.BitEncoder[] _posEncoders = new RangeCoder.BitEncoder[Base.kNumFullDistances - Base.kEndPosModelIndex];
  RangeCoder.BitTreeEncoder _posAlignEncoder = new RangeCoder.BitTreeEncoder(Base.kNumAlignBits);

  LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder();
  LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder();

  LiteralEncoder _literalEncoder = new LiteralEncoder();

  UInt32[] _matchDistances = new UInt32[Base.kMatchMaxLen * 2 + 2];
  
  UInt32 _numFastBytes = kNumFastBytesDefault;
  UInt32 _longestMatchLength;
  UInt32 _numDistancePairs;

  UInt32 _additionalOffset;

  UInt32 _optimumEndIndex;
  UInt32 _optimumCurrentIndex;

  bool _longestMatchWasFound;

  UInt32[] _posSlotPrices = new UInt32[1 << (Base.kNumPosSlotBits + Base.kNumLenToPosStatesBits)];
  UInt32[] _distancesPrices = new UInt32[Base.kNumFullDistances << Base.kNumLenToPosStatesBits];
  UInt32[] _alignPrices = new UInt32[Base.kAlignTableSize];
  UInt32 _alignPriceCount;

  UInt32 _distTableSize = (kDefaultDictionaryLogSize * 2);

  int _posStateBits = 2;
  UInt32 _posStateMask = (4 - 1);
  int _numLiteralPosStateBits = 0;
  int _numLiteralContextBits = 3;

  UInt32 _dictionarySize = (1 << kDefaultDictionaryLogSize);
  UInt32 _dictionarySizePrev = 0xFFFFFFFF;
  UInt32 _numFastBytesPrev = 0xFFFFFFFF;

  Int64 nowPos64;
  bool _finished;
  System.IO.Stream _inStream;

  EMatchFinderType _matchFinderType = EMatchFinderType.BT4;
  bool _writeEndMark = false;
  
  bool _needReleaseMFStream;

  void Create()
  {
   if (_matchFinder == null)
   {
    LZ.BinTree bt = new LZ.BinTree();
    int numHashBytes = 4;
    if (_matchFinderType == EMatchFinderType.BT2)
     numHashBytes = 2;
    bt.SetType(numHashBytes);
    _matchFinder = bt;
   }
   _literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits);

   if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes)
    return;
   _matchFinder.Create(_dictionarySize, kNumOpts, _numFastBytes, Base.kMatchMaxLen + 1);
   _dictionarySizePrev = _dictionarySize;
   _numFastBytesPrev = _numFastBytes;
  }

  public Encoder()
  {
   for (int i = 0; i < kNumOpts; i++)
    _optimum[i] = new Optimal();
   for (int i = 0; i < Base.kNumLenToPosStates; i++)
    _posSlotEncoder[i] = new RangeCoder.BitTreeEncoder(Base.kNumPosSlotBits);
  }

  void SetWriteEndMarkerMode(bool writeEndMarker)
  {
   _writeEndMark = writeEndMarker;
  }

  void Init()
  {
   BaseInit();
   _rangeEncoder.Init();

   uint i;
   for (i = 0; i < Base.kNumStates; i++)
   {
    for (uint j = 0; j <= _posStateMask; j++)
    {
     uint complexState = (i << Base.kNumPosStatesBitsMax) + j;
     _isMatch[complexState].Init();
     _isRep0Long[complexState].Init();
    }
    _isRep[i].Init();
    _isRepG0[i].Init();
    _isRepG1[i].Init();
    _isRepG2[i].Init();
   }
   _literalEncoder.Init();
   for (i = 0; i < Base.kNumLenToPosStates; i++)
    _posSlotEncoder[i].Init();
   for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++)
    _posEncoders[i].Init();

   _lenEncoder.Init((UInt32)1 << _posStateBits);
   _repMatchLenEncoder.Init((UInt32)1 << _posStateBits);

   _posAlignEncoder.Init();

   _longestMatchWasFound = false;
   _optimumEndIndex = 0;
   _optimumCurrentIndex = 0;
   _additionalOffset = 0;
  }

  void ReadMatchDistances(out UInt32 lenRes, out UInt32 numDistancePairs)
  {
   lenRes = 0;
   numDistancePairs = _matchFinder.GetMatches(_matchDistances);
   if (numDistancePairs > 0)
   {
    lenRes = _matchDistances[numDistancePairs - 2];
    if (lenRes == _numFastBytes)
     lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[numDistancePairs - 1],
      Base.kMatchMaxLen - lenRes);
   }
   _additionalOffset++;
  }


  void MovePos(UInt32 num)
  {
   if (num > 0)
   {
    _matchFinder.Skip(num);
    _additionalOffset += num;
   }
  }

  UInt32 GetRepLen1Price(Base.State state, UInt32 posState)
  {
   return _isRepG0[state.Index].GetPrice0() +
     _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0();
  }

  UInt32 GetPureRepPrice(UInt32 repIndex, Base.State state, UInt32 posState)
  {
   UInt32 price;
   if (repIndex == 0)
   {
    price = _isRepG0[state.Index].GetPrice0();
    price += _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
   }
   else
   {
    price = _isRepG0[state.Index].GetPrice1();
    if (repIndex == 1)
     price += _isRepG1[state.Index].GetPrice0();
    else
    {
     price += _isRepG1[state.Index].GetPrice1();
     price += _isRepG2[state.Index].GetPrice(repIndex - 2);
    }
   }
   return price;
  }

  UInt32 GetRepPrice(UInt32 repIndex, UInt32 len, Base.State state, UInt32 posState)
  {
   UInt32 price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState);
   return price + GetPureRepPrice(repIndex, state, posState);
  }
 
  UInt32 GetPosLenPrice(UInt32 pos, UInt32 len, UInt32 posState)
  {
   UInt32 price;
   UInt32 lenToPosState = Base.GetLenToPosState(len);
   if (pos < Base.kNumFullDistances)
    price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos];
   else
    price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + GetPosSlot2(pos)] +
     _alignPrices[pos & Base.kAlignMask];
   return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState);
  }

  UInt32 Backward(out UInt32 backRes, UInt32 cur)
  {
   _optimumEndIndex = cur;
   UInt32 posMem = _optimum[cur].PosPrev;
   UInt32 backMem = _optimum[cur].BackPrev;
   do
   {
    if (_optimum[cur].Prev1IsChar)
    {
     _optimum[posMem].MakeAsChar();
     _optimum[posMem].PosPrev = posMem - 1;
     if (_optimum[cur].Prev2)
     {
      _optimum[posMem - 1].Prev1IsChar = false;
      _optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2;
      _optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2;
     }
    }
    UInt32 posPrev = posMem;
    UInt32 backCur = backMem;

    backMem = _optimum[posPrev].BackPrev;
    posMem = _optimum[posPrev].PosPrev;

    _optimum[posPrev].BackPrev = backCur;
    _optimum[posPrev].PosPrev = cur;
    cur = posPrev;
   }
   while (cur > 0);
   backRes = _optimum[0].BackPrev;
   _optimumCurrentIndex = _optimum[0].PosPrev;
   return _optimumCurrentIndex;
  }

  UInt32[] reps = new UInt32[Base.kNumRepDistances];
  UInt32[] repLens = new UInt32[Base.kNumRepDistances];


  UInt32 GetOptimum(UInt32 position, out UInt32 backRes)
  {
   if (_optimumEndIndex != _optimumCurrentIndex)
   {
    UInt32 lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex;
    backRes = _optimum[_optimumCurrentIndex].BackPrev;
    _optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev;
    return lenRes;
   }
   _optimumCurrentIndex = _optimumEndIndex = 0;

   UInt32 lenMain, numDistancePairs;
   if (!_longestMatchWasFound)
   {
    ReadMatchDistances(out lenMain, out numDistancePairs);
   }
   else
   {
    lenMain = _longestMatchLength;
    numDistancePairs = _numDistancePairs;
    _longestMatchWasFound = false;
   }

   UInt32 numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1;
   if (numAvailableBytes < 2)
   {
    backRes = 0xFFFFFFFF;
    return 1;
   }
   if (numAvailableBytes > Base.kMatchMaxLen)
    numAvailableBytes = Base.kMatchMaxLen;

   UInt32 repMaxIndex = 0;
   UInt32 i;   
   for (i = 0; i < Base.kNumRepDistances; i++)
   {
    reps[i] = _repDistances[i];
    repLens[i] = _matchFinder.GetMatchLen(0 - 1, reps[i], Base.kMatchMaxLen);
    if (repLens[i] > repLens[repMaxIndex])
     repMaxIndex = i;
   }
   if (repLens[repMaxIndex] >= _numFastBytes)
   {
    backRes = repMaxIndex;
    UInt32 lenRes = repLens[repMaxIndex];
    MovePos(lenRes - 1);
    return lenRes;
   }

   if (lenMain >= _numFastBytes)
   {
    backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances;
    MovePos(lenMain - 1);
    return lenMain;
   }
   
   Byte currentByte = _matchFinder.GetIndexByte(0 - 1);
   Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - 1));

   if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2)
   {
    backRes = (UInt32)0xFFFFFFFF;
    return 1;
   }

   _optimum[0].State = _state;

   UInt32 posState = (position & _posStateMask);

   _optimum[1].Price = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() +
     _literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!_state.IsCharState(), matchByte, currentByte);
   _optimum[1].MakeAsChar();

   UInt32 matchPrice = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
   UInt32 repMatchPrice = matchPrice + _isRep[_state.Index].GetPrice1();

   if (matchByte == currentByte)
   {
    UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState);
    if (shortRepPrice < _optimum[1].Price)
    {
     _optimum[1].Price = shortRepPrice;
     _optimum[1].MakeAsShortRep();
    }
   }

   UInt32 lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]);

   if(lenEnd < 2)
   {
    backRes = _optimum[1].BackPrev;
    return 1;
   }
   
   _optimum[1].PosPrev = 0;

   _optimum[0].Backs0 = reps[0];
   _optimum[0].Backs1 = reps[1];
   _optimum[0].Backs2 = reps[2];
   _optimum[0].Backs3 = reps[3];

   UInt32 len = lenEnd;
   do
    _optimum[len--].Price = kIfinityPrice;
   while (len >= 2);

   for (i = 0; i < Base.kNumRepDistances; i++)
   {
    UInt32 repLen = repLens[i];
    if (repLen < 2)
     continue;
    UInt32 price = repMatchPrice + GetPureRepPrice(i, _state, posState);
    do
    {
     UInt32 curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState);
     Optimal optimum = _optimum[repLen];
     if (curAndLenPrice < optimum.Price)
     {
      optimum.Price = curAndLenPrice;
      optimum.PosPrev = 0;
      optimum.BackPrev = i;
      optimum.Prev1IsChar = false;
     }
    }
    while (--repLen >= 2);
   }

   UInt32 normalMatchPrice = matchPrice + _isRep[_state.Index].GetPrice0();
   
   len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2);
   if (len <= lenMain)
   {
    UInt32 offs = 0;
    while (len > _matchDistances[offs])
     offs += 2;
    for (; ; len++)
    {
     UInt32 distance = _matchDistances[offs + 1];
     UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState);
     Optimal optimum = _optimum[len];
     if (curAndLenPrice < optimum.Price)
     {
      optimum.Price = curAndLenPrice;
      optimum.PosPrev = 0;
      optimum.BackPrev = distance + Base.kNumRepDistances;
      optimum.Prev1IsChar = false;
     }
     if (len == _matchDistances[offs])
     {
      offs += 2;
      if (offs == numDistancePairs)
       break;
     }
    }
   }

   UInt32 cur = 0;

   while (true)
   {
    cur++;
    if (cur == lenEnd)
     return Backward(out backRes, cur);
    UInt32 newLen;
    ReadMatchDistances(out newLen, out numDistancePairs);
    if (newLen >= _numFastBytes)
    {
     _numDistancePairs = numDistancePairs;
     _longestMatchLength = newLen;
     _longestMatchWasFound = true;
     return Backward(out backRes, cur);
    }
    position++;
    UInt32 posPrev = _optimum[cur].PosPrev;
    Base.State state;
    if (_optimum[cur].Prev1IsChar)
    {
     posPrev--;
     if (_optimum[cur].Prev2)
     {
      state = _optimum[_optimum[cur].PosPrev2].State;
      if (_optimum[cur].BackPrev2 < Base.kNumRepDistances)
       state.UpdateRep();
      else
       state.UpdateMatch();
     }
     else
      state = _optimum[posPrev].State;
     state.UpdateChar();
    }
    else
     state = _optimum[posPrev].State;
    if (posPrev == cur - 1)
    {
     if (_optimum[cur].IsShortRep())
      state.UpdateShortRep();
     else
      state.UpdateChar();
    }
    else
    {
     UInt32 pos;
     if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2)
     {
      posPrev = _optimum[cur].PosPrev2;
      pos = _optimum[cur].BackPrev2;
      state.UpdateRep();
     }
     else
     {
      pos = _optimum[cur].BackPrev;
      if (pos < Base.kNumRepDistances)
       state.UpdateRep();
      else
       state.UpdateMatch();
     }
     Optimal opt = _optimum[posPrev];
     if (pos < Base.kNumRepDistances)
     {
      if (pos == 0)
      {
       reps[0] = opt.Backs0;
       reps[1] = opt.Backs1;
       reps[2] = opt.Backs2;
       reps[3] = opt.Backs3;
      }
      else if (pos == 1)
      {
       reps[0] = opt.Backs1;
       reps[1] = opt.Backs0;
       reps[2] = opt.Backs2;
       reps[3] = opt.Backs3;
      }
      else if (pos == 2)
      {
       reps[0] = opt.Backs2;
       reps[1] = opt.Backs0;
       reps[2] = opt.Backs1;
       reps[3] = opt.Backs3;
      }
      else
      {
       reps[0] = opt.Backs3;
       reps[1] = opt.Backs0;
       reps[2] = opt.Backs1;
       reps[3] = opt.Backs2;
      }
     }
     else
     {
      reps[0] = (pos - Base.kNumRepDistances);
      reps[1] = opt.Backs0;
      reps[2] = opt.Backs1;
      reps[3] = opt.Backs2;
     }
    }
    _optimum[cur].State = state;
    _optimum[cur].Backs0 = reps[0];
    _optimum[cur].Backs1 = reps[1];
    _optimum[cur].Backs2 = reps[2];
    _optimum[cur].Backs3 = reps[3];
    UInt32 curPrice = _optimum[cur].Price;

    currentByte = _matchFinder.GetIndexByte(0 - 1);
    matchByte = _matchFinder.GetIndexByte((Int32)(0 - reps[0] - 1 - 1));

    posState = (position & _posStateMask);

    UInt32 curAnd1Price = curPrice +
     _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() +
     _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)).
     GetPrice(!state.IsCharState(), matchByte, currentByte);

    Optimal nextOptimum = _optimum[cur + 1];

    bool nextIsChar = false;
    if (curAnd1Price < nextOptimum.Price)
    {
     nextOptimum.Price = curAnd1Price;
     nextOptimum.PosPrev = cur;
     nextOptimum.MakeAsChar();
     nextIsChar = true;
    }

    matchPrice = curPrice + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
    repMatchPrice = matchPrice + _isRep[state.Index].GetPrice1();

    if (matchByte == currentByte &&
     !(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0))
    {
     UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState);
     if (shortRepPrice <= nextOptimum.Price)
     {
      nextOptimum.Price = shortRepPrice;
      nextOptimum.PosPrev = cur;
      nextOptimum.MakeAsShortRep();
      nextIsChar = true;
     }
    }

    UInt32 numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1;
    numAvailableBytesFull = Math.Min(kNumOpts - 1 - cur, numAvailableBytesFull);
    numAvailableBytes = numAvailableBytesFull;

    if (numAvailableBytes < 2)
     continue;
    if (numAvailableBytes > _numFastBytes)
     numAvailableBytes = _numFastBytes;
    if (!nextIsChar && matchByte != currentByte)
    {
     // try Literal + rep0
     UInt32 t = Math.Min(numAvailableBytesFull - 1, _numFastBytes);
     UInt32 lenTest2 = _matchFinder.GetMatchLen(0, reps[0], t);
     if (lenTest2 >= 2)
     {
      Base.State state2 = state;
      state2.UpdateChar();
      UInt32 posStateNext = (position + 1) & _posStateMask;
      UInt32 nextRepMatchPrice = curAnd1Price +
       _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1() +
       _isRep[state2.Index].GetPrice1();
      {
       UInt32 offset = cur + 1 + lenTest2;
       while (lenEnd < offset)
        _optimum[++lenEnd].Price = kIfinityPrice;
       UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(
        0, lenTest2, state2, posStateNext);
       Optimal optimum = _optimum[offset];
       if (curAndLenPrice < optimum.Price)
       {
        optimum.Price = curAndLenPrice;
        optimum.PosPrev = cur + 1;
        optimum.BackPrev = 0;
        optimum.Prev1IsChar = true;
        optimum.Prev2 = false;
       }
      }
     }
    }

    UInt32 startLen = 2; // speed optimization

    for (UInt32 repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++)
    {
     UInt32 lenTest = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes);
     if (lenTest < 2)
      continue;
     UInt32 lenTestTemp = lenTest;
     do
     {
      while (lenEnd < cur + lenTest)
       _optimum[++lenEnd].Price = kIfinityPrice;
      UInt32 curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState);
      Optimal optimum = _optimum[cur + lenTest];
      if (curAndLenPrice < optimum.Price)
      {
       optimum.Price = curAndLenPrice;
       optimum.PosPrev = cur;
       optimum.BackPrev = repIndex;
       optimum.Prev1IsChar = false;
      }
     }
     while(--lenTest >= 2);
     lenTest = lenTestTemp;

     if (repIndex == 0)
      startLen = lenTest + 1;

     // if (_maxMode)
     if (lenTest < numAvailableBytesFull)
     {
      UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
      UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, reps[repIndex], t);
      if (lenTest2 >= 2)
      {
       Base.State state2 = state;
       state2.UpdateRep();
       UInt32 posStateNext = (position + lenTest) & _posStateMask;
       UInt32 curAndLenCharPrice =
         repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) +
         _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() +
         _literalEncoder.GetSubCoder(position + lenTest,
         _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)).GetPrice(true,
         _matchFinder.GetIndexByte((Int32)((Int32)lenTest - 1 - (Int32)(reps[repIndex] + 1))),
         _matchFinder.GetIndexByte((Int32)lenTest - 1));
       state2.UpdateChar();
       posStateNext = (position + lenTest + 1) & _posStateMask;
       UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1();
       UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1();
       
       // for(; lenTest2 >= 2; lenTest2--)
       {
        UInt32 offset = lenTest + 1 + lenTest2;
        while(lenEnd < cur + offset)
         _optimum[++lenEnd].Price = kIfinityPrice;
        UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext);
        Optimal optimum = _optimum[cur + offset];
        if (curAndLenPrice < optimum.Price)
        {
         optimum.Price = curAndLenPrice;
         optimum.PosPrev = cur + lenTest + 1;
         optimum.BackPrev = 0;
         optimum.Prev1IsChar = true;
         optimum.Prev2 = true;
         optimum.PosPrev2 = cur;
         optimum.BackPrev2 = repIndex;
        }
       }
      }
     }
    }

    if (newLen > numAvailableBytes)
    {
     newLen = numAvailableBytes;
     for (numDistancePairs = 0; newLen > _matchDistances[numDistancePairs]; numDistancePairs += 2) ;
     _matchDistances[numDistancePairs] = newLen;
     numDistancePairs += 2;
    }
    if (newLen >= startLen)
    {
     normalMatchPrice = matchPrice + _isRep[state.Index].GetPrice0();
     while (lenEnd < cur + newLen)
      _optimum[++lenEnd].Price = kIfinityPrice;

     UInt32 offs = 0;
     while (startLen > _matchDistances[offs])
      offs += 2;

     for (UInt32 lenTest = startLen; ; lenTest++)
     {
      UInt32 curBack = _matchDistances[offs + 1];
      UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState);
      Optimal optimum = _optimum[cur + lenTest];
      if (curAndLenPrice < optimum.Price)
      {
       optimum.Price = curAndLenPrice;
       optimum.PosPrev = cur;
       optimum.BackPrev = curBack + Base.kNumRepDistances;
       optimum.Prev1IsChar = false;
      }

      if (lenTest == _matchDistances[offs])
      {
       if (lenTest < numAvailableBytesFull)
       {
        UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
        UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, curBack, t);
        if (lenTest2 >= 2)
        {
         Base.State state2 = state;
         state2.UpdateMatch();
         UInt32 posStateNext = (position + lenTest) & _posStateMask;
         UInt32 curAndLenCharPrice = curAndLenPrice +
          _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() +
          _literalEncoder.GetSubCoder(position + lenTest,
          _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)).
          GetPrice(true,
          _matchFinder.GetIndexByte((Int32)lenTest - (Int32)(curBack + 1) - 1),
          _matchFinder.GetIndexByte((Int32)lenTest - 1));
         state2.UpdateChar();
         posStateNext = (position + lenTest + 1) & _posStateMask;
         UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1();
         UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1();

         UInt32 offset = lenTest + 1 + lenTest2;
         while (lenEnd < cur + offset)
          _optimum[++lenEnd].Price = kIfinityPrice;
         curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext);
         optimum = _optimum[cur + offset];
         if (curAndLenPrice < optimum.Price)
         {
          optimum.Price = curAndLenPrice;
          optimum.PosPrev = cur + lenTest + 1;
          optimum.BackPrev = 0;
          optimum.Prev1IsChar = true;
          optimum.Prev2 = true;
          optimum.PosPrev2 = cur;
          optimum.BackPrev2 = curBack + Base.kNumRepDistances;
         }
        }
       }
       offs += 2;
       if (offs == numDistancePairs)
        break;
      }
     }
    }
   }
  }

  bool ChangePair(UInt32 smallDist, UInt32 bigDist)
  {
   const int kDif = 7;
   return (smallDist < ((UInt32)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif));
  }

  void WriteEndMarker(UInt32 posState)
  {
   if (!_writeEndMark)
    return;

   _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 1);
   _isRep[_state.Index].Encode(_rangeEncoder, 0);
   _state.UpdateMatch();
   UInt32 len = Base.kMatchMinLen;
   _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
   UInt32 posSlot = (1 << Base.kNumPosSlotBits) - 1;
   UInt32 lenToPosState = Base.GetLenToPosState(len);
   _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
   int footerBits = 30;
   UInt32 posReduced = (((UInt32)1) << footerBits) - 1;
   _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits);
   _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask);
  }

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

  public void CodeOneBlock(out Int64 inSize, out Int64 outSize, out bool finished)
  {
   inSize = 0;
   outSize = 0;
   finished = true;

   if (_inStream != null)
   {
    _matchFinder.SetStream(_inStream);
    _matchFinder.Init();
    _needReleaseMFStream = true;
    _inStream = null;
    if (_trainSize > 0)
     _matchFinder.Skip(_trainSize);
   }

   if (_finished)
    return;
   _finished = true;


   Int64 progressPosValuePrev = nowPos64;
   if (nowPos64 == 0)
   {
    if (_matchFinder.GetNumAvailableBytes() == 0)
    {
     Flush((UInt32)nowPos64);
     return;
    }
    UInt32 len, numDistancePairs; // it's not used
    ReadMatchDistances(out len, out numDistancePairs);
    UInt32 posState = (UInt32)(nowPos64) & _posStateMask;
    _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 0);
    _state.UpdateChar();
    Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset));
    _literalEncoder.GetSubCoder((UInt32)(nowPos64), _previousByte).Encode(_rangeEncoder, curByte);
    _previousByte = curByte;
    _additionalOffset--;
    nowPos64++;
   }
   if (_matchFinder.GetNumAvailableBytes() == 0)
   {
    Flush((UInt32)nowPos64);
    return;
   }
   while (true)
   {
    UInt32 pos;
    UInt32 len = GetOptimum((UInt32)nowPos64, out pos);
    
    UInt32 posState = ((UInt32)nowPos64) & _posStateMask;
    UInt32 complexState = (_state.Index << Base.kNumPosStatesBitsMax) + posState;
    if (len == 1 && pos == 0xFFFFFFFF)
    {
     _isMatch[complexState].Encode(_rangeEncoder, 0);
     Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset));
     LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)nowPos64, _previousByte);
     if (!_state.IsCharState())
     {
      Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - _additionalOffset));
      subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte);
     }
     else
      subCoder.Encode(_rangeEncoder, curByte);
     _previousByte = curByte;
     _state.UpdateChar();
    }
    else
    {
     _isMatch[complexState].Encode(_rangeEncoder, 1);
     if (pos < Base.kNumRepDistances)
     {
      _isRep[_state.Index].Encode(_rangeEncoder, 1);
      if (pos == 0)
      {
       _isRepG0[_state.Index].Encode(_rangeEncoder, 0);
       if (len == 1)
        _isRep0Long[complexState].Encode(_rangeEncoder, 0);
       else
        _isRep0Long[complexState].Encode(_rangeEncoder, 1);
      }
      else
      {
       _isRepG0[_state.Index].Encode(_rangeEncoder, 1);
       if (pos == 1)
        _isRepG1[_state.Index].Encode(_rangeEncoder, 0);
       else
       {
        _isRepG1[_state.Index].Encode(_rangeEncoder, 1);
        _isRepG2[_state.Index].Encode(_rangeEncoder, pos - 2);
       }
      }
      if (len == 1)
       _state.UpdateShortRep();
      else
      {
       _repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
       _state.UpdateRep();
      }
      UInt32 distance = _repDistances[pos];
      if (pos != 0)
      {
       for (UInt32 i = pos; i >= 1; i--)
        _repDistances[i] = _repDistances[i - 1];
       _repDistances[0] = distance;
      }
     }
     else
     {
      _isRep[_state.Index].Encode(_rangeEncoder, 0);
      _state.UpdateMatch();
      _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
      pos -= Base.kNumRepDistances;
      UInt32 posSlot = GetPosSlot(pos);
      UInt32 lenToPosState = Base.GetLenToPosState(len);
      _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);

      if (posSlot >= Base.kStartPosModelIndex)
      {
       int footerBits = (int)((posSlot >> 1) - 1);
       UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits);
       UInt32 posReduced = pos - baseVal;

       if (posSlot < Base.kEndPosModelIndex)
        RangeCoder.BitTreeEncoder.ReverseEncode(_posEncoders,
          baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced);
       else
       {
        _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits);
        _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask);
        _alignPriceCount++;
       }
      }
      UInt32 distance = pos;
      for (UInt32 i = Base.kNumRepDistances - 1; i >= 1; i--)
       _repDistances[i] = _repDistances[i - 1];
      _repDistances[0] = distance;
      _matchPriceCount++;
     }
     _previousByte = _matchFinder.GetIndexByte((Int32)(len - 1 - _additionalOffset));
    }
    _additionalOffset -= len;
    nowPos64 += len;
    if (_additionalOffset == 0)
    {
     // if (!_fastMode)
     if (_matchPriceCount >= (1 << 7))
      FillDistancesPrices();
     if (_alignPriceCount >= Base.kAlignTableSize)
      FillAlignPrices();
     inSize = nowPos64;
     outSize = _rangeEncoder.GetProcessedSizeAdd();
     if (_matchFinder.GetNumAvailableBytes() == 0)
     {
      Flush((UInt32)nowPos64);
      return;
     }

     if (nowPos64 - progressPosValuePrev >= (1 << 12))
     {
      _finished = false;
      finished = false;
      return;
     }
    }
   }
  }

  void ReleaseMFStream()
  {
   if (_matchFinder != null && _needReleaseMFStream)
   {
    _matchFinder.ReleaseStream();
    _needReleaseMFStream = false;
   }
  }

  void SetOutStream(System.IO.Stream outStream) { _rangeEncoder.SetStream(outStream); }
  void ReleaseOutStream() { _rangeEncoder.ReleaseStream(); }

  void ReleaseStreams()
  {
   ReleaseMFStream();
   ReleaseOutStream();
  }

  void SetStreams(System.IO.Stream inStream, System.IO.Stream outStream,
    Int64 inSize, Int64 outSize)
  {
   _inStream = inStream;
   _finished = false;
   Create();
   SetOutStream(outStream);
   Init();

   // if (!_fastMode)
   {
    FillDistancesPrices();
    FillAlignPrices();
   }

   _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen);
   _lenEncoder.UpdateTables((UInt32)1 << _posStateBits);
   _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen);
   _repMatchLenEncoder.UpdateTables((UInt32)1 << _posStateBits);

   nowPos64 = 0;
  }


  public void Code(System.IO.Stream inStream, System.IO.Stream outStream,
   Int64 inSize, Int64 outSize, ICodeProgress progress)
  {
   _needReleaseMFStream = false;
   try
   {
    SetStreams(inStream, outStream, inSize, outSize);
    while (true)
    {
     Int64 processedInSize;
     Int64 processedOutSize;
     bool finished;
     CodeOneBlock(out processedInSize, out processedOutSize, out finished);
     if (finished)
      return;
     if (progress != null)
     {
      progress.SetProgress(processedInSize, processedOutSize);
     }
    }
   }
   finally
   {
    ReleaseStreams();
   }
  }

  const int kPropSize = 5;
  Byte[] properties = new Byte[kPropSize];

  public void WriteCoderProperties(System.IO.Stream outStream)
  {
   properties[0] = (Byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits);
   for (int i = 0; i < 4; i++)
    properties[1 + i] = (Byte)((_dictionarySize >> (8 * i)) & 0xFF);
   outStream.Write(properties, 0, kPropSize);
  }
  
  UInt32[] tempPrices = new UInt32[Base.kNumFullDistances];
  UInt32 _matchPriceCount;

  void FillDistancesPrices()
  {
   for (UInt32 i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++)
   {
    UInt32 posSlot = GetPosSlot(i);
    int footerBits = (int)((posSlot >> 1) - 1);
    UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits);
    tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders,
     baseVal - posSlot - 1, footerBits, i - baseVal);
   }

   for (UInt32 lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++)
   {
    UInt32 posSlot;
    RangeCoder.BitTreeEncoder encoder = _posSlotEncoder[lenToPosState];
   
    UInt32 st = (lenToPosState << Base.kNumPosSlotBits);
    for (posSlot = 0; posSlot < _distTableSize; posSlot++)
     _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot);
    for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++)
     _posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << RangeCoder.BitEncoder.kNumBitPriceShiftBits);

    UInt32 st2 = lenToPosState * Base.kNumFullDistances;
    UInt32 i;
    for (i = 0; i < Base.kStartPosModelIndex; i++)
     _distancesPrices[st2 + i] = _posSlotPrices[st + i];
    for (; i < Base.kNumFullDistances; i++)
     _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i];
   }
   _matchPriceCount = 0;
  }

  void FillAlignPrices()
  {
   for (UInt32 i = 0; i < Base.kAlignTableSize; i++)
    _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i);
   _alignPriceCount = 0;
  }


  static string[] kMatchFinderIDs =
  {
   "BT2",
   "BT4",
  };

  static int FindMatchFinder(string s)
  {
   for (int m = 0; m < kMatchFinderIDs.Length; m++)
    if (s == kMatchFinderIDs[m])
     return m;
   return -1;
  }
 
  public void SetCoderProperties(CoderPropID[] propIDs, object[] properties)
  {
   for (UInt32 i = 0; i < properties.Length; i++)
   {
    object prop = properties[i];
    switch (propIDs[i])
    {
     case CoderPropID.NumFastBytes:
     {
      if (!(prop is Int32))
       throw new InvalidParamException();
      Int32 numFastBytes = (Int32)prop;
      if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen)
       throw new InvalidParamException();
      _numFastBytes = (UInt32)numFastBytes;
      break;
     }
     case CoderPropID.Algorithm:
     {
      /*
      if (!(prop is Int32))
       throw new InvalidParamException();
      Int32 maximize = (Int32)prop;
      _fastMode = (maximize == 0);
      _maxMode = (maximize >= 2);
      */
      break;
     }
     case CoderPropID.MatchFinder:
     {
      if (!(prop is String))
       throw new InvalidParamException();
      EMatchFinderType matchFinderIndexPrev = _matchFinderType;
      int m = FindMatchFinder(((string)prop).ToUpper());
      if (m < 0)
       throw new InvalidParamException();
      _matchFinderType = (EMatchFinderType)m;
      if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType)
       {
       _dictionarySizePrev = 0xFFFFFFFF;
       _matchFinder = null;
       }
      break;
     }
     case CoderPropID.DictionarySize:
     {
      const int kDicLogSizeMaxCompress = 30;
      if (!(prop is Int32))
       throw new InvalidParamException(); ;
      Int32 dictionarySize = (Int32)prop;
      if (dictionarySize < (UInt32)(1 << Base.kDicLogSizeMin) ||
       dictionarySize > (UInt32)(1 << kDicLogSizeMaxCompress))
       throw new InvalidParamException();
      _dictionarySize = (UInt32)dictionarySize;
      int dicLogSize;
      for (dicLogSize = 0; dicLogSize < (UInt32)kDicLogSizeMaxCompress; dicLogSize++)
       if (dictionarySize <= ((UInt32)(1) << dicLogSize))
        break;
      _distTableSize = (UInt32)dicLogSize * 2;
      break;
     }
     case CoderPropID.PosStateBits:
     {
      if (!(prop is Int32))
       throw new InvalidParamException();
      Int32 v = (Int32)prop;
      if (v < 0 || v > (UInt32)Base.kNumPosStatesBitsEncodingMax)
       throw new InvalidParamException();
      _posStateBits = (int)v;
      _posStateMask = (((UInt32)1) << (int)_posStateBits) - 1;
      break;
     }
     case CoderPropID.LitPosBits:
     {
      if (!(prop is Int32))
       throw new InvalidParamException();
      Int32 v = (Int32)prop;
      if (v < 0 || v > (UInt32)Base.kNumLitPosStatesBitsEncodingMax)
       throw new InvalidParamException();
      _numLiteralPosStateBits = (int)v;
      break;
     }
     case CoderPropID.LitContextBits:
     {
      if (!(prop is Int32))
       throw new InvalidParamException();
      Int32 v = (Int32)prop;
      if (v < 0 || v > (UInt32)Base.kNumLitContextBitsMax)
       throw new InvalidParamException(); ;
      _numLiteralContextBits = (int)v;
      break;
     }
     case CoderPropID.EndMarker:
     {
      if (!(prop is Boolean))
       throw new InvalidParamException();
      SetWriteEndMarkerMode((Boolean)prop);
      break;
     }
     default:
      throw new InvalidParamException();
    }
   }
  }

  uint _trainSize = 0;
  public void SetTrainSize(uint trainSize)
  {
   _trainSize = trainSize;
  }
  
 }
}
namespace SevenZip
{
 /// <summary>
 /// LZMA Benchmark
 /// </summary>
 internal abstract class LzmaBench
 {
  const UInt32 kAdditionalSize = (6 << 20);
  const UInt32 kCompressedAdditionalSize = (1 << 10);
  const UInt32 kMaxLzmaPropSize = 10;

  class CRandomGenerator
  {
   UInt32 A1;
   UInt32 A2;
   public CRandomGenerator() { Init(); }
   public void Init() { A1 = 362436069; A2 = 521288629; }
   public UInt32 GetRnd()
   {
    return
     ((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) ^
     ((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)));
   }
  };

  class CBitRandomGenerator
  {
   CRandomGenerator RG = new CRandomGenerator();
   UInt32 Value;
   int NumBits;
   public void Init()
   {
    Value = 0;
    NumBits = 0;
   }
   public UInt32 GetRnd(int numBits)
   {
    UInt32 result;
    if (NumBits > numBits)
    {
     result = Value & (((UInt32)1 << numBits) - 1);
     Value >>= numBits;
     NumBits -= numBits;
     return result;
    }
    numBits -= NumBits;
    result = (Value << numBits);
    Value = RG.GetRnd();
    result |= Value & (((UInt32)1 << numBits) - 1);
    Value >>= numBits;
    NumBits = 32 - numBits;
    return result;
   }
  };

  class CBenchRandomGenerator
  {
   CBitRandomGenerator RG = new CBitRandomGenerator();
   UInt32 Pos;
   UInt32 Rep0;
   
   public UInt32 BufferSize;
   public Byte[] Buffer = null;

   public CBenchRandomGenerator() { }

   public void Set(UInt32 bufferSize)
   {
    Buffer = new Byte[bufferSize];
    Pos = 0;
    BufferSize = bufferSize;
   }
   UInt32 GetRndBit() { return RG.GetRnd(1); }
   UInt32 GetLogRandBits(int numBits)
   {
    UInt32 len = RG.GetRnd(numBits);
    return RG.GetRnd((int)len);
   }
   UInt32 GetOffset()
   {
    if (GetRndBit() == 0)
     return GetLogRandBits(4);
    return (GetLogRandBits(4) << 10) | RG.GetRnd(10);
   }
   UInt32 GetLen1() { return RG.GetRnd(1 + (int)RG.GetRnd(2)); }
   UInt32 GetLen2() { return RG.GetRnd(2 + (int)RG.GetRnd(2)); }
   public void Generate()
   {
    RG.Init();
    Rep0 = 1;
    while (Pos < BufferSize)
    {
     if (GetRndBit() == 0 || Pos < 1)
      Buffer[Pos++] = (Byte)RG.GetRnd(8);
     else
     {
      UInt32 len;
      if (RG.GetRnd(3) == 0)
       len = 1 + GetLen1();
      else
      {
       do
        Rep0 = GetOffset();
       while (Rep0 >= Pos);
       Rep0++;
       len = 2 + GetLen2();
      }
      for (UInt32 i = 0; i < len && Pos < BufferSize; i++, Pos++)
       Buffer[Pos] = Buffer[Pos - Rep0];
     }
    }
   }
  };

  class CrcOutStream : System.IO.Stream
  {
   public CRC CRC = new CRC();
   public void Init() { CRC.Init(); }
   public UInt32 GetDigest() { return CRC.GetDigest(); }

   public override bool CanRead { get { return false; } }
   public override bool CanSeek { get { return false; } }
   public override bool CanWrite { get { return true; } }
   public override Int64 Length { get { return 0; } }
   public override Int64 Position { get { return 0; } set { } }
   public override void Flush() { }
   public override long Seek(long offset, SeekOrigin origin) { return 0; }
   public override void SetLength(long value) { }
   public override int Read(byte[] buffer, int offset, int count) { return 0; }

   public override void WriteByte(byte b)
   {
    CRC.UpdateByte(b);
   }
   public override void Write(byte[] buffer, int offset, int count)
   {
    CRC.Update(buffer, (uint)offset, (uint)count);
   }
  };

  class CProgressInfo : ICodeProgress
  {
   public Int64 ApprovedStart;
   public Int64 InSize;
   public System.DateTime Time;
   public void Init() { InSize = 0; }
   public void SetProgress(Int64 inSize, Int64 outSize)
   {
    if (inSize >= ApprovedStart && InSize == 0)
    {
     Time = DateTime.UtcNow;
     InSize = inSize;
    }
   }
  }
  const int kSubBits = 8;

  static UInt32 GetLogSize(UInt32 size)
  {
   for (int i = kSubBits; i < 32; i++)
    for (UInt32 j = 0; j < (1 << kSubBits); j++)
     if (size <= (((UInt32)1) << i) + (j << (i - kSubBits)))
      return (UInt32)(i << kSubBits) + j;
   return (32 << kSubBits);
  }

  static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime)
  {
   UInt64 freq = TimeSpan.TicksPerSecond;
   UInt64 elTime = elapsedTime;
   while (freq > 1000000)
   {
    freq >>= 1;
    elTime >>= 1;
   }
   if (elTime == 0)
    elTime = 1;
   return value * freq / elTime;
  }

  static UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 size)
  {
   UInt64 t = GetLogSize(dictionarySize) - (18 << kSubBits);
   UInt64 numCommandsForOne = 1060 + ((t * t * 10) >> (2 * kSubBits));
   UInt64 numCommands = (UInt64)(size) * numCommandsForOne;
   return MyMultDiv64(numCommands, elapsedTime);
  }

  static UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 outSize, UInt64 inSize)
  {
   UInt64 numCommands = inSize * 220 + outSize * 20;
   return MyMultDiv64(numCommands, elapsedTime);
  }

  static UInt64 GetTotalRating(
   UInt32 dictionarySize,
   UInt64 elapsedTimeEn, UInt64 sizeEn,
   UInt64 elapsedTimeDe,
   UInt64 inSizeDe, UInt64 outSizeDe)
  {
   return (GetCompressRating(dictionarySize, elapsedTimeEn, sizeEn) +
    GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2;
  }

  static void PrintValue(UInt64 v)
  {
   string s = v.ToString();
   for (int i = 0; i + s.Length < 6; i++)
    System.Console.Write(" ");
   System.Console.Write(s);
  }

  static void PrintRating(UInt64 rating)
  {
   PrintValue(rating / 1000000);
   System.Console.Write(" MIPS");
  }

  static void PrintResults(
   UInt32 dictionarySize,
   UInt64 elapsedTime,
   UInt64 size,
   bool decompressMode, UInt64 secondSize)
  {
   UInt64 speed = MyMultDiv64(size, elapsedTime);
   PrintValue(speed / 1024);
   System.Console.Write(" KB/s  ");
   UInt64 rating;
   if (decompressMode)
    rating = GetDecompressRating(elapsedTime, size, secondSize);
   else
    rating = GetCompressRating(dictionarySize, elapsedTime, size);
   PrintRating(rating);
  }

  static public int LzmaBenchmark(Int32 numIterations, UInt32 dictionarySize)
  {
   if (numIterations <= 0)
    return 0;
   if (dictionarySize < (1 << 18))
   {
    System.Console.WriteLine("\nError: dictionary size for benchmark must be >= 19 (512 KB)");
    return 1;
   }
   System.Console.Write("\n       Compressing                Decompressing\n\n");

   Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder();
   Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder();


   CoderPropID[] propIDs =
   {
    CoderPropID.DictionarySize,
   };
   object[] properties =
   {
    (Int32)(dictionarySize),
   };

   UInt32 kBufferSize = dictionarySize + kAdditionalSize;
   UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;

   encoder.SetCoderProperties(propIDs, properties);
   System.IO.MemoryStream propStream = new System.IO.MemoryStream();
   encoder.WriteCoderProperties(propStream);
   byte[] propArray = propStream.ToArray();

   CBenchRandomGenerator rg = new CBenchRandomGenerator();

   rg.Set(kBufferSize);
   rg.Generate();
   CRC crc = new CRC();
   crc.Init();
   crc.Update(rg.Buffer, 0, rg.BufferSize);

   CProgressInfo progressInfo = new CProgressInfo();
   progressInfo.ApprovedStart = dictionarySize;

   UInt64 totalBenchSize = 0;
   UInt64 totalEncodeTime = 0;
   UInt64 totalDecodeTime = 0;
   UInt64 totalCompressedSize = 0;

   MemoryStream inStream = new MemoryStream(rg.Buffer, 0, (int)rg.BufferSize);
   MemoryStream compressedStream = new MemoryStream((int)kCompressedBufferSize);
   CrcOutStream crcOutStream = new CrcOutStream();
   for (Int32 i = 0; i < numIterations; i++)
   {
    progressInfo.Init();
    inStream.Seek(0, SeekOrigin.Begin);
    compressedStream.Seek(0, SeekOrigin.Begin);
    encoder.Code(inStream, compressedStream, -1, -1, progressInfo);
    TimeSpan sp2 = DateTime.UtcNow - progressInfo.Time;
    UInt64 encodeTime = (UInt64)sp2.Ticks;

    long compressedSize = compressedStream.Position;
    if (progressInfo.InSize == 0)
     throw (new Exception("Internal ERROR 1282"));

    UInt64 decodeTime = 0;
    for (int j = 0; j < 2; j++)
    {
     compressedStream.Seek(0, SeekOrigin.Begin);
     crcOutStream.Init();

     decoder.SetDecoderProperties(propArray);
     UInt64 outSize = kBufferSize;
     System.DateTime startTime = DateTime.UtcNow;
     decoder.Code(compressedStream, crcOutStream, 0, (Int64)outSize, null);
     TimeSpan sp = (DateTime.UtcNow - startTime);
     decodeTime = (ulong)sp.Ticks;
     if (crcOutStream.GetDigest() != crc.GetDigest())
      throw (new Exception("CRC Error"));
    }
    UInt64 benchSize = kBufferSize - (UInt64)progressInfo.InSize;
    PrintResults(dictionarySize, encodeTime, benchSize, false, 0);
    System.Console.Write("     ");
    PrintResults(dictionarySize, decodeTime, kBufferSize, true, (ulong)compressedSize);
    System.Console.WriteLine();

    totalBenchSize += benchSize;
    totalEncodeTime += encodeTime;
    totalDecodeTime += decodeTime;
    totalCompressedSize += (ulong)compressedSize;
   }
   System.Console.WriteLine("---------------------------------------------------");
   PrintResults(dictionarySize, totalEncodeTime, totalBenchSize, false, 0);
   System.Console.Write("     ");
   PrintResults(dictionarySize, totalDecodeTime,
     kBufferSize * (UInt64)numIterations, true, totalCompressedSize);
   System.Console.WriteLine("    Average");
   return 0;
  }
 }
}
namespace SevenZip
{
 using CommandLineParser;
 
 public class CDoubleStream: Stream
 {
  public System.IO.Stream s1;
  public System.IO.Stream s2;
  public int fileIndex;
  public long skipSize;
  
  public override bool CanRead { get { return true; }}
  public override bool CanWrite { get { return false; }}
  public override bool CanSeek { get { return false; }}
  public override long Length { get { return s1.Length + s2.Length - skipSize; } }
  public override long Position
  {
   get { return 0; }
   set { }
  }
  public override void Flush() { }
  public override int Read(byte[] buffer, int offset, int count)
  {
   int numTotal = 0;
   while (count > 0)
   {
    if (fileIndex == 0)
    {
     int num = s1.Read(buffer, offset, count);
     offset += num;
     count -= num;
     numTotal += num;
     if (num == 0)
      fileIndex++;
    }
    if (fileIndex == 1)
    {
     numTotal += s2.Read(buffer, offset, count);
     return numTotal;
    }
   }
   return numTotal;
  }
  public override void Write(byte[] buffer, int offset, int count)
  {
   throw (new Exception("can't Write"));
  }
  public override long Seek(long offset, System.IO.SeekOrigin origin)
  {
   throw (new Exception("can't Seek"));
  }
  public override void SetLength(long value)
  {
   throw (new Exception("can't SetLength"));
  }
 }
 
 class LzmaAlone
 {
  enum Key
  {
   Help1 = 0,
   Help2,
   Mode,
   Dictionary,
   FastBytes,
   LitContext,
   LitPos,
   PosBits,
   MatchFinder,
   EOS,
   StdIn,
   StdOut,
   Train
  };

  static void PrintHelp()
  {
   System.Console.WriteLine("\nUsage:  LZMA <e|d> [<switches>...] inputFile outputFile\n" +
    "  e: encode file\n" +
    "  d: decode file\n" +
    "  b: Benchmark\n" +
    "<Switches>\n" +
    // "  -a{N}:  set compression mode - [0, 1], default: 1 (max)\n" +
    "  -d{N}:  set dictionary - [0, 29], default: 23 (8MB)\n" +
    "  -fb{N}: set number of fast bytes - [5, 273], default: 128\n" +
    "  -lc{N}: set number of literal context bits - [0, 8], default: 3\n" +
    "  -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" +
    "  -pb{N}: set number of pos bits - [0, 4], default: 2\n" +
    "  -mf{MF_ID}: set Match Finder: [bt2, bt4], default: bt4\n" +
    "  -eos:   write End Of Stream marker\n"
    // + "  -si:    read data from stdin\n"
    // + "  -so:    write data to stdout\n"
    );
  }

  static bool GetNumber(string s, out Int32 v)
  {
   v = 0;
   for (int i = 0; i < s.Length; i++)
   {
    char c = s[i];
    if (c < '0' || c > '9')
     return false;
    v *= 10;
    v += (Int32)(c - '0');
   }
   return true;
  }

  static int IncorrectCommand()
  {
   throw (new Exception("Command line error"));
   // System.Console.WriteLine("\nCommand line error\n");
   // return 1;
  }
  static int Main2(string[] args)
  {
   System.Console.WriteLine("\nLZMA# 4.61  2008-11-23\n");

   if (args.Length == 0)
   {
    PrintHelp();
    return 0;
   }

   SwitchForm[] kSwitchForms = new SwitchForm[13];
   int sw = 0;
   kSwitchForms[sw++] = new SwitchForm("?", SwitchType.Simple, false);
   kSwitchForms[sw++] = new SwitchForm("H", SwitchType.Simple, false);
   kSwitchForms[sw++] = new SwitchForm("A", SwitchType.UnLimitedPostString, false, 1);
   kSwitchForms[sw++] = new SwitchForm("D", SwitchType.UnLimitedPostString, false, 1);
   kSwitchForms[sw++] = new SwitchForm("FB", SwitchType.UnLimitedPostString, false, 1);
   kSwitchForms[sw++] = new SwitchForm("LC", SwitchType.UnLimitedPostString, false, 1);
   kSwitchForms[sw++] = new SwitchForm("LP", SwitchType.UnLimitedPostString, false, 1);
   kSwitchForms[sw++] = new SwitchForm("PB", SwitchType.UnLimitedPostString, false, 1);
   kSwitchForms[sw++] = new SwitchForm("MF", SwitchType.UnLimitedPostString, false, 1);
   kSwitchForms[sw++] = new SwitchForm("EOS", SwitchType.Simple, false);
   kSwitchForms[sw++] = new SwitchForm("SI", SwitchType.Simple, false);
   kSwitchForms[sw++] = new SwitchForm("SO", SwitchType.Simple, false);
   kSwitchForms[sw++] = new SwitchForm("T", SwitchType.UnLimitedPostString, false, 1);


   Parser parser = new Parser(sw);
   try
   {
    parser.ParseStrings(kSwitchForms, args);
   }
   catch
   {
    return IncorrectCommand();
   }

   if (parser[(int)Key.Help1].ThereIs || parser[(int)Key.Help2].ThereIs)
   {
    PrintHelp();
    return 0;
   }

   System.Collections.ArrayList nonSwitchStrings = parser.NonSwitchStrings;

   int paramIndex = 0;
   if (paramIndex >= nonSwitchStrings.Count)
    return IncorrectCommand();
   string command = (string)nonSwitchStrings[paramIndex++];
   command = command.ToLower();

   bool dictionaryIsDefined = false;
   Int32 dictionary = 1 << 21;
   if (parser[(int)Key.Dictionary].ThereIs)
   {
    Int32 dicLog;
    if (!GetNumber((string)parser[(int)Key.Dictionary].PostStrings[0], out dicLog))
     IncorrectCommand();
    dictionary = (Int32)1 << dicLog;
    dictionaryIsDefined = true;
   }
   string mf = "bt4";
   if (parser[(int)Key.MatchFinder].ThereIs)
    mf = (string)parser[(int)Key.MatchFinder].PostStrings[0];
   mf = mf.ToLower();

   if (command == "b")
   {
    const Int32 kNumDefaultItereations = 10;
    Int32 numIterations = kNumDefaultItereations;
    if (paramIndex < nonSwitchStrings.Count)
     if (!GetNumber((string)nonSwitchStrings[paramIndex++], out numIterations))
      numIterations = kNumDefaultItereations;
    return LzmaBench.LzmaBenchmark(numIterations, (UInt32)dictionary);
   }

   string train = "";
   if (parser[(int)Key.Train].ThereIs)
    train = (string)parser[(int)Key.Train].PostStrings[0];

   bool encodeMode = false;
   if (command == "e")
    encodeMode = true;
   else if (command == "d")
    encodeMode = false;
   else
    IncorrectCommand();

   bool stdInMode = parser[(int)Key.StdIn].ThereIs;
   bool stdOutMode = parser[(int)Key.StdOut].ThereIs;

   Stream inStream = null;
   if (stdInMode)
   {
    throw (new Exception("Not implemeted"));
   }
   else
   {
    if (paramIndex >= nonSwitchStrings.Count)
     IncorrectCommand();
    string inputName = (string)nonSwitchStrings[paramIndex++];
    inStream = new FileStream(inputName, FileMode.Open, FileAccess.Read);
   }

   FileStream outStream = null;
   if (stdOutMode)
   {
    throw (new Exception("Not implemeted"));
   }
   else
   {
    if (paramIndex >= nonSwitchStrings.Count)
     IncorrectCommand();
    string outputName = (string)nonSwitchStrings[paramIndex++];
    outStream = new FileStream(outputName, FileMode.Create, FileAccess.Write);
   }

   FileStream trainStream = null;
   if (train.Length != 0)
    trainStream = new FileStream(train, FileMode.Open, FileAccess.Read);

   if (encodeMode)
   {
    if (!dictionaryIsDefined)
     dictionary = 1 << 23;

    Int32 posStateBits = 2;
    Int32 litContextBits = 3; // for normal files
    // UInt32 litContextBits = 0; // for 32-bit data
    Int32 litPosBits = 0;
    // UInt32 litPosBits = 2; // for 32-bit data
    Int32 algorithm = 2;
    Int32 numFastBytes = 128;

    bool eos = parser[(int)Key.EOS].ThereIs || stdInMode;

    if (parser[(int)Key.Mode].ThereIs)
     if (!GetNumber((string)parser[(int)Key.Mode].PostStrings[0], out algorithm))
      IncorrectCommand();

    if (parser[(int)Key.FastBytes].ThereIs)
     if (!GetNumber((string)parser[(int)Key.FastBytes].PostStrings[0], out numFastBytes))
      IncorrectCommand();
    if (parser[(int)Key.LitContext].ThereIs)
     if (!GetNumber((string)parser[(int)Key.LitContext].PostStrings[0], out litContextBits))
      IncorrectCommand();
    if (parser[(int)Key.LitPos].ThereIs)
     if (!GetNumber((string)parser[(int)Key.LitPos].PostStrings[0], out litPosBits))
      IncorrectCommand();
    if (parser[(int)Key.PosBits].ThereIs)
     if (!GetNumber((string)parser[(int)Key.PosBits].PostStrings[0], out posStateBits))
      IncorrectCommand();

    CoderPropID[] propIDs =
    {
     CoderPropID.DictionarySize,
     CoderPropID.PosStateBits,
     CoderPropID.LitContextBits,
     CoderPropID.LitPosBits,
     CoderPropID.Algorithm,
     CoderPropID.NumFastBytes,
     CoderPropID.MatchFinder,
     CoderPropID.EndMarker
    };
    object[] properties =
    {
     (Int32)(dictionary),
     (Int32)(posStateBits),
     (Int32)(litContextBits),
     (Int32)(litPosBits),
     (Int32)(algorithm),
     (Int32)(numFastBytes),
     mf,
     eos
    };

    Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder();
    encoder.SetCoderProperties(propIDs, properties);
    encoder.WriteCoderProperties(outStream);
    Int64 fileSize;
    if (eos || stdInMode)
     fileSize = -1;
    else
     fileSize = inStream.Length;
    for (int i = 0; i < 8; i++)
     outStream.WriteByte((Byte)(fileSize >> (8 * i)));
    if (trainStream != null)
    {
     CDoubleStream doubleStream = new CDoubleStream();
     doubleStream.s1 = trainStream;
     doubleStream.s2 = inStream;
     doubleStream.fileIndex = 0;
     inStream = doubleStream;
     long trainFileSize = trainStream.Length;
     doubleStream.skipSize = 0;
     if (trainFileSize > dictionary)
      doubleStream.skipSize = trainFileSize - dictionary;
     trainStream.Seek(doubleStream.skipSize, SeekOrigin.Begin);
     encoder.SetTrainSize((uint)(trainFileSize - doubleStream.skipSize));
    }
    encoder.Code(inStream, outStream, -1, -1, null);
   }
   else if (command == "d")
   {
    byte[] properties = new byte[5];
    if (inStream.Read(properties, 0, 5) != 5)
     throw (new Exception("input .lzma is too short"));
    Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder();
    decoder.SetDecoderProperties(properties);
    if (trainStream != null)
    {
     if (!decoder.Train(trainStream))
      throw (new Exception("can't train"));
    }
    long outSize = 0;
    for (int i = 0; i < 8; i++)
    {
     int v = inStream.ReadByte();
     if (v < 0)
      throw (new Exception("Can't Read 1"));
     outSize |= ((long)(byte)v) << (8 * i);
    }
    long compressedSize = inStream.Length - inStream.Position;
    decoder.Code(inStream, outStream, compressedSize, outSize, null);
   }
   else
    throw (new Exception("Command Error"));
   return 0;
  }

  [STAThread]
  static int Main(string[] args)
  {
   try
   {
    return Main2(args);
   }
   catch (Exception e)
   {
    Console.WriteLine("{0} Caught exception #1.", e);
    // throw e;
    return 1;
   }
  }
 }
}

posted @ 2012-01-16 20:26  ×jokey  阅读(2829)  评论(0编辑  收藏  举报