/*
* Copyright 2005-2014, martin isenburg, rapidlasso - fast tools to catch reality
*
* This is free software; you can redistribute and/or modify it under the
* terms of the GNU Lesser General Licence as published by the Free Software
* Foundation. See the LICENSE.txt file for more information.
*
* This software is distributed WITHOUT ANY WARRANTY and without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package com.revolsys.elevation.cloud.las.zip;
import static java.lang.Integer.compareUnsigned;
import com.revolsys.io.BaseCloseable;
import com.revolsys.io.channels.ChannelReader;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// -
// Fast arithmetic coding implementation -
// -> 32-bit variables, 32-bit product, periodic updates, table decoding -
// -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// -
// Version 1.00 - April 25, 2004 -
// -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// -
// WARNING -
// ========= -
// -
// The only purpose of this program is to demonstrate the basic principles -
// of arithmetic coding. It is provided as is, without any express or -
// implied warranty, without even the warranty of fitness for any particular -
// purpose, or that the implementations are correct. -
// -
// Permission to copy and redistribute this code is hereby granted, provided -
// that this warning and copyright notices are not removed or altered. -
// -
// Copyright (c) 2004 by Amir Said (said@ieee.org) & -
// William A. Pearlman (pearlw@ecse.rpi.edu) -
// -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// -
// A description of the arithmetic coding method used here is available in -
// -
// Lossless Compression Handbook, ed. K. Sayood -
// Chapter 5: Arithmetic Coding (A. Said), pp. 101-152, Academic Press, 2003 -
// -
// A. Said, Introduction to Arithetic Coding Theory and Practice -
// HP Labs report HPL-2004-76 - http://www.hpl.hp.com/techreports/ -
// -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
public class ArithmeticDecoder implements ArithmeticConstants, BaseCloseable {
public static ArithmeticBitModel createBitModel() {
return new ArithmeticBitModel();
}
public static ArithmeticModel createSymbolModel(final int n) {
return new ArithmeticModel(n, false);
}
public static void initBitModel(final ArithmeticBitModel m) {
m.init();
}
private ChannelReader reader;
private int value;
private int length;
public ArithmeticDecoder(final ChannelReader reader) {
this.reader = reader;
}
@Override
public void close() {
this.reader = null;
}
public int decodeBit(final ArithmeticBitModel model) {
final int x = model.bit0Prob * (this.length >>> BM_LENGTH_SHIFT); // product l x p0
final int sym = compareUnsigned(this.value, x) >= 0 ? 1 : 0; // decision
// update & shift interval
if (sym == 0) {
this.length = x;
++model.bit0Count;
} else {
this.value -= x; // shifted interval base = 0
this.length -= x;
}
if (compareUnsigned(this.length, AC_MIN_LENGTH) < 0) {
renormDecoderInterval();
}
model.updateIfRequired(); // periodic model update
return sym; // return data bit value
}
public int decodeSymbol(final ArithmeticModel model) {
int symbol;
int x;
final int value = this.value;
int length = this.length;
int y = length;
length >>>= DM_LENGTH_SHIFT;
final int[] decoderTable = model.decoderTable;
final int[] distribution = model.distribution;
if (decoderTable == null) {
symbol = 0;
x = 0;
int n = model.symbols;
int k = n >>> 1;
// decode via bisection search
do {
final int z = length * distribution[k];
if (compareUnsigned(z, value) > 0) {
n = k;
y = z; // value is smaller
} else {
symbol = k;
x = z; // value is larger or equal
}
k = symbol + n >>> 1;
} while (k != symbol);
} else {
final int dv = (int)(Integer.toUnsignedLong(value) / length);
final int t = dv >>> model.tableShift;
symbol = decoderTable[t]; // initial decision based on table look-up
int n = decoderTable[t + 1] + 1;
while (n > symbol + 1) { // finish with bisection search
final int k = symbol + n >>> 1;
if (distribution[k] > dv) {
n = k;
} else {
symbol = k;
}
}
// compute products
x = distribution[symbol] * length;
if (symbol != model.lastSymbol) {
y = distribution[symbol + 1] * length;
}
}
this.value -= x; // update interval
length = y - x;
this.length = length;
if (compareUnsigned(length, AC_MIN_LENGTH) < 0) {
renormDecoderInterval(); // renormalization
}
model.update(symbol);
return symbol;
}
public int getBit() {
final int sym = Integer.divideUnsigned(this.value, this.length >>>= 1); // decode symbol,
// change length
this.value -= this.length * sym; // update interval
if (compareUnsigned(this.length, AC_MIN_LENGTH) < 0) {
renormDecoderInterval(); // renormalization
}
if (compareUnsigned(sym, 2) >= 0) {
throw new RuntimeException("4711");
}
return sym;
}
public int getBits(int bits) {
assert bits != 0 && bits <= 32;
if (bits > 19) {
final int tmp = getShort();
bits = bits - 16;
final int tmp1 = getBits(bits) << 16;
return tmp1 | tmp;
}
final int sym = Integer.divideUnsigned(this.value, this.length >>>= bits);// decode
// symbol,
// change
// length
this.value -= this.length * sym; // update interval
if (compareUnsigned(this.length, AC_MIN_LENGTH) < 0) {
renormDecoderInterval(); // renormalization
}
if (compareUnsigned(sym, 1 << bits) >= 0) {
throw new RuntimeException("4711");
}
return sym;
}
public byte getByte() {
final int sym = Integer.divideUnsigned(this.value, this.length >>>= 8); // decode symbol,
// change length
this.value -= this.length * sym; // update interval
if (compareUnsigned(this.length, AC_MIN_LENGTH) < 0) {
renormDecoderInterval(); // renormalization
}
if (compareUnsigned(sym, 1 << 8) >= 0) {
throw new RuntimeException("4711");
}
return (byte)sym;
}
public double getDouble() {
return Double.longBitsToDouble(getLong());
}
public float getFloat() {
return Float.intBitsToFloat(getInt());
}
public int getInt() {
final int lowerInt = getShort();
final int upperInt = getShort();
return upperInt << 16 | lowerInt;
}
public long getLong() {
final long lowerInt = getInt();
final long upperInt = getInt();
return upperInt << 32 | lowerInt;
}
public char getShort() {
final int sym = Integer.divideUnsigned(this.value, this.length >>>= 16); // decode symbol,
// change length
this.value -= this.length * sym; // update interval
if (compareUnsigned(this.length, AC_MIN_LENGTH) < 0) {
renormDecoderInterval(); // renormalization
}
if (compareUnsigned(sym, 1 << 16) >= 0) {
throw new RuntimeException("4711");
}
return (char)sym;
}
private void renormDecoderInterval() {
final ChannelReader reader = this.reader;
int value = this.value;
int length = this.length;
do { // read least-significant byte
final byte b = reader.getByte();
value = value << 8 | b & 0xff;
length <<= 8;// multiplied by 256
} while (Integer.compareUnsigned(length, AC_MIN_LENGTH) < 0);
this.value = value;
this.length = length;
}
public void reset() {
this.length = AC_MAX_LENGTH;
final ChannelReader reader = this.reader;
int value = (reader.getByte() & 0xff) << 24;
value |= (reader.getByte() & 0xff) << 16;
value |= (reader.getByte() & 0xff) << 8;
value |= reader.getByte() & 0xff;
this.value = value;
}
}