content: package zemberek.core.quantization;
  
  public class LinearQuantizer implements Quantizer {
  
    private final double min, max;
    private final int range;
    private final double quantizationStep;
    private double[] quantized;
  
    private LinearQuantizer(double min, double max, int range) {
      if (min > max) {
        throw new IllegalArgumentException(
            "Min value cannot be larger than max value. min:" + min + "  and max:" + max);
      }
      this.min = min;
      this.max = max;
      this.range = range;
      this.quantizationStep = Math.abs(max - min) / (range - 1);
      quantized = new double[range];
      for (int i = 0; i < quantized.length; i++) {
        quantized[i] = min + (quantizationStep * i);
      }
    }
  
    /**
     * Creates a LinearQuantizer with given min max and bit count for quantizationStep count.
     *
     * @param min min value
     * @param max max value
     * @param bits quantizationStep count in bits. quantizer will have 2^bitCount steps.
     * @return LinearQuantizer with given paramters.
     */
    public static LinearQuantizer getByBitRange(double min, double max, int bits) {
      if (bits < 2 || bits > 24) {
        throw new IllegalArgumentException(
            "Bit count cannot be less than 1 or larger than 24" + bits);
      }
      return new LinearQuantizer(min, max, 1 << bits);
    }
  
    /**
     * Creates a LinearQuantizer with given min max and range.
     *
     * @param min min value
     * @param max max value
     * @param stepCount defines how many steps Quantizer will have..
     * @return LinearQuantizer with given paramters.
     */
    public static LinearQuantizer getByStepCount(double min, double max, int stepCount) {
      if (stepCount < 3 || stepCount > 1 << 24) {
        throw new IllegalArgumentException(
            "Step count cannot be less than 3 or larger than 2^24 but it is" + stepCount);
      }
      if (min >= max) {
        throw new IllegalArgumentException(
            "Min value cannot be larger than max value. min:" + min + "  and max:" + max);
      }
      return new LinearQuantizer(min, max, stepCount);
    }
  
  
    /**
     * Applies linear quantization. Returns a value between 0..stepCount-1 it selects the closest
     * integer approximate value by checking the distance between quantization values to minimize
     * quantization error.
     *
     * @param d input to quantize.
     * @return an integer in the range of  0..(2^bitCount-1)
     * @throws IllegalArgumentException if value is less than min or larger than max value.
     */
    public int getQuantizationIndex(double d) {
      if (d < min || d > max) {
        throw new IllegalArgumentException(
            "Value is out of quantization limits. min:" + min + " max:" + max + " but it is " + d);
      }
      if (d == max) {
        return range - 1;
      }
      int low = (int) (Math.abs(d - min) / quantizationStep);
      double v1 = quantized[low];
      double dist = Math.abs(v1 - d);
      if (dist < quantizationStep - dist) {
        return low;
      } else {
        return low + 1;
      }
    }
  
    public double getQuantizedValue(double value) {
      throw new UnsupportedOperationException("This method is not supported in Linear Quantizer yet");
    }
  
    public DoubleLookup getDequantizer() {
      return new DoubleLookup(quantized);
    }
  
    /**
     * Minimum dequantization value.
     *
     * @return Minimum dequantization value.
     */
    public double getMin() {
      return min;
    }
  
    /**
     * Maximum dequantization value.
     *
     * @return Maximum dequantization value.
     */
    public double getMax() {
      return max;
    }
  
    public int getRange() {
      return range;
    }
  
    public double getQuantizationStep() {
      return quantizationStep;
    }
  }
  