use crate::clue_errors::*;
use crate::config::token::*;
use crate::config::token_stream::*;
fn is_numeric(token: &Token) -> bool{
matches!( token,
Token::Float(_) | Token::Int(_)
| Token::VectorF64(_) | Token::VectorI32(_)
)
}
pub fn basic_token_algebraic_operations(tokens: [Token;3],line_number: usize)
->Result<Token,CluEError>{
match tokens[1] {
Token::Plus => tokens[0].clone() + tokens[2].clone(),
Token::Minus => tokens[0].clone() - tokens[2].clone(),
Token::Times => tokens[0].clone() * tokens[2].clone(),
Token::Slash => tokens[0].clone() / tokens[2].clone(),
Token::Hat => tokens[0].clone().pow(tokens[2].clone()),
_ => Err(CluEError::NotAnOperator(line_number, tokens[1].to_string())),
}
}
pub fn contract_operator_inverse_operator_tokens(
tokens: Vec::<Token>,line_number: usize,
op: Token, inv_op: Token)
-> Result<Vec::<Token>, CluEError>{
let mut out = Vec::<Token>::with_capacity(tokens.len());
if tokens.len() < 3{
return Ok(tokens);
}
let start_idx: usize = if tokens[0] == Token::Minus{
let next_token = tokens[1].clone();
if !is_numeric(&next_token){
return Err(CluEError::ExpectedNumber(line_number));
}
let neg_first_token = basic_token_algebraic_operations(
[next_token, Token::Times,Token::Int(-1)], line_number)?;
out.push(neg_first_token);
2
}else{
out.push(tokens[0].clone());
1
};
let mut skip_next = 0;
for ii in start_idx..tokens.len()-1 {
if skip_next > 0{
skip_next -= 1;
continue;
}
if (tokens[ii] == op || tokens[ii] == inv_op)
&& is_numeric(&out[out.len()-1]) {
let mut next_token = tokens[ii+1].clone();
if next_token == Token::Minus{
skip_next += 1;
next_token = tokens[ii+2].clone();
if !is_numeric(&next_token){
return Err(CluEError::ExpectedNumber(line_number));
}
next_token = basic_token_algebraic_operations(
[next_token, Token::Times,Token::Int(-1)],
line_number)?;
}
if !is_numeric(&next_token){
continue;
}
let new_token = basic_token_algebraic_operations(
[out[out.len()-1].clone(), tokens[ii].clone(), next_token],
line_number)?;
let idx = out.len() - 1;
out[idx] = new_token;
skip_next += 1;
}else{
out.push(tokens[ii].clone());
}
}
if skip_next==0{
out.push(tokens[tokens.len()-1].clone());
}
Ok(out)
}
pub fn contract_exponentiation_tokens(tokens: Vec::<Token>,line_number: usize)
-> Result<Vec::<Token>, CluEError>{
contract_operator_inverse_operator_tokens(tokens, line_number,
Token::Hat, Token::Hat)
}
pub fn contract_multiply_divide_tokens(tokens: Vec::<Token>,line_number: usize)
-> Result<Vec::<Token>, CluEError>{
contract_operator_inverse_operator_tokens(tokens, line_number,
Token::Times, Token::Slash)
}
pub fn contract_add_subtract_tokens(tokens: Vec::<Token>,line_number: usize)
-> Result<Vec::<Token>, CluEError>{
contract_operator_inverse_operator_tokens(tokens, line_number,
Token::Plus, Token::Minus)
}
fn contract_emdas(tokens: Vec::<Token>, line_number: usize)
-> Result<Token, CluEError>
{
if tokens.is_empty(){
return Err(CluEError::IndexOutOfBounds(line_number,0, 0));
}
let n_pairs = count_delimiter_pairs(&tokens,
&Token::ParenthesisOpen, &Token::ParenthesisClose,
line_number)?;
if n_pairs != 0 {
return Err(CluEError::InvalidToken(line_number,"parenthaesis".to_string()));
}
let tokens = contract_exponentiation_tokens(tokens,line_number)?;
if count_token(&Token::Hat,&tokens) != 0{
return Err(CluEError::InvalidToken(line_number,Token::Hat.to_string()));
}
let mut tokens = contract_multiply_divide_tokens(tokens,line_number)?;
if count_token(&Token::Times,&tokens) != 0{
return Err(CluEError::InvalidToken(line_number,Token::Times.to_string()));
}
if count_token(&Token::Slash,&tokens) != 0{
return Err(CluEError::InvalidToken(line_number,Token::Slash.to_string()));
}
if tokens[0] == Token::Plus || tokens[0] == Token::Minus{
tokens = add_token_at_index(Token::Float(0.0),0,tokens, line_number)?;
}
let tokens = contract_add_subtract_tokens(tokens,line_number)?;
if tokens.len() != 1 {
return Err(CluEError::CannotCombineTokens(line_number));
}
Ok(tokens[0].clone())
}
fn contract_pemdas(mut tokens: Vec::<Token>, line_number: usize)
-> Result<Token, CluEError>
{
let mut is_fully_contracted = false;
while !is_fully_contracted {
let idx_option = find_deepest_parentheses(&tokens, line_number)?;
let index0: usize;
let index1: usize;
let found_parentheses: bool;
if let Some((idx0,idx1)) = idx_option{
index0 = idx0+1;
index1 = idx1-1;
found_parentheses = true;
}else{ index0 = 0;
index1 = tokens.len() - 1;
found_parentheses = false;
is_fully_contracted = true;
}
let mut new_tokens = Vec::<Token>::with_capacity( tokens.len() );
if found_parentheses {
new_tokens.append(&mut tokens[0..index0-1].to_vec());
}
if index1 >= index0 {
let toks = contract_emdas(tokens[index0..=index1].to_vec(),line_number)?;
new_tokens.push(toks);
}
if found_parentheses && index1+2 < tokens.len()-1{
new_tokens.append(
&mut tokens[index1+2..tokens.len()].to_vec()
);
}
tokens = new_tokens;
}
if tokens.len() != 1 {
return Err(CluEError::CannotCombineTokens(line_number));
}
Ok(tokens[0].clone())
}
fn to_float_vector(tokens: Vec::<Token>, line_number: usize)
-> Result<Token, CluEError>
{
let vector_elements = get_vector_elements(tokens,line_number)?;
let mut out = Vec::<f64>::with_capacity(vector_elements.len());
for token_element in vector_elements{
let token = contract_emdas(token_element,line_number)?;
match token{
Token::Float(x) => out.push(x),
Token::Int(a) => out.push(a as f64),
_ => return Err(CluEError::CannotConvertToFloat(line_number,
token.to_string() ) ),
}
}
Ok(Token::VectorF64(out))
}
fn to_integer_vector(tokens: Vec::<Token>, line_number: usize)
-> Result<Token, CluEError>
{
let vector_elements = get_vector_elements(tokens,line_number)?;
let mut out = Vec::<i32>::with_capacity(vector_elements.len());
for token_element in vector_elements{
let token = contract_emdas(token_element,line_number)?;
match token{
Token::Int(a) => out.push(a),
_ => return Err(CluEError::CannotConvertToFloat(line_number,
token.to_string() ) ),
}
}
Ok(Token::VectorI32(out))
}
fn contract_numeric_vectors(tokens: Vec::<Token>, as_f64: bool,
line_number: usize)
-> Result<Token, CluEError>
{
let indices_open = find_token(&Token::SquareBracketOpen, &tokens);
let indices_close = find_token(&Token::SquareBracketClose, &tokens);
if indices_open.len() != indices_close.len(){
return Err(CluEError::UnmatchedDelimiter(line_number));
}
for (ii, idx_o) in indices_open.iter().enumerate(){
let idx_c = indices_close[ii];
if *idx_o >= idx_c
|| (ii+1<indices_open.len()
&& indices_open[ii+1] <= idx_c){
return Err(CluEError::UnmatchedDelimiter(line_number));
}
}
let mut out = Vec::<Token>::with_capacity(tokens.len());
let mut read_from = 0;
let mut read_to;
for (ii, idx_o) in indices_open.iter().enumerate(){
let idx_c = indices_close[ii];
read_to = *idx_o;
for token in tokens.iter().take(read_to).skip(read_from){
out.push(token.clone());
}
read_from = idx_c + 1;
let array: Token = if as_f64{
to_float_vector(tokens[*idx_o..=idx_c].to_vec(),line_number)?
}else{
to_integer_vector(tokens[*idx_o..=idx_c].to_vec(),line_number)?
};
out.push(array);
}
read_to = tokens.len();
for token in tokens.iter().take(read_to).skip(read_from){
out.push(token.clone());
}
contract_pemdas(out, line_number)
}
pub fn to_f64_token(tokens: Vec::<Token>, line_number: usize)
-> Result<Token, CluEError>
{
let tokens = read_strings_as_floats(tokens, line_number)?;
contract_numeric_vectors(tokens, true, line_number)
}
pub fn to_i32_token(tokens: Vec::<Token>, line_number: usize)
-> Result<Token, CluEError>
{
let tokens = read_strings_as_integers(tokens, line_number)?;
contract_numeric_vectors(tokens, false, line_number)
}
#[cfg(test)]
mod tests{
use super::*;
#[test]
fn test_basic_token_algebraic_operations(){
let mut tokens = vec![Token::Float(2.0), Token::Plus, Token::Float(3.0),
Token::Equals, Token::Float(3.0)];
let answer = basic_token_algebraic_operations(
[tokens[0].clone(),tokens[1].clone(),tokens[2].clone()],0).unwrap();
assert_eq!(answer, Token::Float(2.0 + 3.0));
tokens[1] = Token::Minus;
let answer = basic_token_algebraic_operations(
[tokens[0].clone(),tokens[1].clone(),tokens[2].clone()],0).unwrap();
assert_eq!(answer, Token::Float(2.0 - 3.0));
tokens[1] = Token::Times;
let answer = basic_token_algebraic_operations(
[tokens[0].clone(),tokens[1].clone(),tokens[2].clone()],0).unwrap();
assert_eq!(answer, Token::Float(2.0 * 3.0));
tokens[1] = Token::Slash;
let answer = basic_token_algebraic_operations(
[tokens[0].clone(),tokens[1].clone(),tokens[2].clone()],0).unwrap();
assert_eq!(answer, Token::Float(2.0 / 3.0));
tokens[1] = Token::Hat;
let answer = basic_token_algebraic_operations(
[tokens[0].clone(),tokens[1].clone(),tokens[2].clone()],0).unwrap();
assert_eq!(answer, Token::Float(8.0));
}
#[test]
fn test_contract_multiply_divide_tokens(){
let tokens = vec![Token::Float(2.0), Token::Times, Token::Float(3.0),
Token::Comma, Token::Float(3.0)];
let tokens = contract_multiply_divide_tokens(tokens,0).unwrap();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens,
vec![Token::Float(6.0), Token::Comma, Token::Float(3.0)]);
let tokens = vec![Token::Float(2.0), Token::Slash, Token::Float(3.0),
Token::Comma, Token::Float(3.0)];
let tokens = contract_multiply_divide_tokens(tokens,0).unwrap();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens,
vec![Token::Float(2.0/3.0), Token::Comma, Token::Float(3.0)]);
let tokens = vec![Token::Float(6.0), Token::Times, Token::Float(2.0),
Token::Slash, Token::Float(3.0), Token::Times, Token::Float(5.0)];
let tokens = contract_multiply_divide_tokens(tokens,0).unwrap();
assert_eq!(tokens.len(), 1);
assert_eq!(tokens, vec![Token::Float(6.0*2.0/3.0*5.0)]);
let tokens = vec![Token::Float(2.0), Token::Times, Token::Minus,
Token::Float(3.0)];
let tokens = contract_multiply_divide_tokens(tokens,0).unwrap();
assert_eq!(tokens, vec![Token::Float(-6.0)]);
let tokens = vec![Token::Minus, Token::Float(2.0), Token::Times,
Token::Minus, Token::Float(3.0)];
let tokens = contract_multiply_divide_tokens(tokens,0).unwrap();
assert_eq!(tokens, vec![Token::Float(6.0)]);
}
#[test]
fn test_contract_add_subtract_tokens(){
let tokens = vec![Token::Float(2.0), Token::Plus, Token::Float(3.0),
Token::Comma, Token::Float(3.0)];
let tokens = contract_add_subtract_tokens(tokens,0).unwrap();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens,
vec![Token::Float(5.0), Token::Comma, Token::Float(3.0)]);
let tokens = vec![Token::Float(2.0), Token::Minus, Token::Float(3.0),
Token::Comma, Token::Float(3.0)];
let tokens = contract_add_subtract_tokens(tokens,0).unwrap();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens,
vec![Token::Float(2.0 - 3.0), Token::Comma, Token::Float(3.0)]);
let tokens = vec![Token::Float(6.0), Token::Plus, Token::Float(2.0),
Token::Minus, Token::Float(3.0), Token::Plus, Token::Float(5.0)];
let tokens = contract_add_subtract_tokens(tokens,0).unwrap();
assert_eq!(tokens.len(), 1);
assert_eq!(tokens, vec![Token::Float(6.0 + 2.0 - 3.0 + 5.0)]);
}
#[test]
fn test_contract_exponentiation_tokens(){
let tokens = vec![Token::Float(2.0), Token::Hat, Token::Float(3.0),
Token::Comma, Token::Float(3.0)];
let tokens = contract_exponentiation_tokens(tokens,0).unwrap();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens,
vec![Token::Float(8.0), Token::Comma, Token::Float(3.0)]);
let tokens = vec![Token::Float(-2.0), Token::Hat, Token::Float(3.0),
Token::Comma, Token::Float(3.0)];
let tokens = contract_exponentiation_tokens(tokens,0).unwrap();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens,
vec![Token::Float(-8.0), Token::Comma, Token::Float(3.0)]);
}
#[test]
fn test_contract_emdas(){
let tokens = vec![ Token::Float(1.0) ];
let result = contract_emdas(tokens,0).unwrap();
assert_eq!(result, Token::Float(1.0));
let tokens = vec![
Token::Float(2.0), Token::Hat, Token::Float(5.0),
Token::Plus, Token::Float(3.0), Token::Times, Token::Float(12.0),
Token::Slash, Token::Float(9.0), Token::Minus, Token::Float(-4.0)];
let result = contract_emdas(tokens,0).unwrap();
assert_eq!(result, Token::Float(40.0));
let tokens = vec![Token::Minus,
Token::Float(2.0), Token::Hat, Token::Float(5.0),
Token::Plus, Token::Float(3.0), Token::Times, Token::Float(12.0),
Token::Slash, Token::Float(9.0), Token::Minus, Token::Float(-4.0)];
let result = contract_emdas(tokens,0).unwrap();
assert_eq!(result, Token::Float(-24.0));
}
#[test]
fn test_contract_pemdas(){
let tokens = vec![ Token::ParenthesisOpen,
Token::Float(2.0), Token::Hat, Token::Float(5.0),
Token::Plus, Token::Float(3.0), Token::Times, Token::Float(12.0),
Token::Slash, Token::Float(9.0), Token::Minus, Token::Float(-4.0),
Token::ParenthesisClose];
let result = contract_pemdas(tokens,0).unwrap();
assert_eq!(result, Token::Float(40.0));
let tokens = vec![
Token::ParenthesisOpen,
Token::Minus,Token::Float(2.0),
Token::ParenthesisClose, Token::Hat, Token::Float(2.0),
Token::Plus, Token::Float(3.0),
Token::Times, Token::Float(12.0),
Token::Slash, Token::Float(9.0), Token::Minus, Token::Float(-4.0)];
let result = contract_pemdas(tokens,0).unwrap();
assert_eq!(result, Token::Float(12.0));
let tokens = vec![
Token::ParenthesisOpen,
Token::ParenthesisOpen,
Token::Minus,Token::Float(2.0),
Token::ParenthesisClose, Token::Hat, Token::Float(2.0),
Token::Plus, Token::Float(3.0),
Token::ParenthesisClose,
Token::Times, Token::Float(12.0),
Token::Slash, Token::Float(7.0), Token::Minus,
Token::ParenthesisOpen,
Token::Float(-2.0), Token::Plus, Token::Float(-2.0),
Token::ParenthesisClose,
];
let result = contract_pemdas(tokens,0).unwrap();
assert_eq!(result, Token::Float(16.0));
}
#[test]
fn test_to_float_vector(){
let tokens = vec![
Token::SquareBracketOpen,
Token::Float(1.0), Token::Comma, Token::Float(-1.0) ,
Token::SquareBracketClose];
let vec64 = to_float_vector(tokens,0).unwrap();
assert_eq!(vec64, Token::VectorF64(vec![1.0,-1.0]));
let inv_sqrt2 = f64::sqrt(1.0/2.0);
let tokens = vec![
Token::SquareBracketOpen,
Token::Float(1.0*inv_sqrt2), Token::Comma, Token::Float(-1.0*inv_sqrt2),
Token::SquareBracketClose];
let vec64 = to_float_vector(tokens,0).unwrap();
assert_eq!(vec64, Token::VectorF64(vec![inv_sqrt2,-inv_sqrt2]));
}
#[test]
fn test_contract_numeric_vector(){
let tokens = vec![
Token::SquareBracketOpen,
Token::Float(1.0), Token::Comma, Token::Float(-1.0) ,
Token::SquareBracketClose];
let vec64 = contract_numeric_vectors(tokens,true,0).unwrap();
assert_eq!(vec64, Token::VectorF64(vec![1.0,-1.0]));
let inv_sqrt2 = f64::sqrt(1.0/2.0);
let tokens = vec![
Token::Float(inv_sqrt2), Token::Times,
Token::SquareBracketOpen,
Token::Float(1.0), Token::Comma, Token::Float(-1.0) ,
Token::SquareBracketClose];
let vec64 = contract_numeric_vectors(tokens,true, 0).unwrap();
assert_eq!(vec64, Token::VectorF64(vec![inv_sqrt2,-inv_sqrt2]));
let sqrt2 = f64::sqrt(2.0);
let tokens = vec![
Token::SquareBracketOpen,
Token::Float(1.0), Token::Comma, Token::Float(-1.0) ,
Token::SquareBracketClose,
Token::Slash, Token::Float(sqrt2),
];
let vec64 = contract_numeric_vectors(tokens,true,0).unwrap();
assert_eq!(vec64, Token::VectorF64(vec![1.0/sqrt2, -1.0/sqrt2]));
let tokens = vec![
Token::Float(1.0), Token::Plus,
Token::SquareBracketOpen,
Token::Float(1.0), Token::Comma, Token::Float(-1.0) ,
Token::SquareBracketClose];
let vec64 = contract_numeric_vectors(tokens,true, 0).unwrap();
assert_eq!(vec64, Token::VectorF64(vec![2.0, 0.0]));
let tokens = vec![
Token::SquareBracketOpen,
Token::Float(-1.0), Token::Comma, Token::Float(1.0) ,
Token::SquareBracketClose,
Token::Plus,
Token::SquareBracketOpen,
Token::Float(1.0), Token::Comma, Token::Float(-1.0) ,
Token::SquareBracketClose];
let vec64 = contract_numeric_vectors(tokens,true, 0).unwrap();
assert_eq!(vec64, Token::VectorF64(vec![0.0, 0.0]));
let tokens = vec![Token::SquareBracketOpen,
Token::Int(1), Token::Comma, Token::Int(-1),
Token::SquareBracketClose];
let veci32 = contract_numeric_vectors(tokens,false, 0).unwrap();
assert_eq!(veci32, Token::VectorI32(vec![1, -1]));
}
#[test]
fn test_to_f64_token(){
let tokens = vec![Token::SquareBracketOpen,
Token::UserInputValue("1".to_string()),Token::Comma,
Token::UserInputValue("-1".to_string()),
Token::SquareBracketClose];
let result = to_f64_token(tokens,0).unwrap();
assert_eq!(result, Token::VectorF64(vec![1.0,-1.0]));
let tokens = vec![Token::UserInputValue("12".to_string()), Token::Times,
Token::UserInputValue("10".to_string()), Token::Hat,
Token::UserInputValue("1".to_string()) ];
let result = to_f64_token(tokens,0).unwrap();
assert_eq!(result, Token::Float(120.0) );
let tokens = vec![Token::UserInputValue("12".to_string()), Token::Times,
Token::UserInputValue("10".to_string()), Token::Hat, Token::Minus,
Token::UserInputValue("1".to_string()) ];
let Token::Float(result) = to_f64_token(tokens,0).unwrap() else{
panic!("Expected a float.");
};
assert!( (result-1.2).abs()<1e-12 );
let tokens = vec![Token::UserInputValue("80e".to_string()), Token::Minus,
Token::UserInputValue("10".to_string()) ];
let Token::Float(result) = to_f64_token(tokens,0).unwrap() else{
panic!("Expected a float.");
};
assert!( (result-80e-10).abs()<1e-12 );
let tokens = vec![Token::UserInputValue("80e".to_string()), Token::Minus,
Token::UserInputValue("02".to_string()) ];
let Token::Float(result) = to_f64_token(tokens,0).unwrap() else{
panic!("Expected a float.");
};
assert!( (result-80e-2).abs()<1e-12 );
}
#[test]
fn test_to_i32_token(){
let tokens = vec![Token::SquareBracketOpen,
Token::UserInputValue("1".to_string()),Token::Comma,
Token::UserInputValue("-1".to_string()),
Token::SquareBracketClose];
let result = to_i32_token(tokens,0).unwrap();
assert_eq!(result, Token::VectorI32(vec![1,-1]));
}
}