/* complg.c  version 2.0; B D McKay, Jun 2015. */

#define USAGE "complg [-lq] [-a] [-L] [-r|-R] [infile [outfile]]"

#define HELPTEXT \
" Take the complements of a file of graphs.\n\
\n\
    The output file has a header if and only if the input file does.\n\
    The output format is defined by the header or first graph.\n\
\n\
    -r  Only complement if the complement has fewer directed edges.\n\
    -R  Only complement if the complement has fewer directed edges\n\
        or has the same number of directed edges and is canonically\n\
        less than the original.\n\
    -a  Also output the input graph (before the complement).\n\
    -L  Complement the loops too. By default, preserve them.\n\
    -l  Canonically label outputs.\n\
    -q  Suppress auxiliary information.\n"

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

#include "gtools.h" 

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

static void
compl(graph *g, int m, int n, graph *h, boolean comploops)
/* h := complement of g */
{
    int i,j;
    setword *gi,*hi;
#if MAXN
    set all[MAXM];
#else
    DYNALLSTAT(set,all,all_sz);
    DYNALLOC1(set,all,all_sz,m,"complg");
#endif

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

    gi = (setword*) g;
    hi = (setword*) h;

    for (i = 0; i < n; ++i)
    {
        for (j = 0; j < m; ++j) hi[j] = gi[j] ^ all[j];
        if (!comploops) FLIPELEMENT(hi,i);
        gi += m;
        hi += m;
    }
}

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

int
main(int argc, char *argv[])
{
    char *infilename,*outfilename;
    FILE *infile,*outfile;
    boolean also,dolabel,badargs,restricted,Restricted,quiet;
    boolean digraph,Lswitch;
    int j,m,n,argnum;
    int codetype,outcode;
    graph *g;
    size_t ii,ned,nedc,nn,loops,loopsc,gwords;
    nauty_counter nin;
    char *arg,sw;
    static graph *gq;
    double t;
#if MAXN
    graph gc[MAXN*MAXM],h[MAXN*MAXM],hc[MAXN*MAXM];
#else
    DYNALLSTAT(graph,gc,gc_sz);
    DYNALLSTAT(graph,h,h_sz);
    DYNALLSTAT(graph,hc,hc_sz);
#endif

    HELP; PUTVERSION;

    infilename = outfilename = NULL;
    dolabel = badargs = also = FALSE;
    restricted = Restricted = quiet = Lswitch = FALSE;

    argnum = 0;
    badargs = FALSE;
    for (j = 1; !badargs && j < argc; ++j)
    {
        arg = argv[j];
        if (arg[0] == '-' && arg[1] != '\0')
        {
            ++arg;
            while (*arg != '\0')
            {
                sw = *arg++;
                     SWBOOLEAN('r',restricted)
                else SWBOOLEAN('R',Restricted)
                else SWBOOLEAN('l',dolabel)
                else SWBOOLEAN('L',Lswitch)
                else SWBOOLEAN('a',also)
                else SWBOOLEAN('q',quiet)
                else badargs = TRUE;
            }
        }
        else
        {
            ++argnum;
            if      (argnum == 1) infilename = arg;
            else if (argnum == 2) outfilename = arg;
            else                  badargs = TRUE;
        }
    }

    if (badargs)
    {
        fprintf(stderr,">E Usage: %s\n",USAGE);
        GETHELP;
        exit(1);
    }

    if (!quiet)
    {
        fprintf(stderr,">A complg");
        if (restricted || Restricted || dolabel || Lswitch || also)
            fprintf(stderr," -");
        if (restricted && !Restricted) fprintf(stderr,"r");
        if (Restricted) fprintf(stderr,"R");
        if (dolabel) fprintf(stderr,"l");
        if (Lswitch) fprintf(stderr,"L");
        if (also) fprintf(stderr,"a");
        if (argnum > 0) fprintf(stderr," %s",infilename);
        if (argnum > 1) fprintf(stderr," %s",outfilename);
        fprintf(stderr,"\n");
        fflush(stderr);
    }

    if (infilename && infilename[0] == '-') infilename = NULL;
    infile = opengraphfile(infilename,&codetype,FALSE,1);
    if (!infile) exit(1);
    if (!infilename) infilename = "stdin";

    if (!outfilename || outfilename[0] == '-')
    {
        outfilename = "stdout";
        outfile = stdout;
    }
    else if ((outfile = fopen(outfilename,"w")) == NULL)
    {
        fprintf(stderr,"Can't open output file %s\n",outfilename);
        gt_abort(NULL);
    }

    if      (codetype&SPARSE6)  outcode = SPARSE6;
    else if (codetype&DIGRAPH6) outcode = DIGRAPH6;
    else                        outcode = GRAPH6;

    if (codetype&HAS_HEADER)
    {
        if (outcode == SPARSE6)       writeline(outfile,SPARSE6_HEADER);
        else if (outcode == DIGRAPH6) writeline(outfile,DIGRAPH6_HEADER);
        else                          writeline(outfile,GRAPH6_HEADER);
    }

    nauty_check(WORDSIZE,1,1,NAUTYVERSIONID);

    nin = 0;
    t = CPUTIME;
    while (TRUE)
    {
        if ((g = readgg(infile,NULL,0,&m,&n,&digraph)) == NULL) break;
        ++nin;
#if !MAXN
        DYNALLOC2(graph,gc,gc_sz,n,m,"complg");
#endif

        if (restricted || Restricted)
        {
            ned = loops = 0;
            gwords = m * (size_t)n;
            nn = n * (size_t)(n-1);
            for (ii = 0; ii < gwords; ++ii) ned += POPCOUNT(g[ii]);
            for (ii = 0; ii < n; ++ii) if (ISELEMENT(g+m*ii,ii)) ++loops;
            if (Lswitch)
            {
                loopsc = n - loops;
                nedc = nn + n - (ned-loops)/2;
            }
            else
            {
                loopsc = loops;
                nedc = nn - (ned-3*loops)/2;
            }

            if (ned > nedc || (ned == nedc && !Restricted))
            {
                compl(g,m,n,gc,Lswitch);
                if (dolabel)
                {
#if !MAXN
                    DYNALLOC2(graph,hc,hc_sz,n,m,"complg");
#endif
                    fcanonise(gc,m,n,hc,NULL,digraph||loopsc>0);
                    gq = hc;
                }
                else
                    gq = gc;
            }
            else if (ned < nedc)
            {
                if (dolabel)
                {
#if !MAXN
                    DYNALLOC2(graph,h,h_sz,n,m,"complg");
#endif
                    fcanonise(g,m,n,h,NULL,digraph||loops>0);
                    gq = h;
                }
                else
                    gq = g;
            }
            else 
            {
                compl(g,m,n,gc,Lswitch);
#if !MAXN
                DYNALLOC2(graph,h,h_sz,n,m,"complg");
                DYNALLOC2(graph,hc,hc_sz,n,m,"complg");
#endif
                fcanonise(g,m,n,h,NULL,digraph||loops>0);
                fcanonise(gc,m,n,hc,NULL,digraph||loopsc>0);
                for (ii = 0; ii < gwords; ++ii)
                    if (h[ii] != hc[ii]) break;
                if (ii == gwords || hc[ii] < h[ii])
                {
                    if (dolabel) gq = hc; else gq = gc;
                }
                else
                {
                    if (dolabel) gq = h; else gq = g;
                }
            }
        }
        else   /* Not restricted */
        {
            compl(g,m,n,gc,Lswitch);
            if (dolabel)
            {
#if !MAXN
                DYNALLOC2(graph,hc,hc_sz,n,m,"complg");
#endif
                fcanonise(gc,m,n,hc,NULL,digraph||loopsc>0);
                gq = hc;
            }
            else
                gq = gc;
        }

        if (also)
        {
            if (outcode == SPARSE6)       writes6(outfile,g,m,n);
            else if (outcode == DIGRAPH6) writed6(outfile,g,m,n);
            else                          writeg6(outfile,g,m,n);
        }

        if (outcode == SPARSE6)       writes6(outfile,gq,m,n);
        else if (outcode == DIGRAPH6) writed6(outfile,gq,m,n);
        else                          writeg6(outfile,gq,m,n);
        FREES(g);
    }
    t = CPUTIME - t;

    if (!quiet)
        fprintf(stderr,">Z  " COUNTER_FMT 
                " graphs converted from %s to %s in %3.2f sec.\n",
                nin,infilename,outfilename,t);

    exit(0);
}
