/*
 * make.koho.data written by Jim Warhol, for CSC 671 Winter '95.
 *
 * make.koho.data generates data points for CSC 671/871 programming
 * assignment I (Kohonen Networks).
 *
 * make.koho.data idealx idealy idealz point_count fuzz_factor
 *
 * idealx, idealy and idealz are floating point numbers representing
 * the vector coordinates <x,y,z> for an idealized point for which 
 * point_count "fuzzy" data points will be generated, and sent to 
 * standard output. The idealized point and all output data points 
 * will be normalized to length 1. fuzz_factor is the upper limit of 
 * the random "fuzz factor" which will be added to or subtracted from 
 * each idealized data point. i.e., the coordinates of the generated data
 * points will be within the range [ideal-fuzz_factor..ideal+fuzz_factor].
 *
 * Compile with: gcc make.koho.data.c -o bin/make.koho.data -lm
 */

#include <math.h>
#include <stdio.h>

/*
 * global definitions.
 */

   char point_label[20];
   int point_count, i;
   float fuzz_factor;

   struct vector {
      float x; float y; float z;
   } Ideal, W;

/*
 * function prototypes.
 */

void initialize(int argc, char *argv[]);
struct vector make_noise(struct vector V);
struct vector normalize(struct vector V);

/* main program */

void main(int argc, char *argv[])
{
   initialize(argc, argv);

   Ideal = normalize(Ideal);

   /* generate point_count fuzzy data points around ideal point */

   for (; point_count > 0; point_count--) {
      W = make_noise(Ideal);
      printf("%9.6f %9.6f %9.6f (based on %s %9.6f %9.6f %9.6f)\n", 
         W.x, W.y, W.z, point_label, Ideal.x, Ideal.y, Ideal.z);
   }
  
   exit(0);

}

/* void initialize(int argc, char *argv[])
 * 
 * initialize initializes the ideal point label and vector, the number of
 * data points to be generated and the fuzz factor. This information is 
 * obtained from the command line (see documentation above).
 *
 * initialize also initializes the random number generator.
 */

void initialize(int argc, char *argv[])
{
   /* get command arguments */

   if (argc != 7) {
      fprintf(stderr, "ERROR: You must specify 6 parameters:\n");
      fprintf(stderr, "   make.koho.data point_label "
         "idealx idealy idealz point_count fuzz_factor\n");
      fprintf(stderr, "   e.g., make.koho.data A 0.0 0.0 1.0 15 0.03\n");
      exit(1); }

   if (sscanf(argv[1], "%s", &point_label) != 1) {
      fprintf(stderr, "ERROR: ideal point label is incorrect.\n");
      exit(1); }

   if (sscanf(argv[2], "%f", &Ideal.x) != 1) {
      fprintf(stderr, "ERROR: ideal point x coordinate is incorrect.\n");
      exit(1); }

   if (sscanf(argv[3], "%f", &Ideal.y) != 1) {
      fprintf(stderr, "ERROR: ideal point y coordinate is incorrect.\n");
      exit(1); }

   if (sscanf(argv[4], "%f", &Ideal.z) != 1) {
      fprintf(stderr, "ERROR: ideal point z coordinate is incorrect.\n");
      exit(1); }

   if (sscanf(argv[5], "%d", &point_count) != 1) {
      fprintf(stderr, "ERROR: point count parameter is incorrect.\n");
      exit(1); }

   if (sscanf(argv[6], "%f", &fuzz_factor) != 1) {
      fprintf(stderr, "ERROR: fuzz factor parameter is incorrect.\n");
      exit(1); }

   /* initialize random number generator */

   srandom((int) time()); 

}

/* struct vector make_noise(struct vector V);
 *
 * Vnew = make_noise(Vold);
 *
 * make_noise "fuzzies up" vector Vold to make it imprecisely in the 
 * vicinity of the original vector. This is done by adding a random 
 * number in the range [-fuzz_factor..+fuzz_factor]. This new vector 
 * is then normalized to remain on the unit sphere. 
 */

struct vector make_noise(struct vector V)
{
   /* add random noise value between -fuzz_factor and +fuzz_factor */

   V.x += fmod(random(), 2.0 * fuzz_factor) - fuzz_factor;
   V.y += fmod(random(), 2.0 * fuzz_factor) - fuzz_factor;
   V.z += fmod(random(), 2.0 * fuzz_factor) - fuzz_factor;
   
   return normalize(V);
}

/* struct vector normalize(struct vector V);
 *
 * Vnew = normalize(Vold);
 *
 * normalizes vector Vold so it has length 1 (and thus falls on the unit 
 * sphere).
 */

struct vector normalize(struct vector V)
{
   struct vector Result;
   double vector_length;
 
   vector_length = sqrt(V.x * V.x + V.y * V.y + V.z * V.z);

   Result.x = V.x / vector_length;
   Result.y = V.y / vector_length;
   Result.z = V.z / vector_length;

   return Result;
};
