Branch data Line data Source code
1 : : //- Class: DLList
2 : : //- Description: DLList is a doubly linked list class that accepts generic
3 : : //- pointers to any object. It is controlled by a macro that
4 : : //- substitutes the specific pointer definitions in the class
5 : : //- declaration. The list is implemented as an array that is
6 : : //- grown by a specified amount when the list becomes full.
7 : : //- Insertions and deletions at any point other than the end
8 : : //- of the list are handled inefficiently at the current time
9 : : //- since all data from that point to the end is bubbled
10 : : //- down/up to fill/create the void. Operators {+} and {+=}
11 : : //- are provided as an efficient means of assigning one list
12 : : //- to another or appending one list onto another.
13 : : //-
14 : : //- The macro {DLListdeclare(name,typePtr)} is defined to
15 : : //- create specific instances of the DLList class.
16 : : //- {name} is the name of the DLList class created, and
17 : : //- {typePtr} is the type of elements stored in the list.
18 : : //- The macro also defines the functions {push(item)} and
19 : : //- {pop()} which allow {DLLists} to perform like stacks.
20 : : //-
21 : : //- Assumptions: All data are stored contiguously in the array with
22 : : //- empty slots at the end of the array.
23 : : //-
24 : : //- Owner: Greg Sjaardema
25 : : //- Checked by: Jim Hipp, 3/28/94
26 : : //- Version: $Id:
27 : :
28 : : #ifndef DLLIST_HPP
29 : : #define DLLIST_HPP
30 : :
31 : : #include "ArrayBasedContainer.hpp"
32 : : #include "CubitDefines.h"
33 : : #include "CubitMessage.hpp"
34 : : #include <cstring>
35 : : #include <cassert>
36 : : #include "CGMUtilConfigure.h"
37 : :
38 : : class CUBIT_UTIL_EXPORT DLList : public ArrayBasedContainer
39 : : {
40 : : public:
41 : :
42 : : DLList (int list_size);
43 : : //- Constructor: Create a list with initial size {list_size}. The list
44 : : //- will be grown by {list_size} each time it is filled. Memory for the
45 : : //- list is not allocated until the first element is inserted using
46 : : //- {insertLink}.
47 : : //- If {list_size} is zero, the default increment ({INCREMENT}) will be used
48 : : //- From an efficiency standpoint, it is very important that the
49 : : //- increment be set large enough to reduce the number of list
50 : : //- growths, but small enough to not waste memory.
51 : : //- It is more efficient to sligthly overestimate the size than
52 : : //- to underestimate the size.
53 : :
54 : : DLList(const DLList& copy_from); //- Copy Constructor
55 : :
56 : : virtual ~DLList();
57 : : //- Destructor: Free all resources used by this list.
58 : :
59 : : void step();
60 : : //- Move the pointer to the next element in the list. If pointer reaches
61 : : //- the end of the list, wrap around to the beginning
62 : :
63 : : void step(int n);
64 : : //- Move the pointer {n} positions forward in the list wrapping
65 : : //- around to the beginning of the list if the end is reached
66 : : //- If {n} is less than zero, move backward.
67 : :
68 : : void back();
69 : : //- Move the pointer to the previous element in the list. If reaches
70 : : //- the beginning of the list, wrap around to the end
71 : :
72 : : void back(int n);
73 : : //- Move the pointer {n} positions backward in the list wrapping
74 : : //- around to the end of the list if the beginning is reached.
75 : : //- If {n} is less than zero, move forward.
76 : :
77 : : void reset(); //- Set the pointer to the beginning of the list.
78 : : void last(); //- Set the pointer to the end of the list.
79 : :
80 : : void clean_out();
81 : : //- Delete all elements in the list, reset the pointer to zero.
82 : : //- Does not release memory already allocated for the list. This
83 : : //- call is more efficient creating a new list repeatadly within
84 : : //- a loop.
85 : :
86 : : void shrink(int k);
87 : : //- itemCount -= k; Doesn't change array elements.
88 : : //- Sets the index to the new end of the list if the old_index >=
89 : : //- new_itemCount
90 : :
91 : : CubitBoolean is_at_beginning() const;
92 : : //- returns CUBIT_TRUE if list is at beginning
93 : :
94 : : CubitBoolean is_at_end() const;
95 : : //- returns CUBIT_TRUE if list is at end
96 : :
97 : : int get_index();
98 : : //- Returns the current index value. This function and set_index are
99 : : //- included for efficiency when one wants to return to a previous location
100 : : //- in the list when it is known that the list has not changed. This
101 : : //- is the only recommended usage for these functions.
102 : :
103 : : void set_index(int new_index);
104 : : //- Sets the list to the given index. If the requested index is less than
105 : : //- zero or out of the scope of the number of items in the list, an assert
106 : : //- occurs. This function and get_index are included for efficiency when
107 : : //- one wants to return to a previous location in the list when it is known
108 : : //- that the list has not changed. This is the only recommended usage for
109 : : //- these functions.
110 : :
111 : : void compress();
112 : : //- Remove all null (0) pointers from list. Must be called after
113 : : //- {nullify_link()} is called before calling any list modification
114 : : //- function except {move_to()} or {nullify_link()}
115 : :
116 : : void reverse();
117 : : //- Reverse the items in the list.
118 : :
119 : : DLList& operator=(const DLList&);
120 : : //- Create a copy of a list.
121 : :
122 : : virtual void merge_unique(DLList& merge_list,
123 : : int merge_list_unique = CUBIT_FALSE);
124 : : //- Merges the contents of the list, merge_list, with those of "this"
125 : : //- list, ensuring that items that are being added do not already
126 : : //- exist in "this" list. If the items are known to appear in the
127 : : //- merge_list only once, then it can be done faster.
128 : :
129 : : void intersect(DLList& merge_list);
130 : : //- Merges the contents of the list merge_list, with those of "this"
131 : : //- list, but only the items that are common to both lists are kept
132 : : //- in "this" list. This is a rather costly process I think.
133 : : //- if merge_list is the same as the calling list, it just returns
134 : :
135 : : int append_unique(void* new_item);
136 : : //- Appends the new item to the list, if it doesn't already exist
137 : : //- in the list. In either case, index is not changed.
138 : : //- Return CUBIT_TRUE if the item was added, else CUBIT_FALSE.
139 : :
140 : 0 : SetDynamicMemoryAllocation(memoryManager)
141 : : //- overloaded new and delete operators
142 : :
143 : : //Added by J. Kraftcheck, 5/25/99
144 : : //These are not very efficient (O(n^2))
145 : : //In order to maintain the correct functionality of these
146 : : //operators when either list contains duplicates, it is
147 : : //also necessary to create a temporary list during the check.
148 : : int operator< ( const DLList& list ) const; //subset
149 : : int operator<=( const DLList& list ) const; //subset or equivalent
150 : : int operator> ( const DLList& list ) const; //superset
151 : : int operator>=( const DLList& list ) const; //superset or equivalent
152 : : int operator==( const DLList& list ) const; //non-order-dependend compare
153 : : int operator!=( const DLList& list ) const; //non-order-dependend compare
154 : :
155 : : protected:
156 : :
157 : : void insert_link(void* newBody);
158 : : //- put new item in list after current item and make it current
159 : :
160 : : void insert_link_first(void* new_item);
161 : : //- put new item in list at index 0 and make it current
162 : :
163 : : void append_link(void* newBody);
164 : : //- put new item at end of list and keep pointer where it is.
165 : : //- This call should be used to add items to a list if order
166 : : //- of items is not important.
167 : :
168 : : void *nullify_link();
169 : : //- Change the current item to a null pointer and return a pointer
170 : : //- to the item (before nulled). See the discussion for {nullItem}.
171 : :
172 : : void *cut_link();
173 : : //- remove the item at the current location and return a pointer to it.
174 : : //- The next node becomes the current node
175 : : //- Returns {NULL} if there are no items in list
176 : :
177 : : void* extract_link();
178 : : //- remove the item at the current location and return a pointer to it.
179 : : //- used for efficiency in cases where preservation of list order is not
180 : : //- important. moves last list item (itemCount - 1) to current index and
181 : : //- decrements itemCount. eliminates the need to perform the list bubble
182 : : //- down (i.e. cut_link) but sacrifices list order in the process. this
183 : : //- function should not be used when up-stream order from the removed node
184 : : //- is important. when processing a list using this function the user
185 : : //- should reset the list to the head (index = 0) before beginning to
186 : : //- ensure all list nodes are processed properly.
187 : : //- Returns {NULL} if there are no items in list
188 : :
189 : : CubitBoolean omit_link(void*);
190 : : //- Finds instance of item by matching pointer and deleting all instances
191 : : //- of it from the list. The current position of the list is not changed.
192 : : //- Returns CUBIT_TRUE if the item was found and deleted, CUBIT_FALSE
193 : : //- if not.
194 : :
195 : : void *next_item(int n=1) const;
196 : : //- Return a pointer to the item {n} items after the current item.
197 : : //- Index is not changed.
198 : : //- Returns {NULL} if there are no items in list
199 : :
200 : : void *prev_item(int n=1) const;
201 : : //- Return a pointer to the item {n} items before the current item.
202 : : //- Index is not changed.
203 : : //- Returns {NULL} if there are no items in list
204 : :
205 : : void *get_item() const;
206 : : //- Return a void pointer to the current item.
207 : : //- Index is not changed.
208 : : //- Returns {NULL} if there are no items in list
209 : :
210 : : void *change_item_to(void* newBody);
211 : :
212 : : void *move_to_and_remove_item(void* item);
213 : : //- search for item in list and remove if found. Function uses
214 : : //- move_to_item to find item and cut_link to remove it. If item is found
215 : : //- it is returned else NULL is returned.
216 : :
217 : : void *get_item_and_step();
218 : : //- Return a pointer to the current item and increment the index by 1.
219 : : //- Returns {NULL} if there are no items in list
220 : :
221 : : void *step_and_get_item();
222 : : //- Increment the index by 1, then return a pointer to the current item.
223 : : //- Returns {NULL} if there are no items in list
224 : :
225 : : void *get_item_and_back();
226 : : //- Return a pointer to the current item and decrement the index by 1.
227 : : //- Returns {NULL} if there are no items in list
228 : :
229 : : CubitBoolean move_to_item(void* body);
230 : : CubitBoolean move_to_nearby_item(void* body);
231 : : int distance_to_nearby_item(void* body);
232 : : //- Sets the index to the item in the list that matches {body}
233 : : //- Returns {CUBIT_FALSE} if there are no items in list or if
234 : : //- the requested item is not in the list.
235 : : //- Returns {CUBIT_TRUE} if the item is Found.
236 : : //- move_to_item searches from the current item forward
237 : : //- move_to_nearby_item searches from the current item both
238 : : //- forward and backward.
239 : :
240 : : CubitBoolean move_between_items(void *body1, void *body2);
241 : : //- Sets the index to the place matching one of the items, where the
242 : : //- previous item is the other item (i.e. no order assumed on the
243 : : //- parameters)
244 : : //- Returns {CUBIT_FALSE} if there are no items in list or the items
245 : : //- are not consecutive in the list.
246 : : //- Returns {CUBIT_TRUE} if the consecutive items are found.
247 : :
248 : : //void *change_item_to(void* newBody); //commented out 8-12-98
249 : : //- Sets the item at the current index value to {newBody} and
250 : : //- returns the old value at that location.
251 : : //- Returns {NULL} if there are no items in list
252 : :
253 : : void *nullItem;
254 : : //- Item removed in last {nullify_link} operation. It is saved here
255 : : //- only for execution speed reasons. The primary use of {nullify_link}
256 : : //- is in deleting a partial mesh. After {nullify_link()} is called,
257 : : //- a destructor for that item is called which does a {move_to}.
258 : : //- Since we just nulled that entry, we can't {move_to()} it, so
259 : : //- {move_to()} checks to see if the item matches
260 : : //- {nullItem}. It it does, we return, otherwise we do a normal
261 : : //- {move_to()}.
262 : :
263 : : int where_is_item( void *item ) const;
264 : : //- Return index of item in list, or -1 if item is not in the list.
265 : : //- This information should never be passed outside the class.
266 : :
267 : : int index; //- index of current item in {listArray}
268 : :
269 : : private:
270 : :
271 : : // int listIsSorted; //- This is now a bit in ArrayBasedContainer.hpp
272 : : //- flag used to indicate if an ordered list is no longer ordered
273 : :
274 : : static const char* type() { return "DLList"; }
275 : : //- declare which type of ArrayBasedContainer this is
276 : :
277 : : static MemoryManager memoryManager;
278 : : //- memory management static objects for performing block allocation
279 : : };
280 : :
281 : : // *** inline function definitions *******************************************
282 : :
283 : : inline void DLList::step() { if (itemCount) index++;
284 : : if (index >= itemCount) index = 0;}
285 : :
286 : : inline void DLList::step(int n) {
287 : : if (itemCount) {
288 : : index += n;
289 : : if (index < 0) {
290 : : index = itemCount - (-index)%itemCount;
291 : : }
292 : : if (index >= itemCount) {
293 : : index %= itemCount;
294 : : }
295 : : }
296 : : }
297 : :
298 : :
299 [ + + ]: 1116 : inline void DLList::back() { if (itemCount) index--;
300 [ + + ]: 558 : if (index < 0) index = itemCount - 1; }
301 : :
302 : : inline void DLList::back(int n) { step(-n); }
303 : :
304 : 96 : inline void DLList::reset() { index = 0; }
305 : :
306 [ + - ]: 1116 : inline void DLList::last() { if (itemCount) index = itemCount - 1; }
307 : :
308 : : inline CubitBoolean DLList::is_at_beginning() const
309 : : {if (!index) return CUBIT_TRUE;
310 : : else return CUBIT_FALSE;}
311 : :
312 : : inline CubitBoolean DLList::is_at_end() const
313 : : {
314 : : if (itemCount == 0 || index == itemCount-1)
315 : : return CUBIT_TRUE;
316 : : else
317 : : return CUBIT_FALSE;
318 : : }
319 : :
320 : : //- Add item to end of list, do not change current index value.
321 : : //- This function used to reduce time required to do an insert
322 : : //- followed by a move_to back to where we were.
323 : 0 : inline void DLList::append_link ( void* new_item )
324 : : {
325 [ # # ]: 0 : assert(new_item != NULL);
326 : : // see if the list must be lengthened
327 : :
328 [ # # ]: 0 : if ( itemCount == listLength )
329 : 0 : lengthen_list();
330 : :
331 : 0 : listArray[itemCount++] = new_item;
332 : 0 : }
333 : :
334 : 462 : inline void *DLList::get_item () const
335 : : {
336 [ - + ]: 462 : if ( !itemCount ) {
337 [ # # ][ # # ]: 0 : PRINT_WARNING("Attempted get of empty DLList\n");
338 : 0 : return NULL;
339 : : }
340 : : else {
341 [ - + ]: 462 : assert(listArray[index] != NULL);
342 : 462 : return listArray[index];
343 : : }
344 : : }
345 : :
346 : 0 : inline void *DLList::get_item_and_step ()
347 : : {
348 : : #ifndef GDSJAAR
349 [ # # ]: 0 : if ( !itemCount )
350 : : {
351 [ # # ][ # # ]: 0 : PRINT_WARNING("Attempted get_and_step from empty DLList\n");
352 : 0 : return NULL;
353 : : }
354 : : else
355 : : #endif
356 : : {
357 : 0 : void *temp = listArray[index++];
358 [ # # ]: 0 : if (index == itemCount) index=0;
359 [ # # ]: 0 : assert(temp != NULL);
360 : 0 : return temp;
361 : : }
362 : : }
363 : :
364 : : inline void *DLList::next_item ( int n ) const
365 : : {
366 : : if ( !itemCount )
367 : : {
368 : : PRINT_WARNING("Attempted next of empty DLList\n");
369 : : return NULL;
370 : : }
371 : : else
372 : : {
373 : : // return the proper index
374 : : // beware of negative n leading to negative new_index
375 : : int new_index = index+n;
376 : : while (new_index < 0)
377 : : new_index += itemCount;
378 : :
379 : : new_index = new_index%itemCount;
380 : :
381 : : assert(listArray[new_index] != NULL);
382 : : return listArray[new_index];
383 : : }
384 : : }
385 : :
386 : : inline void DLList::clean_out ()
387 : : {
388 : : itemCount = 0;
389 : : index = 0;
390 : : }
391 : :
392 : :
393 : : class DLListIterator
394 : : {
395 : : public:
396 : : DLListIterator() { index = 0; }
397 : : // constructor
398 : : virtual ~DLListIterator() {}
399 : : // destructor
400 : :
401 : : void step( int n = 1 )
402 : : { index += n; }
403 : :
404 : : protected:
405 : :
406 : : // data
407 : : int index;
408 : : };
409 : :
410 : :
411 : : // *** macro definitions *****************************************************
412 : :
413 : : // Three separate derived classes are possible from DLList all of which are
414 : : // defined by one of the following macros:
415 : : //
416 : : // DLListdeclare(name, typePtr)
417 : : //
418 : : // The first is the standard DLList declaration macro, the second provides
419 : : // sorting/searching functionality, and the third provides sorting/searching
420 : : // functionallity for simple intrinsic data lists. All three are described in
421 : : // more detail below. Two support macros
422 : : //
423 : : // CommonDefine(typePtr, notSorted)
424 : : // CommonSortedDefine(name, typePtr)
425 : : //
426 : : // are also defined but are not meant to be utilized externally. They simply
427 : : // collect class definitions that are common to the three list defintions
428 : : // macros described above
429 : :
430 : : // The following macro defintion provides the core functionality for the
431 : : // standard DLListdeclare as-well-as the sorted SDLListdeclare macros.
432 : : // typePtr is still the type pointer of the data stored by the list.
433 : : // notSorted is simply a semi-colon (;) for the standard list (if white space
434 : : // is used for the parameter the CPP outputs a warning message that a NULL
435 : : // string is being substituted for the parameter ... the proper outcome but
436 : : // the excessive warning messages are a real nuisance) and a call to the
437 : : // set_sort_flag function (with an argument of False) for the sorted lists.
438 : : #define CommonDefine(typePtr, notSorted) \
439 : : \
440 : : typePtr remove() {return (typePtr) cut_link();} \
441 : : int omit(typePtr objPtr) {return omit_link((void*) objPtr);} \
442 : : typePtr get() const {return (typePtr) get_item();} \
443 : : CubitBoolean set(typePtr objPtr) {return move_to_item((void*)objPtr);} \
444 : : typePtr next() const {return (typePtr) next_item();} \
445 : : typePtr next( int n ) const {return (typePtr) next_item(n);} \
446 : : typePtr prev() const {return (typePtr) prev_item();} \
447 : : typePtr prev(int n) const {return (typePtr) prev_item(n);} \
448 : : typePtr get_and_step() {return (typePtr) get_item_and_step();} \
449 : : typePtr get_and_back() {return (typePtr) get_item_and_back();} \
450 : : typePtr step_and_get() {return (typePtr) step_and_get_item();} \
451 : : \
452 : : CubitBoolean move_between(typePtr objPtr1, typePtr objPtr2) \
453 : : { \
454 : : if (nullItem && (((typePtr) nullItem == objPtr1) || \
455 : : ((typePtr) nullItem == objPtr2) )) \
456 : : return CUBIT_FALSE; \
457 : : else \
458 : : return (move_between_items(((void*) objPtr1), ((void*) objPtr2))); \
459 : : } \
460 : : typePtr pop() {last(); return remove();} \
461 : : typePtr push(typePtr objPtr) \
462 : : { notSorted last(); append_link((void*)objPtr); return objPtr; } \
463 : : \
464 : : void insert_first(typePtr objPtr) \
465 : : { notSorted insert_link_first((void*) objPtr); } \
466 : : void append(typePtr objPtr) \
467 : : { notSorted append_link((void*) objPtr); } \
468 : : typePtr extract() \
469 : : { notSorted return (typePtr) extract_link(); } \
470 : : typePtr nullify() \
471 : : { notSorted return (typePtr) nullify_link(); } \
472 : : typePtr change_to(typePtr objPtr) \
473 : : { notSorted return (typePtr) change_item_to((void*) objPtr); } \
474 : :
475 : :
476 : :
477 : : // The standard DLList declaration macro creates a list class that is derived
478 : : // from DLList. The class (name) stores pointers to data of type typePtr.
479 : : // No list ordering capability is provided by the standard declare macro
480 : : #define DLListdeclare(name, typePtr) \
481 : : \
482 : : class name : public DLList \
483 : : { \
484 : : friend class name##Iterator; \
485 : : \
486 : : public: \
487 : : \
488 : : name(int list_size=0) : DLList (list_size) \
489 : : { \
490 : : assert(sizeof(typePtr) == sizeof(void*)); \
491 : : } \
492 : : void insert(typePtr objPtr) { insert_link((void*) objPtr); } \
493 : : typePtr remove(typePtr objPtr) \
494 : : { \
495 : : return (typePtr) move_to_and_remove_item((void*) objPtr); \
496 : : } \
497 : : CubitBoolean move_to(const typePtr objPtr) \
498 : : { \
499 : : if (nullItem && (typePtr)nullItem == objPtr) \
500 : : return CUBIT_FALSE; \
501 : : else \
502 : : return (move_to_item((void*) objPtr)); \
503 : : } \
504 : : CubitBoolean move_to_nearby(const typePtr objPtr) \
505 : : { \
506 : : if (nullItem && (typePtr)nullItem == objPtr) \
507 : : return CUBIT_FALSE; \
508 : : else \
509 : : return (move_to_nearby_item((void*) objPtr)); \
510 : : } \
511 : : int distance_to_nearby(const typePtr objPtr) \
512 : : { \
513 : : if (nullItem && (typePtr)nullItem == objPtr) \
514 : : return CUBIT_FALSE; \
515 : : else \
516 : : return (distance_to_nearby_item((void*) objPtr)); \
517 : : } \
518 : : CubitBoolean is_in_list(const typePtr objPtr) const \
519 : : { \
520 : : return where_is_item((void*) objPtr) >= 0 ? CUBIT_TRUE : CUBIT_FALSE; \
521 : : } \
522 : : name& operator=(const name##Iterator & iter); \
523 : : CommonDefine(typePtr, ;) \
524 : : }; \
525 : : \
526 : : class name##Iterator : public DLListIterator \
527 : : { \
528 : : friend class name; \
529 : : public: \
530 : : name##Iterator() : DLListIterator() { dlList = NULL; } \
531 : : void watch( const name *dl_list ) \
532 : : { dlList = dl_list; } \
533 : : int size() const { return dlList ? dlList->size() : 0; } \
534 : : typePtr get() const \
535 : : {return (typePtr) get_item();} \
536 : : typePtr get_and_step( int n = 1 ) \
537 : : { \
538 : : typePtr temp = (typePtr) get_item(); \
539 : : step( n ); \
540 : : return temp; \
541 : : } \
542 : : typePtr next( int n = 1 ) const \
543 : : {return (typePtr) next_item(n);} \
544 : : CubitBoolean is_in_list( const typePtr objPtr ) const \
545 : : { return where_is_item( (void*) objPtr ) >= 0 ? CUBIT_TRUE : \
546 : : CUBIT_FALSE; } \
547 : : CubitBoolean move_to(const typePtr objPtr ) \
548 : : { index = where_is_item( (void*) objPtr ); \
549 : : return index > 0 ? CUBIT_TRUE : CUBIT_FALSE; } \
550 : : private: \
551 : : const name *dlList; \
552 : : inline void *next_item( int n = 1 ) const; \
553 : : void *get_item() const { return next_item(0); } \
554 : : int where_is_item( void *item ) const \
555 : : { return dlList ? dlList->where_is_item( item ) : -1; } \
556 : : }; \
557 : : \
558 : : inline void *name##Iterator::next_item( int n ) const \
559 : : { \
560 : : int size_loc = size(); \
561 : : if ( size_loc == 0 ) \
562 : : return NULL; \
563 : : int index_loc = index + n; \
564 : : while ( index_loc < 0 ) \
565 : : index_loc += size_loc; \
566 : : while ( index_loc >= size_loc ) \
567 : : index_loc -= size_loc; \
568 : : return dlList->listArray[index_loc]; \
569 : : } \
570 : : inline name& name::operator=(const name##Iterator& iter) \
571 : : { if ( iter.dlList ) *this = *(iter.dlList); \
572 : : else clean_out(); \
573 : : return *this; } \
574 : :
575 : : // Use the list iterator like so:
576 : : //
577 : : // DLListDeclare( DLCubitNodeList, CubitNode * );
578 : : // DLCubitNodeList node_list;
579 : : // DLCubitNodeListIterator node_iterator;
580 : : // node_iterator.watch( node_list );
581 : : // node_iterator.reset();
582 : : // for (int i = node_iterator.size(); i--; )
583 : : // {
584 : : // CubitNode *node = node_iterator.get_and_step();
585 : : // ...
586 : : // }
587 : : //
588 : :
589 : : #endif //- DLLIST_HPP
590 : :
591 : :
|