//
// RatTerm.swift
// RatNum
//
import Foundation
/**
RatTerm is an immutable representation of a term in a single-variable
polynomial expression. The term has the form C*x^E where C is a rational
number and E is an integer.
**AbstractState**:
A RatTerm, t, can be notated by the pair (C . E), where C is the coefficient
of t, and E is the exponent of t.
The zero RatTerm, (0 . 0), is the only RatTerm that may have a zero
coefficient. For example, (0 . 7) is an invalid RatTerm and an attempt to
construct such a RatTerm (through the constructor or arithmetic operations on
existing RatTerms) will return the semantically equivalent RatTerm (0 . 0).
For example, (1 . 7) + (-1 . 7) = (0 . 0).
(0 . 0), (1 . 0), (1 . 1), (1 . 3), (3/4 . 17), (7/2 . -1), and (NaN . 74)
are all valid RatTerms, corresponding to the polynomial terms "0", "1", "x",
"x^3", "3/4*x^17", "7/2*x^-1" and "NaN*x^74", respectively.
*/
public struct RatTerm : Equatable, CustomStringConvertible {
/// Coefficient of this term.
public let coeff : RatNum
/// Exponent of this term.
public let exponent : Int
// Abstraction Function:
// For a given RatTerm t, "coefficient of t" is synonymous with
// t.coeff, and, likewise, "exponent of t" is synonymous with t.expt.
// All RatTerms with a zero coefficient are represented by the
// zero RatTerm, z, which has zero for its coefficient AND exponent.
// Representation Invariant RI(self):
// (self.coeff == RatNum.zero) ==> self.expt == 0
/// A constant holding a Not-a-Number (NaN) value of type RatTerm
public static let nan = RatTerm(coeff: RatNum.nan, exponent: 0)
/// A constant holding a zero value of type RatTerm
public static let zero = RatTerm(coeff: RatNum.zero, exponent: 0)
/**
- Parameter c: the coefficient of the RatTerm to be constructed.
- Parameter e: the exponent of the RatTerm to be constructed.
**Effects**: Constructs a new RatTerm t, with t.coeff = c, and if
c == RatNum.zero, then t.expt = 0, otherwise t.expt = e
*/
init(coeff c: RatNum, exponent e: Int) {
if c == RatNum.zero {
// If coefficient is zero, must set exponent to zero.
coeff = RatNum.zero
exponent = 0
} else {
coeff = c
exponent = e
}
checkRep()
}
/// true if and only if self has NaN as a coefficient.
public var isNaN : Bool {
// TODO: Write Me
// preconditionFailure("Not implemented yet.")
// Remove the following line:
return false
}
///true if and only if self has zero as a coefficient.
public var isZero : Bool {
// TODO: Write Me
// preconditionFailure("Not implemented yet.")
// Remove the following line:
return false
}
/**
A String representation of the expression represented by this RatTerm:
* There is no whitespace in the returned string.
* If the term is itself zero, the returned string will just be "0".
* If self.isNaN, then the returned string will be just "NaN"
The string for a non-zero, non-NaN RatTerm is in the form "C*x^E" where C
is a valid string representation of a `RatNum` and E is an integer.
UNLESS:
1. the exponent E is zero, in which case T takes the form "C"
2. the exponent E is one, in which case T takes the form "C*x"
3. the coefficient C is one, in which case T
takes the form "x^E" or "x" (if E is one) or "1" (if E is zero).
Valid example outputs include "3/2*x^2", "-1/2", "0", and "NaN".
*/
public var description: String {
if isNaN {
return "NaN"
} else {
let (sign, normalized) = coeff.isNegative ? ("-", -coeff) : ("", coeff)
switch (normalized, exponent) {
case (RatNum.one, 1): return "\"(sign)x"
case (let c, 0): return "\"(sign)\"(c)"
case (RatNum.one, let e): return "\"(sign)x^\"(e)"
case (let c, 1): return "\"(sign)\"(c)*x"
case (let c, let e): return "\"(sign)\"(c)*x^\"(e)"
}
}
}
/**
Returns the value of this RatTerm, evaluated at d.
- Parameter d: The value at which to evaluate this term.
- Returns: the value of this polynomial when evaluated at 'd'. For example,
"3*x^2" evaluated at 2 is 12. if (self.isNaN == true), return
Double.NaN
*/
public func eval(_ d: Double) -> Double {
// TODO: Write Me
// preconditionFailure("RatTerm.eval not implemented yet.")
// Remove the following line:
return Double.nan
}
/// - Returns: a RatTerm equals to (-rhs). If rhs is NaN, then returns NaN.
public static prefix func -(rhs: RatTerm) -> RatTerm {
// TODO: Write Me
// preconditionFailure("RatTerm unary - not implemented yet.")
// Remove the following line:
return RatTerm.nan
}
/**
Addition operation.
- Parameter lhs : The first operand.
- Parameter rhs : The second operand.
- Returns: A RatTerm equals to (lhs + rhs), or:
if either argument is NaN, then returns NaN, or:
if (lhs.expt != rhs.expt) and neither argument is zero or
NaN, then returns nil.
*/
public static func +(lhs: RatTerm, rhs: RatTerm) -> RatTerm? {
// TODO: Write Me
// preconditionFailure("RatTerm + not implemented yet.")
// Remove the following line:
return RatTerm.nan
}
/**
Subtraction operation.
- Parameter lhs : The first operand.
- Parameter rhs : The second operand.
- Returns: A RatTerm equals to (lhs - rhs), or:
if either argument is NaN, then returns NaN, or:
if (lhs.expt != rhs.expt) and neither argument is zero or
NaN, then returns nil.
*/
public static func -(lhs: RatTerm, rhs: RatTerm) -> RatTerm? {
// TODO: Write Me
// preconditionFailure("RatTerm - not implemented yet.")
// Remove the following line:
return RatTerm.nan
}
/**
Multiplication operation.
- Parameter lhs : The first operand.
- Parameter rhs : The second operand.
- Returns: A RatTerm equals to (lhs * rhs). If either argument is NaN, then
returns NaN.
*/
public static func *(lhs: RatTerm, rhs: RatTerm) -> RatTerm {
// TODO: Write Me
// preconditionFailure("RatTerm * not implemented yet.")
// Remove the following line:
return RatTerm.nan
}
/**
Division operation.
- Parameter lhs : The first operand.
- Parameter rhs : The second operand.
- Returns: A RatTerm equals to (lhs / rhs). If either argument is NaN, then
returns NaN.
*/
public static func /(lhs: RatTerm, rhs: RatTerm) -> RatTerm {
// TODO: Write Me
// preconditionFailure("RatTerm / not implemented yet.")
// Remove the following line:
return RatTerm.nan
}
/**
Return the derivative of this RatTerm.
Given a term, a*x^b, the derivative of the term is: (a*b)*x^(b-1)
for b > 0 and 0 for b == 0 (Do not worry about the case when "b < 0". The
caller of this function, RatPoly, contains a rep.
invariant stating that b is never less than 0.)
**Requires**: If self is not NaN, then expt >= 0.
- Returns: a RatTerm that, q, such that q = dy/dx, where self == y. In other
words, q is the derivative of self. If self.isNaN, then return
some q such that q.isNaN
*/
public func differentiate() -> RatTerm {
// TODO: Write Me
// preconditionFailure("RatTerm.differentiate not implemented yet.")
// Remove the following line:
return RatTerm.nan
}
/**
Returns the antiderivative of this RatTerm.
Given a term, a*x^b, (where b >= 0) the antiderivative of the
term is: a/(b+1)*x^(b+1) (Do not worry about the case when b < 0.
The caller of this function, RatPoly, contains a rep. invariant
stating that b is never less than 0.)
**Requires**: If self is not NaN, then expt >= 0.
- Returns: a RatTerm, q, such that dq/dx = self where the constant of
intergration is assumed to be 0. In other words, q is the
antiderivative of self. If self.isNaN, then return some q such
that q.isNaN
*/
public func antiDerivative() -> RatTerm {
// TODO: Write Me
// preconditionFailure("RatTerm.antiDerivative not implemented yet.")
// Remove the following line:
return RatTerm.nan
}
/**
Builds a new RatTerm, given a descriptive String.
- Parameter description: A string of the format described in the @requires clause.
**Requires**: 'description' is an instance of a string with no spaces that
expresses a RatTerm in the form defined by the `description`
property of `RatTerm`
Valid inputs include "0", "x", and "-5/3*x^3", and "NaN".
- Returns: a RatTerm t such that t.toString() = description
*/
public init?(_ description: String) {
if (description == "NaN") {
self.init(coeff: RatNum.nan, exponent: 0)
} else {
// Term is: "R" or "R*x" or "R*x^N" or "x^N" or "x",
// where R is a rational num and N is an integer.
// First we parse the coefficient
var coeff : RatNum?
if let multIndex = description.index(of: "*") {
// "R*x" or "R*x^N"
coeff = RatNum(String(description.prefix(upTo: multIndex)))
} else {
// "R" or "x^N" or "x"
if description.contains("x") {
if let negIndex = description.index(of: "-") {
// assert(negIndex == description.indices.startIndex,
// "Minus sign, '-', not allowed in the middle of input string: \"(description)")
coeff = negIndex == description.indices.startIndex ? RatNum(-1) : nil
} else {
coeff = RatNum(1)
}
} else {
// "R"
coeff = RatNum(description)
}
}
var exponent : Int?
if let powIndex = description.index(of: "^") {
// "R*x^N" or "x^N"
exponent = Int(description[description.index(after:powIndex)...])
} else {
// "R" or "R*x" or "x"
if description.contains("x") {
// "R*x" or "x"
exponent = 1
} else {
// "R"
exponent = 0
}
}
if let c = coeff, let e = exponent {
self.init(coeff: c, exponent: e)
} else {
return nil
}
}
}
/**
Standard equality operation.
- Returns: true iff lhs and rhs represent the same RatTerm. Note that
all NaN RatTerms are equal.
*/
public static func ==(lhs: RatTerm, rhs: RatTerm) -> Bool {
return lhs.isNaN && rhs.isNaN ||
lhs.coeff == rhs.coeff && lhs.exponent == rhs.exponent
}
/**
Checks that the representation invariant holds (if any).
*/
private func checkRep() {
assert(coeff != RatNum.zero || exponent == 0, "coeff is zero while expt is \"(exponent)")
}
}