commit 127ea02fb87bfc9b120ff5288566b811ff857a70
Author: Ravi Gulhane <>
Date: Wed Jun 3 15:34:57 2026 +0530
first commit
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d34e698
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# EventBuzz WhatsApp Concierge Service
+
+Hapi webhook server + BullMQ workers for the virtual concierge. Uses **one** MongoDB connection via `../backend/src/config/database` (do not add a separate `mongoose` dependency in chatbot).
+
+## Run locally
+
+1. Start Redis and MongoDB (same `MONGODB_URI` as backend). **DigitalOcean Managed Redis** needs TLS — the app enables it automatically for `*.ondigitalocean.com` / port `25061`, or set `REDIS_TLS=true` / use `rediss://` in `REDIS_URL`.
+2. Copy env from `backend/.env` and `chatbot/.env.example`.
+3. One terminal — `npm run dev` starts the **API** (port 3020), **ingest**, and **inbound** workers together (reloads on file changes). Use Ctrl+C to stop all.
+4. Optional — run only one process: `npm run dev:server`, `npm run dev:ingest`, or `npm run dev:inbound`.
+5. Optional — purge scheduler: `npm run dev:purge` (normally not needed locally).
+6. Point Meta webhook to `https:///webhook/whatsapp`.
+
+Dashboard APIs live on the main backend (`/api/concierge-bots`).
diff --git a/chatbot_plan.md b/chatbot_plan.md
new file mode 100644
index 0000000..ea91d0e
--- /dev/null
+++ b/chatbot_plan.md
@@ -0,0 +1,368 @@
+This execution plan covers everything needed to build, deploy, and scale the **Event Buzz Virtual Concierge SaaS Chatbot** as a decoupled microservice running alongside your primary Node.js and Managed MongoDB stack on DigitalOcean.
+
+---
+
+## 🛠️ Step 1: System Infrastructure & Architecture Design
+
+Before writing any application logic, you must isolate this high-concurrency chatbot service from your main ticketing web platform so that peak event traffic doesn't degrade your core registration experience.
+
+```
+ [ Meta WhatsApp Cloud API ]
+ │ (HTTPS Post Webhooks)
+ ▼
+ [ DigitalOcean Droplet ]
+ │
+ [ Nginx Proxy ]
+ │
+ [ PM2 Cluster Mode ]
+ │
+ [ Node.js Webhook Server ]
+ │
+ [ BullMQ / Redis Queue ]
+ │
+ [ Background Worker ]
+ / | \
+ / | \
+ ▼ ▼ ▼
+ [ OpenAI API ] [ DO Managed Mongo ] [ Ext. Location APIs ]
+ (Embeddings/LLM) (Shared Cluster DB) (Google Places/Maps)
+
+```
+
+### 1.1 Infrastructure Components
+
+* **Compute Layer:** Spin up a dedicated DigitalOcean Droplet (Ubuntu 24.04, minimum 2 vCPUs / 4GB RAM to comfortably process in-memory mathematical arrays).
+* **Process Manager:** Use **PM2** running in cluster mode (`pm2 start server.js -i max`) to natively scale across all available virtual CPU cores.
+* **Message Broker:** Install **Redis** directly on the droplet or use a managed instance. This acts as the backbone for your queue architecture (**BullMQ**) to prevent webhook timeouts.
+* **Reverse Proxy:** Configure **Nginx** to handle SSL termination (via Let's Encrypt Certbot) and proxy incoming WhatsApp webhooks from port `443` to your internal Node.js port.
+
+### 1.2 Shared Database Connections
+
+Configure a dedicated, isolated database client instance connecting to your existing DigitalOcean Managed MongoDB cluster.
+
+* Add a strict connection pool parameter (`maxPoolSize: 50`) to handle rapid parallel worker queries.
+* Create a separate database name (e.g., `eventbuzz_whatsapp`) within the same cluster to logically isolate chatbot data from ticketing collections while retaining shared cluster resources.
+
+---
+
+## 🗄️ Step 2: Exact MongoDB Data Schemas
+
+Implement these exact Mongoose schemas to track configuration, session routing states, and the multi-tenant knowledge chunks.
+
+### 2.1 Knowledge Source Configuration Schema (`KnowledgeSource`)
+
+Tracks the individual assets uploaded by the organizer and controls the text verification states.
+
+```javascript
+const mongoose = require('mongoose');
+
+const KnowledgeSourceSchema = new mongoose.Schema({
+ eventId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
+ type: { type: String, enum: ['url', 'pdf'], required: true },
+ sourcePath: { type: String, required: true }, // URL string or S3/DO Spaces file key
+ displayName: { type: String, required: true },
+ status: {
+ type: String,
+ enum: ['PENDING', 'PROCESSING', 'READY', 'FAILED'],
+ default: 'PENDING'
+ },
+ errorMessage: { type: String, default: null },
+ lastSyncedAt: { type: Date, default: Date.now }
+}, { timestamps: true });
+
+module.exports = mongoose.model('KnowledgeSource', KnowledgeSourceSchema);
+
+```
+
+### 2.2 Granular Vector Text Chunk Schema (`ContextChunk`)
+
+Stores individual text blocks alongside their mathematical representation array for localized context parsing.
+
+```javascript
+const ContextChunkSchema = new mongoose.Schema({
+ eventId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
+ sourceId: { type: mongoose.Schema.Types.ObjectId, ref: 'KnowledgeSource', required: true, index: true },
+ rawText: { type: String, required: true },
+ embedding: { type: [Number], required: true }, // Storing 1536 float values natively
+ isActive: { type: Boolean, default: false, index: true } // False until organizer explicitly approves via preview
+});
+
+module.exports = mongoose.model('ContextChunk', ContextChunkSchema);
+
+```
+
+### 2.3 Interactive User Session State Schema (`ChatSubscriber`)
+
+Manages the individual conversation paths, data access masks, and tracking rules for attendees.
+
+```javascript
+const ChatSubscriberSchema = new mongoose.Schema({
+ eventId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
+ attendeeId: { type: mongoose.Schema.Types.ObjectId, default: null }, // Linked when ticket matches
+ phoneNumber: { type: String, required: true, index: true }, // E.164 international format
+ sessionState: {
+ type: String,
+ enum: ['WELCOME_FORK', 'AWAITING_TICKET', 'MAIN_MENU', 'LOCAL_GUIDE_MODE'],
+ default: 'WELCOME_FORK'
+ },
+ metaContext: {
+ guestModeEnabled: { type: Boolean, default: false },
+ lastMenuInteraction: { type: Date, default: Date.now }
+ }
+}, { timestamps: true });
+
+// Ensures unique session footprint per phone number per event
+ChatSubscriberSchema.index({ eventId: 1, phoneNumber: 1 }, { unique: true });
+
+module.exports = mongoose.model('ChatSubscriber', ChatSubscriberSchema);
+
+```
+
+---
+
+## ⚙️ Step 3: The Content Ingestion Pipeline (Scraping, Chunking & Verification)
+
+When an event organizer triggers a sync or uploads documents, the backend processes it asynchronously using an execution queue.
+
+```
+[ Dashboard Input ] ──> [ BullMQ Job ] ──> [ Text Extractor ] ──> [ Token Splitter ] ──> [ Vector Array Generation ] ──> [ Staging Database ]
+
+```
+
+### 3.1 Text Extraction Rules
+
+* **For URLs:** Use a structured engine like `cheerio` or `playwright`. Inject a global sanitizer middleware to systematically drop `
+
+```
+
+Or in a Node.js project:
+
+```javascript
+const Complex = require('complex.js');
+```
+
+or
+
+```javascript
+import Complex from 'complex.js';
+```
+
+
+## Coding Style
+
+As every library I publish, Complex.js is also built to be as small as possible after compressing it with Google Closure Compiler in advanced mode. Thus the coding style orientates a little on maxing-out the compression rate. Please make sure you keep this style if you plan to extend the library.
+
+## Building the library
+
+After cloning the Git repository run:
+
+```
+npm install
+npm run build
+```
+
+## Run a test
+
+Testing the source against the shipped test suite is as easy as
+
+```
+npm run test
+```
+
+## Copyright and Licensing
+
+Copyright (c) 2025, [Robert Eisele](https://raw.org/)
+Licensed under the MIT license.
diff --git a/node_modules/complex.js/complex.d.ts b/node_modules/complex.js/complex.d.ts
new file mode 100644
index 0000000..37003f1
--- /dev/null
+++ b/node_modules/complex.js/complex.d.ts
@@ -0,0 +1,323 @@
+type AValue =
+ | Complex
+ | { im: number; re: number }
+ | { abs: number; arg: number }
+ | { r: number; phi: number }
+ | [number, number]
+ | string
+ | number
+ | null
+ | undefined;
+type BValue = number | undefined;
+
+export function Complex(a: AValue, b?: BValue): Complex;
+
+export default Complex;
+
+/**
+ *
+ * This class allows the manipulation of complex numbers.
+ * You can pass a complex number in different formats. Either as object, double, string or two integer parameters.
+ *
+ * Object form
+ * { re: , im: }
+ * { arg: , abs: }
+ * { phi: , r: }
+ *
+ * Array / Vector form
+ * [ real, imaginary ]
+ *
+ * Double form
+ * 99.3 - Single double value
+ *
+ * String form
+ * '23.1337' - Simple real number
+ * '15+3i' - a simple complex number
+ * '3-i' - a simple complex number
+ *
+ * Example:
+ *
+ * var c = new Complex('99.3+8i');
+ * c.mul({r: 3, i: 9}).div(4.9).sub(3, 2);
+ *
+ */
+export class Complex {
+ re: number;
+ im: number;
+
+ constructor(a: AValue, b?: BValue);
+
+ /**
+ * Calculates the sign of a complex number, which is a normalized complex
+ *
+ */
+ sign(): Complex;
+ /**
+ * Adds two complex numbers
+ *
+ */
+ add(a: AValue, b?: BValue): Complex;
+ /**
+ * Subtracts two complex numbers
+ *
+ */
+ sub(a: AValue, b?: BValue): Complex;
+ /**
+ * Multiplies two complex numbers
+ *
+ */
+ mul(a: AValue, b?: BValue): Complex;
+ /**
+ * Divides two complex numbers
+ *
+ */
+ div(a: AValue, b?: BValue): Complex;
+ /**
+ * Calculate the power of two complex numbers
+ *
+ */
+ pow(a: AValue, b?: BValue): Complex;
+ /**
+ * Calculate the complex square root
+ *
+ */
+ sqrt(): Complex;
+ /**
+ * Calculate the complex exponent
+ *
+ */
+ exp(): Complex;
+ /**
+ * Calculate the complex exponent and subtracts one.
+ *
+ * This may be more accurate than `Complex(x).exp().sub(1)` if
+ * `x` is small.
+ *
+ */
+ expm1(): Complex;
+ /**
+ * Calculate the natural log
+ *
+ */
+ log(): Complex;
+ /**
+ * Calculate the magnitude of the complex number
+ *
+ */
+ abs(): number;
+ /**
+ * Calculate the angle of the complex number
+ *
+ */
+ arg(): number;
+ /**
+ * Calculate the sine of the complex number
+ *
+ */
+ sin(): Complex;
+ /**
+ * Calculate the cosine
+ *
+ */
+ cos(): Complex;
+ /**
+ * Calculate the tangent
+ *
+ */
+ tan(): Complex;
+ /**
+ * Calculate the cotangent
+ *
+ */
+ cot(): Complex;
+ /**
+ * Calculate the secant
+ *
+ */
+ sec(): Complex;
+ /**
+ * Calculate the cosecans
+ *
+ */
+ csc(): Complex;
+ /**
+ * Calculate the complex arcus sinus
+ *
+ */
+ asin(): Complex;
+ /**
+ * Calculate the complex arcus cosinus
+ *
+ */
+ acos(): Complex;
+ /**
+ * Calculate the complex arcus tangent
+ *
+ */
+ atan(): Complex;
+ /**
+ * Calculate the complex arcus cotangent
+ *
+ */
+ acot(): Complex;
+ /**
+ * Calculate the complex arcus secant
+ *
+ */
+ asec(): Complex;
+ /**
+ * Calculate the complex arcus cosecans
+ *
+ */
+ acsc(): Complex;
+ /**
+ * Calculate the complex sinh
+ *
+ */
+ sinh(): Complex;
+ /**
+ * Calculate the complex cosh
+ *
+ */
+ cosh(): Complex;
+ /**
+ * Calculate the complex tanh
+ *
+ */
+ tanh(): Complex;
+ /**
+ * Calculate the complex coth
+ *
+ */
+ coth(): Complex;
+ /**
+ * Calculate the complex coth
+ *
+ */
+ csch(): Complex;
+ /**
+ * Calculate the complex sech
+ *
+ */
+ sech(): Complex;
+ /**
+ * Calculate the complex asinh
+ *
+ */
+ asinh(): Complex;
+ /**
+ * Calculate the complex acosh
+ *
+ */
+ acosh(): Complex;
+ /**
+ * Calculate the complex atanh
+ *
+ */
+ atanh(): Complex;
+ /**
+ * Calculate the complex acoth
+ *
+ */
+ acoth(): Complex;
+ /**
+ * Calculate the complex acsch
+ *
+ */
+ acsch(): Complex;
+ /**
+ * Calculate the complex asech
+ *
+ */
+ asech(): Complex;
+ /**
+ * Calculate the complex inverse 1/z
+ *
+ */
+ inverse(): Complex;
+ /**
+ * Returns the complex conjugate
+ *
+ */
+ conjugate(): Complex;
+ /**
+ * Gets the negated complex number
+ *
+ */
+ neg(): Complex;
+ /**
+ * Ceils the actual complex number
+ *
+ */
+ ceil(places: number): Complex;
+ /**
+ * Floors the actual complex number
+ *
+ */
+ floor(places: number): Complex;
+ /**
+ * Ceils the actual complex number
+ *
+ */
+ round(places: number): Complex;
+ /**
+ * Compares two complex numbers
+ *
+ * **Note:** new Complex(Infinity).equals(Infinity) === false
+ *
+ */
+ equals(a: AValue, b?: BValue): boolean;
+ /**
+ * Clones the actual object
+ *
+ */
+ clone(): Complex;
+ /**
+ * Gets a string of the actual complex number
+ *
+ */
+ toString(): string;
+ /**
+ * Returns the actual number as a vector
+ *
+ */
+ toVector(): number[];
+ /**
+ * Returns the actual real value of the current object
+ *
+ * @returns {number|null}
+ */
+ valueOf(): number | null;
+ /**
+ * Determines whether a complex number is not on the Riemann sphere.
+ *
+ */
+ isNaN(): boolean;
+ /**
+ * Determines whether or not a complex number is at the zero pole of the
+ * Riemann sphere.
+ *
+ */
+ isZero(): boolean;
+ /**
+ * Determines whether a complex number is not at the infinity pole of the
+ * Riemann sphere.
+ *
+ */
+ isFinite(): boolean;
+ /**
+ * Determines whether or not a complex number is at the infinity pole of the
+ * Riemann sphere.
+ *
+ */
+ isInfinite(): boolean;
+
+ static ZERO: Complex;
+ static ONE: Complex;
+ static I: Complex;
+ static PI: Complex;
+ static E: Complex;
+ static INFINITY: Complex;
+ static NAN: Complex;
+ static EPSILON: number;
+}
diff --git a/node_modules/complex.js/dist/complex.js b/node_modules/complex.js/dist/complex.js
new file mode 100644
index 0000000..11ff7ec
--- /dev/null
+++ b/node_modules/complex.js/dist/complex.js
@@ -0,0 +1,1476 @@
+'use strict';
+
+/**
+ *
+ * This class allows the manipulation of complex numbers.
+ * You can pass a complex number in different formats. Either as object, double, string or two integer parameters.
+ *
+ * Object form
+ * { re: , im: }
+ * { arg: , abs: }
+ * { phi: , r: }
+ *
+ * Array / Vector form
+ * [ real, imaginary ]
+ *
+ * Double form
+ * 99.3 - Single double value
+ *
+ * String form
+ * '23.1337' - Simple real number
+ * '15+3i' - a simple complex number
+ * '3-i' - a simple complex number
+ *
+ * Example:
+ *
+ * const c = new Complex('99.3+8i');
+ * c.mul({r: 3, i: 9}).div(4.9).sub(3, 2);
+ *
+ */
+
+
+const cosh = Math.cosh || function (x) {
+ return Math.abs(x) < 1e-9 ? 1 - x : (Math.exp(x) + Math.exp(-x)) * 0.5;
+};
+
+const sinh = Math.sinh || function (x) {
+ return Math.abs(x) < 1e-9 ? x : (Math.exp(x) - Math.exp(-x)) * 0.5;
+};
+
+/**
+ * Calculates cos(x) - 1 using Taylor series if x is small (-¼π ≤ x ≤ ¼π).
+ *
+ * @param {number} x
+ * @returns {number} cos(x) - 1
+ */
+const cosm1 = x => {
+ // cos(x) - 1 = − 2sin^2(x / 2)
+ const s = Math.sin(0.5 * x);
+ return -2 * s * s;
+};
+
+const hypot = function (x, y) {
+
+ x = Math.abs(x);
+ y = Math.abs(y);
+
+ // Ensure `x` is the larger value
+ if (x < y) [x, y] = [y, x];
+
+ // If both are below the threshold, use straightforward Pythagoras
+ if (x < 1e8) return Math.sqrt(x * x + y * y);
+
+ // For larger values, scale to avoid overflow
+ y /= x;
+ return x * Math.sqrt(1 + y * y);
+};
+
+const parser_exit = function () {
+ throw SyntaxError('Invalid Param');
+};
+
+/**
+ * Calculates log(sqrt(a^2+b^2)) in a way to avoid overflows
+ *
+ * @param {number} a
+ * @param {number} b
+ * @returns {number}
+ */
+function logHypot(a, b) {
+
+ const _a = Math.abs(a);
+ const _b = Math.abs(b);
+
+ if (a === 0) {
+ return Math.log(_b);
+ }
+
+ if (b === 0) {
+ return Math.log(_a);
+ }
+
+ if (_a < 3000 && _b < 3000) {
+ return Math.log(a * a + b * b) * 0.5;
+ }
+
+ /* I got 4 ideas to compute this property without overflow:
+ *
+ * Testing 1000000 times with random samples for a,b ∈ [1, 1000000000] against a big decimal library to get an error estimate
+ *
+ * 1. Only eliminate the square root: (OVERALL ERROR: 3.9122483030951116e-11)
+
+ Math.log(a * a + b * b) / 2
+
+ *
+ *
+ * 2. Try to use the non-overflowing pythagoras: (OVERALL ERROR: 8.889760039210159e-10)
+
+ const fn = function(a, b) {
+ a = Math.abs(a);
+ b = Math.abs(b);
+ let t = Math.min(a, b);
+ a = Math.max(a, b);
+ t = t / a;
+
+ return Math.log(a) + Math.log(1 + t * t) / 2;
+ };
+
+ * 3. Abuse the identity cos(atan(y/x) = x / sqrt(x^2+y^2): (OVERALL ERROR: 3.4780178737037204e-10)
+
+ Math.log(a / Math.cos(Math.atan2(b, a)))
+
+ * 4. Use 3. and apply log rules: (OVERALL ERROR: 1.2014087502620896e-9)
+
+ Math.log(a) - Math.log(Math.cos(Math.atan2(b, a)))
+
+ */
+
+ a = a * 0.5;
+ b = b * 0.5;
+
+ return 0.5 * Math.log(a * a + b * b) + Math.LN2;
+}
+
+const P = { 're': 0, 'im': 0 };
+const parse = function (a, b) {
+
+ const z = P;
+
+ if (a === undefined || a === null) {
+ z['re'] =
+ z['im'] = 0;
+ } else if (b !== undefined) {
+ z['re'] = a;
+ z['im'] = b;
+ } else
+ switch (typeof a) {
+
+ case 'object':
+
+ if ('im' in a && 're' in a) {
+ z['re'] = a['re'];
+ z['im'] = a['im'];
+ } else if ('abs' in a && 'arg' in a) {
+ if (!isFinite(a['abs']) && isFinite(a['arg'])) {
+ return Complex['INFINITY'];
+ }
+ z['re'] = a['abs'] * Math.cos(a['arg']);
+ z['im'] = a['abs'] * Math.sin(a['arg']);
+ } else if ('r' in a && 'phi' in a) {
+ if (!isFinite(a['r']) && isFinite(a['phi'])) {
+ return Complex['INFINITY'];
+ }
+ z['re'] = a['r'] * Math.cos(a['phi']);
+ z['im'] = a['r'] * Math.sin(a['phi']);
+ } else if (a.length === 2) { // Quick array check
+ z['re'] = a[0];
+ z['im'] = a[1];
+ } else {
+ parser_exit();
+ }
+ break;
+
+ case 'string':
+
+ z['im'] = /* void */
+ z['re'] = 0;
+
+ const tokens = a.replace(/_/g, '')
+ .match(/\d+\.?\d*e[+-]?\d+|\d+\.?\d*|\.\d+|./g);
+ let plus = 1;
+ let minus = 0;
+
+ if (tokens === null) {
+ parser_exit();
+ }
+
+ for (let i = 0; i < tokens.length; i++) {
+
+ const c = tokens[i];
+
+ if (c === ' ' || c === '\t' || c === '\n') {
+ /* void */
+ } else if (c === '+') {
+ plus++;
+ } else if (c === '-') {
+ minus++;
+ } else if (c === 'i' || c === 'I') {
+
+ if (plus + minus === 0) {
+ parser_exit();
+ }
+
+ if (tokens[i + 1] !== ' ' && !isNaN(tokens[i + 1])) {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + tokens[i + 1]);
+ i++;
+ } else {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + '1');
+ }
+ plus = minus = 0;
+
+ } else {
+
+ if (plus + minus === 0 || isNaN(c)) {
+ parser_exit();
+ }
+
+ if (tokens[i + 1] === 'i' || tokens[i + 1] === 'I') {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + c);
+ i++;
+ } else {
+ z['re'] += parseFloat((minus % 2 ? '-' : '') + c);
+ }
+ plus = minus = 0;
+ }
+ }
+
+ // Still something on the stack
+ if (plus + minus > 0) {
+ parser_exit();
+ }
+ break;
+
+ case 'number':
+ z['im'] = 0;
+ z['re'] = a;
+ break;
+
+ default:
+ parser_exit();
+ }
+
+ if (isNaN(z['re']) || isNaN(z['im'])) {
+ // If a calculation is NaN, we treat it as NaN and don't throw
+ //parser_exit();
+ }
+
+ return z;
+};
+
+/**
+ * @constructor
+ * @returns {Complex}
+ */
+function Complex(a, b) {
+
+ if (!(this instanceof Complex)) {
+ return new Complex(a, b);
+ }
+
+ const z = parse(a, b);
+
+ this['re'] = z['re'];
+ this['im'] = z['im'];
+}
+
+Complex.prototype = {
+
+ 're': 0,
+ 'im': 0,
+
+ /**
+ * Calculates the sign of a complex number, which is a normalized complex
+ *
+ * @returns {Complex}
+ */
+ 'sign': function () {
+
+ const abs = hypot(this['re'], this['im']);
+
+ return new Complex(
+ this['re'] / abs,
+ this['im'] / abs);
+ },
+
+ /**
+ * Adds two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'add': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+
+ if (tInfin || zInfin) {
+
+ if (tInfin && zInfin) {
+ // Infinity + Infinity = NaN
+ return Complex['NAN'];
+ }
+ // Infinity + z = Infinity { where z != Infinity }
+ return Complex['INFINITY'];
+ }
+
+ return new Complex(
+ this['re'] + z['re'],
+ this['im'] + z['im']);
+ },
+
+ /**
+ * Subtracts two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'sub': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+
+ if (tInfin || zInfin) {
+
+ if (tInfin && zInfin) {
+ // Infinity - Infinity = NaN
+ return Complex['NAN'];
+ }
+ // Infinity - z = Infinity { where z != Infinity }
+ return Complex['INFINITY'];
+ }
+
+ return new Complex(
+ this['re'] - z['re'],
+ this['im'] - z['im']);
+ },
+
+ /**
+ * Multiplies two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'mul': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ // Infinity * 0 = NaN
+ if (tInfin && zIsZero || zInfin && tIsZero) {
+ return Complex['NAN'];
+ }
+
+ // Infinity * z = Infinity { where z != 0 }
+ if (tInfin || zInfin) {
+ return Complex['INFINITY'];
+ }
+
+ // Shortcut for real values
+ if (z['im'] === 0 && this['im'] === 0) {
+ return new Complex(this['re'] * z['re'], 0);
+ }
+
+ return new Complex(
+ this['re'] * z['re'] - this['im'] * z['im'],
+ this['re'] * z['im'] + this['im'] * z['re']);
+ },
+
+ /**
+ * Divides two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'div': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ // 0 / 0 = NaN and Infinity / Infinity = NaN
+ if (tIsZero && zIsZero || tInfin && zInfin) {
+ return Complex['NAN'];
+ }
+
+ // Infinity / 0 = Infinity
+ if (zIsZero || tInfin) {
+ return Complex['INFINITY'];
+ }
+
+ // 0 / Infinity = 0
+ if (tIsZero || zInfin) {
+ return Complex['ZERO'];
+ }
+
+ if (0 === z['im']) {
+ // Divisor is real
+ return new Complex(this['re'] / z['re'], this['im'] / z['re']);
+ }
+
+ if (Math.abs(z['re']) < Math.abs(z['im'])) {
+
+ const x = z['re'] / z['im'];
+ const t = z['re'] * x + z['im'];
+
+ return new Complex(
+ (this['re'] * x + this['im']) / t,
+ (this['im'] * x - this['re']) / t);
+
+ } else {
+
+ const x = z['im'] / z['re'];
+ const t = z['im'] * x + z['re'];
+
+ return new Complex(
+ (this['re'] + this['im'] * x) / t,
+ (this['im'] - this['re'] * x) / t);
+ }
+ },
+
+ /**
+ * Calculate the power of two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'pow': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ if (zIsZero) {
+ return Complex['ONE'];
+ }
+
+ // If the exponent is real
+ if (z['im'] === 0) {
+
+ if (this['im'] === 0 && this['re'] > 0) {
+
+ return new Complex(Math.pow(this['re'], z['re']), 0);
+
+ } else if (this['re'] === 0) { // If base is fully imaginary
+
+ switch ((z['re'] % 4 + 4) % 4) {
+ case 0:
+ return new Complex(Math.pow(this['im'], z['re']), 0);
+ case 1:
+ return new Complex(0, Math.pow(this['im'], z['re']));
+ case 2:
+ return new Complex(-Math.pow(this['im'], z['re']), 0);
+ case 3:
+ return new Complex(0, -Math.pow(this['im'], z['re']));
+ }
+ }
+ }
+
+ /* I couldn't find a good formula, so here is a derivation and optimization
+ *
+ * z_1^z_2 = (a + bi)^(c + di)
+ * = exp((c + di) * log(a + bi)
+ * = pow(a^2 + b^2, (c + di) / 2) * exp(i(c + di)atan2(b, a))
+ * =>...
+ * Re = (pow(a^2 + b^2, c / 2) * exp(-d * atan2(b, a))) * cos(d * log(a^2 + b^2) / 2 + c * atan2(b, a))
+ * Im = (pow(a^2 + b^2, c / 2) * exp(-d * atan2(b, a))) * sin(d * log(a^2 + b^2) / 2 + c * atan2(b, a))
+ *
+ * =>...
+ * Re = exp(c * log(sqrt(a^2 + b^2)) - d * atan2(b, a)) * cos(d * log(sqrt(a^2 + b^2)) + c * atan2(b, a))
+ * Im = exp(c * log(sqrt(a^2 + b^2)) - d * atan2(b, a)) * sin(d * log(sqrt(a^2 + b^2)) + c * atan2(b, a))
+ *
+ * =>
+ * Re = exp(c * logsq2 - d * arg(z_1)) * cos(d * logsq2 + c * arg(z_1))
+ * Im = exp(c * logsq2 - d * arg(z_1)) * sin(d * logsq2 + c * arg(z_1))
+ *
+ */
+
+ if (tIsZero && z['re'] > 0) { // Same behavior as Wolframalpha, Zero if real part is zero
+ return Complex['ZERO'];
+ }
+
+ const arg = Math.atan2(this['im'], this['re']);
+ const loh = logHypot(this['re'], this['im']);
+
+ let re = Math.exp(z['re'] * loh - z['im'] * arg);
+ let im = z['im'] * loh + z['re'] * arg;
+ return new Complex(
+ re * Math.cos(im),
+ re * Math.sin(im));
+ },
+
+ /**
+ * Calculate the complex square root
+ *
+ * @returns {Complex}
+ */
+ 'sqrt': function () {
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+ // Real number case
+ if (a >= 0) {
+ return new Complex(Math.sqrt(a), 0);
+ } else {
+ return new Complex(0, Math.sqrt(-a));
+ }
+ }
+
+ const r = hypot(a, b);
+
+ let re = Math.sqrt(0.5 * (r + Math.abs(a))); // sqrt(2x) / 2 = sqrt(x / 2)
+ let im = Math.abs(b) / (2 * re);
+
+ if (a >= 0) {
+ return new Complex(re, b < 0 ? -im : im);
+ } else {
+ return new Complex(im, b < 0 ? -re : re);
+ }
+ },
+
+ /**
+ * Calculate the complex exponent
+ *
+ * @returns {Complex}
+ */
+ 'exp': function () {
+
+ const er = Math.exp(this['re']);
+
+ if (this['im'] === 0) {
+ return new Complex(er, 0);
+ }
+ return new Complex(
+ er * Math.cos(this['im']),
+ er * Math.sin(this['im']));
+ },
+
+ /**
+ * Calculate the complex exponent and subtracts one.
+ *
+ * This may be more accurate than `Complex(x).exp().sub(1)` if
+ * `x` is small.
+ *
+ * @returns {Complex}
+ */
+ 'expm1': function () {
+
+ /**
+ * exp(a + i*b) - 1
+ = exp(a) * (cos(b) + j*sin(b)) - 1
+ = expm1(a)*cos(b) + cosm1(b) + j*exp(a)*sin(b)
+ */
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.expm1(a) * Math.cos(b) + cosm1(b),
+ Math.exp(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the natural log
+ *
+ * @returns {Complex}
+ */
+ 'log': function () {
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0 && a > 0) {
+ return new Complex(Math.log(a), 0);
+ }
+
+ return new Complex(
+ logHypot(a, b),
+ Math.atan2(b, a));
+ },
+
+ /**
+ * Calculate the magnitude of the complex number
+ *
+ * @returns {number}
+ */
+ 'abs': function () {
+
+ return hypot(this['re'], this['im']);
+ },
+
+ /**
+ * Calculate the angle of the complex number
+ *
+ * @returns {number}
+ */
+ 'arg': function () {
+
+ return Math.atan2(this['im'], this['re']);
+ },
+
+ /**
+ * Calculate the sine of the complex number
+ *
+ * @returns {Complex}
+ */
+ 'sin': function () {
+
+ // sin(z) = ( e^iz - e^-iz ) / 2i
+ // = sin(a)cosh(b) + i cos(a)sinh(b)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.sin(a) * cosh(b),
+ Math.cos(a) * sinh(b));
+ },
+
+ /**
+ * Calculate the cosine
+ *
+ * @returns {Complex}
+ */
+ 'cos': function () {
+
+ // cos(z) = ( e^iz + e^-iz ) / 2
+ // = cos(a)cosh(b) - i sin(a)sinh(b)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.cos(a) * cosh(b),
+ -Math.sin(a) * sinh(b));
+ },
+
+ /**
+ * Calculate the tangent
+ *
+ * @returns {Complex}
+ */
+ 'tan': function () {
+
+ // tan(z) = sin(z) / cos(z)
+ // = ( e^iz - e^-iz ) / ( i( e^iz + e^-iz ) )
+ // = ( e^2iz - 1 ) / i( e^2iz + 1 )
+ // = ( sin(2a) + i sinh(2b) ) / ( cos(2a) + cosh(2b) )
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = Math.cos(a) + cosh(b);
+
+ return new Complex(
+ Math.sin(a) / d,
+ sinh(b) / d);
+ },
+
+ /**
+ * Calculate the cotangent
+ *
+ * @returns {Complex}
+ */
+ 'cot': function () {
+
+ // cot(c) = i(e^(ci) + e^(-ci)) / (e^(ci) - e^(-ci))
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = Math.cos(a) - cosh(b);
+
+ return new Complex(
+ -Math.sin(a) / d,
+ sinh(b) / d);
+ },
+
+ /**
+ * Calculate the secant
+ *
+ * @returns {Complex}
+ */
+ 'sec': function () {
+
+ // sec(c) = 2 / (e^(ci) + e^(-ci))
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = 0.5 * cosh(2 * b) + 0.5 * Math.cos(2 * a);
+
+ return new Complex(
+ Math.cos(a) * cosh(b) / d,
+ Math.sin(a) * sinh(b) / d);
+ },
+
+ /**
+ * Calculate the cosecans
+ *
+ * @returns {Complex}
+ */
+ 'csc': function () {
+
+ // csc(c) = 2i / (e^(ci) - e^(-ci))
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = 0.5 * cosh(2 * b) - 0.5 * Math.cos(2 * a);
+
+ return new Complex(
+ Math.sin(a) * cosh(b) / d,
+ -Math.cos(a) * sinh(b) / d);
+ },
+
+ /**
+ * Calculate the complex arcus sinus
+ *
+ * @returns {Complex}
+ */
+ 'asin': function () {
+
+ // asin(c) = -i * log(ci + sqrt(1 - c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const t1 = new Complex(
+ b * b - a * a + 1,
+ -2 * a * b)['sqrt']();
+
+ const t2 = new Complex(
+ t1['re'] - b,
+ t1['im'] + a)['log']();
+
+ return new Complex(t2['im'], -t2['re']);
+ },
+
+ /**
+ * Calculate the complex arcus cosinus
+ *
+ * @returns {Complex}
+ */
+ 'acos': function () {
+
+ // acos(c) = i * log(c - i * sqrt(1 - c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const t1 = new Complex(
+ b * b - a * a + 1,
+ -2 * a * b)['sqrt']();
+
+ const t2 = new Complex(
+ t1['re'] - b,
+ t1['im'] + a)['log']();
+
+ return new Complex(Math.PI / 2 - t2['im'], t2['re']);
+ },
+
+ /**
+ * Calculate the complex arcus tangent
+ *
+ * @returns {Complex}
+ */
+ 'atan': function () {
+
+ // atan(c) = i / 2 log((i + x) / (i - x))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0) {
+
+ if (b === 1) {
+ return new Complex(0, Infinity);
+ }
+
+ if (b === -1) {
+ return new Complex(0, -Infinity);
+ }
+ }
+
+ const d = a * a + (1.0 - b) * (1.0 - b);
+
+ const t1 = new Complex(
+ (1 - b * b - a * a) / d,
+ -2 * a / d).log();
+
+ return new Complex(-0.5 * t1['im'], 0.5 * t1['re']);
+ },
+
+ /**
+ * Calculate the complex arcus cotangent
+ *
+ * @returns {Complex}
+ */
+ 'acot': function () {
+
+ // acot(c) = i / 2 log((c - i) / (c + i))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+ return new Complex(Math.atan2(1, a), 0);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).atan()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).atan();
+ },
+
+ /**
+ * Calculate the complex arcus secant
+ *
+ * @returns {Complex}
+ */
+ 'asec': function () {
+
+ // asec(c) = -i * log(1 / c + sqrt(1 - i / c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0 && b === 0) {
+ return new Complex(0, Infinity);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).acos()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).acos();
+ },
+
+ /**
+ * Calculate the complex arcus cosecans
+ *
+ * @returns {Complex}
+ */
+ 'acsc': function () {
+
+ // acsc(c) = -i * log(i / c + sqrt(1 - 1 / c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0 && b === 0) {
+ return new Complex(Math.PI / 2, Infinity);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).asin()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).asin();
+ },
+
+ /**
+ * Calculate the complex sinh
+ *
+ * @returns {Complex}
+ */
+ 'sinh': function () {
+
+ // sinh(c) = (e^c - e^-c) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ sinh(a) * Math.cos(b),
+ cosh(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the complex cosh
+ *
+ * @returns {Complex}
+ */
+ 'cosh': function () {
+
+ // cosh(c) = (e^c + e^-c) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ cosh(a) * Math.cos(b),
+ sinh(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the complex tanh
+ *
+ * @returns {Complex}
+ */
+ 'tanh': function () {
+
+ // tanh(c) = (e^c - e^-c) / (e^c + e^-c)
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = cosh(a) + Math.cos(b);
+
+ return new Complex(
+ sinh(a) / d,
+ Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex coth
+ *
+ * @returns {Complex}
+ */
+ 'coth': function () {
+
+ // coth(c) = (e^c + e^-c) / (e^c - e^-c)
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = cosh(a) - Math.cos(b);
+
+ return new Complex(
+ sinh(a) / d,
+ -Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex csch
+ *
+ * @returns {Complex}
+ */
+ 'csch': function () {
+
+ // csch(c) = 2 / (e^c - e^-c)
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = Math.cos(2 * b) - cosh(2 * a);
+
+ return new Complex(
+ -2 * sinh(a) * Math.cos(b) / d,
+ 2 * cosh(a) * Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex sech
+ *
+ * @returns {Complex}
+ */
+ 'sech': function () {
+
+ // sech(c) = 2 / (e^c + e^-c)
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = Math.cos(2 * b) + cosh(2 * a);
+
+ return new Complex(
+ 2 * cosh(a) * Math.cos(b) / d,
+ -2 * sinh(a) * Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex asinh
+ *
+ * @returns {Complex}
+ */
+ 'asinh': function () {
+
+ // asinh(z) = log(z + sqrt(z^2 + 1))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ if (a === 0) {
+ return new Complex(0, 0);
+ }
+
+ // Use |a| to keep asinh(-a) = -asinh(a) and avoid cancellation for large -a
+ const x = Math.abs(a);
+ const r = Math.log(x + Math.sqrt(x * x + 1));
+
+ return new Complex(a < 0 ? -r : r, 0);
+ }
+
+ // z^2 + 1 = (a^2 - b^2 + 1) + i (2ab)
+ const re2 = a * a - b * b + 1;
+ const im2 = 2 * a * b;
+
+ const t = new Complex(re2, im2)['sqrt'](); // sqrt(z^2 + 1)
+
+ return new Complex(a + t['re'], b + t['im'])['log']();
+ },
+
+ /**
+ * Calculate the complex acosh
+ *
+ * @returns {Complex}
+ */
+ 'acosh': function () {
+
+ // acosh(z)= log(z + sqrt(z^2 - 1)) = log(z + sqrt(z - 1) * sqrt(z + 1))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ // z = a is real
+ if (a > 1) {
+ // acosh(a) = log(a + sqrt(a - 1) * sqrt(a + 1)), a > 1
+ return new Complex(
+ Math.log(a + Math.sqrt(a - 1) * Math.sqrt(a + 1)), 0);
+ }
+
+ if (a < -1) {
+ // acosh(a) = log(-a + sqrt(a^2 - 1)) + i*pi, a < -1
+ const t = Math.sqrt(a * a - 1);
+ return new Complex(Math.log(-a + t), Math.PI);
+ }
+
+ // -1 <= a <= 1 : purely imaginary
+ // acosh(a) = i * acos(a)
+ return new Complex(0, Math.acos(a));
+ }
+
+ const t1 = new Complex(a - 1, b)['sqrt'](); // sqrt(z - 1)
+ const t2 = new Complex(a + 1, b)['sqrt'](); // sqrt(z + 1)
+
+ return new Complex(
+ a + t1['re'] * t2['re'] - t1['im'] * t2['im'],
+ b + t1['re'] * t2['im'] + t1['im'] * t2['re']
+ )['log']();
+ },
+
+ /**
+ * Calculate the complex atanh
+ *
+ * @returns {Complex}
+ */
+ 'atanh': function () {
+
+ // atanh(z) = log((1 + z) / (1 - z)) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ if (a === 0) {
+ return new Complex(0, 0);
+ }
+
+ if (a === 1) {
+ // limit x -> 1^- atanh(x) = +Infinity
+ return new Complex(Infinity, 0);
+ }
+
+ if (a === -1) {
+ // limit x -> -1^+ atanh(x) = -Infinity
+ return new Complex(-Infinity, 0);
+ }
+
+ if (-1 < a && a < 1) {
+ // Purely real
+ return new Complex(
+ 0.5 * Math.log((1 + a) / (1 - a)),
+ 0
+ );
+ }
+
+ if (a > 1) {
+ // Our branch: Im(atanh(a)) = -π/2 for a > 1
+ const t = (a + 1) / (a - 1); // > 0
+ return new Complex(
+ 0.5 * Math.log(t),
+ -Math.PI / 2
+ );
+ }
+
+ // a < -1: Im(atanh(a)) = +π/2
+ const t = (1 + a) / (1 - a); // < 0
+ return new Complex(
+ 0.5 * Math.log(-t), // log((1 - a)/(1 + a))
+ Math.PI / 2
+ );
+ }
+
+ // Use atanh(z) = 0.5 * Log((1+z)/(1-z)) with principal Log.
+ const oneMinus = 1 - a;
+ const onePlus = 1 + a;
+ const d = oneMinus * oneMinus + b * b; // |1 - z|^2
+
+ if (d === 0) {
+ // (1 - z) == 0 for finite z only at z = 1+0i, already handled above.
+ // If we ever get here, just propagate infinities consistently:
+ return new Complex(
+ (a !== -1) ? (a / 0) : 0,
+ (b !== 0) ? (b / 0) : 0
+ );
+ }
+
+ // (1 + z) / (1 - z) with a single complex division
+ const xr = (onePlus * oneMinus - b * b) / d;
+ const xi = (b * oneMinus + onePlus * b) / d;
+
+ // 0.5 * log(xr + i xi)
+ return new Complex(
+ logHypot(xr, xi) / 2,
+ Math.atan2(xi, xr) / 2
+ );
+ },
+
+ /**
+ * Calculate the complex acoth
+ *
+ * @returns {Complex}
+ */
+ 'acoth': function () {
+
+ // acoth(z) = log((z + 1) / (z - 1)) / 2 = atanh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ // z = 0 -> acoth(0) = i * π / 2 (log((1+0)/(1-0))/2 = log(-1)/2)
+ if (a === 0 && b === 0) {
+ return new Complex(0, Math.PI / 2);
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1 / z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['atanh']();
+ }
+
+ // Fallback for weird infinities/NaNs: mirror other functions' style
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0
+ )['atanh']();
+ },
+
+ /**
+ * Calculate the complex acsch
+ *
+ * @returns {Complex}
+ */
+ 'acsch': function () {
+
+ // acsch(c) = log((1+sqrt(1+c^2))/c) = = asinh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ // z = a real
+ if (a === 0) {
+ // acsch(0) -> +/- Infinity, we keep your previous behavior
+ return new Complex(Infinity, 0);
+ }
+
+ // acsch(a) = asinh(1/a) = log(1/a + sqrt(1/a^2 + 1))
+ const inv = 1 / a;
+ return new Complex(
+ Math.log(inv + Math.sqrt(inv * inv + 1)),
+ 0
+ );
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1/z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['asinh']();
+ }
+
+ // Handle 0 + 0i or infinities in the same spirit as your existing code
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0)['asinh']();
+ },
+
+ /**
+ * Calculate the complex asech
+ *
+ * @returns {Complex}
+ */
+ 'asech': function () {
+
+ // asech(z) = acosh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (this['isZero']()) {
+ // asech(0) = acosh(∞) -> ∞
+ return Complex['INFINITY'];
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1 / z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['acosh']();
+ }
+
+ // Fallback for weird infinities/NaNs
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0)['acosh']();
+ },
+
+ /**
+ * Calculate the complex inverse 1/z
+ *
+ * @returns {Complex}
+ */
+ 'inverse': function () {
+
+ // 1 / 0 = Infinity and 1 / Infinity = 0
+ if (this['isZero']()) {
+ return Complex['INFINITY'];
+ }
+
+ if (this['isInfinite']()) {
+ return Complex['ZERO'];
+ }
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const d = a * a + b * b;
+
+ return new Complex(a / d, -b / d);
+ },
+
+ /**
+ * Returns the complex conjugate
+ *
+ * @returns {Complex}
+ */
+ 'conjugate': function () {
+
+ return new Complex(this['re'], -this['im']);
+ },
+
+ /**
+ * Gets the negated complex number
+ *
+ * @returns {Complex}
+ */
+ 'neg': function () {
+
+ return new Complex(-this['re'], -this['im']);
+ },
+
+ /**
+ * Ceils the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'ceil': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.ceil(this['re'] * places) / places,
+ Math.ceil(this['im'] * places) / places);
+ },
+
+ /**
+ * Floors the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'floor': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.floor(this['re'] * places) / places,
+ Math.floor(this['im'] * places) / places);
+ },
+
+ /**
+ * Ceils the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'round': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.round(this['re'] * places) / places,
+ Math.round(this['im'] * places) / places);
+ },
+
+ /**
+ * Compares two complex numbers
+ *
+ * **Note:** new Complex(Infinity).equals(Infinity) === false
+ *
+ * @returns {boolean}
+ */
+ 'equals': function (a, b) {
+
+ const z = parse(a, b);
+
+ return Math.abs(z['re'] - this['re']) <= Complex['EPSILON'] &&
+ Math.abs(z['im'] - this['im']) <= Complex['EPSILON'];
+ },
+
+ /**
+ * Clones the actual object
+ *
+ * @returns {Complex}
+ */
+ 'clone': function () {
+
+ return new Complex(this['re'], this['im']);
+ },
+
+ /**
+ * Gets a string of the actual complex number
+ *
+ * @returns {string}
+ */
+ 'toString': function () {
+
+ let a = this['re'];
+ let b = this['im'];
+ let ret = "";
+
+ if (this['isNaN']()) {
+ return 'NaN';
+ }
+
+ if (this['isInfinite']()) {
+ return 'Infinity';
+ }
+
+ if (Math.abs(a) < Complex['EPSILON']) {
+ a = 0;
+ }
+
+ if (Math.abs(b) < Complex['EPSILON']) {
+ b = 0;
+ }
+
+ // If is real number
+ if (b === 0) {
+ return ret + a;
+ }
+
+ if (a !== 0) {
+ ret += a;
+ ret += " ";
+ if (b < 0) {
+ b = -b;
+ ret += "-";
+ } else {
+ ret += "+";
+ }
+ ret += " ";
+ } else if (b < 0) {
+ b = -b;
+ ret += "-";
+ }
+
+ if (1 !== b) { // b is the absolute imaginary part
+ ret += b;
+ }
+ return ret + "i";
+ },
+
+ /**
+ * Returns the actual number as a vector
+ *
+ * @returns {Array}
+ */
+ 'toVector': function () {
+
+ return [this['re'], this['im']];
+ },
+
+ /**
+ * Returns the actual real value of the current object
+ *
+ * @returns {number|null}
+ */
+ 'valueOf': function () {
+
+ if (this['im'] === 0) {
+ return this['re'];
+ }
+ return null;
+ },
+
+ /**
+ * Determines whether a complex number is not on the Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isNaN': function () {
+ return isNaN(this['re']) || isNaN(this['im']);
+ },
+
+ /**
+ * Determines whether or not a complex number is at the zero pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isZero': function () {
+ return this['im'] === 0 && this['re'] === 0;
+ },
+
+ /**
+ * Determines whether a complex number is not at the infinity pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isFinite': function () {
+ return isFinite(this['re']) && isFinite(this['im']);
+ },
+
+ /**
+ * Determines whether or not a complex number is at the infinity pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isInfinite': function () {
+ return !this['isFinite']();
+ }
+};
+
+Complex['ZERO'] = new Complex(0, 0);
+Complex['ONE'] = new Complex(1, 0);
+Complex['I'] = new Complex(0, 1);
+Complex['PI'] = new Complex(Math.PI, 0);
+Complex['E'] = new Complex(Math.E, 0);
+Complex['INFINITY'] = new Complex(Infinity, Infinity);
+Complex['NAN'] = new Complex(NaN, NaN);
+Complex['EPSILON'] = 1e-15;
+
+Object.defineProperty(Complex, "__esModule", { 'value': true });
+Complex['default'] = Complex;
+Complex['Complex'] = Complex;
+module['exports'] = Complex;
diff --git a/node_modules/complex.js/dist/complex.min.js b/node_modules/complex.js/dist/complex.min.js
new file mode 100644
index 0000000..37c9da1
--- /dev/null
+++ b/node_modules/complex.js/dist/complex.min.js
@@ -0,0 +1,27 @@
+/*
+Complex.js v2.4.3 11/13/2025
+https://raw.org/article/complex-numbers-in-javascript/
+
+Copyright (c) 2025, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+'use strict';(function(q){function l(a,b){if(a===void 0||a===null)f.re=f.im=0;else if(b!==void 0)f.re=a,f.im=b;else switch(typeof a){case "object":if("im"in a&&"re"in a)f.re=a.re,f.im=a.im;else if("abs"in a&&"arg"in a){if(!isFinite(a.abs)&&isFinite(a.arg))return c.INFINITY;f.re=a.abs*Math.cos(a.arg);f.im=a.abs*Math.sin(a.arg)}else if("r"in a&&"phi"in a){if(!isFinite(a.r)&&isFinite(a.phi))return c.INFINITY;f.re=a.r*Math.cos(a.phi);f.im=a.r*Math.sin(a.phi)}else a.length===2?(f.re=a[0],f.im=a[1]):m();
+break;case "string":f.im=f.re=0;a=a.replace(/_/g,"").match(/\d+\.?\d*e[+-]?\d+|\d+\.?\d*|\.\d+|./g);b=1;let d=0;a===null&&m();for(let e=0;e0&&
+m();break;case "number":f.im=0;f.re=a;break;default:m()}return f}function m(){throw SyntaxError("Invalid Param");}function n(a,b){a=Math.abs(a);b=Math.abs(b);a
+0)return new c(Math.pow(this.re,a.re),0);if(this.re===0)switch((a.re%4+4)%4){case 0:return new c(Math.pow(this.im,a.re),0);case 1:return new c(0,Math.pow(this.im,a.re));case 2:return new c(-Math.pow(this.im,a.re),0);case 3:return new c(0,-Math.pow(this.im,a.re))}}if(b&&a.re>0)return c.ZERO;const d=Math.atan2(this.im,this.re),e=p(this.re,this.im);b=Math.exp(a.re*e-a.im*d);a=a.im*e+a.re*d;return new c(b*Math.cos(a),b*Math.sin(a))},sqrt:function(){const a=this.re,b=this.im;if(b===0)return a>=0?new c(Math.sqrt(a),
+0):new c(0,Math.sqrt(-a));var d=n(a,b);d=Math.sqrt(.5*(d+Math.abs(a)));let e=Math.abs(b)/(2*d);return a>=0?new c(d,b<0?-e:e):new c(e,b<0?-d:d)},exp:function(){const a=Math.exp(this.re);return this.im===0?new c(a,0):new c(a*Math.cos(this.im),a*Math.sin(this.im))},expm1:function(){const a=this.re,b=this.im,d=Math.sin(.5*b);return new c(Math.expm1(a)*Math.cos(b)+-2*d*d,Math.exp(a)*Math.sin(b))},log:function(){const a=this.re,b=this.im;return b===0&&a>0?new c(Math.log(a),0):new c(p(a,b),Math.atan2(b,
+a))},abs:function(){return n(this.re,this.im)},arg:function(){return Math.atan2(this.im,this.re)},sin:function(){const a=this.re,b=this.im;return new c(Math.sin(a)*h(b),Math.cos(a)*k(b))},cos:function(){const a=this.re,b=this.im;return new c(Math.cos(a)*h(b),-Math.sin(a)*k(b))},tan:function(){const a=2*this.re,b=2*this.im,d=Math.cos(a)+h(b);return new c(Math.sin(a)/d,k(b)/d)},cot:function(){const a=2*this.re,b=2*this.im,d=Math.cos(a)-h(b);return new c(-Math.sin(a)/d,k(b)/d)},sec:function(){const a=
+this.re,b=this.im,d=.5*h(2*b)+.5*Math.cos(2*a);return new c(Math.cos(a)*h(b)/d,Math.sin(a)*k(b)/d)},csc:function(){const a=this.re,b=this.im,d=.5*h(2*b)-.5*Math.cos(2*a);return new c(Math.sin(a)*h(b)/d,-Math.cos(a)*k(b)/d)},asin:function(){var a=this.re;const b=this.im,d=(new c(b*b-a*a+1,-2*a*b)).sqrt();a=(new c(d.re-b,d.im+a)).log();return new c(a.im,-a.re)},acos:function(){var a=this.re;const b=this.im,d=(new c(b*b-a*a+1,-2*a*b)).sqrt();a=(new c(d.re-b,d.im+a)).log();return new c(Math.PI/2-a.im,
+a.re)},atan:function(){var a=this.re;const b=this.im;if(a===0){if(b===1)return new c(0,Infinity);if(b===-1)return new c(0,-Infinity)}const d=a*a+(1-b)*(1-b);a=(new c((1-b*b-a*a)/d,-2*a/d)).log();return new c(-.5*a.im,.5*a.re)},acot:function(){const a=this.re,b=this.im;if(b===0)return new c(Math.atan2(1,a),0);const d=a*a+b*b;return d!==0?(new c(a/d,-b/d)).atan():(new c(a!==0?a/0:0,b!==0?-b/0:0)).atan()},asec:function(){const a=this.re,b=this.im;if(a===0&&b===0)return new c(0,Infinity);const d=a*a+
+b*b;return d!==0?(new c(a/d,-b/d)).acos():(new c(a!==0?a/0:0,b!==0?-b/0:0)).acos()},acsc:function(){const a=this.re,b=this.im;if(a===0&&b===0)return new c(Math.PI/2,Infinity);const d=a*a+b*b;return d!==0?(new c(a/d,-b/d)).asin():(new c(a!==0?a/0:0,b!==0?-b/0:0)).asin()},sinh:function(){const a=this.re,b=this.im;return new c(k(a)*Math.cos(b),h(a)*Math.sin(b))},cosh:function(){const a=this.re,b=this.im;return new c(h(a)*Math.cos(b),k(a)*Math.sin(b))},tanh:function(){const a=2*this.re,b=2*this.im,d=
+h(a)+Math.cos(b);return new c(k(a)/d,Math.sin(b)/d)},coth:function(){const a=2*this.re,b=2*this.im,d=h(a)-Math.cos(b);return new c(k(a)/d,-Math.sin(b)/d)},csch:function(){const a=this.re,b=this.im,d=Math.cos(2*b)-h(2*a);return new c(-2*k(a)*Math.cos(b)/d,2*h(a)*Math.sin(b)/d)},sech:function(){const a=this.re,b=this.im,d=Math.cos(2*b)+h(2*a);return new c(2*h(a)*Math.cos(b)/d,-2*k(a)*Math.sin(b)/d)},asinh:function(){const a=this.re;var b=this.im;if(b===0){if(a===0)return new c(0,0);b=Math.abs(a);b=
+Math.log(b+Math.sqrt(b*b+1));return new c(a<0?-b:b,0)}const d=(new c(a*a-b*b+1,2*a*b)).sqrt();return(new c(a+d.re,b+d.im)).log()},acosh:function(){const a=this.re,b=this.im;if(b===0)return a>1?new c(Math.log(a+Math.sqrt(a-1)*Math.sqrt(a+1)),0):a<-1?new c(Math.log(-a+Math.sqrt(a*a-1)),Math.PI):new c(0,Math.acos(a));const d=(new c(a-1,b)).sqrt(),e=(new c(a+1,b)).sqrt();return(new c(a+d.re*e.re-d.im*e.im,b+d.re*e.im+d.im*e.re)).log()},atanh:function(){var a=this.re,b=this.im;if(b===0)return a===0?new c(0,
+0):a===1?new c(Infinity,0):a===-1?new c(-Infinity,0):-11?new c(.5*Math.log((a+1)/(a-1)),-Math.PI/2):new c(.5*Math.log(-((1+a)/(1-a))),Math.PI/2);const d=1-a,e=1+a,g=d*d+b*b;if(g===0)return new c(a!==-1?a/0:0,b!==0?b/0:0);a=(e*d-b*b)/g;b=(b*d+e*b)/g;return new c(p(a,b)/2,Math.atan2(b,a)/2)},acoth:function(){const a=this.re,b=this.im;if(a===0&&b===0)return new c(0,Math.PI/2);const d=a*a+b*b;return d!==0?(new c(a/d,-b/d)).atanh():(new c(a!==0?a/0:0,b!==0?-b/
+0:0)).atanh()},acsch:function(){var a=this.re;const b=this.im;if(b===0){if(a===0)return new c(Infinity,0);a=1/a;return new c(Math.log(a+Math.sqrt(a*a+1)),0)}const d=a*a+b*b;return d!==0?(new c(a/d,-b/d)).asinh():(new c(a!==0?a/0:0,b!==0?-b/0:0)).asinh()},asech:function(){const a=this.re,b=this.im;if(this.isZero())return c.INFINITY;const d=a*a+b*b;return d!==0?(new c(a/d,-b/d)).acosh():(new c(a!==0?a/0:0,b!==0?-b/0:0)).acosh()},inverse:function(){if(this.isZero())return c.INFINITY;if(this.isInfinite())return c.ZERO;
+const a=this.re,b=this.im,d=a*a+b*b;return new c(a/d,-b/d)},conjugate:function(){return new c(this.re,-this.im)},neg:function(){return new c(-this.re,-this.im)},ceil:function(a){a=Math.pow(10,a||0);return new c(Math.ceil(this.re*a)/a,Math.ceil(this.im*a)/a)},floor:function(a){a=Math.pow(10,a||0);return new c(Math.floor(this.re*a)/a,Math.floor(this.im*a)/a)},round:function(a){a=Math.pow(10,a||0);return new c(Math.round(this.re*a)/a,Math.round(this.im*a)/a)},equals:function(a,b){a=l(a,b);return Math.abs(a.re-
+this.re)<=c.EPSILON&&Math.abs(a.im-this.im)<=c.EPSILON},clone:function(){return new c(this.re,this.im)},toString:function(){let a=this.re,b=this.im,d="";if(this.isNaN())return"NaN";if(this.isInfinite())return"Infinity";Math.abs(a), im: }
+ * { arg: , abs: }
+ * { phi: , r: }
+ *
+ * Array / Vector form
+ * [ real, imaginary ]
+ *
+ * Double form
+ * 99.3 - Single double value
+ *
+ * String form
+ * '23.1337' - Simple real number
+ * '15+3i' - a simple complex number
+ * '3-i' - a simple complex number
+ *
+ * Example:
+ *
+ * const c = new Complex('99.3+8i');
+ * c.mul({r: 3, i: 9}).div(4.9).sub(3, 2);
+ *
+ */
+
+
+const cosh = Math.cosh || function (x) {
+ return Math.abs(x) < 1e-9 ? 1 - x : (Math.exp(x) + Math.exp(-x)) * 0.5;
+};
+
+const sinh = Math.sinh || function (x) {
+ return Math.abs(x) < 1e-9 ? x : (Math.exp(x) - Math.exp(-x)) * 0.5;
+};
+
+/**
+ * Calculates cos(x) - 1 using Taylor series if x is small (-¼π ≤ x ≤ ¼π).
+ *
+ * @param {number} x
+ * @returns {number} cos(x) - 1
+ */
+const cosm1 = x => {
+ // cos(x) - 1 = − 2sin^2(x / 2)
+ const s = Math.sin(0.5 * x);
+ return -2 * s * s;
+};
+
+const hypot = function (x, y) {
+
+ x = Math.abs(x);
+ y = Math.abs(y);
+
+ // Ensure `x` is the larger value
+ if (x < y) [x, y] = [y, x];
+
+ // If both are below the threshold, use straightforward Pythagoras
+ if (x < 1e8) return Math.sqrt(x * x + y * y);
+
+ // For larger values, scale to avoid overflow
+ y /= x;
+ return x * Math.sqrt(1 + y * y);
+};
+
+const parser_exit = function () {
+ throw SyntaxError('Invalid Param');
+};
+
+/**
+ * Calculates log(sqrt(a^2+b^2)) in a way to avoid overflows
+ *
+ * @param {number} a
+ * @param {number} b
+ * @returns {number}
+ */
+function logHypot(a, b) {
+
+ const _a = Math.abs(a);
+ const _b = Math.abs(b);
+
+ if (a === 0) {
+ return Math.log(_b);
+ }
+
+ if (b === 0) {
+ return Math.log(_a);
+ }
+
+ if (_a < 3000 && _b < 3000) {
+ return Math.log(a * a + b * b) * 0.5;
+ }
+
+ /* I got 4 ideas to compute this property without overflow:
+ *
+ * Testing 1000000 times with random samples for a,b ∈ [1, 1000000000] against a big decimal library to get an error estimate
+ *
+ * 1. Only eliminate the square root: (OVERALL ERROR: 3.9122483030951116e-11)
+
+ Math.log(a * a + b * b) / 2
+
+ *
+ *
+ * 2. Try to use the non-overflowing pythagoras: (OVERALL ERROR: 8.889760039210159e-10)
+
+ const fn = function(a, b) {
+ a = Math.abs(a);
+ b = Math.abs(b);
+ let t = Math.min(a, b);
+ a = Math.max(a, b);
+ t = t / a;
+
+ return Math.log(a) + Math.log(1 + t * t) / 2;
+ };
+
+ * 3. Abuse the identity cos(atan(y/x) = x / sqrt(x^2+y^2): (OVERALL ERROR: 3.4780178737037204e-10)
+
+ Math.log(a / Math.cos(Math.atan2(b, a)))
+
+ * 4. Use 3. and apply log rules: (OVERALL ERROR: 1.2014087502620896e-9)
+
+ Math.log(a) - Math.log(Math.cos(Math.atan2(b, a)))
+
+ */
+
+ a = a * 0.5;
+ b = b * 0.5;
+
+ return 0.5 * Math.log(a * a + b * b) + Math.LN2;
+}
+
+const P = { 're': 0, 'im': 0 };
+const parse = function (a, b) {
+
+ const z = P;
+
+ if (a === undefined || a === null) {
+ z['re'] =
+ z['im'] = 0;
+ } else if (b !== undefined) {
+ z['re'] = a;
+ z['im'] = b;
+ } else
+ switch (typeof a) {
+
+ case 'object':
+
+ if ('im' in a && 're' in a) {
+ z['re'] = a['re'];
+ z['im'] = a['im'];
+ } else if ('abs' in a && 'arg' in a) {
+ if (!isFinite(a['abs']) && isFinite(a['arg'])) {
+ return Complex['INFINITY'];
+ }
+ z['re'] = a['abs'] * Math.cos(a['arg']);
+ z['im'] = a['abs'] * Math.sin(a['arg']);
+ } else if ('r' in a && 'phi' in a) {
+ if (!isFinite(a['r']) && isFinite(a['phi'])) {
+ return Complex['INFINITY'];
+ }
+ z['re'] = a['r'] * Math.cos(a['phi']);
+ z['im'] = a['r'] * Math.sin(a['phi']);
+ } else if (a.length === 2) { // Quick array check
+ z['re'] = a[0];
+ z['im'] = a[1];
+ } else {
+ parser_exit();
+ }
+ break;
+
+ case 'string':
+
+ z['im'] = /* void */
+ z['re'] = 0;
+
+ const tokens = a.replace(/_/g, '')
+ .match(/\d+\.?\d*e[+-]?\d+|\d+\.?\d*|\.\d+|./g);
+ let plus = 1;
+ let minus = 0;
+
+ if (tokens === null) {
+ parser_exit();
+ }
+
+ for (let i = 0; i < tokens.length; i++) {
+
+ const c = tokens[i];
+
+ if (c === ' ' || c === '\t' || c === '\n') {
+ /* void */
+ } else if (c === '+') {
+ plus++;
+ } else if (c === '-') {
+ minus++;
+ } else if (c === 'i' || c === 'I') {
+
+ if (plus + minus === 0) {
+ parser_exit();
+ }
+
+ if (tokens[i + 1] !== ' ' && !isNaN(tokens[i + 1])) {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + tokens[i + 1]);
+ i++;
+ } else {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + '1');
+ }
+ plus = minus = 0;
+
+ } else {
+
+ if (plus + minus === 0 || isNaN(c)) {
+ parser_exit();
+ }
+
+ if (tokens[i + 1] === 'i' || tokens[i + 1] === 'I') {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + c);
+ i++;
+ } else {
+ z['re'] += parseFloat((minus % 2 ? '-' : '') + c);
+ }
+ plus = minus = 0;
+ }
+ }
+
+ // Still something on the stack
+ if (plus + minus > 0) {
+ parser_exit();
+ }
+ break;
+
+ case 'number':
+ z['im'] = 0;
+ z['re'] = a;
+ break;
+
+ default:
+ parser_exit();
+ }
+
+ if (isNaN(z['re']) || isNaN(z['im'])) {
+ // If a calculation is NaN, we treat it as NaN and don't throw
+ //parser_exit();
+ }
+
+ return z;
+};
+
+/**
+ * @constructor
+ * @returns {Complex}
+ */
+function Complex(a, b) {
+
+ if (!(this instanceof Complex)) {
+ return new Complex(a, b);
+ }
+
+ const z = parse(a, b);
+
+ this['re'] = z['re'];
+ this['im'] = z['im'];
+}
+
+Complex.prototype = {
+
+ 're': 0,
+ 'im': 0,
+
+ /**
+ * Calculates the sign of a complex number, which is a normalized complex
+ *
+ * @returns {Complex}
+ */
+ 'sign': function () {
+
+ const abs = hypot(this['re'], this['im']);
+
+ return new Complex(
+ this['re'] / abs,
+ this['im'] / abs);
+ },
+
+ /**
+ * Adds two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'add': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+
+ if (tInfin || zInfin) {
+
+ if (tInfin && zInfin) {
+ // Infinity + Infinity = NaN
+ return Complex['NAN'];
+ }
+ // Infinity + z = Infinity { where z != Infinity }
+ return Complex['INFINITY'];
+ }
+
+ return new Complex(
+ this['re'] + z['re'],
+ this['im'] + z['im']);
+ },
+
+ /**
+ * Subtracts two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'sub': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+
+ if (tInfin || zInfin) {
+
+ if (tInfin && zInfin) {
+ // Infinity - Infinity = NaN
+ return Complex['NAN'];
+ }
+ // Infinity - z = Infinity { where z != Infinity }
+ return Complex['INFINITY'];
+ }
+
+ return new Complex(
+ this['re'] - z['re'],
+ this['im'] - z['im']);
+ },
+
+ /**
+ * Multiplies two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'mul': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ // Infinity * 0 = NaN
+ if (tInfin && zIsZero || zInfin && tIsZero) {
+ return Complex['NAN'];
+ }
+
+ // Infinity * z = Infinity { where z != 0 }
+ if (tInfin || zInfin) {
+ return Complex['INFINITY'];
+ }
+
+ // Shortcut for real values
+ if (z['im'] === 0 && this['im'] === 0) {
+ return new Complex(this['re'] * z['re'], 0);
+ }
+
+ return new Complex(
+ this['re'] * z['re'] - this['im'] * z['im'],
+ this['re'] * z['im'] + this['im'] * z['re']);
+ },
+
+ /**
+ * Divides two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'div': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ // 0 / 0 = NaN and Infinity / Infinity = NaN
+ if (tIsZero && zIsZero || tInfin && zInfin) {
+ return Complex['NAN'];
+ }
+
+ // Infinity / 0 = Infinity
+ if (zIsZero || tInfin) {
+ return Complex['INFINITY'];
+ }
+
+ // 0 / Infinity = 0
+ if (tIsZero || zInfin) {
+ return Complex['ZERO'];
+ }
+
+ if (0 === z['im']) {
+ // Divisor is real
+ return new Complex(this['re'] / z['re'], this['im'] / z['re']);
+ }
+
+ if (Math.abs(z['re']) < Math.abs(z['im'])) {
+
+ const x = z['re'] / z['im'];
+ const t = z['re'] * x + z['im'];
+
+ return new Complex(
+ (this['re'] * x + this['im']) / t,
+ (this['im'] * x - this['re']) / t);
+
+ } else {
+
+ const x = z['im'] / z['re'];
+ const t = z['im'] * x + z['re'];
+
+ return new Complex(
+ (this['re'] + this['im'] * x) / t,
+ (this['im'] - this['re'] * x) / t);
+ }
+ },
+
+ /**
+ * Calculate the power of two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'pow': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ if (zIsZero) {
+ return Complex['ONE'];
+ }
+
+ // If the exponent is real
+ if (z['im'] === 0) {
+
+ if (this['im'] === 0 && this['re'] > 0) {
+
+ return new Complex(Math.pow(this['re'], z['re']), 0);
+
+ } else if (this['re'] === 0) { // If base is fully imaginary
+
+ switch ((z['re'] % 4 + 4) % 4) {
+ case 0:
+ return new Complex(Math.pow(this['im'], z['re']), 0);
+ case 1:
+ return new Complex(0, Math.pow(this['im'], z['re']));
+ case 2:
+ return new Complex(-Math.pow(this['im'], z['re']), 0);
+ case 3:
+ return new Complex(0, -Math.pow(this['im'], z['re']));
+ }
+ }
+ }
+
+ /* I couldn't find a good formula, so here is a derivation and optimization
+ *
+ * z_1^z_2 = (a + bi)^(c + di)
+ * = exp((c + di) * log(a + bi)
+ * = pow(a^2 + b^2, (c + di) / 2) * exp(i(c + di)atan2(b, a))
+ * =>...
+ * Re = (pow(a^2 + b^2, c / 2) * exp(-d * atan2(b, a))) * cos(d * log(a^2 + b^2) / 2 + c * atan2(b, a))
+ * Im = (pow(a^2 + b^2, c / 2) * exp(-d * atan2(b, a))) * sin(d * log(a^2 + b^2) / 2 + c * atan2(b, a))
+ *
+ * =>...
+ * Re = exp(c * log(sqrt(a^2 + b^2)) - d * atan2(b, a)) * cos(d * log(sqrt(a^2 + b^2)) + c * atan2(b, a))
+ * Im = exp(c * log(sqrt(a^2 + b^2)) - d * atan2(b, a)) * sin(d * log(sqrt(a^2 + b^2)) + c * atan2(b, a))
+ *
+ * =>
+ * Re = exp(c * logsq2 - d * arg(z_1)) * cos(d * logsq2 + c * arg(z_1))
+ * Im = exp(c * logsq2 - d * arg(z_1)) * sin(d * logsq2 + c * arg(z_1))
+ *
+ */
+
+ if (tIsZero && z['re'] > 0) { // Same behavior as Wolframalpha, Zero if real part is zero
+ return Complex['ZERO'];
+ }
+
+ const arg = Math.atan2(this['im'], this['re']);
+ const loh = logHypot(this['re'], this['im']);
+
+ let re = Math.exp(z['re'] * loh - z['im'] * arg);
+ let im = z['im'] * loh + z['re'] * arg;
+ return new Complex(
+ re * Math.cos(im),
+ re * Math.sin(im));
+ },
+
+ /**
+ * Calculate the complex square root
+ *
+ * @returns {Complex}
+ */
+ 'sqrt': function () {
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+ // Real number case
+ if (a >= 0) {
+ return new Complex(Math.sqrt(a), 0);
+ } else {
+ return new Complex(0, Math.sqrt(-a));
+ }
+ }
+
+ const r = hypot(a, b);
+
+ let re = Math.sqrt(0.5 * (r + Math.abs(a))); // sqrt(2x) / 2 = sqrt(x / 2)
+ let im = Math.abs(b) / (2 * re);
+
+ if (a >= 0) {
+ return new Complex(re, b < 0 ? -im : im);
+ } else {
+ return new Complex(im, b < 0 ? -re : re);
+ }
+ },
+
+ /**
+ * Calculate the complex exponent
+ *
+ * @returns {Complex}
+ */
+ 'exp': function () {
+
+ const er = Math.exp(this['re']);
+
+ if (this['im'] === 0) {
+ return new Complex(er, 0);
+ }
+ return new Complex(
+ er * Math.cos(this['im']),
+ er * Math.sin(this['im']));
+ },
+
+ /**
+ * Calculate the complex exponent and subtracts one.
+ *
+ * This may be more accurate than `Complex(x).exp().sub(1)` if
+ * `x` is small.
+ *
+ * @returns {Complex}
+ */
+ 'expm1': function () {
+
+ /**
+ * exp(a + i*b) - 1
+ = exp(a) * (cos(b) + j*sin(b)) - 1
+ = expm1(a)*cos(b) + cosm1(b) + j*exp(a)*sin(b)
+ */
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.expm1(a) * Math.cos(b) + cosm1(b),
+ Math.exp(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the natural log
+ *
+ * @returns {Complex}
+ */
+ 'log': function () {
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0 && a > 0) {
+ return new Complex(Math.log(a), 0);
+ }
+
+ return new Complex(
+ logHypot(a, b),
+ Math.atan2(b, a));
+ },
+
+ /**
+ * Calculate the magnitude of the complex number
+ *
+ * @returns {number}
+ */
+ 'abs': function () {
+
+ return hypot(this['re'], this['im']);
+ },
+
+ /**
+ * Calculate the angle of the complex number
+ *
+ * @returns {number}
+ */
+ 'arg': function () {
+
+ return Math.atan2(this['im'], this['re']);
+ },
+
+ /**
+ * Calculate the sine of the complex number
+ *
+ * @returns {Complex}
+ */
+ 'sin': function () {
+
+ // sin(z) = ( e^iz - e^-iz ) / 2i
+ // = sin(a)cosh(b) + i cos(a)sinh(b)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.sin(a) * cosh(b),
+ Math.cos(a) * sinh(b));
+ },
+
+ /**
+ * Calculate the cosine
+ *
+ * @returns {Complex}
+ */
+ 'cos': function () {
+
+ // cos(z) = ( e^iz + e^-iz ) / 2
+ // = cos(a)cosh(b) - i sin(a)sinh(b)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.cos(a) * cosh(b),
+ -Math.sin(a) * sinh(b));
+ },
+
+ /**
+ * Calculate the tangent
+ *
+ * @returns {Complex}
+ */
+ 'tan': function () {
+
+ // tan(z) = sin(z) / cos(z)
+ // = ( e^iz - e^-iz ) / ( i( e^iz + e^-iz ) )
+ // = ( e^2iz - 1 ) / i( e^2iz + 1 )
+ // = ( sin(2a) + i sinh(2b) ) / ( cos(2a) + cosh(2b) )
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = Math.cos(a) + cosh(b);
+
+ return new Complex(
+ Math.sin(a) / d,
+ sinh(b) / d);
+ },
+
+ /**
+ * Calculate the cotangent
+ *
+ * @returns {Complex}
+ */
+ 'cot': function () {
+
+ // cot(c) = i(e^(ci) + e^(-ci)) / (e^(ci) - e^(-ci))
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = Math.cos(a) - cosh(b);
+
+ return new Complex(
+ -Math.sin(a) / d,
+ sinh(b) / d);
+ },
+
+ /**
+ * Calculate the secant
+ *
+ * @returns {Complex}
+ */
+ 'sec': function () {
+
+ // sec(c) = 2 / (e^(ci) + e^(-ci))
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = 0.5 * cosh(2 * b) + 0.5 * Math.cos(2 * a);
+
+ return new Complex(
+ Math.cos(a) * cosh(b) / d,
+ Math.sin(a) * sinh(b) / d);
+ },
+
+ /**
+ * Calculate the cosecans
+ *
+ * @returns {Complex}
+ */
+ 'csc': function () {
+
+ // csc(c) = 2i / (e^(ci) - e^(-ci))
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = 0.5 * cosh(2 * b) - 0.5 * Math.cos(2 * a);
+
+ return new Complex(
+ Math.sin(a) * cosh(b) / d,
+ -Math.cos(a) * sinh(b) / d);
+ },
+
+ /**
+ * Calculate the complex arcus sinus
+ *
+ * @returns {Complex}
+ */
+ 'asin': function () {
+
+ // asin(c) = -i * log(ci + sqrt(1 - c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const t1 = new Complex(
+ b * b - a * a + 1,
+ -2 * a * b)['sqrt']();
+
+ const t2 = new Complex(
+ t1['re'] - b,
+ t1['im'] + a)['log']();
+
+ return new Complex(t2['im'], -t2['re']);
+ },
+
+ /**
+ * Calculate the complex arcus cosinus
+ *
+ * @returns {Complex}
+ */
+ 'acos': function () {
+
+ // acos(c) = i * log(c - i * sqrt(1 - c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const t1 = new Complex(
+ b * b - a * a + 1,
+ -2 * a * b)['sqrt']();
+
+ const t2 = new Complex(
+ t1['re'] - b,
+ t1['im'] + a)['log']();
+
+ return new Complex(Math.PI / 2 - t2['im'], t2['re']);
+ },
+
+ /**
+ * Calculate the complex arcus tangent
+ *
+ * @returns {Complex}
+ */
+ 'atan': function () {
+
+ // atan(c) = i / 2 log((i + x) / (i - x))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0) {
+
+ if (b === 1) {
+ return new Complex(0, Infinity);
+ }
+
+ if (b === -1) {
+ return new Complex(0, -Infinity);
+ }
+ }
+
+ const d = a * a + (1.0 - b) * (1.0 - b);
+
+ const t1 = new Complex(
+ (1 - b * b - a * a) / d,
+ -2 * a / d).log();
+
+ return new Complex(-0.5 * t1['im'], 0.5 * t1['re']);
+ },
+
+ /**
+ * Calculate the complex arcus cotangent
+ *
+ * @returns {Complex}
+ */
+ 'acot': function () {
+
+ // acot(c) = i / 2 log((c - i) / (c + i))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+ return new Complex(Math.atan2(1, a), 0);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).atan()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).atan();
+ },
+
+ /**
+ * Calculate the complex arcus secant
+ *
+ * @returns {Complex}
+ */
+ 'asec': function () {
+
+ // asec(c) = -i * log(1 / c + sqrt(1 - i / c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0 && b === 0) {
+ return new Complex(0, Infinity);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).acos()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).acos();
+ },
+
+ /**
+ * Calculate the complex arcus cosecans
+ *
+ * @returns {Complex}
+ */
+ 'acsc': function () {
+
+ // acsc(c) = -i * log(i / c + sqrt(1 - 1 / c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0 && b === 0) {
+ return new Complex(Math.PI / 2, Infinity);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).asin()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).asin();
+ },
+
+ /**
+ * Calculate the complex sinh
+ *
+ * @returns {Complex}
+ */
+ 'sinh': function () {
+
+ // sinh(c) = (e^c - e^-c) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ sinh(a) * Math.cos(b),
+ cosh(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the complex cosh
+ *
+ * @returns {Complex}
+ */
+ 'cosh': function () {
+
+ // cosh(c) = (e^c + e^-c) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ cosh(a) * Math.cos(b),
+ sinh(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the complex tanh
+ *
+ * @returns {Complex}
+ */
+ 'tanh': function () {
+
+ // tanh(c) = (e^c - e^-c) / (e^c + e^-c)
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = cosh(a) + Math.cos(b);
+
+ return new Complex(
+ sinh(a) / d,
+ Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex coth
+ *
+ * @returns {Complex}
+ */
+ 'coth': function () {
+
+ // coth(c) = (e^c + e^-c) / (e^c - e^-c)
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = cosh(a) - Math.cos(b);
+
+ return new Complex(
+ sinh(a) / d,
+ -Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex csch
+ *
+ * @returns {Complex}
+ */
+ 'csch': function () {
+
+ // csch(c) = 2 / (e^c - e^-c)
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = Math.cos(2 * b) - cosh(2 * a);
+
+ return new Complex(
+ -2 * sinh(a) * Math.cos(b) / d,
+ 2 * cosh(a) * Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex sech
+ *
+ * @returns {Complex}
+ */
+ 'sech': function () {
+
+ // sech(c) = 2 / (e^c + e^-c)
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = Math.cos(2 * b) + cosh(2 * a);
+
+ return new Complex(
+ 2 * cosh(a) * Math.cos(b) / d,
+ -2 * sinh(a) * Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex asinh
+ *
+ * @returns {Complex}
+ */
+ 'asinh': function () {
+
+ // asinh(z) = log(z + sqrt(z^2 + 1))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ if (a === 0) {
+ return new Complex(0, 0);
+ }
+
+ // Use |a| to keep asinh(-a) = -asinh(a) and avoid cancellation for large -a
+ const x = Math.abs(a);
+ const r = Math.log(x + Math.sqrt(x * x + 1));
+
+ return new Complex(a < 0 ? -r : r, 0);
+ }
+
+ // z^2 + 1 = (a^2 - b^2 + 1) + i (2ab)
+ const re2 = a * a - b * b + 1;
+ const im2 = 2 * a * b;
+
+ const t = new Complex(re2, im2)['sqrt'](); // sqrt(z^2 + 1)
+
+ return new Complex(a + t['re'], b + t['im'])['log']();
+ },
+
+ /**
+ * Calculate the complex acosh
+ *
+ * @returns {Complex}
+ */
+ 'acosh': function () {
+
+ // acosh(z)= log(z + sqrt(z^2 - 1)) = log(z + sqrt(z - 1) * sqrt(z + 1))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ // z = a is real
+ if (a > 1) {
+ // acosh(a) = log(a + sqrt(a - 1) * sqrt(a + 1)), a > 1
+ return new Complex(
+ Math.log(a + Math.sqrt(a - 1) * Math.sqrt(a + 1)), 0);
+ }
+
+ if (a < -1) {
+ // acosh(a) = log(-a + sqrt(a^2 - 1)) + i*pi, a < -1
+ const t = Math.sqrt(a * a - 1);
+ return new Complex(Math.log(-a + t), Math.PI);
+ }
+
+ // -1 <= a <= 1 : purely imaginary
+ // acosh(a) = i * acos(a)
+ return new Complex(0, Math.acos(a));
+ }
+
+ const t1 = new Complex(a - 1, b)['sqrt'](); // sqrt(z - 1)
+ const t2 = new Complex(a + 1, b)['sqrt'](); // sqrt(z + 1)
+
+ return new Complex(
+ a + t1['re'] * t2['re'] - t1['im'] * t2['im'],
+ b + t1['re'] * t2['im'] + t1['im'] * t2['re']
+ )['log']();
+ },
+
+ /**
+ * Calculate the complex atanh
+ *
+ * @returns {Complex}
+ */
+ 'atanh': function () {
+
+ // atanh(z) = log((1 + z) / (1 - z)) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ if (a === 0) {
+ return new Complex(0, 0);
+ }
+
+ if (a === 1) {
+ // limit x -> 1^- atanh(x) = +Infinity
+ return new Complex(Infinity, 0);
+ }
+
+ if (a === -1) {
+ // limit x -> -1^+ atanh(x) = -Infinity
+ return new Complex(-Infinity, 0);
+ }
+
+ if (-1 < a && a < 1) {
+ // Purely real
+ return new Complex(
+ 0.5 * Math.log((1 + a) / (1 - a)),
+ 0
+ );
+ }
+
+ if (a > 1) {
+ // Our branch: Im(atanh(a)) = -π/2 for a > 1
+ const t = (a + 1) / (a - 1); // > 0
+ return new Complex(
+ 0.5 * Math.log(t),
+ -Math.PI / 2
+ );
+ }
+
+ // a < -1: Im(atanh(a)) = +π/2
+ const t = (1 + a) / (1 - a); // < 0
+ return new Complex(
+ 0.5 * Math.log(-t), // log((1 - a)/(1 + a))
+ Math.PI / 2
+ );
+ }
+
+ // Use atanh(z) = 0.5 * Log((1+z)/(1-z)) with principal Log.
+ const oneMinus = 1 - a;
+ const onePlus = 1 + a;
+ const d = oneMinus * oneMinus + b * b; // |1 - z|^2
+
+ if (d === 0) {
+ // (1 - z) == 0 for finite z only at z = 1+0i, already handled above.
+ // If we ever get here, just propagate infinities consistently:
+ return new Complex(
+ (a !== -1) ? (a / 0) : 0,
+ (b !== 0) ? (b / 0) : 0
+ );
+ }
+
+ // (1 + z) / (1 - z) with a single complex division
+ const xr = (onePlus * oneMinus - b * b) / d;
+ const xi = (b * oneMinus + onePlus * b) / d;
+
+ // 0.5 * log(xr + i xi)
+ return new Complex(
+ logHypot(xr, xi) / 2,
+ Math.atan2(xi, xr) / 2
+ );
+ },
+
+ /**
+ * Calculate the complex acoth
+ *
+ * @returns {Complex}
+ */
+ 'acoth': function () {
+
+ // acoth(z) = log((z + 1) / (z - 1)) / 2 = atanh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ // z = 0 -> acoth(0) = i * π / 2 (log((1+0)/(1-0))/2 = log(-1)/2)
+ if (a === 0 && b === 0) {
+ return new Complex(0, Math.PI / 2);
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1 / z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['atanh']();
+ }
+
+ // Fallback for weird infinities/NaNs: mirror other functions' style
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0
+ )['atanh']();
+ },
+
+ /**
+ * Calculate the complex acsch
+ *
+ * @returns {Complex}
+ */
+ 'acsch': function () {
+
+ // acsch(c) = log((1+sqrt(1+c^2))/c) = = asinh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ // z = a real
+ if (a === 0) {
+ // acsch(0) -> +/- Infinity, we keep your previous behavior
+ return new Complex(Infinity, 0);
+ }
+
+ // acsch(a) = asinh(1/a) = log(1/a + sqrt(1/a^2 + 1))
+ const inv = 1 / a;
+ return new Complex(
+ Math.log(inv + Math.sqrt(inv * inv + 1)),
+ 0
+ );
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1/z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['asinh']();
+ }
+
+ // Handle 0 + 0i or infinities in the same spirit as your existing code
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0)['asinh']();
+ },
+
+ /**
+ * Calculate the complex asech
+ *
+ * @returns {Complex}
+ */
+ 'asech': function () {
+
+ // asech(z) = acosh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (this['isZero']()) {
+ // asech(0) = acosh(∞) -> ∞
+ return Complex['INFINITY'];
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1 / z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['acosh']();
+ }
+
+ // Fallback for weird infinities/NaNs
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0)['acosh']();
+ },
+
+ /**
+ * Calculate the complex inverse 1/z
+ *
+ * @returns {Complex}
+ */
+ 'inverse': function () {
+
+ // 1 / 0 = Infinity and 1 / Infinity = 0
+ if (this['isZero']()) {
+ return Complex['INFINITY'];
+ }
+
+ if (this['isInfinite']()) {
+ return Complex['ZERO'];
+ }
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const d = a * a + b * b;
+
+ return new Complex(a / d, -b / d);
+ },
+
+ /**
+ * Returns the complex conjugate
+ *
+ * @returns {Complex}
+ */
+ 'conjugate': function () {
+
+ return new Complex(this['re'], -this['im']);
+ },
+
+ /**
+ * Gets the negated complex number
+ *
+ * @returns {Complex}
+ */
+ 'neg': function () {
+
+ return new Complex(-this['re'], -this['im']);
+ },
+
+ /**
+ * Ceils the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'ceil': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.ceil(this['re'] * places) / places,
+ Math.ceil(this['im'] * places) / places);
+ },
+
+ /**
+ * Floors the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'floor': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.floor(this['re'] * places) / places,
+ Math.floor(this['im'] * places) / places);
+ },
+
+ /**
+ * Ceils the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'round': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.round(this['re'] * places) / places,
+ Math.round(this['im'] * places) / places);
+ },
+
+ /**
+ * Compares two complex numbers
+ *
+ * **Note:** new Complex(Infinity).equals(Infinity) === false
+ *
+ * @returns {boolean}
+ */
+ 'equals': function (a, b) {
+
+ const z = parse(a, b);
+
+ return Math.abs(z['re'] - this['re']) <= Complex['EPSILON'] &&
+ Math.abs(z['im'] - this['im']) <= Complex['EPSILON'];
+ },
+
+ /**
+ * Clones the actual object
+ *
+ * @returns {Complex}
+ */
+ 'clone': function () {
+
+ return new Complex(this['re'], this['im']);
+ },
+
+ /**
+ * Gets a string of the actual complex number
+ *
+ * @returns {string}
+ */
+ 'toString': function () {
+
+ let a = this['re'];
+ let b = this['im'];
+ let ret = "";
+
+ if (this['isNaN']()) {
+ return 'NaN';
+ }
+
+ if (this['isInfinite']()) {
+ return 'Infinity';
+ }
+
+ if (Math.abs(a) < Complex['EPSILON']) {
+ a = 0;
+ }
+
+ if (Math.abs(b) < Complex['EPSILON']) {
+ b = 0;
+ }
+
+ // If is real number
+ if (b === 0) {
+ return ret + a;
+ }
+
+ if (a !== 0) {
+ ret += a;
+ ret += " ";
+ if (b < 0) {
+ b = -b;
+ ret += "-";
+ } else {
+ ret += "+";
+ }
+ ret += " ";
+ } else if (b < 0) {
+ b = -b;
+ ret += "-";
+ }
+
+ if (1 !== b) { // b is the absolute imaginary part
+ ret += b;
+ }
+ return ret + "i";
+ },
+
+ /**
+ * Returns the actual number as a vector
+ *
+ * @returns {Array}
+ */
+ 'toVector': function () {
+
+ return [this['re'], this['im']];
+ },
+
+ /**
+ * Returns the actual real value of the current object
+ *
+ * @returns {number|null}
+ */
+ 'valueOf': function () {
+
+ if (this['im'] === 0) {
+ return this['re'];
+ }
+ return null;
+ },
+
+ /**
+ * Determines whether a complex number is not on the Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isNaN': function () {
+ return isNaN(this['re']) || isNaN(this['im']);
+ },
+
+ /**
+ * Determines whether or not a complex number is at the zero pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isZero': function () {
+ return this['im'] === 0 && this['re'] === 0;
+ },
+
+ /**
+ * Determines whether a complex number is not at the infinity pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isFinite': function () {
+ return isFinite(this['re']) && isFinite(this['im']);
+ },
+
+ /**
+ * Determines whether or not a complex number is at the infinity pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isInfinite': function () {
+ return !this['isFinite']();
+ }
+};
+
+Complex['ZERO'] = new Complex(0, 0);
+Complex['ONE'] = new Complex(1, 0);
+Complex['I'] = new Complex(0, 1);
+Complex['PI'] = new Complex(Math.PI, 0);
+Complex['E'] = new Complex(Math.E, 0);
+Complex['INFINITY'] = new Complex(Infinity, Infinity);
+Complex['NAN'] = new Complex(NaN, NaN);
+Complex['EPSILON'] = 1e-15;
+export {
+ Complex as default, Complex
+};
diff --git a/node_modules/complex.js/examples/gamma.js b/node_modules/complex.js/examples/gamma.js
new file mode 100644
index 0000000..1cc739d
--- /dev/null
+++ b/node_modules/complex.js/examples/gamma.js
@@ -0,0 +1,33 @@
+/*
+ * A gamma function implementation based on Lanczos Approximation
+ * https://en.wikipedia.org/wiki/Lanczos_approximation
+ */
+
+var Complex = require('complex.js');
+
+var P = [
+ Complex(0.99999999999980993),
+ Complex(676.5203681218851), Complex(-1259.1392167224028), Complex(771.32342877765313),
+ Complex(-176.61502916214059), Complex(12.507343278686905), Complex(-0.13857109526572012),
+ Complex(9.9843695780195716e-6), Complex(1.5056327351493116e-7)
+];
+
+var SQRT2PI = Complex(Math.sqrt(2 * Math.PI));
+
+function gamma(z) {
+
+ z = z.sub(1);
+
+ var x = P[0];
+ var t = z.add(7.5);
+ for (var i = 1; i < P.length; i++) {
+ x = x.add(P[i].div(z.add(i)));
+ }
+ return SQRT2PI.mul(t.pow(z.add(0.5))).mul(t.neg().exp()).mul(x);
+}
+
+var fac = 1;
+for (var i = 1; i <= 10; i++) {
+ console.log(fac, gamma(Complex(i)));
+ fac *= i;
+}
diff --git a/node_modules/complex.js/package.json b/node_modules/complex.js/package.json
new file mode 100644
index 0000000..0f19826
--- /dev/null
+++ b/node_modules/complex.js/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "complex.js",
+ "title": "Complex.js",
+ "version": "2.4.3",
+ "homepage": "https://raw.org/article/complex-numbers-in-javascript/",
+ "bugs": "https://github.com/rawify/Complex.js/issues",
+ "description": "The RAW complex numbers library",
+ "keywords": [
+ "complex numbers",
+ "math",
+ "complex",
+ "number",
+ "calculus",
+ "parser",
+ "arithmetic"
+ ],
+ "private": false,
+ "main": "./dist/complex.js",
+ "module": "./dist/complex.mjs",
+ "types": "./complex.d.ts",
+ "browser": "./dist/complex.min.js",
+ "unpkg": "./dist/complex.min.js",
+ "readmeFilename": "README.md",
+ "exports": {
+ ".": {
+ "types": "./complex.d.ts",
+ "require": "./dist/complex.js",
+ "import": "./dist/complex.mjs"
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "git@github.com:rawify/Complex.js.git"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ },
+ "author": {
+ "name": "Robert Eisele",
+ "email": "robert@raw.org",
+ "url": "https://raw.org/"
+ },
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "directories": {
+ "example": "examples",
+ "test": "tests"
+ },
+ "scripts": {
+ "build": "crude-build Complex",
+ "test": "mocha tests/*.js"
+ },
+ "devDependencies": {
+ "crude-build": "^0.1.2",
+ "mocha": "*"
+ }
+}
diff --git a/node_modules/complex.js/src/complex.js b/node_modules/complex.js/src/complex.js
new file mode 100644
index 0000000..fbc1913
--- /dev/null
+++ b/node_modules/complex.js/src/complex.js
@@ -0,0 +1,1477 @@
+/**
+ * @license Complex.js v2.4.3 11/13/2025
+ * https://raw.org/article/complex-numbers-in-javascript/
+ *
+ * Copyright (c) 2025, Robert Eisele (https://raw.org/)
+ * Licensed under the MIT license.
+ **/
+
+/**
+ *
+ * This class allows the manipulation of complex numbers.
+ * You can pass a complex number in different formats. Either as object, double, string or two integer parameters.
+ *
+ * Object form
+ * { re: , im: }
+ * { arg: , abs: }
+ * { phi: , r: }
+ *
+ * Array / Vector form
+ * [ real, imaginary ]
+ *
+ * Double form
+ * 99.3 - Single double value
+ *
+ * String form
+ * '23.1337' - Simple real number
+ * '15+3i' - a simple complex number
+ * '3-i' - a simple complex number
+ *
+ * Example:
+ *
+ * const c = new Complex('99.3+8i');
+ * c.mul({r: 3, i: 9}).div(4.9).sub(3, 2);
+ *
+ */
+
+
+const cosh = Math.cosh || function (x) {
+ return Math.abs(x) < 1e-9 ? 1 - x : (Math.exp(x) + Math.exp(-x)) * 0.5;
+};
+
+const sinh = Math.sinh || function (x) {
+ return Math.abs(x) < 1e-9 ? x : (Math.exp(x) - Math.exp(-x)) * 0.5;
+};
+
+/**
+ * Calculates cos(x) - 1 using Taylor series if x is small (-¼π ≤ x ≤ ¼π).
+ *
+ * @param {number} x
+ * @returns {number} cos(x) - 1
+ */
+const cosm1 = x => {
+ // cos(x) - 1 = − 2sin^2(x / 2)
+ const s = Math.sin(0.5 * x);
+ return -2 * s * s;
+};
+
+const hypot = function (x, y) {
+
+ x = Math.abs(x);
+ y = Math.abs(y);
+
+ // Ensure `x` is the larger value
+ if (x < y) [x, y] = [y, x];
+
+ // If both are below the threshold, use straightforward Pythagoras
+ if (x < 1e8) return Math.sqrt(x * x + y * y);
+
+ // For larger values, scale to avoid overflow
+ y /= x;
+ return x * Math.sqrt(1 + y * y);
+};
+
+const parser_exit = function () {
+ throw SyntaxError('Invalid Param');
+};
+
+/**
+ * Calculates log(sqrt(a^2+b^2)) in a way to avoid overflows
+ *
+ * @param {number} a
+ * @param {number} b
+ * @returns {number}
+ */
+function logHypot(a, b) {
+
+ const _a = Math.abs(a);
+ const _b = Math.abs(b);
+
+ if (a === 0) {
+ return Math.log(_b);
+ }
+
+ if (b === 0) {
+ return Math.log(_a);
+ }
+
+ if (_a < 3000 && _b < 3000) {
+ return Math.log(a * a + b * b) * 0.5;
+ }
+
+ /* I got 4 ideas to compute this property without overflow:
+ *
+ * Testing 1000000 times with random samples for a,b ∈ [1, 1000000000] against a big decimal library to get an error estimate
+ *
+ * 1. Only eliminate the square root: (OVERALL ERROR: 3.9122483030951116e-11)
+
+ Math.log(a * a + b * b) / 2
+
+ *
+ *
+ * 2. Try to use the non-overflowing pythagoras: (OVERALL ERROR: 8.889760039210159e-10)
+
+ const fn = function(a, b) {
+ a = Math.abs(a);
+ b = Math.abs(b);
+ let t = Math.min(a, b);
+ a = Math.max(a, b);
+ t = t / a;
+
+ return Math.log(a) + Math.log(1 + t * t) / 2;
+ };
+
+ * 3. Abuse the identity cos(atan(y/x) = x / sqrt(x^2+y^2): (OVERALL ERROR: 3.4780178737037204e-10)
+
+ Math.log(a / Math.cos(Math.atan2(b, a)))
+
+ * 4. Use 3. and apply log rules: (OVERALL ERROR: 1.2014087502620896e-9)
+
+ Math.log(a) - Math.log(Math.cos(Math.atan2(b, a)))
+
+ */
+
+ a = a * 0.5;
+ b = b * 0.5;
+
+ return 0.5 * Math.log(a * a + b * b) + Math.LN2;
+}
+
+const P = { 're': 0, 'im': 0 };
+const parse = function (a, b) {
+
+ const z = P;
+
+ if (a === undefined || a === null) {
+ z['re'] =
+ z['im'] = 0;
+ } else if (b !== undefined) {
+ z['re'] = a;
+ z['im'] = b;
+ } else
+ switch (typeof a) {
+
+ case 'object':
+
+ if ('im' in a && 're' in a) {
+ z['re'] = a['re'];
+ z['im'] = a['im'];
+ } else if ('abs' in a && 'arg' in a) {
+ if (!isFinite(a['abs']) && isFinite(a['arg'])) {
+ return Complex['INFINITY'];
+ }
+ z['re'] = a['abs'] * Math.cos(a['arg']);
+ z['im'] = a['abs'] * Math.sin(a['arg']);
+ } else if ('r' in a && 'phi' in a) {
+ if (!isFinite(a['r']) && isFinite(a['phi'])) {
+ return Complex['INFINITY'];
+ }
+ z['re'] = a['r'] * Math.cos(a['phi']);
+ z['im'] = a['r'] * Math.sin(a['phi']);
+ } else if (a.length === 2) { // Quick array check
+ z['re'] = a[0];
+ z['im'] = a[1];
+ } else {
+ parser_exit();
+ }
+ break;
+
+ case 'string':
+
+ z['im'] = /* void */
+ z['re'] = 0;
+
+ const tokens = a.replace(/_/g, '')
+ .match(/\d+\.?\d*e[+-]?\d+|\d+\.?\d*|\.\d+|./g);
+ let plus = 1;
+ let minus = 0;
+
+ if (tokens === null) {
+ parser_exit();
+ }
+
+ for (let i = 0; i < tokens.length; i++) {
+
+ const c = tokens[i];
+
+ if (c === ' ' || c === '\t' || c === '\n') {
+ /* void */
+ } else if (c === '+') {
+ plus++;
+ } else if (c === '-') {
+ minus++;
+ } else if (c === 'i' || c === 'I') {
+
+ if (plus + minus === 0) {
+ parser_exit();
+ }
+
+ if (tokens[i + 1] !== ' ' && !isNaN(tokens[i + 1])) {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + tokens[i + 1]);
+ i++;
+ } else {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + '1');
+ }
+ plus = minus = 0;
+
+ } else {
+
+ if (plus + minus === 0 || isNaN(c)) {
+ parser_exit();
+ }
+
+ if (tokens[i + 1] === 'i' || tokens[i + 1] === 'I') {
+ z['im'] += parseFloat((minus % 2 ? '-' : '') + c);
+ i++;
+ } else {
+ z['re'] += parseFloat((minus % 2 ? '-' : '') + c);
+ }
+ plus = minus = 0;
+ }
+ }
+
+ // Still something on the stack
+ if (plus + minus > 0) {
+ parser_exit();
+ }
+ break;
+
+ case 'number':
+ z['im'] = 0;
+ z['re'] = a;
+ break;
+
+ default:
+ parser_exit();
+ }
+
+ if (isNaN(z['re']) || isNaN(z['im'])) {
+ // If a calculation is NaN, we treat it as NaN and don't throw
+ //parser_exit();
+ }
+
+ return z;
+};
+
+/**
+ * @constructor
+ * @returns {Complex}
+ */
+function Complex(a, b) {
+
+ if (!(this instanceof Complex)) {
+ return new Complex(a, b);
+ }
+
+ const z = parse(a, b);
+
+ this['re'] = z['re'];
+ this['im'] = z['im'];
+}
+
+Complex.prototype = {
+
+ 're': 0,
+ 'im': 0,
+
+ /**
+ * Calculates the sign of a complex number, which is a normalized complex
+ *
+ * @returns {Complex}
+ */
+ 'sign': function () {
+
+ const abs = hypot(this['re'], this['im']);
+
+ return new Complex(
+ this['re'] / abs,
+ this['im'] / abs);
+ },
+
+ /**
+ * Adds two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'add': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+
+ if (tInfin || zInfin) {
+
+ if (tInfin && zInfin) {
+ // Infinity + Infinity = NaN
+ return Complex['NAN'];
+ }
+ // Infinity + z = Infinity { where z != Infinity }
+ return Complex['INFINITY'];
+ }
+
+ return new Complex(
+ this['re'] + z['re'],
+ this['im'] + z['im']);
+ },
+
+ /**
+ * Subtracts two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'sub': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+
+ if (tInfin || zInfin) {
+
+ if (tInfin && zInfin) {
+ // Infinity - Infinity = NaN
+ return Complex['NAN'];
+ }
+ // Infinity - z = Infinity { where z != Infinity }
+ return Complex['INFINITY'];
+ }
+
+ return new Complex(
+ this['re'] - z['re'],
+ this['im'] - z['im']);
+ },
+
+ /**
+ * Multiplies two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'mul': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ // Infinity * 0 = NaN
+ if (tInfin && zIsZero || zInfin && tIsZero) {
+ return Complex['NAN'];
+ }
+
+ // Infinity * z = Infinity { where z != 0 }
+ if (tInfin || zInfin) {
+ return Complex['INFINITY'];
+ }
+
+ // Shortcut for real values
+ if (z['im'] === 0 && this['im'] === 0) {
+ return new Complex(this['re'] * z['re'], 0);
+ }
+
+ return new Complex(
+ this['re'] * z['re'] - this['im'] * z['im'],
+ this['re'] * z['im'] + this['im'] * z['re']);
+ },
+
+ /**
+ * Divides two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'div': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tInfin = this['isInfinite']();
+ const zInfin = !(isFinite(z['re']) && isFinite(z['im']));
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ // 0 / 0 = NaN and Infinity / Infinity = NaN
+ if (tIsZero && zIsZero || tInfin && zInfin) {
+ return Complex['NAN'];
+ }
+
+ // Infinity / 0 = Infinity
+ if (zIsZero || tInfin) {
+ return Complex['INFINITY'];
+ }
+
+ // 0 / Infinity = 0
+ if (tIsZero || zInfin) {
+ return Complex['ZERO'];
+ }
+
+ if (0 === z['im']) {
+ // Divisor is real
+ return new Complex(this['re'] / z['re'], this['im'] / z['re']);
+ }
+
+ if (Math.abs(z['re']) < Math.abs(z['im'])) {
+
+ const x = z['re'] / z['im'];
+ const t = z['re'] * x + z['im'];
+
+ return new Complex(
+ (this['re'] * x + this['im']) / t,
+ (this['im'] * x - this['re']) / t);
+
+ } else {
+
+ const x = z['im'] / z['re'];
+ const t = z['im'] * x + z['re'];
+
+ return new Complex(
+ (this['re'] + this['im'] * x) / t,
+ (this['im'] - this['re'] * x) / t);
+ }
+ },
+
+ /**
+ * Calculate the power of two complex numbers
+ *
+ * @returns {Complex}
+ */
+ 'pow': function (a, b) {
+
+ const z = parse(a, b);
+
+ const tIsZero = this['re'] === 0 && this['im'] === 0;
+ const zIsZero = z['re'] === 0 && z['im'] === 0;
+
+ if (zIsZero) {
+ return Complex['ONE'];
+ }
+
+ // If the exponent is real
+ if (z['im'] === 0) {
+
+ if (this['im'] === 0 && this['re'] > 0) {
+
+ return new Complex(Math.pow(this['re'], z['re']), 0);
+
+ } else if (this['re'] === 0) { // If base is fully imaginary
+
+ switch ((z['re'] % 4 + 4) % 4) {
+ case 0:
+ return new Complex(Math.pow(this['im'], z['re']), 0);
+ case 1:
+ return new Complex(0, Math.pow(this['im'], z['re']));
+ case 2:
+ return new Complex(-Math.pow(this['im'], z['re']), 0);
+ case 3:
+ return new Complex(0, -Math.pow(this['im'], z['re']));
+ }
+ }
+ }
+
+ /* I couldn't find a good formula, so here is a derivation and optimization
+ *
+ * z_1^z_2 = (a + bi)^(c + di)
+ * = exp((c + di) * log(a + bi)
+ * = pow(a^2 + b^2, (c + di) / 2) * exp(i(c + di)atan2(b, a))
+ * =>...
+ * Re = (pow(a^2 + b^2, c / 2) * exp(-d * atan2(b, a))) * cos(d * log(a^2 + b^2) / 2 + c * atan2(b, a))
+ * Im = (pow(a^2 + b^2, c / 2) * exp(-d * atan2(b, a))) * sin(d * log(a^2 + b^2) / 2 + c * atan2(b, a))
+ *
+ * =>...
+ * Re = exp(c * log(sqrt(a^2 + b^2)) - d * atan2(b, a)) * cos(d * log(sqrt(a^2 + b^2)) + c * atan2(b, a))
+ * Im = exp(c * log(sqrt(a^2 + b^2)) - d * atan2(b, a)) * sin(d * log(sqrt(a^2 + b^2)) + c * atan2(b, a))
+ *
+ * =>
+ * Re = exp(c * logsq2 - d * arg(z_1)) * cos(d * logsq2 + c * arg(z_1))
+ * Im = exp(c * logsq2 - d * arg(z_1)) * sin(d * logsq2 + c * arg(z_1))
+ *
+ */
+
+ if (tIsZero && z['re'] > 0) { // Same behavior as Wolframalpha, Zero if real part is zero
+ return Complex['ZERO'];
+ }
+
+ const arg = Math.atan2(this['im'], this['re']);
+ const loh = logHypot(this['re'], this['im']);
+
+ let re = Math.exp(z['re'] * loh - z['im'] * arg);
+ let im = z['im'] * loh + z['re'] * arg;
+ return new Complex(
+ re * Math.cos(im),
+ re * Math.sin(im));
+ },
+
+ /**
+ * Calculate the complex square root
+ *
+ * @returns {Complex}
+ */
+ 'sqrt': function () {
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+ // Real number case
+ if (a >= 0) {
+ return new Complex(Math.sqrt(a), 0);
+ } else {
+ return new Complex(0, Math.sqrt(-a));
+ }
+ }
+
+ const r = hypot(a, b);
+
+ let re = Math.sqrt(0.5 * (r + Math.abs(a))); // sqrt(2x) / 2 = sqrt(x / 2)
+ let im = Math.abs(b) / (2 * re);
+
+ if (a >= 0) {
+ return new Complex(re, b < 0 ? -im : im);
+ } else {
+ return new Complex(im, b < 0 ? -re : re);
+ }
+ },
+
+ /**
+ * Calculate the complex exponent
+ *
+ * @returns {Complex}
+ */
+ 'exp': function () {
+
+ const er = Math.exp(this['re']);
+
+ if (this['im'] === 0) {
+ return new Complex(er, 0);
+ }
+ return new Complex(
+ er * Math.cos(this['im']),
+ er * Math.sin(this['im']));
+ },
+
+ /**
+ * Calculate the complex exponent and subtracts one.
+ *
+ * This may be more accurate than `Complex(x).exp().sub(1)` if
+ * `x` is small.
+ *
+ * @returns {Complex}
+ */
+ 'expm1': function () {
+
+ /**
+ * exp(a + i*b) - 1
+ = exp(a) * (cos(b) + j*sin(b)) - 1
+ = expm1(a)*cos(b) + cosm1(b) + j*exp(a)*sin(b)
+ */
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.expm1(a) * Math.cos(b) + cosm1(b),
+ Math.exp(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the natural log
+ *
+ * @returns {Complex}
+ */
+ 'log': function () {
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0 && a > 0) {
+ return new Complex(Math.log(a), 0);
+ }
+
+ return new Complex(
+ logHypot(a, b),
+ Math.atan2(b, a));
+ },
+
+ /**
+ * Calculate the magnitude of the complex number
+ *
+ * @returns {number}
+ */
+ 'abs': function () {
+
+ return hypot(this['re'], this['im']);
+ },
+
+ /**
+ * Calculate the angle of the complex number
+ *
+ * @returns {number}
+ */
+ 'arg': function () {
+
+ return Math.atan2(this['im'], this['re']);
+ },
+
+ /**
+ * Calculate the sine of the complex number
+ *
+ * @returns {Complex}
+ */
+ 'sin': function () {
+
+ // sin(z) = ( e^iz - e^-iz ) / 2i
+ // = sin(a)cosh(b) + i cos(a)sinh(b)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.sin(a) * cosh(b),
+ Math.cos(a) * sinh(b));
+ },
+
+ /**
+ * Calculate the cosine
+ *
+ * @returns {Complex}
+ */
+ 'cos': function () {
+
+ // cos(z) = ( e^iz + e^-iz ) / 2
+ // = cos(a)cosh(b) - i sin(a)sinh(b)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ Math.cos(a) * cosh(b),
+ -Math.sin(a) * sinh(b));
+ },
+
+ /**
+ * Calculate the tangent
+ *
+ * @returns {Complex}
+ */
+ 'tan': function () {
+
+ // tan(z) = sin(z) / cos(z)
+ // = ( e^iz - e^-iz ) / ( i( e^iz + e^-iz ) )
+ // = ( e^2iz - 1 ) / i( e^2iz + 1 )
+ // = ( sin(2a) + i sinh(2b) ) / ( cos(2a) + cosh(2b) )
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = Math.cos(a) + cosh(b);
+
+ return new Complex(
+ Math.sin(a) / d,
+ sinh(b) / d);
+ },
+
+ /**
+ * Calculate the cotangent
+ *
+ * @returns {Complex}
+ */
+ 'cot': function () {
+
+ // cot(c) = i(e^(ci) + e^(-ci)) / (e^(ci) - e^(-ci))
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = Math.cos(a) - cosh(b);
+
+ return new Complex(
+ -Math.sin(a) / d,
+ sinh(b) / d);
+ },
+
+ /**
+ * Calculate the secant
+ *
+ * @returns {Complex}
+ */
+ 'sec': function () {
+
+ // sec(c) = 2 / (e^(ci) + e^(-ci))
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = 0.5 * cosh(2 * b) + 0.5 * Math.cos(2 * a);
+
+ return new Complex(
+ Math.cos(a) * cosh(b) / d,
+ Math.sin(a) * sinh(b) / d);
+ },
+
+ /**
+ * Calculate the cosecans
+ *
+ * @returns {Complex}
+ */
+ 'csc': function () {
+
+ // csc(c) = 2i / (e^(ci) - e^(-ci))
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = 0.5 * cosh(2 * b) - 0.5 * Math.cos(2 * a);
+
+ return new Complex(
+ Math.sin(a) * cosh(b) / d,
+ -Math.cos(a) * sinh(b) / d);
+ },
+
+ /**
+ * Calculate the complex arcus sinus
+ *
+ * @returns {Complex}
+ */
+ 'asin': function () {
+
+ // asin(c) = -i * log(ci + sqrt(1 - c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const t1 = new Complex(
+ b * b - a * a + 1,
+ -2 * a * b)['sqrt']();
+
+ const t2 = new Complex(
+ t1['re'] - b,
+ t1['im'] + a)['log']();
+
+ return new Complex(t2['im'], -t2['re']);
+ },
+
+ /**
+ * Calculate the complex arcus cosinus
+ *
+ * @returns {Complex}
+ */
+ 'acos': function () {
+
+ // acos(c) = i * log(c - i * sqrt(1 - c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const t1 = new Complex(
+ b * b - a * a + 1,
+ -2 * a * b)['sqrt']();
+
+ const t2 = new Complex(
+ t1['re'] - b,
+ t1['im'] + a)['log']();
+
+ return new Complex(Math.PI / 2 - t2['im'], t2['re']);
+ },
+
+ /**
+ * Calculate the complex arcus tangent
+ *
+ * @returns {Complex}
+ */
+ 'atan': function () {
+
+ // atan(c) = i / 2 log((i + x) / (i - x))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0) {
+
+ if (b === 1) {
+ return new Complex(0, Infinity);
+ }
+
+ if (b === -1) {
+ return new Complex(0, -Infinity);
+ }
+ }
+
+ const d = a * a + (1.0 - b) * (1.0 - b);
+
+ const t1 = new Complex(
+ (1 - b * b - a * a) / d,
+ -2 * a / d).log();
+
+ return new Complex(-0.5 * t1['im'], 0.5 * t1['re']);
+ },
+
+ /**
+ * Calculate the complex arcus cotangent
+ *
+ * @returns {Complex}
+ */
+ 'acot': function () {
+
+ // acot(c) = i / 2 log((c - i) / (c + i))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+ return new Complex(Math.atan2(1, a), 0);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).atan()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).atan();
+ },
+
+ /**
+ * Calculate the complex arcus secant
+ *
+ * @returns {Complex}
+ */
+ 'asec': function () {
+
+ // asec(c) = -i * log(1 / c + sqrt(1 - i / c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0 && b === 0) {
+ return new Complex(0, Infinity);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).acos()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).acos();
+ },
+
+ /**
+ * Calculate the complex arcus cosecans
+ *
+ * @returns {Complex}
+ */
+ 'acsc': function () {
+
+ // acsc(c) = -i * log(i / c + sqrt(1 - 1 / c^2))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (a === 0 && b === 0) {
+ return new Complex(Math.PI / 2, Infinity);
+ }
+
+ const d = a * a + b * b;
+ return (d !== 0)
+ ? new Complex(
+ a / d,
+ -b / d).asin()
+ : new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0).asin();
+ },
+
+ /**
+ * Calculate the complex sinh
+ *
+ * @returns {Complex}
+ */
+ 'sinh': function () {
+
+ // sinh(c) = (e^c - e^-c) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ sinh(a) * Math.cos(b),
+ cosh(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the complex cosh
+ *
+ * @returns {Complex}
+ */
+ 'cosh': function () {
+
+ // cosh(c) = (e^c + e^-c) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ return new Complex(
+ cosh(a) * Math.cos(b),
+ sinh(a) * Math.sin(b));
+ },
+
+ /**
+ * Calculate the complex tanh
+ *
+ * @returns {Complex}
+ */
+ 'tanh': function () {
+
+ // tanh(c) = (e^c - e^-c) / (e^c + e^-c)
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = cosh(a) + Math.cos(b);
+
+ return new Complex(
+ sinh(a) / d,
+ Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex coth
+ *
+ * @returns {Complex}
+ */
+ 'coth': function () {
+
+ // coth(c) = (e^c + e^-c) / (e^c - e^-c)
+
+ const a = 2 * this['re'];
+ const b = 2 * this['im'];
+ const d = cosh(a) - Math.cos(b);
+
+ return new Complex(
+ sinh(a) / d,
+ -Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex csch
+ *
+ * @returns {Complex}
+ */
+ 'csch': function () {
+
+ // csch(c) = 2 / (e^c - e^-c)
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = Math.cos(2 * b) - cosh(2 * a);
+
+ return new Complex(
+ -2 * sinh(a) * Math.cos(b) / d,
+ 2 * cosh(a) * Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex sech
+ *
+ * @returns {Complex}
+ */
+ 'sech': function () {
+
+ // sech(c) = 2 / (e^c + e^-c)
+
+ const a = this['re'];
+ const b = this['im'];
+ const d = Math.cos(2 * b) + cosh(2 * a);
+
+ return new Complex(
+ 2 * cosh(a) * Math.cos(b) / d,
+ -2 * sinh(a) * Math.sin(b) / d);
+ },
+
+ /**
+ * Calculate the complex asinh
+ *
+ * @returns {Complex}
+ */
+ 'asinh': function () {
+
+ // asinh(z) = log(z + sqrt(z^2 + 1))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ if (a === 0) {
+ return new Complex(0, 0);
+ }
+
+ // Use |a| to keep asinh(-a) = -asinh(a) and avoid cancellation for large -a
+ const x = Math.abs(a);
+ const r = Math.log(x + Math.sqrt(x * x + 1));
+
+ return new Complex(a < 0 ? -r : r, 0);
+ }
+
+ // z^2 + 1 = (a^2 - b^2 + 1) + i (2ab)
+ const re2 = a * a - b * b + 1;
+ const im2 = 2 * a * b;
+
+ const t = new Complex(re2, im2)['sqrt'](); // sqrt(z^2 + 1)
+
+ return new Complex(a + t['re'], b + t['im'])['log']();
+ },
+
+ /**
+ * Calculate the complex acosh
+ *
+ * @returns {Complex}
+ */
+ 'acosh': function () {
+
+ // acosh(z)= log(z + sqrt(z^2 - 1)) = log(z + sqrt(z - 1) * sqrt(z + 1))
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ // z = a is real
+ if (a > 1) {
+ // acosh(a) = log(a + sqrt(a - 1) * sqrt(a + 1)), a > 1
+ return new Complex(
+ Math.log(a + Math.sqrt(a - 1) * Math.sqrt(a + 1)), 0);
+ }
+
+ if (a < -1) {
+ // acosh(a) = log(-a + sqrt(a^2 - 1)) + i*pi, a < -1
+ const t = Math.sqrt(a * a - 1);
+ return new Complex(Math.log(-a + t), Math.PI);
+ }
+
+ // -1 <= a <= 1 : purely imaginary
+ // acosh(a) = i * acos(a)
+ return new Complex(0, Math.acos(a));
+ }
+
+ const t1 = new Complex(a - 1, b)['sqrt'](); // sqrt(z - 1)
+ const t2 = new Complex(a + 1, b)['sqrt'](); // sqrt(z + 1)
+
+ return new Complex(
+ a + t1['re'] * t2['re'] - t1['im'] * t2['im'],
+ b + t1['re'] * t2['im'] + t1['im'] * t2['re']
+ )['log']();
+ },
+
+ /**
+ * Calculate the complex atanh
+ *
+ * @returns {Complex}
+ */
+ 'atanh': function () {
+
+ // atanh(z) = log((1 + z) / (1 - z)) / 2
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ if (a === 0) {
+ return new Complex(0, 0);
+ }
+
+ if (a === 1) {
+ // limit x -> 1^- atanh(x) = +Infinity
+ return new Complex(Infinity, 0);
+ }
+
+ if (a === -1) {
+ // limit x -> -1^+ atanh(x) = -Infinity
+ return new Complex(-Infinity, 0);
+ }
+
+ if (-1 < a && a < 1) {
+ // Purely real
+ return new Complex(
+ 0.5 * Math.log((1 + a) / (1 - a)),
+ 0
+ );
+ }
+
+ if (a > 1) {
+ // Our branch: Im(atanh(a)) = -π/2 for a > 1
+ const t = (a + 1) / (a - 1); // > 0
+ return new Complex(
+ 0.5 * Math.log(t),
+ -Math.PI / 2
+ );
+ }
+
+ // a < -1: Im(atanh(a)) = +π/2
+ const t = (1 + a) / (1 - a); // < 0
+ return new Complex(
+ 0.5 * Math.log(-t), // log((1 - a)/(1 + a))
+ Math.PI / 2
+ );
+ }
+
+ // Use atanh(z) = 0.5 * Log((1+z)/(1-z)) with principal Log.
+ const oneMinus = 1 - a;
+ const onePlus = 1 + a;
+ const d = oneMinus * oneMinus + b * b; // |1 - z|^2
+
+ if (d === 0) {
+ // (1 - z) == 0 for finite z only at z = 1+0i, already handled above.
+ // If we ever get here, just propagate infinities consistently:
+ return new Complex(
+ (a !== -1) ? (a / 0) : 0,
+ (b !== 0) ? (b / 0) : 0
+ );
+ }
+
+ // (1 + z) / (1 - z) with a single complex division
+ const xr = (onePlus * oneMinus - b * b) / d;
+ const xi = (b * oneMinus + onePlus * b) / d;
+
+ // 0.5 * log(xr + i xi)
+ return new Complex(
+ logHypot(xr, xi) / 2,
+ Math.atan2(xi, xr) / 2
+ );
+ },
+
+ /**
+ * Calculate the complex acoth
+ *
+ * @returns {Complex}
+ */
+ 'acoth': function () {
+
+ // acoth(z) = log((z + 1) / (z - 1)) / 2 = atanh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ // z = 0 -> acoth(0) = i * π / 2 (log((1+0)/(1-0))/2 = log(-1)/2)
+ if (a === 0 && b === 0) {
+ return new Complex(0, Math.PI / 2);
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1 / z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['atanh']();
+ }
+
+ // Fallback for weird infinities/NaNs: mirror other functions' style
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0
+ )['atanh']();
+ },
+
+ /**
+ * Calculate the complex acsch
+ *
+ * @returns {Complex}
+ */
+ 'acsch': function () {
+
+ // acsch(c) = log((1+sqrt(1+c^2))/c) = = asinh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (b === 0) {
+
+ // z = a real
+ if (a === 0) {
+ // acsch(0) -> +/- Infinity, we keep your previous behavior
+ return new Complex(Infinity, 0);
+ }
+
+ // acsch(a) = asinh(1/a) = log(1/a + sqrt(1/a^2 + 1))
+ const inv = 1 / a;
+ return new Complex(
+ Math.log(inv + Math.sqrt(inv * inv + 1)),
+ 0
+ );
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1/z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['asinh']();
+ }
+
+ // Handle 0 + 0i or infinities in the same spirit as your existing code
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0)['asinh']();
+ },
+
+ /**
+ * Calculate the complex asech
+ *
+ * @returns {Complex}
+ */
+ 'asech': function () {
+
+ // asech(z) = acosh(1 / z)
+
+ const a = this['re'];
+ const b = this['im'];
+
+ if (this['isZero']()) {
+ // asech(0) = acosh(∞) -> ∞
+ return Complex['INFINITY'];
+ }
+
+ const d = a * a + b * b;
+
+ if (d !== 0) {
+ // 1 / z = (a - i b) / (a^2 + b^2)
+ return new Complex(a / d, -b / d)['acosh']();
+ }
+
+ // Fallback for weird infinities/NaNs
+ return new Complex(
+ (a !== 0) ? a / 0 : 0,
+ (b !== 0) ? -b / 0 : 0)['acosh']();
+ },
+
+ /**
+ * Calculate the complex inverse 1/z
+ *
+ * @returns {Complex}
+ */
+ 'inverse': function () {
+
+ // 1 / 0 = Infinity and 1 / Infinity = 0
+ if (this['isZero']()) {
+ return Complex['INFINITY'];
+ }
+
+ if (this['isInfinite']()) {
+ return Complex['ZERO'];
+ }
+
+ const a = this['re'];
+ const b = this['im'];
+
+ const d = a * a + b * b;
+
+ return new Complex(a / d, -b / d);
+ },
+
+ /**
+ * Returns the complex conjugate
+ *
+ * @returns {Complex}
+ */
+ 'conjugate': function () {
+
+ return new Complex(this['re'], -this['im']);
+ },
+
+ /**
+ * Gets the negated complex number
+ *
+ * @returns {Complex}
+ */
+ 'neg': function () {
+
+ return new Complex(-this['re'], -this['im']);
+ },
+
+ /**
+ * Ceils the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'ceil': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.ceil(this['re'] * places) / places,
+ Math.ceil(this['im'] * places) / places);
+ },
+
+ /**
+ * Floors the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'floor': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.floor(this['re'] * places) / places,
+ Math.floor(this['im'] * places) / places);
+ },
+
+ /**
+ * Ceils the actual complex number
+ *
+ * @returns {Complex}
+ */
+ 'round': function (places) {
+
+ places = Math.pow(10, places || 0);
+
+ return new Complex(
+ Math.round(this['re'] * places) / places,
+ Math.round(this['im'] * places) / places);
+ },
+
+ /**
+ * Compares two complex numbers
+ *
+ * **Note:** new Complex(Infinity).equals(Infinity) === false
+ *
+ * @returns {boolean}
+ */
+ 'equals': function (a, b) {
+
+ const z = parse(a, b);
+
+ return Math.abs(z['re'] - this['re']) <= Complex['EPSILON'] &&
+ Math.abs(z['im'] - this['im']) <= Complex['EPSILON'];
+ },
+
+ /**
+ * Clones the actual object
+ *
+ * @returns {Complex}
+ */
+ 'clone': function () {
+
+ return new Complex(this['re'], this['im']);
+ },
+
+ /**
+ * Gets a string of the actual complex number
+ *
+ * @returns {string}
+ */
+ 'toString': function () {
+
+ let a = this['re'];
+ let b = this['im'];
+ let ret = "";
+
+ if (this['isNaN']()) {
+ return 'NaN';
+ }
+
+ if (this['isInfinite']()) {
+ return 'Infinity';
+ }
+
+ if (Math.abs(a) < Complex['EPSILON']) {
+ a = 0;
+ }
+
+ if (Math.abs(b) < Complex['EPSILON']) {
+ b = 0;
+ }
+
+ // If is real number
+ if (b === 0) {
+ return ret + a;
+ }
+
+ if (a !== 0) {
+ ret += a;
+ ret += " ";
+ if (b < 0) {
+ b = -b;
+ ret += "-";
+ } else {
+ ret += "+";
+ }
+ ret += " ";
+ } else if (b < 0) {
+ b = -b;
+ ret += "-";
+ }
+
+ if (1 !== b) { // b is the absolute imaginary part
+ ret += b;
+ }
+ return ret + "i";
+ },
+
+ /**
+ * Returns the actual number as a vector
+ *
+ * @returns {Array}
+ */
+ 'toVector': function () {
+
+ return [this['re'], this['im']];
+ },
+
+ /**
+ * Returns the actual real value of the current object
+ *
+ * @returns {number|null}
+ */
+ 'valueOf': function () {
+
+ if (this['im'] === 0) {
+ return this['re'];
+ }
+ return null;
+ },
+
+ /**
+ * Determines whether a complex number is not on the Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isNaN': function () {
+ return isNaN(this['re']) || isNaN(this['im']);
+ },
+
+ /**
+ * Determines whether or not a complex number is at the zero pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isZero': function () {
+ return this['im'] === 0 && this['re'] === 0;
+ },
+
+ /**
+ * Determines whether a complex number is not at the infinity pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isFinite': function () {
+ return isFinite(this['re']) && isFinite(this['im']);
+ },
+
+ /**
+ * Determines whether or not a complex number is at the infinity pole of the
+ * Riemann sphere.
+ *
+ * @returns {boolean}
+ */
+ 'isInfinite': function () {
+ return !this['isFinite']();
+ }
+};
+
+Complex['ZERO'] = new Complex(0, 0);
+Complex['ONE'] = new Complex(1, 0);
+Complex['I'] = new Complex(0, 1);
+Complex['PI'] = new Complex(Math.PI, 0);
+Complex['E'] = new Complex(Math.E, 0);
+Complex['INFINITY'] = new Complex(Infinity, Infinity);
+Complex['NAN'] = new Complex(NaN, NaN);
+Complex['EPSILON'] = 1e-15;
diff --git a/node_modules/complex.js/tests/complex.test.js b/node_modules/complex.js/tests/complex.test.js
new file mode 100644
index 0000000..f7ffbea
--- /dev/null
+++ b/node_modules/complex.js/tests/complex.test.js
@@ -0,0 +1,1089 @@
+
+var Complex = require("complex.js");
+var assert = require("assert");
+
+var functionTests = [{
+ set: Complex.I,
+ fn: "mul",
+ param: Complex(Math.PI).exp(),
+ expect: "23.140692632779267i"
+}, {
+ set: new Complex(1, 4),
+ fn: "mul",
+ param: 3,
+ expect: "3 + 12i"
+}, {
+ set: "4 + 3i",
+ fn: "add",
+ param: "-3 - 2i",
+ expect: "1 + i"
+}, {
+ set: "3i",
+ fn: "add",
+ param: "-2i",
+ expect: "i"
+}, {
+ set: "4",
+ fn: "add",
+ param: "-3",
+ expect: "1"
+}, {
+ set: 9,
+ fn: "sqrt",
+ expect: "3"
+}, {
+ set: -9,
+ fn: "sqrt",
+ expect: "3i"
+}, {
+ set: "-36",
+ fn: "sqrt",
+ expect: "6i"
+}, {
+ set: "36i",
+ fn: "sqrt",
+ expect: "4.242640687119285 + 4.242640687119286i"
+}, {
+ set: Infinity,
+ fn: "mul",
+ param: "i",
+ expect: "Infinity"
+}, {
+ set: "-36i",
+ fn: "sqrt",
+ expect: "4.242640687119285 - 4.242640687119286i"
+}, {
+ set: "4 + 2i",
+ fn: "div",
+ param: "0",
+ expect: "Infinity"
+}, {
+ set: "0",
+ fn: "div",
+ param: Infinity,
+ expect: "0"
+}, {
+ set: -Infinity,
+ fn: "div",
+ param: 0,
+ expect: "Infinity"
+}, {
+ set: Infinity,
+ fn: "div",
+ param: Infinity,
+ expect: "NaN"
+}, {
+ set: 0,
+ fn: "div",
+ param: 0,
+ expect: "NaN"
+}, {
+ set: "4 + 2i",
+ fn: "div",
+ param: "1 + i",
+ expect: "3 - i"
+}, {
+ set: "25",
+ fn: "div",
+ param: "3 - 4i",
+ expect: "3 + 4i"
+}, {
+ set: "3 - 2i",
+ fn: "div",
+ param: "i",
+ expect: "-2 - 3i"
+}, {
+ set: "4i",
+ fn: "mul",
+ param: "-5i",
+ expect: "20"
+}, {
+ set: "3 - 6i",
+ fn: "mul",
+ param: "i",
+ expect: "6 + 3i"
+}, {
+ set: Infinity,
+ fn: "mul",
+ param: 0,
+ expect: "NaN"
+}, {
+ set: "3 + 4i",
+ fn: "add",
+ param: "5 - 7i",
+ expect: "8 - 3i"
+}, {
+ set: Infinity,
+ fn: "add",
+ param: Infinity,
+ expect: "NaN"
+}, {
+ set: -Infinity,
+ fn: "sub",
+ param: -Infinity,
+ expect: "NaN"
+}, {
+ set: "6i",
+ fn: "div",
+ param: "3 - 12i",
+ expect: "-0.47058823529411764 + 0.11764705882352941i"
+}, {
+ set: "36 + 36i",
+ fn: "sqrt",
+ expect: "6.59210468080686 + 2.730539163373364i"
+}, {
+ set: "36 - 36i",
+ fn: "sqrt",
+ expect: "6.59210468080686 - 2.730539163373364i"
+}, {
+ set: "-36 + 36i",
+ fn: "sqrt",
+ expect: "2.730539163373364 + 6.59210468080686i"
+}, {
+ set: "-36 - 36i",
+ fn: "sqrt",
+ expect: "2.730539163373364 - 6.59210468080686i"
+}, {
+ set: "0",
+ fn: "sqrt",
+ expect: "0"
+}, {
+ set: Math.E,
+ fn: "log",
+ expect: "1"
+}, {
+ set: 0,
+ fn: "log",
+ expect: "Infinity"
+}, {
+ set: Infinity,
+ fn: "mul",
+ param: 3,
+ expect: "Infinity"
+}, {
+ set: "-1",
+ fn: "log",
+ expect: Math.PI + "i"
+}, {
+ set: "i",
+ fn: "log",
+ expect: (Math.PI / 2) + "i"
+}, {
+ set: "3 + 2i",
+ fn: "log",
+ expect: Math.log(13) / 2 + " + " + Math.atan2(2, 3) + "i"
+}, {
+ set: "3 - 2i",
+ fn: "log",
+ expect: Math.log(13) / 2 + " - " + Math.atan2(2, 3) + "i"
+}, {
+ set: 1,
+ fn: "exp",
+ expect: "" + Math.E
+}, {
+ set: "i",
+ fn: "exp",
+ expect: Math.cos(1) + " + " + Math.sin(1) + "i"
+}, {
+ set: "i",
+ fn: "mul",
+ param: "i",
+ expect: "-1"
+}, {
+ set: "3 + 2i",
+ fn: "exp",
+ expect: "-8.358532650935372 + 18.263727040666765i"
+}, {
+ set: "3 - 2i",
+ fn: "exp",
+ expect: "-8.358532650935372 - 18.263727040666765i"
+}, {
+ set: "3 - 2i",
+ fn: "expm1",
+ expect: "-9.358532650935372 - 18.263727040666765i"
+}, {
+ set: "0",
+ fn: "expm1",
+ expect: "0"
+}, {
+ set: "1e-6",
+ fn: "expm1",
+ expect: "0.0000010000005000001665"
+}, {
+ set: "1e-5 + 5i",
+ fn: "expm1",
+ expect: "-0.7163349779007357 - 0.9589338639538314i"
+}, {
+ set: "1.2e-7 - 2e-6i",
+ fn: "expm1",
+ expect: "1.1999800719976027e-7 - 0.000002000000239998681i"
+}, {
+ set: "3",
+ fn: "pow",
+ param: "3",
+ expect: "27"
+}, {
+ set: -2,
+ fn: "pow",
+ param: 1.5,
+ expect: "-2.82842712474619i"
+}, {
+ set: -8,
+ fn: "pow",
+ param: 1 / 3,
+ expect: "1 + 1.732050807568877i"
+}, {
+ set: -25,
+ fn: "sqrt",
+ expect: "5i"
+}, {
+ set: -25,
+ fn: "pow",
+ param: 0.5,
+ expect: "5i"
+}, {
+ set: "0",
+ fn: "pow",
+ param: "1+i",
+ expect: "0"
+}, {
+ set: "i",
+ fn: "pow",
+ param: "0",
+ expect: "1"
+}, {
+ set: "87",
+ fn: "pow",
+ param: "3",
+ expect: "658503"
+}, {
+ set: "i",
+ fn: "pow",
+ param: "1",
+ expect: "i"
+}, {
+ set: "i",
+ fn: "pow",
+ param: "2",
+ expect: "-1"
+}, {
+ set: "i",
+ fn: "pow",
+ param: "3",
+ expect: "-i"
+}, {
+ set: "i",
+ fn: "pow",
+ param: "4",
+ expect: "1"
+}, {
+ set: "i",
+ fn: "pow",
+ param: "5",
+ expect: "i"
+}, {
+ set: 7,
+ fn: "pow",
+ param: 2,
+ expect: '49'
+}, {
+ set: 0,
+ fn: "pow",
+ param: 2,
+ expect: '0'
+}, {
+ set: "3i",
+ fn: "pow",
+ param: "3i",
+ expect: "-0.008876640735623678 - 0.0013801328997494863i"
+}, {
+ set: { re: 3, im: 4 },
+ fn: "abs",
+ expect: "5"
+}, {
+ set: { re: 10, im: 24 },
+ fn: "abs",
+ expect: "26"
+}, {
+ set: "+++++--+1 + 4i",
+ fn: "mul",
+ param: "3 + 2i",
+ expect: "-5 + 14i"
+}, {
+ set: "4 + 16i",
+ fn: "div",
+ param: "4.0000",
+ expect: "1 + 4i"
+}, {
+ set: { re: -7.1, im: 2.5 },
+ fn: "neg",
+ expect: "7.1 - 2.5i"
+}, {
+ set: { re: 1, im: 1 },
+ fn: "div",
+ param: { re: 3, im: 4 },
+ expect: 7 / 25 + " - " + 1 / 25 + "i"
+}, {
+ set: new Complex(-7.1, 2.5),
+ fn: "neg",
+ expect: "7.1 - 2.5i"
+}, {
+ set: { re: 1, im: 1 },
+ fn: "arg",
+ expect: "" + Math.PI / 4
+}, {
+ set: { re: -1, im: -1 },
+ fn: "arg",
+ expect: "" + -3 * Math.PI / 4
+}, {
+ set: { re: 0, im: 1 },
+ fn: "arg",
+ expect: "" + Math.PI / 2
+}, {
+ set: { re: 1, im: 0.5 * Math.sqrt(4 / 3) },
+ fn: "arg",
+ expect: "" + Math.PI / 6
+}, {
+ set: "3 + 4i",
+ fn: "conjugate",
+ expect: "3 - 4i"
+}, {
+ set: { re: 99, im: 50 },
+ fn: "conjugate",
+ expect: "99 - 50i"
+}, {
+ set: { re: 0, im: 0 },
+ fn: "conjugate",
+ expect: "0"
+}, {
+ set: { re: 1, im: 23 },
+ fn: "conjugate",
+ expect: "1 - 23i"
+}, {
+ set: "2 + 8i",
+ fn: "div",
+ param: new Complex(1, 2),
+ expect: "3.6 + 0.8i"
+}, {
+ set: "2 + 8i",
+ fn: "div",
+ param: "2 + 8i",
+ expect: "1"
+}, {
+ set: -Infinity,
+ fn: "div",
+ param: 3,
+ expect: "Infinity"
+}, {
+ set: "3+4i",
+ fn: "add",
+ param: "5 - i",
+ expect: "8 + 3i"
+}, {
+ set: { re: 1, im: 2 },
+ fn: "add",
+ param: "4 + 6i",
+ expect: "5 + 8i"
+}, {
+ set: { re: 5, im: 8 },
+ fn: "sub",
+ param: "4 + 6i",
+ expect: "1 + 2i"
+}, {
+ set: "3 + 4i",
+ fn: "sub",
+ param: "2 - 5i",
+ expect: "1 + 9i"
+}, {
+ set: "1 + 2i",
+ fn: "pow",
+ param: "2",
+ expect: "-2.999999999999999 + 4.000000000000001i"
+}, {
+ set: "1 + 2i",
+ fn: "pow",
+ param: "1 + 2i",
+ expect: "-0.22251715680177267 + 0.10070913113607541i"
+}, {
+ set: { re: 1, im: 2 },
+ fn: "pow",
+ param: new Complex(3, 4),
+ expect: "0.1290095940744669 + 0.03392409290517001i"
+}, {
+ fn: "abs",
+ set: new Complex(3, 4),
+ expect: "5"
+}, {
+ param: 2,
+ fn: "pow",
+ set: new Complex(1, 2),
+ expect: "-2.999999999999999 + 4.000000000000001i"
+}, {
+ set: "i",
+ fn: "pow",
+ param: 7,
+ expect: "-i"
+}, {
+ set: "2+3i",
+ fn: "mul",
+ param: "4+5i",
+ expect: "-7 + 22i"
+}, {
+ set: "3 + 4i",
+ fn: "mul",
+ param: "2 - 5i",
+ expect: "26 - 7i"
+}, {
+ set: "i",
+ fn: "pow",
+ param: 4,
+ expect: "1"
+}, {
+ set: "i",
+ fn: "pow",
+ param: 5,
+ expect: "i"
+}, {
+ set: "0-0i",
+ fn: "pow",
+ param: 2,
+ expect: "0"
+}, {
+ set: "0-0i",
+ fn: "pow",
+ param: 0,
+ expect: "1"
+}, {
+ set: "1 + 4i",
+ fn: "sqrt",
+ expect: "1.600485180440241 + 1.2496210676876531i"
+}, {
+ set: { re: -3, im: 4 },
+ fn: "sqrt",
+ expect: "1 + 2i"
+}, {
+ set: { re: 3, im: -4 },
+ fn: "sqrt",
+ expect: "2 - i"
+}, {
+ set: { re: -3, im: -4 },
+ fn: "sqrt",
+ expect: "1 - 2i"
+}, {
+ set: [-2, 0],
+ fn: "pow",
+ param: 2,
+ expect: "4"
+}, {
+ set: { abs: 1, arg: 0 },
+ fn: "equals",
+ param: { re: 1, im: 0 },
+ expect: "true"
+}, {
+ set: -Complex.E.pow(2),
+ fn: "log",
+ expect: "2 + 3.141592653589793i"
+}, {
+ set: "4 + 3i",
+ fn: "log",
+ expect: "1.6094379124341003 + 0.6435011087932844i"
+}, {
+ set: "4 + 3i",
+ fn: "exp",
+ expect: "-54.051758861078156 + 7.704891372731154i"
+}, {
+ set: "1-2i",
+ fn: "sqrt",
+ expect: "1.272019649514069 - 0.7861513777574233i"
+}, {
+ set: { re: 1, im: 2 },
+ fn: "sin",
+ expect: "3.165778513216168 + 1.9596010414216063i"
+}, {
+ set: "i",
+ fn: "cos",
+ expect: "1.5430806348152437"
+}, {
+ set: "i",
+ fn: "acos",
+ expect: "1.5707963267948966 - 0.8813735870195428i"
+}, {
+ set: { re: 1, im: 2 },
+ fn: "cos",
+ expect: "2.0327230070196656 - 3.0518977991518i"
+}, {
+ set: { re: 1, im: 2 },
+ fn: "tan",
+ expect: "0.03381282607989669 + 1.0147936161466335i"
+}, {
+ set: { re: 1, im: 3 },
+ fn: "sinh",
+ expect: "-1.1634403637032504 + 0.21775955162215221i"
+}, {
+ set: { re: 1, im: 3 },
+ fn: "cosh",
+ expect: "-1.5276382501165433 + 0.1658444019189788i"
+}, {
+ set: { re: 1, im: 3 },
+ fn: "tanh",
+ expect: "0.7680176472869112 - 0.059168539566050726i"
+}, {
+ set: { re: 1, im: 3 },
+ fn: "inverse",
+ expect: "0.1 - 0.3i"
+}, {
+ set: "3+4i",
+ fn: "inverse",
+ expect: "0.12 - 0.16i" // 3/25 - (4/25)i
+}, {
+ set: { re: 0.5, im: -0.5 },
+ fn: "inverse",
+ expect: "1 + i"
+}, {
+ set: "1 + i",
+ fn: "inverse",
+ expect: "0.5 - 0.5i"
+}, {
+ set: "0",
+ fn: "inverse",
+ expect: "Infinity"
+}, {
+ set: Infinity,
+ fn: "inverse",
+ expect: "0"
+}, {
+ set: Complex['EPSILON'],
+ fn: "equals",
+ param: 1e-16,
+ expect: "true"
+}, {
+ set: 0,
+ fn: "equals",
+ param: "5i",
+ expect: "false"
+}, {
+ set: 5,
+ fn: "equals",
+ param: "5i",
+ expect: "false"
+}, {
+ set: 5,
+ fn: "equals",
+ param: 5,
+ expect: "true"
+}, {
+ set: "10i",
+ fn: "equals",
+ param: "10i",
+ expect: "true"
+}, {
+ set: "2 + 3i",
+ fn: "equals",
+ param: "2 + 3i",
+ expect: "true"
+}, {
+ set: "2 + 3i",
+ fn: "equals",
+ param: "5i",
+ expect: "false"
+}, {
+ set: "2 + 3i",
+ fn: "round",
+ param: "0",
+ expect: "2 + 3i"
+}, {
+ set: "2.5 + 3.5i",
+ fn: "round",
+ param: "1",
+ expect: "2.5 + 3.5i"
+}, {
+ set: "2.5 + 3.5i",
+ fn: "sign",
+ param: null,
+ expect: "0.5812381937190965 + 0.813733471206735i"
+}, {
+ set: "10 + 24i",
+ fn: "sign",
+ param: null,
+ expect: "0.38461538461538464 + 0.9230769230769231i"
+}, {
+ set: "1e3i",
+ fn: "add",
+ param: "3e-3 + 1e2i",
+ expect: "0.003 + 1100i"
+}, {
+ set: "3.14-4i",
+ fn: "coth",
+ expect: "0.9994481238383576 + 0.0037048958915019857i"
+}, {
+ set: "8i-31",
+ fn: "cot",
+ expect: "1.6636768291213935e-7 - 1.0000001515864902i"
+}, {
+ set: Complex(1, 1).sub(0, 1), // Distance
+ fn: "abs",
+ expect: "1"
+}, {
+ set: Complex(1, 1), // Rotate around center
+ fn: "mul",
+ param: { abs: 1, arg: Math.PI / 2 },
+ expect: "-0.9999999999999999 + i"
+}, {
+ set: Complex(1, 1).sub(0, 1).mul({ abs: 1, arg: Math.PI / 2 }), // Rotate around another point
+ fn: "add",
+ param: "i",
+ expect: "2i"
+}, {
+ set: Complex(0, 10000000000),
+ fn: "log",
+ param: null,
+ expect: "23.025850929940457 + 1.5707963267948966i"
+}, {
+ set: Complex(0, 1000000000000000),
+ fn: "log",
+ param: null,
+ expect: "34.538776394910684 + 1.5707963267948966i"
+}, {
+ set: Complex(0, 100000000000000000),
+ fn: "log",
+ param: null,
+ expect: "39.14394658089878 + 1.5707963267948966i"
+}, {
+ set: Complex(0, 10000000000000000000),
+ fn: "log",
+ param: null,
+ expect: "43.74911676688687 + 1.5707963267948966i"
+}, {
+ set: Complex(0, 1e+30),
+ fn: "log",
+ param: null,
+ expect: "69.07755278982137 + 1.5707963267948966i"
+}, {
+ set: Complex(1, 10000000000),
+ fn: "log",
+ param: null,
+ expect: "23.025850929940454 + 1.5707963266948965i"
+}, {
+ set: Complex(1, 1000000000000000),
+ fn: "log",
+ param: null,
+ expect: "34.538776394910684 + 1.5707963267948957i"
+}, {
+ set: Complex(1, 100000000000000000),
+ fn: "log",
+ param: null,
+ expect: "39.14394658089878 + 1.5707963267948966i"
+}, {
+ set: Complex(1, 10000000000000000000),
+ fn: "log",
+ param: null,
+ expect: "43.74911676688687 + 1.5707963267948966i"
+}, {
+ set: Complex(1, 1e+30),
+ fn: "log",
+ param: null,
+ expect: "69.07755278982137 + 1.5707963267948966i"
+}, {
+ set: Complex(-1, 10000000000),
+ fn: "log",
+ param: null,
+ expect: "23.025850929940454 + 1.5707963268948968i"
+}, {
+ set: Complex(-1, 1000000000000000),
+ fn: "log",
+ param: null,
+ expect: "34.538776394910684 + 1.5707963267948977i"
+}, {
+ set: Complex(-1, 100000000000000000),
+ fn: "log",
+ param: null,
+ expect: "39.14394658089878 + 1.5707963267948968i"
+}, {
+ set: Complex(-1, 10000000000000000000),
+ fn: "log",
+ param: null,
+ expect: "43.74911676688687 + 1.5707963267948966i"
+}, {
+ set: Complex(-1, 1e+30),
+ fn: "log",
+ param: null,
+ expect: "69.07755278982137 + 1.5707963267948966i"
+}];
+
+var constructorTests = [{
+ set: null,
+ expect: "0"
+}, {
+ set: undefined,
+ expect: "0"
+}, {
+ set: "foo",
+ error: "SyntaxError: Invalid Param"
+}, {
+ set: {},
+ error: "SyntaxError: Invalid Param"
+}, {
+ set: " + i",
+ expect: "i"
+}, {
+ set: "3+4i",
+ expect: "3 + 4i"
+}, {
+ set: "i",
+ expect: "i"
+}, {
+ set: "3",
+ expect: "3"
+}, {
+ set: [9, 8],
+ expect: "9 + 8i"
+}, {
+ set: "2.3",
+ expect: "2.3"
+}, {
+ set: "2.3",
+ expect: "2.3"
+}, {
+ set: "0",
+ expect: "0"
+}, {
+ set: "-0",
+ expect: "0"
+}, {
+ set: { re: -0, im: 0 },
+ expect: "0"
+}, {
+ set: { re: 0, im: -0 },
+ expect: "0"
+}, {
+ set: Infinity,
+ expect: "Infinity"
+}, {
+ set: -Infinity,
+ expect: "Infinity"
+}, {
+ set: { re: Infinity, im: 0 },
+ expect: "Infinity"
+}, {
+ set: { re: -Infinity, im: 0 },
+ expect: "Infinity"
+}, {
+ set: { re: 0, im: Infinity },
+ expect: "Infinity"
+}, {
+ set: { re: 0, im: -Infinity },
+ expect: "Infinity"
+}, {
+ set: " + 7 - i + 3i - + + + + 43 + 2i - i4 + - 33 + 65 - 1 ",
+ expect: "-5"
+}, {
+ set: " + 7 - i + 3i - + + + + 43 + 2i - i4 + - 33 + 65 - 1 + ",
+ error: "SyntaxError: Invalid Param"
+}, {
+ set: "-3x + 4",
+ error: "SyntaxError: Invalid Param"
+}, {
+ set: "- + 7",
+ expect: "-7"
+}, {
+ set: "4 5i",
+ error: "SyntaxError: Invalid Param"
+}, {
+ set: "-",
+ error: "SyntaxError: Invalid Param"
+}, {
+ set: "2.2e-1-3.2e-1i",
+ expect: "0.22 - 0.32i"
+}, {
+ set: "2.2.",
+ error: "SyntaxError: Invalid Param"
+}, {
+ set: { r: 0, phi: 4 },
+ expect: "0"
+}, {
+ set: { r: 1, phi: 1 },
+ expect: "0.5403023058681398 + 0.8414709848078965i"
+}, {
+ set: { r: Infinity, phi: 0 },
+ expect: "Infinity"
+}, {
+ set: { r: Infinity, phi: 2 },
+ expect: "Infinity"
+}, {
+ set: { r: Infinity, phi: Infinity },
+ expect: "NaN"
+}, {
+ set: { r: Infinity, phi: NaN },
+ expect: "NaN"
+}, {
+ set: "1000_000i",
+ expect: "1000000i"
+}
+];
+
+for (let i = 0, len = constructorTests.length; i < len; ++i) {
+ if (constructorTests[i].set != null && constructorTests[i].set.hasOwnProperty('r')) {
+ constructorTests.push({
+ set: {
+ abs: constructorTests[i].set.r,
+ arg: constructorTests[i].set.phi,
+ },
+ expect: constructorTests[i].expect
+ })
+ }
+}
+
+
+function stringify(value) {
+ return JSON.stringify(value, function replacer(key, val) {
+ if (typeof val === "number") {
+ return val.toString();
+ }
+ return val;
+ })
+}
+
+function describeTest(test) {
+ var ctor = "new Complex(" + (test.set !== undefined ? stringify(test.set) : "") + ")";
+
+ var fnCall = test.fn == null
+ ? ""
+ : "." + test.fn + "(" + (test.param !== undefined ? stringify(test.param) : "") + ")";
+
+ var expectedResult = test.expect == null
+ ? ""
+ : " === " + stringify(test.expect);
+
+ var error = test.error == null
+ ? ""
+ : " should throw " + test.error;
+
+ return ctor + fnCall + expectedResult + error;
+}
+
+describe("Complex functions", function () {
+
+ for (var i = 0; i < functionTests.length; i++) {
+
+ (function (test) {
+
+ it(describeTest(test), function () {
+ if (test.error) {
+ try {
+ new Complex(test.set)[test.fn](test.param);
+ } catch (e) {
+ assert.strictEqual(e.toString(), test.error.toString());
+ }
+ } else {
+ assert.strictEqual(new Complex(test.set)[test.fn](test.param).toString(), test.expect);
+ }
+ });
+ })(functionTests[i]);
+ }
+});
+
+describe("Complex constructor", function () {
+
+ for (var i = 0; i < constructorTests.length; i++) {
+
+ (function (test) {
+ it(describeTest(test), function () {
+ if (test.error) {
+ try {
+ new Complex(test.set);
+ } catch (e) {
+ assert.strictEqual(e.toString(), test.error.toString());
+ }
+ } else {
+ assert.strictEqual(new Complex(test.set).toString(), test.expect);
+ }
+ });
+ })(constructorTests[i]);
+ }
+});
+
+describe("Complex Details", function () {
+
+ it("should work with different params", function () {
+ assert.strictEqual(Complex(1, -1).toString(), "1 - i");
+ assert.strictEqual(Complex(0, 0).toString(), "0");
+ assert.strictEqual(Complex(0, 2).toString(), "2i");
+ assert.strictEqual(Complex.I.toString(), "i");
+ assert.strictEqual(Complex(0, -2).toString(), "-2i");
+ assert.strictEqual(Complex({ re: 0, im: -2 }).toString(), "-2i");
+ });
+
+ it("Complex Combinations", function () {
+
+ var zero = Complex(0, 0), one = Complex(1, 1), two = Complex(2, 2);
+
+ assert.strictEqual(zero.toString(), "0");
+ assert.strictEqual(one.toString(), "1 + i");
+ assert(one.neg().equals(Complex(-1, -1)));
+ assert(one.conjugate().equals(Complex(1, -1)));
+ assert.strictEqual(one.abs(), Math.SQRT2);
+ assert.strictEqual(one.arg(), Math.PI / 4);
+ assert.strictEqual(one.add(one).toString(), two.toString());
+ assert.strictEqual(one.sub(one).toString(), zero.toString());
+ assert.strictEqual(one.mul(2).toString(), two.toString());
+ assert.strictEqual(one.mul(one).toString(), Complex(0, 2).toString());
+ assert.strictEqual(one.div(2).toString(), "0.5 + 0.5i");
+ assert.strictEqual(one.div(one).toString(), "1");
+ assert.strictEqual(one.div(0).toString(), "Infinity");
+ assert.strictEqual(one.exp().toString(), "1.4686939399158851 + 2.2873552871788423i");
+ assert.strictEqual(one.log().toString(), "0.34657359027997264 + 0.7853981633974483i");
+ assert.strictEqual(one.pow(one).toString(), "0.2739572538301211 + 0.5837007587586147i");
+ assert.strictEqual(one.pow(zero).toString(), "1");
+ assert.strictEqual(one.sqrt().toString(), "1.09868411346781 + 0.45508986056222733i");
+ assert.strictEqual(one.sin().toString(), "1.2984575814159773 + 0.6349639147847361i");
+ assert.strictEqual(one.cos().toString(), "0.8337300251311491 - 0.9888977057628651i");
+ assert.strictEqual(one.tan().toString(), "0.27175258531951174 + 1.0839233273386948i");
+ assert.strictEqual(one.asin().toString(), "0.6662394324925153 + 1.0612750619050355i");
+ assert.strictEqual(one.acos().toString(), "0.9045568943023813 - 1.0612750619050355i");
+ assert.strictEqual(one.atan().toString(), "1.0172219678978514 + 0.40235947810852507i");
+
+ var t = Complex(3, 4);
+ assert.strictEqual(t.asinh().toString(), "2.29991404087927 + 0.9176168533514786i");
+
+ assert.strictEqual(t.abs(), 5);
+
+ assert.strictEqual(Complex("5i + 3").log().exp().toString(), "3 + 5i")
+ assert.strictEqual(Complex("-2i - 1").log().exp().toString(), "-1 - 2i")
+ });
+
+ it("should calculate distributed conjugate", function () {
+
+ var c1 = Complex(7, 3);
+ var c2 = Complex(1, 2);
+
+ var r1 = c1.add(c2).conjugate();
+ var r2 = c1.conjugate().add(c2.conjugate());
+
+ assert.strictEqual(r1.toString(), r2.toString());
+ });
+
+ it("should be raised to power of 6", function () {
+ var c1 = Complex(2, 2);
+
+ var t = c1.pow(6);
+
+ assert.strictEqual(t.toString(), "-9.405287417451663e-14 - 511.9999999999995i");
+ });
+
+ it("should handle inverse trig fns", function () {
+
+ var values = [
+ new Complex(2.3, 1.4),
+ new Complex(-2.3, 1.4),
+ new Complex(-2.3, -1.4),
+ new Complex(2.3, -1.4)];
+
+ var fns = ['sin', 'cos', 'tan'];
+
+ for (var i = 0; i < values.length; i++) {
+
+ for (var j = 0; j < 3; j++) {
+
+ var a = values[i]['a' + fns[j]]()[fns[j]]();
+
+ var res = values[i];
+
+ assert(Math.abs(a.re - res.re) < 1e-12 && Math.abs(a.im - res.im) < 1e-12);
+ }
+ }
+ });
+
+ it('should handle get real part', function () {
+ assert.strictEqual(Complex({ abs: 1, arg: Math.PI / 4 }).re, Math.SQRT2 / 2);
+ });
+
+ it('should handle get complex part', function () {
+ assert.strictEqual(Complex({ abs: 1, arg: Math.PI / 4 }).im, 0.7071067811865475);
+ });
+
+ it('should calculate the acosh correctly', function () {
+ assert.strictEqual(Complex({ re: 0.451, im: 0 }).acosh().im, 1.1029108863861707);
+ });
+
+ it('should handle sum', function () {
+ assert.strictEqual(Complex({ abs: 1, arg: 0 }).add({ abs: 1, arg: Math.PI / 2 }).abs(), Math.SQRT2);
+ assert.strictEqual(Complex({ abs: 1, arg: 0 }).add({ abs: 1, arg: Math.PI / 2 }).arg(), Math.PI / 4);
+ });
+
+ it('should handle conjugate', function () {
+ assert.strictEqual(Complex({ abs: 1, arg: Math.PI / 4 }).conjugate().toString(), Complex({ abs: 1, arg: -Math.PI / 4 }).toString());
+ });
+
+ it('should handle substract', function () {
+ assert.strictEqual(Complex({ abs: 1, arg: 0 }).sub({ abs: 1, arg: Math.PI / 2 }).abs().toString(), "1.414213562373095");
+ assert.strictEqual(Complex({ abs: 1, arg: 0 }).sub({ abs: 1, arg: Math.PI / 2 }).arg().toString(), "-0.7853981633974484");
+ });
+
+ it('should handle arg for the first quadrant', function () {
+ assert.strictEqual(Complex({ re: 1, im: 1 }).arg(), Math.PI / 4);
+ });
+
+ it('should handle arg for the second quadrant', function () {
+ assert.strictEqual(Complex({ re: -1, im: 1 }).arg(), 3 * Math.PI / 4);
+ });
+
+ it('should handle arg for the third quadrant', function () {
+ assert.strictEqual(Complex({ re: -1, im: -1 }).arg(), -3 * Math.PI / 4);
+ });
+
+ it('should handle arg for the fourth quadrant', function () {
+ assert.strictEqual(Complex({ re: 1, im: -1 }).arg(), -Math.PI / 4);
+ });
+
+ it('should handle arg for the fourth and first quadrant', function () {
+ assert.strictEqual(Complex({ re: 1, im: 0 }).arg(), 0);
+ });
+
+ it('should handle arg for first and second quadrant', function () {
+ assert.strictEqual(Complex({ re: 0, im: 1 }).arg(), Math.PI / 2);
+ });
+
+ it('should handle arg for the second and third quadrant', function () {
+ assert.strictEqual(Complex({ re: -1, im: 0 }).arg(), Math.PI);
+ });
+
+ it('should handle arg for the third and fourth quadrant', function () {
+ assert.strictEqual(Complex({ re: 0, im: -1 }).arg(), -Math.PI / 2);
+ });
+
+ it("should eat its own dog food", function () {
+
+ var a = Complex(1, -5).toString();
+ var b = Complex(a).toString();
+ var c = Complex(b).mul(a);
+
+ assert.strictEqual(c.toString(), '-24 - 10i');
+ });
+
+ it("should calculate the absolute value of i", function () {
+
+ var a = Complex("i").sign().inverse().mul("i");
+
+ assert.strictEqual(a.toString(), '1');
+ });
+
+ it('should take the natural logarithm', function () {
+ var n = Complex(Math.E * Math.E).log().div("i").mul(-Math.PI * 2, 1);
+
+ assert.strictEqual(n.toString(), '2 + ' + 4 * Math.PI + "i");
+ });
+
+ it('should follow exp rules', function () {
+
+ var a = Math.random() * 10;
+ var b = Math.random() * 10;
+
+ var t1 = Complex({ re: a, im: b }).exp();
+ var t2 = Complex(a).exp().mul(Complex.I.mul(b).exp());
+ var t3 = Complex(a).exp().mul(Complex({ re: 0, im: b }).exp());
+
+ assert.strictEqual(t1.toString(), t2.toString());
+ assert.strictEqual(t2.toString(), t3.toString());
+ });
+
+});
diff --git a/node_modules/cron-parser/LICENSE b/node_modules/cron-parser/LICENSE
new file mode 100644
index 0000000..958d42e
--- /dev/null
+++ b/node_modules/cron-parser/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2016 Harri Siirak
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/node_modules/cron-parser/README.md b/node_modules/cron-parser/README.md
new file mode 100644
index 0000000..6c2aada
--- /dev/null
+++ b/node_modules/cron-parser/README.md
@@ -0,0 +1,175 @@
+cron-parser
+================
+
+[](https://github.com/harrisiirak/cron-parser/actions/workflows/push.yml)
+[](http://badge.fury.io/js/cron-parser)
+
+Node.js library for parsing and manipulating crontab instructions. It includes support for timezones and DST transitions.
+
+__Compatibility__
+Node >= 12.0.0
+TypeScript >= 4.2
+
+Setup
+========
+```bash
+npm install cron-parser
+```
+
+Supported format
+========
+
+```
+* * * * * *
+┬ ┬ ┬ ┬ ┬ ┬
+│ │ │ │ │ |
+│ │ │ │ │ └ day of week (0 - 7, 1L - 7L) (0 or 7 is Sun)
+│ │ │ │ └───── month (1 - 12)
+│ │ │ └────────── day of month (1 - 31, L)
+│ │ └─────────────── hour (0 - 23)
+│ └──────────────────── minute (0 - 59)
+└───────────────────────── second (0 - 59, optional)
+```
+
+Supports mixed use of ranges and range increments (W character not supported currently). See tests for examples.
+
+Usage
+========
+
+Simple expression.
+
+```javascript
+var parser = require('cron-parser');
+
+try {
+ var interval = parser.parseExpression('*/2 * * * *');
+
+ console.log('Date: ', interval.next().toString()); // Sat Dec 29 2012 00:42:00 GMT+0200 (EET)
+ console.log('Date: ', interval.next().toString()); // Sat Dec 29 2012 00:44:00 GMT+0200 (EET)
+
+ console.log('Date: ', interval.prev().toString()); // Sat Dec 29 2012 00:42:00 GMT+0200 (EET)
+ console.log('Date: ', interval.prev().toString()); // Sat Dec 29 2012 00:40:00 GMT+0200 (EET)
+} catch (err) {
+ console.log('Error: ' + err.message);
+}
+
+```
+
+Iteration with limited timespan. Also returns ES6 compatible iterator (when iterator flag is set to true).
+
+```javascript
+var parser = require('cron-parser');
+
+var options = {
+ currentDate: new Date('Wed, 26 Dec 2012 12:38:53 UTC'),
+ endDate: new Date('Wed, 26 Dec 2012 14:40:00 UTC'),
+ iterator: true
+};
+
+try {
+ var interval = parser.parseExpression('*/22 * * * *', options);
+
+ while (true) {
+ try {
+ var obj = interval.next();
+ console.log('value:', obj.value.toString(), 'done:', obj.done);
+ } catch (e) {
+ break;
+ }
+ }
+
+ // value: Wed Dec 26 2012 14:44:00 GMT+0200 (EET) done: false
+ // value: Wed Dec 26 2012 15:00:00 GMT+0200 (EET) done: false
+ // value: Wed Dec 26 2012 15:22:00 GMT+0200 (EET) done: false
+ // value: Wed Dec 26 2012 15:44:00 GMT+0200 (EET) done: false
+ // value: Wed Dec 26 2012 16:00:00 GMT+0200 (EET) done: false
+ // value: Wed Dec 26 2012 16:22:00 GMT+0200 (EET) done: true
+} catch (err) {
+ console.log('Error: ' + err.message);
+}
+
+```
+
+Timezone support
+
+```javascript
+var parser = require('cron-parser');
+
+var options = {
+ currentDate: '2016-03-27 00:00:01',
+ tz: 'Europe/Athens'
+};
+
+try {
+ var interval = parser.parseExpression('0 * * * *', options);
+
+ console.log('Date: ', interval.next().toString()); // Date: Sun Mar 27 2016 01:00:00 GMT+0200
+ console.log('Date: ', interval.next().toString()); // Date: Sun Mar 27 2016 02:00:00 GMT+0200
+ console.log('Date: ', interval.next().toString()); // Date: Sun Mar 27 2016 04:00:00 GMT+0300 (Notice DST transition)
+} catch (err) {
+ console.log('Error: ' + err.message);
+}
+```
+
+Manipulation
+
+```javascript
+var parser = require('cron-parser');
+
+var interval = parser.parseExpression('0 7 * * 0-4');
+var fields = JSON.parse(JSON.stringify(interval.fields)); // Fields is immutable
+fields.hour = [8];
+fields.minute = [29];
+fields.dayOfWeek = [1,3,4,5,6,7];
+var modifiedInterval = parser.fieldsToExpression(fields);
+var cronString = modifiedInterval.stringify();
+console.log(cronString); // "29 8 * * 1,3-7"
+```
+
+Options
+========
+
+* *currentDate* - Start date of the iteration
+* *endDate* - End date of the iteration
+
+`currentDate` and `endDate` accept `string`, `integer` and `Date` as input.
+
+In case of using `string` as input, not every string format accepted
+by the `Date` constructor will work correctly.
+The supported formats are:
+- [`ISO8601`](https://moment.github.io/luxon/#/parsing?id=iso-8601)
+- [`HTTP and RFC2822`](https://moment.github.io/luxon/#/parsing?id=http-and-rfc2822)
+- [`SQL`](https://moment.github.io/luxon/#/parsing?id=sql)
+
+The reason being that those are the formats accepted by the
+[`luxon`](https://moment.github.io/luxon/) library which is being used to handle dates.
+
+Using `Date` as an input can be problematic specially when using the `tz` option. The issue being that, when creating a new `Date` object without
+any timezone information, it will be created in the timezone of the system that is running the code. This (most of times) won't be what the user
+will be expecting. Using one of the supported `string` formats will solve the issue(see timezone example).
+
+* *iterator* - Return ES6 compatible iterator object
+* *utc* - Enable UTC
+* *tz* - Timezone string. It won't be used in case `utc` is enabled
+
+Last weekday of the month
+=========================
+
+This library supports parsing the range `0L - 7L` in the `weekday` position of
+the cron expression, where the `L` means "last occurrence of this weekday for
+the month in progress".
+
+For example, the following expression will run on the last monday of the month
+at midnight:
+
+```
+0 0 0 * * 1L
+```
+
+The library also supports combining `L` expressions with other weekday
+expressions. For example, the following cron will run every Monday as well
+as the last Wednesday of the month:
+
+```
+0 0 0 * * 1,3L
+```
diff --git a/node_modules/cron-parser/lib/date.js b/node_modules/cron-parser/lib/date.js
new file mode 100644
index 0000000..c54e3a5
--- /dev/null
+++ b/node_modules/cron-parser/lib/date.js
@@ -0,0 +1,252 @@
+'use strict';
+
+var luxon = require('luxon');
+
+CronDate.prototype.addYear = function() {
+ this._date = this._date.plus({ years: 1 });
+};
+
+CronDate.prototype.addMonth = function() {
+ this._date = this._date.plus({ months: 1 }).startOf('month');
+};
+
+CronDate.prototype.addDay = function() {
+ this._date = this._date.plus({ days: 1 }).startOf('day');
+};
+
+CronDate.prototype.addHour = function() {
+ var prev = this._date;
+ this._date = this._date.plus({ hours: 1 }).startOf('hour');
+ if (this._date <= prev) {
+ this._date = this._date.plus({ hours: 1 });
+ }
+};
+
+CronDate.prototype.addMinute = function() {
+ var prev = this._date;
+ this._date = this._date.plus({ minutes: 1 }).startOf('minute');
+ if (this._date < prev) {
+ this._date = this._date.plus({ hours: 1 });
+ }
+};
+
+CronDate.prototype.addSecond = function() {
+ var prev = this._date;
+ this._date = this._date.plus({ seconds: 1 }).startOf('second');
+ if (this._date < prev) {
+ this._date = this._date.plus({ hours: 1 });
+ }
+};
+
+CronDate.prototype.subtractYear = function() {
+ this._date = this._date.minus({ years: 1 });
+};
+
+CronDate.prototype.subtractMonth = function() {
+ this._date = this._date
+ .minus({ months: 1 })
+ .endOf('month')
+ .startOf('second');
+};
+
+CronDate.prototype.subtractDay = function() {
+ this._date = this._date
+ .minus({ days: 1 })
+ .endOf('day')
+ .startOf('second');
+};
+
+CronDate.prototype.subtractHour = function() {
+ var prev = this._date;
+ this._date = this._date
+ .minus({ hours: 1 })
+ .endOf('hour')
+ .startOf('second');
+ if (this._date >= prev) {
+ this._date = this._date.minus({ hours: 1 });
+ }
+};
+
+CronDate.prototype.subtractMinute = function() {
+ var prev = this._date;
+ this._date = this._date.minus({ minutes: 1 })
+ .endOf('minute')
+ .startOf('second');
+ if (this._date > prev) {
+ this._date = this._date.minus({ hours: 1 });
+ }
+};
+
+CronDate.prototype.subtractSecond = function() {
+ var prev = this._date;
+ this._date = this._date
+ .minus({ seconds: 1 })
+ .startOf('second');
+ if (this._date > prev) {
+ this._date = this._date.minus({ hours: 1 });
+ }
+};
+
+CronDate.prototype.getDate = function() {
+ return this._date.day;
+};
+
+CronDate.prototype.getFullYear = function() {
+ return this._date.year;
+};
+
+CronDate.prototype.getDay = function() {
+ var weekday = this._date.weekday;
+ return weekday == 7 ? 0 : weekday;
+};
+
+CronDate.prototype.getMonth = function() {
+ return this._date.month - 1;
+};
+
+CronDate.prototype.getHours = function() {
+ return this._date.hour;
+};
+
+CronDate.prototype.getMinutes = function() {
+ return this._date.minute;
+};
+
+CronDate.prototype.getSeconds = function() {
+ return this._date.second;
+};
+
+CronDate.prototype.getMilliseconds = function() {
+ return this._date.millisecond;
+};
+
+CronDate.prototype.getTime = function() {
+ return this._date.valueOf();
+};
+
+CronDate.prototype.getUTCDate = function() {
+ return this._getUTC().day;
+};
+
+CronDate.prototype.getUTCFullYear = function() {
+ return this._getUTC().year;
+};
+
+CronDate.prototype.getUTCDay = function() {
+ var weekday = this._getUTC().weekday;
+ return weekday == 7 ? 0 : weekday;
+};
+
+CronDate.prototype.getUTCMonth = function() {
+ return this._getUTC().month - 1;
+};
+
+CronDate.prototype.getUTCHours = function() {
+ return this._getUTC().hour;
+};
+
+CronDate.prototype.getUTCMinutes = function() {
+ return this._getUTC().minute;
+};
+
+CronDate.prototype.getUTCSeconds = function() {
+ return this._getUTC().second;
+};
+
+CronDate.prototype.toISOString = function() {
+ return this._date.toUTC().toISO();
+};
+
+CronDate.prototype.toJSON = function() {
+ return this._date.toJSON();
+};
+
+CronDate.prototype.setDate = function(d) {
+ this._date = this._date.set({ day: d });
+};
+
+CronDate.prototype.setFullYear = function(y) {
+ this._date = this._date.set({ year: y });
+};
+
+CronDate.prototype.setDay = function(d) {
+ this._date = this._date.set({ weekday: d });
+};
+
+CronDate.prototype.setMonth = function(m) {
+ this._date = this._date.set({ month: m + 1 });
+};
+
+CronDate.prototype.setHours = function(h) {
+ this._date = this._date.set({ hour: h });
+};
+
+CronDate.prototype.setMinutes = function(m) {
+ this._date = this._date.set({ minute: m });
+};
+
+CronDate.prototype.setSeconds = function(s) {
+ this._date = this._date.set({ second: s });
+};
+
+CronDate.prototype.setMilliseconds = function(s) {
+ this._date = this._date.set({ millisecond: s });
+};
+
+CronDate.prototype._getUTC = function() {
+ return this._date.toUTC();
+};
+
+CronDate.prototype.toString = function() {
+ return this.toDate().toString();
+};
+
+CronDate.prototype.toDate = function() {
+ return this._date.toJSDate();
+};
+
+CronDate.prototype.isLastDayOfMonth = function() {
+ //next day
+ var newDate = this._date.plus({ days: 1 }).startOf('day');
+ return this._date.month !== newDate.month;
+};
+
+/**
+ * Returns true when the current weekday is the last occurrence of this weekday
+ * for the present month.
+ */
+CronDate.prototype.isLastWeekdayOfMonth = function() {
+ // Check this by adding 7 days to the current date and seeing if it's
+ // a different month
+ var newDate = this._date.plus({ days: 7 }).startOf('day');
+ return this._date.month !== newDate.month;
+};
+
+function CronDate (timestamp, tz) {
+ var dateOpts = { zone: tz };
+ if (!timestamp) {
+ this._date = luxon.DateTime.local();
+ } else if (timestamp instanceof CronDate) {
+ this._date = timestamp._date;
+ } else if (timestamp instanceof Date) {
+ this._date = luxon.DateTime.fromJSDate(timestamp, dateOpts);
+ } else if (typeof timestamp === 'number') {
+ this._date = luxon.DateTime.fromMillis(timestamp, dateOpts);
+ } else if (typeof timestamp === 'string') {
+ this._date = luxon.DateTime.fromISO(timestamp, dateOpts);
+ this._date.isValid || (this._date = luxon.DateTime.fromRFC2822(timestamp, dateOpts));
+ this._date.isValid || (this._date = luxon.DateTime.fromSQL(timestamp, dateOpts));
+ // RFC2822-like format without the required timezone offset (used in tests)
+ this._date.isValid || (this._date = luxon.DateTime.fromFormat(timestamp, 'EEE, d MMM yyyy HH:mm:ss', dateOpts));
+ }
+
+ if (!this._date || !this._date.isValid) {
+ throw new Error('CronDate: unhandled timestamp: ' + JSON.stringify(timestamp));
+ }
+
+ if (tz && tz !== this._date.zoneName) {
+ this._date = this._date.setZone(tz);
+ }
+}
+
+module.exports = CronDate;
diff --git a/node_modules/cron-parser/lib/expression.js b/node_modules/cron-parser/lib/expression.js
new file mode 100644
index 0000000..0a56853
--- /dev/null
+++ b/node_modules/cron-parser/lib/expression.js
@@ -0,0 +1,1002 @@
+'use strict';
+
+// Load Date class extensions
+var CronDate = require('./date');
+
+var stringifyField = require('./field_stringify');
+
+/**
+ * Cron iteration loop safety limit
+ */
+var LOOP_LIMIT = 10000;
+
+/**
+ * Construct a new expression parser
+ *
+ * Options:
+ * currentDate: iterator start date
+ * endDate: iterator end date
+ *
+ * @constructor
+ * @private
+ * @param {Object} fields Expression fields parsed values
+ * @param {Object} options Parser options
+ */
+function CronExpression (fields, options) {
+ this._options = options;
+ this._utc = options.utc || false;
+ this._tz = this._utc ? 'UTC' : options.tz;
+ this._currentDate = new CronDate(options.currentDate, this._tz);
+ this._startDate = options.startDate ? new CronDate(options.startDate, this._tz) : null;
+ this._endDate = options.endDate ? new CronDate(options.endDate, this._tz) : null;
+ this._isIterator = options.iterator || false;
+ this._hasIterated = false;
+ this._nthDayOfWeek = options.nthDayOfWeek || 0;
+ this.fields = CronExpression._freezeFields(fields);
+}
+
+/**
+ * Field mappings
+ * @type {Array}
+ */
+CronExpression.map = [ 'second', 'minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek' ];
+
+/**
+ * Prefined intervals
+ * @type {Object}
+ */
+CronExpression.predefined = {
+ '@yearly': '0 0 1 1 *',
+ '@monthly': '0 0 1 * *',
+ '@weekly': '0 0 * * 0',
+ '@daily': '0 0 * * *',
+ '@hourly': '0 * * * *'
+};
+
+/**
+ * Fields constraints
+ * @type {Array}
+ */
+CronExpression.constraints = [
+ { min: 0, max: 59, chars: [] }, // Second
+ { min: 0, max: 59, chars: [] }, // Minute
+ { min: 0, max: 23, chars: [] }, // Hour
+ { min: 1, max: 31, chars: ['L'] }, // Day of month
+ { min: 1, max: 12, chars: [] }, // Month
+ { min: 0, max: 7, chars: ['L'] }, // Day of week
+];
+
+/**
+ * Days in month
+ * @type {number[]}
+ */
+CronExpression.daysInMonth = [
+ 31,
+ 29,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31
+];
+
+/**
+ * Field aliases
+ * @type {Object}
+ */
+CronExpression.aliases = {
+ month: {
+ jan: 1,
+ feb: 2,
+ mar: 3,
+ apr: 4,
+ may: 5,
+ jun: 6,
+ jul: 7,
+ aug: 8,
+ sep: 9,
+ oct: 10,
+ nov: 11,
+ dec: 12
+ },
+
+ dayOfWeek: {
+ sun: 0,
+ mon: 1,
+ tue: 2,
+ wed: 3,
+ thu: 4,
+ fri: 5,
+ sat: 6
+ }
+};
+
+/**
+ * Field defaults
+ * @type {Array}
+ */
+CronExpression.parseDefaults = [ '0', '*', '*', '*', '*', '*' ];
+
+CronExpression.standardValidCharacters = /^[,*\d/-]+$/;
+CronExpression.dayOfWeekValidCharacters = /^[?,*\dL#/-]+$/;
+CronExpression.dayOfMonthValidCharacters = /^[?,*\dL/-]+$/;
+CronExpression.validCharacters = {
+ second: CronExpression.standardValidCharacters,
+ minute: CronExpression.standardValidCharacters,
+ hour: CronExpression.standardValidCharacters,
+ dayOfMonth: CronExpression.dayOfMonthValidCharacters,
+ month: CronExpression.standardValidCharacters,
+ dayOfWeek: CronExpression.dayOfWeekValidCharacters,
+};
+
+CronExpression._isValidConstraintChar = function _isValidConstraintChar(constraints, value) {
+ if (typeof value !== 'string') {
+ return false;
+ }
+
+ return constraints.chars.some(function(char) {
+ return value.indexOf(char) > -1;
+ });
+};
+
+/**
+ * Parse input interval
+ *
+ * @param {String} field Field symbolic name
+ * @param {String} value Field value
+ * @param {Array} constraints Range upper and lower constraints
+ * @return {Array} Sequence of sorted values
+ * @private
+ */
+CronExpression._parseField = function _parseField (field, value, constraints) {
+ // Replace aliases
+ switch (field) {
+ case 'month':
+ case 'dayOfWeek':
+ var aliases = CronExpression.aliases[field];
+
+ value = value.replace(/[a-z]{3}/gi, function(match) {
+ match = match.toLowerCase();
+
+ if (typeof aliases[match] !== 'undefined') {
+ return aliases[match];
+ } else {
+ throw new Error('Validation error, cannot resolve alias "' + match + '"');
+ }
+ });
+ break;
+ }
+
+ // Check for valid characters.
+ if (!(CronExpression.validCharacters[field].test(value))) {
+ throw new Error('Invalid characters, got value: ' + value);
+ }
+
+ // Replace '*' and '?'
+ if (value.indexOf('*') !== -1) {
+ value = value.replace(/\*/g, constraints.min + '-' + constraints.max);
+ } else if (value.indexOf('?') !== -1) {
+ value = value.replace(/\?/g, constraints.min + '-' + constraints.max);
+ }
+
+ //
+ // Inline parsing functions
+ //
+ // Parser path:
+ // - parseSequence
+ // - parseRepeat
+ // - parseRange
+
+ /**
+ * Parse sequence
+ *
+ * @param {String} val
+ * @return {Array}
+ * @private
+ */
+ function parseSequence (val) {
+ var stack = [];
+
+ function handleResult (result) {
+ if (result instanceof Array) { // Make sequence linear
+ for (var i = 0, c = result.length; i < c; i++) {
+ var value = result[i];
+
+ if (CronExpression._isValidConstraintChar(constraints, value)) {
+ stack.push(value);
+ continue;
+ }
+ // Check constraints
+ if (typeof value !== 'number' || Number.isNaN(value) || value < constraints.min || value > constraints.max) {
+ throw new Error(
+ 'Constraint error, got value ' + value + ' expected range ' +
+ constraints.min + '-' + constraints.max
+ );
+ }
+
+ stack.push(value);
+ }
+ } else { // Scalar value
+
+ if (CronExpression._isValidConstraintChar(constraints, result)) {
+ stack.push(result);
+ return;
+ }
+
+ var numResult = +result;
+
+ // Check constraints
+ if (Number.isNaN(numResult) || numResult < constraints.min || numResult > constraints.max) {
+ throw new Error(
+ 'Constraint error, got value ' + result + ' expected range ' +
+ constraints.min + '-' + constraints.max
+ );
+ }
+
+ if (field === 'dayOfWeek') {
+ numResult = numResult % 7;
+ }
+
+ stack.push(numResult);
+ }
+ }
+
+ var atoms = val.split(',');
+ if (!atoms.every(function (atom) {
+ return atom.length > 0;
+ })) {
+ throw new Error('Invalid list value format');
+ }
+
+ if (atoms.length > 1) {
+ for (var i = 0, c = atoms.length; i < c; i++) {
+ handleResult(parseRepeat(atoms[i]));
+ }
+ } else {
+ handleResult(parseRepeat(val));
+ }
+
+ stack.sort(CronExpression._sortCompareFn);
+
+ return stack;
+ }
+
+ /**
+ * Parse repetition interval
+ *
+ * @param {String} val
+ * @return {Array}
+ */
+ function parseRepeat (val) {
+ var repeatInterval = 1;
+ var atoms = val.split('/');
+
+ if (atoms.length > 2) {
+ throw new Error('Invalid repeat: ' + val);
+ }
+
+ if (atoms.length > 1) {
+ if (atoms[0] == +atoms[0]) {
+ atoms = [atoms[0] + '-' + constraints.max, atoms[1]];
+ }
+ return parseRange(atoms[0], atoms[atoms.length - 1]);
+ }
+
+ return parseRange(val, repeatInterval);
+ }
+
+ /**
+ * Parse range
+ *
+ * @param {String} val
+ * @param {Number} repeatInterval Repetition interval
+ * @return {Array}
+ * @private
+ */
+ function parseRange (val, repeatInterval) {
+ var stack = [];
+ var atoms = val.split('-');
+
+ if (atoms.length > 1 ) {
+ // Invalid range, return value
+ if (atoms.length < 2) {
+ return +val;
+ }
+
+ if (!atoms[0].length) {
+ if (!atoms[1].length) {
+ throw new Error('Invalid range: ' + val);
+ }
+
+ return +val;
+ }
+
+ // Validate range
+ var min = +atoms[0];
+ var max = +atoms[1];
+
+ if (Number.isNaN(min) || Number.isNaN(max) ||
+ min < constraints.min || max > constraints.max) {
+ throw new Error(
+ 'Constraint error, got range ' +
+ min + '-' + max +
+ ' expected range ' +
+ constraints.min + '-' + constraints.max
+ );
+ } else if (min > max) {
+ throw new Error('Invalid range: ' + val);
+ }
+
+ // Create range
+ var repeatIndex = +repeatInterval;
+
+ if (Number.isNaN(repeatIndex) || repeatIndex <= 0) {
+ throw new Error('Constraint error, cannot repeat at every ' + repeatIndex + ' time.');
+ }
+
+ // JS DOW is in range of 0-6 (SUN-SAT) but we also support 7 in the expression
+ // Handle case when range contains 7 instead of 0 and translate this value to 0
+ if (field === 'dayOfWeek' && max % 7 === 0) {
+ stack.push(0);
+ }
+
+ for (var index = min, count = max; index <= count; index++) {
+ var exists = stack.indexOf(index) !== -1;
+ if (!exists && repeatIndex > 0 && (repeatIndex % repeatInterval) === 0) {
+ repeatIndex = 1;
+ stack.push(index);
+ } else {
+ repeatIndex++;
+ }
+ }
+ return stack;
+ }
+
+ return Number.isNaN(+val) ? val : +val;
+ }
+
+ return parseSequence(value);
+};
+
+CronExpression._sortCompareFn = function(a, b) {
+ var aIsNumber = typeof a === 'number';
+ var bIsNumber = typeof b === 'number';
+
+ if (aIsNumber && bIsNumber) {
+ return a - b;
+ }
+
+ if (!aIsNumber && bIsNumber) {
+ return 1;
+ }
+
+ if (aIsNumber && !bIsNumber) {
+ return -1;
+ }
+
+ return a.localeCompare(b);
+};
+
+CronExpression._handleMaxDaysInMonth = function(mappedFields) {
+ // Filter out any day of month value that is larger than given month expects
+ if (mappedFields.month.length === 1) {
+ var daysInMonth = CronExpression.daysInMonth[mappedFields.month[0] - 1];
+
+ if (mappedFields.dayOfMonth[0] > daysInMonth) {
+ throw new Error('Invalid explicit day of month definition');
+ }
+
+ return mappedFields.dayOfMonth
+ .filter(function(dayOfMonth) {
+ return dayOfMonth === 'L' ? true : dayOfMonth <= daysInMonth;
+ })
+ .sort(CronExpression._sortCompareFn);
+ }
+};
+
+CronExpression._freezeFields = function(fields) {
+ for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
+ var field = CronExpression.map[i]; // Field name
+ var value = fields[field];
+ fields[field] = Object.freeze(value);
+ }
+ return Object.freeze(fields);
+};
+
+CronExpression.prototype._applyTimezoneShift = function(currentDate, dateMathVerb, method) {
+ if ((method === 'Month') || (method === 'Day')) {
+ var prevTime = currentDate.getTime();
+ currentDate[dateMathVerb + method]();
+ var currTime = currentDate.getTime();
+ if (prevTime === currTime) {
+ // Jumped into a not existent date due to a DST transition
+ if ((currentDate.getMinutes() === 0) &&
+ (currentDate.getSeconds() === 0)) {
+ currentDate.addHour();
+ } else if ((currentDate.getMinutes() === 59) &&
+ (currentDate.getSeconds() === 59)) {
+ currentDate.subtractHour();
+ }
+ }
+ } else {
+ var previousHour = currentDate.getHours();
+ currentDate[dateMathVerb + method]();
+ var currentHour = currentDate.getHours();
+ var diff = currentHour - previousHour;
+ if (diff === 2) {
+ // Starting DST
+ if (this.fields.hour.length !== 24) {
+ // Hour is specified
+ this._dstStart = currentHour;
+ }
+ } else if ((diff === 0) &&
+ (currentDate.getMinutes() === 0) &&
+ (currentDate.getSeconds() === 0)) {
+ // Ending DST
+ if (this.fields.hour.length !== 24) {
+ // Hour is specified
+ this._dstEnd = currentHour;
+ }
+ }
+ }
+};
+
+
+/**
+ * Find next or previous matching schedule date
+ *
+ * @return {CronDate}
+ * @private
+ */
+CronExpression.prototype._findSchedule = function _findSchedule (reverse) {
+
+ /**
+ * Match field value
+ *
+ * @param {String} value
+ * @param {Array} sequence
+ * @return {Boolean}
+ * @private
+ */
+ function matchSchedule (value, sequence) {
+ for (var i = 0, c = sequence.length; i < c; i++) {
+ if (sequence[i] >= value) {
+ return sequence[i] === value;
+ }
+ }
+
+ return sequence[0] === value;
+ }
+
+ /**
+ * Helps determine if the provided date is the correct nth occurence of the
+ * desired day of week.
+ *
+ * @param {CronDate} date
+ * @param {Number} nthDayOfWeek
+ * @return {Boolean}
+ * @private
+ */
+ function isNthDayMatch(date, nthDayOfWeek) {
+ if (nthDayOfWeek < 6) {
+ if (
+ date.getDate() < 8 &&
+ nthDayOfWeek === 1 // First occurence has to happen in first 7 days of the month
+ ) {
+ return true;
+ }
+
+ var offset = date.getDate() % 7 ? 1 : 0; // Math is off by 1 when dayOfWeek isn't divisible by 7
+ var adjustedDate = date.getDate() - (date.getDate() % 7); // find the first occurance
+ var occurrence = Math.floor(adjustedDate / 7) + offset;
+
+ return occurrence === nthDayOfWeek;
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper function that checks if 'L' is in the array
+ *
+ * @param {Array} expressions
+ */
+ function isLInExpressions(expressions) {
+ return expressions.length > 0 && expressions.some(function(expression) {
+ return typeof expression === 'string' && expression.indexOf('L') >= 0;
+ });
+ }
+
+
+ // Whether to use backwards directionality when searching
+ reverse = reverse || false;
+ var dateMathVerb = reverse ? 'subtract' : 'add';
+
+ var currentDate = new CronDate(this._currentDate, this._tz);
+ var startDate = this._startDate;
+ var endDate = this._endDate;
+
+ // Find matching schedule
+ var startTimestamp = currentDate.getTime();
+ var stepCount = 0;
+
+ function isLastWeekdayOfMonthMatch(expressions) {
+ return expressions.some(function(expression) {
+ // There might be multiple expressions and not all of them will contain
+ // the "L".
+ if (!isLInExpressions([expression])) {
+ return false;
+ }
+
+ // The first character represents the weekday
+ var weekday = Number.parseInt(expression[0]) % 7;
+
+ if (Number.isNaN(weekday)) {
+ throw new Error('Invalid last weekday of the month expression: ' + expression);
+ }
+
+ return currentDate.getDay() === weekday && currentDate.isLastWeekdayOfMonth();
+ });
+ }
+
+ while (stepCount < LOOP_LIMIT) {
+ stepCount++;
+
+ // Validate timespan
+ if (reverse) {
+ if (startDate && (currentDate.getTime() - startDate.getTime() < 0)) {
+ throw new Error('Out of the timespan range');
+ }
+ } else {
+ if (endDate && (endDate.getTime() - currentDate.getTime()) < 0) {
+ throw new Error('Out of the timespan range');
+ }
+ }
+
+ // Day of month and week matching:
+ //
+ // "The day of a command's execution can be specified by two fields --
+ // day of month, and day of week. If both fields are restricted (ie,
+ // aren't *), the command will be run when either field matches the cur-
+ // rent time. For example, "30 4 1,15 * 5" would cause a command to be
+ // run at 4:30 am on the 1st and 15th of each month, plus every Friday."
+ //
+ // http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5
+ //
+
+ var dayOfMonthMatch = matchSchedule(currentDate.getDate(), this.fields.dayOfMonth);
+ if (isLInExpressions(this.fields.dayOfMonth)) {
+ dayOfMonthMatch = dayOfMonthMatch || currentDate.isLastDayOfMonth();
+ }
+ var dayOfWeekMatch = matchSchedule(currentDate.getDay(), this.fields.dayOfWeek);
+ if (isLInExpressions(this.fields.dayOfWeek)) {
+ dayOfWeekMatch = dayOfWeekMatch || isLastWeekdayOfMonthMatch(this.fields.dayOfWeek);
+ }
+ var isDayOfMonthWildcardMatch = this.fields.dayOfMonth.length >= CronExpression.daysInMonth[currentDate.getMonth()];
+ var isDayOfWeekWildcardMatch = this.fields.dayOfWeek.length === CronExpression.constraints[5].max - CronExpression.constraints[5].min + 1;
+ var currentHour = currentDate.getHours();
+
+ // Add or subtract day if select day not match with month (according to calendar)
+ if (!dayOfMonthMatch && (!dayOfWeekMatch || isDayOfWeekWildcardMatch)) {
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
+ continue;
+ }
+
+ // Add or subtract day if not day of month is set (and no match) and day of week is wildcard
+ if (!isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch && !dayOfMonthMatch) {
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
+ continue;
+ }
+
+ // Add or subtract day if not day of week is set (and no match) and day of month is wildcard
+ if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && !dayOfWeekMatch) {
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
+ continue;
+ }
+
+ // Add or subtract day if day of week & nthDayOfWeek are set (and no match)
+ if (
+ this._nthDayOfWeek > 0 &&
+ !isNthDayMatch(currentDate, this._nthDayOfWeek)
+ ) {
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
+ continue;
+ }
+
+ // Match month
+ if (!matchSchedule(currentDate.getMonth() + 1, this.fields.month)) {
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Month');
+ continue;
+ }
+
+ // Match hour
+ if (!matchSchedule(currentHour, this.fields.hour)) {
+ if (this._dstStart !== currentHour) {
+ this._dstStart = null;
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Hour');
+ continue;
+ } else if (!matchSchedule(currentHour - 1, this.fields.hour)) {
+ currentDate[dateMathVerb + 'Hour']();
+ continue;
+ }
+ } else if (this._dstEnd === currentHour) {
+ if (!reverse) {
+ this._dstEnd = null;
+ this._applyTimezoneShift(currentDate, 'add', 'Hour');
+ continue;
+ }
+ }
+
+ // Match minute
+ if (!matchSchedule(currentDate.getMinutes(), this.fields.minute)) {
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Minute');
+ continue;
+ }
+
+ // Match second
+ if (!matchSchedule(currentDate.getSeconds(), this.fields.second)) {
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Second');
+ continue;
+ }
+
+ // Increase a second in case in the first iteration the currentDate was not
+ // modified
+ if (startTimestamp === currentDate.getTime()) {
+ if ((dateMathVerb === 'add') || (currentDate.getMilliseconds() === 0)) {
+ this._applyTimezoneShift(currentDate, dateMathVerb, 'Second');
+ } else {
+ currentDate.setMilliseconds(0);
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ if (stepCount >= LOOP_LIMIT) {
+ throw new Error('Invalid expression, loop limit exceeded');
+ }
+
+ this._currentDate = new CronDate(currentDate, this._tz);
+ this._hasIterated = true;
+
+ return currentDate;
+};
+
+/**
+ * Find next suitable date
+ *
+ * @public
+ * @return {CronDate|Object}
+ */
+CronExpression.prototype.next = function next () {
+ var schedule = this._findSchedule();
+
+ // Try to return ES6 compatible iterator
+ if (this._isIterator) {
+ return {
+ value: schedule,
+ done: !this.hasNext()
+ };
+ }
+
+ return schedule;
+};
+
+/**
+ * Find previous suitable date
+ *
+ * @public
+ * @return {CronDate|Object}
+ */
+CronExpression.prototype.prev = function prev () {
+ var schedule = this._findSchedule(true);
+
+ // Try to return ES6 compatible iterator
+ if (this._isIterator) {
+ return {
+ value: schedule,
+ done: !this.hasPrev()
+ };
+ }
+
+ return schedule;
+};
+
+/**
+ * Check if next suitable date exists
+ *
+ * @public
+ * @return {Boolean}
+ */
+CronExpression.prototype.hasNext = function() {
+ var current = this._currentDate;
+ var hasIterated = this._hasIterated;
+
+ try {
+ this._findSchedule();
+ return true;
+ } catch (err) {
+ return false;
+ } finally {
+ this._currentDate = current;
+ this._hasIterated = hasIterated;
+ }
+};
+
+/**
+ * Check if previous suitable date exists
+ *
+ * @public
+ * @return {Boolean}
+ */
+CronExpression.prototype.hasPrev = function() {
+ var current = this._currentDate;
+ var hasIterated = this._hasIterated;
+
+ try {
+ this._findSchedule(true);
+ return true;
+ } catch (err) {
+ return false;
+ } finally {
+ this._currentDate = current;
+ this._hasIterated = hasIterated;
+ }
+};
+
+/**
+ * Iterate over expression iterator
+ *
+ * @public
+ * @param {Number} steps Numbers of steps to iterate
+ * @param {Function} callback Optional callback
+ * @return {Array} Array of the iterated results
+ */
+CronExpression.prototype.iterate = function iterate (steps, callback) {
+ var dates = [];
+
+ if (steps >= 0) {
+ for (var i = 0, c = steps; i < c; i++) {
+ try {
+ var item = this.next();
+ dates.push(item);
+
+ // Fire the callback
+ if (callback) {
+ callback(item, i);
+ }
+ } catch (err) {
+ break;
+ }
+ }
+ } else {
+ for (var i = 0, c = steps; i > c; i--) {
+ try {
+ var item = this.prev();
+ dates.push(item);
+
+ // Fire the callback
+ if (callback) {
+ callback(item, i);
+ }
+ } catch (err) {
+ break;
+ }
+ }
+ }
+
+ return dates;
+};
+
+/**
+ * Reset expression iterator state
+ *
+ * @public
+ */
+CronExpression.prototype.reset = function reset (newDate) {
+ this._currentDate = new CronDate(newDate || this._options.currentDate);
+};
+
+/**
+ * Stringify the expression
+ *
+ * @public
+ * @param {Boolean} [includeSeconds] Should stringify seconds
+ * @return {String}
+ */
+CronExpression.prototype.stringify = function stringify(includeSeconds) {
+ var resultArr = [];
+ for (var i = includeSeconds ? 0 : 1, c = CronExpression.map.length; i < c; ++i) {
+ var field = CronExpression.map[i];
+ var value = this.fields[field];
+ var constraint = CronExpression.constraints[i];
+
+ if (field === 'dayOfMonth' && this.fields.month.length === 1) {
+ constraint = { min: 1, max: CronExpression.daysInMonth[this.fields.month[0] - 1] };
+ } else if (field === 'dayOfWeek') {
+ // Prefer 0-6 range when serializing day of week field
+ constraint = { min: 0, max: 6 };
+ value = value[value.length - 1] === 7 ? value.slice(0, -1) : value;
+ }
+
+ resultArr.push(stringifyField(value, constraint.min, constraint.max));
+ }
+ return resultArr.join(' ');
+};
+
+/**
+ * Parse input expression (async)
+ *
+ * @public
+ * @param {String} expression Input expression
+ * @param {Object} [options] Parsing options
+ */
+CronExpression.parse = function parse(expression, options) {
+ var self = this;
+ if (typeof options === 'function') {
+ options = {};
+ }
+
+ function parse (expression, options) {
+ if (!options) {
+ options = {};
+ }
+
+ if (typeof options.currentDate === 'undefined') {
+ options.currentDate = new CronDate(undefined, self._tz);
+ }
+
+ // Is input expression predefined?
+ if (CronExpression.predefined[expression]) {
+ expression = CronExpression.predefined[expression];
+ }
+
+ // Split fields
+ var fields = [];
+ var atoms = (expression + '').trim().split(/\s+/);
+
+ if (atoms.length > 6) {
+ throw new Error('Invalid cron expression');
+ }
+
+ // Resolve fields
+ var start = (CronExpression.map.length - atoms.length);
+ for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
+ var field = CronExpression.map[i]; // Field name
+ var value = atoms[atoms.length > c ? i : i - start]; // Field value
+
+ if (i < start || !value) { // Use default value
+ fields.push(CronExpression._parseField(
+ field,
+ CronExpression.parseDefaults[i],
+ CronExpression.constraints[i]
+ )
+ );
+ } else {
+ var val = field === 'dayOfWeek' ? parseNthDay(value) : value;
+
+ fields.push(CronExpression._parseField(
+ field,
+ val,
+ CronExpression.constraints[i]
+ )
+ );
+ }
+ }
+
+ var mappedFields = {};
+ for (var i = 0, c = CronExpression.map.length; i < c; i++) {
+ var key = CronExpression.map[i];
+ mappedFields[key] = fields[i];
+ }
+
+ var dayOfMonth = CronExpression._handleMaxDaysInMonth(mappedFields);
+ mappedFields.dayOfMonth = dayOfMonth || mappedFields.dayOfMonth;
+ return new CronExpression(mappedFields, options);
+
+ /**
+ * Parses out the # special character for the dayOfWeek field & adds it to options.
+ *
+ * @param {String} val
+ * @return {String}
+ * @private
+ */
+ function parseNthDay(val) {
+ var atoms = val.split('#');
+ if (atoms.length > 1) {
+ var nthValue = +atoms[atoms.length - 1];
+ if(/,/.test(val)) {
+ throw new Error('Constraint error, invalid dayOfWeek `#` and `,` '
+ + 'special characters are incompatible');
+ }
+ if(/\//.test(val)) {
+ throw new Error('Constraint error, invalid dayOfWeek `#` and `/` '
+ + 'special characters are incompatible');
+ }
+ if(/-/.test(val)) {
+ throw new Error('Constraint error, invalid dayOfWeek `#` and `-` '
+ + 'special characters are incompatible');
+ }
+ if (atoms.length > 2 || Number.isNaN(nthValue) || (nthValue < 1 || nthValue > 5)) {
+ throw new Error('Constraint error, invalid dayOfWeek occurrence number (#)');
+ }
+
+ options.nthDayOfWeek = nthValue;
+ return atoms[0];
+ }
+ return val;
+ }
+ }
+
+ return parse(expression, options);
+};
+
+/**
+ * Convert cron fields back to Cron Expression
+ *
+ * @public
+ * @param {Object} fields Input fields
+ * @param {Object} [options] Parsing options
+ * @return {Object}
+ */
+CronExpression.fieldsToExpression = function fieldsToExpression(fields, options) {
+ function validateConstraints (field, values, constraints) {
+ if (!values) {
+ throw new Error('Validation error, Field ' + field + ' is missing');
+ }
+ if (values.length === 0) {
+ throw new Error('Validation error, Field ' + field + ' contains no values');
+ }
+ for (var i = 0, c = values.length; i < c; i++) {
+ var value = values[i];
+
+ if (CronExpression._isValidConstraintChar(constraints, value)) {
+ continue;
+ }
+
+ // Check constraints
+ if (typeof value !== 'number' || Number.isNaN(value) || value < constraints.min || value > constraints.max) {
+ throw new Error(
+ 'Constraint error, got value ' + value + ' expected range ' +
+ constraints.min + '-' + constraints.max
+ );
+ }
+ }
+ }
+
+ var mappedFields = {};
+ for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
+ var field = CronExpression.map[i]; // Field name
+ var values = fields[field];
+ validateConstraints(
+ field,
+ values,
+ CronExpression.constraints[i]
+ );
+ var copy = [];
+ var j = -1;
+ while (++j < values.length) {
+ copy[j] = values[j];
+ }
+ values = copy.sort(CronExpression._sortCompareFn)
+ .filter(function(item, pos, ary) {
+ return !pos || item !== ary[pos - 1];
+ });
+ if (values.length !== copy.length) {
+ throw new Error('Validation error, Field ' + field + ' contains duplicate values');
+ }
+ mappedFields[field] = values;
+ }
+ var dayOfMonth = CronExpression._handleMaxDaysInMonth(mappedFields);
+ mappedFields.dayOfMonth = dayOfMonth || mappedFields.dayOfMonth;
+ return new CronExpression(mappedFields, options || {});
+};
+
+module.exports = CronExpression;
diff --git a/node_modules/cron-parser/lib/field_compactor.js b/node_modules/cron-parser/lib/field_compactor.js
new file mode 100644
index 0000000..f9aeb80
--- /dev/null
+++ b/node_modules/cron-parser/lib/field_compactor.js
@@ -0,0 +1,70 @@
+'use strict';
+
+function buildRange(item) {
+ return {
+ start: item,
+ count: 1
+ };
+}
+
+function completeRangeWithItem(range, item) {
+ range.end = item;
+ range.step = item - range.start;
+ range.count = 2;
+}
+
+function finalizeCurrentRange(results, currentRange, currentItemRange) {
+ if (currentRange) {
+ // Two elements do not form a range so split them into 2 single elements
+ if (currentRange.count === 2) {
+ results.push(buildRange(currentRange.start));
+ results.push(buildRange(currentRange.end));
+ } else {
+ results.push(currentRange);
+ }
+ }
+ if (currentItemRange) {
+ results.push(currentItemRange);
+ }
+}
+
+function compactField(arr) {
+ var results = [];
+ var currentRange = undefined;
+
+ for (var i = 0; i < arr.length; i++) {
+ var currentItem = arr[i];
+ if (typeof currentItem !== 'number') {
+ // String elements can't form a range
+ finalizeCurrentRange(results, currentRange, buildRange(currentItem));
+ currentRange = undefined;
+ } else if (!currentRange) {
+ // Start a new range
+ currentRange = buildRange(currentItem);
+ } else if (currentRange.count === 1) {
+ // Guess that the current item starts a range
+ completeRangeWithItem(currentRange, currentItem);
+ } else {
+ if (currentRange.step === currentItem - currentRange.end) {
+ // We found another item that matches the current range
+ currentRange.count++;
+ currentRange.end = currentItem;
+ } else if (currentRange.count === 2) { // The current range can't be continued
+ // Break the first item of the current range into a single element, and try to start a new range with the second item
+ results.push(buildRange(currentRange.start));
+ currentRange = buildRange(currentRange.end);
+ completeRangeWithItem(currentRange, currentItem);
+ } else {
+ // Persist the current range and start a new one with current item
+ finalizeCurrentRange(results, currentRange);
+ currentRange = buildRange(currentItem);
+ }
+ }
+ }
+
+ finalizeCurrentRange(results, currentRange);
+
+ return results;
+}
+
+module.exports = compactField;
diff --git a/node_modules/cron-parser/lib/field_stringify.js b/node_modules/cron-parser/lib/field_stringify.js
new file mode 100644
index 0000000..9a8c4d7
--- /dev/null
+++ b/node_modules/cron-parser/lib/field_stringify.js
@@ -0,0 +1,58 @@
+'use strict';
+
+var compactField = require('./field_compactor');
+
+function stringifyField(arr, min, max) {
+ var ranges = compactField(arr);
+ if (ranges.length === 1) {
+ var singleRange = ranges[0];
+ var step = singleRange.step;
+ if (step === 1 && singleRange.start === min && singleRange.end === max) {
+ return '*';
+ }
+ if (step !== 1 && singleRange.start === min && singleRange.end === max - step + 1) {
+ return '*/' + step;
+ }
+ }
+
+ var result = [];
+ for (var i = 0, l = ranges.length; i < l; ++i) {
+ var range = ranges[i];
+ if (range.count === 1) {
+ result.push(range.start);
+ continue;
+ }
+
+ var step = range.step;
+ if (range.step === 1) {
+ result.push(range.start + '-' + range.end);
+ continue;
+ }
+
+ var multiplier = range.start == 0 ? range.count - 1 : range.count;
+ if (range.step * multiplier > range.end) {
+ result = result.concat(
+ Array
+ .from({ length: range.end - range.start + 1 })
+ .map(function (_, index) {
+ var value = range.start + index;
+ if ((value - range.start) % range.step === 0) {
+ return value;
+ }
+ return null;
+ })
+ .filter(function (value) {
+ return value != null;
+ })
+ );
+ } else if (range.end === max - range.step + 1) {
+ result.push(range.start + '/' + range.step);
+ } else {
+ result.push(range.start + '-' + range.end + '/' + range.step);
+ }
+ }
+
+ return result.join(',');
+}
+
+module.exports = stringifyField;
diff --git a/node_modules/cron-parser/lib/parser.js b/node_modules/cron-parser/lib/parser.js
new file mode 100644
index 0000000..362a416
--- /dev/null
+++ b/node_modules/cron-parser/lib/parser.js
@@ -0,0 +1,116 @@
+'use strict';
+
+var CronExpression = require('./expression');
+
+function CronParser() {}
+
+/**
+ * Parse crontab entry
+ *
+ * @private
+ * @param {String} entry Crontab file entry/line
+ */
+CronParser._parseEntry = function _parseEntry (entry) {
+ var atoms = entry.split(' ');
+
+ if (atoms.length === 6) {
+ return {
+ interval: CronExpression.parse(entry)
+ };
+ } else if (atoms.length > 6) {
+ return {
+ interval: CronExpression.parse(
+ atoms.slice(0, 6).join(' ')
+ ),
+ command: atoms.slice(6, atoms.length)
+ };
+ } else {
+ throw new Error('Invalid entry: ' + entry);
+ }
+};
+
+/**
+ * Wrapper for CronExpression.parser method
+ *
+ * @public
+ * @param {String} expression Input expression
+ * @param {Object} [options] Parsing options
+ * @return {Object}
+ */
+CronParser.parseExpression = function parseExpression (expression, options) {
+ return CronExpression.parse(expression, options);
+};
+
+/**
+ * Wrapper for CronExpression.fieldsToExpression method
+ *
+ * @public
+ * @param {Object} fields Input fields
+ * @param {Object} [options] Parsing options
+ * @return {Object}
+ */
+CronParser.fieldsToExpression = function fieldsToExpression (fields, options) {
+ return CronExpression.fieldsToExpression(fields, options);
+};
+
+/**
+ * Parse content string
+ *
+ * @public
+ * @param {String} data Crontab content
+ * @return {Object}
+ */
+CronParser.parseString = function parseString (data) {
+ var blocks = data.split('\n');
+
+ var response = {
+ variables: {},
+ expressions: [],
+ errors: {}
+ };
+
+ for (var i = 0, c = blocks.length; i < c; i++) {
+ var block = blocks[i];
+ var matches = null;
+ var entry = block.trim(); // Remove surrounding spaces
+
+ if (entry.length > 0) {
+ if (entry.match(/^#/)) { // Comment
+ continue;
+ } else if ((matches = entry.match(/^(.*)=(.*)$/))) { // Variable
+ response.variables[matches[1]] = matches[2];
+ } else { // Expression?
+ var result = null;
+
+ try {
+ result = CronParser._parseEntry('0 ' + entry);
+ response.expressions.push(result.interval);
+ } catch (err) {
+ response.errors[entry] = err;
+ }
+ }
+ }
+ }
+
+ return response;
+};
+
+/**
+ * Parse crontab file
+ *
+ * @public
+ * @param {String} filePath Path to file
+ * @param {Function} callback
+ */
+CronParser.parseFile = function parseFile (filePath, callback) {
+ require('fs').readFile(filePath, function(err, data) {
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ return callback(null, CronParser.parseString(data.toString()));
+ });
+};
+
+module.exports = CronParser;
diff --git a/node_modules/cron-parser/package.json b/node_modules/cron-parser/package.json
new file mode 100644
index 0000000..c855343
--- /dev/null
+++ b/node_modules/cron-parser/package.json
@@ -0,0 +1,91 @@
+{
+ "name": "cron-parser",
+ "version": "4.9.0",
+ "description": "Node.js library for parsing crontab instructions",
+ "main": "lib/parser.js",
+ "types": "types/index.d.ts",
+ "typesVersions": {
+ "<4.1": {
+ "*": [
+ "types/ts3/*"
+ ]
+ }
+ },
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "test:tsd": "tsd",
+ "test:unit": "TZ=UTC tap ./test/*.js",
+ "test:cover": "TZ=UTC tap --coverage-report=html ./test/*.js",
+ "lint": "eslint .",
+ "lint:fix": "eslint --fix .",
+ "test": "npm run lint && npm run test:unit && npm run test:tsd"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/harrisiirak/cron-parser.git"
+ },
+ "keywords": [
+ "cron",
+ "crontab",
+ "parser"
+ ],
+ "author": "Harri Siirak",
+ "contributors": [
+ "Nicholas Clawson",
+ "Daniel Prentis ",
+ "Renault John Lecoultre",
+ "Richard Astbury ",
+ "Meaglin Wasabi ",
+ "Mike Kusold ",
+ "Alex Kit ",
+ "Santiago Gimeno ",
+ "Daniel ",
+ "Christian Steininger ",
+ "Mykola Piskovyi ",
+ "Brian Vaughn ",
+ "Nicholas Clawson ",
+ "Yasuhiroki ",
+ "Nicholas Clawson ",
+ "Brendan Warkentin ",
+ "Charlie Fish ",
+ "Ian Graves ",
+ "Andy Thompson ",
+ "Regev Brody "
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "luxon": "^3.2.1"
+ },
+ "devDependencies": {
+ "eslint": "^8.27.0",
+ "sinon": "^15.0.1",
+ "tap": "^16.3.3",
+ "tsd": "^0.26.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "browser": {
+ "fs": false
+ },
+ "tap": {
+ "check-coverage": false
+ },
+ "tsd": {
+ "directory": "test",
+ "compilerOptions": {
+ "lib": [
+ "es2017",
+ "dom"
+ ]
+ }
+ },
+ "files": [
+ "lib",
+ "types",
+ "LICENSE",
+ "README.md"
+ ]
+}
diff --git a/node_modules/cron-parser/types/common.d.ts b/node_modules/cron-parser/types/common.d.ts
new file mode 100644
index 0000000..a937304
--- /dev/null
+++ b/node_modules/cron-parser/types/common.d.ts
@@ -0,0 +1,131 @@
+export type DateType = Date | number | string
+
+export interface CronDate {
+ addYear(): void
+
+ addMonth(): void
+
+ addDay(): void
+
+ addHour(): void
+
+ addMinute(): void
+
+ addSecond(): void
+
+ subtractYear(): void
+
+ subtractMonth(): void
+
+ subtractDay(): void
+
+ subtractHour(): void
+
+ subtractMinute(): void
+
+ subtractSecond(): void
+
+ getDate(): number
+
+ getFullYear(): number
+
+ getDay(): number
+
+ getMonth(): number
+
+ getHours(): number
+
+ getMinutes(): number
+
+ getSeconds(): number
+
+ getMilliseconds(): number
+
+ getTime(): number
+
+ getUTCDate(): number
+
+ getUTCFullYear(): number
+
+ getUTCDay(): number
+
+ getUTCMonth(): number
+
+ getUTCHours(): number
+
+ getUTCMinutes(): number
+
+ getUTCSeconds(): number
+
+ toISOString(): string
+
+ toJSON(): string
+
+ setDate(d: number): void
+
+ setFullYear(y: number): void
+
+ setDay(d: number): void
+
+ setMonth(m: number): void
+
+ setHours(h: number): void
+
+ setMinutes(m: number): void
+
+ setSeconds(s: number): void
+
+ setMilliseconds(s: number): void
+
+ getTime(): number
+
+ toString(): string
+
+ toDate(): Date
+
+ isLastDayOfMonth(): boolean
+}
+
+export interface ParserOptions {
+ currentDate?: DateType
+ startDate?: DateType
+ endDate?: DateType
+ utc?: boolean
+ tz?: string
+ nthDayOfWeek?: number
+ iterator?: IsIterable
+}
+
+type IteratorResultOrCronDate = IsIterable extends true
+ ? IteratorResult
+ : CronDate;
+
+export interface ICronExpression {
+ readonly fields: CronFields;
+
+ /** Find next suitable date */
+ next(): IteratorResultOrCronDate
+
+ /** Find previous suitable date */
+ prev(): IteratorResultOrCronDate
+
+ /** Check if next suitable date exists */
+ hasNext(): boolean
+
+ /** Check if previous suitable date exists */
+ hasPrev(): boolean
+
+ /** Iterate over expression iterator */
+ iterate(steps: number, callback?: (item: IteratorResultOrCronDate, i: number) => void): IteratorResultOrCronDate[]
+
+ /** Reset expression iterator state */
+ reset(resetDate?: string | number | Date): void
+
+ stringify(includeSeconds?: boolean): string
+}
+
+export interface IStringResult {
+ variables: Record,
+ expressions: ICronExpression[],
+ errors: Record,
+}
diff --git a/node_modules/cron-parser/types/index.d.ts b/node_modules/cron-parser/types/index.d.ts
new file mode 100644
index 0000000..c771c48
--- /dev/null
+++ b/node_modules/cron-parser/types/index.d.ts
@@ -0,0 +1,45 @@
+import {
+ CronDate,
+ DateType,
+ ICronExpression,
+ IStringResult,
+ ParserOptions,
+} from './common';
+
+type BuildRangeTuple =
+ Current["length"] extends Count
+ ? Current
+ : BuildRangeTuple<[number, ...Current], Count>
+type RangeTuple = BuildRangeTuple<[], Count>
+type BuildRange =
+ Accu["length"] extends End
+ ? Current
+ : BuildRange
+type Range = BuildRange>
+
+export type SixtyRange = Range<0, 30> | Range<30, 60>; // Typescript restriction on recursion depth
+export type HourRange = Range<0, 24>;
+export type DayOfTheMonthRange = Range<1, 32> | 'L';
+export type MonthRange = Range<1, 13>;
+export type DayOfTheWeekRange = Range<0, 8>;
+
+export type CronFields = {
+ readonly second: readonly SixtyRange[];
+ readonly minute: readonly SixtyRange[];
+ readonly hour: readonly HourRange[];
+ readonly dayOfMonth: readonly DayOfTheMonthRange[];
+ readonly month: readonly MonthRange[];
+ readonly dayOfWeek: readonly DayOfTheWeekRange[];
+}
+
+export {ParserOptions, CronDate, DateType}
+export type CronExpression = ICronExpression
+export type StringResult = IStringResult
+
+export function parseExpression(expression: string, options?: ParserOptions): CronExpression;
+
+export function fieldsToExpression(fields: CronFields, options?: ParserOptions): CronExpression;
+
+export function parseFile(filePath: string, callback: (err: any, data: StringResult) => any): void;
+
+export function parseString(data: string): StringResult;
diff --git a/node_modules/cron-parser/types/ts3/index.d.ts b/node_modules/cron-parser/types/ts3/index.d.ts
new file mode 100644
index 0000000..cd80088
--- /dev/null
+++ b/node_modules/cron-parser/types/ts3/index.d.ts
@@ -0,0 +1,28 @@
+import {
+ CronDate,
+ DateType,
+ ICronExpression,
+ IStringResult,
+ ParserOptions,
+} from '../common';
+
+export type CronFields = {
+ readonly second: readonly number[];
+ readonly minute: readonly number[];
+ readonly hour: readonly number[];
+ readonly dayOfMonth: readonly (number | 'L')[];
+ readonly month: readonly number[];
+ readonly dayOfWeek: readonly number[];
+}
+
+export {ParserOptions, CronDate, DateType}
+export type CronExpression = ICronExpression
+export type StringResult = IStringResult
+
+export function parseExpression(expression: string, options?: ParserOptions): CronExpression;
+
+export function fieldsToExpression(fields: CronFields, options?: ParserOptions): CronExpression;
+
+export function parseFile(filePath: string, callback: (err: any, data: StringResult) => any): void;
+
+export function parseString(data: string): StringResult;
diff --git a/node_modules/debug/LICENSE b/node_modules/debug/LICENSE
new file mode 100644
index 0000000..1a9820e
--- /dev/null
+++ b/node_modules/debug/LICENSE
@@ -0,0 +1,20 @@
+(The MIT License)
+
+Copyright (c) 2014-2017 TJ Holowaychuk
+Copyright (c) 2018-2021 Josh Junon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the 'Software'), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/node_modules/debug/README.md b/node_modules/debug/README.md
new file mode 100644
index 0000000..9ebdfbf
--- /dev/null
+++ b/node_modules/debug/README.md
@@ -0,0 +1,481 @@
+# debug
+[](#backers)
+[](#sponsors)
+
+
+
+A tiny JavaScript debugging utility modelled after Node.js core's debugging
+technique. Works in Node.js and web browsers.
+
+## Installation
+
+```bash
+$ npm install debug
+```
+
+## Usage
+
+`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
+
+Example [_app.js_](./examples/node/app.js):
+
+```js
+var debug = require('debug')('http')
+ , http = require('http')
+ , name = 'My App';
+
+// fake app
+
+debug('booting %o', name);
+
+http.createServer(function(req, res){
+ debug(req.method + ' ' + req.url);
+ res.end('hello\n');
+}).listen(3000, function(){
+ debug('listening');
+});
+
+// fake worker of some kind
+
+require('./worker');
+```
+
+Example [_worker.js_](./examples/node/worker.js):
+
+```js
+var a = require('debug')('worker:a')
+ , b = require('debug')('worker:b');
+
+function work() {
+ a('doing lots of uninteresting work');
+ setTimeout(work, Math.random() * 1000);
+}
+
+work();
+
+function workb() {
+ b('doing some work');
+ setTimeout(workb, Math.random() * 2000);
+}
+
+workb();
+```
+
+The `DEBUG` environment variable is then used to enable these based on space or
+comma-delimited names.
+
+Here are some examples:
+
+
+
+
+
+#### Windows command prompt notes
+
+##### CMD
+
+On Windows the environment variable is set using the `set` command.
+
+```cmd
+set DEBUG=*,-not_this
+```
+
+Example:
+
+```cmd
+set DEBUG=* & node app.js
+```
+
+##### PowerShell (VS Code default)
+
+PowerShell uses different syntax to set environment variables.
+
+```cmd
+$env:DEBUG = "*,-not_this"
+```
+
+Example:
+
+```cmd
+$env:DEBUG='app';node app.js
+```
+
+Then, run the program to be debugged as usual.
+
+npm script example:
+```js
+ "windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js",
+```
+
+## Namespace Colors
+
+Every debug instance has a color generated for it based on its namespace name.
+This helps when visually parsing the debug output to identify which debug instance
+a debug line belongs to.
+
+#### Node.js
+
+In Node.js, colors are enabled when stderr is a TTY. You also _should_ install
+the [`supports-color`](https://npmjs.org/supports-color) module alongside debug,
+otherwise debug will only use a small handful of basic colors.
+
+
+
+#### Web Browser
+
+Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
+option. These are WebKit web inspectors, Firefox ([since version
+31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
+and the Firebug plugin for Firefox (any version).
+
+
+
+
+## Millisecond diff
+
+When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
+
+
+
+When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:
+
+
+
+
+## Conventions
+
+If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". If you append a "*" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable. You can then use it for normal output as well as debug output.
+
+## Wildcards
+
+The `*` character may be used as a wildcard. Suppose for example your library has
+debuggers named "connect:bodyParser", "connect:compress", "connect:session",
+instead of listing all three with
+`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do
+`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
+
+You can also exclude specific debuggers by prefixing them with a "-" character.
+For example, `DEBUG=*,-connect:*` would include all debuggers except those
+starting with "connect:".
+
+## Environment Variables
+
+When running through Node.js, you can set a few environment variables that will
+change the behavior of the debug logging:
+
+| Name | Purpose |
+|-----------|-------------------------------------------------|
+| `DEBUG` | Enables/disables specific debugging namespaces. |
+| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |
+| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
+| `DEBUG_DEPTH` | Object inspection depth. |
+| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
+
+
+__Note:__ The environment variables beginning with `DEBUG_` end up being
+converted into an Options object that gets used with `%o`/`%O` formatters.
+See the Node.js documentation for
+[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
+for the complete list.
+
+## Formatters
+
+Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
+Below are the officially supported formatters:
+
+| Formatter | Representation |
+|-----------|----------------|
+| `%O` | Pretty-print an Object on multiple lines. |
+| `%o` | Pretty-print an Object all on a single line. |
+| `%s` | String. |
+| `%d` | Number (both integer and float). |
+| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
+| `%%` | Single percent sign ('%'). This does not consume an argument. |
+
+
+### Custom formatters
+
+You can add custom formatters by extending the `debug.formatters` object.
+For example, if you wanted to add support for rendering a Buffer as hex with
+`%h`, you could do something like:
+
+```js
+const createDebug = require('debug')
+createDebug.formatters.h = (v) => {
+ return v.toString('hex')
+}
+
+// …elsewhere
+const debug = createDebug('foo')
+debug('this is hex: %h', new Buffer('hello world'))
+// foo this is hex: 68656c6c6f20776f726c6421 +0ms
+```
+
+
+## Browser Support
+
+You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
+or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
+if you don't want to build it yourself.
+
+Debug's enable state is currently persisted by `localStorage`.
+Consider the situation shown below where you have `worker:a` and `worker:b`,
+and wish to debug both. You can enable this using `localStorage.debug`:
+
+```js
+localStorage.debug = 'worker:*'
+```
+
+And then refresh the page.
+
+```js
+a = debug('worker:a');
+b = debug('worker:b');
+
+setInterval(function(){
+ a('doing some work');
+}, 1000);
+
+setInterval(function(){
+ b('doing some work');
+}, 1200);
+```
+
+In Chromium-based web browsers (e.g. Brave, Chrome, and Electron), the JavaScript console will—by default—only show messages logged by `debug` if the "Verbose" log level is _enabled_.
+
+
+
+## Output streams
+
+ By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
+
+Example [_stdout.js_](./examples/node/stdout.js):
+
+```js
+var debug = require('debug');
+var error = debug('app:error');
+
+// by default stderr is used
+error('goes to stderr!');
+
+var log = debug('app:log');
+// set this namespace to log via console.log
+log.log = console.log.bind(console); // don't forget to bind to console!
+log('goes to stdout');
+error('still goes to stderr!');
+
+// set all output to go via console.info
+// overrides all per-namespace log settings
+debug.log = console.info.bind(console);
+error('now goes to stdout via console.info');
+log('still goes to stdout, but via console.info now');
+```
+
+## Extend
+You can simply extend debugger
+```js
+const log = require('debug')('auth');
+
+//creates new debug instance with extended namespace
+const logSign = log.extend('sign');
+const logLogin = log.extend('login');
+
+log('hello'); // auth hello
+logSign('hello'); //auth:sign hello
+logLogin('hello'); //auth:login hello
+```
+
+## Set dynamically
+
+You can also enable debug dynamically by calling the `enable()` method :
+
+```js
+let debug = require('debug');
+
+console.log(1, debug.enabled('test'));
+
+debug.enable('test');
+console.log(2, debug.enabled('test'));
+
+debug.disable();
+console.log(3, debug.enabled('test'));
+
+```
+
+print :
+```
+1 false
+2 true
+3 false
+```
+
+Usage :
+`enable(namespaces)`
+`namespaces` can include modes separated by a colon and wildcards.
+
+Note that calling `enable()` completely overrides previously set DEBUG variable :
+
+```
+$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"))'
+=> false
+```
+
+`disable()`
+
+Will disable all namespaces. The functions returns the namespaces currently
+enabled (and skipped). This can be useful if you want to disable debugging
+temporarily without knowing what was enabled to begin with.
+
+For example:
+
+```js
+let debug = require('debug');
+debug.enable('foo:*,-foo:bar');
+let namespaces = debug.disable();
+debug.enable(namespaces);
+```
+
+Note: There is no guarantee that the string will be identical to the initial
+enable string, but semantically they will be identical.
+
+## Checking whether a debug target is enabled
+
+After you've created a debug instance, you can determine whether or not it is
+enabled by checking the `enabled` property:
+
+```javascript
+const debug = require('debug')('http');
+
+if (debug.enabled) {
+ // do stuff...
+}
+```
+
+You can also manually toggle this property to force the debug instance to be
+enabled or disabled.
+
+## Usage in child processes
+
+Due to the way `debug` detects if the output is a TTY or not, colors are not shown in child processes when `stderr` is piped. A solution is to pass the `DEBUG_COLORS=1` environment variable to the child process.
+For example:
+
+```javascript
+worker = fork(WORKER_WRAP_PATH, [workerPath], {
+ stdio: [
+ /* stdin: */ 0,
+ /* stdout: */ 'pipe',
+ /* stderr: */ 'pipe',
+ 'ipc',
+ ],
+ env: Object.assign({}, process.env, {
+ DEBUG_COLORS: 1 // without this settings, colors won't be shown
+ }),
+});
+
+worker.stderr.pipe(process.stderr, { end: false });
+```
+
+
+## Authors
+
+ - TJ Holowaychuk
+ - Nathan Rajlich
+ - Andrew Rhyne
+ - Josh Junon
+
+## Backers
+
+Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Sponsors
+
+Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
+Copyright (c) 2018-2021 Josh Junon
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/debug/package.json b/node_modules/debug/package.json
new file mode 100644
index 0000000..ee8abb5
--- /dev/null
+++ b/node_modules/debug/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "debug",
+ "version": "4.4.3",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/debug-js/debug.git"
+ },
+ "description": "Lightweight debugging utility for Node.js and the browser",
+ "keywords": [
+ "debug",
+ "log",
+ "debugger"
+ ],
+ "files": [
+ "src",
+ "LICENSE",
+ "README.md"
+ ],
+ "author": "Josh Junon (https://github.com/qix-)",
+ "contributors": [
+ "TJ Holowaychuk ",
+ "Nathan Rajlich (http://n8.io)",
+ "Andrew Rhyne "
+ ],
+ "license": "MIT",
+ "scripts": {
+ "lint": "xo",
+ "test": "npm run test:node && npm run test:browser && npm run lint",
+ "test:node": "mocha test.js test.node.js",
+ "test:browser": "karma start --single-run",
+ "test:coverage": "cat ./coverage/lcov.info | coveralls"
+ },
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "devDependencies": {
+ "brfs": "^2.0.1",
+ "browserify": "^16.2.3",
+ "coveralls": "^3.0.2",
+ "karma": "^3.1.4",
+ "karma-browserify": "^6.0.0",
+ "karma-chrome-launcher": "^2.2.0",
+ "karma-mocha": "^1.3.0",
+ "mocha": "^5.2.0",
+ "mocha-lcov-reporter": "^1.2.0",
+ "sinon": "^14.0.0",
+ "xo": "^0.23.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ },
+ "main": "./src/index.js",
+ "browser": "./src/browser.js",
+ "engines": {
+ "node": ">=6.0"
+ },
+ "xo": {
+ "rules": {
+ "import/extensions": "off"
+ }
+ }
+}
diff --git a/node_modules/debug/src/browser.js b/node_modules/debug/src/browser.js
new file mode 100644
index 0000000..5993451
--- /dev/null
+++ b/node_modules/debug/src/browser.js
@@ -0,0 +1,272 @@
+/* eslint-env browser */
+
+/**
+ * This is the web browser implementation of `debug()`.
+ */
+
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+exports.storage = localstorage();
+exports.destroy = (() => {
+ let warned = false;
+
+ return () => {
+ if (!warned) {
+ warned = true;
+ console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
+ }
+ };
+})();
+
+/**
+ * Colors.
+ */
+
+exports.colors = [
+ '#0000CC',
+ '#0000FF',
+ '#0033CC',
+ '#0033FF',
+ '#0066CC',
+ '#0066FF',
+ '#0099CC',
+ '#0099FF',
+ '#00CC00',
+ '#00CC33',
+ '#00CC66',
+ '#00CC99',
+ '#00CCCC',
+ '#00CCFF',
+ '#3300CC',
+ '#3300FF',
+ '#3333CC',
+ '#3333FF',
+ '#3366CC',
+ '#3366FF',
+ '#3399CC',
+ '#3399FF',
+ '#33CC00',
+ '#33CC33',
+ '#33CC66',
+ '#33CC99',
+ '#33CCCC',
+ '#33CCFF',
+ '#6600CC',
+ '#6600FF',
+ '#6633CC',
+ '#6633FF',
+ '#66CC00',
+ '#66CC33',
+ '#9900CC',
+ '#9900FF',
+ '#9933CC',
+ '#9933FF',
+ '#99CC00',
+ '#99CC33',
+ '#CC0000',
+ '#CC0033',
+ '#CC0066',
+ '#CC0099',
+ '#CC00CC',
+ '#CC00FF',
+ '#CC3300',
+ '#CC3333',
+ '#CC3366',
+ '#CC3399',
+ '#CC33CC',
+ '#CC33FF',
+ '#CC6600',
+ '#CC6633',
+ '#CC9900',
+ '#CC9933',
+ '#CCCC00',
+ '#CCCC33',
+ '#FF0000',
+ '#FF0033',
+ '#FF0066',
+ '#FF0099',
+ '#FF00CC',
+ '#FF00FF',
+ '#FF3300',
+ '#FF3333',
+ '#FF3366',
+ '#FF3399',
+ '#FF33CC',
+ '#FF33FF',
+ '#FF6600',
+ '#FF6633',
+ '#FF9900',
+ '#FF9933',
+ '#FFCC00',
+ '#FFCC33'
+];
+
+/**
+ * Currently only WebKit-based Web Inspectors, Firefox >= v31,
+ * and the Firebug extension (any Firefox version) are known
+ * to support "%c" CSS customizations.
+ *
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
+ */
+
+// eslint-disable-next-line complexity
+function useColors() {
+ // NB: In an Electron preload script, document will be defined but not fully
+ // initialized. Since we know we're in Chrome, we'll just detect this case
+ // explicitly
+ if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
+ return true;
+ }
+
+ // Internet Explorer and Edge do not support colors.
+ if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
+ return false;
+ }
+
+ let m;
+
+ // Is webkit? http://stackoverflow.com/a/16459606/376773
+ // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
+ // eslint-disable-next-line no-return-assign
+ return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
+ // Is firebug? http://stackoverflow.com/a/398120/376773
+ (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
+ // Is firefox >= v31?
+ // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
+ (typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31) ||
+ // Double check webkit in userAgent just in case we are in a worker
+ (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
+}
+
+/**
+ * Colorize log arguments if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs(args) {
+ args[0] = (this.useColors ? '%c' : '') +
+ this.namespace +
+ (this.useColors ? ' %c' : ' ') +
+ args[0] +
+ (this.useColors ? '%c ' : ' ') +
+ '+' + module.exports.humanize(this.diff);
+
+ if (!this.useColors) {
+ return;
+ }
+
+ const c = 'color: ' + this.color;
+ args.splice(1, 0, c, 'color: inherit');
+
+ // The final "%c" is somewhat tricky, because there could be other
+ // arguments passed either before or after the %c, so we need to
+ // figure out the correct index to insert the CSS into
+ let index = 0;
+ let lastC = 0;
+ args[0].replace(/%[a-zA-Z%]/g, match => {
+ if (match === '%%') {
+ return;
+ }
+ index++;
+ if (match === '%c') {
+ // We only are interested in the *last* %c
+ // (the user may have provided their own)
+ lastC = index;
+ }
+ });
+
+ args.splice(lastC, 0, c);
+}
+
+/**
+ * Invokes `console.debug()` when available.
+ * No-op when `console.debug` is not a "function".
+ * If `console.debug` is not available, falls back
+ * to `console.log`.
+ *
+ * @api public
+ */
+exports.log = console.debug || console.log || (() => {});
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+function save(namespaces) {
+ try {
+ if (namespaces) {
+ exports.storage.setItem('debug', namespaces);
+ } else {
+ exports.storage.removeItem('debug');
+ }
+ } catch (error) {
+ // Swallow
+ // XXX (@Qix-) should we be logging these?
+ }
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+function load() {
+ let r;
+ try {
+ r = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG') ;
+ } catch (error) {
+ // Swallow
+ // XXX (@Qix-) should we be logging these?
+ }
+
+ // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
+ if (!r && typeof process !== 'undefined' && 'env' in process) {
+ r = process.env.DEBUG;
+ }
+
+ return r;
+}
+
+/**
+ * Localstorage attempts to return the localstorage.
+ *
+ * This is necessary because safari throws
+ * when a user disables cookies/localstorage
+ * and you attempt to access it.
+ *
+ * @return {LocalStorage}
+ * @api private
+ */
+
+function localstorage() {
+ try {
+ // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
+ // The Browser also has localStorage in the global context.
+ return localStorage;
+ } catch (error) {
+ // Swallow
+ // XXX (@Qix-) should we be logging these?
+ }
+}
+
+module.exports = require('./common')(exports);
+
+const {formatters} = module.exports;
+
+/**
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
+ */
+
+formatters.j = function (v) {
+ try {
+ return JSON.stringify(v);
+ } catch (error) {
+ return '[UnexpectedJSONParseError]: ' + error.message;
+ }
+};
diff --git a/node_modules/debug/src/common.js b/node_modules/debug/src/common.js
new file mode 100644
index 0000000..141cb57
--- /dev/null
+++ b/node_modules/debug/src/common.js
@@ -0,0 +1,292 @@
+
+/**
+ * This is the common logic for both the Node.js and web browser
+ * implementations of `debug()`.
+ */
+
+function setup(env) {
+ createDebug.debug = createDebug;
+ createDebug.default = createDebug;
+ createDebug.coerce = coerce;
+ createDebug.disable = disable;
+ createDebug.enable = enable;
+ createDebug.enabled = enabled;
+ createDebug.humanize = require('ms');
+ createDebug.destroy = destroy;
+
+ Object.keys(env).forEach(key => {
+ createDebug[key] = env[key];
+ });
+
+ /**
+ * The currently active debug mode names, and names to skip.
+ */
+
+ createDebug.names = [];
+ createDebug.skips = [];
+
+ /**
+ * Map of special "%n" handling functions, for the debug "format" argument.
+ *
+ * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
+ */
+ createDebug.formatters = {};
+
+ /**
+ * Selects a color for a debug namespace
+ * @param {String} namespace The namespace string for the debug instance to be colored
+ * @return {Number|String} An ANSI color code for the given namespace
+ * @api private
+ */
+ function selectColor(namespace) {
+ let hash = 0;
+
+ for (let i = 0; i < namespace.length; i++) {
+ hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
+ hash |= 0; // Convert to 32bit integer
+ }
+
+ return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
+ }
+ createDebug.selectColor = selectColor;
+
+ /**
+ * Create a debugger with the given `namespace`.
+ *
+ * @param {String} namespace
+ * @return {Function}
+ * @api public
+ */
+ function createDebug(namespace) {
+ let prevTime;
+ let enableOverride = null;
+ let namespacesCache;
+ let enabledCache;
+
+ function debug(...args) {
+ // Disabled?
+ if (!debug.enabled) {
+ return;
+ }
+
+ const self = debug;
+
+ // Set `diff` timestamp
+ const curr = Number(new Date());
+ const ms = curr - (prevTime || curr);
+ self.diff = ms;
+ self.prev = prevTime;
+ self.curr = curr;
+ prevTime = curr;
+
+ args[0] = createDebug.coerce(args[0]);
+
+ if (typeof args[0] !== 'string') {
+ // Anything else let's inspect with %O
+ args.unshift('%O');
+ }
+
+ // Apply any `formatters` transformations
+ let index = 0;
+ args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
+ // If we encounter an escaped % then don't increase the array index
+ if (match === '%%') {
+ return '%';
+ }
+ index++;
+ const formatter = createDebug.formatters[format];
+ if (typeof formatter === 'function') {
+ const val = args[index];
+ match = formatter.call(self, val);
+
+ // Now we need to remove `args[index]` since it's inlined in the `format`
+ args.splice(index, 1);
+ index--;
+ }
+ return match;
+ });
+
+ // Apply env-specific formatting (colors, etc.)
+ createDebug.formatArgs.call(self, args);
+
+ const logFn = self.log || createDebug.log;
+ logFn.apply(self, args);
+ }
+
+ debug.namespace = namespace;
+ debug.useColors = createDebug.useColors();
+ debug.color = createDebug.selectColor(namespace);
+ debug.extend = extend;
+ debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
+
+ Object.defineProperty(debug, 'enabled', {
+ enumerable: true,
+ configurable: false,
+ get: () => {
+ if (enableOverride !== null) {
+ return enableOverride;
+ }
+ if (namespacesCache !== createDebug.namespaces) {
+ namespacesCache = createDebug.namespaces;
+ enabledCache = createDebug.enabled(namespace);
+ }
+
+ return enabledCache;
+ },
+ set: v => {
+ enableOverride = v;
+ }
+ });
+
+ // Env-specific initialization logic for debug instances
+ if (typeof createDebug.init === 'function') {
+ createDebug.init(debug);
+ }
+
+ return debug;
+ }
+
+ function extend(namespace, delimiter) {
+ const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
+ newDebug.log = this.log;
+ return newDebug;
+ }
+
+ /**
+ * Enables a debug mode by namespaces. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} namespaces
+ * @api public
+ */
+ function enable(namespaces) {
+ createDebug.save(namespaces);
+ createDebug.namespaces = namespaces;
+
+ createDebug.names = [];
+ createDebug.skips = [];
+
+ const split = (typeof namespaces === 'string' ? namespaces : '')
+ .trim()
+ .replace(/\s+/g, ',')
+ .split(',')
+ .filter(Boolean);
+
+ for (const ns of split) {
+ if (ns[0] === '-') {
+ createDebug.skips.push(ns.slice(1));
+ } else {
+ createDebug.names.push(ns);
+ }
+ }
+ }
+
+ /**
+ * Checks if the given string matches a namespace template, honoring
+ * asterisks as wildcards.
+ *
+ * @param {String} search
+ * @param {String} template
+ * @return {Boolean}
+ */
+ function matchesTemplate(search, template) {
+ let searchIndex = 0;
+ let templateIndex = 0;
+ let starIndex = -1;
+ let matchIndex = 0;
+
+ while (searchIndex < search.length) {
+ if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {
+ // Match character or proceed with wildcard
+ if (template[templateIndex] === '*') {
+ starIndex = templateIndex;
+ matchIndex = searchIndex;
+ templateIndex++; // Skip the '*'
+ } else {
+ searchIndex++;
+ templateIndex++;
+ }
+ } else if (starIndex !== -1) { // eslint-disable-line no-negated-condition
+ // Backtrack to the last '*' and try to match more characters
+ templateIndex = starIndex + 1;
+ matchIndex++;
+ searchIndex = matchIndex;
+ } else {
+ return false; // No match
+ }
+ }
+
+ // Handle trailing '*' in template
+ while (templateIndex < template.length && template[templateIndex] === '*') {
+ templateIndex++;
+ }
+
+ return templateIndex === template.length;
+ }
+
+ /**
+ * Disable debug output.
+ *
+ * @return {String} namespaces
+ * @api public
+ */
+ function disable() {
+ const namespaces = [
+ ...createDebug.names,
+ ...createDebug.skips.map(namespace => '-' + namespace)
+ ].join(',');
+ createDebug.enable('');
+ return namespaces;
+ }
+
+ /**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+ function enabled(name) {
+ for (const skip of createDebug.skips) {
+ if (matchesTemplate(name, skip)) {
+ return false;
+ }
+ }
+
+ for (const ns of createDebug.names) {
+ if (matchesTemplate(name, ns)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Coerce `val`.
+ *
+ * @param {Mixed} val
+ * @return {Mixed}
+ * @api private
+ */
+ function coerce(val) {
+ if (val instanceof Error) {
+ return val.stack || val.message;
+ }
+ return val;
+ }
+
+ /**
+ * XXX DO NOT USE. This is a temporary stub function.
+ * XXX It WILL be removed in the next major release.
+ */
+ function destroy() {
+ console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
+ }
+
+ createDebug.enable(createDebug.load());
+
+ return createDebug;
+}
+
+module.exports = setup;
diff --git a/node_modules/debug/src/index.js b/node_modules/debug/src/index.js
new file mode 100644
index 0000000..bf4c57f
--- /dev/null
+++ b/node_modules/debug/src/index.js
@@ -0,0 +1,10 @@
+/**
+ * Detect Electron renderer / nwjs process, which is node, but we should
+ * treat as a browser.
+ */
+
+if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {
+ module.exports = require('./browser.js');
+} else {
+ module.exports = require('./node.js');
+}
diff --git a/node_modules/debug/src/node.js b/node_modules/debug/src/node.js
new file mode 100644
index 0000000..715560a
--- /dev/null
+++ b/node_modules/debug/src/node.js
@@ -0,0 +1,263 @@
+/**
+ * Module dependencies.
+ */
+
+const tty = require('tty');
+const util = require('util');
+
+/**
+ * This is the Node.js implementation of `debug()`.
+ */
+
+exports.init = init;
+exports.log = log;
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+exports.destroy = util.deprecate(
+ () => {},
+ 'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'
+);
+
+/**
+ * Colors.
+ */
+
+exports.colors = [6, 2, 3, 4, 5, 1];
+
+try {
+ // Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json)
+ // eslint-disable-next-line import/no-extraneous-dependencies
+ const supportsColor = require('supports-color');
+
+ if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
+ exports.colors = [
+ 20,
+ 21,
+ 26,
+ 27,
+ 32,
+ 33,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 45,
+ 56,
+ 57,
+ 62,
+ 63,
+ 68,
+ 69,
+ 74,
+ 75,
+ 76,
+ 77,
+ 78,
+ 79,
+ 80,
+ 81,
+ 92,
+ 93,
+ 98,
+ 99,
+ 112,
+ 113,
+ 128,
+ 129,
+ 134,
+ 135,
+ 148,
+ 149,
+ 160,
+ 161,
+ 162,
+ 163,
+ 164,
+ 165,
+ 166,
+ 167,
+ 168,
+ 169,
+ 170,
+ 171,
+ 172,
+ 173,
+ 178,
+ 179,
+ 184,
+ 185,
+ 196,
+ 197,
+ 198,
+ 199,
+ 200,
+ 201,
+ 202,
+ 203,
+ 204,
+ 205,
+ 206,
+ 207,
+ 208,
+ 209,
+ 214,
+ 215,
+ 220,
+ 221
+ ];
+ }
+} catch (error) {
+ // Swallow - we only care if `supports-color` is available; it doesn't have to be.
+}
+
+/**
+ * Build up the default `inspectOpts` object from the environment variables.
+ *
+ * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
+ */
+
+exports.inspectOpts = Object.keys(process.env).filter(key => {
+ return /^debug_/i.test(key);
+}).reduce((obj, key) => {
+ // Camel-case
+ const prop = key
+ .substring(6)
+ .toLowerCase()
+ .replace(/_([a-z])/g, (_, k) => {
+ return k.toUpperCase();
+ });
+
+ // Coerce string value into JS value
+ let val = process.env[key];
+ if (/^(yes|on|true|enabled)$/i.test(val)) {
+ val = true;
+ } else if (/^(no|off|false|disabled)$/i.test(val)) {
+ val = false;
+ } else if (val === 'null') {
+ val = null;
+ } else {
+ val = Number(val);
+ }
+
+ obj[prop] = val;
+ return obj;
+}, {});
+
+/**
+ * Is stdout a TTY? Colored output is enabled when `true`.
+ */
+
+function useColors() {
+ return 'colors' in exports.inspectOpts ?
+ Boolean(exports.inspectOpts.colors) :
+ tty.isatty(process.stderr.fd);
+}
+
+/**
+ * Adds ANSI color escape codes if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs(args) {
+ const {namespace: name, useColors} = this;
+
+ if (useColors) {
+ const c = this.color;
+ const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
+ const prefix = ` ${colorCode};1m${name} \u001B[0m`;
+
+ args[0] = prefix + args[0].split('\n').join('\n' + prefix);
+ args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m');
+ } else {
+ args[0] = getDate() + name + ' ' + args[0];
+ }
+}
+
+function getDate() {
+ if (exports.inspectOpts.hideDate) {
+ return '';
+ }
+ return new Date().toISOString() + ' ';
+}
+
+/**
+ * Invokes `util.formatWithOptions()` with the specified arguments and writes to stderr.
+ */
+
+function log(...args) {
+ return process.stderr.write(util.formatWithOptions(exports.inspectOpts, ...args) + '\n');
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+function save(namespaces) {
+ if (namespaces) {
+ process.env.DEBUG = namespaces;
+ } else {
+ // If you set a process.env field to null or undefined, it gets cast to the
+ // string 'null' or 'undefined'. Just delete instead.
+ delete process.env.DEBUG;
+ }
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+ return process.env.DEBUG;
+}
+
+/**
+ * Init logic for `debug` instances.
+ *
+ * Create a new `inspectOpts` object in case `useColors` is set
+ * differently for a particular `debug` instance.
+ */
+
+function init(debug) {
+ debug.inspectOpts = {};
+
+ const keys = Object.keys(exports.inspectOpts);
+ for (let i = 0; i < keys.length; i++) {
+ debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
+ }
+}
+
+module.exports = require('./common')(exports);
+
+const {formatters} = module.exports;
+
+/**
+ * Map %o to `util.inspect()`, all on a single line.
+ */
+
+formatters.o = function (v) {
+ this.inspectOpts.colors = this.useColors;
+ return util.inspect(v, this.inspectOpts)
+ .split('\n')
+ .map(str => str.trim())
+ .join(' ');
+};
+
+/**
+ * Map %O to `util.inspect()`, allowing multiple lines if needed.
+ */
+
+formatters.O = function (v) {
+ this.inspectOpts.colors = this.useColors;
+ return util.inspect(v, this.inspectOpts);
+};
diff --git a/node_modules/decimal.js/LICENCE.md b/node_modules/decimal.js/LICENCE.md
new file mode 100644
index 0000000..57740b9
--- /dev/null
+++ b/node_modules/decimal.js/LICENCE.md
@@ -0,0 +1,23 @@
+The MIT Licence.
+
+Copyright (c) 2025 Michael Mclaughlin
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/node_modules/decimal.js/README.md b/node_modules/decimal.js/README.md
new file mode 100644
index 0000000..f75c0a5
--- /dev/null
+++ b/node_modules/decimal.js/README.md
@@ -0,0 +1,246 @@
+
+
+An arbitrary-precision Decimal type for JavaScript.
+
+[](https://www.npmjs.com/package/decimal.js)
+[](https://www.npmjs.com/package/decimal.js)
+[](https://cdnjs.com/libraries/decimal.js)
+[](https://www.jsdelivr.com/package/npm/decimal.js)
+
+
+
+## Features
+
+ - Integers and floats
+ - Simple but full-featured API
+ - Replicates many of the methods of JavaScript's `Number.prototype` and `Math` objects
+ - Also handles hexadecimal, binary and octal values
+ - Faster, smaller, and perhaps easier to use than JavaScript versions of Java's BigDecimal
+ - No dependencies
+ - Wide platform compatibility: uses JavaScript 1.5 (ECMAScript 3) features only
+ - Comprehensive [documentation](https://mikemcl.github.io/decimal.js/) and test set
+ - Used under the hood by [math.js](https://github.com/josdejong/mathjs)
+ - Includes a TypeScript declaration file: *decimal.d.ts*
+
+
+
+The library is similar to [bignumber.js](https://github.com/MikeMcl/bignumber.js/), but here
+precision is specified in terms of significant digits rather than decimal places, and all
+calculations are rounded to the precision (similar to Python's decimal module) rather than just
+those involving division.
+
+This library also adds the trigonometric functions, among others, and supports non-integer powers,
+which makes it a significantly larger library than *bignumber.js* and the even smaller
+[big.js](https://github.com/MikeMcl/big.js/).
+
+For a lighter version of this library without the trigonometric functions see
+[decimal.js-light](https://github.com/MikeMcl/decimal.js-light/).
+
+## Load
+
+The library is the single JavaScript file *decimal.js* or ES module *decimal.mjs*.
+
+Browser:
+
+```html
+
+
+
+```
+
+[Node.js](https://nodejs.org):
+
+```bash
+npm install decimal.js
+```
+```js
+const Decimal = require('decimal.js');
+
+import Decimal from 'decimal.js';
+
+import {Decimal} from 'decimal.js';
+```
+
+## Use
+
+*In all examples below, semicolons and `toString` calls are not shown.
+If a commented-out value is in quotes it means `toString` has been called on the preceding expression.*
+
+The library exports a single constructor function, `Decimal`, which expects a single argument that is a number, string or Decimal instance.
+
+```js
+x = new Decimal(123.4567)
+y = new Decimal('123456.7e-3')
+z = new Decimal(x)
+x.equals(y) && y.equals(z) && x.equals(z) // true
+```
+
+If using values with more than a few digits, it is recommended to pass strings rather than numbers to avoid a potential loss of precision.
+
+```js
+// Precision loss from using numeric literals with more than 15 significant digits.
+new Decimal(1.0000000000000001) // '1'
+new Decimal(88259496234518.57) // '88259496234518.56'
+new Decimal(99999999999999999999) // '100000000000000000000'
+
+// Precision loss from using numeric literals outside the range of Number values.
+new Decimal(2e+308) // 'Infinity'
+new Decimal(1e-324) // '0'
+
+// Precision loss from the unexpected result of arithmetic with Number values.
+new Decimal(0.7 + 0.1) // '0.7999999999999999'
+```
+
+As with JavaScript numbers, strings can contain underscores as separators to improve readability.
+
+```js
+x = new Decimal('2_147_483_647')
+```
+
+String values in binary, hexadecimal or octal notation are also accepted if the appropriate prefix is included.
+
+```js
+x = new Decimal('0xff.f') // '255.9375'
+y = new Decimal('0b10101100') // '172'
+z = x.plus(y) // '427.9375'
+
+z.toBinary() // '0b110101011.1111'
+z.toBinary(13) // '0b1.101010111111p+8'
+
+// Using binary exponential notation to create a Decimal with the value of `Number.MAX_VALUE`.
+x = new Decimal('0b1.1111111111111111111111111111111111111111111111111111p+1023')
+// '1.7976931348623157081e+308'
+```
+
+Decimal instances are immutable in the sense that they are not changed by their methods.
+
+```js
+0.3 - 0.1 // 0.19999999999999998
+x = new Decimal(0.3)
+x.minus(0.1) // '0.2'
+x // '0.3'
+```
+
+The methods that return a Decimal can be chained.
+
+```js
+x.dividedBy(y).plus(z).times(9).floor()
+x.times('1.23456780123456789e+9').plus(9876.5432321).dividedBy('4444562598.111772').ceil()
+```
+
+Many method names have a shorter alias.
+
+```js
+x.squareRoot().dividedBy(y).toPower(3).equals(x.sqrt().div(y).pow(3)) // true
+x.comparedTo(y.modulo(z).negated() === x.cmp(y.mod(z).neg()) // true
+```
+
+Most of the methods of JavaScript's `Number.prototype` and `Math` objects are replicated.
+
+```js
+x = new Decimal(255.5)
+x.toExponential(5) // '2.55500e+2'
+x.toFixed(5) // '255.50000'
+x.toPrecision(5) // '255.50'
+
+Decimal.sqrt('6.98372465832e+9823') // '8.3568682281821340204e+4911'
+Decimal.pow(2, 0.0979843) // '1.0702770511687781839'
+
+// Using `toFixed()` to avoid exponential notation:
+x = new Decimal('0.0000001')
+x.toString() // '1e-7'
+x.toFixed() // '0.0000001'
+```
+
+And there are `isNaN` and `isFinite` methods, as `NaN` and `Infinity` are valid `Decimal` values.
+
+```js
+x = new Decimal(NaN) // 'NaN'
+y = new Decimal(Infinity) // 'Infinity'
+x.isNaN() && !y.isNaN() && !x.isFinite() && !y.isFinite() // true
+```
+
+There is also a `toFraction` method with an optional *maximum denominator* argument.
+
+```js
+z = new Decimal(355)
+pi = z.dividedBy(113) // '3.1415929204'
+pi.toFraction() // [ '7853982301', '2500000000' ]
+pi.toFraction(1000) // [ '355', '113' ]
+```
+
+All calculations are rounded according to the number of significant digits and rounding mode specified
+by the `precision` and `rounding` properties of the Decimal constructor.
+
+For advanced usage, multiple Decimal constructors can be created, each with their own independent
+configuration which applies to all Decimal numbers created from it.
+
+```js
+// Set the precision and rounding of the default Decimal constructor
+Decimal.set({ precision: 5, rounding: 4 })
+
+// Create another Decimal constructor, optionally passing in a configuration object
+Dec = Decimal.clone({ precision: 9, rounding: 1 })
+
+x = new Decimal(5)
+y = new Dec(5)
+
+x.div(3) // '1.6667'
+y.div(3) // '1.66666666'
+```
+
+The value of a Decimal is stored in a floating point format in terms of its digits, exponent and sign, but these properties should be considered read-only.
+
+```js
+x = new Decimal(-12345.67);
+x.d // [ 12345, 6700000 ] digits (base 10000000)
+x.e // 4 exponent (base 10)
+x.s // -1 sign
+```
+
+For further information see the [API](http://mikemcl.github.io/decimal.js/) reference in the *doc* directory.
+
+## Test
+
+To run the tests using Node.js from the root directory:
+
+```bash
+npm test
+```
+
+Each separate test module can also be executed individually, for example:
+
+```bash
+node test/modules/toFraction
+```
+
+To run the tests in a browser, open *test/test.html*.
+
+## Minify
+
+Two minification examples:
+
+Using [uglify-js](https://github.com/mishoo/UglifyJS) to minify the *decimal.js* file:
+
+```bash
+npm install uglify-js -g
+uglifyjs decimal.js --source-map url=decimal.min.js.map -c -m -o decimal.min.js
+```
+
+Using [terser](https://github.com/terser/terser) to minify the ES module version, *decimal.mjs*:
+
+```bash
+npm install terser -g
+terser decimal.mjs --source-map url=decimal.min.mjs.map -c -m --toplevel -o decimal.min.mjs
+```
+
+```js
+import Decimal from './decimal.min.mjs';
+```
+
+## Licence
+
+[The MIT Licence](LICENCE.md)
diff --git a/node_modules/decimal.js/decimal.d.ts b/node_modules/decimal.js/decimal.d.ts
new file mode 100644
index 0000000..e0db32e
--- /dev/null
+++ b/node_modules/decimal.js/decimal.d.ts
@@ -0,0 +1,301 @@
+// Type definitions for decimal.js >=7.0.0
+// Project: https://github.com/MikeMcl/decimal.js
+// Definitions by: Michael Mclaughlin
+// Definitions: https://github.com/MikeMcl/decimal.js
+//
+// Documentation: http://mikemcl.github.io/decimal.js/
+//
+// Exports:
+//
+// class Decimal (default export)
+// type Decimal.Constructor
+// type Decimal.Instance
+// type Decimal.Modulo
+// type Decimal.Rounding
+// type Decimal.Value
+// interface Decimal.Config
+//
+// Example (alternative syntax commented-out):
+//
+// import {Decimal} from "decimal.js"
+// //import Decimal from "decimal.js"
+//
+// let r: Decimal.Rounding = Decimal.ROUND_UP;
+// let c: Decimal.Configuration = {precision: 4, rounding: r};
+// Decimal.set(c);
+// let v: Decimal.Value = '12345.6789';
+// let d: Decimal = new Decimal(v);
+// //let d: Decimal.Instance = new Decimal(v);
+//
+// The use of compiler option `--strictNullChecks` is recommended.
+
+export default Decimal;
+
+export namespace Decimal {
+ export type Constructor = typeof Decimal;
+ export type Instance = Decimal;
+ export type Rounding = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
+ export type Modulo = Rounding | 9;
+ export type Value = string | number | bigint | Decimal;
+
+ // http://mikemcl.github.io/decimal.js/#constructor-properties
+ export interface Config {
+ precision?: number;
+ rounding?: Rounding;
+ toExpNeg?: number;
+ toExpPos?: number;
+ minE?: number;
+ maxE?: number;
+ crypto?: boolean;
+ modulo?: Modulo;
+ defaults?: boolean;
+ }
+}
+
+export declare class Decimal {
+ readonly d: number[];
+ readonly e: number;
+ readonly s: number;
+
+ constructor(n: Decimal.Value);
+
+ absoluteValue(): Decimal;
+ abs(): Decimal;
+
+ ceil(): Decimal;
+
+ clampedTo(min: Decimal.Value, max: Decimal.Value): Decimal;
+ clamp(min: Decimal.Value, max: Decimal.Value): Decimal;
+
+ comparedTo(n: Decimal.Value): number;
+ cmp(n: Decimal.Value): number;
+
+ cosine(): Decimal;
+ cos(): Decimal;
+
+ cubeRoot(): Decimal;
+ cbrt(): Decimal;
+
+ decimalPlaces(): number;
+ dp(): number;
+
+ dividedBy(n: Decimal.Value): Decimal;
+ div(n: Decimal.Value): Decimal;
+
+ dividedToIntegerBy(n: Decimal.Value): Decimal;
+ divToInt(n: Decimal.Value): Decimal;
+
+ equals(n: Decimal.Value): boolean;
+ eq(n: Decimal.Value): boolean;
+
+ floor(): Decimal;
+
+ greaterThan(n: Decimal.Value): boolean;
+ gt(n: Decimal.Value): boolean;
+
+ greaterThanOrEqualTo(n: Decimal.Value): boolean;
+ gte(n: Decimal.Value): boolean;
+
+ hyperbolicCosine(): Decimal;
+ cosh(): Decimal;
+
+ hyperbolicSine(): Decimal;
+ sinh(): Decimal;
+
+ hyperbolicTangent(): Decimal;
+ tanh(): Decimal;
+
+ inverseCosine(): Decimal;
+ acos(): Decimal;
+
+ inverseHyperbolicCosine(): Decimal;
+ acosh(): Decimal;
+
+ inverseHyperbolicSine(): Decimal;
+ asinh(): Decimal;
+
+ inverseHyperbolicTangent(): Decimal;
+ atanh(): Decimal;
+
+ inverseSine(): Decimal;
+ asin(): Decimal;
+
+ inverseTangent(): Decimal;
+ atan(): Decimal;
+
+ isFinite(): boolean;
+
+ isInteger(): boolean;
+ isInt(): boolean;
+
+ isNaN(): boolean;
+
+ isNegative(): boolean;
+ isNeg(): boolean;
+
+ isPositive(): boolean;
+ isPos(): boolean;
+
+ isZero(): boolean;
+
+ lessThan(n: Decimal.Value): boolean;
+ lt(n: Decimal.Value): boolean;
+
+ lessThanOrEqualTo(n: Decimal.Value): boolean;
+ lte(n: Decimal.Value): boolean;
+
+ logarithm(n?: Decimal.Value): Decimal;
+ log(n?: Decimal.Value): Decimal;
+
+ minus(n: Decimal.Value): Decimal;
+ sub(n: Decimal.Value): Decimal;
+
+ modulo(n: Decimal.Value): Decimal;
+ mod(n: Decimal.Value): Decimal;
+
+ naturalExponential(): Decimal;
+ exp(): Decimal;
+
+ naturalLogarithm(): Decimal;
+ ln(): Decimal;
+
+ negated(): Decimal;
+ neg(): Decimal;
+
+ plus(n: Decimal.Value): Decimal;
+ add(n: Decimal.Value): Decimal;
+
+ precision(includeZeros?: boolean): number;
+ sd(includeZeros?: boolean): number;
+
+ round(): Decimal;
+
+ sine() : Decimal;
+ sin() : Decimal;
+
+ squareRoot(): Decimal;
+ sqrt(): Decimal;
+
+ tangent() : Decimal;
+ tan() : Decimal;
+
+ times(n: Decimal.Value): Decimal;
+ mul(n: Decimal.Value) : Decimal;
+
+ toBinary(significantDigits?: number): string;
+ toBinary(significantDigits: number, rounding: Decimal.Rounding): string;
+
+ toDecimalPlaces(decimalPlaces?: number): Decimal;
+ toDecimalPlaces(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
+ toDP(decimalPlaces?: number): Decimal;
+ toDP(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
+
+ toExponential(decimalPlaces?: number): string;
+ toExponential(decimalPlaces: number, rounding: Decimal.Rounding): string;
+
+ toFixed(decimalPlaces?: number): string;
+ toFixed(decimalPlaces: number, rounding: Decimal.Rounding): string;
+
+ toFraction(max_denominator?: Decimal.Value): Decimal[];
+
+ toHexadecimal(significantDigits?: number): string;
+ toHexadecimal(significantDigits: number, rounding: Decimal.Rounding): string;
+ toHex(significantDigits?: number): string;
+ toHex(significantDigits: number, rounding?: Decimal.Rounding): string;
+
+ toJSON(): string;
+
+ toNearest(n: Decimal.Value, rounding?: Decimal.Rounding): Decimal;
+
+ toNumber(): number;
+
+ toOctal(significantDigits?: number): string;
+ toOctal(significantDigits: number, rounding: Decimal.Rounding): string;
+
+ toPower(n: Decimal.Value): Decimal;
+ pow(n: Decimal.Value): Decimal;
+
+ toPrecision(significantDigits?: number): string;
+ toPrecision(significantDigits: number, rounding: Decimal.Rounding): string;
+
+ toSignificantDigits(significantDigits?: number): Decimal;
+ toSignificantDigits(significantDigits: number, rounding: Decimal.Rounding): Decimal;
+ toSD(significantDigits?: number): Decimal;
+ toSD(significantDigits: number, rounding: Decimal.Rounding): Decimal;
+
+ toString(): string;
+
+ truncated(): Decimal;
+ trunc(): Decimal;
+
+ valueOf(): string;
+
+ static abs(n: Decimal.Value): Decimal;
+ static acos(n: Decimal.Value): Decimal;
+ static acosh(n: Decimal.Value): Decimal;
+ static add(x: Decimal.Value, y: Decimal.Value): Decimal;
+ static asin(n: Decimal.Value): Decimal;
+ static asinh(n: Decimal.Value): Decimal;
+ static atan(n: Decimal.Value): Decimal;
+ static atanh(n: Decimal.Value): Decimal;
+ static atan2(y: Decimal.Value, x: Decimal.Value): Decimal;
+ static cbrt(n: Decimal.Value): Decimal;
+ static ceil(n: Decimal.Value): Decimal;
+ static clamp(n: Decimal.Value, min: Decimal.Value, max: Decimal.Value): Decimal;
+ static clone(object?: Decimal.Config): Decimal.Constructor;
+ static config(object: Decimal.Config): Decimal.Constructor;
+ static cos(n: Decimal.Value): Decimal;
+ static cosh(n: Decimal.Value): Decimal;
+ static div(x: Decimal.Value, y: Decimal.Value): Decimal;
+ static exp(n: Decimal.Value): Decimal;
+ static floor(n: Decimal.Value): Decimal;
+ static hypot(...n: Decimal.Value[]): Decimal;
+ static isDecimal(object: any): object is Decimal;
+ static ln(n: Decimal.Value): Decimal;
+ static log(n: Decimal.Value, base?: Decimal.Value): Decimal;
+ static log2(n: Decimal.Value): Decimal;
+ static log10(n: Decimal.Value): Decimal;
+ static max(...n: Decimal.Value[]): Decimal;
+ static min(...n: Decimal.Value[]): Decimal;
+ static mod(x: Decimal.Value, y: Decimal.Value): Decimal;
+ static mul(x: Decimal.Value, y: Decimal.Value): Decimal;
+ static noConflict(): Decimal.Constructor; // Browser only
+ static pow(base: Decimal.Value, exponent: Decimal.Value): Decimal;
+ static random(significantDigits?: number): Decimal;
+ static round(n: Decimal.Value): Decimal;
+ static set(object: Decimal.Config): Decimal.Constructor;
+ static sign(n: Decimal.Value): number;
+ static sin(n: Decimal.Value): Decimal;
+ static sinh(n: Decimal.Value): Decimal;
+ static sqrt(n: Decimal.Value): Decimal;
+ static sub(x: Decimal.Value, y: Decimal.Value): Decimal;
+ static sum(...n: Decimal.Value[]): Decimal;
+ static tan(n: Decimal.Value): Decimal;
+ static tanh(n: Decimal.Value): Decimal;
+ static trunc(n: Decimal.Value): Decimal;
+
+ static readonly default?: Decimal.Constructor;
+ static readonly Decimal?: Decimal.Constructor;
+
+ static readonly precision: number;
+ static readonly rounding: Decimal.Rounding;
+ static readonly toExpNeg: number;
+ static readonly toExpPos: number;
+ static readonly minE: number;
+ static readonly maxE: number;
+ static readonly crypto: boolean;
+ static readonly modulo: Decimal.Modulo;
+
+ static readonly ROUND_UP: 0;
+ static readonly ROUND_DOWN: 1;
+ static readonly ROUND_CEIL: 2;
+ static readonly ROUND_FLOOR: 3;
+ static readonly ROUND_HALF_UP: 4;
+ static readonly ROUND_HALF_DOWN: 5;
+ static readonly ROUND_HALF_EVEN: 6;
+ static readonly ROUND_HALF_CEIL: 7;
+ static readonly ROUND_HALF_FLOOR: 8;
+ static readonly EUCLID: 9;
+}
+
+export declare function Decimal(n: Decimal.Value): Decimal;
diff --git a/node_modules/decimal.js/decimal.js b/node_modules/decimal.js/decimal.js
new file mode 100644
index 0000000..23295c6
--- /dev/null
+++ b/node_modules/decimal.js/decimal.js
@@ -0,0 +1,4951 @@
+;(function (globalScope) {
+ 'use strict';
+
+
+ /*!
+ * decimal.js v10.6.0
+ * An arbitrary-precision Decimal type for JavaScript.
+ * https://github.com/MikeMcl/decimal.js
+ * Copyright (c) 2025 Michael Mclaughlin
+ * MIT Licence
+ */
+
+
+ // ----------------------------------- EDITABLE DEFAULTS ------------------------------------ //
+
+
+ // The maximum exponent magnitude.
+ // The limit on the value of `toExpNeg`, `toExpPos`, `minE` and `maxE`.
+ var EXP_LIMIT = 9e15, // 0 to 9e15
+
+ // The limit on the value of `precision`, and on the value of the first argument to
+ // `toDecimalPlaces`, `toExponential`, `toFixed`, `toPrecision` and `toSignificantDigits`.
+ MAX_DIGITS = 1e9, // 0 to 1e9
+
+ // Base conversion alphabet.
+ NUMERALS = '0123456789abcdef',
+
+ // The natural logarithm of 10 (1025 digits).
+ LN10 = '2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220114804663715659121373450747856947683463616792101806445070648000277502684916746550586856935673420670581136429224554405758925724208241314695689016758940256776311356919292033376587141660230105703089634572075440370847469940168269282808481184289314848524948644871927809676271275775397027668605952496716674183485704422507197965004714951050492214776567636938662976979522110718264549734772662425709429322582798502585509785265383207606726317164309505995087807523710333101197857547331541421808427543863591778117054309827482385045648019095610299291824318237525357709750539565187697510374970888692180205189339507238539205144634197265287286965110862571492198849978748873771345686209167058',
+
+ // Pi (1025 digits).
+ PI = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632789',
+
+
+ // The initial configuration properties of the Decimal constructor.
+ DEFAULTS = {
+
+ // These values must be integers within the stated ranges (inclusive).
+ // Most of these values can be changed at run-time using the `Decimal.config` method.
+
+ // The maximum number of significant digits of the result of a calculation or base conversion.
+ // E.g. `Decimal.config({ precision: 20 });`
+ precision: 20, // 1 to MAX_DIGITS
+
+ // The rounding mode used when rounding to `precision`.
+ //
+ // ROUND_UP 0 Away from zero.
+ // ROUND_DOWN 1 Towards zero.
+ // ROUND_CEIL 2 Towards +Infinity.
+ // ROUND_FLOOR 3 Towards -Infinity.
+ // ROUND_HALF_UP 4 Towards nearest neighbour. If equidistant, up.
+ // ROUND_HALF_DOWN 5 Towards nearest neighbour. If equidistant, down.
+ // ROUND_HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour.
+ // ROUND_HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity.
+ // ROUND_HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity.
+ //
+ // E.g.
+ // `Decimal.rounding = 4;`
+ // `Decimal.rounding = Decimal.ROUND_HALF_UP;`
+ rounding: 4, // 0 to 8
+
+ // The modulo mode used when calculating the modulus: a mod n.
+ // The quotient (q = a / n) is calculated according to the corresponding rounding mode.
+ // The remainder (r) is calculated as: r = a - n * q.
+ //
+ // UP 0 The remainder is positive if the dividend is negative, else is negative.
+ // DOWN 1 The remainder has the same sign as the dividend (JavaScript %).
+ // FLOOR 3 The remainder has the same sign as the divisor (Python %).
+ // HALF_EVEN 6 The IEEE 754 remainder function.
+ // EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). Always positive.
+ //
+ // Truncated division (1), floored division (3), the IEEE 754 remainder (6), and Euclidian
+ // division (9) are commonly used for the modulus operation. The other rounding modes can also
+ // be used, but they may not give useful results.
+ modulo: 1, // 0 to 9
+
+ // The exponent value at and beneath which `toString` returns exponential notation.
+ // JavaScript numbers: -7
+ toExpNeg: -7, // 0 to -EXP_LIMIT
+
+ // The exponent value at and above which `toString` returns exponential notation.
+ // JavaScript numbers: 21
+ toExpPos: 21, // 0 to EXP_LIMIT
+
+ // The minimum exponent value, beneath which underflow to zero occurs.
+ // JavaScript numbers: -324 (5e-324)
+ minE: -EXP_LIMIT, // -1 to -EXP_LIMIT
+
+ // The maximum exponent value, above which overflow to Infinity occurs.
+ // JavaScript numbers: 308 (1.7976931348623157e+308)
+ maxE: EXP_LIMIT, // 1 to EXP_LIMIT
+
+ // Whether to use cryptographically-secure random number generation, if available.
+ crypto: false // true/false
+ },
+
+
+ // ----------------------------------- END OF EDITABLE DEFAULTS ------------------------------- //
+
+
+ Decimal, inexact, noConflict, quadrant,
+ external = true,
+
+ decimalError = '[DecimalError] ',
+ invalidArgument = decimalError + 'Invalid argument: ',
+ precisionLimitExceeded = decimalError + 'Precision limit exceeded',
+ cryptoUnavailable = decimalError + 'crypto unavailable',
+ tag = '[object Decimal]',
+
+ mathfloor = Math.floor,
+ mathpow = Math.pow,
+
+ isBinary = /^0b([01]+(\.[01]*)?|\.[01]+)(p[+-]?\d+)?$/i,
+ isHex = /^0x([0-9a-f]+(\.[0-9a-f]*)?|\.[0-9a-f]+)(p[+-]?\d+)?$/i,
+ isOctal = /^0o([0-7]+(\.[0-7]*)?|\.[0-7]+)(p[+-]?\d+)?$/i,
+ isDecimal = /^(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,
+
+ BASE = 1e7,
+ LOG_BASE = 7,
+ MAX_SAFE_INTEGER = 9007199254740991,
+
+ LN10_PRECISION = LN10.length - 1,
+ PI_PRECISION = PI.length - 1,
+
+ // Decimal.prototype object
+ P = { toStringTag: tag };
+
+
+ // Decimal prototype methods
+
+
+ /*
+ * absoluteValue abs
+ * ceil
+ * clampedTo clamp
+ * comparedTo cmp
+ * cosine cos
+ * cubeRoot cbrt
+ * decimalPlaces dp
+ * dividedBy div
+ * dividedToIntegerBy divToInt
+ * equals eq
+ * floor
+ * greaterThan gt
+ * greaterThanOrEqualTo gte
+ * hyperbolicCosine cosh
+ * hyperbolicSine sinh
+ * hyperbolicTangent tanh
+ * inverseCosine acos
+ * inverseHyperbolicCosine acosh
+ * inverseHyperbolicSine asinh
+ * inverseHyperbolicTangent atanh
+ * inverseSine asin
+ * inverseTangent atan
+ * isFinite
+ * isInteger isInt
+ * isNaN
+ * isNegative isNeg
+ * isPositive isPos
+ * isZero
+ * lessThan lt
+ * lessThanOrEqualTo lte
+ * logarithm log
+ * [maximum] [max]
+ * [minimum] [min]
+ * minus sub
+ * modulo mod
+ * naturalExponential exp
+ * naturalLogarithm ln
+ * negated neg
+ * plus add
+ * precision sd
+ * round
+ * sine sin
+ * squareRoot sqrt
+ * tangent tan
+ * times mul
+ * toBinary
+ * toDecimalPlaces toDP
+ * toExponential
+ * toFixed
+ * toFraction
+ * toHexadecimal toHex
+ * toNearest
+ * toNumber
+ * toOctal
+ * toPower pow
+ * toPrecision
+ * toSignificantDigits toSD
+ * toString
+ * truncated trunc
+ * valueOf toJSON
+ */
+
+
+ /*
+ * Return a new Decimal whose value is the absolute value of this Decimal.
+ *
+ */
+ P.absoluteValue = P.abs = function () {
+ var x = new this.constructor(this);
+ if (x.s < 0) x.s = 1;
+ return finalise(x);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a whole number in the
+ * direction of positive Infinity.
+ *
+ */
+ P.ceil = function () {
+ return finalise(new this.constructor(this), this.e + 1, 2);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal clamped to the range
+ * delineated by `min` and `max`.
+ *
+ * min {number|string|bigint|Decimal}
+ * max {number|string|bigint|Decimal}
+ *
+ */
+ P.clampedTo = P.clamp = function (min, max) {
+ var k,
+ x = this,
+ Ctor = x.constructor;
+ min = new Ctor(min);
+ max = new Ctor(max);
+ if (!min.s || !max.s) return new Ctor(NaN);
+ if (min.gt(max)) throw Error(invalidArgument + max);
+ k = x.cmp(min);
+ return k < 0 ? min : x.cmp(max) > 0 ? max : new Ctor(x);
+ };
+
+
+ /*
+ * Return
+ * 1 if the value of this Decimal is greater than the value of `y`,
+ * -1 if the value of this Decimal is less than the value of `y`,
+ * 0 if they have the same value,
+ * NaN if the value of either Decimal is NaN.
+ *
+ */
+ P.comparedTo = P.cmp = function (y) {
+ var i, j, xdL, ydL,
+ x = this,
+ xd = x.d,
+ yd = (y = new x.constructor(y)).d,
+ xs = x.s,
+ ys = y.s;
+
+ // Either NaN or ±Infinity?
+ if (!xd || !yd) {
+ return !xs || !ys ? NaN : xs !== ys ? xs : xd === yd ? 0 : !xd ^ xs < 0 ? 1 : -1;
+ }
+
+ // Either zero?
+ if (!xd[0] || !yd[0]) return xd[0] ? xs : yd[0] ? -ys : 0;
+
+ // Signs differ?
+ if (xs !== ys) return xs;
+
+ // Compare exponents.
+ if (x.e !== y.e) return x.e > y.e ^ xs < 0 ? 1 : -1;
+
+ xdL = xd.length;
+ ydL = yd.length;
+
+ // Compare digit by digit.
+ for (i = 0, j = xdL < ydL ? xdL : ydL; i < j; ++i) {
+ if (xd[i] !== yd[i]) return xd[i] > yd[i] ^ xs < 0 ? 1 : -1;
+ }
+
+ // Compare lengths.
+ return xdL === ydL ? 0 : xdL > ydL ^ xs < 0 ? 1 : -1;
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the cosine of the value in radians of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-1, 1]
+ *
+ * cos(0) = 1
+ * cos(-0) = 1
+ * cos(Infinity) = NaN
+ * cos(-Infinity) = NaN
+ * cos(NaN) = NaN
+ *
+ */
+ P.cosine = P.cos = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.d) return new Ctor(NaN);
+
+ // cos(0) = cos(-0) = 1
+ if (!x.d[0]) return new Ctor(1);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(x.e, x.sd()) + LOG_BASE;
+ Ctor.rounding = 1;
+
+ x = cosine(Ctor, toLessThanHalfPi(Ctor, x));
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return finalise(quadrant == 2 || quadrant == 3 ? x.neg() : x, pr, rm, true);
+ };
+
+
+ /*
+ *
+ * Return a new Decimal whose value is the cube root of the value of this Decimal, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * cbrt(0) = 0
+ * cbrt(-0) = -0
+ * cbrt(1) = 1
+ * cbrt(-1) = -1
+ * cbrt(N) = N
+ * cbrt(-I) = -I
+ * cbrt(I) = I
+ *
+ * Math.cbrt(x) = (x < 0 ? -Math.pow(-x, 1/3) : Math.pow(x, 1/3))
+ *
+ */
+ P.cubeRoot = P.cbrt = function () {
+ var e, m, n, r, rep, s, sd, t, t3, t3plusx,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite() || x.isZero()) return new Ctor(x);
+ external = false;
+
+ // Initial estimate.
+ s = x.s * mathpow(x.s * x, 1 / 3);
+
+ // Math.cbrt underflow/overflow?
+ // Pass x to Math.pow as integer, then adjust the exponent of the result.
+ if (!s || Math.abs(s) == 1 / 0) {
+ n = digitsToString(x.d);
+ e = x.e;
+
+ // Adjust n exponent so it is a multiple of 3 away from x exponent.
+ if (s = (e - n.length + 1) % 3) n += (s == 1 || s == -2 ? '0' : '00');
+ s = mathpow(n, 1 / 3);
+
+ // Rarely, e may be one less than the result exponent value.
+ e = mathfloor((e + 1) / 3) - (e % 3 == (e < 0 ? -1 : 2));
+
+ if (s == 1 / 0) {
+ n = '5e' + e;
+ } else {
+ n = s.toExponential();
+ n = n.slice(0, n.indexOf('e') + 1) + e;
+ }
+
+ r = new Ctor(n);
+ r.s = x.s;
+ } else {
+ r = new Ctor(s.toString());
+ }
+
+ sd = (e = Ctor.precision) + 3;
+
+ // Halley's method.
+ // TODO? Compare Newton's method.
+ for (;;) {
+ t = r;
+ t3 = t.times(t).times(t);
+ t3plusx = t3.plus(x);
+ r = divide(t3plusx.plus(x).times(t), t3plusx.plus(t3), sd + 2, 1);
+
+ // TODO? Replace with for-loop and checkRoundingDigits.
+ if (digitsToString(t.d).slice(0, sd) === (n = digitsToString(r.d)).slice(0, sd)) {
+ n = n.slice(sd - 3, sd + 1);
+
+ // The 4th rounding digit may be in error by -1 so if the 4 rounding digits are 9999 or 4999
+ // , i.e. approaching a rounding boundary, continue the iteration.
+ if (n == '9999' || !rep && n == '4999') {
+
+ // On the first iteration only, check to see if rounding up gives the exact result as the
+ // nines may infinitely repeat.
+ if (!rep) {
+ finalise(t, e + 1, 0);
+
+ if (t.times(t).times(t).eq(x)) {
+ r = t;
+ break;
+ }
+ }
+
+ sd += 4;
+ rep = 1;
+ } else {
+
+ // If the rounding digits are null, 0{0,4} or 50{0,3}, check for an exact result.
+ // If not, then there are further digits and m will be truthy.
+ if (!+n || !+n.slice(1) && n.charAt(0) == '5') {
+
+ // Truncate to the first rounding digit.
+ finalise(r, e + 1, 1);
+ m = !r.times(r).times(r).eq(x);
+ }
+
+ break;
+ }
+ }
+ }
+
+ external = true;
+
+ return finalise(r, e, Ctor.rounding, m);
+ };
+
+
+ /*
+ * Return the number of decimal places of the value of this Decimal.
+ *
+ */
+ P.decimalPlaces = P.dp = function () {
+ var w,
+ d = this.d,
+ n = NaN;
+
+ if (d) {
+ w = d.length - 1;
+ n = (w - mathfloor(this.e / LOG_BASE)) * LOG_BASE;
+
+ // Subtract the number of trailing zeros of the last word.
+ w = d[w];
+ if (w) for (; w % 10 == 0; w /= 10) n--;
+ if (n < 0) n = 0;
+ }
+
+ return n;
+ };
+
+
+ /*
+ * n / 0 = I
+ * n / N = N
+ * n / I = 0
+ * 0 / n = 0
+ * 0 / 0 = N
+ * 0 / N = N
+ * 0 / I = 0
+ * N / n = N
+ * N / 0 = N
+ * N / N = N
+ * N / I = N
+ * I / n = I
+ * I / 0 = I
+ * I / N = N
+ * I / I = N
+ *
+ * Return a new Decimal whose value is the value of this Decimal divided by `y`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ */
+ P.dividedBy = P.div = function (y) {
+ return divide(this, new this.constructor(y));
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the integer part of dividing the value of this Decimal
+ * by the value of `y`, rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ */
+ P.dividedToIntegerBy = P.divToInt = function (y) {
+ var x = this,
+ Ctor = x.constructor;
+ return finalise(divide(x, new Ctor(y), 0, 1, 1), Ctor.precision, Ctor.rounding);
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is equal to the value of `y`, otherwise return false.
+ *
+ */
+ P.equals = P.eq = function (y) {
+ return this.cmp(y) === 0;
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a whole number in the
+ * direction of negative Infinity.
+ *
+ */
+ P.floor = function () {
+ return finalise(new this.constructor(this), this.e + 1, 3);
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is greater than the value of `y`, otherwise return
+ * false.
+ *
+ */
+ P.greaterThan = P.gt = function (y) {
+ return this.cmp(y) > 0;
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is greater than or equal to the value of `y`,
+ * otherwise return false.
+ *
+ */
+ P.greaterThanOrEqualTo = P.gte = function (y) {
+ var k = this.cmp(y);
+ return k == 1 || k === 0;
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the hyperbolic cosine of the value in radians of this
+ * Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [1, Infinity]
+ *
+ * cosh(x) = 1 + x^2/2! + x^4/4! + x^6/6! + ...
+ *
+ * cosh(0) = 1
+ * cosh(-0) = 1
+ * cosh(Infinity) = Infinity
+ * cosh(-Infinity) = Infinity
+ * cosh(NaN) = NaN
+ *
+ * x time taken (ms) result
+ * 1000 9 9.8503555700852349694e+433
+ * 10000 25 4.4034091128314607936e+4342
+ * 100000 171 1.4033316802130615897e+43429
+ * 1000000 3817 1.5166076984010437725e+434294
+ * 10000000 abandoned after 2 minute wait
+ *
+ * TODO? Compare performance of cosh(x) = 0.5 * (exp(x) + exp(-x))
+ *
+ */
+ P.hyperbolicCosine = P.cosh = function () {
+ var k, n, pr, rm, len,
+ x = this,
+ Ctor = x.constructor,
+ one = new Ctor(1);
+
+ if (!x.isFinite()) return new Ctor(x.s ? 1 / 0 : NaN);
+ if (x.isZero()) return one;
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(x.e, x.sd()) + 4;
+ Ctor.rounding = 1;
+ len = x.d.length;
+
+ // Argument reduction: cos(4x) = 1 - 8cos^2(x) + 8cos^4(x) + 1
+ // i.e. cos(x) = 1 - cos^2(x/4)(8 - 8cos^2(x/4))
+
+ // Estimate the optimum number of times to use the argument reduction.
+ // TODO? Estimation reused from cosine() and may not be optimal here.
+ if (len < 32) {
+ k = Math.ceil(len / 3);
+ n = (1 / tinyPow(4, k)).toString();
+ } else {
+ k = 16;
+ n = '2.3283064365386962890625e-10';
+ }
+
+ x = taylorSeries(Ctor, 1, x.times(n), new Ctor(1), true);
+
+ // Reverse argument reduction
+ var cosh2_x,
+ i = k,
+ d8 = new Ctor(8);
+ for (; i--;) {
+ cosh2_x = x.times(x);
+ x = one.minus(cosh2_x.times(d8.minus(cosh2_x.times(d8))));
+ }
+
+ return finalise(x, Ctor.precision = pr, Ctor.rounding = rm, true);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the hyperbolic sine of the value in radians of this
+ * Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-Infinity, Infinity]
+ *
+ * sinh(x) = x + x^3/3! + x^5/5! + x^7/7! + ...
+ *
+ * sinh(0) = 0
+ * sinh(-0) = -0
+ * sinh(Infinity) = Infinity
+ * sinh(-Infinity) = -Infinity
+ * sinh(NaN) = NaN
+ *
+ * x time taken (ms)
+ * 10 2 ms
+ * 100 5 ms
+ * 1000 14 ms
+ * 10000 82 ms
+ * 100000 886 ms 1.4033316802130615897e+43429
+ * 200000 2613 ms
+ * 300000 5407 ms
+ * 400000 8824 ms
+ * 500000 13026 ms 8.7080643612718084129e+217146
+ * 1000000 48543 ms
+ *
+ * TODO? Compare performance of sinh(x) = 0.5 * (exp(x) - exp(-x))
+ *
+ */
+ P.hyperbolicSine = P.sinh = function () {
+ var k, pr, rm, len,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite() || x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(x.e, x.sd()) + 4;
+ Ctor.rounding = 1;
+ len = x.d.length;
+
+ if (len < 3) {
+ x = taylorSeries(Ctor, 2, x, x, true);
+ } else {
+
+ // Alternative argument reduction: sinh(3x) = sinh(x)(3 + 4sinh^2(x))
+ // i.e. sinh(x) = sinh(x/3)(3 + 4sinh^2(x/3))
+ // 3 multiplications and 1 addition
+
+ // Argument reduction: sinh(5x) = sinh(x)(5 + sinh^2(x)(20 + 16sinh^2(x)))
+ // i.e. sinh(x) = sinh(x/5)(5 + sinh^2(x/5)(20 + 16sinh^2(x/5)))
+ // 4 multiplications and 2 additions
+
+ // Estimate the optimum number of times to use the argument reduction.
+ k = 1.4 * Math.sqrt(len);
+ k = k > 16 ? 16 : k | 0;
+
+ x = x.times(1 / tinyPow(5, k));
+ x = taylorSeries(Ctor, 2, x, x, true);
+
+ // Reverse argument reduction
+ var sinh2_x,
+ d5 = new Ctor(5),
+ d16 = new Ctor(16),
+ d20 = new Ctor(20);
+ for (; k--;) {
+ sinh2_x = x.times(x);
+ x = x.times(d5.plus(sinh2_x.times(d16.times(sinh2_x).plus(d20))));
+ }
+ }
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return finalise(x, pr, rm, true);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the hyperbolic tangent of the value in radians of this
+ * Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-1, 1]
+ *
+ * tanh(x) = sinh(x) / cosh(x)
+ *
+ * tanh(0) = 0
+ * tanh(-0) = -0
+ * tanh(Infinity) = 1
+ * tanh(-Infinity) = -1
+ * tanh(NaN) = NaN
+ *
+ */
+ P.hyperbolicTangent = P.tanh = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite()) return new Ctor(x.s);
+ if (x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + 7;
+ Ctor.rounding = 1;
+
+ return divide(x.sinh(), x.cosh(), Ctor.precision = pr, Ctor.rounding = rm);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the arccosine (inverse cosine) in radians of the value of
+ * this Decimal.
+ *
+ * Domain: [-1, 1]
+ * Range: [0, pi]
+ *
+ * acos(x) = pi/2 - asin(x)
+ *
+ * acos(0) = pi/2
+ * acos(-0) = pi/2
+ * acos(1) = 0
+ * acos(-1) = pi
+ * acos(1/2) = pi/3
+ * acos(-1/2) = 2*pi/3
+ * acos(|x| > 1) = NaN
+ * acos(NaN) = NaN
+ *
+ */
+ P.inverseCosine = P.acos = function () {
+ var x = this,
+ Ctor = x.constructor,
+ k = x.abs().cmp(1),
+ pr = Ctor.precision,
+ rm = Ctor.rounding;
+
+ if (k !== -1) {
+ return k === 0
+ // |x| is 1
+ ? x.isNeg() ? getPi(Ctor, pr, rm) : new Ctor(0)
+ // |x| > 1 or x is NaN
+ : new Ctor(NaN);
+ }
+
+ if (x.isZero()) return getPi(Ctor, pr + 4, rm).times(0.5);
+
+ // TODO? Special case acos(0.5) = pi/3 and acos(-0.5) = 2*pi/3
+
+ Ctor.precision = pr + 6;
+ Ctor.rounding = 1;
+
+ // See https://github.com/MikeMcl/decimal.js/pull/217
+ x = new Ctor(1).minus(x).div(x.plus(1)).sqrt().atan();
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.times(2);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the inverse of the hyperbolic cosine in radians of the
+ * value of this Decimal.
+ *
+ * Domain: [1, Infinity]
+ * Range: [0, Infinity]
+ *
+ * acosh(x) = ln(x + sqrt(x^2 - 1))
+ *
+ * acosh(x < 1) = NaN
+ * acosh(NaN) = NaN
+ * acosh(Infinity) = Infinity
+ * acosh(-Infinity) = NaN
+ * acosh(0) = NaN
+ * acosh(-0) = NaN
+ * acosh(1) = 0
+ * acosh(-1) = NaN
+ *
+ */
+ P.inverseHyperbolicCosine = P.acosh = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (x.lte(1)) return new Ctor(x.eq(1) ? 0 : NaN);
+ if (!x.isFinite()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(Math.abs(x.e), x.sd()) + 4;
+ Ctor.rounding = 1;
+ external = false;
+
+ x = x.times(x).minus(1).sqrt().plus(x);
+
+ external = true;
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.ln();
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the inverse of the hyperbolic sine in radians of the value
+ * of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-Infinity, Infinity]
+ *
+ * asinh(x) = ln(x + sqrt(x^2 + 1))
+ *
+ * asinh(NaN) = NaN
+ * asinh(Infinity) = Infinity
+ * asinh(-Infinity) = -Infinity
+ * asinh(0) = 0
+ * asinh(-0) = -0
+ *
+ */
+ P.inverseHyperbolicSine = P.asinh = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite() || x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + 2 * Math.max(Math.abs(x.e), x.sd()) + 6;
+ Ctor.rounding = 1;
+ external = false;
+
+ x = x.times(x).plus(1).sqrt().plus(x);
+
+ external = true;
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.ln();
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the inverse of the hyperbolic tangent in radians of the
+ * value of this Decimal.
+ *
+ * Domain: [-1, 1]
+ * Range: [-Infinity, Infinity]
+ *
+ * atanh(x) = 0.5 * ln((1 + x) / (1 - x))
+ *
+ * atanh(|x| > 1) = NaN
+ * atanh(NaN) = NaN
+ * atanh(Infinity) = NaN
+ * atanh(-Infinity) = NaN
+ * atanh(0) = 0
+ * atanh(-0) = -0
+ * atanh(1) = Infinity
+ * atanh(-1) = -Infinity
+ *
+ */
+ P.inverseHyperbolicTangent = P.atanh = function () {
+ var pr, rm, wpr, xsd,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite()) return new Ctor(NaN);
+ if (x.e >= 0) return new Ctor(x.abs().eq(1) ? x.s / 0 : x.isZero() ? x : NaN);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ xsd = x.sd();
+
+ if (Math.max(xsd, pr) < 2 * -x.e - 1) return finalise(new Ctor(x), pr, rm, true);
+
+ Ctor.precision = wpr = xsd - x.e;
+
+ x = divide(x.plus(1), new Ctor(1).minus(x), wpr + pr, 1);
+
+ Ctor.precision = pr + 4;
+ Ctor.rounding = 1;
+
+ x = x.ln();
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.times(0.5);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the arcsine (inverse sine) in radians of the value of this
+ * Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-pi/2, pi/2]
+ *
+ * asin(x) = 2*atan(x/(1 + sqrt(1 - x^2)))
+ *
+ * asin(0) = 0
+ * asin(-0) = -0
+ * asin(1/2) = pi/6
+ * asin(-1/2) = -pi/6
+ * asin(1) = pi/2
+ * asin(-1) = -pi/2
+ * asin(|x| > 1) = NaN
+ * asin(NaN) = NaN
+ *
+ * TODO? Compare performance of Taylor series.
+ *
+ */
+ P.inverseSine = P.asin = function () {
+ var halfPi, k,
+ pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (x.isZero()) return new Ctor(x);
+
+ k = x.abs().cmp(1);
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+
+ if (k !== -1) {
+
+ // |x| is 1
+ if (k === 0) {
+ halfPi = getPi(Ctor, pr + 4, rm).times(0.5);
+ halfPi.s = x.s;
+ return halfPi;
+ }
+
+ // |x| > 1 or x is NaN
+ return new Ctor(NaN);
+ }
+
+ // TODO? Special case asin(1/2) = pi/6 and asin(-1/2) = -pi/6
+
+ Ctor.precision = pr + 6;
+ Ctor.rounding = 1;
+
+ x = x.div(new Ctor(1).minus(x.times(x)).sqrt().plus(1)).atan();
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.times(2);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the arctangent (inverse tangent) in radians of the value
+ * of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-pi/2, pi/2]
+ *
+ * atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ...
+ *
+ * atan(0) = 0
+ * atan(-0) = -0
+ * atan(1) = pi/4
+ * atan(-1) = -pi/4
+ * atan(Infinity) = pi/2
+ * atan(-Infinity) = -pi/2
+ * atan(NaN) = NaN
+ *
+ */
+ P.inverseTangent = P.atan = function () {
+ var i, j, k, n, px, t, r, wpr, x2,
+ x = this,
+ Ctor = x.constructor,
+ pr = Ctor.precision,
+ rm = Ctor.rounding;
+
+ if (!x.isFinite()) {
+ if (!x.s) return new Ctor(NaN);
+ if (pr + 4 <= PI_PRECISION) {
+ r = getPi(Ctor, pr + 4, rm).times(0.5);
+ r.s = x.s;
+ return r;
+ }
+ } else if (x.isZero()) {
+ return new Ctor(x);
+ } else if (x.abs().eq(1) && pr + 4 <= PI_PRECISION) {
+ r = getPi(Ctor, pr + 4, rm).times(0.25);
+ r.s = x.s;
+ return r;
+ }
+
+ Ctor.precision = wpr = pr + 10;
+ Ctor.rounding = 1;
+
+ // TODO? if (x >= 1 && pr <= PI_PRECISION) atan(x) = halfPi * x.s - atan(1 / x);
+
+ // Argument reduction
+ // Ensure |x| < 0.42
+ // atan(x) = 2 * atan(x / (1 + sqrt(1 + x^2)))
+
+ k = Math.min(28, wpr / LOG_BASE + 2 | 0);
+
+ for (i = k; i; --i) x = x.div(x.times(x).plus(1).sqrt().plus(1));
+
+ external = false;
+
+ j = Math.ceil(wpr / LOG_BASE);
+ n = 1;
+ x2 = x.times(x);
+ r = new Ctor(x);
+ px = x;
+
+ // atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ...
+ for (; i !== -1;) {
+ px = px.times(x2);
+ t = r.minus(px.div(n += 2));
+
+ px = px.times(x2);
+ r = t.plus(px.div(n += 2));
+
+ if (r.d[j] !== void 0) for (i = j; r.d[i] === t.d[i] && i--;);
+ }
+
+ if (k) r = r.times(2 << (k - 1));
+
+ external = true;
+
+ return finalise(r, Ctor.precision = pr, Ctor.rounding = rm, true);
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is a finite number, otherwise return false.
+ *
+ */
+ P.isFinite = function () {
+ return !!this.d;
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is an integer, otherwise return false.
+ *
+ */
+ P.isInteger = P.isInt = function () {
+ return !!this.d && mathfloor(this.e / LOG_BASE) > this.d.length - 2;
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is NaN, otherwise return false.
+ *
+ */
+ P.isNaN = function () {
+ return !this.s;
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is negative, otherwise return false.
+ *
+ */
+ P.isNegative = P.isNeg = function () {
+ return this.s < 0;
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is positive, otherwise return false.
+ *
+ */
+ P.isPositive = P.isPos = function () {
+ return this.s > 0;
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is 0 or -0, otherwise return false.
+ *
+ */
+ P.isZero = function () {
+ return !!this.d && this.d[0] === 0;
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is less than `y`, otherwise return false.
+ *
+ */
+ P.lessThan = P.lt = function (y) {
+ return this.cmp(y) < 0;
+ };
+
+
+ /*
+ * Return true if the value of this Decimal is less than or equal to `y`, otherwise return false.
+ *
+ */
+ P.lessThanOrEqualTo = P.lte = function (y) {
+ return this.cmp(y) < 1;
+ };
+
+
+ /*
+ * Return the logarithm of the value of this Decimal to the specified base, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * If no base is specified, return log[10](arg).
+ *
+ * log[base](arg) = ln(arg) / ln(base)
+ *
+ * The result will always be correctly rounded if the base of the log is 10, and 'almost always'
+ * otherwise:
+ *
+ * Depending on the rounding mode, the result may be incorrectly rounded if the first fifteen
+ * rounding digits are [49]99999999999999 or [50]00000000000000. In that case, the maximum error
+ * between the result and the correctly rounded result will be one ulp (unit in the last place).
+ *
+ * log[-b](a) = NaN
+ * log[0](a) = NaN
+ * log[1](a) = NaN
+ * log[NaN](a) = NaN
+ * log[Infinity](a) = NaN
+ * log[b](0) = -Infinity
+ * log[b](-0) = -Infinity
+ * log[b](-a) = NaN
+ * log[b](1) = 0
+ * log[b](Infinity) = Infinity
+ * log[b](NaN) = NaN
+ *
+ * [base] {number|string|bigint|Decimal} The base of the logarithm.
+ *
+ */
+ P.logarithm = P.log = function (base) {
+ var isBase10, d, denominator, k, inf, num, sd, r,
+ arg = this,
+ Ctor = arg.constructor,
+ pr = Ctor.precision,
+ rm = Ctor.rounding,
+ guard = 5;
+
+ // Default base is 10.
+ if (base == null) {
+ base = new Ctor(10);
+ isBase10 = true;
+ } else {
+ base = new Ctor(base);
+ d = base.d;
+
+ // Return NaN if base is negative, or non-finite, or is 0 or 1.
+ if (base.s < 0 || !d || !d[0] || base.eq(1)) return new Ctor(NaN);
+
+ isBase10 = base.eq(10);
+ }
+
+ d = arg.d;
+
+ // Is arg negative, non-finite, 0 or 1?
+ if (arg.s < 0 || !d || !d[0] || arg.eq(1)) {
+ return new Ctor(d && !d[0] ? -1 / 0 : arg.s != 1 ? NaN : d ? 0 : 1 / 0);
+ }
+
+ // The result will have a non-terminating decimal expansion if base is 10 and arg is not an
+ // integer power of 10.
+ if (isBase10) {
+ if (d.length > 1) {
+ inf = true;
+ } else {
+ for (k = d[0]; k % 10 === 0;) k /= 10;
+ inf = k !== 1;
+ }
+ }
+
+ external = false;
+ sd = pr + guard;
+ num = naturalLogarithm(arg, sd);
+ denominator = isBase10 ? getLn10(Ctor, sd + 10) : naturalLogarithm(base, sd);
+
+ // The result will have 5 rounding digits.
+ r = divide(num, denominator, sd, 1);
+
+ // If at a rounding boundary, i.e. the result's rounding digits are [49]9999 or [50]0000,
+ // calculate 10 further digits.
+ //
+ // If the result is known to have an infinite decimal expansion, repeat this until it is clear
+ // that the result is above or below the boundary. Otherwise, if after calculating the 10
+ // further digits, the last 14 are nines, round up and assume the result is exact.
+ // Also assume the result is exact if the last 14 are zero.
+ //
+ // Example of a result that will be incorrectly rounded:
+ // log[1048576](4503599627370502) = 2.60000000000000009610279511444746...
+ // The above result correctly rounded using ROUND_CEIL to 1 decimal place should be 2.7, but it
+ // will be given as 2.6 as there are 15 zeros immediately after the requested decimal place, so
+ // the exact result would be assumed to be 2.6, which rounded using ROUND_CEIL to 1 decimal
+ // place is still 2.6.
+ if (checkRoundingDigits(r.d, k = pr, rm)) {
+
+ do {
+ sd += 10;
+ num = naturalLogarithm(arg, sd);
+ denominator = isBase10 ? getLn10(Ctor, sd + 10) : naturalLogarithm(base, sd);
+ r = divide(num, denominator, sd, 1);
+
+ if (!inf) {
+
+ // Check for 14 nines from the 2nd rounding digit, as the first may be 4.
+ if (+digitsToString(r.d).slice(k + 1, k + 15) + 1 == 1e14) {
+ r = finalise(r, pr + 1, 0);
+ }
+
+ break;
+ }
+ } while (checkRoundingDigits(r.d, k += 10, rm));
+ }
+
+ external = true;
+
+ return finalise(r, pr, rm);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the maximum of the arguments and the value of this Decimal.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ P.max = function () {
+ Array.prototype.push.call(arguments, this);
+ return maxOrMin(this.constructor, arguments, -1);
+ };
+ */
+
+
+ /*
+ * Return a new Decimal whose value is the minimum of the arguments and the value of this Decimal.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ P.min = function () {
+ Array.prototype.push.call(arguments, this);
+ return maxOrMin(this.constructor, arguments, 1);
+ };
+ */
+
+
+ /*
+ * n - 0 = n
+ * n - N = N
+ * n - I = -I
+ * 0 - n = -n
+ * 0 - 0 = 0
+ * 0 - N = N
+ * 0 - I = -I
+ * N - n = N
+ * N - 0 = N
+ * N - N = N
+ * N - I = N
+ * I - n = I
+ * I - 0 = I
+ * I - N = N
+ * I - I = N
+ *
+ * Return a new Decimal whose value is the value of this Decimal minus `y`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ */
+ P.minus = P.sub = function (y) {
+ var d, e, i, j, k, len, pr, rm, xd, xe, xLTy, yd,
+ x = this,
+ Ctor = x.constructor;
+
+ y = new Ctor(y);
+
+ // If either is not finite...
+ if (!x.d || !y.d) {
+
+ // Return NaN if either is NaN.
+ if (!x.s || !y.s) y = new Ctor(NaN);
+
+ // Return y negated if x is finite and y is ±Infinity.
+ else if (x.d) y.s = -y.s;
+
+ // Return x if y is finite and x is ±Infinity.
+ // Return x if both are ±Infinity with different signs.
+ // Return NaN if both are ±Infinity with the same sign.
+ else y = new Ctor(y.d || x.s !== y.s ? x : NaN);
+
+ return y;
+ }
+
+ // If signs differ...
+ if (x.s != y.s) {
+ y.s = -y.s;
+ return x.plus(y);
+ }
+
+ xd = x.d;
+ yd = y.d;
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+
+ // If either is zero...
+ if (!xd[0] || !yd[0]) {
+
+ // Return y negated if x is zero and y is non-zero.
+ if (yd[0]) y.s = -y.s;
+
+ // Return x if y is zero and x is non-zero.
+ else if (xd[0]) y = new Ctor(x);
+
+ // Return zero if both are zero.
+ // From IEEE 754 (2008) 6.3: 0 - 0 = -0 - -0 = -0 when rounding to -Infinity.
+ else return new Ctor(rm === 3 ? -0 : 0);
+
+ return external ? finalise(y, pr, rm) : y;
+ }
+
+ // x and y are finite, non-zero numbers with the same sign.
+
+ // Calculate base 1e7 exponents.
+ e = mathfloor(y.e / LOG_BASE);
+ xe = mathfloor(x.e / LOG_BASE);
+
+ xd = xd.slice();
+ k = xe - e;
+
+ // If base 1e7 exponents differ...
+ if (k) {
+ xLTy = k < 0;
+
+ if (xLTy) {
+ d = xd;
+ k = -k;
+ len = yd.length;
+ } else {
+ d = yd;
+ e = xe;
+ len = xd.length;
+ }
+
+ // Numbers with massively different exponents would result in a very high number of
+ // zeros needing to be prepended, but this can be avoided while still ensuring correct
+ // rounding by limiting the number of zeros to `Math.ceil(pr / LOG_BASE) + 2`.
+ i = Math.max(Math.ceil(pr / LOG_BASE), len) + 2;
+
+ if (k > i) {
+ k = i;
+ d.length = 1;
+ }
+
+ // Prepend zeros to equalise exponents.
+ d.reverse();
+ for (i = k; i--;) d.push(0);
+ d.reverse();
+
+ // Base 1e7 exponents equal.
+ } else {
+
+ // Check digits to determine which is the bigger number.
+
+ i = xd.length;
+ len = yd.length;
+ xLTy = i < len;
+ if (xLTy) len = i;
+
+ for (i = 0; i < len; i++) {
+ if (xd[i] != yd[i]) {
+ xLTy = xd[i] < yd[i];
+ break;
+ }
+ }
+
+ k = 0;
+ }
+
+ if (xLTy) {
+ d = xd;
+ xd = yd;
+ yd = d;
+ y.s = -y.s;
+ }
+
+ len = xd.length;
+
+ // Append zeros to `xd` if shorter.
+ // Don't add zeros to `yd` if shorter as subtraction only needs to start at `yd` length.
+ for (i = yd.length - len; i > 0; --i) xd[len++] = 0;
+
+ // Subtract yd from xd.
+ for (i = yd.length; i > k;) {
+
+ if (xd[--i] < yd[i]) {
+ for (j = i; j && xd[--j] === 0;) xd[j] = BASE - 1;
+ --xd[j];
+ xd[i] += BASE;
+ }
+
+ xd[i] -= yd[i];
+ }
+
+ // Remove trailing zeros.
+ for (; xd[--len] === 0;) xd.pop();
+
+ // Remove leading zeros and adjust exponent accordingly.
+ for (; xd[0] === 0; xd.shift()) --e;
+
+ // Zero?
+ if (!xd[0]) return new Ctor(rm === 3 ? -0 : 0);
+
+ y.d = xd;
+ y.e = getBase10Exponent(xd, e);
+
+ return external ? finalise(y, pr, rm) : y;
+ };
+
+
+ /*
+ * n % 0 = N
+ * n % N = N
+ * n % I = n
+ * 0 % n = 0
+ * -0 % n = -0
+ * 0 % 0 = N
+ * 0 % N = N
+ * 0 % I = 0
+ * N % n = N
+ * N % 0 = N
+ * N % N = N
+ * N % I = N
+ * I % n = N
+ * I % 0 = N
+ * I % N = N
+ * I % I = N
+ *
+ * Return a new Decimal whose value is the value of this Decimal modulo `y`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * The result depends on the modulo mode.
+ *
+ */
+ P.modulo = P.mod = function (y) {
+ var q,
+ x = this,
+ Ctor = x.constructor;
+
+ y = new Ctor(y);
+
+ // Return NaN if x is ±Infinity or NaN, or y is NaN or ±0.
+ if (!x.d || !y.s || y.d && !y.d[0]) return new Ctor(NaN);
+
+ // Return x if y is ±Infinity or x is ±0.
+ if (!y.d || x.d && !x.d[0]) {
+ return finalise(new Ctor(x), Ctor.precision, Ctor.rounding);
+ }
+
+ // Prevent rounding of intermediate calculations.
+ external = false;
+
+ if (Ctor.modulo == 9) {
+
+ // Euclidian division: q = sign(y) * floor(x / abs(y))
+ // result = x - q * y where 0 <= result < abs(y)
+ q = divide(x, y.abs(), 0, 3, 1);
+ q.s *= y.s;
+ } else {
+ q = divide(x, y, 0, Ctor.modulo, 1);
+ }
+
+ q = q.times(y);
+
+ external = true;
+
+ return x.minus(q);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the natural exponential of the value of this Decimal,
+ * i.e. the base e raised to the power the value of this Decimal, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ */
+ P.naturalExponential = P.exp = function () {
+ return naturalExponential(this);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the natural logarithm of the value of this Decimal,
+ * rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ */
+ P.naturalLogarithm = P.ln = function () {
+ return naturalLogarithm(this);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal negated, i.e. as if multiplied by
+ * -1.
+ *
+ */
+ P.negated = P.neg = function () {
+ var x = new this.constructor(this);
+ x.s = -x.s;
+ return finalise(x);
+ };
+
+
+ /*
+ * n + 0 = n
+ * n + N = N
+ * n + I = I
+ * 0 + n = n
+ * 0 + 0 = 0
+ * 0 + N = N
+ * 0 + I = I
+ * N + n = N
+ * N + 0 = N
+ * N + N = N
+ * N + I = N
+ * I + n = I
+ * I + 0 = I
+ * I + N = N
+ * I + I = I
+ *
+ * Return a new Decimal whose value is the value of this Decimal plus `y`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ */
+ P.plus = P.add = function (y) {
+ var carry, d, e, i, k, len, pr, rm, xd, yd,
+ x = this,
+ Ctor = x.constructor;
+
+ y = new Ctor(y);
+
+ // If either is not finite...
+ if (!x.d || !y.d) {
+
+ // Return NaN if either is NaN.
+ if (!x.s || !y.s) y = new Ctor(NaN);
+
+ // Return x if y is finite and x is ±Infinity.
+ // Return x if both are ±Infinity with the same sign.
+ // Return NaN if both are ±Infinity with different signs.
+ // Return y if x is finite and y is ±Infinity.
+ else if (!x.d) y = new Ctor(y.d || x.s === y.s ? x : NaN);
+
+ return y;
+ }
+
+ // If signs differ...
+ if (x.s != y.s) {
+ y.s = -y.s;
+ return x.minus(y);
+ }
+
+ xd = x.d;
+ yd = y.d;
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+
+ // If either is zero...
+ if (!xd[0] || !yd[0]) {
+
+ // Return x if y is zero.
+ // Return y if y is non-zero.
+ if (!yd[0]) y = new Ctor(x);
+
+ return external ? finalise(y, pr, rm) : y;
+ }
+
+ // x and y are finite, non-zero numbers with the same sign.
+
+ // Calculate base 1e7 exponents.
+ k = mathfloor(x.e / LOG_BASE);
+ e = mathfloor(y.e / LOG_BASE);
+
+ xd = xd.slice();
+ i = k - e;
+
+ // If base 1e7 exponents differ...
+ if (i) {
+
+ if (i < 0) {
+ d = xd;
+ i = -i;
+ len = yd.length;
+ } else {
+ d = yd;
+ e = k;
+ len = xd.length;
+ }
+
+ // Limit number of zeros prepended to max(ceil(pr / LOG_BASE), len) + 1.
+ k = Math.ceil(pr / LOG_BASE);
+ len = k > len ? k + 1 : len + 1;
+
+ if (i > len) {
+ i = len;
+ d.length = 1;
+ }
+
+ // Prepend zeros to equalise exponents. Note: Faster to use reverse then do unshifts.
+ d.reverse();
+ for (; i--;) d.push(0);
+ d.reverse();
+ }
+
+ len = xd.length;
+ i = yd.length;
+
+ // If yd is longer than xd, swap xd and yd so xd points to the longer array.
+ if (len - i < 0) {
+ i = len;
+ d = yd;
+ yd = xd;
+ xd = d;
+ }
+
+ // Only start adding at yd.length - 1 as the further digits of xd can be left as they are.
+ for (carry = 0; i;) {
+ carry = (xd[--i] = xd[i] + yd[i] + carry) / BASE | 0;
+ xd[i] %= BASE;
+ }
+
+ if (carry) {
+ xd.unshift(carry);
+ ++e;
+ }
+
+ // Remove trailing zeros.
+ // No need to check for zero, as +x + +y != 0 && -x + -y != 0
+ for (len = xd.length; xd[--len] == 0;) xd.pop();
+
+ y.d = xd;
+ y.e = getBase10Exponent(xd, e);
+
+ return external ? finalise(y, pr, rm) : y;
+ };
+
+
+ /*
+ * Return the number of significant digits of the value of this Decimal.
+ *
+ * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0.
+ *
+ */
+ P.precision = P.sd = function (z) {
+ var k,
+ x = this;
+
+ if (z !== void 0 && z !== !!z && z !== 1 && z !== 0) throw Error(invalidArgument + z);
+
+ if (x.d) {
+ k = getPrecision(x.d);
+ if (z && x.e + 1 > k) k = x.e + 1;
+ } else {
+ k = NaN;
+ }
+
+ return k;
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a whole number using
+ * rounding mode `rounding`.
+ *
+ */
+ P.round = function () {
+ var x = this,
+ Ctor = x.constructor;
+
+ return finalise(new Ctor(x), x.e + 1, Ctor.rounding);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the sine of the value in radians of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-1, 1]
+ *
+ * sin(x) = x - x^3/3! + x^5/5! - ...
+ *
+ * sin(0) = 0
+ * sin(-0) = -0
+ * sin(Infinity) = NaN
+ * sin(-Infinity) = NaN
+ * sin(NaN) = NaN
+ *
+ */
+ P.sine = P.sin = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite()) return new Ctor(NaN);
+ if (x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(x.e, x.sd()) + LOG_BASE;
+ Ctor.rounding = 1;
+
+ x = sine(Ctor, toLessThanHalfPi(Ctor, x));
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return finalise(quadrant > 2 ? x.neg() : x, pr, rm, true);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the square root of this Decimal, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * sqrt(-n) = N
+ * sqrt(N) = N
+ * sqrt(-I) = N
+ * sqrt(I) = I
+ * sqrt(0) = 0
+ * sqrt(-0) = -0
+ *
+ */
+ P.squareRoot = P.sqrt = function () {
+ var m, n, sd, r, rep, t,
+ x = this,
+ d = x.d,
+ e = x.e,
+ s = x.s,
+ Ctor = x.constructor;
+
+ // Negative/NaN/Infinity/zero?
+ if (s !== 1 || !d || !d[0]) {
+ return new Ctor(!s || s < 0 && (!d || d[0]) ? NaN : d ? x : 1 / 0);
+ }
+
+ external = false;
+
+ // Initial estimate.
+ s = Math.sqrt(+x);
+
+ // Math.sqrt underflow/overflow?
+ // Pass x to Math.sqrt as integer, then adjust the exponent of the result.
+ if (s == 0 || s == 1 / 0) {
+ n = digitsToString(d);
+
+ if ((n.length + e) % 2 == 0) n += '0';
+ s = Math.sqrt(n);
+ e = mathfloor((e + 1) / 2) - (e < 0 || e % 2);
+
+ if (s == 1 / 0) {
+ n = '5e' + e;
+ } else {
+ n = s.toExponential();
+ n = n.slice(0, n.indexOf('e') + 1) + e;
+ }
+
+ r = new Ctor(n);
+ } else {
+ r = new Ctor(s.toString());
+ }
+
+ sd = (e = Ctor.precision) + 3;
+
+ // Newton-Raphson iteration.
+ for (;;) {
+ t = r;
+ r = t.plus(divide(x, t, sd + 2, 1)).times(0.5);
+
+ // TODO? Replace with for-loop and checkRoundingDigits.
+ if (digitsToString(t.d).slice(0, sd) === (n = digitsToString(r.d)).slice(0, sd)) {
+ n = n.slice(sd - 3, sd + 1);
+
+ // The 4th rounding digit may be in error by -1 so if the 4 rounding digits are 9999 or
+ // 4999, i.e. approaching a rounding boundary, continue the iteration.
+ if (n == '9999' || !rep && n == '4999') {
+
+ // On the first iteration only, check to see if rounding up gives the exact result as the
+ // nines may infinitely repeat.
+ if (!rep) {
+ finalise(t, e + 1, 0);
+
+ if (t.times(t).eq(x)) {
+ r = t;
+ break;
+ }
+ }
+
+ sd += 4;
+ rep = 1;
+ } else {
+
+ // If the rounding digits are null, 0{0,4} or 50{0,3}, check for an exact result.
+ // If not, then there are further digits and m will be truthy.
+ if (!+n || !+n.slice(1) && n.charAt(0) == '5') {
+
+ // Truncate to the first rounding digit.
+ finalise(r, e + 1, 1);
+ m = !r.times(r).eq(x);
+ }
+
+ break;
+ }
+ }
+ }
+
+ external = true;
+
+ return finalise(r, e, Ctor.rounding, m);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the tangent of the value in radians of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-Infinity, Infinity]
+ *
+ * tan(0) = 0
+ * tan(-0) = -0
+ * tan(Infinity) = NaN
+ * tan(-Infinity) = NaN
+ * tan(NaN) = NaN
+ *
+ */
+ P.tangent = P.tan = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite()) return new Ctor(NaN);
+ if (x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + 10;
+ Ctor.rounding = 1;
+
+ x = x.sin();
+ x.s = 1;
+ x = divide(x, new Ctor(1).minus(x.times(x)).sqrt(), pr + 10, 0);
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return finalise(quadrant == 2 || quadrant == 4 ? x.neg() : x, pr, rm, true);
+ };
+
+
+ /*
+ * n * 0 = 0
+ * n * N = N
+ * n * I = I
+ * 0 * n = 0
+ * 0 * 0 = 0
+ * 0 * N = N
+ * 0 * I = N
+ * N * n = N
+ * N * 0 = N
+ * N * N = N
+ * N * I = N
+ * I * n = I
+ * I * 0 = N
+ * I * N = N
+ * I * I = I
+ *
+ * Return a new Decimal whose value is this Decimal times `y`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ */
+ P.times = P.mul = function (y) {
+ var carry, e, i, k, r, rL, t, xdL, ydL,
+ x = this,
+ Ctor = x.constructor,
+ xd = x.d,
+ yd = (y = new Ctor(y)).d;
+
+ y.s *= x.s;
+
+ // If either is NaN, ±Infinity or ±0...
+ if (!xd || !xd[0] || !yd || !yd[0]) {
+
+ return new Ctor(!y.s || xd && !xd[0] && !yd || yd && !yd[0] && !xd
+
+ // Return NaN if either is NaN.
+ // Return NaN if x is ±0 and y is ±Infinity, or y is ±0 and x is ±Infinity.
+ ? NaN
+
+ // Return ±Infinity if either is ±Infinity.
+ // Return ±0 if either is ±0.
+ : !xd || !yd ? y.s / 0 : y.s * 0);
+ }
+
+ e = mathfloor(x.e / LOG_BASE) + mathfloor(y.e / LOG_BASE);
+ xdL = xd.length;
+ ydL = yd.length;
+
+ // Ensure xd points to the longer array.
+ if (xdL < ydL) {
+ r = xd;
+ xd = yd;
+ yd = r;
+ rL = xdL;
+ xdL = ydL;
+ ydL = rL;
+ }
+
+ // Initialise the result array with zeros.
+ r = [];
+ rL = xdL + ydL;
+ for (i = rL; i--;) r.push(0);
+
+ // Multiply!
+ for (i = ydL; --i >= 0;) {
+ carry = 0;
+ for (k = xdL + i; k > i;) {
+ t = r[k] + yd[i] * xd[k - i - 1] + carry;
+ r[k--] = t % BASE | 0;
+ carry = t / BASE | 0;
+ }
+
+ r[k] = (r[k] + carry) % BASE | 0;
+ }
+
+ // Remove trailing zeros.
+ for (; !r[--rL];) r.pop();
+
+ if (carry) ++e;
+ else r.shift();
+
+ y.d = r;
+ y.e = getBase10Exponent(r, e);
+
+ return external ? finalise(y, Ctor.precision, Ctor.rounding) : y;
+ };
+
+
+ /*
+ * Return a string representing the value of this Decimal in base 2, round to `sd` significant
+ * digits using rounding mode `rm`.
+ *
+ * If the optional `sd` argument is present then return binary exponential notation.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+ P.toBinary = function (sd, rm) {
+ return toStringBinary(this, 2, sd, rm);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a maximum of `dp`
+ * decimal places using rounding mode `rm` or `rounding` if `rm` is omitted.
+ *
+ * If `dp` is omitted, return a new Decimal whose value is the value of this Decimal.
+ *
+ * [dp] {number} Decimal places. Integer, 0 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+ P.toDecimalPlaces = P.toDP = function (dp, rm) {
+ var x = this,
+ Ctor = x.constructor;
+
+ x = new Ctor(x);
+ if (dp === void 0) return x;
+
+ checkInt32(dp, 0, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+
+ return finalise(x, dp + x.e + 1, rm);
+ };
+
+
+ /*
+ * Return a string representing the value of this Decimal in exponential notation rounded to
+ * `dp` fixed decimal places using rounding mode `rounding`.
+ *
+ * [dp] {number} Decimal places. Integer, 0 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+ P.toExponential = function (dp, rm) {
+ var str,
+ x = this,
+ Ctor = x.constructor;
+
+ if (dp === void 0) {
+ str = finiteToString(x, true);
+ } else {
+ checkInt32(dp, 0, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+
+ x = finalise(new Ctor(x), dp + 1, rm);
+ str = finiteToString(x, true, dp + 1);
+ }
+
+ return x.isNeg() && !x.isZero() ? '-' + str : str;
+ };
+
+
+ /*
+ * Return a string representing the value of this Decimal in normal (fixed-point) notation to
+ * `dp` fixed decimal places and rounded using rounding mode `rm` or `rounding` if `rm` is
+ * omitted.
+ *
+ * As with JavaScript numbers, (-0).toFixed(0) is '0', but e.g. (-0.00001).toFixed(0) is '-0'.
+ *
+ * [dp] {number} Decimal places. Integer, 0 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ * (-0).toFixed(0) is '0', but (-0.1).toFixed(0) is '-0'.
+ * (-0).toFixed(1) is '0.0', but (-0.01).toFixed(1) is '-0.0'.
+ * (-0).toFixed(3) is '0.000'.
+ * (-0.5).toFixed(0) is '-0'.
+ *
+ */
+ P.toFixed = function (dp, rm) {
+ var str, y,
+ x = this,
+ Ctor = x.constructor;
+
+ if (dp === void 0) {
+ str = finiteToString(x);
+ } else {
+ checkInt32(dp, 0, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+
+ y = finalise(new Ctor(x), dp + x.e + 1, rm);
+ str = finiteToString(y, false, dp + y.e + 1);
+ }
+
+ // To determine whether to add the minus sign look at the value before it was rounded,
+ // i.e. look at `x` rather than `y`.
+ return x.isNeg() && !x.isZero() ? '-' + str : str;
+ };
+
+
+ /*
+ * Return an array representing the value of this Decimal as a simple fraction with an integer
+ * numerator and an integer denominator.
+ *
+ * The denominator will be a positive non-zero value less than or equal to the specified maximum
+ * denominator. If a maximum denominator is not specified, the denominator will be the lowest
+ * value necessary to represent the number exactly.
+ *
+ * [maxD] {number|string|bigint|Decimal} Maximum denominator. Integer >= 1 and < Infinity.
+ *
+ */
+ P.toFraction = function (maxD) {
+ var d, d0, d1, d2, e, k, n, n0, n1, pr, q, r,
+ x = this,
+ xd = x.d,
+ Ctor = x.constructor;
+
+ if (!xd) return new Ctor(x);
+
+ n1 = d0 = new Ctor(1);
+ d1 = n0 = new Ctor(0);
+
+ d = new Ctor(d1);
+ e = d.e = getPrecision(xd) - x.e - 1;
+ k = e % LOG_BASE;
+ d.d[0] = mathpow(10, k < 0 ? LOG_BASE + k : k);
+
+ if (maxD == null) {
+
+ // d is 10**e, the minimum max-denominator needed.
+ maxD = e > 0 ? d : n1;
+ } else {
+ n = new Ctor(maxD);
+ if (!n.isInt() || n.lt(n1)) throw Error(invalidArgument + n);
+ maxD = n.gt(d) ? (e > 0 ? d : n1) : n;
+ }
+
+ external = false;
+ n = new Ctor(digitsToString(xd));
+ pr = Ctor.precision;
+ Ctor.precision = e = xd.length * LOG_BASE * 2;
+
+ for (;;) {
+ q = divide(n, d, 0, 1, 1);
+ d2 = d0.plus(q.times(d1));
+ if (d2.cmp(maxD) == 1) break;
+ d0 = d1;
+ d1 = d2;
+ d2 = n1;
+ n1 = n0.plus(q.times(d2));
+ n0 = d2;
+ d2 = d;
+ d = n.minus(q.times(d2));
+ n = d2;
+ }
+
+ d2 = divide(maxD.minus(d0), d1, 0, 1, 1);
+ n0 = n0.plus(d2.times(n1));
+ d0 = d0.plus(d2.times(d1));
+ n0.s = n1.s = x.s;
+
+ // Determine which fraction is closer to x, n0/d0 or n1/d1?
+ r = divide(n1, d1, e, 1).minus(x).abs().cmp(divide(n0, d0, e, 1).minus(x).abs()) < 1
+ ? [n1, d1] : [n0, d0];
+
+ Ctor.precision = pr;
+ external = true;
+
+ return r;
+ };
+
+
+ /*
+ * Return a string representing the value of this Decimal in base 16, round to `sd` significant
+ * digits using rounding mode `rm`.
+ *
+ * If the optional `sd` argument is present then return binary exponential notation.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+ P.toHexadecimal = P.toHex = function (sd, rm) {
+ return toStringBinary(this, 16, sd, rm);
+ };
+
+
+ /*
+ * Returns a new Decimal whose value is the nearest multiple of `y` in the direction of rounding
+ * mode `rm`, or `Decimal.rounding` if `rm` is omitted, to the value of this Decimal.
+ *
+ * The return value will always have the same sign as this Decimal, unless either this Decimal
+ * or `y` is NaN, in which case the return value will be also be NaN.
+ *
+ * The return value is not affected by the value of `precision`.
+ *
+ * y {number|string|bigint|Decimal} The magnitude to round to a multiple of.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ * 'toNearest() rounding mode not an integer: {rm}'
+ * 'toNearest() rounding mode out of range: {rm}'
+ *
+ */
+ P.toNearest = function (y, rm) {
+ var x = this,
+ Ctor = x.constructor;
+
+ x = new Ctor(x);
+
+ if (y == null) {
+
+ // If x is not finite, return x.
+ if (!x.d) return x;
+
+ y = new Ctor(1);
+ rm = Ctor.rounding;
+ } else {
+ y = new Ctor(y);
+ if (rm === void 0) {
+ rm = Ctor.rounding;
+ } else {
+ checkInt32(rm, 0, 8);
+ }
+
+ // If x is not finite, return x if y is not NaN, else NaN.
+ if (!x.d) return y.s ? x : y;
+
+ // If y is not finite, return Infinity with the sign of x if y is Infinity, else NaN.
+ if (!y.d) {
+ if (y.s) y.s = x.s;
+ return y;
+ }
+ }
+
+ // If y is not zero, calculate the nearest multiple of y to x.
+ if (y.d[0]) {
+ external = false;
+ x = divide(x, y, 0, rm, 1).times(y);
+ external = true;
+ finalise(x);
+
+ // If y is zero, return zero with the sign of x.
+ } else {
+ y.s = x.s;
+ x = y;
+ }
+
+ return x;
+ };
+
+
+ /*
+ * Return the value of this Decimal converted to a number primitive.
+ * Zero keeps its sign.
+ *
+ */
+ P.toNumber = function () {
+ return +this;
+ };
+
+
+ /*
+ * Return a string representing the value of this Decimal in base 8, round to `sd` significant
+ * digits using rounding mode `rm`.
+ *
+ * If the optional `sd` argument is present then return binary exponential notation.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+ P.toOctal = function (sd, rm) {
+ return toStringBinary(this, 8, sd, rm);
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal raised to the power `y`, rounded
+ * to `precision` significant digits using rounding mode `rounding`.
+ *
+ * ECMAScript compliant.
+ *
+ * pow(x, NaN) = NaN
+ * pow(x, ±0) = 1
+
+ * pow(NaN, non-zero) = NaN
+ * pow(abs(x) > 1, +Infinity) = +Infinity
+ * pow(abs(x) > 1, -Infinity) = +0
+ * pow(abs(x) == 1, ±Infinity) = NaN
+ * pow(abs(x) < 1, +Infinity) = +0
+ * pow(abs(x) < 1, -Infinity) = +Infinity
+ * pow(+Infinity, y > 0) = +Infinity
+ * pow(+Infinity, y < 0) = +0
+ * pow(-Infinity, odd integer > 0) = -Infinity
+ * pow(-Infinity, even integer > 0) = +Infinity
+ * pow(-Infinity, odd integer < 0) = -0
+ * pow(-Infinity, even integer < 0) = +0
+ * pow(+0, y > 0) = +0
+ * pow(+0, y < 0) = +Infinity
+ * pow(-0, odd integer > 0) = -0
+ * pow(-0, even integer > 0) = +0
+ * pow(-0, odd integer < 0) = -Infinity
+ * pow(-0, even integer < 0) = +Infinity
+ * pow(finite x < 0, finite non-integer) = NaN
+ *
+ * For non-integer or very large exponents pow(x, y) is calculated using
+ *
+ * x^y = exp(y*ln(x))
+ *
+ * Assuming the first 15 rounding digits are each equally likely to be any digit 0-9, the
+ * probability of an incorrectly rounded result
+ * P([49]9{14} | [50]0{14}) = 2 * 0.2 * 10^-14 = 4e-15 = 1/2.5e+14
+ * i.e. 1 in 250,000,000,000,000
+ *
+ * If a result is incorrectly rounded the maximum error will be 1 ulp (unit in last place).
+ *
+ * y {number|string|bigint|Decimal} The power to which to raise this Decimal.
+ *
+ */
+ P.toPower = P.pow = function (y) {
+ var e, k, pr, r, rm, s,
+ x = this,
+ Ctor = x.constructor,
+ yn = +(y = new Ctor(y));
+
+ // Either ±Infinity, NaN or ±0?
+ if (!x.d || !y.d || !x.d[0] || !y.d[0]) return new Ctor(mathpow(+x, yn));
+
+ x = new Ctor(x);
+
+ if (x.eq(1)) return x;
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+
+ if (y.eq(1)) return finalise(x, pr, rm);
+
+ // y exponent
+ e = mathfloor(y.e / LOG_BASE);
+
+ // If y is a small integer use the 'exponentiation by squaring' algorithm.
+ if (e >= y.d.length - 1 && (k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) {
+ r = intPow(Ctor, x, k, pr);
+ return y.s < 0 ? new Ctor(1).div(r) : finalise(r, pr, rm);
+ }
+
+ s = x.s;
+
+ // if x is negative
+ if (s < 0) {
+
+ // if y is not an integer
+ if (e < y.d.length - 1) return new Ctor(NaN);
+
+ // Result is positive if x is negative and the last digit of integer y is even.
+ if ((y.d[e] & 1) == 0) s = 1;
+
+ // if x.eq(-1)
+ if (x.e == 0 && x.d[0] == 1 && x.d.length == 1) {
+ x.s = s;
+ return x;
+ }
+ }
+
+ // Estimate result exponent.
+ // x^y = 10^e, where e = y * log10(x)
+ // log10(x) = log10(x_significand) + x_exponent
+ // log10(x_significand) = ln(x_significand) / ln(10)
+ k = mathpow(+x, yn);
+ e = k == 0 || !isFinite(k)
+ ? mathfloor(yn * (Math.log('0.' + digitsToString(x.d)) / Math.LN10 + x.e + 1))
+ : new Ctor(k + '').e;
+
+ // Exponent estimate may be incorrect e.g. x: 0.999999999999999999, y: 2.29, e: 0, r.e: -1.
+
+ // Overflow/underflow?
+ if (e > Ctor.maxE + 1 || e < Ctor.minE - 1) return new Ctor(e > 0 ? s / 0 : 0);
+
+ external = false;
+ Ctor.rounding = x.s = 1;
+
+ // Estimate the extra guard digits needed to ensure five correct rounding digits from
+ // naturalLogarithm(x). Example of failure without these extra digits (precision: 10):
+ // new Decimal(2.32456).pow('2087987436534566.46411')
+ // should be 1.162377823e+764914905173815, but is 1.162355823e+764914905173815
+ k = Math.min(12, (e + '').length);
+
+ // r = x^y = exp(y*ln(x))
+ r = naturalExponential(y.times(naturalLogarithm(x, pr + k)), pr);
+
+ // r may be Infinity, e.g. (0.9999999999999999).pow(-1e+40)
+ if (r.d) {
+
+ // Truncate to the required precision plus five rounding digits.
+ r = finalise(r, pr + 5, 1);
+
+ // If the rounding digits are [49]9999 or [50]0000 increase the precision by 10 and recalculate
+ // the result.
+ if (checkRoundingDigits(r.d, pr, rm)) {
+ e = pr + 10;
+
+ // Truncate to the increased precision plus five rounding digits.
+ r = finalise(naturalExponential(y.times(naturalLogarithm(x, e + k)), e), e + 5, 1);
+
+ // Check for 14 nines from the 2nd rounding digit (the first rounding digit may be 4 or 9).
+ if (+digitsToString(r.d).slice(pr + 1, pr + 15) + 1 == 1e14) {
+ r = finalise(r, pr + 1, 0);
+ }
+ }
+ }
+
+ r.s = s;
+ external = true;
+ Ctor.rounding = rm;
+
+ return finalise(r, pr, rm);
+ };
+
+
+ /*
+ * Return a string representing the value of this Decimal rounded to `sd` significant digits
+ * using rounding mode `rounding`.
+ *
+ * Return exponential notation if `sd` is less than the number of digits necessary to represent
+ * the integer part of the value in normal notation.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+ P.toPrecision = function (sd, rm) {
+ var str,
+ x = this,
+ Ctor = x.constructor;
+
+ if (sd === void 0) {
+ str = finiteToString(x, x.e <= Ctor.toExpNeg || x.e >= Ctor.toExpPos);
+ } else {
+ checkInt32(sd, 1, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+
+ x = finalise(new Ctor(x), sd, rm);
+ str = finiteToString(x, sd <= x.e || x.e <= Ctor.toExpNeg, sd);
+ }
+
+ return x.isNeg() && !x.isZero() ? '-' + str : str;
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a maximum of `sd`
+ * significant digits using rounding mode `rm`, or to `precision` and `rounding` respectively if
+ * omitted.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ * 'toSD() digits out of range: {sd}'
+ * 'toSD() digits not an integer: {sd}'
+ * 'toSD() rounding mode not an integer: {rm}'
+ * 'toSD() rounding mode out of range: {rm}'
+ *
+ */
+ P.toSignificantDigits = P.toSD = function (sd, rm) {
+ var x = this,
+ Ctor = x.constructor;
+
+ if (sd === void 0) {
+ sd = Ctor.precision;
+ rm = Ctor.rounding;
+ } else {
+ checkInt32(sd, 1, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+ }
+
+ return finalise(new Ctor(x), sd, rm);
+ };
+
+
+ /*
+ * Return a string representing the value of this Decimal.
+ *
+ * Return exponential notation if this Decimal has a positive exponent equal to or greater than
+ * `toExpPos`, or a negative exponent equal to or less than `toExpNeg`.
+ *
+ */
+ P.toString = function () {
+ var x = this,
+ Ctor = x.constructor,
+ str = finiteToString(x, x.e <= Ctor.toExpNeg || x.e >= Ctor.toExpPos);
+
+ return x.isNeg() && !x.isZero() ? '-' + str : str;
+ };
+
+
+ /*
+ * Return a new Decimal whose value is the value of this Decimal truncated to a whole number.
+ *
+ */
+ P.truncated = P.trunc = function () {
+ return finalise(new this.constructor(this), this.e + 1, 1);
+ };
+
+
+ /*
+ * Return a string representing the value of this Decimal.
+ * Unlike `toString`, negative zero will include the minus sign.
+ *
+ */
+ P.valueOf = P.toJSON = function () {
+ var x = this,
+ Ctor = x.constructor,
+ str = finiteToString(x, x.e <= Ctor.toExpNeg || x.e >= Ctor.toExpPos);
+
+ return x.isNeg() ? '-' + str : str;
+ };
+
+
+ // Helper functions for Decimal.prototype (P) and/or Decimal methods, and their callers.
+
+
+ /*
+ * digitsToString P.cubeRoot, P.logarithm, P.squareRoot, P.toFraction, P.toPower,
+ * finiteToString, naturalExponential, naturalLogarithm
+ * checkInt32 P.toDecimalPlaces, P.toExponential, P.toFixed, P.toNearest,
+ * P.toPrecision, P.toSignificantDigits, toStringBinary, random
+ * checkRoundingDigits P.logarithm, P.toPower, naturalExponential, naturalLogarithm
+ * convertBase toStringBinary, parseOther
+ * cos P.cos
+ * divide P.atanh, P.cubeRoot, P.dividedBy, P.dividedToIntegerBy,
+ * P.logarithm, P.modulo, P.squareRoot, P.tan, P.tanh, P.toFraction,
+ * P.toNearest, toStringBinary, naturalExponential, naturalLogarithm,
+ * taylorSeries, atan2, parseOther
+ * finalise P.absoluteValue, P.atan, P.atanh, P.ceil, P.cos, P.cosh,
+ * P.cubeRoot, P.dividedToIntegerBy, P.floor, P.logarithm, P.minus,
+ * P.modulo, P.negated, P.plus, P.round, P.sin, P.sinh, P.squareRoot,
+ * P.tan, P.times, P.toDecimalPlaces, P.toExponential, P.toFixed,
+ * P.toNearest, P.toPower, P.toPrecision, P.toSignificantDigits,
+ * P.truncated, divide, getLn10, getPi, naturalExponential,
+ * naturalLogarithm, ceil, floor, round, trunc
+ * finiteToString P.toExponential, P.toFixed, P.toPrecision, P.toString, P.valueOf,
+ * toStringBinary
+ * getBase10Exponent P.minus, P.plus, P.times, parseOther
+ * getLn10 P.logarithm, naturalLogarithm
+ * getPi P.acos, P.asin, P.atan, toLessThanHalfPi, atan2
+ * getPrecision P.precision, P.toFraction
+ * getZeroString digitsToString, finiteToString
+ * intPow P.toPower, parseOther
+ * isOdd toLessThanHalfPi
+ * maxOrMin max, min
+ * naturalExponential P.naturalExponential, P.toPower
+ * naturalLogarithm P.acosh, P.asinh, P.atanh, P.logarithm, P.naturalLogarithm,
+ * P.toPower, naturalExponential
+ * nonFiniteToString finiteToString, toStringBinary
+ * parseDecimal Decimal
+ * parseOther Decimal
+ * sin P.sin
+ * taylorSeries P.cosh, P.sinh, cos, sin
+ * toLessThanHalfPi P.cos, P.sin
+ * toStringBinary P.toBinary, P.toHexadecimal, P.toOctal
+ * truncate intPow
+ *
+ * Throws: P.logarithm, P.precision, P.toFraction, checkInt32, getLn10, getPi,
+ * naturalLogarithm, config, parseOther, random, Decimal
+ */
+
+
+ function digitsToString(d) {
+ var i, k, ws,
+ indexOfLastWord = d.length - 1,
+ str = '',
+ w = d[0];
+
+ if (indexOfLastWord > 0) {
+ str += w;
+ for (i = 1; i < indexOfLastWord; i++) {
+ ws = d[i] + '';
+ k = LOG_BASE - ws.length;
+ if (k) str += getZeroString(k);
+ str += ws;
+ }
+
+ w = d[i];
+ ws = w + '';
+ k = LOG_BASE - ws.length;
+ if (k) str += getZeroString(k);
+ } else if (w === 0) {
+ return '0';
+ }
+
+ // Remove trailing zeros of last w.
+ for (; w % 10 === 0;) w /= 10;
+
+ return str + w;
+ }
+
+
+ function checkInt32(i, min, max) {
+ if (i !== ~~i || i < min || i > max) {
+ throw Error(invalidArgument + i);
+ }
+ }
+
+
+ /*
+ * Check 5 rounding digits if `repeating` is null, 4 otherwise.
+ * `repeating == null` if caller is `log` or `pow`,
+ * `repeating != null` if caller is `naturalLogarithm` or `naturalExponential`.
+ */
+ function checkRoundingDigits(d, i, rm, repeating) {
+ var di, k, r, rd;
+
+ // Get the length of the first word of the array d.
+ for (k = d[0]; k >= 10; k /= 10) --i;
+
+ // Is the rounding digit in the first word of d?
+ if (--i < 0) {
+ i += LOG_BASE;
+ di = 0;
+ } else {
+ di = Math.ceil((i + 1) / LOG_BASE);
+ i %= LOG_BASE;
+ }
+
+ // i is the index (0 - 6) of the rounding digit.
+ // E.g. if within the word 3487563 the first rounding digit is 5,
+ // then i = 4, k = 1000, rd = 3487563 % 1000 = 563
+ k = mathpow(10, LOG_BASE - i);
+ rd = d[di] % k | 0;
+
+ if (repeating == null) {
+ if (i < 3) {
+ if (i == 0) rd = rd / 100 | 0;
+ else if (i == 1) rd = rd / 10 | 0;
+ r = rm < 4 && rd == 99999 || rm > 3 && rd == 49999 || rd == 50000 || rd == 0;
+ } else {
+ r = (rm < 4 && rd + 1 == k || rm > 3 && rd + 1 == k / 2) &&
+ (d[di + 1] / k / 100 | 0) == mathpow(10, i - 2) - 1 ||
+ (rd == k / 2 || rd == 0) && (d[di + 1] / k / 100 | 0) == 0;
+ }
+ } else {
+ if (i < 4) {
+ if (i == 0) rd = rd / 1000 | 0;
+ else if (i == 1) rd = rd / 100 | 0;
+ else if (i == 2) rd = rd / 10 | 0;
+ r = (repeating || rm < 4) && rd == 9999 || !repeating && rm > 3 && rd == 4999;
+ } else {
+ r = ((repeating || rm < 4) && rd + 1 == k ||
+ (!repeating && rm > 3) && rd + 1 == k / 2) &&
+ (d[di + 1] / k / 1000 | 0) == mathpow(10, i - 3) - 1;
+ }
+ }
+
+ return r;
+ }
+
+
+ // Convert string of `baseIn` to an array of numbers of `baseOut`.
+ // Eg. convertBase('255', 10, 16) returns [15, 15].
+ // Eg. convertBase('ff', 16, 10) returns [2, 5, 5].
+ function convertBase(str, baseIn, baseOut) {
+ var j,
+ arr = [0],
+ arrL,
+ i = 0,
+ strL = str.length;
+
+ for (; i < strL;) {
+ for (arrL = arr.length; arrL--;) arr[arrL] *= baseIn;
+ arr[0] += NUMERALS.indexOf(str.charAt(i++));
+ for (j = 0; j < arr.length; j++) {
+ if (arr[j] > baseOut - 1) {
+ if (arr[j + 1] === void 0) arr[j + 1] = 0;
+ arr[j + 1] += arr[j] / baseOut | 0;
+ arr[j] %= baseOut;
+ }
+ }
+ }
+
+ return arr.reverse();
+ }
+
+
+ /*
+ * cos(x) = 1 - x^2/2! + x^4/4! - ...
+ * |x| < pi/2
+ *
+ */
+ function cosine(Ctor, x) {
+ var k, len, y;
+
+ if (x.isZero()) return x;
+
+ // Argument reduction: cos(4x) = 8*(cos^4(x) - cos^2(x)) + 1
+ // i.e. cos(x) = 8*(cos^4(x/4) - cos^2(x/4)) + 1
+
+ // Estimate the optimum number of times to use the argument reduction.
+ len = x.d.length;
+ if (len < 32) {
+ k = Math.ceil(len / 3);
+ y = (1 / tinyPow(4, k)).toString();
+ } else {
+ k = 16;
+ y = '2.3283064365386962890625e-10';
+ }
+
+ Ctor.precision += k;
+
+ x = taylorSeries(Ctor, 1, x.times(y), new Ctor(1));
+
+ // Reverse argument reduction
+ for (var i = k; i--;) {
+ var cos2x = x.times(x);
+ x = cos2x.times(cos2x).minus(cos2x).times(8).plus(1);
+ }
+
+ Ctor.precision -= k;
+
+ return x;
+ }
+
+
+ /*
+ * Perform division in the specified base.
+ */
+ var divide = (function () {
+
+ // Assumes non-zero x and k, and hence non-zero result.
+ function multiplyInteger(x, k, base) {
+ var temp,
+ carry = 0,
+ i = x.length;
+
+ for (x = x.slice(); i--;) {
+ temp = x[i] * k + carry;
+ x[i] = temp % base | 0;
+ carry = temp / base | 0;
+ }
+
+ if (carry) x.unshift(carry);
+
+ return x;
+ }
+
+ function compare(a, b, aL, bL) {
+ var i, r;
+
+ if (aL != bL) {
+ r = aL > bL ? 1 : -1;
+ } else {
+ for (i = r = 0; i < aL; i++) {
+ if (a[i] != b[i]) {
+ r = a[i] > b[i] ? 1 : -1;
+ break;
+ }
+ }
+ }
+
+ return r;
+ }
+
+ function subtract(a, b, aL, base) {
+ var i = 0;
+
+ // Subtract b from a.
+ for (; aL--;) {
+ a[aL] -= i;
+ i = a[aL] < b[aL] ? 1 : 0;
+ a[aL] = i * base + a[aL] - b[aL];
+ }
+
+ // Remove leading zeros.
+ for (; !a[0] && a.length > 1;) a.shift();
+ }
+
+ return function (x, y, pr, rm, dp, base) {
+ var cmp, e, i, k, logBase, more, prod, prodL, q, qd, rem, remL, rem0, sd, t, xi, xL, yd0,
+ yL, yz,
+ Ctor = x.constructor,
+ sign = x.s == y.s ? 1 : -1,
+ xd = x.d,
+ yd = y.d;
+
+ // Either NaN, Infinity or 0?
+ if (!xd || !xd[0] || !yd || !yd[0]) {
+
+ return new Ctor(// Return NaN if either NaN, or both Infinity or 0.
+ !x.s || !y.s || (xd ? yd && xd[0] == yd[0] : !yd) ? NaN :
+
+ // Return ±0 if x is 0 or y is ±Infinity, or return ±Infinity as y is 0.
+ xd && xd[0] == 0 || !yd ? sign * 0 : sign / 0);
+ }
+
+ if (base) {
+ logBase = 1;
+ e = x.e - y.e;
+ } else {
+ base = BASE;
+ logBase = LOG_BASE;
+ e = mathfloor(x.e / logBase) - mathfloor(y.e / logBase);
+ }
+
+ yL = yd.length;
+ xL = xd.length;
+ q = new Ctor(sign);
+ qd = q.d = [];
+
+ // Result exponent may be one less than e.
+ // The digit array of a Decimal from toStringBinary may have trailing zeros.
+ for (i = 0; yd[i] == (xd[i] || 0); i++);
+
+ if (yd[i] > (xd[i] || 0)) e--;
+
+ if (pr == null) {
+ sd = pr = Ctor.precision;
+ rm = Ctor.rounding;
+ } else if (dp) {
+ sd = pr + (x.e - y.e) + 1;
+ } else {
+ sd = pr;
+ }
+
+ if (sd < 0) {
+ qd.push(1);
+ more = true;
+ } else {
+
+ // Convert precision in number of base 10 digits to base 1e7 digits.
+ sd = sd / logBase + 2 | 0;
+ i = 0;
+
+ // divisor < 1e7
+ if (yL == 1) {
+ k = 0;
+ yd = yd[0];
+ sd++;
+
+ // k is the carry.
+ for (; (i < xL || k) && sd--; i++) {
+ t = k * base + (xd[i] || 0);
+ qd[i] = t / yd | 0;
+ k = t % yd | 0;
+ }
+
+ more = k || i < xL;
+
+ // divisor >= 1e7
+ } else {
+
+ // Normalise xd and yd so highest order digit of yd is >= base/2
+ k = base / (yd[0] + 1) | 0;
+
+ if (k > 1) {
+ yd = multiplyInteger(yd, k, base);
+ xd = multiplyInteger(xd, k, base);
+ yL = yd.length;
+ xL = xd.length;
+ }
+
+ xi = yL;
+ rem = xd.slice(0, yL);
+ remL = rem.length;
+
+ // Add zeros to make remainder as long as divisor.
+ for (; remL < yL;) rem[remL++] = 0;
+
+ yz = yd.slice();
+ yz.unshift(0);
+ yd0 = yd[0];
+
+ if (yd[1] >= base / 2) ++yd0;
+
+ do {
+ k = 0;
+
+ // Compare divisor and remainder.
+ cmp = compare(yd, rem, yL, remL);
+
+ // If divisor < remainder.
+ if (cmp < 0) {
+
+ // Calculate trial digit, k.
+ rem0 = rem[0];
+ if (yL != remL) rem0 = rem0 * base + (rem[1] || 0);
+
+ // k will be how many times the divisor goes into the current remainder.
+ k = rem0 / yd0 | 0;
+
+ // Algorithm:
+ // 1. product = divisor * trial digit (k)
+ // 2. if product > remainder: product -= divisor, k--
+ // 3. remainder -= product
+ // 4. if product was < remainder at 2:
+ // 5. compare new remainder and divisor
+ // 6. If remainder > divisor: remainder -= divisor, k++
+
+ if (k > 1) {
+ if (k >= base) k = base - 1;
+
+ // product = divisor * trial digit.
+ prod = multiplyInteger(yd, k, base);
+ prodL = prod.length;
+ remL = rem.length;
+
+ // Compare product and remainder.
+ cmp = compare(prod, rem, prodL, remL);
+
+ // product > remainder.
+ if (cmp == 1) {
+ k--;
+
+ // Subtract divisor from product.
+ subtract(prod, yL < prodL ? yz : yd, prodL, base);
+ }
+ } else {
+
+ // cmp is -1.
+ // If k is 0, there is no need to compare yd and rem again below, so change cmp to 1
+ // to avoid it. If k is 1 there is a need to compare yd and rem again below.
+ if (k == 0) cmp = k = 1;
+ prod = yd.slice();
+ }
+
+ prodL = prod.length;
+ if (prodL < remL) prod.unshift(0);
+
+ // Subtract product from remainder.
+ subtract(rem, prod, remL, base);
+
+ // If product was < previous remainder.
+ if (cmp == -1) {
+ remL = rem.length;
+
+ // Compare divisor and new remainder.
+ cmp = compare(yd, rem, yL, remL);
+
+ // If divisor < new remainder, subtract divisor from remainder.
+ if (cmp < 1) {
+ k++;
+
+ // Subtract divisor from remainder.
+ subtract(rem, yL < remL ? yz : yd, remL, base);
+ }
+ }
+
+ remL = rem.length;
+ } else if (cmp === 0) {
+ k++;
+ rem = [0];
+ } // if cmp === 1, k will be 0
+
+ // Add the next digit, k, to the result array.
+ qd[i++] = k;
+
+ // Update the remainder.
+ if (cmp && rem[0]) {
+ rem[remL++] = xd[xi] || 0;
+ } else {
+ rem = [xd[xi]];
+ remL = 1;
+ }
+
+ } while ((xi++ < xL || rem[0] !== void 0) && sd--);
+
+ more = rem[0] !== void 0;
+ }
+
+ // Leading zero?
+ if (!qd[0]) qd.shift();
+ }
+
+ // logBase is 1 when divide is being used for base conversion.
+ if (logBase == 1) {
+ q.e = e;
+ inexact = more;
+ } else {
+
+ // To calculate q.e, first get the number of digits of qd[0].
+ for (i = 1, k = qd[0]; k >= 10; k /= 10) i++;
+ q.e = i + e * logBase - 1;
+
+ finalise(q, dp ? pr + q.e + 1 : pr, rm, more);
+ }
+
+ return q;
+ };
+ })();
+
+
+ /*
+ * Round `x` to `sd` significant digits using rounding mode `rm`.
+ * Check for over/under-flow.
+ */
+ function finalise(x, sd, rm, isTruncated) {
+ var digits, i, j, k, rd, roundUp, w, xd, xdi,
+ Ctor = x.constructor;
+
+ // Don't round if sd is null or undefined.
+ out: if (sd != null) {
+ xd = x.d;
+
+ // Infinity/NaN.
+ if (!xd) return x;
+
+ // rd: the rounding digit, i.e. the digit after the digit that may be rounded up.
+ // w: the word of xd containing rd, a base 1e7 number.
+ // xdi: the index of w within xd.
+ // digits: the number of digits of w.
+ // i: what would be the index of rd within w if all the numbers were 7 digits long (i.e. if
+ // they had leading zeros)
+ // j: if > 0, the actual index of rd within w (if < 0, rd is a leading zero).
+
+ // Get the length of the first word of the digits array xd.
+ for (digits = 1, k = xd[0]; k >= 10; k /= 10) digits++;
+ i = sd - digits;
+
+ // Is the rounding digit in the first word of xd?
+ if (i < 0) {
+ i += LOG_BASE;
+ j = sd;
+ w = xd[xdi = 0];
+
+ // Get the rounding digit at index j of w.
+ rd = w / mathpow(10, digits - j - 1) % 10 | 0;
+ } else {
+ xdi = Math.ceil((i + 1) / LOG_BASE);
+ k = xd.length;
+ if (xdi >= k) {
+ if (isTruncated) {
+
+ // Needed by `naturalExponential`, `naturalLogarithm` and `squareRoot`.
+ for (; k++ <= xdi;) xd.push(0);
+ w = rd = 0;
+ digits = 1;
+ i %= LOG_BASE;
+ j = i - LOG_BASE + 1;
+ } else {
+ break out;
+ }
+ } else {
+ w = k = xd[xdi];
+
+ // Get the number of digits of w.
+ for (digits = 1; k >= 10; k /= 10) digits++;
+
+ // Get the index of rd within w.
+ i %= LOG_BASE;
+
+ // Get the index of rd within w, adjusted for leading zeros.
+ // The number of leading zeros of w is given by LOG_BASE - digits.
+ j = i - LOG_BASE + digits;
+
+ // Get the rounding digit at index j of w.
+ rd = j < 0 ? 0 : w / mathpow(10, digits - j - 1) % 10 | 0;
+ }
+ }
+
+ // Are there any non-zero digits after the rounding digit?
+ isTruncated = isTruncated || sd < 0 ||
+ xd[xdi + 1] !== void 0 || (j < 0 ? w : w % mathpow(10, digits - j - 1));
+
+ // The expression `w % mathpow(10, digits - j - 1)` returns all the digits of w to the right
+ // of the digit at (left-to-right) index j, e.g. if w is 908714 and j is 2, the expression
+ // will give 714.
+
+ roundUp = rm < 4
+ ? (rd || isTruncated) && (rm == 0 || rm == (x.s < 0 ? 3 : 2))
+ : rd > 5 || rd == 5 && (rm == 4 || isTruncated || rm == 6 &&
+
+ // Check whether the digit to the left of the rounding digit is odd.
+ ((i > 0 ? j > 0 ? w / mathpow(10, digits - j) : 0 : xd[xdi - 1]) % 10) & 1 ||
+ rm == (x.s < 0 ? 8 : 7));
+
+ if (sd < 1 || !xd[0]) {
+ xd.length = 0;
+ if (roundUp) {
+
+ // Convert sd to decimal places.
+ sd -= x.e + 1;
+
+ // 1, 0.1, 0.01, 0.001, 0.0001 etc.
+ xd[0] = mathpow(10, (LOG_BASE - sd % LOG_BASE) % LOG_BASE);
+ x.e = -sd || 0;
+ } else {
+
+ // Zero.
+ xd[0] = x.e = 0;
+ }
+
+ return x;
+ }
+
+ // Remove excess digits.
+ if (i == 0) {
+ xd.length = xdi;
+ k = 1;
+ xdi--;
+ } else {
+ xd.length = xdi + 1;
+ k = mathpow(10, LOG_BASE - i);
+
+ // E.g. 56700 becomes 56000 if 7 is the rounding digit.
+ // j > 0 means i > number of leading zeros of w.
+ xd[xdi] = j > 0 ? (w / mathpow(10, digits - j) % mathpow(10, j) | 0) * k : 0;
+ }
+
+ if (roundUp) {
+ for (;;) {
+
+ // Is the digit to be rounded up in the first word of xd?
+ if (xdi == 0) {
+
+ // i will be the length of xd[0] before k is added.
+ for (i = 1, j = xd[0]; j >= 10; j /= 10) i++;
+ j = xd[0] += k;
+ for (k = 1; j >= 10; j /= 10) k++;
+
+ // if i != k the length has increased.
+ if (i != k) {
+ x.e++;
+ if (xd[0] == BASE) xd[0] = 1;
+ }
+
+ break;
+ } else {
+ xd[xdi] += k;
+ if (xd[xdi] != BASE) break;
+ xd[xdi--] = 0;
+ k = 1;
+ }
+ }
+ }
+
+ // Remove trailing zeros.
+ for (i = xd.length; xd[--i] === 0;) xd.pop();
+ }
+
+ if (external) {
+
+ // Overflow?
+ if (x.e > Ctor.maxE) {
+
+ // Infinity.
+ x.d = null;
+ x.e = NaN;
+
+ // Underflow?
+ } else if (x.e < Ctor.minE) {
+
+ // Zero.
+ x.e = 0;
+ x.d = [0];
+ // Ctor.underflow = true;
+ } // else Ctor.underflow = false;
+ }
+
+ return x;
+ }
+
+
+ function finiteToString(x, isExp, sd) {
+ if (!x.isFinite()) return nonFiniteToString(x);
+ var k,
+ e = x.e,
+ str = digitsToString(x.d),
+ len = str.length;
+
+ if (isExp) {
+ if (sd && (k = sd - len) > 0) {
+ str = str.charAt(0) + '.' + str.slice(1) + getZeroString(k);
+ } else if (len > 1) {
+ str = str.charAt(0) + '.' + str.slice(1);
+ }
+
+ str = str + (x.e < 0 ? 'e' : 'e+') + x.e;
+ } else if (e < 0) {
+ str = '0.' + getZeroString(-e - 1) + str;
+ if (sd && (k = sd - len) > 0) str += getZeroString(k);
+ } else if (e >= len) {
+ str += getZeroString(e + 1 - len);
+ if (sd && (k = sd - e - 1) > 0) str = str + '.' + getZeroString(k);
+ } else {
+ if ((k = e + 1) < len) str = str.slice(0, k) + '.' + str.slice(k);
+ if (sd && (k = sd - len) > 0) {
+ if (e + 1 === len) str += '.';
+ str += getZeroString(k);
+ }
+ }
+
+ return str;
+ }
+
+
+ // Calculate the base 10 exponent from the base 1e7 exponent.
+ function getBase10Exponent(digits, e) {
+ var w = digits[0];
+
+ // Add the number of digits of the first word of the digits array.
+ for ( e *= LOG_BASE; w >= 10; w /= 10) e++;
+ return e;
+ }
+
+
+ function getLn10(Ctor, sd, pr) {
+ if (sd > LN10_PRECISION) {
+
+ // Reset global state in case the exception is caught.
+ external = true;
+ if (pr) Ctor.precision = pr;
+ throw Error(precisionLimitExceeded);
+ }
+ return finalise(new Ctor(LN10), sd, 1, true);
+ }
+
+
+ function getPi(Ctor, sd, rm) {
+ if (sd > PI_PRECISION) throw Error(precisionLimitExceeded);
+ return finalise(new Ctor(PI), sd, rm, true);
+ }
+
+
+ function getPrecision(digits) {
+ var w = digits.length - 1,
+ len = w * LOG_BASE + 1;
+
+ w = digits[w];
+
+ // If non-zero...
+ if (w) {
+
+ // Subtract the number of trailing zeros of the last word.
+ for (; w % 10 == 0; w /= 10) len--;
+
+ // Add the number of digits of the first word.
+ for (w = digits[0]; w >= 10; w /= 10) len++;
+ }
+
+ return len;
+ }
+
+
+ function getZeroString(k) {
+ var zs = '';
+ for (; k--;) zs += '0';
+ return zs;
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the value of Decimal `x` to the power `n`, where `n` is an
+ * integer of type number.
+ *
+ * Implements 'exponentiation by squaring'. Called by `pow` and `parseOther`.
+ *
+ */
+ function intPow(Ctor, x, n, pr) {
+ var isTruncated,
+ r = new Ctor(1),
+
+ // Max n of 9007199254740991 takes 53 loop iterations.
+ // Maximum digits array length; leaves [28, 34] guard digits.
+ k = Math.ceil(pr / LOG_BASE + 4);
+
+ external = false;
+
+ for (;;) {
+ if (n % 2) {
+ r = r.times(x);
+ if (truncate(r.d, k)) isTruncated = true;
+ }
+
+ n = mathfloor(n / 2);
+ if (n === 0) {
+
+ // To ensure correct rounding when r.d is truncated, increment the last word if it is zero.
+ n = r.d.length - 1;
+ if (isTruncated && r.d[n] === 0) ++r.d[n];
+ break;
+ }
+
+ x = x.times(x);
+ truncate(x.d, k);
+ }
+
+ external = true;
+
+ return r;
+ }
+
+
+ function isOdd(n) {
+ return n.d[n.d.length - 1] & 1;
+ }
+
+
+ /*
+ * Handle `max` (`n` is -1) and `min` (`n` is 1).
+ */
+ function maxOrMin(Ctor, args, n) {
+ var k, y,
+ x = new Ctor(args[0]),
+ i = 0;
+
+ for (; ++i < args.length;) {
+ y = new Ctor(args[i]);
+
+ // NaN?
+ if (!y.s) {
+ x = y;
+ break;
+ }
+
+ k = x.cmp(y);
+
+ if (k === n || k === 0 && x.s === n) {
+ x = y;
+ }
+ }
+
+ return x;
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the natural exponential of `x` rounded to `sd` significant
+ * digits.
+ *
+ * Taylor/Maclaurin series.
+ *
+ * exp(x) = x^0/0! + x^1/1! + x^2/2! + x^3/3! + ...
+ *
+ * Argument reduction:
+ * Repeat x = x / 32, k += 5, until |x| < 0.1
+ * exp(x) = exp(x / 2^k)^(2^k)
+ *
+ * Previously, the argument was initially reduced by
+ * exp(x) = exp(r) * 10^k where r = x - k * ln10, k = floor(x / ln10)
+ * to first put r in the range [0, ln10], before dividing by 32 until |x| < 0.1, but this was
+ * found to be slower than just dividing repeatedly by 32 as above.
+ *
+ * Max integer argument: exp('20723265836946413') = 6.3e+9000000000000000
+ * Min integer argument: exp('-20723265836946411') = 1.2e-9000000000000000
+ * (Math object integer min/max: Math.exp(709) = 8.2e+307, Math.exp(-745) = 5e-324)
+ *
+ * exp(Infinity) = Infinity
+ * exp(-Infinity) = 0
+ * exp(NaN) = NaN
+ * exp(±0) = 1
+ *
+ * exp(x) is non-terminating for any finite, non-zero x.
+ *
+ * The result will always be correctly rounded.
+ *
+ */
+ function naturalExponential(x, sd) {
+ var denominator, guard, j, pow, sum, t, wpr,
+ rep = 0,
+ i = 0,
+ k = 0,
+ Ctor = x.constructor,
+ rm = Ctor.rounding,
+ pr = Ctor.precision;
+
+ // 0/NaN/Infinity?
+ if (!x.d || !x.d[0] || x.e > 17) {
+
+ return new Ctor(x.d
+ ? !x.d[0] ? 1 : x.s < 0 ? 0 : 1 / 0
+ : x.s ? x.s < 0 ? 0 : x : 0 / 0);
+ }
+
+ if (sd == null) {
+ external = false;
+ wpr = pr;
+ } else {
+ wpr = sd;
+ }
+
+ t = new Ctor(0.03125);
+
+ // while abs(x) >= 0.1
+ while (x.e > -2) {
+
+ // x = x / 2^5
+ x = x.times(t);
+ k += 5;
+ }
+
+ // Use 2 * log10(2^k) + 5 (empirically derived) to estimate the increase in precision
+ // necessary to ensure the first 4 rounding digits are correct.
+ guard = Math.log(mathpow(2, k)) / Math.LN10 * 2 + 5 | 0;
+ wpr += guard;
+ denominator = pow = sum = new Ctor(1);
+ Ctor.precision = wpr;
+
+ for (;;) {
+ pow = finalise(pow.times(x), wpr, 1);
+ denominator = denominator.times(++i);
+ t = sum.plus(divide(pow, denominator, wpr, 1));
+
+ if (digitsToString(t.d).slice(0, wpr) === digitsToString(sum.d).slice(0, wpr)) {
+ j = k;
+ while (j--) sum = finalise(sum.times(sum), wpr, 1);
+
+ // Check to see if the first 4 rounding digits are [49]999.
+ // If so, repeat the summation with a higher precision, otherwise
+ // e.g. with precision: 18, rounding: 1
+ // exp(18.404272462595034083567793919843761) = 98372560.1229999999 (should be 98372560.123)
+ // `wpr - guard` is the index of first rounding digit.
+ if (sd == null) {
+
+ if (rep < 3 && checkRoundingDigits(sum.d, wpr - guard, rm, rep)) {
+ Ctor.precision = wpr += 10;
+ denominator = pow = t = new Ctor(1);
+ i = 0;
+ rep++;
+ } else {
+ return finalise(sum, Ctor.precision = pr, rm, external = true);
+ }
+ } else {
+ Ctor.precision = pr;
+ return sum;
+ }
+ }
+
+ sum = t;
+ }
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the natural logarithm of `x` rounded to `sd` significant
+ * digits.
+ *
+ * ln(-n) = NaN
+ * ln(0) = -Infinity
+ * ln(-0) = -Infinity
+ * ln(1) = 0
+ * ln(Infinity) = Infinity
+ * ln(-Infinity) = NaN
+ * ln(NaN) = NaN
+ *
+ * ln(n) (n != 1) is non-terminating.
+ *
+ */
+ function naturalLogarithm(y, sd) {
+ var c, c0, denominator, e, numerator, rep, sum, t, wpr, x1, x2,
+ n = 1,
+ guard = 10,
+ x = y,
+ xd = x.d,
+ Ctor = x.constructor,
+ rm = Ctor.rounding,
+ pr = Ctor.precision;
+
+ // Is x negative or Infinity, NaN, 0 or 1?
+ if (x.s < 0 || !xd || !xd[0] || !x.e && xd[0] == 1 && xd.length == 1) {
+ return new Ctor(xd && !xd[0] ? -1 / 0 : x.s != 1 ? NaN : xd ? 0 : x);
+ }
+
+ if (sd == null) {
+ external = false;
+ wpr = pr;
+ } else {
+ wpr = sd;
+ }
+
+ Ctor.precision = wpr += guard;
+ c = digitsToString(xd);
+ c0 = c.charAt(0);
+
+ if (Math.abs(e = x.e) < 1.5e15) {
+
+ // Argument reduction.
+ // The series converges faster the closer the argument is to 1, so using
+ // ln(a^b) = b * ln(a), ln(a) = ln(a^b) / b
+ // multiply the argument by itself until the leading digits of the significand are 7, 8, 9,
+ // 10, 11, 12 or 13, recording the number of multiplications so the sum of the series can
+ // later be divided by this number, then separate out the power of 10 using
+ // ln(a*10^b) = ln(a) + b*ln(10).
+
+ // max n is 21 (gives 0.9, 1.0 or 1.1) (9e15 / 21 = 4.2e14).
+ //while (c0 < 9 && c0 != 1 || c0 == 1 && c.charAt(1) > 1) {
+ // max n is 6 (gives 0.7 - 1.3)
+ while (c0 < 7 && c0 != 1 || c0 == 1 && c.charAt(1) > 3) {
+ x = x.times(y);
+ c = digitsToString(x.d);
+ c0 = c.charAt(0);
+ n++;
+ }
+
+ e = x.e;
+
+ if (c0 > 1) {
+ x = new Ctor('0.' + c);
+ e++;
+ } else {
+ x = new Ctor(c0 + '.' + c.slice(1));
+ }
+ } else {
+
+ // The argument reduction method above may result in overflow if the argument y is a massive
+ // number with exponent >= 1500000000000000 (9e15 / 6 = 1.5e15), so instead recall this
+ // function using ln(x*10^e) = ln(x) + e*ln(10).
+ t = getLn10(Ctor, wpr + 2, pr).times(e + '');
+ x = naturalLogarithm(new Ctor(c0 + '.' + c.slice(1)), wpr - guard).plus(t);
+ Ctor.precision = pr;
+
+ return sd == null ? finalise(x, pr, rm, external = true) : x;
+ }
+
+ // x1 is x reduced to a value near 1.
+ x1 = x;
+
+ // Taylor series.
+ // ln(y) = ln((1 + x)/(1 - x)) = 2(x + x^3/3 + x^5/5 + x^7/7 + ...)
+ // where x = (y - 1)/(y + 1) (|x| < 1)
+ sum = numerator = x = divide(x.minus(1), x.plus(1), wpr, 1);
+ x2 = finalise(x.times(x), wpr, 1);
+ denominator = 3;
+
+ for (;;) {
+ numerator = finalise(numerator.times(x2), wpr, 1);
+ t = sum.plus(divide(numerator, new Ctor(denominator), wpr, 1));
+
+ if (digitsToString(t.d).slice(0, wpr) === digitsToString(sum.d).slice(0, wpr)) {
+ sum = sum.times(2);
+
+ // Reverse the argument reduction. Check that e is not 0 because, besides preventing an
+ // unnecessary calculation, -0 + 0 = +0 and to ensure correct rounding -0 needs to stay -0.
+ if (e !== 0) sum = sum.plus(getLn10(Ctor, wpr + 2, pr).times(e + ''));
+ sum = divide(sum, new Ctor(n), wpr, 1);
+
+ // Is rm > 3 and the first 4 rounding digits 4999, or rm < 4 (or the summation has
+ // been repeated previously) and the first 4 rounding digits 9999?
+ // If so, restart the summation with a higher precision, otherwise
+ // e.g. with precision: 12, rounding: 1
+ // ln(135520028.6126091714265381533) = 18.7246299999 when it should be 18.72463.
+ // `wpr - guard` is the index of first rounding digit.
+ if (sd == null) {
+ if (checkRoundingDigits(sum.d, wpr - guard, rm, rep)) {
+ Ctor.precision = wpr += guard;
+ t = numerator = x = divide(x1.minus(1), x1.plus(1), wpr, 1);
+ x2 = finalise(x.times(x), wpr, 1);
+ denominator = rep = 1;
+ } else {
+ return finalise(sum, Ctor.precision = pr, rm, external = true);
+ }
+ } else {
+ Ctor.precision = pr;
+ return sum;
+ }
+ }
+
+ sum = t;
+ denominator += 2;
+ }
+ }
+
+
+ // ±Infinity, NaN.
+ function nonFiniteToString(x) {
+ // Unsigned.
+ return String(x.s * x.s / 0);
+ }
+
+
+ /*
+ * Parse the value of a new Decimal `x` from string `str`.
+ */
+ function parseDecimal(x, str) {
+ var e, i, len;
+
+ // TODO BigInt str: no need to check for decimal point, exponential form or leading zeros.
+
+ // Decimal point?
+ if ((e = str.indexOf('.')) > -1) str = str.replace('.', '');
+
+ // Exponential form?
+ if ((i = str.search(/e/i)) > 0) {
+
+ // Determine exponent.
+ if (e < 0) e = i;
+ e += +str.slice(i + 1);
+ str = str.substring(0, i);
+ } else if (e < 0) {
+
+ // Integer.
+ e = str.length;
+ }
+
+ // Determine leading zeros.
+ for (i = 0; str.charCodeAt(i) === 48; i++);
+
+ // Determine trailing zeros.
+ for (len = str.length; str.charCodeAt(len - 1) === 48; --len);
+ str = str.slice(i, len);
+
+ if (str) {
+ len -= i;
+ x.e = e = e - i - 1;
+ x.d = [];
+
+ // Transform base
+
+ // e is the base 10 exponent.
+ // i is where to slice str to get the first word of the digits array.
+ i = (e + 1) % LOG_BASE;
+ if (e < 0) i += LOG_BASE;
+
+ if (i < len) {
+ if (i) x.d.push(+str.slice(0, i));
+ for (len -= LOG_BASE; i < len;) x.d.push(+str.slice(i, i += LOG_BASE));
+ str = str.slice(i);
+ i = LOG_BASE - str.length;
+ } else {
+ i -= len;
+ }
+
+ for (; i--;) str += '0';
+ x.d.push(+str);
+
+ if (external) {
+
+ // Overflow?
+ if (x.e > x.constructor.maxE) {
+
+ // Infinity.
+ x.d = null;
+ x.e = NaN;
+
+ // Underflow?
+ } else if (x.e < x.constructor.minE) {
+
+ // Zero.
+ x.e = 0;
+ x.d = [0];
+ // x.constructor.underflow = true;
+ } // else x.constructor.underflow = false;
+ }
+ } else {
+
+ // Zero.
+ x.e = 0;
+ x.d = [0];
+ }
+
+ return x;
+ }
+
+
+ /*
+ * Parse the value of a new Decimal `x` from a string `str`, which is not a decimal value.
+ */
+ function parseOther(x, str) {
+ var base, Ctor, divisor, i, isFloat, len, p, xd, xe;
+
+ if (str.indexOf('_') > -1) {
+ str = str.replace(/(\d)_(?=\d)/g, '$1');
+ if (isDecimal.test(str)) return parseDecimal(x, str);
+ } else if (str === 'Infinity' || str === 'NaN') {
+ if (!+str) x.s = NaN;
+ x.e = NaN;
+ x.d = null;
+ return x;
+ }
+
+ if (isHex.test(str)) {
+ base = 16;
+ str = str.toLowerCase();
+ } else if (isBinary.test(str)) {
+ base = 2;
+ } else if (isOctal.test(str)) {
+ base = 8;
+ } else {
+ throw Error(invalidArgument + str);
+ }
+
+ // Is there a binary exponent part?
+ i = str.search(/p/i);
+
+ if (i > 0) {
+ p = +str.slice(i + 1);
+ str = str.substring(2, i);
+ } else {
+ str = str.slice(2);
+ }
+
+ // Convert `str` as an integer then divide the result by `base` raised to a power such that the
+ // fraction part will be restored.
+ i = str.indexOf('.');
+ isFloat = i >= 0;
+ Ctor = x.constructor;
+
+ if (isFloat) {
+ str = str.replace('.', '');
+ len = str.length;
+ i = len - i;
+
+ // log[10](16) = 1.2041... , log[10](88) = 1.9444....
+ divisor = intPow(Ctor, new Ctor(base), i, i * 2);
+ }
+
+ xd = convertBase(str, base, BASE);
+ xe = xd.length - 1;
+
+ // Remove trailing zeros.
+ for (i = xe; xd[i] === 0; --i) xd.pop();
+ if (i < 0) return new Ctor(x.s * 0);
+ x.e = getBase10Exponent(xd, xe);
+ x.d = xd;
+ external = false;
+
+ // At what precision to perform the division to ensure exact conversion?
+ // maxDecimalIntegerPartDigitCount = ceil(log[10](b) * otherBaseIntegerPartDigitCount)
+ // log[10](2) = 0.30103, log[10](8) = 0.90309, log[10](16) = 1.20412
+ // E.g. ceil(1.2 * 3) = 4, so up to 4 decimal digits are needed to represent 3 hex int digits.
+ // maxDecimalFractionPartDigitCount = {Hex:4|Oct:3|Bin:1} * otherBaseFractionPartDigitCount
+ // Therefore using 4 * the number of digits of str will always be enough.
+ if (isFloat) x = divide(x, divisor, len * 4);
+
+ // Multiply by the binary exponent part if present.
+ if (p) x = x.times(Math.abs(p) < 54 ? mathpow(2, p) : Decimal.pow(2, p));
+ external = true;
+
+ return x;
+ }
+
+
+ /*
+ * sin(x) = x - x^3/3! + x^5/5! - ...
+ * |x| < pi/2
+ *
+ */
+ function sine(Ctor, x) {
+ var k,
+ len = x.d.length;
+
+ if (len < 3) {
+ return x.isZero() ? x : taylorSeries(Ctor, 2, x, x);
+ }
+
+ // Argument reduction: sin(5x) = 16*sin^5(x) - 20*sin^3(x) + 5*sin(x)
+ // i.e. sin(x) = 16*sin^5(x/5) - 20*sin^3(x/5) + 5*sin(x/5)
+ // and sin(x) = sin(x/5)(5 + sin^2(x/5)(16sin^2(x/5) - 20))
+
+ // Estimate the optimum number of times to use the argument reduction.
+ k = 1.4 * Math.sqrt(len);
+ k = k > 16 ? 16 : k | 0;
+
+ x = x.times(1 / tinyPow(5, k));
+ x = taylorSeries(Ctor, 2, x, x);
+
+ // Reverse argument reduction
+ var sin2_x,
+ d5 = new Ctor(5),
+ d16 = new Ctor(16),
+ d20 = new Ctor(20);
+ for (; k--;) {
+ sin2_x = x.times(x);
+ x = x.times(d5.plus(sin2_x.times(d16.times(sin2_x).minus(d20))));
+ }
+
+ return x;
+ }
+
+
+ // Calculate Taylor series for `cos`, `cosh`, `sin` and `sinh`.
+ function taylorSeries(Ctor, n, x, y, isHyperbolic) {
+ var j, t, u, x2,
+ i = 1,
+ pr = Ctor.precision,
+ k = Math.ceil(pr / LOG_BASE);
+
+ external = false;
+ x2 = x.times(x);
+ u = new Ctor(y);
+
+ for (;;) {
+ t = divide(u.times(x2), new Ctor(n++ * n++), pr, 1);
+ u = isHyperbolic ? y.plus(t) : y.minus(t);
+ y = divide(t.times(x2), new Ctor(n++ * n++), pr, 1);
+ t = u.plus(y);
+
+ if (t.d[k] !== void 0) {
+ for (j = k; t.d[j] === u.d[j] && j--;);
+ if (j == -1) break;
+ }
+
+ j = u;
+ u = y;
+ y = t;
+ t = j;
+ i++;
+ }
+
+ external = true;
+ t.d.length = k + 1;
+
+ return t;
+ }
+
+
+ // Exponent e must be positive and non-zero.
+ function tinyPow(b, e) {
+ var n = b;
+ while (--e) n *= b;
+ return n;
+ }
+
+
+ // Return the absolute value of `x` reduced to less than or equal to half pi.
+ function toLessThanHalfPi(Ctor, x) {
+ var t,
+ isNeg = x.s < 0,
+ pi = getPi(Ctor, Ctor.precision, 1),
+ halfPi = pi.times(0.5);
+
+ x = x.abs();
+
+ if (x.lte(halfPi)) {
+ quadrant = isNeg ? 4 : 1;
+ return x;
+ }
+
+ t = x.divToInt(pi);
+
+ if (t.isZero()) {
+ quadrant = isNeg ? 3 : 2;
+ } else {
+ x = x.minus(t.times(pi));
+
+ // 0 <= x < pi
+ if (x.lte(halfPi)) {
+ quadrant = isOdd(t) ? (isNeg ? 2 : 3) : (isNeg ? 4 : 1);
+ return x;
+ }
+
+ quadrant = isOdd(t) ? (isNeg ? 1 : 4) : (isNeg ? 3 : 2);
+ }
+
+ return x.minus(pi).abs();
+ }
+
+
+ /*
+ * Return the value of Decimal `x` as a string in base `baseOut`.
+ *
+ * If the optional `sd` argument is present include a binary exponent suffix.
+ */
+ function toStringBinary(x, baseOut, sd, rm) {
+ var base, e, i, k, len, roundUp, str, xd, y,
+ Ctor = x.constructor,
+ isExp = sd !== void 0;
+
+ if (isExp) {
+ checkInt32(sd, 1, MAX_DIGITS);
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+ } else {
+ sd = Ctor.precision;
+ rm = Ctor.rounding;
+ }
+
+ if (!x.isFinite()) {
+ str = nonFiniteToString(x);
+ } else {
+ str = finiteToString(x);
+ i = str.indexOf('.');
+
+ // Use exponential notation according to `toExpPos` and `toExpNeg`? No, but if required:
+ // maxBinaryExponent = floor((decimalExponent + 1) * log[2](10))
+ // minBinaryExponent = floor(decimalExponent * log[2](10))
+ // log[2](10) = 3.321928094887362347870319429489390175864
+
+ if (isExp) {
+ base = 2;
+ if (baseOut == 16) {
+ sd = sd * 4 - 3;
+ } else if (baseOut == 8) {
+ sd = sd * 3 - 2;
+ }
+ } else {
+ base = baseOut;
+ }
+
+ // Convert the number as an integer then divide the result by its base raised to a power such
+ // that the fraction part will be restored.
+
+ // Non-integer.
+ if (i >= 0) {
+ str = str.replace('.', '');
+ y = new Ctor(1);
+ y.e = str.length - i;
+ y.d = convertBase(finiteToString(y), 10, base);
+ y.e = y.d.length;
+ }
+
+ xd = convertBase(str, 10, base);
+ e = len = xd.length;
+
+ // Remove trailing zeros.
+ for (; xd[--len] == 0;) xd.pop();
+
+ if (!xd[0]) {
+ str = isExp ? '0p+0' : '0';
+ } else {
+ if (i < 0) {
+ e--;
+ } else {
+ x = new Ctor(x);
+ x.d = xd;
+ x.e = e;
+ x = divide(x, y, sd, rm, 0, base);
+ xd = x.d;
+ e = x.e;
+ roundUp = inexact;
+ }
+
+ // The rounding digit, i.e. the digit after the digit that may be rounded up.
+ i = xd[sd];
+ k = base / 2;
+ roundUp = roundUp || xd[sd + 1] !== void 0;
+
+ roundUp = rm < 4
+ ? (i !== void 0 || roundUp) && (rm === 0 || rm === (x.s < 0 ? 3 : 2))
+ : i > k || i === k && (rm === 4 || roundUp || rm === 6 && xd[sd - 1] & 1 ||
+ rm === (x.s < 0 ? 8 : 7));
+
+ xd.length = sd;
+
+ if (roundUp) {
+
+ // Rounding up may mean the previous digit has to be rounded up and so on.
+ for (; ++xd[--sd] > base - 1;) {
+ xd[sd] = 0;
+ if (!sd) {
+ ++e;
+ xd.unshift(1);
+ }
+ }
+ }
+
+ // Determine trailing zeros.
+ for (len = xd.length; !xd[len - 1]; --len);
+
+ // E.g. [4, 11, 15] becomes 4bf.
+ for (i = 0, str = ''; i < len; i++) str += NUMERALS.charAt(xd[i]);
+
+ // Add binary exponent suffix?
+ if (isExp) {
+ if (len > 1) {
+ if (baseOut == 16 || baseOut == 8) {
+ i = baseOut == 16 ? 4 : 3;
+ for (--len; len % i; len++) str += '0';
+ xd = convertBase(str, base, baseOut);
+ for (len = xd.length; !xd[len - 1]; --len);
+
+ // xd[0] will always be be 1
+ for (i = 1, str = '1.'; i < len; i++) str += NUMERALS.charAt(xd[i]);
+ } else {
+ str = str.charAt(0) + '.' + str.slice(1);
+ }
+ }
+
+ str = str + (e < 0 ? 'p' : 'p+') + e;
+ } else if (e < 0) {
+ for (; ++e;) str = '0' + str;
+ str = '0.' + str;
+ } else {
+ if (++e > len) for (e -= len; e-- ;) str += '0';
+ else if (e < len) str = str.slice(0, e) + '.' + str.slice(e);
+ }
+ }
+
+ str = (baseOut == 16 ? '0x' : baseOut == 2 ? '0b' : baseOut == 8 ? '0o' : '') + str;
+ }
+
+ return x.s < 0 ? '-' + str : str;
+ }
+
+
+ // Does not strip trailing zeros.
+ function truncate(arr, len) {
+ if (arr.length > len) {
+ arr.length = len;
+ return true;
+ }
+ }
+
+
+ // Decimal methods
+
+
+ /*
+ * abs
+ * acos
+ * acosh
+ * add
+ * asin
+ * asinh
+ * atan
+ * atanh
+ * atan2
+ * cbrt
+ * ceil
+ * clamp
+ * clone
+ * config
+ * cos
+ * cosh
+ * div
+ * exp
+ * floor
+ * hypot
+ * ln
+ * log
+ * log2
+ * log10
+ * max
+ * min
+ * mod
+ * mul
+ * pow
+ * random
+ * round
+ * set
+ * sign
+ * sin
+ * sinh
+ * sqrt
+ * sub
+ * sum
+ * tan
+ * tanh
+ * trunc
+ */
+
+
+ /*
+ * Return a new Decimal whose value is the absolute value of `x`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function abs(x) {
+ return new this(x).abs();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the arccosine in radians of `x`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function acos(x) {
+ return new this(x).acos();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the inverse of the hyperbolic cosine of `x`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function acosh(x) {
+ return new this(x).acosh();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the sum of `x` and `y`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+ function add(x, y) {
+ return new this(x).plus(y);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the arcsine in radians of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function asin(x) {
+ return new this(x).asin();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the inverse of the hyperbolic sine of `x`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function asinh(x) {
+ return new this(x).asinh();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the arctangent in radians of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function atan(x) {
+ return new this(x).atan();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the inverse of the hyperbolic tangent of `x`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function atanh(x) {
+ return new this(x).atanh();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the arctangent in radians of `y/x` in the range -pi to pi
+ * (inclusive), rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-pi, pi]
+ *
+ * y {number|string|bigint|Decimal} The y-coordinate.
+ * x {number|string|bigint|Decimal} The x-coordinate.
+ *
+ * atan2(±0, -0) = ±pi
+ * atan2(±0, +0) = ±0
+ * atan2(±0, -x) = ±pi for x > 0
+ * atan2(±0, x) = ±0 for x > 0
+ * atan2(-y, ±0) = -pi/2 for y > 0
+ * atan2(y, ±0) = pi/2 for y > 0
+ * atan2(±y, -Infinity) = ±pi for finite y > 0
+ * atan2(±y, +Infinity) = ±0 for finite y > 0
+ * atan2(±Infinity, x) = ±pi/2 for finite x
+ * atan2(±Infinity, -Infinity) = ±3*pi/4
+ * atan2(±Infinity, +Infinity) = ±pi/4
+ * atan2(NaN, x) = NaN
+ * atan2(y, NaN) = NaN
+ *
+ */
+ function atan2(y, x) {
+ y = new this(y);
+ x = new this(x);
+ var r,
+ pr = this.precision,
+ rm = this.rounding,
+ wpr = pr + 4;
+
+ // Either NaN
+ if (!y.s || !x.s) {
+ r = new this(NaN);
+
+ // Both ±Infinity
+ } else if (!y.d && !x.d) {
+ r = getPi(this, wpr, 1).times(x.s > 0 ? 0.25 : 0.75);
+ r.s = y.s;
+
+ // x is ±Infinity or y is ±0
+ } else if (!x.d || y.isZero()) {
+ r = x.s < 0 ? getPi(this, pr, rm) : new this(0);
+ r.s = y.s;
+
+ // y is ±Infinity or x is ±0
+ } else if (!y.d || x.isZero()) {
+ r = getPi(this, wpr, 1).times(0.5);
+ r.s = y.s;
+
+ // Both non-zero and finite
+ } else if (x.s < 0) {
+ this.precision = wpr;
+ this.rounding = 1;
+ r = this.atan(divide(y, x, wpr, 1));
+ x = getPi(this, wpr, 1);
+ this.precision = pr;
+ this.rounding = rm;
+ r = y.s < 0 ? r.minus(x) : r.plus(x);
+ } else {
+ r = this.atan(divide(y, x, wpr, 1));
+ }
+
+ return r;
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the cube root of `x`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function cbrt(x) {
+ return new this(x).cbrt();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` rounded to an integer using `ROUND_CEIL`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function ceil(x) {
+ return finalise(x = new this(x), x.e + 1, 2);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` clamped to the range delineated by `min` and `max`.
+ *
+ * x {number|string|bigint|Decimal}
+ * min {number|string|bigint|Decimal}
+ * max {number|string|bigint|Decimal}
+ *
+ */
+ function clamp(x, min, max) {
+ return new this(x).clamp(min, max);
+ }
+
+
+ /*
+ * Configure global settings for a Decimal constructor.
+ *
+ * `obj` is an object with one or more of the following properties,
+ *
+ * precision {number}
+ * rounding {number}
+ * toExpNeg {number}
+ * toExpPos {number}
+ * maxE {number}
+ * minE {number}
+ * modulo {number}
+ * crypto {boolean|number}
+ * defaults {true}
+ *
+ * E.g. Decimal.config({ precision: 20, rounding: 4 })
+ *
+ */
+ function config(obj) {
+ if (!obj || typeof obj !== 'object') throw Error(decimalError + 'Object expected');
+ var i, p, v,
+ useDefaults = obj.defaults === true,
+ ps = [
+ 'precision', 1, MAX_DIGITS,
+ 'rounding', 0, 8,
+ 'toExpNeg', -EXP_LIMIT, 0,
+ 'toExpPos', 0, EXP_LIMIT,
+ 'maxE', 0, EXP_LIMIT,
+ 'minE', -EXP_LIMIT, 0,
+ 'modulo', 0, 9
+ ];
+
+ for (i = 0; i < ps.length; i += 3) {
+ if (p = ps[i], useDefaults) this[p] = DEFAULTS[p];
+ if ((v = obj[p]) !== void 0) {
+ if (mathfloor(v) === v && v >= ps[i + 1] && v <= ps[i + 2]) this[p] = v;
+ else throw Error(invalidArgument + p + ': ' + v);
+ }
+ }
+
+ if (p = 'crypto', useDefaults) this[p] = DEFAULTS[p];
+ if ((v = obj[p]) !== void 0) {
+ if (v === true || v === false || v === 0 || v === 1) {
+ if (v) {
+ if (typeof crypto != 'undefined' && crypto &&
+ (crypto.getRandomValues || crypto.randomBytes)) {
+ this[p] = true;
+ } else {
+ throw Error(cryptoUnavailable);
+ }
+ } else {
+ this[p] = false;
+ }
+ } else {
+ throw Error(invalidArgument + p + ': ' + v);
+ }
+ }
+
+ return this;
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the cosine of `x`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function cos(x) {
+ return new this(x).cos();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the hyperbolic cosine of `x`, rounded to precision
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function cosh(x) {
+ return new this(x).cosh();
+ }
+
+
+ /*
+ * Create and return a Decimal constructor with the same configuration properties as this Decimal
+ * constructor.
+ *
+ */
+ function clone(obj) {
+ var i, p, ps;
+
+ /*
+ * The Decimal constructor and exported function.
+ * Return a new Decimal instance.
+ *
+ * v {number|string|bigint|Decimal} A numeric value.
+ *
+ */
+ function Decimal(v) {
+ var e, i, t,
+ x = this;
+
+ // Decimal called without new.
+ if (!(x instanceof Decimal)) return new Decimal(v);
+
+ // Retain a reference to this Decimal constructor, and shadow Decimal.prototype.constructor
+ // which points to Object.
+ x.constructor = Decimal;
+
+ if (isDecimalInstance(v)) {
+ x.s = v.s;
+
+ if (external) {
+ if (!v.d || v.e > Decimal.maxE) {
+
+ // Infinity.
+ x.e = NaN;
+ x.d = null;
+ } else if (v.e < Decimal.minE) {
+
+ // Zero.
+ x.e = 0;
+ x.d = [0];
+ } else {
+ x.e = v.e;
+ x.d = v.d.slice();
+ }
+ } else {
+ x.e = v.e;
+ x.d = v.d ? v.d.slice() : v.d;
+ }
+
+ return;
+ }
+
+ t = typeof v;
+
+ if (t === 'number') {
+ if (v === 0) {
+ x.s = 1 / v < 0 ? -1 : 1;
+ x.e = 0;
+ x.d = [0];
+ return;
+ }
+
+ if (v < 0) {
+ v = -v;
+ x.s = -1;
+ } else {
+ x.s = 1;
+ }
+
+ // Fast path for small integers.
+ if (v === ~~v && v < 1e7) {
+ for (e = 0, i = v; i >= 10; i /= 10) e++;
+
+ if (external) {
+ if (e > Decimal.maxE) {
+ x.e = NaN;
+ x.d = null;
+ } else if (e < Decimal.minE) {
+ x.e = 0;
+ x.d = [0];
+ } else {
+ x.e = e;
+ x.d = [v];
+ }
+ } else {
+ x.e = e;
+ x.d = [v];
+ }
+
+ return;
+ }
+
+ // Infinity or NaN?
+ if (v * 0 !== 0) {
+ if (!v) x.s = NaN;
+ x.e = NaN;
+ x.d = null;
+ return;
+ }
+
+ return parseDecimal(x, v.toString());
+ }
+
+ if (t === 'string') {
+ if ((i = v.charCodeAt(0)) === 45) { // minus sign
+ v = v.slice(1);
+ x.s = -1;
+ } else {
+ if (i === 43) v = v.slice(1); // plus sign
+ x.s = 1;
+ }
+
+ return isDecimal.test(v) ? parseDecimal(x, v) : parseOther(x, v);
+ }
+
+ if (t === 'bigint') {
+ if (v < 0) {
+ v = -v;
+ x.s = -1;
+ } else {
+ x.s = 1;
+ }
+
+ return parseDecimal(x, v.toString());
+ }
+
+ throw Error(invalidArgument + v);
+ }
+
+ Decimal.prototype = P;
+
+ Decimal.ROUND_UP = 0;
+ Decimal.ROUND_DOWN = 1;
+ Decimal.ROUND_CEIL = 2;
+ Decimal.ROUND_FLOOR = 3;
+ Decimal.ROUND_HALF_UP = 4;
+ Decimal.ROUND_HALF_DOWN = 5;
+ Decimal.ROUND_HALF_EVEN = 6;
+ Decimal.ROUND_HALF_CEIL = 7;
+ Decimal.ROUND_HALF_FLOOR = 8;
+ Decimal.EUCLID = 9;
+
+ Decimal.config = Decimal.set = config;
+ Decimal.clone = clone;
+ Decimal.isDecimal = isDecimalInstance;
+
+ Decimal.abs = abs;
+ Decimal.acos = acos;
+ Decimal.acosh = acosh; // ES6
+ Decimal.add = add;
+ Decimal.asin = asin;
+ Decimal.asinh = asinh; // ES6
+ Decimal.atan = atan;
+ Decimal.atanh = atanh; // ES6
+ Decimal.atan2 = atan2;
+ Decimal.cbrt = cbrt; // ES6
+ Decimal.ceil = ceil;
+ Decimal.clamp = clamp;
+ Decimal.cos = cos;
+ Decimal.cosh = cosh; // ES6
+ Decimal.div = div;
+ Decimal.exp = exp;
+ Decimal.floor = floor;
+ Decimal.hypot = hypot; // ES6
+ Decimal.ln = ln;
+ Decimal.log = log;
+ Decimal.log10 = log10; // ES6
+ Decimal.log2 = log2; // ES6
+ Decimal.max = max;
+ Decimal.min = min;
+ Decimal.mod = mod;
+ Decimal.mul = mul;
+ Decimal.pow = pow;
+ Decimal.random = random;
+ Decimal.round = round;
+ Decimal.sign = sign; // ES6
+ Decimal.sin = sin;
+ Decimal.sinh = sinh; // ES6
+ Decimal.sqrt = sqrt;
+ Decimal.sub = sub;
+ Decimal.sum = sum;
+ Decimal.tan = tan;
+ Decimal.tanh = tanh; // ES6
+ Decimal.trunc = trunc; // ES6
+
+ if (obj === void 0) obj = {};
+ if (obj) {
+ if (obj.defaults !== true) {
+ ps = ['precision', 'rounding', 'toExpNeg', 'toExpPos', 'maxE', 'minE', 'modulo', 'crypto'];
+ for (i = 0; i < ps.length;) if (!obj.hasOwnProperty(p = ps[i++])) obj[p] = this[p];
+ }
+ }
+
+ Decimal.config(obj);
+
+ return Decimal;
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` divided by `y`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+ function div(x, y) {
+ return new this(x).div(y);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the natural exponential of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} The power to which to raise the base of the natural log.
+ *
+ */
+ function exp(x) {
+ return new this(x).exp();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` round to an integer using `ROUND_FLOOR`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function floor(x) {
+ return finalise(x = new this(x), x.e + 1, 3);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the square root of the sum of the squares of the arguments,
+ * rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ * hypot(a, b, ...) = sqrt(a^2 + b^2 + ...)
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ */
+ function hypot() {
+ var i, n,
+ t = new this(0);
+
+ external = false;
+
+ for (i = 0; i < arguments.length;) {
+ n = new this(arguments[i++]);
+ if (!n.d) {
+ if (n.s) {
+ external = true;
+ return new this(1 / 0);
+ }
+ t = n;
+ } else if (t.d) {
+ t = t.plus(n.times(n));
+ }
+ }
+
+ external = true;
+
+ return t.sqrt();
+ }
+
+
+ /*
+ * Return true if object is a Decimal instance (where Decimal is any Decimal constructor),
+ * otherwise return false.
+ *
+ */
+ function isDecimalInstance(obj) {
+ return obj instanceof Decimal || obj && obj.toStringTag === tag || false;
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the natural logarithm of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function ln(x) {
+ return new this(x).ln();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the log of `x` to the base `y`, or to base 10 if no base
+ * is specified, rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ * log[y](x)
+ *
+ * x {number|string|bigint|Decimal} The argument of the logarithm.
+ * y {number|string|bigint|Decimal} The base of the logarithm.
+ *
+ */
+ function log(x, y) {
+ return new this(x).log(y);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the base 2 logarithm of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function log2(x) {
+ return new this(x).log(2);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the base 10 logarithm of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function log10(x) {
+ return new this(x).log(10);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the maximum of the arguments.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ */
+ function max() {
+ return maxOrMin(this, arguments, -1);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the minimum of the arguments.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ */
+ function min() {
+ return maxOrMin(this, arguments, 1);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` modulo `y`, rounded to `precision` significant digits
+ * using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+ function mod(x, y) {
+ return new this(x).mod(y);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` multiplied by `y`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+ function mul(x, y) {
+ return new this(x).mul(y);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` raised to the power `y`, rounded to precision
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} The base.
+ * y {number|string|bigint|Decimal} The exponent.
+ *
+ */
+ function pow(x, y) {
+ return new this(x).pow(y);
+ }
+
+
+ /*
+ * Returns a new Decimal with a random value equal to or greater than 0 and less than 1, and with
+ * `sd`, or `Decimal.precision` if `sd` is omitted, significant digits (or less if trailing zeros
+ * are produced).
+ *
+ * [sd] {number} Significant digits. Integer, 0 to MAX_DIGITS inclusive.
+ *
+ */
+ function random(sd) {
+ var d, e, k, n,
+ i = 0,
+ r = new this(1),
+ rd = [];
+
+ if (sd === void 0) sd = this.precision;
+ else checkInt32(sd, 1, MAX_DIGITS);
+
+ k = Math.ceil(sd / LOG_BASE);
+
+ if (!this.crypto) {
+ for (; i < k;) rd[i++] = Math.random() * 1e7 | 0;
+
+ // Browsers supporting crypto.getRandomValues.
+ } else if (crypto.getRandomValues) {
+ d = crypto.getRandomValues(new Uint32Array(k));
+
+ for (; i < k;) {
+ n = d[i];
+
+ // 0 <= n < 4294967296
+ // Probability n >= 4.29e9, is 4967296 / 4294967296 = 0.00116 (1 in 865).
+ if (n >= 4.29e9) {
+ d[i] = crypto.getRandomValues(new Uint32Array(1))[0];
+ } else {
+
+ // 0 <= n <= 4289999999
+ // 0 <= (n % 1e7) <= 9999999
+ rd[i++] = n % 1e7;
+ }
+ }
+
+ // Node.js supporting crypto.randomBytes.
+ } else if (crypto.randomBytes) {
+
+ // buffer
+ d = crypto.randomBytes(k *= 4);
+
+ for (; i < k;) {
+
+ // 0 <= n < 2147483648
+ n = d[i] + (d[i + 1] << 8) + (d[i + 2] << 16) + ((d[i + 3] & 0x7f) << 24);
+
+ // Probability n >= 2.14e9, is 7483648 / 2147483648 = 0.0035 (1 in 286).
+ if (n >= 2.14e9) {
+ crypto.randomBytes(4).copy(d, i);
+ } else {
+
+ // 0 <= n <= 2139999999
+ // 0 <= (n % 1e7) <= 9999999
+ rd.push(n % 1e7);
+ i += 4;
+ }
+ }
+
+ i = k / 4;
+ } else {
+ throw Error(cryptoUnavailable);
+ }
+
+ k = rd[--i];
+ sd %= LOG_BASE;
+
+ // Convert trailing digits to zeros according to sd.
+ if (k && sd) {
+ n = mathpow(10, LOG_BASE - sd);
+ rd[i] = (k / n | 0) * n;
+ }
+
+ // Remove trailing words which are zero.
+ for (; rd[i] === 0; i--) rd.pop();
+
+ // Zero?
+ if (i < 0) {
+ e = 0;
+ rd = [0];
+ } else {
+ e = -1;
+
+ // Remove leading words which are zero and adjust exponent accordingly.
+ for (; rd[0] === 0; e -= LOG_BASE) rd.shift();
+
+ // Count the digits of the first word of rd to determine leading zeros.
+ for (k = 1, n = rd[0]; n >= 10; n /= 10) k++;
+
+ // Adjust the exponent for leading zeros of the first word of rd.
+ if (k < LOG_BASE) e -= LOG_BASE - k;
+ }
+
+ r.e = e;
+ r.d = rd;
+
+ return r;
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` rounded to an integer using rounding mode `rounding`.
+ *
+ * To emulate `Math.round`, set rounding to 7 (ROUND_HALF_CEIL).
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function round(x) {
+ return finalise(x = new this(x), x.e + 1, this.rounding);
+ }
+
+
+ /*
+ * Return
+ * 1 if x > 0,
+ * -1 if x < 0,
+ * 0 if x is 0,
+ * -0 if x is -0,
+ * NaN otherwise
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function sign(x) {
+ x = new this(x);
+ return x.d ? (x.d[0] ? x.s : 0 * x.s) : x.s || NaN;
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the sine of `x`, rounded to `precision` significant digits
+ * using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function sin(x) {
+ return new this(x).sin();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the hyperbolic sine of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function sinh(x) {
+ return new this(x).sinh();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the square root of `x`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function sqrt(x) {
+ return new this(x).sqrt();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` minus `y`, rounded to `precision` significant digits
+ * using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+ function sub(x, y) {
+ return new this(x).sub(y);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the sum of the arguments, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * Only the result is rounded, not the intermediate calculations.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ */
+ function sum() {
+ var i = 0,
+ args = arguments,
+ x = new this(args[i]);
+
+ external = false;
+ for (; x.s && ++i < args.length;) x = x.plus(args[i]);
+ external = true;
+
+ return finalise(x, this.precision, this.rounding);
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the tangent of `x`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function tan(x) {
+ return new this(x).tan();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is the hyperbolic tangent of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+ function tanh(x) {
+ return new this(x).tanh();
+ }
+
+
+ /*
+ * Return a new Decimal whose value is `x` truncated to an integer.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+ function trunc(x) {
+ return finalise(x = new this(x), x.e + 1, 1);
+ }
+
+
+ // Create and configure initial Decimal constructor.
+ Decimal = clone(DEFAULTS);
+ Decimal.prototype.constructor = Decimal;
+ Decimal['default'] = Decimal.Decimal = Decimal;
+
+ // Create the internal constants from their string values.
+ LN10 = new Decimal(LN10);
+ PI = new Decimal(PI);
+
+
+ // Export.
+
+
+ // AMD.
+ if (typeof define == 'function' && define.amd) {
+ define(function () {
+ return Decimal;
+ });
+
+ // Node and other environments that support module.exports.
+ } else if (typeof module != 'undefined' && module.exports) {
+ if (typeof Symbol == 'function' && typeof Symbol.iterator == 'symbol') {
+ P[Symbol['for']('nodejs.util.inspect.custom')] = P.toString;
+ P[Symbol.toStringTag] = 'Decimal';
+ }
+
+ module.exports = Decimal;
+
+ // Browser.
+ } else {
+ if (!globalScope) {
+ globalScope = typeof self != 'undefined' && self && self.self == self ? self : window;
+ }
+
+ noConflict = globalScope.Decimal;
+ Decimal.noConflict = function () {
+ globalScope.Decimal = noConflict;
+ return Decimal;
+ };
+
+ globalScope.Decimal = Decimal;
+ }
+})(this);
diff --git a/node_modules/decimal.js/decimal.mjs b/node_modules/decimal.js/decimal.mjs
new file mode 100644
index 0000000..71ed02e
--- /dev/null
+++ b/node_modules/decimal.js/decimal.mjs
@@ -0,0 +1,4914 @@
+/*!
+ * decimal.js v10.6.0
+ * An arbitrary-precision Decimal type for JavaScript.
+ * https://github.com/MikeMcl/decimal.js
+ * Copyright (c) 2025 Michael Mclaughlin
+ * MIT Licence
+ */
+
+
+// ----------------------------------- EDITABLE DEFAULTS ------------------------------------ //
+
+
+ // The maximum exponent magnitude.
+ // The limit on the value of `toExpNeg`, `toExpPos`, `minE` and `maxE`.
+var EXP_LIMIT = 9e15, // 0 to 9e15
+
+ // The limit on the value of `precision`, and on the value of the first argument to
+ // `toDecimalPlaces`, `toExponential`, `toFixed`, `toPrecision` and `toSignificantDigits`.
+ MAX_DIGITS = 1e9, // 0 to 1e9
+
+ // Base conversion alphabet.
+ NUMERALS = '0123456789abcdef',
+
+ // The natural logarithm of 10 (1025 digits).
+ LN10 = '2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983419677840422862486334095254650828067566662873690987816894829072083255546808437998948262331985283935053089653777326288461633662222876982198867465436674744042432743651550489343149393914796194044002221051017141748003688084012647080685567743216228355220114804663715659121373450747856947683463616792101806445070648000277502684916746550586856935673420670581136429224554405758925724208241314695689016758940256776311356919292033376587141660230105703089634572075440370847469940168269282808481184289314848524948644871927809676271275775397027668605952496716674183485704422507197965004714951050492214776567636938662976979522110718264549734772662425709429322582798502585509785265383207606726317164309505995087807523710333101197857547331541421808427543863591778117054309827482385045648019095610299291824318237525357709750539565187697510374970888692180205189339507238539205144634197265287286965110862571492198849978748873771345686209167058',
+
+ // Pi (1025 digits).
+ PI = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632789',
+
+
+ // The initial configuration properties of the Decimal constructor.
+ DEFAULTS = {
+
+ // These values must be integers within the stated ranges (inclusive).
+ // Most of these values can be changed at run-time using the `Decimal.config` method.
+
+ // The maximum number of significant digits of the result of a calculation or base conversion.
+ // E.g. `Decimal.config({ precision: 20 });`
+ precision: 20, // 1 to MAX_DIGITS
+
+ // The rounding mode used when rounding to `precision`.
+ //
+ // ROUND_UP 0 Away from zero.
+ // ROUND_DOWN 1 Towards zero.
+ // ROUND_CEIL 2 Towards +Infinity.
+ // ROUND_FLOOR 3 Towards -Infinity.
+ // ROUND_HALF_UP 4 Towards nearest neighbour. If equidistant, up.
+ // ROUND_HALF_DOWN 5 Towards nearest neighbour. If equidistant, down.
+ // ROUND_HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour.
+ // ROUND_HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity.
+ // ROUND_HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity.
+ //
+ // E.g.
+ // `Decimal.rounding = 4;`
+ // `Decimal.rounding = Decimal.ROUND_HALF_UP;`
+ rounding: 4, // 0 to 8
+
+ // The modulo mode used when calculating the modulus: a mod n.
+ // The quotient (q = a / n) is calculated according to the corresponding rounding mode.
+ // The remainder (r) is calculated as: r = a - n * q.
+ //
+ // UP 0 The remainder is positive if the dividend is negative, else is negative.
+ // DOWN 1 The remainder has the same sign as the dividend (JavaScript %).
+ // FLOOR 3 The remainder has the same sign as the divisor (Python %).
+ // HALF_EVEN 6 The IEEE 754 remainder function.
+ // EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). Always positive.
+ //
+ // Truncated division (1), floored division (3), the IEEE 754 remainder (6), and Euclidian
+ // division (9) are commonly used for the modulus operation. The other rounding modes can also
+ // be used, but they may not give useful results.
+ modulo: 1, // 0 to 9
+
+ // The exponent value at and beneath which `toString` returns exponential notation.
+ // JavaScript numbers: -7
+ toExpNeg: -7, // 0 to -EXP_LIMIT
+
+ // The exponent value at and above which `toString` returns exponential notation.
+ // JavaScript numbers: 21
+ toExpPos: 21, // 0 to EXP_LIMIT
+
+ // The minimum exponent value, beneath which underflow to zero occurs.
+ // JavaScript numbers: -324 (5e-324)
+ minE: -EXP_LIMIT, // -1 to -EXP_LIMIT
+
+ // The maximum exponent value, above which overflow to Infinity occurs.
+ // JavaScript numbers: 308 (1.7976931348623157e+308)
+ maxE: EXP_LIMIT, // 1 to EXP_LIMIT
+
+ // Whether to use cryptographically-secure random number generation, if available.
+ crypto: false // true/false
+ },
+
+
+// ----------------------------------- END OF EDITABLE DEFAULTS ------------------------------- //
+
+
+ inexact, quadrant,
+ external = true,
+
+ decimalError = '[DecimalError] ',
+ invalidArgument = decimalError + 'Invalid argument: ',
+ precisionLimitExceeded = decimalError + 'Precision limit exceeded',
+ cryptoUnavailable = decimalError + 'crypto unavailable',
+ tag = '[object Decimal]',
+
+ mathfloor = Math.floor,
+ mathpow = Math.pow,
+
+ isBinary = /^0b([01]+(\.[01]*)?|\.[01]+)(p[+-]?\d+)?$/i,
+ isHex = /^0x([0-9a-f]+(\.[0-9a-f]*)?|\.[0-9a-f]+)(p[+-]?\d+)?$/i,
+ isOctal = /^0o([0-7]+(\.[0-7]*)?|\.[0-7]+)(p[+-]?\d+)?$/i,
+ isDecimal = /^(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,
+
+ BASE = 1e7,
+ LOG_BASE = 7,
+ MAX_SAFE_INTEGER = 9007199254740991,
+
+ LN10_PRECISION = LN10.length - 1,
+ PI_PRECISION = PI.length - 1,
+
+ // Decimal.prototype object
+ P = { toStringTag: tag };
+
+
+// Decimal prototype methods
+
+
+/*
+ * absoluteValue abs
+ * ceil
+ * clampedTo clamp
+ * comparedTo cmp
+ * cosine cos
+ * cubeRoot cbrt
+ * decimalPlaces dp
+ * dividedBy div
+ * dividedToIntegerBy divToInt
+ * equals eq
+ * floor
+ * greaterThan gt
+ * greaterThanOrEqualTo gte
+ * hyperbolicCosine cosh
+ * hyperbolicSine sinh
+ * hyperbolicTangent tanh
+ * inverseCosine acos
+ * inverseHyperbolicCosine acosh
+ * inverseHyperbolicSine asinh
+ * inverseHyperbolicTangent atanh
+ * inverseSine asin
+ * inverseTangent atan
+ * isFinite
+ * isInteger isInt
+ * isNaN
+ * isNegative isNeg
+ * isPositive isPos
+ * isZero
+ * lessThan lt
+ * lessThanOrEqualTo lte
+ * logarithm log
+ * [maximum] [max]
+ * [minimum] [min]
+ * minus sub
+ * modulo mod
+ * naturalExponential exp
+ * naturalLogarithm ln
+ * negated neg
+ * plus add
+ * precision sd
+ * round
+ * sine sin
+ * squareRoot sqrt
+ * tangent tan
+ * times mul
+ * toBinary
+ * toDecimalPlaces toDP
+ * toExponential
+ * toFixed
+ * toFraction
+ * toHexadecimal toHex
+ * toNearest
+ * toNumber
+ * toOctal
+ * toPower pow
+ * toPrecision
+ * toSignificantDigits toSD
+ * toString
+ * truncated trunc
+ * valueOf toJSON
+ */
+
+
+/*
+ * Return a new Decimal whose value is the absolute value of this Decimal.
+ *
+ */
+P.absoluteValue = P.abs = function () {
+ var x = new this.constructor(this);
+ if (x.s < 0) x.s = 1;
+ return finalise(x);
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a whole number in the
+ * direction of positive Infinity.
+ *
+ */
+P.ceil = function () {
+ return finalise(new this.constructor(this), this.e + 1, 2);
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal clamped to the range
+ * delineated by `min` and `max`.
+ *
+ * min {number|string|bigint|Decimal}
+ * max {number|string|bigint|Decimal}
+ *
+ */
+P.clampedTo = P.clamp = function (min, max) {
+ var k,
+ x = this,
+ Ctor = x.constructor;
+ min = new Ctor(min);
+ max = new Ctor(max);
+ if (!min.s || !max.s) return new Ctor(NaN);
+ if (min.gt(max)) throw Error(invalidArgument + max);
+ k = x.cmp(min);
+ return k < 0 ? min : x.cmp(max) > 0 ? max : new Ctor(x);
+};
+
+
+/*
+ * Return
+ * 1 if the value of this Decimal is greater than the value of `y`,
+ * -1 if the value of this Decimal is less than the value of `y`,
+ * 0 if they have the same value,
+ * NaN if the value of either Decimal is NaN.
+ *
+ */
+P.comparedTo = P.cmp = function (y) {
+ var i, j, xdL, ydL,
+ x = this,
+ xd = x.d,
+ yd = (y = new x.constructor(y)).d,
+ xs = x.s,
+ ys = y.s;
+
+ // Either NaN or ±Infinity?
+ if (!xd || !yd) {
+ return !xs || !ys ? NaN : xs !== ys ? xs : xd === yd ? 0 : !xd ^ xs < 0 ? 1 : -1;
+ }
+
+ // Either zero?
+ if (!xd[0] || !yd[0]) return xd[0] ? xs : yd[0] ? -ys : 0;
+
+ // Signs differ?
+ if (xs !== ys) return xs;
+
+ // Compare exponents.
+ if (x.e !== y.e) return x.e > y.e ^ xs < 0 ? 1 : -1;
+
+ xdL = xd.length;
+ ydL = yd.length;
+
+ // Compare digit by digit.
+ for (i = 0, j = xdL < ydL ? xdL : ydL; i < j; ++i) {
+ if (xd[i] !== yd[i]) return xd[i] > yd[i] ^ xs < 0 ? 1 : -1;
+ }
+
+ // Compare lengths.
+ return xdL === ydL ? 0 : xdL > ydL ^ xs < 0 ? 1 : -1;
+};
+
+
+/*
+ * Return a new Decimal whose value is the cosine of the value in radians of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-1, 1]
+ *
+ * cos(0) = 1
+ * cos(-0) = 1
+ * cos(Infinity) = NaN
+ * cos(-Infinity) = NaN
+ * cos(NaN) = NaN
+ *
+ */
+P.cosine = P.cos = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.d) return new Ctor(NaN);
+
+ // cos(0) = cos(-0) = 1
+ if (!x.d[0]) return new Ctor(1);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(x.e, x.sd()) + LOG_BASE;
+ Ctor.rounding = 1;
+
+ x = cosine(Ctor, toLessThanHalfPi(Ctor, x));
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return finalise(quadrant == 2 || quadrant == 3 ? x.neg() : x, pr, rm, true);
+};
+
+
+/*
+ *
+ * Return a new Decimal whose value is the cube root of the value of this Decimal, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * cbrt(0) = 0
+ * cbrt(-0) = -0
+ * cbrt(1) = 1
+ * cbrt(-1) = -1
+ * cbrt(N) = N
+ * cbrt(-I) = -I
+ * cbrt(I) = I
+ *
+ * Math.cbrt(x) = (x < 0 ? -Math.pow(-x, 1/3) : Math.pow(x, 1/3))
+ *
+ */
+P.cubeRoot = P.cbrt = function () {
+ var e, m, n, r, rep, s, sd, t, t3, t3plusx,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite() || x.isZero()) return new Ctor(x);
+ external = false;
+
+ // Initial estimate.
+ s = x.s * mathpow(x.s * x, 1 / 3);
+
+ // Math.cbrt underflow/overflow?
+ // Pass x to Math.pow as integer, then adjust the exponent of the result.
+ if (!s || Math.abs(s) == 1 / 0) {
+ n = digitsToString(x.d);
+ e = x.e;
+
+ // Adjust n exponent so it is a multiple of 3 away from x exponent.
+ if (s = (e - n.length + 1) % 3) n += (s == 1 || s == -2 ? '0' : '00');
+ s = mathpow(n, 1 / 3);
+
+ // Rarely, e may be one less than the result exponent value.
+ e = mathfloor((e + 1) / 3) - (e % 3 == (e < 0 ? -1 : 2));
+
+ if (s == 1 / 0) {
+ n = '5e' + e;
+ } else {
+ n = s.toExponential();
+ n = n.slice(0, n.indexOf('e') + 1) + e;
+ }
+
+ r = new Ctor(n);
+ r.s = x.s;
+ } else {
+ r = new Ctor(s.toString());
+ }
+
+ sd = (e = Ctor.precision) + 3;
+
+ // Halley's method.
+ // TODO? Compare Newton's method.
+ for (;;) {
+ t = r;
+ t3 = t.times(t).times(t);
+ t3plusx = t3.plus(x);
+ r = divide(t3plusx.plus(x).times(t), t3plusx.plus(t3), sd + 2, 1);
+
+ // TODO? Replace with for-loop and checkRoundingDigits.
+ if (digitsToString(t.d).slice(0, sd) === (n = digitsToString(r.d)).slice(0, sd)) {
+ n = n.slice(sd - 3, sd + 1);
+
+ // The 4th rounding digit may be in error by -1 so if the 4 rounding digits are 9999 or 4999
+ // , i.e. approaching a rounding boundary, continue the iteration.
+ if (n == '9999' || !rep && n == '4999') {
+
+ // On the first iteration only, check to see if rounding up gives the exact result as the
+ // nines may infinitely repeat.
+ if (!rep) {
+ finalise(t, e + 1, 0);
+
+ if (t.times(t).times(t).eq(x)) {
+ r = t;
+ break;
+ }
+ }
+
+ sd += 4;
+ rep = 1;
+ } else {
+
+ // If the rounding digits are null, 0{0,4} or 50{0,3}, check for an exact result.
+ // If not, then there are further digits and m will be truthy.
+ if (!+n || !+n.slice(1) && n.charAt(0) == '5') {
+
+ // Truncate to the first rounding digit.
+ finalise(r, e + 1, 1);
+ m = !r.times(r).times(r).eq(x);
+ }
+
+ break;
+ }
+ }
+ }
+
+ external = true;
+
+ return finalise(r, e, Ctor.rounding, m);
+};
+
+
+/*
+ * Return the number of decimal places of the value of this Decimal.
+ *
+ */
+P.decimalPlaces = P.dp = function () {
+ var w,
+ d = this.d,
+ n = NaN;
+
+ if (d) {
+ w = d.length - 1;
+ n = (w - mathfloor(this.e / LOG_BASE)) * LOG_BASE;
+
+ // Subtract the number of trailing zeros of the last word.
+ w = d[w];
+ if (w) for (; w % 10 == 0; w /= 10) n--;
+ if (n < 0) n = 0;
+ }
+
+ return n;
+};
+
+
+/*
+ * n / 0 = I
+ * n / N = N
+ * n / I = 0
+ * 0 / n = 0
+ * 0 / 0 = N
+ * 0 / N = N
+ * 0 / I = 0
+ * N / n = N
+ * N / 0 = N
+ * N / N = N
+ * N / I = N
+ * I / n = I
+ * I / 0 = I
+ * I / N = N
+ * I / I = N
+ *
+ * Return a new Decimal whose value is the value of this Decimal divided by `y`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ */
+P.dividedBy = P.div = function (y) {
+ return divide(this, new this.constructor(y));
+};
+
+
+/*
+ * Return a new Decimal whose value is the integer part of dividing the value of this Decimal
+ * by the value of `y`, rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ */
+P.dividedToIntegerBy = P.divToInt = function (y) {
+ var x = this,
+ Ctor = x.constructor;
+ return finalise(divide(x, new Ctor(y), 0, 1, 1), Ctor.precision, Ctor.rounding);
+};
+
+
+/*
+ * Return true if the value of this Decimal is equal to the value of `y`, otherwise return false.
+ *
+ */
+P.equals = P.eq = function (y) {
+ return this.cmp(y) === 0;
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a whole number in the
+ * direction of negative Infinity.
+ *
+ */
+P.floor = function () {
+ return finalise(new this.constructor(this), this.e + 1, 3);
+};
+
+
+/*
+ * Return true if the value of this Decimal is greater than the value of `y`, otherwise return
+ * false.
+ *
+ */
+P.greaterThan = P.gt = function (y) {
+ return this.cmp(y) > 0;
+};
+
+
+/*
+ * Return true if the value of this Decimal is greater than or equal to the value of `y`,
+ * otherwise return false.
+ *
+ */
+P.greaterThanOrEqualTo = P.gte = function (y) {
+ var k = this.cmp(y);
+ return k == 1 || k === 0;
+};
+
+
+/*
+ * Return a new Decimal whose value is the hyperbolic cosine of the value in radians of this
+ * Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [1, Infinity]
+ *
+ * cosh(x) = 1 + x^2/2! + x^4/4! + x^6/6! + ...
+ *
+ * cosh(0) = 1
+ * cosh(-0) = 1
+ * cosh(Infinity) = Infinity
+ * cosh(-Infinity) = Infinity
+ * cosh(NaN) = NaN
+ *
+ * x time taken (ms) result
+ * 1000 9 9.8503555700852349694e+433
+ * 10000 25 4.4034091128314607936e+4342
+ * 100000 171 1.4033316802130615897e+43429
+ * 1000000 3817 1.5166076984010437725e+434294
+ * 10000000 abandoned after 2 minute wait
+ *
+ * TODO? Compare performance of cosh(x) = 0.5 * (exp(x) + exp(-x))
+ *
+ */
+P.hyperbolicCosine = P.cosh = function () {
+ var k, n, pr, rm, len,
+ x = this,
+ Ctor = x.constructor,
+ one = new Ctor(1);
+
+ if (!x.isFinite()) return new Ctor(x.s ? 1 / 0 : NaN);
+ if (x.isZero()) return one;
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(x.e, x.sd()) + 4;
+ Ctor.rounding = 1;
+ len = x.d.length;
+
+ // Argument reduction: cos(4x) = 1 - 8cos^2(x) + 8cos^4(x) + 1
+ // i.e. cos(x) = 1 - cos^2(x/4)(8 - 8cos^2(x/4))
+
+ // Estimate the optimum number of times to use the argument reduction.
+ // TODO? Estimation reused from cosine() and may not be optimal here.
+ if (len < 32) {
+ k = Math.ceil(len / 3);
+ n = (1 / tinyPow(4, k)).toString();
+ } else {
+ k = 16;
+ n = '2.3283064365386962890625e-10';
+ }
+
+ x = taylorSeries(Ctor, 1, x.times(n), new Ctor(1), true);
+
+ // Reverse argument reduction
+ var cosh2_x,
+ i = k,
+ d8 = new Ctor(8);
+ for (; i--;) {
+ cosh2_x = x.times(x);
+ x = one.minus(cosh2_x.times(d8.minus(cosh2_x.times(d8))));
+ }
+
+ return finalise(x, Ctor.precision = pr, Ctor.rounding = rm, true);
+};
+
+
+/*
+ * Return a new Decimal whose value is the hyperbolic sine of the value in radians of this
+ * Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-Infinity, Infinity]
+ *
+ * sinh(x) = x + x^3/3! + x^5/5! + x^7/7! + ...
+ *
+ * sinh(0) = 0
+ * sinh(-0) = -0
+ * sinh(Infinity) = Infinity
+ * sinh(-Infinity) = -Infinity
+ * sinh(NaN) = NaN
+ *
+ * x time taken (ms)
+ * 10 2 ms
+ * 100 5 ms
+ * 1000 14 ms
+ * 10000 82 ms
+ * 100000 886 ms 1.4033316802130615897e+43429
+ * 200000 2613 ms
+ * 300000 5407 ms
+ * 400000 8824 ms
+ * 500000 13026 ms 8.7080643612718084129e+217146
+ * 1000000 48543 ms
+ *
+ * TODO? Compare performance of sinh(x) = 0.5 * (exp(x) - exp(-x))
+ *
+ */
+P.hyperbolicSine = P.sinh = function () {
+ var k, pr, rm, len,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite() || x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(x.e, x.sd()) + 4;
+ Ctor.rounding = 1;
+ len = x.d.length;
+
+ if (len < 3) {
+ x = taylorSeries(Ctor, 2, x, x, true);
+ } else {
+
+ // Alternative argument reduction: sinh(3x) = sinh(x)(3 + 4sinh^2(x))
+ // i.e. sinh(x) = sinh(x/3)(3 + 4sinh^2(x/3))
+ // 3 multiplications and 1 addition
+
+ // Argument reduction: sinh(5x) = sinh(x)(5 + sinh^2(x)(20 + 16sinh^2(x)))
+ // i.e. sinh(x) = sinh(x/5)(5 + sinh^2(x/5)(20 + 16sinh^2(x/5)))
+ // 4 multiplications and 2 additions
+
+ // Estimate the optimum number of times to use the argument reduction.
+ k = 1.4 * Math.sqrt(len);
+ k = k > 16 ? 16 : k | 0;
+
+ x = x.times(1 / tinyPow(5, k));
+ x = taylorSeries(Ctor, 2, x, x, true);
+
+ // Reverse argument reduction
+ var sinh2_x,
+ d5 = new Ctor(5),
+ d16 = new Ctor(16),
+ d20 = new Ctor(20);
+ for (; k--;) {
+ sinh2_x = x.times(x);
+ x = x.times(d5.plus(sinh2_x.times(d16.times(sinh2_x).plus(d20))));
+ }
+ }
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return finalise(x, pr, rm, true);
+};
+
+
+/*
+ * Return a new Decimal whose value is the hyperbolic tangent of the value in radians of this
+ * Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-1, 1]
+ *
+ * tanh(x) = sinh(x) / cosh(x)
+ *
+ * tanh(0) = 0
+ * tanh(-0) = -0
+ * tanh(Infinity) = 1
+ * tanh(-Infinity) = -1
+ * tanh(NaN) = NaN
+ *
+ */
+P.hyperbolicTangent = P.tanh = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite()) return new Ctor(x.s);
+ if (x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + 7;
+ Ctor.rounding = 1;
+
+ return divide(x.sinh(), x.cosh(), Ctor.precision = pr, Ctor.rounding = rm);
+};
+
+
+/*
+ * Return a new Decimal whose value is the arccosine (inverse cosine) in radians of the value of
+ * this Decimal.
+ *
+ * Domain: [-1, 1]
+ * Range: [0, pi]
+ *
+ * acos(x) = pi/2 - asin(x)
+ *
+ * acos(0) = pi/2
+ * acos(-0) = pi/2
+ * acos(1) = 0
+ * acos(-1) = pi
+ * acos(1/2) = pi/3
+ * acos(-1/2) = 2*pi/3
+ * acos(|x| > 1) = NaN
+ * acos(NaN) = NaN
+ *
+ */
+P.inverseCosine = P.acos = function () {
+ var x = this,
+ Ctor = x.constructor,
+ k = x.abs().cmp(1),
+ pr = Ctor.precision,
+ rm = Ctor.rounding;
+
+ if (k !== -1) {
+ return k === 0
+ // |x| is 1
+ ? x.isNeg() ? getPi(Ctor, pr, rm) : new Ctor(0)
+ // |x| > 1 or x is NaN
+ : new Ctor(NaN);
+ }
+
+ if (x.isZero()) return getPi(Ctor, pr + 4, rm).times(0.5);
+
+ // TODO? Special case acos(0.5) = pi/3 and acos(-0.5) = 2*pi/3
+
+ Ctor.precision = pr + 6;
+ Ctor.rounding = 1;
+
+ // See https://github.com/MikeMcl/decimal.js/pull/217
+ x = new Ctor(1).minus(x).div(x.plus(1)).sqrt().atan();
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.times(2);
+};
+
+
+/*
+ * Return a new Decimal whose value is the inverse of the hyperbolic cosine in radians of the
+ * value of this Decimal.
+ *
+ * Domain: [1, Infinity]
+ * Range: [0, Infinity]
+ *
+ * acosh(x) = ln(x + sqrt(x^2 - 1))
+ *
+ * acosh(x < 1) = NaN
+ * acosh(NaN) = NaN
+ * acosh(Infinity) = Infinity
+ * acosh(-Infinity) = NaN
+ * acosh(0) = NaN
+ * acosh(-0) = NaN
+ * acosh(1) = 0
+ * acosh(-1) = NaN
+ *
+ */
+P.inverseHyperbolicCosine = P.acosh = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (x.lte(1)) return new Ctor(x.eq(1) ? 0 : NaN);
+ if (!x.isFinite()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(Math.abs(x.e), x.sd()) + 4;
+ Ctor.rounding = 1;
+ external = false;
+
+ x = x.times(x).minus(1).sqrt().plus(x);
+
+ external = true;
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.ln();
+};
+
+
+/*
+ * Return a new Decimal whose value is the inverse of the hyperbolic sine in radians of the value
+ * of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-Infinity, Infinity]
+ *
+ * asinh(x) = ln(x + sqrt(x^2 + 1))
+ *
+ * asinh(NaN) = NaN
+ * asinh(Infinity) = Infinity
+ * asinh(-Infinity) = -Infinity
+ * asinh(0) = 0
+ * asinh(-0) = -0
+ *
+ */
+P.inverseHyperbolicSine = P.asinh = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite() || x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + 2 * Math.max(Math.abs(x.e), x.sd()) + 6;
+ Ctor.rounding = 1;
+ external = false;
+
+ x = x.times(x).plus(1).sqrt().plus(x);
+
+ external = true;
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.ln();
+};
+
+
+/*
+ * Return a new Decimal whose value is the inverse of the hyperbolic tangent in radians of the
+ * value of this Decimal.
+ *
+ * Domain: [-1, 1]
+ * Range: [-Infinity, Infinity]
+ *
+ * atanh(x) = 0.5 * ln((1 + x) / (1 - x))
+ *
+ * atanh(|x| > 1) = NaN
+ * atanh(NaN) = NaN
+ * atanh(Infinity) = NaN
+ * atanh(-Infinity) = NaN
+ * atanh(0) = 0
+ * atanh(-0) = -0
+ * atanh(1) = Infinity
+ * atanh(-1) = -Infinity
+ *
+ */
+P.inverseHyperbolicTangent = P.atanh = function () {
+ var pr, rm, wpr, xsd,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite()) return new Ctor(NaN);
+ if (x.e >= 0) return new Ctor(x.abs().eq(1) ? x.s / 0 : x.isZero() ? x : NaN);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ xsd = x.sd();
+
+ if (Math.max(xsd, pr) < 2 * -x.e - 1) return finalise(new Ctor(x), pr, rm, true);
+
+ Ctor.precision = wpr = xsd - x.e;
+
+ x = divide(x.plus(1), new Ctor(1).minus(x), wpr + pr, 1);
+
+ Ctor.precision = pr + 4;
+ Ctor.rounding = 1;
+
+ x = x.ln();
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.times(0.5);
+};
+
+
+/*
+ * Return a new Decimal whose value is the arcsine (inverse sine) in radians of the value of this
+ * Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-pi/2, pi/2]
+ *
+ * asin(x) = 2*atan(x/(1 + sqrt(1 - x^2)))
+ *
+ * asin(0) = 0
+ * asin(-0) = -0
+ * asin(1/2) = pi/6
+ * asin(-1/2) = -pi/6
+ * asin(1) = pi/2
+ * asin(-1) = -pi/2
+ * asin(|x| > 1) = NaN
+ * asin(NaN) = NaN
+ *
+ * TODO? Compare performance of Taylor series.
+ *
+ */
+P.inverseSine = P.asin = function () {
+ var halfPi, k,
+ pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (x.isZero()) return new Ctor(x);
+
+ k = x.abs().cmp(1);
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+
+ if (k !== -1) {
+
+ // |x| is 1
+ if (k === 0) {
+ halfPi = getPi(Ctor, pr + 4, rm).times(0.5);
+ halfPi.s = x.s;
+ return halfPi;
+ }
+
+ // |x| > 1 or x is NaN
+ return new Ctor(NaN);
+ }
+
+ // TODO? Special case asin(1/2) = pi/6 and asin(-1/2) = -pi/6
+
+ Ctor.precision = pr + 6;
+ Ctor.rounding = 1;
+
+ x = x.div(new Ctor(1).minus(x.times(x)).sqrt().plus(1)).atan();
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return x.times(2);
+};
+
+
+/*
+ * Return a new Decimal whose value is the arctangent (inverse tangent) in radians of the value
+ * of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-pi/2, pi/2]
+ *
+ * atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ...
+ *
+ * atan(0) = 0
+ * atan(-0) = -0
+ * atan(1) = pi/4
+ * atan(-1) = -pi/4
+ * atan(Infinity) = pi/2
+ * atan(-Infinity) = -pi/2
+ * atan(NaN) = NaN
+ *
+ */
+P.inverseTangent = P.atan = function () {
+ var i, j, k, n, px, t, r, wpr, x2,
+ x = this,
+ Ctor = x.constructor,
+ pr = Ctor.precision,
+ rm = Ctor.rounding;
+
+ if (!x.isFinite()) {
+ if (!x.s) return new Ctor(NaN);
+ if (pr + 4 <= PI_PRECISION) {
+ r = getPi(Ctor, pr + 4, rm).times(0.5);
+ r.s = x.s;
+ return r;
+ }
+ } else if (x.isZero()) {
+ return new Ctor(x);
+ } else if (x.abs().eq(1) && pr + 4 <= PI_PRECISION) {
+ r = getPi(Ctor, pr + 4, rm).times(0.25);
+ r.s = x.s;
+ return r;
+ }
+
+ Ctor.precision = wpr = pr + 10;
+ Ctor.rounding = 1;
+
+ // TODO? if (x >= 1 && pr <= PI_PRECISION) atan(x) = halfPi * x.s - atan(1 / x);
+
+ // Argument reduction
+ // Ensure |x| < 0.42
+ // atan(x) = 2 * atan(x / (1 + sqrt(1 + x^2)))
+
+ k = Math.min(28, wpr / LOG_BASE + 2 | 0);
+
+ for (i = k; i; --i) x = x.div(x.times(x).plus(1).sqrt().plus(1));
+
+ external = false;
+
+ j = Math.ceil(wpr / LOG_BASE);
+ n = 1;
+ x2 = x.times(x);
+ r = new Ctor(x);
+ px = x;
+
+ // atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ...
+ for (; i !== -1;) {
+ px = px.times(x2);
+ t = r.minus(px.div(n += 2));
+
+ px = px.times(x2);
+ r = t.plus(px.div(n += 2));
+
+ if (r.d[j] !== void 0) for (i = j; r.d[i] === t.d[i] && i--;);
+ }
+
+ if (k) r = r.times(2 << (k - 1));
+
+ external = true;
+
+ return finalise(r, Ctor.precision = pr, Ctor.rounding = rm, true);
+};
+
+
+/*
+ * Return true if the value of this Decimal is a finite number, otherwise return false.
+ *
+ */
+P.isFinite = function () {
+ return !!this.d;
+};
+
+
+/*
+ * Return true if the value of this Decimal is an integer, otherwise return false.
+ *
+ */
+P.isInteger = P.isInt = function () {
+ return !!this.d && mathfloor(this.e / LOG_BASE) > this.d.length - 2;
+};
+
+
+/*
+ * Return true if the value of this Decimal is NaN, otherwise return false.
+ *
+ */
+P.isNaN = function () {
+ return !this.s;
+};
+
+
+/*
+ * Return true if the value of this Decimal is negative, otherwise return false.
+ *
+ */
+P.isNegative = P.isNeg = function () {
+ return this.s < 0;
+};
+
+
+/*
+ * Return true if the value of this Decimal is positive, otherwise return false.
+ *
+ */
+P.isPositive = P.isPos = function () {
+ return this.s > 0;
+};
+
+
+/*
+ * Return true if the value of this Decimal is 0 or -0, otherwise return false.
+ *
+ */
+P.isZero = function () {
+ return !!this.d && this.d[0] === 0;
+};
+
+
+/*
+ * Return true if the value of this Decimal is less than `y`, otherwise return false.
+ *
+ */
+P.lessThan = P.lt = function (y) {
+ return this.cmp(y) < 0;
+};
+
+
+/*
+ * Return true if the value of this Decimal is less than or equal to `y`, otherwise return false.
+ *
+ */
+P.lessThanOrEqualTo = P.lte = function (y) {
+ return this.cmp(y) < 1;
+};
+
+
+/*
+ * Return the logarithm of the value of this Decimal to the specified base, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * If no base is specified, return log[10](arg).
+ *
+ * log[base](arg) = ln(arg) / ln(base)
+ *
+ * The result will always be correctly rounded if the base of the log is 10, and 'almost always'
+ * otherwise:
+ *
+ * Depending on the rounding mode, the result may be incorrectly rounded if the first fifteen
+ * rounding digits are [49]99999999999999 or [50]00000000000000. In that case, the maximum error
+ * between the result and the correctly rounded result will be one ulp (unit in the last place).
+ *
+ * log[-b](a) = NaN
+ * log[0](a) = NaN
+ * log[1](a) = NaN
+ * log[NaN](a) = NaN
+ * log[Infinity](a) = NaN
+ * log[b](0) = -Infinity
+ * log[b](-0) = -Infinity
+ * log[b](-a) = NaN
+ * log[b](1) = 0
+ * log[b](Infinity) = Infinity
+ * log[b](NaN) = NaN
+ *
+ * [base] {number|string|bigint|Decimal} The base of the logarithm.
+ *
+ */
+P.logarithm = P.log = function (base) {
+ var isBase10, d, denominator, k, inf, num, sd, r,
+ arg = this,
+ Ctor = arg.constructor,
+ pr = Ctor.precision,
+ rm = Ctor.rounding,
+ guard = 5;
+
+ // Default base is 10.
+ if (base == null) {
+ base = new Ctor(10);
+ isBase10 = true;
+ } else {
+ base = new Ctor(base);
+ d = base.d;
+
+ // Return NaN if base is negative, or non-finite, or is 0 or 1.
+ if (base.s < 0 || !d || !d[0] || base.eq(1)) return new Ctor(NaN);
+
+ isBase10 = base.eq(10);
+ }
+
+ d = arg.d;
+
+ // Is arg negative, non-finite, 0 or 1?
+ if (arg.s < 0 || !d || !d[0] || arg.eq(1)) {
+ return new Ctor(d && !d[0] ? -1 / 0 : arg.s != 1 ? NaN : d ? 0 : 1 / 0);
+ }
+
+ // The result will have a non-terminating decimal expansion if base is 10 and arg is not an
+ // integer power of 10.
+ if (isBase10) {
+ if (d.length > 1) {
+ inf = true;
+ } else {
+ for (k = d[0]; k % 10 === 0;) k /= 10;
+ inf = k !== 1;
+ }
+ }
+
+ external = false;
+ sd = pr + guard;
+ num = naturalLogarithm(arg, sd);
+ denominator = isBase10 ? getLn10(Ctor, sd + 10) : naturalLogarithm(base, sd);
+
+ // The result will have 5 rounding digits.
+ r = divide(num, denominator, sd, 1);
+
+ // If at a rounding boundary, i.e. the result's rounding digits are [49]9999 or [50]0000,
+ // calculate 10 further digits.
+ //
+ // If the result is known to have an infinite decimal expansion, repeat this until it is clear
+ // that the result is above or below the boundary. Otherwise, if after calculating the 10
+ // further digits, the last 14 are nines, round up and assume the result is exact.
+ // Also assume the result is exact if the last 14 are zero.
+ //
+ // Example of a result that will be incorrectly rounded:
+ // log[1048576](4503599627370502) = 2.60000000000000009610279511444746...
+ // The above result correctly rounded using ROUND_CEIL to 1 decimal place should be 2.7, but it
+ // will be given as 2.6 as there are 15 zeros immediately after the requested decimal place, so
+ // the exact result would be assumed to be 2.6, which rounded using ROUND_CEIL to 1 decimal
+ // place is still 2.6.
+ if (checkRoundingDigits(r.d, k = pr, rm)) {
+
+ do {
+ sd += 10;
+ num = naturalLogarithm(arg, sd);
+ denominator = isBase10 ? getLn10(Ctor, sd + 10) : naturalLogarithm(base, sd);
+ r = divide(num, denominator, sd, 1);
+
+ if (!inf) {
+
+ // Check for 14 nines from the 2nd rounding digit, as the first may be 4.
+ if (+digitsToString(r.d).slice(k + 1, k + 15) + 1 == 1e14) {
+ r = finalise(r, pr + 1, 0);
+ }
+
+ break;
+ }
+ } while (checkRoundingDigits(r.d, k += 10, rm));
+ }
+
+ external = true;
+
+ return finalise(r, pr, rm);
+};
+
+
+/*
+ * Return a new Decimal whose value is the maximum of the arguments and the value of this Decimal.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+P.max = function () {
+ Array.prototype.push.call(arguments, this);
+ return maxOrMin(this.constructor, arguments, -1);
+};
+ */
+
+
+/*
+ * Return a new Decimal whose value is the minimum of the arguments and the value of this Decimal.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+P.min = function () {
+ Array.prototype.push.call(arguments, this);
+ return maxOrMin(this.constructor, arguments, 1);
+};
+ */
+
+
+/*
+ * n - 0 = n
+ * n - N = N
+ * n - I = -I
+ * 0 - n = -n
+ * 0 - 0 = 0
+ * 0 - N = N
+ * 0 - I = -I
+ * N - n = N
+ * N - 0 = N
+ * N - N = N
+ * N - I = N
+ * I - n = I
+ * I - 0 = I
+ * I - N = N
+ * I - I = N
+ *
+ * Return a new Decimal whose value is the value of this Decimal minus `y`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ */
+P.minus = P.sub = function (y) {
+ var d, e, i, j, k, len, pr, rm, xd, xe, xLTy, yd,
+ x = this,
+ Ctor = x.constructor;
+
+ y = new Ctor(y);
+
+ // If either is not finite...
+ if (!x.d || !y.d) {
+
+ // Return NaN if either is NaN.
+ if (!x.s || !y.s) y = new Ctor(NaN);
+
+ // Return y negated if x is finite and y is ±Infinity.
+ else if (x.d) y.s = -y.s;
+
+ // Return x if y is finite and x is ±Infinity.
+ // Return x if both are ±Infinity with different signs.
+ // Return NaN if both are ±Infinity with the same sign.
+ else y = new Ctor(y.d || x.s !== y.s ? x : NaN);
+
+ return y;
+ }
+
+ // If signs differ...
+ if (x.s != y.s) {
+ y.s = -y.s;
+ return x.plus(y);
+ }
+
+ xd = x.d;
+ yd = y.d;
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+
+ // If either is zero...
+ if (!xd[0] || !yd[0]) {
+
+ // Return y negated if x is zero and y is non-zero.
+ if (yd[0]) y.s = -y.s;
+
+ // Return x if y is zero and x is non-zero.
+ else if (xd[0]) y = new Ctor(x);
+
+ // Return zero if both are zero.
+ // From IEEE 754 (2008) 6.3: 0 - 0 = -0 - -0 = -0 when rounding to -Infinity.
+ else return new Ctor(rm === 3 ? -0 : 0);
+
+ return external ? finalise(y, pr, rm) : y;
+ }
+
+ // x and y are finite, non-zero numbers with the same sign.
+
+ // Calculate base 1e7 exponents.
+ e = mathfloor(y.e / LOG_BASE);
+ xe = mathfloor(x.e / LOG_BASE);
+
+ xd = xd.slice();
+ k = xe - e;
+
+ // If base 1e7 exponents differ...
+ if (k) {
+ xLTy = k < 0;
+
+ if (xLTy) {
+ d = xd;
+ k = -k;
+ len = yd.length;
+ } else {
+ d = yd;
+ e = xe;
+ len = xd.length;
+ }
+
+ // Numbers with massively different exponents would result in a very high number of
+ // zeros needing to be prepended, but this can be avoided while still ensuring correct
+ // rounding by limiting the number of zeros to `Math.ceil(pr / LOG_BASE) + 2`.
+ i = Math.max(Math.ceil(pr / LOG_BASE), len) + 2;
+
+ if (k > i) {
+ k = i;
+ d.length = 1;
+ }
+
+ // Prepend zeros to equalise exponents.
+ d.reverse();
+ for (i = k; i--;) d.push(0);
+ d.reverse();
+
+ // Base 1e7 exponents equal.
+ } else {
+
+ // Check digits to determine which is the bigger number.
+
+ i = xd.length;
+ len = yd.length;
+ xLTy = i < len;
+ if (xLTy) len = i;
+
+ for (i = 0; i < len; i++) {
+ if (xd[i] != yd[i]) {
+ xLTy = xd[i] < yd[i];
+ break;
+ }
+ }
+
+ k = 0;
+ }
+
+ if (xLTy) {
+ d = xd;
+ xd = yd;
+ yd = d;
+ y.s = -y.s;
+ }
+
+ len = xd.length;
+
+ // Append zeros to `xd` if shorter.
+ // Don't add zeros to `yd` if shorter as subtraction only needs to start at `yd` length.
+ for (i = yd.length - len; i > 0; --i) xd[len++] = 0;
+
+ // Subtract yd from xd.
+ for (i = yd.length; i > k;) {
+
+ if (xd[--i] < yd[i]) {
+ for (j = i; j && xd[--j] === 0;) xd[j] = BASE - 1;
+ --xd[j];
+ xd[i] += BASE;
+ }
+
+ xd[i] -= yd[i];
+ }
+
+ // Remove trailing zeros.
+ for (; xd[--len] === 0;) xd.pop();
+
+ // Remove leading zeros and adjust exponent accordingly.
+ for (; xd[0] === 0; xd.shift()) --e;
+
+ // Zero?
+ if (!xd[0]) return new Ctor(rm === 3 ? -0 : 0);
+
+ y.d = xd;
+ y.e = getBase10Exponent(xd, e);
+
+ return external ? finalise(y, pr, rm) : y;
+};
+
+
+/*
+ * n % 0 = N
+ * n % N = N
+ * n % I = n
+ * 0 % n = 0
+ * -0 % n = -0
+ * 0 % 0 = N
+ * 0 % N = N
+ * 0 % I = 0
+ * N % n = N
+ * N % 0 = N
+ * N % N = N
+ * N % I = N
+ * I % n = N
+ * I % 0 = N
+ * I % N = N
+ * I % I = N
+ *
+ * Return a new Decimal whose value is the value of this Decimal modulo `y`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * The result depends on the modulo mode.
+ *
+ */
+P.modulo = P.mod = function (y) {
+ var q,
+ x = this,
+ Ctor = x.constructor;
+
+ y = new Ctor(y);
+
+ // Return NaN if x is ±Infinity or NaN, or y is NaN or ±0.
+ if (!x.d || !y.s || y.d && !y.d[0]) return new Ctor(NaN);
+
+ // Return x if y is ±Infinity or x is ±0.
+ if (!y.d || x.d && !x.d[0]) {
+ return finalise(new Ctor(x), Ctor.precision, Ctor.rounding);
+ }
+
+ // Prevent rounding of intermediate calculations.
+ external = false;
+
+ if (Ctor.modulo == 9) {
+
+ // Euclidian division: q = sign(y) * floor(x / abs(y))
+ // result = x - q * y where 0 <= result < abs(y)
+ q = divide(x, y.abs(), 0, 3, 1);
+ q.s *= y.s;
+ } else {
+ q = divide(x, y, 0, Ctor.modulo, 1);
+ }
+
+ q = q.times(y);
+
+ external = true;
+
+ return x.minus(q);
+};
+
+
+/*
+ * Return a new Decimal whose value is the natural exponential of the value of this Decimal,
+ * i.e. the base e raised to the power the value of this Decimal, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ */
+P.naturalExponential = P.exp = function () {
+ return naturalExponential(this);
+};
+
+
+/*
+ * Return a new Decimal whose value is the natural logarithm of the value of this Decimal,
+ * rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ */
+P.naturalLogarithm = P.ln = function () {
+ return naturalLogarithm(this);
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal negated, i.e. as if multiplied by
+ * -1.
+ *
+ */
+P.negated = P.neg = function () {
+ var x = new this.constructor(this);
+ x.s = -x.s;
+ return finalise(x);
+};
+
+
+/*
+ * n + 0 = n
+ * n + N = N
+ * n + I = I
+ * 0 + n = n
+ * 0 + 0 = 0
+ * 0 + N = N
+ * 0 + I = I
+ * N + n = N
+ * N + 0 = N
+ * N + N = N
+ * N + I = N
+ * I + n = I
+ * I + 0 = I
+ * I + N = N
+ * I + I = I
+ *
+ * Return a new Decimal whose value is the value of this Decimal plus `y`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ */
+P.plus = P.add = function (y) {
+ var carry, d, e, i, k, len, pr, rm, xd, yd,
+ x = this,
+ Ctor = x.constructor;
+
+ y = new Ctor(y);
+
+ // If either is not finite...
+ if (!x.d || !y.d) {
+
+ // Return NaN if either is NaN.
+ if (!x.s || !y.s) y = new Ctor(NaN);
+
+ // Return x if y is finite and x is ±Infinity.
+ // Return x if both are ±Infinity with the same sign.
+ // Return NaN if both are ±Infinity with different signs.
+ // Return y if x is finite and y is ±Infinity.
+ else if (!x.d) y = new Ctor(y.d || x.s === y.s ? x : NaN);
+
+ return y;
+ }
+
+ // If signs differ...
+ if (x.s != y.s) {
+ y.s = -y.s;
+ return x.minus(y);
+ }
+
+ xd = x.d;
+ yd = y.d;
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+
+ // If either is zero...
+ if (!xd[0] || !yd[0]) {
+
+ // Return x if y is zero.
+ // Return y if y is non-zero.
+ if (!yd[0]) y = new Ctor(x);
+
+ return external ? finalise(y, pr, rm) : y;
+ }
+
+ // x and y are finite, non-zero numbers with the same sign.
+
+ // Calculate base 1e7 exponents.
+ k = mathfloor(x.e / LOG_BASE);
+ e = mathfloor(y.e / LOG_BASE);
+
+ xd = xd.slice();
+ i = k - e;
+
+ // If base 1e7 exponents differ...
+ if (i) {
+
+ if (i < 0) {
+ d = xd;
+ i = -i;
+ len = yd.length;
+ } else {
+ d = yd;
+ e = k;
+ len = xd.length;
+ }
+
+ // Limit number of zeros prepended to max(ceil(pr / LOG_BASE), len) + 1.
+ k = Math.ceil(pr / LOG_BASE);
+ len = k > len ? k + 1 : len + 1;
+
+ if (i > len) {
+ i = len;
+ d.length = 1;
+ }
+
+ // Prepend zeros to equalise exponents. Note: Faster to use reverse then do unshifts.
+ d.reverse();
+ for (; i--;) d.push(0);
+ d.reverse();
+ }
+
+ len = xd.length;
+ i = yd.length;
+
+ // If yd is longer than xd, swap xd and yd so xd points to the longer array.
+ if (len - i < 0) {
+ i = len;
+ d = yd;
+ yd = xd;
+ xd = d;
+ }
+
+ // Only start adding at yd.length - 1 as the further digits of xd can be left as they are.
+ for (carry = 0; i;) {
+ carry = (xd[--i] = xd[i] + yd[i] + carry) / BASE | 0;
+ xd[i] %= BASE;
+ }
+
+ if (carry) {
+ xd.unshift(carry);
+ ++e;
+ }
+
+ // Remove trailing zeros.
+ // No need to check for zero, as +x + +y != 0 && -x + -y != 0
+ for (len = xd.length; xd[--len] == 0;) xd.pop();
+
+ y.d = xd;
+ y.e = getBase10Exponent(xd, e);
+
+ return external ? finalise(y, pr, rm) : y;
+};
+
+
+/*
+ * Return the number of significant digits of the value of this Decimal.
+ *
+ * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0.
+ *
+ */
+P.precision = P.sd = function (z) {
+ var k,
+ x = this;
+
+ if (z !== void 0 && z !== !!z && z !== 1 && z !== 0) throw Error(invalidArgument + z);
+
+ if (x.d) {
+ k = getPrecision(x.d);
+ if (z && x.e + 1 > k) k = x.e + 1;
+ } else {
+ k = NaN;
+ }
+
+ return k;
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a whole number using
+ * rounding mode `rounding`.
+ *
+ */
+P.round = function () {
+ var x = this,
+ Ctor = x.constructor;
+
+ return finalise(new Ctor(x), x.e + 1, Ctor.rounding);
+};
+
+
+/*
+ * Return a new Decimal whose value is the sine of the value in radians of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-1, 1]
+ *
+ * sin(x) = x - x^3/3! + x^5/5! - ...
+ *
+ * sin(0) = 0
+ * sin(-0) = -0
+ * sin(Infinity) = NaN
+ * sin(-Infinity) = NaN
+ * sin(NaN) = NaN
+ *
+ */
+P.sine = P.sin = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite()) return new Ctor(NaN);
+ if (x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + Math.max(x.e, x.sd()) + LOG_BASE;
+ Ctor.rounding = 1;
+
+ x = sine(Ctor, toLessThanHalfPi(Ctor, x));
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return finalise(quadrant > 2 ? x.neg() : x, pr, rm, true);
+};
+
+
+/*
+ * Return a new Decimal whose value is the square root of this Decimal, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * sqrt(-n) = N
+ * sqrt(N) = N
+ * sqrt(-I) = N
+ * sqrt(I) = I
+ * sqrt(0) = 0
+ * sqrt(-0) = -0
+ *
+ */
+P.squareRoot = P.sqrt = function () {
+ var m, n, sd, r, rep, t,
+ x = this,
+ d = x.d,
+ e = x.e,
+ s = x.s,
+ Ctor = x.constructor;
+
+ // Negative/NaN/Infinity/zero?
+ if (s !== 1 || !d || !d[0]) {
+ return new Ctor(!s || s < 0 && (!d || d[0]) ? NaN : d ? x : 1 / 0);
+ }
+
+ external = false;
+
+ // Initial estimate.
+ s = Math.sqrt(+x);
+
+ // Math.sqrt underflow/overflow?
+ // Pass x to Math.sqrt as integer, then adjust the exponent of the result.
+ if (s == 0 || s == 1 / 0) {
+ n = digitsToString(d);
+
+ if ((n.length + e) % 2 == 0) n += '0';
+ s = Math.sqrt(n);
+ e = mathfloor((e + 1) / 2) - (e < 0 || e % 2);
+
+ if (s == 1 / 0) {
+ n = '5e' + e;
+ } else {
+ n = s.toExponential();
+ n = n.slice(0, n.indexOf('e') + 1) + e;
+ }
+
+ r = new Ctor(n);
+ } else {
+ r = new Ctor(s.toString());
+ }
+
+ sd = (e = Ctor.precision) + 3;
+
+ // Newton-Raphson iteration.
+ for (;;) {
+ t = r;
+ r = t.plus(divide(x, t, sd + 2, 1)).times(0.5);
+
+ // TODO? Replace with for-loop and checkRoundingDigits.
+ if (digitsToString(t.d).slice(0, sd) === (n = digitsToString(r.d)).slice(0, sd)) {
+ n = n.slice(sd - 3, sd + 1);
+
+ // The 4th rounding digit may be in error by -1 so if the 4 rounding digits are 9999 or
+ // 4999, i.e. approaching a rounding boundary, continue the iteration.
+ if (n == '9999' || !rep && n == '4999') {
+
+ // On the first iteration only, check to see if rounding up gives the exact result as the
+ // nines may infinitely repeat.
+ if (!rep) {
+ finalise(t, e + 1, 0);
+
+ if (t.times(t).eq(x)) {
+ r = t;
+ break;
+ }
+ }
+
+ sd += 4;
+ rep = 1;
+ } else {
+
+ // If the rounding digits are null, 0{0,4} or 50{0,3}, check for an exact result.
+ // If not, then there are further digits and m will be truthy.
+ if (!+n || !+n.slice(1) && n.charAt(0) == '5') {
+
+ // Truncate to the first rounding digit.
+ finalise(r, e + 1, 1);
+ m = !r.times(r).eq(x);
+ }
+
+ break;
+ }
+ }
+ }
+
+ external = true;
+
+ return finalise(r, e, Ctor.rounding, m);
+};
+
+
+/*
+ * Return a new Decimal whose value is the tangent of the value in radians of this Decimal.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-Infinity, Infinity]
+ *
+ * tan(0) = 0
+ * tan(-0) = -0
+ * tan(Infinity) = NaN
+ * tan(-Infinity) = NaN
+ * tan(NaN) = NaN
+ *
+ */
+P.tangent = P.tan = function () {
+ var pr, rm,
+ x = this,
+ Ctor = x.constructor;
+
+ if (!x.isFinite()) return new Ctor(NaN);
+ if (x.isZero()) return new Ctor(x);
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+ Ctor.precision = pr + 10;
+ Ctor.rounding = 1;
+
+ x = x.sin();
+ x.s = 1;
+ x = divide(x, new Ctor(1).minus(x.times(x)).sqrt(), pr + 10, 0);
+
+ Ctor.precision = pr;
+ Ctor.rounding = rm;
+
+ return finalise(quadrant == 2 || quadrant == 4 ? x.neg() : x, pr, rm, true);
+};
+
+
+/*
+ * n * 0 = 0
+ * n * N = N
+ * n * I = I
+ * 0 * n = 0
+ * 0 * 0 = 0
+ * 0 * N = N
+ * 0 * I = N
+ * N * n = N
+ * N * 0 = N
+ * N * N = N
+ * N * I = N
+ * I * n = I
+ * I * 0 = N
+ * I * N = N
+ * I * I = I
+ *
+ * Return a new Decimal whose value is this Decimal times `y`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ */
+P.times = P.mul = function (y) {
+ var carry, e, i, k, r, rL, t, xdL, ydL,
+ x = this,
+ Ctor = x.constructor,
+ xd = x.d,
+ yd = (y = new Ctor(y)).d;
+
+ y.s *= x.s;
+
+ // If either is NaN, ±Infinity or ±0...
+ if (!xd || !xd[0] || !yd || !yd[0]) {
+
+ return new Ctor(!y.s || xd && !xd[0] && !yd || yd && !yd[0] && !xd
+
+ // Return NaN if either is NaN.
+ // Return NaN if x is ±0 and y is ±Infinity, or y is ±0 and x is ±Infinity.
+ ? NaN
+
+ // Return ±Infinity if either is ±Infinity.
+ // Return ±0 if either is ±0.
+ : !xd || !yd ? y.s / 0 : y.s * 0);
+ }
+
+ e = mathfloor(x.e / LOG_BASE) + mathfloor(y.e / LOG_BASE);
+ xdL = xd.length;
+ ydL = yd.length;
+
+ // Ensure xd points to the longer array.
+ if (xdL < ydL) {
+ r = xd;
+ xd = yd;
+ yd = r;
+ rL = xdL;
+ xdL = ydL;
+ ydL = rL;
+ }
+
+ // Initialise the result array with zeros.
+ r = [];
+ rL = xdL + ydL;
+ for (i = rL; i--;) r.push(0);
+
+ // Multiply!
+ for (i = ydL; --i >= 0;) {
+ carry = 0;
+ for (k = xdL + i; k > i;) {
+ t = r[k] + yd[i] * xd[k - i - 1] + carry;
+ r[k--] = t % BASE | 0;
+ carry = t / BASE | 0;
+ }
+
+ r[k] = (r[k] + carry) % BASE | 0;
+ }
+
+ // Remove trailing zeros.
+ for (; !r[--rL];) r.pop();
+
+ if (carry) ++e;
+ else r.shift();
+
+ y.d = r;
+ y.e = getBase10Exponent(r, e);
+
+ return external ? finalise(y, Ctor.precision, Ctor.rounding) : y;
+};
+
+
+/*
+ * Return a string representing the value of this Decimal in base 2, round to `sd` significant
+ * digits using rounding mode `rm`.
+ *
+ * If the optional `sd` argument is present then return binary exponential notation.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+P.toBinary = function (sd, rm) {
+ return toStringBinary(this, 2, sd, rm);
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a maximum of `dp`
+ * decimal places using rounding mode `rm` or `rounding` if `rm` is omitted.
+ *
+ * If `dp` is omitted, return a new Decimal whose value is the value of this Decimal.
+ *
+ * [dp] {number} Decimal places. Integer, 0 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+P.toDecimalPlaces = P.toDP = function (dp, rm) {
+ var x = this,
+ Ctor = x.constructor;
+
+ x = new Ctor(x);
+ if (dp === void 0) return x;
+
+ checkInt32(dp, 0, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+
+ return finalise(x, dp + x.e + 1, rm);
+};
+
+
+/*
+ * Return a string representing the value of this Decimal in exponential notation rounded to
+ * `dp` fixed decimal places using rounding mode `rounding`.
+ *
+ * [dp] {number} Decimal places. Integer, 0 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+P.toExponential = function (dp, rm) {
+ var str,
+ x = this,
+ Ctor = x.constructor;
+
+ if (dp === void 0) {
+ str = finiteToString(x, true);
+ } else {
+ checkInt32(dp, 0, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+
+ x = finalise(new Ctor(x), dp + 1, rm);
+ str = finiteToString(x, true, dp + 1);
+ }
+
+ return x.isNeg() && !x.isZero() ? '-' + str : str;
+};
+
+
+/*
+ * Return a string representing the value of this Decimal in normal (fixed-point) notation to
+ * `dp` fixed decimal places and rounded using rounding mode `rm` or `rounding` if `rm` is
+ * omitted.
+ *
+ * As with JavaScript numbers, (-0).toFixed(0) is '0', but e.g. (-0.00001).toFixed(0) is '-0'.
+ *
+ * [dp] {number} Decimal places. Integer, 0 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ * (-0).toFixed(0) is '0', but (-0.1).toFixed(0) is '-0'.
+ * (-0).toFixed(1) is '0.0', but (-0.01).toFixed(1) is '-0.0'.
+ * (-0).toFixed(3) is '0.000'.
+ * (-0.5).toFixed(0) is '-0'.
+ *
+ */
+P.toFixed = function (dp, rm) {
+ var str, y,
+ x = this,
+ Ctor = x.constructor;
+
+ if (dp === void 0) {
+ str = finiteToString(x);
+ } else {
+ checkInt32(dp, 0, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+
+ y = finalise(new Ctor(x), dp + x.e + 1, rm);
+ str = finiteToString(y, false, dp + y.e + 1);
+ }
+
+ // To determine whether to add the minus sign look at the value before it was rounded,
+ // i.e. look at `x` rather than `y`.
+ return x.isNeg() && !x.isZero() ? '-' + str : str;
+};
+
+
+/*
+ * Return an array representing the value of this Decimal as a simple fraction with an integer
+ * numerator and an integer denominator.
+ *
+ * The denominator will be a positive non-zero value less than or equal to the specified maximum
+ * denominator. If a maximum denominator is not specified, the denominator will be the lowest
+ * value necessary to represent the number exactly.
+ *
+ * [maxD] {number|string|bigint|Decimal} Maximum denominator. Integer >= 1 and < Infinity.
+ *
+ */
+P.toFraction = function (maxD) {
+ var d, d0, d1, d2, e, k, n, n0, n1, pr, q, r,
+ x = this,
+ xd = x.d,
+ Ctor = x.constructor;
+
+ if (!xd) return new Ctor(x);
+
+ n1 = d0 = new Ctor(1);
+ d1 = n0 = new Ctor(0);
+
+ d = new Ctor(d1);
+ e = d.e = getPrecision(xd) - x.e - 1;
+ k = e % LOG_BASE;
+ d.d[0] = mathpow(10, k < 0 ? LOG_BASE + k : k);
+
+ if (maxD == null) {
+
+ // d is 10**e, the minimum max-denominator needed.
+ maxD = e > 0 ? d : n1;
+ } else {
+ n = new Ctor(maxD);
+ if (!n.isInt() || n.lt(n1)) throw Error(invalidArgument + n);
+ maxD = n.gt(d) ? (e > 0 ? d : n1) : n;
+ }
+
+ external = false;
+ n = new Ctor(digitsToString(xd));
+ pr = Ctor.precision;
+ Ctor.precision = e = xd.length * LOG_BASE * 2;
+
+ for (;;) {
+ q = divide(n, d, 0, 1, 1);
+ d2 = d0.plus(q.times(d1));
+ if (d2.cmp(maxD) == 1) break;
+ d0 = d1;
+ d1 = d2;
+ d2 = n1;
+ n1 = n0.plus(q.times(d2));
+ n0 = d2;
+ d2 = d;
+ d = n.minus(q.times(d2));
+ n = d2;
+ }
+
+ d2 = divide(maxD.minus(d0), d1, 0, 1, 1);
+ n0 = n0.plus(d2.times(n1));
+ d0 = d0.plus(d2.times(d1));
+ n0.s = n1.s = x.s;
+
+ // Determine which fraction is closer to x, n0/d0 or n1/d1?
+ r = divide(n1, d1, e, 1).minus(x).abs().cmp(divide(n0, d0, e, 1).minus(x).abs()) < 1
+ ? [n1, d1] : [n0, d0];
+
+ Ctor.precision = pr;
+ external = true;
+
+ return r;
+};
+
+
+/*
+ * Return a string representing the value of this Decimal in base 16, round to `sd` significant
+ * digits using rounding mode `rm`.
+ *
+ * If the optional `sd` argument is present then return binary exponential notation.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+P.toHexadecimal = P.toHex = function (sd, rm) {
+ return toStringBinary(this, 16, sd, rm);
+};
+
+
+/*
+ * Returns a new Decimal whose value is the nearest multiple of `y` in the direction of rounding
+ * mode `rm`, or `Decimal.rounding` if `rm` is omitted, to the value of this Decimal.
+ *
+ * The return value will always have the same sign as this Decimal, unless either this Decimal
+ * or `y` is NaN, in which case the return value will be also be NaN.
+ *
+ * The return value is not affected by the value of `precision`.
+ *
+ * y {number|string|bigint|Decimal} The magnitude to round to a multiple of.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ * 'toNearest() rounding mode not an integer: {rm}'
+ * 'toNearest() rounding mode out of range: {rm}'
+ *
+ */
+P.toNearest = function (y, rm) {
+ var x = this,
+ Ctor = x.constructor;
+
+ x = new Ctor(x);
+
+ if (y == null) {
+
+ // If x is not finite, return x.
+ if (!x.d) return x;
+
+ y = new Ctor(1);
+ rm = Ctor.rounding;
+ } else {
+ y = new Ctor(y);
+ if (rm === void 0) {
+ rm = Ctor.rounding;
+ } else {
+ checkInt32(rm, 0, 8);
+ }
+
+ // If x is not finite, return x if y is not NaN, else NaN.
+ if (!x.d) return y.s ? x : y;
+
+ // If y is not finite, return Infinity with the sign of x if y is Infinity, else NaN.
+ if (!y.d) {
+ if (y.s) y.s = x.s;
+ return y;
+ }
+ }
+
+ // If y is not zero, calculate the nearest multiple of y to x.
+ if (y.d[0]) {
+ external = false;
+ x = divide(x, y, 0, rm, 1).times(y);
+ external = true;
+ finalise(x);
+
+ // If y is zero, return zero with the sign of x.
+ } else {
+ y.s = x.s;
+ x = y;
+ }
+
+ return x;
+};
+
+
+/*
+ * Return the value of this Decimal converted to a number primitive.
+ * Zero keeps its sign.
+ *
+ */
+P.toNumber = function () {
+ return +this;
+};
+
+
+/*
+ * Return a string representing the value of this Decimal in base 8, round to `sd` significant
+ * digits using rounding mode `rm`.
+ *
+ * If the optional `sd` argument is present then return binary exponential notation.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+P.toOctal = function (sd, rm) {
+ return toStringBinary(this, 8, sd, rm);
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal raised to the power `y`, rounded
+ * to `precision` significant digits using rounding mode `rounding`.
+ *
+ * ECMAScript compliant.
+ *
+ * pow(x, NaN) = NaN
+ * pow(x, ±0) = 1
+
+ * pow(NaN, non-zero) = NaN
+ * pow(abs(x) > 1, +Infinity) = +Infinity
+ * pow(abs(x) > 1, -Infinity) = +0
+ * pow(abs(x) == 1, ±Infinity) = NaN
+ * pow(abs(x) < 1, +Infinity) = +0
+ * pow(abs(x) < 1, -Infinity) = +Infinity
+ * pow(+Infinity, y > 0) = +Infinity
+ * pow(+Infinity, y < 0) = +0
+ * pow(-Infinity, odd integer > 0) = -Infinity
+ * pow(-Infinity, even integer > 0) = +Infinity
+ * pow(-Infinity, odd integer < 0) = -0
+ * pow(-Infinity, even integer < 0) = +0
+ * pow(+0, y > 0) = +0
+ * pow(+0, y < 0) = +Infinity
+ * pow(-0, odd integer > 0) = -0
+ * pow(-0, even integer > 0) = +0
+ * pow(-0, odd integer < 0) = -Infinity
+ * pow(-0, even integer < 0) = +Infinity
+ * pow(finite x < 0, finite non-integer) = NaN
+ *
+ * For non-integer or very large exponents pow(x, y) is calculated using
+ *
+ * x^y = exp(y*ln(x))
+ *
+ * Assuming the first 15 rounding digits are each equally likely to be any digit 0-9, the
+ * probability of an incorrectly rounded result
+ * P([49]9{14} | [50]0{14}) = 2 * 0.2 * 10^-14 = 4e-15 = 1/2.5e+14
+ * i.e. 1 in 250,000,000,000,000
+ *
+ * If a result is incorrectly rounded the maximum error will be 1 ulp (unit in last place).
+ *
+ * y {number|string|bigint|Decimal} The power to which to raise this Decimal.
+ *
+ */
+P.toPower = P.pow = function (y) {
+ var e, k, pr, r, rm, s,
+ x = this,
+ Ctor = x.constructor,
+ yn = +(y = new Ctor(y));
+
+ // Either ±Infinity, NaN or ±0?
+ if (!x.d || !y.d || !x.d[0] || !y.d[0]) return new Ctor(mathpow(+x, yn));
+
+ x = new Ctor(x);
+
+ if (x.eq(1)) return x;
+
+ pr = Ctor.precision;
+ rm = Ctor.rounding;
+
+ if (y.eq(1)) return finalise(x, pr, rm);
+
+ // y exponent
+ e = mathfloor(y.e / LOG_BASE);
+
+ // If y is a small integer use the 'exponentiation by squaring' algorithm.
+ if (e >= y.d.length - 1 && (k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) {
+ r = intPow(Ctor, x, k, pr);
+ return y.s < 0 ? new Ctor(1).div(r) : finalise(r, pr, rm);
+ }
+
+ s = x.s;
+
+ // if x is negative
+ if (s < 0) {
+
+ // if y is not an integer
+ if (e < y.d.length - 1) return new Ctor(NaN);
+
+ // Result is positive if x is negative and the last digit of integer y is even.
+ if ((y.d[e] & 1) == 0) s = 1;
+
+ // if x.eq(-1)
+ if (x.e == 0 && x.d[0] == 1 && x.d.length == 1) {
+ x.s = s;
+ return x;
+ }
+ }
+
+ // Estimate result exponent.
+ // x^y = 10^e, where e = y * log10(x)
+ // log10(x) = log10(x_significand) + x_exponent
+ // log10(x_significand) = ln(x_significand) / ln(10)
+ k = mathpow(+x, yn);
+ e = k == 0 || !isFinite(k)
+ ? mathfloor(yn * (Math.log('0.' + digitsToString(x.d)) / Math.LN10 + x.e + 1))
+ : new Ctor(k + '').e;
+
+ // Exponent estimate may be incorrect e.g. x: 0.999999999999999999, y: 2.29, e: 0, r.e: -1.
+
+ // Overflow/underflow?
+ if (e > Ctor.maxE + 1 || e < Ctor.minE - 1) return new Ctor(e > 0 ? s / 0 : 0);
+
+ external = false;
+ Ctor.rounding = x.s = 1;
+
+ // Estimate the extra guard digits needed to ensure five correct rounding digits from
+ // naturalLogarithm(x). Example of failure without these extra digits (precision: 10):
+ // new Decimal(2.32456).pow('2087987436534566.46411')
+ // should be 1.162377823e+764914905173815, but is 1.162355823e+764914905173815
+ k = Math.min(12, (e + '').length);
+
+ // r = x^y = exp(y*ln(x))
+ r = naturalExponential(y.times(naturalLogarithm(x, pr + k)), pr);
+
+ // r may be Infinity, e.g. (0.9999999999999999).pow(-1e+40)
+ if (r.d) {
+
+ // Truncate to the required precision plus five rounding digits.
+ r = finalise(r, pr + 5, 1);
+
+ // If the rounding digits are [49]9999 or [50]0000 increase the precision by 10 and recalculate
+ // the result.
+ if (checkRoundingDigits(r.d, pr, rm)) {
+ e = pr + 10;
+
+ // Truncate to the increased precision plus five rounding digits.
+ r = finalise(naturalExponential(y.times(naturalLogarithm(x, e + k)), e), e + 5, 1);
+
+ // Check for 14 nines from the 2nd rounding digit (the first rounding digit may be 4 or 9).
+ if (+digitsToString(r.d).slice(pr + 1, pr + 15) + 1 == 1e14) {
+ r = finalise(r, pr + 1, 0);
+ }
+ }
+ }
+
+ r.s = s;
+ external = true;
+ Ctor.rounding = rm;
+
+ return finalise(r, pr, rm);
+};
+
+
+/*
+ * Return a string representing the value of this Decimal rounded to `sd` significant digits
+ * using rounding mode `rounding`.
+ *
+ * Return exponential notation if `sd` is less than the number of digits necessary to represent
+ * the integer part of the value in normal notation.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ */
+P.toPrecision = function (sd, rm) {
+ var str,
+ x = this,
+ Ctor = x.constructor;
+
+ if (sd === void 0) {
+ str = finiteToString(x, x.e <= Ctor.toExpNeg || x.e >= Ctor.toExpPos);
+ } else {
+ checkInt32(sd, 1, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+
+ x = finalise(new Ctor(x), sd, rm);
+ str = finiteToString(x, sd <= x.e || x.e <= Ctor.toExpNeg, sd);
+ }
+
+ return x.isNeg() && !x.isZero() ? '-' + str : str;
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal rounded to a maximum of `sd`
+ * significant digits using rounding mode `rm`, or to `precision` and `rounding` respectively if
+ * omitted.
+ *
+ * [sd] {number} Significant digits. Integer, 1 to MAX_DIGITS inclusive.
+ * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive.
+ *
+ * 'toSD() digits out of range: {sd}'
+ * 'toSD() digits not an integer: {sd}'
+ * 'toSD() rounding mode not an integer: {rm}'
+ * 'toSD() rounding mode out of range: {rm}'
+ *
+ */
+P.toSignificantDigits = P.toSD = function (sd, rm) {
+ var x = this,
+ Ctor = x.constructor;
+
+ if (sd === void 0) {
+ sd = Ctor.precision;
+ rm = Ctor.rounding;
+ } else {
+ checkInt32(sd, 1, MAX_DIGITS);
+
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+ }
+
+ return finalise(new Ctor(x), sd, rm);
+};
+
+
+/*
+ * Return a string representing the value of this Decimal.
+ *
+ * Return exponential notation if this Decimal has a positive exponent equal to or greater than
+ * `toExpPos`, or a negative exponent equal to or less than `toExpNeg`.
+ *
+ */
+P.toString = function () {
+ var x = this,
+ Ctor = x.constructor,
+ str = finiteToString(x, x.e <= Ctor.toExpNeg || x.e >= Ctor.toExpPos);
+
+ return x.isNeg() && !x.isZero() ? '-' + str : str;
+};
+
+
+/*
+ * Return a new Decimal whose value is the value of this Decimal truncated to a whole number.
+ *
+ */
+P.truncated = P.trunc = function () {
+ return finalise(new this.constructor(this), this.e + 1, 1);
+};
+
+
+/*
+ * Return a string representing the value of this Decimal.
+ * Unlike `toString`, negative zero will include the minus sign.
+ *
+ */
+P.valueOf = P.toJSON = function () {
+ var x = this,
+ Ctor = x.constructor,
+ str = finiteToString(x, x.e <= Ctor.toExpNeg || x.e >= Ctor.toExpPos);
+
+ return x.isNeg() ? '-' + str : str;
+};
+
+
+// Helper functions for Decimal.prototype (P) and/or Decimal methods, and their callers.
+
+
+/*
+ * digitsToString P.cubeRoot, P.logarithm, P.squareRoot, P.toFraction, P.toPower,
+ * finiteToString, naturalExponential, naturalLogarithm
+ * checkInt32 P.toDecimalPlaces, P.toExponential, P.toFixed, P.toNearest,
+ * P.toPrecision, P.toSignificantDigits, toStringBinary, random
+ * checkRoundingDigits P.logarithm, P.toPower, naturalExponential, naturalLogarithm
+ * convertBase toStringBinary, parseOther
+ * cos P.cos
+ * divide P.atanh, P.cubeRoot, P.dividedBy, P.dividedToIntegerBy,
+ * P.logarithm, P.modulo, P.squareRoot, P.tan, P.tanh, P.toFraction,
+ * P.toNearest, toStringBinary, naturalExponential, naturalLogarithm,
+ * taylorSeries, atan2, parseOther
+ * finalise P.absoluteValue, P.atan, P.atanh, P.ceil, P.cos, P.cosh,
+ * P.cubeRoot, P.dividedToIntegerBy, P.floor, P.logarithm, P.minus,
+ * P.modulo, P.negated, P.plus, P.round, P.sin, P.sinh, P.squareRoot,
+ * P.tan, P.times, P.toDecimalPlaces, P.toExponential, P.toFixed,
+ * P.toNearest, P.toPower, P.toPrecision, P.toSignificantDigits,
+ * P.truncated, divide, getLn10, getPi, naturalExponential,
+ * naturalLogarithm, ceil, floor, round, trunc
+ * finiteToString P.toExponential, P.toFixed, P.toPrecision, P.toString, P.valueOf,
+ * toStringBinary
+ * getBase10Exponent P.minus, P.plus, P.times, parseOther
+ * getLn10 P.logarithm, naturalLogarithm
+ * getPi P.acos, P.asin, P.atan, toLessThanHalfPi, atan2
+ * getPrecision P.precision, P.toFraction
+ * getZeroString digitsToString, finiteToString
+ * intPow P.toPower, parseOther
+ * isOdd toLessThanHalfPi
+ * maxOrMin max, min
+ * naturalExponential P.naturalExponential, P.toPower
+ * naturalLogarithm P.acosh, P.asinh, P.atanh, P.logarithm, P.naturalLogarithm,
+ * P.toPower, naturalExponential
+ * nonFiniteToString finiteToString, toStringBinary
+ * parseDecimal Decimal
+ * parseOther Decimal
+ * sin P.sin
+ * taylorSeries P.cosh, P.sinh, cos, sin
+ * toLessThanHalfPi P.cos, P.sin
+ * toStringBinary P.toBinary, P.toHexadecimal, P.toOctal
+ * truncate intPow
+ *
+ * Throws: P.logarithm, P.precision, P.toFraction, checkInt32, getLn10, getPi,
+ * naturalLogarithm, config, parseOther, random, Decimal
+ */
+
+
+function digitsToString(d) {
+ var i, k, ws,
+ indexOfLastWord = d.length - 1,
+ str = '',
+ w = d[0];
+
+ if (indexOfLastWord > 0) {
+ str += w;
+ for (i = 1; i < indexOfLastWord; i++) {
+ ws = d[i] + '';
+ k = LOG_BASE - ws.length;
+ if (k) str += getZeroString(k);
+ str += ws;
+ }
+
+ w = d[i];
+ ws = w + '';
+ k = LOG_BASE - ws.length;
+ if (k) str += getZeroString(k);
+ } else if (w === 0) {
+ return '0';
+ }
+
+ // Remove trailing zeros of last w.
+ for (; w % 10 === 0;) w /= 10;
+
+ return str + w;
+}
+
+
+function checkInt32(i, min, max) {
+ if (i !== ~~i || i < min || i > max) {
+ throw Error(invalidArgument + i);
+ }
+}
+
+
+/*
+ * Check 5 rounding digits if `repeating` is null, 4 otherwise.
+ * `repeating == null` if caller is `log` or `pow`,
+ * `repeating != null` if caller is `naturalLogarithm` or `naturalExponential`.
+ */
+function checkRoundingDigits(d, i, rm, repeating) {
+ var di, k, r, rd;
+
+ // Get the length of the first word of the array d.
+ for (k = d[0]; k >= 10; k /= 10) --i;
+
+ // Is the rounding digit in the first word of d?
+ if (--i < 0) {
+ i += LOG_BASE;
+ di = 0;
+ } else {
+ di = Math.ceil((i + 1) / LOG_BASE);
+ i %= LOG_BASE;
+ }
+
+ // i is the index (0 - 6) of the rounding digit.
+ // E.g. if within the word 3487563 the first rounding digit is 5,
+ // then i = 4, k = 1000, rd = 3487563 % 1000 = 563
+ k = mathpow(10, LOG_BASE - i);
+ rd = d[di] % k | 0;
+
+ if (repeating == null) {
+ if (i < 3) {
+ if (i == 0) rd = rd / 100 | 0;
+ else if (i == 1) rd = rd / 10 | 0;
+ r = rm < 4 && rd == 99999 || rm > 3 && rd == 49999 || rd == 50000 || rd == 0;
+ } else {
+ r = (rm < 4 && rd + 1 == k || rm > 3 && rd + 1 == k / 2) &&
+ (d[di + 1] / k / 100 | 0) == mathpow(10, i - 2) - 1 ||
+ (rd == k / 2 || rd == 0) && (d[di + 1] / k / 100 | 0) == 0;
+ }
+ } else {
+ if (i < 4) {
+ if (i == 0) rd = rd / 1000 | 0;
+ else if (i == 1) rd = rd / 100 | 0;
+ else if (i == 2) rd = rd / 10 | 0;
+ r = (repeating || rm < 4) && rd == 9999 || !repeating && rm > 3 && rd == 4999;
+ } else {
+ r = ((repeating || rm < 4) && rd + 1 == k ||
+ (!repeating && rm > 3) && rd + 1 == k / 2) &&
+ (d[di + 1] / k / 1000 | 0) == mathpow(10, i - 3) - 1;
+ }
+ }
+
+ return r;
+}
+
+
+// Convert string of `baseIn` to an array of numbers of `baseOut`.
+// Eg. convertBase('255', 10, 16) returns [15, 15].
+// Eg. convertBase('ff', 16, 10) returns [2, 5, 5].
+function convertBase(str, baseIn, baseOut) {
+ var j,
+ arr = [0],
+ arrL,
+ i = 0,
+ strL = str.length;
+
+ for (; i < strL;) {
+ for (arrL = arr.length; arrL--;) arr[arrL] *= baseIn;
+ arr[0] += NUMERALS.indexOf(str.charAt(i++));
+ for (j = 0; j < arr.length; j++) {
+ if (arr[j] > baseOut - 1) {
+ if (arr[j + 1] === void 0) arr[j + 1] = 0;
+ arr[j + 1] += arr[j] / baseOut | 0;
+ arr[j] %= baseOut;
+ }
+ }
+ }
+
+ return arr.reverse();
+}
+
+
+/*
+ * cos(x) = 1 - x^2/2! + x^4/4! - ...
+ * |x| < pi/2
+ *
+ */
+function cosine(Ctor, x) {
+ var k, len, y;
+
+ if (x.isZero()) return x;
+
+ // Argument reduction: cos(4x) = 8*(cos^4(x) - cos^2(x)) + 1
+ // i.e. cos(x) = 8*(cos^4(x/4) - cos^2(x/4)) + 1
+
+ // Estimate the optimum number of times to use the argument reduction.
+ len = x.d.length;
+ if (len < 32) {
+ k = Math.ceil(len / 3);
+ y = (1 / tinyPow(4, k)).toString();
+ } else {
+ k = 16;
+ y = '2.3283064365386962890625e-10';
+ }
+
+ Ctor.precision += k;
+
+ x = taylorSeries(Ctor, 1, x.times(y), new Ctor(1));
+
+ // Reverse argument reduction
+ for (var i = k; i--;) {
+ var cos2x = x.times(x);
+ x = cos2x.times(cos2x).minus(cos2x).times(8).plus(1);
+ }
+
+ Ctor.precision -= k;
+
+ return x;
+}
+
+
+/*
+ * Perform division in the specified base.
+ */
+var divide = (function () {
+
+ // Assumes non-zero x and k, and hence non-zero result.
+ function multiplyInteger(x, k, base) {
+ var temp,
+ carry = 0,
+ i = x.length;
+
+ for (x = x.slice(); i--;) {
+ temp = x[i] * k + carry;
+ x[i] = temp % base | 0;
+ carry = temp / base | 0;
+ }
+
+ if (carry) x.unshift(carry);
+
+ return x;
+ }
+
+ function compare(a, b, aL, bL) {
+ var i, r;
+
+ if (aL != bL) {
+ r = aL > bL ? 1 : -1;
+ } else {
+ for (i = r = 0; i < aL; i++) {
+ if (a[i] != b[i]) {
+ r = a[i] > b[i] ? 1 : -1;
+ break;
+ }
+ }
+ }
+
+ return r;
+ }
+
+ function subtract(a, b, aL, base) {
+ var i = 0;
+
+ // Subtract b from a.
+ for (; aL--;) {
+ a[aL] -= i;
+ i = a[aL] < b[aL] ? 1 : 0;
+ a[aL] = i * base + a[aL] - b[aL];
+ }
+
+ // Remove leading zeros.
+ for (; !a[0] && a.length > 1;) a.shift();
+ }
+
+ return function (x, y, pr, rm, dp, base) {
+ var cmp, e, i, k, logBase, more, prod, prodL, q, qd, rem, remL, rem0, sd, t, xi, xL, yd0,
+ yL, yz,
+ Ctor = x.constructor,
+ sign = x.s == y.s ? 1 : -1,
+ xd = x.d,
+ yd = y.d;
+
+ // Either NaN, Infinity or 0?
+ if (!xd || !xd[0] || !yd || !yd[0]) {
+
+ return new Ctor(// Return NaN if either NaN, or both Infinity or 0.
+ !x.s || !y.s || (xd ? yd && xd[0] == yd[0] : !yd) ? NaN :
+
+ // Return ±0 if x is 0 or y is ±Infinity, or return ±Infinity as y is 0.
+ xd && xd[0] == 0 || !yd ? sign * 0 : sign / 0);
+ }
+
+ if (base) {
+ logBase = 1;
+ e = x.e - y.e;
+ } else {
+ base = BASE;
+ logBase = LOG_BASE;
+ e = mathfloor(x.e / logBase) - mathfloor(y.e / logBase);
+ }
+
+ yL = yd.length;
+ xL = xd.length;
+ q = new Ctor(sign);
+ qd = q.d = [];
+
+ // Result exponent may be one less than e.
+ // The digit array of a Decimal from toStringBinary may have trailing zeros.
+ for (i = 0; yd[i] == (xd[i] || 0); i++);
+
+ if (yd[i] > (xd[i] || 0)) e--;
+
+ if (pr == null) {
+ sd = pr = Ctor.precision;
+ rm = Ctor.rounding;
+ } else if (dp) {
+ sd = pr + (x.e - y.e) + 1;
+ } else {
+ sd = pr;
+ }
+
+ if (sd < 0) {
+ qd.push(1);
+ more = true;
+ } else {
+
+ // Convert precision in number of base 10 digits to base 1e7 digits.
+ sd = sd / logBase + 2 | 0;
+ i = 0;
+
+ // divisor < 1e7
+ if (yL == 1) {
+ k = 0;
+ yd = yd[0];
+ sd++;
+
+ // k is the carry.
+ for (; (i < xL || k) && sd--; i++) {
+ t = k * base + (xd[i] || 0);
+ qd[i] = t / yd | 0;
+ k = t % yd | 0;
+ }
+
+ more = k || i < xL;
+
+ // divisor >= 1e7
+ } else {
+
+ // Normalise xd and yd so highest order digit of yd is >= base/2
+ k = base / (yd[0] + 1) | 0;
+
+ if (k > 1) {
+ yd = multiplyInteger(yd, k, base);
+ xd = multiplyInteger(xd, k, base);
+ yL = yd.length;
+ xL = xd.length;
+ }
+
+ xi = yL;
+ rem = xd.slice(0, yL);
+ remL = rem.length;
+
+ // Add zeros to make remainder as long as divisor.
+ for (; remL < yL;) rem[remL++] = 0;
+
+ yz = yd.slice();
+ yz.unshift(0);
+ yd0 = yd[0];
+
+ if (yd[1] >= base / 2) ++yd0;
+
+ do {
+ k = 0;
+
+ // Compare divisor and remainder.
+ cmp = compare(yd, rem, yL, remL);
+
+ // If divisor < remainder.
+ if (cmp < 0) {
+
+ // Calculate trial digit, k.
+ rem0 = rem[0];
+ if (yL != remL) rem0 = rem0 * base + (rem[1] || 0);
+
+ // k will be how many times the divisor goes into the current remainder.
+ k = rem0 / yd0 | 0;
+
+ // Algorithm:
+ // 1. product = divisor * trial digit (k)
+ // 2. if product > remainder: product -= divisor, k--
+ // 3. remainder -= product
+ // 4. if product was < remainder at 2:
+ // 5. compare new remainder and divisor
+ // 6. If remainder > divisor: remainder -= divisor, k++
+
+ if (k > 1) {
+ if (k >= base) k = base - 1;
+
+ // product = divisor * trial digit.
+ prod = multiplyInteger(yd, k, base);
+ prodL = prod.length;
+ remL = rem.length;
+
+ // Compare product and remainder.
+ cmp = compare(prod, rem, prodL, remL);
+
+ // product > remainder.
+ if (cmp == 1) {
+ k--;
+
+ // Subtract divisor from product.
+ subtract(prod, yL < prodL ? yz : yd, prodL, base);
+ }
+ } else {
+
+ // cmp is -1.
+ // If k is 0, there is no need to compare yd and rem again below, so change cmp to 1
+ // to avoid it. If k is 1 there is a need to compare yd and rem again below.
+ if (k == 0) cmp = k = 1;
+ prod = yd.slice();
+ }
+
+ prodL = prod.length;
+ if (prodL < remL) prod.unshift(0);
+
+ // Subtract product from remainder.
+ subtract(rem, prod, remL, base);
+
+ // If product was < previous remainder.
+ if (cmp == -1) {
+ remL = rem.length;
+
+ // Compare divisor and new remainder.
+ cmp = compare(yd, rem, yL, remL);
+
+ // If divisor < new remainder, subtract divisor from remainder.
+ if (cmp < 1) {
+ k++;
+
+ // Subtract divisor from remainder.
+ subtract(rem, yL < remL ? yz : yd, remL, base);
+ }
+ }
+
+ remL = rem.length;
+ } else if (cmp === 0) {
+ k++;
+ rem = [0];
+ } // if cmp === 1, k will be 0
+
+ // Add the next digit, k, to the result array.
+ qd[i++] = k;
+
+ // Update the remainder.
+ if (cmp && rem[0]) {
+ rem[remL++] = xd[xi] || 0;
+ } else {
+ rem = [xd[xi]];
+ remL = 1;
+ }
+
+ } while ((xi++ < xL || rem[0] !== void 0) && sd--);
+
+ more = rem[0] !== void 0;
+ }
+
+ // Leading zero?
+ if (!qd[0]) qd.shift();
+ }
+
+ // logBase is 1 when divide is being used for base conversion.
+ if (logBase == 1) {
+ q.e = e;
+ inexact = more;
+ } else {
+
+ // To calculate q.e, first get the number of digits of qd[0].
+ for (i = 1, k = qd[0]; k >= 10; k /= 10) i++;
+ q.e = i + e * logBase - 1;
+
+ finalise(q, dp ? pr + q.e + 1 : pr, rm, more);
+ }
+
+ return q;
+ };
+})();
+
+
+/*
+ * Round `x` to `sd` significant digits using rounding mode `rm`.
+ * Check for over/under-flow.
+ */
+ function finalise(x, sd, rm, isTruncated) {
+ var digits, i, j, k, rd, roundUp, w, xd, xdi,
+ Ctor = x.constructor;
+
+ // Don't round if sd is null or undefined.
+ out: if (sd != null) {
+ xd = x.d;
+
+ // Infinity/NaN.
+ if (!xd) return x;
+
+ // rd: the rounding digit, i.e. the digit after the digit that may be rounded up.
+ // w: the word of xd containing rd, a base 1e7 number.
+ // xdi: the index of w within xd.
+ // digits: the number of digits of w.
+ // i: what would be the index of rd within w if all the numbers were 7 digits long (i.e. if
+ // they had leading zeros)
+ // j: if > 0, the actual index of rd within w (if < 0, rd is a leading zero).
+
+ // Get the length of the first word of the digits array xd.
+ for (digits = 1, k = xd[0]; k >= 10; k /= 10) digits++;
+ i = sd - digits;
+
+ // Is the rounding digit in the first word of xd?
+ if (i < 0) {
+ i += LOG_BASE;
+ j = sd;
+ w = xd[xdi = 0];
+
+ // Get the rounding digit at index j of w.
+ rd = w / mathpow(10, digits - j - 1) % 10 | 0;
+ } else {
+ xdi = Math.ceil((i + 1) / LOG_BASE);
+ k = xd.length;
+ if (xdi >= k) {
+ if (isTruncated) {
+
+ // Needed by `naturalExponential`, `naturalLogarithm` and `squareRoot`.
+ for (; k++ <= xdi;) xd.push(0);
+ w = rd = 0;
+ digits = 1;
+ i %= LOG_BASE;
+ j = i - LOG_BASE + 1;
+ } else {
+ break out;
+ }
+ } else {
+ w = k = xd[xdi];
+
+ // Get the number of digits of w.
+ for (digits = 1; k >= 10; k /= 10) digits++;
+
+ // Get the index of rd within w.
+ i %= LOG_BASE;
+
+ // Get the index of rd within w, adjusted for leading zeros.
+ // The number of leading zeros of w is given by LOG_BASE - digits.
+ j = i - LOG_BASE + digits;
+
+ // Get the rounding digit at index j of w.
+ rd = j < 0 ? 0 : w / mathpow(10, digits - j - 1) % 10 | 0;
+ }
+ }
+
+ // Are there any non-zero digits after the rounding digit?
+ isTruncated = isTruncated || sd < 0 ||
+ xd[xdi + 1] !== void 0 || (j < 0 ? w : w % mathpow(10, digits - j - 1));
+
+ // The expression `w % mathpow(10, digits - j - 1)` returns all the digits of w to the right
+ // of the digit at (left-to-right) index j, e.g. if w is 908714 and j is 2, the expression
+ // will give 714.
+
+ roundUp = rm < 4
+ ? (rd || isTruncated) && (rm == 0 || rm == (x.s < 0 ? 3 : 2))
+ : rd > 5 || rd == 5 && (rm == 4 || isTruncated || rm == 6 &&
+
+ // Check whether the digit to the left of the rounding digit is odd.
+ ((i > 0 ? j > 0 ? w / mathpow(10, digits - j) : 0 : xd[xdi - 1]) % 10) & 1 ||
+ rm == (x.s < 0 ? 8 : 7));
+
+ if (sd < 1 || !xd[0]) {
+ xd.length = 0;
+ if (roundUp) {
+
+ // Convert sd to decimal places.
+ sd -= x.e + 1;
+
+ // 1, 0.1, 0.01, 0.001, 0.0001 etc.
+ xd[0] = mathpow(10, (LOG_BASE - sd % LOG_BASE) % LOG_BASE);
+ x.e = -sd || 0;
+ } else {
+
+ // Zero.
+ xd[0] = x.e = 0;
+ }
+
+ return x;
+ }
+
+ // Remove excess digits.
+ if (i == 0) {
+ xd.length = xdi;
+ k = 1;
+ xdi--;
+ } else {
+ xd.length = xdi + 1;
+ k = mathpow(10, LOG_BASE - i);
+
+ // E.g. 56700 becomes 56000 if 7 is the rounding digit.
+ // j > 0 means i > number of leading zeros of w.
+ xd[xdi] = j > 0 ? (w / mathpow(10, digits - j) % mathpow(10, j) | 0) * k : 0;
+ }
+
+ if (roundUp) {
+ for (;;) {
+
+ // Is the digit to be rounded up in the first word of xd?
+ if (xdi == 0) {
+
+ // i will be the length of xd[0] before k is added.
+ for (i = 1, j = xd[0]; j >= 10; j /= 10) i++;
+ j = xd[0] += k;
+ for (k = 1; j >= 10; j /= 10) k++;
+
+ // if i != k the length has increased.
+ if (i != k) {
+ x.e++;
+ if (xd[0] == BASE) xd[0] = 1;
+ }
+
+ break;
+ } else {
+ xd[xdi] += k;
+ if (xd[xdi] != BASE) break;
+ xd[xdi--] = 0;
+ k = 1;
+ }
+ }
+ }
+
+ // Remove trailing zeros.
+ for (i = xd.length; xd[--i] === 0;) xd.pop();
+ }
+
+ if (external) {
+
+ // Overflow?
+ if (x.e > Ctor.maxE) {
+
+ // Infinity.
+ x.d = null;
+ x.e = NaN;
+
+ // Underflow?
+ } else if (x.e < Ctor.minE) {
+
+ // Zero.
+ x.e = 0;
+ x.d = [0];
+ // Ctor.underflow = true;
+ } // else Ctor.underflow = false;
+ }
+
+ return x;
+}
+
+
+function finiteToString(x, isExp, sd) {
+ if (!x.isFinite()) return nonFiniteToString(x);
+ var k,
+ e = x.e,
+ str = digitsToString(x.d),
+ len = str.length;
+
+ if (isExp) {
+ if (sd && (k = sd - len) > 0) {
+ str = str.charAt(0) + '.' + str.slice(1) + getZeroString(k);
+ } else if (len > 1) {
+ str = str.charAt(0) + '.' + str.slice(1);
+ }
+
+ str = str + (x.e < 0 ? 'e' : 'e+') + x.e;
+ } else if (e < 0) {
+ str = '0.' + getZeroString(-e - 1) + str;
+ if (sd && (k = sd - len) > 0) str += getZeroString(k);
+ } else if (e >= len) {
+ str += getZeroString(e + 1 - len);
+ if (sd && (k = sd - e - 1) > 0) str = str + '.' + getZeroString(k);
+ } else {
+ if ((k = e + 1) < len) str = str.slice(0, k) + '.' + str.slice(k);
+ if (sd && (k = sd - len) > 0) {
+ if (e + 1 === len) str += '.';
+ str += getZeroString(k);
+ }
+ }
+
+ return str;
+}
+
+
+// Calculate the base 10 exponent from the base 1e7 exponent.
+function getBase10Exponent(digits, e) {
+ var w = digits[0];
+
+ // Add the number of digits of the first word of the digits array.
+ for ( e *= LOG_BASE; w >= 10; w /= 10) e++;
+ return e;
+}
+
+
+function getLn10(Ctor, sd, pr) {
+ if (sd > LN10_PRECISION) {
+
+ // Reset global state in case the exception is caught.
+ external = true;
+ if (pr) Ctor.precision = pr;
+ throw Error(precisionLimitExceeded);
+ }
+ return finalise(new Ctor(LN10), sd, 1, true);
+}
+
+
+function getPi(Ctor, sd, rm) {
+ if (sd > PI_PRECISION) throw Error(precisionLimitExceeded);
+ return finalise(new Ctor(PI), sd, rm, true);
+}
+
+
+function getPrecision(digits) {
+ var w = digits.length - 1,
+ len = w * LOG_BASE + 1;
+
+ w = digits[w];
+
+ // If non-zero...
+ if (w) {
+
+ // Subtract the number of trailing zeros of the last word.
+ for (; w % 10 == 0; w /= 10) len--;
+
+ // Add the number of digits of the first word.
+ for (w = digits[0]; w >= 10; w /= 10) len++;
+ }
+
+ return len;
+}
+
+
+function getZeroString(k) {
+ var zs = '';
+ for (; k--;) zs += '0';
+ return zs;
+}
+
+
+/*
+ * Return a new Decimal whose value is the value of Decimal `x` to the power `n`, where `n` is an
+ * integer of type number.
+ *
+ * Implements 'exponentiation by squaring'. Called by `pow` and `parseOther`.
+ *
+ */
+function intPow(Ctor, x, n, pr) {
+ var isTruncated,
+ r = new Ctor(1),
+
+ // Max n of 9007199254740991 takes 53 loop iterations.
+ // Maximum digits array length; leaves [28, 34] guard digits.
+ k = Math.ceil(pr / LOG_BASE + 4);
+
+ external = false;
+
+ for (;;) {
+ if (n % 2) {
+ r = r.times(x);
+ if (truncate(r.d, k)) isTruncated = true;
+ }
+
+ n = mathfloor(n / 2);
+ if (n === 0) {
+
+ // To ensure correct rounding when r.d is truncated, increment the last word if it is zero.
+ n = r.d.length - 1;
+ if (isTruncated && r.d[n] === 0) ++r.d[n];
+ break;
+ }
+
+ x = x.times(x);
+ truncate(x.d, k);
+ }
+
+ external = true;
+
+ return r;
+}
+
+
+function isOdd(n) {
+ return n.d[n.d.length - 1] & 1;
+}
+
+
+/*
+ * Handle `max` (`n` is -1) and `min` (`n` is 1).
+ */
+function maxOrMin(Ctor, args, n) {
+ var k, y,
+ x = new Ctor(args[0]),
+ i = 0;
+
+ for (; ++i < args.length;) {
+ y = new Ctor(args[i]);
+
+ // NaN?
+ if (!y.s) {
+ x = y;
+ break;
+ }
+
+ k = x.cmp(y);
+
+ if (k === n || k === 0 && x.s === n) {
+ x = y;
+ }
+ }
+
+ return x;
+}
+
+
+/*
+ * Return a new Decimal whose value is the natural exponential of `x` rounded to `sd` significant
+ * digits.
+ *
+ * Taylor/Maclaurin series.
+ *
+ * exp(x) = x^0/0! + x^1/1! + x^2/2! + x^3/3! + ...
+ *
+ * Argument reduction:
+ * Repeat x = x / 32, k += 5, until |x| < 0.1
+ * exp(x) = exp(x / 2^k)^(2^k)
+ *
+ * Previously, the argument was initially reduced by
+ * exp(x) = exp(r) * 10^k where r = x - k * ln10, k = floor(x / ln10)
+ * to first put r in the range [0, ln10], before dividing by 32 until |x| < 0.1, but this was
+ * found to be slower than just dividing repeatedly by 32 as above.
+ *
+ * Max integer argument: exp('20723265836946413') = 6.3e+9000000000000000
+ * Min integer argument: exp('-20723265836946411') = 1.2e-9000000000000000
+ * (Math object integer min/max: Math.exp(709) = 8.2e+307, Math.exp(-745) = 5e-324)
+ *
+ * exp(Infinity) = Infinity
+ * exp(-Infinity) = 0
+ * exp(NaN) = NaN
+ * exp(±0) = 1
+ *
+ * exp(x) is non-terminating for any finite, non-zero x.
+ *
+ * The result will always be correctly rounded.
+ *
+ */
+function naturalExponential(x, sd) {
+ var denominator, guard, j, pow, sum, t, wpr,
+ rep = 0,
+ i = 0,
+ k = 0,
+ Ctor = x.constructor,
+ rm = Ctor.rounding,
+ pr = Ctor.precision;
+
+ // 0/NaN/Infinity?
+ if (!x.d || !x.d[0] || x.e > 17) {
+
+ return new Ctor(x.d
+ ? !x.d[0] ? 1 : x.s < 0 ? 0 : 1 / 0
+ : x.s ? x.s < 0 ? 0 : x : 0 / 0);
+ }
+
+ if (sd == null) {
+ external = false;
+ wpr = pr;
+ } else {
+ wpr = sd;
+ }
+
+ t = new Ctor(0.03125);
+
+ // while abs(x) >= 0.1
+ while (x.e > -2) {
+
+ // x = x / 2^5
+ x = x.times(t);
+ k += 5;
+ }
+
+ // Use 2 * log10(2^k) + 5 (empirically derived) to estimate the increase in precision
+ // necessary to ensure the first 4 rounding digits are correct.
+ guard = Math.log(mathpow(2, k)) / Math.LN10 * 2 + 5 | 0;
+ wpr += guard;
+ denominator = pow = sum = new Ctor(1);
+ Ctor.precision = wpr;
+
+ for (;;) {
+ pow = finalise(pow.times(x), wpr, 1);
+ denominator = denominator.times(++i);
+ t = sum.plus(divide(pow, denominator, wpr, 1));
+
+ if (digitsToString(t.d).slice(0, wpr) === digitsToString(sum.d).slice(0, wpr)) {
+ j = k;
+ while (j--) sum = finalise(sum.times(sum), wpr, 1);
+
+ // Check to see if the first 4 rounding digits are [49]999.
+ // If so, repeat the summation with a higher precision, otherwise
+ // e.g. with precision: 18, rounding: 1
+ // exp(18.404272462595034083567793919843761) = 98372560.1229999999 (should be 98372560.123)
+ // `wpr - guard` is the index of first rounding digit.
+ if (sd == null) {
+
+ if (rep < 3 && checkRoundingDigits(sum.d, wpr - guard, rm, rep)) {
+ Ctor.precision = wpr += 10;
+ denominator = pow = t = new Ctor(1);
+ i = 0;
+ rep++;
+ } else {
+ return finalise(sum, Ctor.precision = pr, rm, external = true);
+ }
+ } else {
+ Ctor.precision = pr;
+ return sum;
+ }
+ }
+
+ sum = t;
+ }
+}
+
+
+/*
+ * Return a new Decimal whose value is the natural logarithm of `x` rounded to `sd` significant
+ * digits.
+ *
+ * ln(-n) = NaN
+ * ln(0) = -Infinity
+ * ln(-0) = -Infinity
+ * ln(1) = 0
+ * ln(Infinity) = Infinity
+ * ln(-Infinity) = NaN
+ * ln(NaN) = NaN
+ *
+ * ln(n) (n != 1) is non-terminating.
+ *
+ */
+function naturalLogarithm(y, sd) {
+ var c, c0, denominator, e, numerator, rep, sum, t, wpr, x1, x2,
+ n = 1,
+ guard = 10,
+ x = y,
+ xd = x.d,
+ Ctor = x.constructor,
+ rm = Ctor.rounding,
+ pr = Ctor.precision;
+
+ // Is x negative or Infinity, NaN, 0 or 1?
+ if (x.s < 0 || !xd || !xd[0] || !x.e && xd[0] == 1 && xd.length == 1) {
+ return new Ctor(xd && !xd[0] ? -1 / 0 : x.s != 1 ? NaN : xd ? 0 : x);
+ }
+
+ if (sd == null) {
+ external = false;
+ wpr = pr;
+ } else {
+ wpr = sd;
+ }
+
+ Ctor.precision = wpr += guard;
+ c = digitsToString(xd);
+ c0 = c.charAt(0);
+
+ if (Math.abs(e = x.e) < 1.5e15) {
+
+ // Argument reduction.
+ // The series converges faster the closer the argument is to 1, so using
+ // ln(a^b) = b * ln(a), ln(a) = ln(a^b) / b
+ // multiply the argument by itself until the leading digits of the significand are 7, 8, 9,
+ // 10, 11, 12 or 13, recording the number of multiplications so the sum of the series can
+ // later be divided by this number, then separate out the power of 10 using
+ // ln(a*10^b) = ln(a) + b*ln(10).
+
+ // max n is 21 (gives 0.9, 1.0 or 1.1) (9e15 / 21 = 4.2e14).
+ //while (c0 < 9 && c0 != 1 || c0 == 1 && c.charAt(1) > 1) {
+ // max n is 6 (gives 0.7 - 1.3)
+ while (c0 < 7 && c0 != 1 || c0 == 1 && c.charAt(1) > 3) {
+ x = x.times(y);
+ c = digitsToString(x.d);
+ c0 = c.charAt(0);
+ n++;
+ }
+
+ e = x.e;
+
+ if (c0 > 1) {
+ x = new Ctor('0.' + c);
+ e++;
+ } else {
+ x = new Ctor(c0 + '.' + c.slice(1));
+ }
+ } else {
+
+ // The argument reduction method above may result in overflow if the argument y is a massive
+ // number with exponent >= 1500000000000000 (9e15 / 6 = 1.5e15), so instead recall this
+ // function using ln(x*10^e) = ln(x) + e*ln(10).
+ t = getLn10(Ctor, wpr + 2, pr).times(e + '');
+ x = naturalLogarithm(new Ctor(c0 + '.' + c.slice(1)), wpr - guard).plus(t);
+ Ctor.precision = pr;
+
+ return sd == null ? finalise(x, pr, rm, external = true) : x;
+ }
+
+ // x1 is x reduced to a value near 1.
+ x1 = x;
+
+ // Taylor series.
+ // ln(y) = ln((1 + x)/(1 - x)) = 2(x + x^3/3 + x^5/5 + x^7/7 + ...)
+ // where x = (y - 1)/(y + 1) (|x| < 1)
+ sum = numerator = x = divide(x.minus(1), x.plus(1), wpr, 1);
+ x2 = finalise(x.times(x), wpr, 1);
+ denominator = 3;
+
+ for (;;) {
+ numerator = finalise(numerator.times(x2), wpr, 1);
+ t = sum.plus(divide(numerator, new Ctor(denominator), wpr, 1));
+
+ if (digitsToString(t.d).slice(0, wpr) === digitsToString(sum.d).slice(0, wpr)) {
+ sum = sum.times(2);
+
+ // Reverse the argument reduction. Check that e is not 0 because, besides preventing an
+ // unnecessary calculation, -0 + 0 = +0 and to ensure correct rounding -0 needs to stay -0.
+ if (e !== 0) sum = sum.plus(getLn10(Ctor, wpr + 2, pr).times(e + ''));
+ sum = divide(sum, new Ctor(n), wpr, 1);
+
+ // Is rm > 3 and the first 4 rounding digits 4999, or rm < 4 (or the summation has
+ // been repeated previously) and the first 4 rounding digits 9999?
+ // If so, restart the summation with a higher precision, otherwise
+ // e.g. with precision: 12, rounding: 1
+ // ln(135520028.6126091714265381533) = 18.7246299999 when it should be 18.72463.
+ // `wpr - guard` is the index of first rounding digit.
+ if (sd == null) {
+ if (checkRoundingDigits(sum.d, wpr - guard, rm, rep)) {
+ Ctor.precision = wpr += guard;
+ t = numerator = x = divide(x1.minus(1), x1.plus(1), wpr, 1);
+ x2 = finalise(x.times(x), wpr, 1);
+ denominator = rep = 1;
+ } else {
+ return finalise(sum, Ctor.precision = pr, rm, external = true);
+ }
+ } else {
+ Ctor.precision = pr;
+ return sum;
+ }
+ }
+
+ sum = t;
+ denominator += 2;
+ }
+}
+
+
+// ±Infinity, NaN.
+function nonFiniteToString(x) {
+ // Unsigned.
+ return String(x.s * x.s / 0);
+}
+
+
+/*
+ * Parse the value of a new Decimal `x` from string `str`.
+ */
+function parseDecimal(x, str) {
+ var e, i, len;
+
+ // TODO BigInt str: no need to check for decimal point, exponential form or leading zeros.
+ // Decimal point?
+ if ((e = str.indexOf('.')) > -1) str = str.replace('.', '');
+
+ // Exponential form?
+ if ((i = str.search(/e/i)) > 0) {
+
+ // Determine exponent.
+ if (e < 0) e = i;
+ e += +str.slice(i + 1);
+ str = str.substring(0, i);
+ } else if (e < 0) {
+
+ // Integer.
+ e = str.length;
+ }
+
+ // Determine leading zeros.
+ for (i = 0; str.charCodeAt(i) === 48; i++);
+
+ // Determine trailing zeros.
+ for (len = str.length; str.charCodeAt(len - 1) === 48; --len);
+ str = str.slice(i, len);
+
+ if (str) {
+ len -= i;
+ x.e = e = e - i - 1;
+ x.d = [];
+
+ // Transform base
+
+ // e is the base 10 exponent.
+ // i is where to slice str to get the first word of the digits array.
+ i = (e + 1) % LOG_BASE;
+ if (e < 0) i += LOG_BASE;
+
+ if (i < len) {
+ if (i) x.d.push(+str.slice(0, i));
+ for (len -= LOG_BASE; i < len;) x.d.push(+str.slice(i, i += LOG_BASE));
+ str = str.slice(i);
+ i = LOG_BASE - str.length;
+ } else {
+ i -= len;
+ }
+
+ for (; i--;) str += '0';
+ x.d.push(+str);
+
+ if (external) {
+
+ // Overflow?
+ if (x.e > x.constructor.maxE) {
+
+ // Infinity.
+ x.d = null;
+ x.e = NaN;
+
+ // Underflow?
+ } else if (x.e < x.constructor.minE) {
+
+ // Zero.
+ x.e = 0;
+ x.d = [0];
+ // x.constructor.underflow = true;
+ } // else x.constructor.underflow = false;
+ }
+ } else {
+
+ // Zero.
+ x.e = 0;
+ x.d = [0];
+ }
+
+ return x;
+}
+
+
+/*
+ * Parse the value of a new Decimal `x` from a string `str`, which is not a decimal value.
+ */
+function parseOther(x, str) {
+ var base, Ctor, divisor, i, isFloat, len, p, xd, xe;
+
+ if (str.indexOf('_') > -1) {
+ str = str.replace(/(\d)_(?=\d)/g, '$1');
+ if (isDecimal.test(str)) return parseDecimal(x, str);
+ } else if (str === 'Infinity' || str === 'NaN') {
+ if (!+str) x.s = NaN;
+ x.e = NaN;
+ x.d = null;
+ return x;
+ }
+
+ if (isHex.test(str)) {
+ base = 16;
+ str = str.toLowerCase();
+ } else if (isBinary.test(str)) {
+ base = 2;
+ } else if (isOctal.test(str)) {
+ base = 8;
+ } else {
+ throw Error(invalidArgument + str);
+ }
+
+ // Is there a binary exponent part?
+ i = str.search(/p/i);
+
+ if (i > 0) {
+ p = +str.slice(i + 1);
+ str = str.substring(2, i);
+ } else {
+ str = str.slice(2);
+ }
+
+ // Convert `str` as an integer then divide the result by `base` raised to a power such that the
+ // fraction part will be restored.
+ i = str.indexOf('.');
+ isFloat = i >= 0;
+ Ctor = x.constructor;
+
+ if (isFloat) {
+ str = str.replace('.', '');
+ len = str.length;
+ i = len - i;
+
+ // log[10](16) = 1.2041... , log[10](88) = 1.9444....
+ divisor = intPow(Ctor, new Ctor(base), i, i * 2);
+ }
+
+ xd = convertBase(str, base, BASE);
+ xe = xd.length - 1;
+
+ // Remove trailing zeros.
+ for (i = xe; xd[i] === 0; --i) xd.pop();
+ if (i < 0) return new Ctor(x.s * 0);
+ x.e = getBase10Exponent(xd, xe);
+ x.d = xd;
+ external = false;
+
+ // At what precision to perform the division to ensure exact conversion?
+ // maxDecimalIntegerPartDigitCount = ceil(log[10](b) * otherBaseIntegerPartDigitCount)
+ // log[10](2) = 0.30103, log[10](8) = 0.90309, log[10](16) = 1.20412
+ // E.g. ceil(1.2 * 3) = 4, so up to 4 decimal digits are needed to represent 3 hex int digits.
+ // maxDecimalFractionPartDigitCount = {Hex:4|Oct:3|Bin:1} * otherBaseFractionPartDigitCount
+ // Therefore using 4 * the number of digits of str will always be enough.
+ if (isFloat) x = divide(x, divisor, len * 4);
+
+ // Multiply by the binary exponent part if present.
+ if (p) x = x.times(Math.abs(p) < 54 ? mathpow(2, p) : Decimal.pow(2, p));
+ external = true;
+
+ return x;
+}
+
+
+/*
+ * sin(x) = x - x^3/3! + x^5/5! - ...
+ * |x| < pi/2
+ *
+ */
+function sine(Ctor, x) {
+ var k,
+ len = x.d.length;
+
+ if (len < 3) {
+ return x.isZero() ? x : taylorSeries(Ctor, 2, x, x);
+ }
+
+ // Argument reduction: sin(5x) = 16*sin^5(x) - 20*sin^3(x) + 5*sin(x)
+ // i.e. sin(x) = 16*sin^5(x/5) - 20*sin^3(x/5) + 5*sin(x/5)
+ // and sin(x) = sin(x/5)(5 + sin^2(x/5)(16sin^2(x/5) - 20))
+
+ // Estimate the optimum number of times to use the argument reduction.
+ k = 1.4 * Math.sqrt(len);
+ k = k > 16 ? 16 : k | 0;
+
+ x = x.times(1 / tinyPow(5, k));
+ x = taylorSeries(Ctor, 2, x, x);
+
+ // Reverse argument reduction
+ var sin2_x,
+ d5 = new Ctor(5),
+ d16 = new Ctor(16),
+ d20 = new Ctor(20);
+ for (; k--;) {
+ sin2_x = x.times(x);
+ x = x.times(d5.plus(sin2_x.times(d16.times(sin2_x).minus(d20))));
+ }
+
+ return x;
+}
+
+
+// Calculate Taylor series for `cos`, `cosh`, `sin` and `sinh`.
+function taylorSeries(Ctor, n, x, y, isHyperbolic) {
+ var j, t, u, x2,
+ i = 1,
+ pr = Ctor.precision,
+ k = Math.ceil(pr / LOG_BASE);
+
+ external = false;
+ x2 = x.times(x);
+ u = new Ctor(y);
+
+ for (;;) {
+ t = divide(u.times(x2), new Ctor(n++ * n++), pr, 1);
+ u = isHyperbolic ? y.plus(t) : y.minus(t);
+ y = divide(t.times(x2), new Ctor(n++ * n++), pr, 1);
+ t = u.plus(y);
+
+ if (t.d[k] !== void 0) {
+ for (j = k; t.d[j] === u.d[j] && j--;);
+ if (j == -1) break;
+ }
+
+ j = u;
+ u = y;
+ y = t;
+ t = j;
+ i++;
+ }
+
+ external = true;
+ t.d.length = k + 1;
+
+ return t;
+}
+
+
+// Exponent e must be positive and non-zero.
+function tinyPow(b, e) {
+ var n = b;
+ while (--e) n *= b;
+ return n;
+}
+
+
+// Return the absolute value of `x` reduced to less than or equal to half pi.
+function toLessThanHalfPi(Ctor, x) {
+ var t,
+ isNeg = x.s < 0,
+ pi = getPi(Ctor, Ctor.precision, 1),
+ halfPi = pi.times(0.5);
+
+ x = x.abs();
+
+ if (x.lte(halfPi)) {
+ quadrant = isNeg ? 4 : 1;
+ return x;
+ }
+
+ t = x.divToInt(pi);
+
+ if (t.isZero()) {
+ quadrant = isNeg ? 3 : 2;
+ } else {
+ x = x.minus(t.times(pi));
+
+ // 0 <= x < pi
+ if (x.lte(halfPi)) {
+ quadrant = isOdd(t) ? (isNeg ? 2 : 3) : (isNeg ? 4 : 1);
+ return x;
+ }
+
+ quadrant = isOdd(t) ? (isNeg ? 1 : 4) : (isNeg ? 3 : 2);
+ }
+
+ return x.minus(pi).abs();
+}
+
+
+/*
+ * Return the value of Decimal `x` as a string in base `baseOut`.
+ *
+ * If the optional `sd` argument is present include a binary exponent suffix.
+ */
+function toStringBinary(x, baseOut, sd, rm) {
+ var base, e, i, k, len, roundUp, str, xd, y,
+ Ctor = x.constructor,
+ isExp = sd !== void 0;
+
+ if (isExp) {
+ checkInt32(sd, 1, MAX_DIGITS);
+ if (rm === void 0) rm = Ctor.rounding;
+ else checkInt32(rm, 0, 8);
+ } else {
+ sd = Ctor.precision;
+ rm = Ctor.rounding;
+ }
+
+ if (!x.isFinite()) {
+ str = nonFiniteToString(x);
+ } else {
+ str = finiteToString(x);
+ i = str.indexOf('.');
+
+ // Use exponential notation according to `toExpPos` and `toExpNeg`? No, but if required:
+ // maxBinaryExponent = floor((decimalExponent + 1) * log[2](10))
+ // minBinaryExponent = floor(decimalExponent * log[2](10))
+ // log[2](10) = 3.321928094887362347870319429489390175864
+
+ if (isExp) {
+ base = 2;
+ if (baseOut == 16) {
+ sd = sd * 4 - 3;
+ } else if (baseOut == 8) {
+ sd = sd * 3 - 2;
+ }
+ } else {
+ base = baseOut;
+ }
+
+ // Convert the number as an integer then divide the result by its base raised to a power such
+ // that the fraction part will be restored.
+
+ // Non-integer.
+ if (i >= 0) {
+ str = str.replace('.', '');
+ y = new Ctor(1);
+ y.e = str.length - i;
+ y.d = convertBase(finiteToString(y), 10, base);
+ y.e = y.d.length;
+ }
+
+ xd = convertBase(str, 10, base);
+ e = len = xd.length;
+
+ // Remove trailing zeros.
+ for (; xd[--len] == 0;) xd.pop();
+
+ if (!xd[0]) {
+ str = isExp ? '0p+0' : '0';
+ } else {
+ if (i < 0) {
+ e--;
+ } else {
+ x = new Ctor(x);
+ x.d = xd;
+ x.e = e;
+ x = divide(x, y, sd, rm, 0, base);
+ xd = x.d;
+ e = x.e;
+ roundUp = inexact;
+ }
+
+ // The rounding digit, i.e. the digit after the digit that may be rounded up.
+ i = xd[sd];
+ k = base / 2;
+ roundUp = roundUp || xd[sd + 1] !== void 0;
+
+ roundUp = rm < 4
+ ? (i !== void 0 || roundUp) && (rm === 0 || rm === (x.s < 0 ? 3 : 2))
+ : i > k || i === k && (rm === 4 || roundUp || rm === 6 && xd[sd - 1] & 1 ||
+ rm === (x.s < 0 ? 8 : 7));
+
+ xd.length = sd;
+
+ if (roundUp) {
+
+ // Rounding up may mean the previous digit has to be rounded up and so on.
+ for (; ++xd[--sd] > base - 1;) {
+ xd[sd] = 0;
+ if (!sd) {
+ ++e;
+ xd.unshift(1);
+ }
+ }
+ }
+
+ // Determine trailing zeros.
+ for (len = xd.length; !xd[len - 1]; --len);
+
+ // E.g. [4, 11, 15] becomes 4bf.
+ for (i = 0, str = ''; i < len; i++) str += NUMERALS.charAt(xd[i]);
+
+ // Add binary exponent suffix?
+ if (isExp) {
+ if (len > 1) {
+ if (baseOut == 16 || baseOut == 8) {
+ i = baseOut == 16 ? 4 : 3;
+ for (--len; len % i; len++) str += '0';
+ xd = convertBase(str, base, baseOut);
+ for (len = xd.length; !xd[len - 1]; --len);
+
+ // xd[0] will always be be 1
+ for (i = 1, str = '1.'; i < len; i++) str += NUMERALS.charAt(xd[i]);
+ } else {
+ str = str.charAt(0) + '.' + str.slice(1);
+ }
+ }
+
+ str = str + (e < 0 ? 'p' : 'p+') + e;
+ } else if (e < 0) {
+ for (; ++e;) str = '0' + str;
+ str = '0.' + str;
+ } else {
+ if (++e > len) for (e -= len; e-- ;) str += '0';
+ else if (e < len) str = str.slice(0, e) + '.' + str.slice(e);
+ }
+ }
+
+ str = (baseOut == 16 ? '0x' : baseOut == 2 ? '0b' : baseOut == 8 ? '0o' : '') + str;
+ }
+
+ return x.s < 0 ? '-' + str : str;
+}
+
+
+// Does not strip trailing zeros.
+function truncate(arr, len) {
+ if (arr.length > len) {
+ arr.length = len;
+ return true;
+ }
+}
+
+
+// Decimal methods
+
+
+/*
+ * abs
+ * acos
+ * acosh
+ * add
+ * asin
+ * asinh
+ * atan
+ * atanh
+ * atan2
+ * cbrt
+ * ceil
+ * clamp
+ * clone
+ * config
+ * cos
+ * cosh
+ * div
+ * exp
+ * floor
+ * hypot
+ * ln
+ * log
+ * log2
+ * log10
+ * max
+ * min
+ * mod
+ * mul
+ * pow
+ * random
+ * round
+ * set
+ * sign
+ * sin
+ * sinh
+ * sqrt
+ * sub
+ * sum
+ * tan
+ * tanh
+ * trunc
+ */
+
+
+/*
+ * Return a new Decimal whose value is the absolute value of `x`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function abs(x) {
+ return new this(x).abs();
+}
+
+
+/*
+ * Return a new Decimal whose value is the arccosine in radians of `x`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function acos(x) {
+ return new this(x).acos();
+}
+
+
+/*
+ * Return a new Decimal whose value is the inverse of the hyperbolic cosine of `x`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function acosh(x) {
+ return new this(x).acosh();
+}
+
+
+/*
+ * Return a new Decimal whose value is the sum of `x` and `y`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+function add(x, y) {
+ return new this(x).plus(y);
+}
+
+
+/*
+ * Return a new Decimal whose value is the arcsine in radians of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function asin(x) {
+ return new this(x).asin();
+}
+
+
+/*
+ * Return a new Decimal whose value is the inverse of the hyperbolic sine of `x`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function asinh(x) {
+ return new this(x).asinh();
+}
+
+
+/*
+ * Return a new Decimal whose value is the arctangent in radians of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function atan(x) {
+ return new this(x).atan();
+}
+
+
+/*
+ * Return a new Decimal whose value is the inverse of the hyperbolic tangent of `x`, rounded to
+ * `precision` significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function atanh(x) {
+ return new this(x).atanh();
+}
+
+
+/*
+ * Return a new Decimal whose value is the arctangent in radians of `y/x` in the range -pi to pi
+ * (inclusive), rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ * Domain: [-Infinity, Infinity]
+ * Range: [-pi, pi]
+ *
+ * y {number|string|bigint|Decimal} The y-coordinate.
+ * x {number|string|bigint|Decimal} The x-coordinate.
+ *
+ * atan2(±0, -0) = ±pi
+ * atan2(±0, +0) = ±0
+ * atan2(±0, -x) = ±pi for x > 0
+ * atan2(±0, x) = ±0 for x > 0
+ * atan2(-y, ±0) = -pi/2 for y > 0
+ * atan2(y, ±0) = pi/2 for y > 0
+ * atan2(±y, -Infinity) = ±pi for finite y > 0
+ * atan2(±y, +Infinity) = ±0 for finite y > 0
+ * atan2(±Infinity, x) = ±pi/2 for finite x
+ * atan2(±Infinity, -Infinity) = ±3*pi/4
+ * atan2(±Infinity, +Infinity) = ±pi/4
+ * atan2(NaN, x) = NaN
+ * atan2(y, NaN) = NaN
+ *
+ */
+function atan2(y, x) {
+ y = new this(y);
+ x = new this(x);
+ var r,
+ pr = this.precision,
+ rm = this.rounding,
+ wpr = pr + 4;
+
+ // Either NaN
+ if (!y.s || !x.s) {
+ r = new this(NaN);
+
+ // Both ±Infinity
+ } else if (!y.d && !x.d) {
+ r = getPi(this, wpr, 1).times(x.s > 0 ? 0.25 : 0.75);
+ r.s = y.s;
+
+ // x is ±Infinity or y is ±0
+ } else if (!x.d || y.isZero()) {
+ r = x.s < 0 ? getPi(this, pr, rm) : new this(0);
+ r.s = y.s;
+
+ // y is ±Infinity or x is ±0
+ } else if (!y.d || x.isZero()) {
+ r = getPi(this, wpr, 1).times(0.5);
+ r.s = y.s;
+
+ // Both non-zero and finite
+ } else if (x.s < 0) {
+ this.precision = wpr;
+ this.rounding = 1;
+ r = this.atan(divide(y, x, wpr, 1));
+ x = getPi(this, wpr, 1);
+ this.precision = pr;
+ this.rounding = rm;
+ r = y.s < 0 ? r.minus(x) : r.plus(x);
+ } else {
+ r = this.atan(divide(y, x, wpr, 1));
+ }
+
+ return r;
+}
+
+
+/*
+ * Return a new Decimal whose value is the cube root of `x`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function cbrt(x) {
+ return new this(x).cbrt();
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` rounded to an integer using `ROUND_CEIL`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function ceil(x) {
+ return finalise(x = new this(x), x.e + 1, 2);
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` clamped to the range delineated by `min` and `max`.
+ *
+ * x {number|string|bigint|Decimal}
+ * min {number|string|bigint|Decimal}
+ * max {number|string|bigint|Decimal}
+ *
+ */
+function clamp(x, min, max) {
+ return new this(x).clamp(min, max);
+}
+
+
+/*
+ * Configure global settings for a Decimal constructor.
+ *
+ * `obj` is an object with one or more of the following properties,
+ *
+ * precision {number}
+ * rounding {number}
+ * toExpNeg {number}
+ * toExpPos {number}
+ * maxE {number}
+ * minE {number}
+ * modulo {number}
+ * crypto {boolean|number}
+ * defaults {true}
+ *
+ * E.g. Decimal.config({ precision: 20, rounding: 4 })
+ *
+ */
+function config(obj) {
+ if (!obj || typeof obj !== 'object') throw Error(decimalError + 'Object expected');
+ var i, p, v,
+ useDefaults = obj.defaults === true,
+ ps = [
+ 'precision', 1, MAX_DIGITS,
+ 'rounding', 0, 8,
+ 'toExpNeg', -EXP_LIMIT, 0,
+ 'toExpPos', 0, EXP_LIMIT,
+ 'maxE', 0, EXP_LIMIT,
+ 'minE', -EXP_LIMIT, 0,
+ 'modulo', 0, 9
+ ];
+
+ for (i = 0; i < ps.length; i += 3) {
+ if (p = ps[i], useDefaults) this[p] = DEFAULTS[p];
+ if ((v = obj[p]) !== void 0) {
+ if (mathfloor(v) === v && v >= ps[i + 1] && v <= ps[i + 2]) this[p] = v;
+ else throw Error(invalidArgument + p + ': ' + v);
+ }
+ }
+
+ if (p = 'crypto', useDefaults) this[p] = DEFAULTS[p];
+ if ((v = obj[p]) !== void 0) {
+ if (v === true || v === false || v === 0 || v === 1) {
+ if (v) {
+ if (typeof crypto != 'undefined' && crypto &&
+ (crypto.getRandomValues || crypto.randomBytes)) {
+ this[p] = true;
+ } else {
+ throw Error(cryptoUnavailable);
+ }
+ } else {
+ this[p] = false;
+ }
+ } else {
+ throw Error(invalidArgument + p + ': ' + v);
+ }
+ }
+
+ return this;
+}
+
+
+/*
+ * Return a new Decimal whose value is the cosine of `x`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function cos(x) {
+ return new this(x).cos();
+}
+
+
+/*
+ * Return a new Decimal whose value is the hyperbolic cosine of `x`, rounded to precision
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function cosh(x) {
+ return new this(x).cosh();
+}
+
+
+/*
+ * Create and return a Decimal constructor with the same configuration properties as this Decimal
+ * constructor.
+ *
+ */
+function clone(obj) {
+ var i, p, ps;
+
+ /*
+ * The Decimal constructor and exported function.
+ * Return a new Decimal instance.
+ *
+ * v {number|string|bigint|Decimal} A numeric value.
+ *
+ */
+ function Decimal(v) {
+ var e, i, t,
+ x = this;
+
+ // Decimal called without new.
+ if (!(x instanceof Decimal)) return new Decimal(v);
+
+ // Retain a reference to this Decimal constructor, and shadow Decimal.prototype.constructor
+ // which points to Object.
+ x.constructor = Decimal;
+
+ if (isDecimalInstance(v)) {
+ x.s = v.s;
+
+ if (external) {
+ if (!v.d || v.e > Decimal.maxE) {
+
+ // Infinity.
+ x.e = NaN;
+ x.d = null;
+ } else if (v.e < Decimal.minE) {
+
+ // Zero.
+ x.e = 0;
+ x.d = [0];
+ } else {
+ x.e = v.e;
+ x.d = v.d.slice();
+ }
+ } else {
+ x.e = v.e;
+ x.d = v.d ? v.d.slice() : v.d;
+ }
+
+ return;
+ }
+
+ t = typeof v;
+
+ if (t === 'number') {
+ if (v === 0) {
+ x.s = 1 / v < 0 ? -1 : 1;
+ x.e = 0;
+ x.d = [0];
+ return;
+ }
+
+ if (v < 0) {
+ v = -v;
+ x.s = -1;
+ } else {
+ x.s = 1;
+ }
+
+ // Fast path for small integers.
+ if (v === ~~v && v < 1e7) {
+ for (e = 0, i = v; i >= 10; i /= 10) e++;
+
+ if (external) {
+ if (e > Decimal.maxE) {
+ x.e = NaN;
+ x.d = null;
+ } else if (e < Decimal.minE) {
+ x.e = 0;
+ x.d = [0];
+ } else {
+ x.e = e;
+ x.d = [v];
+ }
+ } else {
+ x.e = e;
+ x.d = [v];
+ }
+
+ return;
+ }
+
+ // Infinity or NaN?
+ if (v * 0 !== 0) {
+ if (!v) x.s = NaN;
+ x.e = NaN;
+ x.d = null;
+ return;
+ }
+
+ return parseDecimal(x, v.toString());
+ }
+
+ if (t === 'string') {
+ if ((i = v.charCodeAt(0)) === 45) { // minus sign
+ v = v.slice(1);
+ x.s = -1;
+ } else {
+ if (i === 43) v = v.slice(1); // plus sign
+ x.s = 1;
+ }
+
+ return isDecimal.test(v) ? parseDecimal(x, v) : parseOther(x, v);
+ }
+
+ if (t === 'bigint') {
+ if (v < 0) {
+ v = -v;
+ x.s = -1;
+ } else {
+ x.s = 1;
+ }
+
+ return parseDecimal(x, v.toString());
+ }
+
+ throw Error(invalidArgument + v);
+ }
+
+ Decimal.prototype = P;
+
+ Decimal.ROUND_UP = 0;
+ Decimal.ROUND_DOWN = 1;
+ Decimal.ROUND_CEIL = 2;
+ Decimal.ROUND_FLOOR = 3;
+ Decimal.ROUND_HALF_UP = 4;
+ Decimal.ROUND_HALF_DOWN = 5;
+ Decimal.ROUND_HALF_EVEN = 6;
+ Decimal.ROUND_HALF_CEIL = 7;
+ Decimal.ROUND_HALF_FLOOR = 8;
+ Decimal.EUCLID = 9;
+
+ Decimal.config = Decimal.set = config;
+ Decimal.clone = clone;
+ Decimal.isDecimal = isDecimalInstance;
+
+ Decimal.abs = abs;
+ Decimal.acos = acos;
+ Decimal.acosh = acosh; // ES6
+ Decimal.add = add;
+ Decimal.asin = asin;
+ Decimal.asinh = asinh; // ES6
+ Decimal.atan = atan;
+ Decimal.atanh = atanh; // ES6
+ Decimal.atan2 = atan2;
+ Decimal.cbrt = cbrt; // ES6
+ Decimal.ceil = ceil;
+ Decimal.clamp = clamp;
+ Decimal.cos = cos;
+ Decimal.cosh = cosh; // ES6
+ Decimal.div = div;
+ Decimal.exp = exp;
+ Decimal.floor = floor;
+ Decimal.hypot = hypot; // ES6
+ Decimal.ln = ln;
+ Decimal.log = log;
+ Decimal.log10 = log10; // ES6
+ Decimal.log2 = log2; // ES6
+ Decimal.max = max;
+ Decimal.min = min;
+ Decimal.mod = mod;
+ Decimal.mul = mul;
+ Decimal.pow = pow;
+ Decimal.random = random;
+ Decimal.round = round;
+ Decimal.sign = sign; // ES6
+ Decimal.sin = sin;
+ Decimal.sinh = sinh; // ES6
+ Decimal.sqrt = sqrt;
+ Decimal.sub = sub;
+ Decimal.sum = sum;
+ Decimal.tan = tan;
+ Decimal.tanh = tanh; // ES6
+ Decimal.trunc = trunc; // ES6
+
+ if (obj === void 0) obj = {};
+ if (obj) {
+ if (obj.defaults !== true) {
+ ps = ['precision', 'rounding', 'toExpNeg', 'toExpPos', 'maxE', 'minE', 'modulo', 'crypto'];
+ for (i = 0; i < ps.length;) if (!obj.hasOwnProperty(p = ps[i++])) obj[p] = this[p];
+ }
+ }
+
+ Decimal.config(obj);
+
+ return Decimal;
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` divided by `y`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+function div(x, y) {
+ return new this(x).div(y);
+}
+
+
+/*
+ * Return a new Decimal whose value is the natural exponential of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} The power to which to raise the base of the natural log.
+ *
+ */
+function exp(x) {
+ return new this(x).exp();
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` round to an integer using `ROUND_FLOOR`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function floor(x) {
+ return finalise(x = new this(x), x.e + 1, 3);
+}
+
+
+/*
+ * Return a new Decimal whose value is the square root of the sum of the squares of the arguments,
+ * rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ * hypot(a, b, ...) = sqrt(a^2 + b^2 + ...)
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ */
+function hypot() {
+ var i, n,
+ t = new this(0);
+
+ external = false;
+
+ for (i = 0; i < arguments.length;) {
+ n = new this(arguments[i++]);
+ if (!n.d) {
+ if (n.s) {
+ external = true;
+ return new this(1 / 0);
+ }
+ t = n;
+ } else if (t.d) {
+ t = t.plus(n.times(n));
+ }
+ }
+
+ external = true;
+
+ return t.sqrt();
+}
+
+
+/*
+ * Return true if object is a Decimal instance (where Decimal is any Decimal constructor),
+ * otherwise return false.
+ *
+ */
+function isDecimalInstance(obj) {
+ return obj instanceof Decimal || obj && obj.toStringTag === tag || false;
+}
+
+
+/*
+ * Return a new Decimal whose value is the natural logarithm of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function ln(x) {
+ return new this(x).ln();
+}
+
+
+/*
+ * Return a new Decimal whose value is the log of `x` to the base `y`, or to base 10 if no base
+ * is specified, rounded to `precision` significant digits using rounding mode `rounding`.
+ *
+ * log[y](x)
+ *
+ * x {number|string|bigint|Decimal} The argument of the logarithm.
+ * y {number|string|bigint|Decimal} The base of the logarithm.
+ *
+ */
+function log(x, y) {
+ return new this(x).log(y);
+}
+
+
+/*
+ * Return a new Decimal whose value is the base 2 logarithm of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function log2(x) {
+ return new this(x).log(2);
+}
+
+
+/*
+ * Return a new Decimal whose value is the base 10 logarithm of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function log10(x) {
+ return new this(x).log(10);
+}
+
+
+/*
+ * Return a new Decimal whose value is the maximum of the arguments.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ */
+function max() {
+ return maxOrMin(this, arguments, -1);
+}
+
+
+/*
+ * Return a new Decimal whose value is the minimum of the arguments.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ */
+function min() {
+ return maxOrMin(this, arguments, 1);
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` modulo `y`, rounded to `precision` significant digits
+ * using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+function mod(x, y) {
+ return new this(x).mod(y);
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` multiplied by `y`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+function mul(x, y) {
+ return new this(x).mul(y);
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` raised to the power `y`, rounded to precision
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} The base.
+ * y {number|string|bigint|Decimal} The exponent.
+ *
+ */
+function pow(x, y) {
+ return new this(x).pow(y);
+}
+
+
+/*
+ * Returns a new Decimal with a random value equal to or greater than 0 and less than 1, and with
+ * `sd`, or `Decimal.precision` if `sd` is omitted, significant digits (or less if trailing zeros
+ * are produced).
+ *
+ * [sd] {number} Significant digits. Integer, 0 to MAX_DIGITS inclusive.
+ *
+ */
+function random(sd) {
+ var d, e, k, n,
+ i = 0,
+ r = new this(1),
+ rd = [];
+
+ if (sd === void 0) sd = this.precision;
+ else checkInt32(sd, 1, MAX_DIGITS);
+
+ k = Math.ceil(sd / LOG_BASE);
+
+ if (!this.crypto) {
+ for (; i < k;) rd[i++] = Math.random() * 1e7 | 0;
+
+ // Browsers supporting crypto.getRandomValues.
+ } else if (crypto.getRandomValues) {
+ d = crypto.getRandomValues(new Uint32Array(k));
+
+ for (; i < k;) {
+ n = d[i];
+
+ // 0 <= n < 4294967296
+ // Probability n >= 4.29e9, is 4967296 / 4294967296 = 0.00116 (1 in 865).
+ if (n >= 4.29e9) {
+ d[i] = crypto.getRandomValues(new Uint32Array(1))[0];
+ } else {
+
+ // 0 <= n <= 4289999999
+ // 0 <= (n % 1e7) <= 9999999
+ rd[i++] = n % 1e7;
+ }
+ }
+
+ // Node.js supporting crypto.randomBytes.
+ } else if (crypto.randomBytes) {
+
+ // buffer
+ d = crypto.randomBytes(k *= 4);
+
+ for (; i < k;) {
+
+ // 0 <= n < 2147483648
+ n = d[i] + (d[i + 1] << 8) + (d[i + 2] << 16) + ((d[i + 3] & 0x7f) << 24);
+
+ // Probability n >= 2.14e9, is 7483648 / 2147483648 = 0.0035 (1 in 286).
+ if (n >= 2.14e9) {
+ crypto.randomBytes(4).copy(d, i);
+ } else {
+
+ // 0 <= n <= 2139999999
+ // 0 <= (n % 1e7) <= 9999999
+ rd.push(n % 1e7);
+ i += 4;
+ }
+ }
+
+ i = k / 4;
+ } else {
+ throw Error(cryptoUnavailable);
+ }
+
+ k = rd[--i];
+ sd %= LOG_BASE;
+
+ // Convert trailing digits to zeros according to sd.
+ if (k && sd) {
+ n = mathpow(10, LOG_BASE - sd);
+ rd[i] = (k / n | 0) * n;
+ }
+
+ // Remove trailing words which are zero.
+ for (; rd[i] === 0; i--) rd.pop();
+
+ // Zero?
+ if (i < 0) {
+ e = 0;
+ rd = [0];
+ } else {
+ e = -1;
+
+ // Remove leading words which are zero and adjust exponent accordingly.
+ for (; rd[0] === 0; e -= LOG_BASE) rd.shift();
+
+ // Count the digits of the first word of rd to determine leading zeros.
+ for (k = 1, n = rd[0]; n >= 10; n /= 10) k++;
+
+ // Adjust the exponent for leading zeros of the first word of rd.
+ if (k < LOG_BASE) e -= LOG_BASE - k;
+ }
+
+ r.e = e;
+ r.d = rd;
+
+ return r;
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` rounded to an integer using rounding mode `rounding`.
+ *
+ * To emulate `Math.round`, set rounding to 7 (ROUND_HALF_CEIL).
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function round(x) {
+ return finalise(x = new this(x), x.e + 1, this.rounding);
+}
+
+
+/*
+ * Return
+ * 1 if x > 0,
+ * -1 if x < 0,
+ * 0 if x is 0,
+ * -0 if x is -0,
+ * NaN otherwise
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function sign(x) {
+ x = new this(x);
+ return x.d ? (x.d[0] ? x.s : 0 * x.s) : x.s || NaN;
+}
+
+
+/*
+ * Return a new Decimal whose value is the sine of `x`, rounded to `precision` significant digits
+ * using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function sin(x) {
+ return new this(x).sin();
+}
+
+
+/*
+ * Return a new Decimal whose value is the hyperbolic sine of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function sinh(x) {
+ return new this(x).sinh();
+}
+
+
+/*
+ * Return a new Decimal whose value is the square root of `x`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function sqrt(x) {
+ return new this(x).sqrt();
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` minus `y`, rounded to `precision` significant digits
+ * using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal}
+ * y {number|string|bigint|Decimal}
+ *
+ */
+function sub(x, y) {
+ return new this(x).sub(y);
+}
+
+
+/*
+ * Return a new Decimal whose value is the sum of the arguments, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * Only the result is rounded, not the intermediate calculations.
+ *
+ * arguments {number|string|bigint|Decimal}
+ *
+ */
+function sum() {
+ var i = 0,
+ args = arguments,
+ x = new this(args[i]);
+
+ external = false;
+ for (; x.s && ++i < args.length;) x = x.plus(args[i]);
+ external = true;
+
+ return finalise(x, this.precision, this.rounding);
+}
+
+
+/*
+ * Return a new Decimal whose value is the tangent of `x`, rounded to `precision` significant
+ * digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function tan(x) {
+ return new this(x).tan();
+}
+
+
+/*
+ * Return a new Decimal whose value is the hyperbolic tangent of `x`, rounded to `precision`
+ * significant digits using rounding mode `rounding`.
+ *
+ * x {number|string|bigint|Decimal} A value in radians.
+ *
+ */
+function tanh(x) {
+ return new this(x).tanh();
+}
+
+
+/*
+ * Return a new Decimal whose value is `x` truncated to an integer.
+ *
+ * x {number|string|bigint|Decimal}
+ *
+ */
+function trunc(x) {
+ return finalise(x = new this(x), x.e + 1, 1);
+}
+
+
+P[Symbol.for('nodejs.util.inspect.custom')] = P.toString;
+P[Symbol.toStringTag] = 'Decimal';
+
+// Create and configure initial Decimal constructor.
+export var Decimal = P.constructor = clone(DEFAULTS);
+
+// Create the internal constants from their string values.
+LN10 = new Decimal(LN10);
+PI = new Decimal(PI);
+
+export default Decimal;
diff --git a/node_modules/decimal.js/package.json b/node_modules/decimal.js/package.json
new file mode 100644
index 0000000..601307d
--- /dev/null
+++ b/node_modules/decimal.js/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "decimal.js",
+ "description": "An arbitrary-precision Decimal type for JavaScript.",
+ "version": "10.6.0",
+ "keywords": [
+ "arbitrary",
+ "precision",
+ "arithmetic",
+ "big",
+ "number",
+ "decimal",
+ "float",
+ "biginteger",
+ "bigdecimal",
+ "bignumber",
+ "bigint",
+ "bignum"
+ ],
+ "repository" : {
+ "type": "git",
+ "url": "https://github.com/MikeMcl/decimal.js.git"
+ },
+ "main": "decimal",
+ "module": "decimal.mjs",
+ "browser": "decimal.js",
+ "exports": {
+ ".": {
+ "types": "./decimal.d.ts",
+ "import": "./decimal.mjs",
+ "require": "./decimal.js"
+ },
+ "./decimal.mjs": "./decimal.mjs",
+ "./decimal.js": "./decimal.js",
+ "./package.json": "./package.json",
+ "./decimal": {
+ "types": "./decimal.d.ts",
+ "import": "./decimal.mjs",
+ "require": "./decimal.js"
+ }
+ },
+ "author": {
+ "name": "Michael Mclaughlin",
+ "email": "M8ch88l@gmail.com"
+ },
+ "license": "MIT",
+ "scripts": {
+ "test": "node ./test/test.js"
+ },
+ "types": "decimal.d.ts",
+ "files": [
+ "decimal.js",
+ "decimal.mjs",
+ "decimal.d.ts"
+ ]
+}
diff --git a/node_modules/denque/CHANGELOG.md b/node_modules/denque/CHANGELOG.md
new file mode 100644
index 0000000..391a1f5
--- /dev/null
+++ b/node_modules/denque/CHANGELOG.md
@@ -0,0 +1,29 @@
+## 2.1.0
+
+ - fix: issue where `clear()` is still keeping references to the elements (#47)
+ - refactor: performance optimizations for growth and array copy (#43)
+ - refactor: performance optimizations for toArray and fromArray (#46)
+ - test: add additional benchmarks for queue growth and `toArray` (#45)
+
+## 2.0.1
+
+ - fix(types): incorrect return type on `size()`
+
+## 2.0.0
+
+ - fix!: `push` & `unshift` now accept `undefined` values to match behaviour of `Array` (fixes #25) (#35)
+ - This is only a **BREAKING** change if you are currently expecting `push(undefined)` and `unshift(undefined)` to do
+ nothing - the new behaviour now correctly adds undefined values to the queue.
+ - **Note**: behaviour of `push()` & `unshift()` (no arguments) remains unchanged (nothing gets added to the queue).
+ - **Note**: If you need to differentiate between `undefined` values in the queue and the return value of `pop()` then
+ check the queue `.length` before popping.
+ - fix: incorrect methods in types definition file
+
+## 1.5.1
+
+ - perf: minor performance tweak when growing queue size (#29)
+
+## 1.5.0
+
+ - feat: adds capacity option for circular buffers (#27)
+
diff --git a/node_modules/denque/LICENSE b/node_modules/denque/LICENSE
new file mode 100644
index 0000000..c9cde92
--- /dev/null
+++ b/node_modules/denque/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2018-present Invertase Limited
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/node_modules/denque/README.md b/node_modules/denque/README.md
new file mode 100644
index 0000000..3c645d3
--- /dev/null
+++ b/node_modules/denque/README.md
@@ -0,0 +1,77 @@
+
+
Denque
+
+
+
+
+
+
+
+
+
+
+
+Denque is a well tested, extremely fast and lightweight [double-ended queue](http://en.wikipedia.org/wiki/Double-ended_queue)
+implementation with zero dependencies and includes TypeScript types.
+
+Double-ended queues can also be used as a:
+
+- [Stack](http://en.wikipedia.org/wiki/Stack_\(abstract_data_type\))
+- [Queue](http://en.wikipedia.org/wiki/Queue_\(data_structure\))
+
+This implementation is currently the fastest available, even faster than `double-ended-queue`, see the [benchmarks](https://docs.page/invertase/denque/benchmarks).
+
+Every queue operation is done at a constant `O(1)` - including random access from `.peekAt(index)`.
+
+**Works on all node versions >= v0.10**
+
+## Quick Start
+
+Install the package:
+
+```bash
+npm install denque
+```
+
+Create and consume a queue:
+
+```js
+const Denque = require("denque");
+
+const denque = new Denque([1,2,3,4]);
+denque.shift(); // 1
+denque.pop(); // 4
+```
+
+
+See the [API reference documentation](https://docs.page/invertase/denque/api) for more examples.
+
+---
+
+## Who's using it?
+
+- [Kafka Node.js client](https://www.npmjs.com/package/kafka-node)
+- [MariaDB Node.js client](https://www.npmjs.com/package/mariadb)
+- [MongoDB Node.js client](https://www.npmjs.com/package/mongodb)
+- [MySQL Node.js client](https://www.npmjs.com/package/mysql2)
+- [Redis Node.js clients](https://www.npmjs.com/package/redis)
+
+... and [many more](https://www.npmjs.com/browse/depended/denque).
+
+
+---
+
+## License
+
+- See [LICENSE](/LICENSE)
+
+---
+
+
+
+
+
+
+ Built and maintained by Invertase .
+
+
diff --git a/node_modules/denque/index.d.ts b/node_modules/denque/index.d.ts
new file mode 100644
index 0000000..e125dd4
--- /dev/null
+++ b/node_modules/denque/index.d.ts
@@ -0,0 +1,47 @@
+declare class Denque {
+ length: number;
+
+ constructor();
+
+ constructor(array: T[]);
+
+ constructor(array: T[], options: IDenqueOptions);
+
+ push(item: T): number;
+
+ unshift(item: T): number;
+
+ pop(): T | undefined;
+
+ shift(): T | undefined;
+
+ peekBack(): T | undefined;
+
+ peekFront(): T | undefined;
+
+ peekAt(index: number): T | undefined;
+
+ get(index: number): T | undefined;
+
+ remove(index: number, count: number): T[];
+
+ removeOne(index: number): T | undefined;
+
+ splice(index: number, count: number, ...item: T[]): T[] | undefined;
+
+ isEmpty(): boolean;
+
+ clear(): void;
+
+ size(): number;
+
+ toString(): string;
+
+ toArray(): T[];
+}
+
+interface IDenqueOptions {
+ capacity?: number
+}
+
+export = Denque;
diff --git a/node_modules/denque/index.js b/node_modules/denque/index.js
new file mode 100644
index 0000000..6b2e9d8
--- /dev/null
+++ b/node_modules/denque/index.js
@@ -0,0 +1,481 @@
+'use strict';
+
+/**
+ * Custom implementation of a double ended queue.
+ */
+function Denque(array, options) {
+ var options = options || {};
+ this._capacity = options.capacity;
+
+ this._head = 0;
+ this._tail = 0;
+
+ if (Array.isArray(array)) {
+ this._fromArray(array);
+ } else {
+ this._capacityMask = 0x3;
+ this._list = new Array(4);
+ }
+}
+
+/**
+ * --------------
+ * PUBLIC API
+ * -------------
+ */
+
+/**
+ * Returns the item at the specified index from the list.
+ * 0 is the first element, 1 is the second, and so on...
+ * Elements at negative values are that many from the end: -1 is one before the end
+ * (the last element), -2 is two before the end (one before last), etc.
+ * @param index
+ * @returns {*}
+ */
+Denque.prototype.peekAt = function peekAt(index) {
+ var i = index;
+ // expect a number or return undefined
+ if ((i !== (i | 0))) {
+ return void 0;
+ }
+ var len = this.size();
+ if (i >= len || i < -len) return undefined;
+ if (i < 0) i += len;
+ i = (this._head + i) & this._capacityMask;
+ return this._list[i];
+};
+
+/**
+ * Alias for peekAt()
+ * @param i
+ * @returns {*}
+ */
+Denque.prototype.get = function get(i) {
+ return this.peekAt(i);
+};
+
+/**
+ * Returns the first item in the list without removing it.
+ * @returns {*}
+ */
+Denque.prototype.peek = function peek() {
+ if (this._head === this._tail) return undefined;
+ return this._list[this._head];
+};
+
+/**
+ * Alias for peek()
+ * @returns {*}
+ */
+Denque.prototype.peekFront = function peekFront() {
+ return this.peek();
+};
+
+/**
+ * Returns the item that is at the back of the queue without removing it.
+ * Uses peekAt(-1)
+ */
+Denque.prototype.peekBack = function peekBack() {
+ return this.peekAt(-1);
+};
+
+/**
+ * Returns the current length of the queue
+ * @return {Number}
+ */
+Object.defineProperty(Denque.prototype, 'length', {
+ get: function length() {
+ return this.size();
+ }
+});
+
+/**
+ * Return the number of items on the list, or 0 if empty.
+ * @returns {number}
+ */
+Denque.prototype.size = function size() {
+ if (this._head === this._tail) return 0;
+ if (this._head < this._tail) return this._tail - this._head;
+ else return this._capacityMask + 1 - (this._head - this._tail);
+};
+
+/**
+ * Add an item at the beginning of the list.
+ * @param item
+ */
+Denque.prototype.unshift = function unshift(item) {
+ if (arguments.length === 0) return this.size();
+ var len = this._list.length;
+ this._head = (this._head - 1 + len) & this._capacityMask;
+ this._list[this._head] = item;
+ if (this._tail === this._head) this._growArray();
+ if (this._capacity && this.size() > this._capacity) this.pop();
+ if (this._head < this._tail) return this._tail - this._head;
+ else return this._capacityMask + 1 - (this._head - this._tail);
+};
+
+/**
+ * Remove and return the first item on the list,
+ * Returns undefined if the list is empty.
+ * @returns {*}
+ */
+Denque.prototype.shift = function shift() {
+ var head = this._head;
+ if (head === this._tail) return undefined;
+ var item = this._list[head];
+ this._list[head] = undefined;
+ this._head = (head + 1) & this._capacityMask;
+ if (head < 2 && this._tail > 10000 && this._tail <= this._list.length >>> 2) this._shrinkArray();
+ return item;
+};
+
+/**
+ * Add an item to the bottom of the list.
+ * @param item
+ */
+Denque.prototype.push = function push(item) {
+ if (arguments.length === 0) return this.size();
+ var tail = this._tail;
+ this._list[tail] = item;
+ this._tail = (tail + 1) & this._capacityMask;
+ if (this._tail === this._head) {
+ this._growArray();
+ }
+ if (this._capacity && this.size() > this._capacity) {
+ this.shift();
+ }
+ if (this._head < this._tail) return this._tail - this._head;
+ else return this._capacityMask + 1 - (this._head - this._tail);
+};
+
+/**
+ * Remove and return the last item on the list.
+ * Returns undefined if the list is empty.
+ * @returns {*}
+ */
+Denque.prototype.pop = function pop() {
+ var tail = this._tail;
+ if (tail === this._head) return undefined;
+ var len = this._list.length;
+ this._tail = (tail - 1 + len) & this._capacityMask;
+ var item = this._list[this._tail];
+ this._list[this._tail] = undefined;
+ if (this._head < 2 && tail > 10000 && tail <= len >>> 2) this._shrinkArray();
+ return item;
+};
+
+/**
+ * Remove and return the item at the specified index from the list.
+ * Returns undefined if the list is empty.
+ * @param index
+ * @returns {*}
+ */
+Denque.prototype.removeOne = function removeOne(index) {
+ var i = index;
+ // expect a number or return undefined
+ if ((i !== (i | 0))) {
+ return void 0;
+ }
+ if (this._head === this._tail) return void 0;
+ var size = this.size();
+ var len = this._list.length;
+ if (i >= size || i < -size) return void 0;
+ if (i < 0) i += size;
+ i = (this._head + i) & this._capacityMask;
+ var item = this._list[i];
+ var k;
+ if (index < size / 2) {
+ for (k = index; k > 0; k--) {
+ this._list[i] = this._list[i = (i - 1 + len) & this._capacityMask];
+ }
+ this._list[i] = void 0;
+ this._head = (this._head + 1 + len) & this._capacityMask;
+ } else {
+ for (k = size - 1 - index; k > 0; k--) {
+ this._list[i] = this._list[i = (i + 1 + len) & this._capacityMask];
+ }
+ this._list[i] = void 0;
+ this._tail = (this._tail - 1 + len) & this._capacityMask;
+ }
+ return item;
+};
+
+/**
+ * Remove number of items from the specified index from the list.
+ * Returns array of removed items.
+ * Returns undefined if the list is empty.
+ * @param index
+ * @param count
+ * @returns {array}
+ */
+Denque.prototype.remove = function remove(index, count) {
+ var i = index;
+ var removed;
+ var del_count = count;
+ // expect a number or return undefined
+ if ((i !== (i | 0))) {
+ return void 0;
+ }
+ if (this._head === this._tail) return void 0;
+ var size = this.size();
+ var len = this._list.length;
+ if (i >= size || i < -size || count < 1) return void 0;
+ if (i < 0) i += size;
+ if (count === 1 || !count) {
+ removed = new Array(1);
+ removed[0] = this.removeOne(i);
+ return removed;
+ }
+ if (i === 0 && i + count >= size) {
+ removed = this.toArray();
+ this.clear();
+ return removed;
+ }
+ if (i + count > size) count = size - i;
+ var k;
+ removed = new Array(count);
+ for (k = 0; k < count; k++) {
+ removed[k] = this._list[(this._head + i + k) & this._capacityMask];
+ }
+ i = (this._head + i) & this._capacityMask;
+ if (index + count === size) {
+ this._tail = (this._tail - count + len) & this._capacityMask;
+ for (k = count; k > 0; k--) {
+ this._list[i = (i + 1 + len) & this._capacityMask] = void 0;
+ }
+ return removed;
+ }
+ if (index === 0) {
+ this._head = (this._head + count + len) & this._capacityMask;
+ for (k = count - 1; k > 0; k--) {
+ this._list[i = (i + 1 + len) & this._capacityMask] = void 0;
+ }
+ return removed;
+ }
+ if (i < size / 2) {
+ this._head = (this._head + index + count + len) & this._capacityMask;
+ for (k = index; k > 0; k--) {
+ this.unshift(this._list[i = (i - 1 + len) & this._capacityMask]);
+ }
+ i = (this._head - 1 + len) & this._capacityMask;
+ while (del_count > 0) {
+ this._list[i = (i - 1 + len) & this._capacityMask] = void 0;
+ del_count--;
+ }
+ if (index < 0) this._tail = i;
+ } else {
+ this._tail = i;
+ i = (i + count + len) & this._capacityMask;
+ for (k = size - (count + index); k > 0; k--) {
+ this.push(this._list[i++]);
+ }
+ i = this._tail;
+ while (del_count > 0) {
+ this._list[i = (i + 1 + len) & this._capacityMask] = void 0;
+ del_count--;
+ }
+ }
+ if (this._head < 2 && this._tail > 10000 && this._tail <= len >>> 2) this._shrinkArray();
+ return removed;
+};
+
+/**
+ * Native splice implementation.
+ * Remove number of items from the specified index from the list and/or add new elements.
+ * Returns array of removed items or empty array if count == 0.
+ * Returns undefined if the list is empty.
+ *
+ * @param index
+ * @param count
+ * @param {...*} [elements]
+ * @returns {array}
+ */
+Denque.prototype.splice = function splice(index, count) {
+ var i = index;
+ // expect a number or return undefined
+ if ((i !== (i | 0))) {
+ return void 0;
+ }
+ var size = this.size();
+ if (i < 0) i += size;
+ if (i > size) return void 0;
+ if (arguments.length > 2) {
+ var k;
+ var temp;
+ var removed;
+ var arg_len = arguments.length;
+ var len = this._list.length;
+ var arguments_index = 2;
+ if (!size || i < size / 2) {
+ temp = new Array(i);
+ for (k = 0; k < i; k++) {
+ temp[k] = this._list[(this._head + k) & this._capacityMask];
+ }
+ if (count === 0) {
+ removed = [];
+ if (i > 0) {
+ this._head = (this._head + i + len) & this._capacityMask;
+ }
+ } else {
+ removed = this.remove(i, count);
+ this._head = (this._head + i + len) & this._capacityMask;
+ }
+ while (arg_len > arguments_index) {
+ this.unshift(arguments[--arg_len]);
+ }
+ for (k = i; k > 0; k--) {
+ this.unshift(temp[k - 1]);
+ }
+ } else {
+ temp = new Array(size - (i + count));
+ var leng = temp.length;
+ for (k = 0; k < leng; k++) {
+ temp[k] = this._list[(this._head + i + count + k) & this._capacityMask];
+ }
+ if (count === 0) {
+ removed = [];
+ if (i != size) {
+ this._tail = (this._head + i + len) & this._capacityMask;
+ }
+ } else {
+ removed = this.remove(i, count);
+ this._tail = (this._tail - leng + len) & this._capacityMask;
+ }
+ while (arguments_index < arg_len) {
+ this.push(arguments[arguments_index++]);
+ }
+ for (k = 0; k < leng; k++) {
+ this.push(temp[k]);
+ }
+ }
+ return removed;
+ } else {
+ return this.remove(i, count);
+ }
+};
+
+/**
+ * Soft clear - does not reset capacity.
+ */
+Denque.prototype.clear = function clear() {
+ this._list = new Array(this._list.length);
+ this._head = 0;
+ this._tail = 0;
+};
+
+/**
+ * Returns true or false whether the list is empty.
+ * @returns {boolean}
+ */
+Denque.prototype.isEmpty = function isEmpty() {
+ return this._head === this._tail;
+};
+
+/**
+ * Returns an array of all queue items.
+ * @returns {Array}
+ */
+Denque.prototype.toArray = function toArray() {
+ return this._copyArray(false);
+};
+
+/**
+ * -------------
+ * INTERNALS
+ * -------------
+ */
+
+/**
+ * Fills the queue with items from an array
+ * For use in the constructor
+ * @param array
+ * @private
+ */
+Denque.prototype._fromArray = function _fromArray(array) {
+ var length = array.length;
+ var capacity = this._nextPowerOf2(length);
+
+ this._list = new Array(capacity);
+ this._capacityMask = capacity - 1;
+ this._tail = length;
+
+ for (var i = 0; i < length; i++) this._list[i] = array[i];
+};
+
+/**
+ *
+ * @param fullCopy
+ * @param size Initialize the array with a specific size. Will default to the current list size
+ * @returns {Array}
+ * @private
+ */
+Denque.prototype._copyArray = function _copyArray(fullCopy, size) {
+ var src = this._list;
+ var capacity = src.length;
+ var length = this.length;
+ size = size | length;
+
+ // No prealloc requested and the buffer is contiguous
+ if (size == length && this._head < this._tail) {
+ // Simply do a fast slice copy
+ return this._list.slice(this._head, this._tail);
+ }
+
+ var dest = new Array(size);
+
+ var k = 0;
+ var i;
+ if (fullCopy || this._head > this._tail) {
+ for (i = this._head; i < capacity; i++) dest[k++] = src[i];
+ for (i = 0; i < this._tail; i++) dest[k++] = src[i];
+ } else {
+ for (i = this._head; i < this._tail; i++) dest[k++] = src[i];
+ }
+
+ return dest;
+}
+
+/**
+ * Grows the internal list array.
+ * @private
+ */
+Denque.prototype._growArray = function _growArray() {
+ if (this._head != 0) {
+ // double array size and copy existing data, head to end, then beginning to tail.
+ var newList = this._copyArray(true, this._list.length << 1);
+
+ this._tail = this._list.length;
+ this._head = 0;
+
+ this._list = newList;
+ } else {
+ this._tail = this._list.length;
+ this._list.length <<= 1;
+ }
+
+ this._capacityMask = (this._capacityMask << 1) | 1;
+};
+
+/**
+ * Shrinks the internal list array.
+ * @private
+ */
+Denque.prototype._shrinkArray = function _shrinkArray() {
+ this._list.length >>>= 1;
+ this._capacityMask >>>= 1;
+};
+
+/**
+ * Find the next power of 2, at least 4
+ * @private
+ * @param {number} num
+ * @returns {number}
+ */
+Denque.prototype._nextPowerOf2 = function _nextPowerOf2(num) {
+ var log2 = Math.log(num) / Math.log(2);
+ var nextPow2 = 1 << (log2 + 1);
+
+ return Math.max(nextPow2, 4);
+}
+
+module.exports = Denque;
diff --git a/node_modules/denque/package.json b/node_modules/denque/package.json
new file mode 100644
index 0000000..a635910
--- /dev/null
+++ b/node_modules/denque/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "denque",
+ "version": "2.1.0",
+ "description": "The fastest javascript implementation of a double-ended queue. Used by the official Redis, MongoDB, MariaDB & MySQL libraries for Node.js and many other libraries. Maintains compatability with deque.",
+ "main": "index.js",
+ "engines": {
+ "node": ">=0.10"
+ },
+ "keywords": [
+ "data-structure",
+ "data-structures",
+ "queue",
+ "double",
+ "end",
+ "ended",
+ "deque",
+ "denque",
+ "double-ended-queue"
+ ],
+ "scripts": {
+ "test": "istanbul cover --report lcov _mocha && npm run typescript",
+ "coveralls": "cat ./coverage/lcov.info | coveralls",
+ "typescript": "tsc --project ./test/type/tsconfig.json",
+ "benchmark_thousand": "node benchmark/thousand",
+ "benchmark_2mil": "node benchmark/two_million",
+ "benchmark_splice": "node benchmark/splice",
+ "benchmark_remove": "node benchmark/remove",
+ "benchmark_removeOne": "node benchmark/removeOne",
+ "benchmark_growth": "node benchmark/growth",
+ "benchmark_toArray": "node benchmark/toArray",
+ "benchmark_fromArray": "node benchmark/fromArray"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/invertase/denque.git"
+ },
+ "license": "Apache-2.0",
+ "author": {
+ "name": "Invertase",
+ "email": "oss@invertase.io",
+ "url": "http://github.com/invertase/"
+ },
+ "contributors": [
+ "Mike Diarmid (Salakar) "
+ ],
+ "bugs": {
+ "url": "https://github.com/invertase/denque/issues"
+ },
+ "homepage": "https://docs.page/invertase/denque",
+ "devDependencies": {
+ "benchmark": "^2.1.4",
+ "codecov": "^3.8.3",
+ "double-ended-queue": "^2.1.0-0",
+ "istanbul": "^0.4.5",
+ "mocha": "^3.5.3",
+ "typescript": "^3.4.1"
+ }
+}
diff --git a/node_modules/detect-libc/LICENSE b/node_modules/detect-libc/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/node_modules/detect-libc/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/node_modules/detect-libc/README.md b/node_modules/detect-libc/README.md
new file mode 100644
index 0000000..23212fd
--- /dev/null
+++ b/node_modules/detect-libc/README.md
@@ -0,0 +1,163 @@
+# detect-libc
+
+Node.js module to detect details of the C standard library (libc)
+implementation provided by a given Linux system.
+
+Currently supports detection of GNU glibc and MUSL libc.
+
+Provides asychronous and synchronous functions for the
+family (e.g. `glibc`, `musl`) and version (e.g. `1.23`, `1.2.3`).
+
+The version numbers of libc implementations
+are not guaranteed to be semver-compliant.
+
+For previous v1.x releases, please see the
+[v1](https://github.com/lovell/detect-libc/tree/v1) branch.
+
+## Install
+
+```sh
+npm install detect-libc
+```
+
+## API
+
+### GLIBC
+
+```ts
+const GLIBC: string = 'glibc';
+```
+
+A String constant containing the value `glibc`.
+
+### MUSL
+
+```ts
+const MUSL: string = 'musl';
+```
+
+A String constant containing the value `musl`.
+
+### family
+
+```ts
+function family(): Promise;
+```
+
+Resolves asychronously with:
+
+* `glibc` or `musl` when the libc family can be determined
+* `null` when the libc family cannot be determined
+* `null` when run on a non-Linux platform
+
+```js
+const { family, GLIBC, MUSL } = require('detect-libc');
+
+switch (await family()) {
+ case GLIBC: ...
+ case MUSL: ...
+ case null: ...
+}
+```
+
+### familySync
+
+```ts
+function familySync(): string | null;
+```
+
+Synchronous version of `family()`.
+
+```js
+const { familySync, GLIBC, MUSL } = require('detect-libc');
+
+switch (familySync()) {
+ case GLIBC: ...
+ case MUSL: ...
+ case null: ...
+}
+```
+
+### version
+
+```ts
+function version(): Promise;
+```
+
+Resolves asychronously with:
+
+* The version when it can be determined
+* `null` when the libc family cannot be determined
+* `null` when run on a non-Linux platform
+
+```js
+const { version } = require('detect-libc');
+
+const v = await version();
+if (v) {
+ const [major, minor, patch] = v.split('.');
+}
+```
+
+### versionSync
+
+```ts
+function versionSync(): string | null;
+```
+
+Synchronous version of `version()`.
+
+```js
+const { versionSync } = require('detect-libc');
+
+const v = versionSync();
+if (v) {
+ const [major, minor, patch] = v.split('.');
+}
+```
+
+### isNonGlibcLinux
+
+```ts
+function isNonGlibcLinux(): Promise;
+```
+
+Resolves asychronously with:
+
+* `false` when the libc family is `glibc`
+* `true` when the libc family is not `glibc`
+* `false` when run on a non-Linux platform
+
+```js
+const { isNonGlibcLinux } = require('detect-libc');
+
+if (await isNonGlibcLinux()) { ... }
+```
+
+### isNonGlibcLinuxSync
+
+```ts
+function isNonGlibcLinuxSync(): boolean;
+```
+
+Synchronous version of `isNonGlibcLinux()`.
+
+```js
+const { isNonGlibcLinuxSync } = require('detect-libc');
+
+if (isNonGlibcLinuxSync()) { ... }
+```
+
+## Licensing
+
+Copyright 2017 Lovell Fuller and others.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/node_modules/detect-libc/index.d.ts b/node_modules/detect-libc/index.d.ts
new file mode 100644
index 0000000..4c0fb2b
--- /dev/null
+++ b/node_modules/detect-libc/index.d.ts
@@ -0,0 +1,14 @@
+// Copyright 2017 Lovell Fuller and others.
+// SPDX-License-Identifier: Apache-2.0
+
+export const GLIBC: 'glibc';
+export const MUSL: 'musl';
+
+export function family(): Promise;
+export function familySync(): string | null;
+
+export function isNonGlibcLinux(): Promise;
+export function isNonGlibcLinuxSync(): boolean;
+
+export function version(): Promise;
+export function versionSync(): string | null;
diff --git a/node_modules/detect-libc/lib/detect-libc.js b/node_modules/detect-libc/lib/detect-libc.js
new file mode 100644
index 0000000..01299b4
--- /dev/null
+++ b/node_modules/detect-libc/lib/detect-libc.js
@@ -0,0 +1,313 @@
+// Copyright 2017 Lovell Fuller and others.
+// SPDX-License-Identifier: Apache-2.0
+
+'use strict';
+
+const childProcess = require('child_process');
+const { isLinux, getReport } = require('./process');
+const { LDD_PATH, SELF_PATH, readFile, readFileSync } = require('./filesystem');
+const { interpreterPath } = require('./elf');
+
+let cachedFamilyInterpreter;
+let cachedFamilyFilesystem;
+let cachedVersionFilesystem;
+
+const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true';
+let commandOut = '';
+
+const safeCommand = () => {
+ if (!commandOut) {
+ return new Promise((resolve) => {
+ childProcess.exec(command, (err, out) => {
+ commandOut = err ? ' ' : out;
+ resolve(commandOut);
+ });
+ });
+ }
+ return commandOut;
+};
+
+const safeCommandSync = () => {
+ if (!commandOut) {
+ try {
+ commandOut = childProcess.execSync(command, { encoding: 'utf8' });
+ } catch (_err) {
+ commandOut = ' ';
+ }
+ }
+ return commandOut;
+};
+
+/**
+ * A String constant containing the value `glibc`.
+ * @type {string}
+ * @public
+ */
+const GLIBC = 'glibc';
+
+/**
+ * A Regexp constant to get the GLIBC Version.
+ * @type {string}
+ */
+const RE_GLIBC_VERSION = /LIBC[a-z0-9 \-).]*?(\d+\.\d+)/i;
+
+/**
+ * A String constant containing the value `musl`.
+ * @type {string}
+ * @public
+ */
+const MUSL = 'musl';
+
+const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-');
+
+const familyFromReport = () => {
+ const report = getReport();
+ if (report.header && report.header.glibcVersionRuntime) {
+ return GLIBC;
+ }
+ if (Array.isArray(report.sharedObjects)) {
+ if (report.sharedObjects.some(isFileMusl)) {
+ return MUSL;
+ }
+ }
+ return null;
+};
+
+const familyFromCommand = (out) => {
+ const [getconf, ldd1] = out.split(/[\r\n]+/);
+ if (getconf && getconf.includes(GLIBC)) {
+ return GLIBC;
+ }
+ if (ldd1 && ldd1.includes(MUSL)) {
+ return MUSL;
+ }
+ return null;
+};
+
+const familyFromInterpreterPath = (path) => {
+ if (path) {
+ if (path.includes('/ld-musl-')) {
+ return MUSL;
+ } else if (path.includes('/ld-linux-')) {
+ return GLIBC;
+ }
+ }
+ return null;
+};
+
+const getFamilyFromLddContent = (content) => {
+ content = content.toString();
+ if (content.includes('musl')) {
+ return MUSL;
+ }
+ if (content.includes('GNU C Library')) {
+ return GLIBC;
+ }
+ return null;
+};
+
+const familyFromFilesystem = async () => {
+ if (cachedFamilyFilesystem !== undefined) {
+ return cachedFamilyFilesystem;
+ }
+ cachedFamilyFilesystem = null;
+ try {
+ const lddContent = await readFile(LDD_PATH);
+ cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
+ } catch (e) {}
+ return cachedFamilyFilesystem;
+};
+
+const familyFromFilesystemSync = () => {
+ if (cachedFamilyFilesystem !== undefined) {
+ return cachedFamilyFilesystem;
+ }
+ cachedFamilyFilesystem = null;
+ try {
+ const lddContent = readFileSync(LDD_PATH);
+ cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
+ } catch (e) {}
+ return cachedFamilyFilesystem;
+};
+
+const familyFromInterpreter = async () => {
+ if (cachedFamilyInterpreter !== undefined) {
+ return cachedFamilyInterpreter;
+ }
+ cachedFamilyInterpreter = null;
+ try {
+ const selfContent = await readFile(SELF_PATH);
+ const path = interpreterPath(selfContent);
+ cachedFamilyInterpreter = familyFromInterpreterPath(path);
+ } catch (e) {}
+ return cachedFamilyInterpreter;
+};
+
+const familyFromInterpreterSync = () => {
+ if (cachedFamilyInterpreter !== undefined) {
+ return cachedFamilyInterpreter;
+ }
+ cachedFamilyInterpreter = null;
+ try {
+ const selfContent = readFileSync(SELF_PATH);
+ const path = interpreterPath(selfContent);
+ cachedFamilyInterpreter = familyFromInterpreterPath(path);
+ } catch (e) {}
+ return cachedFamilyInterpreter;
+};
+
+/**
+ * Resolves with the libc family when it can be determined, `null` otherwise.
+ * @returns {Promise}
+ */
+const family = async () => {
+ let family = null;
+ if (isLinux()) {
+ family = await familyFromInterpreter();
+ if (!family) {
+ family = await familyFromFilesystem();
+ if (!family) {
+ family = familyFromReport();
+ }
+ if (!family) {
+ const out = await safeCommand();
+ family = familyFromCommand(out);
+ }
+ }
+ }
+ return family;
+};
+
+/**
+ * Returns the libc family when it can be determined, `null` otherwise.
+ * @returns {?string}
+ */
+const familySync = () => {
+ let family = null;
+ if (isLinux()) {
+ family = familyFromInterpreterSync();
+ if (!family) {
+ family = familyFromFilesystemSync();
+ if (!family) {
+ family = familyFromReport();
+ }
+ if (!family) {
+ const out = safeCommandSync();
+ family = familyFromCommand(out);
+ }
+ }
+ }
+ return family;
+};
+
+/**
+ * Resolves `true` only when the platform is Linux and the libc family is not `glibc`.
+ * @returns {Promise}
+ */
+const isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC;
+
+/**
+ * Returns `true` only when the platform is Linux and the libc family is not `glibc`.
+ * @returns {boolean}
+ */
+const isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC;
+
+const versionFromFilesystem = async () => {
+ if (cachedVersionFilesystem !== undefined) {
+ return cachedVersionFilesystem;
+ }
+ cachedVersionFilesystem = null;
+ try {
+ const lddContent = await readFile(LDD_PATH);
+ const versionMatch = lddContent.match(RE_GLIBC_VERSION);
+ if (versionMatch) {
+ cachedVersionFilesystem = versionMatch[1];
+ }
+ } catch (e) {}
+ return cachedVersionFilesystem;
+};
+
+const versionFromFilesystemSync = () => {
+ if (cachedVersionFilesystem !== undefined) {
+ return cachedVersionFilesystem;
+ }
+ cachedVersionFilesystem = null;
+ try {
+ const lddContent = readFileSync(LDD_PATH);
+ const versionMatch = lddContent.match(RE_GLIBC_VERSION);
+ if (versionMatch) {
+ cachedVersionFilesystem = versionMatch[1];
+ }
+ } catch (e) {}
+ return cachedVersionFilesystem;
+};
+
+const versionFromReport = () => {
+ const report = getReport();
+ if (report.header && report.header.glibcVersionRuntime) {
+ return report.header.glibcVersionRuntime;
+ }
+ return null;
+};
+
+const versionSuffix = (s) => s.trim().split(/\s+/)[1];
+
+const versionFromCommand = (out) => {
+ const [getconf, ldd1, ldd2] = out.split(/[\r\n]+/);
+ if (getconf && getconf.includes(GLIBC)) {
+ return versionSuffix(getconf);
+ }
+ if (ldd1 && ldd2 && ldd1.includes(MUSL)) {
+ return versionSuffix(ldd2);
+ }
+ return null;
+};
+
+/**
+ * Resolves with the libc version when it can be determined, `null` otherwise.
+ * @returns {Promise}
+ */
+const version = async () => {
+ let version = null;
+ if (isLinux()) {
+ version = await versionFromFilesystem();
+ if (!version) {
+ version = versionFromReport();
+ }
+ if (!version) {
+ const out = await safeCommand();
+ version = versionFromCommand(out);
+ }
+ }
+ return version;
+};
+
+/**
+ * Returns the libc version when it can be determined, `null` otherwise.
+ * @returns {?string}
+ */
+const versionSync = () => {
+ let version = null;
+ if (isLinux()) {
+ version = versionFromFilesystemSync();
+ if (!version) {
+ version = versionFromReport();
+ }
+ if (!version) {
+ const out = safeCommandSync();
+ version = versionFromCommand(out);
+ }
+ }
+ return version;
+};
+
+module.exports = {
+ GLIBC,
+ MUSL,
+ family,
+ familySync,
+ isNonGlibcLinux,
+ isNonGlibcLinuxSync,
+ version,
+ versionSync
+};
diff --git a/node_modules/detect-libc/lib/elf.js b/node_modules/detect-libc/lib/elf.js
new file mode 100644
index 0000000..aa166aa
--- /dev/null
+++ b/node_modules/detect-libc/lib/elf.js
@@ -0,0 +1,39 @@
+// Copyright 2017 Lovell Fuller and others.
+// SPDX-License-Identifier: Apache-2.0
+
+'use strict';
+
+const interpreterPath = (elf) => {
+ if (elf.length < 64) {
+ return null;
+ }
+ if (elf.readUInt32BE(0) !== 0x7F454C46) {
+ // Unexpected magic bytes
+ return null;
+ }
+ if (elf.readUInt8(4) !== 2) {
+ // Not a 64-bit ELF
+ return null;
+ }
+ if (elf.readUInt8(5) !== 1) {
+ // Not little-endian
+ return null;
+ }
+ const offset = elf.readUInt32LE(32);
+ const size = elf.readUInt16LE(54);
+ const count = elf.readUInt16LE(56);
+ for (let i = 0; i < count; i++) {
+ const headerOffset = offset + (i * size);
+ const type = elf.readUInt32LE(headerOffset);
+ if (type === 3) {
+ const fileOffset = elf.readUInt32LE(headerOffset + 8);
+ const fileSize = elf.readUInt32LE(headerOffset + 32);
+ return elf.subarray(fileOffset, fileOffset + fileSize).toString().replace(/\0.*$/g, '');
+ }
+ }
+ return null;
+};
+
+module.exports = {
+ interpreterPath
+};
diff --git a/node_modules/detect-libc/lib/filesystem.js b/node_modules/detect-libc/lib/filesystem.js
new file mode 100644
index 0000000..4c2443c
--- /dev/null
+++ b/node_modules/detect-libc/lib/filesystem.js
@@ -0,0 +1,51 @@
+// Copyright 2017 Lovell Fuller and others.
+// SPDX-License-Identifier: Apache-2.0
+
+'use strict';
+
+const fs = require('fs');
+
+const LDD_PATH = '/usr/bin/ldd';
+const SELF_PATH = '/proc/self/exe';
+const MAX_LENGTH = 2048;
+
+/**
+ * Read the content of a file synchronous
+ *
+ * @param {string} path
+ * @returns {Buffer}
+ */
+const readFileSync = (path) => {
+ const fd = fs.openSync(path, 'r');
+ const buffer = Buffer.alloc(MAX_LENGTH);
+ const bytesRead = fs.readSync(fd, buffer, 0, MAX_LENGTH, 0);
+ fs.close(fd, () => {});
+ return buffer.subarray(0, bytesRead);
+};
+
+/**
+ * Read the content of a file
+ *
+ * @param {string} path
+ * @returns {Promise}
+ */
+const readFile = (path) => new Promise((resolve, reject) => {
+ fs.open(path, 'r', (err, fd) => {
+ if (err) {
+ reject(err);
+ } else {
+ const buffer = Buffer.alloc(MAX_LENGTH);
+ fs.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
+ resolve(buffer.subarray(0, bytesRead));
+ fs.close(fd, () => {});
+ });
+ }
+ });
+});
+
+module.exports = {
+ LDD_PATH,
+ SELF_PATH,
+ readFileSync,
+ readFile
+};
diff --git a/node_modules/detect-libc/lib/process.js b/node_modules/detect-libc/lib/process.js
new file mode 100644
index 0000000..ee78ad2
--- /dev/null
+++ b/node_modules/detect-libc/lib/process.js
@@ -0,0 +1,24 @@
+// Copyright 2017 Lovell Fuller and others.
+// SPDX-License-Identifier: Apache-2.0
+
+'use strict';
+
+const isLinux = () => process.platform === 'linux';
+
+let report = null;
+const getReport = () => {
+ if (!report) {
+ /* istanbul ignore next */
+ if (isLinux() && process.report) {
+ const orig = process.report.excludeNetwork;
+ process.report.excludeNetwork = true;
+ report = process.report.getReport();
+ process.report.excludeNetwork = orig;
+ } else {
+ report = {};
+ }
+ }
+ return report;
+};
+
+module.exports = { isLinux, getReport };
diff --git a/node_modules/detect-libc/package.json b/node_modules/detect-libc/package.json
new file mode 100644
index 0000000..36d0f2b
--- /dev/null
+++ b/node_modules/detect-libc/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "detect-libc",
+ "version": "2.1.2",
+ "description": "Node.js module to detect the C standard library (libc) implementation family and version",
+ "main": "lib/detect-libc.js",
+ "files": [
+ "lib/",
+ "index.d.ts"
+ ],
+ "scripts": {
+ "test": "semistandard && nyc --reporter=text --check-coverage --branches=100 ava test/unit.js",
+ "changelog": "conventional-changelog -i CHANGELOG.md -s",
+ "bench": "node benchmark/detect-libc",
+ "bench:calls": "node benchmark/call-familySync.js && sleep 1 && node benchmark/call-isNonGlibcLinuxSync.js && sleep 1 && node benchmark/call-versionSync.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/lovell/detect-libc.git"
+ },
+ "keywords": [
+ "libc",
+ "glibc",
+ "musl"
+ ],
+ "author": "Lovell Fuller ",
+ "contributors": [
+ "Niklas Salmoukas ",
+ "Vinícius Lourenço "
+ ],
+ "license": "Apache-2.0",
+ "devDependencies": {
+ "ava": "^2.4.0",
+ "benchmark": "^2.1.4",
+ "conventional-changelog-cli": "^5.0.0",
+ "eslint-config-standard": "^13.0.1",
+ "nyc": "^15.1.0",
+ "proxyquire": "^2.1.3",
+ "semistandard": "^14.2.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "types": "index.d.ts"
+}
diff --git a/node_modules/dotenv/CHANGELOG.md b/node_modules/dotenv/CHANGELOG.md
new file mode 100644
index 0000000..8b80dd6
--- /dev/null
+++ b/node_modules/dotenv/CHANGELOG.md
@@ -0,0 +1,520 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.6.1...master)
+
+## [16.6.1](https://github.com/motdotla/dotenv/compare/v16.6.0...v16.6.1) (2025-06-27)
+
+### Changed
+
+- Default `quiet` to true – hiding the runtime log message ([#874](https://github.com/motdotla/dotenv/pull/874))
+- NOTICE: 17.0.0 will be released with quiet defaulting to false. Use `config({ quiet: true })` to suppress.
+- And check out the new [dotenvx](https://github.com/dotenvx/dotenvx). As coding workflows evolve and agents increasingly handle secrets, encrypted .env files offer a much safer way to deploy both agents and code together with secure secrets. Simply switch `require('dotenv').config()` for `require('@dotenvx/dotenvx').config()`.
+
+## [16.6.0](https://github.com/motdotla/dotenv/compare/v16.5.0...v16.6.0) (2025-06-26)
+
+### Added
+
+- Default log helpful message `[dotenv@16.6.0] injecting env (1) from .env` ([#870](https://github.com/motdotla/dotenv/pull/870))
+- Use `{ quiet: true }` to suppress
+- Aligns dotenv more closely with [dotenvx](https://github.com/dotenvx/dotenvx).
+
+## [16.5.0](https://github.com/motdotla/dotenv/compare/v16.4.7...v16.5.0) (2025-04-07)
+
+### Added
+
+- 🎉 Added new sponsor [Graphite](https://graphite.dev/?utm_source=github&utm_medium=repo&utm_campaign=dotenv) - *the AI developer productivity platform helping teams on GitHub ship higher quality software, faster*.
+
+> [!TIP]
+> **[Become a sponsor](https://github.com/sponsors/motdotla)**
+>
+> The dotenvx README is viewed thousands of times DAILY on GitHub and NPM.
+> Sponsoring dotenv is a great way to get in front of developers and give back to the developer community at the same time.
+
+### Changed
+
+- Remove `_log` method. Use `_debug` [#862](https://github.com/motdotla/dotenv/pull/862)
+
+## [16.4.7](https://github.com/motdotla/dotenv/compare/v16.4.6...v16.4.7) (2024-12-03)
+
+### Changed
+
+- Ignore `.tap` folder when publishing. (oops, sorry about that everyone. - @motdotla) [#848](https://github.com/motdotla/dotenv/pull/848)
+
+## [16.4.6](https://github.com/motdotla/dotenv/compare/v16.4.5...v16.4.6) (2024-12-02)
+
+### Changed
+
+- Clean up stale dev dependencies [#847](https://github.com/motdotla/dotenv/pull/847)
+- Various README updates clarifying usage and alternative solutions using [dotenvx](https://github.com/dotenvx/dotenvx)
+
+## [16.4.5](https://github.com/motdotla/dotenv/compare/v16.4.4...v16.4.5) (2024-02-19)
+
+### Changed
+
+- 🐞 Fix recent regression when using `path` option. return to historical behavior: do not attempt to auto find `.env` if `path` set. (regression was introduced in `16.4.3`) [#814](https://github.com/motdotla/dotenv/pull/814)
+
+## [16.4.4](https://github.com/motdotla/dotenv/compare/v16.4.3...v16.4.4) (2024-02-13)
+
+### Changed
+
+- 🐞 Replaced chaining operator `?.` with old school `&&` (fixing node 12 failures) [#812](https://github.com/motdotla/dotenv/pull/812)
+
+## [16.4.3](https://github.com/motdotla/dotenv/compare/v16.4.2...v16.4.3) (2024-02-12)
+
+### Changed
+
+- Fixed processing of multiple files in `options.path` [#805](https://github.com/motdotla/dotenv/pull/805)
+
+## [16.4.2](https://github.com/motdotla/dotenv/compare/v16.4.1...v16.4.2) (2024-02-10)
+
+### Changed
+
+- Changed funding link in package.json to [`dotenvx.com`](https://dotenvx.com)
+
+## [16.4.1](https://github.com/motdotla/dotenv/compare/v16.4.0...v16.4.1) (2024-01-24)
+
+- Patch support for array as `path` option [#797](https://github.com/motdotla/dotenv/pull/797)
+
+## [16.4.0](https://github.com/motdotla/dotenv/compare/v16.3.2...v16.4.0) (2024-01-23)
+
+- Add `error.code` to error messages around `.env.vault` decryption handling [#795](https://github.com/motdotla/dotenv/pull/795)
+- Add ability to find `.env.vault` file when filename(s) passed as an array [#784](https://github.com/motdotla/dotenv/pull/784)
+
+## [16.3.2](https://github.com/motdotla/dotenv/compare/v16.3.1...v16.3.2) (2024-01-18)
+
+### Added
+
+- Add debug message when no encoding set [#735](https://github.com/motdotla/dotenv/pull/735)
+
+### Changed
+
+- Fix output typing for `populate` [#792](https://github.com/motdotla/dotenv/pull/792)
+- Use subarray instead of slice [#793](https://github.com/motdotla/dotenv/pull/793)
+
+## [16.3.1](https://github.com/motdotla/dotenv/compare/v16.3.0...v16.3.1) (2023-06-17)
+
+### Added
+
+- Add missing type definitions for `processEnv` and `DOTENV_KEY` options. [#756](https://github.com/motdotla/dotenv/pull/756)
+
+## [16.3.0](https://github.com/motdotla/dotenv/compare/v16.2.0...v16.3.0) (2023-06-16)
+
+### Added
+
+- Optionally pass `DOTENV_KEY` to options rather than relying on `process.env.DOTENV_KEY`. Defaults to `process.env.DOTENV_KEY` [#754](https://github.com/motdotla/dotenv/pull/754)
+
+## [16.2.0](https://github.com/motdotla/dotenv/compare/v16.1.4...v16.2.0) (2023-06-15)
+
+### Added
+
+- Optionally write to your own target object rather than `process.env`. Defaults to `process.env`. [#753](https://github.com/motdotla/dotenv/pull/753)
+- Add import type URL to types file [#751](https://github.com/motdotla/dotenv/pull/751)
+
+## [16.1.4](https://github.com/motdotla/dotenv/compare/v16.1.3...v16.1.4) (2023-06-04)
+
+### Added
+
+- Added `.github/` to `.npmignore` [#747](https://github.com/motdotla/dotenv/pull/747)
+
+## [16.1.3](https://github.com/motdotla/dotenv/compare/v16.1.2...v16.1.3) (2023-05-31)
+
+### Removed
+
+- Removed `browser` keys for `path`, `os`, and `crypto` in package.json. These were set to false incorrectly as of 16.1. Instead, if using dotenv on the front-end make sure to include polyfills for `path`, `os`, and `crypto`. [node-polyfill-webpack-plugin](https://github.com/Richienb/node-polyfill-webpack-plugin) provides these.
+
+## [16.1.2](https://github.com/motdotla/dotenv/compare/v16.1.1...v16.1.2) (2023-05-31)
+
+### Changed
+
+- Exposed private function `_configDotenv` as `configDotenv`. [#744](https://github.com/motdotla/dotenv/pull/744)
+
+## [16.1.1](https://github.com/motdotla/dotenv/compare/v16.1.0...v16.1.1) (2023-05-30)
+
+### Added
+
+- Added type definition for `decrypt` function
+
+### Changed
+
+- Fixed `{crypto: false}` in `packageJson.browser`
+
+## [16.1.0](https://github.com/motdotla/dotenv/compare/v16.0.3...v16.1.0) (2023-05-30)
+
+### Added
+
+- Add `populate` convenience method [#733](https://github.com/motdotla/dotenv/pull/733)
+- Accept URL as path option [#720](https://github.com/motdotla/dotenv/pull/720)
+- Add dotenv to `npm fund` command
+- Spanish language README [#698](https://github.com/motdotla/dotenv/pull/698)
+- Add `.env.vault` support. 🎉 ([#730](https://github.com/motdotla/dotenv/pull/730))
+
+ℹ️ `.env.vault` extends the `.env` file format standard with a localized encrypted vault file. Package it securely with your production code deploys. It's cloud agnostic so that you can deploy your secrets anywhere – without [risky third-party integrations](https://techcrunch.com/2023/01/05/circleci-breach/). [read more](https://github.com/motdotla/dotenv#-deploying)
+
+### Changed
+
+- Fixed "cannot resolve 'fs'" error on tools like Replit [#693](https://github.com/motdotla/dotenv/pull/693)
+
+## [16.0.3](https://github.com/motdotla/dotenv/compare/v16.0.2...v16.0.3) (2022-09-29)
+
+### Changed
+
+- Added library version to debug logs ([#682](https://github.com/motdotla/dotenv/pull/682))
+
+## [16.0.2](https://github.com/motdotla/dotenv/compare/v16.0.1...v16.0.2) (2022-08-30)
+
+### Added
+
+- Export `env-options.js` and `cli-options.js` in package.json for use with downstream [dotenv-expand](https://github.com/motdotla/dotenv-expand) module
+
+## [16.0.1](https://github.com/motdotla/dotenv/compare/v16.0.0...v16.0.1) (2022-05-10)
+
+### Changed
+
+- Minor README clarifications
+- Development ONLY: updated devDependencies as recommended for development only security risks ([#658](https://github.com/motdotla/dotenv/pull/658))
+
+## [16.0.0](https://github.com/motdotla/dotenv/compare/v15.0.1...v16.0.0) (2022-02-02)
+
+### Added
+
+- _Breaking:_ Backtick support 🎉 ([#615](https://github.com/motdotla/dotenv/pull/615))
+
+If you had values containing the backtick character, please quote those values with either single or double quotes.
+
+## [15.0.1](https://github.com/motdotla/dotenv/compare/v15.0.0...v15.0.1) (2022-02-02)
+
+### Changed
+
+- Properly parse empty single or double quoted values 🐞 ([#614](https://github.com/motdotla/dotenv/pull/614))
+
+## [15.0.0](https://github.com/motdotla/dotenv/compare/v14.3.2...v15.0.0) (2022-01-31)
+
+`v15.0.0` is a major new release with some important breaking changes.
+
+### Added
+
+- _Breaking:_ Multiline parsing support (just works. no need for the flag.)
+
+### Changed
+
+- _Breaking:_ `#` marks the beginning of a comment (UNLESS the value is wrapped in quotes. Please update your `.env` files to wrap in quotes any values containing `#`. For example: `SECRET_HASH="something-with-a-#-hash"`).
+
+..Understandably, (as some teams have noted) this is tedious to do across the entire team. To make it less tedious, we recommend using [dotenv cli](https://github.com/dotenv-org/cli) going forward. It's an optional plugin that will keep your `.env` files in sync between machines, environments, or team members.
+
+### Removed
+
+- _Breaking:_ Remove multiline option (just works out of the box now. no need for the flag.)
+
+## [14.3.2](https://github.com/motdotla/dotenv/compare/v14.3.1...v14.3.2) (2022-01-25)
+
+### Changed
+
+- Preserve backwards compatibility on values containing `#` 🐞 ([#603](https://github.com/motdotla/dotenv/pull/603))
+
+## [14.3.1](https://github.com/motdotla/dotenv/compare/v14.3.0...v14.3.1) (2022-01-25)
+
+### Changed
+
+- Preserve backwards compatibility on exports by re-introducing the prior in-place exports 🐞 ([#606](https://github.com/motdotla/dotenv/pull/606))
+
+## [14.3.0](https://github.com/motdotla/dotenv/compare/v14.2.0...v14.3.0) (2022-01-24)
+
+### Added
+
+- Add `multiline` option 🎉 ([#486](https://github.com/motdotla/dotenv/pull/486))
+
+## [14.2.0](https://github.com/motdotla/dotenv/compare/v14.1.1...v14.2.0) (2022-01-17)
+
+### Added
+
+- Add `dotenv_config_override` cli option
+- Add `DOTENV_CONFIG_OVERRIDE` command line env option
+
+## [14.1.1](https://github.com/motdotla/dotenv/compare/v14.1.0...v14.1.1) (2022-01-17)
+
+### Added
+
+- Add React gotcha to FAQ on README
+
+## [14.1.0](https://github.com/motdotla/dotenv/compare/v14.0.1...v14.1.0) (2022-01-17)
+
+### Added
+
+- Add `override` option 🎉 ([#595](https://github.com/motdotla/dotenv/pull/595))
+
+## [14.0.1](https://github.com/motdotla/dotenv/compare/v14.0.0...v14.0.1) (2022-01-16)
+
+### Added
+
+- Log error on failure to load `.env` file ([#594](https://github.com/motdotla/dotenv/pull/594))
+
+## [14.0.0](https://github.com/motdotla/dotenv/compare/v13.0.1...v14.0.0) (2022-01-16)
+
+### Added
+
+- _Breaking:_ Support inline comments for the parser 🎉 ([#568](https://github.com/motdotla/dotenv/pull/568))
+
+## [13.0.1](https://github.com/motdotla/dotenv/compare/v13.0.0...v13.0.1) (2022-01-16)
+
+### Changed
+
+* Hide comments and newlines from debug output ([#404](https://github.com/motdotla/dotenv/pull/404))
+
+## [13.0.0](https://github.com/motdotla/dotenv/compare/v12.0.4...v13.0.0) (2022-01-16)
+
+### Added
+
+* _Breaking:_ Add type file for `config.js` ([#539](https://github.com/motdotla/dotenv/pull/539))
+
+## [12.0.4](https://github.com/motdotla/dotenv/compare/v12.0.3...v12.0.4) (2022-01-16)
+
+### Changed
+
+* README updates
+* Minor order adjustment to package json format
+
+## [12.0.3](https://github.com/motdotla/dotenv/compare/v12.0.2...v12.0.3) (2022-01-15)
+
+### Changed
+
+* Simplified jsdoc for consistency across editors
+
+## [12.0.2](https://github.com/motdotla/dotenv/compare/v12.0.1...v12.0.2) (2022-01-15)
+
+### Changed
+
+* Improve embedded jsdoc type documentation
+
+## [12.0.1](https://github.com/motdotla/dotenv/compare/v12.0.0...v12.0.1) (2022-01-15)
+
+### Changed
+
+* README updates and clarifications
+
+## [12.0.0](https://github.com/motdotla/dotenv/compare/v11.0.0...v12.0.0) (2022-01-15)
+
+### Removed
+
+- _Breaking:_ drop support for Flow static type checker ([#584](https://github.com/motdotla/dotenv/pull/584))
+
+### Changed
+
+- Move types/index.d.ts to lib/main.d.ts ([#585](https://github.com/motdotla/dotenv/pull/585))
+- Typescript cleanup ([#587](https://github.com/motdotla/dotenv/pull/587))
+- Explicit typescript inclusion in package.json ([#566](https://github.com/motdotla/dotenv/pull/566))
+
+## [11.0.0](https://github.com/motdotla/dotenv/compare/v10.0.0...v11.0.0) (2022-01-11)
+
+### Changed
+
+- _Breaking:_ drop support for Node v10 ([#558](https://github.com/motdotla/dotenv/pull/558))
+- Patch debug option ([#550](https://github.com/motdotla/dotenv/pull/550))
+
+## [10.0.0](https://github.com/motdotla/dotenv/compare/v9.0.2...v10.0.0) (2021-05-20)
+
+### Added
+
+- Add generic support to parse function
+- Allow for import "dotenv/config.js"
+- Add support to resolve home directory in path via ~
+
+## [9.0.2](https://github.com/motdotla/dotenv/compare/v9.0.1...v9.0.2) (2021-05-10)
+
+### Changed
+
+- Support windows newlines with debug mode
+
+## [9.0.1](https://github.com/motdotla/dotenv/compare/v9.0.0...v9.0.1) (2021-05-08)
+
+### Changed
+
+- Updates to README
+
+## [9.0.0](https://github.com/motdotla/dotenv/compare/v8.6.0...v9.0.0) (2021-05-05)
+
+### Changed
+
+- _Breaking:_ drop support for Node v8
+
+## [8.6.0](https://github.com/motdotla/dotenv/compare/v8.5.1...v8.6.0) (2021-05-05)
+
+### Added
+
+- define package.json in exports
+
+## [8.5.1](https://github.com/motdotla/dotenv/compare/v8.5.0...v8.5.1) (2021-05-05)
+
+### Changed
+
+- updated dev dependencies via npm audit
+
+## [8.5.0](https://github.com/motdotla/dotenv/compare/v8.4.0...v8.5.0) (2021-05-05)
+
+### Added
+
+- allow for `import "dotenv/config"`
+
+## [8.4.0](https://github.com/motdotla/dotenv/compare/v8.3.0...v8.4.0) (2021-05-05)
+
+### Changed
+
+- point to exact types file to work with VS Code
+
+## [8.3.0](https://github.com/motdotla/dotenv/compare/v8.2.0...v8.3.0) (2021-05-05)
+
+### Changed
+
+- _Breaking:_ drop support for Node v8 (mistake to be released as minor bump. later bumped to 9.0.0. see above.)
+
+## [8.2.0](https://github.com/motdotla/dotenv/compare/v8.1.0...v8.2.0) (2019-10-16)
+
+### Added
+
+- TypeScript types
+
+## [8.1.0](https://github.com/motdotla/dotenv/compare/v8.0.0...v8.1.0) (2019-08-18)
+
+### Changed
+
+- _Breaking:_ drop support for Node v6 ([#392](https://github.com/motdotla/dotenv/issues/392))
+
+# [8.0.0](https://github.com/motdotla/dotenv/compare/v7.0.0...v8.0.0) (2019-05-02)
+
+### Changed
+
+- _Breaking:_ drop support for Node v6 ([#302](https://github.com/motdotla/dotenv/issues/392))
+
+## [7.0.0] - 2019-03-12
+
+### Fixed
+
+- Fix removing unbalanced quotes ([#376](https://github.com/motdotla/dotenv/pull/376))
+
+### Removed
+
+- Removed `load` alias for `config` for consistency throughout code and documentation.
+
+## [6.2.0] - 2018-12-03
+
+### Added
+
+- Support preload configuration via environment variables ([#351](https://github.com/motdotla/dotenv/issues/351))
+
+## [6.1.0] - 2018-10-08
+
+### Added
+
+- `debug` option for `config` and `parse` methods will turn on logging
+
+## [6.0.0] - 2018-06-02
+
+### Changed
+
+- _Breaking:_ drop support for Node v4 ([#304](https://github.com/motdotla/dotenv/pull/304))
+
+## [5.0.0] - 2018-01-29
+
+### Added
+
+- Testing against Node v8 and v9
+- Documentation on trim behavior of values
+- Documentation on how to use with `import`
+
+### Changed
+
+- _Breaking_: default `path` is now `path.resolve(process.cwd(), '.env')`
+- _Breaking_: does not write over keys already in `process.env` if the key has a falsy value
+- using `const` and `let` instead of `var`
+
+### Removed
+
+- Testing against Node v7
+
+## [4.0.0] - 2016-12-23
+
+### Changed
+
+- Return Object with parsed content or error instead of false ([#165](https://github.com/motdotla/dotenv/pull/165)).
+
+### Removed
+
+- `verbose` option removed in favor of returning result.
+
+## [3.0.0] - 2016-12-20
+
+### Added
+
+- `verbose` option will log any error messages. Off by default.
+- parses email addresses correctly
+- allow importing config method directly in ES6
+
+### Changed
+
+- Suppress error messages by default ([#154](https://github.com/motdotla/dotenv/pull/154))
+- Ignoring more files for NPM to make package download smaller
+
+### Fixed
+
+- False positive test due to case-sensitive variable ([#124](https://github.com/motdotla/dotenv/pull/124))
+
+### Removed
+
+- `silent` option removed in favor of `verbose`
+
+## [2.0.0] - 2016-01-20
+
+### Added
+
+- CHANGELOG to ["make it easier for users and contributors to see precisely what notable changes have been made between each release"](http://keepachangelog.com/). Linked to from README
+- LICENSE to be more explicit about what was defined in `package.json`. Linked to from README
+- Testing nodejs v4 on travis-ci
+- added examples of how to use dotenv in different ways
+- return parsed object on success rather than boolean true
+
+### Changed
+
+- README has shorter description not referencing ruby gem since we don't have or want feature parity
+
+### Removed
+
+- Variable expansion and escaping so environment variables are encouraged to be fully orthogonal
+
+## [1.2.0] - 2015-06-20
+
+### Added
+
+- Preload hook to require dotenv without including it in your code
+
+### Changed
+
+- clarified license to be "BSD-2-Clause" in `package.json`
+
+### Fixed
+
+- retain spaces in string vars
+
+## [1.1.0] - 2015-03-31
+
+### Added
+
+- Silent option to silence `console.log` when `.env` missing
+
+## [1.0.0] - 2015-03-13
+
+### Removed
+
+- support for multiple `.env` files. should always use one `.env` file for the current environment
+
+[7.0.0]: https://github.com/motdotla/dotenv/compare/v6.2.0...v7.0.0
+[6.2.0]: https://github.com/motdotla/dotenv/compare/v6.1.0...v6.2.0
+[6.1.0]: https://github.com/motdotla/dotenv/compare/v6.0.0...v6.1.0
+[6.0.0]: https://github.com/motdotla/dotenv/compare/v5.0.0...v6.0.0
+[5.0.0]: https://github.com/motdotla/dotenv/compare/v4.0.0...v5.0.0
+[4.0.0]: https://github.com/motdotla/dotenv/compare/v3.0.0...v4.0.0
+[3.0.0]: https://github.com/motdotla/dotenv/compare/v2.0.0...v3.0.0
+[2.0.0]: https://github.com/motdotla/dotenv/compare/v1.2.0...v2.0.0
+[1.2.0]: https://github.com/motdotla/dotenv/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/motdotla/dotenv/compare/v1.0.0...v1.1.0
+[1.0.0]: https://github.com/motdotla/dotenv/compare/v0.4.0...v1.0.0
diff --git a/node_modules/dotenv/LICENSE b/node_modules/dotenv/LICENSE
new file mode 100644
index 0000000..c430ad8
--- /dev/null
+++ b/node_modules/dotenv/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2015, Scott Motte
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/node_modules/dotenv/README-es.md b/node_modules/dotenv/README-es.md
new file mode 100644
index 0000000..5fc934d
--- /dev/null
+++ b/node_modules/dotenv/README-es.md
@@ -0,0 +1,411 @@
+
+🎉 announcing
dotenvx .
run anywhere, multi-environment, encrypted envs .
+
+
+
+
+
+
+# dotenv [](https://www.npmjs.com/package/dotenv)
+
+
+
+Dotenv es un módulo de dependencia cero que carga las variables de entorno desde un archivo `.env` en [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env). El almacenamiento de la configuración del entorno separado del código está basado en la metodología [The Twelve-Factor App](http://12factor.net/config).
+
+[](https://github.com/feross/standard)
+[](LICENSE)
+
+## Instalación
+
+```bash
+# instalación local (recomendado)
+npm install dotenv --save
+```
+
+O installación con yarn? `yarn add dotenv`
+
+## Uso
+
+Cree un archivo `.env` en la raíz de su proyecto:
+
+```dosini
+S3_BUCKET="YOURS3BUCKET"
+SECRET_KEY="YOURSECRETKEYGOESHERE"
+```
+
+Tan prónto como sea posible en su aplicación, importe y configure dotenv:
+
+```javascript
+require('dotenv').config()
+console.log(process.env) // elimine esto después que haya confirmado que esta funcionando
+```
+
+.. o usa ES6?
+
+```javascript
+import * as dotenv from 'dotenv' // vea en https://github.com/motdotla/dotenv#como-uso-dotenv-con-import
+// REVISAR LINK DE REFERENCIA DE IMPORTACIÓN
+dotenv.config()
+import express from 'express'
+```
+
+Eso es todo. `process.env` ahora tiene las claves y los valores que definiste en tu archivo `.env`:
+
+```javascript
+require('dotenv').config()
+
+...
+
+s3.getBucketCors({Bucket: process.env.S3_BUCKET}, function(err, data) {})
+```
+
+### Valores multilínea
+
+Si necesita variables de varias líneas, por ejemplo, claves privadas, ahora se admiten en la versión (`>= v15.0.0`) con saltos de línea:
+
+```dosini
+PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
+...
+Kh9NV...
+...
+-----END RSA PRIVATE KEY-----"
+```
+
+Alternativamente, puede usar comillas dobles y usar el carácter `\n`:
+
+```dosini
+PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nKh9NV...\n-----END RSA PRIVATE KEY-----\n"
+```
+
+### Comentarios
+
+Los comentarios pueden ser agregados en tu archivo o en la misma línea:
+
+```dosini
+# This is a comment
+SECRET_KEY=YOURSECRETKEYGOESHERE # comment
+SECRET_HASH="something-with-a-#-hash"
+```
+
+Los comentarios comienzan donde existe un `#`, entonces, si su valor contiene un `#`, enciérrelo entre comillas. Este es un cambio importante desde la versión `>= v15.0.0` en adelante.
+
+### Análisis
+
+El motor que analiza el contenido de su archivo que contiene variables de entorno está disponible para su uso. Este Acepta una Cadena o un Búfer y devolverá un Objeto con las claves y los valores analizados.
+
+```javascript
+const dotenv = require('dotenv')
+const buf = Buffer.from('BASICO=basico')
+const config = dotenv.parse(buf) // devolverá un objeto
+console.log(typeof config, config) // objeto { BASICO : 'basico' }
+```
+
+### Precarga
+
+Puede usar el `--require` (`-r`) [opción de línea de comando](https://nodejs.org/api/cli.html#-r---require-module) para precargar dotenv. Al hacer esto, no necesita requerir ni cargar dotnev en el código de su aplicación.
+
+```bash
+$ node -r dotenv/config tu_script.js
+```
+
+Las opciones de configuración a continuación se admiten como argumentos de línea de comandos en el formato `dotenv_config_=value`
+
+```bash
+$ node -r dotenv/config tu_script.js dotenv_config_path=/custom/path/to/.env dotenv_config_debug=true
+```
+
+Además, puede usar variables de entorno para establecer opciones de configuración. Los argumentos de línea de comandos precederán a estos.
+
+```bash
+$ DOTENV_CONFIG_ =value node -r dotenv/config tu_script.js
+```
+
+```bash
+$ DOTENV_CONFIG_ENCODING=latin1 DOTENV_CONFIG_DEBUG=true node -r dotenv/config tu_script.js dotenv_config_path=/custom/path/to/.env
+```
+
+### Expansión Variable
+
+Necesitaras agregar el valor de otro variable en una de sus variables? Usa [dotenv-expand](https://github.com/motdotla/dotenv-expand).
+
+## Ejemplos
+
+Vea [ejemplos](https://github.com/dotenv-org/examples) sobre el uso de dotenv con varios frameworks, lenguajes y configuraciones.
+
+* [nodejs](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs)
+* [nodejs (depurar en)](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs-debug)
+* [nodejs (anular en)](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs-override)
+* [esm](https://github.com/dotenv-org/examples/tree/master/dotenv-esm)
+* [esm (precarga)](https://github.com/dotenv-org/examples/tree/master/dotenv-esm-preload)
+* [typescript](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript)
+* [typescript parse](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript-parse)
+* [typescript config](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript-config)
+* [webpack](https://github.com/dotenv-org/examples/tree/master/dotenv-webpack)
+* [webpack (plugin)](https://github.com/dotenv-org/examples/tree/master/dotenv-webpack2)
+* [react](https://github.com/dotenv-org/examples/tree/master/dotenv-react)
+* [react (typescript)](https://github.com/dotenv-org/examples/tree/master/dotenv-react-typescript)
+* [express](https://github.com/dotenv-org/examples/tree/master/dotenv-express)
+* [nestjs](https://github.com/dotenv-org/examples/tree/master/dotenv-nestjs)
+
+## Documentación
+
+Dotenv expone dos funciones:
+
+* `configuración`
+* `analizar`
+
+### Configuración
+
+`Configuración` leerá su archivo `.env`, analizará el contenido, lo asignará a [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env),
+y devolverá un Objeto con una clave `parsed` que contiene el contenido cargado o una clave `error` si falla.
+
+```js
+const result = dotenv.config()
+
+if (result.error) {
+ throw result.error
+}
+
+console.log(result.parsed)
+```
+
+Adicionalmente, puede pasar opciones a `configuracion`.
+
+#### Opciones
+
+##### Ruta
+
+Por defecto: `path.resolve(process.cwd(), '.env')`
+
+Especifique una ruta personalizada si el archivo que contiene las variables de entorno se encuentra localizado en otro lugar.
+
+```js
+require('dotenv').config({ path: '/personalizado/ruta/a/.env' })
+```
+
+##### Codificación
+
+Por defecto: `utf8`
+
+Especifique la codificación del archivo que contiene las variables de entorno.
+
+```js
+require('dotenv').config({ encoding: 'latin1' })
+```
+
+##### Depurar
+
+Por defecto: `false`
+
+Active el registro de ayuda para depurar por qué ciertas claves o valores no se inician como lo esperabas.
+
+```js
+require('dotenv').config({ debug: process.env.DEBUG })
+```
+
+##### Anular
+
+Por defecto: `false`
+
+Anule cualquier variable de entorno que ya se haya configurada en su maquina con los valores de su archivo .env.
+
+```js
+require('dotenv').config({ override: true })
+```
+
+### Analizar
+
+El motor que analiza el contenido del archivo que contiene las variables de entorno está disponible para su uso. Acepta una Cadena o un Búfer y retornará un objecto con los valores analizados.
+
+```js
+const dotenv = require('dotenv')
+const buf = Buffer.from('BASICO=basico')
+const config = dotenv.parse(buf) // devolverá un objeto
+console.log(typeof config, config) // objeto { BASICO : 'basico' }
+```
+
+#### Opciones
+
+##### Depurar
+
+Por defecto: `false`
+
+Active el registro de ayuda para depurar por qué ciertas claves o valores no se inician como lo esperabas.
+
+```js
+const dotenv = require('dotenv')
+const buf = Buffer.from('hola mundo')
+const opt = { debug: true }
+const config = dotenv.parse(buf, opt)
+// espere por un mensaje de depuración porque el búfer no esta listo KEY=VAL
+```
+
+## FAQ
+
+### ¿Por qué el archivo `.env` no carga mis variables de entorno correctamente?
+
+Lo más probable es que su archivo `.env` no esté en el lugar correcto. [Vea este stack overflow](https://stackoverflow.com/questions/42335016/dotenv-file-is-not-loading-environment-variables).
+
+Active el modo de depuración y vuelva a intentarlo...
+
+```js
+require('dotenv').config({ debug: true })
+```
+
+Recibirá un error apropiado en su consola.
+
+### ¿Debo confirmar mi archivo `.env`?
+
+No. Recomendamos **enfáticamente** no enviar su archivo `.env` a la versión de control. Solo debe incluir los valores especificos del entorno, como la base de datos, contraseñas o claves API.
+
+### ¿Debería tener multiples archivos `.env`?
+
+No. Recomendamos **enfáticamente** no tener un archivo `.env` "principal" y un archivo `.env` de "entorno" como `.env.test`. Su configuración debe variar entre implementaciones y no debe compartir valores entre entornos.
+
+> En una Aplicación de Doce Factores, las variables de entorno son controles diferenciados, cada uno totalmente independiente a otras variables de entorno. Nunca se agrupan como "entornos", sino que se gestionan de manera independiente para cada despliegue. Este es un modelo que se escala sin problemas a medida que la aplicación se expande de forma natural en más despliegues a lo largo de su vida.
+>
+> – [La Apliación de los Doce Factores](https://12factor.net/es/)
+
+### ¿Qué reglas sigue el motor de análisis?
+
+El motor de análisis actualmente admite las siguientes reglas:
+
+- `BASICO=basico` se convierte en `{BASICO: 'basico'}`
+- las líneas vacías se saltan
+- las líneas que comienzan con `#` se tratan como comentarios
+- `#` marca el comienzo de un comentario (a menos que el valor esté entre comillas)
+- valores vacíos se convierten en cadenas vacías (`VACIO=` se convierte en `{VACIO: ''}`)
+- las comillas internas se mantienen (piensa en JSON) (`JSON={"foo": "bar"}` se convierte en `{JSON:"{\"foo\": \"bar\"}"`)
+- los espacios en blanco se eliminan de ambos extremos de los valores no citanos (aprende más en [`trim`](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)) (`FOO= algo ` se convierte en `{FOO: 'algo'}`)
+- los valores entre comillas simples y dobles se escapan (`CITA_SIMPLE='citado'` se convierte en `{CITA_SIMPLE: "citado"}`)
+- los valores entre comillas simples y dobles mantienen los espacios en blanco en ambos extremos (`FOO=" algo "` se convierte en `{FOO: ' algo '}`)
+- los valores entre comillas dobles expanden nuevas líneas (`MULTILINEA="nueva\nlínea"` se convierte en
+
+```
+{MULTILINEA: 'nueva
+línea'}
+```
+
+- se admite la comilla simple invertida (`` SIGNO_ACENTO=`Esto tiene comillas 'simples' y "dobles" en su interior.` ``)
+
+### ¿Qué sucede con las variables de entorno que ya estaban configuradas?
+
+Por defecto, nunca modificaremos ninguna variable de entorno que ya haya sido establecida. En particular, si hay una variable en su archivo `.env` que colisiona con una que ya existe en su entorno, entonces esa variable se omitirá.
+
+Si por el contrario, quieres anular `process.env` utiliza la opción `override`.
+
+```javascript
+require('dotenv').config({ override: true })
+```
+
+### ¿Por qué mis variables de entorno no aparecen para React?
+
+Su código React se ejecuta en Webpack, donde el módulo `fs` o incluso el propio `process` global no son accesibles fuera-de-la-caja. El módulo `process.env` sólo puede ser inyectado a través de la configuración de Webpack.
+
+Si estás usando [`react-scripts`](https://www.npmjs.com/package/react-scripts), el cual se distribuye a través de [`create-react-app`](https://create-react-app.dev/), tiene dotenv incorporado pero con una singularidad. Escriba sus variables de entorno con `REACT_APP_`. Vea [este stack overflow](https://stackoverflow.com/questions/42182577/is-it-possible-to-use-dotenv-in-a-react-project) para más detalles.
+
+Si estás utilizando otros frameworks (por ejemplo, Next.js, Gatsby...), debes consultar su documentación para saber cómo injectar variables de entorno en el cliente.
+
+### ¿Puedo personalizar/escribir plugins para dotenv?
+
+Sí! `dotenv.config()` devuelve un objeto que representa el archivo `.env` analizado. Esto te da todo lo que necesitas para poder establecer valores en `process.env`. Por ejemplo:
+
+```js
+const dotenv = require('dotenv')
+const variableExpansion = require('dotenv-expand')
+const miEnv = dotenv.config()
+variableExpansion(miEnv)
+```
+
+### Cómo uso dotnev con `import`?
+
+Simplemente..
+
+```javascript
+// index.mjs (ESM)
+import * as dotenv from 'dotenv' // vea https://github.com/motdotla/dotenv#como-uso-dotenv-con-import
+dotenv.config()
+import express from 'express'
+```
+
+Un poco de historia...
+
+> Cuando se ejecuta un módulo que contiene una sentencia `import`, los módulos que importa serán cargados primero, y luego se ejecuta cada bloque del módulo en un recorrido en profundidad del gráfico de dependencias, evitando los ciclos al saltarse todo lo que ya se ha ejecutado.
+>
+> – [ES6 en Profundidad: Módulos](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/)
+
+¿Qué significa esto en lenguaje sencillo? Significa que se podrías pensar que lo siguiente funcionaría pero no lo hará.
+
+```js
+// notificarError.mjs
+import { Cliente } from 'mejor-servicio-para-notificar-error'
+
+export default new Client(process.env.CLAVE_API)
+
+// index.mjs
+import dotenv from 'dotenv'
+dotenv.config()
+
+import notificarError from './notificarError.mjs'
+notificarError.report(new Error('ejemplo documentado'))
+```
+
+`process.env.CLAVE_API` será vacio.
+
+En su lugar, el código anterior debe ser escrito como...
+
+```js
+// notificarError.mjs
+import { Cliente } from 'mejor-servicio-para-notificar-errores'
+
+export default new Client(process.env.CLAVE_API)
+
+// index.mjs
+import * as dotenv from 'dotenv'
+dotenv.config()
+
+import notificarError from './notificarError.mjs'
+notificarError.report(new Error('ejemplo documentado'))
+```
+
+¿Esto tiene algo de sentido? Esto es poco poco intuitivo, pero es como funciona la importación de módulos en ES6. Aquí hay un ejemplo [ejemplo práctico de esta trampa](https://github.com/dotenv-org/examples/tree/master/dotenv-es6-import-pitfall).
+
+Existen dos arternativas a este planteamiento:
+
+1. Precarga dotenv: `node --require dotenv/config index.js` (_Nota: no es necesario usar `import` dotenv con este método_)
+2. Cree un archivo separado que ejecutará `config` primero como se describe en [este comentario #133](https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822)
+
+### ¿Qué pasa con la expansión de variable?
+
+Prueba [dotenv-expand](https://github.com/motdotla/dotenv-expand)
+
+## Guía de contribución
+
+Vea [CONTRIBUTING.md](CONTRIBUTING.md)
+
+## REGISTRO DE CAMBIOS
+
+Vea [CHANGELOG.md](CHANGELOG.md)
+
+## ¿Quiénes utilizan dotenv?
+
+[Estos módulos npm dependen de él.](https://www.npmjs.com/browse/depended/dotenv)
+
+Los proyectos que lo amplían suelen utilizar la [palabra clave "dotenv" en npm](https://www.npmjs.com/search?q=keywords:dotenv).
diff --git a/node_modules/dotenv/README.md b/node_modules/dotenv/README.md
new file mode 100644
index 0000000..251aada
--- /dev/null
+++ b/node_modules/dotenv/README.md
@@ -0,0 +1,645 @@
+
+🎉 announcing
dotenvx .
run anywhere, multi-environment, encrypted envs .
+
+
+
+
+
+
+# dotenv [](https://www.npmjs.com/package/dotenv)
+
+
+
+Dotenv is a zero-dependency module that loads environment variables from a `.env` file into [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env). Storing configuration in the environment separate from code is based on [The Twelve-Factor App](https://12factor.net/config) methodology.
+
+[](https://github.com/feross/standard)
+[](LICENSE)
+[](https://codecov.io/gh/motdotla/dotenv-expand)
+
+* [🌱 Install](#-install)
+* [🏗️ Usage (.env)](#%EF%B8%8F-usage)
+* [🌴 Multiple Environments 🆕](#-manage-multiple-environments)
+* [🚀 Deploying (encryption) 🆕](#-deploying)
+* [📚 Examples](#-examples)
+* [📖 Docs](#-documentation)
+* [❓ FAQ](#-faq)
+* [⏱️ Changelog](./CHANGELOG.md)
+
+## 🌱 Install
+
+```bash
+npm install dotenv --save
+```
+
+You can also use an npm-compatible package manager like yarn, bun or pnpm:
+
+```bash
+yarn add dotenv
+```
+```bash
+bun add dotenv
+```
+```bash
+pnpm add dotenv
+```
+
+## 🏗️ Usage
+
+
+
+
+
+
+
+
+Create a `.env` file in the root of your project (if using a monorepo structure like `apps/backend/app.js`, put it in the root of the folder where your `app.js` process runs):
+
+```dosini
+S3_BUCKET="YOURS3BUCKET"
+SECRET_KEY="YOURSECRETKEYGOESHERE"
+```
+
+As early as possible in your application, import and configure dotenv:
+
+```javascript
+require('dotenv').config()
+console.log(process.env) // remove this after you've confirmed it is working
+```
+
+.. [or using ES6?](#how-do-i-use-dotenv-with-import)
+
+```javascript
+import 'dotenv/config'
+```
+
+That's it. `process.env` now has the keys and values you defined in your `.env` file:
+
+```javascript
+require('dotenv').config()
+// or import 'dotenv/config' if you're using ES6
+
+...
+
+s3.getBucketCors({Bucket: process.env.S3_BUCKET}, function(err, data) {})
+```
+
+### Multiline values
+
+If you need multiline variables, for example private keys, those are now supported (`>= v15.0.0`) with line breaks:
+
+```dosini
+PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
+...
+Kh9NV...
+...
+-----END RSA PRIVATE KEY-----"
+```
+
+Alternatively, you can double quote strings and use the `\n` character:
+
+```dosini
+PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nKh9NV...\n-----END RSA PRIVATE KEY-----\n"
+```
+
+### Comments
+
+Comments may be added to your file on their own line or inline:
+
+```dosini
+# This is a comment
+SECRET_KEY=YOURSECRETKEYGOESHERE # comment
+SECRET_HASH="something-with-a-#-hash"
+```
+
+Comments begin where a `#` exists, so if your value contains a `#` please wrap it in quotes. This is a breaking change from `>= v15.0.0` and on.
+
+### Parsing
+
+The engine which parses the contents of your file containing environment variables is available to use. It accepts a String or Buffer and will return an Object with the parsed keys and values.
+
+```javascript
+const dotenv = require('dotenv')
+const buf = Buffer.from('BASIC=basic')
+const config = dotenv.parse(buf) // will return an object
+console.log(typeof config, config) // object { BASIC : 'basic' }
+```
+
+### Preload
+
+> Note: Consider using [`dotenvx`](https://github.com/dotenvx/dotenvx) instead of preloading. I am now doing (and recommending) so.
+>
+> It serves the same purpose (you do not need to require and load dotenv), adds better debugging, and works with ANY language, framework, or platform. – [motdotla](https://github.com/motdotla)
+
+You can use the `--require` (`-r`) [command line option](https://nodejs.org/api/cli.html#-r---require-module) to preload dotenv. By doing this, you do not need to require and load dotenv in your application code.
+
+```bash
+$ node -r dotenv/config your_script.js
+```
+
+The configuration options below are supported as command line arguments in the format `dotenv_config_ =value`
+
+```bash
+$ node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env dotenv_config_debug=true
+```
+
+Additionally, you can use environment variables to set configuration options. Command line arguments will precede these.
+
+```bash
+$ DOTENV_CONFIG_ =value node -r dotenv/config your_script.js
+```
+
+```bash
+$ DOTENV_CONFIG_ENCODING=latin1 DOTENV_CONFIG_DEBUG=true node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env
+```
+
+### Variable Expansion
+
+You need to add the value of another variable in one of your variables? Use [dotenv-expand](https://github.com/motdotla/dotenv-expand).
+
+### Command Substitution
+
+Use [dotenvx](https://github.com/dotenvx/dotenvx) to use command substitution.
+
+Add the output of a command to one of your variables in your .env file.
+
+```ini
+# .env
+DATABASE_URL="postgres://$(whoami)@localhost/my_database"
+```
+```js
+// index.js
+console.log('DATABASE_URL', process.env.DATABASE_URL)
+```
+```sh
+$ dotenvx run --debug -- node index.js
+[dotenvx@0.14.1] injecting env (1) from .env
+DATABASE_URL postgres://yourusername@localhost/my_database
+```
+
+### Syncing
+
+You need to keep `.env` files in sync between machines, environments, or team members? Use [dotenvx](https://github.com/dotenvx/dotenvx) to encrypt your `.env` files and safely include them in source control. This still subscribes to the twelve-factor app rules by generating a decryption key separate from code.
+
+### Multiple Environments
+
+Use [dotenvx](https://github.com/dotenvx/dotenvx) to generate `.env.ci`, `.env.production` files, and more.
+
+### Deploying
+
+You need to deploy your secrets in a cloud-agnostic manner? Use [dotenvx](https://github.com/dotenvx/dotenvx) to generate a private decryption key that is set on your production server.
+
+## 🌴 Manage Multiple Environments
+
+Use [dotenvx](https://github.com/dotenvx/dotenvx)
+
+Run any environment locally. Create a `.env.ENVIRONMENT` file and use `--env-file` to load it. It's straightforward, yet flexible.
+
+```bash
+$ echo "HELLO=production" > .env.production
+$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
+
+$ dotenvx run --env-file=.env.production -- node index.js
+Hello production
+> ^^
+```
+
+or with multiple .env files
+
+```bash
+$ echo "HELLO=local" > .env.local
+$ echo "HELLO=World" > .env
+$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
+
+$ dotenvx run --env-file=.env.local --env-file=.env -- node index.js
+Hello local
+```
+
+[more environment examples](https://dotenvx.com/docs/quickstart/environments)
+
+## 🚀 Deploying
+
+Use [dotenvx](https://github.com/dotenvx/dotenvx).
+
+Add encryption to your `.env` files with a single command. Pass the `--encrypt` flag.
+
+```
+$ dotenvx set HELLO Production --encrypt -f .env.production
+$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
+
+$ DOTENV_PRIVATE_KEY_PRODUCTION="<.env.production private key>" dotenvx run -- node index.js
+[dotenvx] injecting env (2) from .env.production
+Hello Production
+```
+
+[learn more](https://github.com/dotenvx/dotenvx?tab=readme-ov-file#encryption)
+
+## 📚 Examples
+
+See [examples](https://github.com/dotenv-org/examples) of using dotenv with various frameworks, languages, and configurations.
+
+* [nodejs](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs)
+* [nodejs (debug on)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs-debug)
+* [nodejs (override on)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs-override)
+* [nodejs (processEnv override)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-custom-target)
+* [esm](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-esm)
+* [esm (preload)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-esm-preload)
+* [typescript](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript)
+* [typescript parse](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript-parse)
+* [typescript config](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript-config)
+* [webpack](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-webpack)
+* [webpack (plugin)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-webpack2)
+* [react](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-react)
+* [react (typescript)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-react-typescript)
+* [express](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-express)
+* [nestjs](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nestjs)
+* [fastify](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-fastify)
+
+## 📖 Documentation
+
+Dotenv exposes four functions:
+
+* `config`
+* `parse`
+* `populate`
+* `decrypt`
+
+### Config
+
+`config` will read your `.env` file, parse the contents, assign it to
+[`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env),
+and return an Object with a `parsed` key containing the loaded content or an `error` key if it failed.
+
+```js
+const result = dotenv.config()
+
+if (result.error) {
+ throw result.error
+}
+
+console.log(result.parsed)
+```
+
+You can additionally, pass options to `config`.
+
+#### Options
+
+##### path
+
+Default: `path.resolve(process.cwd(), '.env')`
+
+Specify a custom path if your file containing environment variables is located elsewhere.
+
+```js
+require('dotenv').config({ path: '/custom/path/to/.env' })
+```
+
+By default, `config` will look for a file called .env in the current working directory.
+
+Pass in multiple files as an array, and they will be parsed in order and combined with `process.env` (or `option.processEnv`, if set). The first value set for a variable will win, unless the `options.override` flag is set, in which case the last value set will win. If a value already exists in `process.env` and the `options.override` flag is NOT set, no changes will be made to that value.
+
+```js
+require('dotenv').config({ path: ['.env.local', '.env'] })
+```
+
+##### encoding
+
+Default: `utf8`
+
+Specify the encoding of your file containing environment variables.
+
+```js
+require('dotenv').config({ encoding: 'latin1' })
+```
+
+##### debug
+
+Default: `false`
+
+Turn on logging to help debug why certain keys or values are not being set as you expect.
+
+```js
+require('dotenv').config({ debug: process.env.DEBUG })
+```
+
+##### override
+
+Default: `false`
+
+Override any environment variables that have already been set on your machine with values from your .env file(s). If multiple files have been provided in `option.path` the override will also be used as each file is combined with the next. Without `override` being set, the first value wins. With `override` set the last value wins.
+
+```js
+require('dotenv').config({ override: true })
+```
+
+##### processEnv
+
+Default: `process.env`
+
+Specify an object to write your environment variables to. Defaults to `process.env` environment variables.
+
+```js
+const myObject = {}
+require('dotenv').config({ processEnv: myObject })
+
+console.log(myObject) // values from .env
+console.log(process.env) // this was not changed or written to
+```
+
+### Parse
+
+The engine which parses the contents of your file containing environment
+variables is available to use. It accepts a String or Buffer and will return
+an Object with the parsed keys and values.
+
+```js
+const dotenv = require('dotenv')
+const buf = Buffer.from('BASIC=basic')
+const config = dotenv.parse(buf) // will return an object
+console.log(typeof config, config) // object { BASIC : 'basic' }
+```
+
+#### Options
+
+##### debug
+
+Default: `false`
+
+Turn on logging to help debug why certain keys or values are not being set as you expect.
+
+```js
+const dotenv = require('dotenv')
+const buf = Buffer.from('hello world')
+const opt = { debug: true }
+const config = dotenv.parse(buf, opt)
+// expect a debug message because the buffer is not in KEY=VAL form
+```
+
+### Populate
+
+The engine which populates the contents of your .env file to `process.env` is available for use. It accepts a target, a source, and options. This is useful for power users who want to supply their own objects.
+
+For example, customizing the source:
+
+```js
+const dotenv = require('dotenv')
+const parsed = { HELLO: 'world' }
+
+dotenv.populate(process.env, parsed)
+
+console.log(process.env.HELLO) // world
+```
+
+For example, customizing the source AND target:
+
+```js
+const dotenv = require('dotenv')
+const parsed = { HELLO: 'universe' }
+const target = { HELLO: 'world' } // empty object
+
+dotenv.populate(target, parsed, { override: true, debug: true })
+
+console.log(target) // { HELLO: 'universe' }
+```
+
+#### options
+
+##### Debug
+
+Default: `false`
+
+Turn on logging to help debug why certain keys or values are not being populated as you expect.
+
+##### override
+
+Default: `false`
+
+Override any environment variables that have already been set.
+
+## ❓ FAQ
+
+### Why is the `.env` file not loading my environment variables successfully?
+
+Most likely your `.env` file is not in the correct place. [See this stack overflow](https://stackoverflow.com/questions/42335016/dotenv-file-is-not-loading-environment-variables).
+
+Turn on debug mode and try again..
+
+```js
+require('dotenv').config({ debug: true })
+```
+
+You will receive a helpful error outputted to your console.
+
+### Should I commit my `.env` file?
+
+No. We **strongly** recommend against committing your `.env` file to version
+control. It should only include environment-specific values such as database
+passwords or API keys. Your production database should have a different
+password than your development database.
+
+### Should I have multiple `.env` files?
+
+We recommend creating one `.env` file per environment. Use `.env` for local/development, `.env.production` for production and so on. This still follows the twelve factor principles as each is attributed individually to its own environment. Avoid custom set ups that work in inheritance somehow (`.env.production` inherits values form `.env` for example). It is better to duplicate values if necessary across each `.env.environment` file.
+
+> In a twelve-factor app, env vars are granular controls, each fully orthogonal to other env vars. They are never grouped together as “environments”, but instead are independently managed for each deploy. This is a model that scales up smoothly as the app naturally expands into more deploys over its lifetime.
+>
+> – [The Twelve-Factor App](http://12factor.net/config)
+
+### What rules does the parsing engine follow?
+
+The parsing engine currently supports the following rules:
+
+- `BASIC=basic` becomes `{BASIC: 'basic'}`
+- empty lines are skipped
+- lines beginning with `#` are treated as comments
+- `#` marks the beginning of a comment (unless when the value is wrapped in quotes)
+- empty values become empty strings (`EMPTY=` becomes `{EMPTY: ''}`)
+- inner quotes are maintained (think JSON) (`JSON={"foo": "bar"}` becomes `{JSON:"{\"foo\": \"bar\"}"`)
+- whitespace is removed from both ends of unquoted values (see more on [`trim`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)) (`FOO= some value ` becomes `{FOO: 'some value'}`)
+- single and double quoted values are escaped (`SINGLE_QUOTE='quoted'` becomes `{SINGLE_QUOTE: "quoted"}`)
+- single and double quoted values maintain whitespace from both ends (`FOO=" some value "` becomes `{FOO: ' some value '}`)
+- double quoted values expand new lines (`MULTILINE="new\nline"` becomes
+
+```
+{MULTILINE: 'new
+line'}
+```
+
+- backticks are supported (`` BACKTICK_KEY=`This has 'single' and "double" quotes inside of it.` ``)
+
+### What happens to environment variables that were already set?
+
+By default, we will never modify any environment variables that have already been set. In particular, if there is a variable in your `.env` file which collides with one that already exists in your environment, then that variable will be skipped.
+
+If instead, you want to override `process.env` use the `override` option.
+
+```javascript
+require('dotenv').config({ override: true })
+```
+
+### How come my environment variables are not showing up for React?
+
+Your React code is run in Webpack, where the `fs` module or even the `process` global itself are not accessible out-of-the-box. `process.env` can only be injected through Webpack configuration.
+
+If you are using [`react-scripts`](https://www.npmjs.com/package/react-scripts), which is distributed through [`create-react-app`](https://create-react-app.dev/), it has dotenv built in but with a quirk. Preface your environment variables with `REACT_APP_`. See [this stack overflow](https://stackoverflow.com/questions/42182577/is-it-possible-to-use-dotenv-in-a-react-project) for more details.
+
+If you are using other frameworks (e.g. Next.js, Gatsby...), you need to consult their documentation for how to inject environment variables into the client.
+
+### Can I customize/write plugins for dotenv?
+
+Yes! `dotenv.config()` returns an object representing the parsed `.env` file. This gives you everything you need to continue setting values on `process.env`. For example:
+
+```js
+const dotenv = require('dotenv')
+const variableExpansion = require('dotenv-expand')
+const myEnv = dotenv.config()
+variableExpansion(myEnv)
+```
+
+### How do I use dotenv with `import`?
+
+Simply..
+
+```javascript
+// index.mjs (ESM)
+import 'dotenv/config' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
+import express from 'express'
+```
+
+A little background..
+
+> When you run a module containing an `import` declaration, the modules it imports are loaded first, then each module body is executed in a depth-first traversal of the dependency graph, avoiding cycles by skipping anything already executed.
+>
+> – [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/)
+
+What does this mean in plain language? It means you would think the following would work but it won't.
+
+`errorReporter.mjs`:
+```js
+class Client {
+ constructor (apiKey) {
+ console.log('apiKey', apiKey)
+
+ this.apiKey = apiKey
+ }
+}
+
+export default new Client(process.env.API_KEY)
+```
+`index.mjs`:
+```js
+// Note: this is INCORRECT and will not work
+import * as dotenv from 'dotenv'
+dotenv.config()
+
+import errorReporter from './errorReporter.mjs' // process.env.API_KEY will be blank!
+```
+
+`process.env.API_KEY` will be blank.
+
+Instead, `index.mjs` should be written as..
+
+```js
+import 'dotenv/config'
+
+import errorReporter from './errorReporter.mjs'
+```
+
+Does that make sense? It's a bit unintuitive, but it is how importing of ES6 modules work. Here is a [working example of this pitfall](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-es6-import-pitfall).
+
+There are two alternatives to this approach:
+
+1. Preload dotenv: `node --require dotenv/config index.js` (_Note: you do not need to `import` dotenv with this approach_)
+2. Create a separate file that will execute `config` first as outlined in [this comment on #133](https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822)
+
+### Why am I getting the error `Module not found: Error: Can't resolve 'crypto|os|path'`?
+
+You are using dotenv on the front-end and have not included a polyfill. Webpack < 5 used to include these for you. Do the following:
+
+```bash
+npm install node-polyfill-webpack-plugin
+```
+
+Configure your `webpack.config.js` to something like the following.
+
+```js
+require('dotenv').config()
+
+const path = require('path');
+const webpack = require('webpack')
+
+const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
+
+module.exports = {
+ mode: 'development',
+ entry: './src/index.ts',
+ output: {
+ filename: 'bundle.js',
+ path: path.resolve(__dirname, 'dist'),
+ },
+ plugins: [
+ new NodePolyfillPlugin(),
+ new webpack.DefinePlugin({
+ 'process.env': {
+ HELLO: JSON.stringify(process.env.HELLO)
+ }
+ }),
+ ]
+};
+```
+
+Alternatively, just use [dotenv-webpack](https://github.com/mrsteele/dotenv-webpack) which does this and more behind the scenes for you.
+
+### What about variable expansion?
+
+Try [dotenv-expand](https://github.com/motdotla/dotenv-expand)
+
+### What about syncing and securing .env files?
+
+Use [dotenvx](https://github.com/dotenvx/dotenvx)
+
+### What if I accidentally commit my `.env` file to code?
+
+Remove it, [remove git history](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository) and then install the [git pre-commit hook](https://github.com/dotenvx/dotenvx#pre-commit) to prevent this from ever happening again.
+
+```
+brew install dotenvx/brew/dotenvx
+dotenvx precommit --install
+```
+
+### How can I prevent committing my `.env` file to a Docker build?
+
+Use the [docker prebuild hook](https://dotenvx.com/docs/features/prebuild).
+
+```bash
+# Dockerfile
+...
+RUN curl -fsS https://dotenvx.sh/ | sh
+...
+RUN dotenvx prebuild
+CMD ["dotenvx", "run", "--", "node", "index.js"]
+```
+
+## Contributing Guide
+
+See [CONTRIBUTING.md](CONTRIBUTING.md)
+
+## CHANGELOG
+
+See [CHANGELOG.md](CHANGELOG.md)
+
+## Who's using dotenv?
+
+[These npm modules depend on it.](https://www.npmjs.com/browse/depended/dotenv)
+
+Projects that expand it often use the [keyword "dotenv" on npm](https://www.npmjs.com/search?q=keywords:dotenv).
diff --git a/node_modules/dotenv/SECURITY.md b/node_modules/dotenv/SECURITY.md
new file mode 100644
index 0000000..237a8ce
--- /dev/null
+++ b/node_modules/dotenv/SECURITY.md
@@ -0,0 +1 @@
+Please report any security vulnerabilities to security@dotenvx.com.
diff --git a/node_modules/dotenv/config.d.ts b/node_modules/dotenv/config.d.ts
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/node_modules/dotenv/config.d.ts
@@ -0,0 +1 @@
+export {};
diff --git a/node_modules/dotenv/config.js b/node_modules/dotenv/config.js
new file mode 100644
index 0000000..b0b5576
--- /dev/null
+++ b/node_modules/dotenv/config.js
@@ -0,0 +1,9 @@
+(function () {
+ require('./lib/main').config(
+ Object.assign(
+ {},
+ require('./lib/env-options'),
+ require('./lib/cli-options')(process.argv)
+ )
+ )
+})()
diff --git a/node_modules/dotenv/lib/cli-options.js b/node_modules/dotenv/lib/cli-options.js
new file mode 100644
index 0000000..b6e40db
--- /dev/null
+++ b/node_modules/dotenv/lib/cli-options.js
@@ -0,0 +1,17 @@
+const re = /^dotenv_config_(encoding|path|quiet|debug|override|DOTENV_KEY)=(.+)$/
+
+module.exports = function optionMatcher (args) {
+ const options = args.reduce(function (acc, cur) {
+ const matches = cur.match(re)
+ if (matches) {
+ acc[matches[1]] = matches[2]
+ }
+ return acc
+ }, {})
+
+ if (!('quiet' in options)) {
+ options.quiet = 'true'
+ }
+
+ return options
+}
diff --git a/node_modules/dotenv/lib/env-options.js b/node_modules/dotenv/lib/env-options.js
new file mode 100644
index 0000000..5dcee8a
--- /dev/null
+++ b/node_modules/dotenv/lib/env-options.js
@@ -0,0 +1,28 @@
+// ../config.js accepts options via environment variables
+const options = {}
+
+if (process.env.DOTENV_CONFIG_ENCODING != null) {
+ options.encoding = process.env.DOTENV_CONFIG_ENCODING
+}
+
+if (process.env.DOTENV_CONFIG_PATH != null) {
+ options.path = process.env.DOTENV_CONFIG_PATH
+}
+
+if (process.env.DOTENV_CONFIG_QUIET != null) {
+ options.quiet = process.env.DOTENV_CONFIG_QUIET
+}
+
+if (process.env.DOTENV_CONFIG_DEBUG != null) {
+ options.debug = process.env.DOTENV_CONFIG_DEBUG
+}
+
+if (process.env.DOTENV_CONFIG_OVERRIDE != null) {
+ options.override = process.env.DOTENV_CONFIG_OVERRIDE
+}
+
+if (process.env.DOTENV_CONFIG_DOTENV_KEY != null) {
+ options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY
+}
+
+module.exports = options
diff --git a/node_modules/dotenv/lib/main.d.ts b/node_modules/dotenv/lib/main.d.ts
new file mode 100644
index 0000000..82604e8
--- /dev/null
+++ b/node_modules/dotenv/lib/main.d.ts
@@ -0,0 +1,162 @@
+// TypeScript Version: 3.0
+///
+import type { URL } from 'url';
+
+export interface DotenvParseOutput {
+ [name: string]: string;
+}
+
+/**
+ * Parses a string or buffer in the .env file format into an object.
+ *
+ * See https://dotenvx.com/docs
+ *
+ * @param src - contents to be parsed. example: `'DB_HOST=localhost'`
+ * @returns an object with keys and values based on `src`. example: `{ DB_HOST : 'localhost' }`
+ */
+export function parse(
+ src: string | Buffer
+): T;
+
+export interface DotenvConfigOptions {
+ /**
+ * Default: `path.resolve(process.cwd(), '.env')`
+ *
+ * Specify a custom path if your file containing environment variables is located elsewhere.
+ * Can also be an array of strings, specifying multiple paths.
+ *
+ * example: `require('dotenv').config({ path: '/custom/path/to/.env' })`
+ * example: `require('dotenv').config({ path: ['/path/to/first.env', '/path/to/second.env'] })`
+ */
+ path?: string | string[] | URL;
+
+ /**
+ * Default: `utf8`
+ *
+ * Specify the encoding of your file containing environment variables.
+ *
+ * example: `require('dotenv').config({ encoding: 'latin1' })`
+ */
+ encoding?: string;
+
+ /**
+ * Default: `false`
+ *
+ * Suppress all output (except errors).
+ *
+ * example: `require('dotenv').config({ quiet: true })`
+ */
+ quiet?: boolean;
+
+ /**
+ * Default: `false`
+ *
+ * Turn on logging to help debug why certain keys or values are not being set as you expect.
+ *
+ * example: `require('dotenv').config({ debug: process.env.DEBUG })`
+ */
+ debug?: boolean;
+
+ /**
+ * Default: `false`
+ *
+ * Override any environment variables that have already been set on your machine with values from your .env file.
+ *
+ * example: `require('dotenv').config({ override: true })`
+ */
+ override?: boolean;
+
+ /**
+ * Default: `process.env`
+ *
+ * Specify an object to write your secrets to. Defaults to process.env environment variables.
+ *
+ * example: `const processEnv = {}; require('dotenv').config({ processEnv: processEnv })`
+ */
+ processEnv?: DotenvPopulateInput;
+
+ /**
+ * Default: `undefined`
+ *
+ * Pass the DOTENV_KEY directly to config options. Defaults to looking for process.env.DOTENV_KEY environment variable. Note this only applies to decrypting .env.vault files. If passed as null or undefined, or not passed at all, dotenv falls back to its traditional job of parsing a .env file.
+ *
+ * example: `require('dotenv').config({ DOTENV_KEY: 'dotenv://:key_1234…@dotenvx.com/vault/.env.vault?environment=production' })`
+ */
+ DOTENV_KEY?: string;
+}
+
+export interface DotenvConfigOutput {
+ error?: Error;
+ parsed?: DotenvParseOutput;
+}
+
+export interface DotenvPopulateOptions {
+ /**
+ * Default: `false`
+ *
+ * Turn on logging to help debug why certain keys or values are not being set as you expect.
+ *
+ * example: `require('dotenv').config({ debug: process.env.DEBUG })`
+ */
+ debug?: boolean;
+
+ /**
+ * Default: `false`
+ *
+ * Override any environment variables that have already been set on your machine with values from your .env file.
+ *
+ * example: `require('dotenv').config({ override: true })`
+ */
+ override?: boolean;
+}
+
+export interface DotenvPopulateInput {
+ [name: string]: string;
+}
+
+/**
+ * Loads `.env` file contents into process.env by default. If `DOTENV_KEY` is present, it smartly attempts to load encrypted `.env.vault` file contents into process.env.
+ *
+ * See https://dotenvx.com/docs
+ *
+ * @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', quiet: false, debug: true, override: false }`
+ * @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } }
+ *
+ */
+export function config(options?: DotenvConfigOptions): DotenvConfigOutput;
+
+/**
+ * Loads `.env` file contents into process.env.
+ *
+ * See https://dotenvx.com/docs
+ *
+ * @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', quiet: false, debug: true, override: false }`
+ * @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } }
+ *
+ */
+export function configDotenv(options?: DotenvConfigOptions): DotenvConfigOutput;
+
+/**
+ * Loads `source` json contents into `target` like process.env.
+ *
+ * See https://dotenvx.com/docs
+ *
+ * @param processEnv - the target JSON object. in most cases use process.env but you can also pass your own JSON object
+ * @param parsed - the source JSON object
+ * @param options - additional options. example: `{ quiet: false, debug: true, override: false }`
+ * @returns {void}
+ *
+ */
+export function populate(processEnv: DotenvPopulateInput, parsed: DotenvPopulateInput, options?: DotenvConfigOptions): void;
+
+/**
+ * Decrypt ciphertext
+ *
+ * See https://dotenvx.com/docs
+ *
+ * @param encrypted - the encrypted ciphertext string
+ * @param keyStr - the decryption key string
+ * @returns {string}
+ *
+ */
+export function decrypt(encrypted: string, keyStr: string): string;
diff --git a/node_modules/dotenv/lib/main.js b/node_modules/dotenv/lib/main.js
new file mode 100644
index 0000000..91cabe4
--- /dev/null
+++ b/node_modules/dotenv/lib/main.js
@@ -0,0 +1,386 @@
+const fs = require('fs')
+const path = require('path')
+const os = require('os')
+const crypto = require('crypto')
+const packageJson = require('../package.json')
+
+const version = packageJson.version
+
+const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
+
+// Parse src into an Object
+function parse (src) {
+ const obj = {}
+
+ // Convert buffer to string
+ let lines = src.toString()
+
+ // Convert line breaks to same format
+ lines = lines.replace(/\r\n?/mg, '\n')
+
+ let match
+ while ((match = LINE.exec(lines)) != null) {
+ const key = match[1]
+
+ // Default undefined or null to empty string
+ let value = (match[2] || '')
+
+ // Remove whitespace
+ value = value.trim()
+
+ // Check if double quoted
+ const maybeQuote = value[0]
+
+ // Remove surrounding quotes
+ value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2')
+
+ // Expand newlines if double quoted
+ if (maybeQuote === '"') {
+ value = value.replace(/\\n/g, '\n')
+ value = value.replace(/\\r/g, '\r')
+ }
+
+ // Add to object
+ obj[key] = value
+ }
+
+ return obj
+}
+
+function _parseVault (options) {
+ options = options || {}
+
+ const vaultPath = _vaultPath(options)
+ options.path = vaultPath // parse .env.vault
+ const result = DotenvModule.configDotenv(options)
+ if (!result.parsed) {
+ const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`)
+ err.code = 'MISSING_DATA'
+ throw err
+ }
+
+ // handle scenario for comma separated keys - for use with key rotation
+ // example: DOTENV_KEY="dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=prod,dotenv://:key_7890@dotenvx.com/vault/.env.vault?environment=prod"
+ const keys = _dotenvKey(options).split(',')
+ const length = keys.length
+
+ let decrypted
+ for (let i = 0; i < length; i++) {
+ try {
+ // Get full key
+ const key = keys[i].trim()
+
+ // Get instructions for decrypt
+ const attrs = _instructions(result, key)
+
+ // Decrypt
+ decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key)
+
+ break
+ } catch (error) {
+ // last key
+ if (i + 1 >= length) {
+ throw error
+ }
+ // try next key
+ }
+ }
+
+ // Parse decrypted .env string
+ return DotenvModule.parse(decrypted)
+}
+
+function _warn (message) {
+ console.log(`[dotenv@${version}][WARN] ${message}`)
+}
+
+function _debug (message) {
+ console.log(`[dotenv@${version}][DEBUG] ${message}`)
+}
+
+function _log (message) {
+ console.log(`[dotenv@${version}] ${message}`)
+}
+
+function _dotenvKey (options) {
+ // prioritize developer directly setting options.DOTENV_KEY
+ if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
+ return options.DOTENV_KEY
+ }
+
+ // secondary infra already contains a DOTENV_KEY environment variable
+ if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
+ return process.env.DOTENV_KEY
+ }
+
+ // fallback to empty string
+ return ''
+}
+
+function _instructions (result, dotenvKey) {
+ // Parse DOTENV_KEY. Format is a URI
+ let uri
+ try {
+ uri = new URL(dotenvKey)
+ } catch (error) {
+ if (error.code === 'ERR_INVALID_URL') {
+ const err = new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development')
+ err.code = 'INVALID_DOTENV_KEY'
+ throw err
+ }
+
+ throw error
+ }
+
+ // Get decrypt key
+ const key = uri.password
+ if (!key) {
+ const err = new Error('INVALID_DOTENV_KEY: Missing key part')
+ err.code = 'INVALID_DOTENV_KEY'
+ throw err
+ }
+
+ // Get environment
+ const environment = uri.searchParams.get('environment')
+ if (!environment) {
+ const err = new Error('INVALID_DOTENV_KEY: Missing environment part')
+ err.code = 'INVALID_DOTENV_KEY'
+ throw err
+ }
+
+ // Get ciphertext payload
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`
+ const ciphertext = result.parsed[environmentKey] // DOTENV_VAULT_PRODUCTION
+ if (!ciphertext) {
+ const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`)
+ err.code = 'NOT_FOUND_DOTENV_ENVIRONMENT'
+ throw err
+ }
+
+ return { ciphertext, key }
+}
+
+function _vaultPath (options) {
+ let possibleVaultPath = null
+
+ if (options && options.path && options.path.length > 0) {
+ if (Array.isArray(options.path)) {
+ for (const filepath of options.path) {
+ if (fs.existsSync(filepath)) {
+ possibleVaultPath = filepath.endsWith('.vault') ? filepath : `${filepath}.vault`
+ }
+ }
+ } else {
+ possibleVaultPath = options.path.endsWith('.vault') ? options.path : `${options.path}.vault`
+ }
+ } else {
+ possibleVaultPath = path.resolve(process.cwd(), '.env.vault')
+ }
+
+ if (fs.existsSync(possibleVaultPath)) {
+ return possibleVaultPath
+ }
+
+ return null
+}
+
+function _resolveHome (envPath) {
+ return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath
+}
+
+function _configVault (options) {
+ const debug = Boolean(options && options.debug)
+ const quiet = options && 'quiet' in options ? options.quiet : true
+
+ if (debug || !quiet) {
+ _log('Loading env from encrypted .env.vault')
+ }
+
+ const parsed = DotenvModule._parseVault(options)
+
+ let processEnv = process.env
+ if (options && options.processEnv != null) {
+ processEnv = options.processEnv
+ }
+
+ DotenvModule.populate(processEnv, parsed, options)
+
+ return { parsed }
+}
+
+function configDotenv (options) {
+ const dotenvPath = path.resolve(process.cwd(), '.env')
+ let encoding = 'utf8'
+ const debug = Boolean(options && options.debug)
+ const quiet = options && 'quiet' in options ? options.quiet : true
+
+ if (options && options.encoding) {
+ encoding = options.encoding
+ } else {
+ if (debug) {
+ _debug('No encoding is specified. UTF-8 is used by default')
+ }
+ }
+
+ let optionPaths = [dotenvPath] // default, look for .env
+ if (options && options.path) {
+ if (!Array.isArray(options.path)) {
+ optionPaths = [_resolveHome(options.path)]
+ } else {
+ optionPaths = [] // reset default
+ for (const filepath of options.path) {
+ optionPaths.push(_resolveHome(filepath))
+ }
+ }
+ }
+
+ // Build the parsed data in a temporary object (because we need to return it). Once we have the final
+ // parsed data, we will combine it with process.env (or options.processEnv if provided).
+ let lastError
+ const parsedAll = {}
+ for (const path of optionPaths) {
+ try {
+ // Specifying an encoding returns a string instead of a buffer
+ const parsed = DotenvModule.parse(fs.readFileSync(path, { encoding }))
+
+ DotenvModule.populate(parsedAll, parsed, options)
+ } catch (e) {
+ if (debug) {
+ _debug(`Failed to load ${path} ${e.message}`)
+ }
+ lastError = e
+ }
+ }
+
+ let processEnv = process.env
+ if (options && options.processEnv != null) {
+ processEnv = options.processEnv
+ }
+
+ DotenvModule.populate(processEnv, parsedAll, options)
+
+ if (debug || !quiet) {
+ const keysCount = Object.keys(parsedAll).length
+ const shortPaths = []
+ for (const filePath of optionPaths) {
+ try {
+ const relative = path.relative(process.cwd(), filePath)
+ shortPaths.push(relative)
+ } catch (e) {
+ if (debug) {
+ _debug(`Failed to load ${filePath} ${e.message}`)
+ }
+ lastError = e
+ }
+ }
+
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(',')}`)
+ }
+
+ if (lastError) {
+ return { parsed: parsedAll, error: lastError }
+ } else {
+ return { parsed: parsedAll }
+ }
+}
+
+// Populates process.env from .env file
+function config (options) {
+ // fallback to original dotenv if DOTENV_KEY is not set
+ if (_dotenvKey(options).length === 0) {
+ return DotenvModule.configDotenv(options)
+ }
+
+ const vaultPath = _vaultPath(options)
+
+ // dotenvKey exists but .env.vault file does not exist
+ if (!vaultPath) {
+ _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`)
+
+ return DotenvModule.configDotenv(options)
+ }
+
+ return DotenvModule._configVault(options)
+}
+
+function decrypt (encrypted, keyStr) {
+ const key = Buffer.from(keyStr.slice(-64), 'hex')
+ let ciphertext = Buffer.from(encrypted, 'base64')
+
+ const nonce = ciphertext.subarray(0, 12)
+ const authTag = ciphertext.subarray(-16)
+ ciphertext = ciphertext.subarray(12, -16)
+
+ try {
+ const aesgcm = crypto.createDecipheriv('aes-256-gcm', key, nonce)
+ aesgcm.setAuthTag(authTag)
+ return `${aesgcm.update(ciphertext)}${aesgcm.final()}`
+ } catch (error) {
+ const isRange = error instanceof RangeError
+ const invalidKeyLength = error.message === 'Invalid key length'
+ const decryptionFailed = error.message === 'Unsupported state or unable to authenticate data'
+
+ if (isRange || invalidKeyLength) {
+ const err = new Error('INVALID_DOTENV_KEY: It must be 64 characters long (or more)')
+ err.code = 'INVALID_DOTENV_KEY'
+ throw err
+ } else if (decryptionFailed) {
+ const err = new Error('DECRYPTION_FAILED: Please check your DOTENV_KEY')
+ err.code = 'DECRYPTION_FAILED'
+ throw err
+ } else {
+ throw error
+ }
+ }
+}
+
+// Populate process.env with parsed values
+function populate (processEnv, parsed, options = {}) {
+ const debug = Boolean(options && options.debug)
+ const override = Boolean(options && options.override)
+
+ if (typeof parsed !== 'object') {
+ const err = new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate')
+ err.code = 'OBJECT_REQUIRED'
+ throw err
+ }
+
+ // Set process.env
+ for (const key of Object.keys(parsed)) {
+ if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
+ if (override === true) {
+ processEnv[key] = parsed[key]
+ }
+
+ if (debug) {
+ if (override === true) {
+ _debug(`"${key}" is already defined and WAS overwritten`)
+ } else {
+ _debug(`"${key}" is already defined and was NOT overwritten`)
+ }
+ }
+ } else {
+ processEnv[key] = parsed[key]
+ }
+ }
+}
+
+const DotenvModule = {
+ configDotenv,
+ _configVault,
+ _parseVault,
+ config,
+ decrypt,
+ parse,
+ populate
+}
+
+module.exports.configDotenv = DotenvModule.configDotenv
+module.exports._configVault = DotenvModule._configVault
+module.exports._parseVault = DotenvModule._parseVault
+module.exports.config = DotenvModule.config
+module.exports.decrypt = DotenvModule.decrypt
+module.exports.parse = DotenvModule.parse
+module.exports.populate = DotenvModule.populate
+
+module.exports = DotenvModule
diff --git a/node_modules/dotenv/package.json b/node_modules/dotenv/package.json
new file mode 100644
index 0000000..e937256
--- /dev/null
+++ b/node_modules/dotenv/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "dotenv",
+ "version": "16.6.1",
+ "description": "Loads environment variables from .env file",
+ "main": "lib/main.js",
+ "types": "lib/main.d.ts",
+ "exports": {
+ ".": {
+ "types": "./lib/main.d.ts",
+ "require": "./lib/main.js",
+ "default": "./lib/main.js"
+ },
+ "./config": "./config.js",
+ "./config.js": "./config.js",
+ "./lib/env-options": "./lib/env-options.js",
+ "./lib/env-options.js": "./lib/env-options.js",
+ "./lib/cli-options": "./lib/cli-options.js",
+ "./lib/cli-options.js": "./lib/cli-options.js",
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "dts-check": "tsc --project tests/types/tsconfig.json",
+ "lint": "standard",
+ "pretest": "npm run lint && npm run dts-check",
+ "test": "tap run --allow-empty-coverage --disable-coverage --timeout=60000",
+ "test:coverage": "tap run --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
+ "prerelease": "npm test",
+ "release": "standard-version"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/motdotla/dotenv.git"
+ },
+ "homepage": "https://github.com/motdotla/dotenv#readme",
+ "funding": "https://dotenvx.com",
+ "keywords": [
+ "dotenv",
+ "env",
+ ".env",
+ "environment",
+ "variables",
+ "config",
+ "settings"
+ ],
+ "readmeFilename": "README.md",
+ "license": "BSD-2-Clause",
+ "devDependencies": {
+ "@types/node": "^18.11.3",
+ "decache": "^4.6.2",
+ "sinon": "^14.0.1",
+ "standard": "^17.0.0",
+ "standard-version": "^9.5.0",
+ "tap": "^19.2.0",
+ "typescript": "^4.8.4"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "browser": {
+ "fs": false
+ }
+}
diff --git a/node_modules/escape-latex/LICENSE.md b/node_modules/escape-latex/LICENSE.md
new file mode 100644
index 0000000..6e54eff
--- /dev/null
+++ b/node_modules/escape-latex/LICENSE.md
@@ -0,0 +1,18 @@
+Copyright (c) 2012 Dang Mai
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/escape-latex/README.md b/node_modules/escape-latex/README.md
new file mode 100644
index 0000000..78ff9da
--- /dev/null
+++ b/node_modules/escape-latex/README.md
@@ -0,0 +1,75 @@
+# escape-latex
+
+[](https://greenkeeper.io/)
+
+[](https://travis-ci.org/dangmai/escape-latex)
+
+Escape LaTeX special characters with Javascript in NodeJS (>= 4.x) environment.
+
+## Usage
+
+```javascript
+npm install escape-latex
+var lescape = require('escape-latex');
+lescape("String to be escaped here #yolo");
+```
+
+## API
+
+```javascript
+lescape((input: String), {
+ preserveFormatting: Boolean,
+ escapeMapFn: Function,
+});
+```
+
+By default,
+`escape-latex` only escapes characters that would result in malformed LaTeX.
+These characters include `# $ % & \ ^ _ { }`.
+
+This means that the final LaTeX output might not look the same as your input Javascript string.
+For example, multiple spaces are kept as-is, which may be truncated to 1 space by your LaTeX software.
+
+If you want the final output string to be as similar to your input Javascript string as possible,
+you can set the `preserveFormatting` param to `true`, like so:
+
+```javascript
+lescape("Hello World", { preserveFormatting: true });
+// Hello~~~World
+```
+
+Which will be converted to three non-breaking spaces by your LaTeX software.
+
+The list of format characters that are escaped include `space, \t (tab), – (en-dash), — (em-dash)`.
+
+There is also the param `escapeMapFn` to modify the mapping of escaped characters,
+so you can add/modify/remove your own escapes if necessary.
+
+It accepts a callback function that takes in the default character escapes and the formatting escapes as parameters, and returns a complete escape mapping. Here's an example:
+
+```javascript
+lescape("Hello World", {
+ preseveFormatting: true,
+ escapeMapFn: function(defaultEscapes, formattingEscapes) {
+ formattingEscapes[" "] = "\\\\";
+ return Object.assign({}, defaultEscapes, formattingEscapes);
+ },
+});
+// Hello\\\\\\world
+```
+
+## Testing
+
+```
+npm test
+```
+
+## Notes
+
+* If you are updating from `escape-latex < 1.0.0`,
+ the `en-dash` and `em-dash` are no longer escaped by default.
+ Please use `preserveFormatting` to turn them on if necessary.
+
+## License
+
+MIT
diff --git a/node_modules/escape-latex/dist/index.js b/node_modules/escape-latex/dist/index.js
new file mode 100644
index 0000000..0da9c0d
--- /dev/null
+++ b/node_modules/escape-latex/dist/index.js
@@ -0,0 +1,80 @@
+"use strict";
+
+// Map the characters to escape to their escaped values. The list is derived
+// from http://www.cespedes.org/blog/85/how-to-escape-latex-special-characters
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var defaultEscapes = {
+ "{": "\\{",
+ "}": "\\}",
+ "\\": "\\textbackslash{}",
+ "#": "\\#",
+ $: "\\$",
+ "%": "\\%",
+ "&": "\\&",
+ "^": "\\textasciicircum{}",
+ _: "\\_",
+ "~": "\\textasciitilde{}"
+};
+var formatEscapes = {
+ "\u2013": "\\--",
+ "\u2014": "\\---",
+ " ": "~",
+ "\t": "\\qquad{}",
+ "\r\n": "\\newline{}",
+ "\n": "\\newline{}"
+};
+
+var defaultEscapeMapFn = function defaultEscapeMapFn(defaultEscapes, formatEscapes) {
+ return _extends({}, defaultEscapes, formatEscapes);
+};
+
+/**
+ * Escape a string to be used in LaTeX documents.
+ * @param {string} str the string to be escaped.
+ * @param {boolean} params.preserveFormatting whether formatting escapes should
+ * be performed (default: false).
+ * @param {function} params.escapeMapFn the function to modify the escape maps.
+ * @return {string} the escaped string, ready to be used in LaTeX.
+ */
+module.exports = function (str) {
+ var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
+ _ref$preserveFormatti = _ref.preserveFormatting,
+ preserveFormatting = _ref$preserveFormatti === undefined ? false : _ref$preserveFormatti,
+ _ref$escapeMapFn = _ref.escapeMapFn,
+ escapeMapFn = _ref$escapeMapFn === undefined ? defaultEscapeMapFn : _ref$escapeMapFn;
+
+ var runningStr = String(str);
+ var result = "";
+
+ var escapes = escapeMapFn(_extends({}, defaultEscapes), preserveFormatting ? _extends({}, formatEscapes) : {});
+ var escapeKeys = Object.keys(escapes); // as it is reused later on
+
+ // Algorithm: Go through the string character by character, if it matches
+ // with one of the special characters then we'll replace it with the escaped
+ // version.
+
+ var _loop = function _loop() {
+ var specialCharFound = false;
+ escapeKeys.forEach(function (key, index) {
+ if (specialCharFound) {
+ return;
+ }
+ if (runningStr.length >= key.length && runningStr.slice(0, key.length) === key) {
+ result += escapes[escapeKeys[index]];
+ runningStr = runningStr.slice(key.length, runningStr.length);
+ specialCharFound = true;
+ }
+ });
+ if (!specialCharFound) {
+ result += runningStr.slice(0, 1);
+ runningStr = runningStr.slice(1, runningStr.length);
+ }
+ };
+
+ while (runningStr) {
+ _loop();
+ }
+ return result;
+};
\ No newline at end of file
diff --git a/node_modules/escape-latex/package.json b/node_modules/escape-latex/package.json
new file mode 100644
index 0000000..a57945d
--- /dev/null
+++ b/node_modules/escape-latex/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "escape-latex",
+ "version": "1.2.0",
+ "description": "Escape LaTeX special characters with Javascript",
+ "main": "./dist/index.js",
+ "files": ["dist"],
+ "scripts": {
+ "test": "mocha --require babel-core/register -u tdd ./src/**/*.test.js",
+ "preversion": "npm test && npm run build",
+ "postversion": "git push && git push --tags",
+ "precommit": "npm run lint && lint-staged",
+ "prettier": "prettier --write ./src/**/*.js",
+ "lint": "eslint ./src",
+ "init": "mkdir dist",
+ "clean": "rm -rf dist",
+ "prebuild": "npm run clean && npm run init",
+ "build": "babel ./src -d ./dist --ignore index.test.js"
+ },
+ "lint-staged": {
+ "*.{js,json,css,md}": ["npm run prettier", "git add"]
+ },
+ "eslintConfig": {
+ "parserOptions": {
+ "ecmaVersion": 8
+ },
+ "extends": ["eslint:recommended", "google", "prettier"],
+ "env": {
+ "node": "true"
+ }
+ },
+ "prettier": {
+ "trailingComma": "all"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/dangmai/escape-latex"
+ },
+ "keywords": ["latex", "escape"],
+ "devDependencies": {
+ "babel-cli": "^6.26.0",
+ "babel-core": "^6.26.0",
+ "babel-plugin-transform-object-assign": "^6.22.0",
+ "babel-preset-env": "^1.6.1",
+ "chai": "^4.1.2",
+ "eslint": "^5.0.1",
+ "eslint-config-google": "^0.9.1",
+ "eslint-config-prettier": "^3.0.1",
+ "husky": "^0.14.3",
+ "lint-staged": "^7.0.5",
+ "mocha": "^5.0.0",
+ "prettier": "^1.9.2"
+ },
+ "author": "Dang Mai",
+ "license": "MIT"
+}
diff --git a/node_modules/fraction.js/CHANGELOG.md b/node_modules/fraction.js/CHANGELOG.md
new file mode 100644
index 0000000..ec492a7
--- /dev/null
+++ b/node_modules/fraction.js/CHANGELOG.md
@@ -0,0 +1,38 @@
+# CHANGELOG
+
+v5.2.2
+ - Improved documentation and removed unecessary check
+
+v5.2.1:
+ - 2bb7b05: Added negative sign check
+
+v5.2:
+ - 6f9d124: Implemented log and improved simplify
+ - b773e7a: Added named export to TS definition
+ - 70304f9: Fixed merge conflict
+ - 3b940d3: Implemented other comparing functions
+ - 10acdfc: Update README.md
+ - ba41d00: Update README.md
+ - 73ded97: Update README.md
+ - acabc39: Fixed param parsing
+
+v5.0.5:
+ - 2c9d4c2: Improved roundTo() and param parser
+
+v5.0.4:
+ - 39e61e7: Fixed bignum param passing
+
+v5.0.3:
+ - 7d9a3ec: Upgraded bundler for code quality
+
+v5.0.2:
+ - c64b1d6: fixed esm export
+
+v5.0.1:
+ - e440f9c: Fixed CJS export
+ - 9bbdd29: Fixed CJS export
+
+v5.0.0:
+ - ac7cd06: Fixed readme
+ - 33cc9e5: Added crude build
+ - 1adcc76: Release breaking v5.0. Fraction.js now builds on BigInt. The API stays the same as v4, except that the object attributes `n`, `d`, and `s`, are not Number but BigInt and may break code that directly accesses these attributes.
\ No newline at end of file
diff --git a/node_modules/fraction.js/LICENSE b/node_modules/fraction.js/LICENSE
new file mode 100644
index 0000000..8741015
--- /dev/null
+++ b/node_modules/fraction.js/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Robert Eisele
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/node_modules/fraction.js/README.md b/node_modules/fraction.js/README.md
new file mode 100644
index 0000000..e53343e
--- /dev/null
+++ b/node_modules/fraction.js/README.md
@@ -0,0 +1,520 @@
+# Fraction.js - ℚ in JavaScript
+
+[](https://npmjs.org/package/fraction.js "View this project on npm")
+[](http://opensource.org/licenses/MIT)
+
+Do you find the limitations of floating-point arithmetic frustrating, especially when rational and irrational numbers like π or √2 are stored within the same finite precision? This can lead to avoidable inaccuracies such as:
+
+```javascript
+1 / 98 * 98 // Results in 0.9999999999999999
+```
+
+For applications requiring higher precision or where working with fractions is preferable, consider incorporating *Fraction.js* into your project.
+
+The library effectively addresses precision issues, as demonstrated below:
+
+```javascript
+Fraction(1).div(98).mul(98) // Returns 1
+```
+
+*Fraction.js* uses a `BigInt` representation for both the numerator and denominator, ensuring minimal performance overhead while maximizing accuracy. Its design is optimized for precision, making it an ideal choice as a foundational library for other math tools, such as [Polynomial.js](https://github.com/rawify/Polynomial.js) and [Math.js](https://github.com/josdejong/mathjs).
+
+## Convert Decimal to Fraction
+
+One of the core features of *Fraction.js* is its ability to seamlessly convert decimal numbers into fractions.
+
+```javascript
+let x = new Fraction(1.88);
+let res = x.toFraction(true); // Returns "1 22/25" as a string
+```
+
+This is particularly useful when you need precise fraction representations instead of dealing with the limitations of floating-point arithmetic. What if you allow some error tolerance?
+
+```javascript
+let x = new Fraction(0.33333);
+let res = x.simplify(0.001) // Error < 0.001
+ .toFraction(); // Returns "1/3" as a string
+```
+
+## Precision
+
+As native `BigInt` support in JavaScript becomes more common, libraries like *Fraction.js* use it to handle calculations with higher precision. This improves the speed and accuracy of math operations with large numbers, providing a better solution for tasks that need more precision than floating-point numbers can offer.
+
+## Examples / Motivation
+
+A simple example of using *Fraction.js* might look like this:
+
+```javascript
+var f = new Fraction("9.4'31'"); // 9.4313131313131...
+f.mul([-4, 3]).mod("4.'8'"); // 4.88888888888888...
+```
+
+The result can then be displayed as:
+
+```javascript
+console.log(f.toFraction()); // -4154 / 1485
+```
+
+Additionally, you can access the internal attributes of the fraction, such as the sign (s), numerator (n), and denominator (d). Keep in mind that these values are stored as `BigInt`:
+
+```javascript
+Number(f.s) * Number(f.n) / Number(f.d) = -1 * 4154 / 1485 = -2.797306...
+```
+
+If you attempted to calculate this manually using floating-point arithmetic, you'd get something like:
+
+```javascript
+(9.4313131 * (-4 / 3)) % 4.888888 = -2.797308133...
+```
+
+While the result is reasonably close, it’s not as accurate as the fraction-based approach that *Fraction.js* provides, especially when dealing with repeating decimals or complex operations. This highlights the value of precision that the library brings.
+
+### Laplace Probability
+
+Here's a straightforward example of using *Fraction.js* to calculate probabilities. Let's determine the probability of rolling a specific outcome on a fair die:
+
+- **P({3})**: The probability of rolling a 3.
+- **P({1, 4})**: The probability of rolling either 1 or 4.
+- **P({2, 4, 6})**: The probability of rolling 2, 4, or 6.
+
+#### P({3}):
+
+```javascript
+var p = new Fraction([3].length, 6).toString(); // "0.1(6)"
+```
+
+#### P({1, 4}):
+
+```javascript
+var p = new Fraction([1, 4].length, 6).toString(); // "0.(3)"
+```
+
+#### P({2, 4, 6}):
+
+```javascript
+var p = new Fraction([2, 4, 6].length, 6).toString(); // "0.5"
+```
+
+### Convert degrees/minutes/seconds to precise rational representation:
+
+57+45/60+17/3600
+
+```javascript
+var deg = 57; // 57°
+var min = 45; // 45 Minutes
+var sec = 17; // 17 Seconds
+
+new Fraction(deg).add(min, 60).add(sec, 3600).toString() // -> 57.7547(2)
+```
+
+
+### Rational approximation of irrational numbers
+
+To approximate a number like *sqrt(5) - 2* with a numerator and denominator, you can reformat the equation as follows: *pow(n / d + 2, 2) = 5*.
+
+Then the following algorithm will generate the rational number besides the binary representation.
+
+```javascript
+var x = "/", s = "";
+
+var a = new Fraction(0),
+ b = new Fraction(1);
+for (var n = 0; n <= 10; n++) {
+
+ var c = a.add(b).div(2);
+
+ console.log(n + "\t" + a + "\t" + b + "\t" + c + "\t" + x);
+
+ if (c.add(2).pow(2).valueOf() < 5) {
+ a = c;
+ x = "1";
+ } else {
+ b = c;
+ x = "0";
+ }
+ s+= x;
+}
+console.log(s)
+```
+
+The result is
+
+```
+n a[n] b[n] c[n] x[n]
+0 0/1 1/1 1/2 /
+1 0/1 1/2 1/4 0
+2 0/1 1/4 1/8 0
+3 1/8 1/4 3/16 1
+4 3/16 1/4 7/32 1
+5 7/32 1/4 15/64 1
+6 15/64 1/4 31/128 1
+7 15/64 31/128 61/256 0
+8 15/64 61/256 121/512 0
+9 15/64 121/512 241/1024 0
+10 241/1024 121/512 483/2048 1
+```
+
+Thus the approximation after 11 iterations of the bisection method is *483 / 2048* and the binary representation is 0.00111100011 (see [WolframAlpha](http://www.wolframalpha.com/input/?i=sqrt%285%29-2+binary))
+
+I published another example on how to approximate PI with fraction.js on my [blog](https://raw.org/article/rational-numbers-in-javascript/) (Still not the best idea to approximate irrational numbers, but it illustrates the capabilities of Fraction.js perfectly).
+
+
+### Get the exact fractional part of a number
+
+```javascript
+var f = new Fraction("-6.(3416)");
+console.log(f.mod(1).abs().toFraction()); // = 3416/9999
+```
+
+### Mathematical correct modulo
+
+The behaviour on negative congruences is different to most modulo implementations in computer science. Even the *mod()* function of Fraction.js behaves in the typical way. To solve the problem of having the mathematical correct modulo with Fraction.js you could come up with this:
+
+```javascript
+var a = -1;
+var b = 10.99;
+
+console.log(new Fraction(a)
+ .mod(b)); // Not correct, usual Modulo
+
+console.log(new Fraction(a)
+ .mod(b).add(b).mod(b)); // Correct! Mathematical Modulo
+```
+
+fmod() imprecision circumvented
+---
+It turns out that Fraction.js outperforms almost any fmod() implementation, including JavaScript itself, [php.js](http://phpjs.org/functions/fmod/), C++, Python, Java and even Wolframalpha due to the fact that numbers like 0.05, 0.1, ... are infinite decimal in base 2.
+
+The equation *fmod(4.55, 0.05)* gives *0.04999999999999957*, wolframalpha says *1/20*. The correct answer should be **zero**, as 0.05 divides 4.55 without any remainder.
+
+
+## Parser
+
+Any function (see below) as well as the constructor of the *Fraction* class parses its input and reduce it to the smallest term.
+
+You can pass either Arrays, Objects, Integers, Doubles or Strings.
+
+### Arrays / Objects
+
+```javascript
+new Fraction(numerator, denominator);
+new Fraction([numerator, denominator]);
+new Fraction({n: numerator, d: denominator});
+```
+
+### Integers
+
+```javascript
+new Fraction(123);
+```
+
+### Doubles
+
+```javascript
+new Fraction(55.4);
+```
+
+**Note:** If you pass a double as it is, Fraction.js will perform a number analysis based on Farey Sequences. If you concern performance, cache Fraction.js objects and pass arrays/objects.
+
+The method is really precise, but too large exact numbers, like 1234567.9991829 will result in a wrong approximation. If you want to keep the number as it is, convert it to a string, as the string parser will not perform any further observations. If you have problems with the approximation, in the file `examples/approx.js` is a different approximation algorithm, which might work better in some more specific use-cases.
+
+
+### Strings
+
+```javascript
+new Fraction("123.45");
+new Fraction("123/45"); // A rational number represented as two decimals, separated by a slash
+new Fraction("123:45"); // A rational number represented as two decimals, separated by a colon
+new Fraction("4 123/45"); // A rational number represented as a whole number and a fraction
+new Fraction("123.'456'"); // Note the quotes, see below!
+new Fraction("123.(456)"); // Note the brackets, see below!
+new Fraction("123.45'6'"); // Note the quotes, see below!
+new Fraction("123.45(6)"); // Note the brackets, see below!
+```
+
+### Two arguments
+
+```javascript
+new Fraction(3, 2); // 3/2 = 1.5
+```
+
+### Repeating decimal places
+
+*Fraction.js* can easily handle repeating decimal places. For example *1/3* is *0.3333...*. There is only one repeating digit. As you can see in the examples above, you can pass a number like *1/3* as "0.'3'" or "0.(3)", which are synonym. There are no tests to parse something like 0.166666666 to 1/6! If you really want to handle this number, wrap around brackets on your own with the function below for example: 0.1(66666666)
+
+Assume you want to divide 123.32 / 33.6(567). [WolframAlpha](http://www.wolframalpha.com/input/?i=123.32+%2F+%2812453%2F370%29) states that you'll get a period of 1776 digits. *Fraction.js* comes to the same result. Give it a try:
+
+```javascript
+var f = new Fraction("123.32");
+console.log("Bam: " + f.div("33.6(567)"));
+```
+
+To automatically make a number like "0.123123123" to something more Fraction.js friendly like "0.(123)", I hacked this little brute force algorithm in a 10 minutes. Improvements are welcome...
+
+```javascript
+function formatDecimal(str) {
+
+ var comma, pre, offset, pad, times, repeat;
+
+ if (-1 === (comma = str.indexOf(".")))
+ return str;
+
+ pre = str.substr(0, comma + 1);
+ str = str.substr(comma + 1);
+
+ for (var i = 0; i < str.length; i++) {
+
+ offset = str.substr(0, i);
+
+ for (var j = 0; j < 5; j++) {
+
+ pad = str.substr(i, j + 1);
+
+ times = Math.ceil((str.length - offset.length) / pad.length);
+
+ repeat = new Array(times + 1).join(pad); // Silly String.repeat hack
+
+ if (0 === (offset + repeat).indexOf(str)) {
+ return pre + offset + "(" + pad + ")";
+ }
+ }
+ }
+ return null;
+}
+
+var f, x = formatDecimal("13.0123123123"); // = 13.0(123)
+if (x !== null) {
+ f = new Fraction(x);
+}
+```
+
+## Attributes
+
+
+The Fraction object allows direct access to the numerator, denominator and sign attributes. It is ensured that only the sign-attribute holds sign information so that a sign comparison is only necessary against this attribute.
+
+```javascript
+var f = new Fraction('-1/2');
+console.log(f.n); // Numerator: 1
+console.log(f.d); // Denominator: 2
+console.log(f.s); // Sign: -1
+```
+
+
+## Functions
+
+### Fraction abs()
+
+Returns the actual number without any sign information
+
+### Fraction neg()
+
+Returns the actual number with flipped sign in order to get the additive inverse
+
+### Fraction add(n)
+
+Returns the sum of the actual number and the parameter n
+
+### Fraction sub(n)
+
+Returns the difference of the actual number and the parameter n
+
+### Fraction mul(n)
+
+Returns the product of the actual number and the parameter n
+
+### Fraction div(n)
+
+Returns the quotient of the actual number and the parameter n
+
+### Fraction pow(exp)
+
+Returns the power of the actual number, raised to an possible rational exponent. If the result becomes non-rational the function returns `null`.
+
+### Fraction log(base)
+
+Returns the logarithm of the actual number to a given rational base. If the result becomes non-rational the function returns `null`.
+
+### Fraction mod(n)
+
+Returns the modulus (rest of the division) of the actual object and n (this % n). It's a much more precise [fmod()](#fmod-impreciseness-circumvented) if you like. Please note that *mod()* is just like the modulo operator of most programming languages. If you want a mathematical correct modulo, see [here](#mathematical-correct-modulo).
+
+### Fraction mod()
+
+Returns the modulus (rest of the division) of the actual object (numerator mod denominator)
+
+### Fraction gcd(n)
+
+Returns the fractional greatest common divisor
+
+### Fraction lcm(n)
+
+Returns the fractional least common multiple
+
+### Fraction ceil([places=0-16])
+
+Returns the ceiling of a rational number with Math.ceil
+
+### Fraction floor([places=0-16])
+
+Returns the floor of a rational number with Math.floor
+
+### Fraction round([places=0-16])
+
+Returns the rational number rounded with Math.round
+
+### Fraction roundTo(multiple)
+
+Rounds a fraction to the closest multiple of another fraction.
+
+### Fraction inverse()
+
+Returns the multiplicative inverse of the actual number (n / d becomes d / n) in order to get the reciprocal
+
+### Fraction simplify([eps=0.001])
+
+Simplifies the rational number under a certain error threshold. Ex. `0.333` will be `1/3` with `eps=0.001`
+
+### boolean equals(n)
+
+Check if two rational numbers are equal
+
+### boolean lt(n)
+
+Check if this rational number is less than another
+
+### boolean lte(n)
+
+Check if this rational number is less than or equal another
+
+### boolean gt(n)
+
+Check if this rational number is greater than another
+
+### boolean gte(n)
+
+Check if this rational number is greater than or equal another
+
+### int compare(n)
+
+Compare two numbers.
+```
+result < 0: n is greater than actual number
+result > 0: n is smaller than actual number
+result = 0: n is equal to the actual number
+```
+
+### boolean divisible(n)
+
+Check if two numbers are divisible (n divides this)
+
+### double valueOf()
+
+Returns a decimal representation of the fraction
+
+### String toString([decimalPlaces=15])
+
+Generates an exact string representation of the given object. For repeating decimal places, digits within repeating cycles are enclosed in parentheses, e.g., `1/3 = "0.(3)"`. For other numbers, the string will include up to the specified `decimalPlaces` significant digits, including any trailing zeros if truncation occurs. For example, `1/2` will be represented as `"0.5"`, without additional trailing zeros.
+
+**Note:** Since both `valueOf()` and `toString()` are provided, `toString()` will only be invoked implicitly when the object is used in a string context. For instance, when using the plus operator like `"123" + new Fraction`, `valueOf()` will be called first, as JavaScript attempts to combine primitives before concatenating them, with the string type taking precedence. However, `alert(new Fraction)` or `String(new Fraction)` will behave as expected. To ensure specific behavior, explicitly call either `toString()` or `valueOf()`.
+
+### String toLatex(showMixed=false)
+
+Generates an exact LaTeX representation of the actual object. You can see a [live demo](https://raw.org/article/rational-numbers-in-javascript/) on my blog.
+
+The optional boolean parameter indicates if you want to show the a mixed fraction. "1 1/3" instead of "4/3"
+
+### String toFraction(showMixed=false)
+
+Gets a string representation of the fraction
+
+The optional boolean parameter indicates if you want to showa mixed fraction. "1 1/3" instead of "4/3"
+
+### Array toContinued()
+
+Gets an array of the fraction represented as a continued fraction. The first element always contains the whole part.
+
+```javascript
+var f = new Fraction('88/33');
+var c = f.toContinued(); // [2, 1, 2]
+```
+
+### Fraction clone()
+
+Creates a copy of the actual Fraction object
+
+
+## Exceptions
+
+If a really hard error occurs (parsing error, division by zero), *Fraction.js* throws exceptions! Please make sure you handle them correctly.
+
+
+## Installation
+
+You can install `Fraction.js` via npm:
+
+```bash
+npm install fraction.js
+```
+
+Or with yarn:
+
+```bash
+yarn add fraction.js
+```
+
+Alternatively, download or clone the repository:
+
+```bash
+git clone https://github.com/rawify/Fraction.js
+```
+
+## Usage
+
+Include the `fraction.min.js` file in your project:
+
+```html
+
+
+```
+
+Or in a Node.js project:
+
+```javascript
+const Fraction = require('fraction.js');
+```
+
+or
+
+```javascript
+import Fraction from 'fraction.js';
+```
+
+
+## Coding Style
+
+As every library I publish, Fraction.js is also built to be as small as possible after compressing it with Google Closure Compiler in advanced mode. Thus the coding style orientates a little on maxing-out the compression rate. Please make sure you keep this style if you plan to extend the library.
+
+## Building the library
+
+After cloning the Git repository run:
+
+```bash
+npm install
+npm run build
+```
+
+## Run a test
+
+Testing the source against the shipped test suite is as easy as
+
+```bash
+npm run test
+```
+
+## Copyright and Licensing
+
+Copyright (c) 2025, [Robert Eisele](https://raw.org/)
+Licensed under the MIT license.
diff --git a/node_modules/fraction.js/dist/fraction.js b/node_modules/fraction.js/dist/fraction.js
new file mode 100644
index 0000000..816d5db
--- /dev/null
+++ b/node_modules/fraction.js/dist/fraction.js
@@ -0,0 +1,1045 @@
+'use strict';
+
+/**
+ *
+ * This class offers the possibility to calculate fractions.
+ * You can pass a fraction in different formats. Either as array, as double, as string or as an integer.
+ *
+ * Array/Object form
+ * [ 0 => , 1 => ]
+ * { n => , d => }
+ *
+ * Integer form
+ * - Single integer value as BigInt or Number
+ *
+ * Double form
+ * - Single double value as Number
+ *
+ * String form
+ * 123.456 - a simple double
+ * 123/456 - a string fraction
+ * 123.'456' - a double with repeating decimal places
+ * 123.(456) - synonym
+ * 123.45'6' - a double with repeating last place
+ * 123.45(6) - synonym
+ *
+ * Example:
+ * let f = new Fraction("9.4'31'");
+ * f.mul([-4, 3]).div(4.9);
+ *
+ */
+
+// Set Identity function to downgrade BigInt to Number if needed
+if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; };
+
+const C_ZERO = BigInt(0);
+const C_ONE = BigInt(1);
+const C_TWO = BigInt(2);
+const C_THREE = BigInt(3);
+const C_FIVE = BigInt(5);
+const C_TEN = BigInt(10);
+const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
+
+// Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
+// Example: 1/7 = 0.(142857) has 6 repeating decimal places.
+// If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
+const MAX_CYCLE_LEN = 2000;
+
+// Parsed data to avoid calling "new" all the time
+const P = {
+ "s": C_ONE,
+ "n": C_ZERO,
+ "d": C_ONE
+};
+
+function assign(n, s) {
+
+ try {
+ n = BigInt(n);
+ } catch (e) {
+ throw InvalidParameter();
+ }
+ return n * s;
+}
+
+function ifloor(x) {
+ return typeof x === 'bigint' ? x : Math.floor(x);
+}
+
+// Creates a new Fraction internally without the need of the bulky constructor
+function newFraction(n, d) {
+
+ if (d === C_ZERO) {
+ throw DivisionByZero();
+ }
+
+ const f = Object.create(Fraction.prototype);
+ f["s"] = n < C_ZERO ? -C_ONE : C_ONE;
+
+ n = n < C_ZERO ? -n : n;
+
+ const a = gcd(n, d);
+
+ f["n"] = n / a;
+ f["d"] = d / a;
+ return f;
+}
+
+const FACTORSTEPS = [C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO * C_THREE, C_TWO, C_TWO * C_THREE]; // repeats
+function factorize(n) {
+
+ const factors = Object.create(null);
+ if (n <= C_ONE) {
+ factors[n] = C_ONE;
+ return factors;
+ }
+
+ const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; };
+
+ while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; }
+ while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; }
+ while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; }
+
+ // 30-wheel trial division: test only residues coprime to 2*3*5
+ // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ...
+ for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) {
+ while (n % p === C_ZERO) { add(p); n /= p; }
+ p += FACTORSTEPS[si];
+ si = (si + 1) & 7; // fast modulo 8
+ }
+ if (n > C_ONE) add(n);
+ return factors;
+}
+
+const parse = function (p1, p2) {
+
+ let n = C_ZERO, d = C_ONE, s = C_ONE;
+
+ if (p1 === undefined || p1 === null) { // No argument
+ /* void */
+ } else if (p2 !== undefined) { // Two arguments
+
+ if (typeof p1 === "bigint") {
+ n = p1;
+ } else if (isNaN(p1)) {
+ throw InvalidParameter();
+ } else if (p1 % 1 !== 0) {
+ throw NonIntegerParameter();
+ } else {
+ n = BigInt(p1);
+ }
+
+ if (typeof p2 === "bigint") {
+ d = p2;
+ } else if (isNaN(p2)) {
+ throw InvalidParameter();
+ } else if (p2 % 1 !== 0) {
+ throw NonIntegerParameter();
+ } else {
+ d = BigInt(p2);
+ }
+
+ s = n * d;
+
+ } else if (typeof p1 === "object") {
+ if ("d" in p1 && "n" in p1) {
+ n = BigInt(p1["n"]);
+ d = BigInt(p1["d"]);
+ if ("s" in p1)
+ n *= BigInt(p1["s"]);
+ } else if (0 in p1) {
+ n = BigInt(p1[0]);
+ if (1 in p1)
+ d = BigInt(p1[1]);
+ } else if (typeof p1 === "bigint") {
+ n = p1;
+ } else {
+ throw InvalidParameter();
+ }
+ s = n * d;
+ } else if (typeof p1 === "number") {
+
+ if (isNaN(p1)) {
+ throw InvalidParameter();
+ }
+
+ if (p1 < 0) {
+ s = -C_ONE;
+ p1 = -p1;
+ }
+
+ if (p1 % 1 === 0) {
+ n = BigInt(p1);
+ } else {
+
+ let z = 1;
+
+ let A = 0, B = 1;
+ let C = 1, D = 1;
+
+ let N = 10000000;
+
+ if (p1 >= 1) {
+ z = 10 ** Math.floor(1 + Math.log10(p1));
+ p1 /= z;
+ }
+
+ // Using Farey Sequences
+
+ while (B <= N && D <= N) {
+ let M = (A + C) / (B + D);
+
+ if (p1 === M) {
+ if (B + D <= N) {
+ n = A + C;
+ d = B + D;
+ } else if (D > B) {
+ n = C;
+ d = D;
+ } else {
+ n = A;
+ d = B;
+ }
+ break;
+
+ } else {
+
+ if (p1 > M) {
+ A += C;
+ B += D;
+ } else {
+ C += A;
+ D += B;
+ }
+
+ if (B > N) {
+ n = C;
+ d = D;
+ } else {
+ n = A;
+ d = B;
+ }
+ }
+ }
+ n = BigInt(n) * BigInt(z);
+ d = BigInt(d);
+ }
+
+ } else if (typeof p1 === "string") {
+
+ let ndx = 0;
+
+ let v = C_ZERO, w = C_ZERO, x = C_ZERO, y = C_ONE, z = C_ONE;
+
+ let match = p1.replace(/_/g, '').match(/\d+|./g);
+
+ if (match === null)
+ throw InvalidParameter();
+
+ if (match[ndx] === '-') {// Check for minus sign at the beginning
+ s = -C_ONE;
+ ndx++;
+ } else if (match[ndx] === '+') {// Check for plus sign at the beginning
+ ndx++;
+ }
+
+ if (match.length === ndx + 1) { // Check if it's just a simple number "1234"
+ w = assign(match[ndx++], s);
+ } else if (match[ndx + 1] === '.' || match[ndx] === '.') { // Check if it's a decimal number
+
+ if (match[ndx] !== '.') { // Handle 0.5 and .5
+ v = assign(match[ndx++], s);
+ }
+ ndx++;
+
+ // Check for decimal places
+ if (ndx + 1 === match.length || match[ndx + 1] === '(' && match[ndx + 3] === ')' || match[ndx + 1] === "'" && match[ndx + 3] === "'") {
+ w = assign(match[ndx], s);
+ y = C_TEN ** BigInt(match[ndx].length);
+ ndx++;
+ }
+
+ // Check for repeating places
+ if (match[ndx] === '(' && match[ndx + 2] === ')' || match[ndx] === "'" && match[ndx + 2] === "'") {
+ x = assign(match[ndx + 1], s);
+ z = C_TEN ** BigInt(match[ndx + 1].length) - C_ONE;
+ ndx += 3;
+ }
+
+ } else if (match[ndx + 1] === '/' || match[ndx + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
+ w = assign(match[ndx], s);
+ y = assign(match[ndx + 2], C_ONE);
+ ndx += 3;
+ } else if (match[ndx + 3] === '/' && match[ndx + 1] === ' ') { // Check for a complex fraction "123 1/2"
+ v = assign(match[ndx], s);
+ w = assign(match[ndx + 2], s);
+ y = assign(match[ndx + 4], C_ONE);
+ ndx += 5;
+ }
+
+ if (match.length <= ndx) { // Check for more tokens on the stack
+ d = y * z;
+ s = /* void */
+ n = x + d * v + z * w;
+ } else {
+ throw InvalidParameter();
+ }
+
+ } else if (typeof p1 === "bigint") {
+ n = p1;
+ s = p1;
+ d = C_ONE;
+ } else {
+ throw InvalidParameter();
+ }
+
+ if (d === C_ZERO) {
+ throw DivisionByZero();
+ }
+
+ P["s"] = s < C_ZERO ? -C_ONE : C_ONE;
+ P["n"] = n < C_ZERO ? -n : n;
+ P["d"] = d < C_ZERO ? -d : d;
+};
+
+function modpow(b, e, m) {
+
+ let r = C_ONE;
+ for (; e > C_ZERO; b = (b * b) % m, e >>= C_ONE) {
+
+ if (e & C_ONE) {
+ r = (r * b) % m;
+ }
+ }
+ return r;
+}
+
+function cycleLen(n, d) {
+
+ for (; d % C_TWO === C_ZERO;
+ d /= C_TWO) {
+ }
+
+ for (; d % C_FIVE === C_ZERO;
+ d /= C_FIVE) {
+ }
+
+ if (d === C_ONE) // Catch non-cyclic numbers
+ return C_ZERO;
+
+ // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
+ // 10^(d-1) % d == 1
+ // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
+ // as we want to translate the numbers to strings.
+
+ let rem = C_TEN % d;
+ let t = 1;
+
+ for (; rem !== C_ONE; t++) {
+ rem = rem * C_TEN % d;
+
+ if (t > MAX_CYCLE_LEN)
+ return C_ZERO; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
+ }
+ return BigInt(t);
+}
+
+function cycleStart(n, d, len) {
+
+ let rem1 = C_ONE;
+ let rem2 = modpow(C_TEN, len, d);
+
+ for (let t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
+ // Solve 10^s == 10^(s+t) (mod d)
+
+ if (rem1 === rem2)
+ return BigInt(t);
+
+ rem1 = rem1 * C_TEN % d;
+ rem2 = rem2 * C_TEN % d;
+ }
+ return 0;
+}
+
+function gcd(a, b) {
+
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+
+ while (1) {
+ a %= b;
+ if (!a)
+ return b;
+ b %= a;
+ if (!b)
+ return a;
+ }
+}
+
+/**
+ * Module constructor
+ *
+ * @constructor
+ * @param {number|Fraction=} a
+ * @param {number=} b
+ */
+function Fraction(a, b) {
+
+ parse(a, b);
+
+ if (this instanceof Fraction) {
+ a = gcd(P["d"], P["n"]); // Abuse a
+ this["s"] = P["s"];
+ this["n"] = P["n"] / a;
+ this["d"] = P["d"] / a;
+ } else {
+ return newFraction(P['s'] * P['n'], P['d']);
+ }
+}
+
+const DivisionByZero = function () { return new Error("Division by Zero"); };
+const InvalidParameter = function () { return new Error("Invalid argument"); };
+const NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
+
+Fraction.prototype = {
+
+ "s": C_ONE,
+ "n": C_ZERO,
+ "d": C_ONE,
+
+ /**
+ * Calculates the absolute value
+ *
+ * Ex: new Fraction(-4).abs() => 4
+ **/
+ "abs": function () {
+
+ return newFraction(this["n"], this["d"]);
+ },
+
+ /**
+ * Inverts the sign of the current fraction
+ *
+ * Ex: new Fraction(-4).neg() => 4
+ **/
+ "neg": function () {
+
+ return newFraction(-this["s"] * this["n"], this["d"]);
+ },
+
+ /**
+ * Adds two rational numbers
+ *
+ * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
+ **/
+ "add": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Subtracts two rational numbers
+ *
+ * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
+ **/
+ "sub": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Multiplies two rational numbers
+ *
+ * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
+ **/
+ "mul": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * P["s"] * this["n"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Divides two rational numbers
+ *
+ * Ex: new Fraction("-17.(345)").inverse().div(3)
+ **/
+ "div": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * P["s"] * this["n"] * P["d"],
+ this["d"] * P["n"]
+ );
+ },
+
+ /**
+ * Clones the actual object
+ *
+ * Ex: new Fraction("-17.(345)").clone()
+ **/
+ "clone": function () {
+ return newFraction(this['s'] * this['n'], this['d']);
+ },
+
+ /**
+ * Calculates the modulo of two rational numbers - a more precise fmod
+ *
+ * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
+ * Ex: new Fraction(20, 10).mod().equals(0) ? "is Integer"
+ **/
+ "mod": function (a, b) {
+
+ if (a === undefined) {
+ return newFraction(this["s"] * this["n"] % this["d"], C_ONE);
+ }
+
+ parse(a, b);
+ if (C_ZERO === P["n"] * this["d"]) {
+ throw DivisionByZero();
+ }
+
+ /**
+ * I derived the rational modulo similar to the modulo for integers
+ *
+ * https://raw.org/book/analysis/rational-numbers/
+ *
+ * n1/d1 = (n2/d2) * q + r, where 0 ≤ r < n2/d2
+ * => d2 * n1 = n2 * d1 * q + d1 * d2 * r
+ * => r = (d2 * n1 - n2 * d1 * q) / (d1 * d2)
+ * = (d2 * n1 - n2 * d1 * floor((d2 * n1) / (n2 * d1))) / (d1 * d2)
+ * = ((d2 * n1) % (n2 * d1)) / (d1 * d2)
+ */
+ return newFraction(
+ this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]),
+ P["d"] * this["d"]);
+ },
+
+ /**
+ * Calculates the fractional gcd of two rational numbers
+ *
+ * Ex: new Fraction(5,8).gcd(3,7) => 1/56
+ */
+ "gcd": function (a, b) {
+
+ parse(a, b);
+
+ // https://raw.org/book/analysis/rational-numbers/
+ // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
+
+ return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]);
+ },
+
+ /**
+ * Calculates the fractional lcm of two rational numbers
+ *
+ * Ex: new Fraction(5,8).lcm(3,7) => 15
+ */
+ "lcm": function (a, b) {
+
+ parse(a, b);
+
+ // https://raw.org/book/analysis/rational-numbers/
+ // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
+
+ if (P["n"] === C_ZERO && this["n"] === C_ZERO) {
+ return newFraction(C_ZERO, C_ONE);
+ }
+ return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]));
+ },
+
+ /**
+ * Gets the inverse of the fraction, means numerator and denominator are exchanged
+ *
+ * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
+ **/
+ "inverse": function () {
+ return newFraction(this["s"] * this["d"], this["n"]);
+ },
+
+ /**
+ * Calculates the fraction to some integer exponent
+ *
+ * Ex: new Fraction(-1,2).pow(-3) => -8
+ */
+ "pow": function (a, b) {
+
+ parse(a, b);
+
+ // Trivial case when exp is an integer
+
+ if (P['d'] === C_ONE) {
+
+ if (P['s'] < C_ZERO) {
+ return newFraction((this['s'] * this["d"]) ** P['n'], this["n"] ** P['n']);
+ } else {
+ return newFraction((this['s'] * this["n"]) ** P['n'], this["d"] ** P['n']);
+ }
+ }
+
+ // Negative roots become complex
+ // (-a/b)^(c/d) = x
+ // ⇔ (-1)^(c/d) * (a/b)^(c/d) = x
+ // ⇔ (cos(pi) + i*sin(pi))^(c/d) * (a/b)^(c/d) = x
+ // ⇔ (cos(c*pi/d) + i*sin(c*pi/d)) * (a/b)^(c/d) = x # DeMoivre's formula
+ // From which follows that only for c=0 the root is non-complex
+ if (this['s'] < C_ZERO) return null;
+
+ // Now prime factor n and d
+ let N = factorize(this['n']);
+ let D = factorize(this['d']);
+
+ // Exponentiate and take root for n and d individually
+ let n = C_ONE;
+ let d = C_ONE;
+ for (let k in N) {
+ if (k === '1') continue;
+ if (k === '0') {
+ n = C_ZERO;
+ break;
+ }
+ N[k] *= P['n'];
+
+ if (N[k] % P['d'] === C_ZERO) {
+ N[k] /= P['d'];
+ } else return null;
+ n *= BigInt(k) ** N[k];
+ }
+
+ for (let k in D) {
+ if (k === '1') continue;
+ D[k] *= P['n'];
+
+ if (D[k] % P['d'] === C_ZERO) {
+ D[k] /= P['d'];
+ } else return null;
+ d *= BigInt(k) ** D[k];
+ }
+
+ if (P['s'] < C_ZERO) {
+ return newFraction(d, n);
+ }
+ return newFraction(n, d);
+ },
+
+ /**
+ * Calculates the logarithm of a fraction to a given rational base
+ *
+ * Ex: new Fraction(27, 8).log(9, 4) => 3/2
+ */
+ "log": function (a, b) {
+
+ parse(a, b);
+
+ if (this['s'] <= C_ZERO || P['s'] <= C_ZERO) return null;
+
+ const allPrimes = Object.create(null);
+
+ const baseFactors = factorize(P['n']);
+ const T1 = factorize(P['d']);
+
+ const numberFactors = factorize(this['n']);
+ const T2 = factorize(this['d']);
+
+ for (const prime in T1) {
+ baseFactors[prime] = (baseFactors[prime] || C_ZERO) - T1[prime];
+ }
+ for (const prime in T2) {
+ numberFactors[prime] = (numberFactors[prime] || C_ZERO) - T2[prime];
+ }
+
+ for (const prime in baseFactors) {
+ if (prime === '1') continue;
+ allPrimes[prime] = true;
+ }
+ for (const prime in numberFactors) {
+ if (prime === '1') continue;
+ allPrimes[prime] = true;
+ }
+
+ let retN = null;
+ let retD = null;
+
+ // Iterate over all unique primes to determine if a consistent ratio exists
+ for (const prime in allPrimes) {
+
+ const baseExponent = baseFactors[prime] || C_ZERO;
+ const numberExponent = numberFactors[prime] || C_ZERO;
+
+ if (baseExponent === C_ZERO) {
+ if (numberExponent !== C_ZERO) {
+ return null; // Logarithm cannot be expressed as a rational number
+ }
+ continue; // Skip this prime since both exponents are zero
+ }
+
+ // Calculate the ratio of exponents for this prime
+ let curN = numberExponent;
+ let curD = baseExponent;
+
+ // Simplify the current ratio
+ const gcdValue = gcd(curN, curD);
+ curN /= gcdValue;
+ curD /= gcdValue;
+
+ // Check if this is the first ratio; otherwise, ensure ratios are consistent
+ if (retN === null && retD === null) {
+ retN = curN;
+ retD = curD;
+ } else if (curN * retD !== retN * curD) {
+ return null; // Ratios do not match, logarithm cannot be rational
+ }
+ }
+
+ return retN !== null && retD !== null
+ ? newFraction(retN, retD)
+ : null;
+ },
+
+ /**
+ * Check if two rational numbers are the same
+ *
+ * Ex: new Fraction(19.6).equals([98, 5]);
+ **/
+ "equals": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is less than another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "lt": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] < P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is less than or equal another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "lte": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] <= P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is greater than another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "gt": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] > P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is greater than or equal another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "gte": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] >= P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Compare two rational numbers
+ * < 0 iff this < that
+ * > 0 iff this > that
+ * = 0 iff this = that
+ *
+ * Ex: new Fraction(19.6).compare([98, 5]);
+ **/
+ "compare": function (a, b) {
+
+ parse(a, b);
+ let t = this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"];
+
+ return (C_ZERO < t) - (t < C_ZERO);
+ },
+
+ /**
+ * Calculates the ceil of a rational number
+ *
+ * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
+ **/
+ "ceil": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+ (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Calculates the floor of a rational number
+ *
+ * Ex: new Fraction('4.(3)').floor() => (4 / 1)
+ **/
+ "floor": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) -
+ (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Rounds a rational numbers
+ *
+ * Ex: new Fraction('4.(3)').round() => (4 / 1)
+ **/
+ "round": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ /* Derivation:
+
+ s >= 0:
+ round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0
+ = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+ s < 0:
+ round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0
+ =-ifloor(n / d) - 2(n % d) > d ? 1 : 0
+
+ =>:
+
+ round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0)
+ where C = s >= 0 ? 1 : 0, to fix the >= for the positve case.
+ */
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+ this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Rounds a rational number to a multiple of another rational number
+ *
+ * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8
+ **/
+ "roundTo": function (a, b) {
+
+ /*
+ k * x/y ≤ a/b < (k+1) * x/y
+ ⇔ k ≤ a/b / (x/y) < (k+1)
+ ⇔ k = floor(a/b * y/x)
+ ⇔ k = floor((a * y) / (b * x))
+ */
+
+ parse(a, b);
+
+ const n = this['n'] * P['d'];
+ const d = this['d'] * P['n'];
+ const r = n % d;
+
+ // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+ let k = ifloor(n / d);
+ if (r + r >= d) {
+ k++;
+ }
+ return newFraction(this['s'] * k * P['n'], P['d']);
+ },
+
+ /**
+ * Check if two rational numbers are divisible
+ *
+ * Ex: new Fraction(19.6).divisible(1.5);
+ */
+ "divisible": function (a, b) {
+
+ parse(a, b);
+ if (P['n'] === C_ZERO) return false;
+ return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO;
+ },
+
+ /**
+ * Returns a decimal representation of the fraction
+ *
+ * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
+ **/
+ 'valueOf': function () {
+ //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) {
+ return Number(this['s'] * this['n']) / Number(this['d']);
+ //}
+ },
+
+ /**
+ * Creates a string representation of a fraction with all digits
+ *
+ * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
+ **/
+ 'toString': function (dec = 15) {
+
+ let N = this["n"];
+ let D = this["d"];
+
+ let cycLen = cycleLen(N, D); // Cycle length
+ let cycOff = cycleStart(N, D, cycLen); // Cycle start
+
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ // Append integer part
+ str += ifloor(N / D);
+
+ N %= D;
+ N *= C_TEN;
+
+ if (N)
+ str += ".";
+
+ if (cycLen) {
+
+ for (let i = cycOff; i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ str += "(";
+ for (let i = cycLen; i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ str += ")";
+ } else {
+ for (let i = dec; N && i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ }
+ return str;
+ },
+
+ /**
+ * Returns a string-fraction representation of a Fraction object
+ *
+ * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
+ **/
+ 'toFraction': function (showMixed = false) {
+
+ let n = this["n"];
+ let d = this["d"];
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ if (d === C_ONE) {
+ str += n;
+ } else {
+ const whole = ifloor(n / d);
+ if (showMixed && whole > C_ZERO) {
+ str += whole;
+ str += " ";
+ n %= d;
+ }
+
+ str += n;
+ str += '/';
+ str += d;
+ }
+ return str;
+ },
+
+ /**
+ * Returns a latex representation of a Fraction object
+ *
+ * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
+ **/
+ 'toLatex': function (showMixed = false) {
+
+ let n = this["n"];
+ let d = this["d"];
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ if (d === C_ONE) {
+ str += n;
+ } else {
+ const whole = ifloor(n / d);
+ if (showMixed && whole > C_ZERO) {
+ str += whole;
+ n %= d;
+ }
+
+ str += "\\frac{";
+ str += n;
+ str += '}{';
+ str += d;
+ str += '}';
+ }
+ return str;
+ },
+
+ /**
+ * Returns an array of continued fraction elements
+ *
+ * Ex: new Fraction("7/8").toContinued() => [0,1,7]
+ */
+ 'toContinued': function () {
+
+ let a = this['n'];
+ let b = this['d'];
+ const res = [];
+
+ while (b) {
+ res.push(ifloor(a / b));
+ const t = a % b;
+ a = b;
+ b = t;
+ }
+ return res;
+ },
+
+ "simplify": function (eps = 1e-3) {
+
+ // Continued fractions give best approximations for a max denominator,
+ // generally outperforming mediants in denominator–accuracy trade-offs.
+ // Semiconvergents can further reduce the denominator within tolerance.
+
+ const ieps = BigInt(Math.ceil(1 / eps));
+
+ const thisABS = this['abs']();
+ const cont = thisABS['toContinued']();
+
+ for (let i = 1; i < cont.length; i++) {
+
+ let s = newFraction(cont[i - 1], C_ONE);
+ for (let k = i - 2; k >= 0; k--) {
+ s = s['inverse']()['add'](cont[k]);
+ }
+
+ let t = s['sub'](thisABS);
+ if (t['n'] * ieps < t['d']) { // More robust than Math.abs(t.valueOf()) < eps
+ return s['mul'](this['s']);
+ }
+ }
+ return this;
+ }
+};
+
+Object.defineProperty(Fraction, "__esModule", { 'value': true });
+Fraction['default'] = Fraction;
+Fraction['Fraction'] = Fraction;
+module['exports'] = Fraction;
diff --git a/node_modules/fraction.js/dist/fraction.min.js b/node_modules/fraction.js/dist/fraction.min.js
new file mode 100644
index 0000000..97b02ee
--- /dev/null
+++ b/node_modules/fraction.js/dist/fraction.min.js
@@ -0,0 +1,21 @@
+/*
+Fraction.js v5.3.4 8/22/2025
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2025, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+'use strict';(function(F){function D(){return Error("Parameters must be integer")}function x(){return Error("Invalid argument")}function C(){return Error("Division by Zero")}function q(a,b){var d=g,c=h;let f=h;if(void 0!==a&&null!==a)if(void 0!==b){if("bigint"===typeof a)d=a;else{if(isNaN(a))throw x();if(0!==a%1)throw D();d=BigInt(a)}if("bigint"===typeof b)c=b;else{if(isNaN(b))throw x();if(0!==b%1)throw D();c=BigInt(b)}f=d*c}else if("object"===typeof a){if("d"in a&&"n"in a)d=BigInt(a.n),c=BigInt(a.d),
+"s"in a&&(d*=BigInt(a.s));else if(0 in a)d=BigInt(a[0]),1 in a&&(c=BigInt(a[1]));else if("bigint"===typeof a)d=a;else throw x();f=d*c}else if("number"===typeof a){if(isNaN(a))throw x();0>a&&(f=-h,a=-a);if(0===a%1)d=BigInt(a);else{b=1;var k=0,l=1,m=1;let r=1;1<=a&&(b=10**Math.floor(1+Math.log10(a)),a/=b);for(;1E7>=l&&1E7>=r;)if(c=(k+m)/(l+r),a===c){1E7>=l+r?(d=k+m,c=l+r):r>l?(d=m,c=r):(d=k,c=l);break}else a>c?(k+=m,l+=r):(m+=k,r+=l),1E7h&&(b[a]=(b[a]||g)+h);return b}function y(a,b){if(!a)return b;if(!b)return a;for(;;){a%=b;if(!a)return b;b%=a;if(!b)return a}}function v(a,b){q(a,b);if(this instanceof v)a=y(e.d,e.n),this.s=e.s,this.n=e.n/a,this.d=e.d/a;else return n(e.s*e.n,e.d)}"undefined"===typeof BigInt&&
+(BigInt=function(a){if(isNaN(a))throw Error("");return a});const g=BigInt(0),h=BigInt(1),p=BigInt(2),B=BigInt(3),z=BigInt(5),t=BigInt(10),e={s:h,n:g,d:h},G=[p*p,p,p*p,p,p*p,p*B,p,p*B];v.prototype={s:h,n:g,d:h,abs:function(){return n(this.n,this.d)},neg:function(){return n(-this.s*this.n,this.d)},add:function(a,b){q(a,b);return n(this.s*this.n*e.d+e.s*this.d*e.n,this.d*e.d)},sub:function(a,b){q(a,b);return n(this.s*this.n*e.d-e.s*this.d*e.n,this.d*e.d)},mul:function(a,b){q(a,b);return n(this.s*e.s*
+this.n*e.n,this.d*e.d)},div:function(a,b){q(a,b);return n(this.s*e.s*this.n*e.d,this.d*e.n)},clone:function(){return n(this.s*this.n,this.d)},mod:function(a,b){if(void 0===a)return n(this.s*this.n%this.d,h);q(a,b);if(g===e.n*this.d)throw C();return n(this.s*e.d*this.n%(e.n*this.d),e.d*this.d)},gcd:function(a,b){q(a,b);return n(y(e.n,this.n)*y(e.d,this.d),e.d*this.d)},lcm:function(a,b){q(a,b);return e.n===g&&this.n===g?n(g,h):n(e.n*this.n,y(e.n,this.n)*y(e.d,this.d))},inverse:function(){return n(this.s*
+this.d,this.n)},pow:function(a,b){q(a,b);if(e.d===h)return e.se.s*e.n*this.d},gte:function(a,b){q(a,b);return this.s*this.n*e.d>=e.s*e.n*this.d},compare:function(a,b){q(a,b);a=this.s*this.n*e.d-e.s*e.n*this.d;return(gg&&this.s>=g?h:g),a)},floor:function(a){a=t**BigInt(a||0);return n(u(this.s*a*this.n/
+this.d)-(a*this.n%this.d>g&&this.s=g?h:g)+a*this.n%this.d*p>this.d?h:g),a)},roundTo:function(a,b){q(a,b);var d=this.n*e.d;a=this.d*e.n;b=d%a;d=u(d/a);b+b>=a&&d++;return n(this.s*d*e.n,e.d)},divisible:function(a,b){q(a,b);return e.n===g?!1:this.n*e.d%(e.n*this.d)===g},valueOf:function(){return Number(this.s*this.n)/Number(this.d)},toString:function(a=15){let b=this.n,d=this.d;var c;a:{for(c=d;c%p===g;c/=
+p);for(;c%z===g;c/=z);if(c===h)c=g;else{for(var f=t%c,k=1;f!==h;k++)if(f=f*t%c,2E3g;k=k*k%d,l>>=h)l&h&&(m=m*k%d);k=m;for(l=0;300>l;l++){if(f===k){f=BigInt(l);break a}f=f*t%d;k=k*t%d}f=0}k=f;f=this.sg&&(c+=f,c+=" ",b%=d);c=c+b+"/"+d}return c},toLatex:function(a=!1){let b=this.n,d=this.d,c=this.sg&&(c+=f,b%=d);c=c+"\\frac{"+b+"}{"+d;c+="}"}return c},toContinued:function(){let a=this.n,b=this.d;const d=[];for(;b;){d.push(u(a/b));const c=a%b;a=b;b=c}return d},simplify:function(a=.001){a=BigInt(Math.ceil(1/a));const b=this.abs(),d=b.toContinued();for(let f=1;f , 1 => ]
+ * { n => , d => }
+ *
+ * Integer form
+ * - Single integer value as BigInt or Number
+ *
+ * Double form
+ * - Single double value as Number
+ *
+ * String form
+ * 123.456 - a simple double
+ * 123/456 - a string fraction
+ * 123.'456' - a double with repeating decimal places
+ * 123.(456) - synonym
+ * 123.45'6' - a double with repeating last place
+ * 123.45(6) - synonym
+ *
+ * Example:
+ * let f = new Fraction("9.4'31'");
+ * f.mul([-4, 3]).div(4.9);
+ *
+ */
+
+// Set Identity function to downgrade BigInt to Number if needed
+if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; };
+
+const C_ZERO = BigInt(0);
+const C_ONE = BigInt(1);
+const C_TWO = BigInt(2);
+const C_THREE = BigInt(3);
+const C_FIVE = BigInt(5);
+const C_TEN = BigInt(10);
+const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
+
+// Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
+// Example: 1/7 = 0.(142857) has 6 repeating decimal places.
+// If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
+const MAX_CYCLE_LEN = 2000;
+
+// Parsed data to avoid calling "new" all the time
+const P = {
+ "s": C_ONE,
+ "n": C_ZERO,
+ "d": C_ONE
+};
+
+function assign(n, s) {
+
+ try {
+ n = BigInt(n);
+ } catch (e) {
+ throw InvalidParameter();
+ }
+ return n * s;
+}
+
+function ifloor(x) {
+ return typeof x === 'bigint' ? x : Math.floor(x);
+}
+
+// Creates a new Fraction internally without the need of the bulky constructor
+function newFraction(n, d) {
+
+ if (d === C_ZERO) {
+ throw DivisionByZero();
+ }
+
+ const f = Object.create(Fraction.prototype);
+ f["s"] = n < C_ZERO ? -C_ONE : C_ONE;
+
+ n = n < C_ZERO ? -n : n;
+
+ const a = gcd(n, d);
+
+ f["n"] = n / a;
+ f["d"] = d / a;
+ return f;
+}
+
+const FACTORSTEPS = [C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO * C_THREE, C_TWO, C_TWO * C_THREE]; // repeats
+function factorize(n) {
+
+ const factors = Object.create(null);
+ if (n <= C_ONE) {
+ factors[n] = C_ONE;
+ return factors;
+ }
+
+ const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; };
+
+ while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; }
+ while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; }
+ while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; }
+
+ // 30-wheel trial division: test only residues coprime to 2*3*5
+ // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ...
+ for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) {
+ while (n % p === C_ZERO) { add(p); n /= p; }
+ p += FACTORSTEPS[si];
+ si = (si + 1) & 7; // fast modulo 8
+ }
+ if (n > C_ONE) add(n);
+ return factors;
+}
+
+const parse = function (p1, p2) {
+
+ let n = C_ZERO, d = C_ONE, s = C_ONE;
+
+ if (p1 === undefined || p1 === null) { // No argument
+ /* void */
+ } else if (p2 !== undefined) { // Two arguments
+
+ if (typeof p1 === "bigint") {
+ n = p1;
+ } else if (isNaN(p1)) {
+ throw InvalidParameter();
+ } else if (p1 % 1 !== 0) {
+ throw NonIntegerParameter();
+ } else {
+ n = BigInt(p1);
+ }
+
+ if (typeof p2 === "bigint") {
+ d = p2;
+ } else if (isNaN(p2)) {
+ throw InvalidParameter();
+ } else if (p2 % 1 !== 0) {
+ throw NonIntegerParameter();
+ } else {
+ d = BigInt(p2);
+ }
+
+ s = n * d;
+
+ } else if (typeof p1 === "object") {
+ if ("d" in p1 && "n" in p1) {
+ n = BigInt(p1["n"]);
+ d = BigInt(p1["d"]);
+ if ("s" in p1)
+ n *= BigInt(p1["s"]);
+ } else if (0 in p1) {
+ n = BigInt(p1[0]);
+ if (1 in p1)
+ d = BigInt(p1[1]);
+ } else if (typeof p1 === "bigint") {
+ n = p1;
+ } else {
+ throw InvalidParameter();
+ }
+ s = n * d;
+ } else if (typeof p1 === "number") {
+
+ if (isNaN(p1)) {
+ throw InvalidParameter();
+ }
+
+ if (p1 < 0) {
+ s = -C_ONE;
+ p1 = -p1;
+ }
+
+ if (p1 % 1 === 0) {
+ n = BigInt(p1);
+ } else {
+
+ let z = 1;
+
+ let A = 0, B = 1;
+ let C = 1, D = 1;
+
+ let N = 10000000;
+
+ if (p1 >= 1) {
+ z = 10 ** Math.floor(1 + Math.log10(p1));
+ p1 /= z;
+ }
+
+ // Using Farey Sequences
+
+ while (B <= N && D <= N) {
+ let M = (A + C) / (B + D);
+
+ if (p1 === M) {
+ if (B + D <= N) {
+ n = A + C;
+ d = B + D;
+ } else if (D > B) {
+ n = C;
+ d = D;
+ } else {
+ n = A;
+ d = B;
+ }
+ break;
+
+ } else {
+
+ if (p1 > M) {
+ A += C;
+ B += D;
+ } else {
+ C += A;
+ D += B;
+ }
+
+ if (B > N) {
+ n = C;
+ d = D;
+ } else {
+ n = A;
+ d = B;
+ }
+ }
+ }
+ n = BigInt(n) * BigInt(z);
+ d = BigInt(d);
+ }
+
+ } else if (typeof p1 === "string") {
+
+ let ndx = 0;
+
+ let v = C_ZERO, w = C_ZERO, x = C_ZERO, y = C_ONE, z = C_ONE;
+
+ let match = p1.replace(/_/g, '').match(/\d+|./g);
+
+ if (match === null)
+ throw InvalidParameter();
+
+ if (match[ndx] === '-') {// Check for minus sign at the beginning
+ s = -C_ONE;
+ ndx++;
+ } else if (match[ndx] === '+') {// Check for plus sign at the beginning
+ ndx++;
+ }
+
+ if (match.length === ndx + 1) { // Check if it's just a simple number "1234"
+ w = assign(match[ndx++], s);
+ } else if (match[ndx + 1] === '.' || match[ndx] === '.') { // Check if it's a decimal number
+
+ if (match[ndx] !== '.') { // Handle 0.5 and .5
+ v = assign(match[ndx++], s);
+ }
+ ndx++;
+
+ // Check for decimal places
+ if (ndx + 1 === match.length || match[ndx + 1] === '(' && match[ndx + 3] === ')' || match[ndx + 1] === "'" && match[ndx + 3] === "'") {
+ w = assign(match[ndx], s);
+ y = C_TEN ** BigInt(match[ndx].length);
+ ndx++;
+ }
+
+ // Check for repeating places
+ if (match[ndx] === '(' && match[ndx + 2] === ')' || match[ndx] === "'" && match[ndx + 2] === "'") {
+ x = assign(match[ndx + 1], s);
+ z = C_TEN ** BigInt(match[ndx + 1].length) - C_ONE;
+ ndx += 3;
+ }
+
+ } else if (match[ndx + 1] === '/' || match[ndx + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
+ w = assign(match[ndx], s);
+ y = assign(match[ndx + 2], C_ONE);
+ ndx += 3;
+ } else if (match[ndx + 3] === '/' && match[ndx + 1] === ' ') { // Check for a complex fraction "123 1/2"
+ v = assign(match[ndx], s);
+ w = assign(match[ndx + 2], s);
+ y = assign(match[ndx + 4], C_ONE);
+ ndx += 5;
+ }
+
+ if (match.length <= ndx) { // Check for more tokens on the stack
+ d = y * z;
+ s = /* void */
+ n = x + d * v + z * w;
+ } else {
+ throw InvalidParameter();
+ }
+
+ } else if (typeof p1 === "bigint") {
+ n = p1;
+ s = p1;
+ d = C_ONE;
+ } else {
+ throw InvalidParameter();
+ }
+
+ if (d === C_ZERO) {
+ throw DivisionByZero();
+ }
+
+ P["s"] = s < C_ZERO ? -C_ONE : C_ONE;
+ P["n"] = n < C_ZERO ? -n : n;
+ P["d"] = d < C_ZERO ? -d : d;
+};
+
+function modpow(b, e, m) {
+
+ let r = C_ONE;
+ for (; e > C_ZERO; b = (b * b) % m, e >>= C_ONE) {
+
+ if (e & C_ONE) {
+ r = (r * b) % m;
+ }
+ }
+ return r;
+}
+
+function cycleLen(n, d) {
+
+ for (; d % C_TWO === C_ZERO;
+ d /= C_TWO) {
+ }
+
+ for (; d % C_FIVE === C_ZERO;
+ d /= C_FIVE) {
+ }
+
+ if (d === C_ONE) // Catch non-cyclic numbers
+ return C_ZERO;
+
+ // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
+ // 10^(d-1) % d == 1
+ // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
+ // as we want to translate the numbers to strings.
+
+ let rem = C_TEN % d;
+ let t = 1;
+
+ for (; rem !== C_ONE; t++) {
+ rem = rem * C_TEN % d;
+
+ if (t > MAX_CYCLE_LEN)
+ return C_ZERO; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
+ }
+ return BigInt(t);
+}
+
+function cycleStart(n, d, len) {
+
+ let rem1 = C_ONE;
+ let rem2 = modpow(C_TEN, len, d);
+
+ for (let t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
+ // Solve 10^s == 10^(s+t) (mod d)
+
+ if (rem1 === rem2)
+ return BigInt(t);
+
+ rem1 = rem1 * C_TEN % d;
+ rem2 = rem2 * C_TEN % d;
+ }
+ return 0;
+}
+
+function gcd(a, b) {
+
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+
+ while (1) {
+ a %= b;
+ if (!a)
+ return b;
+ b %= a;
+ if (!b)
+ return a;
+ }
+}
+
+/**
+ * Module constructor
+ *
+ * @constructor
+ * @param {number|Fraction=} a
+ * @param {number=} b
+ */
+function Fraction(a, b) {
+
+ parse(a, b);
+
+ if (this instanceof Fraction) {
+ a = gcd(P["d"], P["n"]); // Abuse a
+ this["s"] = P["s"];
+ this["n"] = P["n"] / a;
+ this["d"] = P["d"] / a;
+ } else {
+ return newFraction(P['s'] * P['n'], P['d']);
+ }
+}
+
+const DivisionByZero = function () { return new Error("Division by Zero"); };
+const InvalidParameter = function () { return new Error("Invalid argument"); };
+const NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
+
+Fraction.prototype = {
+
+ "s": C_ONE,
+ "n": C_ZERO,
+ "d": C_ONE,
+
+ /**
+ * Calculates the absolute value
+ *
+ * Ex: new Fraction(-4).abs() => 4
+ **/
+ "abs": function () {
+
+ return newFraction(this["n"], this["d"]);
+ },
+
+ /**
+ * Inverts the sign of the current fraction
+ *
+ * Ex: new Fraction(-4).neg() => 4
+ **/
+ "neg": function () {
+
+ return newFraction(-this["s"] * this["n"], this["d"]);
+ },
+
+ /**
+ * Adds two rational numbers
+ *
+ * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
+ **/
+ "add": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Subtracts two rational numbers
+ *
+ * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
+ **/
+ "sub": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Multiplies two rational numbers
+ *
+ * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
+ **/
+ "mul": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * P["s"] * this["n"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Divides two rational numbers
+ *
+ * Ex: new Fraction("-17.(345)").inverse().div(3)
+ **/
+ "div": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * P["s"] * this["n"] * P["d"],
+ this["d"] * P["n"]
+ );
+ },
+
+ /**
+ * Clones the actual object
+ *
+ * Ex: new Fraction("-17.(345)").clone()
+ **/
+ "clone": function () {
+ return newFraction(this['s'] * this['n'], this['d']);
+ },
+
+ /**
+ * Calculates the modulo of two rational numbers - a more precise fmod
+ *
+ * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
+ * Ex: new Fraction(20, 10).mod().equals(0) ? "is Integer"
+ **/
+ "mod": function (a, b) {
+
+ if (a === undefined) {
+ return newFraction(this["s"] * this["n"] % this["d"], C_ONE);
+ }
+
+ parse(a, b);
+ if (C_ZERO === P["n"] * this["d"]) {
+ throw DivisionByZero();
+ }
+
+ /**
+ * I derived the rational modulo similar to the modulo for integers
+ *
+ * https://raw.org/book/analysis/rational-numbers/
+ *
+ * n1/d1 = (n2/d2) * q + r, where 0 ≤ r < n2/d2
+ * => d2 * n1 = n2 * d1 * q + d1 * d2 * r
+ * => r = (d2 * n1 - n2 * d1 * q) / (d1 * d2)
+ * = (d2 * n1 - n2 * d1 * floor((d2 * n1) / (n2 * d1))) / (d1 * d2)
+ * = ((d2 * n1) % (n2 * d1)) / (d1 * d2)
+ */
+ return newFraction(
+ this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]),
+ P["d"] * this["d"]);
+ },
+
+ /**
+ * Calculates the fractional gcd of two rational numbers
+ *
+ * Ex: new Fraction(5,8).gcd(3,7) => 1/56
+ */
+ "gcd": function (a, b) {
+
+ parse(a, b);
+
+ // https://raw.org/book/analysis/rational-numbers/
+ // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
+
+ return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]);
+ },
+
+ /**
+ * Calculates the fractional lcm of two rational numbers
+ *
+ * Ex: new Fraction(5,8).lcm(3,7) => 15
+ */
+ "lcm": function (a, b) {
+
+ parse(a, b);
+
+ // https://raw.org/book/analysis/rational-numbers/
+ // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
+
+ if (P["n"] === C_ZERO && this["n"] === C_ZERO) {
+ return newFraction(C_ZERO, C_ONE);
+ }
+ return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]));
+ },
+
+ /**
+ * Gets the inverse of the fraction, means numerator and denominator are exchanged
+ *
+ * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
+ **/
+ "inverse": function () {
+ return newFraction(this["s"] * this["d"], this["n"]);
+ },
+
+ /**
+ * Calculates the fraction to some integer exponent
+ *
+ * Ex: new Fraction(-1,2).pow(-3) => -8
+ */
+ "pow": function (a, b) {
+
+ parse(a, b);
+
+ // Trivial case when exp is an integer
+
+ if (P['d'] === C_ONE) {
+
+ if (P['s'] < C_ZERO) {
+ return newFraction((this['s'] * this["d"]) ** P['n'], this["n"] ** P['n']);
+ } else {
+ return newFraction((this['s'] * this["n"]) ** P['n'], this["d"] ** P['n']);
+ }
+ }
+
+ // Negative roots become complex
+ // (-a/b)^(c/d) = x
+ // ⇔ (-1)^(c/d) * (a/b)^(c/d) = x
+ // ⇔ (cos(pi) + i*sin(pi))^(c/d) * (a/b)^(c/d) = x
+ // ⇔ (cos(c*pi/d) + i*sin(c*pi/d)) * (a/b)^(c/d) = x # DeMoivre's formula
+ // From which follows that only for c=0 the root is non-complex
+ if (this['s'] < C_ZERO) return null;
+
+ // Now prime factor n and d
+ let N = factorize(this['n']);
+ let D = factorize(this['d']);
+
+ // Exponentiate and take root for n and d individually
+ let n = C_ONE;
+ let d = C_ONE;
+ for (let k in N) {
+ if (k === '1') continue;
+ if (k === '0') {
+ n = C_ZERO;
+ break;
+ }
+ N[k] *= P['n'];
+
+ if (N[k] % P['d'] === C_ZERO) {
+ N[k] /= P['d'];
+ } else return null;
+ n *= BigInt(k) ** N[k];
+ }
+
+ for (let k in D) {
+ if (k === '1') continue;
+ D[k] *= P['n'];
+
+ if (D[k] % P['d'] === C_ZERO) {
+ D[k] /= P['d'];
+ } else return null;
+ d *= BigInt(k) ** D[k];
+ }
+
+ if (P['s'] < C_ZERO) {
+ return newFraction(d, n);
+ }
+ return newFraction(n, d);
+ },
+
+ /**
+ * Calculates the logarithm of a fraction to a given rational base
+ *
+ * Ex: new Fraction(27, 8).log(9, 4) => 3/2
+ */
+ "log": function (a, b) {
+
+ parse(a, b);
+
+ if (this['s'] <= C_ZERO || P['s'] <= C_ZERO) return null;
+
+ const allPrimes = Object.create(null);
+
+ const baseFactors = factorize(P['n']);
+ const T1 = factorize(P['d']);
+
+ const numberFactors = factorize(this['n']);
+ const T2 = factorize(this['d']);
+
+ for (const prime in T1) {
+ baseFactors[prime] = (baseFactors[prime] || C_ZERO) - T1[prime];
+ }
+ for (const prime in T2) {
+ numberFactors[prime] = (numberFactors[prime] || C_ZERO) - T2[prime];
+ }
+
+ for (const prime in baseFactors) {
+ if (prime === '1') continue;
+ allPrimes[prime] = true;
+ }
+ for (const prime in numberFactors) {
+ if (prime === '1') continue;
+ allPrimes[prime] = true;
+ }
+
+ let retN = null;
+ let retD = null;
+
+ // Iterate over all unique primes to determine if a consistent ratio exists
+ for (const prime in allPrimes) {
+
+ const baseExponent = baseFactors[prime] || C_ZERO;
+ const numberExponent = numberFactors[prime] || C_ZERO;
+
+ if (baseExponent === C_ZERO) {
+ if (numberExponent !== C_ZERO) {
+ return null; // Logarithm cannot be expressed as a rational number
+ }
+ continue; // Skip this prime since both exponents are zero
+ }
+
+ // Calculate the ratio of exponents for this prime
+ let curN = numberExponent;
+ let curD = baseExponent;
+
+ // Simplify the current ratio
+ const gcdValue = gcd(curN, curD);
+ curN /= gcdValue;
+ curD /= gcdValue;
+
+ // Check if this is the first ratio; otherwise, ensure ratios are consistent
+ if (retN === null && retD === null) {
+ retN = curN;
+ retD = curD;
+ } else if (curN * retD !== retN * curD) {
+ return null; // Ratios do not match, logarithm cannot be rational
+ }
+ }
+
+ return retN !== null && retD !== null
+ ? newFraction(retN, retD)
+ : null;
+ },
+
+ /**
+ * Check if two rational numbers are the same
+ *
+ * Ex: new Fraction(19.6).equals([98, 5]);
+ **/
+ "equals": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is less than another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "lt": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] < P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is less than or equal another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "lte": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] <= P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is greater than another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "gt": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] > P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is greater than or equal another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "gte": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] >= P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Compare two rational numbers
+ * < 0 iff this < that
+ * > 0 iff this > that
+ * = 0 iff this = that
+ *
+ * Ex: new Fraction(19.6).compare([98, 5]);
+ **/
+ "compare": function (a, b) {
+
+ parse(a, b);
+ let t = this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"];
+
+ return (C_ZERO < t) - (t < C_ZERO);
+ },
+
+ /**
+ * Calculates the ceil of a rational number
+ *
+ * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
+ **/
+ "ceil": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+ (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Calculates the floor of a rational number
+ *
+ * Ex: new Fraction('4.(3)').floor() => (4 / 1)
+ **/
+ "floor": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) -
+ (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Rounds a rational numbers
+ *
+ * Ex: new Fraction('4.(3)').round() => (4 / 1)
+ **/
+ "round": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ /* Derivation:
+
+ s >= 0:
+ round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0
+ = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+ s < 0:
+ round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0
+ =-ifloor(n / d) - 2(n % d) > d ? 1 : 0
+
+ =>:
+
+ round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0)
+ where C = s >= 0 ? 1 : 0, to fix the >= for the positve case.
+ */
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+ this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Rounds a rational number to a multiple of another rational number
+ *
+ * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8
+ **/
+ "roundTo": function (a, b) {
+
+ /*
+ k * x/y ≤ a/b < (k+1) * x/y
+ ⇔ k ≤ a/b / (x/y) < (k+1)
+ ⇔ k = floor(a/b * y/x)
+ ⇔ k = floor((a * y) / (b * x))
+ */
+
+ parse(a, b);
+
+ const n = this['n'] * P['d'];
+ const d = this['d'] * P['n'];
+ const r = n % d;
+
+ // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+ let k = ifloor(n / d);
+ if (r + r >= d) {
+ k++;
+ }
+ return newFraction(this['s'] * k * P['n'], P['d']);
+ },
+
+ /**
+ * Check if two rational numbers are divisible
+ *
+ * Ex: new Fraction(19.6).divisible(1.5);
+ */
+ "divisible": function (a, b) {
+
+ parse(a, b);
+ if (P['n'] === C_ZERO) return false;
+ return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO;
+ },
+
+ /**
+ * Returns a decimal representation of the fraction
+ *
+ * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
+ **/
+ 'valueOf': function () {
+ //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) {
+ return Number(this['s'] * this['n']) / Number(this['d']);
+ //}
+ },
+
+ /**
+ * Creates a string representation of a fraction with all digits
+ *
+ * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
+ **/
+ 'toString': function (dec = 15) {
+
+ let N = this["n"];
+ let D = this["d"];
+
+ let cycLen = cycleLen(N, D); // Cycle length
+ let cycOff = cycleStart(N, D, cycLen); // Cycle start
+
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ // Append integer part
+ str += ifloor(N / D);
+
+ N %= D;
+ N *= C_TEN;
+
+ if (N)
+ str += ".";
+
+ if (cycLen) {
+
+ for (let i = cycOff; i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ str += "(";
+ for (let i = cycLen; i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ str += ")";
+ } else {
+ for (let i = dec; N && i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ }
+ return str;
+ },
+
+ /**
+ * Returns a string-fraction representation of a Fraction object
+ *
+ * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
+ **/
+ 'toFraction': function (showMixed = false) {
+
+ let n = this["n"];
+ let d = this["d"];
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ if (d === C_ONE) {
+ str += n;
+ } else {
+ const whole = ifloor(n / d);
+ if (showMixed && whole > C_ZERO) {
+ str += whole;
+ str += " ";
+ n %= d;
+ }
+
+ str += n;
+ str += '/';
+ str += d;
+ }
+ return str;
+ },
+
+ /**
+ * Returns a latex representation of a Fraction object
+ *
+ * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
+ **/
+ 'toLatex': function (showMixed = false) {
+
+ let n = this["n"];
+ let d = this["d"];
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ if (d === C_ONE) {
+ str += n;
+ } else {
+ const whole = ifloor(n / d);
+ if (showMixed && whole > C_ZERO) {
+ str += whole;
+ n %= d;
+ }
+
+ str += "\\frac{";
+ str += n;
+ str += '}{';
+ str += d;
+ str += '}';
+ }
+ return str;
+ },
+
+ /**
+ * Returns an array of continued fraction elements
+ *
+ * Ex: new Fraction("7/8").toContinued() => [0,1,7]
+ */
+ 'toContinued': function () {
+
+ let a = this['n'];
+ let b = this['d'];
+ const res = [];
+
+ while (b) {
+ res.push(ifloor(a / b));
+ const t = a % b;
+ a = b;
+ b = t;
+ }
+ return res;
+ },
+
+ "simplify": function (eps = 1e-3) {
+
+ // Continued fractions give best approximations for a max denominator,
+ // generally outperforming mediants in denominator–accuracy trade-offs.
+ // Semiconvergents can further reduce the denominator within tolerance.
+
+ const ieps = BigInt(Math.ceil(1 / eps));
+
+ const thisABS = this['abs']();
+ const cont = thisABS['toContinued']();
+
+ for (let i = 1; i < cont.length; i++) {
+
+ let s = newFraction(cont[i - 1], C_ONE);
+ for (let k = i - 2; k >= 0; k--) {
+ s = s['inverse']()['add'](cont[k]);
+ }
+
+ let t = s['sub'](thisABS);
+ if (t['n'] * ieps < t['d']) { // More robust than Math.abs(t.valueOf()) < eps
+ return s['mul'](this['s']);
+ }
+ }
+ return this;
+ }
+};
+export {
+ Fraction as default, Fraction
+};
diff --git a/node_modules/fraction.js/examples/angles.js b/node_modules/fraction.js/examples/angles.js
new file mode 100644
index 0000000..436947e
--- /dev/null
+++ b/node_modules/fraction.js/examples/angles.js
@@ -0,0 +1,26 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+
+// This example generates a list of angles with human readable radians
+
+var Fraction = require('fraction.js');
+
+var tab = [];
+for (var d = 1; d <= 360; d++) {
+
+ var pi = Fraction(2, 360).mul(d);
+ var tau = Fraction(1, 360).mul(d);
+
+ if (pi.d <= 6n && pi.d != 5n)
+ tab.push([
+ d,
+ pi.toFraction() + "pi",
+ tau.toFraction() + "tau"]);
+}
+
+console.table(tab);
diff --git a/node_modules/fraction.js/examples/approx.js b/node_modules/fraction.js/examples/approx.js
new file mode 100644
index 0000000..36aa030
--- /dev/null
+++ b/node_modules/fraction.js/examples/approx.js
@@ -0,0 +1,54 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+// Another rational approximation, not using Farey Sequences but Binary Search using the mediant
+function approximate(p, precision) {
+
+ var num1 = Math.floor(p);
+ var den1 = 1;
+
+ var num2 = num1 + 1;
+ var den2 = 1;
+
+ if (p !== num1) {
+
+ while (den1 <= precision && den2 <= precision) {
+
+ var m = (num1 + num2) / (den1 + den2);
+
+ if (p === m) {
+
+ if (den1 + den2 <= precision) {
+ den1 += den2;
+ num1 += num2;
+ den2 = precision + 1;
+ } else if (den1 > den2) {
+ den2 = precision + 1;
+ } else {
+ den1 = precision + 1;
+ }
+ break;
+
+ } else if (p < m) {
+ num2 += num1;
+ den2 += den1;
+ } else {
+ num1 += num2;
+ den1 += den2;
+ }
+ }
+ }
+
+ if (den1 > precision) {
+ den1 = den2;
+ num1 = num2;
+ }
+ return new Fraction(num1, den1);
+}
+
diff --git a/node_modules/fraction.js/examples/egyptian.js b/node_modules/fraction.js/examples/egyptian.js
new file mode 100644
index 0000000..66fc209
--- /dev/null
+++ b/node_modules/fraction.js/examples/egyptian.js
@@ -0,0 +1,24 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+// Based on http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fractions/egyptian.html
+function egyptian(a, b) {
+
+ var res = [];
+
+ do {
+ var t = Math.ceil(b / a);
+ var x = new Fraction(a, b).sub(1, t);
+ res.push(t);
+ a = Number(x.n);
+ b = Number(x.d);
+ } while (a !== 0n);
+ return res;
+}
+console.log("1 / " + egyptian(521, 1050).join(" + 1 / "));
diff --git a/node_modules/fraction.js/examples/hesse-convergence.js b/node_modules/fraction.js/examples/hesse-convergence.js
new file mode 100644
index 0000000..c33a58b
--- /dev/null
+++ b/node_modules/fraction.js/examples/hesse-convergence.js
@@ -0,0 +1,111 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+/*
+We have the polynom f(x) = 1/3x_1^2 + x_2^2 + x_1 * x_2 + 3
+
+The gradient of f(x):
+
+grad(x) = | x_1^2+x_2 |
+ | 2x_2+x_1 |
+
+And thus the Hesse-Matrix H:
+| 2x_1 1 |
+| 1 2 |
+
+The inverse Hesse-Matrix H^-1 is
+| -2 / (1-4x_1) 1 / (1 - 4x_1) |
+| 1 / (1 - 4x_1) -2x_1 / (1 - 4x_1) |
+
+We now want to find lim ->oo x[n], with the starting element of (3 2)^T
+
+*/
+
+// Get the Hesse Matrix
+function H(x) {
+
+ var z = Fraction(1).sub(Fraction(4).mul(x[0]));
+
+ return [
+ Fraction(-2).div(z),
+ Fraction(1).div(z),
+ Fraction(1).div(z),
+ Fraction(-2).mul(x[0]).div(z),
+ ];
+}
+
+// Get the gradient of f(x)
+function grad(x) {
+
+ return [
+ Fraction(x[0]).mul(x[0]).add(x[1]),
+ Fraction(2).mul(x[1]).add(x[0])
+ ];
+}
+
+// A simple matrix multiplication helper
+function matrMult(m, v) {
+
+ return [
+ Fraction(m[0]).mul(v[0]).add(Fraction(m[1]).mul(v[1])),
+ Fraction(m[2]).mul(v[0]).add(Fraction(m[3]).mul(v[1]))
+ ];
+}
+
+// A simple vector subtraction helper
+function vecSub(a, b) {
+
+ return [
+ Fraction(a[0]).sub(b[0]),
+ Fraction(a[1]).sub(b[1])
+ ];
+}
+
+// Main function, gets a vector and the actual index
+function run(V, j) {
+
+ var t = H(V);
+ //console.log("H(X)");
+ for (var i in t) {
+
+ // console.log(t[i].toFraction());
+ }
+
+ var s = grad(V);
+ //console.log("vf(X)");
+ for (var i in s) {
+
+ // console.log(s[i].toFraction());
+ }
+
+ //console.log("multiplication");
+ var r = matrMult(t, s);
+ for (var i in r) {
+
+ // console.log(r[i].toFraction());
+ }
+
+ var R = (vecSub(V, r));
+
+ console.log("X" + j);
+ console.log(R[0].toFraction(), "= " + R[0].valueOf());
+ console.log(R[1].toFraction(), "= " + R[1].valueOf());
+ console.log("\n");
+
+ return R;
+}
+
+
+// Set the starting vector
+var v = [3, 2];
+
+for (var i = 0; i < 15; i++) {
+
+ v = run(v, i);
+}
diff --git a/node_modules/fraction.js/examples/integrate.js b/node_modules/fraction.js/examples/integrate.js
new file mode 100644
index 0000000..6376aed
--- /dev/null
+++ b/node_modules/fraction.js/examples/integrate.js
@@ -0,0 +1,67 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+// NOTE: This is a nice example, but a stable version of this is served with Polynomial.js:
+// https://github.com/rawify/Polynomial.js
+
+function integrate(poly) {
+
+ poly = poly.replace(/\s+/g, "");
+
+ var regex = /(\([+-]?[0-9/]+\)|[+-]?[0-9/]+)x(?:\^(\([+-]?[0-9/]+\)|[+-]?[0-9]+))?/g;
+ var arr;
+ var res = {};
+ while (null !== (arr = regex.exec(poly))) {
+
+ var a = (arr[1] || "1").replace("(", "").replace(")", "").split("/");
+ var b = (arr[2] || "1").replace("(", "").replace(")", "").split("/");
+
+ var exp = new Fraction(b).add(1);
+ var key = "" + exp;
+
+ if (res[key] !== undefined) {
+ res[key] = { x: new Fraction(a).div(exp).add(res[key].x), e: exp };
+ } else {
+ res[key] = { x: new Fraction(a).div(exp), e: exp };
+ }
+ }
+
+ var str = "";
+ var c = 0;
+ for (var i in res) {
+ if (res[i].x.s !== -1n && c > 0) {
+ str += "+";
+ } else if (res[i].x.s === -1n) {
+ str += "-";
+ }
+ if (res[i].x.n !== res[i].x.d) {
+ if (res[i].x.d !== 1n) {
+ str += res[i].x.n + "/" + res[i].x.d;
+ } else {
+ str += res[i].x.n;
+ }
+ }
+ str += "x";
+ if (res[i].e.n !== res[i].e.d) {
+ str += "^";
+ if (res[i].e.d !== 1n) {
+ str += "(" + res[i].e.n + "/" + res[i].e.d + ")";
+ } else {
+ str += res[i].e.n;
+ }
+ }
+ c++;
+ }
+ return str;
+}
+
+var poly = "-2/3x^3-2x^2+3x+8x^3-1/3x^(4/8)";
+
+console.log("f(x): " + poly);
+console.log("F(x): " + integrate(poly));
diff --git a/node_modules/fraction.js/examples/ratio-chain.js b/node_modules/fraction.js/examples/ratio-chain.js
new file mode 100644
index 0000000..fab7876
--- /dev/null
+++ b/node_modules/fraction.js/examples/ratio-chain.js
@@ -0,0 +1,24 @@
+/*
+Given the ratio a : b : c = 2 : 3 : 4
+What is c, given a = 40?
+
+A general ratio chain is a_1 : a_2 : a_3 : ... : a_n = r_1 : r2 : r_3 : ... : r_n.
+Now each term can be expressed as a_i = r_i * x for some unknown proportional constant x.
+If a_k is known it follows that x = a_k / r_k. Substituting x into the first equation yields
+a_i = r_i / r_k * a_k.
+
+Given an array r and a given value a_k, the following function calculates all a_i:
+*/
+
+function calculateRatios(r, a_k, k) {
+ const x = Fraction(a_k).div(r[k]);
+ return r.map(r_i => x.mul(r_i));
+}
+
+// Example usage:
+const r = [2, 3, 4]; // Ratio array representing a : b : c = 2 : 3 : 4
+const a_k = 40; // Given value of a (corresponding to r[0])
+const k = 0; // Index of the known value (a corresponds to r[0])
+
+const result = calculateRatios(r, a_k, k);
+console.log(result); // Output: [40, 60, 80]
diff --git a/node_modules/fraction.js/examples/rational-pow.js b/node_modules/fraction.js/examples/rational-pow.js
new file mode 100644
index 0000000..1268e27
--- /dev/null
+++ b/node_modules/fraction.js/examples/rational-pow.js
@@ -0,0 +1,29 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+// Calculates (a/b)^(c/d) if result is rational
+// Derivation: https://raw.org/book/analysis/rational-numbers/
+function root(a, b, c, d) {
+
+ // Initial estimate
+ let x = Fraction(100 * (Math.floor(Math.pow(a / b, c / d)) || 1), 100);
+ const abc = Fraction(a, b).pow(c);
+
+ for (let i = 0; i < 30; i++) {
+ const n = abc.mul(x.pow(1 - d)).sub(x).div(d).add(x)
+
+ if (x.n === n.n && x.d === n.d) {
+ return n;
+ }
+ x = n;
+ }
+ return null;
+}
+
+root(18, 2, 1, 2); // 3/1
diff --git a/node_modules/fraction.js/examples/tape-measure.js b/node_modules/fraction.js/examples/tape-measure.js
new file mode 100644
index 0000000..14ec524
--- /dev/null
+++ b/node_modules/fraction.js/examples/tape-measure.js
@@ -0,0 +1,16 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+const Fraction = require('fraction.js');
+
+function closestTapeMeasure(frac) {
+
+ // A tape measure is usually divided in parts of 1/16
+
+ return Fraction(frac).roundTo("1/16");
+}
+console.log(closestTapeMeasure("1/3")); // 5/16
diff --git a/node_modules/fraction.js/examples/toFraction.js b/node_modules/fraction.js/examples/toFraction.js
new file mode 100644
index 0000000..f935e47
--- /dev/null
+++ b/node_modules/fraction.js/examples/toFraction.js
@@ -0,0 +1,35 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+
+const Fraction = require('fraction.js');
+
+function toFraction(frac) {
+
+ var map = {
+ '1:4': "¼",
+ '1:2': "½",
+ '3:4': "¾",
+ '1:7': "⅐",
+ '1:9': "⅑",
+ '1:10': "⅒",
+ '1:3': "⅓",
+ '2:3': "⅔",
+ '1:5': "⅕",
+ '2:5': "⅖",
+ '3:5': "⅗",
+ '4:5': "⅘",
+ '1:6': "⅙",
+ '5:6': "⅚",
+ '1:8': "⅛",
+ '3:8': "⅜",
+ '5:8': "⅝",
+ '7:8': "⅞"
+ };
+ return map[frac.n + ":" + frac.d] || frac.toFraction(false);
+}
+console.log(toFraction(Fraction(0.25))); // ¼
diff --git a/node_modules/fraction.js/examples/valueOfPi.js b/node_modules/fraction.js/examples/valueOfPi.js
new file mode 100644
index 0000000..8fc877e
--- /dev/null
+++ b/node_modules/fraction.js/examples/valueOfPi.js
@@ -0,0 +1,42 @@
+/*
+Fraction.js v5.0.0 10/1/2024
+https://raw.org/article/rational-numbers-in-javascript/
+
+Copyright (c) 2024, Robert Eisele (https://raw.org/)
+Licensed under the MIT license.
+*/
+
+var Fraction = require("fraction.js")
+
+function valueOfPi(val) {
+
+ let minLen = Infinity, minI = 0, min = null;
+ const choose = [val, val * Math.PI, val / Math.PI];
+ for (let i = 0; i < choose.length; i++) {
+ let el = new Fraction(choose[i]).simplify(1e-13);
+ let len = Math.log(Number(el.n) + 1) + Math.log(Number(el.d));
+ if (len < minLen) {
+ minLen = len;
+ minI = i;
+ min = el;
+ }
+ }
+
+ if (minI == 2) {
+ return min.toFraction().replace(/(\d+)(\/\d+)?/, (_, p, q) =>
+ (p == "1" ? "" : p) + "π" + (q || ""));
+ }
+
+ if (minI == 1) {
+ return min.toFraction().replace(/(\d+)(\/\d+)?/, (_, p, q) =>
+ p + (!q ? "/π" : "/(" + q.slice(1) + "π)"));
+ }
+ return min.toFraction();
+}
+
+console.log(valueOfPi(-3)); // -3
+console.log(valueOfPi(4 * Math.PI)); // 4π
+console.log(valueOfPi(3.14)); // 157/50
+console.log(valueOfPi(3 / 2 * Math.PI)); // 3π/2
+console.log(valueOfPi(Math.PI / 2)); // π/2
+console.log(valueOfPi(-1 / (2 * Math.PI))); // -1/(2π)
diff --git a/node_modules/fraction.js/fraction.d.mts b/node_modules/fraction.js/fraction.d.mts
new file mode 100644
index 0000000..0604ad7
--- /dev/null
+++ b/node_modules/fraction.js/fraction.d.mts
@@ -0,0 +1,79 @@
+/**
+ * Interface representing a fraction with numerator and denominator.
+ */
+export interface NumeratorDenominator {
+ n: number | bigint;
+ d: number | bigint;
+}
+
+/**
+ * Type for handling multiple types of input for Fraction operations.
+ */
+export type FractionInput =
+ | Fraction
+ | number
+ | bigint
+ | string
+ | [number | bigint | string, number | bigint | string]
+ | NumeratorDenominator;
+
+/**
+ * Function signature for Fraction operations like add, sub, mul, etc.
+ */
+export type FractionParam = {
+ (numerator: number | bigint, denominator: number | bigint): Fraction;
+ (num: FractionInput): Fraction;
+};
+
+/**
+ * Fraction class representing a rational number with numerator and denominator.
+ */
+declare class Fraction {
+ constructor();
+ constructor(num: FractionInput);
+ constructor(numerator: number | bigint, denominator: number | bigint);
+
+ s: bigint;
+ n: bigint;
+ d: bigint;
+
+ abs(): Fraction;
+ neg(): Fraction;
+
+ add: FractionParam;
+ sub: FractionParam;
+ mul: FractionParam;
+ div: FractionParam;
+ pow: FractionParam;
+ log: FractionParam;
+ gcd: FractionParam;
+ lcm: FractionParam;
+
+ mod(): Fraction;
+ mod(num: FractionInput): Fraction;
+
+ ceil(places?: number): Fraction;
+ floor(places?: number): Fraction;
+ round(places?: number): Fraction;
+ roundTo: FractionParam;
+
+ inverse(): Fraction;
+ simplify(eps?: number): Fraction;
+
+ equals(num: FractionInput): boolean;
+ lt(num: FractionInput): boolean;
+ lte(num: FractionInput): boolean;
+ gt(num: FractionInput): boolean;
+ gte(num: FractionInput): boolean;
+ compare(num: FractionInput): number;
+ divisible(num: FractionInput): boolean;
+
+ valueOf(): number;
+ toString(decimalPlaces?: number): string;
+ toLatex(showMixed?: boolean): string;
+ toFraction(showMixed?: boolean): string;
+ toContinued(): bigint[];
+ clone(): Fraction;
+}
+
+export { Fraction as default, Fraction };
\ No newline at end of file
diff --git a/node_modules/fraction.js/fraction.d.ts b/node_modules/fraction.js/fraction.d.ts
new file mode 100644
index 0000000..97222b9
--- /dev/null
+++ b/node_modules/fraction.js/fraction.d.ts
@@ -0,0 +1,79 @@
+declare class Fraction {
+ constructor();
+ constructor(num: Fraction.FractionInput);
+ constructor(numerator: number | bigint, denominator: number | bigint);
+
+ s: bigint;
+ n: bigint;
+ d: bigint;
+
+ abs(): Fraction;
+ neg(): Fraction;
+
+ add: Fraction.FractionParam;
+ sub: Fraction.FractionParam;
+ mul: Fraction.FractionParam;
+ div: Fraction.FractionParam;
+ pow: Fraction.FractionParam;
+ log: Fraction.FractionParam;
+ gcd: Fraction.FractionParam;
+ lcm: Fraction.FractionParam;
+
+ mod(): Fraction;
+ mod(num: Fraction.FractionInput): Fraction;
+
+ ceil(places?: number): Fraction;
+ floor(places?: number): Fraction;
+ round(places?: number): Fraction;
+ roundTo: Fraction.FractionParam;
+
+ inverse(): Fraction;
+ simplify(eps?: number): Fraction;
+
+ equals(num: Fraction.FractionInput): boolean;
+ lt(num: Fraction.FractionInput): boolean;
+ lte(num: Fraction.FractionInput): boolean;
+ gt(num: Fraction.FractionInput): boolean;
+ gte(num: Fraction.FractionInput): boolean;
+ compare(num: Fraction.FractionInput): number;
+ divisible(num: Fraction.FractionInput): boolean;
+
+ valueOf(): number;
+ toString(decimalPlaces?: number): string;
+ toLatex(showMixed?: boolean): string;
+ toFraction(showMixed?: boolean): string;
+ toContinued(): bigint[];
+ clone(): Fraction;
+
+ static default: typeof Fraction;
+ static Fraction: typeof Fraction;
+}
+
+declare namespace Fraction {
+ interface NumeratorDenominator { n: number | bigint; d: number | bigint; }
+ type FractionInput =
+ | Fraction
+ | number
+ | bigint
+ | string
+ | [number | bigint | string, number | bigint | string]
+ | NumeratorDenominator;
+
+ type FractionParam = {
+ (numerator: number | bigint, denominator: number | bigint): Fraction;
+ (num: FractionInput): Fraction;
+ };
+}
+
+/**
+ * Export matches CJS runtime:
+ * module.exports = Fraction;
+ * module.exports.default = Fraction;
+ * module.exports.Fraction = Fraction;
+ */
+declare const FractionExport: typeof Fraction & {
+ default: typeof Fraction;
+ Fraction: typeof Fraction;
+};
+
+export = FractionExport;
\ No newline at end of file
diff --git a/node_modules/fraction.js/package.json b/node_modules/fraction.js/package.json
new file mode 100644
index 0000000..03f7986
--- /dev/null
+++ b/node_modules/fraction.js/package.json
@@ -0,0 +1,81 @@
+{
+ "name": "fraction.js",
+ "title": "Fraction.js",
+ "version": "5.3.4",
+ "description": "The RAW rational numbers library",
+ "homepage": "https://raw.org/article/rational-numbers-in-javascript/",
+ "bugs": "https://github.com/rawify/Fraction.js/issues",
+ "keywords": [
+ "math",
+ "numbers",
+ "parser",
+ "ratio",
+ "fraction",
+ "fractions",
+ "rational",
+ "rationals",
+ "rational numbers",
+ "bigint",
+ "arbitrary precision",
+ "mixed numbers",
+ "decimal",
+ "numerator",
+ "denominator",
+ "simplification"
+ ],
+ "private": false,
+ "main": "./dist/fraction.js",
+ "module": "./dist/fraction.mjs",
+ "browser": "./dist/fraction.min.js",
+ "unpkg": "./dist/fraction.min.js",
+ "types": "./fraction.d.mts",
+ "exports": {
+ ".": {
+ "types": {
+ "import": "./fraction.d.mts",
+ "require": "./fraction.d.ts"
+ },
+ "import": "./dist/fraction.mjs",
+ "require": "./dist/fraction.js",
+ "browser": "./dist/fraction.min.js"
+ },
+ "./package.json": "./package.json"
+ },
+ "typesVersions": {
+ "<4.7": {
+ "*": [
+ "fraction.d.ts"
+ ]
+ }
+ },
+ "sideEffects": false,
+ "repository": {
+ "type": "git",
+ "url": "git+ssh://git@github.com/rawify/Fraction.js.git"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ },
+ "author": {
+ "name": "Robert Eisele",
+ "email": "robert@raw.org",
+ "url": "https://raw.org/"
+ },
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "directories": {
+ "example": "examples",
+ "test": "tests"
+ },
+ "scripts": {
+ "build": "crude-build Fraction",
+ "test": "mocha tests/*.js"
+ },
+ "devDependencies": {
+ "crude-build": "^0.1.2",
+ "mocha": "*"
+ }
+}
\ No newline at end of file
diff --git a/node_modules/fraction.js/src/fraction.js b/node_modules/fraction.js/src/fraction.js
new file mode 100644
index 0000000..4292c48
--- /dev/null
+++ b/node_modules/fraction.js/src/fraction.js
@@ -0,0 +1,1046 @@
+/**
+ * @license Fraction.js v5.3.4 8/22/2025
+ * https://raw.org/article/rational-numbers-in-javascript/
+ *
+ * Copyright (c) 2025, Robert Eisele (https://raw.org/)
+ * Licensed under the MIT license.
+ **/
+
+/**
+ *
+ * This class offers the possibility to calculate fractions.
+ * You can pass a fraction in different formats. Either as array, as double, as string or as an integer.
+ *
+ * Array/Object form
+ * [ 0 => , 1 => ]
+ * { n => , d => }
+ *
+ * Integer form
+ * - Single integer value as BigInt or Number
+ *
+ * Double form
+ * - Single double value as Number
+ *
+ * String form
+ * 123.456 - a simple double
+ * 123/456 - a string fraction
+ * 123.'456' - a double with repeating decimal places
+ * 123.(456) - synonym
+ * 123.45'6' - a double with repeating last place
+ * 123.45(6) - synonym
+ *
+ * Example:
+ * let f = new Fraction("9.4'31'");
+ * f.mul([-4, 3]).div(4.9);
+ *
+ */
+
+// Set Identity function to downgrade BigInt to Number if needed
+if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; };
+
+const C_ZERO = BigInt(0);
+const C_ONE = BigInt(1);
+const C_TWO = BigInt(2);
+const C_THREE = BigInt(3);
+const C_FIVE = BigInt(5);
+const C_TEN = BigInt(10);
+const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
+
+// Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
+// Example: 1/7 = 0.(142857) has 6 repeating decimal places.
+// If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
+const MAX_CYCLE_LEN = 2000;
+
+// Parsed data to avoid calling "new" all the time
+const P = {
+ "s": C_ONE,
+ "n": C_ZERO,
+ "d": C_ONE
+};
+
+function assign(n, s) {
+
+ try {
+ n = BigInt(n);
+ } catch (e) {
+ throw InvalidParameter();
+ }
+ return n * s;
+}
+
+function ifloor(x) {
+ return typeof x === 'bigint' ? x : Math.floor(x);
+}
+
+// Creates a new Fraction internally without the need of the bulky constructor
+function newFraction(n, d) {
+
+ if (d === C_ZERO) {
+ throw DivisionByZero();
+ }
+
+ const f = Object.create(Fraction.prototype);
+ f["s"] = n < C_ZERO ? -C_ONE : C_ONE;
+
+ n = n < C_ZERO ? -n : n;
+
+ const a = gcd(n, d);
+
+ f["n"] = n / a;
+ f["d"] = d / a;
+ return f;
+}
+
+const FACTORSTEPS = [C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO * C_THREE, C_TWO, C_TWO * C_THREE]; // repeats
+function factorize(n) {
+
+ const factors = Object.create(null);
+ if (n <= C_ONE) {
+ factors[n] = C_ONE;
+ return factors;
+ }
+
+ const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; };
+
+ while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; }
+ while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; }
+ while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; }
+
+ // 30-wheel trial division: test only residues coprime to 2*3*5
+ // Residue step pattern after 5: 7,11,13,17,19,23,29,31, ...
+ for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) {
+ while (n % p === C_ZERO) { add(p); n /= p; }
+ p += FACTORSTEPS[si];
+ si = (si + 1) & 7; // fast modulo 8
+ }
+ if (n > C_ONE) add(n);
+ return factors;
+}
+
+const parse = function (p1, p2) {
+
+ let n = C_ZERO, d = C_ONE, s = C_ONE;
+
+ if (p1 === undefined || p1 === null) { // No argument
+ /* void */
+ } else if (p2 !== undefined) { // Two arguments
+
+ if (typeof p1 === "bigint") {
+ n = p1;
+ } else if (isNaN(p1)) {
+ throw InvalidParameter();
+ } else if (p1 % 1 !== 0) {
+ throw NonIntegerParameter();
+ } else {
+ n = BigInt(p1);
+ }
+
+ if (typeof p2 === "bigint") {
+ d = p2;
+ } else if (isNaN(p2)) {
+ throw InvalidParameter();
+ } else if (p2 % 1 !== 0) {
+ throw NonIntegerParameter();
+ } else {
+ d = BigInt(p2);
+ }
+
+ s = n * d;
+
+ } else if (typeof p1 === "object") {
+ if ("d" in p1 && "n" in p1) {
+ n = BigInt(p1["n"]);
+ d = BigInt(p1["d"]);
+ if ("s" in p1)
+ n *= BigInt(p1["s"]);
+ } else if (0 in p1) {
+ n = BigInt(p1[0]);
+ if (1 in p1)
+ d = BigInt(p1[1]);
+ } else if (typeof p1 === "bigint") {
+ n = p1;
+ } else {
+ throw InvalidParameter();
+ }
+ s = n * d;
+ } else if (typeof p1 === "number") {
+
+ if (isNaN(p1)) {
+ throw InvalidParameter();
+ }
+
+ if (p1 < 0) {
+ s = -C_ONE;
+ p1 = -p1;
+ }
+
+ if (p1 % 1 === 0) {
+ n = BigInt(p1);
+ } else {
+
+ let z = 1;
+
+ let A = 0, B = 1;
+ let C = 1, D = 1;
+
+ let N = 10000000;
+
+ if (p1 >= 1) {
+ z = 10 ** Math.floor(1 + Math.log10(p1));
+ p1 /= z;
+ }
+
+ // Using Farey Sequences
+
+ while (B <= N && D <= N) {
+ let M = (A + C) / (B + D);
+
+ if (p1 === M) {
+ if (B + D <= N) {
+ n = A + C;
+ d = B + D;
+ } else if (D > B) {
+ n = C;
+ d = D;
+ } else {
+ n = A;
+ d = B;
+ }
+ break;
+
+ } else {
+
+ if (p1 > M) {
+ A += C;
+ B += D;
+ } else {
+ C += A;
+ D += B;
+ }
+
+ if (B > N) {
+ n = C;
+ d = D;
+ } else {
+ n = A;
+ d = B;
+ }
+ }
+ }
+ n = BigInt(n) * BigInt(z);
+ d = BigInt(d);
+ }
+
+ } else if (typeof p1 === "string") {
+
+ let ndx = 0;
+
+ let v = C_ZERO, w = C_ZERO, x = C_ZERO, y = C_ONE, z = C_ONE;
+
+ let match = p1.replace(/_/g, '').match(/\d+|./g);
+
+ if (match === null)
+ throw InvalidParameter();
+
+ if (match[ndx] === '-') {// Check for minus sign at the beginning
+ s = -C_ONE;
+ ndx++;
+ } else if (match[ndx] === '+') {// Check for plus sign at the beginning
+ ndx++;
+ }
+
+ if (match.length === ndx + 1) { // Check if it's just a simple number "1234"
+ w = assign(match[ndx++], s);
+ } else if (match[ndx + 1] === '.' || match[ndx] === '.') { // Check if it's a decimal number
+
+ if (match[ndx] !== '.') { // Handle 0.5 and .5
+ v = assign(match[ndx++], s);
+ }
+ ndx++;
+
+ // Check for decimal places
+ if (ndx + 1 === match.length || match[ndx + 1] === '(' && match[ndx + 3] === ')' || match[ndx + 1] === "'" && match[ndx + 3] === "'") {
+ w = assign(match[ndx], s);
+ y = C_TEN ** BigInt(match[ndx].length);
+ ndx++;
+ }
+
+ // Check for repeating places
+ if (match[ndx] === '(' && match[ndx + 2] === ')' || match[ndx] === "'" && match[ndx + 2] === "'") {
+ x = assign(match[ndx + 1], s);
+ z = C_TEN ** BigInt(match[ndx + 1].length) - C_ONE;
+ ndx += 3;
+ }
+
+ } else if (match[ndx + 1] === '/' || match[ndx + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
+ w = assign(match[ndx], s);
+ y = assign(match[ndx + 2], C_ONE);
+ ndx += 3;
+ } else if (match[ndx + 3] === '/' && match[ndx + 1] === ' ') { // Check for a complex fraction "123 1/2"
+ v = assign(match[ndx], s);
+ w = assign(match[ndx + 2], s);
+ y = assign(match[ndx + 4], C_ONE);
+ ndx += 5;
+ }
+
+ if (match.length <= ndx) { // Check for more tokens on the stack
+ d = y * z;
+ s = /* void */
+ n = x + d * v + z * w;
+ } else {
+ throw InvalidParameter();
+ }
+
+ } else if (typeof p1 === "bigint") {
+ n = p1;
+ s = p1;
+ d = C_ONE;
+ } else {
+ throw InvalidParameter();
+ }
+
+ if (d === C_ZERO) {
+ throw DivisionByZero();
+ }
+
+ P["s"] = s < C_ZERO ? -C_ONE : C_ONE;
+ P["n"] = n < C_ZERO ? -n : n;
+ P["d"] = d < C_ZERO ? -d : d;
+};
+
+function modpow(b, e, m) {
+
+ let r = C_ONE;
+ for (; e > C_ZERO; b = (b * b) % m, e >>= C_ONE) {
+
+ if (e & C_ONE) {
+ r = (r * b) % m;
+ }
+ }
+ return r;
+}
+
+function cycleLen(n, d) {
+
+ for (; d % C_TWO === C_ZERO;
+ d /= C_TWO) {
+ }
+
+ for (; d % C_FIVE === C_ZERO;
+ d /= C_FIVE) {
+ }
+
+ if (d === C_ONE) // Catch non-cyclic numbers
+ return C_ZERO;
+
+ // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
+ // 10^(d-1) % d == 1
+ // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
+ // as we want to translate the numbers to strings.
+
+ let rem = C_TEN % d;
+ let t = 1;
+
+ for (; rem !== C_ONE; t++) {
+ rem = rem * C_TEN % d;
+
+ if (t > MAX_CYCLE_LEN)
+ return C_ZERO; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
+ }
+ return BigInt(t);
+}
+
+function cycleStart(n, d, len) {
+
+ let rem1 = C_ONE;
+ let rem2 = modpow(C_TEN, len, d);
+
+ for (let t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
+ // Solve 10^s == 10^(s+t) (mod d)
+
+ if (rem1 === rem2)
+ return BigInt(t);
+
+ rem1 = rem1 * C_TEN % d;
+ rem2 = rem2 * C_TEN % d;
+ }
+ return 0;
+}
+
+function gcd(a, b) {
+
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+
+ while (1) {
+ a %= b;
+ if (!a)
+ return b;
+ b %= a;
+ if (!b)
+ return a;
+ }
+}
+
+/**
+ * Module constructor
+ *
+ * @constructor
+ * @param {number|Fraction=} a
+ * @param {number=} b
+ */
+function Fraction(a, b) {
+
+ parse(a, b);
+
+ if (this instanceof Fraction) {
+ a = gcd(P["d"], P["n"]); // Abuse a
+ this["s"] = P["s"];
+ this["n"] = P["n"] / a;
+ this["d"] = P["d"] / a;
+ } else {
+ return newFraction(P['s'] * P['n'], P['d']);
+ }
+}
+
+const DivisionByZero = function () { return new Error("Division by Zero"); };
+const InvalidParameter = function () { return new Error("Invalid argument"); };
+const NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
+
+Fraction.prototype = {
+
+ "s": C_ONE,
+ "n": C_ZERO,
+ "d": C_ONE,
+
+ /**
+ * Calculates the absolute value
+ *
+ * Ex: new Fraction(-4).abs() => 4
+ **/
+ "abs": function () {
+
+ return newFraction(this["n"], this["d"]);
+ },
+
+ /**
+ * Inverts the sign of the current fraction
+ *
+ * Ex: new Fraction(-4).neg() => 4
+ **/
+ "neg": function () {
+
+ return newFraction(-this["s"] * this["n"], this["d"]);
+ },
+
+ /**
+ * Adds two rational numbers
+ *
+ * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
+ **/
+ "add": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Subtracts two rational numbers
+ *
+ * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
+ **/
+ "sub": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Multiplies two rational numbers
+ *
+ * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
+ **/
+ "mul": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * P["s"] * this["n"] * P["n"],
+ this["d"] * P["d"]
+ );
+ },
+
+ /**
+ * Divides two rational numbers
+ *
+ * Ex: new Fraction("-17.(345)").inverse().div(3)
+ **/
+ "div": function (a, b) {
+
+ parse(a, b);
+ return newFraction(
+ this["s"] * P["s"] * this["n"] * P["d"],
+ this["d"] * P["n"]
+ );
+ },
+
+ /**
+ * Clones the actual object
+ *
+ * Ex: new Fraction("-17.(345)").clone()
+ **/
+ "clone": function () {
+ return newFraction(this['s'] * this['n'], this['d']);
+ },
+
+ /**
+ * Calculates the modulo of two rational numbers - a more precise fmod
+ *
+ * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
+ * Ex: new Fraction(20, 10).mod().equals(0) ? "is Integer"
+ **/
+ "mod": function (a, b) {
+
+ if (a === undefined) {
+ return newFraction(this["s"] * this["n"] % this["d"], C_ONE);
+ }
+
+ parse(a, b);
+ if (C_ZERO === P["n"] * this["d"]) {
+ throw DivisionByZero();
+ }
+
+ /**
+ * I derived the rational modulo similar to the modulo for integers
+ *
+ * https://raw.org/book/analysis/rational-numbers/
+ *
+ * n1/d1 = (n2/d2) * q + r, where 0 ≤ r < n2/d2
+ * => d2 * n1 = n2 * d1 * q + d1 * d2 * r
+ * => r = (d2 * n1 - n2 * d1 * q) / (d1 * d2)
+ * = (d2 * n1 - n2 * d1 * floor((d2 * n1) / (n2 * d1))) / (d1 * d2)
+ * = ((d2 * n1) % (n2 * d1)) / (d1 * d2)
+ */
+ return newFraction(
+ this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]),
+ P["d"] * this["d"]);
+ },
+
+ /**
+ * Calculates the fractional gcd of two rational numbers
+ *
+ * Ex: new Fraction(5,8).gcd(3,7) => 1/56
+ */
+ "gcd": function (a, b) {
+
+ parse(a, b);
+
+ // https://raw.org/book/analysis/rational-numbers/
+ // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
+
+ return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]);
+ },
+
+ /**
+ * Calculates the fractional lcm of two rational numbers
+ *
+ * Ex: new Fraction(5,8).lcm(3,7) => 15
+ */
+ "lcm": function (a, b) {
+
+ parse(a, b);
+
+ // https://raw.org/book/analysis/rational-numbers/
+ // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
+
+ if (P["n"] === C_ZERO && this["n"] === C_ZERO) {
+ return newFraction(C_ZERO, C_ONE);
+ }
+ return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]));
+ },
+
+ /**
+ * Gets the inverse of the fraction, means numerator and denominator are exchanged
+ *
+ * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
+ **/
+ "inverse": function () {
+ return newFraction(this["s"] * this["d"], this["n"]);
+ },
+
+ /**
+ * Calculates the fraction to some integer exponent
+ *
+ * Ex: new Fraction(-1,2).pow(-3) => -8
+ */
+ "pow": function (a, b) {
+
+ parse(a, b);
+
+ // Trivial case when exp is an integer
+
+ if (P['d'] === C_ONE) {
+
+ if (P['s'] < C_ZERO) {
+ return newFraction((this['s'] * this["d"]) ** P['n'], this["n"] ** P['n']);
+ } else {
+ return newFraction((this['s'] * this["n"]) ** P['n'], this["d"] ** P['n']);
+ }
+ }
+
+ // Negative roots become complex
+ // (-a/b)^(c/d) = x
+ // ⇔ (-1)^(c/d) * (a/b)^(c/d) = x
+ // ⇔ (cos(pi) + i*sin(pi))^(c/d) * (a/b)^(c/d) = x
+ // ⇔ (cos(c*pi/d) + i*sin(c*pi/d)) * (a/b)^(c/d) = x # DeMoivre's formula
+ // From which follows that only for c=0 the root is non-complex
+ if (this['s'] < C_ZERO) return null;
+
+ // Now prime factor n and d
+ let N = factorize(this['n']);
+ let D = factorize(this['d']);
+
+ // Exponentiate and take root for n and d individually
+ let n = C_ONE;
+ let d = C_ONE;
+ for (let k in N) {
+ if (k === '1') continue;
+ if (k === '0') {
+ n = C_ZERO;
+ break;
+ }
+ N[k] *= P['n'];
+
+ if (N[k] % P['d'] === C_ZERO) {
+ N[k] /= P['d'];
+ } else return null;
+ n *= BigInt(k) ** N[k];
+ }
+
+ for (let k in D) {
+ if (k === '1') continue;
+ D[k] *= P['n'];
+
+ if (D[k] % P['d'] === C_ZERO) {
+ D[k] /= P['d'];
+ } else return null;
+ d *= BigInt(k) ** D[k];
+ }
+
+ if (P['s'] < C_ZERO) {
+ return newFraction(d, n);
+ }
+ return newFraction(n, d);
+ },
+
+ /**
+ * Calculates the logarithm of a fraction to a given rational base
+ *
+ * Ex: new Fraction(27, 8).log(9, 4) => 3/2
+ */
+ "log": function (a, b) {
+
+ parse(a, b);
+
+ if (this['s'] <= C_ZERO || P['s'] <= C_ZERO) return null;
+
+ const allPrimes = Object.create(null);
+
+ const baseFactors = factorize(P['n']);
+ const T1 = factorize(P['d']);
+
+ const numberFactors = factorize(this['n']);
+ const T2 = factorize(this['d']);
+
+ for (const prime in T1) {
+ baseFactors[prime] = (baseFactors[prime] || C_ZERO) - T1[prime];
+ }
+ for (const prime in T2) {
+ numberFactors[prime] = (numberFactors[prime] || C_ZERO) - T2[prime];
+ }
+
+ for (const prime in baseFactors) {
+ if (prime === '1') continue;
+ allPrimes[prime] = true;
+ }
+ for (const prime in numberFactors) {
+ if (prime === '1') continue;
+ allPrimes[prime] = true;
+ }
+
+ let retN = null;
+ let retD = null;
+
+ // Iterate over all unique primes to determine if a consistent ratio exists
+ for (const prime in allPrimes) {
+
+ const baseExponent = baseFactors[prime] || C_ZERO;
+ const numberExponent = numberFactors[prime] || C_ZERO;
+
+ if (baseExponent === C_ZERO) {
+ if (numberExponent !== C_ZERO) {
+ return null; // Logarithm cannot be expressed as a rational number
+ }
+ continue; // Skip this prime since both exponents are zero
+ }
+
+ // Calculate the ratio of exponents for this prime
+ let curN = numberExponent;
+ let curD = baseExponent;
+
+ // Simplify the current ratio
+ const gcdValue = gcd(curN, curD);
+ curN /= gcdValue;
+ curD /= gcdValue;
+
+ // Check if this is the first ratio; otherwise, ensure ratios are consistent
+ if (retN === null && retD === null) {
+ retN = curN;
+ retD = curD;
+ } else if (curN * retD !== retN * curD) {
+ return null; // Ratios do not match, logarithm cannot be rational
+ }
+ }
+
+ return retN !== null && retD !== null
+ ? newFraction(retN, retD)
+ : null;
+ },
+
+ /**
+ * Check if two rational numbers are the same
+ *
+ * Ex: new Fraction(19.6).equals([98, 5]);
+ **/
+ "equals": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is less than another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "lt": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] < P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is less than or equal another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "lte": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] <= P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is greater than another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "gt": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] > P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Check if this rational number is greater than or equal another
+ *
+ * Ex: new Fraction(19.6).lt([98, 5]);
+ **/
+ "gte": function (a, b) {
+
+ parse(a, b);
+ return this["s"] * this["n"] * P["d"] >= P["s"] * P["n"] * this["d"];
+ },
+
+ /**
+ * Compare two rational numbers
+ * < 0 iff this < that
+ * > 0 iff this > that
+ * = 0 iff this = that
+ *
+ * Ex: new Fraction(19.6).compare([98, 5]);
+ **/
+ "compare": function (a, b) {
+
+ parse(a, b);
+ let t = this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"];
+
+ return (C_ZERO < t) - (t < C_ZERO);
+ },
+
+ /**
+ * Calculates the ceil of a rational number
+ *
+ * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
+ **/
+ "ceil": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+ (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Calculates the floor of a rational number
+ *
+ * Ex: new Fraction('4.(3)').floor() => (4 / 1)
+ **/
+ "floor": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) -
+ (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Rounds a rational numbers
+ *
+ * Ex: new Fraction('4.(3)').round() => (4 / 1)
+ **/
+ "round": function (places) {
+
+ places = C_TEN ** BigInt(places || 0);
+
+ /* Derivation:
+
+ s >= 0:
+ round(n / d) = ifloor(n / d) + (n % d) / d >= 0.5 ? 1 : 0
+ = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+ s < 0:
+ round(n / d) =-ifloor(n / d) - (n % d) / d > 0.5 ? 1 : 0
+ =-ifloor(n / d) - 2(n % d) > d ? 1 : 0
+
+ =>:
+
+ round(s * n / d) = s * ifloor(n / d) + s * (C + 2(n % d) > d ? 1 : 0)
+ where C = s >= 0 ? 1 : 0, to fix the >= for the positve case.
+ */
+
+ return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) +
+ this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO),
+ places);
+ },
+
+ /**
+ * Rounds a rational number to a multiple of another rational number
+ *
+ * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8
+ **/
+ "roundTo": function (a, b) {
+
+ /*
+ k * x/y ≤ a/b < (k+1) * x/y
+ ⇔ k ≤ a/b / (x/y) < (k+1)
+ ⇔ k = floor(a/b * y/x)
+ ⇔ k = floor((a * y) / (b * x))
+ */
+
+ parse(a, b);
+
+ const n = this['n'] * P['d'];
+ const d = this['d'] * P['n'];
+ const r = n % d;
+
+ // round(n / d) = ifloor(n / d) + 2(n % d) >= d ? 1 : 0
+ let k = ifloor(n / d);
+ if (r + r >= d) {
+ k++;
+ }
+ return newFraction(this['s'] * k * P['n'], P['d']);
+ },
+
+ /**
+ * Check if two rational numbers are divisible
+ *
+ * Ex: new Fraction(19.6).divisible(1.5);
+ */
+ "divisible": function (a, b) {
+
+ parse(a, b);
+ if (P['n'] === C_ZERO) return false;
+ return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO;
+ },
+
+ /**
+ * Returns a decimal representation of the fraction
+ *
+ * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
+ **/
+ 'valueOf': function () {
+ //if (this['n'] <= MAX_INTEGER && this['d'] <= MAX_INTEGER) {
+ return Number(this['s'] * this['n']) / Number(this['d']);
+ //}
+ },
+
+ /**
+ * Creates a string representation of a fraction with all digits
+ *
+ * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
+ **/
+ 'toString': function (dec = 15) {
+
+ let N = this["n"];
+ let D = this["d"];
+
+ let cycLen = cycleLen(N, D); // Cycle length
+ let cycOff = cycleStart(N, D, cycLen); // Cycle start
+
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ // Append integer part
+ str += ifloor(N / D);
+
+ N %= D;
+ N *= C_TEN;
+
+ if (N)
+ str += ".";
+
+ if (cycLen) {
+
+ for (let i = cycOff; i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ str += "(";
+ for (let i = cycLen; i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ str += ")";
+ } else {
+ for (let i = dec; N && i--;) {
+ str += ifloor(N / D);
+ N %= D;
+ N *= C_TEN;
+ }
+ }
+ return str;
+ },
+
+ /**
+ * Returns a string-fraction representation of a Fraction object
+ *
+ * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
+ **/
+ 'toFraction': function (showMixed = false) {
+
+ let n = this["n"];
+ let d = this["d"];
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ if (d === C_ONE) {
+ str += n;
+ } else {
+ const whole = ifloor(n / d);
+ if (showMixed && whole > C_ZERO) {
+ str += whole;
+ str += " ";
+ n %= d;
+ }
+
+ str += n;
+ str += '/';
+ str += d;
+ }
+ return str;
+ },
+
+ /**
+ * Returns a latex representation of a Fraction object
+ *
+ * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
+ **/
+ 'toLatex': function (showMixed = false) {
+
+ let n = this["n"];
+ let d = this["d"];
+ let str = this['s'] < C_ZERO ? "-" : "";
+
+ if (d === C_ONE) {
+ str += n;
+ } else {
+ const whole = ifloor(n / d);
+ if (showMixed && whole > C_ZERO) {
+ str += whole;
+ n %= d;
+ }
+
+ str += "\\frac{";
+ str += n;
+ str += '}{';
+ str += d;
+ str += '}';
+ }
+ return str;
+ },
+
+ /**
+ * Returns an array of continued fraction elements
+ *
+ * Ex: new Fraction("7/8").toContinued() => [0,1,7]
+ */
+ 'toContinued': function () {
+
+ let a = this['n'];
+ let b = this['d'];
+ const res = [];
+
+ while (b) {
+ res.push(ifloor(a / b));
+ const t = a % b;
+ a = b;
+ b = t;
+ }
+ return res;
+ },
+
+ "simplify": function (eps = 1e-3) {
+
+ // Continued fractions give best approximations for a max denominator,
+ // generally outperforming mediants in denominator–accuracy trade-offs.
+ // Semiconvergents can further reduce the denominator within tolerance.
+
+ const ieps = BigInt(Math.ceil(1 / eps));
+
+ const thisABS = this['abs']();
+ const cont = thisABS['toContinued']();
+
+ for (let i = 1; i < cont.length; i++) {
+
+ let s = newFraction(cont[i - 1], C_ONE);
+ for (let k = i - 2; k >= 0; k--) {
+ s = s['inverse']()['add'](cont[k]);
+ }
+
+ let t = s['sub'](thisABS);
+ if (t['n'] * ieps < t['d']) { // More robust than Math.abs(t.valueOf()) < eps
+ return s['mul'](this['s']);
+ }
+ }
+ return this;
+ }
+};
diff --git a/node_modules/fraction.js/tests/fraction.test.js b/node_modules/fraction.js/tests/fraction.test.js
new file mode 100644
index 0000000..b65833c
--- /dev/null
+++ b/node_modules/fraction.js/tests/fraction.test.js
@@ -0,0 +1,1806 @@
+const Fraction = require('fraction.js');
+const assert = require('assert');
+
+var DivisionByZero = function () { return new Error("Division by Zero"); };
+var InvalidParameter = function () { return new Error("Invalid argument"); };
+var NonIntegerParameter = function () { return new Error("Parameters must be integer"); };
+
+var tests = [{
+ set: "",
+ expectError: InvalidParameter()
+}, {
+ set: "foo",
+ expectError: InvalidParameter()
+}, {
+ set: " 123",
+ expectError: InvalidParameter()
+}, {
+ set: 0,
+ expect: 0
+}, {
+ set: .2,
+ expect: "0.2"
+}, {
+ set: .333,
+ expect: "0.333"
+}, {
+ set: 1.1,
+ expect: "1.1"
+}, {
+ set: 1.2,
+ expect: "1.2"
+}, {
+ set: 1.3,
+ expect: "1.3"
+}, {
+ set: 1.4,
+ expect: "1.4"
+}, {
+ set: 1.5,
+ expect: "1.5"
+}, {
+ set: 2.555,
+ expect: "2.555"
+}, {
+ set: 1e12,
+ expect: "1000000000000"
+}, {
+ set: " - ",
+ expectError: InvalidParameter()
+}, {
+ set: ".5",
+ expect: "0.5"
+}, {
+ set: "2_000_000",
+ expect: "2000000"
+}, {
+ set: "-.5",
+ expect: "-0.5"
+}, {
+ set: "123",
+ expect: "123"
+}, {
+ set: "-123",
+ expect: "-123"
+}, {
+ set: "123.4",
+ expect: "123.4"
+}, {
+ set: "-123.4",
+ expect: "-123.4"
+}, {
+ set: "123.",
+ expect: "123"
+}, {
+ set: "-123.",
+ expect: "-123"
+}, {
+ set: "123.4(56)",
+ expect: "123.4(56)"
+}, {
+ set: "-123.4(56)",
+ expect: "-123.4(56)"
+}, {
+ set: "123.(4)",
+ expect: "123.(4)"
+}, {
+ set: "-123.(4)",
+ expect: "-123.(4)"
+}, {
+ set: "0/0",
+ expectError: DivisionByZero()
+}, {
+ set: "9/0",
+ expectError: DivisionByZero()
+}, {
+ label: "0/1+0/1",
+ set: "0/1",
+ param: "0/1",
+ expect: "0"
+}, {
+ label: "1/9+0/1",
+ set: "1/9",
+ param: "0/1",
+ expect: "0.(1)"
+}, {
+ set: "123/456",
+ expect: "0.269(736842105263157894)"
+}, {
+ set: "-123/456",
+ expect: "-0.269(736842105263157894)"
+}, {
+ set: "19 123/456",
+ expect: "19.269(736842105263157894)"
+}, {
+ set: "-19 123/456",
+ expect: "-19.269(736842105263157894)"
+}, {
+ set: "123.(22)123",
+ expectError: InvalidParameter()
+}, {
+ set: "+33.3(3)",
+ expect: "33.(3)"
+}, {
+ set: "3.'09009'",
+ expect: "3.(09009)"
+}, {
+ set: "123.(((",
+ expectError: InvalidParameter()
+}, {
+ set: "123.((",
+ expectError: InvalidParameter()
+}, {
+ set: "123.()",
+ expectError: InvalidParameter()
+}, {
+ set: null,
+ expect: "0" // I would say it's just fine
+}, {
+ set: [22, 7],
+ expect: '3.(142857)' // We got Pi! - almost ;o
+}, {
+ set: "355/113",
+ expect: "3.(1415929203539823008849557522123893805309734513274336283185840707964601769911504424778761061946902654867256637168)" // Yay, a better PI
+}, {
+ set: "3 1/7",
+ expect: '3.(142857)'
+}, {
+ set: [36, -36],
+ expect: "-1"
+}, {
+ set: [1n, 3n],
+ expect: "0.(3)"
+}, {
+ set: 1n,
+ set2: 3n,
+ expect: "0.(3)"
+}, {
+ set: { n: 1n, d: 3n },
+ expect: "0.(3)"
+}, {
+ set: { n: 1n, d: 3n },
+ expect: "0.(3)"
+}, {
+ set: [1n, 3n],
+ expect: "0.(3)"
+}, {
+ set: "9/12",
+ expect: "0.75"
+}, {
+ set: "0.09(33)",
+ expect: "0.09(3)"
+}, {
+ set: 1 / 2,
+ expect: "0.5"
+}, {
+ set: 1 / 3,
+ expect: "0.(3)"
+}, {
+ set: "0.'3'",
+ expect: "0.(3)"
+}, {
+ set: "0.00002",
+ expect: "0.00002"
+}, {
+ set: 7 / 8,
+ expect: "0.875"
+}, {
+ set: 0.003,
+ expect: "0.003"
+}, {
+ set: 4,
+ expect: "4"
+}, {
+ set: -99,
+ expect: "-99"
+}, {
+ set: "-92332.1192",
+ expect: "-92332.1192"
+}, {
+ set: '88.92933(12111)',
+ expect: "88.92933(12111)"
+}, {
+ set: '-192322.823(123)',
+ expect: "-192322.8(231)"
+}, {
+ label: "-99.12 % 0.09(34)",
+ set: '-99.12',
+ fn: "mod",
+ param: "0.09(34)",
+ expect: "-0.07(95)"
+}, {
+ label: "0.4 / 0.1",
+ set: .4,
+ fn: "div",
+ param: ".1",
+ expect: "4"
+}, {
+ label: "1 / -.1",
+ set: 1,
+ fn: "div",
+ param: "-.1",
+ expect: "-10"
+}, {
+ label: "1 - (-1)",
+ set: 1,
+ fn: "sub",
+ param: "-1",
+ expect: "2"
+}, {
+ label: "1 + (-1)",
+ set: 1,
+ fn: "add",
+ param: "-1",
+ expect: "0"
+}, {
+ label: "-187 % 12",
+ set: '-187',
+ fn: "mod",
+ param: "12",
+ expect: "-7"
+}, {
+ label: "Negate by 99 * -1",
+ set: '99',
+ fn: "mul",
+ param: "-1",
+ expect: "-99"
+}, {
+ label: "0.5050000000000000000000000",
+ set: "0.5050000000000000000000000",
+ expect: "101/200",
+ fn: "toFraction",
+ param: true
+}, {
+ label: "0.505000000(0000000000)",
+ set: "0.505000000(0000000000)",
+ expect: "101/200",
+ fn: "toFraction",
+ param: true
+}, {
+ set: [20, -5],
+ expect: "-4",
+ fn: "toFraction",
+ param: true
+}, {
+ set: [-10, -7],
+ expect: "1 3/7",
+ fn: "toFraction",
+ param: true
+}, {
+ set: [21, -6],
+ expect: "-3 1/2",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "10/78",
+ expect: "5/39",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "0/91",
+ expect: "0",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "-0/287",
+ expect: "0",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "-5/20",
+ expect: "-1/4",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "42/9",
+ expect: "4 2/3",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "71/23",
+ expect: "3 2/23",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "6/3",
+ expect: "2",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "28/4",
+ expect: "7",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "105/35",
+ expect: "3",
+ fn: "toFraction",
+ param: true
+}, {
+ set: "4/6",
+ expect: "2/3",
+ fn: "toFraction",
+ param: true
+}, {
+ label: "99.(9) + 66",
+ set: '99.(999999)',
+ fn: "add",
+ param: "66",
+ expect: "166"
+}, {
+ label: "123.32 / 33.'9821'",
+ set: '123.32',
+ fn: "div",
+ param: "33.'9821'",
+ expect: "3.628958880242975"
+}, {
+ label: "-82.124 / 66.(3)",
+ set: '-82.124',
+ fn: "div",
+ param: "66.(3)",
+ expect: "-1.238(050251256281407035175879396984924623115577889447236180904522613065326633165829145728643216080402010)"
+}, {
+ label: "100 - .91",
+ set: '100',
+ fn: "sub",
+ param: ".91",
+ expect: "99.09"
+}, {
+ label: "381.(33411) % 11.119(356)",
+ set: '381.(33411)',
+ fn: "mod",
+ param: "11.119(356)",
+ expect: "3.275(997225017295217)"
+}, {
+ label: "13/26 mod 1",
+ set: '13/26',
+ fn: "mod",
+ param: "1.000",
+ expect: "0.5"
+}, {
+ label: "381.(33411) % 1", // Extract fraction part of a number
+ set: '381.(33411)',
+ fn: "mod",
+ param: "1",
+ expect: "0.(33411)"
+}, {
+ label: "-222/3",
+ set: {
+ n: 3,
+ d: 222,
+ s: -1
+ },
+ fn: "inverse",
+ param: null,
+ expect: "-74"
+}, {
+ label: "inverse",
+ set: 1 / 2,
+ fn: "inverse",
+ param: null,
+ expect: "2"
+}, {
+ label: "abs(-222/3)",
+ set: {
+ n: -222,
+ d: 3
+ },
+ fn: "abs",
+ param: null,
+ expect: "74"
+}, {
+ label: "9 % -2",
+ set: 9,
+ fn: "mod",
+ param: "-2",
+ expect: "1"
+}, {
+ label: "-9 % 2",
+ set: '-9',
+ fn: "mod",
+ param: "-2",
+ expect: "-1"
+}, {
+ label: "1 / 195312500",
+ set: '1',
+ fn: "div",
+ param: "195312500",
+ expect: "0.00000000512"
+}, {
+ label: "10 / 0",
+ set: 10,
+ fn: "div",
+ param: 0,
+ expectError: DivisionByZero()
+}, {
+ label: "-3 / 4",
+ set: [-3, 4],
+ fn: "inverse",
+ param: null,
+ expect: "-1.(3)"
+}, {
+ label: "-19.6",
+ set: [-98, 5],
+ fn: "equals",
+ param: '-19.6',
+ expect: "true" // actually, we get a real bool but we call toString() in the test below
+}, {
+ label: "-19.6",
+ set: [98, -5],
+ fn: "equals",
+ param: '-19.6',
+ expect: "true"
+}, {
+ label: "99/88",
+ set: [99, 88],
+ fn: "equals",
+ param: [88, 99],
+ expect: "false"
+}, {
+ label: "99/88",
+ set: [99, -88],
+ fn: "equals",
+ param: [9, 8],
+ expect: "false"
+}, {
+ label: "12.5",
+ set: 12.5,
+ fn: "add",
+ param: 0,
+ expect: "12.5"
+}, {
+ label: "0/1 -> 1/0",
+ set: 0,
+ fn: "inverse",
+ param: null,
+ expectError: DivisionByZero()
+}, {
+ label: "abs(-100.25)",
+ set: -100.25,
+ fn: "abs",
+ param: null,
+ expect: "100.25"
+}, {
+ label: "0.022222222",
+ set: '0.0(22222222)',
+ fn: "abs",
+ param: null,
+ expect: "0.0(2)"
+}, {
+ label: "1.5 | 100.5",
+ set: 100.5,
+ fn: "divisible",
+ param: '1.5',
+ expect: "true"
+}, {
+ label: "1.5 | 100.6",
+ set: 100.6,
+ fn: "divisible",
+ param: 1.6,
+ expect: "false"
+}, {
+ label: "(1/6) | (2/3)", // == 4
+ set: [2, 3],
+ fn: "divisible",
+ param: [1, 6],
+ expect: "true"
+}, {
+ label: "(1/6) | (2/5)",
+ set: [2, 5],
+ fn: "divisible",
+ param: [1, 6],
+ expect: "false"
+}, {
+ label: "0 | (2/5)",
+ set: [2, 5],
+ fn: "divisible",
+ param: 0,
+ expect: "false"
+}, {
+ label: "6 | 0",
+ set: 0,
+ fn: "divisible",
+ param: 6,
+ expect: "true"
+}, {
+ label: "fmod(4.55, 0.05)", // http://phpjs.org/functions/fmod/ (comment section)
+ set: 4.55,
+ fn: "mod",
+ param: 0.05,
+ expect: "0"
+}, {
+ label: "fmod(99.12, 0.4)",
+ set: 99.12,
+ fn: "mod",
+ param: "0.4",
+ expect: "0.32"
+}, {
+ label: "fmod(fmod(1.0,0.1))", // http://stackoverflow.com/questions/4218961/why-fmod1-0-0-1-1
+ set: 1.0,
+ fn: "mod",
+ param: 0.1,
+ expect: "0"
+}, {
+ label: "bignum",
+ set: [5385020324, 1673196525],
+ fn: "add",
+ param: 0,
+ expect: "3.21(840276592733181776121606516006839065124164060763872313206005492988936251824931324190982287630557922656455433410609073551596098372245902196097377144624418820138297860736950789447760776337973807350574075570710380240599651018280712721418065340531352107607323652551812465663589637206543923464101146157950573080469432602963360804254598843372567965379918536467197121390148715495330113717514444395585868193217769203770011415724163065662594535928766646225254382476081224230369471990147720394052336440275597631903998844367669243157195775313960803259497565595290726533154854597848271290188102679689703515252041298615534717298077104242133182771222884293284077911887845930112722413166618308629346454087334421161315763550250022184333666363549254920906556389124702491239037207539024741878423396797336762338781453063321417070239253574830368476888869943116813489676593728283053898883754853602746993512910863832926021645903191198654921901657666901979730085800889408373591978384009612977172541043856160291750546158945674358246709841810124486123947693472528578195558946669459524487119048971249805817042322628538808374587079661786890216019304725725509141850506771761314768448972244907094819599867385572056456428511886850828834945135927771544947477105237234460548500123140047759781236696030073335228807028510891749551057667897081007863078128255137273847732859712937785356684266362554153643129279150277938809369688357439064129062782986595074359241811119587401724970711375341877428295519559485099934689381452068220139292962014728066686607540019843156200674036183526020650801913421377683054893985032630879985)"
+}, {
+ label: "ceil(0.4)",
+ set: 0.4,
+ fn: "ceil",
+ param: null,
+ expect: "1"
+},
+
+
+{
+ label: "1 < 2",
+ set: 1,
+ fn: "lt",
+ param: 2,
+ expect: "true"
+}, {
+ label: "2 < 2",
+ set: 2,
+ fn: "lt",
+ param: 2,
+ expect: "false"
+}, {
+ label: "3 > 2",
+ set: 3,
+ fn: "gt",
+ param: 2,
+ expect: "true"
+}, {
+ label: "2 > 2",
+ set: 2,
+ fn: "gt",
+ param: 2,
+ expect: "false"
+}, {
+ label: "1 <= 2",
+ set: 1,
+ fn: "lte",
+ param: 2,
+ expect: "true"
+}, {
+ label: "2 <= 2",
+ set: 2,
+ fn: "lte",
+ param: 2,
+ expect: "true"
+}, {
+ label: "3 <= 2",
+ set: 3,
+ fn: "lte",
+ param: 2,
+ expect: "false"
+}, {
+ label: "3 >= 2",
+ set: 3,
+ fn: "gte",
+ param: 2,
+ expect: "true"
+}, {
+ label: "2 >= 2",
+ set: 2,
+ fn: "gte",
+ param: 2,
+ expect: "true"
+}, {
+ label: "ceil(0.5)",
+ set: 0.5,
+ fn: "ceil",
+ param: null,
+ expect: "1"
+}, {
+ label: "ceil(0.23, 2)",
+ set: 0.23,
+ fn: "ceil",
+ param: 2,
+ expect: "0.23"
+}, {
+ label: "ceil(0.6)",
+ set: 0.6,
+ fn: "ceil",
+ param: null,
+ expect: "1"
+}, {
+ label: "ceil(-0.4)",
+ set: -0.4,
+ fn: "ceil",
+ param: null,
+ expect: "0"
+}, {
+ label: "ceil(-0.5)",
+ set: -0.5,
+ fn: "ceil",
+ param: null,
+ expect: "0"
+}, {
+ label: "ceil(-0.6)",
+ set: -0.6,
+ fn: "ceil",
+ param: null,
+ expect: "0"
+}, {
+ label: "floor(0.4)",
+ set: 0.4,
+ fn: "floor",
+ param: null,
+ expect: "0"
+}, {
+ label: "floor(0.4, 1)",
+ set: 0.4,
+ fn: "floor",
+ param: 1,
+ expect: "0.4"
+}, {
+ label: "floor(0.5)",
+ set: 0.5,
+ fn: "floor",
+ param: null,
+ expect: "0"
+}, {
+ label: "floor(0.6)",
+ set: 0.6,
+ fn: "floor",
+ param: null,
+ expect: "0"
+}, {
+ label: "floor(-0.4)",
+ set: -0.4,
+ fn: "floor",
+ param: null,
+ expect: "-1"
+}, {
+ label: "floor(-0.5)",
+ set: -0.5,
+ fn: "floor",
+ param: null,
+ expect: "-1"
+}, {
+ label: "floor(-0.6)",
+ set: -0.6,
+ fn: "floor",
+ param: null,
+ expect: "-1"
+}, {
+ label: "floor(10.4)",
+ set: 10.4,
+ fn: "floor",
+ param: null,
+ expect: "10"
+}, {
+ label: "floor(10.4, 1)",
+ set: 10.4,
+ fn: "floor",
+ param: 1,
+ expect: "10.4"
+}, {
+ label: "floor(10.5)",
+ set: 10.5,
+ fn: "floor",
+ param: null,
+ expect: "10"
+}, {
+ label: "floor(10.6)",
+ set: 10.6,
+ fn: "floor",
+ param: null,
+ expect: "10"
+}, {
+ label: "floor(-10.4)",
+ set: -10.4,
+ fn: "floor",
+ param: null,
+ expect: "-11"
+}, {
+ label: "floor(-10.5)",
+ set: -10.5,
+ fn: "floor",
+ param: null,
+ expect: "-11"
+}, {
+ label: "floor(-10.6)",
+ set: -10.6,
+ fn: "floor",
+ param: null,
+ expect: "-11"
+}, {
+ label: "floor(-10.543,3)",
+ set: -10.543,
+ fn: "floor",
+ param: 3,
+ expect: "-10.543"
+}, {
+ label: "floor(10.543,3)",
+ set: 10.543,
+ fn: "floor",
+ param: 3,
+ expect: "10.543"
+}, {
+ label: "round(-10.543,3)",
+ set: -10.543,
+ fn: "round",
+ param: 3,
+ expect: "-10.543"
+}, {
+ label: "round(10.543,3)",
+ set: 10.543,
+ fn: "round",
+ param: 3,
+ expect: "10.543"
+}, {
+ label: "round(10.4)",
+ set: 10.4,
+ fn: "round",
+ param: null,
+ expect: "10"
+}, {
+ label: "round(10.5)",
+ set: 10.5,
+ fn: "round",
+ param: null,
+ expect: "11"
+}, {
+ label: "round(10.5, 1)",
+ set: 10.5,
+ fn: "round",
+ param: 1,
+ expect: "10.5"
+}, {
+ label: "round(10.6)",
+ set: 10.6,
+ fn: "round",
+ param: null,
+ expect: "11"
+}, {
+ label: "round(-10.4)",
+ set: -10.4,
+ fn: "round",
+ param: null,
+ expect: "-10"
+}, {
+ label: "round(-10.5)",
+ set: -10.5,
+ fn: "round",
+ param: null,
+ expect: "-10"
+}, {
+ label: "round(-10.6)",
+ set: -10.6,
+ fn: "round",
+ param: null,
+ expect: "-11"
+}, {
+ label: "round(-0.4)",
+ set: -0.4,
+ fn: "round",
+ param: null,
+ expect: "0"
+}, {
+ label: "round(-0.5)",
+ set: -0.5,
+ fn: "round",
+ param: null,
+ expect: "0"
+}, {
+ label: "round(-0.6)",
+ set: -0.6,
+ fn: "round",
+ param: null,
+ expect: "-1"
+}, {
+ label: "round(-0)",
+ set: -0,
+ fn: "round",
+ param: null,
+ expect: "0"
+}, {
+ label: "round(big fraction)",
+ set: [
+ '409652136432929109317120'.repeat(100),
+ '63723676445298091081155'.repeat(100)
+ ],
+ fn: "round",
+ param: null,
+ expect: "6428570341270001560623330590225448467479093479780591305451264291405695842465355472558570608574213642"
+}, {
+ label: "round(big numerator)",
+ set: ['409652136432929109317'.repeat(100), 10],
+ fn: "round",
+ param: null,
+ expect: '409652136432929109317'.repeat(99) + '40965213643292910932'
+}, {
+ label: "17402216385200408/5539306332998545",
+ set: [17402216385200408, 5539306332998545],
+ fn: "add",
+ param: 0,
+ expect: "3.141587653589870"
+}, {
+ label: "17402216385200401/553930633299855",
+ set: [17402216385200401, 553930633299855],
+ fn: "add",
+ param: 0,
+ expect: "31.415876535898660"
+}, {
+ label: "1283191/418183",
+ set: [1283191, 418183],
+ fn: "add",
+ param: 0,
+ expect: "3.068491545567371"
+}, {
+ label: "1.001",
+ set: "1.001",
+ fn: "add",
+ param: 0,
+ expect: "1.001"
+}, {
+ label: "99+1",
+ set: [99, 1],
+ fn: "add",
+ param: 1,
+ expect: "100"
+}, {
+ label: "gcd(5/8, 3/7)",
+ set: [5, 8],
+ fn: "gcd",
+ param: [3, 7],
+ expect: "0.017(857142)" // == 1/56
+}, {
+ label: "gcd(52, 39)",
+ set: 52,
+ fn: "gcd",
+ param: 39,
+ expect: "13"
+}, {
+ label: "gcd(51357, 3819)",
+ set: 51357,
+ fn: "gcd",
+ param: 3819,
+ expect: "57"
+}, {
+ label: "gcd(841, 299)",
+ set: 841,
+ fn: "gcd",
+ param: 299,
+ expect: "1"
+}, {
+ label: "gcd(2/3, 7/5)",
+ set: [2, 3],
+ fn: "gcd",
+ param: [7, 5],
+ expect: "0.0(6)" // == 1/15
+}, {
+ label: "lcm(-3, 3)",
+ set: -3,
+ fn: "lcm",
+ param: 3,
+ expect: "3"
+}, {
+ label: "lcm(3,-3)",
+ set: 3,
+ fn: "lcm",
+ param: -3,
+ expect: "3"
+}, {
+ label: "lcm(0,3)",
+ set: 0,
+ fn: "lcm",
+ param: 3,
+ expect: "0"
+}, {
+ label: "lcm(3, 0)",
+ set: 3,
+ fn: "lcm",
+ param: 0,
+ expect: "0"
+}, {
+ label: "lcm(0, 0)",
+ set: 0,
+ fn: "lcm",
+ param: 0,
+ expect: "0"
+}, {
+ label: "lcm(200, 333)",
+ set: 200,
+ fn: "lcm",
+ param: 333, expect: "66600"
+},
+{
+ label: "1 + -1",
+ set: 1,
+ fn: "add",
+ param: -1,
+ expect: "0"
+}, {
+ label: "3/10+3/14",
+ set: "3/10",
+ fn: "add",
+ param: "3/14",
+ expect: "0.5(142857)"
+}, {
+ label: "3/10-3/14",
+ set: "3/10",
+ fn: "sub",
+ param: "3/14",
+ expect: "0.0(857142)"
+}, {
+ label: "3/10*3/14",
+ set: "3/10",
+ fn: "mul",
+ param: "3/14",
+ expect: "0.06(428571)"
+}, {
+ label: "3/10 / 3/14",
+ set: "3/10",
+ fn: "div",
+ param: "3/14",
+ expect: "1.4"
+}, {
+ label: "1-2",
+ set: "1",
+ fn: "sub",
+ param: "2",
+ expect: "-1"
+}, {
+ label: "1--1",
+ set: "1",
+ fn: "sub",
+ param: "-1",
+ expect: "2"
+}, {
+ label: "0/1*1/3",
+ set: "0/1",
+ fn: "mul",
+ param: "1/3",
+ expect: "0"
+}, {
+ label: "3/10 * 8/12",
+ set: "3/10",
+ fn: "mul",
+ param: "8/12",
+ expect: "0.2"
+}, {
+ label: ".5+5",
+ set: ".5",
+ fn: "add",
+ param: 5, expect: "5.5"
+},
+{
+ label: "10/12-5/60",
+ set: "10/12",
+ fn: "sub",
+ param: "5/60",
+ expect: "0.75"
+}, {
+ label: "10/15 / 3/4",
+ set: "10/15",
+ fn: "div",
+ param: "3/4",
+ expect: "0.(8)"
+}, {
+ label: "1/4 + 3/8",
+ set: "1/4",
+ fn: "add",
+ param: "3/8",
+ expect: "0.625"
+}, {
+ label: "2-1/3",
+ set: "2",
+ fn: "sub",
+ param: "1/3",
+ expect: "1.(6)"
+}, {
+ label: "5*6",
+ set: "5",
+ fn: "mul",
+ param: 6,
+ expect: "30"
+}, {
+ label: "1/2-1/5",
+ set: "1/2",
+ fn: "sub",
+ param: "1/5",
+ expect: "0.3"
+}, {
+ label: "1/2-5",
+ set: "1/2",
+ fn: "add",
+ param: -5,
+ expect: "-4.5"
+}, {
+ label: "1*-1",
+ set: "1",
+ fn: "mul",
+ param: -1,
+ expect: "-1"
+}, {
+ label: "5/10",
+ set: 5.0,
+ fn: "div",
+ param: 10,
+ expect: "0.5"
+}, {
+ label: "1/-1",
+ set: "1",
+ fn: "div",
+ param: -1,
+ expect: "-1"
+}, {
+ label: "4/5 + 13/2",
+ set: "4/5",
+ fn: "add",
+ param: "13/2",
+ expect: "7.3"
+}, {
+ label: "4/5 + 61/2",
+ set: "4/5",
+ fn: "add",
+ param: "61/2",
+ expect: "31.3"
+}, {
+ label: "0.8 + 6.5",
+ set: "0.8",
+ fn: "add",
+ param: "6.5",
+ expect: "7.3"
+}, {
+ label: "2/7 inverse",
+ set: "2/7",
+ fn: "inverse",
+ param: null,
+ expect: "3.5"
+}, {
+ label: "neg 1/3",
+ set: "1/3",
+ fn: "neg",
+ param: null,
+ expect: "-0.(3)"
+}, {
+ label: "1/2+1/3",
+ set: "1/2",
+ fn: "add",
+ param: "1/3",
+ expect: "0.8(3)"
+}, {
+ label: "1/2+3",
+ set: ".5",
+ fn: "add",
+ param: 3,
+ expect: "3.5"
+}, {
+ label: "1/2+3.14",
+ set: "1/2",
+ fn: "add",
+ param: "3.14",
+ expect: "3.64"
+}, {
+ label: "3.5 < 4.1",
+ set: 3.5,
+ fn: "compare",
+ param: 4.1,
+ expect: "-1"
+}, {
+ label: "3.5 > 4.1",
+ set: 4.1,
+ fn: "compare",
+ param: 3.1,
+ expect: "1"
+}, {
+ label: "-3.5 > -4.1",
+ set: -3.5,
+ fn: "compare",
+ param: -4.1,
+ expect: "1"
+}, {
+ label: "-3.5 > -4.1",
+ set: -4.1,
+ fn: "compare",
+ param: -3.5,
+ expect: "-1"
+}, {
+ label: "4.3 == 4.3",
+ set: 4.3,
+ fn: "compare",
+ param: 4.3,
+ expect: "0"
+}, {
+ label: "-4.3 == -4.3",
+ set: -4.3,
+ fn: "compare",
+ param: -4.3,
+ expect: "0"
+}, {
+ label: "-4.3 < 4.3",
+ set: -4.3,
+ fn: "compare",
+ param: 4.3,
+ expect: "-1"
+}, {
+ label: "4.3 == -4.3",
+ set: 4.3,
+ fn: "compare",
+ param: -4.3,
+ expect: "1"
+}, {
+ label: "2^0.5",
+ set: 2,
+ fn: "pow",
+ param: 0.5,
+ expect: "null"
+}, {
+ label: "(-8/27)^(1/3)",
+ set: [-8, 27],
+ fn: "pow",
+ param: [1, 3],
+ expect: "null"
+}, {
+ label: "(-8/27)^(2/3)",
+ set: [-8, 27],
+ fn: "pow",
+ param: [2, 3],
+ expect: "null"
+}, {
+ label: "(-32/243)^(5/3)",
+ set: [-32, 243],
+ fn: "pow",
+ param: [5, 3],
+ expect: "null"
+}, {
+ label: "sqrt(0)",
+ set: 0,
+ fn: "pow",
+ param: 0.5,
+ expect: "0"
+}, {
+ label: "27^(2/3)",
+ set: 27,
+ fn: "pow",
+ param: "2/3",
+ expect: "9"
+}, {
+ label: "(243/1024)^(2/5)",
+ set: "243/1024",
+ fn: "pow",
+ param: "2/5",
+ expect: "0.5625"
+}, {
+ label: "-0.5^-3",
+ set: -0.5,
+ fn: "pow",
+ param: -3,
+ expect: "-8"
+}, {
+ label: "",
+ set: -3,
+ fn: "pow",
+ param: -3,
+ expect: "-0.(037)"
+}, {
+ label: "-3",
+ set: -3,
+ fn: "pow",
+ param: 2,
+ expect: "9"
+}, {
+ label: "-3",
+ set: -3,
+ fn: "pow",
+ param: 3,
+ expect: "-27"
+}, {
+ label: "0^0",
+ set: 0,
+ fn: "pow",
+ param: 0,
+ expect: "1"
+}, {
+ label: "2/3^7",
+ set: [2, 3],
+ fn: "pow",
+ param: 7,
+ expect: "0.(058527663465935070873342478280749885688157293095564700502972107910379515317786922725194330132601737540009144947416552354823959762231367169638774577046181984453589391860996799268404206675811614083219021490626428898033836305441243712848651120256)"
+}, {
+ label: "-0.6^4",
+ set: -0.6,
+ fn: "pow",
+ param: 4,
+ expect: "0.1296"
+}, {
+ label: "8128371:12394 - 8128371/12394",
+ set: "8128371:12394",
+ fn: "sub",
+ param: "8128371/12394",
+ expect: "0"
+}, {
+ label: "3/4 + 1/4",
+ set: "3/4",
+ fn: "add",
+ param: "1/4",
+ expect: "1"
+}, {
+ label: "1/10 + 2/10",
+ set: "1/10",
+ fn: "add",
+ param: "2/10",
+ expect: "0.3"
+}, {
+ label: "5/10 + 2/10",
+ set: "5/10",
+ fn: "add",
+ param: "2/10",
+ expect: "0.7"
+}, {
+ label: "18/10 + 2/10",
+ set: "18/10",
+ fn: "add",
+ param: "2/10",
+ expect: "2"
+}, {
+ label: "1/3 + 1/6",
+ set: "1/3",
+ fn: "add",
+ param: "1/6",
+ expect: "0.5"
+}, {
+ label: "1/3 + 2/6",
+ set: "1/3",
+ fn: "add",
+ param: "2/6",
+ expect: "0.(6)"
+}, {
+ label: "3/4 / 1/4",
+ set: "3/4",
+ fn: "div",
+ param: "1/4",
+ expect: "3"
+}, {
+ label: "1/10 / 2/10",
+ set: "1/10",
+ fn: "div",
+ param: "2/10",
+ expect: "0.5"
+}, {
+ label: "5/10 / 2/10",
+ set: "5/10",
+ fn: "div",
+ param: "2/10",
+ expect: "2.5"
+}, {
+ label: "18/10 / 2/10",
+ set: "18/10",
+ fn: "div",
+ param: "2/10",
+ expect: "9"
+}, {
+ label: "1/3 / 1/6",
+ set: "1/3",
+ fn: "div",
+ param: "1/6",
+ expect: "2"
+}, {
+ label: "1/3 / 2/6",
+ set: "1/3",
+ fn: "div",
+ param: "2/6",
+ expect: "1"
+}, {
+ label: "3/4 * 1/4",
+ set: "3/4",
+ fn: "mul",
+ param: "1/4",
+ expect: "0.1875"
+}, {
+ label: "1/10 * 2/10",
+ set: "1/10",
+ fn: "mul",
+ param: "2/10",
+ expect: "0.02"
+}, {
+ label: "5/10 * 2/10",
+ set: "5/10",
+ fn: "mul",
+ param: "2/10",
+ expect: "0.1"
+}, {
+ label: "18/10 * 2/10",
+ set: "18/10",
+ fn: "mul",
+ param: "2/10",
+ expect: "0.36"
+}, {
+ label: "1/3 * 1/6",
+ set: "1/3",
+ fn: "mul",
+ param: "1/6",
+ expect: "0.0(5)"
+}, {
+ label: "1/3 * 2/6",
+ set: "1/3",
+ fn: "mul",
+ param: "2/6",
+ expect: "0.(1)"
+}, {
+ label: "5/4 - 1/4",
+ set: "5/4",
+ fn: "sub",
+ param: "1/4",
+ expect: "1"
+}, {
+ label: "5/10 - 2/10",
+ set: "5/10",
+ fn: "sub",
+ param: "2/10",
+ expect: "0.3"
+}, {
+ label: "9/10 - 2/10",
+ set: "9/10",
+ fn: "sub",
+ param: "2/10",
+ expect: "0.7"
+}, {
+ label: "22/10 - 2/10",
+ set: "22/10",
+ fn: "sub",
+ param: "2/10",
+ expect: "2"
+}, {
+ label: "2/3 - 1/6",
+ set: "2/3",
+ fn: "sub",
+ param: "1/6",
+ expect: "0.5"
+}, {
+ label: "3/3 - 2/6",
+ set: "3/3",
+ fn: "sub",
+ param: "2/6",
+ expect: "0.(6)"
+}, {
+ label: "0.006999999999999999",
+ set: 0.006999999999999999,
+ fn: "add",
+ param: 0,
+ expect: "0.007"
+}, {
+ label: "1/7 - 1",
+ set: 1 / 7,
+ fn: "add",
+ param: -1,
+ expect: "-0.(857142)"
+}, {
+ label: "0.0065 + 0.0005",
+ set: 0.0065,
+ fn: "add",
+ param: 0.0005,
+ expect: "0.007"
+}, {
+ label: "6.5/.5",
+ set: 6.5,
+ fn: "div",
+ param: .5,
+ expect: "13"
+}, {
+ label: "0.999999999999999999999999999",
+ set: 0.999999999999999999999999999,
+ fn: "sub",
+ param: 1,
+ expect: "0"
+}, {
+ label: "0.5833333333333334",
+ set: 0.5833333333333334,
+ fn: "add",
+ param: 0,
+ expect: "0.58(3)"
+}, {
+ label: "1.75/3",
+ set: 1.75 / 3,
+ fn: "add",
+ param: 0,
+ expect: "0.58(3)"
+}, {
+ label: "3.3333333333333",
+ set: 3.3333333333333,
+ fn: "add",
+ param: 0,
+ expect: "3.(3)"
+}, {
+ label: "4.285714285714285714285714",
+ set: 4.285714285714285714285714,
+ fn: "add",
+ param: 0,
+ expect: "4.(285714)"
+}, {
+ label: "-4",
+ set: -4,
+ fn: "neg",
+ param: 0,
+ expect: "4"
+}, {
+ label: "4",
+ set: 4,
+ fn: "neg",
+ param: 0,
+ expect: "-4"
+}, {
+ label: "0",
+ set: 0,
+ fn: "neg",
+ param: 0,
+ expect: "0"
+}, {
+ label: "6869570742453802/5329686054127205",
+ set: "6869570742453802/5329686054127205",
+ fn: "neg",
+ param: 0,
+ expect: "-1.288925965373540"
+}, {
+ label: "686970702/53212205",
+ set: "686970702/53212205",
+ fn: "neg",
+ param: 0,
+ expect: "-12.910021338149772"
+}, {
+ label: "1/3000000000000000",
+ set: "1/3000000000000000",
+ fn: "add",
+ param: 0,
+ expect: "0.000000000000000(3)"
+}, {
+ label: "toString(15) .0000000000000003",
+ set: ".0000000000000003",
+ fn: "toString",
+ param: 15,
+ expect: "0.000000000000000"
+}, {
+ label: "toString(16) .0000000000000003",
+ set: ".0000000000000003",
+ fn: "toString",
+ param: 16,
+ expect: "0.0000000000000003"
+}, {
+ label: "12 / 4.3",
+ set: 12,
+ set2: 4.3,
+ fn: "toString",
+ param: null,
+ expectError: NonIntegerParameter()
+}, {
+ label: "12.5 / 4",
+ set: 12.5,
+ set2: 4,
+ fn: "toString",
+ param: null,
+ expectError: NonIntegerParameter()
+}, {
+ label: "0.9 round to multiple of 1/8",
+ set: .9,
+ fn: "roundTo",
+ param: "1/8",
+ expect: "0.875"
+}, {
+ label: "1/3 round to multiple of 1/16",
+ set: 1 / 3,
+ fn: "roundTo",
+ param: "1/16",
+ expect: "0.3125"
+}, {
+ label: "1/3 round to multiple of 1/16",
+ set: -1 / 3,
+ fn: "roundTo",
+ param: "1/16",
+ expect: "-0.3125"
+}, {
+ label: "1/2 round to multiple of 1/4",
+ set: 1 / 2,
+ fn: "roundTo",
+ param: "1/4",
+ expect: "0.5"
+}, {
+ label: "1/4 round to multiple of 1/2",
+ set: 1 / 4,
+ fn: "roundTo",
+ param: "1/2",
+ expect: "0.5"
+}, {
+ label: "10/3 round to multiple of 1/2",
+ set: "10/3",
+ fn: "roundTo",
+ param: "1/2",
+ expect: "3.5"
+}, {
+ label: "-10/3 round to multiple of 1/2",
+ set: "-10/3",
+ fn: "roundTo",
+ param: "1/2",
+ expect: "-3.5"
+}, {
+ label: "log_2(8)",
+ set: "8",
+ fn: "log",
+ param: "2",
+ expect: "3" // because 2^3 = 8
+}, {
+ label: "log_2(3)",
+ set: "3",
+ fn: "log",
+ param: "2",
+ expect: 'null' // because 2^(p/q) != 3
+}, {
+ label: "log_10(1000)",
+ set: "1000",
+ fn: "log",
+ param: "10",
+ expect: "3" // because 10^3 = 1000
+}, {
+ label: "log_27(81)",
+ set: "81",
+ fn: "log",
+ param: "27",
+ expect: "1.(3)" // because 27^(4/3) = 81
+}, {
+ label: "log_27(9)",
+ set: "9",
+ fn: "log",
+ param: "27",
+ expect: "0.(6)" // because 27^(2/3) = 9
+}, {
+ label: "log_9/4(27/8)",
+ set: "27/8",
+ fn: "log",
+ param: "9/4",
+ expect: "1.5" // because (9/4)^(3/2) = 27/8
+}];
+
+describe('Fraction', function () {
+ for (var i = 0; i < tests.length; i++) {
+
+ (function (i) {
+ var action;
+
+ if (tests[i].fn) {
+ action = function () {
+ var x = Fraction(tests[i].set, tests[i].set2)[tests[i].fn](tests[i].param);
+ if (x === null) return "null";
+ return x.toString();
+ };
+ } else {
+ action = function () {
+ var x = new Fraction(tests[i].set, tests[i].set2);
+ if (x === null) return "null";
+ return x.toString();
+ };
+ }
+
+ it(String(tests[i].label || tests[i].set), function () {
+ if (tests[i].expectError) {
+ assert.throws(action, tests[i].expectError);
+ } else {
+ assert.equal(action(), tests[i].expect);
+ }
+ });
+
+ })(i);
+ }
+});
+
+describe('JSON', function () {
+
+ it("Should be possible to stringify the object", function () {
+
+ if (typeof Fraction(1).n !== 'number') {
+ return;
+ }
+ assert.equal('{"s":1,"n":14623,"d":330}', JSON.stringify(new Fraction("44.3(12)")));
+ assert.equal('{"s":-1,"n":2,"d":1}', JSON.stringify(new Fraction(-1 / 2).inverse()));
+ });
+});
+
+describe('Arguments', function () {
+
+ it("Should be possible to use different kind of params", function () {
+
+ // String
+ var fraction = new Fraction("0.1");
+ assert.equal("1/10", fraction.n + "/" + fraction.d);
+
+ var fraction = new Fraction("6234/6460");
+ assert.equal("3117/3230", fraction.n + "/" + fraction.d);
+
+ // Two params
+ var fraction = new Fraction(1, 2);
+ assert.equal("1/2", fraction.n + "/" + fraction.d);
+
+ // Object
+ var fraction = new Fraction({ n: 1, d: 3 });
+ assert.equal("1/3", fraction.n + "/" + fraction.d);
+
+ // Array
+ var fraction = new Fraction([1, 4]);
+ assert.equal("1/4", fraction.n + "/" + fraction.d);
+ });
+});
+
+describe('fractions', function () {
+
+ it("Should pass 0.08 = 2/25", function () {
+
+ var fraction = new Fraction("0.08");
+ assert.equal("2/25", fraction.n + "/" + fraction.d);
+ });
+
+ it("Should pass 0.200 = 1/5", function () {
+
+ var fraction = new Fraction("0.200");
+ assert.equal("1/5", fraction.n + "/" + fraction.d);
+ });
+
+ it("Should pass 0.125 = 1/8", function () {
+
+ var fraction = new Fraction("0.125");
+ assert.equal("1/8", fraction.n + "/" + fraction.d);
+ });
+
+ it("Should pass 8.36 = 209/25", function () {
+
+ var fraction = new Fraction(8.36);
+ assert.equal("209/25", fraction.n + "/" + fraction.d);
+ });
+
+});
+
+describe('constructors', function () {
+
+ it("Should pass 0.08 = 2/25", function () {
+
+ var tmp = new Fraction({ d: 4, n: 2, s: -1 });
+ assert.equal("-1/2", tmp.s * tmp.n + "/" + tmp.d);
+
+ var tmp = new Fraction(-88.3);
+ assert.equal("-883/10", tmp.s * tmp.n + "/" + tmp.d);
+
+ var tmp = new Fraction(-88.3).clone();
+ assert.equal("-883/10", tmp.s * tmp.n + "/" + tmp.d);
+
+ var tmp = new Fraction("123.'3'");
+ assert.equal("370/3", tmp.s * tmp.n + "/" + tmp.d);
+
+ var tmp = new Fraction("123.'3'").clone();
+ assert.equal("370/3", tmp.s * tmp.n + "/" + tmp.d);
+
+ var tmp = new Fraction([-1023461776, 334639305]);
+ tmp = tmp.add([4, 25]);
+ assert.equal("-4849597436/1673196525", tmp.s * tmp.n + "/" + tmp.d);
+ });
+});
+
+describe('Latex Output', function () {
+
+ it("Should pass 123.'3' = \\frac{370}{3}", function () {
+
+ var tmp = new Fraction("123.'3'");
+ assert.equal("\\frac{370}{3}", tmp.toLatex());
+ });
+
+ it("Should pass 1.'3' = \\frac{4}{3}", function () {
+
+ var tmp = new Fraction("1.'3'");
+ assert.equal("\\frac{4}{3}", tmp.toLatex());
+ });
+
+ it("Should pass -1.0000000000 = -1", function () {
+
+ var tmp = new Fraction("-1.0000000000");
+ assert.equal('-1', tmp.toLatex());
+ });
+
+ it("Should pass -0.0000000000 = 0", function () {
+
+ var tmp = new Fraction("-0.0000000000");
+ assert.equal('0', tmp.toLatex());
+ });
+});
+
+describe('Fraction Output', function () {
+
+ it("Should pass 123.'3' = 123 1/3", function () {
+
+ var tmp = new Fraction("123.'3'");
+ assert.equal('370/3', tmp.toFraction());
+ });
+
+ it("Should pass 1.'3' = 1 1/3", function () {
+
+ var tmp = new Fraction("1.'3'");
+ assert.equal('4/3', tmp.toFraction());
+ });
+
+ it("Should pass -1.0000000000 = -1", function () {
+
+ var tmp = new Fraction("-1.0000000000");
+ assert.equal('-1', tmp.toFraction());
+ });
+
+ it("Should pass -0.0000000000 = 0", function () {
+
+ var tmp = new Fraction("-0.0000000000");
+ assert.equal('0', tmp.toFraction());
+ });
+
+ it("Should pass 1/-99/293 = -1/29007", function () {
+
+ var tmp = new Fraction(-99).inverse().div(293);
+ assert.equal('-1/29007', tmp.toFraction());
+ });
+
+ it('Should work with large calculations', function () {
+ var x = Fraction(1123875);
+ var y = Fraction(1238750184);
+ var z = Fraction(1657134);
+ var r = Fraction(77344464613500, 92063);
+ assert.equal(x.mul(y).div(z).toFraction(), r.toFraction());
+ });
+});
+
+describe('Fraction toContinued', function () {
+
+ it("Should pass 415/93", function () {
+
+ var tmp = new Fraction(415, 93);
+ assert.equal('4,2,6,7', tmp.toContinued().toString());
+ });
+
+ it("Should pass 0/2", function () {
+
+ var tmp = new Fraction(0, 2);
+ assert.equal('0', tmp.toContinued().toString());
+ });
+
+ it("Should pass 1/7", function () {
+
+ var tmp = new Fraction(1, 7);
+ assert.equal('0,7', tmp.toContinued().toString());
+ });
+
+ it("Should pass 23/88", function () {
+
+ var tmp = new Fraction('23/88');
+ assert.equal('0,3,1,4,1,3', tmp.toContinued().toString());
+ });
+
+ it("Should pass 1/99", function () {
+
+ var tmp = new Fraction('1/99');
+ assert.equal('0,99', tmp.toContinued().toString());
+ });
+
+ it("Should pass 1768/99", function () {
+
+ var tmp = new Fraction('1768/99');
+ assert.equal('17,1,6,14', tmp.toContinued().toString());
+ });
+
+ it("Should pass 1768/99", function () {
+
+ var tmp = new Fraction('7/8');
+ assert.equal('0,1,7', tmp.toContinued().toString());
+ });
+
+});
+
+
+describe('Fraction simplify', function () {
+
+ it("Should pass 415/93", function () {
+
+ var tmp = new Fraction(415, 93);
+ assert.equal('9/2', tmp.simplify(0.1).toFraction());
+ assert.equal('58/13', tmp.simplify(0.01).toFraction());
+ assert.equal('415/93', tmp.simplify(0.0001).toFraction());
+ });
+
+});
diff --git a/node_modules/ioredis/LICENSE b/node_modules/ioredis/LICENSE
new file mode 100644
index 0000000..1d4bd28
--- /dev/null
+++ b/node_modules/ioredis/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2022 Zihua Li
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/node_modules/ioredis/README.md b/node_modules/ioredis/README.md
new file mode 100644
index 0000000..cd47b60
--- /dev/null
+++ b/node_modules/ioredis/README.md
@@ -0,0 +1,1540 @@
+[](https://github.com/redis/ioredis)
+
+[](https://github.com/redis/ioredis/actions/workflows/release.yml?query=branch%3Amain)
+[](https://coveralls.io/github/luin/ioredis?branch=main)
+[](http://commitizen.github.io/cz-cli/)
+[](https://github.com/semantic-release/semantic-release)
+
+[](https://discord.gg/redis)
+[](https://www.twitch.tv/redisinc)
+[](https://www.youtube.com/redisinc)
+[](https://twitter.com/redisinc)
+
+A robust, performance-focused and full-featured [Redis](http://redis.io) client for [Node.js](https://nodejs.org).
+
+Supports Redis >= 2.6.12. Completely compatible with Redis 7.x.
+
+ioredis is a stable project and maintenance is done on a best-effort basis for relevant issues (contributions to ioredis will still be evaluated, reviewed, and merged when they benefit the project). For new projects, node-redis is the recommended client library. [node-redis](https://github.com/redis/node-redis) is the open-source (MIT license) Redis JavaScript client library redesigned from the ground up and actively maintained. [node-redis](https://github.com/redis/node-redis) supports new (hash-field expiration) and future commands and the capabilities available in Redis Stack and Redis 8 (search, JSON, time-series, probabilistic data structures).
+
+# Features
+
+ioredis is a robust, full-featured Redis client that is
+used in the world's biggest online commerce company [Alibaba](http://www.alibaba.com/) and many other awesome companies.
+
+0. Full-featured. It supports [Cluster](http://redis.io/topics/cluster-tutorial), [Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/), [Streams](https://redis.io/topics/streams-intro), [Pipelining](http://redis.io/topics/pipelining), and of course [Lua scripting](http://redis.io/commands/eval), [Redis Functions](https://redis.io/topics/functions-intro), [Pub/Sub](http://redis.io/topics/pubsub) (with the support of binary messages).
+1. High performance 🚀.
+2. Delightful API 😄. It works with Node callbacks and Native promises.
+3. Transformation of command arguments and replies.
+4. Transparent key prefixing.
+5. Abstraction for Lua scripting, allowing you to [define custom commands](https://github.com/redis/ioredis#lua-scripting).
+6. Supports [binary data](https://github.com/redis/ioredis#handle-binary-data).
+7. Supports [TLS](https://github.com/redis/ioredis#tls-options) 🔒.
+8. Supports offline queue and ready checking.
+9. Supports ES6 types, such as `Map` and `Set`.
+10. Supports GEO commands 📍.
+11. Supports Redis ACL.
+12. Sophisticated error handling strategy.
+13. Supports NAT mapping.
+14. Supports autopipelining.
+
+**100% written in TypeScript and official declarations are provided:**
+
+
+
+# Versions
+
+| Version | Branch | Node.js Version | Redis Version |
+| -------------- | ------ | --------------- | --------------- |
+| 5.x.x (latest) | main | >= 12 | 2.6.12 ~ latest |
+| 4.x.x | v4 | >= 8 | 2.6.12 ~ 7 |
+
+Refer to [CHANGELOG.md](CHANGELOG.md) for features and bug fixes introduced in v5.
+
+🚀 [Upgrading from v4 to v5](https://github.com/redis/ioredis/wiki/Upgrading-from-v4-to-v5)
+
+# Links
+
+- [API Documentation](https://redis.github.io/ioredis/) ([Redis](https://redis.github.io/ioredis/classes/Redis.html), [Cluster](https://redis.github.io/ioredis/classes/Cluster.html))
+- [Changelog](CHANGELOG.md)
+
+
+
+# Quick Start
+
+## Install
+
+```shell
+npm install ioredis
+```
+
+In a TypeScript project, you may want to add TypeScript declarations for Node.js:
+
+```shell
+npm install --save-dev @types/node
+```
+
+## Basic Usage
+
+```javascript
+// Import ioredis.
+// You can also use `import { Redis } from "ioredis"`
+// if your project is a TypeScript project,
+// Note that `import Redis from "ioredis"` is still supported,
+// but will be deprecated in the next major version.
+const Redis = require("ioredis");
+
+// Create a Redis instance.
+// By default, it will connect to localhost:6379.
+// We are going to cover how to specify connection options soon.
+const redis = new Redis();
+
+redis.set("mykey", "value"); // Returns a promise which resolves to "OK" when the command succeeds.
+
+// ioredis supports the node.js callback style
+redis.get("mykey", (err, result) => {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log(result); // Prints "value"
+ }
+});
+
+// Or ioredis returns a promise if the last argument isn't a function
+redis.get("mykey").then((result) => {
+ console.log(result); // Prints "value"
+});
+
+redis.zadd("sortedSet", 1, "one", 2, "dos", 4, "quatro", 3, "three");
+redis.zrange("sortedSet", 0, 2, "WITHSCORES").then((elements) => {
+ // ["one", "1", "dos", "2", "three", "3"] as if the command was `redis> ZRANGE sortedSet 0 2 WITHSCORES`
+ console.log(elements);
+});
+
+// All arguments are passed directly to the redis server,
+// so technically ioredis supports all Redis commands.
+// The format is: redis[SOME_REDIS_COMMAND_IN_LOWERCASE](ARGUMENTS_ARE_JOINED_INTO_COMMAND_STRING)
+// so the following statement is equivalent to the CLI: `redis> SET mykey hello EX 10`
+redis.set("mykey", "hello", "EX", 10);
+```
+
+See the `examples/` folder for more examples. For example:
+
+- [TTL](examples/ttl.js)
+- [Strings](examples/string.js)
+- [Hashes](examples/hash.js)
+- [Lists](examples/list.js)
+- [Sets](examples/set.js)
+- [Sorted Sets](examples/zset.js)
+- [Streams](examples/stream.js)
+- [Redis Modules](examples/module.js) e.g. RedisJSON
+
+All Redis commands are supported. See [the documentation](https://redis.github.io/ioredis/classes/Redis.html) for details.
+
+## Connect to Redis
+
+When a new `Redis` instance is created,
+a connection to Redis will be created at the same time.
+You can specify which Redis to connect to by:
+
+```javascript
+new Redis(); // Connect to 127.0.0.1:6379
+new Redis(6380); // 127.0.0.1:6380
+new Redis(6379, "192.168.1.1"); // 192.168.1.1:6379
+new Redis("/tmp/redis.sock");
+new Redis({
+ port: 6379, // Redis port
+ host: "127.0.0.1", // Redis host
+ username: "default", // needs Redis >= 6
+ password: "my-top-secret",
+ db: 0, // Defaults to 0
+});
+```
+
+You can also specify connection options as a [`redis://` URL](http://www.iana.org/assignments/uri-schemes/prov/redis) or [`rediss://` URL](https://www.iana.org/assignments/uri-schemes/prov/rediss) when using [TLS encryption](#tls-options):
+
+```javascript
+// Connect to 127.0.0.1:6380, db 4, using password "authpassword":
+new Redis("redis://:authpassword@127.0.0.1:6380/4");
+
+// Username can also be passed via URI.
+new Redis("redis://username:authpassword@127.0.0.1:6380/4");
+```
+
+See [API Documentation](https://redis.github.io/ioredis/index.html#RedisOptions) for all available options.
+
+## Pub/Sub
+
+Redis provides several commands for developers to implement the [Publish–subscribe pattern](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern). There are two roles in this pattern: publisher and subscriber. Publishers are not programmed to send their messages to specific subscribers. Rather, published messages are characterized into channels, without knowledge of what (if any) subscribers there may be.
+
+By leveraging Node.js's built-in events module, ioredis makes pub/sub very straightforward to use. Below is a simple example that consists of two files, one is publisher.js that publishes messages to a channel, the other is subscriber.js that listens for messages on specific channels.
+
+```javascript
+// publisher.js
+
+const Redis = require("ioredis");
+const redis = new Redis();
+
+setInterval(() => {
+ const message = { foo: Math.random() };
+ // Publish to my-channel-1 or my-channel-2 randomly.
+ const channel = `my-channel-${1 + Math.round(Math.random())}`;
+
+ // Message can be either a string or a buffer
+ redis.publish(channel, JSON.stringify(message));
+ console.log("Published %s to %s", message, channel);
+}, 1000);
+```
+
+```javascript
+// subscriber.js
+
+const Redis = require("ioredis");
+const redis = new Redis();
+
+redis.subscribe("my-channel-1", "my-channel-2", (err, count) => {
+ if (err) {
+ // Just like other commands, subscribe() can fail for some reasons,
+ // ex network issues.
+ console.error("Failed to subscribe: %s", err.message);
+ } else {
+ // `count` represents the number of channels this client are currently subscribed to.
+ console.log(
+ `Subscribed successfully! This client is currently subscribed to ${count} channels.`
+ );
+ }
+});
+
+redis.on("message", (channel, message) => {
+ console.log(`Received ${message} from ${channel}`);
+});
+
+// There's also an event called 'messageBuffer', which is the same as 'message' except
+// it returns buffers instead of strings.
+// It's useful when the messages are binary data.
+redis.on("messageBuffer", (channel, message) => {
+ // Both `channel` and `message` are buffers.
+ console.log(channel, message);
+});
+```
+
+It's worth noticing that a connection (aka a `Redis` instance) can't play both roles at the same time. More specifically, when a client issues `subscribe()` or `psubscribe()`, it enters the "subscriber" mode. From that point, only commands that modify the subscription set are valid. Namely, they are: `subscribe`, `psubscribe`, `unsubscribe`, `punsubscribe`, `ping`, and `quit`. When the subscription set is empty (via `unsubscribe`/`punsubscribe`), the connection is put back into the regular mode.
+
+If you want to do pub/sub in the same file/process, you should create a separate connection:
+
+```javascript
+const Redis = require("ioredis");
+const sub = new Redis();
+const pub = new Redis();
+
+sub.subscribe(/* ... */); // From now, `sub` enters the subscriber mode.
+sub.on("message" /* ... */);
+
+setInterval(() => {
+ // `pub` can be used to publish messages, or send other regular commands (e.g. `hgetall`)
+ // because it's not in the subscriber mode.
+ pub.publish(/* ... */);
+}, 1000);
+```
+
+`PSUBSCRIBE` is also supported in a similar way when you want to subscribe all channels whose name matches a pattern:
+
+```javascript
+redis.psubscribe("pat?ern", (err, count) => {});
+
+// Event names are "pmessage"/"pmessageBuffer" instead of "message/messageBuffer".
+redis.on("pmessage", (pattern, channel, message) => {});
+redis.on("pmessageBuffer", (pattern, channel, message) => {});
+```
+
+## Streams
+
+Redis v5 introduces a new data type called streams. It doubles as a communication channel for building streaming architectures and as a log-like data structure for persisting data. With ioredis, the usage can be pretty straightforward. Say we have a producer publishes messages to a stream with `redis.xadd("mystream", "*", "randomValue", Math.random())` (You may find the [official documentation of Streams](https://redis.io/topics/streams-intro) as a starter to understand the parameters used), to consume the messages, we'll have a consumer with the following code:
+
+```javascript
+const Redis = require("ioredis");
+const redis = new Redis();
+
+const processMessage = (message) => {
+ console.log("Id: %s. Data: %O", message[0], message[1]);
+};
+
+async function listenForMessage(lastId = "$") {
+ // `results` is an array, each element of which corresponds to a key.
+ // Because we only listen to one key (mystream) here, `results` only contains
+ // a single element. See more: https://redis.io/commands/xread#return-value
+ const results = await redis.xread("BLOCK", 0, "STREAMS", "mystream", lastId);
+ const [key, messages] = results[0]; // `key` equals to "mystream"
+
+ messages.forEach(processMessage);
+
+ // Pass the last id of the results to the next round.
+ await listenForMessage(messages[messages.length - 1][0]);
+}
+
+listenForMessage();
+```
+
+## Expiration
+
+Redis can set a timeout to expire your key, after the timeout has expired the key will be automatically deleted. (You can find the [official Expire documentation](https://redis.io/commands/expire/) to understand better the different parameters you can use), to set your key to expire in 60 seconds, we will have the following code:
+
+```javascript
+redis.set("key", "data", "EX", 60);
+// Equivalent to redis command "SET key data EX 60", because on ioredis set method,
+// all arguments are passed directly to the redis server.
+```
+
+## Handle Binary Data
+
+Binary data support is out of the box. Pass buffers to send binary data:
+
+```javascript
+redis.set("foo", Buffer.from([0x62, 0x75, 0x66]));
+```
+
+Every command that returns a [bulk string](https://redis.io/docs/reference/protocol-spec/#resp-bulk-strings)
+has a variant command with a `Buffer` suffix. The variant command returns a buffer instead of a UTF-8 string:
+
+```javascript
+const result = await redis.getBuffer("foo");
+// result is ``
+```
+
+It's worth noticing that you don't need the `Buffer` suffix variant in order to **send** binary data. That means
+in most case you should just use `redis.set()` instead of `redis.setBuffer()` unless you want to get the old value
+with the `GET` parameter:
+
+```javascript
+const result = await redis.setBuffer("foo", "new value", "GET");
+// result is `` as `GET` indicates returning the old value.
+```
+
+## Pipelining
+
+If you want to send a batch of commands (e.g. > 5), you can use pipelining to queue
+the commands in memory and then send them to Redis all at once. This way the performance improves by 50%~300% (See [benchmark section](#benchmarks)).
+
+`redis.pipeline()` creates a `Pipeline` instance. You can call any Redis
+commands on it just like the `Redis` instance. The commands are queued in memory
+and flushed to Redis by calling the `exec` method:
+
+```javascript
+const pipeline = redis.pipeline();
+pipeline.set("foo", "bar");
+pipeline.del("cc");
+pipeline.exec((err, results) => {
+ // `err` is always null, and `results` is an array of responses
+ // corresponding to the sequence of queued commands.
+ // Each response follows the format `[err, result]`.
+});
+
+// You can even chain the commands:
+redis
+ .pipeline()
+ .set("foo", "bar")
+ .del("cc")
+ .exec((err, results) => {});
+
+// `exec` also returns a Promise:
+const promise = redis.pipeline().set("foo", "bar").get("foo").exec();
+promise.then((result) => {
+ // result === [[null, 'OK'], [null, 'bar']]
+});
+```
+
+Each chained command can also have a callback, which will be invoked when the command
+gets a reply:
+
+```javascript
+redis
+ .pipeline()
+ .set("foo", "bar")
+ .get("foo", (err, result) => {
+ // result === 'bar'
+ })
+ .exec((err, result) => {
+ // result[1][1] === 'bar'
+ });
+```
+
+In addition to adding commands to the `pipeline` queue individually, you can also pass an array of commands and arguments to the constructor:
+
+```javascript
+redis
+ .pipeline([
+ ["set", "foo", "bar"],
+ ["get", "foo"],
+ ])
+ .exec(() => {
+ /* ... */
+ });
+```
+
+`#length` property shows how many commands in the pipeline:
+
+```javascript
+const length = redis.pipeline().set("foo", "bar").get("foo").length;
+// length === 2
+```
+
+## Transaction
+
+Most of the time, the transaction commands `multi` & `exec` are used together with pipeline.
+Therefore, when `multi` is called, a `Pipeline` instance is created automatically by default,
+so you can use `multi` just like `pipeline`:
+
+```javascript
+redis
+ .multi()
+ .set("foo", "bar")
+ .get("foo")
+ .exec((err, results) => {
+ // results === [[null, 'OK'], [null, 'bar']]
+ });
+```
+
+If there's a syntax error in the transaction's command chain (e.g. wrong number of arguments, wrong command name, etc),
+then none of the commands would be executed, and an error is returned:
+
+```javascript
+redis
+ .multi()
+ .set("foo")
+ .set("foo", "new value")
+ .exec((err, results) => {
+ // err:
+ // { [ReplyError: EXECABORT Transaction discarded because of previous errors.]
+ // name: 'ReplyError',
+ // message: 'EXECABORT Transaction discarded because of previous errors.',
+ // command: { name: 'exec', args: [] },
+ // previousErrors:
+ // [ { [ReplyError: ERR wrong number of arguments for 'set' command]
+ // name: 'ReplyError',
+ // message: 'ERR wrong number of arguments for \'set\' command',
+ // command: [Object] } ] }
+ });
+```
+
+In terms of the interface, `multi` differs from `pipeline` in that when specifying a callback
+to each chained command, the queueing state is passed to the callback instead of the result of the command:
+
+```javascript
+redis
+ .multi()
+ .set("foo", "bar", (err, result) => {
+ // result === 'QUEUED'
+ })
+ .exec(/* ... */);
+```
+
+If you want to use transaction without pipeline, pass `{ pipeline: false }` to `multi`,
+and every command will be sent to Redis immediately without waiting for an `exec` invocation:
+
+```javascript
+redis.multi({ pipeline: false });
+redis.set("foo", "bar");
+redis.get("foo");
+redis.exec((err, result) => {
+ // result === [[null, 'OK'], [null, 'bar']]
+});
+```
+
+The constructor of `multi` also accepts a batch of commands:
+
+```javascript
+redis
+ .multi([
+ ["set", "foo", "bar"],
+ ["get", "foo"],
+ ])
+ .exec(() => {
+ /* ... */
+ });
+```
+
+Inline transactions are supported by pipeline, which means you can group a subset of commands
+in the pipeline into a transaction:
+
+```javascript
+redis
+ .pipeline()
+ .get("foo")
+ .multi()
+ .set("foo", "bar")
+ .get("foo")
+ .exec()
+ .get("foo")
+ .exec();
+```
+
+## Lua Scripting
+
+ioredis supports all of the scripting commands such as `EVAL`, `EVALSHA` and `SCRIPT`.
+However, it's tedious to use in real world scenarios since developers have to take
+care of script caching and to detect when to use `EVAL` and when to use `EVALSHA`.
+ioredis exposes a `defineCommand` method to make scripting much easier to use:
+
+```javascript
+const redis = new Redis();
+
+// This will define a command myecho:
+redis.defineCommand("myecho", {
+ numberOfKeys: 2,
+ lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
+});
+
+// Now `myecho` can be used just like any other ordinary command,
+// and ioredis will try to use `EVALSHA` internally when possible for better performance.
+redis.myecho("k1", "k2", "a1", "a2", (err, result) => {
+ // result === ['k1', 'k2', 'a1', 'a2']
+});
+
+// `myechoBuffer` is also defined automatically to return buffers instead of strings:
+redis.myechoBuffer("k1", "k2", "a1", "a2", (err, result) => {
+ // result[0] equals to Buffer.from('k1');
+});
+
+// And of course it works with pipeline:
+redis.pipeline().set("foo", "bar").myecho("k1", "k2", "a1", "a2").exec();
+```
+
+### Dynamic Keys
+
+If the number of keys can't be determined when defining a command, you can
+omit the `numberOfKeys` property and pass the number of keys as the first argument
+when you call the command:
+
+```javascript
+redis.defineCommand("echoDynamicKeyNumber", {
+ lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
+});
+
+// Now you have to pass the number of keys as the first argument every time
+// you invoke the `echoDynamicKeyNumber` command:
+redis.echoDynamicKeyNumber(2, "k1", "k2", "a1", "a2", (err, result) => {
+ // result === ['k1', 'k2', 'a1', 'a2']
+});
+```
+
+### As Constructor Options
+
+Besides `defineCommand()`, you can also define custom commands with the `scripts` constructor option:
+
+```javascript
+const redis = new Redis({
+ scripts: {
+ myecho: {
+ numberOfKeys: 2,
+ lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
+ },
+ },
+});
+```
+
+### TypeScript Usages
+
+You can refer to [the example](examples/typescript/scripts.ts) for how to declare your custom commands.
+
+## Transparent Key Prefixing
+
+This feature allows you to specify a string that will automatically be prepended
+to all the keys in a command, which makes it easier to manage your key
+namespaces.
+
+**Warning** This feature won't apply to commands like [KEYS](http://redis.io/commands/KEYS) and [SCAN](http://redis.io/commands/scan) that take patterns rather than actual keys([#239](https://github.com/redis/ioredis/issues/239)),
+and this feature also won't apply to the replies of commands even if they are key names ([#325](https://github.com/redis/ioredis/issues/325)).
+
+```javascript
+const fooRedis = new Redis({ keyPrefix: "foo:" });
+fooRedis.set("bar", "baz"); // Actually sends SET foo:bar baz
+
+fooRedis.defineCommand("myecho", {
+ numberOfKeys: 2,
+ lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
+});
+
+// Works well with pipelining/transaction
+fooRedis
+ .pipeline()
+ // Sends SORT foo:list BY foo:weight_*->fieldname
+ .sort("list", "BY", "weight_*->fieldname")
+ // Supports custom commands
+ // Sends EVALSHA xxx foo:k1 foo:k2 a1 a2
+ .myecho("k1", "k2", "a1", "a2")
+ .exec();
+```
+
+## Transforming Arguments & Replies
+
+Most Redis commands take one or more Strings as arguments,
+and replies are sent back as a single String or an Array of Strings. However, sometimes
+you may want something different. For instance, it would be more convenient if the `HGETALL`
+command returns a hash (e.g. `{ key: val1, key2: v2 }`) rather than an array of key values (e.g. `[key1, val1, key2, val2]`).
+
+ioredis has a flexible system for transforming arguments and replies. There are two types
+of transformers, argument transformer and reply transformer:
+
+```javascript
+const Redis = require("ioredis");
+
+// Here's the built-in argument transformer converting
+// hmset('key', { k1: 'v1', k2: 'v2' })
+// or
+// hmset('key', new Map([['k1', 'v1'], ['k2', 'v2']]))
+// into
+// hmset('key', 'k1', 'v1', 'k2', 'v2')
+Redis.Command.setArgumentTransformer("hmset", (args) => {
+ if (args.length === 2) {
+ if (args[1] instanceof Map) {
+ // utils is a internal module of ioredis
+ return [args[0], ...utils.convertMapToArray(args[1])];
+ }
+ if (typeof args[1] === "object" && args[1] !== null) {
+ return [args[0], ...utils.convertObjectToArray(args[1])];
+ }
+ }
+ return args;
+});
+
+// Here's the built-in reply transformer converting the HGETALL reply
+// ['k1', 'v1', 'k2', 'v2']
+// into
+// { k1: 'v1', 'k2': 'v2' }
+Redis.Command.setReplyTransformer("hgetall", (result) => {
+ if (Array.isArray(result)) {
+ const obj = {};
+ for (let i = 0; i < result.length; i += 2) {
+ obj[result[i]] = result[i + 1];
+ }
+ return obj;
+ }
+ return result;
+});
+```
+
+There are three built-in transformers, two argument transformers for `hmset` & `mset` and
+a reply transformer for `hgetall`. Transformers for `hmset` and `hgetall` were mentioned
+above, and the transformer for `mset` is similar to the one for `hmset`:
+
+```javascript
+redis.mset({ k1: "v1", k2: "v2" });
+redis.get("k1", (err, result) => {
+ // result === 'v1';
+});
+
+redis.mset(
+ new Map([
+ ["k3", "v3"],
+ ["k4", "v4"],
+ ])
+);
+redis.get("k3", (err, result) => {
+ // result === 'v3';
+});
+```
+
+Another useful example of a reply transformer is one that changes `hgetall` to return array of arrays instead of objects which avoids an unwanted conversation of hash keys to strings when dealing with binary hash keys:
+
+```javascript
+Redis.Command.setReplyTransformer("hgetall", (result) => {
+ const arr = [];
+ for (let i = 0; i < result.length; i += 2) {
+ arr.push([result[i], result[i + 1]]);
+ }
+ return arr;
+});
+redis.hset("h1", Buffer.from([0x01]), Buffer.from([0x02]));
+redis.hset("h1", Buffer.from([0x03]), Buffer.from([0x04]));
+redis.hgetallBuffer("h1", (err, result) => {
+ // result === [ [ , ], [ , ] ];
+});
+```
+
+## Monitor
+
+Redis supports the MONITOR command,
+which lets you see all commands received by the Redis server across all client connections,
+including from other client libraries and other computers.
+
+The `monitor` method returns a monitor instance.
+After you send the MONITOR command, no other commands are valid on that connection. ioredis will emit a monitor event for every new monitor message that comes across.
+The callback for the monitor event takes a timestamp from the Redis server and an array of command arguments.
+
+Here is a simple example:
+
+```javascript
+redis.monitor((err, monitor) => {
+ monitor.on("monitor", (time, args, source, database) => {});
+});
+```
+
+Here is another example illustrating an `async` function and `monitor.disconnect()`:
+
+```javascript
+async () => {
+ const monitor = await redis.monitor();
+ monitor.on("monitor", console.log);
+ // Any other tasks
+ monitor.disconnect();
+};
+```
+
+## Streamify Scanning
+
+Redis 2.8 added the `SCAN` command to incrementally iterate through the keys in the database. It's different from `KEYS` in that
+`SCAN` only returns a small number of elements each call, so it can be used in production without the downside
+of blocking the server for a long time. However, it requires recording the cursor on the client side each time
+the `SCAN` command is called in order to iterate through all the keys correctly. Since it's a relatively common use case, ioredis
+provides a streaming interface for the `SCAN` command to make things much easier. A readable stream can be created by calling `scanStream`:
+
+```javascript
+const redis = new Redis();
+// Create a readable stream (object mode)
+const stream = redis.scanStream();
+stream.on("data", (resultKeys) => {
+ // `resultKeys` is an array of strings representing key names.
+ // Note that resultKeys may contain 0 keys, and that it will sometimes
+ // contain duplicates due to SCAN's implementation in Redis.
+ for (let i = 0; i < resultKeys.length; i++) {
+ console.log(resultKeys[i]);
+ }
+});
+stream.on("end", () => {
+ console.log("all keys have been visited");
+});
+```
+
+`scanStream` accepts an option, with which you can specify the `MATCH` pattern, the `TYPE` filter, and the `COUNT` argument:
+
+```javascript
+const stream = redis.scanStream({
+ // only returns keys following the pattern of `user:*`
+ match: "user:*",
+ // only return objects that match a given type,
+ // (requires Redis >= 6.0)
+ type: "zset",
+ // returns approximately 100 elements per call
+ count: 100,
+});
+```
+
+Just like other commands, `scanStream` has a binary version `scanBufferStream`, which returns an array of buffers. It's useful when
+the key names are not utf8 strings.
+
+There are also `hscanStream`, `zscanStream` and `sscanStream` to iterate through elements in a hash, zset and set. The interface of each is
+similar to `scanStream` except the first argument is the key name:
+
+```javascript
+const stream = redis.zscanStream("myhash", {
+ match: "age:??",
+});
+```
+
+The `hscanStream` also accepts the `noValues` option to specify whether Redis should return only the keys in the hash table without their corresponding values.
+
+```javascript
+const stream = redis.hscanStream("myhash", {
+ match: "age:??",
+ noValues: true,
+});
+```
+
+You can learn more from the [Redis documentation](http://redis.io/commands/scan).
+
+**Useful Tips**
+It's pretty common that doing an async task in the `data` handler. We'd like the scanning process to be paused until the async task to be finished. `Stream#pause()` and `Stream#resume()` do the trick. For example if we want to migrate data in Redis to MySQL:
+
+```javascript
+const stream = redis.scanStream();
+stream.on("data", (resultKeys) => {
+ // Pause the stream from scanning more keys until we've migrated the current keys.
+ stream.pause();
+
+ Promise.all(resultKeys.map(migrateKeyToMySQL)).then(() => {
+ // Resume the stream here.
+ stream.resume();
+ });
+});
+
+stream.on("end", () => {
+ console.log("done migration");
+});
+```
+
+## Auto-reconnect
+
+By default, ioredis will try to reconnect when the connection to Redis is lost
+except when the connection is closed manually by `redis.disconnect()` or `redis.quit()`.
+
+It's very flexible to control how long to wait to reconnect after disconnection
+using the `retryStrategy` option:
+
+```javascript
+const redis = new Redis({
+ // This is the default value of `retryStrategy`
+ retryStrategy(times) {
+ const delay = Math.min(times * 50, 2000);
+ return delay;
+ },
+});
+```
+
+`retryStrategy` is a function that will be called when the connection is lost.
+The argument `times` means this is the nth reconnection being made and
+the return value represents how long (in ms) to wait to reconnect. When the
+return value isn't a number, ioredis will stop trying to reconnect, and the connection
+will be lost forever if the user doesn't call `redis.connect()` manually.
+
+When reconnected, the client will auto subscribe to channels that the previous connection subscribed to.
+This behavior can be disabled by setting the `autoResubscribe` option to `false`.
+
+And if the previous connection has some unfulfilled commands (most likely blocking commands such as `brpop` and `blpop`),
+the client will resend them when reconnected. This behavior can be disabled by setting the `autoResendUnfulfilledCommands` option to `false`.
+
+By default, all pending commands will be flushed with an error every 20 retry attempts. That makes sure commands won't wait forever when the connection is down. You can change this behavior by setting `maxRetriesPerRequest`:
+
+```javascript
+const redis = new Redis({
+ maxRetriesPerRequest: 1,
+});
+```
+
+Set maxRetriesPerRequest to `null` to disable this behavior, and every command will wait forever until the connection is alive again (which is the default behavior before ioredis v4).
+
+### Blocking Command Timeout
+
+ioredis can apply a client-side timeout to blocking commands (such as `blpop`, `brpop`, `bzpopmin`, `bzmpop`, `blmpop`, `xread`, `xreadgroup`, etc.). This protects against scenarios where the TCP connection becomes a zombie (e.g., due to a silent network failure like a Docker network disconnect) and Redis never replies.
+
+This feature is **opt-in**. It is **disabled by default** and is only enabled
+when `blockingTimeout` is set to a positive number of milliseconds. If
+`blockingTimeout` is omitted, `0`, or negative (for example `-1`), ioredis
+does not arm any client-side timeouts for blocking commands and their
+behavior matches Redis exactly.
+
+```javascript
+const redis = new Redis({
+ blockingTimeout: 30000, // Enable blocking timeout protection
+});
+```
+
+When enabled:
+
+- For commands with a finite timeout (e.g., `blpop("key", 5)`), ioredis sets a client-side deadline based on the command's timeout plus a small grace period (`blockingTimeoutGrace`, default 100ms). If no reply arrives before the deadline, the command resolves with `null`—the same value Redis returns when a blocking command times out normally.
+- For commands that block forever (e.g., `timeout = 0` or `BLOCK 0`), the `blockingTimeout` value is used as a safety net.
+
+### Reconnect on Error
+
+Besides auto-reconnect when the connection is closed, ioredis supports reconnecting on certain Redis errors using the `reconnectOnError` option. Here's an example that will reconnect when receiving `READONLY` error:
+
+```javascript
+const redis = new Redis({
+ reconnectOnError(err) {
+ const targetError = "READONLY";
+ if (err.message.includes(targetError)) {
+ // Only reconnect when the error contains "READONLY"
+ return true; // or `return 1;`
+ }
+ },
+});
+```
+
+This feature is useful when using Amazon ElastiCache instances with Auto-failover disabled. On these instances, test your `reconnectOnError` handler by manually promoting the replica node to the primary role using the AWS console. The following writes fail with the error `READONLY`. Using `reconnectOnError`, we can force the connection to reconnect on this error in order to connect to the new master. Furthermore, if the `reconnectOnError` returns `2`, ioredis will resend the failed command after reconnecting.
+
+On ElastiCache instances with Auto-failover enabled, `reconnectOnError` does not execute. Instead of returning a Redis error, AWS closes all connections to the master endpoint until the new primary node is ready. ioredis reconnects via `retryStrategy` instead of `reconnectOnError` after about a minute. On ElastiCache instances with Auto-failover enabled, test failover events with the `Failover primary` option in the AWS console.
+
+## Connection Events
+
+The Redis instance will emit some events about the state of the connection to the Redis server.
+
+| Event | Description |
+| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| connect | emits when a connection is established to the Redis server. |
+| ready | If `enableReadyCheck` is `true`, client will emit `ready` when the server reports that it is ready to receive commands (e.g. finish loading data from disk). Otherwise, `ready` will be emitted immediately right after the `connect` event. |
+| error | emits when an error occurs while connecting. However, ioredis emits all `error` events silently (only emits when there's at least one listener) so that your application won't crash if you're not listening to the `error` event. When `redis.connect()` is explicitly called the error will also be rejected from the returned promise, in addition to emitting it. If `redis.connect()` is not called explicitly and `lazyConnect` is true, ioredis will try to connect automatically on the first command and emit the `error` event silently. |
+| close | emits when an established Redis server connection has closed. |
+| reconnecting | emits after `close` when a reconnection will be made. The argument of the event is the time (in ms) before reconnecting. |
+| end | emits after `close` when no more reconnections will be made, or the connection is failed to establish. |
+| wait | emits when `lazyConnect` is set and will wait for the first command to be called before connecting. |
+
+You can also check out the `Redis#status` property to get the current connection status.
+
+Besides the above connection events, there are several other custom events:
+
+| Event | Description |
+| :----- | :------------------------------------------------------------------ |
+| select | emits when the database changed. The argument is the new db number. |
+
+## Diagnostics Channel
+
+ioredis publishes telemetry through Node.js [`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html), allowing APM tools and custom instrumentation to observe commands, connections, and batch operations without modifying application code.
+
+These channels use `TracingChannel#tracePromise()` and emit `start`, `end`, `asyncStart`, `asyncEnd`, and `error` sub-events. Requires Node.js >= 18.19.0; on older versions the channels are silently unavailable (zero overhead). Subscribe via `tracing::`:
+
+```typescript
+import dc from "node:diagnostics_channel";
+
+dc.subscribe("tracing:ioredis:command:start", ({ command, args }) => {
+ console.log(`> ${command}`, args);
+});
+
+dc.subscribe("tracing:ioredis:command:asyncEnd", ({ command }) => {
+ console.log(`${command} settled`);
+});
+
+dc.subscribe("tracing:ioredis:command:error", ({ command, error }) => {
+ console.error(`${command} failed:`, error);
+});
+```
+
+| Channel name | Payload | Description |
+| :---------------- | :---------------------- | :------------------------------------------------------- |
+| `ioredis:command` | `CommandTraceContext` | Individual command (standalone, pipeline, or within MULTI) |
+| `ioredis:batch` | `BatchOperationContext` | MULTI transaction as a whole |
+| `ioredis:connect` | `ConnectTraceContext` | Socket connection attempt |
+
+Command arguments are sanitized before emission using rules adapted from `@opentelemetry/redis-common`. Sensitive values (e.g. values in `SET`, `AUTH` passwords) are replaced with `?`, while read-only commands like `GET` and `DEL` retain all arguments. This is safe by default: unlisted or custom commands have all arguments redacted.
+
+Context types (`CommandTraceContext`, `BatchOperationContext`, `ConnectTraceContext`) are exported from `ioredis`.
+
+## Offline Queue
+
+When a command can't be processed by Redis (being sent before the `ready` event), by default, it's added to the offline queue and will be
+executed when it can be processed. You can disable this feature by setting the `enableOfflineQueue`
+option to `false`:
+
+```javascript
+const redis = new Redis({ enableOfflineQueue: false });
+```
+
+## TLS Options
+
+Redis doesn't support TLS natively, however if the redis server you want to connect to is hosted behind a TLS proxy (e.g. [stunnel](https://www.stunnel.org/)) or is offered by a PaaS service that supports TLS connection (e.g. [Redis.com](https://redis.com/)), you can set the `tls` option:
+
+```javascript
+const redis = new Redis({
+ host: "localhost",
+ tls: {
+ // Refer to `tls.connect()` section in
+ // https://nodejs.org/api/tls.html
+ // for all supported options
+ ca: fs.readFileSync("cert.pem"),
+ },
+});
+```
+
+Alternatively, specify the connection through a [`rediss://` URL](https://www.iana.org/assignments/uri-schemes/prov/rediss).
+
+```javascript
+const redis = new Redis("rediss://redis.my-service.com");
+```
+
+If you do not want to use a connection string, you can also specify an empty `tls: {}` object:
+
+```javascript
+const redis = new Redis({
+ host: "redis.my-service.com",
+ tls: {},
+});
+```
+
+### TLS Profiles
+
+> **Warning**
+> TLS profiles described in this section are going to be deprecated in the next major version. Please provide TLS options explicitly.
+
+To make it easier to configure we provide a few pre-configured TLS profiles that can be specified by setting the `tls` option to the profile's name or specifying a `tls.profile` option in case you need to customize some values of the profile.
+
+Profiles:
+
+- `RedisCloudFixed`: Contains the CA for [Redis.com](https://redis.com/) Cloud fixed subscriptions
+- `RedisCloudFlexible`: Contains the CA for [Redis.com](https://redis.com/) Cloud flexible subscriptions
+
+```javascript
+const redis = new Redis({
+ host: "localhost",
+ tls: "RedisCloudFixed",
+});
+
+const redisWithClientCertificate = new Redis({
+ host: "localhost",
+ tls: {
+ profile: "RedisCloudFixed",
+ key: "123",
+ },
+});
+```
+
+
+
+## Sentinel
+
+ioredis supports Sentinel out of the box. It works transparently as all features that work when
+you connect to a single node also work when you connect to a sentinel group. Make sure to run Redis >= 2.8.12 if you want to use this feature. Sentinels have a default port of 26379.
+
+To connect using Sentinel, use:
+
+```javascript
+const redis = new Redis({
+ sentinels: [
+ { host: "localhost", port: 26379 },
+ { host: "localhost", port: 26380 },
+ ],
+ name: "mymaster",
+});
+
+redis.set("foo", "bar");
+```
+
+The arguments passed to the constructor are different from the ones you use to connect to a single node, where:
+
+- `name` identifies a group of Redis instances composed of a master and one or more slaves (`mymaster` in the example);
+- `sentinelPassword` (optional) password for Sentinel instances.
+- `sentinels` are a list of sentinels to connect to. The list does not need to enumerate all your sentinel instances, but a few so that if one is down the client will try the next one.
+- `role` (optional) with a value of `slave` will return a random slave from the Sentinel group.
+- `preferredSlaves` (optional) can be used to prefer a particular slave or set of slaves based on priority. It accepts a function or array.
+- `enableTLSForSentinelMode` (optional) set to true if connecting to sentinel instances that are encrypted
+
+ioredis **guarantees** that the node you connected to is always a master even after a failover. When a failover happens, instead of trying to reconnect to the failed node (which will be demoted to slave when it's available again), ioredis will ask sentinels for the new master node and connect to it. All commands sent during the failover are queued and will be executed when the new connection is established so that none of the commands will be lost.
+
+It's possible to connect to a slave instead of a master by specifying the option `role` with the value of `slave` and ioredis will try to connect to a random slave of the specified master, with the guarantee that the connected node is always a slave. If the current node is promoted to master due to a failover, ioredis will disconnect from it and ask the sentinels for another slave node to connect to.
+
+If you specify the option `preferredSlaves` along with `role: 'slave'` ioredis will attempt to use this value when selecting the slave from the pool of available slaves. The value of `preferredSlaves` should either be a function that accepts an array of available slaves and returns a single result, or an array of slave values priorities by the lowest `prio` value first with a default value of `1`.
+
+```javascript
+// available slaves format
+const availableSlaves = [{ ip: "127.0.0.1", port: "31231", flags: "slave" }];
+
+// preferredSlaves array format
+let preferredSlaves = [
+ { ip: "127.0.0.1", port: "31231", prio: 1 },
+ { ip: "127.0.0.1", port: "31232", prio: 2 },
+];
+
+// preferredSlaves function format
+preferredSlaves = function (availableSlaves) {
+ for (let i = 0; i < availableSlaves.length; i++) {
+ const slave = availableSlaves[i];
+ if (slave.ip === "127.0.0.1") {
+ if (slave.port === "31234") {
+ return slave;
+ }
+ }
+ }
+ // if no preferred slaves are available a random one is used
+ return false;
+};
+
+const redis = new Redis({
+ sentinels: [
+ { host: "127.0.0.1", port: 26379 },
+ { host: "127.0.0.1", port: 26380 },
+ ],
+ name: "mymaster",
+ role: "slave",
+ preferredSlaves: preferredSlaves,
+});
+```
+
+Besides the `retryStrategy` option, there's also a `sentinelRetryStrategy` in Sentinel mode which will be invoked when all the sentinel nodes are unreachable during connecting. If `sentinelRetryStrategy` returns a valid delay time, ioredis will try to reconnect from scratch. The default value of `sentinelRetryStrategy` is:
+
+```javascript
+function (times) {
+ const delay = Math.min(times * 10, 1000);
+ return delay;
+}
+```
+
+## Cluster
+
+Redis Cluster provides a way to run a Redis installation where data is automatically sharded across multiple Redis nodes.
+You can connect to a Redis Cluster like this:
+
+```javascript
+const Redis = require("ioredis");
+
+const cluster = new Redis.Cluster([
+ {
+ port: 6380,
+ host: "127.0.0.1",
+ },
+ {
+ port: 6381,
+ host: "127.0.0.1",
+ },
+]);
+
+cluster.set("foo", "bar");
+cluster.get("foo", (err, res) => {
+ // res === 'bar'
+});
+```
+
+`Cluster` constructor accepts two arguments, where:
+
+0. The first argument is a list of nodes of the cluster you want to connect to.
+ Just like Sentinel, the list does not need to enumerate all your cluster nodes,
+ but a few so that if one is unreachable the client will try the next one, and the client will discover other nodes automatically when at least one node is connected.
+1. The second argument is the options, where:
+
+ - `clusterRetryStrategy`: When none of the startup nodes are reachable, `clusterRetryStrategy` will be invoked. When a number is returned,
+ ioredis will try to reconnect to the startup nodes from scratch after the specified delay (in ms). Otherwise, an error of "None of startup nodes is available" will be returned.
+ The default value of this option is:
+
+ ```javascript
+ function (times) {
+ const delay = Math.min(100 + times * 2, 2000);
+ return delay;
+ }
+ ```
+
+ It's possible to modify the `startupNodes` property in order to switch to another set of nodes here:
+
+ ```javascript
+ function (times) {
+ this.startupNodes = [{ port: 6790, host: '127.0.0.1' }];
+ return Math.min(100 + times * 2, 2000);
+ }
+ ```
+
+ - `dnsLookup`: Alternative DNS lookup function (`dns.lookup()` is used by default). It may be useful to override this in special cases, such as when AWS ElastiCache used with TLS enabled.
+ - `enableOfflineQueue`: Similar to the `enableOfflineQueue` option of `Redis` class.
+ - `enableReadyCheck`: When enabled, "ready" event will only be emitted when `CLUSTER INFO` command
+ reporting the cluster is ready for handling commands. Otherwise, it will be emitted immediately after "connect" is emitted.
+ - `scaleReads`: Config where to send the read queries. See below for more details.
+ - `maxRedirections`: When a cluster related error (e.g. `MOVED`, `ASK` and `CLUSTERDOWN` etc.) is received, the client will redirect the
+ command to another node. This option limits the max redirections allowed when sending a command. The default value is `16`.
+ - `retryDelayOnFailover`: If the target node is disconnected when sending a command,
+ ioredis will retry after the specified delay. The default value is `100`. You should make sure `retryDelayOnFailover * maxRedirections > cluster-node-timeout`
+ to insure that no command will fail during a failover.
+ - `retryDelayOnClusterDown`: When a cluster is down, all commands will be rejected with the error of `CLUSTERDOWN`. If this option is a number (by default, it is `100`), the client
+ will resend the commands after the specified time (in ms).
+ - `retryDelayOnTryAgain`: If this option is a number (by default, it is `100`), the client
+ will resend the commands rejected with `TRYAGAIN` error after the specified time (in ms).
+ - `retryDelayOnMoved`: By default, this value is `0` (in ms), which means when a `MOVED` error is received, the client will resend
+ the command instantly to the node returned together with the `MOVED` error. However, sometimes it takes time for a cluster to become
+ state stabilized after a failover, so adding a delay before resending can prevent a ping pong effect.
+ - `redisOptions`: Default options passed to the constructor of `Redis` when connecting to a node.
+ - `slotsRefreshTimeout`: Milliseconds before a timeout occurs while refreshing slots from the cluster (default `1000`).
+ - `slotsRefreshInterval`: Milliseconds between every automatic slots refresh (by default, it is disabled).
+
+### Read-Write Splitting
+
+A typical redis cluster contains three or more masters and several slaves for each master. It's possible to scale out redis cluster by sending read queries to slaves and write queries to masters by setting the `scaleReads` option.
+
+`scaleReads` is "master" by default, which means ioredis will never send any queries to slaves. There are other three available options:
+
+1. "all": Send write queries to masters and read queries to masters or slaves randomly.
+2. "slave": Send write queries to masters and read queries to slaves.
+3. a custom `function(nodes, command): node`: Will choose the custom function to select to which node to send read queries (write queries keep being sent to master). The first node in `nodes` is always the master serving the relevant slots. If the function returns an array of nodes, a random node of that list will be selected.
+
+For example:
+
+```javascript
+const cluster = new Redis.Cluster(
+ [
+ /* nodes */
+ ],
+ {
+ scaleReads: "slave",
+ }
+);
+cluster.set("foo", "bar"); // This query will be sent to one of the masters.
+cluster.get("foo", (err, res) => {
+ // This query will be sent to one of the slaves.
+});
+```
+
+**NB** In the code snippet above, the `res` may not be equal to "bar" because of the lag of replication between the master and slaves.
+
+### Running Commands to Multiple Nodes
+
+Every command will be sent to exactly one node. For commands containing keys, (e.g. `GET`, `SET` and `HGETALL`), ioredis sends them to the node that serving the keys, and for other commands not containing keys, (e.g. `INFO`, `KEYS` and `FLUSHDB`), ioredis sends them to a random node.
+
+Sometimes you may want to send a command to multiple nodes (masters or slaves) of the cluster, you can get the nodes via `Cluster#nodes()` method.
+
+`Cluster#nodes()` accepts a parameter role, which can be "master", "slave" and "all" (default), and returns an array of `Redis` instance. For example:
+
+```javascript
+// Send `FLUSHDB` command to all slaves:
+const slaves = cluster.nodes("slave");
+Promise.all(slaves.map((node) => node.flushdb()));
+
+// Get keys of all the masters:
+const masters = cluster.nodes("master");
+Promise.all(
+ masters
+ .map((node) => node.keys())
+ .then((keys) => {
+ // keys: [['key1', 'key2'], ['key3', 'key4']]
+ })
+);
+```
+
+### NAT Mapping
+
+Sometimes the cluster is hosted within a internal network that can only be accessed via a NAT (Network Address Translation) instance. See [Accessing ElastiCache from outside AWS](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/accessing-elasticache.html) as an example.
+
+You can specify nat mapping rules via `natMap` option:
+
+```javascript
+const cluster = new Redis.Cluster(
+ [
+ {
+ host: "203.0.113.73",
+ port: 30001,
+ },
+ ],
+ {
+ natMap: {
+ "10.0.1.230:30001": { host: "203.0.113.73", port: 30001 },
+ "10.0.1.231:30001": { host: "203.0.113.73", port: 30002 },
+ "10.0.1.232:30001": { host: "203.0.113.73", port: 30003 },
+ },
+ }
+);
+```
+
+Or you can specify this parameter through function:
+
+```javascript
+const cluster = new Redis.Cluster(
+ [
+ {
+ host: "203.0.113.73",
+ port: 30001,
+ },
+ ],
+ {
+ natMap: (key) => {
+ if (key.includes("30001")) {
+ return { host: "203.0.113.73", port: 30001 };
+ }
+
+ return null;
+ },
+ }
+);
+```
+
+This option is also useful when the cluster is running inside a Docker container.
+Also it works for Clusters in cloud infrastructure where cluster nodes connected through dedicated subnet.
+
+Specifying through may be useful if you don't know concrete internal host and know only node port.
+
+### Transaction and Pipeline in Cluster Mode
+
+Almost all features that are supported by `Redis` are also supported by `Redis.Cluster`, e.g. custom commands, transaction and pipeline.
+However there are some differences when using transaction and pipeline in Cluster mode:
+
+0. All keys in a pipeline should belong to slots served by the same node, since ioredis sends all commands in a pipeline to the same node.
+1. You can't use `multi` without pipeline (aka `cluster.multi({ pipeline: false })`). This is because when you call `cluster.multi({ pipeline: false })`, ioredis doesn't know which node the `multi` command should be sent to.
+
+When any commands in a pipeline receives a `MOVED` or `ASK` error, ioredis will resend the whole pipeline to the specified node automatically if all of the following conditions are satisfied:
+
+0. All errors received in the pipeline are the same. For example, we won't resend the pipeline if we got two `MOVED` errors pointing to different nodes.
+1. All commands executed successfully are readonly commands. This makes sure that resending the pipeline won't have side effects.
+
+### Pub/Sub
+
+Pub/Sub in cluster mode works exactly as the same as in standalone mode. Internally, when a node of the cluster receives a message, it will broadcast the message to the other nodes. ioredis makes sure that each message will only be received once by strictly subscribing one node at the same time.
+
+```javascript
+const nodes = [
+ /* nodes */
+];
+const pub = new Redis.Cluster(nodes);
+const sub = new Redis.Cluster(nodes);
+sub.on("message", (channel, message) => {
+ console.log(channel, message);
+});
+
+sub.subscribe("news", () => {
+ pub.publish("news", "highlights");
+});
+```
+
+### Sharded Pub/Sub
+
+For sharded Pub/Sub, use the `spublish` and `ssubscribe` commands instead of the traditional `publish` and `subscribe`. With the old commands, the Redis cluster handles message propagation behind the scenes, allowing you to publish or subscribe to any node without considering sharding. However, this approach has scalability limitations that are addressed with sharded Pub/Sub. Here’s what you need to know:
+
+1. Instead of a single subscriber connection, there is now one subscriber connection per shard. Because of the potential overhead, you can enable or disable the use of the cluster subscriber group with the `shardedSubscribers` option. By default, this option is set to `false`, meaning sharded subscriptions are disabled. You should enable this option when establishing your cluster connection before using `ssubscribe`.
+2. All channel names that you pass to a single `ssubscribe` need to map to the same hash slot. You can call `ssubscribe` multiple times on the same cluster client instance to subscribe to channels across slots. The cluster's subscriber group takes care of forwarding the `ssubscribe` command to the shard that is responsible for the channels.
+
+The following basic example shows you how to use sharded Pub/Sub:
+
+```javascript
+const cluster: Cluster = new Cluster([{ host: host, port: port }], {
+ shardedSubscribers: true,
+});
+
+//Register the callback
+cluster.on("smessage", (channel, message) => {
+ console.log(message);
+});
+
+//Subscribe to the channels on the same slot
+cluster
+ .ssubscribe("channel{my}:1", "channel{my}:2")
+ .then((count: number) => {
+ console.log(count);
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+
+//Publish a message
+cluster
+ .spublish("channel{my}:1", "This is a test message to my first channel.")
+ .then((value: number) => {
+ console.log("Published a message to channel{my}:1");
+ });
+```
+
+### Events
+
+| Event | Description |
+| :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| connect | emits when a connection is established to the Redis server. |
+| ready | emits when `CLUSTER INFO` reporting the cluster is able to receive commands (if `enableReadyCheck` is `true`) or immediately after `connect` event (if `enableReadyCheck` is false). |
+| error | emits when an error occurs while connecting with a property of `lastNodeError` representing the last node error received. This event is emitted silently (only emitting if there's at least one listener). |
+| close | emits when an established Redis server connection has closed. |
+| reconnecting | emits after `close` when a reconnection will be made. The argument of the event is the time (in ms) before reconnecting. |
+| end | emits after `close` when no more reconnections will be made. |
+| +node | emits when a new node is connected. |
+| -node | emits when a node is disconnected. |
+| node error | emits when an error occurs when connecting to a node. The second argument indicates the address of the node. |
+
+### Password
+
+Setting the `password` option to access password-protected clusters:
+
+```javascript
+const Redis = require("ioredis");
+const cluster = new Redis.Cluster(nodes, {
+ redisOptions: {
+ password: "your-cluster-password",
+ },
+});
+```
+
+If some of nodes in the cluster using a different password, you should specify them in the first parameter:
+
+```javascript
+const Redis = require("ioredis");
+const cluster = new Redis.Cluster(
+ [
+ // Use password "password-for-30001" for 30001
+ { port: 30001, password: "password-for-30001" },
+ // Don't use password when accessing 30002
+ { port: 30002, password: null },
+ // Other nodes will use "fallback-password"
+ ],
+ {
+ redisOptions: {
+ password: "fallback-password",
+ },
+ }
+);
+```
+
+### Special Note: Aws Elasticache Clusters with TLS
+
+AWS ElastiCache for Redis (Clustered Mode) supports TLS encryption. If you use
+this, you may encounter errors with invalid certificates. To resolve this
+issue, construct the `Cluster` with the `dnsLookup` option as follows:
+
+```javascript
+const cluster = new Redis.Cluster(
+ [
+ {
+ host: "clustercfg.myCluster.abcdefg.xyz.cache.amazonaws.com",
+ port: 6379,
+ },
+ ],
+ {
+ dnsLookup: (address, callback) => callback(null, address),
+ redisOptions: {
+ tls: {},
+ },
+ }
+);
+```
+
+
+
+## Autopipelining
+
+In standard mode, when you issue multiple commands, ioredis sends them to the server one by one. As described in Redis pipeline documentation, this is a suboptimal use of the network link, especially when such link is not very performant.
+
+The TCP and network overhead negatively affects performance. Commands are stuck in the send queue until the previous ones are correctly delivered to the server. This is a problem known as Head-Of-Line blocking (HOL).
+
+ioredis supports a feature called “auto pipelining”. It can be enabled by setting the option `enableAutoPipelining` to `true`. No other code change is necessary.
+
+In auto pipelining mode, all commands issued during an event loop are enqueued in a pipeline automatically managed by ioredis. At the end of the iteration, the pipeline is executed and thus all commands are sent to the server at the same time.
+
+This feature can dramatically improve throughput and avoids HOL blocking. In our benchmarks, the improvement was between 35% and 50%.
+
+While an automatic pipeline is executing, all new commands will be enqueued in a new pipeline which will be executed as soon as the previous finishes.
+
+When using Redis Cluster, one pipeline per node is created. Commands are assigned to pipelines according to which node serves the slot.
+
+A pipeline will thus contain commands using different slots but that ultimately are assigned to the same node.
+
+Note that the same slot limitation within a single command still holds, as it is a Redis limitation.
+
+### Example of Automatic Pipeline Enqueuing
+
+This sample code uses ioredis with automatic pipeline enabled.
+
+```javascript
+const Redis = require("./built");
+const http = require("http");
+
+const db = new Redis({ enableAutoPipelining: true });
+
+const server = http.createServer((request, response) => {
+ const key = new URL(request.url, "https://localhost:3000/").searchParams.get(
+ "key"
+ );
+
+ db.get(key, (err, value) => {
+ response.writeHead(200, { "Content-Type": "text/plain" });
+ response.end(value);
+ });
+});
+
+server.listen(3000);
+```
+
+When Node receives requests, it schedules them to be processed in one or more iterations of the events loop.
+
+All commands issued by requests processing during one iteration of the loop will be wrapped in a pipeline automatically created by ioredis.
+
+In the example above, the pipeline will have the following contents:
+
+```
+GET key1
+GET key2
+GET key3
+...
+GET keyN
+```
+
+When all events in the current loop have been processed, the pipeline is executed and thus all commands are sent to the server at the same time.
+
+While waiting for pipeline response from Redis, Node will still be able to process requests. All commands issued by request handler will be enqueued in a new automatically created pipeline. This pipeline will not be sent to the server yet.
+
+As soon as a previous automatic pipeline has received all responses from the server, the new pipeline is immediately sent without waiting for the events loop iteration to finish.
+
+This approach increases the utilization of the network link, reduces the TCP overhead and idle times and therefore improves throughput.
+
+### Benchmarks
+
+Here's some of the results of our tests for a single node.
+
+Each iteration of the test runs 1000 random commands on the server.
+
+| | Samples | Result | Tolerance |
+| ------------------------- | ------- | ------------- | --------- |
+| default | 1000 | 174.62 op/sec | ± 0.45 % |
+| enableAutoPipelining=true | 1500 | 233.33 op/sec | ± 0.88 % |
+
+And here's the same test for a cluster of 3 masters and 3 replicas:
+
+| | Samples | Result | Tolerance |
+| ------------------------- | ------- | ------------- | --------- |
+| default | 1000 | 164.05 op/sec | ± 0.42 % |
+| enableAutoPipelining=true | 3000 | 235.31 op/sec | ± 0.94 % |
+
+# Error Handling
+
+All the errors returned by the Redis server are instances of `ReplyError`, which can be accessed via `Redis`:
+
+```javascript
+const Redis = require("ioredis");
+const redis = new Redis();
+// This command causes a reply error since the SET command requires two arguments.
+redis.set("foo", (err) => {
+ err instanceof Redis.ReplyError;
+});
+```
+
+This is the error stack of the `ReplyError`:
+
+```
+ReplyError: ERR wrong number of arguments for 'set' command
+ at ReplyParser._parseResult (/app/node_modules/ioredis/lib/parsers/javascript.js:60:14)
+ at ReplyParser.execute (/app/node_modules/ioredis/lib/parsers/javascript.js:178:20)
+ at Socket. (/app/node_modules/ioredis/lib/redis/event_handler.js:99:22)
+ at Socket.emit (events.js:97:17)
+ at readableAddChunk (_stream_readable.js:143:16)
+ at Socket.Readable.push (_stream_readable.js:106:10)
+ at TCP.onread (net.js:509:20)
+```
+
+By default, the error stack doesn't make any sense because the whole stack happens in the ioredis
+module itself, not in your code. So it's not easy to find out where the error happens in your code.
+ioredis provides an option `showFriendlyErrorStack` to solve the problem. When you enable
+`showFriendlyErrorStack`, ioredis will optimize the error stack for you:
+
+```javascript
+const Redis = require("ioredis");
+const redis = new Redis({ showFriendlyErrorStack: true });
+redis.set("foo");
+```
+
+And the output will be:
+
+```
+ReplyError: ERR wrong number of arguments for 'set' command
+ at Object. (/app/index.js:3:7)
+ at Module._compile (module.js:446:26)
+ at Object.Module._extensions..js (module.js:464:10)
+ at Module.load (module.js:341:32)
+ at Function.Module._load (module.js:296:12)
+ at Function.Module.runMain (module.js:487:10)
+ at startup (node.js:111:16)
+ at node.js:799:3
+```
+
+This time the stack tells you that the error happens on the third line in your code. Pretty sweet!
+However, it would decrease the performance significantly to optimize the error stack. So by
+default, this option is disabled and can only be used for debugging purposes. You **shouldn't** use this feature in a production environment.
+
+# Running tests
+
+Start a Redis server on 127.0.0.1:6379, and then:
+
+```shell
+npm test
+```
+
+`FLUSH ALL` will be invoked after each test, so make sure there's no valuable data in it before running tests.
+
+If your testing environment does not let you spin up a Redis server [ioredis-mock](https://github.com/stipsan/ioredis-mock) is a drop-in replacement you can use in your tests. It aims to behave identically to ioredis connected to a Redis server so that your integration tests is easier to write and of better quality.
+
+# Debug
+
+You can set the `DEBUG` env to `ioredis:*` to print debug info:
+
+```shell
+$ DEBUG=ioredis:* node app.js
+```
+
+# Join in!
+
+I'm happy to receive bug reports, fixes, documentation enhancements, and any other improvements.
+
+And since I'm not a native English speaker, if you find any grammar mistakes in the documentation, please also let me know. :)
+
+# Contributors
+
+This project exists thanks to all the people who contribute:
+
+
+
+# License
+
+MIT
+
+[](https://app.fossa.io/projects/git%2Bgithub.com%2Fluin%2Fioredis?ref=badge_large)
diff --git a/node_modules/ioredis/built/Command.d.ts b/node_modules/ioredis/built/Command.d.ts
new file mode 100644
index 0000000..66afea3
--- /dev/null
+++ b/node_modules/ioredis/built/Command.d.ts
@@ -0,0 +1,166 @@
+///
+import { Callback, Respondable, CommandParameter } from "./types";
+export declare type ArgumentType = string | Buffer | number | (string | Buffer | number | any[])[];
+interface CommandOptions {
+ /**
+ * Set the encoding of the reply, by default buffer will be returned.
+ */
+ replyEncoding?: BufferEncoding | null;
+ errorStack?: Error;
+ keyPrefix?: string;
+ /**
+ * Force the command to be readOnly so it will also execute on slaves
+ */
+ readOnly?: boolean;
+}
+declare type ArgumentTransformer = (args: any[]) => any[];
+declare type ReplyTransformer = (reply: any) => any;
+export interface CommandNameFlags {
+ VALID_IN_SUBSCRIBER_MODE: [
+ "subscribe",
+ "psubscribe",
+ "unsubscribe",
+ "punsubscribe",
+ "ssubscribe",
+ "sunsubscribe",
+ "ping",
+ "quit"
+ ];
+ VALID_IN_MONITOR_MODE: ["monitor", "auth"];
+ ENTER_SUBSCRIBER_MODE: ["subscribe", "psubscribe", "ssubscribe"];
+ EXIT_SUBSCRIBER_MODE: ["unsubscribe", "punsubscribe", "sunsubscribe"];
+ WILL_DISCONNECT: ["quit"];
+ HANDSHAKE_COMMANDS: ["auth", "select", "client", "readonly", "info"];
+ IGNORE_RECONNECT_ON_ERROR: ["client"];
+ BLOCKING_COMMANDS: [
+ "blpop",
+ "brpop",
+ "brpoplpush",
+ "blmove",
+ "bzpopmin",
+ "bzpopmax",
+ "bzmpop",
+ "blmpop",
+ "xread",
+ "xreadgroup"
+ ];
+ LAST_ARG_TIMEOUT_COMMANDS: [
+ "blpop",
+ "brpop",
+ "brpoplpush",
+ "blmove",
+ "bzpopmin",
+ "bzpopmax"
+ ];
+ FIRST_ARG_TIMEOUT_COMMANDS: ["bzmpop", "blmpop"];
+ BLOCK_OPTION_COMMANDS: ["xread", "xreadgroup"];
+}
+/**
+ * Command instance
+ *
+ * It's rare that you need to create a Command instance yourself.
+ *
+ * ```js
+ * var infoCommand = new Command('info', null, function (err, result) {
+ * console.log('result', result);
+ * });
+ *
+ * redis.sendCommand(infoCommand);
+ *
+ * // When no callback provided, Command instance will have a `promise` property,
+ * // which will resolve/reject with the result of the command.
+ * var getCommand = new Command('get', ['foo']);
+ * getCommand.promise.then(function (result) {
+ * console.log('result', result);
+ * });
+ * ```
+ */
+export default class Command implements Respondable {
+ name: string;
+ static FLAGS: {
+ [key in keyof CommandNameFlags]: CommandNameFlags[key];
+ };
+ private static flagMap?;
+ private static _transformer;
+ /**
+ * Check whether the command has the flag
+ */
+ static checkFlag(flagName: T, commandName: string): commandName is CommandNameFlags[T][number];
+ static setArgumentTransformer(name: string, func: ArgumentTransformer): void;
+ static setReplyTransformer(name: string, func: ReplyTransformer): void;
+ private static getFlagMap;
+ ignore?: boolean;
+ isReadOnly?: boolean;
+ args: CommandParameter[];
+ inTransaction: boolean;
+ pipelineIndex?: number;
+ isTraced: boolean;
+ isResolved: boolean;
+ reject: (err: Error) => void;
+ resolve: (result: any) => void;
+ promise: Promise;
+ private replyEncoding;
+ private errorStack;
+ private bufferMode;
+ private callback;
+ private transformed;
+ private _commandTimeoutTimer?;
+ private _blockingTimeoutTimer?;
+ private _blockingDeadline?;
+ private slot?;
+ private keys?;
+ /**
+ * Creates an instance of Command.
+ * @param name Command name
+ * @param args An array of command arguments
+ * @param options
+ * @param callback The callback that handles the response.
+ * If omit, the response will be handled via Promise
+ */
+ constructor(name: string, args?: Array, options?: CommandOptions, callback?: Callback);
+ getSlot(): number;
+ getKeys(): Array;
+ /**
+ * Convert command to writable buffer or string
+ */
+ toWritable(_socket: object): string | Buffer;
+ stringifyArguments(): void;
+ /**
+ * Convert buffer/buffer[] to string/string[],
+ * and apply reply transformer.
+ */
+ transformReply(result: Buffer | Buffer[]): string | string[] | Buffer | Buffer[];
+ /**
+ * Set the wait time before terminating the attempt to execute a command
+ * and generating an error.
+ */
+ setTimeout(ms: number): void;
+ /**
+ * Set a timeout for blocking commands.
+ * When the timeout expires, the command resolves with null (matching Redis behavior).
+ * This handles the case of undetectable network failures (e.g., docker network disconnect)
+ * where the TCP connection becomes a zombie and no close event fires.
+ */
+ setBlockingTimeout(ms: number): void;
+ /**
+ * Extract the blocking timeout from the command arguments.
+ *
+ * @returns The timeout in seconds, null for indefinite blocking (timeout of 0),
+ * or undefined if this is not a blocking command
+ */
+ extractBlockingTimeout(): number | null | undefined;
+ /**
+ * Clear the command and blocking timers
+ */
+ private _clearTimers;
+ private initPromise;
+ /**
+ * Iterate through the command arguments that are considered keys.
+ */
+ private _iterateKeys;
+ /**
+ * Convert the value from buffer to the target encoding.
+ */
+ private _convertValue;
+}
+export {};
diff --git a/node_modules/ioredis/built/Command.js b/node_modules/ioredis/built/Command.js
new file mode 100644
index 0000000..ae9b61e
--- /dev/null
+++ b/node_modules/ioredis/built/Command.js
@@ -0,0 +1,450 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const commands_1 = require("@ioredis/commands");
+const calculateSlot = require("cluster-key-slot");
+const standard_as_callback_1 = require("standard-as-callback");
+const utils_1 = require("./utils");
+const argumentParsers_1 = require("./utils/argumentParsers");
+/**
+ * Command instance
+ *
+ * It's rare that you need to create a Command instance yourself.
+ *
+ * ```js
+ * var infoCommand = new Command('info', null, function (err, result) {
+ * console.log('result', result);
+ * });
+ *
+ * redis.sendCommand(infoCommand);
+ *
+ * // When no callback provided, Command instance will have a `promise` property,
+ * // which will resolve/reject with the result of the command.
+ * var getCommand = new Command('get', ['foo']);
+ * getCommand.promise.then(function (result) {
+ * console.log('result', result);
+ * });
+ * ```
+ */
+class Command {
+ /**
+ * Creates an instance of Command.
+ * @param name Command name
+ * @param args An array of command arguments
+ * @param options
+ * @param callback The callback that handles the response.
+ * If omit, the response will be handled via Promise
+ */
+ constructor(name, args = [], options = {}, callback) {
+ this.name = name;
+ this.inTransaction = false;
+ this.isTraced = false;
+ this.isResolved = false;
+ this.transformed = false;
+ this.replyEncoding = options.replyEncoding;
+ this.errorStack = options.errorStack;
+ this.args = args.flat();
+ this.callback = callback;
+ this.initPromise();
+ if (options.keyPrefix) {
+ // @ts-expect-error
+ const isBufferKeyPrefix = options.keyPrefix instanceof Buffer;
+ // @ts-expect-error
+ let keyPrefixBuffer = isBufferKeyPrefix
+ ? options.keyPrefix
+ : null;
+ this._iterateKeys((key) => {
+ if (key instanceof Buffer) {
+ if (keyPrefixBuffer === null) {
+ keyPrefixBuffer = Buffer.from(options.keyPrefix);
+ }
+ return Buffer.concat([keyPrefixBuffer, key]);
+ }
+ else if (isBufferKeyPrefix) {
+ // @ts-expect-error
+ return Buffer.concat([options.keyPrefix, Buffer.from(String(key))]);
+ }
+ return options.keyPrefix + key;
+ });
+ }
+ if (options.readOnly) {
+ this.isReadOnly = true;
+ }
+ }
+ /**
+ * Check whether the command has the flag
+ */
+ static checkFlag(flagName, commandName) {
+ commandName = commandName.toLowerCase();
+ return !!this.getFlagMap()[flagName][commandName];
+ }
+ static setArgumentTransformer(name, func) {
+ this._transformer.argument[name] = func;
+ }
+ static setReplyTransformer(name, func) {
+ this._transformer.reply[name] = func;
+ }
+ static getFlagMap() {
+ if (!this.flagMap) {
+ this.flagMap = Object.keys(Command.FLAGS).reduce((map, flagName) => {
+ map[flagName] = {};
+ Command.FLAGS[flagName].forEach((commandName) => {
+ map[flagName][commandName] = true;
+ });
+ return map;
+ }, {});
+ }
+ return this.flagMap;
+ }
+ getSlot() {
+ if (typeof this.slot === "undefined") {
+ const key = this.getKeys()[0];
+ this.slot = key == null ? null : calculateSlot(key);
+ }
+ return this.slot;
+ }
+ getKeys() {
+ return this._iterateKeys();
+ }
+ /**
+ * Convert command to writable buffer or string
+ */
+ toWritable(_socket) {
+ let result;
+ const commandStr = "*" +
+ (this.args.length + 1) +
+ "\r\n$" +
+ Buffer.byteLength(this.name) +
+ "\r\n" +
+ this.name +
+ "\r\n";
+ if (this.bufferMode) {
+ const buffers = new MixedBuffers();
+ buffers.push(commandStr);
+ for (let i = 0; i < this.args.length; ++i) {
+ const arg = this.args[i];
+ if (arg instanceof Buffer) {
+ if (arg.length === 0) {
+ buffers.push("$0\r\n\r\n");
+ }
+ else {
+ buffers.push("$" + arg.length + "\r\n");
+ buffers.push(arg);
+ buffers.push("\r\n");
+ }
+ }
+ else {
+ buffers.push("$" +
+ Buffer.byteLength(arg) +
+ "\r\n" +
+ arg +
+ "\r\n");
+ }
+ }
+ result = buffers.toBuffer();
+ }
+ else {
+ result = commandStr;
+ for (let i = 0; i < this.args.length; ++i) {
+ const arg = this.args[i];
+ result +=
+ "$" +
+ Buffer.byteLength(arg) +
+ "\r\n" +
+ arg +
+ "\r\n";
+ }
+ }
+ return result;
+ }
+ stringifyArguments() {
+ for (let i = 0; i < this.args.length; ++i) {
+ const arg = this.args[i];
+ if (typeof arg === "string") {
+ // buffers and strings don't need any transformation
+ }
+ else if (arg instanceof Buffer) {
+ this.bufferMode = true;
+ }
+ else {
+ this.args[i] = (0, utils_1.toArg)(arg);
+ }
+ }
+ }
+ /**
+ * Convert buffer/buffer[] to string/string[],
+ * and apply reply transformer.
+ */
+ transformReply(result) {
+ if (this.replyEncoding) {
+ result = (0, utils_1.convertBufferToString)(result, this.replyEncoding);
+ }
+ const transformer = Command._transformer.reply[this.name];
+ if (transformer) {
+ result = transformer(result);
+ }
+ return result;
+ }
+ /**
+ * Set the wait time before terminating the attempt to execute a command
+ * and generating an error.
+ */
+ setTimeout(ms) {
+ if (!this._commandTimeoutTimer) {
+ this._commandTimeoutTimer = setTimeout(() => {
+ if (!this.isResolved) {
+ this.reject(new Error("Command timed out"));
+ }
+ }, ms);
+ }
+ }
+ /**
+ * Set a timeout for blocking commands.
+ * When the timeout expires, the command resolves with null (matching Redis behavior).
+ * This handles the case of undetectable network failures (e.g., docker network disconnect)
+ * where the TCP connection becomes a zombie and no close event fires.
+ */
+ setBlockingTimeout(ms) {
+ if (ms <= 0) {
+ return;
+ }
+ // Clear existing timer if any (can happen when command moves from offline to command queue)
+ if (this._blockingTimeoutTimer) {
+ clearTimeout(this._blockingTimeoutTimer);
+ this._blockingTimeoutTimer = undefined;
+ }
+ const now = Date.now();
+ // First call: establish absolute deadline
+ if (this._blockingDeadline === undefined) {
+ this._blockingDeadline = now + ms;
+ }
+ // Check if we've already exceeded the deadline
+ const remaining = this._blockingDeadline - now;
+ if (remaining <= 0) {
+ // Resolve with null to indicate timeout (same as Redis behavior)
+ this.resolve(null);
+ return;
+ }
+ this._blockingTimeoutTimer = setTimeout(() => {
+ if (this.isResolved) {
+ this._blockingTimeoutTimer = undefined;
+ return;
+ }
+ this._blockingTimeoutTimer = undefined;
+ // Timeout expired - resolve with null (same as Redis behavior when blocking command times out)
+ this.resolve(null);
+ }, remaining);
+ }
+ /**
+ * Extract the blocking timeout from the command arguments.
+ *
+ * @returns The timeout in seconds, null for indefinite blocking (timeout of 0),
+ * or undefined if this is not a blocking command
+ */
+ extractBlockingTimeout() {
+ const args = this.args;
+ if (!args || args.length === 0) {
+ return undefined;
+ }
+ const name = this.name.toLowerCase();
+ if (Command.checkFlag("LAST_ARG_TIMEOUT_COMMANDS", name)) {
+ return (0, argumentParsers_1.parseSecondsArgument)(args[args.length - 1]);
+ }
+ if (Command.checkFlag("FIRST_ARG_TIMEOUT_COMMANDS", name)) {
+ return (0, argumentParsers_1.parseSecondsArgument)(args[0]);
+ }
+ if (Command.checkFlag("BLOCK_OPTION_COMMANDS", name)) {
+ return (0, argumentParsers_1.parseBlockOption)(args);
+ }
+ return undefined;
+ }
+ /**
+ * Clear the command and blocking timers
+ */
+ _clearTimers() {
+ const existingTimer = this._commandTimeoutTimer;
+ if (existingTimer) {
+ clearTimeout(existingTimer);
+ delete this._commandTimeoutTimer;
+ }
+ const blockingTimer = this._blockingTimeoutTimer;
+ if (blockingTimer) {
+ clearTimeout(blockingTimer);
+ delete this._blockingTimeoutTimer;
+ }
+ }
+ initPromise() {
+ const promise = new Promise((resolve, reject) => {
+ if (!this.transformed) {
+ this.transformed = true;
+ const transformer = Command._transformer.argument[this.name];
+ if (transformer) {
+ this.args = transformer(this.args);
+ }
+ this.stringifyArguments();
+ }
+ this.resolve = this._convertValue(resolve);
+ this.reject = (err) => {
+ this._clearTimers();
+ if (this.errorStack) {
+ reject((0, utils_1.optimizeErrorStack)(err, this.errorStack.stack, __dirname));
+ }
+ else {
+ reject(err);
+ }
+ };
+ });
+ this.promise = (0, standard_as_callback_1.default)(promise, this.callback);
+ }
+ /**
+ * Iterate through the command arguments that are considered keys.
+ */
+ _iterateKeys(transform = (key) => key) {
+ if (typeof this.keys === "undefined") {
+ this.keys = [];
+ if ((0, commands_1.exists)(this.name, { caseInsensitive: true })) {
+ // @ts-expect-error
+ const keyIndexes = (0, commands_1.getKeyIndexes)(this.name, this.args, {
+ nameCaseInsensitive: true,
+ });
+ for (const index of keyIndexes) {
+ this.args[index] = transform(this.args[index]);
+ this.keys.push(this.args[index]);
+ }
+ }
+ }
+ return this.keys;
+ }
+ /**
+ * Convert the value from buffer to the target encoding.
+ */
+ _convertValue(resolve) {
+ return (value) => {
+ try {
+ this._clearTimers();
+ resolve(this.transformReply(value));
+ this.isResolved = true;
+ }
+ catch (err) {
+ this.reject(err);
+ }
+ return this.promise;
+ };
+ }
+}
+exports.default = Command;
+Command.FLAGS = {
+ VALID_IN_SUBSCRIBER_MODE: [
+ "subscribe",
+ "psubscribe",
+ "unsubscribe",
+ "punsubscribe",
+ "ssubscribe",
+ "sunsubscribe",
+ "ping",
+ "quit",
+ ],
+ VALID_IN_MONITOR_MODE: ["monitor", "auth"],
+ ENTER_SUBSCRIBER_MODE: ["subscribe", "psubscribe", "ssubscribe"],
+ EXIT_SUBSCRIBER_MODE: ["unsubscribe", "punsubscribe", "sunsubscribe"],
+ WILL_DISCONNECT: ["quit"],
+ HANDSHAKE_COMMANDS: ["auth", "select", "client", "readonly", "info"],
+ IGNORE_RECONNECT_ON_ERROR: ["client"],
+ BLOCKING_COMMANDS: [
+ "blpop",
+ "brpop",
+ "brpoplpush",
+ "blmove",
+ "bzpopmin",
+ "bzpopmax",
+ "bzmpop",
+ "blmpop",
+ "xread",
+ "xreadgroup",
+ ],
+ LAST_ARG_TIMEOUT_COMMANDS: [
+ "blpop",
+ "brpop",
+ "brpoplpush",
+ "blmove",
+ "bzpopmin",
+ "bzpopmax",
+ ],
+ FIRST_ARG_TIMEOUT_COMMANDS: ["bzmpop", "blmpop"],
+ BLOCK_OPTION_COMMANDS: ["xread", "xreadgroup"],
+};
+Command._transformer = {
+ argument: {},
+ reply: {},
+};
+const msetArgumentTransformer = function (args) {
+ if (args.length === 1) {
+ if (args[0] instanceof Map) {
+ return (0, utils_1.convertMapToArray)(args[0]);
+ }
+ if (typeof args[0] === "object" && args[0] !== null) {
+ return (0, utils_1.convertObjectToArray)(args[0]);
+ }
+ }
+ return args;
+};
+const hsetArgumentTransformer = function (args) {
+ if (args.length === 2) {
+ if (args[1] instanceof Map) {
+ return [args[0]].concat((0, utils_1.convertMapToArray)(args[1]));
+ }
+ if (typeof args[1] === "object" && args[1] !== null) {
+ return [args[0]].concat((0, utils_1.convertObjectToArray)(args[1]));
+ }
+ }
+ return args;
+};
+Command.setArgumentTransformer("mset", msetArgumentTransformer);
+Command.setArgumentTransformer("msetnx", msetArgumentTransformer);
+Command.setArgumentTransformer("hset", hsetArgumentTransformer);
+Command.setArgumentTransformer("hmset", hsetArgumentTransformer);
+Command.setReplyTransformer("hgetall", function (result) {
+ if (Array.isArray(result)) {
+ const obj = {};
+ for (let i = 0; i < result.length; i += 2) {
+ const key = result[i];
+ const value = result[i + 1];
+ if (key in obj) {
+ // can only be truthy if the property is special somehow, like '__proto__' or 'constructor'
+ // https://github.com/luin/ioredis/issues/1267
+ Object.defineProperty(obj, key, {
+ value,
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ });
+ }
+ else {
+ obj[key] = value;
+ }
+ }
+ return obj;
+ }
+ return result;
+});
+class MixedBuffers {
+ constructor() {
+ this.length = 0;
+ this.items = [];
+ }
+ push(x) {
+ this.length += Buffer.byteLength(x);
+ this.items.push(x);
+ }
+ toBuffer() {
+ const result = Buffer.allocUnsafe(this.length);
+ let offset = 0;
+ for (const item of this.items) {
+ const length = Buffer.byteLength(item);
+ Buffer.isBuffer(item)
+ ? item.copy(result, offset)
+ : result.write(item, offset, length);
+ offset += length;
+ }
+ return result;
+ }
+}
diff --git a/node_modules/ioredis/built/DataHandler.d.ts b/node_modules/ioredis/built/DataHandler.d.ts
new file mode 100644
index 0000000..93e97d9
--- /dev/null
+++ b/node_modules/ioredis/built/DataHandler.d.ts
@@ -0,0 +1,37 @@
+///
+import { NetStream, CommandItem } from "./types";
+import Deque = require("denque");
+import { EventEmitter } from "events";
+import SubscriptionSet from "./SubscriptionSet";
+export interface Condition {
+ select: number;
+ auth?: string | [string, string];
+ subscriber: false | SubscriptionSet;
+}
+export declare type FlushQueueOptions = {
+ offlineQueue?: boolean;
+ commandQueue?: boolean;
+};
+export interface DataHandledable extends EventEmitter {
+ stream: NetStream;
+ status: string;
+ condition: Condition | null;
+ commandQueue: Deque;
+ disconnect(reconnect: boolean): void;
+ recoverFromFatalError(commandError: Error, err: Error, options: FlushQueueOptions): void;
+ handleReconnection(err: Error, item: CommandItem): void;
+}
+interface ParserOptions {
+ stringNumbers: boolean;
+}
+export default class DataHandler {
+ private redis;
+ constructor(redis: DataHandledable, parserOptions: ParserOptions);
+ private returnFatalError;
+ private returnError;
+ private returnReply;
+ private handleSubscriberReply;
+ private handleMonitorReply;
+ private shiftCommand;
+}
+export {};
diff --git a/node_modules/ioredis/built/DataHandler.js b/node_modules/ioredis/built/DataHandler.js
new file mode 100644
index 0000000..7f95466
--- /dev/null
+++ b/node_modules/ioredis/built/DataHandler.js
@@ -0,0 +1,224 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const Command_1 = require("./Command");
+const utils_1 = require("./utils");
+const RedisParser = require("redis-parser");
+const SubscriptionSet_1 = require("./SubscriptionSet");
+const debug = (0, utils_1.Debug)("dataHandler");
+class DataHandler {
+ constructor(redis, parserOptions) {
+ this.redis = redis;
+ const parser = new RedisParser({
+ stringNumbers: parserOptions.stringNumbers,
+ returnBuffers: true,
+ returnError: (err) => {
+ this.returnError(err);
+ },
+ returnFatalError: (err) => {
+ this.returnFatalError(err);
+ },
+ returnReply: (reply) => {
+ this.returnReply(reply);
+ },
+ });
+ // prependListener ensures the parser receives and processes data before socket timeout checks are performed
+ redis.stream.prependListener("data", (data) => {
+ parser.execute(data);
+ });
+ // prependListener() doesn't enable flowing mode automatically - we need to resume the stream manually
+ redis.stream.resume();
+ }
+ returnFatalError(err) {
+ err.message += ". Please report this.";
+ this.redis.recoverFromFatalError(err, err, { offlineQueue: false });
+ }
+ returnError(err) {
+ const item = this.shiftCommand(err);
+ if (!item) {
+ return;
+ }
+ err.command = {
+ name: item.command.name,
+ args: item.command.args,
+ };
+ if (item.command.name == "ssubscribe" && err.message.includes("MOVED")) {
+ this.redis.emit("moved");
+ return;
+ }
+ this.redis.handleReconnection(err, item);
+ }
+ returnReply(reply) {
+ if (this.handleMonitorReply(reply)) {
+ return;
+ }
+ if (this.handleSubscriberReply(reply)) {
+ return;
+ }
+ const item = this.shiftCommand(reply);
+ if (!item) {
+ return;
+ }
+ if (Command_1.default.checkFlag("ENTER_SUBSCRIBER_MODE", item.command.name)) {
+ this.redis.condition.subscriber = new SubscriptionSet_1.default();
+ this.redis.condition.subscriber.add(item.command.name, reply[1].toString());
+ if (!fillSubCommand(item.command, reply[2])) {
+ this.redis.commandQueue.unshift(item);
+ }
+ }
+ else if (Command_1.default.checkFlag("EXIT_SUBSCRIBER_MODE", item.command.name)) {
+ if (!fillUnsubCommand(item.command, reply[2])) {
+ this.redis.commandQueue.unshift(item);
+ }
+ }
+ else {
+ item.command.resolve(reply);
+ }
+ }
+ handleSubscriberReply(reply) {
+ if (!this.redis.condition.subscriber) {
+ return false;
+ }
+ const replyType = Array.isArray(reply) ? reply[0].toString() : null;
+ debug('receive reply "%s" in subscriber mode', replyType);
+ switch (replyType) {
+ case "message":
+ if (this.redis.listeners("message").length > 0) {
+ // Check if there're listeners to avoid unnecessary `toString()`.
+ this.redis.emit("message", reply[1].toString(), reply[2] ? reply[2].toString() : "");
+ }
+ this.redis.emit("messageBuffer", reply[1], reply[2]);
+ break;
+ case "pmessage": {
+ const pattern = reply[1].toString();
+ if (this.redis.listeners("pmessage").length > 0) {
+ this.redis.emit("pmessage", pattern, reply[2].toString(), reply[3].toString());
+ }
+ this.redis.emit("pmessageBuffer", pattern, reply[2], reply[3]);
+ break;
+ }
+ case "smessage": {
+ if (this.redis.listeners("smessage").length > 0) {
+ this.redis.emit("smessage", reply[1].toString(), reply[2] ? reply[2].toString() : "");
+ }
+ this.redis.emit("smessageBuffer", reply[1], reply[2]);
+ break;
+ }
+ case "ssubscribe":
+ case "subscribe":
+ case "psubscribe": {
+ const channel = reply[1].toString();
+ this.redis.condition.subscriber.add(replyType, channel);
+ const item = this.shiftCommand(reply);
+ if (!item) {
+ return;
+ }
+ if (!fillSubCommand(item.command, reply[2])) {
+ this.redis.commandQueue.unshift(item);
+ }
+ break;
+ }
+ case "sunsubscribe":
+ case "unsubscribe":
+ case "punsubscribe": {
+ const channel = reply[1] ? reply[1].toString() : null;
+ if (channel) {
+ this.redis.condition.subscriber.del(replyType, channel);
+ }
+ const count = reply[2];
+ if (Number(count) === 0) {
+ this.redis.condition.subscriber = false;
+ }
+ const item = this.shiftCommand(reply);
+ if (!item) {
+ return;
+ }
+ if (!fillUnsubCommand(item.command, count)) {
+ this.redis.commandQueue.unshift(item);
+ }
+ break;
+ }
+ default: {
+ const item = this.shiftCommand(reply);
+ if (!item) {
+ return;
+ }
+ item.command.resolve(reply);
+ }
+ }
+ return true;
+ }
+ handleMonitorReply(reply) {
+ if (this.redis.status !== "monitoring") {
+ return false;
+ }
+ const replyStr = reply.toString();
+ if (replyStr === "OK") {
+ // Valid commands in the monitoring mode are AUTH and MONITOR,
+ // both of which always reply with 'OK'.
+ // So if we got an 'OK', we can make certain that
+ // the reply is made to AUTH & MONITOR.
+ return false;
+ }
+ // Since commands sent in the monitoring mode will trigger an exception,
+ // any replies we received in the monitoring mode should consider to be
+ // realtime monitor data instead of result of commands.
+ const len = replyStr.indexOf(" ");
+ const timestamp = replyStr.slice(0, len);
+ const argIndex = replyStr.indexOf('"');
+ const args = replyStr
+ .slice(argIndex + 1, -1)
+ .split('" "')
+ .map((elem) => elem.replace(/\\"/g, '"'));
+ const dbAndSource = replyStr.slice(len + 2, argIndex - 2).split(" ");
+ this.redis.emit("monitor", timestamp, args, dbAndSource[1], dbAndSource[0]);
+ return true;
+ }
+ shiftCommand(reply) {
+ const item = this.redis.commandQueue.shift();
+ if (!item) {
+ const message = "Command queue state error. If you can reproduce this, please report it.";
+ const error = new Error(message +
+ (reply instanceof Error
+ ? ` Last error: ${reply.message}`
+ : ` Last reply: ${reply.toString()}`));
+ this.redis.emit("error", error);
+ return null;
+ }
+ return item;
+ }
+}
+exports.default = DataHandler;
+const remainingRepliesMap = new WeakMap();
+function fillSubCommand(command, count) {
+ let remainingReplies = remainingRepliesMap.has(command)
+ ? remainingRepliesMap.get(command)
+ : command.args.length;
+ remainingReplies -= 1;
+ if (remainingReplies <= 0) {
+ command.resolve(count);
+ remainingRepliesMap.delete(command);
+ return true;
+ }
+ remainingRepliesMap.set(command, remainingReplies);
+ return false;
+}
+function fillUnsubCommand(command, count) {
+ let remainingReplies = remainingRepliesMap.has(command)
+ ? remainingRepliesMap.get(command)
+ : command.args.length;
+ if (remainingReplies === 0) {
+ if (Number(count) === 0) {
+ remainingRepliesMap.delete(command);
+ command.resolve(count);
+ return true;
+ }
+ return false;
+ }
+ remainingReplies -= 1;
+ if (remainingReplies <= 0) {
+ command.resolve(count);
+ return true;
+ }
+ remainingRepliesMap.set(command, remainingReplies);
+ return false;
+}
diff --git a/node_modules/ioredis/built/Pipeline.d.ts b/node_modules/ioredis/built/Pipeline.d.ts
new file mode 100644
index 0000000..8503d72
--- /dev/null
+++ b/node_modules/ioredis/built/Pipeline.d.ts
@@ -0,0 +1,31 @@
+import Redis from "./Redis";
+import Cluster from "./cluster";
+import Command from "./Command";
+import Commander from "./utils/Commander";
+declare class Pipeline extends Commander<{
+ type: "pipeline";
+}> {
+ redis: Redis | Cluster;
+ isCluster: boolean;
+ isPipeline: boolean;
+ leftRedirections: {
+ value?: number;
+ };
+ promise: Promise;
+ resolve: (result: unknown) => void;
+ reject: (error: Error) => void;
+ private replyPending;
+ private _queue;
+ private _result;
+ private _transactions;
+ private _shaToScript;
+ private preferKey;
+ constructor(redis: Redis | Cluster);
+ fillResult(value: unknown[], position: number): void;
+ sendCommand(command: Command): unknown;
+ addBatch(commands: any): this;
+}
+export default Pipeline;
+interface Pipeline {
+ length: number;
+}
diff --git a/node_modules/ioredis/built/Pipeline.js b/node_modules/ioredis/built/Pipeline.js
new file mode 100644
index 0000000..224e361
--- /dev/null
+++ b/node_modules/ioredis/built/Pipeline.js
@@ -0,0 +1,352 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const calculateSlot = require("cluster-key-slot");
+const commands_1 = require("@ioredis/commands");
+const standard_as_callback_1 = require("standard-as-callback");
+const util_1 = require("util");
+const Command_1 = require("./Command");
+const buffer_1 = require("buffer");
+const utils_1 = require("./utils");
+const Commander_1 = require("./utils/Commander");
+/*
+ This function derives from the cluster-key-slot implementation.
+ Instead of checking that all keys have the same slot, it checks that all slots are served by the same set of nodes.
+ If this is satisfied, it returns the first key's slot.
+*/
+function generateMultiWithNodes(redis, keys) {
+ const slot = calculateSlot(keys[0]);
+ const target = redis._groupsBySlot[slot];
+ for (let i = 1; i < keys.length; i++) {
+ if (redis._groupsBySlot[calculateSlot(keys[i])] !== target) {
+ return -1;
+ }
+ }
+ return slot;
+}
+class Pipeline extends Commander_1.default {
+ constructor(redis) {
+ super();
+ this.redis = redis;
+ this.isPipeline = true;
+ this.replyPending = 0;
+ this._queue = [];
+ this._result = [];
+ this._transactions = 0;
+ this._shaToScript = {};
+ this.isCluster =
+ this.redis.constructor.name === "Cluster" || this.redis.isCluster;
+ this.options = redis.options;
+ Object.keys(redis.scriptsSet).forEach((name) => {
+ const script = redis.scriptsSet[name];
+ this._shaToScript[script.sha] = script;
+ this[name] = redis[name];
+ this[name + "Buffer"] = redis[name + "Buffer"];
+ });
+ redis.addedBuiltinSet.forEach((name) => {
+ this[name] = redis[name];
+ this[name + "Buffer"] = redis[name + "Buffer"];
+ });
+ this.promise = new Promise((resolve, reject) => {
+ this.resolve = resolve;
+ this.reject = reject;
+ });
+ const _this = this;
+ Object.defineProperty(this, "length", {
+ get: function () {
+ return _this._queue.length;
+ },
+ });
+ }
+ fillResult(value, position) {
+ if (this._queue[position].name === "exec" && Array.isArray(value[1])) {
+ const execLength = value[1].length;
+ for (let i = 0; i < execLength; i++) {
+ if (value[1][i] instanceof Error) {
+ continue;
+ }
+ const cmd = this._queue[position - (execLength - i)];
+ try {
+ value[1][i] = cmd.transformReply(value[1][i]);
+ }
+ catch (err) {
+ value[1][i] = err;
+ }
+ }
+ }
+ this._result[position] = value;
+ if (--this.replyPending) {
+ return;
+ }
+ if (this.isCluster) {
+ let retriable = true;
+ let commonError;
+ for (let i = 0; i < this._result.length; ++i) {
+ const error = this._result[i][0];
+ const command = this._queue[i];
+ if (error) {
+ if (command.name === "exec" &&
+ error.message ===
+ "EXECABORT Transaction discarded because of previous errors.") {
+ continue;
+ }
+ if (!commonError) {
+ commonError = {
+ name: error.name,
+ message: error.message,
+ };
+ }
+ else if (commonError.name !== error.name ||
+ commonError.message !== error.message) {
+ retriable = false;
+ break;
+ }
+ }
+ else if (!command.inTransaction) {
+ const isReadOnly = (0, commands_1.exists)(command.name, { caseInsensitive: true }) &&
+ (0, commands_1.hasFlag)(command.name, "readonly", { nameCaseInsensitive: true });
+ if (!isReadOnly) {
+ retriable = false;
+ break;
+ }
+ }
+ }
+ if (commonError && retriable) {
+ const _this = this;
+ const errv = commonError.message.split(" ");
+ const queue = this._queue;
+ let inTransaction = false;
+ this._queue = [];
+ for (let i = 0; i < queue.length; ++i) {
+ if (errv[0] === "ASK" &&
+ !inTransaction &&
+ queue[i].name !== "asking" &&
+ (!queue[i - 1] || queue[i - 1].name !== "asking")) {
+ const asking = new Command_1.default("asking");
+ asking.ignore = true;
+ this.sendCommand(asking);
+ }
+ queue[i].initPromise();
+ this.sendCommand(queue[i]);
+ inTransaction = queue[i].inTransaction;
+ }
+ let matched = true;
+ if (typeof this.leftRedirections === "undefined") {
+ this.leftRedirections = {};
+ }
+ const exec = function () {
+ _this.exec();
+ };
+ const cluster = this.redis;
+ cluster.handleError(commonError, this.leftRedirections, {
+ moved: function (_slot, key) {
+ _this.preferKey = key;
+ if (cluster.slots[errv[1]]) {
+ if (cluster.slots[errv[1]][0] !== key) {
+ cluster.slots[errv[1]] = [key];
+ }
+ }
+ else {
+ cluster.slots[errv[1]] = [key];
+ }
+ cluster._groupsBySlot[errv[1]] =
+ cluster._groupsIds[cluster.slots[errv[1]].join(";")];
+ cluster.refreshSlotsCache();
+ _this.exec();
+ },
+ ask: function (_slot, key) {
+ _this.preferKey = key;
+ _this.exec();
+ },
+ tryagain: exec,
+ clusterDown: exec,
+ connectionClosed: exec,
+ maxRedirections: () => {
+ matched = false;
+ },
+ defaults: () => {
+ matched = false;
+ },
+ });
+ if (matched) {
+ return;
+ }
+ }
+ }
+ let ignoredCount = 0;
+ for (let i = 0; i < this._queue.length - ignoredCount; ++i) {
+ if (this._queue[i + ignoredCount].ignore) {
+ ignoredCount += 1;
+ }
+ this._result[i] = this._result[i + ignoredCount];
+ }
+ this.resolve(this._result.slice(0, this._result.length - ignoredCount));
+ }
+ sendCommand(command) {
+ if (this._transactions > 0) {
+ command.inTransaction = true;
+ }
+ const position = this._queue.length;
+ command.pipelineIndex = position;
+ command.promise
+ .then((result) => {
+ this.fillResult([null, result], position);
+ })
+ .catch((error) => {
+ this.fillResult([error], position);
+ });
+ this._queue.push(command);
+ return this;
+ }
+ addBatch(commands) {
+ let command, commandName, args;
+ for (let i = 0; i < commands.length; ++i) {
+ command = commands[i];
+ commandName = command[0];
+ args = command.slice(1);
+ this[commandName].apply(this, args);
+ }
+ return this;
+ }
+}
+exports.default = Pipeline;
+// @ts-expect-error
+const multi = Pipeline.prototype.multi;
+// @ts-expect-error
+Pipeline.prototype.multi = function () {
+ this._transactions += 1;
+ return multi.apply(this, arguments);
+};
+// @ts-expect-error
+const execBuffer = Pipeline.prototype.execBuffer;
+// @ts-expect-error
+Pipeline.prototype.execBuffer = (0, util_1.deprecate)(function () {
+ if (this._transactions > 0) {
+ this._transactions -= 1;
+ }
+ return execBuffer.apply(this, arguments);
+}, "Pipeline#execBuffer: Use Pipeline#exec instead");
+// NOTE: To avoid an unhandled promise rejection, this will unconditionally always return this.promise,
+// which always has the rejection handled by standard-as-callback
+// adding the provided rejection callback.
+//
+// If a different promise instance were returned, that promise would cause its own unhandled promise rejection
+// errors, even if that promise unconditionally resolved to **the resolved value of** this.promise.
+Pipeline.prototype.exec = function (callback) {
+ // Wait for the cluster to be connected, since we need nodes information before continuing
+ if (this.isCluster && !this.redis.slots.length) {
+ if (this.redis.status === "wait")
+ this.redis.connect().catch(utils_1.noop);
+ if (callback && !this.nodeifiedPromise) {
+ this.nodeifiedPromise = true;
+ (0, standard_as_callback_1.default)(this.promise, callback);
+ }
+ this.redis.delayUntilReady((err) => {
+ if (err) {
+ this.reject(err);
+ return;
+ }
+ this.exec(callback);
+ });
+ return this.promise;
+ }
+ if (this._transactions > 0) {
+ this._transactions -= 1;
+ return execBuffer.apply(this, arguments);
+ }
+ if (!this.nodeifiedPromise) {
+ this.nodeifiedPromise = true;
+ (0, standard_as_callback_1.default)(this.promise, callback);
+ }
+ if (!this._queue.length) {
+ this.resolve([]);
+ }
+ let pipelineSlot;
+ if (this.isCluster) {
+ // List of the first key for each command
+ const sampleKeys = [];
+ for (let i = 0; i < this._queue.length; i++) {
+ const keys = this._queue[i].getKeys();
+ if (keys.length) {
+ sampleKeys.push(keys[0]);
+ }
+ // For each command, check that the keys belong to the same slot
+ if (keys.length && calculateSlot.generateMulti(keys) < 0) {
+ this.reject(new Error("All the keys in a pipeline command should belong to the same slot"));
+ return this.promise;
+ }
+ }
+ if (sampleKeys.length) {
+ pipelineSlot = generateMultiWithNodes(this.redis, sampleKeys);
+ if (pipelineSlot < 0) {
+ this.reject(new Error("All keys in the pipeline should belong to the same slots allocation group"));
+ return this.promise;
+ }
+ }
+ else {
+ // Send the pipeline to a random node
+ pipelineSlot = (Math.random() * 16384) | 0;
+ }
+ }
+ const _this = this;
+ execPipeline();
+ return this.promise;
+ function execPipeline() {
+ let writePending = (_this.replyPending = _this._queue.length);
+ let node;
+ if (_this.isCluster) {
+ node = {
+ slot: pipelineSlot,
+ redis: _this.redis.connectionPool.nodes.all[_this.preferKey],
+ };
+ }
+ let data = "";
+ let buffers;
+ const stream = {
+ isPipeline: true,
+ destination: _this.isCluster ? node : { redis: _this.redis },
+ write(writable) {
+ if (typeof writable !== "string") {
+ if (!buffers) {
+ buffers = [];
+ }
+ if (data) {
+ buffers.push(Buffer.from(data, "utf8"));
+ data = "";
+ }
+ buffers.push(writable);
+ }
+ else {
+ if (data.length + writable.length >= buffer_1.constants.MAX_STRING_LENGTH) {
+ if (!buffers) {
+ buffers = [];
+ }
+ if (data) {
+ buffers.push(Buffer.from(data, "utf8"));
+ data = "";
+ }
+ }
+ data += writable;
+ }
+ if (!--writePending) {
+ if (buffers) {
+ if (data) {
+ buffers.push(Buffer.from(data, "utf8"));
+ }
+ stream.destination.redis.stream.write(Buffer.concat(buffers));
+ }
+ else {
+ stream.destination.redis.stream.write(data);
+ }
+ // Reset writePending for resending
+ writePending = _this._queue.length;
+ data = "";
+ buffers = undefined;
+ }
+ },
+ };
+ for (let i = 0; i < _this._queue.length; ++i) {
+ _this.redis.sendCommand(_this._queue[i], stream, node);
+ }
+ return _this.promise;
+ }
+};
diff --git a/node_modules/ioredis/built/Redis.d.ts b/node_modules/ioredis/built/Redis.d.ts
new file mode 100644
index 0000000..2b1f35e
--- /dev/null
+++ b/node_modules/ioredis/built/Redis.d.ts
@@ -0,0 +1,243 @@
+///
+import { EventEmitter } from "events";
+import Cluster from "./cluster";
+import Command from "./Command";
+import { DataHandledable, FlushQueueOptions, Condition } from "./DataHandler";
+import { RedisOptions } from "./redis/RedisOptions";
+import ScanStream from "./ScanStream";
+import { Transaction } from "./transaction";
+import { Callback, CommandItem, NetStream, ScanStreamOptions, WriteableStream } from "./types";
+import { type BatchOperationContext } from "./tracing";
+import Commander from "./utils/Commander";
+import Deque = require("denque");
+declare type RedisStatus = "wait" | "reconnecting" | "connecting" | "connect" | "ready" | "close" | "end";
+/**
+ * This is the major component of ioredis.
+ * Use it to connect to a standalone Redis server or Sentinels.
+ *
+ * ```typescript
+ * const redis = new Redis(); // Default port is 6379
+ * async function main() {
+ * redis.set("foo", "bar");
+ * redis.get("foo", (err, result) => {
+ * // `result` should be "bar"
+ * console.log(err, result);
+ * });
+ * // Or use Promise
+ * const result = await redis.get("foo");
+ * }
+ * ```
+ */
+declare class Redis extends Commander implements DataHandledable {
+ static Cluster: typeof Cluster;
+ static Command: typeof Command;
+ /**
+ * Default options
+ */
+ private static defaultOptions;
+ /**
+ * Create a Redis instance.
+ * This is the same as `new Redis()` but is included for compatibility with node-redis.
+ */
+ static createClient(...args: ConstructorParameters): Redis;
+ options: RedisOptions;
+ status: RedisStatus;
+ /**
+ * @ignore
+ */
+ stream: NetStream;
+ /**
+ * @ignore
+ */
+ isCluster: boolean;
+ /**
+ * @ignore
+ */
+ condition: Condition | null;
+ /**
+ * @ignore
+ */
+ commandQueue: Deque;
+ private connector;
+ private reconnectTimeout;
+ private offlineQueue;
+ private connectionEpoch;
+ private retryAttempts;
+ private manuallyClosing;
+ private socketTimeoutTimer;
+ private _autoPipelines;
+ private _runningAutoPipelines;
+ constructor(port: number, host: string, options: RedisOptions);
+ constructor(path: string, options: RedisOptions);
+ constructor(port: number, options: RedisOptions);
+ constructor(port: number, host: string);
+ constructor(options: RedisOptions);
+ constructor(port: number);
+ constructor(path: string);
+ constructor();
+ get autoPipelineQueueSize(): number;
+ /**
+ * Create a connection to Redis.
+ * This method will be invoked automatically when creating a new Redis instance
+ * unless `lazyConnect: true` is passed.
+ *
+ * When calling this method manually, a Promise is returned, which will
+ * be resolved when the connection status is ready. The promise can reject
+ * if the connection fails, times out, or if Redis is already connecting/connected.
+ */
+ connect(callback?: Callback): Promise;
+ private _connect;
+ /**
+ * Disconnect from Redis.
+ *
+ * This method closes the connection immediately,
+ * and may lose some pending replies that haven't written to client.
+ * If you want to wait for the pending replies, use Redis#quit instead.
+ */
+ disconnect(reconnect?: boolean): void;
+ /**
+ * Disconnect from Redis.
+ *
+ * @deprecated
+ */
+ end(): void;
+ /**
+ * Create a new instance with the same options as the current one.
+ *
+ * @example
+ * ```js
+ * var redis = new Redis(6380);
+ * var anotherRedis = redis.duplicate();
+ * ```
+ */
+ duplicate(override?: Partial): Redis;
+ /**
+ * Mode of the connection.
+ *
+ * One of `"normal"`, `"subscriber"`, or `"monitor"`. When the connection is
+ * not in `"normal"` mode, certain commands are not allowed.
+ */
+ get mode(): "normal" | "subscriber" | "monitor";
+ /**
+ * Listen for all requests received by the server in real time.
+ *
+ * This command will create a new connection to Redis and send a
+ * MONITOR command via the new connection in order to avoid disturbing
+ * the current connection.
+ *
+ * @param callback The callback function. If omit, a promise will be returned.
+ * @example
+ * ```js
+ * var redis = new Redis();
+ * redis.monitor(function (err, monitor) {
+ * // Entering monitoring mode.
+ * monitor.on('monitor', function (time, args, source, database) {
+ * console.log(time + ": " + util.inspect(args));
+ * });
+ * });
+ *
+ * // supports promise as well as other commands
+ * redis.monitor().then(function (monitor) {
+ * monitor.on('monitor', function (time, args, source, database) {
+ * console.log(time + ": " + util.inspect(args));
+ * });
+ * });
+ * ```
+ */
+ monitor(callback?: Callback): Promise;
+ /**
+ * Send a command to Redis
+ *
+ * This method is used internally and in most cases you should not
+ * use it directly. If you need to send a command that is not supported
+ * by the library, you can use the `call` method:
+ *
+ * ```js
+ * const redis = new Redis();
+ *
+ * redis.call('set', 'foo', 'bar');
+ * // or
+ * redis.call(['set', 'foo', 'bar']);
+ * ```
+ *
+ * @ignore
+ */
+ sendCommand(command: Command, stream?: WriteableStream): unknown;
+ private getBlockingTimeoutInMs;
+ private getConfiguredBlockingTimeout;
+ private setSocketTimeout;
+ scanStream(options?: ScanStreamOptions): ScanStream;
+ scanBufferStream(options?: ScanStreamOptions): ScanStream;
+ sscanStream(key: string, options?: ScanStreamOptions): ScanStream;
+ sscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
+ hscanStream(key: string, options?: ScanStreamOptions): ScanStream;
+ hscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
+ zscanStream(key: string, options?: ScanStreamOptions): ScanStream;
+ zscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
+ /**
+ * Emit only when there's at least one listener.
+ *
+ * @ignore
+ */
+ silentEmit(eventName: string, arg?: unknown): boolean;
+ /**
+ * @ignore
+ */
+ recoverFromFatalError(_commandError: Error, err: Error, options: FlushQueueOptions): void;
+ /**
+ * @ignore
+ */
+ handleReconnection(err: Error, item: CommandItem): void;
+ /**
+ * @ignore
+ */
+ _getServerAddress(): {
+ address: string;
+ port: number | undefined;
+ };
+ private _buildCommandContext;
+ _buildBatchContext(batchSize: number): BatchOperationContext;
+ /**
+ * Get description of the connection. Used for debugging.
+ */
+ private _getDescription;
+ private resetCommandQueue;
+ private resetOfflineQueue;
+ private parseOptions;
+ /**
+ * Change instance's status
+ */
+ private setStatus;
+ private createScanStream;
+ /**
+ * Flush offline queue and command queue with error.
+ *
+ * @param error The error object to send to the commands
+ * @param options options
+ */
+ private flushQueue;
+ /**
+ * Check whether Redis has finished loading the persistent data and is able to
+ * process commands.
+ */
+ private _readyCheck;
+}
+interface Redis extends EventEmitter {
+ on(event: "message", cb: (channel: string, message: string) => void): this;
+ once(event: "message", cb: (channel: string, message: string) => void): this;
+ on(event: "messageBuffer", cb: (channel: Buffer, message: Buffer) => void): this;
+ once(event: "messageBuffer", cb: (channel: Buffer, message: Buffer) => void): this;
+ on(event: "pmessage", cb: (pattern: string, channel: string, message: string) => void): this;
+ once(event: "pmessage", cb: (pattern: string, channel: string, message: string) => void): this;
+ on(event: "pmessageBuffer", cb: (pattern: string, channel: Buffer, message: Buffer) => void): this;
+ once(event: "pmessageBuffer", cb: (pattern: string, channel: Buffer, message: Buffer) => void): this;
+ on(event: "error", cb: (error: Error) => void): this;
+ once(event: "error", cb: (error: Error) => void): this;
+ on(event: RedisStatus, cb: () => void): this;
+ once(event: RedisStatus, cb: () => void): this;
+ on(event: string | symbol, listener: (...args: any[]) => void): this;
+ once(event: string | symbol, listener: (...args: any[]) => void): this;
+}
+interface Redis extends Transaction {
+}
+export default Redis;
diff --git a/node_modules/ioredis/built/Redis.js b/node_modules/ioredis/built/Redis.js
new file mode 100644
index 0000000..30c5b45
--- /dev/null
+++ b/node_modules/ioredis/built/Redis.js
@@ -0,0 +1,800 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const commands_1 = require("@ioredis/commands");
+const events_1 = require("events");
+const standard_as_callback_1 = require("standard-as-callback");
+const cluster_1 = require("./cluster");
+const Command_1 = require("./Command");
+const connectors_1 = require("./connectors");
+const SentinelConnector_1 = require("./connectors/SentinelConnector");
+const eventHandler = require("./redis/event_handler");
+const RedisOptions_1 = require("./redis/RedisOptions");
+const ScanStream_1 = require("./ScanStream");
+const transaction_1 = require("./transaction");
+const utils_1 = require("./utils");
+const tracing_1 = require("./tracing");
+const applyMixin_1 = require("./utils/applyMixin");
+const Commander_1 = require("./utils/Commander");
+const lodash_1 = require("./utils/lodash");
+const Deque = require("denque");
+const debug = (0, utils_1.Debug)("redis");
+/**
+ * This is the major component of ioredis.
+ * Use it to connect to a standalone Redis server or Sentinels.
+ *
+ * ```typescript
+ * const redis = new Redis(); // Default port is 6379
+ * async function main() {
+ * redis.set("foo", "bar");
+ * redis.get("foo", (err, result) => {
+ * // `result` should be "bar"
+ * console.log(err, result);
+ * });
+ * // Or use Promise
+ * const result = await redis.get("foo");
+ * }
+ * ```
+ */
+class Redis extends Commander_1.default {
+ constructor(arg1, arg2, arg3) {
+ super();
+ this.status = "wait";
+ /**
+ * @ignore
+ */
+ this.isCluster = false;
+ this.reconnectTimeout = null;
+ this.connectionEpoch = 0;
+ this.retryAttempts = 0;
+ this.manuallyClosing = false;
+ // Prepare autopipelines structures
+ this._autoPipelines = new Map();
+ this._runningAutoPipelines = new Set();
+ this.parseOptions(arg1, arg2, arg3);
+ events_1.EventEmitter.call(this);
+ this.resetCommandQueue();
+ this.resetOfflineQueue();
+ if (this.options.Connector) {
+ this.connector = new this.options.Connector(this.options);
+ }
+ else if (this.options.sentinels) {
+ const sentinelConnector = new SentinelConnector_1.default(this.options);
+ sentinelConnector.emitter = this;
+ this.connector = sentinelConnector;
+ }
+ else {
+ this.connector = new connectors_1.StandaloneConnector(this.options);
+ }
+ if (this.options.scripts) {
+ Object.entries(this.options.scripts).forEach(([name, definition]) => {
+ this.defineCommand(name, definition);
+ });
+ }
+ // end(or wait) -> connecting -> connect -> ready -> end
+ if (this.options.lazyConnect) {
+ this.setStatus("wait");
+ }
+ else {
+ this.connect().catch(lodash_1.noop);
+ }
+ }
+ /**
+ * Create a Redis instance.
+ * This is the same as `new Redis()` but is included for compatibility with node-redis.
+ */
+ static createClient(...args) {
+ return new Redis(...args);
+ }
+ get autoPipelineQueueSize() {
+ let queued = 0;
+ for (const pipeline of this._autoPipelines.values()) {
+ queued += pipeline.length;
+ }
+ return queued;
+ }
+ /**
+ * Create a connection to Redis.
+ * This method will be invoked automatically when creating a new Redis instance
+ * unless `lazyConnect: true` is passed.
+ *
+ * When calling this method manually, a Promise is returned, which will
+ * be resolved when the connection status is ready. The promise can reject
+ * if the connection fails, times out, or if Redis is already connecting/connected.
+ */
+ connect(callback) {
+ const promise = (0, tracing_1.traceConnect)(() => this._connect(), () => {
+ const { address, port } = this._getServerAddress();
+ return {
+ serverAddress: address,
+ serverPort: port,
+ connectionEpoch: this.connectionEpoch,
+ };
+ });
+ return (0, standard_as_callback_1.default)(promise, callback);
+ }
+ _connect() {
+ return new Promise((resolve, reject) => {
+ if (this.status === "connecting" ||
+ this.status === "connect" ||
+ this.status === "ready") {
+ reject(new Error("Redis is already connecting/connected"));
+ return;
+ }
+ this.connectionEpoch += 1;
+ this.setStatus("connecting");
+ const { options } = this;
+ this.condition = {
+ select: options.db,
+ auth: options.username
+ ? [options.username, options.password]
+ : options.password,
+ subscriber: false,
+ };
+ const _this = this;
+ (0, standard_as_callback_1.default)(this.connector.connect(function (type, err) {
+ _this.silentEmit(type, err);
+ }), function (err, stream) {
+ if (err) {
+ _this.flushQueue(err);
+ _this.silentEmit("error", err);
+ reject(err);
+ _this.setStatus("end");
+ return;
+ }
+ let CONNECT_EVENT = options.tls ? "secureConnect" : "connect";
+ if ("sentinels" in options &&
+ options.sentinels &&
+ !options.enableTLSForSentinelMode) {
+ CONNECT_EVENT = "connect";
+ }
+ _this.stream = stream;
+ if (options.noDelay) {
+ stream.setNoDelay(true);
+ }
+ // Node ignores setKeepAlive before connect, therefore we wait for the event:
+ // https://github.com/nodejs/node/issues/31663
+ if (typeof options.keepAlive === "number") {
+ if (stream.connecting) {
+ stream.once(CONNECT_EVENT, () => {
+ stream.setKeepAlive(true, options.keepAlive);
+ });
+ }
+ else {
+ stream.setKeepAlive(true, options.keepAlive);
+ }
+ }
+ if (stream.connecting) {
+ stream.once(CONNECT_EVENT, eventHandler.connectHandler(_this));
+ if (options.connectTimeout) {
+ /*
+ * Typically, Socket#setTimeout(0) will clear the timer
+ * set before. However, in some platforms (Electron 3.x~4.x),
+ * the timer will not be cleared. So we introduce a variable here.
+ *
+ * See https://github.com/electron/electron/issues/14915
+ */
+ let connectTimeoutCleared = false;
+ stream.setTimeout(options.connectTimeout, function () {
+ if (connectTimeoutCleared) {
+ return;
+ }
+ stream.setTimeout(0);
+ stream.destroy();
+ const err = new Error("connect ETIMEDOUT");
+ // @ts-expect-error
+ err.errorno = "ETIMEDOUT";
+ // @ts-expect-error
+ err.code = "ETIMEDOUT";
+ // @ts-expect-error
+ err.syscall = "connect";
+ eventHandler.errorHandler(_this)(err);
+ });
+ stream.once(CONNECT_EVENT, function () {
+ connectTimeoutCleared = true;
+ stream.setTimeout(0);
+ });
+ }
+ }
+ else if (stream.destroyed) {
+ const firstError = _this.connector.firstError;
+ if (firstError) {
+ process.nextTick(() => {
+ eventHandler.errorHandler(_this)(firstError);
+ });
+ }
+ process.nextTick(eventHandler.closeHandler(_this));
+ }
+ else {
+ process.nextTick(eventHandler.connectHandler(_this));
+ }
+ if (!stream.destroyed) {
+ stream.once("error", eventHandler.errorHandler(_this));
+ stream.once("close", eventHandler.closeHandler(_this));
+ }
+ const connectionReadyHandler = function () {
+ _this.removeListener("close", connectionCloseHandler);
+ resolve();
+ };
+ var connectionCloseHandler = function () {
+ _this.removeListener("ready", connectionReadyHandler);
+ reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
+ };
+ _this.once("ready", connectionReadyHandler);
+ _this.once("close", connectionCloseHandler);
+ });
+ });
+ }
+ /**
+ * Disconnect from Redis.
+ *
+ * This method closes the connection immediately,
+ * and may lose some pending replies that haven't written to client.
+ * If you want to wait for the pending replies, use Redis#quit instead.
+ */
+ disconnect(reconnect = false) {
+ if (!reconnect) {
+ this.manuallyClosing = true;
+ }
+ if (this.reconnectTimeout && !reconnect) {
+ clearTimeout(this.reconnectTimeout);
+ this.reconnectTimeout = null;
+ }
+ if (this.status === "wait") {
+ eventHandler.closeHandler(this)();
+ }
+ else {
+ this.connector.disconnect();
+ }
+ }
+ /**
+ * Disconnect from Redis.
+ *
+ * @deprecated
+ */
+ end() {
+ this.disconnect();
+ }
+ /**
+ * Create a new instance with the same options as the current one.
+ *
+ * @example
+ * ```js
+ * var redis = new Redis(6380);
+ * var anotherRedis = redis.duplicate();
+ * ```
+ */
+ duplicate(override) {
+ return new Redis({ ...this.options, ...override });
+ }
+ /**
+ * Mode of the connection.
+ *
+ * One of `"normal"`, `"subscriber"`, or `"monitor"`. When the connection is
+ * not in `"normal"` mode, certain commands are not allowed.
+ */
+ get mode() {
+ var _a;
+ return this.options.monitor
+ ? "monitor"
+ : ((_a = this.condition) === null || _a === void 0 ? void 0 : _a.subscriber)
+ ? "subscriber"
+ : "normal";
+ }
+ /**
+ * Listen for all requests received by the server in real time.
+ *
+ * This command will create a new connection to Redis and send a
+ * MONITOR command via the new connection in order to avoid disturbing
+ * the current connection.
+ *
+ * @param callback The callback function. If omit, a promise will be returned.
+ * @example
+ * ```js
+ * var redis = new Redis();
+ * redis.monitor(function (err, monitor) {
+ * // Entering monitoring mode.
+ * monitor.on('monitor', function (time, args, source, database) {
+ * console.log(time + ": " + util.inspect(args));
+ * });
+ * });
+ *
+ * // supports promise as well as other commands
+ * redis.monitor().then(function (monitor) {
+ * monitor.on('monitor', function (time, args, source, database) {
+ * console.log(time + ": " + util.inspect(args));
+ * });
+ * });
+ * ```
+ */
+ monitor(callback) {
+ const monitorInstance = this.duplicate({
+ monitor: true,
+ lazyConnect: false,
+ });
+ return (0, standard_as_callback_1.default)(new Promise(function (resolve, reject) {
+ monitorInstance.once("error", reject);
+ monitorInstance.once("monitoring", function () {
+ resolve(monitorInstance);
+ });
+ }), callback);
+ }
+ /**
+ * Send a command to Redis
+ *
+ * This method is used internally and in most cases you should not
+ * use it directly. If you need to send a command that is not supported
+ * by the library, you can use the `call` method:
+ *
+ * ```js
+ * const redis = new Redis();
+ *
+ * redis.call('set', 'foo', 'bar');
+ * // or
+ * redis.call(['set', 'foo', 'bar']);
+ * ```
+ *
+ * @ignore
+ */
+ sendCommand(command, stream) {
+ var _a, _b;
+ if (this.status === "wait") {
+ this.connect().catch(lodash_1.noop);
+ }
+ if (this.status === "end") {
+ command.reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
+ return command.promise;
+ }
+ if (((_a = this.condition) === null || _a === void 0 ? void 0 : _a.subscriber) &&
+ !Command_1.default.checkFlag("VALID_IN_SUBSCRIBER_MODE", command.name)) {
+ command.reject(new Error("Connection in subscriber mode, only subscriber commands may be used"));
+ return command.promise;
+ }
+ if (typeof this.options.commandTimeout === "number") {
+ command.setTimeout(this.options.commandTimeout);
+ }
+ const blockingTimeout = this.getBlockingTimeoutInMs(command);
+ let writable = this.status === "ready" ||
+ (!stream &&
+ this.status === "connect" &&
+ (0, commands_1.exists)(command.name, { caseInsensitive: true }) &&
+ ((0, commands_1.hasFlag)(command.name, "loading", { nameCaseInsensitive: true }) ||
+ Command_1.default.checkFlag("HANDSHAKE_COMMANDS", command.name)));
+ if (!this.stream) {
+ writable = false;
+ }
+ else if (!this.stream.writable) {
+ writable = false;
+ // @ts-expect-error
+ }
+ else if (this.stream._writableState && this.stream._writableState.ended) {
+ // TODO: We should be able to remove this as the PR has already been merged.
+ // https://github.com/iojs/io.js/pull/1217
+ writable = false;
+ }
+ if (!writable) {
+ if (!this.options.enableOfflineQueue) {
+ command.reject(new Error("Stream isn't writeable and enableOfflineQueue options is false"));
+ return command.promise;
+ }
+ if (command.name === "quit" && this.offlineQueue.length === 0) {
+ this.disconnect();
+ command.resolve(Buffer.from("OK"));
+ return command.promise;
+ }
+ // @ts-expect-error
+ if (debug.enabled) {
+ debug("queue command[%s]: %d -> %s(%o)", this._getDescription(), this.condition.select, command.name, command.args);
+ }
+ this.offlineQueue.push({
+ command: command,
+ stream: stream,
+ select: this.condition.select,
+ });
+ // For blocking commands in the offline queue, arm a client-side timeout
+ // only when blockingTimeout is configured. Without this option, queued
+ // blocking commands may wait indefinitely on a dead connection.
+ if (Command_1.default.checkFlag("BLOCKING_COMMANDS", command.name)) {
+ const offlineTimeout = this.getConfiguredBlockingTimeout();
+ if (offlineTimeout !== undefined) {
+ command.setBlockingTimeout(offlineTimeout);
+ }
+ }
+ }
+ else {
+ // @ts-expect-error
+ if (debug.enabled) {
+ debug("write command[%s]: %d -> %s(%o)", this._getDescription(), (_b = this.condition) === null || _b === void 0 ? void 0 : _b.select, command.name, command.args);
+ }
+ if (stream) {
+ if ("isPipeline" in stream && stream.isPipeline) {
+ stream.write(command.toWritable(stream.destination.redis.stream));
+ }
+ else {
+ stream.write(command.toWritable(stream));
+ }
+ }
+ else {
+ this.stream.write(command.toWritable(this.stream));
+ }
+ this.commandQueue.push({
+ command: command,
+ stream: stream,
+ select: this.condition.select,
+ });
+ if (blockingTimeout !== undefined) {
+ command.setBlockingTimeout(blockingTimeout);
+ }
+ if (Command_1.default.checkFlag("WILL_DISCONNECT", command.name)) {
+ this.manuallyClosing = true;
+ }
+ if (this.options.socketTimeout !== undefined &&
+ this.socketTimeoutTimer === undefined) {
+ this.setSocketTimeout();
+ }
+ }
+ if (command.name === "select" && (0, utils_1.isInt)(command.args[0])) {
+ const db = parseInt(command.args[0], 10);
+ if (this.condition.select !== db) {
+ this.condition.select = db;
+ this.emit("select", db);
+ debug("switch to db [%d]", this.condition.select);
+ }
+ }
+ if (!writable || command.isTraced) {
+ return command.promise;
+ }
+ // Trace on the write path only, and only once per command. Commands may
+ // pass through sendCommand multiple times (offline queue flush,
+ // prevCommandQueue resend after reconnect). The isTraced flag ensures
+ // we don't emit duplicate trace events.
+ command.isTraced = true;
+ return (0, tracing_1.traceCommand)(() => command.promise, () => this._buildCommandContext(command));
+ }
+ getBlockingTimeoutInMs(command) {
+ var _a;
+ if (!Command_1.default.checkFlag("BLOCKING_COMMANDS", command.name)) {
+ return undefined;
+ }
+ // Feature is opt-in: only enabled when blockingTimeout is set to a positive number
+ const configuredTimeout = this.getConfiguredBlockingTimeout();
+ if (configuredTimeout === undefined) {
+ return undefined;
+ }
+ const timeout = command.extractBlockingTimeout();
+ if (typeof timeout === "number") {
+ if (timeout > 0) {
+ // Finite timeout from command args - add grace period
+ return (timeout +
+ ((_a = this.options.blockingTimeoutGrace) !== null && _a !== void 0 ? _a : RedisOptions_1.DEFAULT_REDIS_OPTIONS.blockingTimeoutGrace));
+ }
+ // Command has timeout=0 (block forever), use blockingTimeout option as safety net
+ return configuredTimeout;
+ }
+ if (timeout === null) {
+ // No BLOCK option found (e.g., XREAD without BLOCK), use blockingTimeout as safety net
+ return configuredTimeout;
+ }
+ return undefined;
+ }
+ getConfiguredBlockingTimeout() {
+ if (typeof this.options.blockingTimeout === "number" &&
+ this.options.blockingTimeout > 0) {
+ return this.options.blockingTimeout;
+ }
+ return undefined;
+ }
+ setSocketTimeout() {
+ this.socketTimeoutTimer = setTimeout(() => {
+ this.stream.destroy(new Error(`Socket timeout. Expecting data, but didn't receive any in ${this.options.socketTimeout}ms.`));
+ this.socketTimeoutTimer = undefined;
+ }, this.options.socketTimeout);
+ // this handler must run after the "data" handler in "DataHandler"
+ // so that `this.commandQueue.length` will be updated
+ this.stream.once("data", () => {
+ clearTimeout(this.socketTimeoutTimer);
+ this.socketTimeoutTimer = undefined;
+ if (this.commandQueue.length === 0)
+ return;
+ this.setSocketTimeout();
+ });
+ }
+ scanStream(options) {
+ return this.createScanStream("scan", { options });
+ }
+ scanBufferStream(options) {
+ return this.createScanStream("scanBuffer", { options });
+ }
+ sscanStream(key, options) {
+ return this.createScanStream("sscan", { key, options });
+ }
+ sscanBufferStream(key, options) {
+ return this.createScanStream("sscanBuffer", { key, options });
+ }
+ hscanStream(key, options) {
+ return this.createScanStream("hscan", { key, options });
+ }
+ hscanBufferStream(key, options) {
+ return this.createScanStream("hscanBuffer", { key, options });
+ }
+ zscanStream(key, options) {
+ return this.createScanStream("zscan", { key, options });
+ }
+ zscanBufferStream(key, options) {
+ return this.createScanStream("zscanBuffer", { key, options });
+ }
+ /**
+ * Emit only when there's at least one listener.
+ *
+ * @ignore
+ */
+ silentEmit(eventName, arg) {
+ let error;
+ if (eventName === "error") {
+ error = arg;
+ if (this.status === "end") {
+ return;
+ }
+ if (this.manuallyClosing) {
+ // ignore connection related errors when manually disconnecting
+ if (error instanceof Error &&
+ (error.message === utils_1.CONNECTION_CLOSED_ERROR_MSG ||
+ // @ts-expect-error
+ error.syscall === "connect" ||
+ // @ts-expect-error
+ error.syscall === "read")) {
+ return;
+ }
+ }
+ }
+ if (this.listeners(eventName).length > 0) {
+ return this.emit.apply(this, arguments);
+ }
+ if (error && error instanceof Error) {
+ console.error("[ioredis] Unhandled error event:", error.stack);
+ }
+ return false;
+ }
+ /**
+ * @ignore
+ */
+ recoverFromFatalError(_commandError, err, options) {
+ this.flushQueue(err, options);
+ this.silentEmit("error", err);
+ this.disconnect(true);
+ }
+ /**
+ * @ignore
+ */
+ handleReconnection(err, item) {
+ var _a;
+ let needReconnect = false;
+ if (this.options.reconnectOnError &&
+ !Command_1.default.checkFlag("IGNORE_RECONNECT_ON_ERROR", item.command.name)) {
+ needReconnect = this.options.reconnectOnError(err);
+ }
+ switch (needReconnect) {
+ case 1:
+ case true:
+ if (this.status !== "reconnecting") {
+ this.disconnect(true);
+ }
+ item.command.reject(err);
+ break;
+ case 2:
+ if (this.status !== "reconnecting") {
+ this.disconnect(true);
+ }
+ if (((_a = this.condition) === null || _a === void 0 ? void 0 : _a.select) !== item.select &&
+ item.command.name !== "select") {
+ this.select(item.select);
+ }
+ // TODO
+ // @ts-expect-error
+ this.sendCommand(item.command);
+ break;
+ default:
+ item.command.reject(err);
+ }
+ }
+ /**
+ * @ignore
+ */
+ _getServerAddress() {
+ if ("path" in this.options && this.options.path) {
+ return { address: this.options.path, port: undefined };
+ }
+ return {
+ address: ("host" in this.options && this.options.host) || "localhost",
+ port: ("port" in this.options && this.options.port) || 6379,
+ };
+ }
+ _buildCommandContext(command) {
+ var _a, _b, _c;
+ const { address, port } = this._getServerAddress();
+ return {
+ command: command.name,
+ args: (0, tracing_1.sanitizeArgs)(command.name, command.args),
+ database: (_c = (_b = (_a = this.condition) === null || _a === void 0 ? void 0 : _a.select) !== null && _b !== void 0 ? _b : this.options.db) !== null && _c !== void 0 ? _c : 0,
+ serverAddress: address,
+ serverPort: port,
+ };
+ }
+ _buildBatchContext(batchSize) {
+ var _a, _b, _c;
+ const { address, port } = this._getServerAddress();
+ return {
+ batchMode: "MULTI",
+ batchSize,
+ database: (_c = (_b = (_a = this.condition) === null || _a === void 0 ? void 0 : _a.select) !== null && _b !== void 0 ? _b : this.options.db) !== null && _c !== void 0 ? _c : 0,
+ serverAddress: address,
+ serverPort: port,
+ };
+ }
+ /**
+ * Get description of the connection. Used for debugging.
+ */
+ _getDescription() {
+ let description;
+ if ("path" in this.options && this.options.path) {
+ description = this.options.path;
+ }
+ else if (this.stream &&
+ this.stream.remoteAddress &&
+ this.stream.remotePort) {
+ description = this.stream.remoteAddress + ":" + this.stream.remotePort;
+ }
+ else if ("host" in this.options && this.options.host) {
+ description = this.options.host + ":" + this.options.port;
+ }
+ else {
+ // Unexpected
+ description = "";
+ }
+ if (this.options.connectionName) {
+ description += ` (${this.options.connectionName})`;
+ }
+ return description;
+ }
+ resetCommandQueue() {
+ this.commandQueue = new Deque();
+ }
+ resetOfflineQueue() {
+ this.offlineQueue = new Deque();
+ }
+ parseOptions(...args) {
+ const options = {};
+ let isTls = false;
+ for (let i = 0; i < args.length; ++i) {
+ const arg = args[i];
+ if (arg === null || typeof arg === "undefined") {
+ continue;
+ }
+ if (typeof arg === "object") {
+ (0, lodash_1.defaults)(options, arg);
+ }
+ else if (typeof arg === "string") {
+ (0, lodash_1.defaults)(options, (0, utils_1.parseURL)(arg));
+ if (arg.startsWith("rediss://")) {
+ isTls = true;
+ }
+ }
+ else if (typeof arg === "number") {
+ options.port = arg;
+ }
+ else {
+ throw new Error("Invalid argument " + arg);
+ }
+ }
+ if (isTls) {
+ (0, lodash_1.defaults)(options, { tls: true });
+ }
+ (0, lodash_1.defaults)(options, Redis.defaultOptions);
+ if (typeof options.port === "string") {
+ options.port = parseInt(options.port, 10);
+ }
+ if (typeof options.db === "string") {
+ options.db = parseInt(options.db, 10);
+ }
+ // @ts-expect-error
+ this.options = (0, utils_1.resolveTLSProfile)(options);
+ }
+ /**
+ * Change instance's status
+ */
+ setStatus(status, arg) {
+ // @ts-expect-error
+ if (debug.enabled) {
+ debug("status[%s]: %s -> %s", this._getDescription(), this.status || "[empty]", status);
+ }
+ this.status = status;
+ process.nextTick(this.emit.bind(this, status, arg));
+ }
+ createScanStream(command, { key, options = {} }) {
+ return new ScanStream_1.default({
+ objectMode: true,
+ key: key,
+ redis: this,
+ command: command,
+ ...options,
+ });
+ }
+ /**
+ * Flush offline queue and command queue with error.
+ *
+ * @param error The error object to send to the commands
+ * @param options options
+ */
+ flushQueue(error, options) {
+ options = (0, lodash_1.defaults)({}, options, {
+ offlineQueue: true,
+ commandQueue: true,
+ });
+ let item;
+ if (options.offlineQueue) {
+ while ((item = this.offlineQueue.shift())) {
+ item.command.reject(error);
+ }
+ }
+ if (options.commandQueue) {
+ if (this.commandQueue.length > 0) {
+ if (this.stream) {
+ this.stream.removeAllListeners("data");
+ }
+ while ((item = this.commandQueue.shift())) {
+ item.command.reject(error);
+ }
+ }
+ }
+ }
+ /**
+ * Check whether Redis has finished loading the persistent data and is able to
+ * process commands.
+ */
+ _readyCheck(callback) {
+ const _this = this;
+ this.info(function (err, res) {
+ if (err) {
+ if (err.message && err.message.includes("NOPERM")) {
+ console.warn(`Skipping the ready check because INFO command fails: "${err.message}". You can disable ready check with "enableReadyCheck". More: https://github.com/luin/ioredis/wiki/Disable-ready-check.`);
+ return callback(null, {});
+ }
+ return callback(err);
+ }
+ if (typeof res !== "string") {
+ return callback(null, res);
+ }
+ const info = {};
+ const lines = res.split("\r\n");
+ for (let i = 0; i < lines.length; ++i) {
+ const [fieldName, ...fieldValueParts] = lines[i].split(":");
+ const fieldValue = fieldValueParts.join(":");
+ if (fieldValue) {
+ info[fieldName] = fieldValue;
+ }
+ }
+ if (!info.loading || info.loading === "0") {
+ callback(null, info);
+ }
+ else {
+ const loadingEtaMs = (info.loading_eta_seconds || 1) * 1000;
+ const retryTime = _this.options.maxLoadingRetryTime &&
+ _this.options.maxLoadingRetryTime < loadingEtaMs
+ ? _this.options.maxLoadingRetryTime
+ : loadingEtaMs;
+ debug("Redis server still loading, trying again in " + retryTime + "ms");
+ setTimeout(function () {
+ _this._readyCheck(callback);
+ }, retryTime);
+ }
+ }).catch(lodash_1.noop);
+ }
+}
+Redis.Cluster = cluster_1.default;
+Redis.Command = Command_1.default;
+/**
+ * Default options
+ */
+Redis.defaultOptions = RedisOptions_1.DEFAULT_REDIS_OPTIONS;
+(0, applyMixin_1.default)(Redis, events_1.EventEmitter);
+(0, transaction_1.addTransactionSupport)(Redis.prototype);
+exports.default = Redis;
diff --git a/node_modules/ioredis/built/ScanStream.d.ts b/node_modules/ioredis/built/ScanStream.d.ts
new file mode 100644
index 0000000..38c3803
--- /dev/null
+++ b/node_modules/ioredis/built/ScanStream.d.ts
@@ -0,0 +1,23 @@
+///
+import { Readable, ReadableOptions } from "stream";
+interface Options extends ReadableOptions {
+ key?: string;
+ match?: string;
+ type?: string;
+ command: string;
+ redis: any;
+ count?: string | number;
+ noValues?: boolean;
+}
+/**
+ * Convenient class to convert the process of scanning keys to a readable stream.
+ */
+export default class ScanStream extends Readable {
+ private opt;
+ private _redisCursor;
+ private _redisDrained;
+ constructor(opt: Options);
+ _read(): void;
+ close(): void;
+}
+export {};
diff --git a/node_modules/ioredis/built/ScanStream.js b/node_modules/ioredis/built/ScanStream.js
new file mode 100644
index 0000000..3abecaf
--- /dev/null
+++ b/node_modules/ioredis/built/ScanStream.js
@@ -0,0 +1,51 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const stream_1 = require("stream");
+/**
+ * Convenient class to convert the process of scanning keys to a readable stream.
+ */
+class ScanStream extends stream_1.Readable {
+ constructor(opt) {
+ super(opt);
+ this.opt = opt;
+ this._redisCursor = "0";
+ this._redisDrained = false;
+ }
+ _read() {
+ if (this._redisDrained) {
+ this.push(null);
+ return;
+ }
+ const args = [this._redisCursor];
+ if (this.opt.key) {
+ args.unshift(this.opt.key);
+ }
+ if (this.opt.match) {
+ args.push("MATCH", this.opt.match);
+ }
+ if (this.opt.type) {
+ args.push("TYPE", this.opt.type);
+ }
+ if (this.opt.count) {
+ args.push("COUNT", String(this.opt.count));
+ }
+ if (this.opt.noValues) {
+ args.push("NOVALUES");
+ }
+ this.opt.redis[this.opt.command](args, (err, res) => {
+ if (err) {
+ this.emit("error", err);
+ return;
+ }
+ this._redisCursor = res[0] instanceof Buffer ? res[0].toString() : res[0];
+ if (this._redisCursor === "0") {
+ this._redisDrained = true;
+ }
+ this.push(res[1]);
+ });
+ }
+ close() {
+ this._redisDrained = true;
+ }
+}
+exports.default = ScanStream;
diff --git a/node_modules/ioredis/built/Script.d.ts b/node_modules/ioredis/built/Script.d.ts
new file mode 100644
index 0000000..b17f4d8
--- /dev/null
+++ b/node_modules/ioredis/built/Script.d.ts
@@ -0,0 +1,11 @@
+import { Callback } from "./types";
+export default class Script {
+ private lua;
+ private numberOfKeys;
+ private keyPrefix;
+ private readOnly;
+ private sha;
+ private Command;
+ constructor(lua: string, numberOfKeys?: number | null, keyPrefix?: string, readOnly?: boolean);
+ execute(container: any, args: any[], options: any, callback?: Callback): any;
+}
diff --git a/node_modules/ioredis/built/Script.js b/node_modules/ioredis/built/Script.js
new file mode 100644
index 0000000..34ea485
--- /dev/null
+++ b/node_modules/ioredis/built/Script.js
@@ -0,0 +1,62 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const crypto_1 = require("crypto");
+const Command_1 = require("./Command");
+const standard_as_callback_1 = require("standard-as-callback");
+class Script {
+ constructor(lua, numberOfKeys = null, keyPrefix = "", readOnly = false) {
+ this.lua = lua;
+ this.numberOfKeys = numberOfKeys;
+ this.keyPrefix = keyPrefix;
+ this.readOnly = readOnly;
+ this.sha = (0, crypto_1.createHash)("sha1").update(lua).digest("hex");
+ const sha = this.sha;
+ const socketHasScriptLoaded = new WeakSet();
+ this.Command = class CustomScriptCommand extends Command_1.default {
+ toWritable(socket) {
+ const origReject = this.reject;
+ this.reject = (err) => {
+ if (err.message.indexOf("NOSCRIPT") !== -1) {
+ socketHasScriptLoaded.delete(socket);
+ }
+ origReject.call(this, err);
+ };
+ if (!socketHasScriptLoaded.has(socket)) {
+ socketHasScriptLoaded.add(socket);
+ this.name = "eval";
+ this.args[0] = lua;
+ }
+ else if (this.name === "eval") {
+ this.name = "evalsha";
+ this.args[0] = sha;
+ }
+ return super.toWritable(socket);
+ }
+ };
+ }
+ execute(container, args, options, callback) {
+ if (typeof this.numberOfKeys === "number") {
+ args.unshift(this.numberOfKeys);
+ }
+ if (this.keyPrefix) {
+ options.keyPrefix = this.keyPrefix;
+ }
+ if (this.readOnly) {
+ options.readOnly = true;
+ }
+ const evalsha = new this.Command("evalsha", [this.sha, ...args], options);
+ evalsha.promise = evalsha.promise.catch((err) => {
+ if (err.message.indexOf("NOSCRIPT") === -1) {
+ throw err;
+ }
+ // Resend the same custom evalsha command that gets transformed
+ // to an eval in case it's not loaded yet on the connection.
+ const resend = new this.Command("evalsha", [this.sha, ...args], options);
+ const client = container.isPipeline ? container.redis : container;
+ return client.sendCommand(resend);
+ });
+ (0, standard_as_callback_1.default)(evalsha.promise, callback);
+ return container.sendCommand(evalsha);
+ }
+}
+exports.default = Script;
diff --git a/node_modules/ioredis/built/SubscriptionSet.d.ts b/node_modules/ioredis/built/SubscriptionSet.d.ts
new file mode 100644
index 0000000..2b5c7cc
--- /dev/null
+++ b/node_modules/ioredis/built/SubscriptionSet.d.ts
@@ -0,0 +1,14 @@
+import { CommandNameFlags } from "./Command";
+declare type AddSet = CommandNameFlags["ENTER_SUBSCRIBER_MODE"][number];
+declare type DelSet = CommandNameFlags["EXIT_SUBSCRIBER_MODE"][number];
+/**
+ * Tiny class to simplify dealing with subscription set
+ */
+export default class SubscriptionSet {
+ private set;
+ add(set: AddSet, channel: string): void;
+ del(set: DelSet, channel: string): void;
+ channels(set: AddSet | DelSet): string[];
+ isEmpty(): boolean;
+}
+export {};
diff --git a/node_modules/ioredis/built/SubscriptionSet.js b/node_modules/ioredis/built/SubscriptionSet.js
new file mode 100644
index 0000000..df39e61
--- /dev/null
+++ b/node_modules/ioredis/built/SubscriptionSet.js
@@ -0,0 +1,41 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+/**
+ * Tiny class to simplify dealing with subscription set
+ */
+class SubscriptionSet {
+ constructor() {
+ this.set = {
+ subscribe: {},
+ psubscribe: {},
+ ssubscribe: {},
+ };
+ }
+ add(set, channel) {
+ this.set[mapSet(set)][channel] = true;
+ }
+ del(set, channel) {
+ delete this.set[mapSet(set)][channel];
+ }
+ channels(set) {
+ return Object.keys(this.set[mapSet(set)]);
+ }
+ isEmpty() {
+ return (this.channels("subscribe").length === 0 &&
+ this.channels("psubscribe").length === 0 &&
+ this.channels("ssubscribe").length === 0);
+ }
+}
+exports.default = SubscriptionSet;
+function mapSet(set) {
+ if (set === "unsubscribe") {
+ return "subscribe";
+ }
+ if (set === "punsubscribe") {
+ return "psubscribe";
+ }
+ if (set === "sunsubscribe") {
+ return "ssubscribe";
+ }
+ return set;
+}
diff --git a/node_modules/ioredis/built/autoPipelining.d.ts b/node_modules/ioredis/built/autoPipelining.d.ts
new file mode 100644
index 0000000..64505fc
--- /dev/null
+++ b/node_modules/ioredis/built/autoPipelining.d.ts
@@ -0,0 +1,8 @@
+///
+import { ArgumentType } from "./Command";
+export declare const kExec: unique symbol;
+export declare const kCallbacks: unique symbol;
+export declare const notAllowedAutoPipelineCommands: string[];
+export declare function shouldUseAutoPipelining(client: any, functionName: string, commandName: string): boolean;
+export declare function getFirstValueInFlattenedArray(args: ArgumentType[]): string | Buffer | number | null | undefined;
+export declare function executeWithAutoPipelining(client: any, functionName: string, commandName: string, args: ArgumentType[], callback: any): Promise;
diff --git a/node_modules/ioredis/built/autoPipelining.js b/node_modules/ioredis/built/autoPipelining.js
new file mode 100644
index 0000000..d18998f
--- /dev/null
+++ b/node_modules/ioredis/built/autoPipelining.js
@@ -0,0 +1,177 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.executeWithAutoPipelining = exports.getFirstValueInFlattenedArray = exports.shouldUseAutoPipelining = exports.notAllowedAutoPipelineCommands = exports.kCallbacks = exports.kExec = void 0;
+const lodash_1 = require("./utils/lodash");
+const calculateSlot = require("cluster-key-slot");
+const standard_as_callback_1 = require("standard-as-callback");
+const commands_1 = require("@ioredis/commands");
+exports.kExec = Symbol("exec");
+exports.kCallbacks = Symbol("callbacks");
+exports.notAllowedAutoPipelineCommands = [
+ "auth",
+ "info",
+ "script",
+ "quit",
+ "cluster",
+ "pipeline",
+ "multi",
+ "subscribe",
+ "psubscribe",
+ "unsubscribe",
+ "unpsubscribe",
+ "select",
+ "client",
+];
+function executeAutoPipeline(client, slotKey) {
+ /*
+ If a pipeline is already executing, keep queueing up commands
+ since ioredis won't serve two pipelines at the same time
+ */
+ if (client._runningAutoPipelines.has(slotKey)) {
+ return;
+ }
+ if (!client._autoPipelines.has(slotKey)) {
+ /*
+ Rare edge case. Somehow, something has deleted this running autopipeline in an immediate
+ call to executeAutoPipeline.
+
+ Maybe the callback in the pipeline.exec is sometimes called in the same tick,
+ e.g. if redis is disconnected?
+ */
+ return;
+ }
+ client._runningAutoPipelines.add(slotKey);
+ // Get the pipeline and immediately delete it so that new commands are queued on a new pipeline
+ const pipeline = client._autoPipelines.get(slotKey);
+ client._autoPipelines.delete(slotKey);
+ const callbacks = pipeline[exports.kCallbacks];
+ // Stop keeping a reference to callbacks immediately after the callbacks stop being used.
+ // This allows the GC to reclaim objects referenced by callbacks, especially with 16384 slots
+ // in Redis.Cluster
+ pipeline[exports.kCallbacks] = null;
+ // Perform the call
+ pipeline.exec(function (err, results) {
+ client._runningAutoPipelines.delete(slotKey);
+ /*
+ Invoke all callback in nextTick so the stack is cleared
+ and callbacks can throw errors without affecting other callbacks.
+ */
+ if (err) {
+ for (let i = 0; i < callbacks.length; i++) {
+ process.nextTick(callbacks[i], err);
+ }
+ }
+ else {
+ for (let i = 0; i < callbacks.length; i++) {
+ process.nextTick(callbacks[i], ...results[i]);
+ }
+ }
+ // If there is another pipeline on the same node, immediately execute it without waiting for nextTick
+ if (client._autoPipelines.has(slotKey)) {
+ executeAutoPipeline(client, slotKey);
+ }
+ });
+}
+function shouldUseAutoPipelining(client, functionName, commandName) {
+ return (functionName &&
+ client.options.enableAutoPipelining &&
+ !client.isPipeline &&
+ !exports.notAllowedAutoPipelineCommands.includes(commandName) &&
+ !client.options.autoPipeliningIgnoredCommands.includes(commandName));
+}
+exports.shouldUseAutoPipelining = shouldUseAutoPipelining;
+function getFirstValueInFlattenedArray(args) {
+ for (let i = 0; i < args.length; i++) {
+ const arg = args[i];
+ if (typeof arg === "string") {
+ return arg;
+ }
+ else if (Array.isArray(arg) || (0, lodash_1.isArguments)(arg)) {
+ if (arg.length === 0) {
+ continue;
+ }
+ return arg[0];
+ }
+ const flattened = [arg].flat();
+ if (flattened.length > 0) {
+ return flattened[0];
+ }
+ }
+ return undefined;
+}
+exports.getFirstValueInFlattenedArray = getFirstValueInFlattenedArray;
+function getFirstKeyForCommand(commandName, args) {
+ if ((0, commands_1.exists)(commandName, { caseInsensitive: true })) {
+ const flattenedArgs = args.flat();
+ const keyIndexes = (0, commands_1.getKeyIndexes)(commandName, flattenedArgs, {
+ nameCaseInsensitive: true,
+ });
+ if (keyIndexes.length) {
+ return flattenedArgs[keyIndexes[0]];
+ }
+ }
+ return getFirstValueInFlattenedArray(args);
+}
+function executeWithAutoPipelining(client, functionName, commandName, args, callback) {
+ // On cluster mode let's wait for slots to be available
+ if (client.isCluster && !client.slots.length) {
+ if (client.status === "wait")
+ client.connect().catch(lodash_1.noop);
+ return (0, standard_as_callback_1.default)(new Promise(function (resolve, reject) {
+ client.delayUntilReady((err) => {
+ if (err) {
+ reject(err);
+ return;
+ }
+ executeWithAutoPipelining(client, functionName, commandName, args, null).then(resolve, reject);
+ });
+ }), callback);
+ }
+ // If we have slot information, we can improve routing by grouping slots served by the same subset of nodes
+ const prefix = client.options.keyPrefix || "";
+ let slotKey = client.isCluster
+ ? client.slots[calculateSlot(`${prefix}${getFirstKeyForCommand(commandName, args)}`)].join(",")
+ : "main";
+ // When scaleReads is enabled, separate read and write commands into different pipelines
+ // so they can be routed to replicas and masters respectively
+ if (client.isCluster && client.options.scaleReads !== "master") {
+ const isReadOnly = (0, commands_1.exists)(commandName) && (0, commands_1.hasFlag)(commandName, "readonly");
+ slotKey += isReadOnly ? ":read" : ":write";
+ }
+ if (!client._autoPipelines.has(slotKey)) {
+ const pipeline = client.pipeline();
+ pipeline[exports.kExec] = false;
+ pipeline[exports.kCallbacks] = [];
+ client._autoPipelines.set(slotKey, pipeline);
+ }
+ const pipeline = client._autoPipelines.get(slotKey);
+ /*
+ Mark the pipeline as scheduled.
+ The symbol will make sure that the pipeline is only scheduled once per tick.
+ New commands are appended to an already scheduled pipeline.
+ */
+ if (!pipeline[exports.kExec]) {
+ pipeline[exports.kExec] = true;
+ /*
+ Deferring with setImmediate so we have a chance to capture multiple
+ commands that can be scheduled by I/O events already in the event loop queue.
+ */
+ setImmediate(executeAutoPipeline, client, slotKey);
+ }
+ // Create the promise which will execute the command in the pipeline.
+ const autoPipelinePromise = new Promise(function (resolve, reject) {
+ pipeline[exports.kCallbacks].push(function (err, value) {
+ if (err) {
+ reject(err);
+ return;
+ }
+ resolve(value);
+ });
+ if (functionName === "call") {
+ args.unshift(commandName);
+ }
+ pipeline[functionName](...args);
+ });
+ return (0, standard_as_callback_1.default)(autoPipelinePromise, callback);
+}
+exports.executeWithAutoPipelining = executeWithAutoPipelining;
diff --git a/node_modules/ioredis/built/cluster/ClusterOptions.d.ts b/node_modules/ioredis/built/cluster/ClusterOptions.d.ts
new file mode 100644
index 0000000..80c7e2c
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ClusterOptions.d.ts
@@ -0,0 +1,172 @@
+///
+import { SrvRecord } from "dns";
+import { RedisOptions } from "../redis/RedisOptions";
+import { CommanderOptions } from "../utils/Commander";
+import { NodeRole } from "./util";
+export declare type DNSResolveSrvFunction = (hostname: string, callback: (err: NodeJS.ErrnoException | null | undefined, records?: SrvRecord[]) => void) => void;
+export declare type DNSLookupFunction = (hostname: string, callback: (err: NodeJS.ErrnoException | null | undefined, address: string, family?: number) => void) => void;
+export declare type NatMapFunction = (key: string) => {
+ host: string;
+ port: number;
+} | null;
+export declare type NatMap = {
+ [key: string]: {
+ host: string;
+ port: number;
+ };
+} | NatMapFunction;
+/**
+ * Options for Cluster constructor
+ */
+export interface ClusterOptions extends CommanderOptions {
+ /**
+ * See "Quick Start" section.
+ *
+ * @default (times) => Math.min(100 + times * 2, 2000)
+ */
+ clusterRetryStrategy?: ((times: number, reason?: Error) => number | void | null) | undefined;
+ /**
+ * See Redis class.
+ *
+ * @default true
+ */
+ enableOfflineQueue?: boolean | undefined;
+ /**
+ * When enabled, ioredis only emits "ready" event when `CLUSTER INFO`
+ * command reporting the cluster is ready for handling commands.
+ *
+ * @default true
+ */
+ enableReadyCheck?: boolean | undefined;
+ /**
+ * Scale reads to the node with the specified role.
+ *
+ * @default "master"
+ */
+ scaleReads?: NodeRole | Function | undefined;
+ /**
+ * When a MOVED or ASK error is received, client will redirect the
+ * command to another node.
+ * This option limits the max redirections allowed to send a command.
+ *
+ * @default 16
+ */
+ maxRedirections?: number | undefined;
+ /**
+ * When an error is received when sending a command (e.g.
+ * "Connection is closed." when the target Redis node is down), client will retry
+ * if `retryDelayOnFailover` is valid delay time (in ms).
+ *
+ * @default 100
+ */
+ retryDelayOnFailover?: number | undefined;
+ /**
+ * When a CLUSTERDOWN error is received, client will retry
+ * if `retryDelayOnClusterDown` is valid delay time (in ms).
+ *
+ * @default 100
+ */
+ retryDelayOnClusterDown?: number | undefined;
+ /**
+ * When a TRYAGAIN error is received, client will retry
+ * if `retryDelayOnTryAgain` is valid delay time (in ms).
+ *
+ * @default 100
+ */
+ retryDelayOnTryAgain?: number | undefined;
+ /**
+ * By default, this value is 0, which means when a `MOVED` error is received,
+ * the client will resend the command instantly to the node returned together with
+ * the `MOVED` error. However, sometimes it takes time for a cluster to become
+ * state stabilized after a failover, so adding a delay before resending can
+ * prevent a ping pong effect.
+ *
+ * @default 0
+ */
+ retryDelayOnMoved?: number | undefined;
+ /**
+ * The milliseconds before a timeout occurs while refreshing
+ * slots from the cluster.
+ *
+ * @default 1000
+ */
+ slotsRefreshTimeout?: number | undefined;
+ /**
+ * The milliseconds between every automatic slots refresh.
+ *
+ * @default 5000
+ */
+ slotsRefreshInterval?: number | undefined;
+ /**
+ * Use sharded subscribers instead of a single subscriber.
+ *
+ * If sharded subscribers are used, then one additional subscriber connection per master node
+ * is established. If you don't plan to use SPUBLISH/SSUBSCRIBE, then this should be disabled.
+ *
+ * @default false
+ */
+ shardedSubscribers?: boolean | undefined;
+ /**
+ * Passed to the constructor of `Redis`
+ *
+ * @default null
+ */
+ redisOptions?: Omit | undefined;
+ /**
+ * By default, When a new Cluster instance is created,
+ * it will connect to the Redis cluster automatically.
+ * If you want to keep the instance disconnected until the first command is called,
+ * set this option to `true`.
+ *
+ * @default false
+ */
+ lazyConnect?: boolean | undefined;
+ /**
+ * Discover nodes using SRV records
+ *
+ * @default false
+ */
+ useSRVRecords?: boolean | undefined;
+ /**
+ * SRV records will be resolved via this function.
+ *
+ * You may provide a custom `resolveSrv` function when you want to customize
+ * the cache behavior of the default function.
+ *
+ * @default require('dns').resolveSrv
+ */
+ resolveSrv?: DNSResolveSrvFunction | undefined;
+ /**
+ * Hostnames will be resolved to IP addresses via this function.
+ * This is needed when the addresses of startup nodes are hostnames instead
+ * of IPs.
+ *
+ * You may provide a custom `lookup` function when you want to customize
+ * the cache behavior of the default function.
+ *
+ * @default require('dns').lookup
+ */
+ dnsLookup?: DNSLookupFunction | undefined;
+ natMap?: NatMap | undefined;
+ /**
+ * See Redis class.
+ *
+ * @default false
+ */
+ enableAutoPipelining?: boolean | undefined;
+ /**
+ * See Redis class.
+ *
+ * @default []
+ */
+ autoPipeliningIgnoredCommands?: string[] | undefined;
+ /**
+ * Custom LUA commands
+ */
+ scripts?: Record | undefined;
+}
+export declare const DEFAULT_CLUSTER_OPTIONS: ClusterOptions;
diff --git a/node_modules/ioredis/built/cluster/ClusterOptions.js b/node_modules/ioredis/built/cluster/ClusterOptions.js
new file mode 100644
index 0000000..8c3e2e5
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ClusterOptions.js
@@ -0,0 +1,22 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.DEFAULT_CLUSTER_OPTIONS = void 0;
+const dns_1 = require("dns");
+exports.DEFAULT_CLUSTER_OPTIONS = {
+ clusterRetryStrategy: (times) => Math.min(100 + times * 2, 2000),
+ enableOfflineQueue: true,
+ enableReadyCheck: true,
+ scaleReads: "master",
+ maxRedirections: 16,
+ retryDelayOnMoved: 0,
+ retryDelayOnFailover: 100,
+ retryDelayOnClusterDown: 100,
+ retryDelayOnTryAgain: 100,
+ slotsRefreshTimeout: 1000,
+ useSRVRecords: false,
+ resolveSrv: dns_1.resolveSrv,
+ dnsLookup: dns_1.lookup,
+ enableAutoPipelining: false,
+ autoPipeliningIgnoredCommands: [],
+ shardedSubscribers: false,
+};
diff --git a/node_modules/ioredis/built/cluster/ClusterSubscriber.d.ts b/node_modules/ioredis/built/cluster/ClusterSubscriber.d.ts
new file mode 100644
index 0000000..93a221a
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ClusterSubscriber.d.ts
@@ -0,0 +1,29 @@
+///
+import { EventEmitter } from "events";
+import ConnectionPool from "./ConnectionPool";
+export default class ClusterSubscriber {
+ private connectionPool;
+ private emitter;
+ private isSharded;
+ private started;
+ private subscriber;
+ private lastActiveSubscriber;
+ private slotRange;
+ constructor(connectionPool: ConnectionPool, emitter: EventEmitter, isSharded?: boolean);
+ getInstance(): any;
+ /**
+ * Associate this subscriber to a specific slot range.
+ *
+ * Returns the range or an empty array if the slot range couldn't be associated.
+ *
+ * BTW: This is more for debugging and testing purposes.
+ *
+ * @param range
+ */
+ associateSlotRange(range: number[]): number[];
+ start(): void;
+ stop(): void;
+ isStarted(): boolean;
+ private onSubscriberEnd;
+ private selectSubscriber;
+}
diff --git a/node_modules/ioredis/built/cluster/ClusterSubscriber.js b/node_modules/ioredis/built/cluster/ClusterSubscriber.js
new file mode 100644
index 0000000..ceb02cd
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ClusterSubscriber.js
@@ -0,0 +1,223 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const util_1 = require("./util");
+const utils_1 = require("../utils");
+const Redis_1 = require("../Redis");
+const debug = (0, utils_1.Debug)("cluster:subscriber");
+class ClusterSubscriber {
+ constructor(connectionPool, emitter, isSharded = false) {
+ this.connectionPool = connectionPool;
+ this.emitter = emitter;
+ this.isSharded = isSharded;
+ this.started = false;
+ //There is only one connection for the entire pool
+ this.subscriber = null;
+ //The slot range for which this subscriber is responsible
+ this.slotRange = [];
+ this.onSubscriberEnd = () => {
+ if (!this.started) {
+ debug("subscriber has disconnected, but ClusterSubscriber is not started, so not reconnecting.");
+ return;
+ }
+ // If the subscriber closes whilst it's still the active connection,
+ // we might as well try to connecting to a new node if possible to
+ // minimise the number of missed publishes.
+ debug("subscriber has disconnected, selecting a new one...");
+ this.selectSubscriber();
+ };
+ // If the current node we're using as the subscriber disappears
+ // from the node pool for some reason, we will select a new one
+ // to connect to.
+ // Note that this event is only triggered if the connection to
+ // the node has been used; cluster subscriptions are setup with
+ // lazyConnect = true. It's possible for the subscriber node to
+ // disappear without this method being called!
+ // See https://github.com/luin/ioredis/pull/1589
+ this.connectionPool.on("-node", (_, key) => {
+ if (!this.started || !this.subscriber) {
+ return;
+ }
+ if ((0, util_1.getNodeKey)(this.subscriber.options) === key) {
+ debug("subscriber has left, selecting a new one...");
+ this.selectSubscriber();
+ }
+ });
+ this.connectionPool.on("+node", () => {
+ if (!this.started || this.subscriber) {
+ return;
+ }
+ debug("a new node is discovered and there is no subscriber, selecting a new one...");
+ this.selectSubscriber();
+ });
+ }
+ getInstance() {
+ return this.subscriber;
+ }
+ /**
+ * Associate this subscriber to a specific slot range.
+ *
+ * Returns the range or an empty array if the slot range couldn't be associated.
+ *
+ * BTW: This is more for debugging and testing purposes.
+ *
+ * @param range
+ */
+ associateSlotRange(range) {
+ if (this.isSharded) {
+ this.slotRange = range;
+ }
+ return this.slotRange;
+ }
+ start() {
+ this.started = true;
+ this.selectSubscriber();
+ debug("started");
+ }
+ stop() {
+ this.started = false;
+ if (this.subscriber) {
+ this.subscriber.disconnect();
+ this.subscriber = null;
+ }
+ }
+ isStarted() {
+ return this.started;
+ }
+ selectSubscriber() {
+ const lastActiveSubscriber = this.lastActiveSubscriber;
+ // Disconnect the previous subscriber even if there
+ // will not be a new one.
+ if (lastActiveSubscriber) {
+ lastActiveSubscriber.off("end", this.onSubscriberEnd);
+ lastActiveSubscriber.disconnect();
+ }
+ if (this.subscriber) {
+ this.subscriber.off("end", this.onSubscriberEnd);
+ this.subscriber.disconnect();
+ }
+ const sampleNode = (0, utils_1.sample)(this.connectionPool.getNodes());
+ if (!sampleNode) {
+ debug("selecting subscriber failed since there is no node discovered in the cluster yet");
+ this.subscriber = null;
+ return;
+ }
+ const { options } = sampleNode;
+ debug("selected a subscriber %s:%s", options.host, options.port);
+ /*
+ * Create a specialized Redis connection for the subscription.
+ * Note that auto reconnection is enabled here.
+ *
+ * `enableReadyCheck` is also enabled because although subscription is allowed
+ * while redis is loading data from the disk, we can check if the password
+ * provided for the subscriber is correct, and if not, the current subscriber
+ * will be disconnected and a new subscriber will be selected.
+ */
+ let connectionPrefix = "subscriber";
+ if (this.isSharded)
+ connectionPrefix = "ssubscriber";
+ this.subscriber = new Redis_1.default({
+ port: options.port,
+ host: options.host,
+ username: options.username,
+ password: options.password,
+ enableReadyCheck: true,
+ connectionName: (0, util_1.getConnectionName)(connectionPrefix, options.connectionName),
+ lazyConnect: true,
+ tls: options.tls,
+ // Don't try to reconnect the subscriber connection. If the connection fails
+ // we will get an end event (handled below), at which point we'll pick a new
+ // node from the pool and try to connect to that as the subscriber connection.
+ retryStrategy: null,
+ });
+ // Ignore the errors since they're handled in the connection pool.
+ this.subscriber.on("error", utils_1.noop);
+ this.subscriber.on("moved", () => {
+ this.emitter.emit("forceRefresh");
+ });
+ // The node we lost connection to may not come back up in a
+ // reasonable amount of time (e.g. a slave that's taken down
+ // for maintainence), we could potentially miss many published
+ // messages so we should reconnect as quickly as possible, to
+ // a different node if needed.
+ this.subscriber.once("end", this.onSubscriberEnd);
+ // Re-subscribe previous channels
+ const previousChannels = { subscribe: [], psubscribe: [], ssubscribe: [] };
+ if (lastActiveSubscriber) {
+ const condition = lastActiveSubscriber.condition || lastActiveSubscriber.prevCondition;
+ if (condition && condition.subscriber) {
+ previousChannels.subscribe = condition.subscriber.channels("subscribe");
+ previousChannels.psubscribe =
+ condition.subscriber.channels("psubscribe");
+ previousChannels.ssubscribe =
+ condition.subscriber.channels("ssubscribe");
+ }
+ }
+ if (previousChannels.subscribe.length ||
+ previousChannels.psubscribe.length ||
+ previousChannels.ssubscribe.length) {
+ let pending = 0;
+ for (const type of ["subscribe", "psubscribe", "ssubscribe"]) {
+ const channels = previousChannels[type];
+ if (channels.length == 0) {
+ continue;
+ }
+ debug("%s %d channels", type, channels.length);
+ if (type === "ssubscribe") {
+ for (const channel of channels) {
+ pending += 1;
+ this.subscriber[type](channel)
+ .then(() => {
+ if (!--pending) {
+ this.lastActiveSubscriber = this.subscriber;
+ }
+ })
+ .catch(() => {
+ // TODO: should probably disconnect the subscriber and try again.
+ debug("failed to ssubscribe to channel: %s", channel);
+ });
+ }
+ }
+ else {
+ pending += 1;
+ this.subscriber[type](channels)
+ .then(() => {
+ if (!--pending) {
+ this.lastActiveSubscriber = this.subscriber;
+ }
+ })
+ .catch(() => {
+ // TODO: should probably disconnect the subscriber and try again.
+ debug("failed to %s %d channels", type, channels.length);
+ });
+ }
+ }
+ }
+ else {
+ this.lastActiveSubscriber = this.subscriber;
+ }
+ for (const event of [
+ "message",
+ "messageBuffer",
+ ]) {
+ this.subscriber.on(event, (arg1, arg2) => {
+ this.emitter.emit(event, arg1, arg2);
+ });
+ }
+ for (const event of ["pmessage", "pmessageBuffer"]) {
+ this.subscriber.on(event, (arg1, arg2, arg3) => {
+ this.emitter.emit(event, arg1, arg2, arg3);
+ });
+ }
+ if (this.isSharded == true) {
+ for (const event of [
+ "smessage",
+ "smessageBuffer",
+ ]) {
+ this.subscriber.on(event, (arg1, arg2) => {
+ this.emitter.emit(event, arg1, arg2);
+ });
+ }
+ }
+ }
+}
+exports.default = ClusterSubscriber;
diff --git a/node_modules/ioredis/built/cluster/ClusterSubscriberGroup.d.ts b/node_modules/ioredis/built/cluster/ClusterSubscriberGroup.d.ts
new file mode 100644
index 0000000..496b96d
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ClusterSubscriberGroup.d.ts
@@ -0,0 +1,108 @@
+///
+import * as EventEmitter from "events";
+import ShardedSubscriber from "./ShardedSubscriber";
+import { ClusterOptions } from "./ClusterOptions";
+/**
+ * Redis distinguishes between "normal" and sharded PubSub. When using the normal PubSub feature,
+ * exactly one subscriber exists per cluster instance because the Redis cluster bus forwards
+ * messages between shards. Sharded PubSub removes this limitation by making each shard
+ * responsible for its own messages.
+ *
+ * This class coordinates one ShardedSubscriber per master node in the cluster, providing
+ * sharded PubSub support while keeping the public API backward compatible.
+ */
+export default class ClusterSubscriberGroup {
+ private readonly subscriberGroupEmitter;
+ private readonly options;
+ private shardedSubscribers;
+ private clusterSlots;
+ private subscriberToSlotsIndex;
+ private channels;
+ private failedAttemptsByNode;
+ private isResetting;
+ private pendingReset;
+ private static readonly MAX_RETRY_ATTEMPTS;
+ private static readonly MAX_BACKOFF_MS;
+ private static readonly BASE_BACKOFF_MS;
+ /**
+ * Register callbacks
+ *
+ * @param cluster
+ */
+ constructor(subscriberGroupEmitter: EventEmitter, options: ClusterOptions);
+ /**
+ * Get the responsible subscriber.
+ *
+ * @param slot
+ */
+ getResponsibleSubscriber(slot: number): ShardedSubscriber | undefined;
+ /**
+ * Adds a channel for which this subscriber group is responsible
+ *
+ * @param channels
+ */
+ addChannels(channels: (string | Buffer)[]): number;
+ /**
+ * Removes channels for which the subscriber group is responsible by optionally unsubscribing
+ * @param channels
+ */
+ removeChannels(channels: (string | Buffer)[]): number;
+ /**
+ * Disconnect all subscribers and clear some of the internal state.
+ */
+ stop(): void;
+ /**
+ * Start all not yet started subscribers
+ */
+ start(): Promise;
+ /**
+ * Resets the subscriber group by disconnecting all subscribers that are no longer needed and connecting new ones.
+ */
+ reset(clusterSlots: string[][], clusterNodes: any[]): Promise;
+ /**
+ * Refreshes the subscriber-related slot ranges
+ *
+ * Returns false if no refresh was needed
+ *
+ * @param targetSlots
+ */
+ private _refreshSlots;
+ /**
+ * Resubscribes to the previous channels
+ *
+ * @private
+ */
+ private _resubscribe;
+ /**
+ * Deep equality of the cluster slots objects
+ *
+ * @param other
+ * @private
+ */
+ private _slotsAreEqual;
+ /**
+ * Checks if any subscribers are in an unhealthy state.
+ *
+ * A subscriber is considered unhealthy if:
+ * - It exists but is not started (failed/disconnected)
+ * - It's missing entirely for a node that should have one
+ *
+ * @returns true if any subscribers need to be recreated
+ */
+ private hasUnhealthySubscribers;
+ /**
+ * Handles failed subscriber connections by emitting an event to refresh the slots cache
+ * after a backoff period.
+ *
+ * @param error
+ * @param nodeKey
+ */
+ private handleSubscriberConnectFailed;
+ /**
+ * Handles successful subscriber connections by resetting the failed attempts counter.
+ *
+ * @param nodeKey
+ */
+ private handleSubscriberConnectSucceeded;
+ private shouldStartSubscriber;
+}
diff --git a/node_modules/ioredis/built/cluster/ClusterSubscriberGroup.js b/node_modules/ioredis/built/cluster/ClusterSubscriberGroup.js
new file mode 100644
index 0000000..8f524a8
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ClusterSubscriberGroup.js
@@ -0,0 +1,373 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const utils_1 = require("../utils");
+const util_1 = require("./util");
+const calculateSlot = require("cluster-key-slot");
+const ShardedSubscriber_1 = require("./ShardedSubscriber");
+const debug = (0, utils_1.Debug)("cluster:subscriberGroup");
+/**
+ * Redis distinguishes between "normal" and sharded PubSub. When using the normal PubSub feature,
+ * exactly one subscriber exists per cluster instance because the Redis cluster bus forwards
+ * messages between shards. Sharded PubSub removes this limitation by making each shard
+ * responsible for its own messages.
+ *
+ * This class coordinates one ShardedSubscriber per master node in the cluster, providing
+ * sharded PubSub support while keeping the public API backward compatible.
+ */
+class ClusterSubscriberGroup {
+ /**
+ * Register callbacks
+ *
+ * @param cluster
+ */
+ constructor(subscriberGroupEmitter, options) {
+ this.subscriberGroupEmitter = subscriberGroupEmitter;
+ this.options = options;
+ this.shardedSubscribers = new Map();
+ this.clusterSlots = [];
+ // Simple [min, max] slot ranges aren't enough because you can migrate single slots
+ this.subscriberToSlotsIndex = new Map();
+ this.channels = new Map();
+ this.failedAttemptsByNode = new Map();
+ // Only latest pending reset kept; throttled by refreshSlotsCache's isRefreshing + backoff delay
+ this.isResetting = false;
+ this.pendingReset = null;
+ /**
+ * Handles failed subscriber connections by emitting an event to refresh the slots cache
+ * after a backoff period.
+ *
+ * @param error
+ * @param nodeKey
+ */
+ this.handleSubscriberConnectFailed = (error, nodeKey) => {
+ const currentAttempts = this.failedAttemptsByNode.get(nodeKey) || 0;
+ const failedAttempts = currentAttempts + 1;
+ this.failedAttemptsByNode.set(nodeKey, failedAttempts);
+ const attempts = Math.min(failedAttempts, ClusterSubscriberGroup.MAX_RETRY_ATTEMPTS);
+ const backoff = Math.min(ClusterSubscriberGroup.BASE_BACKOFF_MS * 2 ** attempts, ClusterSubscriberGroup.MAX_BACKOFF_MS);
+ const jitter = Math.floor((Math.random() - 0.5) * (backoff * 0.5));
+ const delay = Math.max(0, backoff + jitter);
+ debug("Failed to connect subscriber for %s. Refreshing slots in %dms", nodeKey, delay);
+ this.subscriberGroupEmitter.emit("subscriberConnectFailed", {
+ delay,
+ error,
+ });
+ };
+ /**
+ * Handles successful subscriber connections by resetting the failed attempts counter.
+ *
+ * @param nodeKey
+ */
+ this.handleSubscriberConnectSucceeded = (nodeKey) => {
+ this.failedAttemptsByNode.delete(nodeKey);
+ };
+ }
+ /**
+ * Get the responsible subscriber.
+ *
+ * @param slot
+ */
+ getResponsibleSubscriber(slot) {
+ const nodeKey = this.clusterSlots[slot][0];
+ const sub = this.shardedSubscribers.get(nodeKey);
+ if (sub && sub.subscriberStatus === "idle") {
+ sub
+ .start()
+ .then(() => {
+ this.handleSubscriberConnectSucceeded(sub.getNodeKey());
+ })
+ .catch((err) => {
+ this.handleSubscriberConnectFailed(err, sub.getNodeKey());
+ });
+ }
+ return sub;
+ }
+ /**
+ * Adds a channel for which this subscriber group is responsible
+ *
+ * @param channels
+ */
+ addChannels(channels) {
+ const slot = calculateSlot(channels[0]);
+ // Check if the all channels belong to the same slot and otherwise reject the operation
+ for (const c of channels) {
+ if (calculateSlot(c) !== slot) {
+ return -1;
+ }
+ }
+ const currChannels = this.channels.get(slot);
+ if (!currChannels) {
+ this.channels.set(slot, channels);
+ }
+ else {
+ this.channels.set(slot, currChannels.concat(channels));
+ }
+ return Array.from(this.channels.values()).reduce((sum, array) => sum + array.length, 0);
+ }
+ /**
+ * Removes channels for which the subscriber group is responsible by optionally unsubscribing
+ * @param channels
+ */
+ removeChannels(channels) {
+ const slot = calculateSlot(channels[0]);
+ // Check if the all channels belong to the same slot and otherwise reject the operation
+ for (const c of channels) {
+ if (calculateSlot(c) !== slot) {
+ return -1;
+ }
+ }
+ const slotChannels = this.channels.get(slot);
+ if (slotChannels) {
+ const updatedChannels = slotChannels.filter((c) => !channels.includes(c));
+ this.channels.set(slot, updatedChannels);
+ }
+ return Array.from(this.channels.values()).reduce((sum, array) => sum + array.length, 0);
+ }
+ /**
+ * Disconnect all subscribers and clear some of the internal state.
+ */
+ stop() {
+ for (const s of this.shardedSubscribers.values()) {
+ s.stop();
+ }
+ // Clear subscriber instances and pending operations.
+ // Channels are preserved for resubscription on reconnect.
+ this.pendingReset = null;
+ this.shardedSubscribers.clear();
+ this.subscriberToSlotsIndex.clear();
+ }
+ /**
+ * Start all not yet started subscribers
+ */
+ start() {
+ const startPromises = [];
+ for (const s of this.shardedSubscribers.values()) {
+ if (this.shouldStartSubscriber(s)) {
+ startPromises.push(s
+ .start()
+ .then(() => {
+ this.handleSubscriberConnectSucceeded(s.getNodeKey());
+ })
+ .catch((err) => {
+ this.handleSubscriberConnectFailed(err, s.getNodeKey());
+ }));
+ this.subscriberGroupEmitter.emit("+subscriber");
+ }
+ }
+ return Promise.all(startPromises);
+ }
+ /**
+ * Resets the subscriber group by disconnecting all subscribers that are no longer needed and connecting new ones.
+ */
+ async reset(clusterSlots, clusterNodes) {
+ if (this.isResetting) {
+ this.pendingReset = { slots: clusterSlots, nodes: clusterNodes };
+ return;
+ }
+ this.isResetting = true;
+ try {
+ const hasTopologyChanged = this._refreshSlots(clusterSlots);
+ const hasFailedSubscribers = this.hasUnhealthySubscribers();
+ if (!hasTopologyChanged && !hasFailedSubscribers) {
+ debug("No topology change detected or failed subscribers. Skipping reset.");
+ return;
+ }
+ // For each of the sharded subscribers
+ for (const [nodeKey, shardedSubscriber] of this.shardedSubscribers) {
+ if (
+ // If the subscriber is still responsible for a slot range and is healthy then keep it
+ this.subscriberToSlotsIndex.has(nodeKey) &&
+ shardedSubscriber.isHealthy()) {
+ debug("Skipping deleting subscriber for %s", nodeKey);
+ continue;
+ }
+ debug("Removing subscriber for %s", nodeKey);
+ // Otherwise stop the subscriber and remove it
+ shardedSubscriber.stop();
+ this.shardedSubscribers.delete(nodeKey);
+ this.subscriberGroupEmitter.emit("-subscriber");
+ }
+ const startPromises = [];
+ // For each node in slots cache
+ for (const [nodeKey, _] of this.subscriberToSlotsIndex) {
+ const existingSubscriber = this.shardedSubscribers.get(nodeKey);
+ // If we already have a subscriber for this node, only ensure it is healthy
+ // when it now owns slots with active channel subscriptions.
+ if (existingSubscriber && existingSubscriber.isHealthy()) {
+ debug("Skipping creating new subscriber for %s", nodeKey);
+ if (!existingSubscriber.isStarted() &&
+ this.shouldStartSubscriber(existingSubscriber)) {
+ startPromises.push(existingSubscriber
+ .start()
+ .then(() => {
+ this.handleSubscriberConnectSucceeded(nodeKey);
+ })
+ .catch((error) => {
+ this.handleSubscriberConnectFailed(error, nodeKey);
+ }));
+ }
+ continue;
+ }
+ // If we have an existing subscriber but it is not healthy, stop it
+ if (existingSubscriber && !existingSubscriber.isHealthy()) {
+ debug("Replacing subscriber for %s", nodeKey);
+ existingSubscriber.stop();
+ this.shardedSubscribers.delete(nodeKey);
+ this.subscriberGroupEmitter.emit("-subscriber");
+ }
+ debug("Creating new subscriber for %s", nodeKey);
+ // Otherwise create a new subscriber
+ const redis = clusterNodes.find((node) => {
+ return (0, util_1.getNodeKey)(node.options) === nodeKey;
+ });
+ if (!redis) {
+ debug("Failed to find node for key %s", nodeKey);
+ continue;
+ }
+ const sub = new ShardedSubscriber_1.default(this.subscriberGroupEmitter, redis.options, this.options.redisOptions);
+ this.shardedSubscribers.set(nodeKey, sub);
+ if (this.shouldStartSubscriber(sub)) {
+ startPromises.push(sub
+ .start()
+ .then(() => {
+ this.handleSubscriberConnectSucceeded(nodeKey);
+ })
+ .catch((error) => {
+ this.handleSubscriberConnectFailed(error, nodeKey);
+ }));
+ }
+ this.subscriberGroupEmitter.emit("+subscriber");
+ }
+ // It's vital to await the start promises before resubscribing
+ // Otherwise we might try to resubscribe to a subscriber that is not yet connected
+ // This can cause a race condition
+ await Promise.all(startPromises);
+ this._resubscribe();
+ this.subscriberGroupEmitter.emit("subscribersReady");
+ }
+ finally {
+ this.isResetting = false;
+ if (this.pendingReset) {
+ const { slots, nodes } = this.pendingReset;
+ this.pendingReset = null;
+ await this.reset(slots, nodes);
+ }
+ }
+ }
+ /**
+ * Refreshes the subscriber-related slot ranges
+ *
+ * Returns false if no refresh was needed
+ *
+ * @param targetSlots
+ */
+ _refreshSlots(targetSlots) {
+ //If there was an actual change, then reassign the slot ranges
+ // Also rebuild if subscriberToSlotsIndex is empty (e.g., after stop() was called)
+ if (this._slotsAreEqual(targetSlots) && this.subscriberToSlotsIndex.size > 0) {
+ debug("Nothing to refresh because the new cluster map is equal to the previous one.");
+ return false;
+ }
+ debug("Refreshing the slots of the subscriber group.");
+ //Rebuild the slots index
+ this.subscriberToSlotsIndex = new Map();
+ for (let slot = 0; slot < targetSlots.length; slot++) {
+ const node = targetSlots[slot][0];
+ if (!this.subscriberToSlotsIndex.has(node)) {
+ this.subscriberToSlotsIndex.set(node, []);
+ }
+ this.subscriberToSlotsIndex.get(node).push(Number(slot));
+ }
+ //Update the cached slots map
+ this.clusterSlots = JSON.parse(JSON.stringify(targetSlots));
+ return true;
+ }
+ /**
+ * Resubscribes to the previous channels
+ *
+ * @private
+ */
+ _resubscribe() {
+ if (this.shardedSubscribers) {
+ this.shardedSubscribers.forEach((s, nodeKey) => {
+ const subscriberSlots = this.subscriberToSlotsIndex.get(nodeKey);
+ if (subscriberSlots) {
+ //Resubscribe on the underlying connection
+ subscriberSlots.forEach((ss) => {
+ //Might return null if being disconnected
+ const redis = s.getInstance();
+ const channels = this.channels.get(ss);
+ if (channels && channels.length > 0) {
+ if (!redis || redis.status === "end") {
+ return;
+ }
+ if (redis.status === "ready") {
+ redis.ssubscribe(...channels).catch((err) => {
+ // TODO: Should we emit an error event here?
+ debug("Failed to ssubscribe on node %s: %s", nodeKey, err);
+ });
+ }
+ else {
+ redis.once("ready", () => {
+ redis.ssubscribe(...channels).catch((err) => {
+ // TODO: Should we emit an error event here?
+ debug("Failed to ssubscribe on node %s: %s", nodeKey, err);
+ });
+ });
+ }
+ }
+ });
+ }
+ });
+ }
+ }
+ /**
+ * Deep equality of the cluster slots objects
+ *
+ * @param other
+ * @private
+ */
+ _slotsAreEqual(other) {
+ if (this.clusterSlots === undefined) {
+ return false;
+ }
+ else {
+ return JSON.stringify(this.clusterSlots) === JSON.stringify(other);
+ }
+ }
+ /**
+ * Checks if any subscribers are in an unhealthy state.
+ *
+ * A subscriber is considered unhealthy if:
+ * - It exists but is not started (failed/disconnected)
+ * - It's missing entirely for a node that should have one
+ *
+ * @returns true if any subscribers need to be recreated
+ */
+ hasUnhealthySubscribers() {
+ const hasFailedSubscribers = Array.from(this.shardedSubscribers.values()).some((sub) => !sub.isHealthy());
+ const hasMissingSubscribers = Array.from(this.subscriberToSlotsIndex.keys()).some((nodeKey) => !this.shardedSubscribers.has(nodeKey));
+ return hasFailedSubscribers || hasMissingSubscribers;
+ }
+ shouldStartSubscriber(sub) {
+ if (sub.isStarted()) {
+ return false;
+ }
+ if (!sub.isLazyConnect()) {
+ return true;
+ }
+ const subscriberSlots = this.subscriberToSlotsIndex.get(sub.getNodeKey());
+ if (!subscriberSlots) {
+ return false;
+ }
+ return subscriberSlots.some((slot) => {
+ const channels = this.channels.get(slot);
+ return Boolean(channels && channels.length > 0);
+ });
+ }
+}
+exports.default = ClusterSubscriberGroup;
+// Retry strategy
+ClusterSubscriberGroup.MAX_RETRY_ATTEMPTS = 10;
+ClusterSubscriberGroup.MAX_BACKOFF_MS = 2000;
+ClusterSubscriberGroup.BASE_BACKOFF_MS = 100;
diff --git a/node_modules/ioredis/built/cluster/ConnectionPool.d.ts b/node_modules/ioredis/built/cluster/ConnectionPool.d.ts
new file mode 100644
index 0000000..0de5d7a
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ConnectionPool.d.ts
@@ -0,0 +1,37 @@
+///
+import { EventEmitter } from "events";
+import { RedisOptions, NodeKey, NodeRole } from "./util";
+import Redis from "../Redis";
+export default class ConnectionPool extends EventEmitter {
+ private redisOptions;
+ private nodes;
+ private specifiedOptions;
+ constructor(redisOptions: any);
+ getNodes(role?: NodeRole): Redis[];
+ getInstanceByKey(key: NodeKey): Redis;
+ getSampleInstance(role: NodeRole): Redis;
+ /**
+ * Add a master node to the pool
+ * @param node
+ */
+ addMasterNode(node: RedisOptions): boolean;
+ /**
+ * Creates a Redis connection instance from the node options
+ * @param node
+ * @param readOnly
+ */
+ createRedisFromOptions(node: RedisOptions, readOnly: boolean): Redis;
+ /**
+ * Find or create a connection to the node
+ */
+ findOrCreate(node: RedisOptions, readOnly?: boolean): Redis;
+ /**
+ * Reset the pool with a set of nodes.
+ * The old node will be removed.
+ */
+ reset(nodes: RedisOptions[]): void;
+ /**
+ * Remove a node from the pool.
+ */
+ private removeNode;
+}
diff --git a/node_modules/ioredis/built/cluster/ConnectionPool.js b/node_modules/ioredis/built/cluster/ConnectionPool.js
new file mode 100644
index 0000000..4fef1b8
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ConnectionPool.js
@@ -0,0 +1,154 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const events_1 = require("events");
+const utils_1 = require("../utils");
+const util_1 = require("./util");
+const Redis_1 = require("../Redis");
+const debug = (0, utils_1.Debug)("cluster:connectionPool");
+class ConnectionPool extends events_1.EventEmitter {
+ constructor(redisOptions) {
+ super();
+ this.redisOptions = redisOptions;
+ // master + slave = all
+ this.nodes = {
+ all: {},
+ master: {},
+ slave: {},
+ };
+ this.specifiedOptions = {};
+ }
+ getNodes(role = "all") {
+ const nodes = this.nodes[role];
+ return Object.keys(nodes).map((key) => nodes[key]);
+ }
+ getInstanceByKey(key) {
+ return this.nodes.all[key];
+ }
+ getSampleInstance(role) {
+ const keys = Object.keys(this.nodes[role]);
+ const sampleKey = (0, utils_1.sample)(keys);
+ return this.nodes[role][sampleKey];
+ }
+ /**
+ * Add a master node to the pool
+ * @param node
+ */
+ addMasterNode(node) {
+ const key = (0, util_1.getNodeKey)(node.options);
+ const redis = this.createRedisFromOptions(node, node.options.readOnly);
+ //Master nodes aren't read-only
+ if (!node.options.readOnly) {
+ this.nodes.all[key] = redis;
+ this.nodes.master[key] = redis;
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Creates a Redis connection instance from the node options
+ * @param node
+ * @param readOnly
+ */
+ createRedisFromOptions(node, readOnly) {
+ const redis = new Redis_1.default((0, utils_1.defaults)({
+ // Never try to reconnect when a node is lose,
+ // instead, waiting for a `MOVED` error and
+ // fetch the slots again.
+ retryStrategy: null,
+ // Offline queue should be enabled so that
+ // we don't need to wait for the `ready` event
+ // before sending commands to the node.
+ enableOfflineQueue: true,
+ readOnly: readOnly,
+ }, node, this.redisOptions, { lazyConnect: true }));
+ return redis;
+ }
+ /**
+ * Find or create a connection to the node
+ */
+ findOrCreate(node, readOnly = false) {
+ const key = (0, util_1.getNodeKey)(node);
+ readOnly = Boolean(readOnly);
+ if (this.specifiedOptions[key]) {
+ Object.assign(node, this.specifiedOptions[key]);
+ }
+ else {
+ this.specifiedOptions[key] = node;
+ }
+ let redis;
+ if (this.nodes.all[key]) {
+ redis = this.nodes.all[key];
+ if (redis.options.readOnly !== readOnly) {
+ redis.options.readOnly = readOnly;
+ debug("Change role of %s to %s", key, readOnly ? "slave" : "master");
+ redis[readOnly ? "readonly" : "readwrite"]().catch(utils_1.noop);
+ if (readOnly) {
+ delete this.nodes.master[key];
+ this.nodes.slave[key] = redis;
+ }
+ else {
+ delete this.nodes.slave[key];
+ this.nodes.master[key] = redis;
+ }
+ }
+ }
+ else {
+ debug("Connecting to %s as %s", key, readOnly ? "slave" : "master");
+ redis = this.createRedisFromOptions(node, readOnly);
+ this.nodes.all[key] = redis;
+ this.nodes[readOnly ? "slave" : "master"][key] = redis;
+ redis.once("end", () => {
+ this.removeNode(key);
+ this.emit("-node", redis, key);
+ if (!Object.keys(this.nodes.all).length) {
+ this.emit("drain");
+ }
+ });
+ this.emit("+node", redis, key);
+ redis.on("error", function (error) {
+ this.emit("nodeError", error, key);
+ });
+ }
+ return redis;
+ }
+ /**
+ * Reset the pool with a set of nodes.
+ * The old node will be removed.
+ */
+ reset(nodes) {
+ debug("Reset with %O", nodes);
+ const newNodes = {};
+ nodes.forEach((node) => {
+ const key = (0, util_1.getNodeKey)(node);
+ // Don't override the existing (master) node
+ // when the current one is slave.
+ if (!(node.readOnly && newNodes[key])) {
+ newNodes[key] = node;
+ }
+ });
+ Object.keys(this.nodes.all).forEach((key) => {
+ if (!newNodes[key]) {
+ debug("Disconnect %s because the node does not hold any slot", key);
+ this.nodes.all[key].disconnect();
+ this.removeNode(key);
+ }
+ });
+ Object.keys(newNodes).forEach((key) => {
+ const node = newNodes[key];
+ this.findOrCreate(node, node.readOnly);
+ });
+ }
+ /**
+ * Remove a node from the pool.
+ */
+ removeNode(key) {
+ const { nodes } = this;
+ if (nodes.all[key]) {
+ debug("Remove %s from the pool", key);
+ delete nodes.all[key];
+ }
+ delete nodes.master[key];
+ delete nodes.slave[key];
+ }
+}
+exports.default = ConnectionPool;
diff --git a/node_modules/ioredis/built/cluster/DelayQueue.d.ts b/node_modules/ioredis/built/cluster/DelayQueue.d.ts
new file mode 100644
index 0000000..2d64896
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/DelayQueue.d.ts
@@ -0,0 +1,20 @@
+export interface DelayQueueOptions {
+ callback?: Function;
+ timeout: number;
+}
+/**
+ * Queue that runs items after specified duration
+ */
+export default class DelayQueue {
+ private queues;
+ private timeouts;
+ /**
+ * Add a new item to the queue
+ *
+ * @param bucket bucket name
+ * @param item function that will run later
+ * @param options
+ */
+ push(bucket: string, item: Function, options: DelayQueueOptions): void;
+ private execute;
+}
diff --git a/node_modules/ioredis/built/cluster/DelayQueue.js b/node_modules/ioredis/built/cluster/DelayQueue.js
new file mode 100644
index 0000000..e17df51
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/DelayQueue.js
@@ -0,0 +1,53 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const utils_1 = require("../utils");
+const Deque = require("denque");
+const debug = (0, utils_1.Debug)("delayqueue");
+/**
+ * Queue that runs items after specified duration
+ */
+class DelayQueue {
+ constructor() {
+ this.queues = {};
+ this.timeouts = {};
+ }
+ /**
+ * Add a new item to the queue
+ *
+ * @param bucket bucket name
+ * @param item function that will run later
+ * @param options
+ */
+ push(bucket, item, options) {
+ const callback = options.callback || process.nextTick;
+ if (!this.queues[bucket]) {
+ this.queues[bucket] = new Deque();
+ }
+ const queue = this.queues[bucket];
+ queue.push(item);
+ if (!this.timeouts[bucket]) {
+ this.timeouts[bucket] = setTimeout(() => {
+ callback(() => {
+ this.timeouts[bucket] = null;
+ this.execute(bucket);
+ });
+ }, options.timeout);
+ }
+ }
+ execute(bucket) {
+ const queue = this.queues[bucket];
+ if (!queue) {
+ return;
+ }
+ const { length } = queue;
+ if (!length) {
+ return;
+ }
+ debug("send %d commands in %s queue", length, bucket);
+ this.queues[bucket] = null;
+ while (queue.length > 0) {
+ queue.shift()();
+ }
+ }
+}
+exports.default = DelayQueue;
diff --git a/node_modules/ioredis/built/cluster/ShardedSubscriber.d.ts b/node_modules/ioredis/built/cluster/ShardedSubscriber.d.ts
new file mode 100644
index 0000000..921bcdb
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ShardedSubscriber.d.ts
@@ -0,0 +1,36 @@
+///
+import EventEmitter = require("events");
+import { RedisOptions } from "./util";
+import Redis from "../Redis";
+import { ClusterOptions } from "./ClusterOptions";
+declare const SubscriberStatus: {
+ readonly IDLE: "idle";
+ readonly STARTING: "starting";
+ readonly CONNECTED: "connected";
+ readonly STOPPING: "stopping";
+ readonly ENDED: "ended";
+};
+declare type SubscriberStatus = typeof SubscriberStatus[keyof typeof SubscriberStatus];
+export default class ShardedSubscriber {
+ private readonly emitter;
+ private readonly nodeKey;
+ private status;
+ private instance;
+ private connectPromise;
+ private lazyConnect;
+ private readonly messageListeners;
+ constructor(emitter: EventEmitter, options: RedisOptions, redisOptions?: ClusterOptions["redisOptions"]);
+ start(): Promise;
+ stop(): void;
+ isStarted(): boolean;
+ get subscriberStatus(): SubscriberStatus;
+ isHealthy(): boolean;
+ getInstance(): Redis | null;
+ getNodeKey(): string;
+ isLazyConnect(): boolean;
+ private onEnd;
+ private onError;
+ private onMoved;
+ private updateStatus;
+}
+export {};
diff --git a/node_modules/ioredis/built/cluster/ShardedSubscriber.js b/node_modules/ioredis/built/cluster/ShardedSubscriber.js
new file mode 100644
index 0000000..163fb63
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/ShardedSubscriber.js
@@ -0,0 +1,147 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const util_1 = require("./util");
+const utils_1 = require("../utils");
+const Redis_1 = require("../Redis");
+const debug = (0, utils_1.Debug)("cluster:subscriberGroup:shardedSubscriber");
+const SubscriberStatus = {
+ IDLE: "idle",
+ STARTING: "starting",
+ CONNECTED: "connected",
+ STOPPING: "stopping",
+ ENDED: "ended",
+};
+const ALLOWED_STATUS_UPDATES = {
+ [SubscriberStatus.IDLE]: [
+ SubscriberStatus.STARTING,
+ SubscriberStatus.STOPPING,
+ SubscriberStatus.ENDED,
+ ],
+ [SubscriberStatus.STARTING]: [
+ SubscriberStatus.CONNECTED,
+ SubscriberStatus.STOPPING,
+ SubscriberStatus.ENDED,
+ ],
+ [SubscriberStatus.CONNECTED]: [
+ SubscriberStatus.STOPPING,
+ SubscriberStatus.ENDED,
+ ],
+ [SubscriberStatus.STOPPING]: [SubscriberStatus.ENDED],
+ [SubscriberStatus.ENDED]: [],
+};
+class ShardedSubscriber {
+ constructor(emitter, options, redisOptions) {
+ var _a;
+ this.emitter = emitter;
+ this.status = SubscriberStatus.IDLE;
+ this.instance = null;
+ this.connectPromise = null;
+ // Store listener references for cleanup
+ this.messageListeners = new Map();
+ this.onEnd = () => {
+ this.updateStatus(SubscriberStatus.ENDED);
+ this.emitter.emit("-node", this.instance, this.nodeKey);
+ };
+ this.onError = (error) => {
+ this.emitter.emit("nodeError", error, this.nodeKey);
+ };
+ this.onMoved = () => {
+ this.emitter.emit("moved");
+ };
+ this.instance = new Redis_1.default((0, utils_1.defaults)({
+ enableReadyCheck: false,
+ enableOfflineQueue: true,
+ connectionName: (0, util_1.getConnectionName)("ssubscriber", options.connectionName),
+ /**
+ * Disable auto reconnection for subscribers.
+ * The ClusterSubscriberGroup will handle the reconnection.
+ */
+ retryStrategy: null,
+ lazyConnect: true,
+ }, options, redisOptions));
+ this.lazyConnect = (_a = redisOptions === null || redisOptions === void 0 ? void 0 : redisOptions.lazyConnect) !== null && _a !== void 0 ? _a : true;
+ this.nodeKey = (0, util_1.getNodeKey)(options);
+ // Register listeners
+ this.instance.on("end", this.onEnd);
+ this.instance.on("error", this.onError);
+ this.instance.on("moved", this.onMoved);
+ for (const event of ["smessage", "smessageBuffer"]) {
+ const listener = (...args) => {
+ this.emitter.emit(event, ...args);
+ };
+ this.messageListeners.set(event, listener);
+ this.instance.on(event, listener);
+ }
+ }
+ async start() {
+ if (this.connectPromise) {
+ return this.connectPromise;
+ }
+ if (this.status === SubscriberStatus.STARTING ||
+ this.status === SubscriberStatus.CONNECTED) {
+ return;
+ }
+ if (this.status === SubscriberStatus.ENDED || !this.instance) {
+ throw new Error(`Sharded subscriber ${this.nodeKey} cannot be restarted once ended.`);
+ }
+ this.updateStatus(SubscriberStatus.STARTING);
+ this.connectPromise = this.instance.connect();
+ try {
+ await this.connectPromise;
+ this.updateStatus(SubscriberStatus.CONNECTED);
+ }
+ catch (err) {
+ this.updateStatus(SubscriberStatus.ENDED);
+ throw err;
+ }
+ finally {
+ this.connectPromise = null;
+ }
+ }
+ stop() {
+ this.updateStatus(SubscriberStatus.STOPPING);
+ if (this.instance) {
+ this.instance.disconnect();
+ this.instance.removeAllListeners();
+ this.messageListeners.clear();
+ this.instance = null;
+ }
+ this.updateStatus(SubscriberStatus.ENDED);
+ debug("stopped %s", this.nodeKey);
+ }
+ isStarted() {
+ return [
+ SubscriberStatus.CONNECTED,
+ SubscriberStatus.STARTING,
+ ].includes(this.status);
+ }
+ get subscriberStatus() {
+ return this.status;
+ }
+ isHealthy() {
+ return ((this.status === SubscriberStatus.IDLE ||
+ this.status === SubscriberStatus.CONNECTED ||
+ this.status === SubscriberStatus.STARTING) &&
+ this.instance !== null);
+ }
+ getInstance() {
+ return this.instance;
+ }
+ getNodeKey() {
+ return this.nodeKey;
+ }
+ isLazyConnect() {
+ return this.lazyConnect;
+ }
+ updateStatus(nextStatus) {
+ if (this.status === nextStatus) {
+ return;
+ }
+ if (!ALLOWED_STATUS_UPDATES[this.status].includes(nextStatus)) {
+ debug("Invalid status transition for %s: %s -> %s", this.nodeKey, this.status, nextStatus);
+ return;
+ }
+ this.status = nextStatus;
+ }
+}
+exports.default = ShardedSubscriber;
diff --git a/node_modules/ioredis/built/cluster/index.d.ts b/node_modules/ioredis/built/cluster/index.d.ts
new file mode 100644
index 0000000..3f5e1b4
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/index.d.ts
@@ -0,0 +1,163 @@
+///
+import { EventEmitter } from "events";
+import Command from "../Command";
+import Redis from "../Redis";
+import ScanStream from "../ScanStream";
+import { Transaction } from "../transaction";
+import { Callback, ScanStreamOptions, WriteableStream } from "../types";
+import Commander from "../utils/Commander";
+import { ClusterOptions } from "./ClusterOptions";
+import { NodeKey, NodeRole } from "./util";
+export declare type ClusterNode = string | number | {
+ host?: string | undefined;
+ port?: number | undefined;
+};
+declare type ClusterStatus = "end" | "close" | "wait" | "connecting" | "connect" | "ready" | "reconnecting" | "disconnecting";
+/**
+ * Client for the official Redis Cluster
+ */
+declare class Cluster extends Commander {
+ options: ClusterOptions;
+ slots: NodeKey[][];
+ status: ClusterStatus;
+ /**
+ * @ignore
+ */
+ _groupsIds: {
+ [key: string]: number;
+ };
+ /**
+ * @ignore
+ */
+ _groupsBySlot: number[];
+ /**
+ * @ignore
+ */
+ isCluster: boolean;
+ private startupNodes;
+ private connectionPool;
+ private manuallyClosing;
+ private retryAttempts;
+ private delayQueue;
+ private offlineQueue;
+ private subscriber;
+ private shardedSubscribers;
+ private slotsTimer;
+ private reconnectTimeout;
+ private isRefreshing;
+ private _refreshSlotsCacheCallbacks;
+ private _autoPipelines;
+ private _runningAutoPipelines;
+ private _readyDelayedCallbacks;
+ private subscriberGroupEmitter;
+ /**
+ * Every time Cluster#connect() is called, this value will be
+ * auto-incrementing. The purpose of this value is used for
+ * discarding previous connect attampts when creating a new
+ * connection.
+ */
+ private connectionEpoch;
+ /**
+ * Creates an instance of Cluster.
+ */
+ constructor(startupNodes: ClusterNode[], options?: ClusterOptions);
+ /**
+ * Connect to a cluster
+ */
+ connect(): Promise;
+ /**
+ * Disconnect from every node in the cluster.
+ */
+ disconnect(reconnect?: boolean): void;
+ /**
+ * Quit the cluster gracefully.
+ */
+ quit(callback?: Callback<"OK">): Promise<"OK">;
+ /**
+ * Create a new instance with the same startup nodes and options as the current one.
+ *
+ * @example
+ * ```js
+ * var cluster = new Redis.Cluster([{ host: "127.0.0.1", port: "30001" }]);
+ * var anotherCluster = cluster.duplicate();
+ * ```
+ */
+ duplicate(overrideStartupNodes?: any[], overrideOptions?: {}): Cluster;
+ /**
+ * Get nodes with the specified role
+ */
+ nodes(role?: NodeRole): Redis[];
+ /**
+ * This is needed in order not to install a listener for each auto pipeline
+ *
+ * @ignore
+ */
+ delayUntilReady(callback: Callback): void;
+ /**
+ * Get the number of commands queued in automatic pipelines.
+ *
+ * This is not available (and returns 0) until the cluster is connected and slots information have been received.
+ */
+ get autoPipelineQueueSize(): number;
+ /**
+ * Refresh the slot cache
+ *
+ * @ignore
+ */
+ refreshSlotsCache(callback?: Callback): void;
+ /**
+ * @ignore
+ */
+ sendCommand(command: Command, stream?: WriteableStream, node?: any): unknown;
+ sscanStream(key: string, options?: ScanStreamOptions): ScanStream;
+ sscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
+ hscanStream(key: string, options?: ScanStreamOptions): ScanStream;
+ hscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
+ zscanStream(key: string, options?: ScanStreamOptions): ScanStream;
+ zscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
+ /**
+ * @ignore
+ */
+ handleError(error: Error, ttl: {
+ value?: any;
+ }, handlers: any): void;
+ private resetOfflineQueue;
+ private clearNodesRefreshInterval;
+ private resetNodesRefreshInterval;
+ /**
+ * Change cluster instance's status
+ */
+ private setStatus;
+ /**
+ * Called when closed to check whether a reconnection should be made
+ */
+ private handleCloseEvent;
+ /**
+ * Flush offline queue with error.
+ */
+ private flushQueue;
+ private executeOfflineCommands;
+ private natMapper;
+ private getInfoFromNode;
+ private invokeReadyDelayedCallbacks;
+ /**
+ * Check whether Cluster is able to process commands
+ */
+ private readyCheck;
+ private resolveSrv;
+ private dnsLookup;
+ /**
+ * Normalize startup nodes, and resolving hostnames to IPs.
+ *
+ * This process happens every time when #connect() is called since
+ * #startupNodes and DNS records may chanage.
+ */
+ private resolveStartupNodeHostnames;
+ private createScanStream;
+ private createShardedSubscriberGroup;
+}
+interface Cluster extends EventEmitter {
+}
+interface Cluster extends Transaction {
+}
+export default Cluster;
diff --git a/node_modules/ioredis/built/cluster/index.js b/node_modules/ioredis/built/cluster/index.js
new file mode 100644
index 0000000..1c8d57b
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/index.js
@@ -0,0 +1,937 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const commands_1 = require("@ioredis/commands");
+const events_1 = require("events");
+const redis_errors_1 = require("redis-errors");
+const standard_as_callback_1 = require("standard-as-callback");
+const Command_1 = require("../Command");
+const ClusterAllFailedError_1 = require("../errors/ClusterAllFailedError");
+const Redis_1 = require("../Redis");
+const ScanStream_1 = require("../ScanStream");
+const transaction_1 = require("../transaction");
+const utils_1 = require("../utils");
+const applyMixin_1 = require("../utils/applyMixin");
+const Commander_1 = require("../utils/Commander");
+const ClusterOptions_1 = require("./ClusterOptions");
+const ClusterSubscriber_1 = require("./ClusterSubscriber");
+const ConnectionPool_1 = require("./ConnectionPool");
+const DelayQueue_1 = require("./DelayQueue");
+const util_1 = require("./util");
+const Deque = require("denque");
+const ClusterSubscriberGroup_1 = require("./ClusterSubscriberGroup");
+const debug = (0, utils_1.Debug)("cluster");
+const REJECT_OVERWRITTEN_COMMANDS = new WeakSet();
+/**
+ * Client for the official Redis Cluster
+ */
+class Cluster extends Commander_1.default {
+ /**
+ * Creates an instance of Cluster.
+ */
+ //TODO: Add an option that enables or disables sharded PubSub
+ constructor(startupNodes, options = {}) {
+ super();
+ this.slots = [];
+ /**
+ * @ignore
+ */
+ this._groupsIds = {};
+ /**
+ * @ignore
+ */
+ this._groupsBySlot = Array(16384);
+ /**
+ * @ignore
+ */
+ this.isCluster = true;
+ this.retryAttempts = 0;
+ this.delayQueue = new DelayQueue_1.default();
+ this.offlineQueue = new Deque();
+ this.isRefreshing = false;
+ this._refreshSlotsCacheCallbacks = [];
+ this._autoPipelines = new Map();
+ this._runningAutoPipelines = new Set();
+ this._readyDelayedCallbacks = [];
+ /**
+ * Every time Cluster#connect() is called, this value will be
+ * auto-incrementing. The purpose of this value is used for
+ * discarding previous connect attampts when creating a new
+ * connection.
+ */
+ this.connectionEpoch = 0;
+ events_1.EventEmitter.call(this);
+ this.startupNodes = startupNodes;
+ this.options = (0, utils_1.defaults)({}, options, ClusterOptions_1.DEFAULT_CLUSTER_OPTIONS, this.options);
+ if (this.options.shardedSubscribers) {
+ this.createShardedSubscriberGroup();
+ }
+ if (this.options.redisOptions &&
+ this.options.redisOptions.keyPrefix &&
+ !this.options.keyPrefix) {
+ this.options.keyPrefix = this.options.redisOptions.keyPrefix;
+ }
+ // validate options
+ if (typeof this.options.scaleReads !== "function" &&
+ ["all", "master", "slave"].indexOf(this.options.scaleReads) === -1) {
+ throw new Error('Invalid option scaleReads "' +
+ this.options.scaleReads +
+ '". Expected "all", "master", "slave" or a custom function');
+ }
+ this.connectionPool = new ConnectionPool_1.default(this.options.redisOptions);
+ this.connectionPool.on("-node", (redis, key) => {
+ this.emit("-node", redis);
+ });
+ this.connectionPool.on("+node", (redis) => {
+ this.emit("+node", redis);
+ });
+ this.connectionPool.on("drain", () => {
+ this.setStatus("close");
+ });
+ this.connectionPool.on("nodeError", (error, key) => {
+ this.emit("node error", error, key);
+ });
+ this.subscriber = new ClusterSubscriber_1.default(this.connectionPool, this);
+ if (this.options.scripts) {
+ Object.entries(this.options.scripts).forEach(([name, definition]) => {
+ this.defineCommand(name, definition);
+ });
+ }
+ if (this.options.lazyConnect) {
+ this.setStatus("wait");
+ }
+ else {
+ this.connect().catch((err) => {
+ debug("connecting failed: %s", err);
+ });
+ }
+ }
+ /**
+ * Connect to a cluster
+ */
+ connect() {
+ return new Promise((resolve, reject) => {
+ if (this.status === "connecting" ||
+ this.status === "connect" ||
+ this.status === "ready") {
+ reject(new Error("Redis is already connecting/connected"));
+ return;
+ }
+ const epoch = ++this.connectionEpoch;
+ this.setStatus("connecting");
+ this.resolveStartupNodeHostnames()
+ .then((nodes) => {
+ if (this.connectionEpoch !== epoch) {
+ debug("discard connecting after resolving startup nodes because epoch not match: %d != %d", epoch, this.connectionEpoch);
+ reject(new redis_errors_1.RedisError("Connection is discarded because a new connection is made"));
+ return;
+ }
+ if (this.status !== "connecting") {
+ debug("discard connecting after resolving startup nodes because the status changed to %s", this.status);
+ reject(new redis_errors_1.RedisError("Connection is aborted"));
+ return;
+ }
+ this.connectionPool.reset(nodes);
+ if (this.options.shardedSubscribers) {
+ this.shardedSubscribers
+ .reset(this.slots, this.connectionPool.getNodes("all"))
+ .catch((err) => {
+ // TODO should we emit an error event here?
+ debug("Error while starting subscribers: %s", err);
+ });
+ }
+ const readyHandler = () => {
+ this.setStatus("ready");
+ this.retryAttempts = 0;
+ this.executeOfflineCommands();
+ this.resetNodesRefreshInterval();
+ resolve();
+ };
+ let closeListener = undefined;
+ const refreshListener = () => {
+ this.invokeReadyDelayedCallbacks(undefined);
+ this.removeListener("close", closeListener);
+ this.manuallyClosing = false;
+ this.setStatus("connect");
+ if (this.options.enableReadyCheck) {
+ this.readyCheck((err, fail) => {
+ if (err || fail) {
+ debug("Ready check failed (%s). Reconnecting...", err || fail);
+ if (this.status === "connect") {
+ this.disconnect(true);
+ }
+ }
+ else {
+ readyHandler();
+ }
+ });
+ }
+ else {
+ readyHandler();
+ }
+ };
+ closeListener = () => {
+ const error = new Error("None of startup nodes is available");
+ this.removeListener("refresh", refreshListener);
+ this.invokeReadyDelayedCallbacks(error);
+ reject(error);
+ };
+ this.once("refresh", refreshListener);
+ this.once("close", closeListener);
+ this.once("close", this.handleCloseEvent.bind(this));
+ this.refreshSlotsCache((err) => {
+ if (err && err.message === ClusterAllFailedError_1.default.defaultMessage) {
+ Redis_1.default.prototype.silentEmit.call(this, "error", err);
+ this.connectionPool.reset([]);
+ }
+ });
+ this.subscriber.start();
+ if (this.options.shardedSubscribers) {
+ this.shardedSubscribers.start().catch((err) => {
+ // TODO should we emit an error event here?
+ debug("Error while starting subscribers: %s", err);
+ });
+ }
+ })
+ .catch((err) => {
+ this.setStatus("close");
+ this.handleCloseEvent(err);
+ this.invokeReadyDelayedCallbacks(err);
+ reject(err);
+ });
+ });
+ }
+ /**
+ * Disconnect from every node in the cluster.
+ */
+ disconnect(reconnect = false) {
+ const status = this.status;
+ this.setStatus("disconnecting");
+ if (!reconnect) {
+ this.manuallyClosing = true;
+ }
+ if (this.reconnectTimeout && !reconnect) {
+ clearTimeout(this.reconnectTimeout);
+ this.reconnectTimeout = null;
+ debug("Canceled reconnecting attempts");
+ }
+ this.clearNodesRefreshInterval();
+ this.subscriber.stop();
+ if (this.options.shardedSubscribers) {
+ this.shardedSubscribers.stop();
+ }
+ if (status === "wait") {
+ this.setStatus("close");
+ this.handleCloseEvent();
+ }
+ else {
+ this.connectionPool.reset([]);
+ }
+ }
+ /**
+ * Quit the cluster gracefully.
+ */
+ quit(callback) {
+ const status = this.status;
+ this.setStatus("disconnecting");
+ this.manuallyClosing = true;
+ if (this.reconnectTimeout) {
+ clearTimeout(this.reconnectTimeout);
+ this.reconnectTimeout = null;
+ }
+ this.clearNodesRefreshInterval();
+ this.subscriber.stop();
+ if (this.options.shardedSubscribers) {
+ this.shardedSubscribers.stop();
+ }
+ if (status === "wait") {
+ const ret = (0, standard_as_callback_1.default)(Promise.resolve("OK"), callback);
+ // use setImmediate to make sure "close" event
+ // being emitted after quit() is returned
+ setImmediate(function () {
+ this.setStatus("close");
+ this.handleCloseEvent();
+ }.bind(this));
+ return ret;
+ }
+ return (0, standard_as_callback_1.default)(Promise.all(this.nodes().map((node) => node.quit().catch((err) => {
+ // Ignore the error caused by disconnecting since
+ // we're disconnecting...
+ if (err.message === utils_1.CONNECTION_CLOSED_ERROR_MSG) {
+ return "OK";
+ }
+ throw err;
+ }))).then(() => "OK"), callback);
+ }
+ /**
+ * Create a new instance with the same startup nodes and options as the current one.
+ *
+ * @example
+ * ```js
+ * var cluster = new Redis.Cluster([{ host: "127.0.0.1", port: "30001" }]);
+ * var anotherCluster = cluster.duplicate();
+ * ```
+ */
+ duplicate(overrideStartupNodes = [], overrideOptions = {}) {
+ const startupNodes = overrideStartupNodes.length > 0
+ ? overrideStartupNodes
+ : this.startupNodes.slice(0);
+ const options = Object.assign({}, this.options, overrideOptions);
+ return new Cluster(startupNodes, options);
+ }
+ /**
+ * Get nodes with the specified role
+ */
+ nodes(role = "all") {
+ if (role !== "all" && role !== "master" && role !== "slave") {
+ throw new Error('Invalid role "' + role + '". Expected "all", "master" or "slave"');
+ }
+ return this.connectionPool.getNodes(role);
+ }
+ /**
+ * This is needed in order not to install a listener for each auto pipeline
+ *
+ * @ignore
+ */
+ delayUntilReady(callback) {
+ this._readyDelayedCallbacks.push(callback);
+ }
+ /**
+ * Get the number of commands queued in automatic pipelines.
+ *
+ * This is not available (and returns 0) until the cluster is connected and slots information have been received.
+ */
+ get autoPipelineQueueSize() {
+ let queued = 0;
+ for (const pipeline of this._autoPipelines.values()) {
+ queued += pipeline.length;
+ }
+ return queued;
+ }
+ /**
+ * Refresh the slot cache
+ *
+ * @ignore
+ */
+ refreshSlotsCache(callback) {
+ if (callback) {
+ this._refreshSlotsCacheCallbacks.push(callback);
+ }
+ if (this.isRefreshing) {
+ return;
+ }
+ this.isRefreshing = true;
+ const _this = this;
+ const wrapper = (error) => {
+ this.isRefreshing = false;
+ for (const callback of this._refreshSlotsCacheCallbacks) {
+ callback(error);
+ }
+ this._refreshSlotsCacheCallbacks = [];
+ };
+ const nodes = (0, utils_1.shuffle)(this.connectionPool.getNodes());
+ let lastNodeError = null;
+ function tryNode(index) {
+ if (index === nodes.length) {
+ const error = new ClusterAllFailedError_1.default(ClusterAllFailedError_1.default.defaultMessage, lastNodeError);
+ return wrapper(error);
+ }
+ const node = nodes[index];
+ const key = `${node.options.host}:${node.options.port}`;
+ debug("getting slot cache from %s", key);
+ _this.getInfoFromNode(node, function (err) {
+ switch (_this.status) {
+ case "close":
+ case "end":
+ return wrapper(new Error("Cluster is disconnected."));
+ case "disconnecting":
+ return wrapper(new Error("Cluster is disconnecting."));
+ }
+ if (err) {
+ _this.emit("node error", err, key);
+ lastNodeError = err;
+ tryNode(index + 1);
+ }
+ else {
+ _this.emit("refresh");
+ wrapper();
+ }
+ });
+ }
+ tryNode(0);
+ }
+ /**
+ * @ignore
+ */
+ sendCommand(command, stream, node) {
+ if (this.status === "wait") {
+ this.connect().catch(utils_1.noop);
+ }
+ if (this.status === "end") {
+ command.reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
+ return command.promise;
+ }
+ let to = this.options.scaleReads;
+ if (to !== "master") {
+ const isCommandReadOnly = command.isReadOnly ||
+ ((0, commands_1.exists)(command.name) && (0, commands_1.hasFlag)(command.name, "readonly"));
+ if (!isCommandReadOnly) {
+ to = "master";
+ }
+ }
+ let targetSlot = node ? node.slot : command.getSlot();
+ const ttl = {};
+ const _this = this;
+ if (!node && !REJECT_OVERWRITTEN_COMMANDS.has(command)) {
+ REJECT_OVERWRITTEN_COMMANDS.add(command);
+ const reject = command.reject;
+ command.reject = function (err) {
+ const partialTry = tryConnection.bind(null, true);
+ _this.handleError(err, ttl, {
+ moved: function (slot, key) {
+ debug("command %s is moved to %s", command.name, key);
+ targetSlot = Number(slot);
+ if (_this.slots[slot]) {
+ _this.slots[slot][0] = key;
+ }
+ else {
+ _this.slots[slot] = [key];
+ }
+ _this._groupsBySlot[slot] =
+ _this._groupsIds[_this.slots[slot].join(";")];
+ _this.connectionPool.findOrCreate(_this.natMapper(key));
+ tryConnection();
+ debug("refreshing slot caches... (triggered by MOVED error)");
+ _this.refreshSlotsCache();
+ },
+ ask: function (slot, key) {
+ debug("command %s is required to ask %s:%s", command.name, key);
+ const mapped = _this.natMapper(key);
+ _this.connectionPool.findOrCreate(mapped);
+ tryConnection(false, `${mapped.host}:${mapped.port}`);
+ },
+ tryagain: partialTry,
+ clusterDown: partialTry,
+ connectionClosed: partialTry,
+ maxRedirections: function (redirectionError) {
+ reject.call(command, redirectionError);
+ },
+ defaults: function () {
+ reject.call(command, err);
+ },
+ });
+ };
+ }
+ tryConnection();
+ function tryConnection(random, asking) {
+ if (_this.status === "end") {
+ command.reject(new redis_errors_1.AbortError("Cluster is ended."));
+ return;
+ }
+ let redis;
+ if (_this.status === "ready" || command.name === "cluster") {
+ if (node && node.redis) {
+ redis = node.redis;
+ }
+ else if (Command_1.default.checkFlag("ENTER_SUBSCRIBER_MODE", command.name) ||
+ Command_1.default.checkFlag("EXIT_SUBSCRIBER_MODE", command.name)) {
+ if (_this.options.shardedSubscribers &&
+ (command.name == "ssubscribe" || command.name == "sunsubscribe")) {
+ const sub = _this.shardedSubscribers.getResponsibleSubscriber(targetSlot);
+ if (!sub) {
+ command.reject(new redis_errors_1.AbortError(`No sharded subscriber for slot: ${targetSlot}`));
+ return;
+ }
+ let status = -1;
+ if (command.name == "ssubscribe") {
+ status = _this.shardedSubscribers.addChannels(command.getKeys());
+ }
+ if (command.name == "sunsubscribe") {
+ status = _this.shardedSubscribers.removeChannels(command.getKeys());
+ }
+ if (status !== -1) {
+ redis = sub.getInstance();
+ }
+ else {
+ command.reject(new redis_errors_1.AbortError("Possible CROSSSLOT error: All channels must hash to the same slot"));
+ }
+ }
+ else {
+ redis = _this.subscriber.getInstance();
+ }
+ if (!redis) {
+ command.reject(new redis_errors_1.AbortError("No subscriber for the cluster"));
+ return;
+ }
+ }
+ else {
+ if (!random) {
+ if (typeof targetSlot === "number" && _this.slots[targetSlot]) {
+ const nodeKeys = _this.slots[targetSlot];
+ if (typeof to === "function") {
+ const nodes = nodeKeys.map(function (key) {
+ return _this.connectionPool.getInstanceByKey(key);
+ });
+ redis = to(nodes, command);
+ if (Array.isArray(redis)) {
+ redis = (0, utils_1.sample)(redis);
+ }
+ if (!redis) {
+ redis = nodes[0];
+ }
+ }
+ else {
+ let key;
+ if (to === "all") {
+ key = (0, utils_1.sample)(nodeKeys);
+ }
+ else if (to === "slave" && nodeKeys.length > 1) {
+ key = (0, utils_1.sample)(nodeKeys, 1);
+ }
+ else {
+ key = nodeKeys[0];
+ }
+ redis = _this.connectionPool.getInstanceByKey(key);
+ }
+ }
+ if (asking) {
+ redis = _this.connectionPool.getInstanceByKey(asking);
+ redis.asking();
+ }
+ }
+ if (!redis) {
+ redis =
+ (typeof to === "function"
+ ? null
+ : _this.connectionPool.getSampleInstance(to)) ||
+ _this.connectionPool.getSampleInstance("all");
+ }
+ }
+ if (node && !node.redis) {
+ node.redis = redis;
+ }
+ }
+ if (redis) {
+ redis.sendCommand(command, stream);
+ }
+ else if (_this.options.enableOfflineQueue) {
+ _this.offlineQueue.push({
+ command: command,
+ stream: stream,
+ node: node,
+ });
+ }
+ else {
+ command.reject(new Error("Cluster isn't ready and enableOfflineQueue options is false"));
+ }
+ }
+ return command.promise;
+ }
+ sscanStream(key, options) {
+ return this.createScanStream("sscan", { key, options });
+ }
+ sscanBufferStream(key, options) {
+ return this.createScanStream("sscanBuffer", { key, options });
+ }
+ hscanStream(key, options) {
+ return this.createScanStream("hscan", { key, options });
+ }
+ hscanBufferStream(key, options) {
+ return this.createScanStream("hscanBuffer", { key, options });
+ }
+ zscanStream(key, options) {
+ return this.createScanStream("zscan", { key, options });
+ }
+ zscanBufferStream(key, options) {
+ return this.createScanStream("zscanBuffer", { key, options });
+ }
+ /**
+ * @ignore
+ */
+ handleError(error, ttl, handlers) {
+ if (typeof ttl.value === "undefined") {
+ ttl.value = this.options.maxRedirections;
+ }
+ else {
+ ttl.value -= 1;
+ }
+ if (ttl.value <= 0) {
+ handlers.maxRedirections(new Error("Too many Cluster redirections. Last error: " + error));
+ return;
+ }
+ const errv = error.message.split(" ");
+ if (errv[0] === "MOVED") {
+ const timeout = this.options.retryDelayOnMoved;
+ if (timeout && typeof timeout === "number") {
+ this.delayQueue.push("moved", handlers.moved.bind(null, errv[1], errv[2]), { timeout });
+ }
+ else {
+ handlers.moved(errv[1], errv[2]);
+ }
+ }
+ else if (errv[0] === "ASK") {
+ handlers.ask(errv[1], errv[2]);
+ }
+ else if (errv[0] === "TRYAGAIN") {
+ this.delayQueue.push("tryagain", handlers.tryagain, {
+ timeout: this.options.retryDelayOnTryAgain,
+ });
+ }
+ else if (errv[0] === "CLUSTERDOWN" &&
+ this.options.retryDelayOnClusterDown > 0) {
+ this.delayQueue.push("clusterdown", handlers.connectionClosed, {
+ timeout: this.options.retryDelayOnClusterDown,
+ callback: this.refreshSlotsCache.bind(this),
+ });
+ }
+ else if (error.message === utils_1.CONNECTION_CLOSED_ERROR_MSG &&
+ this.options.retryDelayOnFailover > 0 &&
+ this.status === "ready") {
+ this.delayQueue.push("failover", handlers.connectionClosed, {
+ timeout: this.options.retryDelayOnFailover,
+ callback: this.refreshSlotsCache.bind(this),
+ });
+ }
+ else {
+ handlers.defaults();
+ }
+ }
+ resetOfflineQueue() {
+ this.offlineQueue = new Deque();
+ }
+ clearNodesRefreshInterval() {
+ if (this.slotsTimer) {
+ clearTimeout(this.slotsTimer);
+ this.slotsTimer = null;
+ }
+ }
+ resetNodesRefreshInterval() {
+ if (this.slotsTimer || !this.options.slotsRefreshInterval) {
+ return;
+ }
+ const nextRound = () => {
+ this.slotsTimer = setTimeout(() => {
+ debug('refreshing slot caches... (triggered by "slotsRefreshInterval" option)');
+ this.refreshSlotsCache(() => {
+ nextRound();
+ });
+ }, this.options.slotsRefreshInterval);
+ };
+ nextRound();
+ }
+ /**
+ * Change cluster instance's status
+ */
+ setStatus(status) {
+ debug("status: %s -> %s", this.status || "[empty]", status);
+ this.status = status;
+ process.nextTick(() => {
+ this.emit(status);
+ });
+ }
+ /**
+ * Called when closed to check whether a reconnection should be made
+ */
+ handleCloseEvent(reason) {
+ var _a;
+ if (reason) {
+ debug("closed because %s", reason);
+ }
+ let retryDelay;
+ if (!this.manuallyClosing &&
+ typeof this.options.clusterRetryStrategy === "function") {
+ retryDelay = this.options.clusterRetryStrategy.call(this, ++this.retryAttempts, reason);
+ }
+ if (typeof retryDelay === "number") {
+ this.setStatus("reconnecting");
+ this.reconnectTimeout = setTimeout(() => {
+ this.reconnectTimeout = null;
+ debug("Cluster is disconnected. Retrying after %dms", retryDelay);
+ this.connect().catch(function (err) {
+ debug("Got error %s when reconnecting. Ignoring...", err);
+ });
+ }, retryDelay);
+ }
+ else {
+ if (this.options.shardedSubscribers) {
+ (_a = this.subscriberGroupEmitter) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
+ }
+ this.setStatus("end");
+ this.flushQueue(new Error("None of startup nodes is available"));
+ }
+ }
+ /**
+ * Flush offline queue with error.
+ */
+ flushQueue(error) {
+ let item;
+ while ((item = this.offlineQueue.shift())) {
+ item.command.reject(error);
+ }
+ }
+ executeOfflineCommands() {
+ if (this.offlineQueue.length) {
+ debug("send %d commands in offline queue", this.offlineQueue.length);
+ const offlineQueue = this.offlineQueue;
+ this.resetOfflineQueue();
+ let item;
+ while ((item = offlineQueue.shift())) {
+ this.sendCommand(item.command, item.stream, item.node);
+ }
+ }
+ }
+ natMapper(nodeKey) {
+ const key = typeof nodeKey === "string"
+ ? nodeKey
+ : `${nodeKey.host}:${nodeKey.port}`;
+ let mapped = null;
+ if (this.options.natMap && typeof this.options.natMap === "function") {
+ mapped = this.options.natMap(key);
+ }
+ else if (this.options.natMap && typeof this.options.natMap === "object") {
+ mapped = this.options.natMap[key];
+ }
+ if (mapped) {
+ debug("NAT mapping %s -> %O", key, mapped);
+ return Object.assign({}, mapped);
+ }
+ return typeof nodeKey === "string"
+ ? (0, util_1.nodeKeyToRedisOptions)(nodeKey)
+ : nodeKey;
+ }
+ getInfoFromNode(redis, callback) {
+ if (!redis) {
+ return callback(new Error("Node is disconnected"));
+ }
+ // Use a duplication of the connection to avoid
+ // timeouts when the connection is in the blocking
+ // mode (e.g. waiting for BLPOP).
+ const duplicatedConnection = redis.duplicate({
+ enableOfflineQueue: true,
+ enableReadyCheck: false,
+ retryStrategy: null,
+ connectionName: (0, util_1.getConnectionName)("refresher", this.options.redisOptions && this.options.redisOptions.connectionName),
+ });
+ // Ignore error events since we will handle
+ // exceptions for the CLUSTER SLOTS command.
+ duplicatedConnection.on("error", utils_1.noop);
+ duplicatedConnection.cluster("SLOTS", (0, utils_1.timeout)((err, result) => {
+ duplicatedConnection.disconnect();
+ if (err) {
+ debug("error encountered running CLUSTER.SLOTS: %s", err);
+ return callback(err);
+ }
+ if (this.status === "disconnecting" ||
+ this.status === "close" ||
+ this.status === "end") {
+ debug("ignore CLUSTER.SLOTS results (count: %d) since cluster status is %s", result.length, this.status);
+ callback();
+ return;
+ }
+ const nodes = [];
+ debug("cluster slots result count: %d", result.length);
+ for (let i = 0; i < result.length; ++i) {
+ const items = result[i];
+ const slotRangeStart = items[0];
+ const slotRangeEnd = items[1];
+ const keys = [];
+ for (let j = 2; j < items.length; j++) {
+ if (!items[j][0]) {
+ continue;
+ }
+ const node = this.natMapper({
+ host: items[j][0],
+ port: items[j][1],
+ });
+ node.readOnly = j !== 2;
+ nodes.push(node);
+ keys.push(node.host + ":" + node.port);
+ }
+ debug("cluster slots result [%d]: slots %d~%d served by %s", i, slotRangeStart, slotRangeEnd, keys);
+ for (let slot = slotRangeStart; slot <= slotRangeEnd; slot++) {
+ this.slots[slot] = keys;
+ }
+ }
+ // Assign to each node keys a numeric value to make autopipeline comparison faster.
+ this._groupsIds = Object.create(null);
+ let j = 0;
+ for (let i = 0; i < 16384; i++) {
+ const target = (this.slots[i] || []).join(";");
+ if (!target.length) {
+ this._groupsBySlot[i] = undefined;
+ continue;
+ }
+ if (!this._groupsIds[target]) {
+ this._groupsIds[target] = ++j;
+ }
+ this._groupsBySlot[i] = this._groupsIds[target];
+ }
+ this.connectionPool.reset(nodes);
+ if (this.options.shardedSubscribers) {
+ this.shardedSubscribers
+ .reset(this.slots, this.connectionPool.getNodes("all"))
+ .catch((err) => {
+ // TODO should we emit an error event here?
+ debug("Error while starting subscribers: %s", err);
+ });
+ }
+ callback();
+ }, this.options.slotsRefreshTimeout));
+ }
+ invokeReadyDelayedCallbacks(err) {
+ for (const c of this._readyDelayedCallbacks) {
+ process.nextTick(c, err);
+ }
+ this._readyDelayedCallbacks = [];
+ }
+ /**
+ * Check whether Cluster is able to process commands
+ */
+ readyCheck(callback) {
+ this.cluster("INFO", (err, res) => {
+ if (err) {
+ return callback(err);
+ }
+ if (typeof res !== "string") {
+ return callback();
+ }
+ let state;
+ const lines = res.split("\r\n");
+ for (let i = 0; i < lines.length; ++i) {
+ const parts = lines[i].split(":");
+ if (parts[0] === "cluster_state") {
+ state = parts[1];
+ break;
+ }
+ }
+ if (state === "fail") {
+ debug("cluster state not ok (%s)", state);
+ callback(null, state);
+ }
+ else {
+ callback();
+ }
+ });
+ }
+ resolveSrv(hostname) {
+ return new Promise((resolve, reject) => {
+ this.options.resolveSrv(hostname, (err, records) => {
+ if (err) {
+ return reject(err);
+ }
+ const self = this, groupedRecords = (0, util_1.groupSrvRecords)(records), sortedKeys = Object.keys(groupedRecords).sort((a, b) => parseInt(a) - parseInt(b));
+ function tryFirstOne(err) {
+ if (!sortedKeys.length) {
+ return reject(err);
+ }
+ const key = sortedKeys[0], group = groupedRecords[key], record = (0, util_1.weightSrvRecords)(group);
+ if (!group.records.length) {
+ sortedKeys.shift();
+ }
+ self.dnsLookup(record.name).then((host) => resolve({
+ host,
+ port: record.port,
+ }), tryFirstOne);
+ }
+ tryFirstOne();
+ });
+ });
+ }
+ dnsLookup(hostname) {
+ return new Promise((resolve, reject) => {
+ this.options.dnsLookup(hostname, (err, address) => {
+ if (err) {
+ debug("failed to resolve hostname %s to IP: %s", hostname, err.message);
+ reject(err);
+ }
+ else {
+ debug("resolved hostname %s to IP %s", hostname, address);
+ resolve(address);
+ }
+ });
+ });
+ }
+ /**
+ * Normalize startup nodes, and resolving hostnames to IPs.
+ *
+ * This process happens every time when #connect() is called since
+ * #startupNodes and DNS records may chanage.
+ */
+ async resolveStartupNodeHostnames() {
+ if (!Array.isArray(this.startupNodes) || this.startupNodes.length === 0) {
+ throw new Error("`startupNodes` should contain at least one node.");
+ }
+ const startupNodes = (0, util_1.normalizeNodeOptions)(this.startupNodes);
+ const hostnames = (0, util_1.getUniqueHostnamesFromOptions)(startupNodes);
+ if (hostnames.length === 0) {
+ return startupNodes;
+ }
+ const configs = await Promise.all(hostnames.map((this.options.useSRVRecords ? this.resolveSrv : this.dnsLookup).bind(this)));
+ const hostnameToConfig = (0, utils_1.zipMap)(hostnames, configs);
+ return startupNodes.map((node) => {
+ const config = hostnameToConfig.get(node.host);
+ if (!config) {
+ return node;
+ }
+ if (this.options.useSRVRecords) {
+ return Object.assign({}, node, config);
+ }
+ return Object.assign({}, node, { host: config });
+ });
+ }
+ createScanStream(command, { key, options = {} }) {
+ return new ScanStream_1.default({
+ objectMode: true,
+ key: key,
+ redis: this,
+ command: command,
+ ...options,
+ });
+ }
+ createShardedSubscriberGroup() {
+ this.subscriberGroupEmitter = new events_1.EventEmitter();
+ this.shardedSubscribers = new ClusterSubscriberGroup_1.default(this.subscriberGroupEmitter, this.options);
+ // Error handler used only for sharded-subscriber-triggered slots cache refreshes.
+ // Normal (non-subscriber) connections are created with lazyConnect: true and can
+ // become zombied. For sharded subscribers, a ClusterAllFailedError means
+ // we have lost all nodes from the subscriber perspective and must tear down.
+ const refreshSlotsCacheCallback = (err) => {
+ // Disconnect only when refreshing the slots cache fails with ClusterAllFailedError
+ if (err instanceof ClusterAllFailedError_1.default) {
+ this.disconnect(true);
+ }
+ };
+ this.subscriberGroupEmitter.on("-node", (redis, nodeKey) => {
+ this.emit("-node", redis, nodeKey);
+ this.refreshSlotsCache(refreshSlotsCacheCallback);
+ });
+ this.subscriberGroupEmitter.on("subscriberConnectFailed", ({ delay, error }) => {
+ this.emit("error", error);
+ setTimeout(() => {
+ this.refreshSlotsCache(refreshSlotsCacheCallback);
+ }, delay);
+ });
+ this.subscriberGroupEmitter.on("moved", () => {
+ this.refreshSlotsCache(refreshSlotsCacheCallback);
+ });
+ this.subscriberGroupEmitter.on("-subscriber", () => {
+ this.emit("-subscriber");
+ });
+ this.subscriberGroupEmitter.on("+subscriber", () => {
+ this.emit("+subscriber");
+ });
+ this.subscriberGroupEmitter.on("nodeError", (error, nodeKey) => {
+ this.emit("nodeError", error, nodeKey);
+ });
+ this.subscriberGroupEmitter.on("subscribersReady", () => {
+ this.emit("subscribersReady");
+ });
+ for (const event of ["smessage", "smessageBuffer"]) {
+ this.subscriberGroupEmitter.on(event, (arg1, arg2, arg3) => {
+ this.emit(event, arg1, arg2, arg3);
+ });
+ }
+ }
+}
+(0, applyMixin_1.default)(Cluster, events_1.EventEmitter);
+(0, transaction_1.addTransactionSupport)(Cluster.prototype);
+exports.default = Cluster;
diff --git a/node_modules/ioredis/built/cluster/util.d.ts b/node_modules/ioredis/built/cluster/util.d.ts
new file mode 100644
index 0000000..d8629c3
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/util.d.ts
@@ -0,0 +1,25 @@
+///
+import { SrvRecord } from "dns";
+export declare type NodeKey = string;
+export declare type NodeRole = "master" | "slave" | "all";
+export interface RedisOptions {
+ port: number;
+ host: string;
+ username?: string | undefined;
+ password?: string | undefined;
+ [key: string]: any;
+}
+export interface SrvRecordsGroup {
+ totalWeight: number;
+ records: SrvRecord[];
+}
+export interface GroupedSrvRecords {
+ [key: number]: SrvRecordsGroup;
+}
+export declare function getNodeKey(node: RedisOptions): NodeKey;
+export declare function nodeKeyToRedisOptions(nodeKey: NodeKey): RedisOptions;
+export declare function normalizeNodeOptions(nodes: Array): RedisOptions[];
+export declare function getUniqueHostnamesFromOptions(nodes: RedisOptions[]): string[];
+export declare function groupSrvRecords(records: SrvRecord[]): GroupedSrvRecords;
+export declare function weightSrvRecords(recordsGroup: SrvRecordsGroup): SrvRecord;
+export declare function getConnectionName(component: any, nodeConnectionName: any): string;
diff --git a/node_modules/ioredis/built/cluster/util.js b/node_modules/ioredis/built/cluster/util.js
new file mode 100644
index 0000000..e3a779b
--- /dev/null
+++ b/node_modules/ioredis/built/cluster/util.js
@@ -0,0 +1,100 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.getConnectionName = exports.weightSrvRecords = exports.groupSrvRecords = exports.getUniqueHostnamesFromOptions = exports.normalizeNodeOptions = exports.nodeKeyToRedisOptions = exports.getNodeKey = void 0;
+const utils_1 = require("../utils");
+const net_1 = require("net");
+function getNodeKey(node) {
+ node.port = node.port || 6379;
+ node.host = node.host || "127.0.0.1";
+ return node.host + ":" + node.port;
+}
+exports.getNodeKey = getNodeKey;
+function nodeKeyToRedisOptions(nodeKey) {
+ const portIndex = nodeKey.lastIndexOf(":");
+ if (portIndex === -1) {
+ throw new Error(`Invalid node key ${nodeKey}`);
+ }
+ return {
+ host: nodeKey.slice(0, portIndex),
+ port: Number(nodeKey.slice(portIndex + 1)),
+ };
+}
+exports.nodeKeyToRedisOptions = nodeKeyToRedisOptions;
+function normalizeNodeOptions(nodes) {
+ return nodes.map((node) => {
+ const options = {};
+ if (typeof node === "object") {
+ Object.assign(options, node);
+ }
+ else if (typeof node === "string") {
+ Object.assign(options, (0, utils_1.parseURL)(node));
+ }
+ else if (typeof node === "number") {
+ options.port = node;
+ }
+ else {
+ throw new Error("Invalid argument " + node);
+ }
+ if (typeof options.port === "string") {
+ options.port = parseInt(options.port, 10);
+ }
+ // Cluster mode only support db 0
+ delete options.db;
+ if (!options.port) {
+ options.port = 6379;
+ }
+ if (!options.host) {
+ options.host = "127.0.0.1";
+ }
+ return (0, utils_1.resolveTLSProfile)(options);
+ });
+}
+exports.normalizeNodeOptions = normalizeNodeOptions;
+function getUniqueHostnamesFromOptions(nodes) {
+ const uniqueHostsMap = {};
+ nodes.forEach((node) => {
+ uniqueHostsMap[node.host] = true;
+ });
+ return Object.keys(uniqueHostsMap).filter((host) => !(0, net_1.isIP)(host));
+}
+exports.getUniqueHostnamesFromOptions = getUniqueHostnamesFromOptions;
+function groupSrvRecords(records) {
+ const recordsByPriority = {};
+ for (const record of records) {
+ if (!recordsByPriority.hasOwnProperty(record.priority)) {
+ recordsByPriority[record.priority] = {
+ totalWeight: record.weight,
+ records: [record],
+ };
+ }
+ else {
+ recordsByPriority[record.priority].totalWeight += record.weight;
+ recordsByPriority[record.priority].records.push(record);
+ }
+ }
+ return recordsByPriority;
+}
+exports.groupSrvRecords = groupSrvRecords;
+function weightSrvRecords(recordsGroup) {
+ if (recordsGroup.records.length === 1) {
+ recordsGroup.totalWeight = 0;
+ return recordsGroup.records.shift();
+ }
+ // + `recordsGroup.records.length` to support `weight` 0
+ const random = Math.floor(Math.random() * (recordsGroup.totalWeight + recordsGroup.records.length));
+ let total = 0;
+ for (const [i, record] of recordsGroup.records.entries()) {
+ total += 1 + record.weight;
+ if (total > random) {
+ recordsGroup.totalWeight -= record.weight;
+ recordsGroup.records.splice(i, 1);
+ return record;
+ }
+ }
+}
+exports.weightSrvRecords = weightSrvRecords;
+function getConnectionName(component, nodeConnectionName) {
+ const prefix = `ioredis-cluster(${component})`;
+ return nodeConnectionName ? `${prefix}:${nodeConnectionName}` : prefix;
+}
+exports.getConnectionName = getConnectionName;
diff --git a/node_modules/ioredis/built/connectors/AbstractConnector.d.ts b/node_modules/ioredis/built/connectors/AbstractConnector.d.ts
new file mode 100644
index 0000000..14bdbaa
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/AbstractConnector.d.ts
@@ -0,0 +1,12 @@
+import { NetStream } from "../types";
+export declare type ErrorEmitter = (type: string, err: Error) => void;
+export default abstract class AbstractConnector {
+ firstError?: Error;
+ protected connecting: boolean;
+ protected stream: NetStream;
+ private disconnectTimeout;
+ constructor(disconnectTimeout: number);
+ check(info: any): boolean;
+ disconnect(): void;
+ abstract connect(_: ErrorEmitter): Promise;
+}
diff --git a/node_modules/ioredis/built/connectors/AbstractConnector.js b/node_modules/ioredis/built/connectors/AbstractConnector.js
new file mode 100644
index 0000000..2e2151d
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/AbstractConnector.js
@@ -0,0 +1,26 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const utils_1 = require("../utils");
+const debug = (0, utils_1.Debug)("AbstractConnector");
+class AbstractConnector {
+ constructor(disconnectTimeout) {
+ this.connecting = false;
+ this.disconnectTimeout = disconnectTimeout;
+ }
+ check(info) {
+ return true;
+ }
+ disconnect() {
+ this.connecting = false;
+ if (this.stream) {
+ const stream = this.stream; // Make sure callbacks refer to the same instance
+ const timeout = setTimeout(() => {
+ debug("stream %s:%s still open, destroying it", stream.remoteAddress, stream.remotePort);
+ stream.destroy();
+ }, this.disconnectTimeout);
+ stream.on("close", () => clearTimeout(timeout));
+ stream.end();
+ }
+ }
+}
+exports.default = AbstractConnector;
diff --git a/node_modules/ioredis/built/connectors/ConnectorConstructor.d.ts b/node_modules/ioredis/built/connectors/ConnectorConstructor.d.ts
new file mode 100644
index 0000000..2f332ae
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/ConnectorConstructor.d.ts
@@ -0,0 +1,5 @@
+import AbstractConnector from "./AbstractConnector";
+interface ConnectorConstructor {
+ new (options: unknown): AbstractConnector;
+}
+export default ConnectorConstructor;
diff --git a/node_modules/ioredis/built/connectors/ConnectorConstructor.js b/node_modules/ioredis/built/connectors/ConnectorConstructor.js
new file mode 100644
index 0000000..c8ad2e5
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/ConnectorConstructor.js
@@ -0,0 +1,2 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
diff --git a/node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.d.ts b/node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.d.ts
new file mode 100644
index 0000000..abd44e5
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.d.ts
@@ -0,0 +1,11 @@
+import SentinelConnector from "./index";
+import { Sentinel } from "./types";
+export declare class FailoverDetector {
+ private connector;
+ private sentinels;
+ private isDisconnected;
+ constructor(connector: SentinelConnector, sentinels: Sentinel[]);
+ cleanup(): void;
+ subscribe(): Promise;
+ private disconnect;
+}
diff --git a/node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.js b/node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.js
new file mode 100644
index 0000000..8db28d0
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.js
@@ -0,0 +1,45 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.FailoverDetector = void 0;
+const utils_1 = require("../../utils");
+const debug = (0, utils_1.Debug)("FailoverDetector");
+const CHANNEL_NAME = "+switch-master";
+class FailoverDetector {
+ // sentinels can't be used for regular commands after this
+ constructor(connector, sentinels) {
+ this.isDisconnected = false;
+ this.connector = connector;
+ this.sentinels = sentinels;
+ }
+ cleanup() {
+ this.isDisconnected = true;
+ for (const sentinel of this.sentinels) {
+ sentinel.client.disconnect();
+ }
+ }
+ async subscribe() {
+ debug("Starting FailoverDetector");
+ const promises = [];
+ for (const sentinel of this.sentinels) {
+ const promise = sentinel.client.subscribe(CHANNEL_NAME).catch((err) => {
+ debug("Failed to subscribe to failover messages on sentinel %s:%s (%s)", sentinel.address.host || "127.0.0.1", sentinel.address.port || 26739, err.message);
+ });
+ promises.push(promise);
+ sentinel.client.on("message", (channel) => {
+ if (!this.isDisconnected && channel === CHANNEL_NAME) {
+ this.disconnect();
+ }
+ });
+ }
+ await Promise.all(promises);
+ }
+ disconnect() {
+ // Avoid disconnecting more than once per failover.
+ // A new FailoverDetector will be created after reconnecting.
+ this.isDisconnected = true;
+ debug("Failover detected, disconnecting");
+ // Will call this.cleanup()
+ this.connector.disconnect();
+ }
+}
+exports.FailoverDetector = FailoverDetector;
diff --git a/node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.d.ts b/node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.d.ts
new file mode 100644
index 0000000..fbe5f34
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.d.ts
@@ -0,0 +1,13 @@
+import { SentinelAddress } from "./types";
+export default class SentinelIterator implements Iterator> {
+ private cursor;
+ private sentinels;
+ constructor(sentinels: Array>);
+ next(): {
+ done: boolean;
+ value: Partial;
+ };
+ reset(moveCurrentEndpointToFirst: boolean): void;
+ add(sentinel: SentinelAddress): boolean;
+ toString(): string;
+}
diff --git a/node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.js b/node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.js
new file mode 100644
index 0000000..3399bae
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.js
@@ -0,0 +1,37 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+function isSentinelEql(a, b) {
+ return ((a.host || "127.0.0.1") === (b.host || "127.0.0.1") &&
+ (a.port || 26379) === (b.port || 26379));
+}
+class SentinelIterator {
+ constructor(sentinels) {
+ this.cursor = 0;
+ this.sentinels = sentinels.slice(0);
+ }
+ next() {
+ const done = this.cursor >= this.sentinels.length;
+ return { done, value: done ? undefined : this.sentinels[this.cursor++] };
+ }
+ reset(moveCurrentEndpointToFirst) {
+ if (moveCurrentEndpointToFirst &&
+ this.sentinels.length > 1 &&
+ this.cursor !== 1) {
+ this.sentinels.unshift(...this.sentinels.splice(this.cursor - 1));
+ }
+ this.cursor = 0;
+ }
+ add(sentinel) {
+ for (let i = 0; i < this.sentinels.length; i++) {
+ if (isSentinelEql(sentinel, this.sentinels[i])) {
+ return false;
+ }
+ }
+ this.sentinels.push(sentinel);
+ return true;
+ }
+ toString() {
+ return `${JSON.stringify(this.sentinels)} @${this.cursor}`;
+ }
+}
+exports.default = SentinelIterator;
diff --git a/node_modules/ioredis/built/connectors/SentinelConnector/index.d.ts b/node_modules/ioredis/built/connectors/SentinelConnector/index.d.ts
new file mode 100644
index 0000000..66660e5
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/SentinelConnector/index.d.ts
@@ -0,0 +1,72 @@
+///
+import { EventEmitter } from "events";
+import { NatMap } from "../../cluster/ClusterOptions";
+import { ConnectionOptions } from "tls";
+import SentinelIterator from "./SentinelIterator";
+import { SentinelAddress } from "./types";
+import AbstractConnector, { ErrorEmitter } from "../AbstractConnector";
+import { NetStream } from "../../types";
+interface AddressFromResponse {
+ port: string;
+ ip: string;
+ flags?: string | undefined;
+}
+declare type PreferredSlaves = ((slaves: AddressFromResponse[]) => AddressFromResponse | null) | Array<{
+ port: string;
+ ip: string;
+ prio?: number | undefined;
+}> | {
+ port: string;
+ ip: string;
+ prio?: number | undefined;
+};
+export { SentinelAddress, SentinelIterator };
+export interface SentinelConnectionOptions {
+ /**
+ * Master group name of the Sentinel
+ */
+ name?: string | undefined;
+ /**
+ * @default "master"
+ */
+ role?: "master" | "slave" | undefined;
+ tls?: ConnectionOptions | undefined;
+ sentinelUsername?: string | undefined;
+ sentinelPassword?: string | undefined;
+ sentinels?: Array> | undefined;
+ sentinelRetryStrategy?: ((retryAttempts: number) => number | void | null) | undefined;
+ sentinelReconnectStrategy?: ((retryAttempts: number) => number | void | null) | undefined;
+ preferredSlaves?: PreferredSlaves | undefined;
+ connectTimeout?: number | undefined;
+ disconnectTimeout?: number | undefined;
+ sentinelCommandTimeout?: number | undefined;
+ enableTLSForSentinelMode?: boolean | undefined;
+ sentinelTLS?: ConnectionOptions | undefined;
+ natMap?: NatMap | undefined;
+ updateSentinels?: boolean | undefined;
+ /**
+ * @default 10
+ */
+ sentinelMaxConnections?: number | undefined;
+ failoverDetector?: boolean | undefined;
+}
+export default class SentinelConnector extends AbstractConnector {
+ protected options: SentinelConnectionOptions;
+ emitter: EventEmitter | null;
+ protected sentinelIterator: SentinelIterator;
+ private retryAttempts;
+ private failoverDetector;
+ constructor(options: SentinelConnectionOptions);
+ check(info: {
+ role?: string;
+ }): boolean;
+ disconnect(): void;
+ connect(eventEmitter: ErrorEmitter): Promise;
+ private updateSentinels;
+ private resolveMaster;
+ private resolveSlave;
+ private sentinelNatResolve;
+ private connectToSentinel;
+ private resolve;
+ private initFailoverDetector;
+}
diff --git a/node_modules/ioredis/built/connectors/SentinelConnector/index.js b/node_modules/ioredis/built/connectors/SentinelConnector/index.js
new file mode 100644
index 0000000..5cb4b48
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/SentinelConnector/index.js
@@ -0,0 +1,305 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.SentinelIterator = void 0;
+const net_1 = require("net");
+const utils_1 = require("../../utils");
+const tls_1 = require("tls");
+const SentinelIterator_1 = require("./SentinelIterator");
+exports.SentinelIterator = SentinelIterator_1.default;
+const AbstractConnector_1 = require("../AbstractConnector");
+const Redis_1 = require("../../Redis");
+const FailoverDetector_1 = require("./FailoverDetector");
+const debug = (0, utils_1.Debug)("SentinelConnector");
+class SentinelConnector extends AbstractConnector_1.default {
+ constructor(options) {
+ super(options.disconnectTimeout);
+ this.options = options;
+ this.emitter = null;
+ this.failoverDetector = null;
+ if (!this.options.sentinels.length) {
+ throw new Error("Requires at least one sentinel to connect to.");
+ }
+ if (!this.options.name) {
+ throw new Error("Requires the name of master.");
+ }
+ this.sentinelIterator = new SentinelIterator_1.default(this.options.sentinels);
+ }
+ check(info) {
+ const roleMatches = !info.role || this.options.role === info.role;
+ if (!roleMatches) {
+ debug("role invalid, expected %s, but got %s", this.options.role, info.role);
+ // Start from the next item.
+ // Note that `reset` will move the cursor to the previous element,
+ // so we advance two steps here.
+ this.sentinelIterator.next();
+ this.sentinelIterator.next();
+ this.sentinelIterator.reset(true);
+ }
+ return roleMatches;
+ }
+ disconnect() {
+ super.disconnect();
+ if (this.failoverDetector) {
+ this.failoverDetector.cleanup();
+ }
+ }
+ connect(eventEmitter) {
+ this.connecting = true;
+ this.retryAttempts = 0;
+ let lastError;
+ const connectToNext = async () => {
+ const endpoint = this.sentinelIterator.next();
+ if (endpoint.done) {
+ this.sentinelIterator.reset(false);
+ const retryDelay = typeof this.options.sentinelRetryStrategy === "function"
+ ? this.options.sentinelRetryStrategy(++this.retryAttempts)
+ : null;
+ let errorMsg = typeof retryDelay !== "number"
+ ? "All sentinels are unreachable and retry is disabled."
+ : `All sentinels are unreachable. Retrying from scratch after ${retryDelay}ms.`;
+ if (lastError) {
+ errorMsg += ` Last error: ${lastError.message}`;
+ }
+ debug(errorMsg);
+ const error = new Error(errorMsg);
+ if (typeof retryDelay === "number") {
+ eventEmitter("error", error);
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
+ return connectToNext();
+ }
+ else {
+ throw error;
+ }
+ }
+ let resolved = null;
+ let err = null;
+ try {
+ resolved = await this.resolve(endpoint.value);
+ }
+ catch (error) {
+ err = error;
+ }
+ if (!this.connecting) {
+ throw new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG);
+ }
+ const endpointAddress = endpoint.value.host + ":" + endpoint.value.port;
+ if (resolved) {
+ debug("resolved: %s:%s from sentinel %s", resolved.host, resolved.port, endpointAddress);
+ if (this.options.enableTLSForSentinelMode && this.options.tls) {
+ Object.assign(resolved, this.options.tls);
+ this.stream = (0, tls_1.connect)(resolved);
+ this.stream.once("secureConnect", this.initFailoverDetector.bind(this));
+ }
+ else {
+ this.stream = (0, net_1.createConnection)(resolved);
+ this.stream.once("connect", this.initFailoverDetector.bind(this));
+ }
+ this.stream.once("error", (err) => {
+ this.firstError = err;
+ });
+ return this.stream;
+ }
+ else {
+ const errorMsg = err
+ ? "failed to connect to sentinel " +
+ endpointAddress +
+ " because " +
+ err.message
+ : "connected to sentinel " +
+ endpointAddress +
+ " successfully, but got an invalid reply: " +
+ resolved;
+ debug(errorMsg);
+ eventEmitter("sentinelError", new Error(errorMsg));
+ if (err) {
+ lastError = err;
+ }
+ return connectToNext();
+ }
+ };
+ return connectToNext();
+ }
+ async updateSentinels(client) {
+ if (!this.options.updateSentinels) {
+ return;
+ }
+ const result = await client.sentinel("sentinels", this.options.name);
+ if (!Array.isArray(result)) {
+ return;
+ }
+ result
+ .map(utils_1.packObject)
+ .forEach((sentinel) => {
+ const flags = sentinel.flags ? sentinel.flags.split(",") : [];
+ if (flags.indexOf("disconnected") === -1 &&
+ sentinel.ip &&
+ sentinel.port) {
+ const endpoint = this.sentinelNatResolve(addressResponseToAddress(sentinel));
+ if (this.sentinelIterator.add(endpoint)) {
+ debug("adding sentinel %s:%s", endpoint.host, endpoint.port);
+ }
+ }
+ });
+ debug("Updated internal sentinels: %s", this.sentinelIterator);
+ }
+ async resolveMaster(client) {
+ const result = await client.sentinel("get-master-addr-by-name", this.options.name);
+ await this.updateSentinels(client);
+ return this.sentinelNatResolve(Array.isArray(result)
+ ? { host: result[0], port: Number(result[1]) }
+ : null);
+ }
+ async resolveSlave(client) {
+ const result = await client.sentinel("slaves", this.options.name);
+ if (!Array.isArray(result)) {
+ return null;
+ }
+ const availableSlaves = result
+ .map(utils_1.packObject)
+ .filter((slave) => slave.flags && !slave.flags.match(/(disconnected|s_down|o_down)/));
+ return this.sentinelNatResolve(selectPreferredSentinel(availableSlaves, this.options.preferredSlaves));
+ }
+ sentinelNatResolve(item) {
+ if (!item || !this.options.natMap)
+ return item;
+ const key = `${item.host}:${item.port}`;
+ let result = item;
+ if (typeof this.options.natMap === "function") {
+ result = this.options.natMap(key) || item;
+ }
+ else if (typeof this.options.natMap === "object") {
+ result = this.options.natMap[key] || item;
+ }
+ return result;
+ }
+ connectToSentinel(endpoint, options) {
+ const redis = new Redis_1.default({
+ port: endpoint.port || 26379,
+ host: endpoint.host,
+ username: this.options.sentinelUsername || null,
+ password: this.options.sentinelPassword || null,
+ family: endpoint.family ||
+ // @ts-expect-error
+ ("path" in this.options && this.options.path
+ ? undefined
+ : // @ts-expect-error
+ this.options.family),
+ tls: this.options.sentinelTLS,
+ retryStrategy: null,
+ enableReadyCheck: false,
+ connectTimeout: this.options.connectTimeout,
+ commandTimeout: this.options.sentinelCommandTimeout,
+ ...options,
+ });
+ // @ts-expect-error
+ return redis;
+ }
+ async resolve(endpoint) {
+ const client = this.connectToSentinel(endpoint);
+ // ignore the errors since resolve* methods will handle them
+ client.on("error", noop);
+ try {
+ if (this.options.role === "slave") {
+ return await this.resolveSlave(client);
+ }
+ else {
+ return await this.resolveMaster(client);
+ }
+ }
+ finally {
+ client.disconnect();
+ }
+ }
+ async initFailoverDetector() {
+ var _a;
+ if (!this.options.failoverDetector) {
+ return;
+ }
+ // Move the current sentinel to the first position
+ this.sentinelIterator.reset(true);
+ const sentinels = [];
+ // In case of a large amount of sentinels, limit the number of concurrent connections
+ while (sentinels.length < this.options.sentinelMaxConnections) {
+ const { done, value } = this.sentinelIterator.next();
+ if (done) {
+ break;
+ }
+ const client = this.connectToSentinel(value, {
+ lazyConnect: true,
+ retryStrategy: this.options.sentinelReconnectStrategy,
+ });
+ client.on("reconnecting", () => {
+ var _a;
+ // Tests listen to this event
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit("sentinelReconnecting");
+ });
+ sentinels.push({ address: value, client });
+ }
+ this.sentinelIterator.reset(false);
+ if (this.failoverDetector) {
+ // Clean up previous detector
+ this.failoverDetector.cleanup();
+ }
+ this.failoverDetector = new FailoverDetector_1.FailoverDetector(this, sentinels);
+ await this.failoverDetector.subscribe();
+ // Tests listen to this event
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit("failoverSubscribed");
+ }
+}
+exports.default = SentinelConnector;
+function selectPreferredSentinel(availableSlaves, preferredSlaves) {
+ if (availableSlaves.length === 0) {
+ return null;
+ }
+ let selectedSlave;
+ if (typeof preferredSlaves === "function") {
+ selectedSlave = preferredSlaves(availableSlaves);
+ }
+ else if (preferredSlaves !== null && typeof preferredSlaves === "object") {
+ const preferredSlavesArray = Array.isArray(preferredSlaves)
+ ? preferredSlaves
+ : [preferredSlaves];
+ // sort by priority
+ preferredSlavesArray.sort((a, b) => {
+ // default the priority to 1
+ if (!a.prio) {
+ a.prio = 1;
+ }
+ if (!b.prio) {
+ b.prio = 1;
+ }
+ // lowest priority first
+ if (a.prio < b.prio) {
+ return -1;
+ }
+ if (a.prio > b.prio) {
+ return 1;
+ }
+ return 0;
+ });
+ // loop over preferred slaves and return the first match
+ for (let p = 0; p < preferredSlavesArray.length; p++) {
+ for (let a = 0; a < availableSlaves.length; a++) {
+ const slave = availableSlaves[a];
+ if (slave.ip === preferredSlavesArray[p].ip) {
+ if (slave.port === preferredSlavesArray[p].port) {
+ selectedSlave = slave;
+ break;
+ }
+ }
+ }
+ if (selectedSlave) {
+ break;
+ }
+ }
+ }
+ // if none of the preferred slaves are available, a random available slave is returned
+ if (!selectedSlave) {
+ selectedSlave = (0, utils_1.sample)(availableSlaves);
+ }
+ return addressResponseToAddress(selectedSlave);
+}
+function addressResponseToAddress(input) {
+ return { host: input.ip, port: Number(input.port) };
+}
+function noop() { }
diff --git a/node_modules/ioredis/built/connectors/SentinelConnector/types.d.ts b/node_modules/ioredis/built/connectors/SentinelConnector/types.d.ts
new file mode 100644
index 0000000..4734cdc
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/SentinelConnector/types.d.ts
@@ -0,0 +1,21 @@
+import { RedisOptions } from "../../redis/RedisOptions";
+export interface SentinelAddress {
+ port: number;
+ host: string;
+ family?: number;
+}
+export interface RedisClient {
+ options: RedisOptions;
+ sentinel(subcommand: "sentinels", name: string): Promise;
+ sentinel(subcommand: "get-master-addr-by-name", name: string): Promise;
+ sentinel(subcommand: "slaves", name: string): Promise;
+ subscribe(...channelNames: string[]): Promise;
+ on(event: "message", callback: (channel: string, message: string) => void): void;
+ on(event: "error", callback: (error: Error) => void): void;
+ on(event: "reconnecting", callback: () => void): void;
+ disconnect(): void;
+}
+export interface Sentinel {
+ address: Partial;
+ client: RedisClient;
+}
diff --git a/node_modules/ioredis/built/connectors/SentinelConnector/types.js b/node_modules/ioredis/built/connectors/SentinelConnector/types.js
new file mode 100644
index 0000000..c8ad2e5
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/SentinelConnector/types.js
@@ -0,0 +1,2 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
diff --git a/node_modules/ioredis/built/connectors/StandaloneConnector.d.ts b/node_modules/ioredis/built/connectors/StandaloneConnector.d.ts
new file mode 100644
index 0000000..45fb552
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/StandaloneConnector.d.ts
@@ -0,0 +1,17 @@
+///
+import { IpcNetConnectOpts, TcpNetConnectOpts } from "net";
+import { ConnectionOptions } from "tls";
+import { NetStream } from "../types";
+import AbstractConnector, { ErrorEmitter } from "./AbstractConnector";
+declare type TcpOptions = Pick;
+declare type IpcOptions = Pick;
+export declare type StandaloneConnectionOptions = Partial & {
+ disconnectTimeout?: number | undefined;
+ tls?: ConnectionOptions | undefined;
+};
+export default class StandaloneConnector extends AbstractConnector {
+ protected options: StandaloneConnectionOptions;
+ constructor(options: StandaloneConnectionOptions);
+ connect(_: ErrorEmitter): Promise;
+}
+export {};
diff --git a/node_modules/ioredis/built/connectors/StandaloneConnector.js b/node_modules/ioredis/built/connectors/StandaloneConnector.js
new file mode 100644
index 0000000..d17c91a
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/StandaloneConnector.js
@@ -0,0 +1,69 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const net_1 = require("net");
+const tls_1 = require("tls");
+const utils_1 = require("../utils");
+const AbstractConnector_1 = require("./AbstractConnector");
+class StandaloneConnector extends AbstractConnector_1.default {
+ constructor(options) {
+ super(options.disconnectTimeout);
+ this.options = options;
+ }
+ connect(_) {
+ const { options } = this;
+ this.connecting = true;
+ let connectionOptions;
+ if ("path" in options && options.path) {
+ connectionOptions = {
+ path: options.path,
+ };
+ }
+ else {
+ connectionOptions = {};
+ if ("port" in options && options.port != null) {
+ connectionOptions.port = options.port;
+ }
+ if ("host" in options && options.host != null) {
+ connectionOptions.host = options.host;
+ }
+ if ("family" in options && options.family != null) {
+ connectionOptions.family = options.family;
+ }
+ }
+ if (options.tls) {
+ Object.assign(connectionOptions, options.tls);
+ }
+ // TODO:
+ // We use native Promise here since other Promise
+ // implementation may use different schedulers that
+ // cause issue when the stream is resolved in the
+ // next tick.
+ // Should use the provided promise in the next major
+ // version and do not connect before resolved.
+ return new Promise((resolve, reject) => {
+ process.nextTick(() => {
+ if (!this.connecting) {
+ reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
+ return;
+ }
+ try {
+ if (options.tls) {
+ this.stream = (0, tls_1.connect)(connectionOptions);
+ }
+ else {
+ this.stream = (0, net_1.createConnection)(connectionOptions);
+ }
+ }
+ catch (err) {
+ reject(err);
+ return;
+ }
+ this.stream.once("error", (err) => {
+ this.firstError = err;
+ });
+ resolve(this.stream);
+ });
+ });
+ }
+}
+exports.default = StandaloneConnector;
diff --git a/node_modules/ioredis/built/connectors/index.d.ts b/node_modules/ioredis/built/connectors/index.d.ts
new file mode 100644
index 0000000..fa74d6f
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/index.d.ts
@@ -0,0 +1,3 @@
+import StandaloneConnector from "./StandaloneConnector";
+import SentinelConnector from "./SentinelConnector";
+export { StandaloneConnector, SentinelConnector };
diff --git a/node_modules/ioredis/built/connectors/index.js b/node_modules/ioredis/built/connectors/index.js
new file mode 100644
index 0000000..49a03a2
--- /dev/null
+++ b/node_modules/ioredis/built/connectors/index.js
@@ -0,0 +1,7 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.SentinelConnector = exports.StandaloneConnector = void 0;
+const StandaloneConnector_1 = require("./StandaloneConnector");
+exports.StandaloneConnector = StandaloneConnector_1.default;
+const SentinelConnector_1 = require("./SentinelConnector");
+exports.SentinelConnector = SentinelConnector_1.default;
diff --git a/node_modules/ioredis/built/constants/TLSProfiles.d.ts b/node_modules/ioredis/built/constants/TLSProfiles.d.ts
new file mode 100644
index 0000000..7547966
--- /dev/null
+++ b/node_modules/ioredis/built/constants/TLSProfiles.d.ts
@@ -0,0 +1,9 @@
+declare const TLSProfiles: {
+ readonly RedisCloudFixed: {
+ readonly ca: "-----BEGIN CERTIFICATE-----\nMIIDTzCCAjegAwIBAgIJAKSVpiDswLcwMA0GCSqGSIb3DQEBBQUAMD4xFjAUBgNV\nBAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1\ndGhvcml0eTAeFw0xMzEwMDExMjE0NTVaFw0yMzA5MjkxMjE0NTVaMD4xFjAUBgNV\nBAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1\ndGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALZqkh/DczWP\nJnxnHLQ7QL0T4B4CDKWBKCcisriGbA6ZePWVNo4hfKQC6JrzfR+081NeD6VcWUiz\nrmd+jtPhIY4c+WVQYm5PKaN6DT1imYdxQw7aqO5j2KUCEh/cznpLxeSHoTxlR34E\nQwF28Wl3eg2vc5ct8LjU3eozWVk3gb7alx9mSA2SgmuX5lEQawl++rSjsBStemY2\nBDwOpAMXIrdEyP/cVn8mkvi/BDs5M5G+09j0gfhyCzRWMQ7Hn71u1eolRxwVxgi3\nTMn+/vTaFSqxKjgck6zuAYjBRPaHe7qLxHNr1So/Mc9nPy+3wHebFwbIcnUojwbp\n4nctkWbjb2cCAwEAAaNQME4wHQYDVR0OBBYEFP1whtcrydmW3ZJeuSoKZIKjze3w\nMB8GA1UdIwQYMBaAFP1whtcrydmW3ZJeuSoKZIKjze3wMAwGA1UdEwQFMAMBAf8w\nDQYJKoZIhvcNAQEFBQADggEBAG2erXhwRAa7+ZOBs0B6X57Hwyd1R4kfmXcs0rta\nlbPpvgULSiB+TCbf3EbhJnHGyvdCY1tvlffLjdA7HJ0PCOn+YYLBA0pTU/dyvrN6\nSu8NuS5yubnt9mb13nDGYo1rnt0YRfxN+8DM3fXIVr038A30UlPX2Ou1ExFJT0MZ\nuFKY6ZvLdI6/1cbgmguMlAhM+DhKyV6Sr5699LM3zqeI816pZmlREETYkGr91q7k\nBpXJu/dtHaGxg1ZGu6w/PCsYGUcECWENYD4VQPd8N32JjOfu6vEgoEAwfPP+3oGp\nZ4m3ewACcWOAenqflb+cQYC4PsF7qbXDmRaWrbKntOlZ3n0=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIGMTCCBBmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwajELMAkGA1UEBhMCVVMx\nCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJDQTESMBAGA1UECgwJUmVkaXNMYWJzMS0w\nKwYDVQQDDCRSZWRpc0xhYnMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN\nMTgwMjI1MTUzNzM3WhcNMjgwMjIzMTUzNzM3WjBfMQswCQYDVQQGEwJVUzELMAkG\nA1UECAwCQ0ExEjAQBgNVBAoMCVJlZGlzTGFiczEvMC0GA1UEAwwmUkNQIEludGVy\nbWVkaWF0ZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA\nA4ICDwAwggIKAoICAQDf9dqbxc8Bq7Ctq9rWcxrGNKKHivqLAFpPq02yLPx6fsOv\nTq7GsDChAYBBc4v7Y2Ap9RD5Vs3dIhEANcnolf27QwrG9RMnnvzk8pCvp1o6zSU4\nVuOE1W66/O1/7e2rVxyrnTcP7UgK43zNIXu7+tiAqWsO92uSnuMoGPGpeaUm1jym\nhjWKtkAwDFSqvHY+XL5qDVBEjeUe+WHkYUg40cAXjusAqgm2hZt29c2wnVrxW25W\nP0meNlzHGFdA2AC5z54iRiqj57dTfBTkHoBczQxcyw6hhzxZQ4e5I5zOKjXXEhZN\nr0tA3YC14CTabKRus/JmZieyZzRgEy2oti64tmLYTqSlAD78pRL40VNoaSYetXLw\nhhNsXCHgWaY6d5bLOc/aIQMAV5oLvZQKvuXAF1IDmhPA+bZbpWipp0zagf1P1H3s\nUzsMdn2KM0ejzgotbtNlj5TcrVwpmvE3ktvUAuA+hi3FkVx1US+2Gsp5x4YOzJ7u\nP1WPk6ShF0JgnJH2ILdj6kttTWwFzH17keSFICWDfH/+kM+k7Y1v3EXMQXE7y0T9\nMjvJskz6d/nv+sQhY04xt64xFMGTnZjlJMzfQNi7zWFLTZnDD0lPowq7l3YiPoTT\nt5Xky83lu0KZsZBo0WlWaDG00gLVdtRgVbcuSWxpi5BdLb1kRab66JptWjxwXQID\nAQABo4HrMIHoMDoGA1UdHwQzMDEwL6AtoCuGKWh0dHBzOi8vcmwtY2Etc2VydmVy\nLnJlZGlzbGFicy5jb20vdjEvY3JsMEYGCCsGAQUFBwEBBDowODA2BggrBgEFBQcw\nAYYqaHR0cHM6Ly9ybC1jYS1zZXJ2ZXIucmVkaXNsYWJzLmNvbS92MS9vY3NwMB0G\nA1UdDgQWBBQHar5OKvQUpP2qWt6mckzToeCOHDAfBgNVHSMEGDAWgBQi42wH6hM4\nL2sujEvLM0/u8lRXTzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIB\nhjANBgkqhkiG9w0BAQsFAAOCAgEAirEn/iTsAKyhd+pu2W3Z5NjCko4NPU0EYUbr\nAP7+POK2rzjIrJO3nFYQ/LLuC7KCXG+2qwan2SAOGmqWst13Y+WHp44Kae0kaChW\nvcYLXXSoGQGC8QuFSNUdaeg3RbMDYFT04dOkqufeWVccoHVxyTSg9eD8LZuHn5jw\n7QDLiEECBmIJHk5Eeo2TAZrx4Yx6ufSUX5HeVjlAzqwtAqdt99uCJ/EL8bgpWbe+\nXoSpvUv0SEC1I1dCAhCKAvRlIOA6VBcmzg5Am12KzkqTul12/VEFIgzqu0Zy2Jbc\nAUPrYVu/+tOGXQaijy7YgwH8P8n3s7ZeUa1VABJHcxrxYduDDJBLZi+MjheUDaZ1\njQRHYevI2tlqeSBqdPKG4zBY5lS0GiAlmuze5oENt0P3XboHoZPHiqcK3VECgTVh\n/BkJcuudETSJcZDmQ8YfoKfBzRQNg2sv/hwvUv73Ss51Sco8GEt2lD8uEdib1Q6z\nzDT5lXJowSzOD5ZA9OGDjnSRL+2riNtKWKEqvtEG3VBJoBzu9GoxbAc7wIZLxmli\niF5a/Zf5X+UXD3s4TMmy6C4QZJpAA2egsSQCnraWO2ULhh7iXMysSkF/nzVfZn43\niqpaB8++9a37hWq14ZmOv0TJIDz//b2+KC4VFXWQ5W5QC6whsjT+OlG4p5ZYG0jo\n616pxqo=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFujCCA6KgAwIBAgIJAJ1aTT1lu2ScMA0GCSqGSIb3DQEBCwUAMGoxCzAJBgNV\nBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCQ0ExEjAQBgNVBAoMCVJlZGlz\nTGFiczEtMCsGA1UEAwwkUmVkaXNMYWJzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9y\naXR5MB4XDTE4MDIyNTE1MjA0MloXDTM4MDIyMDE1MjA0MlowajELMAkGA1UEBhMC\nVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJDQTESMBAGA1UECgwJUmVkaXNMYWJz\nMS0wKwYDVQQDDCRSZWRpc0xhYnMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw\nggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLEjXy7YrbN5Waau5cd6g1\nG5C2tMmeTpZ0duFAPxNU4oE3RHS5gGiok346fUXuUxbZ6QkuzeN2/2Z+RmRcJhQY\nDm0ZgdG4x59An1TJfnzKKoWj8ISmoHS/TGNBdFzXV7FYNLBuqZouqePI6ReC6Qhl\npp45huV32Q3a6IDrrvx7Wo5ZczEQeFNbCeCOQYNDdTmCyEkHqc2AGo8eoIlSTutT\nULOC7R5gzJVTS0e1hesQ7jmqHjbO+VQS1NAL4/5K6cuTEqUl+XhVhPdLWBXJQ5ag\n54qhX4v+ojLzeU1R/Vc6NjMvVtptWY6JihpgplprN0Yh2556ewcXMeturcKgXfGJ\nxeYzsjzXerEjrVocX5V8BNrg64NlifzTMKNOOv4fVZszq1SIHR8F9ROrqiOdh8iC\nJpUbLpXH9hWCSEO6VRMB2xJoKu3cgl63kF30s77x7wLFMEHiwsQRKxooE1UhgS9K\n2sO4TlQ1eWUvFvHSTVDQDlGQ6zu4qjbOpb3Q8bQwoK+ai2alkXVR4Ltxe9QlgYK3\nStsnPhruzZGA0wbXdpw0bnM+YdlEm5ffSTpNIfgHeaa7Dtb801FtA71ZlH7A6TaI\nSIQuUST9EKmv7xrJyx0W1pGoPOLw5T029aTjnICSLdtV9bLwysrLhIYG5bnPq78B\ncS+jZHFGzD7PUVGQD01nOQIDAQABo2MwYTAdBgNVHQ4EFgQUIuNsB+oTOC9rLoxL\nyzNP7vJUV08wHwYDVR0jBBgwFoAUIuNsB+oTOC9rLoxLyzNP7vJUV08wDwYDVR0T\nAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAHfg\nz5pMNUAKdMzK1aS1EDdK9yKz4qicILz5czSLj1mC7HKDRy8cVADUxEICis++CsCu\nrYOvyCVergHQLREcxPq4rc5Nq1uj6J6649NEeh4WazOOjL4ZfQ1jVznMbGy+fJm3\n3Hoelv6jWRG9iqeJZja7/1s6YC6bWymI/OY1e4wUKeNHAo+Vger7MlHV+RuabaX+\nhSJ8bJAM59NCM7AgMTQpJCncrcdLeceYniGy5Q/qt2b5mJkQVkIdy4TPGGB+AXDJ\nD0q3I/JDRkDUFNFdeW0js7fHdsvCR7O3tJy5zIgEV/o/BCkmJVtuwPYOrw/yOlKj\nTY/U7ATAx9VFF6/vYEOMYSmrZlFX+98L6nJtwDqfLB5VTltqZ4H/KBxGE3IRSt9l\nFXy40U+LnXzhhW+7VBAvyYX8GEXhHkKU8Gqk1xitrqfBXY74xKgyUSTolFSfFVgj\nmcM/X4K45bka+qpkj7Kfv/8D4j6aZekwhN2ly6hhC1SmQ8qjMjpG/mrWOSSHZFmf\nybu9iD2AYHeIOkshIl6xYIa++Q/00/vs46IzAbQyriOi0XxlSMMVtPx0Q3isp+ji\nn8Mq9eOuxYOEQ4of8twUkUDd528iwGtEdwf0Q01UyT84S62N8AySl1ZBKXJz6W4F\nUhWfa/HQYOAPDdEjNgnVwLI23b8t0TozyCWw7q8h\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIIEjzCCA3egAwIBAgIQe55B/ALCKJDZtdNT8kD6hTANBgkqhkiG9w0BAQsFADBM\nMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv\nYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjAxMjYxMjAwMDBaFw0y\nNTAxMjYwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu\nIG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIE9WIFRMUyBDQSAy\nMDIyIFEyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmGmg1LW9b7Lf\n8zDD83yBDTEkt+FOxKJZqF4veWc5KZsQj9HfnUS2e5nj/E+JImlGPsQuoiosLuXD\nBVBNAMcUFa11buFMGMeEMwiTmCXoXRrXQmH0qjpOfKgYc5gHG3BsRGaRrf7VR4eg\nofNMG9wUBw4/g/TT7+bQJdA4NfE7Y4d5gEryZiBGB/swaX6Jp/8MF4TgUmOWmalK\ndZCKyb4sPGQFRTtElk67F7vU+wdGcrcOx1tDcIB0ncjLPMnaFicagl+daWGsKqTh\ncounQb6QJtYHa91KvCfKWocMxQ7OIbB5UARLPmC4CJ1/f8YFm35ebfzAeULYdGXu\njE9CLor0OwIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG\nCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW\nBBSH5Zq7a7B/t95GfJWkDBpA8HHqdjAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj\nmove4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw\nMi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1\ncmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w\nK6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD\nVR0gBBowGDAIBgZngQwBAgIwDAYKKwYBBAGgMgoBAjANBgkqhkiG9w0BAQsFAAOC\nAQEAKRic9/f+nmhQU/wz04APZLjgG5OgsuUOyUEZjKVhNGDwxGTvKhyXGGAMW2B/\n3bRi+aElpXwoxu3pL6fkElbX3B0BeS5LoDtxkyiVEBMZ8m+sXbocwlPyxrPbX6mY\n0rVIvnuUeBH8X0L5IwfpNVvKnBIilTbcebfHyXkPezGwz7E1yhUULjJFm2bt0SdX\ny+4X/WeiiYIv+fTVgZZgl+/2MKIsu/qdBJc3f3TvJ8nz+Eax1zgZmww+RSQWeOj3\n15Iw6Z5FX+NwzY/Ab+9PosR5UosSeq+9HhtaxZttXG1nVh+avYPGYddWmiMT90J5\nZgKnO/Fx2hBgTxhOTMYaD312kg==\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G\nA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp\nZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4\nMTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG\nA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8\nRgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT\ngHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm\nKPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\nQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ\nXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw\nDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o\nLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU\nRUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp\njjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK\n6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX\nmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs\nMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\nWD9f\n-----END CERTIFICATE-----";
+ };
+ readonly RedisCloudFlexible: {
+ readonly ca: "-----BEGIN CERTIFICATE-----\nMIIDTzCCAjegAwIBAgIJAKSVpiDswLcwMA0GCSqGSIb3DQEBBQUAMD4xFjAUBgNV\nBAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1\ndGhvcml0eTAeFw0xMzEwMDExMjE0NTVaFw0yMzA5MjkxMjE0NTVaMD4xFjAUBgNV\nBAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1\ndGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALZqkh/DczWP\nJnxnHLQ7QL0T4B4CDKWBKCcisriGbA6ZePWVNo4hfKQC6JrzfR+081NeD6VcWUiz\nrmd+jtPhIY4c+WVQYm5PKaN6DT1imYdxQw7aqO5j2KUCEh/cznpLxeSHoTxlR34E\nQwF28Wl3eg2vc5ct8LjU3eozWVk3gb7alx9mSA2SgmuX5lEQawl++rSjsBStemY2\nBDwOpAMXIrdEyP/cVn8mkvi/BDs5M5G+09j0gfhyCzRWMQ7Hn71u1eolRxwVxgi3\nTMn+/vTaFSqxKjgck6zuAYjBRPaHe7qLxHNr1So/Mc9nPy+3wHebFwbIcnUojwbp\n4nctkWbjb2cCAwEAAaNQME4wHQYDVR0OBBYEFP1whtcrydmW3ZJeuSoKZIKjze3w\nMB8GA1UdIwQYMBaAFP1whtcrydmW3ZJeuSoKZIKjze3wMAwGA1UdEwQFMAMBAf8w\nDQYJKoZIhvcNAQEFBQADggEBAG2erXhwRAa7+ZOBs0B6X57Hwyd1R4kfmXcs0rta\nlbPpvgULSiB+TCbf3EbhJnHGyvdCY1tvlffLjdA7HJ0PCOn+YYLBA0pTU/dyvrN6\nSu8NuS5yubnt9mb13nDGYo1rnt0YRfxN+8DM3fXIVr038A30UlPX2Ou1ExFJT0MZ\nuFKY6ZvLdI6/1cbgmguMlAhM+DhKyV6Sr5699LM3zqeI816pZmlREETYkGr91q7k\nBpXJu/dtHaGxg1ZGu6w/PCsYGUcECWENYD4VQPd8N32JjOfu6vEgoEAwfPP+3oGp\nZ4m3ewACcWOAenqflb+cQYC4PsF7qbXDmRaWrbKntOlZ3n0=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIGMTCCBBmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwajELMAkGA1UEBhMCVVMx\nCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJDQTESMBAGA1UECgwJUmVkaXNMYWJzMS0w\nKwYDVQQDDCRSZWRpc0xhYnMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN\nMTgwMjI1MTUzNzM3WhcNMjgwMjIzMTUzNzM3WjBfMQswCQYDVQQGEwJVUzELMAkG\nA1UECAwCQ0ExEjAQBgNVBAoMCVJlZGlzTGFiczEvMC0GA1UEAwwmUkNQIEludGVy\nbWVkaWF0ZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA\nA4ICDwAwggIKAoICAQDf9dqbxc8Bq7Ctq9rWcxrGNKKHivqLAFpPq02yLPx6fsOv\nTq7GsDChAYBBc4v7Y2Ap9RD5Vs3dIhEANcnolf27QwrG9RMnnvzk8pCvp1o6zSU4\nVuOE1W66/O1/7e2rVxyrnTcP7UgK43zNIXu7+tiAqWsO92uSnuMoGPGpeaUm1jym\nhjWKtkAwDFSqvHY+XL5qDVBEjeUe+WHkYUg40cAXjusAqgm2hZt29c2wnVrxW25W\nP0meNlzHGFdA2AC5z54iRiqj57dTfBTkHoBczQxcyw6hhzxZQ4e5I5zOKjXXEhZN\nr0tA3YC14CTabKRus/JmZieyZzRgEy2oti64tmLYTqSlAD78pRL40VNoaSYetXLw\nhhNsXCHgWaY6d5bLOc/aIQMAV5oLvZQKvuXAF1IDmhPA+bZbpWipp0zagf1P1H3s\nUzsMdn2KM0ejzgotbtNlj5TcrVwpmvE3ktvUAuA+hi3FkVx1US+2Gsp5x4YOzJ7u\nP1WPk6ShF0JgnJH2ILdj6kttTWwFzH17keSFICWDfH/+kM+k7Y1v3EXMQXE7y0T9\nMjvJskz6d/nv+sQhY04xt64xFMGTnZjlJMzfQNi7zWFLTZnDD0lPowq7l3YiPoTT\nt5Xky83lu0KZsZBo0WlWaDG00gLVdtRgVbcuSWxpi5BdLb1kRab66JptWjxwXQID\nAQABo4HrMIHoMDoGA1UdHwQzMDEwL6AtoCuGKWh0dHBzOi8vcmwtY2Etc2VydmVy\nLnJlZGlzbGFicy5jb20vdjEvY3JsMEYGCCsGAQUFBwEBBDowODA2BggrBgEFBQcw\nAYYqaHR0cHM6Ly9ybC1jYS1zZXJ2ZXIucmVkaXNsYWJzLmNvbS92MS9vY3NwMB0G\nA1UdDgQWBBQHar5OKvQUpP2qWt6mckzToeCOHDAfBgNVHSMEGDAWgBQi42wH6hM4\nL2sujEvLM0/u8lRXTzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIB\nhjANBgkqhkiG9w0BAQsFAAOCAgEAirEn/iTsAKyhd+pu2W3Z5NjCko4NPU0EYUbr\nAP7+POK2rzjIrJO3nFYQ/LLuC7KCXG+2qwan2SAOGmqWst13Y+WHp44Kae0kaChW\nvcYLXXSoGQGC8QuFSNUdaeg3RbMDYFT04dOkqufeWVccoHVxyTSg9eD8LZuHn5jw\n7QDLiEECBmIJHk5Eeo2TAZrx4Yx6ufSUX5HeVjlAzqwtAqdt99uCJ/EL8bgpWbe+\nXoSpvUv0SEC1I1dCAhCKAvRlIOA6VBcmzg5Am12KzkqTul12/VEFIgzqu0Zy2Jbc\nAUPrYVu/+tOGXQaijy7YgwH8P8n3s7ZeUa1VABJHcxrxYduDDJBLZi+MjheUDaZ1\njQRHYevI2tlqeSBqdPKG4zBY5lS0GiAlmuze5oENt0P3XboHoZPHiqcK3VECgTVh\n/BkJcuudETSJcZDmQ8YfoKfBzRQNg2sv/hwvUv73Ss51Sco8GEt2lD8uEdib1Q6z\nzDT5lXJowSzOD5ZA9OGDjnSRL+2riNtKWKEqvtEG3VBJoBzu9GoxbAc7wIZLxmli\niF5a/Zf5X+UXD3s4TMmy6C4QZJpAA2egsSQCnraWO2ULhh7iXMysSkF/nzVfZn43\niqpaB8++9a37hWq14ZmOv0TJIDz//b2+KC4VFXWQ5W5QC6whsjT+OlG4p5ZYG0jo\n616pxqo=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFujCCA6KgAwIBAgIJAJ1aTT1lu2ScMA0GCSqGSIb3DQEBCwUAMGoxCzAJBgNV\nBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCQ0ExEjAQBgNVBAoMCVJlZGlz\nTGFiczEtMCsGA1UEAwwkUmVkaXNMYWJzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9y\naXR5MB4XDTE4MDIyNTE1MjA0MloXDTM4MDIyMDE1MjA0MlowajELMAkGA1UEBhMC\nVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJDQTESMBAGA1UECgwJUmVkaXNMYWJz\nMS0wKwYDVQQDDCRSZWRpc0xhYnMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw\nggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLEjXy7YrbN5Waau5cd6g1\nG5C2tMmeTpZ0duFAPxNU4oE3RHS5gGiok346fUXuUxbZ6QkuzeN2/2Z+RmRcJhQY\nDm0ZgdG4x59An1TJfnzKKoWj8ISmoHS/TGNBdFzXV7FYNLBuqZouqePI6ReC6Qhl\npp45huV32Q3a6IDrrvx7Wo5ZczEQeFNbCeCOQYNDdTmCyEkHqc2AGo8eoIlSTutT\nULOC7R5gzJVTS0e1hesQ7jmqHjbO+VQS1NAL4/5K6cuTEqUl+XhVhPdLWBXJQ5ag\n54qhX4v+ojLzeU1R/Vc6NjMvVtptWY6JihpgplprN0Yh2556ewcXMeturcKgXfGJ\nxeYzsjzXerEjrVocX5V8BNrg64NlifzTMKNOOv4fVZszq1SIHR8F9ROrqiOdh8iC\nJpUbLpXH9hWCSEO6VRMB2xJoKu3cgl63kF30s77x7wLFMEHiwsQRKxooE1UhgS9K\n2sO4TlQ1eWUvFvHSTVDQDlGQ6zu4qjbOpb3Q8bQwoK+ai2alkXVR4Ltxe9QlgYK3\nStsnPhruzZGA0wbXdpw0bnM+YdlEm5ffSTpNIfgHeaa7Dtb801FtA71ZlH7A6TaI\nSIQuUST9EKmv7xrJyx0W1pGoPOLw5T029aTjnICSLdtV9bLwysrLhIYG5bnPq78B\ncS+jZHFGzD7PUVGQD01nOQIDAQABo2MwYTAdBgNVHQ4EFgQUIuNsB+oTOC9rLoxL\nyzNP7vJUV08wHwYDVR0jBBgwFoAUIuNsB+oTOC9rLoxLyzNP7vJUV08wDwYDVR0T\nAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAHfg\nz5pMNUAKdMzK1aS1EDdK9yKz4qicILz5czSLj1mC7HKDRy8cVADUxEICis++CsCu\nrYOvyCVergHQLREcxPq4rc5Nq1uj6J6649NEeh4WazOOjL4ZfQ1jVznMbGy+fJm3\n3Hoelv6jWRG9iqeJZja7/1s6YC6bWymI/OY1e4wUKeNHAo+Vger7MlHV+RuabaX+\nhSJ8bJAM59NCM7AgMTQpJCncrcdLeceYniGy5Q/qt2b5mJkQVkIdy4TPGGB+AXDJ\nD0q3I/JDRkDUFNFdeW0js7fHdsvCR7O3tJy5zIgEV/o/BCkmJVtuwPYOrw/yOlKj\nTY/U7ATAx9VFF6/vYEOMYSmrZlFX+98L6nJtwDqfLB5VTltqZ4H/KBxGE3IRSt9l\nFXy40U+LnXzhhW+7VBAvyYX8GEXhHkKU8Gqk1xitrqfBXY74xKgyUSTolFSfFVgj\nmcM/X4K45bka+qpkj7Kfv/8D4j6aZekwhN2ly6hhC1SmQ8qjMjpG/mrWOSSHZFmf\nybu9iD2AYHeIOkshIl6xYIa++Q/00/vs46IzAbQyriOi0XxlSMMVtPx0Q3isp+ji\nn8Mq9eOuxYOEQ4of8twUkUDd528iwGtEdwf0Q01UyT84S62N8AySl1ZBKXJz6W4F\nUhWfa/HQYOAPDdEjNgnVwLI23b8t0TozyCWw7q8h\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIIEjzCCA3egAwIBAgIQe55B/ALCKJDZtdNT8kD6hTANBgkqhkiG9w0BAQsFADBM\nMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv\nYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjAxMjYxMjAwMDBaFw0y\nNTAxMjYwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu\nIG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIE9WIFRMUyBDQSAy\nMDIyIFEyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmGmg1LW9b7Lf\n8zDD83yBDTEkt+FOxKJZqF4veWc5KZsQj9HfnUS2e5nj/E+JImlGPsQuoiosLuXD\nBVBNAMcUFa11buFMGMeEMwiTmCXoXRrXQmH0qjpOfKgYc5gHG3BsRGaRrf7VR4eg\nofNMG9wUBw4/g/TT7+bQJdA4NfE7Y4d5gEryZiBGB/swaX6Jp/8MF4TgUmOWmalK\ndZCKyb4sPGQFRTtElk67F7vU+wdGcrcOx1tDcIB0ncjLPMnaFicagl+daWGsKqTh\ncounQb6QJtYHa91KvCfKWocMxQ7OIbB5UARLPmC4CJ1/f8YFm35ebfzAeULYdGXu\njE9CLor0OwIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG\nCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW\nBBSH5Zq7a7B/t95GfJWkDBpA8HHqdjAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj\nmove4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw\nMi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1\ncmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w\nK6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD\nVR0gBBowGDAIBgZngQwBAgIwDAYKKwYBBAGgMgoBAjANBgkqhkiG9w0BAQsFAAOC\nAQEAKRic9/f+nmhQU/wz04APZLjgG5OgsuUOyUEZjKVhNGDwxGTvKhyXGGAMW2B/\n3bRi+aElpXwoxu3pL6fkElbX3B0BeS5LoDtxkyiVEBMZ8m+sXbocwlPyxrPbX6mY\n0rVIvnuUeBH8X0L5IwfpNVvKnBIilTbcebfHyXkPezGwz7E1yhUULjJFm2bt0SdX\ny+4X/WeiiYIv+fTVgZZgl+/2MKIsu/qdBJc3f3TvJ8nz+Eax1zgZmww+RSQWeOj3\n15Iw6Z5FX+NwzY/Ab+9PosR5UosSeq+9HhtaxZttXG1nVh+avYPGYddWmiMT90J5\nZgKnO/Fx2hBgTxhOTMYaD312kg==\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G\nA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp\nZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4\nMTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG\nA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8\nRgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT\ngHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm\nKPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\nQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ\nXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw\nDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o\nLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU\nRUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp\njjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK\n6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX\nmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs\nMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\nWD9f\n-----END CERTIFICATE-----";
+ };
+};
+export default TLSProfiles;
diff --git a/node_modules/ioredis/built/constants/TLSProfiles.js b/node_modules/ioredis/built/constants/TLSProfiles.js
new file mode 100644
index 0000000..f90760d
--- /dev/null
+++ b/node_modules/ioredis/built/constants/TLSProfiles.js
@@ -0,0 +1,149 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+/**
+ * TLS settings for Redis Cloud. Updated on 2022-08-19.
+ */
+const RedisCloudCA = `-----BEGIN CERTIFICATE-----
+MIIDTzCCAjegAwIBAgIJAKSVpiDswLcwMA0GCSqGSIb3DQEBBQUAMD4xFjAUBgNV
+BAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTAeFw0xMzEwMDExMjE0NTVaFw0yMzA5MjkxMjE0NTVaMD4xFjAUBgNV
+BAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALZqkh/DczWP
+JnxnHLQ7QL0T4B4CDKWBKCcisriGbA6ZePWVNo4hfKQC6JrzfR+081NeD6VcWUiz
+rmd+jtPhIY4c+WVQYm5PKaN6DT1imYdxQw7aqO5j2KUCEh/cznpLxeSHoTxlR34E
+QwF28Wl3eg2vc5ct8LjU3eozWVk3gb7alx9mSA2SgmuX5lEQawl++rSjsBStemY2
+BDwOpAMXIrdEyP/cVn8mkvi/BDs5M5G+09j0gfhyCzRWMQ7Hn71u1eolRxwVxgi3
+TMn+/vTaFSqxKjgck6zuAYjBRPaHe7qLxHNr1So/Mc9nPy+3wHebFwbIcnUojwbp
+4nctkWbjb2cCAwEAAaNQME4wHQYDVR0OBBYEFP1whtcrydmW3ZJeuSoKZIKjze3w
+MB8GA1UdIwQYMBaAFP1whtcrydmW3ZJeuSoKZIKjze3wMAwGA1UdEwQFMAMBAf8w
+DQYJKoZIhvcNAQEFBQADggEBAG2erXhwRAa7+ZOBs0B6X57Hwyd1R4kfmXcs0rta
+lbPpvgULSiB+TCbf3EbhJnHGyvdCY1tvlffLjdA7HJ0PCOn+YYLBA0pTU/dyvrN6
+Su8NuS5yubnt9mb13nDGYo1rnt0YRfxN+8DM3fXIVr038A30UlPX2Ou1ExFJT0MZ
+uFKY6ZvLdI6/1cbgmguMlAhM+DhKyV6Sr5699LM3zqeI816pZmlREETYkGr91q7k
+BpXJu/dtHaGxg1ZGu6w/PCsYGUcECWENYD4VQPd8N32JjOfu6vEgoEAwfPP+3oGp
+Z4m3ewACcWOAenqflb+cQYC4PsF7qbXDmRaWrbKntOlZ3n0=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGMTCCBBmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwajELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMQswCQYDVQQHDAJDQTESMBAGA1UECgwJUmVkaXNMYWJzMS0w
+KwYDVQQDDCRSZWRpc0xhYnMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
+MTgwMjI1MTUzNzM3WhcNMjgwMjIzMTUzNzM3WjBfMQswCQYDVQQGEwJVUzELMAkG
+A1UECAwCQ0ExEjAQBgNVBAoMCVJlZGlzTGFiczEvMC0GA1UEAwwmUkNQIEludGVy
+bWVkaWF0ZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDf9dqbxc8Bq7Ctq9rWcxrGNKKHivqLAFpPq02yLPx6fsOv
+Tq7GsDChAYBBc4v7Y2Ap9RD5Vs3dIhEANcnolf27QwrG9RMnnvzk8pCvp1o6zSU4
+VuOE1W66/O1/7e2rVxyrnTcP7UgK43zNIXu7+tiAqWsO92uSnuMoGPGpeaUm1jym
+hjWKtkAwDFSqvHY+XL5qDVBEjeUe+WHkYUg40cAXjusAqgm2hZt29c2wnVrxW25W
+P0meNlzHGFdA2AC5z54iRiqj57dTfBTkHoBczQxcyw6hhzxZQ4e5I5zOKjXXEhZN
+r0tA3YC14CTabKRus/JmZieyZzRgEy2oti64tmLYTqSlAD78pRL40VNoaSYetXLw
+hhNsXCHgWaY6d5bLOc/aIQMAV5oLvZQKvuXAF1IDmhPA+bZbpWipp0zagf1P1H3s
+UzsMdn2KM0ejzgotbtNlj5TcrVwpmvE3ktvUAuA+hi3FkVx1US+2Gsp5x4YOzJ7u
+P1WPk6ShF0JgnJH2ILdj6kttTWwFzH17keSFICWDfH/+kM+k7Y1v3EXMQXE7y0T9
+MjvJskz6d/nv+sQhY04xt64xFMGTnZjlJMzfQNi7zWFLTZnDD0lPowq7l3YiPoTT
+t5Xky83lu0KZsZBo0WlWaDG00gLVdtRgVbcuSWxpi5BdLb1kRab66JptWjxwXQID
+AQABo4HrMIHoMDoGA1UdHwQzMDEwL6AtoCuGKWh0dHBzOi8vcmwtY2Etc2VydmVy
+LnJlZGlzbGFicy5jb20vdjEvY3JsMEYGCCsGAQUFBwEBBDowODA2BggrBgEFBQcw
+AYYqaHR0cHM6Ly9ybC1jYS1zZXJ2ZXIucmVkaXNsYWJzLmNvbS92MS9vY3NwMB0G
+A1UdDgQWBBQHar5OKvQUpP2qWt6mckzToeCOHDAfBgNVHSMEGDAWgBQi42wH6hM4
+L2sujEvLM0/u8lRXTzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQsFAAOCAgEAirEn/iTsAKyhd+pu2W3Z5NjCko4NPU0EYUbr
+AP7+POK2rzjIrJO3nFYQ/LLuC7KCXG+2qwan2SAOGmqWst13Y+WHp44Kae0kaChW
+vcYLXXSoGQGC8QuFSNUdaeg3RbMDYFT04dOkqufeWVccoHVxyTSg9eD8LZuHn5jw
+7QDLiEECBmIJHk5Eeo2TAZrx4Yx6ufSUX5HeVjlAzqwtAqdt99uCJ/EL8bgpWbe+
+XoSpvUv0SEC1I1dCAhCKAvRlIOA6VBcmzg5Am12KzkqTul12/VEFIgzqu0Zy2Jbc
+AUPrYVu/+tOGXQaijy7YgwH8P8n3s7ZeUa1VABJHcxrxYduDDJBLZi+MjheUDaZ1
+jQRHYevI2tlqeSBqdPKG4zBY5lS0GiAlmuze5oENt0P3XboHoZPHiqcK3VECgTVh
+/BkJcuudETSJcZDmQ8YfoKfBzRQNg2sv/hwvUv73Ss51Sco8GEt2lD8uEdib1Q6z
+zDT5lXJowSzOD5ZA9OGDjnSRL+2riNtKWKEqvtEG3VBJoBzu9GoxbAc7wIZLxmli
+iF5a/Zf5X+UXD3s4TMmy6C4QZJpAA2egsSQCnraWO2ULhh7iXMysSkF/nzVfZn43
+iqpaB8++9a37hWq14ZmOv0TJIDz//b2+KC4VFXWQ5W5QC6whsjT+OlG4p5ZYG0jo
+616pxqo=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFujCCA6KgAwIBAgIJAJ1aTT1lu2ScMA0GCSqGSIb3DQEBCwUAMGoxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCQ0ExEjAQBgNVBAoMCVJlZGlz
+TGFiczEtMCsGA1UEAwwkUmVkaXNMYWJzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9y
+aXR5MB4XDTE4MDIyNTE1MjA0MloXDTM4MDIyMDE1MjA0MlowajELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJDQTESMBAGA1UECgwJUmVkaXNMYWJz
+MS0wKwYDVQQDDCRSZWRpc0xhYnMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw
+ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLEjXy7YrbN5Waau5cd6g1
+G5C2tMmeTpZ0duFAPxNU4oE3RHS5gGiok346fUXuUxbZ6QkuzeN2/2Z+RmRcJhQY
+Dm0ZgdG4x59An1TJfnzKKoWj8ISmoHS/TGNBdFzXV7FYNLBuqZouqePI6ReC6Qhl
+pp45huV32Q3a6IDrrvx7Wo5ZczEQeFNbCeCOQYNDdTmCyEkHqc2AGo8eoIlSTutT
+ULOC7R5gzJVTS0e1hesQ7jmqHjbO+VQS1NAL4/5K6cuTEqUl+XhVhPdLWBXJQ5ag
+54qhX4v+ojLzeU1R/Vc6NjMvVtptWY6JihpgplprN0Yh2556ewcXMeturcKgXfGJ
+xeYzsjzXerEjrVocX5V8BNrg64NlifzTMKNOOv4fVZszq1SIHR8F9ROrqiOdh8iC
+JpUbLpXH9hWCSEO6VRMB2xJoKu3cgl63kF30s77x7wLFMEHiwsQRKxooE1UhgS9K
+2sO4TlQ1eWUvFvHSTVDQDlGQ6zu4qjbOpb3Q8bQwoK+ai2alkXVR4Ltxe9QlgYK3
+StsnPhruzZGA0wbXdpw0bnM+YdlEm5ffSTpNIfgHeaa7Dtb801FtA71ZlH7A6TaI
+SIQuUST9EKmv7xrJyx0W1pGoPOLw5T029aTjnICSLdtV9bLwysrLhIYG5bnPq78B
+cS+jZHFGzD7PUVGQD01nOQIDAQABo2MwYTAdBgNVHQ4EFgQUIuNsB+oTOC9rLoxL
+yzNP7vJUV08wHwYDVR0jBBgwFoAUIuNsB+oTOC9rLoxLyzNP7vJUV08wDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAHfg
+z5pMNUAKdMzK1aS1EDdK9yKz4qicILz5czSLj1mC7HKDRy8cVADUxEICis++CsCu
+rYOvyCVergHQLREcxPq4rc5Nq1uj6J6649NEeh4WazOOjL4ZfQ1jVznMbGy+fJm3
+3Hoelv6jWRG9iqeJZja7/1s6YC6bWymI/OY1e4wUKeNHAo+Vger7MlHV+RuabaX+
+hSJ8bJAM59NCM7AgMTQpJCncrcdLeceYniGy5Q/qt2b5mJkQVkIdy4TPGGB+AXDJ
+D0q3I/JDRkDUFNFdeW0js7fHdsvCR7O3tJy5zIgEV/o/BCkmJVtuwPYOrw/yOlKj
+TY/U7ATAx9VFF6/vYEOMYSmrZlFX+98L6nJtwDqfLB5VTltqZ4H/KBxGE3IRSt9l
+FXy40U+LnXzhhW+7VBAvyYX8GEXhHkKU8Gqk1xitrqfBXY74xKgyUSTolFSfFVgj
+mcM/X4K45bka+qpkj7Kfv/8D4j6aZekwhN2ly6hhC1SmQ8qjMjpG/mrWOSSHZFmf
+ybu9iD2AYHeIOkshIl6xYIa++Q/00/vs46IzAbQyriOi0XxlSMMVtPx0Q3isp+ji
+n8Mq9eOuxYOEQ4of8twUkUDd528iwGtEdwf0Q01UyT84S62N8AySl1ZBKXJz6W4F
+UhWfa/HQYOAPDdEjNgnVwLI23b8t0TozyCWw7q8h
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIEjzCCA3egAwIBAgIQe55B/ALCKJDZtdNT8kD6hTANBgkqhkiG9w0BAQsFADBM
+MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv
+YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjAxMjYxMjAwMDBaFw0y
+NTAxMjYwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu
+IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIE9WIFRMUyBDQSAy
+MDIyIFEyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmGmg1LW9b7Lf
+8zDD83yBDTEkt+FOxKJZqF4veWc5KZsQj9HfnUS2e5nj/E+JImlGPsQuoiosLuXD
+BVBNAMcUFa11buFMGMeEMwiTmCXoXRrXQmH0qjpOfKgYc5gHG3BsRGaRrf7VR4eg
+ofNMG9wUBw4/g/TT7+bQJdA4NfE7Y4d5gEryZiBGB/swaX6Jp/8MF4TgUmOWmalK
+dZCKyb4sPGQFRTtElk67F7vU+wdGcrcOx1tDcIB0ncjLPMnaFicagl+daWGsKqTh
+counQb6QJtYHa91KvCfKWocMxQ7OIbB5UARLPmC4CJ1/f8YFm35ebfzAeULYdGXu
+jE9CLor0OwIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG
+CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
+BBSH5Zq7a7B/t95GfJWkDBpA8HHqdjAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj
+move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw
+Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1
+cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w
+K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD
+VR0gBBowGDAIBgZngQwBAgIwDAYKKwYBBAGgMgoBAjANBgkqhkiG9w0BAQsFAAOC
+AQEAKRic9/f+nmhQU/wz04APZLjgG5OgsuUOyUEZjKVhNGDwxGTvKhyXGGAMW2B/
+3bRi+aElpXwoxu3pL6fkElbX3B0BeS5LoDtxkyiVEBMZ8m+sXbocwlPyxrPbX6mY
+0rVIvnuUeBH8X0L5IwfpNVvKnBIilTbcebfHyXkPezGwz7E1yhUULjJFm2bt0SdX
+y+4X/WeiiYIv+fTVgZZgl+/2MKIsu/qdBJc3f3TvJ8nz+Eax1zgZmww+RSQWeOj3
+15Iw6Z5FX+NwzY/Ab+9PosR5UosSeq+9HhtaxZttXG1nVh+avYPGYddWmiMT90J5
+ZgKnO/Fx2hBgTxhOTMYaD312kg==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
+MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
+RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
+gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
+KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
+QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
+XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
+LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
+RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
+jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
+6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
+mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
+Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
+WD9f
+-----END CERTIFICATE-----`;
+const TLSProfiles = {
+ RedisCloudFixed: { ca: RedisCloudCA },
+ RedisCloudFlexible: { ca: RedisCloudCA },
+};
+exports.default = TLSProfiles;
diff --git a/node_modules/ioredis/built/errors/ClusterAllFailedError.d.ts b/node_modules/ioredis/built/errors/ClusterAllFailedError.d.ts
new file mode 100644
index 0000000..870640f
--- /dev/null
+++ b/node_modules/ioredis/built/errors/ClusterAllFailedError.d.ts
@@ -0,0 +1,7 @@
+import { RedisError } from "redis-errors";
+export default class ClusterAllFailedError extends RedisError {
+ lastNodeError: RedisError;
+ static defaultMessage: string;
+ constructor(message: any, lastNodeError: RedisError);
+ get name(): string;
+}
diff --git a/node_modules/ioredis/built/errors/ClusterAllFailedError.js b/node_modules/ioredis/built/errors/ClusterAllFailedError.js
new file mode 100644
index 0000000..c223f16
--- /dev/null
+++ b/node_modules/ioredis/built/errors/ClusterAllFailedError.js
@@ -0,0 +1,15 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const redis_errors_1 = require("redis-errors");
+class ClusterAllFailedError extends redis_errors_1.RedisError {
+ constructor(message, lastNodeError) {
+ super(message);
+ this.lastNodeError = lastNodeError;
+ Error.captureStackTrace(this, this.constructor);
+ }
+ get name() {
+ return this.constructor.name;
+ }
+}
+exports.default = ClusterAllFailedError;
+ClusterAllFailedError.defaultMessage = "Failed to refresh slots cache.";
diff --git a/node_modules/ioredis/built/errors/MaxRetriesPerRequestError.d.ts b/node_modules/ioredis/built/errors/MaxRetriesPerRequestError.d.ts
new file mode 100644
index 0000000..9c4b4e5
--- /dev/null
+++ b/node_modules/ioredis/built/errors/MaxRetriesPerRequestError.d.ts
@@ -0,0 +1,5 @@
+import { AbortError } from "redis-errors";
+export default class MaxRetriesPerRequestError extends AbortError {
+ constructor(maxRetriesPerRequest: number);
+ get name(): string;
+}
diff --git a/node_modules/ioredis/built/errors/MaxRetriesPerRequestError.js b/node_modules/ioredis/built/errors/MaxRetriesPerRequestError.js
new file mode 100644
index 0000000..d8c8e1d
--- /dev/null
+++ b/node_modules/ioredis/built/errors/MaxRetriesPerRequestError.js
@@ -0,0 +1,14 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const redis_errors_1 = require("redis-errors");
+class MaxRetriesPerRequestError extends redis_errors_1.AbortError {
+ constructor(maxRetriesPerRequest) {
+ const message = `Reached the max retries per request limit (which is ${maxRetriesPerRequest}). Refer to "maxRetriesPerRequest" option for details.`;
+ super(message);
+ Error.captureStackTrace(this, this.constructor);
+ }
+ get name() {
+ return this.constructor.name;
+ }
+}
+exports.default = MaxRetriesPerRequestError;
diff --git a/node_modules/ioredis/built/errors/index.d.ts b/node_modules/ioredis/built/errors/index.d.ts
new file mode 100644
index 0000000..6d25308
--- /dev/null
+++ b/node_modules/ioredis/built/errors/index.d.ts
@@ -0,0 +1,2 @@
+import MaxRetriesPerRequestError from "./MaxRetriesPerRequestError";
+export { MaxRetriesPerRequestError };
diff --git a/node_modules/ioredis/built/errors/index.js b/node_modules/ioredis/built/errors/index.js
new file mode 100644
index 0000000..02b72a9
--- /dev/null
+++ b/node_modules/ioredis/built/errors/index.js
@@ -0,0 +1,5 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.MaxRetriesPerRequestError = void 0;
+const MaxRetriesPerRequestError_1 = require("./MaxRetriesPerRequestError");
+exports.MaxRetriesPerRequestError = MaxRetriesPerRequestError_1.default;
diff --git a/node_modules/ioredis/built/index.d.ts b/node_modules/ioredis/built/index.d.ts
new file mode 100644
index 0000000..d4517f3
--- /dev/null
+++ b/node_modules/ioredis/built/index.d.ts
@@ -0,0 +1,44 @@
+export { default } from "./Redis";
+export { default as Redis } from "./Redis";
+export { default as Cluster } from "./cluster";
+/**
+ * @ignore
+ */
+export { default as Command } from "./Command";
+/**
+ * @ignore
+ */
+export { default as RedisCommander, Result, ClientContext, } from "./utils/RedisCommander";
+/**
+ * @ignore
+ */
+export { default as ScanStream } from "./ScanStream";
+/**
+ * @ignore
+ */
+export { default as Pipeline } from "./Pipeline";
+/**
+ * @ignore
+ */
+export { default as AbstractConnector } from "./connectors/AbstractConnector";
+/**
+ * @ignore
+ */
+export { default as SentinelConnector, SentinelIterator, } from "./connectors/SentinelConnector";
+/**
+ * @ignore
+ */
+export { Callback } from "./types";
+export { SentinelAddress, SentinelConnectionOptions, } from "./connectors/SentinelConnector";
+export { StandaloneConnectionOptions } from "./connectors/StandaloneConnector";
+export { RedisOptions, CommonRedisOptions } from "./redis/RedisOptions";
+export { ClusterNode } from "./cluster";
+export { ClusterOptions, DNSLookupFunction, DNSResolveSrvFunction, NatMap, } from "./cluster/ClusterOptions";
+export { NodeRole } from "./cluster/util";
+export type { RedisKey, RedisValue, ChainableCommander, } from "./utils/RedisCommander";
+export type { CommandTraceContext, BatchOperationContext, ConnectTraceContext, } from "./tracing";
+export declare const ReplyError: any;
+/**
+ * @ignore
+ */
+export declare function print(err: Error | null, reply?: any): void;
diff --git a/node_modules/ioredis/built/index.js b/node_modules/ioredis/built/index.js
new file mode 100644
index 0000000..24a1895
--- /dev/null
+++ b/node_modules/ioredis/built/index.js
@@ -0,0 +1,62 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.print = exports.ReplyError = exports.SentinelIterator = exports.SentinelConnector = exports.AbstractConnector = exports.Pipeline = exports.ScanStream = exports.Command = exports.Cluster = exports.Redis = exports.default = void 0;
+exports = module.exports = require("./Redis").default;
+var Redis_1 = require("./Redis");
+Object.defineProperty(exports, "default", { enumerable: true, get: function () { return Redis_1.default; } });
+var Redis_2 = require("./Redis");
+Object.defineProperty(exports, "Redis", { enumerable: true, get: function () { return Redis_2.default; } });
+var cluster_1 = require("./cluster");
+Object.defineProperty(exports, "Cluster", { enumerable: true, get: function () { return cluster_1.default; } });
+/**
+ * @ignore
+ */
+var Command_1 = require("./Command");
+Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return Command_1.default; } });
+/**
+ * @ignore
+ */
+var ScanStream_1 = require("./ScanStream");
+Object.defineProperty(exports, "ScanStream", { enumerable: true, get: function () { return ScanStream_1.default; } });
+/**
+ * @ignore
+ */
+var Pipeline_1 = require("./Pipeline");
+Object.defineProperty(exports, "Pipeline", { enumerable: true, get: function () { return Pipeline_1.default; } });
+/**
+ * @ignore
+ */
+var AbstractConnector_1 = require("./connectors/AbstractConnector");
+Object.defineProperty(exports, "AbstractConnector", { enumerable: true, get: function () { return AbstractConnector_1.default; } });
+/**
+ * @ignore
+ */
+var SentinelConnector_1 = require("./connectors/SentinelConnector");
+Object.defineProperty(exports, "SentinelConnector", { enumerable: true, get: function () { return SentinelConnector_1.default; } });
+Object.defineProperty(exports, "SentinelIterator", { enumerable: true, get: function () { return SentinelConnector_1.SentinelIterator; } });
+// No TS typings
+exports.ReplyError = require("redis-errors").ReplyError;
+/**
+ * @ignore
+ */
+Object.defineProperty(exports, "Promise", {
+ get() {
+ console.warn("ioredis v5 does not support plugging third-party Promise library anymore. Native Promise will be used.");
+ return Promise;
+ },
+ set(_lib) {
+ console.warn("ioredis v5 does not support plugging third-party Promise library anymore. Native Promise will be used.");
+ },
+});
+/**
+ * @ignore
+ */
+function print(err, reply) {
+ if (err) {
+ console.log("Error: " + err);
+ }
+ else {
+ console.log("Reply: " + reply);
+ }
+}
+exports.print = print;
diff --git a/node_modules/ioredis/built/redis/RedisOptions.d.ts b/node_modules/ioredis/built/redis/RedisOptions.d.ts
new file mode 100644
index 0000000..6242728
--- /dev/null
+++ b/node_modules/ioredis/built/redis/RedisOptions.d.ts
@@ -0,0 +1,197 @@
+import { CommanderOptions } from "../utils/Commander";
+import ConnectorConstructor from "../connectors/ConnectorConstructor";
+import { SentinelConnectionOptions } from "../connectors/SentinelConnector";
+import { StandaloneConnectionOptions } from "../connectors/StandaloneConnector";
+export declare type ReconnectOnError = (err: Error) => boolean | 1 | 2;
+export interface CommonRedisOptions extends CommanderOptions {
+ Connector?: ConnectorConstructor | undefined;
+ retryStrategy?: ((times: number) => number | void | null) | undefined;
+ /**
+ * If a command does not return a reply within a set number of milliseconds,
+ * a "Command timed out" error will be thrown.
+ */
+ commandTimeout?: number | undefined;
+ /**
+ * Enables client-side timeout protection for blocking commands when set
+ * to a positive number. If `blockingTimeout` is undefined, `0`, or
+ * negative (e.g. `-1`), the protection is disabled and no client-side
+ * timers are installed for blocking commands.
+ */
+ blockingTimeout?: number | undefined;
+ /**
+ * Grace period (ms) added to blocking command timeouts. Only used when
+ * `blockingTimeout` is a positive number. Defaults to 100ms.
+ */
+ blockingTimeoutGrace?: number | undefined;
+ /**
+ * If the socket does not receive data within a set number of milliseconds:
+ * 1. the socket is considered "dead" and will be destroyed
+ * 2. the client will reject any running commands (altought they might have been processed by the server)
+ * 3. the reconnect strategy will kick in (depending on the configuration)
+ */
+ socketTimeout?: number | undefined;
+ /**
+ * Enable/disable keep-alive functionality.
+ * @link https://nodejs.org/api/net.html#socketsetkeepaliveenable-initialdelay
+ * @default 0
+ */
+ keepAlive?: number | undefined;
+ /**
+ * Enable/disable the use of Nagle's algorithm.
+ * @link https://nodejs.org/api/net.html#socketsetnodelaynodelay
+ * @default true
+ */
+ noDelay?: boolean | undefined;
+ /**
+ * Set the name of the connection to make it easier to identity the connection
+ * in client list.
+ * @link https://redis.io/commands/client-setname
+ */
+ connectionName?: string | undefined;
+ /**
+ * If true, skips setting library info via CLIENT SETINFO.
+ * @link https://redis.io/docs/latest/commands/client-setinfo/
+ * @default false
+ */
+ disableClientInfo?: boolean | undefined;
+ /**
+ * Tag to append to the library name in CLIENT SETINFO (ioredis(tag)).
+ * @link https://redis.io/docs/latest/commands/client-setinfo/
+ * @default undefined
+ */
+ clientInfoTag?: string | undefined;
+ /**
+ * If set, client will send AUTH command with the value of this option as the first argument when connected.
+ * This is supported since Redis 6.
+ */
+ username?: string | undefined;
+ /**
+ * If set, client will send AUTH command with the value of this option when connected.
+ */
+ password?: string | undefined;
+ /**
+ * Database index to use.
+ *
+ * @default 0
+ */
+ db?: number | undefined;
+ /**
+ * When the client reconnects, channels subscribed in the previous connection will be
+ * resubscribed automatically if `autoResubscribe` is `true`.
+ * @default true
+ */
+ autoResubscribe?: boolean | undefined;
+ /**
+ * Whether or not to resend unfulfilled commands on reconnect.
+ * Unfulfilled commands are most likely to be blocking commands such as `brpop` or `blpop`.
+ * @default true
+ */
+ autoResendUnfulfilledCommands?: boolean | undefined;
+ /**
+ * Whether or not to reconnect on certain Redis errors.
+ * This options by default is `null`, which means it should never reconnect on Redis errors.
+ * You can pass a function that accepts an Redis error, and returns:
+ * - `true` or `1` to trigger a reconnection.
+ * - `false` or `0` to not reconnect.
+ * - `2` to reconnect and resend the failed command (who triggered the error) after reconnection.
+ * @example
+ * ```js
+ * const redis = new Redis({
+ * reconnectOnError(err) {
+ * const targetError = "READONLY";
+ * if (err.message.includes(targetError)) {
+ * // Only reconnect when the error contains "READONLY"
+ * return true; // or `return 1;`
+ * }
+ * },
+ * });
+ * ```
+ * @default null
+ */
+ reconnectOnError?: ReconnectOnError | null | undefined;
+ /**
+ * @default false
+ */
+ readOnly?: boolean | undefined;
+ /**
+ * When enabled, numbers returned by Redis will be converted to JavaScript strings instead of numbers.
+ * This is necessary if you want to handle big numbers (above `Number.MAX_SAFE_INTEGER` === 2^53).
+ * @default false
+ */
+ stringNumbers?: boolean | undefined;
+ /**
+ * How long the client will wait before killing a socket due to inactivity during initial connection.
+ * @default 10000
+ */
+ connectTimeout?: number | undefined;
+ /**
+ * This option is used internally when you call `redis.monitor()` to tell Redis
+ * to enter the monitor mode when the connection is established.
+ *
+ * @default false
+ */
+ monitor?: boolean | undefined;
+ /**
+ * The commands that don't get a reply due to the connection to the server is lost are
+ * put into a queue and will be resent on reconnect (if allowed by the `retryStrategy` option).
+ * This option is used to configure how many reconnection attempts should be allowed before
+ * the queue is flushed with a `MaxRetriesPerRequestError` error.
+ * Set this options to `null` instead of a number to let commands wait forever
+ * until the connection is alive again.
+ *
+ * @default 20
+ */
+ maxRetriesPerRequest?: number | null | undefined;
+ /**
+ * @default 10000
+ */
+ maxLoadingRetryTime?: number | undefined;
+ /**
+ * @default false
+ */
+ enableAutoPipelining?: boolean | undefined;
+ /**
+ * @default []
+ */
+ autoPipeliningIgnoredCommands?: string[] | undefined;
+ offlineQueue?: boolean | undefined;
+ commandQueue?: boolean | undefined;
+ /**
+ *
+ * By default, if the connection to Redis server has not been established, commands are added to a queue
+ * and are executed once the connection is "ready" (when `enableReadyCheck` is true, "ready" means
+ * the Redis server has loaded the database from disk, otherwise means the connection to the Redis
+ * server has been established). If this option is false, when execute the command when the connection
+ * isn't ready, an error will be returned.
+ *
+ * @default true
+ */
+ enableOfflineQueue?: boolean | undefined;
+ /**
+ * The client will sent an INFO command to check whether the server is still loading data from the disk (
+ * which happens when the server is just launched) when the connection is established, and only wait until
+ * the loading process is finished before emitting the `ready` event.
+ *
+ * @default true
+ */
+ enableReadyCheck?: boolean | undefined;
+ /**
+ * When a Redis instance is initialized, a connection to the server is immediately established. Set this to
+ * true will delay the connection to the server until the first command is sent or `redis.connect()` is called
+ * explicitly. When `redis.connect()` is called explicitly, a Promise is returned, which will be resolved
+ * when the connection is ready or rejected when it fails. The rejection should be handled by the user.
+ *
+ * @default false
+ */
+ lazyConnect?: boolean | undefined;
+ /**
+ * @default undefined
+ */
+ scripts?: Record | undefined;
+}
+export declare type RedisOptions = CommonRedisOptions & SentinelConnectionOptions & StandaloneConnectionOptions;
+export declare const DEFAULT_REDIS_OPTIONS: RedisOptions;
diff --git a/node_modules/ioredis/built/redis/RedisOptions.js b/node_modules/ioredis/built/redis/RedisOptions.js
new file mode 100644
index 0000000..99ee7dc
--- /dev/null
+++ b/node_modules/ioredis/built/redis/RedisOptions.js
@@ -0,0 +1,58 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.DEFAULT_REDIS_OPTIONS = void 0;
+exports.DEFAULT_REDIS_OPTIONS = {
+ // Connection
+ port: 6379,
+ host: "localhost",
+ family: 0,
+ connectTimeout: 10000,
+ disconnectTimeout: 2000,
+ retryStrategy: function (times) {
+ return Math.min(times * 50, 2000);
+ },
+ keepAlive: 0,
+ noDelay: true,
+ connectionName: null,
+ disableClientInfo: false,
+ clientInfoTag: undefined,
+ // Sentinel
+ sentinels: null,
+ name: null,
+ role: "master",
+ sentinelRetryStrategy: function (times) {
+ return Math.min(times * 10, 1000);
+ },
+ sentinelReconnectStrategy: function () {
+ // This strategy only applies when sentinels are used for detecting
+ // a failover, not during initial master resolution.
+ // The deployment can still function when some of the sentinels are down
+ // for a long period of time, so we may not want to attempt reconnection
+ // very often. Therefore the default interval is fairly long (1 minute).
+ return 60000;
+ },
+ natMap: null,
+ enableTLSForSentinelMode: false,
+ updateSentinels: true,
+ failoverDetector: false,
+ // Status
+ username: null,
+ password: null,
+ db: 0,
+ // Others
+ enableOfflineQueue: true,
+ enableReadyCheck: true,
+ autoResubscribe: true,
+ autoResendUnfulfilledCommands: true,
+ lazyConnect: false,
+ keyPrefix: "",
+ reconnectOnError: null,
+ readOnly: false,
+ stringNumbers: false,
+ maxRetriesPerRequest: 20,
+ maxLoadingRetryTime: 10000,
+ enableAutoPipelining: false,
+ autoPipeliningIgnoredCommands: [],
+ sentinelMaxConnections: 10,
+ blockingTimeoutGrace: 100,
+};
diff --git a/node_modules/ioredis/built/redis/event_handler.d.ts b/node_modules/ioredis/built/redis/event_handler.d.ts
new file mode 100644
index 0000000..1ce79dd
--- /dev/null
+++ b/node_modules/ioredis/built/redis/event_handler.d.ts
@@ -0,0 +1,4 @@
+export declare function connectHandler(self: any): () => void;
+export declare function closeHandler(self: any): () => void;
+export declare function errorHandler(self: any): (error: any) => void;
+export declare function readyHandler(self: any): () => void;
diff --git a/node_modules/ioredis/built/redis/event_handler.js b/node_modules/ioredis/built/redis/event_handler.js
new file mode 100644
index 0000000..ba8b1dc
--- /dev/null
+++ b/node_modules/ioredis/built/redis/event_handler.js
@@ -0,0 +1,315 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.readyHandler = exports.errorHandler = exports.closeHandler = exports.connectHandler = void 0;
+const redis_errors_1 = require("redis-errors");
+const Command_1 = require("../Command");
+const errors_1 = require("../errors");
+const utils_1 = require("../utils");
+const DataHandler_1 = require("../DataHandler");
+const debug = (0, utils_1.Debug)("connection");
+function connectHandler(self) {
+ return function () {
+ var _a;
+ self.setStatus("connect");
+ self.resetCommandQueue();
+ // AUTH command should be processed before any other commands
+ let flushed = false;
+ const { connectionEpoch } = self;
+ if (self.condition.auth) {
+ self.auth(self.condition.auth, function (err) {
+ if (connectionEpoch !== self.connectionEpoch) {
+ return;
+ }
+ if (err) {
+ if (err.message.indexOf("no password is set") !== -1) {
+ console.warn("[WARN] Redis server does not require a password, but a password was supplied.");
+ }
+ else if (err.message.indexOf("without any password configured for the default user") !== -1) {
+ console.warn("[WARN] This Redis server's `default` user does not require a password, but a password was supplied");
+ }
+ else if (err.message.indexOf("wrong number of arguments for 'auth' command") !== -1) {
+ console.warn(`[ERROR] The server returned "wrong number of arguments for 'auth' command". You are probably passing both username and password to Redis version 5 or below. You should only pass the 'password' option for Redis version 5 and under.`);
+ }
+ else {
+ flushed = true;
+ self.recoverFromFatalError(err, err);
+ }
+ }
+ });
+ }
+ if (self.condition.select) {
+ self.select(self.condition.select).catch((err) => {
+ // If the node is in cluster mode, select is disallowed.
+ // In this case, reconnect won't help.
+ self.silentEmit("error", err);
+ });
+ }
+ /*
+ No need to keep the reference of DataHandler here
+ because we don't need to do the cleanup.
+ `Stream#end()` will remove all listeners for us.
+ */
+ new DataHandler_1.default(self, {
+ stringNumbers: self.options.stringNumbers,
+ });
+ const clientCommandPromises = [];
+ if (self.options.connectionName) {
+ debug("set the connection name [%s]", self.options.connectionName);
+ clientCommandPromises.push(self.client("setname", self.options.connectionName).catch(utils_1.noop));
+ }
+ if (!self.options.disableClientInfo) {
+ debug("set the client info");
+ clientCommandPromises.push((0, utils_1.getPackageMeta)()
+ .then((packageMeta) => {
+ return self
+ .client("SETINFO", "LIB-VER", packageMeta.version)
+ .catch(utils_1.noop);
+ })
+ .catch(utils_1.noop));
+ clientCommandPromises.push(self
+ .client("SETINFO", "LIB-NAME", ((_a = self.options) === null || _a === void 0 ? void 0 : _a.clientInfoTag)
+ ? `ioredis(${self.options.clientInfoTag})`
+ : "ioredis")
+ .catch(utils_1.noop));
+ }
+ Promise.all(clientCommandPromises)
+ .catch(utils_1.noop)
+ .finally(() => {
+ if (!self.options.enableReadyCheck) {
+ exports.readyHandler(self)();
+ }
+ if (self.options.enableReadyCheck) {
+ self._readyCheck(function (err, info) {
+ if (connectionEpoch !== self.connectionEpoch) {
+ return;
+ }
+ if (err) {
+ if (!flushed) {
+ self.recoverFromFatalError(new Error("Ready check failed: " + err.message), err);
+ }
+ }
+ else {
+ if (self.connector.check(info)) {
+ exports.readyHandler(self)();
+ }
+ else {
+ self.disconnect(true);
+ }
+ }
+ });
+ }
+ });
+ };
+}
+exports.connectHandler = connectHandler;
+function abortError(command) {
+ const err = new redis_errors_1.AbortError("Command aborted due to connection close");
+ err.command = {
+ name: command.name,
+ args: command.args,
+ };
+ return err;
+}
+// If a contiguous set of pipeline commands starts from index zero then they
+// can be safely reattempted. If however we have a chain of pipelined commands
+// starting at index 1 or more it means we received a partial response before
+// the connection close and those pipelined commands must be aborted. For
+// example, if the queue looks like this: [2, 3, 4, 0, 1, 2] then after
+// aborting and purging we'll have a queue that looks like this: [0, 1, 2]
+function abortIncompletePipelines(commandQueue) {
+ var _a;
+ let expectedIndex = 0;
+ for (let i = 0; i < commandQueue.length;) {
+ const command = (_a = commandQueue.peekAt(i)) === null || _a === void 0 ? void 0 : _a.command;
+ const pipelineIndex = command.pipelineIndex;
+ if (pipelineIndex === undefined || pipelineIndex === 0) {
+ expectedIndex = 0;
+ }
+ if (pipelineIndex !== undefined && pipelineIndex !== expectedIndex++) {
+ commandQueue.remove(i, 1);
+ command.reject(abortError(command));
+ continue;
+ }
+ i++;
+ }
+}
+// If only a partial transaction result was received before connection close,
+// we have to abort any transaction fragments that may have ended up in the
+// offline queue
+function abortTransactionFragments(commandQueue) {
+ var _a;
+ for (let i = 0; i < commandQueue.length;) {
+ const command = (_a = commandQueue.peekAt(i)) === null || _a === void 0 ? void 0 : _a.command;
+ if (command.name === "multi") {
+ break;
+ }
+ if (command.name === "exec") {
+ commandQueue.remove(i, 1);
+ command.reject(abortError(command));
+ break;
+ }
+ if (command.inTransaction) {
+ commandQueue.remove(i, 1);
+ command.reject(abortError(command));
+ }
+ else {
+ i++;
+ }
+ }
+}
+function closeHandler(self) {
+ return function () {
+ const prevStatus = self.status;
+ self.setStatus("close");
+ if (self.commandQueue.length) {
+ abortIncompletePipelines(self.commandQueue);
+ }
+ if (self.offlineQueue.length) {
+ abortTransactionFragments(self.offlineQueue);
+ }
+ if (prevStatus === "ready") {
+ if (!self.prevCondition) {
+ self.prevCondition = self.condition;
+ }
+ if (self.commandQueue.length) {
+ self.prevCommandQueue = self.commandQueue;
+ }
+ }
+ if (self.manuallyClosing) {
+ self.manuallyClosing = false;
+ debug("skip reconnecting since the connection is manually closed.");
+ return close();
+ }
+ if (typeof self.options.retryStrategy !== "function") {
+ debug("skip reconnecting because `retryStrategy` is not a function");
+ return close();
+ }
+ const retryDelay = self.options.retryStrategy(++self.retryAttempts);
+ if (typeof retryDelay !== "number") {
+ debug("skip reconnecting because `retryStrategy` doesn't return a number");
+ return close();
+ }
+ debug("reconnect in %sms", retryDelay);
+ self.setStatus("reconnecting", retryDelay);
+ self.reconnectTimeout = setTimeout(function () {
+ self.reconnectTimeout = null;
+ self.connect().catch(utils_1.noop);
+ }, retryDelay);
+ const { maxRetriesPerRequest } = self.options;
+ if (typeof maxRetriesPerRequest === "number") {
+ if (maxRetriesPerRequest < 0) {
+ debug("maxRetriesPerRequest is negative, ignoring...");
+ }
+ else {
+ const remainder = self.retryAttempts % (maxRetriesPerRequest + 1);
+ if (remainder === 0) {
+ debug("reach maxRetriesPerRequest limitation, flushing command queue...");
+ self.flushQueue(new errors_1.MaxRetriesPerRequestError(maxRetriesPerRequest));
+ }
+ }
+ }
+ };
+ function close() {
+ self.setStatus("end");
+ self.flushQueue(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
+ }
+}
+exports.closeHandler = closeHandler;
+function errorHandler(self) {
+ return function (error) {
+ debug("error: %s", error);
+ self.silentEmit("error", error);
+ };
+}
+exports.errorHandler = errorHandler;
+function readyHandler(self) {
+ return function () {
+ self.setStatus("ready");
+ self.retryAttempts = 0;
+ if (self.options.monitor) {
+ self.call("monitor").then(() => self.setStatus("monitoring"), (error) => self.emit("error", error));
+ const { sendCommand } = self;
+ self.sendCommand = function (command) {
+ if (Command_1.default.checkFlag("VALID_IN_MONITOR_MODE", command.name)) {
+ return sendCommand.call(self, command);
+ }
+ command.reject(new Error("Connection is in monitoring mode, can't process commands."));
+ return command.promise;
+ };
+ self.once("close", function () {
+ delete self.sendCommand;
+ });
+ return;
+ }
+ const finalSelect = self.prevCondition
+ ? self.prevCondition.select
+ : self.condition.select;
+ if (self.options.readOnly) {
+ debug("set the connection to readonly mode");
+ self.readonly().catch(utils_1.noop);
+ }
+ if (self.prevCondition) {
+ const condition = self.prevCondition;
+ self.prevCondition = null;
+ if (condition.subscriber && self.options.autoResubscribe) {
+ // We re-select the previous db first since
+ // `SELECT` command is not valid in sub mode.
+ if (self.condition.select !== finalSelect) {
+ debug("connect to db [%d]", finalSelect);
+ self.select(finalSelect);
+ }
+ const subscribeChannels = condition.subscriber.channels("subscribe");
+ if (subscribeChannels.length) {
+ debug("subscribe %d channels", subscribeChannels.length);
+ self.subscribe(subscribeChannels);
+ }
+ const psubscribeChannels = condition.subscriber.channels("psubscribe");
+ if (psubscribeChannels.length) {
+ debug("psubscribe %d channels", psubscribeChannels.length);
+ self.psubscribe(psubscribeChannels);
+ }
+ const ssubscribeChannels = condition.subscriber.channels("ssubscribe");
+ if (ssubscribeChannels.length) {
+ debug("ssubscribe %s", ssubscribeChannels.length);
+ for (const channel of ssubscribeChannels) {
+ self.ssubscribe(channel);
+ }
+ }
+ }
+ }
+ if (self.prevCommandQueue) {
+ if (self.options.autoResendUnfulfilledCommands) {
+ debug("resend %d unfulfilled commands", self.prevCommandQueue.length);
+ while (self.prevCommandQueue.length > 0) {
+ const item = self.prevCommandQueue.shift();
+ if (item.select !== self.condition.select &&
+ item.command.name !== "select") {
+ self.select(item.select);
+ }
+ self.sendCommand(item.command, item.stream);
+ }
+ }
+ else {
+ self.prevCommandQueue = null;
+ }
+ }
+ if (self.offlineQueue.length) {
+ debug("send %d commands in offline queue", self.offlineQueue.length);
+ const offlineQueue = self.offlineQueue;
+ self.resetOfflineQueue();
+ while (offlineQueue.length > 0) {
+ const item = offlineQueue.shift();
+ if (item.select !== self.condition.select &&
+ item.command.name !== "select") {
+ self.select(item.select);
+ }
+ self.sendCommand(item.command, item.stream);
+ }
+ }
+ if (self.condition.select !== finalSelect) {
+ debug("connect to db [%d]", finalSelect);
+ self.select(finalSelect);
+ }
+ };
+}
+exports.readyHandler = readyHandler;
diff --git a/node_modules/ioredis/built/tracing.d.ts b/node_modules/ioredis/built/tracing.d.ts
new file mode 100644
index 0000000..cff7c6b
--- /dev/null
+++ b/node_modules/ioredis/built/tracing.d.ts
@@ -0,0 +1,26 @@
+import type { CommandParameter } from "./types";
+export declare function sanitizeArgs(commandName: string, args: CommandParameter[]): string[];
+export interface CommandTraceContext {
+ command: string;
+ args: string[];
+ database: number;
+ serverAddress: string;
+ serverPort: number | undefined;
+}
+export interface BatchOperationContext {
+ batchMode: "MULTI";
+ batchSize: number;
+ database: number;
+ serverAddress: string;
+ serverPort: number | undefined;
+}
+export interface ConnectTraceContext {
+ serverAddress: string;
+ serverPort: number | undefined;
+ connectionEpoch: number;
+}
+declare type CommandContext = CommandTraceContext;
+export declare function traceCommand(fn: () => Promise, contextFactory: () => CommandContext): Promise;
+export declare function traceBatch(fn: () => Promise, contextFactory: () => BatchOperationContext): Promise;
+export declare function traceConnect(fn: () => Promise, contextFactory: () => ConnectTraceContext): Promise;
+export {};
diff --git a/node_modules/ioredis/built/tracing.js b/node_modules/ioredis/built/tracing.js
new file mode 100644
index 0000000..d402b2a
--- /dev/null
+++ b/node_modules/ioredis/built/tracing.js
@@ -0,0 +1,96 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.traceConnect = exports.traceBatch = exports.traceCommand = exports.sanitizeArgs = void 0;
+// Argument sanitization rules adapted from @opentelemetry/redis-common (Apache 2.0).
+// https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/packages/redis-common/src/index.ts
+//
+// Each entry specifies how many positional args (after the command name) are safe
+// to emit. -1 means all args are safe (read-only/structural commands).
+// Unlisted commands default to 0 (all args redacted) for safe-by-default behavior,
+// which covers AUTH, HELLO, and unknown/custom commands.
+const SERIALIZATION_SUBSETS = [
+ { regex: /^ECHO/i, args: 0 },
+ {
+ regex: /^(LPUSH|MSET|PFA|PUBLISH|RPUSH|SADD|SET|SPUBLISH|XADD|ZADD)/i,
+ args: 1,
+ },
+ { regex: /^(HSET|HMSET|LSET|LINSERT)/i, args: 2 },
+ {
+ regex: /^(ACL|BIT|B[LRZ]|CLIENT|CLUSTER|CONFIG|COMMAND|DECR|DEL|EVAL|EX|FUNCTION|GEO|GET|HINCR|HMGET|HSCAN|INCR|L[TRLM]|MEMORY|P[EFISTU]|RPOP|S[CDIMORSU]|XACK|X[CDGILPRT]|Z[CDILMPRS])/i,
+ args: -1,
+ },
+];
+function sanitizeArgs(commandName, args) {
+ let allowedArgCount = 0;
+ for (const subset of SERIALIZATION_SUBSETS) {
+ if (subset.regex.test(commandName)) {
+ allowedArgCount = subset.args;
+ break;
+ }
+ }
+ if (allowedArgCount === -1) {
+ return args.map((a) => String(a));
+ }
+ const result = [];
+ for (let i = 0; i < args.length; i++) {
+ if (i < allowedArgCount) {
+ result.push(String(args[i]));
+ }
+ else {
+ result.push("?");
+ }
+ }
+ return result;
+}
+exports.sanitizeArgs = sanitizeArgs;
+// Load diagnostics_channel with Node 18 compatibility
+const dc = (() => {
+ try {
+ return "getBuiltinModule" in process
+ ? process.getBuiltinModule("node:diagnostics_channel")
+ : require("node:diagnostics_channel");
+ }
+ catch {
+ return undefined;
+ }
+})();
+const hasTracingChannel = dc && typeof dc.tracingChannel === "function";
+const commandChannel = hasTracingChannel
+ ? dc.tracingChannel("ioredis:command")
+ : undefined;
+const batchChannel = hasTracingChannel
+ ? dc.tracingChannel("ioredis:batch")
+ : undefined;
+const connectChannel = hasTracingChannel
+ ? dc.tracingChannel("ioredis:connect")
+ : undefined;
+function shouldTrace(channel) {
+ return !!channel && channel.hasSubscribers !== false;
+}
+const noop = () => { };
+function traceCommand(fn, contextFactory) {
+ if (!shouldTrace(commandChannel))
+ return fn();
+ // tracePromise returns a wrapper promise that re-rejects on error.
+ // Silence the wrapper to prevent unhandled rejections when callers
+ // (e.g. Pipeline) discard the return value. Callers that await this
+ // promise still see the rejection through their own .then() chain.
+ const traced = commandChannel.tracePromise(fn, contextFactory());
+ traced.catch(noop);
+ return traced;
+}
+exports.traceCommand = traceCommand;
+function traceBatch(fn, contextFactory) {
+ if (!shouldTrace(batchChannel))
+ return fn();
+ const traced = batchChannel.tracePromise(fn, contextFactory());
+ traced.catch(noop);
+ return traced;
+}
+exports.traceBatch = traceBatch;
+function traceConnect(fn, contextFactory) {
+ if (!shouldTrace(connectChannel))
+ return fn();
+ return connectChannel.tracePromise(fn, contextFactory());
+}
+exports.traceConnect = traceConnect;
diff --git a/node_modules/ioredis/built/transaction.d.ts b/node_modules/ioredis/built/transaction.d.ts
new file mode 100644
index 0000000..d499f25
--- /dev/null
+++ b/node_modules/ioredis/built/transaction.d.ts
@@ -0,0 +1,13 @@
+import { ChainableCommander } from "./utils/RedisCommander";
+export interface Transaction {
+ pipeline(commands?: unknown[][]): ChainableCommander;
+ multi(options: {
+ pipeline: false;
+ }): Promise<"OK">;
+ multi(): ChainableCommander;
+ multi(options: {
+ pipeline: true;
+ }): ChainableCommander;
+ multi(commands?: unknown[][]): ChainableCommander;
+}
+export declare function addTransactionSupport(redis: any): void;
diff --git a/node_modules/ioredis/built/transaction.js b/node_modules/ioredis/built/transaction.js
new file mode 100644
index 0000000..27892ec
--- /dev/null
+++ b/node_modules/ioredis/built/transaction.js
@@ -0,0 +1,100 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.addTransactionSupport = void 0;
+const utils_1 = require("./utils");
+const standard_as_callback_1 = require("standard-as-callback");
+const Pipeline_1 = require("./Pipeline");
+const tracing_1 = require("./tracing");
+function addTransactionSupport(redis) {
+ redis.pipeline = function (commands) {
+ const pipeline = new Pipeline_1.default(this);
+ if (Array.isArray(commands)) {
+ pipeline.addBatch(commands);
+ }
+ return pipeline;
+ };
+ const { multi } = redis;
+ redis.multi = function (commands, options) {
+ if (typeof options === "undefined" && !Array.isArray(commands)) {
+ options = commands;
+ commands = null;
+ }
+ if (options && options.pipeline === false) {
+ return multi.call(this);
+ }
+ const pipeline = new Pipeline_1.default(this);
+ // @ts-expect-error
+ pipeline.multi();
+ if (Array.isArray(commands)) {
+ pipeline.addBatch(commands);
+ }
+ const exec = pipeline.exec;
+ pipeline.exec = function (callback) {
+ // Wait for the cluster to be connected, since we need nodes information before continuing
+ if (this.isCluster && !this.redis.slots.length) {
+ if (this.redis.status === "wait")
+ this.redis.connect().catch(utils_1.noop);
+ return (0, standard_as_callback_1.default)(new Promise((resolve, reject) => {
+ this.redis.delayUntilReady((err) => {
+ if (err) {
+ reject(err);
+ return;
+ }
+ this.exec(pipeline).then(resolve, reject);
+ });
+ }), callback);
+ }
+ if (this._transactions > 0) {
+ exec.call(pipeline);
+ }
+ // Returns directly when the pipeline
+ // has been called multiple times (retries).
+ if (this.nodeifiedPromise) {
+ return exec.call(pipeline);
+ }
+ // Count only user commands (exclude MULTI/EXEC wrappers).
+ const batchSize = Math.max(pipeline.length - 2, 0);
+ const execAndUnwrap = () => exec.call(pipeline).then(function (result) {
+ const execResult = result[result.length - 1];
+ if (typeof execResult === "undefined") {
+ throw new Error("Pipeline cannot be used to send any commands when the `exec()` has been called on it.");
+ }
+ if (execResult[0]) {
+ execResult[0].previousErrors = [];
+ for (let i = 0; i < result.length - 1; ++i) {
+ if (result[i][0]) {
+ execResult[0].previousErrors.push(result[i][0]);
+ }
+ }
+ throw execResult[0];
+ }
+ return (0, utils_1.wrapMultiResult)(execResult[1]);
+ });
+ // Trace MULTI as a single batch operation on ioredis:batch.
+ const promise = "_buildBatchContext" in this.redis
+ ? (0, tracing_1.traceBatch)(execAndUnwrap, () => this.redis._buildBatchContext(batchSize))
+ : execAndUnwrap();
+ return (0, standard_as_callback_1.default)(promise, callback);
+ };
+ // @ts-expect-error
+ const { execBuffer } = pipeline;
+ // @ts-expect-error
+ pipeline.execBuffer = function (callback) {
+ if (this._transactions > 0) {
+ execBuffer.call(pipeline);
+ }
+ return pipeline.exec(callback);
+ };
+ return pipeline;
+ };
+ const { exec } = redis;
+ redis.exec = function (callback) {
+ return (0, standard_as_callback_1.default)(exec.call(this).then(function (results) {
+ if (Array.isArray(results)) {
+ results = (0, utils_1.wrapMultiResult)(results);
+ }
+ return results;
+ }), callback);
+ };
+}
+exports.addTransactionSupport = addTransactionSupport;
diff --git a/node_modules/ioredis/built/types.d.ts b/node_modules/ioredis/built/types.d.ts
new file mode 100644
index 0000000..e6d95a9
--- /dev/null
+++ b/node_modules/ioredis/built/types.d.ts
@@ -0,0 +1,33 @@
+///
+import { Socket } from "net";
+import { TLSSocket } from "tls";
+export declare type Callback = (err?: Error | null, result?: T) => void;
+export declare type NetStream = Socket | TLSSocket;
+export declare type CommandParameter = string | Buffer | number | any[];
+export interface Respondable {
+ name: string;
+ args: CommandParameter[];
+ resolve(result: any): void;
+ reject(error: Error): void;
+}
+export interface PipelineWriteableStream {
+ isPipeline: true;
+ write(data: string | Buffer): unknown;
+ destination: {
+ redis: {
+ stream: NetStream;
+ };
+ };
+}
+export declare type WriteableStream = NetStream | PipelineWriteableStream;
+export interface CommandItem {
+ command: Respondable;
+ stream: WriteableStream;
+ select: number;
+}
+export interface ScanStreamOptions {
+ match?: string;
+ type?: string;
+ count?: number;
+ noValues?: boolean;
+}
diff --git a/node_modules/ioredis/built/types.js b/node_modules/ioredis/built/types.js
new file mode 100644
index 0000000..c8ad2e5
--- /dev/null
+++ b/node_modules/ioredis/built/types.js
@@ -0,0 +1,2 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
diff --git a/node_modules/ioredis/built/utils/Commander.d.ts b/node_modules/ioredis/built/utils/Commander.d.ts
new file mode 100644
index 0000000..cbee525
--- /dev/null
+++ b/node_modules/ioredis/built/utils/Commander.d.ts
@@ -0,0 +1,50 @@
+import Command from "../Command";
+import { WriteableStream } from "../types";
+import RedisCommander, { ClientContext } from "./RedisCommander";
+export interface CommanderOptions {
+ keyPrefix?: string | undefined;
+ showFriendlyErrorStack?: boolean | undefined;
+}
+declare class Commander {
+ options: CommanderOptions;
+ /**
+ * @ignore
+ */
+ scriptsSet: {};
+ /**
+ * @ignore
+ */
+ addedBuiltinSet: Set;
+ /**
+ * Return supported builtin commands
+ */
+ getBuiltinCommands(): string[];
+ /**
+ * Create a builtin command
+ */
+ createBuiltinCommand(commandName: string): {
+ string: any;
+ buffer: any;
+ };
+ /**
+ * Create add builtin command
+ */
+ addBuiltinCommand(commandName: string): void;
+ /**
+ * Define a custom command using lua script
+ */
+ defineCommand(name: string, definition: {
+ lua: string;
+ numberOfKeys?: number;
+ readOnly?: boolean;
+ }): void;
+ /**
+ * @ignore
+ */
+ sendCommand(command: Command, stream?: WriteableStream, node?: unknown): unknown;
+}
+interface Commander extends RedisCommander {
+}
+export default Commander;
diff --git a/node_modules/ioredis/built/utils/Commander.js b/node_modules/ioredis/built/utils/Commander.js
new file mode 100644
index 0000000..31a9155
--- /dev/null
+++ b/node_modules/ioredis/built/utils/Commander.js
@@ -0,0 +1,117 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const commands_1 = require("@ioredis/commands");
+const autoPipelining_1 = require("../autoPipelining");
+const Command_1 = require("../Command");
+const Script_1 = require("../Script");
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+class Commander {
+ constructor() {
+ this.options = {};
+ /**
+ * @ignore
+ */
+ this.scriptsSet = {};
+ /**
+ * @ignore
+ */
+ this.addedBuiltinSet = new Set();
+ }
+ /**
+ * Return supported builtin commands
+ */
+ getBuiltinCommands() {
+ return commands.slice(0);
+ }
+ /**
+ * Create a builtin command
+ */
+ createBuiltinCommand(commandName) {
+ return {
+ string: generateFunction(null, commandName, "utf8"),
+ buffer: generateFunction(null, commandName, null),
+ };
+ }
+ /**
+ * Create add builtin command
+ */
+ addBuiltinCommand(commandName) {
+ this.addedBuiltinSet.add(commandName);
+ this[commandName] = generateFunction(commandName, commandName, "utf8");
+ this[commandName + "Buffer"] = generateFunction(commandName + "Buffer", commandName, null);
+ }
+ /**
+ * Define a custom command using lua script
+ */
+ defineCommand(name, definition) {
+ const script = new Script_1.default(definition.lua, definition.numberOfKeys, this.options.keyPrefix, definition.readOnly);
+ this.scriptsSet[name] = script;
+ this[name] = generateScriptingFunction(name, name, script, "utf8");
+ this[name + "Buffer"] = generateScriptingFunction(name + "Buffer", name, script, null);
+ }
+ /**
+ * @ignore
+ */
+ sendCommand(command, stream, node) {
+ throw new Error('"sendCommand" is not implemented');
+ }
+}
+const commands = commands_1.list.filter((command) => command !== "monitor");
+commands.push("sentinel");
+commands.forEach(function (commandName) {
+ Commander.prototype[commandName] = generateFunction(commandName, commandName, "utf8");
+ Commander.prototype[commandName + "Buffer"] = generateFunction(commandName + "Buffer", commandName, null);
+});
+Commander.prototype.call = generateFunction("call", "utf8");
+Commander.prototype.callBuffer = generateFunction("callBuffer", null);
+// @ts-expect-error
+Commander.prototype.send_command = Commander.prototype.call;
+function generateFunction(functionName, _commandName, _encoding) {
+ if (typeof _encoding === "undefined") {
+ _encoding = _commandName;
+ _commandName = null;
+ }
+ return function (...args) {
+ const commandName = (_commandName || args.shift());
+ let callback = args[args.length - 1];
+ if (typeof callback === "function") {
+ args.pop();
+ }
+ else {
+ callback = undefined;
+ }
+ const options = {
+ errorStack: this.options.showFriendlyErrorStack ? new Error() : undefined,
+ keyPrefix: this.options.keyPrefix,
+ replyEncoding: _encoding,
+ };
+ // No auto pipeline, use regular command sending
+ if (!(0, autoPipelining_1.shouldUseAutoPipelining)(this, functionName, commandName)) {
+ return this.sendCommand(
+ // @ts-expect-error
+ new Command_1.default(commandName, args, options, callback));
+ }
+ // Create a new pipeline and make sure it's scheduled
+ return (0, autoPipelining_1.executeWithAutoPipelining)(this, functionName, commandName,
+ // @ts-expect-error
+ args, callback);
+ };
+}
+function generateScriptingFunction(functionName, commandName, script, encoding) {
+ return function (...args) {
+ const callback = typeof args[args.length - 1] === "function" ? args.pop() : undefined;
+ const options = {
+ replyEncoding: encoding,
+ };
+ if (this.options.showFriendlyErrorStack) {
+ options.errorStack = new Error();
+ }
+ // No auto pipeline, use regular command sending
+ if (!(0, autoPipelining_1.shouldUseAutoPipelining)(this, functionName, commandName)) {
+ return script.execute(this, args, options, callback);
+ }
+ // Create a new pipeline and make sure it's scheduled
+ return (0, autoPipelining_1.executeWithAutoPipelining)(this, functionName, commandName, args, callback);
+ };
+}
+exports.default = Commander;
diff --git a/node_modules/ioredis/built/utils/RedisCommander.d.ts b/node_modules/ioredis/built/utils/RedisCommander.d.ts
new file mode 100644
index 0000000..d99eafe
--- /dev/null
+++ b/node_modules/ioredis/built/utils/RedisCommander.d.ts
@@ -0,0 +1,9728 @@
+/**
+ * This file is generated by @ioredis/interface-generator.
+ * Don't edit it manually. Instead, run `npm run generate` to update
+ * this file.
+ */
+///
+import { Callback } from "../types";
+export declare type RedisKey = string | Buffer;
+export declare type RedisValue = string | Buffer | number;
+export interface ResultTypes {
+ default: Promise;
+ pipeline: ChainableCommander;
+}
+export interface ChainableCommander extends RedisCommander<{
+ type: "pipeline";
+}> {
+ length: number;
+}
+export declare type ClientContext = {
+ type: keyof ResultTypes;
+};
+export declare type Result = ResultTypes[Context["type"]];
+interface RedisCommander {
+ /**
+ * Call arbitrary commands.
+ *
+ * `redis.call('set', 'foo', 'bar')` is the same as `redis.set('foo', 'bar')`,
+ * so the only case you need to use this method is when the command is not
+ * supported by ioredis.
+ *
+ * ```ts
+ * redis.call('set', 'foo', 'bar');
+ * redis.call('get', 'foo', (err, value) => {
+ * // value === 'bar'
+ * });
+ * ```
+ */
+ call(command: string, callback?: Callback): Result;
+ call(command: string, args: (string | Buffer | number)[], callback?: Callback): Result;
+ call(...args: [
+ command: string,
+ ...args: (string | Buffer | number)[],
+ callback: Callback
+ ]): Result;
+ call(...args: [command: string, ...args: (string | Buffer | number)[]]): Result;
+ callBuffer(command: string, callback?: Callback): Result;
+ callBuffer(command: string, args: (string | Buffer | number)[], callback?: Callback): Result;
+ callBuffer(...args: [
+ command: string,
+ ...args: (string | Buffer | number)[],
+ callback: Callback
+ ]): Result;
+ callBuffer(...args: [command: string, ...args: (string | Buffer | number)[]]): Result;
+ /**
+ * List the ACL categories or the commands inside a category
+ * - _group_: server
+ * - _complexity_: O(1) since the categories and commands are a fixed set.
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "CAT", callback?: Callback): Result;
+ acl(subcommand: "CAT", categoryname: string | Buffer, callback?: Callback): Result;
+ /**
+ * Remove the specified ACL users and the associated rules
+ * - _group_: server
+ * - _complexity_: O(1) amortized time considering the typical user.
+ * - _since_: 6.0.0
+ */
+ acl(...args: [
+ subcommand: "DELUSER",
+ ...usernames: (string | Buffer)[],
+ callback: Callback
+ ]): Result;
+ acl(...args: [subcommand: "DELUSER", ...usernames: (string | Buffer)[]]): Result;
+ /**
+ * Returns whether the user can execute the given command without executing the command.
+ * - _group_: server
+ * - _complexity_: O(1).
+ * - _since_: 7.0.0
+ */
+ acl(subcommand: "DRYRUN", username: string | Buffer, command: string | Buffer, callback?: Callback): Result;
+ aclBuffer(subcommand: "DRYRUN", username: string | Buffer, command: string | Buffer, callback?: Callback): Result;
+ acl(...args: [
+ subcommand: "DRYRUN",
+ username: string | Buffer,
+ command: string | Buffer,
+ ...args: (string | Buffer | number)[],
+ callback: Callback
+ ]): Result;
+ aclBuffer(...args: [
+ subcommand: "DRYRUN",
+ username: string | Buffer,
+ command: string | Buffer,
+ ...args: (string | Buffer | number)[],
+ callback: Callback
+ ]): Result;
+ acl(...args: [
+ subcommand: "DRYRUN",
+ username: string | Buffer,
+ command: string | Buffer,
+ ...args: (string | Buffer | number)[]
+ ]): Result;
+ aclBuffer(...args: [
+ subcommand: "DRYRUN",
+ username: string | Buffer,
+ command: string | Buffer,
+ ...args: (string | Buffer | number)[]
+ ]): Result;
+ /**
+ * Generate a pseudorandom secure password to use for ACL users
+ * - _group_: server
+ * - _complexity_: O(1)
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "GENPASS", callback?: Callback): Result;
+ aclBuffer(subcommand: "GENPASS", callback?: Callback): Result;
+ acl(subcommand: "GENPASS", bits: number | string, callback?: Callback): Result;
+ aclBuffer(subcommand: "GENPASS", bits: number | string, callback?: Callback): Result;
+ /**
+ * Get the rules for a specific ACL user
+ * - _group_: server
+ * - _complexity_: O(N). Where N is the number of password, command and pattern rules that the user has.
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "GETUSER", username: string | Buffer, callback?: Callback): Result;
+ aclBuffer(subcommand: "GETUSER", username: string | Buffer, callback?: Callback): Result;
+ /**
+ * Show helpful text about the different subcommands
+ * - _group_: server
+ * - _complexity_: O(1)
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "HELP", callback?: Callback): Result;
+ /**
+ * List the current ACL rules in ACL config file format
+ * - _group_: server
+ * - _complexity_: O(N). Where N is the number of configured users.
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "LIST", callback?: Callback): Result;
+ aclBuffer(subcommand: "LIST", callback?: Callback): Result;
+ /**
+ * Reload the ACLs from the configured ACL file
+ * - _group_: server
+ * - _complexity_: O(N). Where N is the number of configured users.
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "LOAD", callback?: Callback<"OK">): Result<"OK", Context>;
+ /**
+ * List latest events denied because of ACLs in place
+ * - _group_: server
+ * - _complexity_: O(N) with N being the number of entries shown.
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "LOG", callback?: Callback): Result;
+ acl(subcommand: "LOG", count: number | string, callback?: Callback): Result;
+ acl(subcommand: "LOG", reset: "RESET", callback?: Callback): Result;
+ /**
+ * Save the current ACL rules in the configured ACL file
+ * - _group_: server
+ * - _complexity_: O(N). Where N is the number of configured users.
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "SAVE", callback?: Callback<"OK">): Result<"OK", Context>;
+ /**
+ * Modify or create the rules for a specific ACL user
+ * - _group_: server
+ * - _complexity_: O(N). Where N is the number of rules provided.
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "SETUSER", username: string | Buffer, callback?: Callback<"OK">): Result<"OK", Context>;
+ acl(...args: [
+ subcommand: "SETUSER",
+ username: string | Buffer,
+ ...rules: (string | Buffer)[],
+ callback: Callback<"OK">
+ ]): Result<"OK", Context>;
+ acl(...args: [
+ subcommand: "SETUSER",
+ username: string | Buffer,
+ ...rules: (string | Buffer)[]
+ ]): Result<"OK", Context>;
+ /**
+ * List the username of all the configured ACL rules
+ * - _group_: server
+ * - _complexity_: O(N). Where N is the number of configured users.
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "USERS", callback?: Callback): Result;
+ aclBuffer(subcommand: "USERS", callback?: Callback): Result;
+ /**
+ * Return the name of the user associated to the current connection
+ * - _group_: server
+ * - _complexity_: O(1)
+ * - _since_: 6.0.0
+ */
+ acl(subcommand: "WHOAMI", callback?: Callback): Result;
+ aclBuffer(subcommand: "WHOAMI", callback?: Callback): Result;
+ /**
+ * Append a value to a key
+ * - _group_: string
+ * - _complexity_: O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.
+ * - _since_: 2.0.0
+ */
+ append(key: RedisKey, value: string | Buffer | number, callback?: Callback): Result;
+ /**
+ * Returns the number of non-empty elements in an array.
+ * - _group_: array
+ * - _complexity_: O(1)
+ * - _since_: 8.8.0
+ */
+ arcount(key: RedisKey, callback?: Callback): Result;
+ /**
+ * Deletes elements at the specified indices in an array.
+ * - _group_: array
+ * - _complexity_: O(N) where N is the number of indices to delete
+ * - _since_: 8.8.0
+ */
+ ardel(...args: [key: RedisKey, ...indices: (number | string)[], callback: Callback]): Result;
+ ardel(...args: [key: RedisKey, ...indices: (number | string)[]]): Result;
+ /**
+ * Deletes elements in one or more ranges.
+ * - _group_: array
+ * - _complexity_: Proportional to the number of existing elements / slices touched, not to the numeric span of the requested ranges
+ * - _since_: 8.8.0
+ */
+ ardelrange(...args: [key: RedisKey, ...ranges: (string | number)[], callback: Callback]): Result;
+ ardelrange(...args: [key: RedisKey, ...ranges: (string | number)[]]): Result;
+ /**
+ * Gets the value at an index in an array.
+ * - _group_: array
+ * - _complexity_: O(1)
+ * - _since_: 8.8.0
+ */
+ arget(key: RedisKey, index: number | string, callback?: Callback): Result;
+ argetBuffer(key: RedisKey, index: number | string, callback?: Callback): Result;
+ /**
+ * Gets values in a range of indices.
+ * - _group_: array
+ * - _complexity_: O(N) where N is the range length
+ * - _since_: 8.8.0
+ */
+ argetrange(key: RedisKey, start: number | string, end: number | string, callback?: Callback<(string | null)[]>): Result<(string | null)[], Context>;
+ argetrangeBuffer(key: RedisKey, start: number | string, end: number | string, callback?: Callback<(Buffer | null)[]>): Result<(Buffer | null)[], Context>;
+ /**
+ * Searches array elements in a range using textual predicates.
+ * - _group_: array
+ * - _complexity_: O(P * C) where P is the number of visited positions in touched slices and C is the cost of evaluating the predicates on one existing element.
+ * - _since_: 8.8.0
+ */
+ argrep(key: RedisKey, start: number | string, end: number | string, predicate: 'EXACT' | 'MATCH' | 'GLOB' | 'RE', value: RedisValue, callback: Callback): Result;
+ argrep(...args: [key: RedisKey, start: number | string, end: number | string, predicate: 'EXACT' | 'MATCH' | 'GLOB' | 'RE', value: RedisValue, ...args: RedisValue[], callback: Callback>]): Result, Context>;
+ argrep(...args: [key: RedisKey, start: number | string, end: number | string, predicate: 'EXACT' | 'MATCH' | 'GLOB' | 'RE', value: RedisValue, ...args: T]): Result<'WITHVALUES' extends T[number] ? Array<[index: number, value: string]> : number[], Context>;
+ argrepBuffer(key: RedisKey, start: number | string, end: number | string, predicate: 'EXACT' | 'MATCH' | 'GLOB' | 'RE', value: RedisValue, callback: Callback): Result;
+ argrepBuffer(...args: [key: RedisKey, start: number | string, end: number | string, predicate: 'EXACT' | 'MATCH' | 'GLOB' | 'RE', value: RedisValue, ...args: RedisValue[], callback: Callback>]): Result, Context>;
+ argrepBuffer(...args: [key: RedisKey, start: number | string, end: number | string, predicate: 'EXACT' | 'MATCH' | 'GLOB' | 'RE', value: RedisValue, ...args: T]): Result<'WITHVALUES' extends T[number] ? Array<[index: number, value: Buffer]> : number[], Context>;
+ /**
+ * Returns metadata about an array.
+ * - _group_: array
+ * - _complexity_: O(1), or O(N) with FULL option where N is the number of slices.
+ * - _since_: 8.8.0
+ */
+ arinfo(key: RedisKey, callback?: Callback<(string | number)[]>): Result<(string | number)[], Context>;
+ arinfoBuffer(key: RedisKey, callback?: Callback<(Buffer | number)[]>): Result<(Buffer | number)[], Context>;
+ arinfo(key: RedisKey, full: 'FULL', callback?: Callback<(string | number)[]>): Result<(string | number)[], Context>;
+ arinfoBuffer(key: RedisKey, full: 'FULL', callback?: Callback<(Buffer | number)[]>): Result<(Buffer | number)[], Context>;
+ /**
+ * Inserts one or more values at consecutive indices.
+ * - _group_: array
+ * - _complexity_: O(N) where N is the number of values
+ * - _since_: 8.8.0
+ */
+ arinsert(...args: [key: RedisKey, ...values: (string | Buffer | number)[], callback: Callback]): Result;
+ arinsert(...args: [key: RedisKey, ...values: (string | Buffer | number)[]]): Result;
+ /**
+ * Returns the most recently inserted elements.
+ * - _group_: array
+ * - _complexity_: O(N) where N is the count
+ * - _since_: 8.8.0
+ */
+ arlastitems(key: RedisKey, count: number | string, callback?: Callback<(string | null)[]>): Result<(string | null)[], Context>;
+ arlastitemsBuffer(key: RedisKey, count: number | string, callback?: Callback<(Buffer | null)[]>): Result<(Buffer | null)[], Context>;
+ arlastitems(key: RedisKey, count: number | string, rev: 'REV', callback?: Callback<(string | null)[]>): Result<(string | null)[], Context>;
+ arlastitemsBuffer(key: RedisKey, count: number | string, rev: 'REV', callback?: Callback<(Buffer | null)[]>): Result<(Buffer | null)[], Context>;
+ /**
+ * Returns the length of an array (max index + 1).
+ * - _group_: array
+ * - _complexity_: O(1)
+ * - _since_: 8.8.0
+ */
+ arlen(key: RedisKey, callback?: Callback): Result;
+ /**
+ * Gets values at multiple indices in an array.
+ * - _group_: array
+ * - _complexity_: O(N) where N is the number of indices
+ * - _since_: 8.8.0
+ */
+ armget(...args: [key: RedisKey, ...indices: (number | string)[], callback: Callback<(string | null)[]>]): Result<(string | null)[], Context>;
+ armgetBuffer(...args: [key: RedisKey, ...indices: (number | string)[], callback: Callback<(Buffer | null)[]>]): Result<(Buffer | null)[], Context>;
+ armget(...args: [key: RedisKey, ...indices: (number | string)[]]): Result<(string | null)[], Context>;
+ armgetBuffer(...args: [key: RedisKey, ...indices: (number | string)[]]): Result<(Buffer | null)[], Context>;
+ /**
+ * Sets multiple index-value pairs in an array.
+ * - _group_: array
+ * - _complexity_: O(N) where N is the number of pairs
+ * - _since_: 8.8.0
+ */
+ armset(...args: [key: RedisKey, ...data: (string | Buffer | number)[], callback: Callback]): Result;
+ armset(...args: [key: RedisKey, ...data: (string | Buffer | number)[]]): Result;
+ /**
+ * Returns the next index ARINSERT would use.
+ * - _group_: array
+ * - _complexity_: O(1)
+ * - _since_: 8.8.0
+ */
+ arnext(key: RedisKey, callback?: Callback): Result;
+ /**
+ * Performs aggregate operations on array elements in a range.
+ * - _group_: array
+ * - _complexity_: O(P) where P is visited positions in touched slices (dense scanned slots + sparse entries), with worst-case O(|end-start|+1) and typical case close to O(N), where N is the number of existing elements in range.
+ * - _since_: 8.8.0
+ */
+ arop(key: RedisKey, start: number | string, end: number | string, sum: 'SUM', callback?: Callback): Result;
+ aropBuffer(key: RedisKey, start: number | string, end: number | string, sum: 'SUM', callback?: Callback): Result;
+ arop(key: RedisKey, start: number | string, end: number | string, min: 'MIN', callback?: Callback): Result;
+ aropBuffer(key: RedisKey, start: number | string, end: number | string, min: 'MIN', callback?: Callback): Result;
+ arop(key: RedisKey, start: number | string, end: number | string, max: 'MAX', callback?: Callback): Result;
+ aropBuffer(key: RedisKey, start: number | string, end: number | string, max: 'MAX', callback?: Callback): Result;
+ arop(key: RedisKey, start: number | string, end: number | string, and: 'AND', callback?: Callback): Result;
+ arop(key: RedisKey, start: number | string, end: number | string, or: 'OR', callback?: Callback): Result;
+ arop(key: RedisKey, start: number | string, end: number | string, xor: 'XOR', callback?: Callback): Result;
+ arop(key: RedisKey, start: number | string, end: number | string, match: 'MATCH', value: string | Buffer | number, callback?: Callback): Result;
+ arop(key: RedisKey, start: number | string, end: number | string, used: 'USED', callback?: Callback): Result;
+ /**
+ * Inserts values into a ring buffer of specified size, wrapping and truncating as needed.
+ * - _group_: array
+ * - _complexity_: O(M) normally, O(N+M) on ring resize, where N is the maximum of the old and new ring size and M is the number of inserted values
+ * - _since_: 8.8.0
+ */
+ arring(...args: [key: RedisKey, size: number | string, ...values: (string | Buffer | number)[], callback: Callback]): Result;
+ arring(...args: [key: RedisKey, size: number | string, ...values: (string | Buffer | number)[]]): Result;
+ /**
+ * Iterates existing elements in a range, returning index-value pairs.
+ * - _group_: array
+ * - _complexity_: O(P) where P is visited positions in touched slices (dense scanned slots + sparse entries), with worst-case O(|end-start|+1) and typical case close to O(N), where N is the number of existing elements in range.
+ * - _since_: 8.8.0
+ */
+ arscan(key: RedisKey, start: number | string, end: number | string, callback?: Callback>): Result, Context>;
+ arscanBuffer(key: RedisKey, start: number | string, end: number | string, callback?: Callback>): Result, Context>;
+ arscan(key: RedisKey, start: number | string, end: number | string, limitToken: 'LIMIT', limit: number | string, callback?: Callback>): Result, Context>;
+ arscanBuffer(key: RedisKey, start: number | string, end: number | string, limitToken: 'LIMIT', limit: number | string, callback?: Callback>): Result, Context>;
+ /**
+ * Sets the ARINSERT / ARRING cursor to a specific index.
+ * - _group_: array
+ * - _complexity_: O(1)
+ * - _since_: 8.8.0
+ */
+ arseek(key: RedisKey, index: number | string, callback?: Callback): Result;
+ /**
+ * Sets one or more contiguous values starting at an index in an array.
+ * - _group_: array
+ * - _complexity_: O(N) where N is the number of values
+ * - _since_: 8.8.0
+ */
+ arset(...args: [key: RedisKey, index: number | string, ...values: (string | Buffer | number)[], callback: Callback]): Result;
+ arset(...args: [key: RedisKey, index: number | string, ...values: (string | Buffer | number)[]]): Result;
+ /**
+ * Signals that a cluster client is following an -ASK redirect.
+ * - _group_: cluster
+ * - _complexity_: O(1)
+ * - _since_: 3.0.0
+ */
+ asking(callback?: Callback<"OK">): Result<"OK", Context>;
+ /**
+ * Authenticate to the server
+ * - _group_: connection
+ * - _complexity_: O(N) where N is the number of passwords defined for the user
+ * - _since_: 1.0.0
+ */
+ auth(password: string | Buffer, callback?: Callback<"OK">): Result<"OK", Context>;
+ auth(username: string | Buffer, password: string | Buffer, callback?: Callback<"OK">): Result<"OK", Context>;
+ /**
+ * Asynchronously rewrite the append-only file
+ * - _group_: server
+ * - _complexity_: O(1)
+ * - _since_: 1.0.0
+ */
+ bgrewriteaof(callback?: Callback): Result;
+ bgrewriteaofBuffer(callback?: Callback): Result;
+ /**
+ * Asynchronously save the dataset to disk
+ * - _group_: server
+ * - _complexity_: O(1)
+ * - _since_: 1.0.0
+ */
+ bgsave(callback?: Callback<"OK">): Result<"OK", Context>;
+ bgsave(schedule: "SCHEDULE", callback?: Callback<"OK">): Result<"OK", Context>;
+ /**
+ * Count set bits in a string
+ * - _group_: bitmap
+ * - _complexity_: O(N)
+ * - _since_: 2.6.0
+ */
+ bitcount(key: RedisKey, callback?: Callback): Result;
+ bitcount(key: RedisKey, start: number | string, end: number | string, callback?: Callback): Result;
+ bitcount(key: RedisKey, start: number | string, end: number | string, byte: "BYTE", callback?: Callback): Result;
+ bitcount(key: RedisKey, start: number | string, end: number | string, bit: "BIT", callback?: Callback): Result;
+ /**
+ * Perform arbitrary bitfield integer operations on strings
+ * - _group_: bitmap
+ * - _complexity_: O(1) for each subcommand specified
+ * - _since_: 3.2.0
+ */
+ bitfield(key: RedisKey, encodingOffsetToken: "GET", encoding: string | Buffer, offset: number | string, callback?: Callback): Result;
+ bitfield(key: RedisKey, encodingOffsetValueToken: "SET", encoding: string | Buffer, offset: number | string, value: number | string, callback?: Callback