@aleph-ai/tinyaleph 1.3.0 → 1.4.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 +423 -12
- package/backends/cryptographic/index.js +455 -2
- package/core/beacon.js +735 -0
- package/core/crt-homology.js +1004 -0
- package/core/enochian-vocabulary.js +910 -0
- package/core/enochian.js +744 -0
- package/core/errors.js +587 -0
- package/core/hilbert.js +651 -1
- package/core/index.js +86 -1
- package/core/lambda.js +284 -33
- package/core/logger.js +350 -0
- package/core/prime.js +136 -1
- package/core/quaternion-semantics.js +623 -0
- package/core/reduction.js +391 -1
- package/core/rformer-crt.js +892 -0
- package/core/topology.js +655 -0
- package/docs/README.md +54 -0
- package/docs/reference/07-topology.md +257 -0
- package/docs/reference/08-observer.md +421 -0
- package/docs/reference/09-crt-homology.md +369 -0
- package/modular.js +231 -3
- package/package.json +1 -1
package/core/logger.js
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Logger for TinyAleph
|
|
3
|
+
*
|
|
4
|
+
* Provides structured logging with:
|
|
5
|
+
* - Multiple log levels (TRACE through FATAL)
|
|
6
|
+
* - JSON and text output formats
|
|
7
|
+
* - Child logger namespaces
|
|
8
|
+
* - Log history for analysis
|
|
9
|
+
* - Event emission for log aggregation
|
|
10
|
+
*
|
|
11
|
+
* Browser-compatible: Uses console API instead of Node.js streams.
|
|
12
|
+
* Extracted from apps/sentient/lib/error-handler.js for library reuse.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { LogLevel, LogLevelNames, SimpleEventEmitter } = require('./errors');
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// LOGGER
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Structured Logger
|
|
23
|
+
*/
|
|
24
|
+
class Logger extends SimpleEventEmitter {
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
super();
|
|
27
|
+
|
|
28
|
+
this.name = options.name || 'aleph';
|
|
29
|
+
this.level = options.level ?? LogLevel.INFO;
|
|
30
|
+
this.format = options.format || 'text'; // text, json
|
|
31
|
+
this.colorize = options.colorize ?? true;
|
|
32
|
+
this.includeTimestamp = options.includeTimestamp ?? true;
|
|
33
|
+
this.includeLevel = options.includeLevel ?? true;
|
|
34
|
+
|
|
35
|
+
// Custom output functions (defaults to console)
|
|
36
|
+
this.output = options.output || {
|
|
37
|
+
log: (...args) => console.log(...args),
|
|
38
|
+
error: (...args) => console.error(...args),
|
|
39
|
+
warn: (...args) => console.warn(...args),
|
|
40
|
+
info: (...args) => console.info(...args),
|
|
41
|
+
debug: (...args) => console.debug(...args)
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Log history for error aggregation
|
|
45
|
+
this.history = [];
|
|
46
|
+
this.maxHistory = options.maxHistory || 1000;
|
|
47
|
+
|
|
48
|
+
// Child loggers
|
|
49
|
+
this.children = new Map();
|
|
50
|
+
|
|
51
|
+
// Colors (browser console supports CSS, Node uses ANSI)
|
|
52
|
+
this.isBrowser = typeof window !== 'undefined';
|
|
53
|
+
this.ansiColors = {
|
|
54
|
+
trace: '\x1b[90m',
|
|
55
|
+
debug: '\x1b[36m',
|
|
56
|
+
info: '\x1b[32m',
|
|
57
|
+
warn: '\x1b[33m',
|
|
58
|
+
error: '\x1b[31m',
|
|
59
|
+
fatal: '\x1b[35m',
|
|
60
|
+
reset: '\x1b[0m'
|
|
61
|
+
};
|
|
62
|
+
this.cssColors = {
|
|
63
|
+
trace: 'color: #888',
|
|
64
|
+
debug: 'color: #0aa',
|
|
65
|
+
info: 'color: #0a0',
|
|
66
|
+
warn: 'color: #aa0',
|
|
67
|
+
error: 'color: #a00',
|
|
68
|
+
fatal: 'color: #a0a'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create a child logger with a specific namespace
|
|
74
|
+
* @param {string} namespace - Child namespace
|
|
75
|
+
* @returns {Logger}
|
|
76
|
+
*/
|
|
77
|
+
child(namespace) {
|
|
78
|
+
if (this.children.has(namespace)) {
|
|
79
|
+
return this.children.get(namespace);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const child = new Logger({
|
|
83
|
+
name: `${this.name}:${namespace}`,
|
|
84
|
+
level: this.level,
|
|
85
|
+
format: this.format,
|
|
86
|
+
colorize: this.colorize,
|
|
87
|
+
output: this.output
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Forward events to parent
|
|
91
|
+
child.on('log', (entry) => {
|
|
92
|
+
this.history.push(entry);
|
|
93
|
+
if (this.history.length > this.maxHistory) {
|
|
94
|
+
this.history.shift();
|
|
95
|
+
}
|
|
96
|
+
this.emit('log', entry);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
this.children.set(namespace, child);
|
|
100
|
+
return child;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Log at a specific level
|
|
105
|
+
* @param {number} level - Log level
|
|
106
|
+
* @param {string} message - Log message
|
|
107
|
+
* @param {Object} data - Additional data
|
|
108
|
+
*/
|
|
109
|
+
log(level, message, data = {}) {
|
|
110
|
+
if (level < this.level) return;
|
|
111
|
+
|
|
112
|
+
const entry = {
|
|
113
|
+
timestamp: new Date().toISOString(),
|
|
114
|
+
level: LogLevelNames[level].toLowerCase(),
|
|
115
|
+
name: this.name,
|
|
116
|
+
message,
|
|
117
|
+
data: Object.keys(data).length > 0 ? data : undefined
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Add to history
|
|
121
|
+
this.history.push(entry);
|
|
122
|
+
if (this.history.length > this.maxHistory) {
|
|
123
|
+
this.history.shift();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Format and output
|
|
127
|
+
this.writeOutput(entry, level);
|
|
128
|
+
|
|
129
|
+
// Emit event
|
|
130
|
+
this.emit('log', entry);
|
|
131
|
+
|
|
132
|
+
return entry;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Write log entry to output
|
|
137
|
+
* @param {Object} entry - Log entry
|
|
138
|
+
* @param {number} level - Log level number
|
|
139
|
+
*/
|
|
140
|
+
writeOutput(entry, level) {
|
|
141
|
+
if (this.format === 'json') {
|
|
142
|
+
const output = JSON.stringify(entry);
|
|
143
|
+
if (level >= LogLevel.ERROR) {
|
|
144
|
+
this.output.error(output);
|
|
145
|
+
} else {
|
|
146
|
+
this.output.log(output);
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Text format
|
|
152
|
+
if (this.isBrowser && this.colorize) {
|
|
153
|
+
this.writeBrowserColorized(entry, level);
|
|
154
|
+
} else if (this.colorize) {
|
|
155
|
+
this.writeAnsiColorized(entry, level);
|
|
156
|
+
} else {
|
|
157
|
+
this.writePlain(entry, level);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Write colorized output for browser console
|
|
163
|
+
*/
|
|
164
|
+
writeBrowserColorized(entry, level) {
|
|
165
|
+
const parts = [];
|
|
166
|
+
const styles = [];
|
|
167
|
+
|
|
168
|
+
if (this.includeTimestamp) {
|
|
169
|
+
parts.push(`%c[${entry.timestamp}]`);
|
|
170
|
+
styles.push('color: #666');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (this.includeLevel) {
|
|
174
|
+
const levelStr = entry.level.toUpperCase().padEnd(5);
|
|
175
|
+
parts.push(`%c${levelStr}`);
|
|
176
|
+
styles.push(this.cssColors[entry.level] || '');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
parts.push(`%c[${entry.name}]`);
|
|
180
|
+
styles.push('color: #888');
|
|
181
|
+
|
|
182
|
+
parts.push(`%c${entry.message}`);
|
|
183
|
+
styles.push('color: inherit');
|
|
184
|
+
|
|
185
|
+
const formatStr = parts.join(' ');
|
|
186
|
+
const args = [formatStr, ...styles];
|
|
187
|
+
|
|
188
|
+
if (entry.data) {
|
|
189
|
+
args.push(entry.data);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (level >= LogLevel.ERROR) {
|
|
193
|
+
this.output.error(...args);
|
|
194
|
+
} else if (level >= LogLevel.WARN) {
|
|
195
|
+
this.output.warn(...args);
|
|
196
|
+
} else if (level >= LogLevel.INFO) {
|
|
197
|
+
this.output.info(...args);
|
|
198
|
+
} else {
|
|
199
|
+
this.output.debug(...args);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Write ANSI colorized output for Node.js terminal
|
|
205
|
+
*/
|
|
206
|
+
writeAnsiColorized(entry, level) {
|
|
207
|
+
const parts = [];
|
|
208
|
+
|
|
209
|
+
if (this.includeTimestamp) {
|
|
210
|
+
parts.push(`[${entry.timestamp}]`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (this.includeLevel) {
|
|
214
|
+
const levelStr = entry.level.toUpperCase().padEnd(5);
|
|
215
|
+
const color = this.ansiColors[entry.level] || '';
|
|
216
|
+
parts.push(`${color}${levelStr}${this.ansiColors.reset}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
parts.push(`[${entry.name}]`);
|
|
220
|
+
parts.push(entry.message);
|
|
221
|
+
|
|
222
|
+
if (entry.data) {
|
|
223
|
+
parts.push(JSON.stringify(entry.data));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const output = parts.join(' ');
|
|
227
|
+
|
|
228
|
+
if (level >= LogLevel.ERROR) {
|
|
229
|
+
this.output.error(output);
|
|
230
|
+
} else {
|
|
231
|
+
this.output.log(output);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Write plain text output
|
|
237
|
+
*/
|
|
238
|
+
writePlain(entry, level) {
|
|
239
|
+
const parts = [];
|
|
240
|
+
|
|
241
|
+
if (this.includeTimestamp) {
|
|
242
|
+
parts.push(`[${entry.timestamp}]`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (this.includeLevel) {
|
|
246
|
+
parts.push(entry.level.toUpperCase().padEnd(5));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
parts.push(`[${entry.name}]`);
|
|
250
|
+
parts.push(entry.message);
|
|
251
|
+
|
|
252
|
+
if (entry.data) {
|
|
253
|
+
parts.push(JSON.stringify(entry.data));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const output = parts.join(' ');
|
|
257
|
+
|
|
258
|
+
if (level >= LogLevel.ERROR) {
|
|
259
|
+
this.output.error(output);
|
|
260
|
+
} else {
|
|
261
|
+
this.output.log(output);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Convenience methods
|
|
266
|
+
trace(message, data) { return this.log(LogLevel.TRACE, message, data); }
|
|
267
|
+
debug(message, data) { return this.log(LogLevel.DEBUG, message, data); }
|
|
268
|
+
info(message, data) { return this.log(LogLevel.INFO, message, data); }
|
|
269
|
+
warn(message, data) { return this.log(LogLevel.WARN, message, data); }
|
|
270
|
+
error(message, data) { return this.log(LogLevel.ERROR, message, data); }
|
|
271
|
+
fatal(message, data) { return this.log(LogLevel.FATAL, message, data); }
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Set log level
|
|
275
|
+
* @param {number|string} level - New level
|
|
276
|
+
*/
|
|
277
|
+
setLevel(level) {
|
|
278
|
+
if (typeof level === 'string') {
|
|
279
|
+
this.level = LogLevel[level.toUpperCase()] ?? LogLevel.INFO;
|
|
280
|
+
} else {
|
|
281
|
+
this.level = level;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get recent log entries
|
|
287
|
+
* @param {number} count - Number of entries
|
|
288
|
+
* @param {string} level - Optional level filter
|
|
289
|
+
* @returns {Array}
|
|
290
|
+
*/
|
|
291
|
+
getRecent(count = 100, level = null) {
|
|
292
|
+
let entries = this.history.slice(-count);
|
|
293
|
+
if (level) {
|
|
294
|
+
entries = entries.filter(e => e.level === level);
|
|
295
|
+
}
|
|
296
|
+
return entries;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get error summary
|
|
301
|
+
* @returns {Object}
|
|
302
|
+
*/
|
|
303
|
+
getErrorSummary() {
|
|
304
|
+
const errors = this.history.filter(e => e.level === 'error' || e.level === 'fatal');
|
|
305
|
+
const categories = {};
|
|
306
|
+
|
|
307
|
+
for (const error of errors) {
|
|
308
|
+
const cat = error.data?.category || 'unknown';
|
|
309
|
+
categories[cat] = (categories[cat] || 0) + 1;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
totalErrors: errors.length,
|
|
314
|
+
byCategory: categories,
|
|
315
|
+
recentErrors: errors.slice(-10)
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Clear log history
|
|
321
|
+
*/
|
|
322
|
+
clearHistory() {
|
|
323
|
+
this.history = [];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Create a namespaced logger
|
|
329
|
+
* @param {string} namespace - Logger namespace
|
|
330
|
+
* @param {Object} options - Logger options
|
|
331
|
+
* @returns {Logger}
|
|
332
|
+
*/
|
|
333
|
+
function createLogger(namespace, options = {}) {
|
|
334
|
+
return new Logger({
|
|
335
|
+
name: namespace,
|
|
336
|
+
...options
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ============================================================================
|
|
341
|
+
// EXPORTS
|
|
342
|
+
// ============================================================================
|
|
343
|
+
|
|
344
|
+
module.exports = {
|
|
345
|
+
Logger,
|
|
346
|
+
createLogger,
|
|
347
|
+
// Re-export LogLevel for convenience
|
|
348
|
+
LogLevel,
|
|
349
|
+
LogLevelNames
|
|
350
|
+
};
|
package/core/prime.js
CHANGED
|
@@ -175,10 +175,145 @@ function firstNPrimes(n) {
|
|
|
175
175
|
// Default prime list (first 100 primes)
|
|
176
176
|
const DEFAULT_PRIMES = firstNPrimes(100);
|
|
177
177
|
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// 108 INVARIANT (from 108bio.pdf - Twist Eigenstates and Topological Morphogenesis)
|
|
180
|
+
// ============================================================================
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The 108 Invariant - The minimal self-referential twist cavity
|
|
184
|
+
*
|
|
185
|
+
* 108 = 2² × 3³ is identified as the fundamental invariant for:
|
|
186
|
+
* - Standard Model gauge groups (SU(3) × SU(2) × U(1))
|
|
187
|
+
* - Biological self-organization (pentagon angle 108°)
|
|
188
|
+
* - Consciousness as free energy minimization
|
|
189
|
+
*
|
|
190
|
+
* Key relationships:
|
|
191
|
+
* - Trefoil complexity (17) × 108 = 1836 (proton/electron mass ratio)
|
|
192
|
+
* - 108 + 29 (mod-30 prime sieve boundary) = 137 (fine structure constant inverse)
|
|
193
|
+
* - 5³ = 125 GeV (Higgs mass prediction)
|
|
194
|
+
*/
|
|
195
|
+
const TWIST_108 = {
|
|
196
|
+
// Fundamental value
|
|
197
|
+
value: 108,
|
|
198
|
+
|
|
199
|
+
// Prime factorization: 2² × 3³
|
|
200
|
+
binary: 4, // 2²
|
|
201
|
+
ternary: 27, // 3³
|
|
202
|
+
|
|
203
|
+
// Gauge symmetry angles
|
|
204
|
+
su3Angle: 120, // SU(3) color symmetry: 360°/3 = 120° (ternary)
|
|
205
|
+
su2Angle: 180, // SU(2) weak symmetry: 360°/2 = 180° (binary)
|
|
206
|
+
u1Angle: 360, // U(1) electromagnetic: full rotation
|
|
207
|
+
|
|
208
|
+
// Biological resonance
|
|
209
|
+
pentagonAngle: 108, // Internal angle of regular pentagon
|
|
210
|
+
phyllotaxisComplement: 137.5, // Golden angle in phyllotaxis
|
|
211
|
+
|
|
212
|
+
// Physical constants derived
|
|
213
|
+
trefoilComplexity: 17, // Trefoil knot complexity number
|
|
214
|
+
protonElectronRatio: 1836, // 17 × 108 = proton/electron mass ratio
|
|
215
|
+
fineStructureInverse: 137, // 108 + 29 = α⁻¹
|
|
216
|
+
higgsMass: 125, // 5³ GeV (first prime outside minimal twist set)
|
|
217
|
+
mod30Boundary: 29, // Prime sieve boundary
|
|
218
|
+
|
|
219
|
+
// Twist eigenstate formula: |n⟩τ = e^(i·2π/n)
|
|
220
|
+
twistEigenstate(n) {
|
|
221
|
+
const angle = (2 * Math.PI) / n;
|
|
222
|
+
return { re: Math.cos(angle), im: Math.sin(angle) };
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// Check if n exhibits 108-resonance (divides cleanly with 108)
|
|
226
|
+
resonates(n) {
|
|
227
|
+
if (n === 0) return false;
|
|
228
|
+
const ratio = 108 / n;
|
|
229
|
+
return Math.abs(ratio - Math.round(ratio)) < 0.001 ||
|
|
230
|
+
Math.abs(n / 108 - Math.round(n / 108)) < 0.001;
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
// Get gauge group contribution from factorization
|
|
234
|
+
gaugeDecomposition(n) {
|
|
235
|
+
const factors = factorize(n);
|
|
236
|
+
return {
|
|
237
|
+
su3Contribution: factors[3] || 0, // Powers of 3
|
|
238
|
+
su2Contribution: factors[2] || 0, // Powers of 2
|
|
239
|
+
u1Contribution: Object.keys(factors)
|
|
240
|
+
.filter(p => p !== '2' && p !== '3')
|
|
241
|
+
.reduce((sum, p) => sum + factors[p], 0)
|
|
242
|
+
};
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
// Derive mass ratio from topological integers
|
|
246
|
+
deriveMassRatio(crossings, sticks, bridge, unknotting) {
|
|
247
|
+
const trefoilComplexity = sticks * crossings - bridge + unknotting;
|
|
248
|
+
return trefoilComplexity * 108;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Twist angle for a prime (from Enochian layer and 108bio.pdf)
|
|
254
|
+
* κ(p) = 360°/p
|
|
255
|
+
* @param {number} p - Prime number
|
|
256
|
+
* @returns {number} Twist angle in degrees
|
|
257
|
+
*/
|
|
258
|
+
function twistAngle(p) {
|
|
259
|
+
return 360 / p;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Total twist for a sequence of primes
|
|
264
|
+
* T(P) = Σᵢ κ(pᵢ)
|
|
265
|
+
* @param {Array<number>} primes - Sequence of primes
|
|
266
|
+
* @returns {number} Total twist in degrees
|
|
267
|
+
*/
|
|
268
|
+
function totalTwist(primes) {
|
|
269
|
+
return primes.reduce((sum, p) => sum + twistAngle(p), 0);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Check twist closure (from discrete.pdf equation 18)
|
|
274
|
+
* A sequence is twist-closed when T(P) mod 360 ∈ [0,ε) ∪ (360-ε, 360]
|
|
275
|
+
* @param {Array<number>} primes - Sequence of primes
|
|
276
|
+
* @param {number} epsilon - Tolerance in degrees (default 1.0)
|
|
277
|
+
* @returns {boolean} True if twist-closed
|
|
278
|
+
*/
|
|
279
|
+
function isTwistClosed(primes, epsilon = 1.0) {
|
|
280
|
+
const twist = totalTwist(primes);
|
|
281
|
+
const mod = ((twist % 360) + 360) % 360;
|
|
282
|
+
return mod < epsilon || mod > (360 - epsilon);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Find primes that would close a twist sequence
|
|
287
|
+
* @param {Array<number>} currentPrimes - Current prime sequence
|
|
288
|
+
* @param {number} epsilon - Closure tolerance
|
|
289
|
+
* @returns {Array<Object>} Candidate primes with closure errors
|
|
290
|
+
*/
|
|
291
|
+
function findClosingPrimes(currentPrimes, epsilon = 1.0) {
|
|
292
|
+
const currentTwist = totalTwist(currentPrimes);
|
|
293
|
+
const mod = ((currentTwist % 360) + 360) % 360;
|
|
294
|
+
|
|
295
|
+
const candidates = [];
|
|
296
|
+
const testPrimes = primesUpTo(100);
|
|
297
|
+
|
|
298
|
+
for (const p of testPrimes) {
|
|
299
|
+
const newMod = ((mod + twistAngle(p)) % 360);
|
|
300
|
+
const error = Math.min(newMod, 360 - newMod);
|
|
301
|
+
candidates.push({ prime: p, error, closesAt: error < epsilon });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return candidates.sort((a, b) => a.error - b.error);
|
|
305
|
+
}
|
|
306
|
+
|
|
178
307
|
module.exports = {
|
|
179
308
|
primeGenerator, nthPrime, primesUpTo, isPrime,
|
|
180
309
|
factorize, primeSignature, firstNPrimes,
|
|
181
310
|
GaussianInteger, EisensteinInteger,
|
|
182
311
|
primeToFrequency, primeToAngle, sumOfTwoSquares,
|
|
183
|
-
DEFAULT_PRIMES
|
|
312
|
+
DEFAULT_PRIMES,
|
|
313
|
+
// 108 Invariant exports
|
|
314
|
+
TWIST_108,
|
|
315
|
+
twistAngle,
|
|
316
|
+
totalTwist,
|
|
317
|
+
isTwistClosed,
|
|
318
|
+
findClosingPrimes
|
|
184
319
|
};
|