es6-fuzz 5.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +1 -1
- package/docs/CHANGELOG.md +5 -5
- package/package.json +3 -3
- package/test/curve/{grade.js → grade.test.js} +7 -0
- package/test/curve/{reverse-grade.js → reverse-grade.test.js} +7 -0
- package/test/curve/{shape.js → shape.test.js} +13 -0
- package/test/curve/sigmoid/sigmoid-basic.test.js +27 -0
- package/test/curve/sigmoid/sigmoid-center-behavior.test.js +66 -0
- package/test/curve/sigmoid/sigmoid-slope.test.js +15 -0
- package/test/curve/{trapezoid.js → trapezoid/trapezoid-basic.test.js} +11 -28
- package/test/curve/trapezoid/trapezoid-bounds.test.js +30 -0
- package/test/edge-cases/constant-edge-values.test.js +56 -0
- package/test/edge-cases/fuzzy-function-edge-cases.test.js +71 -0
- package/test/edge-cases/grade-edge-cases.test.js +42 -0
- package/test/edge-cases/reverse-grade-edge-cases.test.js +42 -0
- package/test/edge-cases/shapes-large-values.test.js +78 -0
- package/test/edge-cases/shapes-negative-values.test.js +75 -0
- package/test/edge-cases/sigmoid-edge-cases.test.js +70 -0
- package/test/edge-cases/trapezoid-special-cases.test.js +94 -0
- package/test/edge-cases/triangle-degenerate-cases.test.js +62 -0
- package/test/error-handling/empty-rules-handling.test.js +25 -0
- package/test/error-handling/es6-class-instantiation.test.js +52 -0
- package/test/error-handling/fuzzy-function-validation.test.js +66 -0
- package/test/error-handling/invalid-chaining-sequences.test.js +40 -0
- package/test/error-handling/logic-validation.test.js +78 -0
- package/test/error-handling/property-immutability.test.js +29 -0
- package/test/error-handling/shape-constructor-validation.test.js +70 -0
- package/test/integration/and-or-not-combinations.test.js +42 -0
- package/test/integration/complex-logic-chains.test.js +85 -0
- package/test/integration/edge-case-combinations.test.js +55 -0
- package/test/integration/mixing-shape-types.test.js +44 -0
- package/test/integration/performance-many-rules.test.js +42 -0
- package/test/integration/real-world-scenarios.test.js +50 -0
- package/test/integration/state-management.test.js +47 -0
- package/test/logic/attack-rage-calculation.test.js +22 -0
- package/test/logic/behaves-like-number.test.js +22 -0
- package/test/logic/interface.test.js +20 -0
- package/test/logic/not-operation.test.js +17 -0
- package/test/bug-74-test.js +0 -100
- package/test/curve/sigmoid.js +0 -93
- package/test/edge-cases.js +0 -244
- package/test/error-handling.js +0 -229
- package/test/integration.js +0 -343
- package/test/logic-test.js +0 -67
- /package/test/curve/{constant.js → constant.test.js} +0 -0
- /package/test/curve/{fuzzy-function.js → fuzzy-function.test.js} +0 -0
- /package/test/curve/{triangle.js → triangle.test.js} +0 -0
- /package/test/{example-test.js → example.test.js} +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Sigmoid = require('../../lib/curve/sigmoid');
|
|
5
|
+
|
|
6
|
+
describe('Sigmoid edge cases', function() {
|
|
7
|
+
describe('zero slope (becomes step function)', function() {
|
|
8
|
+
const sigmoid = new Sigmoid(5, 0);
|
|
9
|
+
|
|
10
|
+
it('should return 0 for value before center', function() {
|
|
11
|
+
assert.equal(sigmoid.fuzzify(4.9), 0);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return 0.5 for value at center', function() {
|
|
15
|
+
assert.equal(sigmoid.fuzzify(5), 0.5);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return 1 for value after center', function() {
|
|
19
|
+
assert.equal(sigmoid.fuzzify(5.1), 1);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('very small slope (near step function)', function() {
|
|
24
|
+
const sigmoid = new Sigmoid(5, 0.0001);
|
|
25
|
+
|
|
26
|
+
it('should return near 0 for value slightly before center', function() {
|
|
27
|
+
assert(sigmoid.fuzzify(4.99) < 0.01);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return 0.5 for value at center', function() {
|
|
31
|
+
assert(sigmoid.fuzzify(5) === 0.5);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return near 1 for value slightly after center', function() {
|
|
35
|
+
assert(sigmoid.fuzzify(5.01) > 0.99);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('very large slope (near constant 0.5)', function() {
|
|
40
|
+
const sigmoid = new Sigmoid(5, 10000);
|
|
41
|
+
|
|
42
|
+
it('should return approximately 0.5 for value far left', function() {
|
|
43
|
+
assert(Math.abs(sigmoid.fuzzify(0) - 0.5) < 0.1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return 0.5 for value at center', function() {
|
|
47
|
+
assert(sigmoid.fuzzify(5) === 0.5);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return approximately 0.5 for value far right', function() {
|
|
51
|
+
assert(Math.abs(sigmoid.fuzzify(10) - 0.5) < 0.1);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('negative slope', function() {
|
|
56
|
+
const sigmoid = new Sigmoid(5, -1);
|
|
57
|
+
|
|
58
|
+
it('should return greater than 0.5 for value before center', function() {
|
|
59
|
+
assert(sigmoid.fuzzify(3) > 0.5);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should return 0.5 for value at center', function() {
|
|
63
|
+
assert(sigmoid.fuzzify(5) === 0.5);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should return less than 0.5 for value after center', function() {
|
|
67
|
+
assert(sigmoid.fuzzify(7) < 0.5);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Trapezoid = require('../../lib/curve/trapezoid');
|
|
5
|
+
|
|
6
|
+
describe('Trapezoid special cases', function() {
|
|
7
|
+
describe('square trapezoid (x1=x2)', function() {
|
|
8
|
+
const trapezoid = new Trapezoid(0, 5, 5, 10);
|
|
9
|
+
|
|
10
|
+
it('should return 0 for value before x0', function() {
|
|
11
|
+
assert.equal(trapezoid.fuzzify(-1), 0);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return 0 for value at x0', function() {
|
|
15
|
+
assert.equal(trapezoid.fuzzify(0), 0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return 0.5 for value at midpoint of left slope', function() {
|
|
19
|
+
assert.equal(trapezoid.fuzzify(2.5), 0.5);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should return 1 for value at x1=x2', function() {
|
|
23
|
+
assert.equal(trapezoid.fuzzify(5), 1);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should return 0.5 for value at midpoint of right slope', function() {
|
|
27
|
+
assert.equal(trapezoid.fuzzify(7.5), 0.5);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return 0 for value at x3', function() {
|
|
31
|
+
assert.equal(trapezoid.fuzzify(10), 0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return 0 for value after x3', function() {
|
|
35
|
+
assert.equal(trapezoid.fuzzify(11), 0);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('degenerate trapezoid (x0=x1 and x2=x3)', function() {
|
|
40
|
+
const trapezoid = new Trapezoid(5, 5, 10, 10);
|
|
41
|
+
|
|
42
|
+
it('should return 0 for value before x0', function() {
|
|
43
|
+
assert.equal(trapezoid.fuzzify(4.9), 0);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return 1 for value at x0=x1', function() {
|
|
47
|
+
assert.equal(trapezoid.fuzzify(5), 1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return 1 for value in middle', function() {
|
|
51
|
+
assert.equal(trapezoid.fuzzify(7.5), 1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should return 1 for value at x2=x3', function() {
|
|
55
|
+
assert.equal(trapezoid.fuzzify(10), 1);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return 0 for value after x3', function() {
|
|
59
|
+
assert.equal(trapezoid.fuzzify(10.1), 0);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('very narrow trapezoid (x1 and x2 very close)', function() {
|
|
64
|
+
const trapezoid = new Trapezoid(0, 4.999, 5.001, 10);
|
|
65
|
+
|
|
66
|
+
it('should return 1 for value at center', function() {
|
|
67
|
+
assert.equal(trapezoid.fuzzify(5), 1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return approximately 1 for value at x1', function() {
|
|
71
|
+
assert(Math.abs(trapezoid.fuzzify(4.999) - 1) < 0.01);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should return approximately 1 for value at x2', function() {
|
|
75
|
+
assert(Math.abs(trapezoid.fuzzify(5.001) - 1) < 0.01);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('all points equal (degenerate to point)', function() {
|
|
80
|
+
const trapezoid = new Trapezoid(5, 5, 5, 5);
|
|
81
|
+
|
|
82
|
+
it('should return 0 for value before the point', function() {
|
|
83
|
+
assert.equal(trapezoid.fuzzify(4.9), 0);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should return 1 for value at the point', function() {
|
|
87
|
+
assert.equal(trapezoid.fuzzify(5), 1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should return 0 for value after the point', function() {
|
|
91
|
+
assert.equal(trapezoid.fuzzify(5.1), 0);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Triangle = require('../../lib/curve/triangle');
|
|
5
|
+
|
|
6
|
+
describe('Triangle degenerate cases', function() {
|
|
7
|
+
describe('triangle with all points equal (x0=x1=x2)', function() {
|
|
8
|
+
const triangle = new Triangle(5, 5, 5);
|
|
9
|
+
|
|
10
|
+
it('should return 0 for value before the point', function() {
|
|
11
|
+
assert.equal(triangle.fuzzify(4.9), 0);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return 1 for value at the point', function() {
|
|
15
|
+
assert.equal(triangle.fuzzify(5), 1);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return 0 for value after the point', function() {
|
|
19
|
+
assert.equal(triangle.fuzzify(5.1), 0);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('triangle with x0=x1 (right angle left)', function() {
|
|
24
|
+
const triangle = new Triangle(5, 5, 10);
|
|
25
|
+
|
|
26
|
+
it('should return 0 for value before x0', function() {
|
|
27
|
+
assert.equal(triangle.fuzzify(4.9), 0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return 1 for value at x0=x1', function() {
|
|
31
|
+
assert.equal(triangle.fuzzify(5), 1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return 0.5 for value at midpoint', function() {
|
|
35
|
+
assert.equal(triangle.fuzzify(7.5), 0.5);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return 0 for value at x2', function() {
|
|
39
|
+
assert.equal(triangle.fuzzify(10), 0);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('triangle with x1=x2 (right angle right)', function() {
|
|
44
|
+
const triangle = new Triangle(0, 5, 5);
|
|
45
|
+
|
|
46
|
+
it('should return 0 for value at x0', function() {
|
|
47
|
+
assert.equal(triangle.fuzzify(0), 0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return 0.5 for value at midpoint', function() {
|
|
51
|
+
assert.equal(triangle.fuzzify(2.5), 0.5);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should return 1 for value at x1=x2', function() {
|
|
55
|
+
assert.equal(triangle.fuzzify(5), 1);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return 0 for value after x2', function() {
|
|
59
|
+
assert.equal(triangle.fuzzify(5.1), 0);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Logic = require('../../lib/logic');
|
|
5
|
+
|
|
6
|
+
describe('Empty rules array handling', function() {
|
|
7
|
+
describe('defuzzify with no rules', function() {
|
|
8
|
+
const logic = new Logic();
|
|
9
|
+
logic.initCalled = true;
|
|
10
|
+
logic.rules = [];
|
|
11
|
+
const result = logic.defuzzify(5);
|
|
12
|
+
|
|
13
|
+
it('should return defuzzified as none', function() {
|
|
14
|
+
assert.equal(result.defuzzified, 'none');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return fuzzified as 0', function() {
|
|
18
|
+
assert.equal(result.fuzzified, 0);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return empty rules array', function() {
|
|
22
|
+
assert.deepEqual(result.rules, []);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Logic = require('../../lib/logic');
|
|
5
|
+
const Triangle = require('../../lib/curve/triangle');
|
|
6
|
+
const Trapezoid = require('../../lib/curve/trapezoid');
|
|
7
|
+
const Grade = require('../../lib/curve/grade');
|
|
8
|
+
const ReverseGrade = require('../../lib/curve/reverse-grade');
|
|
9
|
+
const Sigmoid = require('../../lib/curve/sigmoid');
|
|
10
|
+
const Constant = require('../../lib/curve/constant');
|
|
11
|
+
const FuzzyFunction = require('../../lib/curve/fuzzy-function');
|
|
12
|
+
const Shape = require('../../lib/curve/shape');
|
|
13
|
+
|
|
14
|
+
describe('ES6 class instantiation', function() {
|
|
15
|
+
describe('shapes without new keyword', function() {
|
|
16
|
+
it('should throw error when calling Triangle without new', function() {
|
|
17
|
+
assert.throws(() => Triangle(0, 5, 10), /Class constructor .* cannot be invoked without 'new'/);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should throw error when calling Trapezoid without new', function() {
|
|
21
|
+
assert.throws(() => Trapezoid(0, 5, 10, 15), /Class constructor .* cannot be invoked without 'new'/);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should throw error when calling Grade without new', function() {
|
|
25
|
+
assert.throws(() => Grade(0, 10), /Class constructor .* cannot be invoked without 'new'/);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should throw error when calling ReverseGrade without new', function() {
|
|
29
|
+
assert.throws(() => ReverseGrade(0, 10), /Class constructor .* cannot be invoked without 'new'/);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should throw error when calling Sigmoid without new', function() {
|
|
33
|
+
assert.throws(() => Sigmoid(5, 1), /Class constructor .* cannot be invoked without 'new'/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should throw error when calling Constant without new', function() {
|
|
37
|
+
assert.throws(() => Constant(0.5), /Class constructor .* cannot be invoked without 'new'/);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should throw error when calling Shape without new', function() {
|
|
41
|
+
assert.throws(() => Shape(0, 5, 10, 15), /Class constructor .* cannot be invoked without 'new'/);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should throw error when calling FuzzyFunction without new', function() {
|
|
45
|
+
assert.throws(() => FuzzyFunction(x => x), /Class constructor .* cannot be invoked without 'new'/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should throw error when calling Logic without new', function() {
|
|
49
|
+
assert.throws(() => Logic(), /Class constructor .* cannot be invoked without 'new'/);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const FuzzyFunction = require('../../lib/curve/fuzzy-function');
|
|
5
|
+
|
|
6
|
+
describe('FuzzyFunction validation', function() {
|
|
7
|
+
describe('invalid callback types', function() {
|
|
8
|
+
it('should throw TypeError when callback is string', function() {
|
|
9
|
+
assert.throws(() => {
|
|
10
|
+
const func = new FuzzyFunction('not a function');
|
|
11
|
+
func.fuzzify(5);
|
|
12
|
+
}, TypeError);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should throw TypeError when callback is null', function() {
|
|
16
|
+
assert.throws(() => {
|
|
17
|
+
const func = new FuzzyFunction(null);
|
|
18
|
+
func.fuzzify(5);
|
|
19
|
+
}, TypeError);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should throw TypeError when callback is undefined', function() {
|
|
23
|
+
assert.throws(() => {
|
|
24
|
+
const func = new FuzzyFunction(undefined);
|
|
25
|
+
func.fuzzify(5);
|
|
26
|
+
}, TypeError);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('callback returns non-number types', function() {
|
|
31
|
+
it('should throw error when callback returns string', function() {
|
|
32
|
+
const func = new FuzzyFunction(() => 'string');
|
|
33
|
+
assert.throws(() => func.fuzzify(5), /fuzzified result must be smaller than 1/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return null when callback returns null', function() {
|
|
37
|
+
const func = new FuzzyFunction(() => null);
|
|
38
|
+
assert.equal(func.fuzzify(5), null);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should throw error when callback returns undefined', function() {
|
|
42
|
+
const func = new FuzzyFunction(() => undefined);
|
|
43
|
+
assert.throws(() => func.fuzzify(5), /fuzzified result must be smaller than 1/);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('callback with conditional returns', function() {
|
|
48
|
+
const func = new FuzzyFunction(x => {
|
|
49
|
+
if (x < 0) return -1; // Invalid
|
|
50
|
+
if (x > 1) return 2; // Invalid
|
|
51
|
+
return x; // Valid for [0,1]
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should return valid value for input 0.5', function() {
|
|
55
|
+
assert.equal(func.fuzzify(0.5), 0.5);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should throw error for negative input', function() {
|
|
59
|
+
assert.throws(() => func.fuzzify(-1), /fuzzified result must be smaller than 1/);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should throw error for input greater than 1', function() {
|
|
63
|
+
assert.throws(() => func.fuzzify(2), /fuzzified result must be smaller than 1/);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Logic = require('../../lib/logic');
|
|
5
|
+
const Triangle = require('../../lib/curve/triangle');
|
|
6
|
+
|
|
7
|
+
describe('Invalid chaining sequences', function() {
|
|
8
|
+
describe('multiple init calls', function() {
|
|
9
|
+
const logic = new Logic();
|
|
10
|
+
const triangle1 = new Triangle(0, 5, 10);
|
|
11
|
+
const triangle2 = new Triangle(10, 15, 20);
|
|
12
|
+
|
|
13
|
+
it('should not throw error on second init call', function() {
|
|
14
|
+
logic.init('cold', triangle1);
|
|
15
|
+
assert.doesNotThrow(() => logic.init('hot', triangle2));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should contain both rules after multiple init calls', function() {
|
|
19
|
+
assert.equal(logic.rules.length, 2);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('chaining after defuzzify', function() {
|
|
24
|
+
const logic = new Logic();
|
|
25
|
+
const triangle = new Triangle(0, 5, 10);
|
|
26
|
+
|
|
27
|
+
it('should allow chaining operations after defuzzify', function() {
|
|
28
|
+
logic.init('cold', triangle).and('warm', new Triangle(5, 10, 15));
|
|
29
|
+
logic.defuzzify(7.5);
|
|
30
|
+
|
|
31
|
+
// Can continue chaining after defuzzify
|
|
32
|
+
assert.doesNotThrow(() => logic.or('hot', new Triangle(10, 15, 20)));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should have all rules after chaining post-defuzzify', function() {
|
|
36
|
+
logic.defuzzify(12.5);
|
|
37
|
+
assert.equal(logic.rules.length, 3);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Logic = require('../../lib/logic');
|
|
5
|
+
const Triangle = require('../../lib/curve/triangle');
|
|
6
|
+
|
|
7
|
+
describe('Logic class validation', function() {
|
|
8
|
+
describe('method calls without init', function() {
|
|
9
|
+
it('should throw error when calling defuzzify without init', function() {
|
|
10
|
+
const logic = new Logic();
|
|
11
|
+
assert.throws(() => logic.defuzzify(5), /init needs to be called before performing logic/);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should throw error when calling and() without init', function() {
|
|
15
|
+
const logic = new Logic();
|
|
16
|
+
const triangle = new Triangle(0, 5, 10);
|
|
17
|
+
assert.throws(() => logic.and('hot', triangle), /init needs to be called before performing logic/);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should throw error when calling or() without init', function() {
|
|
21
|
+
const logic = new Logic();
|
|
22
|
+
const triangle = new Triangle(0, 5, 10);
|
|
23
|
+
assert.throws(() => logic.or('hot', triangle), /init needs to be called before performing logic/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should throw error when calling not() without init', function() {
|
|
27
|
+
const logic = new Logic();
|
|
28
|
+
const triangle = new Triangle(0, 5, 10);
|
|
29
|
+
assert.throws(() => logic.not('hot', triangle), /init needs to be called before performing logic/);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('invalid output names', function() {
|
|
34
|
+
const logic = new Logic();
|
|
35
|
+
const triangle = new Triangle(0, 5, 10);
|
|
36
|
+
|
|
37
|
+
it('should throw error for output name with numbers', function() {
|
|
38
|
+
assert.throws(() => logic.init('hot123', triangle), /Output names can only be strings without space/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should throw error for output name with spaces', function() {
|
|
42
|
+
assert.throws(() => logic.init('hot cold', triangle), /Output names can only be strings without space/);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should throw error for output name with hyphen', function() {
|
|
46
|
+
assert.throws(() => logic.init('hot-cold', triangle), /Output names can only be strings without space/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should throw error for output name with underscore', function() {
|
|
50
|
+
assert.throws(() => logic.init('hot_cold', triangle), /Output names can only be strings without space/);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should throw error for output name with dot', function() {
|
|
54
|
+
assert.throws(() => logic.init('hot.cold', triangle), /Output names can only be strings without space/);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('valid output names', function() {
|
|
59
|
+
const logic = new Logic();
|
|
60
|
+
const triangle = new Triangle(0, 5, 10);
|
|
61
|
+
|
|
62
|
+
it('should allow lowercase output name', function() {
|
|
63
|
+
assert.doesNotThrow(() => logic.init('hot', triangle));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should allow title case output name', function() {
|
|
67
|
+
assert.doesNotThrow(() => logic.init('Hot', triangle));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should allow uppercase output name', function() {
|
|
71
|
+
assert.doesNotThrow(() => logic.init('HOT', triangle));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should allow camelCase output name', function() {
|
|
75
|
+
assert.doesNotThrow(() => logic.init('veryHot', triangle));
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Triangle = require('../../lib/curve/triangle');
|
|
5
|
+
|
|
6
|
+
describe('Property immutability', function() {
|
|
7
|
+
describe('shape properties are read-only', function() {
|
|
8
|
+
const triangle = new Triangle(0, 5, 10);
|
|
9
|
+
|
|
10
|
+
it('should throw error when attempting to modify x0', function() {
|
|
11
|
+
assert.throws(() => {
|
|
12
|
+
'use strict';
|
|
13
|
+
triangle.x0 = 100;
|
|
14
|
+
}, /Cannot assign to read only property/);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should keep x0 unchanged after modification attempt', function() {
|
|
18
|
+
assert.equal(triangle.x0, 0);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should keep x1 unchanged', function() {
|
|
22
|
+
assert.equal(triangle.x1, 5);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should keep x2 unchanged', function() {
|
|
26
|
+
assert.equal(triangle.x2, 10);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Triangle = require('../../lib/curve/triangle');
|
|
5
|
+
const Trapezoid = require('../../lib/curve/trapezoid');
|
|
6
|
+
const Grade = require('../../lib/curve/grade');
|
|
7
|
+
const Sigmoid = require('../../lib/curve/sigmoid');
|
|
8
|
+
|
|
9
|
+
describe('Shape constructor validation', function() {
|
|
10
|
+
describe('NaN parameters', function() {
|
|
11
|
+
it('should return number when Triangle has NaN in x0', function() {
|
|
12
|
+
const triangle = new Triangle(NaN, 5, 10);
|
|
13
|
+
const result = triangle.fuzzify(5);
|
|
14
|
+
assert(typeof result === 'number');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return number when Trapezoid has NaN in x1', function() {
|
|
18
|
+
const trapezoid = new Trapezoid(0, NaN, 10, 15);
|
|
19
|
+
const result = trapezoid.fuzzify(5);
|
|
20
|
+
assert(typeof result === 'number');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return number when Grade has NaN in x0', function() {
|
|
24
|
+
const grade = new Grade(NaN, 10);
|
|
25
|
+
const result = grade.fuzzify(5);
|
|
26
|
+
assert(typeof result === 'number');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Infinity parameters', function() {
|
|
31
|
+
describe('Triangle with Infinity in x2', function() {
|
|
32
|
+
const triangle = new Triangle(0, 10, Infinity);
|
|
33
|
+
|
|
34
|
+
it('should return 0 for value at x0', function() {
|
|
35
|
+
assert.equal(triangle.fuzzify(0), 0);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return 1 for value at x1', function() {
|
|
39
|
+
assert.equal(triangle.fuzzify(10), 1);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should return NaN for value beyond x1', function() {
|
|
43
|
+
assert(isNaN(triangle.fuzzify(20)));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('Grade with Infinity in x1', function() {
|
|
48
|
+
const grade = new Grade(0, Infinity);
|
|
49
|
+
|
|
50
|
+
it('should return 0 for value at x0', function() {
|
|
51
|
+
assert.equal(grade.fuzzify(0), 0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should return 0 for large finite value', function() {
|
|
55
|
+
assert.equal(grade.fuzzify(1000), 0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return 1 for Infinity value', function() {
|
|
59
|
+
assert.equal(grade.fuzzify(Infinity), 1);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('undefined parameters with defaults', function() {
|
|
65
|
+
it('should use default parameters for Sigmoid', function() {
|
|
66
|
+
const sigmoid = new Sigmoid();
|
|
67
|
+
assert.equal(sigmoid.fuzzify(0), 0.5);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const Logic = require('../../lib/logic');
|
|
5
|
+
|
|
6
|
+
describe('AND/OR/NOT combinations', function() {
|
|
7
|
+
it('should correctly implement fuzzy AND logic', function() {
|
|
8
|
+
const logic = new Logic();
|
|
9
|
+
|
|
10
|
+
const hot = new logic.c.Trapezoid(25, 30, 40, 45);
|
|
11
|
+
const humid = new logic.c.Trapezoid(60, 70, 80, 90);
|
|
12
|
+
|
|
13
|
+
logic.init('comfortable', hot)
|
|
14
|
+
.and('uncomfortable', humid);
|
|
15
|
+
|
|
16
|
+
// At 35°C and representing humidity
|
|
17
|
+
// hot.fuzzify(35) = 1, humid.fuzzify(35) < 0.5
|
|
18
|
+
// AND takes minimum of lastShape and current
|
|
19
|
+
const result = logic.defuzzify(35);
|
|
20
|
+
// Since humid has lower value, uncomfortable wins
|
|
21
|
+
assert.equal(result.defuzzified, 'uncomfortable');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should correctly implement fuzzy NOT logic', function() {
|
|
25
|
+
const logic = new Logic();
|
|
26
|
+
|
|
27
|
+
const cold = new logic.c.Triangle(0, 10, 20);
|
|
28
|
+
|
|
29
|
+
logic.init('cold', cold)
|
|
30
|
+
.not('notCold', cold);
|
|
31
|
+
|
|
32
|
+
// At 10°C, cold = 1, NOT cold = 0
|
|
33
|
+
// But the logic compares with lastShape, so notCold wins when NOT result differs
|
|
34
|
+
const result = logic.defuzzify(10);
|
|
35
|
+
assert.equal(result.defuzzified, 'notCold');
|
|
36
|
+
assert.equal(result.fuzzified, 1); // But fuzzified is the shape's value, not NOT result
|
|
37
|
+
|
|
38
|
+
// At 20°C, cold = 0, so NOT cold = 1
|
|
39
|
+
const result2 = logic.defuzzify(20);
|
|
40
|
+
assert.equal(result2.defuzzified, 'notCold');
|
|
41
|
+
});
|
|
42
|
+
});
|