/***********************************************************************
*                                                                      *
* This file is part of CARAT.                                          *
* Copyright (C) 2015  Tilman Schulz                                    *
*                                                                      *
* CARAT is free software: you can redistribute it and/or modify        *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or    *
* (at your option) any later version.                                  *
*                                                                      *
* This program is distributed in the hope that it will be useful,      *
* but WITHOUT ANY WARRANTY; without even the implied warranty of       *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
* GNU General Public License for more details.                         *
*                                                                      *
* You should have received a copy of the GNU General Public License    *
* along with this program.  If not, see <http://www.gnu.org/licenses/> *
*                                                                      *
***********************************************************************/
#include "typedef.h"
#include "voronoi.h"
#include "longtools.h"
#include "symm.h"
#include "bravais.h"
#include "autgrp.h"
#include "polyeder.h"
#include "matrix.h"
#include "reduction.h"
#include "orbit.h"
#include "tools.h"
/**************************************************************************\
@---------------------------------------------------------------------------
@---------------------------------------------------------------------------
@ FILE: normalizer.c
@---------------------------------------------------------------------------
@---------------------------------------------------------------------------
@
\**************************************************************************/

static matrix_TYP *search_normal_isometry(Vneu, V, Vanz, G, Gtr, bifo, no)
voronoi_TYP *Vneu, **V;
bravais_TYP *G, *Gtr;
matrix_TYP *bifo;
int Vanz, *no;
{
   int i;
   int found;
   matrix_TYP *X;
 
   found = FALSE;
   *no = -1;
   for(i=0;i<Vanz && found == FALSE;i++)
   {
      X = calc_voronoi_isometry(V[i], Vneu, G, Gtr, bifo);
      if(X != NULL)
      {
       found = TRUE;
       *no = i;
      }
   }
   if(found == TRUE)
   {
     i = *no;
   }
   return(X);
}


/**************************************************************************\
@---------------------------------------------------------------------------
@ voronoi_TYP **normalizer(P, G, Gtr, prime, V_no)
@ matrix_TYP *P;
@ bravais_TYP *G, *Gtr;
@ int prime, *V_no;
@
@  The arguments of normalizer are
@  P:     A G-perfect form (can be calculated with 'first_perfect')
@  G:     A group given as 'bravais_TYP*' where 'G->form' must contain a
@         Z-Basis of the integral G-invariant matrices.
@  Gtr:   The group G^{tr} given as 'bravais_TYP', also
@         with a Z-basis in 'G->form'
@  prime: a prime number (used to calculate determinates modulo p
@         for a simple criterion of two symmetric matrices been
@         not isometric.
@  V_no:  via this pointer the number of returned 'voronoi_TYP'
@         is returned.
@
@ 'normalizer' calculates representatives of the G-perfect forms.
@ The are returned in a list 'voronoi_TYP**'
@ Furthermore generators of the integral normalizer of G are calculated
@ and written to G->normal. Their number is G->normal_no.
@  
@---------------------------------------------------------------------------
@
\**************************************************************************/
voronoi_TYP **normalizer(P, G, Gtr, prime, V_no)
matrix_TYP *P;
bravais_TYP *G, *Gtr;
int prime, *V_no;
{
   int i,j,k,l;
   int dim, fdim;
   voronoi_TYP **V;
   voronoi_TYP *Vneu;
   matrix_TYP *Dir, **N, *X, *bifo;
   int Vanz, Nanz, diranz, no;
   int lc, rc;

   dim = G->dim;
   fdim = G->form_no;
   if( (V = (voronoi_TYP **)malloc(1 *sizeof(voronoi_TYP *))) == NULL)
   {
     printf("malloc of 'V' in 'normalizer' failed\n");
     exit(2);
   }
   V[0] = init_voronoi();
   V[0]->gram = copy_mat(P);
   Check_mat(V[0]->gram);
   bifo = trace_bifo(G->form, Gtr->form, fdim);
   calc_voronoi_complete(V[0], G, Gtr, bifo, prime);
   Nanz = V[0]->stab->gen_no;
   if( (N = (matrix_TYP **)malloc(Nanz *sizeof(matrix_TYP *))) == NULL)
   {
     printf("malloc of 'N' in 'normalizer' failed\n");
     exit(2);
   }
   for(i=0;i<Nanz;i++)
     N[i] = copy_mat(V[0]->stab->gen[i]);
   Vanz = 1;
   Vneu = init_voronoi();
   for(i=0;i<Vanz;i++)
   {
      diranz = V[i]->dir_reps->cols;
      real_mat(V[i]->dir_reps, 3, diranz);

      /* geaendert testweise am 06.11.96 tilman */
      for(j=0;j<diranz;j++)
      {
         /***************************************************************\
         | Calculate 'Vneu', the neighbour of V[i] in direction j
         \***************************************************************/
         Dir = vec_to_form(V[i]->pol->vert[V[i]->dir_reps->array.SZ[0][j]]->v,
                           G->form, fdim);
         Vneu->gram = voronoi_neighbour(V[i]->gram, Dir, V[i]->min, &lc, &rc);
         free_mat(Dir);

         /* changed: tilman 07/11/96 */
         /* voronoi_neighbour returns NULL sometimes, and this causes
            chrashes in calc_voronoi_basics */
         if (Vneu->gram != NULL){
            divide_by_gcd(Vneu->gram);
            calc_voronoi_basics(Vneu, G, Gtr, prime);
            /***************************************************************\
            | Check if 'Vneu' is a new typ of perfect form or not
            | if 'Vneu' is new, 'no = -1' and 'X = NULL'
            | if not, 'X' is an isometry in the integral normalizer of 'G'
            |          and 'Vneu' is isometric to V[no].
            \***************************************************************/
            X = search_normal_isometry(Vneu, V, Vanz, G, Gtr, bifo, &no);
            if(no == -1)
            {
              V[i]->dir_reps->array.SZ[2][j] = Vanz;
              calc_voronoi_complete(Vneu, G, Gtr, bifo, prime);

              l = Vneu->stab->gen_no;
              Nanz += l;
              if((N =(matrix_TYP **)realloc(N,Nanz *sizeof(matrix_TYP *)))
                    == NULL)
              {
                printf("realloc of 'N' in 'normalizer' failed\n");
                exit(2);
              }
              for(k=0;k<l; k++)
                N[Nanz-l+k] = copy_mat(Vneu->stab->gen[k]);

              Vanz++;
              if((V=(voronoi_TYP **)realloc(V,Vanz *sizeof(voronoi_TYP *)))
                       == NULL)
              {
                printf("realloc of 'V' in 'normalizer' failed\n");
                exit(2);
              }
              V[Vanz-1] = Vneu;
              Vneu = init_voronoi();
            }
            else
            {
               V[i]->dir_reps->array.SZ[2][j] = no;
               Nanz ++;
               if((N=(matrix_TYP **)realloc(N,Nanz *sizeof(matrix_TYP *)))
                      == NULL)
               {
                 printf("realloc of 'N' in 'normalizer' failed\n");
                 exit(2);
               }
               N[Nanz-1] =  X;
               clear_voronoi(Vneu);
            }
         }
      }
   }
   *V_no = Vanz;
   if(G->normal_no != 0)
   {
     for(i=0;i<G->normal_no;i++)
       free_mat(G->normal[i]);
     free(G->normal);
   }
   G->normal = N;
   G->normal_no = Nanz;
   free_mat(bifo);
   if (Vneu != NULL){
      clear_voronoi(Vneu);
      free(Vneu);
      Vneu = NULL;
   }

   /* inserted tilman 20.06.97 to reduce the number of generators for
   the normalizer */
   red_normal(G);

   return(V);
}
