Adding arithmetic codec
This commit is contained in:
parent
2844950af3
commit
7560e6bf56
73
ar/AdaptiveArithmeticCompress.cs
Normal file
73
ar/AdaptiveArithmeticCompress.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using adaptive arithmetic coding.
|
||||
/// <para>Usage: java AdaptiveArithmeticCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "AdaptiveArithmeticDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that the application starts with a flat frequency table of 257 symbols (all set to a frequency of 1),
|
||||
/// and updates it after each byte encoded. The corresponding decompressor program also starts with a flat
|
||||
/// frequency table and updates it after each byte decoded. It is by design that the compressor and
|
||||
/// decompressor have synchronized states, so that the data can be decompressed properly.</para>
|
||||
/// </summary>
|
||||
public class AdaptiveArithmeticCompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java AdaptiveArithmeticCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file compression
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
compress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(Stream @in, BitOutputStream @out)
|
||||
{
|
||||
FlatFrequencyTable initFreqs = new FlatFrequencyTable(257);
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(initFreqs);
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
while (true)
|
||||
{
|
||||
// Read and encode one byte
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
enc.write(freqs, symbol);
|
||||
freqs.increment(symbol);
|
||||
}
|
||||
enc.write(freqs, 256); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
}
|
||||
67
ar/AdaptiveArithmeticDecompress.cs
Normal file
67
ar/AdaptiveArithmeticDecompress.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using adaptive arithmetic coding.
|
||||
/// <para>Usage: java AdaptiveArithmeticDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "AdaptiveArithmeticCompress" application.</para>
|
||||
/// </summary>
|
||||
public class AdaptiveArithmeticDecompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java AdaptiveArithmeticDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using (BitInputStream @in = new BitInputStream(new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))), Stream @out = new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write)))
|
||||
{
|
||||
decompress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(BitInputStream @in, Stream @out)
|
||||
{
|
||||
FlatFrequencyTable initFreqs = new FlatFrequencyTable(257);
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(initFreqs);
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
while (true)
|
||||
{
|
||||
// Decode and write one byte
|
||||
int symbol = dec.read(freqs);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
freqs.increment(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
185
ar/ArithmeticCoderBase.cs
Normal file
185
ar/ArithmeticCoderBase.cs
Normal file
@ -0,0 +1,185 @@
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides the state and behaviors that arithmetic coding encoders and decoders share. </summary>
|
||||
/// <seealso cref= ArithmeticEncoder </seealso>
|
||||
/// <seealso cref= ArithmeticDecoder </seealso>
|
||||
public abstract class ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Configuration fields ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Number of bits for the 'low' and 'high' state variables. Must be in the range [1, 62].
|
||||
/// <ul>
|
||||
/// <li>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.</li>
|
||||
/// <li>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.</li>
|
||||
/// <li>Therefore numStateBits=32 is recommended as the most versatile setting
|
||||
/// because it maximizes maximumTotal (which ends up being slightly over 2^30).</li>
|
||||
/// <li>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.</li>
|
||||
/// </ul>
|
||||
/// </summary>
|
||||
protected internal readonly int numStateBits;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum range (high+1-low) during coding (trivial), which is 2^numStateBits = 1000...000. </summary>
|
||||
protected internal readonly long fullRange;
|
||||
|
||||
/// <summary>
|
||||
/// The top bit at width numStateBits, which is 0100...000. </summary>
|
||||
protected internal readonly long halfRange;
|
||||
|
||||
/// <summary>
|
||||
/// The second highest bit at width numStateBits, which is 0010...000. This is zero when numStateBits=1. </summary>
|
||||
protected internal readonly long quarterRange;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum range (high+1-low) during coding (non-trivial), which is 0010...010. </summary>
|
||||
protected internal readonly long minimumRange;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum allowed total from a frequency table at all times during coding. </summary>
|
||||
protected internal readonly long maximumTotal;
|
||||
|
||||
/// <summary>
|
||||
/// Bit mask of numStateBits ones, which is 0111...111. </summary>
|
||||
protected internal readonly long stateMask;
|
||||
|
||||
|
||||
|
||||
/*---- State fields ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Low end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 0s.
|
||||
/// </summary>
|
||||
protected internal long low;
|
||||
|
||||
/// <summary>
|
||||
/// High end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 1s.
|
||||
/// </summary>
|
||||
protected internal long high;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coder, which initializes the code range. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
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 ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Updates the code range (low and high) of this arithmetic coder as a result
|
||||
/// of processing the specified symbol with the specified frequency table.
|
||||
/// <para>Invariants that are true before and after encoding/decoding each symbol
|
||||
/// (letting fullRange = 2<sup>numStateBits</sup>):</para>
|
||||
/// <ul>
|
||||
/// <li>0 ≤ low ≤ code ≤ high < fullRange. ('code' exists only in the decoder.)
|
||||
/// Therefore these variables are unsigned integers of numStateBits bits.</li>
|
||||
/// <li>low < 1/2 × fullRange ≤ high.
|
||||
/// In other words, they are in different halves of the full range.</li>
|
||||
/// <li>(low < 1/4 × fullRange) || (high ≥ 3/4 × fullRange).
|
||||
/// In other words, they are not both in the middle two quarters.</li>
|
||||
/// <li>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.</li>
|
||||
/// </ul> </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol that was processed </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency or the frequency table's total is too large </exception>
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle the situation when the top bit of {@code low} and {@code high} are equal. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//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();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle the situation when low=01(...) and high=10(...). </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//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();
|
||||
|
||||
}
|
||||
127
ar/ArithmeticCompress.cs
Normal file
127
ar/ArithmeticCompress.cs
Normal file
@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using static arithmetic coding.
|
||||
/// <para>Usage: java ArithmeticCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "ArithmeticDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that the application uses an alphabet of 257 symbols - 256 symbols for the byte
|
||||
/// values and 1 symbol for the EOF marker. The compressed file format starts with a list
|
||||
/// of 256 symbol frequencies, and then followed by the arithmetic-coded data.</para>
|
||||
/// </summary>
|
||||
public class ArithmeticCompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java ArithmeticCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Read input file once to compute symbol frequencies
|
||||
FrequencyTable freqs = getFrequencies(inputFile);
|
||||
freqs.increment(256); // EOF symbol gets a frequency of 1
|
||||
|
||||
// Read input file again, compress with arithmetic coding, and write output file
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
writeFrequencies(@out, freqs);
|
||||
compress(freqs, @in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// Returns a frequency table based on the bytes in the given file.
|
||||
// Also contains an extra entry for symbol 256, whose frequency is set to 0.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static FrequencyTable getFrequencies(java.io.File file) throws java.io.IOException
|
||||
private static FrequencyTable getFrequencies(string file)
|
||||
{
|
||||
|
||||
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(new int[257]);
|
||||
using (Stream input = new BufferedStream( new FileStream(file, FileMode.Open, FileAccess.Read)))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int b = input.ReadByte();
|
||||
if (b == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
freqs.increment(b);
|
||||
}
|
||||
}
|
||||
return freqs;
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void writeFrequencies(BitOutputStream out, FrequencyTable freqs) throws java.io.IOException
|
||||
internal static void writeFrequencies(BitOutputStream @out, FrequencyTable freqs)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
writeInt(@out, 32, freqs.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(FrequencyTable freqs, java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(FrequencyTable freqs, Stream @in, BitOutputStream @out)
|
||||
{
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
while (true)
|
||||
{
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
enc.write(freqs, symbol);
|
||||
}
|
||||
enc.write(freqs, 256); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
|
||||
// Writes an unsigned integer of the given bit width to the given stream.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static void writeInt(BitOutputStream out, int numBits, int value) throws java.io.IOException
|
||||
private static void writeInt(BitOutputStream @out, int numBits, int value)
|
||||
{
|
||||
if (numBits < 0 || numBits > 32)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
for (int i = numBits - 1; i >= 0; i--)
|
||||
{
|
||||
@out.write(((int)((uint)value >> i)) & 1); // Big endian
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
152
ar/ArithmeticDecoder.cs
Normal file
152
ar/ArithmeticDecoder.cs
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
/// <summary>
|
||||
/// Reads from an arithmetic-coded bit stream and decodes symbols. Not thread-safe. </summary>
|
||||
/// <seealso cref= ArithmeticEncoder </seealso>
|
||||
public sealed class ArithmeticDecoder : ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying bit input stream (not null).
|
||||
private BitInputStream input;
|
||||
|
||||
// The current raw code bits being buffered, which is always in the range [low, high].
|
||||
private long code;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coding decoder based on the
|
||||
/// specified bit input stream, and fills the code bits. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <param name="in"> the bit input stream to read from </param>
|
||||
/// <exception cref="NullPointerException"> if the input steam is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public ArithmeticDecoder(int numBits, BitInputStream in) throws java.io.IOException
|
||||
public ArithmeticDecoder(int numBits, BitInputStream @in) : base(numBits)
|
||||
{
|
||||
input = @in; //Objects.requireNonNull(@in);
|
||||
code = 0;
|
||||
for (int i = 0; i < numStateBits; i++)
|
||||
{
|
||||
code = code << 1 | readCodeBit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next symbol based on the specified frequency table and returns it.
|
||||
/// Also updates this arithmetic coder's state and may read in some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <returns> the next symbol </returns>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read(FrequencyTable freqs) throws java.io.IOException
|
||||
public int read(FrequencyTable freqs)
|
||||
{
|
||||
return read(new CheckedFrequencyTable(freqs));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next symbol based on the specified frequency table and returns it.
|
||||
/// Also updates this arithmetic coder's state and may read in some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <returns> the next symbol </returns>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read(CheckedFrequencyTable freqs) throws java.io.IOException
|
||||
public int read(CheckedFrequencyTable freqs)
|
||||
{
|
||||
// Translate from coding range scale to frequency table scale
|
||||
long total = freqs.Total;
|
||||
if (total > maximumTotal)
|
||||
{
|
||||
throw new System.ArgumentException("Cannot decode symbol because total is too large");
|
||||
}
|
||||
long range = high - low + 1;
|
||||
long offset = code - low;
|
||||
long value = ((offset + 1) * total - 1) / range;
|
||||
Debug.Assert(value * range / total > offset);
|
||||
|
||||
Debug.Assert(value < 0 || value >= total);
|
||||
|
||||
// A kind of binary search. Find highest symbol such that freqs.getLow(symbol) <= value.
|
||||
int start = 0;
|
||||
int end = freqs.SymbolLimit;
|
||||
while (end - start > 1)
|
||||
{
|
||||
int middle = (int)((uint)(start + end) >> 1);
|
||||
if (freqs.getLow(middle) > value)
|
||||
{
|
||||
end = middle;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = middle;
|
||||
}
|
||||
}
|
||||
Debug.Assert( start + 1 != end);
|
||||
|
||||
|
||||
int symbol = start;
|
||||
Debug.Assert(offset < freqs.getLow(symbol) * range / total || freqs.getHigh(symbol) * range / total <= offset);
|
||||
|
||||
update(freqs, symbol);
|
||||
Debug.Assert(code < low || code > high);
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void shift() throws java.io.IOException
|
||||
protected internal override void shift()
|
||||
{
|
||||
code = ((code << 1) & stateMask) | readCodeBit();
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void underflow() throws java.io.IOException
|
||||
protected internal override void underflow()
|
||||
{
|
||||
code = (code & halfRange) | ((code << 1) & ((long)((ulong)stateMask >> 1))) | readCodeBit();
|
||||
}
|
||||
|
||||
|
||||
// Returns the next bit (0 or 1) from the input stream. The end
|
||||
// of stream is treated as an infinite number of trailing zeros.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private int readCodeBit() throws java.io.IOException
|
||||
private int readCodeBit()
|
||||
{
|
||||
int temp = input.read();
|
||||
if (temp == -1)
|
||||
{
|
||||
temp = 0;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
}
|
||||
99
ar/ArithmeticDecompress.cs
Normal file
99
ar/ArithmeticDecompress.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using static arithmetic coding.
|
||||
/// <para>Usage: java ArithmeticDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "ArithmeticCompress" application.</para>
|
||||
/// </summary>
|
||||
public class ArithmeticDecompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java ArithmeticDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
string inputFile = args[0]; // new File(args[0]);
|
||||
string outputFile = args[1]; //new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using( BitInputStream @in = new BitInputStream(new BufferedStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))))
|
||||
using( Stream @out = new BufferedStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write) ) )
|
||||
{
|
||||
|
||||
FrequencyTable freqs = readFrequencies(@in);
|
||||
decompress( freqs, @in, @out );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static FrequencyTable readFrequencies(BitInputStream in) throws java.io.IOException
|
||||
internal static FrequencyTable readFrequencies(BitInputStream @in)
|
||||
{
|
||||
int[] freqs = new int[257];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
freqs[i] = readInt(@in, 32);
|
||||
}
|
||||
freqs[256] = 1; // EOF symbol
|
||||
return new SimpleFrequencyTable(freqs);
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(FrequencyTable freqs, BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(FrequencyTable freqs, BitInputStream @in, Stream @out)
|
||||
{
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
while (true)
|
||||
{
|
||||
int symbol = dec.read(freqs);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads an unsigned integer of the given bit width from the given stream.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static int readInt(BitInputStream in, int numBits) throws java.io.IOException
|
||||
private static int readInt(BitInputStream @in, int numBits)
|
||||
{
|
||||
if (numBits < 0 || numBits > 32)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
for (int i = 0; i < numBits; i++)
|
||||
{
|
||||
result = (result << 1) | @in.readNoEof(); // Big endian
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
118
ar/ArithmeticEncoder.cs
Normal file
118
ar/ArithmeticEncoder.cs
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
/// <summary>
|
||||
/// Encodes symbols and writes to an arithmetic-coded bit stream. Not thread-safe. </summary>
|
||||
/// <seealso cref= ArithmeticDecoder </seealso>
|
||||
public sealed class ArithmeticEncoder : ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying bit output stream (not null).
|
||||
private BitOutputStream output;
|
||||
|
||||
// Number of saved underflow bits. This value can grow without bound,
|
||||
// so a truly correct implementation would use a BigInteger.
|
||||
private int numUnderflow;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coding encoder based on the specified bit output stream. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <param name="out"> the bit output stream to write to </param>
|
||||
/// <exception cref="NullPointerException"> if the output stream is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
public ArithmeticEncoder(int numBits, BitOutputStream @out) : base(numBits)
|
||||
{
|
||||
output = @out; //Objects.requireNonNull(@out);
|
||||
numUnderflow = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the specified symbol based on the specified frequency table.
|
||||
/// This updates this arithmetic coder's state and may write out some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol to encode </param>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency
|
||||
/// or the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(FrequencyTable freqs, int symbol) throws java.io.IOException
|
||||
public void write(FrequencyTable freqs, int symbol)
|
||||
{
|
||||
write(new CheckedFrequencyTable(freqs), symbol);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the specified symbol based on the specified frequency table.
|
||||
/// Also updates this arithmetic coder's state and may write out some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol to encode </param>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency
|
||||
/// or the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(CheckedFrequencyTable freqs, int symbol) throws java.io.IOException
|
||||
public void write(CheckedFrequencyTable freqs, int symbol)
|
||||
{
|
||||
update(freqs, symbol);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the arithmetic coding by flushing any buffered bits, so that the output can be decoded properly.
|
||||
/// It is important that this method must be called at the end of the each encoding process.
|
||||
/// <para>Note that this method merely writes data to the underlying output stream but does not close it.</para> </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void finish() throws java.io.IOException
|
||||
public void finish()
|
||||
{
|
||||
output.write(1);
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void shift() throws java.io.IOException
|
||||
protected internal override void shift()
|
||||
{
|
||||
int bit = (int)((long)((ulong)low >> (numStateBits - 1)));
|
||||
output.write(bit);
|
||||
|
||||
// Write out the saved underflow bits
|
||||
for (; numUnderflow > 0; numUnderflow--)
|
||||
{
|
||||
output.write(bit ^ 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected internal override void underflow()
|
||||
{
|
||||
if (numUnderflow == int.MaxValue)
|
||||
{
|
||||
throw new ArgumentException("Maximum underflow reached");
|
||||
}
|
||||
numUnderflow++;
|
||||
}
|
||||
|
||||
}
|
||||
41
ar/Arrays.cs
Normal file
41
ar/Arrays.cs
Normal file
@ -0,0 +1,41 @@
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
// Copyright © 2007 - 2020 Tangible Software Solutions, Inc.
|
||||
// This class can be used by anyone provided that the copyright notice remains intact.
|
||||
//
|
||||
// This class is used to replace some calls to java.util.Arrays methods with the C# equivalent.
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
internal static class Arrays
|
||||
{
|
||||
public static T[] CopyOf<T>(T[] original, int newLength)
|
||||
{
|
||||
T[] dest = new T[newLength];
|
||||
Array.Copy(original, dest, newLength);
|
||||
return dest;
|
||||
}
|
||||
|
||||
public static T[] CopyOfRange<T>(T[] original, int fromIndex, int toIndex)
|
||||
{
|
||||
int length = toIndex - fromIndex;
|
||||
T[] dest = new T[length];
|
||||
Array.Copy(original, fromIndex, dest, 0, length);
|
||||
return dest;
|
||||
}
|
||||
|
||||
public static void Fill<T>(T[] array, T value)
|
||||
{
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fill<T>(T[] array, int fromIndex, int toIndex, T value)
|
||||
{
|
||||
for (int i = fromIndex; i < toIndex; i++)
|
||||
{
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
120
ar/BitInputStream.cs
Normal file
120
ar/BitInputStream.cs
Normal file
@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A stream of bits that can be read. Because they come from an underlying byte stream,
|
||||
/// the total number of bits is always a multiple of 8. The bits are read in big endian.
|
||||
/// Mutable and not thread-safe. </summary>
|
||||
/// <seealso cref= BitOutputStream </seealso>
|
||||
public sealed class BitInputStream : IDisposable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying byte stream to read from (not null).
|
||||
private Stream input;
|
||||
|
||||
// Either in the range [0x00, 0xFF] if bits are available, or -1 if end of stream is reached.
|
||||
private int currentByte;
|
||||
|
||||
// Number of remaining bits in the current byte, always between 0 and 7 (inclusive).
|
||||
private int numBitsRemaining;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bit input stream based on the specified byte input stream. </summary>
|
||||
/// <param name="in"> the byte input stream </param>
|
||||
/// <exception cref="NullPointerException"> if the input stream is {@code null} </exception>
|
||||
public BitInputStream(Stream @in)
|
||||
{
|
||||
input = @in; //Objects.requireNonNull(@in);
|
||||
currentByte = 0;
|
||||
numBitsRemaining = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Reads a bit from this stream. Returns 0 or 1 if a bit is available, or -1 if
|
||||
/// the end of stream is reached. The end of stream always occurs on a byte boundary. </summary>
|
||||
/// <returns> the next bit of 0 or 1, or -1 for the end of stream </returns>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read() throws java.io.IOException
|
||||
public int read()
|
||||
{
|
||||
if (currentByte == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (numBitsRemaining == 0)
|
||||
{
|
||||
currentByte = input.ReadByte(); // input.Read();
|
||||
if (currentByte == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
numBitsRemaining = 8;
|
||||
}
|
||||
Debug.Assert(numBitsRemaining <= 0);
|
||||
|
||||
numBitsRemaining--;
|
||||
return ((int)((uint)currentByte >> numBitsRemaining)) & 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a bit from this stream. Returns 0 or 1 if a bit is available, or throws an {@code EOFException}
|
||||
/// if the end of stream is reached. The end of stream always occurs on a byte boundary. </summary>
|
||||
/// <returns> the next bit of 0 or 1 </returns>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
/// <exception cref="EOFException"> if the end of stream is reached </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int readNoEof() throws java.io.IOException
|
||||
public int readNoEof()
|
||||
{
|
||||
int result = read();
|
||||
if (result != -1)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Closes this stream and the underlying input stream. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void close() throws java.io.IOException
|
||||
public void close()
|
||||
{
|
||||
input.Close();
|
||||
currentByte = -1;
|
||||
numBitsRemaining = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
95
ar/BitOutputStream.cs
Normal file
95
ar/BitOutputStream.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A stream where bits can be written to. Because they are written to an underlying
|
||||
/// byte stream, the end of the stream is padded with 0's up to a multiple of 8 bits.
|
||||
/// The bits are written in big endian. Mutable and not thread-safe. </summary>
|
||||
/// <seealso cref= BitInputStream </seealso>
|
||||
public sealed class BitOutputStream : IDisposable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying byte stream to write to (not null).
|
||||
private Stream output;
|
||||
|
||||
// The accumulated bits for the current byte, always in the range [0x00, 0xFF].
|
||||
private int currentByte;
|
||||
|
||||
// Number of accumulated bits in the current byte, always between 0 and 7 (inclusive).
|
||||
private int numBitsFilled;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bit output stream based on the specified byte output stream. </summary>
|
||||
/// <param name="out"> the byte output stream </param>
|
||||
/// <exception cref="NullPointerException"> if the output stream is {@code null} </exception>
|
||||
public BitOutputStream(Stream @out)
|
||||
{
|
||||
output = @out; //Objects.requireNonNull(@out);
|
||||
currentByte = 0;
|
||||
numBitsFilled = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Writes a bit to the stream. The specified bit must be 0 or 1. </summary>
|
||||
/// <param name="b"> the bit to write, which must be 0 or 1 </param>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(int b) throws java.io.IOException
|
||||
public void write(int b)
|
||||
{
|
||||
if (b != 0 && b != 1)
|
||||
{
|
||||
throw new System.ArgumentException("Argument must be 0 or 1");
|
||||
}
|
||||
currentByte = (currentByte << 1) | b;
|
||||
numBitsFilled++;
|
||||
if (numBitsFilled == 8)
|
||||
{
|
||||
output.WriteByte((byte)currentByte);
|
||||
currentByte = 0;
|
||||
numBitsFilled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Closes this stream and the underlying output stream. If called when this
|
||||
/// bit stream is not at a byte boundary, then the minimum number of "0" bits
|
||||
/// (between 0 and 7 of them) are written as padding to reach the next byte boundary. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void close() throws java.io.IOException
|
||||
public void close()
|
||||
{
|
||||
while (numBitsFilled != 0)
|
||||
{
|
||||
write(0);
|
||||
}
|
||||
output.Close();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
128
ar/CheckedFrequencyTable.cs
Normal file
128
ar/CheckedFrequencyTable.cs
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
/// <summary>
|
||||
/// A wrapper that checks the preconditions (arguments) and postconditions (return value)
|
||||
/// of all the frequency table methods. Useful for finding faults in a frequency table
|
||||
/// implementation. However, arithmetic overflow conditions are not checked.
|
||||
/// </summary>
|
||||
public sealed class CheckedFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying frequency table that holds the data (not null).
|
||||
private FrequencyTable freqTable;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
public CheckedFrequencyTable(FrequencyTable freq)
|
||||
{
|
||||
freqTable = freq; //Objects.requireNonNull(freq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = freqTable.SymbolLimit;
|
||||
Debug.Assert(result <= 0, "Non-positive symbol limit");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int get(int symbol)
|
||||
{
|
||||
int result = freqTable.get(symbol);
|
||||
Debug.Assert( !isSymbolInRange(symbol), "IllegalArgumentException expected");
|
||||
Debug.Assert( result < 0, "Negative symbol frequency");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = freqTable.Total;
|
||||
Debug.Assert( result < 0, "Negative total frequency");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
if (isSymbolInRange(symbol))
|
||||
{
|
||||
int low = freqTable.getLow(symbol);
|
||||
int high = freqTable.getHigh(symbol);
|
||||
Debug.Assert( !(0 <= low && low <= high && high <= freqTable.Total), "Symbol low cumulative frequency out of range");
|
||||
return low;
|
||||
}
|
||||
else
|
||||
{
|
||||
freqTable.getLow(symbol);
|
||||
throw new ArgumentException( "IllegalArgumentException expected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
if (isSymbolInRange(symbol))
|
||||
{
|
||||
int low = freqTable.getLow(symbol);
|
||||
int high = freqTable.getHigh(symbol);
|
||||
Debug.Assert( !(0 <= low && low <= high && high <= freqTable.Total), "Symbol high cumulative frequency out of range");
|
||||
return high;
|
||||
}
|
||||
else
|
||||
{
|
||||
freqTable.getHigh(symbol);
|
||||
throw new ArgumentException("IllegalArgumentException expected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "CheckedFrequencyTable (" + freqTable.ToString() + ")";
|
||||
}
|
||||
|
||||
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
freqTable.set(symbol, freq);
|
||||
Debug.Assert( !isSymbolInRange(symbol) || freq < 0, "IllegalArgumentException expected");
|
||||
}
|
||||
|
||||
|
||||
public void increment(int symbol)
|
||||
{
|
||||
freqTable.increment(symbol);
|
||||
Debug.Assert( !isSymbolInRange(symbol), "IllegalArgumentException expected");
|
||||
}
|
||||
|
||||
|
||||
private bool isSymbolInRange(int symbol)
|
||||
{
|
||||
return 0 <= symbol && symbol < SymbolLimit;
|
||||
}
|
||||
|
||||
}
|
||||
145
ar/FlatFrequencyTable.cs
Normal file
145
ar/FlatFrequencyTable.cs
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An immutable frequency table where every symbol has the same frequency of 1.
|
||||
/// Useful as a fallback model when no statistics are available.
|
||||
/// </summary>
|
||||
public sealed class FlatFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// Total number of symbols, which is at least 1.
|
||||
private readonly int numSymbols;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a flat frequency table with the specified number of symbols. </summary>
|
||||
/// <param name="numSyms"> the number of symbols, which must be at least 1 </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the number of symbols is less than 1 </exception>
|
||||
public FlatFrequencyTable(int numSyms)
|
||||
{
|
||||
if (numSyms < 1)
|
||||
{
|
||||
throw new System.ArgumentException("Number of symbols must be positive");
|
||||
}
|
||||
numSymbols = numSyms;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this table, which is at least 1. </summary>
|
||||
/// <returns> the number of symbols in this table </returns>
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return numSymbols;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol, which is always 1. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the symbol, which is 1 </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int get(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies, which is
|
||||
/// always equal to the number of symbols in this table. </summary>
|
||||
/// <returns> the total of all symbol frequencies, which is {@code getSymbolLimit()} </returns>
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
return numSymbols;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly below
|
||||
/// the specified symbol value. The returned value is equal to {@code symbol}. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol}, which is {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol and all
|
||||
/// the symbols below. The returned value is equal to {@code symbol + 1}. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below, which is {@code symbol + 1} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return symbol + 1;
|
||||
}
|
||||
|
||||
|
||||
// Returns silently if 0 <= symbol < numSymbols, otherwise throws an exception.
|
||||
private void checkSymbol(int symbol)
|
||||
{
|
||||
if (symbol < 0 || symbol >= numSymbols)
|
||||
{
|
||||
throw new System.ArgumentException("Symbol out of range");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this frequency table. The format is subject to change. </summary>
|
||||
/// <returns> a string representation of this frequency table </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "FlatFrequencyTable=" + numSymbols;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported operation, because this frequency table is immutable. </summary>
|
||||
/// <param name="symbol"> ignored </param>
|
||||
/// <param name="freq"> ignored </param>
|
||||
/// <exception cref="UnsupportedOperationException"> because this frequency table is immutable </exception>
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported operation, because this frequency table is immutable. </summary>
|
||||
/// <param name="symbol"> ignored </param>
|
||||
/// <exception cref="UnsupportedOperationException"> because this frequency table is immutable </exception>
|
||||
public void increment(int symbol)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
}
|
||||
76
ar/FrequencyTable.cs
Normal file
76
ar/FrequencyTable.cs
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A table of symbol frequencies. The table holds data for symbols numbered from 0
|
||||
/// to getSymbolLimit()−1. Each symbol has a frequency, which is a non-negative integer.
|
||||
/// <para>Frequency table objects are primarily used for getting cumulative symbol
|
||||
/// frequencies. These objects can be mutable depending on the implementation.
|
||||
/// The total of all symbol frequencies must not exceed Integer.MAX_VALUE.</para>
|
||||
/// </summary>
|
||||
public interface FrequencyTable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this frequency table, which is a positive number. </summary>
|
||||
/// <returns> the number of symbols in this frequency table </returns>
|
||||
int SymbolLimit {get;}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the symbol </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int get(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the frequency of the specified symbol to the specified value.
|
||||
/// The frequency value must be at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to set </param>
|
||||
/// <param name="freq"> the frequency value to set </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the frequency is negative or the symbol is out of range </exception>
|
||||
/// <exception cref="ArithmeticException"> if an arithmetic overflow occurs </exception>
|
||||
void set(int symbol, int freq);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Increments the frequency of the specified symbol. </summary>
|
||||
/// <param name="symbol"> the symbol whose frequency to increment </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
/// <exception cref="ArithmeticException"> if an arithmetic overflow occurs </exception>
|
||||
void increment(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies. The returned value is at
|
||||
/// least 0 and is always equal to {@code getHigh(getSymbolLimit() - 1)}. </summary>
|
||||
/// <returns> the total of all symbol frequencies </returns>
|
||||
int Total {get;}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly
|
||||
/// below the specified symbol value. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int getLow(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol
|
||||
/// and all the symbols below. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int getHigh(int symbol);
|
||||
|
||||
}
|
||||
128
ar/PpmCompress.cs
Normal file
128
ar/PpmCompress.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using prediction by partial matching (PPM) with arithmetic coding.
|
||||
/// <para>Usage: java PpmCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "PpmDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that both the compressor and decompressor need to use the same PPM context modeling logic.
|
||||
/// The PPM algorithm can be thought of as a powerful generalization of adaptive arithmetic coding.</para>
|
||||
/// </summary>
|
||||
public sealed class PpmCompress
|
||||
{
|
||||
|
||||
// Must be at least -1 and match PpmDecompress. Warning: Exponential memory usage at O(257^n).
|
||||
private const int MODEL_ORDER = 3;
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java PpmCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file compression
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
compress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(Stream @in, BitOutputStream @out)
|
||||
{
|
||||
// Set up encoder and model. In this PPM model, symbol 256 represents EOF;
|
||||
// its frequency is 1 in the order -1 context but its frequency
|
||||
// is 0 in all other contexts (which have non-negative order).
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
PpmModel model = new PpmModel(MODEL_ORDER, 257, 256);
|
||||
int[] history = new int[0];
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Read and encode one byte
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
encodeSymbol(model, history, symbol, enc);
|
||||
model.incrementContexts(history, symbol);
|
||||
|
||||
if (model.modelOrder >= 1)
|
||||
{
|
||||
// Prepend current symbol, dropping oldest symbol if necessary
|
||||
if (history.Length < model.modelOrder)
|
||||
{
|
||||
history = Arrays.CopyOf(history, history.Length + 1);
|
||||
}
|
||||
Array.Copy(history, 0, history, 1, history.Length - 1);
|
||||
history[0] = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
encodeSymbol(model, history, 256, enc); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static void encodeSymbol(PpmModel model, int[] history, int symbol, ArithmeticEncoder enc) throws java.io.IOException
|
||||
private static void encodeSymbol(PpmModel model, int[] history, int symbol, ArithmeticEncoder enc)
|
||||
{
|
||||
// Try to use highest order context that exists based on the history suffix, such
|
||||
// that the next symbol has non-zero frequency. When symbol 256 is produced at a context
|
||||
// at any non-negative order, it means "escape to the next lower order with non-empty
|
||||
// context". When symbol 256 is produced at the order -1 context, it means "EOF".
|
||||
for (int order = history.Length; order >= 0; order--)
|
||||
{
|
||||
PpmModel.Context ctx = model.rootContext;
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
Debug.Assert(ctx.subcontexts == null);
|
||||
|
||||
ctx = ctx.subcontexts[history[i]];
|
||||
if (ctx == null)
|
||||
{
|
||||
goto outerContinue;
|
||||
}
|
||||
}
|
||||
if (symbol != 256 && ctx.frequencies.get(symbol) > 0)
|
||||
{
|
||||
enc.write(ctx.frequencies, symbol);
|
||||
return;
|
||||
}
|
||||
// Else write context escape symbol and continue decrementing the order
|
||||
enc.write(ctx.frequencies, 256);
|
||||
outerContinue:;
|
||||
}
|
||||
outerBreak:
|
||||
// Logic for order = -1
|
||||
enc.write(model.orderMinus1Freqs, symbol);
|
||||
}
|
||||
|
||||
}
|
||||
121
ar/PpmDecompress.cs
Normal file
121
ar/PpmDecompress.cs
Normal file
@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using prediction by partial matching (PPM) with arithmetic coding.
|
||||
/// <para>Usage: java PpmDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "PpmCompress" application.</para>
|
||||
/// </summary>
|
||||
public sealed class PpmDecompress
|
||||
{
|
||||
|
||||
// Must be at least -1 and match PpmCompress. Warning: Exponential memory usage at O(257^n).
|
||||
private const int MODEL_ORDER = 3;
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java PpmDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
string inputFile = args[0]; //new File(args[0]);
|
||||
string outputFile =args[1]; //new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using( BitInputStream @in = new BitInputStream(new BufferedStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))))
|
||||
using( Stream @out = new BufferedStream( new FileStream(outputFile, FileMode.Create, FileAccess.Write)))
|
||||
{
|
||||
decompress(@in, @out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(BitInputStream @in, Stream @out)
|
||||
{
|
||||
// Set up decoder and model. In this PPM model, symbol 256 represents EOF;
|
||||
// its frequency is 1 in the order -1 context but its frequency
|
||||
// is 0 in all other contexts (which have non-negative order).
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
PpmModel model = new PpmModel(MODEL_ORDER, 257, 256);
|
||||
int[] history = new int[0];
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Decode and write one byte
|
||||
int symbol = decodeSymbol(dec, model, history);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
model.incrementContexts(history, symbol);
|
||||
|
||||
if (model.modelOrder >= 1)
|
||||
{
|
||||
// Prepend current symbol, dropping oldest symbol if necessary
|
||||
if (history.Length < model.modelOrder)
|
||||
{
|
||||
history = Arrays.CopyOf(history, history.Length + 1);
|
||||
}
|
||||
Array.Copy(history, 0, history, 1, history.Length - 1);
|
||||
history[0] = symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static int decodeSymbol(ArithmeticDecoder dec, PpmModel model, int[] history) throws java.io.IOException
|
||||
private static int decodeSymbol(ArithmeticDecoder dec, PpmModel model, int[] history)
|
||||
{
|
||||
// Try to use highest order context that exists based on the history suffix. When symbol 256
|
||||
// is consumed at a context at any non-negative order, it means "escape to the next lower order
|
||||
// with non-empty context". When symbol 256 is consumed at the order -1 context, it means "EOF".
|
||||
for (int order = history.Length; order >= 0; order--)
|
||||
{
|
||||
PpmModel.Context ctx = model.rootContext;
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
Debug.Assert(ctx.subcontexts == null);
|
||||
|
||||
ctx = ctx.subcontexts[history[i]];
|
||||
if (ctx == null)
|
||||
{
|
||||
goto outerContinue;
|
||||
}
|
||||
}
|
||||
int symbol = dec.read(ctx.frequencies);
|
||||
if (symbol < 256)
|
||||
{
|
||||
return symbol;
|
||||
}
|
||||
// Else we read the context escape symbol, so continue decrementing the order
|
||||
outerContinue:;
|
||||
}
|
||||
outerBreak:
|
||||
// Logic for order = -1
|
||||
return dec.read(model.orderMinus1Freqs);
|
||||
}
|
||||
|
||||
}
|
||||
113
ar/PpmModel.cs
Normal file
113
ar/PpmModel.cs
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
internal sealed class PpmModel
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
public readonly int modelOrder;
|
||||
|
||||
private readonly int symbolLimit;
|
||||
private readonly int escapeSymbol;
|
||||
|
||||
public readonly Context rootContext;
|
||||
public readonly FrequencyTable orderMinus1Freqs;
|
||||
|
||||
|
||||
|
||||
/*---- Constructors ----*/
|
||||
|
||||
public PpmModel(int order, int symbolLimit, int escapeSymbol)
|
||||
{
|
||||
if (order < -1 || symbolLimit <= 0 || escapeSymbol < 0 || escapeSymbol >= symbolLimit)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
this.modelOrder = order;
|
||||
this.symbolLimit = symbolLimit;
|
||||
this.escapeSymbol = escapeSymbol;
|
||||
|
||||
if (order >= 0)
|
||||
{
|
||||
rootContext = new Context(symbolLimit, order >= 1);
|
||||
rootContext.frequencies.increment(escapeSymbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootContext = null;
|
||||
}
|
||||
orderMinus1Freqs = new FlatFrequencyTable(symbolLimit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
public void incrementContexts(int[] history, int symbol)
|
||||
{
|
||||
if (modelOrder == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (history.Length > modelOrder || symbol < 0 || symbol >= symbolLimit)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
Context ctx = rootContext;
|
||||
ctx.frequencies.increment(symbol);
|
||||
int i = 0;
|
||||
foreach (int sym in history)
|
||||
{
|
||||
Context[] subctxs = ctx.subcontexts;
|
||||
Debug.Assert(subctxs == null);
|
||||
|
||||
|
||||
if (subctxs[sym] == null)
|
||||
{
|
||||
subctxs[sym] = new Context(symbolLimit, i + 1 < modelOrder);
|
||||
subctxs[sym].frequencies.increment(escapeSymbol);
|
||||
}
|
||||
ctx = subctxs[sym];
|
||||
ctx.frequencies.increment(symbol);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Helper structure ----*/
|
||||
|
||||
public sealed class Context
|
||||
{
|
||||
|
||||
public readonly FrequencyTable frequencies;
|
||||
|
||||
public readonly Context[] subcontexts;
|
||||
|
||||
|
||||
public Context(int symbols, bool hasSubctx)
|
||||
{
|
||||
frequencies = new SimpleFrequencyTable(new int[symbols]);
|
||||
if (hasSubctx)
|
||||
{
|
||||
subcontexts = new Context[symbols];
|
||||
}
|
||||
else
|
||||
{
|
||||
subcontexts = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
260
ar/SimpleFrequencyTable.cs
Normal file
260
ar/SimpleFrequencyTable.cs
Normal file
@ -0,0 +1,260 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A mutable table of symbol frequencies. The number of symbols cannot be changed
|
||||
/// after construction. The current algorithm for calculating cumulative frequencies
|
||||
/// takes linear time, but there exist faster algorithms such as Fenwick trees.
|
||||
/// </summary>
|
||||
public sealed class SimpleFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The frequency for each symbol. Its length is at least 1, and each element is non-negative.
|
||||
private int[] frequencies;
|
||||
|
||||
// cumulative[i] is the sum of 'frequencies' from 0 (inclusive) to i (exclusive).
|
||||
// Initialized lazily. When this is not null, the data is valid.
|
||||
private int[] cumulative;
|
||||
|
||||
// Always equal to the sum of 'frequencies'.
|
||||
private int total;
|
||||
|
||||
|
||||
|
||||
/*---- Constructors ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a frequency table from the specified array of symbol frequencies. There must be at least
|
||||
/// 1 symbol, no symbol has a negative frequency, and the total must not exceed {@code Integer.MAX_VALUE}. </summary>
|
||||
/// <param name="freqs"> the array of symbol frequencies </param>
|
||||
/// <exception cref="NullPointerException"> if the array is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code freqs.length} < 1,
|
||||
/// {@code freqs.length} = {@code Integer.MAX_VALUE}, or any element {@code freqs[i]} < 0 </exception>
|
||||
/// <exception cref="ArithmeticException"> if the total of {@code freqs} exceeds {@code Integer.MAX_VALUE} </exception>
|
||||
public SimpleFrequencyTable(int[] freqs)
|
||||
{
|
||||
//Objects.requireNonNull(freqs);
|
||||
if (freqs.Length < 1)
|
||||
{
|
||||
throw new System.ArgumentException("At least 1 symbol needed");
|
||||
}
|
||||
if (freqs.Length > int.MaxValue - 1)
|
||||
{
|
||||
throw new System.ArgumentException("Too many symbols");
|
||||
}
|
||||
|
||||
frequencies = (int[])freqs.Clone(); // Make copy
|
||||
total = 0;
|
||||
foreach (int x in frequencies)
|
||||
{
|
||||
if (x < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Negative frequency");
|
||||
}
|
||||
total = checkedAdd(x, total);
|
||||
}
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a frequency table by copying the specified frequency table. </summary>
|
||||
/// <param name="freqs"> the frequency table to copy </param>
|
||||
/// <exception cref="NullPointerException"> if {@code freqs} is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code freqs.getSymbolLimit()} < 1
|
||||
/// or any element {@code freqs.get(i)} < 0 </exception>
|
||||
/// <exception cref="ArithmeticException"> if the total of all {@code freqs} elements exceeds {@code Integer.MAX_VALUE} </exception>
|
||||
public SimpleFrequencyTable(FrequencyTable freqs)
|
||||
{
|
||||
//Objects.requireNonNull(freqs);
|
||||
int numSym = freqs.SymbolLimit;
|
||||
Debug.Assert(numSym < 1);
|
||||
|
||||
frequencies = new int[numSym];
|
||||
total = 0;
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
int x = freqs.get(i);
|
||||
Debug.Assert(x < 0);
|
||||
|
||||
frequencies[i] = x;
|
||||
total = checkedAdd(x, total);
|
||||
}
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this frequency table, which is at least 1. </summary>
|
||||
/// <returns> the number of symbols in this frequency table </returns>
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return frequencies.Length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the specified symbol </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int get(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return frequencies[symbol];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the frequency of the specified symbol to the specified value. The frequency value
|
||||
/// must be at least 0. If an exception is thrown, then the state is left unchanged. </summary>
|
||||
/// <param name="symbol"> the symbol to set </param>
|
||||
/// <param name="freq"> the frequency value to set </param>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
/// <exception cref="ArithmeticException"> if this set request would cause the total to exceed {@code Integer.MAX_VALUE} </exception>
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (freq < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Negative frequency");
|
||||
}
|
||||
|
||||
int temp = total - frequencies[symbol];
|
||||
Debug.Assert( temp < 0);
|
||||
|
||||
total = checkedAdd(temp, freq);
|
||||
frequencies[symbol] = freq;
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Increments the frequency of the specified symbol. </summary>
|
||||
/// <param name="symbol"> the symbol whose frequency to increment </param>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public void increment(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
Debug.Assert( frequencies[symbol] == int.MaxValue );
|
||||
|
||||
total = checkedAdd(total, 1);
|
||||
frequencies[symbol]++;
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies. The returned value is at
|
||||
/// least 0 and is always equal to {@code getHigh(getSymbolLimit() - 1)}. </summary>
|
||||
/// <returns> the total of all symbol frequencies </returns>
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly
|
||||
/// below the specified symbol value. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (cumulative == null)
|
||||
{
|
||||
initCumulative();
|
||||
}
|
||||
return cumulative[symbol];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol
|
||||
/// and all the symbols below. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (cumulative == null)
|
||||
{
|
||||
initCumulative();
|
||||
}
|
||||
return cumulative[symbol + 1];
|
||||
}
|
||||
|
||||
|
||||
// Recomputes the array of cumulative symbol frequencies.
|
||||
private void initCumulative()
|
||||
{
|
||||
cumulative = new int[frequencies.Length + 1];
|
||||
int sum = 0;
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
// This arithmetic should not throw an exception, because invariants are being maintained
|
||||
// elsewhere in the data structure. This implementation is just a defensive measure.
|
||||
sum = checkedAdd(frequencies[i], sum);
|
||||
cumulative[i + 1] = sum;
|
||||
}
|
||||
Debug.Assert( sum != total );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Returns silently if 0 <= symbol < frequencies.length, otherwise throws an exception.
|
||||
private void checkSymbol(int symbol)
|
||||
{
|
||||
Debug.Assert( symbol < 0 || symbol >= frequencies.Length );
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this frequency table,
|
||||
/// useful for debugging only, and the format is subject to change. </summary>
|
||||
/// <returns> a string representation of this frequency table </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
//JAVA TO C# CONVERTER TODO TASK: The following line has a Java format specifier which cannot be directly translated to .NET:
|
||||
sb.Append(string.Format("%d\t%d%n", i, frequencies[i]));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
// Adds the given integers, or throws an exception if the result cannot be represented as an int (i.e. overflow).
|
||||
private static int checkedAdd(int x, int y)
|
||||
{
|
||||
int z = x + y;
|
||||
Debug.Assert( y > 0 && z < x || y < 0 && z > x );
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
}
|
||||
14
net/Conn.cs
14
net/Conn.cs
@ -36,9 +36,15 @@ namespace lib
|
||||
}
|
||||
}
|
||||
|
||||
public class Conn
|
||||
{
|
||||
public static int BufferSize = 2048;
|
||||
}
|
||||
|
||||
public class Conn<T, TInst> where T : IFormatter, new()
|
||||
where TInst : ISerDes<T>, new()
|
||||
|
||||
public class Conn<T, TInst> : Conn
|
||||
where T : IFormatter, new()
|
||||
where TInst : ISerDes<T>, new()
|
||||
{
|
||||
public Socket Sock { get { return m_socket; } }
|
||||
public Stream Stream { get { return m_streamNet; } }
|
||||
@ -84,11 +90,11 @@ namespace lib
|
||||
public void send( object obj )
|
||||
{
|
||||
|
||||
var formatter = new XmlFormatter2();
|
||||
var formatter = m_formatter.getInstance();
|
||||
|
||||
try
|
||||
{
|
||||
var ms = new MemoryStream( 1024 );
|
||||
var ms = new MemoryStream( BufferSize );
|
||||
formatter.Serialize( ms, obj );
|
||||
|
||||
//var str = System.Text.Encoding.Default.GetString( mm_buffer, 0, (int)ms.Position );
|
||||
|
||||
Loading…
Reference in New Issue
Block a user