rangops 1.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +23 -0
- data/LICENSE.txt +21 -0
- data/README.md +49 -0
- data/Rakefile +19 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/docs/README_md.html +150 -0
- data/docs/Rangops/Set.html +719 -0
- data/docs/Rangops.html +105 -0
- data/docs/created.rid +3 -0
- data/docs/css/fonts.css +167 -0
- data/docs/css/rdoc.css +683 -0
- data/docs/fonts/Lato-Light.ttf +0 -0
- data/docs/fonts/Lato-LightItalic.ttf +0 -0
- data/docs/fonts/Lato-Regular.ttf +0 -0
- data/docs/fonts/Lato-RegularItalic.ttf +0 -0
- data/docs/fonts/SourceCodePro-Bold.ttf +0 -0
- data/docs/fonts/SourceCodePro-Regular.ttf +0 -0
- data/docs/images/add.png +0 -0
- data/docs/images/arrow_up.png +0 -0
- data/docs/images/brick.png +0 -0
- data/docs/images/brick_link.png +0 -0
- data/docs/images/bug.png +0 -0
- data/docs/images/bullet_black.png +0 -0
- data/docs/images/bullet_toggle_minus.png +0 -0
- data/docs/images/bullet_toggle_plus.png +0 -0
- data/docs/images/date.png +0 -0
- data/docs/images/delete.png +0 -0
- data/docs/images/find.png +0 -0
- data/docs/images/loadingAnimation.gif +0 -0
- data/docs/images/macFFBgHack.png +0 -0
- data/docs/images/package.png +0 -0
- data/docs/images/page_green.png +0 -0
- data/docs/images/page_white_text.png +0 -0
- data/docs/images/page_white_width.png +0 -0
- data/docs/images/plugin.png +0 -0
- data/docs/images/ruby.png +0 -0
- data/docs/images/tag_blue.png +0 -0
- data/docs/images/tag_green.png +0 -0
- data/docs/images/transparent.png +0 -0
- data/docs/images/wrench.png +0 -0
- data/docs/images/wrench_orange.png +0 -0
- data/docs/images/zoom.png +0 -0
- data/docs/index.html +159 -0
- data/docs/js/darkfish.js +120 -0
- data/docs/js/navigation.js +105 -0
- data/docs/js/navigation.js.gz +0 -0
- data/docs/js/search.js +110 -0
- data/docs/js/search_index.js +1 -0
- data/docs/js/search_index.js.gz +0 -0
- data/docs/js/searcher.js +229 -0
- data/docs/js/searcher.js.gz +0 -0
- data/docs/table_of_contents.html +190 -0
- data/lib/rangops/set.rb +183 -0
- data/lib/rangops/version.rb +3 -0
- data/lib/rangops.rb +9 -0
- data/rangops.gemspec +38 -0
- metadata +141 -0
data/docs/js/searcher.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Searcher = function(data) {
|
|
2
|
+
this.data = data;
|
|
3
|
+
this.handlers = [];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
Searcher.prototype = new function() {
|
|
7
|
+
// search is performed in chunks of 1000 for non-blocking user input
|
|
8
|
+
var CHUNK_SIZE = 1000;
|
|
9
|
+
// do not try to find more than 100 results
|
|
10
|
+
var MAX_RESULTS = 100;
|
|
11
|
+
var huid = 1;
|
|
12
|
+
var suid = 1;
|
|
13
|
+
var runs = 0;
|
|
14
|
+
|
|
15
|
+
this.find = function(query) {
|
|
16
|
+
var queries = splitQuery(query);
|
|
17
|
+
var regexps = buildRegexps(queries);
|
|
18
|
+
var highlighters = buildHilighters(queries);
|
|
19
|
+
var state = { from: 0, pass: 0, limit: MAX_RESULTS, n: suid++};
|
|
20
|
+
var _this = this;
|
|
21
|
+
|
|
22
|
+
this.currentSuid = state.n;
|
|
23
|
+
|
|
24
|
+
if (!query) return;
|
|
25
|
+
|
|
26
|
+
var run = function() {
|
|
27
|
+
// stop current search thread if new search started
|
|
28
|
+
if (state.n != _this.currentSuid) return;
|
|
29
|
+
|
|
30
|
+
var results =
|
|
31
|
+
performSearch(_this.data, regexps, queries, highlighters, state);
|
|
32
|
+
var hasMore = (state.limit > 0 && state.pass < 4);
|
|
33
|
+
|
|
34
|
+
triggerResults.call(_this, results, !hasMore);
|
|
35
|
+
if (hasMore) {
|
|
36
|
+
setTimeout(run, 2);
|
|
37
|
+
}
|
|
38
|
+
runs++;
|
|
39
|
+
};
|
|
40
|
+
runs = 0;
|
|
41
|
+
|
|
42
|
+
// start search thread
|
|
43
|
+
run();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* ----- Events ------ */
|
|
47
|
+
this.ready = function(fn) {
|
|
48
|
+
fn.huid = huid;
|
|
49
|
+
this.handlers.push(fn);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ----- Utilities ------ */
|
|
53
|
+
function splitQuery(query) {
|
|
54
|
+
return query.split(/(\s+|::?|\(\)?)/).filter(function(string) {
|
|
55
|
+
return string.match(/\S/);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildRegexps(queries) {
|
|
60
|
+
return queries.map(function(query) {
|
|
61
|
+
return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i');
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildHilighters(queries) {
|
|
66
|
+
return queries.map(function(query) {
|
|
67
|
+
return query.split('').map(function(l, i) {
|
|
68
|
+
return '\u0001$' + (i*2+1) + '\u0002$' + (i*2+2);
|
|
69
|
+
}).join('');
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// function longMatchRegexp(index, longIndex, regexps) {
|
|
74
|
+
// for (var i = regexps.length - 1; i >= 0; i--){
|
|
75
|
+
// if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
|
|
76
|
+
// };
|
|
77
|
+
// return true;
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/* ----- Mathchers ------ */
|
|
82
|
+
|
|
83
|
+
/*
|
|
84
|
+
* This record matches if the index starts with queries[0] and the record
|
|
85
|
+
* matches all of the regexps
|
|
86
|
+
*/
|
|
87
|
+
function matchPassBeginning(index, longIndex, queries, regexps) {
|
|
88
|
+
if (index.indexOf(queries[0]) != 0) return false;
|
|
89
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
90
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
|
91
|
+
return false;
|
|
92
|
+
};
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* This record matches if the longIndex starts with queries[0] and the
|
|
98
|
+
* longIndex matches all of the regexps
|
|
99
|
+
*/
|
|
100
|
+
function matchPassLongIndex(index, longIndex, queries, regexps) {
|
|
101
|
+
if (longIndex.indexOf(queries[0]) != 0) return false;
|
|
102
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
103
|
+
if (!longIndex.match(regexps[i]))
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* This record matches if the index contains queries[0] and the record
|
|
111
|
+
* matches all of the regexps
|
|
112
|
+
*/
|
|
113
|
+
function matchPassContains(index, longIndex, queries, regexps) {
|
|
114
|
+
if (index.indexOf(queries[0]) == -1) return false;
|
|
115
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
116
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
|
117
|
+
return false;
|
|
118
|
+
};
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* This record matches if regexps[0] matches the index and the record
|
|
124
|
+
* matches all of the regexps
|
|
125
|
+
*/
|
|
126
|
+
function matchPassRegexp(index, longIndex, queries, regexps) {
|
|
127
|
+
if (!index.match(regexps[0])) return false;
|
|
128
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
129
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
|
130
|
+
return false;
|
|
131
|
+
};
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
/* ----- Highlighters ------ */
|
|
137
|
+
function highlightRegexp(info, queries, regexps, highlighters) {
|
|
138
|
+
var result = createResult(info);
|
|
139
|
+
for (var i=0, l = regexps.length; i < l; i++) {
|
|
140
|
+
result.title = result.title.replace(regexps[i], highlighters[i]);
|
|
141
|
+
result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
|
|
142
|
+
};
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function hltSubstring(string, pos, length) {
|
|
147
|
+
return string.substring(0, pos) + '\u0001' + string.substring(pos, pos + length) + '\u0002' + string.substring(pos + length);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function highlightQuery(info, queries, regexps, highlighters) {
|
|
151
|
+
var result = createResult(info);
|
|
152
|
+
var pos = 0;
|
|
153
|
+
var lcTitle = result.title.toLowerCase();
|
|
154
|
+
|
|
155
|
+
pos = lcTitle.indexOf(queries[0]);
|
|
156
|
+
if (pos != -1) {
|
|
157
|
+
result.title = hltSubstring(result.title, pos, queries[0].length);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
result.namespace = result.namespace.replace(regexps[0], highlighters[0]);
|
|
161
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
162
|
+
result.title = result.title.replace(regexps[i], highlighters[i]);
|
|
163
|
+
result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
|
|
164
|
+
};
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function createResult(info) {
|
|
169
|
+
var result = {};
|
|
170
|
+
result.title = info[0];
|
|
171
|
+
result.namespace = info[1];
|
|
172
|
+
result.path = info[2];
|
|
173
|
+
result.params = info[3];
|
|
174
|
+
result.snippet = info[4];
|
|
175
|
+
result.badge = info[6];
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* ----- Searching ------ */
|
|
180
|
+
function performSearch(data, regexps, queries, highlighters, state) {
|
|
181
|
+
var searchIndex = data.searchIndex;
|
|
182
|
+
var longSearchIndex = data.longSearchIndex;
|
|
183
|
+
var info = data.info;
|
|
184
|
+
var result = [];
|
|
185
|
+
var i = state.from;
|
|
186
|
+
var l = searchIndex.length;
|
|
187
|
+
var togo = CHUNK_SIZE;
|
|
188
|
+
var matchFunc, hltFunc;
|
|
189
|
+
|
|
190
|
+
while (state.pass < 4 && state.limit > 0 && togo > 0) {
|
|
191
|
+
if (state.pass == 0) {
|
|
192
|
+
matchFunc = matchPassBeginning;
|
|
193
|
+
hltFunc = highlightQuery;
|
|
194
|
+
} else if (state.pass == 1) {
|
|
195
|
+
matchFunc = matchPassLongIndex;
|
|
196
|
+
hltFunc = highlightQuery;
|
|
197
|
+
} else if (state.pass == 2) {
|
|
198
|
+
matchFunc = matchPassContains;
|
|
199
|
+
hltFunc = highlightQuery;
|
|
200
|
+
} else if (state.pass == 3) {
|
|
201
|
+
matchFunc = matchPassRegexp;
|
|
202
|
+
hltFunc = highlightRegexp;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
for (; togo > 0 && i < l && state.limit > 0; i++, togo--) {
|
|
206
|
+
if (info[i].n == state.n) continue;
|
|
207
|
+
if (matchFunc(searchIndex[i], longSearchIndex[i], queries, regexps)) {
|
|
208
|
+
info[i].n = state.n;
|
|
209
|
+
result.push(hltFunc(info[i], queries, regexps, highlighters));
|
|
210
|
+
state.limit--;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
if (searchIndex.length <= i) {
|
|
214
|
+
state.pass++;
|
|
215
|
+
i = state.from = 0;
|
|
216
|
+
} else {
|
|
217
|
+
state.from = i;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function triggerResults(results, isLast) {
|
|
224
|
+
this.handlers.forEach(function(fn) {
|
|
225
|
+
fn.call(this, results, isLast)
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
Binary file
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
|
|
8
|
+
<title>Table of Contents - Rangops</title>
|
|
9
|
+
|
|
10
|
+
<meta name="keywords" content="ruby,documentation,Table of Contents - Rangops">
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
<meta name="description" content="Table of Contents - Rangops: Rangops Rangops is a simple Ruby extension library that aims to treat Ranges as sets. It provides methods for elementary set related operations and p">
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
<script type="text/javascript">
|
|
17
|
+
var rdoc_rel_prefix = "./";
|
|
18
|
+
var index_rel_prefix = "./";
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script src="./js/navigation.js" defer></script>
|
|
22
|
+
<script src="./js/search.js" defer></script>
|
|
23
|
+
<script src="./js/search_index.js" defer></script>
|
|
24
|
+
<script src="./js/searcher.js" defer></script>
|
|
25
|
+
<script src="./js/darkfish.js" defer></script>
|
|
26
|
+
|
|
27
|
+
<link href="./css/fonts.css" rel="stylesheet">
|
|
28
|
+
<link href="./css/rdoc.css" rel="stylesheet">
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
<body id="top" class="table-of-contents">
|
|
32
|
+
<div id="navigation-toggle" role="button" tabindex="0" aria-label="Toggle sidebar" aria-expanded="true" aria-controls="navigation">
|
|
33
|
+
<span aria-hidden="true">☰</span>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
<nav id="navigation" role="navigation">
|
|
38
|
+
<div id="project-navigation">
|
|
39
|
+
<div id="home-section" role="region" title="Quick navigation" class="nav-section">
|
|
40
|
+
<h2>
|
|
41
|
+
<a href="./index.html" rel="home">Home</a>
|
|
42
|
+
</h2>
|
|
43
|
+
|
|
44
|
+
<div id="table-of-contents-navigation">
|
|
45
|
+
<a href="./table_of_contents.html#pages">Pages</a>
|
|
46
|
+
<a href="./table_of_contents.html#classes">Classes</a>
|
|
47
|
+
<a href="./table_of_contents.html#methods">Methods</a>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
<div id="search-section" role="search" class="project-section initially-hidden">
|
|
53
|
+
<form action="#" method="get" accept-charset="utf-8">
|
|
54
|
+
<div id="search-field-wrapper">
|
|
55
|
+
<input id="search-field" role="combobox" aria-label="Search"
|
|
56
|
+
aria-autocomplete="list" aria-controls="search-results"
|
|
57
|
+
type="text" name="search" placeholder="Search (/) for a class, method, ..." spellcheck="false"
|
|
58
|
+
title="Type to search, Up and Down to navigate, Enter to load">
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<ul id="search-results" aria-label="Search Results"
|
|
62
|
+
aria-busy="false" aria-expanded="false"
|
|
63
|
+
aria-atomic="false" class="initially-hidden"></ul>
|
|
64
|
+
</form>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<footer id="validator-badges" role="contentinfo">
|
|
70
|
+
<p><a href="https://validator.w3.org/check/referer">Validate</a>
|
|
71
|
+
<p>Generated by <a href="https://ruby.github.io/rdoc/">RDoc</a> 6.14.2.
|
|
72
|
+
<p>Based on <a href="http://deveiate.org/projects/Darkfish-RDoc/">Darkfish</a> by <a href="http://deveiate.org">Michael Granger</a>.
|
|
73
|
+
</footer>
|
|
74
|
+
|
|
75
|
+
</nav>
|
|
76
|
+
<main role="main">
|
|
77
|
+
<h1 class="class">Table of Contents - Rangops</h1>
|
|
78
|
+
|
|
79
|
+
<h2 id="pages">Pages</h2>
|
|
80
|
+
<ul>
|
|
81
|
+
<li class="file">
|
|
82
|
+
<a href="README_md.html">README</a>
|
|
83
|
+
|
|
84
|
+
<ul>
|
|
85
|
+
<li><a href="README_md.html#label-Rangops">Rangops</a>
|
|
86
|
+
<li><a href="README_md.html#label-License">License</a>
|
|
87
|
+
</ul>
|
|
88
|
+
</li>
|
|
89
|
+
</ul>
|
|
90
|
+
|
|
91
|
+
<h2 id="classes">Classes and Modules</h2>
|
|
92
|
+
<ul>
|
|
93
|
+
<li class="module">
|
|
94
|
+
<a href="Rangops.html">Rangops</a>
|
|
95
|
+
</li>
|
|
96
|
+
<li class="module">
|
|
97
|
+
<a href="Rangops/Set.html">Rangops::Set</a>
|
|
98
|
+
</li>
|
|
99
|
+
</ul>
|
|
100
|
+
|
|
101
|
+
<h2 id="methods">Methods</h2>
|
|
102
|
+
<ul>
|
|
103
|
+
|
|
104
|
+
<li class="method">
|
|
105
|
+
<a href="Rangops/Set.html#method-c-sort_by_boundaries">::sort_by_boundaries</a>
|
|
106
|
+
—
|
|
107
|
+
<span class="container">Rangops::Set</span>
|
|
108
|
+
|
|
109
|
+
<li class="method">
|
|
110
|
+
<a href="Rangops/Set.html#method-i-26">#&</a>
|
|
111
|
+
—
|
|
112
|
+
<span class="container">Rangops::Set</span>
|
|
113
|
+
|
|
114
|
+
<li class="method">
|
|
115
|
+
<a href="Rangops/Set.html#method-i-2B">#+</a>
|
|
116
|
+
—
|
|
117
|
+
<span class="container">Rangops::Set</span>
|
|
118
|
+
|
|
119
|
+
<li class="method">
|
|
120
|
+
<a href="Rangops/Set.html#method-i-2D">#-</a>
|
|
121
|
+
—
|
|
122
|
+
<span class="container">Rangops::Set</span>
|
|
123
|
+
|
|
124
|
+
<li class="method">
|
|
125
|
+
<a href="Rangops/Set.html#method-i-7C">#|</a>
|
|
126
|
+
—
|
|
127
|
+
<span class="container">Rangops::Set</span>
|
|
128
|
+
|
|
129
|
+
<li class="method">
|
|
130
|
+
<a href="Rangops/Set.html#method-i-complement">#complement</a>
|
|
131
|
+
—
|
|
132
|
+
<span class="container">Rangops::Set</span>
|
|
133
|
+
|
|
134
|
+
<li class="method">
|
|
135
|
+
<a href="Rangops/Set.html#method-i-contains-3F">#contains?</a>
|
|
136
|
+
—
|
|
137
|
+
<span class="container">Rangops::Set</span>
|
|
138
|
+
|
|
139
|
+
<li class="method">
|
|
140
|
+
<a href="Rangops/Set.html#method-i-difference">#difference</a>
|
|
141
|
+
—
|
|
142
|
+
<span class="container">Rangops::Set</span>
|
|
143
|
+
|
|
144
|
+
<li class="method">
|
|
145
|
+
<a href="Rangops/Set.html#method-i-disjoint-3F">#disjoint?</a>
|
|
146
|
+
—
|
|
147
|
+
<span class="container">Rangops::Set</span>
|
|
148
|
+
|
|
149
|
+
<li class="method">
|
|
150
|
+
<a href="Rangops/Set.html#method-i-intersect-3F">#intersect?</a>
|
|
151
|
+
—
|
|
152
|
+
<span class="container">Rangops::Set</span>
|
|
153
|
+
|
|
154
|
+
<li class="method">
|
|
155
|
+
<a href="Rangops/Set.html#method-i-intersection">#intersection</a>
|
|
156
|
+
—
|
|
157
|
+
<span class="container">Rangops::Set</span>
|
|
158
|
+
|
|
159
|
+
<li class="method">
|
|
160
|
+
<a href="Rangops/Set.html#method-i-is_contained_by-3F">#is_contained_by?</a>
|
|
161
|
+
—
|
|
162
|
+
<span class="container">Rangops::Set</span>
|
|
163
|
+
|
|
164
|
+
<li class="method">
|
|
165
|
+
<a href="Rangops/Set.html#method-i-proper_subset-3F">#proper_subset?</a>
|
|
166
|
+
—
|
|
167
|
+
<span class="container">Rangops::Set</span>
|
|
168
|
+
|
|
169
|
+
<li class="method">
|
|
170
|
+
<a href="Rangops/Set.html#method-i-proper_superset-3F">#proper_superset?</a>
|
|
171
|
+
—
|
|
172
|
+
<span class="container">Rangops::Set</span>
|
|
173
|
+
|
|
174
|
+
<li class="method">
|
|
175
|
+
<a href="Rangops/Set.html#method-i-subset-3F">#subset?</a>
|
|
176
|
+
—
|
|
177
|
+
<span class="container">Rangops::Set</span>
|
|
178
|
+
|
|
179
|
+
<li class="method">
|
|
180
|
+
<a href="Rangops/Set.html#method-i-superset-3F">#superset?</a>
|
|
181
|
+
—
|
|
182
|
+
<span class="container">Rangops::Set</span>
|
|
183
|
+
|
|
184
|
+
<li class="method">
|
|
185
|
+
<a href="Rangops/Set.html#method-i-union">#union</a>
|
|
186
|
+
—
|
|
187
|
+
<span class="container">Rangops::Set</span>
|
|
188
|
+
</ul>
|
|
189
|
+
</main>
|
|
190
|
+
|
data/lib/rangops/set.rb
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
module Rangops
|
|
2
|
+
|
|
3
|
+
# Module defining basic set operations that can be performed
|
|
4
|
+
# on ranges.
|
|
5
|
+
#
|
|
6
|
+
# * union
|
|
7
|
+
# * intersection
|
|
8
|
+
# * relative complement
|
|
9
|
+
# * symmetric difference
|
|
10
|
+
#
|
|
11
|
+
# along with some convenient aliases and predicates.
|
|
12
|
+
# Loosely follows conventions of `Set` module from standard library.
|
|
13
|
+
#
|
|
14
|
+
# Operations involving 2 ranges require them to overlap to produce result.
|
|
15
|
+
# If the result of operation cannot be expressed as single range,
|
|
16
|
+
# an array of ranges is returned.
|
|
17
|
+
module Set
|
|
18
|
+
|
|
19
|
+
# Union of 2 ranges. Returns a range covering sum of all elements
|
|
20
|
+
# belonging to both ranges. Returns `nil` if ranges don't overlap.
|
|
21
|
+
#
|
|
22
|
+
# (1..10).union(5..15)
|
|
23
|
+
# => 1..15
|
|
24
|
+
# (5..10) | (9..24)
|
|
25
|
+
# => 5..24
|
|
26
|
+
# (1...10) + (10..30)
|
|
27
|
+
# => nil
|
|
28
|
+
def union(other)
|
|
29
|
+
validate_args(self, other)
|
|
30
|
+
return nil unless intersect?(other)
|
|
31
|
+
|
|
32
|
+
lower, upper = Set.sort_by_boundaries(self, other)
|
|
33
|
+
Range.new(lower.begin, upper.end, upper.exclude_end?)
|
|
34
|
+
end
|
|
35
|
+
alias_method :|, :union
|
|
36
|
+
alias_method :+, :union
|
|
37
|
+
|
|
38
|
+
# Intersection of 2 ranges. Returns a range covering elements
|
|
39
|
+
# common to both ranges. Returns `nil` if ranges don't overlap.
|
|
40
|
+
#
|
|
41
|
+
# (1..10).intersection(5..15)
|
|
42
|
+
# => 5..10
|
|
43
|
+
#
|
|
44
|
+
# (5..10) & (9..24)
|
|
45
|
+
# => 9..10
|
|
46
|
+
def intersection(other)
|
|
47
|
+
validate_args(self, other)
|
|
48
|
+
return nil unless intersect?(other)
|
|
49
|
+
|
|
50
|
+
lower, upper = Set.sort_by_boundaries(self, other)
|
|
51
|
+
Range.new(upper.begin, lower.end, lower.exclude_end?)
|
|
52
|
+
end
|
|
53
|
+
alias_method :&, :intersection
|
|
54
|
+
|
|
55
|
+
# Relative complement of 2 ranges. Returns a range covering
|
|
56
|
+
# elements from `other` that are not covered by `self`.
|
|
57
|
+
# Returns `nil` if ranges don't overlap.
|
|
58
|
+
#
|
|
59
|
+
# (1..10).complement(5..15)
|
|
60
|
+
# => 10..15
|
|
61
|
+
def complement(other)
|
|
62
|
+
validate_args(self, other)
|
|
63
|
+
return nil unless intersect?(other)
|
|
64
|
+
|
|
65
|
+
_, upper = Set.sort_by_boundaries(self, other)
|
|
66
|
+
new_begin = [self.end, other.end].compact.min
|
|
67
|
+
Range.new(new_begin, upper.end, upper.exclude_end?)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Symmetric difference of 2 ranges. Returns ranges covering
|
|
71
|
+
# elements of both operands, excluding elements common for both
|
|
72
|
+
# of them. Returns `nil` if ranges don't overlap.
|
|
73
|
+
#
|
|
74
|
+
# (1..10).difference(5..15)
|
|
75
|
+
# => [1..5, 10..15]
|
|
76
|
+
#
|
|
77
|
+
# (11..19) - (15..28)
|
|
78
|
+
# => [11..15, 19..28]
|
|
79
|
+
def difference(other)
|
|
80
|
+
validate_args(self, other)
|
|
81
|
+
return nil unless intersect?(other)
|
|
82
|
+
|
|
83
|
+
lower, upper = Set.sort_by_boundaries(self, other)
|
|
84
|
+
[Range.new(lower.begin, upper.begin),
|
|
85
|
+
Range.new(lower.end, upper.end, upper.exclude_end?)]
|
|
86
|
+
end
|
|
87
|
+
alias_method :-, :difference
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Checks if 2 ranges have any common elements.
|
|
91
|
+
#
|
|
92
|
+
# (1..10).intersect?(8..15)
|
|
93
|
+
# => true
|
|
94
|
+
#
|
|
95
|
+
# (1..10).intersect?(11..15)
|
|
96
|
+
# => false
|
|
97
|
+
def intersect?(other)
|
|
98
|
+
lower, upper = Set.sort_by_boundaries(self, other)
|
|
99
|
+
lower.cover?(upper.begin) || upper.cover?(lower.end)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Opposite of `intersect?.
|
|
104
|
+
def disjoint?(other)
|
|
105
|
+
!intersect?(other)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Checks if `self` is superset of `other`, i.e. all
|
|
109
|
+
# elements of `other` fit within `self`.
|
|
110
|
+
#
|
|
111
|
+
# (1..10).superset?(2..5)
|
|
112
|
+
# => true
|
|
113
|
+
#
|
|
114
|
+
# (1..10).superset?(1..10)
|
|
115
|
+
# => true
|
|
116
|
+
#
|
|
117
|
+
# (1..10).superset?(5..12)
|
|
118
|
+
# => false
|
|
119
|
+
def superset?(other)
|
|
120
|
+
cover?(other.begin) && cover?(other.end)
|
|
121
|
+
end
|
|
122
|
+
alias_method :contains?, :superset?
|
|
123
|
+
|
|
124
|
+
# Checks if `self` is proper superset of `other`,
|
|
125
|
+
# i.e. is superset and has elements not present
|
|
126
|
+
# in `other`.
|
|
127
|
+
#
|
|
128
|
+
# (1..10).proper_superset?(2..5)
|
|
129
|
+
# => true
|
|
130
|
+
#
|
|
131
|
+
# (1..10).proper_superset?(1..10)
|
|
132
|
+
# => false
|
|
133
|
+
def proper_superset?(other)
|
|
134
|
+
superset?(other) && self != other
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Checks if `self` is subset of `other`, i.e. all
|
|
138
|
+
# elements of `self` fit within `other`.
|
|
139
|
+
#
|
|
140
|
+
# (1..10).subset?(0..12)
|
|
141
|
+
# => true
|
|
142
|
+
#
|
|
143
|
+
# (1..10).subset?(1..10)
|
|
144
|
+
# => true
|
|
145
|
+
#
|
|
146
|
+
# (1..10).subset?(5..12)
|
|
147
|
+
# => false
|
|
148
|
+
def subset?(other)
|
|
149
|
+
other.superset?(self)
|
|
150
|
+
end
|
|
151
|
+
alias_method :is_contained_by?, :subset?
|
|
152
|
+
|
|
153
|
+
# Checks if `self` is proper subset of `other`,
|
|
154
|
+
# i.e. is subset and has elements not present
|
|
155
|
+
# in `other`.
|
|
156
|
+
#
|
|
157
|
+
# (1..10).proper_subset?(0..12)
|
|
158
|
+
# => true
|
|
159
|
+
#
|
|
160
|
+
# (1..10).proper_subset?(1..10)
|
|
161
|
+
# => false
|
|
162
|
+
def proper_subset?(other)
|
|
163
|
+
subset?(other) && self != other
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
def validate_args(*args)
|
|
169
|
+
args.reject{ |a| a.is_a?(Range) }.each do |arg|
|
|
170
|
+
raise ArgumentError, "expected a Range, got #{other.class} instead"
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Determine which range has lower begin, and which one higher end.
|
|
175
|
+
def self.sort_by_boundaries(a, b)
|
|
176
|
+
ary = [a, b]
|
|
177
|
+
lower = ary.find{ |r| r.begin.nil? } || ary.sort_by(&:begin).first
|
|
178
|
+
upper = ary.find{ |r| r.end.nil? } || ary.sort_by(&:end).last
|
|
179
|
+
[lower, upper]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end
|
|
183
|
+
end
|
data/lib/rangops.rb
ADDED
data/rangops.gemspec
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require "rangops/version"
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "rangops"
|
|
8
|
+
spec.version = Rangops::VERSION
|
|
9
|
+
spec.authors = ["Bartosz Pietraszko"]
|
|
10
|
+
spec.email = ["bartosz.pietraszko@proton.me"]
|
|
11
|
+
|
|
12
|
+
spec.summary = 'Simple Ruby extension library for set operations on ranges.'
|
|
13
|
+
spec.homepage = "https://bartpiet.github.io/rangops/"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
|
18
|
+
if spec.respond_to?(:metadata)
|
|
19
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
20
|
+
else
|
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
|
22
|
+
"public gem pushes."
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Specify which files should be added to the gem when it is released.
|
|
26
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
27
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
28
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
29
|
+
end
|
|
30
|
+
spec.require_paths = ["lib"]
|
|
31
|
+
|
|
32
|
+
spec.required_ruby_version = '>= 2.7.0'
|
|
33
|
+
|
|
34
|
+
spec.add_development_dependency "bundler"
|
|
35
|
+
spec.add_development_dependency "rake"
|
|
36
|
+
spec.add_development_dependency "minitest"
|
|
37
|
+
|
|
38
|
+
end
|