@final
def _call_cython_op(self, values: np.ndarray, *, min_count: int, ngroups: int, comp_ids: np.ndarray, mask: (np.ndarray | None), result_mask: (np.ndarray | None), **kwargs) -> np.ndarray:
    orig_values = values
    dtype = values.dtype
    is_numeric = is_numeric_dtype(dtype)
    is_datetimelike = needs_i8_conversion(dtype)
    if is_datetimelike:
        values = values.view('int64')
        is_numeric = True
    elif is_bool_dtype(dtype):
        values = values.astype('int64')
    elif is_integer_dtype(dtype):
        dtype_str = (dtype.kind + '8')
        values = values.astype(dtype_str, copy=False)
    elif is_numeric:
        if (not is_complex_dtype(dtype)):
            values = ensure_float64(values)
    values = values.T
    if (mask is not None):
        mask = mask.T
        if (result_mask is not None):
            result_mask = result_mask.T
    out_shape = self._get_output_shape(ngroups, values)
    (func, values) = self.get_cython_func_and_vals(values, is_numeric)
    out_dtype = self.get_out_dtype(values.dtype)
    result = maybe_fill(np.empty(out_shape, dtype=out_dtype))
    if (self.kind == 'aggregate'):
        counts = np.zeros(ngroups, dtype=np.int64)
        if (self.how in ['min', 'max', 'mean']):
            func(result, counts, values, comp_ids, min_count, mask=mask, result_mask=result_mask, is_datetimelike=is_datetimelike)
        elif (self.how in ['add']):
            func(result, counts, values, comp_ids, min_count, datetimelike=is_datetimelike)
        else:
            func(result, counts, values, comp_ids, min_count)
    elif self.uses_mask():
        func(result, values, comp_ids, ngroups, is_datetimelike, mask=mask, **kwargs)
    else:
        func(result, values, comp_ids, ngroups, is_datetimelike, **kwargs)
    if (self.kind == 'aggregate'):
        if (is_integer_dtype(result.dtype) and (not is_datetimelike)):
            cutoff = max(1, min_count)
            empty_groups = (counts < cutoff)
            if empty_groups.any():
                result = result.astype('float64')
                result[empty_groups] = np.nan
    result = result.T
    if (self.how not in self.cast_blocklist):
        res_dtype = self._get_result_dtype(orig_values.dtype)
        op_result = maybe_downcast_to_dtype(result, res_dtype)
    else:
        op_result = result
    return op_result
@final
def _call_cython_op(self, values: np.ndarray, *, min_count: int, ngroups: int, comp_ids: np.ndarray, mask: (np.ndarray | None), result_mask: (np.ndarray | None), **kwargs) -> np.ndarray:
    orig_values = values
    dtype = values.dtype
    is_numeric = is_numeric_dtype(dtype)
    is_datetimelike = needs_i8_conversion(dtype)
    if is_datetimelike:
        values = values.view('int64')
        is_numeric = True
    elif is_bool_dtype(dtype):
        values = values.astype('int64')
    elif is_integer_dtype(dtype):
        dtype_str = (dtype.kind + '8')
        values = values.astype(dtype_str, copy=False)
    elif is_numeric:
        if (not is_complex_dtype(dtype)):
            values = ensure_float64(values)
    values = values.T
    if (mask is not None):
        mask = mask.T
        if (result_mask is not None):
            result_mask = result_mask.T
    out_shape = self._get_output_shape(ngroups, values)
    (func, values) = self.get_cython_func_and_vals(values, is_numeric)
    out_dtype = self.get_out_dtype(values.dtype)
    result = maybe_fill(np.empty(out_shape, dtype=out_dtype))
    if (self.kind == 'aggregate'):
        counts = np.zeros(ngroups, dtype=np.int64)
        if (self.how in ['min', 'max', 'mean']):
            func(result, counts, values, comp_ids, min_count, mask=mask, result_mask=result_mask, is_datetimelike=is_datetimelike)
        elif (self.how in ['add']):
            func(result, counts, values, comp_ids, min_count, datetimelike=is_datetimelike)
        else:
            func(result, counts, values, comp_ids, min_count)
    elif self.uses_mask():
        func(result, values, comp_ids, ngroups, is_datetimelike, mask=mask, **kwargs)
    else:
        func(result, values, comp_ids, ngroups, is_datetimelike, **kwargs)
    if (self.kind == 'aggregate'):
        if (is_integer_dtype(result.dtype) and (not is_datetimelike)):
            cutoff = max(1, min_count)
            empty_groups = (counts < cutoff)
            if empty_groups.any():
                result = result.astype('float64')
                result[empty_groups] = np.nan
    result = result.T
    if (self.how not in self.cast_blocklist):
        res_dtype = self._get_result_dtype(orig_values.dtype)
        op_result = maybe_downcast_to_dtype(result, res_dtype)
    else:
        op_result = result
    return op_result