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

public class unroll_part{
    
    public static void main(String[] args)
    {
        final LoopIteration li = new PrintIteration();
        final sLoopIteration sli = new sPrintIteration();
        intCell i = new intCell();
        // final Code<intCell> j = <| new intCell() |>;
        Integer init = 0;
        rolled(i, init, 3, 30, li);
        System.out.println(i.value);
        Code<? extends FibFun> CodeFib6 = <| new FibFun() {
            public void Fib(){
                final intCell j = new intCell();
                Integer init = 0;
                `(unroll_part ( <| j |>, <| init |>, 3, 30, sli, 6));
                System.out.println(j.value);
            }
        } |>;
        FibFun sfib6 = CodeFib6.run();
        sfib6.Fib();
    }
    
    public static separable Code<Void> unroll(Code<intCell> i,
                                              Code<Integer> init,
                                              int incr,
                                              int iterations,
                                              sLoopIteration F){
        Code<Void> C = <| { } |>;
        final int fIncr = incr;
        final int fIterations = iterations;
        for(int x = 0; x < iterations; x++){
            final int xx = x;
            C = <| { `C;
                `( F.iteration(<| `init + xx * fIncr |>)); } |>;
        }
        C = <| { `C; `(i).value = `init + fIterations * fIncr; } |>;
        return C;
    }
    
    public static void rolled(intCell i, Integer init, int incr, int iterations, LoopIteration F){
        for(int x = 0; x < iterations; x++){
            F.iteration(init + x * incr);
        }
        i.value = init + iterations * incr;
    }
    
    public static separable Code<Void> unroll_part(Code<intCell> i,
                                                   Code<Integer> init,
                                                   int incr,
                                                   int iterations,
                                                   sLoopIteration F,
                                                   int blockSize){
        final int loops = iterations/blockSize;
        final int leftover = iterations % blockSize;
        final int fBlockSize = blockSize;
        final int fIncr = incr;
        if(loops < 2)
            return unroll(i, init, incr, iterations, F);
        else{
            final Code<Integer> ii = <| `(i).value |>;
            return
                <| { for (`(i).value = `init;
                          `(i).value < (`init + new Integer(loops * fBlockSize * fIncr));){
                              `(unroll(i, ii, fIncr, fBlockSize, F));
                          }
                          `(unroll(i, ii, fIncr, leftover, F)); } |>;
        }
    }
    
    public static class intCell{
        public Integer value = 0;
    }
    
    public static abstract class FibFun{
        public abstract void Fib();
    }
    
    public static interface LoopIteration{
        public void iteration (Integer i);
    }
    
    public static class PrintIteration implements LoopIteration{
        public void iteration (Integer i){
            new Integer(i * i);
        }
    }
    
    public static interface sLoopIteration{
        public separable Code<Void> iteration (Code<Integer> i);
    }
    
    public static class sPrintIteration implements sLoopIteration{
        public separable Code<Void> iteration (Code<Integer> i){
            return <| {new Integer(`i * `i); } |>;
        }
    }
}
