UNPKG

5.71 kBJavaScriptView Raw
1/* @flow weak */
2"use strict";
3
4var isArray = Array.isArray;
5function isObject(o) {
6 /* eslint-disable no-new-object */
7 return new Object(o) === o;
8 /* eslint-enable no-new-object */
9}
10
11/* undefined-safe isNaN */
12function isNaN(n) {
13 return typeof n === "number" && n !== n;
14}
15
16/**
17 ### Utility functions
18
19 Utility functions are exposed (and documented) only to make contributions to jsverify more easy.
20 The changes here don't follow semver, i.e. there might be backward-incompatible changes even in patch releases.
21
22 Use [underscore.js](http://underscorejs.org/), [lodash](https://lodash.com/), [ramda](http://ramda.github.io/ramdocs/docs/), [lazy.js](http://danieltao.com/lazy.js/) or some other utility belt.
23*/
24
25/* Simple sort */
26function sort(arr) {
27 var res = arr.slice();
28 res.sort();
29 return res;
30}
31
32/**
33 - `utils.isEqual(x: json, y: json): bool`
34
35 Equality test for `json` objects.
36*/
37function isEqual(a, b) {
38 var i;
39
40 if (isNaN(a) && isNaN(b)) {
41 return true;
42 }
43
44 if (a === b) {
45 return true;
46 } else if (isArray(a) && isArray(b) && a.length === b.length) {
47 for (i = 0; i < a.length; i++) {
48 if (!isEqual(a[i], b[i])) {
49 return false;
50 }
51 }
52 return true;
53 } else if (isObject(a) && isObject(b) && !isArray(a) && !isArray(b)) {
54 var akeys = Object.keys(a);
55 var bkeys = Object.keys(b);
56 if (!isEqual(sort(akeys), sort(bkeys))) {
57 return false;
58 }
59
60 for (i = 0; i < akeys.length; i++) {
61 if (!isEqual(a[akeys[i]], b[akeys[i]])) {
62 return false;
63 }
64 }
65 return true;
66 }
67
68 return false;
69}
70
71/**
72 - `utils.isApproxEqual(x: a, y: b, opts: obj): bool`
73
74 Tests whether two objects are approximately and optimistically equal.
75 Returns `false` only if they are distinguishable not equal.
76 Returns `true` when `x` and `y` are `NaN`.
77 This function works with cyclic data.
78
79 Takes optional 'opts' parameter with properties:
80
81 - `fnEqual` - whether all functions are considered equal (default: yes)
82 - `depth` - how deep to recurse until treating as equal (default: 5)
83*/
84function isApproxEqual(x, y, opts) {
85 opts = opts || {};
86 var fnEqual = opts.fnEqual !== false;
87 var depth = opts.depth || 5; // totally arbitrary
88
89 // state contains pairs we checked (or are still checking, but assume equal!)
90 var state = [];
91
92 function loop(a, b, n) {
93 if (isNaN(a) && isNaN(b)) {
94 return true;
95 }
96
97 // trivial check
98 if (a === b) {
99 return true;
100 }
101
102 // depth check
103 if (n >= depth) {
104 return true;
105 }
106
107 var i;
108
109 // check if pair already occured
110 for (i = 0; i < state.length; i++) {
111 if (state[i][0] === a && state[i][1] === b) {
112 return true;
113 }
114 }
115
116 // add to state
117 state.push([a, b]);
118
119 if (typeof a === "function" && typeof b === "function") {
120 return fnEqual;
121 }
122
123 if (isArray(a) && isArray(b) && a.length === b.length) {
124 for (i = 0; i < a.length; i++) {
125 if (!loop(a[i], b[i], n + 1)) {
126 return false;
127 }
128 }
129 return true;
130 } else if (isObject(a) && isObject(b) && !isArray(a) && !isArray(b)) {
131 var akeys = Object.keys(a);
132 var bkeys = Object.keys(b);
133 if (!loop(sort(akeys), sort(bkeys), n + 1)) {
134 return false;
135 }
136
137 for (i = 0; i < akeys.length; i++) {
138 if (!loop(a[akeys[i]], b[akeys[i]], n + 1)) {
139 return false;
140 }
141 }
142 return true;
143 }
144
145 return false;
146 }
147 return loop(x, y, 0);
148}
149
150function identity(x) {
151 return x;
152}
153
154function pluck(arr, key) {
155 return arr.map(function (e) {
156 return e[key];
157 });
158}
159
160/**
161 - `utils.force(x: a | () -> a) : a`
162
163 Evaluate `x` as nullary function, if it is one.
164*/
165function force(arb) {
166 return (typeof arb === "function") ? arb() : arb;
167}
168
169/**
170 - `utils.merge(x... : obj): obj`
171
172 Merge two objects, a bit like `_.extend({}, x, y)`.
173*/
174function merge() {
175 var res = {};
176
177 for (var i = 0; i < arguments.length; i++) {
178 var arg = arguments[i];
179 var keys = Object.keys(arg);
180 for (var j = 0; j < keys.length; j++) {
181 var key = keys[j];
182 res[key] = arg[key];
183 }
184 }
185
186 return res;
187}
188
189function div2(x) {
190 return Math.floor(x / 2);
191}
192
193function log2(x) {
194 return Math.log(x) / Math.log(2);
195}
196
197function ilog2(x) {
198 return x <= 0 ? 0 : Math.floor(log2(x));
199}
200
201function curriedN(n) {
202 var n1 = n - 1;
203 return function curriedNInstance(result, args) {
204 if (args.length === n) {
205 return result(args[n1]);
206 } else {
207 return result;
208 }
209 };
210}
211
212var curried2 = curriedN(2);
213var curried3 = curriedN(3);
214
215function charArrayToString(arr) {
216 return arr.join("");
217}
218
219function stringToCharArray(str) {
220 return str.split("");
221}
222
223function pairArrayToDict(arrayOfPairs) {
224 var res = {};
225 arrayOfPairs.forEach(function (p) {
226 res[p[0]] = p[1];
227 });
228 return res;
229}
230
231function dictToPairArray(m) {
232 var res = [];
233 Object.keys(m).forEach(function (k) {
234 res.push([k, m[k]]);
235 });
236 return res;
237}
238
239function partition(arr, pred) {
240 var truthy = [];
241 var falsy = [];
242
243 for (var i = 0; i < arr.length; i++) {
244 var x = arr[i];
245 if (pred(x)) {
246 truthy.push(x);
247 } else {
248 falsy.push(x);
249 }
250 }
251
252 return [truthy, falsy];
253}
254
255module.exports = {
256 isArray: isArray,
257 isObject: isObject,
258 isEqual: isEqual,
259 isApproxEqual: isApproxEqual,
260 identity: identity,
261 pluck: pluck,
262 force: force,
263 merge: merge,
264 div2: div2,
265 ilog2: ilog2,
266 curried2: curried2,
267 curried3: curried3,
268 charArrayToString: charArrayToString,
269 stringToCharArray: stringToCharArray,
270 pairArrayToDict: pairArrayToDict,
271 dictToPairArray: dictToPairArray,
272 partition: partition,
273};