Repository: scipy/scipy
File: scipy/ndimage/src/_rank_filter_1d.cpp (C++)

Code before:
template <typename T>
void _rank_filter(T *in_arr, int rank, int arr_len, int win_len, T *out_arr,
                  int mode, T cval, int origin) {
  int i, arr_len_thresh, lim = (win_len - 1) / 2 - origin;
  int lim2 = arr_len - lim;
  if (lim2 < 0) {
      if (arr_len == 1) {
          switch (mode) {
              case REFLECT:
              case NEAREST:
              case WRAP:
              case MIRROR:
                  out_arr[0] = in_arr[0];
                  return;
              case CONSTANT:
                  if (win_len == 1) {
                      out_arr[0] = in_arr[0];
                  }
                  else {
                      out_arr[0] = cval;
                  }
                  return;
          }
      }
      else {
          return;
      }
  }
  int offset;
  Mediator *m = MediatorNew(win_len, rank);
  T *data = new T[win_len]();

  switch (mode) {
  case REFLECT:
    for (i = win_len - lim - 1; i > -1; i--) {
      MediatorInsert(data, m, in_arr[i]);
    }
    break;
  case CONSTANT:
    for (i = win_len - lim; i > 0; i--) {
      MediatorInsert(data, m, cval);
    }
    break;
  case NEAREST:
    for (i = win_len - lim; i > 0; i--) {
      MediatorInsert(data, m, in_arr[0]);
    }
    break;
  case MIRROR:
    for (i = win_len - lim; i > 0; i--) {
      MediatorInsert(data, m, in_arr[i]);

Review comment (bug):
@tqbl This latest patch seems to prevent the crashing caught by the regression guard, `test_gh_22586_crash_property`, by restoring the original `lim2` conditional and only special casing the scenario that is the subject of the original ticket.

That said, I suspect this is a move in the right direction, but still not the full story since the case you describe at https://github.com/scipy/scipy/pull/23293/files#r2190998466 may very well still not return the correct answer (though it should no longer segfault).

Can I convince you that this might be "ok" to delay to another issue/ticket? Probably a few permutations of cases similar to the one you describe should be tested against SciPy `1.14.1`, and we should try to see if we can find a sensible algorithm that allows us to match that previous behavior without the "lazy" `return` here.

Or, we could try to make this PR a more complete fix, though that may take a fair bit more time.

Code after:
template <typename T>
void _rank_filter(T *in_arr, int rank, int arr_len, int win_len, T *out_arr,
                  int mode, T cval, int origin) {
  int i, arr_len_thresh, lim = (win_len - 1) / 2 - origin;
  int lim2 = arr_len - lim;
  /* Note: `arr_len == 1` is the only case implemented here for `lim2 < 0`; the calling code */
  /* in _filters.py ensures that this function isn't called otherwise. xref gh-23293 for details. */
  if (lim2 < 0 && arr_len == 1) {
      switch (mode) {
          case REFLECT:
          case NEAREST:
          case WRAP:
          case MIRROR:
              out_arr[0] = in_arr[0];
              return;
          case CONSTANT:
              if (win_len == 1) {
                  out_arr[0] = in_arr[0];
              }
              else {
                  out_arr[0] = cval;
              }
              return;
      }
  }
  int offset;
  Mediator *m = MediatorNew(win_len, rank);
  T *data = new T[win_len]();

  switch (mode) {
  case REFLECT:
    for (i = win_len - lim - 1; i > -1; i--) {
      MediatorInsert(data, m, in_arr[i]);
    }
    break;
  case CONSTANT:
    for (i = win_len - lim; i > 0; i--) {
      MediatorInsert(data, m, cval);
    }
    break;
  case NEAREST:
    for (i = win_len - lim; i > 0; i--) {
      MediatorInsert(data, m, in_arr[0]);
    }
    break;
  case MIRROR:
    for (i = win_len - lim; i > 0; i--) {
      MediatorInsert(data, m, in_arr[i]);
    }
    break;
  case WRAP: