/* gpmakesubwa.c 22/1/96.
 * 31/12/98 - altered to make states of type labeled, where label is
 * the coset rep. of coset of subgroup correspondiong to state.
 * 6/8/98 large scale reorganisation to omit globals, etc.
 * 5/2/98 change generators from type char to type `gen'.
 *
 * Build a candidate word-acceptor for those irreducible words in
 * a shortlex automatic group that lie in a given subgroup.
 * This will work, for example, for quasiconvex subgroups of
 * hyperbolic groups.
 *
 * SYNOPSIS:
 *      gpmakesubwa [-w] [-kbprogcos/-diff1cos/-diff2cos]
 *              [-diff1/-diff2/-diff1c] [-mrl maxreducelen] [-nc nochange]
 *		[-v] [-silent] [-l] [-ms maxstates] groupname [subname]
 *
 * subname defaults to "sub".
 * autgroup (or equivalents) should have been run on grouname already.
 * groupname.subname should contain the definition of the subgroup,
 * in the standard format.
 *
 * Two word-reduction routines are required, one for the group elements,
 * and one for the cosets of the subgroup in the group.
 * As in wordreduce, there are four possibilities for the automaton used
 * to perform each of these reductions.
 * However, since the group will have had its automatic structure calculated
 * already, we always use diff_reduction for that, and do not allow
 * rws_reduction.
 * Input is from groupname.diff1, groupname.diff2 (default) or
 * groupname.diff1c for the reduction in the group, and
 * input is from groupname.wa (word-acceptor),
 * groupname.subname.kbprog, groupname.subname.diff1,
 * etc. for the coset reduction.
 * 
 * The subgroup generators are input from groupname.subname, or from
 * groupname.subname.words, if -w is called.
 * (-w should be called after a run of gpchecksubwa has found words that
 *  should be accpeted by the subgroup word-acceptor but are not.)
 * Output is to groupname.subname.wa.
 *
 * OPTIONS:
 * -diff1/diff2/diff1c
 *		This determines which finite state automaton is used for
 *		the word-reduction in the group.
 *		The default is groupname.diff2.
 * -kbprogcos/diff1cos/diff2cos
 *  		Similar, for the coset reduction.
 *		Input from groupname.subname.kbprog, etc.
 * -nc	  nochange
 *              Exit when nochange words have been added to the
 *              language of subwa, without subwa changing.
 *		Default is 64.
 * -ms    maxstates
 *              At most maxstates states are allowed in the subgroup
 *		word-acceptor being produced. Default is 32767
 *		represent words in the subgroup can be remembered.
 *		Default is 32767.
 * -v           verbose
 * -silent      no diagnostics
 * -l/-h        large/huge hash-tables (for big examples)
 * -mrl maxreducelen
 *		To change the maximum word-length possible during reduction.
 *		Default is 4095
 *		
 */

#include <stdio.h>
#include "defs.h"
#include "fsa.h"
#include "rws.h"
#include "hash.h"
#include "definitions.h"

#define MAXEQNS         32767
#define MAXSTATES       32767
#define MAXREDUCELEN    4095
#define NOCHANGE	64
#define MAXNOCHANGE	4096

static FILE *rfile, *wfile;

int  (*reduce_word)();
int  (*reduce_word_cos)();

/* Functions defined in this file */
void fsa_init_subwa();
int  calc_inv();
void invert_word();
int  add_word_fsa();
void badusage_gpmakesubwa();

/* Functions defined in other files used in this file */
void  read_kbinput_simple();
void  fsa_read();
int   fsa_table_dptr_init();
boolean  read_word();
int   diff_reduce();
int   diff_reduce_cos();
int   rws_reduce();
int   add_word_to_buffer();
void  printbuffer();
void  process_names();
void  fsa_init();
fsa  *fsa_laband();
int   fsa_labeled_minimize();
void  fsa_print();
void  srec_copy();
void  fsa_clear();
void  gen_hash_init();
void  gen_hash_clear();
int   gen_hash_rec_len();
gen  *gen_hash_rec();
int   gen_hash_locate();
int   stringlen();
int   genstrlen();
void  genstrcpy();
void  genstrcat();
   

int 
main (int argc, char *argv[])
{ int  arg, delim, numsubgens, numsubelts, nochangect, eltct, genct, aw;
  char gpname[100], inf_wa[100], inf_gred[100], inf_cosred[100], suffix[100],
       inf_sub[100], fsaname[100], outf[100], tempfilename[100];
  boolean  wordfile, diff1_ip, diff2_ip, diff1c_ip, open, first,
  	   rws_ipcos, diff1_ipcos, diff2_ipcos, diff1c_ipcos;
  gen_hash_table cosht;
  gen * cosht_ptr;
  gen  gtestword[MAXREDUCELEN+1], costestword[MAXREDUCELEN+1];
  int ngens, *inv, **subwa_table;
  int i, l, ns;
  fsa   gwa, subwa, *subgwa;
  char **names;
  static gen_hash_table ght;
  rewriting_system rws, *rwsptr;
  reduction_struct grs, cosrs;
  int maxstates=MAXSTATES;
  int nochange=NOCHANGE;

  setbuf(stdout,(char*)0);
  setbuf(stderr,(char*)0);

  rwsptr = &rws;
  rwsptr->maxeqns = MAXEQNS;
  rwsptr->maxreducelen = MAXREDUCELEN;
  rwsptr->maxreducelenset = FALSE;
  rwsptr->inv_of=0;
  rwsptr->weight=0;rwsptr->level=0;rwsptr->history=0;
  rwsptr->slowhistory=0;rwsptr->slowhistorysp=0;
  rwsptr->preflen=0;rwsptr->prefno=0;
  rwsptr->cosets=FALSE;

  gpname[0] = '\0';
  suffix[0] = '\0';
  arg = 1;
  wordfile=FALSE;
  diff1_ip = diff2_ip = diff1c_ip 
  	   = rws_ipcos = diff1_ipcos = diff2_ipcos = FALSE;
  while (argc > arg) {
    if (strcmp(argv[arg],"-w")==0)
      wordfile = TRUE;
    else if (strcmp(argv[arg],"-diff1")==0)
      diff1_ip = TRUE;
    else if (strcmp(argv[arg],"-diff2")==0)
      diff2_ip = TRUE;
    else if (strcmp(argv[arg],"-diff1c")==0)
      diff1c_ip = TRUE;
    else if (strcmp(argv[arg],"-kbprogcos")==0)
      rws_ipcos = TRUE;
    else if (strcmp(argv[arg],"-diff1cos")==0)
      diff1_ipcos = TRUE;
    else if (strcmp(argv[arg],"-diff2cos")==0)
      diff2_ipcos = TRUE;
    else if (strcmp(argv[arg],"-silent")==0)
      kbm_print_level = 0;
    else if (strcmp(argv[arg],"-v")==0)
      kbm_print_level = 2;
    else if (strcmp(argv[arg],"-vv")==0)
      kbm_print_level = 3;
    else if (strcmp(argv[arg],"-l")==0)
      kbm_large = TRUE;
    else if (strcmp(argv[arg],"-h")==0)
      kbm_huge = TRUE;
    else if (strcmp(argv[arg], "-mrl") == 0) {
      rwsptr->maxreducelenset = TRUE;
      arg++;
      if (arg >= argc)
        badusage_gpmakesubwa();
      rwsptr->maxreducelen = atoi(argv[arg]);
    }
    else if (strcmp(argv[arg], "-nc") == 0) {
      arg++;
      if (arg >= argc)
        badusage_gpmakesubwa();
      nochange = atoi(argv[arg]);
    }
    else if (strcmp(argv[arg], "-ms") == 0) {
      arg++;
      if (arg >= argc)
        badusage_gpmakesubwa();
      maxstates = atoi(argv[arg]);
    }
    else {
       if (argv[arg][0] == '-')
         badusage_gpmakesubwa();
       if (strcmp(suffix,""))
         badusage_gpmakesubwa();
       if (strcmp(gpname,"")==0)
         strcpy(gpname,argv[arg]);
       else
         strcpy(suffix,argv[arg]);
    }
    arg++;
  }

  if (stringlen(gpname)==0)
    badusage_gpmakesubwa();

  if (stringlen(suffix)==0)
     strcpy(suffix,"sub");
  strcpy(inf_sub,gpname); strcat(inf_sub,"."); strcat(inf_sub,suffix);
  strcpy(outf,inf_sub); strcat(outf,".wa");
  if (wordfile) strcat(inf_sub,".words");

  strcpy(tempfilename,inf_sub);
  strcat(tempfilename,"temp_wa_XXX");

/* First sort out the reduction automaton and routine for the group */
  strcpy(inf_gred,gpname);
  if (diff1_ip)
    strcat(inf_gred,".diff1");
  else if (diff2_ip)
    strcat(inf_gred,".diff2");
  else if (diff1c_ip)
    strcat(inf_gred,".diff1c");
  else {
      diff2_ip = TRUE;
      strcpy(inf_gred,gpname); strcat(inf_gred,".diff2");
  }

  if ((rfile = fopen(inf_gred,"r")) == 0) {
    fprintf(stderr,"Cannot open file %s.\n",inf_gred);
    exit(1);
  }
 
  tmalloc(grs.wd_fsa,fsa,1)
  fsa_read(rfile,grs.wd_fsa,DENSE,0,0,TRUE,fsaname);
  fclose(rfile);
  ngens = grs.wd_fsa->alphabet->base->size;
  process_names(grs.wd_fsa->alphabet->base->names,ngens);
  grs.separator=ngens+1;
  cosrs.separator=ngens+1;
/* Set the pointers in the word-difference machine */
  if (fsa_table_dptr_init(grs.wd_fsa)==-1) exit(1);

/* Now the word-reduction machine for cosets */
  if (strncmp(suffix,"sub",3)==0) {
    strcpy(inf_cosred,gpname);
    strcat(inf_cosred,".cos");
    strcat(inf_cosred,suffix+3);
  }
  else {
    strcpy(inf_cosred,gpname);
    strcat(inf_cosred,".");
    strcat(inf_cosred,suffix);
    strcat(inf_cosred,"_cos");
  }

  open = FALSE;
  if (rws_ipcos)
    strcat(inf_cosred,".kbprog");
  else if (diff1_ipcos)
    strcat(inf_cosred,".midiff1");
  else if (diff2_ipcos)
    strcat(inf_cosred,".midiff2");
  else {
    strcat(inf_cosred,".kbprog");
    if (rfile = fopen(inf_cosred,"r")) {
      rws_ipcos = TRUE;
      open = TRUE;
    }
    else {
      diff2_ipcos = TRUE;
      strcpy(inf_cosred+stringlen(inf_cosred)-6,"midiff2");
    }
  }

  if (!open) 
    if ((rfile = fopen(inf_cosred,"r")) == 0) {
      fprintf(stderr,"Cannot open file %s.\n",inf_cosred);
      exit(1);
    }
 
  if (rws_ipcos) {
    read_kbinput_simple(rfile,FALSE,rwsptr);
    fclose(rfile);
    tmalloc(rwsptr->reduction_fsa,fsa,1);
    strcpy(inf_cosred+stringlen(inf_cosred)-6,"reduce");
    if ((rfile = fopen(inf_cosred,"r")) == 0) {
      fprintf(stderr,"Cannot open file %s.\n",inf_cosred);
      exit(1);
    }
    fsa_read(rfile,rwsptr->reduction_fsa,DENSE,0,0,TRUE,fsaname);
    fclose(rfile);
    process_names(rws.gen_name,ngens);
    tmalloc(rws.history,int,rws.maxreducelen+1);
    cosrs.rws = rwsptr;
  }
  else {
    tmalloc(cosrs.wd_fsa,fsa,1);
    fsa_read(rfile,cosrs.wd_fsa,DENSE,0,0,TRUE,fsaname);
    fclose(rfile);
    ngens = cosrs.wd_fsa->alphabet->base->size;
    process_names(cosrs.wd_fsa->alphabet->base->names,ngens);
/* Set the pointers in the word-difference machine */
    fsa_table_dptr_init(cosrs.wd_fsa);
  }

/* Set the generator names and the reduction algorithms. */
  names =  grs.wd_fsa->alphabet->base->names;
  reduce_word = diff_reduce;
  reduce_word_cos = rws_ipcos ? rws_reduce : diff_reduce_cos;

/* Now read the word-acceptor for the group */
  strcpy(inf_wa,gpname);
  strcat(inf_wa,".wa");
  if ((rfile = fopen(inf_wa,"r")) == 0) {
    fprintf(stderr,"Cannot open file %s.\n",inf_wa);
    exit(1);
  }
  fsa_read(rfile,&gwa,DENSE,0,0,TRUE,fsaname);
  fclose(rfile);

/* Now we initialise the subgroup word-acceptor that we shall be building */
  fsa_init_subwa(&subwa,&gwa,&cosht,ngens,maxstates);
  subwa_table = subwa.table->table_data_ptr; /* for fast access to table */
/* Calculate inverses of G-generators */
  tmalloc(inv,int,ngens+1);
  if (calc_inv(gtestword,ngens,inv,&grs)==-1)
    exit(1);
/* Initialise the hash table for storing the words in the G-generators that
 * lie in the subgroup.
 */
  gen_hash_init(&ght,FALSE,0,0,0);
  
/* Now open the file containing the words that generate the subgroup,
 * store them in the hash-table ght, and add them and their inverses to
 * the language accepted by subwa.
 */
  if ((rfile = fopen(inf_sub,"r")) == 0) {
    fprintf(stderr,"Cannot open file %s.\n",inf_sub);
    exit(1);
  }
  read_ident(rfile,kbm_buffer,&delim,FALSE);
    /* this is the name of the record containing the subgenerators */
  if (delim != ':'){
    fprintf(stderr,
        "#Input error: file must contain a record assignment\n");
    exit(1);
  }
  check_next_char(rfile,'=');
  read_ident(rfile,kbm_buffer,&delim,FALSE);
  if (delim != '(' || strcmp(kbm_buffer,"rec")!=0){
    fprintf(stderr,
        "#Input error: file must contain a record assignment\n");
    exit(1);
  }
  read_ident(rfile,kbm_buffer,&delim,FALSE);
    if (strcmp(kbm_buffer,"subGenerators")!=0) {
    fprintf(stderr,
    "Input error. 'subGenerators' list expected in subgroup generator file.\n");
    exit(1);
  }
  if (delim != ':'){
    fprintf(stderr,
        "#Input error: bad 'subgens' list assignment\n");
    exit(1);
  }
  check_next_char(rfile,'=');
  check_next_char(rfile,'[');
  numsubgens=0;
  numsubelts=0;
  do {
    if (!read_word(rfile,gtestword,gtestword+rws.maxreducelen,
                                    &delim,names,ngens,TRUE)) {
      fprintf(stderr,
        "#Input error: no subgroup generators or missing word.\n");
      exit(1);
    }
    /* Check that it is reduced as a word in G */
    if ((*reduce_word)(gtestword,&grs)==-1) exit(1);
    if (genstrlen(gtestword)>0) {
      /* Copy it into the hash-table and see if it is new */
      genstrcpy(ght.current_ptr,gtestword);
      if (gen_hash_locate(&ght,genstrlen(gtestword)+1) > numsubgens) {
        /*It is new - so add it to language of subwa */
        numsubgens++;
        if (add_word_fsa(&subwa,ngens,maxstates,
                           gtestword,costestword,names,&cosht,&cosrs)==-1)
           exit(1);
        /* Now try its inverse */
        invert_word(gtestword,inv);
        if ((*reduce_word)(gtestword,&grs)==-1) exit(1);
        genstrcpy(ght.current_ptr,gtestword);
        if (gen_hash_locate(&ght,genstrlen(gtestword)+1) > numsubgens) {
          /*It is new - so add it to language of subwa */
          numsubgens++;
          if (add_word_fsa(&subwa,ngens,maxstates,
                             gtestword,costestword,names,&cosht,&cosrs)==-1)
            exit(1);
        }
      }
    }
  } while (delim!=']');
  fclose(rfile);

  if (numsubgens*numsubgens>nochange) {
    nochange=numsubgens*numsubgens<=MAXNOCHANGE ? numsubgens*numsubgens
                                                : MAXNOCHANGE;
    if (kbm_print_level>1)
      printf(
       "  #Due to large number of subgp. gens., increasing nochange to %d.\n",
       nochange);
  }
  if (kbm_print_level>0)
    printf("#There are %d subgroup generators (including inverses).\n",
      numsubgens);

  /* Now we start to go through the sugroup elements stored in ght, and
   * multiply them by the subgroup generators to get new words in the
   * subgroup, and add them to the language of subwa.
   * We stop after considering nochange words in the subgroup generators
   * with no change to subwa.
   */
  numsubelts=numsubgens;
  nochangect=0;
  for (eltct=1;eltct<=numsubelts && nochangect<nochange;eltct++)
    for (genct=1;genct<=numsubgens && nochangect<nochange;genct++) {
      genstrcpy(gtestword,gen_hash_rec(&ght,eltct));
      genstrcat(gtestword,gen_hash_rec(&ght,genct));
      /* Reduce it as a word in G */
      if ((*reduce_word)(gtestword,&grs)==-1) exit(1);
      if (genstrlen(gtestword)>0) {
        /* Copy it into the hash-table and see if it is new */
        genstrcpy(ght.current_ptr,gtestword);
        if (gen_hash_locate(&ght,genstrlen(gtestword)+1) > numsubelts) {
          /*It is new - so add it to language of subwa */
          numsubelts++;
          if (kbm_print_level>1 &&
               ((numsubelts<=500 && numsubelts%100==0)|| numsubelts%500==0))
            printf("  #Number of subgroup elements stored=%d.\n",numsubelts);
          aw=add_word_fsa(&subwa,ngens,maxstates,
                           gtestword,costestword,names,&cosht,&cosrs);
          if (aw==-1)
            exit(1);
          if (aw==1)
            nochangect=0;
          else 
            nochangect++;
          /* Now try its inverse */
          invert_word(gtestword,inv);
          if ((*reduce_word)(gtestword,&grs)==-1) exit(1);
          genstrcpy(ght.current_ptr,gtestword);
          if (gen_hash_locate(&ght,genstrlen(gtestword)+1) > numsubgens) {
            /*It is new - so add it to language of subwa */
            numsubelts++;
            if (kbm_print_level>1 &&
               ((numsubelts<=500 && numsubelts%100==0)|| numsubelts%500==0))
              printf("  #Number of subgroup elements stored=%d.\n",numsubelts);
            aw=add_word_fsa(&subwa,ngens,maxstates,
                              gtestword,costestword,names,&cosht,&cosrs);
            if (aw==-1)
              exit(1);
            if (aw==1)
              nochangect=0;
            else
              nochangect++;
          }
        }
      }
    }

  /* record the coset reps. as labels of the states */
  ns = subwa.states->size;
  subwa.states->type = LABELED;
  tmalloc(subwa.states->labels,srec,1);
  subwa.states->labels->size=ns;
  subwa.states->labels->type=WORDS;
  subwa.states->labels->alphabet_size=ngens;
  for (i=1;i<=ngens;i++) {
    l = stringlen(grs.wd_fsa->alphabet->base->names[i]);
    tmalloc(subwa.states->labels->alphabet[i],char,l+1);
    strcpy(subwa.states->labels->alphabet[i],
                          grs.wd_fsa->alphabet->base->names[i]);
  }
  tmalloc(subwa.states->setToLabels,setToLabelsType,ns+1);
  tmalloc(subwa.states->labels->words,gen *,ns+1);
  subwa.states->setToLabels[0]=0;
  for (i=1;i<=ns;i++) {
    cosht_ptr=gen_hash_rec(&cosht,i);
    l = gen_hash_rec_len(&cosht,i);
    tmalloc(subwa.states->labels->words[i],gen,l+1);
    genstrcpy(subwa.states->labels->words[i],cosht_ptr);
    subwa.states->setToLabels[i]=i;
  }
  if (kbm_print_level>1) {
    printf(
     "  #Initial subgroup word-acceptor with %d states computed.\n",ns);
    printf("  #Now and-ing it with group word-acceptor.\n");
  }
  /* Now we "and" the subwa fsa with gwa and minimize */
  subgwa = fsa_laband(&subwa,&gwa,DENSE,TRUE,tempfilename);
  if (kbm_print_level>1)
    printf(
   "  #Subgroup word-acceptor with %d states before minimization computed.\n",
    subgwa->states->size);
  if (fsa_labeled_minimize(subgwa)==-1) exit(1);

  base_prefix(fsaname);
  strcat(fsaname,".wa");
  wfile = fopen(outf,"w");
  fsa_print(wfile,subgwa,fsaname);
  fclose(wfile);
  if (kbm_print_level>0)
    printf(
     "#Subgroup word-acceptor with %d states computed.\n",
       subgwa->states->size);

  gen_hash_clear(&ght);
  gen_hash_clear(&cosht);
  tfree(inv);
  fsa_clear(subgwa); tfree(subgwa);
  fsa_clear(grs.wd_fsa);
  tfree(grs.wd_fsa);
  if (rws_ipcos) {
    rws_clear(&rws);
    fsa_clear(rws.reduction_fsa);
    tfree(rws.reduction_fsa);
  }
  else {
    fsa_clear(cosrs.wd_fsa);
    tfree(cosrs.wd_fsa);
  }
  exit(0);
}
 
void
fsa_init_subwa(subwaptr,gwaptr,coshtptr,ngens,maxstates)
  fsa *subwaptr;
  fsa *gwaptr;
  gen_hash_table *coshtptr;
  int ngens,maxstates;
/* Initialise the subgroup word-acceptor */
{ int i;
  fsa_init(subwaptr);


  subwaptr->states->size = 1;

  srec_copy(subwaptr->alphabet,gwaptr->alphabet);

  subwaptr->num_initial = 1;
  tmalloc(subwaptr->initial,int,2);
  subwaptr->initial[1] = 1;
/* The initial state is also the single accepting state */
  subwaptr->num_accepting = 1;
  tmalloc(subwaptr->accepting,int,2);
  subwaptr->accepting[1] = 1;

  subwaptr->flags[DFA] = TRUE;
  subwaptr->flags[ACCESSIBLE] = TRUE;

  fsa_table_init(subwaptr->table,maxstates,ngens);
  subwaptr->table->printing_format = DENSE;
  for (i=1;i<=ngens;i++)
    set_dense_target(subwaptr->table->table_data_ptr,i,1,0);

/* The states of subwa will be cosets of the subgroup in G.
 * They will be stored as G-words, specifying the minimal coset rep.
 * These words will be stored in the hash-table *coshtptr.
 * We initialise the word for the first state as the empty word.
 */
  gen_hash_init(coshtptr,FALSE,0,0,0);
  coshtptr->current_ptr[0]='\0';
  if (gen_hash_locate(coshtptr,1)!=1) {
    fprintf(stderr,"Hash-initialisation problem in fsa_init_subwa.\n");
    exit(1);
  }
}

int
calc_inv(gtestword,ngens,inv,rsptr)
  gen *gtestword;
  int ngens;
  int *inv;
  reduction_struct *rsptr;
/* Calculates inverses of generators - essentially the same as the routine
 * calculate_inverses in ../lib/worddiff.c, but it is not convenient to
 * use that here.
 */
{ int i, j;
  for (i=1;i<=ngens;i++) for (j=1;j<=ngens;j++) {
    gtestword[0] = i;
    gtestword[1] = j;
    gtestword[2] = '\0';
    if ((*reduce_word)(gtestword,rsptr)==-1) return -1;
    if (genstrlen(gtestword)==0) {
      inv[i] = j;
      break;
    }
  }
  return 0;
}

void
invert_word(gtestword,inv)
  gen *gtestword;
  int *inv;
/* Invert the word in gtestword */
{ gen *wb, *we, swap;
  wb=gtestword; we=gtestword+genstrlen(gtestword)-1;
  while (wb<we) {
    swap=inv[*wb]; *wb=inv[*we]; *we=swap;
    wb++; we--;
  }
  if (wb==we)
    *wb=inv[*wb];
}

int
add_word_fsa(
      subwaptr,ngens,maxstates,gtestword,costestword,names,coshtptr,rsptr)
   fsa *subwaptr;
   int ngens,maxstates;
   gen *gtestword,*costestword;
   char **names;
   gen_hash_table *coshtptr;
   reduction_struct *rsptr;
/* Alter the fsa subwa to make it accept the word gtestword */
{ int state, i, j,  glen, coslen, newstate, imstate;
  gen *cosrep, g;
  int changed=0;
  int **subwa_table=subwaptr->table->table_data_ptr;
  kbm_buffer[0]='\0';
  add_word_to_buffer(stdout,gtestword,names);
    /* In case there is an error and we need to print gtestword */
  state=1;
  costestword[0]=rsptr->separator;
  costestword[1]='\0';
  /* We reduce the word _H*gtestword symbol by symbol, tracing it through
   * subwa as we go.
   */
  glen=genstrlen(gtestword);
  for (i=0;i<glen;i++) {
    coslen=genstrlen(costestword);
    g=gtestword[i];
    costestword[coslen]=g;
    costestword[coslen+1]='\0';
    if ((*reduce_word_cos)(costestword,rsptr)==-1) return -1;
    cosrep=costestword;
    while (*cosrep!=rsptr->separator)
      cosrep++;
    cosrep++;
    /* Copy the coset-rep into the hash-table, and see if we have it already */
    genstrcpy(coshtptr->current_ptr,cosrep);
    newstate = gen_hash_locate(coshtptr,genstrlen(cosrep)+1);
    if (newstate>subwaptr->states->size) {
      /* New state! */
      changed=1;
      if (newstate>maxstates) {
        fprintf(stderr,"Too many states in subwa - increase maxstates.\n");
        exit(1);
      }
      if (kbm_print_level>1 &&
           ((newstate<=500 && newstate%100==0)|| newstate%500==0))
        printf("  #Number of states in subwa = %d.\n",newstate);
      for (j=1;j<=ngens;j++)
        set_dense_target(subwa_table,j,newstate,0);
      subwaptr->states->size=newstate;
                         /* should be subwaptr->states->size+1 ! */
    }
    if ((imstate=dense_target(subwa_table,g,state)) && imstate!=newstate) {
      fprintf(stderr,"Error tracing word: "); printbuffer(stdout);
      exit(1);
    }
    set_dense_target(subwa_table,g,state,newstate);
    state=newstate;
  }
  if (state!=1) {
    fprintf(stderr,"Final error tracing word: "); printbuffer(stdout);
    exit(1);
  }
  return changed;
}

void 
badusage_gpmakesubwa (void)
{
    fprintf(stderr,
   "Usage: gpmakesubwa [-w] [-kbprogcos/-diff1cos/-diff2cos]\n");
    fprintf(stderr,
   "\t[-diff1/-diff2/-diff1c] [-mrl maxreducelen] [-nc nochange]\n");
    fprintf(stderr,
     "\t[-v] [-silent] [-l] [-ms maxstates] groupname [subname]\n");
    exit(1);
}
