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