@gkucmierz/utils 2.0.7 → 2.1.0
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.
- package/README.md +4 -0
- package/main.mjs +9 -1
- package/package.json +6 -1
- package/src/langtons-ant.mjs +79 -0
- package/src/math3d.mjs +4 -4
- package/src/rand-normal.mjs +7 -0
- package/src/set-safe-interval.mjs +44 -0
package/README.md
CHANGED
|
@@ -48,6 +48,9 @@ This library provides a wide range of mathematical functions and data structures
|
|
|
48
48
|
- `heronsFormula`: Triangle area calculation.
|
|
49
49
|
- `squareRoot`: Integer square root using Newton's method.
|
|
50
50
|
|
|
51
|
+
- **Automata & Simulation**:
|
|
52
|
+
- `createLangtonsAnt`, `createUnlimitedGrid`: Infinite 2D grid and Langton's Ant cellular automaton engine (Project Euler 349 compatible).
|
|
53
|
+
|
|
51
54
|
- **String & Encoding & Arrays**:
|
|
52
55
|
- `base64`: Base64 and Base64Url encoding/decoding.
|
|
53
56
|
- `copyCase`: Match case of a string to another.
|
|
@@ -55,6 +58,7 @@ This library provides a wide range of mathematical functions and data structures
|
|
|
55
58
|
- `bijectiveNumeration`: Bijective base-k numeration system.
|
|
56
59
|
|
|
57
60
|
- **Developer Utilities**:
|
|
61
|
+
- `setSafeInterval`: A safe, asynchronous alternative to `setInterval` that prevents overlapping executions and supports immediate yielding.
|
|
58
62
|
- `consumeIteratorNonBlocking`: Yield consumption macro-tasks queue resolver for heavy computations.
|
|
59
63
|
- `memoize`: Function memoization based on arguments.
|
|
60
64
|
- `binarySearch`: Various binary search implementations (exact, lower bound, upper bound).
|
package/main.mjs
CHANGED
|
@@ -53,6 +53,9 @@ import {
|
|
|
53
53
|
import {
|
|
54
54
|
heronsFormula, heronsFormulaBI
|
|
55
55
|
} from './src/herons-formula.mjs'
|
|
56
|
+
import {
|
|
57
|
+
createLangtonsAnt, createUnlimitedGrid
|
|
58
|
+
} from './src/langtons-ant.mjs'
|
|
56
59
|
import {
|
|
57
60
|
lcm, lcmBI
|
|
58
61
|
} from './src/lcm.mjs'
|
|
@@ -101,6 +104,9 @@ import {
|
|
|
101
104
|
import {
|
|
102
105
|
array2range, range2array
|
|
103
106
|
} from './src/range-array.mjs'
|
|
107
|
+
import {
|
|
108
|
+
setSafeInterval
|
|
109
|
+
} from './src/set-safe-interval.mjs'
|
|
104
110
|
import {
|
|
105
111
|
simulatedAnnealing
|
|
106
112
|
} from './src/simulated-annealing.mjs'
|
|
@@ -129,6 +135,7 @@ export * from './src/gpn.mjs';
|
|
|
129
135
|
export * from './src/gray-code.mjs';
|
|
130
136
|
export * from './src/heap.mjs';
|
|
131
137
|
export * from './src/herons-formula.mjs';
|
|
138
|
+
export * from './src/langtons-ant.mjs';
|
|
132
139
|
export * from './src/lcm.mjs';
|
|
133
140
|
export * from './src/list-node.mjs';
|
|
134
141
|
export * from './src/lucas-lehmer.mjs';
|
|
@@ -145,10 +152,11 @@ export * from './src/phi.mjs';
|
|
|
145
152
|
export * from './src/pow-mod.mjs';
|
|
146
153
|
export * from './src/rand-normal.mjs';
|
|
147
154
|
export * from './src/range-array.mjs';
|
|
155
|
+
export * from './src/set-safe-interval.mjs';
|
|
148
156
|
export * from './src/simulated-annealing.mjs';
|
|
149
157
|
export * from './src/square-root.mjs';
|
|
150
158
|
export * from './src/tonelli-shanks.mjs';
|
|
151
159
|
|
|
152
160
|
export default [
|
|
153
|
-
SetCnt, Trie, arrayHistogram, fromBase64, fromBase64Url, toBase64, toBase64Url, bijective2num, bijective2numBI, num2bijective, num2bijectiveBI, binarySearchArr, binarySearchGE, binarySearchLE, binarySearchRangeIncl, combinations, combinationsIterator, consumeIteratorNonBlocking, copyCase, egcd, factors, factorsBI, formatBigNumber, formatBigNumberBI, wrapFn, gcd, gcdBI, getType, gpn, gpnBI, bin2gray, gray2bin, Heap, heronsFormula, heronsFormulaBI, lcm, lcmBI, ListNode, lucasLehmerBI, axisAngleToMatrix4, crossProduct, dotProduct, getRotationMatrixFromVectors, multiplyMatrix4, normalize, projectToTrackball, matrixAsArray, memoize, mod, modBI, nChooseK, naturalSearch, nelderMead, particleSwarmOptimization, permutations, permutationsIterator, phi, phiBI, powMod, powModBI, randNormal, array2range, range2array, simulatedAnnealing, squareRoot, squareRootBI, tonelliShanksBI
|
|
161
|
+
SetCnt, Trie, arrayHistogram, fromBase64, fromBase64Url, toBase64, toBase64Url, bijective2num, bijective2numBI, num2bijective, num2bijectiveBI, binarySearchArr, binarySearchGE, binarySearchLE, binarySearchRangeIncl, combinations, combinationsIterator, consumeIteratorNonBlocking, copyCase, egcd, factors, factorsBI, formatBigNumber, formatBigNumberBI, wrapFn, gcd, gcdBI, getType, gpn, gpnBI, bin2gray, gray2bin, Heap, heronsFormula, heronsFormulaBI, createLangtonsAnt, createUnlimitedGrid, lcm, lcmBI, ListNode, lucasLehmerBI, axisAngleToMatrix4, crossProduct, dotProduct, getRotationMatrixFromVectors, multiplyMatrix4, normalize, projectToTrackball, matrixAsArray, memoize, mod, modBI, nChooseK, naturalSearch, nelderMead, particleSwarmOptimization, permutations, permutationsIterator, phi, phiBI, powMod, powModBI, randNormal, array2range, range2array, setSafeInterval, simulatedAnnealing, squareRoot, squareRootBI, tonelliShanksBI
|
|
154
162
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gkucmierz/utils",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Usefull functions for solving programming tasks",
|
|
6
6
|
"keywords": [
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
"iterator",
|
|
20
20
|
"javascript",
|
|
21
21
|
"lcm",
|
|
22
|
+
"langtons-ant",
|
|
22
23
|
"lucas-lehmer",
|
|
23
24
|
"math",
|
|
25
|
+
"cellular-automaton",
|
|
24
26
|
"memoize",
|
|
25
27
|
"mersenne",
|
|
26
28
|
"mod",
|
|
@@ -34,6 +36,9 @@
|
|
|
34
36
|
"primality-test",
|
|
35
37
|
"prime",
|
|
36
38
|
"range-array",
|
|
39
|
+
"set-safe-interval",
|
|
40
|
+
"setinterval",
|
|
41
|
+
"settimeout",
|
|
37
42
|
"square-root",
|
|
38
43
|
"tonelli-shanks",
|
|
39
44
|
"trie",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an unlimited, dynamically expanding 2D grid using nested Maps.
|
|
3
|
+
* Ideal for sparse matrices or algorithms operating on an infinite plane (e.g., Langton's Ant, Game of Life).
|
|
4
|
+
*
|
|
5
|
+
* @returns {Object} An object containing `get(x, y)` and `set(x, y, val)` methods.
|
|
6
|
+
*/
|
|
7
|
+
export const createUnlimitedGrid = () => {
|
|
8
|
+
const grid = new Map();
|
|
9
|
+
|
|
10
|
+
const getRow = y => {
|
|
11
|
+
let row;
|
|
12
|
+
if (!(row = grid.get(y))) {
|
|
13
|
+
row = new Map();
|
|
14
|
+
grid.set(y, row);
|
|
15
|
+
}
|
|
16
|
+
return row;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const set = (x, y, val) => {
|
|
20
|
+
const row = getRow(y);
|
|
21
|
+
row.set(x, val);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const get = (x, y) => {
|
|
25
|
+
const row = getRow(y);
|
|
26
|
+
return row.get(x) || 0;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return { set, get };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initializes a Langton's Ant automaton on a given grid.
|
|
34
|
+
* The ant follows standard rules: turns right on 0, turns left on 1, and flips the cell state.
|
|
35
|
+
*
|
|
36
|
+
* @param {Object} grid - The grid interface containing `get(x, y)` and `set(x, y, val)` methods.
|
|
37
|
+
* @param {number} startX - The initial X coordinate of the ant.
|
|
38
|
+
* @param {number} startY - The initial Y coordinate of the ant.
|
|
39
|
+
* @param {number} [initialDir=-1] - The initial direction (0: TOP, 1: RIGHT, 2: BOTTOM, 3: LEFT). If -1, a random direction is chosen.
|
|
40
|
+
* @returns {Object} An object containing a `step()` method which advances the simulation by one tick and returns `{x, y, state}` of the modified cell.
|
|
41
|
+
*/
|
|
42
|
+
export const createLangtonsAnt = (grid, startX, startY, initialDir = -1) => {
|
|
43
|
+
const [X, Y] = [0, 1];
|
|
44
|
+
const [TOP, RIGHT, BOT, LEFT] = [0, 1, 2, 3];
|
|
45
|
+
const coordMap = new Map([
|
|
46
|
+
[TOP, [ 0,-1]],
|
|
47
|
+
[RIGHT, [ 1, 0]],
|
|
48
|
+
[BOT, [ 0, 1]],
|
|
49
|
+
[LEFT, [-1, 0]],
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
let dir = initialDir !== -1 ? initialDir : Math.floor(Math.random() * 4);
|
|
53
|
+
const pos = [startX, startY];
|
|
54
|
+
|
|
55
|
+
const step = () => {
|
|
56
|
+
const currentState = grid.get(pos[X], pos[Y]);
|
|
57
|
+
const nextState = currentState === 0 ? 1 : 0;
|
|
58
|
+
|
|
59
|
+
grid.set(pos[X], pos[Y], nextState);
|
|
60
|
+
|
|
61
|
+
if (currentState === 0) {
|
|
62
|
+
dir = (dir + 1) % 4; // Turn Right
|
|
63
|
+
} else {
|
|
64
|
+
dir = (dir + 3) % 4; // Turn Left
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const move = coordMap.get(dir);
|
|
68
|
+
pos[X] += move[X];
|
|
69
|
+
pos[Y] += move[Y];
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
x: pos[X] - move[X],
|
|
73
|
+
y: pos[Y] - move[Y],
|
|
74
|
+
state: nextState
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return { step };
|
|
79
|
+
};
|
package/src/math3d.mjs
CHANGED
|
@@ -20,9 +20,9 @@ export const projectToTrackball = (x, y, radius = 1) => {
|
|
|
20
20
|
const d2 = x*x + y*y;
|
|
21
21
|
const r2 = radius * radius;
|
|
22
22
|
if (d2 <= r2 * 0.5) {
|
|
23
|
-
|
|
23
|
+
return normalize([x, y, Math.sqrt(r2 - d2)]);
|
|
24
24
|
} else {
|
|
25
|
-
|
|
25
|
+
return normalize([x, y, (r2 * 0.5) / Math.sqrt(d2)]);
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
|
|
@@ -47,7 +47,7 @@ export const multiplyMatrix4 = (a, b) => {
|
|
|
47
47
|
for (let r = 0; r < 4; r++) {
|
|
48
48
|
let sum = 0;
|
|
49
49
|
for (let i = 0; i < 4; i++) {
|
|
50
|
-
|
|
50
|
+
sum += a[i*4 + r] * b[c*4 + i]; // Note: column-major alignment
|
|
51
51
|
}
|
|
52
52
|
out[c*4 + r] = sum;
|
|
53
53
|
}
|
|
@@ -58,7 +58,7 @@ export const multiplyMatrix4 = (a, b) => {
|
|
|
58
58
|
export const getRotationMatrixFromVectors = (u, v) => {
|
|
59
59
|
const axis = crossProduct(u, v);
|
|
60
60
|
if (axis[0] === 0 && axis[1] === 0 && axis[2] === 0) {
|
|
61
|
-
|
|
61
|
+
return [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; // Identity
|
|
62
62
|
}
|
|
63
63
|
const dot = dotProduct(u, v);
|
|
64
64
|
const angle = Math.acos(Math.max(-1, Math.min(1, dot)));
|
package/src/rand-normal.mjs
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a random number from a normal (Gaussian) distribution.
|
|
3
|
+
* Uses the Box-Muller transform.
|
|
4
|
+
* @param {number} [mean=0] - The mean of the normal distribution.
|
|
5
|
+
* @param {number} [sigma=1] - The standard deviation of the normal distribution.
|
|
6
|
+
* @returns {number} A random number from the specified normal distribution.
|
|
7
|
+
*/
|
|
1
8
|
export const randNormal = (mean = 0, sigma = 1) => {
|
|
2
9
|
const y1 = Math.random();
|
|
3
10
|
const y2 = Math.random();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A safe alternative to setInterval that uses recursive setTimeout.
|
|
3
|
+
* It waits for the callback (even if it's async) to finish before scheduling the next tick,
|
|
4
|
+
* preventing overlapping executions and event loop blocking.
|
|
5
|
+
*
|
|
6
|
+
* @param {Function} cb - Function to execute (can be async)
|
|
7
|
+
* @param {number} delay - Delay in ms between executions
|
|
8
|
+
* @param {boolean} [immediate=true] - Whether to fire the first execution immediately
|
|
9
|
+
* @param {boolean} [nextTick=false] - If immediate is true, whether to yield to the event loop before the first execution
|
|
10
|
+
* @returns {Function} A function to clear the interval
|
|
11
|
+
*/
|
|
12
|
+
export const setSafeInterval = (cb, delay, immediate = true, nextTick = false) => {
|
|
13
|
+
let isCleared = false;
|
|
14
|
+
let timerId = null;
|
|
15
|
+
|
|
16
|
+
const loop = async () => {
|
|
17
|
+
if (isCleared) return;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
await cb();
|
|
21
|
+
} catch (e) {
|
|
22
|
+
console.error('SafeInterval callback error:', e);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!isCleared) {
|
|
26
|
+
timerId = setTimeout(loop, delay);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (immediate) {
|
|
31
|
+
if (nextTick) {
|
|
32
|
+
timerId = setTimeout(loop, 0);
|
|
33
|
+
} else {
|
|
34
|
+
loop();
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
timerId = setTimeout(loop, delay);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return () => {
|
|
41
|
+
isCleared = true;
|
|
42
|
+
clearTimeout(timerId);
|
|
43
|
+
};
|
|
44
|
+
};
|