131 lines
4.2 KiB
Rust
131 lines
4.2 KiB
Rust
// Copyright 2022 Christian Ulrich
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use std::cmp::{Ordering, PartialOrd};
|
|
use std::io;
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub enum Token {
|
|
Integer(u64),
|
|
List(Vec<Token>),
|
|
}
|
|
|
|
pub struct PacketPairs {}
|
|
|
|
impl Token {
|
|
fn parse_integer(input: &str) -> Result<Token, &'static str> {
|
|
Ok(Self::Integer(
|
|
input.parse().map_err(|_| "cannot parse integer")?,
|
|
))
|
|
}
|
|
|
|
fn parse_list(input: &str) -> Result<Token, &'static str> {
|
|
let mut elements = Vec::new();
|
|
let mut element = String::new();
|
|
let mut bracket_counter = 0;
|
|
for c in input.chars() {
|
|
match c {
|
|
']' if { bracket_counter > 0 } => {
|
|
element.push(c);
|
|
bracket_counter -= 1;
|
|
}
|
|
'[' => {
|
|
element.push(c);
|
|
bracket_counter += 1;
|
|
}
|
|
',' if { bracket_counter == 0 } => {
|
|
elements.push(Self::from_str(&element)?);
|
|
element.clear();
|
|
}
|
|
_ => element.push(c),
|
|
}
|
|
}
|
|
if element.len() > 0 {
|
|
elements.push(Self::from_str(&element)?);
|
|
}
|
|
Ok(Self::List(elements))
|
|
}
|
|
|
|
pub fn from_str(input: &str) -> Result<Token, &'static str> {
|
|
let input = input.trim();
|
|
if input.starts_with('[') && input.ends_with(']') {
|
|
Self::parse_list(&input[1..input.len() - 1])
|
|
} else {
|
|
Self::parse_integer(input)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for Token {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
type T = Token;
|
|
|
|
match (self, other) {
|
|
(T::Integer(a), T::Integer(b)) => a.partial_cmp(b),
|
|
(T::List(a), T::List(b)) => {
|
|
let common_len = usize::min(a.len(), b.len());
|
|
for (left, right) in Iterator::zip(a[..common_len].iter(), b[..common_len].iter()) {
|
|
if left > right {
|
|
return Some(Ordering::Greater);
|
|
}
|
|
if left < right {
|
|
return Some(Ordering::Less);
|
|
}
|
|
}
|
|
if a.len() > common_len {
|
|
return Some(Ordering::Greater);
|
|
}
|
|
if b.len() > common_len {
|
|
return Some(Ordering::Less);
|
|
}
|
|
Some(Ordering::Equal)
|
|
}
|
|
(T::List(_), T::Integer(_)) => self.partial_cmp(&T::List(vec![other.clone()])),
|
|
(T::Integer(_), T::List(_)) => T::List(vec![self.clone()]).partial_cmp(other),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn packet_pairs() -> PacketPairs {
|
|
PacketPairs {}
|
|
}
|
|
|
|
impl Iterator for PacketPairs {
|
|
type Item = Result<(Token, Token), &'static str>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
const ERROR: &str = "cannot parse packet pair";
|
|
|
|
let left = match io::stdin().lines().next()?.unwrap().as_str() {
|
|
"" => return None,
|
|
line => Token::from_str(&line),
|
|
};
|
|
let right = match io::stdin().lines().next()?.unwrap().as_str() {
|
|
"" => return None,
|
|
line => Token::from_str(&line),
|
|
};
|
|
match io::stdin().lines().next()?.unwrap().as_str() {
|
|
"" => match (left, right) {
|
|
(Ok(left), Ok(right)) => Some(Ok((left, right))),
|
|
e => {
|
|
println!("{:?}", e);
|
|
Some(Err(ERROR))
|
|
}
|
|
},
|
|
_ => Some(Err(ERROR)),
|
|
}
|
|
}
|
|
}
|