diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e92f57 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tags diff --git a/include/vectorwrapper/sequence_range.hpp b/include/vectorwrapper/sequence_range.hpp new file mode 100644 index 0000000..9260fee --- /dev/null +++ b/include/vectorwrapper/sequence_range.hpp @@ -0,0 +1,244 @@ +/* + * Copyright 2015-2017 Michele "King_DuckZ" Santullo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "vectorwrapper.hpp" +#include "sequence_bt.hpp" +#include "size_type.hpp" +#include +#include +#include +#include + +#if defined VWR_OUTER_NAMESPACE +namespace VWR_OUTER_NAMESPACE { +#endif + +namespace vwr { + namespace implem { + //sets value to the index of the first I >= MAX or to sizeof...(I) if + //no such value is found + template + struct find_ge_than_max; + + template + struct find_ge_than_max { + static constexpr size_type value = + IDX + (I >= MAX ? 0 : 1); + }; + template + struct find_ge_than_max { + static constexpr size_type value = + (L >= MAX ? IDX : find_ge_than_max::value); + }; + + //helper to identify the invalid index in the build error messages + template + struct is_invalid_index { + static_assert(not INVALID, + "Invalid index specified, indices must all be >= 0 and <= dimensions" + ); + enum { + value = 0 + }; + }; + } //namespace implem + + template + class sequence_range; + + template + class sequence_range_iterator : OP, CMP { + static_assert(not implem::is_invalid_index< + implem::find_ge_than_max::value, + sizeof...(I) != implem::find_ge_than_max::value + >::value, ""); + static_assert(sizeof...(I) > 0, "At least one index must be specified"); + + typedef sequence_range sequence_range_type; + public: + explicit sequence_range_iterator (const sequence_range_type& parSeq); + sequence_range_iterator (const V& parCurrent, const sequence_range_type& parSeq); + ~sequence_range_iterator() = default; + + sequence_range_iterator& operator++ (); + const V& operator*() const { return m_current; } + bool operator!= (const sequence_range_iterator& parOther) const; + bool operator== (const sequence_range_iterator& parOther) const; + + private: + V m_current; + const sequence_range_type& m_seq; + size_type m_active_elem; + }; + + template + class sequence_range { + public: + typedef sequence_range_iterator const_iterator; + + sequence_range (const V& parFrom, const V& parUpper); + sequence_range (const V& parFrom, const V& parUpper, const V& parStep); + const_iterator cbegin() const; + const_iterator cend() const; + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + [[gnu::always_inline]] const V& from() const { return m_from; } + [[gnu::always_inline]] const V& upper() const { return m_upper; } + [[gnu::always_inline]] const V& step() const { return m_step; } + + private: + V m_from; + V m_upper; + V m_end; + V m_step; + }; + + template + class stepping_sequence_range : public sequence_range { + public: + stepping_sequence_range (const V& parFrom, const V& parUpper, const V& parStep); + private: + V m_step; + }; + + namespace implem { + template + struct get_at_index; + template + struct get_at_index { + static constexpr size_type value = N; + }; + template + struct get_at_index { + static_assert(SEEK < IDX, "Index out of range"); + static constexpr size_type value = 0; + }; + template + struct get_at_index { + static constexpr size_type value = (IDX == SEEK ? L : get_at_index::value); + }; + + template + struct make_sequence_helper; + + template + struct make_sequence_helper> { + typedef sequence_range, OP, CMP, I...> type; + }; + + template + inline V make_end_vector (V parFrom, const V& parUpper) { + constexpr const size_type last_idx = get_at_index::value; + auto& last = parFrom[last_idx]; + const auto& upper_last = parUpper[last_idx]; + last = upper_last; + return parFrom; + } + } //namespace implem + + template + sequence_range_iterator::sequence_range_iterator (const sequence_range_type& parSeq) : + sequence_range_iterator(parSeq.from(), parSeq) + { + } + + template + sequence_range_iterator::sequence_range_iterator (const V& parCurrent, const sequence_range_type& parSeq) : + m_current(parCurrent), + m_seq(parSeq), + m_active_elem(implem::get_at_index<0, 0, I...>::value) + { + } + + template + auto sequence_range_iterator::operator++ () -> sequence_range_iterator& { + static_assert(sizeof...(I) > 0, "At least one index must be given"); + const OP& advance_op = *static_cast(this); + const CMP& cmp_op = *static_cast(this); + + std::array lst {I...}; + size_type index = lst[0]; + m_current[index] = advance_op(m_current[index], m_seq.step()[index]); + if (1 < sizeof...(I) and not cmp_op(m_current[index], m_seq.upper()[index])) { + size_type count = 1; + do { + m_current[index] = m_seq.from()[index]; + index = lst[count++]; + m_current[index] = advance_op(m_current[index], m_seq.step()[index]); + m_active_elem = index; + } while (count < sizeof...(I) and not cmp_op(m_current[index], m_seq.upper()[index])); + } + + return *this; + } + + template + bool sequence_range_iterator::operator!= (const sequence_range_iterator& parOther) const { + return not this->operator==(parOther); + } + + template + bool sequence_range_iterator::operator== (const sequence_range_iterator& parOther) const { + assert(&m_seq.from() == &parOther.m_seq.from() and &m_seq.upper() == &parOther.m_seq.upper()); + const CMP& cmp_op = *static_cast(this); + return m_current == parOther.m_current or ( + implem::get_at_index::value == m_active_elem and + not cmp_op(m_current[m_active_elem], m_seq.upper()[m_active_elem]) + ); + } + + template + sequence_range::sequence_range (const V& parFrom, const V& parUpper) : + m_from(parFrom), + m_upper(parUpper), + m_end(implem::make_end_vector(parFrom, parUpper)), + m_step(1) + { + } + + template + sequence_range::sequence_range (const V& parFrom, const V& parUpper, const V& parStep) : + m_from(parFrom), + m_upper(parUpper), + m_end(implem::make_end_vector(parFrom, parUpper)), + m_step(parStep) + { + } + + template + auto sequence_range::cbegin() const -> const_iterator { + return sequence_range_iterator(*this); + } + + template + auto sequence_range::cend() const -> const_iterator { + return sequence_range_iterator(m_end, *this); + } + + template + using increasing_sequence_range = typename implem::make_sequence_helper< + typename V::vector_type, + std::plus, + std::less, + bt::number_range + >::type; +} //namespace vwr + +#if defined VWR_OUTER_NAMESPACE +} //namespace VWR_OUTER_NAMESPACE +#endif diff --git a/include/vectorwrapper/vector_ostream.hpp b/include/vectorwrapper/vector_ostream.hpp new file mode 100644 index 0000000..f09d6d5 --- /dev/null +++ b/include/vectorwrapper/vector_ostream.hpp @@ -0,0 +1,36 @@ +/* + * Copyright 2015-2017 Michele "King_DuckZ" Santullo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "vectorwrapper.hpp" +#include "size_type.hpp" +#include + +namespace vwr { + template + inline + std::ostream& operator<< (std::ostream& parStream, const Vec& parVec) { + static_assert(S > 0, "Invalid size"); + + parStream << '<'; + for (size_type z = 0; z < S - 1; ++z) { + parStream << parVec[z] << ','; + } + parStream << parVec[S - 1] << '>'; + return parStream; + } +} //namespace vwr diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 52abae0..8672eeb 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(${PROJECT_NAME} example.cpp test_get_at.cpp test_operators.cpp + test_sequence_range.cpp ) target_link_libraries(${PROJECT_NAME} diff --git a/test/unit/test_sequence_range.cpp b/test/unit/test_sequence_range.cpp new file mode 100644 index 0000000..652f63d --- /dev/null +++ b/test/unit/test_sequence_range.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2015-2017 Michele "King_DuckZ" Santullo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sample_vectors.hpp" +#include "vectorwrapper/sequence_range.hpp" +#include +#include +#include +#include + +TEST(vwr, vector_iterator) { + using namespace vwr; + + ivec3 from(100, 200, 300); + ivec3 to(102, 203, 304); + + std::array expected{ + ivec3(100, 200, 300), + ivec3(101, 200, 300), + ivec3(100, 201, 300), + ivec3(101, 201, 300), + ivec3(100, 202, 300), + ivec3(101, 202, 300), + ivec3(100, 200, 301), + ivec3(101, 200, 301), + ivec3(100, 201, 301), + ivec3(101, 201, 301), + ivec3(100, 202, 301), + ivec3(101, 202, 301), + ivec3(100, 200, 302), + ivec3(101, 200, 302), + ivec3(100, 201, 302), + ivec3(101, 201, 302), + ivec3(100, 202, 302), + ivec3(101, 202, 302), + ivec3(100, 200, 303), + ivec3(101, 200, 303), + ivec3(100, 201, 303), + ivec3(101, 201, 303), + ivec3(100, 202, 303), + ivec3(101, 202, 303) + }; + + { + auto seq = increasing_sequence_range(from, to); + auto it = seq.begin(); + auto it_end = seq.end(); + + std::vector results; + for (; it != it_end; ++it) { + results.push_back(*it); + } + + EXPECT_EQ(expected.size(), results.size()); + for (std::size_t z = 0; z < expected.size(); ++z) { + EXPECT_EQ(expected[z], results[z]); + } + } + + { + using dec_sequence_range = sequence_range, std::greater, 0, 1, 2>; + std::vector results; + for (auto& itm : dec_sequence_range(to, from)) { + results.push_back(itm); + } + + EXPECT_EQ(expected.size(), results.size()); + for (std::size_t z = 0; z < expected.size(); ++z) { + EXPECT_EQ(expected[z] + 1, results[results.size() - 1 - z]); + } + } + + { + auto seq = increasing_sequence_range(from, to, ivec3(3)); + std::vector results; + for (auto& itm : seq) { + results.push_back(itm); + } + + ASSERT_EQ(2, results.size()); + EXPECT_EQ(ivec3(100, 200, 300), results[0]); + EXPECT_EQ(ivec3(100, 200, 303), results[1]); + } +}