context: namespace Maticsoft.ZipLib.Zip.Compression
  {
      using Maticsoft.ZipLib;
      using System;
  
      public class DeflaterHuffman
      {
          private static readonly byte[] bit4Reverse = new byte[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
          private const int BITLEN_NUM = 0x13;
          private static readonly int[] BL_ORDER = new int[] { 
              0x10, 0x11, 0x12, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 
              14, 1, 15
           };
          private Tree blTree;
          private const int BUFSIZE = 0x4000;
          private short[] d_buf;
          private const int DIST_NUM = 30;
          private Tree distTree;
          private const int EOF_SYMBOL = 0x100;
          private int extra_bits;
          private byte[] l_buf;
          private int last_lit;
          private const int LITERAL_NUM = 0x11e;
          private Tree literalTree;
          public DeflaterPending pending;
          private const int REP_11_138 = 0x12;
          private const int REP_3_10 = 0x11;
          private const int REP_3_6 = 0x10;
          private static short[] staticDCodes;
          private static byte[] staticDLength;
          private static short[] staticLCodes = new short[0x11e];
          private static byte[] staticLLength = new byte[0x11e];
  
          static DeflaterHuffman()
          {
              int index = 0;
              while (index < 0x90)
              {
                  staticLCodes[index] = BitReverse((0x30 + index) << 8);
                  staticLLength[index++] = 8;
              }
              while (index < 0x100)
              {
                  staticLCodes[index] = BitReverse((0x100 + index) << 7);
                  staticLLength[index++] = 9;
              }
              while (index < 280)
              {
                  staticLCodes[index] = BitReverse((-256 + index) << 9);
                  staticLLength[index++] = 7;
              }
              while (index < 0x11e)
              {
                  staticLCodes[index] = BitReverse((-88 + index) << 8);
                  staticLLength[index++] = 8;
              }
              staticDCodes = new short[30];
              staticDLength = new byte[30];
              for (index = 0; index < 30; index++)
              {
                  staticDCodes[index] = BitReverse(index << 11);
                  staticDLength[index] = 5;
              }
          }
  
          public DeflaterHuffman(DeflaterPending pending)
          {
              this.pending = pending;
              this.literalTree = new Tree(this, 0x11e, 0x101, 15);
              this.distTree = new Tree(this, 30, 1, 15);
              this.blTree = new Tree(this, 0x13, 4, 7);
              this.d_buf = new short[0x4000];
              this.l_buf = new byte[0x4000];
          }
  
          public static short BitReverse(int toReverse)
          {
              return (short) ((((bit4Reverse[toReverse & 15] << 12) | (bit4Reverse[(toReverse >> 4) & 15] << 8)) | (bit4Reverse[(toReverse >> 8) & 15] << 4)) | bit4Reverse[toReverse >> 12]);
          }
  
          public void CompressBlock()
          {
              for (int i = 0; i < this.last_lit; i++)
              {
                  int length = this.l_buf[i] & 0xff;
                  int distance = this.d_buf[i];
                  if (distance-- != 0)
                  {
                      int code = Lcode(length);
                      this.literalTree.WriteSymbol(code);
                      int count = (code - 0x105) / 4;
                      if ((count > 0) && (count <= 5))
                      {
                          this.pending.WriteBits(length & ((((int) 1) << count) - 1), count);
                      }
                      int num6 = Dcode(distance);
                      this.distTree.WriteSymbol(num6);
                      count = (num6 / 2) - 1;
                      if (count > 0)
                      {
                          this.pending.WriteBits(distance & ((((int) 1) << count) - 1), count);
                      }
                  }
                  else
                  {
                      this.literalTree.WriteSymbol(length);
                  }
              }
              this.literalTree.WriteSymbol(0x100);
          }
  
          private static int Dcode(int distance)
          {
              int num = 0;
              while (distance >= 4)
              {
                  num += 2;
                  distance = distance >> 1;
              }
              return (num + distance);
          }
  
          public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
          {
              int num2;
              this.literalTree.freqs[0x100] = (short) (this.literalTree.freqs[0x100] + 1);
              this.literalTree.BuildTree();
              this.distTree.BuildTree();
              this.literalTree.CalcBLFreq(this.blTree);
              this.distTree.CalcBLFreq(this.blTree);
              this.blTree.BuildTree();
              int blTreeCodes = 4;
              for (num2 = 0x12; num2 > blTreeCodes; num2--)
              {
                  if (this.blTree.length[BL_ORDER[num2]] > 0)
                  {
                      blTreeCodes = num2 + 1;
                  }
              }
              int num3 = ((((14 + (blTreeCodes * 3)) + this.blTree.GetEncodedLength()) + this.literalTree.GetEncodedLength()) + this.distTree.GetEncodedLength()) + this.extra_bits;
              int num4 = this.extra_bits;
              for (num2 = 0; num2 < 0x11e; num2++)
              {
                  num4 += this.literalTree.freqs[num2] * staticLLength[num2];
              }
              for (num2 = 0; num2 < 30; num2++)
              {
                  num4 += this.distTree.freqs[num2] * staticDLength[num2];
              }
              if (num3 >= num4)
              {
                  num3 = num4;
              }
              if ((storedOffset >= 0) && ((storedLength + 4) < (num3 >> 3)))
              {
                  this.FlushStoredBlock(stored, storedOffset, storedLength, lastBlock);
              }
              else if (num3 == num4)
              {
                  this.pending.WriteBits(2 + (lastBlock ? 1 : 0), 3);
                  this.literalTree.SetStaticCodes(staticLCodes, staticLLength);
                  this.distTree.SetStaticCodes(staticDCodes, staticDLength);
                  this.CompressBlock();
                  this.Reset();
              }
              else
              {
                  this.pending.WriteBits(4 + (lastBlock ? 1 : 0), 3);
                  this.SendAllTrees(blTreeCodes);
                  this.CompressBlock();
                  this.Reset();
              }
          }
  
          public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
          {
              this.pending.WriteBits(lastBlock ? 1 : 0, 3);
              this.pending.AlignToByte();
              this.pending.WriteShort(storedLength);
              this.pending.WriteShort(~storedLength);
              this.pending.WriteBlock(stored, storedOffset, storedLength);
              this.Reset();
          }
  
          public bool IsFull()
          {
              return (this.last_lit >= 0x4000);
          }
  
          private static int Lcode(int length)
          {
              if (length == 0xff)
              {
                  return 0x11d;
              }
              int num = 0x101;
              while (length >= 8)
              {
                  num += 4;
                  length = length >> 1;
              }
              return (num + length);
          }
  
          public void Reset()
          {
              this.last_lit = 0;
              this.extra_bits = 0;
              this.literalTree.Reset();
              this.distTree.Reset();
              this.blTree.Reset();
          }
  
          public void SendAllTrees(int blTreeCodes)
          {
              this.blTree.BuildCodes();
              this.literalTree.BuildCodes();
              this.distTree.BuildCodes();
              this.pending.WriteBits(this.literalTree.numCodes - 0x101, 5);
              this.pending.WriteBits(this.distTree.numCodes - 1, 5);
              this.pending.WriteBits(blTreeCodes - 4, 4);
              for (int i = 0; i < blTreeCodes; i++)
              {
                  this.pending.WriteBits(this.blTree.length[BL_ORDER[i]], 3);
              }
              this.literalTree.WriteTree(this.blTree);
              this.distTree.WriteTree(this.blTree);
          }
  
          public bool TallyDist(int distance, int length)
          {
              this.d_buf[this.last_lit] = (short) distance;
              this.l_buf[this.last_lit++] = (byte) (length - 3);
              int index = Lcode(length - 3);
              this.literalTree.freqs[index] = (short) (this.literalTree.freqs[index] + 1);
              if ((index >= 0x109) && (index < 0x11d))
              {
                  this.extra_bits += (index - 0x105) / 4;
              }
              int num2 = Dcode(distance - 1);
              this.distTree.freqs[num2] = (short) (this.distTree.freqs[num2] + 1);
              if (num2 >= 4)
              {
                  this.extra_bits += (num2 / 2) - 1;
              }
              return this.IsFull();
          }
  
          public bool TallyLit(int literal)
          {
              this.d_buf[this.last_lit] = 0;
              this.l_buf[this.last_lit++] = (byte) literal;
              this.literalTree.freqs[literal] = (short) (this.literalTree.freqs[literal] + 1);
              return this.IsFull();
          }
  
          private class Tree
          {
              private int[] bl_counts;
              private short[] codes;
              private DeflaterHuffman dh;
              public short[] freqs;
              public byte[] length;
              private int maxLength;
              public int minNumCodes;
              public int numCodes;
  
              public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength)
              {
                  this.dh = dh;
                  this.minNumCodes = minCodes;
                  this.maxLength = maxLength;
                  this.freqs = new short[elems];
                  this.bl_counts = new int[maxLength];
              }
  
              public void BuildCodes()
              {
                  int length = this.freqs.Length;
                  int[] numArray = new int[this.maxLength];
                  int num2 = 0;
                  this.codes = new short[this.freqs.Length];
                  int index = 0;
                  while (index < this.maxLength)
                  {
                      numArray[index] = num2;
                      num2 += this.bl_counts[index] << (15 - index);
                      index++;
                  }
                  for (int i = 0; i < this.numCodes; i++)
                  {
                      index = this.length[i];
                      if (index > 0)
                      {
                          this.codes[i] = DeflaterHuffman.BitReverse(numArray[index - 1]);
                          numArray[index - 1] += ((int) 1) << (0x10 - index);
                      }
                  }
              }
  
              private void BuildLength(int[] childs)
              {
                  int num4;
                  this.length = new byte[this.freqs.Length];
                  int num = childs.Length / 2;
  
gt:                 int num2 = (num + 1) / 2;