#include <stdexcept>

#include <aeryn/test_runner.hpp>
#include <aeryn/is_equal.hpp>
#include <aeryn/use_name.hpp>
#include <aeryn/throws_exception.hpp>

#include "factorial.hpp"

const unsigned long long data[][2] = {
  { 0 , 1 } ,
  { 1 , 1 } , 
  { 2 , 2 } ,
  { 3 , 6 } ,
  { 4 , 24 } ,
  { 5 , 120 } ,
  { 6 , 720 } ,
  { 7 , 5040 } ,
  { 8 , 40320 } ,
  { 9 , 362880 } ,
  { 10 , 3628800 } ,
  { 11 , 39916800 } ,
  { 12 , 479001600 } ,
  { 13 , 6227020800ull } ,
  { 14 , 87178291200ull } ,
  { 20 , 2432902008176640000ull } ,
  //
  //  There is a largest unsigned long long and there is no point in checking anything bigger.
  //
  //{ 30 , 265252859812191058636308480000000ull } ,
  //{ 40 , 815915283247897734345611269596115894272000000000ull }
} ;

//////  There must be a way of calculating this number rather than having to know it a priori.

const int dataCount = 16 ;


#define correctTest( method ) for ( int i = 0 ; i < dataCount ; ++i ) { IS_EQUAL ( data[i][1] , Factorial::method ( data[i][0] ) ) ; }

void correctIterative ( ) { correctTest ( iterative ) ; }
void correctRecursive ( ) { correctTest ( recursive ) ; }
void correctTailRecursive ( ) { correctTest ( tailRecursive ) ; }

#define negativeTest( method )   for ( int i = -20 ; i < 0 ; ++i ) { THROWS_EXCEPTION ( Factorial::method ( i )  , std::invalid_argument ) ; }

void negativeIterative ( ) { negativeTest ( iterative ) ; }
void negativeRecursive ( ) { negativeTest ( recursive ) ; }
void negativeTailRecursive ( ) { negativeTest ( tailRecursive ) ; }

int main ( ) {
  using namespace Aeryn ;
  TestRunner testRunner ;  
  testRunner.Add ( TestCase ( USE_NAME ( correctIterative ) ) ) ;
  testRunner.Add ( TestCase ( USE_NAME ( correctRecursive ) ) ) ;
  testRunner.Add ( TestCase ( USE_NAME ( correctTailRecursive ) ) ) ;
  //
  //  There are no negative numbers even though there are negative literals: what look like negative numbers
  //  are actually just very large numbers.
  //
  //testRunner.Add ( TestCase ( USE_NAME ( negativeIterative ) ) ) ;
  //testRunner.Add ( TestCase ( USE_NAME ( negativeRecursive ) ) ) ;
  //testRunner.Add ( TestCase ( USE_NAME ( negativeTailRecursive ) ) ) ;
  return testRunner.Run ( ) ;  
}

