class CVertex
{
public:
	double X;
	double Y;
	double Z;
};

typedef CVertex *CVertexPtr;

enum Rotation {
	abcd,acdb,adbc,badc,bcad,bdca,
	cabd,cbda,cdab,dacb,dbac,dcba
};

const int NAdj[12]={0,2,1,1,0,3,0,3,2,2,1,3};
const Rotation TAdj[12]={
	abcd,bcad,cabd,abcd,cabd,bcad,
	bcad,cabd,abcd,cabd,bcad,abcd
};
const Rotation RAdj[12]={
	abcd,cabd,bcad,abcd,bcad,cabd,
	cabd,bcad,abcd,bcad,cabd,abcd
};
const Rotation Mult[12][12]={
	abcd,acdb,adbc,badc,bcad,bdca,cabd,cbda,cdab,dacb,dbac,dcba,
	acdb,adbc,abcd,bdca,badc,bcad,cbda,cdab,cabd,dcba,dacb,dbac,
	adbc,abcd,acdb,bcad,bdca,badc,cdab,cabd,cbda,dbac,dcba,dacb,
	badc,cabd,dacb,abcd,cbda,dbac,acdb,bcad,dcba,adbc,bdca,cdab,
	bcad,cdab,dbac,adbc,cabd,dcba,abcd,bdca,dacb,acdb,badc,cbda,
	bdca,cbda,dcba,acdb,cdab,dacb,adbc,badc,dbac,abcd,bcad,cabd,
	cabd,dacb,badc,dbac,abcd,cbda,bcad,dcba,acdb,cdab,adbc,bdca,
	cbda,dcba,bdca,dacb,acdb,cdab,badc,dbac,adbc,cabd,abcd,bcad,
	cdab,dbac,bcad,dcba,adbc,cabd,bdca,dacb,abcd,cbda,acdb,badc,
	dacb,badc,cabd,cbda,dbac,abcd,dcba,acdb,bcad,bdca,cdab,adbc,
	dbac,bcad,cdab,cabd,dcba,adbc,dacb,abcd,bdca,badc,cbda,acdb,
	dcba,bdca,cbda,cdab,dacb,acdb,dbac,adbc,badc,bcad,cabd,abcd
};

const Rotation Revr[12]={
	abcd,adbc,acdb,badc,cabd,dacb,
	bcad,dbac,cdab,bdca,cbda,dcba
};

const int Vert[12][4]={
	0,1,2,3, 0,2,3,1, 0,3,1,2, 1,0,3,2,
	1,2,0,3, 1,3,2,0, 2,0,1,3, 2,1,3,0,
	2,3,0,1, 3,0,2,1, 3,1,0,2, 3,2,1,0
};

inline Rotation operator*(Rotation a,Rotation b){ return Mult[b][a];}

class CTetrahedron
{
public:
	class CAdjPtr;
	class CPtr
	{	public:
		CTetrahedron *P;
		Rotation R;
		CPtr(CTetrahedron* p=0,Rotation r=abcd):P(p),R(r){}
		CPtr(CAdjPtr&A):P(A.Ptr.P->P[NAdj[A.Ptr.R]].P),R(A.Ptr.P->P[NAdj[A.Ptr.R]].R*TAdj[A.Ptr.R]){}
		CPtr operator*(Rotation r){ return CPtr(P,R*r);}
		CVertexPtr& operator[](int n){ return P->V[Vert[R][n]];}
		CAdjPtr Adj(){ return CAdjPtr(*this);}
		CPtr Abcd(){ return *this;}
		CPtr Acdb(){ return *this*acdb;}
		CPtr Adbc(){ return *this*adbc;}
		CPtr Badc(){ return *this*badc;}
		CPtr Bcad(){ return *this*bcad;}
		CPtr Bdca(){ return *this*bdca;}
		CPtr Cabd(){ return *this*cabd;}
		CPtr Cbda(){ return *this*cbda;}
		CPtr Cdab(){ return *this*cdab;}
		CPtr Dacb(){ return *this*dacb;}
		CPtr Dbac(){ return *this*dbac;}
		CPtr Dcba(){ return *this*dcba;}
	};
	class CAdjPtr
	{	public:
		CPtr &Ptr;
		CAdjPtr(CPtr&p):Ptr(p){}
		void operator=(CPtr&p){ Ptr.P->P[NAdj[Ptr.R]].P=p.P; Ptr.P->P[NAdj[Ptr.R]].R=p.R*RAdj[Ptr.R];}
		CPtr Adj(){ return Ptr;}
		CVertexPtr& operator[](int n){ return CPtr(*this)[n];}
		CPtr Abcd(){ return CPtr(*this).Abcd();}
		CPtr Acdb(){ return CPtr(*this).Acdb();}
		CPtr Adbc(){ return CPtr(*this).Adbc();}
		CPtr Badc(){ return CPtr(*this).Badc();}
		CPtr Bcad(){ return CPtr(*this).Bcad();}
		CPtr Bdca(){ return CPtr(*this).Bdca();}
		CPtr Cabd(){ return CPtr(*this).Cabd();}
		CPtr Cbda(){ return CPtr(*this).Cbda();}
		CPtr Cdab(){ return CPtr(*this).Cdab();}
		CPtr Dacb(){ return CPtr(*this).Dacb();}
		CPtr Dbac(){ return CPtr(*this).Dbac();}
		CPtr Dcba(){ return CPtr(*this).Dcba();}
	};
	CVertex *V[4];
	CPtr P[4];
	CVertexPtr& operator[](int n){ return V[n];}
	CTetrahedron(CVertex*v0=0,CVertex*v1=0,CVertex*v2=0,CVertex*v3=0);
};

inline CTetrahedron::CTetrahedron(CVertex*v0,CVertex*v1,CVertex*v2,CVertex*v3)
{	V[0]=v0; V[1]=v1; V[2]=v2; V[3]=v3;
	P[0]=0; P[1]=0; P[2]=0; P[3]=0;
}
