Esempio n. 1
0
int glp_read_graph(glp_graph *G, const char *fname)
{   glp_data *data;
    jmp_buf jump;
    int nv, na, i, j, k, ret;
    glp_erase_graph(G, G->v_size, G->a_size);
    xprintf("Reading graph from `%s'...\n", fname);
    data = glp_sdf_open_file(fname);
    if (data == NULL)
    {   ret = 1;
        goto done;
    }
    if (setjmp(jump))
    {   ret = 1;
        goto done;
    }
    glp_sdf_set_jump(data, jump);
    nv = glp_sdf_read_int(data);
    if (nv < 0)
        glp_sdf_error(data, "invalid number of vertices\n");
    na = glp_sdf_read_int(data);
    if (na < 0)
        glp_sdf_error(data, "invalid number of arcs\n");
    xprintf("Graph has %d vert%s and %d arc%s\n",
            nv, nv == 1 ? "ex" : "ices", na, na == 1 ? "" : "s");
    if (nv > 0) glp_add_vertices(G, nv);
    for (k = 1; k <= na; k++)
    {   i = glp_sdf_read_int(data);
        if (!(1 <= i && i <= nv))
            glp_sdf_error(data, "tail vertex number out of range\n");
        j = glp_sdf_read_int(data);
        if (!(1 <= j && j <= nv))
            glp_sdf_error(data, "head vertex number out of range\n");
        glp_add_arc(G, i, j);
    }
    xprintf("%d lines were read\n", glp_sdf_line(data));
    ret = 0;
done:
    if (data != NULL) glp_sdf_close_file(data);
    return ret;
}
Esempio n. 2
0
int glp_netgen(glp_graph *G_, int _v_rhs, int _a_cap, int _a_cost,
      const int parm[1+15])
{     struct csa _csa, *csa = &_csa;
      int iseed, nprob, ntsorc, ntsink, iphic, i, nskel, nltr, ltsink,
         ntrans, npsink, nftr, npsorc, ntravl, ntrrem, lsorc, lpick,
         nsksr, nsrchn, j, item, l, ks, k, ksp, li, n, ii, it, ih, icap,
         jcap, icost, jcost, ret;
      G = G_;
      v_rhs = _v_rhs;
      a_cap = _a_cap;
      a_cost = _a_cost;
      if (G != NULL)
      {  if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double))
            xerror("glp_netgen: v_rhs = %d; invalid offset\n", v_rhs);
         if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
            xerror("glp_netgen: a_cap = %d; invalid offset\n", a_cap);
         if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
            xerror("glp_netgen: a_cost = %d; invalid offset\n", a_cost);
      }
      /* Input the user's random number seed and fix it if
         non-positive. */
      iseed = parm[1];
      nprob = parm[2];
      if (iseed <= 0) iseed = 13502460;
      setran(csa, iseed);
      /* Input the user's problem characteristics. */
      nodes = parm[3];
      nsorc = parm[4];
      nsink = parm[5];
      iarcs = parm[6];
      mincst = parm[7];
      maxcst = parm[8];
      itsup = parm[9];
      ntsorc = parm[10];
      ntsink = parm[11];
      iphic = parm[12];
      ipcap = parm[13];
      mincap = parm[14];
      maxcap = parm[15];
      /* Check the size of the problem. */
      if (!(10 <= nodes && nodes <= 100000))
      {  ret = 1;
         goto done;
      }
      /* Check user supplied parameters for consistency. */
      if (!(nsorc >= 0 && nsink >= 0 && nsorc + nsink <= nodes))
      {  ret = 2;
         goto done;
      }
      if (iarcs < 0)
      {  ret = 3;
         goto done;
      }
      if (mincst > maxcst)
      {  ret = 4;
         goto done;
      }
      if (itsup < 0)
      {  ret = 5;
         goto done;
      }
      if (!(0 <= ntsorc && ntsorc <= nsorc))
      {  ret = 6;
         goto done;
      }
      if (!(0 <= ntsink && ntsink <= nsink))
      {  ret = 7;
         goto done;
      }
      if (!(0 <= iphic && iphic <= 100))
      {  ret = 8;
         goto done;
      }
      if (!(0 <= ipcap && ipcap <= 100))
      {  ret = 9;
         goto done;
      }
      if (mincap > maxcap)
      {  ret = 10;
         goto done;
      }
      /* Initailize the graph object. */
      if (G != NULL)
      {  glp_erase_graph(G, G->v_size, G->a_size);
         glp_add_vertices(G, nodes);
         if (v_rhs >= 0)
         {  double zero = 0.0;
            for (i = 1; i <= nodes; i++)
            {  glp_vertex *v = G->v[i];
               memcpy((char *)v->data + v_rhs, &zero, sizeof(double));
            }
         }
      }
      /* Allocate working arrays. */
      ipred = xcalloc(1+nodes, sizeof(int));
      ihead = xcalloc(1+nodes, sizeof(int));
      itail = xcalloc(1+nodes, sizeof(int));
      iflag = xcalloc(1+nodes, sizeof(int));
      isup = xcalloc(1+nodes, sizeof(int));
      lsinks = xcalloc(1+nodes, sizeof(int));
      /* Print the problem documentation records. */
      if (G == NULL)
      {  xprintf("BEGIN\n");
         xprintf("NETGEN PROBLEM%8d%10s%10d NODES AND%10d ARCS\n",
            nprob, "", nodes, iarcs);
         xprintf("USER:%11d%11d%11d%11d%11d%11d\nDATA:%11d%11d%11d%11d%"
            "11d%11d\n", iseed, nsorc, nsink, mincst,
            maxcst, itsup, ntsorc, ntsink, iphic, ipcap,
            mincap, maxcap);
      }
      else
         glp_set_graph_name(G, "NETGEN");
      /* Set various constants used in the program. */
      narcs = 0;
      nskel = 0;
      nltr = nodes - nsink;
      ltsink = nltr + ntsink;
      ntrans = nltr - nsorc;
      nfsink = nltr + 1;
      nonsor = nodes - nsorc + ntsorc;
      npsink = nsink - ntsink;
      nodlft = nodes - nsink + ntsink;
      nftr = nsorc + 1;
      nftsor = nsorc - ntsorc + 1;
      npsorc = nsorc - ntsorc;
      /* Randomly distribute the supply among the source nodes. */
      if (npsorc + npsink == nodes && npsorc == npsink &&
          itsup == nsorc)
      {  assign(csa);
         nskel = nsorc;
         goto L390;
      }
      cresup(csa);
      /* Print the supply records. */
      if (G == NULL)
      {  xprintf("SUPPLY\n");
         for (i = 1; i <= nsorc; i++)
            xprintf("%6s%6d%18s%10d\n", "", i, "", isup[i]);
         xprintf("ARCS\n");
      }
      else
      {  if (v_rhs >= 0)
         {  for (i = 1; i <= nsorc; i++)
            {  double temp = (double)isup[i];
               glp_vertex *v = G->v[i];
               memcpy((char *)v->data + v_rhs, &temp, sizeof(double));
            }
         }
      }
      /* Make the sources point to themselves in ipred array. */
      for (i = 1; i <= nsorc; i++)
         ipred[i] = i;
      if (ntrans == 0) goto L170;
      /* Chain the transshipment nodes together in the ipred array. */
      ist = nftr;
      ipred[nltr] = 0;
      for (i = nftr; i < nltr; i++)
         ipred[i] = i+1;
      /* Form even length chains for 60 percent of the transshipments.*/
      ntravl = 6 * ntrans / 10;
      ntrrem = ntrans - ntravl;
L140: lsorc = 1;
      while (ntravl != 0)
      {  lpick = iran(csa, 1, ntravl + ntrrem);
         ntravl--;
         chain(csa, lpick, lsorc);
         if (lsorc == nsorc) goto L140;
         lsorc++;
      }
      /* Add the remaining transshipments to the chains. */
      while (ntrrem != 0)
      {
         lpick = iran(csa, 1, ntrrem);
         ntrrem--;
         lsorc = iran(csa, 1, nsorc);
         chain(csa, lpick, lsorc);
      }
L170: /* Set all demands equal to zero. */
      for (i = nfsink; i <= nodes; i++)
         ipred[i] = 0;
      /* The following loop takes one chain at a time (through the use
         of logic contained in the loop and calls to other routines) and
         creates the remaining network arcs. */
      for (lsorc = 1; lsorc <= nsorc; lsorc++)
      {  chnarc(csa, lsorc);
         for (i = nfsink; i <= nodes; i++)
            iflag[i] = 0;
         /* Choose the number of sinks to be hooked up to the current
            chain. */
         if (ntrans != 0)
            nsksr = (nsort * 2 * nsink) / ntrans;
         else
            nsksr = nsink / nsorc + 1;
         if (nsksr < 2) nsksr = 2;
         if (nsksr > nsink) nsksr = nsink;
         nsrchn = nsort;
         /* Randomly pick nsksr sinks and put their names in lsinks. */
         ktl = nsink;
         for (j = 1; j <= nsksr; j++)
         {  item = iran(csa, 1, ktl);
            ktl--;
            for (l = nfsink; l <= nodes; l++)
            {  if (iflag[l] != 1)
               {  item--;
                  if (item == 0) goto L230;
               }
            }
            break;
L230:       lsinks[j] = l;
            iflag[l] = 1;
         }
         /* If last source chain, add all sinks with zero demand to
            lsinks list. */
         if (lsorc == nsorc)
         {  for (j = nfsink; j <= nodes; j++)
            {  if (ipred[j] == 0 && iflag[j] != 1)
               {  nsksr++;
                  lsinks[nsksr] = j;
                  iflag[j] = 1;
               }
            }
         }
         /* Create demands for group of sinks in lsinks. */
         ks = isup[lsorc] / nsksr;
         k = ipred[lsorc];
         for (i = 1; i <= nsksr; i++)
         {  nsort++;
            ksp = iran(csa, 1, ks);
            j = iran(csa, 1, nsksr);
            itail[nsort] = k;
            li = lsinks[i];
            ihead[nsort] = li;
            ipred[li] += ksp;
            li = lsinks[j];
            ipred[li] += ks - ksp;
            n = iran(csa, 1, nsrchn);
            k = lsorc;
            for (ii = 1; ii <= n; ii++)
               k = ipred[k];
         }
         li = lsinks[1];
         ipred[li] += isup[lsorc] - ks * nsksr;
         nskel += nsort;
         /* Sort the arcs in the chain from source lsorc using itail as
            sort key. */
         sort(csa);
         /* Print this part of skeleton and create the arcs for these
            nodes. */
         i = 1;
         itail[nsort+1] = 0;
L300:    for (j = nftsor; j <= nodes; j++)
            iflag[j] = 0;
         ktl = nonsor - 1;
         it = itail[i];
         iflag[it] = 1;
L320:    ih = ihead[i];
         iflag[ih] = 1;
         narcs++;
         ktl--;
         /* Determine if this skeleton arc should be capacitated. */
         icap = itsup;
         jcap = iran(csa, 1, 100);
         if (jcap <= ipcap)
         {  icap = isup[lsorc];
            if (mincap > icap) icap = mincap;
         }
         /* Determine if this skeleton arc should have the maximum
            cost. */
         icost = maxcst;
         jcost = iran(csa, 1, 100);
         if (jcost > iphic)
            icost = iran(csa, mincst, maxcst);
         if (G == NULL)
            xprintf("%6s%6d%6d%2s%10d%10d\n", "", it, ih, "", icost,
               icap);
         else
         {  glp_arc *a = glp_add_arc(G, it, ih);
            if (a_cap >= 0)
            {  double temp = (double)icap;
               memcpy((char *)a->data + a_cap, &temp, sizeof(double));
            }
            if (a_cost >= 0)
            {  double temp = (double)icost;
               memcpy((char *)a->data + a_cost, &temp, sizeof(double));
            }
         }
         i++;
         if (itail[i] == it) goto L320;
         pickj(csa, it);
         if (i <= nsort) goto L300;
      }
      /* Create arcs from the transshipment sinks. */
      if (ntsink != 0)
      {  for (i = nfsink; i <= ltsink; i++)
         {  for (j = nftsor; j <= nodes; j++)
               iflag[j] = 0;
            ktl = nonsor - 1;
            iflag[i] = 1;
            pickj(csa, i);
         }
      }
L390: /* Print the demand records and end record. */
      if (G == NULL)
      {  xprintf("DEMAND\n");
         for (i = nfsink; i <= nodes; i++)
            xprintf("%6s%6d%18s%10d\n", "", i, "", ipred[i]);
         xprintf("END\n");
      }
      else
      {  if (v_rhs >= 0)
         {  for (i = nfsink; i <= nodes; i++)
            {  double temp = - (double)ipred[i];
               glp_vertex *v = G->v[i];
               memcpy((char *)v->data + v_rhs, &temp, sizeof(double));
            }
         }
      }
      /* Free working arrays. */
      xfree(ipred);
      xfree(ihead);
      xfree(itail);
      xfree(iflag);
      xfree(isup);
      xfree(lsinks);
      /* The instance has been successfully generated. */
      ret = 0;
done: return ret;
}
Esempio n. 3
0
int kellerman(int n, int (*func)(void *info, int i, int ind[]),
      void *info, void /* glp_graph */ *H_)
{     glp_graph *H = H_;
      struct set W_, *W = &W_, V_, *V = &V_;
      glp_arc *a;
      int i, j, k, m, t, len, card, best;
      xassert(n >= 0);
      /* H := (V, 0; 0), where V is the set of vertices of graph G */
      glp_erase_graph(H, H->v_size, H->a_size);
      glp_add_vertices(H, n);
      /* W := 0 */
      W->size = 0;
      W->list = xcalloc(1+n, sizeof(int));
      W->pos = xcalloc(1+n, sizeof(int));
      memset(&W->pos[1], 0, sizeof(int) * n);
      /* V := 0 */
      V->size = 0;
      V->list = xcalloc(1+n, sizeof(int));
      V->pos = xcalloc(1+n, sizeof(int));
      memset(&V->pos[1], 0, sizeof(int) * n);
      /* main loop */
      for (i = 1; i <= n; i++)
      {  /* W must be empty */
         xassert(W->size == 0);
         /* W := { j : i > j and (i,j) in E } */
         len = func(info, i, W->list);
         xassert(0 <= len && len <= n);
         for (t = 1; t <= len; t++)
         {  j = W->list[t];
            xassert(1 <= j && j <= n);
            if (j >= i) continue;
            xassert(W->pos[j] == 0);
            W->list[++W->size] = j, W->pos[j] = W->size;
         }
         /* on i-th iteration we need to cover edges (i,j) for all
            j in W */
         /* if W is empty, it is a special case */
         if (W->size == 0)
         {  /* set k := k + 1 and create new clique C[k] = { i } */
            k = glp_add_vertices(H, 1) - n;
            glp_add_arc(H, i, n + k);
            continue;
         }
         /* try to include vertex i into existing cliques */
         /* V must be empty */
         xassert(V->size == 0);
         /* k is the number of cliques found so far */
         k = H->nv - n;
         for (m = 1; m <= k; m++)
         {  /* do while V != W; since here V is within W, we can use
               equivalent condition: do while |V| < |W| */
            if (V->size == W->size) break;
            /* check if C[m] is within W */
            for (a = H->v[n + m]->in; a != NULL; a = a->h_next)
            {  j = a->tail->i;
               if (W->pos[j] == 0) break;
            }
            if (a != NULL) continue;
            /* C[m] is within W, expand clique C[m] with vertex i */
            /* C[m] := C[m] union {i} */
            glp_add_arc(H, i, n + m);
            /* V is a set of vertices whose incident edges are already
               covered by existing cliques */
            /* V := V union C[m] */
            for (a = H->v[n + m]->in; a != NULL; a = a->h_next)
            {  j = a->tail->i;
               if (V->pos[j] == 0)
                  V->list[++V->size] = j, V->pos[j] = V->size;
            }
         }
         /* remove from set W the vertices whose incident edges are
            already covered by existing cliques */
         /* W := W \ V, V := 0 */
         for (t = 1; t <= V->size; t++)
         {  j = V->list[t], V->pos[j] = 0;
            if (W->pos[j] != 0)
            {  /* remove vertex j from W */
               if (W->pos[j] != W->size)
               {  int jj = W->list[W->size];
                  W->list[W->pos[j]] = jj;
                  W->pos[jj] = W->pos[j];
               }
               W->size--, W->pos[j] = 0;
            }
         }
         V->size = 0;
         /* now set W contains only vertices whose incident edges are
            still not covered by existing cliques; create new cliques
            to cover remaining edges until set W becomes empty */
         while (W->size > 0)
         {  /* find clique C[m], 1 <= m <= k, which shares maximal
               number of vertices with W; to break ties choose clique
               having smallest number m */
            m = 0, best = -1;
            k = H->nv - n;
            for (t = 1; t <= k; t++)
            {  /* compute cardinality of intersection of W and C[t] */
               card = 0;
               for (a = H->v[n + t]->in; a != NULL; a = a->h_next)
               {  j = a->tail->i;
                  if (W->pos[j] != 0) card++;
               }
               if (best < card)
                  m = t, best = card;
            }
            xassert(m > 0);
            /* set k := k + 1 and create new clique:
               C[k] := (W intersect C[m]) union { i }, which covers all
               edges incident to vertices from (W intersect C[m]) */
            k = glp_add_vertices(H, 1) - n;
            for (a = H->v[n + m]->in; a != NULL; a = a->h_next)
            {  j = a->tail->i;
               if (W->pos[j] != 0)
               {  /* vertex j is in both W and C[m]; include it in new
                     clique C[k] */
                  glp_add_arc(H, j, n + k);
                  /* remove vertex j from W, since edge (i,j) will be
                     covered by new clique C[k] */
                  if (W->pos[j] != W->size)
                  {  int jj = W->list[W->size];
                     W->list[W->pos[j]] = jj;
                     W->pos[jj] = W->pos[j];
                  }
                  W->size--, W->pos[j] = 0;
               }
            }
            /* include vertex i to new clique C[k] to cover edges (i,j)
               incident to all vertices j just removed from W */
            glp_add_arc(H, i, n + k);
         }
      }
      /* free working arrays */
      xfree(W->list);
      xfree(W->pos);
      xfree(V->list);
      xfree(V->pos);
      /* return the number of cliques in the edge covering found */
      return H->nv - n;
}
Esempio n. 4
0
int glp_read_maxflow(glp_graph *G, int *_s, int *_t, int a_cap,
      const char *fname)
{     struct csa _csa, *csa = &_csa;
      glp_arc *a;
      int i, j, k, s, t, nv, na, ret = 0;
      double cap;
      if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
         xerror("glp_read_maxflow: a_cap = %d; invalid offset\n",
            a_cap);
      glp_erase_graph(G, G->v_size, G->a_size);
      if (setjmp(csa->jump))
      {  ret = 1;
         goto done;
      }
      csa->fname = fname;
      csa->fp = NULL;
      csa->count = 0;
      csa->c = '\n';
      csa->field[0] = '\0';
      csa->empty = csa->nonint = 0;
      xprintf("Reading maximum flow problem data from `%s'...\n",
         fname);
      csa->fp = xfopen(fname, "r");
      if (csa->fp == NULL)
      {  xprintf("Unable to open `%s' - %s\n", fname, xerrmsg());
         longjmp(csa->jump, 1);
      }
      /* read problem line */
      read_designator(csa);
      if (strcmp(csa->field, "p") != 0)
         error(csa, "problem line missing or invalid");
      read_field(csa);
      if (strcmp(csa->field, "max") != 0)
         error(csa, "wrong problem designator; `max' expected");
      read_field(csa);
      if (!(str2int(csa->field, &nv) == 0 && nv >= 2))
         error(csa, "number of nodes missing or invalid");
      read_field(csa);
      if (!(str2int(csa->field, &na) == 0 && na >= 0))
         error(csa, "number of arcs missing or invalid");
      xprintf("Flow network has %d node%s and %d arc%s\n",
         nv, nv == 1 ? "" : "s", na, na == 1 ? "" : "s");
      if (nv > 0) glp_add_vertices(G, nv);
      end_of_line(csa);
      /* read node descriptor lines */
      s = t = 0;
      for (;;)
      {  read_designator(csa);
         if (strcmp(csa->field, "n") != 0) break;
         read_field(csa);
         if (str2int(csa->field, &i) != 0)
            error(csa, "node number missing or invalid");
         if (!(1 <= i && i <= nv))
            error(csa, "node number %d out of range", i);
         read_field(csa);
         if (strcmp(csa->field, "s") == 0)
         {  if (s > 0)
               error(csa, "only one source node allowed");
            s = i;
         }
         else if (strcmp(csa->field, "t") == 0)
         {  if (t > 0)
               error(csa, "only one sink node allowed");
            t = i;
         }
         else
            error(csa, "wrong node designator; `s' or `t' expected");
         if (s > 0 && s == t)
            error(csa, "source and sink nodes must be distinct");
         end_of_line(csa);
      }
      if (s == 0)
         error(csa, "source node descriptor missing\n");
      if (t == 0)
         error(csa, "sink node descriptor missing\n");
      if (_s != NULL) *_s = s;
      if (_t != NULL) *_t = t;
      /* read arc descriptor lines */
      for (k = 1; k <= na; k++)
      {  if (k > 1) read_designator(csa);
         if (strcmp(csa->field, "a") != 0)
            error(csa, "wrong line designator; `a' expected");
         read_field(csa);
         if (str2int(csa->field, &i) != 0)
            error(csa, "starting node number missing or invalid");
         if (!(1 <= i && i <= nv))
            error(csa, "starting node number %d out of range", i);
         read_field(csa);
         if (str2int(csa->field, &j) != 0)
            error(csa, "ending node number missing or invalid");
         if (!(1 <= j && j <= nv))
            error(csa, "ending node number %d out of range", j);
         read_field(csa);
         if (!(str2num(csa->field, &cap) == 0 && cap >= 0.0))
            error(csa, "arc capacity missing or invalid");
         check_int(csa, cap);
         a = glp_add_arc(G, i, j);
         if (a_cap >= 0)
            memcpy((char *)a->data + a_cap, &cap, sizeof(double));
         end_of_line(csa);
      }
      xprintf("%d lines were read\n", csa->count);
done: if (ret) glp_erase_graph(G, G->v_size, G->a_size);
      if (csa->fp != NULL) xfclose(csa->fp);
      return ret;
}
Esempio n. 5
0
int glp_read_mincost(glp_graph *G, int v_rhs, int a_low, int a_cap,
      int a_cost, const char *fname)
{     struct csa _csa, *csa = &_csa;
      glp_vertex *v;
      glp_arc *a;
      int i, j, k, nv, na, ret = 0;
      double rhs, low, cap, cost;
      char *flag = NULL;
      if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double))
         xerror("glp_read_mincost: v_rhs = %d; invalid offset\n",
            v_rhs);
      if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double))
         xerror("glp_read_mincost: a_low = %d; invalid offset\n",
            a_low);
      if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
         xerror("glp_read_mincost: a_cap = %d; invalid offset\n",
            a_cap);
      if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
         xerror("glp_read_mincost: a_cost = %d; invalid offset\n",
            a_cost);
      glp_erase_graph(G, G->v_size, G->a_size);
      if (setjmp(csa->jump))
      {  ret = 1;
         goto done;
      }
      csa->fname = fname;
      csa->fp = NULL;
      csa->count = 0;
      csa->c = '\n';
      csa->field[0] = '\0';
      csa->empty = csa->nonint = 0;
      xprintf("Reading min-cost flow problem data from `%s'...\n",
         fname);
      csa->fp = xfopen(fname, "r");
      if (csa->fp == NULL)
      {  xprintf("Unable to open `%s' - %s\n", fname, xerrmsg());
         longjmp(csa->jump, 1);
      }
      /* read problem line */
      read_designator(csa);
      if (strcmp(csa->field, "p") != 0)
         error(csa, "problem line missing or invalid");
      read_field(csa);
      if (strcmp(csa->field, "min") != 0)
         error(csa, "wrong problem designator; `min' expected");
      read_field(csa);
      if (!(str2int(csa->field, &nv) == 0 && nv >= 0))
         error(csa, "number of nodes missing or invalid");
      read_field(csa);
      if (!(str2int(csa->field, &na) == 0 && na >= 0))
         error(csa, "number of arcs missing or invalid");
      xprintf("Flow network has %d node%s and %d arc%s\n",
         nv, nv == 1 ? "" : "s", na, na == 1 ? "" : "s");
      if (nv > 0) glp_add_vertices(G, nv);
      end_of_line(csa);
      /* read node descriptor lines */
      flag = xcalloc(1+nv, sizeof(char));
      memset(&flag[1], 0, nv * sizeof(char));
      if (v_rhs >= 0)
      {  rhs = 0.0;
         for (i = 1; i <= nv; i++)
         {  v = G->v[i];
            memcpy((char *)v->data + v_rhs, &rhs, sizeof(double));
         }
      }
      for (;;)
      {  read_designator(csa);
         if (strcmp(csa->field, "n") != 0) break;
         read_field(csa);
         if (str2int(csa->field, &i) != 0)
            error(csa, "node number missing or invalid");
         if (!(1 <= i && i <= nv))
            error(csa, "node number %d out of range", i);
         if (flag[i])
            error(csa, "duplicate descriptor of node %d", i);
         read_field(csa);
         if (str2num(csa->field, &rhs) != 0)
            error(csa, "node supply/demand missing or invalid");
         check_int(csa, rhs);
         if (v_rhs >= 0)
         {  v = G->v[i];
            memcpy((char *)v->data + v_rhs, &rhs, sizeof(double));
         }
         flag[i] = 1;
         end_of_line(csa);
      }
      xfree(flag), flag = NULL;
      /* read arc descriptor lines */
      for (k = 1; k <= na; k++)
      {  if (k > 1) read_designator(csa);
         if (strcmp(csa->field, "a") != 0)
            error(csa, "wrong line designator; `a' expected");
         read_field(csa);
         if (str2int(csa->field, &i) != 0)
            error(csa, "starting node number missing or invalid");
         if (!(1 <= i && i <= nv))
            error(csa, "starting node number %d out of range", i);
         read_field(csa);
         if (str2int(csa->field, &j) != 0)
            error(csa, "ending node number missing or invalid");
         if (!(1 <= j && j <= nv))
            error(csa, "ending node number %d out of range", j);
         read_field(csa);
         if (!(str2num(csa->field, &low) == 0 && low >= 0.0))
            error(csa, "lower bound of arc flow missing or invalid");
         check_int(csa, low);
         read_field(csa);
         if (!(str2num(csa->field, &cap) == 0 && cap >= low))
            error(csa, "upper bound of arc flow missing or invalid");
         check_int(csa, cap);
         read_field(csa);
         if (str2num(csa->field, &cost) != 0)
            error(csa, "per-unit cost of arc flow missing or invalid");
         check_int(csa, cost);
         a = glp_add_arc(G, i, j);
         if (a_low >= 0)
            memcpy((char *)a->data + a_low, &low, sizeof(double));
         if (a_cap >= 0)
            memcpy((char *)a->data + a_cap, &cap, sizeof(double));
         if (a_cost >= 0)
            memcpy((char *)a->data + a_cost, &cost, sizeof(double));
         end_of_line(csa);
      }
      xprintf("%d lines were read\n", csa->count);
done: if (ret) glp_erase_graph(G, G->v_size, G->a_size);
      if (csa->fp != NULL) xfclose(csa->fp);
      if (flag != NULL) xfree(flag);
      return ret;
}