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();
}