/*
 * Title: coils.c
 * Author: Jed Marti KI7NNP
 * Description: We have the best result. Evaluate all the coils sorting from worst to best so
 *   we can see what doesn't work.
 * Revision History: (Created Wed Jan 25 20:59:27 2023)
 *   Mon Mar 13 07:49:04 2023 - JBM Add CSV of results for inductorx.
 */


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

#include "equations.h"
#include "indequ.h"


// The list of coils.
extern anInductor *theInductors;

// The symbol table.
extern symtab S[];

// See evaluate.c
double sqrd(double x);

// For American display.
static double meters2inches(double m) { return m * 39.3701; }


void showInductor(FILE *fout, anInductor *i)
/*     Show an inductor values. These were sorted in increasing order of goodness.
 * Parameters:
 *   i: An anInductor value.
 * External References:
 *   NORESULT: constant indicates result could not be computed.
 * Returned values: non
 * Errors detected: none
 */
{
  if (i->computed == NORESULT)
    fprintf(fout, "  *** L=%6.3f uH %2d turns, dia %g\", length %g\", #%d, %s\n",
           i->L * 1.0E6, i->N, 2.0 * 39.3701 * i->R, 39.3701 * i->LEN, i->G, i->FN);
  else
    fprintf(fout, " %4.1f%% L=%6.3f : %6.3f uH, %2d turns, dia %g\", len %g\", #%d %s\n",
            i->goodness * 100.0,
           i->L * 1.0E6, i->computed * 1.0E6, i->N,  2.0 * 39.3701 * i->R, 39.3701 * i->LEN, i->G,
           i->FN);
}



void CforInductorX(char *fname, char *varname, anInductor *i)
/*     Generate a C file with the measured inductors and their values. The inductor design
 *   program will use this to suggest an already prepared STL file.
 * Parameters:
 *   fname: file to write to.
 *   varname: Variable name to store the table in.
 *   i: The list of inductors.
 * External References:
 *   VERSION, RELEASE, PATCH: program version from indequ.h.
 * Returned values: none
 * Errors detected: Couldn't open for output.
 * Bugs: Needs STL file name.
 */
{
  FILE *fh;

  if ((fh = fopen(fname, "wb")) == NULL)
  {
    fprintf(stderr, "Couldn't open %s for output\n", fname);
    return;
  }
  printf("Writing inductor results to %s\n", fname);
  fprintf(fh, "/* Title: %s\n\
   Author: indequ V%d.%02d.%03d\n\
   Description: Inductor table of results from a set of equations.\n*/\n\n",  fname, VERSION,
          RELEASE, PATCH);

  fprintf(fh, "#include <stdio.h>\n\
#include <stdlib.h>\n\
#include <stdint.h>\n\
#include \"inductorx.h\"\n\n");
  if (varname != NULL)  fprintf(fh, "NearTable %s[] = {\n", varname);
  else fprintf(fh, "NearTable ???[] = {\n");
  for (; i != NULL; i = (anInductor*) (i->next))
  {
    fprintf(fh, "  {.measuredInductance = %g,\n", i->L);
    fprintf(fh, "   .computedInductance = %g,\n", i->computed);
    fprintf(fh, "   .radius = %g,\n", i->R);
    fprintf(fh, "   .length = %g,\n", i->LEN);
    fprintf(fh, "   .wire = %g,\n", i->W);
    fprintf(fh, "   .turns = %d}", i->N);
    if (i->next != NULL) fprintf(fh, ",\n"); else fprintf(fh, "};\n");
  }

  fprintf(fh, "\n\n\n// EOF\n");
  fclose(fh);
}


static int32_t indLength(anInductor *i)
{
  int32_t cnt;
  for (cnt = 0; i != NULL; i = i->next) ++cnt;
  return cnt;
}


void jaForFitGraph(char *fname, char *varname,  anInductor *i)
{
  FILE *fh;
  int32_t j;
  
  if ((fh = fopen(fname, "wb")) == NULL)
  {
    fprintf(stderr, "Couldn't open %s for output\n", fname);
    return;
  }
  printf("Writing inductor results to %s\n", fname);
  fprintf(fh, "/* Title: %s\n\
   Author: indequ V%d.%02d.%03d\n\
   Description: Inductor table of results from a set of equations.\n*/\n\n",  fname, VERSION,
          RELEASE, PATCH);

  fprintf(fh, "%s = genericVector(%d);\n", varname, indLength(i));
  for (j = 0; i != NULL; ++j, i = i->next)
  {
    fprintf(fh, "\n//////////////////// %d ////////////////////\n", j);
    fprintf(fh, "%s[%d] = newInductor();\n", varname, j);
    fprintf(fh, "%s[%d].measuredInductance = %g;\n", varname, j, i->L);
    fprintf(fh, "%s[%d].computedInductance = %g;\n", varname, j, i->computed);
    fprintf(fh, "%s[%d].radius = %g;\n", varname, j, i->R);
    fprintf(fh, "%s[%d].length = %g;\n", varname, j, i->LEN);
    fprintf(fh, "%s[%d].wire = %g;\n", varname, j, i->W);
    fprintf(fh, "%s[%d].turns = %d;\n", varname, j, i->N);
  }
  fprintf(fh, "end;\n");
  fclose(fh);
}


static anInductor *sortInd(anInductor *l, anInductor *i)
/*     Sort the inductors with the worst performance first. List insertion sort of one item.
 * Parameters:
 *   l: The sorted list.
 *   i: The new result to add to the list.
 * External References: none.
 * Returned values: The sorted list.
 * Errors detected: none
 */
{
  if (l == NULL) return i;

  if (i->goodness >= l->goodness)
  {
    i->next = l;
    return i;
  }

  l->next = sortInd(l->next, i);
  return l;
}



void saveTempResults(result *r)
/*      Save the results in a file in /tmp so if it crashes, we have the last gasp results.
 * Parameters:
 *   r: The list of results.
 * External References:
 *   VERSION, RELEASE, PATCH: Program version used to create file name.
 * Returned values: none
 * Errors detected: Can't open output file.
 */
{
  FILE *fh;
  char fname[256];
  sprintf(fname, "/tmp/%d_%02d_%03d.txt", VERSION, RELEASE, PATCH);
  
  if ((fh = fopen(fname, "wb")) == NULL)
  {
    fprintf(stderr, "Couldn't open %s for output\n", fname);
    exit(-1);
  }
  showResult(fh, r);
  fclose(fh);
}



void showInductorResults(result *r)
/*     Show the inductor sorted from worst performance to the best. These can be written to a
 *   file as well.
 * Parameters:
 *   r: The best results list. We show the first values only.
 * External References:
 *   S[]: symbol table - check for output file.
 *   theInductors: A list of all the measured inductors.
 *   NORESULT: constant indicates did not work.
 * Returned values: none
 * Errors detected: none
 */
{
  anInductor *sorted = NULL;
  anInductor *nores = NULL;
  anInductor *i;
  anInductor *nxt;
  double     v;
  FILE       *fout = NULL;

  if (S[s_resultf].value.sv != NULL)
  {
    fout = fopen(S[s_resultf].value.sv, "wb");
    fprintf(fout, "Results from V%d.%02d.%03d %s\n", VERSION, RELEASE, PATCH, EQUATION);
  }

  fprintf(fout, "Random seed %ld\n", S[s_seed].value.lv);

  // Show equation parameters.
  showResult(stdout, r);
  if (fout) showResult(fout, r);

  for (i = theInductors; i != NULL; i = nxt)
  {
    nxt = i->next;
    i->next = NULL;

    v = evalInductorAbs(i, r);
    if (v == NORESULT)
    {
      i->goodness = i->computed = NORESULT;
      i->next = nores;
      nores = i;
    }
    else
    {
      i->computed = v;
#ifdef RMSERROR
      i->goodness = sqrd((v - i->L) / i->L);
#else
      i->goodness = fabs(v - i->L) / i->L;
#endif
      sorted = sortInd(sorted, i);
    }
  }

  printf("\n\nWorst first\n");
  if (fout) fprintf(fout, "Worst first\n");
  for (i = sorted; i != NULL; i = i->next)
  {
    showInductor(stdout, i);
    if (fout) showInductor(fout, i);
  }

  // Put the range table in the output file (only).
  if (fout) dumpRangeTable(fout, "\n\nRange table\n");
    
  if (fout) fclose(fout);

  // Now do the CSV results for inductorx.
  if (S[s_resultc].value.sv != NULL)
  {
    if (S[s_resultc].value.sv != NULL)
      CforInductorX(S[s_resultc].value.sv, S[s_cvarname].value.sv, sorted);
    if (S[s_resultja].value.sv != NULL)
      jaForFitGraph(S[s_resultja].value.sv, S[s_javarname].value.sv, sorted);
  }
}


// EOF
