/**
   * Integer version of {@link #biorthogonal_F32}.
   *
   * @param J The wavelet's degree. K = J-2.
   * @param borderType How image borders are handled.
   * @return Description of the Daub J/K wavelet.
   */
  public static WaveletDescription<WlCoef_I32> biorthogonal_I32(int J, BorderType borderType) {
    if (J != 5) {
      throw new IllegalArgumentException("Only 5 is currently supported");
    }

    WlCoef_I32 forward = new WlCoef_I32();

    forward.offsetScaling = -2;
    forward.offsetWavelet = 0;

    forward.scaling = new int[5];
    forward.wavelet = new int[3];

    forward.denominatorScaling = 8;
    forward.scaling[0] = -1;
    forward.scaling[1] = 2;
    forward.scaling[2] = 6;
    forward.scaling[3] = 2;
    forward.scaling[4] = -1;

    forward.denominatorWavelet = 2;
    forward.wavelet[0] = -1;
    forward.wavelet[1] = 2;
    forward.wavelet[2] = -1;

    BorderIndex1D border;
    WlBorderCoef<WlCoef_I32> inverse;

    if (borderType == BorderType.WRAP) {
      WlCoef_I32 inner = computeInnerBiorthogonalInverse(forward);
      inverse = new WlBorderCoefStandard<>(inner);
      border = new BorderIndex1D_Wrap();
    } else if (borderType == BorderType.REFLECT) {
      WlCoef_I32 inner = computeInnerBiorthogonalInverse(forward);
      inverse =
          convertToInt(
              (WlBorderCoefFixed<WlCoef_F32>) biorthogonal_F32(J, borderType).getInverse(), inner);
      border = new BorderIndex1D_Reflect();
    } else {
      throw new IllegalArgumentException("Unsupported border type: " + borderType);
    }
    return new WaveletDescription<>(border, forward, inverse);
  }
  private static void convertCoef_F32_to_I32(
      int denominatorScaling, int denominatorWavelet, WlCoef_F32 o, WlCoef_I32 r) {
    r.denominatorScaling = denominatorScaling;
    r.denominatorWavelet = denominatorWavelet;
    r.scaling = new int[o.scaling.length];
    r.wavelet = new int[o.wavelet.length];
    r.offsetScaling = o.offsetScaling;
    r.offsetWavelet = o.offsetWavelet;

    for (int j = 0; j < o.scaling.length; j++) {
      r.scaling[j] = Math.round(o.scaling[j] * denominatorScaling);
    }
    for (int j = 0; j < o.wavelet.length; j++) {
      r.wavelet[j] = Math.round(o.wavelet[j] * denominatorWavelet);
    }
  }
  private static WlCoef_I32 computeInnerBiorthogonalInverse(WlCoef_I32 coef) {
    WlCoef_I32 ret = new WlCoef_I32();

    // center at zero
    ret.offsetScaling = -coef.wavelet.length / 2;
    // center at one
    ret.offsetWavelet = 1 - coef.scaling.length / 2;

    ret.denominatorScaling = coef.denominatorWavelet;
    ret.denominatorWavelet = coef.denominatorScaling;

    ret.scaling = new int[coef.wavelet.length];
    ret.wavelet = new int[coef.scaling.length];

    for (int i = 0; i < ret.scaling.length; i++) {
      if (i % 2 == 0) ret.scaling[i] = -coef.wavelet[i];
      else ret.scaling[i] = coef.wavelet[i];
    }
    for (int i = 0; i < ret.wavelet.length; i++) {
      if (i % 2 == 1) ret.wavelet[i] = -coef.scaling[i];
      else ret.wavelet[i] = coef.scaling[i];
    }
    return ret;
  }