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 if inclusive {
348 for i in start..=end {
349 let val = I::from_int(i);
350 body(scope, val.into())
351 }
352 } else {
353 for i in start..end {
354 let val = I::from_int(i);
355 body(scope, val.into())
356 }
357 }
358}
359
360fn iter_expand<I: Int>(
361 scope: &Scope,
362 start: NativeExpand<I>,
363 end: NativeExpand<I>,
364 inclusive: bool,
365 body: &mut dyn FnMut(&Scope, <I as CubeType>::ExpandType),
366) {
367 let start = I::__expand_cast_from(scope, start).expand;
368 let mut end = I::__expand_cast_from(scope, end);
369 let step: ExpandValue = I::new(1).into();
370
371 if inclusive {
372 end = end.__expand_add_method(scope, I::new(1).into());
373 }
374
375 let start = start.read_value(scope);
376 let end = end.read_value(scope);
377 let step = step.read_value(scope);
378
379 let range_loop = RangeLoopOp::new(scope.ctx_mut(), start, end, step);
380 let i = range_loop.iter_var(scope.ctx());
381 let body_block = range_loop.loop_body(scope.ctx());
382 let child = scope.loop_child(OpInserter::new_at_block_end(body_block));
383
384 body(&child, i.into());
385 child.terminate_yield();
386
387 register_range_loop::<I>(scope, &range_loop, &child);
388 scope.set_may_return(&[child]);
389}
390
391pub struct SteppedRangeExpand<I: Int> {
392 start: NativeExpand<I>,
393 end: NativeExpand<I>,
394 step: NativeExpand<I>,
395 inclusive: bool,
396}
397
398impl<I: Int + Into<ExpandValue>> Iterable for SteppedRangeExpand<I> {
399 type Item = NativeExpand<I>;
400
401 fn expand(self, scope: &Scope, body: &mut dyn FnMut(&Scope, <I as CubeType>::ExpandType)) {
402 let mut end = self.end;
403 if self.inclusive {
404 end = end.__expand_add_method(scope, I::new(1).into());
405 }
406
407 let start = self.start.read_value(scope);
408 let end = end.read_value(scope);
409 let step = self.step.read_value(scope);
410
411 let range_loop = RangeLoopOp::new(scope.ctx_mut(), start, end, step);
412 let i = range_loop.iter_var(scope.ctx());
413 let body_block = range_loop.loop_body(scope.ctx());
414 let child = scope.loop_child(OpInserter::new_at_block_end(body_block));
415
416 body(&child, i.into());
417
418 register_range_loop::<I>(scope, &range_loop, &child);
419 scope.set_may_return(&[child]);
420 }
421
422 fn expand_unroll(
423 self,
424 scope: &Scope,
425 body: &mut dyn FnMut(&Scope, <I as CubeType>::ExpandType),
426 ) {
427 let start = self
428 .start
429 .expand
430 .as_const()
431 .expect("Only constant start can be unrolled.")
432 .as_i128();
433 let end = self
434 .end
435 .expand
436 .as_const()
437 .expect("Only constant end can be unrolled.")
438 .as_i128();
439 let step = self
440 .step
441 .expand
442 .as_const()
443 .expect("Only constant step can be unrolled.")
444 .as_i128();
445
446 match (self.inclusive, step.is_negative()) {
447 (true, true) => {
448 for i in (end..=start).rev().step_by(step.unsigned_abs() as usize) {
449 let val = I::from_int_128(i);
450 body(scope, val.into())
451 }
452 }
453 (true, false) => {
454 for i in (start..=end).step_by(step.unsigned_abs() as usize) {
455 let val = I::from_int_128(i);
456 body(scope, val.into())
457 }
458 }
459 (false, true) => {
460 for i in (end..start).rev().step_by(step.unsigned_abs() as usize) {
461 let val = I::from_int_128(i);
462 body(scope, val.into())
463 }
464 }
465 (false, false) => {
466 for i in (start..end).step_by(step.unsigned_abs() as usize) {
467 let val = I::from_int_128(i);
468 body(scope, val.into())
469 }
470 }
471 }
472 }
473
474 fn const_len(&self) -> Option<usize> {
475 let start = self.start.constant()?.as_i128();
476 let end = self.end.constant()?.as_i128();
477 let step = self.step.constant()?.as_i128().unsigned_abs();
478 Some((start.abs_diff(end) / step) as usize)
479 }
480}
481
482pub fn range<T: Int>(start: T, end: T) -> impl Iterator<Item = T> {
488 let start: i64 = start.to_i64().unwrap();
489 let end: i64 = end.to_i64().unwrap();
490 (start..end).map(<T as NumCast>::from).map(Option::unwrap)
491}
492
493pub mod range {
494 use cubecl_ir::Scope;
495
496 use crate::prelude::{Int, NativeExpand};
497
498 use super::RangeExpand;
499
500 pub fn expand<I: Int>(
501 _scope: &Scope,
502 start: NativeExpand<I>,
503 end: NativeExpand<I>,
504 ) -> RangeExpand<I> {
505 RangeExpand { start, end }
506 }
507}
508
509pub fn range_stepped<I: Int>(start: I, end: I, step: I) -> Box<dyn Iterator<Item = I>> {
517 let start = start.to_i128().unwrap();
518 let end = end.to_i128().unwrap();
519 let step = step.to_i128().unwrap();
520
521 if step < 0 {
522 Box::new(
523 (end..start)
524 .rev()
525 .step_by(step.unsigned_abs() as usize)
526 .map(<I as NumCast>::from)
527 .map(Option::unwrap),
528 )
529 } else {
530 Box::new(
531 (start..end)
532 .step_by(step.unsigned_abs() as usize)
533 .map(<I as NumCast>::from)
534 .map(Option::unwrap),
535 )
536 }
537}
538
539pub mod range_stepped {
540 use cubecl_ir::Scope;
541
542 use crate::prelude::{Int, NativeExpand};
543
544 use super::SteppedRangeExpand;
545
546 pub fn expand<I: Int>(
547 _scope: &Scope,
548 start: NativeExpand<I>,
549 end: NativeExpand<I>,
550 step: NativeExpand<I>,
551 ) -> SteppedRangeExpand<I> {
552 SteppedRangeExpand {
553 start,
554 end,
555 step,
556 inclusive: false,
557 }
558 }
559}