289 lines
6.5 KiB
Java
289 lines
6.5 KiB
Java
package btools.codec;
|
|
|
|
import java.util.TreeMap;
|
|
|
|
import btools.util.BitCoderContext;
|
|
|
|
public final class StatCoderContext extends BitCoderContext
|
|
{
|
|
private static TreeMap<String, long[]> statsPerName;
|
|
private long lastbitpos = 0;
|
|
|
|
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 > 2)
|
|
{
|
|
noisybits++;
|
|
p >>= 1;
|
|
}
|
|
return predictor + decodeNoisyDiff( noisybits );
|
|
}
|
|
|
|
/**
|
|
* 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 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see #encodeSortedArray
|
|
*
|
|
* @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
|
|
*/
|
|
public void decodeSortedArray( int[] values, int offset, int subsize, int nextbit, int value )
|
|
{
|
|
if ( subsize == 1 ) // last-choice shortcut
|
|
{
|
|
while (nextbit != 0)
|
|
{
|
|
if ( decodeBit() )
|
|
{
|
|
value |= nextbit;
|
|
}
|
|
nextbit >>= 1;
|
|
}
|
|
values[offset] = value;
|
|
return;
|
|
}
|
|
if ( nextbit == 0 )
|
|
{
|
|
while (subsize-- > 0)
|
|
{
|
|
values[offset++] = value;
|
|
}
|
|
return;
|
|
}
|
|
|
|
int size1 = decodeBounded( subsize );
|
|
int size2 = subsize - size1;
|
|
|
|
if ( size1 > 0 )
|
|
{
|
|
decodeSortedArray( values, offset, size1, nextbit >> 1, value );
|
|
}
|
|
if ( size2 > 0 )
|
|
{
|
|
decodeSortedArray( values, offset + size1, size2, nextbit >> 1, value | nextbit );
|
|
}
|
|
}
|
|
|
|
}
|