use itertools::Itertools;
use pest::{
error::Error,
iterators::{Pair, Pairs},
Parser,
};
use pest_derive::Parser;
use std::{cmp::Ordering, collections::HashSet};
#[derive(Parser)]
#[grammar = "frame_format_grammar.pest"]
struct FrameSequenceParser;
pub fn parse_frame_sequence(input: &str) -> Result<Vec<isize>, Error<Rule>> {
FrameSequenceParser::parse(Rule::FrameSequenceString, input)
.map(|token_tree| remove_duplicates(frame_sequence_token_tree_to_frames(token_tree)))
}
fn chop(seq: &mut Vec<isize>, result: &mut Vec<isize>, elements: usize) {
if seq.len() < elements {
let mut new_seq = seq
.iter()
.tuple_windows()
.flat_map(|pair: (&isize, &isize)| {
let left = *pair.0;
let right = (*pair.0 + *pair.1) / 2;
if left < right {
result.push(right);
vec![left, right]
} else {
vec![left]
}
})
.collect::<Vec<_>>();
new_seq.push(*seq.last().unwrap());
if new_seq.len() < elements {
chop(&mut new_seq, result, elements);
}
*seq = new_seq;
}
}
pub(crate) fn binary_sequence(range: (isize, isize)) -> Vec<isize> {
match range.0.cmp(&range.1) {
Ordering::Less => {
let mut seq = vec![range.0, range.1];
let mut result = seq.clone();
chop(&mut seq, &mut result, (range.1 - range.0) as _);
result
}
Ordering::Greater => {
let mut seq = vec![range.1, range.0];
let mut result = seq.clone();
chop(&mut seq, &mut result, (range.0 - range.1) as _);
result.reverse();
result
}
Ordering::Equal => vec![range.0],
}
}
fn frame_to_number(frame: Pair<Rule>) -> isize {
frame.as_str().parse::<isize>().unwrap()
}
fn frame_sequence_token_tree_to_frames(pairs: Pairs<Rule>) -> Vec<isize> {
pairs
.into_iter()
.flat_map(|pair| {
match pair.as_rule() {
Rule::FrameSequenceString | Rule::FrameSequence | Rule::FrameSequencePart => {
frame_sequence_token_tree_to_frames(pair.into_inner())
}
Rule::FrameRange => {
let mut pairs = pair.into_inner();
let left = frame_to_number(pairs.next().unwrap());
let right = frame_to_number(pairs.next().unwrap());
if pairs.next().is_some() {
let pair = pairs.next().unwrap();
match pair.as_rule() {
Rule::PositiveNumber => {
let step = frame_to_number(pair);
match left.cmp(&right) {
Ordering::Less => {
(left..right + 1).step_by(step as _).collect::<Vec<_>>()
}
Ordering::Greater => (right..left + 1)
.rev()
.step_by(step as _)
.collect::<Vec<_>>(),
Ordering::Equal => vec![left],
}
}
Rule::BinarySequenceSymbol => binary_sequence((left, right)),
_ => unreachable!(),
}
} else if left < right {
(left..right + 1).collect::<Vec<_>>()
} else if right < left {
(right..left + 1).rev().collect::<Vec<_>>()
}
else {
vec![left]
}
}
Rule::Frame => vec![frame_to_number(pair)],
_ => vec![],
}
})
.collect::<Vec<_>>()
}
fn remove_duplicates(elements: Vec<isize>) -> Vec<isize> {
let mut set = HashSet::<isize>::new();
elements
.iter()
.filter_map(|e| {
if set.contains(e) {
None
} else {
set.insert(*e);
Some(*e)
}
})
.collect()
}
#[cfg(test)]
mod tests {
#[test]
fn test_individual_frames() {
use crate::parse_frame_sequence;
let frames = parse_frame_sequence("1,2,3,5,8,13").unwrap();
assert_eq!([1, 2, 3, 5, 8, 13], frames.as_slice());
}
#[test]
fn test_frame_sequence() {
use crate::parse_frame_sequence;
let frames = parse_frame_sequence("10-15").unwrap();
assert_eq!([10, 11, 12, 13, 14, 15], frames.as_slice());
}
#[test]
fn test_fram_sequence_with_step() {
use crate::parse_frame_sequence;
let frames = parse_frame_sequence("10-20@2").unwrap();
assert_eq!([10, 12, 14, 16, 18, 20], frames.as_slice());
}
#[test]
fn test_frame_sequence_with_step_reversed() {
use crate::parse_frame_sequence;
let frames = parse_frame_sequence("42-33@3").unwrap();
assert_eq!([42, 39, 36, 33], frames.as_slice());
}
#[test]
fn test_binary_frame_sequence() {
use crate::parse_frame_sequence;
let frames = parse_frame_sequence("10-20@b").unwrap();
assert_eq!(
[10, 20, 15, 12, 17, 11, 13, 16, 18, 14, 19],
frames.as_slice()
);
}
}