#!/bin/sh

# configure: probe BLAS/LAPACK for single- and double-precision procedures
# used by Armadillo and generate src/Makevars with the appropriate build flags.
#
# Procedures checked (Fortran calling convention):
#   single-precision BLAS:   sgemm_, sdot_
#   single-precision LAPACK: sgesv_
#   double-precision BLAS:   dgemm_, ddot_
#   double-precision LAPACK: dgesv_
#
# Scalar type selection:
#   single-precision routines available -> SCALAR = float
#   otherwise                           -> SCALAR = double  (-DHARMONY_SCALAR_DOUBLE)
#
# Armadillo BLAS/LAPACK flags are then set for the chosen precision:
#   ARMA_DONT_USE_BLAS   -- added when the chosen-precision BLAS is absent
#   ARMA_DONT_USE_LAPACK -- added when the chosen-precision LAPACK is absent

PKG_ROOT="$(cd "$(dirname "$0")"; pwd)"

# Retrieve R build configuration
BLAS_LIBS=`"${R_HOME}/bin/R" CMD config BLAS_LIBS`
LAPACK_LIBS=`"${R_HOME}/bin/R" CMD config LAPACK_LIBS`
FLIBS=`"${R_HOME}/bin/R" CMD config FLIBS`
CC=`"${R_HOME}/bin/R" CMD config CC`
CFLAGS=`"${R_HOME}/bin/R" CMD config CFLAGS`

PKG_CXXFLAGS="-DARMA_USE_CURRENT"
PKG_LIBS="${LAPACK_LIBS} ${BLAS_LIBS} ${FLIBS}"

## Uncomment to include openmp parallelization
# PKG_CXXFLAGS="${PKG_CXXFLAGS} $(SHLIB_OPENMP_CXXFLAGS)"


# Temporary directory for test programs
CONFTMP="${TMPDIR:-/tmp}/harmony_configure_$$"
mkdir -p "${CONFTMP}"

# ---------------------------------------------------------------------------
# Check single-precision BLAS: sgemm_, sdot_
# ---------------------------------------------------------------------------
echo "checking for single-precision BLAS procedures (sdot_, sgemm_)..."

cat > "${CONFTMP}/single_blas_test.c" << 'SINGLE_BLAS_EOF'
extern void sgemm_(char*, char*, int*, int*, int*,
                   float*, float*, int*, float*, int*,
                   float*, float*, int*);

extern float sdot_(
             const int* n, const float* x, const int* incx,
             const float* y, const int* incy);

int main(void) {
    /* dummy calls so the linker must resolve each symbol */
    /* this will segfault at runtime */
    float A = 0.0f, B = 0.0f, C = 0.0f;
    float sc = 0.0f;
    int dim = 1;
    char trA = 'N', trB = 'U';
    sgemm_(&trA, &trB, &dim, &dim, &dim, &sc, &A, 0, &B, &dim, &sc, &C, &dim);
    sdot_(&dim, &A, &dim, &B, &dim);
    return 0;
}
SINGLE_BLAS_EOF

SINGLE_BLAS_OK=no
if ${CC} ${CFLAGS} -o "${CONFTMP}/single_blas_test" "${CONFTMP}/single_blas_test.c" \
       ${BLAS_LIBS} ${FLIBS} 2>/dev/null; then
    SINGLE_BLAS_OK=yes
fi

if [ "${SINGLE_BLAS_OK}" = "yes" ]; then
    echo "  single-precision BLAS found"
else
    echo "  single-precision BLAS NOT found"
fi

# ---------------------------------------------------------------------------
# Check single-precision LAPACK: sgesv_
# ---------------------------------------------------------------------------
echo "checking for single-precision LAPACK procedures (sgesv_)..."

cat > "${CONFTMP}/single_lapack_test.c" << 'SINGLE_LAPACK_EOF'
extern void sgesv_(int*, int*, float*, int*, int*, float*, int*, int*);

int main(void) {
    float A = 0.0f, B = 0.0f;
    int dim = 1, res = 0;
    sgesv_(&dim, &dim, &A, &dim, &dim, &B, &dim, &res);
    return 0;
}
SINGLE_LAPACK_EOF

SINGLE_LAPACK_OK=no
if ${CC} ${CFLAGS} -o "${CONFTMP}/single_lapack_test" "${CONFTMP}/single_lapack_test.c" \
       ${LAPACK_LIBS} ${BLAS_LIBS} ${FLIBS} 2>/dev/null; then
    SINGLE_LAPACK_OK=yes
fi

if [ "${SINGLE_LAPACK_OK}" = "yes" ]; then
    echo "  single-precision LAPACK found"
else
    echo "  single-precision LAPACK NOT found"
fi

# ---------------------------------------------------------------------------
# Check double-precision BLAS: dgemm_, ddot_
# ---------------------------------------------------------------------------
echo "checking for double-precision BLAS procedures (ddot_, dgemm_)..."

cat > "${CONFTMP}/double_blas_test.c" << 'DOUBLE_BLAS_EOF'
extern void dgemm_(char*, char*, int*, int*, int*,
                   double*, double*, int*, double*, int*,
                   double*, double*, int*);

extern double ddot_(
               const int* n, const double* x, const int* incx,
               const double* y, const int* incy);

int main(void) {
    /* dummy calls so the linker must resolve each symbol */
    /* this will segfault at runtime */
    double A = 0.0, B = 0.0, C = 0.0;
    double sc = 0.0;
    int dim = 1;
    char trA = 'N', trB = 'U';
    dgemm_(&trA, &trB, &dim, &dim, &dim, &sc, &A, 0, &B, &dim, &sc, &C, &dim);
    ddot_(&dim, &A, &dim, &B, &dim);
    return 0;
}
DOUBLE_BLAS_EOF

DOUBLE_BLAS_OK=no
if ${CC} ${CFLAGS} -o "${CONFTMP}/double_blas_test" "${CONFTMP}/double_blas_test.c" \
       ${BLAS_LIBS} ${FLIBS} 2>/dev/null; then
    DOUBLE_BLAS_OK=yes
fi

if [ "${DOUBLE_BLAS_OK}" = "yes" ]; then
    echo "  double-precision BLAS found"
else
    echo "  double-precision BLAS NOT found"
fi

# ---------------------------------------------------------------------------
# Check double-precision LAPACK: dgesv_
# ---------------------------------------------------------------------------
echo "checking for double-precision LAPACK procedures (dgesv_)..."

cat > "${CONFTMP}/double_lapack_test.c" << 'DOUBLE_LAPACK_EOF'
extern void dgesv_(int*, int*, double*, int*, int*, double*, int*, int*);

int main(void) {
    double A = 0.0, B = 0.0;
    int dim = 1, res = 0;
    dgesv_(&dim, &dim, &A, &dim, &dim, &B, &dim, &res);
    return 0;
}
DOUBLE_LAPACK_EOF

DOUBLE_LAPACK_OK=no
if ${CC} ${CFLAGS} -o "${CONFTMP}/double_lapack_test" "${CONFTMP}/double_lapack_test.c" \
       ${LAPACK_LIBS} ${BLAS_LIBS} ${FLIBS} 2>/dev/null; then
    DOUBLE_LAPACK_OK=yes
fi

if [ "${DOUBLE_LAPACK_OK}" = "yes" ]; then
    echo "  double-precision LAPACK found"
else
    echo "  double-precision LAPACK NOT found"
fi

# ---------------------------------------------------------------------------
# Select scalar precision and set Armadillo flags accordingly
# ---------------------------------------------------------------------------
if [ "${SINGLE_BLAS_OK}" = "yes" ] && [ "${SINGLE_LAPACK_OK}" = "yes" ]; then
    echo "  single-precision routines fully available -- using float scalar"
    USE_DOUBLE=no
else
    echo "  single-precision routines not fully available -- using double scalar"
    USE_DOUBLE=yes
    PKG_CXXFLAGS="${PKG_CXXFLAGS} -DHARMONY_SCALAR_DOUBLE"
fi

if [ "${USE_DOUBLE}" = "no" ]; then
    # float path: Armadillo needs single-precision BLAS/LAPACK
    if [ "${SINGLE_BLAS_OK}" = "no" ]; then
        echo "  disabling BLAS for Armadillo (single-precision routines absent)"
        PKG_CXXFLAGS="${PKG_CXXFLAGS} -DARMA_DONT_USE_BLAS"
    fi
    if [ "${SINGLE_LAPACK_OK}" = "no" ]; then
        echo "  disabling LAPACK for Armadillo (single-precision routines absent)"
        PKG_CXXFLAGS="${PKG_CXXFLAGS} -DARMA_DONT_USE_LAPACK"
    fi
else
    # double path: Armadillo needs double-precision BLAS/LAPACK
    if [ "${DOUBLE_BLAS_OK}" = "no" ]; then
        echo "  disabling BLAS for Armadillo (double-precision routines absent)"
        PKG_CXXFLAGS="${PKG_CXXFLAGS} -DARMA_DONT_USE_BLAS"
    fi
    if [ "${DOUBLE_LAPACK_OK}" = "no" ]; then
        echo "  disabling LAPACK for Armadillo (double-precision routines absent)"
        PKG_CXXFLAGS="${PKG_CXXFLAGS} -DARMA_DONT_USE_LAPACK"
    fi
fi

# ---------------------------------------------------------------------------
# Clean up and generate src/Makevars
# ---------------------------------------------------------------------------
rm -rf "${CONFTMP}"

sed -e "s|@PKG_CXXFLAGS@|${PKG_CXXFLAGS}|g" \
    -e "s|@PKG_LIBS@|${PKG_LIBS}|g" \
    "${PKG_ROOT}/src/Makevars.in" > "${PKG_ROOT}/src/Makevars"

echo "configure: src/Makevars written"
echo "  PKG_CXXFLAGS = ${PKG_CXXFLAGS}"
echo "  PKG_LIBS     = ${PKG_LIBS}"
