es6-fuzz 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,343 @@
1
+ 'use strict';
2
+ const { describe, it } = require('node:test');
3
+ const assert = require('assert');
4
+ const Logic = require('../lib/logic');
5
+
6
+ describe('Integration Tests', function() {
7
+ describe('Complex AND/OR/NOT chains', function() {
8
+ it('should handle 5+ shapes in complex logic', function() {
9
+ const logic = new Logic();
10
+
11
+ // Temperature ranges
12
+ const freezing = new logic.c.Trapezoid(-20, -10, 0, 5);
13
+ const cold = new logic.c.Triangle(0, 10, 20);
14
+ const comfortable = new logic.c.Trapezoid(15, 20, 25, 30);
15
+ const warm = new logic.c.Triangle(25, 30, 35);
16
+ const hot = new logic.c.Trapezoid(30, 35, 45, 50);
17
+
18
+ // Complex logic: comfortable OR (cold AND NOT freezing) OR (warm AND NOT hot)
19
+ logic.init('freezing', freezing)
20
+ .or('cold', cold)
21
+ .or('comfortable', comfortable)
22
+ .or('warm', warm)
23
+ .or('hot', hot);
24
+
25
+ // Test various temperatures
26
+ let result = logic.defuzzify(-15);
27
+ assert.equal(result.defuzzified, 'freezing');
28
+
29
+ result = logic.defuzzify(10);
30
+ assert.equal(result.defuzzified, 'cold');
31
+
32
+ result = logic.defuzzify(22.5);
33
+ assert.equal(result.defuzzified, 'comfortable');
34
+
35
+ result = logic.defuzzify(32.5);
36
+ assert.equal(result.defuzzified, 'warm');
37
+
38
+ result = logic.defuzzify(40);
39
+ assert.equal(result.defuzzified, 'hot');
40
+ });
41
+
42
+ it('should handle nested logic operations', function() {
43
+ const logic = new Logic();
44
+
45
+ // Speed ranges
46
+ const slow = new logic.c.ReverseGrade(0, 30);
47
+ const moderate = new logic.c.Trapezoid(20, 30, 50, 60);
48
+ const fast = new logic.c.Grade(50, 80);
49
+
50
+ // Distance ranges
51
+ const near = new logic.c.ReverseGrade(0, 100);
52
+ const medium = new logic.c.Trapezoid(50, 100, 200, 250);
53
+ const far = new logic.c.Grade(200, 300);
54
+
55
+ // Initialize with speed
56
+ logic.init('slow', slow)
57
+ .or('moderate', moderate)
58
+ .or('fast', fast);
59
+
60
+ // Test speed logic
61
+ let speedResult = logic.defuzzify(25);
62
+ // At 25: slow=0.1667, moderate=0.75, fast=0
63
+ assert.equal(speedResult.defuzzified, 'moderate');
64
+
65
+ speedResult = logic.defuzzify(40);
66
+ assert.equal(speedResult.defuzzified, 'moderate');
67
+
68
+ speedResult = logic.defuzzify(70);
69
+ assert.equal(speedResult.defuzzified, 'fast');
70
+
71
+ // Create separate distance logic
72
+ const distanceLogic = new Logic();
73
+ distanceLogic.init('near', near)
74
+ .or('medium', medium)
75
+ .or('far', far);
76
+
77
+ let distResult = distanceLogic.defuzzify(50);
78
+ assert.equal(distResult.defuzzified, 'near');
79
+
80
+ distResult = distanceLogic.defuzzify(150);
81
+ assert.equal(distResult.defuzzified, 'medium');
82
+
83
+ distResult = distanceLogic.defuzzify(250);
84
+ assert.equal(distResult.defuzzified, 'far');
85
+ });
86
+ });
87
+
88
+ describe('Mixing all shape types', function() {
89
+ it('should work with all shape types in one Logic instance', function() {
90
+ const logic = new Logic();
91
+
92
+ // Use every shape type
93
+ const triangleShape = new logic.c.Triangle(0, 5, 10);
94
+ const trapezoidShape = new logic.c.Trapezoid(8, 12, 18, 22);
95
+ const gradeShape = new logic.c.Grade(20, 30);
96
+ const reverseGradeShape = new logic.c.ReverseGrade(28, 35);
97
+ const sigmoidShape = new logic.c.Sigmoid(40, 2);
98
+ const constantShape = new logic.c.Constant(0.5);
99
+ const fuzzyFuncShape = new logic.c.FuzzyFunction(x => Math.sin(x/10) * 0.5 + 0.5);
100
+
101
+ logic.init('triangle', triangleShape)
102
+ .or('trapezoid', trapezoidShape)
103
+ .or('grade', gradeShape)
104
+ .or('reverseGrade', reverseGradeShape)
105
+ .or('sigmoid', sigmoidShape)
106
+ .or('constant', constantShape)
107
+ .or('fuzzyFunc', fuzzyFuncShape);
108
+
109
+ // Test different values
110
+ let result = logic.defuzzify(5);
111
+ assert.equal(result.defuzzified, 'triangle');
112
+ assert.equal(result.fuzzified, 1);
113
+
114
+ result = logic.defuzzify(15);
115
+ assert.equal(result.defuzzified, 'trapezoid');
116
+ assert.equal(result.fuzzified, 1);
117
+
118
+ result = logic.defuzzify(25);
119
+ // At 25: grade=0.5, reverseGrade starts at 28 so still 1
120
+ assert.equal(result.defuzzified, 'reverseGrade');
121
+
122
+ result = logic.defuzzify(42);
123
+ // Sigmoid at x=42 with center=40 should be > 0.5
124
+ assert(result.fuzzified > 0.5);
125
+ });
126
+ });
127
+
128
+ describe('Performance with many rules', function() {
129
+ it('should handle 100+ rules efficiently', function() {
130
+ const logic = new Logic();
131
+
132
+ // Create 100 overlapping triangles
133
+ const shapes = [];
134
+ for (let i = 0; i < 100; i++) {
135
+ const start = i * 5;
136
+ const peak = start + 5;
137
+ const end = start + 10;
138
+ shapes.push({
139
+ name: `shape${String.fromCharCode(65 + Math.floor(i/26))}${String.fromCharCode(65 + (i%26))}`,
140
+ shape: new logic.c.Triangle(start, peak, end)
141
+ });
142
+ }
143
+
144
+ // Initialize with first shape
145
+ logic.init(shapes[0].name, shapes[0].shape);
146
+
147
+ // Add all other shapes
148
+ for (let i = 1; i < shapes.length; i++) {
149
+ logic.or(shapes[i].name, shapes[i].shape);
150
+ }
151
+
152
+ // Test that it works correctly
153
+ const result = logic.defuzzify(7.5); // Should match shape1 which is shapeAA (0-5-10)
154
+ assert.equal(result.defuzzified, 'shapeAA');
155
+ assert.equal(result.fuzzified, 0.5);
156
+
157
+ const result2 = logic.defuzzify(252.5); // Both BX and BY match, BX wins (comes first)
158
+ assert.equal(result2.defuzzified, 'shapeBX');
159
+ assert.equal(result2.fuzzified, 0.5);
160
+
161
+ // Verify we have 100 rules
162
+ assert.equal(logic.rules.length, 100);
163
+ });
164
+ });
165
+
166
+ describe('AND/OR/NOT combinations', function() {
167
+ it('should correctly implement fuzzy AND logic', function() {
168
+ const logic = new Logic();
169
+
170
+ const hot = new logic.c.Trapezoid(25, 30, 40, 45);
171
+ const humid = new logic.c.Trapezoid(60, 70, 80, 90);
172
+
173
+ logic.init('comfortable', hot)
174
+ .and('uncomfortable', humid);
175
+
176
+ // At 35°C and representing humidity
177
+ // hot.fuzzify(35) = 1, humid.fuzzify(35) < 0.5
178
+ // AND takes minimum of lastShape and current
179
+ const result = logic.defuzzify(35);
180
+ // Since humid has lower value, uncomfortable wins
181
+ assert.equal(result.defuzzified, 'uncomfortable');
182
+ });
183
+
184
+ it('should correctly implement fuzzy NOT logic', function() {
185
+ const logic = new Logic();
186
+
187
+ const cold = new logic.c.Triangle(0, 10, 20);
188
+
189
+ logic.init('cold', cold)
190
+ .not('notCold', cold);
191
+
192
+ // At 10°C, cold = 1, NOT cold = 0
193
+ // But the logic compares with lastShape, so notCold wins when NOT result differs
194
+ const result = logic.defuzzify(10);
195
+ assert.equal(result.defuzzified, 'notCold');
196
+ assert.equal(result.fuzzified, 1); // But fuzzified is the shape's value, not NOT result
197
+
198
+ // At 20°C, cold = 0, so NOT cold = 1
199
+ const result2 = logic.defuzzify(20);
200
+ assert.equal(result2.defuzzified, 'notCold');
201
+ });
202
+ });
203
+
204
+ describe('Edge case combinations', function() {
205
+ it('should handle overlapping shapes', function() {
206
+ const logic = new Logic();
207
+
208
+ // Two overlapping triangles
209
+ const shapeA = new logic.c.Triangle(0, 10, 20);
210
+ const shapeB = new logic.c.Triangle(10, 20, 30);
211
+
212
+ logic.init('shapeA', shapeA)
213
+ .or('shapeB', shapeB);
214
+
215
+ // At overlap point (x=15), both have value 0.5
216
+ const result = logic.defuzzify(15);
217
+ // OR takes maximum, both are 0.5, so first wins
218
+ assert.equal(result.defuzzified, 'shapeA');
219
+ assert.equal(result.fuzzified, 0.5);
220
+ });
221
+
222
+ it('should handle disjoint shapes', function() {
223
+ const logic = new Logic();
224
+
225
+ // Two non-overlapping triangles
226
+ const shapeA = new logic.c.Triangle(0, 5, 10);
227
+ const shapeB = new logic.c.Triangle(20, 25, 30);
228
+
229
+ logic.init('shapeA', shapeA)
230
+ .or('shapeB', shapeB);
231
+
232
+ // In the gap (x=15), both return 0
233
+ const result = logic.defuzzify(15);
234
+ assert.equal(result.fuzzified, 0);
235
+ assert.equal(result.defuzzified, 'shapeA'); // First shape wins when all are 0
236
+ });
237
+
238
+ it('should handle shapes meeting at boundaries', function() {
239
+ const logic = new Logic();
240
+
241
+ // Two shapes that meet exactly at x=10
242
+ const shape1 = new logic.c.Grade(0, 10);
243
+ const shape2 = new logic.c.ReverseGrade(10, 20);
244
+
245
+ logic.init('rising', shape1)
246
+ .or('falling', shape2);
247
+
248
+ // At boundary (x=10), grade=1, reverseGrade=1
249
+ const result = logic.defuzzify(10);
250
+ assert.equal(result.fuzzified, 1);
251
+ assert.equal(result.defuzzified, 'rising'); // First wins on tie
252
+ });
253
+ });
254
+
255
+ describe('Complex real-world scenarios', function() {
256
+ it('should model temperature and humidity comfort zones', function() {
257
+ const logic = new Logic();
258
+
259
+ // Temperature comfort zones
260
+ const tooCold = new logic.c.ReverseGrade(10, 18);
261
+ const comfortable = new logic.c.Trapezoid(16, 20, 24, 28);
262
+ const tooHot = new logic.c.Grade(26, 35);
263
+
264
+ // Combined logic
265
+ logic.init('tooCold', tooCold)
266
+ .or('comfortable', comfortable)
267
+ .or('tooHot', tooHot);
268
+
269
+ // Test various temperatures
270
+ assert.equal(logic.defuzzify(5).defuzzified, 'tooCold');
271
+ assert.equal(logic.defuzzify(22).defuzzified, 'comfortable');
272
+ assert.equal(logic.defuzzify(30).defuzzified, 'tooHot');
273
+
274
+ // Test boundary conditions
275
+ const boundary1 = logic.defuzzify(18);
276
+ assert(boundary1.fuzzified <= 0.5); // Transitioning from cold
277
+
278
+ const boundary2 = logic.defuzzify(26);
279
+ assert(boundary2.fuzzified >= 0.5); // Transitioning to hot
280
+ });
281
+
282
+ it('should model speed control with multiple factors', function() {
283
+ const logic = new Logic();
284
+
285
+ // Distance to obstacle
286
+ const veryClose = new logic.c.ReverseGrade(0, 10);
287
+ const close = new logic.c.Triangle(5, 20, 35);
288
+ const safe = new logic.c.Grade(30, 50);
289
+
290
+ logic.init('stop', veryClose)
291
+ .or('slow', close)
292
+ .or('normal', safe);
293
+
294
+ // Test obstacle avoidance
295
+ assert.equal(logic.defuzzify(5).defuzzified, 'stop');
296
+ assert.equal(logic.defuzzify(20).defuzzified, 'slow');
297
+ assert.equal(logic.defuzzify(50).defuzzified, 'normal');
298
+ });
299
+ });
300
+
301
+ describe('Memory and state management', function() {
302
+ it('should handle multiple Logic instances independently', function() {
303
+ const logic1 = new Logic();
304
+ const logic2 = new Logic();
305
+
306
+ const shape1 = new logic1.c.Triangle(0, 5, 10);
307
+ const shape2 = new logic2.c.Triangle(10, 15, 20);
308
+
309
+ logic1.init('low', shape1);
310
+ logic2.init('high', shape2);
311
+
312
+ // Each should work independently
313
+ assert.equal(logic1.defuzzify(5).defuzzified, 'low');
314
+ assert.equal(logic2.defuzzify(15).defuzzified, 'high');
315
+
316
+ // Verify they have separate rules
317
+ assert.equal(logic1.rules.length, 1);
318
+ assert.equal(logic2.rules.length, 1);
319
+ assert.notEqual(logic1.rules[0], logic2.rules[0]);
320
+ });
321
+
322
+ it('should allow reuse after defuzzify', function() {
323
+ const logic = new Logic();
324
+ const triangle = new logic.c.Triangle(0, 5, 10);
325
+
326
+ logic.init('test', triangle);
327
+
328
+ // Multiple defuzzify calls
329
+ const result1 = logic.defuzzify(5);
330
+ const result2 = logic.defuzzify(5);
331
+ const result3 = logic.defuzzify(5);
332
+
333
+ // All should give same result
334
+ assert.equal(result1.fuzzified, 1);
335
+ assert.equal(result2.fuzzified, 1);
336
+ assert.equal(result3.fuzzified, 1);
337
+
338
+ // Can add more rules after defuzzify
339
+ logic.or('testB', new logic.c.Triangle(10, 15, 20));
340
+ assert.equal(logic.rules.length, 2);
341
+ });
342
+ });
343
+ });
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+ const { describe, it } = require('node:test');
2
3
  const Logic = require('../lib/logic');
3
4
  const Triangle = require('../lib/curve/triangle');
4
5
  const Trapezoid = require('../lib/curve/trapezoid');
@@ -60,7 +61,7 @@ describe('js-bool test', () => {
60
61
 
61
62
 
62
63
  describe('can combine 2 fuzzy decisions via boonJs', ()=>{
63
- it.only('monster bites when heat is cold and distance is close', ()=>{
64
+ it('monster bites when heat is cold and distance is close', ()=>{
64
65
 
65
66
  const resHeat = logicHeat.defuzzify(2, 'heat');
66
67
  const resClose = logicDistance.defuzzify(2, 'distance');
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+ const { describe, it, beforeEach } = require('node:test');
2
3
  const Logic = require('../lib/logic');
3
4
  const Triangle = require('../lib/curve/triangle');
4
5
  const Trapezoid = require('../lib/curve/trapezoid');
@@ -1,13 +0,0 @@
1
- const Constant= require('../lib/curve/constant');
2
- const assert = require('assert');
3
-
4
- describe('Constant', function() {
5
- it('is a function', function() {
6
- assert.equal(typeof Constant, 'function');
7
- });
8
- it('can create a new instance', function() {
9
- var shape = new Constant(1);
10
- var res = shape.fuzzify(5);
11
- assert.equal(res, 1);
12
- });
13
- });
@@ -1,37 +0,0 @@
1
- 'use strict';
2
- const Sigmoid = require('../lib/curve/sigmoid');
3
- const assert = require('assert');
4
-
5
- describe('Sigmoid', function() {
6
- it('is a function', function() {
7
- assert.equal(typeof Sigmoid, 'function');
8
- });
9
- it('can create a new instance', function() {
10
- var sigmoid = new Sigmoid();
11
- var res = sigmoid.fuzzify(5);
12
- assert.equal(res, 0.9933071490757153);
13
- });
14
-
15
- it('0', function() {
16
- var sigmoid = new Sigmoid();
17
- var res = sigmoid.fuzzify(0);
18
- assert.equal(res, 0.5);
19
- });
20
-
21
- it('uses treshold', function() {
22
- var sigmoid = new Sigmoid(1);
23
- var res = sigmoid.fuzzify(1);
24
- assert.equal(res, 0.5);
25
- });
26
-
27
- describe('slope', function() {
28
- it('slope makes actually a difference', function() {
29
- const fvalue = 10;
30
- var sigmoid = new Sigmoid(0, 10);
31
- var resSmall = sigmoid.fuzzify(fvalue);
32
- var sigmoid2 = new Sigmoid(0, 100000);
33
- var resBig = sigmoid2.fuzzify(fvalue);
34
- assert.notEqual(resSmall, resBig);
35
- });
36
- });
37
- });
@@ -1,25 +0,0 @@
1
- 'use strict';
2
- const Trapezoid = require('../lib/curve/trapezoid');
3
- const assert = require('assert');
4
-
5
- describe('Trapezoid', function() {
6
- it('is a function', function() {
7
- assert.equal(typeof Trapezoid, 'function');
8
- });
9
- it('can create a new instance', function() {
10
- var trapez = new Trapezoid(0, 10, 20);
11
- assert.equal(trapez.x0, 0);
12
- assert.equal(trapez.x1, 10);
13
- assert.equal(trapez.x2, 20);
14
- });
15
- it('fuzzify', function() {
16
- var trapez = new Trapezoid(20, 30, 90);
17
- var res = trapez.fuzzify(25);
18
- assert.equal(res, 0.5);
19
- });
20
- it('avoid NaN if going over max', function() {
21
- var trapez = new Trapezoid(20, 30, 90, 100);
22
- var res = trapez.fuzzify(99);
23
- assert.equal(res, 0.09999999999999964);
24
- });
25
- });