#!/usr/bin/bash

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# PALM -- Permutation Analysis of Linear Models
# Copyright (C) 2015 Anderson M. Winkler
# FMRIB / Univ. of Oxford
# Mar/2014
# http://brainder.org
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# This is a wrapper to call PALM in Octave or Matlab, depending
# on the user's preferences and what is available in the system.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# =========================================================================
# Do you prefer Matlab or Octave? Octave is free (it doesn't need licences),
# is faster to load, but somewhat slower to run. Matlab uses licenses (this
# can be expensive, or may bother other users in a network if the number of
# licenses available is limited), is slower to load, but runs faster
# once loaded. In the variable below, use 1 for Octave, 2 for Matlab:
WHICH_TO_RUN=1

# If Octave isn't in the path, or to use a specific version, add here the
# path or command that invokes the Octave executable binary.
# This only has effect if WHICH_TO_RUN above os set as 1.
#OCTAVEBIN=/usr/bin/octave
OCTAVEBIN=/usr/bin/octave
#OCTAVEBIN="/usr/bin/flatpak run org.octave.Octave"

# If Matlab isn't in the path, or to use a specific version, add here the
# path or command that invokes the Matlab executable binary.
# This only has effect if WHICH_TO_RUN above os set as 2.
MATLABBIN=/opt/r16b/bin/matlab
#MATLABBIN=/Applications/MATLAB_R2023a.app/bin/matlab

# If you are intending to run PALM in a cluster, for some environments
# it may be necessary to change the variable below to 1.
# In this case, also, provide the path to a temporary directory where
# a tiny temporary file can be saved.
IS_CLUSTER=0
TEMP_DIR=/tmp

# If you are using MATLAB and wish to use more than one compute thread
# then set this to 1 and ensure that OMP_NUM_THREADS is set appropriately
# e.g. set to the value of SLURM_NPROCS
# If OMP_NUM_THREADS is not set then default to MATLAB's 'automatic' 
# determination of number of threads.
USE_OMP_THREADS=0
if [[ ${USE_OMP_THREADS} -eq 1 ]] && [[ -z ${OMP_NUM_THREADS} ]]; then
  OMP_NUM_THREADS="'automatic'"
fi

# =========================================================================
# Normally there is no need to edit below this line:

# Locate PALM
cmdfull=${BASH_SOURCE[0]}
PALMDIR=$(dirname ${cmdfull})
rlink=$(readlink ${cmdfull})
cnt=1
while [[ ${rlink} != "" ]] && [[ ${cnt} -lt 100 ]]; do
  dirnam=$(dirname ${rlink})
  if [[ ${dirnam:0} == "/" ]] ; then
    PALMDIR=${dirnam}
  else
    PALMDIR="${PALMDIR}/${dirnam}"
  fi
  rlink=$(readlink ${rlink})
  cnt=$(expr ${cnt} + 1)
done
if [[ ${cnt} -eq 100 ]] ; then
  echo "Error: Too many levels of symbolic links."
  exit 1
fi

# Command to run inside either Octave or Matlab. It varies slightly
# depending whether it's a cluster environment or not.
if [[ ${WHICH_TO_RUN} -eq 2 ]] && [[ ${USE_OMP_THREADS} -eq 1 ]]; then
  RUNCMD="LASTN = maxNumCompThreads(${OMP_NUM_THREADS});"
fi
RUNCMD="$RUNCMD addpath('$PALMDIR'); try palm ${1+"$@"} ; catch ME, palm_error(ME), exit(1), end; exit(0)"
if [[ ${IS_CLUSTER} -ne 0 ]] ; then
   TEMP_NAME=$(tr -cd '[:alnum:]' < /dev/urandom | fold -w10 | head -n 1)
   echo ${RUNCMD} > ${TEMP_DIR}/palm_temp_${TEMP_NAME}.m
   RUNCMD="${TEMP_DIR}/palm_temp_${TEMP_NAME}.m"
fi

# Call either Octave or Matlab depending on the user's choice.
set -o pipefail
if [[ ${WHICH_TO_RUN} -eq 1 ]] ; then
   if [[ ${OCTAVEBIN} == "" ]] || [[ ! -e $(echo ${OCTAVEBIN} | awk '{print $1}') ]] ; then
      OCTAVECMD=octave-cli
   else
      OCTAVECMD=${OCTAVEBIN}
   fi
   minver=$(${OCTAVECMD} --version | awk 'NR==1 {print "4.0.0\n" $NF}' | sort -V | head -n 1)
   if [[ "${minver}" != "4.0.0" ]] ; then
      # Simpler version with no error catching, for Octave < 4.0.0. This will always end
      # with a success status, even if there was a crash within the code. The user can still
      # for success should they need by verifying that the *_elapsed.csv was created.
      RUNCMD="addpath('$PALMDIR'); palm ${1+"$@"} ; exit"
   fi
   OT_OPTS="-q --no-window-system"
   ${OCTAVECMD} ${OT_OPTS} --eval "${RUNCMD}"
   status=$?
elif [[ ${WHICH_TO_RUN} -eq 2 ]] ; then
   if [[ ${MATLABBIN} == "" ]] || [[ ! -e ${MATLABBIN} ]]; then
      MATLABCMD=matlab
   else
      MATLABCMD=${MATLABBIN}
   fi
   if   [[ "$(uname)" == "Darwin" ]] ; then
      SEDOPT="-l"
   elif [[ "$(uname)" == "Linux"  ]] ; then
      SEDOPT="-u"
   else
      SEDOPT=""
   fi
   ML_OPTS="-nodesktop -nosplash"
   if [[ ${USE_OMP_THREADS} -ne 1 ]]; then
     ML_OPTS="${ML_OPTS} -singleCompThread"
   fi
   SED_STR='1,/^.......................................................................$/ d'
   if [[ ${IS_CLUSTER} -eq 0 ]] ; then
     ${MATLABCMD} ${ML_OPTS} -r "${RUNCMD}" | sed ${SEDOPT} -e "$SED_STR"
   else
     ${MATLABCMD} ${ML_OPTS} < "${RUNCMD}" | sed ${SEDOPT} -e "$SED_STR"
   fi
   status=$?
fi

# If a temp file was created earlier, delete it now and exit.
rm -rf ${TEMP_DIR}/palm_temp_${TEMP_NAME}.m
exit ${status}

# That's it!
