cube.h

#ifndef __SW_CUBE_H__
#define __SW_CUBE_H__
 
 
#include <iostream>
//#include <iomanip>
//#include <chrono>
#include <ctime>
#include <random>
#include <vector>
 
// Uncomment to disable assert().
// #define NDEBUG
#include <cassert>
 
 
namespace SW {
 
 
/*
enum fill_type
{
  fill::zeros,
  fill::ones,
  fill::eye,
  fill::randu,
  fill::randn,
  fill::none
};
*/
 
namespace ORDER_TYPE {
  enum order_types
  {
    CDR,
    DCR,
    DRC,
    CRD,
    RCD,
    RDC
  };
}
 
 
namespace FILL_TYPE {
  enum fill_types
  {
    ZEROS,                             // Set all elements to 0.
    ONES,                              // Set all elements to 1.
    TWOS,                              // Set all elements to 2.
    EYE,                               // Set the elements along the main diagonal to 1 and off-diagonal elements to 0.
    RAND,                              // Set each element to a random value.
    RANDU,                             // Set each element to a random value from a uniform distribution in the [0,1] interval.
    RANDN,                             // Set each element to a random value from a normal/Gaussian distribution with zero mean and unit variance.
    SEQUENTIAL,                        // Set each element to the next sequential value.
    HIGHER,                            // Set each element to a higher value than the previous element.
    LOWER,                             // Set each element to a lower value than the previous element.
    NONE                               // Do not modify the elements.
  };
}
 
 
// Default random engine.
int RandRange(int min_value, int max_value)
{
  std::default_random_engine generator;
  std::uniform_int_distribution<int> distribution(min_value, max_value);
  auto random_value = distribution(generator);  // generates number in the range 1..6
 
  return random_value;
}
 
 
// Default random engine.
double RandRangeNormal(double min_value, double max_value)
{
  std::default_random_engine generator;
  std::uniform_real_distribution<double> distribution(min_value, max_value);
  auto random_value = distribution(generator);  // generates number in the range 1..6
 
  return random_value;
}
 
 
// Mersenne Twister 19937 generator.
double RandRangeMersenneTwister(double min_value, double max_value)
{
  std::random_device rd;     // only used once to initialise (seed) engine.
  std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case).
  std::uniform_real_distribution<double> uni(min_value, max_value); // guaranteed unbiased.
 
  auto random_value = uni(rng);
 
  return random_value;
}
 
 
// Mersenne Twister 19937 generator.
int RandRange3(int min_value, int max_value)
{
  std::random_device rd;     // only used once to initialise (seed) engine.
  std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case).
  std::uniform_int_distribution<int> uni(min_value, max_value); // guaranteed unbiased.
 
  auto random_value = uni(rng);
 
  return random_value;
}
 
 
// C / C++ standard library generator
// The following is the standard C / C++ generator defined in <cstdlib>.Beware: on some machines it has
// an unacceptably short period!!
// Program 1 : http ://www.physics.buffalo.edu/phy410-505/topic2/generators.cpp
/*
double rand(unsigned& seed, bool set)
{
if (set)
srand(seed);
else
seed = std::rand();
return seed / (RAND_MAX + 1.0);
}
*/
 
 
/*
// Park - Miller algorithm.
// The following Park - Miller generator is very popular and widely used.It produces sequences of high
// quality and has a long period 231 - 1 = 2147483647.  The following implementation uses Schrage's 
// algorithm, which works on old machines with 16 - bit int's.
// Program 1: http ://www.physics.buffalo.edu/phy410-505/topic2/generators.cpp
double ranf(unsigned& seed, bool set)
{
const int IA = 16807, IC = 2147483647, IQ = 127773, IR = 2836;
int iseed = seed;
if (iseed <= 0)
iseed += IC;
if (!set) {
int h = iseed / IQ;
int l = iseed % IQ;
iseed = IA * l - IR * h;
}
if (iseed <= 0)
iseed += IC;
return (seed = iseed) / double(IC);
}
*/
 
/*
 
 
// Quick and dirty algorithm
// The following implements the Quick and Dirty algorithm from Numerical Recipes[1].
// Program 1: http ://www.physics.buffalo.edu/phy410-505/topic2/generators.cpp
double ranq(unsigned& seed, bool set)
{
static unsigned long idum;
const double TWO_POWER_32 = 4294967296.0;
if (set) {
idum = (unsigned long)seed;
return idum / TWO_POWER_32;
}
idum = 1664525L * idum + 1013904223L;
seed = int(idum);
return idum / TWO_POWER_32;
}
*/
 
/*
// Marsaglia Xorshift algorithm
// The new Third Edition of Numerical Recipes proposes a high-quality generator based on the Xorshift
// algorithms of Marsaglia[5]. It uses 64-bit unsigned integer arithmetic that is standard on all modern
// machines.  It has an extremely long period ~ 3.13857.  Note the use of C/C++ bitwise xor (^) and right
// shift (>>) operators.
// Program 1: http://www.physics.buffalo.edu/phy410-505/topic2/generators.cpp
#ifdef _MSC_VER // Microsoft C++
typedef unsigned __int64 Ullong; // 64-bit unsigned integer
#else // Macintosh, Linux
typedef unsigned long long Ullong;
#endif
inline Ullong int64(Ullong& u, Ullong& v, Ullong& w) {
u = u * 2862933555777941757LL + 7046029254386353087LL;
v ^= v >> 17; v ^= v << 31; v ^= v >> 8;
w = 4294957665U * (w & 0xffffffff) + (w >> 32);
Ullong x = u ^ (u << 21); x ^= x >> 35; x ^= x << 4;
return (x + v) ^ w;
}
double ranx(unsigned& seed, bool set) {
static Ullong u = 0, v = 4101842887655102017LL, w = 1;
if (set) {
Ullong j = seed;
v = 4101842887655102017LL;
w = 1;
u = j ^ v;
int64(u, v, w);
v = u;
int64(u, v, w);
w = v;
seed = int(int64(u, v, w));
}
else {
6Ullong x = int64(u, v, w);
seed = unsigned(x);
return 5.42101086242752217E-20 * x;
}
}
*/
 
/*
#define ranf() ((float) rand() / (float) RAND_MAX)
float ranfGauss(int m, float s)
{
  static int pass = 0;
  static float y2;
  float x1, x2, w, y1;
 
  if (pass)
  {
    y1 = y2;
  }
  else  {
    do {
      x1 = 2.0f * ranf() - 1.0f;
      x2 = 2.0f * ranf() - 1.0f;
      w = x1 * x1 + x2 * x2;
    } while (w >= 1.0f);
 
    w = (float)sqrt(-2.0 * log(w) / w);
    y1 = x1 * w;
    y2 = x2 * w;
  }
  pass = !pass;
 
  return ((y1 * s + (float)m));
}
*/
 
/*
// Gaussian White Noise.
#define PI 3.1415926536f
 
float R1 = (float) rand() / (float) RAND_MAX;
float R2 = (float) rand() / (float) RAND_MAX;
 
float X = (float) sqrt( -2.0f * log( R1 )) * cos( 2.0f * PI * R2 );
*/
 
 
 
/*
// set the initial seed to whatever you like
static int RandSeed = 1;
 
// using rand() (16bit precision)
// takes about 110 seconds for 2 billion calls
float RandFloat1()
{
return ((float)rand()/RAND_MAX) * 2.0f - 1.0f;
}
 
// direct implementation of rand() (16 bit precision)
// takes about 32 seconds for 2 billion calls
float RandFloat2()
{
return ((float)(((RandSeed = RandSeed * 214013L + 2531011L) >> 16) & 0x7fff)/RAND_MAX) * 2.0f - 1.0f;
}
 
// fast rand float, using full 32bit precision
// takes about 12 seconds for 2 billion calls
float Fast_RandFloat()
{
RandSeed *= 16807;
return (float)RandSeed * 4.6566129e-010f;
}
*/
 
/*
// Gaussian random numbers
// This algorithm (adapted from "Natur als fraktale Grafik" by
// Reinhard Scholl) implements a generation method for gaussian
// distributed random numbers with mean=0 and variance=1
// (standard gaussian distribution) mapped to the range of
// -1 to +1 with the maximum at 0.
// For only positive results you might abs() the return value.
// The q variable defines the precision, with q=15 the smallest
// distance between two numbers will be 1/(2^q div 3)=1/10922
// which usually gives good results.
 
// Note: the random() function used is the standard random
// function from Delphi/Pascal that produces *linear*
// distributed numbers from 0 to parameter-1, the equivalent
// C function is probably rand().
 
Code :
const q=15;
c1=(1 shl q)-1;
c2=(c1 div 3)+1;
c3=1/c1;
 
function GRandom:single;
begin
result:=(2*(random(c2)+random(c2)+random(c2))-3*(c2-1))*c3;
end;
*/
 
 
 
 
// Calculate pseudo-random 32 bit number based on linear congruential method.
unsigned long GenerateRandomNumber(void)
{
  // Change this for different random sequences.
  static unsigned long randSeed = 22222;
  randSeed = (randSeed * 196314165) + 907633515;
  return randSeed;
}
 
 
 
// Mapping of 32768 integers.
int RandN(int min_value, int max_value)
{
  int result = min_value + (rand() % (int)(max_value - min_value + 1));
 
  return result;
}
 
 
unsigned long _Randseed = 1;
int randN(int seed)
{
  _Randseed = _Randseed * 1103515245 + seed;
  return ((unsigned int)(_Randseed >> 16) & RAND_MAX);
}
 
 
/*
int RandU(int min_value, int max_value)
{
  return min_value + (int)((double)rand() / (RAND_MAX + 1) * (max_value - min_value + 1));
}
*/
int RandU(int min_value, int max_value)
{
  return ((65539 * rand()) % (2 ^ 31));
 
  //return ((65539 * seed) & 0x7FFFFFFF);
}
 
 
// For 1st result use a random seed.
// For next results use prev result as the seed.
int RandU(int seed)
{
  int result = (25179 * seed + 13849) % 101;
  return result;
}
 
const int HALF_RAND_MAX = RAND_MAX / 2;
float randu() 
{
  // Generates a random float uniformly between [-1.0, 1.0) using RAND_MAX
  return ((float)rand()) / (HALF_RAND_MAX)-1;
}
 
 
 
int MinMax(int a, int b, int c)
{
  if (b < a)
    return a;
  else if (b > c)
    return c;
  else
    return b;
}
 
 
/*
// A simple taylor series, but optimized for integer phase.
// phase is in 0 -> (2^32)-1 range and maps to 0 -> ~2PI
float fastsin(UINT32 phase)
{
  const float frf3 = -1.0f / 6.0f;
  const float frf5 = 1.0f / 120.0f;
  const float frf7 = -1.0f / 5040.0f;
  const float frf9 = 1.0f / 362880.0f;
  const float f0pi5 = 1.570796327f;
  float x, x2, asin;
  UINT32 tmp = 0x3f800000 | (phase >> 7);
  if (phase & 0x40000000)
    tmp ^= 0x007fffff;
  x = (*((float*)&tmp) - 1.0f) * f0pi5;
  x2 = x * x;
  asin = ((((frf9 * x2 + frf7) * x2 + frf5) * x2 + frf3) * x2 + 1.0f) * x;
  return (phase & 0x80000000) ? -asin : asin;
}
 
// A simple taylor series, but optimized for integer phase.
// fastsin2 is less accurate than fastsin.
// phase is in 0 -> (2^32)-1 range and maps to 0 -> ~2PI
float fastsin2(UINT32 phase)
{
  const float frf3 = -1.0f / 6.0f;
  const float frf5 = 1.0f / 120.0f;
  const float frf7 = -1.0f / 5040.0f;
  const float f0pi5 = 1.570796327f;
  float x, x2, asin;
  UINT32 tmp = 0x3f800000 | (phase >> 7);
  if (phase & 0x40000000)
    tmp ^= 0x007fffff;
  x = (*((float*)&tmp) - 1.0f) * f0pi5;
  x2 = x * x;
  asin = (((frf7 * x2 + frf5) * x2 + frf3) * x2 + 1.0f) * x;
  return (phase & 0x80000000) ? -asin : asin;
}
*/
 
 
/*
// C++ gaussian noise generation
#include <cstdlib>
#include <ctime>
 
// Generate a new random seed from system time - do this once in your constructor.
srand(time(0));
 
// Setup constants.
const static int q = 15;
const static float c1 = (1 << q) - 1;
const static float c2 = ((int)(c1 / 3)) + 1;
const static float c3 = 1.f / c1;
 
// random number in range 0 - 1 not including 1.
float random = 0.f;
 
// the white noise.
float noise = 0.f;
 
for (int i = 0; i < numSamples; i++)
{
  random = ((float)rand() / (float)(RAND_MAX + 1));
  noise = (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3;
}
*/
 
/*
// intended for 32-bit floating point numbers only and should work a bit faster than the regular ones.
 
// fastabs() gives you the absolute value of a float
float fastabs(float f)
{int i=((*(int*)&f)&0x7fffffff);return (*(float*)&i);}
 
// fastneg() gives you the negative number (faster than multiplying with -1)
float fastneg(float f)
{int i=((*(int*)&f)^0x80000000);return (*(float*)&i);}
 
// fastsgn() gives back +1 for 0 or positive numbers, -1 for negative numbers
int fastsgn(float f)
{return 1+(((*(int*)&f)>>31)<<1);}
*/
 
/*
// uses IEEE 32-bit floating point representation to
// quickly compute approximations to the log2 of a value.
//
// Both functions return under-estimates of the actual value, although the second flavour is less of an under-estimate than the first.
//
// Try examples of 0.1, 1, 2, 5, 100.
int floorOfLn2( float f ) {
assert( f > 0. );
assert( sizeof(f) == sizeof(int) );
assert( sizeof(f) == 4 );
return (((*(int *)&f)&0x7f800000)>>23)-0x7f;
}
 
float approxLn2( float f ) {
assert( f > 0. );
assert( sizeof(f) == sizeof(int) );
assert( sizeof(f) == 4 );
int i = (*(int *)&f);
return (((i&0x7f800000)>>23)-0x7f)+(i&0x007fffff)/(float)0x800000;
}
*/
 
/*
// fastpow(f,n) gives a rather *rough* estimate of a float number f to the power of an integer number n (y=f^n). It is fast but result can be quite a bit off, since we directly mess with the floating point exponent.-> use it only for getting rough estimates of the values and where precision is not that important.
float fastpower(float f,int n)
{
long *lp,l;
lp=(long*)(&f);
l=*lp;l-=0x3F800000l;l<<=(n-1);l+=0x3F800000l;
*lp=l;
return f;
}
 
// fastroot(f,n) gives the n-th root of f. Same thing concerning precision applies here.
float fastroot(float f,int n)
{
long *lp,l;
lp=(long*)(&f);
l=*lp;l-=0x3F800000l;l>>=(n-1);l+=0x3F800000l;
*lp=l;
return f;
}
*/
 
 
 
 
 
template <typename T> class Cube
{
private:
  std::vector<std::vector<std::vector<T> > > data;
  mutable unsigned rows;               // Width.
  mutable unsigned cols;               // Height.
  mutable unsigned depth;              // Depth.
 
  FILL_TYPE::fill_types fill_type;
  ORDER_TYPE::order_types order_type;
 
public:
  Cube();
  Cube(unsigned _rows, unsigned _cols, unsigned _depth);
  Cube(unsigned _rows, unsigned _cols, unsigned _depth, FILL_TYPE::fill_types _fill_type);
  Cube(unsigned _rows, unsigned _cols, unsigned _depth, FILL_TYPE::fill_types _fill_type, ORDER_TYPE::order_types _order_type);
  Cube(unsigned _rows, unsigned _cols, unsigned _depth, ORDER_TYPE::order_types _order_type);
  ~Cube();
 
  Cube(const Cube<T>& rhs);
 
 
  // Arithmetic operators and Relational operators
  const Cube<T>& operator = (const Cube<T> &);  // Assignment operator.
 
  // Operator overloading, for comparitive operations.
  bool operator==(const Cube<T>& rhs) const;   // Comparison operator.
  bool operator!=(const Cube<T>& rhs) const;   // 
  bool operator<(const Cube<T>& rhs) const;
  bool operator<=(const Cube<T>& rhs) const;
  bool operator>(const Cube<T>& rhs) const;
  bool operator>=(const Cube<T>& rhs) const;
 
  // Arithmetic operators.
  Cube<T> operator - () const;         // Negate  operator.
  Cube<T> operator ++ ();              // Prefix  increment operator.
  Cube<T> operator ++ (int);           // Postfix increment operator.
  Cube<T> operator -- ();              // Prefix  decrement operator.
  Cube<T> operator -- (int);           // Postfix decrement operator.
 
  // Mathematical operations.
  Cube<T> operator + (const Cube<T> &);
  Cube<T> operator += (const Cube<T> &);
  Cube<T> operator - (const Cube<T> &);
  Cube<T> operator -= (const Cube<T> &);
  Cube<T> operator * (const Cube<T> &);
  Cube<T> operator *= (const Cube<T> &);
  Cube<T> operator / (const Cube<T> &);
  Cube<T> operator /= (const Cube<T> &);
  Cube<T> operator % (const Cube<T> &);
  Cube<T> operator %= (const Cube<T> &);
  Cube<T> operator ^ (const Cube<T> &);
  Cube<T> operator ^= (const Cube<T> &);
 
  // Scalar operations.
  Cube<T> operator+(const T& rhs);
  Cube<T> operator+=(const T& rhs);
  Cube<T> operator-(const T& rhs);
  Cube<T> operator-=(const T& rhs);
  Cube<T> operator*(const T& rhs);
  Cube<T> operator*=(const T& rhs);
  Cube<T> operator/(const T& rhs);
  Cube<T> operator/=(const T& rhs);
  Cube<T> operator%(const T& rhs);
  Cube<T> operator%=(const T& rhs);
  Cube<T> operator^(const T& rhs);
  Cube<T> operator^=(const T& rhs);
 
  // Access the individual elements.
  T& operator()(const unsigned& row, const unsigned& col, const unsigned& depth);
  const T& operator()(const unsigned& row, const unsigned& col, const unsigned& depth) const;
 
  void fill();
  void fill(T value);
  void fill_higher(T seq = 0, T min_inc = 1);
  void fill_lower(T seq = 0, T min_inc = 1);
  void fill_sequentially(T seq = 0, T inc = 1);
 
  // Access the row, column and depth sizes.
  unsigned getCols() const;
  unsigned getDepth() const;
  FILL_TYPE::fill_types getFillType() const;
  ORDER_TYPE::order_types getOrderType() const;
  unsigned getRows() const;
  unsigned getSize() const;
  unsigned getVolume();
  T& max();
  T& min();
  void print(std::ostream& os = cout);
  void randomize();
  void read(std::istream& is = cin);
  void sequence(T seq, T inc=1);
  T& sum();
 
  friend Cube<T> operator + (const Cube<T> &, const Cube<T> &);
  friend Cube<T> operator - (const Cube<T> &, const Cube<T> &);
  friend Cube<T> operator * (const Cube<T> &, const Cube<T> &);
  friend Cube<T> operator / (const Cube<T> &, const Cube<T> &);
  friend Cube<T> operator % (const Cube<T> &, const Cube<T> &);
  friend Cube<T> operator ^ (const Cube<T> &, const Cube<T> &);
 
  friend int operator == (const Cube<T> &, const Cube<T> &);
  friend int operator != (const Cube<T> &, const Cube<T> &);
  friend int operator <  (const Cube<T> &, const Cube<T> &);
  friend int operator <= (const Cube<T> &, const Cube<T> &);
  friend int operator >  (const Cube<T> &, const Cube<T> &);
  friend int operator >= (const Cube<T> &, const Cube<T> &);
 
  // Other functions
  friend Cube<T> abs(const Cube<T> &);
  friend double div(const Cube<T> &, const Cube<T> &);
  friend unsigned size(const Cube<T> &);
  friend Cube<T> sqrt(const Cube<T> &);
  friend Cube<T> pow(const Cube<T> &, const Cube<T> &);
 
 
  // I/O stream functions
  //template<typename T>
  friend std::ostream & operator << (std::ostream &, const Cube<T> &);
 
  //template<typename T>
  friend std::istream & operator >> (std::istream &, Cube<T> &);
};
 
 
 
// Basic Constructor.
template<typename T>
Cube<T>::Cube()
{
  rows = 0;
  cols = 0;
  depth = 0;
  fill_type = FILL_TYPE::NONE;
  order_type = ORDER_TYPE::CDR;
}
 
 
// Parameter Constructor.
template<typename T>
Cube<T>::Cube(unsigned _rows, unsigned _cols, unsigned _depth)
{
  data.resize(_rows);
  for (unsigned i = 0; i<data.size(); i++)
  {
    data[i].resize(_cols);
    for (unsigned j = 0; j < data[i].size(); j++)
    {
      data[i][j].resize(_depth);
    }
  }
 
  rows = _rows;
  cols = _cols;
  depth = _depth;
  fill_type = FILL_TYPE::NONE;
  order_type = ORDER_TYPE::CDR;
}
 
 
// Parameter Constructor.
template<typename T>
Cube<T>::Cube(unsigned _rows, unsigned _cols, unsigned _depth, FILL_TYPE::fill_types _fill_type)
{
  data.resize(_rows);
  for (unsigned i = 0; i<data.size(); i++)
  {
    data[i].resize(_cols);
    for (unsigned j = 0; j < data[i].size(); j++)
    {
      data[i][j].resize(_depth);
    }
  }
 
  rows = _rows;
  cols = _cols;
  depth = _depth;
  fill_type = _fill_type;
  order_type = ORDER_TYPE::CDR;
 
  fill();                              // Populate grid depending on FILLS type.
}
 
 
 
// Parameter Constructor.
template<typename T>
Cube<T>::Cube(unsigned _rows, unsigned _cols, unsigned _depth, FILL_TYPE::fill_types _fill_type, ORDER_TYPE::order_types _order_type)
{
  data.resize(_rows);
  for (unsigned i = 0; i<data.size(); i++)
  {
    data[i].resize(_cols);
    for (unsigned j = 0; j < data[i].size(); j++)
    {
      data[i][j].resize(_depth);
    }
  }
 
  rows = _rows;
  cols = _cols;
  depth = _depth;
  fill_type = _fill_type;
  order_type = _order_type;
 
  fill();                              // Populate grid depending on FILLS type.
}
 
 
// Parameter Constructor.
template<typename T>
Cube<T>::Cube(unsigned _rows, unsigned _cols, unsigned _depth, ORDER_TYPE::order_types _order_type)
{
  data.resize(_rows);
  for (unsigned i = 0; i<data.size(); i++)
  {
    data[i].resize(_cols);
    for (unsigned j = 0; j < data[i].size(); j++)
    {
      data[i][j].resize(_depth);
    }
  }
 
  rows = _rows;
  cols = _cols;
  depth = _depth;
  fill_type = FILL_TYPE::none;
  order_type = _order_type;
}
 
 
// Destructor.
template <typename T>
Cube<T>::~Cube()
{
  // does nothing
}
 
 
// Copy Constructor.
template<typename T>
Cube<T>::Cube(const Cube<T>& rhs)
{
  data = rhs.data;
  rows = rhs.getRows();
  cols = rhs.getCols();
  depth = rhs.getDepth();
  fill_type = rhs.getFillType();
  order_type = rhs.getOrderype();
}
 
 
// Assignment Operator.
template<typename T>
const Cube<T>& Cube<T>::operator=(const Cube<T>& rhs)
{
  if (&rhs == this)
    return *this;
 
  unsigned new_cols = rhs.getCols();
  unsigned new_depth = rhs.getDepth();
  unsigned new_rows = rhs.getRows();
  bool new_fill_type = rhs.getFillType();
  bool new_order_type = rhs.getOrderType();
 
  data.resize(new_rows);
  for (unsigned i = 0; i < data.size(); i++)
  {
    data[i].resize(new_cols);
    for (unsigned i = 0; i < data.size(); i++)
    {
      data[i][j].resize(new_depth);
    }
  }
  /*
  for (unsigned i = 0; i<new_rows; i++)
  {
    for (unsigned j = 0; j<new_cols; j++)
    {
      data[i][j] = rhs(i, j);
    }
  }
  */
  for (unsigned i = 0; i<new_rows; i++)
  {
    for (unsigned j = 0; j<new_cols; j++)
    {
      data[i][j] = rhs(i, j);
 
      for (unsigned k = 0; k < new_depth; k++)
      {
        data[i][j][k] = rhs(i, j, k);
      }
    }
  }
 
  rows = new_rows;
  cols = new_cols;
  depth = new_depth;
  fill_type = new_fill_type;
  order_type = new_order_type;
 
  return *this;
}
 
 
// Comparison Operator ==.
template<typename T>
bool Cube<T>::operator==(const Cube<T>& rhs) const
{
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
  unsigned rows = rhs.getRows();
  fill_types fill_type = rhs.getFillType();
  order_types order_type = rhs.getOrderType();
 
  if (getCols() != cols)
    return false;
 
  if (getDepth() != depth)
    return false;
 
  if (getRows() != rows)
    return false;
 
  if (getFillType() != fill_type)
    return false;
 
  if (getOrderType() != order_type)
    return false;
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (this->data[i][j][k] != rhs(i, j, k))
          return false;
      }
    }
  }
 
  return true;
}
 
 
// Comparison Operator !=.
template<typename T>
bool Cube<T>::operator!=(const Cube<T>& rhs) const
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
  fill_types fill_type = rhs.getFillType();
  order_types order_type = rhs.getOrderType();
 
  if (getCols() != cols)
    return true;
 
  if (getDepth() != depth)
    return true;
 
  if (getRows() != rows)
    return true;
 
  if (getFillType() != fill_type)
    return true;
 
  if (getOrderType() != order_type)
    return true;
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (this->data[i][j][k] == rhs(i, j, k))
          return false;
      }
    }
  }
 
  return false;
}
 
 
// Comparison Operator <.
//
// If every cell is < than rhs.
template<typename T>
bool Cube<T>::operator<(const Cube<T>& rhs) const
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  if (getCols() != cols)
    return false;
 
  if (getDepth() != depth)
    return false;
 
  if (getRows() != rows)
    return false;
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (this->data[i][j][k] >= rhs(i, j, k))
          return false;
      }
    }
  }
 
  return true;
}
 
 
// Comparison Operator <=.
//
// If every cell is <= than rhs.
template<typename T>
bool Cube<T>::operator<=(const Cube<T>& rhs) const
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  if (getRows() != rows)
    return false;
 
  if (getCols() != cols)
    return false;
 
  if (getDepth() != depth)
    return false;
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (this->data[i][j][k] > rhs(i, j, k))
          return false;
      }
    }
  }
 
  return true;
}
 
 
// Comparison Operator <.
//
// If every cell is > than rhs.
template<typename T>
bool Cube<T>::operator>(const Cube<T>& rhs) const
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  if (getRows() != rows)
    return false;
 
  if (getCols() != cols)
    return false;
 
  if (getDepth() != depth)
    return false;
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (this->data[i][j][k] <= rhs(i, j, k))
          return false;
      }
    }
  }
 
  return true;
}
 
 
// Comparison Operator >=.
//
// If every cell is >= than rhs.
template<typename T>
bool Cube<T>::operator>=(const Cube<T>& rhs) const
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  if (getRows() != rows)
    return false;
 
  if (getCols() != cols)
    return false;
 
  if (getDepth() != depth)
    return false;
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (this->data[i][j][k] < rhs(i, j, k))
          return false;
      }
    }
  }
 
  return true;
}
 
 
// Unary - operator.
template<typename T>
Cube<T> Cube<T>::operator -() const
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        if (result(i, j, k) != 0)
          result(i, j, k) = -this->data[i][j][k];
      }
    }
  }
 
  return result;
}
 
 
// Prefix increment operator.
template<typename T>
Cube<T> Cube<T>::operator ++ ()
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = this->data[i][j][k] + 1;
      }
    }
  }
 
  return *this = result;
}
 
 
// Postfix increment operator.
template<typename T>
Cube<T> Cube<T>::operator ++ (int)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = this->data[i][j][k] + 1;
      }
    }
  }
 
  return result;
}
 
 
// Prefix decrement operator.
template<typename T>
Cube<T> Cube<T>::operator -- ()
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = this->data[i][j][k] - 1;
      }
    }
  }
 
  return *this = result;
}
 
 
// Postfix decrement operator.
template<typename T>
Cube<T> Cube<T>::operator -- (int)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = this->data[i][j][k] - 1;
      }
    }
  }
 
  return result;
}
 
 
// Addition of two Cubes.
template<typename T>
Cube<T> Cube<T>::operator+(const Cube<T>& rhs)
{
  float new_rows = (x > rhs.getRows()) ? x : rhs.getRows();
  float new_cols = (y > rhs.getCols()) ? y : rhs.getCols();
  float new_depth = z + rhs.getDepth();
  Cube result(new_rows, new_cols, new_depth);
 
  for (unsigned i = 0; i<new_rows; i++)
  {
    for (unsigned j = 0; j<new_cols; j++)
    {
      for (unsigned k = 0; k<new_depth; k++)
      {
        //unsigned temp_i = 
        result(i, j, k) = this->data[i][j][k] + rhs(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
// Cumulative addition of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator+=(const Cube<T>& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        this->data[i][j][k] += rhs(i, j, k);
      }
    }
  }
 
  return *this;
}
 
 
// Subtraction of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator-(const Cube<T>& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = this->data[i][j][k] - rhs(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
// Cumulative subtraction of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator-=(const Cube<T>& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        this->data[i][j][k] -= rhs(i, j, k);
      }
    }
  }
 
  return *this;
}
 
 
// Left multiplication of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator*(const Cube<T>& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) += this->data[i][j][k] * rhs(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
// Cumulative left multiplication of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator*=(const Cube<T>& rhs)
{
  Cube result = (*this) * rhs;
  (*this) = result;
  return *this;
}
 
 
// Left division of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator/(const Cube<T>& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (rhs(i, j, k) != 0)
          result(i, j, k) += this->data[i][j][k] / rhs(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
// Cumulative left division of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator/=(const Cube<T>& rhs)
{
  Cube result = (*this) / rhs;
  (*this) = result;
  return *this;
}
 
 
// Left mod of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator%(const Cube<T>& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<rows; k++)
      {
        if (rhs(i, j, k) != 0)
          result(i, j, k) += this->data[i][j][k] % rhs(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
// Cumulative left mod of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator%=(const Cube<T>& rhs)
{
  Cube result = (*this) % rhs;
  (*this) = result;
  return *this;
}
 
 
// Left raise of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator^(const Cube<T>& rhs)
{
  //	unsigned rows = rhs.get_rows();
  //	unsigned cols = rhs.get_cols();
  Matrix result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) += this->data[i][j][k] ^ rhs(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
// Cumulative left raise of this Cube and another.
template<typename T>
Cube<T> Cube<T>::operator^=(const Cube<T>& rhs)
{
  Cube result = (*this) ^ rhs;
  (*this) = result;
  return *this;
}
 
 
// Addition of this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator+(const T& rhs)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        result(i, j, k) = this->data[i][j][k] + rhs;
      }
    }
  }
 
  return result;
}
 
 
// Cumulative addition of this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator+=(const T& rhs)
{
  Cube result = (*this) + rhs;
  (*this) = result;
  return *this;
}
 
 
// Subtraction of this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator-(const T& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        result(i, j, k) = this->data[i][j][k] - rhs;
      }
    }
  }
 
  return result;
}
 
 
// Cumulative subtraction of a valye from this Cube.
template<typename T>
Cube<T> Cube<T>::operator-=(const T& rhs)
{
  Cube result = (*this) - rhs;
  (*this) = result;
  return *this;
}
 
 
// Left multiplication of this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator*(const T& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) += this->data[i][j][k] * rhs;
      }
    }
  }
 
  return result;
}
 
 
// Cumulative multiplication from this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator*=(const T& rhs)
{
  Cube result = (*this) * rhs;
  (*this) = result;
  return *this;
}
 
 
// Left division of this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator/(const T& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (rhs != 0)
          result(i, j, k) += this->data[i][j][k] / rhs;
      }
    }
  }
 
  return result;
}
 
 
// Cumulative division from this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator/=(const T& rhs)
{
  if (rhs != 0)
  {
    Cube result = (*this) / rhs;
    (*this) = result;
  }
 
  return *this;
}
 
 
// Left mod of this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator%(const T& rhs)
{
  unsigned rows = rhs.getRows();
  unsigned cols = rhs.getCols();
  unsigned depth = rhs.getDepth();
 
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<rows; k++)
      {
        if (rhs != 0)
          result(i, j, k) += this->data[i][j][k] % rhs;
      }
    }
  }
 
  return result;
}
 
 
// Cumulative left mod of this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator%=(const T& rhs)
{
  if (rhs != 0)
  {
    Cube result = (*this) % rhs;
    (*this) = result;
  }
 
  return *this;
}
 
 
// Left raise of this Cube and a value.
template<typename T>
Cube<T> Cube<T>::operator^(const T& rhs)
{
  //	unsigned rows = rhs.get_rows();
  //	unsigned cols = rhs.get_cols();
  Matrix result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) += this->data[i][j][k] ^ rhs;
      }
    }
  }
 
  return result;
}
 
 
// Cumulative left raise of this Cube and a value..
template<typename T>
Cube<T> Cube<T>::operator^=(const T& rhs)
{
  Cube result = (*this) ^ rhs;
  (*this) = result;
  return *this;
}
 
 
// Access the individual elements.
template<typename T>
T& Cube<T>::operator()(const unsigned& row, const unsigned& col, const unsigned& depth)
{
  assert(row >= 0);
  assert(col >= 0);
  assert(depth >= 0);
  assert(row < this->rows);
  assert(col < this->cols);
  assert(depth < this->depth);
 
  return this->data[row][col][depth];
}
 
 
// Access the individual elements (const).
template<typename T>
const T& Cube<T>::operator()(const unsigned& row, const unsigned& col, const unsigned& depth) const
{
  assert(row >= 0);
  assert(col >= 0);
  assert(depth >= 0);
  assert(row < this->rows);
  assert(col < this->cols);
  assert(depth < this->depth);
 
  return this->data[row][col][depth];
}
 
 
// Populates every cell with a specific value which is dependant on the FILL_TYPE.
template<typename T>
void Cube<T>::fill()
{
  std::srand(std::time(0)); // use current time as seed for random generator.
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        switch (fill_type)
        {
        case 0: // Zeros.
          data[i][j][k] = 0;
          break;
 
        case 1: // Ones.
          data[i][j][k] = 1;
          break;
 
        case 2: // Twos.
          data[i][j][k] = 2;
          break;
 
        case 3: // Eye.
          break;
 
        case 4: // Rand.
          //std::srand(std::time(0)); // use current time as seed for random generator
          data[i][j][k] = rand();
          break;
 
        case 5: // Randu.
          //data[i][j][k] = RandRangeNormal(DBL_MIN, DBL_MAX);
          //data[i][j][k] = RandRange(INT_MIN, INT_MAX);
          //data[i][j][k] = RandRange(DBL_MIN, DBL_MAX);
          //data[i][j][k] = RandU(DBL_MIN, DBL_MAX);
          data[i][j][k] = RandU(INT_MIN, INT_MAX);
          //data[i][j][k] = RandU(0, INT_MAX);
          //data[i][j][k] = RandU(0, INT_MAX);
          //RandRangeMersenneTwister
          break;
 
        case 6: // Randn.
          //data[i][j][k] = RandRangeMersenneTwister(DBL_MIN, DBL_MAX);
          //data[i][j][k] = RandN(DBL_MIN, DBL_MAX);
          //data[i][j][k] = RandN(INT_MIN, INT_MAX);
          data[i][j][k] = RandN(0, INT_MAX);
          break;
 
        case 7: // Sequential.
          fill_sequentially();
          return;
          break;
 
        case 8: // Higher.
          fill_higher();
          return;
          break;
 
        case 9: // Lower.
          fill_lower();
          return;
          break;
 
        case 10: // None.
          break;
 
        Default: // None.
          return;
        }
 
 
      }
    }
  }
}
 
 
// Populates every cell with the specific value.
template<typename T>
void Cube<T>::fill(T value)
{
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        data[i][j][k] = value;
      }
    }
  }
}
 
 
// Populates every cell with the a higher number than the previous cell.
//
// seq is the starting sequence.
// min_inc is the increment to the next number.
template<typename T>
void Cube<T>::fill_higher(T seq = 0, T min_inc = 1)
{
  T tmp;
 
  switch (order_type)
  {
  case 0: // CDR.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp <= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 1: // DCR.
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned j = 0; j < cols; j++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp <= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 2: // DRC.
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        for (unsigned j = 0; j < cols; j++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp <= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 3: // CRD.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        for (unsigned k = 0; k < depth; k++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp <= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 4: // RCD.
    for (unsigned i = 0; i < rows; i++)
    {
      for (unsigned j = 0; j < cols; j++)
      {
        for (unsigned k = 0; k < depth; k++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp <= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 5: // RDC.
    for (unsigned i = 0; i < rows; i++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned j = 0; j < cols; j++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp <= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  default: // CDR.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp <= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
  }
}
 
 
// Populates every cell with the a lower number than the previous cell.
//
// seq is the starting sequence.
// min_inc is the minimum increment to the next number.
template<typename T>
void Cube<T>::fill_lower(T seq = 0, T min_inc = 1)
{
  T tmp;
 
  switch (order_type)
  {
  case 0: // CDR.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp >= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 1: // DCR.
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned j = 0; j < cols; j++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp >= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 2: // DRC.
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        for (unsigned j = 0; j < cols; j++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp >= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 3: // CRD.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        for (unsigned k = 0; k < depth; k++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp >= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 4: // RCD.
    for (unsigned i = 0; i < rows; i++)
    {
      for (unsigned j = 0; j < cols; j++)
      {
        for (unsigned k = 0; k < depth; k++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp >= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  case 5: // RDC.
    for (unsigned i = 0; i < rows; i++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned j = 0; j < cols; j++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp >= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
    break;
 
  default: // CDR.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
 
          tmp = rand();
          while (tmp >= seq + min_inc)
            tmp = rand();
          seq = tmp;
        }
      }
    }
  }
}
 
 
 
// Populates every cell with the a sequential number.
//
// seq is the starting sequence.
// inc is the increment to the next number.
template<typename T>
void Cube<T>::fill_sequentially(T seq = 0, T inc = 1)
{
  switch (order_type)
  {
  case 0: // CDR.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 1: // DCR.
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned j = 0; j < cols; j++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 2: // DRC.
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        for (unsigned j = 0; j < cols; j++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 3: // CRD.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        for (unsigned k = 0; k < depth; k++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 4: // RCD.
    for (unsigned i = 0; i < rows; i++)
    {
      for (unsigned j = 0; j < cols; j++)
      {
        for (unsigned k = 0; k < depth; k++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 5: // RDC.
    for (unsigned i = 0; i < rows; i++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned j = 0; j < cols; j++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  default: // CDR.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
  }
}
 
 
// Get the number of columns.
template<typename T>
unsigned Cube<T>::getCols() const
{
  return this->cols;
}
 
 
// Get the number of columns.
template<typename T>
unsigned Cube<T>::getDepth() const
{
  return this->depth;
}
 
 
// Gets the fill_type.
//
// Cells are populated using the fill_type.
template<typename T>
FILL_TYPE::fill_types Cube<T>::getFillType() const
{
  return this->fill_type;
}
 
 
// Gets the fill_type.
//
// Cells are populated in the order determined by the order_type.
template<typename T>
ORDER_TYPE::order_types Cube<T>::getOrderType() const
{
  return this->order_type;
}
 
 
// Get the number of rows.
template<typename T>
unsigned Cube<T>::getRows() const
{
  return this->rows;
}
 
 
// Returns the size.
//
// Returns the volume.
template <typename T>
unsigned Cube<T>::getSize() const
{
  return rows * cols * depth;
}
 
 
// Returns the volume.
template <typename T>
unsigned Cube<T>::getVolume()
{
  return rows * cols * depth;
}
 
 
// Returns the maximum value.
template <typename T>
T& Cube<T>::max()
{
  T result = data[0][0][0];
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (data[i][j][k] > result)
          result = data[i][j][k];
      }
    }
  }
 
  return result;
}
 
 
// Returns the minimum value.
template <typename T>
T& Cube<T>::min()
{
  T result = data[0][0][0];
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (data[i][j][k] < result)
          result = data[i][j][k];
      }
    }
  }
 
  return result;
}
 
 
// Prints the cube.
template <typename T>
void Cube<T>::print(std::ostream& os)
{
  unsigned rows = getRows();
  unsigned cols = getCols();
  unsigned depth = getDepth();
 
  for (unsigned j = 0; j < cols; j++)
  {
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        os << data[i][j][k];
        //if ((i*j) < (((rows - 1)*(cols - 1)) - 1))
        //if ((i*j*k) < (((rows - 1)*(cols - 1)*(depth -1)) - 1))
        if ((i < rows - 1) || (j < cols - 1) || (k < depth -1))
          os << ",";
      }
 
      os << "  ";
    }
 
    os << std::endl;
  }
}
 
 
// Randomizes every cell.
template<typename T>
void Cube<T>::randomize()
{
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        data[i][j][k] = rand();
      }
    }
  }
}
 
 
// Reads the input stream into the data element.
template <typename T>
void Cube<T>::read(std::istream& is)
{
  is >> data;
}
 
 
// Populates every cell with the a sequential number.
//
// seq is the starting sequence.
// inc is the increment to the next number.
template<typename T>
void Cube<T>::sequence(T seq = 0, T inc = 1)
{
  switch (order_type)
  {
  case 0: // CDR.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 1: // DCR.
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned j = 0; j < cols; j++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 2: // DRC.
    for (unsigned k = 0; k < depth; k++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        for (unsigned j = 0; j < cols; j++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 3: // CRD.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned i = 0; i < rows; i++)
      {
        for (unsigned k = 0; k < depth; k++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 4: // RCD.
    for (unsigned i = 0; i < rows; i++)
    {
      for (unsigned j = 0; j < cols; j++)
      {
        for (unsigned k = 0; k < depth; k++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  case 5: // RDC.
    for (unsigned i = 0; i < rows; i++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned j = 0; j < cols; j++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
    break;
 
  default: // CDR.
    for (unsigned j = 0; j < cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        for (unsigned i = 0; i < rows; i++)
        {
          data[i][j][k] = seq;
          seq += inc;
        }
      }
    }
  }
}
 
 
// Returns the sum of all cells.
template <typename T>
T& Cube<T>::sum()
{
  T result = 0;
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (data[i][j][k] < result)
          result += data[i][j][k];
      }
    }
  }
 
  return result;
}
 
 
 
//**************************************************************************************************
 
 
 
 
// Various friendship operators and functions.
 
template<typename T>
Cube<T> operator + (const Cube<T> &u, const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = u(i, j, k) + v(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
template<typename T>
Cube<T> operator - (const Cube<T> &u, const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = u(i, j, k) - v(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
template<typename T>
Cube<T> operator * (const Cube<T> &u, const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = u(i, j, k) * v(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
//  This algorithm is the long division algorithm.
template<typename T>
Cube<T> operator / (const Cube<T> &u, const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (v(i, j, k) != 0)
          result(i, j, k) = u(i, j, k) / v(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
template<typename T>
Cube<T> operator % (const Cube<T> &u, const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (v(i, j, k) != 0)
          result(i, j, k) = u(i, j, k) % v(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
template<typename T>
Cube<T> operator ^ (const Cube<T> &u, const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = u(i, j, k) ^ v(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
template<typename T>
int operator == (const Cube<T> &u, const Cube<T> &v)
{
  return (u == v);
}
 
 
template<typename T>
int operator != (const Cube<T> &u, const Cube<T> &v)
{
  return !(u == v);
}
 
 
template<typename T>
int operator < (const Cube<T> &u, const Cube<T> &v)
{
  return (u < v);
}
 
 
template<typename T>
int operator <= (const Cube<T> &u, const Cube<T> &v)
{
  return (u <= v);
}
 
 
template<typename T>
int operator >(const Cube<T> &u, const Cube<T> &v)
{
  return (u > v);
}
 
 
template<typename T>
int operator >= (const Cube<T> &u, const Cube<T> &v)
{
  return (u >= v);
}
 
 
// Calculate the absolute value of a number.
template<typename T>
Cube<T> abs(const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = abs(v(i, j, k));
      }
    }
  }
 
  return result;
}
 
 
// Double division function.
template<typename T>
double div(const Cube<T> &u, const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        if (v(i, j, k) != 0)
          result(i, j, k) = u(i, j, k) / v(i, j, k);
      }
    }
  }
 
  return result;
}
 
 
// Returns the size of the cube.
template<typename T>
unsigned size(const Cube<T> &c)
{
  return c.rows * c.cols * c.depth;
}
 
 
// Calculate the integer square root of a number
//    based on the formula (a+b)^2 = a^2 + 2ab + b^2
template<typename T>
Cube<T> sqrt(const Cube<T> &v)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = sqrt(v(i, j, k));
      }
    }
  }
 
  return result;
}
 
 
// Raise a number X to a power of degree
template<typename T>
Cube<T> pow(const Cube<T> &X, const Cube<T> &degree)
{
  Cube result(rows, cols, depth);
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k<depth; k++)
      {
        result(i, j, k) = pow(X(i, j, k), degree);
      }
    }
  }
 
  return result;
}
 
 
 
template<typename T>
//std::ostream & operator << (std::ostream &s, const Cube<T> &m)
std::ostream & operator << (std::ostream &s, Cube<T> &m)
{
  m.print(s);
  return s;
  /*
 
  unsigned rows = m.getRows();
  unsigned cols = m.getCols();
  unsigned depth = m.getDepth();
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        s << m(i, j, k);
 
        if ((i*j) < (((rows - 1)*(cols - 1)) - 1))
          s << ", ";
 
        //if (())
      }
    }
    s << std::endl;
  }
 
  return s;
  */
}
 
/*
template<typename T>
std::ostream & operator << (std::ostream &s, const Cube<T> &m)
{
  unsigned rows = m.getRows();
  unsigned cols = m.getCols();
  unsigned depth = m.getDepth();
 
  for (unsigned i = 0; i<rows; i++)
  {
    for (unsigned j = 0; j<cols; j++)
    {
      for (unsigned k = 0; k < depth; k++)
      {
        s << m(i, j, k);
 
        if ((i*j) < (((rows - 1)*(cols - 1)) - 1))
          s << ", ";
 
        //if (())
      }
    }
    s << std::endl;
  }
 
  return s;
}
*/
/*
template<typename T>
std::ostream & operator << (std::ostream &s, const Cube<T> &m)
{
  m.print(s);
  return s;
}
*/
 
/*
template<typename T>
std::istream & operator >> (std::istream &s, Cube<T> &v)
{
  std::string temp(10000, ' ');
 
  s >> temp;
  return s;
}
*/
 
 
template<typename T>
std::istream & operator >> (std::istream &s, Cube<T> &m)
{
  m.read(s);
  return s;
}
 
 
 
 
 
} // end namespace SW.
 
#endif