// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

////////////////////////////////////////////////////////////////////////////////
//
// Notes:
//
// This test will Stack overflow when built in debug. It relies on the FSharp
// compiler optimizing the tail calls to loops.
//
////////////////////////////////////////////////////////////////////////////////

open System

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// 16 byte struct
[<Struct>]
type Point2D(x: double, y: double) =
    member _.X = x
    member _.Y = y

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// F# compiler will optimize the call away
let first() =
    let callee firstArg secondArg thirdArg =
        if firstArg = 10 then firstArg * secondArg * thirdArg
        else firstArg + secondArg + thirdArg

    let retVal = callee 10 20 30

    if retVal <> 6000 then
        printfn "Method -- Failed, expected result: 6000, calculated: %d" retVal

        -1
    else
        0

// F# Compiler will optimize the call away and treat this as a loop
let second() =
    let rec secondCallee(iteration, firstArg, secondArg, thirdArg) =
        if iteration = 0 then
            firstArg
        else
            let mutable retVal =
                if firstArg = 10 then
                    firstArg * secondArg * thirdArg
                else
                    firstArg + secondArg + thirdArg

            secondCallee(iteration - 1, retVal, secondArg, thirdArg)

    let retVal = secondCallee(100, 10, 20, 30)

    if retVal <> 10950 then
        printfn "Method -- Failed, expected result: 10950, calculated: %d" retVal
        -2
    else
        0

// F# Compiler will optimize the call away and treat this as a loop
let third() =
    let rec thirdCallee(iteration, firstArg, secondArg, thirdArg, point: Point2D, secondPoint: Point2D, thirdPoint: Point2D) =
        if point.X <> 10.0 then -100
        else if point.Y <> 20.0 then -101
        else if secondPoint.X <> 30.0 then -102
        else if secondPoint.Y <> 40.0 then -102
        else if thirdPoint.X <> 30.0 then -103
        else if thirdPoint.Y <> 40.0 then -103
        else if iteration = 0 then
            firstArg
        else
            let mutable retVal =
                if firstArg = 10 then
                    firstArg * secondArg * thirdArg
                else
                    firstArg + secondArg + thirdArg

            if retVal > 5000 then
                thirdCallee(iteration - 1, retVal, secondArg, thirdArg, point, secondPoint, thirdPoint)
            else
                thirdCallee(iteration - 1, secondArg, thirdArg, thirdArg, point, secondPoint, thirdPoint)

    let point = Point2D(10.0, 20.0)
    let secondPoint = Point2D(30.0, 40.0)

    let retVal = thirdCallee(100, 10, 20, 30, point, secondPoint, secondPoint)

    if retVal <> 10950 then
        printfn "Method -- Failed, expected result: 10950, calculated: %d" retVal
        -3
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a fast tail call on unix x64 as there is no stack usage
let fourth() =
    let rec fourthMethodFirstCallee(iterationCount, firstArg: Point2D, secondArg: Point2D, thirdArg: Point2D, fourthArg: Point2D) =
        if firstArg.X <> 10.0 then -100
        else if firstArg.Y <> 20.0 then -101
        else if secondArg.X <> 30.0 then -102
        else if secondArg.Y <> 40.0 then -103
        else if thirdArg.X <> 10.0 then -104
        else if thirdArg.Y <> 20.0 then -105
        else if fourthArg.X <> 30.0 then -106
        else if fourthArg.Y <> 40.0 then -107
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            fourthMethodSecondCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg)
        else
            fourthMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg)

    and fourthMethodSecondCallee(iterationCount, firstArg, secondArg, thirdArg, fourthArg) =
        if firstArg.X <> 10.0 then -150
        else if firstArg.Y <> 20.0 then -151
        else if secondArg.X <> 30.0 then -152
        else if secondArg.Y <> 40.0 then -153
        else if thirdArg.X <> 10.0 then -154
        else if thirdArg.Y <> 20.0 then -155
        else if fourthArg.X <> 30.0 then -156
        else if fourthArg.Y <> 40.0 then -157
        else if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            fourthMethodSecondCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg)
        else
            fourthMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg)

    let point = Point2D(10.0, 20.0)
    let secondPoint = Point2D(30.0, 40.0)

    let retVal = fourthMethodFirstCallee(1000000, point, secondPoint, point, secondPoint)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -4
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a fast tail call on unix x64 as the caller and callee have equal stack size
let fifth() =
    let rec fifthMethodFirstCallee(iterationCount, firstArg: Point2D, secondArg: Point2D, thirdArg: Point2D, fourthArg: Point2D, fifthArg: Point2D) =
        if firstArg.X <> 10.0 then -100
        else if firstArg.Y <> 20.0 then -101
        else if secondArg.X <> 30.0 then -102
        else if secondArg.Y <> 40.0 then -103
        else if thirdArg.X <> 10.0 then -104
        else if thirdArg.Y <> 20.0 then -105
        else if fourthArg.X <> 30.0 then -106
        else if fourthArg.Y <> 40.0 then -107
        else if fifthArg.X <> 10.0 then -108
        else if fifthArg.Y <> 20.0 then -109
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            fifthMethodSecondCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)
        else
            fifthMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)

    and fifthMethodSecondCallee(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg) =
        if firstArg.X <> 10.0 then -150
        else if firstArg.Y <> 20.0 then -151
        else if secondArg.X <> 30.0 then -152
        else if secondArg.Y <> 40.0 then -153
        else if thirdArg.X <> 10.0 then -154
        else if thirdArg.Y <> 20.0 then -155
        else if fourthArg.X <> 30.0 then -156
        else if fourthArg.Y <> 40.0 then -157
        else if fifthArg.X <> 10.0 then -158
        else if fifthArg.Y <> 20.0 then -159
        else if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            fifthMethodSecondCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)
        else
            fifthMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)

    let point = Point2D(10.0, 20.0)
    let secondPoint = Point2D(30.0, 40.0)

    let retVal = fifthMethodFirstCallee(1000000, point, secondPoint, point, secondPoint, point)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -5
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a tail call via helper on unix x64 as the caller has less available incoming
// arg size than the callee
let sixth() =
    let rec sixthMethodFirstCallee(iterationCount, firstArg: Point2D, secondArg: Point2D, thirdArg: Point2D, fourthArg: Point2D, fifthArg: Point2D) =
        if firstArg.X <> 10.0 then -100
        else if firstArg.Y <> 20.0 then -101
        else if secondArg.X <> 30.0 then -102
        else if secondArg.Y <> 40.0 then -103
        else if thirdArg.X <> 10.0 then -104
        else if thirdArg.Y <> 20.0 then -105
        else if fourthArg.X <> 30.0 then -106
        else if fourthArg.Y <> 40.0 then -107
        else if fifthArg.X <> 10.0 then -108
        else if fifthArg.Y <> 20.0 then -109
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            sixthMethodSecondCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg)
        else
            sixthMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)

    and sixthMethodSecondCallee(iterationCount, firstArg, secondArg, thirdArg, fourthArg) =
        if firstArg.X <> 10.0 then -150
        else if firstArg.Y <> 20.0 then -151
        else if secondArg.X <> 30.0 then -152
        else if secondArg.Y <> 40.0 then -153
        else if thirdArg.X <> 10.0 then -154
        else if thirdArg.Y <> 20.0 then -155
        else if fourthArg.X <> 30.0 then -156
        else if fourthArg.Y <> 40.0 then -157
        else if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            sixthMethodSecondCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg)
        else
            sixthMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, thirdArg)

    let point = new Point2D(10.0, 20.0)
    let secondPoint = new Point2D(30.0, 40.0)

    let retVal = sixthMethodFirstCallee(1000000, point, secondPoint, point, secondPoint, point)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -6
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a tail call via helper on unix x64 as the caller has less available incoming
// arg size than the callee
let seventh() =
    let rec seventhMethodFirstCallee(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg) =
        if firstArg <> 1 then -100
        else if secondArg <> 2 then -101
        else if thirdArg <> 3 then -102
        else if fourthArg <> 4 then -103
        else if fifthArg <> 5 then -104
        else if sixthArg <> 6 then -105
        else if seventhArg <> 7 then -106
        else if eighthArg <> 8 then -107
        else if ninthArg <> 9 then -108
        else if tenthArg <> 10 then -109
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            seventhMethodSecondCallee(iterationCount)
        else
            seventhMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg)

    and seventhMethodSecondCallee(iterationCount) =
        if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            seventhMethodSecondCallee(iterationCount - 1)
        else
            seventhMethodFirstCallee(iterationCount - 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    let retVal = seventhMethodFirstCallee(1000000, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -7
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a fast tail call as the caller and callee have the incoming arg size
let seventhFastTailCall() =
    let rec seventhMethodFirstCalleeFastTailCall(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg) =
        if firstArg <> 1 then -100
        else if secondArg <> 2 then -101
        else if thirdArg <> 3 then -102
        else if fourthArg <> 4 then -103
        else if fifthArg <> 5 then -104
        else if sixthArg <> 6 then -105
        else if seventhArg <> 7 then -106
        else if eighthArg <> 8 then -107
        else if ninthArg <> 9 then -108
        else if tenthArg <> 10 then -109
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            seventhMethodSecondCalleeFastTailCall(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg)
        else
            seventhMethodFirstCalleeFastTailCall(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg)

    and seventhMethodSecondCalleeFastTailCall(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg) =
        if firstArg <> 1 then -110
        else if secondArg <> 2 then -111
        else if thirdArg <> 3 then -112
        else if fourthArg <> 4 then -113
        else if fifthArg <> 5 then -114
        else if sixthArg <> 6 then -115
        else if seventhArg <> 7 then -116
        else if eighthArg <> 8 then -117
        else if ninthArg <> 9 then -118
        else if tenthArg <> 10 then -119
        else if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            seventhMethodSecondCalleeFastTailCall(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg)
        else
            seventhMethodFirstCalleeFastTailCall(iterationCount - 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    let retVal = seventhMethodFirstCalleeFastTailCall(1000000, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -8
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a fast tail call as the caller and callee have the incoming arg size
let seventhFastTailCallReversed() =
    let rec seventhMethodFirstCalleeFastTailCallReversed(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg) =
        if firstArg <> 1 then -100
        else if secondArg <> 2 then -101
        else if thirdArg <> 3 then -102
        else if fourthArg <> 4 then -103
        else if fifthArg <> 5 then -104
        else if sixthArg <> 6 then -105
        else if seventhArg <> 7 then -106
        else if eighthArg <> 8 then -107
        else if ninthArg <> 9 then -108
        else if tenthArg <> 10 then -109
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            seventhMethodSecondCalleeFastTailCallReversed(iterationCount - 1, tenthArg, ninthArg, eighthArg, seventhArg, sixthArg, fifthArg, fourthArg, thirdArg, secondArg, firstArg)
        else
            seventhMethodFirstCalleeFastTailCallReversed(iterationCount - 1, tenthArg, ninthArg, eighthArg, seventhArg, sixthArg, fifthArg, fourthArg, thirdArg, secondArg, firstArg)

    and seventhMethodSecondCalleeFastTailCallReversed(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg, seventhArg, eighthArg, ninthArg, tenthArg) =
        if firstArg <> 10 then -110
        else if secondArg <> 9 then -111
        else if thirdArg <> 8 then -112
        else if fourthArg <> 7 then -113
        else if fifthArg <> 6 then -114
        else if sixthArg <> 5 then -115
        else if seventhArg <> 4 then -116
        else if eighthArg <> 3 then -117
        else if ninthArg <> 2 then -118
        else if tenthArg <> 1 then -119
        else if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            seventhMethodSecondCalleeFastTailCallReversed(iterationCount - 1, tenthArg, ninthArg, eighthArg, seventhArg, sixthArg, fifthArg, fourthArg, thirdArg, secondArg, firstArg)
        else
            seventhMethodFirstCalleeFastTailCallReversed(iterationCount - 1, tenthArg, ninthArg, eighthArg, seventhArg, sixthArg, fifthArg, fourthArg, thirdArg, secondArg, firstArg)

    let retVal = seventhMethodFirstCalleeFastTailCallReversed(1000000, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -8
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a tail call via helper on unix x64 as the caller has less available incoming
// arg size than the callee
let eight() =
    let rec eightMethodFirstCallee(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg) =
        if firstArg <> 1 then -100
        else if secondArg <> 2 then -101
        else if thirdArg <> 3 then -102
        else if fourthArg <> 4 then -103
        else if fifthArg <> 5 then -104
        else if sixthArg <> 6 then -105
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            eightMethodSecondCallee(iterationCount)
        else
            eightMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg)

    and eightMethodSecondCallee(iterationCount) =
        if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            eightMethodSecondCallee(iterationCount - 1)
        else
            eightMethodFirstCallee(iterationCount - 1, 1, 2, 3, 4, 5, 6)

    let retVal = eightMethodFirstCallee(1000000, 1, 2, 3, 4, 5, 6)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -9
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a tail call via helper on unix x64 as the caller has less available incoming
// arg size than the callee
let eightFastTailCall() =
    let rec eightMethodFirstCalleeFastTailCall(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg) =
        if firstArg <> 1 then -100
        else if secondArg <> 2 then -101
        else if thirdArg <> 3 then -102
        else if fourthArg <> 4 then -103
        else if fifthArg <> 5 then -104
        else if sixthArg <> 6 then -105
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            eightMethodSecondCalleeFastTailCall(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg)
        else
            eightMethodFirstCalleeFastTailCall(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg)

    and eightMethodSecondCalleeFastTailCall(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg) =
        if firstArg <> 1 then -100
        else if secondArg <> 2 then -101
        else if thirdArg <> 3 then -102
        else if fourthArg <> 4 then -103
        else if fifthArg <> 5 then -104
        else if sixthArg <> 6 then -105
        else if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            eightMethodSecondCalleeFastTailCall(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg)
        else
            eightMethodFirstCalleeFastTailCall(iterationCount - 1, 1, 2, 3, 4, 5, 6)

    let retVal = eightMethodFirstCalleeFastTailCall(1000000, 1, 2, 3, 4, 5, 6)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -10
    else
        0

// Will create a tail il instruction and force a tail call. This is will become
// a tail call via helper on unix x64 as the caller has less available incoming
// arg size than the callee
//
// Reversing the call arguments is a simple way to stress LowerFastTailCall
let eightFastTailCallReversed() =
    let rec eightMethodFirstCalleeFastTailCallReversed(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg) =
        if firstArg <> 1 then -100
        else if secondArg <> 2 then -101
        else if thirdArg <> 3 then -102
        else if fourthArg <> 4 then -103
        else if fifthArg <> 5 then -104
        else if sixthArg <> 6 then -105
        else if iterationCount = 0 then
            100
        else if iterationCount % 2 = 0 then
            eightMethodSecondCalleeFastTailCallReversed(iterationCount - 1, sixthArg, fifthArg, fourthArg, thirdArg, secondArg, firstArg)
        else
            eightMethodFirstCalleeFastTailCallReversed(iterationCount - 1, sixthArg, fifthArg, fourthArg, thirdArg, secondArg, firstArg)

    and eightMethodSecondCalleeFastTailCallReversed(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg, sixthArg) =
        if firstArg <> 6 then -100
        else if secondArg <> 5 then -101
        else if thirdArg <> 4 then -102
        else if fourthArg <> 3 then -103
        else if fifthArg <> 2 then -104
        else if sixthArg <> 1 then -105
        else if iterationCount = 0 then
            101
        else if iterationCount % 2 = 0 then
            eightMethodSecondCalleeFastTailCallReversed(iterationCount - 1, sixthArg, fifthArg, fourthArg, thirdArg, secondArg, firstArg)
        else
            eightMethodFirstCalleeFastTailCallReversed(iterationCount - 1, sixthArg, fifthArg, fourthArg, thirdArg, secondArg, firstArg)

    let retVal = eightMethodFirstCalleeFastTailCallReversed(1000000, 1, 2, 3, 4, 5, 6)

    if retVal <> 100 && retVal <> 101 then
        printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
        -11
    else
        0

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

type Driver() =
    // Notes:
    //
    // Drive all of the different methods that will be called and account for
    // timing.
    member _.Start() =
        let rec runMethod(iterationCount: int, cb: _ -> int) =
            if iterationCount > 0 then
                let retVal = cb()

                if retVal <> 0 then
                    retVal
                else
                    runMethod(iterationCount - 1, cb)
            else
                0

        let runMethodWithTiming(methodName: string, worksOnlyOnNetCore5: bool, iterationCount: int, cb: _ -> int) =
            let canRun = (worksOnlyOnNetCore5 && System.Environment.Version.Major > 3) || worksOnlyOnNetCore5 = false

            if canRun then
                let startTime = DateTime.Now
                let retVal = runMethod(iterationCount, cb)
                let endTime = DateTime.Now

                let elapsedTime = (endTime - startTime).TotalMilliseconds

                if retVal <> 0 then
                    failwith "Incorrect method passed"

                printfn "[%s] - %fms" methodName elapsedTime
            else
                printfn "[%s] - skipped. Only works on .Net Core 5.0 and above" methodName

        runMethodWithTiming("FirstMethod", false, 100000, first)
        runMethodWithTiming("SecondMethod", false, 100000, second)

        // ThirdMethod will SO in debug
        runMethodWithTiming("ThirdMethod", false, 100000, third)

        // FourthMethod will SO in debug
        runMethodWithTiming("FourthMethod", false, 10, fourth)

        // The rest of the methods require generic tailcall helper therefore
        // will only work on >3.1

        // All the following SO in debug

        runMethodWithTiming("FifthMethod", true, 10, fifth)
        runMethodWithTiming("SixthMethod", true, 10, sixth)
        runMethodWithTiming("SeventhMethod", true, 10, seventh)
        runMethodWithTiming("SeventhMethodFastTailCall", false, 10, seventhFastTailCall)
        runMethodWithTiming("SeventhMethodFastTailCallReversed", false, 10, seventhFastTailCallReversed)
        runMethodWithTiming("EigthhMethod", true, 10, eight)
        runMethodWithTiming("EigthMethodFastTailCall", false, 10, eightFastTailCall)
        runMethodWithTiming("EigthMethodFastTailCallReversed", false, 10, eightFastTailCallReversed)

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

[<EntryPoint>]
let main argv =
    let driver = Driver()
    driver.Start()

    // If we have gotten to this point we have not StackOverflowed. Therefore
    // consider this a passing test
    100
