///////////////////////////////////////////////////////////////////////
// Useful classes.
// Author: Jeff Comer <jcomer2@illinois.edu>
#ifndef USEFUL_H
#define USEFUL_H

#ifdef __CUDACC__
    #define HOST __host__
    #define DEVICE __device__
#else
    #define HOST
    #define DEVICE
#endif

#if defined(__CUDACC__) // NVCC
   #define MY_ALIGN(n) __align__(n)
#elif defined(__GNUC__) // GCC
  #define MY_ALIGN(n) __attribute__((aligned(n)))
#elif defined(_MSC_VER) // MSVC
  #define MY_ALIGN(n) __declspec(align(n))
#else
  #error "Please provide a definition for MY_ALIGN macro for your host compiler!"
#endif

#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cuda_runtime.h>

// using namespace std;

bool isReal(char c);

bool isInt(char c);

int firstSpace(const char* s, int max);


/*class int2 {
public:
	int2(int x, int y) : x(x), y(y) {}
	int x, y;
}*/

// A classic growable string class.
class String {
public:
	HOST DEVICE inline String() {
		cap = 16;
		c = new char[cap];
		c[0] = '\0';
		len = 1;
	}

	HOST DEVICE inline String(const char* s) {
		len = strlen(s) + 1;
		cap = len;
		c = new char[len];
		for (int i = 0; i < len; i++)
			c[i] = s[i];
	}

	HOST DEVICE inline String(const String& s) {
		len = s.len;
		cap = s.len;
		c = new char[cap];
		for (int i = 0; i < s.len; i++)
			c[i] = s.c[i];
	}

	HOST DEVICE inline static size_t strlen(const char * s) {
		size_t i;
		for (i = 0; s[i] != '\0'; i++);
		return i;
	}

	HOST DEVICE inline ~String() {
		delete[] c;
	}

	String& operator=(const String& s);

	void add(char c0);
	void add(const char* s);
	void add(String& s);
	void printInline() const;
	void print() const;
	int length() const;

	// Negative indices go from the end.
	String range(int first, int last) const;

	String trim();

	static bool isWhite(char c);

	int tokenCount() const;
	int tokenCount(char delim) const;

	int tokenize(String* tokenList) const;
	int tokenize(String* tokenList, char delim) const;

	void lower();
	void upper();

	char operator[](int i) const;
	bool operator==(const String& s) const;
	bool operator==(const char* s) const;
	bool operator!=(const String& s) const;
	operator const char*() const {
		return c;
	}
	const char* val() const;

	String getNumbers() const;

private:
	char* c;
	int cap, len;

	void grow(int n);
};

String operator+(String s, int i);
String operator+(String s, const char* c);
String operator+(String s1, String s2);

// class Vector3
// Operations on 3D float vectors
//
class MY_ALIGN(16) Vector3 {
public:
	HOST DEVICE inline Vector3() : x(0), y(0), z(0), w(0) {}
	HOST DEVICE inline Vector3(float s):x(s), y(s), z(s), w(s) {}
	HOST DEVICE inline Vector3(const Vector3& v):x(v.x), y(v.y), z(v.z), w(v.w)  {}
	HOST DEVICE inline Vector3(float x0, float y0, float z0) : x(x0), y(y0), z(z0), w(0) {}
	HOST DEVICE inline Vector3(const float* d) : x(d[0]), y(d[1]), z(d[2]), w(0) {}
             DEVICE inline Vector3(const float4 a) : x(a.x ), y(a.y ), z(a.z ), w(a.w) {}

	static Vector3 random(float s);

	HOST DEVICE inline Vector3 cross(const Vector3& w) const {
		Vector3 v;
		v.x = y*w.z - z*w.y;
		v.y = z*w.x - x*w.z;
		v.z = x*w.y - y*w.x;
		return v;
	}

	HOST DEVICE inline Vector3& operator=(const Vector3& v) {
		x = v.x;
		y = v.y;
		z = v.z;
		return *this;
	}

	HOST DEVICE inline Vector3& operator+=(const Vector3& v) {
		x += v.x;
		y += v.y;
		z += v.z;
		return *this;
	}

	HOST DEVICE inline Vector3& operator-=(const Vector3& v) {
		x -= v.x;
		y -= v.y;
		z -= v.z;
		return *this;
	}

	HOST DEVICE inline Vector3& operator*=(float s) {
		x *= s;
		y *= s;
		z *= s;
		return *this;
	}

	HOST DEVICE inline Vector3& operator/=(float s) {
		const float sinv = 1.0f/s;
		x *= sinv;
		y *= sinv;
		z *= sinv;
		return *this;
	}

	HOST DEVICE inline Vector3 operator-() const {
		Vector3 v;
		v.x = -x;
		v.y = -y;
		v.z = -z;
		return v;
	}

	HOST DEVICE inline Vector3 operator+(const Vector3& w ) const {
		Vector3 v;
		v.x = x + w.x;
		v.y = y + w.y;
		v.z = z + w.z;
		return v;
	}

	HOST DEVICE inline Vector3 operator-(const Vector3& w ) const {
		Vector3 v;
		v.x = x - w.x;
		v.y = y - w.y;
		v.z = z - w.z;
		return v;
	}

	HOST DEVICE inline Vector3 operator*(float s) const {
		Vector3 v;
		v.x = s*x;
		v.y = s*y;
		v.z = s*z;
		return v;
	}

	HOST DEVICE inline float dot(const Vector3& w) const { return x*w.x + y*w.y + z*w.z; }

	HOST DEVICE inline float length() const { return sqrtf(x*x + y*y + z*z); }
	HOST DEVICE inline float length2() const { return x*x + y*y + z*z; }
	HOST DEVICE inline float rLength() const {
		float l = length();
		if (l != 0.0f)
			return 1.0f / l;
		return 0.0f;
	}

	HOST DEVICE inline float rLength2() const {
		float l2 = length2();
		if (l2 != 0.0f) return 1.0f / l2;
		return 0.0f;
	}

	HOST DEVICE static inline Vector3 element_mult(const Vector3 v, const Vector3 w) {
		Vector3 ret(
			v.x*w.x,
			v.y*w.y,
			v.z*w.z);
		return ret;
	}
	HOST DEVICE static inline Vector3 element_sqrt(const Vector3 w) {
		Vector3 ret(
			sqrt(w.x),
			sqrt(w.y),
			sqrt(w.z));
		return ret;
	}

	HOST DEVICE inline void print() const {
		printf("%0.3f %0.3f %0.3f\n", x,y,z);
	}

        float x, y, z, w; //append a member w	
	String toString() const;
};

HOST DEVICE inline Vector3 operator*(float s, Vector3 v) {
	v.x *= s;
	v.y *= s;
	v.z *= s;
	return v;
}

HOST DEVICE inline Vector3 operator/(Vector3 v, float s) {
	const float sinv = 1.0f/s;
	return v*sinv;
}

HOST DEVICE inline Vector3 operator/(float s, Vector3 v) {
    v.x = s / v.x;
    v.y = s / v.y;
    v.z = s / v.z;
    return v;
}


// class Matrix3
// Operations on 3D float matrices
class MY_ALIGN(16) Matrix3  {
	friend class TrajectoryWriter;
	friend class BaseGrid;
	friend class RigidBodyController; /* for trajectory writing */
public:
	HOST DEVICE inline Matrix3() : isDiag(false) {}
	HOST DEVICE Matrix3(float s);
	HOST DEVICE Matrix3(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz);
	HOST DEVICE Matrix3(float x, float y, float z);
	HOST DEVICE Matrix3(const Vector3& ex, const Vector3& ey, const Vector3& ez);
	HOST DEVICE Matrix3(const float* d);


	// Operators 
	HOST DEVICE inline const Matrix3 operator*(float s) const {
		Matrix3 m;
		m.exx = s*exx; m.exy = s*exy; m.exz = s*exz;
		m.eyx = s*eyx; m.eyy = s*eyy; m.eyz = s*eyz;
		m.ezx = s*ezx; m.ezy = s*ezy; m.ezz = s*ezz;
		m.isDiag = isDiag;
		return m;
	}
	HOST DEVICE friend inline Matrix3 operator*(float s, Matrix3 m) { return m*s; }
	HOST DEVICE friend inline Matrix3 operator/(Matrix3 m, float s) {
		const float sinv = 1.0f/s;
		return m*sinv;
	}

	HOST DEVICE inline const Vector3 operator*(const Vector3& v) const	{ return this->transform(v); }

	HOST DEVICE inline Matrix3 operator*(const Matrix3& m) const {
		Matrix3 ret;
		ret.exx = exx*m.exx + exy*m.eyx + exz*m.ezx;
		ret.eyx = eyx*m.exx + eyy*m.eyx + eyz*m.ezx;
		ret.ezx = ezx*m.exx + ezy*m.eyx + ezz*m.ezx;

		ret.exy = exx*m.exy + exy*m.eyy + exz*m.ezy;
		ret.eyy = eyx*m.exy + eyy*m.eyy + eyz*m.ezy;
		ret.ezy = ezx*m.exy + ezy*m.eyy + ezz*m.ezy;

		ret.exz = exx*m.exz + exy*m.eyz + exz*m.ezz;
		ret.eyz = eyx*m.exz + eyy*m.eyz + eyz*m.ezz;
		ret.ezz = ezx*m.exz + ezy*m.eyz + ezz*m.ezz;
		ret.setIsDiag();
		return ret;
	}
	
	HOST DEVICE inline Matrix3 operator-() const {
		Matrix3 m;
		m.exx = -exx;
		m.exy = -exy;
		m.exz = -exz;
		m.eyx = -eyx;
		m.eyy = -eyy;
		m.eyz = -eyz;
		m.ezx = -ezx;
		m.ezy = -ezy;
		m.ezz = -ezz;
		m.isDiag = isDiag;
		return m;
	}

	HOST DEVICE inline Matrix3 transpose() const {
		Matrix3 m;
		m.exx = exx;
		m.exy = eyx;
		m.exz = ezx;
		m.eyx = exy;
		m.eyy = eyy;
		m.eyz = ezy;
		m.ezx = exz;
		m.ezy = eyz;
		m.ezz = ezz;
		m.isDiag = isDiag;
		return m;
	}

	HOST DEVICE inline Vector3 transform(const Vector3& v) const {
		Vector3 w;
		if (isDiag) {
			w.x = exx*v.x;
			w.y = eyy*v.y;
			w.z = ezz*v.z;
		} else {
			w.x = exx*v.x + exy*v.y + exz*v.z;
			w.y = eyx*v.x + eyy*v.y + eyz*v.z;
			w.z = ezx*v.x + ezy*v.y + ezz*v.z;
		}
		return w;
	}

	HOST DEVICE inline Matrix3 transform(const Matrix3& m) const {
		Matrix3 ret;
		ret.exx = exx*m.exx + exy*m.eyx + exz*m.ezx;
		ret.eyx = eyx*m.exx + eyy*m.eyx + eyz*m.ezx;
		ret.ezx = ezx*m.exx + ezy*m.eyx + ezz*m.ezx;

		ret.exy = exx*m.exy + exy*m.eyy + exz*m.ezy;
		ret.eyy = eyx*m.exy + eyy*m.eyy + eyz*m.ezy;
		ret.ezy = ezx*m.exy + ezy*m.eyy + ezz*m.ezy;

		ret.exz = exx*m.exz + exy*m.eyz + exz*m.ezz;
		ret.eyz = eyx*m.exz + eyy*m.eyz + eyz*m.ezz;
		ret.ezz = ezx*m.exz + ezy*m.eyz + ezz*m.ezz;
		ret.setIsDiag();
		return ret;
	}

	
	HOST DEVICE
	Matrix3 inverse() const;

        HOST DEVICE
        float det() const;

        //Han-Yi Chou
	HOST DEVICE inline Matrix3 normalized() const {
                
		Vector3 x = this->ex();
		Vector3 y = this->ey();
                /*
                x = x / x.length();
		float error = x.dot(y);
		y = y-(error*x);
                y = y / y.length();
		Vector3 z = x.cross(y);
		z = z / z.length();*/
		//x = (0.5*(3-x.dot(x)))*x; /* approximate normalization */
		//y = (0.5*(3-y.dot(y)))*y; 
		//z = (0.5*(3-z.dot(z)))*z; 
		//return Matrix3(x,y,z);		
		Vector3 z = x.cross(y);
                z = z / z.length();
                x = x / x.length();
                y = z.cross(x);
                y = y / y.length();

                return Matrix3(x,y,z);
	}

	HOST DEVICE void setIsDiag() {
		isDiag = (exy == 0 && exz == 0 &&
							eyx == 0 && eyz == 0 &&
							ezx == 0 && ezy == 0) ? true : false;
	}
	

	
	HOST DEVICE inline Vector3 ex() const { return Vector3(exx,eyx,ezx); }
	HOST DEVICE inline Vector3 ey() const { return Vector3(exy,eyy,ezy); }
	HOST DEVICE inline Vector3 ez() const { return Vector3(exz,eyz,ezz); }
	String toString() const;
	String toString1() const;

	HOST DEVICE inline void print() const {
		printf("%0.3f %0.3f %0.3f\n", exx,exy,exz);
		printf("%0.3f %0.3f %0.3f\n", eyx,eyy,eyz);
		printf("%0.3f %0.3f %0.3f\n", ezx,ezy,ezz);
	}

	HOST DEVICE inline bool isDiagonal() const { return isDiag; }
	
	
private:
	float exx, exy, exz;
	float eyx, eyy, eyz;
	float ezx, ezy, ezz;
	bool isDiag;

};

// class IndexList
// A growable list of integers.
class MY_ALIGN(16) IndexList {
public:
	IndexList();
	IndexList(const IndexList& l);
	IndexList(const int* a, int n);

	~IndexList();

	IndexList& operator=(const IndexList& l);

	void add(const int val);
	void add(const IndexList& l);

	HOST DEVICE inline int length() const {
		return num;
	}

	HOST DEVICE inline int get(const int i) const {
#ifdef DEBUG
		if (i < 0 || i >= num) {
			printf("Warning! IndexList::get out of bounds.\n");
			return 0;
		}
#endif
		return lis[i];
	}

	int* getList();
	void setList(int* list) { lis = list; }

	void clear();
	String toString() const;

	int find(int key);

	IndexList range(int i0, int i1);

public:
	int num, maxnum;
	int* lis;
};

class ForceEnergy {
public:
        HOST DEVICE ForceEnergy() : f(0.f), e(0.f) {};
	HOST DEVICE ForceEnergy(Vector3 &f, float &e) :
		f(f), e(e) {};
        HOST DEVICE explicit ForceEnergy(float e) : f(e), e(e) {};
        HOST DEVICE ForceEnergy(float f, float e) :
        f(f), e(e) {};
        HOST DEVICE ForceEnergy(const ForceEnergy& src)
        {
            f = src.f;
            e = src.e;
        }
        HOST DEVICE ForceEnergy& operator=(const ForceEnergy& src)
        {
            if(&src != this)
            {
                this->f = src.f;
                this->e = src.e;
            }
            return *this;
        }
        HOST DEVICE ForceEnergy operator+(const ForceEnergy& src)
        {
            ForceEnergy fe;
            fe.f = this->f + src.f;
            fe.e = this->e + src.e;
            return fe;
        }
        HOST DEVICE ForceEnergy& operator+=(ForceEnergy& src)
        {
            this->f += src.f;
            this->e += src.e;
            return *this; 
        }
	Vector3 f;
	float e;
};

#endif