#!/bin/bash
# Copyright (c) 1998 Lawrence Livermore National Security, LLC and other
# HYPRE Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

# Generate multiprecision code
#
# The script takes an external header file, an internal header file, and an
# output file prefix, then generates various C files and header files.
#
# The generated files begin with the prefix name and contain code corresponding
# to the functions listed in mup.fixed, mup.functions, and mup.methods (methods
# are not fully implemented yet - see NOTE below).
#
# Usage:   <this-script> <external-header> <internal-header> <prefix>
# Example: <this-script> HYPRE_krylov.h _hypre_krylov.h mup

scriptdir=`dirname $0`

EXTH=$1
INTH=$2
OUTP=$3

#### NOTE: Add mup.methods to this later.
####
#### It should be easy to just add it to the loops below, but some up-front
#### preprocessing will be necessary to move existing implementation code out of
#### the way temporarily to prevent overwriting existing code.  Developers will
#### then need to do a manual merge.  The header files can be generated fully
#### automatically as below.

############################################################################
# Make sure the function list files are sorted (list capital letters first)
############################################################################

for i in fixed functions methods
do
    (export LC_COLLATE=C; sort mup.${i} | uniq) > mup.${i}.tmp
    mv mup.${i}.tmp mup.${i}
done

############################################################################
# Generate def/undef header files
############################################################################

extp=${EXTH%.*}  # extract the prefix from the external header
intp=${INTH%.*}  # extract the prefix from the internal header

# strip off leading 'HYPRE_' or '_hypre_' and convert to uppercase
extname=`echo $extp | sed -e 's/HYPRE_//g' -e 's/_hypre_//g' | awk '{print toupper($0)}'`
intname=`echo $intp | sed -e 's/HYPRE_//g' -e 's/_hypre_//g' | awk '{print toupper($0)}'`

#===========================================================================
# Create def header file
#===========================================================================

MUP_HEADER=${intp}_mup_def.h

cat > $MUP_HEADER <<@

/*** DO NOT EDIT THIS FILE DIRECTLY (use $0 to generate) ***/

@

# Generate copyright header
$scriptdir/write_header.sh >> $MUP_HEADER

cat >> $MUP_HEADER <<@
/* Header file for transforming multiprecision functions names */

#ifndef hypre_${intname}_MUP_DEF_HEADER
#define hypre_${intname}_MUP_DEF_HEADER

#include "_hypre_mup_def.h"

@

cat>> $MUP_HEADER <<@
$(
cat mup.functions mup.methods | while read -r func_name
do
   echo "#define $func_name HYPRE_MULTIPRECISION_FUNC ( $func_name )"
done
cat mup.fixed | while read -r func_name
do
   echo "#define $func_name HYPRE_FIXEDPRECISION_FUNC ( $func_name )"
done
)

#endif
@

#===========================================================================
# Exit if we only need the def header file
#===========================================================================

if [ "${OUTP}" = "onlydef" ]
then
    exit
fi

#===========================================================================
# Create undef header file
#===========================================================================

MUP_HEADER=${intp}_mup_undef.h

cat > $MUP_HEADER <<@

/*** DO NOT EDIT THIS FILE DIRECTLY (use $0 to generate) ***/

@

# Generate copyright header
$scriptdir/write_header.sh >> $MUP_HEADER

cat >> $MUP_HEADER <<@
/* Header file for transforming multiprecision functions names */

#undef hypre_${intname}_MUP_DEF_HEADER

@

cat>> $MUP_HEADER <<@
$(
cat mup.functions mup.methods mup.fixed | while read -r func_name
do
   echo "#undef $func_name"
done
)
@

############################################################################
# Create temporary files and initial code
############################################################################

# Create file with list of MUP functions and methods
cat mup.functions mup.methods > mup.pre

# Create prototype information files
for i in fixed functions
do
    $scriptdir/gen_proto_info.sh mup.${i} ${EXTH} > ${OUTP}.${i}.ext.proto
    $scriptdir/gen_proto_info.sh mup.${i} ${INTH} > ${OUTP}.${i}.int.proto
done
cat ${OUTP}.functions.ext.proto > ${OUTP}.pre.ext.proto
cat ${OUTP}.functions.int.proto > ${OUTP}.pre.int.proto

# Create C implementation files and header files
for i in fixed functions pre
do
    $scriptdir/gen_${i}_code.sh ${OUTP}.${i}.ext.proto ${OUTP}_${i}_ext
    $scriptdir/gen_${i}_code.sh ${OUTP}.${i}.int.proto ${OUTP}_${i}_int
done

############################################################################
# Finalize code
############################################################################

for i in fixed functions pre
do

#========================================
# generate implementation file
#========================================

FOUT=${OUTP}_${i}.c

cat > $FOUT <<@

/*** DO NOT EDIT THIS FILE DIRECTLY (use $0 to generate) ***/

#include "$INTH"

#ifdef HYPRE_MIXED_PRECISION

@

$scriptdir/write_header.sh >> $FOUT
cat ${OUTP}_${i}_ext.c ${OUTP}_${i}_int.c >> $FOUT

cat >> $FOUT <<@

#endif

@

#========================================
# add header info to the prototype files
#========================================

FOUT=${OUTP}_${i}_ext.h
cat > $FOUT.tmp <<@

/*** DO NOT EDIT THIS FILE DIRECTLY (use $0 to generate) ***/

@
$scriptdir/write_header.sh >> $FOUT.tmp
cat $FOUT >> $FOUT.tmp
mv $FOUT.tmp $FOUT

FOUT=${OUTP}_${i}_int.h
cat > $FOUT.tmp <<@

/*** DO NOT EDIT THIS FILE DIRECTLY (use $0 to generate) ***/

@
$scriptdir/write_header.sh >> $FOUT.tmp
cat $FOUT >> $FOUT.tmp
mv $FOUT.tmp $FOUT

done

############################################################################
# Remove temporary files
############################################################################

rm -f mup.pre
rm -f ${OUTP}.*.proto
rm -f ${OUTP}_*_ext.c
rm -f ${OUTP}_*_int.c

############################################################################
# Generate header files
############################################################################

#===========================================================================
# Create external header file
#===========================================================================

MUP_HEADER=${extp}_mup.h

cat > $MUP_HEADER <<@

/*** DO NOT EDIT THIS FILE DIRECTLY (use $0 to generate) ***/

#ifndef HYPRE_${extname}_MUP_HEADER
#define HYPRE_${extname}_MUP_HEADER

#ifdef __cplusplus
extern "C" {
#endif

#if defined (HYPRE_MIXED_PRECISION)
@

cat ${OUTP}_fixed_ext.h      >> $MUP_HEADER
cat ${OUTP}_functions_ext.h  >> $MUP_HEADER
cat ${OUTP}_pre_ext.h        >> $MUP_HEADER

cat >> $MUP_HEADER <<@

#endif

#ifdef __cplusplus
}
#endif

#endif

@

rm -f ${OUTP}_fixed_ext.h ${OUTP}_functions_ext.h ${OUTP}_pre_ext.h

#===========================================================================
# Create internal header file
#===========================================================================

MUP_HEADER=${intp}_mup.h

cat > $MUP_HEADER <<@

/*** DO NOT EDIT THIS FILE DIRECTLY (use $0 to generate) ***/

#ifndef hypre_${intname}_MUP_HEADER
#define hypre_${intname}_MUP_HEADER

#ifdef __cplusplus
extern "C" {
#endif

#if defined (HYPRE_MIXED_PRECISION)
@

cat ${OUTP}_fixed_int.h      >> $MUP_HEADER
cat ${OUTP}_functions_int.h  >> $MUP_HEADER
cat ${OUTP}_pre_int.h        >> $MUP_HEADER

cat >> $MUP_HEADER <<@

#endif

#ifdef __cplusplus
}
#endif

#endif

@

rm -f ${OUTP}_fixed_int.h ${OUTP}_functions_int.h ${OUTP}_pre_int.h
