/*
  Space Group Info's (c) 1994-96 Ralf W. Grosse-Kunstleve
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>


#define SGCOREDEF__
#include "sginfo.h"


typedef struct
  {
    int         OriginChoice;
    int         CellChoice;
    int         BasisChoice;
    const char  *BT_or_UA;
  }
  T_ExtInfo;


static const char *Ext_BT_or_UA[] =
  {
    /*  0 */ "abc",
    /*  1 */ "ba-c",
    /*  2 */ "cab",
    /*  3 */ "-cba",
    /*  4 */ "bca",
    /*  5 */ "a-cb",
    /*  6 */         "bac", /* 6 -> 1 */
    /*  7 */         "cba", /* 7 -> 3 */
    /*  8 */         "acb", /* 8 -> 5 */
    /*  9 */ "-b", "b-", "bb", "bb", /* 10, 11, 12 ->  9 */
    /* 13 */ "-c", "c-", "bc", "cb", /* 14, 15, 16 -> 13 */
    /* 17 */ "-a", "a-", "ba", "ab", /* 18, 19, 20 -> 17 */
    /* 21 */ "b",
    /* 22 */ "c",
    /* 23 */ "a",
    NULL
  };


typedef struct
    {
      int     Improper, Rotation, RefAxis, DirCode, Screw;
      T_RTMx  SeitzMx;
    }
    T_HallGenerator;


#define SkipWhite(cp) while (*(cp) && (*(cp) == '_' || isspace(*(cp)))) (cp)++


static const char *IErr_Corrupt_TabSgName =
   "Internal Error: Corrupt TabSgName";


static int FindSchoenfliesSymbol(const char *SfSymbol)
{
  int         SgNumber;
  const char  **TabSymbol;
  const char  *s, *t;


  TabSymbol = SchoenfliesSymbols + 1;

  for (SgNumber = 1; SgNumber <= 230; SgNumber++)
  {
    t = *TabSymbol;
    s = SfSymbol;

    while (*t && *s)
    {
      if (   toupper(*t) != toupper(*s)
          && (*t != '^' || isalpha(*s) || isdigit(*s)))
        break;

      t++;
      s++;
    }

    if (*t == *s)
      return SgNumber;

    TabSymbol++;
  }

  return -1;
}


static int SgLabelCmp(const int SgNumber,
                      const char *SgLabel, const char *WtdLbl)
{
  const char  *sgl, *wl;


  /* first try: plain strcmp
   */
  sgl = SgLabel;

  for (wl = WtdLbl; ; wl++)
  {
    SkipWhite(wl);
    SkipWhite(sgl);

    if (*sgl == '\0' || *sgl == '=')
    {
      if (*wl == '\0') return 0;
      break;
    }

    if (*sgl == '-')
    {
      if (*wl != '-' && toupper(*wl) != 'B')
        break;
    }
    else if (toupper(*sgl) != toupper(*wl))
      break;

    sgl++;
  }

  /* second try: swap the dash (there should be only one)
   */
  sgl = SgLabel;

  for (wl = WtdLbl; ; wl++)
  {
    SkipWhite(wl);
    SkipWhite(sgl);

    if (*sgl == '-')
    {
      if (wl[1] != '-' && toupper(wl[1]) != 'B')
        break;
      if (toupper(sgl[1]) != toupper(*wl))
        break;

      sgl++;
      wl++;
    }
    else
    {
      if (*sgl == '\0' || *sgl == '=')
      {
        if (*wl == '\0') return 0;
        break;
      }

      if (toupper(*sgl) != toupper(*wl))
        break;
    }

    sgl++;
  }

  if (SgNumber >= 195) /* cubic space groups only */
  {
    /* third try: ignore the "-3" dash
     */
    sgl = SgLabel;

    for (wl = WtdLbl; ; wl++)
    {
      SkipWhite(wl);
      SkipWhite(sgl);

      if (*sgl == '-' && sgl[1] == '3')
        sgl++;

      if (*sgl == '\0' || *sgl == '=')
      {
        if (*wl == '\0') return 0;
        break;
      }

      if (toupper(*sgl) != toupper(*wl))
        break;

      sgl++;
    }
  }

  return -1;
}


static int ParseExtension(const char *Ext, T_ExtInfo *ExtInfo)
{
  int         i, mode;
  const char  *e, *t;


  ExtInfo->OriginChoice =
  ExtInfo->CellChoice =
  ExtInfo->BasisChoice = ' ';
  ExtInfo->BT_or_UA = "";

  mode = 0;

  while (*Ext)
  {
    if      (strchr("12",   *Ext) != NULL)
    {
      ExtInfo->CellChoice   =
      ExtInfo->OriginChoice = *Ext++;
    }
    else if (strchr("3",    *Ext) != NULL)
    {
      ExtInfo->CellChoice   = *Ext++;
    }
    else if (strchr("Ss",   *Ext) != NULL)
    {
      ExtInfo->OriginChoice = '1';
      Ext++;
    }
    else if (strchr("Zz",   *Ext) != NULL)
    {
      ExtInfo->OriginChoice = '2';
      Ext++;
    }
    else if (strchr("Hh",   *Ext) != NULL)
    {
      ExtInfo->BasisChoice = 'H';
      Ext++;
    }
    else if (strchr("Rr",   *Ext) != NULL)
    {
      ExtInfo->BasisChoice = 'R';
      Ext++;
    }
    else if (mode == 0)
      mode = 1;

    if (mode == 2)
      break;

    for (i = 0; Ext_BT_or_UA[i]; i++)
    {
      for (e = Ext, t = Ext_BT_or_UA[i]; *t; e++, t++)
        if (toupper(*e) != toupper(*t))
          break;

      if (*t == '\0')
      {
        if      (6 <= i && i <=  8)
          i = 2 * i - 11;
        else if (9 <= i && i <= 20)
          i = 9 + ((i - 9) / 4) * 4;

        ExtInfo->BT_or_UA = Ext_BT_or_UA[i];
        Ext = e;
        break;
      }
    }

    if (mode == 0)
      break;

    mode = 2;
  }

  if (*Ext)
    return -1;

  return 0;
}


static void ExpandMonoclinic(int unique_axis, const char *o, char *m)
{
  if (*o) *m++ = *o++;

  switch (tolower(unique_axis))
  {
    case 'a':
      while (*o) *m++ = *o++;
      *m++ = '1';
      *m++ = '1';
      break;
    case 'c':
      *m++ = '1';
      *m++ = '1';
      while (*o) *m++ = *o++;
      break;
    default:
      *m++ = '1';
      while (*o) *m++ = *o++;
      *m++ = '1';
      break;
  }

  *m = '\0';
}


const T_TabSgName *FindTabSgNameEntry(const char *UserSgName, int VolLetter)
{
#define                   MaxWtdLbl 20
  char     WtdLblOriginal[MaxWtdLbl    + 1];
  char     WtdLblModified[MaxWtdLbl    + 1];
  char    *WtdLbl;
#define          MaxWtdExt  5
  char    WtdExt[MaxWtdExt    + 1];
  int     WtdSgNumber;
  int     WtdLblOriginChoice;
  int     WtdLblBasisChoice;
  int     iwl, iwe;
  char    *wl, *we;

  int                i, IsExpanded, lbl_match;
  const char         *sgl;
  const T_TabSgName  *tsgn;
  int                 WtdCC;
  const char         *WtdUA;
  char                WtdUA_Buf[2];
  T_ExtInfo          ExtInfo, WtdExtInfo;


  if      (VolLetter == 0 || isspace(VolLetter))
    VolLetter = 'A';
  else if (VolLetter == '1')
    VolLetter = 'I';
  else
  {
           VolLetter = toupper(VolLetter);
    if (   VolLetter != 'I'
        && VolLetter != 'A')
      return NULL;
  }

  WtdLbl = WtdLblOriginal;

   wl = WtdLbl;
  iwl = 0;

  while (*UserSgName && *UserSgName != ':')
  {
    if (isspace(*UserSgName) == 0 && *UserSgName != '_')
    {
      if (iwl >= MaxWtdLbl)
        return NULL;

      *wl++ = *UserSgName;
      iwl++;
    }

    UserSgName++;
  }

  *wl = '\0';

  if (iwl == 0)
    return NULL;

   we = WtdExt;
  iwe = 0;
  *we = '\0';

  if (*UserSgName)
  {
    UserSgName++;

    while (*UserSgName)
    {
      if (isspace(*UserSgName) == 0 && *UserSgName != '_')
      {
        if (iwe >= MaxWtdExt)
          return NULL;

        *we++ = *UserSgName;
        iwe++;
      }

      UserSgName++;
    }
  }

  *we = '\0';

  WtdLblOriginChoice = ' ';
  WtdLblBasisChoice  = ' ';

  if (iwl > 1)
  {
    wl = &WtdLbl[iwl - 1];

    if      (*wl == 'S' || *wl == 's')
    { WtdLblOriginChoice = '1'; *wl = '\0'; iwl--; }
    else if (*wl == 'Z' || *wl == 'z')
    { WtdLblOriginChoice = '2'; *wl = '\0'; iwl--; }
    else if (*wl == 'H' || *wl == 'h')
    { WtdLblBasisChoice  = 'H'; *wl = '\0'; iwl--; }
    else if (*wl == 'R' || *wl == 'r')
    { WtdLblBasisChoice  = 'R'; *wl = '\0'; iwl--; }
  }

  if (isalpha(WtdLbl[0]))
    WtdSgNumber = FindSchoenfliesSymbol(WtdLbl);
  else
  {
    for (wl = WtdLbl; *wl; wl++)
      if (isdigit(*wl) == 0)
        return NULL;

    if (   sscanf(WtdLbl, "%d", &WtdSgNumber) != 1
        || WtdSgNumber <   1
        || WtdSgNumber > 230)
      return NULL;
  }

  if (ParseExtension(WtdExt, &WtdExtInfo) != 0)
    return NULL;

  if      (WtdExtInfo.OriginChoice == ' ')
           WtdExtInfo.OriginChoice =  WtdLblOriginChoice;
  else if (WtdExtInfo.OriginChoice != WtdLblOriginChoice
                                   && WtdLblOriginChoice != ' ')
    return NULL;

  if      (WtdExtInfo.BasisChoice == ' ')
           WtdExtInfo.BasisChoice =  WtdLblBasisChoice;
  else if (WtdExtInfo.BasisChoice != WtdLblBasisChoice
                                  && WtdLblBasisChoice != ' ')
    return NULL;

  if (   WtdExtInfo.OriginChoice != ' '
      && WtdExtInfo.BasisChoice  != ' ')
    return NULL;

  for (IsExpanded = 0; IsExpanded < 4; IsExpanded++)
  {
    for (tsgn = TabSgName; tsgn->HallSymbol; tsgn++)
    {
      if (   IsExpanded != 0
          && tsgn->SgNumber > 15)
        break;

      lbl_match = 0;

      if (WtdSgNumber == -1)
      {
        i = 1;
                sgl = tsgn->SgLabels;
        while (*sgl && i <= 2)
        {
          while (*sgl && strchr(" =\t", *sgl) != NULL) sgl++;

          if (SgLabelCmp(tsgn->SgNumber, sgl, WtdLbl) == 0)
          {
            lbl_match = i;
            break;
          }

          while (*sgl && strchr(" =\t", *sgl) == NULL) sgl++;

          i++;
        }
      }

      if (ParseExtension(tsgn->Extension, &ExtInfo) != 0) {
        SetSgError(IErr_Corrupt_TabSgName);
        return NULL;
      }

      if (WtdSgNumber == tsgn->SgNumber || lbl_match != 0)
      {
        if (   tsgn->SgNumber >=  3
            && tsgn->SgNumber <  16)
        {
          if (   WtdLblOriginChoice != ' '
              || WtdExtInfo.BasisChoice != ' '
              || (int) strlen(WtdExtInfo.BT_or_UA) > 2)
            continue; /* next tsgn */

          if (WtdSgNumber == tsgn->SgNumber)
          {
            if (WtdExtInfo.BT_or_UA[0])
              WtdUA = WtdExtInfo.BT_or_UA;
            else if (VolLetter == 'I')
            {
              if (   ExtInfo.BT_or_UA[0] != 'c'
                  && ExtInfo.BT_or_UA[1] != 'c')
                continue; /* next tsgn */

              if (   ExtInfo.CellChoice == ' '
                  && (   WtdExtInfo.CellChoice == ' '
                      || WtdExtInfo.CellChoice == '1'))
                return tsgn;

              i = 0;
              for (sgl = tsgn->SgLabels; *sgl; sgl++)
                if (*sgl == '=') i++;

              if (   i == 2
                  && (   WtdExtInfo.CellChoice == ' '
                      || WtdExtInfo.CellChoice == ExtInfo.CellChoice))
                return tsgn;

              continue; /* next tsgn */
            }
            else
              WtdUA = "b";
          }
          else /* if (lbl_match != 0) */
          {
            if (WtdExtInfo.BT_or_UA[0])
              WtdUA = WtdExtInfo.BT_or_UA;
            else if (lbl_match > 1)
              WtdUA = ExtInfo.BT_or_UA;
            else if (   VolLetter == 'I'
                     && ExtInfo.CellChoice == ' ')
              WtdUA = "c";
            else
              WtdUA = "b";
          }

          if (WtdExtInfo.CellChoice != ' ')
            WtdCC = WtdExtInfo.CellChoice;
          else if (ExtInfo.CellChoice == '1')
            WtdCC = ExtInfo.CellChoice;
          else
            WtdCC = ' ';

          if (strcmp(ExtInfo.BT_or_UA, WtdUA) == 0)
          {
            if (WtdCC == ' ' && lbl_match > 1)
              return tsgn;
            if (ExtInfo.CellChoice == WtdCC)
              return tsgn;
            if (ExtInfo.CellChoice == ' ' && WtdCC == '1')
              return tsgn;
            if (ExtInfo.CellChoice == '1' && WtdCC == ' ')
              return tsgn;
          }
        }
        else if (ExtInfo.BasisChoice != ' ')
        {
          if (   WtdExtInfo.OriginChoice != ' '
              || WtdExtInfo.CellChoice != ' '
              || WtdExtInfo.BT_or_UA[0] != '\0')
            continue; /* next tsgn */

          if (ExtInfo.BasisChoice == WtdExtInfo.BasisChoice)
            return tsgn;

          if (WtdExtInfo.BasisChoice == ' ')
          {
            if (ExtInfo.BasisChoice == 'R' && VolLetter == 'I')
              return tsgn;
            if (ExtInfo.BasisChoice == 'H' && VolLetter != 'I')
              return tsgn;
          }
        }
        else if (WtdExtInfo.BasisChoice == ' ')
        {
          if ( (WtdExtInfo.OriginChoice == ' ' && ExtInfo.OriginChoice == '1')
            || (WtdExtInfo.OriginChoice == '1' && ExtInfo.OriginChoice == ' ')
            ||  WtdExtInfo.OriginChoice ==        ExtInfo.OriginChoice)
          {
            if (WtdExtInfo.BT_or_UA[0])
            {
              if (WtdExtInfo.BT_or_UA == ExtInfo.BT_or_UA)
                return tsgn;
              if (   WtdExtInfo.BT_or_UA == Ext_BT_or_UA[0]
                  &&    ExtInfo.BT_or_UA[0] == '\0')
                return tsgn;
            }
            else
            {
              if (lbl_match != 0)
                return tsgn;
              if (ExtInfo.BT_or_UA[0] == '\0')
                return tsgn;
            }
          }
        }
      }
    }

    if (WtdSgNumber != -1)
      return NULL;

    if ((int) strlen(WtdExtInfo.BT_or_UA) > 2)
      return NULL;

    if (IsExpanded == 0)
    {
      iwl += 2;

      if (iwl > MaxWtdLbl)
        IsExpanded = 2;
      else
      {
        if (WtdExtInfo.BT_or_UA[0])
          WtdUA = WtdExtInfo.BT_or_UA;
        else
        {
          if (VolLetter == 'I')
            WtdUA = "c";
          else
            WtdUA = "b";
        }

        ExpandMonoclinic(WtdUA[0], WtdLblOriginal, WtdLblModified);

        WtdLbl = WtdLblModified;
      }
    }
    else if (IsExpanded == 1)
    {
      if (WtdExtInfo.BT_or_UA[0])
        return NULL;

      if (VolLetter == 'I')
        WtdUA = "b";
      else
        WtdUA = "c";

      ExpandMonoclinic(WtdUA[0], WtdLblOriginal, WtdLblModified);
    }

    if (IsExpanded == 2)
    {
      if (WtdExtInfo.BT_or_UA[0])
        return NULL;

      iwl -= 2;

      if (iwl < 2)
        return NULL;
                                            iwl--;
      WtdUA_Buf[0] = tolower(WtdLblOriginal[iwl]);
                             WtdLblOriginal[iwl] = '\0';
      WtdUA_Buf[1] = '\0';

      if (strchr("abc", WtdUA_Buf[0]) == NULL)
        return NULL;

      WtdUA = WtdUA_Buf;

      iwl += 2;

      if (iwl > MaxWtdLbl)
        return NULL;

      ExpandMonoclinic(WtdUA[0], WtdLblOriginal, WtdLblModified);

      WtdLbl = WtdLblModified;
    }
  }

  return NULL;
}


unsigned int SgID_Number(const T_TabSgName *tsgn)
{
  unsigned int  ID;
  int           iBT;
  const char    *UA;
  T_ExtInfo     ExtInfo;


  ID = tsgn->SgNumber;

  if (ParseExtension(tsgn->Extension, &ExtInfo) != 0)
    ID = 0;

  if (ID >= 3 && ID < 16)
  {
    UA = ExtInfo.BT_or_UA;

    if (   *UA != 'b'
        || (   ExtInfo.CellChoice != ' '
            && ExtInfo.CellChoice != '1'))
    {
      if (*UA == '-')
      {
        ID += 3000;
        UA++;
      }

      switch (*UA)
      {
        case 'b': ID += 10000; break;
        case 'c': ID += 20000; break;
        case 'a': ID += 30000; break;
        default:  ID = 0;      break;
      }

      if (ID != 0)
      {
        switch (ExtInfo.CellChoice)
        {
          case ' ':             break;
          case '1': ID += 1000; break;
          case '2': ID += 2000; break;
          case '3': ID += 3000; break;
          default:  ID = 0;     break;
        }
      }
    }
  }
  else
  {
    if (ExtInfo.BasisChoice == 'R')
      ID += 20000;
    else
    {
      if (ExtInfo.BT_or_UA[0])
      {
        for (iBT = 0; iBT < 6; iBT++)
          if (ExtInfo.BT_or_UA == Ext_BT_or_UA[iBT])
            break;
      }
      else
        iBT = 0;

      if (iBT < 6)
      {
        if (ExtInfo.OriginChoice == '2') ID += 20000;
        else if (iBT)                    ID += 10000;

        if (iBT)
          ID += (iBT + 1) * 1000;
      }
      else
        ID = 0;
    }
  }

  if (ID == 0)
    SetSgError(IErr_Corrupt_TabSgName);

  return ID;
}


int ParseSymXYZ(const char *SymXYZ, T_RTMx *SeitzMx, int FacTr)
{
  unsigned int  P_mode;
  int           Row, Column, Sign, GotXYZ, i;
  double        Value, Value1, Value2, Delta;


  for (i = 0; i < 12; i++) SeitzMx->a[i] = 0;

#define P_Blank   0x01u
#define P_Comma   0x02u
#define P_Plus    0x04u
#define P_Dash    0x08u
#define P_Slash   0x10u
#define P_Value1  0x20u
#define P_Value2  0x40u
#define P_XYZ     0x80u

  Value1 = 0.;

  Row    = 0;
  Sign   = 1;
  Value  = 0.;
  GotXYZ = 0;
  P_mode = P_Blank | P_Plus | P_Dash | P_Value1 | P_XYZ;

  do
  {
    switch (*SymXYZ)
    {
      case ' ':
      case '\t':
      case '_':
        if ((P_mode & P_Blank) == 0) return -1;
        break;
      case ',':
      case ';':
        if (Row == 2)                return -1;
      case '\0':
        if ((P_mode & P_Comma) == 0) return -1;
        if (GotXYZ == 0)             return -1;
        if (P_mode & P_Slash) Value += Value1;
        Value *= FacTr;
        if (Value < 0.) i = Value - .5;
        else            i = Value + .5;
        Delta = Value - i;
        if (Delta < 0.) Delta = -Delta;
        if (Delta > .01 * FacTr) return -1;
        i %= FacTr; if (i < 0) i += FacTr;
        SeitzMx->s.T[Row] = i;
        Row++;
        Sign   = 1;
        Value  = 0.;
        P_mode = P_Blank | P_Plus | P_Dash | P_Value1 | P_XYZ;
        GotXYZ = 0;
        break;
      case '+':
        if ((P_mode & P_Plus)  == 0) return -1;
        if (P_mode & P_Slash) Value += Value1;
        Sign =  1;
        if (P_mode & P_Value2)
          P_mode = P_Value2;
        else
          P_mode = P_Blank | P_Value1 | P_XYZ;
        break;
      case '-':
        if ((P_mode & P_Dash)  == 0) return -1;
        if (P_mode & P_Slash) Value += Value1;
        Sign = -1;
        if (P_mode & P_Value2)
          P_mode = P_Value2;
        else
          P_mode = P_Blank | P_Value1 | P_XYZ;
        break;
      case '/':
      case ':':
        if ((P_mode & P_Slash) == 0) return -1;
        Sign =  1;
        P_mode = P_Blank | P_Plus | P_Dash | P_Value2;
        break;
      case '.':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        if      (P_mode & P_Value1)
        {
          if (sscanf(SymXYZ, "%lf%n", &Value1, &i) != 1) return -1;
          if (Sign == -1) Value1 = -Value1;
          P_mode = P_Blank | P_Comma | P_Plus | P_Dash | P_Slash;
        }
        else if (P_mode & P_Value2)
        {
          if (sscanf(SymXYZ, "%lf%n", &Value2, &i) != 1) return -1;
          if (Sign == -1) Value2 = -Value2;
          if (Value1 != 0.)
          {
            if (Value2 == 0.) return -1;
            Value += Value1 / Value2;
          }
          P_mode = P_Blank | P_Comma | P_Plus | P_Dash;
        }
        else
          return -1;
        SymXYZ += (i - 1);
        break;
      case 'X':
      case 'x': Column = 0; goto Process_XYZ;
      case 'Y':
      case 'y': Column = 1; goto Process_XYZ;
      case 'Z':
      case 'z': Column = 2;
       Process_XYZ:
        if ((P_mode & P_XYZ) == 0) return -1;
        i = Row * 3 + Column;
        if (SeitzMx->s.R[i] != 0) return -1;
        SeitzMx->s.R[i] = Sign;
        GotXYZ = 1;
        P_mode = P_Blank | P_Comma | P_Plus | P_Dash;
        break;
    }
  }
  while (*SymXYZ++);

  if (Row != 3) return -1;

  return 0;

#undef P_Blank
#undef P_Comma
#undef P_Plus
#undef P_Dash
#undef P_Slash
#undef P_Value1
#undef P_Value2
#undef P_XYZ
}


static int LookupRotMx(T_HallGenerator *HG)
{
  int                   i, f, refaxis, dircode;
  int                   iNextBasis, nNextBasis;
  const T_TabXtalRotMx  *txrmx;


  if (HG->Rotation <= 0) return 0;

  refaxis = HG->RefAxis;
  dircode = HG->DirCode;

  if (HG->Rotation == 1)
  {
    refaxis = 'o';
    dircode = '.';
    nNextBasis = 0;
  }
  else if (dircode == '*')
  {
    if (refaxis == 0) refaxis = 'o';
    nNextBasis = 0;
  }
  else
  {
    if (dircode == 0) dircode = '=';

    switch (refaxis)
    {
      case 'z': nNextBasis = 0; break;
      case 'x': nNextBasis = 1; break;
      case 'y': nNextBasis = 2; break;
      default:
        return 0;
    }
  }

  for (txrmx = TabXtalRotMx; txrmx->Order; txrmx++)
    if (txrmx->Order == HG->Rotation) break;

  while (txrmx->Order == HG->Rotation)
  {
    if (txrmx->DirCode == dircode)
    {
      if (HG->Improper == 0) f =  1;
      else                   f = -1;

      for (i = 0; i < 9; i++)
        HG->SeitzMx.s.R[i] = txrmx->RMx[i] * f;

      for (iNextBasis = 0; iNextBasis < nNextBasis; iNextBasis++)
        RotateRotMx(HG->SeitzMx.s.R, RMx_3_111, RMx_3i111);

      return 1;
    }

    txrmx++;
  }

  return 0;
}


int ParseHallSymbol(const char *hsym, T_SgInfo *SgInfo)
{
  int                  c, i, pos_hsym;
  const int            *ht;
  int                  Centric;
  const T_LatticeInfo  *LatticeInfo;
  int                  FieldType, PreviousFT;
  int                  iOriginShift, SignOriginShift;
  int                  digit, rotation, refaxis, dircode;
  const int            *translation;
  int                  PreviousRotation, PreviousRefAxis;
  int                  nHG, ClearHG;
  T_HallGenerator      HG;

  enum ListOfFieldTypes
    {
      FT_Delimiter,
      FT_Improper,
      FT_Digit,
      FT_Rotation,
      FT_RefAxis,
      FT_DirCode,
      FT_Translation,
      FT_OriginShift
    };

  static const char *Err_Ill_ori_shi_val =
    "Error: Illegal origin shift value";

  static const char *Err_Too_ori_shi_val =
    "Error: Too much origin shift values";


  Centric = 0;
  LatticeInfo = NULL;

  HG.Rotation = HG.RefAxis = HG.DirCode = HG.Screw = 0;

  nHG = 0;
  ClearHG = 1;
  FieldType = FT_Delimiter;
  PreviousRotation = 0;
  PreviousRefAxis = 0;
  iOriginShift = 0;
  SignOriginShift = 0;

  pos_hsym = 0;

  do
  {
    if (*hsym == '_' || *hsym == '.' || *hsym == '\t' || *hsym == '\0')
      c = ' ';
    else
      c = *hsym;

    pos_hsym++;

    if (LatticeInfo == NULL)
    {
      if (Centric == 0 && c == '-')
      {
        if (AddInversion2ListSeitzMx(SgInfo) < 0)
          return pos_hsym;
        Centric = 1;
      }
      else if (c != ' ')
      {
        c = toupper(c);

        switch (c)
        {
          case 'P': LatticeInfo = LI_P; break;
          case 'A': LatticeInfo = LI_A; break;
          case 'B': LatticeInfo = LI_B; break;
          case 'C': LatticeInfo = LI_C; break;
          case 'I': LatticeInfo = LI_I; break;
          case 'R': LatticeInfo = LI_R; break;
          case 'S': LatticeInfo = LI_S; break;
          case 'T': LatticeInfo = LI_T; break;
          case 'F': LatticeInfo = LI_F; break;
          default:
            SetSgError("Error: Illegal lattice code");
            return pos_hsym;
        }

        if (AddLatticeTr2ListSeitzMx(SgInfo, LatticeInfo) < 0)
          return pos_hsym;
      }
    }
    else if (FieldType != FT_OriginShift)
    {
      c = tolower(c);
      if      (c == 'q') c = '\'';
      else if (c == '+') c = '"';

      PreviousFT = FieldType;
      digit = rotation = refaxis = dircode = 0;
      translation = NULL;

      ht = HallTranslations;

      while (*ht)
      {
        if (c == *ht)
        {
          translation = ht;
          FieldType = FT_Translation;
          break;
        }
        ht += 4;
      }

      if (translation == NULL)
      {
        switch (c)
        {
          case  ' ': FieldType = FT_Delimiter; break;

          case  '-': FieldType = FT_Improper; break;

          case  '1': digit = 1; FieldType = FT_Digit; break;
          case  '2': digit = 2; FieldType = FT_Digit; break;
          case  '3': digit = 3; FieldType = FT_Digit; break;
          case  '4': digit = 4; FieldType = FT_Digit; break;
          case  '5': digit = 5; FieldType = FT_Digit; break;
          case  '6': digit = 6; FieldType = FT_Digit; break;

          case  'x':
          case  'y':
          case  'z': refaxis = c; FieldType = FT_RefAxis; break;

          case  '"':
          case '\'':
          case  '*': dircode = c; FieldType = FT_DirCode; break;

          case  '(': FieldType = FT_OriginShift; break;

          default:
            SetSgError("Error: Illegal character in Hall symbol");
            return pos_hsym;
        }

        if (FieldType == FT_Digit)
        {
          if (   ClearHG == 0
              && HG.Rotation > digit
              && HG.Screw == 0
              && HG.DirCode == 0)
          {
            HG.Screw = digit;
            FieldType = FT_Translation;
          }
          else if (digit == 5)
          {
            SetSgError("Error: Illegal 5-fold rotation");
            return pos_hsym;
          }
          else
          {
            rotation = digit;
            FieldType = FT_Rotation;
          }
        }
      }

      if (   ClearHG == 0
          && (    FieldType == FT_Delimiter
              ||  FieldType == FT_OriginShift
              ||  FieldType  < PreviousFT
              || (FieldType == PreviousFT && FieldType != FT_Translation))
          && ! (   FieldType == FT_RefAxis && HG.RefAxis == 0
                && PreviousFT == FT_DirCode))
      {
        if (HG.RefAxis == 0)
        {
          if (nHG == 0)
            HG.RefAxis = 'z';
          else
          {
            if (HG.Rotation == 2)
            {
              if      (PreviousRotation == 2 || PreviousRotation == 4)
                HG.RefAxis = 'x';
              else if (PreviousRotation == 3 || PreviousRotation == 6)
              {
                HG.RefAxis = PreviousRefAxis;
                if (HG.DirCode == 0) HG.DirCode = '\'';
              }
            }
            else if (HG.Rotation == 3)
            {
              if (HG.DirCode == 0) HG.DirCode = '*';
            }
          }
        }

        PreviousRefAxis = HG.RefAxis;
        PreviousRotation = HG.Rotation;

        if (LookupRotMx(&HG) == 0)
        {
          SetSgError("Error: Illegal generator or need explicit axis symbol");
          return pos_hsym - 1;
        }

        if (HG.Screw)
        {
          switch (HG.RefAxis)
          {
            case 'x': i =  0; break;
            case 'y': i =  1; break;
            case 'z': i =  2; break;
            default:  i = -1; break;
          }

          if (HG.DirCode != 0 || i < 0)
          {
            SetSgError("Error: Screw for non-principal direction");
            return pos_hsym - 1;
          }

          HG.SeitzMx.s.T[i] += STBF * HG.Screw / HG.Rotation;
        }

        for (i = 0; i < 3; i++)
          HG.SeitzMx.s.T[i] %= STBF;

        if (Add2ListSeitzMx(SgInfo, &HG.SeitzMx) < 0)
          return pos_hsym - 1;

        if (SgInfo->StatusLatticeTr == -1)
        {
          if (AddLatticeTr2ListSeitzMx(SgInfo, SgInfo->LatticeInfo) < 0)
            return pos_hsym - 1;
        }

        nHG++;
        ClearHG = 1;
      }

      if (FieldType != FT_Delimiter && FieldType != FT_OriginShift)
      {
        if (ClearHG)
        {
          HG.Improper = 0;
          HG.Rotation = 1;
          HG.RefAxis = 0;
          HG.DirCode = 0;
          HG.Screw = 0;
          for (i = 0; i < 12; i++) HG.SeitzMx.a[i] = 0;

          ClearHG = 0;
        }

        switch (FieldType)
        {
          case FT_Improper:    HG.Improper = 1;        break;
          case FT_Rotation:    HG.Rotation = rotation; break;
          case FT_RefAxis:     HG.RefAxis  = refaxis;  break;
          case FT_DirCode:     HG.DirCode  = dircode;  break;
          case FT_Translation:
            if (translation != NULL)
            {
              for (i = 0; i < 3; i++)
                HG.SeitzMx.s.T[i] += *(++translation);
            }
            break;
        }
      }
    }
    else /* FieldType == FT_OriginShift */
    {
      if (iOriginShift > 3) {
        SetSgError(Err_Too_ori_shi_val);
        return pos_hsym;
      }

      if (*hsym == '\0') c = ')';

      digit = -1;

      switch (c)
      {
        case ' ': break;

        case ')':
          if (iOriginShift != 3)
          {
            SetSgError("Error: Missing origin shift values");
            return pos_hsym;
          }
          iOriginShift++;
          FieldType = FT_Delimiter;
          break;

        case '-':
          if (SignOriginShift != 0) {
            SetSgError(Err_Ill_ori_shi_val);
            return pos_hsym;
          }
          SignOriginShift = 1;
          break;

        case '0': digit = 0; break;
        case '1': digit = 1; break;
        case '2': digit = 2; break;
        case '3': digit = 3; break;
        case '4': digit = 4; break;
        case '5': digit = 5; break;
        case '6': digit = 6; break;

        default:
          SetSgError(Err_Ill_ori_shi_val);
          return pos_hsym;
      }

      if (digit >= 0)
      {
        if (iOriginShift >= 3) {
          SetSgError(Err_Too_ori_shi_val);
          return pos_hsym;
        }
        if (SignOriginShift) digit *= -1;
        SignOriginShift = 0;
        SgInfo->OriginShift[iOriginShift++] = digit;
      }
    }
  }
  while (*hsym++ != '\0');

  if (LatticeInfo == NULL) {
    SetSgError("Error: Lattice type not specified");
    return pos_hsym;
  }

  return pos_hsym;
}


static const char *PrintSgLabel(const char *lbl, int space, int *n,
                                FILE *fpout)
{
  while (*lbl && *lbl != ' ')
  {
    if (*lbl == '_')
    {
      if (space)
      {
        putc(space, fpout);
        if (n) (*n)++;
      }
    }
    else
    {
      putc(*lbl, fpout);
      if (n) (*n)++;
    }

    lbl++;
  }

  return lbl;
}


int PrintFullHM_SgName(const T_TabSgName *tsgn, int space, FILE *fpout)
{
  int         n;
  const char  *lbl;


  lbl = tsgn->SgLabels;

  if (tsgn->SgNumber >= 3 && tsgn->SgNumber < 16)
    while (*lbl) if (*lbl++ == '=') break;

  SkipWhite(lbl);

  n = 0;

  PrintSgLabel(lbl, space, &n, fpout);

  lbl = tsgn->Extension;

  if (*lbl && strchr("12HhRr", *lbl))
  {
    putc(':', fpout);
    putc(*lbl, fpout);
    n += 2;
  }

  return n;
}


void PrintTabSgNameEntry(const T_TabSgName *tsgn, int Style, int space,
                         FILE *fpout)
{
  int         n;
  const char  *lbl, *SfSymbol;


  if (Style)
    n = fprintf(fpout, "%3d", tsgn->SgNumber);
  else
    n = fprintf(fpout,  "%d", tsgn->SgNumber);

  if (tsgn->Extension[0])
    n += fprintf(fpout, ":%s", tsgn->Extension);

  if (Style)
    while (n < 9) { putc(' ', fpout); n++; }

  putc(' ', fpout); n++;
  putc(' ', fpout); n++;

  if (tsgn->SgNumber >= 1 && tsgn->SgNumber <= 230)
    SfSymbol = SchoenfliesSymbols[tsgn->SgNumber];
  else
    SfSymbol = "";

  n += fprintf(fpout, "%s", SfSymbol);

  if (Style)
    while (n < 23) { putc(' ', fpout); n++; }

  putc(' ', fpout); n++;
  putc(' ', fpout); n++;

  if (tsgn->SgNumber >= 3 && tsgn->SgNumber < 16)
  {
    lbl = PrintSgLabel(tsgn->SgLabels, space, &n, fpout);

    if (tsgn->Extension[0])
      n += fprintf(fpout, ":%s", tsgn->Extension);

    putc(' ', fpout); putc('=', fpout); putc(' ', fpout); n += 3;

    n += PrintFullHM_SgName(tsgn, space, fpout);

    while (*lbl) if (*lbl++ == '=') break;
    while (*lbl) if (*lbl++ == '=') break;
    SkipWhite(lbl);

    if (*lbl)
    {
      putc(' ', fpout); putc('=', fpout); putc(' ', fpout); n += 3;

      PrintSgLabel(lbl, space, &n, fpout);
    }
  }
  else
    n += PrintFullHM_SgName(tsgn, space, fpout);

  if (Style)
    while (n < 51) { putc(' ', fpout); n++; }

  putc(' ', fpout);
  putc(' ', fpout);

  fprintf(fpout, "%s", tsgn->HallSymbol);
}


static int FindGCD2(int ri, int rj)
{
  int  rk;


  if (ri < 0) ri = -ri;

  if (rj)
  {
    for (;;)
    {
      rk = ri % rj; if (rk == 0) { ri = rj; break; }
      ri = rj % rk; if (ri == 0) { ri = rk; break; }
      rj = rk % ri; if (rj == 0) {          break; }
    }

    if (ri < 0) ri = -ri;
  }

  return ri;
}


static void SimplifyFraction(int nume, int deno, int *o_nume, int *o_deno)
{
  int gcd = FindGCD2(nume, deno);
  if (gcd)
  {
    *o_nume = nume / gcd;
    *o_deno = deno / gcd;

    if (*o_deno < 0) {
      *o_nume *= -1;
      *o_deno *= -1;
    }
  }
}


const char *FormatFraction(int nume, int deno, int Decimal,
                           char *Buffer, int SizeBuffer)
{
  int          n, d;
  char         *cp, *cpp;
  static char  StaticBuffer[40];


  if (NULL == Buffer) {
              Buffer =        StaticBuffer;
          SizeBuffer = sizeof StaticBuffer / sizeof (*StaticBuffer);
  }

  Buffer[SizeBuffer - 1] = '\0';

  if (nume == 0)
  {
    Buffer[0] = '0';
    Buffer[1] = '\0';
  }
  if (Decimal)
  {
    sprintf(Buffer, "%.6g", (double) nume / deno);

         cp = Buffer;
    if (*cp == '-') cp++;
    if (*cp == '0') {
      cpp = cp + 1; while (*cp) *cp++ = *cpp++;
    }
  }
  else
  {
    SimplifyFraction(nume, deno, &n, &d);

    if (d == 1)
      sprintf(Buffer, "%d", n);
    else
      sprintf(Buffer, "%d/%d", n, d);
  }

  if (Buffer[SizeBuffer - 1] != '\0') {
      Buffer[SizeBuffer - 1] =  '\0';
    SetSgError("Internal Error: FormatFraction(): Buffer too small");
    return NULL;
  }

  return Buffer;
}


const char *RTMx2XYZ(const T_RTMx *RTMx, int FacRo, int FacTr,
                     int Decimal, int TrFirst, int Low,
                     const char *Seperator,
                     char *BufferXYZ, int SizeBufferXYZ)
{
  static const char *UpperXYZ = "XYZ";
  static const char *LowerXYZ = "xyz";

  int         i, j, p, iRo, iTr;
  char        *xyz, buf_tr[32];
  const char  *sep, *LetterXYZ, *ro, *tr;

  static char  StaticBufferXYZ[80];


  if (NULL == BufferXYZ) {
              BufferXYZ  =        StaticBufferXYZ;
          SizeBufferXYZ  = sizeof StaticBufferXYZ / sizeof (*StaticBufferXYZ);
  }

  BufferXYZ[SizeBufferXYZ - 1] = '\0';

  if (Low)
    LetterXYZ = LowerXYZ;
  else
    LetterXYZ = UpperXYZ;

  if (Seperator == NULL)
      Seperator = ",";

  xyz = BufferXYZ;

  for (i = 0; i < 3; i++)
  {
    if (i != 0)
      for (sep = Seperator; *sep; sep++) *xyz++ = *sep;

        iTr = iModPositive(RTMx->s.T[i], FacTr);
    if (iTr >  FacTr / 2)
        iTr -= FacTr;

        tr = FormatFraction(iTr, FacTr, Decimal,
                            buf_tr, sizeof buf_tr / sizeof (*buf_tr));
    if (tr == NULL)
      return NULL;

    p = 0;

    if (  TrFirst && iTr) {
      if (*tr) p = 1;
      while (*tr) *xyz++ = *tr++;
    }

    for (j = 0; j < 3; j++)
    {
          iRo = RTMx->s.R[i * 3 + j];
      if (iRo)
      {
            ro = FormatFraction(iRo, FacRo, Decimal, NULL, 0);
        if (ro == NULL)
          return NULL;

        if      (*ro == '-')
          *xyz++ = *ro++;
        else if (*ro && p)
          *xyz++ = '+';

        if (ro[0] != '1' || ro[1] != '\0') {
          while (*ro) *xyz++ = *ro++;
          *xyz++ = '*';
        }

        *xyz++ = LetterXYZ[j];

        p = 1;
      }
    }

    if (! TrFirst && iTr)
    {
      if (*tr && *tr != '-' && p)
        *xyz++ = '+';

      while (*tr) *xyz++ = *tr++;
    }
  }

  *xyz = '\0';

  if (BufferXYZ[SizeBufferXYZ - 1] != '\0') {
      BufferXYZ[SizeBufferXYZ - 1] =  '\0';
    SetSgError("Internal Error: RTMx2XYZ(): BufferXYZ too small");
    return NULL;
  }

  return BufferXYZ;
}


void PrintMapleRTMx(const T_RTMx *RTMx, int FacRo, int FacTr,
                    const char *Label, FILE *fpout)
{
  int         i, j, nt;
  const int   *r, *t;
  const char  *ff;


  if (Label)
    fprintf(fpout, "%s", Label);

  fprintf(fpout, " := matrix(4,4, [");

  r = RTMx->s.R;
  t = RTMx->s.T;

  for (i = 0; i < 3; i++, t++)
  {
    putc(' ', fpout);

    for (j = 0; j < 3; j++, r++)
    {
          ff = FormatFraction(*r, FacRo, 0, NULL, 0);
      if (ff == NULL)
        return;

      fprintf(fpout, "%s,", ff);
    }

        nt = iModPositive(*t, FacTr);
    if (nt >  FacTr / 2)
        nt -= FacTr;

        ff = FormatFraction(nt, FacTr, 0, NULL, 0);
    if (ff == NULL)
      return;

    fprintf(fpout, "%s,", ff);
  }

  fprintf(fpout, " 0,0,0,1]);\n");
}


static void PrintSeitzMx(const T_RTMx *SMx, FILE *fpout)
{
  int         i, nt;
  const char  *ff;
  const int   *r, *t;


  r = SMx->s.R;
  t = SMx->s.T;

  for (i = 0; i < 3; i++)
  {
    fprintf(fpout, " %2d", *r++);
    fprintf(fpout, " %2d", *r++);
    fprintf(fpout, " %2d", *r++);

        nt = iModPositive(*t++, STBF);
    if (nt >  STBF / 2)
        nt -= STBF;

        ff = FormatFraction(nt, STBF, 0, NULL, 0);
    if (ff == NULL)
      return;

    fprintf(fpout, " %6s\n", ff);
  }

  putc('\n', fpout);
}


void ListSgInfo(const T_SgInfo *SgInfo, int F_XYZ, int F_Verbose, FILE *fpout)
{
  int           iList, i_si_v;
  char          buf[8];
  const char    *xyz;
  const T_RTMx  *lsmx;
  T_RotMxInfo   *rmxi, RotMxInfo;


  iList = PG_Index(SgInfo->PointGroup);

  fprintf(fpout, "Point Group  %s\n", PG_Names[iList]);
  fprintf(fpout, "Laue  Group  %s\n",
    PG_Names[PG_Index(LG_Code_of_PG_Index[iList])]);

  fprintf(fpout, "%s\n", XS_Name[SgInfo->XtalSystem]);

  if (SgInfo->UniqueRefAxis != 0 || SgInfo->UniqueDirCode != 0)
  {
    fprintf(fpout, "Unique Axis  ");
    if (SgInfo->UniqueRefAxis != 0 && SgInfo->UniqueRefAxis != 'o')
      fprintf(fpout, "%c", SgInfo->UniqueRefAxis);
    if (SgInfo->UniqueDirCode != 0 && SgInfo->UniqueDirCode != '=')
      fprintf(fpout, "%c", SgInfo->UniqueDirCode);
    fprintf(fpout, "\n");
  }

  if (SgInfo->ExtraInfo != EI_Unknown)
    fprintf(fpout, "%s\n", EI_Name[SgInfo->ExtraInfo]);

  if (SgInfo->InversionOffOrigin)
    fprintf(fpout, "Note: Inversion operation off origin\n");

  putc('\n', fpout);

  fprintf(fpout, "Order   %3d\n", SgInfo->OrderL);
  fprintf(fpout, "Order P %3d\n", SgInfo->OrderP);
  putc('\n', fpout);

  if (SgInfo->n_si_Vector >= 0)
  {
    fprintf(fpout, "s.i.Vector  Modulus\n");
    for (i_si_v = 0; i_si_v < SgInfo->n_si_Vector; i_si_v++)
      fprintf(fpout, " %2d %2d %2d   %d\n",
        SgInfo->si_Vector[i_si_v * 3 + 0],
        SgInfo->si_Vector[i_si_v * 3 + 1],
        SgInfo->si_Vector[i_si_v * 3 + 2],
        SgInfo->si_Modulus[i_si_v]);
    putc('\n', fpout);
  }

  if (F_XYZ || F_Verbose)
  {
    fprintf(fpout, "#List   %3d\n", SgInfo->nList);
    putc('\n', fpout);

    lsmx = SgInfo->ListSeitzMx;
    rmxi = SgInfo->ListRotMxInfo;

    if (rmxi == NULL) rmxi = &RotMxInfo;

    for (iList = 0; iList < SgInfo->nList; iList++, lsmx++)
    {
      if (rmxi == &RotMxInfo)
      {
        if (GetRotMxInfo(lsmx->s.R, &RotMxInfo) == 0) {
          SetSgError("Error: Illegal SeitzMx in list");
          return;
        }
      }

      if (F_Verbose)
      {
        sprintf(buf, "(%d)", iList + 1);
        fprintf(fpout, "%-4s", buf);

        fprintf(fpout, "  %2d", rmxi->Order);
        if (rmxi->Inverse) fprintf(fpout, "^-1");
        else               fprintf(fpout, "   ");

        fprintf(fpout, " [%2d %2d %2d]",
                        rmxi->EigenVector[0],
                        rmxi->EigenVector[1],
                        rmxi->EigenVector[2]);

        if (rmxi->RefAxis) fprintf(fpout, " '%c'", rmxi->RefAxis);
        else               fprintf(fpout, "    ");
        if (rmxi->DirCode) fprintf(fpout, " '%c'", rmxi->DirCode);
        else               fprintf(fpout, "    ");

        fprintf(fpout, "    ");
      }

          xyz = RTMx2XYZ(lsmx, 1, STBF, 0, 0, 1, ", ", NULL, 0);
      if (xyz)
        fprintf(fpout, "%s", xyz);

      putc('\n', fpout);

      if (xyz == NULL)
        return;

      if (F_Verbose)
        PrintSeitzMx(lsmx, fpout);

      if (rmxi != &RotMxInfo) rmxi++;
    }

    if (iList && F_Verbose == 0)
      putc('\n', fpout);
  }
}
