/****************************************************************************
**
*A  GAP.c                      ANUPQ source                    Eamonn O'Brien
*A                                                             & Frank Celler
**
*Y  Copyright 1995-1997,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
*Y  Copyright 1997-1997,  School of Mathematical Sciences, ANU,     Australia
**
*/
#include "pq_defs.h"
#include "pga_vars.h"
#include "pcp_vars.h"
#include "pq_functions.h"
#include "constants.h"

#if defined(GAP_LINK)


/****************************************************************************
**
*V  p1, p2
*/
int p1[2], p2[2];


/****************************************************************************
**
*F  WriteGap( <str> )
**                                          write <str> to gap via input pipe
*/
void WriteGap(char *str)
{
   write(p2[1], str, strlen(str));
}


/****************************************************************************
**
*F  ReadGap( <str> )
**                     read string from gap via output pipe into buffer <str>
*/
void ReadGap(char *str)
{
   char *ptr;

   /* read next line                                                      */
   ptr = str;
   do
      while (read(p1[0], ptr, 1) != 1)
         ;
   while (*ptr++ != '\n');

   /* append a trailing '\0'                                              */
   *(--ptr) = 0;

   /* if line begins with a '#' ignore this line and start again          */
   if (*str == '#') {
      fprintf(stderr, "%s\n", str);
      ReadGap(str);
   }
}


/****************************************************************************
**
*F  WriteGapInfo( <auts>, <pga> )
**                                write initial information to GAP input pipe
*/
void WriteGapInfo(int ***auts, struct pga_vars *pga)
{
   int i;
   char str[MAXWORD];

   /* we need the GAP library files defined in the package "anupq"        */
   WriteGap("LoadPackage( \"anupq\" );\n");

   /* give debug information if 'ANUPQlog' is set to 'LogTo'              */
   WriteGap("ANUPQlog( \"debug.out\" );\n");

   /* use a record 'ANUPQglb' to hold all global variables                */
   WriteGap("ANUPQglb := rec();;\n");
   sprintf(str, "ANUPQglb.d := %d;\n", pga->ndgen);
   WriteGap(str);
   ReadGap(str);
   sprintf(str, "ANUPQglb.F := GF(%d);\n", pga->p);
   WriteGap(str);
   ReadGap(str);
   sprintf(str, "ANUPQglb.q := %d;\n", pga->q);
   WriteGap(str);
   ReadGap(str);
   sprintf(str, "ANUPQglb.genD := [];\n");
   WriteGap(str);
   ReadGap(str);
   sprintf(str, "ANUPQglb.genQ := [];\n");
   WriteGap(str);
   ReadGap(str);
   for (i = 1; i <= pga->m; ++i)
      write_GAP_matrix(0, "ANUPQglb.genD", auts[i], pga->ndgen, 1, i);
}


/****************************************************************************
**
*F  start_GAP_file( <auts>, <pga> )
**       start GAP process or write necessary information to existing process
*/
void start_GAP_file(int ***auts, struct pga_vars *pga)
{
   int pid;
   char *path;
   int i;
   char tmp[200];

   pipe(p1);
   pipe(p2);
#ifdef HAVE_WORKING_VFORK
   if ((pid = vfork()) == 0)
#else
   if ((pid = fork()) == 0)
#endif
   {
      dup2(p1[1], 1);
      dup2(p2[0], 0);
      if ((path = (char *)getenv("ANUPQ_GAP_EXEC")) == NULL)
#if defined(ANUPQ_GAP_EXEC)
         path = ANUPQ_GAP_EXEC;
#else
         path = "gap";
#endif
      if (execlp(path, path, "-r -q", 0) == -1) {
         perror("Error in system call to GAP");
         exit(FAILURE);
      }
   } else if (pid == -1) {
      perror("cannot fork");
      exit(FAILURE);
   } else {

      /* write GAP information to file                                   */
      WriteGapInfo(auts, pga);

      /* try to syncronise with gap process                              */
      WriteGap("165287638495312637;\n");
      for (i = 0; i < 100; i++) {
         ReadGap(tmp);
         if (!strcmp(tmp, "165287638495312637"))
            break;
      }
      if (i == 100)
         fprintf(stderr, "WARNING: did not got magic number, got '%s'\n", tmp);
   }
}


/****************************************************************************
**
*F  QuitGap()
**                                                     close the pipes to GAP
*/
void QuitGap(void)
{
   WriteGap("quit;\n");
   close(p1[1]);
   close(p2[1]);
   close(p1[0]);
   close(p2[0]);
   wait((int *)0);
}


/****************************************************************************
**
*F  ReadFromGapPipe( <value> )
**  read a string from the GAP output pipe and check whether it is an integer
*/
void ReadFromGapPipe(int *value)
{
   Logical error;
   char str[MAXWORD];

   ReadGap(str);
   *value = string_to_int(str, &error);
   if (error) {
      fprintf(stderr, "Error in reading stabiliser data from GAP pipe\n");
      fprintf(stderr, "got line: '%s'\n", str);
      exit(FAILURE);
   }
}


/****************************************************************************
**
*F  read_stabiliser_gens( <nr>, <sol>, <pga> )
**                    read the insoluble stabiliser generators from the pipe;
**                    each list of stabilisers is preceded by two integers --
**                     the first indicates whether the stabiliser is soluble;
**                  the second is the number of generators for the stabiliser
*/
int ***read_stabiliser_gens(int nr_gens, int ***sol_gens, struct pga_vars *pga)
{
   int ndgen = pga->ndgen;
   int gamma, i, j;
   int ***stabiliser;
   int current;

   /* check if any gens for the stabiliser have already been computed     */
   current = pga->nmr_stabilisers;

   /* read the first two integers (is soluble and number of generators)   */
   ReadFromGapPipe(&pga->soluble);
   ReadFromGapPipe(&pga->nmr_stabilisers);

   /* get enough room for the generators                                  */
   pga->nmr_stabilisers += current;

   /* memory leakage September 1996 */
   stabiliser = reallocate_array(sol_gens,
                                 current,
                                 ndgen,
                                 nr_gens,
                                 pga->nmr_stabilisers,
                                 ndgen,
                                 nr_gens,
                                 TRUE);
   /*
      if (current != 0) {
      stabiliser = reallocate_array( sol_gens, current, ndgen,
      nr_gens, pga->nmr_stabilisers,
      ndgen, nr_gens, TRUE );
      }
      else {
      stabiliser = allocate_array( pga->nmr_stabilisers, ndgen,
      nr_gens, TRUE );
      }
      */

   /* now read in the insoluble generators                                */
   for (gamma = current + 1; gamma <= pga->nmr_stabilisers; ++gamma)
      for (i = 1; i <= ndgen; ++i)
         for (j = 1; j <= ndgen; ++j)
            ReadFromGapPipe(&stabiliser[gamma][i][j]);

   /* return the result                                                   */
   return stabiliser;
}


/****************************************************************************
**
*F  insoluble_stab_gens( <rep>, <orbit_length> )
**          calculate the stabiliser of the supplied representative using GAP
*/
void insoluble_stab_gens(int rep, int orbit_length)
{
   char str[MAXWORD];

   sprintf(str,
           "stab := ANUPQstabilizer( %d, %d, ANUPQglb );;1;\n",
           rep,
           orbit_length);
   WriteGap(str);
   ReadGap(str);
   sprintf(str, "ANUPQoutputResult( stab, \"*stdout*\" );;\n");
   WriteGap(str);
}


/****************************************************************************
**
*F  write_GAP_matrix
**                                     write out a matrix in a GAP input form
**
*/
void write_GAP_matrix(
    FILE *GAP_input, char *gen, int **A, int size, int start, int nr)
{
   int i, j;
   char str[MAXWORD];

   sprintf(str, "%s[%d] := [\n", gen, nr);
   WriteGap(str);
   for (i = start; i < start + size; ++i) {
      WriteGap("[");
      for (j = start; j < start + size - 1; ++j) {
         sprintf(str, "%d, ", A[i][j]);
         WriteGap(str);
      }
      if (i != start + size - 1)
         sprintf(str, "%d],\n", A[i][j]);
      else
         sprintf(str, "%d]] * One( ANUPQglb.F );;\n", A[i][j]);
      WriteGap(str);
   }
}


/****************************************************************************
**
*F  StartGapFile( <pga> )
**                            write basic input information to GAP input pipe
*/
void StartGapFile(struct pga_vars *pga)
{
   char str[MAXWORD];

   sprintf(str, "ANUPQglb.s := %d;\n", pga->s);
   WriteGap(str);
   ReadGap(str);
   sprintf(str, "ANUPQglb.r := %d;\n", pga->r);
   WriteGap(str);
   ReadGap(str);
}


#endif
