1 //Written in the D programming language
2 /*
3  * Some algorithm routines and templates
4  *
5  * Copyright 2013-6 Jaypha
6  *
7  * Distributed under the Boost Software License, Version 1.0.
8  * (See http://www.boost.org/LICENSE_1_0.txt)
9  *
10  * Authors: Jason den Dulk
11  */
12 
13 
14 module jaypha.algorithm;
15 
16 import jaypha.traits;
17 
18 import std.range.primitives;
19 
20 public import std.algorithm;
21 import std.typecons, std.range, std.array, std.traits;
22 
23 
24 //----------------------------------------------------------------------------
25 // Consumes the front of the range as long the elements are inside pattern.
26 
27 auto munch(alias pred = "a == b", R1,R2)(ref R1 range, R2 pattern)
28   if (isInputRange!R1 && isInputRange!R2 &&
29       isScalarType!(ElementType!R2) && isScalarType!(ElementType!R1))
30 {
31   alias ElementType!R1 E1;
32   alias ElementType!R2 E2;
33 
34   auto a = appender!(E1[]);
35 
36   for (; !range.empty && !find!pred(pattern, cast(E2)range.front).empty; range.popFront())
37     a.put(range.front);
38 
39   return a.data;
40 }
41 
42 unittest
43 {
44   import std.array;
45   import std.range.interfaces;
46 
47   ubyte[] txt = cast(ubyte[]) "acabbacbxyz".dup;
48 
49   txt.munch("abc");
50   assert(txt == "xyz");
51 }
52 
53 //-----------------------------------------------------------------------------
54 // Grabs as much of seq, unitl a character is found that matches choices
55 // according to the given predicate. Returns the first part while setting
56 // seq to the remainder (including the found character).
57 
58 auto grab(alias pred = "a == b", R1, R2)(ref R1 seq, R2 choices)
59   if (!hasSlicing!R1 && isInputRange!R1 && isForwardRange!R2)
60 {
61   alias ElementType!R1 E;
62 
63   auto a = appender!(E[]);
64 
65   for (; !seq.empty && find!pred(choices, seq.front).empty; seq.popFront())
66     a.put(seq.front);
67   return a.data;
68 }
69 
70 
71 S1 grab(alias pred = "a == b",S1, S2)(ref S1 seq, S2 choices)
72   if (hasSlicing!S1 && isForwardRange!S2)
73 {
74   auto remainder = findAmong!(pred,S1,S2)(seq, choices);
75   scope(exit) seq = remainder;
76   return seq[0..$-remainder.length];
77 }
78 
79 //-------------------------------------
80 
81 unittest
82 {
83   dstring s = "abcdefg";
84   dstring c = "cg";
85 
86   auto x = grab(s,c);
87   assert(x == "ab");
88   assert(s.front == 'c');
89   s = s[1..$];
90   x = grab(s,c);
91   assert(x == "def");
92   assert(s.front == 'g');
93 
94   string t1 = "to be or not to be";
95   auto t = grab(t1, "abc");
96   assert(t == "to ");
97   assert(t1 == "be or not to be");
98 }
99 
100 //----------------------------------------------------------------------------
101 // Map-like algorithm that merges the index and values of an associative
102 // array.
103 
104 template meld(alias fun)
105 {
106   auto meld(A)(A t) if (isAssociativeArray!A)
107   {
108     return MeldResult!(fun, A)(t);
109   }
110 }
111 
112 private struct MeldResult(alias F, T:T[U],U)
113 {
114   U[] val;
115   T[U] home;
116   
117   this(T[U] t) { home = t; val = t.keys; }
118 
119   @property bool empty() { return (val.length == 0); }
120   @property U front() { return F(val[0],home[val[0]]); }
121   void popFront() { val = val[1..$]; }
122 }
123 
124 //-------------------------------------
125 
126 unittest
127 {
128   string[string] x = [ "one":"1", "bee":"3", "john":"66" ];
129 
130   auto y = x.meld!((a,b) => (a~b));
131   string[] yy = new string[](3);
132   y.copy(yy);
133   sort(yy);  // Do sort to make the order predictable
134   assert(yy == ["bee3","john66","one1"]);
135 }
136 
137 //----------------------------------------------------------------------------
138 // Returns everything in primary that is not in secondary
139 
140 T[] diff(T)(T[] primary, T[] secondary)
141 {
142   auto result = appender!(T[])();
143   result.reserve(primary.length);
144 
145   foreach (t;primary)
146   {
147     if (!secondary.canFind(t))
148       result.put(t);
149   }
150 
151   return result.data;
152 }
153 
154 //-------------------------------------
155 
156 unittest
157 {
158   auto t1 = [ 2, 12, 5, 25, 5, 7 ];
159   auto t2 = [ 2, 3, 17, 5 ];
160 
161   auto t3 = diff(t1,t2);
162 
163   assert(t3 == [ 12, 25, 7 ]);
164 }
165 
166 //----------------------------------------------------------------------------
167 // Alternative to std.alogrithm.findSplit usable with non-rewindable ranges.
168 
169 auto findSplit(R1,R2)(ref R1 haystack, R2 needle)
170   if (isInputRange!R1 && isDynamicArray!R2 && isComparable!(ElementType!R1,ElementType!R2))
171 {
172   import std.algorithm.searching : endsWith, findSplitBefore;
173 
174   assert(needle.length > 0);
175   auto store = appender!R2();
176   while (!haystack.empty)
177   {
178     store.put(haystack.front);
179     haystack.popFront();
180     if (store.data.endsWith(needle))
181       return store.data.findSplitBefore(needle);
182   }
183   return tuple(store.data, uninitializedArray!(R2)(0));
184 }
185 
186 //-------------------------------------
187 
188 unittest
189 {
190   import std.stdio;
191   string haystack = "donôbôbôbcbxyz";
192   string needle = "ôbôbc";
193 
194   auto res = findSplit(haystack, needle);
195   assert(res[0] == "donôb");
196   assert(res[1] == "ôbôbc");
197   assert(haystack == "bxyz");
198 
199   auto res2 = findSplit(haystack, needle);
200   assert(res2[0] == "bxyz");
201   assert(res2[1] == "");
202   assert(haystack == "");
203 
204   haystack = "asdfjkewu";
205   auto res3 = findSplit(haystack, "a");
206   assert(res3[0] == "");
207   assert(res3[1] == "a");
208   assert(haystack == "sdfjkewu");
209 
210   haystack = "";
211   auto res4 = findSplit(haystack, "p");
212   assert(res4[0] == "");
213   assert(res4[1] == "");
214   assert(haystack == "");
215 
216   struct NoForward
217   {
218     ubyte[] bytes;
219     @property bool empty() { return bytes.length == 0; }
220     @property ubyte front() { return bytes[0]; }
221     void popFront() { bytes = bytes[1..$]; }
222   }
223 
224   auto x = NoForward(cast(ubyte[])"123456789");
225   auto n = cast(ubyte[]) "456";
226   auto res5 = findSplit(x,n);
227   assert(res5[0] == cast(ubyte[]) "123");
228   assert(res5[1] == cast(ubyte[]) "456");
229 
230   res5 = findSplit(x,n);
231   assert(res5[0] == cast(ubyte[]) "789");
232   assert(res5[1] == cast(ubyte[]) "");
233 
234 }
235 
236 //----------------------------------------------------------------------------
237 // Similar to std.algorithm.map except that the mapping function is not a
238 // template parameter
239 
240 struct rtMap(R,T) if(isInputRange!R)
241 {
242   this(R range, T delegate(ElementType!R) mapper)
243   {
244     _range = range;
245     _mapper = mapper;
246     if (!_range.empty)
247       _front = _mapper(_range.front);
248   }
249 
250   @property T front() { return _front; }
251   @property bool empty() { return _range.empty; }
252   void popFront()
253   {
254     _range.popFront();
255     if (!_range.empty)
256       _front = _mapper(_range.front);
257   }
258 
259   private:
260     R _range;
261     T _front;
262     T delegate(ElementType!R) _mapper;
263 }
264 
265 unittest
266 {
267   uint m1(uint i) { return i+3; }
268   uint m2(uint i) { return i+6; }
269 
270   uint[] src = [ 0, 4, 7 ];
271 
272   auto rmap = rtMap!(uint[], uint)(src, &m1);
273 
274   auto dest = appender!(uint[]);
275   rmap.copy(dest);
276 
277   assert(dest.data == [ 3, 7, 10 ]);
278 
279   rmap = rtMap!(uint[], uint)(src, &m2);
280 
281   dest = appender!(uint[]);
282   rmap.copy(dest);
283 
284   assert(dest.data == [ 6, 10, 13 ]);
285 }
286 
287 //----------------------------------------------------------------------------
288