|
|
1.1 root 1: #ifndef listdeclare
2: #ifndef GENERICH
3: #include <generic.h>
4: #endif
5: #include <stream.h>
6:
7: #ifndef TRUE
8: #define FALSE 0
9: #define TRUE (!FALSE)
10: #endif
11:
12: #ifndef NULL
13: #define NULL 0
14: #endif
15:
16: #ifndef BIT_DEFINED
17: #define BIT_DEFINED
18: typedef int bit;
19: #endif
20:
21: struct lnk {
22: lnk* nxt;
23: lnk* prv;
24: void init(lnk* p, lnk* s) { prv = p; nxt = s; }
25: lnk() {}
26: ~lnk() {}
27: };
28: struct LstHead {
29: lnk *t; // tail
30: lnk *h() { return t ? t->nxt : 0; }
31: int sz;
32: lnk *cache; // a recently retrieved link
33: int cacheNo; // its index or garbage if cache == NULL
34: int refCount;
35: LstHead() { sz = 0; cache = t = NULL; }
36: virtual ~LstHead();
37: void insert(lnk*); // used by put() and unget()
38: void unget(lnk* nl) { insert(nl); cacheNo++; }
39: void put(lnk* nl) { insert(nl); t = nl; }
40: lnk* get();
41: lnk* unput();
42: lnk* getAt(int);
43: };
44: struct lstiter;
45: struct List {
46: friend lstiter;
47: lstiter *fI;
48: LstHead *it;
49: virtual void sepWork();
50: void separate() { if (it->refCount > 1) sepWork(); }
51: int length() const { return it->sz; }
52: List() { fI = NULL; }
53: List(List& x) { fI = NULL; it = x.it; it->refCount++; }
54: ~List();
55: lnk* tail() const { return it->t; }
56: lnk* head() const { return it->h(); }
57: lstiter* firstIt() { return fI; }
58: void newIt(lstiter*, int=0);
59: void removeIt(lstiter*);
60: operator void*() { return length() ? this : 0; }
61: List& operator=(List&);
62: List& unget(lnk*);
63: List& put(lnk*);
64: lnk* get();
65: lnk* unput();
66: };
67: struct lstiter {
68: friend List;
69: lnk *pred;
70: List *aList;
71: lstiter *nextIt;
72: int index;
73: lstiter(List&, int =0);
74: lstiter(lstiter&);
75: ~lstiter();
76: List *theList() { return aList; }
77: lstiter& operator=(lstiter&);
78: bit operator==(lstiter& oo) {
79: return aList == oo.aList &&
80: index == oo.index; }
81: bit atEnd() { return index == aList->length(); }
82: bit r_atEnd() { return index == 0 ; }
83: lnk* next();
84: lnk* r_next();
85: lnk* peek();
86: lnk* r_peek();
87: void insert(lnk*);
88: void r_insert(lnk*);
89: lnk* remove();
90: lnk* r_remove();
91: void reset(int =0);
92: void r_reset(int i=0) { reset(aList->length() - i); }
93: };
94:
95: extern "C" {
96: extern int abort();
97: }
98:
99: #define lnnk(type) name2(type,lnnk)
100: #define list(type) name2(type,_list)
101: #define ListHead(type) name2(type,ListHead)
102: #define listiterator(type) name2(type,_list_iterator)
103: #define listsubitem(type) name2(type,listsubitem)
104: #define plist(type) name2(type,P_list)
105: #define plistiterator(type) name2(type,P_list_iterator)
106: #define plistsubitem(type) name2(type,Plistsubitem)
107: #define CMPF(type) name2(type,CMPFN)
108: #define CMPFP(type) name2(type,CMPFNP)
109:
110: #define listdeclare(type) \
111: extern GPT errorhandler(list,type); \
112: extern GPT set_handler(list,type,GPT); \
113: extern GPT errorhandler(listiterator,type); \
114: extern GPT set_handler(listiterator,type,GPT); \
115: class list(type); \
116: class lnnk(type); \
117: class ListHead(type); \
118: class listiterator(type); \
119: class listsubitem(type); \
120: typedef int (*CMPF(type))(type&, type&); \
121: ostream& operator<<(ostream&, list(type)&); \
122: class lnnk(type) : public lnk { \
123: friend ListHead(type); \
124: friend list(type); \
125: friend listsubitem(type); \
126: friend listiterator(type); \
127: friend ostream& operator<<(ostream&, list(type)&); \
128: friend void voidP_list_sort_internal(voidP_list&, CMPF(voidP)); \
129: type val; \
130: lnnk(type)(type& pp) : val(pp) {} \
131: ~lnnk(type)() {} \
132: }; \
133: class ListHead(type) : public LstHead { \
134: friend list(type); \
135: ListHead(type)() {} \
136: ~ListHead(type)(); \
137: lnnk(type) *tail() { return (lnnk(type)*)t; } \
138: lnnk(type) *head() { return (lnnk(type)*)h(); } \
139: bit firstX(type&); \
140: bit lastX(type&); \
141: }; \
142: class list(type) : public List { \
143: lnnk(type) *tail() const { return (lnnk(type)*)List::tail(); } \
144: lnnk(type) *head() const { return (lnnk(type)*)List::head(); } \
145: void init() { it = new ListHead(type); it->refCount = 1; } \
146: void sepWork(); \
147: public: \
148: friend ostream& operator<<(ostream&, list(type)&); \
149: friend void voidP_list_sort_internal(voidP_list&, CMPF(voidP)); \
150: list(type)() { init(); } \
151: list(type)(list(type)& x) : List(x) {} \
152: ~list(type)() {} \
153: operator const void*() const { return length() ? this : 0; } \
154: bit operator==(list(type)&) const; \
155: bit operator!=(list(type)& x) const { return !(*this == x); } \
156: list(type)& operator=(list(type)& ll) { return (list(type)&) \
157: (*(List*)this = (List&)ll); } \
158: list(type)& put(type &x) { return (list(type)&) \
159: List::put(new lnnk(type)(x)); } \
160: list(type)& put(list(type)&); /* append */ \
161: list(type)& operator+=(list(type)& oo) { return put(oo); } \
162: list(type)& operator+=(type& t) { return put(t); } \
163: list(type) operator+(list(type)& ll) { list(type) ans = *this; \
164: ans += ll; return ans; } \
165: list(type) operator+(type& t) { list(type) ans = *this; \
166: ans += t; return ans; } \
167: bit unputX(type &t); \
168: bit unput(); \
169: bit getX(type&); /* get */ \
170: bit get(); /* get */ \
171: list(type)& unget(type &x) { return (list(type)&) \
172: List::unget(new lnnk(type)(x)); } \
173: list(type)& unget(list(type)&); /* prepend */ \
174: bit firstX(type& t) { return \
175: ((ListHead(type)*)it)->firstX(t); } \
176: bit lastX(type& t) { return \
177: ((ListHead(type)*)it)->lastX(t); } \
178: /* getAt(0) returns the head of the list */ \
179: type getAt(int ii) { return ((lnnk(type)*)it->getAt(ii))->val; } \
180: inline listsubitem(type) operator[](unsigned ii); \
181: list(type)(type& x) { init(); put(x); } \
182: list(type)(type& x, type& y) { init(); put(x); put(y); } \
183: list(type)(type& x, type& y, type& z) { init(); put(x); put(y); \
184: put(z); } \
185: list(type)(type& x, type& y, type& z, type& w) { init(); \
186: put(x); put(y); put(z); put(w); } \
187: void sort(CMPF(type)); \
188: }; \
189: class listiterator(type) : public lstiter { \
190: friend listsubitem(type); \
191: friend list(type); \
192: public: \
193: listiterator(type)(list(type)& a, int i=0) : \
194: lstiter(*(List*)&a, i) {} \
195: listiterator(type)(listiterator(type)& a) : \
196: lstiter(*(lstiter*)&a) {} \
197: ~listiterator(type)() {} \
198: listiterator(type)& operator=(listiterator(type)& a) { return \
199: (listiterator(type)&)(*(lstiter*)this = (lstiter&)a); } \
200: bit nextX(type&); \
201: bit nextX(type*&); \
202: bit next() { return lstiter::next() != 0; } \
203: bit r_nextX(type&); \
204: bit r_nextX(type*&); \
205: bit r_next() { return lstiter::r_next() != 0; } \
206: bit peekX(type&); \
207: bit peekX(type*&); \
208: bit peek() { return !atEnd(); } \
209: bit r_peekX(type&); \
210: bit r_peekX(type*&); \
211: bit r_peek() { return !r_atEnd(); } \
212: /* remove -- deletes current from list */ \
213: bit remove(); \
214: bit r_remove(); \
215: bit removeX(type&); \
216: bit r_removeX(type&); \
217: /* insert -- put it at the left of the pointer */ \
218: void insert(type& x) { lstiter::insert(new lnnk(type)(x)); } \
219: void r_insert(type& x) { lstiter::r_insert(new lnnk(type)(x)); } \
220: bit replace(type&); \
221: bit r_replace(type&); \
222: }; \
223: class listsubitem(type) : public listiterator(type) { \
224: public: \
225: listsubitem(type)(list(type)& t, unsigned i) : \
226: listiterator(type)(t, i) {} \
227: listsubitem(type)(listsubitem(type)& a) : \
228: listiterator(type)((listiterator(type)&)a) {} \
229: ~listsubitem(type)() {} \
230: type& operator=(type& x) { r_replace(x); return x; } \
231: operator type(); \
232: }; \
233: inline listsubitem(type) \
234: list(type)::operator[](unsigned ii) \
235: { \
236: return listsubitem(type)(*this, ii); \
237: }
238:
239: typedef void* voidP;
240: listdeclare(voidP)
241:
242: #define plistdeclare(type) \
243: extern GPT errorhandler(Plist,type); \
244: extern GPT set_handler(Plist,type,GPT); \
245: extern GPT errorhandler(Plistiterator,type); \
246: extern GPT set_handler(Plistiterator,type,GPT); \
247: class plist(type); \
248: typedef int (*CMPFP(type))(type*&, type*&); \
249: ostream& operator<<(ostream&, plist(type)&); \
250: class plistiterator(type); \
251: class plistsubitem(type); \
252: class plist(type) : public voidP_list { \
253: public: \
254: friend ostream& operator<<(ostream&, plist(type)&); \
255: plist(type)() {} \
256: plist(type)(type* x) : voidP_list((voidP) x) {} \
257: plist(type)(type* x, type* y) : \
258: voidP_list((voidP) x, (voidP) y) {} \
259: plist(type)(type* x, type* y, type* z) : \
260: voidP_list((voidP) x, (voidP) y, (voidP) z) {} \
261: plist(type)(type* x, type* y, type* z, type* w) : \
262: voidP_list ((voidP) x, (voidP) y, (voidP) z, (voidP) w) {} \
263: plist(type)(plist(type)& ll) : \
264: voidP_list((voidP_list&) ll) {} \
265: ~plist(type)() {} \
266: operator const void*() const \
267: { return voidP_list::operator void*(); } \
268: bit operator==(plist(type)& ll) { return ((voidP_list&)*this == \
269: (voidP_list&)ll); } \
270: plist(type)& operator=(plist(type)& ll) { return (plist(type)&) \
271: ((voidP_list&)*this = (voidP_list&)ll); } \
272: plist(type) operator+(plist(type)& ll) { \
273: return (plist(type)&)((voidP_list&)*this + (voidP_list&)ll); } \
274: plist(type) operator+(type* t) { \
275: return (plist(type)&) ((voidP_list&)*this + (voidP)t); } \
276: plist(type)& put(type *t) { return (plist(type)&) \
277: voidP_list::put((voidP)t); } \
278: plist(type)& put(plist(type)& ll) { return (plist(type)&) \
279: voidP_list::put((voidP_list&)ll); } \
280: plist(type)& operator+=(plist(type)& oo) { return put(oo); } \
281: plist(type)& operator+=(type* t) { return put(t); } \
282: bit unputX(type *&t) { return voidP_list::unputX((voidP&)t); } \
283: bit unput() { return voidP_list::unput(); } \
284: bit getX(type *&t) { return voidP_list::getX((voidP&)t); } \
285: bit get() { return voidP_list::get(); } \
286: plist(type)& unget(type *x) { return (plist(type)&) \
287: voidP_list::unget((voidP)x); } \
288: plist(type)& unget(plist(type)& ll) { return (plist(type)&) \
289: voidP_list::unget((voidP_list&)ll); } \
290: bit firstX(type *&t) { return voidP_list::firstX((voidP&)t); } \
291: bit lastX(type *&t) { return voidP_list::lastX((voidP&)t); } \
292: /* getAt(0) returns the head of the list */ \
293: type* getAt(int ii) { return (type *) voidP_list::getAt(ii); } \
294: inline plistsubitem(type) operator[](unsigned ii); \
295: void sort(CMPFP(type) pf) { voidP_list::sort((CMPF(voidP))pf); } \
296: }; \
297: class plistiterator(type) : public voidP_list_iterator { \
298: public: \
299: plistiterator(type)(plist(type)& a, int i=0) : \
300: ((voidP_list&)a, i) {} \
301: plistiterator(type)(plistiterator(type)& a) : \
302: ((voidP_list_iterator&)a) {} \
303: ~plistiterator(type)() {} \
304: plistiterator(type)& operator=(plistiterator(type)& a) { return \
305: (plistiterator(type)&)((voidP_list_iterator&)*this = \
306: (voidP_list_iterator&)a); } \
307: bit nextX(type *&t) { return \
308: voidP_list_iterator::nextX((voidP&)t); } \
309: bit nextX(type **&t) { return \
310: voidP_list_iterator::nextX((voidP*&)t); } \
311: bit next() { return voidP_list_iterator::next(); } \
312: bit r_nextX(type *&t) { return \
313: voidP_list_iterator::r_nextX((voidP&)t); } \
314: bit r_nextX(type **&t) { return \
315: voidP_list_iterator::r_nextX((voidP*&)t); } \
316: bit r_next() { return voidP_list_iterator::r_next(); } \
317: bit peekX(type *&t) { return \
318: voidP_list_iterator::peekX((voidP&)t); } \
319: bit peekX(type **&t) { return \
320: voidP_list_iterator::peekX((voidP*&)t); } \
321: bit peek() { return voidP_list_iterator::peek(); } \
322: bit r_peekX(type *&t) { return \
323: voidP_list_iterator::r_peekX((voidP&)t); } \
324: bit r_peekX(type **&t) { return \
325: voidP_list_iterator::r_peekX((voidP*&)t); } \
326: bit r_peek() { return voidP_list_iterator::r_peek(); } \
327: bit remove() { return voidP_list_iterator::remove(); } \
328: bit r_remove() { return voidP_list_iterator::r_remove(); } \
329: bit removeX(type *&x) { return \
330: voidP_list_iterator::removeX((voidP&)x); } \
331: bit r_removeX(type *&x) { return \
332: voidP_list_iterator::r_removeX((voidP&)x); } \
333: void insert(type *&x) { voidP_list_iterator::insert((voidP&)x); } \
334: void r_insert(type *&x) { \
335: voidP_list_iterator::r_insert((voidP&)x); } \
336: void replace(type *x) { voidP_list_iterator::replace((voidP)x); } \
337: void r_replace(type *x) { \
338: voidP_list_iterator::r_replace((voidP)x); } \
339: }; \
340: class plistsubitem(type) : public voidPlistsubitem { \
341: public: \
342: plistsubitem(type)(plist(type)& t, int i) : \
343: voidPlistsubitem ((voidP_list&)t, i) {} \
344: plistsubitem(type)(plistsubitem(type)&ll) : \
345: voidPlistsubitem ((voidPlistsubitem&)ll){} \
346: ~plistsubitem(type)() {} \
347: type*& operator=(type *&t) { return \
348: (type*&) ((voidPlistsubitem&)*this =(voidP&)t); } \
349: operator type*() { return (type*) voidPlistsubitem::operator voidP(); } \
350: }; \
351: inline \
352: plistsubitem(type) \
353: plist(type)::operator[](unsigned ii) \
354: { \
355: return plistsubitem(type)(*this, ii); \
356: }
357:
358:
359: #define listimplement(type) \
360: GPT errorhandler(list,type) = genericerror; \
361: GPT errorhandler(listiterator,type) = genericerror; \
362: bit \
363: ListHead(type)::firstX(type& v) \
364: { \
365: lnnk(type) *aLink = (lnnk(type) *)LstHead::h(); \
366: return aLink ? (v = aLink->val, TRUE) : FALSE; \
367: } \
368: bit \
369: ListHead(type)::lastX(type& v) \
370: { \
371: lnnk(type) *aLink = (lnnk(type) *)LstHead::t; \
372: return aLink ? (v = aLink->val, TRUE) : FALSE; \
373: } \
374: ListHead(type)::~ListHead(type)() \
375: { \
376: lnnk(type) *temp; \
377: while (temp = (lnnk(type) *)LstHead::get()) \
378: delete temp; \
379: } \
380: bit \
381: listiterator(type)::remove() \
382: { \
383: lnnk(type) *aLink = (lnnk(type) *)lstiter::remove(); \
384: return aLink ? (delete aLink, TRUE) : FALSE; \
385: } \
386: bit \
387: listiterator(type)::removeX(type &x) \
388: { \
389: lnnk(type) *aLink = (lnnk(type) *)lstiter::remove(); \
390: return aLink ? (x = aLink->val, delete aLink, TRUE) : FALSE; \
391: } \
392: bit \
393: listiterator(type)::r_remove() \
394: { \
395: lnnk(type) *aLink = (lnnk(type) *)lstiter::r_remove(); \
396: return aLink ? (delete aLink, TRUE) : FALSE; \
397: } \
398: bit \
399: listiterator(type)::r_removeX(type &x) \
400: { \
401: lnnk(type) *aLink = (lnnk(type) *)lstiter::r_remove(); \
402: return aLink ? (x = aLink->val, delete aLink, TRUE) : FALSE; \
403: } \
404: bit \
405: listiterator(type)::nextX(type& t) \
406: { \
407: return atEnd() ? FALSE : \
408: (t = ((lnnk(type)*)lstiter::next())->val, TRUE); \
409: } \
410: bit \
411: listiterator(type)::nextX(type*& t) \
412: { \
413: return atEnd() ? FALSE : \
414: (theList()->separate(), t = &((lnnk(type)*)lstiter::next())->val, \
415: TRUE); \
416: } \
417: bit \
418: listiterator(type)::r_nextX(type& t) \
419: { \
420: return r_atEnd() ? FALSE : \
421: (t = ((lnnk(type)*)lstiter::r_next())->val, TRUE); \
422: } \
423: bit \
424: listiterator(type)::r_nextX(type*& t) \
425: { \
426: return r_atEnd() ? FALSE : \
427: (theList()->separate(), t = &((lnnk(type)*)lstiter::r_next())->val, \
428: TRUE); \
429: } \
430: bit \
431: listiterator(type)::r_peekX(type& t) \
432: { \
433: return r_atEnd() ? FALSE : \
434: (t = ((lnnk(type)*)lstiter::r_peek())->val, TRUE); \
435: } \
436: bit \
437: listiterator(type)::r_peekX(type*& t) \
438: { \
439: return r_atEnd() ? FALSE : \
440: (theList()->separate(), t = &((lnnk(type)*)lstiter::r_peek())->val, \
441: TRUE); \
442: } \
443: bit \
444: listiterator(type)::peekX(type& t) \
445: { \
446: return atEnd() ? FALSE : \
447: (t = ((lnnk(type)*)lstiter::peek())->val, TRUE); \
448: } \
449: bit \
450: listiterator(type)::peekX(type*& t) \
451: { \
452: return atEnd() ? FALSE : \
453: (theList()->separate(), t = &((lnnk(type)*)lstiter::peek())->val, \
454: TRUE); \
455: } \
456: bit \
457: listiterator(type)::replace(type& x) \
458: { \
459: return r_atEnd() ? FALSE : \
460: (theList()->separate(), ((lnnk(type)*)lstiter::r_peek())->val = x, \
461: TRUE); \
462: } \
463: bit \
464: listiterator(type)::r_replace(type& x) \
465: { \
466: return atEnd() ? FALSE : \
467: (theList()->separate(), ((lnnk(type)*)lstiter::peek())->val = x, \
468: TRUE); \
469: } \
470: bit \
471: list(type)::operator==(list(type)& x) const \
472: { \
473: if ( length() != x.length() ) \
474: return FALSE; \
475: if ( length() == 0 ) \
476: return TRUE; \
477: lnnk(type) *mine = (lnnk(type) *)head(); \
478: lnnk(type) *yours = (lnnk(type) *)x.head(); \
479: for ( int i = length(); i--; ) \
480: if ( mine->val == yours->val ) { \
481: mine = (lnnk(type) *)mine->nxt; \
482: yours = (lnnk(type) *)yours->nxt; \
483: } else \
484: return FALSE; \
485: return TRUE; \
486: } \
487: bit \
488: list(type)::getX(type& t) \
489: { \
490: lnnk(type) *aLink = (lnnk(type) *)List::get(); \
491: return aLink ? (t = aLink->val, delete aLink, TRUE) : FALSE; \
492: } \
493: bit \
494: list(type)::get() \
495: { \
496: lnnk(type) *aLink = (lnnk(type) *)List::get(); \
497: return aLink ? (delete aLink, TRUE) : FALSE; \
498: } \
499: bit \
500: list(type)::unputX(type& t) \
501: { \
502: lnnk(type) *aLink = (lnnk(type) *)List::unput(); \
503: return aLink ? (t = aLink->val, delete aLink, TRUE) : FALSE; \
504: } \
505: bit \
506: list(type)::unput() \
507: { \
508: lnnk(type) *aLink = (lnnk(type) *)List::unput(); \
509: return aLink ? (delete aLink, TRUE) : FALSE; \
510: } \
511: list(type)& \
512: list(type)::put(list(type)& a) \
513: { \
514: if (a.length()) { \
515: type t; \
516: listiterator(type) ita(a); \
517: while (ita.nextX(t)) \
518: put(t); \
519: } \
520: return *this; \
521: } \
522: list(type)& \
523: list(type)::unget(list(type)& a) \
524: { \
525: if (a.length()) { \
526: type t; \
527: listiterator(type) ita(a); \
528: ita.r_reset(); \
529: while (ita.r_nextX(t)) \
530: unget(t); \
531: } \
532: return *this; \
533: } \
534: listsubitem(type)::operator type() \
535: { \
536: type t; \
537: peekX(t); \
538: return t; \
539: } \
540: void \
541: list(type)::sepWork() \
542: { \
543: it->refCount--; \
544: ListHead(type) *newLst = new ListHead(type); \
545: if (tail()) { \
546: for (lnnk(type) *oldLnk = (lnnk(type) *)head();; \
547: oldLnk = (lnnk(type) *)oldLnk->nxt) { \
548: lnnk(type) *newLnk; \
549: newLst->put(newLnk = new lnnk(type)(oldLnk->val)); \
550: /* only be one or two listiterators (I hope) */ \
551: for (listiterator(type) *anIt = \
552: (listiterator(type) *)firstIt(); anIt; \
553: anIt = (listiterator(type) *)anIt->nextIt) { \
554: if (anIt->pred == oldLnk) { \
555: anIt->pred = newLnk; \
556: } \
557: } \
558: if ( oldLnk == tail() ) \
559: break; \
560: } \
561: } \
562: it = newLst; \
563: it->refCount = 1; \
564: } \
565: void \
566: list(type)::sort(CMPF(type) cmp) \
567: { \
568: if (length() < 2) return; \
569: separate(); \
570: voidP_list_sort_internal(*(voidP_list*)this, (CMPF(voidP))cmp); \
571: for (listiterator(type) *anIt = \
572: (listiterator(type) *)firstIt(); anIt; \
573: anIt = (listiterator(type) *)anIt->nextIt) \
574: anIt->reset(); \
575: }
576:
577: #define listoutimplement(type) \
578: ostream& \
579: operator<<(ostream& oo, list(type)& ll) \
580: { \
581: oo<<"("; \
582: if ( ll.length() ) { \
583: listiterator(type) it(ll); \
584: type t; \
585: it.nextX(t); \
586: oo<<t; \
587: while (it.nextX(t)) \
588: oo << " " << t; \
589: } else \
590: oo<<"EMPTYLIST"; \
591: oo<<")"; \
592: return oo; \
593: }
594:
595: #endif
596:
597: #define plistimplement(type) \
598: GPT errorhandler(Plist,type) = genericerror; \
599: GPT errorhandler(Plistiterator,type) = genericerror;
600:
601: #define plistoutimplement(type) \
602: ostream& operator<<(ostream& oo, plist(type)& ll) \
603: { \
604: oo<<"("; \
605: if ( ll.length() ) { \
606: plistiterator(type) it(ll); \
607: type *t; \
608: it.nextX(t); \
609: oo<<t; \
610: while (it.nextX(t)) \
611: oo << " " << t; \
612: } else \
613: oo<<"EMPTYLIST"; \
614: oo<<")"; \
615: return oo; \
616: }
617:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.