Skip to content

Instantly share code, notes, and snippets.

@JustSimplyKyle
Last active July 11, 2024 16:42
Show Gist options
  • Save JustSimplyKyle/ef61e64e9d0cbf0b08e137b0065aad08 to your computer and use it in GitHub Desktop.
Save JustSimplyKyle/ef61e64e9d0cbf0b08e137b0065aad08 to your computer and use it in GitHub Desktop.
Calcualtor
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