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

public class Map2{
    public static void main(String[] args){
        final MapFn mapFun = new Square();
        MintList Slist = new MintList(1,new MintList(2,new MintList(3,null)));
        
        MintList mappedList = Map(mapFun,Slist);
        
        if(mappedList != null)
        {
            MintList tail = mappedList.tail;
            do
            {
                System.out.println("Result Element = "+ mappedList.head);
                mappedList = mappedList.tail;
            }while(mappedList != null);
            
        }
        
        
        // Code<Map2.MintList> stagedMappedList = SMap(<|mapFun|>, Slist);
        Code<Map2.MintList> stagedMappedList = SMap(<|new Square()|>, Slist);
        
        MintList resultStagedMappedList = stagedMappedList.run();
        
        if(resultStagedMappedList != null)
        {
            MintList tail = resultStagedMappedList.tail;
            do
            {
                System.out.println("Staged Result Elemet = "+ resultStagedMappedList.head);
                resultStagedMappedList = resultStagedMappedList.tail;
            }while(resultStagedMappedList != null);
            
        }
    }
    
    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 SafeCode<Map2.MintList> SMap(final SafeCode<? extends Map2.MapFn> f,final MintList list){
        if(list == null)
        {
            return <|(Map2.MintList)null|>;
        }
        final int head = list.head;
        
        SafeCode<Map2.MintList> mappedList = <| new Map2.MintList(`(f).Apply(head) , `(Map2.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 class MintList{
        
        public separable MintList(int head,MintList tail){
            this.head = head;
            this.tail = tail;
        }
        
        public int head;
        public MintList tail;
        
    }
    
    
    
}

