/*
    mm: mo.m
    Copyright (C) 2006  Macaulay Institute

    This file is part of mm, a series of programs to test the effect of
    various mechanisms for mitigating against floating point issues on
    memory usage and time.

    mm is free software; you can redistributed it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    mm is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details. (LICENCE file in
    this directory.)

    You should have received a copy of the GNU General Public License
    along with this programl if not, write to the Free Software
    Fountation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Contact information:
      Gary Polhill,
      Macaulay Institute, Craigiebuckler, Aberdeen, AB15 8QH. United Kingdom
      g.polhill@macaulay.ac.uk
*/

#import <stdio.h>
#import <stdlib.h>
#import <errno.h>
#import "common.h"

#ifdef DOUBLESIMPLE
#  import <DoubleSimple.h>
#endif

/**
 * mo should be compiled in several different modes to get the operator
 * required. By default no operator will be used. To get the operators you
 * require, compile with -DOPERATOR=<op> where <op> is one of
 *
 * 0 -- No operation
 * 1 -- Addition
 * 2 -- Subtraction
 * 3 -- Multiplication
 * 4 -- Division
 * 5 -- Less than
 * 6 -- Equal to
 * 7 -- More than
 * 8 -- More than or equal to
 * 9 -- Not equal to
 * 10 -- Less than or equal to
 */

void mo(long n);

/**
 * main
 */

int main(int argc, char **argv) {
  int maxarg, minarg, seedarg;

#ifdef DOUBLESIMPLE
  DoubleSimple *init;

  maxarg = 4;
  minarg = 3;
  seedarg = 3;
#else
  maxarg = 3;
  minarg = 2;
  seedarg = 2;
#endif
  if(argc < minarg || argc > maxarg) {
    fprintf(stderr, "Usage: %s <number of numbers>%s[<seed>]\n", argv[0],
#ifdef DOUBLESIMPLE
	    " <DoubleSimple class initialiser string> "
				// e.g.1 DoubleSimple=0.5
				// e.g.2 DoubleEpsilon=0.5~0.0001
#else
	    " "
#endif
	    );
    fprintf(stderr, "Compiled with operator: ");
#if OPERATOR == 0
    fprintf(stderr, "NOP\n");
#elif OPERATOR == 1
    fprintf(stderr, "ADD\n");
#elif OPERATOR == 2
    fprintf(stderr, "SUB\n");
#elif OPERATOR == 3
    fprintf(stderr, "MUL\n");
#elif OPERATOR == 4
    fprintf(stderr, "DIV\n");
#elif OPERATOR == 5
    fprintf(stderr, "LT\n");
#elif OPERATOR == 6
    fprintf(stderr, "EQ\n");
#elif OPERATOR == 7
    fprintf(stderr, "GT\n");
#elif OPERATOR == 8
    fprintf(stderr, "GE\n");
#elif OPERATOR == 9
    fprintf(stderr, "NE\n");
#elif OPERATOR == 10
    fprintf(stderr, "LE\n");
#else
    fprintf(stderr, "Unrecognised!\n");
#   error Unrecognised OPERATOR macro value
#endif
    return 1;
  }

  if(argc == maxarg) {
    set_seed(argv[seedarg]);
  }

#ifdef DOUBLESIMPLE
  init = [DoubleSimple parseKeepSameClass: argv[2]];
  printf("DoubleSimple parse check. Class: %s, Value: ", [init name]);
  [init writeAccurately: stdout];
  printf("\n");
  [init free];
#endif

  mo(atol(argv[1]));

  return 0;
}

/**
 * mo
 */

void mo(long n) {
#ifdef DOUBLESIMPLE
  DoubleSimple **vector, *dummy;
#else
  double *vector, dummy;
#endif
  long double start_time, finish_time;
  long i, j;
  int fd;
  void *start, *finish;

  // Allocate memory

#ifdef DOUBLESIMPLE
  vector = (DoubleSimple **)malloc(n * sizeof(DoubleSimple *));
  dummy = [DoubleSimple new];
#else
  vector = (double *)malloc(n * sizeof(double));
#endif
  if(vector == NULL) die("memory allocation error: %s\n", strerror(errno));

  // Get the random numbers
  for(i = 0; i < n; i++) {
#ifdef DOUBLESIMPLE
    vector[i] = [DoubleSimple new: drand48()];
#else
    vector[i] = drand48();
#endif
  }

  // Get current process info

  fd = open_process_info();
  start = read_process_info(fd);

  // Operate on the numbers
  for(i = 0; i < n; i++) {
    for(j = 0; j < n; j++) {
#ifdef DOUBLESIMPLE
      [dummy set: vector[i]];
#  if OPERATOR == 0
#  elif OPERATOR == 1
      [dummy add: vector[j]];
#  elif OPERATOR == 2
      [dummy sub: vector[j]];
#  elif OPERATOR == 3
      [dummy mul: vector[j]];
#  elif OPERATOR == 4
      [dummy div: vector[j]];
#  elif OPERATOR == 5
      [dummy lt: vector[j]];
#  elif OPERATOR == 6
      [dummy eq: vector[j]];
#  elif OPERATOR == 7
      [dummy gt: vector[j]];
#  elif OPERATOR == 8
      [dummy ge: vector[j]];
#  elif OPERATOR == 9
      [dummy ne: vector[j]];
#  elif OPERATOR == 10
      [dummy le: vector[j]];
#  else
#    error Unrecognised OPERATOR macro value
#  endif
#else
      dummy =
#  if OPERATOR == 0
	vector[i], vector[j];
#  elif OPERATOR == 1
      (vector[i] + vector[j]);
#  elif OPERATOR == 2
      (vector[i] - vector[j]);
#  elif OPERATOR == 3
      (vector[i] * vector[j]);
#  elif OPERATOR == 4
      (vector[i] / vector[j]);
#  elif OPERATOR == 5
      (vector[i] < vector[j]) ? 1.0 : 0.0;
#  elif OPERATOR == 6
      (vector[i] == vector[j]) ? 1.0 : 0.0;
#  elif OPERATOR == 7
      (vector[i] > vector[j]) ? 1.0 : 0.0;
#  elif OPERATOR == 8
      (vector[i] >= vector[j]) ? 1.0 : 0.0;
#  elif OPERATOR == 9
      (vector[i] != vector[j]) ? 1.0 : 0.0;
#  elif OPERATOR == 10
      (vector[i] <= vector[j]) ? 1.0 : 0.0;
#  else
      (0.0);
#    error Unrecognised OPERATOR macro value
#  endif
#endif
    }
  }

  // Read the current process info

  finish = read_process_info(fd);

  close_process_info(fd);

  // Display process information

  start_time = get_time_stamp(start);
  finish_time = get_time_stamp(finish);

  printf("Time taken for %lld %s operations: %Lg seconds\n",
	 (long long)n * (long long)n,
#if OPERATOR == 0
	 "NOP",
#elif OPERATOR == 1
	 "ADD",
#elif OPERATOR == 2
	 "SUB",
#elif OPERATOR == 3
	 "MUL",
#elif OPERATOR == 4
	 "DIV",
#elif OPERATOR == 5
	 "LT",
#elif OPERATOR == 6
	 "EQ",
#elif OPERATOR == 7
	 "GT",
#elif OPERATOR == 8
	 "GE",
#elif OPERATOR == 9
	 "NE",
#elif OPERATOR == 10
	 "LE",
#else
	 "unrecognised!",
#     error Unrecognised OPERATOR macro value
#endif
	 finish_time - start_time);

  printf("Size of process in Kbytes: %ld\n", get_memory_usage(finish));

  printf("Process info for checking data structure read correctly:\n\t"
	 "Start:\n");

  printf("\t\tPID: %ld, PPID: %ld, UID: %ld, EUID: %ld\n",
	 get_pr_pid(start), get_pr_ppid(start),
	 get_pr_uid(start), get_pr_euid(start));
  printf("\t\tExec: %s, args: %d\n",
	 get_pr_name(start), get_pr_argc(start) - 1);

  printf("\tFinish:\n");
  printf("\t\tPID: %ld, PPID: %ld, UID: %ld, EUID: %ld\n",
	 get_pr_pid(finish), get_pr_ppid(finish),
	 get_pr_uid(finish), get_pr_euid(finish));
  printf("\t\tExec: %s, args: %d\n",
	 get_pr_name(finish), get_pr_argc(finish) - 1);

  // Free the memory

#ifdef DOUBLESIMPLE
  [dummy free];
  for(i = 0; i < n; i++) {
    [vector[i] free];
  }
#endif
  free(vector);
}
