import scala.testing.SUnit
import java.math.BigInteger
import java.lang.IllegalArgumentException

object Factorial_S_Test extends Application {
  val data = Array (
                    Tuple2 ( 0 , 1 ) ,
                    Tuple2 ( 1 , 1 ) ,
                    Tuple2 ( 2 , 2 ) ,
                    Tuple2 ( 3 , 6 ) ,
                    Tuple2 ( 4 , 24 ) ,
                    Tuple2 ( 5 , 120 ) ,
                    Tuple2 ( 6 , 720 ) ,
                    Tuple2 ( 7 , 5040 ) ,
                    Tuple2 ( 8 , 40320 ) ,
                    Tuple2 ( 9 , 362880 ) ,
                    Tuple2 ( 10 , 3628800 ) ,
                    Tuple2 ( 11 , 39916800 ) ,
                    Tuple2 ( 12 , 479001600 ) ,
                    Tuple2 ( 13 , new BigInt ( new BigInteger ( "6227020800" ) ) ) ,
                    Tuple2 ( 14 , new BigInt ( new BigInteger ( "87178291200" ) ) ) ,
                    Tuple2 ( 20 , new BigInt ( new BigInteger ( "2432902008176640000" ) ) ) ,
                    Tuple2 ( 30 , new BigInt ( new BigInteger ( "265252859812191058636308480000000" ) ) ) ,
                    Tuple2 ( 40 , new BigInt ( new BigInteger ( "815915283247897734345611269596115894272000000000" ) ) )
                    )

  class MyTest ( n : String ) extends SUnit.TestCase ( n ) {

    def correctTest ( function : BigInt => BigInt ) = { data.foreach ( datum => assertEquals ( datum._1.toString ( ) , datum._2 , function ( datum._1 ) ) ) }
    def negativeTest ( function : BigInt => BigInt ) = {
      for ( val i <- Iterator.range ( -20 , -1 ) ) {
        try {
          function ( i )
          fail ( "Negative parameter did not throw exception." )
        }
        catch { case e : IllegalArgumentException => }
      }
    }
     
    override def runTest ( ) = n match {

     case "correctIterative" => { correctTest ( Factorial_S.iterative ) }
     case "correctRecursive" => { correctTest ( Factorial_S.recursive ) }
     case "correctTailRecursive" => { correctTest ( Factorial_S.tailRecursive ) }
     case "correctApplicative" => { correctTest ( Factorial_S.applicative ) }

     case "negativeIterative" => { negativeTest ( Factorial_S.iterative ) }
     case "negativeRecursive" => { negativeTest ( Factorial_S.recursive ) }
     case "negativeTailRecursive" => { negativeTest ( Factorial_S.tailRecursive ) }
     case "negativeApplicative" => { negativeTest ( Factorial_S.applicative ) }

     //  Do not need to test for floating point parameters types, they will not compile.
     case "iterativeEnormousSucceeds" => { Factorial_S.iterative ( 10000 ) }
     case "recursiveEnormousFails" => {
       try {
         Factorial_S.recursive ( 10000 )
         fail ( "Negative parameter did not throw exception." )
       }
       catch { case e : StackOverflowError => }
     }
     case "tailRecursiveEnormousSucceeds" => { Factorial_S.tailRecursive ( 10000 ) }
     case "applicativeEnormousSucceeds" => { Factorial_S.applicative ( 10000 ) }
    }
  }
  val suite = new SUnit.TestSuite ( )
  Array (
        "correctIterative" , "correctRecursive" , "correctTailRecursive" , "correctApplicative" ,
        "negativeIterative" , "negativeRecursive" , "negativeTailRecursive" , "negativeApplicative" ,
        "iterativeEnormousSucceeds" , "recursiveEnormousFails" , "tailRecursiveEnormousSucceeds" , "applicativeEnormousSucceeds"
        ).foreach ( test => suite.addTest ( new MyTest ( test ) ) ) 
  val r = new SUnit.TestResult ( ) 
  suite.run ( r )
  for ( val tf <- r.failures ( ) ) { Console.println ( tf.toString ( ) ) }
}

