alpar@9: /* glpqmd.c (quotient minimum degree algorithm) */ alpar@9: alpar@9: /*********************************************************************** alpar@9: * This code is part of GLPK (GNU Linear Programming Kit). alpar@9: * alpar@9: * THIS CODE IS THE RESULT OF TRANSLATION OF THE FORTRAN SUBROUTINES alpar@9: * GENQMD, QMDRCH, QMDQT, QMDUPD, AND QMDMRG FROM THE BOOK: alpar@9: * alpar@9: * ALAN GEORGE, JOSEPH W-H LIU. COMPUTER SOLUTION OF LARGE SPARSE alpar@9: * POSITIVE DEFINITE SYSTEMS. PRENTICE-HALL, 1981. alpar@9: * alpar@9: * THE TRANSLATION HAS BEEN DONE WITH THE PERMISSION OF THE AUTHORS alpar@9: * OF THE ORIGINAL FORTRAN SUBROUTINES: ALAN GEORGE AND JOSEPH LIU, alpar@9: * UNIVERSITY OF WATERLOO, WATERLOO, ONTARIO, CANADA. alpar@9: * alpar@9: * The translation was made by Andrew Makhorin . alpar@9: * alpar@9: * GLPK is free software: you can redistribute it and/or modify it alpar@9: * under the terms of the GNU General Public License as published by alpar@9: * the Free Software Foundation, either version 3 of the License, or alpar@9: * (at your option) any later version. alpar@9: * alpar@9: * GLPK is distributed in the hope that it will be useful, but WITHOUT alpar@9: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY alpar@9: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public alpar@9: * License for more details. alpar@9: * alpar@9: * You should have received a copy of the GNU General Public License alpar@9: * along with GLPK. If not, see . alpar@9: ***********************************************************************/ alpar@9: alpar@9: #include "glpqmd.h" alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * genqmd - GENeral Quotient Minimum Degree algorithm alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * #include "glpqmd.h" alpar@9: * void genqmd(int *neqns, int xadj[], int adjncy[], int perm[], alpar@9: * int invp[], int deg[], int marker[], int rchset[], int nbrhd[], alpar@9: * int qsize[], int qlink[], int *nofsub); alpar@9: * alpar@9: * PURPOSE alpar@9: * alpar@9: * This routine implements the minimum degree algorithm. It makes use alpar@9: * of the implicit representation of the elimination graph by quotient alpar@9: * graphs, and the notion of indistinguishable nodes. alpar@9: * alpar@9: * CAUTION alpar@9: * alpar@9: * The adjancy vector adjncy will be destroyed. alpar@9: * alpar@9: * INPUT PARAMETERS alpar@9: * alpar@9: * neqns - number of equations; alpar@9: * (xadj, adjncy) - alpar@9: * the adjancy structure. alpar@9: * alpar@9: * OUTPUT PARAMETERS alpar@9: * alpar@9: * perm - the minimum degree ordering; alpar@9: * invp - the inverse of perm. alpar@9: * alpar@9: * WORKING PARAMETERS alpar@9: * alpar@9: * deg - the degree vector. deg[i] is negative means node i has been alpar@9: * numbered; alpar@9: * marker - a marker vector, where marker[i] is negative means node i alpar@9: * has been merged with another nodeand thus can be ignored; alpar@9: * rchset - vector used for the reachable set; alpar@9: * nbrhd - vector used for neighborhood set; alpar@9: * qsize - vector used to store the size of indistinguishable alpar@9: * supernodes; alpar@9: * qlink - vector used to store indistinguishable nodes, i, qlink[i], alpar@9: * qlink[qlink[i]], ... are the members of the supernode alpar@9: * represented by i. alpar@9: * alpar@9: * PROGRAM SUBROUTINES alpar@9: * alpar@9: * qmdrch, qmdqt, qmdupd. alpar@9: ***********************************************************************/ alpar@9: alpar@9: void genqmd(int *_neqns, int xadj[], int adjncy[], int perm[], alpar@9: int invp[], int deg[], int marker[], int rchset[], int nbrhd[], alpar@9: int qsize[], int qlink[], int *_nofsub) alpar@9: { int inode, ip, irch, j, mindeg, ndeg, nhdsze, node, np, num, alpar@9: nump1, nxnode, rchsze, search, thresh; alpar@9: # define neqns (*_neqns) alpar@9: # define nofsub (*_nofsub) alpar@9: /* Initialize degree vector and other working variables. */ alpar@9: mindeg = neqns; alpar@9: nofsub = 0; alpar@9: for (node = 1; node <= neqns; node++) alpar@9: { perm[node] = node; alpar@9: invp[node] = node; alpar@9: marker[node] = 0; alpar@9: qsize[node] = 1; alpar@9: qlink[node] = 0; alpar@9: ndeg = xadj[node+1] - xadj[node]; alpar@9: deg[node] = ndeg; alpar@9: if (ndeg < mindeg) mindeg = ndeg; alpar@9: } alpar@9: num = 0; alpar@9: /* Perform threshold search to get a node of min degree. alpar@9: Variable search point to where search should start. */ alpar@9: s200: search = 1; alpar@9: thresh = mindeg; alpar@9: mindeg = neqns; alpar@9: s300: nump1 = num + 1; alpar@9: if (nump1 > search) search = nump1; alpar@9: for (j = search; j <= neqns; j++) alpar@9: { node = perm[j]; alpar@9: if (marker[node] >= 0) alpar@9: { ndeg = deg[node]; alpar@9: if (ndeg <= thresh) goto s500; alpar@9: if (ndeg < mindeg) mindeg = ndeg; alpar@9: } alpar@9: } alpar@9: goto s200; alpar@9: /* Node has minimum degree. Find its reachable sets by calling alpar@9: qmdrch. */ alpar@9: s500: search = j; alpar@9: nofsub += deg[node]; alpar@9: marker[node] = 1; alpar@9: qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, &nhdsze, alpar@9: nbrhd); alpar@9: /* Eliminate all nodes indistinguishable from node. They are given alpar@9: by node, qlink[node], ... . */ alpar@9: nxnode = node; alpar@9: s600: num++; alpar@9: np = invp[nxnode]; alpar@9: ip = perm[num]; alpar@9: perm[np] = ip; alpar@9: invp[ip] = np; alpar@9: perm[num] = nxnode; alpar@9: invp[nxnode] = num; alpar@9: deg[nxnode] = -1; alpar@9: nxnode = qlink[nxnode]; alpar@9: if (nxnode > 0) goto s600; alpar@9: if (rchsze > 0) alpar@9: { /* Update the degrees of the nodes in the reachable set and alpar@9: identify indistinguishable nodes. */ alpar@9: qmdupd(xadj, adjncy, &rchsze, rchset, deg, qsize, qlink, alpar@9: marker, &rchset[rchsze+1], &nbrhd[nhdsze+1]); alpar@9: /* Reset marker value of nodes in reach set. Update threshold alpar@9: value for cyclic search. Also call qmdqt to form new alpar@9: quotient graph. */ alpar@9: marker[node] = 0; alpar@9: for (irch = 1; irch <= rchsze; irch++) alpar@9: { inode = rchset[irch]; alpar@9: if (marker[inode] >= 0) alpar@9: { marker[inode] = 0; alpar@9: ndeg = deg[inode]; alpar@9: if (ndeg < mindeg) mindeg = ndeg; alpar@9: if (ndeg <= thresh) alpar@9: { mindeg = thresh; alpar@9: thresh = ndeg; alpar@9: search = invp[inode]; alpar@9: } alpar@9: } alpar@9: } alpar@9: if (nhdsze > 0) alpar@9: qmdqt(&node, xadj, adjncy, marker, &rchsze, rchset, nbrhd); alpar@9: } alpar@9: if (num < neqns) goto s300; alpar@9: return; alpar@9: # undef neqns alpar@9: # undef nofsub alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * qmdrch - Quotient MD ReaCHable set alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * #include "glpqmd.h" alpar@9: * void qmdrch(int *root, int xadj[], int adjncy[], int deg[], alpar@9: * int marker[], int *rchsze, int rchset[], int *nhdsze, alpar@9: * int nbrhd[]); alpar@9: * alpar@9: * PURPOSE alpar@9: * alpar@9: * This subroutine determines the reachable set of a node through a alpar@9: * given subset. The adjancy structure is assumed to be stored in a alpar@9: * quotient graph format. alpar@9: * alpar@9: * INPUT PARAMETERS alpar@9: * alpar@9: * root - the given node not in the subset; alpar@9: * (xadj, adjncy) - alpar@9: * the adjancy structure pair; alpar@9: * deg - the degree vector. deg[i] < 0 means the node belongs to the alpar@9: * given subset. alpar@9: * alpar@9: * OUTPUT PARAMETERS alpar@9: * alpar@9: * (rchsze, rchset) - alpar@9: * the reachable set; alpar@9: * (nhdsze, nbrhd) - alpar@9: * the neighborhood set. alpar@9: * alpar@9: * UPDATED PARAMETERS alpar@9: * alpar@9: * marker - the marker vector for reach and nbrhd sets. > 0 means the alpar@9: * node is in reach set. < 0 means the node has been merged alpar@9: * with others in the quotient or it is in nbrhd set. alpar@9: ***********************************************************************/ alpar@9: alpar@9: void qmdrch(int *_root, int xadj[], int adjncy[], int deg[], alpar@9: int marker[], int *_rchsze, int rchset[], int *_nhdsze, alpar@9: int nbrhd[]) alpar@9: { int i, istop, istrt, j, jstop, jstrt, nabor, node; alpar@9: # define root (*_root) alpar@9: # define rchsze (*_rchsze) alpar@9: # define nhdsze (*_nhdsze) alpar@9: /* Loop through the neighbors of root in the quotient graph. */ alpar@9: nhdsze = 0; alpar@9: rchsze = 0; alpar@9: istrt = xadj[root]; alpar@9: istop = xadj[root+1] - 1; alpar@9: if (istop < istrt) return; alpar@9: for (i = istrt; i <= istop; i++) alpar@9: { nabor = adjncy[i]; alpar@9: if (nabor == 0) return; alpar@9: if (marker[nabor] == 0) alpar@9: { if (deg[nabor] >= 0) alpar@9: { /* Include nabor into the reachable set. */ alpar@9: rchsze++; alpar@9: rchset[rchsze] = nabor; alpar@9: marker[nabor] = 1; alpar@9: goto s600; alpar@9: } alpar@9: /* nabor has been eliminated. Find nodes reachable from alpar@9: it. */ alpar@9: marker[nabor] = -1; alpar@9: nhdsze++; alpar@9: nbrhd[nhdsze] = nabor; alpar@9: s300: jstrt = xadj[nabor]; alpar@9: jstop = xadj[nabor+1] - 1; alpar@9: for (j = jstrt; j <= jstop; j++) alpar@9: { node = adjncy[j]; alpar@9: nabor = - node; alpar@9: if (node < 0) goto s300; alpar@9: if (node == 0) goto s600; alpar@9: if (marker[node] == 0) alpar@9: { rchsze++; alpar@9: rchset[rchsze] = node; alpar@9: marker[node] = 1; alpar@9: } alpar@9: } alpar@9: } alpar@9: s600: ; alpar@9: } alpar@9: return; alpar@9: # undef root alpar@9: # undef rchsze alpar@9: # undef nhdsze alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * qmdqt - Quotient MD Quotient graph Transformation alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * #include "glpqmd.h" alpar@9: * void qmdqt(int *root, int xadj[], int adjncy[], int marker[], alpar@9: * int *rchsze, int rchset[], int nbrhd[]); alpar@9: * alpar@9: * PURPOSE alpar@9: * alpar@9: * This subroutine performs the quotient graph transformation after a alpar@9: * node has been eliminated. alpar@9: * alpar@9: * INPUT PARAMETERS alpar@9: * alpar@9: * root - the node just eliminated. It becomes the representative of alpar@9: * the new supernode; alpar@9: * (xadj, adjncy) - alpar@9: * the adjancy structure; alpar@9: * (rchsze, rchset) - alpar@9: * the reachable set of root in the old quotient graph; alpar@9: * nbrhd - the neighborhood set which will be merged with root to form alpar@9: * the new supernode; alpar@9: * marker - the marker vector. alpar@9: * alpar@9: * UPDATED PARAMETERS alpar@9: * alpar@9: * adjncy - becomes the adjncy of the quotient graph. alpar@9: ***********************************************************************/ alpar@9: alpar@9: void qmdqt(int *_root, int xadj[], int adjncy[], int marker[], alpar@9: int *_rchsze, int rchset[], int nbrhd[]) alpar@9: { int inhd, irch, j, jstop, jstrt, link, nabor, node; alpar@9: # define root (*_root) alpar@9: # define rchsze (*_rchsze) alpar@9: irch = 0; alpar@9: inhd = 0; alpar@9: node = root; alpar@9: s100: jstrt = xadj[node]; alpar@9: jstop = xadj[node+1] - 2; alpar@9: if (jstop >= jstrt) alpar@9: { /* Place reach nodes into the adjacent list of node. */ alpar@9: for (j = jstrt; j <= jstop; j++) alpar@9: { irch++; alpar@9: adjncy[j] = rchset[irch]; alpar@9: if (irch >= rchsze) goto s400; alpar@9: } alpar@9: } alpar@9: /* Link to other space provided by the nbrhd set. */ alpar@9: link = adjncy[jstop+1]; alpar@9: node = - link; alpar@9: if (link >= 0) alpar@9: { inhd++; alpar@9: node = nbrhd[inhd]; alpar@9: adjncy[jstop+1] = - node; alpar@9: } alpar@9: goto s100; alpar@9: /* All reachable nodes have been saved. End the adjacent list. alpar@9: Add root to the neighborhood list of each node in the reach alpar@9: set. */ alpar@9: s400: adjncy[j+1] = 0; alpar@9: for (irch = 1; irch <= rchsze; irch++) alpar@9: { node = rchset[irch]; alpar@9: if (marker[node] >= 0) alpar@9: { jstrt = xadj[node]; alpar@9: jstop = xadj[node+1] - 1; alpar@9: for (j = jstrt; j <= jstop; j++) alpar@9: { nabor = adjncy[j]; alpar@9: if (marker[nabor] < 0) alpar@9: { adjncy[j] = root; alpar@9: goto s600; alpar@9: } alpar@9: } alpar@9: } alpar@9: s600: ; alpar@9: } alpar@9: return; alpar@9: # undef root alpar@9: # undef rchsze alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * qmdupd - Quotient MD UPDate alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * #include "glpqmd.h" alpar@9: * void qmdupd(int xadj[], int adjncy[], int *nlist, int list[], alpar@9: * int deg[], int qsize[], int qlink[], int marker[], int rchset[], alpar@9: * int nbrhd[]); alpar@9: * alpar@9: * PURPOSE alpar@9: * alpar@9: * This routine performs degree update for a set of nodes in the minimum alpar@9: * degree algorithm. alpar@9: * alpar@9: * INPUT PARAMETERS alpar@9: * alpar@9: * (xadj, adjncy) - alpar@9: * the adjancy structure; alpar@9: * (nlist, list) - alpar@9: * the list of nodes whose degree has to be updated. alpar@9: * alpar@9: * UPDATED PARAMETERS alpar@9: * alpar@9: * deg - the degree vector; alpar@9: * qsize - size of indistinguishable supernodes; alpar@9: * qlink - linked list for indistinguishable nodes; alpar@9: * marker - used to mark those nodes in reach/nbrhd sets. alpar@9: * alpar@9: * WORKING PARAMETERS alpar@9: * alpar@9: * rchset - the reachable set; alpar@9: * nbrhd - the neighborhood set. alpar@9: * alpar@9: * PROGRAM SUBROUTINES alpar@9: * alpar@9: * qmdmrg. alpar@9: ***********************************************************************/ alpar@9: alpar@9: void qmdupd(int xadj[], int adjncy[], int *_nlist, int list[], alpar@9: int deg[], int qsize[], int qlink[], int marker[], int rchset[], alpar@9: int nbrhd[]) alpar@9: { int deg0, deg1, il, inhd, inode, irch, j, jstop, jstrt, mark, alpar@9: nabor, nhdsze, node, rchsze; alpar@9: # define nlist (*_nlist) alpar@9: /* Find all eliminated supernodes that are adjacent to some nodes alpar@9: in the given list. Put them into (nhdsze, nbrhd). deg0 contains alpar@9: the number of nodes in the list. */ alpar@9: if (nlist <= 0) return; alpar@9: deg0 = 0; alpar@9: nhdsze = 0; alpar@9: for (il = 1; il <= nlist; il++) alpar@9: { node = list[il]; alpar@9: deg0 += qsize[node]; alpar@9: jstrt = xadj[node]; alpar@9: jstop = xadj[node+1] - 1; alpar@9: for (j = jstrt; j <= jstop; j++) alpar@9: { nabor = adjncy[j]; alpar@9: if (marker[nabor] == 0 && deg[nabor] < 0) alpar@9: { marker[nabor] = -1; alpar@9: nhdsze++; alpar@9: nbrhd[nhdsze] = nabor; alpar@9: } alpar@9: } alpar@9: } alpar@9: /* Merge indistinguishable nodes in the list by calling the alpar@9: subroutine qmdmrg. */ alpar@9: if (nhdsze > 0) alpar@9: qmdmrg(xadj, adjncy, deg, qsize, qlink, marker, °0, &nhdsze, alpar@9: nbrhd, rchset, &nbrhd[nhdsze+1]); alpar@9: /* Find the new degrees of the nodes that have not been merged. */ alpar@9: for (il = 1; il <= nlist; il++) alpar@9: { node = list[il]; alpar@9: mark = marker[node]; alpar@9: if (mark == 0 || mark == 1) alpar@9: { marker[node] = 2; alpar@9: qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, alpar@9: &nhdsze, nbrhd); alpar@9: deg1 = deg0; alpar@9: if (rchsze > 0) alpar@9: { for (irch = 1; irch <= rchsze; irch++) alpar@9: { inode = rchset[irch]; alpar@9: deg1 += qsize[inode]; alpar@9: marker[inode] = 0; alpar@9: } alpar@9: } alpar@9: deg[node] = deg1 - 1; alpar@9: if (nhdsze > 0) alpar@9: { for (inhd = 1; inhd <= nhdsze; inhd++) alpar@9: { inode = nbrhd[inhd]; alpar@9: marker[inode] = 0; alpar@9: } alpar@9: } alpar@9: } alpar@9: } alpar@9: return; alpar@9: # undef nlist alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * qmdmrg - Quotient MD MeRGe alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * #include "qmdmrg.h" alpar@9: * void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[], alpar@9: * int qlink[], int marker[], int *deg0, int *nhdsze, int nbrhd[], alpar@9: * int rchset[], int ovrlp[]); alpar@9: * alpar@9: * PURPOSE alpar@9: * alpar@9: * This routine merges indistinguishable nodes in the minimum degree alpar@9: * ordering algorithm. It also computes the new degrees of these new alpar@9: * supernodes. alpar@9: * alpar@9: * INPUT PARAMETERS alpar@9: * alpar@9: * (xadj, adjncy) - alpar@9: * the adjancy structure; alpar@9: * deg0 - the number of nodes in the given set; alpar@9: * (nhdsze, nbrhd) - alpar@9: * the set of eliminated supernodes adjacent to some nodes in alpar@9: * the set. alpar@9: * alpar@9: * UPDATED PARAMETERS alpar@9: * alpar@9: * deg - the degree vector; alpar@9: * qsize - size of indistinguishable nodes; alpar@9: * qlink - linked list for indistinguishable nodes; alpar@9: * marker - the given set is given by those nodes with marker value set alpar@9: * to 1. Those nodes with degree updated will have marker value alpar@9: * set to 2. alpar@9: * alpar@9: * WORKING PARAMETERS alpar@9: * alpar@9: * rchset - the reachable set; alpar@9: * ovrlp - temp vector to store the intersection of two reachable sets. alpar@9: ***********************************************************************/ alpar@9: alpar@9: void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[], alpar@9: int qlink[], int marker[], int *_deg0, int *_nhdsze, int nbrhd[], alpar@9: int rchset[], int ovrlp[]) alpar@9: { int deg1, head, inhd, iov, irch, j, jstop, jstrt, link, lnode, alpar@9: mark, mrgsze, nabor, node, novrlp, rchsze, root; alpar@9: # define deg0 (*_deg0) alpar@9: # define nhdsze (*_nhdsze) alpar@9: /* Initialization. */ alpar@9: if (nhdsze <= 0) return; alpar@9: for (inhd = 1; inhd <= nhdsze; inhd++) alpar@9: { root = nbrhd[inhd]; alpar@9: marker[root] = 0; alpar@9: } alpar@9: /* Loop through each eliminated supernode in the set alpar@9: (nhdsze, nbrhd). */ alpar@9: for (inhd = 1; inhd <= nhdsze; inhd++) alpar@9: { root = nbrhd[inhd]; alpar@9: marker[root] = -1; alpar@9: rchsze = 0; alpar@9: novrlp = 0; alpar@9: deg1 = 0; alpar@9: s200: jstrt = xadj[root]; alpar@9: jstop = xadj[root+1] - 1; alpar@9: /* Determine the reachable set and its intersection with the alpar@9: input reachable set. */ alpar@9: for (j = jstrt; j <= jstop; j++) alpar@9: { nabor = adjncy[j]; alpar@9: root = - nabor; alpar@9: if (nabor < 0) goto s200; alpar@9: if (nabor == 0) break; alpar@9: mark = marker[nabor]; alpar@9: if (mark == 0) alpar@9: { rchsze++; alpar@9: rchset[rchsze] = nabor; alpar@9: deg1 += qsize[nabor]; alpar@9: marker[nabor] = 1; alpar@9: } alpar@9: else if (mark == 1) alpar@9: { novrlp++; alpar@9: ovrlp[novrlp] = nabor; alpar@9: marker[nabor] = 2; alpar@9: } alpar@9: } alpar@9: /* From the overlapped set, determine the nodes that can be alpar@9: merged together. */ alpar@9: head = 0; alpar@9: mrgsze = 0; alpar@9: for (iov = 1; iov <= novrlp; iov++) alpar@9: { node = ovrlp[iov]; alpar@9: jstrt = xadj[node]; alpar@9: jstop = xadj[node+1] - 1; alpar@9: for (j = jstrt; j <= jstop; j++) alpar@9: { nabor = adjncy[j]; alpar@9: if (marker[nabor] == 0) alpar@9: { marker[node] = 1; alpar@9: goto s1100; alpar@9: } alpar@9: } alpar@9: /* Node belongs to the new merged supernode. Update the alpar@9: vectors qlink and qsize. */ alpar@9: mrgsze += qsize[node]; alpar@9: marker[node] = -1; alpar@9: lnode = node; alpar@9: s900: link = qlink[lnode]; alpar@9: if (link > 0) alpar@9: { lnode = link; alpar@9: goto s900; alpar@9: } alpar@9: qlink[lnode] = head; alpar@9: head = node; alpar@9: s1100: ; alpar@9: } alpar@9: if (head > 0) alpar@9: { qsize[head] = mrgsze; alpar@9: deg[head] = deg0 + deg1 - 1; alpar@9: marker[head] = 2; alpar@9: } alpar@9: /* Reset marker values. */ alpar@9: root = nbrhd[inhd]; alpar@9: marker[root] = 0; alpar@9: if (rchsze > 0) alpar@9: { for (irch = 1; irch <= rchsze; irch++) alpar@9: { node = rchset[irch]; alpar@9: marker[node] = 0; alpar@9: } alpar@9: } alpar@9: } alpar@9: return; alpar@9: # undef deg0 alpar@9: # undef nhdsze alpar@9: } alpar@9: alpar@9: /* eof */