Last active
July 11, 2024 16:42
-
-
Save JustSimplyKyle/ef61e64e9d0cbf0b08e137b0065aad08 to your computer and use it in GitHub Desktop.
Calcualtor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::fmt; | |
#[derive(Debug, Clone, Copy, PartialEq)] | |
pub struct Character { | |
pub mode: Option<CharacterMode>, | |
} | |
impl Character { | |
fn exists(&self) -> bool { | |
self.mode.is_some() | |
} | |
} | |
#[derive(Debug, Clone, Copy, PartialEq)] | |
pub enum CharacterMode { | |
Number(f64), | |
Parenthesis(ParenthesisType), | |
Arithmetic(ArithmeticType), | |
} | |
impl Character { | |
fn new(mode: CharacterMode) -> Self { | |
Self { mode: Some(mode) } | |
} | |
fn empty() -> Self { | |
Self { mode: None } | |
} | |
fn is_some(&self) -> bool { | |
self.mode.is_some() | |
} | |
#[must_use] | |
fn is_opening(&self) -> bool { | |
self.mode == Some(CharacterMode::Parenthesis(ParenthesisType::Opening)) | |
} | |
#[must_use] | |
fn is_closing(&self) -> bool { | |
self.mode == Some(CharacterMode::Parenthesis(ParenthesisType::Closing)) | |
} | |
#[must_use] | |
fn is_parenthesis(&self) -> bool { | |
matches!(self.mode, Some(CharacterMode::Parenthesis(_))) | |
} | |
#[must_use] | |
fn is_number(&self) -> bool { | |
matches!(self.mode, Some(CharacterMode::Number(_))) | |
} | |
#[must_use] | |
fn is_arithmetic(&self) -> bool { | |
matches!(self.mode, Some(CharacterMode::Arithmetic(_))) | |
} | |
} | |
impl TryFrom<String> for Character { | |
type Error = String; | |
fn try_from(chars: String) -> Result<Self, Self::Error> { | |
if let Ok(x) = chars.parse::<f64>() { | |
return Ok(Self::new(CharacterMode::Number(x))); | |
} | |
if chars.chars().count() == 1 { | |
let ch = chars.chars().next().unwrap(); | |
if let Ok(val) = ArithmeticType::try_from(ch) { | |
return Ok(Self::new(CharacterMode::Arithmetic(val))); | |
}; | |
if let Ok(val) = ParenthesisType::try_from(ch) { | |
return Ok(Self::new(CharacterMode::Parenthesis(val))); | |
}; | |
} | |
Err(String::from("not supported arithmetic nor number")) | |
} | |
} | |
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | |
pub enum ParenthesisType { | |
Opening, | |
Closing, | |
} | |
impl std::fmt::Display for ParenthesisType { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
write!( | |
f, | |
"{}", | |
match self { | |
Self::Opening => "(", | |
Self::Closing => ")", | |
} | |
) | |
} | |
} | |
impl TryFrom<char> for ParenthesisType { | |
type Error = char; | |
fn try_from(c: char) -> Result<Self, Self::Error> { | |
match c { | |
'(' => Ok(Self::Opening), | |
')' => Ok(Self::Closing), | |
x => Err(x), | |
} | |
} | |
} | |
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | |
pub enum ArithmeticType { | |
Add, | |
Mul, | |
Sub, | |
Div, | |
} | |
impl TryFrom<char> for ArithmeticType { | |
type Error = char; | |
fn try_from(c: char) -> Result<Self, Self::Error> { | |
match c { | |
'+' => Ok(Self::Add), | |
'-' => Ok(Self::Sub), | |
'*' => Ok(Self::Mul), | |
'/' => Ok(Self::Div), | |
x => Err(x), | |
} | |
} | |
} | |
fn parse_input_to_characters(input: &str) -> Vec<Character> { | |
let not_arithmetic = | |
|x| ArithmeticType::try_from(x).is_err() && ParenthesisType::try_from(x).is_err(); | |
input | |
.chars() | |
.collect::<Vec<_>>() | |
.chunk_by(|a, b| not_arithmetic(*a) && not_arithmetic(*b)) | |
.map(|x| x.into_iter().collect::<String>()) | |
.filter_map(|x| Character::try_from(x).ok()) | |
.collect::<Vec<_>>() | |
} | |
fn evaluate_expression(mut chars: Vec<Character>) -> Vec<Character> { | |
for &op in &[ | |
ArithmeticType::Mul, | |
ArithmeticType::Div, | |
ArithmeticType::Add, | |
ArithmeticType::Sub, | |
] { | |
parse_single_arithmetic_calculation(&mut chars, op); | |
} | |
chars | |
} | |
fn parse_single_arithmetic_calculation(chars: &mut Vec<Character>, op: ArithmeticType) { | |
chars.retain(Character::is_some); | |
let locations: Vec<usize> = chars | |
.iter() | |
.enumerate() | |
.filter(|(_, x)| x.is_arithmetic()) | |
.map(|(i, _)| i) | |
.collect(); | |
for &i in &locations { | |
if i == 0 || i == chars.len() - 1 { | |
continue; | |
} | |
if let ( | |
Some(CharacterMode::Number(lhs)), | |
Some(CharacterMode::Arithmetic(mhs)), | |
Some(CharacterMode::Number(rhs)), | |
) = (chars[i - 1].mode, chars[i].mode, chars[i + 1].mode) | |
{ | |
if mhs == op { | |
chars[i + 1].mode = Some(CharacterMode::Number(match op { | |
ArithmeticType::Add => lhs + rhs, | |
ArithmeticType::Sub => lhs - rhs, | |
ArithmeticType::Mul => lhs * rhs, | |
ArithmeticType::Div => lhs / rhs, | |
})); | |
chars[i] = Character::empty(); | |
chars[i - 1] = Character::empty(); | |
} | |
} | |
} | |
chars.retain(Character::is_some); | |
} | |
fn main() { | |
// let input = std::io::stdin().lines().flatten().next().unwrap_or_default(); | |
let input = "(((8*(2-3))*4)/2)".to_string(); | |
let mut characters = parse_input_to_characters(&input); | |
handle_parenthesis(&mut characters, 0); | |
let characters = evaluate_expression(characters); | |
dbg!(&characters); | |
println!("ans: {:?}", characters[0].mode.unwrap()); | |
} | |
fn print_chars(s: &Vec<Character>) { | |
for x in s.iter().filter(|x| x.is_some()) { | |
let str = match x.mode.unwrap() { | |
CharacterMode::Number(x) => { | |
&*x.to_string() | |
} | |
CharacterMode::Arithmetic(x) => { | |
match x { | |
ArithmeticType::Add => "+", | |
ArithmeticType::Sub => "-", | |
ArithmeticType::Mul => "*", | |
ArithmeticType::Div => "/" | |
} | |
} | |
CharacterMode::Parenthesis(x) => { | |
match x { | |
ParenthesisType::Opening => "(", | |
ParenthesisType::Closing => ")" | |
} | |
} | |
}; | |
print!("{str}"); | |
} | |
println!(); | |
} | |
fn handle_parenthesis(chars: &mut Vec<Character>, start: usize) -> usize { | |
let opener = chars | |
.iter() | |
.enumerate() | |
.skip(start) | |
.skip_while(|(_, x)| !x.is_opening()); | |
let parenthesis_finder = opener | |
.scan(0, |state, x| { | |
if x.1.is_opening() { | |
*state += 1; | |
} else if x.1.is_closing() { | |
if *state == 1 { | |
*state -= 1; | |
return Some(x); | |
} else { | |
*state -= 1; | |
} | |
}; | |
if *state > 0 { | |
Some(x) | |
} else { | |
None | |
} | |
}); | |
print_chars(&*chars); | |
let parenthesis_count = parenthesis_finder | |
.clone() | |
.filter(|x| x.1.is_parenthesis()) | |
.count(); | |
let mut opening_parenthesis_iter = parenthesis_finder.clone().filter(|x| x.1.is_opening()); | |
let closing_parenthesis_iter = parenthesis_finder.filter(|x| x.1.is_closing()); | |
let closing_pos = closing_parenthesis_iter.last().map(|x| x.0); | |
let opening_pos = opening_parenthesis_iter.next().map(|x| x.0); | |
if parenthesis_count == 0 { | |
return 0; | |
} | |
if let (Some(opening_pos), Some(closing_pos)) = (opening_pos, closing_pos) { | |
if parenthesis_count == 2 { | |
print!("sub expression: "); | |
let sub = (&chars[opening_pos + 1..closing_pos]).to_vec(); | |
let ans = evaluate_expression(sub); | |
for i in opening_pos..=closing_pos { | |
chars[i] = Character::empty(); | |
} | |
chars[opening_pos] = ans[0]; | |
return handle_parenthesis(chars, 0); | |
} | |
if parenthesis_count > 2 { | |
if let Some(next) = opening_parenthesis_iter.next() { | |
return handle_parenthesis(chars, next.0); | |
} | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment