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