/****************************************************************************
**
*A  collect.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 "constants.h"
#define MAXEXP (1 << 30)

void stack_overflow(void);
void integer_overflow(void);
void add_string(int string,
                int length,
                int exponent,
                int collected_part,
                struct pcp_vars *pcp);

/* an exponent vector with base address collected_part
   is multiplied on the right by a string with base address
   pointer; the string is assumed to be normal

   this routine follows the algorithm devised by
   M.R. Vaughan-Lee for collection from the left;
   step numbers correspond to step numbers in
   "Collection from the Left" by M.R. Vaughan-Lee,
   J. Symbolic Computation (1990) 9, 725-733

   this version embodies a simpler form of combinatorial
   collection as described on page 733, which lessens the
   chance of integer overflow; it also incorporates recursion
   when adding strings and generators to the exponent vector

   during the collection algorithm, pointers to a number of strings
   are stored on a stack; theoretically, the stack depth could get
   as large as (p - 1) * pcp->lastg * pcp->cc, but, in practice, depths
   this large do not arise; however, it could be necessary to increase
   the dimensions of the stack arrays for some calculations with large
   groups; STACK_SIZE is the maximum depth of the stack; this constant
   is declared in the constants header file; overflow is tested in
   this algorithm

   integer overflow is also tested for, although it can only arise
   if p^3 > 2^31; if p^2 > 2^31, integer overflow can arise which
   is not tested for

   if either overflow arises, a message is printed out and
   the program terminates;

   #####################################################################

   note the sections in this code enclosed within

   #ifndef EXPONENT_P
   #endif

   if you insert as part of the header material of this file

   #define EXPONENT_P

   or supply EXPONENT_P as a -D flag to the compiler, then the
   resulting collector assumes that all generators of the pcp
   have order p and does not execute these portions of the code */

/* stack size on Apollo causes problems */

#ifdef APOLLO
static t_int strstk[STACK_SIZE]; /* string stack */
static t_int lenstk[STACK_SIZE]; /* length stack */
static t_int expstk[STACK_SIZE]; /* exponent stack */
#endif

/* if Lie program, use different collect routine */
#if defined(GROUP)

void collect(int pointer, int collected_part, struct pcp_vars *pcp)
{
   register int *y = y_address;

   register int p1;          /* string pointer */
   register int ce;          /* collected exponent */
   register int cg;          /* collected generator */
   register int ug;          /* uncollected generator */
   register int ue;          /* uncollected exponent */
   register int sp = 0;      /* stack pointer */
   register int exp;         /* exponent */
   register int len = 1;     /* length */
   register int str;         /* string pointer */
   register int halfwt;      /* last generator with weight <= cc/2 */
   register int thirdwt;     /* last generator with weight <= cc/3 */
   register int weight_diff; /* current class - weight of ug */
   register int entry;       /* value to be inserted in collected part */
   register int firstcg;     /* first collected generator for loop counter */
   register int lastcg;      /* last collected generator for loop counter */
   register int maxexp;      /* max exponent allowed in call to add_string */

   register int cp = collected_part;
   register int class_end = pcp->clend;
   register int current_class = pcp->cc;
   register int prime = pcp->p;
   register int pm1 = pcp->pm1;
   register int p_pcomm = pcp->ppcomm;
   register int p_power = pcp->ppower;
   register int structure = pcp->structure;

#ifndef APOLLO
   int strstk[STACK_SIZE]; /* string stack */
   int lenstk[STACK_SIZE]; /* length stack */
   int expstk[STACK_SIZE]; /* exponent stack */
#endif

   register int i;

#include "access.h"

   /* if prime is 2, use special collector */
   if (prime == 2) {
      collectp2(pointer, collected_part, pcp);
      return;
   }

   /* Step (0) --
      initialize collector */

   if (pointer < 0)
      lenstk[0] = y[-pointer + 1];
   else if (pointer == 0)
      return;

   strstk[0] = pointer;
   expstk[0] = 1;

   maxexp = (MAXEXP / prime) * 2;
   halfwt = y[class_end + current_class / 2];
   thirdwt = y[class_end + current_class / 3];

   /* Step (1) --
      process next word on stack */

   while (sp >= 0) {

      str = strstk[sp];
      exp = expstk[sp];

      /* check if str is a string or a generator */

      if (str < 0) {
         /* we have a genuine string */
         len = lenstk[sp];
         sp--;

         /* get first generator exponent pair from string */
         i = y[-str + 2];
         ug = FIELD2(i);

         /* if ug > halfwt, the string can be added to the
            collected part without creating any commutators */
         if (ug > halfwt) {
            add_string(str, len, exp, cp, pcp);
            continue;
         }

         /* ug <= halfwt and so exp must equal 1; stack remainder of string */
         ue = FIELD1(i);
         if (len != 1) {
            strstk[++sp] = str - 1;
            lenstk[sp] = len - 1;
         }
      } else {
         /* str is a generator */
         ug = str;
         ue = exp;
         sp--;
         /* if ug > halfwt, ug commutes with all higher generators */
         if (ug > halfwt) {
            add_string(ug, 1, ue, cp, pcp);
            continue;
         }
      }

      /* ug <= halfwt; if ug > thirdwt, any commutators arising in
         collecting ug commute with all generators after ug, so ug
         can be collected without stacking up collected part */

      if (ug <= thirdwt) {

         /* Step (2) --
            combinatorial collection;
            scan collected part towards the left;
            bypass generators we know must commute with ug;
            when 2 * WT(cg) + WT(ug) > current_class, all generators
            occurring in [cg, ug] commute with each other;
            [cg^ce, ug] = [cg, ug]^ce;
            if cg1,..., cgk all satisfy this weight condition then
            [cg1 * ... * cgk, ug] = [cg1, ug] ... [cgk, ug] */

         if (ue != 1) {
            /* we only move one ug at a time; stack ug^(ue - 1) */
            if (++sp >= STACK_SIZE)
               stack_overflow();
            strstk[sp] = ug;
            expstk[sp] = ue - 1;
            ue = 1;
         }

         weight_diff = current_class - WT(y[structure + ug]);
         firstcg = y[class_end + weight_diff];
         lastcg = y[class_end + weight_diff / 2];

         /* scan collected part to the left, bypassing generators
            which must commute with ug; the collected part between
            lastcg and firstcg contains a word w; we add in [w, ug] */

         for (cg = firstcg; cg > ug; cg--) {
            ce = y[cp + cg];
            if (ce != 0) {
               /* add [cg, ug]^ce to the collected part */
               p1 = y[p_pcomm + cg] + ug;
               p1 = y[p1];
               if (p1 != 0) {
                  if (cg <= lastcg)
                     break;
                  if (p1 < 0)
                     len = y[-p1 + 1];
                  add_string(p1, len, ce, cp, pcp);
               }
            }
         }

         if (cg == ug) {
            /* we have reached ug position during combinatorial
               collection; add 1 to ug entry of collected part
               without stacking any entries if appropriate */
            if (y[cp + ug] == pm1) {
               if (y[p_power + ug] == 0) {
                  y[cp + ug] = 0;
                  continue;
               }
            } else {
               ++y[cp + ug];
               continue;
            }
         }

         /* we have now added in [w, ug]; stack up
            collected part between firstcg and cg + 1 */
         for (i = firstcg; i > cg; i--) {
            ce = y[cp + i];
            if (ce != 0) {
               y[cp + i] = 0;
               if (++sp >= STACK_SIZE)
                  stack_overflow();
               strstk[sp] = i;
               expstk[sp] = ce;
            }
         }

         /* Step (3) --
            ordinary collection; we have moved ug to the cg position;
            continue scanning to the left */
         for (; cg > ug; cg--) {
            ce = y[cp + cg];
            if (ce != 0) {
               /* zero the cg entry of collected part */
               y[cp + cg] = 0;

               /* get [cg, ug] */
               p1 = y[p_pcomm + cg] + ug;
               p1 = y[p1];
               if (p1 == 0) {
                  /* cg commutes with ug so stack cg^ce */
                  if (++sp >= STACK_SIZE)
                     stack_overflow();
                  strstk[sp] = cg;
                  expstk[sp] = ce;
               } else {
                  /* cg does not commute with ug;
                     we can only move ug past one cg at a time;
                     stack [cg, ug] and then cg a total of ce times */

                  if (sp + ce + ce >= STACK_SIZE)
                     stack_overflow();
                  if (p1 < 0)
                     len = y[-p1 + 1];

                  for (; ce > 0; --ce) {
                     strstk[++sp] = p1;
                     lenstk[sp] = len;
                     expstk[sp] = 1;
                     strstk[++sp] = cg;
                     expstk[sp] = 1;
                  }
               }
            }
         }

         /* we have moved ug to the correct position;
            add 1 to the ug entry of collected part */
         add_string(ug, 1, 1, cp, pcp);
         continue;
      } /* ug <= thirdwt */

      /* Step (6) -- ug > thirdwt;
         move ug^ue past entries in the collected part, adding
         commutators directly to the collected part;
         scan collected part towards the left, bypassing generators
         we know must commute with ug; if the cg position in
         the collected part contains an entry ce then this
         represents cg^ce;  [cg^ce, ug^ue] = [cg, ug]^(ce * ue),
         and we add the ce * ue power of [cg, ug] directly to
         the collected part */

      weight_diff = current_class - WT(y[structure + ug]);
      firstcg = y[class_end + weight_diff];

      for (cg = firstcg; cg > ug; cg--) {
         ce = y[cp + cg];
         if (ce != 0) {
            /* add [cg, ug]^(ce * ue) directly to the collected part */
            p1 = y[p_pcomm + cg];
            p1 = y[p1 + ug];
            if (p1 != 0) {
               exp = ce * ue;
               if (exp > maxexp)
                  integer_overflow();
               if (p1 < 0)
                  len = y[-p1 + 1];
               add_string(p1, len, exp, cp, pcp);
            }
         }
      }

      /* add ue to the ug entry of collected part */
      entry = y[cp + ug] + ue;
      if (entry < prime) {
         y[cp + ug] = entry;
         continue;
      } else {
         y[cp + ug] = entry - prime;
         p1 = y[p_power + ug];
         if (p1 == 0)
            continue;
      }

      /* adding ue to the ug entry has created an entry >= prime;
         we have to stack some of collected part */
      for (cg = firstcg; cg > ug; cg--) {
         ce = y[cp + cg];
         if (ce != 0) {
            /* set entry to zero and stack cg^ce */
            y[cp + cg] = 0;
            if (++sp >= STACK_SIZE)
               stack_overflow();
            strstk[sp] = cg;
            expstk[sp] = ce;
         }
      }

      /* add in ug^p; p1 is a pointer to ug^p */
      if (p1 < 0)
         len = y[-p1 + 1];
      add_string(p1, len, 1, cp, pcp);
      continue;
   }
}

#endif /* GROUP*/

/* add exponent times the string with address string and length length
   directly to the collected part with base address collected_part,
   recursively adding powers as required */

void add_string(int string,
                int length,
                int exponent,
                int collected_part,
                struct pcp_vars *pcp)
{
   register int *y = y_address;

   register int cp = collected_part;
   register int exp = exponent;
   register int len = length;
   register int str = string;
   register int entry;
   register int ug;
   register int ue;
   register int power;

   register int class_begin = pcp->ccbeg;
   register int prime = pcp->p;
   register int p_power = pcp->ppower;

   register int i;
   int lower, upper;
#include "access.h"

   if (str > 0) {
      /* Step (4) --
         we have moved generator str to the correct position;
         add exp to the str entry of the collected part;
         reduce entry modulo p and add a power of str^p
         to the collected part if necessary */

      entry = y[cp + str] + exp;
      y[cp + str] = entry % prime;
      if (str < class_begin) {
         exp = entry / prime;
#ifndef EXPONENT_P
         if (exp != 0) {
            /* we need to recursively add in (str^p)^exp */
            str = y[p_power + str];
            if (str != 0) {
               if (str < 0)
                  len = y[-str + 1];
               add_string(str, len, exp, cp, pcp);
            }
         }
#endif
      }
   } else {
      /* Step (5) --
         add string with base address -str and length len
         directly to the collected part exp times; if this
         creates an entry >= prime we reduce the entry modulo
         prime and add in the appropriate power */

      lower = -str + 2;
      upper = -str + len + 1;

      /* get one generator exponent pair at a time from string */

      for (i = lower; i <= upper; i++) {
         ug = FIELD2(y[i]);
         ue = FIELD1(y[i]) * exp;
         entry = y[cp + ug] + ue;

         /* add ue to ug entry of the collected part and reduce mod p */
         y[cp + ug] = entry % prime;

#ifndef EXPONENT_P
         /* we need to recursively add in (ug^p)^power */
         if (ug < class_begin) {
            power = entry / prime;
            if (power != 0) {
               str = y[p_power + ug];
               if (str != 0) {
                  if (str < 0)
                     len = y[-str + 1];
                  add_string(str, len, power, cp, pcp);
               }
            }
         }
#endif
      }
   }
}

/* stack is not big enough */

void stack_overflow(void)
{
   printf("Stack overflow in collection routine; you should increase\n");
   printf("value of STACK_SIZE in constants.h and recompile.\n");
   exit(FAILURE);
}

/* arithmetic overflow */

void integer_overflow(void)
{
   printf("Arithmetic overflow may occur in collection ");
   printf("routine. Results may be invalid.\n");
   exit(FAILURE);
}
