/* linegraphg.c  version 1.2; B D McKay, Jan 2016. */

#define USAGE "linegraphg [-t] [-q] [infile [outfile]]"

#define HELPTEXT \
" Take the linegraphs of a file of graphs.\n\
  Input graphs with no edges produce only a warning message.\n\
\n\
    The output file has a header if and only if the input file does.\n\
\n\
    -t  make the total graph\n\
    -q  Suppress auxiliary information.\n"

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

#include "gtools.h" 

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

static void
linegraph(sparsegraph *g, sparsegraph *h)
/* h := linegraph of g */
/* Has the side effect of sorting the edge lists of g */
{
    DYNALLSTAT(size_t,eno,eno_sz);   /* edge number */
    int *ge,*gd,*he,*hd;
    size_t *gv,*hv;
    int gnv,hnv,jj;
    size_t i,j,k,gnde,hnde,xhnde,num;
    size_t hi,lo,mid,v,w;

    sortlists_sg(g);
    SG_VDE(g,gv,gd,ge);
    gnv = g->nv;
    gnde = g->nde;
    DYNALLOC1(size_t,eno,eno_sz,gnde,"linegraphg");

    hnv = gnde/2;
    if (hnv != gnde/2) gt_abort(">E linegraphg: too many input edges\n");

    hnde = 0;
    num = 0;
    for (i = 0; i < gnv; ++i)
    {
        xhnde = hnde;
        hnde += gd[i]*((size_t)gd[i]-1);
        if (hnde < xhnde) gt_abort(">E linegraphg: too many output edges\n");

        for (j = gv[i]; j < gv[i]+gd[i]; ++j)
        {
            if (ge[j] == i)
                gt_abort(">E linegraphg can't handle loops\n");
            else if (ge[j] > i)
                eno[j] = num++;
            else
            {
                lo = gv[ge[j]];
                hi = lo + gd[ge[j]] - 1;
                while (lo <= hi)
                {
                    mid = lo + (hi-lo)/2;
                    if (ge[mid] == i) break;
                    else if  (ge[mid] < i) lo = mid+1;
                    else hi = mid-1;
                }
                if (lo > hi)
                    gt_abort(">E linegraphg : binary search failed\n");
                eno[j] = eno[mid];
            }
        }
    }

    SG_ALLOC(*h,hnv,hnde,"linegraphg");
    h->nv = hnv;
    h->nde = hnde;
    SG_VDE(h,hv,hd,he);

    for (i = 0; i < hnv; ++i) hd[i] = 0;
    for (i = 0; i < gnv; ++i)
    {
        for (j = gv[i]; j < gv[i]+gd[i]; ++j)
            hd[eno[j]] += gd[i]-1;
    }

    hv[0] = 0;
    for (i = 1; i < hnv; ++i) hv[i] = hv[i-1] + hd[i-1];
    for (i = 0; i < hnv; ++i) hd[i] = 0;

    for (i = 0; i < gnv; ++i)
    {
        for (jj = 0; jj < gd[i]-1; ++jj)
        {
            j = gv[i] + jj;
            for (k = j+1; k < gv[i]+gd[i]; ++k)
            {
                v = eno[j]; w = eno[k];
                he[hv[v]+(hd[v]++)] = w;
                he[hv[w]+(hd[w]++)] = v;
            }
        }
    }
}

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

static void
totalgraph(sparsegraph *g, sparsegraph *h)
/* h := total graph of g */
/* Has the side effect of sorting the edge lists of g */
{
    DYNALLSTAT(size_t,eno,eno_sz);   /* edge number */
    int *ge,*gd,*he,*hd;
    size_t *gv,*hv;
    int gnv,hnv,jj;
    size_t i,j,k,gnde,hnde,xhnde,num;
    size_t hi,lo,mid,v,w;

    sortlists_sg(g);
    SG_VDE(g,gv,gd,ge);
    gnv = g->nv;
    gnde = g->nde;
    DYNALLOC1(size_t,eno,eno_sz,gnde,"linegraphg");

    hnv = gnv + gnde/2;
    if (hnv - gnv != gnde/2) gt_abort(">E linegraphg: too many input edges\n");

    hnde = 3*gnde;
    if (hnde/3 != gnde) gt_abort(">E linegraphg: too many output edges\n");
    num = gnv;
    for (i = 0; i < gnv; ++i)
    {
        xhnde = hnde;
        hnde += gd[i]*((size_t)gd[i]-1);
        if (hnde < xhnde) gt_abort(">E linegraphg: too many output edges\n");

        for (j = gv[i]; j < gv[i]+gd[i]; ++j)
        {
            if (ge[j] == i)
                gt_abort(">E linegraphg can't handle loops\n");
            else if (ge[j] > i)
                eno[j] = num++;
            else
            {
                lo = gv[ge[j]];
                hi = lo + gd[ge[j]] - 1;
                while (lo <= hi)
                {
                    mid = lo + (hi-lo)/2;
                    if (ge[mid] == i) break;
                    else if  (ge[mid] < i) lo = mid+1;
                    else hi = mid-1;
                }
                if (lo > hi)
                    gt_abort(">E linegraphg : binary search failed\n");
                eno[j] = eno[mid];
            }
        }
    }

    SG_ALLOC(*h,hnv,hnde,"linegraphg");
    h->nv = hnv;
    h->nde = hnde;
    SG_VDE(h,hv,hd,he);

    for (i = 0; i < gnv; ++i) hd[i] = 2*gd[i];
    for (i = gnv; i < hnv; ++i) hd[i] = 2;

    for (i = 0; i < gnv; ++i)
    {
        for (j = gv[i]; j < gv[i]+gd[i]; ++j)
            hd[eno[j]] += gd[i]-1;
    }

    hv[0] = 0;
    for (i = 1; i < hnv; ++i) hv[i] = hv[i-1] + hd[i-1];
    if (hnde - hv[hnv-1] != hd[hnv-1])
        gt_abort(">E linegraphg: too many output edges\n");
    for (i = 0; i < hnv; ++i) hd[i] = 0;

    for (i = 0; i < gnv; ++i)
    {
        for (j = gv[i]; j < gv[i]+gd[i]; ++j)
        {
            v = eno[j];
            he[hv[i]+(hd[i]++)] = ge[j];
            he[hv[i]+(hd[i]++)] = v;
            he[hv[v]+(hd[v]++)] = i;
        }
    }

    for (i = 0; i < gnv; ++i)
    {
        for (jj = 0; jj < gd[i]-1; ++jj)
        {
            j = gv[i] + jj;
            for (k = j+1; k < gv[i]+gd[i]; ++k)
            {
                v = eno[j]; w = eno[k];
                he[hv[v]+(hd[v]++)] = w;
                he[hv[w]+(hd[w]++)] = v;
            }
        }
    }
}

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

int
main(int argc, char *argv[])
{
    char *infilename,*outfilename;
    FILE *infile,*outfile;
    boolean badargs,quiet,tswitch;
    int j,argnum;
    int codetype,outcode;
    sparsegraph g,h;
    nauty_counter nin,nullgraphs;
    char *arg,sw;
    double t;

    HELP; PUTVERSION;

    SG_INIT(g);
    SG_INIT(h);

    infilename = outfilename = NULL;
    quiet = tswitch = 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('t',tswitch)
                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 linegraphg");
        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";

    NODIGRAPHSYET(codetype);

    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                  outcode = GRAPH6;

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

    nin = 0;
    nullgraphs = 0;
    t = CPUTIME;
    while (read_sg(infile,&g))
    {
        ++nin;

        if (g.nde > 0 || tswitch)
        {
            if (tswitch) totalgraph(&g,&h);
            else         linegraph(&g,&h);
            if (outcode == SPARSE6) writes6_sg(outfile,&h);
            else                    writeg6_sg(outfile,&h);
        }
        else
            ++nullgraphs;
    }
    t = CPUTIME - t;

    if (!quiet)
    {
        if (nullgraphs > 0)
            fprintf(stderr,">W " COUNTER_FMT " null graphs not written\n",
                    nullgraphs);

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

    exit(0);
}
