selection_algorithms.hpp
Go to the documentation of this file.
00001 /* 00002 Copyright 2005-2007 Adobe Systems Incorporated 00003 Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt 00004 or a copy at http://stlab.adobe.com/licenses.html) 00005 */ 00006 /*************************************************************************************************/ 00007 00008 #ifndef ADOBE_ALGORITHM_SELECTION_HPP 00009 #define ADOBE_ALGORITHM_SELECTION_HPP 00010 00011 #include <adobe/config.hpp> 00012 00013 #include <algorithm> 00014 #include <functional> 00015 #include <vector> 00016 00017 #include <boost/range/begin.hpp> 00018 #include <boost/range/end.hpp> 00019 #include <boost/range/value_type.hpp> 00020 #include <boost/range/size_type.hpp> 00021 #include <boost/range/size.hpp> 00022 00023 #include <adobe/algorithm/other_of.hpp> 00024 #include <adobe/algorithm/rotate.hpp> 00025 #include <adobe/iterator/type_functions.hpp> 00026 00027 /*************************************************************************************************/ 00028 00029 namespace adobe { 00030 00031 /*************************************************************************************************/ 00047 /*************************************************************************************************/ 00054 template <typename S, // S models ForwardIterator, value_type(S) == I 00055 typename O, // O models OutputIterator 00056 typename P> // P models BinaryPredicate 00057 inline O selection_operation_remainder(S first, 00058 S last, 00059 O output, 00060 bool this_inside, 00061 bool other_inside, 00062 P pred) 00063 { 00064 bool prev_op_result(pred(this_inside, other_inside)); 00065 00066 while (first != last) 00067 { 00068 this_inside = !this_inside; 00069 00070 bool cur_op_result(pred(this_inside, other_inside)); 00071 00072 if (prev_op_result ^ cur_op_result) 00073 *output++ = *first; 00074 00075 prev_op_result = cur_op_result; 00076 00077 ++first; 00078 } 00079 00080 return output; 00081 } 00082 00083 /*************************************************************************************************/ 00089 template <typename C1, // C1 models ConvertibleToBool 00090 typename C2 = C1> // C2 models ConvertibleToBool 00091 struct logical_xor : std::binary_function<C1, C2, bool> 00092 { 00094 bool operator()(const C1& x, const C2& y) const 00095 { return static_cast<bool>(x) ^ static_cast<bool>(y); } 00096 }; 00097 00098 /*************************************************************************************************/ 00104 template <typename S1, // S1 models ForwardIterator, value_type(S1) == value_type(S2) 00105 typename S2, // S2 models ForwardIterator, value_type(S2) == value_type(S1) 00106 typename O, // O models OutputIterator 00107 typename P, // P models BinaryPredicate 00108 typename C> // C models StrictWeakOrdering 00109 O selection_operation(S1 s1_first, 00110 S1 s1_last, 00111 S2 s2_first, 00112 S2 s2_last, 00113 O output, 00114 bool s1_inside, 00115 bool s2_inside, 00116 P pred, 00117 C comp) 00118 { 00119 if (s1_first == s1_last) 00120 return selection_operation_remainder(s2_first, s2_last, output, s2_inside, s1_inside, pred); 00121 else if (s2_first == s2_last) 00122 return selection_operation_remainder(s1_first, s1_last, output, s1_inside, s2_inside, pred); 00123 00124 bool prev_op_result(pred(s1_inside, s2_inside)); 00125 00126 while (true) 00127 { 00128 typename std::iterator_traits<S1>::value_type next(comp(*s1_first, *s2_first) ? *s1_first : *s2_first); 00129 00130 if (*s1_first == next) 00131 { 00132 s1_inside = !s1_inside; 00133 00134 ++s1_first; 00135 } 00136 00137 if (*s2_first == next) 00138 { 00139 s2_inside = !s2_inside; 00140 00141 ++s2_first; 00142 } 00143 00144 bool cur_op_result(pred(s1_inside, s2_inside)); 00145 00146 if (prev_op_result ^ cur_op_result) 00147 *output++ = next; 00148 00149 prev_op_result = cur_op_result; 00150 00151 if (s1_first == s1_last) 00152 return selection_operation_remainder(s2_first, s2_last, output, s2_inside, s1_inside, pred); 00153 else if (s2_first == s2_last) 00154 return selection_operation_remainder(s1_first, s1_last, output, s1_inside, s2_inside, pred); 00155 } 00156 00157 return output; 00158 } 00159 00160 /*************************************************************************************************/ 00166 template <typename S1, // S1 models ForwardIterator, value_type(S1) == I 00167 typename S2, // S2 models ForwardIterator, value_type(S2) == I 00168 typename O, // O models OutputIterator 00169 typename C> // C models StrictWeakOrdering 00170 inline O selection_union(S1 s1_first, 00171 S1 s1_last, 00172 S2 s2_first, 00173 S2 s2_last, 00174 O output, 00175 C comp, 00176 bool s1_inside = false, 00177 bool s2_inside = false) 00178 { 00179 return selection_operation(s1_first, s1_last, 00180 s2_first, s2_last, 00181 output, 00182 s1_inside, 00183 s2_inside, 00184 std::logical_or<bool>(), 00185 comp); 00186 } 00187 00188 /*************************************************************************************************/ 00194 template <typename S1, // S1 models ForwardIterator, value_type(S1) == I 00195 typename S2, // S2 models ForwardIterator, value_type(S2) == I 00196 typename O, // O models OutputIterator 00197 typename C> // C models StrictWeakOrdering 00198 inline O selection_intersection(S1 s1_first, 00199 S1 s1_last, 00200 S2 s2_first, 00201 S2 s2_last, 00202 O output, 00203 C comp, 00204 bool s1_inside = false, 00205 bool s2_inside = false) 00206 { 00207 return selection_operation(s1_first, s1_last, 00208 s2_first, s2_last, 00209 output, 00210 s1_inside, 00211 s2_inside, 00212 std::logical_and<bool>(), 00213 comp); 00214 } 00215 00216 /*************************************************************************************************/ 00222 template <typename S1, // S1 models ForwardIterator, value_type(S1) == I 00223 typename S2, // S2 models ForwardIterator, value_type(S2) == I 00224 typename O, // O models OutputIterator 00225 typename C> // C models StrictWeakOrdering 00226 inline O selection_difference(S1 s1_first, 00227 S1 s1_last, 00228 S2 s2_first, 00229 S2 s2_last, 00230 O output, 00231 C comp, 00232 bool s1_inside = false, 00233 bool s2_inside = false) 00234 { 00235 return selection_intersection(s1_first, s1_last, 00236 s2_first, s2_last, 00237 output, 00238 comp, 00239 s1_inside, 00240 !s2_inside); 00241 } 00242 00243 /*************************************************************************************************/ 00249 template <typename S1, // S1 models ForwardIterator, value_type(S1) == I 00250 typename S2, // S2 models ForwardIterator, value_type(S2) == I 00251 typename O, // O models OutputIterator 00252 typename C> // C models StrictWeakOrdering 00253 inline O selection_symmetric_difference(S1 s1_first, 00254 S1 s1_last, 00255 S2 s2_first, 00256 S2 s2_last, 00257 O output, 00258 C comp, 00259 bool s1_inside = false, 00260 bool s2_inside = false) 00261 { 00262 return selection_operation(s1_first, s1_last, 00263 s2_first, s2_last, 00264 output, 00265 s1_inside, 00266 s2_inside, 00267 logical_xor<bool>(), 00268 comp); 00269 } 00270 00271 /*************************************************************************************************/ 00277 template <typename S1, // S1 models InputIterator, value_type(S1) == I 00278 typename S2, // S2 models InputIterator, value_type(S2) == I 00279 typename C> // C models StrictWeakOrdering 00280 inline bool selection_includes(S1 s1_first, 00281 S1 s1_last, 00282 S2 s2_first, 00283 S2 s2_last, 00284 C comp, 00285 bool s1_inside = false, 00286 bool s2_inside = false) 00287 { 00288 if (s1_inside == s2_inside) 00289 { 00290 typedef typename std::iterator_traits<S1>::value_type value_type; 00291 00292 std::vector<value_type> result; 00293 00294 selection_intersection(s1_first, s1_last, 00295 s2_first, s2_last, 00296 std::back_inserter(result), 00297 comp, 00298 s1_inside, s2_inside); 00299 00300 return std::equal(s2_first, s2_last, result.begin()); 00301 } 00302 else if (s1_inside) 00303 { 00304 return selection_includes(boost::next(s1_first), s1_last, 00305 s2_first, s2_last, 00306 comp, !s1_inside, s2_inside); 00307 } 00308 00309 // s2_inside == true 00310 return selection_includes(s1_first, s1_last, 00311 boost::next(s2_first), s2_last, 00312 comp, s1_inside, !s2_inside); 00313 } 00314 00315 /****************************************************************************************************/ 00321 template <typename Selection1, typename Selection2> 00322 Selection1 selection_intersection(const Selection1& x, const Selection2& y) 00323 { 00324 if (&x == &y) 00325 return x; 00326 00327 Selection1 result; 00328 00329 adobe::selection_intersection(x.begin(), x.end(), 00330 y.begin(), y.end(), 00331 std::back_inserter(result), 00332 std::less<typename boost::range_value<Selection1>::type>(), 00333 start_selected(x), 00334 start_selected(y)); 00335 00336 return result; 00337 } 00338 00339 /****************************************************************************************************/ 00345 template <typename Selection1, typename Selection2> 00346 Selection1 selection_union(const Selection1& x, const Selection2& y) 00347 { 00348 if (&x == &y) 00349 return x; 00350 00351 Selection1 result; 00352 00353 adobe::selection_union(x.begin(), x.end(), 00354 y.begin(), y.end(), 00355 std::back_inserter(result), 00356 std::less<typename boost::range_value<Selection1>::type>(), 00357 start_selected(x), 00358 start_selected(y)); 00359 00360 return result; 00361 } 00362 00363 /****************************************************************************************************/ 00369 template <typename Selection1, typename Selection2> 00370 Selection1 selection_difference(const Selection1& x, const Selection2& y) 00371 { 00372 if (&x == &y) 00373 return Selection1(); 00374 00375 Selection1 result; 00376 00377 adobe::selection_difference(x.begin(), x.end(), 00378 y.begin(), y.end(), 00379 std::back_inserter(result), 00380 std::less<typename boost::range_value<Selection1>::type>(), 00381 start_selected(x), 00382 start_selected(y)); 00383 00384 return result; 00385 } 00386 00387 /****************************************************************************************************/ 00393 template <typename Selection1, typename Selection2> 00394 Selection1 selection_symmetric_difference(const Selection1& x, const Selection2& y) 00395 { 00396 if (&x == &y) 00397 return Selection1(); 00398 00399 Selection1 result; 00400 00401 adobe::selection_symmetric_difference(x.begin(), x.end(), 00402 y.begin(), y.end(), 00403 std::back_inserter(result), 00404 std::less<typename boost::range_value<Selection1>::type>(), 00405 start_selected(x), 00406 start_selected(y)); 00407 00408 return result; 00409 } 00410 00411 /****************************************************************************************************/ 00417 template <typename Selection1, typename Selection2> 00418 bool selection_includes(const Selection1& x, const Selection2& y) 00419 { 00420 if (&x == &y) 00421 return true; 00422 00423 return adobe::selection_includes(x.begin(), x.end(), 00424 y.begin(), y.end(), 00425 std::less<typename boost::range_value<Selection1>::type>(), 00426 start_selected(x), 00427 start_selected(y)); 00428 } 00429 00430 /****************************************************************************************************/ 00436 template <typename Selection> 00437 inline void invert(Selection& x) 00438 { x.invert(); } 00439 00440 /****************************************************************************************************/ 00446 template <typename Selection> 00447 inline bool start_selected(const Selection& x) 00448 { return x.start_selected(); } 00449 00450 /****************************************************************************************************/ 00456 template <typename Selection> 00457 inline typename boost::range_size<Selection>::type size(const Selection& x) 00458 { return x.size(); } 00459 00460 /****************************************************************************************************/ 00466 template <typename Selection, typename ForwardRange> 00467 typename boost::range_size<Selection>::type size(const Selection& x, const ForwardRange& range) 00468 { 00469 typedef typename boost::range_const_iterator<Selection>::type selection_const_iterator; 00470 typedef typename boost::range_size<Selection>::type selection_size_type; 00471 typedef typename boost::range_const_iterator<ForwardRange>::type range_const_iterator; 00472 00473 if (x.empty()) 00474 return 0; 00475 00476 // this is the case when the selection has no elements, but it starts selected 00477 // (in other words, every item in the selection is toggled as selected) 00478 if (x.size() == 0) 00479 return boost::size(range); 00480 00481 selection_const_iterator s_iter(boost::begin(x)); 00482 selection_const_iterator s_last(boost::end(x)); 00483 00484 range_const_iterator prev(boost::begin(range)); 00485 range_const_iterator iter(boost::next(prev, *s_iter)); 00486 range_const_iterator last(boost::end(range)); 00487 00488 selection_size_type result(0); 00489 bool inside(start_selected(x)); 00490 00491 while (true) 00492 { 00493 if (inside) 00494 result += static_cast<selection_size_type>(std::distance(prev, iter)); 00495 00496 if (iter == last) 00497 break; 00498 00499 prev = iter; 00500 00501 iter = ++s_iter == s_last ? last : boost::next(boost::begin(range), *s_iter); 00502 00503 inside = !inside; 00504 } 00505 00506 return result; 00507 } 00508 00509 /****************************************************************************************************/ 00515 template <typename Selection> 00516 bool is_selected(const Selection& x, typename Selection::value_type index) 00517 { 00518 typename boost::range_const_iterator<Selection>::type found(std::upper_bound(boost::begin(x), boost::end(x), index)); 00519 typename boost::range_size<Selection>::type count(std::distance(boost::begin(x), found)); 00520 00521 return (count % 2 == 1) ^ start_selected(x); 00522 } 00523 00524 /****************************************************************************************************/ 00531 template <typename Selection, typename ForwardRange, typename OutputIterator> 00532 OutputIterator selection_copy(const Selection& x, const ForwardRange& range, OutputIterator output) 00533 { 00534 typedef typename boost::range_const_iterator<Selection>::type selection_const_iterator; 00535 typedef typename boost::range_const_iterator<ForwardRange>::type range_const_iterator; 00536 00537 if (boost::size(range) == 0) 00538 return output; 00539 00540 bool inside(start_selected(x)); 00541 00542 selection_const_iterator s_iter(boost::begin(x)); 00543 selection_const_iterator s_last(boost::end(x)); 00544 00545 range_const_iterator iter(boost::begin(range)); 00546 range_const_iterator last(boost::end(range)); 00547 00548 while (iter != last) 00549 { 00550 if (s_iter != s_last && iter == boost::next(boost::begin(range), *s_iter)) 00551 { 00552 ++s_iter; 00553 00554 inside = !inside; 00555 } 00556 00557 if (inside) 00558 *output++ = *iter; 00559 00560 ++iter; 00561 } 00562 00563 return output; 00564 } 00565 00566 /*************************************************************************************************/ 00572 template <typename Selection, 00573 typename ForwardRange, 00574 typename O1, // O1 models OutputIterator 00575 typename O2> // O2 models OutputIterator 00576 std::pair<O1, O2> selection_partition_copy(const Selection& selection, 00577 ForwardRange& range, 00578 O1 false_output, 00579 O2 true_output) 00580 { 00581 typedef typename boost::range_const_iterator<Selection>::type selection_const_iterator; 00582 typedef typename boost::range_iterator<ForwardRange>::type range_iterator; 00583 00584 if (boost::size(range) == 0) 00585 return std::make_pair(false_output, true_output); 00586 00587 selection_const_iterator selection_first(boost::begin(selection)); 00588 selection_const_iterator selection_last(boost::end(selection)); 00589 00590 range_iterator first(boost::begin(range)); 00591 range_iterator last(boost::end(range)); 00592 00593 bool inside(start_selected(selection)); 00594 00595 while (true) 00596 { 00597 range_iterator copy_last(selection_first == selection_last ? last : boost::next(boost::begin(range), *selection_first)); 00598 00599 // REVISIT (fbrereto) : It'd be nice to collapse the following into ? : 00600 // notation, but some compilers require that the 00601 // types returned by ? : be the same, which we cannot 00602 // guarantee here. 00603 if (inside) 00604 std::copy(first, copy_last, true_output); 00605 else 00606 std::copy(first, copy_last, false_output); 00607 00608 if (copy_last == last) 00609 break; 00610 00611 first = copy_last; 00612 ++selection_first; 00613 inside = !inside; 00614 } 00615 00616 return std::make_pair(false_output, true_output); 00617 } 00618 00619 /****************************************************************************************************/ 00625 template <typename Selection, typename ForwardRange, typename UnaryFunction> 00626 UnaryFunction selection_foreach(const Selection& x, const ForwardRange& range, UnaryFunction proc) 00627 { 00628 typedef typename boost::range_const_iterator<Selection>::type selection_const_iterator; 00629 typedef typename boost::range_const_iterator<ForwardRange>::type range_const_iterator; 00630 00631 bool inside(start_selected(x)); 00632 00633 selection_const_iterator s_iter(boost::begin(x)); 00634 selection_const_iterator s_last(boost::end(x)); 00635 00636 range_const_iterator iter(boost::begin(range)); 00637 range_const_iterator last(boost::end(range)); 00638 00639 while (iter != last) 00640 { 00641 if (s_iter != s_last && iter == boost::next(boost::begin(range), *s_iter)) 00642 { 00643 ++s_iter; 00644 00645 inside = !inside; 00646 } 00647 00648 if (inside) 00649 proc(*iter); 00650 00651 ++iter; 00652 } 00653 00654 return proc; 00655 } 00656 00657 /****************************************************************************************************/ 00663 template <typename Selection> 00664 inline 00665 std::pair<typename boost::range_const_iterator<Selection>::type, 00666 typename boost::range_size<Selection>::type> 00667 selection_find_boundary(const Selection& selection, 00668 typename Selection::size_type p, 00669 std::random_access_iterator_tag) 00670 { 00671 typedef typename boost::range_const_iterator<Selection>::type const_iterator; 00672 typedef typename boost::range_size<Selection>::type size_type; 00673 typedef std::pair<const_iterator, size_type> result_type; 00674 00675 const_iterator bound(std::lower_bound(boost::begin(selection), boost::end(selection), p)); 00676 00677 return result_type(bound, static_cast<size_type>(std::distance(boost::begin(selection), bound))); 00678 } 00679 00680 /****************************************************************************************************/ 00686 template <typename Selection> 00687 std::pair<typename boost::range_const_iterator<Selection>::type, 00688 typename boost::range_size<Selection>::type> 00689 selection_find_boundary(const Selection& selection, 00690 typename Selection::size_type p, 00691 std::forward_iterator_tag) 00692 { 00693 typedef typename boost::range_const_iterator<Selection>::type const_iterator; 00694 typedef typename boost::range_size<Selection>::type size_type; 00695 typedef std::pair<const_iterator, size_type> result_type; 00696 00697 const_iterator iter(boost::begin(selection)); 00698 const_iterator last(boost::end(selection)); 00699 size_type boundary_count(0); 00700 00701 while (iter != last && *iter < p) 00702 { 00703 ++boundary_count; 00704 ++iter; 00705 } 00706 00707 return result_type(iter, boundary_count); 00708 } 00709 00710 /****************************************************************************************************/ 00729 template <typename Selection> 00730 inline 00731 std::pair<typename boost::range_const_iterator<Selection>::type, 00732 typename boost::range_size<Selection>::type> 00733 selection_find_boundary(const Selection& selection, 00734 typename Selection::size_type p) 00735 { 00736 typedef typename boost::range_const_iterator<Selection>::type const_iterator; 00737 typedef typename boost::range_size<Selection>::type size_type; 00738 typedef std::pair<const_iterator, size_type> result_type; 00739 typedef typename iterator_category<const_iterator>::type iterator_category; 00740 00741 if (boost::size(selection) == 0) 00742 return result_type(boost::end(selection), 0); 00743 00744 return selection_find_boundary(selection, p, iterator_category()); 00745 } 00746 00747 /****************************************************************************************************/ 00779 template <typename SelectionIterator, typename RangeIterator> 00780 RangeIterator selection_stable_partition(SelectionIterator selection_first, 00781 SelectionIterator selection_last, 00782 RangeIterator first, 00783 RangeIterator range_first, 00784 RangeIterator range_last, 00785 std::size_t boundary_count = 0) 00786 { 00787 std::size_t n(static_cast<std::size_t>(std::distance(selection_first, selection_last))); 00788 00789 if (n == 0) 00790 return boundary_count % 2 ? range_first : range_last; 00791 00792 std::size_t half(n / 2); 00793 SelectionIterator selection_middle(boost::next(selection_first, half)); 00794 RangeIterator range_middle(boost::next(first, *selection_middle)); 00795 00796 RangeIterator i(selection_stable_partition(selection_first, selection_middle, 00797 first, range_first, range_middle, 00798 boundary_count)); 00799 00800 RangeIterator j(selection_stable_partition(boost::next(selection_middle), selection_last, 00801 first, range_middle, range_last, 00802 boundary_count + half + 1)); 00803 00804 return other_of(adobe::rotate(i, range_middle, j), range_middle); 00805 } 00806 00807 /*************************************************************************************************/ 00813 template <typename Selection, 00814 typename ForwardRange> 00815 inline 00816 typename boost::range_iterator<ForwardRange>::type 00817 selection_stable_partition(const Selection& selection, 00818 ForwardRange& range) 00819 { 00820 return selection_stable_partition(boost::begin(selection), boost::end(selection), 00821 boost::begin(range), 00822 boost::begin(range), boost::end(range), 00823 start_selected(selection)); 00824 } 00825 00826 /*************************************************************************************************/ 00862 template <typename Selection, 00863 typename ForwardRange> 00864 std::pair<typename boost::range_iterator<ForwardRange>::type, 00865 typename boost::range_iterator<ForwardRange>::type> 00866 selection_stable_partition_about(const Selection& selection, 00867 ForwardRange& range, 00868 std::size_t p, 00869 typename boost::range_size<Selection>::type prior_boundary_count = 0) 00870 { 00871 typedef typename boost::range_size<Selection>::type size_type; 00872 typedef typename boost::range_const_iterator<Selection>::type selection_const_iterator; 00873 typedef typename boost::range_iterator<ForwardRange>::type range_iterator; 00874 00875 std::pair<selection_const_iterator, size_type> selection_split = 00876 adobe::selection_find_boundary(selection, p); 00877 00878 range_iterator first(boost::begin(range)); 00879 range_iterator range_p(boost::next(first, p)); 00880 range_iterator last(boost::end(range)); 00881 00882 range_iterator i(selection_stable_partition(boost::begin(selection), selection_split.first, 00883 first, first, range_p, 00884 prior_boundary_count)); 00885 00886 range_iterator j(selection_stable_partition(selection_split.first, boost::end(selection), 00887 first, range_p, last, 00888 selection_split.second + 1)); 00889 00890 return std::pair<range_iterator, range_iterator>(i, j); 00891 } 00892 00893 /****************************************************************************************************/ 00899 template <typename Selection, 00900 typename ForwardRange> 00901 Selection index_set_to_selection(const ForwardRange& index_set) 00902 { 00903 Selection result; 00904 00905 // REVISIT (fbrereto) : This would go much faster using divide-and-conquer 00906 // and eventually balanced reduction. 00907 00908 typedef typename boost::range_const_iterator<ForwardRange>::type range_const_iterator; 00909 00910 range_const_iterator iter(boost::begin(index_set)); 00911 range_const_iterator last(boost::end(index_set)); 00912 00913 for (; iter != last; ++iter) 00914 { 00915 Selection tmp; 00916 00917 tmp.push_back(*iter); 00918 tmp.push_back(*iter + 1); 00919 00920 result = selection_union(result, tmp); 00921 } 00922 00923 return result; 00924 } 00925 00926 /****************************************************************************************************/ 00932 template <typename Selection, 00933 typename OutputIterator> 00934 OutputIterator selection_to_index_set(const Selection& selection, 00935 typename boost::range_size<Selection>::type max_index, 00936 OutputIterator output) 00937 { 00938 typedef typename boost::range_size<Selection>::type size_type; 00939 typedef typename boost::range_const_iterator<Selection>::type selection_const_iterator; 00940 00941 bool selected(start_selected(selection)); 00942 std::size_t index(0); 00943 selection_const_iterator iter(boost::begin(selection)); 00944 selection_const_iterator last(boost::end(selection)); 00945 00946 while (iter != last) 00947 { 00948 while (index != *iter && index != max_index) 00949 { 00950 if (selected) 00951 *output++ = index; 00952 00953 ++index; 00954 } 00955 00956 selected = !selected; 00957 ++iter; 00958 } 00959 00960 return output; 00961 } 00962 00963 /*************************************************************************************************/ 00964 00965 } // namespace adobe 00966 00967 /*************************************************************************************************/ 00968 00969 #endif 00970 00971 /*************************************************************************************************/ |