#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <complex.h>
#include <string.h>

#include "../numeric/SU21-numeric.h"
#include "../numeric/SU21-numeric-base.c"

#define GOODPREFIX "C1/C1-0-KK"

// The golden ration
#define GR (1.618033988749894848204586834366)

// The original generators act unitarily with respect to the
// form $\diag(1,1,-\GR)=\diag(1,1,-R_0^2)$.

// R0 =$\sqrt(\GR))$
#define R0 (1.272019649514068964252422461737)

// Powers of $Z=\zeta_5^2=e^{4\pi i/5}$
#define Z  (-0.809016994374947424102293417183+0.587785252292473129168705954639*I)
#define Z2 ( 0.309016994374947424102293417183-0.951056516295153572116439333379*I)
#define Z3 ( 0.309016994374947424102293417183+0.951056516295153572116439333379*I)
#define Z4 (-0.809016994374947424102293417183-0.587785252292473129168705954639*I)

typedef struct {
  char Nam[MaxGenNameLength+1]; // Generator name
  char InvNam[MaxGenNameLength+1]; // Inverse generator name
  int Len; // ``Length'' of Generator
  Mtx_t Mtx; // matrix for generator
} Gen_t;

#define MaxNoGens 5
#define ActualNoGens 5
#define KSiz 200

void SetupGens(Elt_t GenElts[],int *NoGenElts_p,
	       Mtx_t FF0,Mtx_t ConjMtx,Mtx_t ConjMtxInv);
void SetupK(Elt_t KElts[],
	       Mtx_t FF0,Mtx_t ConjMtx,Mtx_t ConjMtxInv);

int main(int argc,char *(argv[])) {
  // The generators as group elements
  Elt_t GenElts[2*ActualNoGens],KElts[KSiz];

  char *Prefix_p,FileNam[MAX_FILENAME_LEN]="../";
  int Ix,GenIx,GenEltIx,NoGenElts;
  size_t NamLen;

  // The form with respect to which the original, given matrices are
  // unitary:
  Mtx_t FF0=
    {{ 1, 0,    0  },
     { 0, 1,    0  },
     { 0, 0, -R0*R0}};

  // Matrix by which to conjugate them to make them unitary with
  // respect to $\diag{1,1,-1}$
  Mtx_t ConjMtxInv, ConjMtx={
    { 1, 0,  0},
    { 0, 1,  0},
    { 0, 0, R0}};

  FILE *GensFile;
  FILE *KFile;

  // Check that the prefix is right
  Prefix_p=getenv("PREFIX");
  assert(Prefix_p != NULL);
  assert(strncmp(Prefix_p,GOODPREFIX,50) == 0);

  // Open the output files
  {
    size_t len=strlen(Prefix_p);
    assert(3+len+5<MAX_FILENAME_LEN);
    strcpy(&FileNam[3],Prefix_p);
    strcpy(&FileNam[3]+len,"-Gens");
    printf("%s\n",FileNam);
    GensFile=fopen(FileNam,"w");
    assert(GensFile != NULL);
    strcpy(&FileNam[3]+len,"-K");
    printf("%s\n",FileNam);
    KFile=fopen(FileNam,"w");
    assert(KFile != NULL);
  }

  // Calculate the inverse of ConjMtx
  InvMtx(ConjMtxInv,ConjMtx);
  
  // Prepare the generators and write the generator file
  SetupGens(GenElts,&NoGenElts,FF0,ConjMtx,ConjMtxInv);
  fprintf(GensFile,"NoGenElts=%i\n",NoGenElts);
  for(GenEltIx=0;GenEltIx<NoGenElts;GenEltIx++)
    OutputElt(GensFile,GenElts[GenEltIx]);

  // Prepare K (the finite group) and write the K file
  SetupK(KElts,FF0,ConjMtx,ConjMtxInv);
  fprintf(KFile,"KSiz=%i\n",KSiz);
  for(GenEltIx=0;GenEltIx<KSiz;GenEltIx++)
    OutputElt(KFile,KElts[GenEltIx]);
}

// Prepare the generating elements
//
void SetupGens(Elt_t GenElts[],int *NoGenElts_p,
	       Mtx_t FF0,Mtx_t ConjMtx,Mtx_t ConjMtxInv) {

  // The generators
  Gen_t Gens[MaxNoGens]={
    {"A","a",100,
     {{-Z3,    0,    0},
      {        0,-Z2-1,Z2+1},
      {        0,     Z,Z2+1}
     }
    },

    {"G1","g1",1000,
     {{1,0,0},
      {0,(2*Z3+2*Z2+3),(2*(Z3+Z2+2))},
      {0,(2*(Z3+Z2+1)),(2*Z3+2*Z2+3)}
     }
    },
    {"G2","g2",1000,
     {{(2*(Z3+Z2+2)),(2*Z3+2*Z2+3),(2*(-3*Z3-2*Z-2))},
      {(2*Z3+2*Z2+3),(2*(Z3+Z2+2)),(2*(-3*Z3-2*Z-2))},
      {(2*Z*(Z2-Z+1)),(2*Z*(Z2-Z+1)),(4*Z3+4*Z2+7)}
     }
    },
    {"G3","g3",1000,
     {{(2*Z3+2*Z2+3),(2*(-Z3-2*Z2+Z-3)),(2*(-3*Z2+2*Z-3))},
      {(2*(3*Z3+2*Z2+Z+4)),(-8*Z3-8*Z2-13),(2*(-3*Z3-6*Z2+2*Z-8))},
      {(2*(-3*Z3-Z2-Z-3)),(2*(5*Z3+3*Z2+Z+6)),(10*Z3+10*Z2+17)}
     }
    },
    {"G4","g4",1000,
     {{1,0,0},
      {0,(3*(2*Z3+2*Z2+3)),(2*(-5*Z3+Z2-4*Z-2))},
      {0,(2*(3*Z3-Z2+2*Z+1)),(3*(2*Z3+2*Z2+3))}
     }
    }
  };

  int GenIx,GenEltIx;
  size_t NamLen;
  Mtx_t MtxInv,tmpMtx;

  GenEltIx=0;
  for(GenIx=0;GenIx<ActualNoGens;GenIx++) {
    // Check that the matrix is unitary with respect to FF0
    printf("Checking GenIx=%i\n",GenIx);
    // OutputMtx(stdout,Gens[GenIx].Mtx);
    assert(CheckPU(Gens[GenIx].Mtx,FF0,epsBig));
    // Calculate the conjugated matrix
    ConjugateMtx(GenElts[GenEltIx].Mtx,
		 Gens[GenIx].Mtx,ConjMtx,ConjMtxInv);
    // and check that that is unitary relative to FF1
    assert(CheckPU(GenElts[GenEltIx].Mtx,FF1,epsBigger));
    InvMtx(MtxInv,GenElts[GenEltIx].Mtx);
    // Set up the generator name
    NamLen=strlen(Gens[GenIx].Nam);
    assert(NamLen>0 && NamLen<MaxGenNameLength);
    strcpy(GenElts[GenEltIx].Word,Gens[GenIx].Nam);
    // and ``length''
    GenElts[GenEltIx].Len=Gens[GenIx].Len;
    // and KIx and KStart
    GenElts[GenEltIx].KIx = -1;
    GenElts[GenEltIx].KStart = 0;
    
    FixUpElt(&GenElts[GenEltIx]);
    GenEltIx++;

    // Now do the inverse generator.  If the inverse name is empty, we
    // skip this.
    NamLen=strlen(Gens[GenIx].InvNam);
    if(NamLen>0) {
      // The matrix
      memcpy(&GenElts[GenEltIx].Mtx,MtxInv,sizeof(Mtx_t));
      assert(CheckPU(GenElts[GenEltIx].Mtx,FF1,epsBigger));
      // the inverse generator name
      assert(NamLen<MaxGenNameLength);
      strcpy(GenElts[GenEltIx].Word,Gens[GenIx].InvNam);
      // the length
      GenElts[GenEltIx].Len=Gens[GenIx].Len;
      // and KIx and KStart
      GenElts[GenEltIx].KIx = -1;
      GenElts[GenEltIx].KStart = 0;

      FixUpElt(&GenElts[GenEltIx]);
      GenEltIx++;
    }
  }
  *NoGenElts_p=GenEltIx;
}

// Set up the 200-element finite group, K
//
void SetupK(Elt_t KElts[],
	       Mtx_t FF0,Mtx_t ConjMtx,Mtx_t ConjMtxInv) {

  int Ix1,Ix2,RIx,CIx;
  Elt_t *KElt_p;
  Mtx_t tmpMtx,KMtx;
  // Powers of
  //
  //   -Z = -\zeta_5^2 = e^{\pi i} e^{4\pi i/ 5}
  //      = e^{18\pi i / 10} = (e^{2\pi i / 10))^9 = \zeta_{10}^{-1}
  //           
  COMPLEX_t MinusZeta5Pow[10]={1,-Z,Z2,-Z3,Z4,-1,Z,-Z2,Z3,-Z4};

  // Exponent strings
  char ExpString[10][3]=
    {"??","","^2","^3","^4","^5","^6","^7","^8","^9"};
  char Word1[5],Word2[9];

  KElt_p=KElts;

  // Set up elements of the form:
  //
  //   (-Z)^{Ix1}    0        0
  //          0  (-Z)^{Ix2}   0
  //          0      0        1
  //
  //   = K1^{Ix2} K2 K1^{Ix1} K2

  memset(KMtx,0,sizeof(Mtx_t));
  KMtx[2][2]=1;

  for(Ix1=0;Ix1<10;Ix1++) {
    KMtx[0][0]=MinusZeta5Pow[Ix1];
    if(Ix1 == 0)
      strncpy(Word2,"",1);
    else {
      strncpy(&Word2[0],"K2K1",5);
      strncpy(&Word2[4],ExpString[Ix1],3);
      strncpy(&Word2[4+strlen(ExpString[Ix1])],"K2",3);
    }
    for(Ix2=0;Ix2<10;Ix2++) {
      KMtx[1][1]=MinusZeta5Pow[Ix2];
      if(Ix2 == 0) {
	if(Ix1 == 0)
	  strncpy(Word1,"1",2);
	else
	  strncpy(Word1,"",1);
      }
      else {
	strncpy(&Word1[0],"K1",3);
	strncpy(&Word1[2],ExpString[Ix2],3);
      }
      // Set up the matrix
      assert(CheckUnitary(KMtx,FF0));
      ConjugateMtx(KElt_p->Mtx,KMtx,ConjMtx,ConjMtxInv);
      assert(CheckUnitary(KElt_p->Mtx,FF1));
      // the generator name
      sprintf(KElt_p->Word,"%s%s",Word1,Word2);
      // the ``length''
      if(Ix1 == 0 && Ix2 ==0)
	KElt_p->Len = 0;
      else
	KElt_p->Len = 2;
      // KIx and KStart
      KElt_p->KIx = KElt_p->KStart = (KElt_p-KElts);

      FixUpElt(KElt_p);
      KElt_p++;
    }
  }

  // Set up elements of the form:
  //
  //          0    (-Z)^{Ix1}   0
  //   (-Z)^{Ix2}       0       0
  //          0         0        1
  //
  //   = K1^{Ix2} K2 K1^{Ix1}


  memset(KMtx,0,sizeof(Mtx_t));
  KMtx[2][2]=1;

  for(Ix1=0;Ix1<10;Ix1++) {
    KMtx[0][1]=MinusZeta5Pow[Ix1];
    if(Ix1 == 0)
      strncpy(Word2,"K2",3);
    else {
      strncpy(&Word2[0],"K2K1",5);
      strncpy(&Word2[4],ExpString[Ix1],3);
    }
    for(Ix2=0;Ix2<10;Ix2++) {
      KMtx[1][0]=MinusZeta5Pow[Ix2];
      if(Ix2 == 0) {
	strncpy(Word1,"",1);
      }
      else {
	strncpy(&Word1[0],"K1",3);
	strncpy(&Word1[2],ExpString[Ix2],3);
      }
      // Set up the matrix
      assert(CheckUnitary(KMtx,FF0));
      ConjugateMtx(KElt_p->Mtx,KMtx,ConjMtx,ConjMtxInv);
      assert(CheckUnitary(KElt_p->Mtx,FF1));
      // the generator name
      sprintf(KElt_p->Word,"%s%s",Word1,Word2);
      // the ``length''
      KElt_p->Len = 2;
      // KIx and KStart
      KElt_p->KIx = KElt_p->KStart = (KElt_p-KElts);

      FixUpElt(KElt_p);
      KElt_p++;
    }
  }
}
