/* Title: breed.c
 * Author: Jed Marti KI7NNP
 * Description: Various functions for breeding results. 
 * Revision History: (Created Tue Jan 24 16:10:27 2023)
 *   Sun Feb 26 18:09:15 2023 - Wire size back in it.
 *   Mon Feb 27 07:09:02 2023 - Cubics.
 *   Wed Nov 29 18:34:24 2023 - Fix flag initialization.
 *   Sun Dec  3 11:43:41 2023 - Compile for different equations.
 */


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

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

// Range and symbol table.
extern range R[];
extern symtab S[];

// This is for fiddling the direction changes + or - by some percent. See goodDir below.
static uint8_t curDir = 0;


// A macro for adjusting swapping randomly parameter values between two sets.
#define bflag(var)                               \
if (uniform() >= pct) n->var = a->var;           \
else { n->var = b->var; flag |= 1 << s_##var; }  \

result *breed(result *a, result *b, double pct)
/*     Breed two results into a new one. Take pct percent from a, the rest from b.
 * Parameters:
 *   a, b: The two results to breed.
 *   pct: [0.0 .. 1.0] Take this pct from a.
 * External References: none
 * Returned values: A new result structure.
 * Errors detected: none
 */
{
  result   *n;
  uint32_t flag = 0;
  int32_t  cnt = 0;

  n = newResult();

  // We're not going to search around any of these. Could be dangerous but we can't search
  // the whole thing might be 10^21 or worse. If the search space is only 5, then we're good
  // to go as that's only 4 million or so combinations. We check the polynomial against the
  // library until we get a good one but we only try the number of times from the control
  // file.
 loop:
  flag = 0;
#define X(nme) bflag(nme);
  VARS
#undef X
  n->flags = flag;

  // If already tried this one, then try again. If we've exceeded the retry count, then just
  // punt and return the last one tried (probably a mistake).
  if (doneAlready(n))
  {
    ++cnt;
    if (cnt < S[s_tries].value.iv)  goto loop;
  }
  return n;
}
#undef bflag


// Macro for mutate. Sets the flag if a random number is used.
#define muta(var) \
if (uniform() < pct) { n->var = genUniform(&R[s_##var]); flags |= 1 << s_##var; } \
 else n->var = a->var;

 
result *mutate(result *a, double pct)
/*     Create a copy of a gene, then randomly mutate pct of these to some new value.
 * Parameters:
 *   a: A result to mutate.
 *   pct: [0.0 .. 1.0] mutate this percent.
 * External References: none
 * Returned values: a new mutated result.
 * Errors detected: none.
 */
{
  result   *n;
  uint32_t flags = 0;
  int32_t  cnt = 0;
  
  n = newResult();
 loop:
  flags = 0;
#define X(nme) muta(nme);
  VARS
#undef X
  n->flags = flags;

  // If already tried this one, then try again. If we've exceeded the retry count, then just
  // punt and return the last one tried (probably a mistake).
  if (doneAlready(n))
  {
    ++cnt;
    if (cnt < S[s_tries].value.iv) goto loop;
  }
  return n;
}

#undef muta


// Macro for goodDir - used on all equation coefficients. Note: percentage changed at version
// 2.3.4 - times not divide.
#define dirct(var) \
if (uniform() < pct) \
  { n->var = a->var + a->var*(curDir?S[s_direction].value.dv:-S[s_direction].value.dv); \
    flags |= 1 << s_##var; \
  } \
 else n->var = a->var;

result *goodDir(result *a, double pct)
/*     Move one or more parameters (0 < uniform <  pct < 1) in the current direction which 
 *   oscillates on each call.
 * Parameters:
 *   a: The set of coefficients to modify.
 *   pct: The random percent of coefficients to change (0 <= pct <= 1).
 * External References:
 *   curDir: 0-1 which direction to go.
 *   S[]: Symbol table values for tries and direction percent
 * Returned values: a new result.
 * Errors detected: none.
 */
{
  result *n;
  uint32_t flags = 0;
  int32_t  cnt = 0;
  
  n = newResult();
 loop:
  flags = 0;
#define X(nme) dirct(nme);
  VARS
#undef X
  n->flags = flags;

  // If already tried this one, then try again. If we've exceeded the retry count, then just
  // punt and return the last one tried (probably a mistake).
  if (doneAlready(n))
  {
    ++cnt;
    if (cnt < S[s_tries].value.iv) goto loop;
  }
  curDir ^= 1;
  return n;
}
#undef direct
  
// EOF
