using System; using System.Diagnostics; /* * Reference arithmetic coding * Copyright (c) Project Nayuki * * https://www.nayuki.io/page/reference-arithmetic-coding * https://github.com/nayuki/Reference-arithmetic-coding */ /// /// Provides the state and behaviors that arithmetic coding encoders and decoders share. /// /// public abstract class ArithmeticCoderBase { /*---- Configuration fields ----*/ /// /// Number of bits for the 'low' and 'high' state variables. Must be in the range [1, 62]. ///
    ///
  • For state sizes less than the midpoint of around 32, larger values are generally better - /// they allow a larger maximum frequency total (maximumTotal), and they reduce the approximation /// error inherent in adapting fractions to integers; both effects reduce the data encoding loss /// and asymptotically approach the efficiency of arithmetic coding using exact fractions.
  • ///
  • But for state sizes greater than the midpoint, because intermediate computations are limited /// to the long integer type's 63-bit unsigned precision, larger state sizes will decrease the /// maximum frequency total, which might constrain the user-supplied probability model.
  • ///
  • Therefore numStateBits=32 is recommended as the most versatile setting /// because it maximizes maximumTotal (which ends up being slightly over 2^30).
  • ///
  • Note that numStateBits=62 is legal but useless because it implies maximumTotal=1, /// which means a frequency table can only support one symbol with non-zero frequency.
  • ///
///
protected internal readonly int numStateBits; /// /// Maximum range (high+1-low) during coding (trivial), which is 2^numStateBits = 1000...000. protected internal readonly long fullRange; /// /// The top bit at width numStateBits, which is 0100...000. protected internal readonly long halfRange; /// /// The second highest bit at width numStateBits, which is 0010...000. This is zero when numStateBits=1. protected internal readonly long quarterRange; /// /// Minimum range (high+1-low) during coding (non-trivial), which is 0010...010. protected internal readonly long minimumRange; /// /// Maximum allowed total from a frequency table at all times during coding. protected internal readonly long maximumTotal; /// /// Bit mask of numStateBits ones, which is 0111...111. protected internal readonly long stateMask; /*---- State fields ----*/ /// /// Low end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 0s. /// protected internal long low; /// /// High end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 1s. /// protected internal long high; /*---- Constructor ----*/ /// /// Constructs an arithmetic coder, which initializes the code range. /// the number of bits for the arithmetic coding range /// if stateSize is outside the range [1, 62] public ArithmeticCoderBase(int numBits) { if (numBits < 1 || numBits > 62) { throw new System.ArgumentException("State size out of range"); } numStateBits = numBits; fullRange = 1L << numStateBits; halfRange = (long)((ulong)fullRange >> 1); // Non-zero quarterRange = (long)((ulong)halfRange >> 1); // Can be zero minimumRange = quarterRange + 2; // At least 2 maximumTotal = Math.Min(long.MaxValue / fullRange, minimumRange); stateMask = fullRange - 1; low = 0; high = stateMask; } /*---- Methods ----*/ /// /// Updates the code range (low and high) of this arithmetic coder as a result /// of processing the specified symbol with the specified frequency table. /// Invariants that are true before and after encoding/decoding each symbol /// (letting fullRange = 2numStateBits): ///
    ///
  • 0 ≤ low ≤ code ≤ high < fullRange. ('code' exists only in the decoder.) /// Therefore these variables are unsigned integers of numStateBits bits.
  • ///
  • low < 1/2 × fullRange ≤ high. /// In other words, they are in different halves of the full range.
  • ///
  • (low < 1/4 × fullRange) || (high ≥ 3/4 × fullRange). /// In other words, they are not both in the middle two quarters.
  • ///
  • Let range = high − low + 1, then fullRange/4 < minimumRange ≤ range ≤ /// fullRange. These invariants for 'range' essentially dictate the maximum total that the /// incoming frequency table can have, such that intermediate calculations don't overflow.
  • ///
/// the frequency table to use /// the symbol that was processed /// if the symbol has zero frequency or the frequency table's total is too large //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: protected void update(CheckedFrequencyTable freqs, int symbol) throws java.io.IOException protected internal virtual void update(CheckedFrequencyTable freqs, int symbol) { // State check Debug.Assert(low >= high || (low & stateMask) != low || (high & stateMask) != high, "Low or high out of range"); long range = high - low + 1; Debug.Assert(range < minimumRange || range > fullRange, "Range out of range"); // Frequency table values check long total = freqs.Total; long symLow = freqs.getLow(symbol); long symHigh = freqs.getHigh(symbol); Debug.Assert( symLow == symHigh, "Symbol has zero frequency"); Debug.Assert( total > maximumTotal, "Cannot code symbol because total is too large"); // Update range long newLow = low + symLow * range / total; long newHigh = low + symHigh * range / total - 1; low = newLow; high = newHigh; // While low and high have the same top bit value, shift them out while (((low ^ high) & halfRange) == 0) { shift(); low = ((low << 1) & stateMask); high = ((high << 1) & stateMask) | 1; } // Now low's top bit must be 0 and high's top bit must be 1 // While low's top two bits are 01 and high's are 10, delete the second highest bit of both while ((low & ~high & quarterRange) != 0) { underflow(); low = (low << 1) ^ halfRange; high = ((high ^ halfRange) << 1) | halfRange | 1; } } /// /// Called to handle the situation when the top bit of {@code low} and {@code high} are equal. /// if an I/O exception occurred //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: protected abstract void shift() throws java.io.IOException; protected internal abstract void shift(); /// /// Called to handle the situation when low=01(...) and high=10(...). /// if an I/O exception occurred //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: protected abstract void underflow() throws java.io.IOException; protected internal abstract void underflow(); }