//
//  RatPolyStack.swift
//  RatNum
//

import Foundation

/**
 RatPolyStack is a mutable finite sequence of RatPoly objects.
 
 **Abstract State**:
 Each RatPolyStack can be described by [p1, p2, ... ], where [] is an empty
 stack, [p1] is a one element stack containing the Poly 'p1', and so on.
 RatPolyStacks can also be described constructively, with the append
 operation, ':'. such that [p1]:S is the result of putting p1 at the front of
 the RatPolyStack S.
 
 A finite sequence has an associated size, corresponding to the number of
 elements in the sequence. Thus the size of [] is 0, the size of [p1] is 1,
 the size of [p1, p1] is 2, and so on.
 */
public class RatPolyStack : Sequence {
  
  public typealias Iterator = Array<RatPoly>.Iterator
  
  private var polys = [RatPoly]()
  
  // Abstraction Function:
  // Each element of a RatPolyStack, s, is mapped to the
  // corresponding element of polys.
  // Note: the value in polys[0] is the value at the *bottom* of
  // the stack, and polys[polys.count-1] is the value
  // at the *top* of the stack.

  //
  // RepInvariant: None necessary
  
  /**
   **Effects**: Constructs a new RatPolyStack, [].
   */
  public init() {
    checkRep();
  }
  
  /// The number of RayPolys in this RatPolyStack.
  public var count : Int {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.count not implemented yet.")
    // Remove the following line:
    return 0
  }
  
  /**
   Pushes a RatPoly onto the top of self.
   
   **Modifies**: self
   
   **Effects**: self_post = [p]:self
   
   -Parameter  p: The RatPoly to push onto this stack.
   */
  public func push(_ p : RatPoly) {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.push not implemented yet.")
  }
  
  /**
   Removes and returns the top RatPoly.
   
   **Requires**: self.count > 0
   
   **Modifies**: self
   
   **Effects**: If self = [p]:S then self_post = S
   
   - Returns: p where self = [p]:S
   */
  public func pop() -> RatPoly {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.pop not implemented yet.")
    // Remove the following line:
    return RatPoly.nan
  }
  
  /**
   Duplicates the top RatPoly on self.
   
   **Requires**: self.count > 0
   
   **Modifies**: self
   
   **Effects**: If self = [p]:S then self_post = [p, p]:S
   */
  public func dup() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.dup not implemented yet.")
  }
  
  /**
   Swaps the top two elements of self.
   
   **Requires**: self.count >= 2
   
   **Modifies**: self
   
   **Effects**: If self = [p1, p2]:S then self_post = [p2, p1]:S
   */
  public func swap() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.swap not implemented yet.")
  }
  
  /**
   Clears the stack.
   
   **Modifies**: self
   
   **Effects**: self_post = []
   
   */
  public func clear() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.clear not implemented yet.")
  }
  
  /**
   Returns the RatPoly that is 'index' elements from the top of the stack.
   
   **Requires**: index >= 0 && index < self.count

   -Parameter  index:  The index of the RatPoly to be retrieved.

   -Returns: If self = S:[p]:T where S.count = index, then returns p.
   
   */
  subscript(indexFromTop index : Int) -> RatPoly {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.subscript not implemented yet.")
    // Remove the following line:
    return RatPoly.nan
  }
  
  /**
   Pops two elements off of the stack, adds them, and places the result on
   top of the stack.
   
   **Requires**: self.count >= 2
   
   **Modifies**: self
   
   **Effects**: If self = [p1, p2]:S then self_post = [p3]:S where p3 = p1 + p2
   */
  public func add() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.add not implemented yet.")
  }
  
  /**
   Subtracts the top poly from the next from top poly, pops both off the
   stack, and places the result on top of the stack.
   
   **Requires**: self.count >= 2
   
   **Modifies**: self
   
   **Effects**: If self = [p1, p2]:S then self_post = [p3]:S where p3 = p2 - p1
   
   */
  public func sub() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.sub not implemented yet.")
  }
  
  /**
   Pops two elements off of the stack, multiplies them, and places the
   result on top of the stack.
   
   **Requires**: self.count >= 2
   
   **Modifies**: self
   
   **Effects**: If self = [p1, p2]:S then self_post = [p3]:S where p3 = p1 * p2
   
   */
  public func mul() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.mul not implemented yet.")
  }
  
  /**
   Divides the next from top poly by the top poly, pops both off the stack,
   and places the result on top of the stack.
   
   **Requires**: self.count >= 2
   
   **Modifies**: self
   
   **Effects**: If self = [p1, p2]:S then self_post = [p3]:S where p3 = p2 / p1
   
   */
  public func div() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.div not implemented yet.")
  }
  
  /**
   Pops the top element off of the stack, differentiates it, and places the
   result on top of the stack.
   
   **Requires**: self.count >= 1
   
   **Modifies**: self
   
   **Effects**: If self = [p1]:S then self_post = [p2]:S where p2 = derivative
   of p1
   */
  public func differentiate() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.differentiate not implemented yet.")
  }
  
  /**
   Pops the top element off of the stack, integrates it, and places the
   result on top of the stack.
   
   **Requires**: self.count >= 1
   
   **Modifies**: self
   
   **Effects**: If self = [p1]:S then self_post = [p2]:S where p2 = indefinite
   integral of p1 with integration constant 0
   */
  public func integrate() {
    // TODO: Write Me
    // preconditionFailure("RatPolyStack.integrate not implemented yet.")
  }
  
  /**
   Returns an iterator of the elements contained in the stack.
   
   - Returns: an iterator of the elements contained in the stack in order from
   the bottom of the stack to the top of the stack.
   */
  public func makeIterator() -> Iterator {
    return polys.reversed().makeIterator()
  }
  
  
  /**
   Checks that the representation invariant holds (if any).
   */
  private func checkRep() {
    // In this case, we have nothing.  We'll include this
    // method anyway as good practice -- your code should
    // call it in the standard places for checkRep() functions.
  }
}