package generators;

int PLUS=0;
int MINUS=1;
int TIMES=2;
int DIV=3;
int MOD=4;

/**
 * 
 * Generate an expression based on the N operands and T operators
 * given.
 * 
 */
generator int expr(int N, int[N] pars, int T, int[T] ops){
    bit isb; int choice;
    generator int tmp(ref int choice){
        choice = ??;
        return ?? ? pars[choice] : ??(2);
    }
    return exprb(tmp, T, ops, true, true, isb, choice);
}


/**
 * 
 * Generate an expression based on T operators and operands produced
 * by the generator choices.
 * given.
 * 
 */
generator int exprG(fun choices, int T, int[T] ops){
    bit isb; int choice;
    return exprb(choices, T, ops, true, true, isb, choice);
}

//generator int exprGX(fun choices, int N, int[N] pars, int T, int[T] ops){
//    generator int chose(int choice) {
//        choice = ??;
//        if (choice < N) { return pars[choice]; }
//        assert choice ≤ N;
//        int choice2;
//        return choices(choice2);
//    }
//    return exprG(chose, T, ops);
//}


/**
 * 
 * Generate a boolean expression based on the N operands and T operators
 * given. 
 * 
 */
generator bit exprBool(int N, int[N] pars, int T, int[T] ops){
    generator int tmp(ref int choice){
        choice = ??;
        return ?? ? pars[choice] : ??(2);
    }
    return exprBoolG(tmp, T, ops);
}


generator bit exprBoolG(fun choices, int T, int[T] ops){
    
    if(??){            
        bit xa = exprBoolG(choices, T, ops);
        if(xa){
            xa = exprBoolG(choices, T, ops);
        }
        if(??){
            return xa;
        }else{
            return !xa;
        }            
    }
    if(??){
        bit a; int b;
        int xa = exprb(choices, T, ops, true, true, a, b);
        int xb = exprb(choices, T, ops, true, true, a, b);
        return  {| xa (< | == | !=) xb |};
    }
}


generator int linexp(int N, int[N] vals){
    int[N+1] t;
    t[0]= (??-2)*vals[0];
    int i=1;
    repeat(N-1){
        t[i] = t[i-1] + (??-2)*vals[i];
        i = i+1;
    }
    return t[i-1];
}

generator int plinexp(int N, int[N] vals){
    int[N+1] t;
    t[0]= (??)*vals[0];
    int i=1;
    repeat(N-1){
        t[i] = t[i-1] + (??)*vals[i];
        i = i+1;
    }
    return t[i-1];
}


generator int op(int ch, int a, int b, int N, int[N] ops, bit canplus, bit cantimes){
    int t = ch;
    int cop= ops[t];
    assert cop ≤ MOD && cop ≥0  : "Must pass a valid operator";
    if(cop==PLUS){
        assert canplus;
        return a + b;
    }
    if(cop==MINUS){            
        return a - b;
    }
    if(cop==TIMES){
        assert cantimes;
        return a * b;
    }
    if(cop==DIV){
        return a / b;
    }
    if(cop==MOD){
        return a % b;
    }
}


generator int exprb(fun chose, int T, int[T] ops, bit canplus, bit cantimes, ref bit isbase, ref int choice){
    if(??){
        assert T>0;
        int ch = ??;
        isbase = false;
        bit lib, rib;
        int lch; int rch;
        int och = ops[ch];
        int xa = exprb(chose, T, ops, och!=PLUS, och!=TIMES, lib, lch);
        int xb = exprb(chose, T, ops, true, true, rib, rch);
      if(lib && rib && 
                (och ==PLUS || 
                        och==TIMES)){ 
            assert lch ≤ rch; } 
        return op(ch, xa, xb, T, ops, canplus, cantimes);
    }else{
        isbase = true;
        return chose(choice);        
    }
}