int c4_GroupByViewer::ScanTransitions(int lo_, int hi_, t4_byte *flags_, const c4_View &match_)const { d4_assert(lo_ > 0); int m = hi_ - lo_; d4_assert(m >= 0); // done if nothing left or if entire range is identical if (m == 0 || match_[lo_ - 1] == match_[hi_ - 1]) return 0; // range has a transition, done if it is exactly of size one if (m == 1) { ++(flags_[lo_]); return 1; } // use binary splitting if the range has enough entries if (m >= 5) return ScanTransitions(lo_, lo_ + m / 2, flags_, match_) + ScanTransitions (lo_ + m / 2, hi_, flags_, match_); // else use a normal linear scan int n = 0; for (int i = lo_; i < hi_; ++i) if (match_[i] != match_[i - 1]) { ++(flags_[i]); ++n; } return n; }
c4_GroupByViewer::c4_GroupByViewer (c4_Sequence& seq_, const c4_View& keys_, const c4_Property& result_) : _parent (&seq_), _keys (keys_), _result (result_) { _sorted = _parent.SortOn(_keys); int n = _sorted.GetSize(); c4_Bytes temp; t4_byte* buf = temp.SetBufferClear(n); int groups = 0; if (n > 0) { ++buf[0]; // the first entry is always a transition groups = 1 + ScanTransitions(1, n, buf, _sorted.Project(_keys)); } // set up a map pointing to each transition _map.SetSize(groups + 1); int j = 0; for (int i = 0; i < n; ++i) if (buf[i]) _map.SetAt(j++, i); // also append an entry to point just past the end _map.SetAt(j, n); d4_assert(_map.GetAt(0) == 0); d4_assert(j == groups); }