class Fractify::Calculator

Public Class Methods

evaluate(formula) click to toggle source
# File lib/fractify/calculator.rb, line 6
def evaluate(formula)
  fraction_string = ''
  current_rank = 0

  incorrect_syntax, inside_of_a_fraction, minus_registered,
  operator_awaiting_an_argument,
  last_char_is_a_closed_bracket = false
  at_least_one_fraction = false
  fraction = nil

  operators = Fractify::OperatorArray.new
  index = 0

  formula.each_char do |char|
    if inside_of_a_fraction
      if char == ')'
        fraction_string = "(#{fraction_string})"
        unless Fractify::Fraction.valid?(fraction_string)
          incorrect_syntax = true
          break
        end

        fraction = Fractify::Fraction.new(string: fraction_string)
        operators.last.to_right = fraction if at_least_one_fraction

        last_char_is_a_closed_bracket = true
        fraction_string = ''
        inside_of_a_fraction = false
        operator_awaiting_an_argument = false
        minus_registered = false
        current_rank -= 3
        at_least_one_fraction = true
      else
        fraction_string += char
      end
    elsif char == '('
      if last_char_is_a_closed_bracket
        incorrect_syntax = true
        break
      end
      current_rank += 3
      minus_registered = false if minus_registered
    elsif char == ')'
      last_char_is_a_closed_bracket = true
      if current_rank.zero?
        incorrect_syntax = true
        break
      end
      current_rank -= 3
    elsif Fractify::Fraction.numeric?(char) || char == '.'
      if current_rank.zero?
        incorrect_syntax = true
        break
      end

      unless inside_of_a_fraction
        inside_of_a_fraction = true
        operator_awaiting_an_argument = false

        if minus_registered
          fraction_string += '-'
          minus_registered = false
          operators.pop
        end
      end
      last_char_is_a_closed_bracket = false
      fraction_string += char
    elsif char == '-'
      minus_registered = true
      operator_awaiting_an_argument = true
      operator = Fractify::Operator.new(char, calculate_rank(current_rank, char), fraction)
      operators.push(operator)
      last_char_is_a_closed_bracket = false
    elsif char_is_an_operator(char)
      if operator_awaiting_an_argument || inside_of_a_fraction
        incorrect_syntax = true
        break
      end

      last_char_is_a_closed_bracket = false
      operator_awaiting_an_argument = true
      operator = Fractify::Operator.new(char, calculate_rank(current_rank, char), fraction)
      operators.push(operator)
    elsif char != ' '
      incorrect_syntax = true
      break
    end
    index += 1
  end

  if operator_awaiting_an_argument || inside_of_a_fraction || incorrect_syntax
    return nil
  end

  operators.sort_descending!
  operators.each do |op|
    case op.operator_character
    when '+'
      fraction = op.to_left + op.to_right
    when '-'
      fraction = op.to_left - op.to_right
    when '*'
      fraction = op.to_left * op.to_right
    when '/'
      fraction = op.to_left / op.to_right
    when ':'
      fraction = op.to_left / op.to_right
    when '÷'
      fraction = op.to_left / op.to_right
    when '^'
      fraction = op.to_left**op.to_right
    end
    left = op.to_left
    operators.each do |o|
      o.to_left = fraction if o.to_left == left
      o.to_right = fraction if o.to_right == left
    end

    op.executed!
    if op != operators.last
      new_op_index = find_operator_with_left(operators, op.to_right)
      operators[new_op_index].to_left = op.to_left unless new_op_index.negative?
    end
  end

  fraction
end
valid?(formula) click to toggle source
# File lib/fractify/calculator.rb, line 134
def valid?(formula)
  fraction_string = ''
  current_rank = 0

  incorrect_syntax, inside_of_a_fraction, minus_registered,
  operator_awaiting_an_argument,
  last_char_is_a_closed_bracket = false
  at_least_one_fraction = false

  index = 0

  formula.each_char do |char|
    if inside_of_a_fraction
      if char == ')'
        fraction_string = "(#{fraction_string})"
        unless Fractify::Fraction.valid?(fraction_string)
          incorrect_syntax = true
          break
        end

        last_char_is_a_closed_bracket = true
        fraction_string = ''
        inside_of_a_fraction = false
        operator_awaiting_an_argument = false
        minus_registered = false
        current_rank -= 3
        at_least_one_fraction = true
      else
        fraction_string += char
      end
    elsif char == '('
      if last_char_is_a_closed_bracket
        incorrect_syntax = true
        break
      end
      current_rank += 3
      minus_registered = false if minus_registered
    elsif char == ')'
      last_char_is_a_closed_bracket = true
      if current_rank.zero?
        incorrect_syntax = true
        break
      end
      current_rank -= 3
    elsif Fractify::Fraction.numeric?(char) || char == '.'
      if current_rank.zero?
        incorrect_syntax = true
        break
      end

      unless inside_of_a_fraction
        inside_of_a_fraction = true
        operator_awaiting_an_argument = false

        if minus_registered
          fraction_string += '-'
          minus_registered = false
        end
      end
      last_char_is_a_closed_bracket = false
      fraction_string += char
    elsif char == '-'
      minus_registered = true
      operator_awaiting_an_argument = true
      last_char_is_a_closed_bracket = false
    elsif char_is_an_operator(char)
      if operator_awaiting_an_argument || inside_of_a_fraction
        incorrect_syntax = true
        break
      end

      last_char_is_a_closed_bracket = false
      operator_awaiting_an_argument = true
    elsif char != ' '
      incorrect_syntax = true
      break
    end
    index += 1
  end

  if operator_awaiting_an_argument || inside_of_a_fraction
    incorrect_syntax = true
  end

  !incorrect_syntax
end

Private Class Methods

calculate_rank(current_rank, operator) click to toggle source
# File lib/fractify/calculator.rb, line 223
def calculate_rank(current_rank, operator)
  case operator
  when '*'
    current_rank + 1
  when '/'
    current_rank + 1
  when ':'
    current_rank + 1
  when '÷'
    current_rank + 1
  when '^'
    current_rank + 2
  else
    current_rank
  end
end
char_is_an_operator(char) click to toggle source
# File lib/fractify/calculator.rb, line 240
def char_is_an_operator(char)
  %w[+ - * / : ÷ ^].include? char
end
find_operator_with_left(operators, fraction) click to toggle source
# File lib/fractify/calculator.rb, line 244
def find_operator_with_left(operators, fraction)
  counter = 0
  operators.each do |op|
    return counter if op.to_left == fraction && op.not_executed?

    counter += 1
  end

  -1
end