diff --git a/adobe_source_libraries/Jamfile.v2 b/adobe_source_libraries/Jamfile.v2 index 1067708..64d50c0 100644 --- a/adobe_source_libraries/Jamfile.v2 +++ b/adobe_source_libraries/Jamfile.v2 @@ -40,9 +40,9 @@ adobe_requirements = msvc:ADOBE_TEST_MICROSOFT_NO_DEPRECATE=0 msvc,multi:USE_WINTHREAD msvc:_WIN32_WINNT=0x400 - darwin:"-Werror -Wall -Wno-trigraphs -Wreturn-type -Wnon-virtual-dtor -Woverloaded-virtual -Wformat -Wmissing-braces -Wparentheses -Wswitch -Wunused-function -Wunused-label -Wunused-parameter -Wunused-variable -Wunused-value -Wunknown-pragmas -Wsign-compare" + darwin:"-Wno-error -Wall -Wno-trigraphs -Wreturn-type -Wnon-virtual-dtor -Woverloaded-virtual -Wformat -Wmissing-braces -Wparentheses -Wswitch -Wunused-function -Wunused-label -Wno-unused-parameter -Wno-unused-variable -Wno-unused-value -Wunknown-pragmas -Wsign-compare" darwin:"-Xlinker -Y -Xlinker 5" - gcc,debug:"-Werror -Wall -Wno-trigraphs -Wreturn-type -Wnon-virtual-dtor -Woverloaded-virtual -Wformat -Wmissing-braces -Wparentheses -Wswitch -Wunused-function -Wunused-label -Wunused-parameter -Wunused-variable -Wunused-value -Wunknown-pragmas -Wsign-compare -Wno-parentheses" + gcc,debug:"-Wno-error -Wall -Wno-trigraphs -Wreturn-type -Wnon-virtual-dtor -Woverloaded-virtual -Wformat -Wmissing-braces -Wparentheses -Wswitch -Wunused-function -Wunused-label -Wno-unused-parameter -Wno-unused-variable -Wno-unused-value -Wunknown-pragmas -Wsign-compare -Wno-parentheses" ; asl_requirements = diff --git a/adobe_source_libraries/library/algorithm.hpp b/adobe_source_libraries/library/algorithm.hpp new file mode 100644 index 0000000..fdcb3f0 --- /dev/null +++ b/adobe_source_libraries/library/algorithm.hpp @@ -0,0 +1,16 @@ +#ifndef LIBRARY_ALGORITHM_HPP +#define LIBRARY_ALGORITHM_HPP + +#include + +namespace library { + + template + inline bool found(Iterator begin, Iterator end, const T& t) { + return std::find(begin, end, t) != end; + } + +} + +#endif + diff --git a/adobe_source_libraries/library/bipartite_graph/detail/edge_iterator.hpp b/adobe_source_libraries/library/bipartite_graph/detail/edge_iterator.hpp new file mode 100644 index 0000000..92e5052 --- /dev/null +++ b/adobe_source_libraries/library/bipartite_graph/detail/edge_iterator.hpp @@ -0,0 +1,84 @@ +#ifndef LIBRARY_EDGE_ITERATOR_HPP +#define LIBRARY_EDGE_ITERATOR_HPP + +#include +#include + +namespace pm { + + namespace detail { + + template < + typename LeftVertexIterator, + typename OutgoingRightVertexIterator, + typename RightEdge, + typename IncomingRightVertexIterator, + typename LeftEdge, + typename Graph + > + class edge_iterator : + public boost::iterator_facade< + /*Derived=*/edge_iterator< + LeftVertexIterator, + OutgoingRightVertexIterator, + RightEdge, + IncomingRightVertexIterator, + LeftEdge, + Graph>, + /*Value=*/boost::variant, + /*CategoryOrTraversal=*/boost::forward_traversal_tag, + /*Reference=*/boost::variant, + /*Difference=*/int + > + { + public: + edge_iterator() {} + edge_iterator(const LeftVertexIterator& il, const LeftVertexIterator& el, const Graph& g) : + il(il), el(el), g(&g) { + reset(); + } + + private: + friend class boost::iterator_core_access; + typedef typename edge_iterator::iterator_facade_ super; + typedef typename super::reference edge; + + edge dereference() const { + return (ior != eor) ? edge(RightEdge(*il, *ior)) : edge(LeftEdge(*iir, *il)); + } + + bool equal(const edge_iterator& rhs) const { + return (il == rhs.il) && (il == el || (ior == rhs.ior && iir == rhs.iir)); + } + + void increment() { + if (ior != eor) { + ++ior; + } else if (iir != eir) { + ++iir; + } else { + ++il; + reset(); + } + } + + void reset() { + while (il != el && degree(*il, *g) == 0) + ++il; + if (il != el) { + tie(ior, eor) = outgoing_vertices(*il, *g); + tie(iir, eir) = incoming_vertices(*il, *g); + } + } + + LeftVertexIterator il, el; + OutgoingRightVertexIterator ior, eor; + IncomingRightVertexIterator iir, eir; + const Graph* g; + }; + + } + +} + +#endif diff --git a/adobe_source_libraries/library/constructer.hpp b/adobe_source_libraries/library/constructer.hpp new file mode 100644 index 0000000..e3cd252 --- /dev/null +++ b/adobe_source_libraries/library/constructer.hpp @@ -0,0 +1,26 @@ +#ifndef LIBRARY_CONSTRUCTER_HPP +#define LIBRARY_CONSTRUCTER_HPP + +namespace library { + + template + class constructer { + public: + typedef T result_type; + T operator() () const { return T(); } + /* essentially a cast function */ + template + T operator() (const A1& a1) const { return T(a1); } + }; + + template + class constructer { + public: + typedef T* result_type; + T* operator() (T& t) { return &t; } + }; + +} + +#endif + diff --git a/adobe_source_libraries/library/debug.hpp b/adobe_source_libraries/library/debug.hpp new file mode 100644 index 0000000..412330f --- /dev/null +++ b/adobe_source_libraries/library/debug.hpp @@ -0,0 +1,62 @@ +#undef DEBUG_PRINT_SIZEOF +#undef DEBUG_PRINT_VARIABLE +#undef DEBUG_PRINT +#undef DEBUG_DO + +#ifndef LIBRARY_DEBUG_HPP +#define LIBRARY_DEBUG_HPP + +#include + +namespace debug { + + class indenter { + private: + unsigned int level; + static const unsigned int MULTIPLIER = 2; + public: + indenter() : level(0) {} + /* prefix */ + indenter& operator++ () { ++level; return *this; } + indenter& operator-- () { --level; return *this; } + /* postfix */ + indenter operator++ (int) { indenter copy(*this); ++level; return copy; } + indenter operator-- (int) { indenter copy(*this); --level; return copy; } + friend std::ostream& operator<<(std::ostream&, const indenter&); + }; + + inline std::ostream& operator<<(std::ostream& out, const indenter& indent) { + out << std::string(indent.level * indenter::MULTIPLIER, ' '); + return out; + } + +} + +#endif + +#ifdef DEBUG + +#include +#include + +#define DEBUG_PRINT_SIZEOF(T) \ + std::cerr << left << setw(64) << "sizeof( " #T " ) = " << right << setw(4) << sizeof(T) << " bytes\n"; + +#define DEBUG_PRINT_VARIABLE(x) \ + std::cerr << #x " = " << (x) << std::endl + +#define DEBUG_PRINT(expr) \ + std::cerr << expr << std::endl + +#define DEBUG_DO(block) \ + block + +#else +#define DEBUG_PRINT_SIZEOF(T) +#define DEBUG_PRINT_VARIABLE(x) +#define DEBUG_PRINT(expr) +#define DEBUG_DO(block) +#endif + +#undef DEBUG + diff --git a/adobe_source_libraries/library/fast_property_map.hpp b/adobe_source_libraries/library/fast_property_map.hpp new file mode 100644 index 0000000..7301b36 --- /dev/null +++ b/adobe_source_libraries/library/fast_property_map.hpp @@ -0,0 +1,74 @@ +#ifndef LIBRARY_FAST_PROPERTY_MAP_HPP +#define LIBRARY_FAST_PROPERTY_MAP_HPP + +#include + +#include + +#include +#include + +namespace library { + + template < + typename Key, + typename Value, + typename IndexMap = identity + > + class fast_property_map : member { + private: + typedef member index; + /* IndexMap needs to provide a hashing function that + * 1. is regular + * 2. runs in constant time + * 3. yields a value in [0, n) for existing keys in the map + * 4. yields the value n for new keys added to the map */ + + std::vector table; + + public: + typedef Value value_type; + typedef Value& reference; + typedef const Value& const_reference; + typedef Key key_type; + typedef boost::lvalue_property_map_tag category; + + fast_property_map() {} + + explicit fast_property_map(size_t n, const Value& value = Value(), const IndexMap& index = IndexMap()) : + fast_property_map::index(index), + table(n, value) {} + + size_t size() const { return table.size(); } + + reference operator[] (const key_type& key) { + return table[get(index::_m(), key)]; + } + + const_reference operator[] (const key_type& key) const { + return table[get(index::_m(), key)]; + } + + friend + const_reference get(const fast_property_map& pmap, const key_type& key) { + return pmap[key]; + } + + friend + reference get(fast_property_map& pmap, const key_type& key) { + return pmap[key]; + } + + friend + void put(fast_property_map& pmap, const key_type& key, const value_type& val) { + size_t i = get(pmap.index::_m(), key); + if (i == pmap.table.size()) pmap.table.push_back(val); + else pmap.table[i] = val; + } + + }; + +} + +#endif + diff --git a/adobe_source_libraries/library/graph_traits.hpp b/adobe_source_libraries/library/graph_traits.hpp new file mode 100644 index 0000000..42639b8 --- /dev/null +++ b/adobe_source_libraries/library/graph_traits.hpp @@ -0,0 +1,15 @@ +#ifndef LIBRARY_GRAPH_TRAITS_HPP +#define LIBRARY_GRAPH_TRAITS_HPP + +#include + +namespace library { + + struct vertex_and_edge_list_graph_tag : + public boost::vertex_list_graph_tag, + public boost::edge_list_graph_tag {}; + +} + +#endif + diff --git a/adobe_source_libraries/library/identity.hpp b/adobe_source_libraries/library/identity.hpp new file mode 100644 index 0000000..4829382 --- /dev/null +++ b/adobe_source_libraries/library/identity.hpp @@ -0,0 +1,16 @@ +#ifndef LIBRARY_IDENTITY_HPP +#define LIBRARY_IDENTITY_HPP + +#include + +namespace library { + + template + struct identity : public std::unary_function { + const T& operator() (const T& t) const { return t; } + }; + +} + +#endif + diff --git a/adobe_source_libraries/library/iterator/composite_iterator.hpp b/adobe_source_libraries/library/iterator/composite_iterator.hpp new file mode 100644 index 0000000..9f2e3aa --- /dev/null +++ b/adobe_source_libraries/library/iterator/composite_iterator.hpp @@ -0,0 +1,71 @@ +#ifndef LIBRARY_COMPOSITE_ITERATOR_HPP +#define LIBRARY_COMPOSITE_ITERATOR_HPP + +#include + +#include +#include + +namespace library { + + /* This is like a concatenated-heterogeneous-range iterator. */ + template + class composite_iterator : + public boost::iterator_facade< + /*Derived=*/composite_iterator, + /*Value=*/boost::variant< + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type + >, + /* It may be possible to extend this in the future to other traversal + * categories. */ + /*CategoryOrTraversal=*/boost::forward_traversal_tag, + /*Reference=*/boost::variant< + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type + > + > + { + private: + typedef LeftIterator left_iterator; + typedef RightIterator right_iterator; + + typedef typename composite_iterator::iterator_facade_ super; + + left_iterator left, middle; + right_iterator right; + + public: + composite_iterator() {} + + composite_iterator( + const left_iterator& left, + const left_iterator& middle, + const right_iterator& right) : + left(left), + middle(middle), + right(right) {} + + private: + friend class boost::iterator_core_access; + + void increment() { + if (left != middle) ++left; + else ++right; + } + + bool equal(const composite_iterator& rhs) const { + return left == rhs.left && right == rhs.right; + } + + typename composite_iterator::value_type dereference() const { + if (left != middle) return typename composite_iterator::value_type(*left); + else return typename composite_iterator::value_type(*right); + } + + }; + +} + +#endif + diff --git a/adobe_source_libraries/library/iterator/multiplex_output_iterator.hpp b/adobe_source_libraries/library/iterator/multiplex_output_iterator.hpp new file mode 100644 index 0000000..1e817c4 --- /dev/null +++ b/adobe_source_libraries/library/iterator/multiplex_output_iterator.hpp @@ -0,0 +1,82 @@ +#ifndef LIBRARY_MULTIPLEX_OUTPUT_ITERATOR_HPP +#define LIBRARY_MULTIPLEX_OUTPUT_ITERATOR_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace library { + +#define MULTIPLEX_VISITOR_MEMBER_ITERATOR(n) \ + mutable Iterator ## n i ## n; + +#define MULTIPLEX_PARAM(n) \ + const Iterator ## n & i ## n + +#define MULTIPLEX_VISITOR_CTOR_MEM_INIT(n) \ + i ## n (i ## n) + +#define MULTIPLEX_VISITOR_MEMBER_OPERATOR(n) \ + void operator() (const T ## n & x) const { * i ## n ++ = x; } + +#define MULTIPLEX_VISITOR_MEMBER_ITERATOR_BOOST(z, n, data) \ + MULTIPLEX_VISITOR_MEMBER_ITERATOR(n) +#define MULTIPLEX_PARAM_BOOST(z, n, data) \ + MULTIPLEX_PARAM(n) +#define MULTIPLEX_VISITOR_CTOR_MEM_INIT_BOOST(z, n, data) \ + MULTIPLEX_VISITOR_CTOR_MEM_INIT(n) +#define MULTIPLEX_VISITOR_MEMBER_OPERATOR_BOOST(z, n, data) \ + MULTIPLEX_VISITOR_MEMBER_OPERATOR(n) + +#define MAKE_MULTIPLEX_OUTPUT_ITERATOR(n) \ + template \ + class multiplex_output_iterator_function ## n { \ + private: \ + class multiplex_output_iterator_visitor : public boost::static_visitor { \ + private: \ + BOOST_PP_REPEAT(n, MULTIPLEX_VISITOR_MEMBER_ITERATOR_BOOST,) \ + public: \ + explicit multiplex_output_iterator_visitor( \ + BOOST_PP_ENUM(n, MULTIPLEX_PARAM_BOOST,) \ + ) : \ + BOOST_PP_ENUM(n, MULTIPLEX_VISITOR_CTOR_MEM_INIT_BOOST,) {} \ + BOOST_PP_REPEAT(n, MULTIPLEX_VISITOR_MEMBER_OPERATOR_BOOST,) \ + template \ + void operator() (const U&) const {} \ + }; \ + multiplex_output_iterator_visitor visitor; \ + public: \ + explicit multiplex_output_iterator_function ## n (BOOST_PP_ENUM(n, MULTIPLEX_PARAM_BOOST,)) : \ + visitor(BOOST_PP_ENUM_PARAMS(n, i)) {} \ + template \ + void operator() (const boost::variant& v) const { \ + apply_visitor(visitor, v); \ + } \ + }; \ + template \ + boost::function_output_iterator< \ + multiplex_output_iterator_function ## n< \ + BOOST_PP_ENUM_PARAMS(n, T), BOOST_PP_ENUM_PARAMS(n, Iterator) \ + > \ + > \ + make_multiplex_output_iterator(BOOST_PP_ENUM(n, MULTIPLEX_PARAM_BOOST,)) { \ + return boost::make_function_output_iterator( \ + multiplex_output_iterator_function ## n< \ + BOOST_PP_ENUM_PARAMS(n, T), BOOST_PP_ENUM_PARAMS(n, Iterator) \ + >(BOOST_PP_ENUM_PARAMS(n, i)) \ + ); \ + } + + MAKE_MULTIPLEX_OUTPUT_ITERATOR(1) + MAKE_MULTIPLEX_OUTPUT_ITERATOR(2) + +} + +#endif + diff --git a/adobe_source_libraries/library/iterator/transform_iterator.hpp b/adobe_source_libraries/library/iterator/transform_iterator.hpp new file mode 100644 index 0000000..3e5935d --- /dev/null +++ b/adobe_source_libraries/library/iterator/transform_iterator.hpp @@ -0,0 +1,191 @@ +// (C) Copyright David Abrahams 2002. +// (C) Copyright Jeremy Siek 2002. +// (C) Copyright Thomas Witt 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#ifndef LIBRARY_TRANSFORM_ITERATOR_HPP +#define LIBRARY_TRANSFORM_ITERATOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1310)) +# include + +#endif +#include + +#include + +namespace library +{ + template + class transform_iterator; + + namespace detail + { + + template + struct function_object_result + { + typedef typename UnaryFunc::result_type type; + }; + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + template + struct function_object_result + { + typedef Return type; + }; +#endif + + // Compute the iterator_adaptor instantiation to be used for transform_iterator + template + struct transform_iterator_base + { + private: + // By default, dereferencing the iterator yields the same as + // the function. Do we need to adjust the way + // function_object_result is computed for the standard + // proposal (e.g. using Doug's result_of)? + typedef typename boost::detail::ia_dflt_help< + Reference + , function_object_result + >::type reference; + + // To get the default for Value: remove any reference on the + // result type, but retain any constness to signal + // non-writability. Note that if we adopt Thomas' suggestion + // to key non-writability *only* on the Reference argument, + // we'd need to strip constness here as well. + typedef typename boost::detail::ia_dflt_help< + Value + , boost::remove_reference + >::type cv_value_type; + + public: + typedef boost::iterator_adaptor< + transform_iterator + , Iterator + , cv_value_type + , boost::use_default // Leave the traversal category alone + , reference + > type; + }; + } + + template + class transform_iterator + : public library::detail::transform_iterator_base::type, + private library::member + { + typedef typename + library::detail::transform_iterator_base::type + super_t; + + typedef typename + library::member + function; + + friend class boost::iterator_core_access; + + public: + transform_iterator() { } + + transform_iterator(Iterator const& x, UnaryFunc f) + : super_t(x), function(f) { } + + explicit transform_iterator(Iterator const& x) + : super_t(x) + { + // Pro8 is a little too aggressive about instantiating the + // body of this function. +#if !BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) + // don't provide this constructor if UnaryFunc is a + // function pointer type, since it will be 0. Too dangerous. + BOOST_STATIC_ASSERT(boost::is_class::value); +#endif + } + + template< + class OtherUnaryFunction + , class OtherIterator + , class OtherReference + , class OtherValue> + transform_iterator( + transform_iterator const& t + , typename boost::enable_if_convertible::type* = 0 +#if !BOOST_WORKAROUND(BOOST_MSVC, == 1310) + , typename boost::enable_if_convertible::type* = 0 +#endif + ) + : super_t(t.base()), function(t.functor()) + {} + + UnaryFunc functor() const + { return function::_m(); } + + private: + typename super_t::reference dereference() const + { return functor()(*this->base()); } + + }; + + template + transform_iterator + make_transform_iterator(Iterator it, UnaryFunc fun) + { + return transform_iterator(it, fun); + } + + // Version which allows explicit specification of the UnaryFunc + // type. + // + // This generator is not provided if UnaryFunc is a function + // pointer type, because it's too dangerous: the default-constructed + // function pointer in the iterator be 0, leading to a runtime + // crash. + template +#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) + typename boost::mpl::if_< +#else + typename boost::iterators::enable_if< +#endif + boost::is_class // We should probably find a cheaper test than is_class<> + , transform_iterator +#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) + , int[3] +#endif + >::type + make_transform_iterator(Iterator it) + { + return transform_iterator(it, UnaryFunc()); + } + +#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) && !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING) + template + transform_iterator< Return (*)(Argument), Iterator, Return> + make_transform_iterator(Iterator it, Return (*fun)(Argument)) + { + return transform_iterator(it, fun); + } +#endif + +} // namespace library + +#include + +#endif // BOOST_TRANSFORM_ITERATOR_23022003THW_HPP diff --git a/adobe_source_libraries/library/iterator/variant_iterator.hpp b/adobe_source_libraries/library/iterator/variant_iterator.hpp new file mode 100644 index 0000000..d31f0ae --- /dev/null +++ b/adobe_source_libraries/library/iterator/variant_iterator.hpp @@ -0,0 +1,85 @@ +#ifndef LIBRARY_VARIANT_ITERATOR_HPP +#define LIBRARY_VARIANT_ITERATOR_HPP + +#include + +#include +#include +#include + +namespace library { + + /* This is like a concatenated-heterogeneous-range iterator. */ + template + class variant_iterator : + public boost::iterator_facade< + /*Derived=*/variant_iterator, + /*Value=*/boost::variant< + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type + >, + /* TODO: correct this to be the meet of the component categories / + * traversals. */ + /*CategoryOrTraversal=*/boost::forward_traversal_tag, + /*Reference=*/boost::variant< + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type + > + > + { + private: + typedef Iterator1 iterator1; + typedef Iterator2 iterator2; + + typedef typename variant_iterator::iterator_facade_ super; + + boost::variant base; + + public: + variant_iterator() {} + + variant_iterator(const iterator1& i) : base(i) {} + variant_iterator(const iterator2& i) : base(i) {} + + private: + friend class boost::iterator_core_access; + + struct incrementer : public boost::static_visitor<> { + template + void operator() (Iterator& i) const { ++i; } + }; + + void increment() { + boost::apply_visitor(incrementer(), base); + } + + struct equals : public boost::static_visitor { + template + bool operator() (const T& lhs, const T& rhs) const { return lhs == rhs; } + template + bool operator() (const T&, const U&) const { return false; } + }; + + bool equal(const variant_iterator& rhs) const { + return boost::apply_visitor(equals(), base, rhs.base); + } + + template + struct dereferencer : public boost::static_visitor { + template + typename super::reference operator() (const Iterator& i) const { return *i; } + }; + + typename super::reference dereference() const { + return boost::apply_visitor( + dereferencer(), + base + ); + } + + }; + +} + +#endif + diff --git a/adobe_source_libraries/library/member.hpp b/adobe_source_libraries/library/member.hpp new file mode 100644 index 0000000..0f748a3 --- /dev/null +++ b/adobe_source_libraries/library/member.hpp @@ -0,0 +1,30 @@ +#ifndef LIBRARY_MEMBER_HPP +#define LIBRARY_MEMBER_HPP + +#include +#include + +namespace library { + + template + class member { + public: + member() {} + explicit member(const M& m) : m(m) {} + const M& _m() const { return m; } + private: + M m; + }; + + template + class member >::type> { + public: + member() {} + explicit member(const M& m) {} + M _m() const { return M(); } + }; + +} + +#endif + diff --git a/adobe_source_libraries/library/wrapper.hpp b/adobe_source_libraries/library/wrapper.hpp new file mode 100644 index 0000000..41acd4d --- /dev/null +++ b/adobe_source_libraries/library/wrapper.hpp @@ -0,0 +1,45 @@ +#ifndef LIBRARY_WRAPPER_H +#define LIBRARY_WRAPPER_H + +#include + +#include + +namespace library { + + template + struct wrapper : boost::totally_ordered > + { + T base; + + explicit wrapper(const T& copy = T()) : base(copy) {} + wrapper& operator= (const T& rhs) { base = rhs; return *this; } + + friend + bool operator== (const wrapper& lhs, const wrapper& rhs) { + return lhs.base == rhs.base; + } + + friend + bool operator< (const wrapper& lhs, const wrapper& rhs) { + return lhs.base < rhs.base; + } + + }; + + template + std::ostream& operator<< (std::ostream& out, const wrapper& w) { + return out << w.base; + } + + struct unwrapper { + template + friend T& get(const unwrapper&, wrapper& w) { return w.base; } + template + friend const T& get(const unwrapper&, const wrapper& w) { return w.base; } + }; + +} + +#endif + diff --git a/adobe_source_libraries/pm/cgraph.hpp b/adobe_source_libraries/pm/cgraph.hpp new file mode 100644 index 0000000..9f68f20 --- /dev/null +++ b/adobe_source_libraries/pm/cgraph.hpp @@ -0,0 +1,509 @@ +#ifndef PM_CGRAPH_HPP +#define PM_CGRAPH_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace pm { + + template < + typename UserVariable = detail::empty, + typename UserMethod = detail::empty, + typename UserConstraint = detail::empty, + typename UserInputEdge = detail::empty, + typename UserOutputEdge = detail::empty + > + class basic_cgraph { + public: + /***************************************************/ + /* associated types */ + + /* The handles must be lightweight + * and have variable semantics (i.e. no references) */ + typedef library::wrapper variable; + typedef library::wrapper method; + typedef library::wrapper constraint; + + typedef UserVariable user_variable; + typedef UserMethod user_method; + typedef UserConstraint user_constraint; + typedef UserInputEdge user_input_edge; + typedef UserOutputEdge user_output_edge; + + private: + struct base_variable : public user_variable { + base_variable(const user_variable& user_v = user_variable()) : + user_variable(user_v) {} + }; + + struct base_method : public user_method { + bool is_self_loop; + constraint its_constraint; + + base_method(const user_method& user_m = user_method()) : + user_method(user_m), is_self_loop(false) {} + }; + + struct base_input_edge : public user_input_edge { + bool is_self_loop; + base_input_edge(const user_input_edge& user_ie = user_input_edge()) : + user_input_edge(user_ie), is_self_loop(false) {} + }; + + struct base_output_edge : public user_output_edge { + bool is_self_loop; + base_output_edge(const user_output_edge& user_oe = user_output_edge()) : + user_output_edge(user_oe), is_self_loop(false) {} + }; + + typedef detail::vertex stored_variable; + typedef detail::vertex stored_method; + + typedef library::fast_property_map + variable_property_map; + typedef library::fast_property_map + method_property_map; + + public: + + typedef library::transform_iterator, boost::counting_iterator > + variable_iterator; + typedef library::transform_iterator, boost::counting_iterator > + method_iterator; + typedef library::transform_iterator, boost::counting_iterator > + constraint_iterator; + + typedef detail::edge input_edge; + typedef detail::edge output_edge; + + typedef typename stored_variable::edge_list::const_iterator + outgoing_method_iterator; + typedef typename stored_variable::edge_list::const_iterator + incoming_method_iterator; + typedef typename stored_method::edge_list::const_iterator + outgoing_variable_iterator; + typedef typename stored_method::edge_list::const_iterator + incoming_variable_iterator; + + typedef detail::xput_edge_iterator + out_input_edge_iterator; + typedef detail::xput_edge_iterator + in_output_edge_iterator; + typedef detail::xput_edge_iterator + out_output_edge_iterator; + typedef detail::xput_edge_iterator + in_input_edge_iterator; + + typedef unsigned int vertex_list_size_type; + typedef unsigned int edge_list_size_type; + typedef edge_list_size_type degree_size_type; + + typedef typename std::vector::const_iterator csm_iterator; + //typedef typename std::set::const_iterator attached_variable_iterator; + + private: + struct base_constraint : public user_constraint { + /* Note: disallows constraint of constraints */ + std::vector its_methods; + //std::set its_variables; + + base_constraint(const user_constraint& user_c = user_constraint()) : + user_constraint(user_c) {} + }; + + typedef base_constraint stored_constraint; + + typedef library::fast_property_map + constraint_property_map; + + typedef base_input_edge stored_input_edge; + typedef base_output_edge stored_output_edge; + + typedef std::map input_edge_property_map; + typedef std::map output_edge_property_map; + + /***************************************************/ + /* Members */ + + variable_property_map its_variables; + method_property_map its_methods; + constraint_property_map its_constraints; + + input_edge_property_map its_input_edges; + output_edge_property_map its_output_edges; + + /***************************************************/ + /* traits */ + + template + struct is_vertex : public boost::mpl::or_< + boost::is_same, + boost::is_same + > {}; + + template + struct vertex_traits_base; + + template + struct vertex_traits_base { + typedef stored_variable stored_type; + typedef method other; + typedef input_edge out_edge; + typedef out_input_edge_iterator out_edge_iterator; + typedef in_output_edge_iterator in_edge_iterator; + typedef outgoing_method_iterator outgoing_vertex_iterator; + typedef incoming_method_iterator incoming_vertex_iterator; + }; + + template + struct vertex_traits_base { + typedef stored_method stored_type; + typedef variable other; + typedef output_edge out_edge; + typedef out_output_edge_iterator out_edge_iterator; + typedef in_input_edge_iterator in_edge_iterator; + typedef outgoing_variable_iterator outgoing_vertex_iterator; + typedef incoming_variable_iterator incoming_vertex_iterator; + }; + + template + struct vertex_traits {}; + + template + struct vertex_traits< + Vertex, + typename boost::enable_if >::type + > : public vertex_traits_base + { + typedef vertex_traits_base super; + typedef typename super::stored_type::edge_list edge_list; + }; + + + template + struct is_edge : public boost::mpl::or_< + boost::is_same, + boost::is_same + > {}; + + template + struct edge_traits_base; + + template + struct edge_traits_base { + }; + + template + struct edge_traits_base { + }; + + template + struct edge_traits {}; + + template + struct edge_traits< + Edge, + typename boost::enable_if >::type + > : public edge_traits_base + { + typedef typename Edge::source_type source; + typedef typename Edge::target_type target; + }; + + public: + /***************************************************/ + /* PropertyMapGraph */ + + stored_variable& operator[] (const variable& v) { return its_variables[v]; } + const stored_variable& operator[] (const variable& v) const { return its_variables[v]; } + stored_method& operator[] (const method& m) { return its_methods[m]; } + const stored_method& operator[] (const method& m) const { return its_methods[m]; } + stored_constraint& operator[] (const constraint& c) { return its_constraints[c]; } + const stored_constraint& operator[] (const constraint& c) const { return its_constraints[c]; } + stored_input_edge& operator[] (const input_edge& e) { return its_input_edges[e]; } + const stored_input_edge& operator[] (const input_edge& e) const { return its_input_edges.find(e)->second; } + stored_output_edge& operator[] (const output_edge& e) { return its_output_edges[e]; } + const stored_output_edge& operator[] (const output_edge& e) const { return its_output_edges.find(e)->second; } + + private: + /***************************************************/ + /* vertex helpers */ + + /* warrants (i.e. anti-privacy) */ + template + static typename vertex_traits::edge_list& out_edge_list(const Vertex& v, basic_cgraph& g) { return g[v].out_edges; } + template + static const typename vertex_traits::edge_list& out_edge_list(const Vertex& v, const basic_cgraph& g) { return g[v].out_edges; } + template + static typename vertex_traits::edge_list& in_edge_list(const Vertex& v, basic_cgraph& g) { return g[v].in_edges; } + template + static const typename vertex_traits::edge_list& in_edge_list(const Vertex& v, const basic_cgraph& g) { return g[v].in_edges; } + + /***************************************************/ + /* edge helpers */ + + template + static typename vertex_traits::out_edge make_edge(const Source& s, const typename vertex_traits::other& t) { + return typename vertex_traits::out_edge(s, t); + } + + /***************************************************/ + /* Graph */ + + public: + static variable null_variable() { return variable(-1); } + static method null_method() { return method(-1); } + static constraint null_constraint() { return constraint(-1); } + + /***************************************************/ + /* IncidenceGraph */ + + private: + template + friend + std::pair::out_edge_iterator, typename vertex_traits::out_edge_iterator> + out_edges(const Source& s, const basic_cgraph& g) + { + typedef typename vertex_traits::out_edge_iterator iterator; + return make_pair(iterator(s, out_edge_list(s, g).begin()), iterator(s, out_edge_list(s, g).end())); + } + + /* TODO: fix templates */ + template + friend typename edge_traits::source source(const Edge& e, const basic_cgraph& g) { return e.source; } + template + friend typename edge_traits::target target(const Edge& e, const basic_cgraph& g) { return e.target; } + + template + friend degree_size_type out_degree(const Source& s, const basic_cgraph& g) + { return out_edge_list(s, g).size(); } + + /***************************************************/ + /* BidirectionalGraph */ + + template + friend + std::pair::in_edge_iterator, typename vertex_traits::in_edge_iterator> + in_edges(const Target& t, const basic_cgraph& g) { + typedef typename vertex_traits::in_edge_iterator iterator; + return make_pair(iterator(t, in_edge_list(t, g).begin()), iterator(t, in_edge_list(t, g).end())); + } + + template + friend degree_size_type in_degree(const Target& t, const basic_cgraph& g) + { return in_edge_list(t, g).size(); } + template + friend degree_size_type degree(const Vertex& v, const basic_cgraph& g) + { return in_degree(v, g) + out_degree(v, g); } + + + /***************************************************/ + /* AdjacencyGraph */ + + template + friend + std::pair::outgoing_vertex_iterator, typename vertex_traits::outgoing_vertex_iterator> + outgoing_vertices(const Source& s, const basic_cgraph& g) { + const typename vertex_traits::edge_list& list = out_edge_list(s, g); + return make_pair(list.begin(), list.end()); + } + + template + friend + std::pair::incoming_vertex_iterator, typename vertex_traits::incoming_vertex_iterator> + incoming_vertices(const Target& t, const basic_cgraph& g) { + const typename vertex_traits::edge_list& list = in_edge_list(t, g); + return make_pair(list.begin(), list.end()); + } + + /***************************************************/ + /* VertexListGraph */ + + friend + std::pair variables(const basic_cgraph& g) { + return std::make_pair(variable_iterator(0), variable_iterator(num_variables(g))); + } + + friend + std::pair methods(const basic_cgraph& g) { + return std::make_pair(method_iterator(0), method_iterator(num_methods(g))); + } + + friend + std::pair constraints(const basic_cgraph& g) { + return std::make_pair(constraint_iterator(0), constraint_iterator(num_constraints(g))); + } + + friend vertex_list_size_type num_variables(const basic_cgraph& g) { return g.its_variables.size(); } + friend vertex_list_size_type num_methods(const basic_cgraph& g) { return g.its_methods.size(); } + friend size_t num_constraints(const basic_cgraph& g) { return g.its_constraints.size(); } + + /***************************************************/ + /* MutableGraph + MutablePropertyGraph */ + + /* should we take const graphs and use const_cast like BGL? maybe make members mutable? */ + friend + variable add_variable(basic_cgraph& g, const user_variable& user_v = user_variable()) { + variable v(g.its_variables.size()); + put(g.its_variables, v, stored_variable(user_v)); + return v; + } + + friend + method add_method(basic_cgraph& g, const user_method& user_m = user_method()) { + method m(g.its_methods.size()); + put(g.its_methods, m, stored_method(user_m)); + return m; + } + + friend + constraint add_constraint(basic_cgraph& g, const user_constraint& user_c = user_constraint()) { + constraint c(g.its_constraints.size()); + put(g.its_constraints, c, stored_constraint(user_c)); + return c; + } + + template + friend + void clear_vertex(const Vertex& v, basic_cgraph& g) { +#if __GXX_LAMBDAS__ + for_each(out_edge_list(v, g).begin(), out_edge_list(v, g).end(), [v] (vertex_traits::other t) { + stored_method::edge_list& sources = in_edge_list(t, g); +# ifdef ALLOW_PARALLEL_EDGES + sources.erase(remove(sources.begin(), sources.end(), v), sources.end()); +# else + sources.erase(find(sources.begin(), sources.end(), v)); +# endif + }); +#endif + out_edge_list(v, g).clear(); +#if __GXX_LAMBDAS__ + for_each(in_edge_list(v, g).begin(), in_edge_list(v, g).end(), [v] (vertex_traits::other s) { + stored_method::edge_list& targets = out_edge_list(s, g); +# ifdef ALLOW_PARALLEL_EDGES + targets.erase(remove(targets.begin(), targets.end(), v), targets.end()); +# else + targets.erase(find(targets.begin(), targets.end(), v)); +# endif + }); +#endif + in_edge_list(v, g).clear(); + } + + //static method pick_method(const variable&, const method& m) { return m; } + //static method pick_method(const method& m, const variable&) { return m; } + + template + friend + std::pair::out_edge, bool> + add_edge(const Source& s, const typename vertex_traits::other& t, basic_cgraph& g) { + typename vertex_traits::edge_list& targets = out_edge_list(s, g); + bool is_added = +#ifdef CHECK_PARALLEL_EDGES + !library::found(targets.begin(), targets.end(), t); +#else + true; +#endif + if (is_added) + { + out_edge_list(s, g).push_back(t); + in_edge_list(t, g).push_back(s); + } + + typename vertex_traits::edge_list& predecessors = in_edge_list(s, g); + if (library::found(predecessors.begin(), predecessors.end(), t)) { + g[make_edge(s, t)].is_self_loop = true; + g[make_edge(t, s)].is_self_loop = true; + /* TODO: Determine if this is desired. */ + //g[pick_method(s, t)].is_self_loop = true; + } + + return std::make_pair(make_edge(s, t), is_added); + } + + /* If we want to remove an edge in constant time, we would have to store + * two pointers to edge_lists and two iterators over them. But that really + * just moves the (O(E/V) time) computation to edge construction, affecting + * all other operations. So two vertex handles are preferable for + * implementing edge handles. */ + template + friend + void remove_edge(const Source& s, const typename vertex_traits::other& t, basic_cgraph& g) { + typedef typename vertex_traits::other Target; + typename vertex_traits::edge_list& targets = out_edge_list(s, g); + typename vertex_traits::edge_list& sources = in_edge_list(t, g); +#ifdef ALLOW_PARALLEL_EDGES + targets.erase(remove(targets.begin(), targets.end(), t), targets.end()); + sources.erase(remove(sources.begin(), sources.end(), s), sources.end()); +#else + targets.erase(find(targets.begin(), targets.end(), t)); + sources.erase(find(sources.begin(), sources.end(), s)); +#endif + } + + template + friend + void remove_edge(const Edge& e, basic_cgraph& g, typename boost::enable_if >::type* = 0) { + remove_edge(source(e, g), target(e, g), g); + } + + template + friend + void remove_edge(const Iterator& iterator, basic_cgraph& g) { + remove_edge(*iterator, g); + } + + /***************************************************/ + /* special basic_cgraph features */ + + friend + void add_method(const constraint& c, const method& m, basic_cgraph& g) { + /* TODO: add check for already existing? for method restriction? */ + g[c].its_methods.push_back(m); + g[m].its_constraint = c; + } + + friend + std::pair methods(const constraint& c, const basic_cgraph& g) { + return std::make_pair(g[c].its_methods.begin(), g[c].its_methods.end()); + } + + friend + degree_size_type degree(const constraint& c, const basic_cgraph& g) { + return g[c].its_methods.size(); + } + + /* Non-requirements necessary to support legacy compilers. */ + public: + typedef void edge_property_type; + typedef void vertex_property_type; + typedef void graph_tag; + + }; + + typedef basic_cgraph cgraph; + +} + +#endif + diff --git a/adobe_source_libraries/pm/detail/cgraph.hpp b/adobe_source_libraries/pm/detail/cgraph.hpp new file mode 100644 index 0000000..0893a24 --- /dev/null +++ b/adobe_source_libraries/pm/detail/cgraph.hpp @@ -0,0 +1,143 @@ +#ifndef PM_DETAIL_CGRAPH_HPP +#define PM_DETAIL_CGRAPH_HPP + +#include + +#include +#include + +#include +#include + +namespace pm { + namespace detail { + + /***************************************************/ + + template + struct vertex : public BaseVertex { + public: + typedef std::vector edge_list; + + vertex(const BaseVertex& base_vertex = BaseVertex()) : BaseVertex(base_vertex) {} + + private: + template + friend class cgraph_base; + +#if __GNUC__ >= 4 || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) + public: +#endif + edge_list out_edges, in_edges; + + }; + + /***************************************************/ + + template + class vertex_from_iterator { + private: + VertexIterator begin; + public: + explicit vertex_from_iterator(const VertexIterator& begin) : + begin(begin) {} + size_t operator() (const VertexIterator& i) const { + return i - begin; + } + }; + + template + struct vertex_iterator_generator { + typedef typename library::transform_iterator, VertexIterator> type; + }; + + /***************************************************/ + + template + struct edge : private boost::totally_ordered > { + typedef Source source_type; + typedef Target target_type; + + edge() {} + edge(const Source& source, const Target& target) : + source(source), target(target) {} + edge(const Target& target, const Source& source) : + source(source), target(target) {} + + Source source; + Target target; + + friend + bool operator== (const edge& lhs, const edge& rhs) { + return lhs.source == rhs.source && lhs.target == rhs.target; + } + + friend + bool operator< (const edge& lhs, const edge& rhs) { + return lhs.source < rhs.source || (lhs.source == rhs.source && lhs.target < rhs.target); + } + + }; + + /***************************************************/ + + template + class xput_edge_iterator : public + boost::iterator_adaptor< + /*Derived=*/xput_edge_iterator, + /*Base=*/Iterator, + /*Value=*/Edge, + /*CategoryOrTraversal=*/boost::use_default, + /*Reference=*/Edge + > + { + private: + typedef typename xput_edge_iterator::iterator_adaptor_ super; + + public: + xput_edge_iterator() {} + xput_edge_iterator(const Basis& basis, const Iterator& iterator) : + super(iterator), basis(basis) {} + + private: + friend class boost::iterator_core_access; + + Basis basis; + +#if __GNUC__ >= 4 || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) + public: +#endif + Edge dereference() const { + return Edge(basis, *this->base()); + } + + }; + + /***************************************************/ + + /* FIXME: Remove constructors when initializer lists become available. */ + struct variable { + std::string its_label; + explicit variable(const std::string& label = std::string()) : + its_label(label) {} + }; + + struct method { + std::string its_label; + explicit method(const std::string& label = std::string()) : + its_label(label) {} + }; + + struct constraint { + std::string its_label; + explicit constraint(const std::string& label = std::string()) : + its_label(label) {} + }; + + } +} + +#endif + diff --git a/adobe_source_libraries/pm/detail/empty.hpp b/adobe_source_libraries/pm/detail/empty.hpp new file mode 100644 index 0000000..56bd88e --- /dev/null +++ b/adobe_source_libraries/pm/detail/empty.hpp @@ -0,0 +1,14 @@ +#ifndef PM_DETAIL_EMPTY_HPP +#define PM_DETAIL_EMPTY_HPP + +namespace pm { + namespace detail { + + /* For default template parameters */ + struct empty {}; + + } +} + +#endif + diff --git a/adobe_source_libraries/pm/graph_traits.hpp b/adobe_source_libraries/pm/graph_traits.hpp new file mode 100644 index 0000000..7c56f89 --- /dev/null +++ b/adobe_source_libraries/pm/graph_traits.hpp @@ -0,0 +1,154 @@ +#ifndef PM_GRAPH_TRAITS_HPP +#define PM_GRAPH_TRAITS_HPP + +#include + +#include +#include +#include +#include +#include +//#include + +#include +#include +#include +#include + +#include + +namespace boost { + + template + struct graph_traits > { + private: + typedef pm::basic_cgraph G; + public: + typedef typename boost::variant + vertex_descriptor; + typedef typename boost::variant + edge_descriptor; + typedef boost::directed_tag directed_category; + typedef boost::disallow_parallel_edge_tag edge_parallel_category; + struct traversal_category : + public library::vertex_and_edge_list_graph_tag, + public boost::incidence_graph_tag {}; + + typedef library::composite_iterator + vertex_iterator; + typedef typename G::vertex_list_size_type vertices_size_type; + + typedef library::variant_iterator + out_edge_iterator; + typedef typename G::degree_size_type degree_size_type; + + //typedef boost::detail::adj_list_edge_iterator + typedef pm::detail::edge_iterator + edge_iterator; + typedef typename G::degree_size_type edges_size_type; + + private: + typedef vertex_descriptor vertex_; + typedef edge_descriptor edge_; + + /* TODO: repair boost::variant */ + friend + bool operator!= (const vertex_& lhs, const vertex_& rhs) { + return !(lhs == rhs); + } + + friend + bool operator!= (const edge_& lhs, const edge_& rhs) { + return !(lhs == rhs); + } + + friend + std::pair + vertices(const G& g) { + typename G::variable_iterator bv, ev; + typename G::method_iterator bm, em; + tie(bv, ev) = variables(g); + tie(bm, em) = methods(g); + return std::make_pair(vertex_iterator(bv, ev, bm), vertex_iterator(ev, ev, em)); + } + + friend vertices_size_type num_vertices(const G& g) { return num_variables(g) + num_methods(g); } + + friend + std::pair + edges(const G& g) { + typename G::variable_iterator bv, ev; + tie(bv, ev) = variables(g); + return make_pair(edge_iterator(bv, ev, g), edge_iterator(ev, ev, g)); + } + + friend + edges_size_type + num_edges(const G& g) { + typename G::method_iterator bm, em; + edges_size_type E = 0; + for (tie(bm, em) = variables(g); bm != em; ++bm) + E += degree(*bm, g); + return E; + } + + class source_visitor : public boost::static_visitor { + private: + const G& g; + public: + explicit source_visitor(const G& g) : g(g) {} + + vertex_ operator() (const typename G::output_edge& e) const { return vertex_(source(e, g)); } + vertex_ operator() (const typename G::input_edge& e) const { return vertex_(source(e, g)); } + }; + + friend vertex_ source(const edge_& e, const G& g) { return boost::apply_visitor(source_visitor(g), e); } + + class target_visitor : public boost::static_visitor { + private: + const G& g; + public: + explicit target_visitor(const G& g) : g(g) {} + + vertex_ operator() (const typename G::output_edge& e) const { return vertex_(target(e, g)); } + vertex_ operator() (const typename G::input_edge& e) const { return vertex_(target(e, g)); } + }; + + friend vertex_ target(const edge_& e, const G& g) { return boost::apply_visitor(target_visitor(g), e); } + + typedef std::pair out_edge_range; + + class out_edges_visitor : public boost::static_visitor { + private: + const G& g; + public: + explicit out_edges_visitor(const G& g) : g(g) {} + + out_edge_range operator() (const typename G::variable& v) const { return out_edge_range(out_edges(v, g)); } + out_edge_range operator() (const typename G::method& m) const { return out_edge_range(out_edges(m, g)); } + }; + + friend out_edge_range out_edges(const vertex_& v, const G& g) { return boost::apply_visitor(out_edges_visitor(g), v); } + + class out_degree_visitor : public boost::static_visitor { + private: + const G& g; + public: + explicit out_degree_visitor(const G& g) : g(g) {} + + degree_size_type operator() (const typename G::variable& v) const { return degree_size_type(out_degree(v, g)); } + degree_size_type operator() (const typename G::method& m) const { return degree_size_type(out_degree(m, g)); } + }; + + friend degree_size_type out_degree(const vertex_& v, const G& g) { return boost::apply_visitor(out_degree_visitor(g), v); } + + /* Non-requirements necessary to support legacy compilers. */ + public: + typedef void in_edge_iterator; + + }; + +} + +#endif + diff --git a/adobe_source_libraries/pm/planners/detail/mgraph.hpp b/adobe_source_libraries/pm/planners/detail/mgraph.hpp new file mode 100644 index 0000000..d74f23c --- /dev/null +++ b/adobe_source_libraries/pm/planners/detail/mgraph.hpp @@ -0,0 +1,73 @@ +#ifndef PM_MGRAPH_DETAIL_HPP +#define PM_MGRAPH_DETAIL_HPP + +#include +#include + +#include +#include + +namespace pm { + namespace detail { + + template + class is_vertex_enabled_visitor : public boost::static_visitor { + private: + const Mgraph* mg; + + public: + is_vertex_enabled_visitor() {} + is_vertex_enabled_visitor(const Mgraph& mg) : mg(&mg) {} + + bool operator() (const cgraph::variable& v) const { return true; } + bool operator() (const cgraph::method& m) const { return is_selected(m, *mg); } + }; + + template > + class is_edge_enabled_visitor : public boost::static_visitor { + private: + const Mgraph* mg; + + public: + is_edge_enabled_visitor() {} + is_edge_enabled_visitor(const Mgraph& mg) : mg(&mg) {} + + bool operator() (const cgraph::input_edge& ie) const { return VertexVisitor(*mg)(target(ie, mg->g())) && !mg->g()[ie].is_self_loop; } + bool operator() (const cgraph::output_edge& oe) const { return VertexVisitor(*mg)(source(oe, mg->g())); } + }; + + template > + class is_vertex_enabled { + private: + typedef boost::graph_traits Traits; + + const Mgraph* mg; + + public: + is_vertex_enabled() {} + is_vertex_enabled(const Mgraph& mg) : mg(&mg) {} + + bool operator() (const typename Traits::vertex_descriptor& v) const { return boost::apply_visitor(VertexVisitor(*mg), v); } + + }; + + template > + class is_edge_enabled { + private: + typedef boost::graph_traits Traits; + + const Mgraph* mg; + + public: + is_edge_enabled() {} + is_edge_enabled(const Mgraph& mg) : mg(&mg) {} + + bool operator() (const typename Traits::edge_descriptor& e) const { return boost::apply_visitor(EdgeVisitor(*mg), e); } + + }; + + } +} + +#endif + diff --git a/adobe_source_libraries/pm/planners/mgraph.hpp b/adobe_source_libraries/pm/planners/mgraph.hpp new file mode 100644 index 0000000..9b71b32 --- /dev/null +++ b/adobe_source_libraries/pm/planners/mgraph.hpp @@ -0,0 +1,105 @@ +#ifndef PM_MGRAPH_HPP +#define PM_MGRAPH_HPP + +#include + +#include + +#include +#include +#include + +namespace pm { + + template < + typename Derived, + typename UserVariable = detail::empty, + typename UserMethod = detail::empty, + typename UserConstraint = detail::empty, + typename IsVertexEnabledVisitor = detail::is_vertex_enabled_visitor, + typename IsEdgeEnabledVisitor = detail::is_edge_enabled_visitor + > + class mgraph : + public boost::filtered_graph< + cgraph, + detail::is_edge_enabled, + detail::is_vertex_enabled + > + { + private: + typedef detail::is_edge_enabled IsEdgeEnabled; + typedef detail::is_vertex_enabled IsVertexEnabled; + + const Derived& derived() const { return static_cast(*this); } + + public: + /* An mgraph should not be constructed unless its underlying cgraph is + * complete and immutable. This restriction is imposed because cgraphs + * are constructed piecemeal instead of atomically, leaving them open to + * change. Such post hoc changes should be considered corrupting. */ + mgraph(cgraph& g) : + boost::filtered_graph(g, IsEdgeEnabled(derived()), IsVertexEnabled(derived())), + its_g(g), + its_variables(num_variables(g)), + its_methods(num_methods(g)), + its_constraints(num_constraints(g)) {} + + /* Clients should implement: + * + * template + * void plan(PriorityInputIterator first, PriorityInputIterator last); + * + * ~mgraph(); */ + + const UserVariable& operator[] (const cgraph::variable& v) const { return its_variables[v]; } + const UserMethod& operator[] (const cgraph::method& m) const { return its_methods[m]; } + const UserConstraint& operator[] (const cgraph::constraint& c) const { return its_constraints[c]; } + + const cgraph& g() const { return its_g; } + + protected: + typedef mgraph super; + + public: + UserVariable& operator[] (const cgraph::variable& v) { return its_variables[v]; } + UserMethod& operator[] (const cgraph::method& m) { return its_methods[m]; } + UserConstraint& operator[] (const cgraph::constraint& c) { return its_constraints[c]; } + + cgraph& g() { return its_g; } + mgraph& mg() { return *this; } + + private: + /* Non-copyable. */ + mgraph(const mgraph&); + mgraph& operator= (const mgraph&); + + cgraph& its_g; + + library::fast_property_map its_variables; + library::fast_property_map its_methods; + library::fast_property_map its_constraints; + + /* These functions should not be considered part of the public interface, + * but are left as free functions for consistency. */ + friend cgraph::variable add_variable(mgraph& mg, const cgraph::user_variable& user_v = cgraph::user_variable()) { + cgraph::variable v = add_variable(mg.its_g, user_v); + put(mg.its_variables, v, UserVariable()); + return v; + } + friend cgraph::method add_method(mgraph& mg, const cgraph::user_method& user_m = cgraph::user_method()) { + cgraph::method m = add_method(mg.its_g, user_m); + put(mg.its_methods, m, UserMethod()); + return m; + } + friend cgraph::constraint add_constraint(mgraph& mg, const cgraph::user_constraint& user_c = cgraph::user_constraint()) { + cgraph::constraint c = add_constraint(mg.its_g, user_c); + put(mg.its_constraints, c, UserConstraint()); + return c; + } + + }; + +} + +#endif + diff --git a/adobe_source_libraries/pm/planners/query.hpp b/adobe_source_libraries/pm/planners/query.hpp new file mode 100644 index 0000000..7906abe --- /dev/null +++ b/adobe_source_libraries/pm/planners/query.hpp @@ -0,0 +1,62 @@ +#ifndef PM_MGRAPH_QUERY_HPP +#define PM_MGRAPH_QUERY_HPP + +#include +#include + +#include +#include + +#include + +#include +#include + +namespace pm { + + template + void its_priorities(const Mgraph& mg, OutputIterator i) { + + std::vector priorities; + + const pm::vertex_index_map indices(mg.g()); + + topological_sort( + mg, + library::make_multiplex_output_iterator(back_inserter(priorities)), + boost::vertex_index_map(indices) + ); + + copy( + priorities.rbegin(), + priorities.rend(), + i + ); + + } + + template + void its_plan(const Mgraph& mg, OutputIterator i) { + + std::vector plan; + + const pm::vertex_index_map indices(mg.g()); + + topological_sort( + mg, + library::make_multiplex_output_iterator(back_inserter(plan)), + boost::vertex_index_map(indices) + ); + + copy( + plan.rbegin(), + plan.rend(), + i + ); + + } + +} + +#endif + diff --git a/adobe_source_libraries/pm/planners/quickplan/driver.hpp b/adobe_source_libraries/pm/planners/quickplan/driver.hpp new file mode 100644 index 0000000..d91a1eb --- /dev/null +++ b/adobe_source_libraries/pm/planners/quickplan/driver.hpp @@ -0,0 +1,56 @@ + template + void mgraph::plan(PriorityInputIterator first, PriorityInputIterator last) { + using namespace pm; + //assert(distance(first, last) == num_variables(g())); + + /* Only needs to be done before first run, but checks itself so no penalty + * for repeated calls. */ + prepare(); + + /* Initialize. */ + { + size_t strength = 1; + for (PriorityInputIterator iv = first; iv != last; ++iv, ++strength) { + mg()[mg()[*iv].stay_constraint].strength = strength; + retractable_cns_queue.push_back(mg()[*iv].stay_constraint); + mg()[*iv].num_constraints = mg()[*iv].constraints.size(); + } + + cgraph::constraint_iterator ic, ec; + for (tie(ic, ec) = constraints(g()); ic != ec; ++ic) { + mg()[*ic].mark = its_mark.upstream_mark(); + if (mg()[*ic].selected_method != cgraph::null_method()) { + mg()[mg()[*ic].selected_method].is_selected = false; + mg()[*ic].selected_method = cgraph::null_method(); + } + if (mg()[*ic].strength == 0) + unenforced_cns_queue.push_back(*ic); + } + } + + free_variable_set.clear(); + make_heap(retractable_cns_queue.begin(), retractable_cns_queue.end(), stronger(*this)); + make_heap(unenforced_cns_queue.begin(), unenforced_cns_queue.end(), not2(stronger(*this))); + + while (!unenforced_cns_queue.empty()) { + cn_to_enforce = unenforced_cns_queue.front(); + pop_heap(unenforced_cns_queue.begin(), unenforced_cns_queue.end(), not2(stronger(*this))); + unenforced_cns_queue.erase(--unenforced_cns_queue.end()); + constraint_hierarchy_planner(mg()[cn_to_enforce].strength); + /* If we fail to enforce a required method, */ + if (mg()[cn_to_enforce].selected_method == cgraph::null_method() && mg()[cn_to_enforce].strength == 0) + //throw cyclic_solution_exception(); + return; + } + + /* Could skip over first, but would have to enforce its stay constraint + * directly. */ + for (PriorityInputIterator i = first; i != last; ++i) { + if (mg()[mg()[*i].stay_constraint].selected_method == cgraph::null_method()) + unenforced_cns_queue.push_back(mg()[*i].stay_constraint); + } + + constraint_hierarchy_solver(); + } + + diff --git a/adobe_source_libraries/pm/planners/quickplan/mgraph.hpp b/adobe_source_libraries/pm/planners/quickplan/mgraph.hpp new file mode 100644 index 0000000..80eb640 --- /dev/null +++ b/adobe_source_libraries/pm/planners/quickplan/mgraph.hpp @@ -0,0 +1,187 @@ +#ifndef PM_QUICK_PLAN_MGRAPH_HPP +#define PM_QUICK_PLAN_MGRAPH_HPP + +#include +#include +#include +#include +#include +#include + +#include + +namespace quickplan { + + typedef size_t strength_t; + + class mark { + public: + typedef unsigned int type; + + static const type NULL_MARK = 0; + static const type POTENTIALLY_UNDETERMINED = 1; + static const type INITIAL_UPSTREAM_MARK = 2; + static const type INITIAL_DOWNSTREAM_MARK; + + type upstream_mark() const { return upstream_mark_; } + type new_upstream_mark() { return ++upstream_mark_; } + type downstream_mark() const { return downstream_mark_; } + type new_downstream_mark() { return ++downstream_mark_; } + + mark() : + upstream_mark_(INITIAL_UPSTREAM_MARK), + downstream_mark_(INITIAL_DOWNSTREAM_MARK) {} + + private: + type upstream_mark_, downstream_mark_; + + }; + + struct method { + bool is_selected; + + method() : is_selected(false) {} + }; + + struct constraint { + std::vector variables; + pm::cgraph::method selected_method; + strength_t strength; + mark::type mark; + + constraint() : + selected_method(pm::cgraph::null_method()), + strength(0), + mark(mark::INITIAL_UPSTREAM_MARK) {} + }; + + struct variable { + std::vector constraints; + pm::cgraph::constraint determined_by; + size_t num_constraints; + pm::cgraph::constraint stay_constraint; + mark::type mark; + + variable() : + determined_by(pm::cgraph::null_constraint()), + num_constraints(0), + stay_constraint(pm::cgraph::null_constraint()) {} + }; + + class mgraph : public pm::mgraph { + public: + mgraph(pm::cgraph&); + + template + void plan(PriorityInputIterator first, PriorityInputIterator last); + + private: + typedef pm::mgraph super; + + void prepare(); + + /* These functions should not be considered part of the public interface, + * but are left as free functions for consistency. */ + friend void add_stay_constraint(const pm::cgraph::variable& v, mgraph& mg) { + pm::cgraph& g = mg.g(); + pm::cgraph::method m = add_method(mg); + g[m].its_label = "__mt_stay_" + g[v].its_label; + g[m].is_self_loop = true; + add_edge(v, m, g); + add_edge(m, v, g); + pm::cgraph::constraint c = add_constraint(mg); + g[c].its_label = "__cn_stay_" + g[v].its_label; + add_method(c, m, g); + mg[v].stay_constraint = c; + } + + friend pm::cgraph::variable add_variable(mgraph& mg, const pm::cgraph::user_variable& user_v = pm::cgraph::user_variable()) { + pm::cgraph::variable v = add_variable(static_cast(mg), user_v); + add_stay_constraint(v, mg); + return v; + } + + pm::cgraph::constraint cn_to_enforce; + /* Use as heaps. */ + /* constraint_hierarchy_planner: delete_min() */ + std::vector retractable_cns_queue; + /* constraint_hierarchy_solver: delete_max() */ + std::vector unenforced_cns_queue; + std::vector free_variable_set; + std::set potential_undetermined_vars; + strength_t strongest_retracted_strength; + mark its_mark; + std::stack > undo_stack; + + void collect_upstream_constraints(const pm::cgraph::constraint&); + void collect_downstream_unenforced_constraints(const pm::cgraph::variable&); + void collect_unenforced_constraints(const std::vector&, const pm::cgraph::constraint&); + void multi_output_planner(); + void constraint_hierarchy_planner(strength_t); + void constraint_hierarchy_solver(); + + /* Helpers */ + void eliminate_constraint(const pm::cgraph::constraint&, const pm::cgraph::method&); + void undo(); + }; + + class stronger : public std::binary_function { + private: + const mgraph& mg; + public: + explicit stronger(const mgraph& mg) : mg(mg) {} + bool operator() (const pm::cgraph::constraint& lhs, const pm::cgraph::constraint& rhs) const { + return mg[lhs].strength < mg[rhs].strength; + } + }; + +#include + + /* + class is_vertex_enabled_visitor : public detail::is_vertex_enabled_visitor { + private: + typedef detail::is_vertex_enabled_visitor super; + + public: + is_vertex_enabled_visitor() {} + is_vertex_enabled_visitor(const mgraph& mg) : super(mg) {} + + bool operator() (const cgraph::method& m) const { + return super::operator()(m) && !is_stay_constraint(mg->g()[m].its_constraint, *mg); + } + }; + */ + + inline bool is_stay_constraint(const pm::cgraph::constraint& c, const mgraph& mg) { + /* A strength of "infinity" means the constraint is disabled. */ + return mg[c].strength != 0 && mg[c].strength != std::numeric_limits::max(); + } + + inline bool is_selected(const pm::cgraph::method& m, const mgraph& mg) { + return mg[m].is_selected && !is_stay_constraint(mg.g()[m].its_constraint, mg); + } + + inline bool is_contributing(const pm::cgraph::variable& v, const mgraph& mg) { + return mg[v].determined_by == mg[v].stay_constraint; + //return mg[mg[v].stay_constraint].selected_method != pm::cgraph::null_method(); + //return is_stay_constraint(mv[v].determined_by); + } + + inline bool is_derived(const pm::cgraph::variable& v, const mgraph& mg) { + return !is_contributing(v, mg); + } + + /* a variable that is not an input to any selected method is a leaf */ + inline bool is_leaf(const pm::cgraph::variable& v, const mgraph& mg) { + pm::cgraph::outgoing_method_iterator iom, eom; + tie(iom, eom) = outgoing_vertices(v, mg.g()); + //return none(iom, eom, [&mg] (const pm::cgraph::method& m) { return is_selected(m, mg); }); + for (tie(iom, eom) = outgoing_vertices(v, mg.g()); iom != eom; ++iom) + if (is_selected(*iom, mg)) return false; + return true; + } + +} + +#endif + diff --git a/adobe_source_libraries/pm/vertex_index_map.hpp b/adobe_source_libraries/pm/vertex_index_map.hpp new file mode 100644 index 0000000..184a3f2 --- /dev/null +++ b/adobe_source_libraries/pm/vertex_index_map.hpp @@ -0,0 +1,49 @@ +#ifndef PM_CGRAPH_VERTEX_INDEX_MAP_HPP +#define PM_CGRAPH_VERTEX_INDEX_MAP_HPP + +#include +#include +#include + +#include + +namespace pm { + + namespace detail { + + typedef unsigned int vertex_index; + + class index_visitor : public boost::static_visitor { + private: + const cgraph& g; + + public: + explicit index_visitor(const cgraph& g) : g(g) {} + + vertex_index operator() (const cgraph::variable& v) const { return v.base; } + vertex_index operator() (const cgraph::method& m) const { return num_variables(g) + m.base; } + + }; + + } + + class vertex_index_map { + public: + typedef detail::index_visitor::result_type value_type; + typedef value_type reference; + typedef boost::graph_traits::vertex_descriptor key_type; + typedef boost::readable_property_map_tag category; + + explicit vertex_index_map(const cgraph& g) : visitor(g) {} + + private: + detail::index_visitor visitor; + + friend reference get(const vertex_index_map& vim, const key_type& k) { return apply_visitor(vim.visitor, k); } + + }; + +} + +#endif + diff --git a/adobe_source_libraries/source/adam.cpp b/adobe_source_libraries/source/adam.cpp index 96ce57f..d2fdf11 100644 --- a/adobe_source_libraries/source/adam.cpp +++ b/adobe_source_libraries/source/adam.cpp @@ -4,21 +4,26 @@ or a copy at http://stlab.adobe.com/licenses.html) */ -/**************************************************************************************************/ +/*************************************************************************************************/ #include #include +#include +#include +#include +#include #include -#include #include +#include +#include +#include +#include +#include #include #include -#include -#include -#include #include #include #include @@ -30,504 +35,386 @@ #include #include -#ifndef NDEBUG +#include +#include +#include -#include +#include -#endif // NDEBUG - -/**************************************************************************************************/ - -namespace anonymous_adam_cpp { // can't instantiate templates on types from real anonymous - -/**************************************************************************************************/ - -#ifndef NDEBUG - -struct check_reentrancy -{ - check_reentrancy(bool& x) : check_m(x) - { assert(!x && "FATAL (sparent) : Function Not Reentrant."); check_m = true; } - ~check_reentrancy() { check_m = false; } - - bool& check_m; -}; - -#endif // NDEBUG - -/**************************************************************************************************/ - -/* - REVISIT (sparent) : Move to utility? This is generally useful to provide a copy for - non-copyable types such as boost::signal<>. -*/ - -template // T models default constructable -struct empty_copy : T -{ - empty_copy() : T() { } - empty_copy(const empty_copy&) : T() { } - empty_copy& operator=(const empty_copy&) { return *this; } -}; +#ifdef ADOBE_STD_SERIALIZATION +//#define DEBUG +#endif -/**************************************************************************************************/ +#ifdef DEBUG +#include +#endif +#include +/*************************************************************************************************/ typedef adobe::sheet_t sheet_t; -// This currently establishes an upperbound on the number of cells in the sheet at 1K. -typedef std::bitset<1024> cell_bits_t; -typedef int priority_t; - -struct compare_contributing_t; - -enum access_specifier_t -{ - access_input, - access_interface_input, - access_interface_output, - access_output, - access_logic, - access_constant, - access_invariant -}; - -/**************************************************************************************************/ +/*************************************************************************************************/ /* - REVISIT (sparent) : A thought - this could be packaged as a general template function for - converting exceptions on function object calls. -*/ -/* - REVIST (sparent) : Some version of MSVC didn't like function level try blocks. Need to test. -*/ +create sheet_t +add cells +prepare (finish up initialization using global information) + You can't add cells after the sheet is prepared. + +### Question: How can these three steps be grouped into an "editor" object which +then returns a sheet? -void evaluate(adobe::virtual_machine_t& machine, const adobe::line_position_t& position, - const adobe::array_t& expression) -#ifdef BOOST_MSVC -{ -#endif -try +struct sheet_t::editor_t { - machine.evaluate(expression); -} -catch (const std::exception& error) -{ - throw adobe::stream_error_t(error, position); -} -#ifdef BOOST_MSVC -} -#endif - -/**************************************************************************************************/ + void add_xxx(); + sheet_t finalize(); // returns a prepared sheet +}; -struct scope_count : boost::noncopyable -{ - scope_count(std::size_t& x) : value_m(x) { ++value_m; } - ~scope_count() { --value_m; } +#### Is this necessary - in the future maybe we want to allow editing of sheets? +#### We could also make prepare be automatic? Would be fragile but would contain the +#### fragility to the class - could simply assert !prepared_m on add - and call prepare() +on monitor, set, and udate.... going with that. - private: - std::size_t& value_m; -}; +set initial values if any +hook monitor functions. -template -struct scope_value_t : boost::noncopyable -{ - scope_value_t(T& x, const T& v) : value_m(x), store_m(x) { x = v; } - ~scope_value_t() { value_m = store_m; } +current_mark() - get the current priority to start tracking contributing from. +set and update... - private: - T& value_m; - T store_m; -}; +Allow you to get "contributing_since_mark()" + - this allows multiple dialogs on the same sheet to do the correct thing. -/**************************************************************************************************/ +First update should force all monitors. +Any monitors added after first update are called upon add. -} // namespace anonymous_adam_cpp -using namespace anonymous_adam_cpp; +*/ -/**************************************************************************************************/ +/*************************************************************************************************/ namespace adobe { -/**************************************************************************************************/ +/*************************************************************************************************/ -class sheet_t::implementation_t : boost::noncopyable -{ -public: +class sheet_t::implementation_t : boost::noncopyable { + public: typedef sheet_t::connection_t connection_t; explicit implementation_t(virtual_machine_t& machine); - any_regular_t inspect(const array_t& expression); - - void set(name_t, const any_regular_t&); // input cell. - void touch(const name_t*, const name_t*); // range of input cells. - - any_regular_t get(name_t); + /* construct *************************************************************/ void add_input (name_t, const line_position_t&, const array_t& initializer); void add_output (name_t, const line_position_t&, const array_t& expression); void add_constant (name_t, const line_position_t&, const array_t& initializer); void add_logic (name_t, const line_position_t&, const array_t& expression); void add_invariant (name_t, const line_position_t&, const array_t& expression); - void add_interface (name_t, bool linked, const line_position_t&, const array_t& initializer, - const line_position_t&, const array_t& expression); + void add_interface (name_t, bool linked, + const line_position_t&, const array_t& initializer, + const line_position_t&, const array_t& expression); void add_relation (const line_position_t&, const array_t& conditional, - const relation_t* first, const relation_t* last); - connection_t monitor_value(name_t, const monitor_value_t&); //output only + const relation_t* first, const relation_t* last); - //input only - connection_t monitor_enabled(name_t, const name_t* first, const name_t* last, - const monitor_enabled_t&); + //void reinitialize(const dictionary_t& dictionary); + void reinitialize(); + /* monitor ***************************************************************/ - connection_t monitor_contributing(name_t, const dictionary_t&, - const monitor_contributing_t&); + connection_t monitor_value (name_t, const monitor_value_t&); /* Output only. */ + connection_t monitor_enabled (name_t, const name_t* first, const name_t* last, const monitor_enabled_t&) { return connection_t(); } + connection_t monitor_contributing (name_t, const dictionary_t&, const monitor_contributing_t&) { return connection_t(); } + connection_t monitor_invariant_dependent(name_t, const monitor_invariant_t&) { return connection_t(); } - #if 0 - connection_t monitor_invariant_contributing(name_t invariant, const monitor_invariant_t&); -// REVISIT (sparent) : UNIMPLEMENTED - #endif - - connection_t monitor_invariant_dependent(name_t invariant, const monitor_invariant_t&); + /* query *****************************************************************/ + any_regular_t inspect(const array_t& expression); + any_regular_t get(name_t); + + /* More like is_input/output? */ bool has_input(name_t) const; bool has_output(name_t) const; - void update(); - - void reinitialize(); + /* All contributing values that have changed since mark. */ + dictionary_t contributing(const dictionary_t&) const; + dictionary_t contributing_to_cell(name_t) const { return dictionary_t(); } + /* workflow : set ********************************************************/ + + void set(name_t, const any_regular_t&); void set(const dictionary_t& dictionary); -// set input cells to corresponding values in dictionary. - - dictionary_t contributing(const dictionary_t&) const; -// all contributing values that have changed since mark - dictionary_t contributing_to_cell(name_t) const; + + /* Touch the range of input cells, keeping their relative priority. */ + void touch(const name_t*, const name_t*) {} -private: - struct relation_cell_t; + /* workflow : update *****************************************************/ - typedef vector relation_index_t; - typedef std::vector relation_set_t; + /* Plan, then evaluate. */ + void update(); - struct relation_cell_t - { - relation_cell_t(const line_position_t& position, - const array_t& conditional, const relation_t* first, - const relation_t* last) : - resolved_m(false), - position_m(position), - conditional_m(conditional), - terms_m(first, last) - { } - - bool resolved_m; - - line_position_t position_m; - array_t conditional_m; - relation_set_t terms_m; - - // REVISIT (sparent) : There should be a function object to set members - void clear_resolved() - { - resolved_m = false; - } + private: + + /* types *****************************************************************/ + + enum cell_kind_t { + CONSTANT_CELL, + INPUT_CELL, + INTERFACE_CELL, + OUTPUT_CELL, + LOGIC_CELL, + INVARIANT_CELL + }; + + typedef std::list priorities_t; + + struct variable { + + typedef boost::signal value_monitor_slot_t; + + cell_kind_t its_kind; + name_t its_name; + any_regular_t its_value; + array_t its_initializer; + priorities_t::iterator its_priority; + /* Needs to stay true at all times outside of evaluation phase. */ + bool is_cached; + bool is_poisoned; + std::vector its_contributors; + value_monitor_slot_t its_value_monitors; + + variable(const cell_kind_t& kind, + const name_t& name, + const any_regular_t& initial_value, + const array_t& initializer, + const priorities_t::iterator& initial_priority) : + its_kind(kind), + its_name(name), + its_value(initial_value), + its_initializer(initializer), + its_priority(initial_priority), + is_cached(true) {} + + variable(const variable& copy) : + its_kind (copy.its_kind), + its_name (copy.its_name), + its_value (copy.its_value), + its_initializer(copy.its_initializer), + its_priority (copy.its_priority), + is_cached (copy.is_cached), + is_poisoned (copy.is_poisoned) {} + + variable& operator=(const variable& rhs) { + its_kind = rhs.its_kind; + its_value = rhs.its_value; + its_initializer = rhs.its_initializer; + its_priority = rhs.its_priority; + is_cached = rhs.is_cached; + is_poisoned = rhs.is_poisoned; + return *this; + } + + }; + + struct method { + array_t its_expression; + + method(const array_t& expression) : + its_expression(expression) {} + }; - struct cell_t + struct constraint { + array_t its_condition; + + constraint(const array_t& condition) : + its_condition(condition) {} + + }; + + /* members ***************************************************************/ + + adobe::virtual_machine_t& its_machine; + /* WARNING: ORDER OF INITIALIZATION IMPORTANT! + * its_g must come before its_mg */ + pm::cgraph its_g; + quickplan::mgraph its_mg; + priorities_t its_priorities; + std::map its_symtab; + library::fast_property_map its_variables; + library::fast_property_map its_methods; + library::fast_property_map its_constraints; + + /* helpers ***************************************************************/ + + pm::cgraph::variable add_variable( + cell_kind_t kind, + name_t name, + const any_regular_t& initial_value = any_regular_t(), + const array_t& initializer = array_t()) { - typedef boost::function calculator_t; - - typedef empty_copy > monitor_invariant_list_t; - typedef empty_copy > monitor_value_list_t; - typedef empty_copy > monitor_contributing_list_t; - - cell_t(access_specifier_t specifier, name_t, const calculator_t& calculator, - std::size_t cell_set_pos, cell_t* = 0); // output - cell_t(access_specifier_t specifier, name_t, any_regular_t, - std::size_t cell_set_pos); // constant - - cell_t(name_t, any_regular_t, std::size_t cell_set_pos); // input cell - cell_t(name_t, bool linked, const calculator_t& init_expression, - std::size_t cell_set_pos); // interface cell (input) - - #if 0 - // compiler generated. - cell_t(const cell_t& x); - cell_t& operator=(const cell_t& x); - #endif - - access_specifier_t specifier_m; - name_t name_m; - - calculator_t calculator_m; - - bool linked_m; - bool invariant_m; - - priority_t priority_m; // For linked input cells only - zero otherwise - - bool resolved_m; // For interface cells only - false if cell hasn't been flowed - bool evaluated_m; // true if cell has been calculated (or has no calculator). - - std::size_t relation_count_m; - std::size_t initial_relation_count_m; - - bool dirty_m; // denotes change state_m value - - any_regular_t state_m; - cell_bits_t contributing_m; - cell_bits_t init_contributing_m; - - std::size_t cell_set_pos_m; // self index in sheet_t::cell_set_m - - calculator_t term_m; - - // For output half of interface cells this points to corresponding input half. NULL otherwise. - cell_t* interface_input_m; - - // For output half of interface cells this points to any possible connected relations. - relation_index_t relation_index_m; - - monitor_value_list_t monitor_value_m; - monitor_contributing_list_t monitor_contributing_m; - monitor_invariant_list_t monitor_invariant_m; - - void calculate(); - - void clear_dirty() - { - dirty_m = false; - relation_count_m = initial_relation_count_m; - term_m.clear(); - evaluated_m = specifier_m == access_input || specifier_m == access_constant /* || calculator_m.empty() */; - - /* - REVISIT (sparent) : What exactly is the distinction between evaluated and resolved. - */ - - resolved_m = evaluated_m; + DEBUG_PRINT("add_variable(" << name << ")"); + //DEBUG_PRINT_VARIABLE(initial_value); + /* Initialize variable. */ + pm::cgraph::variable v = quickplan::add_variable(its_mg, + pm::cgraph::user_variable(name.c_str())); + its_symtab.insert(std::make_pair(name, v)); + its_priorities.push_back(v); + put(its_variables, v, + variable(kind, name, initial_value, initializer, --its_priorities.end())); + + /* Initialize stay-constraint. */ + pm::cgraph::constraint c = its_mg[v].stay_constraint; + /* Construct a void expression for the conditional. */ + put(its_constraints, c, constraint(array_t())); + + /* Initialize stay-method. */ + /* A stay constraint has exactly one method. */ + //pm::cgraph::csm_iterator imt, emt; + //tie(imt, emt) = methods(c, its_g); + //assert(distance(imt, emt) == 1); + //pm::cgraph::method m = *imt; + pm::cgraph::method m = *methods(c, its_g).first; + /* Construct a void expression for the stay-method. */ + put(its_methods, m, method(array_t())); + + return v; + } + + pm::cgraph::variable add_variable( + cell_kind_t kind, + name_t name, + const array_t& initializer) + { + //DEBUG_PRINT("add_variable(" << name << ")"); + //DEBUG_PRINT_VARIABLE(initializer); + any_regular_t initial_value; + if (!initializer.empty()) + initial_value = inspect(initializer); + return add_variable(kind, name, initial_value, initializer); + } + + pm::cgraph::method add_method( + const pm::cgraph::constraint& c, + const array_t& expression, + const name_t& output) + { + /* Initialize method. */ + pm::cgraph::method m = pm::add_method(its_mg, + pm::cgraph::user_method(std::string("__mt_assign_") + output.c_str())); + put(its_methods, m, method(expression)); + /* Add to constraint. */ + pm::add_method(c, m, its_g); + /* Connect to output. */ + pm::cgraph::variable v = its_symtab.find(output)->second; + pm::add_edge(m, v, its_g); + /* Connect to inputs. */ + /* Expressions are represented by a stack language. + * Each term is either a name_t or some numerical type (?). + * The ".variable" operator indicates that the operand on top of the + * stack is a variable. */ + //DEBUG_PRINT(""); + std::set inputs; + for (array_t::const_iterator i = expression.begin(); i != expression.end(); ++i) { + //DEBUG_PRINT(" " << *i << " :: " << i->type_info()); + name_t term; + if (i->cast(term) && term.c_str() == std::string(".variable")) { + name_t input; + array_t::const_iterator j = i; --j; + bool is_variable_name = j->cast(input); + assert(is_variable_name && "Expected name of variable."); + inputs.insert(its_symtab.find(input)->second); + //DEBUG_PRINT(" INPUT: " << input); } - - /* - REVISIT (sparent) : A member wise implementation of swap would be better - but I'm - going to swap this way for expendiancy since cell_t will likely change a lot when - I get around to rewriting Adam. - */ - // friend void swap(cell_t& x, cell_t&y) { std::swap(x, y); } - }; - - friend struct cell_t; - friend struct compare_contributing_t; - - any_regular_t calculate_expression(const line_position_t& position, - const array_t& expression); - - dictionary_t contributing_set(const dictionary_t&, const cell_bits_t&) const; - - void initialize_one(cell_t& cell); + } + //DEBUG_PRINT(""); + //std::for_each(inputs.begin(), inputs.end(), [&] (const pm::cgraph::variable& v) { add_edge(v, m, its_g); }); + std::for_each(inputs.begin(), inputs.end(), boost::bind( + static_cast (*) (const pm::cgraph::variable&, const pm::cgraph::method&, pm::cgraph&)>(pm::add_edge), + _1, + m, + boost::ref(its_g))); - void enabled_filter(const cell_bits_t& touch_set, - std::size_t contributing_index_pos, - monitor_enabled_t monitor, - const cell_bits_t& new_priority_accessed_bits, - const cell_bits_t& new_active_bits); + return m; + } -// std::size_t cell_set_to_contributing(std::size_t cell_set_pos) const; - - priority_t name_to_priority(name_t name) const; - void flow(cell_bits_t& priority_accessed); + pm::cgraph::method add_solo_method( + const array_t& expression, + const name_t& output) + { + pm::cgraph::constraint c = pm::add_constraint(its_mg, + pm::cgraph::user_constraint(std::string("__cn_assign_") + output.c_str())); + put(its_constraints, c, constraint(array_t())); + return add_method(c, expression, output); + } -/* - NOTE (sparent) : cell_t contains boost::signal<> which is not copyable. The cells support - limited copying until they have monitors attached - this allows them to be placed into a - container prior to any connections being made. A deque is used rather than a vector because it - does not reallocate when it grows. -*/ - - typedef std::deque cell_set_t; - typedef std::deque relation_cell_set_t; - - typedef std::vector get_stack_t; - typedef std::vector index_vector_t; - - typedef hash_index< cell_t, - boost::hash, - equal_to, - mem_data_t > index_t; - - index_t name_index_m; - index_t setable_index_m; // input of interface or input; - index_t input_index_m; - index_t output_index_m; - - index_vector_t invariant_index_m; + bool is_cell_of_kind(const pm::cgraph::variable& v, cell_kind_t kind) const { + return its_variables[v].its_kind == kind; + } - priority_t priority_high_m; - priority_t priority_low_m; + bool is_input(const pm::cgraph::variable& v) const { + return is_cell_of_kind(v, INPUT_CELL) || + is_cell_of_kind(v, INTERFACE_CELL); + } - cell_bits_t conditional_indirect_contributing_m; - - virtual_machine_t& machine_m; - get_stack_t get_stack_m; - std::size_t get_count_m; + bool is_output(const pm::cgraph::variable& v) const { + return is_cell_of_kind(v, OUTPUT_CELL) || + is_cell_of_kind(v, INTERFACE_CELL); + } - cell_bits_t init_dirty_m; - cell_bits_t priority_accessed_m; - cell_bits_t value_accessed_m; - cell_bits_t active_m; + void set(const pm::cgraph::variable& v, const any_regular_t& value) { + DEBUG_PRINT("set(" << its_g[v].its_label << ", " << value << ")"); + assert(is_input(v) && "Attempt to set non-input value."); + variable& its_v = its_variables[v]; + its_v.its_value = value; + its_v.its_value_monitors(value); + touch(v); + } - typedef boost::signal - monitor_enabled_list_t; - monitor_enabled_list_t monitor_enabled_m; - - cell_bits_t accumulate_contributing_m; + void touch(const pm::cgraph::variable v) { + priorities_t::iterator& i = its_variables[v].its_priority; + its_priorities.erase(i); + its_priorities.push_front(v); + i = its_priorities.begin(); + } - bool has_output_m; // true if there are any output cells. - bool initialize_mode_m; // true during reinitialize call. - - // Actual cell storage - every thing else is index or state. - - cell_set_t cell_set_m; - relation_cell_set_t relation_cell_set_m; - -#ifndef NEBUG - bool updated_m; - bool check_update_reentrancy_m; -#endif -}; + any_regular_t get(const pm::cgraph::variable&); -/**************************************************************************************************/ + template + void evaluate(InputIterator first, InputIterator last); -void -sheet_t::implementation_t::enabled_filter(const cell_bits_t& touch_set, - std::size_t contributing_index_pos, - monitor_enabled_t monitor, - const cell_bits_t& new_priority_accessed_bits, - const cell_bits_t& - new_active_bits) -{ - cell_bits_t new_priority_accessed_touch = new_priority_accessed_bits & touch_set; - cell_bits_t old_priority_accessed_touch = priority_accessed_m & touch_set; - bool unchanged_priority_accessed_touch = - (new_priority_accessed_touch ^ old_priority_accessed_touch).none(); - - cell_t& cell = cell_set_m[contributing_index_pos]; - - bool active(active_m.test(contributing_index_pos)); - bool new_active(new_active_bits.test(contributing_index_pos)); - - if (unchanged_priority_accessed_touch && (active == new_active)) return; - - monitor(new_active || - (value_accessed_m.test(cell.cell_set_pos_m) && new_priority_accessed_touch.any())); -} + /* A variable whose value was used in the last evaluation phase is relevant. */ + friend bool is_relevant(const pm::cgraph::variable& v, const implementation_t& this_) { + return this_.its_variables[v].is_cached; + } -/**************************************************************************************************/ + /* A variable should be disabled if it is contributing but not relevant. */ + friend bool is_enabled(const pm::cgraph::variable& v, const implementation_t& this_) { + return is_relevant(v, this_) || !is_contributing(v, this_.its_mg); + } -/* - REVISIT (sparent) : Need to figure out what happens if this is called on an input cell during - initialization (before it is resolved). -*/ + /* FIXME: Would love to put this closer to its actual use site. */ + struct contributing_visitor : public boost::dfs_visitor<> { -void sheet_t::implementation_t::cell_t::calculate() -{ - if (evaluated_m) return; - // REVISIT (sparent) : review resolved_m resolved issue. - //assert(resolved_m && "Cell in an invalid state?"); - - // This is to handle conditionals which refer to cells involved in relate clauses - if (relation_count_m) throw std::logic_error(make_string("cell ", name_m.c_str(), - " is attached to an unresolved relate clause.")); - - any_regular_t result = term_m.empty() ? calculator_m() : term_m(); - - dirty_m = (result != state_m); - state_m = move(result); - evaluated_m = true; -} + struct contributing_visitor_visitor : public boost::static_visitor<> { + pm::cgraph::variable contributor; + sheet_t::implementation_t& this_; + contributing_visitor_visitor(const pm::cgraph::variable& contributor, sheet_t::implementation_t& this_) : + contributor(contributor), this_(this_) {} + void operator() (const pm::cgraph::variable& v) { + this_.its_variables[v].its_contributors.push_back(contributor); + } + void operator() (const pm::cgraph::method&) {} + }; -/**************************************************************************************************/ - -sheet_t::implementation_t::cell_t::cell_t(name_t name, any_regular_t x, - std::size_t cell_set_pos) : - specifier_m(access_input), - name_m(name), - invariant_m(false), - priority_m(0), - resolved_m(true), - evaluated_m(true), - relation_count_m(0), - initial_relation_count_m(0), - dirty_m(false), - state_m(move(x)), - cell_set_pos_m(cell_set_pos), - interface_input_m(0) -{ - init_contributing_m.set(cell_set_pos); -} + contributing_visitor_visitor cvv; + contributing_visitor(const pm::cgraph::variable& contributor, sheet_t::implementation_t& this_) : + cvv(contributor, this_) {} + void discover_vertex(const boost::graph_traits::vertex_descriptor& v, const quickplan::mgraph& mg) { boost::apply_visitor(cvv, v); } + }; -/**************************************************************************************************/ - -sheet_t::implementation_t::cell_t::cell_t(name_t name, bool linked, const calculator_t& initializer, - std::size_t cell_set_pos) : - specifier_m(access_interface_input), - name_m(name), - calculator_m(initializer), - linked_m(linked), - invariant_m(false), - priority_m(0), - resolved_m(true), - evaluated_m(true), - relation_count_m(0), - initial_relation_count_m(0), - cell_set_pos_m(cell_set_pos), - interface_input_m(0) -{ - contributing_m.set(cell_set_pos); -} - -/**************************************************************************************************/ - -sheet_t::implementation_t::cell_t::cell_t(access_specifier_t specifier, name_t name, - const calculator_t& calculator, - std::size_t cell_set_pos, cell_t* input) : - specifier_m(specifier), - name_m(name), - calculator_m(calculator), - linked_m(false), - invariant_m(false), - priority_m(0), - resolved_m(false), - evaluated_m(calculator_m.empty()), - relation_count_m(0), - initial_relation_count_m(0), - cell_set_pos_m(cell_set_pos), - interface_input_m(input) -{ } - -/**************************************************************************************************/ - -sheet_t::implementation_t::cell_t::cell_t(access_specifier_t specifier, name_t name, - any_regular_t x, std::size_t cell_set_pos) : - specifier_m(specifier), - name_m(name), - linked_m(false), - invariant_m(false), - priority_m(0), - resolved_m(true), - evaluated_m(true), - relation_count_m(0), - initial_relation_count_m(0), - state_m(move(x)), - cell_set_pos_m(cell_set_pos), - interface_input_m(0) -{ } - -/**************************************************************************************************/ +}; + +/*************************************************************************************************/ sheet_t::sheet_t() : object_m(new implementation_t(machine_m)) @@ -539,6 +426,9 @@ sheet_t::~sheet_t() any_regular_t sheet_t::inspect(const array_t& expression) { return object_m->inspect(expression); } +any_regular_t sheet_t::get(name_t name) +{ return object_m->get(name); } + void sheet_t::set(name_t input, const any_regular_t& value) { object_m->set(input, value); } @@ -562,9 +452,9 @@ void sheet_t::add_invariant(name_t invariant, const line_position_t& position, const array_t& expression) { object_m->add_invariant(invariant, position, expression); } -void sheet_t::add_interface(name_t name, bool linked, const line_position_t& position1, - const array_t& initializer, const line_position_t& position2, - const array_t& expression) +void sheet_t::add_interface(name_t name, bool linked, + const line_position_t& position1, const array_t& initializer, + const line_position_t& position2, const array_t& expression) { object_m->add_interface(name, linked, position1, initializer, position2, expression); } void sheet_t::add_relation(const line_position_t& position, const array_t& conditional, @@ -602,15 +492,15 @@ bool sheet_t::has_output(name_t name) const void sheet_t::update() { object_m->update(); } +//void sheet_t::reinitialize(const dictionary_t& dictionary) +//{ object_m->reinitialize(dictionary); } + void sheet_t::reinitialize() { object_m->reinitialize(); } void sheet_t::set(const dictionary_t& dictionary) { object_m->set(dictionary); } -any_regular_t sheet_t::get(name_t cell) -{ return object_m->get(cell); } - dictionary_t sheet_t::contributing(const dictionary_t& mark) const { return object_m->contributing(mark); } @@ -620,877 +510,312 @@ dictionary_t sheet_t::contributing() const dictionary_t sheet_t::contributing_to_cell(name_t x) const { return object_m->contributing_to_cell(x); } -/**************************************************************************************************/ -sheet_t::implementation_t::implementation_t(virtual_machine_t& machine) : - name_index_m(boost::hash(), equal_to(), &cell_t::name_m), - input_index_m(boost::hash(), equal_to(), &cell_t::name_m), - output_index_m(boost::hash(), equal_to(), &cell_t::name_m), - priority_high_m(0), - priority_low_m(0), - machine_m(machine), - get_count_m(0), - has_output_m(false), - initialize_mode_m(false) -#ifndef NDEBUG - , - updated_m(false), - check_update_reentrancy_m(false) -#endif -{ } - -/**************************************************************************************************/ - -any_regular_t sheet_t::implementation_t::inspect(const array_t& expression) -{ - machine_m.evaluate(expression); - - any_regular_t result = move(machine_m.back()); - machine_m.pop_back(); - - return result; -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::set(name_t n, const any_regular_t& v) -{ - index_t::iterator iter(input_index_m.find(n)); - if (iter == input_index_m.end()) - { - throw std::logic_error(make_string("input cell ", n.c_str(), " does not exist.")); - } - ++priority_high_m; - iter->state_m = v; - iter->priority_m = priority_high_m; - - // Leave contributing untouched. - - if (iter->specifier_m == access_input) init_dirty_m.set(iter->cell_set_pos_m); -} - -/**************************************************************************************************/ +/*************************************************************************************************/ -void sheet_t::implementation_t::touch(const name_t* first, const name_t* last) -{ - // REVISIT (sparent) : This should be constrained to interface cells only. - // REVISIT (sparent) : This logic is similar to the logic in flow and should be the same. +/* construct *************************************************************/ - // build an index of the cells to touch sorted by current priority. - - typedef table_index priority_index_t; - - priority_index_t index(&cell_t::priority_m); - - // REVISIT (sparent) : This loop is transform - - while (first != last) - { - index_t::iterator iter(input_index_m.find(*first)); - if (iter == input_index_m.end()) - { - throw std::logic_error(make_string("input cell ", first->c_str(), " does not exist.")); - } - - index.push_back(*iter); - ++first; - } - - index.sort(); - - // Touch the cells - keeping their relative priority - - for (priority_index_t::iterator f(index.begin()), l(index.end()); f != l; ++f) - { - ++priority_high_m; - f->priority_m = priority_high_m; - } -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::add_input(name_t name, const line_position_t& position, - const array_t& initializer) -{ - scope_value_t scope(initialize_mode_m, true); - - any_regular_t initial_value; - - if (initializer.size()) initial_value = calculate_expression(position, initializer); - - cell_set_m.push_back(cell_t(name, move(initial_value), cell_set_m.size())); - // REVISIT (sparent) : Non-transactional on failure. - input_index_m.insert(cell_set_m.back()); -} - - -/**************************************************************************************************/ -void sheet_t::implementation_t::add_output(name_t output, const line_position_t& position, - const array_t& expression) -{ - // REVISIT (sparent) : Non-transactional on failure. - cell_set_m.push_back(cell_t(access_output, output, - boost::bind(&implementation_t::calculate_expression, - boost::ref(*this), position, expression), - cell_set_m.size())); - - output_index_m.insert(cell_set_m.back()); - - if (!name_index_m.insert(cell_set_m.back()).second) { - throw stream_error_t(make_string("cell named '", output.c_str(), "'already exists."), position); - } - - has_output_m = true; -} - - -/**************************************************************************************************/ -// REVISIT (sparent) : Hacked glom of input/output pair. - -void sheet_t::implementation_t::add_interface(name_t name, bool linked, - const line_position_t& position1, - const array_t& initializer_expression, - const line_position_t& position2, - const array_t& expression) -{ - scope_value_t scope(initialize_mode_m, true); - - if (initializer_expression.size()) { - cell_set_m.push_back(cell_t(name, linked, boost::bind(&implementation_t::calculate_expression, - boost::ref(*this), position1, initializer_expression), cell_set_m.size())); - } else { - cell_set_m.push_back(cell_t(name, linked, cell_t::calculator_t(), cell_set_m.size())); - } - - // REVISIT (sparent) : Non-transactional on failure. - input_index_m.insert(cell_set_m.back()); - - if (initializer_expression.size()) - initialize_one(cell_set_m.back()); - - if (expression.size()) - { - // REVISIT (sparent) : Non-transactional on failure. - cell_set_m.push_back(cell_t(access_interface_output, name, - boost::bind(&implementation_t::calculate_expression, - boost::ref(*this), position2, expression), - cell_set_m.size(), &cell_set_m.back())); - } - else - { - cell_set_m.push_back(cell_t(access_interface_output, name, - boost::bind(&implementation_t::get, boost::ref(*this), name), - cell_set_m.size(), &cell_set_m.back())); - } - output_index_m.insert(cell_set_m.back()); - - if (!name_index_m.insert(cell_set_m.back()).second) { - throw stream_error_t(make_string("cell named '", name.c_str(), "'already exists."), position2); - } -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::add_constant(name_t name, const line_position_t& position, - const array_t& initializer) -{ - scope_value_t scope(initialize_mode_m, true); - - cell_set_m.push_back(cell_t(access_constant, name, - calculate_expression(position, initializer), - cell_set_m.size())); - // REVISIT (sparent) : Non-transactional on failure. - - if (!name_index_m.insert(cell_set_m.back()).second) { - throw stream_error_t(make_string("cell named '", name.c_str(), "'already exists."), position); - } -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::add_logic(name_t logic, const line_position_t& position, - const array_t& expression) -{ - cell_set_m.push_back(cell_t(access_logic, logic, - boost::bind(&implementation_t::calculate_expression, - boost::ref(*this), position, expression), - cell_set_m.size())); - - if (!name_index_m.insert(cell_set_m.back()).second) { - throw stream_error_t(make_string("cell named '", logic.c_str(), "'already exists."), position); - } -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::add_invariant(name_t invariant, const line_position_t& position, - const array_t& expression) -{ - // REVISIT (sparent) : Should invariants also go in name_index_m? - cell_set_m.push_back(cell_t(access_invariant, invariant, - boost::bind(&implementation_t::calculate_expression, - boost::ref(*this), position, expression), - cell_set_m.size())); - invariant_index_m.push_back(&cell_set_m.back()); -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::add_relation(const line_position_t& position, - const array_t& conditional, const relation_t* first, const relation_t* last) -{ - relation_cell_set_m.push_back(relation_cell_t(position, conditional, first, last)); - - for (; first != last; ++first) { - index_t::iterator p = output_index_m.find(first->name_m); - - if (p == output_index_m.end() || !p->interface_input_m) - throw stream_error_t(make_string("interface cell ", first->name_m.c_str(), " does not exist."), position); - - p->relation_index_m.push_back(&relation_cell_set_m.back()); - ++p->initial_relation_count_m; - } -} - -/**************************************************************************************************/ - -any_regular_t sheet_t::implementation_t::calculate_expression( - const line_position_t& position, - const array_t& expression) -{ - evaluate(machine_m, position, expression); - - any_regular_t result = move(machine_m.back()); - machine_m.pop_back(); - - return result; -} - -/**************************************************************************************************/ - -sheet_t::connection_t -sheet_t::implementation_t::monitor_enabled(name_t n, const name_t* first, const name_t* last, - const monitor_enabled_t& monitor) -{ - assert(updated_m && "Must call sheet_t::update() prior to monitor_enabled."); - index_t::iterator iter(input_index_m.find(n)); - - if (iter == input_index_m.end()) - throw std::logic_error(make_string("Attempt to monitor nonexistent cell: ", n.c_str())); - - cell_bits_t touch_set; - while(first != last) { - index_t::iterator i(input_index_m.find(*first)); - if (i == input_index_m.end()) - throw std::logic_error(make_string("Attempt to monitor nonexistent cell: ", - first->c_str())); - touch_set.set(i->cell_set_pos_m); - ++first; - - } - - monitor(active_m.test(iter->cell_set_pos_m) || (touch_set & priority_accessed_m).any()); - - return monitor_enabled_m.connect(boost::bind(&sheet_t::implementation_t::enabled_filter, - this, touch_set, iter->cell_set_pos_m, monitor, - _1, _2)); -} - -/**************************************************************************************************/ -sheet_t::connection_t -sheet_t::implementation_t::monitor_invariant_dependent(name_t n, - const monitor_invariant_t& monitor) -{ - assert(updated_m && "Must call sheet_t::update() prior to monitor_invariant_dependent."); - - index_t::iterator iter(output_index_m.find(n)); - - if (iter == output_index_m.end()) - throw std::logic_error(make_string("Attempt to monitor nonexistent cell: ", n.c_str())); - - monitor(iter->invariant_m); - - return iter->monitor_invariant_m.connect(monitor); -} - -/**************************************************************************************************/ - -sheet_t::connection_t -sheet_t::implementation_t::monitor_value(name_t name, const monitor_value_t& monitor) -{ - assert(updated_m && "Must call sheet_t::update() prior to monitor_value."); - - cell_t* cell_ptr(NULL); - - index_t::iterator iter(output_index_m.find(name)); - bool is_output_cell(iter != output_index_m.end()); - if(is_output_cell) - { - cell_ptr = &*iter; - } - else - { - index_vector_t::iterator i(adobe::find_if(invariant_index_m, bind(&cell_t::name_m, _1))); - if(i == invariant_index_m.end()) - throw std::logic_error(make_string("Attempt to monitor nonexistent cell: ", name.c_str())); - cell_ptr = *i; - } - - monitor(cell_ptr->state_m); - - return cell_ptr->monitor_value_m.connect(monitor); -} - -/**************************************************************************************************/ - -sheet_t::connection_t -sheet_t::implementation_t::monitor_contributing(name_t n, const dictionary_t& mark, - const monitor_contributing_t& monitor) -{ - assert(updated_m && "Must call sheet_t::update() prior to monitor_contributing."); - - index_t::iterator iter(output_index_m.find(n)); - - if (iter == output_index_m.end()) - { - throw std::logic_error(make_string("Attempt to monitor nonexistent cell: ", n.c_str())); - } - - monitor(contributing_set(mark, iter->contributing_m)); - - return iter->monitor_contributing_m.connect( - boost::bind(monitor, boost::bind(&sheet_t::implementation_t::contributing_set, - boost::ref(*this), mark, _1))); -} - -/**************************************************************************************************/ - -inline bool sheet_t::implementation_t::has_input(name_t name) const -{ - return input_index_m.find(name) != input_index_m.end(); -} - -/**************************************************************************************************/ - -inline bool sheet_t::implementation_t::has_output(name_t name) const -{ - return output_index_m.find(name) != output_index_m.end(); -} - -/**************************************************************************************************/ - -priority_t sheet_t::implementation_t::name_to_priority(name_t name) const -{ - index_t::const_iterator i = input_index_m.find(name); - assert(i != input_index_m.end() && i->specifier_m == access_interface_input - && "interface cell not found, should not be possible - preflight in add_interface."); - return i->priority_m; -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::flow(cell_bits_t& priority_accessed) -{ - vector cell_names; - - for (relation_cell_set_t::iterator f(relation_cell_set_m.begin()), l(relation_cell_set_m.end()); - f != l; ++f) { - if (!f->resolved_m) { - transform(f->terms_m, std::back_inserter(cell_names), &relation_t::name_m); - } - } - - // Make the list unique - sort(cell_names); - unique(cell_names); - - // Sort the names by the associate priority - sort(cell_names, less(), boost::bind(&implementation_t::name_to_priority, this, _1)); - - for (vector::const_iterator f = cell_names.begin(), l = cell_names.end(); f != l; ++f) { - index_t::const_iterator i = input_index_m.find(*f); - assert(i != input_index_m.end() && i->specifier_m == access_interface_input - && "interface cell not found, should not be possible - preflight in add_interface."); - priority_accessed.set(i->cell_set_pos_m); - } - - /* - pop the top cell from the stack - if the cell is not resolved then resolve as a contributor - find which relations the cell contributes to if any- - if there is only one unresolved cell on the relation then - resolve that cell as derived - (need to have the cell refer to the deriving term in the relation?) - push the cell to the top of stack - loop until stack is empty. - */ - - while(!cell_names.empty()) { - name_t name = cell_names.back(); cell_names.pop_back(); - - index_t::iterator i = output_index_m.find(name); - /* - REVSIT (sparent) : I need to make sure that only interface cells are name by the relate - clauses prior to this. - */ - assert(i != output_index_m.end()); - - if (i->relation_count_m == 0) continue; - - i->resolved_m = true; - - // REVISIT (sparent) : Need an index here to find the relations quickly. - - for (relation_index_t::iterator f = i->relation_index_m.begin(), l = i->relation_index_m.end(); - f != l; ++f) { - - if ((*f)->resolved_m) continue; - - --i->relation_count_m; - - cell_t* cell_to_resolve = 0; - const relation_t* term = 0; - bool at_least_one = false; - - for (relation_set_t::iterator tf((*f)->terms_m.begin()), tl((*f)->terms_m.end()); tf != tl; ++tf) { - - index_t::iterator iter(output_index_m.find(tf->name_m)); - assert(iter != output_index_m.end()); - - cell_t& cell(*iter); - - if (cell.resolved_m) continue; - - if (!cell_to_resolve) { - cell_to_resolve = &cell; - term = &(*tf); - at_least_one = true; - } else { - cell_to_resolve = NULL; break; - } - } - - // REVISIT (sparent) : This is a runtime error. - - assert(at_least_one && "All terms of relation resolved but relation not applied."); - - if (!cell_to_resolve) continue; - - (*f)->resolved_m = true; - cell_to_resolve->resolved_m = true; - cell_to_resolve->term_m = boost::bind(&implementation_t::calculate_expression, - boost::ref(*this), term->position_m, term->expression_m); // cell needs to use the term for calculate. - --cell_to_resolve->relation_count_m; - - // This will be a derived cell and will have a priority lower than any cell contributing to it - - assert(cell_to_resolve->interface_input_m && "Missing input half of interface cell."); - if (cell_to_resolve->interface_input_m->linked_m) { - cell_to_resolve->interface_input_m->priority_m = --priority_low_m; - } - - if (cell_to_resolve->relation_count_m) cell_names.push_back(cell_to_resolve->name_m); - } - - assert(i->relation_count_m == 0 && "Cell still belongs to relation but all relations resolved."); - } -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::update() -{ -#ifndef NDEBUG - check_reentrancy checker(check_update_reentrancy_m); - updated_m = true; -#endif - - conditional_indirect_contributing_m.reset(); - - value_accessed_m.reset(); - - for_each(cell_set_m, &cell_t::clear_dirty); - for_each(relation_cell_set_m, &relation_cell_t::clear_resolved); - -// Solve the conditionals. - - accumulate_contributing_m.reset(); - - for (relation_cell_set_t::iterator current_cell(relation_cell_set_m.begin()), - last_cell(relation_cell_set_m.end()); current_cell != last_cell; ++current_cell) - { - if (current_cell->conditional_m.empty()) continue; - - if (!calculate_expression(current_cell->position_m, current_cell->conditional_m).cast()) - { - // remove this relation from any terms. - for (relation_set_t::iterator current_term(current_cell->terms_m.begin()), - last_term(current_cell->terms_m.end()); current_term != last_term; - ++current_term) - { - index_t::iterator iter(output_index_m.find(current_term->name_m)); - - assert(iter != output_index_m.end() && "Cell was present when we added the relation..."); - - --iter->relation_count_m; - } - current_cell->resolved_m = true; - } - } - - conditional_indirect_contributing_m = accumulate_contributing_m; - - cell_bits_t priority_accessed; - - flow(priority_accessed); - -#ifndef NDEBUG - for (relation_cell_set_t::iterator first(relation_cell_set_m.begin()), - last(relation_cell_set_m.end()); first != last; ++first) - { - if (first->resolved_m) continue; - - - std::clog << "(warning) relation unnecessary and ignored\n" << first->position_m; - - } -#endif - -// calculate the output/interface_output cells and apply. - - for (index_t::const_iterator iter (output_index_m.begin()), last (output_index_m.end()); - iter != last; ++iter) - { - cell_t& cell(*iter); - - // REVISIT (sparent) : This is a copy/paste of get(); - - - if (!cell.evaluated_m) { - accumulate_contributing_m.reset(); - - get_stack_m.push_back(cell.name_m); - - cell.calculate(); - - get_stack_m.pop_back(); - - cell.contributing_m = accumulate_contributing_m; - - cell.contributing_m |= conditional_indirect_contributing_m; - } - - /* - REVISIT (sparent) : This would be slightly more efficient if I moved the link flag - to the output side. - */ - - // Apply the interface output to interface inputs of linked cells. - if (cell.interface_input_m && cell.interface_input_m->linked_m) { - cell.interface_input_m->state_m = cell.state_m; - } - } - -// Then we can check the invariants - - - cell_bits_t contributing; - - for (index_vector_t::const_iterator iter (invariant_index_m.begin()), - last (invariant_index_m.end()); iter != last; ++iter) - { - cell_t& cell(**iter); - bool old_inv(false); - if(!cell.monitor_value_m.empty()) - old_inv = cell.state_m.cast(); - cell.calculate(); - bool new_inv(cell.state_m.cast()); - if(!new_inv) contributing |= cell.contributing_m; - if(old_inv != new_inv) cell.monitor_value_m(any_regular_t(new_inv)); - } - - - /* REVISIT (sparent) : Shoule we report - * conditional_indirect_contributing with the invariants? */ - - /* REVISIT (sparent): Monitoring a value should return all of - - value - contributing - invariant_dependent - - Otherwise the client risks getting out of sync. - */ - - cell_bits_t active; - // REVIST (sparent) : input monitor should recieve priority_accessed and poison bits. - - for (index_t::const_iterator iter (output_index_m.begin()), last (output_index_m.end()); - iter != last; ++iter) - { - cell_t& cell(*iter); - bool invariant ((contributing & cell.contributing_m).none()); - - if (invariant != cell.invariant_m) cell.monitor_invariant_m(invariant); - cell.invariant_m = invariant; - - if (cell.dirty_m) cell.monitor_value_m(cell.state_m); - - - /* - REVISIT (sparent) : Is there any way to prune this down a - bit? Calculating the contributing each time is expensive. - */ - - if (!cell.monitor_contributing_m.empty()) - { - // REVISIT (sparent) : no need to notify if contributing didn't change... - cell.monitor_contributing_m(cell.contributing_m); - } - - // Build bitset of active cells: - index_t::iterator input_iter(input_index_m.find(cell.name_m)); - const bool is_pure_output(input_iter == input_index_m.end()); - if (!is_pure_output) - { - if(priority_accessed.test(input_iter->cell_set_pos_m)) -//priority_accessed_m not updated yet, so we use priority_accessed - { - active.set(input_iter->cell_set_pos_m); - } - } - if(is_pure_output || !has_output_m) - { - active |= cell.contributing_m; - } - } - - // update - monitor_enabled_m(priority_accessed, active); - priority_accessed_m = priority_accessed; - active_m = active; -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::initialize_one(cell_t& cell) -{ - /* - REVISIT (sparent) : Should have more checking here - detecting cycles (and forward - references?) - */ - - accumulate_contributing_m.reset(); - cell.state_m = cell.calculator_m(); - cell.priority_m = ++priority_high_m; - cell.init_contributing_m |= accumulate_contributing_m; -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::reinitialize() -{ - scope_value_t scope(initialize_mode_m, true); - - for (index_t::iterator f = output_index_m.begin(), l = output_index_m.end(); f != l; ++f) { - - if (!f->interface_input_m) continue; - - cell_t& cell = *f->interface_input_m; - - if ((init_dirty_m & cell.init_contributing_m).none()) continue; - - initialize_one(cell); - } - - init_dirty_m.reset(); -} - -/**************************************************************************************************/ - -dictionary_t sheet_t::implementation_t::contributing(const dictionary_t& mark) const -{ - cell_bits_t contributing; - - for (index_t::const_iterator iter (output_index_m.begin()), last (output_index_m.end()); - iter != last; ++iter) - { -// REVISIT (mmarcus) : check whether really want to exclude interface cells - index_t::iterator input_iter(input_index_m.find(iter->name_m)); - const bool is_pure_output(input_iter == input_index_m.end()); - if(!has_output_m || is_pure_output) - contributing |= iter->contributing_m; - } - - return contributing_set(mark, contributing); -} - -/**************************************************************************************************/ - -dictionary_t sheet_t::implementation_t::contributing_to_cell(name_t x) const -{ - index_t::iterator iter = output_index_m.find(x); - - if (iter == output_index_m.end()) - throw std::logic_error(make_string("No monitorable cell: ", x.c_str())); - - return contributing_set(dictionary_t(), iter->contributing_m); -} - -/**************************************************************************************************/ -/* - NOTE (sparent) : A mark containes a dictionary of contributing values. - - If a contributing value has changed or been added since the mark - then it will be reported in the dictionary result. - - If a value is contributing, and the set of values which are - contributing has changed, then the value will be reported as - having been touched. -*/ - -dictionary_t sheet_t::implementation_t::contributing_set(const dictionary_t& mark, - const cell_bits_t& contributing) const -{ - dictionary_t changed; - dictionary_t touched; - bool include_touched(false); - - for (std::size_t index(0), last(cell_set_m.size()); index != last; ++index) - { - if (contributing[index]) - { - const cell_t& cell = cell_set_m[index]; - const name_t& name(cell.name_m); - const any_regular_t& value(cell.state_m); - bool priority_accessed( - priority_accessed_m.test(cell.cell_set_pos_m)); - - if (!mark.count(name)) - { - include_touched = true; - changed.insert(make_pair(name, value)); - } else if (get_value(mark, name) != value) changed.insert(make_pair(name, value)); - else if (priority_accessed) touched.insert(make_pair(name, value)); - } - } - - if (include_touched) { changed.insert(touched.begin(), touched.end()); } - return changed; -} - -/**************************************************************************************************/ - -void sheet_t::implementation_t::set(const dictionary_t& dict) -{ - for (dictionary_t::const_iterator iter(dict.begin()), last(dict.end()); - iter != last; ++iter) - { - set(iter->first, iter->second); - } -} - -/**************************************************************************************************/ - -any_regular_t sheet_t::implementation_t::get(name_t variable_name) -{ - if (initialize_mode_m) { - index_t::iterator iter(input_index_m.find(variable_name)); - - if (iter == input_index_m.end()) { - iter = name_index_m.find(variable_name); - if (iter == name_index_m.end() || iter->specifier_m != access_constant) { - throw std::logic_error(make_string("Variable ", variable_name.c_str(), - " not found.")); - } - } - - cell_t& cell = *iter; - - accumulate_contributing_m |= cell.init_contributing_m; - return cell.state_m; - } - - scope_count scope(get_count_m); - - /* - REVISIT (sparent) - If we go to three pass on interface cells then the max count is - number of cells plus number of interface cells. - */ - - if (get_count_m > cell_set_m.size()) - { - throw std::logic_error(std::string("cycle detected, consider using a relate { } clause.")); - } - - // If the variable is on the top of the stack then we assume it - // must refer to an input cell. - - if (get_stack_m.size() && (get_stack_m.back() == variable_name)) - { - index_t::iterator iter(input_index_m.find(variable_name)); - if (iter == input_index_m.end()) - { - throw std::logic_error(make_string("input variable ", variable_name.c_str(), - " not found.")); - } - value_accessed_m.set(iter->cell_set_pos_m); - - accumulate_contributing_m |= iter->contributing_m; - return iter->state_m; - } - - cell_t* cell_ptr; - - index_t::iterator iter (name_index_m.find(variable_name)); - if (iter != name_index_m.end()) cell_ptr = &(*iter); - else - { - index_t::iterator iter(input_index_m.find(variable_name)); - if (iter == input_index_m.end()) - { - throw std::logic_error(make_string("variable ", variable_name.c_str(), " not found.")); - } - cell_ptr = &(*iter); - } - - cell_t& cell(*cell_ptr); - - if (cell.specifier_m == access_interface_output) get_stack_m.push_back(variable_name); - - if (cell.specifier_m == access_interface_input) value_accessed_m.set(cell.cell_set_pos_m); -// REVISIT (sparent) : paired call should be ctor/dtor - - try - { - // REVISIT (sparent) : First pass getting the logic correct. - - if (cell.evaluated_m) accumulate_contributing_m |= cell.contributing_m; - else { - cell_bits_t old = accumulate_contributing_m; - accumulate_contributing_m.reset(); - - cell.calculate(); - - cell.contributing_m = accumulate_contributing_m; - accumulate_contributing_m |= old; - } - - if(cell.specifier_m != access_input && cell.specifier_m != access_constant) - cell.contributing_m |= conditional_indirect_contributing_m; - } - catch (...) - { - if (cell.specifier_m == access_interface_output) get_stack_m.pop_back(); - throw; - } - - if (cell.specifier_m == access_interface_output) get_stack_m.pop_back(); - - return cell.state_m; -} - -/**************************************************************************************************/ +sheet_t::implementation_t::implementation_t(virtual_machine_t& machine) : + its_machine(machine), + its_g(), + its_mg(its_g) { + machine.set_variable_lookup( + boost::bind( + static_cast( + &implementation_t::get), + boost::ref(*this), + _1) + //[&] (name_t name) { return get(name); } + ); + } + +void sheet_t::implementation_t::add_constant( + name_t name, + const line_position_t& position, const array_t& initializer) +{ + //assert(!initializer.empty() && "Expected value for constant cell."); + add_variable(CONSTANT_CELL, name, initializer); +} + +void sheet_t::implementation_t::add_input( + name_t name, + const line_position_t& position, const array_t& initializer) +{ + add_variable(INPUT_CELL, name, initializer); +}; + +void sheet_t::implementation_t::add_interface( + name_t name, bool linked, + const line_position_t& position1, const array_t& initializer, + const line_position_t& position2, const array_t& expression) +{ + add_variable(INTERFACE_CELL, name, initializer); + //add_input(name, position1, initializer); + //add_output(name + "_out", position1, initializer); +}; + +void sheet_t::implementation_t::add_output( + name_t name, + const line_position_t& position, const array_t& expression) +{ + add_variable(OUTPUT_CELL, name); + add_solo_method(expression, name); +} + +void sheet_t::implementation_t::add_logic( + name_t name, + const line_position_t& position, const array_t& expression) +{ + add_variable(LOGIC_CELL, name); + add_solo_method(expression, name); +} + +void sheet_t::implementation_t::add_invariant( + name_t name, + const line_position_t& position, const array_t& expression) +{ + add_variable(INVARIANT_CELL, name); + add_solo_method(expression, name); +} + +void sheet_t::implementation_t::add_relation( + const line_position_t& position, const array_t& conditional, + const relation_t* first, const relation_t* last) +{ + pm::cgraph::constraint c = add_constraint(its_mg, + pm::cgraph::user_constraint("__cn_@_" + boost::lexical_cast(position.line_number_m))); + put(its_constraints, c, constraint(conditional)); + for (const relation_t* i = first; i != last; ++i) + add_method(c, i->expression_m, i->name_m); +} + +//void sheet_t::implementation_t::reinitialize(const dictionary_t& dictionary) { +void sheet_t::implementation_t::reinitialize() { + /* Adobe's adam.cpp: For each interface cell whose (true input + * cell) contributors are "dirty," i.e. set, reinitialize using + * initializer. */ + /* Adobe's spec: Input cells are re-initialized, in sheet order, + * and interface cell initializers are re-evaluated. Priorities + * are updated, but no callbacks are triggered. Calls to + * reinitialize should be followed by calls to \ref update, just + * as if the cells were updated by \ref set. */ + pm::cgraph::variable_iterator iv, ev; + for (tie(iv, ev) = pm::variables(its_g); iv != ev; ++iv) { + variable& its_v = its_variables[*iv]; + if (its_v.its_kind == INTERFACE_CELL) + set(*iv, inspect(its_v.its_initializer)); + } + /* + for (dictionary_t::const_iterator i(dictionary.begin()), e(dictionary.end()); i != e; ++i) { + pm::cgraph::variable v = its_symtab.find(i->first)->second; + if (!cell_is_of_kind(v, INTERFACE_CELL)) continue; + its_variables[v].its_initializer = i->second; + touch(v); + } + pm::cgraph::variable_iterator iv, ev; + for (tie(iv, ev) = variables(its_g); iv != ev; ++iv) { + variable& its_v = its_variables[*iv]; + if (!its_v.its_initializer.empty()) + its_v.its_value = inspect(its_v.its_initializer); + } + */ +} + +/* monitor ***************************************************************/ + +sheet_t::implementation_t::connection_t sheet_t::implementation_t::monitor_value(name_t name, const monitor_value_t& monitor) { + pm::cgraph::variable v = its_symtab.find(name)->second; + assert(is_output(v) && "Attempt to monitor non-output value."); + implementation_t::variable& its_v = its_variables[v]; + monitor(its_v.its_value); + return its_v.its_value_monitors.connect(monitor); +} + +//sheet_t::implementation_t::connection_t monitor_enabled (name_t, const name_t* first, const name_t* last, const monitor_enabled_t&) { return connection_t(); } +//sheet_t::implementation_t::connection_t monitor_contributing (name_t, const dictionary_t&, const monitor_contributing_t&) { return connection_t(); } +//sheet_t::implementation_t::connection_t monitor_invariant_dependent(name_t, const monitor_invariant_t&) { return connection_t(); } + +/* query *****************************************************************/ + +debug::indenter the_indent; + +any_regular_t sheet_t::implementation_t::inspect(const array_t& expression) { + /* FIXME: Put this check into operator<< and virtual_machine_t::evaluate? */ + if (expression.empty()) + return any_regular_t(); + its_machine.evaluate(expression); + any_regular_t result(its_machine.back()); + its_machine.pop_back(); + DEBUG_PRINT(the_indent << "inspect: " << expression << " = " << result); + return result; +} + +bool sheet_t::implementation_t::has_input(name_t name) const { + return is_input(its_symtab.find(name)->second); +} + +bool sheet_t::implementation_t::has_output(name_t name) const { + return is_output(its_symtab.find(name)->second); +} + +dictionary_t sheet_t::implementation_t::contributing(const dictionary_t& old_contribs) const { + dictionary_t new_contribs; + + pm::cgraph::variable_iterator iv, ev; + for (tie(iv, ev) = variables(its_g); iv != ev; ++iv) { + if (is_contributing(*iv, its_mg)/* && is_cell_of_kind(*iv, INTERFACE_CELL)*/) { + const variable& its_v = its_variables[*iv]; + dictionary_t::const_iterator icell = old_contribs.find(its_v.its_name); + /* If it is not in the old_contribs, or the value has changed, */ + if (icell == old_contribs.end() || its_v.its_value != icell->second) + new_contribs.insert(make_pair(its_v.its_name, its_v.its_value)); + } + } + + return new_contribs; +} + +/* workflow : set ********************************************************/ + +void sheet_t::implementation_t::set(name_t name, const any_regular_t& value) { + pm::cgraph::variable v = its_symtab.find(name)->second; + set(v, value); +} + +/* workflow : update *****************************************************/ + +void sheet_t::implementation_t::update() { + /* Initialize. */ + DEBUG_PRINT(""); + pm::cgraph::constraint_iterator ic, ec; + tie(ic, ec) = constraints(its_g); + for (; ic != ec; ++ic) { + constraint& its_c = its_constraints[*ic]; + /* We will pretend that disabled when-constraints are extremely weak + * stay-constraints. This means they will be immediately retracted and not + * considered part of the graph. */ + /* If its condition is not empty and false, */ + its_mg[*ic].strength = (!its_c.its_condition.empty() && !inspect(its_c.its_condition).cast()) ? + /* give it a strength of infinity... */ + /* (weaker strengths are numerically higher) */ + (std::numeric_limits::max()) : + /* else make it a must-constraint. Actual stay-constraints will get a + * new strength during the planning phase. */ + (0); + DEBUG_PRINT(its_g[*ic].its_label << " has strength " << its_mg[*ic].strength); + } + DEBUG_PRINT(""); + + /* Plan. */ + DEBUG_PRINT(""); + its_mg.plan(its_priorities.begin(), its_priorities.end()); + DEBUG_PRINT(""); + DEBUG_PRINT("plan = " << its_mg); + //pm::its_priorities(its_mg, its_priorities.begin()); + DEBUG_PRINT("priority = ["); + for (priorities_t::iterator iv = its_priorities.begin(); iv != its_priorities.end(); ++iv) { + DEBUG_PRINT(" " << its_g[*iv].its_label); + //its_variables[*iv].its_priority = iv; + } + DEBUG_PRINT("]"); + + /* Evaluate. */ + std::vector the_plan; + pm::its_plan(its_mg, std::back_inserter(the_plan)); + evaluate(the_plan.begin(), the_plan.end()); +} + +template +void sheet_t::implementation_t::evaluate(InputIterator first, InputIterator last) { + pm::cgraph::variable_iterator iv, ev; + + /* Initialize. */ + tie(iv, ev) = variables(its_g); + for (; iv != ev; ++iv) { + variable& its_v = its_variables[*iv]; + its_v.is_cached = false; + //its_v.its_contributors.clear(); + } + //std::for_each(iv, ev, [&] (const pm::cgraph::variable& v) { + // its_variables[v].is_cached = false; + //}); + + /* Evaluate. */ + tie(iv, ev) = variables(its_g); + boost::function f = boost::bind(quickplan::is_leaf, _1, boost::ref(its_mg)); + //auto f = [&] (const pm::cgraph::variable& v) { return is_leaf(v, its_mg); }; + DEBUG_PRINT(""); + std::for_each( + make_filter_iterator(f, iv, ev), + make_filter_iterator(f, ev, ev), + boost::bind( + static_cast( + &implementation_t::get), + boost::ref(*this), + _1) + //[&] (const pm::cgraph::variable& v) { get(v); } + ); + DEBUG_PRINT(""); + + /* Mark contributors. */ + //for (tie(iv, ev) = variables(its_g); iv != ev; ++iv) { + //if (is_contributing(*iv, its_mg)) { + //depth_first_visit(its_mg, + //boost::graph_traits::vertex_descriptor(*iv), + //contributing_visitor(*iv, *this), + //library::fast_property_map< + //boost::graph_traits::vertex_descriptor, + //boost::default_color_type, + //pm::vertex_index_map + //>( + //num_variables(its_g) + num_methods(its_g), + //boost::color_traits::white(), + //pm::vertex_index_map(its_g) + //) + //); + //} + //} + +} + +any_regular_t sheet_t::implementation_t::get(const pm::cgraph::variable& v) { + variable& its_v = its_variables[v]; + DEBUG_PRINT(the_indent++ << "get: " << its_g[v].its_label << " => " << its_v.its_value); + + if (!its_v.is_cached) { + its_v.is_cached = true; + /* Invariants are not preconditions on methods. */ + any_regular_t old_value = its_v.its_value; + pm::cgraph::constraint c = its_mg[v].determined_by; + pm::cgraph::method m = its_mg[c].selected_method; + DEBUG_PRINT(the_indent << "inspect: " << its_g[m].its_label); + method& its_m = its_methods[m]; + any_regular_t new_value = inspect(its_m.its_expression); + if (!empty(new_value) && new_value != old_value) { + its_v.its_value = new_value; + its_v.its_value_monitors(new_value); + } + } + + DEBUG_PRINT(--the_indent << "get: " << its_g[v].its_label << " <= " << its_v.its_value << std::endl); + return its_v.its_value; +} + +any_regular_t sheet_t::implementation_t::get(name_t name) { + pm::cgraph::variable v = its_symtab.find(name)->second; + return get(v); +} + +/* free functions ********************************************************/ + +void sheet_t::implementation_t::set(const dictionary_t& dict) { + for (dictionary_t::const_iterator iter(dict.begin()), last(dict.end()); iter != last; ++iter) + set(iter->first, iter->second); +} + +/*************************************************************************************************/ } // namespace adobe -/**************************************************************************************************/ +/*************************************************************************************************/ diff --git a/adobe_source_libraries/source/quickplan.cpp b/adobe_source_libraries/source/quickplan.cpp new file mode 100644 index 0000000..bac4667 --- /dev/null +++ b/adobe_source_libraries/source/quickplan.cpp @@ -0,0 +1,328 @@ +#include +#include +#include +#include + +#include +#include + +#include + +#define DEBUG +#include + +using namespace std; +using namespace pm; +using namespace library; + +namespace quickplan { + + const mark::type mark::INITIAL_DOWNSTREAM_MARK = numeric_limits::max() / 2; + + pair + safe_outgoing_vertices(const cgraph::method& m, const cgraph& g) { + typedef pair return_t; + return (m == cgraph::null_method()) ? return_t() : outgoing_vertices(m, g); + } + + mgraph::mgraph(pm::cgraph& g) : super(g) { + /* Need to add stay constraint for every variable. None may be + * user-defined. */ + cgraph::variable_iterator iv, ev; + for (tie(iv, ev) = variables(g); iv != ev; ++iv) { + add_stay_constraint(*iv, *this); + } + } + + void mgraph::prepare() { + /* Call this only once and never again. */ + static bool is_prepared = false; + if (is_prepared) { /*throw already_prepared_exception();*/ return; } + else { is_prepared = true; } + + { + /* Need to build variable list for each constraint. */ + /* Need variable list for setting num_constraints, + * need num_constraints for setting free variables, + * only care about free variables that are outputs, + * thus only need to add outputs to variable lists. */ + fast_property_map, unwrapper> cns_of_var(num_variables(g())); + fast_property_map, unwrapper> vars_of_cn(num_constraints(g()));; + + cgraph::variable_iterator iv, ev; + for (tie(iv, ev) = variables(g()); iv != ev; ++iv) { + cgraph::incoming_method_iterator iim, eim; + for (tie(iim, eim) = incoming_vertices(*iv, g()); iim != eim; ++iim) { + vars_of_cn[g()[*iim].its_constraint].insert(*iv); + /* Not sure if this is correct. */ + cns_of_var[*iv].insert(g()[*iim].its_constraint); + } + mg()[*iv].constraints.assign(cns_of_var[*iv].begin(), cns_of_var[*iv].end()); + mg()[*iv].num_constraints = mg()[*iv].constraints.size(); + DEBUG_PRINT("variable " << g()[*iv].its_label << " is attached to " << mg()[*iv].num_constraints << " constraints"); + } + cgraph::constraint_iterator ic, ec; + for (tie(ic, ec) = constraints(g()); ic != ec; ++ic) { + mg()[*ic].variables.assign(vars_of_cn[*ic].begin(), vars_of_cn[*ic].end()); + } + } + + } + + void mgraph::collect_upstream_constraints(const cgraph::constraint& cn) { + DEBUG_PRINT("CUC: " << g()[cn].its_label); + mg()[cn].mark = its_mark.upstream_mark(); + /* All upstream constraints weaker than the constraint to be enforced + * should be added to the retractable constraints queue. */ + if (stronger(*this)(cn_to_enforce, cn)) { + DEBUG_PRINT(" collected " << g()[cn].its_label); + retractable_cns_queue.push_back(cn); + } + for (vector::const_iterator iv = mg()[cn].variables.begin(); iv != mg()[cn].variables.end(); ++iv) { + /* Computation of a variable's num_constraints field */ + if (mg()[*iv].mark == its_mark.upstream_mark()) { + ++mg()[*iv].num_constraints; + } else { + mg()[*iv].mark = its_mark.upstream_mark(); + mg()[*iv].num_constraints = 1; + } + cgraph::constraint e = mg()[*iv].determined_by; + //if (e == cgraph::null_constraint()) + //DEBUG_PRINT(g()[*iv].its_label << " undetermined"); + if (e != cgraph::null_constraint() && mg()[e].mark != its_mark.upstream_mark()) + collect_upstream_constraints(e); + /* Input variables that are being visited for the first time and variables + * that have not yet been visited by any constraint other than the + * constraint that outputs them are potential free variables */ + else if (mg()[*iv].num_constraints == 1) + free_variable_set.push_back(*iv); + } + } + + void mgraph::collect_downstream_unenforced_constraints(const cgraph::variable& v) { + mg()[v].mark = its_mark.downstream_mark(); + for (vector::const_iterator icn = mg()[v].constraints.begin(); icn != mg()[v].constraints.end(); ++icn) { + if (mg()[*icn].selected_method == cgraph::null_method()) { + /* Weaker strengths are numerically greater. */ + if (mg()[*icn].strength >= strongest_retracted_strength) + unenforced_cns_queue.push_back(*icn); + } else { + //if (mg()[*icn].selected_method != cgraph::null_method()) && + if (mg()[*icn].mark != its_mark.downstream_mark()) { + mg()[*icn].mark = its_mark.downstream_mark(); + cgraph::outgoing_variable_iterator iw, ew; + for (tie(iw, ew) = outgoing_vertices(mg()[*icn].selected_method, g()); iw != ew; ++iw) { + if (mg()[*iw].mark != its_mark.downstream_mark()) + collect_downstream_unenforced_constraints(*iw); + } + } + } + } + } + + void mgraph::collect_unenforced_constraints(const vector& undetermined_vars, const cgraph::constraint& newly_enforced_cn) { + cgraph::outgoing_variable_iterator iv, ev; + for (tie(iv, ev) = outgoing_vertices(mg()[newly_enforced_cn].selected_method, g()); iv != ev; ++iv) + collect_downstream_unenforced_constraints(*iv); + for (vector::const_iterator iv = undetermined_vars.begin(); iv != undetermined_vars.end(); ++iv) + collect_downstream_unenforced_constraints(*iv); + } + + void mgraph::multi_output_planner() { + while (mg()[cn_to_enforce].selected_method == cgraph::null_method() && !free_variable_set.empty()) { + /* Remove an arbitrary element from the free variable set. */ + cgraph::variable free_var = free_variable_set.back(); + free_variable_set.pop_back(); + if (mg()[free_var].num_constraints == 1) { + /* cn = the constraint to which free_var belongs whose mark equals + * upstream_mark */ + cgraph::constraint cn = cgraph::null_constraint(); + for (vector::const_iterator icn = mg()[free_var].constraints.begin(); icn != mg()[free_var].constraints.end(); ++icn) { + if (mg()[*icn].mark == its_mark.upstream_mark()) { + cn = *icn; + break; + } + } + + DEBUG_PRINT("MOP: free variable " << g()[free_var].its_label << " attached to " << g()[cn].its_label); + + /* mt = cn.method with the smallest number of outputs such that all of + * its outputs are free_variables */ + cgraph::method mt = cgraph::null_method(); + cgraph::csm_iterator imt, emt; + for (tie(imt, emt) = methods(cn, g()); imt != emt; ++imt) { + cgraph::outgoing_variable_iterator ivar, evar; + for (tie(ivar, evar) = outgoing_vertices(*imt, g()); ivar != evar; ++ivar) + if (mg()[*ivar].num_constraints != 1) break; + if (ivar == evar && (mt == cgraph::null_method() || out_degree(*imt, g()) < out_degree(mt, g()))) { + mt = *imt; + break; + } + } + + //if (imt != emt) + if (mt != cgraph::null_method()) { + eliminate_constraint(cn, mt); + + /* The output variables' determined_by fields must be set so that + * collect_upstream_constraints can perform its reverse depth-first + * search. */ + cgraph::outgoing_variable_iterator ioutput, eoutput; + for (tie(ioutput, eoutput) = outgoing_vertices(mt, g()); ioutput != eoutput; ++ioutput) + mg()[*ioutput].determined_by = cn; + + /* A variable that cannot be made the output of a constraint and + * which is marked 'potentially_undetermined' is a potential + * undetermined variable. */ + } else if (mg()[free_var].mark == mark::POTENTIALLY_UNDETERMINED) { + potential_undetermined_vars.insert(free_var); + } + } else if (mg()[free_var].mark == mark::POTENTIALLY_UNDETERMINED) { + potential_undetermined_vars.insert(free_var); + } + + } + } + + void mgraph::constraint_hierarchy_planner(strength_t ceiling_strength) { + DEBUG_PRINT("CHP: attempt to enforce " << g()[cn_to_enforce].its_label); + multi_output_planner(); + while (mg()[cn_to_enforce].selected_method == cgraph::null_method() && !retractable_cns_queue.empty()) { + cgraph::constraint cn = retractable_cns_queue.front(); + pop_heap(retractable_cns_queue.begin(), retractable_cns_queue.end(), stronger(*this)); + retractable_cns_queue.erase(--retractable_cns_queue.end()); + /* Stronger strengths are numerically lesser. */ + if (mg()[cn].strength <= ceiling_strength) continue; + strongest_retracted_strength = min(strongest_retracted_strength, mg()[cn].strength); + eliminate_constraint(cn, cgraph::null_method()); + multi_output_planner(); + } + } + + class free : public unary_function { + private: + const mgraph& mg; + public: + free(const mgraph& mg) : mg(mg) {} + bool operator() (const cgraph::variable& v) const { + return mg[v].num_constraints == 1; + } + }; + + + void mgraph::constraint_hierarchy_solver() { + static const strength_t WEAKEST_CONSTRAINT_STRENGTH = numeric_limits::max(); + + while (!unenforced_cns_queue.empty()) { + cn_to_enforce = unenforced_cns_queue.front(); + pop_heap(unenforced_cns_queue.begin(), unenforced_cns_queue.end(), not2(stronger(*this))); + unenforced_cns_queue.erase(--unenforced_cns_queue.end()); + //if (mg()[cn_to_enforce].mark == mark::NULL_MARK) continue; + its_mark.new_upstream_mark(); + its_mark.new_downstream_mark(); + strongest_retracted_strength = WEAKEST_CONSTRAINT_STRENGTH; + while (!undo_stack.empty()) undo_stack.pop(); + potential_undetermined_vars.clear(); + retractable_cns_queue.clear(); + + DEBUG_PRINT(""); + + collect_upstream_constraints(cn_to_enforce); + make_heap(retractable_cns_queue.begin(), retractable_cns_queue.end(), stronger(*this)); + /* Cull from the free variable set variables that belong to more than one + * constraint. */ + //free_variable_set = free_variable_set - { v | v.num_constraints > 1 } + vector::iterator ev = remove_if(free_variable_set.begin(), free_variable_set.end(), not1(free(*this))); + free_variable_set.erase(ev, free_variable_set.end()); + constraint_hierarchy_planner(mg()[cn_to_enforce].strength); + if (mg()[cn_to_enforce].selected_method == cgraph::null_method()) { + undo(); + /* Collect unenforced constraints only if a constraint was retracted and + * the enforced constraint is not an input constraint. */ + /* Stronger constraints are numerically less. */ + } else if (strongest_retracted_strength < WEAKEST_CONSTRAINT_STRENGTH /* && cn_to_enforce is not an input constraint that overrides a previously enforced stay constraint */) { + /* Collect unenforced constraints that are downstream of either the + * newly undetermined variables or the outputs of the newly enforced + * constraint. */ + vector undetermined_variables; + for (set::const_iterator iw = potential_undetermined_vars.begin(); iw != potential_undetermined_vars.end(); ++iw) { + if (mg()[*iw].determined_by == cgraph::null_constraint()) + undetermined_variables.push_back(*iw); + } + for (vector::const_iterator iw = free_variable_set.begin(); iw != free_variable_set.end(); ++iw) { + if (mg()[*iw].determined_by == cgraph::null_constraint() && mg()[*iw].mark == mark::POTENTIALLY_UNDETERMINED) + undetermined_variables.push_back(*iw); + } + collect_unenforced_constraints(undetermined_variables, cn_to_enforce); + make_heap(unenforced_cns_queue.begin(), unenforced_cns_queue.end(), not2(stronger(*this))); + } + } + } + + void mgraph::eliminate_constraint(const cgraph::constraint& cn, const cgraph::method& mt) { + DEBUG_DO( + cerr << ((mt == cgraph::null_method()) ? (" retracted ") : (" eliminated ")) << g()[cn].its_label; + if (mt != cgraph::null_method()) cerr << " by choosing " << g()[mt].its_label; + cerr << endl; + ); + /* Any variable that is no longer output by cn and which does not + * become a free variable is a potential undetermined variable. */ + cgraph::outgoing_variable_iterator bov_prev, eov_prev, bov_next, eov_next;; + assert(bov_prev == eov_prev); + assert(bov_next == eov_next); + tie(bov_prev, eov_prev) = safe_outgoing_vertices(mg()[cn].selected_method, g()); + tie(bov_next, eov_next) = safe_outgoing_vertices(mt, g()); + + typedef vector variable_list; + variable_list newly_undetermined_vars; + set_difference(bov_prev, eov_prev, bov_next, eov_next, back_insert_iterator(newly_undetermined_vars)); + + for (variable_list::const_iterator ivar = newly_undetermined_vars.begin(); ivar != newly_undetermined_vars.end(); ++ivar) { + mg()[*ivar].determined_by = cgraph::null_constraint(); + if (mg()[*ivar].num_constraints > 2) + potential_undetermined_vars.insert(*ivar); + else + mg()[*ivar].mark = mark::POTENTIALLY_UNDETERMINED; + } + + /* A constraint can be removed from the set of unsatisfied + * constraints by setting its mark field to NULL. */ + mg()[cn].mark = mark::NULL_MARK; + /* Keep track of redetermined constraints so they can be undone if + * necessary. */ + undo_stack.push(make_pair(cn, mg()[cn].selected_method)); + if (mg()[cn].selected_method != cgraph::null_method()) mg()[mg()[cn].selected_method].is_selected = false; + mg()[cn].selected_method = mt; + if (mt != cgraph::null_method()) mg()[mt].is_selected = true; + for (vector::const_iterator ivar = mg()[cn].variables.begin(); ivar != mg()[cn].variables.end(); ++ivar) { + --mg()[*ivar].num_constraints; + DEBUG_PRINT(" variable " << g()[*ivar].its_label << " now has " << mg()[*ivar].num_constraints << " constraints attached"); + if (mg()[*ivar].num_constraints == 1) { + DEBUG_PRINT(" new free variable: " << g()[*ivar].its_label); + free_variable_set.push_back(*ivar); + } + } + } + + void mgraph::undo() { + DEBUG_PRINT("UNDO:"); + while (!undo_stack.empty()) { + cgraph::constraint cn; + cgraph::method mt_to_restore; + tie(cn, mt_to_restore) = undo_stack.top(); + undo_stack.pop(); + cgraph::method mt_to_undo = mg()[cn].selected_method; + cgraph::outgoing_variable_iterator ivar, evar; + for (tie(ivar, evar) = safe_outgoing_vertices(mg()[cn].selected_method, g()); ivar != evar; ++ivar) + mg()[*ivar].determined_by = cgraph::null_constraint(); + for (tie(ivar, evar) = safe_outgoing_vertices(mt_to_restore, g()); ivar != evar; ++ivar) + mg()[*ivar].determined_by = cn; + DEBUG_PRINT(" resetting method for " << g()[cn].its_label << " to " << g()[mt_to_restore].its_label << " from " << g()[mt_to_undo].its_label); + mg()[mt_to_undo].is_selected = false; + mg()[cn].selected_method = mt_to_restore; + mg()[mt_to_restore].is_selected = true; + } + } + +} +