#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConsecutiveSegment {
Single(u32),
Range {
start: u32,
end: u32,
},
}
#[must_use]
pub fn consecutive_segments(values: &[u32]) -> Vec<ConsecutiveSegment> {
let mut iter = values.iter();
let Some(&first) = iter.next() else {
return Vec::new();
};
let mut segments = Vec::new();
let mut start = first;
let mut prev = first;
for &value in iter {
if value == prev {
continue;
}
if value == prev + 1 {
prev = value;
continue;
}
push_segment(&mut segments, start, prev);
start = value;
prev = value;
}
push_segment(&mut segments, start, prev);
segments
}
fn push_segment(segments: &mut Vec<ConsecutiveSegment>, start: u32, end: u32) {
if start == end {
segments.push(ConsecutiveSegment::Single(start));
} else {
segments.push(ConsecutiveSegment::Range { start, end });
}
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing,
clippy::todo,
clippy::unimplemented,
clippy::unreachable,
clippy::get_unwrap,
reason = "Panicking is acceptable and often desired in tests."
)]
mod tests {
use super::*;
#[test]
fn test_consecutive_segments() {
for (input, expected) in [
(&[][..], vec![]),
(&[1][..], vec![ConsecutiveSegment::Single(1)]),
(
&[1, 2, 3][..],
vec![ConsecutiveSegment::Range { start: 1, end: 3 }],
),
(
&[1, 3, 5][..],
vec![
ConsecutiveSegment::Single(1),
ConsecutiveSegment::Single(3),
ConsecutiveSegment::Single(5),
],
),
(
&[1, 2, 4, 5, 6, 8][..],
vec![
ConsecutiveSegment::Range { start: 1, end: 2 },
ConsecutiveSegment::Range { start: 4, end: 6 },
ConsecutiveSegment::Single(8),
],
),
(
&[1, 1, 2, 2, 3][..],
vec![ConsecutiveSegment::Range { start: 1, end: 3 }],
),
(
&[3, 2, 1][..],
vec![
ConsecutiveSegment::Single(3),
ConsecutiveSegment::Single(2),
ConsecutiveSegment::Single(1),
],
),
] {
assert_eq!(consecutive_segments(input), expected);
}
}
}