#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "constants.h"
#include "simfold.h"
#include "common.h"
#include "externs.h"
#include "params.h"

//#define MAXSLEN 4000
#define ABERANT 99999

void my_strcpy (char *source, char *dest, int from, int len)
{
  int i;
  for (i=0; i < len; i++)
    {
      dest[i] = source[from+i];
    }
  dest[i] = '\0';
}

void my_strcat (char *source, char *dest, int from, int len)
{
  int i, l;
  l = strlen (dest);  
  for (i=0; i < len; i++)
    {
      dest[l+i] = source[from+i];
    }
  dest[l+i] = '\0';
}


int get_data_from_buffer (char *buffer, char *header, char last_char, char *output)
// function to get the sequence, structure etc data from the XML lines
{
    char *begin;
    begin  = strstr (buffer, header);    
    //printf ("%s\n", buffer);
    if (begin == NULL) { return 0; }     //printf ("Formatting error, %s not there\n", header); exit(1); }
    begin += strlen(header);        
    int i = 0;
    while (1)
    {
        if (begin[i] == last_char) { output[i] = '\0'; break; }
        output[i] = begin[i++];
    }
    return 1;
}


int main(int argc, char *argv[])
{
    if (argc != 9)
    {
        printf ("Usage: %s <xml_file> <opt_problem_file> --constr-only {1|0} --objective {const|l2norm|l1norm} {MATLAB|CPLEX} <simple_model>\n", argv[0]);
        //printf ("\t num_str    -1 if you want all structures in the file\n");
        exit(0);
    }
    
            
    char filename[200];
    int i, j;
    char sequence[MAXSLEN];
    char structure[MAXSLEN];
    
    // sometimes the initial state is not the completely unfolded sequence. In this case, sequence0 and structure0 are the initial state
    char sequence0[MAXSLEN];
    char structure0[MAXSLEN];    
    
    int self_comp;
    double lb, ub, dg_37, std_dg_37=ABERANT, err_dg_37=ABERANT;
    char buffer [10000];
    // configuration file
    char config_file[200] = "../MultiRNAFold/params/multirnafold.conf";    
    double energy;
    double counter_min[MAXNUMPARAMS];
    int param_met[MAXNUMPARAMS];
    char experiment_for_param[MAXNUMPARAMS][100]; 

    double counter0[MAXNUMPARAMS];    // just in case we have sequence0 and structure0
    char problem_name[200];
    char solver[10];
    int res;
    FILE *xml, *problem; 
    int constraints_only;
    char objective[20];
    char exp_id[100];
    
    simple_internal_energy = 0;
    strcpy (filename, argv[1]);
    strcpy (problem_name, argv[2]);
    constraints_only = atoi (argv[4]);
    strcpy (objective, argv[6]);
    strcpy (solver, argv[7]);
    simple_internal_energy = atoi (argv[8]);
    
    // initialize the thermodynamic parameters
    // call init_data only once for the same dna_or_rna and same temperature
    // if one of them changes, call init_data again (see below)
    init_data (argv[0], config_file, RNA, 37.0);   
       
    num_params = create_string_params();   
    //  for (i=0; i < num_params; i++)
    //     printf ("%d\t%s\n", i+1, string_params[i]);
    
    //  exit(0);
    
    for (i=0; i < num_params; i++)    
    {
        counter0[i] = 0;
        param_met[i] = 0;
        experiment_for_param[i][0] = '\0';
    }
    
    if ((problem = fopen (problem_name,"w")) == NULL)
    {
      printf ("Cannot open file %s\n", problem_name);
      return 0;
    }

    
    if ((xml = fopen (filename, "r")) == NULL)
    {
        printf ("Cannot open file %s\n", filename);
        exit (0);
    }           
    
    int k;
    k = 0;
    int delta_index = 1;
    double margin;
    int counter = 0;
    int num_experiments;    // = 686;    //    506;    //524;
    
    num_experiments = 0;
    // get num_experiments
    while (!feof (xml))
    {    
        // the file is in XML format, among whose lines it has the following types:
        //    <EXPERIMENT sequence="CCGG CCGG" structure="(((( ))))" dG-37="-4.36" STD-dG-37="0.1"> </EXPERIMENT>
        //    <EXPERIMENT sequence="UGACCUCA UGAGGUCA" structure="(((((((( ))))))))" dG-37="-12.34" ERR-dG-37="3%"> </EXPERIMENT>
        char *begin;
        fgets (buffer, sizeof(buffer), xml);
        begin = strstr (buffer, "<EXPERIMENT");        
        if (begin == NULL) continue;    
        num_experiments++;
    }
    fclose (xml);
    
    //printf ("Num exp: %d\n", num_experiments);
    
    if ((xml = fopen (filename, "r")) == NULL)
    {
        printf ("Cannot open file %s\n", filename);
        exit (0);
    }               
    if (!constraints_only)
    {
        fprintf (problem, "MINIMIZE\n");
        if (strcmp (objective, "const") == 0)
        {
            // tried to see if there is any solution of the system, but it's infeasible, 
            //    unless I set a ridicilous error range, i.e 20 times bigger
            fprintf (problem, "    0");    
        }
        else if (strcmp (objective, "l2norm") == 0)
        {        
            // now try the regression-type thingy: least squares
            fprintf (problem, "    [ v0*v0");
            for (i=1; i < num_experiments; i++)
            {
                fprintf (problem, " + v%d*v%d", i, i);
                if (i % 10 == 0)
                    fprintf (problem, "\n    ");
            }
            // add regularizer
            // don't add it for now
            /*            
            for (i=0; i < num_params; i++)
            {
                fprintf (problem, " + 0.01 x%d*x%d", i, i);
                if (i % 10 == 0)
                    fprintf (problem, "\n    ");            
            }
            */
            fprintf (problem, " ] / 2");
        }    
        else if (strcmp (objective, "l1norm") == 0)
        {
            fprintf (problem, "    w0");
            for (i=1; i < num_experiments; i++)
            {
                fprintf (problem, " + w%d", i);
                if (i % 10 == 0)
                    fprintf (problem, "\n    ");
            }       
        }
        else
        {
            printf ("Objective not correct\n");
            exit(1);
        }
        fprintf (problem, "\n\nSUBJECT TO\n");
    }    
    while (!feof (xml))
    {    
        // the file is in XML format, among whose lines it has the following types:
        //    <EXPERIMENT sequence="CCGG CCGG" structure="(((( ))))" dG-37="-4.36" STD-dG-37="0.1"> </EXPERIMENT>
        //    <EXPERIMENT sequence="UGACCUCA UGAGGUCA" structure="(((((((( ))))))))" dG-37="-12.34" ERR-dG-37="3%"> </EXPERIMENT>
        
        // try including only the first paper. With everything I can't save the solution because it says "No basic solution"
        //    Problem solved: In the cplex script, I have to write: "write file.vec"
        //if (counter >= 94) break;
        
        fgets (buffer, sizeof(buffer), xml);
        //printf ("%s", buffer);                         
        char *begin;
        char temp[100];
        sequence[0] = '\0'; structure[0] = '\0'; dg_37 = ABERANT; err_dg_37 = ABERANT; std_dg_37 = ABERANT;
        begin = strstr (buffer, "<EXPERIMENT");        
        if (begin == NULL) continue;
        
        sequence0[0] = '\0';
        structure0[0] = '\0';
        get_data_from_buffer (buffer, "id=\"", '\"', exp_id);
        get_data_from_buffer (buffer, "sequence0=\"", '\"', sequence0);        
        get_data_from_buffer (buffer, "structure0=\"", '\"', structure0);        
        get_data_from_buffer (buffer, "sequence=\"", '\"', sequence);
        get_data_from_buffer (buffer, "structure=\"", '\"', structure);
        get_data_from_buffer (buffer, "dG-37=\"", '\"', temp);  dg_37 = atof (temp);
        if (get_data_from_buffer (buffer, "STD-dG-37=\"", '\"', temp))  std_dg_37 = atof (temp);
        if (get_data_from_buffer (buffer, "ERR-dG-37=\"", '%', temp))  err_dg_37 = atof (temp);                                      
           
        //strcpy (sequence,  "GCCGGA GCCGGA");
        //strcpy (structure, ".((((. .)))).");
        //printf ("%s\t%s\n", sequence, structure);
        
        // Self-complementarity doesn't have to do anything with dG. Symmetry does! 
        // If symmetric, the 0.43 penalty is part of the free energy value.
        // Not exactly sure what symmetric means.
        //  Following conversations with Dave Mathews,
        //      I consider that 2 sequences are symmetric if they are perfectly complementary and identical
        
        
        // first, adjust dg_37 according to self-complementarity
        //  Let S1 be the first sequence, let S2 be the second, and let S3 be the reverse complement of S1. 
        //    I considered the S1/S2 duplex is self-complementary if S1, S2 and S3 are identical.
        // This is incorect!! The 0.43 penalty has to be added when S1 and S2 are identical,
        //      even if they are not perfectly complementary.
        char *space;
        int self_comp = 0;
        //char rev_comp_of_first_part[MAXSLEN/2];        
        space = strstr (sequence, " ");
        if (space != NULL)
        {
            int space_position;
            space_position = space - sequence;
            char first_part[MAXSLEN/2];
            strcpy (first_part, sequence);
            first_part[space_position] = '\0';
            //reverse_complement_of_seq (first_part, rev_comp_of_first_part);
            // Mirela comment added on Dec 14, 2007: the self-complementary sequences can have mismatches, so the following was incorrect
            // if (strcmp (first_part, space+1) == 0 && strstr (structure, ".") == NULL)
            if (strcmp (first_part, space+1) == 0)  // && strstr (structure, ".") == NULL)
            // they are identical and are not perfectly complementary
            //if (strcmp (first_part, space+1) == 0 && strcmp (first_part, rev_comp_of_first_part) == 0)
            {
                //printf ("self-comp: %s\t%s\n", sequence, structure);
                dg_37 -= 0.43;
                self_comp = 1;
            }    
        }
        
        // compute the lower bound and upper bound
        if (err_dg_37 < ABERANT && std_dg_37 < ABERANT)
        {
            printf ("One of err_dg_37 and std_dg_37 hs to be ABERANT. err_dg_37 = %.2lf, std_dg_37 = %.2lf\n", err_dg_37, std_dg_37);
            exit (1);
        }
        if (err_dg_37 < ABERANT)
        {
            //margin = dg_37 * err_dg_37/100;    // it gets infeasible
            margin = dg_37 * err_dg_37/100;
            if (margin < 0) margin *= -1;
        }
        else
        {
            // I assume a normal distribution
            // Thus 99.73% of the area is within three standard deviations.
            //margin = std_dg_37*3; // this gets unfeasible, try with 4
            margin = std_dg_37*3;
        }
        lb = dg_37 - margin;
        ub = dg_37 + margin;

              
        //if (self_comp) printf ("SELF-COMP  ");
        //else           printf ("NON-SE-CO  "); 
        //printf ("%s %s %lf %lf\n", sequence, structure, dg_37, std_dg_37 < ABERANT? std_dg_37 : err_dg_37);
        if (strcmp (solver, "CPLEX") ==0)
        {
            if (strcmp (sequence0, "") != 0)
            {
                fprintf (problem, "    \\* %s %s %s %s %lf %lf *\\\n", sequence0, structure0, sequence, structure, 
                    dg_37, std_dg_37 < ABERANT? std_dg_37 : err_dg_37);   
            }
            else
            {
                fprintf (problem, "    \\* %s %s %lf %lf *\\\n", sequence, structure, dg_37, std_dg_37 < ABERANT? std_dg_37 : err_dg_37);   
            }            
        }
        if (strcmp (sequence0, "") != 0)
        {            
            count_each_structure_type (sequence0, structure0, counter0, 1);            
        }  
        
        // now I have the real sequence and structure
        // store the real structure in matrix_A
        count_each_structure_type (sequence, structure, counter_min, 1);

        // do correction for Giese_Serra_1998 structures: the dangling ends should actually be tstackh, otherwise it's not full rank
        if ((strstr (exp_id, "Giese_Serra_1998") != NULL) ||
            (strstr (exp_id, "Dale_Serra_2000") != NULL) ||
            (strstr (exp_id, "Serra_Turner_1994") != NULL) ||
            (strstr (exp_id, "Vecenie_Serra_2004") != NULL) ||
            (strstr (exp_id, "Vecenie_Serra_2006") != NULL) )
        {         
            int bi,bj,bk,bl;   
            char *b;
            char stype[50];
            int index;
            int to_be_adjusted = 0;
            for (i=0; i < num_params; i++)
            {
                if ( (b = strstr (string_params[i], "dangle")) != NULL && counter_min[i] > 0)
                {
                    to_be_adjusted = 1;
                    counter_min[i] = 0;
                    // has dangle
                    //printf ("*** %s ", string_params[i]);
                    if (b[7] == 't')    // it's top
                    {
                        if (b[11] == '0')         bi=0;
                        else if (b[11] == '1')    bi=1;
                        else if (b[11] == '2')    bi=2;
                        else if (b[11] == '3')    bi=3;
                        if (b[14] == '0')         bj=0;
                        else if (b[14] == '1')    bj=1;
                        else if (b[14] == '2')    bj=2;
                        else if (b[14] == '3')    bj=3;
                        if (b[17] == '0')         bk=0;
                        else if (b[17] == '1')    bk=1;
                        else if (b[17] == '2')    bk=2;
                        else if (b[17] == '3')    bk=3;
                                                
                        //printf ("top i=%d j=%d k=%d ", bi, bj, bk);
                    }
                    else
                    {
                        if (b[17] == '0')         bl=0;
                        else if (b[17] == '1')    bl=1;
                        else if (b[17] == '2')    bl=2;
                        else if (b[17] == '3')    bl=3;                        
                        //printf ("bot ");
                    }
                    //break;                    
                } 
            }
            if (to_be_adjusted)
            {
                sprintf (stype, "tstackh[%d][%d][%d][%d]", bi, bj, bk, bl);
                //printf (" %s ", stype);
                index = structure_type_index (stype);
                counter_min [index] = 2;
                //printf (" %d %s %s\n", index, sequence, structure);
            }                
        }           
        
        char lb_constraint[5000];  
              
        if (strcmp (solver, "MATLAB") ==0)
        {
            for (i=0; i < num_params; i++)
                fprintf (problem, "%.1lf ", counter_min[i]-counter0[i]);
            fprintf (problem, "%.2lf\n", dg_37);
        }
        else if (strcmp (solver, "CPLEX") ==0)    
        {
            int l=0;
            // first write the lower bound
            //sprintf (lb_constraint, "    tc_%s: ", exp_id);    // thermodynamic constraints
            // get the id only, it's too long otherwise
            char idid[10];
            for (int ii=0; ii < strlen (exp_id); ii++)
            {
                if (exp_id[ii] == '_')
                {
                    idid[ii] = '\0';
                    break;
                }
                idid[ii] = exp_id[ii];
            }
            sprintf (lb_constraint, "    tc_%s: ", idid);    // thermodynamic constraints
            fprintf (problem,      "    \\* "); 
            for (i=0; i < num_params; i++)
            {                
                if (counter_min[i] - counter0[i] != 0)
                {
                    param_met[i] = 1;
                    if (experiment_for_param[i][0] == '\0')
                        strcpy (experiment_for_param[i], exp_id);
                    if (l > 0 && counter_min[i]-counter0[i] > 0)
                    { 
                        sprintf (lb_constraint, "%s + ", lb_constraint);  
                        fprintf (problem, " + ");
                    }   
                    // don't write the counter if it's 1    
                    if (counter_min[i]-counter0[i] == 1)  
                    {
                        sprintf (lb_constraint, "%s x%d ", lb_constraint, i);   
                        fprintf (problem, " %s ", string_params[i]);
                    }    
                    else if (counter_min[i]-counter0[i] == -1)  
                    {
                        sprintf (lb_constraint, "%s - x%d ", lb_constraint, i);   
                        fprintf (problem, " - %s ", string_params[i]);
                    }                        
                    else 
                    {
                        sprintf (lb_constraint, "%s %.1lf x%d ", lb_constraint, counter_min[i]-counter0[i], i);
                        fprintf (problem, " %.1lf %s ", counter_min[i]-counter0[i], string_params[i]);
                    }    
                    //if (counter_min[i] == 1)  fprintf (problem, " x%d (%s) ", i, string_params[i]);   
                    //else fprintf (problem, " %d x%d (%s) ", counter_min[i], i, string_params[i]);
                    l++;
                    if (l % 10 == 0)
                    {
                        sprintf (lb_constraint, "%s\n        ", lb_constraint);
                    }    
                }    
            }    
            
            // check if there is any free parameter
            double diff_free;
            diff_free = counter_min[num_params] - counter0[num_params];
            fprintf (problem, " + %.2lf = %.2lf ", diff_free/100.0, dg_37);
            if (diff_free != 0)
            {
                dg_37 -= diff_free/100.0;
            }                        
            
            fprintf (problem, " *\\\n");
            if (strcmp (objective, "const") == 0)    
            {
                sprintf (lb_constraint, "%s >= %.2lf\n", lb_constraint, lb);
            }
            else     // if l1norm or l2norm    
            {
                sprintf (lb_constraint, "%s - v%d = %.2lf\n", lb_constraint, counter, dg_37);
            }
            counter++;
            fprintf (problem, "%s\n", lb_constraint);
            
            
            if (strcmp (objective, "const") == 0)    
            {
                // second write the upper bound
                l = 0;
                fprintf (problem, "    tc_%s: ", exp_id);
                for (i=0; i < num_params; i++)
                {
                    if (counter_min[i]-counter0[i] != 0)
                    {
                        if (l > 0 && counter_min[i]-counter0[i] > 0) 
                            fprintf (problem, " + ");                     
                        // don't write the counter if it's 1    
                        if (counter_min[i]-counter0[i] == 1)  fprintf (problem, " x%d ", i);   
                        else if (counter_min[i]-counter0[i] == -1)  fprintf (problem, " - x%d ", i);   
                        else fprintf (problem, " %.1lf x%d ", counter_min[i]-counter0[i], i);
                        //if (counter_min[i] == 1)  fprintf (problem, " x%d (%s) ", i, string_params[i]);   
                        //else fprintf (problem, " %d x%d (%s) ", counter_min[i], i, string_params[i]);
                        l++;
                        if (l % 10 == 0)
                            fprintf (problem, "\n        ");
                    }    
                }    
                fprintf (problem, " <= %.2lf\n", ub);            
            }    
        }    // end if CPLEX
        //fclose (problem);
        //exit(1);
    }
    
    // TODO: in the file write_optimization_for_regression from ../RNA-thermodynamic-set, we make some of the variable equal to Turner values, so that the matrix becomes full rank. I don't think we need to do this here.
    
    
    
    // We eliminated the following parameters, so we don't need this any more.
    /*
    // now add the formulae for loop length greater than measured values
    // for hairpin loops, the last length is 9    
    int hairpin_last_measured_len = 9;
    int internal_last_measured_len = 6;
    int bulge_last_measured_len = 6;
    int max_len_tabulated = 30;
    int hairpin_last_measured_index;
    int internal_last_measured_index;
    int bulge_last_measured_index;
    char hairpin_last_measured_type[100];
    char internal_last_measured_type[100];
    char bulge_last_measured_type[100];
    sprintf (hairpin_last_measured_type, "hairpin_penalty_by_size[%d]", hairpin_last_measured_len);
    sprintf (internal_last_measured_type, "internal_penalty_by_size[%d]", internal_last_measured_len);
    sprintf (bulge_last_measured_type, "bulge_penalty_by_size[%d]", bulge_last_measured_len);
    hairpin_last_measured_index = structure_type_index (hairpin_last_measured_type);
    internal_last_measured_index = structure_type_index (internal_last_measured_type);
    bulge_last_measured_index = structure_type_index (bulge_last_measured_type);
    
    double RT;
    RT = 1.75 * 1.98717 / 1000 * 310.15;
    //printf ("%lf\n", RT*log(1.0*10/9));
    if (strcmp (solver, "MATLAB") == 0)
    {
        for (i=1; i <= max_len_tabulated - hairpin_last_measured_len; i++)
        {        
            for (j=0; j < num_params; j++)
            {       
                if (j == hairpin_last_measured_index)
                    fprintf (problem, "-1 ");
                else if (j == hairpin_last_measured_index+i)
                    fprintf (problem, "1 ");
                else
                    fprintf (problem, "0 ");
            }
            fprintf (problem, "%lf\n", RT*log (1.0*(hairpin_last_measured_len+i)/hairpin_last_measured_len));                
        }                                    
        for (i=1; i <= max_len_tabulated - internal_last_measured_len; i++)
        {        
            for (j=0; j < num_params; j++)
            {       
                if (j == internal_last_measured_index)
                    fprintf (problem, "-1 ");
                else if (j == internal_last_measured_index+i)
                    fprintf (problem, "1 ");
                else
                    fprintf (problem, "0 ");
            }
            fprintf (problem, "%lf\n", RT*log (1.0*(internal_last_measured_len+i)/internal_last_measured_len));                
         }                                            
        for (i=1; i <= max_len_tabulated - bulge_last_measured_len; i++)
        {        
            for (j=0; j < num_params; j++)
            {       
                if (j == bulge_last_measured_index)
                    fprintf (problem, "-1 ");
                else if (j == bulge_last_measured_index+i)
                    fprintf (problem, "1 ");
                else
                    fprintf (problem, "0 ");
            }
            fprintf (problem, "%lf\n", RT*log (1.0*(bulge_last_measured_len+i)/bulge_last_measured_len));                
         }                                                
    }
    else if (strcmp (solver, "CPLEX") == 0)
    {
        for (i=1; i <= max_len_tabulated - hairpin_last_measured_len; i++)
        {        
            fprintf (problem, "    hc%d: x%d - x%d = %lf\n", 
                hairpin_last_measured_len+i, hairpin_last_measured_index+i, hairpin_last_measured_index, 
                RT*log (1.0*(hairpin_last_measured_len+i)/hairpin_last_measured_len));
        }
        for (i=1; i <= max_len_tabulated - internal_last_measured_len; i++)
        {        
            fprintf (problem, "    ic%d: x%d - x%d = %lf\n", 
                internal_last_measured_len+i, internal_last_measured_index+i, internal_last_measured_index, 
                RT*log (1.0*(internal_last_measured_len+i)/internal_last_measured_len));
        }
        for (i=1; i <= max_len_tabulated - bulge_last_measured_len; i++)
        {        
            fprintf (problem, "    bc%d: x%d - x%d = %lf\n", 
                bulge_last_measured_len+i, bulge_last_measured_index+i, bulge_last_measured_index, 
                RT*log (1.0*(bulge_last_measured_len+i)/bulge_last_measured_len));
        }    
    }        
    // formulae added
    */    
    
    if (strcmp (objective, "l2norm") == 0 || strcmp (objective, "l1norm") == 0)    
    {
        // now num_experiments should be the same as counter
        if (num_experiments != counter)
        {
            printf ("Num_experiments (%d) should be the same as counter (%d)\n", num_experiments, counter);
            exit (1);
        }
    }    
    
    if (strcmp (objective, "l1norm") == 0)
    {
        // add the w inequalities
        for (i=0; i < num_experiments; i++)
        {
            fprintf (problem, "    ac%d_m: w%d - v%d >= 0\n", i, i, i);
            fprintf (problem, "    ac%d_p: w%d + v%d >= 0\n", i, i, i);
        }               
    }
    
    
    if (!constraints_only)
    {
        // By default, CPLEX sets positive bounds or zero
        fprintf (problem, "\nBOUNDS\n");
        for (i=0; i < num_params; i++) 
        {
            if (param_met[i])
                fprintf (problem, "    -infinity <= x%d <= +infinity\n", i);
        }
        if (strcmp (objective, "l2norm") == 0 || strcmp (objective, "l1norm") == 0)    
        {
            for (i=0; i < num_experiments; i++)
            {
                fprintf (problem, "    -infinity <= v%d <= +infinity\n", i);
            }
        }
        fprintf (problem, "\nEND\n");
    }        
    
    fclose (problem);
    fclose (xml);

    PARAMTYPE params_t99[MAXNUMPARAMS];
    save_parameters_in_array(params_t99);
    
    // write the param_met binary values in a file
    // not sure if I need this
    FILE *pmet;
    if ((pmet = fopen ("parameters_met_few.txt", "w")) == NULL)
    {
        printf ("Cannot open file parameters_met_few.txt\n");
        exit (0);
    }              
    for (i=0; i < num_params; i++) 
    {
        if (param_met[i])
            fprintf (pmet, "%32s\t% 2.2f\t[%s]\n", string_params_human_readable[i], params_t99[i]/100.0, experiment_for_param[i]);
        else
            fprintf (pmet, "%32s\t% 2.2f\t\n", string_params_human_readable[i], params_t99[i]/100.0);
    }
    fclose (pmet);
    
    return 0;
}
