00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #ifndef _GLIBCXX_PARALLEL_LOSERTREE_H
00033 #define _GLIBCXX_PARALLEL_LOSERTREE_H 1
00034
00035 #include <functional>
00036
00037 #include <bits/stl_algobase.h>
00038 #include <parallel/features.h>
00039 #include <parallel/base.h>
00040
00041 namespace __gnu_parallel
00042 {
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056 template<typename T, typename Comparator>
00057 class LoserTreeBase
00058 {
00059 protected:
00060
00061 struct Loser
00062 {
00063
00064 bool sup;
00065
00066 int source;
00067
00068 T key;
00069 };
00070
00071 unsigned int ik, k, offset;
00072
00073
00074 unsigned int _M_log_k;
00075
00076
00077 Loser* losers;
00078
00079
00080 Comparator comp;
00081
00082
00083
00084
00085
00086
00087 bool first_insert;
00088
00089 public:
00090
00091
00092
00093
00094
00095
00096 LoserTreeBase(unsigned int _k, Comparator _comp)
00097 : comp(_comp)
00098 {
00099 ik = _k;
00100
00101
00102 _M_log_k = __log2(ik - 1) + 1;
00103
00104
00105 k = 1 << _M_log_k;
00106 offset = k;
00107
00108
00109 losers = static_cast<Loser*>(::operator new(2 * k * sizeof(Loser)));
00110 for (unsigned int i = ik - 1; i < k; ++i)
00111 losers[i + k].sup = true;
00112
00113 first_insert = true;
00114 }
00115
00116
00117
00118
00119 ~LoserTreeBase()
00120 { ::operator delete(losers); }
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130 inline void
00131 insert_start(const T& key, int source, bool sup)
00132 {
00133 unsigned int pos = k + source;
00134
00135 if(first_insert)
00136 {
00137
00138 for (unsigned int i = 0; i < (2 * k); ++i)
00139 new(&(losers[i].key)) T(key);
00140 first_insert = false;
00141 }
00142 else
00143 new(&(losers[pos].key)) T(key);
00144
00145 losers[pos].sup = sup;
00146 losers[pos].source = source;
00147 }
00148
00149
00150
00151
00152 int get_min_source()
00153 { return losers[0].source; }
00154 };
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164 template<bool stable, typename T, typename Comparator>
00165 class LoserTree : public LoserTreeBase<T, Comparator>
00166 {
00167 typedef LoserTreeBase<T, Comparator> Base;
00168 using Base::k;
00169 using Base::losers;
00170 using Base::first_insert;
00171
00172 public:
00173 LoserTree(unsigned int _k, Comparator _comp)
00174 : Base::LoserTreeBase(_k, _comp)
00175 {}
00176
00177 unsigned int
00178 init_winner(unsigned int root)
00179 {
00180 if (root >= k)
00181 {
00182 return root;
00183 }
00184 else
00185 {
00186 unsigned int left = init_winner (2 * root);
00187 unsigned int right = init_winner (2 * root + 1);
00188 if (losers[right].sup
00189 || (!losers[left].sup
00190 && !comp(losers[right].key, losers[left].key)))
00191 {
00192
00193 losers[root] = losers[right];
00194 return left;
00195 }
00196 else
00197 {
00198
00199 losers[root] = losers[left];
00200 return right;
00201 }
00202 }
00203 }
00204
00205 void init()
00206 { losers[0] = losers[init_winner(1)]; }
00207
00208
00209
00210
00211
00212
00213
00214
00215 void delete_min_insert(T key, bool sup)
00216 {
00217 #if _GLIBCXX_ASSERTIONS
00218
00219 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00220 #endif
00221
00222 int source = losers[0].source;
00223 for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2)
00224 {
00225
00226 if ((sup && (!losers[pos].sup || losers[pos].source < source))
00227 || (!sup && !losers[pos].sup
00228 && ((comp(losers[pos].key, key))
00229 || (!comp(key, losers[pos].key)
00230 && losers[pos].source < source))))
00231 {
00232
00233 std::swap(losers[pos].sup, sup);
00234 std::swap(losers[pos].source, source);
00235 std::swap(losers[pos].key, key);
00236 }
00237 }
00238
00239 losers[0].sup = sup;
00240 losers[0].source = source;
00241 losers[0].key = key;
00242 }
00243 };
00244
00245
00246
00247
00248
00249
00250 template<typename T, typename Comparator>
00251 class LoserTree<false, T, Comparator> :
00252 public LoserTreeBase<T, Comparator>
00253 {
00254 typedef LoserTreeBase<T, Comparator> Base;
00255 using Base::_M_log_k;
00256 using Base::k;
00257 using Base::losers;
00258 using Base::first_insert;
00259
00260 public:
00261 LoserTree(unsigned int _k, Comparator _comp)
00262 : Base::LoserTreeBase(_k, _comp)
00263 {}
00264
00265
00266
00267
00268
00269
00270
00271
00272 unsigned int
00273 init_winner (unsigned int root)
00274 {
00275 if (root >= k)
00276 {
00277 return root;
00278 }
00279 else
00280 {
00281 unsigned int left = init_winner (2 * root);
00282 unsigned int right = init_winner (2 * root + 1);
00283 if (losers[right].sup ||
00284 (!losers[left].sup
00285 && !comp(losers[right].key, losers[left].key)))
00286 {
00287
00288 losers[root] = losers[right];
00289 return left;
00290 }
00291 else
00292 {
00293
00294 losers[root] = losers[left];
00295 return right;
00296 }
00297 }
00298 }
00299
00300 inline void
00301 init()
00302 { losers[0] = losers[init_winner(1)]; }
00303
00304
00305
00306
00307
00308
00309
00310
00311 inline void
00312 delete_min_insert(T key, bool sup)
00313 {
00314 #if _GLIBCXX_ASSERTIONS
00315
00316 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00317 #endif
00318
00319 int source = losers[0].source;
00320 for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2)
00321 {
00322
00323 if (sup || (!losers[pos].sup && comp(losers[pos].key, key)))
00324 {
00325
00326 std::swap(losers[pos].sup, sup);
00327 std::swap(losers[pos].source, source);
00328 std::swap(losers[pos].key, key);
00329 }
00330 }
00331
00332 losers[0].sup = sup;
00333 losers[0].source = source;
00334 losers[0].key = key;
00335 }
00336 };
00337
00338
00339
00340
00341
00342 template<typename T, typename Comparator>
00343 class LoserTreePointerBase
00344 {
00345 protected:
00346
00347 struct Loser
00348 {
00349 bool sup;
00350 int source;
00351 const T* keyp;
00352 };
00353
00354 unsigned int ik, k, offset;
00355 Loser* losers;
00356 Comparator comp;
00357
00358 public:
00359 LoserTreePointerBase(unsigned int _k, Comparator _comp = std::less<T>())
00360 : comp(_comp)
00361 {
00362 ik = _k;
00363
00364
00365 k = 1 << (__log2(ik - 1) + 1);
00366 offset = k;
00367 losers = new Loser[k * 2];
00368 for (unsigned int i = ik - 1; i < k; i++)
00369 losers[i + k].sup = true;
00370 }
00371
00372 ~LoserTreePointerBase()
00373 { ::operator delete[](losers); }
00374
00375 int get_min_source()
00376 { return losers[0].source; }
00377
00378 void insert_start(const T& key, int source, bool sup)
00379 {
00380 unsigned int pos = k + source;
00381
00382 losers[pos].sup = sup;
00383 losers[pos].source = source;
00384 losers[pos].keyp = &key;
00385 }
00386 };
00387
00388
00389
00390
00391
00392
00393 template<bool stable, typename T, typename Comparator>
00394 class LoserTreePointer : public LoserTreePointerBase<T, Comparator>
00395 {
00396 typedef LoserTreePointerBase<T, Comparator> Base;
00397 using Base::k;
00398 using Base::losers;
00399
00400 public:
00401 LoserTreePointer(unsigned int _k, Comparator _comp = std::less<T>())
00402 : Base::LoserTreePointerBase(_k, _comp)
00403 {}
00404
00405 unsigned int
00406 init_winner(unsigned int root)
00407 {
00408 if (root >= k)
00409 {
00410 return root;
00411 }
00412 else
00413 {
00414 unsigned int left = init_winner (2 * root);
00415 unsigned int right = init_winner (2 * root + 1);
00416 if (losers[right].sup
00417 || (!losers[left].sup && !comp(*losers[right].keyp,
00418 *losers[left].keyp)))
00419 {
00420
00421 losers[root] = losers[right];
00422 return left;
00423 }
00424 else
00425 {
00426
00427 losers[root] = losers[left];
00428 return right;
00429 }
00430 }
00431 }
00432
00433 void init()
00434 { losers[0] = losers[init_winner(1)]; }
00435
00436 void delete_min_insert(const T& key, bool sup)
00437 {
00438 #if _GLIBCXX_ASSERTIONS
00439
00440 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00441 #endif
00442
00443 const T* keyp = &key;
00444 int source = losers[0].source;
00445 for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2)
00446 {
00447
00448 if ((sup && (!losers[pos].sup || losers[pos].source < source)) ||
00449 (!sup && !losers[pos].sup &&
00450 ((comp(*losers[pos].keyp, *keyp)) ||
00451 (!comp(*keyp, *losers[pos].keyp)
00452 && losers[pos].source < source))))
00453 {
00454
00455 std::swap(losers[pos].sup, sup);
00456 std::swap(losers[pos].source, source);
00457 std::swap(losers[pos].keyp, keyp);
00458 }
00459 }
00460
00461 losers[0].sup = sup;
00462 losers[0].source = source;
00463 losers[0].keyp = keyp;
00464 }
00465 };
00466
00467
00468
00469
00470
00471
00472 template<typename T, typename Comparator>
00473 class LoserTreePointer<false, T, Comparator> :
00474 public LoserTreePointerBase<T, Comparator>
00475 {
00476 typedef LoserTreePointerBase<T, Comparator> Base;
00477 using Base::k;
00478 using Base::losers;
00479
00480 public:
00481 LoserTreePointer(unsigned int _k, Comparator _comp = std::less<T>())
00482 : Base::LoserTreePointerBase(_k, _comp)
00483 {}
00484
00485 unsigned int
00486 init_winner(unsigned int root)
00487 {
00488 if (root >= k)
00489 {
00490 return root;
00491 }
00492 else
00493 {
00494 unsigned int left = init_winner (2 * root);
00495 unsigned int right = init_winner (2 * root + 1);
00496 if (losers[right].sup
00497 || (!losers[left].sup
00498 && !comp(*losers[right].keyp, *losers[left].keyp)))
00499 {
00500
00501 losers[root] = losers[right];
00502 return left;
00503 }
00504 else
00505 {
00506
00507 losers[root] = losers[left];
00508 return right;
00509 }
00510 }
00511 }
00512
00513 void init()
00514 { losers[0] = losers[init_winner(1)]; }
00515
00516 void delete_min_insert(const T& key, bool sup)
00517 {
00518 #if _GLIBCXX_ASSERTIONS
00519
00520 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00521 #endif
00522
00523 const T* keyp = &key;
00524 int source = losers[0].source;
00525 for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2)
00526 {
00527
00528 if (sup || (!losers[pos].sup && comp(*losers[pos].keyp, *keyp)))
00529 {
00530
00531 std::swap(losers[pos].sup, sup);
00532 std::swap(losers[pos].source, source);
00533 std::swap(losers[pos].keyp, keyp);
00534 }
00535 }
00536
00537 losers[0].sup = sup;
00538 losers[0].source = source;
00539 losers[0].keyp = keyp;
00540 }
00541 };
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553 template<typename T, typename Comparator>
00554 class LoserTreeUnguardedBase
00555 {
00556 protected:
00557 struct Loser
00558 {
00559 int source;
00560 T key;
00561 };
00562
00563 unsigned int ik, k, offset;
00564 Loser* losers;
00565 Comparator comp;
00566
00567 public:
00568 inline
00569 LoserTreeUnguardedBase(unsigned int _k, const T _sentinel,
00570 Comparator _comp = std::less<T>())
00571 : comp(_comp)
00572 {
00573 ik = _k;
00574
00575
00576 k = 1 << (__log2(ik - 1) + 1);
00577 offset = k;
00578
00579 losers = static_cast<Loser*>(::operator new(2 * k * sizeof(Loser)));
00580
00581 for (unsigned int i = k + ik - 1; i < (2 * k); ++i)
00582 {
00583 losers[i].key = _sentinel;
00584 losers[i].source = -1;
00585 }
00586 }
00587
00588 inline ~LoserTreeUnguardedBase()
00589 { ::operator delete(losers); }
00590
00591 inline int
00592 get_min_source()
00593 {
00594 #if _GLIBCXX_ASSERTIONS
00595
00596 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00597 #endif
00598 return losers[0].source;
00599 }
00600
00601 inline void
00602 insert_start(const T& key, int source, bool)
00603 {
00604 unsigned int pos = k + source;
00605
00606 new(&(losers[pos].key)) T(key);
00607 losers[pos].source = source;
00608 }
00609 };
00610
00611
00612
00613
00614
00615
00616 template<bool stable, typename T, typename Comparator>
00617 class LoserTreeUnguarded : public LoserTreeUnguardedBase<T, Comparator>
00618 {
00619 typedef LoserTreeUnguardedBase<T, Comparator> Base;
00620 using Base::k;
00621 using Base::losers;
00622
00623 public:
00624 LoserTreeUnguarded(unsigned int _k, const T _sentinel,
00625 Comparator _comp = std::less<T>())
00626 : Base::LoserTreeUnguardedBase(_k, _sentinel, _comp)
00627 {}
00628
00629 unsigned int
00630 init_winner(unsigned int root)
00631 {
00632 if (root >= k)
00633 {
00634 return root;
00635 }
00636 else
00637 {
00638 unsigned int left = init_winner (2 * root);
00639 unsigned int right = init_winner (2 * root + 1);
00640 if (!comp(losers[right].key, losers[left].key))
00641 {
00642
00643 losers[root] = losers[right];
00644 return left;
00645 }
00646 else
00647 {
00648
00649 losers[root] = losers[left];
00650 return right;
00651 }
00652 }
00653 }
00654
00655 inline void
00656 init()
00657 {
00658 losers[0] = losers[init_winner(1)];
00659
00660 #if _GLIBCXX_ASSERTIONS
00661
00662 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00663 #endif
00664 }
00665
00666
00667 inline void
00668 delete_min_insert(T key, bool)
00669 {
00670 #if _GLIBCXX_ASSERTIONS
00671
00672 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00673 #endif
00674
00675 int source = losers[0].source;
00676 for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2)
00677 {
00678
00679 if (comp(losers[pos].key, key)
00680 || (!comp(key, losers[pos].key) && losers[pos].source < source))
00681 {
00682
00683 std::swap(losers[pos].source, source);
00684 std::swap(losers[pos].key, key);
00685 }
00686 }
00687
00688 losers[0].source = source;
00689 losers[0].key = key;
00690 }
00691 };
00692
00693
00694
00695
00696
00697
00698 template<typename T, typename Comparator>
00699 class LoserTreeUnguarded<false, T, Comparator> :
00700 public LoserTreeUnguardedBase<T, Comparator>
00701 {
00702 typedef LoserTreeUnguardedBase<T, Comparator> Base;
00703 using Base::k;
00704 using Base::losers;
00705
00706 public:
00707 LoserTreeUnguarded(unsigned int _k, const T _sentinel,
00708 Comparator _comp = std::less<T>())
00709 : Base::LoserTreeUnguardedBase(_k, _sentinel, _comp)
00710 {}
00711
00712 unsigned int
00713 init_winner (unsigned int root)
00714 {
00715 if (root >= k)
00716 {
00717 return root;
00718 }
00719 else
00720 {
00721 unsigned int left = init_winner (2 * root);
00722 unsigned int right = init_winner (2 * root + 1);
00723
00724 #if _GLIBCXX_ASSERTIONS
00725
00726 if (losers[left].source == -1)
00727 _GLIBCXX_PARALLEL_ASSERT(losers[right].source == -1);
00728 #endif
00729
00730 if (!comp(losers[right].key, losers[left].key))
00731 {
00732
00733 losers[root] = losers[right];
00734 return left;
00735 }
00736 else
00737 {
00738
00739 losers[root] = losers[left];
00740 return right;
00741 }
00742 }
00743 }
00744
00745 inline void
00746 init()
00747 {
00748 losers[0] = losers[init_winner(1)];
00749
00750 #if _GLIBCXX_ASSERTIONS
00751
00752 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00753 #endif
00754 }
00755
00756
00757 inline void
00758 delete_min_insert(T key, bool)
00759 {
00760 #if _GLIBCXX_ASSERTIONS
00761
00762 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00763 #endif
00764
00765 int source = losers[0].source;
00766 for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2)
00767 {
00768
00769 if (comp(losers[pos].key, key))
00770 {
00771
00772 std::swap(losers[pos].source, source);
00773 std::swap(losers[pos].key, key);
00774 }
00775 }
00776
00777 losers[0].source = source;
00778 losers[0].key = key;
00779 }
00780 };
00781
00782
00783
00784
00785
00786
00787
00788 template<typename T, typename Comparator>
00789 class LoserTreePointerUnguardedBase
00790 {
00791 protected:
00792 struct Loser
00793 {
00794 int source;
00795 const T* keyp;
00796 };
00797
00798 unsigned int ik, k, offset;
00799 Loser* losers;
00800 Comparator comp;
00801
00802 public:
00803
00804 inline
00805 LoserTreePointerUnguardedBase(unsigned int _k, const T& _sentinel,
00806 Comparator _comp = std::less<T>())
00807 : comp(_comp)
00808 {
00809 ik = _k;
00810
00811
00812 k = 1 << (__log2(ik - 1) + 1);
00813 offset = k;
00814
00815 losers = new Loser[2 * k];
00816
00817 for (unsigned int i = k + ik - 1; i < (2 * k); ++i)
00818 {
00819 losers[i].keyp = &_sentinel;
00820 losers[i].source = -1;
00821 }
00822 }
00823
00824 inline ~LoserTreePointerUnguardedBase()
00825 { delete[] losers; }
00826
00827 inline int
00828 get_min_source()
00829 {
00830 #if _GLIBCXX_ASSERTIONS
00831
00832 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00833 #endif
00834 return losers[0].source;
00835 }
00836
00837 inline void
00838 insert_start(const T& key, int source, bool)
00839 {
00840 unsigned int pos = k + source;
00841
00842 losers[pos].keyp = &key;
00843 losers[pos].source = source;
00844 }
00845 };
00846
00847
00848
00849
00850
00851
00852 template<bool stable, typename T, typename Comparator>
00853 class LoserTreePointerUnguarded :
00854 public LoserTreePointerUnguardedBase<T, Comparator>
00855 {
00856 typedef LoserTreePointerUnguardedBase<T, Comparator> Base;
00857 using Base::k;
00858 using Base::losers;
00859
00860 public:
00861 LoserTreePointerUnguarded(unsigned int _k, const T& _sentinel,
00862 Comparator _comp = std::less<T>())
00863 : Base::LoserTreePointerUnguardedBase(_k, _sentinel, _comp)
00864 {}
00865
00866 unsigned int
00867 init_winner(unsigned int root)
00868 {
00869 if (root >= k)
00870 {
00871 return root;
00872 }
00873 else
00874 {
00875 unsigned int left = init_winner (2 * root);
00876 unsigned int right = init_winner (2 * root + 1);
00877 if (!comp(*losers[right].keyp, *losers[left].keyp))
00878 {
00879
00880 losers[root] = losers[right];
00881 return left;
00882 }
00883 else
00884 {
00885
00886 losers[root] = losers[left];
00887 return right;
00888 }
00889 }
00890 }
00891
00892 inline void
00893 init()
00894 {
00895 losers[0] = losers[init_winner(1)];
00896
00897 #if _GLIBCXX_ASSERTIONS
00898
00899 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00900 #endif
00901 }
00902
00903 inline void
00904 delete_min_insert(const T& key, bool sup)
00905 {
00906 #if _GLIBCXX_ASSERTIONS
00907
00908 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00909 #endif
00910
00911 const T* keyp = &key;
00912 int source = losers[0].source;
00913 for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2)
00914 {
00915
00916 if (comp(*losers[pos].keyp, *keyp)
00917 || (!comp(*keyp, *losers[pos].keyp) && losers[pos].source < source))
00918 {
00919
00920 std::swap(losers[pos].source, source);
00921 std::swap(losers[pos].keyp, keyp);
00922 }
00923 }
00924
00925 losers[0].source = source;
00926 losers[0].keyp = keyp;
00927 }
00928 };
00929
00930
00931
00932
00933
00934
00935 template<typename T, typename Comparator>
00936 class LoserTreePointerUnguarded<false, T, Comparator> :
00937 public LoserTreePointerUnguardedBase<T, Comparator>
00938 {
00939 typedef LoserTreePointerUnguardedBase<T, Comparator> Base;
00940 using Base::k;
00941 using Base::losers;
00942
00943 public:
00944 LoserTreePointerUnguarded(unsigned int _k, const T& _sentinel,
00945 Comparator _comp = std::less<T>())
00946 : Base::LoserTreePointerUnguardedBase(_k, _sentinel, _comp)
00947 {}
00948
00949 unsigned int
00950 init_winner(unsigned int root)
00951 {
00952 if (root >= k)
00953 {
00954 return root;
00955 }
00956 else
00957 {
00958 unsigned int left = init_winner (2 * root);
00959 unsigned int right = init_winner (2 * root + 1);
00960
00961 #if _GLIBCXX_ASSERTIONS
00962
00963 if (losers[left].source == -1)
00964 _GLIBCXX_PARALLEL_ASSERT(losers[right].source == -1);
00965 #endif
00966
00967 if (!comp(*losers[right].keyp, *losers[left].keyp))
00968 {
00969
00970 losers[root] = losers[right];
00971 return left;
00972 }
00973 else
00974 {
00975
00976 losers[root] = losers[left];
00977 return right;
00978 }
00979 }
00980 }
00981
00982 inline void
00983 init()
00984 {
00985 losers[0] = losers[init_winner(1)];
00986
00987 #if _GLIBCXX_ASSERTIONS
00988
00989 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00990 #endif
00991 }
00992
00993 inline void
00994 delete_min_insert(const T& key, bool sup)
00995 {
00996 #if _GLIBCXX_ASSERTIONS
00997
00998 _GLIBCXX_PARALLEL_ASSERT(losers[0].source != -1);
00999 #endif
01000
01001 const T* keyp = &key;
01002 int source = losers[0].source;
01003 for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2)
01004 {
01005
01006 if (comp(*(losers[pos].keyp), *keyp))
01007 {
01008
01009 std::swap(losers[pos].source, source);
01010 std::swap(losers[pos].keyp, keyp);
01011 }
01012 }
01013
01014 losers[0].source = source;
01015 losers[0].keyp = keyp;
01016 }
01017 };
01018
01019 }
01020
01021 #endif