[mathicgb] 379/393: Moved Zip into Range.hpp and extended the functionality for manipulating ranges significantly. Added tests for that functionality and used it in Poly.
Doug Torrance
dtorrance-guest at moszumanska.debian.org
Fri Apr 3 15:59:37 UTC 2015
This is an automated email from the git hooks/post-receive script.
dtorrance-guest pushed a commit to branch upstream
in repository mathicgb.
commit 7bfa73459a60976ebded3eda585fa1c4e9168a08
Author: Bjarke Hammersholt Roune <bjarkehr.code at gmail.com>
Date: Fri Sep 20 20:52:03 2013 +0200
Moved Zip into Range.hpp and extended the functionality for manipulating ranges significantly. Added tests for that functionality and used it in Poly.
---
Makefile.am | 16 +-
build/vs12/mathicgb-lib/mathicgb-lib.vcxproj | 1 -
.../vs12/mathicgb-lib/mathicgb-lib.vcxproj.filters | 3 -
build/vs12/mathicgb-test/mathicgb-test.vcxproj | 1 +
.../mathicgb-test/mathicgb-test.vcxproj.filters | 3 +
src/mathicgb.cpp | 2 +-
src/mathicgb/MathicIO.hpp | 6 +-
src/mathicgb/Poly.cpp | 83 +++--
src/mathicgb/Poly.hpp | 34 +-
src/mathicgb/Range.hpp | 361 ++++++++++++++++++++-
src/mathicgb/Zip.hpp | 91 ------
src/test/Range.cpp | 166 ++++++++++
12 files changed, 614 insertions(+), 153 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 4d136c9..171d553 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,7 +13,7 @@ libmathicgb_la_LIBADD= $(DEPS_LIBS)
# the sources that are built to make libmathicgb. Listing the headers in
# sources ensure that those files are included in distributions.
-libmathicgb_la_SOURCES = src/mathicgb/Zip.hpp src/mathicgb/Range.hpp \
+libmathicgb_la_SOURCES = src/mathicgb/Range.hpp \
src/mathicgb/ReducerPack.hpp src/mathicgb/ReducerPack.cpp \
src/mathicgb/ClassicGBAlg.cpp src/mathicgb/ClassicGBAlg.hpp \
src/mathicgb/MonoLookup.hpp src/mathicgb/MonoLookup.cpp \
@@ -96,13 +96,13 @@ check_PROGRAMS=$(TESTS)
unittest_LDADD = $(DEPS_LIBS) $(top_builddir)/libmathicgb.la
test_LIBS=
-unittest_SOURCES= src/test/gtestInclude.cpp src/test/testMain.cpp \
- src/test/gb-test.cpp src/test/ideals.cpp src/test/poly-test.cpp \
- src/test/ideals.hpp src/test/SparseMatrix.cpp \
- src/test/QuadMatrixBuilder.cpp src/test/F4MatrixBuilder.cpp \
- src/test/F4MatrixReducer.cpp src/test/mathicgb.cpp \
- src/test/PrimeField.cpp src/test/MonoMonoid.cpp \
- src/test/Scanner.cpp src/test/MathicIO.cpp
+unittest_SOURCES=src/test/Range.cpp src/test/gtestInclude.cpp \
+ src/test/testMain.cpp src/test/gb-test.cpp src/test/ideals.cpp \
+ src/test/poly-test.cpp src/test/ideals.hpp src/test/SparseMatrix.cpp \
+ src/test/QuadMatrixBuilder.cpp src/test/F4MatrixBuilder.cpp \
+ src/test/F4MatrixReducer.cpp src/test/mathicgb.cpp \
+ src/test/PrimeField.cpp src/test/MonoMonoid.cpp src/test/Scanner.cpp \
+ src/test/MathicIO.cpp
else
diff --git a/build/vs12/mathicgb-lib/mathicgb-lib.vcxproj b/build/vs12/mathicgb-lib/mathicgb-lib.vcxproj
index 674e3ae..3e31c01 100755
--- a/build/vs12/mathicgb-lib/mathicgb-lib.vcxproj
+++ b/build/vs12/mathicgb-lib/mathicgb-lib.vcxproj
@@ -531,7 +531,6 @@
<ClInclude Include="..\..\..\src\mathicgb\stdinc.h" />
<ClInclude Include="..\..\..\src\mathicgb\TypicalReducer.hpp" />
<ClInclude Include="..\..\..\src\mathicgb\Unchar.hpp" />
- <ClInclude Include="..\..\..\src\mathicgb\Zip.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
diff --git a/build/vs12/mathicgb-lib/mathicgb-lib.vcxproj.filters b/build/vs12/mathicgb-lib/mathicgb-lib.vcxproj.filters
index ccd539c..82c3898 100755
--- a/build/vs12/mathicgb-lib/mathicgb-lib.vcxproj.filters
+++ b/build/vs12/mathicgb-lib/mathicgb-lib.vcxproj.filters
@@ -283,8 +283,5 @@
<ClInclude Include="..\..\..\src\mathicgb\Range.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\..\..\src\mathicgb\Zip.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
diff --git a/build/vs12/mathicgb-test/mathicgb-test.vcxproj b/build/vs12/mathicgb-test/mathicgb-test.vcxproj
index 1fa51fa..d5fbfb6 100755
--- a/build/vs12/mathicgb-test/mathicgb-test.vcxproj
+++ b/build/vs12/mathicgb-test/mathicgb-test.vcxproj
@@ -407,6 +407,7 @@
<ClCompile Include="..\..\..\src\test\poly-test.cpp" />
<ClCompile Include="..\..\..\src\test\PrimeField.cpp" />
<ClCompile Include="..\..\..\src\test\QuadMatrixBuilder.cpp" />
+ <ClCompile Include="..\..\..\src\test\Range.cpp" />
<ClCompile Include="..\..\..\src\test\Scanner.cpp" />
<ClCompile Include="..\..\..\src\test\SparseMatrix.cpp" />
<ClCompile Include="..\..\..\src\test\testMain.cpp" />
diff --git a/build/vs12/mathicgb-test/mathicgb-test.vcxproj.filters b/build/vs12/mathicgb-test/mathicgb-test.vcxproj.filters
index 1750b4f..6efa514 100755
--- a/build/vs12/mathicgb-test/mathicgb-test.vcxproj.filters
+++ b/build/vs12/mathicgb-test/mathicgb-test.vcxproj.filters
@@ -57,6 +57,9 @@
<ClCompile Include="..\..\..\src\test\Scanner.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\src\test\Range.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\test\ideals.hpp">
diff --git a/src/mathicgb.cpp b/src/mathicgb.cpp
index a9c78e3..1566885 100755
--- a/src/mathicgb.cpp
+++ b/src/mathicgb.cpp
@@ -734,7 +734,7 @@ void GroebnerInputIdealStream::appendPolynomialDone() {
// todo: give Poly a Poly&& constructor
auto poly = make_unique<Poly>(std::move(mPimpl->poly));
if (!poly->termsAreInDescendingOrder())
- poly->sortTermsDescending();
+ *poly = poly->polyWithTermsDescending();
mPimpl->basis.insert(std::move(poly));
mPimpl->poly.setToZero();
diff --git a/src/mathicgb/MathicIO.hpp b/src/mathicgb/MathicIO.hpp
index 4193e76..1ec1709 100755
--- a/src/mathicgb/MathicIO.hpp
+++ b/src/mathicgb/MathicIO.hpp
@@ -352,7 +352,7 @@ Basis MathicIO<M, BF>::readBasis(
Basis basis(ring);
for (size_t i = 0; i < polyCount; ++i) {
auto p = make_unique<Poly>(readPoly(ring, readComponent, in));
- p->sortTermsDescending();
+ *p = p->polyWithTermsDescending();
basis.insert(std::move(p));
}
return std::move(basis);
@@ -378,9 +378,7 @@ Poly MathicIO<M, BF>::readPoly(
const bool readComponent,
Scanner& in
) {
- auto p = readPolyDoNotOrder(ring, readComponent, in);
- p.sortTermsDescending();
- return p;
+ return readPolyDoNotOrder(ring, readComponent, in).polyWithTermsDescending();
}
template<class M, class BF>
diff --git a/src/mathicgb/Poly.cpp b/src/mathicgb/Poly.cpp
index 5791ff8..9352499 100755
--- a/src/mathicgb/Poly.cpp
+++ b/src/mathicgb/Poly.cpp
@@ -3,33 +3,58 @@
#include "stdinc.h"
#include "Poly.hpp"
-#include "Range.hpp"
-#include "Zip.hpp"
-#include <ostream>
-#include <iostream>
#include <algorithm>
MATHICGB_NAMESPACE_BEGIN
-void Poly::sortTermsDescending() {
- const size_t count = termCount();
- std::vector<size_t> ordered(count);
- for (size_t i = 0; i < count; ++i)
- ordered[i] = i;
-
- auto cmp = [&](size_t a, size_t b) {
- MATHICGB_ASSERT(a < termCount());
- MATHICGB_ASSERT(b < termCount());
- return monoid().lessThan(mono(b), mono(a));
+Poly Poly::polyWithTermsDescending() {
+ // *** Sort (term, index) pairs in descending order of monomial.
+ // Grr, if only C++11 lambda's allowed auto in the parameter list,
+ // then it would not have been necessary to ever mention the type
+ // Entry. But alas, that won't be here until C++14.
+ typedef std::pair<NewConstTerm, size_t> Entry;
+ auto greaterThan = [&](const Entry& a, const Entry& b) {
+ return monoid().lessThan(*b.first.mono, *a.first.mono);
};
- std::sort(ordered.begin(), ordered.end(), cmp);
+ auto ordered = rangeToVector(indexRange(*this));
+ std::sort(std::begin(ordered), std::end(ordered), greaterThan);
+ // *** Make a new polynomial with terms in that order
Poly poly(ring());
- for (size_t i = 0; i < count; ++i)
- poly.append(coef(ordered[i]), mono(ordered[i]));
- *this = std::move(poly);
+ poly.reserve(termCount());
+ for (const auto& p : ordered)
+ poly.append(p.first);
+
+ MATHICGB_ASSERT(poly.termsAreInDescendingOrder());
+ MATHICGB_ASSERT(termCount() == poly.termCount());
- MATHICGB_ASSERT(termsAreInDescendingOrder());
+ // This return statements causes no copy. The return value optimization
+ // will be used at the option of the compiler. If a crappy compiler gets
+ // that wrong, poly will be treated as an r-value, which is to say that
+ // this code becomes equivalent to return std::move(poly). That happens
+ // because poly is a local variable being returned, so the standard
+ // allows movement out of poly in this particular situation - that is
+ // safe/reasonable because the very next thing that will happen to poly is
+ // that it will get destructed, so anyone in a position to know that the
+ // contents of poly had been moved out would then also be using
+ // a pointer to the now-invalid poly object, which invokes undefined
+ // behavior anyway.
+ //
+ // Capturing the returned Poly into another poly also will not cause a copy.
+ // Consider the code
+ //
+ // Poly p1(q.polyWithTermsDescending());
+ // Poly p2(ring);
+ // p2 = q.polyWithTermsDescending();
+ //
+ // The return value is an unnamed temporary that is constructed/assigned
+ // into p1/p2. Unnamed temporaries are r-values, so the guts of those
+ // temporary objects will be moved into p1/p2. There will be no copy.
+ //
+ // (Of course there is the one copy that we are doing further up into poly
+ // to avoid doing an in-place sort. The point is that there are no further
+ // copies than that one.)
+ return poly;
}
void Poly::makeMonic() {
@@ -61,22 +86,16 @@ bool operator==(const Poly& a, const Poly& b) {
}
size_t Poly::getMemoryUse() const {
- size_t total = sizeof(const PolyRing *);
- total += sizeof(coefficient) * mCoefs.capacity();
- total += sizeof(int) * mMonos.capacity();
- return total;
+ return
+ sizeof(mCoefs.front()) * mCoefs.capacity() +
+ sizeof(mMonos.front()) * mMonos.capacity();
}
bool Poly::termsAreInDescendingOrder() const {
- if (!isZero()) {
- auto prev = leadMono().ptr();
- for (const auto& mono : range(++monoBegin(), monoEnd())) {
- if (monoid().lessThan(*prev, mono))
- return false;
- prev = mono.ptr();
- }
- }
- return true;
+ auto greaterThanOrEqual = [&](ConstMonoRef a, ConstMonoRef b) {
+ return !monoid().lessThan(a, b);
+ };
+ return std::is_sorted(monoBegin(), monoEnd(), greaterThanOrEqual);
}
MATHICGB_NAMESPACE_END
diff --git a/src/mathicgb/Poly.hpp b/src/mathicgb/Poly.hpp
index 942ccf2..26d77d8 100755
--- a/src/mathicgb/Poly.hpp
+++ b/src/mathicgb/Poly.hpp
@@ -5,7 +5,6 @@
#include "PolyRing.hpp"
#include "Range.hpp"
-#include "Zip.hpp"
#include <vector>
#include <ostream>
#include <utility>
@@ -41,10 +40,24 @@ public:
size_t termCount() const {return mCoefs.size();}
size_t getMemoryUse() const;
- /// Orders terms in descending order.
- void sortTermsDescending();
+ /// Returns a polynomial whose terms have been permuted to be in
+ /// descending order.
+ ///
+ /// Making the copy is not wasteful, because doing the permutation in-place
+ /// would be require swapping monomials which is slow if they are large.
+ /// The returned object is not copy (return value optimization) and using
+ /// move assignment this code will only create the single copy of a Poly
+ /// p that is necessary to avoid an in-place operation:
+ ///
+ /// p = p.polyWithTermsDescending()
+ Poly polyWithTermsDescending();
/// Appends the given term as the last term in the polynomial.
+ void append(const NewConstTerm& term) {
+ MATHICGB_ASSERT(term.mono != nullptr);
+ append(term.coef, *term.mono);
+ }
+
void append(coefficient coef, ConstMonoRef mono);
/// Hint that space for the give number of terms is going to be needed.
@@ -82,7 +95,7 @@ public:
/// Returns the coefficient of the leading term.
coefficient leadCoef() const {
MATHICGB_ASSERT(!isZero());
- return coef(0);
+ return mCoefs.front();
}
/// Returns true if the polynomial is monic. A polynomial is monic if
@@ -116,13 +129,13 @@ public:
/// Returns the monomial of the leading term.
ConstMonoRef leadMono() const {
MATHICGB_ASSERT(!isZero());
- return mono(0);
+ return Monoid::toRef(&mMonos.front());
}
/// Returns the monomial of the last term.
ConstMonoRef backMono() const {
MATHICGB_ASSERT(!isZero());
- return mono(termCount() - 1);
+ return Monoid::toRef(&mMonos[(termCount() - 1) * monoid().entryCount()]);
}
/// Returns true if the terms are in descending order. The terms are in
@@ -133,6 +146,7 @@ public:
public:
typedef std::forward_iterator_tag iterator_category;
typedef ConstMonoRef value_type;
+ typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef ConstMonoRef reference;
@@ -182,6 +196,9 @@ public:
public:
typedef std::forward_iterator_tag iterator_category;
typedef NewConstTerm value_type;
+ typedef ptrdiff_t difference_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
ConstTermIterator() {}
@@ -210,6 +227,11 @@ public:
Iterator mIt;
};
+ NewConstTerm term(size_t index) const {
+ NewConstTerm t = {coef(index), mono(index).ptr()};
+ return t;
+ }
+
typedef Range<ConstTermIterator> ConstTermIteratorRange;
ConstTermIterator begin() const {return makeZip(coefBegin(), monoBegin());}
diff --git a/src/mathicgb/Range.hpp b/src/mathicgb/Range.hpp
index 39d6c83..35b95c3 100644
--- a/src/mathicgb/Range.hpp
+++ b/src/mathicgb/Range.hpp
@@ -1,12 +1,18 @@
-// MathicGB copyright 2012 all rights reserved. MathicGB comes with ABSOLUTELY
+// MathicGB copyright 2013 all rights reserved. MathicGB comes with ABSOLUTELY
// NO WARRANTY and is licensed as GPL v2.0 or later - see LICENSE.txt.
#ifndef MATHICGB_RANGE_GUARD
#define MATHICGB_RANGE_GUARD
+#include <limits>
+#include <vector>
#include <utility>
+#include <type_traits>
MATHICGB_NAMESPACE_BEGIN
+// Read on, this file contains several free-standing functions that make use
+// of the range concept and Range class.
+
/// An object that combines two iterators into a range suitable for use with
/// C++11's range for. It is most conveniently used with the function range.
/// For example:
@@ -18,27 +24,368 @@ MATHICGB_NAMESPACE_BEGIN
/// here. range() is useful when a class does not have begin or end members, or
/// if it offers several different ranges that can be iterated through -
/// then the default range can only offer one of those ranges.
-template<class Iterator>
+template<class Iter>
class Range {
public:
+ typedef Iter Iterator;
+
Range(Iterator begin, Iterator end): mBegin(begin), mEnd(end) {}
Range(std::pair<Iterator, Iterator> pair):
- mBegin(pair.first), mEnd(pair.second) {}
+ mBegin(pair.first), mEnd(pair.second)
+ {}
- Iterator begin() {return mBegin;}
- Iterator end() {return mBegin;}
+ Iterator begin() const {return mBegin;}
+ Iterator end() const {return mEnd;}
private:
Iterator mBegin;
Iterator mEnd;
};
-/// Convenience function for constructing a Range object.
+/// Convenience function for constructing a Range object. This function
+/// removes the need to explicitly specify the types involved.
template<class Iterator>
Range<Iterator> range(Iterator begin, Iterator end) {
return Range<Iterator>(begin, end);
}
-MATHICGB_NAMESPACE_END
+/// As range(std::begin(r), std::end(r)).
+template<class RangeParam>
+auto range(RangeParam&& r) -> decltype(range(std::begin(r), std::end(r))) {
+ return range(std::begin(r), std::end(r));
+}
+
+
+// *** Zipping ranges together
+
+/// Zip ties two iterators together into a single iterator. The point
+/// is to enable the zip() function defined further down in this header.
+///
+/// Note that equality is defined ONLY by equality of the first iterator!
+template<class Iterator1, class Iterator2>
+class Zip {
+public:
+ typedef decltype(*std::declval<Iterator1>()) ValueType1;
+ typedef decltype(*std::declval<Iterator2>()) ValueType2;
+
+ /// It would be possible to do something fancy to infer the strongest
+ /// iterator category that both sub-iterators can support. Since this
+ /// is intended mainly to be used for things like range-for, I did not
+ /// implement that since I do not need it.
+ typedef std::forward_iterator_tag iterator_category;
+ typedef std::pair<ValueType1, ValueType2> value_type;
+ typedef ptrdiff_t difference_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+
+ Zip() {}
+ Zip(Iterator1 it1, Iterator2 it2): mIt1(it1), mIt2(it2) {}
+
+ Zip& operator++() {
+ ++mIt1;
+ ++mIt2;
+ return *this;
+ }
+
+ value_type operator*() const {
+ // We cannot use std::make_pair here. If .first or .second is a
+ // reference, then std::make_pair will remove the reference and then the
+ // conversion to value_type will add the reference back in - but now the
+ // reference is not what was originally referenced, instead it is a
+ // reference to the temporary object returned by std::make_pair, which
+ // then promptly goes out of scope, leading to undefined behavior.
+ return value_type(*mIt1, *mIt2);
+ }
+
+ /// Compares only the first iterator!
+ bool operator!=(const Zip& it) const {return mIt1 != it.mIt1;}
+
+ /// Compares only the first iterator!
+ bool operator==(const Zip& it) const {return mIt1 == it.mIt1;}
+
+private:
+ Iterator1 mIt1;
+ Iterator2 mIt2;
+};
+
+/// Creates a Zip iterator out of it1 and it2. This is a convenience function
+/// that removes the need to specify the iterator types explicitly.
+///
+/// There would rarely (never?) be a need to call this function directly.
+/// Prefer to call zip() directly.
+template<class Iterator1, class Iterator2>
+auto makeZip(
+ const Iterator1& it1,
+ const Iterator2& it2
+) -> Zip<Iterator1, Iterator2> {
+ return Zip<Iterator1, Iterator2>(it1, it2);
+}
+
+/// As zip(range(begin1, end1), range(begin2, end2)).
+template<class Iterator1, class Iterator2>
+auto zip(
+ const Iterator1& begin1,
+ const Iterator1& end1,
+ const Iterator2& begin2,
+ const Iterator2& end2
+) -> decltype(range(makeZip(begin1, begin2), makeZip(end1, end2))) {
+ return range(makeZip(begin1, begin2), makeZip(end1, end2));
+}
+
+/// Zips two ranges into a single range of pairs.
+///
+/// Example:
+/// std::vector<std::string> a = {"hello", "world"};
+/// std::vector<int> b = {4, 2, 1, 0};
+/// for (const auto& p : zip(a, b))
+/// std::cout << p.first << p.second << ' ';
+///
+/// The output will be "hello4 world2 ". If the ranges have different lengths
+/// then the length of the range will be the length of the first range. If the
+/// first range is shorter than the second one, this means you will likely
+/// run into undefined behavior. We can't even do an assert for that because
+/// it's perfectly reasonable to use giant implicitly represented ranges for
+/// the second parameter and if that's a forward iterator, doing std::distance
+/// on it might never terminate or take a very long time.
+template<class Range1, class Range2>
+auto zip(Range1&& range1, Range2&& range2) ->
+ decltype(
+ zip(
+ std::begin(range1),
+ std::end(range1),
+ std::begin(range2),
+ std::end(range2)
+ )
+ )
+{
+ return zip(
+ std::begin(range1),
+ std::end(range1),
+ std::begin(range2),
+ std::end(range2)
+ );
+}
+
+
+// *** Implicitly represented ranges of integers
+
+/// An Iterator that wraps an integer.
+template<class Integer>
+class IntIterator {
+public:
+ /// It could perfectly well be a random access iterator, I just do not
+ /// need that functionality right now, so I'm not implementing it.
+ typedef std::forward_iterator_tag iterator_category;
+ typedef Integer value_type;
+ typedef ptrdiff_t difference_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+
+ IntIterator(Integer integer): mInteger(integer) {}
+
+ value_type operator*() const {return mInteger;}
+
+ IntIterator& operator++() {
+ ++mInteger;
+ return *this;
+ }
+
+ bool operator==(const IntIterator& it) const {return **this == *it;}
+ bool operator!=(const IntIterator& it) const {return **this != *it;}
+
+private:
+ Integer mInteger;
+};
+
+/// Constructs a half-open interval of integers [begin, end) that is
+/// compatible with range-for.
+///
+/// Example:
+/// for (auto x : intRange(5, 10)
+/// std::cout << ' ' << x;
+/// The output will be " 5 6 7 8 9". See indexRange() for further examples
+/// of how this sort of thing can be useful.
+template<class Integer>
+Range<IntIterator<Integer>> intRange(Integer begin, Integer end) {
+ return Range<IntIterator<Integer>>(begin, end);
+}
+
+/// As intRange(Integer(0), end).
+template<class Integer>
+auto intRange(Integer end) -> decltype(intRange<Integer>(0, 0)) {
+ return intRange(Integer(0), end);
+}
+
+/// As intRange<size_t>(0, max) where max is the maximum representable
+/// integer of type Integer as indicated by std::numeric_limits.
+inline auto intRange() -> decltype(intRange<size_t>(0, 0)) {
+ return intRange<size_t>(0, std::numeric_limits<size_t>::max());
+}
+/// Adds indices to the elements of the range, specifying the offset of each
+/// element in the range. Equivalent to zip(range(begin, end), intRange()).
+///
+/// Example:
+/// const char*[] strs = {"hello", "world", "!"};
+/// for (const auto& p : indexRange(strs))
+/// std::cout << ' ' << p.first << p.second;
+///
+/// The output will be " hello0 world1 !2".
+template<class Iterator>
+auto indexRange(
+ const Iterator& begin,
+ const Iterator& end
+) -> decltype(zip(range(begin, end), intRange())) {
+ return zip(range(begin, end), intRange());
+}
+
+/// As indexRange(std::begin(r), std::end(r)).
+template<class Range>
+auto indexRange(
+ Range&& r
+) -> decltype(indexRange(std::begin(r), std::end(r))) {
+ return indexRange(std::begin(r), std::end(r));
+}
+
+
+// *** Conversion from range to container
+
+/// As rangeToVector(range(begin, end)).
+template<class Iterator>
+auto rangeToVector(
+ Iterator begin,
+ Iterator end
+) -> decltype(
+ std::vector<typename std::decay<decltype(*begin)>::type>(begin, end)
+) {
+ return std::vector<typename std::decay<decltype(*begin)>::type>(begin, end);
+}
+
+/// Converts a range into a vector. This is a convenience function.
+/// When using this function, it is not necessary to name the type of the
+/// vector. It is also not necessary to give a name to the range object.
+///
+/// Example:
+/// const char*[] strs = {"hello", "world", "!"};
+/// auto v = rangeToVector(indexRange(strs));
+///
+/// Without rangeToVector, the last line would have to be replaced by
+///
+/// auto r = indexRange(strs);
+/// std::vector<std::pair<const char*, size_t>> v(r.begin(), r.end());
+///
+/// The return type is std::vector<T> where T is whatever type is contained
+/// in the range, after stripping references and other qualifiers. So if
+/// the iterators in the range return const T&, the return type will still
+/// be std::vector<T>. More precisely, if the returned type is S, the returned
+/// type will be std::vector<std::decay<S>::type>.
+template<class RangeParam>
+auto rangeToVector(
+ RangeParam&& range
+) -> decltype(rangeToVector(std::begin(range), std::end(range))) {
+ return rangeToVector(std::begin(range), std::end(range));
+}
+
+
+// *** Range of adjacent pairs
+
+/// Wraps an iterator of pairs to be an iterator of opposite pairs, so that
+/// if *it == (a, b) then *OppositePairIterator(it) == (b, a).
+template<class Iterator>
+class OppositePairIterator {
+public:
+ typedef decltype((*std::declval<Iterator>()).first) InnerValueType1;
+ typedef decltype((*std::declval<Iterator>()).second) InnerValueType2;
+
+ typedef typename Iterator::iterator_category iterator_category;
+ typedef std::pair<InnerValueType2, InnerValueType1> value_type;
+ typedef typename Iterator::difference_type difference_type;
+ typedef typename Iterator::pointer pointer;
+ typedef typename Iterator::reference reference;
+
+ OppositePairIterator() {}
+ OppositePairIterator(const Iterator& it): mIt(it) {}
+
+ OppositePairIterator& operator++() {
+ ++mIt;
+ return *this;
+ }
+
+ value_type operator*() const {
+ return value_type((*mIt).second, (*mIt).first);
+ }
+
+ bool operator!=(const OppositePairIterator& it) const {return mIt != it.mIt;}
+ bool operator==(const OppositePairIterator& it) const {return mIt == it.mIt;}
+
+private:
+ Iterator mIt;
+};
+
+/// As oppositePairRange(range(begin, end)).
+template<class Iterator>
+Range<OppositePairIterator<Iterator>> oppositePairRange(
+ const Iterator& begin,
+ const Iterator& end
+) {
+ return range<OppositePairIterator<Iterator>>(begin, end);
+}
+
+/// Swaps the elements of the pairs in a range of pairs.
+///
+/// Example:
+/// auto print = [](std::pair<int, int> p) {
+/// std::cout << '(' << p.first << ',' << p.second << ')';
+/// auto r = zip(intRange(1, 3), intRange(101, 103));
+/// for (auto p : r)
+/// print(p);
+/// std::cout << '\n';
+/// for (auto p : oppositePairRange(r))
+/// print(p);
+///
+/// The output will be
+///
+/// (1,101)(2,102)(3,103)
+/// (101,1)(102,2)(103,3)
+template<class RangeParam>
+auto oppositePairRange(
+ RangeParam&& r
+) -> decltype(oppositePairRange(std::begin(r), std::end(r))) {
+ return oppositePairRange(std::begin(r), std::end(r));
+}
+
+
+/// As adjPairRange(range(begin, end)).
+template<class Iterator>
+auto adjPairRange(
+ const Iterator& begin,
+ const Iterator& end
+) -> decltype(oppositePairRange(zip(end, end, end, end))) {
+ if (begin == end)
+ return oppositePairRange(zip(end, end, end, end));
+ // This is a bit tricky. What we really want is
+ // zip(begin, end - 1, begin + 1, end)
+ // The -1 is bad because we want this to work for forward iterators
+ // that do not have operator--(). We cannot just leave out the -1 because
+ // zip defines the length of the range according to the first component
+ // range. However, if we swap the ranges around to
+ // zip(begin + 1, end, begin, end - 1)
+ // then we can replace this by
+ // zip(begin + 1, end, begin, end)
+ // because zip does not actually care about the length of the second range.
+ // The problem now is that the pairs of elements will be the wrong way
+ // around, but oppositePairRange solves that nicely.
+ auto pastBegin = begin;
+ ++pastBegin;
+ return oppositePairRange(zip(pastBegin, end, begin, end));
+}
+
+template<class RangeParam>
+auto adjPairRange(
+ RangeParam&& r
+) -> decltype(adjPairRange(std::begin(r), std::end(r))) {
+ return adjPairRange(std::begin(r), std::end(r));
+}
+
+MATHICGB_NAMESPACE_END
#endif
diff --git a/src/mathicgb/Zip.hpp b/src/mathicgb/Zip.hpp
deleted file mode 100644
index b51d650..0000000
--- a/src/mathicgb/Zip.hpp
+++ /dev/null
@@ -1,91 +0,0 @@
-// MathicGB copyright 2012 all rights reserved. MathicGB comes with ABSOLUTELY
-// NO WARRANTY and is licensed as GPL v2.0 or later - see LICENSE.txt.
-#ifndef MATHICGB_ZIP_GUARD
-#define MATHICGB_ZIP_GUARD
-
-#include "Range.hpp"
-
-MATHICGB_NAMESPACE_BEGIN
-
-/// Zip ties two iterators together into a single iterator. The point
-/// is to enable the zip() function defined further down in this header.
-template<class Iterator1, class Iterator2>
-class Zip {
-public:
- typedef decltype(*std::declval<Iterator1>()) Value1;
- typedef decltype(*std::declval<Iterator2>()) Value2;
- typedef std::pair<Value1, Value2> value_type;
-
- Zip() {}
- Zip(Iterator1 it1, Iterator2 it2): mIt1(it1), mIt2(it2) {}
-
- Zip& operator++() {
- ++mIt1;
- ++mIt2;
- return *this;
- }
-
- value_type operator*() const {
- // We cannot use std::make_pair here. If .first or .second is a reference,
- // then std::make_pair will remove the reference and then the conversion
- // to value_type will add the reference back in - but now the reference
- // is not what was originally referenced, instead it is a reference to
- // the temporary object returned by std::make_pair, which then promptly
- // goes out of scope, leading to undefined behavior.
- return value_type(*mIt1, *mIt2);
- }
-
- bool operator!=(const Zip& it) const {
- MATHICGB_ASSERT((mIt1 != it.mIt1) == (mIt2 != it.mIt2));
- return mIt1 != it.mIt1;
- }
-
- bool operator==(const Zip& it) const {
- MATHICGB_ASSERT((mIt1 == it.mIt1) == (mIt2 == it.mIt2));
- return mIt1 == it.mIt1;
- }
-
-private:
- Iterator1 mIt1;
- Iterator2 mIt2;
-};
-
-/// Creates a Zip iterator out of it1 and it2. This is a convenience function
-/// that avoids the need to specify the iterator types explicitly.
-template<class Iterator1, class Iterator2>
-auto makeZip(
- Iterator1&& it1,
- Iterator2&& it2
-) -> Zip<Iterator1, Iterator2> {
- return Zip<Iterator1, Iterator2>(
- std::forward<Iterator1>(it1),
- std::forward<Iterator2>(it2)
- );
-}
-
-/// Zips two ranges into a single zipped range. Example:
-///
-/// std::vector<string> a = {"hello", "world"};
-/// std::vector<int> b = {4, 2};
-/// for (const auto& p : zip(a, b))
-/// std::cout << p.first << p.second << ' ';
-///
-/// The output will be "hello4 world2 ".
-template<class Range1, class Range2>
-auto zip(Range1&& range1, Range2&& range2) ->
- decltype(
- range(
- makeZip(std::begin(range1), std::begin(range2)),
- makeZip(std::end(range1), std::end(range2))
- )
- )
-{
- return range(
- makeZip(std::begin(range1), std::begin(range2)),
- makeZip(std::end(range1), std::end(range2))
- );
-}
-
-MATHICGB_NAMESPACE_END
-
-#endif
diff --git a/src/test/Range.cpp b/src/test/Range.cpp
new file mode 100644
index 0000000..49a43b7
--- /dev/null
+++ b/src/test/Range.cpp
@@ -0,0 +1,166 @@
+#include <utility>
+#include <algorithm>
+#include <iterator>
+
+// MathicGB copyright 2012 all rights reserved. MathicGB comes with ABSOLUTELY
+// NO WARRANTY and is licensed as GPL v2.0 or later - see LICENSE.txt.
+#include "mathicgb/stdinc.h"
+#include "mathicgb/Range.hpp"
+
+#include <gtest/gtest.h>
+#include <iterator>
+#include <vector>
+#include <list>
+
+using namespace mgb;
+
+namespace {
+ template<class Range1, class Range2>
+ void checkRange(const Range1& r1, const Range2& r2) {
+ const auto d1 = std::distance(std::begin(r1), std::end(r1));
+ const auto d2 = std::distance(std::begin(r2), std::end(r2));
+ ASSERT_EQ(d1, d2);
+ ASSERT_TRUE(std::equal(std::begin(r1), std::end(r1), std::begin(r2)));
+ }
+}
+
+TEST(Range, Simple) {
+ int intArray[] = {1, 5, 3, 7};
+ const std::vector<int> intVector(std::begin(intArray), std::end(intArray));
+ const std::list<int> intList(std::begin(intArray), std::end(intArray));
+ std::vector<int> v;
+
+ auto checkClear = [&]() {
+ checkRange(intVector, v);
+ v.clear();
+ };
+
+ // Let's try the pattern without using range just to make sure everything
+ // is OK.
+ for (const auto& i : intArray)
+ v.push_back(i);
+ checkClear();
+
+ // *** check using intArray.
+ for (const auto& i : range(intArray))
+ v.push_back(i);
+ checkClear();
+
+ for (const auto& i : range(std::begin(intArray), std::end(intArray)))
+ v.push_back(i);
+ checkClear();
+
+ // *** check using intVector
+ for (const auto& i : range(intVector))
+ v.push_back(i);
+ checkClear();
+
+ for (const auto& i : range(std::begin(intVector), std::end(intVector)))
+ v.push_back(i);
+ checkClear();
+
+ // *** check using intList
+ for (const auto& i : range(intList))
+ v.push_back(i);
+ checkClear();
+
+ for (const auto& i : range(std::begin(intList), std::end(intList)))
+ v.push_back(i);
+ checkClear();
+
+ // *** check using iterated range
+ for (const auto& i : range(range(std::begin(intArray), std::end(intArray))))
+ v.push_back(i);
+ checkClear();
+
+ for (const auto& i : range(range(intVector)))
+ v.push_back(i);
+ checkClear();
+
+ for (const auto& i : range(range(range(range(intList)))))
+ v.push_back(i);
+ checkClear();
+}
+
+TEST(Range, rangeToVector) {
+ int intArray[] = {1, 5, 3, 7};
+ const std::vector<int> intVector(std::begin(intArray), std::end(intArray));
+ ASSERT_EQ(intVector, rangeToVector(std::begin(intArray), std::end(intArray)));
+ ASSERT_EQ(intVector, rangeToVector(intArray));
+ ASSERT_EQ(intVector, rangeToVector(range(intArray)));
+}
+
+TEST(Range, zip) {
+ const std::string a[] = {"hello", "world"};
+ const int b[] = {4, 2, 1, 0};
+ std::ostringstream out;
+
+ // Put a range() around b just to test something other than a built-in
+ for (const auto& p : zip(a, range(b)))
+ out << p.first << p.second << ' ';
+ ASSERT_EQ("hello4 world2 ", out.str());
+
+ // Now try the version on iterators and put the range around a instead.
+ out.str("");
+ for (
+ const auto& p :
+ zip(std::begin(range(a)), std::end(range(a)), std::begin(b), std::end(b))
+ )
+ out << p.first << p.second << ' ';
+ ASSERT_EQ("hello4 world2 ", out.str());
+}
+
+TEST(Range, intRange) {
+ const int int05[] = {0, 1, 2, 3, 4};
+ ASSERT_EQ(rangeToVector(int05), rangeToVector(intRange(0, 5)));
+ ASSERT_EQ(rangeToVector(int05), rangeToVector(intRange(5)));
+
+ // gcc 4.7.3 won't parse "const signed char" here, but it will parse
+ // it like this with the typedef.
+ typedef signed char C;
+ const C scharm2p5[] = {-2, -1, 0, 1, 2, 3, 4};
+ ASSERT_EQ(
+ rangeToVector(range(scharm2p5)),
+ rangeToVector(intRange(C(-2), C(5)))
+ );
+
+ // Normally we should not dereference an end() iterator, but for the case
+ // of intRange() it is OK.
+ ASSERT_EQ(std::numeric_limits<size_t>::max(), *intRange().end());
+ ASSERT_EQ(size_t(0), *intRange().begin());
+}
+
+TEST(Range, indexRange) {
+ typedef std::pair<short, size_t> Pair;
+ Pair indexed[] = {Pair(-2, 0), Pair(-1, 1), Pair(0, 2)};
+ ASSERT_EQ(
+ rangeToVector(indexed),
+ rangeToVector(indexRange(intRange<short>(-2, 1)))
+ );
+}
+
+
+TEST(Range, oppositePairRange) {
+ std::string strs[] = {"hello", "world", "!"};
+ const auto r = zip(strs, intRange(10));
+ const auto opR = zip(intRange(3), strs);
+ ASSERT_EQ(rangeToVector(r), rangeToVector(oppositePairRange(opR)));
+ ASSERT_EQ(
+ rangeToVector(opR),
+ rangeToVector(oppositePairRange(std::begin(r), std::end(r)))
+ );
+}
+
+TEST(Range, adjPairRange) {
+ typedef std::pair<int, int> Pair;
+
+ std::vector<Pair> none;
+ ASSERT_EQ(rangeToVector(none), rangeToVector(adjPairRange(intRange(0))));
+ ASSERT_EQ(rangeToVector(none), rangeToVector(adjPairRange(intRange(1))));
+
+ Pair adj2[] = {Pair(0, 1)};
+ ASSERT_EQ(rangeToVector(adj2), rangeToVector(adjPairRange(intRange(2))));
+
+ Pair adj4[] = {Pair(0, 1), Pair(1, 2), Pair(2, 3)};
+ ASSERT_EQ(rangeToVector(adj4), rangeToVector(adjPairRange(intRange(4))));
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/mathicgb.git
More information about the debian-science-commits
mailing list