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 }