/** This trait defines the AbstractSet type. Classes that inherit
 *  from AbstractSet must implement at least the following methods:
 *  contains, add and remove
 *
 *  This trait defines two methods: addAll and removeAll
 */
trait AbstractSet  {
  def contains(n: Int): Boolean
  def add(n: Int): AbstractSet
  def remove(n: Int): AbstractSet
  
  def addAll(lst: Iterable[Int]): AbstractSet =
    lst.foldLeft(this)((s, elem) => s add elem)
  def removeAll(lst: Iterable[Int]): AbstractSet =
    lst.foldLeft(this)((s, elem) => s remove elem)
}

/** This is the MutableSet class.
 *
 *  The add and remove methods should modify the instance of the
 *  set on which they are called.
 */
class MutableSet extends AbstractSet {
  def contains(n: Int): Boolean = // Your Code Here
  
  def add(n: Int): MutableSet = {
    // Your Code Here

    this
  }
  
  def remove(n: Int): MutableSet = {
    // Your Code Here

    this
  }
}

/** This is the ImmutableSet class.
 *
 *  The add and remove methods should NOT modify the instance of
 *  the set on which they are called.
 */
class ImmutableSet extends AbstractSet {
  // Your Code Here
}

trait LockingSet extends AbstractSet {
  abstract override def add(n: Int): AbstractSet =
    synchronized { super.add(n) }
  abstract override def remove(n: Int): AbstractSet =
    synchronized { super.remove(n) }
  abstract override def contains(n: Int): Boolean =
    synchronized { super.contains(n) }
}

trait LoggingSet extends AbstractSet {
  // Your Code Here
}

/***************************************************************
 *************** BEGIN UNIT TESTING LIBRARY CODE ***************
 ***************************************************************
 *  The following code defines a simple unit testing library for
 *  MutableSet and ImmutableSet. It defines a simple "domain-
 *  specific language" for testing sets of integers.
 *
 *  You are not expected to understand this code.
 */

/** Takes an arbitrary number of tests and prints the result of
 *  the tests. A big "FAILED" message should be printed if any
 *  test fails.
 */
def asserting(tests: (String, Boolean)*) {
  tests.foreach { test =>
    val msg = test._1
    val pass_? = test._2
    
    if (pass_?)
      println("Passed: " + msg)
    else
      println("FAILED: " + msg)
  }
}

/** The Numbers class represents a range of numbers.
 *
 *  It defines some convenient utility methods to test
 *  sets and see whether they have these numbers or not.
 */
class Numbers(nums: Iterable[Int]) extends Iterable[Int] {
  def containsNoneFrom_:(s: AbstractSet): Boolean = {
    ((nums map (s contains _)) exists (_ == true)) == false
  }
  
  def containsAllFrom_:(s: AbstractSet): Boolean = {
    ((nums map (s contains _)) exists (_ == false)) == false
  }
  
  def elements = nums.elements
}

/** These values define ranges of odd numbers, even numbers,
 *  and all numbers.
 */
val allNumbers = new Numbers(1 to 10)
val evenNumbers = new Numbers((1 to 10) filter (_ % 2 == 0))
val oddNumbers = new Numbers((1 to 10) filter (_ % 2 != 0))

/***************************************************************
 **************** END UNIT TESTING LIBRARY CODE ****************
 ***************************************************************/
 

/** What follows are the unit tests for MutableSet and ImmutableSet.
 *  This code uses the "domain-specific language" defined in the
 *  unit testing library above.
 */

val m1 = new MutableSet
val m2 = new MutableSet

asserting (
 "m1 contains no numbers" -> (m1 containsNoneFrom_: allNumbers),
 "m2 contains no numbers" -> (m2 containsNoneFrom_: allNumbers)
)

val m3 = m1 addAll allNumbers

asserting (
 "m1 contains all numbers" -> (m1 containsAllFrom_: allNumbers),
 "m2 still contains no numbers" -> (m2 containsNoneFrom_: allNumbers),
 "m3 contains all numbers" -> (m3 containsAllFrom_: allNumbers)
)

m3 addAll evenNumbers
m3 removeAll evenNumbers

asserting (
 "m1 contains no even numbers" -> (m1 containsNoneFrom_: evenNumbers),
 "m1 contains all odd numbers" -> (m1 containsAllFrom_: oddNumbers),
 "m3 contains no even numbers" -> (m3 containsNoneFrom_: evenNumbers),
 "m3 contains all odd numbers" -> (m3 containsAllFrom_: oddNumbers)
)

m2 addAll evenNumbers

asserting (
 "m2 contains all even numbers" -> (m2 containsAllFrom_: evenNumbers),
 "m2 contains no odd numbers" -> (m2 containsNoneFrom_: oddNumbers)
)

val i1 = new ImmutableSet
val i2 = new ImmutableSet

asserting (
 "i1 contains no numbers" -> (i1 containsNoneFrom_: allNumbers),
 "i2 contains no numbers" -> (i2 containsNoneFrom_: allNumbers)
)

val i3 = i1 addAll allNumbers

asserting (
 "i1 still contains no numbers" -> (i1 containsNoneFrom_: allNumbers),
 "i3 contains all numbers" -> (i3 containsAllFrom_: allNumbers)
)

val _i3 = i3 addAll evenNumbers
val i4 = i3 removeAll evenNumbers
val _i4 = _i3 removeAll evenNumbers

asserting (
 "i3 still contains all numbers" -> (i3 containsAllFrom_: allNumbers),
 "i4 contains no even numbers" -> (i4 containsNoneFrom_: evenNumbers),
 "i4 contains all odd numbers" -> (i4 containsAllFrom_: oddNumbers),
 "_i4 contains no even numbers" -> (_i4 containsNoneFrom_: evenNumbers),
 "_i4 contains all odd numbers" -> (_i4 containsAllFrom_: oddNumbers)
)

val i5 = i2 addAll evenNumbers

asserting (
 "i2 still contains no numbers" -> (i2 containsNoneFrom_: allNumbers),
 "i5 contains all even numbers" -> (i5 containsAllFrom_: evenNumbers),
 "i5 contains no odd numbers" -> (i5 containsNoneFrom_: oddNumbers)
)

()
