DEV Community

Discussion on: Can you…? - Calculator

Collapse
 
katafrakt profile image
PaweÅ‚ ÅšwiÄ…tkowski • Edited

Okay, let's do this without using sum or eval then 🙃

module Calculator
  DivisionByZero = Class.new(RuntimeError)

  module_function

  def add(x,y) = y.abs.times.
    reduce(x) {|acc,_| y > 0 ? acc.next : acc.pred }

  def sub(x,y) = y.abs.times.
    reduce(x) {|acc,_| y > 0 ? acc.pred : acc.next }

  def mult(x,y) = y.abs.times.
    reduce(0) {|acc,_| add(acc,x)}.
    then {|num| y < 0 ? invert(num) : num } 

  def div(x,y) 
    raise DivisionByZero if y.zero?

    divisor = y.abs
    val = x.abs
    times = 0
    while sub(val,divisor) >= 0
      val = sub(val,divisor)
      times = times.next
    end
    times = x >= 0 ? times : invert(times)
    y >= 0 ? times : invert(times)
  end

  def invert(x) = x > 0 ? sub(0,x) : x.abs
end

require 'rspec'

RSpec.describe(Calculator) do
  specify { expect(Calculator.add(5,6)).to eq(11) } 
  specify { expect(Calculator.add(5,0)).to eq(5) } 
  specify { expect(Calculator.add(0,5)).to eq(5) } 
  specify { expect(Calculator.add(-5,5)).to eq(0) } 

  specify { expect(Calculator.sub(5,6)).to eq(-1) }
  specify { expect(Calculator.sub(5,1)).to eq(4) }
  specify { expect(Calculator.sub(0,1)).to eq(-1) }
  specify { expect(Calculator.sub(-5,2)).to eq(-7) }
  specify { expect(Calculator.sub(-5,-2)).to eq(-3) }
  specify { expect(Calculator.sub(5,-2)).to eq(7) }

  specify { expect(Calculator.mult(2,3)).to eq(6) }
  specify { expect(Calculator.mult(2,0)).to eq(0) }
  specify { expect(Calculator.mult(0,10)).to eq(0) }
  specify { expect(Calculator.mult(0,-10)).to eq(0) }
  specify { expect(Calculator.mult(1,-10)).to eq(-10) }
  specify { expect(Calculator.mult(-1,-10)).to eq(10) }

  specify { expect(Calculator.div(6,2)).to eq(3) }
  specify { expect(Calculator.div(6,6)).to eq(1) }
  specify { expect(Calculator.div(7,3)).to eq(2) }
  specify { expect(Calculator.div(-6,3)).to eq(-2) }
  specify { expect(Calculator.div(0,3)).to eq(0) }
  specify { expect(Calculator.div(6,-5)).to eq(-1) }
  specify { expect{ Calculator.div(6,0) }.to raise_error(Calculator::DivisionByZero) }
end
Enter fullscreen mode Exit fullscreen mode

I'm kind of not happy with div, but hard to do it nicely without introducing fractions.

cc @ben

Collapse
 
vulcanwm profile image
Medea

Woah this is really nice!

Collapse
 
ben profile image
Ben Halpern

Nice stuff!