/****************************************************************************
**
*A  interactive_pq.c            ANUPQ source                   Eamonn O'Brien
**
*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 "exp_vars.h"
#include "constants.h"
#include "menus.h"
#include "pq_functions.h"
#include "pretty_filterfns.h"
#include "word_types.h"
#include "global.h"

#define MAXOPTION 31 /* maximum number of menu options */
#define GAP_PRES_FORMAT 2

#define BOTH_TAILS 0
#define NEW_TAILS 1
#define COMPUTE_TAILS 2

#if defined(GROUP)

/* interactive menu for p-quotient calculation */

void interactive_pq(Logical group_present,
                    int format,
                    int output_level,
                    int **head,
                    int **list,
                    struct pcp_vars *pcp)
{
   register int *y = y_address;

   int option, t, class;
   register int cp;
   Logical print_flag;
   int type;
   int i;
   int factor, limit;
   int ***auts;
   char *s;
   char *name;
   FILE *FileName;

   int *queue, *long_queue;
   int start_length = 0;
   int prev_qlength = 0, current_qlength;
   int long_queue_length = 0, queue_length = 0;
   int consistency_type;
   int nmr_of_auts;
   int nmr_of_exponents;
   int tail_type;
   int start_gen, final_gen;

   Logical queue_setup = FALSE;   /* redundancy queue set up? */
   Logical echelon_ready = FALSE; /* ready to echelonise? */

   Logical output; /* temporarily store value of pcp->fullop */
   struct exp_vars exp_flag;

   Logical symbols_setup = FALSE;

   int file_format;

   if (isatty(0))
      list_interactive_pq_menu();

   if (format != BASIC && group_present == TRUE) {
      setup_symbols(pcp);
      symbols_setup = TRUE;
   }

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

      case -1:
         list_interactive_pq_menu();
         break;

      case COLLECT:
         t = runTime();
         if (format != BASIC && symbols_setup == FALSE) {
            setup_symbols(pcp);
            symbols_setup = TRUE;
         }
         type = WORD;
         if (!is_space_exhausted(3 * pcp->lastg + 2, pcp)) {
            cp = pcp->lused;
            setup_word_to_collect(stdin, format, type, cp, pcp);
            t = runTime() - t;
            printf("Collection took %.2f seconds\n", t * CLK_SCALE);
            echelon_ready = TRUE;
         }
         break;

      case SOLVE:
         t = runTime();
         if (format != BASIC && symbols_setup == FALSE) {
            setup_symbols(pcp);
            symbols_setup = TRUE;
         }
         setup_to_solve_equation(format, pcp);
         t = runTime() - t;
         printf("Solving the equation took %.2f seconds\n", t * CLK_SCALE);
         break;

      case COMMUTATOR:
         t = runTime();
         if (format != BASIC && symbols_setup == FALSE) {
            setup_symbols(pcp);
            symbols_setup = TRUE;
         }
         calculate_commutator(format, pcp);
         cp = pcp->lused;
         echelon_ready = TRUE;
         t = runTime() - t;
         printf("Commutator calculation took %.2f seconds\n", t * CLK_SCALE);
         break;

      case DISPLAY_PRESENTATION:
         print_flag = (output_level >= MAX_PRINT - 1) ? TRUE : FALSE;
         print_presentation(print_flag, pcp);
         break;

      case PRINT_LEVEL:
         print_level(&output_level, pcp);
         break;

      case SETUP:
         if (pcp->complete) {
            printf("Group is complete\n");
            break;
         }
         setup(pcp);
         pcp->update = FALSE;
         pcp->middle_of_tails = FALSE;
         printf("Setup performed for class %d\n", pcp->cc);
         break;

      case TAILS:
         t = runTime();
         if (pcp->complete) {
            printf("Group is complete\n");
            break;
         }
         pcp->middle_of_tails = FALSE;
         read_value(TRUE,
                    "Input class for tails computation (0 for all): ",
                    &class,
                    -9999);
	 /* by negative class we mean that we want to extend the lower central
	    series up to -class, by adding p-powers. Variable i is hijacked. */
	 if (class < 0) { i = class; class = 0; } else { i = 1; }

         tail_info(&tail_type);
         if (class == 0 || (class > 1 && class <= pcp->cc)) {
            if (class > 0) {
               tails(tail_type, class, pcp->cc, i, pcp);
               if (class != 2)
                  pcp->middle_of_tails = TRUE;
            } else {
	      /* variable end_weight is never used. We hijack it to pass the
		 l.c.s. desired depth. */
               for (class = pcp->cc; class > 1; --class)
                  tails(tail_type, class, pcp->cc, i, pcp);
            }
            if (pcp->overflow && !isatty(0))
               exit(FAILURE);
            t = runTime() - t;
            printf("Tails computation took %.2f seconds \n", t * CLK_SCALE);
         } else
            printf("Class %d is invalid for tails calculations\n", class);
         break;

      case CONSISTENCY:
         t = runTime();
         if (pcp->complete) {
            printf("Group is complete\n");
            break;
         }
         read_value(TRUE,
                    "Input class for consistency check (0 for all): ",
                    &class,
                    0);
         consistency_info(&consistency_type);
         if (class == 0 || (class > 2 && class <= pcp->cc)) {
            if (pcp->m != 0) {
               queue_setup = TRUE;
               start_length = queue_length;
               queue_space(
                   &queue, &long_queue, &current_qlength, &prev_qlength, pcp);
            }

            if (class > 0)
               consistency(consistency_type, queue, &queue_length, class, pcp);
            else
               for (class = pcp->cc; class > 2; --class)
                  consistency(
                      consistency_type, queue, &queue_length, class, pcp);
            if (pcp->overflow && !isatty(0))
               exit(FAILURE);

            if (pcp->m != 0) {
               s = (queue_length - start_length == 1) ? "y" : "ies";
               printf("Consistency checks gave %d redundanc%s\n",
                      queue_length - start_length,
                      s);
            }
            if (pcp->complete && output_level <= 1)
               text(5, pcp->cc, pcp->p, pcp->lastg, 0);
            t = runTime() - t;
            printf("Consistency checks took %.2f seconds\n", t * CLK_SCALE);
         } else
            printf("Class %d is invalid for consistency checks\n", class);
         break;

      case RELATIONS:
         t = runTime();
         if (pcp->complete) {
            printf("Group is complete\n");
            break;
         }

         /* if no tails have been added, do not perform update */
         if (y[pcp->clend + pcp->cc - 1] < pcp->lastg) {
            if (!pcp->complete && pcp->cc > 1 && !pcp->middle_of_tails &&
                !pcp->update) {
               update_generators(pcp);
               pcp->update = TRUE;
            }
            if (!pcp->complete)
               collect_relations(pcp);
         }

         if (pcp->complete && output_level <= 1)
            text(5, pcp->cc, pcp->p, pcp->lastg, 0);

         t = runTime() - t;
         printf("Collection of relations took %.2f seconds\n", t * CLK_SCALE);
         break;

      case EXTRA_RELATIONS:
         t = runTime();
         if (pcp->complete) {
            printf("Group is complete\n");
            break;
         }
         if (pcp->extra_relations == 0) {
            read_value(TRUE,
                       "Input exponent law (0 if none): ",
                       &pcp->extra_relations,
                       0);
         }
         read_value(TRUE,
                    "Input start weight for exponent checking: ",
                    &pcp->start_wt,
                    1);
         read_value(TRUE,
                    "Input end weight for exponent checking: ",
                    &pcp->end_wt,
                    pcp->start_wt);
         exponent_info(&exp_flag, pcp);
         if (pcp->m != 0) {
            queue_setup = TRUE;
            start_length = queue_length;
            queue_space(
                &queue, &long_queue, &current_qlength, &prev_qlength, pcp);
            exp_flag.queue = queue;
            exp_flag.queue_length = queue_length;
         }

         extra_relations(&exp_flag, pcp);

         if (pcp->m != 0) {
            queue = exp_flag.queue;
            queue_length = exp_flag.queue_length;
            s = (queue_length - start_length == 1) ? "y" : "ies";
            printf("Exponent checks gave %d redundanc%s\n",
                   queue_length - start_length,
                   s);
         }

         if (pcp->complete && output_level <= 1)
            text(5, pcp->cc, pcp->p, pcp->lastg, 0);

         t = runTime() - t;
         printf("Time to check exponents is %.2f seconds\n", t * CLK_SCALE);
         break;

      case ELIMINATE:
         t = runTime();
         symbols_setup = FALSE;
         if (pcp->cc == 1)
            class1_eliminate(pcp);
         else {
            /* if no tails have been added, do not perform update */
            if (y[pcp->clend + pcp->cc - 1] < pcp->lastg) {
               if (pcp->cc > 1 && !pcp->middle_of_tails && !pcp->update) {
                  update_generators(pcp);
                  pcp->update = TRUE;
               }
               eliminate(pcp->middle_of_tails, pcp);
               queue_length = 0;
               long_queue_length = 0;
            }
         }

         t = runTime() - t;
         printf("Elimination took %.2f seconds\n", t * CLK_SCALE);
         break;

      case LAST_CLASS:
         last_class(pcp);
         break;

      case MAXOCCUR:
         set_maxoccur(pcp);
         break;

      case METABELIAN:
         pcp->metabelian = TRUE;
         break;

      case JACOBI:
         calculate_jacobi(pcp);
         if (pcp->redgen != 0 && pcp->m != 0) {
            queue_setup = TRUE;
            if (prev_qlength == 0)
               queue_space(
                   &queue, &long_queue, &current_qlength, &prev_qlength, pcp);
            queue[++queue_length] = pcp->redgen;
         }
         break;

      case ECHELON:
         if (echelon_ready) {
            for (i = 1; i <= pcp->lastg; ++i)
               y[cp + pcp->lastg + i] = 0;
            output = pcp->fullop;
            pcp->fullop = TRUE;
            echelon(pcp);
            pcp->fullop = output;
            if (pcp->redgen != 0 && pcp->m != 0) {
               queue_setup = TRUE;
               if (prev_qlength == 0)
                  queue_space(&queue,
                              &long_queue,
                              &current_qlength,
                              &prev_qlength,
                              pcp);
               queue[++queue_length] = pcp->redgen;
            }
            echelon_ready = FALSE;
         } else
            printf("No relation to echelonise; first collect or commute\n");
         break;

      case AUTS:
         t = runTime();
         if (pcp->m == 0) {
            auts = read_auts(PQ, &pcp->m, &nmr_of_exponents, pcp);
            Setup_Action(head, list, auts, nmr_of_exponents, pcp);
         }
         Extend_Auts(head, list, y[pcp->clend + 1] + 1, pcp);

#ifdef DEBUG
         read_value(TRUE, "Input start generator: ", &start_gen, 1);
         read_value(TRUE, "Input final generator: ", &final_gen, start_gen);
         List_Auts(*head, *list, start_gen, final_gen, pcp);
/* print_array (*head, 0, (*head)[0] + 2); */
#endif
         queue_setup = TRUE;
         queue_space(&queue, &long_queue, &current_qlength, &prev_qlength, pcp);

         t = runTime() - t;
         printf("Extension of automorphisms took %.2f seconds\n",
                t * CLK_SCALE);
         break;

      case CLOSE_RELATIONS:
         t = runTime();
         s = (queue_length == 1) ? "y" : "ies";
         printf("The queue currently contains %d entr%s\n", queue_length, s);
         /*
           print_array (queue, 1, queue_length + 1);
           */
         read_value(TRUE, "Input queue factor: ", &factor, 0);
         limit = factor * (pcp->lastg - pcp->ccbeg + 1) / 100;
         if (!pcp->complete) {
            close_relations(TRUE,
                            limit,
                            1,
                            *head,
                            *list,
                            queue,
                            queue_length,
                            long_queue,
                            &long_queue_length,
                            pcp);
         }

         if (!pcp->complete && !pcp->overflow) {
            if (pcp->fullop || pcp->diagn)
               printf("Length of long queue after short queue closed is %d\n",
                      long_queue_length);
            close_relations(TRUE,
                            limit,
                            2,
                            *head,
                            *list,
                            long_queue,
                            long_queue_length,
                            long_queue,
                            &long_queue_length,
                            pcp);
            if (pcp->fullop || pcp->diagn) {
               printf("Final long queue length was %d\n", long_queue_length);
            }
         }

         if (pcp->complete && output_level <= 1)
            text(5, pcp->cc, pcp->p, pcp->lastg, 0);

         queue_length = long_queue_length = 0;
         t = runTime() - t;
         printf("Closing relations took %.2f seconds\n", t * CLK_SCALE);
         break;

      case STRUCTURE:
         read_value(
             TRUE, "Input initial pcp generator number: ", &start_gen, 1);
         if (start_gen <= pcp->lastg) {
            read_value(TRUE,
                       "Input final pcp generator number: ",
                       &final_gen,
                       start_gen);
            print_structure(start_gen, MIN(final_gen, pcp->lastg), pcp);
         } else
            printf("Invalid range supplied for pcp generator numbers\n");
         break;

      case ENGEL:
         t = runTime();
         queue_setup = TRUE;
         queue_space(&queue, &long_queue, &current_qlength, &prev_qlength, pcp);
         list_commutators(queue, &queue_length, pcp);

         /*
           List_Commutators (queue, &queue_length, pcp);
           */
         t = runTime() - t;
         printf(
             "Evaluation of Engel [y, (p - 1)x] identity took %.2f seconds\n",
             t * CLK_SCALE);
         break;

      case LIST_AUTOMORPHISMS:
         read_value(TRUE, "Input start generator: ", &start_gen, 1);
         read_value(TRUE, "Input final generator: ", &final_gen, start_gen);
         List_Auts(*head, *list, start_gen, final_gen, pcp);
         break;

         break;

      case RELATIONS_FILE:
         t = runTime();
         if (pcp->m != 0) {
            queue_setup = TRUE;
            start_length = queue_length;
            queue_space(
                &queue, &long_queue, &current_qlength, &prev_qlength, pcp);
         }

         read_relator_file(queue, &queue_length, pcp);

         if (pcp->m != 0) {
            s = (queue_length - start_length == 1) ? "y" : "ies";
            printf("Relation file gave %d redundanc%s\n",
                   queue_length - start_length,
                   s);
            if (queue_length != 0)
               print_array(queue, 1, queue_length + 1);
         }
         t = runTime() - t;
         printf("Processing relations file took %.2f seconds\n", t * CLK_SCALE);
         break;

      case DGEN_WORD:
         if (format != BASIC && symbols_setup == FALSE) {
            setup_symbols(pcp);
            symbols_setup = TRUE;
         }
         type = WORD;
         if (!is_space_exhausted(3 * pcp->lastg + 2, pcp)) {
            cp = pcp->lused;
            setup_defgen_word_to_collect(stdin, format, type, pcp->lused, pcp);
            echelon_ready = TRUE;
         }
         break;

      case DGEN_COMM:
         if (format != BASIC && symbols_setup == FALSE) {
            setup_symbols(pcp);
            symbols_setup = TRUE;
         }
         commute_defining_generators(format, pcp);
         echelon_ready = TRUE;
         break;

      case DGEN_AUT:
         if (format != BASIC && symbols_setup == FALSE) {
            setup_symbols(pcp);
            symbols_setup = TRUE;
         }
         auts = determine_action(format, &nmr_of_auts, pcp);
         break;

      case COMPACT:
         compact(pcp);
         break;

      case FORMULA:
         t = runTime();
         if (pcp->m != 0) {
            queue_setup = TRUE;
            start_length = queue_length;
            queue_space(
                &queue, &long_queue, &current_qlength, &prev_qlength, pcp);
         }

         evaluate_formula(queue, &queue_length, pcp);

         if (pcp->m != 0) {
            s = (queue_length - start_length == 1) ? "y" : "ies";
            printf("Formula checks gave %d redundanc%s\n",
                   queue_length - start_length,
                   s);
            if (queue_length != 0)
               print_array(queue, 1, queue_length + 1);
         }

         t = runTime() - t;
         printf("Formula evaluation took %.2f seconds\n", t * CLK_SCALE);
         break;

      case OUTPUT_PRESENTATION:
         /* TODO: We used to support more output formats, but now only
         GAP output is supported. As such, the following query is no redundant.
         We keep it for backward compatibility only. */
         name = GetString("Enter output file name: ");
         read_value(TRUE,
                    "Output file in GAP (2) format? ",
                    &file_format,
                    GAP_PRES_FORMAT);
         FileName = OpenFile(name, "a+");
         if (FileName != NULL) {
            if (file_format == GAP_PRES_FORMAT) {
               GAP_presentation(FileName, pcp, 1);
               printf("Group presentation written in GAP format to file\n");
            } else
               printf("Format must be %d\n", GAP_PRES_FORMAT);
         }
         CloseFile(FileName);
         break;

      case COMPACT_PRESENTATION:
         compact_description(TRUE, pcp);
         printf("Group description written to gps%d^%d\n", pcp->p, pcp->lastg);
         break;

      case EXIT:
      case MAXOPTION:
         printf("Exiting from interactive p-Quotient menu\n");
         break;

      } /* switch */
   } while (option != 0 && option != MAXOPTION);

   if (queue_setup) {
      free_vector(queue, 1);
      free_vector(long_queue, 1);
   }
}

/* interactive p-quotient menu */

void list_interactive_pq_menu(void)
{
   printf("\nAdvanced p-Quotient Menu\n");
   printf("-------------------------\n");
   printf("%d. Do individual collection\n", COLLECT);
   printf("%d. Solve the equation ax = b for x\n", SOLVE);
   printf("%d. Calculate commutator\n", COMMUTATOR);
   printf("%d. Display group presentation\n", DISPLAY_PRESENTATION);
   printf("%d. Set print level\n", PRINT_LEVEL);
   printf("%d. Set up tables for next class\n", SETUP);
   printf("%d. Insert tails for some or all classes\n", TAILS);
   printf("%d. Check consistency for some or all classes\n", CONSISTENCY);
   printf("%d. Collect defining relations\n", RELATIONS);
   printf("%d. Carry out exponent checks\n", EXTRA_RELATIONS);
   printf("%d. Eliminate redundant generators\n", ELIMINATE);
   printf("%d. Revert to presentation for previous class\n", LAST_CLASS);
   printf("%d. Set maximal occurrences for pcp generators\n", MAXOCCUR);
   printf("%d. Set metabelian flag\n", METABELIAN);
   printf("%d. Carry out an individual consistency calculation\n", JACOBI);
   printf("%d. Carry out compaction\n", COMPACT);
   printf("%d. Carry out echelonisation\n", ECHELON);
   printf("%d. Supply and/or extend automorphisms\n", AUTS);
   printf("%d. Close relations under automorphism actions\n", CLOSE_RELATIONS);
   printf("%d. Print structure of a range of pcp generators\n", STRUCTURE);
   printf("%d. Display automorphism actions on generators\n",
          LIST_AUTOMORPHISMS);
   printf("%d. Collect word in defining generators\n", DGEN_WORD);
   printf("%d. Compute commutator of defining generators\n", DGEN_COMM);
   printf("%d. Write presentation to file in GAP format\n",
          OUTPUT_PRESENTATION);
   printf("%d. Write compact description of group to file\n",
          COMPACT_PRESENTATION);
   printf("%d. Evaluate certain formulae\n", FORMULA);
   printf("%d. Evaluate action specified on defining generators\n", DGEN_AUT);
   printf("%d. Evaluate Engel (p - 1)-identity\n", ENGEL);
   printf("%d. Process contents of relation file\n", RELATIONS_FILE);
   printf("%d. Exit to basic menu\n", MAXOPTION);
}

#endif

/* set up space for the queues used in exponent checking */

int queue_space(int **queue,
                int **long_queue,
                int *current_qlength,
                int *prev_qlength,
                struct pcp_vars *pcp)
{
   *current_qlength = pcp->lastg - pcp->ccbeg + 1;
   if (*prev_qlength == 0) {
      *queue = allocate_vector(*current_qlength, 1, FALSE);
      *long_queue = allocate_vector(*current_qlength, 1, FALSE);
   } else if (*current_qlength != *prev_qlength) {
      *queue =
          reallocate_vector(*queue, *prev_qlength, *current_qlength, 1, FALSE);
      *long_queue = reallocate_vector(
          *long_queue, *prev_qlength, *current_qlength, 1, FALSE);
   }
   *prev_qlength = *current_qlength;

   return 0;
}
