#!/usr/bin/ruby -w ### Rational Number Class ### class RatNum ### Split string and assign NUMER, DENOM, and WHOLE def initialize( frac ) w, n, d = frac.match(/\//) ? frac.match(%r#(?:(\S+) )??(\S+)/(\S+)#).captures : [1, frac, nil] w, n, d = w.to_i, n.to_i, d.to_i # conversion to integer ## Checks for fraction entries that can be simply "1" n = n + ( w * d ) if d # convert to improper fraction ## Adjust fraction: "34" -> "34/1" and raise w flag n, d, w = n, ( d == 0 ) ? 1 : d, ( d > 1 ) ? 0 : 1 n = d = w = 1 if n == d # when "2/2" equals "1" ## Checks for "-" in denominator and adjusts accordingly n = -n and d = -d if ( n > 0 && d < 0 ) @n, @d, @w = n, d, w end ### Readers attr_reader :w attr_reader :n attr_reader :d ### Stringify object to fraction def to_string return "0" if ( n == 0 && d == 0 ) return n.to_s if ( w == 1 ) if ( n.abs > d ) then a = ( n / d ) c = n - ( d * a ) c = -c if ( ( a < 0 ) && ( c < 0 ) ) return "#{a} #{c}/#{d}" end return "#{n}/#{d}" end ### Overloaded addition operator def +( other ) n1, n2 = n, other.n d1, d2 = d, other.d com_denom = ( d1 == d2 ) ? d1 : d1 * d2 n_sum = ( d1 == d2 ) \ ? n1 + n2 \ : begin n1 = n1 * ( com_denom / d1 ) n2 = n2 * ( com_denom / d2 ) n1 + n2 end gcd = _simplify( n_sum, com_denom ) n_sum, com_denom = n_sum.div(gcd), com_denom.div(gcd) sum_str = ( com_denom != 0 ) \ ? ( n_sum == com_denom ) \ ? "1" \ : "#{n_sum}/#{com_denom}" \ : "0" return sum_str end ### Overloaded subtraction operator def -( other ) n1, n2 = n, other.n d1, d2 = d, other.d com_denom = ( d1 == d2 ) ? d1 : d1 * d2 n_dif = ( d1 == d2 ) \ ? n1 - n2 \ : begin n1 = n1 * ( com_denom / d1 ) n2 = n2 * ( com_denom / d2 ) n1 - n2 end gcd = _simplify( n_dif, com_denom ) n_dif, com_denom = n_dif.div(gcd), com_denom.div(gcd) dif_str = ( com_denom != 0 ) \ ? ( n_dif == com_denom ) \ ? "1" \ : "#{n_dif}/#{com_denom}" \ : "0" return dif_str end ### Overloaded multiplication operator def *( other ) n_prod = ( n * other.n ) d_prod = ( d * other.d ) return "0" if ( n_prod == 0 || d_prod == 0 ) gcd = _simplify( n_prod, d_prod ) n_prod, d_prod = n_prod.div(gcd), d_prod.div(gcd) prod_str = ( n_prod == d_prod ) ? "1" : "#{n_prod}/#{d_prod}" return prod_str end ### Overloaded division operator def /( other ) n_quot = ( n * other.d ) d_quot = ( d * other.n ) return "undef" if ( n_quot == 0 || d_quot == 0 ) n_quot, d_quot = -n_quot, -d_quot \ if ( n_quot < 0 && d_quot < 0 ) gcd = _simplify( n_quot, d_quot ) n_quot, d_quot = n_quot.div(gcd), d_quot.div(gcd) quot_str = ( n_quot == d_quot ) ? "1" : "#{n_quot}/#{d_quot}" return quot_str end ### Checks for object equality def ==( other ) return ( ( n == other.n ) && ( d == other.d ) ) end ### Simplify fraction using Euclid's algorithm def _simplify(x, y) until ( y == 0 ) ( x, y ) = y, x.modulo(y) end return x end end ### Main Program ### command = "" puts "\nThis program will perform arithmetic operations on fractions." puts "Enter fractions like this: 3/4, 15/16, etc..., or \"exit\" to exit." while ( command != "Q" ) print "\nEnter first fraction: " frac01 = gets.chomp if ( frac01 =~ /^\S+\/(\S+)$/ ) puts "Division by zero not allowed, starting over..." and redo if ( $1 == 0 ) end print "Enter second fraction: " frac02 = gets.chomp if ( frac02 =~ /^\S+\/(\S+)$/ ) puts "Division by zero not allowed, starting over..." and redo if ( $1 == 0 ) end exit if ( frac01 == "exit" || frac02 == "exit" ) f1 = RatNum.new(frac01) f2 = RatNum.new(frac02) while ( command != "Q" ) puts "\nChoose: (A)dd, (S)ubtract, (M)ultiply, (D)ivide" puts "\tTest for (E)quality, or (Q)uit" print "Answer: " command = gets.chomp if ( command == "A" ) total = RatNum.new( f1 + f2 ) puts f1.to_string + " + " \ + f2.to_string + " = " \ + total.to_string elsif ( command == "S" ) total = RatNum.new( f1 - f2 ) puts f1.to_string + " - " \ + f2.to_string + " = " \ + total.to_string elsif ( command == "M" ) total = RatNum.new( f1 * f2 ) puts f1.to_string + " * " \ + f2.to_string + " = " \ + total.to_string elsif ( command == "D" ) total = RatNum.new( f1 / f2 ) puts f1.to_string + " / " \ + f2.to_string + " = " \ + total.to_string elsif ( command == "E" ) is_equal = ( f1 == f2 ) ? %q{ } : " not " puts f1.to_string + " and " \ + f2.to_string + " are" \ + is_equal + "equal." \ elsif ( command == "Q" ) break else puts "Command not recognized..." end end print "Type (Q) again to exit or another character to start over: " command = gets.chomp if ( command == "Q" ) puts "Goodbye.." and exit end end