1use core::mem::MaybeUninit;
9use core::ops::{Add, Mul};
10
11use crate::{MaybeSendSync, Result, StridedError, StridedView, StridedViewMut};
12
13fn same_len(actual: usize, expected: usize) -> Result<()> {
14 if actual == expected {
15 Ok(())
16 } else {
17 Err(StridedError::ShapeMismatch(vec![actual], vec![expected]))
18 }
19}
20
21fn product(shape: &[usize]) -> Result<usize> {
22 if shape.contains(&0) {
23 return Ok(0);
24 }
25 shape.iter().try_fold(1usize, |n, &d| {
26 n.checked_mul(d).ok_or(StridedError::OffsetOverflow)
27 })
28}
29
30fn for_each_chunk<T: MaybeSendSync, F: Fn(usize, &mut [T]) + crate::MaybeSync>(
31 output: &mut [T],
32 operation: F,
33) {
34 #[cfg(feature = "parallel")]
35 {
36 let threads = crate::threading::parallel_threads_for_len(output.len());
37 if threads > 1 {
38 let ptr = crate::threading::SendPtr(output.as_mut_ptr());
39 crate::threading::parallel_for_each(0..output.len(), threads, &|range| {
40 let chunk = unsafe {
43 core::slice::from_raw_parts_mut(ptr.as_ptr().add(range.start), range.len())
44 };
45 operation(range.start, chunk);
46 });
47 return;
48 }
49 }
50 operation(0, output);
51}
52
53fn batch_source_offset(mut batch: usize, shape: &[usize], strides: &[isize]) -> isize {
55 let mut offset = 0isize;
56 for (&dim, &stride) in shape.iter().zip(strides) {
57 let coord = batch % dim;
58 batch /= dim;
59 offset += coord as isize * stride;
60 }
61 offset
62}
63
64fn dense_view<'a, T>(data: &'a [T], shape: &[usize]) -> Result<StridedView<'a, T>> {
66 let len = product(shape)?;
67 same_len(data.len(), len)?;
68 let strides = crate::col_major_strides(shape);
69 StridedView::new(data, shape, &strides, 0)
70}
71
72fn copy_strided_run<T: Copy>(dst: &mut [MaybeUninit<T>], data: &[T], base: isize, stride: isize) {
75 if stride == 1 {
76 let start = base as usize;
79 let src = &data[start..start + dst.len()];
80 for (slot, &value) in dst.iter_mut().zip(src) {
81 slot.write(value);
82 }
83 } else {
84 let mut offset = base;
85 for slot in dst.iter_mut() {
86 slot.write(data[offset as usize]);
87 offset += stride;
88 }
89 }
90}
91
92pub fn axpby_accum<T>(y: &mut [T], x: &[T], alpha: T, beta: T) -> Result<()>
107where
108 T: Copy + Send + Sync + Add<Output = T> + Mul<Output = T>,
109{
110 same_len(x.len(), y.len())?;
111 for_each_chunk(y, |start, dst| {
112 let len = dst.len();
113 for (out, &src) in dst.iter_mut().zip(&x[start..start + len]) {
114 *out = alpha * src + beta * *out;
115 }
116 });
117 Ok(())
118}
119
120pub fn triangular_mask_into_uninit<T: Copy + MaybeSendSync>(
139 output: &mut [MaybeUninit<T>],
140 input: &[T],
141 shape: &[usize],
142 k: i64,
143 upper: bool,
144 fill: T,
145) -> Result<()> {
146 let src = dense_view(input, shape)?;
147 triangular_mask_view_into_uninit(output, &src, k, upper, fill)
148}
149
150pub fn triangular_mask_view_into_uninit<T: Copy + MaybeSendSync>(
163 output: &mut [MaybeUninit<T>],
164 src: &StridedView<'_, T>,
165 k: i64,
166 upper: bool,
167 fill: T,
168) -> Result<()> {
169 let shape = src.dims();
170 if shape.len() < 2 {
171 return Err(StridedError::RankMismatch(shape.len(), 2));
172 }
173 let len = product(shape)?;
174 same_len(output.len(), len)?;
175 if len == 0 {
176 return Ok(());
177 }
178 let rows = shape[0];
179 let cols = shape[1];
180 let data = src.data();
181 let strides = src.strides();
182 let offset = src.offset();
183 for_each_chunk(output, |start, chunk| {
184 let mut pos = 0;
185 let mut batch = usize::MAX;
186 let mut batch_offset = 0isize;
187 while pos < chunk.len() {
188 let flat = start + pos;
189 let row = flat % rows;
190 let col = (flat / rows) % cols;
191 let run_batch = flat / (rows * cols);
193 if run_batch != batch {
194 batch = run_batch;
195 batch_offset = batch_source_offset(run_batch, &shape[2..], &strides[2..]);
196 }
197 let count = (rows - row).min(chunk.len() - pos);
198 let boundary = col as i128 - k as i128;
199 let split = if upper {
200 boundary.saturating_add(1)
201 } else {
202 boundary
203 }
204 .clamp(row as i128, (row + count) as i128) as usize
205 - row;
206 let (low, high) = chunk[pos..pos + count].split_at_mut(split);
207 let (kept, first_row, masked) = if upper {
208 (low, row, high)
209 } else {
210 (high, row + split, low)
211 };
212 let base = offset
213 + batch_offset
214 + (col as isize) * strides[1]
215 + (first_row as isize) * strides[0];
216 copy_strided_run(kept, data, base, strides[0]);
219 masked.fill(MaybeUninit::new(fill));
220 pos += count;
221 }
222 });
223 Ok(())
224}
225
226pub fn embed_diagonal_into_uninit<T: Copy + MaybeSendSync + 'static>(
246 output: &mut [MaybeUninit<T>],
247 input: &[T],
248 shape: &[usize],
249 axis: usize,
250 insert_axis: usize,
251 zero: T,
252) -> Result<()> {
253 if axis >= shape.len() {
254 return Err(StridedError::InvalidAxis {
255 axis,
256 rank: shape.len(),
257 });
258 }
259 if insert_axis > shape.len() {
260 return Err(StridedError::InvalidAxis {
261 axis: insert_axis,
262 rank: shape.len() + 1,
263 });
264 }
265 let len = product(shape)?;
266 same_len(input.len(), len)?;
267 let src = dense_view(input, shape)?;
268 embed_diagonal_view_into_uninit(output, &src, axis, insert_axis, zero)
269}
270
271pub fn embed_diagonal_view_into_uninit<T: Copy + MaybeSendSync + 'static>(
284 output: &mut [MaybeUninit<T>],
285 src: &StridedView<'_, T>,
286 axis: usize,
287 insert_axis: usize,
288 zero: T,
289) -> Result<()> {
290 let shape = src.dims();
291 if axis >= shape.len() {
292 return Err(StridedError::InvalidAxis {
293 axis,
294 rank: shape.len(),
295 });
296 }
297 if insert_axis > shape.len() {
298 return Err(StridedError::InvalidAxis {
299 axis: insert_axis,
300 rank: shape.len() + 1,
301 });
302 }
303 let len = product(shape)?;
304 let out_len = len
305 .checked_mul(shape[axis])
306 .ok_or(StridedError::OffsetOverflow)?;
307 same_len(output.len(), out_len)?;
308 if out_len == 0 {
309 return Ok(());
310 }
311 isize::try_from(out_len).map_err(|_| StridedError::OffsetOverflow)?;
312 let mut out_shape: crate::InlineVec<usize> = shape.iter().copied().collect();
313 out_shape.insert(insert_axis, shape[axis]);
314 let mut dst_strides = crate::col_major_strides(&out_shape);
315 let inserted_stride = dst_strides.remove(insert_axis);
316 dst_strides[axis] = dst_strides[axis]
317 .checked_add(inserted_stride)
318 .ok_or(StridedError::OffsetOverflow)?;
319 let mut dst = StridedViewMut::new(output, shape, &dst_strides, 0)?;
320 crate::map_view::validate_destination_layout_without_alloc(shape, &dst_strides)?;
321 for_each_chunk(dst.data_mut(), |_, chunk| {
324 chunk.fill(MaybeUninit::new(zero))
325 });
326 crate::map_into(&mut dst, src, MaybeUninit::new)
327}