#!/bin/bash

# Before running this script, make sure set up the directory for storing the downloaded matrix files (ARCHIVE_LOCATION)
ARCHIVE_LOCATION=""
SSGET=./ssget

if [ ! "${BENCHMARK}" ]; then
    BENCHMARK="spmv"
    echo "BENCHMARK   environment variable not set - assuming \"${BENCHMARK}\"" 1>&2
fi

if [ ! "${DRY_RUN}" ]; then
    DRY_RUN="false"
    echo "DRY_RUN     environment variable not set - assuming \"${DRY_RUN}\"" 1>&2
fi

if [ ! "${EXECUTOR}" ]; then
    EXECUTOR="cuda"
    echo "EXECUTOR    environment variable not set - assuming \"${EXECUTOR}\"" 1>&2
fi

if [ ! "${SEGMENTS}" ]; then
    echo "SEGMENTS    environment variable not set - running entire suite" 1>&2
    SEGMENTS=1
    SEGMENT_ID=1
elif [ ! "${SEGMENT_ID}" ]; then
    echo "SEGMENT_ID  environment variable not set - exiting" 1>&2
    exit 1
fi

if [ ! "${FORMATS}" ]; then
    echo "FORMATS    environment variable not set - assuming \"csr\"" 1>&2
    FORMATS="csr"
fi

if [ ! "${DEVICE_ID}" ]; then
    DEVICE_ID="0"
    echo "DEVICE_ID   environment variable not set - assuming \"${DEVICE_ID}\"" 1>&2
fi

if [ ! "${SINGLE_JSON}" ]; then
    SINGLE_JSON="false"
    echo "SINGLE_JSON environment variable not set - assuming \"${SINGLE_JSONR}\"" 1>&2
fi

if [ ! "${LAUNCHER}" ]; then
    LAUNCHER=""
    echo "LAUNCHER    environment variable not set - assuming \"${LAUNCHER}\"" 1>&2
fi

# This allows using a matrix list file for benchmarking.
# The file should contains a suitesparse matrix on each line.
# The allowed formats to target suitesparse matrix is:
#   id or group/name or name.
# Example:
# 1903
# Freescale/circuit5M
# thermal2
if [ ! "${MATRIX_LIST_FILE}" ]; then
    use_matrix_list_file=0
elif [ -f "${MATRIX_LIST_FILE}" ]; then
    use_matrix_list_file=1
else
    echo -e "A matrix list file was set to ${MATRIX_LIST_FILE} but it cannot be found."
    exit 1
fi

# Runs the SpMV benchmarks for all SpMV formats by using file $1 as the input,
# and updating it with the results. Backups are created after each
# benchmark run, to prevent data loss in case of a crash. Once the benchmarking
# is completed, the backups and the results are combined, and the newest file is
# taken as the final result.
run_spmv_benchmarks() {
    [ "${DRY_RUN}" == "true" ] && return
    if [ "${EXECUTOR}" == "cuda" ] || [ "${EXECUTOR}" == "hip" ]; then
        ${LAUNCHER} ../mat/tests/bench_spmv -formats "${FORMATS}" -repetitions 5 -use_gpu -AJSON "$1"
    else
        ${LAUNCHER} ../mat/tests/bench_spmv -formats "${FORMATS}" -repetitions 5 -AJSON "$1"
    fi
}

NUM_PROBLEMS="$(${SSGET} -n)"

# Creates an input file for $1-th problem in the SuiteSparse collection
generate_suite_sparse_input() {
    INPUT=$(${SSGET} -i "$1" -e)
    cat << EOT
[{
    "filename": "${INPUT}",
    "problem": $(${SSGET} -i "$1" -j)
}]
EOT
}

# Append an input file for $1-th problem in the SuiteSparse collection
append_suite_sparse_input() {
    INPUT=$(${SSGET} -i "$1" -e)
    cat << EOT
 {
    "filename": "${INPUT}",
    "problem": $(${SSGET} -i "$1" -j)
 },
EOT
}

parse_matrix_list() {
    local source_list_file=$1
    local benchmark_list=""
    local id=0
    for mtx in $(cat "${source_list_file}"); do
	echo "$mtx" >&2
        if [[ ! "$mtx" =~ ^[0-9]+$ ]]; then
            if [[ "$mtx" =~ ^[a-zA-Z0-9_-]+$ ]]; then
                id=$(${SSGET} -s "[ @name == $mtx ]")
            elif [[ "$mtx" =~ ^([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_-]+)$ ]]; then
                local group="${BASH_REMATCH[1]}"
                local name="${BASH_REMATCH[2]}"
                id=$(${SSGET} -s "[ @name == $name ] && [ @group == $group ]")
            else
                >&2 echo -e "Could not recognize entry $mtx."
            fi
        else
            id=$mtx
        fi
        benchmark_list="$benchmark_list $id"
    done
    echo "$benchmark_list"
}

if [ $use_matrix_list_file -eq 1 ]; then
    MATRIX_LIST=($(parse_matrix_list "$MATRIX_LIST_FILE"))
    NUM_PROBLEMS=${#MATRIX_LIST[@]}
fi

RESULT_DIR="results/${SYSTEM_NAME}/${EXECUTOR}/SuiteSparse"
if [ "${SINGLE_JSON}" == "true" ]; then
    RESULT_FILE="${RESULT_DIR}/SEGMENT${SEGMENT_ID}.json"
    cat << EOT >"${RESULT_FILE}"
[
EOT
    mkdir -p "$(dirname "${RESULT_FILE}")"
fi
LOOP_START=$((1 + (${NUM_PROBLEMS}) * (${SEGMENT_ID} - 1) / ${SEGMENTS}))
LOOP_END=$((1 + (${NUM_PROBLEMS}) * (${SEGMENT_ID}) / ${SEGMENTS}))

for (( p=${LOOP_START}; p < ${LOOP_END}; ++p )); do
    if [ $use_matrix_list_file -eq 1 ]; then
        i=${MATRIX_LIST[$((p-1))]}
    else
        i=$p
    fi
    if [ "${BENCHMARK}" == "preconditioner" ]; then
        break
    fi
    if [ "$(${SSGET} -i "$i" -preal)" = "0" ] || [ "$(${SSGET} -i "$i" -pbinary)" = "1" ]; then
        [ "${DRY_RUN}" != "true" ] && ${SSGET} -i "$i" -c >/dev/null
        continue
    fi
    # filter matrices for spmv tests
    if [ "${BENCHMARK}" == "spmv" ]; then
	# deselect non-square matrices and matrices with more than 2B non zeros
        if [ "$(${SSGET} -i "$i" -pcols)" != "$(${SSGET} -i "$i" -prows)" ] || [ "$(${SSGET} -i "$i" -pnonzeros)" -gt 2000000000 ]; then
            [ "${DRY_RUN}" != "true" ] && ${SSGET} -i "$i" -c >/dev/null
            continue
	    fi
    fi
    PREFIX="(PROB$p/${NUM_PROBLEMS} ID$i SEG${SEGMENT_ID}):\t"
    GROUP=$(${SSGET} -i "$i" -pgroup)
    NAME=$(${SSGET} -i "$i" -pname)
    if [ "${SINGLE_JSON}" == "false" ]; then
        RESULT_FILE="${RESULT_DIR}/${GROUP}/${NAME}.json"
        mkdir -p "$(dirname "${RESULT_FILE}")"
        echo -e "${PREFIX}Extracting the matrix for ${GROUP}/${NAME}" 1>&2
        generate_suite_sparse_input "$i" >"${RESULT_FILE}"
        echo -e "${PREFIX}Running SpMV for ${GROUP}/${NAME}" 1>&2
        run_spmv_benchmarks "${RESULT_FILE}"
        # echo -e "${PREFIX}Cleaning up problem ${GROUP}/${NAME}" 1>&2
        # [ "${DRY_RUN}" != "true" ] && ${SSGET} -i "$i" -c >/dev/null
    else
        append_suite_sparse_input "$i" >>"${RESULT_FILE}"
    fi
done
if [ "${SINGLE_JSON}" == "true" ]; then
    cat << EOT >"${RESULT_FILE}"
]
EOT
    echo -e "${PREFIX}Running SpMV for SEG${SEGMENT_ID}" 1>&2
    run_spmv_benchmarks "${RESULT_FILE}"
    for (( p=${LOOP_START}; p < ${LOOP_END}; ++p )); do
        if [ $use_matrix_list_file -eq 1 ]; then
            i=${MATRIX_LIST[$((p-1))]}
        else
            i=$p
	fi
	echo -e "${PREFIX}Cleaning up problem ${i}" 1>&2
        [ "${DRY_RUN}" != "true" ] && ${SSGET} -i "$i" -c >/dev/null
    done
fi
