es6-fuzz 4.0.1 → 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.
- package/Readme.md +2 -3
- package/docs/CHANGELOG.md +26 -16
- package/jsdoc.json +0 -1
- package/lib/curve/grade.js +7 -0
- package/lib/curve/reverse-grade.js +6 -1
- package/lib/curve/sigmoid.js +13 -7
- package/lib/curve/trapezoid.js +25 -2
- package/lib/curve/triangle.js +27 -0
- package/lib/logic.js +0 -2
- package/package.json +6 -6
- package/test/bug-74-test.js +100 -0
- package/test/curve/constant.js +15 -0
- package/test/{shape-fuzzy-function-test.js → curve/fuzzy-function.js} +2 -1
- package/test/{shape-grade-test.js → curve/grade.js} +2 -1
- package/test/{shape-reverse-grade-test.js → curve/reverse-grade.js} +2 -1
- package/test/{shape-test.js → curve/shape.js} +2 -1
- package/test/curve/sigmoid.js +93 -0
- package/test/curve/trapezoid.js +71 -0
- package/test/{shape-triangle-test.js → curve/triangle.js} +2 -1
- package/test/edge-cases.js +244 -0
- package/test/error-handling.js +229 -0
- package/test/example-test.js +1 -0
- package/test/init-called.test.js +1 -0
- package/test/integration.js +343 -0
- package/test/js-bool.test.js +2 -1
- package/test/logic-test.js +1 -0
- package/test/constant-test.js +0 -13
- package/test/shape-sigmoid-test.js +0 -37
- package/test/shape-trapezoid-test.js +0 -25
package/Readme.md
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/es6-fuzz)
|
|
6
6
|
[](https://github.com/sebs/es6-fuzz/blob/master/LICENSE.md)
|
|
7
7
|
[](https://github.com/sebs/es6-fuzz)
|
|
8
|
-
[](https://travis-ci.org/sebs/es6-fuzz)
|
|
9
8
|
[](https://github.com/sebs/es6-fuzz/issues)
|
|
10
9
|
|
|
11
10
|
Supported fuzzyfiers
|
|
@@ -19,8 +18,8 @@ Supported fuzzyfiers
|
|
|
19
18
|
* Triangle
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
* [api docs](
|
|
23
|
-
* [changelog](
|
|
21
|
+
* [api docs](http://sebs.github.io/es6-fuzz)
|
|
22
|
+
* [changelog](https://github.com/sebs/es6-fuzz/blob/master/docs/CHANGELOG.md)
|
|
24
23
|
|
|
25
24
|
## Install and Usage
|
|
26
25
|
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
|
+
2025-06-26
|
|
2
|
+
==========
|
|
3
|
+
|
|
4
|
+
* 5.0.0
|
|
5
|
+
* (churn): changelog
|
|
6
|
+
* 4.0.4
|
|
7
|
+
* fix: edge cases
|
|
8
|
+
* 4.0.3
|
|
9
|
+
* refactor: cleaned up test structure
|
|
10
|
+
* 4.0.2
|
|
11
|
+
* docs: add a clearer test case for constant
|
|
12
|
+
* fix: [#76](https://github.com/sebs/es6-fuzz/issues/76) sigmoid implementation with weird microoptimizations
|
|
13
|
+
* added a different sigmoid function with easier to understand parameters
|
|
14
|
+
|
|
15
|
+
2023-01-18
|
|
16
|
+
==========
|
|
17
|
+
|
|
18
|
+
* test: find the bug with trapezoid special form square
|
|
19
|
+
|
|
20
|
+
2023-01-17
|
|
21
|
+
==========
|
|
22
|
+
|
|
23
|
+
* test: more tests for the trapezoid
|
|
24
|
+
|
|
1
25
|
2022-06-14
|
|
2
26
|
==========
|
|
3
27
|
|
|
28
|
+
* chore: link docs
|
|
29
|
+
* (churn): changelog
|
|
4
30
|
* 4.0.1
|
|
5
31
|
* fix: update readme
|
|
6
|
-
* (churn): changelog
|
|
7
32
|
* 4.0.0
|
|
8
33
|
* feature: make the api work with boonJsInputs
|
|
9
34
|
* feature: add a test for outoput names only to consist of alphabet
|
|
@@ -135,18 +160,3 @@
|
|
|
135
160
|
* 2.6.0
|
|
136
161
|
* Added link to api docs
|
|
137
162
|
* More docs
|
|
138
|
-
|
|
139
|
-
2017-04-04
|
|
140
|
-
==========
|
|
141
|
-
|
|
142
|
-
* Added latest es5 export to dist
|
|
143
|
-
* (churn): changelog
|
|
144
|
-
* 2.5.10
|
|
145
|
-
* Added curves
|
|
146
|
-
* 2.5.9
|
|
147
|
-
* add missing dep
|
|
148
|
-
* 2.5.8
|
|
149
|
-
* Pages
|
|
150
|
-
* 2.5.7
|
|
151
|
-
* 2.5.6
|
|
152
|
-
* Added docs
|
package/jsdoc.json
CHANGED
package/lib/curve/grade.js
CHANGED
|
@@ -14,6 +14,13 @@ class Grade extends Shape {
|
|
|
14
14
|
fuzzify(val) {
|
|
15
15
|
let result = 0;
|
|
16
16
|
const x = val;
|
|
17
|
+
|
|
18
|
+
// Handle case where x0 = x1 (vertical grade/step function)
|
|
19
|
+
if (this.x1 === this.x0) {
|
|
20
|
+
if (x < this.x0) return 0;
|
|
21
|
+
return 1; // x >= x0
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
if (x <= this.x0) {
|
|
18
25
|
result = 0;
|
|
19
26
|
} else if (x >= this.x1) {
|
|
@@ -16,7 +16,12 @@ class ReverseGrade extends Shape {
|
|
|
16
16
|
} else if (x >= this.x1) {
|
|
17
17
|
result = 0;
|
|
18
18
|
} else {
|
|
19
|
-
|
|
19
|
+
// Handle case where x0 = x1 (vertical reverse grade)
|
|
20
|
+
if (this.x1 === this.x0) {
|
|
21
|
+
result = 0;
|
|
22
|
+
} else {
|
|
23
|
+
result = (-x / (this.x1 - this.x0)) + (this.x1 / (this.x1 - this.x0));
|
|
24
|
+
}
|
|
20
25
|
}
|
|
21
26
|
return result;
|
|
22
27
|
}
|
package/lib/curve/sigmoid.js
CHANGED
|
@@ -9,20 +9,26 @@
|
|
|
9
9
|
class Sigmoid {
|
|
10
10
|
/**
|
|
11
11
|
* Create a Sigmoid Function.
|
|
12
|
-
* @param {number}
|
|
13
|
-
* @param {number} slope
|
|
12
|
+
* @param {number} center - The center point of the sigmoid curve (where it outputs 0.5)
|
|
13
|
+
* @param {number} slope - The slope of the sigmoid curve (higher = steeper transition)
|
|
14
14
|
*/
|
|
15
|
-
constructor(
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
15
|
+
constructor(center=0, slope=1) {
|
|
16
|
+
this.center = center;
|
|
17
|
+
this.slope = slope;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Fuzzify
|
|
21
|
-
* @param {number} - Point on X axis
|
|
21
|
+
* @param {number} x - Point on X axis
|
|
22
22
|
* @return {number} fuzzy output 0..1
|
|
23
23
|
*/
|
|
24
24
|
fuzzify(x) {
|
|
25
|
-
|
|
25
|
+
if (this.slope === 0) {
|
|
26
|
+
// When slope is 0, sigmoid becomes a step function
|
|
27
|
+
if (x < this.center) return 0;
|
|
28
|
+
if (x > this.center) return 1;
|
|
29
|
+
return 0.5; // At center point
|
|
30
|
+
}
|
|
31
|
+
return 1.0 / (1.0 + Math.exp(-(x - this.center) / this.slope));
|
|
26
32
|
}
|
|
27
33
|
}
|
|
28
34
|
module.exports = Sigmoid;
|
package/lib/curve/trapezoid.js
CHANGED
|
@@ -15,6 +15,19 @@ class Trapezoid extends Shape {
|
|
|
15
15
|
let result = 0;
|
|
16
16
|
const x = val;
|
|
17
17
|
|
|
18
|
+
// Special case: all points equal (spike at single point)
|
|
19
|
+
if (this.x0 === this.x1 && this.x1 === this.x2 && this.x2 === this.x3) {
|
|
20
|
+
return x === this.x0 ? 1 : 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Special case: x0=x1 and x2=x3 (rectangle)
|
|
24
|
+
if (this.x0 === this.x1 && this.x2 === this.x3) {
|
|
25
|
+
if (x < this.x0) return 0;
|
|
26
|
+
if (x >= this.x0 && x <= this.x2) return 1;
|
|
27
|
+
if (x > this.x2) return 0;
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
if (x <= this.x0) {
|
|
19
32
|
result = 0;
|
|
20
33
|
} else if (x >= this.x3) {
|
|
@@ -22,9 +35,19 @@ class Trapezoid extends Shape {
|
|
|
22
35
|
} else if ((x >= this.x1) && (x <= this.x2)) {
|
|
23
36
|
result = 1;
|
|
24
37
|
} else if ((x > this.x0) && (x < this.x1)) {
|
|
25
|
-
|
|
38
|
+
// Handle case where x0 = x1 (vertical left edge)
|
|
39
|
+
if (this.x1 === this.x0) {
|
|
40
|
+
result = 1;
|
|
41
|
+
} else {
|
|
42
|
+
result = (x / (this.x1 - this.x0)) - (this.x0 / (this.x1 - this.x0));
|
|
43
|
+
}
|
|
26
44
|
} else {
|
|
27
|
-
|
|
45
|
+
// Handle case where x2 = x3 (vertical right edge)
|
|
46
|
+
if (this.x3 === this.x2) {
|
|
47
|
+
result = 1;
|
|
48
|
+
} else {
|
|
49
|
+
result = (-x / (this.x3 - this.x2)) + (this.x3 / (this.x3 - this.x2));
|
|
50
|
+
}
|
|
28
51
|
}
|
|
29
52
|
return result;
|
|
30
53
|
}
|
package/lib/curve/triangle.js
CHANGED
|
@@ -13,6 +13,33 @@ class Triangle extends Shape {
|
|
|
13
13
|
*/
|
|
14
14
|
fuzzify(x) {
|
|
15
15
|
let result = 0;
|
|
16
|
+
|
|
17
|
+
// Special case: all points equal (spike at single point)
|
|
18
|
+
if (this.x0 === this.x1 && this.x1 === this.x2) {
|
|
19
|
+
return x === this.x0 ? 1 : 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Special case: x0 = x1 (vertical left edge)
|
|
23
|
+
if (this.x0 === this.x1) {
|
|
24
|
+
if (x < this.x0) return 0;
|
|
25
|
+
if (x === this.x0) return 1;
|
|
26
|
+
if (x >= this.x2) return 0;
|
|
27
|
+
if (x < this.x2) {
|
|
28
|
+
return (this.x2 - x) / (this.x2 - this.x1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Special case: x1 = x2 (vertical right edge)
|
|
33
|
+
if (this.x1 === this.x2) {
|
|
34
|
+
if (x <= this.x0) return 0;
|
|
35
|
+
if (x > this.x0 && x < this.x1) {
|
|
36
|
+
return (x - this.x0) / (this.x1 - this.x0);
|
|
37
|
+
}
|
|
38
|
+
if (x === this.x1) return 1;
|
|
39
|
+
if (x > this.x1) return 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Normal triangle
|
|
16
43
|
if (x <= this.x0) {
|
|
17
44
|
result = 0;
|
|
18
45
|
} else if (x >= this.x2) {
|
package/lib/logic.js
CHANGED
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "es6-fuzz",
|
|
3
3
|
"description": "fuzzy logic with and for es6",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0",
|
|
5
5
|
"main": "lib/logic.js",
|
|
6
6
|
"repository": "git@github.com:sebs/es6-fuzz.git",
|
|
7
7
|
"homepage": "https://github.com/sebs/es6-fuzz",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"changelog": "rm ./docs/CHANGELOG.md && npx changelog https://github.com/sebs/es6-fuzz all > ./docs/CHANGELOG.md && git add . && git commit . -m '(churn): changelog'",
|
|
10
|
-
"test": "
|
|
11
|
-
"test:watch": "
|
|
10
|
+
"test": "node --test",
|
|
11
|
+
"test:watch": "node --test --watch",
|
|
12
12
|
"addpush": "git add . && git commit . -m 'docs'",
|
|
13
13
|
"pages": "node ./scripts/publish-gh.js",
|
|
14
14
|
"docs": "jsdoc -c ./jsdoc.json -t ./node_modules/ink-docstrap/template -R ./Readme.md -u ./docs",
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"author": "Sebastian Schürmann",
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"gh-pages": "
|
|
28
|
+
"gh-pages": "6.3.0",
|
|
29
29
|
"ink-docstrap": "1.3.2",
|
|
30
|
-
"jsdoc": "
|
|
30
|
+
"jsdoc": "4.0.4"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"boon-js": "2.0.
|
|
33
|
+
"boon-js": "2.0.5"
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const { describe, it } = require('node:test');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const Logic = require('../lib/logic');
|
|
4
|
+
const Triangle = require('../lib/curve/triangle');
|
|
5
|
+
const Trapezoid = require('../lib/curve/trapezoid');
|
|
6
|
+
const Grade = require('../lib/curve/grade');
|
|
7
|
+
const getEvaluator = require('boon-js').getEvaluator;
|
|
8
|
+
|
|
9
|
+
const assess = (likelihood, impact) => {
|
|
10
|
+
var logicLikelihood = new Logic();
|
|
11
|
+
const lowLikelihood = new Trapezoid(0, 0, 2, 3);
|
|
12
|
+
const moderateLikelihood = new Trapezoid(2, 3, 7, 8);
|
|
13
|
+
const highLikelihood = new Trapezoid(7, 8, 10, 10);
|
|
14
|
+
|
|
15
|
+
logicLikelihood.init('low', lowLikelihood)
|
|
16
|
+
logicLikelihood.or('moderate', moderateLikelihood)
|
|
17
|
+
logicLikelihood.or('high', highLikelihood);
|
|
18
|
+
|
|
19
|
+
var logicImpact = new Logic();
|
|
20
|
+
const publicImpact = new Trapezoid(0, 0, 1.5, 2.5);
|
|
21
|
+
const internalImpact = new Trapezoid(1.5, 2.5, 4.5, 5.5);
|
|
22
|
+
const confidentialImpact = new Trapezoid(4.5, 5.5, 7.5, 8.5);
|
|
23
|
+
const restrictedImpact = new Trapezoid(7.5, 8.5, 10, 10);
|
|
24
|
+
|
|
25
|
+
logicImpact.init('public', publicImpact)
|
|
26
|
+
logicImpact.or('internal', internalImpact)
|
|
27
|
+
logicImpact.or('confidential', confidentialImpact);
|
|
28
|
+
logicImpact.or('restricted', restrictedImpact);
|
|
29
|
+
|
|
30
|
+
const tests = [];
|
|
31
|
+
// assessment very low
|
|
32
|
+
const securityTest_low_public = getEvaluator('likelihood.low AND impact.public');
|
|
33
|
+
tests.push(securityTest_low_public);
|
|
34
|
+
// assessment low
|
|
35
|
+
const securityTest_low_internal = getEvaluator('likelihood.low AND impact.internal');
|
|
36
|
+
tests.push(securityTest_low_internal);
|
|
37
|
+
// assessment medium
|
|
38
|
+
const securityTest_low_confidential = getEvaluator('likelihood.low AND impact.confidential');
|
|
39
|
+
tests.push(securityTest_low_confidential);
|
|
40
|
+
// assessment high
|
|
41
|
+
const securityTest_low_restricted = getEvaluator('likelihood.low AND impact.restricted');
|
|
42
|
+
tests.push(securityTest_low_restricted);
|
|
43
|
+
// assessment low
|
|
44
|
+
const securityTest_moderate_public = getEvaluator('likelihood.moderate AND impact.public');
|
|
45
|
+
tests.push(securityTest_moderate_public);
|
|
46
|
+
// assessment medium
|
|
47
|
+
const securityTest_moderate_internal = getEvaluator('likelihood.moderate AND impact.internal');
|
|
48
|
+
tests.push(securityTest_moderate_internal);
|
|
49
|
+
// assessment high
|
|
50
|
+
const securityTest_moderate_confidential = getEvaluator('likelihood.moderate AND impact.confidential');
|
|
51
|
+
tests.push(securityTest_moderate_confidential);
|
|
52
|
+
// assessment very high
|
|
53
|
+
const securityTest_moderate_restricted = getEvaluator('likelihood.moderate AND impact.restricted');
|
|
54
|
+
tests.push(securityTest_moderate_restricted);
|
|
55
|
+
// assessment low
|
|
56
|
+
const securityTest_high_public = getEvaluator('likelihood.high AND impact.public');
|
|
57
|
+
tests.push(securityTest_high_public);
|
|
58
|
+
// assessment medium
|
|
59
|
+
const securityTest_high_internal = getEvaluator('likelihood.high AND impact.internal');
|
|
60
|
+
tests.push(securityTest_high_internal);
|
|
61
|
+
// assessment high
|
|
62
|
+
const securityTest_high_confidential = getEvaluator('likelihood.high AND impact.confidential');
|
|
63
|
+
tests.push(securityTest_high_confidential);
|
|
64
|
+
// assessment very high
|
|
65
|
+
const securityTest_high_restricted = getEvaluator('likelihood.high AND impact.restricted');
|
|
66
|
+
tests.push(securityTest_high_restricted);
|
|
67
|
+
|
|
68
|
+
const resLikelihood = logicLikelihood.defuzzify(likelihood, 'likelihood');
|
|
69
|
+
const resImpact = logicImpact.defuzzify(impact, 'impact');
|
|
70
|
+
|
|
71
|
+
const jsBoonInput = { ...resLikelihood.boonJsInputs, ...resImpact.boonJsInputs }
|
|
72
|
+
|
|
73
|
+
const results = [];
|
|
74
|
+
for(let i = 0; i < tests.length; i++){
|
|
75
|
+
results.push( tests[i](jsBoonInput) );
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if(results[0]) return "very low";
|
|
79
|
+
else if(results[1]) return "low";
|
|
80
|
+
else if(results[2]) return "medium";
|
|
81
|
+
else if(results[3]) return "high";
|
|
82
|
+
else if(results[4]) return "low";
|
|
83
|
+
else if(results[5]) return "medium";
|
|
84
|
+
else if(results[6]) return "high";
|
|
85
|
+
else if(results[7]) return "high";
|
|
86
|
+
else if(results[8]) return "low";
|
|
87
|
+
else if(results[9]) return "medium";
|
|
88
|
+
else if(results[10]) return "high";
|
|
89
|
+
else if(results[11]) return "very high";
|
|
90
|
+
else return "Unknown";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
describe('Bug #74 Wrong evaluation at limit of trapezoid', function() {
|
|
95
|
+
it.skip('asses', ()=>{
|
|
96
|
+
const restrictedImpact = new Trapezoid(7.5, 8.5, 10, 10);
|
|
97
|
+
var res = restrictedImpact.fuzzify(10);
|
|
98
|
+
assert.equal(res, 1);
|
|
99
|
+
})
|
|
100
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { describe, it } = require('node:test');
|
|
2
|
+
const Constant= require('../../lib/curve/constant');
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
|
|
5
|
+
describe('Constant', function() {
|
|
6
|
+
it('is a function', function() {
|
|
7
|
+
assert.equal(typeof Constant, 'function');
|
|
8
|
+
});
|
|
9
|
+
it('same value at 5 and 100', function() {
|
|
10
|
+
var shape = new Constant(1);
|
|
11
|
+
var resFive = shape.fuzzify(5);
|
|
12
|
+
var resHundret = shape.fuzzify(100);
|
|
13
|
+
assert.equal(resFive, resHundret);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const FuzzyFunction= require('../../lib/curve/fuzzy-function');
|
|
3
4
|
const assert = require('assert');
|
|
4
5
|
|
|
5
6
|
describe('FuzzyFunction', function() {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const Sigmoid = require('../../lib/curve/sigmoid');
|
|
4
|
+
const assert = require('assert');
|
|
5
|
+
|
|
6
|
+
describe('Sigmoid', function() {
|
|
7
|
+
it('is a function', function() {
|
|
8
|
+
assert.equal(typeof Sigmoid, 'function');
|
|
9
|
+
});
|
|
10
|
+
it('can create a new instance', function() {
|
|
11
|
+
var sigmoid = new Sigmoid();
|
|
12
|
+
var res = sigmoid.fuzzify(5);
|
|
13
|
+
assert.equal(res, 0.9933071490757153);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('0', function() {
|
|
17
|
+
var sigmoid = new Sigmoid();
|
|
18
|
+
var res = sigmoid.fuzzify(0);
|
|
19
|
+
assert.equal(res, 0.5);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('uses treshold', function() {
|
|
23
|
+
var sigmoid = new Sigmoid(1);
|
|
24
|
+
var res = sigmoid.fuzzify(1);
|
|
25
|
+
assert.equal(res, 0.5);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('slope', function() {
|
|
29
|
+
it('slope makes actually a difference', function() {
|
|
30
|
+
const fvalue = 10;
|
|
31
|
+
var sigmoid = new Sigmoid(0, 10);
|
|
32
|
+
var resSmall = sigmoid.fuzzify(fvalue);
|
|
33
|
+
var sigmoid2 = new Sigmoid(0, 100000);
|
|
34
|
+
var resBig = sigmoid2.fuzzify(fvalue);
|
|
35
|
+
assert.notEqual(resSmall, resBig);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('center parameter behavior', function() {
|
|
40
|
+
it('should correctly handle positive center values', function() {
|
|
41
|
+
// This test verifies that the sigmoid is properly centered
|
|
42
|
+
var sigmoid = new Sigmoid(2, 1);
|
|
43
|
+
|
|
44
|
+
// With center=2, the sigmoid is centered at x=2
|
|
45
|
+
// So sigmoid.fuzzify(2) should return 0.5
|
|
46
|
+
var res = sigmoid.fuzzify(2);
|
|
47
|
+
assert.equal(res, 0.5);
|
|
48
|
+
|
|
49
|
+
// Values to the right of the center should approach 1
|
|
50
|
+
var rightOfCenter = sigmoid.fuzzify(4);
|
|
51
|
+
assert(rightOfCenter > 0.5);
|
|
52
|
+
assert(rightOfCenter < 1);
|
|
53
|
+
|
|
54
|
+
// Values to the left of the center should approach 0
|
|
55
|
+
var leftOfCenter = sigmoid.fuzzify(0);
|
|
56
|
+
assert(leftOfCenter < 0.5);
|
|
57
|
+
assert(leftOfCenter > 0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should correctly handle negative center values', function() {
|
|
61
|
+
var sigmoid = new Sigmoid(-3, 1);
|
|
62
|
+
|
|
63
|
+
// With center=-3, the sigmoid is centered at x=-3
|
|
64
|
+
// So sigmoid.fuzzify(-3) should return 0.5
|
|
65
|
+
var res = sigmoid.fuzzify(-3);
|
|
66
|
+
assert.equal(res, 0.5);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('sigmoid should be 0.075 at zero', function() {
|
|
70
|
+
// Test that verifies the sigmoid value at x=0 when center=5
|
|
71
|
+
var sigmoid = new Sigmoid(5, 2);
|
|
72
|
+
var atZero = sigmoid.fuzzify(0);
|
|
73
|
+
assert.equal(atZero, 0.07585818002124355);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('verifies sigmoid symmetry', function() {
|
|
77
|
+
// Test that verifies the sigmoid curve is symmetric around its center
|
|
78
|
+
var sigmoid = new Sigmoid(5, 2);
|
|
79
|
+
|
|
80
|
+
// Test symmetry around the center point
|
|
81
|
+
var atCenter = sigmoid.fuzzify(5);
|
|
82
|
+
assert.equal(atCenter, 0.5);
|
|
83
|
+
|
|
84
|
+
// Test points equidistant from center
|
|
85
|
+
var rightPoint = sigmoid.fuzzify(7); // 2 units right of center
|
|
86
|
+
var leftPoint = sigmoid.fuzzify(3); // 2 units left of center
|
|
87
|
+
|
|
88
|
+
// Due to sigmoid symmetry: f(center+d) + f(center-d) should equal 1
|
|
89
|
+
var sum = rightPoint + leftPoint;
|
|
90
|
+
assert(Math.abs(sum - 1) < 0.0000001, `Sum ${sum} should be approximately 1`);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { describe, it } = require('node:test');
|
|
3
|
+
const Trapezoid = require('../../lib/curve/trapezoid');
|
|
4
|
+
const assert = require('assert');
|
|
5
|
+
|
|
6
|
+
describe('Trapezoid', function() {
|
|
7
|
+
it('is a function', function() {
|
|
8
|
+
assert.equal(typeof Trapezoid, 'function');
|
|
9
|
+
});
|
|
10
|
+
it('can create a new instance', function() {
|
|
11
|
+
var trapez = new Trapezoid(0, 10, 20);
|
|
12
|
+
assert.equal(trapez.x0, 0);
|
|
13
|
+
assert.equal(trapez.x1, 10);
|
|
14
|
+
assert.equal(trapez.x2, 20);
|
|
15
|
+
});
|
|
16
|
+
it('fuzzify', function() {
|
|
17
|
+
var trapez = new Trapezoid(20, 30, 90);
|
|
18
|
+
var res = trapez.fuzzify(25);
|
|
19
|
+
assert.equal(res, 0.5);
|
|
20
|
+
});
|
|
21
|
+
it('avoid NaN if going over max', function() {
|
|
22
|
+
var trapez = new Trapezoid(20, 30, 90, 100);
|
|
23
|
+
var res = trapez.fuzzify(99);
|
|
24
|
+
assert.equal(res, 0.09999999999999964);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('edge case triangle', function() {
|
|
28
|
+
var trapez = new Trapezoid(0, 0, 1.5, 2.5);
|
|
29
|
+
var res = trapez.fuzzify(0);
|
|
30
|
+
assert.equal(res, 0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('edge case triangle right bounds', function() {
|
|
34
|
+
var trapez = new Trapezoid(0, 0, 1.5, 2.5);
|
|
35
|
+
var res = trapez.fuzzify(2.5);
|
|
36
|
+
assert.equal(res, 0);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('edge case triangle right top', function() {
|
|
40
|
+
var trapez = new Trapezoid(0, 0, 1.5, 2.5);
|
|
41
|
+
var res = trapez.fuzzify(1.5);
|
|
42
|
+
assert.equal(res, 1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
describe('bounds', ()=>{
|
|
47
|
+
it('trapezoid left bounds 0', function() {
|
|
48
|
+
var trapez = new Trapezoid(20, 30, 90, 100);
|
|
49
|
+
var res = trapez.fuzzify(20);
|
|
50
|
+
assert.equal(res, 0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('trapezoid right bounds 0', function() {
|
|
54
|
+
var trapez = new Trapezoid(20, 30, 90, 100);
|
|
55
|
+
var res = trapez.fuzzify(100);
|
|
56
|
+
assert.equal(res, 0);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('trapezoid left top = 1', function() {
|
|
60
|
+
var trapez = new Trapezoid(20, 30, 90, 100);
|
|
61
|
+
var res = trapez.fuzzify(30);
|
|
62
|
+
assert.equal(res, 1);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('trapezoid right top = 1', function() {
|
|
66
|
+
var trapez = new Trapezoid(20, 30, 90, 100);
|
|
67
|
+
var res = trapez.fuzzify(90);
|
|
68
|
+
assert.equal(res, 1);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|