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

// higher order function version 
public class HigherOrderMSP {
    public static interface ILambda_double_double {
        public double apply(double param);
    }
    
    public static double power(double x, int n){
        if (n == 1)
            return x;
        else
            return x * power(x, n-1);
    }
    
    public static separable Code<Double> spower(final Code<Double> x, int n){
        if (n == 1)
            return x;
        else
            return <| `x * `(spower(x, n-1)) |>;
    }
    
    public static void main(String[] args) {
        System.out.println("power(2, 17)                = "+power(2,17));
        long start = System.nanoTime();
        for(int x = 0; x <  134217728; x++)
            power(2, 17);
        long unstaged = System.nanoTime() - start;
        System.out.println("\t"+unstaged);
        
        System.out.println("spower(<|2.0|>, 17).run()   = "+spower(<|2.0|>,17).run());
        
        ILambda<Double,Double> powerOf17 =
            new ILambda<Double,Double>() {
            public Double apply(Double param) {
                return power(param, 17);
            }
        };
        System.out.println("powerOf17.apply(2.0)        = "+powerOf17.apply(2.0));
        
        ILambda<Double,Double> badPowerOf17 =
            new ILambda<Double,Double>() {
            public Double apply(final Double param) {
                // not allowed to ` because not in <| |>
                return spower(<|param|>,17).run();
            }
        };
        System.out.println("badPowerOf17.apply(2.0)     = "+badPowerOf17.apply(2.0));
        System.out.println("badPowerOf17.apply(3.0)     = "+badPowerOf17.apply(3.0));
        
        Code<? extends ILambda<Double,Double>> codePowerOf17 =
            <| new ILambda<Double,Double>() {
            public Double apply(Double param) {
                // allowed to ` because in <| |>
                return `(spower(<|param|>,17));
                // return 1.0*param*param....;
            }
        } |>;
        ILambda<Double,Double> stagedPowerOf17 = codePowerOf17.run();
        System.out.println("stagedPowerOf17.apply(2.0)  = "+stagedPowerOf17.apply(2.0));
        start = System.nanoTime();
        for(int x = 0; x<134217728; x++)
            stagedPowerOf17.apply(2.0);
        long staged = System.nanoTime() - start;
        System.out.println("\t"+staged);
        System.out.println("stagedPowerOf17.apply(3.0)  = "+stagedPowerOf17.apply(3.0));
        
        Code<? extends ILambda_double_double> primCodePowerOf17 =
            <| new ILambda_double_double() {
            public double apply(double param) {
                // allowed to ` because in <| |>
                return `(spower(<|param|>,17));
                // return 1.0*param*param....;
            }
        } |>;
        ILambda_double_double primStagedPowerOf17 = primCodePowerOf17.run();
        System.out.println("stagedPowerOf17.apply(2.0)  = "+primStagedPowerOf17.apply(2.0));
        start = System.nanoTime();
        for(int x = 0; x<134217728; x++)
            primStagedPowerOf17.apply(2.0);
        long primStaged = System.nanoTime() - start;
        System.out.println("\t"+primStaged);
        System.out.println("stagedPowerOf17.apply(3.0)  = "+stagedPowerOf17.apply(3.0));
    }
}
