// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

#ifndef T_AR_SUNH
#define T_AR_SUNH

#include <defines.h>
#include <DREstring.h>

#include <stdarg.h>

// some defines so that we can use macros to call templates indirectly
// and thereby accommodate the Mac's templateless implementation
#define BIG_ARRAY(x) big_array<x>
#define BIG_2_ARRAY(x) big_2_array<x>
#define BIG_4_ARRAY(x) big_4_array<x>

// a class for large arrays.
// big_arrays are easy under UNIX

template <class T> class big_array
{ boolean _auto_resize;
  uint32 _size;
  T* _tpp;
  
public:

// constructor
  big_array(const uint32 size = 0)
  { _size = size;
      heap_check(_tpp = new T [_size]);
  }
   
// destructor
  ~big_array(void)
  { destroy_array(_tpp);
  }

// set all elements to a single value
  void set_value(const T value)
  { for (int n = 0; n < _size; n++)
    _tpp[n] = (T)value;
  }

// switch on auto resizing
  void auto_resize(void)
    { _auto_resize = true; }

// switch off auto resizing
  void no_auto_resize(void)
    { _auto_resize = false; }

// return the size of the array
  uint32 size(void)
    { return _size; }

// resize the array
  void resize(const uint32 new_size)
  { // how much of the array can we preserve?
    const uint32 smaller_size = MIN(_size, new_size);

    T* temp;
    heap_check(temp = new T [new_size]);
    for (uint32 n = 0; n < smaller_size; n++)
      temp[n] = _tpp[n];
    
    destroy_array(_tpp);

    _size = new_size;
    _tpp = temp;
  }

// retrieve an element
  T& operator[](const uint32 n) 
  { if (n >= _size) then
    { if (_auto_resize) then
      { const uint32 new_size = MAX(n, 2 * _size);
        resize(new_size);
      }
      else
        fatal_error("big_array index out of bounds");
    }
    return _tpp[n];
  }
};

// a class for large multidimensional arrays

template <class T> class big_multi_array
{
protected:
  int _n_dimensions;
  int* _dimensions;
  big_array<T> _array;

public:
// constructor
  big_multi_array(const uint n_dimensions, ...)
  { va_list ap;
    va_start(ap, n_dimensions);
    _n_dimensions = n_dimensions;
    heap_check(_dimensions = new int [n_dimensions]);
    uint32 product = 1;
    for (int n = 0; n < n_dimensions; n++)
    { _dimensions[n] = va_arg(ap, const uint);
      product *= _dimensions[n];
    }
    va_end(ap);
    _array.resize(product);
  }

// destructor
  ~big_multi_array(void)
  { destroy_array(_dimensions); }

// set every element to zero
  void set_value(const T value)
  { _array.set_value(value); }

// a function used only in DOS
void freeze(void)
{ }

// return a reference to an element. Unfortunately, for no reason of which I
// am aware, one cannot declare an operator[](...). This is very slow (which
// is a good reason not to use this template class unless one really needs
// it). On the other hand, it is nice to be able to handle _all_ kinds of
// multidimensional array with a single routine.
  T& element(const uint element, ...)
  { int* _elements;
    heap_check(_elements = new int [_n_dimensions]);
    va_list ap;
    va_start(ap, element);
    _elements[0] = element;
    for (int n = 1; n < _n_dimensions; n++)
      _elements[n] = va_arg(ap, const uint);
    va_end(ap);
    for (n = 0; n < _n_dimensions; n++)
      if ((_elements[n] < 0) || (_elements[n] >= _dimensions[n])) then
	fatal_error((DREstring)"Element out of range in big_multi_array; "
	+ (DREstring)" dimension number " + DREstring10(n) + (DREstring)", value "
	+ DREstring10(_elements[n]) + (DREstring)", bound "
	+ DREstring10(_dimensions[n]));
// we store in order of rightmost index contiguous
    uint index = 0;
    for (n = 0; n < _n_dimensions - 1; n++)
      index = (index + _elements[n]) * _dimensions[n + 1];
    index += _elements[_n_dimensions - 1];
    destroy_array(_elements);
    return _array[index];
  }
};

// faster versions for specific sizes
template <class T> class big_2_array : public big_multi_array<T>
{
public:
  big_2_array(const uint d1 = 0, const uint d2 = 0) :
    big_multi_array<T>(2, d1, d2)
  { }

  T& element(const uint element0, const uint element1) 
  { if ((element0 > _dimensions[0]) || (element1 > _dimensions[1])) then
      fatal_error("Index out of bounds");

// we store in order of rightmost index contiguous
    uint index = element0 * _dimensions[1] + element1;
    return _array[index];
  }
};

template <class T> class big_4_array : public big_multi_array<T>
{
public:
  big_4_array(const uint d1 = 0, const uint d2 = 0, const uint d3 = 0, const uint d4 = 0) :
    big_multi_array<T>(4, d1, d2, d3, d4)
  { }

  T& element(const uint element0, const uint element1, const uint element2, const uint element3)
  { if ((element0 > _dimensions[0]) || (element1 > _dimensions[1]) ||
	(element2 > _dimensions[2]) || (element3 > _dimensions[3])) then
      fatal_error("Index out of bounds");

// we store in order of rightmost index contiguous
    uint index = ((element0 * _dimensions[1] + element1) * _dimensions[2] + element2)
                   * _dimensions[3] + element3;
    return _array[index];
  }
};

#endif

