#!/bin/bash

dir=`dirname $0`

is_xfail() {
    case "$1" in
        # "jlamp0_mono")
        #     return 0
        #     ;;
        *)
            return 1
            ;;
    esac
}

updateoracle=false
suffix=.opt
files=""
success=true

while test $# != 0; do
    case "$1" in
        "-update-oracle")
            updateoracle=true
            shift 1
            ;;
        "-suffix")
            suffix="$2"
            shift 2
            ;;
        "-"*)
            printf "unknown option: %s\n" "$1"
            printf "usage: ce-bench [-update-oracle] [-suffix s] <files>\n"
            printf "  <files> must be given without the '.mlw' suffix\n"
            printf "  The suffix is appended on the why3 executable name, e.g. '.opt'"
            printf "  if <files> empty, use all files from directory 'ce'\n"
            exit 2;;
        *)
            files="$files $1"
            shift 1
    esac
done

if test "$files" = "" ; then
    files="$dir/ce/*.mlw"
fi

failed=""

colorize() {
    if command -v pygmentize &> /dev/null; then
        pygmentize -ldiff
    else
        cat
    fi
}

whydata=$(bin/why3"$suffix" --print-datadir)
whylib=$(bin/why3"$suffix" --print-libdir)

# $1 = prover, $2 = dir, $3 = filename, $4 = true for WP; false for SP, $5 = steps
run () {
    printf "  $2 ($1)... "
    file_path="$2/$3"
    wp_sp=""
    if $4; then
        f="${file_path}_$1_WP"
        oracle_file="$2/oracles/$3_$1_WP.oracle"
        echo "Weakest Precondition" > "$f.out"
        printf "Weakest Precondition ${file_path} ($1)... "
    else
        f="${file_path}_$1_SP"
        oracle_file="$2/oracles/$3_$1_SP.oracle"
        wp_sp=",vc_sp"
        echo "Strongest Postcondition" > "$f.out"
        printf "Strongest Postcondition ${file_path} ($1)... "
    fi
    steps=$5
    if [ "$3" = "strings" ] && [ "$1" = "CVC4,1.8" ] ; then
        prover="$1,strings+counterexamples"
    else
        prover="$1,counterexamples"
    fi
    $dir/../bin/why3"$suffix" prove -a split_vc -P $prover --stepslimit=$steps --timelimit=0 \
            "${file_path}.mlw" --json-model-values --debug=print_model_attrs${wp_sp} | \
        sed -e "s|$whydata|WHY3DATA|g" -e "s|$whylib|WHY3LIB|g" | \
        # This ad hoc sed removes any timing information from counterexamples output.
        # Counterexamples in JSON format cannot match this regexp.
        sed 's/ *([0-9]\+\.[0-9]\+s, [0-9]\+ steps)//g' | \
        # This ad hoc sed removes diff between Timeout and Unknown (unknown)
        # when running from platform with different speed.
        sed 's/Prover result is: \(Timeout\.\|Unknown (\(unknown\|incomplete\|unknown + \(incomplete\|interrupted\)\))\.\|Out of memory.\|Step limit exceeded\.\|Step limit exceeded ([^)]*)\.\)$/Prover result is: Unknown or time\/memory\/step limit./' | \
        # Remove steps informations
        sed -r 's/Unknown \(sat\).*$/Unknown \(sat\)/' | \
        sed -r 's/Valid \(.*$/Valid/' >> "$f.out"
    str_oracle=$(tr -d ' \n' < "${oracle_file}")
    str_out=$(tr -d ' \n' < "$f.out")
    if $updateoracle; then
        echo "Updating oracle for ${file_path}, prover $1"
        mv "$f.out" "${oracle_file}"
    else
        if [ "$str_oracle" = "$str_out" ] ; then
            echo "ok"
        else
            if is_xfail "$3"; then
                echo "XFAILED."
            else
                echo "FAILED!"
                # echo "Oracle is ${str_oracle}"
                # echo "Output is ${str_out}"
                echo "diff is the following:"
                echo "$f"
                echo diff -u "${oracle_file}" "$f.out"
                diff -u "${oracle_file}" "$f.out"|colorize
	              failed="$failed$f\n"
                success=false
            fi
        fi
    fi
}

for file in $files; do
    filedir=`dirname $file`
    filebase=`basename $file .mlw`
    printf "Running provers on $filedir/$filebase.mlw\n";
    cvc4steps=150000
    case "$filebase" in
      "bv32")
        z3steps=2500000
        ;;
      "int_overflow")
        z3steps=400000
        ;;
      "floats")
        z3steps=400000
        ;;
      "strings")
        z3steps=3000
        ;;
      "threshold")
        z3steps=2500000
        ;;
      *)
        z3steps=7500000
        ;;
    esac
    run CVC4,1.8 $filedir $filebase true  $cvc4steps
    run CVC4,1.8 $filedir $filebase false $cvc4steps
    run Z3,4.8.10 $filedir $filebase true  $z3steps
    run Z3,4.8.10 $filedir $filebase false $z3steps
done

if [ "$success" = true ]; then
    echo "ce-bench: success"
    exit 0
else
    printf "\nce-bench: failed for the following files\n$failed\n"
    exit 1
fi
