/****************************************************************************
**
*A  interactive_pga.c           ANUPQ source                   Eamonn O'Brien
**
*A  @(#)$Id$
**
*Y  Copyright 1995-2001,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
*Y  Copyright 1995-2001,  School of Mathematical Sciences, ANU,     Australia
**
*/

#include "pq_defs.h"
#include "pcp_vars.h"
#include "pga_vars.h"
#include "constants.h"
#include "menus.h"
#include "standard.h"
#include "pq_functions.h"
#include "global.h"

#define MAX_INTERACTIVE_OPTION 18  /* maximum number of menu options */

#define COMBINATION 100

/* interactive menu for p-group generation */

void interactive_pga (Logical group_present, FILE *StartFile, int group_nmr, int ***auts, struct pga_vars *pga, struct pcp_vars *pcp)
{
   struct pga_vars flag;
   int option;
   Logical soluble_group = TRUE;

   FILE * OutputFile;
   FILE *LINK_input;

   char *StartName; 
   int t;

   int **perms;
   int index;
   int **S;
   int k;
   int K;
   int label;
   int *a, *b;
   char *c;
   int *orbit_length;
   int nmr_of_exponents;
   int *subset;
   int alpha;
   int upper_step;
   int rep;
   int i;

   list_interactive_pga_menu ();

   do {
      option = read_option (MAX_INTERACTIVE_OPTION);      
      switch (option) {

      case -1:
	 list_interactive_pga_menu ();
	 break;

      case SUPPLY_AUTS:
	 auts = read_auts (PGA, &pga->m, &nmr_of_exponents, pcp);
#ifdef HAVE_GMP
	 autgp_order (pga, pcp);
#endif 
	 pga->soluble = TRUE; 
	 start_group (&StartFile, auts, pga, pcp);
	 break;

      case EXTEND_AUTS:
	 extend_automorphisms (auts, pga->m, pcp);
	 print_auts (pga->m, pcp->lastg, auts, pcp);
	 break;

      case RESTORE_GP:
	 StartName = GetString ("Enter input file name: ");
	 StartFile = OpenFileInput (StartName);
	 if (StartFile != NULL) {
	    read_value (TRUE, "Which group? ", &group_nmr, 0);
	    auts = restore_group (TRUE, StartFile, group_nmr, pga, pcp);
	    RESET(StartFile);
	 }
	 break;

      case DISPLAY_GP:
	 print_presentation (FALSE, pcp);
	 print_structure (1, pcp->lastg, pcp);
	 print_pcp_relations (pcp);
	 break;

      case SINGLE_STAGE:
	 t = runTime ();
	 if (group_present && pga->m == 0)
	    start_group (&StartFile, auts, pga, pcp);
	 construct (1, &flag, SINGLE_STAGE, OutputFile, StartFile, 
		    0, ALL, group_nmr, pga, pcp);
	 t = runTime () - t;
	 printf ("Time for intermediate stage is %.2f seconds\n", t * CLK_SCALE);
	 break;

      case DEGREE:
	 read_step_size (pga, pcp);
	 read_subgroup_rank (&k);
	 query_exponent_law (pga);
	 enforce_laws (pga, pga, pcp);
	 extend_automorphisms (auts, pga->m, pcp);
	 step_range (k, &pga->s, &upper_step, auts, pga, pcp);

	 if (pga->s > upper_step)  
	    printf ("Desired step size is invalid for current group\n");
	 else {
	    if (pga->s < upper_step) {
	       printf ("The permitted relative step sizes range from %d to %d\n", 
		       pga->s, upper_step);
	       read_value (TRUE, "Input the chosen relative step size: ", 
			   &pga->s, 0);
	    }
         

	    store_definition_sets (pga->r, pga->s, pga->s, pga);
	    get_definition_sets (pga);
	    pga->print_degree = TRUE;
	    compute_degree (pga);
	    pga->print_degree = FALSE;
	 }
	 break;

      case PERMUTATIONS:
	 if (pga->Degree != 0) {
	    t = runTime ();

	    query_solubility (pga);
	    pga->trace = FALSE;
	    if (pga->soluble) 
	       query_space_efficiency (pga);
	    else 
	       pga->space_efficient = FALSE;
	    query_perm_information (pga);

	    strip_identities (auts, pga, pcp);
	    soluble_group = (pga->soluble || pga->Degree == 1 || 
			     pga->nmr_of_perms == 0);
	    if (!soluble_group) {
#if defined (GAP_LINK)
	       StartGapFile (pga);
#else
#if defined (GAP_LINK_VIA_FILE)
	       start_GAP_file (&LINK_input, auts, pga, pcp);
#endif
#endif
	    }
	    perms = permute_subgroups (LINK_input, &a, &b, &c, 
				       auts, pga, pcp); 

#if defined (GAP_LINK_VIA_FILE)
	    if (!soluble_group)
	       CloseFile (LINK_input);
#endif 
	    t = runTime () - t;
	    printf ("Time to compute permutations is %.2f seconds\n", t * CLK_SCALE);
	 }
	 else
	    printf ("You must first select option %d\n", DEGREE);

	 break;
         
      case ORBITS: 
	 orbit_option (option, perms, &a, &b, &c, &orbit_length, pga);
	 break;

      case STABILISERS: case STABILISER: 
	 stabiliser_option (option, auts, perms, a, b, c, orbit_length, 
			    pga, pcp);
	 /*
	   free_space (pga->soluble, perms, orbit_length,
	   a, b, c, pga);
	   */
	 break;

      case MATRIX_TO_LABEL:
	 S = allocate_matrix (pga->s, pga->q, 0, FALSE);
	 subset = allocate_vector (pga->s, 0, FALSE);
	 printf ("Input the %d x %d subgroup matrix:\n", pga->s, pga->q);
	 read_matrix (S, pga->s, pga->q);
	 K = echelonise_matrix (S, pga->s, pga->q, pga->p, subset, pga);
	 printf ("The standard matrix is:\n");
	 print_matrix (S, pga->s, pga->q);
	 printf ("The label is %d\n", subgroup_to_label (S, K, subset, pga));
	 free_vector (subset, 0);
	 break;

      case LABEL_TO_MATRIX:
	 read_value (TRUE, "Input allowable subgroup label: ", &label, 1);
	 S = label_to_subgroup (&index, &subset, label, pga);
	 printf ("The corresponding standard matrix is\n");
	 print_matrix (S, pga->s, pga->q);
	 break;

      case IMAGE:
	 t = runTime ();
	 /*
	   invert_automorphisms (auts, pga, pcp);
	   print_auts (pga->m, pcp->lastg, auts, pcp);
	   */
	 printf ("Input the subgroup label and automorphism number: ");
	 read_value (TRUE, "", &label, 1);
	 read_value (FALSE, "", &alpha, 1);
	 printf ("Image is %d\n", find_image (label, auts[alpha], pga, pcp));
	 t = runTime () - t;
	 printf ("Computation time in seconds is %.2f\n", t * CLK_SCALE );
	 break;

      case SUBGROUP_RANK:
	 read_subgroup_rank (&k);
	 printf ("Closure of initial segment subgroup has rank %d\n", 
		 close_subgroup (k, auts, pga, pcp));
	 break;

      case ORBIT_REP:
	 printf ("Input label for subgroup: ");
	 read_value (TRUE, "", &label, 1);
	 rep = abs (a[label]);
	 for (i = 1; i <= pga->nmr_orbits && pga->rep[i] != rep; ++i)
	    ;
	 printf ("Subgroup with label %d has representative %d and is in orbit %d\n", 
		 label, rep, i);
	 break;
         
        
      case COMPACT_DESCRIPTION:
	 Compact_Description = TRUE;
	 read_value (TRUE, "Lower bound for order (0 for all groups generated)? ",
		     &Compact_Order, 0);
	 break;

      case AUT_CLASSES:
	 t = runTime ();
	 permute_elements ();
	 t = runTime () - t;
	 printf ("Time to compute orbits is %.2f seconds\n", t * CLK_SCALE );
	 break;

	 /*
	   printf ("Input label: ");
	   scanf ("%d", &l);
	   process_complete_orbit (a, l, pga, pcp);
	   break;
           
	   case TEMP:
	   printf ("Input label: ");
	   scanf ("%d", &l);
	   printf ("Input label: ");
	   scanf ("%d", &u);
	   for (i = l; i <= u; ++i) {
	   x = IsValidAllowableSubgroup (i, pga);
	   printf ("%d is %d\n", i, x);
	   }
	   StartName = GetString ("Enter output file name: ");
	   OutputFile = OpenFileOutput (StartName);
	   part_setup_reps (pga->rep, pga->nmr_orbits, orbit_length, perms, a, b, c, 
	   auts, OutputFile, OutputFile, pga, pcp); 
           
	   list_word (pga, pcp);
           
	   read_value (TRUE, "Input the rank of the subgroup: ", &pga->q, 1);
	   strip_identities (auts, pga, pcp);
	   break;
	   */

      case EXIT: case MAX_INTERACTIVE_OPTION:
	 printf ("Exiting from interactive p-group generation menu\n");
	 break;

      }                         /* switch */

   } while (option != 0 && option != MAX_INTERACTIVE_OPTION);      

#if defined (GAP_LINK)
   if (!soluble_group)
      QuitGap ();
#endif

}

/* list available menu options */

void list_interactive_pga_menu (void)
{
   printf ("\nAdvanced Menu for p-Group Generation\n");
   printf ("-------------------------------------\n");
   printf ("%d. Read automorphism information for starting group\n",
	   SUPPLY_AUTS);
   printf ("%d. Extend and display automorphisms\n", EXTEND_AUTS);
   printf ("%d. Specify input file and group number\n", RESTORE_GP);
   printf ("%d. List group presentation\n", DISPLAY_GP);
   printf ("%d. Carry out intermediate stage calculation\n", SINGLE_STAGE);
   printf ("%d. Compute definition sets & find degree\n", DEGREE);
   printf ("%d. Construct permutations of subgroups under automorphisms\n",
	   PERMUTATIONS);
   printf ("%d. Compute and list orbit information\n", ORBITS);
   printf ("%d. Process all orbit representatives\n", STABILISERS);
   printf ("%d. Process individual orbit representative\n", STABILISER);
   printf ("%d. Compute label for standard matrix of subgroup\n",
	   MATRIX_TO_LABEL);
   printf ("%d. Compute standard matrix for subgroup from label\n",
	   LABEL_TO_MATRIX);
   printf ("%d. Find image of allowable subgroup under automorphism\n",
	   IMAGE);
   printf ("%d. Find rank of closure of initial segment subgroup\n",
	   SUBGROUP_RANK);
   printf ("%d. List representative and orbit for supplied label\n", ORBIT_REP);
   printf ("%d. Write compact descriptions of generated groups to file\n", 
	   COMPACT_DESCRIPTION);
   printf ("%d. Find automorphism classes of elements of vector space\n",
	   AUT_CLASSES);
   printf ("%d. Exit to main p-group generation menu\n", MAX_INTERACTIVE_OPTION);
}

void orbit_option (int option, int **perms, int **a, int **b, char **c, int **orbit_length, struct pga_vars *pga)
{
   int t; 
   Logical soluble_group;
/*    FILE * file; */


   if (option != COMBINATION && option != STANDARDISE) {
      query_solubility (pga);
      if (pga->soluble) 
	 query_space_efficiency (pga);
      else
	 pga->space_efficient = FALSE;
      query_orbit_information (pga);
   }
   else if (option == COMBINATION) {
      pga->print_orbit_summary = FALSE;
      pga->print_orbits = FALSE;
   }
   else if (option == STANDARDISE) {
      pga->print_orbit_summary = FALSE;
      pga->print_orbits = FALSE;
   }
            
   soluble_group = (pga->soluble || pga->Degree == 1 || pga->nmr_of_perms == 0);

   if (!pga->space_efficient) {
      t = runTime ();
      if (soluble_group)  
	 compute_orbits (a, b, c, perms, pga);
      else   
	 insoluble_compute_orbits (a, b, c, perms, pga);
      if (option != COMBINATION && option != STANDARDISE) {
	 t = runTime () - t;
	 printf ("Time to compute orbits is %.2f seconds\n", t * CLK_SCALE );
      }
   }

   /* if in soluble portion of combination, we do not need to 
      set up representives */
   if (option == COMBINATION && pga->soluble) return;

   *orbit_length = find_orbit_reps (*a, *b, pga);

   if (pga->print_orbit_summary)   
      orbit_summary (*orbit_length, pga);
   /*   file = OpenFile ("COUNT", "a+");
        fprintf (file, "%d,\n", pga->nmr_orbits);
   */

}

void stabiliser_option (int option, int ***auts, int **perms, int *a, int *b, char *c, int *orbit_length, struct pga_vars *pga, struct pcp_vars *pcp)
{
   int t;
   int i;
   Logical soluble_group;
   FILE * OutputFile;
   char *StartName; 
   int *rep;
   int *length;
   rep = allocate_vector (1, 1, 0); 
   length = allocate_vector (1, 1, 0); 

   t = runTime ();

   query_solubility (pga);
   if (pga->soluble)
      query_space_efficiency (pga);
   else
      pga->space_efficient = FALSE;
   soluble_group = (pga->soluble || pga->Degree == 1 || pga->nmr_of_perms == 0);

   query_terminal (pga);
   query_exponent_law (pga);
   query_metabelian_law (pga);
   query_group_information (pga->p, pga);
   query_aut_group_information (pga);
   StartName = GetString ("Enter output file name: ");
   OutputFile = OpenFileOutput (StartName);

   pga->final_stage = (pga->q == pga->multiplicator_rank);
   pga->nmr_of_descendants = 0;
   pga->nmr_of_capables = 0;

   if (option == STABILISER) {
      read_value (TRUE, "Input the orbit representative: ", &rep[1], 1);
      /* find the length of the orbit having this representative */
      for (i = 1; i <= pga->nmr_orbits && pga->rep[i] != rep[1]; ++i)
	 ;
      if (pga->rep[i] == rep[1])
	 length[1] = orbit_length[i];
      else {
	 printf ("%d is not an orbit representative\n", rep[1]);
	 return;
      }
   }

   if (option == STABILISER)
      setup_reps (rep, 1, length, perms, a, b, c, auts, 
		  OutputFile, OutputFile, pga, pcp); 
   else 
      setup_reps (pga->rep, pga->nmr_orbits, orbit_length, perms, a, b, c, 
		  auts, OutputFile, OutputFile, pga, pcp); 

   /*
     #if defined (GAP_LINK)
     if (!soluble_group)
     QuitGap ();
     #endif
     */

   RESET (OutputFile);

   printf ("Time to process representative is %.2f seconds\n", 
	   (runTime () - t) * CLK_SCALE);
}


/* list orbit representatives as words subject to the supplied map */ 

int list_word (struct pga_vars *pga, struct pcp_vars *pcp)
{
   register int i, j;
   int start_length;

   int start[100];
   int word[100];

   int **S;
   int index;
   int *subset;
   int length = 0;
   register int k, r;
   register int lastg = pcp->lastg;

   start_length = 0;
   /*
     read_value (TRUE, "Input length of initial segment: ", &start_length, 0);
     for (i = 1; i <= start_length; ++i)
     scanf ("%d", &start[i]);
     */

   for (r = 1; r <= pga->nmr_orbits; ++r) {

      S = label_to_subgroup (&index, &subset, pga->rep[r], pga);

      print_matrix (S, pga->s, pga->q);

      for (i = 0; i < pga->q; ++i) {

	 if  (1 << i & pga->list[index]) continue;

	 for (j = 1; j <= lastg; ++j)
	    word[j] = 0;

	 for (j = 0; j < pga->s; ++j)
	    if (S[j][i] != 0)
	       word[pcp->ccbeg + subset[j]] = pga->p - S[j][i];

	 word[pcp->ccbeg + i] = 1;

	 print_array (word, pcp->ccbeg, lastg + 1);
     
	 length = 0;
	 for (k = pcp->ccbeg; k <= lastg; ++k)
	    if (word[k] != 0)
	       ++length;

	 printf ("%d\n", length + start_length);
	 for (k = 1; k <= start_length; ++k)
	    printf ("%d 1 ", start[k]);
   
	 for (k = pcp->ccbeg; k <= lastg; ++k)
	    if (word[k] != 0)
	       printf ("%d %d ", k, word[k]);
	 printf ("\n");
      }
   }

   return 0;
}
