diff options
| author | shumkovnd <[email protected]> | 2023-11-10 14:39:34 +0300 |
|---|---|---|
| committer | shumkovnd <[email protected]> | 2023-11-10 16:42:24 +0300 |
| commit | 77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch) | |
| tree | c51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/kiwisolver | |
| parent | dd6d20cadb65582270ac23f4b3b14ae189704b9d (diff) | |
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/kiwisolver')
67 files changed, 14120 insertions, 0 deletions
diff --git a/contrib/python/kiwisolver/py2/.dist-info/METADATA b/contrib/python/kiwisolver/py2/.dist-info/METADATA new file mode 100644 index 00000000000..6c363cae42f --- /dev/null +++ b/contrib/python/kiwisolver/py2/.dist-info/METADATA @@ -0,0 +1,42 @@ +Metadata-Version: 2.1 +Name: kiwisolver +Version: 1.1.0 +Summary: A fast implementation of the Cassowary constraint solver +Home-page: https://github.com/nucleic/kiwi +Author: The Nucleic Development Team +Author-email: [email protected] +License: BSD +Platform: UNKNOWN +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Requires-Dist: setuptools + +Welcome to Kiwi +=============== + +.. image:: https://travis-ci.org/nucleic/kiwi.svg?branch=master + :target: https://travis-ci.org/nucleic/kiwi +.. image:: https://codecov.io/gh/nucleic/kiwi/branch/master/graph/badge.svg + :target: https://codecov.io/gh/nucleic/kiwi +.. image:: https://readthedocs.org/projects/kiwisolver/badge/?version=latest + :target: https://kiwisolver.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +Kiwi is an efficient C++ implementation of the Cassowary constraint solving +algorithm. Kiwi is an implementation of the algorithm based on the seminal +Cassowary paper. It is *not* a refactoring of the original C++ solver. Kiwi +has been designed from the ground up to be lightweight and fast. Kiwi ranges +from 10x to 500x faster than the original Cassowary solver with typical use +cases gaining a 40x improvement. Memory savings are consistently > 5x. + +In addition to the C++ solver, Kiwi ships with hand-rolled Python bindings. + + diff --git a/contrib/python/kiwisolver/py2/.dist-info/top_level.txt b/contrib/python/kiwisolver/py2/.dist-info/top_level.txt new file mode 100644 index 00000000000..9b85884d1a9 --- /dev/null +++ b/contrib/python/kiwisolver/py2/.dist-info/top_level.txt @@ -0,0 +1 @@ +kiwisolver diff --git a/contrib/python/kiwisolver/py2/README.rst b/contrib/python/kiwisolver/py2/README.rst new file mode 100644 index 00000000000..4faa3c329ba --- /dev/null +++ b/contrib/python/kiwisolver/py2/README.rst @@ -0,0 +1,19 @@ +Welcome to Kiwi +=============== + +.. image:: https://travis-ci.org/nucleic/kiwi.svg?branch=master + :target: https://travis-ci.org/nucleic/kiwi +.. image:: https://codecov.io/gh/nucleic/kiwi/branch/master/graph/badge.svg + :target: https://codecov.io/gh/nucleic/kiwi +.. image:: https://readthedocs.org/projects/kiwisolver/badge/?version=latest + :target: https://kiwisolver.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +Kiwi is an efficient C++ implementation of the Cassowary constraint solving +algorithm. Kiwi is an implementation of the algorithm based on the seminal +Cassowary paper. It is *not* a refactoring of the original C++ solver. Kiwi +has been designed from the ground up to be lightweight and fast. Kiwi ranges +from 10x to 500x faster than the original Cassowary solver with typical use +cases gaining a 40x improvement. Memory savings are consistently > 5x. + +In addition to the C++ solver, Kiwi ships with hand-rolled Python bindings. diff --git a/contrib/python/kiwisolver/py2/kiwi/AssocVector.h b/contrib/python/kiwisolver/py2/kiwi/AssocVector.h new file mode 100644 index 00000000000..0d5eb288efc --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/AssocVector.h @@ -0,0 +1,354 @@ +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2001 by Andrei Alexandrescu +// This code accompanies the book: +// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design +// Patterns Applied". Copyright (c) 2001. Addison-Wesley. +// Permission to use, copy, modify, distribute and sell this software for any +// purpose is hereby granted without fee, provided that the above copyright +// notice appear in all copies and that both that copyright notice and this +// permission notice appear in supporting documentation. +// The author or Addison-Wesley Longman make no representations about the +// suitability of this software for any purpose. It is provided "as is" +// without express or implied warranty. +//////////////////////////////////////////////////////////////////////////////// +#pragma once + +// $Id: AssocVector.h 765 2006-10-18 13:55:32Z syntheticpp $ + + +#include <algorithm> +#include <functional> +#include <vector> +#include <utility> + +namespace Loki +{ +//////////////////////////////////////////////////////////////////////////////// +// class template AssocVectorCompare +// Used by AssocVector +//////////////////////////////////////////////////////////////////////////////// + + namespace Private + { + template <class Value, class C> + class AssocVectorCompare : public C + { + typedef std::pair<typename C::first_argument_type, Value> + Data; + typedef typename C::first_argument_type first_argument_type; + + public: + AssocVectorCompare() + {} + + AssocVectorCompare(const C& src) : C(src) + {} + + bool operator()(const first_argument_type& lhs, + const first_argument_type& rhs) const + { return C::operator()(lhs, rhs); } + + bool operator()(const Data& lhs, const Data& rhs) const + { return operator()(lhs.first, rhs.first); } + + bool operator()(const Data& lhs, + const first_argument_type& rhs) const + { return operator()(lhs.first, rhs); } + + bool operator()(const first_argument_type& lhs, + const Data& rhs) const + { return operator()(lhs, rhs.first); } + }; + } + +//////////////////////////////////////////////////////////////////////////////// +// class template AssocVector +// An associative vector built as a syntactic drop-in replacement for std::map +// BEWARE: AssocVector doesn't respect all map's guarantees, the most important +// being: +// * iterators are invalidated by insert and erase operations +// * the complexity of insert/erase is O(N) not O(log N) +// * value_type is std::pair<K, V> not std::pair<const K, V> +// * iterators are random +//////////////////////////////////////////////////////////////////////////////// + + + template + < + class K, + class V, + class C = std::less<K>, + class A = std::allocator< std::pair<K, V> > + > + class AssocVector + : private std::vector< std::pair<K, V>, A > + , private Private::AssocVectorCompare<V, C> + { + typedef std::vector<std::pair<K, V>, A> Base; + typedef Private::AssocVectorCompare<V, C> MyCompare; + + public: + typedef K key_type; + typedef V mapped_type; + typedef typename Base::value_type value_type; + + typedef C key_compare; + typedef A allocator_type; + typedef typename A::reference reference; + typedef typename A::const_reference const_reference; + typedef typename Base::iterator iterator; + typedef typename Base::const_iterator const_iterator; + typedef typename Base::size_type size_type; + typedef typename Base::difference_type difference_type; + typedef typename A::pointer pointer; + typedef typename A::const_pointer const_pointer; + typedef typename Base::reverse_iterator reverse_iterator; + typedef typename Base::const_reverse_iterator const_reverse_iterator; + + class value_compare + : public std::binary_function<value_type, value_type, bool> + , private key_compare + { + friend class AssocVector; + + protected: + value_compare(key_compare pred) : key_compare(pred) + {} + + public: + bool operator()(const value_type& lhs, const value_type& rhs) const + { return key_compare::operator()(lhs.first, rhs.first); } + }; + + // 23.3.1.1 construct/copy/destroy + + explicit AssocVector(const key_compare& comp = key_compare(), + const A& alloc = A()) + : Base(alloc), MyCompare(comp) + {} + + template <class InputIterator> + AssocVector(InputIterator first, InputIterator last, + const key_compare& comp = key_compare(), + const A& alloc = A()) + : Base(first, last, alloc), MyCompare(comp) + { + MyCompare& me = *this; + std::sort(begin(), end(), me); + } + + AssocVector& operator=(const AssocVector& rhs) + { + AssocVector(rhs).swap(*this); + return *this; + } + + // iterators: + // The following are here because MWCW gets 'using' wrong + iterator begin() { return Base::begin(); } + const_iterator begin() const { return Base::begin(); } + iterator end() { return Base::end(); } + const_iterator end() const { return Base::end(); } + reverse_iterator rbegin() { return Base::rbegin(); } + const_reverse_iterator rbegin() const { return Base::rbegin(); } + reverse_iterator rend() { return Base::rend(); } + const_reverse_iterator rend() const { return Base::rend(); } + + // capacity: + bool empty() const { return Base::empty(); } + size_type size() const { return Base::size(); } + size_type max_size() { return Base::max_size(); } + + // 23.3.1.2 element access: + mapped_type& operator[](const key_type& key) + { return insert(value_type(key, mapped_type())).first->second; } + + // modifiers: + std::pair<iterator, bool> insert(const value_type& val) + { + bool found(true); + iterator i(lower_bound(val.first)); + + if (i == end() || this->operator()(val.first, i->first)) + { + i = Base::insert(i, val); + found = false; + } + return std::make_pair(i, !found); + } + //Section [23.1.2], Table 69 + //http://developer.apple.com/documentation/DeveloperTools/gcc-3.3/libstdc++/23_containers/howto.html#4 + iterator insert(iterator pos, const value_type& val) + { + if( (pos == begin() || this->operator()(*(pos-1),val)) && + (pos == end() || this->operator()(val, *pos)) ) + { + return Base::insert(pos, val); + } + return insert(val).first; + } + + template <class InputIterator> + void insert(InputIterator first, InputIterator last) + { for (; first != last; ++first) insert(*first); } + + void erase(iterator pos) + { Base::erase(pos); } + + size_type erase(const key_type& k) + { + iterator i(find(k)); + if (i == end()) return 0; + erase(i); + return 1; + } + + void erase(iterator first, iterator last) + { Base::erase(first, last); } + + void swap(AssocVector& other) + { + Base::swap(other); + MyCompare& me = *this; + MyCompare& rhs = other; + std::swap(me, rhs); + } + + void clear() + { Base::clear(); } + + // observers: + key_compare key_comp() const + { return *this; } + + value_compare value_comp() const + { + const key_compare& comp = *this; + return value_compare(comp); + } + + // 23.3.1.3 map operations: + iterator find(const key_type& k) + { + iterator i(lower_bound(k)); + if (i != end() && this->operator()(k, i->first)) + { + i = end(); + } + return i; + } + + const_iterator find(const key_type& k) const + { + const_iterator i(lower_bound(k)); + if (i != end() && this->operator()(k, i->first)) + { + i = end(); + } + return i; + } + + size_type count(const key_type& k) const + { return find(k) != end(); } + + iterator lower_bound(const key_type& k) + { + MyCompare& me = *this; + return std::lower_bound(begin(), end(), k, me); + } + + const_iterator lower_bound(const key_type& k) const + { + const MyCompare& me = *this; + return std::lower_bound(begin(), end(), k, me); + } + + iterator upper_bound(const key_type& k) + { + MyCompare& me = *this; + return std::upper_bound(begin(), end(), k, me); + } + + const_iterator upper_bound(const key_type& k) const + { + const MyCompare& me = *this; + return std::upper_bound(begin(), end(), k, me); + } + + std::pair<iterator, iterator> equal_range(const key_type& k) + { + MyCompare& me = *this; + return std::equal_range(begin(), end(), k, me); + } + + std::pair<const_iterator, const_iterator> equal_range( + const key_type& k) const + { + const MyCompare& me = *this; + return std::equal_range(begin(), end(), k, me); + } + + template <class K1, class V1, class C1, class A1> + friend bool operator==(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + + bool operator<(const AssocVector& rhs) const + { + const Base& me = *this; + const Base& yo = rhs; + return me < yo; + } + + template <class K1, class V1, class C1, class A1> + friend bool operator!=(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + + template <class K1, class V1, class C1, class A1> + friend bool operator>(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + + template <class K1, class V1, class C1, class A1> + friend bool operator>=(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + + template <class K1, class V1, class C1, class A1> + friend bool operator<=(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + }; + + template <class K, class V, class C, class A> + inline bool operator==(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { + const std::vector<std::pair<K, V>, A>& me = lhs; + return me == rhs; + } + + template <class K, class V, class C, class A> + inline bool operator!=(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { return !(lhs == rhs); } + + template <class K, class V, class C, class A> + inline bool operator>(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { return rhs < lhs; } + + template <class K, class V, class C, class A> + inline bool operator>=(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { return !(lhs < rhs); } + + template <class K, class V, class C, class A> + inline bool operator<=(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { return !(rhs < lhs); } + + + // specialized algorithms: + template <class K, class V, class C, class A> + void swap(AssocVector<K, V, C, A>& lhs, AssocVector<K, V, C, A>& rhs) + { lhs.swap(rhs); } + +} // namespace Loki diff --git a/contrib/python/kiwisolver/py2/kiwi/constraint.h b/contrib/python/kiwisolver/py2/kiwi/constraint.h new file mode 100644 index 00000000000..558dd924aeb --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/constraint.h @@ -0,0 +1,124 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <map> +#include <vector> +#include "expression.h" +#include "shareddata.h" +#include "strength.h" +#include "term.h" +#include "variable.h" + + +namespace kiwi +{ + +enum RelationalOperator { OP_LE, OP_GE, OP_EQ }; + + +class Constraint +{ + +public: + + Constraint() : m_data( 0 ) {} + + Constraint( const Expression& expr, + RelationalOperator op, + double strength = strength::required ) : + m_data( new ConstraintData( expr, op, strength ) ) {} + + Constraint( const Constraint& other, double strength ) : + m_data( new ConstraintData( other, strength ) ) {} + + ~Constraint() {} + + const Expression& expression() const + { + return m_data->m_expression; + } + + RelationalOperator op() const + { + return m_data->m_op; + } + + double strength() const + { + return m_data->m_strength; + } + + bool operator!() const + { + return !m_data; + } + +private: + + static Expression reduce( const Expression& expr ) + { + std::map<Variable, double> vars; + typedef std::vector<Term>::const_iterator iter_t; + iter_t end = expr.terms().end(); + for( iter_t it = expr.terms().begin(); it != end; ++it ) + vars[ it->variable() ] += it->coefficient(); + std::vector<Term> terms( vars.begin(), vars.end() ); + return Expression( terms, expr.constant() ); + } + + class ConstraintData : public SharedData + { + + public: + + ConstraintData( const Expression& expr, + RelationalOperator op, + double strength ) : + SharedData(), + m_expression( reduce( expr ) ), + m_strength( strength::clip( strength ) ), + m_op( op ) {} + + ConstraintData( const Constraint& other, double strength ) : + SharedData(), + m_expression( other.expression() ), + m_strength( strength::clip( strength ) ), + m_op( other.op() ) {} + + ~ConstraintData() {} + + Expression m_expression; + double m_strength; + RelationalOperator m_op; + + private: + + ConstraintData( const ConstraintData& other ); + + ConstraintData& operator=( const ConstraintData& other ); + }; + + SharedDataPtr<ConstraintData> m_data; + + friend bool operator<( const Constraint& lhs, const Constraint& rhs ) + { + return lhs.m_data < rhs.m_data; + } + + friend bool operator==( const Constraint& lhs, const Constraint& rhs ) + { + return lhs.m_data == rhs.m_data; + } + + friend bool operator!=( const Constraint& lhs, const Constraint& rhs ) + { + return lhs.m_data != rhs.m_data; + } +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/debug.h b/contrib/python/kiwisolver/py2/kiwi/debug.h new file mode 100644 index 00000000000..007a24da5ed --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/debug.h @@ -0,0 +1,203 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <iostream> +#include <sstream> +#include <vector> +#include "constraint.h" +#include "solverimpl.h" +#include "term.h" + + +namespace kiwi +{ + +namespace impl +{ + +class DebugHelper +{ + +public: + + static void dump( const SolverImpl& solver, std::ostream& out ) + { + out << "Objective" << std::endl; + out << "---------" << std::endl; + dump( *solver.m_objective, out ); + out << std::endl; + out << "Tableau" << std::endl; + out << "-------" << std::endl; + dump( solver.m_rows, out ); + out << std::endl; + out << "Infeasible" << std::endl; + out << "----------" << std::endl; + dump( solver.m_infeasible_rows, out ); + out << std::endl; + out << "Variables" << std::endl; + out << "---------" << std::endl; + dump( solver.m_vars, out ); + out << std::endl; + out << "Edit Variables" << std::endl; + out << "--------------" << std::endl; + dump( solver.m_edits, out ); + out << std::endl; + out << "Constraints" << std::endl; + out << "-----------" << std::endl; + dump( solver.m_cns, out ); + out << std::endl; + out << std::endl; + } + + static void dump( const SolverImpl::RowMap& rows, std::ostream& out ) + { + typedef SolverImpl::RowMap::const_iterator iter_t; + iter_t end = rows.end(); + for( iter_t it = rows.begin(); it != end; ++it ) + { + dump( it->first, out ); + out << " | "; + dump( *it->second, out ); + } + } + + static void dump( const std::vector<Symbol>& symbols, std::ostream& out ) + { + typedef std::vector<Symbol>::const_iterator iter_t; + iter_t end = symbols.end(); + for( iter_t it = symbols.begin(); it != end; ++it ) + { + dump( *it, out ); + out << std::endl; + } + } + + static void dump( const SolverImpl::VarMap& vars, std::ostream& out ) + { + typedef SolverImpl::VarMap::const_iterator iter_t; + iter_t end = vars.end(); + for( iter_t it = vars.begin(); it != end; ++it ) + { + out << it->first.name() << " = "; + dump( it->second, out ); + out << std::endl; + } + } + + static void dump( const SolverImpl::CnMap& cns, std::ostream& out ) + { + typedef SolverImpl::CnMap::const_iterator iter_t; + iter_t end = cns.end(); + for( iter_t it = cns.begin(); it != end; ++it ) + dump( it->first, out ); + } + + static void dump( const SolverImpl::EditMap& edits, std::ostream& out ) + { + typedef SolverImpl::EditMap::const_iterator iter_t; + iter_t end = edits.end(); + for( iter_t it = edits.begin(); it != end; ++it ) + out << it->first.name() << std::endl; + } + + static void dump( const Row& row, std::ostream& out ) + { + typedef Row::CellMap::const_iterator iter_t; + out << row.constant(); + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + out << " + " << it->second << " * "; + dump( it->first, out ); + } + out << std::endl; + } + + static void dump( const Symbol& symbol, std::ostream& out ) + { + switch( symbol.type() ) + { + case Symbol::Invalid: + out << "i"; + break; + case Symbol::External: + out << "v"; + break; + case Symbol::Slack: + out << "s"; + break; + case Symbol::Error: + out << "e"; + break; + case Symbol::Dummy: + out << "d"; + break; + default: + break; + } + out << symbol.id(); + } + + static void dump( const Constraint& cn, std::ostream& out ) + { + typedef std::vector<Term>::const_iterator iter_t; + iter_t begin = cn.expression().terms().begin(); + iter_t end = cn.expression().terms().end(); + for( iter_t it = begin; it != end; ++it ) + { + out << it->coefficient() << " * "; + out << it->variable().name() << " + "; + } + out << cn.expression().constant(); + switch( cn.op() ) + { + case OP_LE: + out << " <= 0 "; + break; + case OP_GE: + out << " >= 0 "; + break; + case OP_EQ: + out << " == 0 "; + break; + default: + break; + } + out << " | strength = " << cn.strength() << std::endl; + } +}; + +} // namespace impl + + +namespace debug +{ + +template<typename T> +void dump( const T& value ) +{ + impl::DebugHelper::dump( value, std::cout ); +} + +template<typename T> +void dump( const T& value, std::ostream& out ) +{ + impl::DebugHelper::dump( value, out ); +} + +template<typename T> +std::string dumps( const T& value ) +{ + std::stringstream stream; + impl::DebugHelper::dump( value, stream ); + return stream.str(); +} + +} // namespace debug + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/errors.h b/contrib/python/kiwisolver/py2/kiwi/errors.h new file mode 100644 index 00000000000..6c77eee0b70 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/errors.h @@ -0,0 +1,188 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <exception> +#include <string> +#include "constraint.h" +#include "variable.h" + + +namespace kiwi +{ + + +class UnsatisfiableConstraint : public std::exception +{ + +public: + + UnsatisfiableConstraint( const Constraint& constraint ) : + m_constraint( constraint ) {} + + ~UnsatisfiableConstraint() throw() {} + + const char* what() const throw() + { + return "The constraint can not be satisfied."; + } + + const Constraint& constraint() const + { + return m_constraint; + } + +private: + + Constraint m_constraint; +}; + + +class UnknownConstraint : public std::exception +{ + +public: + + UnknownConstraint( const Constraint& constraint ) : + m_constraint( constraint ) {} + + ~UnknownConstraint() throw() {} + + const char* what() const throw() + { + return "The constraint has not been added to the solver."; + } + + const Constraint& constraint() const + { + return m_constraint; + } + +private: + + Constraint m_constraint; +}; + + +class DuplicateConstraint : public std::exception +{ + +public: + + DuplicateConstraint( const Constraint& constraint ) : + m_constraint( constraint ) {} + + ~DuplicateConstraint() throw() {} + + const char* what() const throw() + { + return "The constraint has already been added to the solver."; + } + + const Constraint& constraint() const + { + return m_constraint; + } + +private: + + Constraint m_constraint; +}; + + +class UnknownEditVariable : public std::exception +{ + +public: + + UnknownEditVariable( const Variable& variable ) : + m_variable( variable ) {} + + ~UnknownEditVariable() throw() {} + + const char* what() const throw() + { + return "The edit variable has not been added to the solver."; + } + + const Variable& variable() const + { + return m_variable; + } + +private: + + Variable m_variable; +}; + + +class DuplicateEditVariable : public std::exception +{ + +public: + + DuplicateEditVariable( const Variable& variable ) : + m_variable( variable ) {} + + ~DuplicateEditVariable() throw() {} + + const char* what() const throw() + { + return "The edit variable has already been added to the solver."; + } + + const Variable& variable() const + { + return m_variable; + } + +private: + + Variable m_variable; +}; + + +class BadRequiredStrength : public std::exception +{ + +public: + + BadRequiredStrength() {} + + ~BadRequiredStrength() throw() {} + + const char* what() const throw() + { + return "A required strength cannot be used in this context."; + } +}; + + +class InternalSolverError : public std::exception +{ + +public: + + InternalSolverError() : m_msg( "An internal solver error ocurred." ) {} + + InternalSolverError( const char* msg ) : m_msg( msg ) {} + + InternalSolverError( const std::string& msg ) : m_msg( msg ) {} + + ~InternalSolverError() throw() {} + + const char* what() const throw() + { + return m_msg.c_str(); + } + +private: + + std::string m_msg; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/expression.h b/contrib/python/kiwisolver/py2/kiwi/expression.h new file mode 100644 index 00000000000..6d757206bd7 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/expression.h @@ -0,0 +1,57 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <vector> +#include "term.h" + + +namespace kiwi +{ + +class Expression +{ + +public: + + Expression( double constant = 0.0 ) : m_constant( constant ) {} + + Expression( const Term& term, double constant = 0.0 ) : + m_terms( 1, term ), m_constant( constant ) {} + + Expression( const std::vector<Term>& terms, double constant = 0.0 ) : + m_terms( terms ), m_constant( constant ) {} + + ~Expression() {} + + const std::vector<Term>& terms() const + { + return m_terms; + } + + double constant() const + { + return m_constant; + } + + double value() const + { + typedef std::vector<Term>::const_iterator iter_t; + double result = m_constant; + iter_t end = m_terms.end(); + for( iter_t it = m_terms.begin(); it != end; ++it ) + result += it->value(); + return result; + } + +private: + + std::vector<Term> m_terms; + double m_constant; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/kiwi.h b/contrib/python/kiwisolver/py2/kiwi/kiwi.h new file mode 100644 index 00000000000..e5e3d85c90b --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/kiwi.h @@ -0,0 +1,19 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include "constraint.h" +#include "debug.h" +#include "errors.h" +#include "expression.h" +#include "shareddata.h" +#include "solver.h" +#include "strength.h" +#include "symbolics.h" +#include "term.h" +#include "variable.h" +#include "version.h" diff --git a/contrib/python/kiwisolver/py2/kiwi/maptype.h b/contrib/python/kiwisolver/py2/kiwi/maptype.h new file mode 100644 index 00000000000..d125b07cc2d --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/maptype.h @@ -0,0 +1,38 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <functional> +#include <map> +#include <memory> +#include <utility> +#include "AssocVector.h" + + +namespace kiwi +{ + +namespace impl +{ + +template< + typename K, + typename V, + typename C = std::less<K>, + typename A = std::allocator< std::pair<K, V> > > +class MapType +{ +public: + typedef Loki::AssocVector<K, V, C, A> Type; + //typedef std::map<K, V, C, A> Type; +private: + MapType(); +}; + +} // namespace impl + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/row.h b/contrib/python/kiwisolver/py2/kiwi/row.h new file mode 100644 index 00000000000..33b501df76c --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/row.h @@ -0,0 +1,192 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include "maptype.h" +#include "symbol.h" +#include "util.h" + + +namespace kiwi +{ + +namespace impl +{ + +class Row +{ + +public: + + typedef MapType<Symbol, double>::Type CellMap; + + Row() : m_constant( 0.0 ) {} + + Row( double constant ) : m_constant( constant ) {} + + Row( const Row& other ) : + m_cells( other.m_cells ), m_constant( other.m_constant ) {} + + ~Row() {} + + const CellMap& cells() const + { + return m_cells; + } + + double constant() const + { + return m_constant; + } + + /* Add a constant value to the row constant. + + The new value of the constant is returned. + + */ + double add( double value ) + { + return m_constant += value; + } + + /* Insert a symbol into the row with a given coefficient. + + If the symbol already exists in the row, the coefficient will be + added to the existing coefficient. If the resulting coefficient + is zero, the symbol will be removed from the row. + + */ + void insert( const Symbol& symbol, double coefficient = 1.0 ) + { + if( nearZero( m_cells[ symbol ] += coefficient ) ) + m_cells.erase( symbol ); + } + + /* Insert a row into this row with a given coefficient. + + The constant and the cells of the other row will be multiplied by + the coefficient and added to this row. Any cell with a resulting + coefficient of zero will be removed from the row. + + */ + void insert( const Row& other, double coefficient = 1.0 ) + { + typedef CellMap::const_iterator iter_t; + m_constant += other.m_constant * coefficient; + iter_t end = other.m_cells.end(); + for( iter_t it = other.m_cells.begin(); it != end; ++it ) + { + double coeff = it->second * coefficient; + if( nearZero( m_cells[ it->first ] += coeff ) ) + m_cells.erase( it->first ); + } + } + + /* Remove the given symbol from the row. + + */ + void remove( const Symbol& symbol ) + { + CellMap::iterator it = m_cells.find( symbol ); + if( it != m_cells.end() ) + m_cells.erase( it ); + } + + /* Reverse the sign of the constant and all cells in the row. + + */ + void reverseSign() + { + typedef CellMap::iterator iter_t; + m_constant = -m_constant; + iter_t end = m_cells.end(); + for( iter_t it = m_cells.begin(); it != end; ++it ) + it->second = -it->second; + } + + /* Solve the row for the given symbol. + + This method assumes the row is of the form a * x + b * y + c = 0 + and (assuming solve for x) will modify the row to represent the + right hand side of x = -b/a * y - c / a. The target symbol will + be removed from the row, and the constant and other cells will + be multiplied by the negative inverse of the target coefficient. + + The given symbol *must* exist in the row. + + */ + void solveFor( const Symbol& symbol ) + { + typedef CellMap::iterator iter_t; + double coeff = -1.0 / m_cells[ symbol ]; + m_cells.erase( symbol ); + m_constant *= coeff; + iter_t end = m_cells.end(); + for( iter_t it = m_cells.begin(); it != end; ++it ) + it->second *= coeff; + } + + /* Solve the row for the given symbols. + + This method assumes the row is of the form x = b * y + c and will + solve the row such that y = x / b - c / b. The rhs symbol will be + removed from the row, the lhs added, and the result divided by the + negative inverse of the rhs coefficient. + + The lhs symbol *must not* exist in the row, and the rhs symbol + *must* exist in the row. + + */ + void solveFor( const Symbol& lhs, const Symbol& rhs ) + { + insert( lhs, -1.0 ); + solveFor( rhs ); + } + + /* Get the coefficient for the given symbol. + + If the symbol does not exist in the row, zero will be returned. + + */ + double coefficientFor( const Symbol& symbol ) const + { + CellMap::const_iterator it = m_cells.find( symbol ); + if( it == m_cells.end() ) + return 0.0; + return it->second; + } + + /* Substitute a symbol with the data from another row. + + Given a row of the form a * x + b and a substitution of the + form x = 3 * y + c the row will be updated to reflect the + expression 3 * a * y + a * c + b. + + If the symbol does not exist in the row, this is a no-op. + + */ + void substitute( const Symbol& symbol, const Row& row ) + { + typedef CellMap::iterator iter_t; + iter_t it = m_cells.find( symbol ); + if( it != m_cells.end() ) + { + double coefficient = it->second; + m_cells.erase( it ); + insert( row, coefficient ); + } + } + +private: + + CellMap m_cells; + double m_constant; +}; + +} // namespace impl + +} // namespace diff --git a/contrib/python/kiwisolver/py2/kiwi/shareddata.h b/contrib/python/kiwisolver/py2/kiwi/shareddata.h new file mode 100644 index 00000000000..b7d389ee6dc --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/shareddata.h @@ -0,0 +1,157 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once + + +namespace kiwi +{ + +class SharedData +{ + +public: + + SharedData() : m_refcount( 0 ) {} + + SharedData( const SharedData& other ) : m_refcount( 0 ) {} + + int m_refcount; + +private: + + SharedData& operator=( const SharedData& other ); +}; + + +template<typename T> +class SharedDataPtr +{ + +public: + + typedef T Type; + + SharedDataPtr() : m_data( 0 ) {} + + explicit SharedDataPtr( T* data ) : m_data( data ) + { + incref( m_data ); + } + + ~SharedDataPtr() + { + decref( m_data ); + } + + T* data() + { + return m_data; + } + + const T* data() const + { + return m_data; + } + + operator T*() + { + return m_data; + } + + operator const T*() const + { + return m_data; + } + + T* operator->() + { + return m_data; + } + + const T* operator->() const + { + return m_data; + } + + T& operator*() + { + return *m_data; + } + + const T& operator*() const + { + return *m_data; + } + + bool operator!() const + { + return !m_data; + } + + bool operator<( const SharedDataPtr<T>& other ) const + { + return m_data < other.m_data; + } + + bool operator==( const SharedDataPtr<T>& other ) const + { + return m_data == other.m_data; + } + + bool operator!=( const SharedDataPtr<T>& other ) const + { + return m_data != other.m_data; + } + + SharedDataPtr( const SharedDataPtr<T>& other ) : m_data( other.m_data ) + { + incref( m_data ); + } + + SharedDataPtr<T>& operator=( const SharedDataPtr<T>& other ) + { + if( m_data != other.m_data ) + { + T* temp = m_data; + m_data = other.m_data; + incref( m_data ); + decref( temp ); + } + return *this; + } + + SharedDataPtr<T>& operator=( T* other ) + { + if( m_data != other ) + { + T* temp = m_data; + m_data = other; + incref( m_data ); + decref( temp ); + } + return *this; + } + +private: + + static void incref( T* data ) + { + if( data ) + ++data->m_refcount; + } + + static void decref( T* data ) + { + if( data && --data->m_refcount == 0 ) + delete data; + } + + T* m_data; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/solver.h b/contrib/python/kiwisolver/py2/kiwi/solver.h new file mode 100644 index 00000000000..8eed1841bf9 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/solver.h @@ -0,0 +1,178 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include "constraint.h" +#include "debug.h" +#include "solverimpl.h" +#include "strength.h" +#include "variable.h" + + +namespace kiwi +{ + +class Solver +{ + +public: + + Solver() {} + + ~Solver() {} + + /* Add a constraint to the solver. + + Throws + ------ + DuplicateConstraint + The given constraint has already been added to the solver. + + UnsatisfiableConstraint + The given constraint is required and cannot be satisfied. + + */ + void addConstraint( const Constraint& constraint ) + { + m_impl.addConstraint( constraint ); + } + + /* Remove a constraint from the solver. + + Throws + ------ + UnknownConstraint + The given constraint has not been added to the solver. + + */ + void removeConstraint( const Constraint& constraint ) + { + m_impl.removeConstraint( constraint ); + } + + /* Test whether a constraint has been added to the solver. + + */ + bool hasConstraint( const Constraint& constraint ) const + { + return m_impl.hasConstraint( constraint ); + } + + /* Add an edit variable to the solver. + + This method should be called before the `suggestValue` method is + used to supply a suggested value for the given edit variable. + + Throws + ------ + DuplicateEditVariable + The given edit variable has already been added to the solver. + + BadRequiredStrength + The given strength is >= required. + + */ + void addEditVariable( const Variable& variable, double strength ) + { + m_impl.addEditVariable( variable, strength ); + } + + /* Remove an edit variable from the solver. + + Throws + ------ + UnknownEditVariable + The given edit variable has not been added to the solver. + + */ + void removeEditVariable( const Variable& variable ) + { + m_impl.removeEditVariable( variable ); + } + + /* Test whether an edit variable has been added to the solver. + + */ + bool hasEditVariable( const Variable& variable ) const + { + return m_impl.hasEditVariable( variable ); + } + + /* Suggest a value for the given edit variable. + + This method should be used after an edit variable as been added to + the solver in order to suggest the value for that variable. After + all suggestions have been made, the `solve` method can be used to + update the values of all variables. + + Throws + ------ + UnknownEditVariable + The given edit variable has not been added to the solver. + + */ + void suggestValue( const Variable& variable, double value ) + { + m_impl.suggestValue( variable, value ); + } + + /* Update the values of the external solver variables. + + */ + void updateVariables() + { + m_impl.updateVariables(); + } + + /* Reset the solver to the empty starting condition. + + This method resets the internal solver state to the empty starting + condition, as if no constraints or edit variables have been added. + This can be faster than deleting the solver and creating a new one + when the entire system must change, since it can avoid unecessary + heap (de)allocations. + + */ + void reset() + { + m_impl.reset(); + } + + /* Dump a representation of the solver internals to stdout. + + */ + void dump() + { + debug::dump( m_impl ); + } + + /* Dump a representation of the solver internals to a stream. + + */ + void dump( std::ostream& out ) + { + debug::dump( m_impl, out ); + } + + /* Dump a representation of the solver internals to a string. + + */ + std::string dumps() + { + return debug::dumps( m_impl ); + } + +private: + + Solver( const Solver& ); + + Solver& operator=( const Solver& ); + + impl::SolverImpl m_impl; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/solverimpl.h b/contrib/python/kiwisolver/py2/kiwi/solverimpl.h new file mode 100644 index 00000000000..9b56ee28188 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/solverimpl.h @@ -0,0 +1,840 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <algorithm> +#include <limits> +#include <memory> +#include <vector> +#include "constraint.h" +#include "errors.h" +#include "expression.h" +#include "maptype.h" +#include "row.h" +#include "symbol.h" +#include "term.h" +#include "util.h" +#include "variable.h" + + +namespace kiwi +{ + +namespace impl +{ + +class SolverImpl +{ + friend class DebugHelper; + + struct Tag + { + Symbol marker; + Symbol other; + }; + + struct EditInfo + { + Tag tag; + Constraint constraint; + double constant; + }; + + typedef MapType<Variable, Symbol>::Type VarMap; + + typedef MapType<Symbol, Row*>::Type RowMap; + + typedef MapType<Constraint, Tag>::Type CnMap; + + typedef MapType<Variable, EditInfo>::Type EditMap; + + struct DualOptimizeGuard + { + DualOptimizeGuard( SolverImpl& impl ) : m_impl( impl ) {} + ~DualOptimizeGuard() { m_impl.dualOptimize(); } + SolverImpl& m_impl; + }; + +public: + + SolverImpl() : m_objective( new Row() ), m_id_tick( 1 ) {} + + ~SolverImpl() { clearRows(); } + + /* Add a constraint to the solver. + + Throws + ------ + DuplicateConstraint + The given constraint has already been added to the solver. + + UnsatisfiableConstraint + The given constraint is required and cannot be satisfied. + + */ + void addConstraint( const Constraint& constraint ) + { + if( m_cns.find( constraint ) != m_cns.end() ) + throw DuplicateConstraint( constraint ); + + // Creating a row causes symbols to be reserved for the variables + // in the constraint. If this method exits with an exception, + // then its possible those variables will linger in the var map. + // Since its likely that those variables will be used in other + // constraints and since exceptional conditions are uncommon, + // i'm not too worried about aggressive cleanup of the var map. + Tag tag; + std::auto_ptr<Row> rowptr( createRow( constraint, tag ) ); + Symbol subject( chooseSubject( *rowptr, tag ) ); + + // If chooseSubject could not find a valid entering symbol, one + // last option is available if the entire row is composed of + // dummy variables. If the constant of the row is zero, then + // this represents redundant constraints and the new dummy + // marker can enter the basis. If the constant is non-zero, + // then it represents an unsatisfiable constraint. + if( subject.type() == Symbol::Invalid && allDummies( *rowptr ) ) + { + if( !nearZero( rowptr->constant() ) ) + throw UnsatisfiableConstraint( constraint ); + else + subject = tag.marker; + } + + // If an entering symbol still isn't found, then the row must + // be added using an artificial variable. If that fails, then + // the row represents an unsatisfiable constraint. + if( subject.type() == Symbol::Invalid ) + { + if( !addWithArtificialVariable( *rowptr ) ) + throw UnsatisfiableConstraint( constraint ); + } + else + { + rowptr->solveFor( subject ); + substitute( subject, *rowptr ); + m_rows[ subject ] = rowptr.release(); + } + + m_cns[ constraint ] = tag; + + // Optimizing after each constraint is added performs less + // aggregate work due to a smaller average system size. It + // also ensures the solver remains in a consistent state. + optimize( *m_objective ); + } + + /* Remove a constraint from the solver. + + Throws + ------ + UnknownConstraint + The given constraint has not been added to the solver. + + */ + void removeConstraint( const Constraint& constraint ) + { + CnMap::iterator cn_it = m_cns.find( constraint ); + if( cn_it == m_cns.end() ) + throw UnknownConstraint( constraint ); + + Tag tag( cn_it->second ); + m_cns.erase( cn_it ); + + // Remove the error effects from the objective function + // *before* pivoting, or substitutions into the objective + // will lead to incorrect solver results. + removeConstraintEffects( constraint, tag ); + + // If the marker is basic, simply drop the row. Otherwise, + // pivot the marker into the basis and then drop the row. + RowMap::iterator row_it = m_rows.find( tag.marker ); + if( row_it != m_rows.end() ) + { + std::auto_ptr<Row> rowptr( row_it->second ); + m_rows.erase( row_it ); + } + else + { + row_it = getMarkerLeavingRow( tag.marker ); + if( row_it == m_rows.end() ) + throw InternalSolverError( "failed to find leaving row" ); + Symbol leaving( row_it->first ); + std::auto_ptr<Row> rowptr( row_it->second ); + m_rows.erase( row_it ); + rowptr->solveFor( leaving, tag.marker ); + substitute( tag.marker, *rowptr ); + } + + // Optimizing after each constraint is removed ensures that the + // solver remains consistent. It makes the solver api easier to + // use at a small tradeoff for speed. + optimize( *m_objective ); + } + + /* Test whether a constraint has been added to the solver. + + */ + bool hasConstraint( const Constraint& constraint ) const + { + return m_cns.find( constraint ) != m_cns.end(); + } + + /* Add an edit variable to the solver. + + This method should be called before the `suggestValue` method is + used to supply a suggested value for the given edit variable. + + Throws + ------ + DuplicateEditVariable + The given edit variable has already been added to the solver. + + BadRequiredStrength + The given strength is >= required. + + */ + void addEditVariable( const Variable& variable, double strength ) + { + if( m_edits.find( variable ) != m_edits.end() ) + throw DuplicateEditVariable( variable ); + strength = strength::clip( strength ); + if( strength == strength::required ) + throw BadRequiredStrength(); + Constraint cn( Expression( variable ), OP_EQ, strength ); + addConstraint( cn ); + EditInfo info; + info.tag = m_cns[ cn ]; + info.constraint = cn; + info.constant = 0.0; + m_edits[ variable ] = info; + } + + /* Remove an edit variable from the solver. + + Throws + ------ + UnknownEditVariable + The given edit variable has not been added to the solver. + + */ + void removeEditVariable( const Variable& variable ) + { + EditMap::iterator it = m_edits.find( variable ); + if( it == m_edits.end() ) + throw UnknownEditVariable( variable ); + removeConstraint( it->second.constraint ); + m_edits.erase( it ); + } + + /* Test whether an edit variable has been added to the solver. + + */ + bool hasEditVariable( const Variable& variable ) const + { + return m_edits.find( variable ) != m_edits.end(); + } + + /* Suggest a value for the given edit variable. + + This method should be used after an edit variable as been added to + the solver in order to suggest the value for that variable. + + Throws + ------ + UnknownEditVariable + The given edit variable has not been added to the solver. + + */ + void suggestValue( const Variable& variable, double value ) + { + EditMap::iterator it = m_edits.find( variable ); + if( it == m_edits.end() ) + throw UnknownEditVariable( variable ); + + DualOptimizeGuard guard( *this ); + EditInfo& info = it->second; + double delta = value - info.constant; + info.constant = value; + + // Check first if the positive error variable is basic. + RowMap::iterator row_it = m_rows.find( info.tag.marker ); + if( row_it != m_rows.end() ) + { + if( row_it->second->add( -delta ) < 0.0 ) + m_infeasible_rows.push_back( row_it->first ); + return; + } + + // Check next if the negative error variable is basic. + row_it = m_rows.find( info.tag.other ); + if( row_it != m_rows.end() ) + { + if( row_it->second->add( delta ) < 0.0 ) + m_infeasible_rows.push_back( row_it->first ); + return; + } + + // Otherwise update each row where the error variables exist. + RowMap::iterator end = m_rows.end(); + for( row_it = m_rows.begin(); row_it != end; ++row_it ) + { + double coeff = row_it->second->coefficientFor( info.tag.marker ); + if( coeff != 0.0 && + row_it->second->add( delta * coeff ) < 0.0 && + row_it->first.type() != Symbol::External ) + m_infeasible_rows.push_back( row_it->first ); + } + } + + /* Update the values of the external solver variables. + + */ + void updateVariables() + { + typedef RowMap::iterator row_iter_t; + typedef VarMap::iterator var_iter_t; + row_iter_t row_end = m_rows.end(); + var_iter_t var_end = m_vars.end(); + for( var_iter_t var_it = m_vars.begin(); var_it != var_end; ++var_it ) + { + Variable& var( const_cast<Variable&>( var_it->first ) ); + row_iter_t row_it = m_rows.find( var_it->second ); + if( row_it == row_end ) + var.setValue( 0.0 ); + else + var.setValue( row_it->second->constant() ); + } + } + + /* Reset the solver to the empty starting condition. + + This method resets the internal solver state to the empty starting + condition, as if no constraints or edit variables have been added. + This can be faster than deleting the solver and creating a new one + when the entire system must change, since it can avoid unecessary + heap (de)allocations. + + */ + void reset() + { + clearRows(); + m_cns.clear(); + m_vars.clear(); + m_edits.clear(); + m_infeasible_rows.clear(); + m_objective.reset( new Row() ); + m_artificial.reset(); + m_id_tick = 1; + } + +private: + + SolverImpl( const SolverImpl& ); + + SolverImpl& operator=( const SolverImpl& ); + + struct RowDeleter + { + template<typename T> + void operator()( T& pair ) { delete pair.second; } + }; + + void clearRows() + { + std::for_each( m_rows.begin(), m_rows.end(), RowDeleter() ); + m_rows.clear(); + } + + /* Get the symbol for the given variable. + + If a symbol does not exist for the variable, one will be created. + + */ + Symbol getVarSymbol( const Variable& variable ) + { + VarMap::iterator it = m_vars.find( variable ); + if( it != m_vars.end() ) + return it->second; + Symbol symbol( Symbol::External, m_id_tick++ ); + m_vars[ variable ] = symbol; + return symbol; + } + + /* Create a new Row object for the given constraint. + + The terms in the constraint will be converted to cells in the row. + Any term in the constraint with a coefficient of zero is ignored. + This method uses the `getVarSymbol` method to get the symbol for + the variables added to the row. If the symbol for a given cell + variable is basic, the cell variable will be substituted with the + basic row. + + The necessary slack and error variables will be added to the row. + If the constant for the row is negative, the sign for the row + will be inverted so the constant becomes positive. + + The tag will be updated with the marker and error symbols to use + for tracking the movement of the constraint in the tableau. + + */ + Row* createRow( const Constraint& constraint, Tag& tag ) + { + typedef std::vector<Term>::const_iterator iter_t; + const Expression& expr( constraint.expression() ); + Row* row = new Row( expr.constant() ); + + // Substitute the current basic variables into the row. + iter_t end = expr.terms().end(); + for( iter_t it = expr.terms().begin(); it != end; ++it ) + { + if( !nearZero( it->coefficient() ) ) + { + Symbol symbol( getVarSymbol( it->variable() ) ); + RowMap::const_iterator row_it = m_rows.find( symbol ); + if( row_it != m_rows.end() ) + row->insert( *row_it->second, it->coefficient() ); + else + row->insert( symbol, it->coefficient() ); + } + } + + // Add the necessary slack, error, and dummy variables. + switch( constraint.op() ) + { + case OP_LE: + case OP_GE: + { + double coeff = constraint.op() == OP_LE ? 1.0 : -1.0; + Symbol slack( Symbol::Slack, m_id_tick++ ); + tag.marker = slack; + row->insert( slack, coeff ); + if( constraint.strength() < strength::required ) + { + Symbol error( Symbol::Error, m_id_tick++ ); + tag.other = error; + row->insert( error, -coeff ); + m_objective->insert( error, constraint.strength() ); + } + break; + } + case OP_EQ: + { + if( constraint.strength() < strength::required ) + { + Symbol errplus( Symbol::Error, m_id_tick++ ); + Symbol errminus( Symbol::Error, m_id_tick++ ); + tag.marker = errplus; + tag.other = errminus; + row->insert( errplus, -1.0 ); // v = eplus - eminus + row->insert( errminus, 1.0 ); // v - eplus + eminus = 0 + m_objective->insert( errplus, constraint.strength() ); + m_objective->insert( errminus, constraint.strength() ); + } + else + { + Symbol dummy( Symbol::Dummy, m_id_tick++ ); + tag.marker = dummy; + row->insert( dummy ); + } + break; + } + } + + // Ensure the row as a positive constant. + if( row->constant() < 0.0 ) + row->reverseSign(); + + return row; + } + + /* Choose the subject for solving for the row. + + This method will choose the best subject for using as the solve + target for the row. An invalid symbol will be returned if there + is no valid target. + + The symbols are chosen according to the following precedence: + + 1) The first symbol representing an external variable. + 2) A negative slack or error tag variable. + + If a subject cannot be found, an invalid symbol will be returned. + + */ + Symbol chooseSubject( const Row& row, const Tag& tag ) + { + typedef Row::CellMap::const_iterator iter_t; + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + if( it->first.type() == Symbol::External ) + return it->first; + } + if( tag.marker.type() == Symbol::Slack || tag.marker.type() == Symbol::Error ) + { + if( row.coefficientFor( tag.marker ) < 0.0 ) + return tag.marker; + } + if( tag.other.type() == Symbol::Slack || tag.other.type() == Symbol::Error ) + { + if( row.coefficientFor( tag.other ) < 0.0 ) + return tag.other; + } + return Symbol(); + } + + /* Add the row to the tableau using an artificial variable. + + This will return false if the constraint cannot be satisfied. + + */ + bool addWithArtificialVariable( const Row& row ) + { + // Create and add the artificial variable to the tableau + Symbol art( Symbol::Slack, m_id_tick++ ); + m_rows[ art ] = new Row( row ); + m_artificial.reset( new Row( row ) ); + + // Optimize the artificial objective. This is successful + // only if the artificial objective is optimized to zero. + optimize( *m_artificial ); + bool success = nearZero( m_artificial->constant() ); + m_artificial.reset(); + + // If the artificial variable is not basic, pivot the row so that + // it becomes basic. If the row is constant, exit early. + RowMap::iterator it = m_rows.find( art ); + if( it != m_rows.end() ) + { + std::auto_ptr<Row> rowptr( it->second ); + m_rows.erase( it ); + if( rowptr->cells().empty() ) + return success; + Symbol entering( anyPivotableSymbol( *rowptr ) ); + if( entering.type() == Symbol::Invalid ) + return false; // unsatisfiable (will this ever happen?) + rowptr->solveFor( art, entering ); + substitute( entering, *rowptr ); + m_rows[ entering ] = rowptr.release(); + } + + // Remove the artificial variable from the tableau. + RowMap::iterator end = m_rows.end(); + for( it = m_rows.begin(); it != end; ++it ) + it->second->remove( art ); + m_objective->remove( art ); + return success; + } + + /* Substitute the parametric symbol with the given row. + + This method will substitute all instances of the parametric symbol + in the tableau and the objective function with the given row. + + */ + void substitute( const Symbol& symbol, const Row& row ) + { + typedef RowMap::iterator iter_t; + iter_t end = m_rows.end(); + for( iter_t it = m_rows.begin(); it != end; ++it ) + { + it->second->substitute( symbol, row ); + if( it->first.type() != Symbol::External && + it->second->constant() < 0.0 ) + m_infeasible_rows.push_back( it->first ); + } + m_objective->substitute( symbol, row ); + if( m_artificial.get() ) + m_artificial->substitute( symbol, row ); + } + + /* Optimize the system for the given objective function. + + This method performs iterations of Phase 2 of the simplex method + until the objective function reaches a minimum. + + Throws + ------ + InternalSolverError + The value of the objective function is unbounded. + + */ + void optimize( const Row& objective ) + { + while( true ) + { + Symbol entering( getEnteringSymbol( objective ) ); + if( entering.type() == Symbol::Invalid ) + return; + RowMap::iterator it = getLeavingRow( entering ); + if( it == m_rows.end() ) + throw InternalSolverError( "The objective is unbounded." ); + // pivot the entering symbol into the basis + Symbol leaving( it->first ); + Row* row = it->second; + m_rows.erase( it ); + row->solveFor( leaving, entering ); + substitute( entering, *row ); + m_rows[ entering ] = row; + } + } + + /* Optimize the system using the dual of the simplex method. + + The current state of the system should be such that the objective + function is optimal, but not feasible. This method will perform + an iteration of the dual simplex method to make the solution both + optimal and feasible. + + Throws + ------ + InternalSolverError + The system cannot be dual optimized. + + */ + void dualOptimize() + { + while( !m_infeasible_rows.empty() ) + { + + Symbol leaving( m_infeasible_rows.back() ); + m_infeasible_rows.pop_back(); + RowMap::iterator it = m_rows.find( leaving ); + if( it != m_rows.end() && !nearZero( it->second->constant() ) && + it->second->constant() < 0.0 ) + { + Symbol entering( getDualEnteringSymbol( *it->second ) ); + if( entering.type() == Symbol::Invalid ) + throw InternalSolverError( "Dual optimize failed." ); + // pivot the entering symbol into the basis + Row* row = it->second; + m_rows.erase( it ); + row->solveFor( leaving, entering ); + substitute( entering, *row ); + m_rows[ entering ] = row; + } + } + } + + /* Compute the entering variable for a pivot operation. + + This method will return first symbol in the objective function which + is non-dummy and has a coefficient less than zero. If no symbol meets + the criteria, it means the objective function is at a minimum, and an + invalid symbol is returned. + + */ + Symbol getEnteringSymbol( const Row& objective ) + { + typedef Row::CellMap::const_iterator iter_t; + iter_t end = objective.cells().end(); + for( iter_t it = objective.cells().begin(); it != end; ++it ) + { + if( it->first.type() != Symbol::Dummy && it->second < 0.0 ) + return it->first; + } + return Symbol(); + } + + /* Compute the entering symbol for the dual optimize operation. + + This method will return the symbol in the row which has a positive + coefficient and yields the minimum ratio for its respective symbol + in the objective function. The provided row *must* be infeasible. + If no symbol is found which meats the criteria, an invalid symbol + is returned. + + */ + Symbol getDualEnteringSymbol( const Row& row ) + { + typedef Row::CellMap::const_iterator iter_t; + Symbol entering; + double ratio = std::numeric_limits<double>::max(); + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + if( it->second > 0.0 && it->first.type() != Symbol::Dummy ) + { + double coeff = m_objective->coefficientFor( it->first ); + double r = coeff / it->second; + if( r < ratio ) + { + ratio = r; + entering = it->first; + } + } + } + return entering; + } + + /* Get the first Slack or Error symbol in the row. + + If no such symbol is present, and Invalid symbol will be returned. + + */ + Symbol anyPivotableSymbol( const Row& row ) + { + typedef Row::CellMap::const_iterator iter_t; + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + const Symbol& sym( it->first ); + if( sym.type() == Symbol::Slack || sym.type() == Symbol::Error ) + return sym; + } + return Symbol(); + } + + /* Compute the row which holds the exit symbol for a pivot. + + This method will return an iterator to the row in the row map + which holds the exit symbol. If no appropriate exit symbol is + found, the end() iterator will be returned. This indicates that + the objective function is unbounded. + + */ + RowMap::iterator getLeavingRow( const Symbol& entering ) + { + typedef RowMap::iterator iter_t; + double ratio = std::numeric_limits<double>::max(); + iter_t end = m_rows.end(); + iter_t found = m_rows.end(); + for( iter_t it = m_rows.begin(); it != end; ++it ) + { + if( it->first.type() != Symbol::External ) + { + double temp = it->second->coefficientFor( entering ); + if( temp < 0.0 ) + { + double temp_ratio = -it->second->constant() / temp; + if( temp_ratio < ratio ) + { + ratio = temp_ratio; + found = it; + } + } + } + } + return found; + } + + /* Compute the leaving row for a marker variable. + + This method will return an iterator to the row in the row map + which holds the given marker variable. The row will be chosen + according to the following precedence: + + 1) The row with a restricted basic varible and a negative coefficient + for the marker with the smallest ratio of -constant / coefficient. + + 2) The row with a restricted basic variable and the smallest ratio + of constant / coefficient. + + 3) The last unrestricted row which contains the marker. + + If the marker does not exist in any row, the row map end() iterator + will be returned. This indicates an internal solver error since + the marker *should* exist somewhere in the tableau. + + */ + RowMap::iterator getMarkerLeavingRow( const Symbol& marker ) + { + const double dmax = std::numeric_limits<double>::max(); + typedef RowMap::iterator iter_t; + double r1 = dmax; + double r2 = dmax; + iter_t end = m_rows.end(); + iter_t first = end; + iter_t second = end; + iter_t third = end; + for( iter_t it = m_rows.begin(); it != end; ++it ) + { + double c = it->second->coefficientFor( marker ); + if( c == 0.0 ) + continue; + if( it->first.type() == Symbol::External ) + { + third = it; + } + else if( c < 0.0 ) + { + double r = -it->second->constant() / c; + if( r < r1 ) + { + r1 = r; + first = it; + } + } + else + { + double r = it->second->constant() / c; + if( r < r2 ) + { + r2 = r; + second = it; + } + } + } + if( first != end ) + return first; + if( second != end ) + return second; + return third; + } + + /* Remove the effects of a constraint on the objective function. + + */ + void removeConstraintEffects( const Constraint& cn, const Tag& tag ) + { + if( tag.marker.type() == Symbol::Error ) + removeMarkerEffects( tag.marker, cn.strength() ); + if( tag.other.type() == Symbol::Error ) + removeMarkerEffects( tag.other, cn.strength() ); + } + + /* Remove the effects of an error marker on the objective function. + + */ + void removeMarkerEffects( const Symbol& marker, double strength ) + { + RowMap::iterator row_it = m_rows.find( marker ); + if( row_it != m_rows.end() ) + m_objective->insert( *row_it->second, -strength ); + else + m_objective->insert( marker, -strength ); + } + + /* Test whether a row is composed of all dummy variables. + + */ + bool allDummies( const Row& row ) + { + typedef Row::CellMap::const_iterator iter_t; + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + if( it->first.type() != Symbol::Dummy ) + return false; + } + return true; + } + + CnMap m_cns; + RowMap m_rows; + VarMap m_vars; + EditMap m_edits; + std::vector<Symbol> m_infeasible_rows; + std::auto_ptr<Row> m_objective; + std::auto_ptr<Row> m_artificial; + Symbol::Id m_id_tick; +}; + +} // namespace impl + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/strength.h b/contrib/python/kiwisolver/py2/kiwi/strength.h new file mode 100644 index 00000000000..b077f3e17c1 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/strength.h @@ -0,0 +1,44 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <algorithm> + + +namespace kiwi +{ + +namespace strength +{ + +inline double create( double a, double b, double c, double w = 1.0 ) +{ + double result = 0.0; + result += std::max( 0.0, std::min( 1000.0, a * w ) ) * 1000000.0; + result += std::max( 0.0, std::min( 1000.0, b * w ) ) * 1000.0; + result += std::max( 0.0, std::min( 1000.0, c * w ) ); + return result; +} + + +const double required = create( 1000.0, 1000.0, 1000.0 ); + +const double strong = create( 1.0, 0.0, 0.0 ); + +const double medium = create( 0.0, 1.0, 0.0 ); + +const double weak = create( 0.0, 0.0, 1.0 ); + + +inline double clip( double value ) +{ + return std::max( 0.0, std::min( required, value ) ); +} + +} // namespace strength + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/symbol.h b/contrib/python/kiwisolver/py2/kiwi/symbol.h new file mode 100644 index 00000000000..b971efbdd25 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/symbol.h @@ -0,0 +1,68 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once + + +namespace kiwi +{ + +namespace impl +{ + +class Symbol +{ + +public: + + typedef unsigned long long Id; + + enum Type + { + Invalid, + External, + Slack, + Error, + Dummy + }; + + Symbol() : m_id( 0 ), m_type( Invalid ) {} + + Symbol( Type type, Id id ) : m_id( id ), m_type( type ) {} + + ~Symbol() {} + + Id id() const + { + return m_id; + } + + Type type() const + { + return m_type; + } + +private: + + Id m_id; + Type m_type; + + friend bool operator<( const Symbol& lhs, const Symbol& rhs ) + { + return lhs.m_id < rhs.m_id; + } + + friend bool operator==( const Symbol& lhs, const Symbol& rhs ) + { + return lhs.m_id == rhs.m_id; + } + +}; + +} // namespace impl + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/symbolics.h b/contrib/python/kiwisolver/py2/kiwi/symbolics.h new file mode 100644 index 00000000000..51ac6a8a37b --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/symbolics.h @@ -0,0 +1,685 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <vector> +#include "constraint.h" +#include "expression.h" +#include "term.h" +#include "variable.h" + + +namespace kiwi +{ + +// Variable multiply, divide, and unary invert + +inline +Term operator*( const Variable& variable, double coefficient ) +{ + return Term( variable, coefficient ); +} + + +inline +Term operator/( const Variable& variable, double denominator ) +{ + return variable * ( 1.0 / denominator ); +} + + +inline +Term operator-( const Variable& variable ) +{ + return variable * -1.0; +} + + +// Term multiply, divide, and unary invert + +inline +Term operator*( const Term& term, double coefficient ) +{ + return Term( term.variable(), term.coefficient() * coefficient ); +} + + +inline +Term operator/( const Term& term, double denominator ) +{ + return term * ( 1.0 / denominator ); +} + + +inline +Term operator-( const Term& term ) +{ + return term * -1.0; +} + + +// Expression multiply, divide, and unary invert + +inline +Expression operator*( const Expression& expression, double coefficient ) +{ + std::vector<Term> terms; + terms.reserve( expression.terms().size() ); + typedef std::vector<Term>::const_iterator iter_t; + iter_t begin = expression.terms().begin(); + iter_t end = expression.terms().end(); + for( iter_t it = begin; it != end; ++it ) + terms.push_back( ( *it ) * coefficient ); + return Expression( terms, expression.constant() * coefficient ); +} + + +inline +Expression operator/( const Expression& expression, double denominator ) +{ + return expression * ( 1.0 / denominator ); +} + + +inline +Expression operator-( const Expression& expression ) +{ + return expression * -1.0; +} + + +// Double multiply + +inline +Expression operator*( double coefficient, const Expression& expression ) +{ + return expression * coefficient; +} + + +inline +Term operator*( double coefficient, const Term& term ) +{ + return term * coefficient; +} + + +inline +Term operator*( double coefficient, const Variable& variable ) +{ + return variable * coefficient; +} + + +// Expression add and subtract + +inline +Expression operator+( const Expression& first, const Expression& second ) +{ + std::vector<Term> terms; + terms.reserve( first.terms().size() + second.terms().size() ); + terms.insert( terms.begin(), first.terms().begin(), first.terms().end() ); + terms.insert( terms.end(), second.terms().begin(), second.terms().end() ); + return Expression( terms, first.constant() + second.constant() ); +} + + +inline +Expression operator+( const Expression& first, const Term& second ) +{ + std::vector<Term> terms; + terms.reserve( first.terms().size() + 1 ); + terms.insert( terms.begin(), first.terms().begin(), first.terms().end() ); + terms.push_back( second ); + return Expression( terms, first.constant() ); +} + + +inline +Expression operator+( const Expression& expression, const Variable& variable ) +{ + return expression + Term( variable ); +} + + +inline +Expression operator+( const Expression& expression, double constant ) +{ + return Expression( expression.terms(), expression.constant() + constant ); +} + + +inline +Expression operator-( const Expression& first, const Expression& second ) +{ + return first + -second; +} + + +inline +Expression operator-( const Expression& expression, const Term& term ) +{ + return expression + -term; +} + + +inline +Expression operator-( const Expression& expression, const Variable& variable ) +{ + return expression + -variable; +} + + +inline +Expression operator-( const Expression& expression, double constant ) +{ + return expression + -constant; +} + + +// Term add and subtract + +inline +Expression operator+( const Term& term, const Expression& expression ) +{ + return expression + term; +} + + +inline +Expression operator+( const Term& first, const Term& second ) +{ + std::vector<Term> terms; + terms.reserve( 2 ); + terms.push_back( first ); + terms.push_back( second ); + return Expression( terms ); +} + + +inline +Expression operator+( const Term& term, const Variable& variable ) +{ + return term + Term( variable ); +} + + +inline +Expression operator+( const Term& term, double constant ) +{ + return Expression( term, constant ); +} + + +inline +Expression operator-( const Term& term, const Expression& expression ) +{ + return -expression + term; +} + + +inline +Expression operator-( const Term& first, const Term& second ) +{ + return first + -second; +} + + +inline +Expression operator-( const Term& term, const Variable& variable ) +{ + return term + -variable; +} + + +inline +Expression operator-( const Term& term, double constant ) +{ + return term + -constant; +} + + +// Variable add and subtract + +inline +Expression operator+( const Variable& variable, const Expression& expression ) +{ + return expression + variable; +} + + +inline +Expression operator+( const Variable& variable, const Term& term ) +{ + return term + variable; +} + + +inline +Expression operator+( const Variable& first, const Variable& second ) +{ + return Term( first ) + second; +} + + +inline +Expression operator+( const Variable& variable, double constant ) +{ + return Term( variable ) + constant; +} + + +inline +Expression operator-( const Variable& variable, const Expression& expression ) +{ + return variable + -expression; +} + + +inline +Expression operator-( const Variable& variable, const Term& term ) +{ + return variable + -term; +} + + +inline +Expression operator-( const Variable& first, const Variable& second ) +{ + return first + -second; +} + + +inline +Expression operator-( const Variable& variable, double constant ) +{ + return variable + -constant; +} + + +// Double add and subtract + +inline +Expression operator+( double constant, const Expression& expression ) +{ + return expression + constant; +} + + +inline +Expression operator+( double constant, const Term& term ) +{ + return term + constant; +} + + +inline +Expression operator+( double constant, const Variable& variable ) +{ + return variable + constant; +} + + +inline +Expression operator-( double constant, const Expression& expression ) +{ + return -expression + constant; +} + + +inline +Expression operator-( double constant, const Term& term ) +{ + return -term + constant; +} + + +inline +Expression operator-( double constant, const Variable& variable ) +{ + return -variable + constant; +} + + +// Expression relations + +inline +Constraint operator==( const Expression& first, const Expression& second ) +{ + return Constraint( first - second, OP_EQ ); +} + + +inline +Constraint operator==( const Expression& expression, const Term& term ) +{ + return expression == Expression( term ); +} + + +inline +Constraint operator==( const Expression& expression, const Variable& variable ) +{ + return expression == Term( variable ); +} + + +inline +Constraint operator==( const Expression& expression, double constant ) +{ + return expression == Expression( constant ); +} + + +inline +Constraint operator<=( const Expression& first, const Expression& second ) +{ + return Constraint( first - second, OP_LE ); +} + + +inline +Constraint operator<=( const Expression& expression, const Term& term ) +{ + return expression <= Expression( term ); +} + + +inline +Constraint operator<=( const Expression& expression, const Variable& variable ) +{ + return expression <= Term( variable ); +} + + +inline +Constraint operator<=( const Expression& expression, double constant ) +{ + return expression <= Expression( constant ); +} + + +inline +Constraint operator>=( const Expression& first, const Expression& second ) +{ + return Constraint( first - second, OP_GE ); +} + + +inline +Constraint operator>=( const Expression& expression, const Term& term ) +{ + return expression >= Expression( term ); +} + + +inline +Constraint operator>=( const Expression& expression, const Variable& variable ) +{ + return expression >= Term( variable ); +} + + +inline +Constraint operator>=( const Expression& expression, double constant ) +{ + return expression >= Expression( constant ); +} + + +// Term relations + +inline +Constraint operator==( const Term& term, const Expression& expression ) +{ + return expression == term; +} + + +inline +Constraint operator==( const Term& first, const Term& second ) +{ + return Expression( first ) == second; +} + + +inline +Constraint operator==( const Term& term, const Variable& variable ) +{ + return Expression( term ) == variable; +} + + +inline +Constraint operator==( const Term& term, double constant ) +{ + return Expression( term ) == constant; +} + + +inline +Constraint operator<=( const Term& term, const Expression& expression ) +{ + return expression >= term; +} + + +inline +Constraint operator<=( const Term& first, const Term& second ) +{ + return Expression( first ) <= second; +} + + +inline +Constraint operator<=( const Term& term, const Variable& variable ) +{ + return Expression( term ) <= variable; +} + + +inline +Constraint operator<=( const Term& term, double constant ) +{ + return Expression( term ) <= constant; +} + + +inline +Constraint operator>=( const Term& term, const Expression& expression ) +{ + return expression <= term; +} + + +inline +Constraint operator>=( const Term& first, const Term& second ) +{ + return Expression( first ) >= second; +} + + +inline +Constraint operator>=( const Term& term, const Variable& variable ) +{ + return Expression( term ) >= variable; +} + + +inline +Constraint operator>=( const Term& term, double constant ) +{ + return Expression( term ) >= constant; +} + + +// Variable relations +inline +Constraint operator==( const Variable& variable, const Expression& expression ) +{ + return expression == variable; +} + + +inline +Constraint operator==( const Variable& variable, const Term& term ) +{ + return term == variable; +} + + +inline +Constraint operator==( const Variable& first, const Variable& second ) +{ + return Term( first ) == second; +} + + +inline +Constraint operator==( const Variable& variable, double constant ) +{ + return Term( variable ) == constant; +} + + +inline +Constraint operator<=( const Variable& variable, const Expression& expression ) +{ + return expression >= variable; +} + + +inline +Constraint operator<=( const Variable& variable, const Term& term ) +{ + return term >= variable; +} + + +inline +Constraint operator<=( const Variable& first, const Variable& second ) +{ + return Term( first ) <= second; +} + + +inline +Constraint operator<=( const Variable& variable, double constant ) +{ + return Term( variable ) <= constant; +} + + +inline +Constraint operator>=( const Variable& variable, const Expression& expression ) +{ + return expression <= variable; +} + + +inline +Constraint operator>=( const Variable& variable, const Term& term ) +{ + return term <= variable; +} + + +inline +Constraint operator>=( const Variable& first, const Variable& second ) +{ + return Term( first ) >= second; +} + + +inline +Constraint operator>=( const Variable& variable, double constant ) +{ + return Term( variable ) >= constant; +} + + +// Double relations + +inline +Constraint operator==( double constant, const Expression& expression ) +{ + return expression == constant; +} + + +inline +Constraint operator==( double constant, const Term& term ) +{ + return term == constant; +} + + +inline +Constraint operator==( double constant, const Variable& variable ) +{ + return variable == constant; +} + + +inline +Constraint operator<=( double constant, const Expression& expression ) +{ + return expression >= constant; +} + + +inline +Constraint operator<=( double constant, const Term& term ) +{ + return term >= constant; +} + + +inline +Constraint operator<=( double constant, const Variable& variable ) +{ + return variable >= constant; +} + + +inline +Constraint operator>=( double constant, const Expression& expression ) +{ + return expression <= constant; +} + + +inline +Constraint operator>=( double constant, const Term& term ) +{ + return term <= constant; +} + + +inline +Constraint operator>=( double constant, const Variable& variable ) +{ + return variable <= constant; +} + + +// Constraint strength modifier + +inline +Constraint operator|( const Constraint& constraint, double strength ) +{ + return Constraint( constraint, strength ); +} + + +inline +Constraint operator|( double strength, const Constraint& constraint ) +{ + return constraint | strength; +} + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/term.h b/contrib/python/kiwisolver/py2/kiwi/term.h new file mode 100644 index 00000000000..1ee962287cc --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/term.h @@ -0,0 +1,51 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <utility> +#include "variable.h" + + +namespace kiwi +{ + +class Term +{ + +public: + + Term( const Variable& variable, double coefficient = 1.0 ) : + m_variable( variable ), m_coefficient( coefficient ) {} + + // to facilitate efficient map -> vector copies + Term( const std::pair<const Variable, double>& pair ) : + m_variable( pair.first ), m_coefficient( pair.second ) {} + + ~Term() {} + + const Variable& variable() const + { + return m_variable; + } + + double coefficient() const + { + return m_coefficient; + } + + double value() const + { + return m_coefficient * m_variable.value(); + } + +private: + + Variable m_variable; + double m_coefficient; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/util.h b/contrib/python/kiwisolver/py2/kiwi/util.h new file mode 100644 index 00000000000..0d36a33eb15 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/util.h @@ -0,0 +1,25 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once + + +namespace kiwi +{ + +namespace impl +{ + +inline bool nearZero( double value ) +{ + const double eps = 1.0e-8; + return value < 0.0 ? -value < eps : value < eps; +} + +} // namespace impl + +} // namespace diff --git a/contrib/python/kiwisolver/py2/kiwi/variable.h b/contrib/python/kiwisolver/py2/kiwi/variable.h new file mode 100644 index 00000000000..eaee93f2a13 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/variable.h @@ -0,0 +1,121 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <memory> +#include <string> +#include "shareddata.h" + + +namespace kiwi +{ + +class Variable +{ + +public: + + class Context + { + public: + Context() {} + virtual ~Context() {} // LCOV_EXCL_LINE + }; + + Variable( Context* context = 0 ) : + m_data( new VariableData( "", context ) ) {} + + Variable( const std::string& name, Context* context = 0 ) : + m_data( new VariableData( name, context ) ) {} + + Variable( const char* name, Context* context = 0 ) : + m_data( new VariableData( name, context ) ) {} + + ~Variable() {} + + const std::string& name() const + { + return m_data->m_name; + } + + void setName( const char* name ) + { + m_data->m_name = name; + } + + void setName( const std::string& name ) + { + m_data->m_name = name; + } + + Context* context() const + { + return m_data->m_context.get(); + } + + void setContext( Context* context ) + { + m_data->m_context.reset( context ); + } + + double value() const + { + return m_data->m_value; + } + + void setValue( double value ) + { + m_data->m_value = value; + } + + // operator== is used for symbolics + bool equals( const Variable& other ) + { + return m_data == other.m_data; + } + +private: + + class VariableData : public SharedData + { + + public: + + VariableData( const std::string& name, Context* context ) : + SharedData(), + m_name( name ), + m_context( context ), + m_value( 0.0 ) {} + + VariableData( const char* name, Context* context ) : + SharedData(), + m_name( name ), + m_context( context ), + m_value( 0.0 ) {} + + ~VariableData() {} + + std::string m_name; + std::auto_ptr<Context> m_context; + double m_value; + + private: + + VariableData( const VariableData& other ); + + VariableData& operator=( const VariableData& other ); + }; + + SharedDataPtr<VariableData> m_data; + + friend bool operator<( const Variable& lhs, const Variable& rhs ) + { + return lhs.m_data < rhs.m_data; + } +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py2/kiwi/version.h b/contrib/python/kiwisolver/py2/kiwi/version.h new file mode 100644 index 00000000000..197f3ce1869 --- /dev/null +++ b/contrib/python/kiwisolver/py2/kiwi/version.h @@ -0,0 +1,14 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once + +#define KIWI_MAJOR_VERSION 1 +#define KIWI_MINOR_VERSION 0 +#define KIWI_MICRO_VERSION 1 +#define KIWI_VERSION_HEX 0x010001 +#define KIWI_VERSION "1.1.0" diff --git a/contrib/python/kiwisolver/py2/py/constraint.cpp b/contrib/python/kiwisolver/py2/py/constraint.cpp new file mode 100644 index 00000000000..2fbdd0aeb83 --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/constraint.cpp @@ -0,0 +1,299 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <algorithm> +#include <sstream> +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + static const char *kwlist[] = { "expression", "op", "strength", 0 }; + PyObject* pyexpr; + PyObject* pyop; + PyObject* pystrength = 0; + if( !PyArg_ParseTupleAndKeywords( + args, kwargs, "OO|O:__new__", const_cast<char**>( kwlist ), + &pyexpr, &pyop, &pystrength ) ) + return 0; + if( !Expression::TypeCheck( pyexpr ) ) + return py_expected_type_fail( pyexpr, "Expression" ); + kiwi::RelationalOperator op; + if( !convert_to_relational_op( pyop, op ) ) + return 0; + double strength = kiwi::strength::required; + if( pystrength && !convert_to_strength( pystrength, strength ) ) + return 0; + PyObjectPtr pycn( PyType_GenericNew( type, args, kwargs ) ); + if( !pycn ) + return 0; + Constraint* cn = reinterpret_cast<Constraint*>( pycn.get() ); + cn->expression = reduce_expression( pyexpr ); + if( !cn->expression ) + return 0; + kiwi::Expression expr( convert_to_kiwi_expression( cn->expression ) ); + new( &cn->constraint ) kiwi::Constraint( expr, op, strength ); + return pycn.release(); +} + + +static void +Constraint_clear( Constraint* self ) +{ + Py_CLEAR( self->expression ); +} + + +static int +Constraint_traverse( Constraint* self, visitproc visit, void* arg ) +{ + Py_VISIT( self->expression ); + return 0; +} + + +static void +Constraint_dealloc( Constraint* self ) +{ + PyObject_GC_UnTrack( self ); + Constraint_clear( self ); + self->constraint.~Constraint(); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Constraint_repr( Constraint* self ) +{ + std::stringstream stream; + Expression* expr = reinterpret_cast<Expression*>( self->expression ); + Py_ssize_t size = PyTuple_GET_SIZE( expr->terms ); + for( Py_ssize_t i = 0; i < size; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( expr->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + stream << term->coefficient << " * "; + stream << reinterpret_cast<Variable*>( term->variable )->variable.name(); + stream << " + "; + } + stream << expr->constant; + switch( self->constraint.op() ) + { + case kiwi::OP_EQ: + stream << " == 0"; + break; + case kiwi::OP_LE: + stream << " <= 0"; + break; + case kiwi::OP_GE: + stream << " >= 0"; + break; + } + stream << " | strength = " << self->constraint.strength(); + return FROM_STRING( stream.str().c_str() ); +} + + +static PyObject* +Constraint_expression( Constraint* self ) +{ + return newref( self->expression ); +} + + +static PyObject* +Constraint_op( Constraint* self ) +{ + PyObject* res = 0; + switch( self->constraint.op() ) + { + case kiwi::OP_EQ: + res = FROM_STRING( "==" ); + break; + case kiwi::OP_LE: + res = FROM_STRING( "<=" ); + break; + case kiwi::OP_GE: + res = FROM_STRING( ">=" ); + break; + } + return res; +} + + +static PyObject* +Constraint_strength( Constraint* self ) +{ + return PyFloat_FromDouble( self->constraint.strength() ); +} + + +static PyObject* +Constraint_or( PyObject* pyoldcn, PyObject* value ) +{ + if( !Constraint::TypeCheck( pyoldcn ) ) + std::swap( pyoldcn, value ); + double strength; + if( !convert_to_strength( value, strength ) ) + return 0; + PyObject* pynewcn = PyType_GenericNew( &Constraint_Type, 0, 0 ); + if( !pynewcn ) + return 0; + Constraint* oldcn = reinterpret_cast<Constraint*>( pyoldcn ); + Constraint* newcn = reinterpret_cast<Constraint*>( pynewcn ); + newcn->expression = newref( oldcn->expression ); + new( &newcn->constraint ) kiwi::Constraint( oldcn->constraint, strength ); + return pynewcn; +} + + +static PyMethodDef +Constraint_methods[] = { + { "expression", ( PyCFunction )Constraint_expression, METH_NOARGS, + "Get the expression object for the constraint." }, + { "op", ( PyCFunction )Constraint_op, METH_NOARGS, + "Get the relational operator for the constraint." }, + { "strength", ( PyCFunction )Constraint_strength, METH_NOARGS, + "Get the strength for the constraint." }, + { 0 } // sentinel +}; + + +static PyNumberMethods +Constraint_as_number = { + 0, /* nb_add */ + 0, /* nb_subtract */ + 0, /* nb_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_divide */ +#endif + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ +#if PY_MAJOR_VERSION >= 3 + 0, /* nb_bool */ +#else + 0, /* nb_nonzero */ +#endif + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + (binaryfunc)Constraint_or, /* nb_or */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_coerce */ +#endif + 0, /* nb_int */ + 0, /* nb_long */ + 0, /* nb_float */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_oct */ + 0, /* nb_hex */ +#endif + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_inplace_divide */ +#endif + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)0, /* nb_floor_divide */ + (binaryfunc)0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +#if PY_VERSION_HEX >= 0x02050000 + (unaryfunc)0, /* nb_index */ +#endif +#if PY_VERSION_HEX >= 0x03050000 + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ +#endif +}; + + +PyTypeObject Constraint_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Constraint", /* tp_name */ + sizeof( Constraint ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Constraint_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)Constraint_repr, /* tp_repr */ + (PyNumberMethods*)&Constraint_as_number,/* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + 0, /* Documentation string */ + (traverseproc)Constraint_traverse, /* tp_traverse */ + (inquiry)Constraint_clear, /* tp_clear */ + (richcmpfunc)0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Constraint_methods,/* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Constraint_new, /* tp_new */ + (freefunc)PyObject_GC_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_constraint() +{ + return PyType_Ready( &Constraint_Type ); +} diff --git a/contrib/python/kiwisolver/py2/py/expression.cpp b/contrib/python/kiwisolver/py2/py/expression.cpp new file mode 100644 index 00000000000..5cd34e4c120 --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/expression.cpp @@ -0,0 +1,326 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <sstream> +#include <Python.h> +#include "pythonhelpers.h" +#include "symbolics.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + static const char *kwlist[] = { "terms", "constant", 0 }; + PyObject* pyterms; + PyObject* pyconstant = 0; + if( !PyArg_ParseTupleAndKeywords( + args, kwargs, "O|O:__new__", const_cast<char**>( kwlist ), + &pyterms, &pyconstant ) ) + return 0; + PyObjectPtr terms( PySequence_Tuple( pyterms ) ); + if( !terms ) + return 0; + Py_ssize_t end = PyTuple_GET_SIZE( terms.get() ); + for( Py_ssize_t i = 0; i < end; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( terms.get(), i ); + if( !Term::TypeCheck( item ) ) + return py_expected_type_fail( item, "Term" ); + } + double constant = 0.0; + if( pyconstant && !convert_to_double( pyconstant, constant ) ) + return 0; + PyObject* pyexpr = PyType_GenericNew( type, args, kwargs ); + if( !pyexpr ) + return 0; + Expression* self = reinterpret_cast<Expression*>( pyexpr ); + self->terms = terms.release(); + self->constant = constant; + return pyexpr; +} + + +static void +Expression_clear( Expression* self ) +{ + Py_CLEAR( self->terms ); +} + + +static int +Expression_traverse( Expression* self, visitproc visit, void* arg ) +{ + Py_VISIT( self->terms ); + return 0; +} + + +static void +Expression_dealloc( Expression* self ) +{ + PyObject_GC_UnTrack( self ); + Expression_clear( self ); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Expression_repr( Expression* self ) +{ + std::stringstream stream; + Py_ssize_t end = PyTuple_GET_SIZE( self->terms ); + for( Py_ssize_t i = 0; i < end; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( self->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + stream << term->coefficient << " * "; + stream << reinterpret_cast<Variable*>( term->variable )->variable.name(); + stream << " + "; + } + stream << self->constant; + return FROM_STRING( stream.str().c_str() ); +} + + +static PyObject* +Expression_terms( Expression* self ) +{ + return newref( self->terms ); +} + + +static PyObject* +Expression_constant( Expression* self ) +{ + return PyFloat_FromDouble( self->constant ); +} + + +static PyObject* +Expression_value( Expression* self ) +{ + double result = self->constant; + Py_ssize_t size = PyTuple_GET_SIZE( self->terms ); + for( Py_ssize_t i = 0; i < size; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( self->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + Variable* pyvar = reinterpret_cast<Variable*>( term->variable ); + result += term->coefficient * pyvar->variable.value(); + } + return PyFloat_FromDouble( result ); +} + + +static PyObject* +Expression_add( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryAdd, Expression>()( first, second ); +} + + +static PyObject* +Expression_sub( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinarySub, Expression>()( first, second ); +} + + +static PyObject* +Expression_mul( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryMul, Expression>()( first, second ); +} + + +static PyObject* +Expression_div( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryDiv, Expression>()( first, second ); +} + + +static PyObject* +Expression_neg( PyObject* value ) +{ + return UnaryInvoke<UnaryNeg, Expression>()( value ); +} + + +static PyObject* +Expression_richcmp( PyObject* first, PyObject* second, int op ) +{ + switch( op ) + { + case Py_EQ: + return BinaryInvoke<CmpEQ, Expression>()( first, second ); + case Py_LE: + return BinaryInvoke<CmpLE, Expression>()( first, second ); + case Py_GE: + return BinaryInvoke<CmpGE, Expression>()( first, second ); + default: + break; + } + PyErr_Format( + PyExc_TypeError, + "unsupported operand type(s) for %s: " + "'%.100s' and '%.100s'", + pyop_str( op ), + first->ob_type->tp_name, + second->ob_type->tp_name + ); + return 0; +} + + +static PyMethodDef +Expression_methods[] = { + { "terms", ( PyCFunction )Expression_terms, METH_NOARGS, + "Get the tuple of terms for the expression." }, + { "constant", ( PyCFunction )Expression_constant, METH_NOARGS, + "Get the constant for the expression." }, + { "value", ( PyCFunction )Expression_value, METH_NOARGS, + "Get the value for the expression." }, + { 0 } // sentinel +}; + + +static PyNumberMethods +Expression_as_number = { + (binaryfunc)Expression_add, /* nb_add */ + (binaryfunc)Expression_sub, /* nb_subtract */ + (binaryfunc)Expression_mul, /* nb_multiply */ +#if PY_MAJOR_VERSION < 3 + (binaryfunc)Expression_div, /* nb_divide */ +#endif + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + (unaryfunc)Expression_neg, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ +#if PY_MAJOR_VERSION >= 3 + 0, /* nb_bool */ +#else + 0, /* nb_nonzero */ +#endif + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + (binaryfunc)0, /* nb_or */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_coerce */ +#endif + 0, /* nb_int */ +#if PY_MAJOR_VERSION >= 3 + (void *)0, /* nb_reserved */ +#else + 0, /* nb_long */ +#endif + 0, /* nb_float */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_oct */ + 0, /* nb_hex */ +#endif + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_inplace_divide */ +#endif + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)0, /* nb_floor_divide */ + (binaryfunc)Expression_div, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +#if PY_VERSION_HEX >= 0x02050000 + (unaryfunc)0, /* nb_index */ +#endif +#if PY_VERSION_HEX >= 0x03050000 + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ +#endif +}; + + +PyTypeObject Expression_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Expression", /* tp_name */ + sizeof( Expression ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Expression_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)Expression_repr, /* tp_repr */ + (PyNumberMethods*)&Expression_as_number,/* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, +#else + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + 0, /* Documentation string */ + (traverseproc)Expression_traverse, /* tp_traverse */ + (inquiry)Expression_clear, /* tp_clear */ + (richcmpfunc)Expression_richcmp, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Expression_methods,/* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Expression_new, /* tp_new */ + (freefunc)PyObject_GC_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_expression() +{ + return PyType_Ready( &Expression_Type ); +} diff --git a/contrib/python/kiwisolver/py2/py/kiwisolver.cpp b/contrib/python/kiwisolver/py2/py/kiwisolver.cpp new file mode 100644 index 00000000000..54b333a2c8b --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/kiwisolver.cpp @@ -0,0 +1,86 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "types.h" + +#define PY_KIWI_VERSION "1.1.0" + +using namespace PythonHelpers; + +static PyMethodDef +kiwisolver_methods[] = { + { 0 } // Sentinel +}; + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef kiwisolver_moduledef = { + PyModuleDef_HEAD_INIT, + "kiwisolver", + NULL, + sizeof( struct module_state ), + kiwisolver_methods, + NULL +}; + +PyMODINIT_FUNC +PyInit_kiwisolver( void ) +#else +PyMODINIT_FUNC +initkiwisolver( void ) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *mod = PyModule_Create( &kiwisolver_moduledef ); +#else + PyObject* mod = Py_InitModule( "kiwisolver", kiwisolver_methods ); +#endif + if( !mod ) + INITERROR; + if( import_variable() < 0 ) + INITERROR; + if( import_term() < 0 ) + INITERROR; + if( import_expression() < 0 ) + INITERROR; + if( import_constraint() < 0 ) + INITERROR; + if( import_solver() < 0 ) + INITERROR; + if( import_strength() < 0 ) + INITERROR; + PyObject* kiwiversion = FROM_STRING( KIWI_VERSION ); + if( !kiwiversion ) + INITERROR; + PyObject* pyversion = FROM_STRING( PY_KIWI_VERSION ); + if( !pyversion ) + INITERROR; + PyObject* pystrength = PyType_GenericNew( &strength_Type, 0, 0 ); + if( !pystrength ) + INITERROR; + + PyModule_AddObject( mod, "__version__", pyversion ); + PyModule_AddObject( mod, "__kiwi_version__", kiwiversion ); + PyModule_AddObject( mod, "strength", pystrength ); + PyModule_AddObject( mod, "Variable", newref( pyobject_cast( &Variable_Type ) ) ); + PyModule_AddObject( mod, "Term", newref( pyobject_cast( &Term_Type ) ) ); + PyModule_AddObject( mod, "Expression", newref( pyobject_cast( &Expression_Type ) ) ); + PyModule_AddObject( mod, "Constraint", newref( pyobject_cast( &Constraint_Type ) ) ); + PyModule_AddObject( mod, "Solver", newref( pyobject_cast( &Solver_Type ) ) ); + PyModule_AddObject( mod, "DuplicateConstraint", newref( DuplicateConstraint ) ); + PyModule_AddObject( mod, "UnsatisfiableConstraint", newref( UnsatisfiableConstraint ) ); + PyModule_AddObject( mod, "UnknownConstraint", newref( UnknownConstraint ) ); + PyModule_AddObject( mod, "DuplicateEditVariable", newref( DuplicateEditVariable ) ); + PyModule_AddObject( mod, "UnknownEditVariable", newref( UnknownEditVariable ) ); + PyModule_AddObject( mod, "BadRequiredStrength", newref( BadRequiredStrength ) ); + +#if PY_MAJOR_VERSION >= 3 + return mod; +#endif +} diff --git a/contrib/python/kiwisolver/py2/py/pythonhelpers.h b/contrib/python/kiwisolver/py2/py/pythonhelpers.h new file mode 100644 index 00000000000..a9e0db634d7 --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/pythonhelpers.h @@ -0,0 +1,771 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <Python.h> +#include <structmember.h> +#include <string> + +#if PY_MAJOR_VERSION >= 3 +#define FROM_STRING PyUnicode_FromString +#define INITERROR return NULL +#define MOD_INIT_FUNC(name) PyMODINIT_FUNC PyInit_##name(void) +#else +#define FROM_STRING PyString_FromString +#define INITERROR return +#define MOD_INIT_FUNC(name) PyMODINIT_FUNC init##name(void) +#endif + +#ifndef Py_RETURN_NOTIMPLEMENTED +#define Py_RETURN_NOTIMPLEMENTED \ + return Py_INCREF(Py_NotImplemented), Py_NotImplemented +#endif + +#define pyobject_cast( o ) ( reinterpret_cast<PyObject*>( o ) ) +#define pytype_cast( o ) ( reinterpret_cast<PyTypeObject*>( o ) ) + +struct module_state { + PyObject *error; +}; + + +namespace PythonHelpers +{ + + +/*----------------------------------------------------------------------------- +| Exception Handling +|----------------------------------------------------------------------------*/ +inline PyObject* +py_bad_internal_call( const char* message ) +{ + PyErr_SetString( PyExc_SystemError, message ); + return 0; +} + + +inline PyObject* +py_type_fail( const char* message ) +{ + PyErr_SetString( PyExc_TypeError, message ); + return 0; +} + + +inline PyObject* +py_expected_type_fail( PyObject* pyobj, const char* expected_type ) +{ + PyErr_Format( + PyExc_TypeError, + "Expected object of type `%s`. Got object of type `%s` instead.", + expected_type, pyobj->ob_type->tp_name + ); + return 0; +} + + +inline PyObject* +py_value_fail( const char* message ) +{ + PyErr_SetString( PyExc_ValueError, message ); + return 0; +} + + +inline PyObject* +py_runtime_fail( const char* message ) +{ + PyErr_SetString( PyExc_RuntimeError, message ); + return 0; +} + + +inline PyObject* +py_attr_fail( const char* message ) +{ + PyErr_SetString( PyExc_AttributeError, message ); + return 0; +} + + +inline PyObject* +py_no_attr_fail( PyObject* pyobj, const char* attr ) +{ + PyErr_Format( + PyExc_AttributeError, + "'%s' object has no attribute '%s'", + pyobj->ob_type->tp_name, attr + ); + return 0; +} + + +/*----------------------------------------------------------------------------- +| Utilities +|----------------------------------------------------------------------------*/ +inline PyObject* +newref( PyObject* pyobj ) +{ + Py_INCREF( pyobj ); + return pyobj; +} + + +inline PyObject* +xnewref( PyObject* pyobj ) +{ + Py_XINCREF( pyobj ); + return pyobj; +} + + +inline PyObject* +py_bool( bool val ) +{ + return newref( val ? Py_True : Py_False ); +} + + +inline PyCFunction +lookup_method( PyTypeObject* type, const char* name ) +{ + PyMethodDef* method = type->tp_methods; + for( ; method->ml_name != 0; ++method ) + { + if( strcmp( method->ml_name, name ) == 0 ) + return method->ml_meth; + } + return 0; +} + + +/*----------------------------------------------------------------------------- +| Object Ptr +|----------------------------------------------------------------------------*/ +class PyObjectPtr { + +public: + + PyObjectPtr() : m_pyobj( 0 ) {} + + PyObjectPtr( const PyObjectPtr& objptr ) : + m_pyobj( PythonHelpers::xnewref( objptr.m_pyobj ) ) {} + + PyObjectPtr( PyObject* pyobj ) : m_pyobj( pyobj ) {} + + ~PyObjectPtr() + { + xdecref_release(); + } + + PyObject* get() const + { + return m_pyobj; + } + + void set( PyObject* pyobj ) + { + PyObject* old = m_pyobj; + m_pyobj = pyobj; + Py_XDECREF( old ); + } + + PyObject* release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + return pyobj; + } + + PyObject* decref_release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + Py_DECREF( pyobj ); + return pyobj; + } + + PyObject* xdecref_release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + Py_XDECREF( pyobj ); + return pyobj; + } + + PyObject* incref_release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + Py_INCREF( pyobj ); + return pyobj; + } + + PyObject* xincref_release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + Py_XINCREF( pyobj ); + return pyobj; + } + + void incref() const + { + Py_INCREF( m_pyobj ); + } + + void decref() const + { + Py_DECREF( m_pyobj ); + } + + void xincref() const + { + Py_XINCREF( m_pyobj ); + } + + void xdecref() const + { + Py_XDECREF( m_pyobj ); + } + + PyObject* newref() const + { + Py_INCREF( m_pyobj ); + return m_pyobj; + } + + PyObject* xnewref() const + { + Py_XINCREF( m_pyobj ); + return m_pyobj; + } + + size_t refcount() const + { + if( m_pyobj ) + return m_pyobj->ob_refcnt; + return 0; + } + + bool is_true( bool clear_err=true ) const + { + int truth = PyObject_IsTrue( m_pyobj ); + if( truth == 1 ) + return true; + if( truth == 0 ) + return false; + if( clear_err ) + PyErr_Clear(); + return false; + } + + bool is_None() const + { + return m_pyobj == Py_None; + } + + bool is_True() const + { + return m_pyobj == Py_True; + } + + bool is_False() const + { + return m_pyobj == Py_False; + } + + bool load_dict( PyObjectPtr& out, bool forcecreate=false ) + { + PyObject** dict = _PyObject_GetDictPtr( m_pyobj ); + if( !dict ) + return false; + if( forcecreate && !*dict ) + *dict = PyDict_New(); + out = PythonHelpers::xnewref( *dict ); + return true; + } + + bool richcompare( PyObject* other, int opid, bool clear_err=true ) + { + int r = PyObject_RichCompareBool( m_pyobj, other, opid ); + if( r == 1 ) + return true; + if( r == 0 ) + return false; + if( clear_err && PyErr_Occurred() ) + PyErr_Clear(); + return false; + } + + bool richcompare( PyObjectPtr& other, int opid, bool clear_err=true ) + { + return richcompare( other.m_pyobj, opid, clear_err ); + } + + bool hasattr( PyObject* attr ) + { + return PyObject_HasAttr( m_pyobj, attr ) == 1; + } + + bool hasattr( PyObjectPtr& attr ) + { + return PyObject_HasAttr( m_pyobj, attr.get() ) == 1; + } + + bool hasattr( const char* attr ) + { + return PyObject_HasAttrString( m_pyobj, attr ) == 1; + } + + bool hasattr( std::string& attr ) + { + return hasattr( attr.c_str() ); + } + + PyObjectPtr getattr( PyObject* attr ) + { + return PyObjectPtr( PyObject_GetAttr( m_pyobj, attr ) ); + } + + PyObjectPtr getattr( PyObjectPtr& attr ) + { + return PyObjectPtr( PyObject_GetAttr( m_pyobj, attr.get() ) ); + } + + PyObjectPtr getattr( const char* attr ) + { + return PyObjectPtr( PyObject_GetAttrString( m_pyobj, attr ) ); + } + + PyObjectPtr getattr( std::string& attr ) + { + return getattr( attr.c_str() ); + } + + bool setattr( PyObject* attr, PyObject* value ) + { + return PyObject_SetAttr( m_pyobj, attr, value ) == 0; + } + + bool setattr( PyObjectPtr& attr, PyObjectPtr& value ) + { + return PyObject_SetAttr( m_pyobj, attr.get(), value.get() ) == 0; + } + + PyObjectPtr operator()( PyObjectPtr& args ) const + { + return PyObjectPtr( PyObject_Call( m_pyobj, args.get(), 0 ) ); + } + + PyObjectPtr operator()( PyObjectPtr& args, PyObjectPtr& kwargs ) const + { + return PyObjectPtr( PyObject_Call( m_pyobj, args.get(), kwargs.get() ) ); + } + + operator void*() const + { + return static_cast<void*>( m_pyobj ); + } + + PyObjectPtr& operator=( const PyObjectPtr& rhs ) + { + PyObject* old = m_pyobj; + m_pyobj = rhs.m_pyobj; + Py_XINCREF( m_pyobj ); + Py_XDECREF( old ); + return *this; + } + + PyObjectPtr& operator=( PyObject* rhs ) + { + PyObject* old = m_pyobj; + m_pyobj = rhs; + Py_XDECREF( old ); + return *this; + } + +protected: + + PyObject* m_pyobj; + +}; + + +inline bool +operator!=( const PyObjectPtr& lhs, const PyObjectPtr& rhs ) +{ + return lhs.get() != rhs.get(); +} + + +inline bool +operator!=( const PyObject* lhs, const PyObjectPtr& rhs ) +{ + return lhs != rhs.get(); +} + + +inline bool +operator!=( const PyObjectPtr& lhs, const PyObject* rhs ) +{ + return lhs.get() != rhs; +} + + +inline bool +operator==( const PyObjectPtr& lhs, const PyObjectPtr& rhs ) +{ + return lhs.get() == rhs.get(); +} + + +inline bool +operator==( const PyObject* lhs, const PyObjectPtr& rhs ) +{ + return lhs == rhs.get(); +} + + +inline bool +operator==( const PyObjectPtr& lhs, const PyObject* rhs ) +{ + return lhs.get() == rhs; +} + + +/*----------------------------------------------------------------------------- +| Tuple Ptr +|----------------------------------------------------------------------------*/ +class PyTuplePtr : public PyObjectPtr { + +public: + + PyTuplePtr() : PyObjectPtr() {} + + PyTuplePtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyTuplePtr( PyObject* pytuple ) : PyObjectPtr( pytuple ) {} + + bool check() + { + return PyTuple_Check( m_pyobj ); + } + + bool check_exact() + { + return PyTuple_CheckExact( m_pyobj ); + } + + Py_ssize_t size() const + { + return PyTuple_GET_SIZE( m_pyobj ); + } + + PyObjectPtr get_item( Py_ssize_t index ) const + { + return PyObjectPtr( PythonHelpers::newref( PyTuple_GET_ITEM( m_pyobj, index ) ) ); + } + + void set_item( Py_ssize_t index, PyObject* pyobj ) + { + PyObject* old_item = PyTuple_GET_ITEM( m_pyobj, index ); + PyTuple_SET_ITEM( m_pyobj, index, pyobj ); + Py_XDECREF( old_item ); + } + + void set_item( Py_ssize_t index, PyObjectPtr& item ) + { + PyObject* old_item = PyTuple_GET_ITEM( m_pyobj, index ); + PyTuple_SET_ITEM( m_pyobj, index, item.get() ); + Py_XINCREF( item.get() ); + Py_XDECREF( old_item ); + } + + // pyobj must not be null, only use to fill a new empty tuple + void initialize( Py_ssize_t index, PyObject* pyobj ) + { + PyTuple_SET_ITEM( m_pyobj, index, pyobj ); + } + + // ptr must not be empty, only use to fill a new empty tuple + void initialize( Py_ssize_t index, PyObjectPtr& item ) + { + PyTuple_SET_ITEM( m_pyobj, index, item.get() ); + Py_INCREF( item.get() ); + } + +}; + + +/*----------------------------------------------------------------------------- +| List Ptr +|----------------------------------------------------------------------------*/ +class PyListPtr : public PyObjectPtr { + +public: + + PyListPtr() : PyObjectPtr() {} + + PyListPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyListPtr( PyObject* pylist ) : PyObjectPtr( pylist ) {} + + bool check() const + { + return PyList_Check( m_pyobj ); + } + + bool check_exact() const + { + return PyList_CheckExact( m_pyobj ); + } + + Py_ssize_t size() const + { + return PyList_GET_SIZE( m_pyobj ); + } + + PyObject* borrow_item( Py_ssize_t index ) const + { + return PyList_GET_ITEM( m_pyobj, index ); + } + + PyObjectPtr get_item( Py_ssize_t index ) const + { + return PyObjectPtr( PythonHelpers::newref( PyList_GET_ITEM( m_pyobj, index ) ) ); + } + + void set_item( Py_ssize_t index, PyObject* pyobj ) const + { + PyObject* old_item = PyList_GET_ITEM( m_pyobj, index ); + PyList_SET_ITEM( m_pyobj, index, pyobj ); + Py_XDECREF( old_item ); + } + + void set_item( Py_ssize_t index, PyObjectPtr& item ) const + { + PyObject* old_item = PyList_GET_ITEM( m_pyobj, index ); + PyList_SET_ITEM( m_pyobj, index, item.get() ); + Py_XINCREF( item.get() ); + Py_XDECREF( old_item ); + } + + bool del_item( Py_ssize_t index ) const + { + if( PySequence_DelItem( m_pyobj, index ) == -1 ) + return false; + return true; + } + + bool append( PyObjectPtr& pyobj ) const + { + if( PyList_Append( m_pyobj, pyobj.get() ) == 0 ) + return true; + return false; + } + + Py_ssize_t index( PyObjectPtr& item ) const + { + Py_ssize_t maxidx = size(); + for( Py_ssize_t idx = 0; idx < maxidx; idx++ ) + { + PyObjectPtr other( get_item( idx ) ); + if( item.richcompare( other, Py_EQ ) ) + return idx; + } + return -1; + } + +}; + + +/*----------------------------------------------------------------------------- +| Dict Ptr +|----------------------------------------------------------------------------*/ +class PyDictPtr : public PyObjectPtr { + +public: + + PyDictPtr() : PyObjectPtr() {} + + PyDictPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyDictPtr( PyObject* pydict ) : PyObjectPtr( pydict ) {} + + bool check() + { + return PyDict_Check( m_pyobj ); + } + + bool check_exact() + { + return PyDict_CheckExact( m_pyobj ); + } + + Py_ssize_t size() const + { + return PyDict_Size( m_pyobj ); + } + + PyObjectPtr get_item( PyObject* key ) const + { + return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItem( m_pyobj, key ) ) ) ; + } + + PyObjectPtr get_item( PyObjectPtr& key ) const + { + return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItem( m_pyobj, key.get() ) ) ); + } + + PyObjectPtr get_item( const char* key ) const + { + return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItemString( m_pyobj, key ) ) ); + } + + PyObjectPtr get_item( std::string& key ) const + { + return get_item( key.c_str() ); + } + + bool set_item( PyObject* key, PyObject* value ) const + { + if( PyDict_SetItem( m_pyobj, key, value ) == 0 ) + return true; + return false; + } + + bool set_item( PyObject* key, PyObjectPtr& value ) const + { + if( PyDict_SetItem( m_pyobj, key, value.get() ) == 0 ) + return true; + return false; + } + + bool set_item( PyObjectPtr& key, PyObject* value ) const + { + if( PyDict_SetItem( m_pyobj, key.get(), value ) == 0 ) + return true; + return false; + } + + bool set_item( PyObjectPtr& key, PyObjectPtr& value ) const + { + if( PyDict_SetItem( m_pyobj, key.get(), value.get() ) == 0 ) + return true; + return false; + } + + bool set_item( const char* key, PyObjectPtr& value ) const + { + if( PyDict_SetItemString( m_pyobj, key, value.get() ) == 0 ) + return true; + return false; + } + + bool set_item( const char* key, PyObject* value ) const + { + if( PyDict_SetItemString( m_pyobj, key, value ) == 0 ) + return true; + return false; + } + + bool set_item( std::string& key, PyObjectPtr& value ) const + { + return set_item( key.c_str(), value ); + } + + bool del_item( PyObjectPtr& key ) const + { + if( PyDict_DelItem( m_pyobj, key.get() ) == 0 ) + return true; + return false; + } + + bool del_item( const char* key ) const + { + if( PyDict_DelItemString( m_pyobj, key ) == 0 ) + return true; + return false; + } + + bool del_item( std::string& key ) const + { + return del_item( key.c_str() ); + } + +}; + + +/*----------------------------------------------------------------------------- +| Method Ptr +|----------------------------------------------------------------------------*/ +class PyMethodPtr : public PyObjectPtr { + +public: + + PyMethodPtr() : PyObjectPtr() {} + + PyMethodPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyMethodPtr( PyObject* pymethod ) : PyObjectPtr( pymethod ) {} + + bool check() + { + return PyMethod_Check( m_pyobj ); + } + + PyObjectPtr get_self() const + { + return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_SELF( m_pyobj ) ) ); + } + + PyObjectPtr get_function() const + { + return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_FUNCTION( m_pyobj ) ) ); + } + +#if PY_MAJOR_VERSION < 3 + PyObjectPtr get_class() const + { + return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_CLASS( m_pyobj ) ) ); + } +#endif +}; + + +/*----------------------------------------------------------------------------- +| Weakref Ptr +|----------------------------------------------------------------------------*/ +class PyWeakrefPtr : public PyObjectPtr { + +public: + + PyWeakrefPtr() : PyObjectPtr() {} + + PyWeakrefPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyWeakrefPtr( PyObject* pyweakref ) : PyObjectPtr( pyweakref ) {} + + bool check() + { + return PyWeakref_CheckRef( m_pyobj ); + } + + bool check_exact() + { + return PyWeakref_CheckRefExact( m_pyobj ); + } + + PyObjectPtr get_object() const + { + return PyObjectPtr( PythonHelpers::newref( PyWeakref_GET_OBJECT( m_pyobj ) ) ); + } + +}; + +} // namespace PythonHelpers diff --git a/contrib/python/kiwisolver/py2/py/solver.cpp b/contrib/python/kiwisolver/py2/py/solver.cpp new file mode 100644 index 00000000000..7a2ef23ca33 --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/solver.cpp @@ -0,0 +1,333 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + if( PyTuple_GET_SIZE( args ) != 0 || ( kwargs && PyDict_Size( kwargs ) != 0 ) ) + return py_type_fail( "Solver.__new__ takes no arguments" ); + PyObject* pysolver = PyType_GenericNew( type, args, kwargs ); + if( !pysolver ) + return 0; + Solver* self = reinterpret_cast<Solver*>( pysolver ); + new( &self->solver ) kiwi::Solver(); + return pysolver; +} + + +static void +Solver_dealloc( Solver* self ) +{ + self->solver.~Solver(); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Solver_addConstraint( Solver* self, PyObject* other ) +{ + if( !Constraint::TypeCheck( other ) ) + return py_expected_type_fail( other, "Constraint" ); + Constraint* cn = reinterpret_cast<Constraint*>( other ); + try + { + self->solver.addConstraint( cn->constraint ); + } + catch( const kiwi::DuplicateConstraint& ) + { + PyErr_SetObject( DuplicateConstraint, other ); + return 0; + } + catch( const kiwi::UnsatisfiableConstraint& ) + { + PyErr_SetObject( UnsatisfiableConstraint, other ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_removeConstraint( Solver* self, PyObject* other ) +{ + if( !Constraint::TypeCheck( other ) ) + return py_expected_type_fail( other, "Constraint" ); + Constraint* cn = reinterpret_cast<Constraint*>( other ); + try + { + self->solver.removeConstraint( cn->constraint ); + } + catch( const kiwi::UnknownConstraint& ) + { + PyErr_SetObject( UnknownConstraint, other ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_hasConstraint( Solver* self, PyObject* other ) +{ + if( !Constraint::TypeCheck( other ) ) + return py_expected_type_fail( other, "Constraint" ); + Constraint* cn = reinterpret_cast<Constraint*>( other ); + return newref( self->solver.hasConstraint( cn->constraint ) ? Py_True : Py_False ); +} + + +static PyObject* +Solver_addEditVariable( Solver* self, PyObject* args ) +{ + PyObject* pyvar; + PyObject* pystrength; + if( !PyArg_ParseTuple( args, "OO", &pyvar, &pystrength ) ) + return 0; + if( !Variable::TypeCheck( pyvar ) ) + return py_expected_type_fail( pyvar, "Variable" ); + double strength; + if( !convert_to_strength( pystrength, strength ) ) + return 0; + Variable* var = reinterpret_cast<Variable*>( pyvar ); + try + { + self->solver.addEditVariable( var->variable, strength ); + } + catch( const kiwi::DuplicateEditVariable& ) + { + PyErr_SetObject( DuplicateEditVariable, pyvar ); + return 0; + } + catch( const kiwi::BadRequiredStrength& e ) + { + PyErr_SetString( BadRequiredStrength, e.what() ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_removeEditVariable( Solver* self, PyObject* other ) +{ + if( !Variable::TypeCheck( other ) ) + return py_expected_type_fail( other, "Variable" ); + Variable* var = reinterpret_cast<Variable*>( other ); + try + { + self->solver.removeEditVariable( var->variable ); + } + catch( const kiwi::UnknownEditVariable& ) + { + PyErr_SetObject( UnknownEditVariable, other ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_hasEditVariable( Solver* self, PyObject* other ) +{ + if( !Variable::TypeCheck( other ) ) + return py_expected_type_fail( other, "Variable" ); + Variable* var = reinterpret_cast<Variable*>( other ); + return newref( self->solver.hasEditVariable( var->variable ) ? Py_True : Py_False ); +} + + +static PyObject* +Solver_suggestValue( Solver* self, PyObject* args ) +{ + PyObject* pyvar; + PyObject* pyvalue; + if( !PyArg_ParseTuple( args, "OO", &pyvar, &pyvalue ) ) + return 0; + if( !Variable::TypeCheck( pyvar ) ) + return py_expected_type_fail( pyvar, "Variable" ); + double value; + if( !convert_to_double( pyvalue, value ) ) + return 0; + Variable* var = reinterpret_cast<Variable*>( pyvar ); + try + { + self->solver.suggestValue( var->variable, value ); + } + catch( const kiwi::UnknownEditVariable& ) + { + PyErr_SetObject( UnknownEditVariable, pyvar ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_updateVariables( Solver* self ) +{ + self->solver.updateVariables(); + Py_RETURN_NONE; +} + + +static PyObject* +Solver_reset( Solver* self ) +{ + self->solver.reset(); + Py_RETURN_NONE; +} + + +static PyObject* +Solver_dump( Solver* self ) +{ + PyObjectPtr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) ); + PyObject_Print( dump_str.get(), stdout, 0 ); + Py_RETURN_NONE; +} + +static PyObject* +Solver_dumps( Solver* self ) +{ + return PyUnicode_FromString( self->solver.dumps().c_str() ); +} + +static PyMethodDef +Solver_methods[] = { + { "addConstraint", ( PyCFunction )Solver_addConstraint, METH_O, + "Add a constraint to the solver." }, + { "removeConstraint", ( PyCFunction )Solver_removeConstraint, METH_O, + "Remove a constraint from the solver." }, + { "hasConstraint", ( PyCFunction )Solver_hasConstraint, METH_O, + "Check whether the solver contains a constraint." }, + { "addEditVariable", ( PyCFunction )Solver_addEditVariable, METH_VARARGS, + "Add an edit variable to the solver." }, + { "removeEditVariable", ( PyCFunction )Solver_removeEditVariable, METH_O, + "Remove an edit variable from the solver." }, + { "hasEditVariable", ( PyCFunction )Solver_hasEditVariable, METH_O, + "Check whether the solver contains an edit variable." }, + { "suggestValue", ( PyCFunction )Solver_suggestValue, METH_VARARGS, + "Suggest a desired value for an edit variable." }, + { "updateVariables", ( PyCFunction )Solver_updateVariables, METH_NOARGS, + "Update the values of the solver variables." }, + { "reset", ( PyCFunction )Solver_reset, METH_NOARGS, + "Reset the solver to the initial empty starting condition." }, + { "dump", ( PyCFunction )Solver_dump, METH_NOARGS, + "Dump a representation of the solver internals to stdout." }, + { "dumps", ( PyCFunction )Solver_dumps, METH_NOARGS, + "Dump a representation of the solver internals to a string." }, + { 0 } // sentinel +}; + + +PyTypeObject Solver_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Solver", /* tp_name */ + sizeof( Solver ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Solver_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)0, /* tp_repr */ + (PyNumberMethods*)0, /* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* Documentation string */ + (traverseproc)0, /* tp_traverse */ + (inquiry)0, /* tp_clear */ + (richcmpfunc)0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Solver_methods, /* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Solver_new, /* tp_new */ + (freefunc)PyObject_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +PyObject* DuplicateConstraint; + +PyObject* UnsatisfiableConstraint; + +PyObject* UnknownConstraint; + +PyObject* DuplicateEditVariable; + +PyObject* UnknownEditVariable; + +PyObject* BadRequiredStrength; + + +int import_solver() +{ + DuplicateConstraint = PyErr_NewException( + const_cast<char*>( "kiwisolver.DuplicateConstraint" ), 0, 0 ); + if( !DuplicateConstraint ) + return -1; + UnsatisfiableConstraint = PyErr_NewException( + const_cast<char*>( "kiwisolver.UnsatisfiableConstraint" ), 0, 0 ); + if( !UnsatisfiableConstraint ) + return -1; + UnknownConstraint = PyErr_NewException( + const_cast<char*>( "kiwisolver.UnknownConstraint" ), 0, 0 ); + if( !UnknownConstraint ) + return -1; + DuplicateEditVariable = PyErr_NewException( + const_cast<char*>( "kiwisolver.DuplicateEditVariable" ), 0, 0 ); + if( !DuplicateEditVariable ) + return -1; + UnknownEditVariable = PyErr_NewException( + const_cast<char*>( "kiwisolver.UnknownEditVariable" ), 0, 0 ); + if( !UnknownEditVariable ) + return -1; + BadRequiredStrength = PyErr_NewException( + const_cast<char*>( "kiwisolver.BadRequiredStrength" ), 0, 0 ); + if( !BadRequiredStrength ) + return -1; + return PyType_Ready( &Solver_Type ); +} diff --git a/contrib/python/kiwisolver/py2/py/strength.cpp b/contrib/python/kiwisolver/py2/py/strength.cpp new file mode 100644 index 00000000000..df1552d1947 --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/strength.cpp @@ -0,0 +1,162 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "util.h" + + +using namespace PythonHelpers; + + +struct strength +{ + PyObject_HEAD; +}; + + +static void +strength_dealloc( PyObject* self ) +{ + Py_TYPE( self )->tp_free( self ); +} + + +static PyObject* +strength_weak( strength* self ) +{ + return PyFloat_FromDouble( kiwi::strength::weak ); +} + + +static PyObject* +strength_medium( strength* self ) +{ + return PyFloat_FromDouble( kiwi::strength::medium ); +} + + +static PyObject* +strength_strong( strength* self ) +{ + return PyFloat_FromDouble( kiwi::strength::strong ); +} + + +static PyObject* +strength_required( strength* self ) +{ + return PyFloat_FromDouble( kiwi::strength::required ); +} + + +static PyObject* +strength_create( strength* self, PyObject* args ) +{ + PyObject* pya; + PyObject* pyb; + PyObject* pyc; + PyObject* pyw = 0; + if( !PyArg_ParseTuple( args, "OOO|O", &pya, &pyb, &pyc, &pyw ) ) + return 0; + double a, b, c; + double w = 1.0; + if( !convert_to_double( pya, a ) ) + return 0; + if( !convert_to_double( pyb, b ) ) + return 0; + if( !convert_to_double( pyc, c ) ) + return 0; + if( pyw && !convert_to_double( pyw, w ) ) + return 0; + return PyFloat_FromDouble( kiwi::strength::create( a, b, c, w ) ); +} + + +static PyGetSetDef +strength_getset[] = { + { "weak", ( getter )strength_weak, 0, + "The predefined weak strength." }, + { "medium", ( getter )strength_medium, 0, + "The predefined medium strength." }, + { "strong", ( getter )strength_strong, 0, + "The predefined strong strength." }, + { "required", ( getter )strength_required, 0, + "The predefined required strength." }, + { 0 } // sentinel +}; + + +static PyMethodDef +strength_methods[] = { + { "create", ( PyCFunction )strength_create, METH_VARARGS, + "Create a strength from constituent values and optional weight." }, + { 0 } // sentinel +}; + + +PyTypeObject strength_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.strength", /* tp_name */ + sizeof( strength ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)strength_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)0, /* tp_repr */ + (PyNumberMethods*)0, /* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* Documentation string */ + (traverseproc)0, /* tp_traverse */ + (inquiry)0, /* tp_clear */ + (richcmpfunc)0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)strength_methods, /* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + strength_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)0, /* tp_new */ + (freefunc)PyObject_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_strength() +{ + return PyType_Ready( &strength_Type ); +} diff --git a/contrib/python/kiwisolver/py2/py/symbolics.h b/contrib/python/kiwisolver/py2/py/symbolics.h new file mode 100644 index 00000000000..ac575537403 --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/symbolics.h @@ -0,0 +1,620 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <Python.h> +#include "pythonhelpers.h" +#include "types.h" +#include "util.h" + + +template<typename Op, typename T> +struct UnaryInvoke +{ + PyObject* operator()( PyObject* value ) + { + return Op()( reinterpret_cast<T*>( value ) ); + } +}; + + +template<typename Op, typename T> +struct BinaryInvoke +{ + PyObject* operator()( PyObject* first, PyObject* second ) + { + if( T::TypeCheck( first ) ) + return invoke<Normal>( reinterpret_cast<T*>( first ), second ); + return invoke<Reverse>( reinterpret_cast<T*>( second ), first ); + } + + struct Normal + { + template<typename U> + PyObject* operator()( T* primary, U secondary ) + { + return Op()( primary, secondary ); + } + }; + + struct Reverse + { + template<typename U> + PyObject* operator()( T* primary, U secondary ) + { + return Op()( secondary, primary ); + } + }; + + template<typename Invk> + PyObject* invoke( T* primary, PyObject* secondary ) + { + if( Expression::TypeCheck( secondary ) ) + return Invk()( primary, reinterpret_cast<Expression*>( secondary ) ); + if( Term::TypeCheck( secondary ) ) + return Invk()( primary, reinterpret_cast<Term*>( secondary ) ); + if( Variable::TypeCheck( secondary ) ) + return Invk()( primary, reinterpret_cast<Variable*>( secondary ) ); + if( PyFloat_Check( secondary ) ) + return Invk()( primary, PyFloat_AS_DOUBLE( secondary ) ); +#if PY_MAJOR_VERSION < 3 + if( PyInt_Check( secondary ) ) + return Invk()( primary, double( PyInt_AS_LONG( secondary ) ) ); +#endif + if( PyLong_Check( secondary ) ) + { + double v = PyLong_AsDouble( secondary ); + if( v == -1 && PyErr_Occurred() ) + return 0; + return Invk()( primary, v ); + } + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +struct BinaryMul +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* BinaryMul::operator()( Variable* first, double second ) +{ + PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + if( !pyterm ) + return 0; + Term* term = reinterpret_cast<Term*>( pyterm ); + term->variable = PythonHelpers::newref( pyobject_cast( first ) ); + term->coefficient = second; + return pyterm; +} + + +template<> inline +PyObject* BinaryMul::operator()( Term* first, double second ) +{ + PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + if( !pyterm ) + return 0; + Term* term = reinterpret_cast<Term*>( pyterm ); + term->variable = PythonHelpers::newref( first->variable ); + term->coefficient = first->coefficient * second; + return pyterm; +} + + +template<> inline +PyObject* BinaryMul::operator()( Expression* first, double second ) +{ + using namespace PythonHelpers; + PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + PyObjectPtr terms( PyTuple_New( PyTuple_GET_SIZE( first->terms ) ) ); + if( !terms ) + return 0; + Py_ssize_t end = PyTuple_GET_SIZE( first->terms ); + for( Py_ssize_t i = 0; i < end; ++i ) // memset 0 for safe error return + PyTuple_SET_ITEM( terms.get(), i, 0 ); + for( Py_ssize_t i = 0; i < end; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( first->terms, i ); + PyObject* term = BinaryMul()( reinterpret_cast<Term*>( item ), second ); + if( !term ) + return 0; + PyTuple_SET_ITEM( terms.get(), i, term ); + } + expr->terms = terms.release(); + expr->constant = first->constant * second; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryMul::operator()( double first, Variable* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryMul::operator()( double first, Term* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryMul::operator()( double first, Expression* second ) +{ + return operator()( second, first ); +} + + +struct BinaryDiv +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* BinaryDiv::operator()( Variable* first, double second ) +{ + if( second == 0.0 ) + { + PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" ); + return 0; + } + return BinaryMul()( first, 1.0 / second ); +} + + +template<> inline +PyObject* BinaryDiv::operator()( Term* first, double second ) +{ + if( second == 0.0 ) + { + PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" ); + return 0; + } + return BinaryMul()( first, 1.0 / second ); +} + + +template<> inline +PyObject* BinaryDiv::operator()( Expression* first, double second ) +{ + if( second == 0.0 ) + { + PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" ); + return 0; + } + return BinaryMul()( first, 1.0 / second ); +} + + +struct UnaryNeg +{ + template<typename T> + PyObject* operator()( T value ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* UnaryNeg::operator()( Variable* value ) +{ + return BinaryMul()( value, -1.0 ); +} + + +template<> inline +PyObject* UnaryNeg::operator()( Term* value ) +{ + return BinaryMul()( value, -1.0 ); +} + + +template<> inline +PyObject* UnaryNeg::operator()( Expression* value ) +{ + return BinaryMul()( value, -1.0 ); +} + + +struct BinaryAdd +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* BinaryAdd::operator()( Expression* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->constant = first->constant + second->constant; + expr->terms = PySequence_Concat( first->terms, second->terms ); + if( !expr->terms ) + return 0; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Expression* first, Term* second ) +{ + using namespace PythonHelpers; + PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + PyObject* terms = PyTuple_New( PyTuple_GET_SIZE( first->terms ) + 1 ); + if( !terms ) + return 0; + Py_ssize_t end = PyTuple_GET_SIZE( first->terms ); + for( Py_ssize_t i = 0; i < end; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( first->terms, i ); + PyTuple_SET_ITEM( terms, i, newref( item ) ); + } + PyTuple_SET_ITEM( terms, end, newref( pyobject_cast( second ) ) ); + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->terms = terms; + expr->constant = first->constant; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Expression* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( second, 1.0 ) ); + if( !temp ) + return 0; + return operator()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Expression* first, double second ) +{ + using namespace PythonHelpers; + PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->terms = newref( first->terms ); + expr->constant = first->constant + second; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Term* first, double second ) +{ + PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->constant = second; + expr->terms = PyTuple_Pack( 1, first ); + if( !expr->terms ) + return 0; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Term* first, Expression* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Term* first, Term* second ) +{ + PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->constant = 0.0; + expr->terms = PyTuple_Pack( 2, first, second ); + if( !expr->terms ) + return 0; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Term* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( second, 1.0 ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Variable* first, double second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + if( !temp ) + return 0; + return operator()( reinterpret_cast<Term*>( temp.get() ), second ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Variable* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + if( !temp ) + return 0; + return operator()( reinterpret_cast<Term*>( temp.get() ), second ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Variable* first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + if( !temp ) + return 0; + return operator()( reinterpret_cast<Term*>( temp.get() ), second ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Variable* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + if( !temp ) + return 0; + return operator()( reinterpret_cast<Term*>( temp.get() ), second ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( double first, Variable* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( double first, Term* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( double first, Expression* second ) +{ + return operator()( second, first ); +} + + +struct BinarySub +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* BinarySub::operator()( Variable* first, double second ) +{ + return BinaryAdd()( first, -second ); +} + + +template<> inline +PyObject* BinarySub::operator()( Variable* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Variable* first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Variable* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Term* first, double second ) +{ + return BinaryAdd()( first, -second ); +} + + +template<> inline +PyObject* BinarySub::operator()( Term* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Term* first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Term* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Expression* first, double second ) +{ + return BinaryAdd()( first, -second ); +} + + +template<> inline +PyObject* BinarySub::operator()( Expression* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Expression* first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Expression* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( double first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( double first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( double first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) ); +} + + +template<typename T, typename U> +PyObject* makecn( T first, U second, kiwi::RelationalOperator op ) +{ + PythonHelpers::PyObjectPtr pyexpr( BinarySub()( first, second ) ); + if( !pyexpr ) + return 0; + PythonHelpers::PyObjectPtr pycn( PyType_GenericNew( &Constraint_Type, 0, 0 ) ); + if( !pycn ) + return 0; + Constraint* cn = reinterpret_cast<Constraint*>( pycn.get() ); + cn->expression = reduce_expression( pyexpr.get() ); + if( !cn->expression ) + return 0; + kiwi::Expression expr( convert_to_kiwi_expression( cn->expression ) ); + new( &cn->constraint ) kiwi::Constraint( expr, op, kiwi::strength::required ); + return pycn.release(); +} + + +struct CmpEQ +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + return makecn( first, second, kiwi::OP_EQ ); + } +}; + + +struct CmpLE +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + return makecn( first, second, kiwi::OP_LE ); + } +}; + + +struct CmpGE +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + return makecn( first, second, kiwi::OP_GE ); + } +}; diff --git a/contrib/python/kiwisolver/py2/py/term.cpp b/contrib/python/kiwisolver/py2/py/term.cpp new file mode 100644 index 00000000000..4be64a1eeaf --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/term.cpp @@ -0,0 +1,298 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <sstream> +#include <Python.h> +#include "pythonhelpers.h" +#include "symbolics.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + static const char *kwlist[] = { "variable", "coefficient", 0 }; + PyObject* pyvar; + PyObject* pycoeff = 0; + if( !PyArg_ParseTupleAndKeywords( + args, kwargs, "O|O:__new__", const_cast<char**>( kwlist ), + &pyvar, &pycoeff ) ) + return 0; + if( !Variable::TypeCheck( pyvar ) ) + return py_expected_type_fail( pyvar, "Variable" ); + double coefficient = 1.0; + if( pycoeff && !convert_to_double( pycoeff, coefficient ) ) + return 0; + PyObject* pyterm = PyType_GenericNew( type, args, kwargs ); + if( !pyterm ) + return 0; + Term* self = reinterpret_cast<Term*>( pyterm ); + self->variable = newref( pyvar ); + self->coefficient = coefficient; + return pyterm; +} + + +static void +Term_clear( Term* self ) +{ + Py_CLEAR( self->variable ); +} + + +static int +Term_traverse( Term* self, visitproc visit, void* arg ) +{ + Py_VISIT( self->variable ); + return 0; +} + + +static void +Term_dealloc( Term* self ) +{ + PyObject_GC_UnTrack( self ); + Term_clear( self ); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Term_repr( Term* self ) +{ + std::stringstream stream; + stream << self->coefficient << " * "; + stream << reinterpret_cast<Variable*>( self->variable )->variable.name(); + return FROM_STRING( stream.str().c_str() ); +} + + +static PyObject* +Term_variable( Term* self ) +{ + return newref( self->variable ); +} + + +static PyObject* +Term_coefficient( Term* self ) +{ + return PyFloat_FromDouble( self->coefficient ); +} + + +static PyObject* +Term_value( Term* self ) +{ + Variable* pyvar = reinterpret_cast<Variable*>( self->variable ); + return PyFloat_FromDouble( self->coefficient * pyvar->variable.value() ); +} + + +static PyObject* +Term_add( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryAdd, Term>()( first, second ); +} + + +static PyObject* +Term_sub( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinarySub, Term>()( first, second ); +} + + +static PyObject* +Term_mul( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryMul, Term>()( first, second ); +} + + +static PyObject* +Term_div( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryDiv, Term>()( first, second ); +} + + +static PyObject* +Term_neg( PyObject* value ) +{ + return UnaryInvoke<UnaryNeg, Term>()( value ); +} + + +static PyObject* +Term_richcmp( PyObject* first, PyObject* second, int op ) +{ + switch( op ) + { + case Py_EQ: + return BinaryInvoke<CmpEQ, Term>()( first, second ); + case Py_LE: + return BinaryInvoke<CmpLE, Term>()( first, second ); + case Py_GE: + return BinaryInvoke<CmpGE, Term>()( first, second ); + default: + break; + } + PyErr_Format( + PyExc_TypeError, + "unsupported operand type(s) for %s: " + "'%.100s' and '%.100s'", + pyop_str( op ), + first->ob_type->tp_name, + second->ob_type->tp_name + ); + return 0; +} + + +static PyMethodDef +Term_methods[] = { + { "variable", ( PyCFunction )Term_variable, METH_NOARGS, + "Get the variable for the term." }, + { "coefficient", ( PyCFunction )Term_coefficient, METH_NOARGS, + "Get the coefficient for the term." }, + { "value", ( PyCFunction )Term_value, METH_NOARGS, + "Get the value for the term." }, + { 0 } // sentinel +}; + + +static PyNumberMethods +Term_as_number = { + (binaryfunc)Term_add, /* nb_add */ + (binaryfunc)Term_sub, /* nb_subtract */ + (binaryfunc)Term_mul, /* nb_multiply */ +#if PY_MAJOR_VERSION < 3 + (binaryfunc)Term_div, /* nb_divide */ +#endif + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + (unaryfunc)Term_neg, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ +#if PY_MAJOR_VERSION >= 3 + 0, /* nb_bool */ +#else + 0, /* nb_nonzero */ +#endif + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + (binaryfunc)0, /* nb_or */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_coerce */ +#endif + 0, /* nb_int */ + 0, /* nb_long */ + 0, /* nb_float */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_oct */ + 0, /* nb_hex */ +#endif + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_inplace_divide */ +#endif + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)0, /* nb_floor_divide */ + (binaryfunc)Term_div, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +#if PY_VERSION_HEX >= 0x02050000 + (unaryfunc)0, /* nb_index */ +#endif +#if PY_VERSION_HEX >= 0x03050000 + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ +#endif +}; + + +PyTypeObject Term_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Term", /* tp_name */ + sizeof( Term ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Term_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)Term_repr, /* tp_repr */ + (PyNumberMethods*)&Term_as_number, /* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + 0, /* Documentation string */ + (traverseproc)Term_traverse, /* tp_traverse */ + (inquiry)Term_clear, /* tp_clear */ + (richcmpfunc)Term_richcmp, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Term_methods, /* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Term_new, /* tp_new */ + (freefunc)PyObject_GC_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_term() +{ + return PyType_Ready( &Term_Type ); +} diff --git a/contrib/python/kiwisolver/py2/py/types.h b/contrib/python/kiwisolver/py2/py/types.h new file mode 100644 index 00000000000..628efafbca4 --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/types.h @@ -0,0 +1,112 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <Python.h> +#include <kiwi/kiwi.h> + + +int import_variable(); + +int import_term(); + +int import_expression(); + +int import_constraint(); + +int import_solver(); + +int import_strength(); + + +extern PyTypeObject Variable_Type; + +extern PyTypeObject Term_Type; + +extern PyTypeObject Expression_Type; + +extern PyTypeObject Constraint_Type; + +extern PyTypeObject Solver_Type; + +extern PyTypeObject strength_Type; + +extern PyObject* DuplicateConstraint; + +extern PyObject* UnsatisfiableConstraint; + +extern PyObject* UnknownConstraint; + +extern PyObject* DuplicateEditVariable; + +extern PyObject* UnknownEditVariable; + +extern PyObject* BadRequiredStrength; + + +struct Variable +{ + PyObject_HEAD + PyObject* context; + kiwi::Variable variable; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Variable_Type ) != 0; + } +}; + + +struct Term +{ + PyObject_HEAD + PyObject* variable; + double coefficient; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Term_Type ) != 0; + } +}; + + +struct Expression +{ + PyObject_HEAD + PyObject* terms; + double constant; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Expression_Type ) != 0; + } +}; + + +struct Constraint +{ + PyObject_HEAD + PyObject* expression; + kiwi::Constraint constraint; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Constraint_Type ) != 0; + } +}; + + +struct Solver +{ + PyObject_HEAD + kiwi::Solver solver; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Solver_Type ) != 0; + } +}; diff --git a/contrib/python/kiwisolver/py2/py/util.h b/contrib/python/kiwisolver/py2/py/util.h new file mode 100644 index 00000000000..78e9cbd0b8e --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/util.h @@ -0,0 +1,230 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <map> +#include <string> +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "types.h" + + +inline bool +convert_to_double( PyObject* obj, double& out ) +{ + if( PyFloat_Check( obj ) ) + { + out = PyFloat_AS_DOUBLE( obj ); + return true; + } +#if PY_MAJOR_VERSION < 3 + if( PyInt_Check( obj ) ) + { + out = double( PyInt_AsLong( obj ) ); + return true; + } +#endif + if( PyLong_Check( obj ) ) + { + out = PyLong_AsDouble( obj ); + if( out == -1.0 && PyErr_Occurred() ) + return false; + return true; + } + PythonHelpers::py_expected_type_fail( obj, "float, int, or long" ); + return false; +} + + +inline bool +convert_pystr_to_str( PyObject* value, std::string& out ) +{ +#if PY_MAJOR_VERSION >= 3 + out = PyUnicode_AsUTF8( value ); +#else + if( PyUnicode_Check( value ) ) + { + PythonHelpers::PyObjectPtr py_str( PyUnicode_AsUTF8String( value ) ); + if( !py_str ) + return false; // LCOV_EXCL_LINE + out = PyString_AS_STRING( py_str.get() ); + } + else + out = PyString_AS_STRING( value ); +#endif + return true; +} + + +inline bool +convert_to_strength( PyObject* value, double& out ) +{ +#if PY_MAJOR_VERSION >= 3 + if( PyUnicode_Check( value ) ) + { +#else + if( PyString_Check( value ) | PyUnicode_Check( value )) + { +#endif + std::string str; + if( !convert_pystr_to_str( value, str ) ) + return false; + if( str == "required" ) + out = kiwi::strength::required; + else if( str == "strong" ) + out = kiwi::strength::strong; + else if( str == "medium" ) + out = kiwi::strength::medium; + else if( str == "weak" ) + out = kiwi::strength::weak; + else + { + PyErr_Format( + PyExc_ValueError, + "string strength must be 'required', 'strong', 'medium', " + "or 'weak', not '%s'", + str.c_str() + ); + return false; + } + return true; + } + if( !convert_to_double( value, out ) ) + return false; + return true; +} + + +inline bool +convert_to_relational_op( PyObject* value, kiwi::RelationalOperator& out ) +{ +#if PY_MAJOR_VERSION >= 3 + if( !PyUnicode_Check( value ) ) + { + PythonHelpers::py_expected_type_fail( value, "unicode" ); + return false; + } +#else + if( !(PyString_Check( value ) | PyUnicode_Check( value ) ) ) + { + PythonHelpers::py_expected_type_fail( value, "str or unicode" ); + return false; + } +#endif + std::string str; + if( !convert_pystr_to_str( value, str ) ) + return false; + if( str == "==" ) + out = kiwi::OP_EQ; + else if( str == "<=" ) + out = kiwi::OP_LE; + else if( str == ">=" ) + out = kiwi::OP_GE; + else + { + PyErr_Format( + PyExc_ValueError, + "relational operator must be '==', '<=', or '>=', not '%s'", + str.c_str() + ); + return false; + } + return true; +} + + +inline PyObject* +make_terms( const std::map<PyObject*, double>& coeffs ) +{ + typedef std::map<PyObject*, double>::const_iterator iter_t; + PythonHelpers::PyObjectPtr terms( PyTuple_New( coeffs.size() ) ); + if( !terms ) + return 0; + Py_ssize_t size = PyTuple_GET_SIZE( terms.get() ); + for( Py_ssize_t i = 0; i < size; ++i ) // zero tuple for safe early return + PyTuple_SET_ITEM( terms.get(), i, 0 ); + Py_ssize_t i = 0; + iter_t it = coeffs.begin(); + iter_t end = coeffs.end(); + for( ; it != end; ++it, ++i ) + { + PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + if( !pyterm ) + return 0; + Term* term = reinterpret_cast<Term*>( pyterm ); + term->variable = PythonHelpers::newref( it->first ); + term->coefficient = it->second; + PyTuple_SET_ITEM( terms.get(), i, pyterm ); + } + return terms.release(); +} + + +inline PyObject* +reduce_expression( PyObject* pyexpr ) // pyexpr must be an Expression +{ + Expression* expr = reinterpret_cast<Expression*>( pyexpr ); + std::map<PyObject*, double> coeffs; + Py_ssize_t size = PyTuple_GET_SIZE( expr->terms ); + for( Py_ssize_t i = 0; i < size; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( expr->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + coeffs[ term->variable ] += term->coefficient; + } + PythonHelpers::PyObjectPtr terms( make_terms( coeffs ) ); + if( !terms ) + return 0; + PyObject* pynewexpr = PyType_GenericNew( &Expression_Type, 0, 0 ); + if( !pynewexpr ) + return 0; + Expression* newexpr = reinterpret_cast<Expression*>( pynewexpr ); + newexpr->terms = terms.release(); + newexpr->constant = expr->constant; + return pynewexpr; +} + + +inline kiwi::Expression +convert_to_kiwi_expression( PyObject* pyexpr ) // pyexpr must be an Expression +{ + Expression* expr = reinterpret_cast<Expression*>( pyexpr ); + std::vector<kiwi::Term> kterms; + Py_ssize_t size = PyTuple_GET_SIZE( expr->terms ); + for( Py_ssize_t i = 0; i < size; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( expr->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + Variable* var = reinterpret_cast<Variable*>( term->variable ); + kterms.push_back( kiwi::Term( var->variable, term->coefficient ) ); + } + return kiwi::Expression( kterms, expr->constant ); +} + + +inline const char* +pyop_str( int op ) +{ + switch( op ) + { + case Py_LT: + return "<"; + case Py_LE: + return "<="; + case Py_EQ: + return "=="; + case Py_NE: + return "!="; + case Py_GT: + return ">"; + case Py_GE: + return ">="; + default: + return ""; + } +} diff --git a/contrib/python/kiwisolver/py2/py/variable.cpp b/contrib/python/kiwisolver/py2/py/variable.cpp new file mode 100644 index 00000000000..a622e8529ab --- /dev/null +++ b/contrib/python/kiwisolver/py2/py/variable.cpp @@ -0,0 +1,353 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "symbolics.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + static const char *kwlist[] = { "name", "context", 0 }; + PyObject* context = 0; + PyObject* name = 0; + + if( !PyArg_ParseTupleAndKeywords( + args, kwargs, "|OO:__new__", const_cast<char**>( kwlist ), + &name, &context ) ) + return 0; + + PyObjectPtr pyvar( PyType_GenericNew( type, args, kwargs ) ); + if( !pyvar ) + return 0; + + Variable* self = reinterpret_cast<Variable*>( pyvar.get() ); + self->context = xnewref( context ); + + if( name != 0 ) + { +#if PY_MAJOR_VERSION >= 3 + if( !PyUnicode_Check( name ) ) + return py_expected_type_fail( name, "unicode" ); +#else + if( !( PyString_Check( name ) | PyUnicode_Check( name ) ) ) + { + return py_expected_type_fail( name, "str or unicode" ); + } +#endif + std::string c_name; + if( !convert_pystr_to_str(name, c_name) ) + return 0; // LCOV_EXCL_LINE + new( &self->variable ) kiwi::Variable( c_name ); + } + else + { + new( &self->variable ) kiwi::Variable(); + } + + return pyvar.release(); +} + + +static void +Variable_clear( Variable* self ) +{ + Py_CLEAR( self->context ); +} + + +static int +Variable_traverse( Variable* self, visitproc visit, void* arg ) +{ + Py_VISIT( self->context ); + return 0; +} + + +static void +Variable_dealloc( Variable* self ) +{ + PyObject_GC_UnTrack( self ); + Variable_clear( self ); + self->variable.~Variable(); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Variable_repr( Variable* self ) +{ + return FROM_STRING( self->variable.name().c_str() ); +} + + +static PyObject* +Variable_name( Variable* self ) +{ + return FROM_STRING( self->variable.name().c_str() ); +} + + +static PyObject* +Variable_setName( Variable* self, PyObject* pystr ) +{ +#if PY_MAJOR_VERSION >= 3 + if( !PyUnicode_Check( pystr ) ) + return py_expected_type_fail( pystr, "unicode" ); +#else + if( !(PyString_Check( pystr ) | PyUnicode_Check( pystr ) ) ) + { + return py_expected_type_fail( pystr, "str or unicode" ); + } +#endif + std::string str; + if( !convert_pystr_to_str( pystr, str ) ) + return 0; + self->variable.setName( str ); + Py_RETURN_NONE; +} + + +static PyObject* +Variable_context( Variable* self ) +{ + if( self->context ) + return newref( self->context ); + Py_RETURN_NONE; +} + + +static PyObject* +Variable_setContext( Variable* self, PyObject* value ) +{ + if( value != self->context ) + { + PyObject* temp = self->context; + self->context = newref( value ); + Py_XDECREF( temp ); + } + Py_RETURN_NONE; +} + + +static PyObject* +Variable_value( Variable* self ) +{ + return PyFloat_FromDouble( self->variable.value() ); +} + + +static PyObject* +Variable_add( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryAdd, Variable>()( first, second ); +} + + +static PyObject* +Variable_sub( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinarySub, Variable>()( first, second ); +} + + +static PyObject* +Variable_mul( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryMul, Variable>()( first, second ); +} + + +static PyObject* +Variable_div( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryDiv, Variable>()( first, second ); +} + + +static PyObject* +Variable_neg( PyObject* value ) +{ + return UnaryInvoke<UnaryNeg, Variable>()( value ); +} + + +static PyObject* +Variable_richcmp( PyObject* first, PyObject* second, int op ) +{ + switch( op ) + { + case Py_EQ: + return BinaryInvoke<CmpEQ, Variable>()( first, second ); + case Py_LE: + return BinaryInvoke<CmpLE, Variable>()( first, second ); + case Py_GE: + return BinaryInvoke<CmpGE, Variable>()( first, second ); + default: + break; + } + PyErr_Format( + PyExc_TypeError, + "unsupported operand type(s) for %s: " + "'%.100s' and '%.100s'", + pyop_str( op ), + first->ob_type->tp_name, + second->ob_type->tp_name + ); + return 0; +} + + +static PyMethodDef +Variable_methods[] = { + { "name", ( PyCFunction )Variable_name, METH_NOARGS, + "Get the name of the variable." }, + { "setName", ( PyCFunction )Variable_setName, METH_O, + "Set the name of the variable." }, + { "context", ( PyCFunction )Variable_context, METH_NOARGS, + "Get the context object associated with the variable." }, + { "setContext", ( PyCFunction )Variable_setContext, METH_O, + "Set the context object associated with the variable." }, + { "value", ( PyCFunction )Variable_value, METH_NOARGS, + "Get the current value of the variable." }, + { 0 } // sentinel +}; + + +static PyNumberMethods +Variable_as_number = { + (binaryfunc)Variable_add, /* nb_add */ + (binaryfunc)Variable_sub, /* nb_subtract */ + (binaryfunc)Variable_mul, /* nb_multiply */ +#if PY_MAJOR_VERSION < 3 + (binaryfunc)Variable_div, /* nb_divide */ +#endif + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + (unaryfunc)Variable_neg, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ +#if PY_MAJOR_VERSION >= 3 + 0, /* nb_bool */ +#else + 0, /* nb_nonzero */ +#endif + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + (binaryfunc)0, /* nb_or */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_coerce */ +#endif + 0, /* nb_int */ + 0, /* nb_long */ + 0, /* nb_float */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_oct */ + 0, /* nb_hex */ +#endif + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_inplace_divide */ +#endif + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)0, /* nb_floor_divide */ + (binaryfunc)Variable_div, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +#if PY_VERSION_HEX >= 0x02050000 + (unaryfunc)0, /* nb_index */ +#endif +#if PY_VERSION_HEX >= 0x03050000 + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ +#endif +}; + + +PyTypeObject Variable_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Variable", /* tp_name */ + sizeof( Variable ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Variable_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)Variable_repr, /* tp_repr */ + (PyNumberMethods*)&Variable_as_number, /* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + 0, /* Documentation string */ + (traverseproc)Variable_traverse, /* tp_traverse */ + (inquiry)Variable_clear, /* tp_clear */ + (richcmpfunc)Variable_richcmp, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Variable_methods, /* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Variable_new, /* tp_new */ + (freefunc)PyObject_GC_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_variable() +{ + return PyType_Ready( &Variable_Type ); +} diff --git a/contrib/python/kiwisolver/py2/ya.make b/contrib/python/kiwisolver/py2/ya.make new file mode 100644 index 00000000000..3803e254093 --- /dev/null +++ b/contrib/python/kiwisolver/py2/ya.make @@ -0,0 +1,41 @@ +# Generated by devtools/yamaker (pypi). + +PY2_LIBRARY() + +VERSION(1.1.0) + +LICENSE(BSD-3-Clause) + +PEERDIR( + contrib/python/setuptools +) + +ADDINCL( + contrib/python/kiwisolver/py2 +) + +NO_COMPILER_WARNINGS() + +NO_LINT() + +SRCS( + py/constraint.cpp + py/expression.cpp + py/kiwisolver.cpp + py/solver.cpp + py/strength.cpp + py/term.cpp + py/variable.cpp +) + +PY_REGISTER( + kiwisolver +) + +RESOURCE_FILES( + PREFIX contrib/python/kiwisolver/py2/ + .dist-info/METADATA + .dist-info/top_level.txt +) + +END() diff --git a/contrib/python/kiwisolver/py3/.dist-info/METADATA b/contrib/python/kiwisolver/py3/.dist-info/METADATA new file mode 100644 index 00000000000..6c363cae42f --- /dev/null +++ b/contrib/python/kiwisolver/py3/.dist-info/METADATA @@ -0,0 +1,42 @@ +Metadata-Version: 2.1 +Name: kiwisolver +Version: 1.1.0 +Summary: A fast implementation of the Cassowary constraint solver +Home-page: https://github.com/nucleic/kiwi +Author: The Nucleic Development Team +Author-email: [email protected] +License: BSD +Platform: UNKNOWN +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Requires-Dist: setuptools + +Welcome to Kiwi +=============== + +.. image:: https://travis-ci.org/nucleic/kiwi.svg?branch=master + :target: https://travis-ci.org/nucleic/kiwi +.. image:: https://codecov.io/gh/nucleic/kiwi/branch/master/graph/badge.svg + :target: https://codecov.io/gh/nucleic/kiwi +.. image:: https://readthedocs.org/projects/kiwisolver/badge/?version=latest + :target: https://kiwisolver.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +Kiwi is an efficient C++ implementation of the Cassowary constraint solving +algorithm. Kiwi is an implementation of the algorithm based on the seminal +Cassowary paper. It is *not* a refactoring of the original C++ solver. Kiwi +has been designed from the ground up to be lightweight and fast. Kiwi ranges +from 10x to 500x faster than the original Cassowary solver with typical use +cases gaining a 40x improvement. Memory savings are consistently > 5x. + +In addition to the C++ solver, Kiwi ships with hand-rolled Python bindings. + + diff --git a/contrib/python/kiwisolver/py3/.dist-info/top_level.txt b/contrib/python/kiwisolver/py3/.dist-info/top_level.txt new file mode 100644 index 00000000000..9b85884d1a9 --- /dev/null +++ b/contrib/python/kiwisolver/py3/.dist-info/top_level.txt @@ -0,0 +1 @@ +kiwisolver diff --git a/contrib/python/kiwisolver/py3/README.rst b/contrib/python/kiwisolver/py3/README.rst new file mode 100644 index 00000000000..4faa3c329ba --- /dev/null +++ b/contrib/python/kiwisolver/py3/README.rst @@ -0,0 +1,19 @@ +Welcome to Kiwi +=============== + +.. image:: https://travis-ci.org/nucleic/kiwi.svg?branch=master + :target: https://travis-ci.org/nucleic/kiwi +.. image:: https://codecov.io/gh/nucleic/kiwi/branch/master/graph/badge.svg + :target: https://codecov.io/gh/nucleic/kiwi +.. image:: https://readthedocs.org/projects/kiwisolver/badge/?version=latest + :target: https://kiwisolver.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +Kiwi is an efficient C++ implementation of the Cassowary constraint solving +algorithm. Kiwi is an implementation of the algorithm based on the seminal +Cassowary paper. It is *not* a refactoring of the original C++ solver. Kiwi +has been designed from the ground up to be lightweight and fast. Kiwi ranges +from 10x to 500x faster than the original Cassowary solver with typical use +cases gaining a 40x improvement. Memory savings are consistently > 5x. + +In addition to the C++ solver, Kiwi ships with hand-rolled Python bindings. diff --git a/contrib/python/kiwisolver/py3/kiwi/AssocVector.h b/contrib/python/kiwisolver/py3/kiwi/AssocVector.h new file mode 100644 index 00000000000..0d5eb288efc --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/AssocVector.h @@ -0,0 +1,354 @@ +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2001 by Andrei Alexandrescu +// This code accompanies the book: +// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design +// Patterns Applied". Copyright (c) 2001. Addison-Wesley. +// Permission to use, copy, modify, distribute and sell this software for any +// purpose is hereby granted without fee, provided that the above copyright +// notice appear in all copies and that both that copyright notice and this +// permission notice appear in supporting documentation. +// The author or Addison-Wesley Longman make no representations about the +// suitability of this software for any purpose. It is provided "as is" +// without express or implied warranty. +//////////////////////////////////////////////////////////////////////////////// +#pragma once + +// $Id: AssocVector.h 765 2006-10-18 13:55:32Z syntheticpp $ + + +#include <algorithm> +#include <functional> +#include <vector> +#include <utility> + +namespace Loki +{ +//////////////////////////////////////////////////////////////////////////////// +// class template AssocVectorCompare +// Used by AssocVector +//////////////////////////////////////////////////////////////////////////////// + + namespace Private + { + template <class Value, class C> + class AssocVectorCompare : public C + { + typedef std::pair<typename C::first_argument_type, Value> + Data; + typedef typename C::first_argument_type first_argument_type; + + public: + AssocVectorCompare() + {} + + AssocVectorCompare(const C& src) : C(src) + {} + + bool operator()(const first_argument_type& lhs, + const first_argument_type& rhs) const + { return C::operator()(lhs, rhs); } + + bool operator()(const Data& lhs, const Data& rhs) const + { return operator()(lhs.first, rhs.first); } + + bool operator()(const Data& lhs, + const first_argument_type& rhs) const + { return operator()(lhs.first, rhs); } + + bool operator()(const first_argument_type& lhs, + const Data& rhs) const + { return operator()(lhs, rhs.first); } + }; + } + +//////////////////////////////////////////////////////////////////////////////// +// class template AssocVector +// An associative vector built as a syntactic drop-in replacement for std::map +// BEWARE: AssocVector doesn't respect all map's guarantees, the most important +// being: +// * iterators are invalidated by insert and erase operations +// * the complexity of insert/erase is O(N) not O(log N) +// * value_type is std::pair<K, V> not std::pair<const K, V> +// * iterators are random +//////////////////////////////////////////////////////////////////////////////// + + + template + < + class K, + class V, + class C = std::less<K>, + class A = std::allocator< std::pair<K, V> > + > + class AssocVector + : private std::vector< std::pair<K, V>, A > + , private Private::AssocVectorCompare<V, C> + { + typedef std::vector<std::pair<K, V>, A> Base; + typedef Private::AssocVectorCompare<V, C> MyCompare; + + public: + typedef K key_type; + typedef V mapped_type; + typedef typename Base::value_type value_type; + + typedef C key_compare; + typedef A allocator_type; + typedef typename A::reference reference; + typedef typename A::const_reference const_reference; + typedef typename Base::iterator iterator; + typedef typename Base::const_iterator const_iterator; + typedef typename Base::size_type size_type; + typedef typename Base::difference_type difference_type; + typedef typename A::pointer pointer; + typedef typename A::const_pointer const_pointer; + typedef typename Base::reverse_iterator reverse_iterator; + typedef typename Base::const_reverse_iterator const_reverse_iterator; + + class value_compare + : public std::binary_function<value_type, value_type, bool> + , private key_compare + { + friend class AssocVector; + + protected: + value_compare(key_compare pred) : key_compare(pred) + {} + + public: + bool operator()(const value_type& lhs, const value_type& rhs) const + { return key_compare::operator()(lhs.first, rhs.first); } + }; + + // 23.3.1.1 construct/copy/destroy + + explicit AssocVector(const key_compare& comp = key_compare(), + const A& alloc = A()) + : Base(alloc), MyCompare(comp) + {} + + template <class InputIterator> + AssocVector(InputIterator first, InputIterator last, + const key_compare& comp = key_compare(), + const A& alloc = A()) + : Base(first, last, alloc), MyCompare(comp) + { + MyCompare& me = *this; + std::sort(begin(), end(), me); + } + + AssocVector& operator=(const AssocVector& rhs) + { + AssocVector(rhs).swap(*this); + return *this; + } + + // iterators: + // The following are here because MWCW gets 'using' wrong + iterator begin() { return Base::begin(); } + const_iterator begin() const { return Base::begin(); } + iterator end() { return Base::end(); } + const_iterator end() const { return Base::end(); } + reverse_iterator rbegin() { return Base::rbegin(); } + const_reverse_iterator rbegin() const { return Base::rbegin(); } + reverse_iterator rend() { return Base::rend(); } + const_reverse_iterator rend() const { return Base::rend(); } + + // capacity: + bool empty() const { return Base::empty(); } + size_type size() const { return Base::size(); } + size_type max_size() { return Base::max_size(); } + + // 23.3.1.2 element access: + mapped_type& operator[](const key_type& key) + { return insert(value_type(key, mapped_type())).first->second; } + + // modifiers: + std::pair<iterator, bool> insert(const value_type& val) + { + bool found(true); + iterator i(lower_bound(val.first)); + + if (i == end() || this->operator()(val.first, i->first)) + { + i = Base::insert(i, val); + found = false; + } + return std::make_pair(i, !found); + } + //Section [23.1.2], Table 69 + //http://developer.apple.com/documentation/DeveloperTools/gcc-3.3/libstdc++/23_containers/howto.html#4 + iterator insert(iterator pos, const value_type& val) + { + if( (pos == begin() || this->operator()(*(pos-1),val)) && + (pos == end() || this->operator()(val, *pos)) ) + { + return Base::insert(pos, val); + } + return insert(val).first; + } + + template <class InputIterator> + void insert(InputIterator first, InputIterator last) + { for (; first != last; ++first) insert(*first); } + + void erase(iterator pos) + { Base::erase(pos); } + + size_type erase(const key_type& k) + { + iterator i(find(k)); + if (i == end()) return 0; + erase(i); + return 1; + } + + void erase(iterator first, iterator last) + { Base::erase(first, last); } + + void swap(AssocVector& other) + { + Base::swap(other); + MyCompare& me = *this; + MyCompare& rhs = other; + std::swap(me, rhs); + } + + void clear() + { Base::clear(); } + + // observers: + key_compare key_comp() const + { return *this; } + + value_compare value_comp() const + { + const key_compare& comp = *this; + return value_compare(comp); + } + + // 23.3.1.3 map operations: + iterator find(const key_type& k) + { + iterator i(lower_bound(k)); + if (i != end() && this->operator()(k, i->first)) + { + i = end(); + } + return i; + } + + const_iterator find(const key_type& k) const + { + const_iterator i(lower_bound(k)); + if (i != end() && this->operator()(k, i->first)) + { + i = end(); + } + return i; + } + + size_type count(const key_type& k) const + { return find(k) != end(); } + + iterator lower_bound(const key_type& k) + { + MyCompare& me = *this; + return std::lower_bound(begin(), end(), k, me); + } + + const_iterator lower_bound(const key_type& k) const + { + const MyCompare& me = *this; + return std::lower_bound(begin(), end(), k, me); + } + + iterator upper_bound(const key_type& k) + { + MyCompare& me = *this; + return std::upper_bound(begin(), end(), k, me); + } + + const_iterator upper_bound(const key_type& k) const + { + const MyCompare& me = *this; + return std::upper_bound(begin(), end(), k, me); + } + + std::pair<iterator, iterator> equal_range(const key_type& k) + { + MyCompare& me = *this; + return std::equal_range(begin(), end(), k, me); + } + + std::pair<const_iterator, const_iterator> equal_range( + const key_type& k) const + { + const MyCompare& me = *this; + return std::equal_range(begin(), end(), k, me); + } + + template <class K1, class V1, class C1, class A1> + friend bool operator==(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + + bool operator<(const AssocVector& rhs) const + { + const Base& me = *this; + const Base& yo = rhs; + return me < yo; + } + + template <class K1, class V1, class C1, class A1> + friend bool operator!=(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + + template <class K1, class V1, class C1, class A1> + friend bool operator>(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + + template <class K1, class V1, class C1, class A1> + friend bool operator>=(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + + template <class K1, class V1, class C1, class A1> + friend bool operator<=(const AssocVector<K1, V1, C1, A1>& lhs, + const AssocVector<K1, V1, C1, A1>& rhs); + }; + + template <class K, class V, class C, class A> + inline bool operator==(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { + const std::vector<std::pair<K, V>, A>& me = lhs; + return me == rhs; + } + + template <class K, class V, class C, class A> + inline bool operator!=(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { return !(lhs == rhs); } + + template <class K, class V, class C, class A> + inline bool operator>(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { return rhs < lhs; } + + template <class K, class V, class C, class A> + inline bool operator>=(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { return !(lhs < rhs); } + + template <class K, class V, class C, class A> + inline bool operator<=(const AssocVector<K, V, C, A>& lhs, + const AssocVector<K, V, C, A>& rhs) + { return !(rhs < lhs); } + + + // specialized algorithms: + template <class K, class V, class C, class A> + void swap(AssocVector<K, V, C, A>& lhs, AssocVector<K, V, C, A>& rhs) + { lhs.swap(rhs); } + +} // namespace Loki diff --git a/contrib/python/kiwisolver/py3/kiwi/constraint.h b/contrib/python/kiwisolver/py3/kiwi/constraint.h new file mode 100644 index 00000000000..558dd924aeb --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/constraint.h @@ -0,0 +1,124 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <map> +#include <vector> +#include "expression.h" +#include "shareddata.h" +#include "strength.h" +#include "term.h" +#include "variable.h" + + +namespace kiwi +{ + +enum RelationalOperator { OP_LE, OP_GE, OP_EQ }; + + +class Constraint +{ + +public: + + Constraint() : m_data( 0 ) {} + + Constraint( const Expression& expr, + RelationalOperator op, + double strength = strength::required ) : + m_data( new ConstraintData( expr, op, strength ) ) {} + + Constraint( const Constraint& other, double strength ) : + m_data( new ConstraintData( other, strength ) ) {} + + ~Constraint() {} + + const Expression& expression() const + { + return m_data->m_expression; + } + + RelationalOperator op() const + { + return m_data->m_op; + } + + double strength() const + { + return m_data->m_strength; + } + + bool operator!() const + { + return !m_data; + } + +private: + + static Expression reduce( const Expression& expr ) + { + std::map<Variable, double> vars; + typedef std::vector<Term>::const_iterator iter_t; + iter_t end = expr.terms().end(); + for( iter_t it = expr.terms().begin(); it != end; ++it ) + vars[ it->variable() ] += it->coefficient(); + std::vector<Term> terms( vars.begin(), vars.end() ); + return Expression( terms, expr.constant() ); + } + + class ConstraintData : public SharedData + { + + public: + + ConstraintData( const Expression& expr, + RelationalOperator op, + double strength ) : + SharedData(), + m_expression( reduce( expr ) ), + m_strength( strength::clip( strength ) ), + m_op( op ) {} + + ConstraintData( const Constraint& other, double strength ) : + SharedData(), + m_expression( other.expression() ), + m_strength( strength::clip( strength ) ), + m_op( other.op() ) {} + + ~ConstraintData() {} + + Expression m_expression; + double m_strength; + RelationalOperator m_op; + + private: + + ConstraintData( const ConstraintData& other ); + + ConstraintData& operator=( const ConstraintData& other ); + }; + + SharedDataPtr<ConstraintData> m_data; + + friend bool operator<( const Constraint& lhs, const Constraint& rhs ) + { + return lhs.m_data < rhs.m_data; + } + + friend bool operator==( const Constraint& lhs, const Constraint& rhs ) + { + return lhs.m_data == rhs.m_data; + } + + friend bool operator!=( const Constraint& lhs, const Constraint& rhs ) + { + return lhs.m_data != rhs.m_data; + } +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/debug.h b/contrib/python/kiwisolver/py3/kiwi/debug.h new file mode 100644 index 00000000000..007a24da5ed --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/debug.h @@ -0,0 +1,203 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <iostream> +#include <sstream> +#include <vector> +#include "constraint.h" +#include "solverimpl.h" +#include "term.h" + + +namespace kiwi +{ + +namespace impl +{ + +class DebugHelper +{ + +public: + + static void dump( const SolverImpl& solver, std::ostream& out ) + { + out << "Objective" << std::endl; + out << "---------" << std::endl; + dump( *solver.m_objective, out ); + out << std::endl; + out << "Tableau" << std::endl; + out << "-------" << std::endl; + dump( solver.m_rows, out ); + out << std::endl; + out << "Infeasible" << std::endl; + out << "----------" << std::endl; + dump( solver.m_infeasible_rows, out ); + out << std::endl; + out << "Variables" << std::endl; + out << "---------" << std::endl; + dump( solver.m_vars, out ); + out << std::endl; + out << "Edit Variables" << std::endl; + out << "--------------" << std::endl; + dump( solver.m_edits, out ); + out << std::endl; + out << "Constraints" << std::endl; + out << "-----------" << std::endl; + dump( solver.m_cns, out ); + out << std::endl; + out << std::endl; + } + + static void dump( const SolverImpl::RowMap& rows, std::ostream& out ) + { + typedef SolverImpl::RowMap::const_iterator iter_t; + iter_t end = rows.end(); + for( iter_t it = rows.begin(); it != end; ++it ) + { + dump( it->first, out ); + out << " | "; + dump( *it->second, out ); + } + } + + static void dump( const std::vector<Symbol>& symbols, std::ostream& out ) + { + typedef std::vector<Symbol>::const_iterator iter_t; + iter_t end = symbols.end(); + for( iter_t it = symbols.begin(); it != end; ++it ) + { + dump( *it, out ); + out << std::endl; + } + } + + static void dump( const SolverImpl::VarMap& vars, std::ostream& out ) + { + typedef SolverImpl::VarMap::const_iterator iter_t; + iter_t end = vars.end(); + for( iter_t it = vars.begin(); it != end; ++it ) + { + out << it->first.name() << " = "; + dump( it->second, out ); + out << std::endl; + } + } + + static void dump( const SolverImpl::CnMap& cns, std::ostream& out ) + { + typedef SolverImpl::CnMap::const_iterator iter_t; + iter_t end = cns.end(); + for( iter_t it = cns.begin(); it != end; ++it ) + dump( it->first, out ); + } + + static void dump( const SolverImpl::EditMap& edits, std::ostream& out ) + { + typedef SolverImpl::EditMap::const_iterator iter_t; + iter_t end = edits.end(); + for( iter_t it = edits.begin(); it != end; ++it ) + out << it->first.name() << std::endl; + } + + static void dump( const Row& row, std::ostream& out ) + { + typedef Row::CellMap::const_iterator iter_t; + out << row.constant(); + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + out << " + " << it->second << " * "; + dump( it->first, out ); + } + out << std::endl; + } + + static void dump( const Symbol& symbol, std::ostream& out ) + { + switch( symbol.type() ) + { + case Symbol::Invalid: + out << "i"; + break; + case Symbol::External: + out << "v"; + break; + case Symbol::Slack: + out << "s"; + break; + case Symbol::Error: + out << "e"; + break; + case Symbol::Dummy: + out << "d"; + break; + default: + break; + } + out << symbol.id(); + } + + static void dump( const Constraint& cn, std::ostream& out ) + { + typedef std::vector<Term>::const_iterator iter_t; + iter_t begin = cn.expression().terms().begin(); + iter_t end = cn.expression().terms().end(); + for( iter_t it = begin; it != end; ++it ) + { + out << it->coefficient() << " * "; + out << it->variable().name() << " + "; + } + out << cn.expression().constant(); + switch( cn.op() ) + { + case OP_LE: + out << " <= 0 "; + break; + case OP_GE: + out << " >= 0 "; + break; + case OP_EQ: + out << " == 0 "; + break; + default: + break; + } + out << " | strength = " << cn.strength() << std::endl; + } +}; + +} // namespace impl + + +namespace debug +{ + +template<typename T> +void dump( const T& value ) +{ + impl::DebugHelper::dump( value, std::cout ); +} + +template<typename T> +void dump( const T& value, std::ostream& out ) +{ + impl::DebugHelper::dump( value, out ); +} + +template<typename T> +std::string dumps( const T& value ) +{ + std::stringstream stream; + impl::DebugHelper::dump( value, stream ); + return stream.str(); +} + +} // namespace debug + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/errors.h b/contrib/python/kiwisolver/py3/kiwi/errors.h new file mode 100644 index 00000000000..6c77eee0b70 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/errors.h @@ -0,0 +1,188 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <exception> +#include <string> +#include "constraint.h" +#include "variable.h" + + +namespace kiwi +{ + + +class UnsatisfiableConstraint : public std::exception +{ + +public: + + UnsatisfiableConstraint( const Constraint& constraint ) : + m_constraint( constraint ) {} + + ~UnsatisfiableConstraint() throw() {} + + const char* what() const throw() + { + return "The constraint can not be satisfied."; + } + + const Constraint& constraint() const + { + return m_constraint; + } + +private: + + Constraint m_constraint; +}; + + +class UnknownConstraint : public std::exception +{ + +public: + + UnknownConstraint( const Constraint& constraint ) : + m_constraint( constraint ) {} + + ~UnknownConstraint() throw() {} + + const char* what() const throw() + { + return "The constraint has not been added to the solver."; + } + + const Constraint& constraint() const + { + return m_constraint; + } + +private: + + Constraint m_constraint; +}; + + +class DuplicateConstraint : public std::exception +{ + +public: + + DuplicateConstraint( const Constraint& constraint ) : + m_constraint( constraint ) {} + + ~DuplicateConstraint() throw() {} + + const char* what() const throw() + { + return "The constraint has already been added to the solver."; + } + + const Constraint& constraint() const + { + return m_constraint; + } + +private: + + Constraint m_constraint; +}; + + +class UnknownEditVariable : public std::exception +{ + +public: + + UnknownEditVariable( const Variable& variable ) : + m_variable( variable ) {} + + ~UnknownEditVariable() throw() {} + + const char* what() const throw() + { + return "The edit variable has not been added to the solver."; + } + + const Variable& variable() const + { + return m_variable; + } + +private: + + Variable m_variable; +}; + + +class DuplicateEditVariable : public std::exception +{ + +public: + + DuplicateEditVariable( const Variable& variable ) : + m_variable( variable ) {} + + ~DuplicateEditVariable() throw() {} + + const char* what() const throw() + { + return "The edit variable has already been added to the solver."; + } + + const Variable& variable() const + { + return m_variable; + } + +private: + + Variable m_variable; +}; + + +class BadRequiredStrength : public std::exception +{ + +public: + + BadRequiredStrength() {} + + ~BadRequiredStrength() throw() {} + + const char* what() const throw() + { + return "A required strength cannot be used in this context."; + } +}; + + +class InternalSolverError : public std::exception +{ + +public: + + InternalSolverError() : m_msg( "An internal solver error ocurred." ) {} + + InternalSolverError( const char* msg ) : m_msg( msg ) {} + + InternalSolverError( const std::string& msg ) : m_msg( msg ) {} + + ~InternalSolverError() throw() {} + + const char* what() const throw() + { + return m_msg.c_str(); + } + +private: + + std::string m_msg; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/expression.h b/contrib/python/kiwisolver/py3/kiwi/expression.h new file mode 100644 index 00000000000..6d757206bd7 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/expression.h @@ -0,0 +1,57 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <vector> +#include "term.h" + + +namespace kiwi +{ + +class Expression +{ + +public: + + Expression( double constant = 0.0 ) : m_constant( constant ) {} + + Expression( const Term& term, double constant = 0.0 ) : + m_terms( 1, term ), m_constant( constant ) {} + + Expression( const std::vector<Term>& terms, double constant = 0.0 ) : + m_terms( terms ), m_constant( constant ) {} + + ~Expression() {} + + const std::vector<Term>& terms() const + { + return m_terms; + } + + double constant() const + { + return m_constant; + } + + double value() const + { + typedef std::vector<Term>::const_iterator iter_t; + double result = m_constant; + iter_t end = m_terms.end(); + for( iter_t it = m_terms.begin(); it != end; ++it ) + result += it->value(); + return result; + } + +private: + + std::vector<Term> m_terms; + double m_constant; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/kiwi.h b/contrib/python/kiwisolver/py3/kiwi/kiwi.h new file mode 100644 index 00000000000..e5e3d85c90b --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/kiwi.h @@ -0,0 +1,19 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include "constraint.h" +#include "debug.h" +#include "errors.h" +#include "expression.h" +#include "shareddata.h" +#include "solver.h" +#include "strength.h" +#include "symbolics.h" +#include "term.h" +#include "variable.h" +#include "version.h" diff --git a/contrib/python/kiwisolver/py3/kiwi/maptype.h b/contrib/python/kiwisolver/py3/kiwi/maptype.h new file mode 100644 index 00000000000..d125b07cc2d --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/maptype.h @@ -0,0 +1,38 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <functional> +#include <map> +#include <memory> +#include <utility> +#include "AssocVector.h" + + +namespace kiwi +{ + +namespace impl +{ + +template< + typename K, + typename V, + typename C = std::less<K>, + typename A = std::allocator< std::pair<K, V> > > +class MapType +{ +public: + typedef Loki::AssocVector<K, V, C, A> Type; + //typedef std::map<K, V, C, A> Type; +private: + MapType(); +}; + +} // namespace impl + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/row.h b/contrib/python/kiwisolver/py3/kiwi/row.h new file mode 100644 index 00000000000..33b501df76c --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/row.h @@ -0,0 +1,192 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include "maptype.h" +#include "symbol.h" +#include "util.h" + + +namespace kiwi +{ + +namespace impl +{ + +class Row +{ + +public: + + typedef MapType<Symbol, double>::Type CellMap; + + Row() : m_constant( 0.0 ) {} + + Row( double constant ) : m_constant( constant ) {} + + Row( const Row& other ) : + m_cells( other.m_cells ), m_constant( other.m_constant ) {} + + ~Row() {} + + const CellMap& cells() const + { + return m_cells; + } + + double constant() const + { + return m_constant; + } + + /* Add a constant value to the row constant. + + The new value of the constant is returned. + + */ + double add( double value ) + { + return m_constant += value; + } + + /* Insert a symbol into the row with a given coefficient. + + If the symbol already exists in the row, the coefficient will be + added to the existing coefficient. If the resulting coefficient + is zero, the symbol will be removed from the row. + + */ + void insert( const Symbol& symbol, double coefficient = 1.0 ) + { + if( nearZero( m_cells[ symbol ] += coefficient ) ) + m_cells.erase( symbol ); + } + + /* Insert a row into this row with a given coefficient. + + The constant and the cells of the other row will be multiplied by + the coefficient and added to this row. Any cell with a resulting + coefficient of zero will be removed from the row. + + */ + void insert( const Row& other, double coefficient = 1.0 ) + { + typedef CellMap::const_iterator iter_t; + m_constant += other.m_constant * coefficient; + iter_t end = other.m_cells.end(); + for( iter_t it = other.m_cells.begin(); it != end; ++it ) + { + double coeff = it->second * coefficient; + if( nearZero( m_cells[ it->first ] += coeff ) ) + m_cells.erase( it->first ); + } + } + + /* Remove the given symbol from the row. + + */ + void remove( const Symbol& symbol ) + { + CellMap::iterator it = m_cells.find( symbol ); + if( it != m_cells.end() ) + m_cells.erase( it ); + } + + /* Reverse the sign of the constant and all cells in the row. + + */ + void reverseSign() + { + typedef CellMap::iterator iter_t; + m_constant = -m_constant; + iter_t end = m_cells.end(); + for( iter_t it = m_cells.begin(); it != end; ++it ) + it->second = -it->second; + } + + /* Solve the row for the given symbol. + + This method assumes the row is of the form a * x + b * y + c = 0 + and (assuming solve for x) will modify the row to represent the + right hand side of x = -b/a * y - c / a. The target symbol will + be removed from the row, and the constant and other cells will + be multiplied by the negative inverse of the target coefficient. + + The given symbol *must* exist in the row. + + */ + void solveFor( const Symbol& symbol ) + { + typedef CellMap::iterator iter_t; + double coeff = -1.0 / m_cells[ symbol ]; + m_cells.erase( symbol ); + m_constant *= coeff; + iter_t end = m_cells.end(); + for( iter_t it = m_cells.begin(); it != end; ++it ) + it->second *= coeff; + } + + /* Solve the row for the given symbols. + + This method assumes the row is of the form x = b * y + c and will + solve the row such that y = x / b - c / b. The rhs symbol will be + removed from the row, the lhs added, and the result divided by the + negative inverse of the rhs coefficient. + + The lhs symbol *must not* exist in the row, and the rhs symbol + *must* exist in the row. + + */ + void solveFor( const Symbol& lhs, const Symbol& rhs ) + { + insert( lhs, -1.0 ); + solveFor( rhs ); + } + + /* Get the coefficient for the given symbol. + + If the symbol does not exist in the row, zero will be returned. + + */ + double coefficientFor( const Symbol& symbol ) const + { + CellMap::const_iterator it = m_cells.find( symbol ); + if( it == m_cells.end() ) + return 0.0; + return it->second; + } + + /* Substitute a symbol with the data from another row. + + Given a row of the form a * x + b and a substitution of the + form x = 3 * y + c the row will be updated to reflect the + expression 3 * a * y + a * c + b. + + If the symbol does not exist in the row, this is a no-op. + + */ + void substitute( const Symbol& symbol, const Row& row ) + { + typedef CellMap::iterator iter_t; + iter_t it = m_cells.find( symbol ); + if( it != m_cells.end() ) + { + double coefficient = it->second; + m_cells.erase( it ); + insert( row, coefficient ); + } + } + +private: + + CellMap m_cells; + double m_constant; +}; + +} // namespace impl + +} // namespace diff --git a/contrib/python/kiwisolver/py3/kiwi/shareddata.h b/contrib/python/kiwisolver/py3/kiwi/shareddata.h new file mode 100644 index 00000000000..b7d389ee6dc --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/shareddata.h @@ -0,0 +1,157 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once + + +namespace kiwi +{ + +class SharedData +{ + +public: + + SharedData() : m_refcount( 0 ) {} + + SharedData( const SharedData& other ) : m_refcount( 0 ) {} + + int m_refcount; + +private: + + SharedData& operator=( const SharedData& other ); +}; + + +template<typename T> +class SharedDataPtr +{ + +public: + + typedef T Type; + + SharedDataPtr() : m_data( 0 ) {} + + explicit SharedDataPtr( T* data ) : m_data( data ) + { + incref( m_data ); + } + + ~SharedDataPtr() + { + decref( m_data ); + } + + T* data() + { + return m_data; + } + + const T* data() const + { + return m_data; + } + + operator T*() + { + return m_data; + } + + operator const T*() const + { + return m_data; + } + + T* operator->() + { + return m_data; + } + + const T* operator->() const + { + return m_data; + } + + T& operator*() + { + return *m_data; + } + + const T& operator*() const + { + return *m_data; + } + + bool operator!() const + { + return !m_data; + } + + bool operator<( const SharedDataPtr<T>& other ) const + { + return m_data < other.m_data; + } + + bool operator==( const SharedDataPtr<T>& other ) const + { + return m_data == other.m_data; + } + + bool operator!=( const SharedDataPtr<T>& other ) const + { + return m_data != other.m_data; + } + + SharedDataPtr( const SharedDataPtr<T>& other ) : m_data( other.m_data ) + { + incref( m_data ); + } + + SharedDataPtr<T>& operator=( const SharedDataPtr<T>& other ) + { + if( m_data != other.m_data ) + { + T* temp = m_data; + m_data = other.m_data; + incref( m_data ); + decref( temp ); + } + return *this; + } + + SharedDataPtr<T>& operator=( T* other ) + { + if( m_data != other ) + { + T* temp = m_data; + m_data = other; + incref( m_data ); + decref( temp ); + } + return *this; + } + +private: + + static void incref( T* data ) + { + if( data ) + ++data->m_refcount; + } + + static void decref( T* data ) + { + if( data && --data->m_refcount == 0 ) + delete data; + } + + T* m_data; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/solver.h b/contrib/python/kiwisolver/py3/kiwi/solver.h new file mode 100644 index 00000000000..8eed1841bf9 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/solver.h @@ -0,0 +1,178 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include "constraint.h" +#include "debug.h" +#include "solverimpl.h" +#include "strength.h" +#include "variable.h" + + +namespace kiwi +{ + +class Solver +{ + +public: + + Solver() {} + + ~Solver() {} + + /* Add a constraint to the solver. + + Throws + ------ + DuplicateConstraint + The given constraint has already been added to the solver. + + UnsatisfiableConstraint + The given constraint is required and cannot be satisfied. + + */ + void addConstraint( const Constraint& constraint ) + { + m_impl.addConstraint( constraint ); + } + + /* Remove a constraint from the solver. + + Throws + ------ + UnknownConstraint + The given constraint has not been added to the solver. + + */ + void removeConstraint( const Constraint& constraint ) + { + m_impl.removeConstraint( constraint ); + } + + /* Test whether a constraint has been added to the solver. + + */ + bool hasConstraint( const Constraint& constraint ) const + { + return m_impl.hasConstraint( constraint ); + } + + /* Add an edit variable to the solver. + + This method should be called before the `suggestValue` method is + used to supply a suggested value for the given edit variable. + + Throws + ------ + DuplicateEditVariable + The given edit variable has already been added to the solver. + + BadRequiredStrength + The given strength is >= required. + + */ + void addEditVariable( const Variable& variable, double strength ) + { + m_impl.addEditVariable( variable, strength ); + } + + /* Remove an edit variable from the solver. + + Throws + ------ + UnknownEditVariable + The given edit variable has not been added to the solver. + + */ + void removeEditVariable( const Variable& variable ) + { + m_impl.removeEditVariable( variable ); + } + + /* Test whether an edit variable has been added to the solver. + + */ + bool hasEditVariable( const Variable& variable ) const + { + return m_impl.hasEditVariable( variable ); + } + + /* Suggest a value for the given edit variable. + + This method should be used after an edit variable as been added to + the solver in order to suggest the value for that variable. After + all suggestions have been made, the `solve` method can be used to + update the values of all variables. + + Throws + ------ + UnknownEditVariable + The given edit variable has not been added to the solver. + + */ + void suggestValue( const Variable& variable, double value ) + { + m_impl.suggestValue( variable, value ); + } + + /* Update the values of the external solver variables. + + */ + void updateVariables() + { + m_impl.updateVariables(); + } + + /* Reset the solver to the empty starting condition. + + This method resets the internal solver state to the empty starting + condition, as if no constraints or edit variables have been added. + This can be faster than deleting the solver and creating a new one + when the entire system must change, since it can avoid unecessary + heap (de)allocations. + + */ + void reset() + { + m_impl.reset(); + } + + /* Dump a representation of the solver internals to stdout. + + */ + void dump() + { + debug::dump( m_impl ); + } + + /* Dump a representation of the solver internals to a stream. + + */ + void dump( std::ostream& out ) + { + debug::dump( m_impl, out ); + } + + /* Dump a representation of the solver internals to a string. + + */ + std::string dumps() + { + return debug::dumps( m_impl ); + } + +private: + + Solver( const Solver& ); + + Solver& operator=( const Solver& ); + + impl::SolverImpl m_impl; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/solverimpl.h b/contrib/python/kiwisolver/py3/kiwi/solverimpl.h new file mode 100644 index 00000000000..9b56ee28188 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/solverimpl.h @@ -0,0 +1,840 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <algorithm> +#include <limits> +#include <memory> +#include <vector> +#include "constraint.h" +#include "errors.h" +#include "expression.h" +#include "maptype.h" +#include "row.h" +#include "symbol.h" +#include "term.h" +#include "util.h" +#include "variable.h" + + +namespace kiwi +{ + +namespace impl +{ + +class SolverImpl +{ + friend class DebugHelper; + + struct Tag + { + Symbol marker; + Symbol other; + }; + + struct EditInfo + { + Tag tag; + Constraint constraint; + double constant; + }; + + typedef MapType<Variable, Symbol>::Type VarMap; + + typedef MapType<Symbol, Row*>::Type RowMap; + + typedef MapType<Constraint, Tag>::Type CnMap; + + typedef MapType<Variable, EditInfo>::Type EditMap; + + struct DualOptimizeGuard + { + DualOptimizeGuard( SolverImpl& impl ) : m_impl( impl ) {} + ~DualOptimizeGuard() { m_impl.dualOptimize(); } + SolverImpl& m_impl; + }; + +public: + + SolverImpl() : m_objective( new Row() ), m_id_tick( 1 ) {} + + ~SolverImpl() { clearRows(); } + + /* Add a constraint to the solver. + + Throws + ------ + DuplicateConstraint + The given constraint has already been added to the solver. + + UnsatisfiableConstraint + The given constraint is required and cannot be satisfied. + + */ + void addConstraint( const Constraint& constraint ) + { + if( m_cns.find( constraint ) != m_cns.end() ) + throw DuplicateConstraint( constraint ); + + // Creating a row causes symbols to be reserved for the variables + // in the constraint. If this method exits with an exception, + // then its possible those variables will linger in the var map. + // Since its likely that those variables will be used in other + // constraints and since exceptional conditions are uncommon, + // i'm not too worried about aggressive cleanup of the var map. + Tag tag; + std::auto_ptr<Row> rowptr( createRow( constraint, tag ) ); + Symbol subject( chooseSubject( *rowptr, tag ) ); + + // If chooseSubject could not find a valid entering symbol, one + // last option is available if the entire row is composed of + // dummy variables. If the constant of the row is zero, then + // this represents redundant constraints and the new dummy + // marker can enter the basis. If the constant is non-zero, + // then it represents an unsatisfiable constraint. + if( subject.type() == Symbol::Invalid && allDummies( *rowptr ) ) + { + if( !nearZero( rowptr->constant() ) ) + throw UnsatisfiableConstraint( constraint ); + else + subject = tag.marker; + } + + // If an entering symbol still isn't found, then the row must + // be added using an artificial variable. If that fails, then + // the row represents an unsatisfiable constraint. + if( subject.type() == Symbol::Invalid ) + { + if( !addWithArtificialVariable( *rowptr ) ) + throw UnsatisfiableConstraint( constraint ); + } + else + { + rowptr->solveFor( subject ); + substitute( subject, *rowptr ); + m_rows[ subject ] = rowptr.release(); + } + + m_cns[ constraint ] = tag; + + // Optimizing after each constraint is added performs less + // aggregate work due to a smaller average system size. It + // also ensures the solver remains in a consistent state. + optimize( *m_objective ); + } + + /* Remove a constraint from the solver. + + Throws + ------ + UnknownConstraint + The given constraint has not been added to the solver. + + */ + void removeConstraint( const Constraint& constraint ) + { + CnMap::iterator cn_it = m_cns.find( constraint ); + if( cn_it == m_cns.end() ) + throw UnknownConstraint( constraint ); + + Tag tag( cn_it->second ); + m_cns.erase( cn_it ); + + // Remove the error effects from the objective function + // *before* pivoting, or substitutions into the objective + // will lead to incorrect solver results. + removeConstraintEffects( constraint, tag ); + + // If the marker is basic, simply drop the row. Otherwise, + // pivot the marker into the basis and then drop the row. + RowMap::iterator row_it = m_rows.find( tag.marker ); + if( row_it != m_rows.end() ) + { + std::auto_ptr<Row> rowptr( row_it->second ); + m_rows.erase( row_it ); + } + else + { + row_it = getMarkerLeavingRow( tag.marker ); + if( row_it == m_rows.end() ) + throw InternalSolverError( "failed to find leaving row" ); + Symbol leaving( row_it->first ); + std::auto_ptr<Row> rowptr( row_it->second ); + m_rows.erase( row_it ); + rowptr->solveFor( leaving, tag.marker ); + substitute( tag.marker, *rowptr ); + } + + // Optimizing after each constraint is removed ensures that the + // solver remains consistent. It makes the solver api easier to + // use at a small tradeoff for speed. + optimize( *m_objective ); + } + + /* Test whether a constraint has been added to the solver. + + */ + bool hasConstraint( const Constraint& constraint ) const + { + return m_cns.find( constraint ) != m_cns.end(); + } + + /* Add an edit variable to the solver. + + This method should be called before the `suggestValue` method is + used to supply a suggested value for the given edit variable. + + Throws + ------ + DuplicateEditVariable + The given edit variable has already been added to the solver. + + BadRequiredStrength + The given strength is >= required. + + */ + void addEditVariable( const Variable& variable, double strength ) + { + if( m_edits.find( variable ) != m_edits.end() ) + throw DuplicateEditVariable( variable ); + strength = strength::clip( strength ); + if( strength == strength::required ) + throw BadRequiredStrength(); + Constraint cn( Expression( variable ), OP_EQ, strength ); + addConstraint( cn ); + EditInfo info; + info.tag = m_cns[ cn ]; + info.constraint = cn; + info.constant = 0.0; + m_edits[ variable ] = info; + } + + /* Remove an edit variable from the solver. + + Throws + ------ + UnknownEditVariable + The given edit variable has not been added to the solver. + + */ + void removeEditVariable( const Variable& variable ) + { + EditMap::iterator it = m_edits.find( variable ); + if( it == m_edits.end() ) + throw UnknownEditVariable( variable ); + removeConstraint( it->second.constraint ); + m_edits.erase( it ); + } + + /* Test whether an edit variable has been added to the solver. + + */ + bool hasEditVariable( const Variable& variable ) const + { + return m_edits.find( variable ) != m_edits.end(); + } + + /* Suggest a value for the given edit variable. + + This method should be used after an edit variable as been added to + the solver in order to suggest the value for that variable. + + Throws + ------ + UnknownEditVariable + The given edit variable has not been added to the solver. + + */ + void suggestValue( const Variable& variable, double value ) + { + EditMap::iterator it = m_edits.find( variable ); + if( it == m_edits.end() ) + throw UnknownEditVariable( variable ); + + DualOptimizeGuard guard( *this ); + EditInfo& info = it->second; + double delta = value - info.constant; + info.constant = value; + + // Check first if the positive error variable is basic. + RowMap::iterator row_it = m_rows.find( info.tag.marker ); + if( row_it != m_rows.end() ) + { + if( row_it->second->add( -delta ) < 0.0 ) + m_infeasible_rows.push_back( row_it->first ); + return; + } + + // Check next if the negative error variable is basic. + row_it = m_rows.find( info.tag.other ); + if( row_it != m_rows.end() ) + { + if( row_it->second->add( delta ) < 0.0 ) + m_infeasible_rows.push_back( row_it->first ); + return; + } + + // Otherwise update each row where the error variables exist. + RowMap::iterator end = m_rows.end(); + for( row_it = m_rows.begin(); row_it != end; ++row_it ) + { + double coeff = row_it->second->coefficientFor( info.tag.marker ); + if( coeff != 0.0 && + row_it->second->add( delta * coeff ) < 0.0 && + row_it->first.type() != Symbol::External ) + m_infeasible_rows.push_back( row_it->first ); + } + } + + /* Update the values of the external solver variables. + + */ + void updateVariables() + { + typedef RowMap::iterator row_iter_t; + typedef VarMap::iterator var_iter_t; + row_iter_t row_end = m_rows.end(); + var_iter_t var_end = m_vars.end(); + for( var_iter_t var_it = m_vars.begin(); var_it != var_end; ++var_it ) + { + Variable& var( const_cast<Variable&>( var_it->first ) ); + row_iter_t row_it = m_rows.find( var_it->second ); + if( row_it == row_end ) + var.setValue( 0.0 ); + else + var.setValue( row_it->second->constant() ); + } + } + + /* Reset the solver to the empty starting condition. + + This method resets the internal solver state to the empty starting + condition, as if no constraints or edit variables have been added. + This can be faster than deleting the solver and creating a new one + when the entire system must change, since it can avoid unecessary + heap (de)allocations. + + */ + void reset() + { + clearRows(); + m_cns.clear(); + m_vars.clear(); + m_edits.clear(); + m_infeasible_rows.clear(); + m_objective.reset( new Row() ); + m_artificial.reset(); + m_id_tick = 1; + } + +private: + + SolverImpl( const SolverImpl& ); + + SolverImpl& operator=( const SolverImpl& ); + + struct RowDeleter + { + template<typename T> + void operator()( T& pair ) { delete pair.second; } + }; + + void clearRows() + { + std::for_each( m_rows.begin(), m_rows.end(), RowDeleter() ); + m_rows.clear(); + } + + /* Get the symbol for the given variable. + + If a symbol does not exist for the variable, one will be created. + + */ + Symbol getVarSymbol( const Variable& variable ) + { + VarMap::iterator it = m_vars.find( variable ); + if( it != m_vars.end() ) + return it->second; + Symbol symbol( Symbol::External, m_id_tick++ ); + m_vars[ variable ] = symbol; + return symbol; + } + + /* Create a new Row object for the given constraint. + + The terms in the constraint will be converted to cells in the row. + Any term in the constraint with a coefficient of zero is ignored. + This method uses the `getVarSymbol` method to get the symbol for + the variables added to the row. If the symbol for a given cell + variable is basic, the cell variable will be substituted with the + basic row. + + The necessary slack and error variables will be added to the row. + If the constant for the row is negative, the sign for the row + will be inverted so the constant becomes positive. + + The tag will be updated with the marker and error symbols to use + for tracking the movement of the constraint in the tableau. + + */ + Row* createRow( const Constraint& constraint, Tag& tag ) + { + typedef std::vector<Term>::const_iterator iter_t; + const Expression& expr( constraint.expression() ); + Row* row = new Row( expr.constant() ); + + // Substitute the current basic variables into the row. + iter_t end = expr.terms().end(); + for( iter_t it = expr.terms().begin(); it != end; ++it ) + { + if( !nearZero( it->coefficient() ) ) + { + Symbol symbol( getVarSymbol( it->variable() ) ); + RowMap::const_iterator row_it = m_rows.find( symbol ); + if( row_it != m_rows.end() ) + row->insert( *row_it->second, it->coefficient() ); + else + row->insert( symbol, it->coefficient() ); + } + } + + // Add the necessary slack, error, and dummy variables. + switch( constraint.op() ) + { + case OP_LE: + case OP_GE: + { + double coeff = constraint.op() == OP_LE ? 1.0 : -1.0; + Symbol slack( Symbol::Slack, m_id_tick++ ); + tag.marker = slack; + row->insert( slack, coeff ); + if( constraint.strength() < strength::required ) + { + Symbol error( Symbol::Error, m_id_tick++ ); + tag.other = error; + row->insert( error, -coeff ); + m_objective->insert( error, constraint.strength() ); + } + break; + } + case OP_EQ: + { + if( constraint.strength() < strength::required ) + { + Symbol errplus( Symbol::Error, m_id_tick++ ); + Symbol errminus( Symbol::Error, m_id_tick++ ); + tag.marker = errplus; + tag.other = errminus; + row->insert( errplus, -1.0 ); // v = eplus - eminus + row->insert( errminus, 1.0 ); // v - eplus + eminus = 0 + m_objective->insert( errplus, constraint.strength() ); + m_objective->insert( errminus, constraint.strength() ); + } + else + { + Symbol dummy( Symbol::Dummy, m_id_tick++ ); + tag.marker = dummy; + row->insert( dummy ); + } + break; + } + } + + // Ensure the row as a positive constant. + if( row->constant() < 0.0 ) + row->reverseSign(); + + return row; + } + + /* Choose the subject for solving for the row. + + This method will choose the best subject for using as the solve + target for the row. An invalid symbol will be returned if there + is no valid target. + + The symbols are chosen according to the following precedence: + + 1) The first symbol representing an external variable. + 2) A negative slack or error tag variable. + + If a subject cannot be found, an invalid symbol will be returned. + + */ + Symbol chooseSubject( const Row& row, const Tag& tag ) + { + typedef Row::CellMap::const_iterator iter_t; + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + if( it->first.type() == Symbol::External ) + return it->first; + } + if( tag.marker.type() == Symbol::Slack || tag.marker.type() == Symbol::Error ) + { + if( row.coefficientFor( tag.marker ) < 0.0 ) + return tag.marker; + } + if( tag.other.type() == Symbol::Slack || tag.other.type() == Symbol::Error ) + { + if( row.coefficientFor( tag.other ) < 0.0 ) + return tag.other; + } + return Symbol(); + } + + /* Add the row to the tableau using an artificial variable. + + This will return false if the constraint cannot be satisfied. + + */ + bool addWithArtificialVariable( const Row& row ) + { + // Create and add the artificial variable to the tableau + Symbol art( Symbol::Slack, m_id_tick++ ); + m_rows[ art ] = new Row( row ); + m_artificial.reset( new Row( row ) ); + + // Optimize the artificial objective. This is successful + // only if the artificial objective is optimized to zero. + optimize( *m_artificial ); + bool success = nearZero( m_artificial->constant() ); + m_artificial.reset(); + + // If the artificial variable is not basic, pivot the row so that + // it becomes basic. If the row is constant, exit early. + RowMap::iterator it = m_rows.find( art ); + if( it != m_rows.end() ) + { + std::auto_ptr<Row> rowptr( it->second ); + m_rows.erase( it ); + if( rowptr->cells().empty() ) + return success; + Symbol entering( anyPivotableSymbol( *rowptr ) ); + if( entering.type() == Symbol::Invalid ) + return false; // unsatisfiable (will this ever happen?) + rowptr->solveFor( art, entering ); + substitute( entering, *rowptr ); + m_rows[ entering ] = rowptr.release(); + } + + // Remove the artificial variable from the tableau. + RowMap::iterator end = m_rows.end(); + for( it = m_rows.begin(); it != end; ++it ) + it->second->remove( art ); + m_objective->remove( art ); + return success; + } + + /* Substitute the parametric symbol with the given row. + + This method will substitute all instances of the parametric symbol + in the tableau and the objective function with the given row. + + */ + void substitute( const Symbol& symbol, const Row& row ) + { + typedef RowMap::iterator iter_t; + iter_t end = m_rows.end(); + for( iter_t it = m_rows.begin(); it != end; ++it ) + { + it->second->substitute( symbol, row ); + if( it->first.type() != Symbol::External && + it->second->constant() < 0.0 ) + m_infeasible_rows.push_back( it->first ); + } + m_objective->substitute( symbol, row ); + if( m_artificial.get() ) + m_artificial->substitute( symbol, row ); + } + + /* Optimize the system for the given objective function. + + This method performs iterations of Phase 2 of the simplex method + until the objective function reaches a minimum. + + Throws + ------ + InternalSolverError + The value of the objective function is unbounded. + + */ + void optimize( const Row& objective ) + { + while( true ) + { + Symbol entering( getEnteringSymbol( objective ) ); + if( entering.type() == Symbol::Invalid ) + return; + RowMap::iterator it = getLeavingRow( entering ); + if( it == m_rows.end() ) + throw InternalSolverError( "The objective is unbounded." ); + // pivot the entering symbol into the basis + Symbol leaving( it->first ); + Row* row = it->second; + m_rows.erase( it ); + row->solveFor( leaving, entering ); + substitute( entering, *row ); + m_rows[ entering ] = row; + } + } + + /* Optimize the system using the dual of the simplex method. + + The current state of the system should be such that the objective + function is optimal, but not feasible. This method will perform + an iteration of the dual simplex method to make the solution both + optimal and feasible. + + Throws + ------ + InternalSolverError + The system cannot be dual optimized. + + */ + void dualOptimize() + { + while( !m_infeasible_rows.empty() ) + { + + Symbol leaving( m_infeasible_rows.back() ); + m_infeasible_rows.pop_back(); + RowMap::iterator it = m_rows.find( leaving ); + if( it != m_rows.end() && !nearZero( it->second->constant() ) && + it->second->constant() < 0.0 ) + { + Symbol entering( getDualEnteringSymbol( *it->second ) ); + if( entering.type() == Symbol::Invalid ) + throw InternalSolverError( "Dual optimize failed." ); + // pivot the entering symbol into the basis + Row* row = it->second; + m_rows.erase( it ); + row->solveFor( leaving, entering ); + substitute( entering, *row ); + m_rows[ entering ] = row; + } + } + } + + /* Compute the entering variable for a pivot operation. + + This method will return first symbol in the objective function which + is non-dummy and has a coefficient less than zero. If no symbol meets + the criteria, it means the objective function is at a minimum, and an + invalid symbol is returned. + + */ + Symbol getEnteringSymbol( const Row& objective ) + { + typedef Row::CellMap::const_iterator iter_t; + iter_t end = objective.cells().end(); + for( iter_t it = objective.cells().begin(); it != end; ++it ) + { + if( it->first.type() != Symbol::Dummy && it->second < 0.0 ) + return it->first; + } + return Symbol(); + } + + /* Compute the entering symbol for the dual optimize operation. + + This method will return the symbol in the row which has a positive + coefficient and yields the minimum ratio for its respective symbol + in the objective function. The provided row *must* be infeasible. + If no symbol is found which meats the criteria, an invalid symbol + is returned. + + */ + Symbol getDualEnteringSymbol( const Row& row ) + { + typedef Row::CellMap::const_iterator iter_t; + Symbol entering; + double ratio = std::numeric_limits<double>::max(); + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + if( it->second > 0.0 && it->first.type() != Symbol::Dummy ) + { + double coeff = m_objective->coefficientFor( it->first ); + double r = coeff / it->second; + if( r < ratio ) + { + ratio = r; + entering = it->first; + } + } + } + return entering; + } + + /* Get the first Slack or Error symbol in the row. + + If no such symbol is present, and Invalid symbol will be returned. + + */ + Symbol anyPivotableSymbol( const Row& row ) + { + typedef Row::CellMap::const_iterator iter_t; + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + const Symbol& sym( it->first ); + if( sym.type() == Symbol::Slack || sym.type() == Symbol::Error ) + return sym; + } + return Symbol(); + } + + /* Compute the row which holds the exit symbol for a pivot. + + This method will return an iterator to the row in the row map + which holds the exit symbol. If no appropriate exit symbol is + found, the end() iterator will be returned. This indicates that + the objective function is unbounded. + + */ + RowMap::iterator getLeavingRow( const Symbol& entering ) + { + typedef RowMap::iterator iter_t; + double ratio = std::numeric_limits<double>::max(); + iter_t end = m_rows.end(); + iter_t found = m_rows.end(); + for( iter_t it = m_rows.begin(); it != end; ++it ) + { + if( it->first.type() != Symbol::External ) + { + double temp = it->second->coefficientFor( entering ); + if( temp < 0.0 ) + { + double temp_ratio = -it->second->constant() / temp; + if( temp_ratio < ratio ) + { + ratio = temp_ratio; + found = it; + } + } + } + } + return found; + } + + /* Compute the leaving row for a marker variable. + + This method will return an iterator to the row in the row map + which holds the given marker variable. The row will be chosen + according to the following precedence: + + 1) The row with a restricted basic varible and a negative coefficient + for the marker with the smallest ratio of -constant / coefficient. + + 2) The row with a restricted basic variable and the smallest ratio + of constant / coefficient. + + 3) The last unrestricted row which contains the marker. + + If the marker does not exist in any row, the row map end() iterator + will be returned. This indicates an internal solver error since + the marker *should* exist somewhere in the tableau. + + */ + RowMap::iterator getMarkerLeavingRow( const Symbol& marker ) + { + const double dmax = std::numeric_limits<double>::max(); + typedef RowMap::iterator iter_t; + double r1 = dmax; + double r2 = dmax; + iter_t end = m_rows.end(); + iter_t first = end; + iter_t second = end; + iter_t third = end; + for( iter_t it = m_rows.begin(); it != end; ++it ) + { + double c = it->second->coefficientFor( marker ); + if( c == 0.0 ) + continue; + if( it->first.type() == Symbol::External ) + { + third = it; + } + else if( c < 0.0 ) + { + double r = -it->second->constant() / c; + if( r < r1 ) + { + r1 = r; + first = it; + } + } + else + { + double r = it->second->constant() / c; + if( r < r2 ) + { + r2 = r; + second = it; + } + } + } + if( first != end ) + return first; + if( second != end ) + return second; + return third; + } + + /* Remove the effects of a constraint on the objective function. + + */ + void removeConstraintEffects( const Constraint& cn, const Tag& tag ) + { + if( tag.marker.type() == Symbol::Error ) + removeMarkerEffects( tag.marker, cn.strength() ); + if( tag.other.type() == Symbol::Error ) + removeMarkerEffects( tag.other, cn.strength() ); + } + + /* Remove the effects of an error marker on the objective function. + + */ + void removeMarkerEffects( const Symbol& marker, double strength ) + { + RowMap::iterator row_it = m_rows.find( marker ); + if( row_it != m_rows.end() ) + m_objective->insert( *row_it->second, -strength ); + else + m_objective->insert( marker, -strength ); + } + + /* Test whether a row is composed of all dummy variables. + + */ + bool allDummies( const Row& row ) + { + typedef Row::CellMap::const_iterator iter_t; + iter_t end = row.cells().end(); + for( iter_t it = row.cells().begin(); it != end; ++it ) + { + if( it->first.type() != Symbol::Dummy ) + return false; + } + return true; + } + + CnMap m_cns; + RowMap m_rows; + VarMap m_vars; + EditMap m_edits; + std::vector<Symbol> m_infeasible_rows; + std::auto_ptr<Row> m_objective; + std::auto_ptr<Row> m_artificial; + Symbol::Id m_id_tick; +}; + +} // namespace impl + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/strength.h b/contrib/python/kiwisolver/py3/kiwi/strength.h new file mode 100644 index 00000000000..b077f3e17c1 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/strength.h @@ -0,0 +1,44 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <algorithm> + + +namespace kiwi +{ + +namespace strength +{ + +inline double create( double a, double b, double c, double w = 1.0 ) +{ + double result = 0.0; + result += std::max( 0.0, std::min( 1000.0, a * w ) ) * 1000000.0; + result += std::max( 0.0, std::min( 1000.0, b * w ) ) * 1000.0; + result += std::max( 0.0, std::min( 1000.0, c * w ) ); + return result; +} + + +const double required = create( 1000.0, 1000.0, 1000.0 ); + +const double strong = create( 1.0, 0.0, 0.0 ); + +const double medium = create( 0.0, 1.0, 0.0 ); + +const double weak = create( 0.0, 0.0, 1.0 ); + + +inline double clip( double value ) +{ + return std::max( 0.0, std::min( required, value ) ); +} + +} // namespace strength + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/symbol.h b/contrib/python/kiwisolver/py3/kiwi/symbol.h new file mode 100644 index 00000000000..b971efbdd25 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/symbol.h @@ -0,0 +1,68 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once + + +namespace kiwi +{ + +namespace impl +{ + +class Symbol +{ + +public: + + typedef unsigned long long Id; + + enum Type + { + Invalid, + External, + Slack, + Error, + Dummy + }; + + Symbol() : m_id( 0 ), m_type( Invalid ) {} + + Symbol( Type type, Id id ) : m_id( id ), m_type( type ) {} + + ~Symbol() {} + + Id id() const + { + return m_id; + } + + Type type() const + { + return m_type; + } + +private: + + Id m_id; + Type m_type; + + friend bool operator<( const Symbol& lhs, const Symbol& rhs ) + { + return lhs.m_id < rhs.m_id; + } + + friend bool operator==( const Symbol& lhs, const Symbol& rhs ) + { + return lhs.m_id == rhs.m_id; + } + +}; + +} // namespace impl + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/symbolics.h b/contrib/python/kiwisolver/py3/kiwi/symbolics.h new file mode 100644 index 00000000000..51ac6a8a37b --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/symbolics.h @@ -0,0 +1,685 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <vector> +#include "constraint.h" +#include "expression.h" +#include "term.h" +#include "variable.h" + + +namespace kiwi +{ + +// Variable multiply, divide, and unary invert + +inline +Term operator*( const Variable& variable, double coefficient ) +{ + return Term( variable, coefficient ); +} + + +inline +Term operator/( const Variable& variable, double denominator ) +{ + return variable * ( 1.0 / denominator ); +} + + +inline +Term operator-( const Variable& variable ) +{ + return variable * -1.0; +} + + +// Term multiply, divide, and unary invert + +inline +Term operator*( const Term& term, double coefficient ) +{ + return Term( term.variable(), term.coefficient() * coefficient ); +} + + +inline +Term operator/( const Term& term, double denominator ) +{ + return term * ( 1.0 / denominator ); +} + + +inline +Term operator-( const Term& term ) +{ + return term * -1.0; +} + + +// Expression multiply, divide, and unary invert + +inline +Expression operator*( const Expression& expression, double coefficient ) +{ + std::vector<Term> terms; + terms.reserve( expression.terms().size() ); + typedef std::vector<Term>::const_iterator iter_t; + iter_t begin = expression.terms().begin(); + iter_t end = expression.terms().end(); + for( iter_t it = begin; it != end; ++it ) + terms.push_back( ( *it ) * coefficient ); + return Expression( terms, expression.constant() * coefficient ); +} + + +inline +Expression operator/( const Expression& expression, double denominator ) +{ + return expression * ( 1.0 / denominator ); +} + + +inline +Expression operator-( const Expression& expression ) +{ + return expression * -1.0; +} + + +// Double multiply + +inline +Expression operator*( double coefficient, const Expression& expression ) +{ + return expression * coefficient; +} + + +inline +Term operator*( double coefficient, const Term& term ) +{ + return term * coefficient; +} + + +inline +Term operator*( double coefficient, const Variable& variable ) +{ + return variable * coefficient; +} + + +// Expression add and subtract + +inline +Expression operator+( const Expression& first, const Expression& second ) +{ + std::vector<Term> terms; + terms.reserve( first.terms().size() + second.terms().size() ); + terms.insert( terms.begin(), first.terms().begin(), first.terms().end() ); + terms.insert( terms.end(), second.terms().begin(), second.terms().end() ); + return Expression( terms, first.constant() + second.constant() ); +} + + +inline +Expression operator+( const Expression& first, const Term& second ) +{ + std::vector<Term> terms; + terms.reserve( first.terms().size() + 1 ); + terms.insert( terms.begin(), first.terms().begin(), first.terms().end() ); + terms.push_back( second ); + return Expression( terms, first.constant() ); +} + + +inline +Expression operator+( const Expression& expression, const Variable& variable ) +{ + return expression + Term( variable ); +} + + +inline +Expression operator+( const Expression& expression, double constant ) +{ + return Expression( expression.terms(), expression.constant() + constant ); +} + + +inline +Expression operator-( const Expression& first, const Expression& second ) +{ + return first + -second; +} + + +inline +Expression operator-( const Expression& expression, const Term& term ) +{ + return expression + -term; +} + + +inline +Expression operator-( const Expression& expression, const Variable& variable ) +{ + return expression + -variable; +} + + +inline +Expression operator-( const Expression& expression, double constant ) +{ + return expression + -constant; +} + + +// Term add and subtract + +inline +Expression operator+( const Term& term, const Expression& expression ) +{ + return expression + term; +} + + +inline +Expression operator+( const Term& first, const Term& second ) +{ + std::vector<Term> terms; + terms.reserve( 2 ); + terms.push_back( first ); + terms.push_back( second ); + return Expression( terms ); +} + + +inline +Expression operator+( const Term& term, const Variable& variable ) +{ + return term + Term( variable ); +} + + +inline +Expression operator+( const Term& term, double constant ) +{ + return Expression( term, constant ); +} + + +inline +Expression operator-( const Term& term, const Expression& expression ) +{ + return -expression + term; +} + + +inline +Expression operator-( const Term& first, const Term& second ) +{ + return first + -second; +} + + +inline +Expression operator-( const Term& term, const Variable& variable ) +{ + return term + -variable; +} + + +inline +Expression operator-( const Term& term, double constant ) +{ + return term + -constant; +} + + +// Variable add and subtract + +inline +Expression operator+( const Variable& variable, const Expression& expression ) +{ + return expression + variable; +} + + +inline +Expression operator+( const Variable& variable, const Term& term ) +{ + return term + variable; +} + + +inline +Expression operator+( const Variable& first, const Variable& second ) +{ + return Term( first ) + second; +} + + +inline +Expression operator+( const Variable& variable, double constant ) +{ + return Term( variable ) + constant; +} + + +inline +Expression operator-( const Variable& variable, const Expression& expression ) +{ + return variable + -expression; +} + + +inline +Expression operator-( const Variable& variable, const Term& term ) +{ + return variable + -term; +} + + +inline +Expression operator-( const Variable& first, const Variable& second ) +{ + return first + -second; +} + + +inline +Expression operator-( const Variable& variable, double constant ) +{ + return variable + -constant; +} + + +// Double add and subtract + +inline +Expression operator+( double constant, const Expression& expression ) +{ + return expression + constant; +} + + +inline +Expression operator+( double constant, const Term& term ) +{ + return term + constant; +} + + +inline +Expression operator+( double constant, const Variable& variable ) +{ + return variable + constant; +} + + +inline +Expression operator-( double constant, const Expression& expression ) +{ + return -expression + constant; +} + + +inline +Expression operator-( double constant, const Term& term ) +{ + return -term + constant; +} + + +inline +Expression operator-( double constant, const Variable& variable ) +{ + return -variable + constant; +} + + +// Expression relations + +inline +Constraint operator==( const Expression& first, const Expression& second ) +{ + return Constraint( first - second, OP_EQ ); +} + + +inline +Constraint operator==( const Expression& expression, const Term& term ) +{ + return expression == Expression( term ); +} + + +inline +Constraint operator==( const Expression& expression, const Variable& variable ) +{ + return expression == Term( variable ); +} + + +inline +Constraint operator==( const Expression& expression, double constant ) +{ + return expression == Expression( constant ); +} + + +inline +Constraint operator<=( const Expression& first, const Expression& second ) +{ + return Constraint( first - second, OP_LE ); +} + + +inline +Constraint operator<=( const Expression& expression, const Term& term ) +{ + return expression <= Expression( term ); +} + + +inline +Constraint operator<=( const Expression& expression, const Variable& variable ) +{ + return expression <= Term( variable ); +} + + +inline +Constraint operator<=( const Expression& expression, double constant ) +{ + return expression <= Expression( constant ); +} + + +inline +Constraint operator>=( const Expression& first, const Expression& second ) +{ + return Constraint( first - second, OP_GE ); +} + + +inline +Constraint operator>=( const Expression& expression, const Term& term ) +{ + return expression >= Expression( term ); +} + + +inline +Constraint operator>=( const Expression& expression, const Variable& variable ) +{ + return expression >= Term( variable ); +} + + +inline +Constraint operator>=( const Expression& expression, double constant ) +{ + return expression >= Expression( constant ); +} + + +// Term relations + +inline +Constraint operator==( const Term& term, const Expression& expression ) +{ + return expression == term; +} + + +inline +Constraint operator==( const Term& first, const Term& second ) +{ + return Expression( first ) == second; +} + + +inline +Constraint operator==( const Term& term, const Variable& variable ) +{ + return Expression( term ) == variable; +} + + +inline +Constraint operator==( const Term& term, double constant ) +{ + return Expression( term ) == constant; +} + + +inline +Constraint operator<=( const Term& term, const Expression& expression ) +{ + return expression >= term; +} + + +inline +Constraint operator<=( const Term& first, const Term& second ) +{ + return Expression( first ) <= second; +} + + +inline +Constraint operator<=( const Term& term, const Variable& variable ) +{ + return Expression( term ) <= variable; +} + + +inline +Constraint operator<=( const Term& term, double constant ) +{ + return Expression( term ) <= constant; +} + + +inline +Constraint operator>=( const Term& term, const Expression& expression ) +{ + return expression <= term; +} + + +inline +Constraint operator>=( const Term& first, const Term& second ) +{ + return Expression( first ) >= second; +} + + +inline +Constraint operator>=( const Term& term, const Variable& variable ) +{ + return Expression( term ) >= variable; +} + + +inline +Constraint operator>=( const Term& term, double constant ) +{ + return Expression( term ) >= constant; +} + + +// Variable relations +inline +Constraint operator==( const Variable& variable, const Expression& expression ) +{ + return expression == variable; +} + + +inline +Constraint operator==( const Variable& variable, const Term& term ) +{ + return term == variable; +} + + +inline +Constraint operator==( const Variable& first, const Variable& second ) +{ + return Term( first ) == second; +} + + +inline +Constraint operator==( const Variable& variable, double constant ) +{ + return Term( variable ) == constant; +} + + +inline +Constraint operator<=( const Variable& variable, const Expression& expression ) +{ + return expression >= variable; +} + + +inline +Constraint operator<=( const Variable& variable, const Term& term ) +{ + return term >= variable; +} + + +inline +Constraint operator<=( const Variable& first, const Variable& second ) +{ + return Term( first ) <= second; +} + + +inline +Constraint operator<=( const Variable& variable, double constant ) +{ + return Term( variable ) <= constant; +} + + +inline +Constraint operator>=( const Variable& variable, const Expression& expression ) +{ + return expression <= variable; +} + + +inline +Constraint operator>=( const Variable& variable, const Term& term ) +{ + return term <= variable; +} + + +inline +Constraint operator>=( const Variable& first, const Variable& second ) +{ + return Term( first ) >= second; +} + + +inline +Constraint operator>=( const Variable& variable, double constant ) +{ + return Term( variable ) >= constant; +} + + +// Double relations + +inline +Constraint operator==( double constant, const Expression& expression ) +{ + return expression == constant; +} + + +inline +Constraint operator==( double constant, const Term& term ) +{ + return term == constant; +} + + +inline +Constraint operator==( double constant, const Variable& variable ) +{ + return variable == constant; +} + + +inline +Constraint operator<=( double constant, const Expression& expression ) +{ + return expression >= constant; +} + + +inline +Constraint operator<=( double constant, const Term& term ) +{ + return term >= constant; +} + + +inline +Constraint operator<=( double constant, const Variable& variable ) +{ + return variable >= constant; +} + + +inline +Constraint operator>=( double constant, const Expression& expression ) +{ + return expression <= constant; +} + + +inline +Constraint operator>=( double constant, const Term& term ) +{ + return term <= constant; +} + + +inline +Constraint operator>=( double constant, const Variable& variable ) +{ + return variable <= constant; +} + + +// Constraint strength modifier + +inline +Constraint operator|( const Constraint& constraint, double strength ) +{ + return Constraint( constraint, strength ); +} + + +inline +Constraint operator|( double strength, const Constraint& constraint ) +{ + return constraint | strength; +} + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/term.h b/contrib/python/kiwisolver/py3/kiwi/term.h new file mode 100644 index 00000000000..1ee962287cc --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/term.h @@ -0,0 +1,51 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <utility> +#include "variable.h" + + +namespace kiwi +{ + +class Term +{ + +public: + + Term( const Variable& variable, double coefficient = 1.0 ) : + m_variable( variable ), m_coefficient( coefficient ) {} + + // to facilitate efficient map -> vector copies + Term( const std::pair<const Variable, double>& pair ) : + m_variable( pair.first ), m_coefficient( pair.second ) {} + + ~Term() {} + + const Variable& variable() const + { + return m_variable; + } + + double coefficient() const + { + return m_coefficient; + } + + double value() const + { + return m_coefficient * m_variable.value(); + } + +private: + + Variable m_variable; + double m_coefficient; +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/util.h b/contrib/python/kiwisolver/py3/kiwi/util.h new file mode 100644 index 00000000000..0d36a33eb15 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/util.h @@ -0,0 +1,25 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once + + +namespace kiwi +{ + +namespace impl +{ + +inline bool nearZero( double value ) +{ + const double eps = 1.0e-8; + return value < 0.0 ? -value < eps : value < eps; +} + +} // namespace impl + +} // namespace diff --git a/contrib/python/kiwisolver/py3/kiwi/variable.h b/contrib/python/kiwisolver/py3/kiwi/variable.h new file mode 100644 index 00000000000..eaee93f2a13 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/variable.h @@ -0,0 +1,121 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <memory> +#include <string> +#include "shareddata.h" + + +namespace kiwi +{ + +class Variable +{ + +public: + + class Context + { + public: + Context() {} + virtual ~Context() {} // LCOV_EXCL_LINE + }; + + Variable( Context* context = 0 ) : + m_data( new VariableData( "", context ) ) {} + + Variable( const std::string& name, Context* context = 0 ) : + m_data( new VariableData( name, context ) ) {} + + Variable( const char* name, Context* context = 0 ) : + m_data( new VariableData( name, context ) ) {} + + ~Variable() {} + + const std::string& name() const + { + return m_data->m_name; + } + + void setName( const char* name ) + { + m_data->m_name = name; + } + + void setName( const std::string& name ) + { + m_data->m_name = name; + } + + Context* context() const + { + return m_data->m_context.get(); + } + + void setContext( Context* context ) + { + m_data->m_context.reset( context ); + } + + double value() const + { + return m_data->m_value; + } + + void setValue( double value ) + { + m_data->m_value = value; + } + + // operator== is used for symbolics + bool equals( const Variable& other ) + { + return m_data == other.m_data; + } + +private: + + class VariableData : public SharedData + { + + public: + + VariableData( const std::string& name, Context* context ) : + SharedData(), + m_name( name ), + m_context( context ), + m_value( 0.0 ) {} + + VariableData( const char* name, Context* context ) : + SharedData(), + m_name( name ), + m_context( context ), + m_value( 0.0 ) {} + + ~VariableData() {} + + std::string m_name; + std::auto_ptr<Context> m_context; + double m_value; + + private: + + VariableData( const VariableData& other ); + + VariableData& operator=( const VariableData& other ); + }; + + SharedDataPtr<VariableData> m_data; + + friend bool operator<( const Variable& lhs, const Variable& rhs ) + { + return lhs.m_data < rhs.m_data; + } +}; + +} // namespace kiwi diff --git a/contrib/python/kiwisolver/py3/kiwi/version.h b/contrib/python/kiwisolver/py3/kiwi/version.h new file mode 100644 index 00000000000..197f3ce1869 --- /dev/null +++ b/contrib/python/kiwisolver/py3/kiwi/version.h @@ -0,0 +1,14 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once + +#define KIWI_MAJOR_VERSION 1 +#define KIWI_MINOR_VERSION 0 +#define KIWI_MICRO_VERSION 1 +#define KIWI_VERSION_HEX 0x010001 +#define KIWI_VERSION "1.1.0" diff --git a/contrib/python/kiwisolver/py3/py/constraint.cpp b/contrib/python/kiwisolver/py3/py/constraint.cpp new file mode 100644 index 00000000000..2fbdd0aeb83 --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/constraint.cpp @@ -0,0 +1,299 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <algorithm> +#include <sstream> +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + static const char *kwlist[] = { "expression", "op", "strength", 0 }; + PyObject* pyexpr; + PyObject* pyop; + PyObject* pystrength = 0; + if( !PyArg_ParseTupleAndKeywords( + args, kwargs, "OO|O:__new__", const_cast<char**>( kwlist ), + &pyexpr, &pyop, &pystrength ) ) + return 0; + if( !Expression::TypeCheck( pyexpr ) ) + return py_expected_type_fail( pyexpr, "Expression" ); + kiwi::RelationalOperator op; + if( !convert_to_relational_op( pyop, op ) ) + return 0; + double strength = kiwi::strength::required; + if( pystrength && !convert_to_strength( pystrength, strength ) ) + return 0; + PyObjectPtr pycn( PyType_GenericNew( type, args, kwargs ) ); + if( !pycn ) + return 0; + Constraint* cn = reinterpret_cast<Constraint*>( pycn.get() ); + cn->expression = reduce_expression( pyexpr ); + if( !cn->expression ) + return 0; + kiwi::Expression expr( convert_to_kiwi_expression( cn->expression ) ); + new( &cn->constraint ) kiwi::Constraint( expr, op, strength ); + return pycn.release(); +} + + +static void +Constraint_clear( Constraint* self ) +{ + Py_CLEAR( self->expression ); +} + + +static int +Constraint_traverse( Constraint* self, visitproc visit, void* arg ) +{ + Py_VISIT( self->expression ); + return 0; +} + + +static void +Constraint_dealloc( Constraint* self ) +{ + PyObject_GC_UnTrack( self ); + Constraint_clear( self ); + self->constraint.~Constraint(); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Constraint_repr( Constraint* self ) +{ + std::stringstream stream; + Expression* expr = reinterpret_cast<Expression*>( self->expression ); + Py_ssize_t size = PyTuple_GET_SIZE( expr->terms ); + for( Py_ssize_t i = 0; i < size; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( expr->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + stream << term->coefficient << " * "; + stream << reinterpret_cast<Variable*>( term->variable )->variable.name(); + stream << " + "; + } + stream << expr->constant; + switch( self->constraint.op() ) + { + case kiwi::OP_EQ: + stream << " == 0"; + break; + case kiwi::OP_LE: + stream << " <= 0"; + break; + case kiwi::OP_GE: + stream << " >= 0"; + break; + } + stream << " | strength = " << self->constraint.strength(); + return FROM_STRING( stream.str().c_str() ); +} + + +static PyObject* +Constraint_expression( Constraint* self ) +{ + return newref( self->expression ); +} + + +static PyObject* +Constraint_op( Constraint* self ) +{ + PyObject* res = 0; + switch( self->constraint.op() ) + { + case kiwi::OP_EQ: + res = FROM_STRING( "==" ); + break; + case kiwi::OP_LE: + res = FROM_STRING( "<=" ); + break; + case kiwi::OP_GE: + res = FROM_STRING( ">=" ); + break; + } + return res; +} + + +static PyObject* +Constraint_strength( Constraint* self ) +{ + return PyFloat_FromDouble( self->constraint.strength() ); +} + + +static PyObject* +Constraint_or( PyObject* pyoldcn, PyObject* value ) +{ + if( !Constraint::TypeCheck( pyoldcn ) ) + std::swap( pyoldcn, value ); + double strength; + if( !convert_to_strength( value, strength ) ) + return 0; + PyObject* pynewcn = PyType_GenericNew( &Constraint_Type, 0, 0 ); + if( !pynewcn ) + return 0; + Constraint* oldcn = reinterpret_cast<Constraint*>( pyoldcn ); + Constraint* newcn = reinterpret_cast<Constraint*>( pynewcn ); + newcn->expression = newref( oldcn->expression ); + new( &newcn->constraint ) kiwi::Constraint( oldcn->constraint, strength ); + return pynewcn; +} + + +static PyMethodDef +Constraint_methods[] = { + { "expression", ( PyCFunction )Constraint_expression, METH_NOARGS, + "Get the expression object for the constraint." }, + { "op", ( PyCFunction )Constraint_op, METH_NOARGS, + "Get the relational operator for the constraint." }, + { "strength", ( PyCFunction )Constraint_strength, METH_NOARGS, + "Get the strength for the constraint." }, + { 0 } // sentinel +}; + + +static PyNumberMethods +Constraint_as_number = { + 0, /* nb_add */ + 0, /* nb_subtract */ + 0, /* nb_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_divide */ +#endif + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ +#if PY_MAJOR_VERSION >= 3 + 0, /* nb_bool */ +#else + 0, /* nb_nonzero */ +#endif + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + (binaryfunc)Constraint_or, /* nb_or */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_coerce */ +#endif + 0, /* nb_int */ + 0, /* nb_long */ + 0, /* nb_float */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_oct */ + 0, /* nb_hex */ +#endif + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_inplace_divide */ +#endif + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)0, /* nb_floor_divide */ + (binaryfunc)0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +#if PY_VERSION_HEX >= 0x02050000 + (unaryfunc)0, /* nb_index */ +#endif +#if PY_VERSION_HEX >= 0x03050000 + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ +#endif +}; + + +PyTypeObject Constraint_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Constraint", /* tp_name */ + sizeof( Constraint ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Constraint_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)Constraint_repr, /* tp_repr */ + (PyNumberMethods*)&Constraint_as_number,/* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + 0, /* Documentation string */ + (traverseproc)Constraint_traverse, /* tp_traverse */ + (inquiry)Constraint_clear, /* tp_clear */ + (richcmpfunc)0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Constraint_methods,/* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Constraint_new, /* tp_new */ + (freefunc)PyObject_GC_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_constraint() +{ + return PyType_Ready( &Constraint_Type ); +} diff --git a/contrib/python/kiwisolver/py3/py/expression.cpp b/contrib/python/kiwisolver/py3/py/expression.cpp new file mode 100644 index 00000000000..5cd34e4c120 --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/expression.cpp @@ -0,0 +1,326 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <sstream> +#include <Python.h> +#include "pythonhelpers.h" +#include "symbolics.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + static const char *kwlist[] = { "terms", "constant", 0 }; + PyObject* pyterms; + PyObject* pyconstant = 0; + if( !PyArg_ParseTupleAndKeywords( + args, kwargs, "O|O:__new__", const_cast<char**>( kwlist ), + &pyterms, &pyconstant ) ) + return 0; + PyObjectPtr terms( PySequence_Tuple( pyterms ) ); + if( !terms ) + return 0; + Py_ssize_t end = PyTuple_GET_SIZE( terms.get() ); + for( Py_ssize_t i = 0; i < end; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( terms.get(), i ); + if( !Term::TypeCheck( item ) ) + return py_expected_type_fail( item, "Term" ); + } + double constant = 0.0; + if( pyconstant && !convert_to_double( pyconstant, constant ) ) + return 0; + PyObject* pyexpr = PyType_GenericNew( type, args, kwargs ); + if( !pyexpr ) + return 0; + Expression* self = reinterpret_cast<Expression*>( pyexpr ); + self->terms = terms.release(); + self->constant = constant; + return pyexpr; +} + + +static void +Expression_clear( Expression* self ) +{ + Py_CLEAR( self->terms ); +} + + +static int +Expression_traverse( Expression* self, visitproc visit, void* arg ) +{ + Py_VISIT( self->terms ); + return 0; +} + + +static void +Expression_dealloc( Expression* self ) +{ + PyObject_GC_UnTrack( self ); + Expression_clear( self ); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Expression_repr( Expression* self ) +{ + std::stringstream stream; + Py_ssize_t end = PyTuple_GET_SIZE( self->terms ); + for( Py_ssize_t i = 0; i < end; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( self->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + stream << term->coefficient << " * "; + stream << reinterpret_cast<Variable*>( term->variable )->variable.name(); + stream << " + "; + } + stream << self->constant; + return FROM_STRING( stream.str().c_str() ); +} + + +static PyObject* +Expression_terms( Expression* self ) +{ + return newref( self->terms ); +} + + +static PyObject* +Expression_constant( Expression* self ) +{ + return PyFloat_FromDouble( self->constant ); +} + + +static PyObject* +Expression_value( Expression* self ) +{ + double result = self->constant; + Py_ssize_t size = PyTuple_GET_SIZE( self->terms ); + for( Py_ssize_t i = 0; i < size; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( self->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + Variable* pyvar = reinterpret_cast<Variable*>( term->variable ); + result += term->coefficient * pyvar->variable.value(); + } + return PyFloat_FromDouble( result ); +} + + +static PyObject* +Expression_add( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryAdd, Expression>()( first, second ); +} + + +static PyObject* +Expression_sub( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinarySub, Expression>()( first, second ); +} + + +static PyObject* +Expression_mul( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryMul, Expression>()( first, second ); +} + + +static PyObject* +Expression_div( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryDiv, Expression>()( first, second ); +} + + +static PyObject* +Expression_neg( PyObject* value ) +{ + return UnaryInvoke<UnaryNeg, Expression>()( value ); +} + + +static PyObject* +Expression_richcmp( PyObject* first, PyObject* second, int op ) +{ + switch( op ) + { + case Py_EQ: + return BinaryInvoke<CmpEQ, Expression>()( first, second ); + case Py_LE: + return BinaryInvoke<CmpLE, Expression>()( first, second ); + case Py_GE: + return BinaryInvoke<CmpGE, Expression>()( first, second ); + default: + break; + } + PyErr_Format( + PyExc_TypeError, + "unsupported operand type(s) for %s: " + "'%.100s' and '%.100s'", + pyop_str( op ), + first->ob_type->tp_name, + second->ob_type->tp_name + ); + return 0; +} + + +static PyMethodDef +Expression_methods[] = { + { "terms", ( PyCFunction )Expression_terms, METH_NOARGS, + "Get the tuple of terms for the expression." }, + { "constant", ( PyCFunction )Expression_constant, METH_NOARGS, + "Get the constant for the expression." }, + { "value", ( PyCFunction )Expression_value, METH_NOARGS, + "Get the value for the expression." }, + { 0 } // sentinel +}; + + +static PyNumberMethods +Expression_as_number = { + (binaryfunc)Expression_add, /* nb_add */ + (binaryfunc)Expression_sub, /* nb_subtract */ + (binaryfunc)Expression_mul, /* nb_multiply */ +#if PY_MAJOR_VERSION < 3 + (binaryfunc)Expression_div, /* nb_divide */ +#endif + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + (unaryfunc)Expression_neg, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ +#if PY_MAJOR_VERSION >= 3 + 0, /* nb_bool */ +#else + 0, /* nb_nonzero */ +#endif + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + (binaryfunc)0, /* nb_or */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_coerce */ +#endif + 0, /* nb_int */ +#if PY_MAJOR_VERSION >= 3 + (void *)0, /* nb_reserved */ +#else + 0, /* nb_long */ +#endif + 0, /* nb_float */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_oct */ + 0, /* nb_hex */ +#endif + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_inplace_divide */ +#endif + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)0, /* nb_floor_divide */ + (binaryfunc)Expression_div, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +#if PY_VERSION_HEX >= 0x02050000 + (unaryfunc)0, /* nb_index */ +#endif +#if PY_VERSION_HEX >= 0x03050000 + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ +#endif +}; + + +PyTypeObject Expression_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Expression", /* tp_name */ + sizeof( Expression ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Expression_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)Expression_repr, /* tp_repr */ + (PyNumberMethods*)&Expression_as_number,/* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, +#else + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + 0, /* Documentation string */ + (traverseproc)Expression_traverse, /* tp_traverse */ + (inquiry)Expression_clear, /* tp_clear */ + (richcmpfunc)Expression_richcmp, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Expression_methods,/* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Expression_new, /* tp_new */ + (freefunc)PyObject_GC_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_expression() +{ + return PyType_Ready( &Expression_Type ); +} diff --git a/contrib/python/kiwisolver/py3/py/kiwisolver.cpp b/contrib/python/kiwisolver/py3/py/kiwisolver.cpp new file mode 100644 index 00000000000..54b333a2c8b --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/kiwisolver.cpp @@ -0,0 +1,86 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "types.h" + +#define PY_KIWI_VERSION "1.1.0" + +using namespace PythonHelpers; + +static PyMethodDef +kiwisolver_methods[] = { + { 0 } // Sentinel +}; + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef kiwisolver_moduledef = { + PyModuleDef_HEAD_INIT, + "kiwisolver", + NULL, + sizeof( struct module_state ), + kiwisolver_methods, + NULL +}; + +PyMODINIT_FUNC +PyInit_kiwisolver( void ) +#else +PyMODINIT_FUNC +initkiwisolver( void ) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *mod = PyModule_Create( &kiwisolver_moduledef ); +#else + PyObject* mod = Py_InitModule( "kiwisolver", kiwisolver_methods ); +#endif + if( !mod ) + INITERROR; + if( import_variable() < 0 ) + INITERROR; + if( import_term() < 0 ) + INITERROR; + if( import_expression() < 0 ) + INITERROR; + if( import_constraint() < 0 ) + INITERROR; + if( import_solver() < 0 ) + INITERROR; + if( import_strength() < 0 ) + INITERROR; + PyObject* kiwiversion = FROM_STRING( KIWI_VERSION ); + if( !kiwiversion ) + INITERROR; + PyObject* pyversion = FROM_STRING( PY_KIWI_VERSION ); + if( !pyversion ) + INITERROR; + PyObject* pystrength = PyType_GenericNew( &strength_Type, 0, 0 ); + if( !pystrength ) + INITERROR; + + PyModule_AddObject( mod, "__version__", pyversion ); + PyModule_AddObject( mod, "__kiwi_version__", kiwiversion ); + PyModule_AddObject( mod, "strength", pystrength ); + PyModule_AddObject( mod, "Variable", newref( pyobject_cast( &Variable_Type ) ) ); + PyModule_AddObject( mod, "Term", newref( pyobject_cast( &Term_Type ) ) ); + PyModule_AddObject( mod, "Expression", newref( pyobject_cast( &Expression_Type ) ) ); + PyModule_AddObject( mod, "Constraint", newref( pyobject_cast( &Constraint_Type ) ) ); + PyModule_AddObject( mod, "Solver", newref( pyobject_cast( &Solver_Type ) ) ); + PyModule_AddObject( mod, "DuplicateConstraint", newref( DuplicateConstraint ) ); + PyModule_AddObject( mod, "UnsatisfiableConstraint", newref( UnsatisfiableConstraint ) ); + PyModule_AddObject( mod, "UnknownConstraint", newref( UnknownConstraint ) ); + PyModule_AddObject( mod, "DuplicateEditVariable", newref( DuplicateEditVariable ) ); + PyModule_AddObject( mod, "UnknownEditVariable", newref( UnknownEditVariable ) ); + PyModule_AddObject( mod, "BadRequiredStrength", newref( BadRequiredStrength ) ); + +#if PY_MAJOR_VERSION >= 3 + return mod; +#endif +} diff --git a/contrib/python/kiwisolver/py3/py/pythonhelpers.h b/contrib/python/kiwisolver/py3/py/pythonhelpers.h new file mode 100644 index 00000000000..a9e0db634d7 --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/pythonhelpers.h @@ -0,0 +1,771 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <Python.h> +#include <structmember.h> +#include <string> + +#if PY_MAJOR_VERSION >= 3 +#define FROM_STRING PyUnicode_FromString +#define INITERROR return NULL +#define MOD_INIT_FUNC(name) PyMODINIT_FUNC PyInit_##name(void) +#else +#define FROM_STRING PyString_FromString +#define INITERROR return +#define MOD_INIT_FUNC(name) PyMODINIT_FUNC init##name(void) +#endif + +#ifndef Py_RETURN_NOTIMPLEMENTED +#define Py_RETURN_NOTIMPLEMENTED \ + return Py_INCREF(Py_NotImplemented), Py_NotImplemented +#endif + +#define pyobject_cast( o ) ( reinterpret_cast<PyObject*>( o ) ) +#define pytype_cast( o ) ( reinterpret_cast<PyTypeObject*>( o ) ) + +struct module_state { + PyObject *error; +}; + + +namespace PythonHelpers +{ + + +/*----------------------------------------------------------------------------- +| Exception Handling +|----------------------------------------------------------------------------*/ +inline PyObject* +py_bad_internal_call( const char* message ) +{ + PyErr_SetString( PyExc_SystemError, message ); + return 0; +} + + +inline PyObject* +py_type_fail( const char* message ) +{ + PyErr_SetString( PyExc_TypeError, message ); + return 0; +} + + +inline PyObject* +py_expected_type_fail( PyObject* pyobj, const char* expected_type ) +{ + PyErr_Format( + PyExc_TypeError, + "Expected object of type `%s`. Got object of type `%s` instead.", + expected_type, pyobj->ob_type->tp_name + ); + return 0; +} + + +inline PyObject* +py_value_fail( const char* message ) +{ + PyErr_SetString( PyExc_ValueError, message ); + return 0; +} + + +inline PyObject* +py_runtime_fail( const char* message ) +{ + PyErr_SetString( PyExc_RuntimeError, message ); + return 0; +} + + +inline PyObject* +py_attr_fail( const char* message ) +{ + PyErr_SetString( PyExc_AttributeError, message ); + return 0; +} + + +inline PyObject* +py_no_attr_fail( PyObject* pyobj, const char* attr ) +{ + PyErr_Format( + PyExc_AttributeError, + "'%s' object has no attribute '%s'", + pyobj->ob_type->tp_name, attr + ); + return 0; +} + + +/*----------------------------------------------------------------------------- +| Utilities +|----------------------------------------------------------------------------*/ +inline PyObject* +newref( PyObject* pyobj ) +{ + Py_INCREF( pyobj ); + return pyobj; +} + + +inline PyObject* +xnewref( PyObject* pyobj ) +{ + Py_XINCREF( pyobj ); + return pyobj; +} + + +inline PyObject* +py_bool( bool val ) +{ + return newref( val ? Py_True : Py_False ); +} + + +inline PyCFunction +lookup_method( PyTypeObject* type, const char* name ) +{ + PyMethodDef* method = type->tp_methods; + for( ; method->ml_name != 0; ++method ) + { + if( strcmp( method->ml_name, name ) == 0 ) + return method->ml_meth; + } + return 0; +} + + +/*----------------------------------------------------------------------------- +| Object Ptr +|----------------------------------------------------------------------------*/ +class PyObjectPtr { + +public: + + PyObjectPtr() : m_pyobj( 0 ) {} + + PyObjectPtr( const PyObjectPtr& objptr ) : + m_pyobj( PythonHelpers::xnewref( objptr.m_pyobj ) ) {} + + PyObjectPtr( PyObject* pyobj ) : m_pyobj( pyobj ) {} + + ~PyObjectPtr() + { + xdecref_release(); + } + + PyObject* get() const + { + return m_pyobj; + } + + void set( PyObject* pyobj ) + { + PyObject* old = m_pyobj; + m_pyobj = pyobj; + Py_XDECREF( old ); + } + + PyObject* release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + return pyobj; + } + + PyObject* decref_release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + Py_DECREF( pyobj ); + return pyobj; + } + + PyObject* xdecref_release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + Py_XDECREF( pyobj ); + return pyobj; + } + + PyObject* incref_release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + Py_INCREF( pyobj ); + return pyobj; + } + + PyObject* xincref_release() + { + PyObject* pyobj = m_pyobj; + m_pyobj = 0; + Py_XINCREF( pyobj ); + return pyobj; + } + + void incref() const + { + Py_INCREF( m_pyobj ); + } + + void decref() const + { + Py_DECREF( m_pyobj ); + } + + void xincref() const + { + Py_XINCREF( m_pyobj ); + } + + void xdecref() const + { + Py_XDECREF( m_pyobj ); + } + + PyObject* newref() const + { + Py_INCREF( m_pyobj ); + return m_pyobj; + } + + PyObject* xnewref() const + { + Py_XINCREF( m_pyobj ); + return m_pyobj; + } + + size_t refcount() const + { + if( m_pyobj ) + return m_pyobj->ob_refcnt; + return 0; + } + + bool is_true( bool clear_err=true ) const + { + int truth = PyObject_IsTrue( m_pyobj ); + if( truth == 1 ) + return true; + if( truth == 0 ) + return false; + if( clear_err ) + PyErr_Clear(); + return false; + } + + bool is_None() const + { + return m_pyobj == Py_None; + } + + bool is_True() const + { + return m_pyobj == Py_True; + } + + bool is_False() const + { + return m_pyobj == Py_False; + } + + bool load_dict( PyObjectPtr& out, bool forcecreate=false ) + { + PyObject** dict = _PyObject_GetDictPtr( m_pyobj ); + if( !dict ) + return false; + if( forcecreate && !*dict ) + *dict = PyDict_New(); + out = PythonHelpers::xnewref( *dict ); + return true; + } + + bool richcompare( PyObject* other, int opid, bool clear_err=true ) + { + int r = PyObject_RichCompareBool( m_pyobj, other, opid ); + if( r == 1 ) + return true; + if( r == 0 ) + return false; + if( clear_err && PyErr_Occurred() ) + PyErr_Clear(); + return false; + } + + bool richcompare( PyObjectPtr& other, int opid, bool clear_err=true ) + { + return richcompare( other.m_pyobj, opid, clear_err ); + } + + bool hasattr( PyObject* attr ) + { + return PyObject_HasAttr( m_pyobj, attr ) == 1; + } + + bool hasattr( PyObjectPtr& attr ) + { + return PyObject_HasAttr( m_pyobj, attr.get() ) == 1; + } + + bool hasattr( const char* attr ) + { + return PyObject_HasAttrString( m_pyobj, attr ) == 1; + } + + bool hasattr( std::string& attr ) + { + return hasattr( attr.c_str() ); + } + + PyObjectPtr getattr( PyObject* attr ) + { + return PyObjectPtr( PyObject_GetAttr( m_pyobj, attr ) ); + } + + PyObjectPtr getattr( PyObjectPtr& attr ) + { + return PyObjectPtr( PyObject_GetAttr( m_pyobj, attr.get() ) ); + } + + PyObjectPtr getattr( const char* attr ) + { + return PyObjectPtr( PyObject_GetAttrString( m_pyobj, attr ) ); + } + + PyObjectPtr getattr( std::string& attr ) + { + return getattr( attr.c_str() ); + } + + bool setattr( PyObject* attr, PyObject* value ) + { + return PyObject_SetAttr( m_pyobj, attr, value ) == 0; + } + + bool setattr( PyObjectPtr& attr, PyObjectPtr& value ) + { + return PyObject_SetAttr( m_pyobj, attr.get(), value.get() ) == 0; + } + + PyObjectPtr operator()( PyObjectPtr& args ) const + { + return PyObjectPtr( PyObject_Call( m_pyobj, args.get(), 0 ) ); + } + + PyObjectPtr operator()( PyObjectPtr& args, PyObjectPtr& kwargs ) const + { + return PyObjectPtr( PyObject_Call( m_pyobj, args.get(), kwargs.get() ) ); + } + + operator void*() const + { + return static_cast<void*>( m_pyobj ); + } + + PyObjectPtr& operator=( const PyObjectPtr& rhs ) + { + PyObject* old = m_pyobj; + m_pyobj = rhs.m_pyobj; + Py_XINCREF( m_pyobj ); + Py_XDECREF( old ); + return *this; + } + + PyObjectPtr& operator=( PyObject* rhs ) + { + PyObject* old = m_pyobj; + m_pyobj = rhs; + Py_XDECREF( old ); + return *this; + } + +protected: + + PyObject* m_pyobj; + +}; + + +inline bool +operator!=( const PyObjectPtr& lhs, const PyObjectPtr& rhs ) +{ + return lhs.get() != rhs.get(); +} + + +inline bool +operator!=( const PyObject* lhs, const PyObjectPtr& rhs ) +{ + return lhs != rhs.get(); +} + + +inline bool +operator!=( const PyObjectPtr& lhs, const PyObject* rhs ) +{ + return lhs.get() != rhs; +} + + +inline bool +operator==( const PyObjectPtr& lhs, const PyObjectPtr& rhs ) +{ + return lhs.get() == rhs.get(); +} + + +inline bool +operator==( const PyObject* lhs, const PyObjectPtr& rhs ) +{ + return lhs == rhs.get(); +} + + +inline bool +operator==( const PyObjectPtr& lhs, const PyObject* rhs ) +{ + return lhs.get() == rhs; +} + + +/*----------------------------------------------------------------------------- +| Tuple Ptr +|----------------------------------------------------------------------------*/ +class PyTuplePtr : public PyObjectPtr { + +public: + + PyTuplePtr() : PyObjectPtr() {} + + PyTuplePtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyTuplePtr( PyObject* pytuple ) : PyObjectPtr( pytuple ) {} + + bool check() + { + return PyTuple_Check( m_pyobj ); + } + + bool check_exact() + { + return PyTuple_CheckExact( m_pyobj ); + } + + Py_ssize_t size() const + { + return PyTuple_GET_SIZE( m_pyobj ); + } + + PyObjectPtr get_item( Py_ssize_t index ) const + { + return PyObjectPtr( PythonHelpers::newref( PyTuple_GET_ITEM( m_pyobj, index ) ) ); + } + + void set_item( Py_ssize_t index, PyObject* pyobj ) + { + PyObject* old_item = PyTuple_GET_ITEM( m_pyobj, index ); + PyTuple_SET_ITEM( m_pyobj, index, pyobj ); + Py_XDECREF( old_item ); + } + + void set_item( Py_ssize_t index, PyObjectPtr& item ) + { + PyObject* old_item = PyTuple_GET_ITEM( m_pyobj, index ); + PyTuple_SET_ITEM( m_pyobj, index, item.get() ); + Py_XINCREF( item.get() ); + Py_XDECREF( old_item ); + } + + // pyobj must not be null, only use to fill a new empty tuple + void initialize( Py_ssize_t index, PyObject* pyobj ) + { + PyTuple_SET_ITEM( m_pyobj, index, pyobj ); + } + + // ptr must not be empty, only use to fill a new empty tuple + void initialize( Py_ssize_t index, PyObjectPtr& item ) + { + PyTuple_SET_ITEM( m_pyobj, index, item.get() ); + Py_INCREF( item.get() ); + } + +}; + + +/*----------------------------------------------------------------------------- +| List Ptr +|----------------------------------------------------------------------------*/ +class PyListPtr : public PyObjectPtr { + +public: + + PyListPtr() : PyObjectPtr() {} + + PyListPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyListPtr( PyObject* pylist ) : PyObjectPtr( pylist ) {} + + bool check() const + { + return PyList_Check( m_pyobj ); + } + + bool check_exact() const + { + return PyList_CheckExact( m_pyobj ); + } + + Py_ssize_t size() const + { + return PyList_GET_SIZE( m_pyobj ); + } + + PyObject* borrow_item( Py_ssize_t index ) const + { + return PyList_GET_ITEM( m_pyobj, index ); + } + + PyObjectPtr get_item( Py_ssize_t index ) const + { + return PyObjectPtr( PythonHelpers::newref( PyList_GET_ITEM( m_pyobj, index ) ) ); + } + + void set_item( Py_ssize_t index, PyObject* pyobj ) const + { + PyObject* old_item = PyList_GET_ITEM( m_pyobj, index ); + PyList_SET_ITEM( m_pyobj, index, pyobj ); + Py_XDECREF( old_item ); + } + + void set_item( Py_ssize_t index, PyObjectPtr& item ) const + { + PyObject* old_item = PyList_GET_ITEM( m_pyobj, index ); + PyList_SET_ITEM( m_pyobj, index, item.get() ); + Py_XINCREF( item.get() ); + Py_XDECREF( old_item ); + } + + bool del_item( Py_ssize_t index ) const + { + if( PySequence_DelItem( m_pyobj, index ) == -1 ) + return false; + return true; + } + + bool append( PyObjectPtr& pyobj ) const + { + if( PyList_Append( m_pyobj, pyobj.get() ) == 0 ) + return true; + return false; + } + + Py_ssize_t index( PyObjectPtr& item ) const + { + Py_ssize_t maxidx = size(); + for( Py_ssize_t idx = 0; idx < maxidx; idx++ ) + { + PyObjectPtr other( get_item( idx ) ); + if( item.richcompare( other, Py_EQ ) ) + return idx; + } + return -1; + } + +}; + + +/*----------------------------------------------------------------------------- +| Dict Ptr +|----------------------------------------------------------------------------*/ +class PyDictPtr : public PyObjectPtr { + +public: + + PyDictPtr() : PyObjectPtr() {} + + PyDictPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyDictPtr( PyObject* pydict ) : PyObjectPtr( pydict ) {} + + bool check() + { + return PyDict_Check( m_pyobj ); + } + + bool check_exact() + { + return PyDict_CheckExact( m_pyobj ); + } + + Py_ssize_t size() const + { + return PyDict_Size( m_pyobj ); + } + + PyObjectPtr get_item( PyObject* key ) const + { + return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItem( m_pyobj, key ) ) ) ; + } + + PyObjectPtr get_item( PyObjectPtr& key ) const + { + return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItem( m_pyobj, key.get() ) ) ); + } + + PyObjectPtr get_item( const char* key ) const + { + return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItemString( m_pyobj, key ) ) ); + } + + PyObjectPtr get_item( std::string& key ) const + { + return get_item( key.c_str() ); + } + + bool set_item( PyObject* key, PyObject* value ) const + { + if( PyDict_SetItem( m_pyobj, key, value ) == 0 ) + return true; + return false; + } + + bool set_item( PyObject* key, PyObjectPtr& value ) const + { + if( PyDict_SetItem( m_pyobj, key, value.get() ) == 0 ) + return true; + return false; + } + + bool set_item( PyObjectPtr& key, PyObject* value ) const + { + if( PyDict_SetItem( m_pyobj, key.get(), value ) == 0 ) + return true; + return false; + } + + bool set_item( PyObjectPtr& key, PyObjectPtr& value ) const + { + if( PyDict_SetItem( m_pyobj, key.get(), value.get() ) == 0 ) + return true; + return false; + } + + bool set_item( const char* key, PyObjectPtr& value ) const + { + if( PyDict_SetItemString( m_pyobj, key, value.get() ) == 0 ) + return true; + return false; + } + + bool set_item( const char* key, PyObject* value ) const + { + if( PyDict_SetItemString( m_pyobj, key, value ) == 0 ) + return true; + return false; + } + + bool set_item( std::string& key, PyObjectPtr& value ) const + { + return set_item( key.c_str(), value ); + } + + bool del_item( PyObjectPtr& key ) const + { + if( PyDict_DelItem( m_pyobj, key.get() ) == 0 ) + return true; + return false; + } + + bool del_item( const char* key ) const + { + if( PyDict_DelItemString( m_pyobj, key ) == 0 ) + return true; + return false; + } + + bool del_item( std::string& key ) const + { + return del_item( key.c_str() ); + } + +}; + + +/*----------------------------------------------------------------------------- +| Method Ptr +|----------------------------------------------------------------------------*/ +class PyMethodPtr : public PyObjectPtr { + +public: + + PyMethodPtr() : PyObjectPtr() {} + + PyMethodPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyMethodPtr( PyObject* pymethod ) : PyObjectPtr( pymethod ) {} + + bool check() + { + return PyMethod_Check( m_pyobj ); + } + + PyObjectPtr get_self() const + { + return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_SELF( m_pyobj ) ) ); + } + + PyObjectPtr get_function() const + { + return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_FUNCTION( m_pyobj ) ) ); + } + +#if PY_MAJOR_VERSION < 3 + PyObjectPtr get_class() const + { + return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_CLASS( m_pyobj ) ) ); + } +#endif +}; + + +/*----------------------------------------------------------------------------- +| Weakref Ptr +|----------------------------------------------------------------------------*/ +class PyWeakrefPtr : public PyObjectPtr { + +public: + + PyWeakrefPtr() : PyObjectPtr() {} + + PyWeakrefPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} + + PyWeakrefPtr( PyObject* pyweakref ) : PyObjectPtr( pyweakref ) {} + + bool check() + { + return PyWeakref_CheckRef( m_pyobj ); + } + + bool check_exact() + { + return PyWeakref_CheckRefExact( m_pyobj ); + } + + PyObjectPtr get_object() const + { + return PyObjectPtr( PythonHelpers::newref( PyWeakref_GET_OBJECT( m_pyobj ) ) ); + } + +}; + +} // namespace PythonHelpers diff --git a/contrib/python/kiwisolver/py3/py/solver.cpp b/contrib/python/kiwisolver/py3/py/solver.cpp new file mode 100644 index 00000000000..7a2ef23ca33 --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/solver.cpp @@ -0,0 +1,333 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + if( PyTuple_GET_SIZE( args ) != 0 || ( kwargs && PyDict_Size( kwargs ) != 0 ) ) + return py_type_fail( "Solver.__new__ takes no arguments" ); + PyObject* pysolver = PyType_GenericNew( type, args, kwargs ); + if( !pysolver ) + return 0; + Solver* self = reinterpret_cast<Solver*>( pysolver ); + new( &self->solver ) kiwi::Solver(); + return pysolver; +} + + +static void +Solver_dealloc( Solver* self ) +{ + self->solver.~Solver(); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Solver_addConstraint( Solver* self, PyObject* other ) +{ + if( !Constraint::TypeCheck( other ) ) + return py_expected_type_fail( other, "Constraint" ); + Constraint* cn = reinterpret_cast<Constraint*>( other ); + try + { + self->solver.addConstraint( cn->constraint ); + } + catch( const kiwi::DuplicateConstraint& ) + { + PyErr_SetObject( DuplicateConstraint, other ); + return 0; + } + catch( const kiwi::UnsatisfiableConstraint& ) + { + PyErr_SetObject( UnsatisfiableConstraint, other ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_removeConstraint( Solver* self, PyObject* other ) +{ + if( !Constraint::TypeCheck( other ) ) + return py_expected_type_fail( other, "Constraint" ); + Constraint* cn = reinterpret_cast<Constraint*>( other ); + try + { + self->solver.removeConstraint( cn->constraint ); + } + catch( const kiwi::UnknownConstraint& ) + { + PyErr_SetObject( UnknownConstraint, other ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_hasConstraint( Solver* self, PyObject* other ) +{ + if( !Constraint::TypeCheck( other ) ) + return py_expected_type_fail( other, "Constraint" ); + Constraint* cn = reinterpret_cast<Constraint*>( other ); + return newref( self->solver.hasConstraint( cn->constraint ) ? Py_True : Py_False ); +} + + +static PyObject* +Solver_addEditVariable( Solver* self, PyObject* args ) +{ + PyObject* pyvar; + PyObject* pystrength; + if( !PyArg_ParseTuple( args, "OO", &pyvar, &pystrength ) ) + return 0; + if( !Variable::TypeCheck( pyvar ) ) + return py_expected_type_fail( pyvar, "Variable" ); + double strength; + if( !convert_to_strength( pystrength, strength ) ) + return 0; + Variable* var = reinterpret_cast<Variable*>( pyvar ); + try + { + self->solver.addEditVariable( var->variable, strength ); + } + catch( const kiwi::DuplicateEditVariable& ) + { + PyErr_SetObject( DuplicateEditVariable, pyvar ); + return 0; + } + catch( const kiwi::BadRequiredStrength& e ) + { + PyErr_SetString( BadRequiredStrength, e.what() ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_removeEditVariable( Solver* self, PyObject* other ) +{ + if( !Variable::TypeCheck( other ) ) + return py_expected_type_fail( other, "Variable" ); + Variable* var = reinterpret_cast<Variable*>( other ); + try + { + self->solver.removeEditVariable( var->variable ); + } + catch( const kiwi::UnknownEditVariable& ) + { + PyErr_SetObject( UnknownEditVariable, other ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_hasEditVariable( Solver* self, PyObject* other ) +{ + if( !Variable::TypeCheck( other ) ) + return py_expected_type_fail( other, "Variable" ); + Variable* var = reinterpret_cast<Variable*>( other ); + return newref( self->solver.hasEditVariable( var->variable ) ? Py_True : Py_False ); +} + + +static PyObject* +Solver_suggestValue( Solver* self, PyObject* args ) +{ + PyObject* pyvar; + PyObject* pyvalue; + if( !PyArg_ParseTuple( args, "OO", &pyvar, &pyvalue ) ) + return 0; + if( !Variable::TypeCheck( pyvar ) ) + return py_expected_type_fail( pyvar, "Variable" ); + double value; + if( !convert_to_double( pyvalue, value ) ) + return 0; + Variable* var = reinterpret_cast<Variable*>( pyvar ); + try + { + self->solver.suggestValue( var->variable, value ); + } + catch( const kiwi::UnknownEditVariable& ) + { + PyErr_SetObject( UnknownEditVariable, pyvar ); + return 0; + } + Py_RETURN_NONE; +} + + +static PyObject* +Solver_updateVariables( Solver* self ) +{ + self->solver.updateVariables(); + Py_RETURN_NONE; +} + + +static PyObject* +Solver_reset( Solver* self ) +{ + self->solver.reset(); + Py_RETURN_NONE; +} + + +static PyObject* +Solver_dump( Solver* self ) +{ + PyObjectPtr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) ); + PyObject_Print( dump_str.get(), stdout, 0 ); + Py_RETURN_NONE; +} + +static PyObject* +Solver_dumps( Solver* self ) +{ + return PyUnicode_FromString( self->solver.dumps().c_str() ); +} + +static PyMethodDef +Solver_methods[] = { + { "addConstraint", ( PyCFunction )Solver_addConstraint, METH_O, + "Add a constraint to the solver." }, + { "removeConstraint", ( PyCFunction )Solver_removeConstraint, METH_O, + "Remove a constraint from the solver." }, + { "hasConstraint", ( PyCFunction )Solver_hasConstraint, METH_O, + "Check whether the solver contains a constraint." }, + { "addEditVariable", ( PyCFunction )Solver_addEditVariable, METH_VARARGS, + "Add an edit variable to the solver." }, + { "removeEditVariable", ( PyCFunction )Solver_removeEditVariable, METH_O, + "Remove an edit variable from the solver." }, + { "hasEditVariable", ( PyCFunction )Solver_hasEditVariable, METH_O, + "Check whether the solver contains an edit variable." }, + { "suggestValue", ( PyCFunction )Solver_suggestValue, METH_VARARGS, + "Suggest a desired value for an edit variable." }, + { "updateVariables", ( PyCFunction )Solver_updateVariables, METH_NOARGS, + "Update the values of the solver variables." }, + { "reset", ( PyCFunction )Solver_reset, METH_NOARGS, + "Reset the solver to the initial empty starting condition." }, + { "dump", ( PyCFunction )Solver_dump, METH_NOARGS, + "Dump a representation of the solver internals to stdout." }, + { "dumps", ( PyCFunction )Solver_dumps, METH_NOARGS, + "Dump a representation of the solver internals to a string." }, + { 0 } // sentinel +}; + + +PyTypeObject Solver_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Solver", /* tp_name */ + sizeof( Solver ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Solver_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)0, /* tp_repr */ + (PyNumberMethods*)0, /* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* Documentation string */ + (traverseproc)0, /* tp_traverse */ + (inquiry)0, /* tp_clear */ + (richcmpfunc)0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Solver_methods, /* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Solver_new, /* tp_new */ + (freefunc)PyObject_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +PyObject* DuplicateConstraint; + +PyObject* UnsatisfiableConstraint; + +PyObject* UnknownConstraint; + +PyObject* DuplicateEditVariable; + +PyObject* UnknownEditVariable; + +PyObject* BadRequiredStrength; + + +int import_solver() +{ + DuplicateConstraint = PyErr_NewException( + const_cast<char*>( "kiwisolver.DuplicateConstraint" ), 0, 0 ); + if( !DuplicateConstraint ) + return -1; + UnsatisfiableConstraint = PyErr_NewException( + const_cast<char*>( "kiwisolver.UnsatisfiableConstraint" ), 0, 0 ); + if( !UnsatisfiableConstraint ) + return -1; + UnknownConstraint = PyErr_NewException( + const_cast<char*>( "kiwisolver.UnknownConstraint" ), 0, 0 ); + if( !UnknownConstraint ) + return -1; + DuplicateEditVariable = PyErr_NewException( + const_cast<char*>( "kiwisolver.DuplicateEditVariable" ), 0, 0 ); + if( !DuplicateEditVariable ) + return -1; + UnknownEditVariable = PyErr_NewException( + const_cast<char*>( "kiwisolver.UnknownEditVariable" ), 0, 0 ); + if( !UnknownEditVariable ) + return -1; + BadRequiredStrength = PyErr_NewException( + const_cast<char*>( "kiwisolver.BadRequiredStrength" ), 0, 0 ); + if( !BadRequiredStrength ) + return -1; + return PyType_Ready( &Solver_Type ); +} diff --git a/contrib/python/kiwisolver/py3/py/strength.cpp b/contrib/python/kiwisolver/py3/py/strength.cpp new file mode 100644 index 00000000000..df1552d1947 --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/strength.cpp @@ -0,0 +1,162 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "util.h" + + +using namespace PythonHelpers; + + +struct strength +{ + PyObject_HEAD; +}; + + +static void +strength_dealloc( PyObject* self ) +{ + Py_TYPE( self )->tp_free( self ); +} + + +static PyObject* +strength_weak( strength* self ) +{ + return PyFloat_FromDouble( kiwi::strength::weak ); +} + + +static PyObject* +strength_medium( strength* self ) +{ + return PyFloat_FromDouble( kiwi::strength::medium ); +} + + +static PyObject* +strength_strong( strength* self ) +{ + return PyFloat_FromDouble( kiwi::strength::strong ); +} + + +static PyObject* +strength_required( strength* self ) +{ + return PyFloat_FromDouble( kiwi::strength::required ); +} + + +static PyObject* +strength_create( strength* self, PyObject* args ) +{ + PyObject* pya; + PyObject* pyb; + PyObject* pyc; + PyObject* pyw = 0; + if( !PyArg_ParseTuple( args, "OOO|O", &pya, &pyb, &pyc, &pyw ) ) + return 0; + double a, b, c; + double w = 1.0; + if( !convert_to_double( pya, a ) ) + return 0; + if( !convert_to_double( pyb, b ) ) + return 0; + if( !convert_to_double( pyc, c ) ) + return 0; + if( pyw && !convert_to_double( pyw, w ) ) + return 0; + return PyFloat_FromDouble( kiwi::strength::create( a, b, c, w ) ); +} + + +static PyGetSetDef +strength_getset[] = { + { "weak", ( getter )strength_weak, 0, + "The predefined weak strength." }, + { "medium", ( getter )strength_medium, 0, + "The predefined medium strength." }, + { "strong", ( getter )strength_strong, 0, + "The predefined strong strength." }, + { "required", ( getter )strength_required, 0, + "The predefined required strength." }, + { 0 } // sentinel +}; + + +static PyMethodDef +strength_methods[] = { + { "create", ( PyCFunction )strength_create, METH_VARARGS, + "Create a strength from constituent values and optional weight." }, + { 0 } // sentinel +}; + + +PyTypeObject strength_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.strength", /* tp_name */ + sizeof( strength ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)strength_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)0, /* tp_repr */ + (PyNumberMethods*)0, /* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* Documentation string */ + (traverseproc)0, /* tp_traverse */ + (inquiry)0, /* tp_clear */ + (richcmpfunc)0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)strength_methods, /* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + strength_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)0, /* tp_new */ + (freefunc)PyObject_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_strength() +{ + return PyType_Ready( &strength_Type ); +} diff --git a/contrib/python/kiwisolver/py3/py/symbolics.h b/contrib/python/kiwisolver/py3/py/symbolics.h new file mode 100644 index 00000000000..ac575537403 --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/symbolics.h @@ -0,0 +1,620 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <Python.h> +#include "pythonhelpers.h" +#include "types.h" +#include "util.h" + + +template<typename Op, typename T> +struct UnaryInvoke +{ + PyObject* operator()( PyObject* value ) + { + return Op()( reinterpret_cast<T*>( value ) ); + } +}; + + +template<typename Op, typename T> +struct BinaryInvoke +{ + PyObject* operator()( PyObject* first, PyObject* second ) + { + if( T::TypeCheck( first ) ) + return invoke<Normal>( reinterpret_cast<T*>( first ), second ); + return invoke<Reverse>( reinterpret_cast<T*>( second ), first ); + } + + struct Normal + { + template<typename U> + PyObject* operator()( T* primary, U secondary ) + { + return Op()( primary, secondary ); + } + }; + + struct Reverse + { + template<typename U> + PyObject* operator()( T* primary, U secondary ) + { + return Op()( secondary, primary ); + } + }; + + template<typename Invk> + PyObject* invoke( T* primary, PyObject* secondary ) + { + if( Expression::TypeCheck( secondary ) ) + return Invk()( primary, reinterpret_cast<Expression*>( secondary ) ); + if( Term::TypeCheck( secondary ) ) + return Invk()( primary, reinterpret_cast<Term*>( secondary ) ); + if( Variable::TypeCheck( secondary ) ) + return Invk()( primary, reinterpret_cast<Variable*>( secondary ) ); + if( PyFloat_Check( secondary ) ) + return Invk()( primary, PyFloat_AS_DOUBLE( secondary ) ); +#if PY_MAJOR_VERSION < 3 + if( PyInt_Check( secondary ) ) + return Invk()( primary, double( PyInt_AS_LONG( secondary ) ) ); +#endif + if( PyLong_Check( secondary ) ) + { + double v = PyLong_AsDouble( secondary ); + if( v == -1 && PyErr_Occurred() ) + return 0; + return Invk()( primary, v ); + } + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +struct BinaryMul +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* BinaryMul::operator()( Variable* first, double second ) +{ + PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + if( !pyterm ) + return 0; + Term* term = reinterpret_cast<Term*>( pyterm ); + term->variable = PythonHelpers::newref( pyobject_cast( first ) ); + term->coefficient = second; + return pyterm; +} + + +template<> inline +PyObject* BinaryMul::operator()( Term* first, double second ) +{ + PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + if( !pyterm ) + return 0; + Term* term = reinterpret_cast<Term*>( pyterm ); + term->variable = PythonHelpers::newref( first->variable ); + term->coefficient = first->coefficient * second; + return pyterm; +} + + +template<> inline +PyObject* BinaryMul::operator()( Expression* first, double second ) +{ + using namespace PythonHelpers; + PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + PyObjectPtr terms( PyTuple_New( PyTuple_GET_SIZE( first->terms ) ) ); + if( !terms ) + return 0; + Py_ssize_t end = PyTuple_GET_SIZE( first->terms ); + for( Py_ssize_t i = 0; i < end; ++i ) // memset 0 for safe error return + PyTuple_SET_ITEM( terms.get(), i, 0 ); + for( Py_ssize_t i = 0; i < end; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( first->terms, i ); + PyObject* term = BinaryMul()( reinterpret_cast<Term*>( item ), second ); + if( !term ) + return 0; + PyTuple_SET_ITEM( terms.get(), i, term ); + } + expr->terms = terms.release(); + expr->constant = first->constant * second; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryMul::operator()( double first, Variable* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryMul::operator()( double first, Term* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryMul::operator()( double first, Expression* second ) +{ + return operator()( second, first ); +} + + +struct BinaryDiv +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* BinaryDiv::operator()( Variable* first, double second ) +{ + if( second == 0.0 ) + { + PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" ); + return 0; + } + return BinaryMul()( first, 1.0 / second ); +} + + +template<> inline +PyObject* BinaryDiv::operator()( Term* first, double second ) +{ + if( second == 0.0 ) + { + PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" ); + return 0; + } + return BinaryMul()( first, 1.0 / second ); +} + + +template<> inline +PyObject* BinaryDiv::operator()( Expression* first, double second ) +{ + if( second == 0.0 ) + { + PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" ); + return 0; + } + return BinaryMul()( first, 1.0 / second ); +} + + +struct UnaryNeg +{ + template<typename T> + PyObject* operator()( T value ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* UnaryNeg::operator()( Variable* value ) +{ + return BinaryMul()( value, -1.0 ); +} + + +template<> inline +PyObject* UnaryNeg::operator()( Term* value ) +{ + return BinaryMul()( value, -1.0 ); +} + + +template<> inline +PyObject* UnaryNeg::operator()( Expression* value ) +{ + return BinaryMul()( value, -1.0 ); +} + + +struct BinaryAdd +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* BinaryAdd::operator()( Expression* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->constant = first->constant + second->constant; + expr->terms = PySequence_Concat( first->terms, second->terms ); + if( !expr->terms ) + return 0; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Expression* first, Term* second ) +{ + using namespace PythonHelpers; + PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + PyObject* terms = PyTuple_New( PyTuple_GET_SIZE( first->terms ) + 1 ); + if( !terms ) + return 0; + Py_ssize_t end = PyTuple_GET_SIZE( first->terms ); + for( Py_ssize_t i = 0; i < end; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( first->terms, i ); + PyTuple_SET_ITEM( terms, i, newref( item ) ); + } + PyTuple_SET_ITEM( terms, end, newref( pyobject_cast( second ) ) ); + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->terms = terms; + expr->constant = first->constant; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Expression* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( second, 1.0 ) ); + if( !temp ) + return 0; + return operator()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Expression* first, double second ) +{ + using namespace PythonHelpers; + PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->terms = newref( first->terms ); + expr->constant = first->constant + second; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Term* first, double second ) +{ + PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->constant = second; + expr->terms = PyTuple_Pack( 1, first ); + if( !expr->terms ) + return 0; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Term* first, Expression* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Term* first, Term* second ) +{ + PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + if( !pyexpr ) + return 0; + Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() ); + expr->constant = 0.0; + expr->terms = PyTuple_Pack( 2, first, second ); + if( !expr->terms ) + return 0; + return pyexpr.release(); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Term* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( second, 1.0 ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Variable* first, double second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + if( !temp ) + return 0; + return operator()( reinterpret_cast<Term*>( temp.get() ), second ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Variable* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + if( !temp ) + return 0; + return operator()( reinterpret_cast<Term*>( temp.get() ), second ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Variable* first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + if( !temp ) + return 0; + return operator()( reinterpret_cast<Term*>( temp.get() ), second ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( Variable* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + if( !temp ) + return 0; + return operator()( reinterpret_cast<Term*>( temp.get() ), second ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( double first, Variable* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( double first, Term* second ) +{ + return operator()( second, first ); +} + + +template<> inline +PyObject* BinaryAdd::operator()( double first, Expression* second ) +{ + return operator()( second, first ); +} + + +struct BinarySub +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + Py_RETURN_NOTIMPLEMENTED; + } +}; + + +template<> inline +PyObject* BinarySub::operator()( Variable* first, double second ) +{ + return BinaryAdd()( first, -second ); +} + + +template<> inline +PyObject* BinarySub::operator()( Variable* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Variable* first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Variable* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Term* first, double second ) +{ + return BinaryAdd()( first, -second ); +} + + +template<> inline +PyObject* BinarySub::operator()( Term* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Term* first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Term* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Expression* first, double second ) +{ + return BinaryAdd()( first, -second ); +} + + +template<> inline +PyObject* BinarySub::operator()( Expression* first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Expression* first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( Expression* first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( double first, Variable* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( double first, Term* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) ); +} + + +template<> inline +PyObject* BinarySub::operator()( double first, Expression* second ) +{ + PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + if( !temp ) + return 0; + return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) ); +} + + +template<typename T, typename U> +PyObject* makecn( T first, U second, kiwi::RelationalOperator op ) +{ + PythonHelpers::PyObjectPtr pyexpr( BinarySub()( first, second ) ); + if( !pyexpr ) + return 0; + PythonHelpers::PyObjectPtr pycn( PyType_GenericNew( &Constraint_Type, 0, 0 ) ); + if( !pycn ) + return 0; + Constraint* cn = reinterpret_cast<Constraint*>( pycn.get() ); + cn->expression = reduce_expression( pyexpr.get() ); + if( !cn->expression ) + return 0; + kiwi::Expression expr( convert_to_kiwi_expression( cn->expression ) ); + new( &cn->constraint ) kiwi::Constraint( expr, op, kiwi::strength::required ); + return pycn.release(); +} + + +struct CmpEQ +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + return makecn( first, second, kiwi::OP_EQ ); + } +}; + + +struct CmpLE +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + return makecn( first, second, kiwi::OP_LE ); + } +}; + + +struct CmpGE +{ + template<typename T, typename U> + PyObject* operator()( T first, U second ) + { + return makecn( first, second, kiwi::OP_GE ); + } +}; diff --git a/contrib/python/kiwisolver/py3/py/term.cpp b/contrib/python/kiwisolver/py3/py/term.cpp new file mode 100644 index 00000000000..4be64a1eeaf --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/term.cpp @@ -0,0 +1,298 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <sstream> +#include <Python.h> +#include "pythonhelpers.h" +#include "symbolics.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + static const char *kwlist[] = { "variable", "coefficient", 0 }; + PyObject* pyvar; + PyObject* pycoeff = 0; + if( !PyArg_ParseTupleAndKeywords( + args, kwargs, "O|O:__new__", const_cast<char**>( kwlist ), + &pyvar, &pycoeff ) ) + return 0; + if( !Variable::TypeCheck( pyvar ) ) + return py_expected_type_fail( pyvar, "Variable" ); + double coefficient = 1.0; + if( pycoeff && !convert_to_double( pycoeff, coefficient ) ) + return 0; + PyObject* pyterm = PyType_GenericNew( type, args, kwargs ); + if( !pyterm ) + return 0; + Term* self = reinterpret_cast<Term*>( pyterm ); + self->variable = newref( pyvar ); + self->coefficient = coefficient; + return pyterm; +} + + +static void +Term_clear( Term* self ) +{ + Py_CLEAR( self->variable ); +} + + +static int +Term_traverse( Term* self, visitproc visit, void* arg ) +{ + Py_VISIT( self->variable ); + return 0; +} + + +static void +Term_dealloc( Term* self ) +{ + PyObject_GC_UnTrack( self ); + Term_clear( self ); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Term_repr( Term* self ) +{ + std::stringstream stream; + stream << self->coefficient << " * "; + stream << reinterpret_cast<Variable*>( self->variable )->variable.name(); + return FROM_STRING( stream.str().c_str() ); +} + + +static PyObject* +Term_variable( Term* self ) +{ + return newref( self->variable ); +} + + +static PyObject* +Term_coefficient( Term* self ) +{ + return PyFloat_FromDouble( self->coefficient ); +} + + +static PyObject* +Term_value( Term* self ) +{ + Variable* pyvar = reinterpret_cast<Variable*>( self->variable ); + return PyFloat_FromDouble( self->coefficient * pyvar->variable.value() ); +} + + +static PyObject* +Term_add( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryAdd, Term>()( first, second ); +} + + +static PyObject* +Term_sub( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinarySub, Term>()( first, second ); +} + + +static PyObject* +Term_mul( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryMul, Term>()( first, second ); +} + + +static PyObject* +Term_div( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryDiv, Term>()( first, second ); +} + + +static PyObject* +Term_neg( PyObject* value ) +{ + return UnaryInvoke<UnaryNeg, Term>()( value ); +} + + +static PyObject* +Term_richcmp( PyObject* first, PyObject* second, int op ) +{ + switch( op ) + { + case Py_EQ: + return BinaryInvoke<CmpEQ, Term>()( first, second ); + case Py_LE: + return BinaryInvoke<CmpLE, Term>()( first, second ); + case Py_GE: + return BinaryInvoke<CmpGE, Term>()( first, second ); + default: + break; + } + PyErr_Format( + PyExc_TypeError, + "unsupported operand type(s) for %s: " + "'%.100s' and '%.100s'", + pyop_str( op ), + first->ob_type->tp_name, + second->ob_type->tp_name + ); + return 0; +} + + +static PyMethodDef +Term_methods[] = { + { "variable", ( PyCFunction )Term_variable, METH_NOARGS, + "Get the variable for the term." }, + { "coefficient", ( PyCFunction )Term_coefficient, METH_NOARGS, + "Get the coefficient for the term." }, + { "value", ( PyCFunction )Term_value, METH_NOARGS, + "Get the value for the term." }, + { 0 } // sentinel +}; + + +static PyNumberMethods +Term_as_number = { + (binaryfunc)Term_add, /* nb_add */ + (binaryfunc)Term_sub, /* nb_subtract */ + (binaryfunc)Term_mul, /* nb_multiply */ +#if PY_MAJOR_VERSION < 3 + (binaryfunc)Term_div, /* nb_divide */ +#endif + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + (unaryfunc)Term_neg, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ +#if PY_MAJOR_VERSION >= 3 + 0, /* nb_bool */ +#else + 0, /* nb_nonzero */ +#endif + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + (binaryfunc)0, /* nb_or */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_coerce */ +#endif + 0, /* nb_int */ + 0, /* nb_long */ + 0, /* nb_float */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_oct */ + 0, /* nb_hex */ +#endif + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_inplace_divide */ +#endif + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)0, /* nb_floor_divide */ + (binaryfunc)Term_div, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +#if PY_VERSION_HEX >= 0x02050000 + (unaryfunc)0, /* nb_index */ +#endif +#if PY_VERSION_HEX >= 0x03050000 + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ +#endif +}; + + +PyTypeObject Term_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Term", /* tp_name */ + sizeof( Term ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Term_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)Term_repr, /* tp_repr */ + (PyNumberMethods*)&Term_as_number, /* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + 0, /* Documentation string */ + (traverseproc)Term_traverse, /* tp_traverse */ + (inquiry)Term_clear, /* tp_clear */ + (richcmpfunc)Term_richcmp, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Term_methods, /* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Term_new, /* tp_new */ + (freefunc)PyObject_GC_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_term() +{ + return PyType_Ready( &Term_Type ); +} diff --git a/contrib/python/kiwisolver/py3/py/types.h b/contrib/python/kiwisolver/py3/py/types.h new file mode 100644 index 00000000000..628efafbca4 --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/types.h @@ -0,0 +1,112 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <Python.h> +#include <kiwi/kiwi.h> + + +int import_variable(); + +int import_term(); + +int import_expression(); + +int import_constraint(); + +int import_solver(); + +int import_strength(); + + +extern PyTypeObject Variable_Type; + +extern PyTypeObject Term_Type; + +extern PyTypeObject Expression_Type; + +extern PyTypeObject Constraint_Type; + +extern PyTypeObject Solver_Type; + +extern PyTypeObject strength_Type; + +extern PyObject* DuplicateConstraint; + +extern PyObject* UnsatisfiableConstraint; + +extern PyObject* UnknownConstraint; + +extern PyObject* DuplicateEditVariable; + +extern PyObject* UnknownEditVariable; + +extern PyObject* BadRequiredStrength; + + +struct Variable +{ + PyObject_HEAD + PyObject* context; + kiwi::Variable variable; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Variable_Type ) != 0; + } +}; + + +struct Term +{ + PyObject_HEAD + PyObject* variable; + double coefficient; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Term_Type ) != 0; + } +}; + + +struct Expression +{ + PyObject_HEAD + PyObject* terms; + double constant; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Expression_Type ) != 0; + } +}; + + +struct Constraint +{ + PyObject_HEAD + PyObject* expression; + kiwi::Constraint constraint; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Constraint_Type ) != 0; + } +}; + + +struct Solver +{ + PyObject_HEAD + kiwi::Solver solver; + + static bool TypeCheck( PyObject* obj ) + { + return PyObject_TypeCheck( obj, &Solver_Type ) != 0; + } +}; diff --git a/contrib/python/kiwisolver/py3/py/util.h b/contrib/python/kiwisolver/py3/py/util.h new file mode 100644 index 00000000000..78e9cbd0b8e --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/util.h @@ -0,0 +1,230 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#pragma once +#include <map> +#include <string> +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "types.h" + + +inline bool +convert_to_double( PyObject* obj, double& out ) +{ + if( PyFloat_Check( obj ) ) + { + out = PyFloat_AS_DOUBLE( obj ); + return true; + } +#if PY_MAJOR_VERSION < 3 + if( PyInt_Check( obj ) ) + { + out = double( PyInt_AsLong( obj ) ); + return true; + } +#endif + if( PyLong_Check( obj ) ) + { + out = PyLong_AsDouble( obj ); + if( out == -1.0 && PyErr_Occurred() ) + return false; + return true; + } + PythonHelpers::py_expected_type_fail( obj, "float, int, or long" ); + return false; +} + + +inline bool +convert_pystr_to_str( PyObject* value, std::string& out ) +{ +#if PY_MAJOR_VERSION >= 3 + out = PyUnicode_AsUTF8( value ); +#else + if( PyUnicode_Check( value ) ) + { + PythonHelpers::PyObjectPtr py_str( PyUnicode_AsUTF8String( value ) ); + if( !py_str ) + return false; // LCOV_EXCL_LINE + out = PyString_AS_STRING( py_str.get() ); + } + else + out = PyString_AS_STRING( value ); +#endif + return true; +} + + +inline bool +convert_to_strength( PyObject* value, double& out ) +{ +#if PY_MAJOR_VERSION >= 3 + if( PyUnicode_Check( value ) ) + { +#else + if( PyString_Check( value ) | PyUnicode_Check( value )) + { +#endif + std::string str; + if( !convert_pystr_to_str( value, str ) ) + return false; + if( str == "required" ) + out = kiwi::strength::required; + else if( str == "strong" ) + out = kiwi::strength::strong; + else if( str == "medium" ) + out = kiwi::strength::medium; + else if( str == "weak" ) + out = kiwi::strength::weak; + else + { + PyErr_Format( + PyExc_ValueError, + "string strength must be 'required', 'strong', 'medium', " + "or 'weak', not '%s'", + str.c_str() + ); + return false; + } + return true; + } + if( !convert_to_double( value, out ) ) + return false; + return true; +} + + +inline bool +convert_to_relational_op( PyObject* value, kiwi::RelationalOperator& out ) +{ +#if PY_MAJOR_VERSION >= 3 + if( !PyUnicode_Check( value ) ) + { + PythonHelpers::py_expected_type_fail( value, "unicode" ); + return false; + } +#else + if( !(PyString_Check( value ) | PyUnicode_Check( value ) ) ) + { + PythonHelpers::py_expected_type_fail( value, "str or unicode" ); + return false; + } +#endif + std::string str; + if( !convert_pystr_to_str( value, str ) ) + return false; + if( str == "==" ) + out = kiwi::OP_EQ; + else if( str == "<=" ) + out = kiwi::OP_LE; + else if( str == ">=" ) + out = kiwi::OP_GE; + else + { + PyErr_Format( + PyExc_ValueError, + "relational operator must be '==', '<=', or '>=', not '%s'", + str.c_str() + ); + return false; + } + return true; +} + + +inline PyObject* +make_terms( const std::map<PyObject*, double>& coeffs ) +{ + typedef std::map<PyObject*, double>::const_iterator iter_t; + PythonHelpers::PyObjectPtr terms( PyTuple_New( coeffs.size() ) ); + if( !terms ) + return 0; + Py_ssize_t size = PyTuple_GET_SIZE( terms.get() ); + for( Py_ssize_t i = 0; i < size; ++i ) // zero tuple for safe early return + PyTuple_SET_ITEM( terms.get(), i, 0 ); + Py_ssize_t i = 0; + iter_t it = coeffs.begin(); + iter_t end = coeffs.end(); + for( ; it != end; ++it, ++i ) + { + PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + if( !pyterm ) + return 0; + Term* term = reinterpret_cast<Term*>( pyterm ); + term->variable = PythonHelpers::newref( it->first ); + term->coefficient = it->second; + PyTuple_SET_ITEM( terms.get(), i, pyterm ); + } + return terms.release(); +} + + +inline PyObject* +reduce_expression( PyObject* pyexpr ) // pyexpr must be an Expression +{ + Expression* expr = reinterpret_cast<Expression*>( pyexpr ); + std::map<PyObject*, double> coeffs; + Py_ssize_t size = PyTuple_GET_SIZE( expr->terms ); + for( Py_ssize_t i = 0; i < size; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( expr->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + coeffs[ term->variable ] += term->coefficient; + } + PythonHelpers::PyObjectPtr terms( make_terms( coeffs ) ); + if( !terms ) + return 0; + PyObject* pynewexpr = PyType_GenericNew( &Expression_Type, 0, 0 ); + if( !pynewexpr ) + return 0; + Expression* newexpr = reinterpret_cast<Expression*>( pynewexpr ); + newexpr->terms = terms.release(); + newexpr->constant = expr->constant; + return pynewexpr; +} + + +inline kiwi::Expression +convert_to_kiwi_expression( PyObject* pyexpr ) // pyexpr must be an Expression +{ + Expression* expr = reinterpret_cast<Expression*>( pyexpr ); + std::vector<kiwi::Term> kterms; + Py_ssize_t size = PyTuple_GET_SIZE( expr->terms ); + for( Py_ssize_t i = 0; i < size; ++i ) + { + PyObject* item = PyTuple_GET_ITEM( expr->terms, i ); + Term* term = reinterpret_cast<Term*>( item ); + Variable* var = reinterpret_cast<Variable*>( term->variable ); + kterms.push_back( kiwi::Term( var->variable, term->coefficient ) ); + } + return kiwi::Expression( kterms, expr->constant ); +} + + +inline const char* +pyop_str( int op ) +{ + switch( op ) + { + case Py_LT: + return "<"; + case Py_LE: + return "<="; + case Py_EQ: + return "=="; + case Py_NE: + return "!="; + case Py_GT: + return ">"; + case Py_GE: + return ">="; + default: + return ""; + } +} diff --git a/contrib/python/kiwisolver/py3/py/variable.cpp b/contrib/python/kiwisolver/py3/py/variable.cpp new file mode 100644 index 00000000000..a622e8529ab --- /dev/null +++ b/contrib/python/kiwisolver/py3/py/variable.cpp @@ -0,0 +1,353 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2013-2017, Nucleic Development Team. +| +| Distributed under the terms of the Modified BSD License. +| +| The full license is in the file COPYING.txt, distributed with this software. +|----------------------------------------------------------------------------*/ +#include <Python.h> +#include <kiwi/kiwi.h> +#include "pythonhelpers.h" +#include "symbolics.h" +#include "types.h" +#include "util.h" + + +using namespace PythonHelpers; + + +static PyObject* +Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) +{ + static const char *kwlist[] = { "name", "context", 0 }; + PyObject* context = 0; + PyObject* name = 0; + + if( !PyArg_ParseTupleAndKeywords( + args, kwargs, "|OO:__new__", const_cast<char**>( kwlist ), + &name, &context ) ) + return 0; + + PyObjectPtr pyvar( PyType_GenericNew( type, args, kwargs ) ); + if( !pyvar ) + return 0; + + Variable* self = reinterpret_cast<Variable*>( pyvar.get() ); + self->context = xnewref( context ); + + if( name != 0 ) + { +#if PY_MAJOR_VERSION >= 3 + if( !PyUnicode_Check( name ) ) + return py_expected_type_fail( name, "unicode" ); +#else + if( !( PyString_Check( name ) | PyUnicode_Check( name ) ) ) + { + return py_expected_type_fail( name, "str or unicode" ); + } +#endif + std::string c_name; + if( !convert_pystr_to_str(name, c_name) ) + return 0; // LCOV_EXCL_LINE + new( &self->variable ) kiwi::Variable( c_name ); + } + else + { + new( &self->variable ) kiwi::Variable(); + } + + return pyvar.release(); +} + + +static void +Variable_clear( Variable* self ) +{ + Py_CLEAR( self->context ); +} + + +static int +Variable_traverse( Variable* self, visitproc visit, void* arg ) +{ + Py_VISIT( self->context ); + return 0; +} + + +static void +Variable_dealloc( Variable* self ) +{ + PyObject_GC_UnTrack( self ); + Variable_clear( self ); + self->variable.~Variable(); + Py_TYPE( self )->tp_free( pyobject_cast( self ) ); +} + + +static PyObject* +Variable_repr( Variable* self ) +{ + return FROM_STRING( self->variable.name().c_str() ); +} + + +static PyObject* +Variable_name( Variable* self ) +{ + return FROM_STRING( self->variable.name().c_str() ); +} + + +static PyObject* +Variable_setName( Variable* self, PyObject* pystr ) +{ +#if PY_MAJOR_VERSION >= 3 + if( !PyUnicode_Check( pystr ) ) + return py_expected_type_fail( pystr, "unicode" ); +#else + if( !(PyString_Check( pystr ) | PyUnicode_Check( pystr ) ) ) + { + return py_expected_type_fail( pystr, "str or unicode" ); + } +#endif + std::string str; + if( !convert_pystr_to_str( pystr, str ) ) + return 0; + self->variable.setName( str ); + Py_RETURN_NONE; +} + + +static PyObject* +Variable_context( Variable* self ) +{ + if( self->context ) + return newref( self->context ); + Py_RETURN_NONE; +} + + +static PyObject* +Variable_setContext( Variable* self, PyObject* value ) +{ + if( value != self->context ) + { + PyObject* temp = self->context; + self->context = newref( value ); + Py_XDECREF( temp ); + } + Py_RETURN_NONE; +} + + +static PyObject* +Variable_value( Variable* self ) +{ + return PyFloat_FromDouble( self->variable.value() ); +} + + +static PyObject* +Variable_add( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryAdd, Variable>()( first, second ); +} + + +static PyObject* +Variable_sub( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinarySub, Variable>()( first, second ); +} + + +static PyObject* +Variable_mul( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryMul, Variable>()( first, second ); +} + + +static PyObject* +Variable_div( PyObject* first, PyObject* second ) +{ + return BinaryInvoke<BinaryDiv, Variable>()( first, second ); +} + + +static PyObject* +Variable_neg( PyObject* value ) +{ + return UnaryInvoke<UnaryNeg, Variable>()( value ); +} + + +static PyObject* +Variable_richcmp( PyObject* first, PyObject* second, int op ) +{ + switch( op ) + { + case Py_EQ: + return BinaryInvoke<CmpEQ, Variable>()( first, second ); + case Py_LE: + return BinaryInvoke<CmpLE, Variable>()( first, second ); + case Py_GE: + return BinaryInvoke<CmpGE, Variable>()( first, second ); + default: + break; + } + PyErr_Format( + PyExc_TypeError, + "unsupported operand type(s) for %s: " + "'%.100s' and '%.100s'", + pyop_str( op ), + first->ob_type->tp_name, + second->ob_type->tp_name + ); + return 0; +} + + +static PyMethodDef +Variable_methods[] = { + { "name", ( PyCFunction )Variable_name, METH_NOARGS, + "Get the name of the variable." }, + { "setName", ( PyCFunction )Variable_setName, METH_O, + "Set the name of the variable." }, + { "context", ( PyCFunction )Variable_context, METH_NOARGS, + "Get the context object associated with the variable." }, + { "setContext", ( PyCFunction )Variable_setContext, METH_O, + "Set the context object associated with the variable." }, + { "value", ( PyCFunction )Variable_value, METH_NOARGS, + "Get the current value of the variable." }, + { 0 } // sentinel +}; + + +static PyNumberMethods +Variable_as_number = { + (binaryfunc)Variable_add, /* nb_add */ + (binaryfunc)Variable_sub, /* nb_subtract */ + (binaryfunc)Variable_mul, /* nb_multiply */ +#if PY_MAJOR_VERSION < 3 + (binaryfunc)Variable_div, /* nb_divide */ +#endif + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + (unaryfunc)Variable_neg, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ +#if PY_MAJOR_VERSION >= 3 + 0, /* nb_bool */ +#else + 0, /* nb_nonzero */ +#endif + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + (binaryfunc)0, /* nb_or */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_coerce */ +#endif + 0, /* nb_int */ + 0, /* nb_long */ + 0, /* nb_float */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_oct */ + 0, /* nb_hex */ +#endif + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 + 0, /* nb_inplace_divide */ +#endif + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)0, /* nb_floor_divide */ + (binaryfunc)Variable_div, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +#if PY_VERSION_HEX >= 0x02050000 + (unaryfunc)0, /* nb_index */ +#endif +#if PY_VERSION_HEX >= 0x03050000 + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ +#endif +}; + + +PyTypeObject Variable_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + "kiwisolver.Variable", /* tp_name */ + sizeof( Variable ), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Variable_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03050000 + ( PyAsyncMethods* )0, /* tp_as_async */ +#elif PY_VERSION_HEX >= 0x03000000 + ( void* ) 0, /* tp_reserved */ +#else + ( cmpfunc )0, /* tp_compare */ +#endif + (reprfunc)Variable_repr, /* tp_repr */ + (PyNumberMethods*)&Variable_as_number, /* tp_as_number */ + (PySequenceMethods*)0, /* tp_as_sequence */ + (PyMappingMethods*)0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + (getattrofunc)0, /* tp_getattro */ + (setattrofunc)0, /* tp_setattro */ + (PyBufferProcs*)0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION >= 3 + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#endif + 0, /* Documentation string */ + (traverseproc)Variable_traverse, /* tp_traverse */ + (inquiry)Variable_clear, /* tp_clear */ + (richcmpfunc)Variable_richcmp, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + (struct PyMethodDef*)Variable_methods, /* tp_methods */ + (struct PyMemberDef*)0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)0, /* tp_descr_get */ + (descrsetfunc)0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + (allocfunc)PyType_GenericAlloc, /* tp_alloc */ + (newfunc)Variable_new, /* tp_new */ + (freefunc)PyObject_GC_Del, /* tp_free */ + (inquiry)0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + (destructor)0 /* tp_del */ +}; + + +int import_variable() +{ + return PyType_Ready( &Variable_Type ); +} diff --git a/contrib/python/kiwisolver/py3/ya.make b/contrib/python/kiwisolver/py3/ya.make new file mode 100644 index 00000000000..b98f8485dc0 --- /dev/null +++ b/contrib/python/kiwisolver/py3/ya.make @@ -0,0 +1,41 @@ +# Generated by devtools/yamaker (pypi). + +PY3_LIBRARY() + +VERSION(1.1.0) + +LICENSE(BSD-3-Clause) + +PEERDIR( + contrib/python/setuptools +) + +ADDINCL( + contrib/python/kiwisolver/py3 +) + +NO_COMPILER_WARNINGS() + +NO_LINT() + +SRCS( + py/constraint.cpp + py/expression.cpp + py/kiwisolver.cpp + py/solver.cpp + py/strength.cpp + py/term.cpp + py/variable.cpp +) + +PY_REGISTER( + kiwisolver +) + +RESOURCE_FILES( + PREFIX contrib/python/kiwisolver/py3/ + .dist-info/METADATA + .dist-info/top_level.txt +) + +END() diff --git a/contrib/python/kiwisolver/ya.make b/contrib/python/kiwisolver/ya.make new file mode 100644 index 00000000000..773d6b13bb0 --- /dev/null +++ b/contrib/python/kiwisolver/ya.make @@ -0,0 +1,18 @@ +PY23_LIBRARY() + +LICENSE(Service-Py23-Proxy) + +IF (PYTHON2) + PEERDIR(contrib/python/kiwisolver/py2) +ELSE() + PEERDIR(contrib/python/kiwisolver/py3) +ENDIF() + +NO_LINT() + +END() + +RECURSE( + py2 + py3 +) |
