private static List<double[]> findNeighbors(
      List<double[]> data, double[] target, double maxDistance, int maxN) {
    List<double[]> ret = new ArrayList<double[]>();

    List<double[]> found = new ArrayList<double[]>();
    GrowQueue_F64 distances = new GrowQueue_F64();
    GrowQueue_I32 indexes = new GrowQueue_I32();

    for (int i = 0; i < data.size(); i++) {
      double[] d = data.get(i);

      double dx = d[0] - target[0];
      double dy = d[1] - target[1];

      double dist = dx * dx + dy * dy;
      if (dist <= maxDistance) {
        distances.add(dist);
        found.add(d);
      }
    }

    indexes.resize(distances.size);

    maxN = Math.min(maxN, distances.size);

    QuickSelectArray.selectIndex(distances.data, maxN, distances.size, indexes.data);

    for (int i = 0; i < maxN; i++) {
      ret.add(found.get(indexes.data[i]));
    }

    return ret;
  }
  public static void process(ImageFloat32 input, ImageFloat32 output, int radius, float storage[]) {
    int w = 2 * radius + 1;
    if (storage == null) {
      storage = new float[w * w];
    } else if (storage.length < w * w) {
      throw new IllegalArgumentException("'storage' must be at least of length " + (w * w));
    }

    for (int y = 0; y < radius; y++) {
      int minI = y - radius;
      int maxI = y + radius + 1;
      if (minI < 0) minI = 0;
      if (maxI > input.height) maxI = input.height;

      for (int x = 0; x < input.width; x++) {
        int minJ = x - radius;
        int maxJ = x + radius + 1;

        // bound it ot be inside the image
        if (minJ < 0) minJ = 0;
        if (maxJ > input.width) maxJ = input.width;

        int index = 0;

        for (int i = minI; i < maxI; i++) {
          for (int j = minJ; j < maxJ; j++) {
            storage[index++] = input.get(j, i);
          }
        }

        // use quick select to avoid sorting the whole list
        float median = QuickSelectArray.select(storage, index / 2, index);
        output.set(x, y, median);
      }
    }

    for (int y = input.height - radius; y < input.height; y++) {
      int minI = y - radius;
      int maxI = y + radius + 1;
      if (minI < 0) minI = 0;
      if (maxI > input.height) maxI = input.height;

      for (int x = 0; x < input.width; x++) {
        int minJ = x - radius;
        int maxJ = x + radius + 1;

        // bound it ot be inside the image
        if (minJ < 0) minJ = 0;
        if (maxJ > input.width) maxJ = input.width;

        int index = 0;

        for (int i = minI; i < maxI; i++) {
          for (int j = minJ; j < maxJ; j++) {
            storage[index++] = input.get(j, i);
          }
        }

        // use quick select to avoid sorting the whole list
        float median = QuickSelectArray.select(storage, index / 2, index);
        output.set(x, y, median);
      }
    }

    for (int y = radius; y < input.height - radius; y++) {
      int minI = y - radius;
      int maxI = y + radius + 1;
      for (int x = 0; x < radius; x++) {
        int minJ = x - radius;
        int maxJ = x + radius + 1;

        // bound it ot be inside the image
        if (minJ < 0) minJ = 0;
        if (maxJ > input.width) maxJ = input.width;

        int index = 0;

        for (int i = minI; i < maxI; i++) {
          for (int j = minJ; j < maxJ; j++) {
            storage[index++] = input.get(j, i);
          }
        }

        // use quick select to avoid sorting the whole list
        float median = QuickSelectArray.select(storage, index / 2, index);
        output.set(x, y, median);
      }
    }

    for (int y = radius; y < input.height - radius; y++) {
      int minI = y - radius;
      int maxI = y + radius + 1;
      for (int x = input.width - radius; x < input.width; x++) {
        int minJ = x - radius;
        int maxJ = x + radius + 1;

        // bound it ot be inside the image
        if (minJ < 0) minJ = 0;
        if (maxJ > input.width) maxJ = input.width;

        int index = 0;

        for (int i = minI; i < maxI; i++) {
          for (int j = minJ; j < maxJ; j++) {
            storage[index++] = input.get(j, i);
          }
        }

        // use quick select to avoid sorting the whole list
        float median = QuickSelectArray.select(storage, index / 2, index);
        output.set(x, y, median);
      }
    }
  }