@eturnity/eturnity_maths 7.48.2 → 7.51.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.
@@ -0,0 +1,140 @@
1
+ export function solveStringPatchMatching(stringList, patchList) {
2
+ // Problem Setup
3
+ const n = patchList.length // Number of rows
4
+ const m = stringList.length // Number of columns
5
+ const R = patchList.slice() // Row sum constraints
6
+ const C = stringList.slice() // Column sum constraints
7
+
8
+ // Initialize matrix with zeros
9
+ const matrix = Array.from({ length: n }, () => Array(m).fill(0))
10
+
11
+ // Function to find combinations of columns to satisfy a row sum
12
+ //each row represent a patch, each column represent a string
13
+ //we try to find matches to complete one patch with one or multiple strings
14
+ function findCombinationMatchesForRow(rowSum, colSums) {
15
+ const matches = []
16
+ const colsData = colSums.map((colSum, index) => {
17
+ return { value: colSum, index }
18
+ })
19
+ function findCombinations(
20
+ currentColsData,
21
+ currentSum,
22
+ target,
23
+ availableColsData
24
+ ) {
25
+ const stack = [
26
+ {
27
+ currentColsData,
28
+ currentSum,
29
+ availableColsData,
30
+ index: 0,
31
+ },
32
+ ]
33
+
34
+ let iterations = 0
35
+ while (stack.length > 0 && iterations < 1000 && matches.length < 5) {
36
+ iterations++
37
+ const current = stack[stack.length - 1]
38
+
39
+ if (current.currentSum === target) {
40
+ matches.push(current.currentColsData.map((col) => col.index))
41
+ stack.pop()
42
+ continue
43
+ }
44
+
45
+ if (current.availableColsData.length === 0) {
46
+ stack.pop()
47
+ continue
48
+ }
49
+
50
+ // Filter once per stack frame
51
+ if (!current.filtered) {
52
+ current.availableColsData = current.availableColsData.filter(
53
+ (colData) => colData.value <= target - current.currentSum
54
+ )
55
+ current.filtered = true
56
+ }
57
+
58
+ if (current.index >= current.availableColsData.length) {
59
+ stack.pop()
60
+ continue
61
+ }
62
+
63
+ const selectedCol = current.availableColsData[current.index]
64
+ current.index++
65
+
66
+ const nextColsData = current.currentColsData.concat(selectedCol)
67
+ const nextSum = current.currentSum + selectedCol.value
68
+ const nextAvailableColsData = current.availableColsData.filter(
69
+ (colData) => colData.index > selectedCol.index
70
+ )
71
+
72
+ stack.push({
73
+ currentColsData: nextColsData,
74
+ currentSum: nextSum,
75
+ availableColsData: nextAvailableColsData,
76
+ index: 0,
77
+ })
78
+ }
79
+ }
80
+ findCombinations([], 0, rowSum, colsData)
81
+ return matches
82
+ }
83
+
84
+ // Function to assign values to matrix for matched rows and columns
85
+ function assignCombinationPerRowMatches(matches, rowSums, colSums) {
86
+ matches.forEach(({ row, cols }) => {
87
+ if (rowSums[row] && cols.every((col) => colSums[col])) {
88
+ cols.forEach((col) => {
89
+ matrix[row][col] = colSums[col]
90
+ colSums[col] = null // Mark column as completed
91
+ })
92
+ rowSums[row] = null // Mark row as completed
93
+ }
94
+ })
95
+ }
96
+ // Main greedy decomposition function
97
+ function greedyDecomposition(rowSums, colSums) {
98
+ // Step 1: Try to decompose rows (patch) using column (string) combinations
99
+ const matches = []
100
+ for (let i = 0; i < rowSums.length; i++) {
101
+ if (rowSums[i] !== null) {
102
+ const colCombinations = findCombinationMatchesForRow(
103
+ rowSums[i],
104
+ colSums
105
+ )
106
+ colCombinations.forEach((cols) => {
107
+ matches.push({
108
+ row: i,
109
+ cols,
110
+ })
111
+ })
112
+ }
113
+ }
114
+ //sort the matchs by biggest string first
115
+ matches.sort(
116
+ (a, b) =>
117
+ Math.max(...b.cols.map((col) => colSums[col])) -
118
+ Math.max(...a.cols.map((col) => colSums[col]))
119
+ )
120
+ //sort the matchs by patch in descending order
121
+ matches.sort((a, b) => rowSums[b.row] - rowSums[a.row])
122
+ assignCombinationPerRowMatches(matches, rowSums, colSums)
123
+ }
124
+
125
+ // Run the greedy decomposition algorithm
126
+ greedyDecomposition(R, C)
127
+
128
+ // Output the resulting matrix
129
+ return {
130
+ matrix,
131
+ R,
132
+ C,
133
+ rowsWithSolution: R.map((r, index) => (r == null ? index : null)).filter(
134
+ (r) => r !== null
135
+ ),
136
+ colsWithSolution: C.map((c, index) => (c == null ? index : null)).filter(
137
+ (c) => c !== null
138
+ ),
139
+ }
140
+ }
@@ -0,0 +1,71 @@
1
+ import {
2
+ shortestHamiltonianPathSolver,
3
+ vectorLength,
4
+ substractVector,
5
+ } from '../../index'
6
+ describe('ShortestHamiltonianPathSolver', () => {
7
+ let solver = shortestHamiltonianPathSolver
8
+ it('should return empty array for empty input', () => {
9
+ const result = solver.solve([])
10
+ expect(result.indexOrder).toEqual([])
11
+ })
12
+
13
+ it('should handle single node input', () => {
14
+ const nodes = [{ x: 1, y: 1, z: 1 }]
15
+ const result = solver.solve(nodes)
16
+ expect(result.sortedNodes).toEqual(nodes)
17
+ expect(result.indexOrder).toEqual([0])
18
+ })
19
+
20
+ it('should find optimal path for two nodes', () => {
21
+ const nodes = [
22
+ { x: 0, y: 0, z: 0 },
23
+ { x: 1, y: 1, z: 1 },
24
+ ]
25
+ const result = solver.solve(nodes)
26
+ expect(result.sortedNodes).toEqual(nodes)
27
+ expect(result.indexOrder).toEqual([0, 1])
28
+ })
29
+
30
+ it('should find optimal path for square', () => {
31
+ const input = [
32
+ { x: 0, y: 0, z: 0 },
33
+ { x: 100, y: 100, z: 0 },
34
+ { x: 0, y: 100, z: 0 },
35
+ { x: 100, y: 0, z: 0 },
36
+ ]
37
+ solver.isVerbose = true
38
+ const expectedIndexOrder = []
39
+ const result = solver.solve(input)
40
+ expect(result.cost).toBe(300)
41
+ })
42
+
43
+ it('should find optimal path for line', () => {
44
+ const A = { x: 0, y: 0, z: 0 }
45
+ const B = { x: 0, y: 100, z: 0 }
46
+ const C = { x: 0, y: 200, z: 0 }
47
+ const D = { x: 0, y: 300, z: 0 }
48
+ const E = { x: 0, y: 400, z: 0 }
49
+
50
+ let result = solver.solve([A, B, C, D, E])
51
+ expect(result.cost).toBe(400)
52
+ result = solver.solve([E, B, C, D, A])
53
+ expect(result.cost).toBe(400)
54
+ result = solver.solve([B, C, A, E, D])
55
+ expect(result.cost).toBe(400)
56
+ result = solver.solve([E, D, C, B, A])
57
+ expect(result.cost).toBe(400)
58
+ result = solver.solve([D, E, C, A, B])
59
+ expect(result.cost).toBe(400)
60
+ })
61
+
62
+ it('should handle nodes with same coordinates', () => {
63
+ const nodes = [
64
+ { x: 1, y: 1, z: 1 },
65
+ { x: 1, y: 1, z: 1 },
66
+ ]
67
+ const result = solver.solve(nodes)
68
+ expect(result.sortedNodes.length).toBe(2)
69
+ expect(result.indexOrder.length).toBe(2)
70
+ })
71
+ })
@@ -0,0 +1,26 @@
1
+ import { shortestHamiltonianPathSolver } from '../../index'
2
+ import { scenario_4 } from './scenarios'
3
+
4
+ describe('ShortestHamiltonianPathSolver', () => {
5
+ let solver = shortestHamiltonianPathSolver
6
+ it('should be return shortestHamiltonianPath from real scenario 3', () => {
7
+ // const input = scenario_4.inputData.panels.map((p) => p.center)
8
+ // const N = scenario_4.inputData.panels.length
9
+ // const initialCost = solver.computePathCost(
10
+ // Array(N)
11
+ // .fill()
12
+ // .map((_, i) => i),
13
+ // input,
14
+ // false
15
+ // )
16
+ // const result = solver.solve(input, {
17
+ // hasVirtualNode: true,
18
+ // proximityMatrix: null,
19
+ // preventIntersection: true,
20
+ // })
21
+ // const sortedNodes = result.sortedNodes
22
+ // const areSelfIntersect = solver.isPathValid(result.indexOrder, input, false)
23
+ //console.log('result', initialCost, areSelfIntersect, result)
24
+ //expect(areSelfIntersect).toBe(false)
25
+ })
26
+ })
@@ -0,0 +1,17 @@
1
+ import scenario_1 from './scenario_1.json'
2
+ import scenario_1b from './scenario_1b.json'
3
+ import scenario_1_row from './scenario_1_row.json'
4
+ import scenario_2_row from './scenario_2_row.json'
5
+ import scenario_3 from './scenario_3.json'
6
+ import scenario_4 from './scenario_4.json'
7
+ import scenario_3_simplify from './scenario_3_simplify.json'
8
+
9
+ export {
10
+ scenario_1,
11
+ scenario_1b,
12
+ scenario_1_row,
13
+ scenario_2_row,
14
+ scenario_3,
15
+ scenario_3_simplify,
16
+ scenario_4,
17
+ }
@@ -0,0 +1,58 @@
1
+ {
2
+ "inputData": {
3
+ "panelsIndexes": [
4
+ [4, 13],
5
+ [5, 13],
6
+ [4, 12],
7
+ [5, 12],
8
+ [3, 12]
9
+ ],
10
+ "panels": [
11
+ {
12
+ "center": {
13
+ "x": 100,
14
+ "y": 100,
15
+ "z": 0
16
+ }
17
+ },
18
+ {
19
+ "center": {
20
+ "x": 100,
21
+ "y": 200,
22
+ "z": 0
23
+ }
24
+ },
25
+ {
26
+ "center": {
27
+ "x": 200,
28
+ "y": 100,
29
+ "z": 0
30
+ }
31
+ },
32
+ {
33
+ "center": {
34
+ "x": 200,
35
+ "y": 200,
36
+ "z": 0
37
+ }
38
+ },
39
+ {
40
+ "center": {
41
+ "x": 200,
42
+ "y": 0,
43
+ "z": 0
44
+ }
45
+ }
46
+ ]
47
+ },
48
+ "outputData": {
49
+ "panelsIndexes": [
50
+ [4, 13],
51
+ [5, 13],
52
+ [4, 12],
53
+ [5, 12],
54
+ [3, 12]
55
+ ],
56
+ "indexOrder": [0, 1, 2, 3, 4]
57
+ }
58
+ }
@@ -0,0 +1,159 @@
1
+ {
2
+ "inputData": {
3
+ "panelsIndexes": [
4
+ [3, 13],
5
+ [4, 13],
6
+ [5, 13],
7
+ [4, 12],
8
+ [5, 12],
9
+ [3, 12],
10
+ [2, 12],
11
+ [1, 12],
12
+ [0, 12]
13
+ ],
14
+ "panels": [
15
+ {
16
+ "center": {
17
+ "x": -201592.9454132333,
18
+ "y": -145757.73980606598,
19
+ "z": 3992.2558982478513
20
+ }
21
+ },
22
+ {
23
+ "center": {
24
+ "x": -201387.29671194102,
25
+ "y": -144876.38822909922,
26
+ "z": 4442.865495832077
27
+ }
28
+ },
29
+ {
30
+ "center": {
31
+ "x": -201181.6480106488,
32
+ "y": -143995.03665213246,
33
+ "z": 4893.475093416302
34
+ }
35
+ },
36
+ {
37
+ "center": {
38
+ "x": -199760.98189167224,
39
+ "y": -145255.8616871619,
40
+ "z": 4442.865495832069
41
+ }
42
+ },
43
+ {
44
+ "center": {
45
+ "x": -199555.33319038,
46
+ "y": -144374.51011019514,
47
+ "z": 4893.475093416295
48
+ }
49
+ },
50
+ {
51
+ "center": {
52
+ "x": -199966.63059296447,
53
+ "y": -146137.21326412866,
54
+ "z": 3992.255898247844
55
+ }
56
+ },
57
+ {
58
+ "center": {
59
+ "x": -200172.2792942567,
60
+ "y": -147018.56484109542,
61
+ "z": 3541.6463006636186
62
+ }
63
+ },
64
+ {
65
+ "center": {
66
+ "x": -200377.927995549,
67
+ "y": -147899.91641806217,
68
+ "z": 3091.036703079393
69
+ }
70
+ },
71
+ {
72
+ "center": {
73
+ "x": -200583.57669684122,
74
+ "y": -148781.26799502896,
75
+ "z": 2640.4271054951605
76
+ }
77
+ }
78
+ ]
79
+ },
80
+ "outputData": {
81
+ "panelsIndexes": [
82
+ [3, 13],
83
+ [4, 13],
84
+ [5, 13],
85
+ [4, 12],
86
+ [5, 12],
87
+ [3, 12],
88
+ [2, 12],
89
+ [1, 12],
90
+ [0, 12]
91
+ ],
92
+ "panels": [
93
+ {
94
+ "center": {
95
+ "x": -201592.9454132333,
96
+ "y": -145757.73980606598,
97
+ "z": 3992.2558982478513
98
+ }
99
+ },
100
+ {
101
+ "center": {
102
+ "x": -201387.29671194102,
103
+ "y": -144876.38822909922,
104
+ "z": 4442.865495832077
105
+ }
106
+ },
107
+ {
108
+ "center": {
109
+ "x": -201181.6480106488,
110
+ "y": -143995.03665213246,
111
+ "z": 4893.475093416302
112
+ }
113
+ },
114
+ {
115
+ "center": {
116
+ "x": -199760.98189167224,
117
+ "y": -145255.8616871619,
118
+ "z": 4442.865495832069
119
+ }
120
+ },
121
+ {
122
+ "center": {
123
+ "x": -199555.33319038,
124
+ "y": -144374.51011019514,
125
+ "z": 4893.475093416295
126
+ }
127
+ },
128
+ {
129
+ "center": {
130
+ "x": -199966.63059296447,
131
+ "y": -146137.21326412866,
132
+ "z": 3992.255898247844
133
+ }
134
+ },
135
+ {
136
+ "center": {
137
+ "x": -200172.2792942567,
138
+ "y": -147018.56484109542,
139
+ "z": 3541.6463006636186
140
+ }
141
+ },
142
+ {
143
+ "center": {
144
+ "x": -200377.927995549,
145
+ "y": -147899.91641806217,
146
+ "z": 3091.036703079393
147
+ }
148
+ },
149
+ {
150
+ "center": {
151
+ "x": -200583.57669684122,
152
+ "y": -148781.26799502896,
153
+ "z": 2640.4271054951605
154
+ }
155
+ }
156
+ ],
157
+ "indexOrder": [0, 1, 2, 3, 4, 5, 6, 7, 8]
158
+ }
159
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "inputData": {
3
+ "panelsIndexes": [
4
+ [0, 4],
5
+ [1, 3],
6
+ [0, 3]
7
+ ],
8
+ "panels": [
9
+ {
10
+ "center": {
11
+ "x": 0,
12
+ "y": 0,
13
+ "z": 0
14
+ }
15
+ },
16
+ {
17
+ "center": {
18
+ "x": 200,
19
+ "y": 100,
20
+ "z": 0
21
+ }
22
+ },
23
+ {
24
+ "center": {
25
+ "x": 200,
26
+ "y": 0,
27
+ "z": 0
28
+ }
29
+ }
30
+ ]
31
+ },
32
+ "outputData": {
33
+ "indexOrder": [0, 2, 1]
34
+ }
35
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "inputData": {
3
+ "panelsIndexes": [
4
+ [1, 4],
5
+ [0, 4],
6
+ [1, 3],
7
+ [0, 3],
8
+ [2, 3]
9
+ ],
10
+ "panels": [
11
+ {
12
+ "center": {
13
+ "x": 5815.287972158337,
14
+ "y": -3592.857582161817,
15
+ "z": 3000
16
+ }
17
+ },
18
+ {
19
+ "center": {
20
+ "x": 5835.303850225924,
21
+ "y": -4603.659424574653,
22
+ "z": 3000
23
+ }
24
+ },
25
+ {
26
+ "center": {
27
+ "x": 7484.9606495366115,
28
+ "y": -3559.794756867186,
29
+ "z": 3000
30
+ }
31
+ },
32
+ {
33
+ "center": {
34
+ "x": 7504.9765276042,
35
+ "y": -4570.596599280022,
36
+ "z": 3000
37
+ }
38
+ },
39
+ {
40
+ "center": {
41
+ "x": 7464.944771469023,
42
+ "y": -2548.9929144543503,
43
+ "z": 3000
44
+ }
45
+ }
46
+ ]
47
+ },
48
+ "outputData": {
49
+ "indexOrder": [0, 1, 3, 2, 4]
50
+ }
51
+ }