diff --git a/src/CalcManager/CEngine/CalcInput.rs b/src/CalcManager/CEngine/CalcInput.rs new file mode 100644 index 00000000..d57fd1c2 --- /dev/null +++ b/src/CalcManager/CEngine/CalcInput.rs @@ -0,0 +1,270 @@ +use std::cmp::Ordering; + +const MAX_STRLEN: usize = 84; +const C_NUM_MAX_DIGITS: usize = MAX_STRLEN; +const C_EXP_MAX_DIGITS: usize = 4; + +pub struct CalcNumSec { + value: String, + is_negative: bool, +} + +impl CalcNumSec { + pub fn new() -> Self { + Self { + value: String::new(), + is_negative: false, + } + } + + pub fn clear(&mut self) { + self.value.clear(); + self.is_negative = false; + } + + pub fn is_empty(&self) -> bool { + self.value.is_empty() + } + + pub fn is_negative(&self) -> bool { + self.is_negative + } + + pub fn set_negative(&mut self, is_negative: bool) { + self.is_negative = is_negative; + } +} + +pub struct CalcInput { + base: CalcNumSec, + exponent: CalcNumSec, + has_exponent: bool, + has_decimal: bool, + dec_pt_index: usize, + dec_symbol: char, +} + +impl CalcInput { + pub fn new(dec_symbol: char) -> Self { + Self { + base: CalcNumSec::new(), + exponent: CalcNumSec::new(), + has_exponent: false, + has_decimal: false, + dec_pt_index: 0, + dec_symbol, + } + } + + pub fn clear(&mut self) { + self.base.clear(); + self.exponent.clear(); + self.has_exponent = false; + self.has_decimal = false; + self.dec_pt_index = 0; + } + + pub fn try_toggle_sign(&mut self, is_integer_mode: bool, max_num_str: &str) -> bool { + if self.base.is_empty() { + self.base.set_negative(false); + self.exponent.set_negative(false); + } else if self.has_exponent { + self.exponent.set_negative(!self.exponent.is_negative()); + } else { + if is_integer_mode && self.base.is_negative() { + if self.base.value.len() >= max_num_str.len() && self.base.value.chars().last() > max_num_str.chars().last() { + return false; + } + } + self.base.set_negative(!self.base.is_negative()); + } + true + } + + pub fn try_add_digit(&mut self, value: u32, radix: u32, is_integer_mode: bool, max_num_str: &str, word_bit_width: i32, max_digits: usize) -> bool { + let ch_digit = if value < 10 { + (b'0' + value as u8) as char + } else { + (b'A' + value as u8 - 10) as char + }; + + let (p_num_sec, max_count) = if self.has_exponent { + (&mut self.exponent, C_EXP_MAX_DIGITS) + } else { + let mut max_count = max_digits; + if self.has_decimal { + max_count += 1; + } + if !self.base.is_empty() && self.base.value.starts_with('0') { + max_count += 1; + } + (&mut self.base, max_count) + }; + + if p_num_sec.is_empty() && value == 0 { + return true; + } + + if p_num_sec.value.len() < max_count { + p_num_sec.value.push(ch_digit); + return true; + } + + if is_integer_mode && p_num_sec.value.len() == max_count && !self.has_exponent { + let allow_extra_digit = match radix { + 8 => match word_bit_width % 3 { + 1 => p_num_sec.value.starts_with('1'), + 2 => p_num_sec.value.starts_with(|c| c <= '3'), + _ => false, + }, + 10 => { + if p_num_sec.value.len() < max_num_str.len() { + match p_num_sec.value.cmp(&max_num_str[..p_num_sec.value.len()]) { + Ordering::Less => true, + Ordering::Equal => { + let last_char = max_num_str.chars().nth(p_num_sec.value.len()).unwrap(); + ch_digit <= last_char || (p_num_sec.is_negative() && ch_digit <= last_char + 1) + } + Ordering::Greater => false, + } + } else { + false + } + } + _ => false, + }; + + if allow_extra_digit { + p_num_sec.value.push(ch_digit); + return true; + } + } + + false + } + + pub fn try_add_decimal_pt(&mut self) -> bool { + if self.has_decimal || self.has_exponent { + return false; + } + + if self.base.is_empty() { + self.base.value.push('0'); + } + + self.dec_pt_index = self.base.value.len(); + self.base.value.push(self.dec_symbol); + self.has_decimal = true; + + true + } + + pub fn has_decimal_pt(&self) -> bool { + self.has_decimal + } + + pub fn try_begin_exponent(&mut self) -> bool { + self.try_add_decimal_pt(); + + if self.has_exponent { + return false; + } + + self.has_exponent = true; + true + } + + pub fn backspace(&mut self) { + if self.has_exponent { + if !self.exponent.is_empty() { + self.exponent.value.pop(); + if self.exponent.is_empty() { + self.exponent.clear(); + } + } else { + self.has_exponent = false; + } + } else { + if !self.base.is_empty() { + self.base.value.pop(); + if self.base.value == "0" { + self.base.value.pop(); + } + } + + if self.base.value.len() <= self.dec_pt_index { + self.has_decimal = false; + self.dec_pt_index = 0; + } + + if self.base.is_empty() { + self.base.clear(); + } + } + } + + pub fn set_decimal_symbol(&mut self, dec_symbol: char) { + if self.dec_symbol != dec_symbol { + self.dec_symbol = dec_symbol; + + if self.has_decimal { + self.base.value.replace_range(self.dec_pt_index..=self.dec_pt_index, &dec_symbol.to_string()); + } + } + } + + pub fn is_empty(&self) -> bool { + self.base.is_empty() && !self.has_exponent && self.exponent.is_empty() && !self.has_decimal + } + + pub fn to_string(&self, radix: u32) -> String { + if self.base.value.len() > MAX_STRLEN || (self.has_exponent && self.exponent.value.len() > MAX_STRLEN) { + return String::new(); + } + + let mut result = String::new(); + + if self.base.is_negative() { + result.push('-'); + } + + if self.base.is_empty() { + result.push('0'); + } else { + result.push_str(&self.base.value); + } + + if self.has_exponent { + if !self.has_decimal { + result.push(self.dec_symbol); + } + + result.push(if radix == 10 { 'e' } else { '^' }); + result.push(if self.exponent.is_negative() { '-' } else { '+' }); + + if self.exponent.is_empty() { + result.push('0'); + } else { + result.push_str(&self.exponent.value); + } + } + + if result.len() > C_NUM_MAX_DIGITS * 2 + 4 { + return String::new(); + } + + result + } + + pub fn to_rational(&self, radix: u32, precision: i32) -> Rational { + let rat = string_to_rat(self.base.is_negative(), &self.base.value, self.exponent.is_negative(), &self.exponent.value, radix, precision); + if rat.is_none() { + return Rational::default(); + } + + let result = Rational::from_rat(rat.unwrap()); + destroy_rat(rat.unwrap()); + + result + } +} diff --git a/src/CalcManager/CEngine/History.rs b/src/CalcManager/CEngine/History.rs new file mode 100644 index 00000000..d448c570 --- /dev/null +++ b/src/CalcManager/CEngine/History.rs @@ -0,0 +1,292 @@ +use std::sync::{Arc, Mutex}; +use std::collections::VecDeque; + +use crate::calc_engine::CCalcEngine; +use crate::calc_display::ICalcDisplay; +use crate::calc_history::IHistoryDisplay; +use crate::calc_utils::Rational; + +const MAXPRECDEPTH: usize = 25; + +pub struct CHistoryCollector { + p_history_display: Option>, + p_calc_display: Option>, + i_cur_line_hist_start: i32, + last_op_start_index: i32, + last_bin_op_start_index: i32, + operand_indices: [i32; MAXPRECDEPTH], + cur_operand_index: i32, + b_last_opnd_brace: bool, + decimal_symbol: char, + sp_tokens: Option>>>, + sp_commands: Option>>>>, +} + +impl CHistoryCollector { + pub fn new( + p_calc_display: Option>, + p_history_display: Option>, + decimal_symbol: char, + ) -> Self { + Self { + p_history_display, + p_calc_display, + i_cur_line_hist_start: -1, + last_op_start_index: -1, + last_bin_op_start_index: -1, + operand_indices: [-1; MAXPRECDEPTH], + cur_operand_index: 0, + b_last_opnd_brace: false, + decimal_symbol, + sp_tokens: None, + sp_commands: None, + } + } + + pub fn add_opnd_to_history(&mut self, num_str: &str, rat: &Rational, f_repetition: bool) { + let i_command_end = self.add_command(self.get_operand_commands_from_string(num_str, rat)); + self.last_op_start_index = self.ich_add_sz_to_equation_sz(num_str, i_command_end); + + if f_repetition { + self.set_expression_display(); + } + self.b_last_opnd_brace = false; + self.last_bin_op_start_index = -1; + } + + pub fn remove_last_opnd_from_history(&mut self) { + self.truncate_equation_sz_from_ich(self.last_op_start_index); + self.set_expression_display(); + self.last_op_start_index = -1; + } + + pub fn add_bin_op_to_history(&mut self, n_op_code: i32, is_integer_mode: bool, f_no_repetition: bool) { + let i_command_end = self.add_command(Arc::new(CBinaryCommand::new(n_op_code))); + self.last_bin_op_start_index = self.ich_add_sz_to_equation_sz(" ", -1); + + self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_binary_string(n_op_code, is_integer_mode), i_command_end); + self.ich_add_sz_to_equation_sz(" ", -1); + + if f_no_repetition { + self.set_expression_display(); + } + self.last_op_start_index = -1; + } + + pub fn change_last_bin_op(&mut self, n_op_code: i32, f_prec_inv_to_higher: bool, is_integer_mode: bool) { + self.truncate_equation_sz_from_ich(self.last_bin_op_start_index); + if f_prec_inv_to_higher { + self.enclose_prec_inversion_brackets(); + } + self.add_bin_op_to_history(n_op_code, is_integer_mode, true); + } + + pub fn push_last_opnd_start(&mut self, ich_opnd_start: i32) { + let ich = if ich_opnd_start == -1 { + self.last_op_start_index + } else { + ich_opnd_start + }; + + if self.cur_operand_index < self.operand_indices.len() as i32 { + self.operand_indices[self.cur_operand_index as usize] = ich; + self.cur_operand_index += 1; + } + } + + pub fn pop_last_opnd_start(&mut self) { + if self.cur_operand_index > 0 { + self.cur_operand_index -= 1; + self.last_op_start_index = self.operand_indices[self.cur_operand_index as usize]; + } + } + + pub fn add_open_brace_to_history(&mut self) { + self.add_command(Arc::new(CParentheses::new(IDC_OPENP))); + let ich_opnd_start = self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_string(IDC_OPENP), -1); + self.push_last_opnd_start(ich_opnd_start); + + self.set_expression_display(); + self.last_bin_op_start_index = -1; + } + + pub fn add_close_brace_to_history(&mut self) { + self.add_command(Arc::new(CParentheses::new(IDC_CLOSEP))); + self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_string(IDC_CLOSEP), -1); + self.set_expression_display(); + self.pop_last_opnd_start(); + + self.last_bin_op_start_index = -1; + self.b_last_opnd_brace = true; + } + + pub fn enclose_prec_inversion_brackets(&mut self) { + let ich_start = if self.cur_operand_index > 0 { + self.operand_indices[self.cur_operand_index as usize - 1] + } else { + 0 + }; + + self.insert_sz_in_equation_sz(&CCalcEngine::op_code_to_string(IDC_OPENP), -1, ich_start); + self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_string(IDC_CLOSEP), -1); + } + + pub fn f_opnd_added_to_history(&self) -> bool { + self.last_op_start_index != -1 + } + + pub fn complete_history_line(&mut self, num_str: &str) { + if let Some(p_history_display) = &self.p_history_display { + let added_item_index = p_history_display.add_to_history( + self.sp_tokens.clone().unwrap(), + self.sp_commands.clone().unwrap(), + num_str, + ); + if let Some(p_calc_display) = &self.p_calc_display { + p_calc_display.on_history_item_added(added_item_index); + } + } + + self.sp_tokens = None; + self.sp_commands = None; + self.i_cur_line_hist_start = -1; + self.reinit_history(); + } + + pub fn complete_equation(&mut self, num_str: &str) { + self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_string(IDC_EQU), -1); + self.set_expression_display(); + self.complete_history_line(num_str); + } + + pub fn clear_history_line(&mut self, err_str: &str) { + if err_str.is_empty() { + if let Some(p_calc_display) = &self.p_calc_display { + p_calc_display.set_expression_display( + Arc::new(Mutex::new(VecDeque::new())), + Arc::new(Mutex::new(VecDeque::new())), + ); + } + self.i_cur_line_hist_start = -1; + self.reinit_history(); + } + } + + fn ich_add_sz_to_equation_sz(&mut self, str: &str, i_command_index: i32) -> i32 { + if self.sp_tokens.is_none() { + self.sp_tokens = Some(Arc::new(Mutex::new(VecDeque::new()))); + } + + let mut sp_tokens = self.sp_tokens.as_ref().unwrap().lock().unwrap(); + sp_tokens.push_back((str.to_string(), i_command_index)); + (sp_tokens.len() - 1) as i32 + } + + fn insert_sz_in_equation_sz(&mut self, str: &str, i_command_index: i32, ich: i32) { + let mut sp_tokens = self.sp_tokens.as_ref().unwrap().lock().unwrap(); + sp_tokens.insert(ich as usize, (str.to_string(), i_command_index)); + } + + fn truncate_equation_sz_from_ich(&mut self, ich: i32) { + let mut sp_tokens = self.sp_tokens.as_ref().unwrap().lock().unwrap(); + let mut sp_commands = self.sp_commands.as_ref().unwrap().lock().unwrap(); + + let mut min_idx = -1; + let n_tokens = sp_tokens.len(); + + for i in ich as usize..n_tokens { + let cur_token_id = sp_tokens[i].1; + if cur_token_id != -1 { + if min_idx == -1 || cur_token_id < min_idx { + min_idx = cur_token_id; + sp_commands.truncate(min_idx as usize); + } + } + } + + sp_tokens.truncate(ich as usize); + } + + fn set_expression_display(&self) { + if let Some(p_calc_display) = &self.p_calc_display { + p_calc_display.set_expression_display( + self.sp_tokens.clone().unwrap(), + self.sp_commands.clone().unwrap(), + ); + } + } + + fn add_command(&mut self, sp_command: Arc) -> i32 { + if self.sp_commands.is_none() { + self.sp_commands = Some(Arc::new(Mutex::new(VecDeque::new()))); + } + + let mut sp_commands = self.sp_commands.as_ref().unwrap().lock().unwrap(); + sp_commands.push_back(sp_command); + (sp_commands.len() - 1) as i32 + } + + pub fn update_history_expression(&mut self, radix: u32, precision: i32) { + if self.sp_tokens.is_none() { + return; + } + + let mut sp_tokens = self.sp_tokens.as_ref().unwrap().lock().unwrap(); + let sp_commands = self.sp_commands.as_ref().unwrap().lock().unwrap(); + + for token in sp_tokens.iter_mut() { + let command_position = token.1; + if command_position != -1 { + let exp_command = &sp_commands[command_position as usize]; + if exp_command.get_command_type() == CommandType::OperandCommand { + let opnd_command = exp_command.as_any().downcast_ref::().unwrap(); + token.0 = opnd_command.get_string(radix, precision); + opnd_command.set_commands(self.get_operand_commands_from_string(&token.0)); + } + } + } + + self.set_expression_display(); + } + + pub fn set_decimal_symbol(&mut self, decimal_symbol: char) { + self.decimal_symbol = decimal_symbol; + } + + fn get_operand_commands_from_string(&self, num_str: &str) -> Arc { + let mut commands = Vec::new(); + let f_negative = num_str.starts_with('-'); + + for ch in num_str.chars().skip(if f_negative { 1 } else { 0 }) { + match ch { + ch if ch == self.decimal_symbol => commands.push(IDC_PNT), + 'e' => commands.push(IDC_EXP), + '-' => commands.push(IDC_SIGN), + '+' => {} + ch => { + let num = ch as i32 - '0' as i32; + commands.push(num + IDC_0); + } + } + } + + if f_negative { + commands.push(IDC_SIGN); + } + + Arc::new(COpndCommand::new(commands, f_negative, num_str.contains(self.decimal_symbol), num_str.contains('e'))) + } + + fn reinit_history(&mut self) { + self.last_op_start_index = -1; + self.last_bin_op_start_index = -1; + self.cur_operand_index = 0; + self.b_last_opnd_brace = false; + if let Some(sp_tokens) = &self.sp_tokens { + sp_tokens.lock().unwrap().clear(); + } + if let Some(sp_commands) = &self.sp_commands { + sp_commands.lock().unwrap().clear(); + } + } +} diff --git a/src/CalcManager/CEngine/Number.rs b/src/CalcManager/CEngine/Number.rs new file mode 100644 index 00000000..b6001616 --- /dev/null +++ b/src/CalcManager/CEngine/Number.rs @@ -0,0 +1,79 @@ +use std::cmp::Ordering; + +pub struct Number { + sign: i32, + exp: i32, + mantissa: Vec, +} + +impl Number { + pub fn new() -> Self { + Self { + sign: 1, + exp: 0, + mantissa: vec![0], + } + } + + pub fn with_values(sign: i32, exp: i32, mantissa: Vec) -> Self { + Self { sign, exp, mantissa } + } + + pub fn from_pnumber(p: &PNUMBER) -> Self { + let mut mantissa = Vec::with_capacity(p.cdigit as usize); + mantissa.extend_from_slice(&p.mant[..p.cdigit as usize]); + Self { + sign: p.sign, + exp: p.exp, + mantissa, + } + } + + pub fn to_pnumber(&self) -> PNUMBER { + let mut ret = PNUMBER::new(self.mantissa.len() + 1); + ret.sign = self.sign; + ret.exp = self.exp; + ret.cdigit = self.mantissa.len() as i32; + ret.mant[..self.mantissa.len()].copy_from_slice(&self.mantissa); + ret + } + + pub fn sign(&self) -> i32 { + self.sign + } + + pub fn exp(&self) -> i32 { + self.exp + } + + pub fn mantissa(&self) -> &Vec { + &self.mantissa + } + + pub fn is_zero(&self) -> bool { + self.mantissa.iter().all(|&x| x == 0) + } +} + +impl PartialEq for Number { + fn eq(&self, other: &Self) -> bool { + self.sign == other.sign && self.exp == other.exp && self.mantissa == other.mantissa + } +} + +impl Eq for Number {} + +impl PartialOrd for Number { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Number { + fn cmp(&self, other: &Self) -> Ordering { + self.sign + .cmp(&other.sign) + .then_with(|| self.exp.cmp(&other.exp)) + .then_with(|| self.mantissa.cmp(&other.mantissa)) + } +} diff --git a/src/CalcManager/CEngine/Rational.rs b/src/CalcManager/CEngine/Rational.rs new file mode 100644 index 00000000..20d8ff73 --- /dev/null +++ b/src/CalcManager/CEngine/Rational.rs @@ -0,0 +1,378 @@ +use std::cmp::Ordering; + +pub struct Rational { + p: Number, + q: Number, +} + +impl Rational { + pub fn new() -> Self { + Self { + p: Number::new(), + q: Number::with_values(1, 0, vec![1]), + } + } + + pub fn from_number(n: &Number) -> Self { + let mut q_exp = 0; + if n.exp() < 0 { + q_exp -= n.exp(); + } + + Self { + p: Number::with_values(n.sign(), 0, n.mantissa().clone()), + q: Number::with_values(1, q_exp, vec![1]), + } + } + + pub fn with_values(p: Number, q: Number) -> Self { + Self { p, q } + } + + pub fn from_i32(i: i32) -> Self { + let prat = i32torat(i); + let p = Number::from_pnumber(&prat.pp); + let q = Number::from_pnumber(&prat.pq); + destroyrat(prat); + Self { p, q } + } + + pub fn from_u32(ui: u32) -> Self { + let prat = ui32torat(ui); + let p = Number::from_pnumber(&prat.pp); + let q = Number::from_pnumber(&prat.pq); + destroyrat(prat); + Self { p, q } + } + + pub fn from_u64(ui: u64) -> Self { + let hi = (ui >> 32) as u32; + let lo = ui as u32; + let temp = Rational::from_u32(hi) << 32 | Rational::from_u32(lo); + Self { + p: temp.p, + q: temp.q, + } + } + + pub fn from_prat(prat: &PRAT) -> Self { + Self { + p: Number::from_pnumber(&prat.pp), + q: Number::from_pnumber(&prat.pq), + } + } + + pub fn to_prat(&self) -> PRAT { + let mut ret = PRAT::new(); + ret.pp = self.p.to_pnumber(); + ret.pq = self.q.to_pnumber(); + ret + } + + pub fn p(&self) -> &Number { + &self.p + } + + pub fn q(&self) -> &Number { + &self.q + } + + pub fn negate(&self) -> Self { + Self { + p: Number::with_values(-self.p.sign(), self.p.exp(), self.p.mantissa().clone()), + q: self.q.clone(), + } + } + + pub fn add_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + addrat(&mut lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn sub_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + subrat(&mut lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn mul_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + mulrat(&mut lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn div_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + divrat(&mut lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn rem_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + remrat(&mut lhs_rat, &rhs_rat); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn shl_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + lshrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn shr_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + rshrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn and_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + andrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn or_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + orrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn xor_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + xorrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn to_string(&self, radix: u32, fmt: NumberFormat, precision: i32) -> String { + let rat = self.to_prat(); + let result = rat_to_string(&rat, fmt, radix, precision); + destroyrat(rat); + result + } + + pub fn to_u64(&self) -> u64 { + let rat = self.to_prat(); + let result = rat_to_u64(&rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rat); + result + } +} + +impl PartialEq for Rational { + fn eq(&self, other: &Self) -> bool { + let lhs_rat = self.to_prat(); + let rhs_rat = other.to_prat(); + let result = rat_equ(&lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(lhs_rat); + destroyrat(rhs_rat); + result + } +} + +impl Eq for Rational {} + +impl PartialOrd for Rational { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Rational { + fn cmp(&self, other: &Self) -> Ordering { + let lhs_rat = self.to_prat(); + let rhs_rat = other.to_prat(); + let result = rat_cmp(&lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(lhs_rat); + destroyrat(rhs_rat); + result + } +} + +impl std::ops::AddAssign for Rational { + fn add_assign(&mut self, rhs: Self) { + self.add_assign(&rhs); + } +} + +impl std::ops::SubAssign for Rational { + fn sub_assign(&mut self, rhs: Self) { + self.sub_assign(&rhs); + } +} + +impl std::ops::MulAssign for Rational { + fn mul_assign(&mut self, rhs: Self) { + self.mul_assign(&rhs); + } +} + +impl std::ops::DivAssign for Rational { + fn div_assign(&mut self, rhs: Self) { + self.div_assign(&rhs); + } +} + +impl std::ops::RemAssign for Rational { + fn rem_assign(&mut self, rhs: Self) { + self.rem_assign(&rhs); + } +} + +impl std::ops::ShlAssign for Rational { + fn shl_assign(&mut self, rhs: Self) { + self.shl_assign(&rhs); + } +} + +impl std::ops::ShrAssign for Rational { + fn shr_assign(&mut self, rhs: Self) { + self.shr_assign(&rhs); + } +} + +impl std::ops::BitAndAssign for Rational { + fn bitand_assign(&mut self, rhs: Self) { + self.and_assign(&rhs); + } +} + +impl std::ops::BitOrAssign for Rational { + fn bitor_assign(&mut self, rhs: Self) { + self.or_assign(&rhs); + } +} + +impl std::ops::BitXorAssign for Rational { + fn bitxor_assign(&mut self, rhs: Self) { + self.xor_assign(&rhs); + } +} + +impl std::ops::Neg for Rational { + type Output = Self; + + fn neg(self) -> Self::Output { + self.negate() + } +} + +impl std::ops::Add for Rational { + type Output = Self; + + fn add(mut self, rhs: Self) -> Self::Output { + self.add_assign(rhs); + self + } +} + +impl std::ops::Sub for Rational { + type Output = Self; + + fn sub(mut self, rhs: Self) -> Self::Output { + self.sub_assign(rhs); + self + } +} + +impl std::ops::Mul for Rational { + type Output = Self; + + fn mul(mut self, rhs: Self) -> Self::Output { + self.mul_assign(rhs); + self + } +} + +impl std::ops::Div for Rational { + type Output = Self; + + fn div(mut self, rhs: Self) -> Self::Output { + self.div_assign(rhs); + self + } +} + +impl std::ops::Rem for Rational { + type Output = Self; + + fn rem(mut self, rhs: Self) -> Self::Output { + self.rem_assign(rhs); + self + } +} + +impl std::ops::Shl for Rational { + type Output = Self; + + fn shl(mut self, rhs: Self) -> Self::Output { + self.shl_assign(rhs); + self + } +} + +impl std::ops::Shr for Rational { + type Output = Self; + + fn shr(mut self, rhs: Self) -> Self::Output { + self.shr_assign(rhs); + self + } +} + +impl std::ops::BitAnd for Rational { + type Output = Self; + + fn bitand(mut self, rhs: Self) -> Self::Output { + self.and_assign(rhs); + self + } +} + +impl std::ops::BitOr for Rational { + type Output = Self; + + fn bitor(mut self, rhs: Self) -> Self::Output { + self.or_assign(rhs); + self + } +} + +impl std::ops::BitXor for Rational { + type Output = Self; + + fn bitxor(mut self, rhs: Self) -> Self::Output { + self.xor_assign(rhs); + self + } +} diff --git a/src/CalcManager/CEngine/calc.rs b/src/CalcManager/CEngine/calc.rs new file mode 100644 index 00000000..7654e12e --- /dev/null +++ b/src/CalcManager/CEngine/calc.rs @@ -0,0 +1,234 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use crate::calc_input::CalcInput; +use crate::calc_utils::{Rational, RationalMath}; +use crate::calc_display::ICalcDisplay; +use crate::calc_history::CHistoryCollector; +use crate::calc_resource::IResourceProvider; + +const DEFAULT_MAX_DIGITS: i32 = 32; +const DEFAULT_PRECISION: i32 = 32; +const DEFAULT_RADIX: i32 = 10; + +const DEFAULT_DEC_SEPARATOR: char = '.'; +const DEFAULT_GRP_SEPARATOR: char = ','; +const DEFAULT_GRP_STR: &str = "3;0"; +const DEFAULT_NUMBER_STR: &str = "0"; + +pub struct CCalcEngine { + f_precedence: bool, + f_integer_mode: bool, + p_calc_display: Option>, + resource_provider: Arc, + n_op_code: i32, + n_prev_op_code: i32, + b_change_op: bool, + b_record: bool, + b_set_calc_state: bool, + input: CalcInput, + n_fe: NumberFormat, + memory_value: Rational, + hold_val: Rational, + current_val: Rational, + last_val: Rational, + paren_vals: Vec, + precedence_vals: Vec, + b_error: bool, + b_inv: bool, + b_no_prev_equ: bool, + radix: i32, + precision: i32, + c_int_digits_sav: i32, + dec_grouping: Vec, + number_string: String, + n_temp_com: i32, + open_paren_count: usize, + n_op: Vec, + n_prec_op: Vec, + precedence_op_count: usize, + n_last_com: i32, + angletype: AngleType, + numwidth: NUM_WIDTH, + dw_word_bit_width: i32, + history_collector: CHistoryCollector, + group_separator: char, + chop_numbers: Vec, + max_decimal_value_strings: Vec, + decimal_separator: char, +} + +impl CCalcEngine { + pub fn new( + f_precedence: bool, + f_integer_mode: bool, + resource_provider: Arc, + p_calc_display: Option>, + p_history_display: Option>, + ) -> Self { + let mut engine = CCalcEngine { + f_precedence, + f_integer_mode, + p_calc_display: p_calc_display.clone(), + resource_provider: resource_provider.clone(), + n_op_code: 0, + n_prev_op_code: 0, + b_change_op: false, + b_record: false, + b_set_calc_state: false, + input: CalcInput::new(DEFAULT_DEC_SEPARATOR), + n_fe: NumberFormat::Float, + memory_value: Rational::default(), + hold_val: Rational::default(), + current_val: Rational::default(), + last_val: Rational::default(), + paren_vals: vec![Rational::default(); MAXPRECDEPTH], + precedence_vals: vec![Rational::default(); MAXPRECDEPTH], + b_error: false, + b_inv: false, + b_no_prev_equ: true, + radix: DEFAULT_RADIX, + precision: DEFAULT_PRECISION, + c_int_digits_sav: DEFAULT_MAX_DIGITS, + dec_grouping: vec![], + number_string: DEFAULT_NUMBER_STR.to_string(), + n_temp_com: 0, + open_paren_count: 0, + n_op: vec![0; MAXPRECDEPTH], + n_prec_op: vec![0; MAXPRECDEPTH], + precedence_op_count: 0, + n_last_com: 0, + angletype: AngleType::Degrees, + numwidth: NUM_WIDTH::QWORD_WIDTH, + dw_word_bit_width: 0, + history_collector: CHistoryCollector::new(p_calc_display, p_history_display, DEFAULT_DEC_SEPARATOR), + group_separator: DEFAULT_GRP_SEPARATOR, + chop_numbers: vec![Rational::default(); NUM_WIDTH_LENGTH], + max_decimal_value_strings: vec![String::new(); NUM_WIDTH_LENGTH], + decimal_separator: DEFAULT_DEC_SEPARATOR, + }; + + engine.init_chop_numbers(); + engine.dw_word_bit_width = engine.dw_word_bit_width_from_num_width(engine.numwidth); + engine.max_trigonometric_num = RationalMath::pow(10, 100); + engine.set_radix_type_and_num_width(RadixType::Decimal, engine.numwidth); + engine.settings_changed(); + engine.display_num(); + + engine + } + + fn init_chop_numbers(&mut self) { + self.chop_numbers[0] = Rational::from_rat(rat_qword); + self.chop_numbers[1] = Rational::from_rat(rat_dword); + self.chop_numbers[2] = Rational::from_rat(rat_word); + self.chop_numbers[3] = Rational::from_rat(rat_byte); + + for i in 0..self.chop_numbers.len() { + let max_val = self.chop_numbers[i] / 2; + let max_val = RationalMath::integer(max_val); + self.max_decimal_value_strings[i] = max_val.to_string(10, NumberFormat::Float, self.precision); + } + } + + fn get_chop_number(&self) -> Rational { + self.chop_numbers[self.numwidth as usize].clone() + } + + fn get_max_decimal_value_string(&self) -> String { + self.max_decimal_value_strings[self.numwidth as usize].clone() + } + + pub fn persisted_mem_object(&self) -> Rational { + self.memory_value.clone() + } + + pub fn set_persisted_mem_object(&mut self, mem_object: Rational) { + self.memory_value = mem_object; + } + + pub fn settings_changed(&mut self) { + let last_dec = self.decimal_separator; + let dec_str = self.resource_provider.get_cengine_string("sDecimal"); + self.decimal_separator = if dec_str.is_empty() { + DEFAULT_DEC_SEPARATOR + } else { + dec_str.chars().next().unwrap() + }; + + let last_sep = self.group_separator; + let sep_str = self.resource_provider.get_cengine_string("sThousand"); + self.group_separator = if sep_str.is_empty() { + DEFAULT_GRP_SEPARATOR + } else { + sep_str.chars().next().unwrap() + }; + + let last_dec_grouping = self.dec_grouping.clone(); + let grp_str = self.resource_provider.get_cengine_string("sGrouping"); + self.dec_grouping = if grp_str.is_empty() { + digit_grouping_string_to_grouping_vector(DEFAULT_GRP_STR) + } else { + digit_grouping_string_to_grouping_vector(&grp_str) + }; + + let mut num_changed = false; + + if self.dec_grouping != last_dec_grouping || self.group_separator != last_sep { + num_changed = true; + } + + if self.decimal_separator != last_dec { + self.input.set_decimal_symbol(self.decimal_separator); + self.history_collector.set_decimal_symbol(self.decimal_separator); + s_engine_strings.insert(SIDS_DECIMAL_SEPARATOR.to_string(), self.decimal_separator.to_string()); + num_changed = true; + } + + if num_changed { + self.display_num(); + } + } + + pub fn decimal_separator(&self) -> char { + self.decimal_separator + } + + pub fn get_history_collector_commands_snapshot(&self) -> Vec> { + let mut commands = self.history_collector.get_commands(); + if !self.history_collector.f_opnd_added_to_history() && self.b_record { + commands.push(self.history_collector.get_operand_commands_from_string(&self.number_string, &self.current_val)); + } + commands + } + + pub fn initial_one_time_only_setup(resource_provider: Arc) { + Self::load_engine_strings(resource_provider); + Self::change_base_constants(DEFAULT_RADIX, DEFAULT_MAX_DIGITS, DEFAULT_PRECISION); + } + + fn load_engine_strings(resource_provider: Arc) { + for sid in G_SIDS.iter() { + let loc_string = resource_provider.get_cengine_string(sid); + if !loc_string.is_empty() { + s_engine_strings.insert(sid.to_string(), loc_string); + } + } + } + + fn change_base_constants(radix: i32, max_int_digits: i32, precision: i32) { + // Implementation of ChangeBaseConstants + } + + fn display_num(&self) { + // Implementation of DisplayNum + } + + fn set_radix_type_and_num_width(&mut self, radix_type: RadixType, num_width: NUM_WIDTH) { + // Implementation of SetRadixTypeAndNumWidth + } + + fn dw_word_bit_width_from_num_width(&self, num_width: NUM_WIDTH) -> i32 { + // Implementation of DwWordBitWidthFromNumWidth + } +} diff --git a/src/CalcManager/CEngine/scicomm.rs b/src/CalcManager/CEngine/scicomm.rs new file mode 100644 index 00000000..5c58fc15 --- /dev/null +++ b/src/CalcManager/CEngine/scicomm.rs @@ -0,0 +1,774 @@ +use std::collections::HashMap; +use std::sync::Mutex; + +lazy_static! { + static ref ENGINE_STRINGS: Mutex> = Mutex::new(HashMap::new()); +} + +fn load_engine_strings(resource_provider: &dyn ResourceProvider) { + let sids = vec![ + "SIDS_SIND", "SIDS_ASIND", "SIDS_SINR", "SIDS_ASINR", "SIDS_SING", "SIDS_ASING", + "SIDS_COSD", "SIDS_ACOSD", "SIDS_COSR", "SIDS_ACOSR", "SIDS_COSG", "SIDS_ACOSG", + "SIDS_TAND", "SIDS_ATAND", "SIDS_TANR", "SIDS_ATANR", "SIDS_TANG", "SIDS_ATANG", + "SIDS_SQR", "SIDS_CUBE", "SIDS_FACT", "SIDS_RECIPROC", "SIDS_DEGREES", "SIDS_NEGATE", + "SIDS_TWOPOWX", "SIDS_LOGBASEY", "SIDS_ABS", "SIDS_CEIL", "SIDS_FLOOR", "SIDS_NAND", + "SIDS_NOR", "SIDS_RSH", "SIDS_ROR", "SIDS_ROL", "SIDS_CUBEROOT", "SIDS_MOD", + "SIDS_PROGRAMMER_MOD", "SIDS_FRAC", "SIDS_ASINH", "SIDS_ACOSH", "SIDS_ATANH", + "SIDS_SECH", "SIDS_ASECH", "SIDS_CSCH", "SIDS_ACSCH", "SIDS_COTH", "SIDS_ACOTH", + "SIDS_POWE" + ]; + + let mut engine_strings = ENGINE_STRINGS.lock().unwrap(); + for sid in sids { + if let Some(loc_string) = resource_provider.get_cengine_string(sid) { + engine_strings.insert(sid, loc_string); + } + } +} + +fn initial_one_time_only_setup(resource_provider: &dyn ResourceProvider) { + load_engine_strings(resource_provider); + change_base_constants(DEFAULT_RADIX, DEFAULT_MAX_DIGITS, DEFAULT_PRECISION); +} + +fn handle_error_command(idc: OpCode) { + if !is_gui_setting_op_code(idc) { + // We would have saved the prev command. Need to forget this state + TEMP_COM.store(LAST_COM.load(Ordering::SeqCst), Ordering::SeqCst); + } +} + +fn handle_max_digits_reached(calc_display: &dyn CalcDisplay) { + calc_display.max_digits_reached(); +} + +fn clear_temporary_values(calc_display: &dyn CalcDisplay) { + INV.store(false, Ordering::SeqCst); + INPUT.lock().unwrap().clear(); + RECORD.store(true, Ordering::SeqCst); + check_and_add_last_bin_op_to_history(); + display_num(calc_display); + ERROR.store(false, Ordering::SeqCst); +} + +fn clear_display(calc_display: &dyn CalcDisplay) { + calc_display.set_expression_display(vec![], vec![]); +} + +fn process_command(calc_display: &dyn CalcDisplay, resource_provider: &dyn ResourceProvider, w_param: OpCode) { + let mut w_param = w_param; + if w_param == OpCode::SetResult { + w_param = OpCode::Recall; + SET_CALC_STATE.store(true, Ordering::SeqCst); + } + + process_command_worker(calc_display, resource_provider, w_param); +} + +fn process_command_worker(calc_display: &dyn CalcDisplay, resource_provider: &dyn ResourceProvider, w_param: OpCode) { + if !is_gui_setting_op_code(w_param) { + LAST_COM.store(TEMP_COM.load(Ordering::SeqCst), Ordering::SeqCst); + TEMP_COM.store(w_param as i32, Ordering::SeqCst); + } + + if !NO_PREV_EQU.load(Ordering::SeqCst) { + clear_display(calc_display); + } + + if ERROR.load(Ordering::SeqCst) { + if w_param == OpCode::Clear { + // handle "C" normally + } else if w_param == OpCode::Centr { + // treat "CE" as "C" + w_param = OpCode::Clear; + } else { + handle_error_command(w_param); + return; + } + } + + if RECORD.load(Ordering::SeqCst) { + if is_bin_op_code(w_param) || is_unary_op_code(w_param) || is_op_in_range(w_param, OpCode::Fe, OpCode::MMinus) + || is_op_in_range(w_param, OpCode::OpenP, OpCode::CloseP) || is_op_in_range(w_param, OpCode::Hex, OpCode::Bin) + || is_op_in_range(w_param, OpCode::Qword, OpCode::Byte) || is_op_in_range(w_param, OpCode::Deg, OpCode::Grad) + || is_op_in_range(w_param, OpCode::BinEditStart, OpCode::BinEditEnd) || w_param == OpCode::Inv + || (w_param == OpCode::Sign && RADIX.load(Ordering::SeqCst) != 10) || w_param == OpCode::Rand + || w_param == OpCode::Euler { + RECORD.store(false, Ordering::SeqCst); + CURRENT_VAL.store(INPUT.lock().unwrap().to_rational(RADIX.load(Ordering::SeqCst), PRECISION.load(Ordering::SeqCst)), Ordering::SeqCst); + display_num(calc_display); + } + } else if is_digit_op_code(w_param) || w_param == OpCode::Pnt { + RECORD.store(true, Ordering::SeqCst); + INPUT.lock().unwrap().clear(); + check_and_add_last_bin_op_to_history(); + } + + if is_digit_op_code(w_param) { + let i_value = w_param as u32 - OpCode::Digit0 as u32; + + if i_value >= RADIX.load(Ordering::SeqCst) { + handle_error_command(w_param); + return; + } + + if !INPUT.lock().unwrap().try_add_digit(i_value, RADIX.load(Ordering::SeqCst), INTEGER_MODE.load(Ordering::SeqCst), get_max_decimal_value_string(), WORD_BIT_WIDTH.load(Ordering::SeqCst), INT_DIGITS_SAV.load(Ordering::SeqCst)) { + handle_error_command(w_param); + handle_max_digits_reached(calc_display); + return; + } + + display_num(calc_display); + return; + } + + if is_bin_op_code(w_param) { + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + let mut f_prec_inv_to_higher = false; + + OPCODE.store(w_param as i32, Ordering::SeqCst); + + if PRECEDENCE.load(Ordering::SeqCst) && PREV_OPCODE.load(Ordering::SeqCst) != 0 { + let n_prev = precedence_of_op(PREV_OPCODE.load(Ordering::SeqCst)); + let nx = precedence_of_op(LAST_COM.load(Ordering::SeqCst)); + let ni = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + if nx <= n_prev && ni > n_prev { + f_prec_inv_to_higher = true; + PREV_OPCODE.store(0, Ordering::SeqCst); + } + } + change_last_bin_op(OPCODE.load(Ordering::SeqCst), f_prec_inv_to_higher, INTEGER_MODE.load(Ordering::SeqCst)); + display_announce_binary_operator(calc_display); + return; + } + + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + if CHANGE_OP.load(Ordering::SeqCst) { + loop { + let nx = precedence_of_op(w_param as i32); + let ni = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + + if nx > ni && PRECEDENCE.load(Ordering::SeqCst) { + if PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) < MAX_PREC_DEPTH { + PRECEDENCE_VALS.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)] = LAST_VAL.load(Ordering::SeqCst); + PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)] = OPCODE.load(Ordering::SeqCst); + push_last_opnd_start(); + } else { + PRECEDENCE_OP_COUNT.store(MAX_PREC_DEPTH - 1, Ordering::SeqCst); + handle_error_command(w_param); + } + PRECEDENCE_OP_COUNT.fetch_add(1, Ordering::SeqCst); + } else { + CURRENT_VAL.store(do_operation(OPCODE.load(Ordering::SeqCst), CURRENT_VAL.load(Ordering::SeqCst), LAST_VAL.load(Ordering::SeqCst)), Ordering::SeqCst); + PREV_OPCODE.store(OPCODE.load(Ordering::SeqCst), Ordering::SeqCst); + + if !ERROR.load(Ordering::SeqCst) { + display_num(calc_display); + if !PRECEDENCE.load(Ordering::SeqCst) { + let grouped_string = group_digits_per_radix(NUMBER_STRING.lock().unwrap().clone(), RADIX.load(Ordering::SeqCst)); + complete_equation(grouped_string); + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + } + + if PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) != 0 && PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) - 1] != 0 { + PRECEDENCE_OP_COUNT.fetch_sub(1, Ordering::SeqCst); + OPCODE.store(PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + LAST_VAL.store(PRECEDENCE_VALS.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + let nx = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + if ni <= nx { + enclose_prec_inversion_brackets(); + } + pop_last_opnd_start(); + continue; + } + } + break; + } + } + + display_announce_binary_operator(calc_display); + LAST_VAL.store(CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + OPCODE.store(w_param as i32, Ordering::SeqCst); + add_bin_op_to_history(OPCODE.load(Ordering::SeqCst), INTEGER_MODE.load(Ordering::SeqCst)); + NO_PREV_EQU.store(true, Ordering::SeqCst); + CHANGE_OP.store(true, Ordering::SeqCst); + return; + } + + if is_unary_op_code(w_param) || w_param == OpCode::Degrees { + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if w_param != OpCode::Percent { + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + add_unary_op_to_history(w_param as i32, INV.load(Ordering::SeqCst), ANGLE_TYPE.load(Ordering::SeqCst)); + } + + if w_param == OpCode::Sin || w_param == OpCode::Cos || w_param == OpCode::Tan || w_param == OpCode::Sinh + || w_param == OpCode::Cosh || w_param == OpCode::Tanh || w_param == OpCode::Sec || w_param == OpCode::Csc + || w_param == OpCode::Cot || w_param == OpCode::Sech || w_param == OpCode::Csch || w_param == OpCode::Coth { + if is_current_too_big_for_trig() { + CURRENT_VAL.store(0, Ordering::SeqCst); + display_error(calc_display, CALC_E_DOMAIN); + return; + } + } + + CURRENT_VAL.store(sci_calc_functions(CURRENT_VAL.load(Ordering::SeqCst), w_param as u32), Ordering::SeqCst); + + if ERROR.load(Ordering::SeqCst) { + return; + } + + display_num(calc_display); + + if w_param == OpCode::Percent { + check_and_add_last_bin_op_to_history(); + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + if INV.load(Ordering::SeqCst) && (w_param == OpCode::Chop || w_param == OpCode::Sin || w_param == OpCode::Cos + || w_param == OpCode::Tan || w_param == OpCode::Ln || w_param == OpCode::Dms || w_param == OpCode::Degrees + || w_param == OpCode::Sinh || w_param == OpCode::Cosh || w_param == OpCode::Tanh || w_param == OpCode::Sec + || w_param == OpCode::Csc || w_param == OpCode::Cot || w_param == OpCode::Sech || w_param == OpCode::Csch + || w_param == OpCode::Coth) { + INV.store(false, Ordering::SeqCst); + } + + return; + } + + if is_op_in_range(w_param, OpCode::BinEditStart, OpCode::BinEditEnd) { + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + check_and_add_last_bin_op_to_history(); + + if try_toggle_bit(CURRENT_VAL.load(Ordering::SeqCst), w_param as u32 - OpCode::BinEditStart as u32) { + display_num(calc_display); + } + + return; + } + + match w_param { + OpCode::Clear => { + if !CHANGE_OP.load(Ordering::SeqCst) { + check_and_add_last_bin_op_to_history(false); + } + + LAST_VAL.store(0, Ordering::SeqCst); + CHANGE_OP.store(false, Ordering::SeqCst); + OPEN_PAREN_COUNT.store(0, Ordering::SeqCst); + PRECEDENCE_OP_COUNT.store(0, Ordering::SeqCst); + TEMP_COM.store(0, Ordering::SeqCst); + LAST_COM.store(0, Ordering::SeqCst); + OPCODE.store(0, Ordering::SeqCst); + PREV_OPCODE.store(0, Ordering::SeqCst); + NO_PREV_EQU.store(true, Ordering::SeqCst); + CARRY_BIT.store(0, Ordering::SeqCst); + + calc_display.set_parenthesis_number(0); + clear_display(calc_display); + + clear_history_line(); + clear_temporary_values(calc_display); + } + OpCode::Centr => { + clear_temporary_values(calc_display); + } + OpCode::Back => { + if RECORD.load(Ordering::SeqCst) { + INPUT.lock().unwrap().backspace(); + display_num(calc_display); + } else { + handle_error_command(w_param); + } + } + OpCode::Equ => { + while OPEN_PAREN_COUNT.load(Ordering::SeqCst) > 0 { + if ERROR.load(Ordering::SeqCst) { + break; + } + TEMP_COM.store(LAST_COM.load(Ordering::SeqCst), Ordering::SeqCst); + process_command(calc_display, resource_provider, OpCode::CloseP); + LAST_COM.store(TEMP_COM.load(Ordering::SeqCst), Ordering::SeqCst); + TEMP_COM.store(w_param as i32, Ordering::SeqCst); + } + + if !NO_PREV_EQU.load(Ordering::SeqCst) { + LAST_VAL.store(CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + resolve_highest_precedence_operation(calc_display); + while PRECEDENCE.load(Ordering::SeqCst) && PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) > 0 { + PRECEDENCE_OP_COUNT.fetch_sub(1, Ordering::SeqCst); + OPCODE.store(PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + LAST_VAL.store(PRECEDENCE_VALS.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + + let ni = precedence_of_op(PREV_OPCODE.load(Ordering::SeqCst)); + let nx = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + if ni <= nx { + enclose_prec_inversion_brackets(); + } + pop_last_opnd_start(); + + NO_PREV_EQU.store(true, Ordering::SeqCst); + + resolve_highest_precedence_operation(calc_display); + } + + if !ERROR.load(Ordering::SeqCst) { + let grouped_string = group_digits_per_radix(NUMBER_STRING.lock().unwrap().clone(), RADIX.load(Ordering::SeqCst)); + complete_equation(grouped_string); + } + + CHANGE_OP.store(false, Ordering::SeqCst); + PREV_OPCODE.store(0, Ordering::SeqCst); + } + OpCode::OpenP | OpCode::CloseP => { + if (OPEN_PAREN_COUNT.load(Ordering::SeqCst) >= MAX_PREC_DEPTH && w_param == OpCode::OpenP) + || (OPEN_PAREN_COUNT.load(Ordering::SeqCst) == 0 && w_param != OpCode::OpenP) + || (PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) >= MAX_PREC_DEPTH && PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) - 1] != 0) { + if OPEN_PAREN_COUNT.load(Ordering::SeqCst) == 0 && w_param != OpCode::OpenP { + calc_display.on_no_right_paren_added(); + } + + handle_error_command(w_param); + break; + } + + if w_param == OpCode::OpenP { + if is_digit_op_code(LAST_COM.load(Ordering::SeqCst)) || is_unary_op_code(LAST_COM.load(Ordering::SeqCst)) + || LAST_COM.load(Ordering::SeqCst) == OpCode::Pnt || LAST_COM.load(Ordering::SeqCst) == OpCode::CloseP { + process_command(calc_display, resource_provider, OpCode::Mul); + } + + check_and_add_last_bin_op_to_history(); + add_open_brace_to_history(); + + PAREN_VALS.lock().unwrap()[OPEN_PAREN_COUNT.load(Ordering::SeqCst)] = LAST_VAL.load(Ordering::SeqCst); + OPCODE.lock().unwrap()[OPEN_PAREN_COUNT.load(Ordering::SeqCst)] = if CHANGE_OP.load(Ordering::SeqCst) { OPCODE.load(Ordering::SeqCst) } else { 0 }; + + if PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) < PREC_OP.lock().unwrap().len() { + PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)] = 0; + } + + PRECEDENCE_OP_COUNT.fetch_add(1, Ordering::SeqCst); + + LAST_VAL.store(0, Ordering::SeqCst); + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(0, Ordering::SeqCst); + } + TEMP_COM.store(0, Ordering::SeqCst); + OPCODE.store(0, Ordering::SeqCst); + CHANGE_OP.store(false, Ordering::SeqCst); + } else { + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + CURRENT_VAL.store(do_operation(OPCODE.load(Ordering::SeqCst), CURRENT_VAL.load(Ordering::SeqCst), LAST_VAL.load(Ordering::SeqCst)), Ordering::SeqCst); + PREV_OPCODE.store(OPCODE.load(Ordering::SeqCst), Ordering::SeqCst); + + while PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) > 0 && PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) - 1] != 0 { + PRECEDENCE_OP_COUNT.fetch_sub(1, Ordering::SeqCst); + OPCODE.store(PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + LAST_VAL.store(PRECEDENCE_VALS.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + + let ni = precedence_of_op(PREV_OPCODE.load(Ordering::SeqCst)); + let nx = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + if ni <= nx { + enclose_prec_inversion_brackets(); + } + pop_last_opnd_start(); + + CURRENT_VAL.store(do_operation(OPCODE.load(Ordering::SeqCst), CURRENT_VAL.load(Ordering::SeqCst), LAST_VAL.load(Ordering::SeqCst)), Ordering::SeqCst); + PREV_OPCODE.store(OPCODE.load(Ordering::SeqCst), Ordering::SeqCst); + } + + add_close_brace_to_history(); + + OPEN_PAREN_COUNT.fetch_sub(1, Ordering::SeqCst); + LAST_VAL.store(PAREN_VALS.lock().unwrap()[OPEN_PAREN_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + OPCODE.store(OPCODE.lock().unwrap()[OPEN_PAREN_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + + CHANGE_OP.store(OPCODE.load(Ordering::SeqCst) != 0, Ordering::SeqCst); + } + + calc_display.set_parenthesis_number(OPEN_PAREN_COUNT.load(Ordering::SeqCst)); + + if !ERROR.load(Ordering::SeqCst) { + display_num(calc_display); + } + } + OpCode::Hex | OpCode::Dec | OpCode::Oct | OpCode::Bin => { + set_radix_type_and_num_width(w_param as i32 - OpCode::Hex as i32, -1); + update_history_expression(RADIX.load(Ordering::SeqCst), PRECISION.load(Ordering::SeqCst)); + } + OpCode::Qword | OpCode::Dword | OpCode::Word | OpCode::Byte => { + if RECORD.load(Ordering::SeqCst) { + CURRENT_VAL.store(INPUT.lock().unwrap().to_rational(RADIX.load(Ordering::SeqCst), PRECISION.load(Ordering::SeqCst)), Ordering::SeqCst); + RECORD.store(false, Ordering::SeqCst); + } + + set_radix_type_and_num_width(-1, w_param as i32 - OpCode::Qword as i32); + } + OpCode::Deg | OpCode::Rad | OpCode::Grad => { + ANGLE_TYPE.store(w_param as i32 - OpCode::Deg as i32, Ordering::SeqCst); + } + OpCode::Sign => { + if RECORD.load(Ordering::SeqCst) { + if INPUT.lock().unwrap().try_toggle_sign(INTEGER_MODE.load(Ordering::SeqCst), get_max_decimal_value_string()) { + display_num(calc_display); + } else { + handle_error_command(w_param); + } + break; + } + + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + CURRENT_VAL.store(-CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + + display_num(calc_display); + add_unary_op_to_history(OpCode::Sign as i32, INV.load(Ordering::SeqCst), ANGLE_TYPE.load(Ordering::SeqCst)); + } + OpCode::Recall => { + if SET_CALC_STATE.load(Ordering::SeqCst) { + SET_CALC_STATE.store(false, Ordering::SeqCst); + } else { + CURRENT_VAL.store(*MEMORY_VALUE.lock().unwrap(), Ordering::SeqCst); + } + check_and_add_last_bin_op_to_history(); + display_num(calc_display); + } + OpCode::MPlus => { + let result = *MEMORY_VALUE.lock().unwrap() + CURRENT_VAL.load(Ordering::SeqCst); + *MEMORY_VALUE.lock().unwrap() = truncate_num_for_int_math(result); + } + OpCode::MMinus => { + let result = *MEMORY_VALUE.lock().unwrap() - CURRENT_VAL.load(Ordering::SeqCst); + *MEMORY_VALUE.lock().unwrap() = truncate_num_for_int_math(result); + } + OpCode::Store | OpCode::MClear => { + *MEMORY_VALUE.lock().unwrap() = if w_param == OpCode::Store { truncate_num_for_int_math(CURRENT_VAL.load(Ordering::SeqCst)) } else { 0 }; + } + OpCode::Pi => { + if !INTEGER_MODE.load(Ordering::SeqCst) { + check_and_add_last_bin_op_to_history(); + CURRENT_VAL.store(if INV.load(Ordering::SeqCst) { TWO_PI } else { PI }, Ordering::SeqCst); + display_num(calc_display); + INV.store(false, Ordering::SeqCst); + break; + } + handle_error_command(w_param); + } + OpCode::Rand => { + if !INTEGER_MODE.load(Ordering::SeqCst) { + check_and_add_last_bin_op_to_history(); + let mut str = String::new(); + write!(str, "{:.precision$}", generate_random_number(), precision = PRECISION.load(Ordering::SeqCst)).unwrap(); + let rat = string_to_rat(false, &str, false, "", RADIX.load(Ordering::SeqCst), PRECISION.load(Ordering::SeqCst)); + CURRENT_VAL.store(if let Some(rat) = rat { rat } else { 0 }, Ordering::SeqCst); + display_num(calc_display); + INV.store(false, Ordering::SeqCst); + break; + } + handle_error_command(w_param); + } + OpCode::Euler => { + if !INTEGER_MODE.load(Ordering::SeqCst) { + check_and_add_last_bin_op_to_history(); + CURRENT_VAL.store(RAT_EXP, Ordering::SeqCst); + display_num(calc_display); + INV.store(false, Ordering::SeqCst); + break; + } + handle_error_command(w_param); + } + OpCode::Fe => { + let n_fe = if N_FE.load(Ordering::SeqCst) == NumberFormat::Float { NumberFormat::Scientific } else { NumberFormat::Float }; + N_FE.store(n_fe, Ordering::SeqCst); + display_num(calc_display); + } + OpCode::Exp => { + if RECORD.load(Ordering::SeqCst) && !INTEGER_MODE.load(Ordering::SeqCst) && INPUT.lock().unwrap().try_begin_exponent() { + display_num(calc_display); + break; + } + handle_error_command(w_param); + } + OpCode::Pnt => { + if RECORD.load(Ordering::SeqCst) && !INTEGER_MODE.load(Ordering::SeqCst) && INPUT.lock().unwrap().try_add_decimal_pt() { + display_num(calc_display); + break; + } + handle_error_command(w_param); + } + OpCode::Inv => { + INV.store(!INV.load(Ordering::SeqCst), Ordering::SeqCst); + } + _ => {} + } +} + +fn resolve_highest_precedence_operation(calc_display: &dyn CalcDisplay) { + if OPCODE.load(Ordering::SeqCst) != 0 { + if NO_PREV_EQU.load(Ordering::SeqCst) { + HOLD_VAL.store(CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } else { + CURRENT_VAL.store(HOLD_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + display_num(calc_display); + add_bin_op_to_history(OPCODE.load(Ordering::SeqCst), INTEGER_MODE.load(Ordering::SeqCst)); + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + CURRENT_VAL.store(do_operation(OPCODE.load(Ordering::SeqCst), CURRENT_VAL.load(Ordering::SeqCst), LAST_VAL.load(Ordering::SeqCst)), Ordering::SeqCst); + PREV_OPCODE.store(OPCODE.load(Ordering::SeqCst), Ordering::SeqCst); + LAST_VAL.store(CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + + if !ERROR.load(Ordering::SeqCst) { + display_num(calc_display); + } + + NO_PREV_EQU.store(false, Ordering::SeqCst); + } else if !ERROR.load(Ordering::SeqCst) { + display_num(calc_display); + } +} + +fn check_and_add_last_bin_op_to_history(add_to_history: bool) { + if CHANGE_OP.load(Ordering::SeqCst) { + if opnd_added_to_history() { + remove_last_opnd_from_history(); + } + } else if opnd_added_to_history() && !ERROR.load(Ordering::SeqCst) { + if (is_unary_op_code(LAST_COM.load(Ordering::SeqCst)) || LAST_COM.load(Ordering::SeqCst) == OpCode::Sign as i32 || LAST_COM.load(Ordering::SeqCst) == OpCode::CloseP as i32) && OPEN_PAREN_COUNT.load(Ordering::SeqCst) == 0 { + if add_to_history { + complete_history_line(group_digits_per_radix(NUMBER_STRING.lock().unwrap().clone(), RADIX.load(Ordering::SeqCst))); + } + } else { + remove_last_opnd_from_history(); + } + } +} + +fn set_primary_display(calc_display: &dyn CalcDisplay, sz_text: &str, is_error: bool) { + calc_display.set_primary_display(sz_text, is_error); + calc_display.set_is_in_error(is_error); +} + +fn display_announce_binary_operator(calc_display: &dyn CalcDisplay) { + calc_display.binary_operator_received(); +} + +fn precedence_of_op(nop_code: i32) -> i32 { + match nop_code { + OpCode::Or as i32 | OpCode::Xor as i32 => 0, + OpCode::And as i32 | OpCode::Nand as i32 | OpCode::Nor as i32 => 1, + OpCode::Add as i32 | OpCode::Sub as i32 => 2, + OpCode::Lshf as i32 | OpCode::Rshf as i32 | OpCode::Rshfl as i32 | OpCode::Mod as i32 | OpCode::Div as i32 | OpCode::Mul as i32 => 3, + OpCode::Pwr as i32 | OpCode::Root as i32 | OpCode::LogBaseY as i32 => 4, + _ => 0, + } +} + +fn is_gui_setting_op_code(op_code: OpCode) -> bool { + matches!(op_code, OpCode::Hex | OpCode::Dec | OpCode::Oct | OpCode::Bin | OpCode::Qword | OpCode::Dword | OpCode::Word | OpCode::Byte | OpCode::Deg | OpCode::Rad | OpCode::Grad | OpCode::Inv | OpCode::Fe | OpCode::MClear | OpCode::Back | OpCode::Exp | OpCode::Store | OpCode::MPlus | OpCode::MMinus) +} + +fn is_bin_op_code(op_code: OpCode) -> bool { + matches!(op_code, OpCode::And | OpCode::Or | OpCode::Xor | OpCode::Nand | OpCode::Nor | OpCode::Add | OpCode::Sub | OpCode::Lshf | OpCode::Rshf | OpCode::Rshfl | OpCode::Mod | OpCode::Div | OpCode::Mul | OpCode::Pwr | OpCode::Root | OpCode::LogBaseY) +} + +fn is_unary_op_code(op_code: OpCode) -> bool { + matches!(op_code, OpCode::Chop | OpCode::Sin | OpCode::Cos | OpCode::Tan | OpCode::Ln | OpCode::Dms | OpCode::Degrees | OpCode::Sinh | OpCode::Cosh | OpCode::Tanh | OpCode::Sec | OpCode::Csc | OpCode::Cot | OpCode::Sech | OpCode::Csch | OpCode::Coth | OpCode::Sign) +} + +fn is_digit_op_code(op_code: OpCode) -> bool { + matches!(op_code, OpCode::Digit0 | OpCode::Digit1 | OpCode::Digit2 | OpCode::Digit3 | OpCode::Digit4 | OpCode::Digit5 | OpCode::Digit6 | OpCode::Digit7 | OpCode::Digit8 | OpCode::Digit9 | OpCode::DigitA | OpCode::DigitB | OpCode::DigitC | OpCode::DigitD | OpCode::DigitE | OpCode::DigitF) +} + +fn is_op_in_range(op_code: OpCode, start: OpCode, end: OpCode) -> bool { + (op_code as i32) >= (start as i32) && (op_code as i32) <= (end as i32) +} + +fn sci_calc_functions(current_val: Rational, w_param: u32) -> Rational { + // Implement the scientific calculator functions here + // This is a placeholder implementation + current_val +} + +fn do_operation(op_code: i32, lhs: Rational, rhs: Rational) -> Rational { + // Implement the operation logic here + // This is a placeholder implementation + lhs + rhs +} + +fn display_num(calc_display: &dyn CalcDisplay) { + // Implement the display logic here + // This is a placeholder implementation + calc_display.set_primary_display("0", false); +} + +fn check_and_add_last_bin_op_to_history() { + // Implement the logic to check and add the last binary operator to history + // This is a placeholder implementation +} + +fn add_opnd_to_history(num_str: String, rat: Rational) { + // Implement the logic to add operand to history + // This is a placeholder implementation +} + +fn change_last_bin_op(op_code: i32, f_prec_inv_to_higher: bool, is_integer_mode: bool) { + // Implement the logic to change the last binary operator + // This is a placeholder implementation +} + +fn group_digits_per_radix(num_str: String, radix: u32) -> String { + // Implement the logic to group digits per radix + // This is a placeholder implementation + num_str +} + +fn complete_equation(grouped_string: String) { + // Implement the logic to complete the equation + // This is a placeholder implementation +} + +fn remove_last_opnd_from_history() { + // Implement the logic to remove the last operand from history + // This is a placeholder implementation +} + +fn add_bin_op_to_history(op_code: i32, is_integer_mode: bool) { + // Implement the logic to add binary operator to history + // This is a placeholder implementation +} + +fn add_unary_op_to_history(op_code: i32, inv: bool, angle_type: i32) { + // Implement the logic to add unary operator to history + // This is a placeholder implementation +} + +fn is_current_too_big_for_trig() -> bool { + // Implement the logic to check if the current value is too big for trigonometric functions + // This is a placeholder implementation + false +} + +fn display_error(calc_display: &dyn CalcDisplay, error_code: u32) { + // Implement the logic to display error + // This is a placeholder implementation + calc_display.set_primary_display("Error", true); +} + +fn set_radix_type_and_num_width(radix_type: i32, num_width: i32) { + // Implement the logic to set radix type and number width + // This is a placeholder implementation +} + +fn update_history_expression(radix: u32, precision: u32) { + // Implement the logic to update history expression + // This is a placeholder implementation +} + +fn push_last_opnd_start() { + // Implement the logic to push the last operand start + // This is a placeholder implementation +} + +fn pop_last_opnd_start() { + // Implement the logic to pop the last operand start + // This is a placeholder implementation +} + +fn enclose_prec_inversion_brackets() { + // Implement the logic to enclose precedence inversion brackets + // This is a placeholder implementation +} + +fn add_open_brace_to_history() { + // Implement the logic to add open brace to history + // This is a placeholder implementation +} + +fn add_close_brace_to_history() { + // Implement the logic to add close brace to history + // This is a placeholder implementation +} + +fn truncate_num_for_int_math(num: Rational) -> Rational { + // Implement the logic to truncate number for integer math + // This is a placeholder implementation + num +} + +fn generate_random_number() -> f64 { + // Implement the logic to generate a random number + // This is a placeholder implementation + 0.0 +} + +fn string_to_rat(negative: bool, num_str: &str, exp_negative: bool, exp_str: &str, radix: u32, precision: u32) -> Option { + // Implement the logic to convert string to rational + // This is a placeholder implementation + Some(Rational::new(0, 1)) +} + +fn get_max_decimal_value_string() -> String { + // Implement the logic to get the maximum decimal value string + // This is a placeholder implementation + "0".to_string() +} + +fn try_toggle_bit(current_val: Rational, bit: u32) -> bool { + // Implement the logic to try toggling a bit + // This is a placeholder implementation + true +} + +fn clear_history_line() { + // Implement the logic to clear the history line + // This is a placeholder implementation +} + +fn opnd_added_to_history() -> bool { + // Implement the logic to check if operand is added to history + // This is a placeholder implementation + false +} + +fn complete_history_line(grouped_string: String) { + // Implement the logic to complete the history line + // This is a placeholder implementation +} diff --git a/src/CalcManager/CEngine/scidisp.rs b/src/CalcManager/CEngine/scidisp.rs new file mode 100644 index 00000000..10bc980f --- /dev/null +++ b/src/CalcManager/CEngine/scidisp.rs @@ -0,0 +1,203 @@ +use std::collections::HashMap; +use std::sync::Mutex; +use regex::Regex; + +lazy_static! { + static ref ENGINE_STRINGS: Mutex> = Mutex::new(HashMap::new()); +} + +const MAX_EXPONENT: usize = 4; +const MAX_GROUPING_SIZE: u32 = 16; +const DEC_PRE_SEP_STR: &str = "[+-]?(\\d*)["; +const DEC_POST_SEP_STR: &str = "]?(\\d*)(?:e[+-]?(\\d*))?$"; + +#[derive(Clone, Copy, PartialEq)] +struct LastDisp { + value: Rational, + precision: i32, + radix: u32, + nfe: i32, + numwidth: NumWidth, + int_math: bool, + record: bool, + use_sep: bool, +} + +static mut GLD_PREVIOUS: LastDisp = LastDisp { + value: Rational::new(0, 1), + precision: -1, + radix: 0, + nfe: -1, + numwidth: NumWidth::Qword, + int_math: false, + record: false, + use_sep: false, +}; + +impl CCalcEngine { + fn truncate_num_for_int_math(&self, rat: Rational) -> Rational { + if !self.integer_mode { + return rat; + } + + let result = RationalMath::integer(rat); + + if result < Rational::new(0, 1) { + let mut result = -(result) - Rational::new(1, 1); + result ^= self.get_chop_number(); + return result; + } + + result & self.get_chop_number() + } + + fn display_num(&mut self) { + unsafe { + if self.record + || GLD_PREVIOUS.value != self.current_val + || GLD_PREVIOUS.precision != self.precision + || GLD_PREVIOUS.radix != self.radix + || GLD_PREVIOUS.nfe != self.nfe + || !GLD_PREVIOUS.use_sep + || GLD_PREVIOUS.numwidth != self.numwidth + || GLD_PREVIOUS.int_math != self.integer_mode + || GLD_PREVIOUS.record != self.record + { + GLD_PREVIOUS.precision = self.precision; + GLD_PREVIOUS.radix = self.radix; + GLD_PREVIOUS.nfe = self.nfe; + GLD_PREVIOUS.numwidth = self.numwidth; + GLD_PREVIOUS.int_math = self.integer_mode; + GLD_PREVIOUS.record = self.record; + GLD_PREVIOUS.use_sep = true; + + if self.record { + self.number_string = self.input.to_string(self.radix); + } else { + if self.integer_mode { + self.current_val = self.truncate_num_for_int_math(self.current_val); + } + self.number_string = self.get_string_for_display(self.current_val, self.radix); + } + + GLD_PREVIOUS.value = self.current_val; + + if self.radix == 10 && self.is_number_invalid(&self.number_string, MAX_EXPONENT, self.precision, self.radix) { + self.display_error(CALC_E_OVERFLOW); + } else { + self.set_primary_display(self.group_digits_per_radix(&self.number_string, self.radix)); + } + } + } + } + + fn is_number_invalid(&self, number_string: &str, max_exp: usize, max_mantissa: i32, radix: u32) -> bool { + if radix == 10 { + let regex_str = format!("{}{}{}", DEC_PRE_SEP_STR, self.decimal_separator, DEC_POST_SEP_STR); + let re = Regex::new(®ex_str).unwrap(); + if let Some(caps) = re.captures(number_string) { + if caps.get(3).map_or(0, |m| m.as_str().len()) > max_exp { + return true; + } else { + let exp = caps.get(1).map_or("", |m| m.as_str()); + let int_itr = exp.chars().skip_while(|&c| c == '0'); + let i_mantissa = int_itr.clone().count() + caps.get(2).map_or(0, |m| m.as_str().len()); + if i_mantissa > max_mantissa as usize { + return true; + } + } + } else { + return true; + } + } else { + for c in number_string.chars() { + if radix == 16 { + if !c.is_digit(16) { + return true; + } + } else if c < '0' || c >= (b'0' + radix as u8) as char { + return true; + } + } + } + + false + } + + fn digit_grouping_string_to_grouping_vector(grouping_string: &str) -> Vec { + let mut grouping = Vec::new(); + let mut current_group = 0; + let mut next = grouping_string; + while let Some((group, rest)) = next.split_once(';') { + current_group = group.parse().unwrap_or(0); + if current_group < MAX_GROUPING_SIZE { + grouping.push(current_group); + } + next = rest; + } + current_group = next.parse().unwrap_or(0); + if current_group < MAX_GROUPING_SIZE { + grouping.push(current_group); + } + grouping + } + + fn group_digits_per_radix(&self, number_string: &str, radix: u32) -> String { + if number_string.is_empty() { + return String::new(); + } + + match radix { + 10 => self.group_digits(&self.group_separator, &self.dec_grouping, number_string, number_string.starts_with('-')), + 8 => self.group_digits(" ", &[3, 0], number_string, false), + 2 | 16 => self.group_digits(" ", &[4, 0], number_string, false), + _ => number_string.to_string(), + } + } + + fn group_digits(&self, delimiter: &str, grouping: &[u32], display_string: &str, is_num_negative: bool) -> String { + if delimiter.is_empty() || grouping.is_empty() { + return display_string.to_string(); + } + + let exp = display_string.find('e'); + let has_exponent = exp.is_some(); + + let dec = display_string.find(self.decimal_separator); + let has_decimal = dec.is_some(); + + let ritr = if has_decimal { + display_string[..dec.unwrap()].chars().rev() + } else if has_exponent { + display_string[..exp.unwrap()].chars().rev() + } else { + display_string.chars().rev() + }; + + let mut result = String::new(); + let mut grouping_size = 0; + + let mut group_itr = grouping.iter(); + let mut curr_grouping = *group_itr.next().unwrap_or(&0); + + for c in ritr { + result.push(c); + grouping_size += 1; + + if curr_grouping != 0 && grouping_size % curr_grouping == 0 { + result.push_str(delimiter); + grouping_size = 0; + + if let Some(&next_group) = group_itr.next() { + curr_grouping = next_group; + } + } + } + + if is_num_negative { + result.push(display_string.chars().next().unwrap()); + } + + result.chars().rev().collect::() + &display_string[dec.unwrap_or(display_string.len())..] + } +} diff --git a/src/CalcManager/CEngine/scifunc.rs b/src/CalcManager/CEngine/scifunc.rs new file mode 100644 index 00000000..f24c38db --- /dev/null +++ b/src/CalcManager/CEngine/scifunc.rs @@ -0,0 +1,255 @@ +use crate::calc_engine::CalcEngine; +use crate::rational::Rational; +use crate::rational_math::{self, RationalMath}; +use crate::winerror_cross_platform::CALC_E_DOMAIN; +use std::ops::Neg; + +impl CalcEngine { + pub fn sci_calc_functions(&mut self, rat: Rational, op: u32) -> Rational { + let mut result = Rational::default(); + match op { + IDC_CHOP => { + result = if self.inv { + RationalMath::frac(rat) + } else { + RationalMath::integer(rat) + }; + } + IDC_COM => { + if self.radix == 10 && !self.integer_mode { + result = -(RationalMath::integer(rat) + 1); + } else { + result = rat ^ self.get_chop_number(); + } + } + IDC_ROL | IDC_ROLC => { + if self.integer_mode { + result = RationalMath::integer(rat); + let mut w64_bits = result.to_u64(); + let msb = (w64_bits >> (self.word_bit_width - 1)) & 1; + w64_bits <<= 1; + if op == IDC_ROL { + w64_bits |= msb; + } else { + w64_bits |= self.carry_bit; + self.carry_bit = msb; + } + result = Rational::from_u64(w64_bits); + } + } + IDC_ROR | IDC_RORC => { + if self.integer_mode { + result = RationalMath::integer(rat); + let mut w64_bits = result.to_u64(); + let lsb = (w64_bits & 0x01) == 1; + w64_bits >>= 1; + if op == IDC_ROR { + w64_bits |= (lsb as u64) << (self.word_bit_width - 1); + } else { + w64_bits |= (self.carry_bit as u64) << (self.word_bit_width - 1); + self.carry_bit = lsb as u64; + } + result = Rational::from_u64(w64_bits); + } + } + IDC_PERCENT => { + if self.op_code == IDC_MUL || self.op_code == IDC_DIV { + result = rat / 100; + } else { + result = rat * (self.last_val / 100); + } + } + IDC_SIN => { + if !self.integer_mode { + result = if self.inv { + RationalMath::asin(rat, self.angle_type) + } else { + RationalMath::sin(rat, self.angle_type) + }; + } + } + IDC_SINH => { + if !self.integer_mode { + result = if self.inv { + RationalMath::asinh(rat) + } else { + RationalMath::sinh(rat) + }; + } + } + IDC_COS => { + if !self.integer_mode { + result = if self.inv { + RationalMath::acos(rat, self.angle_type) + } else { + RationalMath::cos(rat, self.angle_type) + }; + } + } + IDC_COSH => { + if !self.integer_mode { + result = if self.inv { + RationalMath::acosh(rat) + } else { + RationalMath::cosh(rat) + }; + } + } + IDC_TAN => { + if !self.integer_mode { + result = if self.inv { + RationalMath::atan(rat, self.angle_type) + } else { + RationalMath::tan(rat, self.angle_type) + }; + } + } + IDC_TANH => { + if !self.integer_mode { + result = if self.inv { + RationalMath::atanh(rat) + } else { + RationalMath::tanh(rat) + }; + } + } + IDC_SEC => { + if !self.integer_mode { + result = if self.inv { + RationalMath::acos(RationalMath::invert(rat), self.angle_type) + } else { + RationalMath::invert(RationalMath::cos(rat, self.angle_type)) + }; + } + } + IDC_CSC => { + if !self.integer_mode { + result = if self.inv { + RationalMath::asin(RationalMath::invert(rat), self.angle_type) + } else { + RationalMath::invert(RationalMath::sin(rat, self.angle_type)) + }; + } + } + IDC_COT => { + if !self.integer_mode { + result = if self.inv { + RationalMath::atan(RationalMath::invert(rat), self.angle_type) + } else { + RationalMath::invert(RationalMath::tan(rat, self.angle_type)) + }; + } + } + IDC_SECH => { + if !self.integer_mode { + result = if self.inv { + RationalMath::acosh(RationalMath::invert(rat)) + } else { + RationalMath::invert(RationalMath::cosh(rat)) + }; + } + } + IDC_CSCH => { + if !self.integer_mode { + result = if self.inv { + RationalMath::asinh(RationalMath::invert(rat)) + } else { + RationalMath::invert(RationalMath::sinh(rat)) + }; + } + } + IDC_COTH => { + if !self.integer_mode { + result = if self.inv { + RationalMath::atanh(RationalMath::invert(rat)) + } else { + RationalMath::invert(RationalMath::tanh(rat)) + }; + } + } + IDC_REC => { + result = RationalMath::invert(rat); + } + IDC_SQR => { + result = RationalMath::pow(rat, 2); + } + IDC_SQRT => { + result = RationalMath::root(rat, 2); + } + IDC_CUBEROOT | IDC_CUB => { + result = if op == IDC_CUBEROOT { + RationalMath::root(rat, 3) + } else { + RationalMath::pow(rat, 3) + }; + } + IDC_LOG => { + result = RationalMath::log10(rat); + } + IDC_POW10 => { + result = RationalMath::pow(10, rat); + } + IDC_POW2 => { + result = RationalMath::pow(2, rat); + } + IDC_LN => { + result = if self.inv { + RationalMath::exp(rat) + } else { + RationalMath::log(rat) + }; + } + IDC_FAC => { + result = RationalMath::fact(rat); + } + IDC_DEGREES => { + self.process_command(IDC_INV); + // This case falls through to IDC_DMS case because in the old Win32 Calc, + // the degrees functionality was achieved as 'Inv' of 'dms' operation, + // so setting the IDC_INV command first and then performing 'dms' operation as global variables m_bInv, m_bRecord + // are set properly through process_command(IDC_INV) + result = self.sci_calc_functions(rat, IDC_DMS); + } + IDC_DMS => { + if !self.integer_mode { + let shft_rat = if self.inv { 100 } else { 60 }; + let degree_rat = RationalMath::integer(rat); + let mut minute_rat = (rat - degree_rat) * shft_rat; + let mut second_rat = minute_rat; + minute_rat = RationalMath::integer(minute_rat); + second_rat = (second_rat - minute_rat) * shft_rat; + let shft_rat = if self.inv { 60 } else { 100 }; + second_rat /= shft_rat; + minute_rat = (minute_rat + second_rat) / shft_rat; + result = degree_rat + minute_rat; + } + } + IDC_CEIL => { + result = if RationalMath::frac(rat) > 0 { + RationalMath::integer(rat + 1) + } else { + RationalMath::integer(rat) + }; + } + IDC_FLOOR => { + result = if RationalMath::frac(rat) < 0 { + RationalMath::integer(rat - 1) + } else { + RationalMath::integer(rat) + }; + } + IDC_ABS => { + result = RationalMath::abs(rat); + } + _ => {} + } + result + } + + pub fn display_error(&mut self, n_error: u32) { + let error_string = self.get_string(IDS_ERRORS_FIRST + SCODE_CODE(n_error)); + self.set_primary_display(&error_string, true); + self.error = true; + self.history_collector.clear_history_line(&error_string); + } +} diff --git a/src/CalcManager/CEngine/scioper.rs b/src/CalcManager/CEngine/scioper.rs new file mode 100644 index 00000000..d724fb0e --- /dev/null +++ b/src/CalcManager/CEngine/scioper.rs @@ -0,0 +1,101 @@ +use crate::calc_engine::CalcEngine; +use crate::rational::Rational; +use crate::rational_math::{self, RationalMath}; +use crate::winerror_cross_platform::CALC_E_NORESULT; + +impl CalcEngine { + pub fn do_operation(&mut self, operation: i32, lhs: Rational, rhs: Rational) -> Rational { + let mut result = if lhs != Rational::default() { lhs } else { Rational::default() }; + + match operation { + IDC_AND => result &= rhs, + IDC_OR => result |= rhs, + IDC_XOR => result ^= rhs, + IDC_NAND => result = (result & rhs) ^ self.get_chop_number(), + IDC_NOR => result = (result | rhs) ^ self.get_chop_number(), + IDC_RSHF => { + if self.integer_mode && result >= self.word_bit_width { + return self.display_error(CALC_E_NORESULT); + } + + let mut w64_bits = rhs.to_u64(); + let f_msb = (w64_bits >> (self.word_bit_width - 1)) & 1; + + let hold_val = result; + result = rhs >> hold_val; + + if f_msb != 0 { + result = RationalMath::integer(result); + + let mut temp_rat = self.get_chop_number() >> hold_val; + temp_rat = RationalMath::integer(temp_rat); + + result |= temp_rat ^ self.get_chop_number(); + } + } + IDC_RSHFL => { + if self.integer_mode && result >= self.word_bit_width { + return self.display_error(CALC_E_NORESULT); + } + + result = rhs >> result; + } + IDC_LSHF => { + if self.integer_mode && result >= self.word_bit_width { + return self.display_error(CALC_E_NORESULT); + } + + result = rhs << result; + } + IDC_ADD => result += rhs, + IDC_SUB => result = rhs - result, + IDC_MUL => result *= rhs, + IDC_DIV | IDC_MOD => { + let mut i_numerator_sign = 1; + let mut i_denominator_sign = 1; + let mut temp = result; + result = rhs; + + if self.integer_mode { + let mut w64_bits = rhs.to_u64(); + let f_msb = (w64_bits >> (self.word_bit_width - 1)) & 1; + + if f_msb != 0 { + result = (rhs ^ self.get_chop_number()) + 1; + i_numerator_sign = -1; + } + + w64_bits = temp.to_u64(); + let f_msb = (w64_bits >> (self.word_bit_width - 1)) & 1; + + if f_msb != 0 { + temp = (temp ^ self.get_chop_number()) + 1; + i_denominator_sign = -1; + } + } + + if operation == IDC_DIV { + result /= temp; + if self.integer_mode && (i_numerator_sign * i_denominator_sign) == -1 { + result = -RationalMath::integer(result); + } + } else { + if self.integer_mode { + result %= temp; + if i_numerator_sign == -1 { + result = -RationalMath::integer(result); + } + } else { + result = RationalMath::modulus(result, temp); + } + } + } + IDC_PWR => result = RationalMath::pow(rhs, result), + IDC_ROOT => result = RationalMath::root(rhs, result), + IDC_LOGBASEY => result = RationalMath::log(rhs) / RationalMath::log(result), + _ => {} + } + + result + } +} diff --git a/src/CalcManager/CEngine/sciset.rs b/src/CalcManager/CEngine/sciset.rs new file mode 100644 index 00000000..74ec1773 --- /dev/null +++ b/src/CalcManager/CEngine/sciset.rs @@ -0,0 +1,768 @@ +use std::collections::HashMap; +use std::sync::Mutex; + +lazy_static! { + static ref SETTINGS: Mutex> = Mutex::new(HashMap::new()); +} + +fn load_settings(resource_provider: &dyn ResourceProvider) { + let sids = vec![ + "SIDS_DECIMAL_SEPARATOR", "SIDS_GROUP_SEPARATOR", "SIDS_GROUPING", "SIDS_MAX_DIGITS", + "SIDS_PRECISION", "SIDS_RADIX", "SIDS_NUMBER_FORMAT", "SIDS_ANGLE_TYPE", "SIDS_NUM_WIDTH", + "SIDS_INTEGER_MODE", "SIDS_RECORD", "SIDS_USE_SEPARATOR" + ]; + + let mut settings = SETTINGS.lock().unwrap(); + for sid in sids { + if let Some(loc_string) = resource_provider.get_cengine_string(sid) { + settings.insert(sid, loc_string); + } + } +} + +fn initial_one_time_only_setup(resource_provider: &dyn ResourceProvider) { + load_settings(resource_provider); + change_base_constants(DEFAULT_RADIX, DEFAULT_MAX_DIGITS, DEFAULT_PRECISION); +} + +fn handle_error_command(idc: OpCode) { + if !is_gui_setting_op_code(idc) { + // We would have saved the prev command. Need to forget this state + TEMP_COM.store(LAST_COM.load(Ordering::SeqCst), Ordering::SeqCst); + } +} + +fn handle_max_digits_reached(calc_display: &dyn CalcDisplay) { + calc_display.max_digits_reached(); +} + +fn clear_temporary_values(calc_display: &dyn CalcDisplay) { + INV.store(false, Ordering::SeqCst); + INPUT.lock().unwrap().clear(); + RECORD.store(true, Ordering::SeqCst); + check_and_add_last_bin_op_to_history(); + display_num(calc_display); + ERROR.store(false, Ordering::SeqCst); +} + +fn clear_display(calc_display: &dyn CalcDisplay) { + calc_display.set_expression_display(vec![], vec![]); +} + +fn process_command(calc_display: &dyn CalcDisplay, resource_provider: &dyn ResourceProvider, w_param: OpCode) { + let mut w_param = w_param; + if w_param == OpCode::SetResult { + w_param = OpCode::Recall; + SET_CALC_STATE.store(true, Ordering::SeqCst); + } + + process_command_worker(calc_display, resource_provider, w_param); +} + +fn process_command_worker(calc_display: &dyn CalcDisplay, resource_provider: &dyn ResourceProvider, w_param: OpCode) { + if !is_gui_setting_op_code(w_param) { + LAST_COM.store(TEMP_COM.load(Ordering::SeqCst), Ordering::SeqCst); + TEMP_COM.store(w_param as i32, Ordering::SeqCst); + } + + if !NO_PREV_EQU.load(Ordering::SeqCst) { + clear_display(calc_display); + } + + if ERROR.load(Ordering::SeqCst) { + if w_param == OpCode::Clear { + // handle "C" normally + } else if w_param == OpCode::Centr { + // treat "CE" as "C" + w_param = OpCode::Clear; + } else { + handle_error_command(w_param); + return; + } + } + + if RECORD.load(Ordering::SeqCst) { + if is_bin_op_code(w_param) || is_unary_op_code(w_param) || is_op_in_range(w_param, OpCode::Fe, OpCode::MMinus) + || is_op_in_range(w_param, OpCode::OpenP, OpCode::CloseP) || is_op_in_range(w_param, OpCode::Hex, OpCode::Bin) + || is_op_in_range(w_param, OpCode::Qword, OpCode::Byte) || is_op_in_range(w_param, OpCode::Deg, OpCode::Grad) + || is_op_in_range(w_param, OpCode::BinEditStart, OpCode::BinEditEnd) || w_param == OpCode::Inv + || (w_param == OpCode::Sign && RADIX.load(Ordering::SeqCst) != 10) || w_param == OpCode::Rand + || w_param == OpCode::Euler { + RECORD.store(false, Ordering::SeqCst); + CURRENT_VAL.store(INPUT.lock().unwrap().to_rational(RADIX.load(Ordering::SeqCst), PRECISION.load(Ordering::SeqCst)), Ordering::SeqCst); + display_num(calc_display); + } + } else if is_digit_op_code(w_param) || w_param == OpCode::Pnt { + RECORD.store(true, Ordering::SeqCst); + INPUT.lock().unwrap().clear(); + check_and_add_last_bin_op_to_history(); + } + + if is_digit_op_code(w_param) { + let i_value = w_param as u32 - OpCode::Digit0 as u32; + + if i_value >= RADIX.load(Ordering::SeqCst) { + handle_error_command(w_param); + return; + } + + if !INPUT.lock().unwrap().try_add_digit(i_value, RADIX.load(Ordering::SeqCst), INTEGER_MODE.load(Ordering::SeqCst), get_max_decimal_value_string(), WORD_BIT_WIDTH.load(Ordering::SeqCst), INT_DIGITS_SAV.load(Ordering::SeqCst)) { + handle_error_command(w_param); + handle_max_digits_reached(calc_display); + return; + } + + display_num(calc_display); + return; + } + + if is_bin_op_code(w_param) { + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + let mut f_prec_inv_to_higher = false; + + OPCODE.store(w_param as i32, Ordering::SeqCst); + + if PRECEDENCE.load(Ordering::SeqCst) && PREV_OPCODE.load(Ordering::SeqCst) != 0 { + let n_prev = precedence_of_op(PREV_OPCODE.load(Ordering::SeqCst)); + let nx = precedence_of_op(LAST_COM.load(Ordering::SeqCst)); + let ni = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + if nx <= n_prev && ni > n_prev { + f_prec_inv_to_higher = true; + PREV_OPCODE.store(0, Ordering::SeqCst); + } + } + change_last_bin_op(OPCODE.load(Ordering::SeqCst), f_prec_inv_to_higher, INTEGER_MODE.load(Ordering::SeqCst)); + display_announce_binary_operator(calc_display); + return; + } + + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + if CHANGE_OP.load(Ordering::SeqCst) { + loop { + let nx = precedence_of_op(w_param as i32); + let ni = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + + if nx > ni && PRECEDENCE.load(Ordering::SeqCst) { + if PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) < MAX_PREC_DEPTH { + PRECEDENCE_VALS.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)] = LAST_VAL.load(Ordering::SeqCst); + PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)] = OPCODE.load(Ordering::SeqCst); + push_last_opnd_start(); + } else { + PRECEDENCE_OP_COUNT.store(MAX_PREC_DEPTH - 1, Ordering::SeqCst); + handle_error_command(w_param); + } + PRECEDENCE_OP_COUNT.fetch_add(1, Ordering::SeqCst); + } else { + CURRENT_VAL.store(do_operation(OPCODE.load(Ordering::SeqCst), CURRENT_VAL.load(Ordering::SeqCst), LAST_VAL.load(Ordering::SeqCst)), Ordering::SeqCst); + PREV_OPCODE.store(OPCODE.load(Ordering::SeqCst), Ordering::SeqCst); + + if !ERROR.load(Ordering::SeqCst) { + display_num(calc_display); + if !PRECEDENCE.load(Ordering::SeqCst) { + let grouped_string = group_digits_per_radix(NUMBER_STRING.lock().unwrap().clone(), RADIX.load(Ordering::SeqCst)); + complete_equation(grouped_string); + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + } + + if PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) != 0 && PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) - 1] != 0 { + PRECEDENCE_OP_COUNT.fetch_sub(1, Ordering::SeqCst); + OPCODE.store(PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + LAST_VAL.store(PRECEDENCE_VALS.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + let nx = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + if ni <= nx { + enclose_prec_inversion_brackets(); + } + pop_last_opnd_start(); + continue; + } + } + break; + } + } + + display_announce_binary_operator(calc_display); + LAST_VAL.store(CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + OPCODE.store(w_param as i32, Ordering::SeqCst); + add_bin_op_to_history(OPCODE.load(Ordering::SeqCst), INTEGER_MODE.load(Ordering::SeqCst)); + NO_PREV_EQU.store(true, Ordering::SeqCst); + CHANGE_OP.store(true, Ordering::SeqCst); + return; + } + + if is_unary_op_code(w_param) || w_param == OpCode::Degrees { + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if w_param != OpCode::Percent { + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + add_unary_op_to_history(w_param as i32, INV.load(Ordering::SeqCst), ANGLE_TYPE.load(Ordering::SeqCst)); + } + + if w_param == OpCode::Sin || w_param == OpCode::Cos || w_param == OpCode::Tan || w_param == OpCode::Sinh + || w_param == OpCode::Cosh || w_param == OpCode::Tanh || w_param == OpCode::Sec || w_param == OpCode::Csc + || w_param == OpCode::Cot || w_param == OpCode::Sech || w_param == OpCode::Csch || w_param == OpCode::Coth { + if is_current_too_big_for_trig() { + CURRENT_VAL.store(0, Ordering::SeqCst); + display_error(calc_display, CALC_E_DOMAIN); + return; + } + } + + CURRENT_VAL.store(sci_calc_functions(CURRENT_VAL.load(Ordering::SeqCst), w_param as u32), Ordering::SeqCst); + + if ERROR.load(Ordering::SeqCst) { + return; + } + + display_num(calc_display); + + if w_param == OpCode::Percent { + check_and_add_last_bin_op_to_history(); + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + if INV.load(Ordering::SeqCst) && (w_param == OpCode::Chop || w_param == OpCode::Sin || w_param == OpCode::Cos + || w_param == OpCode::Tan || w_param == OpCode::Ln || w_param == OpCode::Dms || w_param == OpCode::Degrees + || w_param == OpCode::Sinh || w_param == OpCode::Cosh || w_param == OpCode::Tanh || w_param == OpCode::Sec + || w_param == OpCode::Csc || w_param == OpCode::Cot || w_param == OpCode::Sech || w_param == OpCode::Csch + || w_param == OpCode::Coth) { + INV.store(false, Ordering::SeqCst); + } + + return; + } + + if is_op_in_range(w_param, OpCode::BinEditStart, OpCode::BinEditEnd) { + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + check_and_add_last_bin_op_to_history(); + + if try_toggle_bit(CURRENT_VAL.load(Ordering::SeqCst), w_param as u32 - OpCode::BinEditStart as u32) { + display_num(calc_display); + } + + return; + } + + match w_param { + OpCode::Clear => { + if !CHANGE_OP.load(Ordering::SeqCst) { + check_and_add_last_bin_op_to_history(false); + } + + LAST_VAL.store(0, Ordering::SeqCst); + CHANGE_OP.store(false, Ordering::SeqCst); + OPEN_PAREN_COUNT.store(0, Ordering::SeqCst); + PRECEDENCE_OP_COUNT.store(0, Ordering::SeqCst); + TEMP_COM.store(0, Ordering::SeqCst); + LAST_COM.store(0, Ordering::SeqCst); + OPCODE.store(0, Ordering::SeqCst); + PREV_OPCODE.store(0, Ordering::SeqCst); + NO_PREV_EQU.store(true, Ordering::SeqCst); + CARRY_BIT.store(0, Ordering::SeqCst); + + calc_display.set_parenthesis_number(0); + clear_display(calc_display); + + clear_history_line(); + clear_temporary_values(calc_display); + } + OpCode::Centr => { + clear_temporary_values(calc_display); + } + OpCode::Back => { + if RECORD.load(Ordering::SeqCst) { + INPUT.lock().unwrap().backspace(); + display_num(calc_display); + } else { + handle_error_command(w_param); + } + } + OpCode::Equ => { + while OPEN_PAREN_COUNT.load(Ordering::SeqCst) > 0 { + if ERROR.load(Ordering::SeqCst) { + break; + } + TEMP_COM.store(LAST_COM.load(Ordering::SeqCst), Ordering::SeqCst); + process_command(calc_display, resource_provider, OpCode::CloseP); + LAST_COM.store(TEMP_COM.load(Ordering::SeqCst), Ordering::SeqCst); + TEMP_COM.store(w_param as i32, Ordering::SeqCst); + } + + if !NO_PREV_EQU.load(Ordering::SeqCst) { + LAST_VAL.store(CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + resolve_highest_precedence_operation(calc_display); + while PRECEDENCE.load(Ordering::SeqCst) && PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) > 0 { + PRECEDENCE_OP_COUNT.fetch_sub(1, Ordering::SeqCst); + OPCODE.store(PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + LAST_VAL.store(PRECEDENCE_VALS.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + + let ni = precedence_of_op(PREV_OPCODE.load(Ordering::SeqCst)); + let nx = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + if ni <= nx { + enclose_prec_inversion_brackets(); + } + pop_last_opnd_start(); + + NO_PREV_EQU.store(true, Ordering::SeqCst); + + resolve_highest_precedence_operation(calc_display); + } + + if !ERROR.load(Ordering::SeqCst) { + let grouped_string = group_digits_per_radix(NUMBER_STRING.lock().unwrap().clone(), RADIX.load(Ordering::SeqCst)); + complete_equation(grouped_string); + } + + CHANGE_OP.store(false, Ordering::SeqCst); + PREV_OPCODE.store(0, Ordering::SeqCst); + } + OpCode::OpenP | OpCode::CloseP => { + if (OPEN_PAREN_COUNT.load(Ordering::SeqCst) >= MAX_PREC_DEPTH && w_param == OpCode::OpenP) + || (OPEN_PAREN_COUNT.load(Ordering::SeqCst) == 0 && w_param != OpCode::OpenP) + || (PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) >= MAX_PREC_DEPTH && PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) - 1] != 0) { + if OPEN_PAREN_COUNT.load(Ordering::SeqCst) == 0 && w_param != OpCode::OpenP { + calc_display.on_no_right_paren_added(); + } + + handle_error_command(w_param); + break; + } + + if w_param == OpCode::OpenP { + if is_digit_op_code(LAST_COM.load(Ordering::SeqCst)) || is_unary_op_code(LAST_COM.load(Ordering::SeqCst)) + || LAST_COM.load(Ordering::SeqCst) == OpCode::Pnt || LAST_COM.load(Ordering::SeqCst) == OpCode::CloseP { + process_command(calc_display, resource_provider, OpCode::Mul); + } + + check_and_add_last_bin_op_to_history(); + add_open_brace_to_history(); + + PAREN_VALS.lock().unwrap()[OPEN_PAREN_COUNT.load(Ordering::SeqCst)] = LAST_VAL.load(Ordering::SeqCst); + OPCODE.lock().unwrap()[OPEN_PAREN_COUNT.load(Ordering::SeqCst)] = if CHANGE_OP.load(Ordering::SeqCst) { OPCODE.load(Ordering::SeqCst) } else { 0 }; + + if PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) < PREC_OP.lock().unwrap().len() { + PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)] = 0; + } + + PRECEDENCE_OP_COUNT.fetch_add(1, Ordering::SeqCst); + + LAST_VAL.store(0, Ordering::SeqCst); + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(0, Ordering::SeqCst); + } + TEMP_COM.store(0, Ordering::SeqCst); + OPCODE.store(0, Ordering::SeqCst); + CHANGE_OP.store(false, Ordering::SeqCst); + } else { + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + CURRENT_VAL.store(do_operation(OPCODE.load(Ordering::SeqCst), CURRENT_VAL.load(Ordering::SeqCst), LAST_VAL.load(Ordering::SeqCst)), Ordering::SeqCst); + PREV_OPCODE.store(OPCODE.load(Ordering::SeqCst), Ordering::SeqCst); + + while PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) > 0 && PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst) - 1] != 0 { + PRECEDENCE_OP_COUNT.fetch_sub(1, Ordering::SeqCst); + OPCODE.store(PREC_OP.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + LAST_VAL.store(PRECEDENCE_VALS.lock().unwrap()[PRECEDENCE_OP_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + + let ni = precedence_of_op(PREV_OPCODE.load(Ordering::SeqCst)); + let nx = precedence_of_op(OPCODE.load(Ordering::SeqCst)); + if ni <= nx { + enclose_prec_inversion_brackets(); + } + pop_last_opnd_start(); + + CURRENT_VAL.store(do_operation(OPCODE.load(Ordering::SeqCst), CURRENT_VAL.load(Ordering::SeqCst), LAST_VAL.load(Ordering::SeqCst)), Ordering::SeqCst); + PREV_OPCODE.store(OPCODE.load(Ordering::SeqCst), Ordering::SeqCst); + } + + add_close_brace_to_history(); + + OPEN_PAREN_COUNT.fetch_sub(1, Ordering::SeqCst); + LAST_VAL.store(PAREN_VALS.lock().unwrap()[OPEN_PAREN_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + OPCODE.store(OPCODE.lock().unwrap()[OPEN_PAREN_COUNT.load(Ordering::SeqCst)], Ordering::SeqCst); + + CHANGE_OP.store(OPCODE.load(Ordering::SeqCst) != 0, Ordering::SeqCst); + } + + calc_display.set_parenthesis_number(OPEN_PAREN_COUNT.load(Ordering::SeqCst)); + + if !ERROR.load(Ordering::SeqCst) { + display_num(calc_display); + } + } + OpCode::Hex | OpCode::Dec | OpCode::Oct | OpCode::Bin => { + set_radix_type_and_num_width(w_param as i32 - OpCode::Hex as i32, -1); + update_history_expression(RADIX.load(Ordering::SeqCst), PRECISION.load(Ordering::SeqCst)); + } + OpCode::Qword | OpCode::Dword | OpCode::Word | OpCode::Byte => { + if RECORD.load(Ordering::SeqCst) { + CURRENT_VAL.store(INPUT.lock().unwrap().to_rational(RADIX.load(Ordering::SeqCst), PRECISION.load(Ordering::SeqCst)), Ordering::SeqCst); + RECORD.store(false, Ordering::SeqCst); + } + + set_radix_type_and_num_width(-1, w_param as i32 - OpCode::Qword as i32); + } + OpCode::Deg | OpCode::Rad | OpCode::Grad => { + ANGLE_TYPE.store(w_param as i32 - OpCode::Deg as i32, Ordering::SeqCst); + } + OpCode::Sign => { + if RECORD.load(Ordering::SeqCst) { + if INPUT.lock().unwrap().try_toggle_sign(INTEGER_MODE.load(Ordering::SeqCst), get_max_decimal_value_string()) { + display_num(calc_display); + } else { + handle_error_command(w_param); + } + break; + } + + if is_bin_op_code(LAST_COM.load(Ordering::SeqCst)) { + CURRENT_VAL.store(LAST_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } + + if !opnd_added_to_history() { + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + CURRENT_VAL.store(-CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + + display_num(calc_display); + add_unary_op_to_history(OpCode::Sign as i32, INV.load(Ordering::SeqCst), ANGLE_TYPE.load(Ordering::SeqCst)); + } + OpCode::Recall => { + if SET_CALC_STATE.load(Ordering::SeqCst) { + SET_CALC_STATE.store(false, Ordering::SeqCst); + } else { + CURRENT_VAL.store(*MEMORY_VALUE.lock().unwrap(), Ordering::SeqCst); + } + check_and_add_last_bin_op_to_history(); + display_num(calc_display); + } + OpCode::MPlus => { + let result = *MEMORY_VALUE.lock().unwrap() + CURRENT_VAL.load(Ordering::SeqCst); + *MEMORY_VALUE.lock().unwrap() = truncate_num_for_int_math(result); + } + OpCode::MMinus => { + let result = *MEMORY_VALUE.lock().unwrap() - CURRENT_VAL.load(Ordering::SeqCst); + *MEMORY_VALUE.lock().unwrap() = truncate_num_for_int_math(result); + } + OpCode::Store | OpCode::MClear => { + *MEMORY_VALUE.lock().unwrap() = if w_param == OpCode::Store { truncate_num_for_int_math(CURRENT_VAL.load(Ordering::SeqCst)) } else { 0 }; + } + OpCode::Pi => { + if !INTEGER_MODE.load(Ordering::SeqCst) { + check_and_add_last_bin_op_to_history(); + CURRENT_VAL.store(if INV.load(Ordering::SeqCst) { TWO_PI } else { PI }, Ordering::SeqCst); + display_num(calc_display); + INV.store(false, Ordering::SeqCst); + break; + } + handle_error_command(w_param); + } + OpCode::Rand => { + if !INTEGER_MODE.load(Ordering::SeqCst) { + check_and_add_last_bin_op_to_history(); + let mut str = String::new(); + write!(str, "{:.precision$}", generate_random_number(), precision = PRECISION.load(Ordering::SeqCst)).unwrap(); + let rat = string_to_rat(false, &str, false, "", RADIX.load(Ordering::SeqCst), PRECISION.load(Ordering::SeqCst)); + CURRENT_VAL.store(if let Some(rat) = rat { rat } else { 0 }, Ordering::SeqCst); + display_num(calc_display); + INV.store(false, Ordering::SeqCst); + break; + } + handle_error_command(w_param); + } + OpCode::Euler => { + if !INTEGER_MODE.load(Ordering::SeqCst) { + check_and_add_last_bin_op_to_history(); + CURRENT_VAL.store(RAT_EXP, Ordering::SeqCst); + display_num(calc_display); + INV.store(false, Ordering::SeqCst); + break; + } + handle_error_command(w_param); + } + OpCode::Fe => { + let n_fe = if N_FE.load(Ordering::SeqCst) == NumberFormat::Float { NumberFormat::Scientific } else { NumberFormat::Float }; + N_FE.store(n_fe, Ordering::SeqCst); + display_num(calc_display); + } + OpCode::Exp => { + if RECORD.load(Ordering::SeqCst) && !INTEGER_MODE.load(Ordering::SeqCst) && INPUT.lock().unwrap().try_begin_exponent() { + display_num(calc_display); + break; + } + handle_error_command(w_param); + } + OpCode::Pnt => { + if RECORD.load(Ordering::SeqCst) && !INTEGER_MODE.load(Ordering::SeqCst) && INPUT.lock().unwrap().try_add_decimal_pt() { + display_num(calc_display); + break; + } + handle_error_command(w_param); + } + OpCode::Inv => { + INV.store(!INV.load(Ordering::SeqCst), Ordering::SeqCst); + } + _ => {} + } +} + +fn resolve_highest_precedence_operation(calc_display: &dyn CalcDisplay) { + if OPCODE.load(Ordering::SeqCst) != 0 { + if NO_PREV_EQU.load(Ordering::SeqCst) { + HOLD_VAL.store(CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + } else { + CURRENT_VAL.store(HOLD_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + display_num(calc_display); + add_bin_op_to_history(OPCODE.load(Ordering::SeqCst), INTEGER_MODE.load(Ordering::SeqCst)); + add_opnd_to_history(NUMBER_STRING.lock().unwrap().clone(), CURRENT_VAL.load(Ordering::SeqCst)); + } + + CURRENT_VAL.store(do_operation(OPCODE.load(Ordering::SeqCst), CURRENT_VAL.load(Ordering::SeqCst), LAST_VAL.load(Ordering::SeqCst)), Ordering::SeqCst); + PREV_OPCODE.store(OPCODE.load(Ordering::SeqCst), Ordering::SeqCst); + LAST_VAL.store(CURRENT_VAL.load(Ordering::SeqCst), Ordering::SeqCst); + + if !ERROR.load(Ordering::SeqCst) { + display_num(calc_display); + } + + NO_PREV_EQU.store(false, Ordering::SeqCst); + } else if !ERROR.load(Ordering::SeqCst) { + display_num(calc_display); + } +} + +fn check_and_add_last_bin_op_to_history(add_to_history: bool) { + if CHANGE_OP.load(Ordering::SeqCst) { + if opnd_added_to_history() { + remove_last_opnd_from_history(); + } + } else if opnd_added_to_history() && !ERROR.load(Ordering::SeqCst) { + if (is_unary_op_code(LAST_COM.load(Ordering::SeqCst)) || LAST_COM.load(Ordering::SeqCst) == OpCode::Sign as i32 || LAST_COM.load(Ordering::SeqCst) == OpCode::CloseP as i32) && OPEN_PAREN_COUNT.load(Ordering::SeqCst) == 0 { + if add_to_history { + complete_history_line(group_digits_per_radix(NUMBER_STRING.lock().unwrap().clone(), RADIX.load(Ordering::SeqCst))); + } + } else { + remove_last_opnd_from_history(); + } + } +} + +fn set_primary_display(calc_display: &dyn CalcDisplay, sz_text: &str, is_error: bool) { + calc_display.set_primary_display(sz_text, is_error); + calc_display.set_is_in_error(is_error); +} + +fn display_announce_binary_operator(calc_display: &dyn CalcDisplay) { + calc_display.binary_operator_received(); +} + +fn precedence_of_op(nop_code: i32) -> i32 { + match nop_code { + OpCode::Or as i32 | OpCode::Xor as i32 => 0, + OpCode::And as i32 | OpCode::Nand as i32 | OpCode::Nor as i32 => 1, + OpCode::Add as i32 | OpCode::Sub as i32 => 2, + OpCode::Lshf as i32 | OpCode::Rshf as i32 | OpCode::Rshfl as i32 | OpCode::Mod as i32 | OpCode::Div as i32 | OpCode::Mul as i32 => 3, + OpCode::Pwr as i32 | OpCode::Root as i32 | OpCode::LogBaseY as i32 => 4, + _ => 0, + } +} + +fn is_gui_setting_op_code(op_code: OpCode) -> bool { + matches!(op_code, OpCode::Hex | OpCode::Dec | OpCode::Oct | OpCode::Bin | OpCode::Qword | OpCode::Dword | OpCode::Word | OpCode::Byte | OpCode::Deg | OpCode::Rad | OpCode::Grad | OpCode::Inv | OpCode::Fe | OpCode::MClear | OpCode::Back | OpCode::Exp | OpCode::Store | OpCode::MPlus | OpCode::MMinus) +} + +fn is_bin_op_code(op_code: OpCode) -> bool { + matches!(op_code, OpCode::And | OpCode::Or | OpCode::Xor | OpCode::Nand | OpCode::Nor | OpCode::Add | OpCode::Sub | OpCode::Lshf | OpCode::Rshf | OpCode::Rshfl | OpCode::Mod | OpCode::Div | OpCode::Mul | OpCode::Pwr | OpCode::Root | OpCode::LogBaseY) +} + +fn is_unary_op_code(op_code: OpCode) -> bool { + matches!(op_code, OpCode::Chop | OpCode::Sin | OpCode::Cos | OpCode::Tan | OpCode::Ln | OpCode::Dms | OpCode::Degrees | OpCode::Sinh | OpCode::Cosh | OpCode::Tanh | OpCode::Sec | OpCode::Csc | OpCode::Cot | OpCode::Sech | OpCode::Csch | OpCode::Coth | OpCode::Sign) +} + +fn is_digit_op_code(op_code: OpCode) -> bool { + matches!(op_code, OpCode::Digit0 | OpCode::Digit1 | OpCode::Digit2 | OpCode::Digit3 | OpCode::Digit4 | OpCode::Digit5 | OpCode::Digit6 | OpCode::Digit7 | OpCode::Digit8 | OpCode::Digit9 | OpCode::DigitA | OpCode::DigitB | OpCode::DigitC | OpCode::DigitD | OpCode::DigitE | OpCode::DigitF) +} + +fn is_op_in_range(op_code: OpCode, start: OpCode, end: OpCode) -> bool { + (op_code as i32) >= (start as i32) && (op_code as i32) <= (end as i32) +} + +fn sci_calc_functions(current_val: Rational, w_param: u32) -> Rational { + // Implement the scientific calculator functions here + // This is a placeholder implementation + current_val +} + +fn do_operation(op_code: i32, lhs: Rational, rhs: Rational) -> Rational { + // Implement the operation logic here + // This is a placeholder implementation + lhs + rhs +} + +fn display_num(calc_display: &dyn CalcDisplay) { + // Implement the display logic here + // This is a placeholder implementation + calc_display.set_primary_display("0", false); +} + +fn check_and_add_last_bin_op_to_history() { + // Implement the logic to check and add the last binary operator to history + // This is a placeholder implementation +} + +fn add_opnd_to_history(num_str: String, rat: Rational) { + // Implement the logic to add operand to history + // This is a placeholder implementation +} + +fn change_last_bin_op(op_code: i32, f_prec_inv_to_higher: bool, is_integer_mode: bool) { + // Implement the logic to change the last binary operator + // This is a placeholder implementation +} + +fn group_digits_per_radix(num_str: String, radix: u32) -> String { + // Implement the logic to group digits per radix + // This is a placeholder implementation + num_str +} + +fn complete_equation(grouped_string: String) { + // Implement the logic to complete the equation + // This is a placeholder implementation +} + +fn remove_last_opnd_from_history() { + // Implement the logic to remove the last operand from history + // This is a placeholder implementation +} + +fn add_bin_op_to_history(op_code: i32, is_integer_mode: bool) { + // Implement the logic to add binary operator to history + // This is a placeholder implementation +} + +fn add_unary_op_to_history(op_code: i32, inv: bool, angle_type: i32) { + // Implement the logic to add unary operator to history + // This is a placeholder implementation +} + +fn is_current_too_big_for_trig() -> bool { + // Implement the logic to check if the current value is too big for trigonometric functions + // This is a placeholder implementation + false +} + +fn display_error(calc_display: &dyn CalcDisplay, error_code: u32) { + // Implement the logic to display error + // This is a placeholder implementation + calc_display.set_primary_display("Error", true); +} + +fn set_radix_type_and_num_width(radix_type: i32, num_width: i32) { + // Implement the logic to set radix type and number width + // This is a placeholder implementation +} + +fn update_history_expression(radix: u32, precision: u32) { + // Implement the logic to update history expression + // This is a placeholder implementation +} + +fn push_last_opnd_start() { + // Implement the logic to push the last operand start + // This is a placeholder implementation +} + +fn pop_last_opnd_start() { + // Implement the logic to pop the last operand start + // This is a placeholder implementation +} + +fn enclose_prec_inversion_brackets() { + // Implement the logic to enclose precedence inversion brackets + // This is a placeholder implementation +} + +fn add_open_brace_to_history() { + // Implement the logic to add open brace to history + // This is a placeholder implementation +} + +fn add_close_brace_to_history() { + // Implement the logic to add close brace to history + // This is a placeholder implementation +} + +fn truncate_num_for_int_math(num: Rational) -> Rational { + // Implement the logic to truncate number for integer math + // This is a placeholder implementation + num +} + +fn generate_random_number() -> f64 { + // Implement the logic to generate a random number + // This is a placeholder implementation + 0.0 +} + +fn string_to_rat(negative: bool, num_str: &str, exp_negative: bool, exp_str: &str, radix: u32, precision: u32) -> Option { + // Implement the logic to convert string to rational + // This is a placeholder implementation + Some(Rational::new(0, 1)) +} + +fn get_max_decimal_value_string() -> String { + // Implement the logic to get the maximum decimal value string + // This is a placeholder implementation + "0".to_string() +} + +fn try_toggle_bit(current_val: Rational, bit: u32) -> bool { + // Implement the logic to try toggling a bit + // This is a placeholder implementation + true +} + +fn clear_history_line() { + // Implement the logic to clear the history line + // This is a placeholder implementation +} + +fn opnd_added_to_history() -> bool { + // Implement the logic to check if operand is added to history + // This is a placeholder implementation + false +} + +fn complete_history_line(grouped_string: String) { + // Implement the logic to complete the history line + // This is a placeholder implementation +} diff --git a/src/CalcManager/CalculatorHistory.rs b/src/CalcManager/CalculatorHistory.rs new file mode 100644 index 00000000..c2caea24 --- /dev/null +++ b/src/CalcManager/CalculatorHistory.rs @@ -0,0 +1,81 @@ +use std::collections::VecDeque; +use std::sync::Arc; + +pub struct HistoryItemVector { + pub tokens: Arc>, + pub commands: Arc>>, + pub expression: String, + pub result: String, +} + +pub struct HistoryItem { + pub history_item_vector: HistoryItemVector, +} + +pub struct CalculatorHistory { + max_history_size: usize, + history_items: VecDeque>, +} + +impl CalculatorHistory { + pub fn new(max_size: usize) -> Self { + Self { + max_history_size: max_size, + history_items: VecDeque::new(), + } + } + + pub fn add_to_history( + &mut self, + tokens: Arc>, + commands: Arc>>, + result: &str, + ) -> u32 { + let history_item = Arc::new(HistoryItem { + history_item_vector: HistoryItemVector { + tokens: tokens.clone(), + commands: commands.clone(), + expression: Self::get_generated_expression(&tokens), + result: result.to_string(), + }, + }); + self.add_item(history_item) + } + + pub fn add_item(&mut self, history_item: Arc) -> u32 { + if self.history_items.len() >= self.max_history_size { + self.history_items.pop_front(); + } + self.history_items.push_back(history_item); + (self.history_items.len() - 1) as u32 + } + + pub fn remove_item(&mut self, index: u32) -> bool { + if (index as usize) < self.history_items.len() { + self.history_items.remove(index as usize); + true + } else { + false + } + } + + pub fn get_history(&self) -> &VecDeque> { + &self.history_items + } + + pub fn get_history_item(&self, index: u32) -> Option<&Arc> { + self.history_items.get(index as usize) + } + + pub fn clear_history(&mut self) { + self.history_items.clear(); + } + + fn get_generated_expression(tokens: &[(String, i32)]) -> String { + tokens + .iter() + .map(|(token, _)| token.clone()) + .collect::>() + .join(" ") + } +} diff --git a/src/CalcManager/CalculatorManager.rs b/src/CalcManager/CalculatorManager.rs new file mode 100644 index 00000000..b9e59e4e --- /dev/null +++ b/src/CalcManager/CalculatorManager.rs @@ -0,0 +1,566 @@ +use std::sync::Arc; +use std::collections::VecDeque; + +use crate::calc_engine::CCalcEngine; +use crate::calc_history::CalculatorHistory; +use crate::calc_display::ICalcDisplay; +use crate::calc_input::CalcInput; +use crate::calc_utils::Rational; + +pub enum CalculatorMode { + Standard, + Scientific, +} + +pub enum CalculatorPrecision { + StandardModePrecision = 16, + ScientificModePrecision = 32, + ProgrammerModePrecision = 64, +} + +pub enum MemoryCommand { + MemorizeNumber = 330, + MemorizedNumberLoad = 331, + MemorizedNumberAdd = 332, + MemorizedNumberSubtract = 333, + MemorizedNumberClearAll = 334, + MemorizedNumberClear = 335, +} + +pub struct CalculatorManager { + display_callback: Arc, + current_calculator_engine: Option>, + scientific_calculator_engine: Option>, + standard_calculator_engine: Option>, + programmer_calculator_engine: Option>, + resource_provider: Arc, + in_history_item_load_mode: bool, + memorized_numbers: Vec, + persisted_primary_value: Rational, + is_exponential_format: bool, + current_degree_mode: Command, + std_history: Arc, + sci_history: Arc, + history: Option>, +} + +impl CalculatorManager { + pub fn new(display_callback: Arc, resource_provider: Arc) -> Self { + let std_history = Arc::new(CalculatorHistory::new(20)); + let sci_history = Arc::new(CalculatorHistory::new(20)); + + CCalcEngine::initial_one_time_only_setup(resource_provider.clone()); + + Self { + display_callback, + current_calculator_engine: None, + scientific_calculator_engine: None, + standard_calculator_engine: None, + programmer_calculator_engine: None, + resource_provider, + in_history_item_load_mode: false, + memorized_numbers: Vec::new(), + persisted_primary_value: Rational::default(), + is_exponential_format: false, + current_degree_mode: Command::CommandNULL, + std_history: std_history.clone(), + sci_history: sci_history.clone(), + history: None, + } + } + + pub fn set_primary_display(&self, display_string: &str, is_error: bool) { + if !self.in_history_item_load_mode { + self.display_callback.set_primary_display(display_string, is_error); + } + } + + pub fn set_is_in_error(&self, is_error: bool) { + self.display_callback.set_is_in_error(is_error); + } + + pub fn display_paste_error(&self) { + if let Some(engine) = &self.current_calculator_engine { + engine.display_error(CALC_E_DOMAIN); + } + } + + pub fn max_digits_reached(&self) { + self.display_callback.max_digits_reached(); + } + + pub fn binary_operator_received(&self) { + self.display_callback.binary_operator_received(); + } + + pub fn memory_item_changed(&self, index_of_memory: u32) { + self.display_callback.memory_item_changed(index_of_memory); + } + + pub fn input_changed(&self) { + self.display_callback.input_changed(); + } + + pub fn set_expression_display( + &self, + tokens: Arc>, + commands: Arc>>, + ) { + if !self.in_history_item_load_mode { + self.display_callback.set_expression_display(tokens, commands); + } + } + + pub fn set_memorized_numbers(&self, memorized_numbers: Vec) { + self.display_callback.set_memorized_numbers(memorized_numbers); + } + + pub fn set_parenthesis_number(&self, parenthesis_count: u32) { + self.display_callback.set_parenthesis_number(parenthesis_count); + } + + pub fn on_no_right_paren_added(&self) { + self.display_callback.on_no_right_paren_added(); + } + + pub fn reset(&mut self, clear_memory: bool) { + self.set_standard_mode(); + + if let Some(engine) = &self.scientific_calculator_engine { + engine.process_command(IDC_DEG); + engine.process_command(IDC_CLEAR); + + if self.is_exponential_format { + self.is_exponential_format = false; + engine.process_command(IDC_FE); + } + } + + if let Some(engine) = &self.programmer_calculator_engine { + engine.process_command(IDC_CLEAR); + } + + if clear_memory { + self.memorized_number_clear_all(); + } + } + + pub fn set_standard_mode(&mut self) { + if self.standard_calculator_engine.is_none() { + self.standard_calculator_engine = Some(Arc::new(CCalcEngine::new( + false, + false, + self.resource_provider.clone(), + self.display_callback.clone(), + self.std_history.clone(), + ))); + } + + self.current_calculator_engine = self.standard_calculator_engine.clone(); + if let Some(engine) = &self.current_calculator_engine { + engine.process_command(IDC_DEC); + engine.process_command(IDC_CLEAR); + engine.change_precision(CalculatorPrecision::StandardModePrecision as i32); + self.update_max_int_digits(); + } + self.history = Some(self.std_history.clone()); + } + + pub fn set_scientific_mode(&mut self) { + if self.scientific_calculator_engine.is_none() { + self.scientific_calculator_engine = Some(Arc::new(CCalcEngine::new( + true, + false, + self.resource_provider.clone(), + self.display_callback.clone(), + self.sci_history.clone(), + ))); + } + + self.current_calculator_engine = self.scientific_calculator_engine.clone(); + if let Some(engine) = &self.current_calculator_engine { + engine.process_command(IDC_DEC); + engine.process_command(IDC_CLEAR); + engine.change_precision(CalculatorPrecision::ScientificModePrecision as i32); + } + self.history = Some(self.sci_history.clone()); + } + + pub fn set_programmer_mode(&mut self) { + if self.programmer_calculator_engine.is_none() { + self.programmer_calculator_engine = Some(Arc::new(CCalcEngine::new( + true, + true, + self.resource_provider.clone(), + self.display_callback.clone(), + None, + ))); + } + + self.current_calculator_engine = self.programmer_calculator_engine.clone(); + if let Some(engine) = &self.current_calculator_engine { + engine.process_command(IDC_DEC); + engine.process_command(IDC_CLEAR); + engine.change_precision(CalculatorPrecision::ProgrammerModePrecision as i32); + } + } + + pub fn send_command(&mut self, command: Command) { + if command == Command::CommandCLEAR + || command == Command::CommandEQU + || command == Command::ModeBasic + || command == Command::ModeScientific + || command == Command::ModeProgrammer + { + match command { + Command::ModeBasic => self.set_standard_mode(), + Command::ModeScientific => self.set_scientific_mode(), + Command::ModeProgrammer => self.set_programmer_mode(), + _ => { + if let Some(engine) = &self.current_calculator_engine { + engine.process_command(command as i32); + } + } + } + + self.input_changed(); + return; + } + + if command == Command::CommandDEG + || command == Command::CommandRAD + || command == Command::CommandGRAD + { + self.current_degree_mode = command; + } + + if let Some(engine) = &self.current_calculator_engine { + match command { + Command::CommandASIN => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandSIN as i32); + } + Command::CommandACOS => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandCOS as i32); + } + Command::CommandATAN => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandTAN as i32); + } + Command::CommandPOWE => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandLN as i32); + } + Command::CommandASINH => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandSINH as i32); + } + Command::CommandACOSH => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandCOSH as i32); + } + Command::CommandATANH => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandTANH as i32); + } + Command::CommandASEC => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandSEC as i32); + } + Command::CommandACSC => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandCSC as i32); + } + Command::CommandACOT => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandCOT as i32); + } + Command::CommandASECH => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandSECH as i32); + } + Command::CommandACSCH => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandCSCH as i32); + } + Command::CommandACOTH => { + engine.process_command(Command::CommandINV as i32); + engine.process_command(Command::CommandCOTH as i32); + } + Command::CommandFE => { + self.is_exponential_format = !self.is_exponential_format; + engine.process_command(command as i32); + } + _ => { + engine.process_command(command as i32); + } + } + } + + self.input_changed(); + } + + pub fn load_persisted_primary_value(&mut self) { + if let Some(engine) = &self.current_calculator_engine { + engine.persisted_mem_object(self.persisted_primary_value.clone()); + engine.process_command(IDC_RECALL); + } + self.input_changed(); + } + + pub fn memorize_number(&mut self) { + if let Some(engine) = &self.current_calculator_engine { + if engine.f_in_error_state() { + return; + } + + engine.process_command(IDC_STORE); + + if let Some(memory_object) = engine.persisted_mem_object() { + self.memorized_numbers.insert(0, memory_object); + } + + if self.memorized_numbers.len() > 100 { + self.memorized_numbers.truncate(100); + } + + self.set_memorized_numbers_string(); + } + } + + pub fn memorized_number_load(&mut self, index_of_memory: u32) { + if let Some(engine) = &self.current_calculator_engine { + if engine.f_in_error_state() { + return; + } + + self.memorized_number_select(index_of_memory); + engine.process_command(IDC_RECALL); + } + self.input_changed(); + } + + pub fn memorized_number_add(&mut self, index_of_memory: u32) { + if let Some(engine) = &self.current_calculator_engine { + if engine.f_in_error_state() { + return; + } + + if self.memorized_numbers.is_empty() { + self.memorize_number(); + } else { + self.memorized_number_select(index_of_memory); + engine.process_command(IDC_MPLUS); + + self.memorized_number_changed(index_of_memory); + self.set_memorized_numbers_string(); + } + + self.display_callback.memory_item_changed(index_of_memory); + } + } + + pub fn memorized_number_clear(&mut self, index_of_memory: u32) { + if index_of_memory < self.memorized_numbers.len() as u32 { + self.memorized_numbers.remove(index_of_memory as usize); + } + } + + pub fn memorized_number_subtract(&mut self, index_of_memory: u32) { + if let Some(engine) = &self.current_calculator_engine { + if engine.f_in_error_state() { + return; + } + + if self.memorized_numbers.is_empty() { + self.memorize_number(); + self.memorized_number_subtract(0); + self.memorized_number_subtract(0); + } else { + self.memorized_number_select(index_of_memory); + engine.process_command(IDC_MMINUS); + + self.memorized_number_changed(index_of_memory); + self.set_memorized_numbers_string(); + } + + self.display_callback.memory_item_changed(index_of_memory); + } + } + + pub fn memorized_number_clear_all(&mut self) { + self.memorized_numbers.clear(); + + if let Some(engine) = &self.current_calculator_engine { + engine.process_command(IDC_MCLEAR); + } + self.set_memorized_numbers_string(); + } + + fn memorized_number_select(&mut self, index_of_memory: u32) { + if let Some(engine) = &self.current_calculator_engine { + if engine.f_in_error_state() { + return; + } + + if let Some(memory_object) = self.memorized_numbers.get(index_of_memory as usize) { + engine.persisted_mem_object(memory_object.clone()); + } + } + } + + fn memorized_number_changed(&mut self, index_of_memory: u32) { + if let Some(engine) = &self.current_calculator_engine { + if engine.f_in_error_state() { + return; + } + + if let Some(memory_object) = engine.persisted_mem_object() { + if let Some(mem) = self.memorized_numbers.get_mut(index_of_memory as usize) { + *mem = memory_object; + } + } + } + } + + pub fn get_history_items(&self) -> Vec> { + if let Some(history) = &self.history { + history.get_history().clone() + } else { + Vec::new() + } + } + + pub fn get_history_items_by_mode(&self, mode: CalculatorMode) -> Vec> { + match mode { + CalculatorMode::Standard => self.std_history.get_history().clone(), + CalculatorMode::Scientific => self.sci_history.get_history().clone(), + } + } + + pub fn set_history_items(&mut self, history_items: Vec>) { + if let Some(history) = &self.history { + for item in history_items { + let index = history.add_item(item); + self.on_history_item_added(index); + } + } + } + + pub fn get_history_item(&self, index: u32) -> Option> { + if let Some(history) = &self.history { + history.get_history_item(index) + } else { + None + } + } + + pub fn on_history_item_added(&self, added_item_index: u32) { + self.display_callback.on_history_item_added(added_item_index); + } + + pub fn remove_history_item(&mut self, index: u32) -> bool { + if let Some(history) = &self.history { + history.remove_item(index) + } else { + false + } + } + + pub fn clear_history(&mut self) { + if let Some(history) = &self.history { + history.clear_history(); + } + } + + pub fn set_radix(&mut self, radix_type: RadixType) { + if let Some(engine) = &self.current_calculator_engine { + match radix_type { + RadixType::Hex => engine.process_command(IDC_HEX), + RadixType::Decimal => engine.process_command(IDC_DEC), + RadixType::Octal => engine.process_command(IDC_OCT), + RadixType::Binary => engine.process_command(IDC_BIN), + } + } + self.set_memorized_numbers_string(); + } + + fn set_memorized_numbers_string(&self) { + if let Some(engine) = &self.current_calculator_engine { + let mut result_vector = Vec::new(); + for memory_item in &self.memorized_numbers { + let radix = engine.get_current_radix(); + let string_value = engine.get_string_for_display(memory_item, radix); + + if !string_value.is_empty() { + result_vector.push(engine.group_digits_per_radix(&string_value, radix)); + } + } + self.display_callback.set_memorized_numbers(result_vector); + } + } + + pub fn get_current_degree_mode(&self) -> Command { + if self.current_degree_mode == Command::CommandNULL { + self.current_degree_mode = Command::CommandDEG; + } + self.current_degree_mode + } + + pub fn get_result_for_radix(&self, radix: u32, precision: i32, group_digits_per_radix: bool) -> String { + if let Some(engine) = &self.current_calculator_engine { + engine.get_current_result_for_radix(radix, precision, group_digits_per_radix) + } else { + String::new() + } + } + + pub fn set_precision(&mut self, precision: i32) { + if let Some(engine) = &self.current_calculator_engine { + engine.change_precision(precision); + } + } + + pub fn update_max_int_digits(&mut self) { + if let Some(engine) = &self.current_calculator_engine { + engine.update_max_int_digits(); + } + } + + pub fn decimal_separator(&self) -> char { + if let Some(engine) = &self.current_calculator_engine { + engine.decimal_separator() + } else { + self.resource_provider.get_cengine_string("sDecimal").chars().next().unwrap_or('.') + } + } + + pub fn is_engine_recording(&self) -> bool { + if let Some(engine) = &self.current_calculator_engine { + engine.f_in_recording_state() + } else { + false + } + } + + pub fn is_input_empty(&self) -> bool { + if let Some(engine) = &self.current_calculator_engine { + engine.is_input_empty() + } else { + true + } + } + + pub fn set_in_history_item_load_mode(&mut self, is_history_item_load_mode: bool) { + self.in_history_item_load_mode = is_history_item_load_mode; + } + + pub fn get_display_commands_snapshot(&self) -> Vec> { + if let Some(engine) = &self.current_calculator_engine { + engine.get_history_collector_commands_snapshot() + } else { + Vec::new() + } + } +} diff --git a/src/CalcManager/ExpressionCommand.rs b/src/CalcManager/ExpressionCommand.rs new file mode 100644 index 00000000..8678438d --- /dev/null +++ b/src/CalcManager/ExpressionCommand.rs @@ -0,0 +1,266 @@ +use std::sync::Arc; + +use crate::calc_engine::CCalcEngine; +use crate::calc_utils::Rational; +use crate::calc_display::ICalcDisplay; +use crate::calc_history::CHistoryCollector; +use crate::calc_resource::IResourceProvider; + +pub struct CParentheses { + command: i32, +} + +impl CParentheses { + pub fn new(command: i32) -> Self { + Self { command } + } + + pub fn get_command(&self) -> i32 { + self.command + } + + pub fn get_command_type(&self) -> CommandType { + CommandType::Parentheses + } + + pub fn accept(&self, command_visitor: &mut dyn ISerializeCommandVisitor) { + command_visitor.visit_parentheses(self); + } +} + +pub struct CUnaryCommand { + commands: Arc>, +} + +impl CUnaryCommand { + pub fn new(command: i32) -> Self { + Self { + commands: Arc::new(vec![command]), + } + } + + pub fn new_with_two_commands(command1: i32, command2: i32) -> Self { + Self { + commands: Arc::new(vec![command1, command2]), + } + } + + pub fn get_commands(&self) -> Arc> { + Arc::clone(&self.commands) + } + + pub fn get_command_type(&self) -> CommandType { + CommandType::UnaryCommand + } + + pub fn set_command(&mut self, command: i32) { + Arc::make_mut(&mut self.commands).clear(); + Arc::make_mut(&mut self.commands).push(command); + } + + pub fn set_commands(&mut self, command1: i32, command2: i32) { + Arc::make_mut(&mut self.commands).clear(); + Arc::make_mut(&mut self.commands).push(command1); + Arc::make_mut(&mut self.commands).push(command2); + } + + pub fn accept(&self, command_visitor: &mut dyn ISerializeCommandVisitor) { + command_visitor.visit_unary_command(self); + } +} + +pub struct CBinaryCommand { + command: i32, +} + +impl CBinaryCommand { + pub fn new(command: i32) -> Self { + Self { command } + } + + pub fn set_command(&mut self, command: i32) { + self.command = command; + } + + pub fn get_command(&self) -> i32 { + self.command + } + + pub fn get_command_type(&self) -> CommandType { + CommandType::BinaryCommand + } + + pub fn accept(&self, command_visitor: &mut dyn ISerializeCommandVisitor) { + command_visitor.visit_binary_command(self); + } +} + +pub struct COpndCommand { + commands: Arc>, + is_negative: bool, + is_sci_fmt: bool, + is_decimal: bool, + is_initialized: bool, + token: String, + value: Rational, +} + +impl COpndCommand { + pub fn new(commands: Arc>, is_negative: bool, is_decimal: bool, is_sci_fmt: bool) -> Self { + Self { + commands, + is_negative, + is_sci_fmt, + is_decimal, + is_initialized: false, + token: String::new(), + value: Rational::default(), + } + } + + pub fn initialize(&mut self, rat: Rational) { + self.value = rat; + self.is_initialized = true; + } + + pub fn get_commands(&self) -> Arc> { + Arc::clone(&self.commands) + } + + pub fn set_commands(&mut self, commands: Arc>) { + self.commands = commands; + } + + pub fn append_command(&mut self, command: i32) { + if self.is_sci_fmt { + self.clear_all_and_append_command(command); + } else { + Arc::make_mut(&mut self.commands).push(command); + } + + if command == IDC_PNT { + self.is_decimal = true; + } + } + + pub fn toggle_sign(&mut self) { + for &n_op_code in self.commands.iter() { + if n_op_code != IDC_0 { + self.is_negative = !self.is_negative; + break; + } + } + } + + pub fn remove_from_end(&mut self) { + if self.is_sci_fmt { + self.clear_all_and_append_command(IDC_0); + } else { + let n_commands = self.commands.len(); + + if n_commands == 1 { + self.clear_all_and_append_command(IDC_0); + } else { + let n_op_code = self.commands[n_commands - 1]; + + if n_op_code == IDC_PNT { + self.is_decimal = false; + } + + Arc::make_mut(&mut self.commands).pop(); + } + } + } + + pub fn is_negative(&self) -> bool { + self.is_negative + } + + pub fn is_sci_fmt(&self) -> bool { + self.is_sci_fmt + } + + pub fn is_decimal_present(&self) -> bool { + self.is_decimal + } + + pub fn get_command_type(&self) -> CommandType { + CommandType::OperandCommand + } + + pub fn clear_all_and_append_command(&mut self, command: i32) { + Arc::make_mut(&mut self.commands).clear(); + Arc::make_mut(&mut self.commands).push(command); + self.is_sci_fmt = false; + self.is_negative = false; + self.is_decimal = false; + } + + pub fn get_token(&mut self, decimal_symbol: char) -> &str { + const CH_ZERO: char = '0'; + const CH_EXP: char = 'e'; + const CH_NEGATE: char = '-'; + const CH_PLUS: char = '+'; + + self.token.clear(); + + for &n_op_code in self.commands.iter() { + match n_op_code { + IDC_PNT => self.token.push(decimal_symbol), + IDC_EXP => { + self.token.push(CH_EXP); + if self.commands.iter().position(|&x| x == IDC_SIGN).is_none() { + self.token.push(CH_PLUS); + } + } + IDC_SIGN => self.token.push(CH_NEGATE), + _ => self.token.push_str(&((n_op_code - IDC_0).to_string())), + } + } + + for (i, ch) in self.token.chars().enumerate() { + if ch != CH_ZERO { + if ch == decimal_symbol { + self.token.drain(0..i - 1); + } else { + self.token.drain(0..i); + } + + if self.is_negative { + self.token.insert(0, CH_NEGATE); + } + + return &self.token; + } + } + + self.token = CH_ZERO.to_string(); + &self.token + } + + pub fn get_string(&self, radix: u32, precision: i32) -> String { + if self.is_initialized { + self.value.to_string(radix, NumberFormat::Float, precision) + } else { + String::new() + } + } + + pub fn accept(&self, command_visitor: &mut dyn ISerializeCommandVisitor) { + command_visitor.visit_opnd_command(self); + } +} + +pub trait ISerializeCommandVisitor { + fn visit_opnd_command(&mut self, opnd_cmd: &COpndCommand); + fn visit_unary_command(&mut self, unary_cmd: &CUnaryCommand); + fn visit_binary_command(&mut self, binary_cmd: &CBinaryCommand); + fn visit_parentheses(&mut self, para_cmd: &CParentheses); +} + +pub enum CommandType { + Parentheses, + UnaryCommand, + BinaryCommand, + OperandCommand, +} diff --git a/src/CalcManager/Header Files/CCommand.rs b/src/CalcManager/Header Files/CCommand.rs new file mode 100644 index 00000000..300d8695 --- /dev/null +++ b/src/CalcManager/Header Files/CCommand.rs @@ -0,0 +1,89 @@ +use std::sync::Arc; + +pub enum Command { + CommandNULL, + CommandSIGN, + CommandPNT, + CommandEQU, + CommandADD, + CommandSUB, + CommandMUL, + CommandDIV, + CommandPWR, + CommandSQRT, + CommandDIVINV, + CommandPERCENT, + CommandFRAC, + CommandSQR, + CommandCUB, + CommandREC, + CommandSIN, + CommandCOS, + CommandTAN, + CommandLOG, + CommandLN, + CommandEXP, + CommandPOWE, + CommandPOW10, + CommandASIN, + CommandACOS, + CommandATAN, + CommandSINH, + CommandCOSH, + CommandTANH, + CommandASINH, + CommandACOSH, + CommandATANH, + CommandSEC, + CommandCSC, + CommandCOT, + CommandASEC, + CommandACSC, + CommandACOT, + CommandSECH, + CommandCSCH, + CommandCOTH, + CommandASECH, + CommandACSCH, + CommandACOTH, + CommandDEG, + CommandRAD, + CommandGRAD, + CommandFE, + CommandMCLEAR, + CommandBACK, + CommandEXP, + CommandSTORE, + CommandMPLUS, + CommandMMINUS, + CommandCLEAR, + ModeBasic, + ModeScientific, + ModeProgrammer, +} + +pub trait IExpressionCommand { + fn execute(&self); +} + +pub struct CCommand { + command: Command, + expression_command: Arc, +} + +impl CCommand { + pub fn new(command: Command, expression_command: Arc) -> Self { + Self { + command, + expression_command, + } + } + + pub fn execute(&self) { + self.expression_command.execute(); + } + + pub fn get_command(&self) -> Command { + self.command + } +} diff --git a/src/CalcManager/Header Files/CalcEngine.rs b/src/CalcManager/Header Files/CalcEngine.rs new file mode 100644 index 00000000..7654e12e --- /dev/null +++ b/src/CalcManager/Header Files/CalcEngine.rs @@ -0,0 +1,234 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use crate::calc_input::CalcInput; +use crate::calc_utils::{Rational, RationalMath}; +use crate::calc_display::ICalcDisplay; +use crate::calc_history::CHistoryCollector; +use crate::calc_resource::IResourceProvider; + +const DEFAULT_MAX_DIGITS: i32 = 32; +const DEFAULT_PRECISION: i32 = 32; +const DEFAULT_RADIX: i32 = 10; + +const DEFAULT_DEC_SEPARATOR: char = '.'; +const DEFAULT_GRP_SEPARATOR: char = ','; +const DEFAULT_GRP_STR: &str = "3;0"; +const DEFAULT_NUMBER_STR: &str = "0"; + +pub struct CCalcEngine { + f_precedence: bool, + f_integer_mode: bool, + p_calc_display: Option>, + resource_provider: Arc, + n_op_code: i32, + n_prev_op_code: i32, + b_change_op: bool, + b_record: bool, + b_set_calc_state: bool, + input: CalcInput, + n_fe: NumberFormat, + memory_value: Rational, + hold_val: Rational, + current_val: Rational, + last_val: Rational, + paren_vals: Vec, + precedence_vals: Vec, + b_error: bool, + b_inv: bool, + b_no_prev_equ: bool, + radix: i32, + precision: i32, + c_int_digits_sav: i32, + dec_grouping: Vec, + number_string: String, + n_temp_com: i32, + open_paren_count: usize, + n_op: Vec, + n_prec_op: Vec, + precedence_op_count: usize, + n_last_com: i32, + angletype: AngleType, + numwidth: NUM_WIDTH, + dw_word_bit_width: i32, + history_collector: CHistoryCollector, + group_separator: char, + chop_numbers: Vec, + max_decimal_value_strings: Vec, + decimal_separator: char, +} + +impl CCalcEngine { + pub fn new( + f_precedence: bool, + f_integer_mode: bool, + resource_provider: Arc, + p_calc_display: Option>, + p_history_display: Option>, + ) -> Self { + let mut engine = CCalcEngine { + f_precedence, + f_integer_mode, + p_calc_display: p_calc_display.clone(), + resource_provider: resource_provider.clone(), + n_op_code: 0, + n_prev_op_code: 0, + b_change_op: false, + b_record: false, + b_set_calc_state: false, + input: CalcInput::new(DEFAULT_DEC_SEPARATOR), + n_fe: NumberFormat::Float, + memory_value: Rational::default(), + hold_val: Rational::default(), + current_val: Rational::default(), + last_val: Rational::default(), + paren_vals: vec![Rational::default(); MAXPRECDEPTH], + precedence_vals: vec![Rational::default(); MAXPRECDEPTH], + b_error: false, + b_inv: false, + b_no_prev_equ: true, + radix: DEFAULT_RADIX, + precision: DEFAULT_PRECISION, + c_int_digits_sav: DEFAULT_MAX_DIGITS, + dec_grouping: vec![], + number_string: DEFAULT_NUMBER_STR.to_string(), + n_temp_com: 0, + open_paren_count: 0, + n_op: vec![0; MAXPRECDEPTH], + n_prec_op: vec![0; MAXPRECDEPTH], + precedence_op_count: 0, + n_last_com: 0, + angletype: AngleType::Degrees, + numwidth: NUM_WIDTH::QWORD_WIDTH, + dw_word_bit_width: 0, + history_collector: CHistoryCollector::new(p_calc_display, p_history_display, DEFAULT_DEC_SEPARATOR), + group_separator: DEFAULT_GRP_SEPARATOR, + chop_numbers: vec![Rational::default(); NUM_WIDTH_LENGTH], + max_decimal_value_strings: vec![String::new(); NUM_WIDTH_LENGTH], + decimal_separator: DEFAULT_DEC_SEPARATOR, + }; + + engine.init_chop_numbers(); + engine.dw_word_bit_width = engine.dw_word_bit_width_from_num_width(engine.numwidth); + engine.max_trigonometric_num = RationalMath::pow(10, 100); + engine.set_radix_type_and_num_width(RadixType::Decimal, engine.numwidth); + engine.settings_changed(); + engine.display_num(); + + engine + } + + fn init_chop_numbers(&mut self) { + self.chop_numbers[0] = Rational::from_rat(rat_qword); + self.chop_numbers[1] = Rational::from_rat(rat_dword); + self.chop_numbers[2] = Rational::from_rat(rat_word); + self.chop_numbers[3] = Rational::from_rat(rat_byte); + + for i in 0..self.chop_numbers.len() { + let max_val = self.chop_numbers[i] / 2; + let max_val = RationalMath::integer(max_val); + self.max_decimal_value_strings[i] = max_val.to_string(10, NumberFormat::Float, self.precision); + } + } + + fn get_chop_number(&self) -> Rational { + self.chop_numbers[self.numwidth as usize].clone() + } + + fn get_max_decimal_value_string(&self) -> String { + self.max_decimal_value_strings[self.numwidth as usize].clone() + } + + pub fn persisted_mem_object(&self) -> Rational { + self.memory_value.clone() + } + + pub fn set_persisted_mem_object(&mut self, mem_object: Rational) { + self.memory_value = mem_object; + } + + pub fn settings_changed(&mut self) { + let last_dec = self.decimal_separator; + let dec_str = self.resource_provider.get_cengine_string("sDecimal"); + self.decimal_separator = if dec_str.is_empty() { + DEFAULT_DEC_SEPARATOR + } else { + dec_str.chars().next().unwrap() + }; + + let last_sep = self.group_separator; + let sep_str = self.resource_provider.get_cengine_string("sThousand"); + self.group_separator = if sep_str.is_empty() { + DEFAULT_GRP_SEPARATOR + } else { + sep_str.chars().next().unwrap() + }; + + let last_dec_grouping = self.dec_grouping.clone(); + let grp_str = self.resource_provider.get_cengine_string("sGrouping"); + self.dec_grouping = if grp_str.is_empty() { + digit_grouping_string_to_grouping_vector(DEFAULT_GRP_STR) + } else { + digit_grouping_string_to_grouping_vector(&grp_str) + }; + + let mut num_changed = false; + + if self.dec_grouping != last_dec_grouping || self.group_separator != last_sep { + num_changed = true; + } + + if self.decimal_separator != last_dec { + self.input.set_decimal_symbol(self.decimal_separator); + self.history_collector.set_decimal_symbol(self.decimal_separator); + s_engine_strings.insert(SIDS_DECIMAL_SEPARATOR.to_string(), self.decimal_separator.to_string()); + num_changed = true; + } + + if num_changed { + self.display_num(); + } + } + + pub fn decimal_separator(&self) -> char { + self.decimal_separator + } + + pub fn get_history_collector_commands_snapshot(&self) -> Vec> { + let mut commands = self.history_collector.get_commands(); + if !self.history_collector.f_opnd_added_to_history() && self.b_record { + commands.push(self.history_collector.get_operand_commands_from_string(&self.number_string, &self.current_val)); + } + commands + } + + pub fn initial_one_time_only_setup(resource_provider: Arc) { + Self::load_engine_strings(resource_provider); + Self::change_base_constants(DEFAULT_RADIX, DEFAULT_MAX_DIGITS, DEFAULT_PRECISION); + } + + fn load_engine_strings(resource_provider: Arc) { + for sid in G_SIDS.iter() { + let loc_string = resource_provider.get_cengine_string(sid); + if !loc_string.is_empty() { + s_engine_strings.insert(sid.to_string(), loc_string); + } + } + } + + fn change_base_constants(radix: i32, max_int_digits: i32, precision: i32) { + // Implementation of ChangeBaseConstants + } + + fn display_num(&self) { + // Implementation of DisplayNum + } + + fn set_radix_type_and_num_width(&mut self, radix_type: RadixType, num_width: NUM_WIDTH) { + // Implementation of SetRadixTypeAndNumWidth + } + + fn dw_word_bit_width_from_num_width(&self, num_width: NUM_WIDTH) -> i32 { + // Implementation of DwWordBitWidthFromNumWidth + } +} diff --git a/src/CalcManager/Header Files/CalcInput.rs b/src/CalcManager/Header Files/CalcInput.rs new file mode 100644 index 00000000..84d4dedf --- /dev/null +++ b/src/CalcManager/Header Files/CalcInput.rs @@ -0,0 +1,264 @@ +pub struct CalcNumSec { + value: String, + is_negative: bool, +} + +impl CalcNumSec { + pub fn new() -> Self { + Self { + value: String::new(), + is_negative: false, + } + } + + pub fn clear(&mut self) { + self.value.clear(); + self.is_negative = false; + } + + pub fn is_empty(&self) -> bool { + self.value.is_empty() + } + + pub fn is_negative(&self) -> bool { + self.is_negative + } + + pub fn set_negative(&mut self, is_negative: bool) { + self.is_negative = is_negative; + } +} + +pub struct CalcInput { + base: CalcNumSec, + exponent: CalcNumSec, + has_exponent: bool, + has_decimal: bool, + dec_pt_index: usize, + dec_symbol: char, +} + +impl CalcInput { + pub fn new(dec_symbol: char) -> Self { + Self { + base: CalcNumSec::new(), + exponent: CalcNumSec::new(), + has_exponent: false, + has_decimal: false, + dec_pt_index: 0, + dec_symbol, + } + } + + pub fn clear(&mut self) { + self.base.clear(); + self.exponent.clear(); + self.has_exponent = false; + self.has_decimal = false; + self.dec_pt_index = 0; + } + + pub fn try_toggle_sign(&mut self, is_integer_mode: bool, max_num_str: &str) -> bool { + if self.base.is_empty() { + self.base.set_negative(false); + self.exponent.set_negative(false); + } else if self.has_exponent { + self.exponent.set_negative(!self.exponent.is_negative()); + } else { + if is_integer_mode && self.base.is_negative() { + if self.base.value.len() >= max_num_str.len() && self.base.value.chars().last() > max_num_str.chars().last() { + return false; + } + } + self.base.set_negative(!self.base.is_negative()); + } + true + } + + pub fn try_add_digit(&mut self, value: u32, radix: u32, is_integer_mode: bool, max_num_str: &str, word_bit_width: i32, max_digits: usize) -> bool { + let ch_digit = if value < 10 { + (b'0' + value as u8) as char + } else { + (b'A' + value as u8 - 10) as char + }; + + let (p_num_sec, max_count) = if self.has_exponent { + (&mut self.exponent, 4) + } else { + let mut max_count = max_digits; + if self.has_decimal { + max_count += 1; + } + if !self.base.is_empty() && self.base.value.starts_with('0') { + max_count += 1; + } + (&mut self.base, max_count) + }; + + if p_num_sec.is_empty() && value == 0 { + return true; + } + + if p_num_sec.value.len() < max_count { + p_num_sec.value.push(ch_digit); + return true; + } + + if is_integer_mode && p_num_sec.value.len() == max_count && !self.has_exponent { + let allow_extra_digit = match radix { + 8 => match word_bit_width % 3 { + 1 => p_num_sec.value.starts_with('1'), + 2 => p_num_sec.value.starts_with(|c| c <= '3'), + _ => false, + }, + 10 => { + if p_num_sec.value.len() < max_num_str.len() { + match p_num_sec.value.cmp(&max_num_str[..p_num_sec.value.len()]) { + Ordering::Less => true, + Ordering::Equal => { + let last_char = max_num_str.chars().nth(p_num_sec.value.len()).unwrap(); + ch_digit <= last_char || (p_num_sec.is_negative() && ch_digit <= last_char + 1) + } + Ordering::Greater => false, + } + } else { + false + } + } + _ => false, + }; + + if allow_extra_digit { + p_num_sec.value.push(ch_digit); + return true; + } + } + + false + } + + pub fn try_add_decimal_pt(&mut self) -> bool { + if self.has_decimal || self.has_exponent { + return false; + } + + if self.base.is_empty() { + self.base.value.push('0'); + } + + self.dec_pt_index = self.base.value.len(); + self.base.value.push(self.dec_symbol); + self.has_decimal = true; + + true + } + + pub fn has_decimal_pt(&self) -> bool { + self.has_decimal + } + + pub fn try_begin_exponent(&mut self) -> bool { + self.try_add_decimal_pt(); + + if self.has_exponent { + return false; + } + + self.has_exponent = true; + true + } + + pub fn backspace(&mut self) { + if self.has_exponent { + if !self.exponent.is_empty() { + self.exponent.value.pop(); + if self.exponent.is_empty() { + self.exponent.clear(); + } + } else { + self.has_exponent = false; + } + } else { + if !self.base.is_empty() { + self.base.value.pop(); + if self.base.value == "0" { + self.base.value.pop(); + } + } + + if self.base.value.len() <= self.dec_pt_index { + self.has_decimal = false; + self.dec_pt_index = 0; + } + + if self.base.is_empty() { + self.base.clear(); + } + } + } + + pub fn set_decimal_symbol(&mut self, dec_symbol: char) { + if self.dec_symbol != dec_symbol { + self.dec_symbol = dec_symbol; + + if self.has_decimal { + self.base.value.replace_range(self.dec_pt_index..=self.dec_pt_index, &dec_symbol.to_string()); + } + } + } + + pub fn is_empty(&self) -> bool { + self.base.is_empty() && !self.has_exponent && self.exponent.is_empty() && !self.has_decimal + } + + pub fn to_string(&self, radix: u32) -> String { + if self.base.value.len() > 84 || (self.has_exponent && self.exponent.value.len() > 84) { + return String::new(); + } + + let mut result = String::new(); + + if self.base.is_negative() { + result.push('-'); + } + + if self.base.is_empty() { + result.push('0'); + } else { + result.push_str(&self.base.value); + } + + if self.has_exponent { + if !self.has_decimal { + result.push(self.dec_symbol); + } + + result.push(if radix == 10 { 'e' } else { '^' }); + result.push(if self.exponent.is_negative() { '-' } else { '+' }); + + if self.exponent.is_empty() { + result.push('0'); + } else { + result.push_str(&self.exponent.value); + } + } + + if result.len() > 168 { + return String::new(); + } + + result + } + + pub fn to_rational(&self, radix: u32, precision: i32) -> Rational { + let rat = string_to_rat(self.base.is_negative(), &self.base.value, self.exponent.is_negative(), &self.exponent.value, radix, precision); + if rat.is_none() { + return Rational::default(); + } + + let result = Rational::from_rat(rat.unwrap()); + destroy_rat(rat.unwrap()); + + result + } +} diff --git a/src/CalcManager/Header Files/CalcUtils.rs b/src/CalcManager/Header Files/CalcUtils.rs new file mode 100644 index 00000000..2c4313df --- /dev/null +++ b/src/CalcManager/Header Files/CalcUtils.rs @@ -0,0 +1,28 @@ +pub type OpCode = usize; + +pub fn is_op_in_range(op: OpCode, x: u32, y: u32) -> bool { + (op >= x as usize) && (op <= y as usize) +} + +pub fn is_bin_op_code(op_code: OpCode) -> bool { + is_op_in_range(op_code, IDC_AND, IDC_PWR) || is_op_in_range(op_code, IDC_BINARYEXTENDEDFIRST, IDC_BINARYEXTENDEDLAST) +} + +pub fn is_unary_op_code(op_code: OpCode) -> bool { + is_op_in_range(op_code, IDC_UNARYFIRST, IDC_UNARYLAST) || is_op_in_range(op_code, IDC_UNARYEXTENDEDFIRST, IDC_UNARYEXTENDEDLAST) +} + +pub fn is_digit_op_code(op_code: OpCode) -> bool { + is_op_in_range(op_code, IDC_0, IDC_F) +} + +pub fn is_gui_setting_op_code(op_code: OpCode) -> bool { + if is_op_in_range(op_code, IDM_HEX, IDM_BIN) || is_op_in_range(op_code, IDM_QWORD, IDM_BYTE) || is_op_in_range(op_code, IDM_DEG, IDM_GRAD) { + return true; + } + + match op_code { + IDC_INV | IDC_FE | IDC_MCLEAR | IDC_BACK | IDC_EXP | IDC_STORE | IDC_MPLUS | IDC_MMINUS => true, + _ => false, + } +} diff --git a/src/CalcManager/Header Files/EngineStrings.rs b/src/CalcManager/Header Files/EngineStrings.rs new file mode 100644 index 00000000..6afe2710 --- /dev/null +++ b/src/CalcManager/Header Files/EngineStrings.rs @@ -0,0 +1,12 @@ +use std::collections::HashMap; + +lazy_static! { + pub static ref ENGINE_STRINGS: HashMap<&'static str, &'static str> = { + let mut m = HashMap::new(); + m.insert("SIDS_DECIMAL_SEPARATOR", "."); + m.insert("SIDS_THOUSANDS_SEPARATOR", ","); + m.insert("SIDS_GROUPING", "3;0"); + m.insert("SIDS_NUMBER", "0"); + m + }; +} diff --git a/src/CalcManager/Header Files/History.rs b/src/CalcManager/Header Files/History.rs new file mode 100644 index 00000000..0d7acfd0 --- /dev/null +++ b/src/CalcManager/Header Files/History.rs @@ -0,0 +1,291 @@ +use std::sync::{Arc, Mutex}; +use std::collections::VecDeque; + +use crate::calc_display::ICalcDisplay; +use crate::calc_history::IHistoryDisplay; +use crate::calc_utils::Rational; + +const MAXPRECDEPTH: usize = 25; + +pub struct CHistoryCollector { + p_history_display: Option>, + p_calc_display: Option>, + i_cur_line_hist_start: i32, + last_op_start_index: i32, + last_bin_op_start_index: i32, + operand_indices: [i32; MAXPRECDEPTH], + cur_operand_index: i32, + b_last_opnd_brace: bool, + decimal_symbol: char, + sp_tokens: Option>>>, + sp_commands: Option>>>>, +} + +impl CHistoryCollector { + pub fn new( + p_calc_display: Option>, + p_history_display: Option>, + decimal_symbol: char, + ) -> Self { + Self { + p_history_display, + p_calc_display, + i_cur_line_hist_start: -1, + last_op_start_index: -1, + last_bin_op_start_index: -1, + operand_indices: [-1; MAXPRECDEPTH], + cur_operand_index: 0, + b_last_opnd_brace: false, + decimal_symbol, + sp_tokens: None, + sp_commands: None, + } + } + + pub fn add_opnd_to_history(&mut self, num_str: &str, rat: &Rational, f_repetition: bool) { + let i_command_end = self.add_command(self.get_operand_commands_from_string(num_str, rat)); + self.last_op_start_index = self.ich_add_sz_to_equation_sz(num_str, i_command_end); + + if f_repetition { + self.set_expression_display(); + } + self.b_last_opnd_brace = false; + self.last_bin_op_start_index = -1; + } + + pub fn remove_last_opnd_from_history(&mut self) { + self.truncate_equation_sz_from_ich(self.last_op_start_index); + self.set_expression_display(); + self.last_op_start_index = -1; + } + + pub fn add_bin_op_to_history(&mut self, n_op_code: i32, is_integer_mode: bool, f_no_repetition: bool) { + let i_command_end = self.add_command(Arc::new(CBinaryCommand::new(n_op_code))); + self.last_bin_op_start_index = self.ich_add_sz_to_equation_sz(" ", -1); + + self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_binary_string(n_op_code, is_integer_mode), i_command_end); + self.ich_add_sz_to_equation_sz(" ", -1); + + if f_no_repetition { + self.set_expression_display(); + } + self.last_op_start_index = -1; + } + + pub fn change_last_bin_op(&mut self, n_op_code: i32, f_prec_inv_to_higher: bool, is_integer_mode: bool) { + self.truncate_equation_sz_from_ich(self.last_bin_op_start_index); + if f_prec_inv_to_higher { + self.enclose_prec_inversion_brackets(); + } + self.add_bin_op_to_history(n_op_code, is_integer_mode, true); + } + + pub fn push_last_opnd_start(&mut self, ich_opnd_start: i32) { + let ich = if ich_opnd_start == -1 { + self.last_op_start_index + } else { + ich_opnd_start + }; + + if self.cur_operand_index < self.operand_indices.len() as i32 { + self.operand_indices[self.cur_operand_index as usize] = ich; + self.cur_operand_index += 1; + } + } + + pub fn pop_last_opnd_start(&mut self) { + if self.cur_operand_index > 0 { + self.cur_operand_index -= 1; + self.last_op_start_index = self.operand_indices[self.cur_operand_index as usize]; + } + } + + pub fn add_open_brace_to_history(&mut self) { + self.add_command(Arc::new(CParentheses::new(IDC_OPENP))); + let ich_opnd_start = self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_string(IDC_OPENP), -1); + self.push_last_opnd_start(ich_opnd_start); + + self.set_expression_display(); + self.last_bin_op_start_index = -1; + } + + pub fn add_close_brace_to_history(&mut self) { + self.add_command(Arc::new(CParentheses::new(IDC_CLOSEP))); + self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_string(IDC_CLOSEP), -1); + self.set_expression_display(); + self.pop_last_opnd_start(); + + self.last_bin_op_start_index = -1; + self.b_last_opnd_brace = true; + } + + pub fn enclose_prec_inversion_brackets(&mut self) { + let ich_start = if self.cur_operand_index > 0 { + self.operand_indices[self.cur_operand_index as usize - 1] + } else { + 0 + }; + + self.insert_sz_in_equation_sz(&CCalcEngine::op_code_to_string(IDC_OPENP), -1, ich_start); + self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_string(IDC_CLOSEP), -1); + } + + pub fn f_opnd_added_to_history(&self) -> bool { + self.last_op_start_index != -1 + } + + pub fn complete_history_line(&mut self, num_str: &str) { + if let Some(p_history_display) = &self.p_history_display { + let added_item_index = p_history_display.add_to_history( + self.sp_tokens.clone().unwrap(), + self.sp_commands.clone().unwrap(), + num_str, + ); + if let Some(p_calc_display) = &self.p_calc_display { + p_calc_display.on_history_item_added(added_item_index); + } + } + + self.sp_tokens = None; + self.sp_commands = None; + self.i_cur_line_hist_start = -1; + self.reinit_history(); + } + + pub fn complete_equation(&mut self, num_str: &str) { + self.ich_add_sz_to_equation_sz(&CCalcEngine::op_code_to_string(IDC_EQU), -1); + self.set_expression_display(); + self.complete_history_line(num_str); + } + + pub fn clear_history_line(&mut self, err_str: &str) { + if err_str.is_empty() { + if let Some(p_calc_display) = &self.p_calc_display { + p_calc_display.set_expression_display( + Arc::new(Mutex::new(VecDeque::new())), + Arc::new(Mutex::new(VecDeque::new())), + ); + } + self.i_cur_line_hist_start = -1; + self.reinit_history(); + } + } + + fn ich_add_sz_to_equation_sz(&mut self, str: &str, i_command_index: i32) -> i32 { + if self.sp_tokens.is_none() { + self.sp_tokens = Some(Arc::new(Mutex::new(VecDeque::new()))); + } + + let mut sp_tokens = self.sp_tokens.as_ref().unwrap().lock().unwrap(); + sp_tokens.push_back((str.to_string(), i_command_index)); + (sp_tokens.len() - 1) as i32 + } + + fn insert_sz_in_equation_sz(&mut self, str: &str, i_command_index: i32, ich: i32) { + let mut sp_tokens = self.sp_tokens.as_ref().unwrap().lock().unwrap(); + sp_tokens.insert(ich as usize, (str.to_string(), i_command_index)); + } + + fn truncate_equation_sz_from_ich(&mut self, ich: i32) { + let mut sp_tokens = self.sp_tokens.as_ref().unwrap().lock().unwrap(); + let mut sp_commands = self.sp_commands.as_ref().unwrap().lock().unwrap(); + + let mut min_idx = -1; + let n_tokens = sp_tokens.len(); + + for i in ich as usize..n_tokens { + let cur_token_id = sp_tokens[i].1; + if cur_token_id != -1 { + if min_idx == -1 || cur_token_id < min_idx { + min_idx = cur_token_id; + sp_commands.truncate(min_idx as usize); + } + } + } + + sp_tokens.truncate(ich as usize); + } + + fn set_expression_display(&self) { + if let Some(p_calc_display) = &self.p_calc_display { + p_calc_display.set_expression_display( + self.sp_tokens.clone().unwrap(), + self.sp_commands.clone().unwrap(), + ); + } + } + + fn add_command(&mut self, sp_command: Arc) -> i32 { + if self.sp_commands.is_none() { + self.sp_commands = Some(Arc::new(Mutex::new(VecDeque::new()))); + } + + let mut sp_commands = self.sp_commands.as_ref().unwrap().lock().unwrap(); + sp_commands.push_back(sp_command); + (sp_commands.len() - 1) as i32 + } + + pub fn update_history_expression(&mut self, radix: u32, precision: i32) { + if self.sp_tokens.is_none() { + return; + } + + let mut sp_tokens = self.sp_tokens.as_ref().unwrap().lock().unwrap(); + let sp_commands = self.sp_commands.as_ref().unwrap().lock().unwrap(); + + for token in sp_tokens.iter_mut() { + let command_position = token.1; + if command_position != -1 { + let exp_command = &sp_commands[command_position as usize]; + if exp_command.get_command_type() == CommandType::OperandCommand { + let opnd_command = exp_command.as_any().downcast_ref::().unwrap(); + token.0 = opnd_command.get_string(radix, precision); + opnd_command.set_commands(self.get_operand_commands_from_string(&token.0)); + } + } + } + + self.set_expression_display(); + } + + pub fn set_decimal_symbol(&mut self, decimal_symbol: char) { + self.decimal_symbol = decimal_symbol; + } + + fn get_operand_commands_from_string(&self, num_str: &str) -> Arc { + let mut commands = Vec::new(); + let f_negative = num_str.starts_with('-'); + + for ch in num_str.chars().skip(if f_negative { 1 } else { 0 }) { + match ch { + ch if ch == self.decimal_symbol => commands.push(IDC_PNT), + 'e' => commands.push(IDC_EXP), + '-' => commands.push(IDC_SIGN), + '+' => {} + ch => { + let num = ch as i32 - '0' as i32; + commands.push(num + IDC_0); + } + } + } + + if f_negative { + commands.push(IDC_SIGN); + } + + Arc::new(COpndCommand::new(commands, f_negative, num_str.contains(self.decimal_symbol), num_str.contains('e'))) + } + + fn reinit_history(&mut self) { + self.last_op_start_index = -1; + self.last_bin_op_start_index = -1; + self.cur_operand_index = 0; + self.b_last_opnd_brace = false; + if let Some(sp_tokens) = &self.sp_tokens { + sp_tokens.lock().unwrap().clear(); + } + if let Some(sp_commands) = &self.sp_commands { + sp_commands.lock().unwrap().clear(); + } + } +} diff --git a/src/CalcManager/Header Files/ICalcDisplay.rs b/src/CalcManager/Header Files/ICalcDisplay.rs new file mode 100644 index 00000000..fd27b113 --- /dev/null +++ b/src/CalcManager/Header Files/ICalcDisplay.rs @@ -0,0 +1,17 @@ +pub trait ICalcDisplay { + fn set_primary_display(&self, text: &str, is_error: bool); + fn set_is_in_error(&self, is_in_error: bool); + fn set_expression_display( + &self, + tokens: std::sync::Arc>, + commands: std::sync::Arc>>, + ); + fn set_parenthesis_number(&self, count: u32); + fn on_no_right_paren_added(&self); + fn max_digits_reached(&self); + fn binary_operator_received(&self); + fn on_history_item_added(&self, added_item_index: u32); + fn set_memorized_numbers(&self, memorized_numbers: Vec); + fn memory_item_changed(&self, index_of_memory: u32); + fn input_changed(&self); +} diff --git a/src/CalcManager/Header Files/IHistoryDisplay.rs b/src/CalcManager/Header Files/IHistoryDisplay.rs new file mode 100644 index 00000000..17e009f2 --- /dev/null +++ b/src/CalcManager/Header Files/IHistoryDisplay.rs @@ -0,0 +1,6 @@ +pub trait IHistoryDisplay { + fn add_item_to_history(&self, item: &str); + fn remove_item_from_history(&self, index: usize); + fn clear_history(&self); + fn get_history_items(&self) -> Vec; +} diff --git a/src/CalcManager/Header Files/Number.rs b/src/CalcManager/Header Files/Number.rs new file mode 100644 index 00000000..9d804c95 --- /dev/null +++ b/src/CalcManager/Header Files/Number.rs @@ -0,0 +1,54 @@ +pub struct Number { + sign: i32, + exp: i32, + mantissa: Vec, +} + +impl Number { + pub fn new() -> Self { + Self { + sign: 1, + exp: 0, + mantissa: vec![0], + } + } + + pub fn with_values(sign: i32, exp: i32, mantissa: Vec) -> Self { + Self { sign, exp, mantissa } + } + + pub fn from_pnumber(p: &PNUMBER) -> Self { + let mut mantissa = Vec::with_capacity(p.cdigit as usize); + mantissa.extend_from_slice(&p.mant[..p.cdigit as usize]); + Self { + sign: p.sign, + exp: p.exp, + mantissa, + } + } + + pub fn to_pnumber(&self) -> PNUMBER { + let mut ret = PNUMBER::new(self.mantissa.len() + 1); + ret.sign = self.sign; + ret.exp = self.exp; + ret.cdigit = self.mantissa.len() as i32; + ret.mant[..self.mantissa.len()].copy_from_slice(&self.mantissa); + ret + } + + pub fn sign(&self) -> i32 { + self.sign + } + + pub fn exp(&self) -> i32 { + self.exp + } + + pub fn mantissa(&self) -> &Vec { + &self.mantissa + } + + pub fn is_zero(&self) -> bool { + self.mantissa.iter().all(|&x| x == 0) + } +} diff --git a/src/CalcManager/Header Files/RadixType.rs b/src/CalcManager/Header Files/RadixType.rs new file mode 100644 index 00000000..2c12aaee --- /dev/null +++ b/src/CalcManager/Header Files/RadixType.rs @@ -0,0 +1,6 @@ +pub enum RadixType { + Decimal, + Hexadecimal, + Octal, + Binary, +} diff --git a/src/CalcManager/Header Files/Rational.rs b/src/CalcManager/Header Files/Rational.rs new file mode 100644 index 00000000..20d8ff73 --- /dev/null +++ b/src/CalcManager/Header Files/Rational.rs @@ -0,0 +1,378 @@ +use std::cmp::Ordering; + +pub struct Rational { + p: Number, + q: Number, +} + +impl Rational { + pub fn new() -> Self { + Self { + p: Number::new(), + q: Number::with_values(1, 0, vec![1]), + } + } + + pub fn from_number(n: &Number) -> Self { + let mut q_exp = 0; + if n.exp() < 0 { + q_exp -= n.exp(); + } + + Self { + p: Number::with_values(n.sign(), 0, n.mantissa().clone()), + q: Number::with_values(1, q_exp, vec![1]), + } + } + + pub fn with_values(p: Number, q: Number) -> Self { + Self { p, q } + } + + pub fn from_i32(i: i32) -> Self { + let prat = i32torat(i); + let p = Number::from_pnumber(&prat.pp); + let q = Number::from_pnumber(&prat.pq); + destroyrat(prat); + Self { p, q } + } + + pub fn from_u32(ui: u32) -> Self { + let prat = ui32torat(ui); + let p = Number::from_pnumber(&prat.pp); + let q = Number::from_pnumber(&prat.pq); + destroyrat(prat); + Self { p, q } + } + + pub fn from_u64(ui: u64) -> Self { + let hi = (ui >> 32) as u32; + let lo = ui as u32; + let temp = Rational::from_u32(hi) << 32 | Rational::from_u32(lo); + Self { + p: temp.p, + q: temp.q, + } + } + + pub fn from_prat(prat: &PRAT) -> Self { + Self { + p: Number::from_pnumber(&prat.pp), + q: Number::from_pnumber(&prat.pq), + } + } + + pub fn to_prat(&self) -> PRAT { + let mut ret = PRAT::new(); + ret.pp = self.p.to_pnumber(); + ret.pq = self.q.to_pnumber(); + ret + } + + pub fn p(&self) -> &Number { + &self.p + } + + pub fn q(&self) -> &Number { + &self.q + } + + pub fn negate(&self) -> Self { + Self { + p: Number::with_values(-self.p.sign(), self.p.exp(), self.p.mantissa().clone()), + q: self.q.clone(), + } + } + + pub fn add_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + addrat(&mut lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn sub_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + subrat(&mut lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn mul_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + mulrat(&mut lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn div_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + divrat(&mut lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn rem_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = rhs.to_prat(); + remrat(&mut lhs_rat, &rhs_rat); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn shl_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + lshrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn shr_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + rshrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn and_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + andrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn or_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + orrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn xor_assign(&mut self, rhs: &Self) { + let mut lhs_rat = self.to_prat(); + let rhs_rat = self.to_prat(); + xorrat(&mut lhs_rat, &rhs_rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rhs_rat); + *self = Rational::from_prat(&lhs_rat); + destroyrat(lhs_rat); + } + + pub fn to_string(&self, radix: u32, fmt: NumberFormat, precision: i32) -> String { + let rat = self.to_prat(); + let result = rat_to_string(&rat, fmt, radix, precision); + destroyrat(rat); + result + } + + pub fn to_u64(&self) -> u64 { + let rat = self.to_prat(); + let result = rat_to_u64(&rat, RATIONAL_BASE, RATIONAL_PRECISION); + destroyrat(rat); + result + } +} + +impl PartialEq for Rational { + fn eq(&self, other: &Self) -> bool { + let lhs_rat = self.to_prat(); + let rhs_rat = other.to_prat(); + let result = rat_equ(&lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(lhs_rat); + destroyrat(rhs_rat); + result + } +} + +impl Eq for Rational {} + +impl PartialOrd for Rational { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Rational { + fn cmp(&self, other: &Self) -> Ordering { + let lhs_rat = self.to_prat(); + let rhs_rat = other.to_prat(); + let result = rat_cmp(&lhs_rat, &rhs_rat, RATIONAL_PRECISION); + destroyrat(lhs_rat); + destroyrat(rhs_rat); + result + } +} + +impl std::ops::AddAssign for Rational { + fn add_assign(&mut self, rhs: Self) { + self.add_assign(&rhs); + } +} + +impl std::ops::SubAssign for Rational { + fn sub_assign(&mut self, rhs: Self) { + self.sub_assign(&rhs); + } +} + +impl std::ops::MulAssign for Rational { + fn mul_assign(&mut self, rhs: Self) { + self.mul_assign(&rhs); + } +} + +impl std::ops::DivAssign for Rational { + fn div_assign(&mut self, rhs: Self) { + self.div_assign(&rhs); + } +} + +impl std::ops::RemAssign for Rational { + fn rem_assign(&mut self, rhs: Self) { + self.rem_assign(&rhs); + } +} + +impl std::ops::ShlAssign for Rational { + fn shl_assign(&mut self, rhs: Self) { + self.shl_assign(&rhs); + } +} + +impl std::ops::ShrAssign for Rational { + fn shr_assign(&mut self, rhs: Self) { + self.shr_assign(&rhs); + } +} + +impl std::ops::BitAndAssign for Rational { + fn bitand_assign(&mut self, rhs: Self) { + self.and_assign(&rhs); + } +} + +impl std::ops::BitOrAssign for Rational { + fn bitor_assign(&mut self, rhs: Self) { + self.or_assign(&rhs); + } +} + +impl std::ops::BitXorAssign for Rational { + fn bitxor_assign(&mut self, rhs: Self) { + self.xor_assign(&rhs); + } +} + +impl std::ops::Neg for Rational { + type Output = Self; + + fn neg(self) -> Self::Output { + self.negate() + } +} + +impl std::ops::Add for Rational { + type Output = Self; + + fn add(mut self, rhs: Self) -> Self::Output { + self.add_assign(rhs); + self + } +} + +impl std::ops::Sub for Rational { + type Output = Self; + + fn sub(mut self, rhs: Self) -> Self::Output { + self.sub_assign(rhs); + self + } +} + +impl std::ops::Mul for Rational { + type Output = Self; + + fn mul(mut self, rhs: Self) -> Self::Output { + self.mul_assign(rhs); + self + } +} + +impl std::ops::Div for Rational { + type Output = Self; + + fn div(mut self, rhs: Self) -> Self::Output { + self.div_assign(rhs); + self + } +} + +impl std::ops::Rem for Rational { + type Output = Self; + + fn rem(mut self, rhs: Self) -> Self::Output { + self.rem_assign(rhs); + self + } +} + +impl std::ops::Shl for Rational { + type Output = Self; + + fn shl(mut self, rhs: Self) -> Self::Output { + self.shl_assign(rhs); + self + } +} + +impl std::ops::Shr for Rational { + type Output = Self; + + fn shr(mut self, rhs: Self) -> Self::Output { + self.shr_assign(rhs); + self + } +} + +impl std::ops::BitAnd for Rational { + type Output = Self; + + fn bitand(mut self, rhs: Self) -> Self::Output { + self.and_assign(rhs); + self + } +} + +impl std::ops::BitOr for Rational { + type Output = Self; + + fn bitor(mut self, rhs: Self) -> Self::Output { + self.or_assign(rhs); + self + } +} + +impl std::ops::BitXor for Rational { + type Output = Self; + + fn bitxor(mut self, rhs: Self) -> Self::Output { + self.xor_assign(rhs); + self + } +} diff --git a/src/CalcManager/Header Files/RationalMath.rs b/src/CalcManager/Header Files/RationalMath.rs new file mode 100644 index 00000000..fee1ee4a --- /dev/null +++ b/src/CalcManager/Header Files/RationalMath.rs @@ -0,0 +1,19 @@ +pub struct RationalMath; + +impl RationalMath { + pub fn add(a: Rational, b: Rational) -> Rational { + // Implementation of addition for Rational numbers + } + + pub fn subtract(a: Rational, b: Rational) -> Rational { + // Implementation of subtraction for Rational numbers + } + + pub fn multiply(a: Rational, b: Rational) -> Rational { + // Implementation of multiplication for Rational numbers + } + + pub fn divide(a: Rational, b: Rational) -> Rational { + // Implementation of division for Rational numbers + } +} diff --git a/src/CalcManager/NumberFormattingUtils.rs b/src/CalcManager/NumberFormattingUtils.rs new file mode 100644 index 00000000..e0d9cd6e --- /dev/null +++ b/src/CalcManager/NumberFormattingUtils.rs @@ -0,0 +1,37 @@ +use std::cmp::Ordering; + +pub struct NumberFormattingUtils; + +impl NumberFormattingUtils { + pub fn format_number(number: &Rational, radix: u32, precision: i32) -> String { + number.to_string(radix, NumberFormat::Normal, precision) + } + + pub fn format_number_scientific(number: &Rational, radix: u32, precision: i32) -> String { + number.to_string(radix, NumberFormat::Scientific, precision) + } + + pub fn format_number_engineering(number: &Rational, radix: u32, precision: i32) -> String { + number.to_string(radix, NumberFormat::Engineering, precision) + } + + pub fn format_number_fixed(number: &Rational, radix: u32, precision: i32) -> String { + number.to_string(radix, NumberFormat::Fixed, precision) + } + + pub fn format_number_currency(number: &Rational, radix: u32, precision: i32) -> String { + number.to_string(radix, NumberFormat::Currency, precision) + } + + pub fn format_number_percent(number: &Rational, radix: u32, precision: i32) -> String { + number.to_string(radix, NumberFormat::Percent, precision) + } + + pub fn format_number_fraction(number: &Rational, radix: u32, precision: i32) -> String { + number.to_string(radix, NumberFormat::Fraction, precision) + } + + pub fn format_number_custom(number: &Rational, radix: u32, precision: i32, format: NumberFormat) -> String { + number.to_string(radix, format, precision) + } +}