package btools.codec;

/**
 * Encoder/Decoder for signed integers that automatically detects the typical
 * range of these numbers to determine a noisy-bit count as a very simple
 * dictionary
 * <p>
 * Adapted for 3-pass encoding (counters -&gt; statistics -&gt; encoding )
 * but doesn't do anything at pass1
 */
public final class NoisyDiffCoder {
  private int tot;
  private int[] freqs;
  private int noisybits;
  private StatCoderContext bc;
  private int pass;

  /**
   * Create a decoder and read the noisy-bit count from the gibe context
   */
  public NoisyDiffCoder(StatCoderContext bc) {
    noisybits = bc.decodeVarBits();
    this.bc = bc;
  }

  /**
   * Create an encoder for 3-pass-encoding
   */
  public NoisyDiffCoder() {
  }

  /**
   * encodes a signed int (pass3 only, stats collection in pass2)
   */
  public void encodeSignedValue(int value) {
    if (pass == 3) {
      bc.encodeNoisyDiff(value, noisybits);
    } else if (pass == 2) {
      count(value < 0 ? -value : value);
    }
  }

  /**
   * decodes a signed int
   */
  public int decodeSignedValue() {
    return bc.decodeNoisyDiff(noisybits);
  }

  /**
   * Starts a new encoding pass and (in pass3) calculates the noisy-bit count
   * from the stats collected in pass2 and writes that to the given context
   */
  public void encodeDictionary(StatCoderContext bc) {
    if (++pass == 3) {
      // how many noisy bits?
      for (noisybits = 0; noisybits < 14 && tot > 0; noisybits++) {
        if (freqs[noisybits] < (tot >> 1))
          break;
      }
      bc.encodeVarBits(noisybits);
    }
    this.bc = bc;
  }

  private void count(int value) {
    if (freqs == null)
      freqs = new int[14];
    int bm = 1;
    for (int i = 0; i < 14; i++) {
      if (value < bm)
        break;
      else
        freqs[i]++;
      bm <<= 1;
    }
    tot++;
  }
}
