[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