kiwisolver-1.0.1/0000777000000000000000000000000013173657672012062 5ustar 00000000000000kiwisolver-1.0.1/COPYING.txt0000666000000000000000000000643212666264630013732 0ustar 00000000000000========================= The Kiwi licensing terms ========================= Kiwi is licensed under the terms of the Modified BSD License (also known as New or Revised BSD), as follows: Copyright (c) 2013, Nucleic Development Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Nucleic Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. About Kiwi ---------- Chris Colbert began the Kiwi project in December 2013 in an effort to create a blisteringly fast UI constraint solver. Chris is still the project lead. The Nucleic Development Team is the set of all contributors to the Nucleic project and its subprojects. The core team that coordinates development on GitHub can be found here: http://github.com/nucleic. The current team consists of: * Chris Colbert Our Copyright Policy -------------------- Nucleic uses a shared copyright model. Each contributor maintains copyright over their contributions to Nucleic. But, it is important to note that these contributions are typically only changes to the repositories. Thus, the Nucleic source code, in its entirety is not the copyright of any single person or institution. Instead, it is the collective copyright of the entire Nucleic Development Team. If individual contributors want to maintain a record of what changes/contributions they have specific copyright on, they should indicate their copyright in the commit message of the change, when they commit the change to one of the Nucleic repositories. With this in mind, the following banner should be used in any source code file to indicate the copyright and license terms: #------------------------------------------------------------------------------ # Copyright (c) 2013, 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. #------------------------------------------------------------------------------ kiwisolver-1.0.1/kiwi/0000777000000000000000000000000013173657672013025 5ustar 00000000000000kiwisolver-1.0.1/kiwi/AssocVector.h0000666000000000000000000003034212666264630015425 0ustar 00000000000000//////////////////////////////////////////////////////////////////////////////// // 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 #include #include #include namespace Loki { //////////////////////////////////////////////////////////////////////////////// // class template AssocVectorCompare // Used by AssocVector //////////////////////////////////////////////////////////////////////////////// namespace Private { template class AssocVectorCompare : public C { typedef std::pair 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 not std::pair // * iterators are random //////////////////////////////////////////////////////////////////////////////// template < class K, class V, class C = std::less, class A = std::allocator< std::pair > > class AssocVector : private std::vector< std::pair, A > , private Private::AssocVectorCompare { typedef std::vector, A> Base; typedef Private::AssocVectorCompare 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 , 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 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 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 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 equal_range(const key_type& k) { MyCompare& me = *this; return std::equal_range(begin(), end(), k, me); } std::pair equal_range( const key_type& k) const { const MyCompare& me = *this; return std::equal_range(begin(), end(), k, me); } template friend bool operator==(const AssocVector& lhs, const AssocVector& rhs); bool operator<(const AssocVector& rhs) const { const Base& me = *this; const Base& yo = rhs; return me < yo; } template friend bool operator!=(const AssocVector& lhs, const AssocVector& rhs); template friend bool operator>(const AssocVector& lhs, const AssocVector& rhs); template friend bool operator>=(const AssocVector& lhs, const AssocVector& rhs); template friend bool operator<=(const AssocVector& lhs, const AssocVector& rhs); }; template inline bool operator==(const AssocVector& lhs, const AssocVector& rhs) { const std::vector, A>& me = lhs; return me == rhs; } template inline bool operator!=(const AssocVector& lhs, const AssocVector& rhs) { return !(lhs == rhs); } template inline bool operator>(const AssocVector& lhs, const AssocVector& rhs) { return rhs < lhs; } template inline bool operator>=(const AssocVector& lhs, const AssocVector& rhs) { return !(lhs < rhs); } template inline bool operator<=(const AssocVector& lhs, const AssocVector& rhs) { return !(rhs < lhs); } // specialized algorithms: template void swap(AssocVector& lhs, AssocVector& rhs) { lhs.swap(rhs); } } // namespace Loki kiwisolver-1.0.1/kiwi/constraint.h0000666000000000000000000000553513153746425015363 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #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 vars; typedef std::vector::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 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 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 kiwisolver-1.0.1/kiwi/debug.h0000666000000000000000000001055313153746432014257 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #include "constraint.h" #include "solverimpl.h" #include "term.h" namespace kiwi { namespace impl { class DebugHelper { public: static void dump( const SolverImpl& solver ) { std::cout << "Objective" << std::endl; std::cout << "---------" << std::endl; dump( *solver.m_objective ); std::cout << std::endl; std::cout << "Tableau" << std::endl; std::cout << "-------" << std::endl; dump( solver.m_rows ); std::cout << std::endl; std::cout << "Infeasible" << std::endl; std::cout << "----------" << std::endl; dump( solver.m_infeasible_rows ); std::cout << std::endl; std::cout << "Variables" << std::endl; std::cout << "---------" << std::endl; dump( solver.m_vars ); std::cout << std::endl; std::cout << "Edit Variables" << std::endl; std::cout << "--------------" << std::endl; dump( solver.m_edits ); std::cout << std::endl; std::cout << "Constraints" << std::endl; std::cout << "-----------" << std::endl; dump( solver.m_cns ); std::cout << std::endl; std::cout << std::endl; } static void dump( const SolverImpl::RowMap& rows ) { typedef SolverImpl::RowMap::const_iterator iter_t; iter_t end = rows.end(); for( iter_t it = rows.begin(); it != end; ++it ) { dump( it->first ); std::cout << " | "; dump( *it->second ); } } static void dump( const std::vector& symbols ) { typedef std::vector::const_iterator iter_t; iter_t end = symbols.end(); for( iter_t it = symbols.begin(); it != end; ++it ) { dump( *it ); std::cout << std::endl; } } static void dump( const SolverImpl::VarMap& vars ) { typedef SolverImpl::VarMap::const_iterator iter_t; iter_t end = vars.end(); for( iter_t it = vars.begin(); it != end; ++it ) { std::cout << it->first.name() << " = "; dump( it->second ); std::cout << std::endl; } } static void dump( const SolverImpl::CnMap& cns ) { typedef SolverImpl::CnMap::const_iterator iter_t; iter_t end = cns.end(); for( iter_t it = cns.begin(); it != end; ++it ) dump( it->first ); } static void dump( const SolverImpl::EditMap& edits ) { typedef SolverImpl::EditMap::const_iterator iter_t; iter_t end = edits.end(); for( iter_t it = edits.begin(); it != end; ++it ) std::cout << it->first.name() << std::endl; } static void dump( const Row& row ) { typedef Row::CellMap::const_iterator iter_t; std::cout << row.constant(); iter_t end = row.cells().end(); for( iter_t it = row.cells().begin(); it != end; ++it ) { std::cout << " + " << it->second << " * "; dump( it->first ); } std::cout << std::endl; } static void dump( const Symbol& symbol ) { switch( symbol.type() ) { case Symbol::Invalid: std::cout << "i"; break; case Symbol::External: std::cout << "v"; break; case Symbol::Slack: std::cout << "s"; break; case Symbol::Error: std::cout << "e"; break; case Symbol::Dummy: std::cout << "d"; break; default: break; } std::cout << symbol.id(); } static void dump( const Constraint& cn ) { typedef std::vector::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 ) { std::cout << it->coefficient() << " * "; std::cout << it->variable().name() << " + "; } std::cout << cn.expression().constant(); switch( cn.op() ) { case OP_LE: std::cout << " <= 0 "; break; case OP_GE: std::cout << " >= 0 "; break; case OP_EQ: std::cout << " == 0 "; break; default: break; } std::cout << " | strength = " << cn.strength() << std::endl; } }; } // namespace impl namespace debug { template void dump( const T& value ) { impl::DebugHelper::dump( value ); } } // namespace debug } // namespace kiwi kiwisolver-1.0.1/kiwi/errors.h0000666000000000000000000000631313153746440014503 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #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 kiwisolver-1.0.1/kiwi/expression.h0000666000000000000000000000235113153746445015371 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #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& terms, double constant = 0.0 ) : m_terms( terms ), m_constant( constant ) {} ~Expression() {} const std::vector& terms() const { return m_terms; } double constant() const { return m_constant; } double value() const { typedef std::vector::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 m_terms; double m_constant; }; } // namespace kiwi kiwisolver-1.0.1/kiwi/kiwi.h0000666000000000000000000000116013153746453014131 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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" kiwisolver-1.0.1/kiwi/maptype.h0000666000000000000000000000147613153746463014660 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #include #include #include "AssocVector.h" namespace kiwi { namespace impl { template< typename K, typename V, typename C = std::less, typename A = std::allocator< std::pair > > class MapType { public: typedef Loki::AssocVector Type; //typedef std::map Type; private: MapType(); }; } // namespace impl } // namespace kiwi kiwisolver-1.0.1/kiwi/row.h0000666000000000000000000001126613153746467014012 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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::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 kiwisolver-1.0.1/kiwi/shareddata.h0000666000000000000000000000453113153746474015276 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 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& other ) const { return m_data < other.m_data; } bool operator==( const SharedDataPtr& other ) const { return m_data == other.m_data; } bool operator!=( const SharedDataPtr& other ) const { return m_data != other.m_data; } SharedDataPtr( const SharedDataPtr& other ) : m_data( other.m_data ) { incref( m_data ); } SharedDataPtr& operator=( const SharedDataPtr& other ) { if( m_data != other.m_data ) { T* temp = m_data; m_data = other.m_data; incref( m_data ); decref( temp ); } return *this; } SharedDataPtr& 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 kiwisolver-1.0.1/kiwi/solver.h0000666000000000000000000000701013153746504014475 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 ); } private: Solver( const Solver& ); Solver& operator=( const Solver& ); impl::SolverImpl m_impl; }; } // namespace kiwi kiwisolver-1.0.1/kiwi/solverimpl.h0000666000000000000000000005652613153746510015374 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #include #include #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::Type VarMap; typedef MapType::Type RowMap; typedef MapType::Type CnMap; typedef MapType::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 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 rowptr( createRow( constraint, tag ) ); Symbol subject( chooseSubject( *rowptr, tag ) ); // If chooseSubject could 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 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 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( 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 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::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 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 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() && 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::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::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::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 m_infeasible_rows; std::auto_ptr m_objective; std::auto_ptr m_artificial; Symbol::Id m_id_tick; }; } // namespace impl } // namespace kiwi kiwisolver-1.0.1/kiwi/strength.h0000666000000000000000000000213313153746515015024 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 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 kiwisolver-1.0.1/kiwi/symbol.h0000666000000000000000000000207313153746521014473 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 kiwisolver-1.0.1/kiwi/symbolics.h0000666000000000000000000003067013153746526015203 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #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 terms; terms.reserve( expression.terms().size() ); typedef std::vector::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 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 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 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 kiwisolver-1.0.1/kiwi/term.h0000666000000000000000000000206613153746533014142 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #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& 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 kiwisolver-1.0.1/kiwi/util.h0000666000000000000000000000111313153746540014136 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 kiwisolver-1.0.1/kiwi/variable.h0000666000000000000000000000440013153746545014755 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #include "shareddata.h" namespace kiwi { class Variable { public: class Context { public: Context() {} virtual ~Context() {} }; 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 m_context; double m_value; private: VariableData( const VariableData& other ); VariableData& operator=( const VariableData& other ); }; SharedDataPtr m_data; friend bool operator<( const Variable& lhs, const Variable& rhs ) { return lhs.m_data < rhs.m_data; } }; } // namespace kiwi kiwisolver-1.0.1/kiwi/version.h0000666000000000000000000000102513153746377014660 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 0 #define KIWI_VERSION_HEX 0x010000 #define KIWI_VERSION "1.0.0" kiwisolver-1.0.1/kiwisolver.egg-info/0000777000000000000000000000000013173657672015752 5ustar 00000000000000kiwisolver-1.0.1/kiwisolver.egg-info/dependency_links.txt0000666000000000000000000000000113173657671022017 0ustar 00000000000000 kiwisolver-1.0.1/kiwisolver.egg-info/PKG-INFO0000666000000000000000000000215013173657671017044 0ustar 00000000000000Metadata-Version: 1.0 Name: kiwisolver Version: 1.0.1 Summary: A fast implementation of the Cassowary constraint solver Home-page: https://github.com/nucleic/kiwi Author: The Nucleic Development Team Author-email: sccolbert@gmail.com License: UNKNOWN Description-Content-Type: UNKNOWN Description: Welcome to Kiwi =============== .. image:: https://travis-ci.org/nucleic/kiwi.svg?branch=master :target: https://travis-ci.org/nucleic/kiwi 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. Platform: UNKNOWN kiwisolver-1.0.1/kiwisolver.egg-info/requires.txt0000666000000000000000000000001313173657671020343 0ustar 00000000000000setuptools kiwisolver-1.0.1/kiwisolver.egg-info/SOURCES.txt0000666000000000000000000000123413173657671017635 0ustar 00000000000000COPYING.txt MANIFEST.in README.rst releasenotes.rst setup.py kiwi/AssocVector.h kiwi/constraint.h kiwi/debug.h kiwi/errors.h kiwi/expression.h kiwi/kiwi.h kiwi/maptype.h kiwi/row.h kiwi/shareddata.h kiwi/solver.h kiwi/solverimpl.h kiwi/strength.h kiwi/symbol.h kiwi/symbolics.h kiwi/term.h kiwi/util.h kiwi/variable.h kiwi/version.h kiwisolver.egg-info/PKG-INFO kiwisolver.egg-info/SOURCES.txt kiwisolver.egg-info/dependency_links.txt kiwisolver.egg-info/requires.txt kiwisolver.egg-info/top_level.txt py/constraint.cpp py/expression.cpp py/kiwisolver.cpp py/pythonhelpers.h py/solver.cpp py/strength.cpp py/symbolics.h py/term.cpp py/types.h py/util.h py/variable.cppkiwisolver-1.0.1/kiwisolver.egg-info/top_level.txt0000666000000000000000000000001313173657671020475 0ustar 00000000000000kiwisolver kiwisolver-1.0.1/MANIFEST.in0000666000000000000000000000022412666264630013610 0ustar 00000000000000include MANIFEST.in include COPYING.txt include README.rst include releasenotes.rst recursive-include kiwi *.h recursive-include py *.cpp *.h kiwisolver-1.0.1/PKG-INFO0000666000000000000000000000215013173657672013155 0ustar 00000000000000Metadata-Version: 1.0 Name: kiwisolver Version: 1.0.1 Summary: A fast implementation of the Cassowary constraint solver Home-page: https://github.com/nucleic/kiwi Author: The Nucleic Development Team Author-email: sccolbert@gmail.com License: UNKNOWN Description-Content-Type: UNKNOWN Description: Welcome to Kiwi =============== .. image:: https://travis-ci.org/nucleic/kiwi.svg?branch=master :target: https://travis-ci.org/nucleic/kiwi 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. Platform: UNKNOWN kiwisolver-1.0.1/py/0000777000000000000000000000000013173657672012512 5ustar 00000000000000kiwisolver-1.0.1/py/constraint.cpp0000666000000000000000000002504313153746560015377 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #include #include #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( 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( 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( 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( item ); stream << term->coefficient << " * "; stream << reinterpret_cast( 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( pyoldcn ); Constraint* newcn = reinterpret_cast( 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 ); } kiwisolver-1.0.1/py/expression.cpp0000666000000000000000000002551213153746565015420 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #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( 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( 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( item ); stream << term->coefficient << " * "; stream << reinterpret_cast( 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( item ); Variable* pyvar = reinterpret_cast( term->variable ); result += term->coefficient * pyvar->variable.value(); } return PyFloat_FromDouble( result ); } static PyObject* Expression_add( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Expression_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Expression_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Expression_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Expression_neg( PyObject* value ) { return UnaryInvoke()( value ); } static PyObject* Expression_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) { case Py_EQ: return BinaryInvoke()( first, second ); case Py_LE: return BinaryInvoke()( first, second ); case Py_GE: return BinaryInvoke()( 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 ); } kiwisolver-1.0.1/py/kiwisolver.cpp0000666000000000000000000000561313173657402015410 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #include "pythonhelpers.h" #include "types.h" #define PY_KIWI_VERSION "1.0.1" 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 } kiwisolver-1.0.1/py/pythonhelpers.h0000666000000000000000000004311513153746576015573 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #include #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( o ) ) #define pytype_cast( o ) ( reinterpret_cast( 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( 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 kiwisolver-1.0.1/py/solver.cpp0000666000000000000000000002403513153746602014522 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #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( 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( 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( 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( 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( 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( 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( 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( 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 ) { self->solver.dump(); Py_RETURN_NONE; } 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, "" }, { 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( "kiwisolver.DuplicateConstraint" ), 0, 0 ); if( !DuplicateConstraint ) return -1; UnsatisfiableConstraint = PyErr_NewException( const_cast( "kiwisolver.UnsatisfiableConstraint" ), 0, 0 ); if( !UnsatisfiableConstraint ) return -1; UnknownConstraint = PyErr_NewException( const_cast( "kiwisolver.UnknownConstraint" ), 0, 0 ); if( !UnknownConstraint ) return -1; DuplicateEditVariable = PyErr_NewException( const_cast( "kiwisolver.DuplicateEditVariable" ), 0, 0 ); if( !DuplicateEditVariable ) return -1; UnknownEditVariable = PyErr_NewException( const_cast( "kiwisolver.UnknownEditVariable" ), 0, 0 ); if( !UnknownEditVariable ) return -1; BadRequiredStrength = PyErr_NewException( const_cast( "kiwisolver.BadRequiredStrength" ), 0, 0 ); if( !BadRequiredStrength ) return -1; return PyType_Ready( &Solver_Type ); } kiwisolver-1.0.1/py/strength.cpp0000666000000000000000000001222513153746607015051 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #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 ); } kiwisolver-1.0.1/py/symbolics.h0000666000000000000000000003536313153746614014672 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include "pythonhelpers.h" #include "types.h" #include "util.h" template struct UnaryInvoke { PyObject* operator()( PyObject* value ) { return Op()( reinterpret_cast( value ) ); } }; template struct BinaryInvoke { PyObject* operator()( PyObject* first, PyObject* second ) { if( T::TypeCheck( first ) ) return invoke( reinterpret_cast( first ), second ); return invoke( reinterpret_cast( second ), first ); } struct Normal { template PyObject* operator()( T* primary, U secondary ) { return Op()( primary, secondary ); } }; struct Reverse { template PyObject* operator()( T* primary, U secondary ) { return Op()( secondary, primary ); } }; template PyObject* invoke( T* primary, PyObject* secondary ) { if( Expression::TypeCheck( secondary ) ) return Invk()( primary, reinterpret_cast( secondary ) ); if( Term::TypeCheck( secondary ) ) return Invk()( primary, reinterpret_cast( secondary ) ); if( Variable::TypeCheck( secondary ) ) return Invk()( primary, reinterpret_cast( 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 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( 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( 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( 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( 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 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 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 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( temp.get() ) ); } template 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( 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 PyObject* operator()( T first, U second ) { return makecn( first, second, kiwi::OP_EQ ); } }; struct CmpLE { template PyObject* operator()( T first, U second ) { return makecn( first, second, kiwi::OP_LE ); } }; struct CmpGE { template PyObject* operator()( T first, U second ) { return makecn( first, second, kiwi::OP_GE ); } }; kiwisolver-1.0.1/py/term.cpp0000666000000000000000000002224213153746623014160 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #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( 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( 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( 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( self->variable ); return PyFloat_FromDouble( self->coefficient * pyvar->variable.value() ); } static PyObject* Term_add( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Term_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Term_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Term_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Term_neg( PyObject* value ) { return UnaryInvoke()( value ); } static PyObject* Term_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) { case Py_EQ: return BinaryInvoke()( first, second ); case Py_LE: return BinaryInvoke()( first, second ); case Py_GE: return BinaryInvoke()( 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 ); } kiwisolver-1.0.1/py/types.h0000666000000000000000000000375013153746630014023 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include 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; } }; kiwisolver-1.0.1/py/util.h0000666000000000000000000001443413173655363013641 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #include #include #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; 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& coeffs ) { typedef std::map::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( 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( pyexpr ); std::map 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( 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( 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( pyexpr ); std::vector 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( item ); Variable* var = reinterpret_cast( 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 ""; } } kiwisolver-1.0.1/py/variable.cpp0000666000000000000000000002457013173655363015006 0ustar 00000000000000/*----------------------------------------------------------------------------- | 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 #include #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( kwlist ), &name, &context ) ) return 0; PyObjectPtr pyvar( PyType_GenericNew( type, args, kwargs ) ); if( !pyvar ) return 0; Variable* self = reinterpret_cast( 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; 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()( first, second ); } static PyObject* Variable_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Variable_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Variable_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } static PyObject* Variable_neg( PyObject* value ) { return UnaryInvoke()( value ); } static PyObject* Variable_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) { case Py_EQ: return BinaryInvoke()( first, second ); case Py_LE: return BinaryInvoke()( first, second ); case Py_GE: return BinaryInvoke()( 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 ); } kiwisolver-1.0.1/README.rst0000666000000000000000000000126013140031751013522 0ustar 00000000000000Welcome to Kiwi =============== .. image:: https://travis-ci.org/nucleic/kiwi.svg?branch=master :target: https://travis-ci.org/nucleic/kiwi 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. kiwisolver-1.0.1/releasenotes.rst0000666000000000000000000000214213173655567015305 0ustar 00000000000000Kiwi Release Notes ================== Wrappers x.x.x | Solver x.x.x | unreleased ------------------------------------------ Wrappers 1.0.1 | Solver 1.0.0 | 24/10/2017 ------------------------------------------ - allow unicode strings for variable name in Python 2 - allow unicode strings as strength specifiers in Python 2 Wrappers 1.0.0 | Solver 1.0.0 | 09/06/2017 ------------------------------------------ - Allow anonymous variables (solver PR #32, wrappers PR #22) - Solver: Define binary operators as free functions (PR #23) - Wrappers: support for Python 3 (PR #13) - Wrappers: drop distribute dependency in favor of setuptools (PR #22) - Wrappers: add a comprehensive test suite Wrappers 0.1.3 | Solver 0.1.1 | 07/12/2013 ------------------------------------------ - Update the build script to remove the need for build.py Wrappers 0.1.2 | Solver 0.1.1 | 01/15/2013 ------------------------------------------ - Fix issue #2. Bad handling of zero-size constraints. Wrappers 0.1.1 | Solver 0.1.0 | 01/13/2013 ------------------------------------------ - Initial public release. kiwisolver-1.0.1/setup.cfg0000666000000000000000000000005213173657672013700 0ustar 00000000000000[egg_info] tag_build = tag_date = 0 kiwisolver-1.0.1/setup.py0000666000000000000000000000311313173655715013566 0ustar 00000000000000#------------------------------------------------------------------------------ # 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. #------------------------------------------------------------------------------ from setuptools import setup, Extension from setuptools.command.build_ext import build_ext ext_modules = [ Extension( 'kiwisolver', ['py/kiwisolver.cpp', 'py/constraint.cpp', 'py/expression.cpp', 'py/solver.cpp', 'py/strength.cpp', 'py/term.cpp', 'py/variable.cpp'], include_dirs=['.'], language='c++', ), ] class BuildExt(build_ext): """ A custom build extension for adding compiler-specific options. """ c_opts = { 'msvc': ['/EHsc'] } def build_extensions(self): ct = self.compiler.compiler_type opts = self.c_opts.get(ct, []) for ext in self.extensions: ext.extra_compile_args = opts build_ext.build_extensions(self) setup( name='kiwisolver', version='1.0.1', author='The Nucleic Development Team', author_email='sccolbert@gmail.com', url='https://github.com/nucleic/kiwi', description='A fast implementation of the Cassowary constraint solver', long_description=open('README.rst').read(), install_requires=['setuptools'], ext_modules=ext_modules, cmdclass={'build_ext': BuildExt}, )