Tom Stuart
February 07, 2019
340

# Representations count

In your grandparents’ attic you discover a mysterious old computer. You boot it up and discover it runs Ruby, but doesn’t support negative numbers! How can you implement negative numbers in an elegant way? We’ll explore two solutions and discover how important it is to pick the right representation.

## Tom Stuart

February 07, 2019

## Transcript

3. ### \$ irb >> 5 * 2 => 10 >> 5

- 2 => 3 >> 2 - 5 ImpossibleNumberError (0 is the smallest number)
4. ### class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

end def +(other) end def -(other) end def *(other) end def <(other) end end
5. ### \$ irb >> require 'signed_number' => true >> def pos(number)

SignedNumber.positive(number) end => :pos >> def neg(number) SignedNumber.negative(number) end => :neg
6. ### >> pos(2) + pos(5) == pos(7) => true >> pos(2)

- pos(5) == neg(3) => true >> (pos(9) - pos(3)) * (neg(4) + neg(3)) == neg(42) => true >> pos(2) * neg(3) < pos(4) + neg(9) => true

8. ### class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

end def +(other) end def -(other) end def *(other) end def <(other) end end

11. ### class SignedNumber def self.positive(number) end def self.negative(number) end # …

end protected attr_reader :sign, :size def initialize(sign, size) @sign, @size = sign, size end new(:negative, number) new(:positive, number)

13. ### sign == other.sign && size == other.size class SignedNumber #

… def ==(other) end # … end
14. ### >> pos(1) == pos(1) => true >> neg(2) == neg(2)

=> true >> pos(1) == pos(2) => false >> pos(1) == neg(1) => false ✔ ✔ ✔ ✔

16. ### SignedNumber.new(sign, size + other.size) end # … end class SignedNumber

# … def +(other)
17. ### >> pos(3) + pos(1) == pos(4) => true >> neg(3)

+ neg(1) == neg(4) => true >> pos(3) + neg(1) == pos(2) => false >> pos(3) + neg(1) => #<SignedNumber @sign=:positive, @size=4> ✔ ✔ ✘
18. ### else SignedNumber.new(sign, size - other.size) class SignedNumber # … def

+(other) if sign == other.sign class SignedNumber # … def +(other) SignedNumber.new(sign, size + other.size) end # … end end
19. ### >> pos(3) + neg(1) == pos(2) => true >> neg(3)

+ pos(1) == neg(2) => true >> pos(1) + neg(3) == neg(2) ImpossibleNumberError (0 is the smallest number) ✔ ✔
20. ### else SignedNumber.new( sign == :positive ? :negative : :positive, other.size

- size ) end end end # … end class SignedNumber # … def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else SignedNumber.new(sign, size - other.size) if size >= other.size
21. ### >> pos(1) + neg(3) == neg(2) => true >> neg(1)

+ pos(3) == pos(2) => true >> pos(3) + neg(3) == neg(3) + pos(3) => false >> pos(3) + neg(3) => #<SignedNumber @sign=:positive, @size=0> >> neg(3) + pos(3) => #<SignedNumber @sign=:negative, @size=0> >> pos(0) == neg(0) => false ✔ ✘ ✘ ✔
22. ### class SignedNumber # … def ==(other) end # … end

( || size.zero?) && size == other.size sign == other.sign

24. ### class SignedNumber # … def ==(other) (sign == other.sign ||

size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
25. ### class SignedNumber # … def ==(other) (sign == other.sign ||

size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other)
26. ### )a end end end def -(other) if sign == other.sign

if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size)
27. ### other.size - size )b end else SignedNumber.new(sign, size + other.size)

end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size
28. ### SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end

end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
29. ### class SignedNumber # … def ==(other) (sign == other.sign ||

size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
30. ### class SignedNumber # … def ==(other) (sign == other.sign ||

size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end

33. ### class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

end def +(other) end def -(other) end def *(other) end def <(other) end end

43. ### -2 :negative, 2 :negative, 1 :negative, 0 :positive, 0 :positive,

1 :positive, 2 -1 0 +1 +2

53. ### protected attr_reader :left, :right def initialize(left, right) @left, @right =

left, right end end class SignedNumber def self.positive(number) end def self.negative(number) end # … new(number, 0) new(0, number)

59. ### +2 = +2 +2 = +3 +3 = +2 left

right right left + +
60. ### left + other.right == other.left + right end # …

end class SignedNumber # … def ==(other)

64. ### +2 < +2 +2 < +3 +3 < +2 left

right right left + +
65. ### other.left + right < left + other.right end # …

end class SignedNumber # … def <(other)

70. ### SignedNumber.new(left + other.left, right + other.right) end # … end

class SignedNumber # … def +(other)

75. ### SignedNumber.new(left + other.right, other.left + right) end # … end

class SignedNumber # … def -(other)

87. ### SignedNumber.new( left * other.right + other.left * right, left *

other.left + right * other.right ) end # … end class SignedNumber # … def *(other)

89. ### class SignedNumber # … def ==(other) left + other.right ==

other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end
90. ### class SignedNumber # … def ==(other) left + other.right ==

other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end
91. ### class SignedNumber # … def ==(other) left + other.right ==

other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end class SignedNumber # … def ==(other) (sign == other.sign || size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end