//  READ.C
//  Functions mainly concerned with data input
//  © 2021 Peter J. Meyer

#include "iss.h"

//  Parameters for the simulation are obtained from an input data file.
//  The command line requires only the file name.

static void read_lattice_type(void);
static int read_numeric_parameter_value(double *param, 
    double min_value, double max_value, char *param_name);
static int read_rational_numeric_parameter_value(double *param, 
    double min_value, double max_value, char *param_name);
static int read_alpha_parameter_value(char type[], 
    const char types[][2][30], char *type_name);
static int read_yn_parameter_value(void);

//  When run from the compiler argv[0] is the full path, but when run at
//  the command prompt argv[0] is just the first command line argument.
/*---------------------------*/
void get_exe_name(char *argv[])
{
char *ptr = strrchr(argv[0],'\\');
if ( ptr == NULL )
    strcpy(exe_name,argv[0]);
else
    strcpy(exe_name,++ptr);
_strupr(exe_name);
if ( strstr(exe_name,".EXE") == NULL )
    strcat(exe_name,".EXE");
}

//  Get name of input data file.
//  Construct names of output data file and of map file.
/*--------------------------------------*/
int read_command_line(int i, char *argv[])
{
strcpy(input_filepath,argv[i]);
strupr(input_filepath);

if ( strchr(input_filepath,'.') == NULL || 
         strcmp(".IN",strrchr(input_filepath,'.')) )
    {
    printf("\nInput data file = %s.  "
           "Name must have extension \".IN\".\n\n",input_filepath);
    return ( false );
    }

return ( true );
}

/*----------------------*/
void read_input_data(void)
{
int i;
char *ptr;
FILE *input_file;
double d;

//  Zero temperatures array
i = MAX_NUM_TEMPERATURES;
while ( i )
    temperatur[--i] = 0;

num_temperatures = 0;

if ( !open_file(input_filepath,"rt",&input_file) )
    display_file_error_message("open","input data",input_filepath); 
    //  Does not return.

while ( read_line(input_file) )
    {
    if ( !*line )                               //  if empty line
        continue;

    //  Get lattice type.

    if ( strstr(line,"lattice type:") == line )
        {    
        read_lattice_type();
        lattice_type_specified = true;
        //  Only one of the following will be true.
        honeycomb_lattice = !strcmp(lattice_type,"HON");
        square_lattice = !strcmp(lattice_type,"SQU");
        triangular_lattice = !strcmp(lattice_type,"TRI");
        double_triangular_lattice = !strcmp(lattice_type,"DTR");
        cubic_lattice = !strcmp(lattice_type,"CUB");
        diamond_lattice = !strcmp(lattice_type,"DIA");
        quadrilateral_lattice = !strcmp(lattice_type,"QUA");
        hypercubic_lattice_4d = !strcmp(lattice_type,"HC4");
        hyperdiamond_lattice_4d = !strcmp(lattice_type,"HD4");
        #if TETRAHEDRAL_LATTICE_IMPLEMENTED
        tetrahedral_lattice = !strcmp(lattice_type,"TET");
        #endif

        //  Set flag to show if lattice is such that direction table
        //  depends on parity of site location.
        direction_table_is_parity_dependent = 
            honeycomb_lattice || diamond_lattice || hyperdiamond_lattice_4d;
        }

    //  Get numerical parameters.

    else if ( strstr(line,"lattice size:") == line )
        {
        //  Read size.
        if ( read_numeric_parameter_value(&d,MIN_SIZE,MAX_SIZE,"lattice size") )
            {
            size = (int)d;
            size_specified = true;
            }
        }

    else if ( strstr(line,"q-value:") == line )
        {
        //  Read q-value.
        if ( read_numeric_parameter_value(&d,MIN_Q_VALUE,MAX_Q_VALUE,"q-value") )
            {
            q = (int)d;
            q_value_specified = true;
            }
        }

    else if ( strstr(line,"site concentration:") == line )
        {
        //  Read site concentration
        if ( read_numeric_parameter_value(&site_concentration,
            MIN_CONCENTRATION,MAX_CONCENTRATION,"site concentration") )
            site_concentration_specified = true;
        }

    else if ( strstr(line,"bond concentration:") == line )
        {
        //  Read bond concentration
        if ( read_numeric_parameter_value(&bond_concentration,
            MIN_CONCENTRATION,MAX_CONCENTRATION,"bond concentration") )
            bond_concentration_specified = true;
        }

    else if ( strstr(line,"temperature:") == line 
            || strstr(line,"temperatures:") == line )
        {
        //  Read one or more temperatures.
        strcpy(temp,strchr(line,':')+1);
        _strlwr(temp);
        i = num_temperatures;
        loop
            {
            remove_leading_spaces(temp);            
            use_critical_temperature = !memcmp(temp,"critical",8);
            if ( use_critical_temperature )
                temperatur[i++] = -1; 
            else
                {
                if ( !*temp )
                    break;
                if ( !strchr("0123456789+-.",temp[0]) )
                    display_input_data_error("temperature");    //  Does not return.
                d = atof(temp);        
                if ( d < MIN_TEMPERATURE || d > MAX_TEMPERATURE )
                    display_input_data_error("temperature");    //  Does not return.
                temperatur[i++] = d;
                }
            if ( ( ptr = strchr(temp,',') ) == NULL )
                break;      //  no more temperatures
            strcpy(temp1,temp);
            ptr = strchr(temp1,',') + 1;
            strcpy(temp,ptr);
            }
        num_temperatures = i;
        }

    else if ( strstr(line,"number of configurations:") == line )
        {
        //  Read number of configurations.
        if ( read_numeric_parameter_value(&d,1,
            MAX_NUM_CONFIGURATIONS,"number of configurations") )
            {
            num_configurations = (int)d;
            num_configurations_specified = true;
            }
        }

    else if ( strstr(line,"number of spin assignments:") == line )
        {
        //  Read number of configurations.
        if ( read_numeric_parameter_value(&d,1,
            MAX_NUM_SPIN_ASSIGNMENTS,"number of spin assignments") )
            {
            num_spin_assignments = (int)d;
            num_spin_assignments_specified = true;
            }
        }

    else if ( strstr(line,"number of repetitions:") == line )
        {
        //  Read number of repetitions
        if ( read_numeric_parameter_value(&d,1,
            MAX_NUM_REPETITIONS,"number of repetitions") )
            {
            num_repetitions = (int)d;
            num_repetitions_specified = true;
            }
        }

    else if ( strstr(line,"number of time slices:") == line )
        {
        //  Read number of time slices.
        if ( read_numeric_parameter_value(&d,MIN_NUM_TIME_SLICES,
            MAX_NUM_TIME_SLICES,"number of time slices") )
            {
            num_time_slices = (int)d;
            num_time_slices_specified = true;
            }
        }

    else if ( strstr(line,"step length:") == line )
        {
        //  Read step length (mcs between time slices).
        if ( read_numeric_parameter_value(&d,1,
            MAX_STEP_LENGTH,"step length") )
            {
            step_length = (int)d;
            step_length_specified = true;
            }
        }

    else if ( strstr(line,"percentage range for mean:") == line )
        {
        //  Read percentage range for mean magnetization, internal energy, specific heat.
        if ( read_numeric_parameter_value(&d,MIN_PERCENTAGE_RANGE_FOR_MEAN,
            MAX_PERCENTAGE_RANGE_FOR_MEAN,"percentage range for mean") )
            {
            percentage_range_for_mean = (int)d;
            percentage_range_for_mean_specified = true;
            }
        }

    else if ( strstr(line,"initial magnetization:") == line )
        {
        //  Read initial magnetization.
        if ( read_rational_numeric_parameter_value(&initial_magnetization,
            -1.0,+1.0,"initial magnetization") )
            initial_magnetization_specified = true;
        }

    else if ( strstr(line,"precision:") == line )
        {
        //  Read number of decimal places in required precision for 
        //  determination of percolation threshold.
        if ( read_numeric_parameter_value(&d,
            MIN_PRECISION,MAX_PRECISION,"precision") )
            {
            num_decimal_places = (int)d;
            precision_specified = true;
            }
        }

    else if ( strstr(line,"major axis:") == line )
        {
        //  Read major axis.
        if ( read_numeric_parameter_value(&d,
            MIN_MAJOR_AXIS,MAX_MAJOR_AXIS,"major axis") )
            {
            major_axis = (int)d;
            major_axis_specified = true;
            }
        }

    //  Read textual parameters.

    #if DOES_PERCOLATION_THRESHOLDS
    else if ( strstr(line,"goal:") == line )
        {
        g_type = read_alpha_parameter_value(goal, goals, "goal");
        goal_specified = true;
        goal_is_equilibration = ( !strcmp(goal,"SP") || !strcmp(goal,"EQ") );
        goal_is_site_percolation_threshold = !strcmp(goal,"SI");
        goal_is_bond_percolation_threshold = !strcmp(goal,"BO");

        goal_is_percolation_threshold = 
            goal_is_site_percolation_threshold || goal_is_bond_percolation_threshold;
        }
    //  else goal_is_equilibration set to true in SETPARAM.C
    #endif

    else if ( strstr(line,"model:") == line )
        {
        m_type = read_alpha_parameter_value(model,models,"model");
        model_specified = true;
        //  Only one of the following three will be true.
        model_is_ising = !strcmp(model,"IS");
        model_is_q_state_potts = !strcmp(model,"Q-");
        }

    else if ( strstr(line,"dynamics:") == line )
        {
        d_type = read_alpha_parameter_value(dynamics, dynamics_types,"dynamics");
        dynamics_specified = true;
        dynamics_is_metropolis =    !strcmp(dynamics,"ME");
        dynamics_is_glauber =       !strcmp(dynamics,"GL");
        dynamics_is_swendsen_wang = !strcmp(dynamics,"SW");
        dynamics_is_wolff         = !strcmp(dynamics,"WO");
        }

    #if ALTERNATIVE_SPIN_SELECTIONS
    else if ( strstr(line,"spin selection:") == line )
        {
        ss_type = read_alpha_parameter_value(spin_seln, spin_selns, "spin selections");
        spin_seln_specified = true;
        spin_seln_is_random = !strcmp(spin_seln,"RA");
        spin_seln_is_checkerboard = !strcmp(spin_seln,"CH");
        }
    #endif

    //  Read Y/N parameters.

    else if ( strstr(line,"absolute magnetization:") == line )
        {
        absolute_magnetization_requested = read_yn_parameter_value();
        absolute_magnetization_specified = true;
        }

    else if ( strstr(line,"precompute nn sites:") == line )
        //  Read if nn sites to be precomputed
        {
        use_precomputed_nn_sites = read_yn_parameter_value();
        use_precomputed_nn_sites_specified = true;
        }

    else if ( strstr(line,"timeslice values:") == line )
        //  Read if timeslice values to be written to file
        {
        timeslice_values_requested = read_yn_parameter_value();
        timeslice_values_specified = true;
        }

    else if ( strstr(line,"adjust zero initial magnetization:") == line )
        adjust_zero_initial_magnetization = read_yn_parameter_value();

    else if ( strstr(line,"second moment:") == line )
        //  Read if second moment is to be measured.
        second_moment_measured = read_yn_parameter_value();

    else if ( strstr(line,"autocorrelation:") == line )
        //  Read if autocorrelation is to be measured.
        autocorrelation_measured = read_yn_parameter_value();

    else if ( strstr(line,"internal energy:") == line )
        //  Read if internal energy is to be measured.
        internal_energy_measured = read_yn_parameter_value();

    else if ( strstr(line,"map file:") == line )
        //  Read if map file requested.
        map_file_requested = read_yn_parameter_value();

    else if ( strstr(line,"log values:") == line )
        //  Read if log values to be calculated.
        log_values_requested = read_yn_parameter_value();

    else if ( strstr(line,"binder cumulant:") == line )
        //  Read if Binder cumulant to be measured.;
        binder_cumulant_measured = read_yn_parameter_value();

    #if false
    else if ( strstr(line,"percolation correlation length:") == line )
        //  Read if percolation correlation length to be measured.
        percolation_corr_length_measured = read_yn_parameter_value();

    else if ( strstr(line,"thermal correlation length A:") == line )
        //  Read if thermal correlation length A to be measured.;
        thermal_corr_length_a_measured = read_yn_parameter_value();

    else if ( strstr(line,"thermal correlation length B:") == line )
        //  Read if thermal correlation length B to be measured.;
        thermal_corr_length_b_measured = read_yn_parameter_value();
    #endif

    //  All other lines ignored.
    }

fclose(input_file);
}

/*-------------------------------*/
static void read_lattice_type(void)
{
strcpy(temp,strchr(line,':')+1);
remove_leading_spaces(temp);
memcpy(lattice_type,temp,3);
lattice_type[3] = 0;
_strupr(lattice_type);

if ( !strcmp(lattice_type,"SQ") )
    strcpy(lattice_type,"SQU");

for ( l_type=0; l_type<NUM_LATTICE_TYPES; l_type++ )
    {
    if ( !strcmp(lattice_types[l_type].id,lattice_type) )
        break;
    }

if ( l_type == NUM_LATTICE_TYPES )
    display_input_data_error("lattice type");  //  Does not return.
}

/*--------------------------------------------------*/
static int read_numeric_parameter_value(double *param, 
    double min_value, double max_value, char *param_name)
{
strcpy(temp,strchr(line,':')+1);
remove_leading_spaces(temp);
if ( !*temp )
    return ( false );
else
    {
    *param = atof(temp);        
    if ( *param < min_value || *param > max_value )
        display_input_data_error(param_name);  //  Does not return.
    return ( true );
    }
}

/*-----------------------------------------------------------*/
static int read_rational_numeric_parameter_value(double *param, 
    double min_value, double max_value, char *param_name)
{
int n, d;

strcpy(temp,strchr(line,':')+1);
remove_leading_spaces(temp);
if ( !*temp )
    return ( false );
else
    {
    if ( strchr(temp,'/') == NULL )
        *param = atof(temp);       
    else
        {
        n = atoi(temp);
        d = atoi(1+strchr(temp,'/'));
        if ( d == 0 )
            return ( false );
        *param = (double)n/d;
        } 
    if ( *param < min_value || *param > max_value )
        display_input_data_error(param_name);  //  Does not return.
    return ( true );
    }
}

/*----------------------------------------------*/
static int read_alpha_parameter_value(char type[], 
                                      const char types[][2][30], 
                                      char *type_name)
{
int index;

strcpy(temp,strchr(line,':')+1);
remove_leading_spaces(temp);
memcpy(type,temp,2);
type[2] = 0;
_strupr(type);
index = -1;
do  {
    if ( !types[++index][1][0] )
        display_input_data_error(type_name);    //  Does not return.
    } while ( strcmp(types[index][0],type) );

return ( index );
}

//  
/*------------------------------------*/
static int read_yn_parameter_value(void)
{
int ch;

strcpy(temp,strchr(line,':')+1);
remove_leading_spaces(temp);
_strupr(temp);
ch = temp[0];
if ( ! ( ch == 'Y' || ch == 'N' || ch == 0 ) )
    {
    *strchr(line,':') = 0;
    display_input_data_error(line);    //  Does not return.
    }

return ( ch == 'Y' );
}