tiny-essentials 1.25.4 → 1.25.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Internal helper to validate an array of FuzzySets.
3
+ * @param {FuzzySet[]} sets
4
+ */
5
+ const validateFuzzySets = (sets) => {
6
+ if (!Array.isArray(sets)) {
7
+ throw new TypeError("Parameter 'sets' must be an array.");
8
+ }
9
+ if (!sets.every((set) => set instanceof FuzzySet)) {
10
+ throw new TypeError('All elements in the array must be instances of FuzzySet.');
11
+ }
12
+ };
13
+ /**
14
+ * Performs defuzzification using the Centroid (Center of Gravity) method.
15
+ * @param {Object.<string, number>} fuzzyOutput - Results from rule evaluation.
16
+ * @param {FuzzySet[]} outputSets - The sets defining the output range.
17
+ * @param {number} [step=0.5] - Resolution of the integral approximation.
18
+ * @returns {number} The crisp output value.
19
+ */
20
+ export const defuzzifyCentroid = (fuzzyOutput, outputSets, step = 0.5) => {
21
+ validateType(fuzzyOutput, 'object', 'defuzzifyCentroid.fuzzyOutput');
22
+ for (const outputName in fuzzyOutput) {
23
+ validateType(fuzzyOutput[outputName], 'number', `fuzzyOutput['${outputName}']`);
24
+ }
25
+ validateFuzzySets(outputSets);
26
+ validateType(step, 'number', 'defuzzifyCentroid.step');
27
+ /** @type {number} - Accumulated weighted area for centroid */
28
+ let totalAreaWeighted = 0;
29
+ /** @type {number} - Accumulated total area */
30
+ let totalArea = 0;
31
+ // Numerical integration (Centroid approximation)
32
+ for (let i = 0; i <= 100; i += step) {
33
+ /** @type {number} - Maximum membership found at point x(i) */
34
+ let maxMembershipAtX = 0;
35
+ outputSets.forEach((set) => {
36
+ /** @type {number} - Rule strength applied to the output set */
37
+ const strength = fuzzyOutput[set.name] || 0;
38
+ /** @type {number} - Cut or scale the output set membership */
39
+ const membership = Math.min(strength, set.calculate(i));
40
+ maxMembershipAtX = Math.max(maxMembershipAtX, membership);
41
+ });
42
+ totalAreaWeighted += i * maxMembershipAtX;
43
+ totalArea += maxMembershipAtX;
44
+ }
45
+ return totalArea === 0 ? 0 : totalAreaWeighted / totalArea;
46
+ };
47
+ /**
48
+ * Utility to calculate fuzzy membership using a trapezoidal shape safely.
49
+ * @param {number} value - The input value to check.
50
+ * @param {number} a - Start of the rise.
51
+ * @param {number} b - End of the rise (start of plateau).
52
+ * @param {number} c - Start of the fall (end of plateau).
53
+ * @param {number} d - End of the fall.
54
+ * @param {boolean} [optimize=false] - Optimizes performance by skipping math for absolute bounds.
55
+ * @returns {number} Degree of membership [0, 1].
56
+ */
57
+ export const trapezoid = (value, a, b, c, d, optimize = false) => {
58
+ if (optimize) {
59
+ // If the value is completely outside the outer bounds, return 0 immediately
60
+ if (value <= a || value >= d)
61
+ return 0;
62
+ // If the value is entirely within the plateau, return 1 immediately
63
+ if (value >= b && value <= c)
64
+ return 1;
65
+ }
66
+ /** @type {number} - Safely calculate rising slope */
67
+ const rise = a === b ? 1 : (value - a) / (b - a);
68
+ /** @type {number} - Safely calculate falling slope */
69
+ const fall = c === d ? 1 : (d - value) / (d - c);
70
+ /** @type {number} - Internal value clamping between 0 and 1 */
71
+ const membership = Math.max(0, Math.min(rise, 1, fall));
72
+ return isNaN(membership) ? 0 : membership;
73
+ };
74
+ /**
75
+ * Utility to validate types and throw formatted errors, preventing code repetition.
76
+ * @param {any} value - The value to evaluate.
77
+ * @param {string} expectedType - The expected data type.
78
+ * @param {string} paramName - The name of the parameter for the error message.
79
+ */
80
+ const validateType = (value, expectedType, paramName) => {
81
+ if (typeof value !== expectedType) {
82
+ throw new TypeError(`Parameter '${paramName}' must be a ${expectedType}.`);
83
+ }
84
+ };
85
+ /**
86
+ * Represents a single Membership Function (Trapezoidal).
87
+ */
88
+ class FuzzySet {
89
+ /**
90
+ * Utility to calculate fuzzy membership using a trapezoidal shape.
91
+ * @param {number} value - The input value to check.
92
+ * @param {number} a - Start of the rise.
93
+ * @param {number} b - End of the rise (start of plateau).
94
+ * @param {number} c - Start of the fall (end of plateau).
95
+ * @param {number} d - End of the fall.
96
+ * @param {boolean} [optimize=false] - Performance optimization flag.
97
+ * @returns {number} Degree of membership [0, 1].
98
+ */
99
+ static trapezoid(value, a, b, c, d, optimize = false) {
100
+ return trapezoid(value, a, b, c, d, optimize);
101
+ }
102
+ /** @type {string} - Internal name of the fuzzy set */
103
+ #name = '';
104
+ /** @type {number} - Internal left foot coordinate */
105
+ #a = 0;
106
+ /** @type {number} - Internal left shoulder coordinate */
107
+ #b = 0;
108
+ /** @type {number} - Internal right shoulder coordinate */
109
+ #c = 0;
110
+ /** @type {number} - Internal right foot coordinate */
111
+ #d = 0;
112
+ /** @type {boolean} - Internal flag to enable calculation optimization */
113
+ #optimize = false;
114
+ get name() {
115
+ return this.#name;
116
+ }
117
+ set name(value) {
118
+ validateType(value, 'string', 'FuzzySet.name');
119
+ this.#name = value;
120
+ }
121
+ get a() {
122
+ return this.#a;
123
+ }
124
+ set a(value) {
125
+ validateType(value, 'number', 'FuzzySet.a');
126
+ this.#a = value;
127
+ }
128
+ get b() {
129
+ return this.#b;
130
+ }
131
+ set b(value) {
132
+ validateType(value, 'number', 'FuzzySet.b');
133
+ this.#b = value;
134
+ }
135
+ get c() {
136
+ return this.#c;
137
+ }
138
+ set c(value) {
139
+ validateType(value, 'number', 'FuzzySet.c');
140
+ this.#c = value;
141
+ }
142
+ get d() {
143
+ return this.#d;
144
+ }
145
+ set d(value) {
146
+ validateType(value, 'number', 'FuzzySet.d');
147
+ this.#d = value;
148
+ }
149
+ get optimize() {
150
+ return this.#optimize;
151
+ }
152
+ set optimize(value) {
153
+ validateType(value, 'boolean', 'FuzzySet.optimize');
154
+ this.#optimize = value;
155
+ }
156
+ /**
157
+ * @param {string} name - Name of the set (e.g., "Hot").
158
+ * @param {number} a - Left foot.
159
+ * @param {number} b - Left shoulder.
160
+ * @param {number} c - Right shoulder.
161
+ * @param {number} d - Right feet.
162
+ * @param {boolean} [optimize=false] - Enables performance optimization.
163
+ */
164
+ constructor(name, a, b, c, d, optimize = false) {
165
+ this.name = name;
166
+ this.a = a;
167
+ this.b = b;
168
+ this.c = c;
169
+ this.d = d;
170
+ this.optimize = optimize;
171
+ }
172
+ /**
173
+ * Calculates the membership degree.
174
+ * @param {number} x - Crisp input.
175
+ * @returns {number}
176
+ */
177
+ calculate(x) {
178
+ validateType(x, 'number', 'calculate.x');
179
+ return FuzzySet.trapezoid(x, this.#a, this.#b, this.#c, this.#d, this.#optimize);
180
+ }
181
+ }
182
+ /**
183
+ * The Inference Engine handles linguistic variables and defuzzification.
184
+ */
185
+ class MamdaniInferenceSystem {
186
+ /** @type {Map<string, FuzzySet[]>} - Storage for linguistic variables */
187
+ #variables = new Map();
188
+ /**
189
+ * Registers a linguistic variable and its sets.
190
+ * @param {string} name - Variable name (e.g., "temperature").
191
+ * @param {FuzzySet[]} sets - Array of fuzzy sets.
192
+ */
193
+ addVariable(name, sets) {
194
+ validateType(name, 'string', 'addVariable.name');
195
+ validateFuzzySets(sets);
196
+ this.#variables.set(name, sets);
197
+ }
198
+ /**
199
+ * Removes a linguistic variable.
200
+ * @param {string} name - Variable name (e.g., "temperature").
201
+ * @returns {boolean} True if the element was successfully removed.
202
+ */
203
+ removeVariable(name) {
204
+ validateType(name, 'string', 'removeVariable.name');
205
+ return this.#variables.delete(name);
206
+ }
207
+ /**
208
+ * Gets a linguistic variable and its sets.
209
+ * @param {string} name - Variable name (e.g., "temperature").
210
+ * @returns {FuzzySet[]}
211
+ */
212
+ getVariable(name) {
213
+ validateType(name, 'string', 'getVariable.name');
214
+ /** @type {FuzzySet[] | undefined} - Attempted fetch from map */
215
+ const result = this.#variables.get(name);
216
+ if (!result)
217
+ throw new Error(`Linguistic variable '${name}' not found in the inference system.`);
218
+ return [...result];
219
+ }
220
+ /**
221
+ * Checks if a linguistic variable exists.
222
+ * @param {string} name - Variable name (e.g., "temperature").
223
+ * @returns {boolean}
224
+ */
225
+ hasVariable(name) {
226
+ validateType(name, 'string', 'hasVariable.name');
227
+ return this.#variables.has(name);
228
+ }
229
+ /**
230
+ * Fuzzifies a crisp input into a map of memberships.
231
+ * @param {string} varName
232
+ * @param {number} value
233
+ * @returns {Object.<string, number>}
234
+ */
235
+ fuzzify(varName, value) {
236
+ validateType(varName, 'string', 'fuzzify.varName');
237
+ validateType(value, 'number', 'fuzzify.value');
238
+ /** @type {FuzzySet[]} - The sets associated with the variable */
239
+ const sets = this.#variables.get(varName) || [];
240
+ /** @type {Object.<string, number>} - The fuzzified results dictionary */
241
+ const results = {};
242
+ sets.forEach((set) => {
243
+ results[set.name] = set.calculate(value);
244
+ });
245
+ return results;
246
+ }
247
+ }
248
+ export { FuzzySet, MamdaniInferenceSystem };
package/docs/v1/README.md CHANGED
@@ -63,6 +63,7 @@ This folder contains the core scripts we have worked on so far. Each file is a m
63
63
  * 📝 **[TinyTextDiffer](./libs/TinyTextDiffer.md)** — A granular text comparison utility using the LCS algorithm to detect additions, deletions, and unchanged segments between multiple history versions, returning a clean, parseable diff structure.
64
64
  * 🕒 **[TinyAnalogClock](./libs/TinyAnalogClock.md)** — A lightweight analog clock engine for managing time-based rotations, supporting custom offsets, smooth transitions, and easy binding to CSS variables rendering.
65
65
  * 🔍 **[TinyArrayComparator](./libs/TinyArrayComparator.md)** — A lightweight, highly optimized JavaScript utility class designed to compare two arrays and efficiently detect which items were **added** or **deleted**.
66
+ * 🧠 **[TinyMamdaniInferenceSystem](./libs/TinyMamdaniInferenceSystem.md)** — A implementation of a Mamdani Inference System, allowing you to model logic using trapezoidal membership functions.
66
67
 
67
68
  ### 3. **`fileManager/`**
68
69
  * 📁 **[Main](./fileManager/main.md)** — A Node.js file/directory utility module with support for JSON, backups, renaming, size analysis, and more.
@@ -0,0 +1,105 @@
1
+ # 🧠 Fuzzy Logic Engine Documentation
2
+
3
+ Welcome to the official documentation for the **Fuzzy Logic Engine**! 🚀 This robust, type-safe JavaScript module provides a implementation of a Mamdani Inference System, allowing you to model logic using trapezoidal membership functions.
4
+
5
+ ---
6
+
7
+ ## 🛠️ Core Utilities
8
+
9
+ ### 📐 `trapezoid(value, a, b, c, d, optimize = false)`
10
+ A high-performance utility to safely calculate the fuzzy membership degree using a trapezoidal shape. It includes built-in protections against division by zero and short-circuit optimizations.
11
+
12
+ **Parameters:**
13
+ | Parameter | Type | Description |
14
+ | :--- | :--- | :--- |
15
+ | `value` | `number` | The crisp input value to check. |
16
+ | `a` | `number` | Start of the rise (membership = 0). |
17
+ | `b` | `number` | End of the rise / start of plateau (membership = 1). |
18
+ | `c` | `number` | Start of the fall / end of plateau (membership = 1). |
19
+ | `d` | `number` | End of the fall (membership = 0). |
20
+ | `optimize` | `boolean`, optional | Enables performance optimization by skipping math for absolute bounds. Default is `false`. |
21
+
22
+ **Returns:** * `number` - The degree of membership, safely clamped between `[0, 1]`.
23
+
24
+ ---
25
+
26
+ ### 🎯 `defuzzifyCentroid(fuzzyOutput, outputSets, step = 0.5)`
27
+ Performs defuzzification using the highly accurate Centroid (Center of Gravity) numerical integration method.
28
+
29
+ * **Parameters:**
30
+
31
+ | Parameter | Type | Description |
32
+ | :--- | :--- | :--- |
33
+ | `fuzzyOutput` | `Object.<string, number>` | The evaluated rule strengths (e.g., `{"High": 0.8, "Medium": 0.2}`). |
34
+ | `outputSets` | `FuzzySet[]` | The array of sets defining the output spectrum. |
35
+ | `step` | `number`, optional | Resolution of the integral approximation. Default is `0.5`. |
36
+
37
+ * **Returns:** `number` - The final, precise crisp output value.
38
+
39
+ ---
40
+
41
+ ## 🏗️ Classes
42
+
43
+ ### 🟦 `FuzzySet`
44
+ Represents a single linguistic term (e.g., "Cold", "High", "Severe") defined by a trapezoidal membership function. It includes strict type validations for all its properties.
45
+
46
+ #### ⚙️ Constructor
47
+ ```javascript
48
+ new FuzzySet(name, a, b, c, d, optimize = false)
49
+ ```
50
+
51
+ #### 📦 Properties
52
+ * **`name`** (`string`): The name of the fuzzy set.
53
+ * **`a`, `b`, `c`, `d`** (`number`): The coordinates defining the trapezoidal shape.
54
+ * **`optimize`** (`boolean`): Internal flag to enable calculation optimization for absolute bounds.
55
+
56
+ #### 🧮 Methods
57
+ * **`calculate(x)`**
58
+ Calculates the membership degree for a specific input using the set's coordinates and optimization flag.
59
+ * **Parameters:** `x` (`number`) - The crisp input value.
60
+ * **Returns:** `number` - Degree of membership `[0, 1]`.
61
+
62
+ * **`static trapezoid(value, a, b, c, d, optimize = false)`**
63
+ Static wrapper for the global `trapezoid` utility.
64
+
65
+ ---
66
+
67
+ ### 🧠 `MamdaniInferenceSystem`
68
+ The core engine that handles the storage of linguistic variables, performs fuzzification, evaluates rules, and calculates the final crisp output via defuzzification.
69
+
70
+ #### ⚙️ Constructor
71
+ ```javascript
72
+ const engine = new MamdaniInferenceSystem();
73
+ ```
74
+
75
+ #### 🧮 Variable Management Methods
76
+
77
+ * **`addVariable(name, sets)`** ➕
78
+ Registers a new linguistic variable and its associated fuzzy sets.
79
+ * **Parameters:** * `name` (`string`) - The variable's name (e.g., "temperature").
80
+ * `sets` (`FuzzySet[]`) - An array of `FuzzySet` instances.
81
+
82
+ * **`removeVariable(name)`** 🗑️
83
+ Deletes a linguistic variable from the engine.
84
+ * **Parameters:** `name` (`string`) - The variable's name.
85
+ * **Returns:** `boolean` - `true` if successfully removed.
86
+
87
+ * **`getVariable(name)`** 🔍
88
+ Retrieves a cloned array of the fuzzy sets for a specific variable.
89
+ * **Parameters:** `name` (`string`) - The variable's name.
90
+ * **Returns:** `FuzzySet[]`
91
+ * **Throws:** `Error` if the variable is not found.
92
+
93
+ * **`hasVariable(name)`** ❓
94
+ Checks if a variable is registered in the engine.
95
+ * **Parameters:** `name` (`string`) - The variable's name.
96
+ * **Returns:** `boolean`
97
+
98
+ #### 🔬 Logic Processing Methods
99
+
100
+ * **`fuzzify(varName, value)`** 🌫️
101
+ Converts a crisp numeric input into a dictionary of fuzzy membership degrees based on the variable's sets.
102
+ * **Parameters:**
103
+ * `varName` (`string`) - The variable to evaluate.
104
+ * `value` (`number`) - The crisp input value.
105
+ * **Returns:** `Object.<string, number>` - A map of set names to their membership degrees.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-essentials",
3
- "version": "1.25.4",
3
+ "version": "1.25.6",
4
4
  "description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
5
5
  "scripts": {
6
6
  "test": "npm run test:mjs && npm run test:cjs && npm run test:js",
@@ -264,6 +264,10 @@
264
264
  "require": "./dist/v1/libs/TinyAdvancedRaffle.cjs",
265
265
  "import": "./dist/v1/libs/TinyAdvancedRaffle.mjs"
266
266
  },
267
+ "./libs/TinyMamdaniInferenceSystem": {
268
+ "require": "./dist/v1/libs/TinyMamdaniInferenceSystem.cjs",
269
+ "import": "./dist/v1/libs/TinyMamdaniInferenceSystem.mjs"
270
+ },
267
271
  "./libs/TinyHtmlElems": {
268
272
  "require": "./dist/v1/libs/TinyHtml/index.cjs",
269
273
  "import": "./dist/v1/libs/TinyHtml/index.mjs"