Root/
Source at commit 1322 created 12 years 8 months ago. By meklort, Add doxygen to utils folder | |
---|---|
1 | /******************************************************************************␊ |
2 | *␊ |
3 | * $Id: sortdict.h,v 1.3 2001/03/19 19:27:41 root Exp $␊ |
4 | *␊ |
5 | *␊ |
6 | * Copyright (C) 1997-2011 by Dimitri van Heesch.␊ |
7 | *␊ |
8 | * Permission to use, copy, modify, and distribute this software and its␊ |
9 | * documentation under the terms of the GNU General Public License is hereby ␊ |
10 | * granted. No representations are made about the suitability of this software ␊ |
11 | * for any purpose. It is provided "as is" without express or implied warranty.␊ |
12 | * See the GNU General Public License for more details.␊ |
13 | *␊ |
14 | * Documents produced by Doxygen are derivative works derived from the␊ |
15 | * input used in their production; they are not affected by this license.␊ |
16 | *␊ |
17 | */␊ |
18 | ␊ |
19 | #ifndef _SORTDICT_H␊ |
20 | #define _SORTDICT_H␊ |
21 | ␊ |
22 | #include "qtbc.h"␊ |
23 | #include <qlist.h>␊ |
24 | #include <qdict.h>␊ |
25 | #include <qintdict.h>␊ |
26 | ␊ |
27 | #define AUTORESIZE 1␊ |
28 | ␊ |
29 | #if AUTORESIZE␊ |
30 | const uint SDict_primes[] = ␊ |
31 | {␊ |
32 | 17,␊ |
33 | 29,␊ |
34 | 47,␊ |
35 | 71,␊ |
36 | 113,␊ |
37 | 179,␊ |
38 | 293,␊ |
39 | 457,␊ |
40 | 733,␊ |
41 | 1171,␊ |
42 | 1871,␊ |
43 | 2999,␊ |
44 | 4787,␊ |
45 | 7669,␊ |
46 | 12251,␊ |
47 | 19603,␊ |
48 | 31379,␊ |
49 | 50177,␊ |
50 | 80287,␊ |
51 | 128449,␊ |
52 | 205519,␊ |
53 | 328829,␊ |
54 | 526139,␊ |
55 | 841801,␊ |
56 | 1346881,␊ |
57 | 2155007,␊ |
58 | 3448033,␊ |
59 | 5516827,␊ |
60 | 8826919,␊ |
61 | 14123059,␊ |
62 | 23538433,␊ |
63 | 39230771,␊ |
64 | 65384537,␊ |
65 | 108974231,␊ |
66 | 181623707,␊ |
67 | 302706181,␊ |
68 | 504510283,␊ |
69 | 840850487,␊ |
70 | 0xffffffff␊ |
71 | };␊ |
72 | #endif␊ |
73 | ␊ |
74 | template<class T> class SDict;␊ |
75 | template<class T> class SIntDict;␊ |
76 | ␊ |
77 | /*! internal wrapper class that redirects compareItems() to the ␊ |
78 | * dictionary ␊ |
79 | */␊ |
80 | template<class T>␊ |
81 | class SList : public QList<T>␊ |
82 | {␊ |
83 | public:␊ |
84 | SList(SDict<T> *owner) : m_owner(owner) {}␊ |
85 | virtual ~SList() {}␊ |
86 | int compareItems(GCI item1,GCI item2)␊ |
87 | {␊ |
88 | return m_owner->compareItems(item1,item2);␊ |
89 | }␊ |
90 | private:␊ |
91 | SDict<T> *m_owner; ␊ |
92 | };␊ |
93 | ␊ |
94 | /*! Ordered dictionary of elements of type T. ␊ |
95 | * Internally uses a QList<T> and a QDict<T>.␊ |
96 | */␊ |
97 | template<class T>␊ |
98 | class SDict ␊ |
99 | {␊ |
100 | private:␊ |
101 | SList<T> *m_list;␊ |
102 | QDict<T> *m_dict;␊ |
103 | int m_sizeIndex;␊ |
104 | ␊ |
105 | public:␊ |
106 | /*! Create an ordered dictionary.␊ |
107 | * \param size The size of the dictionary. Should be a prime number for␊ |
108 | * best distribution of elements.␊ |
109 | * \param caseSensitive indicated whether the keys should be sorted␊ |
110 | * in a case sensitive way.␊ |
111 | */␊ |
112 | SDict(int size,bool caseSensitive=TRUE) : m_sizeIndex(0)␊ |
113 | {␊ |
114 | m_list = new SList<T>(this);␊ |
115 | #if AUTORESIZE␊ |
116 | while ((uint)size>SDict_primes[m_sizeIndex]) m_sizeIndex++;␊ |
117 | m_dict = new QDict<T>(SDict_primes[m_sizeIndex],caseSensitive);␊ |
118 | #else␊ |
119 | m_dict = new QDict<T>(size,caseSensitive);␊ |
120 | #endif␊ |
121 | }␊ |
122 | ␊ |
123 | /*! Destroys the dictionary */␊ |
124 | virtual ~SDict() ␊ |
125 | {␊ |
126 | delete m_list;␊ |
127 | delete m_dict;␊ |
128 | }␊ |
129 | ␊ |
130 | /*! Appends an element to the dictionary. The element is owned by the␊ |
131 | * dictionary.␊ |
132 | * \param key The unique key to use to quicky find the item later on.␊ |
133 | * \param d The compound to add.␊ |
134 | * \sa find()␊ |
135 | */␊ |
136 | void append(const char *key,const T *d)␊ |
137 | {␊ |
138 | m_list->append(d);␊ |
139 | m_dict->insert(key,d);␊ |
140 | #if AUTORESIZE␊ |
141 | if (m_dict->size()>SDict_primes[m_sizeIndex])␊ |
142 | {␊ |
143 | m_dict->resize(SDict_primes[++m_sizeIndex]);␊ |
144 | }␊ |
145 | #endif␊ |
146 | }␊ |
147 | ␊ |
148 | /*! Prepends an element to the dictionary. The element is owned by the␊ |
149 | * dictionary.␊ |
150 | * \param key The unique key to use to quicky find the item later on.␊ |
151 | * \param d The compound to add.␊ |
152 | * \sa find()␊ |
153 | */␊ |
154 | void prepend(const char *key,const T *d)␊ |
155 | {␊ |
156 | m_list->prepend(d);␊ |
157 | m_dict->insert(key,d);␊ |
158 | #if AUTORESIZE␊ |
159 | if (m_dict->size()>SDict_primes[m_sizeIndex])␊ |
160 | {␊ |
161 | m_dict->resize(SDict_primes[++m_sizeIndex]);␊ |
162 | }␊ |
163 | #endif␊ |
164 | }␊ |
165 | ␊ |
166 | /*! Remove an item from the dictionary */␊ |
167 | bool remove(const char *key)␊ |
168 | {␊ |
169 | T *item = m_dict->take(key);␊ |
170 | return item ? m_list->remove(item) : FALSE;␊ |
171 | }␊ |
172 | ␊ |
173 | /*! Take an item out of the dictionary without deleting it */␊ |
174 | T *take(const char *key)␊ |
175 | {␊ |
176 | T *item = m_dict->take(key);␊ |
177 | if (item)␊ |
178 | {␊ |
179 | int i = m_list->find(item);␊ |
180 | m_list->take(i);␊ |
181 | }␊ |
182 | return item;␊ |
183 | }␊ |
184 | ␊ |
185 | /*! Sorts the members of the dictionary. First appending a number␊ |
186 | * of members and then sorting them is faster (O(NlogN) than using ␊ |
187 | * inSort() for each member (O(N^2)).␊ |
188 | */␊ |
189 | void sort()␊ |
190 | {␊ |
191 | m_list->sort();␊ |
192 | }␊ |
193 | /*! Inserts a compound into the dictionary in a sorted way.␊ |
194 | * \param key The unique key to use to quicky find the item later on.␊ |
195 | * \param d The compound to add.␊ |
196 | * \sa find()␊ |
197 | */␊ |
198 | void inSort(const char *key,const T *d)␊ |
199 | {␊ |
200 | m_list->inSort(d);␊ |
201 | m_dict->insert(key,d);␊ |
202 | #if AUTORESIZE␊ |
203 | if (m_dict->size()>SDict_primes[m_sizeIndex])␊ |
204 | {␊ |
205 | m_dict->resize(SDict_primes[++m_sizeIndex]);␊ |
206 | }␊ |
207 | #endif␊ |
208 | }␊ |
209 | ␊ |
210 | /*! Indicates whether or not the dictionary owns its elements */␊ |
211 | void setAutoDelete(bool val)␊ |
212 | {␊ |
213 | m_list->setAutoDelete(val);␊ |
214 | }␊ |
215 | ␊ |
216 | /*! Looks up a compound given its key. ␊ |
217 | * \param key The key to identify this element.␊ |
218 | * \return The requested compound or zero if it cannot be found.␊ |
219 | * \sa append() ␊ |
220 | */␊ |
221 | T *find(const char *key)␊ |
222 | {␊ |
223 | return m_dict->find(key);␊ |
224 | }␊ |
225 | T *find(const QCString &key)␊ |
226 | {␊ |
227 | return m_dict->find(key);␊ |
228 | }␊ |
229 | T *find(const QString &key)␊ |
230 | {␊ |
231 | return m_dict->find(key);␊ |
232 | }␊ |
233 | ␊ |
234 | /*! Equavalent to find(). */␊ |
235 | T *operator[](const char *key) const␊ |
236 | {␊ |
237 | return m_dict->find(key);␊ |
238 | }␊ |
239 | ␊ |
240 | /*! Returns the item at position \a i in the sorted dictionary */␊ |
241 | T *at(uint i)␊ |
242 | {␊ |
243 | return m_list->at(i);␊ |
244 | }␊ |
245 | ␊ |
246 | /*! Function that is used to compare two items when sorting.␊ |
247 | * Overload this to properly sort items.␊ |
248 | * \sa inSort()␊ |
249 | */␊ |
250 | virtual int compareItems(GCI item1,GCI item2)␊ |
251 | {␊ |
252 | return item1!=item2;␊ |
253 | }␊ |
254 | ␊ |
255 | /*! Clears the dictionary. Will delete items if setAutoDelete() was␊ |
256 | * set to \c TRUE.␊ |
257 | * \sa setAutoDelete␊ |
258 | */␊ |
259 | void clear()␊ |
260 | {␊ |
261 | m_list->clear();␊ |
262 | m_dict->clear();␊ |
263 | }␊ |
264 | ␊ |
265 | /*! Returns the number of items stored in the dictionary␊ |
266 | */␊ |
267 | int count() const␊ |
268 | {␊ |
269 | return m_list->count();␊ |
270 | }␊ |
271 | ␊ |
272 | class Iterator; // first forward declare␊ |
273 | friend class Iterator; // then make it a friend␊ |
274 | /*! Simple iterator for SDict. It iterates in the order in which the␊ |
275 | * elements are stored.␊ |
276 | */␊ |
277 | class Iterator␊ |
278 | {␊ |
279 | public:␊ |
280 | /*! Create an iterator given the dictionary. */␊ |
281 | Iterator(const SDict<T> &dict)␊ |
282 | {␊ |
283 | m_li = new QListIterator<T>(*dict.m_list);␊ |
284 | }␊ |
285 | ␊ |
286 | /*! Destroys the dictionary */␊ |
287 | virtual ~Iterator()␊ |
288 | {␊ |
289 | delete m_li;␊ |
290 | }␊ |
291 | ␊ |
292 | /*! Set the iterator to the first element in the list. ␊ |
293 | * \return The first compound, or zero if the list was empty. ␊ |
294 | */␊ |
295 | T *toFirst() const␊ |
296 | {␊ |
297 | return m_li->toFirst();␊ |
298 | }␊ |
299 | ␊ |
300 | /*! Set the iterator to the last element in the list. ␊ |
301 | * \return The first compound, or zero if the list was empty. ␊ |
302 | */␊ |
303 | T *toLast() const␊ |
304 | {␊ |
305 | return m_li->toLast();␊ |
306 | }␊ |
307 | ␊ |
308 | /*! Returns the current compound */␊ |
309 | T *current() const␊ |
310 | {␊ |
311 | return m_li->current();␊ |
312 | }␊ |
313 | ␊ |
314 | /*! Moves the iterator to the next element.␊ |
315 | * \return the new "current" element, or zero if the iterator was␊ |
316 | * already pointing at the last element.␊ |
317 | */␊ |
318 | T *operator++()␊ |
319 | {␊ |
320 | return m_li->operator++();␊ |
321 | }␊ |
322 | ␊ |
323 | /*! Moves the iterator to the previous element.␊ |
324 | * \return the new "current" element, or zero if the iterator was␊ |
325 | * already pointing at the first element.␊ |
326 | */␊ |
327 | T *operator--()␊ |
328 | {␊ |
329 | return m_li->operator--();␊ |
330 | }␊ |
331 | ␊ |
332 | private:␊ |
333 | QListIterator<T> *m_li;␊ |
334 | };␊ |
335 | ␊ |
336 | class IteratorDict; // first forward declare␊ |
337 | friend class IteratorDict; // then make it a friend␊ |
338 | /*! Simple iterator for SDict. It iterates in over the dictionary elements␊ |
339 | * in an unsorted way, but does provide information about the element's key.␊ |
340 | */␊ |
341 | class IteratorDict␊ |
342 | {␊ |
343 | public:␊ |
344 | /*! Create an iterator given the dictionary. */␊ |
345 | IteratorDict(const SDict<T> &dict)␊ |
346 | {␊ |
347 | m_di = new QDictIterator<T>(*dict.m_dict);␊ |
348 | }␊ |
349 | ␊ |
350 | /*! Destroys the dictionary */␊ |
351 | virtual ~IteratorDict()␊ |
352 | {␊ |
353 | delete m_di;␊ |
354 | }␊ |
355 | ␊ |
356 | /*! Set the iterator to the first element in the list. ␊ |
357 | * \return The first compound, or zero if the list was empty. ␊ |
358 | */␊ |
359 | T *toFirst() const␊ |
360 | {␊ |
361 | return m_di->toFirst();␊ |
362 | }␊ |
363 | ␊ |
364 | /*! Set the iterator to the last element in the list. ␊ |
365 | * \return The first compound, or zero if the list was empty. ␊ |
366 | */␊ |
367 | T *toLast() const␊ |
368 | {␊ |
369 | return m_di->toLast();␊ |
370 | }␊ |
371 | ␊ |
372 | /*! Returns the current compound */␊ |
373 | T *current() const␊ |
374 | {␊ |
375 | return m_di->current();␊ |
376 | }␊ |
377 | ␊ |
378 | /*! Returns the current key */␊ |
379 | QCString currentKey() const␊ |
380 | {␊ |
381 | return m_di->currentKey();␊ |
382 | }␊ |
383 | ␊ |
384 | /*! Moves the iterator to the next element.␊ |
385 | * \return the new "current" element, or zero if the iterator was␊ |
386 | * already pointing at the last element.␊ |
387 | */␊ |
388 | T *operator++()␊ |
389 | {␊ |
390 | return m_di->operator++();␊ |
391 | }␊ |
392 | ␊ |
393 | /*! Moves the iterator to the previous element.␊ |
394 | * \return the new "current" element, or zero if the iterator was␊ |
395 | * already pointing at the first element.␊ |
396 | */␊ |
397 | T *operator--()␊ |
398 | {␊ |
399 | return m_di->operator--();␊ |
400 | }␊ |
401 | ␊ |
402 | private:␊ |
403 | QDictIterator<T> *m_di;␊ |
404 | };␊ |
405 | };␊ |
406 | ␊ |
407 | /*! internal wrapper class that redirects compareItems() to the ␊ |
408 | * dictionary ␊ |
409 | */␊ |
410 | template<class T>␊ |
411 | class SIntList : public QList<T>␊ |
412 | {␊ |
413 | public:␊ |
414 | SIntList(SIntDict<T> *owner) : m_owner(owner) {}␊ |
415 | virtual ~SIntList() {}␊ |
416 | int compareItems(GCI item1,GCI item2)␊ |
417 | {␊ |
418 | return m_owner->compareItems(item1,item2);␊ |
419 | }␊ |
420 | private:␊ |
421 | SIntDict<T> *m_owner; ␊ |
422 | };␊ |
423 | ␊ |
424 | /*! Ordered dictionary of elements of type T. ␊ |
425 | * Internally uses a QList<T> and a QIntDict<T>.␊ |
426 | */␊ |
427 | template<class T>␊ |
428 | class SIntDict ␊ |
429 | {␊ |
430 | private:␊ |
431 | SIntList<T> *m_list;␊ |
432 | QIntDict<T> *m_dict;␊ |
433 | int m_sizeIndex;␊ |
434 | ␊ |
435 | public:␊ |
436 | /*! Create an ordered dictionary.␊ |
437 | * \param size The size of the dictionary. Should be a prime number for␊ |
438 | * best distribution of elements.␊ |
439 | */␊ |
440 | SIntDict(int size) : m_sizeIndex(0)␊ |
441 | {␊ |
442 | m_list = new SIntList<T>(this);␊ |
443 | #if AUTORESIZE␊ |
444 | while ((uint)size>SDict_primes[m_sizeIndex]) m_sizeIndex++;␊ |
445 | m_dict = new QIntDict<T>(SDict_primes[m_sizeIndex]);␊ |
446 | #else␊ |
447 | m_dict = new QIntDict<T>(size);␊ |
448 | #endif␊ |
449 | }␊ |
450 | ␊ |
451 | /*! Destroys the dictionary */␊ |
452 | virtual ~SIntDict() ␊ |
453 | {␊ |
454 | delete m_list;␊ |
455 | delete m_dict;␊ |
456 | }␊ |
457 | ␊ |
458 | /*! Appends a compound to the dictionary. The element is owned by the␊ |
459 | * dictionary.␊ |
460 | * \param key The unique key to use to quicky find the item later on.␊ |
461 | * \param d The compound to add.␊ |
462 | * \sa find()␊ |
463 | */␊ |
464 | void append(int key,const T *d)␊ |
465 | {␊ |
466 | m_list->append(d);␊ |
467 | m_dict->insert(key,d);␊ |
468 | #if AUTORESIZE␊ |
469 | if (m_dict->size()>SDict_primes[m_sizeIndex])␊ |
470 | {␊ |
471 | m_dict->resize(SDict_primes[++m_sizeIndex]);␊ |
472 | }␊ |
473 | #endif␊ |
474 | }␊ |
475 | ␊ |
476 | /*! Prepend a compound to the dictionary. The element is owned by the␊ |
477 | * dictionary.␊ |
478 | * \param key The unique key to use to quicky find the item later on.␊ |
479 | * \param d The compound to add.␊ |
480 | * \sa find()␊ |
481 | */␊ |
482 | void prepend(int key,const T *d)␊ |
483 | {␊ |
484 | m_list->prepend(d);␊ |
485 | m_dict->insert(key,d);␊ |
486 | #if AUTORESIZE␊ |
487 | if (m_dict->size()>SDict_primes[m_sizeIndex])␊ |
488 | {␊ |
489 | m_dict->resize(SDict_primes[++m_sizeIndex]);␊ |
490 | }␊ |
491 | #endif␊ |
492 | }␊ |
493 | ␊ |
494 | /*! Remove an item from the dictionary */␊ |
495 | bool remove(int key)␊ |
496 | {␊ |
497 | T *item = m_dict->take(key);␊ |
498 | return item ? m_list->remove(item) : FALSE;␊ |
499 | }␊ |
500 | ␊ |
501 | /*! Sorts the members of the dictionary. First appending a number␊ |
502 | * of members and then sorting them is faster (O(NlogN) than using ␊ |
503 | * inSort() for each member (O(N^2)).␊ |
504 | */␊ |
505 | void sort()␊ |
506 | {␊ |
507 | m_list->sort();␊ |
508 | }␊ |
509 | ␊ |
510 | /*! Inserts a compound into the dictionary in a sorted way.␊ |
511 | * \param key The unique key to use to quicky find the item later on.␊ |
512 | * \param d The compound to add.␊ |
513 | * \sa find()␊ |
514 | */␊ |
515 | void inSort(int key,const T *d)␊ |
516 | {␊ |
517 | m_list->inSort(d);␊ |
518 | m_dict->insert(key,d);␊ |
519 | #if AUTORESIZE␊ |
520 | if (m_dict->size()>SDict_primes[m_sizeIndex])␊ |
521 | {␊ |
522 | m_dict->resize(SDict_primes[++m_sizeIndex]);␊ |
523 | }␊ |
524 | #endif␊ |
525 | }␊ |
526 | ␊ |
527 | /*! Indicates whether or not the dictionary owns its elements */␊ |
528 | void setAutoDelete(bool val)␊ |
529 | {␊ |
530 | m_list->setAutoDelete(val);␊ |
531 | }␊ |
532 | ␊ |
533 | /*! Looks up a compound given its key. ␊ |
534 | * \param key The key to identify this element.␊ |
535 | * \return The requested compound or zero if it cannot be found.␊ |
536 | * \sa append() ␊ |
537 | */␊ |
538 | T *find(int key)␊ |
539 | {␊ |
540 | return m_dict->find(key);␊ |
541 | }␊ |
542 | ␊ |
543 | /*! Equavalent to find(). */␊ |
544 | T *operator[](int key) const␊ |
545 | {␊ |
546 | return m_dict->find(key);␊ |
547 | }␊ |
548 | ␊ |
549 | /*! Returns the item at position \a i in the sorted dictionary */␊ |
550 | T *at(uint i)␊ |
551 | {␊ |
552 | return m_list->at(i);␊ |
553 | }␊ |
554 | ␊ |
555 | /*! Function that is used to compare two items when sorting.␊ |
556 | * Overload this to properly sort items.␊ |
557 | * \sa inSort()␊ |
558 | */␊ |
559 | virtual int compareItems(GCI item1,GCI item2)␊ |
560 | {␊ |
561 | return item1!=item2;␊ |
562 | }␊ |
563 | ␊ |
564 | /*! Clears the dictionary. Will delete items if setAutoDelete() was␊ |
565 | * set to \c TRUE.␊ |
566 | * \sa setAutoDelete␊ |
567 | */␊ |
568 | void clear()␊ |
569 | {␊ |
570 | m_list->clear();␊ |
571 | m_dict->clear();␊ |
572 | }␊ |
573 | ␊ |
574 | /*! Returns the number of items stored in the dictionary␊ |
575 | */␊ |
576 | int count()␊ |
577 | {␊ |
578 | return m_list->count();␊ |
579 | }␊ |
580 | ␊ |
581 | class Iterator; // first forward declare␊ |
582 | friend class Iterator; // then make it a friend␊ |
583 | /*! Simple iterator for SDict. It iterates in the order in which the␊ |
584 | * elements are stored.␊ |
585 | */␊ |
586 | class Iterator␊ |
587 | {␊ |
588 | public:␊ |
589 | /*! Create an iterator given the dictionary. */␊ |
590 | Iterator(const SIntDict<T> &dict)␊ |
591 | {␊ |
592 | m_li = new QListIterator<T>(*dict.m_list);␊ |
593 | }␊ |
594 | ␊ |
595 | /*! Destroys the dictionary */␊ |
596 | virtual ~Iterator()␊ |
597 | {␊ |
598 | delete m_li;␊ |
599 | }␊ |
600 | ␊ |
601 | /*! Set the iterator to the first element in the list. ␊ |
602 | * \return The first compound, or zero if the list was empty. ␊ |
603 | */␊ |
604 | T *toFirst() const␊ |
605 | {␊ |
606 | return m_li->toFirst();␊ |
607 | }␊ |
608 | ␊ |
609 | /*! Set the iterator to the last element in the list. ␊ |
610 | * \return The first compound, or zero if the list was empty. ␊ |
611 | */␊ |
612 | T *toLast() const␊ |
613 | {␊ |
614 | return m_li->toLast();␊ |
615 | }␊ |
616 | ␊ |
617 | /*! Returns the current compound */␊ |
618 | T *current() const␊ |
619 | {␊ |
620 | return m_li->current();␊ |
621 | }␊ |
622 | ␊ |
623 | /*! Moves the iterator to the next element.␊ |
624 | * \return the new "current" element, or zero if the iterator was␊ |
625 | * already pointing at the last element.␊ |
626 | */␊ |
627 | T *operator++()␊ |
628 | {␊ |
629 | return m_li->operator++();␊ |
630 | }␊ |
631 | ␊ |
632 | /*! Moves the iterator to the previous element.␊ |
633 | * \return the new "current" element, or zero if the iterator was␊ |
634 | * already pointing at the first element.␊ |
635 | */␊ |
636 | T *operator--()␊ |
637 | {␊ |
638 | return m_li->operator--();␊ |
639 | }␊ |
640 | ␊ |
641 | private:␊ |
642 | QListIterator<T> *m_li;␊ |
643 | };␊ |
644 | ␊ |
645 | };␊ |
646 | ␊ |
647 | #endif␊ |
648 |