package edu.vtc.cis3030
/**
* This class represents rational numbers (fractions). This version illustrates the Ordered
* trait (see Section 19.8 in PiS for more information) and arranges to make this class an
* instance of the Numeric type class. It also illustrates how to program relational equality
* in Scala (see Chapter 30 in PiS for more information).
*
* @param n The numerator.
* @param d The denominator.
*/
class Rational2(n: Int, d: Int = 1) extends Ordered[Rational2] with Equals {
require(d != 0, "invalid Rational2 denominator")
private val commonDivisor = gcd(n.abs, d.abs)
private val signFlag = integerSign(n) * integerSign(d)
val numerator: Int = signFlag * (n.abs / commonDivisor)
val denominator: Int = d.abs / commonDivisor
override def toString: String =
if (denominator == 1) numerator.toString else numerator + "/" + denominator
def +(that: Rational2) =
new Rational2(
numerator * that.denominator + that.numerator * denominator,
denominator * that.denominator)
def -(that: Rational2) =
new Rational2(
numerator * that.denominator - that.numerator * denominator,
denominator * that.denominator)
def *(that: Rational2) =
new Rational2(numerator * that.numerator, denominator * that.denominator)
def /(that: Rational2) =
new Rational2(numerator * that.denominator, denominator * that.numerator)
// See Chapter 30 in PiS for a full discussion of equality in Scala.
def canEqual(other : Any): Boolean = other.isInstanceOf[Rational2]
// The equals method provides value oriented equality for Rational2.
override def equals(other: Any): Boolean = {
other match {
case that: Rational2 =>
(that canEqual this) &&
(this.numerator == that.numerator) &&
(this.denominator == that.denominator)
case _ => false
}
}
// The hashCode method must be overridden to be compatible with equals. This means if two
// objects are equal according to 'equals' then they must have the same hashCode.
override def hashCode(): Int = {
(43 + numerator) * 43 + denominator
}
// The compare method must be compatible with equals.
// If a < b is false and b < a is also false, then it must be the case that a == b.
def compare(that: Rational2): Int = {
// TODO: The calculations here might overflow possibly causing problems.
val leftNumerator = this.numerator * that.denominator
val rightNumerator = that.numerator * this.denominator
leftNumerator - rightNumerator
}
private def integerSign(n: Int): Int =
if (n < 0) -1 else 1
private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
object Rational2 {
// Apply in Rational2's companion object allows things like: val r = Rational2(2, 3)
def apply(n: Int, d: Int = 1) = new Rational2(n, d)
/**
* Implicit value to use as evidence that Rational2 is a Numeric type class instance.
*/
// This syntax creates an anonymous class that extends Numeric and then creates an instance.
implicit val ratNumeric: Numeric[Rational2] = new Numeric[Rational2] {
def compare (x: Rational2, y: Rational2): Int = x compare y
def fromInt (x: Int) = Rational2(x)
def minus (x: Rational2, y: Rational2): Rational2 = x - y
def negate (x: Rational2) = Rational2(-x.numerator, x.denominator)
def plus (x: Rational2, y: Rational2): Rational2 = x + y
def times (x: Rational2, y: Rational2): Rational2 = x * y
def toDouble(x: Rational2): Double = x.numerator.toDouble / x.denominator.toDouble
def toFloat (x: Rational2): Float = x.numerator.toFloat / x.denominator.toFloat
def toInt (x: Rational2): Int = x.numerator / x.denominator
def toLong (x: Rational2): Long = x.numerator.toLong / x.denominator.toLong
override def one = Rational2(1)
override def zero = Rational2(0)
}
}