#!/bin/bash

SELF_NAME=$0

function rep_usage_error() {
	echo "Error: $*" 1>&2
	echo
	echo "Usage:"
	echo "$SELF_NAME [options] {--mode|-m} mode_name mode_arguments ..."
	cat <<EOM
Options:
	--z3 file
	--vutil file
		specify path to a particular executable. The default value assumes
		these executables to be in the current directory
	--time_limit seconds
		time limit for the whole process
Modes:
tptp_vampire
	The mode expects a single file as an argument.
	
	The standard Vampire syntax for specifying problem colors is 
	expected here.

	
smt_z3_range
	The mode expects a single file as an argument.
	
	The file is scanned and we identify functions that appear only 
	with numerals as arguments. We consider these functions to
	represents the state variables after a particular number of 
	unrollings.
	We determine the range of the argument numerals (i.e. what we
	expect to be the number of unrollings) and pick one numeral in 
	the middle of the range.
	The occurrences of state functions which have the numeral lower
	than the midpoint will be colored red, those with numeral greater
	than the midpoint will be blue. Other symbols, oncluding the 
	state occurrences with midpoint numeral remain grey.   

		
z3_extract_proofs
	The mode expects two arguments: a file name containing a Z3 problem,
	and a prefix of a file name that will be used for saving proofs 
	extracted from the problem. The problem should contain (check-sat)
	commands, the (get-proof) commands to obtain proofs will be added 
	automatically.
	
	The files generated by this command can be used for the z3_proof_range 
	mode.
	
z3_proof_range
	The mode expects a single file containing a Z3 proof as an argument.
	
	The symbol coloring is done as in the smt_z3_range mode.
		
smt_z3_multiform
smt_vampire_multiform
	
	The multiform modes expect arguments
		left_formula_count formula_file1 formula_file2 ...
		
	The left_formula_count is an integer greater than zero and
	smaller than the number of formula files. It determines how
	many of the formula files will belong to the first formula
	how many to the second in the interpolation.
	The symbols occurring only in the first formula are red, 
	only in the second blue and those occurring in both are
	grey.
	
	
Modes smt_z3_multiform, z3_proof_range and smt_z3_range can take
an option "--blue" or "-b" which will make them perform the proof
localization by chantification of blue symbols rather than red ones.
 
EOM
	exit 1
}

function ensure_exec_presence() {
	#usage: <var name> <default file name>

	local VAR_NAME=$1
	local DEF=$2
	eval local CUR_VAL=\$$VAR_NAME

	if [ "$CUR_VAL" = "" ]; then 
		CUR_VAL="$BASE_DIR/$DEF"
		eval $VAR_NAME="\"$CUR_VAL\""
	fi
	
	if [ ! -f "$CUR_VAL" ]; then 
		rep_usage_error "$DEF executable not found: $CUR_VAL"
	fi
}

BASE_DIR=`dirname $0`

BLUE_ARG=""
INP=""
MODE=""
TIME_LIMIT=60
function parse_args() {
	
	while [ "$#" -ne 0 ]; do
		case "$1" in
		--mode | -m)
			case "$2" in
			tptp_vampire | smt_z3_range | z3_extract_proofs | z3_proof_range | smt_z3_multiform | smt_vampire_multiform)
				MODE="$2"
				;;
			*)
				rep_usage_error "Invalid mode: $2"
				;;
			esac
			shift 2
			;;
		--z3)
			Z3_EXEC="$2"
			shift 2
			;;
		--vutil)
			VUTIL_EXEC="$2"
			shift 2
			;;
		--vampire)
			VAMPIRE_EXEC="$2"
			shift 2
			;;
		--time_limit | -t)
			TIME_LIMIT="$2"
			shift 2
			;;
		--blue | -b)
			BLUE_ARG="-b"
			shift 1
			;;
		*)
			INP="$INP $1"
			shift 1
		esac
	done
}

function tptp_vampire_mode() {
	$VUTIL_EXEC vamp_casc --smtlib_consider_ints_real on --show_interpolant minimized --mode casc -statistics none -proof off -t $TIME_LIMIT $* 
}

function z3_extract_proofs_mode() {
	local AUX=`mktemp -d -t ezpXXXXX`
	
	local FILE=$1
	local OUT_PREFIX=$2
	local PRB=$AUX/prb.smt2
	
	cat $FILE | sed "s/(check-sat)/(check-sat) (get-proof)/"> $PRB
	
	$Z3_EXEC PROOF_MODE=2 $PRB | awk -v "filePref=$OUT_PREFIX" '
	{
	        proofLine=1
	}
	
	BEGIN { fileIdx=1; outFile=filePref fileIdx; anythingOutput=0; haveFirstRes=0}
	{ proofLine=1 }
	/^sat/ || /^unsat/ || /^\(error/ {
	        if(anythingOutput) {
	                print "done with " outFile
	                close(outFile)
	                fileIdx+=1; outFile=filePref fileIdx; anythingOutput=0
	        }
	        proofLine=0
	        haveFirstRes=1
	}
	proofLine==1 && haveFirstRes==1 {
	        print > outFile
	        anythingOutput=1
	}
	
	'
	
	rm -r $AUX
}

function z3_proof_range_mode() {
	local IN_FILE=$1
		
	cat $IN_FILE | $VUTIL_EXEC zie $BLUE_ARG
}


function smt_z3_range_mode() {
	local AUX_DIR=`mktemp -d -t vint_XXXXXX`
	local IN_FILE=$1
	local PRB_FILE="$AUX_DIR/prb.smt2"
	local PROOF_FILE="$AUX_DIR/proof.txt"
	local Z3_CMD="$Z3_EXEC PROOF_MODE=2 -smt2"
	
	if grep "(get-proof)" $IN_FILE; then
		cp $IN_FILE $PRB_FILE
	else
		cat $IN_FILE | sed "s/(check-sat)/(check-sat) (get-proof)/"> $PRB_FILE
	fi
	
	$Z3_CMD $PRB_FILE | awk '
{
        proofLine=1
}

BEGIN { fileIdx=1; outFile=filePref fileIdx; anythingOutput=0; haveFirstRes=0}
{ proofLine=1 }
/^sat/ || /^unsat/ || /^\(error/ {
        if(anythingOutput) {
			exit
        }
        proofLine=0
        haveFirstRes=1
}
proofLine==1 && haveFirstRes==1 {
        print
        anythingOutput=1
} ' > $PROOF_FILE
		

	cat $PROOF_FILE | $VUTIL_EXEC zie $BLUE_ARG
	
	rm -r $AUX_DIR
}

function smt_z3_multiform_mode() {
	local AUX_DIR=`mktemp -d -t vint_XXXXXX`
	local PRB_FILE="$AUX_DIR/prb.smt2"
	local Z3_OUT="$AUX_DIR/z3_out.txt"
	local PROOF_FILE="$AUX_DIR/proof.txt"
	local Z3_CMD="$Z3_EXEC PROOF_MODE=2 -smt2"
		
	local LEFT_CNT=$1
	shift 1
		
	
	$VUTIL_EXEC sc $* | fold -w 200 -s >$PRB_FILE
	$Z3_CMD $PRB_FILE >$Z3_OUT 2>&1
	
	if ! grep -q "^unsat" $Z3_OUT; then
	        echo "unsuccessful z3 run"
	        echo
	        cat $Z3_OUT
	        rm -r $AUX_DIR
	        exit 1
	fi
	
	
	grep -v ^unsat $Z3_OUT >$PROOF_FILE
	
	cat $PROOF_FILE | $VUTIL_EXEC zie $BLUE_ARG $LEFT_CNT $*
	
	rm -r $AUX_DIR
}

function smt_vampire_multiform_mode() {
	local LEFT_CNT=$1
	shift 1
	
	local VAMP_ARGS="-proof off -sio off -sac on -predicate_definition_inlining non_growing -statistics none -smtlib_flet_as_definition on -t $TIME_LIMIT"
	local VUTIL_ARGS="cpa $# $LEFT_CNT $* $VAMP_ARGS"
	(ulimit -St $TIME_LIMIT; $VUTIL_EXEC $VUTIL_ARGS) | grep -v "Refutation found"
	if [ $? -eq 130 ]; then
	        echo interrupted
	        exit 130
	fi
}


parse_args $*

if [ "$TIME_LIMIT" -ne 0 ]; then
	ulimit -St $TIME_LIMIT
fi

ensure_exec_presence VUTIL_EXEC vutil
ensure_exec_presence Z3_EXEC z3
#ensure_exec_presence VAMPIRE_EXEC vampire

case "$MODE" in
tptp_vampire)
	tptp_vampire_mode $INP
	;;
smt_z3_range)
	smt_z3_range_mode $INP
	;;
z3_extract_proofs)
	z3_extract_proofs_mode $INP
	;;
z3_proof_range)
	z3_proof_range_mode $INP
	;;
smt_z3_multiform)
	smt_z3_multiform_mode $INP
	;;
smt_vampire_multiform)
	smt_vampire_multiform_mode $INP
	;;
*)
	rep_usage_error "mode not specified"
		;;
esac
	
