1use alloc::boxed::Box;
2use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
3use cubecl_ir::{ExpandValue, OpInserter, dialect::branch::RangeLoopOp};
4use num_traits::NumCast;
5
6use crate as cubecl;
7use cubecl::prelude::*;
8
9#[derive_expand(CubeType)]
10pub struct Range<Idx: Scalar> {
11 start: Idx,
12 end: Idx,
13}
14
15impl<Idx: Scalar> RangeExpand<Idx> {
16 pub fn new(start: NativeExpand<Idx>, end: NativeExpand<Idx>) -> Self {
17 Self { start, end }
18 }
19}
20
21impl<I: Int> RangeExpand<I> {
22 pub fn __expand_step_by_method(
23 self,
24 _: &Scope,
25 n: impl Into<NativeExpand<I>>,
26 ) -> SteppedRangeExpand<I> {
27 SteppedRangeExpand {
28 start: self.start,
29 end: self.end,
30 step: n.into(),
31 inclusive: false,
32 }
33 }
34}
35
36impl<I: Int> Iterable for RangeExpand<I> {
37 type Item = NativeExpand<I>;
38
39 fn expand_unroll(self, scope: &Scope, body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
40 iter_expand_unroll(scope, self.start, self.end, false, body);
41 }
42
43 fn expand(self, scope: &Scope, body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
44 iter_expand(scope, self.start, self.end, false, body);
45 }
46
47 fn const_len(&self) -> Option<usize> {
48 let start = self.start.expand.as_const()?.as_i64();
49 let end = self.end.expand.as_const()?.as_i64();
50 Some(start.abs_diff(end) as usize)
51 }
52}
53
54impl IntoSliceIndices for RangeExpand<usize> {
55 fn into_slice_indices<E: CubePrimitive>(
56 self,
57 _scope: &Scope,
58 _list: &impl ListExpand<E>,
59 ) -> (NativeExpand<usize>, NativeExpand<usize>) {
60 (self.start, self.end)
61 }
62}
63
64#[derive_expand(CubeType)]
65pub struct RangeFrom<Idx: Scalar> {
66 start: Idx,
67}
68
69impl<Idx: Scalar> RangeFromExpand<Idx> {
70 pub fn new(start: NativeExpand<Idx>) -> Self {
71 Self { start }
72 }
73}
74
75impl IntoSliceIndices for RangeFromExpand<usize> {
76 fn into_slice_indices<E: CubePrimitive>(
77 self,
78 scope: &Scope,
79 list: &impl ListExpand<E>,
80 ) -> (NativeExpand<usize>, NativeExpand<usize>) {
81 let end = list.__expand_len_method(scope);
82 (self.start, end)
83 }
84}
85
86#[derive_expand(CubeType)]
87pub struct RangeFull;
88
89impl IntoSliceIndices for RangeFullExpand {
90 fn into_slice_indices<E: CubePrimitive>(
91 self,
92 scope: &Scope,
93 list: &impl ListExpand<E>,
94 ) -> (NativeExpand<usize>, NativeExpand<usize>) {
95 let start = NativeExpand::from_lit(scope, 0);
96 let end = list.__expand_len_method(scope);
97 (start, end)
98 }
99}
100
101#[derive_expand(CubeType)]
102pub struct RangeInclusive<Idx: Scalar> {
103 start: Idx,
104 last: Idx,
105}
106
107impl<Idx: Scalar> RangeInclusiveExpand<Idx> {
108 pub fn new(start: NativeExpand<Idx>, last: NativeExpand<Idx>) -> Self {
109 Self { start, last }
110 }
111}
112
113impl<I: Int> RangeInclusiveExpand<I> {
114 pub fn __expand_step_by_method(
115 self,
116 _: &Scope,
117 n: impl Into<NativeExpand<I>>,
118 ) -> SteppedRangeExpand<I> {
119 SteppedRangeExpand {
120 start: self.start,
121 end: self.last,
122 step: n.into(),
123 inclusive: true,
124 }
125 }
126}
127
128impl<I: Int> Iterable for RangeInclusiveExpand<I> {
129 type Item = NativeExpand<I>;
130
131 fn expand_unroll(self, scope: &Scope, body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
132 iter_expand_unroll(scope, self.start, self.last, true, body);
133 }
134
135 fn expand(self, scope: &Scope, body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
136 iter_expand(scope, self.start, self.last, true, body);
137 }
138
139 fn const_len(&self) -> Option<usize> {
140 let start = self.start.expand.as_const()?.as_i64();
141 let end = self.last.expand.as_const()?.as_i64();
142 Some(start.abs_diff(end) as usize + 1)
143 }
144}
145
146impl IntoSliceIndices for RangeInclusiveExpand<usize> {
147 fn into_slice_indices<E: CubePrimitive>(
148 self,
149 scope: &Scope,
150 _list: &impl ListExpand<E>,
151 ) -> (NativeExpand<usize>, NativeExpand<usize>) {
152 let end = self
153 .last
154 .__expand_add_method(scope, NativeExpand::from_lit(scope, 1));
155 (self.start, end)
156 }
157}
158
159#[derive_expand(CubeType)]
160pub struct RangeTo<Idx: Scalar> {
161 end: Idx,
162}
163
164impl<Idx: Scalar> RangeToExpand<Idx> {
165 pub fn new(end: NativeExpand<Idx>) -> Self {
166 Self { end }
167 }
168}
169
170impl<I: Int> RangeToExpand<I> {
171 pub fn __expand_step_by_method(
172 self,
173 scope: &Scope,
174 n: impl Into<NativeExpand<I>>,
175 ) -> SteppedRangeExpand<I> {
176 SteppedRangeExpand {
177 start: NativeExpand::from_lit(scope, I::new(0)),
178 end: self.end,
179 step: n.into(),
180 inclusive: false,
181 }
182 }
183}
184
185impl<I: Int> Iterable for RangeToExpand<I> {
186 type Item = NativeExpand<I>;
187
188 fn expand_unroll(self, scope: &Scope, body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
189 let start = NativeExpand::from_lit(scope, I::new(0));
190 iter_expand_unroll(scope, start, self.end, false, body);
191 }
192
193 fn expand(self, scope: &Scope, body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
194 let start = NativeExpand::from_lit(scope, I::new(0));
195 iter_expand(scope, start, self.end, false, body);
196 }
197
198 fn const_len(&self) -> Option<usize> {
199 Some(self.end.expand.as_const()?.as_usize())
200 }
201}
202
203impl IntoSliceIndices for RangeToExpand<usize> {
204 fn into_slice_indices<E: CubePrimitive>(
205 self,
206 scope: &Scope,
207 _list: &impl ListExpand<E>,
208 ) -> (NativeExpand<usize>, NativeExpand<usize>) {
209 let start = NativeExpand::from_lit(scope, 0);
210 (start, self.end)
211 }
212}
213
214#[derive_expand(CubeType)]
215pub struct RangeToInclusive<Idx: Scalar> {
216 last: Idx,
217}
218
219impl<Idx: Scalar> RangeToInclusiveExpand<Idx> {
220 pub fn new(last: NativeExpand<Idx>) -> Self {
221 Self { last }
222 }
223}
224
225impl<I: Int> RangeToInclusiveExpand<I> {
226 pub fn __expand_step_by_method(
227 self,
228 scope: &Scope,
229 n: impl Into<NativeExpand<I>>,
230 ) -> SteppedRangeExpand<I> {
231 SteppedRangeExpand {
232 start: NativeExpand::from_lit(scope, I::new(0)),
233 end: self.last,
234 step: n.into(),
235 inclusive: true,
236 }
237 }
238}
239
240impl<I: Int> Iterable for RangeToInclusiveExpand<I> {
241 type Item = NativeExpand<I>;
242
243 fn expand_unroll(self, scope: &Scope, body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
244 let start = NativeExpand::from_lit(scope, I::new(0));
245 iter_expand_unroll(scope, start, self.last, true, body);
246 }
247
248 fn expand(self, scope: &Scope, body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
249 let start = NativeExpand::from_lit(scope, I::new(0));
250 iter_expand(scope, start, self.last, true, body);
251 }
252
253 fn const_len(&self) -> Option<usize> {
254 Some(self.last.expand.as_const()?.as_usize() + 1)
255 }
256}
257
258impl IntoSliceIndices for RangeToInclusiveExpand<usize> {
259 fn into_slice_indices<E: CubePrimitive>(
260 self,
261 scope: &Scope,
262 _list: &impl ListExpand<E>,
263 ) -> (NativeExpand<usize>, NativeExpand<usize>) {
264 let start = NativeExpand::from_lit(scope, 0);
265 let end = self
266 .last
267 .__expand_add_method(scope, NativeExpand::from_lit(scope, 1));
268 (start, end)
269 }
270}
271
272pub(crate) trait IntoSliceIndices {
273 fn into_slice_indices<E: CubePrimitive>(
274 self,
275 scope: &Scope,
276 list: &impl ListExpand<E>,
277 ) -> (NativeExpand<usize>, NativeExpand<usize>);
278}
279
280macro_rules! impl_slice_ranges {
281 ($ty: ty, $range: ty) => {
282 impl<E: CubePrimitive> IndexExpand<$range> for $ty {
283 type Output = SliceExpand<E>;
284
285 fn __expand_index_method(&self, scope: &Scope, index: $range) -> &Self::Output {
286 let (start, end) = index.into_slice_indices(scope, self);
287 self.__expand_slice_method(scope, start, end)
288 }
289 }
290
291 impl<E: CubePrimitive> IndexMutExpand<$range> for $ty {
292 fn __expand_index_mut_method(
293 &mut self,
294 scope: &Scope,
295 index: $range,
296 ) -> &mut <Self as IndexExpand<$range>>::Output {
297 let (start, end) = index.into_slice_indices(scope, self);
298 self.__expand_slice_mut_method(scope, start, end)
299 }
300 }
301 };
302 ($ty: ty) => {
303 impl_slice_ranges!($ty, RangeExpand<usize>);
304 impl_slice_ranges!($ty, RangeFromExpand<usize>);
305 impl_slice_ranges!($ty, RangeFullExpand);
306 impl_slice_ranges!($ty, RangeInclusiveExpand<usize>);
307 impl_slice_ranges!($ty, RangeToExpand<usize>);
308 impl_slice_ranges!($ty, RangeToInclusiveExpand<usize>);
309 };
310}
311pub(crate) use impl_slice_ranges;
312
313fn iter_expand_unroll<I: Int>(
314 scope: &Scope,
315 start: NativeExpand<I>,
316 end: NativeExpand<I>,
317 inclusive: bool,
318 mut body: impl FnMut(&Scope, <I as CubeType>::ExpandType),
319) {
320 let start = start
321 .expand
322 .as_const()
323 .expect("Only constant start can be unrolled.")
324 .as_i64();
325 let end = end
326 .expand
327 .as_const()
328 .expect("Only constant end can be unrolled.")
329 .as_i64();
330
331 if inclusive {
332 for i in start..=end {
333 let val = I::from_int(i);
334 body(scope, val.into())
335 }
336 } else {
337 for i in start..end {
338 let val = I::from_int(i);
339 body(scope, val.into())
340 }
341 }
342}
343
344fn iter_expand<I: Int>(
345 scope: &Scope,
346 start: NativeExpand<I>,
347 end: NativeExpand<I>,
348 inclusive: bool,
349 mut body: impl FnMut(&Scope, <I as CubeType>::ExpandType),
350) {
351 let start = I::__expand_cast_from(scope, start).expand;
352 let mut end = I::__expand_cast_from(scope, end);
353 let step: ExpandValue = I::new(1).into();
354
355 if inclusive {
356 end = end.__expand_add_method(scope, I::new(1).into());
357 }
358
359 let start = start.read_value(scope);
360 let end = end.read_value(scope);
361 let step = step.read_value(scope);
362
363 let range_loop = RangeLoopOp::new(scope.ctx_mut(), start, end, step);
364 let i = range_loop.iter_var(scope.ctx());
365 let body_block = range_loop.loop_body(scope.ctx());
366 let child = scope.loop_child(OpInserter::new_at_block_end(body_block));
367
368 body(&child, i.into());
369 child.terminate_yield();
370
371 register_range_loop::<I>(scope, &range_loop, &child);
372 scope.set_may_return(&[child]);
373}
374
375pub struct SteppedRangeExpand<I: Int> {
376 start: NativeExpand<I>,
377 end: NativeExpand<I>,
378 step: NativeExpand<I>,
379 inclusive: bool,
380}
381
382impl<I: Int + Into<ExpandValue>> Iterable for SteppedRangeExpand<I> {
383 type Item = NativeExpand<I>;
384
385 fn expand(self, scope: &Scope, mut body: impl FnMut(&Scope, <I as CubeType>::ExpandType)) {
386 let mut end = self.end;
387 if self.inclusive {
388 end = end.__expand_add_method(scope, I::new(1).into());
389 }
390
391 let start = self.start.read_value(scope);
392 let end = end.read_value(scope);
393 let step = self.step.read_value(scope);
394
395 let range_loop = RangeLoopOp::new(scope.ctx_mut(), start, end, step);
396 let i = range_loop.iter_var(scope.ctx());
397 let body_block = range_loop.loop_body(scope.ctx());
398 let child = scope.loop_child(OpInserter::new_at_block_end(body_block));
399
400 body(&child, i.into());
401
402 register_range_loop::<I>(scope, &range_loop, &child);
403 scope.set_may_return(&[child]);
404 }
405
406 fn expand_unroll(
407 self,
408 scope: &Scope,
409 mut body: impl FnMut(&Scope, <I as CubeType>::ExpandType),
410 ) {
411 let start = self
412 .start
413 .expand
414 .as_const()
415 .expect("Only constant start can be unrolled.")
416 .as_i128();
417 let end = self
418 .end
419 .expand
420 .as_const()
421 .expect("Only constant end can be unrolled.")
422 .as_i128();
423 let step = self
424 .step
425 .expand
426 .as_const()
427 .expect("Only constant step can be unrolled.")
428 .as_i128();
429
430 match (self.inclusive, step.is_negative()) {
431 (true, true) => {
432 for i in (end..=start).rev().step_by(step.unsigned_abs() as usize) {
433 let val = I::from_int_128(i);
434 body(scope, val.into())
435 }
436 }
437 (true, false) => {
438 for i in (start..=end).step_by(step.unsigned_abs() as usize) {
439 let val = I::from_int_128(i);
440 body(scope, val.into())
441 }
442 }
443 (false, true) => {
444 for i in (end..start).rev().step_by(step.unsigned_abs() as usize) {
445 let val = I::from_int_128(i);
446 body(scope, val.into())
447 }
448 }
449 (false, false) => {
450 for i in (start..end).step_by(step.unsigned_abs() as usize) {
451 let val = I::from_int_128(i);
452 body(scope, val.into())
453 }
454 }
455 }
456 }
457
458 fn const_len(&self) -> Option<usize> {
459 let start = self.start.constant()?.as_i128();
460 let end = self.end.constant()?.as_i128();
461 let step = self.step.constant()?.as_i128().unsigned_abs();
462 Some((start.abs_diff(end) / step) as usize)
463 }
464}
465
466pub fn range<T: Int>(start: T, end: T) -> impl Iterator<Item = T> {
472 let start: i64 = start.to_i64().unwrap();
473 let end: i64 = end.to_i64().unwrap();
474 (start..end).map(<T as NumCast>::from).map(Option::unwrap)
475}
476
477pub mod range {
478 use cubecl_ir::Scope;
479
480 use crate::prelude::{Int, NativeExpand};
481
482 use super::RangeExpand;
483
484 pub fn expand<I: Int>(
485 _scope: &Scope,
486 start: NativeExpand<I>,
487 end: NativeExpand<I>,
488 ) -> RangeExpand<I> {
489 RangeExpand { start, end }
490 }
491}
492
493pub fn range_stepped<I: Int>(start: I, end: I, step: I) -> Box<dyn Iterator<Item = I>> {
501 let start = start.to_i128().unwrap();
502 let end = end.to_i128().unwrap();
503 let step = step.to_i128().unwrap();
504
505 if step < 0 {
506 Box::new(
507 (end..start)
508 .rev()
509 .step_by(step.unsigned_abs() as usize)
510 .map(<I as NumCast>::from)
511 .map(Option::unwrap),
512 )
513 } else {
514 Box::new(
515 (start..end)
516 .step_by(step.unsigned_abs() as usize)
517 .map(<I as NumCast>::from)
518 .map(Option::unwrap),
519 )
520 }
521}
522
523pub mod range_stepped {
524 use cubecl_ir::Scope;
525
526 use crate::prelude::{Int, NativeExpand};
527
528 use super::SteppedRangeExpand;
529
530 pub fn expand<I: Int>(
531 _scope: &Scope,
532 start: NativeExpand<I>,
533 end: NativeExpand<I>,
534 step: NativeExpand<I>,
535 ) -> SteppedRangeExpand<I> {
536 SteppedRangeExpand {
537 start,
538 end,
539 step,
540 inclusive: false,
541 }
542 }
543}