///|
/// The ranges the category covers: closed intervals, ascending by their
/// start, neighbours joined, so that a matcher can hold a set of characters
/// as a list instead of a predicate.
///
/// ```mbt check
/// test {
/// let digits = @moonucd.Nd.ranges()
/// assert_eq(digits[0], (0x30, 0x39))
/// assert_eq(digits[1], (0x660, 0x669))
/// }
/// ```
pub fn Category::ranges(self : Category) -> Array[(Int32, Int32)] {
let mut i = 0
while categories[i] != self {
i += 1
}
slice_ranges(category_ranges, category_ranges_offsets, i)
}
///|
/// The ranges the binary property covers: closed intervals, ascending by their
/// start, neighbours joined.
///
/// ```mbt check
/// test {
/// let dashes = @moonucd.Dash.ranges()
/// assert_eq(dashes[0], (0x2D, 0x2D))
/// assert_true(dashes.contains((0x2010, 0x2015)))
/// }
/// ```
pub fn Property::ranges(self : Property) -> Array[(Int32, Int32)] {
let mut i = 0
while properties[i] != self {
i += 1
}
slice_ranges(property_ranges, property_ranges_offsets, i)
}
///|
/// The ranges a table keeps for entry `i`, from the flat start-and-end list the
/// generator writes and the offsets that cut it.
fn slice_ranges(
table : FixedArray[Int32],
offsets : FixedArray[Int32],
i : Int32,
) -> Array[(Int32, Int32)] {
let out = []
let mut k = offsets[i]
let stop = offsets[i + 1]
while k < stop {
out.push((table[k], table[k + 1]))
k += 2
}
out
}