PolyBoRi
|
00001 // -*- c++ -*- 00002 //***************************************************************************** 00099 //***************************************************************************** 00100 00101 // get standard header 00102 #include <stack> 00103 #include <iterator> 00104 #include <utility> // for std::pair 00105 00106 // include basic definitions 00107 #include "pbori_defs.h" 00108 00109 // include polybori functionals 00110 #include "pbori_func.h" 00111 00112 // include polybori properties 00113 #include "pbori_traits.h" 00114 00115 #include "pbori_routines.h" 00116 00117 // include boost's indirect iterator facilities 00118 #include <boost/iterator/indirect_iterator.hpp> 00119 00120 #include "BooleEnv.h" 00121 #include "CDegreeCache.h" 00122 #include "CBidirectTermIter.h" 00123 00124 00125 00126 #ifndef CTermStack_h_ 00127 #define CTermStack_h_ 00128 00129 BEGIN_NAMESPACE_PBORI 00130 00132 template<class NavigatorType> 00133 struct cached_deg { 00134 typedef CDegreeCache<> cache_type; 00135 typedef typename cache_type::manager_type manager_type; 00136 cached_deg(const manager_type & mgr): m_deg_cache(mgr) {} 00137 00138 typename NavigatorType::size_type 00139 operator()(NavigatorType navi) const { 00140 return dd_cached_degree(m_deg_cache, navi); 00141 } 00142 cache_type m_deg_cache; 00143 }; 00144 00146 00147 template <class NavigatorType> 00148 class cached_block_deg { 00149 public: 00150 typedef typename NavigatorType::idx_type idx_type; 00151 00152 typedef cached_block_deg<NavigatorType> self; 00153 00155 typedef std::vector<idx_type> block_idx_type; 00156 00158 typedef typename block_idx_type::const_iterator block_iterator; 00159 typedef CBlockDegreeCache<CCacheTypes::block_degree, CTypes::dd_type> 00160 cache_type; 00161 typedef typename cache_type::manager_type manager_type; 00162 00163 cached_block_deg(const manager_type& mgr): 00164 // m_indices(BoolePolyRing::blockRingBegin()), 00165 m_current_block(BooleEnv::blockBegin()), 00166 m_deg_cache(mgr) { } 00167 00168 typename NavigatorType::size_type 00169 operator()(NavigatorType navi) const { 00170 return dd_cached_block_degree(m_deg_cache, navi, max()); 00171 } 00172 00173 idx_type min() const { 00174 assert(*m_current_block != 0); // assuming first element to be zero 00175 return *(m_current_block - 1); 00176 } 00177 00178 idx_type max() const { 00179 return *m_current_block; 00180 } 00181 self& operator++(){ 00182 assert(max() != CTypes::max_idx); 00183 ++m_current_block; 00184 return *this; 00185 } 00186 00187 self& operator--(){ 00188 assert(min() != 0); 00189 --m_current_block; 00190 return *this; 00191 } 00192 00193 private: 00194 // block_iterator m_indices; 00195 block_iterator m_current_block; 00196 00197 cache_type m_deg_cache; 00198 }; 00199 00200 00201 00202 00210 template <class NavigatorType, class BaseType = internal_tag> 00211 class CTermStackBase: 00212 public BaseType { 00213 00214 public: 00215 00216 template <class, class> friend class CTermStackBase; 00217 00218 typedef CTermStackBase<NavigatorType, BaseType> self; 00219 00221 typedef NavigatorType navigator; 00223 typedef typename navigator::idx_type idx_type; 00224 00226 typedef typename navigator::size_type size_type; 00227 typedef typename navigator::bool_type bool_type; 00228 00229 00231 typedef std::deque<navigator> stack_type; 00232 00233 typedef typename stack_type::reference reference; 00234 typedef typename stack_type::const_reference const_reference; 00235 00236 typedef boost::indirect_iterator<typename stack_type::const_iterator, 00237 typename navigator::value_type, 00238 boost::use_default, 00239 typename navigator::reference> 00240 const_iterator; 00241 00242 typedef typename stack_type::const_iterator stack_iterator; 00243 00244 typedef typename stack_type::const_reverse_iterator stack_reverse_iterator; 00245 00246 typedef boost::indirect_iterator<typename stack_type::const_reverse_iterator, 00247 typename navigator::value_type, 00248 boost::use_default, 00249 typename navigator::reference> 00250 const_reverse_iterator; 00251 00252 private: 00253 void pop() { m_stack.pop_back(); } 00254 00255 protected: 00256 void push(navigator __x) { m_stack.push_back(__x); } 00257 00258 void clear() { m_stack.clear(); } 00259 00260 // reference top() { return m_stack.back(); } 00261 00262 public: 00263 const_reference top() const { return m_stack.back(); } 00264 idx_type index() const { return *top(); } 00265 bool_type empty() const { return m_stack.empty(); } 00266 size_type size() const { return m_stack.size(); } 00267 00268 const_iterator begin() const { return stackBegin(); } 00269 const_iterator end() const { return stackEnd(); } 00270 00271 const_reverse_iterator rbegin() const { return stackRBegin(); } 00272 00273 const_reverse_iterator rend() const { return stackREnd(); } 00274 00276 navigator navigation() const { 00277 assert(m_stack.begin() != m_stack.end()); 00278 return *m_stack.begin(); 00279 } 00280 00282 typedef typename stack_type::value_type top_type; 00283 00285 CTermStackBase(): BaseType(), m_stack() { } 00286 00288 CTermStackBase(navigator navi): BaseType(), m_stack() { 00289 push(navi); 00290 } 00291 00293 00294 00296 bool_type equal(const self& rhs) const { 00297 00298 if(empty() || rhs.empty()) 00299 return (empty() && rhs.empty()); 00300 else 00301 return (m_stack == rhs.m_stack); 00302 } 00303 00304 void incrementThen() { 00305 assert(!top().isConstant()); 00306 00307 push(top()); 00308 m_stack.back().incrementThen(); 00309 } 00310 void incrementElse() { 00311 assert(!isConstant()); 00312 m_stack.back().incrementElse(); 00313 } 00314 00315 void decrementNode() { 00316 assert(!empty()); 00317 pop(); 00318 } 00319 00320 bool_type isConstant() const { 00321 assert(!empty()); 00322 return top().isConstant(); 00323 } 00324 00325 bool_type isTerminated() const { 00326 assert(!empty()); 00327 return top().isTerminated(); 00328 } 00329 00330 bool_type isInvalid() const { 00331 assert(!empty()); 00332 return top().isEmpty(); 00333 } 00334 00335 void followThen() { 00336 assert(!empty()); 00337 while(!isConstant()) 00338 incrementThen(); 00339 } 00340 00341 void markOne() { 00342 assert(empty()); 00343 push(navigator()); 00344 } 00345 00346 bool_type markedOne() const { 00347 if (empty()) 00348 return false; 00349 else 00350 return !m_stack.front().isValid(); 00351 } 00352 00353 void clearOne() { 00354 pop(); 00355 assert(empty()); 00356 } 00357 00358 size_type deg() const { 00359 return (markedOne()? 0: size()); 00360 } 00361 00362 void invalidate() { 00363 push(BooleEnv::zero().navigation()); 00364 } 00365 00366 void restart(navigator navi) { 00367 assert(empty()); 00368 push(navi); 00369 } 00370 00371 bool isOne() const { return markedOne(); } 00372 bool isZero() const { return empty(); } 00373 00374 bool atBegin() const { return empty(); } 00375 00376 bool atEnd() const { return atEnd(top()); } 00377 bool atEnd(navigator navi) const { return navi.isConstant(); } 00378 00379 bool validEnd() const { return validEnd(top()); } 00380 bool validEnd(navigator navi) const { 00381 while(!navi.isConstant()) { 00382 navi.incrementElse(); 00383 } 00384 return navi.terminalValue(); 00385 } 00386 00387 void print() const{ 00388 std::cout <<"("; 00389 std::copy(begin(), end(), std::ostream_iterator<int>(cout, ", ")); 00390 std::cout <<")"; 00391 } 00392 00393 stack_iterator stackBegin() const { 00394 if (markedOne()) 00395 return m_stack.end(); 00396 else 00397 return m_stack.begin(); 00398 } 00399 00400 stack_iterator stackEnd() const { 00401 return m_stack.end(); 00402 } 00403 00404 stack_reverse_iterator stackRBegin() const { 00405 if (markedOne()) 00406 return m_stack.rend(); 00407 else 00408 return m_stack.rbegin(); 00409 } 00410 00411 stack_reverse_iterator stackREnd() const { 00412 return m_stack.rend(); 00413 } 00414 protected: 00415 00416 template <class TermStack> 00417 void append(const TermStack& rhs) { 00418 assert(empty() || rhs.empty() || ((*rhs.begin()) > (*top())) ); 00419 m_stack.insert(m_stack.end(), rhs.m_stack.begin(), rhs.m_stack.end()); 00420 } 00421 00422 00423 private: 00424 stack_type m_stack; 00425 }; 00426 00427 00428 00429 00430 template <class NavigatorType, class Category, class BaseType = internal_tag> 00431 class CTermStack: 00432 public CTermStackBase<NavigatorType, BaseType> { 00433 00434 public: 00435 typedef CTermStackBase<NavigatorType, BaseType> base; 00436 typedef CTermStack<NavigatorType, Category, BaseType> self; 00437 00439 typedef CTermStack<NavigatorType, Category, internal_tag> purestack_type; 00440 typedef Category iterator_category; 00441 00442 typedef typename base::navigator navigator; 00443 typedef typename on_same_type<Category, std::forward_iterator_tag, 00444 project_ith<0>, 00445 handle_else<NavigatorType> >::type 00446 else_handler; 00447 00448 else_handler handleElse; 00449 00450 using base::incrementThen; 00451 using base::followThen; 00452 00454 CTermStack(): base() { } 00455 00457 CTermStack(navigator navi): base(navi) { } 00458 00461 template <class Dummy> 00462 CTermStack(navigator navi, const Dummy&): base(navi) { } 00463 00464 void init() { 00465 followThen(); 00466 terminate(); 00467 } 00468 00469 void initLast() { 00470 followElse(); 00471 terminate(); 00472 } 00473 00474 void incrementElse() { 00475 assert(!base::empty()); 00476 handleElse(base::top()); 00477 base::incrementElse(); 00478 } 00479 00480 void next() { 00481 00482 bool invalid = true; 00483 while (!base::empty() && invalid) { 00484 incrementElse(); 00485 if (invalid = base::isInvalid()) 00486 base::decrementNode(); 00487 } 00488 } 00489 00490 void previous() { 00491 previous(Category()); 00492 } 00493 00494 00495 void increment() { 00496 assert(!base::empty()); 00497 if (base::markedOne()) { 00498 base::clearOne(); 00499 return; 00500 } 00501 00502 next(); 00503 if (!base::empty()) { 00504 followThen(); 00505 terminate(); 00506 } 00507 00508 } 00509 00510 void decrement() { 00511 00512 if (base::markedOne()) { 00513 base::clearOne(); 00514 } 00515 00516 previous(); 00517 followElse(); 00518 base::decrementNode(); 00519 00520 } 00521 00522 void terminate() { 00523 assert(!base::empty()); 00524 00525 bool isZero = base::isInvalid(); 00526 base::decrementNode(); 00527 if (base::empty() && !isZero) 00528 base::markOne(); 00529 } 00530 00531 00532 void followElse() { 00533 while( !base::isConstant() ) // if still in interior of a path 00534 incrementValidElse(); 00535 } 00536 00537 void incrementValidElse() { 00538 assert(!base::empty() && !base::isConstant()); 00539 if(!base::top().elseBranch().isEmpty()) 00540 incrementElse(); // go in direction of last term, if possible 00541 else 00542 incrementThen(); 00543 } 00544 protected: 00545 template <class TermStack> 00546 void append(const TermStack& rhs) { 00547 base::append(rhs); 00548 append(rhs, Category()); 00549 } 00550 00551 private: 00552 void previous(std::forward_iterator_tag); 00553 void previous(std::bidirectional_iterator_tag); 00554 00555 template <class TermStack> 00556 void append(const TermStack&, std::forward_iterator_tag){} 00557 00558 template <class TermStack> 00559 void append(const TermStack& rhs, std::bidirectional_iterator_tag){ 00560 handleElse.append(rhs.handleElse); 00561 } 00562 }; 00563 00564 00565 template <class NavigatorType, class Category, class BaseType> 00566 inline void CTermStack<NavigatorType, Category, BaseType>::previous( 00567 std::forward_iterator_tag) { } 00568 00569 template <class NavigatorType, class Category, class BaseType> 00570 inline void CTermStack<NavigatorType, Category, BaseType>::previous( 00571 std::bidirectional_iterator_tag) { 00572 00573 if(handleElse.empty()) { 00574 base::clear(); 00575 return; 00576 } 00577 00578 navigator navi = handleElse.top(); 00579 00580 assert(base::top().isValid()); 00581 00582 while(!base::empty() && (base::index() >= *navi) ) { 00583 base::decrementNode(); 00584 } 00585 00586 handleElse.pop(); 00587 base::push(navi); 00588 incrementThen(); 00589 } 00590 00591 00592 template <class NavigatorType, class BlockProperty, class Category, class 00593 BaseType = internal_tag> 00594 class CDegStackCore; 00595 00597 template <class NavigatorType, class Category, class BaseType> 00598 class CDegStackCore<NavigatorType, invalid_tag, Category, BaseType>: 00599 public CTermStack<NavigatorType, Category, BaseType> { 00600 00601 public: 00602 typedef CTermStack<NavigatorType, Category, BaseType> base; 00603 typedef NavigatorType navigator; 00604 typedef typename cached_deg<navigator>::manager_type manager_type; 00605 00606 CDegStackCore(): base(), getDeg(typename manager_type::mgrcore_ptr()) {} 00607 00608 CDegStackCore(navigator navi, const manager_type& mgr): 00609 base(navi), getDeg(mgr) {} 00610 00611 00612 void gotoEnd() { 00613 assert(!base::empty()); 00614 while(!base::isConstant()) { 00615 base::incrementElse(); 00616 } 00617 } 00618 00619 cached_deg<navigator> getDeg; 00620 }; 00621 00623 template <class NavigatorType, class Category, class BaseType> 00624 class CDegStackCore<NavigatorType, valid_tag, Category, BaseType> : 00625 public CTermStack<NavigatorType, Category, BaseType> { 00626 00627 public: 00628 typedef CTermStack<NavigatorType, Category, BaseType> base; 00629 typedef NavigatorType navigator; 00630 typedef typename base::idx_type idx_type; 00631 typedef typename base::size_type size_type; 00632 typedef typename cached_block_deg<navigator>::manager_type manager_type; 00633 00634 CDegStackCore(): base(), block(typename manager_type::mgrcore_ptr()) {} 00635 CDegStackCore(navigator navi, const manager_type& mgr): 00636 base(navi), block(mgr) {} 00637 00638 size_type getDeg(navigator navi) const { return block(navi); } 00639 00640 bool atBegin() const { 00641 return base::empty() || (base::index() < block.min()); 00642 } 00643 00644 bool atEnd() const { return atEnd(base::top()); } 00645 bool atEnd(navigator navi) const { 00646 return navi.isConstant() || (*navi >= block.max()); 00647 } 00648 00649 bool validEnd() const{ return validEnd(base::top()); } 00650 bool validEnd(navigator navi) const { 00651 00652 while(!atEnd(navi)) 00653 navi.incrementElse(); 00654 00655 return (navi.isConstant()? navi.terminalValue(): *navi >= block.max()); 00656 } 00657 00658 void next() { 00659 00660 bool invalid = true; 00661 while (!atBegin() && invalid) { 00662 assert(!base::isConstant()); 00663 base::incrementElse(); 00664 if (invalid = base::isInvalid()) 00665 base::decrementNode(); 00666 } 00667 } 00668 void previous() { 00669 00670 if( base::handleElse.empty() || (*base::handleElse.top() < block.min()) ) { 00671 while(!atBegin()) 00672 base::decrementNode(); 00673 return; 00674 } 00675 navigator navi = base::handleElse.top(); 00676 assert(base::top().isValid()); 00677 00678 while(!atBegin() && (base::index() >= *navi) ) { 00679 base::decrementNode(); 00680 } 00681 00682 if (base::empty() || (base::index() < *navi)) { 00683 base::handleElse.pop(); 00684 base::push(navi); 00685 } 00686 base::incrementThen(); 00687 } 00688 00689 void gotoEnd() { 00690 assert(!base::empty()); 00691 while( (!base::isConstant()) && (base::index() < block.max()) ) { 00692 base::incrementElse(); 00693 } 00694 } 00695 00696 protected: 00697 cached_block_deg<navigator> block; 00698 }; 00699 00700 template <class NavigatorType, class BlockProperty, class DescendingProperty, 00701 class BaseType = internal_tag> 00702 class CDegStackBase; 00703 00704 template <class NavigatorType, class BlockProperty, class BaseType> 00705 class CDegStackBase<NavigatorType, valid_tag, BlockProperty, BaseType>: 00706 public CDegStackCore<NavigatorType, BlockProperty, 00707 std::forward_iterator_tag, BaseType> { 00708 00709 public: 00710 typedef CDegStackCore<NavigatorType, BlockProperty, 00711 std::forward_iterator_tag, BaseType> base; 00712 00713 typedef typename base::size_type size_type; 00714 typedef std::greater<size_type> size_comparer; 00715 typedef typename base::manager_type manager_type; 00716 00717 CDegStackBase(): base() {} 00718 CDegStackBase(NavigatorType navi, const manager_type& mgr): base(navi, mgr) {} 00719 00720 integral_constant<bool, false> takeLast; 00721 00722 void proximate() { base::next(); } 00723 00724 void incrementBranch() { base::incrementThen(); } 00725 00726 bool maxOnThen(size_type deg) const { 00727 return (base::getDeg(base::top().thenBranch()) + 1 == deg); 00728 } 00729 00730 }; 00731 00732 00733 template <class NavigatorType, class BlockProperty, class BaseType> 00734 class CDegStackBase<NavigatorType, invalid_tag, BlockProperty, BaseType>: 00735 public CDegStackCore<NavigatorType, BlockProperty, 00736 std::bidirectional_iterator_tag, BaseType> { 00737 00738 public: 00739 typedef CDegStackCore<NavigatorType, BlockProperty, 00740 std::bidirectional_iterator_tag, BaseType> base; 00741 typedef typename base::size_type size_type; 00742 typedef std::greater_equal<size_type> size_comparer; 00743 typedef typename base::manager_type manager_type; 00744 00745 CDegStackBase(): base() {} 00746 CDegStackBase(NavigatorType navi, const manager_type& mgr): base(navi, mgr) {} 00747 00748 integral_constant<bool, true> takeLast; 00749 00750 void proximate() { base::previous(); } 00751 00752 void incrementBranch() { base::incrementValidElse(); } 00753 00754 bool maxOnThen(size_type deg) const { 00755 return !(base::getDeg(base::top().elseBranch()) == deg); 00756 } 00757 }; 00758 00759 00760 template <class NavigatorType, class DescendingProperty, 00761 class BlockProperty = invalid_tag, class BaseType = internal_tag> 00762 class CDegTermStack: 00763 public CDegStackBase<NavigatorType, DescendingProperty, BlockProperty, BaseType> { 00764 00765 public: 00766 typedef CDegStackBase<NavigatorType, DescendingProperty, BlockProperty, BaseType> base; 00767 typedef CDegTermStack<NavigatorType, DescendingProperty, BlockProperty, BaseType> self; 00768 00769 typedef typename base::navigator navigator; 00770 typedef typename navigator::size_type size_type; 00771 typedef typename base::manager_type manager_type; 00772 00773 CDegTermStack(): base(), m_start() {} 00774 CDegTermStack(navigator navi, const manager_type& mgr): 00775 base(navi, mgr), m_start(navi) {} 00776 00777 void init() { 00778 followDeg(); 00779 base::terminate(); 00780 } 00781 void followDeg() { 00782 assert(!base::empty()); 00783 00784 size_type deg = base::getDeg(base::top()); 00785 00786 while (deg > 0) { 00787 00788 if ( base::maxOnThen(deg) ) { 00789 --deg; 00790 base::incrementThen(); 00791 } 00792 else 00793 base::incrementElse(); 00794 00795 } 00796 } 00797 00798 void increment() { 00799 assert(!base::empty()); 00800 if (base::markedOne()) { 00801 base::clearOne(); 00802 return; 00803 } 00804 00805 00806 size_type upperbound = base::size(); 00807 degTerm(); 00808 00809 if(base::empty()) { 00810 restart(); 00811 findTerm(upperbound); 00812 } 00813 if(!base::empty()) 00814 base::terminate(); 00815 } 00816 00817 00818 void degTerm() { 00819 size_type size = base::size() + 1; 00820 00821 assert(!base::isConstant()); 00822 bool doloop; 00823 do { 00824 assert(!base::empty()); 00825 base::proximate(); 00826 00827 if (base::atBegin()) 00828 return; 00829 00830 while (!base::atEnd() && (base::size() < size) ) { 00831 base::incrementBranch(); 00832 } 00833 base::gotoEnd(); 00834 00835 if (doloop = (base::isInvalid() || (base::size() != size)) ) 00836 base::decrementNode(); 00837 00838 } while (!base::empty() && doloop); 00839 00840 } 00841 00842 00843 void decrement() {} 00844 00845 void findTerm(size_type upperbound) { 00846 assert(!base::empty()); 00847 00848 typename base::purestack_type max_elt, current(base::top()); 00849 base::decrementNode(); 00850 00851 typename base::size_comparer comp; 00852 00853 while (!current.empty() && 00854 (base::takeLast() || (max_elt.size() != upperbound)) ) { 00855 00856 while (!base::atEnd(current.top()) && (current.size() < upperbound) ) 00857 current.incrementThen(); 00858 00859 if (base::validEnd(current.top())) { 00860 if (comp(current.size(), max_elt.size())) 00861 max_elt = current; 00862 current.decrementNode(); 00863 } 00864 current.next(); 00865 } 00866 base::append(max_elt); 00867 00868 if(max_elt.empty()) 00869 base::invalidate(); 00870 } 00871 00872 void restart() { base::restart(m_start); } 00873 00874 private: 00875 navigator m_start; 00876 }; 00877 00878 00879 00881 template <class NavigatorType, class DescendingProperty, class BaseType = internal_tag> 00882 class CBlockTermStack: 00883 public CDegTermStack<NavigatorType, DescendingProperty, valid_tag, BaseType> { 00884 00885 public: 00886 typedef CDegTermStack<NavigatorType, DescendingProperty, valid_tag, BaseType> base; 00887 typedef CBlockTermStack<NavigatorType, DescendingProperty, BaseType> self; 00888 00889 typedef typename base::navigator navigator; 00890 typedef typename navigator::size_type size_type; 00891 typedef typename navigator::idx_type idx_type; 00892 typedef typename base::manager_type manager_type; 00893 00895 CBlockTermStack(navigator navi, const manager_type& mgr): 00896 base(navi, mgr) { } 00897 00899 CBlockTermStack(): base() {} 00900 00901 00902 void init() { 00903 assert(!base::empty()); 00904 followDeg(); 00905 base::terminate(); 00906 } 00907 00908 void increment() { 00909 assert(!base::empty()); 00910 00911 if (base::markedOne()) { 00912 base::clearOne(); 00913 return; 00914 } 00915 00916 navigator current = base::top(); 00917 while (*current < base::block.min()) 00918 --base::block; 00919 00920 incrementBlock(); 00921 while ( (base::size() > 1 ) && base::isInvalid() ) { 00922 --base::block; 00923 base::decrementNode(); 00924 incrementBlock(); 00925 } 00926 00927 followDeg(); 00928 00929 assert(!base::empty()); 00930 base::terminate(); 00931 } 00932 00933 void followBlockDeg() { base::followDeg(); } 00934 00935 void followDeg() { 00936 assert(base::top().isValid()); 00937 00938 if (!base::isConstant() ) 00939 followBlockDeg(); 00940 00941 while (!base::isConstant() ) { 00942 ++base::block; 00943 followBlockDeg(); 00944 } 00945 } 00946 00947 void incrementBlock() { 00948 00949 assert(!base::empty()); 00950 size_type size = base::size() + 1; 00951 00952 if (base::index() < base::block.min()) { 00953 base::invalidate(); 00954 return; 00955 } 00956 00957 base::degTerm(); 00958 00959 if (base::size() == size) return; 00960 00961 if (base::empty()) 00962 base::restart(); 00963 else { 00964 assert(base::index() < base::block.min()); 00965 base::incrementThen(); 00966 } 00967 00968 while (!base::isConstant() && (base::index() < base::block.min())) 00969 base::incrementElse(); 00970 00971 assert(size > base::size()); 00972 00973 base::findTerm(size - base::size()); 00974 base::gotoEnd(); 00975 } 00976 }; 00977 00978 END_NAMESPACE_PBORI 00979 00980 #endif