An active slice offset + stride * [0, extent) on one TileLayout axis.
| 77 | |
| 78 | @dataclass(frozen=True) |
| 79 | class AxisRange: |
| 80 | """An active slice offset + stride * [0, extent) on one TileLayout axis.""" |
| 81 | |
| 82 | extent: int |
| 83 | offset: int = 0 |
| 84 | stride: int = 1 |
| 85 | |
| 86 | def intersect(self, lo: int, hi: int) -> AxisRange: |
| 87 | i_lo = max(0, _ceildiv(lo - self.offset, self.stride)) |
| 88 | i_hi = min(self.extent, (hi - 1 - self.offset) // self.stride + 1) |
| 89 | if i_hi <= i_lo: |
| 90 | raise ExecContextError( |
| 91 | f"filter produces empty range: current=[{self.offset}," |
| 92 | f" {self.offset + self.extent}) ∩ [{lo}, {hi})" |
| 93 | ) |
| 94 | return AxisRange( |
| 95 | extent=i_hi - i_lo, offset=self.offset + self.stride * i_lo, stride=self.stride |
| 96 | ) |
| 97 | |
| 98 | def modulo(self, modulus: int, residue: int) -> AxisRange: |
| 99 | residue %= modulus |
| 100 | rhs = (residue - self.offset) % modulus |
| 101 | g = _gcd(self.stride, modulus) |
| 102 | if rhs % g != 0: |
| 103 | raise ExecContextError( |
| 104 | f"modulo filter produces empty range: {self.offset} + {self.stride} * i" |
| 105 | f" == {residue} mod {modulus}" |
| 106 | ) |
| 107 | reduced_stride = self.stride // g |
| 108 | reduced_rhs = rhs // g |
| 109 | reduced_modulus = modulus // g |
| 110 | period = reduced_modulus |
| 111 | i0 = (reduced_rhs * _mod_inverse(reduced_stride, reduced_modulus)) % reduced_modulus |
| 112 | if i0 >= self.extent: |
| 113 | raise ExecContextError( |
| 114 | f"modulo filter produces empty range: {self.offset} + {self.stride} * i" |
| 115 | f" == {residue} mod {modulus}" |
| 116 | ) |
| 117 | return AxisRange( |
| 118 | extent=(self.extent - 1 - i0) // period + 1, |
| 119 | offset=self.offset + self.stride * i0, |
| 120 | stride=self.stride * period, |
| 121 | ) |
| 122 | |
| 123 | |
| 124 | @dataclass(frozen=True) |
no outgoing calls
searching dependent graphs…