/**
   * Create a gaussian kernel based on its width. Supports kernels of even or odd widths .
   *
   * @param sigma Sigma of the Gaussian distribution. If <= 0 then the width will be used.
   * @param width How wide the kernel is. Can be even or odd.
   * @return Gaussian convolution kernel.
   */
  public static Kernel2D_F64 gaussianWidth(double sigma, int width) {
    if (sigma <= 0) sigma = sigmaForRadius(width / 2, 0);
    else if (width <= 0)
      throw new IllegalArgumentException(
          "Must specify the width since it doesn't know if it should be even or odd");

    if (width % 2 == 0) {
      int r = width / 2 - 1;
      Kernel2D_F64 ret = new Kernel2D_F64(width);
      double sum = 0;
      for (int y = 0; y < width; y++) {
        double dy = y <= r ? Math.abs(y - r) + 0.5 : Math.abs(y - r - 1) + 0.5;
        for (int x = 0; x < width; x++) {
          double dx = x <= r ? Math.abs(x - r) + 0.5 : Math.abs(x - r - 1) + 0.5;
          double d = Math.sqrt(dx * dx + dy * dy);
          double val = UtilGaussian.computePDF(0, sigma, d);
          ret.set(x, y, val);
          sum += val;
        }
      }

      for (int i = 0; i < ret.data.length; i++) {
        ret.data[i] /= sum;
      }

      return ret;
    } else {
      return gaussian2D_F64(sigma, width / 2, true);
    }
  }
  /**
   * Computes the derivative of a Gaussian kernel.
   *
   * @param sigma Distributions standard deviation.
   * @param radius Kernel's radius.
   * @param normalize
   * @return The derivative of the gaussian
   */
  protected static Kernel1D_F32 derivative1D_F32(
      int order, double sigma, int radius, boolean normalize) {

    Kernel1D_F32 ret = new Kernel1D_F32(radius * 2 + 1);
    float[] gaussian = ret.data;
    int index = 0;
    switch (order) {
      case 1:
        for (int i = radius; i >= -radius; i--) {
          gaussian[index++] = (float) UtilGaussian.derivative1(0, sigma, i);
        }
        break;

      case 2:
        for (int i = radius; i >= -radius; i--) {
          gaussian[index++] = (float) UtilGaussian.derivative2(0, sigma, i);
        }
        break;

      case 3:
        for (int i = radius; i >= -radius; i--) {
          gaussian[index++] = (float) UtilGaussian.derivative3(0, sigma, i);
        }
        break;

      case 4:
        for (int i = radius; i >= -radius; i--) {
          gaussian[index++] = (float) UtilGaussian.derivative4(0, sigma, i);
        }
        break;

      default:
        throw new IllegalArgumentException("Only derivatives of order 1 to 4 are supported");
    }

    // multiply by the same factor as the gaussian would be normalized by
    // otherwise it will effective change the intensity of the input image
    if (normalize) {
      double sum = 0;
      for (int i = radius; i >= -radius; i--) {
        sum += UtilGaussian.computePDF(0, sigma, i);
      }
      for (int i = 0; i < gaussian.length; i++) {
        gaussian[i] /= sum;
      }
    }

    return ret;
  }
  protected static Kernel1D_F64 gaussian1D_F64(double sigma, int radius, boolean normalize) {
    Kernel1D_F64 ret = new Kernel1D_F64(radius * 2 + 1);
    double[] gaussian = ret.data;
    int index = 0;

    for (int i = radius; i >= -radius; i--) {
      gaussian[index++] = UtilGaussian.computePDF(0, sigma, i);
    }

    if (normalize) {
      KernelMath.normalizeSumToOne(ret);
    }

    return ret;
  }