1 module dud.semver.setoperation;
2 
3 import std.array : array;
4 import std.algorithm.iteration : map, filter, joiner;
5 import std.exception : enforce;
6 import std.stdio;
7 
8 import dud.semver.versionunion;
9 import dud.semver.versionrange;
10 import dud.semver.semver;
11 
12 import dud.semver.checks;
13 
14 @safe pure:
15 
16 //
17 // unionOf
18 //
19 
20 VersionUnion unionOf(const(SemVer) a, const(SemVer) b) {
21 	VersionUnion ret = VersionUnion(
22 			[ VersionRange(a, Inclusive.yes, a, Inclusive.yes)
23 			, VersionRange(b, Inclusive.yes, b, Inclusive.yes)
24 			]);
25 	return ret;
26 }
27 
28 VersionUnion unionOf(const(SemVer) a, const(VersionRange) b) {
29 	return unionOf(b, a);
30 }
31 
32 VersionUnion unionOf(const(VersionRange) a, const(SemVer) b) {
33 	VersionUnion ret = VersionUnion(
34 			[ a.dup
35 			, VersionRange(b, Inclusive.yes, b, Inclusive.yes)
36 			]);
37 	return ret;
38 }
39 
40 VersionUnion unionOf(const(SemVer) a, const(VersionUnion) b) {
41 	return unionOf(b, a);
42 }
43 
44 VersionUnion unionOf(const(VersionUnion) a, const(SemVer) b) {
45 	VersionUnion ret = a.dup();
46 	ret.insert(VersionRange(b, Inclusive.yes, b, Inclusive.yes));
47 	return ret;
48 }
49 
50 VersionUnion unionOf(const(VersionRange) a, const(VersionRange) b) {
51 	VersionUnion ret = VersionUnion([a.dup , b.dup]);
52 	return ret;
53 }
54 
55 VersionUnion unionOf(const(VersionRange) a, const(VersionUnion) b) {
56 	return unionOf(b, a);
57 }
58 
59 VersionUnion unionOf(const(VersionUnion) a, const(VersionRange) b) {
60 	VersionUnion ret = a.dup();
61 	ret.insert(b);
62 	return ret;
63 }
64 
65 VersionUnion unionOf(const(VersionUnion) a, const(VersionUnion) b) {
66 	import std.algorithm.iteration : each;
67 
68 	VersionUnion ret = a.dup();
69 	b.ranges.each!(it => ret.insert(it));
70 	return ret;
71 }
72 
73 //
74 // intersectionOf
75 //
76 
77 SemVer intersectionOf(const(SemVer) a, const(SemVer) b) {
78 	return a == b ? a.dup : SemVer.init;
79 }
80 
81 SemVer intersectionOf(const(VersionRange) a, const(SemVer) b) {
82 	return allowsAny(a, b) ? b.dup : SemVer.init;
83 }
84 
85 SemVer intersectionOf(const(SemVer) a, const(VersionRange) b) {
86 	return intersectionOf(b, a);
87 }
88 
89 SemVer intersectionOf(const(VersionUnion) a, const(SemVer) b) {
90 	return allowsAny(a, b) ? b.dup : SemVer.init;
91 }
92 
93 SemVer intersectionOf(const(SemVer) a, const(VersionUnion) b) {
94 	return intersectionOf(b, a);
95 }
96 
97 VersionRange intersectionOf(const(VersionRange) a, const(VersionRange) b) {
98 	// a: . . ( . . ] . .
99 	// b: . [ . . ) . . .
100 
101 	// a: . [ . . ) . . .
102 	// b: . . ( . . ] . .
103 
104 	// a: . [ . ] . . . .
105 	// b: . . . . [ ] . .
106 
107 	// a: . . . . [ ] . .
108 	// b: . [ . ] . . . .
109 
110 	// a: . . [ ] . . . .
111 	// b: . [ . . ] . . .
112 
113 	// a: . . . . ( . ] .
114 	// b: . [ . . ] . . .
115 
116 	const SemVer low = a.low < b.low ? b.low : a.low;
117 	const SemVer high = a.high > b.high ? b.high : a.high;
118 
119 	const Inclusive incLow =
120 		a.low == b.low && (!a.inclusiveLow || !b.inclusiveLow)
121 			? Inclusive.no
122 			: a.low == low ? a.inclusiveLow : b.inclusiveLow;
123 
124 	const Inclusive incHigh =
125 		a.high == b.high && (!a.inclusiveHigh || !b.inclusiveHigh)
126 			? Inclusive.no
127 			: a.high == high ? a.inclusiveHigh : b.inclusiveHigh;
128 
129 	return low < high
130 		? VersionRange(low, incLow, high, incHigh)
131 		: a.high == b.low && a.inclusiveHigh && b.inclusiveLow
132 			? VersionRange(a.high, Inclusive.yes, a.high, Inclusive.yes)
133 			: b.high == a.low && b.inclusiveHigh && a.inclusiveLow
134 				? VersionRange(b.high, Inclusive.yes, b.high, Inclusive.yes)
135 				: VersionRange.init;
136 }
137 
138 VersionUnion intersectionOf(const(VersionUnion) a, const(VersionRange) b) {
139 	return a.ranges
140 		.map!(it => intersectionOf(it, b))
141 		.filter!(it => it != VersionRange.init)
142 		.array
143 		.VersionUnion;
144 }
145 
146 VersionUnion intersectionOf(const(VersionRange) a, const(VersionUnion) b) {
147 	return intersectionOf(b, a);
148 }
149 
150 VersionUnion intersectionOf(const(VersionUnion) a, const(VersionUnion) b) {
151 	return a.ranges
152 		.map!(it => b.ranges.map!(jt => intersectionOf(it, jt)))
153 		.joiner
154 		.filter!(it => it != VersionRange.init)
155 		.array
156 		.VersionUnion;
157 }
158 
159 //
160 // invert
161 //
162 
163 VersionUnion invert(const(SemVer) a) {
164 	enforce(a != SemVer.init);
165 	return VersionUnion(
166 		[ VersionRange(SemVer.min, Inclusive.yes, a.dup, Inclusive.no)
167 		, VersionRange(a.dup, Inclusive.no, SemVer.max, Inclusive.yes)
168 		]);
169 }
170 
171 VersionUnion invert(const(VersionRange) a) {
172 	enforce(a.low != SemVer.init);
173 	return VersionUnion(
174 		[ VersionRange(SemVer.init, Inclusive.yes, a.low.dup, cast(Inclusive)!a.inclusiveLow)
175 		, VersionRange(a.high.dup, cast(Inclusive)!a.inclusiveHigh, SemVer.max, Inclusive.yes)
176 		]);
177 }
178 
179 VersionUnion invert(const(VersionUnion) a) {
180 	import std.range : chain, chunks, only, repeat, take;
181 
182 	auto zz = chain(
183 			[ [ VersionRange(SemVer.min(), Inclusive.no, SemVer.min(), Inclusive.no) ]
184 			, a.ranges.map!(r => r.dup.repeat.take(2)).joiner.array
185 			, [ VersionRange(SemVer.max(), Inclusive.no, SemVer.max(), Inclusive.yes) ]
186 			]
187 		)
188 		.joiner
189 		.array
190 		.chunks(2)
191 		.map!(c => VersionRange(c[0].high, cast(Inclusive)!c[0].inclusiveHigh,
192 					c[1].low, cast(Inclusive)!c[1].inclusiveLow))
193 		.array;
194 
195 	return VersionUnion(zz);
196 }
197 
198 //
199 // difference
200 //
201 
202 SemVer differenceOf(const(SemVer) a, const(SemVer) b) {
203 	const VersionUnion bInt = invert(b);
204 	return intersectionOf(a, bInt);
205 }
206 
207 VersionUnion differenceOf(const(VersionRange) a, const(SemVer) b) {
208 	const VersionUnion bInt = invert(b);
209 	return intersectionOf(a, bInt);
210 }
211 
212 SemVer differenceOf(const(SemVer) a, const(VersionRange) b) {
213 	const VersionUnion bInt = invert(b);
214 	return intersectionOf(a, bInt);
215 }
216 
217 VersionUnion differenceOf(const(VersionRange) a, const(VersionRange) b) {
218 	const VersionUnion bInt = invert(b);
219 	auto ret = intersectionOf(a, bInt);
220 	return ret;
221 }
222 
223 VersionUnion differenceOf(const(VersionUnion) a, const(VersionRange) b) {
224 	const VersionUnion bInt = invert(b);
225 	auto ret = intersectionOf(a, bInt);
226 	return ret;
227 }
228 
229 VersionUnion differenceOf(const(VersionRange) a, const(VersionUnion) b) {
230 	const VersionUnion bInt = invert(b);
231 	auto ret = intersectionOf(a, bInt);
232 	return ret;
233 }
234 
235 VersionUnion differenceOf(const(VersionUnion) a, const(VersionUnion) b) {
236 	const VersionUnion bInt = invert(b);
237 	auto ret = intersectionOf(a, bInt);
238 	return ret;
239 }