// File: farray.C
//
// Implementation of FArray class

#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "array2.h"
#include "farray.h"

// "Local" functions

static void CrashOnNULL(const void *ptr, char *mesg) ;

static void CrashOnNULL(const void *ptr, char *mesg) {
   if (ptr == NULL) {
      cerr << mesg << endl ;
      exit(1) ;
   }
}

// Default constructor
//
FArray::FArray()

:  fname(NULL)          // init data member fname to NULL

{
#ifndef NDEBUG
   cerr << "FArray default constructor, this = " << this << endl ;
#endif
}


// Alternate constructor
//
FArray::FArray(int n, long seed /* = 0 */ )

:  Array(n, seed),  // init inherited members with base alt constructor
   fname(NULL)      // init data member fname to NULL
{
#ifndef NDEBUG
   cerr << "FArray alternate constructor, this = " << this << endl ;
#endif
}


// Destructor
//
FArray::~FArray() {

#ifndef NDEBUG
   cerr << "FArray destructor, this = " << this << endl ;
#endif

   if (fname != NULL) free(fname) ;
}


// Copy Constructor
//
FArray::FArray(const FArray& B)

:  Array(B),        // init inherited members with base copy constructor
   fname(NULL)      // init data member fname to NULL

{
#ifndef NDEBUG
   cerr << "FArray copy constructor, this = " << this << endl ;
#endif

   if (fname != NULL) free(fname) ;

   if (B.fname != NULL) {
      fname = strdup(B.fname) ;
   } else {
      fname = NULL ;
   }
}


// Assignment
//
FArray& FArray::operator=(const FArray& B) {

   // self assignment?
   if (this == &B) return *this ;

   Array::operator=(B) ;    // do assignment for inherited members

   if (fname != NULL) free(fname) ;

   if (B.fname != NULL) {
      fname = strdup(B.fname) ;
      CrashOnNULL(fname, "Out of memory in FArray assignment!") ;
   } else {
      fname = NULL ;
   }

   return *this ;
}


// Constructor from a file
//
FArray::FArray(const char *ifname) {
   int r ;
   struct stat sbuf ;
   ifstream ifile ;

#ifndef NDEBUG
   cerr << "FArray file constructor, this = " << this << endl ;
#endif

   // compute array size
   //
   CrashOnNULL(ifname, "Bad input file name") ;
   fname = strdup(ifname) ;
   CrashOnNULL(fname, "Can't copy input filename!") ;

   r = stat(ifname, &sbuf) ;
   if (r != 0) {
      cerr << "Cannot get file size for file: " << ifname << endl ;
      exit(1) ;
   }
   size = sbuf.st_size / sizeof(DATA) ;

   ifile.open(ifname) ;
   CrashOnNULL(ifile, "Could not open input file") ;

   arr = new DATA[size] ;
   CrashOnNULL(arr, "Out of memory in FArray file constructor!") ;

   // read in the data
   ifile.read((char *) arr, size * sizeof(DATA)) ;

   ifile.close()  ;
}


// Write array as a binary file
//
bool FArray::write(const char* ofname) {
   ofstream ofile ;

   if (ofname == NULL) return false ;

   ofile.open(ofname) ;
   CrashOnNULL(ofname, "Could not write to output file") ;

   ofile.write((char *) arr, size * sizeof(DATA)) ;

   if (ofile.good()) {
      ofile.close() ;
      return true ;
   } else {
      ofile.close() ;
      return false ;
   }
}


// Perform binary search
//
int FArray::bsearch(DATA key) {
   return bsearch(key, 0, size - 1) ;
}

int FArray::bsearch(DATA key, int low, int high) {

   // Search in empty section?
   if (low > high) return -1 ;

   int mid = (low + high) / 2 ;

   if (arr[mid] == key) {
      return mid ;
   } else if (arr[mid] < key) {
      return bsearch(key, mid+1, high) ;
   } else {
      return bsearch(key, low, mid-1) ;
   }
}


void FArray::sort() {
   quicksort(0, size-1) ;
}


int FArray::partition(int low, int high) {
   DATA x, temp ;
   int i, j ;

   i = low - 1;
   j = high + 1;
   x = arr[low] ;

   while (true) {

      // Find an element less than x
      do {
         j = j - 1;
      } while (arr[j] > x) ;

      // Find an element greater than x
      do {
         i = i + 1;
      } while (arr[i] < x);

      // swap smaller and bigger elements, if needed
      //
      if (i < j) {
         temp = arr[j] ;
         arr[j] = arr[i] ;
         arr[i] = temp ;
      } else {
         return j;
      }
    }
}


void FArray::quicksort(int low, int high) {
   int q ;

   if (low >= high) return ;

   q = partition(low, high) ;
   assert(q < high) ;

   quicksort(low, q) ;
   quicksort(q + 1, high) ;
}
