255 lines
6.4 KiB
Java
255 lines
6.4 KiB
Java
package btools.codec;
|
|
|
|
import java.util.Map;
|
|
import java.util.TreeMap;
|
|
|
|
import btools.util.BitCoderContext;
|
|
|
|
public final class StatCoderContext extends BitCoderContext {
|
|
private static Map<String, long[]> statsPerName;
|
|
private long lastbitpos = 0;
|
|
|
|
|
|
private static final int[] noisy_bits = new int[1024];
|
|
|
|
static {
|
|
// noisybits lookup
|
|
for (int i = 0; i < 1024; i++) {
|
|
int p = i;
|
|
int noisybits = 0;
|
|
while (p > 2) {
|
|
noisybits++;
|
|
p >>= 1;
|
|
}
|
|
noisy_bits[i] = noisybits;
|
|
}
|
|
}
|
|
|
|
|
|
public StatCoderContext(byte[] ab) {
|
|
super(ab);
|
|
}
|
|
|
|
/**
|
|
* assign the de-/encoded bits since the last call assignBits to the given
|
|
* name. Used for encoding statistics
|
|
*
|
|
* @see #getBitReport
|
|
*/
|
|
public void assignBits(String name) {
|
|
long bitpos = getWritingBitPosition();
|
|
if (statsPerName == null) {
|
|
statsPerName = new TreeMap<String, long[]>();
|
|
}
|
|
long[] stats = statsPerName.get(name);
|
|
if (stats == null) {
|
|
stats = new long[2];
|
|
statsPerName.put(name, stats);
|
|
}
|
|
stats[0] += bitpos - lastbitpos;
|
|
stats[1] += 1;
|
|
lastbitpos = bitpos;
|
|
}
|
|
|
|
/**
|
|
* Get a textual report on the bit-statistics
|
|
*
|
|
* @see #assignBits
|
|
*/
|
|
public static String getBitReport() {
|
|
if (statsPerName == null) {
|
|
return "<empty bit report>";
|
|
}
|
|
StringBuilder sb = new StringBuilder();
|
|
for (String name : statsPerName.keySet()) {
|
|
long[] stats = statsPerName.get(name);
|
|
sb.append(name + " count=" + stats[1] + " bits=" + stats[0] + "\n");
|
|
}
|
|
statsPerName = null;
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* encode an unsigned integer with some of of least significant bits
|
|
* considered noisy
|
|
*
|
|
* @see #decodeNoisyNumber
|
|
*/
|
|
public void encodeNoisyNumber(int value, int noisybits) {
|
|
if (value < 0) {
|
|
throw new IllegalArgumentException("encodeVarBits expects positive value");
|
|
}
|
|
if (noisybits > 0) {
|
|
int mask = 0xffffffff >>> (32 - noisybits);
|
|
encodeBounded(mask, value & mask);
|
|
value >>= noisybits;
|
|
}
|
|
encodeVarBits(value);
|
|
}
|
|
|
|
/**
|
|
* decode an unsigned integer with some of of least significant bits
|
|
* considered noisy
|
|
*
|
|
* @see #encodeNoisyNumber
|
|
*/
|
|
public int decodeNoisyNumber(int noisybits) {
|
|
int value = decodeBits(noisybits);
|
|
return value | (decodeVarBits() << noisybits);
|
|
}
|
|
|
|
/**
|
|
* encode a signed integer with some of of least significant bits considered
|
|
* noisy
|
|
*
|
|
* @see #decodeNoisyDiff
|
|
*/
|
|
public void encodeNoisyDiff(int value, int noisybits) {
|
|
if (noisybits > 0) {
|
|
value += 1 << (noisybits - 1);
|
|
int mask = 0xffffffff >>> (32 - noisybits);
|
|
encodeBounded(mask, value & mask);
|
|
value >>= noisybits;
|
|
}
|
|
encodeVarBits(value < 0 ? -value : value);
|
|
if (value != 0) {
|
|
encodeBit(value < 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* decode a signed integer with some of of least significant bits considered
|
|
* noisy
|
|
*
|
|
* @see #encodeNoisyDiff
|
|
*/
|
|
public int decodeNoisyDiff(int noisybits) {
|
|
int value = 0;
|
|
if (noisybits > 0) {
|
|
value = decodeBits(noisybits) - (1 << (noisybits - 1));
|
|
}
|
|
int val2 = decodeVarBits() << noisybits;
|
|
if (val2 != 0) {
|
|
if (decodeBit()) {
|
|
val2 = -val2;
|
|
}
|
|
}
|
|
return value + val2;
|
|
}
|
|
|
|
/**
|
|
* encode a signed integer with the typical range and median taken from the
|
|
* predicted value
|
|
*
|
|
* @see #decodePredictedValue
|
|
*/
|
|
public void encodePredictedValue(int value, int predictor) {
|
|
int p = predictor < 0 ? -predictor : predictor;
|
|
int noisybits = 0;
|
|
|
|
while (p > 2) {
|
|
noisybits++;
|
|
p >>= 1;
|
|
}
|
|
encodeNoisyDiff(value - predictor, noisybits);
|
|
}
|
|
|
|
/**
|
|
* decode a signed integer with the typical range and median taken from the
|
|
* predicted value
|
|
*
|
|
* @see #encodePredictedValue
|
|
*/
|
|
public int decodePredictedValue(int predictor) {
|
|
int p = predictor < 0 ? -predictor : predictor;
|
|
int noisybits = 0;
|
|
while (p > 1023) {
|
|
noisybits++;
|
|
p >>= 1;
|
|
}
|
|
return predictor + decodeNoisyDiff(noisybits + noisy_bits[p]);
|
|
}
|
|
|
|
/**
|
|
* encode an integer-array making use of the fact that it is sorted. This is
|
|
* done, starting with the most significant bit, by recursively encoding the
|
|
* number of values with the current bit being 0. This yields an number of
|
|
* bits per value that only depends on the typical distance between subsequent
|
|
* values and also benefits
|
|
*
|
|
* @param values the array to encode
|
|
* @param offset position in this array where to start
|
|
* @param subsize number of values to encode
|
|
* @param nextbit bitmask with the most significant bit set to 1
|
|
* @param mask should be 0
|
|
*/
|
|
public void encodeSortedArray(int[] values, int offset, int subsize, int nextbit, int mask) {
|
|
if (subsize == 1) { // last-choice shortcut
|
|
while (nextbit != 0) {
|
|
encodeBit((values[offset] & nextbit) != 0);
|
|
nextbit >>= 1;
|
|
}
|
|
}
|
|
if (nextbit == 0) {
|
|
return;
|
|
}
|
|
|
|
int data = mask & values[offset];
|
|
mask |= nextbit;
|
|
|
|
// count 0-bit-fraction
|
|
int i = offset;
|
|
int end = subsize + offset;
|
|
for (; i < end; i++) {
|
|
if ((values[i] & mask) != data) {
|
|
break;
|
|
}
|
|
}
|
|
int size1 = i - offset;
|
|
int size2 = subsize - size1;
|
|
|
|
encodeBounded(subsize, size1);
|
|
if (size1 > 0) {
|
|
encodeSortedArray(values, offset, size1, nextbit >> 1, mask);
|
|
}
|
|
if (size2 > 0) {
|
|
encodeSortedArray(values, i, size2, nextbit >> 1, mask);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param values the array to encode
|
|
* @param offset position in this array where to start
|
|
* @param subsize number of values to encode
|
|
* @param nextbit bitmask with the most significant bit set to 1
|
|
* @param value should be 0
|
|
* @see #encodeSortedArray
|
|
*/
|
|
public void decodeSortedArray(int[] values, int offset, int subsize, int nextbitpos, int value) {
|
|
if (subsize == 1) { // last-choice shortcut
|
|
if (nextbitpos >= 0) {
|
|
value |= decodeBitsReverse(nextbitpos + 1);
|
|
}
|
|
values[offset] = value;
|
|
return;
|
|
}
|
|
if (nextbitpos < 0) {
|
|
while (subsize-- > 0) {
|
|
values[offset++] = value;
|
|
}
|
|
return;
|
|
}
|
|
|
|
int size1 = decodeBounded(subsize);
|
|
int size2 = subsize - size1;
|
|
|
|
if (size1 > 0) {
|
|
decodeSortedArray(values, offset, size1, nextbitpos - 1, value);
|
|
}
|
|
if (size2 > 0) {
|
|
decodeSortedArray(values, offset + size1, size2, nextbitpos - 1, value | (1 << nextbitpos));
|
|
}
|
|
}
|
|
|
|
}
|