1use crate::ops::{reshape, slice};
8use crate::primitive::{Primitive, ScanOp};
9use crate::view::normalize_axis;
10use crate::{Array, DType, Error, Result, SliceArg, at_least_float};
11
12fn single(primitive: Primitive, a: Array) -> Result<Array> {
13 Ok(Array::from_op(primitive, vec![a])?.swap_remove(0))
14}
15
16fn along(a: &Array, axis: Option<isize>) -> Result<(Array, usize)> {
18 match axis {
19 Some(axis) => Ok((a.clone(), normalize_axis(axis, a.ndim())?)),
20 None => Ok((reshape(a, &[-1])?, 0)),
21 }
22}
23
24pub fn sort(a: &Array, axis: Option<isize>) -> Result<Array> {
37 let (a, axis) = along(a, axis)?;
38 single(Primitive::Sort { axis }, a)
39}
40
41pub fn argsort(a: &Array, axis: Option<isize>) -> Result<Array> {
43 let (a, axis) = along(a, axis)?;
44 single(Primitive::ArgSort { axis }, a)
45}
46
47fn kth_index(op: &str, kth: isize, n: usize) -> Result<isize> {
49 let k = if kth < 0 { kth + n as isize } else { kth };
50 if (0..n as isize).contains(&k) {
51 Ok(k)
52 } else {
53 Err(Error::InvalidArgument(format!(
54 "{op}: kth {kth} is out of range for an axis of length {n}"
55 )))
56 }
57}
58
59pub fn partition(a: &Array, kth: isize, axis: Option<isize>) -> Result<Array> {
63 let (a, axis) = along(a, axis)?;
64 kth_index("partition", kth, a.shape()[axis])?;
65 single(Primitive::Sort { axis }, a)
66}
67
68pub fn argpartition(a: &Array, kth: isize, axis: Option<isize>) -> Result<Array> {
70 let (a, axis) = along(a, axis)?;
71 kth_index("argpartition", kth, a.shape()[axis])?;
72 single(Primitive::ArgSort { axis }, a)
73}
74
75pub fn topk(a: &Array, k: usize, axis: Option<isize>) -> Result<Array> {
88 let (a, axis) = along(a, axis)?;
89 let n = a.shape()[axis];
90 if k > n {
91 return Err(Error::InvalidArgument(format!(
92 "topk: k = {k} exceeds the axis length {n}"
93 )));
94 }
95 if k == n {
96 return Ok(a);
97 }
98 let sorted = single(Primitive::Sort { axis }, a.clone())?;
99 let mut args = vec![SliceArg::full(); a.ndim()];
100 args[axis] = SliceArg::new(Some((n - k) as isize), None, 1);
101 slice(&sorted, &args)
102}
103
104fn scan(
105 op: ScanOp,
106 a: &Array,
107 axis: Option<isize>,
108 reverse: bool,
109 inclusive: bool,
110) -> Result<Array> {
111 let (a, axis) = along(a, axis)?;
112 let a = match op {
113 ScanOp::Sum if a.dtype() == DType::Bool => a.astype(DType::I32)?,
115 ScanOp::LogAddExp => a.astype(at_least_float(a.dtype()))?,
116 _ => a,
117 };
118 let narrow = a.dtype();
122 let wide = match narrow {
123 DType::U8 | DType::U16 => Some(DType::U32),
124 DType::I8 | DType::I16 => Some(DType::I32),
125 _ => None,
126 };
127 let a = match wide {
128 Some(w) => a.astype(w)?,
129 None => a,
130 };
131 let out = single(
132 Primitive::Scan {
133 op,
134 axis,
135 reverse,
136 inclusive,
137 },
138 a,
139 )?;
140 match wide {
141 Some(_) => out.astype(narrow),
142 None => Ok(out),
143 }
144}
145
146pub fn cumsum(a: &Array, axis: Option<isize>, reverse: bool, inclusive: bool) -> Result<Array> {
161 scan(ScanOp::Sum, a, axis, reverse, inclusive)
162}
163
164pub fn cumprod(a: &Array, axis: Option<isize>, reverse: bool, inclusive: bool) -> Result<Array> {
166 scan(ScanOp::Prod, a, axis, reverse, inclusive)
167}
168
169pub fn cummax(a: &Array, axis: Option<isize>, reverse: bool, inclusive: bool) -> Result<Array> {
171 scan(ScanOp::Max, a, axis, reverse, inclusive)
172}
173
174pub fn cummin(a: &Array, axis: Option<isize>, reverse: bool, inclusive: bool) -> Result<Array> {
176 scan(ScanOp::Min, a, axis, reverse, inclusive)
177}
178
179pub fn logcumsumexp(
181 a: &Array,
182 axis: Option<isize>,
183 reverse: bool,
184 inclusive: bool,
185) -> Result<Array> {
186 scan(ScanOp::LogAddExp, a, axis, reverse, inclusive)
187}