1 module nxt.ixes;
2 
3 import std.meta : allSatisfy;
4 import std.range.primitives : isInputRange, isBidirectionalRange;
5 
6 /** Get length of Common Prefix of $(D a) and $(D b).
7 	See_Also: http://forum.dlang.org/thread/bmbhovkgqomaidnyakvy@forum.dlang.org#post-bmbhovkgqomaidnyakvy:40forum.dlang.org
8 */
9 auto commonPrefixLength(alias pred = "a == b", Rs...)(Rs rs)
10 if (rs.length >= 2 &&
11 	allSatisfy!(isInputRange, Rs))
12 {
13 	import std.algorithm.searching : commonPrefix;
14 	static if (rs.length == 2)
15 		return commonPrefix!pred(rs[0], rs[1]).length;
16 	else
17 	{
18 		static assert("TODO");
19 		import std.range : zip, StoppingPolicy;
20 		import std.algorithm : countUntil, count;
21 		const hit = zip(a, b).countUntil!(ab => ab[0] != ab[1]); /+ TODO: if countUntil return zip(a, b).count upon failre... +/
22 		return hit == -1 ? zip(a, b).count!pred : hit; /+ TODO: ..then this would not have been needed +/
23 	}
24 }
25 
26 pure @safe unittest {
27 	assert(commonPrefixLength(`åäö_`,
28 							  `åäö-`) == 6);
29 }
30 
31 pure nothrow @safe unittest {
32 	const x = [1, 2, 3, 10], y = [1, 2, 4, 10];
33 	void f() pure nothrow @safe @nogc
34 	{
35 		assert(commonPrefixLength(x, y) == 2);
36 	}
37 	f();
38 	assert(commonPrefixLength([1, 2, 3, 10],
39 							  [1, 2, 3]) == 3);
40 	assert(commonPrefixLength([1, 2, 3, 0, 4],
41 							  [1, 2, 3, 9, 4]) == 3);
42 }
43 
44 /** Get length of Suffix of $(D a) and $(D b).
45 	See_Also: http://forum.dlang.org/thread/bmbhovkgqomaidnyakvy@forum.dlang.org#post-bmbhovkgqomaidnyakvy:40forum.dlang.org
46 */
47 auto commonSuffixLength(Rs...)(Rs rs)
48 	if (rs.length == 2 &&
49 		allSatisfy!(isBidirectionalRange, Rs))
50 {
51 	import std.traits : isNarrowString;
52 	import std.range: retro;
53 	static if (isNarrowString!(typeof(rs[0])) &&
54 			   isNarrowString!(typeof(rs[1])))
55 	{
56 		import std.string: representation;
57 		return commonPrefixLength(rs[0].representation.retro,
58 								  rs[1].representation.retro);
59 	}
60 	else
61 		return commonPrefixLength(rs[0].retro,
62 								  rs[1].retro);
63 }
64 
65 pure @safe unittest {
66 	const x = [1, 2, 3, 10, 11, 12];
67 	const y = [1, 2, 4, 10, 11, 12];
68 	void f() pure nothrow @safe @nogc
69 	{
70 		assert(commonPrefixLength(x, y) == 2);
71 	}
72 	f();
73 	assert(commonSuffixLength(x, y) == 3);
74 	assert(commonSuffixLength([10, 1, 2, 3],
75 							  [1, 2, 3]) == 3);
76 }
77 
78 pure @safe unittest {
79 	assert(commonSuffixLength(`_åäö`,
80 							  `-åäö`) == 6);
81 }
82 
83 /** Get Count of Prefix of $(D a) and $(D b).
84 	See_Also: http://forum.dlang.org/thread/bmbhovkgqomaidnyakvy@forum.dlang.org#post-bmbhovkgqomaidnyakvy:40forum.dlang.org
85 */
86 auto commonPrefixCount(alias pred = "a == b", Rs...)(Rs rs)
87 if (rs.length == 2 &&
88 	allSatisfy!(isInputRange, Rs))
89 {
90 	import std.algorithm.searching : commonPrefix, count;
91 	import std.traits : isNarrowString;
92 	static if (isNarrowString!(typeof(rs[0])) &&
93 			   isNarrowString!(typeof(rs[1])))
94 	{
95 		import std.utf: byDchar;
96 		return commonPrefix!pred(rs[0].byDchar,
97 								 rs[1].byDchar).count;
98 	}
99 	else
100 		return commonPrefix!pred(rs[0], rs[1]).count;
101 }
102 
103 pure @safe unittest {
104 	assert(commonPrefixCount([1, 2, 3, 10],
105 							 [1, 2, 3]) == 3);
106 	assert(commonPrefixCount(`åäö_`,
107 							 `åäö-`) == 3);
108 }
109 
110 /** Get Common Suffix of $(D a) and $(D b).
111 	TODO: Copy implementation of commonPrefix into commonSuffix to splitter
112 */
113 auto commonSuffix(Rs...)(Rs rs)
114 if (rs.length == 2 &&
115 	allSatisfy!(isBidirectionalRange, Rs))
116 {
117 	import std.range : retro;
118 	import std.array : array;
119 	import std.algorithm.searching : commonPrefix;
120 	return commonPrefix(rs[0].retro,
121 						rs[1].retro).array.retro;
122 }
123 
124 pure @safe unittest {
125 	import std.algorithm.comparison : equal;
126 	assert(equal(commonSuffix(`_åäö`,
127 							  `-åäö`), `åäö`));
128 }
129 
130 // pure @safe unittest
131 // {
132 //	 import std.algorithm.comparison : equal;
133 //	 import nxt.splitter_ex : splitterASCIIAmong;
134 //	 import std.range : retro;
135 //	 import std.range.primitives : ElementType;
136 //	 import std.array : array;
137 //	 assert(equal(commonSuffix(`_å-ä-ö`,
138 //							   `-å-ä-ö`).retro.splitterASCIIAmong!('-').array, /+ TODO: how should this be solved? +/
139 //				  [`ö`, `ä`, `å`]));
140 // }
141 
142 /** Get Count of Common Suffix of $(D a) and $(D b).
143 	See_Also: http://forum.dlang.org/thread/bmbhovkgqomaidnyakvy@forum.dlang.org#post-bmbhovkgqomaidnyakvy:40forum.dlang.org
144 */
145 auto commonSuffixCount(alias pred = "a == b", Rs...)(Rs rs)
146 if (rs.length == 2 &&
147 	allSatisfy!(isBidirectionalRange, Rs))
148 {
149 	import std.range : retro;
150 	return commonPrefixCount!pred(rs[0].retro,
151 								  rs[1].retro);
152 }
153 
154 pure @safe unittest {
155 	assert(commonSuffixCount(`_`, `-`) == 0);
156 	assert(commonSuffixCount(`_å`, `-å`) == 1);
157 	assert(commonSuffixCount(`_åä`, `-åä`) == 2);
158 	assert(commonSuffixCount(`_åäö`, `-åäö`) == 3);
159 
160 	import std.algorithm.comparison : among;
161 	assert(commonSuffixCount!((a, b) => (a == b && a == 'ö'))(`_åäö`, `-åäö`) == 1);
162 	assert(commonSuffixCount!((a, b) => (a == b && a.among!('ä', 'ö')))(`_åäö`, `-åäö`) == 2);
163 }
164 
165 /** Get length of Common Prefix of rs $(D rs).
166 	See_Also: http://forum.dlang.org/thread/bmbhovkgqomaidnyakvy@forum.dlang.org#post-bmbhovkgqomaidnyakvy:40forum.dlang.org
167 */
168 // auto commonPrefixLengthN(R...)(R rs) if (rs.length == 2)
169 // {
170 //	 import std.range: zip;
171 //	 return zip!((a, b) => a != b)(rs);
172 // }
173 
174 // unittest
175 // {
176 //	 assert(commonPrefixLengthN([1, 2, 3, 10],
177 //							   [1, 2, 4, 10]) == 2);
178 // }