/* Return Bravais lattice from cell axes according to
 * classification of Setawan and Curtarolo
 *  Comp. Mater. Sci. 49 299 (2010)
 * 
 * Cell must be presented in canonical form
 *
 * MJR (c) 2025
 */

/* CUB   -- cubic
 * FCC   -- fcc
 * BCC   -- bcc
 * TET   -- tetragonal
 * BCT1  -- body centred tetragonal
 * BCT2  -- body centred tetragonal
 * ORC   -- orthorhombic
 * ORCF1 -- face-centred orthorhobic
 * ORCF2 -- face-centred orthorhobic
 * ORCF3 -- face-centred orthorhobic
 * ORCI  -- body centred orthorhombic
 * ORCC  -- base centred orthorhombic
 * HEX   -- hexagonal
 * RHL1   -- rhombohedral
 * RHL2   -- rhombohedral
 * MCL    -- monoclinic
 * MCLC1  -- base centred monoclinic
 * MCLC2  -- base centred monoclinic
 * MCLC3  -- base centred monoclinic
 * MCLC4  -- base centred monoclinic
 * MCLC5  -- base centred monoclinic
 * TRI1a  -- triclinic
 * TRI1b  -- triclinic
 * TRI2a  -- triclinic
 * TRI2b  -- triclinic
 */

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include "c2xsf.h"
#include<string.h>

#include "specials_sc.h"

#define dswap(a,b) tmp=a;a=b;b=tmp
#define iswap(a,b) itmp=a;a=b;b=itmp

char *bravais_sc(double b[3][3], int perm_in[3]){
  double abc[6],b2[3][3],abc2[6],tmp;
  int i,j,itmp,hit,*perm,idummy[3];

  if (perm_in)
    perm=perm_in;
  else
    perm=idummy;

  for(i=0;i<3;i++) perm[i]=i;
  
  basis2abc(b,abc);

  /*
  if (debug)
    fprintf(stderr,"a=%f b=%f c=%f alpha=%f beta=%f gamma=%f\n",
	    abc[0],abc[1],abc[2],abc[3],abc[4],abc[5]);
  */
  
  if (aeq(abc[0],abc[1])&&aeq(abc[0],abc[2])&&
      aeq(abc[3],abc[4])&&aeq(abc[3],abc[5])){
    if (aeq(abc[3],90)) return "CUB";
    if (aeq(abc[3],acos(-1.0/3)*180/M_PI)) return "BCC";
    if (aeq(abc[3],60)) return "FCC";
    if (abc[3]<90) return "RHL1";
    else return "RHL2";
  }

  for(i=0;i<3;i++){
    if (aeq(abc[i],abc[(i+1)%3])){
      if (aeq(abc[3+i],90)&&aeq(abc[3+(i+1)%3],90)){
	tmp=abc[3+(2+i)%3];
	if (aeq(tmp,60)||aeq(tmp,90)||aeq(tmp,120)){
	  for(j=0;j<3;j++) perm[j]=(i+j)%3;
	  if (aeq(tmp,90)) return "TET";
	  if (aeq(tmp,120)) return "HEX";
	  if (aeq(tmp,60)){
	    perm[1]=-perm[1];
	    return "HEX";
	  }
	}
      }
    }
  }

  if (aeq(abc[3],90)&&aeq(abc[4],90)&&aeq(abc[5],90)){
    if ((abc[0]<abc[1])&&(abc[2]<abc[3])) return "ORC";
    if (abc[0]>abc[1]){
      dswap(abc[0],abc[1]);
      iswap(perm[0],perm[1]);
    }
    if (abc[1]>abc[2]){
      dswap(abc[1],abc[2]);
      iswap(perm[1],perm[2]);
    }
    if (abc[0]>abc[1]){
      dswap(abc[0],abc[1]);
      iswap(perm[0],perm[1]);
    }
    return "ORC";
  }

  /* Body-centred orthorhombic/tetragonal lattices. 
   * If have vectors of (-a/2, b/2, c/2)
                        ( a/2,-b/2, c/2)
	 	        ( a/2, b/2,-c/2)
     then sum the three possible pairs to recreate conventional cell.
     If have vectors of (-a/2, b/2, c/2)
                        ( a/2,-b/2, c/2)
	 	        (-a/2,-b/2, c/2)
     then need to detect by trying to negate each axis in turn
  */

  for(i=0;i<3;i++)
    for(j=0;j<3;j++)
      b2[i][j]=b[(i+1)%3][j]+b[(i+2)%3][j];
  basis2abc(b2,abc2);
  
  if (aeq(abc2[3],90)&&aeq(abc2[4],90)&&aeq(abc2[5],90)){
    for (i=0;i<3;i++){
      if (aeq(abc2[i],abc2[(i+1)%3])){
	for(j=0;j<3;j++) perm[j]=(i+j)%3;
	if (abc2[(i+2)%3]<abc2[i])
	  return "BCT1";
	return "BCT2";
      }
    }
    if ((abc2[0]<abc2[1])&&(abc2[2]<abc2[3])) return "ORCI";
    if (abc2[0]>abc2[1]){
      dswap(abc2[0],abc2[1]);
      iswap(perm[0],perm[1]);
    }
    if (abc2[1]>abc2[2]){
      dswap(abc2[1],abc2[2]);
      iswap(perm[1],perm[2]);
    }
    if (abc2[0]>abc2[1]){
      dswap(abc2[0],abc2[1]);
      iswap(perm[0],perm[1]);
    }
    return "ORCI";
  }

  /* Face-centred lattices.
   * If have vectors of (   0, b/2, c/2)
                        ( a/2,   0, c/2)
	 	        ( a/2, b/2,   0)
   * need to sum in pairs and subtract the third
   */

  for(i=0;i<3;i++)
    for(j=0;j<3;j++)
      b2[i][j]=b[(i+1)%3][j]+b[(i+2)%3][j]-b[i][j];
  basis2abc(b2,abc2);
  
  if (aeq(abc2[3],90)&&aeq(abc2[4],90)&&aeq(abc2[5],90)){
    if (!((abc2[0]<abc2[1])&&(abc2[2]<abc2[3]))){
      if (abc2[0]>abc2[1]){
	dswap(abc2[0],abc2[1]);
	iswap(perm[0],perm[1]);
      }
      if (abc2[1]>abc2[2]){
	dswap(abc2[1],abc2[2]);
	iswap(perm[1],perm[2]);
      }
      if (abc2[0]>abc2[1]){
	dswap(abc2[0],abc2[1]);
	iswap(perm[0],perm[1]);
      }
    }
    if (aeq(1/(abc2[0]*abc2[0]),1/(abc2[1]*abc2[1])+1/(abc2[2]*abc2[2])))
      return "ORCF3";
    if (1/(abc2[0]*abc2[0])>1/(abc2[1]*abc2[1])+1/(abc2[2]*abc2[2]))
      return "ORCF1";
    return "ORCF2";
  }


  /* Base centred lattices.
   * If have vectors of ( a/2,-b/2, 0)
                        ( a/2, b/2, 0)
	 	        (   0,   0, c)
   * need to sum and difference one pair
   */

  for(i=0;i<3;i++){
    for(j=0;j<3;j++) b2[i][j]=b[i][j]+b[(i+1)%3][j];
    for(j=0;j<3;j++) b2[(i+1)%3][j]=b[i][j]-b[(i+1)%3][j];
    for(j=0;j<3;j++) b2[(i+2)%3][j]=b[(i+2)%3][j];
    for(j=0;j<3;j++) perm[j]=(i+j)%3;
    basis2abc(b2,abc2);

    if (aeq(abc2[3],90)&&aeq(abc2[4],90)&&aeq(abc2[5],90)){
      if (!((abc2[0]<abc2[1])&&(abc2[2]<abc2[3]))){
	if (abc2[0]>abc2[1]){
	  dswap(abc2[0],abc2[1]);
	  iswap(perm[0],perm[1]);
	}
	if (abc2[1]>abc2[2]){
	  dswap(abc2[1],abc2[2]);
	  iswap(perm[1],perm[2]);
	}
	if (abc2[0]>abc2[1]){
	  dswap(abc2[0],abc2[1]);
	  iswap(perm[0],perm[1]);
	}
      }
      return "ORCC";
    }
  }
  for(j=0;j<3;j++) perm[j]=j;


  for(i=0;i<3;i++){
    if (aeq(abc[3+(i+1)%3],90)&&aeq(abc[3+(i+2)%3],90)){
	if (i){
	  iswap(perm[0],perm[i]);
	  iswap(perm[1],perm[2]);
	}
      if (abc[3+i]>90) perm[1]=-perm[1];
      if (abc[1]>abc[2]){
	dswap(abc[1],abc[2]);
	iswap(perm[1],perm[2]);
      }
      return "MCL";
    }
  }

  /* Base centred again, for monoclinic */
  
  for(i=0;i<3;i++){
    for(j=0;j<3;j++) b2[i][j]=b[i][j]+b[(i+1)%3][j];
    for(j=0;j<3;j++) b2[(i+1)%3][j]=b[i][j]-b[(i+1)%3][j];
    for(j=0;j<3;j++) b2[(i+2)%3][j]=b[(i+2)%3][j];
    for(j=0;j<3;j++) perm[j]=(i+j)%3;
    basis2abc(b2,abc2);
    
    for(j=0;j<3;j++){
      if (aeq(abc2[3+(j+1)%3],90)&&aeq(abc2[3+(j+2)%3],90)){
	if (j){
	  iswap(perm[0],perm[j]);
	  iswap(perm[1],perm[2]);
	}
	if (abc2[3]<90) perm[1]=-perm[1];
	if (abc2[0]>abc2[2]){
	  dswap(abc2[0],abc2[2]);
	  iswap(perm[0],perm[2]);
	}
	if (abc2[1]>abc2[2]){
	  dswap(abc2[1],abc2[2]);
	  iswap(perm[1],perm[2]);
	}
	if (aeq(abc2[5],90)) return "MCLC2";
	if (abc2[5]>90) return "MCLC1";
	tmp=abc2[1]*cos(abc2[3]*M_PI/180)/abc[2]+
	  pow(abc2[1]*sin(abc2[3]*M_PI/180)/abc[0],2);
	if (aeq(tmp,1)) return "MCLC4";
	if (tmp<1) return "MCLC3";
	return "MCLC5";
      }
    }
  }
  for(j=0;j<3;j++) perm[j]=j;


  
  itmp=1;
  for(i=0;i<3;i++)
    if (abc[3+i]>90) itmp=-itmp;

  if (itmp==1){
    hit=0;
    for(i=0;i<3;i++)
      if (abc[3+i]>90) hit=1;
    if (hit){
      for(i=0;i<3;i++)
	if (abc[3+i]<=90)
	  hit=i;
      perm[i]=-perm[i];
      abc[3+(i+1)%3]=180-abc[3+(i+1)%3];
      abc[3+(i+2)%3]=180-abc[3+(i+2)%3];
    }
    if (abc[3]>abc[5]){
      tmp=abc[2];
      abc[2]=abc[0];
      abc[0]=abc[1];
      abc[1]=tmp;
      tmp=abc[5];
      abc[5]=abc[3];
      abc[3]=abc[4];
      abc[4]=tmp;
    }
    if (abc[4]>abc[5]){
      tmp=abc[2];
      abc[2]=abc[1];
      abc[1]=abc[0];
      abc[0]=tmp;
      tmp=abc[5];
      abc[5]=abc[4];
      abc[4]=abc[3];
      abc[3]=tmp;
    }
    if (aeq(abc[3],90)) return "TRI2b";
    else return "TRI1b";
  }

  /* Here itmp==-1 */
  hit=0;
  for(i=0;i<3;i++)
    if (abc[3+i]<90) hit=1;
  if (hit){
    for(i=0;i<3;i++)
      if (abc[3+i]>=90)
	hit=i;
    perm[i]=-perm[i];
    abc[3+(i+1)%3]=180-abc[3+(i+1)%3];
    abc[3+(i+2)%3]=180-abc[3+(i+2)%3];
  }
  if (abc[3]<abc[5]){
    tmp=abc[2];
    abc[2]=abc[0];
    abc[0]=abc[1];
    abc[1]=tmp;
    tmp=abc[5];
    abc[5]=abc[3];
    abc[3]=abc[4];
    abc[4]=tmp;
  }
  if (abc[4]<abc[5]){
    tmp=abc[2];
    abc[2]=abc[1];
    abc[1]=abc[0];
    abc[0]=tmp;
    tmp=abc[5];
    abc[5]=abc[4];
    abc[4]=abc[3];
    abc[3]=tmp;
  }
  if (aeq(abc[3],90)) return "TRI2a";
  else return "TRI1a";

}

void print_specials(struct special_sc *sp){
  struct pt *p;
  if (!sp) return;
  p=sp->pts;
  while(*p->l){
    fprintf(stderr,"%s (% f, % f, % f )\n",p->l,p->k[0],p->k[1],p->k[2]);
    p++;
  }
}

struct special_sc *bspec_sc(double b[3][3]){
  int i,perm[3],map[3],hit;
  double vec[3];
  char *lat,eq[3];
  struct special_sc *sp;
  struct pt *p;

  lat=bravais_sc(b,perm);

  if (debug) fprintf(stderr,"Bravias_sc lattice type: %s ",lat);
  
  hit=0;
  sp=specials_sc;
  while (*(sp->lat)){
    if (!strcmp(lat,sp->lat)) {hit=1;break;}
    sp++;
  }

  if (!hit){
    fprintf(stderr,"(special points not known)\n");
    return NULL;
  }
  
  fprintf(stderr,"(%s) ",sp->full_name);
  if ((perm[0]==0)&&(perm[1]==1)&&(perm[2]==2)){
    fprintf(stderr,"(special points known)\n");
    if (debug>1) print_specials(sp);
    return sp;
  }

  for(i=0;i<3;i++){
    if (perm[i]>=0) map[perm[i]]=i;
    else map[-perm[i]]=-i;
  }

  p=sp->pts;
  while (*(p->l)){
    for(i=0;i<3;i++){
      vec[i]=p->k[abs(map[i])];
      if ((map[i]<0)&&(vec[i]!=0)&&(vec[i]!=0.5)) vec[i]=-vec[i];
    }
    for(i=0;i<3;i++) p->k[i]=vec[i];
    p++;
  }

  for(i=0;i<3;i++) eq[i]=sp->eq[abs(map[i])];
  for(i=0;i<3;i++) sp->eq[i]=eq[i];
  
  fprintf(stderr,"(special points known after perm)\n");
  if (debug>1) print_specials(sp);  
  
  return sp;
}
