1 /**
2 Copyright: Copyright (c) 2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module emulator.utils.groupsequence;
8 
9 import std.range : isInputRange, isInfinite, isForwardRange, ElementType;
10 import std.array : empty, front, popFront, save;
11 import std.functional : binaryFun;
12 import std.typecons : tuple, Tuple;
13 
14 // group
15 /**
16 Similarly to $(D uniq), $(D group) iterates consecutive
17 elements of the given range. The element type is $(D
18 Tuple!(ElementType!R, uint)) because it includes the count of
19 grouped elements seen. Elements are grouped by assessing using
20 the predicate $(D pred), by default $(D "a == b"). The predicate
21 is called for pairs of neighboring elements.
22 
23 $(D Group) is an input range if $(D R) is an input range, and a
24 forward range in all other cases.
25 */
26 struct GroupSequence(alias pred, R) if (isInputRange!R)
27 {
28     private R _input;
29     private Tuple!(ElementType!R, uint) _current;
30     private alias binaryFun!pred comp;
31 
32     this(R input)
33     {
34         _input = input;
35         if (!_input.empty) popFront();
36     }
37 
38     void popFront()
39     {
40         if (_input.empty)
41         {
42             _current[1] = 0;
43         }
44         else
45         {
46             _current = tuple(_input.front, 1u);
47             auto lastInGroup = _current[0];
48             _input.popFront();
49             
50             while (!_input.empty && comp(lastInGroup, _input.front))
51             {
52                 ++_current[1];
53                 lastInGroup = _input.front;
54                 _input.popFront();
55             }
56         }
57     }
58 
59     static if (isInfinite!R)
60     {
61         enum bool empty = false;  // Propagate infiniteness.
62     }
63     else
64     {
65         @property bool empty()
66         {
67             return _current[1] == 0;
68         }
69     }
70 
71      @property ref Tuple!(ElementType!R, uint) front()
72     {
73         assert(!empty);
74         return _current;
75     }
76 
77     static if (isForwardRange!R) {
78         @property typeof(this) save() {
79             typeof(this) ret = this;
80             ret._input = this._input.save;
81             ret._current = this._current;
82             return ret;
83         }
84     }
85 }
86 
87 /// Ditto
88 GroupSequence!(pred, Range) groupSequence(alias pred = "a == b", Range)(Range r)
89 {
90     return typeof(return)(r);
91 }
92 
93 unittest
94 {
95     import std.algorithm : equal;
96     
97     // group continuos
98     int[] numbers = [1, 2, 3,  5, 6, 7,  10];
99     assert(equal(groupSequence!"b-a==1"(numbers), [tuple(1, 3u), tuple(5, 3u), tuple(10, 1u)][]));
100     
101     // Plain group with pred "a==b"
102     int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
103     assert(equal(groupSequence(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u),
104         tuple(4, 3u), tuple(5, 1u) ][]));
105 }