//this is a version of Power.java that has been modified to be used with Timing.java in order to perform timings

import edu.rice.cs.mint.runtime.Code;
import edu.rice.cs.mint.runtime.SafeCode;

public class Power3 {
    
    static Test<Integer> a = new Test<Integer>() {
        public Integer run_unstaged() {
            return power(2, 17);
        }
        
        public Code<? extends TestFun<Integer>> gen_code() {
            return <| new TestFun<Integer>() {
                public Integer run_staged() {
                    return `(spower(<| 2 |>, 17));
                }
            } |>;
        }
    };
    
    public static void main(String[] args){
        //These two lines are to test for bugs
        Code<? extends TestFun<Integer>> b = a.gen_code();
        TestFun<Integer> c = b.run();
        System.out.println(c.run_staged());
        int[] counts = {100000, 100000, 1000, 100000};
        print_timings(a, counts);
    }
    
    public static int power(int x, int n){
        if (n == 1)
            return x;
        else
            return x * power(x, n-1);
    }
    
    public static separable SafeCode<Integer> spower(SafeCode<Integer> x, int n){
        if (n == 1)
            return x;
        else
            return <| `x * `(spower(x, n-1)) |>;
    }
    
    
    public interface Test<T> {
        T run_unstaged (); /* run the unstaged test */
        Code<? extends TestFun<T>> gen_code (); /* generate code for the staged test */
    }
    
    public interface TestFun<T> {
        T run_staged (); /* run a staged test */
    }
    
    
    
    public static <T> void print_timings (Test<T> test, int [] counts){
        
        long start = System.nanoTime();
        for(int i = 0; i < counts[0]; i++)
            test.run_unstaged();
        long time = System.nanoTime() - start;
        System.out.println("test.run_unstaged() --> " + (time / counts[0]));
        
        start = System.nanoTime();
        for(int i = 0; i < counts[1]; i++)
            test.gen_code();
        time = System.nanoTime() - start;
        System.out.println("test.gen_code()     --> " + (time / counts[1]));
        
        Code<? extends TestFun<T>> x = test.gen_code();
        start = System.nanoTime();
        for(int i = 0; i < counts[2]; i++)
            x.run();
        time = System.nanoTime() - start;
        System.out.println("x.run()             --> " + (time / counts[2]));
        
        TestFun<T> y = x.run();
        start = System.nanoTime();
        for(int i = 0; i < counts[3]; i++)
            y.run_staged();
        time = System.nanoTime() - start;
        System.out.println("y.run_staged()      --> " + (time / counts[3]));
    }
}

