Решение на Осма задача от Георги Карапетров

Обратно към всички решения

Към профила на Георги Карапетров

Резултати

  • 2 точки от тестове
  • 0 бонус точки
  • 2 точки общо
  • 12 успешни тест(а)
  • 28 неуспешни тест(а)

Код

class Spreadsheet
class Error < StandardError
def initialize(error_type, object = nil)
error_message = error_text(error_type, object)
super(error_message)
end
private
def error_text(type, object)
errors = {cell_index: "Invalid cell index #{object}",
out_of_bounds: "Cell '#{object}' does not exist",
unknown_function: "Unknown function '#{object}'",
syntax_error: "Invalid expression '#{object}'",
wrong_number_of_arguments: "Wrong number of arguments for '#{object}"}
errors[type]
end
end #of class
module Validation
ALPHABET_SIZE = 26
FUNCTIONS = {"ADD" => :+ , #space to silence skeptic
"MULTIPLY" => :* ,
"SUBTRACT" => :- ,
"DIVIDE" => :/ ,
"MOD" => :% }
def valid_formula?(formula)
return true if (/^\d+$/).match(formula) || valid_index?(formula)
function = function(formula)
raise Error.new(:unknown_function, formula) if FUNCTIONS[function].nil?
syntax_error = (/\(( *\d *, *)+\d\)/).match(formula).nil?
raise Error.new(:syntax_error, formula) if syntax_error
validate_number_of_arguments(formula)
end
def check_index_validity(cell_index)
raise Error.new(:cell_index, cell_index) unless valid_index?(cell_index)
raise Error.new(:out_of_bounds, cell_index) if out_of_bounds(cell_index)
end
def valid_index?(cell_index)
(/^[A-Z]+\d+$/).match(cell_index) != nil
end
private
def function(formula)
(/[A-Z]+/).match(formula)[0]
end
def validate_number_of_arguments(formula)
output = process_number_of_arguments(formula)
return nil if output == :ok
count, should_be = output[:are], output[:should_be]
expected = should_be == :any ? "at least 2" : "2"
message = "#{function(formula)}': expected #{expected}, got #{count.to_s}"
raise Error.new(:wrong_number_of_arguments, message)
end
def out_of_bounds(cell_index)
position = cell_position_from_string(cell_index)
row, column = position[:row], position[:column]
row_error = row >= @sheet.length or row < 0
exceeds_columns = @sheet.length > 0 and column >= @sheet.first.length
column_error = column < 0 or exceeds_columns
row_error or column_error
end
def process_number_of_arguments(formula)
function, count = function(formula), arguments(formula).count
add, multiply = function == "ADD", function == "MULTIPLY"
maximum_count = :any if( add or multiply)
less_than_or_any = maximum_count == :any or count <= 2
return :ok if count > 1 and less_than_or_any
{are: count, should_be: maximum_count}
end
def arguments(formula)
string_array = (/\(.*?\)/).match(formula)[0].tr("()", '').split(/ *, */)
string_array.map { |number| number.to_f }
end
end #of module
module Parsing
def extract_cells_from_row_string(row_string)
cells = row_string.split(/\t|[ ]{2,}/)
p cells
@sheet.push(cells)
end
def pretty(number)
format_string = number % 1 == 0 ? "%.i" : "%.2f"
format_string % number
end
def cell_position_from_string(string_index)
row_and_column = string_index.split(/(\d+)/)
column_string = row_and_column.first
row_string = row_and_column.last
row = row_string.to_i - 1
column = column_from_column_string(column_string)
{:row => row, :column => column}
end
def column_from_column_string(string)
codes = string.chars.map { |letter| letter.ord - 'A'.ord }
calculated, length = [], codes.length
codes.each_with_index do |value, index|
position = position_for_letter(value, index, length)
calculated.push(position)
end
calculated.reduce(:+)
end
def position_for_letter(value, index, length)
return value if length == 1
position = value + (value + 1) * (length - 1 - index) * ALPHABET_SIZE
position -= value if index == 0
position
end
end #of module
include Validation
include Parsing
def initialize(table_string = "")
@sheet = []
if table_string != ""
trimmed_string = table_string.strip
rows = trimmed_string.split("\n")
rows.each { |row| extract_cells_from_row_string(row) }
end
end
def empty?
@sheet.empty?
end
def cell_at(cell_index)
check_index_validity(cell_index)
position = cell_position_from_string(cell_index)
row = position[:row]
column = position[:column]
@sheet[row][column]
end
def to_s
table_array = []
@sheet.each do |row|
cells_string = row.join('\t')
table_array.push(cells_string)
end
table_array.join("\n")
end
def [](cell_index)
cell = cell_at(cell_index)
cell_value = formula?(cell) ? calculate_formula(cell[1 .. -1]) : cell
cell_value
end
#private
def calculate_formula(formula)
return pretty(formula.to_f) if (/\d+\.?\d+$/).match(formula)
if valid_index?(formula)
check_index_validity(formula)
return self[formula]
end
normal_calculate(formula)
end
def normal_calculate(formula)
valid_formula?(formula)
function = function(formula)
operation = FUNCTIONS[function]
pretty(arguments(formula).reduce(operation))
end
def formula?(cell)
cell[0] == '='
end
end #of class

Лог от изпълнението

["foobar"]
...["foo"]
..["foo"]
.["foo", "bar", "baz"]
F["foo", "bar"]
["baz", "larodi"]
F["foo", "bar", "42"]
["baz", "larodi", "100"]
F["foo", "10", "2.1", "=ADD(B1, C1, 2.9)"]
["", "bar", "11", "2.2", "=DIVIDE(B2, C2)"]
["", "baz", "12", "2.3", "=MULTIPLY(C3, B3)"]
F["foo"]
.["foo", "=ADD(2, 2)"]
["", "baz", "larodi"]
..["foo", "bar"]
["", "baz", "larodi"]
F["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
["a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "bc"]
F["foo", "ADD(2, 2)", "=ADD(2, 2)"]
.["=ADD(2, 2)"]
.["=ADD(1, 2, 3, 4, 5)"]
.["=ADD(1)"]
F["42", "=ADD(1, A1, 2, C1)", "10"]
F["2", "3", "5", "=ADD(B1, A1, C1)", "20"]
F["=MULTIPLY(1, 2, 3, 4, 5)"]
["1", "2", "3", "4", "=MULTIPLY(A1, B1, C1, D1, 5)"]
F["=MULTIPLY(1)"]
F["=SUBTRACT(5, 3)", "10"]
F["2", "3", "5", "=SUBTRACT(C1, 1)", "20"]
F["=SUBTRACT(1)"]
F["=DIVIDE(84, 2)", "10"]
F["2", "84", "=DIVIDE(B1, A1)", "20"]
["2", "84", "=DIVIDE(B1, 84)", "20"]
F["=DIVIDE(1)"]
F["=MOD(42, 5)"]
F["10", "84", "=MOD(B1, A1)", "20"]
["5", "83", "=MOD(B1, 2)", "20"]
F["=MOD(1)"]
F["10", "=ADD(A1, 1.1)"]
F["10", "=SUBTRACT(A1, 1.1)"]
F["10", "=MULTIPLY(A1, 1.1)"]
F["10", "=DIVIDE(A1, 4)"]
F["10", "=ADD(5, A1)", "3", "=DIVIDE(B1, C1)", "=MOD(D1, 4)"]
F["=FOO(42)", "100"]
F["=ADD(1, B4)", "100"]
F["=FOO", "100"]
F

Failures:

  1) Spreadsheet#to_s returns multi-cell, oneline tables as a string
     Failure/Error: expect(Spreadsheet.new("foo\tbar\tbaz").to_s).to eq "foo\tbar\tbaz"
       
       expected: "foo\tbar\tbaz"
            got: "foo\\tbar\\tbaz"
       
       (compared using ==)
     # /tmp/d20160121-5693-1lwvn37/spec.rb:32:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) Spreadsheet#to_s returns multi-cell, multiline tables as a string
     Failure/Error: expect(Spreadsheet.new("foo\tbar\nbaz\tlarodi").to_s).to eq "foo\tbar\nbaz\tlarodi"
       
       expected: "foo\tbar\nbaz\tlarodi"
            got: "foo\\tbar\nbaz\\tlarodi"
       
       (compared using ==)
       
       Diff:
       @@ -1,3 +1,3 @@
       -foo	bar
       -baz	larodi
       +foo\tbar
       +baz\tlarodi
     # /tmp/d20160121-5693-1lwvn37/spec.rb:36:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) Spreadsheet#to_s splits cells by two or more spaces
     Failure/Error: expect(Spreadsheet.new("foo  bar   42\nbaz    larodi  100").to_s).to eq "foo\tbar\t42\nbaz\tlarodi\t100"
       
       expected: "foo\tbar\t42\nbaz\tlarodi\t100"
            got: "foo\\tbar\\t42\nbaz\\tlarodi\\t100"
       
       (compared using ==)
       
       Diff:
       @@ -1,3 +1,3 @@
       -foo	bar	42
       -baz	larodi	100
       +foo\tbar\t42
       +baz\tlarodi\t100
     # /tmp/d20160121-5693-1lwvn37/spec.rb:40:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  4) Spreadsheet#to_s returns the evaluated spreadsheet as a table
     Failure/Error: expect(sheet.to_s).to eq \
       
       expected: "foo\t10\t2.1\t15\nbar\t11\t2.2\t5\nbaz\t12\t2.3\t27.60"
            got: "foo\\t10\\t2.1\\t=ADD(B1, C1, 2.9)\n\\tbar\\t11\\t2.2\\t=DIVIDE(B2, C2)\n\\tbaz\\t12\\t2.3\\t=MULTIPLY(C3, B3)"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,4 @@
       -foo	10	2.1	15
       -bar	11	2.2	5
       -baz	12	2.3	27.60
       +foo\t10\t2.1\t=ADD(B1, C1, 2.9)
       +\tbar\t11\t2.2\t=DIVIDE(B2, C2)
       +\tbaz\t12\t2.3\t=MULTIPLY(C3, B3)
     # /tmp/d20160121-5693-1lwvn37/spec.rb:50:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  5) Spreadsheet#[] returns the value of existing cells for simple cell indexes
     Failure/Error: expect(sheet['A2']).to eq 'baz'
       
       expected: "baz"
            got: ""
       
       (compared using ==)
     # /tmp/d20160121-5693-1lwvn37/spec.rb:86:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  6) Spreadsheet#[] returns the value of existing cells for complex cell indexes
     Failure/Error: expect(sheet['AD1']).to eq 'b'
     NameError:
       uninitialized constant Spreadsheet::Parsing::ALPHABET_SIZE
     # /tmp/d20160121-5693-1lwvn37/solution.rb:121:in `position_for_letter'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:113:in `block in column_from_column_string'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:112:in `each'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:112:in `each_with_index'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:112:in `column_from_column_string'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:105:in `cell_position_from_string'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:62:in `out_of_bounds'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:40:in `check_index_validity'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:143:in `cell_at'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:160:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:93:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  7) Spreadsheet#[] raises an exception for less than two arguments passed to ADD
     Failure/Error: expect { Spreadsheet.new('=ADD(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'ADD': expected at least 2, got 1/, got #<Spreadsheet::Error: Invalid expression 'ADD(1)'> with backtrace:
         # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:119:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:119:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:119:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  8) Spreadsheet#[] adds numbers from cell references and as immediate arguments with ADD
     Failure/Error: expect(sheet['B1']).to eq('55')
     Spreadsheet::Error:
       Invalid expression 'ADD(1, A1, 2, C1)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:131:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  9) Spreadsheet#[] adds numbers only from cell references with ADD
     Failure/Error: expect(sheet['D1']).to eq('10')
     Spreadsheet::Error:
       Invalid expression 'ADD(B1, A1, C1)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:137:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  10) Spreadsheet#[] multiplies numbers with MULTIPLY
     Failure/Error: expect(sheet2['E1']).to eq('120')
     Spreadsheet::Error:
       Invalid expression 'MULTIPLY(A1, B1, C1, D1, 5)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:145:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  11) Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY
     Failure/Error: expect { Spreadsheet.new('=MULTIPLY(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'MULTIPLY': expected at least 2, got 1/, got #<Spreadsheet::Error: Invalid expression 'MULTIPLY(1)'> with backtrace:
         # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:149:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:149:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:149:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  12) Spreadsheet#[] subtracts two numbers with SUBTRACT
     Failure/Error: expect(sheet['A1']).to eq('2')
     Spreadsheet::Error:
       Wrong number of arguments for 'SUBTRACT': expected 2, got 2
     # /tmp/d20160121-5693-1lwvn37/solution.rb:58:in `validate_number_of_arguments'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:35:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:161:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  13) Spreadsheet#[] subtracts numbers via cell references
     Failure/Error: expect(sheet['D1']).to eq('4')
     Spreadsheet::Error:
       Invalid expression 'SUBTRACT(C1, 1)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:167:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  14) Spreadsheet#[] raises an exception when SUBTRACT is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=SUBTRACT(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'SUBTRACT': expected 2, got 1/, got #<Spreadsheet::Error: Invalid expression 'SUBTRACT(1)'> with backtrace:
         # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:171:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:171:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:171:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  15) Spreadsheet#[] divides two numbers with DIVIDE
     Failure/Error: expect(sheet['A1']).to eq('42')
     Spreadsheet::Error:
       Invalid expression 'DIVIDE(84, 2)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:183:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  16) Spreadsheet#[] divides numbers via cell references
     Failure/Error: expect(sheet1['C1']).to eq('42')
     Spreadsheet::Error:
       Invalid expression 'DIVIDE(B1, A1)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:190:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  17) Spreadsheet#[] raises an exception when DIVIDE is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=DIVIDE(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'DIVIDE': expected 2, got 1/, got #<Spreadsheet::Error: Invalid expression 'DIVIDE(1)'> with backtrace:
         # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:195:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:195:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:195:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  18) Spreadsheet#[] calculates the modulo of two numbers with MOD
     Failure/Error: expect(Spreadsheet.new('=MOD(42, 5)')['A1']).to eq('2')
     Spreadsheet::Error:
       Invalid expression 'MOD(42, 5)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:205:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  19) Spreadsheet#[] calculates the modulo of two numbers with MOD via cell references
     Failure/Error: expect(sheet1['C1']).to eq('4')
     Spreadsheet::Error:
       Invalid expression 'MOD(B1, A1)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:214:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  20) Spreadsheet#[] raises an exception when MOD is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=MOD(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'MOD': expected 2, got 1/, got #<Spreadsheet::Error: Invalid expression 'MOD(1)'> with backtrace:
         # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:219:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:219:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:219:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  21) Spreadsheet#[] adds floating point numbers with ADD
     Failure/Error: expect(Spreadsheet.new('10  =ADD(A1, 1.1)')['B1']).to eq '11.10'
     Spreadsheet::Error:
       Invalid expression 'ADD(A1, 1.1)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:229:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  22) Spreadsheet#[] subtracts floating point numbers with SUBTRACT
     Failure/Error: expect(Spreadsheet.new('10  =SUBTRACT(A1, 1.1)')['B1']).to eq '8.90'
     Spreadsheet::Error:
       Invalid expression 'SUBTRACT(A1, 1.1)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:234:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  23) Spreadsheet#[] multiplies floating point numbers with MULTIPLY
     Failure/Error: expect(Spreadsheet.new('10  =MULTIPLY(A1, 1.1)')['B1']).to eq '11'
     Spreadsheet::Error:
       Invalid expression 'MULTIPLY(A1, 1.1)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:239:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  24) Spreadsheet#[] divides floating point numbers with DIVIDE
     Failure/Error: expect(Spreadsheet.new('10  =DIVIDE(A1, 4)')['B1']).to eq '2.50'
     Spreadsheet::Error:
       Invalid expression 'DIVIDE(A1, 4)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:244:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  25) Spreadsheet#[] evaluates deeply-nested cell references
     Failure/Error: expect(Spreadsheet.new('10  =ADD(5, A1)  3  =DIVIDE(B1, C1)  =MOD(D1, 4)')['E1']).to eq '1'
     Spreadsheet::Error:
       Invalid expression 'MOD(D1, 4)'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
     # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:250:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  26) Spreadsheet#[] raises an exception for unknown functions
     Failure/Error: expect { Spreadsheet.new('=FOO(42)  100')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Unknown function 'FOO'/, got #<Spreadsheet::Error: Unknown function 'FOO(42)'> with backtrace:
         # /tmp/d20160121-5693-1lwvn37/solution.rb:31:in `valid_formula?'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:254:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:254:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:254:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  27) Spreadsheet#[] raises an exception for missing cells passed as function arguments
     Failure/Error: expect { Spreadsheet.new('=ADD(1, B4)  100')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Cell 'B4' does not exist/, got #<Spreadsheet::Error: Invalid expression 'ADD(1, B4)'> with backtrace:
         # /tmp/d20160121-5693-1lwvn37/solution.rb:34:in `valid_formula?'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:260:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:260:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:260:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  28) Spreadsheet#[] raises an exception for invalid expressions
     Failure/Error: expect { Spreadsheet.new('=FOO  100')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Invalid expression 'FOO'/, got #<Spreadsheet::Error: Unknown function 'FOO'> with backtrace:
         # /tmp/d20160121-5693-1lwvn37/solution.rb:31:in `valid_formula?'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:176:in `normal_calculate'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:172:in `calculate_formula'
         # /tmp/d20160121-5693-1lwvn37/solution.rb:161:in `[]'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:266:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1lwvn37/spec.rb:266:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-1lwvn37/spec.rb:266:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.03585 seconds
40 examples, 28 failures

Failed examples:

rspec /tmp/d20160121-5693-1lwvn37/spec.rb:31 # Spreadsheet#to_s returns multi-cell, oneline tables as a string
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:35 # Spreadsheet#to_s returns multi-cell, multiline tables as a string
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:39 # Spreadsheet#to_s splits cells by two or more spaces
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:43 # Spreadsheet#to_s returns the evaluated spreadsheet as a table
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:78 # Spreadsheet#[] returns the value of existing cells for simple cell indexes
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:90 # Spreadsheet#[] returns the value of existing cells for complex cell indexes
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:118 # Spreadsheet#[] raises an exception for less than two arguments passed to ADD
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:128 # Spreadsheet#[] adds numbers from cell references and as immediate arguments with ADD
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:134 # Spreadsheet#[] adds numbers only from cell references with ADD
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:140 # Spreadsheet#[] multiplies numbers with MULTIPLY
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:148 # Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:158 # Spreadsheet#[] subtracts two numbers with SUBTRACT
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:164 # Spreadsheet#[] subtracts numbers via cell references
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:170 # Spreadsheet#[] raises an exception when SUBTRACT is called with a wrong number of arguments
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:180 # Spreadsheet#[] divides two numbers with DIVIDE
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:186 # Spreadsheet#[] divides numbers via cell references
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:194 # Spreadsheet#[] raises an exception when DIVIDE is called with a wrong number of arguments
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:204 # Spreadsheet#[] calculates the modulo of two numbers with MOD
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:210 # Spreadsheet#[] calculates the modulo of two numbers with MOD via cell references
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:218 # Spreadsheet#[] raises an exception when MOD is called with a wrong number of arguments
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:228 # Spreadsheet#[] adds floating point numbers with ADD
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:233 # Spreadsheet#[] subtracts floating point numbers with SUBTRACT
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:238 # Spreadsheet#[] multiplies floating point numbers with MULTIPLY
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:243 # Spreadsheet#[] divides floating point numbers with DIVIDE
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:249 # Spreadsheet#[] evaluates deeply-nested cell references
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:253 # Spreadsheet#[] raises an exception for unknown functions
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:259 # Spreadsheet#[] raises an exception for missing cells passed as function arguments
rspec /tmp/d20160121-5693-1lwvn37/spec.rb:265 # Spreadsheet#[] raises an exception for invalid expressions

История (1 версия и 0 коментара)

Георги обнови решението на 11.01.2016 16:03 (преди над 8 години)

+class Spreadsheet
+
+ class Error < StandardError
+ def initialize(error_type, object = nil)
+ error_message = error_text(error_type, object)
+ super(error_message)
+ end
+
+ private
+ def error_text(type, object)
+ errors = {cell_index: "Invalid cell index #{object}",
+ out_of_bounds: "Cell '#{object}' does not exist",
+ unknown_function: "Unknown function '#{object}'",
+ syntax_error: "Invalid expression '#{object}'",
+ wrong_number_of_arguments: "Wrong number of arguments for '#{object}"}
+ errors[type]
+ end
+ end #of class
+
+ module Validation
+ ALPHABET_SIZE = 26
+ FUNCTIONS = {"ADD" => :+ , #space to silence skeptic
+ "MULTIPLY" => :* ,
+ "SUBTRACT" => :- ,
+ "DIVIDE" => :/ ,
+ "MOD" => :% }
+
+ def valid_formula?(formula)
+ return true if (/^\d+$/).match(formula) || valid_index?(formula)
+ function = function(formula)
+ raise Error.new(:unknown_function, formula) if FUNCTIONS[function].nil?
+
+ syntax_error = (/\(( *\d *, *)+\d\)/).match(formula).nil?
+ raise Error.new(:syntax_error, formula) if syntax_error
+ validate_number_of_arguments(formula)
+ end
+
+ def check_index_validity(cell_index)
+ raise Error.new(:cell_index, cell_index) unless valid_index?(cell_index)
+ raise Error.new(:out_of_bounds, cell_index) if out_of_bounds(cell_index)
+ end
+
+ def valid_index?(cell_index)
+ (/^[A-Z]+\d+$/).match(cell_index) != nil
+ end
+
+ private
+ def function(formula)
+ (/[A-Z]+/).match(formula)[0]
+ end
+
+ def validate_number_of_arguments(formula)
+ output = process_number_of_arguments(formula)
+ return nil if output == :ok
+ count, should_be = output[:are], output[:should_be]
+ expected = should_be == :any ? "at least 2" : "2"
+ message = "#{function(formula)}': expected #{expected}, got #{count.to_s}"
+ raise Error.new(:wrong_number_of_arguments, message)
+ end
+
+ def out_of_bounds(cell_index)
+ position = cell_position_from_string(cell_index)
+ row, column = position[:row], position[:column]
+
+ row_error = row >= @sheet.length or row < 0
+ exceeds_columns = @sheet.length > 0 and column >= @sheet.first.length
+ column_error = column < 0 or exceeds_columns
+ row_error or column_error
+ end
+
+ def process_number_of_arguments(formula)
+ function, count = function(formula), arguments(formula).count
+
+ add, multiply = function == "ADD", function == "MULTIPLY"
+ maximum_count = :any if( add or multiply)
+ less_than_or_any = maximum_count == :any or count <= 2
+ return :ok if count > 1 and less_than_or_any
+ {are: count, should_be: maximum_count}
+ end
+
+ def arguments(formula)
+ string_array = (/\(.*?\)/).match(formula)[0].tr("()", '').split(/ *, */)
+ string_array.map { |number| number.to_f }
+ end
+
+ end #of module
+
+ module Parsing
+ def extract_cells_from_row_string(row_string)
+ cells = row_string.split(/\t|[ ]{2,}/)
+ p cells
+ @sheet.push(cells)
+ end
+
+ def pretty(number)
+ format_string = number % 1 == 0 ? "%.i" : "%.2f"
+ format_string % number
+ end
+
+ def cell_position_from_string(string_index)
+ row_and_column = string_index.split(/(\d+)/)
+ column_string = row_and_column.first
+ row_string = row_and_column.last
+ row = row_string.to_i - 1
+ column = column_from_column_string(column_string)
+ {:row => row, :column => column}
+ end
+
+ def column_from_column_string(string)
+ codes = string.chars.map { |letter| letter.ord - 'A'.ord }
+ calculated, length = [], codes.length
+ codes.each_with_index do |value, index|
+ position = position_for_letter(value, index, length)
+ calculated.push(position)
+ end
+ calculated.reduce(:+)
+ end
+
+ def position_for_letter(value, index, length)
+ return value if length == 1
+ position = value + (value + 1) * (length - 1 - index) * ALPHABET_SIZE
+ position -= value if index == 0
+ position
+ end
+ end #of module
+
+ include Validation
+ include Parsing
+ def initialize(table_string = "")
+ @sheet = []
+ if table_string != ""
+ trimmed_string = table_string.strip
+ rows = trimmed_string.split("\n")
+ rows.each { |row| extract_cells_from_row_string(row) }
+ end
+ end
+
+ def empty?
+ @sheet.empty?
+ end
+
+ def cell_at(cell_index)
+ check_index_validity(cell_index)
+ position = cell_position_from_string(cell_index)
+ row = position[:row]
+ column = position[:column]
+ @sheet[row][column]
+ end
+
+ def to_s
+ table_array = []
+ @sheet.each do |row|
+ cells_string = row.join('\t')
+ table_array.push(cells_string)
+ end
+ table_array.join("\n")
+ end
+
+ def [](cell_index)
+ cell = cell_at(cell_index)
+ cell_value = formula?(cell) ? calculate_formula(cell[1 .. -1]) : cell
+ cell_value
+ end
+
+ #private
+ def calculate_formula(formula)
+ return pretty(formula.to_f) if (/\d+\.?\d+$/).match(formula)
+ if valid_index?(formula)
+ check_index_validity(formula)
+ return self[formula]
+ end
+ normal_calculate(formula)
+ end
+
+ def normal_calculate(formula)
+ valid_formula?(formula)
+ function = function(formula)
+ operation = FUNCTIONS[function]
+ pretty(arguments(formula).reduce(operation))
+ end
+
+ def formula?(cell)
+ cell[0] == '='
+ end
+
+end #of class