//Jacek Matulewski, e-mail: jacek@fizyka.umk.pl

#ifndef TESTY_CPP
#define TESTY_CPP

#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>

#include "Wektor.h"

template<typename T> bool TestWektora(unsigned int ziarno)
{
	const T zakres=100;
	T tolerancjaBledu=(T)1E-9; //dla double

	bool wynik=true;
	
	srand(ziarno);
	int nieUzywany=rand();
	//srand(0);
	
	//test konstruktora i akcesorów (odczyt)
	T wspolrzedne[3];
	for(int i=0;i<3;++i) wspolrzedne[i]=zakres*rand()/RAND_MAX;
	TWektor<T> wektor(wspolrzedne[0],wspolrzedne[1],wspolrzedne[2]);
	if(!(wektor.X==wspolrzedne[0] && wektor.Y==wspolrzedne[1] && wektor.Z==wspolrzedne[2])) wynik=false;

	if(wektor!=-(-wektor)) wynik=false;
	if(wektor!=+wektor) wynik=false;

	//test operatorow indeksowania [] - odczyt
	for(int i=0;i<3;++i) 
		if(wektor[i]!=wspolrzedne[i])
			wynik=false;

	//test operatora indeksowania [] - zapis
	TWektor<T> wektor1(0,0,0);
	for(int i=0;i<3;++i) wektor1[i]=wspolrzedne[i];
	if(!(wektor1.X==wspolrzedne[0] && wektor1.Y==wspolrzedne[1] && wektor1.Z==wspolrzedne[2])) wynik=false;

	//test metody Wektor::ZapiszWTablicy
	T tablica[3];
	wektor.ZapiszWTablicy(tablica);
	for(int i=0;i<3;++i) 
		if(tablica[i]!=wspolrzedne[i])
			wynik=false;
	
	//test konstruktora copy
	TWektor<T> kopiaWektora(wektor);
	for(int i=0;i<3;++i) 
		if(kopiaWektora[i]!=wektor[i])
			wynik=false;
	
	//testy operatorów relacji
	if(!(wektor==wektor)) wynik=false;
	if(!(wektor==kopiaWektora)) wynik=false;
	if(wektor!=wektor) wynik=false;
	if(!wektor.Rownowazny(wektor)) wynik=false;

	TWektor<T> zero(0,0,0);
	for(int i=0;i<3;++i) wspolrzedne[i]=zakres*rand()/RAND_MAX;
	TWektor<T> wektor2(wspolrzedne[0],wspolrzedne[1],wspolrzedne[2]);

	//testy operatorów arytmetycznych
	T dwa=2;
	T pol=1/dwa;
	if(wektor+wektor!=wektor*2) wynik=false;
	if(wektor+wektor2!=wektor2+wektor) wynik=false;
	if(wektor*(T)2.0!=wektor*2) wynik=false;
	if(dwa*wektor!=wektor*2) wynik=false;
	T liczba=zakres*rand()/RAND_MAX;
	if(liczba*wektor!=wektor*liczba) wynik=false;
	if(wektor+pol*wektor!=(1+pol)*wektor) wynik=false;
	if(wektor-wektor!=zero) wynik=false;
	if(wektor-pol*wektor!=wektor/2) wynik=false;

	//testy iloczynu skalarnego i normowania
	if(wektor*wektor!=wektor.KwadratDlugosci()) wynik=false;
	if(sqrt((long double)(wektor*wektor))!=wektor.Dlugosc()) wynik=false;
	if(wektor*wektor2!=wektor2*wektor) wynik=false;		
	
	//testy iloczynu wektorowego	
	if(wektor*zero!=0) wynik=false;
	if((wektor^wektor)!=zero) wynik=false;	
	if((wektor^wektor2)!=(-wektor2^wektor)) wynik=false;		
	if((wektor^wektor2)!=wektor%wektor2) wynik=false;
	if(wektor%wektor2+wektor!=(wektor%wektor2)+wektor) wynik=false;

	//testy z tolerancją błędu
	if(1-wektor.Unormowany().Dlugosc()>tolerancjaBledu) wynik=false;
	if(wektor*wektor-wektor.Dlugosc()*wektor.Dlugosc()>tolerancjaBledu) wynik=false;
	if(((wektor^(wektor2^wektor))-(wektor2*(wektor*wektor)-wektor*(wektor*wektor2))).Dlugosc()>10*tolerancjaBledu) wynik=false;
	if(wektor*(wektor+wektor2)-(wektor*wektor+wektor*wektor2)>tolerancjaBledu) wynik=false;
	if((wektor^wektor2)*wektor>tolerancjaBledu || (wektor^wektor2)*wektor2>tolerancjaBledu) wynik=false;

	//test funkcji statycznych
	if(!((TWektor<T>::Zero().X==0) && (TWektor<T>::Zero().Y==0) && (TWektor<T>::Zero().Z==0) && (TWektor<T>::Zero().W==1))) wynik=false;
	if(TWektor<T>::Zero()!=TWektor<T>(0,0,0)) wynik=false;
	if(TWektor<T>::Zero().Dlugosc()!=0) wynik=false;
	if((TWektor<T>::WersorX()^TWektor<T>::WersorY())!=TWektor<T>::WersorZ()) wynik=false;
	if((TWektor<T>::WersorX()+TWektor<T>::WersorY()+TWektor<T>::WersorZ())!=TWektor<T>::Jeden()) wynik=false;

	return wynik;
}

//------------------------------

#include "Macierz.h"

template<typename T> bool TestMacierzy(unsigned int ziarno)
{
	bool wynik=true;

	const T zakres=10;
	T tolerancjaBledu=(T)1E-9; //dla double

	//test kolejnosci elementow (do obejrzenia w Locals lub Watch)
	T elementyMacierzyKolejnosc[9]={0,1,2,3,4,5,6,7,8};
	TMacierz<T> mKolejnosc(elementyMacierzyKolejnosc);
	for(int i=0;i<9;++i) 
	{
		if(mKolejnosc[i]!=i) wynik=false;
		if(mKolejnosc.PobierzElement(i)!=i) wynik=false;
	}
	for(int kolumna=0;kolumna<3;++kolumna)
		for(int wiersz=0;wiersz<3;++wiersz)
			if(mKolejnosc.PobierzElement(wiersz,kolumna)!=wiersz+3*kolumna) wynik=false;
	if(mKolejnosc.Wyznacznik()!=0) wynik=false;

	T elementy9[9],elementy16[16];
	mKolejnosc.KopiaElementow(elementy9);
	mKolejnosc.KopiaElementow4x4(elementy16);
	for(int kolumna=0;kolumna<4;++kolumna)
		for(int wiersz=0;wiersz<4;++wiersz)
		{
			int indeks9=wiersz+3*kolumna;
			int indeks16=wiersz+4*kolumna;
			if(wiersz<3 && kolumna<3) if(elementy9[indeks9]!=elementy16[indeks16]) wynik=false;
			else if(elementy16[indeks16]==((wiersz==kolumna)?1.0:0.0)) wynik=false;
		}


	//testy na macierzy jednostkowej
	TMacierz<T> mI=TMacierz<T>::Jednostkowa();
	if(mI.Wyznacznik()!=1) wynik=false;
	if(mI.Slad()!=3) wynik=false;
	if(mI*mI!=mI) wynik=false;
	if(mI*mI*mI!=mI) wynik=false;
	if(mI.Odwrotna()!=mI) wynik=false;

	//tworzenie macierzy z elementami losowymi
	T elementyMacierzy[9];
	for(int i=0;i<9;++i) elementyMacierzy[i]=zakres*rand()/RAND_MAX;
	TMacierz<T> m(elementyMacierzy);

	//odczyt elementow
	for(int i=0;i<9;++i)
	{
		if(m.PobierzElement(i)!=elementyMacierzy[i]) wynik=false;
		if(m[i]!=elementyMacierzy[i]) wynik=false;
	}
	for(int kolumna=0;kolumna<3;++kolumna)
		for(int wiersz=0;wiersz<3;++wiersz)
			if(m.PobierzElement(wiersz,kolumna)!=elementyMacierzy[wiersz+3*kolumna]) wynik=false;
	T kopiaElementow[9];
	m.KopiaElementow(kopiaElementow);
	for(int i=0;i<9;++i) if(kopiaElementow[i]!=elementyMacierzy[i]) wynik=false;

	//zapis elementow
	m.ZerujElementy();
	if(m!=TMacierz<T>::Zerowa()) wynik=false;
	for(int i=0;i<9;++i) m.UstawElement(i,elementyMacierzy[i]);
	for(int i=0;i<9;++i) if(m[i]!=elementyMacierzy[i]) wynik=false;
	m.ZerujElementy();
	for(int kolumna=0;kolumna<3;++kolumna)
		for(int wiersz=0;wiersz<3;++wiersz)
			m.UstawElement(wiersz,kolumna,elementyMacierzy[wiersz+3*kolumna]);
	for(int i=0;i<9;++i) if(m[i]!=elementyMacierzy[i]) wynik=false;

	//testy operatorow i metod
	if(m*mI!=m) wynik=false;
	if(mI*m!=m) wynik=false;
	if(m.Transponowana().Transponowana()!=m) wynik=false;
	if(!(m.Odwrotna()*m).Rowna(mI,tolerancjaBledu)) wynik=false;
	if(!m.Odwrotna().Odwrotna().Rowna(m,1E3*tolerancjaBledu)) wynik=false;
	if(!m.Transponowana().Odwrotna().Rowna(m.Odwrotna().Transponowana(),1E4*tolerancjaBledu)) wynik=false;
	if(fabs(m.Odwrotna().Wyznacznik()-(1/m.Wyznacznik()))>1E4*tolerancjaBledu) wynik=false;

	for(int i=0;i<9;++i) elementyMacierzy[i]=zakres*rand()/RAND_MAX;
	TMacierz<T> m2(elementyMacierzy);
	if(fabs(m.Wyznacznik()*m2.Wyznacznik()-(m*m2).Wyznacznik())>1E5*tolerancjaBledu) wynik=false;
	if(!(m*m2).Odwrotna().Rowna(m2.Odwrotna()*m.Odwrotna(),1E6*tolerancjaBledu)) wynik=false;

	TWektor<T> w1=m2.KolumnaX();
	TWektor<T> w2=m2.KolumnaY();
	if(TMacierz<T>::OperatorGwiazdki(w1)*w2!=(w1^w2)) wynik=false;

	return wynik;
}

//------------------------------

#include "Kwaternion.h"

template<typename T> bool TestKwaternionu(unsigned int ziarno)
{
	bool wynik=true;

	const T zakres=100;
	T tolerancjaBledu=(T)1E-9; //dla double

	srand(ziarno);
	int nieUzywany=rand();

	T wspolrzedne[4];
	for(int i=0;i<4;++i) wspolrzedne[i]=zakres*rand()/RAND_MAX;

	TKwaternion<T> q(wspolrzedne[0],wspolrzedne[1],wspolrzedne[2],wspolrzedne[3]);
	if(-q!=TKwaternion<T>(-wspolrzedne[0],-wspolrzedne[1],-wspolrzedne[2],-wspolrzedne[3])) wynik=false;
	if(q!=-(-q)) wynik=false;
	if(q!=+q) wynik=false;	
	if((q+q)!=q*2) wynik=false;
	if(q-q!=TKwaternion<T>::Zero()) wynik=false;

	T liczba=rand();
	if(liczba*q!=q*liczba) wynik=false;

	if((q*q).s!=q.s*q.s-q.v*q.v) wynik=false;
	if((q*q).v!=(T)2*q.s*q.v) wynik=false;

	if(q*TKwaternion<T>::Jeden()!=q) wynik=false;
	if(q*TKwaternion<T>::Zero()!=TKwaternion<T>::Zero()) wynik=false;

	if(q.Unormowany().KwadratNormy()-1>tolerancjaBledu) wynik=false;

	if(q.Sprzezony().s!=q.s || q.Sprzezony().v!=-q.v) wynik=false;
	if(q.KwadratNormy()!=q.s*q.s+q.v*q.v) wynik=false;
	if(q*q.Sprzezony()!=TKwaternion<T>(q.KwadratNormy(),TWektor<T>::Zero())) wynik=false;
	if(q.Sprzezony()*q!=TKwaternion<T>(q.KwadratNormy(),TWektor<T>::Zero())) wynik=false;

	if((q*q.Odwrotny()-TKwaternion<T>::Jeden()).KwadratNormy()>tolerancjaBledu) wynik=false;
	if((q/q-TKwaternion<T>::Jeden()).KwadratNormy()>tolerancjaBledu) wynik=false;
	if(q/TKwaternion<T>::Jeden()!=q) wynik=false;
	
	if(TKwaternion<T>(0,q.v)*TKwaternion<T>(0,q.v)!=TKwaternion<T>(-q.v*q.v,TWektor<T>::Zero())) wynik=false;	

	double katObrotu=2*M_PI*rand()/RAND_MAX;
	TWektor<T> osObrotu=q.v.Unormowany();
	TKwaternion<T> k=TKwaternion<T>::TworzZKataIOsiObrotu(katObrotu,osObrotu);
	if(k.KwadratNormy()-1>tolerancjaBledu) wynik=false;
	if(k.KatObrotu()-katObrotu>tolerancjaBledu) wynik=false;
	if(katObrotu!=0)
		if((k.OsObrotu()-osObrotu).KwadratDlugosci()>tolerancjaBledu) wynik=false;	

	//dla dowolnego
	TKwaternion<T> qm=TKwaternion<T>::TworzZMacierzyObrotu(q.MacierzObrotu());
	if((q-qm).KwadratNormy()>tolerancjaBledu && (q+qm).KwadratNormy()>tolerancjaBledu) wynik=false;
	if((fabs(q.s)-fabs(qm.s))>tolerancjaBledu) wynik=false;
	if(fabs(q.v.KwadratDlugosci()-qm.v.KwadratDlugosci())>10*tolerancjaBledu) wynik=false;

	//dla jednostkowego
	TKwaternion<T> km=TKwaternion<T>::TworzZMacierzyObrotu(k.MacierzObrotu());
	if((k-km).KwadratNormy()>tolerancjaBledu && (k+km).KwadratNormy()>tolerancjaBledu) wynik=false;
	if((fabs(k.s)-fabs(km.s))>tolerancjaBledu) wynik=false;
	if(fabs(k.v.KwadratDlugosci()-km.v.KwadratDlugosci())>100*tolerancjaBledu) wynik=false;
	if(km.KatObrotu()!=0)
		if(fabs(km.OsObrotu().KwadratDlugosci()-osObrotu.KwadratDlugosci())>100*tolerancjaBledu) wynik=false;
	TMacierz<T> mk=k.MacierzObrotu();
	if(mk.Wyznacznik()-1>tolerancjaBledu) wynik=false;	
	if(mk.KolumnaX()*mk.KolumnaY()>tolerancjaBledu || 
	   mk.KolumnaX()*mk.KolumnaZ()>tolerancjaBledu || 
	   mk.KolumnaY()*mk.KolumnaZ()>tolerancjaBledu) wynik=false;
	if((mk.KolumnaX()%mk.KolumnaY()-mk.KolumnaZ()).KwadratDlugosci()>tolerancjaBledu && 
	   (mk.KolumnaX()%mk.KolumnaY()+mk.KolumnaZ()).KwadratDlugosci()>tolerancjaBledu) wynik=false;
	if(!(mk.Transponowana().Rowna(mk.Odwrotna(),tolerancjaBledu))) wynik=false;

	return wynik;
}

/*
#include <gl\gl.h>
bool TestKonwersjiKwaternionuDoMacierzy(unsigned int ziarno)
{	
	bool wynik=true;
	const double tolerancjaBledu=1E-5;
	double zakres=100.0;

	srand(ziarno);
	int nieUzywany=rand();
	double wspolrzedne[4];
	for(int i=0;i<4;++i) wspolrzedne[i]=zakres*rand()/RAND_MAX;

	//dane
	double katObrotu=wspolrzedne[0];
	Wektor osObrotu(wspolrzedne[1],wspolrzedne[2],wspolrzedne[3]);
	osObrotu.Normuj();

	//bufory
	double macierz1[16],macierz2[16],roznica[16];

	//OpenGL
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glRotated(360.0*katObrotu/(2*M_PI),osObrotu.X,osObrotu.Y,osObrotu.Z);
	glGetDoublev(GL_MODELVIEW_MATRIX,macierz1);
	glPopMatrix();

	//Kwaternion
	Kwaternion k=Kwaternion::TworzZKataIOsiObrotu(katObrotu,osObrotu);
	Macierz m=k.MacierzObrotu();
	m.KopiaElementow4x4(macierz2);

	//porownanie
	for(int i=0;i<16;++i) 
	{
		roznica[i]=macierz1[i]-macierz2[i];
		if(fabs(roznica[i])>tolerancjaBledu) wynik=false;
	}

	return wynik;
}
*/

#endif