Galois
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
TwoLevelIteratorA.h
Go to the documentation of this file.
1 /*
2  * This file belongs to the Galois project, a C++ library for exploiting
3  * parallelism. The code is being released under the terms of the 3-Clause BSD
4  * License (a copy is located in LICENSE.txt at the top-level directory).
5  *
6  * Copyright (C) 2018, The University of Texas at Austin. All rights reserved.
7  * UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS
8  * SOFTWARE AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY,
9  * FITNESS FOR ANY PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF
10  * PERFORMANCE, AND ANY WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF
11  * DEALING OR USAGE OF TRADE. NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH
12  * RESPECT TO THE USE OF THE SOFTWARE OR DOCUMENTATION. Under no circumstances
13  * shall University be liable for incidental, special, indirect, direct or
14  * consequential damages or loss of profits, interruption of business, or
15  * related expenses which may arise from use of Software or Documentation,
16  * including but not limited to those resulting from defects in Software and/or
17  * Documentation, or loss or inaccuracy of data of any kind.
18  */
19 
20 #ifndef GALOIS_TWOLEVELITERATORA_H
21 #define GALOIS_TWOLEVELITERATORA_H
22 
23 #include <cassert>
24 #include <iterator>
25 #include <type_traits>
26 #include <utility>
27 
28 #include <boost/iterator/iterator_adaptor.hpp>
29 
30 #include "galois/config.h"
31 #include "galois/gIO.h"
32 
33 namespace galois {
34 
38 template <class OuterIter, class InnerIter, class CategoryOrTraversal,
39  class InnerBeginFn, class InnerEndFn>
41  : public boost::iterator_adaptor<
42  TwoLevelIteratorA<OuterIter, InnerIter, CategoryOrTraversal,
43  InnerBeginFn, InnerEndFn>,
44  InnerIter, boost::use_default, CategoryOrTraversal> {
45 public:
46  typedef typename TwoLevelIteratorA::iterator_adaptor_::difference_type
48 
49 private:
50  OuterIter m_outer_begin; // TODO could skip this field when modeling a forward
51  // iterator
52  OuterIter m_outer_end;
53  OuterIter m_outer;
54  InnerBeginFn m_inner_begin_fn;
55  InnerEndFn m_inner_end_fn;
56 
57 #if __cplusplus >= 201103L
58  static_assert(
59  std::is_convertible<typename std::result_of<InnerBeginFn(
60  decltype(*std::declval<OuterIter>()))>::type,
61  InnerIter>::value,
62  "Result of InnerBeginFn(*OuterIter) should be convertable to InnerIter");
63  static_assert(
64  std::is_convertible<typename std::result_of<InnerEndFn(
65  decltype(*std::declval<OuterIter>()))>::type,
66  InnerIter>::value,
67  "Result of InnerEndFn(*OuterIter) should be convertable to InnerIter");
68 #endif
69 
71 
76  void seek_forward() {
77  if (this->base_reference() != m_inner_end_fn(*m_outer))
78  return;
79 
80  ++m_outer;
81 
82  for (; m_outer != m_outer_end; ++m_outer) {
83  this->base_reference() = m_inner_begin_fn(*m_outer);
84 
85  if (this->base_reference() != m_inner_end_fn(*m_outer))
86  break;
87  }
88  }
89 
90  template <class Iter>
91  void safe_decrement_dispatch(std::forward_iterator_tag, Iter& it,
92  Iter begin) {
93  Iter prev = begin;
94 
95  for (; begin != it; ++begin)
96  prev = begin;
97  }
98 
99  template <class Iter>
100  void safe_decrement_dispatch(std::bidirectional_iterator_tag, Iter& it,
101  const Iter&) {
102  --it;
103  }
104 
106  template <class Iter>
107  bool safe_decrement(Iter& it, const Iter& begin) {
108  if (it == begin)
109  return true;
110  safe_decrement_dispatch(
111  typename std::iterator_traits<Iter>::iterator_category(), it, begin);
112  return false;
113  }
114 
115  template <class Iter>
116  typename std::iterator_traits<Iter>::difference_type
117  safe_difference_dispatch(Iter it1, Iter it2, Iter end,
118  std::input_iterator_tag) const {
119  if (it1 == it2)
120  return 0;
121 
122  Iter it1_orig(it1);
123  Iter it2_orig(it2);
124 
125  typename std::iterator_traits<Iter>::difference_type count1 = 0;
126  typename std::iterator_traits<Iter>::difference_type count2 = 0;
127 
128  while (true) {
129  if (it1 != end) {
130  ++count1;
131  if (++it1 == it2_orig)
132  return count1;
133  }
134  if (it2 != end) {
135  ++count2;
136  if (++it2 == it1_orig)
137  return -count2;
138  }
139  }
140  }
141 
142  template <class Iter>
143  typename std::iterator_traits<Iter>::difference_type
144  safe_difference_dispatch(Iter it1, Iter it2, Iter,
145  std::random_access_iterator_tag) const {
146  return std::distance(it1, it2);
147  }
148 
153  template <class Iter>
154  typename std::iterator_traits<Iter>::difference_type
155  safe_distance(Iter it1, Iter it2, Iter end) const {
156  return safe_difference_dispatch(
157  it1, it2, end,
158  typename std::iterator_traits<Iter>::iterator_category());
159  }
160 
165  void seek_backward() {
166  InnerIter end;
167 
168  for (end = m_inner_end_fn(*m_outer); m_inner_begin_fn(*m_outer) == end;) {
169  bool too_far __attribute__((unused)) =
170  safe_decrement(m_outer, m_outer_begin);
171  assert(!too_far);
172  end = m_inner_end_fn(*m_outer);
173  }
174 
175  this->base_reference() = end;
176  }
177 
178  void increment() {
179  ++this->base_reference();
180  seek_forward();
181  }
182 
183  void decrement() {
184  if (m_outer == m_outer_end) {
185  bool too_far __attribute__((unused)) =
186  safe_decrement(m_outer, m_outer_begin);
187  assert(!too_far);
188  seek_backward();
189  } else if (!safe_decrement(this->base_reference(),
190  m_inner_begin_fn(*m_outer))) {
191  // Common case
192  return;
193  } else {
194  // Reached end of inner range
195  bool too_far __attribute__((unused)) =
196  safe_decrement(m_outer, m_outer_begin);
197  assert(!too_far);
198  seek_backward();
199  }
200 
201  bool too_far __attribute__((unused)) =
202  safe_decrement(this->base_reference(), m_inner_begin_fn(*m_outer));
203  assert(!too_far);
204  }
205 
206  template <class DiffType = difference_type>
207  void advance_dispatch(DiffType n, std::input_iterator_tag) {
208  if (n < 0) {
209  for (; n; ++n)
210  decrement();
211  } else if (n > 0) {
212  for (; n; --n)
213  increment();
214  }
215  }
216 
217  template <class DiffType = difference_type>
218  void jump_forward(DiffType n) {
219  assert(n >= 0);
220  while (n) {
221  difference_type k =
222  std::distance(this->base_reference(), m_inner_end_fn(*m_outer));
223  difference_type m = std::min(k, n);
224  n -= m;
225  std::advance(this->base_reference(), m);
226  if (m == k)
227  seek_forward();
228  }
229  }
230 
231  template <class DiffType = difference_type>
232  void jump_backward(DiffType n) {
233  // Note: not the same as jump_forward due to difference between beginning
234  // and end of ranges
235  assert(n >= 0);
236  if (n && m_outer == m_outer_end) {
237  decrement();
238  --n;
239  }
240 
241  while (n) {
242  difference_type k =
243  std::distance(m_inner_begin_fn(*m_outer), this->base_reference()) + 1;
244  if (k == 1) {
245  decrement();
246  --n;
247  } else if (k < n) {
248  seek_backward();
249  n -= k;
250  } else {
251  std::advance(this->base_reference(), -n);
252  n = 0;
253  }
254  }
255  }
256 
257  template <class DiffType = difference_type>
258  void advance_dispatch(DiffType n, std::random_access_iterator_tag) {
259  if (n == 1)
260  increment();
261  else if (n == -1)
262  decrement();
263  else if (n < 0)
264  jump_backward(-n);
265  else if (n > 0)
266  jump_forward(n);
267  }
268 
269  void advance(difference_type n) {
270  advance_dispatch(
271  n, typename std::iterator_traits<InnerIter>::iterator_category());
272  }
273 
274  template <class Other>
275  difference_type distance_to_dispatch(Other it2,
276  std::input_iterator_tag) const {
277  // Inline safe_distance here otherwise there is a cyclic dependency:
278  // std::distance -> iterator_adaptor -> distance_to -> safe_distance ->
279  // std::distance
280  if (*this == it2)
281  return 0;
282 
283  TwoLevelIteratorA it1(*this);
284  TwoLevelIteratorA it2_orig(it2);
285 
286  difference_type count1 = 0;
287  difference_type count2 = 0;
288 
289  while (true) {
290  if (it1.m_outer != it1.m_outer_end) {
291  ++count1;
292  if (++it1 == it2_orig)
293  return count1;
294  }
295  if (it2.m_outer != it2.m_outer_end) {
296  ++count2;
297  if (++it2 == *this)
298  return -count2;
299  }
300  }
301  }
302 
303  template <class Other>
304  difference_type distance_to_dispatch(const Other& x,
305  std::random_access_iterator_tag) const {
306  if (*this == x)
307  return 0;
308  else if (m_outer == x.m_outer)
309  return safe_distance(this->base_reference(), x.base_reference(),
310  m_inner_end_fn(*m_outer));
311  else if (safe_distance(m_outer, x.m_outer, m_outer_end) < 0)
312  return -x.distance_to(*this);
313 
314  difference_type me_count = 0;
315 
316  TwoLevelIteratorA me(*this);
317 
318  while (me.m_outer != me.m_outer_end) {
319  difference_type d;
320  if (me.m_outer != x.m_outer)
321  d = std::distance(me.base_reference(), me.m_inner_end_fn(*me.m_outer));
322  else
323  d = std::distance(me.base_reference(), x.base_reference());
324  me_count += d;
325  std::advance(me, d);
326  if (me == x)
327  return me_count;
328  }
329 
330  GALOIS_DIE("invalid iterator ", std::distance(m_outer, x.m_outer));
331  return 0;
332  }
333 
334  template <class OtherOuterIter, class OtherInnerIter, class C, class BF,
335  class EF>
336  difference_type distance_to(
337  const TwoLevelIteratorA<OtherOuterIter, OtherInnerIter, C, BF, EF>& x)
338  const {
339  return distance_to_dispatch(
340  x, typename std::iterator_traits<InnerIter>::iterator_category());
341  }
342 
343  template <class OtherOuterIter, class OtherInnerIter, class C, class BF,
344  class EF>
345  bool
346  equal(const TwoLevelIteratorA<OtherOuterIter, OtherInnerIter, C, BF, EF>& x)
347  const {
348  if (m_outer == m_outer_end && m_outer == x.m_outer)
349  return true;
350 
351  return m_outer == x.m_outer && this->base_reference() == x.base_reference();
352  }
353 
354 public:
356 
357  TwoLevelIteratorA(OuterIter outer_begin, OuterIter outer_end, OuterIter outer,
358  InnerBeginFn inner_begin_fn, InnerEndFn inner_end_fn)
359  : m_outer_begin(outer_begin), m_outer_end(outer_end), m_outer(outer),
360  m_inner_begin_fn(inner_begin_fn), m_inner_end_fn(inner_end_fn) {
361  if (m_outer != m_outer_end) {
362  this->base_reference() = m_inner_begin_fn(*m_outer);
363  seek_forward();
364  }
365  }
366 
367  TwoLevelIteratorA(OuterIter outer_begin, OuterIter outer_end, OuterIter outer,
368  InnerIter inner, InnerBeginFn inner_begin_fn,
369  InnerEndFn inner_end_fn)
370  : m_outer_begin(outer_begin), m_outer_end(outer_end), m_outer(outer),
371  m_inner_begin_fn(inner_begin_fn), m_inner_end_fn(inner_end_fn) {
372  this->base_reference() = inner;
373  }
374 
375  const OuterIter& get_outer_reference() const { return m_outer; }
376 
377  const InnerIter& get_inner_reference() const {
378  return this->base_reference();
379  }
380 };
381 
383 struct GetBegin {
384  template <class T>
385  auto operator()(T&& x) const -> decltype(std::forward<T>(x).begin()) {
386  return std::forward<T>(x).begin();
387  }
388 };
389 
391 struct GetEnd {
392  template <class T>
393  auto operator()(T&& x) const -> decltype(std::forward<T>(x).end()) {
394  return std::forward<T>(x).end();
395  }
396 };
397 
398 #if __cplusplus >= 201103L
399 template <
400  class CategoryOrTraversal = std::forward_iterator_tag, class OuterIter,
401  class InnerIter = decltype(std::declval<OuterIter>()->begin()),
402  class InnerBeginFn = GetBegin, class InnerEndFn = GetEnd,
403  class Iter = TwoLevelIteratorA<OuterIter, InnerIter, CategoryOrTraversal,
404  InnerBeginFn, InnerEndFn>>
405 std::pair<Iter, Iter> make_two_level_iterator(OuterIter outer_begin,
406  OuterIter outer_end) {
407  return std::make_pair(
408  Iter(outer_begin, outer_end, outer_begin, InnerBeginFn(), InnerEndFn()),
409  Iter(outer_begin, outer_end, outer_end, InnerBeginFn(), InnerEndFn()));
410 }
411 #else
412 // XXX(ddn): More direct encoding crashes XL 12.1, so lean towards more verbose
413 // types
414 template <class CategoryOrTraversal, class OuterIter, class InnerIter,
415  class InnerBeginFn, class InnerEndFn>
416 std::pair<TwoLevelIteratorA<OuterIter, InnerIter, CategoryOrTraversal,
417  InnerBeginFn, InnerEndFn>,
418  TwoLevelIteratorA<OuterIter, InnerIter, CategoryOrTraversal,
419  InnerBeginFn, InnerEndFn>>
420 make_two_level_iterator(OuterIter outer_begin, OuterIter outer_end) {
421  return std::make_pair(
422  TwoLevelIteratorA<OuterIter, InnerIter, CategoryOrTraversal, InnerBeginFn,
423  InnerEndFn>(outer_begin, outer_end, outer_begin,
424  InnerBeginFn(), InnerEndFn()),
425  TwoLevelIteratorA<OuterIter, InnerIter, CategoryOrTraversal, InnerBeginFn,
426  InnerEndFn>(outer_begin, outer_end, outer_end,
427  InnerBeginFn(), InnerEndFn()));
428 }
429 #endif
430 
431 } // end namespace galois
432 
433 #endif
Alternate implementation of ChooseTwoLevelIterator.
Definition: TwoLevelIteratorA.h:40
friend class boost::iterator_core_access
Definition: TwoLevelIteratorA.h:70
Helper functor, returns t.end()
Definition: TwoLevelIteratorA.h:383
TwoLevelIteratorA(OuterIter outer_begin, OuterIter outer_end, OuterIter outer, InnerIter inner, InnerBeginFn inner_begin_fn, InnerEndFn inner_end_fn)
Definition: TwoLevelIteratorA.h:367
TwoLevelIteratorA()
Definition: TwoLevelIteratorA.h:355
#define GALOIS_DIE(...)
Definition: gIO.h:96
auto operator()(T &&x) const -> decltype(std::forward< T >(x).begin())
Definition: TwoLevelIteratorA.h:385
TwoLevelIteratorA(OuterIter outer_begin, OuterIter outer_end, OuterIter outer, InnerBeginFn inner_begin_fn, InnerEndFn inner_end_fn)
Definition: TwoLevelIteratorA.h:357
const OuterIter & get_outer_reference() const
Definition: TwoLevelIteratorA.h:375
std::pair< TwoLevelIteratorA< OuterIter, InnerIter, CategoryOrTraversal, InnerBeginFn, InnerEndFn >, TwoLevelIteratorA< OuterIter, InnerIter, CategoryOrTraversal, InnerBeginFn, InnerEndFn > > make_two_level_iterator(OuterIter outer_begin, OuterIter outer_end)
Definition: TwoLevelIteratorA.h:420
const InnerIter & get_inner_reference() const
Definition: TwoLevelIteratorA.h:377
const Ty min(std::atomic< Ty > &a, const Ty &b)
Definition: AtomicHelpers.h:70
auto operator()(T &&x) const -> decltype(std::forward< T >(x).end())
Definition: TwoLevelIteratorA.h:393
TwoLevelIteratorA::iterator_adaptor_::difference_type difference_type
Definition: TwoLevelIteratorA.h:47
Helper functor, returns t.end()
Definition: TwoLevelIteratorA.h:391