/*****************************************************************************
*                                                                            *
* miscellaneous utilities for use with nauty 2.8.                            *
* None of these procedures are needed by nauty, but all are by dreadnaut.    *
*                                                                            *
*   Copyright (1984-2023) Brendan McKay.  All rights reserved.               *
*   Subject to waivers and disclaimers in nauty.h.                           *
*                                                                            *
*   CHANGE HISTORY                                                           *
*       10-Nov-87 : final changes for version 1.2                            *
*        5-Dec-87 : changes made for version 1.3 :                           *
*                   - added procedures readinteger() and readstring()        *
*                   - replaced all uses of fscanf() by appropriate uses      *
*                     of readinteger() or readstring()                       *
*                   - "N:" is now illegal in readgraph() if N is too large   *
*                     or too small                                           *
*       28-Sep-88 : renamed to version 1.4 (no changes to this file)         *
*       23-Mar-89 : changes for version 1.5 :                                *
*                   - declared key in hash()                                 *
*                   - changed file name to naututil.c                        *
*       29-Mar-89 : - declared workperm[] and workset[], and modified        *
*                     many routines to use them.                             *
*                   - added putmapping()                                     *
*                   - reworked some code in mathon() and rangraph()          *
*        3-Apr-89 : - changed putorbits() to use O(n) algorithm              *
*        5-Apr-89 : - modifed readgraph() to not require fresh line          *
*                   - changed MAKEEMPTY uses to EMPTYSET uses                *
*       26-Apr-89 : - moved ptncode() and equitable() to nautaux.c           *
*                   - added putquotient()                                    *
*       18-Aug-89 : - modified putset() to use "i:j" syntax optionally       *
*                   - modified putorbits() to use putset()                   *
*                   - modified calling sequence for mathon()                 *
*       19-Aug-90 : - changed delimeter arg of copycomment to int            *
*       14-Oct-90 : renamed to version 1.6 (no changes to this file)         *
*       23-Jan-91 : changes for version 1.7 :                                *
*                   - fixed bug in complement()                              *
*       27-Aug-92 : - made linelength <= 0 mean no line breaks               *
*        5-Jun-93 : renamed to version 1.7+ (no changes to this file)        *
*       18-Aug-93 : renamed to version 1.8 (no changes to this file)         *
*       17-Sep-93 : renamed to version 1.9 (no changes to this file)         *
*       13-Jul-96 : changes for version 2.0 :                                *
*                   - added dynamic allocation                               *
*                   - added limit parameter to readstring                    *
*                   - added readvperm() and sublabel()                       *
*       31-Aug-96 : - converted from RAN to KRAN                             *
*        6-Feb-97 : - corrected DYNALLOC1 call in putmapping                 *
*       10-Dec-97 : - KRAN now initialises automatically                     *
*        9-Jan-00 : - added naututil_check()                                 *
*       12-Feb-00 : - some minor code formatting                             *
*       16-Nov-00 : - changes as listed in nauty.h                           *
*       23-Apr-01 : changes for version 2.1 :                                *
*                   - removed EXTDEFS                                        *
*        2-Jun-01 : - added converse()                                       *
*       21-Nov-01 : use NAUTYREQUIRED in naututil_check()                    *
*       11-Apr-03 : changes for version 2.2 :                                *
*                   - added rangraph2()                                      *
*       17-Nov-03 : changed INFINITY to NAUTY_INFINITY                       *
*       10-Dec-06 : removed BIGNAUTY                                         *
*        4-Nov-09 : added readgraph_sg, putgraph_sg, putcanon_sg             *
*       10-Nov-09 : removed shortish and permutation types                   *
*       14-Nov-09 : added relabel_sg(), putdegs_sg(), sublabel_sg()          *
*       19-Nov-09 : added individualise()                                    *
*       20-Nov-09 : added hashgraph_sg(), listhash(), hashgraph()            *
*       19-Dec-09 : added ranreg_sg(), rangraph2_sg()                        *
*       21-May-10 : conform to type changes in sparsegraph                   *
*        5-Jun-10 : add mathon_sg()                                          *
*       10-Jun-10 : add putquotient_sg() and complement_sg()                 *
*       26-Jan-11 : fix nde error in sublabel_sg()                           *
*       15-Jan-12 : add TLS_ATTR attributes                                  *
*        3-Mar-12 : add putorbitsplus() and putset_firstbold()               *
*                 : write orbit sizes if not trivial                         *
*       17-Mar-12 : move seed definition to naututil.h                       *
*       20-Sep-12 : allow quoted strings in readstring()                     *
*       20-Sep-12 : the first argument of ungetc is int, not char            *
*        4-Mar-13 : remove a side-effect issue in setinter()                 *
*       17-Dec-15 : add readgraph_swg() and update putgraph_sg()             *
*       22-Jan-16 : add readintger_sl() and getint_sl()                      *
*       29-Feb-16 : add subpartition()                                       *
*        6-Apr-16 : add countcells(), make subpartition return a count       *
*       13-Jun-23 : simplify sethash() without changing the value            *
*                                                                            *
*****************************************************************************/

#define ONE_WORD_SETS
#include "naututil.h"    /* which includes nauty.h, nautinv.h and stdio.h */
#include "nausparse.h"

#if  MAXM==1
#define M 1
#else
#define M m
#endif

#if !MAXN
DYNALLSTAT(int,workperm,workperm_sz);
DYNALLSTAT(set,workset,workset_sz);
#else
static TLS_ATTR int workperm[MAXN+2];   /* used for scratch purposes */
static TLS_ATTR set workset[MAXM];      /* used for scratch purposes */
#endif

#define ECHUNKSIZE 1000  /* should be a multiple of 2 */
typedef struct echunk {struct echunk *next; int edge[ECHUNKSIZE];} echunk;
static TLS_ATTR echunk first_echunk = {NULL,{0}};
typedef struct echunkw {struct echunkw *next; \
 struct {int v1,v2; sg_weight wt;} edge[ECHUNKSIZE];} echunkw;
static TLS_ATTR echunkw first_echunkw = {NULL,{{0,0,0}}};

#ifdef  NLMAP
#define GETNW(c,f) do c = getc(f); while (c==' '||c=='\t')
#define GETNWC(c,f) do c = getc(f); while (c==' '||c==','||c=='\t')
#define GETNWL(c,f) do c = getc(f); while (c==' '||c=='\n'||c=='\t')
#else
#define GETNW(c,f) do c = getc(f); while (c==' '||c=='\t'||c=='\r')
#define GETNWC(c,f) do c = getc(f); while (c==' '||c==','||c=='\t'||c=='\r')
#define GETNWL(c,f) do c = getc(f); while (c==' '||c=='\n'||c=='\t'||c=='\r')
#endif

#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')

static const long fuzz1[] = {1984625421L, 971524688L,1175081625L, 377165387L};
static const long fuzz2[] = {2001381726L,1615243355L, 191176436L,1212176501L};
#define FUZZ1(x) ((x) ^ fuzz1[(x)&3L])
#define FUZZ2(x) ((x) ^ fuzz2[(x)&3L])

#define SORT_OF_SORT 1
#define SORT_NAME sort1int
#define SORT_TYPE1 int
#include "sorttemplates.c"   /* define sort1int(a,n) */

/*****************************************************************************
*                                                                            *
*  setinter(set1,set2,m) = the number of elements in the intersection of     *
*  the sets set1 and set2.                                                   *
*                                                                            *
*****************************************************************************/

int
setinter(set *set1, set *set2, int m)
{
    setword x;
    int count,i;

    count = 0;
    for (i = m; --i >= 0;)
    {
        if ((x = (*set1 & *set2)) != 0) count += POPCOUNT(x);
        ++set1;
        ++set2;
    }

    return count;
}

/*****************************************************************************
*                                                                            *
*  setsize(set1,m) = the number of elements in the set set1.                 *
*                                                                            *
*****************************************************************************/

int
setsize(set *set1, int m)
{
    int count,i;

    if (m == 1) return POPCOUNT(*set1);

    count = 0;
    for (i = m; --i >= 0;) count += POPCOUNT(set1[i]);

    return count;
}

/*****************************************************************************
*                                                                            *
*  setolist(set1,m,list)   Puts all the elements of the set into             *
*  list[0...] and returns the number of elements as function value.          *
*                                                                            *
*****************************************************************************/

int
settolist(set *set1, int m, int *list)
{
    int i,j,k,v;
    setword w;

    k = 0;
    for (i = 0, v = 0; i < m; ++i, v += WORDSIZE)
    {
        w = set1[i];
        while (w)
        {
            TAKEBIT(j,w);
            list[k++] = v + j;
        }
    }

    return k;
}

/*****************************************************************************
*                                                                            *
*  listtoset(list,len,set1,m)  Sets set1[0..m-1] to the set {list[0..len-1]} *
*                                                                            *
*****************************************************************************/

void
listtoset(int *list, int len, set *set1, int m)
{
    int i;
    setword w;

    if (m == 1)
    {
        w = 0;
        for (i = 0; i < len; ++i) w |= bit[list[i]];
        *set1 = w;
    }
    else
    {
        EMPTYSET0(set1,m);
        for (i = 0; i < len; ++i) ADDELEMENT0(set1,list[i]);
    }
}

/*****************************************************************************
*                                                                            *
*  flushline(f) reads from f until '\n' or EOF.                              *
*  If non-trivial characters are skipped, the user is informed.              *
*                                                                            *
*****************************************************************************/

void
flushline(FILE *f)
{
    boolean msg;
    int c;

    msg = FALSE;

    while ((c = getc(f)) != EOF && c != '\n')
        if (msg)
            PUTC((char)c,ERRFILE);
        else if (c != ' ' && c != '\t' && c != '\f' &&
                                  c != '\r' && c != ',')
        {
            msg = TRUE;
            fprintf(ERRFILE,"input skipped : '%c",(char)c);
        }
    if (msg) fprintf(ERRFILE,"'\n\n");
}

/*****************************************************************************
*                                                                            *
*  readinteger(f,&i) reads an optionally-signed integer from f, preceded by  *
*  any amount of white space.  The function value returned is TRUE if an     *
*  integer was found, FALSE otherwise.                                       *
*                                                                            *
*****************************************************************************/

boolean
readinteger(FILE *f, int *p)
{
    int c,ans,minus;

    GETNWL(c,f);
    if (!ISDIGIT(c) && c != '-' && c != '+')
    {
        if (c != EOF) ungetc(c,f);
        return FALSE;
    }

    minus = c == '-';
    ans = (c == '-' || c == '+' ? 0 : c - '0');

    c = getc(f);
    while (ISDIGIT(c))
    {
        ans *= 10;
        ans += c - '0';
        c = getc(f);
    }

    if (c != EOF) ungetc(c,f);

    *p = (minus ? -ans : ans);
    return TRUE;
}

/*****************************************************************************
*                                                                            *
*  readinteger_sl(f,&i) reads an optionally-signed integer from f, preceded  *
*  by any amount of white space except newlines.  The function value         *
*  returned is TRUE if an integer was found, FALSE otherwise.                *
*                                                                            *
*****************************************************************************/

boolean
readinteger_sl(FILE *f, int *p)
{
    int c,ans,minus;

    GETNW(c,f);
    if (!ISDIGIT(c) && c != '-' && c != '+')
    {
        if (c != EOF) ungetc(c,f);
        return FALSE;
    }

    minus = c == '-';
    ans = (c == '-' || c == '+' ? 0 : c - '0');

    c = getc(f);
    while (ISDIGIT(c))
    {
        ans *= 10;
        ans += c - '0';
        c = getc(f);
    }

    if (c != EOF) ungetc(c,f);

    *p = (minus ? -ans : ans);
    return TRUE;
}

/*****************************************************************************
*                                                                            *
*  readstring(f,s,slen) reads a string from f. First any amount of white     *
*  space is skipped (including newlines).  If the next character is a        *
*  double-quote, everything after that before the next double-quote or       *
*  newline is put into s.  If the next character is not a double-quote,      *
*  everything before the next white space is put into s.  A nul is added,    *
*  but no more than slen characters are ever put into s.  The function       *
*  value is TRUE if a string was found and FALSE otherwise.                  *
*                                                                            *
*****************************************************************************/

boolean
readstring(FILE *f, char *s, int slen)
{
    int c;
    char *slim;

    slim = s + slen - 1;
    GETNWL(c,f);
    if (c == EOF)
    {
        *s = '\0';
        return FALSE;
    }

    if (c == '"')
    {
        c = getc(f);
        while (c != '"' && c != '\n' && c != '\r' && c != EOF)
        {
            if (s <= slim) *s++ = (char)c;
            c = getc(f);
        }
        if (c != '"' && c != EOF) ungetc(c,f);
    }
    else
    {
        if (s <= slim) *s++ = (char)c;
        c = getc(f);
        while (c != ' ' && c != '\n' && c != '\t' && c != '\r' && c != EOF)
        {
            if (s <= slim) *s++ = (char)c;
            c = getc(f);
        }
        if (c != EOF) ungetc(c,f);
    }
    if (s <= slim) *s = '\0';
    else           *slim = '\0';

    return TRUE;
}

/*****************************************************************************
*                                                                            *
*  getint(f) reads an integer from f, optionally preceded by '='             *
*  and white space.  -1 is returned if the attempt was unsuccessful.         *
*                                                                            *
*****************************************************************************/

int
getint(FILE *f)
{
    int i,c;

    GETNWL(c,f);
    if (c != '=') ungetc(c,f);

    if (readinteger(f,&i)) return i;
    else                   return -1;
}

/*****************************************************************************
*                                                                            *
* getint_sl(f) reads an integer from f, optionally preceded by '=' and       *
* white space other than newlines.  -1 is returned if the attempt was        *
* unsuccessful.                                                              *
*****************************************************************************/

int
getint_sl(FILE *f)
{
    int i,c;

    GETNW(c,f);
    if (c != '=') ungetc(c,f);

    if (readinteger_sl(f,&i)) return i;
    else                      return -1;
}

/*****************************************************************************
*                                                                            *
*  putset(f,set1,curlenp,linelength,m,compress)   writes the set set1 to     *
*  file f using at most linelength characters per line (excluding '\n').     *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*  *curlenp is the number of characters on the line so far; it is updated.   *
*  A range j1,j1+1,...,j2 for j2-j1>=2 is written as "j1:j2" if compress     *
*  is nonzero (eg. TRUE); otherwise each element is written separately.      *
*  No final '\n' is written.  labelorg is used.                              *
*                                                                            *
*  FUNCTIONS CALLED: nextelement(),itos()                                    *
*                                                                            *
*****************************************************************************/

void
putset(FILE *f, set *set1, int *curlenp, int linelength,
       int m, boolean compress)
{
    int slen,j1,j2;
    char s[40];

    j1 = -1;
    while ((j1 = nextelement(set1,m,j1)) >= 0)
    {
        j2 = j1;
        if (compress)
        {
            while (nextelement(set1,m,j2) == j2 + 1) ++j2;
            if (j2 == j1+1) j2 = j1;
        }
        slen = itos(j1+labelorg,s);
        if (j2 >= j1 + 2)
        {
            s[slen] = ':';
            slen += 1 + itos(j2+labelorg,&s[slen+1]);
        }

        if (linelength > 0 && *curlenp + slen + 1 >= linelength)
        {
            fprintf(f,"\n   ");
            *curlenp = 3;
        }
        fprintf(f," %s",s);
        *curlenp += slen + 1;
        j1 = j2;
    }
}

/*****************************************************************************
*                                                                            *
*  putset_firstbold(f,set1,curlenp,linelength,m,compress) is the same as     *
*  putset(f,set1,curlenp,linelength,m,compress) except that the first        *
*  element of the set is written bold.  This is only useful when output is   *
*  to a device that interprets ANSI control sequences.                       *
*                                                                            *
*  FUNCTIONS CALLED: nextelement(),itos()                                    *
*                                                                            *
*****************************************************************************/

void
putset_firstbold(FILE *f, set *set1, int *curlenp, int linelength,
       int m, boolean compress)
{
    int slen,slen1,j1,j2;
    char s[50],c;
    boolean bold;

    bold = TRUE;
    j1 = -1;
    while ((j1 = nextelement(set1,m,j1)) >= 0)
    {
        j2 = j1;
        if (compress)
        {
            while (nextelement(set1,m,j2) == j2 + 1) ++j2;
            if (j2 == j1+1) j2 = j1;
        }
        slen1 = slen = itos(j1+labelorg,s);
        if (j2 >= j1 + 2)
        {
            s[slen] = ':';
            slen += 1 + itos(j2+labelorg,&s[slen+1]);
        }
        c = s[slen1];

        if (linelength > 0 && *curlenp + slen + 1 >= linelength)
        {
            fprintf(f,"\n   ");
            *curlenp = 3;
        }
        if (bold)
        {
            s[slen1] = '\0';
            fprintf(f," \033[1m%s\033[0m",s);
            s[slen1] = c;
            fprintf(f,"%s",&s[slen1]);
            bold = FALSE;
        }
        else
            fprintf(f," %s",s);

        *curlenp += slen + 1;
        j1 = j2;
    }
}

/*****************************************************************************
*                                                                            *
*  readgraph(f,g,digraph,prompt,edit,linelength,m,n) reads a graph g from f. *
*  Commands: (There is always a "current vertex" v, initially labelorg;      *
*             n is an unsigned integer.)                                     *
*  n  : add edge (v,n)                                                       *
*  -n : delete edge (v,n)                                                    *
*  n: : set v := n, and exit if v >= n.                                      *
*  ?  : type neighbours of vertex v                                          *
*  ;  : increment v, and exit if v >= n.                                     *
*  .  : exit                                                                 *
*  !  : skip rest of input line                                              *
*                                                                            *
* If digraph==FALSE, loops are illegal and (x,y) => (y,x)                    *
* If edit==FALSE, the graph is initialized to empty.                         *
* If prompt==TRUE, prompts are written to PROMPTFILE.                        *
* linelength is a limit on the number of characters per line caused by '?'   *
* A value of linelength <= 0 dictates no line breaks at all.                *
* labelorg is used.                                                          *
*                                                                            *
* FUNCTIONS CALLED : putset()                                                *
*                                                                            *
*****************************************************************************/

void
readgraph(FILE *f, graph *g, boolean digraph, boolean prompt,
      boolean edit, int linelength, int m, int n)
{
    int v,c;
    int curlen,w;
    graph *gv;
    boolean neg;

    if (!edit)
        for (v = 0, gv = g; v < n; ++v, gv += M) EMPTYSET(gv,m);

    v = 0;
    gv = g;
    neg = FALSE;

    while (TRUE)
    {
        GETNWC(c,f);
        if (ISDIGIT(c))
        {
            ungetc(c,f);
            readinteger(f,&w);
            w -= labelorg;
            if (neg)
            {
                neg = FALSE;
                if (w < 0 || w >= n || (!digraph && w == v))
                    fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n",
                            v+labelorg,w+labelorg);
                else
                {
                    DELELEMENT(gv,w);
                    if (!digraph) DELELEMENT(GRAPHROW(g,w,M),v);
                }
            }
            else
            {
                GETNWC(c,f);
                if (c == ':')
                    if (w < 0 || w >= n)
                        fprintf(ERRFILE,
                                "illegal vertex number %d ignored\n\n",
                                w+labelorg);
                    else
                    {
                        v = w;
                        gv = GRAPHROW(g,v,M);
                    }
                else
                {
                    ungetc(c,f);
                    if (w < 0 || w >= n || (!digraph && w == v))
                        fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n",
                                v+labelorg,w+labelorg);
                    else
                    {
                        ADDELEMENT(gv,w);
                        if (!digraph) ADDELEMENT(GRAPHROW(g,w,M),v);
                    }
                }
            }
        }
        else switch(c)
        {
            case ';':
                neg = FALSE;
                ++v;
                if (v >= n) return;
                gv = GRAPHROW(g,v,M);
                break;
            case '?':
                neg = FALSE;
                fprintf(PROMPTFILE,"%2d : ",v+labelorg);
                curlen = 5;
                putset(PROMPTFILE,gv,&curlen,linelength,M,FALSE);
                fprintf(PROMPTFILE,";\n");
                break;
            case '\n':
                neg = FALSE;
                if (prompt) fprintf(PROMPTFILE,"%2d : ",v+labelorg);
                break;
            case EOF:
            case '.':
                return;
            case '-':
                neg = TRUE;
                break;
            case '!':
                do
                    c = getc(f);
                while (c != '\n' && c != EOF);
                if (c == '\n') ungetc(c,f);
                break;
            default :
                fprintf(ERRFILE,"illegal char '%c' - use '.' to exit\n\n",
                        (char)c);
        }
    }
}

/**************************************************************************/

void
ranreg_sg(sparsegraph *sg, int degree, int n)
/* Make a random regular simple undirected graph.
 * For MAXN!=0, the maximum degree is MAXREG.
 * sg must be initialised
 */
{
    long i,k,v,w;
    boolean ok;
    int *dd,*ee;
    size_t *vv,nde,j;
 
#if MAXN
    int p[MAXREG*MAXN];
#else
    DYNALLSTAT(int,p,p_sz);

    DYNALLOC2(int,p,p_sz,degree,n,"genrang");
#endif

    nde = (size_t)n * (size_t)degree;

    SG_ALLOC(*sg,n,nde,"ranreg_sg");
    SG_VDE(sg,vv,dd,ee);
    DYNFREE(sg->w,sg->wlen);

    sg->nv = n;
    sg->nde = nde;

    for (i = j = 0; i < n; ++i)
        for (k = 0; k < degree; ++k) p[j++] = i;

    for (i = 0; i < n; ++i) vv[i] = i*(size_t)degree;

    do
    {   
        ok = TRUE;

        for (j = nde; j > 0; j -= 2)
        {   
            i = KRAN(j-1);
            k = p[i];
            if (k == p[j-1]) break;
            p[i] = p[j-2]; p[j-2] = k;
        }
        if (j > 0) { ok = FALSE; continue; }

        for (i = 0; i < n; ++i) dd[i] = 0;

        for (j = nde; j > 0; )
        {   
            v = p[--j];
            w = p[--j];
            if (v != w)
            {
                for (i = dd[w]; --i >= 0;) if (ee[vv[w]+i] == v) break;
                if (i >= 0) { ok = FALSE; break; }
            }
            ee[vv[w]+(dd[w]++)] = v;
            ee[vv[v]+(dd[v]++)] = w;
        }
    }
    while (!ok);
}

/*****************************************************************************
*                                                                            *
*  readgraph_sg(f,sg,digraph,prompt,linelength,n) reads a graph g from f.    *
*  Commands: (There is always a "current vertex" v, initially labelorg;      *
*             n is an unsigned integer.)                                     *
*  n  : add edge (v,n)                                                       *
*  -n : delete edge (v,n)                                                    *
*  n: : set v := n, and exit if v >= n.                                      *
*  ?  : type neighbours of vertex v     ** NOT IMPLEMENTED **                *
*  ;  : increment v, and exit if v >= n.                                     *
*  .  : exit                                                                 *
*  !  : skip rest of input line                                              *
*  sg must be initialised                                                    *
*                                                                            *
* If digraph==FALSE, loops are illegal and (x,y) => (y,x)                    *
* If prompt==TRUE, prompts are written to PROMPTFILE.                        *
* linelength is a limit on the number of characters per line caused by '?'   *
* A value of linelength <= 0 dictates no line breaks at all.                 *
* labelorg is used.                                                          *
*                                                                            *
*****************************************************************************/

void
readgraph_sg(FILE *f, sparsegraph *sg, boolean digraph, boolean prompt,
             int linelength, int n)
{
    int i,j,k,vv,ww,c;
    boolean neg,done;
    int *d,*e,*evi;
    echunk *ec,*ecnext,*ec_end;
    size_t ned,*v,iec,iec_end;

    sg->nv = n;
    DYNALLOC1(size_t,sg->v,sg->vlen,n,"malloc");
    DYNALLOC1(int,sg->d,sg->dlen,n,"malloc");
    DYNFREE(sg->w,sg->wlen);
    v = sg->v;
    d = sg->d;

    for (i = 0; i < n; ++i) d[i] = 0;

    ec = &first_echunk;
    iec = 0;
    vv = 0;
    neg = done = FALSE;

    while (!done)
    {
        GETNWC(c,f);
        if (ISDIGIT(c))
        {
            ungetc(c,f);
            readinteger(f,&ww);
            ww -= labelorg;
            if (neg)
            {
                neg = FALSE;
                if (ww < 0 || ww >= n || (!digraph && ww == vv))
                    fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n",
                            vv+labelorg,ww+labelorg);
                else
                {
                    if (iec == ECHUNKSIZE)
                    {
                        if (!ec->next)
                        {
                            ecnext = (echunk*)ALLOCS(1,sizeof(echunk));
                            if (!ecnext) alloc_error("malloc");
                            ecnext->next = NULL;
                            ec->next = ecnext;
                        }
                        ec = ec->next;
                        iec = 0;
                    }
                    ec->edge[iec++] = vv;
                    ec->edge[iec++] = -1 - ww;
                    ++d[vv];
                    if (!digraph && ww != vv) ++d[ww];
                }
            }
            else
            {
                GETNWC(c,f);
                if (c == ':')
                {
                    if (ww < 0 || ww >= n)
                        fprintf(ERRFILE,
                                "illegal vertex number %d ignored\n\n",
                                ww+labelorg);
                    else
                        vv = ww;
                }
                else
                {
                    ungetc(c,f);
                    if (ww < 0 || ww >= n || (!digraph && ww == vv))
                        fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n",
                                vv+labelorg,ww+labelorg);
                    else
                    {
                        if (iec == ECHUNKSIZE)
                        {
                            if (!ec->next)
                            {
                                ecnext = (echunk*)ALLOCS(1,sizeof(echunk));
                                if (!ecnext) alloc_error("malloc");
                                ecnext->next = NULL;
                                ec->next = ecnext;
                            }
                            ec = ec->next;
                            iec = 0;
                        }
                        ec->edge[iec++] = vv;
                        ec->edge[iec++] = ww;
                        ++d[vv];
                        if (!digraph && ww != vv) ++d[ww];
                    }
                }
            }
        }
        else switch(c)
        {
            case ';':
                neg = FALSE;
                ++vv;
                if (vv >= n) done = TRUE;
                break;
            case '?':
                neg = FALSE;
                fprintf(ERRFILE,"Command \'?\' not implemented.\n\n");
                break;
            case '\n':
                neg = FALSE;
                if (prompt) fprintf(PROMPTFILE,"%2d : ",vv+labelorg);
                break;
            case EOF:
            case '.':
                done = TRUE;
                break;
            case '-':
                neg = TRUE;
                break;
            case '!':
                do
                    c = getc(f);
                while (c != '\n' && c != EOF);
                if (c == '\n') ungetc(c,f);
                break;
            default :
                fprintf(ERRFILE,"illegal char '%c' - use '.' to exit\n\n",
                        (char)c);
        }
    }

    ned = 0;
    for (i = 0; i < n; ++i) ned += d[i];
    DYNALLOC1(int,sg->e,sg->elen,ned,"malloc");
    e = sg->e;

    v[0] = 0;
    for (i = 1; i < n; ++i) v[i] = v[i-1] + d[i-1];
    for (i = 0; i < n; ++i) d[i] = 0;

    iec_end = iec;
    ec_end = ec;

    iec = 0;
    ec = &first_echunk;

    if (ned != 0) while (TRUE)
    {
        vv = ec->edge[iec++];
        ww = ec->edge[iec++];

        if (ww >= 0)
        {
            e[v[vv]+(d[vv]++)] = ww;
            if (!digraph && ww != vv) e[v[ww] +(d[ww]++)] = vv;
        }
        else
        {
            ww = -1 - ww;
            for (i = 0; i < d[vv]; ++i)
                if (e[v[vv]+i] == ww) break;
            if (i < d[vv])
            {
                e[v[vv]+i] = e[v[vv]+d[vv]-1];
                --d[vv];
            }
            if (!digraph && ww != vv)
            {
                for (i = 0; i < d[ww]; ++i)
                    if (e[v[ww]+i] == vv) break;
                if (i < d[ww])
                {
                    e[v[ww]+i] = e[v[ww]+d[ww]-1];
                    --d[ww];
                }
            }
        }
        if (iec == iec_end && ec == ec_end) break;
        if (iec == ECHUNKSIZE) { iec = 0; ec = ec->next; }
    }

    sortlists_sg(sg);

    ned = 0;
    for (i = 0; i < n; ++i)
    {
        if (d[i] > 1)
        {
            evi = e + v[i];
            j = 1;
            for (k = 1; k < d[i]; ++k)
                if (evi[k] != evi[j-1]) evi[j++] = evi[k];
            d[i] = j;
        }
        ned += d[i];
    }
    sg->nde = ned; 
}

/*****************************************************************************
*                                                                            *
*  readgraph_swg(f,sg,digraph,prompt,linelength,n) reads a sparse weighted   *
*  graph g from f.                                                           *
*  Commands: (There is always a "current vertex" v, initially labelorg;      *
*             n is an unsigned integer, w is a weight.)                      *
*  n  : add edge (v,n)                                                       *
*  -n : delete edge (v,n)                                                    *
*  n: : set v := n, and exit if v >= n.                                      *
*  ?  : type neighbours of vertex v     ** NOT IMPLEMENTED **                *
*  ;  : increment v, and exit if v >= n.                                     *
*  .  : exit                                                                 *
*  !  : skip rest of input line                                              *
*  w# : set the weight for the next edge only                                *
*  W# : set the weight from now on                                           *
*  sg must be initialised                                                    *
*                                                                            *
* If digraph==FALSE, loops are illegal and (x,y) => (y,x)                    *
* For digraphs, an unspecified opposite edge has weight SG_MINWEIGHT         *
* If edges are inserted more than once, the largest weight counts.           *
* If prompt==TRUE, prompts are written to PROMPTFILE.                        *
* linelength is a limit on the number of characters per line caused by '?'   *
* A value of linelength <= 0 dictates no line breaks at all.                 *
* labelorg is used.                                                          *
*                                                                            *
*****************************************************************************/

void
readgraph_swg(FILE *f, sparsegraph *sg, boolean digraph, boolean prompt,
             int linelength, int n)
{
    int i,j,k,vv,ww,c;
    boolean neg,done;
    int *d,*e,*evi;
    echunkw *ec,*ecnext,*ec_end;
    size_t ned,*v,iec,iec_end;
    sg_weight *wt,currwt,defwt,*wvi;

    sg->nv = n;
    DYNALLOC1(size_t,sg->v,sg->vlen,n,"malloc");
    DYNALLOC1(int,sg->d,sg->dlen,n,"malloc");
    v = sg->v;
    d = sg->d;
    wt = sg->w;

    for (i = 0; i < n; ++i) d[i] = 0;

    defwt = currwt = 1; 
    ec = &first_echunkw;
    iec = 0;
    vv = 0;
    neg = done = FALSE;

    while (!done)
    {
        GETNWC(c,f);
        if (ISDIGIT(c))
        {
            ungetc(c,f);
            readinteger(f,&ww);
            ww -= labelorg;
            if (neg)
            {
                neg = FALSE;
                if (ww < 0 || ww >= n || (!digraph && ww == vv))
                    fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n",
                            vv+labelorg,ww+labelorg);
                else
                {
                    if (iec == ECHUNKSIZE)
                    {
                        if (!ec->next)
                        {
                            ecnext = (echunkw*)ALLOCS(1,sizeof(echunkw));
                            if (!ecnext) alloc_error("malloc");
                            ecnext->next = NULL;
                            ec->next = ecnext;
                        }
                        ec = ec->next;
                        iec = 0;
                    }
                    ec->edge[iec].v1 = vv;
                    ec->edge[iec].v2 = -1 - ww;
                    ec->edge[iec].wt = currwt;
                    ++iec;
                    currwt = defwt;
                    ++d[vv];
                    if (ww != vv) ++d[ww];
                }
            }
            else
            {
                GETNWC(c,f);
                if (c == ':')
                {
                    if (ww < 0 || ww >= n)
                        fprintf(ERRFILE,
                                "illegal vertex number %d ignored\n\n",
                                ww+labelorg);
                    else
                        vv = ww;
                }
                else
                {
                    ungetc(c,f);
                    if (ww < 0 || ww >= n || (!digraph && ww == vv))
                        fprintf(ERRFILE,"illegal edge (%d,%d) ignored\n\n",
                                vv+labelorg,ww+labelorg);
                    else
                    {
                        if (iec == ECHUNKSIZE)
                        {
                            if (!ec->next)
                            {
                                ecnext = (echunkw*)ALLOCS(1,sizeof(echunkw));
                                if (!ecnext) alloc_error("malloc");
                                ecnext->next = NULL;
                                ec->next = ecnext;
                            }
                            ec = ec->next;
                            iec = 0;
                        }
                        ec->edge[iec].v1 = vv;
                        ec->edge[iec].v2 = ww;
                        ec->edge[iec].wt = currwt;
                        ++iec;
                        currwt = defwt;
                        ++d[vv];
                        if (ww != vv) ++d[ww];
                    }
                }
            }
        }
        else switch(c)
        {
            case ';':
                neg = FALSE;
                ++vv;
                if (vv >= n) done = TRUE;
                break;
            case '?':
                neg = FALSE;
                fprintf(ERRFILE,"Command \'?\' not implemented.\n\n");
                break;
            case '\n':
                neg = FALSE;
                if (prompt) fprintf(PROMPTFILE,"%2d : ",vv+labelorg);
                break;
            case EOF:
            case '.':
                done = TRUE;
                break;
            case '-':
                neg = TRUE;
                break;
            case 'w':
                readinteger(f,&currwt);
                if (currwt <= SG_MINWEIGHT)
                {
                    fprintf(ERRFILE,"Weight too small\n\n");
                    currwt = 1;
                }
                break;
            case 'W':
                readinteger(f,&currwt);
                if (currwt <= SG_MINWEIGHT)
                {
                    fprintf(ERRFILE,"Weight too small\n\n");
                    currwt = 1;
                }
                defwt = currwt;
                break;
            case '!':
                do
                    c = getc(f);
                while (c != '\n' && c != EOF);
                if (c == '\n') ungetc(c,f);
                break;
            default :
                fprintf(ERRFILE,"illegal char '%c' - use '.' to exit\n\n",
                        (char)c);
        }
    }

    ned = 0;
    for (i = 0; i < n; ++i) ned += d[i];
    DYNALLOC1(int,sg->e,sg->elen,ned,"malloc");
    DYNALLOC1(sg_weight,sg->w,sg->wlen,ned,"malloc");
    e = sg->e;
    wt = sg->w;

    v[0] = 0;
    for (i = 1; i < n; ++i) v[i] = v[i-1] + d[i-1];
    for (i = 0; i < n; ++i) d[i] = 0;

    iec_end = iec;
    ec_end = ec;

    iec = 0;
    ec = &first_echunkw;

    if (ned != 0) while (TRUE)
    {
        vv = ec->edge[iec].v1;
        ww = ec->edge[iec].v2;
        currwt = ec->edge[iec].wt;
        ++iec;

        if (ww >= 0)
        {
            e[v[vv]+d[vv]] = ww;
            wt[v[vv]+d[vv]] = currwt;
            ++d[vv];
            if (ww != vv)
            {
                e[v[ww]+d[ww]] = vv;
                wt[v[ww]+d[ww]] = (digraph ? SG_MINWEIGHT : currwt);
                ++d[ww];
            }
        }
        else
        {
            ww = -1 - ww;
            for (i = 0; i < d[vv]; ++i)
                if (e[v[vv]+i] == ww) break;
            if (i < d[vv])
            {
                e[v[vv]+i] = e[v[vv]+d[vv]-1];
                wt[v[vv]+i] = wt[v[vv]+d[vv]-1];
                --d[vv];
            }
            if (ww != vv)
            {
                for (i = 0; i < d[ww]; ++i)
                    if (e[v[ww]+i] == vv) break;
                if (i < d[ww])
                {
                    e[v[ww]+i] = e[v[ww]+d[ww]-1];
                    wt[v[ww]+i] = wt[v[ww]+d[ww]-1];
                    --d[ww];
                }
            }
        }
        if (iec == iec_end && ec == ec_end) break;
        if (iec == ECHUNKSIZE) { iec = 0; ec = ec->next; }
    }

    sortlists_sg(sg);

    ned = 0;
    for (i = 0; i < n; ++i)
    {
        if (d[i] > 1)
        {
            evi = e + v[i];
            wvi = wt + v[i];
            j = 1;
            for (k = 1; k < d[i]; ++k)
            {
                if (evi[k] != evi[j-1])
                {
                    evi[j] = evi[k];
                    wvi[j] = wvi[k];
                    ++j;
                }
                else if (wvi[k] > wvi[j-1])
                    wvi[j-1] = wvi[k];
            }
            d[i] = j;
        }
        ned += d[i];
    }
    sg->nde = ned; 
}

/*****************************************************************************
*                                                                            *
*  putgraph(f,g,linelength,m,n) writes a list of the edges of g to f         *
*  using at most linelength characters per line (excluding '\n').            *
*  A value of linelength <= 0 dictates no line breaks at all within the      *
*    list for each vertex.                                                   *
*  labelorg is used.                                                         *
*                                                                            *
*  FUNCTIONS CALLED: putset()                                                *
*                                                                            *
*****************************************************************************/

void
putgraph(FILE *f, graph *g, int linelength, int m, int n)
{
    int i,curlen;
    set *pg;

    for (i = 0, pg = g; i < n; ++i, pg += M)
    {
        fprintf(f,"%3d : ",i+labelorg);
        curlen = 7;
        putset(f,pg,&curlen,linelength,M,FALSE);
        fprintf(f,";\n");
    }
}

/*****************************************************************************
*                                                                            *
*  putgraph_sg(f,sg,linelength) writes a list of the edges of g to f         *
*  using at most linelength characters per line (excluding '\n').            *
*  A value of linelength <= 0 dictates no line breaks at all within the      *
*    list for each vertex.                                                   *
*  labelorg is used.                                                         *
*                                                                            *
*****************************************************************************/

void
putgraph_sg(FILE *f, sparsegraph *sg, int linelength)
{
    int i,n,curlen,slen;
    int *d,*e;
    size_t *v,j;
    sg_weight *wt;
    char s[60];

    n = sg->nv;
    SWG_VDE(sg,v,d,e,wt);

    for (i = 0; i < n; ++i)
    {
        fprintf(f,"%3d : ",i+labelorg);
        curlen = 7;

        for (j = v[i]; j < v[i]+d[i]; ++j)
        {
            if (wt && wt[j] != 1)
            {
                s[0] = 'w';
                if (wt[j] == SG_MINWEIGHT)
                {
                    s[1] = 'X';
                    s[2] = ' ';
                    slen = 3;
                }
                else
                {
                    slen = 2 + itos(wt[j],s+1);
                    s[slen-1] = ' ';
                }
                slen += itos(e[j]+labelorg,s+slen);
            }
            else
                slen = itos(e[j]+labelorg,s);

            if (linelength > 0 && curlen + slen + 1 > linelength)
            {
                putstring(f,"\n  ");
                curlen = 2;
            }
            PUTC(' ',f);
            putstring(f,s);
            curlen += slen + 1;
        }
        putstring(f,";\n");
    }
}

/*****************************************************************************
*                                                                            *
*  putmapping(f,lab1,org1,lab2,org2,linelength,n) writes n pairs             *
*  (lab1[i]+org1)-(lab2[i]+org2) to file f in increasing order of lab1[i].   *
*  lab1 and lab2 are assumed to be permutations.  At most linelength         *
*  characters (excluding '\n') are written per line.                         *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*                                                                            *
*  FUNCTIONS CALLED: itos(),putstring()                                      *
*                                                                            *
*****************************************************************************/

void
putmapping(FILE *f, int *lab1, int org1,int *lab2, int org2,
       int linelength, int n)
{
    int i,curlen,slen;
    char s[60];

#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"putmapping");
#endif

    for (i = 0; i < n; ++i) workperm[lab1[i]] = lab2[i];

    curlen = 0;
    for (i = 0; i < n; ++i)
    {
        slen = itos(i+org1,s);
        s[slen++] = '-';
        slen += itos(workperm[i]+org2,&s[slen]);
        if (linelength > 0 && curlen + slen + 1 > linelength)
        {
            putstring(f,"\n  ");
            curlen = 2;
        }
        PUTC(' ',f);
        putstring(f,s);
        curlen += slen + 1;
    }
    PUTC('\n',f);
}

/*****************************************************************************
*                                                                            *
*  putorbits(f,orbits,linelength,n) writes the orbits to f as lists          *
*  of integers separated by semicolons.  No more than linelength characters  *
*  (excluding '\n') are written per line.                                    *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*  labelorg is used.                                                         *
*                                                                            *
*  FUNCTIONS CALLED: itos(),putset()                                         *
*                                                                            *
*****************************************************************************/

void
putorbits(FILE *f, int *orbits, int linelength, int n)
{
    int i,j;
    int m,curlen,sz,slen;
    char s[20];

    m = SETWORDSNEEDED(n);
#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"putorbits");
    DYNALLOC1(set,workset,workset_sz,m,"putorbits");
#endif

    for (i = n; --i >= 0;) workperm[i] = 0;
    for (i = n; --i >= 0;)
        if ((j = orbits[i]) < i)
        {
            workperm[i] = workperm[j];
            workperm[j] = i;
        }

    curlen = 0;
    for (i = 0; i < n; ++i)
        if (orbits[i] == i)
        {
            sz = 0;
            EMPTYSET(workset,m);
            j = i;
            do
            {
                ADDELEMENT(workset,j);
                j = workperm[j];
                ++sz;
            }
            while (j > 0);
            putset(f,workset,&curlen,linelength-1,m,TRUE);
            if (sz > 1)
            {
                s[0] = ' ';
                s[1] = '(';
                slen = 2 + itos(sz,s+2);
                s[slen++] = ')';
                s[slen] = '\0';
                if (linelength > 0 && curlen + slen + 1 >= linelength)
                {
                    fprintf(f,"\n   ");
                    curlen = 3;
                }
                fprintf(f,"%s",s);
                curlen += slen;
            }

            PUTC(';',f);
            ++curlen;
        }
    PUTC('\n',f);
}

/*****************************************************************************
*                                                                            *
*  putorbitsplus(f,orbits,linelength,n) is the same as                       *
*  putorbits(f,orbits,linelength,n) except that the first element of each    *
*  orbit is written bold.  This only works if output is to a device that     *
*  interprets ANSI controls.                                                 *
*                                                                            *
*  FUNCTIONS CALLED: itos(),putset()                                         *
*                                                                            *
*****************************************************************************/

void
putorbitsplus(FILE *f, int *orbits, int linelength, int n)
{
    int i,j;
    int m,curlen,sz,slen;
    char s[20];

    m = SETWORDSNEEDED(n);
#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"putorbits");
    DYNALLOC1(set,workset,workset_sz,m,"putorbits");
#endif

    for (i = n; --i >= 0;) workperm[i] = 0;
    for (i = n; --i >= 0;)
        if ((j = orbits[i]) < i)
        {
            workperm[i] = workperm[j];
            workperm[j] = i;
        }

    curlen = 0;
    for (i = 0; i < n; ++i)
        if (orbits[i] == i)
        {
            sz = 0;
            EMPTYSET(workset,m);
            j = i;
            do
            {
                ADDELEMENT(workset,j);
                j = workperm[j];
                ++sz;
            }
            while (j > 0);
            putset_firstbold(f,workset,&curlen,linelength-1,m,TRUE);
            if (sz > 1)
            {
                s[0] = ' ';
                s[1] = '(';
                slen = 2 + itos(sz,s+2);
                s[slen++] = ')';
                s[slen] = '\0';
                if (linelength > 0 && curlen + slen + 1 >= linelength)
                {
                    fprintf(f,"\n   ");
                    curlen = 3;
                }
                fprintf(f,"%s",s);
                curlen += slen;
            }

            PUTC(';',f);
            ++curlen;
        }
    PUTC('\n',f);
}

/*****************************************************************************
*                                                                            *
*  putquotient(f,g,lab,ptn,level,linelength,m,n) writes the quotient matrix  *
*  of g defined by the partition at the given level.  Precisely, for each    *
*  cell W, it writes the number w of the least vertex in W, then the size    *
*  of W, then the number of times w is joined FROM each cell.  A complete    *
*  join is written as "*", while an empty join is written as "-".  No more   *
*  than linelength  characters (excluding '\n') are written per line unless  *
*  linelength is very small.  A value of linelength <= 0 dictates no line    *
*  breaks at all.   labelorg is used.                                        *
*                                                                            *
*  FUNCTIONS CALLED: itos()                                                  *
*                                                                            *
*****************************************************************************/

void
putquotient(FILE *f, graph *g, int *lab, int *ptn, int level,
        int linelength, int m, int n)
{
    int i;
    char s[50];
    int ic,curlen,v,w,cell1,cell2,numcells,jc,csize,k;
    set *gw;

#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"putquotient");
    DYNALLOC1(set,workset,workset_sz,m,"putquotient");
#endif

    numcells = 0;
    for (cell1 = 0; cell1 < n; cell1 = cell2 + 1)
    {
        for (cell2 = cell1; ptn[cell2] > level; ++cell2) {}
        w = lab[cell1];
        for (i = cell1 + 1; i <= cell2; ++i)
            if (lab[i] < w) w = lab[i];
        workperm[numcells++] = w;
    }

    for (ic = cell1 = 0; ic < numcells; ++ic, cell1 = cell2 + 1)
    {
        for (cell2 = cell1; ptn[cell2] > level; ++cell2) {}
        EMPTYSET(workset,M);
        for (i = cell1; i <= cell2; ++i) ADDELEMENT(workset,lab[i]);
        v = workperm[ic];
        csize = cell2 - cell1 + 1;
        if (v + labelorg < 10)
        {
            s[0] = ' ';
            curlen = 1;
        }
        else
            curlen = 0;
        curlen += itos(v+labelorg,&s[curlen]);
        s[curlen++] = '[';
        curlen += itos(csize,&s[curlen]);
        fprintf(f,"%s",s);
        if (csize < 10)
        {
            fprintf(f,"]  :");
            curlen += 4;
        }
        else
        {
            fprintf(f,"] :");
            curlen += 3;
        }

        for (jc = 0; jc < numcells; ++jc)
        {
            w = workperm[jc];
            gw = GRAPHROW(g,w,m);
            k = setinter(gw,workset,M);
            if (k == 0 || k == csize)
            {
                if (linelength > 0 && curlen + 2 > linelength)
                {
                    fprintf(f,"\n    ");
                    curlen = 4;
                }
                if (k == 0) fprintf(f," -");
                else        fprintf(f," *");
                curlen += 2;
            }
            else
            {
                i = itos(k,s);
                if (linelength > 0 && curlen + i + 1 > linelength)
                {
                    fprintf(f,"\n    ");
                    curlen = 4;
                }
                fprintf(f," %s",s);
                curlen += i + 1;
            }
        }
        fprintf(f,"\n");
    }
}

/*****************************************************************************
*                                                                            *
*  putquotient_sg(f,g,lab,ptn,level,linelength) writes the quotient matrix   *
*  of g defined by the partition at the given level.  Precisely, for each    *
*  cell W, it writes the number w of the least vertex in W, then the size    *
*  of W, then the number of times w is joined FROM each cell.  A complete    *
*  join is written as "*", while an empty join is written as "-".  No more   *
*  than linelength  characters (excluding '\n') are written per line unless  *
*  linelength is very small.  A value of linelength <= 0 dictates no line    *
*  breaks at all.   labelorg is used.                                        *
*                                                                            *
*  Weughts are ignored.                                                      *
*                                                                            *
*****************************************************************************/

void
putquotient_sg(FILE *f, sparsegraph *g, int *lab, int *ptn,
                                                int level, int linelength)
{
    int i,m,n;
    char s[50];
    int ic,curlen,v,w,cell1,cell2,numcells,jc,csize,k;
    int *dd,*ee;
    size_t *vv,j;

    n = g->nv;
    m = SETWORDSNEEDED(n);
    SG_VDE(g,vv,dd,ee);

#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"putquotient");
    DYNALLOC1(set,workset,workset_sz,m,"putquotient");
#endif

    numcells = 0;
    for (cell1 = 0; cell1 < n; cell1 = cell2 + 1)
    {
        for (cell2 = cell1; ptn[cell2] > level; ++cell2) {}
        w = lab[cell1];
        for (i = cell1 + 1; i <= cell2; ++i)
            if (lab[i] < w) w = lab[i];
        workperm[numcells++] = w;
    }

    for (ic = cell1 = 0; ic < numcells; ++ic, cell1 = cell2 + 1)
    {
        for (cell2 = cell1; ptn[cell2] > level; ++cell2) {}
        EMPTYSET(workset,M);
        for (i = cell1; i <= cell2; ++i) ADDELEMENT(workset,lab[i]);
        v = workperm[ic];
        csize = cell2 - cell1 + 1;
        if (v + labelorg < 10)
        {
            s[0] = ' ';
            curlen = 1;
        }
        else
            curlen = 0;
        curlen += itos(v+labelorg,&s[curlen]);
        s[curlen++] = '[';
        curlen += itos(csize,&s[curlen]);
        fprintf(f,"%s",s);
        if (csize < 10)
        {
            fprintf(f,"]  :");
            curlen += 4;
        }
        else
        {
            fprintf(f,"] :");
            curlen += 3;
        }

        for (jc = 0; jc < numcells; ++jc)
        {
            w = workperm[jc];
            k = 0;
            for (j = vv[w]; j < vv[w]+dd[w]; ++j)
                if (ISELEMENT(workset,ee[j])) ++k;

            if (k == 0 || k == csize)
            {
                if (linelength > 0 && curlen + 2 > linelength)
                {
                    fprintf(f,"\n    ");
                    curlen = 4;
                }
                if (k == 0) fprintf(f," -");
                else        fprintf(f," *");
                curlen += 2;
            }
            else
            {
                i = itos(k,s);
                if (linelength > 0 && curlen + i + 1 > linelength)
                {
                    fprintf(f,"\n    ");
                    curlen = 4;
                }
                fprintf(f," %s",s);
                curlen += i + 1;
            }
        }
        fprintf(f,"\n");
    }
}

/*****************************************************************************
*                                                                            *
*  putptn(f,lab,ptn,level,linelength,n) writes the partition at the given    *
*  level as sorted lists of integers separated by semicolons.  No more than  *
*  linelength characters (excluding '\n') are written per line.              *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*  labelorg is used.                                                         *
*                                                                            *
*****************************************************************************/

void
putptn(FILE *f, int *lab, int *ptn, int level, int linelength, int n)
{
    int i;
    int curlen,m;

    m = SETWORDSNEEDED(n);
#if !MAXN
    DYNALLOC1(set,workset,workset_sz,m,"putptn");
#endif

    PUTC('[',f);
    curlen = 1;
    i = 0;
    while (i < n)
    {
        EMPTYSET(workset,m);
        while (TRUE)
        {
            ADDELEMENT(workset,lab[i]);
            if (ptn[i] > level) ++i;
            else                break;
        }
        putset(f,workset,&curlen,linelength-2,m,TRUE);
        if (i < n-1)
        {
            fprintf(f," |");
            curlen += 2;
        }
        ++i;
    }
    fprintf(f," ]\n");
}

/*****************************************************************************
*                                                                            *
*  putcanon(f,canonlab,canong,linelength,m,n) writes the label canonlab      *
*  and the graph canong to f, using at most linelength characters            *
*  (excluding '\n') per line.   labelorg is used.                            *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*                                                                            *
*****************************************************************************/

void
putcanon(FILE *f, int *canonlab, graph *canong, int linelength, int m, int n)
{
    int i;

#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"putcanon");
#endif

    for (i = 0; i < n; ++i) workperm[i] = canonlab[i];
    writeperm(f,workperm,TRUE,linelength,n);
    putgraph(f,canong,linelength,m,n);
}

/*****************************************************************************
*                                                                            *
*  putcanon_sg(f,canonlab,canong,linelength) writes the label canonlab       *
*  and the graph canong to f, using at most linelength characters            *
*  (excluding '\n') per line.   labelorg is used.                            *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*                                                                            *
*****************************************************************************/

void
putcanon_sg(FILE *f, int *canonlab, sparsegraph *canong, int linelength)
{
    int i,n;

    n = canong->nv;
#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"putcanon");
#endif

    for (i = 0; i < n; ++i) workperm[i] = canonlab[i];
    writeperm(f,workperm,TRUE,linelength,n);
    putgraph_sg(f,canong,linelength);
}

/*****************************************************************************
*                                                                            *
*  readptn(f,lab,ptn,&numcells,prompt,n) reads a partition from f            *
*  and establishes it in (lab,ptn).                                          *
*  The format can be just a number, which is fixed alone, or an arbitrary    *
*  partition [...|...|...].  Ranges x:y can be used.                         *
*  labelorg is used.                                                         *
*                                                                            *
*****************************************************************************/

void
readptn(FILE *f, int *lab, int *ptn, int *numcells, boolean prompt, int n)
{
    int i,j;
    int c,v1,v2,m;

    m = SETWORDSNEEDED(n);
#if !MAXN
    DYNALLOC1(set,workset,workset_sz,m,"readptn");
#endif

    GETNW(c,f);
    if (c == '=') GETNW(c,f);
    if (ISDIGIT(c))
    {
        ungetc(c,f);
        readinteger(f,&v1);
        v1 -= labelorg;
        if (v1 >= 0 && v1 < n)
            fixit(lab,ptn,numcells,v1,n);
        else
        {
            fprintf(ERRFILE,"vertex out of range (%d), fixing nothing\n\n",
                    v1+labelorg);
            unitptn(lab,ptn,numcells,n);
        }
        return;
    }
    if (c != '[')
    {
        ungetc(c,f);
        fprintf(ERRFILE,"illegal partition, fixing nothing\n\n");
        unitptn(lab,ptn,numcells,n);
        return;
    }
    EMPTYSET(workset,m);
    *numcells = 0;
    for (i = 0; i < n; ++i) ptn[i] = NAUTY_INFINITY;
    i = 0;
    j = -1;
    while (TRUE)
    {
        GETNWC(c,f);
        if (ISDIGIT(c))
        {
            ungetc(c,f);
            readinteger(f,&v1);
            v1 -= labelorg;
            GETNWC(c,f);
            if (c == ':')
                if (!readinteger(f,&v2))
                {
                    fprintf(ERRFILE,"unfinished range\n\n");
                    v2 = v1;
                }
                else
                    v2 -= labelorg;
            else
            {
                ungetc(c,f);
                v2 = v1;
            }
            while (v1 <= v2)
            {
                if (v1 < 0 || v1 >= n || ISELEMENT(workset,v1))
                    fprintf(ERRFILE,"illegal or repeated number : %d\n\n",
                            v1+labelorg);
                else
                {
                    ADDELEMENT(workset,v1);
                    lab[++j] = v1;
                }
                ++v1;
            }
        }
        else if (c == '|' || c == ']' || c == EOF)
        {
            if (j >= i)
            {
                ++*numcells;
                ptn[j] = 0;
            }
            if (c == '|')
                i = j + 1;
            else if (j == n - 1)
                return;
            else
            {
                i = j + 1;
                ++*numcells;
                for (j = 0; j < n; ++j)
                    if (!ISELEMENT(workset,j)) lab[i++] = j;
                ptn[n-1] = 0;
                return;
            }
        }
        else if (c == '\n')
        {
            if (prompt) fprintf(PROMPTFILE,"] ");
        }
        else
            fprintf(ERRFILE,"illegal character '%c' in partition\n\n",c);
    }
}

/*****************************************************************************
*                                                                            *
*  unitptn(lab,ptn,&numcells,n) establishes the partition with one cell.     *
*                                                                            *
*****************************************************************************/

void
unitptn(int *lab,int *ptn, int *numcells, int n)
{
    int i;

    for (i = 0; i < n; ++i)
    {
        lab[i] = i;
        ptn[i] = NAUTY_INFINITY;
    }
    ptn[n-1] = 0;
    *numcells = 1;
}

/*****************************************************************************
*                                                                            *
*  individualise(lab,ptn,level,v,&pos,&numcells,n) individualises vertex v.  *
*  numcells is updated and the position of the possibly-new singleton is     *
*  returned in pos.                                                          *
*                                                                            *
*****************************************************************************/

void
individualise(int *lab,int *ptn, int level,
                                int v, int *pos, int *numcells, int n)
{
    int i,j;

    for (i = 0; i < n; ++i) if (lab[i] == v) break;

    for (j = i; j > 0 && ptn[j-1] > level; --j) {};

    *pos = j;
    if (ptn[j] <= level) return;   /* individual already */

    lab[i] = lab[j];
    lab[j] = v;
    ptn[j] = level;
    ++*numcells;
}

/*****************************************************************************
*                                                                            *
*  cellstarts(ptn,level,cell,m,n) sets the set cell to contain the indices   *
*  of the starts in ptn of the partition at level level.                     *
*                                                                            *
*****************************************************************************/

void
cellstarts(int *ptn, int level, set *cell, int m, int n)
{
    int i;

    EMPTYSET(cell,m);
    i = 0;
    while (i < n)
    {
        ADDELEMENT(cell,i);
        while (ptn[i] > level) ++i;
        ++i;
    }
}

/*****************************************************************************
*                                                                            *
*  fixit(lab,ptn,&numcells,fixedvertex,n) establishes the partition          *
*  with one cell {fixedvertex} and all the other vertices (if any) in        *
*  another cell.                                                             *
*                                                                            *
*****************************************************************************/

void
fixit(int *lab, int *ptn, int *numcells, int fixedvertex, int n)
{
    int i;

    for (i = 1; i < n; ++i)
    {
        lab[i] = i;
        ptn[i] = 1;
    }

    lab[0] = fixedvertex;
    lab[fixedvertex] = 0;
    ptn[0] = 0;
    ptn[n-1] = 0;
    if (n == 1) *numcells = 1;
    else        *numcells = 2;
}

/*****************************************************************************
*                                                                            *
*  sethash(s,n,seed,key) is a function whose value depends only on the       *
*  set s, a long seed, and an integer key.  It is intended to be independent *
*  of the word size provided long ints have at least 32 bits, and also       *
*  independent of m.  n is the underlying universal set size, NOT the        *
*  number of setwords in the set.                                            *
*  31 bits of seed and 15 bits of key are significant.                       *
*  The result is in 0..2^31-1.                                               *
*                                                                            *
*****************************************************************************/

long
sethash(set *s, int n, long seed, int key)
/* Long hashcode for a set. Some care is needed to make
   it independent of WORDSIZE. */
{
    int i,j,sh,lsh,rsh;
    unsigned long l,res,lshmask,salt;
    setword si;

    lsh = key & 0xF;
    rsh = 28 - lsh;
    salt = (key >> 4) & 0x7FFL;
    res = seed & 0x7FFFFFFFUL;
    lshmask = (1UL << lsh) - 1;

    j = 0;
    for (i = 0; j < n; ++i)
    {
        si = s[i];
        for (sh = WORDSIZE-16; sh >= 0; sh -= 16)
        {
            l = (unsigned long)((si >> sh) & 0xFFFF);
            res = (((res << lsh) ^ ((res >> rsh) & lshmask) ^ l) + salt)
                                                            & 0x7FFFFFFFUL;
            res = FUZZ1(res);
            if ((j += 16) >= n) break;
        }
    }

    return res;
}

/*****************************************************************************
*                                                                            *
*  listhash(x,nx,key) is a function whose value depends on the SET of values *
*  in the first 'nx' entries of the array 'x', and the value of key.         *
*  Machine-independent if long ints have at least 32 bits, otherwise not.    *
*  The result is in 0..2^31-1.                                               *
*                                                                            *
*****************************************************************************/

long
listhash(int *x, int nx, long key)
{
    unsigned long lkey,val,accum;
    int i;

    lkey = (unsigned long)key & 0x7FFFFFFFUL;
    accum = nx;
    
    for (i = 0; i < nx; ++i)
    {
        val = (unsigned long)x[i] & 0x7FFFFFFFUL;
        val = (val + lkey) & 0x7FFFFFFFUL;
        accum += FUZZ1(val);
    }

    return  accum & 0x7FFFFFFFUL;
}

/*****************************************************************************
*                                                                            *
*  hashgraph_sg(sg,key) is a function whose value depends on the sparse      *
*  graph or digraph sg.                                                      *
*  Machine-independent if long ints have at least 32 bits, otherwise not.    *
*  The result is in 0..2^31-1.                                               *
*                                                                            *
*****************************************************************************/

long
hashgraph_sg(sparsegraph *sg, long key)
{
    int n,i;
    int *d,*e;
    size_t *v;
    unsigned long val,accum;

    CHECK_SWG(sg,"hashgraph_sg");
    accum = n = sg->nv;

    SG_VDE(sg,v,d,e);

    for (i = 0; i < n; ++i)
        if (d[i] == 0)
            accum += FUZZ1(i);
        else
        {
            accum = (accum>>7) | ((accum<<24)&0x7FFFFFFFUL);
            val = listhash(e+v[i],d[i],key);
            val = (val + i) & 0x7FFFFFFFUL;
            accum += FUZZ2(val);
        }

   return (long)(accum & 0x7FFFFFFFUL);
}

/*****************************************************************************
*                                                                            *
*  hashgraph(g,m,n,key) is a function whose value depends on the             *
*  graph or digraph sg.                                                      *
*  Machine-independent if long ints have at least 32 bits, otherwise not.    *
*  The result is in 0..2^31-1.                                               *
*                                                                            *
*****************************************************************************/

long
hashgraph(graph *g, int m, int n, long key)
{
    int i;
    set *gi;
    unsigned long val,accum;

    accum = n;
    for (i = 0, gi = g; i < n; ++i, gi += m)
    {
        accum = (accum>>12) | ((accum<<19)&0x7FFFFFFFUL);
        val = sethash(gi,n,key,(key&0xFL)+i);
        val = (val + i) & 0x7FFFFFFFUL;
        accum += FUZZ2(val);
    }

    return (long)(accum & 0x7FFFFFFFUL);
}

/*****************************************************************************
*                                                                            *
*  hash(setarray,length,key) is a function whose value depends only on the   *
*  first 'length' entries of the array 'setarray', and the value of key.     *
*  key should be in the range 1..31 and preferably odd.                      *
*  This works best if long ints have at least 32 bits, but it's valid anyway.*
*  Not machine-indpendent!  Use sethash() in preference.                     *
*                                                                            *
*****************************************************************************/

long
hash(set *setarray, long length, int key)
{
    long code;
    set *sptr;

    code = length;
    sptr = setarray + length;

    while (--sptr >= setarray)
        code = (code<<key) ^ ((code>>(32-key)) + *sptr);

    return code;
}

/*****************************************************************************
*                                                                            *
*  readperm is like readvperm without the last argument.  It is provided     *
*  only for backward compatibility.                                          *
*                                                                            *
*****************************************************************************/

void
readperm(FILE *f, int *perm, boolean prompt, int n)
{
    int nv;

    readvperm(f,perm,prompt,n,&nv);
}

/*****************************************************************************
*                                                                            *
*  readvperm(f,perm,prompt,n,nv) reads a permutation of order n from         *
*  f, terminated by a semicolon.  Any repeated or illegal numbers or         *
*  characters are reported then ignored.    Missing numbers are filled in    *
*  in numerical order.  A prompt is issued for each line if prompt!=FALSE.   *
*  labelorg is used.  *nv is set equal to the number of numbers actually     *
*  given.  Ranges like v1:v2 are allowed.                                    *
*                                                                            *
*****************************************************************************/

void
readvperm(FILE *f, int *perm, boolean prompt, int n, int *nv)
{
    int i;
    int m,c,v1,v2;

    m = SETWORDSNEEDED(n);
#if !MAXN
    DYNALLOC1(set,workset,workset_sz,m,"readperm");
#endif

    EMPTYSET(workset,m);

    i = 0;

    while (TRUE)
    {
        GETNWC(c,f);
        if (c == ';' || c == EOF) break;
        if (ISDIGIT(c))
        {
            ungetc(c,f);
            readinteger(f,&v1);
            v1 -= labelorg;
            GETNWC(c,f);
            if (c == ':')
                if (!readinteger(f,&v2))
                {
                    fprintf(ERRFILE,"unfinished range\n\n");
                    v2 = v1;
                }
                else
                    v2 -= labelorg;
            else
            {
                ungetc(c,f);
                v2 = v1;
            }

            if (v1 < 0 || v1 >= n || v2 >= n || v1 > v2)
            {
                if (v1 < v2)
                    fprintf(ERRFILE,
                      "illegal range in permutation : %d:%d\n\n",
                      v1+labelorg,v2+labelorg);
                else
                    fprintf(ERRFILE,
                      "illegal number in permutation : %d\n\n",
                      v1+labelorg);
            }
            else
            for (; v1 <= v2; ++v1)
            {
                if (!ISELEMENT(workset,v1))
                {
                    perm[i++] = v1;
                    ADDELEMENT(workset,v1);
                }
                else
                    fprintf(ERRFILE,
                      "repeated number in permutation : %d\n\n",
                      v1+labelorg);
            }
        }
        else
        {
            if (c == '\n' && prompt)
                fprintf(PROMPTFILE,"+ ");
            if (c != '\n')
                fprintf(ERRFILE,"bad character '%c' in permutation\n\n",
                       (char)c);
        }
    }

    *nv = i;

    for (v1 = 0; v1 < n; ++v1)
        if (!ISELEMENT(workset,v1)) perm[i++] = v1;
}

/*****************************************************************************
*                                                                            *
*  ranperm(perm,n) creates a random permutation in perm.                     *
*                                                                            *
*****************************************************************************/

void
ranperm(int *perm, int n)
{
    int i,j,t;

    for (i = n; --i >= 0; ) perm[i] = i;

    for (i = n; --i > 0; )
    {
        j = KRAN(i+1);
        t = perm[i];
        perm[i] = perm[j];
        perm[j] = t;
    }
}

/*****************************************************************************
*                                                                            *
*  relabel(g,perm,lab,workg,m,n) replaces g by g^perm, using workg as        *
*  scratch space.  If lab!=NULL, it is taken as a labelling vector and       *
*  also permuted.                                                            *
*                                                                            *
*****************************************************************************/

void
relabel(graph *g, int *lab, int *perm, graph *workg, int m, int n)
{
    long li;
    int i;

    for (li = (long)M * (long)n; --li >= 0;) workg[li] = g[li];

    updatecan(workg,g,perm,0,M,n);
    if (lab != NULL)
    {
#if !MAXN
        DYNALLOC1(int,workperm,workperm_sz,n+2,"relabel");
#endif
        for (i = 0; i < n; ++i) workperm[perm[i]] = i;
        for (i = 0; i < n; ++i) lab[i] = workperm[lab[i]];
    }
}

/*****************************************************************************
*                                                                            *
*  relabel_sg(g,perm,lab,workg,m,n) replaces g by g^perm, using workg as     *
*  scratch space.  If lab!=NULL, it is taken as a labelling vector and       *
*  also permuted.                                                            *
*                                                                            *
*****************************************************************************/

void
relabel_sg(sparsegraph *sg, int *lab, int *perm, sparsegraph *workg)
{
    int i,n;
    sparsegraph *tempsg;
    sparsegraph tmp;

    n = sg->nv;

    if (workg)
    {
        tempsg = copy_sg(sg,workg);
        updatecan_sg((graph*)tempsg,(graph*)sg,perm,0,SETWORDSNEEDED(n),n);
    }
    else
    {
        SG_INIT(tmp);
        tempsg = copy_sg(sg,&tmp);
        updatecan_sg((graph*)tempsg,(graph*)sg,perm,0,SETWORDSNEEDED(n),n);
        SG_FREE(tmp);
    }

    if (lab != NULL)
    {
#if !MAXN
        DYNALLOC1(int,workperm,workperm_sz,n+2,"relabel_sg");
#endif
        for (i = 0; i < n; ++i) workperm[perm[i]] = i;
        for (i = 0; i < n; ++i) lab[i] = workperm[lab[i]];
    }
}

/*****************************************************************************
*                                                                            *
*  sublabel(g,perm,nperm,workg,m,n) replaces g by g^perm, using workg as     *
*  scratch space.  perm is a partial vector, of length nperm, where it is    *
*  known that the elements of perm are distinct.                             *
*                                                                            *
*****************************************************************************/

void
sublabel(graph *g, int *perm, int nperm, graph *workg, int m, int n)
{
    long li;
    int i,j,k;
    int newm;
    set *gi,*wgi;

    for (li = (long)m * (long)n; --li >= 0;) workg[li] = g[li];

    newm = SETWORDSNEEDED(nperm);

    for (li = (long)newm * (long)nperm; --li >= 0;) g[li] = 0;

    for (i = 0, gi = (set*)g; i < nperm; ++i, gi += newm)
    {
        wgi = GRAPHROW(workg,perm[i],m);
        for (j = 0; j < nperm; ++j)
        {
            k = perm[j];
            if (ISELEMENT(wgi,k)) ADDELEMENT(gi,j);
        }
    }
}

/*****************************************************************************
*                                                                            *
*  countcells(ptn,level,n) finds the number of elements of ptn[0..n-1]       *
*  that are <= level.                                                        *
*                                                                            *
*****************************************************************************/

int
countcells(int *ptn, int level, int n)
{
    int i,cnt;

    cnt = 0;
    for (i = 0; i < n; ++i) if (ptn[i] <= level) ++cnt;

    return cnt;
}

/*****************************************************************************
*                                                                            *
*  subpartion(lab,ptn,n,perm,nperm) replaces the partition (lab,ptn) of      *
*  0..n-1 by the induced partition of 0..nperm-1, using the partial          *
*  ordering of 0..n-1 given in perm[0..nperm-1].                             *
*  Return the new number of cells.                                           *
*                                                                            *
*****************************************************************************/

#define DEB(str,x) fprintf(stderr,"%s=%d\n",str,x);

int
subpartition(int *lab, int *ptn, int n, int *perm, int nperm)
{
    int i,j;

#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"subpartition");
#endif
    for (i = 0; i < n; ++i) workperm[i] = -1;
    for (i = 0; i < nperm; ++i) workperm[perm[i]] = i;

    j = -1;
    for (i = 0; i < n; ++i)
    {
        if (workperm[lab[i]] >= 0)
        {      
            ++j;     
            lab[j] = workperm[lab[i]];
            ptn[j] = ptn[i];
        }
        else if (j >= 0 && ptn[i] < ptn[j])
            ptn[j] = ptn[i];
    }

    return countcells(ptn,0,nperm);
}

/*****************************************************************************
*                                                                            *
*  sublabel_sg(sg,perm,nperm,workg) replaces g by g^perm, using workg as     *
*  scratch space.  perm is a partial vector, of length nperm, where it is    *
*  known that the elements of perm are distinct.                             *
*                                                                            *
*****************************************************************************/

void
sublabel_sg(sparsegraph *sg, int *perm, int nperm, sparsegraph *workg)
{
    int i,j,k,n;
    size_t newnde,kk;
    sparsegraph *tempsg;
    sparsegraph tmp;
    int *d,*e;
    int *dd,*ee;
    size_t *v,*vv;

    CHECK_SWG(sg,"sublabel_sg");
    n = sg->nv;
 
#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"relabel_sg");
#endif
    for (i = 0; i < n; ++i) workperm[i] = -1;
    for (i = 0; i < nperm; ++i) workperm[perm[i]] = i;

    newnde = 0;
    SG_VDE(sg,v,d,e);

    for (i = 0; i < nperm; ++i)
    {
        j = perm[i];
        for (k = 0; k < d[j]; ++k)
            if (workperm[e[v[j]+k]] >= 0) ++newnde;
    }
 
    if (workg)
        tempsg = workg;
    else
    {
        SG_INIT(tmp);
        tempsg = &tmp;
    }
 
    SG_ALLOC(*tempsg,nperm,newnde,"sublabel_sg");
    SG_VDE(tempsg,vv,dd,ee);

    kk = 0;
    for (i = 0; i < nperm; ++i)
    {
        j = perm[i];
        vv[i] = kk;
        dd[i] = 0;
        for (k = 0; k < d[j]; ++k)
            if (workperm[e[v[j]+k]] >= 0)
            {
                ee[vv[i]+dd[i]] = workperm[e[v[j]+k]];
                ++dd[i];
            }
        kk += dd[i];
    }
    tempsg->nv = nperm;
    tempsg->nde = newnde;

    copy_sg(tempsg,sg);

    if (!workg) SG_FREE(tmp);
}

/*****************************************************************************
*                                                                            *
*  copycomment(fin,fout,delimiter) copies fin to fout until either EOF or    *
*  the character 'delimiter' is read.  The delimiter itself isn't written.   *
*  Escape sequences \n,\t,\b,\r,\f,\\,\',\",\\n are recognised.  Otherwise,  *
*  '\' is ignored.                                                           *
*                                                                            *
*****************************************************************************/

void
copycomment(FILE *fin, FILE *fout, int delimiter)
{
    int c,backslash;

    backslash = FALSE;

    while ((c = getc(fin)) != EOF && (c != delimiter || backslash))
        if (backslash)
        {
            switch (c)
            {
            case '\n':
                break;
            case 'n':
                PUTC('\n',fout);
                break;
            case 't':
                PUTC('\t',fout);
                break;
            case 'b':
                PUTC('\b',fout);
                break;
            case 'r':
                PUTC('\r',fout);
                break;
            case 'f':
                PUTC('\f',fout);
                break;
            case '\\':
                PUTC('\\',fout);
                break;
            case '\'':
                PUTC('\'',fout);
                break;
            case '"':
                PUTC('"',fout);
                break;
            default:
                PUTC(c,fout);
            }
            backslash = FALSE;
        }
        else if (c == '\\')
            backslash = TRUE;
        else
            PUTC(c,fout);
}

/*****************************************************************************
*                                                                            *
*  converse_sg(g1,g2) performs a digraph converse operation on g1,           *
*  leaving the result in g2. g2 must exist and be initialised.               *
*  If g1 is an undirected graph, g2 will be the same.                        *
*                                                                            *
*****************************************************************************/

void
converse_sg(sparsegraph *g1, sparsegraph *g2)
{
    int *e1,*d1,*e2,*d2;
    size_t *v1,*v2,j;
    int i,k,n;

    CHECK_SWG(g1,"converse_sg");
    n = g1->nv;

    SG_ALLOC(*g2,n,g1->nde,"converse_sg");
    g2->nv = n;
    g2->nde = g1->nde;
    DYNFREE(g2->w,g2->wlen);

    SG_VDE(g1,v1,d1,e1);
    SG_VDE(g2,v2,d2,e2);

    for (i = 0; i < n; ++i) d2[i] = 0;
    for (i = 0; i < n; ++i)
        for (j = v1[i]; j < v1[i]+d1[i]; ++j) ++d2[e1[j]];

    v2[0] = 0;
    for (i = 1; i < n; ++i) v2[i] = v2[i-1] + d2[i-1];
    for (i = 0; i < n; ++i) d2[i] = 0;

    for (i = 0; i < n; ++i)
        for (j = v1[i]; j < v1[i]+d1[i]; ++j)
        {
            k = e1[j];
            e2[v2[k] + (d2[k]++)] = i;
        }
}

/*****************************************************************************
*                                                                            *
*  complement_sg(g1,g2) sets g2 to the complement of g1.                     *
*  If g1 has loops then the loop set is also complemented; otherwise         *
*  no loops are created.  g2 must exist and be initialised.                  *
*                                                                            *
*****************************************************************************/

void
complement_sg(sparsegraph *g1, sparsegraph *g2)
{
    int *e1,*d1,*e2,*d2;
    size_t *v1,*v2,j,ndec;
    int i,l,m,n;
    int loops;

    CHECK_SWG(g1,"complement_sg");
    n = g1->nv;
    SG_VDE(g1,v1,d1,e1);

    loops = 0;
    for (i = 0; i < n; ++i)
        for (j = v1[i]; j < v1[i] + d1[i]; ++j)
            if (e1[j] == i) ++loops;

    if (loops > 1) ndec = n*(size_t)n - g1->nde;
    else           ndec = n*(size_t)n - n - g1->nde; 
    SG_ALLOC(*g2,n,ndec,"converse_sg");
    g2->nv = n;
    SG_VDE(g2,v2,d2,e2);

    m = SETWORDSNEEDED(n);
#if !MAXN
    DYNALLOC1(set,workset,workset_sz,m,"putorbits");
#endif

    DYNFREE(g2->w,g2->wlen);

    ndec = 0;

    for (i = 0; i < n; ++i)
    {
        EMPTYSET(workset,m);
        for (j = v1[i]; j < v1[i]+d1[i]; ++j) ADDELEMENT(workset,e1[j]);
        if (loops == 0) ADDELEMENT(workset,i);

        v2[i] = ndec;
        for (l = 0; l < n; ++l)
            if (!ISELEMENT(workset,l)) e2[ndec++] = l;
        d2[i] = ndec - v2[i];
    }
    g2->nde = ndec;
}

/*****************************************************************************
*                                                                            *
*  mathon_sg(g1,g2) performs a Mathon doubling operation on g1,              *
*  leaving the result in g2. g2 must exist and be initialised.               *
*                                                                            *
*****************************************************************************/

void
mathon_sg(sparsegraph *g1, sparsegraph *g2)
{
    int *e1,*d1,*e2,*d2;
    size_t *v1,*v2,j;
    int i,k,m,n1,n2;

    CHECK_SWG(g1,"mathon_sg");

    n1 = g1->nv;
    n2 = 2*n1 + 2;
    SG_ALLOC(*g2,n2,n2*(size_t)n1,"mathon_sg");
    g2->nv = n2;
    g2->nde = n2*(size_t)n1;
    DYNFREE(g2->w,g2->wlen);

    SG_VDE(g1,v1,d1,e1);
    SG_VDE(g2,v2,d2,e2);

    m = SETWORDSNEEDED(n1);
#if !MAXN
    DYNALLOC1(set,workset,workset_sz,m,"mathon_sg");
#endif

    for (i = 0; i < n2; ++i)
    {
        v2[i] = i*(size_t)n1;
        d2[i] = 0;
    }

    for (i = 0; i < n1; ++i)
    {
        e2[v2[0]+(d2[0]++)] = i+1;
        e2[v2[i+1]+(d2[i+1]++)] = 0;
        e2[v2[n1+1]+(d2[n1+1]++)] = i+n1+2;
        e2[v2[i+n1+2]+(d2[i+n1+2]++)] = n1+1;
    }

    for (i = 0; i < n1; ++i)
    {
        EMPTYSET(workset,m);
        for (j = v1[i]; j < v1[i]+d1[i]; ++j)
        {
            k = e1[j];
            if (k == i) continue;   /* ignore loops */
            ADDELEMENT(workset,k);
            e2[v2[i+1]+(d2[i+1]++)] = k+1;
            e2[v2[i+n1+2]+(d2[i+n1+2]++)] = k+n1+2;
        }
        for (k = 0; k < n1; ++k)
            if (k != i && !ISELEMENT(workset,k))
            {
                e2[v2[i+1]+(d2[i+1]++)] = k+n1+2;
                e2[v2[k+n1+2]+(d2[k+n1+2]++)] = i+1;
            }
    }
}

/*****************************************************************************
*                                                                            *
*  mathon(g1,m1,n1,g2,m2,n2) performs a Mathon doubling operation on g1,     *
*  leaving the result in g2.                                                 *
*  m1,n1 and m2,n2 are the values of m,n before and after the operation.     *
*                                                                            *
*****************************************************************************/

void
mathon(graph *g1, int m1, int n1, graph *g2, int m2, int n2)
{
    int i,j,ii,jj;
    long li;
    set *rowptr,*gp;

    for (li = (long)m2 * (long)n2; --li >= 0;) g2[li] = 0;

    for (i = 1; i <= n1; ++i)
    {
        ii = i + n1 + 1;
        gp = GRAPHROW(g2,0,m2);        /* unnecessarily convoluted code */
        ADDELEMENT(gp,i);             /* needed to avoid compiler bug  */
        gp = GRAPHROW(g2,i,m2);        /* in MSDOS version */
        ADDELEMENT(gp,0);
        gp = GRAPHROW(g2,n1+1,m2);
        ADDELEMENT(gp,ii);
        gp = GRAPHROW(g2,ii,m2);
        ADDELEMENT(gp,n1+1);
    }

    for (i = 0, rowptr = g1; i < n1; ++i, rowptr += m1)
        for (j = 0; j < n1; ++j)
            if (j != i)
            {
                ii = i + n1 + 2;
                jj = j + n1 + 2;
                if (ISELEMENT(rowptr,j))
                {
                    gp = GRAPHROW(g2,i+1,m2);
                    ADDELEMENT(gp,j+1);
                    gp = GRAPHROW(g2,ii,m2);
                    ADDELEMENT(gp,jj);
                }
                else
                {
                    gp = GRAPHROW(g2,i+1,m2);
                    ADDELEMENT(gp,jj);
                    gp = GRAPHROW(g2,ii,m2);
                    ADDELEMENT(gp,j+1);
                }
            }
}

/*****************************************************************************
*                                                                            *
*  rangraph(g,digraph,invprob,m,n) makes a random graph (or digraph if       *
*  digraph!=FALSE) with edge probability 1/invprob.                          *
*                                                                            *
*****************************************************************************/

void
rangraph(graph *g, boolean digraph, int invprob, int m, int n)
{
    int i,j;
    long li;
    set *row,*col;

    for (li = (long)m * (long)n; --li >= 0;) g[li] = 0;

    for (i = 0, row = g; i < n; ++i, row += m)
        if (digraph)
        {
            for (j = 0; j < n; ++j)
                if (KRAN(invprob) == 0) ADDELEMENT(row,j);
        }
        else
        {
            for (j = i + 1, col = GRAPHROW(g,j,m); j < n; ++j, col += m)
                if (KRAN(invprob) == 0)
                {
                    ADDELEMENT(row,j);
                    ADDELEMENT(col,i);
                }
        }
}


/*****************************************************************************
*                                                                            *
*  rangraph2(g,digraph,p1,p2,m,n) makes a random graph (or digraph if        *
*  digraph!=FALSE) with edge probability p1/p2.                              *
*                                                                            *
*****************************************************************************/

void
rangraph2(graph *g, boolean digraph, int p1, int p2, int m, int n)
{
    int i,j;
    long li;
    set *row,*col;

    for (li = (long)m * (long)n; --li >= 0;) g[li] = 0;

    for (i = 0, row = g; i < n; ++i, row += m)
        if (digraph)
        {
            for (j = 0; j < n; ++j)
                if (KRAN(p2) < p1) ADDELEMENT(row,j);
        }
        else
            for (j = i + 1, col = GRAPHROW(g,j,m); j < n; ++j, col += m)
                if (KRAN(p2) < p1)
                {
                    ADDELEMENT(row,j);
                    ADDELEMENT(col,i);
                }
}

/*****************************************************************************
*                                                                            *
*  rangraph2_sg(sg,digraph,p1,p2,n) makes a random graph (or digraph if    *
*  digraph!=FALSE) with edge probability p1/p2. sg must be initialised.      *
*                                                                            *
*****************************************************************************/

void
rangraph2_sg(sparsegraph *sg, boolean digraph, int p1, int p2, int n)
{
    int i,j,k;
    int *dd,*ee;
    double rn,expec,var,sd;
    int ldeg;
    size_t *vv,inc,nde;
 
    sg->nv = n;

    rn = n;
    expec = (rn*rn-rn)*(double)p1/(double)p2;
    var = expec*(double)(p2-p1)/(double)p2;
    if (!digraph) var *= 2.0;
    sd = 1.0;
    if (var > 1.0)
        for (i = 0; i < 19; ++i) sd = (sd + var/sd) / 2.0;
    inc = sd + 20;
 
    SG_ALLOC(*sg,n,(size_t)expec+4*inc,"rangraph2_sg");
    SG_VDE(sg,vv,dd,ee);
    DYNFREE(sg->w,sg->wlen);

    for (i = 0; i < n; ++i) dd[i] = 0;
    vv[0] = 0;
    nde = 0;

    if (!digraph)
    {
        for (i = 0; i < n; ++i)
        {
            ldeg = 0;
            for (j = i+1; j < n; ++j)
            if (KRAN(p2) < p1)
            {
                nde += 2;
                if (nde > sg->elen)
                {
                    DYNREALLOC(int,sg->e,sg->elen,sg->elen+inc,
                                                "rangraph2_sg realloc");
                    ee = sg->e;
                }
                ee[vv[i]+ldeg++] = j;
                ++dd[j];
            }
            if (i < n-1) vv[i+1] = vv[i] + dd[i] + ldeg;
            dd[i] = ldeg;
        }
        for (i = 0; i < n; ++i)
            for (k = 0; k < dd[i]; ++k)
            {
                j = ee[vv[i]+k];
                if (j > i) ee[vv[j]+dd[j]++] = i;
            }
        sg->nde = nde;
    }
    else
    {
        for (i = 0; i < n; ++i)
        {
            ldeg = 0;
            for (j = 0; j < n; ++j)
            if (j != i && KRAN(p2) < p1)
            {
                ++nde;
                if (nde > sg->elen)
                {
                    DYNREALLOC(int,sg->e,sg->elen,sg->elen+inc,
                                             "rangraph2_sg realloc");
                    ee = sg->e;
                }
                ee[vv[i]+ldeg++] = j;
            }
            if (i < n-1) vv[i+1] = vv[i] + ldeg;
            dd[i] = ldeg;
        }
        sg->nde = nde;
    }
}

/****************************************************************************/

static void
putsequence(FILE *f, int *x, int linelength, int n)
/* Write n integers to f with equal values collapsed.
 * labelorg is used. */
{
    char s[60];
    int j,v1,v2,xval,curlen;

    curlen = 0;
    v1 = 0;
    while (v1 < n)
    {
        xval = x[v1];

        for (v2 = v1; v2 < n - 1 && x[v2+1] == xval; ++v2) {}
        j = itos(v1+labelorg,s);
        if (v2 > v1)
        {
            s[j++] = '-';
            j += itos(v2+labelorg,&s[j]);
        }
        s[j++] = ':';
        j += itos(xval,&s[j]);
        s[j] = ' ';
        s[j+1] = '\0';
        if (linelength > 0 && curlen + j >= linelength)
        {
            PUTC('\n',f);
            curlen = 0;
        }
        curlen += j + 1;
        putstring(f,s);
        v1 = v2 + 1;
    }
    PUTC('\n',f);
}

/****************************************************************************/

static void
putnumbers(FILE *f, int *x, int linelength, int n)
/* Write n integers to f with equal values combined as multiplicities.
 * labelorg is NOT used. */
{
    char s[60];
    int j,v1,v2,xval,curlen;

    curlen = 0;
    v1 = 0;
    while (v1 < n)
    {
        xval = x[v1];

        for (v2 = v1; v2 < n - 1 && x[v2+1] == xval; ++v2) {}
        if (v2 > v1)
        {
            j = itos(v2-v1+1,s);
            s[j++] = '*';
        }
        else
            j = 0;

        j += itos(xval,&s[j]);
        s[j] = ' ';
        s[j+1] = '\0';
        if (linelength > 0 && curlen + j >= linelength)
        {
            PUTC('\n',f);
            curlen = 0;
        }
        curlen += j + 1;
        putstring(f,s);
        v1 = v2 + 1;
    }
    PUTC('\n',f);
}

/*****************************************************************************
*                                                                            *
*  putdegs(f,g,linelength,m,n)  writes the degree of each vertex of g to     *
*  file f, using at most linelength characters per line (excluding '\n').    *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*  labelorg is used.                                                         *
*                                                                            *
*  FUNCTIONS CALLED : itos(),putstring(),setsize()                           *
*                                                                            *
*****************************************************************************/

void
putdegs(FILE *f, graph *g, int linelength, int m, int n)
{
    int i;
    graph *gp;

#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n+2,"putdegs");
#endif

    for (i = 0, gp = g; i < n; ++i, gp += M)
        workperm[i] = setsize(gp,m);

    putsequence(f,workperm,linelength,n);
}

/*****************************************************************************
*                                                                            *
*  putdegseq(f,g,linelength,m,n)  writes the sorted degree sequence of g     *
*  file f, using at most linelength characters per line (excluding '\n').    *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*                                                                            *
*****************************************************************************/

void
putdegseq(FILE *f, graph *g, int linelength, int m, int n)
{
    int i;
    graph *gp;

#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,n,"putdegs");
#endif

    for (i = 0, gp = g; i < n; ++i, gp += M)
        workperm[i] = setsize(gp,m);

    sort1int(workperm,n);
    putnumbers(f,workperm,linelength,n);
}

/*****************************************************************************
*                                                                            *
*  putdegs_sg(f,sg,linelength)  writes the degree of each vertex of sg to    *
*  file f, using at most linelength characters per line (excluding '\n').    *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*  labelorg is used.                                                         *
*                                                                            *
*  FUNCTIONS CALLED : itos(),putstring(),                                    *
*                                                                            *
*****************************************************************************/

void
putdegs_sg(FILE *f, sparsegraph *sg, int linelength)
{
    putsequence(f,sg->d,linelength,sg->nv);
}

/*****************************************************************************
*                                                                            *
*  putdegseq_sg(f,sg,linelength) writes the sorted degree sequence of sg to  *
*  file f, using at most linelength characters per line (excluding '\n').    *
*  A value of linelength <= 0 dictates no line breaks at all.                *
*                                                                            *
*****************************************************************************/

void
putdegseq_sg(FILE *f, sparsegraph *sg, int linelength)
{
    int i;
#if !MAXN
    DYNALLOC1(int,workperm,workperm_sz,sg->nv,"putdegs");
#endif

    for (i = 0; i < sg->nv; ++i)
        workperm[i] = sg->d[i];

    sort1int(workperm,sg->nv);
    putnumbers(f,workperm,linelength,sg->nv);
}

/*****************************************************************************
*                                                                            *
*  complement(g,m,n) replaces the graph g by its complement                  *
*  No loops are created unless there are loops present, in which case the    *
*  loops are also complemented.                                              *
*                                                                            *
*****************************************************************************/

void
complement(graph *g, int m, int n)
{
    boolean loops;
    int i,j;
    graph *gp;

#if !MAXN
    DYNALLOC1(set,workset,workset_sz,m,"complement");
#endif

    loops = FALSE;
    for (i = 0, gp = g; i < n && !loops; ++i, gp += M)
        if (ISELEMENT(gp,i)) loops = TRUE;

    EMPTYSET(workset,m);
    for (i = 0; i < n; ++ i) ADDELEMENT(workset,i);

    for (i = 0, gp = g; i < n; ++i, gp += M)
    {
        for (j = 0; j < M; ++j) gp[j] = workset[j] & ~gp[j];
        if (!loops) DELELEMENT(gp,i);
    }
}

/*****************************************************************************
*                                                                            *
*  converse(g,m,n) replaces the digraph g by its converse.                   *
*  There is no effect on an undirected graph.                                *
*                                                                            *
*****************************************************************************/

void
converse(graph *g, int m, int n)
{
    int i,j;
    graph *gi,*gj;

    for (i = 0, gi = g; i < n; ++i, gi += M)
        for (j = i+1, gj = gi+M; j < n; ++j, gj += M)
            if ((ISELEMENT(gi,j)!=0) + (ISELEMENT(gj,i)!=0) == 1)
            {
                FLIPELEMENT(gi,j);
                FLIPELEMENT(gj,i);
            }
}

/*****************************************************************************
*                                                                            *
*  naututil_check() checks that this file is compiled compatibly with the    *
*  given parameters.   If not, call exit(1).                                 *
*                                                                            *
*****************************************************************************/

void
naututil_check(int wordsize, int m, int n, int version)
{
    if (wordsize != WORDSIZE)
    {
        fprintf(ERRFILE,"Error: WORDSIZE mismatch in naututil.c\n");
        exit(1);
    }

#if MAXN
    if (m > MAXM)
    {
        fprintf(ERRFILE,"Error: MAXM inadequate in naututil.c\n");
        exit(1);
    }

    if (n > MAXN)
    {
        fprintf(ERRFILE,"Error: MAXN inadequate in naututil.c\n");
        exit(1);
    }
#endif

    if (version < NAUTYREQUIRED)
    {
        fprintf(ERRFILE,"Error: naututil.c version mismatch\n");
        exit(1);
    }
}

/*****************************************************************************
*                                                                            *
*  naututil_freedyn() - free the dynamic memory in this module               *
*                                                                            *
*****************************************************************************/

void
naututil_freedyn(void)
{
    echunk *ec1,*ec2;

#if !MAXN
    DYNFREE(workperm,workperm_sz);
    DYNFREE(workset,workset_sz);
#endif
    ec1 = first_echunk.next;

    while (ec1)
    {
        ec2 = ec1->next;
        FREES(ec1);
        ec1 = ec2;
    }
}
