/*
 * Copyright(C) 1999-2020 National Technology & Engineering Solutions
 * of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
 * NTESS, the U.S. Government retains certain rights in this software.
 *
 * See packages/seacas/LICENSE for details
 */

#include <stdio.h> // for fprintf, printf, NULL, FILE, etc

static void reflect_params();

/* Print out the input options. */
void reflect_input(int    nvtxs,         /* number of vertices in graph */
                   int    nedges,        /* number of edges in graph */
                   int    igeom,         /* geometric dimension for inertial method */
                   char * graphname,     /* name of graph input file */
                   char * geomname,      /* name of geometry input file */
                   char * inassignname,  /* name of assignment input file */
                   char * outassignname, /* name of assignment output file */
                   char * outfilename,   /* name of information output file */
                   int    architecture,  /* 0=> hypercube, d=> d-dimensional mesh */
                   int    ndims_tot,     /* total number of cuts to make */
                   int    mesh_dims[3],  /* size of mesh */
                   int    global_method, /* global partitioning algorithm */
                   int    local_method,  /* local partitioning algorithm */
                   int    rqi_flag,      /* use RQI/Symmlq eigensolver?  */
                   int    vmax,          /* smallest acceptable coarsened nvtxs */
                   int    ndims,         /* partitioning level */
                   double eigtol,        /* tolerance on eigenvectors */
                   long   seed,          /* random number seed */
                   FILE * outfile)        /* file to write output to */
{
  extern int DEBUG_TRACE;    /* trace main execution path? */
  extern int ECHO;           /* copy input parameters back to screen? */
  extern int OUT_ASSIGN_INV; /* assignment output in inverted format? */
  extern int IN_ASSIGN_INV;  /* assignment input in inverted format? */
  extern int PRINT_HEADERS;  /* print section headers for output */
  FILE *     tempfile;       /* file or stdout */
  int        i;              /* loop counter */

  if (DEBUG_TRACE > 0) {
    printf("<Entering reflect_input>\n");
  }

  for (i = 0; i < 2; i++) {
    if (i == 1) {
      if (ECHO < 0 && outfile != NULL) {
        tempfile = outfile;
      }
      else {
        break;
      }
    }
    else {
      tempfile = stdout;
    }
    fprintf(tempfile, "\n");

    if (PRINT_HEADERS) {
      fprintf(tempfile, "\n           Input and Parameter Values\n\n");
    }

    if (graphname != NULL) {
      fprintf(tempfile, "Graph file: `%s', ", graphname);
    }
    fprintf(tempfile, "# vertices = %d, # edges = %d\n", nvtxs, nedges);

    /* Print global partitioning strategy. */
    fprintf(tempfile, "Global method: ");
    if (global_method == 1) {
      fprintf(tempfile, "Multilevel-KL\n");
    }
    else if (global_method == 2) {
      fprintf(tempfile, "Spectral\n");
    }
    else if (global_method == 3) {
      fprintf(tempfile, "Inertial\n");
    }
    else if (global_method == 4) {
      fprintf(tempfile, "Linear\n");
    }
    else if (global_method == 5) {
      fprintf(tempfile, "Random\n");
    }
    else if (global_method == 6) {
      fprintf(tempfile, "Scattered\n");
    }
    else if (global_method == 7) {
      fprintf(tempfile, "Read-From-File ");
      if (IN_ASSIGN_INV) {
        printf("(inverted format)\n");
      }
      else {
        printf("(normal format)\n");
      }
    }

    if (global_method == 1) {
      fprintf(tempfile, "Number of vertices to coarsen down to: %d\n", vmax);
      fprintf(tempfile, "Eigen tolerance: %g\n", eigtol);
    }

    else if (global_method == 2) {
      if (rqi_flag) {
        fprintf(tempfile, "Multilevel RQI/Symmlq eigensolver\n");
        fprintf(tempfile, "Number of vertices to coarsen down to: %d\n", vmax);
        fprintf(tempfile, "Eigen tolerance: %g\n", eigtol);
      }
    }

    else if (global_method == 3) {
      if (geomname != NULL) {
        fprintf(tempfile, "Geometry input file: `%s', Dimensionality = %d\n", geomname, igeom);
      }
    }

    else if (global_method == 7) {
      fprintf(tempfile, "Assignment input file: `%s'\n", inassignname);
    }

    /* Now describe local method. */
    if (local_method == 1) {
      fprintf(tempfile, "Local method: Kernighan-Lin\n");
    }
    else if (local_method == 2) {
      fprintf(tempfile, "Local method: None\n");
    }

    /* Now describe target architecture. */
    if (architecture == 0) {
      fprintf(tempfile, "Partitioning target: %d-dimensional hypercube\n", ndims_tot);
    }
    else if (architecture > 0) {
      fprintf(tempfile, "Partitioning target: %d-dimensional mesh of size ", architecture);
      if (architecture == 1) {
        fprintf(tempfile, "%d\n", mesh_dims[0]);
      }
      else if (architecture == 2) {
        fprintf(tempfile, "%dx%d\n", mesh_dims[0], mesh_dims[1]);
      }
      else if (architecture == 3) {
        fprintf(tempfile, "%dx%dx%d\n", mesh_dims[0], mesh_dims[1], mesh_dims[2]);
      }
    }

    if (ndims == 1) {
      fprintf(tempfile, "Partitioning mode: Bisection\n");
    }
    else if (ndims == 2) {
      fprintf(tempfile, "Partitioning mode: Quadrisection\n");
    }
    else if (ndims == 3) {
      fprintf(tempfile, "Partitioning mode: Octasection\n");
    }

    /* Add stuff about communications simulator. */

    fprintf(tempfile, "Random seed: %ld\n", seed);

    if (outassignname != NULL) {
      fprintf(tempfile, "Assignment output file: `%s' ", outassignname);
      if (OUT_ASSIGN_INV) {
        printf("(inverted format)\n");
      }
      else {
        printf("(normal format)\n");
      }
    }
    if (outfilename != NULL) {
      fprintf(tempfile, "Output file: `%s'\n", outfilename);
    }

    if (ECHO > 1 || ECHO < -1) {
      reflect_params(tempfile, global_method, local_method, rqi_flag, ndims);
    }
    fprintf(tempfile, "\n");
  }
}

static void reflect_params(FILE *tempfile,      /* file or stdout */
                           int   global_method, /* global partitioning algorithm */
                           int   local_method,  /* local partitioning algorithm */
                           int   rqi_flag,      /* use RQI/SYMMLQ eigensolver? */
                           int   ndims)           /* number of eigenvectors to generate */
{
  extern int CHECK_INPUT;    /* check the input for consistency? */
  extern int OUTPUT_METRICS; /* controls formatting of output */

  extern int    LANCZOS_TYPE;             /* type of Lanczos to use */
  extern double EIGEN_TOLERANCE;          /* eigen-tolerance convergence criteria */
  extern double SRESTOL;                  /* relative residual tol on T evec */
  extern int    LANCZOS_SO_INTERVAL;      /* interval between orthog checks in SO */
  extern int    LANCZOS_MAXITNS;          /* if positive, Lanczos iteration limit */
  extern double BISECTION_SAFETY;         /* divides Lanczos bisection tolerance  */
  extern int    LANCZOS_TIME;             /* detailed Lanczos timing? */
  extern int    LANCZOS_CONVERGENCE_MODE; /* Lanczos convergence test type */
  extern int    RQI_CONVERGENCE_MODE;     /* rqi convergence test type: */
  extern int    WARNING_EVECS;            /* warning level for evec calculation */
  extern int    LANCZOS_SO_PRECISION;     /* single or double precision? */

  extern int    MAKE_CONNECTED;    /* connect graph for spectral method? */
  extern int    PERTURB;           /* randomly perturb matrix in spectral method? */
  extern int    NPERTURB;          /* if so, how many edges to modify? */
  extern double PERTURB_MAX;       /* largest value for perturbation */
  extern int    MAPPING_TYPE;      /* how to map from eigenvectors to partition */
  extern int    COARSE_NLEVEL_RQI; /* levels between RQI calls */
  extern int    OPT3D_NTRIES;      /* # local opts to look for global min in opt3d */

  extern int    KL_METRIC;     /* KL interset cost: 1=>cuts, 2=>hops */
  extern int    KL_RANDOM;     /* use randomness in Kernighan-Lin? */
  extern int    KL_BAD_MOVES;  /* number of unhelpful moves in a row allowed */
  extern int    KL_NTRIES_BAD; /* # unhelpful passes before quitting KL */
  extern int    KL_UNDO_LIST;  /* only resort vtxs affected by last pass? */
  extern double KL_IMBALANCE;  /* allowed fractional imbalance in KL */

  extern double COARSEN_RATIO_MIN; /* required coarsening reduction */
  extern int    COARSE_NLEVEL_KL;  /* # levels between KL calls in uncoarsening */
  extern int    MATCH_TYPE;        /* which type of contraction matching to use? */
  extern int    HEAVY_MATCH;       /* encourage heavy matching edges? */
  extern int    COARSE_KL_BOTTOM;  /* force KL invocation at bottom level? */
  extern int    COARSEN_VWGTS;     /* use vertex weights while coarsening? */
  extern int    COARSEN_EWGTS;     /* use edge weights while coarsening? */
  extern int    KL_ONLY_BNDY;      /* start KL w/ vertices on boundary? */

  extern int REFINE_PARTITION;  /* Postprocess to improve pairwise cuts? */
  extern int INTERNAL_VERTICES; /* Postprocess to increase interval vtxs? */
  extern int REFINE_MAP;        /* Postprocess sets to reduce hops? */

  extern int    SIMULATOR;       /* simulate the communication? */
  extern int    SIMULATION_ITNS; /* simulator iterations */
  extern double CUT_COST;        /* cost of each cut */
  extern double HOP_COST;        /* cost of each hop */
  extern double BDY_COST;        /* cost of each boundary vertex */
  extern double BDY_HOP_COST;    /* cost of each boundary vertex hop */
  extern double STARTUP_COST;    /* initiation cost of each message */

  extern int    TERM_PROP;       /* invoke terminal propagation? */
  extern double CUT_TO_HOP_COST; /* ..if so, importance of cuts/hops */
  extern int    SEQUENCE;        /* just generate spectral ordering? */
  extern char   SEQ_FILENAME[];  /* name of sequence file */
  extern int    MAKE_VWGTS;      /* Force vertex weights to be degrees+1 ? */

  extern int DEBUG_EVECS;       /* Debug flag for eigenvector generation */
  extern int DEBUG_KL;          /* Debug flag for Kernighan-Lin */
  extern int DEBUG_INERTIAL;    /* Debug flag for inertial method */
  extern int DEBUG_CONNECTED;   /* Debug flag for connected components */
  extern int DEBUG_PERTURB;     /* Debug flag for matrix perturbation */
  extern int DEBUG_ASSIGN;      /* Debug flag for assignment to sets */
  extern int DEBUG_OPTIMIZE;    /* Debug flag for optimization/rotation */
  extern int DEBUG_BPMATCH;     /* Debug flag for bipartite matching code */
  extern int DEBUG_COARSEN;     /* Debug flag for coarsening/uncoarsening */
  extern int DEBUG_MEMORY;      /* Debug flag for smalloc/sfree */
  extern int DEBUG_INPUT;       /* Debug flag for having read input files */
  extern int DEBUG_PARAMS;      /* Debug flag for reading parameter file */
  extern int DEBUG_INTERNAL;    /* Debug flag for internal vertices */
  extern int DEBUG_REFINE_PART; /* Debug flag for refining part. */
  extern int DEBUG_REFINE_MAP;  /* Debug flag for refining mapping */
  extern int DEBUG_SIMULATOR;   /* Debug flag for comm simulator */
  extern int DEBUG_TRACE;       /* Trace main execution path */
  extern int DEBUG_MACH_PARAMS; /* Print computed machine params? */

  extern int EXPERT; /* Expert user? */

  char *true_or_false();

  fprintf(tempfile, "Active Parameters:\n");

  fprintf(tempfile, "  CHECK_INPUT = %s\n", true_or_false(CHECK_INPUT));

  if (global_method == 1 || global_method == 2) {
    fprintf(tempfile, "  LANCZOS_TYPE:  ");
    if (LANCZOS_TYPE == 1) {
      fprintf(tempfile, " Full orthogonalization");
    }
    else if (LANCZOS_TYPE == 2) {
      fprintf(tempfile, "Full orthogonalization, inverse operator");
    }
    else if (LANCZOS_TYPE == 3) {
      fprintf(tempfile, "Selective orthogonalization");
    }
    else if (LANCZOS_TYPE == 4) {
      if (EXPERT) {
        fprintf(tempfile, "Selective orthogonalization against both ends");
      }
      else { /* Check input should catch this, but just in case ... */
        LANCZOS_TYPE = 3;
        fprintf(tempfile, "Selective orthogonalization");
      }
    }
    if (TERM_PROP) {
      fprintf(tempfile, " OR extended");
    }
    fprintf(tempfile, "\n");

    fprintf(tempfile, "  EIGEN_TOLERANCE = %g\n", EIGEN_TOLERANCE);
    if (SRESTOL > 0) {
      fprintf(tempfile, "  SRESTOL = %g\n", SRESTOL);
    }
    else {
      fprintf(tempfile, "  SRESTOL = %g ... autoset to square of eigen tolerance\n", SRESTOL);
    }
    if (LANCZOS_MAXITNS > 0) {
      fprintf(tempfile, "  LANCZOS_MAXITNS = %d\n", LANCZOS_MAXITNS);
    }
    else {
      fprintf(tempfile, "  LANCZOS_MAXITNS = %d ... autoset to twice # vertices\n",
              LANCZOS_MAXITNS);
    }
    if (LANCZOS_SO_PRECISION == 1) {
      fprintf(tempfile, "  LANCZOS_SO_PRECISION = 1 ... single precision\n");
    }
    else {
      fprintf(tempfile, "  LANCZOS_SO_PRECISION = 2 ... double precision\n");
    }
    fprintf(tempfile, "  LANCZOS_SO_INTERVAL = %d\n", LANCZOS_SO_INTERVAL);
    if (LANCZOS_CONVERGENCE_MODE == 1) {
      fprintf(tempfile, "  LANCZOS_CONVERGENCE_MODE = 1 ... partition tolerance\n");
    }
    else {
      fprintf(tempfile, "  LANCZOS_CONVERGENCE_MODE = 0 ... residual tolerance\n");
    }
    fprintf(tempfile, "  BISECTION_SAFETY = %g\n", BISECTION_SAFETY);
    if (LANCZOS_TYPE == 3 || LANCZOS_TYPE == 4) {
      if (LANCZOS_TIME == 0) {
        fprintf(tempfile, "  LANCZOS_TIME = 0 ... no detailed timing\n");
      }
      else {
        fprintf(tempfile, "  LANCZOS_TIME = 1 ... detailed timing\n");
      }
    }

    if (WARNING_EVECS > 0) {
      fprintf(tempfile, "  WARNING_EVECS = %d\n", WARNING_EVECS);
    }

    if (MAPPING_TYPE == 0) {
      fprintf(tempfile, "  MAPPING_TYPE = 0 ... cut at origin\n");
    }
    else if (MAPPING_TYPE == 1) {
      fprintf(tempfile, "  MAPPING_TYPE = 1 ... min-cost assignment\n");
    }
    else if (MAPPING_TYPE == 2) {
      fprintf(tempfile, "  MAPPING_TYPE = 2 ... recursive median\n");
    }
    else if (MAPPING_TYPE == 3) {
      fprintf(tempfile, "  MAPPING_TYPE = 3 ... independent medians\n");
    }

    fprintf(tempfile, "  MAKE_CONNECTED = %s\n", true_or_false(MAKE_CONNECTED));
    fprintf(tempfile, "  PERTURB = %s\n", true_or_false(PERTURB));
    if (PERTURB) {
      fprintf(tempfile, "    NPERTURB = %d\n", NPERTURB);
      fprintf(tempfile, "    PERTURB_MAX = %g\n", PERTURB_MAX);
    }
    if (ndims == 3) {
      fprintf(tempfile, "  OPT3D_NTRIES = %d\n", OPT3D_NTRIES);
    }
  }
  if (global_method == 1) {
    fprintf(tempfile, "  COARSEN_RATIO_MIN = %g\n", COARSEN_RATIO_MIN);
    fprintf(tempfile, "  COARSE_NLEVEL_KL = %d\n", COARSE_NLEVEL_KL);
    fprintf(tempfile, "  MATCH_TYPE = %d\n", MATCH_TYPE);
    fprintf(tempfile, "  HEAVY_MATCH = %s\n", true_or_false(HEAVY_MATCH));
    fprintf(tempfile, "  COARSE_KL_BOTTOM = %s\n", true_or_false(COARSE_KL_BOTTOM));
    fprintf(tempfile, "  COARSEN_VWGTS = %s\n", true_or_false(COARSEN_VWGTS));
    fprintf(tempfile, "  COARSEN_EWGTS = %s\n", true_or_false(COARSEN_EWGTS));
    fprintf(tempfile, "  KL_ONLY_BNDY = %s\n", true_or_false(KL_ONLY_BNDY));
    /*
            if (LIMIT_KL_EWGTS) {
                fprintf(tempfile, "  LIMIT_KL_EWGTS = %s, EWGT_RATIO_MAX = %g\n",
                        true_or_false(LIMIT_KL_EWGTS), EWGT_RATIO_MAX);
            }
            else {
                fprintf(tempfile, "  LIMIT_KL_EWGTS = %s\n", true_or_false(LIMIT_KL_EWGTS));
            }
    */
  }

  if (global_method == 2 && rqi_flag) {
    fprintf(tempfile, "  COARSE_NLEVEL_RQI = %d\n", COARSE_NLEVEL_RQI);
    if (RQI_CONVERGENCE_MODE == 1) {
      fprintf(tempfile, "  RQI_CONVERGENCE_MODE = 1 ... partition tolerance\n");
    }
    else {
      fprintf(tempfile, "  RQI_CONVERGENCE_MODE = 0 ... residual tolerance\n");
    }
    fprintf(tempfile, "  COARSEN_RATIO_MIN = %g\n", COARSEN_RATIO_MIN);
    fprintf(tempfile, "  COARSEN_VWGTS = %s\n", true_or_false(COARSEN_VWGTS));
    fprintf(tempfile, "  COARSEN_EWGTS = %s\n", true_or_false(COARSEN_EWGTS));
  }

  if (global_method == 1 || local_method == 1) {
    fprintf(tempfile, "  KL_RANDOM = %s\n", true_or_false(KL_RANDOM));
    if (KL_METRIC == 1) {
      fprintf(tempfile, "  KL_METRIC = Cuts\n");
    }
    else if (KL_METRIC == 2) {
      fprintf(tempfile, "  KL_METRIC = Hops\n");
    }
    fprintf(tempfile, "  KL_NTRIES_BAD = %d\n", KL_NTRIES_BAD);
    fprintf(tempfile, "  KL_BAD_MOVES = %d\n", KL_BAD_MOVES);
    fprintf(tempfile, "  KL_UNDO_LIST = %s\n", true_or_false(KL_UNDO_LIST));
    fprintf(tempfile, "  KL_IMBALANCE = %g\n", KL_IMBALANCE);
  }

  if (global_method == 1 || global_method == 2 || local_method == 1) {
    fprintf(tempfile, "  TERM_PROP = %s\n", true_or_false(TERM_PROP));
    if (TERM_PROP) {
      fprintf(tempfile, "    CUT_TO_HOP_COST = %g\n", CUT_TO_HOP_COST);
    }
  }

  if (SEQUENCE) {
    fprintf(tempfile, "  SEQUENCE = %s, Sequence file name = `%s'\n", true_or_false(SEQUENCE),
            SEQ_FILENAME);
  }

  fprintf(tempfile, "  OUTPUT_METRICS = %d\n", OUTPUT_METRICS);
  fprintf(tempfile, "  MAKE_VWGTS = %s\n", true_or_false(MAKE_VWGTS));
  fprintf(tempfile, "  REFINE_MAP = %s\n", true_or_false(REFINE_MAP));
  fprintf(tempfile, "  REFINE_PARTITION = %d\n", REFINE_PARTITION);
  fprintf(tempfile, "  INTERNAL_VERTICES = %s\n", true_or_false(INTERNAL_VERTICES));

  if (SIMULATOR > 0) {
    fprintf(tempfile, "  SIMULATOR = %d\n", SIMULATOR);
    fprintf(tempfile, "  SIMULATION_ITNS = %d\n", SIMULATION_ITNS);
    fprintf(tempfile, "  CUT_COST = %g\n", CUT_COST);
    fprintf(tempfile, "  HOP_COST = %g\n", HOP_COST);
    fprintf(tempfile, "  BDY_COST = %g\n", BDY_COST);
    fprintf(tempfile, "  BDY_HOP_COST = %g\n", BDY_HOP_COST);
    fprintf(tempfile, "  STARTUP_COST = %g\n", STARTUP_COST);
  }

  /* Now print out all the nonzero debug parameters. */
  if (DEBUG_CONNECTED) {
    fprintf(tempfile, "  DEBUG_CONNECTED = %d\n", DEBUG_CONNECTED);
  }
  if (DEBUG_PERTURB) {
    fprintf(tempfile, "  DEBUG_PERTURB = %d\n", DEBUG_PERTURB);
  }
  if (DEBUG_ASSIGN) {
    fprintf(tempfile, "  DEBUG_ASSIGN = %d\n", DEBUG_ASSIGN);
  }
  if (DEBUG_INERTIAL) {
    fprintf(tempfile, "  DEBUG_INERTIAL = %d\n", DEBUG_INERTIAL);
  }
  if (DEBUG_OPTIMIZE) {
    fprintf(tempfile, "  DEBUG_OPTIMIZE = %d\n", DEBUG_OPTIMIZE);
  }
  if (DEBUG_BPMATCH) {
    fprintf(tempfile, "  DEBUG_BPMATCH = %d\n", DEBUG_BPMATCH);
  }
  if (DEBUG_COARSEN) {
    fprintf(tempfile, "  DEBUG_COARSEN = %d\n", DEBUG_COARSEN);
  }
  if (DEBUG_EVECS) {
    fprintf(tempfile, "  DEBUG_EVECS = %d\n", DEBUG_EVECS);
  }
  if (DEBUG_KL) {
    fprintf(tempfile, "  DEBUG_KL = %d\n", DEBUG_KL);
  }
  if (DEBUG_MEMORY) {
    fprintf(tempfile, "  DEBUG_MEMORY = %d\n", DEBUG_MEMORY);
  }
  if (DEBUG_INPUT) {
    fprintf(tempfile, "  DEBUG_INPUT = %d\n", DEBUG_INPUT);
  }
  if (DEBUG_PARAMS) {
    fprintf(tempfile, "  DEBUG_PARAMS = %d\n", DEBUG_PARAMS);
  }
  if (DEBUG_INTERNAL) {
    fprintf(tempfile, "  DEBUG_INTERNAL = %d\n", DEBUG_INTERNAL);
  }
  if (DEBUG_REFINE_PART) {
    fprintf(tempfile, "  DEBUG_REFINE_PART = %d\n", DEBUG_REFINE_PART);
  }
  if (DEBUG_REFINE_MAP) {
    fprintf(tempfile, "  DEBUG_REFINE_MAP = %d\n", DEBUG_REFINE_MAP);
  }
  if (DEBUG_SIMULATOR) {
    fprintf(tempfile, "  DEBUG_SIMULATOR = %d\n", DEBUG_SIMULATOR);
  }
  if (DEBUG_TRACE) {
    fprintf(tempfile, "  DEBUG_TRACE = %d\n", DEBUG_TRACE);
  }
  if (DEBUG_MACH_PARAMS) {
    fprintf(tempfile, "  DEBUG_MACH_PARAMS = %d\n", DEBUG_MACH_PARAMS);
  }
}
