public class MuRek{

public abstract static class F{
    public int arity;
    public abstract int eval(int... param);
    public abstract String toString();
}

/***************************************************************/

public static class prF extends F{
    public int position;
    public  prF (int arity, int position){
        if (position > arity|| arity < 0|| position <= 0) 
            System.out.println("Construction: Wrong parameters for pr");
        this.arity=arity;
        this.position=position;
    }
    public int eval(int... param){
        if (param.length!=arity) 
            System.out.println("Evaluation: Wrong parameters for pr at "+this);
        return param[position-1];}
    public String toString(){return "pr("+arity+")_"+position;}
}

/***************************************************************/


public static class Zconst extends  F{
    public  Zconst (){
        this.arity=0;
    }
    public int eval(int... param){
        if (param.length!=arity)
            System.out.println("Evaluation: Wrong parameters for Z at "+this);
        return 0;
    }
    public String toString(){return "Z";}
}

/***************************************************************/


public static class Sconst extends  F{
    public  Sconst (){
        this.arity=1;
    }
    public int eval(int... param){
        if (param.length!=arity) 
            System.out.println("Evaluation: Wrong parameters for S at "+this);
        return param[0]+1;
    }
    public String toString(){return "S";}
}

/***************************************************************/

public static class KompOperator extends F{
    public F[] g;
    public KompOperator(F... f){
        if (f.length < 1 || f.length != f[f.length-1].arity+1) 
            System.out.println("Construction: Wrong parameters for Komp");
        arity = f[0].arity;
        this.g=f;
    }
    public int eval(int... param){
        if (param.length!=arity) 
            System.out.println("Evaluation: Wrong parameters for Komp at "+this);
        int data[]= new int[g.length-1];
        for (int i=0; i< g.length-1; i++) data[i]=g[i].eval(param);
        return g[g.length-1].eval(data);
    }
    public String toString(){String str="Komp(";
        str += g[0];
        for (int i=1; i< g.length; i++) str += ","+g[i];
        str += ")";
        return str;
    }
}

/***************************************************************/


public static class PrRekOperator extends F{
    public F g,h;
    public PrRekOperator(F g, F h){
        if (g.arity+2  != h.arity) System.out.println("Construction: Wrong parameters for PrRek");
        this.arity = g.arity+1;
        this.g=g;
        this.h=h;
    }
    public int eval(int... param){
        if (param.length!=arity) System.out.println("Evaluation: Wrong parameters for PrRek");

        int [] g_param = new int[param.length-1 ];
        int [] f_param = new int[param.length ];
        int [] h_param = new int[param.length+1 ];

        for (int i=0; i<g_param.length; i++){
            g_param[i]=param[i]; 
            f_param[i]=param[i]; 
            h_param[i]=param[i];
        }
        int y = param[param.length-1];
        if ( y == 0) return g.eval(g_param);
        f_param[param.length-1] = y-1;
        h_param[param.length-1] = y-1;
        h_param[param.length  ] = this.eval(f_param);
        
        return h.eval(h_param);
    }
    public String toString(){
        return "PrRek("+ g+","+ h + ")";
    }
}

/***************************************************************/
public static F Z = new  Zconst();
public static F S = new  Sconst();

public static F pr    (int a,int p){return new prF(a,p);}

public static F Komp  (F... f)     {return new KompOperator(f);}
public static F PrRek (F g, F h)   {return new PrRekOperator(g,h);}

/***************************************************************/
public static void main(String args[]){

    F c0_1 = Komp(Z,S);
    System.out.println("c0_1 = "+ c0_1 );
    System.out.println("c0_1() --> " + c0_1.eval() );
    System.out.println();

    F c0_2 = Komp(c0_1,S);
    System.out.println("c0_2 = "+  c0_2 );
    System.out.println("c0_2() --> " + c0_2.eval() );
    System.out.println();

    F c1_0 = PrRek(Z,pr(2,2));
    System.out.println("c1_0 = "+ c1_0 );
    System.out.println("c1_0(0) --> " +  c1_0.eval(0) );
    System.out.println("c1_0(1) --> " +  c1_0.eval(1) );
    System.out.println("c1_0(2) --> " +  c1_0.eval(2) );
    System.out.println();

    F c1_1 = PrRek(c0_1,pr(2,2));
    System.out.println("c1_1 = "+ c1_1 );
    System.out.println("c1_1(0) --> " +  c1_1.eval(0) );
    System.out.println("c1_1(1) --> " +  c1_1.eval(1) );
    System.out.println("c1_1(2) --> " +  c1_1.eval(2) );
    System.out.println();

    F c1_2  = PrRek(c0_2,pr(2,2));
    System.out.println("c1_2 = "+ c1_2 );
    System.out.println("c1_2(0) --> " +  c1_2.eval(0) );
    System.out.println("c1_2(1) --> " +  c1_2.eval(1) );
    System.out.println("c1_2(2) --> " +  c1_2.eval(2) );
    System.out.println();

    F c3_2  = Komp(pr(3,1),c1_2);
    System.out.println("c3_2 = " + c3_2 );
    System.out.println("c3_2(0,2,4) --> " +  c3_2.eval(0,2,4) );
    System.out.println("c3_2(1,3,5) --> " +  c3_2.eval(1,3,5) );
    System.out.println("c3_2(2,0,1) --> " +  c3_2.eval(2,0,1) );
    System.out.println();
    
    F c2_1  = Komp(pr(2,1),c1_1);
    System.out.println("c2_1 = " + c2_1 );
    System.out.println("c2_1(0,2) --> " +  c2_1.eval(0,2) );
    System.out.println("c2_1(1,3) --> " +  c2_1.eval(1,3) );
    System.out.println("c2_1(2,0) --> " +  c2_1.eval(2,0) );
    System.out.println();
    F id = pr(1,1);

    F add  = PrRek(id,Komp(pr(3,3),S));
    System.out.println("add = " + add );
    System.out.println("add(1,3) --> " +  add.eval(1,3) );
    System.out.println("add(2,4) --> " +  add.eval(2,4) );
    System.out.println();
   
    F mult  = PrRek(c1_0,Komp(pr(3,1),pr(3,3),add));
    System.out.println("mult = " + mult );
    System.out.println("mult(1,3) --> " +  mult.eval(1,3) );
    System.out.println("mult(2,4) --> " +  mult.eval(2,4) );
    System.out.println();
    
    F v  = PrRek(Z,pr(2,1));
    System.out.println("v = " + v );
    System.out.println("v(0) --> " +  v.eval(0) );
    System.out.println("v(2) --> " +  v.eval(2) );
    System.out.println();

    F sub  = PrRek(id,Komp(pr(3,3),v));
    System.out.println("sub = " + add );
    System.out.println("sub(1,3) --> " +  sub.eval(1,3) );
    System.out.println("sub(4,2) --> " +  sub.eval(4,2) );
    System.out.println();
 
    F sg  = PrRek(Z,c2_1);
    System.out.println("sg = " + add );
    System.out.println("sg(0) --> " +  sg.eval(0) );
    System.out.println("sg(2) --> " +  sg.eval(2) );
    System.out.println();
 
    }
}
