1 /* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8   * the GNU Lesser General Public License as published by the Free
9     Software Foundation; either version 3 of the License, or (at your
10     option) any later version.
11 
12 or
13 
14   * the GNU General Public License as published by the Free Software
15     Foundation; either version 3 of the License, or (at your option) any
16     later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library.  If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_TERM_HPP
30 #define PIRANHA_TERM_HPP
31 
32 #include <cstddef>
33 #include <functional>
34 #include <type_traits>
35 #include <utility>
36 
37 #include "is_cf.hpp"
38 #include "is_key.hpp"
39 #include "math.hpp"
40 #include "symbol_set.hpp"
41 #include "type_traits.hpp"
42 
43 namespace piranha
44 {
45 
46 namespace detail
47 {
48 
49 // Tag for piranha::term.
50 struct term_tag {
51 };
52 }
53 
54 /// Term class.
55 /**
56  * This class represents series terms, which are parametrised over a coefficient type \p Cf and a key type
57  * \p Key. One mutable coefficient instance and one key instance are the only data members and they can be accessed
58  * directly.
59  *
60  * ## Type requirements ##
61  *
62  * - \p Cf must satisfy piranha::is_cf.
63  * - \p Key must satisfy piranha::is_key.
64  *
65  * ## Exception safety guarantee ##
66  *
67  * This class provides the strong exception safety guarantee for all operations.
68  *
69  * ## Move semantics ##
70  *
71  * Move semantics is equivalent to its data members' move semantics.
72  */
73 template <typename Cf, typename Key>
74 class term : detail::term_tag
75 {
76     PIRANHA_TT_CHECK(is_cf, Cf);
77     PIRANHA_TT_CHECK(is_key, Key);
78     // Enabler for the generic binary ctor.
79     template <typename T, typename U>
80     using binary_ctor_enabler =
81         typename std::enable_if<std::is_constructible<Cf, T &&>::value && std::is_constructible<Key, U &&>::value,
82                                 int>::type;
83 
84 public:
85     /// Alias for coefficient type.
86     typedef Cf cf_type;
87     /// Alias for key type.
88     typedef Key key_type;
89     /// Default constructor.
90     /**
91      * Will explicitly call the default constructors of <tt>Cf</tt> and <tt>Key</tt>.
92      *
93      * @throws unspecified any exception thrown by the default constructors of \p Cf and \p Key.
94      */
term()95     term() : m_cf(), m_key()
96     {
97     }
98     /// Default copy constructor.
99     /**
100      * @throws unspecified any exception thrown by the copy constructors of \p Cf and \p Key.
101      */
102     term(const term &) = default;
103     /// Defaulted move constructor.
104     term(term &&) = default;
105     /// Constructor from generic coefficient and key.
106     /**
107      * \note
108      * This constructor is activated only if coefficient and key are constructible from \p T and \p U.
109      *
110      * This constructor will forward perfectly \p cf and \p key to construct coefficient and key.
111      *
112      * @param cf argument used for the construction of the coefficient.
113      * @param key argument used for the construction of the key.
114      *
115      * @throws unspecified any exception thrown by the constructors of \p Cf and \p Key.
116      */
117     template <typename T, typename U, binary_ctor_enabler<T, U> = 0>
term(T && cf,U && key)118     explicit term(T &&cf, U &&key) : m_cf(std::forward<T>(cf)), m_key(std::forward<U>(key))
119     {
120     }
121     /// Trivial destructor.
~term()122     ~term()
123     {
124         PIRANHA_TT_CHECK(is_container_element, term);
125     }
126     /// Copy assignment operator.
127     /**
128      * @param other assignment argument.
129      *
130      * @return reference to \p this.
131      *
132      * @throws unspecified any exception thrown by the copy constructors of \p Cf and \p Key.
133      */
operator =(const term & other)134     term &operator=(const term &other)
135     {
136         if (likely(this != &other)) {
137             term tmp(other);
138             *this = std::move(tmp);
139         }
140         return *this;
141     }
142     /// Trivial move-assignment operator.
143     /**
144      * @param other assignment argument.
145      *
146      * @return reference to \p this.
147      */
operator =(term && other)148     term &operator=(term &&other) noexcept
149     {
150         if (likely(this != &other)) {
151             m_cf = std::move(other.m_cf);
152             m_key = std::move(other.m_key);
153         }
154         return *this;
155     }
156     /// Equality operator.
157     /**
158      * Equivalence of terms is defined by the equivalence of their keys.
159      *
160      * @param other comparison argument.
161      *
162      * @return <tt>m_key == other.m_key</tt>.
163      *
164      * @throws unspecified any exception thrown by the equality operators of \p Key.
165      */
operator ==(const term & other) const166     bool operator==(const term &other) const
167     {
168         return m_key == other.m_key;
169     }
170     /// Hash value.
171     /**
172      * The term's hash value is given by its key's hash value.
173      *
174      * @return hash value of \p m_key as calculated via a default-constructed instance of \p std::hash.
175      *
176      * @throws unspecified any exception thrown by the hash functor of \p Key.
177      */
hash() const178     std::size_t hash() const
179     {
180         return std::hash<key_type>()(m_key);
181     }
182     /// Compatibility test.
183     /**
184      * @param args reference arguments set.
185      *
186      * @return the key's <tt>is_compatible()</tt> method's return value.
187      */
is_compatible(const symbol_set & args) const188     bool is_compatible(const symbol_set &args) const noexcept
189     {
190         // NOTE: if this (and is_ignorable) are made re-implementable at a certain point in derived term classes,
191         // we must take care of asserting noexcept on the corresponding methods in the derived class.
192         return m_key.is_compatible(args);
193     }
194     /// Ignorability test.
195     /**
196      * Note that this method is not allowed to throw, so any exception thrown by calling piranha::math::is_zero() on the
197      * coefficient
198      * will result in the termination of the program.
199      *
200      * @param args reference arguments set.
201      *
202      * @return \p true if either the key's <tt>is_ignorable()</tt> method or piranha::math::is_zero() on the coefficient
203      * return \p true,
204      * \p false otherwise.
205      */
is_ignorable(const symbol_set & args) const206     bool is_ignorable(const symbol_set &args) const noexcept
207     {
208         return (math::is_zero(m_cf) || m_key.is_ignorable(args));
209     }
210     /// Coefficient member.
211     mutable Cf m_cf;
212     /// Key member.
213     Key m_key;
214 };
215 
216 namespace detail
217 {
218 
219 // Enabler for the enable_noexcept_checks specialisation for terms.
220 template <typename T>
221 using term_enc_enabler = typename std::enable_if<std::is_base_of<detail::term_tag, T>::value>::type;
222 }
223 
224 /// Specialisation of piranha::enable_noexcept_checks for piranha::term.
225 /**
226  * This specialisation is activated when \p T is an instance of piranha::term. The value of the type trait
227  * is set to \p true if both the coefficient and key types satisfy piranha::enable_noexcept_checks. Otherwise,
228  * the value of the type trait is set to \p false.
229  */
230 template <typename T>
231 struct enable_noexcept_checks<T, detail::term_enc_enabler<T>> {
232 private:
233     static const bool implementation_defined
234         = enable_noexcept_checks<typename T::cf_type>::value && enable_noexcept_checks<typename T::key_type>::value;
235 
236 public:
237     /// Value of the type trait.
238     static const bool value = implementation_defined;
239 };
240 
241 template <typename T>
242 const bool enable_noexcept_checks<T, typename std::enable_if<std::is_base_of<detail::term_tag, T>::value>::type>::value;
243 }
244 
245 #endif
246