annotate toolboxes/graph_visualisation/lib/lefty/dotty_layout.lefty @ 0:e9a9cd732c1e tip

first hg version after svn
author wolffd
date Tue, 10 Feb 2015 15:05:51 +0000
parents
children
rev   line source
wolffd@0 1 #
wolffd@0 2 # dotty_layout: layout functions and data structures
wolffd@0 3 #
wolffd@0 4 dotty.grablserver = function (lserver) {
wolffd@0 5 local fd;
wolffd@0 6
wolffd@0 7 if (~dotty.lservers[lserver] | tablesize (dotty.lservers[lserver]) == 0) {
wolffd@0 8 if (~((fd = openio ('pipe', lserver, 'r+', '%e -Txdot')) >= 0)) {
wolffd@0 9 dotty.message (0, concat ('cannot start ', lserver));
wolffd@0 10 return null;
wolffd@0 11 }
wolffd@0 12 dotty.lservers[lserver][fd] = [
wolffd@0 13 'fd' = fd;
wolffd@0 14 'count' = 0;
wolffd@0 15 ];
wolffd@0 16 }
wolffd@0 17 for (fd in dotty.lservers[lserver]) {
wolffd@0 18 dotty.lservers[lserver][fd].count = dotty.lservers[
wolffd@0 19 lserver
wolffd@0 20 ][fd].count + 1;
wolffd@0 21 dotty.lservers.inuse[fd] = dotty.lservers[lserver][fd];
wolffd@0 22 remove (fd, dotty.lservers[lserver]);
wolffd@0 23 return fd;
wolffd@0 24 }
wolffd@0 25 };
wolffd@0 26 dotty.releaselserver = function (lserver, fd, state) {
wolffd@0 27 if (state == 'bad' | dotty.lservers.inuse[fd].count > 40) {
wolffd@0 28 closeio (fd, 'kill');
wolffd@0 29 remove (fd, dotty.lservers.inuse);
wolffd@0 30 return;
wolffd@0 31 }
wolffd@0 32 dotty.lservers[lserver][fd] = dotty.lservers.inuse[fd];
wolffd@0 33 remove (fd, dotty.lservers.inuse);
wolffd@0 34 };
wolffd@0 35 dotty.protogt.startlayout = function (gt) {
wolffd@0 36 local lpt, fd;
wolffd@0 37
wolffd@0 38 if (gt.layoutpending >= 1) {
wolffd@0 39 lpt = dotty.layoutpending[gt.gtid];
wolffd@0 40 if (gt.layoutmode == 'async')
wolffd@0 41 monitor ('off', lpt.fd);
wolffd@0 42 dotty.releaselserver (gt.lserver, lpt.fd, 'bad');
wolffd@0 43 remove (gt.gtid, dotty.layoutpending);
wolffd@0 44 gt.layoutpending = 0;
wolffd@0 45 gt.haveinput = 0;
wolffd@0 46 dotty.popbusy (gt, gt.views);
wolffd@0 47 }
wolffd@0 48 if (~((fd = dotty.grablserver (gt.lserver)) >= 0))
wolffd@0 49 return null;
wolffd@0 50 dotty.pushbusy (gt, gt.views);
wolffd@0 51 writegraph (fd, gt.graph, 1);
wolffd@0 52 gt.layoutpending = 1;
wolffd@0 53 dotty.layoutpending[gt.gtid] = [
wolffd@0 54 'fd' = fd;
wolffd@0 55 'gtid' = gt.gtid;
wolffd@0 56 ];
wolffd@0 57 if (gt.layoutmode == 'async')
wolffd@0 58 monitor ('on', fd);
wolffd@0 59 return 1;
wolffd@0 60 };
wolffd@0 61 dotty.protogt.finishlayout = function (gt) {
wolffd@0 62 local graph, lpt, fd;
wolffd@0 63
wolffd@0 64 if (~(gt.layoutpending >= 1)) {
wolffd@0 65 dotty.message (0, concat ('no layout pending for graph ', gt.gtid));
wolffd@0 66 return null;
wolffd@0 67 }
wolffd@0 68 lpt = dotty.layoutpending[gt.gtid];
wolffd@0 69 if (~(graph = readgraph (lpt.fd))) {
wolffd@0 70 if (gt.layoutmode == 'async')
wolffd@0 71 monitor ('off', lpt.fd);
wolffd@0 72 dotty.releaselserver (gt.lserver, lpt.fd, 'bad');
wolffd@0 73 if (gt.layoutpending == 2) {
wolffd@0 74 dotty.message (0, concat ('giving up on ', gt.lserver));
wolffd@0 75 if ((fd = openio ('file', 'dottybug.dot', 'w+')) >= 0) {
wolffd@0 76 writegraph (fd, gt.graph, 0);
wolffd@0 77 closeio (fd);
wolffd@0 78 dotty.message (
wolffd@0 79 0, concat ('graph that causes ', gt.lserver)
wolffd@0 80 );
wolffd@0 81 dotty.message (
wolffd@0 82 0, 'to fail has been saved in file dottybug.dot'
wolffd@0 83 );
wolffd@0 84 dotty.message (
wolffd@0 85 0, 'please fill out a bug report at'
wolffd@0 86 );
wolffd@0 87 dotty.message (
wolffd@0 88 0, 'http://www.graphviz.org/bugs/bugform.html'
wolffd@0 89 );
wolffd@0 90 }
wolffd@0 91 dotty.popbusy (gt, gt.views);
wolffd@0 92 gt.layoutpending = 0;
wolffd@0 93 gt.haveinput = 0;
wolffd@0 94 return 1;
wolffd@0 95 }
wolffd@0 96 dotty.message (
wolffd@0 97 1, concat ('lost connection to ', gt.lserver, ', restarting...')
wolffd@0 98 );
wolffd@0 99 lpt.fd = dotty.grablserver (gt.lserver);
wolffd@0 100 writegraph (lpt.fd, gt.graph, 1);
wolffd@0 101 if (gt.layoutmode == 'async')
wolffd@0 102 monitor ('on', lpt.fd);
wolffd@0 103 gt.layoutpending = 2;
wolffd@0 104 gt.haveinput = 0;
wolffd@0 105 return null;
wolffd@0 106 }
wolffd@0 107 if (gt.layoutmode == 'async')
wolffd@0 108 monitor ('off', lpt.fd);
wolffd@0 109 dotty.releaselserver (gt.lserver, lpt.fd, null);
wolffd@0 110 remove (gt.gtid, dotty.layoutpending);
wolffd@0 111 gt.layoutpending = 0;
wolffd@0 112 gt.haveinput = 0;
wolffd@0 113 gt.unpacklayout (gt, graph);
wolffd@0 114 dotty.popbusy (gt, gt.views);
wolffd@0 115 return 1;
wolffd@0 116 };
wolffd@0 117 dotty.protogt.cancellayout = function (gt) {
wolffd@0 118 local lpt, vid;
wolffd@0 119
wolffd@0 120 if (gt.layoutpending >= 1) {
wolffd@0 121 lpt = dotty.layoutpending[gt.gtid];
wolffd@0 122 if (gt.layoutmode == 'async')
wolffd@0 123 monitor ('off', lpt.fd);
wolffd@0 124 dotty.releaselserver (gt.lserver, lpt.fd, 'bad');
wolffd@0 125 remove (gt.gtid, dotty.layoutpending);
wolffd@0 126 gt.layoutpending = 0;
wolffd@0 127 gt.haveinput = 0;
wolffd@0 128 dotty.popbusy (gt, gt.views);
wolffd@0 129 }
wolffd@0 130 };
wolffd@0 131 dotty.protogt.unpacklayout = function (gt, graph2) {
wolffd@0 132 local graph, gid, sgraph1, sgraph2, nid, node1, node2, eid, edge1, edge2;
wolffd@0 133 local t1, pos, size;
wolffd@0 134
wolffd@0 135 graph = gt.graph;
wolffd@0 136 for (gid in graph2.graphdict) {
wolffd@0 137 if (~(sgraph1 = graph.graphs[graph.graphdict[gid]]))
wolffd@0 138 continue;
wolffd@0 139 sgraph2 = graph2.graphs[graph2.graphdict[gid]];
wolffd@0 140 sgraph1.draws = gt.unpackalldraw (gt, sgraph2.graphattr);
wolffd@0 141 }
wolffd@0 142 for (nid in graph2.nodedict) {
wolffd@0 143 if (~(node1 = graph.nodes[graph.nodedict[nid]]))
wolffd@0 144 continue;
wolffd@0 145 node2 = graph2.nodes[graph2.nodedict[nid]];
wolffd@0 146 node1.draws = gt.unpackalldraw (gt, node2.attr);
wolffd@0 147 t1 = split (node2.attr.pos, ',');
wolffd@0 148 pos = ['x' = ston (t1[0]); 'y' = ston (t1[1]);];
wolffd@0 149 size = [
wolffd@0 150 'x' = ston (node2.attr.width) * 72;
wolffd@0 151 'y' = ston (node2.attr.height) * 72;
wolffd@0 152 ];
wolffd@0 153 node1.pos = pos;
wolffd@0 154 node1.size = size;
wolffd@0 155 node1.rect = [
wolffd@0 156 0 = ['x' = pos.x - size.x / 2; 'y' = pos.y - size.y / 2;];
wolffd@0 157 1 = ['x' = pos.x + size.x / 2; 'y' = pos.y + size.y / 2;];
wolffd@0 158 ];
wolffd@0 159 }
wolffd@0 160 for (eid in graph2.edges) {
wolffd@0 161 edge2 = graph2.edges[eid];
wolffd@0 162 if (edge2.attr.id) {
wolffd@0 163 if (~(edge1 = graph.edges[ston (edge2.attr.id)]))
wolffd@0 164 continue;
wolffd@0 165 } else if (graph == graph2)
wolffd@0 166 edge1 = edge2;
wolffd@0 167 edge1.draws = gt.unpackalldraw (gt, edge2.attr);
wolffd@0 168 }
wolffd@0 169 graph.draws = gt.unpackalldraw (gt, graph2.graphattr);
wolffd@0 170 t1 = split (graph2.graphattr.bb, ',');
wolffd@0 171 graph.rect[0].x = ston (t1[0]);
wolffd@0 172 graph.rect[0].y = ston (t1[1]);
wolffd@0 173 graph.rect[1].x = ston (t1[2]);
wolffd@0 174 graph.rect[1].y = ston (t1[3]);
wolffd@0 175 if (gt.graph ~= graph2)
wolffd@0 176 return;
wolffd@0 177 # strip position and size info from the attributes
wolffd@0 178 for (gid in graph2.graphdict) {
wolffd@0 179 sgraph2 = graph2.graphs[graph2.graphdict[gid]];
wolffd@0 180 gt.removealldraw (gt, sgraph2.graphattr);
wolffd@0 181 if (sgraph2.graphattr.bb)
wolffd@0 182 remove ('bb', sgraph2.graphattr);
wolffd@0 183 }
wolffd@0 184 for (nid in graph2.nodedict) {
wolffd@0 185 node2 = graph2.nodes[graph2.nodedict[nid]];
wolffd@0 186 gt.removealldraw (gt, node2.attr);
wolffd@0 187 if (node2.attr.rects)
wolffd@0 188 remove ('rects', node2.attr);
wolffd@0 189 remove ('pos', node2.attr);
wolffd@0 190 remove ('width', node2.attr);
wolffd@0 191 remove ('height', node2.attr);
wolffd@0 192 }
wolffd@0 193 for (eid in graph2.edges) {
wolffd@0 194 edge2 = graph2.edges[eid];
wolffd@0 195 gt.removealldraw (gt, edge2.attr);
wolffd@0 196 if (edge2.attr.pos)
wolffd@0 197 remove ('pos', edge2.attr);
wolffd@0 198 if (edge2.attr.lp)
wolffd@0 199 remove ('lp', edge2.attr);
wolffd@0 200 }
wolffd@0 201 gt.removealldraw (gt, graph2.graphattr);
wolffd@0 202 remove ('bb', graph2.graphattr);
wolffd@0 203 if (graph2.graphattr.lp)
wolffd@0 204 remove ('lp', graph2.graphattr);
wolffd@0 205 };
wolffd@0 206 #
wolffd@0 207 # draw directive parsing
wolffd@0 208 #
wolffd@0 209 dotty.protogt.unpackalldraw = function (gt, attr) {
wolffd@0 210 local o, did;
wolffd@0 211
wolffd@0 212 o = [];
wolffd@0 213 if (attr._draw_)
wolffd@0 214 o._draw_ = gt.unpackdraw (gt, attr._draw_);
wolffd@0 215 if (attr._ldraw_)
wolffd@0 216 o._ldraw_ = gt.unpackdraw (gt, attr._ldraw_);
wolffd@0 217 if (attr._hdraw_)
wolffd@0 218 o._hdraw_ = gt.unpackdraw (gt, attr._hdraw_);
wolffd@0 219 if (attr._tdraw_)
wolffd@0 220 o._tdraw_ = gt.unpackdraw (gt, attr._tdraw_);
wolffd@0 221 if (attr._hldraw_)
wolffd@0 222 o._hldraw_ = gt.unpackdraw (gt, attr._hldraw_);
wolffd@0 223 if (attr._tldraw_)
wolffd@0 224 o._tldraw_ = gt.unpackdraw (gt, attr._tldraw_);
wolffd@0 225 for (did in o)
wolffd@0 226 if (o[did].ep) {
wolffd@0 227 o.ep = o[did].ep;
wolffd@0 228 break;
wolffd@0 229 }
wolffd@0 230 return o;
wolffd@0 231 };
wolffd@0 232 dotty.protogt.removealldraw = function (gt, attr) {
wolffd@0 233 if (attr._draw_)
wolffd@0 234 remove ('_draw_', attr);
wolffd@0 235 if (attr._ldraw_)
wolffd@0 236 remove ('_ldraw_', attr);
wolffd@0 237 if (attr._hdraw_)
wolffd@0 238 remove ('_hdraw_', attr);
wolffd@0 239 if (attr._tdraw_)
wolffd@0 240 remove ('_tdraw_', attr);
wolffd@0 241 if (attr._hldraw_)
wolffd@0 242 remove ('_hldraw_', attr);
wolffd@0 243 if (attr._tldraw_)
wolffd@0 244 remove ('_tldraw_', attr);
wolffd@0 245 };
wolffd@0 246 dotty.protogt.unpackdraw = function (gt, attr) {
wolffd@0 247 local oo, o, tt, t, n, i, j, s, l, ep;
wolffd@0 248
wolffd@0 249 oo = [];
wolffd@0 250 t = split (attr, ' ', 0);
wolffd@0 251 n = tablesize (t);
wolffd@0 252 if (t[n - 1] == '') {
wolffd@0 253 remove (n - 1, t);
wolffd@0 254 n = n - 1;
wolffd@0 255 }
wolffd@0 256 i = 0;
wolffd@0 257 while (i < n) {
wolffd@0 258 o = [];
wolffd@0 259 if (t[i] == 'E') {
wolffd@0 260 o.type = t[i];
wolffd@0 261 o.c.x = ston (t[i + 1]);
wolffd@0 262 o.c.y = ston (t[i + 2]);
wolffd@0 263 o.s.x = ston (t[i + 3]);
wolffd@0 264 o.s.y = ston (t[i + 4]);
wolffd@0 265 i = i + 5;
wolffd@0 266 } else if (t[i] == 'e') {
wolffd@0 267 o.type = t[i];
wolffd@0 268 o.c.x = ston (t[i + 1]);
wolffd@0 269 o.c.y = ston (t[i + 2]);
wolffd@0 270 o.s.x = ston (t[i + 3]);
wolffd@0 271 o.s.y = ston (t[i + 4]);
wolffd@0 272 i = i + 5;
wolffd@0 273 } else if (t[i] == 'P') {
wolffd@0 274 o.type = t[i];
wolffd@0 275 o.n = ston (t[i + 1]);
wolffd@0 276 for (j = 0; j < o.n; j = j + 1) {
wolffd@0 277 o.ps[j].x = ston (t[i + 2 + j * 2]);
wolffd@0 278 o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
wolffd@0 279 }
wolffd@0 280 i = i + 2 + o.n * 2;
wolffd@0 281 o.ps[o.n] = o.ps[0];
wolffd@0 282 o.n = o.n + 1;
wolffd@0 283 } else if (t[i] == 'p') {
wolffd@0 284 o.type = t[i];
wolffd@0 285 o.n = ston (t[i + 1]);
wolffd@0 286 for (j = 0; j < o.n; j = j + 1) {
wolffd@0 287 o.ps[j].x = ston (t[i + 2 + j * 2]);
wolffd@0 288 o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
wolffd@0 289 }
wolffd@0 290 i = i + 2 + o.n * 2;
wolffd@0 291 o.ps[o.n] = o.ps[0];
wolffd@0 292 o.n = o.n + 1;
wolffd@0 293 } else if (t[i] == 'L') {
wolffd@0 294 o.type = t[i];
wolffd@0 295 o.n = ston (t[i + 1]);
wolffd@0 296 for (j = 0; j < o.n; j = j + 1) {
wolffd@0 297 o.ps[j].x = ston (t[i + 2 + j * 2]);
wolffd@0 298 o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
wolffd@0 299 }
wolffd@0 300 i = i + 2 + o.n * 2;
wolffd@0 301 if (~ep)
wolffd@0 302 ep = copy (o.ps[1]);
wolffd@0 303 } else if (t[i] == 'B') {
wolffd@0 304 o.type = t[i];
wolffd@0 305 o.n = ston (t[i + 1]);
wolffd@0 306 for (j = 0; j < o.n; j = j + 1) {
wolffd@0 307 o.ps[j].x = ston (t[i + 2 + j * 2]);
wolffd@0 308 o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
wolffd@0 309 }
wolffd@0 310 i = i + 2 + o.n * 2;
wolffd@0 311 if (~ep)
wolffd@0 312 ep = copy (o.ps[1]);
wolffd@0 313 } else if (t[i] == 'b') {
wolffd@0 314 o.type = t[i];
wolffd@0 315 o.n = ston (t[i + 1]);
wolffd@0 316 for (j = 0; j < o.n; j = j + 1) {
wolffd@0 317 o.ps[j].x = ston (t[i + 2 + j * 2]);
wolffd@0 318 o.ps[j].y = ston (t[i + 2 + j * 2 + 1]);
wolffd@0 319 }
wolffd@0 320 i = i + 2 + o.n * 2;
wolffd@0 321 if (~ep)
wolffd@0 322 ep = copy (o.ps[1]);
wolffd@0 323 } else if (t[i] == 'T') {
wolffd@0 324 o.type = t[i];
wolffd@0 325 o.p.x = ston (t[i + 1]);
wolffd@0 326 o.p.y = ston (t[i + 2]);
wolffd@0 327 o.j = ston (t[i + 3]);
wolffd@0 328 if (o.j == -1)
wolffd@0 329 o.j = 'lb';
wolffd@0 330 else if (o.j == 1)
wolffd@0 331 o.j = 'rb';
wolffd@0 332 else if (o.j == 0)
wolffd@0 333 o.j = 'cb';
wolffd@0 334 o.w = ston (t[i + 4]);
wolffd@0 335 o.n = ston (t[i + 5]);
wolffd@0 336 i = i + 6;
wolffd@0 337 s = t[i];
wolffd@0 338 i = i + 1;
wolffd@0 339 l = strlen (s) - 1;
wolffd@0 340 while (l < o.n) {
wolffd@0 341 s = concat (s, ' ', t[i]);
wolffd@0 342 l = l + 1 + strlen (t[i]);
wolffd@0 343 i = i + 1;
wolffd@0 344 }
wolffd@0 345 tt = split (s, '');
wolffd@0 346 l = tablesize (tt);
wolffd@0 347 s = '';
wolffd@0 348 for (j = 1; j < l; j = j + 1)
wolffd@0 349 s = concat (s, tt[j]);
wolffd@0 350 o.s = s;
wolffd@0 351 } else if (t[i] == 'C') {
wolffd@0 352 o.type = t[i];
wolffd@0 353 o.n = ston (t[i + 1]);
wolffd@0 354 i = i + 2;
wolffd@0 355 s = t[i];
wolffd@0 356 i = i + 1;
wolffd@0 357 l = strlen (s) - 1;
wolffd@0 358 while (l < o.n) {
wolffd@0 359 s = concat (s, ' ', t[i]);
wolffd@0 360 l = l + 1 + strlen (t[i]);
wolffd@0 361 i = i + 1;
wolffd@0 362 }
wolffd@0 363 tt = split (s, '');
wolffd@0 364 l = tablesize (tt);
wolffd@0 365 s = '';
wolffd@0 366 for (j = 1; j < l; j = j + 1)
wolffd@0 367 s = concat (s, tt[j]);
wolffd@0 368 o.fillcolor = gt.getcolor (gt.views, s);
wolffd@0 369 } else if (t[i] == 'c') {
wolffd@0 370 o.type = t[i];
wolffd@0 371 o.n = ston (t[i + 1]);
wolffd@0 372 i = i + 2;
wolffd@0 373 s = t[i];
wolffd@0 374 i = i + 1;
wolffd@0 375 l = strlen (s) - 1;
wolffd@0 376 while (l < o.n) {
wolffd@0 377 s = concat (s, ' ', t[i]);
wolffd@0 378 l = l + 1 + strlen (t[i]);
wolffd@0 379 i = i + 1;
wolffd@0 380 }
wolffd@0 381 tt = split (s, '');
wolffd@0 382 l = tablesize (tt);
wolffd@0 383 s = '';
wolffd@0 384 for (j = 1; j < l; j = j + 1)
wolffd@0 385 s = concat (s, tt[j]);
wolffd@0 386 o.drawcolor = gt.getcolor (gt.views, s);
wolffd@0 387 } else if (t[i] == 'F') {
wolffd@0 388 o.type = t[i];
wolffd@0 389 o.fs = ston (t[i + 1]);
wolffd@0 390 o.n = ston (t[i + 2]);
wolffd@0 391 i = i + 3;
wolffd@0 392 s = t[i];
wolffd@0 393 i = i + 1;
wolffd@0 394 l = strlen (s) - 1;
wolffd@0 395 while (l < o.n) {
wolffd@0 396 s = concat (s, ' ', t[i]);
wolffd@0 397 l = l + 1 + strlen (t[i]);
wolffd@0 398 i = i + 1;
wolffd@0 399 }
wolffd@0 400 tt = split (s, '');
wolffd@0 401 l = tablesize (tt);
wolffd@0 402 s = '';
wolffd@0 403 for (j = 1; j < l; j = j + 1)
wolffd@0 404 s = concat (s, tt[j]);
wolffd@0 405 o.ofn = s;
wolffd@0 406 o.fn = dotty.fontmap[s];
wolffd@0 407 } else if (t[i] == 'S') {
wolffd@0 408 o.type = t[i];
wolffd@0 409 o.n = ston (t[i + 1]);
wolffd@0 410 i = i + 2;
wolffd@0 411 s = t[i];
wolffd@0 412 i = i + 1;
wolffd@0 413 l = strlen (s) - 1;
wolffd@0 414 while (l < o.n) {
wolffd@0 415 s = concat (s, ' ', t[i]);
wolffd@0 416 l = l + 1 + strlen (t[i]);
wolffd@0 417 i = i + 1;
wolffd@0 418 }
wolffd@0 419 tt = split (s, '');
wolffd@0 420 l = tablesize (tt);
wolffd@0 421 s = '';
wolffd@0 422 for (j = 1; j < l; j = j + 1)
wolffd@0 423 s = concat (s, tt[j]);
wolffd@0 424 if (
wolffd@0 425 s == 'solid' | s == 'dashed' | s == 'dotted' |
wolffd@0 426 s == 'longdashed' | s == 'shortdashed'
wolffd@0 427 )
wolffd@0 428 o.style = s;
wolffd@0 429 else if (s == 'bold')
wolffd@0 430 o.width = 3;
wolffd@0 431 else {
wolffd@0 432 tt = split (s, '(');
wolffd@0 433 if (tt[0] == 'setlinewidth') {
wolffd@0 434 tt = split (tt[1], ')');
wolffd@0 435 o.width = ston (tt[0]);
wolffd@0 436 } else
wolffd@0 437 continue;
wolffd@0 438 }
wolffd@0 439 } else if (t[i] == 'I') {
wolffd@0 440 i = i + 7;
wolffd@0 441 } else {
wolffd@0 442 dotty.message (0, concat ('draw language parser error: ', t[i]));
wolffd@0 443 return null;
wolffd@0 444 }
wolffd@0 445 oo[tablesize (oo)] = o;
wolffd@0 446 }
wolffd@0 447 oo.ep = ep;
wolffd@0 448 return oo;
wolffd@0 449 };