diff --git a/src/specmicp_common/cached_vector.hpp b/src/specmicp_common/cached_vector.hpp index 1a881c6..cf9ecbe 100644 --- a/src/specmicp_common/cached_vector.hpp +++ b/src/specmicp_common/cached_vector.hpp @@ -1,389 +1,447 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget <fabieng@princeton.edu> Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the copyright holder 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 HOLDER 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. * ============================================================================= */ #ifndef SPECMICP_REACTMICP_CACHEDVECTOR_HPP #define SPECMICP_REACTMICP_CACHEDVECTOR_HPP //! \file cached_vector.hpp //! \brief Vector with cache #include <vector> #include <string> #include <utility> #include <stdexcept> #include <algorithm> #include <type_traits> namespace specmicp { namespace utils { //! \brief A vector with cache //! //! This vector is designed to simulate a large vector with only a few different //! values of type T, where T is 'big' //! //! \tparam T must be copyable, assignable, default constructible template <typename T> class CachedVector { - class CachedIterator; + class CachedIterator; // forward declaration of the iterator class public: - using value_type = T; - using reference = T&; - using const_reference = const T&; - using size_type = std::size_t; - using iterator = CachedIterator; - using const_iterator = const CachedIterator; - using index_vector = std::vector<size_type>; + using value_type = T; //!< Type of a value + using reference = T&; //!< Type a reference to a value + using const_reference = const T&; //!< Type of a const reference to a value + using size_type = std::size_t; //!< Type for the size of a vector + using iterator = CachedIterator; //!< Type of an iterator + using const_iterator = const CachedIterator; //!< Type of a const iterator + using index_vector = std::vector<size_type>; //!< Type of the index vector //! \brief Default constructor, an empty vector CachedVector() {} - //! \brief Initialize a 'size' vector with value T + //! \brief Initialize a vector with value T + //! + //! \param size size of the vector + //! \param value initial value for all entry CachedVector(size_type size, T&& value): m_cache({std::forward<T>(value),}), m_indexes(size, 0) {} - //! \brief Initialize a 'size' vector with default T + //! \brief Initialize a vector with default T + //! + //! \param size size of the vector + //! + //! \warning This constructor is only available if a default constructor exist for T CachedVector(size_type size, typename std::enable_if<std::is_default_constructible<T>::value>::type* = nullptr): m_cache(), m_indexes(size, 0) { m_cache.emplace_back(); } //! \brief Reserve storage + //! + //! Reserves memory for the vector + //! + //! \param size_cap the size to reserve in memory for the vector void reserve(size_type size_cap) { m_indexes.reserve(size_cap); } //! \brief Reserve cache storage + //! + //! Reserves memory for the cache + //! + //! \param size_cap The cache will be able to store up to 'size_cap' value + //! without realllocation void reserve_cache(size_type size_cap) { m_cache.reserve(size_cap); } - //! \brief Access specified element + //! \brief Access an element + //! + //! No bounds checkoing is performed in this method. + //! + //! \sa at reference operator[] (size_type pos) { return m_cache[m_indexes[pos]]; } - - //! \brief Access specified element + //! \brief Access an element + //! + //! No bounds checkoing is performed in this method. + //! + //! \sa at const_reference operator[] (size_type pos) const { return m_cache[m_indexes[pos]]; } - //! \brief Access specified element with bounds checking + //! \brief Access an lement with bounds checking + //! + //! \sa operator[] reference at(size_type pos) { std::size_t ind = m_indexes.at(pos); return m_cache.at(ind); } - - //! \brief Access specified element with bounds checking + //! \brief Access an element with bounds checking + //! + //! \sa operator[] const_reference at(size_type pos) const { std::size_t ind = m_indexes.at(pos); return m_cache.at(ind); } //! \brief Return a reference to the position in the cache + //! + //! \sa get size_type& operator() (size_type pos) { return m_indexes[pos]; } //! \brief Return a reference to the position in the cache + //! + //! \sa get const size_type& operator () (size_type pos) const { return m_indexes[pos]; } //! \brief Return the cached value + //! + //! \sa operator() reference get(size_t pos_cache) { return m_cache[pos_cache]; } //! \brief Return the cached value + //! + //! \sa operator() const_reference get(size_t pos_cache) const { return m_cache[pos_cache]; } //! \brief Return the size of the vector size_type size() const { return m_indexes.size(); } //! \brief Return the size of the cache size_type size_cache() const { return m_cache.size(); } //! \brief Push an element to the cache //! //! \param value The element to add //! \return the position of the element in the cache size_type push_back_cache(T&& value) { m_cache.push_back(std::forward<T>(value)); return (m_cache.size() - 1); } //! \brief Emplace a new element to the cache template <typename ...Args> size_type emplace_back_cache(Args...args) { m_cache.emplace_back(args...); return (m_cache.size() - 1); } //! \brief Adds an element to the end //! //! \param index refers to a position in the cache void push_back(size_type index) { m_indexes.push_back(index); } //! \brief Adds an element to the end and a new value to the cache //! //! \param value a new value void push_back(T&& value) { auto ind = push_back_cache(std::forward<T>(value)); m_indexes.push_back(ind); } //! \brief Add a new element at the end and build a new element to the cache template <typename ...Args> void emplace_back(Args...args) { auto ind = emplace_back_cache(args...); m_indexes.push_back(ind); } //! \brief Fork the value at the given position reference fork(size_type pos) { m_cache.push_back(m_cache[m_indexes[pos]]); m_indexes[pos] = m_cache.size() - 1; return m_cache.back(); } //! \brief Set the value to another //! //! Don't check the validity of the value ! void set(size_type pos, size_type pos_cache) { m_indexes[pos] = pos_cache; } // Iterator business //! \brief Return an iterator to the beginning iterator begin() { return CachedIterator(m_indexes.begin(), m_cache); } //! \brief Return an iterator to the end iterator end() { return CachedIterator(m_indexes.end(), m_cache); } //! \brief Return an iterator to the beginning const_iterator cbegin() const { return CachedIterator(m_indexes.begin(), m_cache); } //! \brief Return an iterator to the end const_iterator cend() const{ return CachedIterator(m_indexes.end(), m_cache); } //! \brief Return an iterator to the beginning of the indexes index_vector::iterator begin_index() { return m_indexes.begin(); } //! \brief Return an iterator to the end of the indexes index_vector::iterator end_index() { return m_indexes.end(); } //! \brief Return an iterator to the beginning of the indexes index_vector::const_iterator cbegin_index() const { return m_indexes.cbegin(); } //! \brief Return an iterator to the end of the indexes index_vector::const_iterator cend_index() const{ return m_indexes.cend(); } private: - //! \brief The iterator + //! \brief The iterator class for a cached vector class CachedIterator: public std::vector<size_type>::iterator { using base = std::vector<size_type>; using base_iterator = base::iterator; public : + //! \param base iterator over the index vector + //! \param cache referece to the cache vector CachedIterator( const base_iterator& base, std::vector<T>& cache): base_iterator(base), m_cache(cache) {} + //! \param pos position where to start the iterator + //! \param cache referece to the cache vector CachedIterator( std::size_t pos, std::vector<T>& cache ): base_iterator(), m_cache(cache) { advance(static_cast<base_iterator&>(*this), pos-1); } + //! \brief Dereference operator + //! + //! \return a reference to the corresponding value T& operator* () { return m_cache[base_iterator::operator* ()]; } + //! \brief Const dereference operator + //! + //! \return a const reference to the corresponding value const T& operator* () const { return m_cache[base_iterator::operator* ()]; } private: std::vector<T>& m_cache; }; private: std::vector<T> m_cache; std::vector<size_type> m_indexes; }; -//! \brief A name cached vector +//! \brief A named cached vector //! -//! Every cache value has an associated name +//! Every cache value has an associated name which can be used to obtain the +//! values. template <typename T> -class NameCachedVector: public CachedVector<T> +class NameCachedVector: + public CachedVector<T> { public: - using size_type = typename CachedVector<T>::size_type; - using reference = typename CachedVector<T>::reference; - using const_reference = typename CachedVector<T>::const_reference; + using size_type = typename CachedVector<T>::size_type; //!< Type of the size of a value + using reference = typename CachedVector<T>::reference; //!< Type of a reference + using const_reference = typename CachedVector<T>::const_reference; //!< Type of a const reference NameCachedVector(): CachedVector<T>() {} + + //! \brief Initialize a vector with a default value + //! + //! \param size size of the vector + //! \param name name of the default value + //! \param value the default value NameCachedVector(size_type size, std::string name, T&& value): CachedVector<T>(size, std::forward<T>(value)), m_names({name, }) {} + //! \brief Initialize the vector with the default value + //! + //! \param size size of the vector + //! \param name name of the default value + //! + //! \warning only exist if the value type has a default constructor NameCachedVector(size_type size, std::string name, typename std::enable_if<std::is_default_constructible<T>::value>::type* = nullptr): CachedVector<T>(size), m_names({name, }) {} //! \brief Reserve cache storage void reserve_cache(size_type size_cap) { CachedVector<T>::reserve_cache(size_cap); m_names.reserve(size_cap); } //! \brief Push an element to the cache //! //! \param name label for the value to add //! \param value The element to add //! \return the position of the element in the cache size_type push_back_cache(const std::string& name, T&& value) { m_names.push_back(name); return CachedVector<T>::push_back_cache(std::forward<T>(value)); } //! \brief Emplace a new element to the cache template <typename ...Args> size_type emplace_back_cache(const std::string& name, Args...args) { m_names.push_back(name); return CachedVector<T>::emplace_back_cache(args...); } //! \brief Adds an element to the end and a new value to the cache //! //! \param name label for the value //! \param value a new value void push_back(const std::string& name, T&& value) { CachedVector<T>::push_back(std::forward<T>(value)); m_names.push_back(name); } //! \brief Add a new element at the end and build a new element to the cache template <typename ...Args> void emplace_back(const std::string& name, Args...args) { CachedVector<T>::emplace_back(args...); m_names.push_back(name); } //! \brief Return the value stored as "name" reference get(std::string name) { return CachedVector<T>::get(get_cache_position(name)); } //! \brief Return the value stored as "name" const_reference get(std::string name) const { return CachedVector<T>::get(get_cache_position(name)); } bool has_value(std::string name) const { auto it = std::find(m_names.cbegin(), m_names.cend(), name); return (it != m_names.cend()); } //! \brief Fork the value at the given position reference fork(size_type pos, const std::string& name) { m_names.push_back(name); return CachedVector<T>::fork(pos); } //! \brief Fork the value of a given name reference fork(const std::string& old_name, const std::string& new_name) { auto&& val = get(old_name); auto pos = push_back_cache(new_name, std::forward<T>(val)); return CachedVector<T>::get(pos); } //! \brief Set the given node to the value 'name' void set(size_type pos, const std::string& name) { CachedVector<T>::set(pos, get_cache_position(name)); } //! \brief Return the position in the cache size_type get_cache_position(const std::string& name) const { auto it = std::find(m_names.cbegin(), m_names.cend(), name); if (it == m_names.cend()) { throw std::invalid_argument("Unknow value in NamedCachedVector '" + name + "'."); } return (it-m_names.cbegin()); } private: std::vector<std::string> m_names; }; } //end namespace utils } //end namespace specmicp #endif // SPECMICP_REACTMICP_CACHEDVECTOR_HPP diff --git a/src/specmicp_common/dateandtime.hpp b/src/specmicp_common/dateandtime.hpp index 4adbeb0..86444b7 100644 --- a/src/specmicp_common/dateandtime.hpp +++ b/src/specmicp_common/dateandtime.hpp @@ -1,67 +1,66 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget <fabieng@princeton.edu> Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the copyright holder 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 HOLDER 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. * ============================================================================= */ #ifndef SPECMICP_UTILS_DATEANDTIME_HPP #define SPECMICP_UTILS_DATEANDTIME_HPP //! \file dateandtime.hpp //! \brief Date and time tools #include "types.hpp" #include <string> #include <ctime> namespace specmicp { //! \namespace specmicp::dateandtime //! \brief Date and time utilities namespace dateandtime { //! \brief Return a textual representation of time_point std::string SPECMICP_DLL_PUBLIC to_text(std::time_t& time_point); //! \brief Return a localized textual representation of time point std::string SPECMICP_DLL_PUBLIC to_text_localized(std::time_t& time_point); //! \brief Return a textual representation of the current date and time std::string SPECMICP_DLL_PUBLIC now(); //! \brief Return a localized textual representation of the current date and time -//! Locali std::string SPECMICP_DLL_PUBLIC now_localized(); } //end namespace dateandtime } //end namespace specmicp #endif // SPECMICP_UTILS_DATEANDTIME_HPP diff --git a/src/specmicp_common/filesystem.hpp b/src/specmicp_common/filesystem.hpp index 5c1958e..f05996a 100644 --- a/src/specmicp_common/filesystem.hpp +++ b/src/specmicp_common/filesystem.hpp @@ -1,93 +1,111 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget <fabieng@princeton.edu> Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the copyright holder 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 HOLDER 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. * ============================================================================= */ #ifndef SPECMICP_UTILS_FILESYSTEM #define SPECMICP_UTILS_FILESYSTEM //! \file filesystem.hpp //! \brief helper functions to deal with the filesystem #include "macros.hpp" #include <string> #include <vector> namespace specmicp { namespace utils { //! \brief Check that the directory exist bool SPECMICP_DLL_PUBLIC is_directory(const std::string& path); //! \brief Check that the directory exist bool SPECMICP_DLL_PUBLIC is_file(const std::string& path); //! \brief Return the current directory std::string SPECMICP_DLL_PUBLIC get_current_directory(); -//! \brief Complete a path +//! \brief Complete a path from a given directory and a file +//! +//! \param dir the directory +//! \param file the basename of the file std::string SPECMICP_DLL_PUBLIC complete_path( const std::string& dir, const std::string& file ); //! \brief Return true if the given path is absolute +//! +//! \param path the path to test bool SPECMICP_DLL_PUBLIC is_path_absolute(const std::string& path); //! \brief Return an absolute path from a relative one //! +//! \param[in] rel_path a path +//! \param[out] error a string to contain error message if needed +//! \return An aboslute path, or an empty string if an error occured +//! //! If an error is detected then the return string will be empty, //! and error will contain a message about the error std::string SPECMICP_DLL_PUBLIC relative_to_absolute( const std::string& rel_path, std::string& error ); //! \brief Return the complete path to a file from a set of directories +//! +//! \param filename basename of the file to seek +//! \param directories list of directories where to search the file +//! \return an absolute path, or an empty string if the file wasn't found std::string SPECMICP_DLL_PUBLIC find_path( std::string filename, const std::vector<std::string>& directories ); //! \brief Return true if the environment variable is defined +//! +//! \param env_var the environment variable to check bool SPECMICP_DLL_PUBLIC has_env( const std::string& env_var ); //! \brief Return an environment variable +//! +//! \param env_var the environment variable +//! \return the value stored in the environment variable, or an empty string std::string SPECMICP_DLL_PUBLIC get_env( const std::string& env_var ); } // end namespace utils } // end namespace specmicp #endif // SPECMICP_UTILS_FILESYSTEM diff --git a/src/specmicp_common/moving_average.hpp b/src/specmicp_common/moving_average.hpp index 899cf3b..5c89ef1 100644 --- a/src/specmicp_common/moving_average.hpp +++ b/src/specmicp_common/moving_average.hpp @@ -1,103 +1,111 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget <fabieng@princeton.edu> Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the copyright holder 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 HOLDER 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. * ============================================================================= */ #ifndef SPECMICP_UTILS_MOVINGAVERAGE_HPP #define SPECMICP_UTILS_MOVINGAVERAGE_HPP /*! \file moving_average.hpp \brief Exponential moving average The moving average allows to take a weighted average of the last x points in the data series. It is used in particular in the adaptive timestepping algorithm. -\code{.cpp} - ExponentialMovingAverage moving_average(0.1, 1.0); - for (int i=0; i<10; ++i) { - std::cout << "Current value : " << moving_average.add_point(i); - } - // reset to 1.0 - moving_average.reset(1.0); -\endcode */ #include "types.hpp" namespace specmicp { //! \namespace specmicp::utils //! \brief misc and others used in different places namespace utils { -//! \brief Exponential moving average -//! -//! https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average -//! -//! @param alpha coefficient between 0 and 1 -//! @param init initial value of the average +/*! \brief Exponential moving average + +The exponential moving average \f$Y_n\$ is given by +\f$Y_n = \alpha y + (1 - \alpha) Y_{n-1}\f$ +where y is a value to add to the average, and \f$Y_{n-1}\f$ the previous value. + +The average allows to take into account several previous values while storing +only one value. The impact of the previous iteration on the average value is + controlled by the parameter \$\alpha\$, which is between 0 and 1. + +\code{.cpp} + ExponentialMovingAverage moving_average(0.1, 1.0); + for (int i=0; i<10; ++i) { + std::cout << "Current value : " << moving_average.add_point(i); + } + // reset to 1.0 + moving_average.reset(1.0); +\endcode + +*/ class SPECMICP_DLL_PUBLIC ExponentialMovingAverage { public: + //! \param alpha coefficient between 0 and 1 + //! \param init initial value of the average ExponentialMovingAverage(scalar_t alpha, scalar_t init): m_alpha(alpha), m_current_value(init) { } //! \brief Add a point in the series, return current average value scalar_t add_point(scalar_t value); //! \brief Return the current average value scalar_t current_value() { return m_current_value; } //! \brief Reset the average to 'value' void reset(scalar_t value) { m_current_value = value; } //! \brief Set the average parameter value void set_alpha(scalar_t alpha) {m_alpha = alpha;} private: scalar_t m_alpha; scalar_t m_current_value; }; } // end namespace utils } // end namespace specmicp #endif // SPECMICP_UTILS_MOVINGAVERAGE_HPP diff --git a/src/specmicp_common/string_algorithms.cpp b/src/specmicp_common/string_algorithms.cpp index 7ef9676..d83397b 100644 --- a/src/specmicp_common/string_algorithms.cpp +++ b/src/specmicp_common/string_algorithms.cpp @@ -1,419 +1,419 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget <fabieng@princeton.edu> Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the copyright holder 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 HOLDER 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. * ============================================================================= */ #include "string_algorithms.hpp" #include <algorithm> #define RANGE_SEP ':' namespace specmicp { namespace utils { //! \brief Split a string std::vector<std::string> split(const std::string& to_split, char separator) { std::vector<std::string> splitted; auto nb_elem = std::count(to_split.begin(), to_split.end(), separator); splitted.reserve(nb_elem+1); auto start = to_split.begin(); for (auto current=to_split.cbegin(); current!= to_split.cend(); ++current) { if (*current == separator) { splitted.emplace_back(to_split.substr( start - to_split.cbegin(), current-start)); start = current + 1; } } // last value if (start < to_split.cend()) { splitted.emplace_back(to_split.substr(start - to_split.cbegin())); } return splitted; } std::string strip(const std::string& to_trim) { auto start = to_trim.begin(); auto end = to_trim.end(); // at the beginning auto current=to_trim.begin(); for (; current!=to_trim.cend(); ++current) { if (*current != ' ') { start = current; break; } } if (current == to_trim.cend()) {return "";} // empty string // at the end for (auto current=to_trim.cend()-1;current>=to_trim.begin(); --current) { if (*current != ' ') { end = ++current; break; } } return to_trim.substr(start - to_trim.begin(), end-start); } -void parse_one_term(std::vector<index_t>& numbers, const std::string& term); -void parse_one_term(std::vector<uindex_t>& numbers, const std::string& term); +static void parse_one_term(std::vector<index_t>& numbers, const std::string& term); +static void parse_one_term(std::vector<uindex_t>& numbers, const std::string& term); template <typename T> std::vector<T> range_indices_impl(const std::string& range_str) { std::vector<T> numbers; const uindex_t nb_sep = std::count(range_str.cbegin(), range_str.cend(), ','); numbers.reserve(nb_sep+1); if (nb_sep == 0) { // if only one term parse_one_term(numbers, range_str); } else { const std::vector<std::string> terms = split(range_str, ','); for (const auto& term: terms) { parse_one_term(numbers, term); } } // sort the indices std::sort(numbers.begin(), numbers.end()); return numbers; } template <> std::vector<index_t> range_indices(const std::string& range_str) { return range_indices_impl<index_t>(range_str); } template <> std::vector<uindex_t> range_indices(const std::string& range_str) { return range_indices_impl<uindex_t>(range_str); } void parse_one_term(std::vector<index_t>& numbers, const std::string &term) { if (term == "") return; // check for empty terms const auto to_anal = strip(term); const auto is_range = std::find(to_anal.cbegin(), to_anal.cend(), RANGE_SEP); if (is_range == to_anal.cend()) { // if just a number numbers.push_back(std::stol(to_anal)); } else { // if a range const std::string first_str = to_anal.substr(0, is_range-to_anal.cbegin()); const std::string last_str = to_anal.substr(is_range-to_anal.cbegin()+1); const index_t first = std::stol(first_str); const index_t last = std::stol(last_str); for (auto ind=first; ind<=last; ++ind) { numbers.push_back(ind); } } } void parse_one_term(std::vector<uindex_t>& numbers, const std::string &term) { if (term == "") return; // check for empty terms const auto to_anal = strip(term); const auto is_range = std::find(to_anal.cbegin(), to_anal.cend(), RANGE_SEP); if (is_range == to_anal.cend()) { // if just a number auto val = std::stol(to_anal); if (val < 0) { throw std::invalid_argument("Negative argurment found in " "positive ranges : '"+std::to_string(val) + "'."); } numbers.push_back(val); } else { // if a range const std::string first_str = to_anal.substr(0, is_range-to_anal.cbegin()); const std::string last_str = to_anal.substr(is_range-to_anal.cbegin()+1); const index_t first = std::stol(first_str); if (first < 0 ) { throw std::invalid_argument("Negative argurment found in " "positive ranges : '"+std::to_string(first) + "'."); } const index_t last = std::stol(last_str); for (auto ind=first; ind<=last; ++ind) { numbers.push_back(ind); } } } template <typename T> class ExpressionParser { public: ExpressionParser( const std::string& expr, const std::unordered_map<std::string, T>& vars ): complete_expr(expr), variables(vars) { } T parse() {return parse_term(complete_expr);} T parse_value(const std::string& value); T parse_factor(const std::string& factor); T parse_term(const std::string& term); private: const std::string& complete_expr; const std::unordered_map<std::string, T>& variables; }; template <> scalar_t ExpressionParser<scalar_t>::parse_value(const std::string& expr) { auto trimmed_expr = strip(expr); if (trimmed_expr == "") { throw std::invalid_argument("Error while parsing : " + complete_expr); } scalar_t val = std::nan(""); try { std::size_t pos; val = std::stod(trimmed_expr, &pos); if (pos != trimmed_expr.size()) { throw std::logic_error("Error while processing '" + expr + "'. Just a number was expected." "Error occured while processing '" + complete_expr + "'."); } } catch (const std::invalid_argument& e) { auto it = variables.find(trimmed_expr); if (it == variables.cend()) { throw std::out_of_range("Unknown variable '"+ trimmed_expr + "' in parsing of '" + complete_expr +"'."); } val = it->second; } return val; } template <> index_t ExpressionParser<index_t>::parse_value(const std::string& expr) { auto trimmed_expr = strip(expr); index_t val = INT_MAX; try { std::size_t pos; val = std::stol(trimmed_expr, &pos); if (pos != trimmed_expr.size()) { throw std::logic_error("Error while processing" + expr + ". Just a number was expected." "Error occured while processing " + complete_expr + "."); } } catch (const std::invalid_argument& e) { auto it = variables.find(trimmed_expr); if (it == variables.cend()) { throw std::out_of_range("Unknown variable '"+ trimmed_expr + "' in parsing of '" + complete_expr +"'."); } val = it->second; } return val; } template <typename T> T ExpressionParser<T>::parse_factor(const std::string& expr) { auto it = expr.find_last_of("*/"); if (it == expr.npos) { return parse_value(expr); } auto val1 = parse_factor(expr.substr(0, it)); auto val2 = parse_value(expr.substr(it+1)); if (expr[it] == '*') { return val1*val2; } else { return val1/val2; } return 0; } template <typename T> T ExpressionParser<T>::parse_term(const std::string& expr) { auto it = expr.find_first_of("+-"); if (it == expr.npos) { return parse_factor(expr); } else if (it != 0) { auto val1 = parse_factor(expr.substr(0, it)); auto val2 = parse_factor(expr.substr(it+1)); if (expr[it] == '+') { return val1+val2; } else { return val1-val2; } } else { if (expr[it] == '-') { return -parse_term(expr.substr(1)); } else { return parse_term(expr.substr(1)); } } return 0; } template <> scalar_t ExpressionParser<scalar_t>::parse_term(const std::string& expr) { auto pred = [](const char& t) -> bool {return ((t == '+') or( t == '-'));}; std::size_t pos = 0; char op; auto it = expr.begin(); while (pos < expr.size()) { it = std::find_if(it, expr.end(), pred); if (it == expr.cend()) { pos = expr.size(); break; } op = *it; pos = it-expr.cbegin(); // xe-y notation => it's a number ! if (op == '-' and expr[pos-1] == 'e' and std::isdigit(expr[pos-2])) { ++it; // jump to next char continue; } else { break; } } if (pos == expr.size()) { return parse_factor(expr); } if (pos > 0) { auto val1 = parse_factor(expr.substr(0, pos)); auto val2 = parse_factor(expr.substr(pos+1)); if (op == '+') { return val1+val2; } else { return val1-val2; } } else { if (op == '-') { return -parse_term(expr.substr(1)); } else { return parse_term(expr.substr(1)); } } return 0; } template <> scalar_t parse_expression( const std::string& expr, const std::unordered_map<std::string, scalar_t>& variables ) { return ExpressionParser<scalar_t>(expr, variables).parse(); } template <> index_t parse_expression( const std::string& expr, const std::unordered_map<std::string, index_t>& variables ) { return ExpressionParser<index_t>(expr, variables).parse(); } bool string_to_bool(const std::string& to_bool_str) { // Contain the strings that matches true or false static const char* true_str_test[] = {"True", "true", "1", "yes", "Yes", "Y"}; static const char* false_str_test[] = {"False", "false", "0", "no", "No", "N"}; auto trimmed = strip(to_bool_str); bool val = false; // test for true for (auto test: true_str_test) { if (test == trimmed) { val = true; goto exit; } } // test for false for (auto test: false_str_test) { if (test == trimmed) { val = false; goto exit; } } // no match => error throw std::invalid_argument("Unrecognized value when converting to bool '" + trimmed + "'. Recognized value : true/false." ); exit: return val; } } //end namespace utils } //end namespace specmicp diff --git a/src/specmicp_common/string_algorithms.hpp b/src/specmicp_common/string_algorithms.hpp index 478690d..453e2a6 100644 --- a/src/specmicp_common/string_algorithms.hpp +++ b/src/specmicp_common/string_algorithms.hpp @@ -1,127 +1,126 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget <fabieng@princeton.edu> Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the copyright holder 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 HOLDER 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. * ============================================================================= */ #ifndef SPECMICP_UTILS_STRINGALGORITHMS_HPP #define SPECMICP_UTILS_STRINGALGORITHMS_HPP //! \file string_algorithms.hpp //! \brief Algorithms for string //! //! Commom algorithms to handle/parse/modify strings. #include "types.hpp" #include <string> #include <vector> #include <unordered_map> namespace specmicp { namespace utils { //! \brief Split a string //! //! Split a string into several string //! //! \param to_split the string to split //! \param separator the separator to use std::vector<std::string> SPECMICP_DLL_PUBLIC split( const std::string& to_split, char separator ); //! \brief Strip a string //! //! Remove spaces at the beginning and the end std::string SPECMICP_DLL_PUBLIC strip(const std::string& to_trim); //! \brief Get a range of index from a string //! //! The different format accepted for range_str are : //! - "x" //! - "x-y" //! - "u,v" //! //! where x,y are number and u,v are valid range_str template <typename T=index_t> std::vector<T> range_indices(const std::string& range_str); - template <> std::vector<index_t> range_indices(const std::string& range_str); template <> std::vector<uindex_t> range_indices(const std::string& range_str); //! \brief Parse a simple algebraic expression of type T template <typename T> T parse_expression( const std::string& expr, const std::unordered_map<std::string, T>& variables ); // Note : this is a template for API consistence with the overload // which doesn't take a map of variables //! \brief Parse a simple algebraic expression of floating numbers //! //! Parenthesis are not parsed yet and will result in errors template <> scalar_t parse_expression( const std::string& expr, const std::unordered_map<std::string, scalar_t>& variables ); //! \brief Parse a simple algebraic expression of floating numbers //! //! Parenthesis are not parsed yet and will result in errors template <> index_t parse_expression( const std::string& expr, const std::unordered_map<std::string, index_t>& variables ); //! \brief Parse a simple algebraic equation template <typename T> T parse_expression( const std::string& expr ) { return parse_expression(expr, std::unordered_map<std::string, T>()); } //! \brief Transform a string to a boolean bool string_to_bool(const std::string& to_bool_str); } //end namespace utils } //end namespace specmicp #endif // SPECMICP_UTILS_STRINGALGORITHMS_HPP