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

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

public class Map4{
    public static void main(String[] args){
        final MapFn mapFun = new Square();
        final MintList Slist = new MintList(1,new MintList(2,new MintList(3,null)));
        final sMintList Slist2 = new sMintList(<| 1 |>,new sMintList(<| 2 |>,new sMintList(<| 3 |>,null)));
        
        MintList mappedList = Map(mapFun,Slist);
        
        
        System.out.println("Map--> square items in a list contains 1,2,3");
        Benchmark.TimedTask[] results =
            Benchmark.benchmark(Benchmark.stagingTasks(new Benchmark.Task() {
            public void run() {
                //
                // call unstaged code here
                //
                MintList l = Map(new Square(),Slist);
                //
                //
                //
            }
        }, new Benchmark.Thunk<Code<? extends Benchmark.Task>>() {
            public Code<? extends Benchmark.Task> value() {
                return <| new Benchmark.Task() {
                    public void run() {
                        //
                        // splice in staged code here
                        //
                        sMintList l = `(SMap(new sSquare(), Slist2));
                        //
                        //
                        //
                    }
                } |>;
            }
        }));
        
        Benchmark.print(0, results); // relative to unstaged
        System.out.println();
        Benchmark.print(results.length-1, results); // relative to staged
        
        
    }
    
    
    
    public static MintList Map(MapFn f,MintList list){
        
        if(list == null)
        {
            return null;
        }
        
        MintList mappedList = new MintList(f.Apply(list.head) , Map(f,list.tail));
        
        return mappedList;
        
    }
    
    public static separable Code<sMintList> SMap(final sMapFn f, final sMintList list){
        if(list == null)
        {
            return <|(sMintList)null|>;
        }
        final Code<Integer> head = list.head;
        
        Code<sMintList> mappedList = <| let final int mhead = `(f.Apply(head));
        new sMintList(<| mhead |> , `(SMap(f,list.tail))) |>;
        
        return mappedList;
        
    }
    
    public static interface MapFn{
        public separable int Apply(int element);
        
    }
    
    public static class Square implements MapFn {
        public separable int Apply(int element){
            
            return element * element;
        }
    }
    
    public static interface sMapFn{
        public separable Code<Integer> Apply(Code<Integer> element);
        
    }
    
    public static class sSquare implements sMapFn {
        public separable Code<Integer> Apply(final Code<Integer> element){
            return <| let int e = `element;
            (e * e) |>;
        }
    }
    
    public static class MintList{
        
        public separable MintList(int head,MintList tail){
            this.head = head;
            this.tail = tail;
        }
        
        public int head;
        public MintList tail;
        
    }
    
    public static class sMintList{
        
        public separable sMintList(Code<Integer> head,sMintList tail){
            this.head = head;
            this.tail = tail;
        }
        
        public Code<Integer> head;
        public sMintList tail;
        
    }
    
}

