@rednaxela101/cubing 0.63.5

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.
Files changed (136) hide show
  1. package/README.md +120 -0
  2. package/alg/index.d.ts +1 -0
  3. package/bluetooth/index.d.ts +1 -0
  4. package/dist/bin/chunks/chunk-22VWCGBC.js +13 -0
  5. package/dist/bin/chunks/chunk-22VWCGBC.js.map +7 -0
  6. package/dist/bin/order.js +59 -0
  7. package/dist/bin/order.js.map +7 -0
  8. package/dist/bin/puzzle-geometry-bin.js +407 -0
  9. package/dist/bin/puzzle-geometry-bin.js.map +7 -0
  10. package/dist/bin/scramble.js +188 -0
  11. package/dist/bin/scramble.js.map +7 -0
  12. package/dist/bin/svg.js +56 -0
  13. package/dist/bin/svg.js.map +7 -0
  14. package/dist/lib/cubing/alg/index.d.ts +2 -0
  15. package/dist/lib/cubing/alg/index.js +43 -0
  16. package/dist/lib/cubing/alg/index.js.map +7 -0
  17. package/dist/lib/cubing/bluetooth/index.d.ts +189 -0
  18. package/dist/lib/cubing/bluetooth/index.js +2271 -0
  19. package/dist/lib/cubing/bluetooth/index.js.map +7 -0
  20. package/dist/lib/cubing/chunks/big-puzzle-orientation-BA6QHO5C.js +68 -0
  21. package/dist/lib/cubing/chunks/big-puzzle-orientation-BA6QHO5C.js.map +7 -0
  22. package/dist/lib/cubing/chunks/chunk-2VJB4TMH.js +1774 -0
  23. package/dist/lib/cubing/chunks/chunk-2VJB4TMH.js.map +7 -0
  24. package/dist/lib/cubing/chunks/chunk-52ZXJEUO.js +665 -0
  25. package/dist/lib/cubing/chunks/chunk-52ZXJEUO.js.map +7 -0
  26. package/dist/lib/cubing/chunks/chunk-56VZAEPF.js +274 -0
  27. package/dist/lib/cubing/chunks/chunk-56VZAEPF.js.map +7 -0
  28. package/dist/lib/cubing/chunks/chunk-7GUL3OBQ.js +9 -0
  29. package/dist/lib/cubing/chunks/chunk-7GUL3OBQ.js.map +7 -0
  30. package/dist/lib/cubing/chunks/chunk-7L7BJSIM.js +21 -0
  31. package/dist/lib/cubing/chunks/chunk-7L7BJSIM.js.map +7 -0
  32. package/dist/lib/cubing/chunks/chunk-AESPJKN6.js +265 -0
  33. package/dist/lib/cubing/chunks/chunk-AESPJKN6.js.map +7 -0
  34. package/dist/lib/cubing/chunks/chunk-C2Q347XS.js +89 -0
  35. package/dist/lib/cubing/chunks/chunk-C2Q347XS.js.map +7 -0
  36. package/dist/lib/cubing/chunks/chunk-CGBMJBU3.js +1195 -0
  37. package/dist/lib/cubing/chunks/chunk-CGBMJBU3.js.map +7 -0
  38. package/dist/lib/cubing/chunks/chunk-HZQONEMD.js +249 -0
  39. package/dist/lib/cubing/chunks/chunk-HZQONEMD.js.map +7 -0
  40. package/dist/lib/cubing/chunks/chunk-IAGCVPL3.js +1069 -0
  41. package/dist/lib/cubing/chunks/chunk-IAGCVPL3.js.map +7 -0
  42. package/dist/lib/cubing/chunks/chunk-NAPITA3L.js +1 -0
  43. package/dist/lib/cubing/chunks/chunk-NAPITA3L.js.map +7 -0
  44. package/dist/lib/cubing/chunks/chunk-XPDJUSZ7.js +1876 -0
  45. package/dist/lib/cubing/chunks/chunk-XPDJUSZ7.js.map +7 -0
  46. package/dist/lib/cubing/chunks/chunk-YSI6PF35.js +619 -0
  47. package/dist/lib/cubing/chunks/chunk-YSI6PF35.js.map +7 -0
  48. package/dist/lib/cubing/chunks/inside-LG2EAKCL.js +557 -0
  49. package/dist/lib/cubing/chunks/inside-LG2EAKCL.js.map +7 -0
  50. package/dist/lib/cubing/chunks/puzzles-dynamic-2x2x2-JYJJXB4V.js +142 -0
  51. package/dist/lib/cubing/chunks/puzzles-dynamic-2x2x2-JYJJXB4V.js.map +7 -0
  52. package/dist/lib/cubing/chunks/puzzles-dynamic-3x3x3-FYXD7SIU.js +365 -0
  53. package/dist/lib/cubing/chunks/puzzles-dynamic-3x3x3-FYXD7SIU.js.map +7 -0
  54. package/dist/lib/cubing/chunks/puzzles-dynamic-4x4x4-AIGABOAR.js +132 -0
  55. package/dist/lib/cubing/chunks/puzzles-dynamic-4x4x4-AIGABOAR.js.map +7 -0
  56. package/dist/lib/cubing/chunks/puzzles-dynamic-5x5x5-JUNRX27W.js +277 -0
  57. package/dist/lib/cubing/chunks/puzzles-dynamic-5x5x5-JUNRX27W.js.map +7 -0
  58. package/dist/lib/cubing/chunks/puzzles-dynamic-clock-USGQTDPO.js +836 -0
  59. package/dist/lib/cubing/chunks/puzzles-dynamic-clock-USGQTDPO.js.map +7 -0
  60. package/dist/lib/cubing/chunks/puzzles-dynamic-fto-WJHMLYCJ.js +100 -0
  61. package/dist/lib/cubing/chunks/puzzles-dynamic-fto-WJHMLYCJ.js.map +7 -0
  62. package/dist/lib/cubing/chunks/puzzles-dynamic-megaminx-2LVHIDL4.js +224 -0
  63. package/dist/lib/cubing/chunks/puzzles-dynamic-megaminx-2LVHIDL4.js.map +7 -0
  64. package/dist/lib/cubing/chunks/puzzles-dynamic-pyraminx-7UWX4HHI.js +93 -0
  65. package/dist/lib/cubing/chunks/puzzles-dynamic-pyraminx-7UWX4HHI.js.map +7 -0
  66. package/dist/lib/cubing/chunks/puzzles-dynamic-square1-PQEKBPFN.js +842 -0
  67. package/dist/lib/cubing/chunks/puzzles-dynamic-square1-PQEKBPFN.js.map +7 -0
  68. package/dist/lib/cubing/chunks/search-dynamic-sgs-2x2x2-XFKQ4VJS.js +125 -0
  69. package/dist/lib/cubing/chunks/search-dynamic-sgs-2x2x2-XFKQ4VJS.js.map +7 -0
  70. package/dist/lib/cubing/chunks/search-dynamic-sgs-megaminx-ZDOOLEDW.js +1564 -0
  71. package/dist/lib/cubing/chunks/search-dynamic-sgs-megaminx-ZDOOLEDW.js.map +7 -0
  72. package/dist/lib/cubing/chunks/search-dynamic-sgs-pyraminx-W6UJWBYY.js +108 -0
  73. package/dist/lib/cubing/chunks/search-dynamic-sgs-pyraminx-W6UJWBYY.js.map +7 -0
  74. package/dist/lib/cubing/chunks/search-dynamic-sgs-skewb-FXGKV4KD.js +125 -0
  75. package/dist/lib/cubing/chunks/search-dynamic-sgs-skewb-FXGKV4KD.js.map +7 -0
  76. package/dist/lib/cubing/chunks/search-dynamic-sgs-unofficial-YK6HA7BK.js +644 -0
  77. package/dist/lib/cubing/chunks/search-dynamic-sgs-unofficial-YK6HA7BK.js.map +7 -0
  78. package/dist/lib/cubing/chunks/search-dynamic-solve-3x3x3-DLMAGPXS.js +2549 -0
  79. package/dist/lib/cubing/chunks/search-dynamic-solve-3x3x3-DLMAGPXS.js.map +7 -0
  80. package/dist/lib/cubing/chunks/search-dynamic-solve-4x4x4-NONXQHOJ.js +2923 -0
  81. package/dist/lib/cubing/chunks/search-dynamic-solve-4x4x4-NONXQHOJ.js.map +7 -0
  82. package/dist/lib/cubing/chunks/search-dynamic-solve-fto-3GQ2HLSW.js +1405 -0
  83. package/dist/lib/cubing/chunks/search-dynamic-solve-fto-3GQ2HLSW.js.map +7 -0
  84. package/dist/lib/cubing/chunks/search-worker-entry.js +19 -0
  85. package/dist/lib/cubing/chunks/search-worker-entry.js.map +7 -0
  86. package/dist/lib/cubing/chunks/square1-stickerings-W2ZLZWIN.js +73 -0
  87. package/dist/lib/cubing/chunks/square1-stickerings-W2ZLZWIN.js.map +7 -0
  88. package/dist/lib/cubing/chunks/twips-2RNTWYNZ.js +433 -0
  89. package/dist/lib/cubing/chunks/twips-2RNTWYNZ.js.map +7 -0
  90. package/dist/lib/cubing/chunks/twips_wasm_bg-TJNYGA2C-23XTPLIK.js +10 -0
  91. package/dist/lib/cubing/chunks/twips_wasm_bg-TJNYGA2C-23XTPLIK.js.map +7 -0
  92. package/dist/lib/cubing/chunks/twisty-dynamic-3d-JO6OGKOF.js +2424 -0
  93. package/dist/lib/cubing/chunks/twisty-dynamic-3d-JO6OGKOF.js.map +7 -0
  94. package/dist/lib/cubing/index-BX6f56uo.d.ts +76 -0
  95. package/dist/lib/cubing/index-CcRu94bx.d.ts +2622 -0
  96. package/dist/lib/cubing/kpuzzle/index.d.ts +2 -0
  97. package/dist/lib/cubing/kpuzzle/index.js +12 -0
  98. package/dist/lib/cubing/kpuzzle/index.js.map +7 -0
  99. package/dist/lib/cubing/notation/index.d.ts +32 -0
  100. package/dist/lib/cubing/notation/index.js +20 -0
  101. package/dist/lib/cubing/notation/index.js.map +7 -0
  102. package/dist/lib/cubing/outside-BCzgSYQ_.d.ts +38 -0
  103. package/dist/lib/cubing/protocol/index.d.ts +28 -0
  104. package/dist/lib/cubing/protocol/index.js +20 -0
  105. package/dist/lib/cubing/protocol/index.js.map +7 -0
  106. package/dist/lib/cubing/puzzle-geometry/index.d.ts +2 -0
  107. package/dist/lib/cubing/puzzle-geometry/index.js +5024 -0
  108. package/dist/lib/cubing/puzzle-geometry/index.js.map +7 -0
  109. package/dist/lib/cubing/puzzles/index.d.ts +2 -0
  110. package/dist/lib/cubing/puzzles/index.js +23 -0
  111. package/dist/lib/cubing/puzzles/index.js.map +7 -0
  112. package/dist/lib/cubing/scramble/index.d.ts +2 -0
  113. package/dist/lib/cubing/scramble/index.js +16 -0
  114. package/dist/lib/cubing/scramble/index.js.map +7 -0
  115. package/dist/lib/cubing/search/index.d.ts +7 -0
  116. package/dist/lib/cubing/search/index.js +37 -0
  117. package/dist/lib/cubing/search/index.js.map +7 -0
  118. package/dist/lib/cubing/stream/index.d.ts +2 -0
  119. package/dist/lib/cubing/stream/index.js +111 -0
  120. package/dist/lib/cubing/stream/index.js.map +7 -0
  121. package/dist/lib/cubing/twisty/index.d.ts +2 -0
  122. package/dist/lib/cubing/twisty/index.js +6236 -0
  123. package/dist/lib/cubing/twisty/index.js.map +7 -0
  124. package/experimental-json-schema/kpuzzle/KPatternData.schema.json +34 -0
  125. package/experimental-json-schema/kpuzzle/KPuzzleDefinition.schema.json +97 -0
  126. package/experimental-json-schema/kpuzzle/KTransformationData.schema.json +27 -0
  127. package/kpuzzle/index.d.ts +1 -0
  128. package/notation/index.d.ts +1 -0
  129. package/package.json +146 -0
  130. package/protocol/index.d.ts +1 -0
  131. package/puzzle-geometry/index.d.ts +1 -0
  132. package/puzzles/index.d.ts +1 -0
  133. package/scramble/index.d.ts +1 -0
  134. package/search/index.d.ts +1 -0
  135. package/stream/index.d.ts +1 -0
  136. package/twisty/index.d.ts +1 -0
@@ -0,0 +1,5024 @@
1
+ import {
2
+ Move,
3
+ QuantumMove
4
+ } from "../chunks/chunk-XPDJUSZ7.js";
5
+
6
+ // src/cubing/puzzle-geometry/colors.ts
7
+ function defaultPlatonicColorSchemes() {
8
+ return {
9
+ // the colors should use the same naming convention as the nets, above.
10
+ 4: {
11
+ F: "#44ee00" /* Green */,
12
+ D: "#f4f400" /* Yellow */,
13
+ L: "#ff0000" /* Red */,
14
+ R: "#2266ff" /* Blue */
15
+ },
16
+ 6: {
17
+ U: "#ffffff" /* White */,
18
+ F: "#44ee00" /* Green */,
19
+ R: "#ff0000" /* Red */,
20
+ D: "#f4f400" /* Yellow */,
21
+ B: "#2266ff" /* Blue */,
22
+ L: "#ff8000" /* Orange */
23
+ },
24
+ 8: {
25
+ U: "#ffffff" /* White */,
26
+ F: "#44ee00" /* Green */,
27
+ R: "#ff0000" /* Red */,
28
+ D: "#f4f400" /* Yellow */,
29
+ BB: "#2266ff" /* Blue */,
30
+ L: "#8800dd" /* Purple */,
31
+ BL: "#ff8000" /* Orange */,
32
+ BR: "#888888" /* MediumGray */
33
+ },
34
+ 12: {
35
+ U: "#ffffff" /* White */,
36
+ F: "#008800" /* DarkGreen */,
37
+ R: "#ff0000" /* Red */,
38
+ C: "#e8d0a0" /* Cream */,
39
+ A: "#3399ff" /* Aqua */,
40
+ L: "#8800dd" /* Purple */,
41
+ E: "#ff66cc" /* Pink */,
42
+ BF: "#99ff00" /* Lime */,
43
+ BR: "#0000ff" /* BoldBlue */,
44
+ BL: "#f4f400" /* Yellow */,
45
+ I: "#ff8000" /* Orange */,
46
+ D: "#888888" /* MediumGray */
47
+ },
48
+ 20: {
49
+ R: "#f4f400" /* Yellow */,
50
+ C: "#d41f69" /* Cerise */,
51
+ F: "#008800" /* DarkGreen */,
52
+ E: "#5c5c5c" /* DarkGray */,
53
+ L: "#8800dd" /* Purple */,
54
+ U: "#ffffff" /* White */,
55
+ A: "#007a89" /* Teal */,
56
+ G: "#ff0000" /* Red */,
57
+ I: "#7d3b11" /* Brown */,
58
+ S: "#b9a1ff" /* Lavender */,
59
+ H: "#3399ff" /* Aqua */,
60
+ J: "#5ec4b6" /* SeaGreen */,
61
+ B: "#44ee00" /* Green */,
62
+ K: "#e8d0a0" /* Cream */,
63
+ D: "#aaaaaa" /* LightGray */,
64
+ M: "#ff66cc" /* Pink */,
65
+ O: "#292929" /* harcoal */,
66
+ P: "#ff8000" /* Orange */,
67
+ N: "#980000" /* Burgundy */,
68
+ Q: "#0000ff" /* BoldBlue */
69
+ }
70
+ };
71
+ }
72
+
73
+ // src/cubing/puzzle-geometry/FaceNameSwizzler.ts
74
+ var FaceNameSwizzler = class {
75
+ constructor(facenames, gripnames_arg) {
76
+ this.facenames = facenames;
77
+ if (gripnames_arg) {
78
+ this.gripnames = gripnames_arg;
79
+ }
80
+ for (let i = 0; this.prefixFree && i < facenames.length; i++) {
81
+ for (let j = 0; this.prefixFree && j < facenames.length; j++) {
82
+ if (i !== j && facenames[i].startsWith(facenames[j])) {
83
+ this.prefixFree = false;
84
+ }
85
+ }
86
+ }
87
+ }
88
+ facenames;
89
+ prefixFree = true;
90
+ gripnames = [];
91
+ setGripNames(names) {
92
+ this.gripnames = names;
93
+ }
94
+ // split a string into face names and return a list of
95
+ // indices.
96
+ splitByFaceNames(s) {
97
+ const r = [];
98
+ let at = 0;
99
+ while (at < s.length) {
100
+ if (at > 0 && at < s.length && s[at] === "_") {
101
+ at++;
102
+ }
103
+ let currentMatch = -1;
104
+ for (let i = 0; i < this.facenames.length; i++) {
105
+ if (s.substr(at).startsWith(this.facenames[i]) && (currentMatch < 0 || this.facenames[i].length > this.facenames[currentMatch].length)) {
106
+ currentMatch = i;
107
+ }
108
+ }
109
+ if (currentMatch >= 0) {
110
+ r.push(currentMatch);
111
+ at += this.facenames[currentMatch].length;
112
+ } else {
113
+ throw new Error(`Could not split ${s} into face names.`);
114
+ }
115
+ }
116
+ return r;
117
+ }
118
+ // cons a grip from an array of numbers.
119
+ joinByFaceIndices(list) {
120
+ let sep = "";
121
+ const r = [];
122
+ for (let i = 0; i < list.length; i++) {
123
+ r.push(sep);
124
+ r.push(this.facenames[list[i]]);
125
+ if (!this.prefixFree) {
126
+ sep = "_";
127
+ }
128
+ }
129
+ return r.join("");
130
+ }
131
+ /*
132
+ * Try to match something the user gave us with some geometric
133
+ * feature. We used to have strict requirements:
134
+ *
135
+ * a) The set of face names are prefix free
136
+ * b) When specifying a corner, all coincident planes were
137
+ * specified
138
+ *
139
+ * But, to allow megaminx to have more reasonable and
140
+ * conventional names, and to permit shorter canonical
141
+ * names, we are relaxing these requirements and adding
142
+ * new syntax. Now:
143
+ *
144
+ * a) Face names need not be syntax free.
145
+ * b) When parsing a geometric name, we use greedy
146
+ * matching, so the longest name that matches the
147
+ * user string at the current position is the one
148
+ * assumed to match.
149
+ * c) Underscores are permitted to separate face names
150
+ * (both in user input and in geometric
151
+ * descriptions).
152
+ * d) Default names of corner moves where corners have
153
+ * more than three corners, need only include three
154
+ * of the corners.
155
+ *
156
+ * This code is not performance-sensitive so we can do it a
157
+ * slow and simple way.
158
+ */
159
+ spinmatch(userinput, longname) {
160
+ if (userinput === longname) {
161
+ return true;
162
+ }
163
+ try {
164
+ const e1 = this.splitByFaceNames(userinput);
165
+ const e2 = this.splitByFaceNames(longname);
166
+ if (e1.length !== e2.length && e1.length < 3) {
167
+ return false;
168
+ }
169
+ for (let i = 0; i < e1.length; i++) {
170
+ for (let j = 0; j < i; j++) {
171
+ if (e1[i] === e1[j]) {
172
+ return false;
173
+ }
174
+ }
175
+ let found = false;
176
+ for (let j = 0; j < e2.length; j++) {
177
+ if (e1[i] === e2[j]) {
178
+ found = true;
179
+ break;
180
+ }
181
+ }
182
+ if (!found) {
183
+ return false;
184
+ }
185
+ }
186
+ return true;
187
+ } catch {
188
+ return false;
189
+ }
190
+ }
191
+ /* same as above, but permit both to have v's on the end. */
192
+ spinmatchv(userinput, longname) {
193
+ if (userinput.endsWith("v") && longname.endsWith("v")) {
194
+ return this.spinmatch(
195
+ userinput.slice(0, userinput.length - 1),
196
+ longname.slice(0, longname.length - 1)
197
+ );
198
+ } else {
199
+ return this.spinmatch(userinput, longname);
200
+ }
201
+ }
202
+ unswizzle(s) {
203
+ if ((s.endsWith("v") || s.endsWith("w")) && s[0] <= "Z") {
204
+ s = s.slice(0, s.length - 1);
205
+ }
206
+ const upperCaseGrip = s.toUpperCase();
207
+ for (let i = 0; i < this.gripnames.length; i++) {
208
+ const g = this.gripnames[i];
209
+ if (this.spinmatch(upperCaseGrip, g)) {
210
+ return g;
211
+ }
212
+ }
213
+ return s;
214
+ }
215
+ };
216
+
217
+ // src/cubing/puzzle-geometry/notation-mapping/FaceRenamingMapper.ts
218
+ var FaceRenamingMapper = class {
219
+ constructor(internalNames, externalNames) {
220
+ this.internalNames = internalNames;
221
+ this.externalNames = externalNames;
222
+ }
223
+ internalNames;
224
+ externalNames;
225
+ // TODO: consider putting a cache in front of this
226
+ convertString(grip, a, b) {
227
+ let suffix = "";
228
+ if ((grip.endsWith("v") || grip.endsWith("v")) && grip <= "_") {
229
+ suffix = grip.slice(grip.length - 1);
230
+ grip = grip.slice(0, grip.length - 1);
231
+ }
232
+ const upper = grip.toUpperCase();
233
+ let isLowerCase = false;
234
+ if (grip !== upper) {
235
+ isLowerCase = true;
236
+ grip = upper;
237
+ }
238
+ grip = b.joinByFaceIndices(a.splitByFaceNames(grip));
239
+ if (isLowerCase) {
240
+ grip = grip.toLowerCase();
241
+ }
242
+ return grip + suffix;
243
+ }
244
+ convert(move, a, b) {
245
+ const grip = move.family;
246
+ const ngrip = this.convertString(grip, a, b);
247
+ if (grip === ngrip) {
248
+ return move;
249
+ } else {
250
+ return new Move(
251
+ new QuantumMove(ngrip, move.innerLayer, move.outerLayer),
252
+ move.amount
253
+ );
254
+ }
255
+ }
256
+ notationToInternal(move) {
257
+ const r = this.convert(move, this.externalNames, this.internalNames);
258
+ return r;
259
+ }
260
+ notationToExternal(move) {
261
+ return this.convert(move, this.internalNames, this.externalNames);
262
+ }
263
+ };
264
+
265
+ // src/cubing/puzzle-geometry/notation-mapping/FTONotationMapper.ts
266
+ var FTONotationMapper = class {
267
+ constructor(child, sw) {
268
+ this.child = child;
269
+ this.sw = sw;
270
+ }
271
+ child;
272
+ sw;
273
+ notationToInternal(move) {
274
+ if (move.family === "T" && move.innerLayer === void 0 && move.outerLayer === void 0) {
275
+ return new Move(
276
+ new QuantumMove("FLRv", move.innerLayer, move.outerLayer),
277
+ move.amount
278
+ );
279
+ } else {
280
+ const r = this.child.notationToInternal(move);
281
+ return r;
282
+ }
283
+ }
284
+ // we never rewrite click moves to these moves.
285
+ notationToExternal(move) {
286
+ let fam = move.family;
287
+ if (fam.length > 0 && fam[fam.length - 1] === "v") {
288
+ fam = fam.substring(0, fam.length - 1);
289
+ }
290
+ if (this.sw.spinmatch(fam, "FLUR")) {
291
+ return new Move(
292
+ new QuantumMove("T", move.innerLayer, move.outerLayer),
293
+ move.amount
294
+ );
295
+ }
296
+ return this.child.notationToExternal(move);
297
+ }
298
+ };
299
+
300
+ // src/cubing/puzzle-geometry/notation-mapping/MegaminxScramblingNotationMapper.ts
301
+ var MegaminxScramblingNotationMapper = class {
302
+ constructor(child) {
303
+ this.child = child;
304
+ }
305
+ child;
306
+ notationToInternal(move) {
307
+ if (move.innerLayer === void 0 && move.outerLayer === void 0) {
308
+ if (Math.abs(move.amount) === 1) {
309
+ if (move.family === "R++") {
310
+ return new Move(new QuantumMove("L", 3, 2), -2 * move.amount);
311
+ } else if (move.family === "R--") {
312
+ return new Move(new QuantumMove("L", 3, 2), 2 * move.amount);
313
+ } else if (move.family === "D++") {
314
+ return new Move(new QuantumMove("U", 3, 2), -2 * move.amount);
315
+ } else if (move.family === "D--") {
316
+ return new Move(new QuantumMove("U", 3, 2), 2 * move.amount);
317
+ }
318
+ if (move.family === "R_PLUSPLUS_") {
319
+ return new Move(new QuantumMove("L", 3, 2), -2 * move.amount);
320
+ } else if (move.family === "D_PLUSPLUS_") {
321
+ return new Move(new QuantumMove("U", 3, 2), -2 * move.amount);
322
+ }
323
+ }
324
+ if (move.family === "y") {
325
+ return new Move("Uv", move.amount);
326
+ }
327
+ if (move.family === "x" && Math.abs(move.amount) === 2) {
328
+ return new Move("ERv", move.amount / 2);
329
+ }
330
+ }
331
+ return this.child.notationToInternal(move);
332
+ }
333
+ // we never rewrite click moves to these moves.
334
+ notationToExternal(move) {
335
+ if (move.family === "ERv" && Math.abs(move.amount) === 1) {
336
+ return new Move(
337
+ new QuantumMove("x", move.innerLayer, move.outerLayer),
338
+ move.amount * 2
339
+ );
340
+ }
341
+ if (move.family === "ILv" && Math.abs(move.amount) === 1) {
342
+ return new Move(
343
+ new QuantumMove("x", move.innerLayer, move.outerLayer),
344
+ -move.amount * 2
345
+ );
346
+ }
347
+ if (move.family === "Uv") {
348
+ return new Move(
349
+ new QuantumMove("y", move.innerLayer, move.outerLayer),
350
+ move.amount
351
+ );
352
+ }
353
+ if (move.family === "Dv") {
354
+ return new Move("y", -move.amount);
355
+ }
356
+ return this.child.notationToExternal(move);
357
+ }
358
+ };
359
+
360
+ // src/cubing/puzzle-geometry/notation-mapping/NullMapper.ts
361
+ var NullMapper = class {
362
+ notationToInternal(move) {
363
+ return move;
364
+ }
365
+ notationToExternal(move) {
366
+ return move;
367
+ }
368
+ };
369
+
370
+ // src/cubing/puzzle-geometry/notation-mapping/NxNxNCubeMapper.ts
371
+ var NxNxNCubeMapper = class {
372
+ constructor(slices) {
373
+ this.slices = slices;
374
+ }
375
+ slices;
376
+ notationToInternal(move) {
377
+ const grip = move.family;
378
+ if (!(move.innerLayer || move.outerLayer)) {
379
+ if (grip === "x") {
380
+ move = new Move("Rv", move.amount);
381
+ } else if (grip === "y") {
382
+ move = new Move("Uv", move.amount);
383
+ } else if (grip === "z") {
384
+ move = new Move("Fv", move.amount);
385
+ }
386
+ if ((this.slices & 1) === 1) {
387
+ if (grip === "E") {
388
+ move = new Move(
389
+ new QuantumMove("D", (this.slices + 1) / 2),
390
+ move.amount
391
+ );
392
+ } else if (grip === "M") {
393
+ move = new Move(
394
+ new QuantumMove("L", (this.slices + 1) / 2),
395
+ move.amount
396
+ );
397
+ } else if (grip === "S") {
398
+ move = new Move(
399
+ new QuantumMove("F", (this.slices + 1) / 2),
400
+ move.amount
401
+ );
402
+ }
403
+ }
404
+ if (this.slices > 2) {
405
+ if (grip === "e") {
406
+ move = new Move(
407
+ new QuantumMove("D", this.slices - 1, 2),
408
+ move.amount
409
+ );
410
+ } else if (grip === "m") {
411
+ move = new Move(
412
+ new QuantumMove("L", this.slices - 1, 2),
413
+ move.amount
414
+ );
415
+ } else if (grip === "s") {
416
+ move = new Move(
417
+ new QuantumMove("F", this.slices - 1, 2),
418
+ move.amount
419
+ );
420
+ }
421
+ }
422
+ }
423
+ return move;
424
+ }
425
+ // do we want to map slice moves to E/M/S instead of 2U/etc.?
426
+ notationToExternal(move) {
427
+ const grip = move.family;
428
+ if (!(move.innerLayer || move.outerLayer)) {
429
+ if (grip === "Rv") {
430
+ return new Move("x", move.amount);
431
+ } else if (grip === "Uv") {
432
+ return new Move("y", move.amount);
433
+ } else if (grip === "Fv") {
434
+ return new Move("z", move.amount);
435
+ } else if (grip === "Lv") {
436
+ return new Move("x", -move.amount);
437
+ } else if (grip === "Dv") {
438
+ return new Move("y", -move.amount);
439
+ } else if (grip === "Bv") {
440
+ return new Move("z", -move.amount);
441
+ }
442
+ }
443
+ return move;
444
+ }
445
+ };
446
+
447
+ // src/cubing/puzzle-geometry/notation-mapping/PyraminxNotationMapper.ts
448
+ var pyraminxFamilyMap = {
449
+ U: "frl",
450
+ L: "fld",
451
+ R: "fdr",
452
+ B: "dlr",
453
+ u: "FRL",
454
+ l: "FLD",
455
+ r: "FDR",
456
+ b: "DLR",
457
+ Uv: "FRLv",
458
+ Lv: "FLDv",
459
+ Rv: "FDRv",
460
+ Bv: "DLRv",
461
+ D: "D",
462
+ F: "F",
463
+ BL: "L",
464
+ BR: "R"
465
+ };
466
+ var tetraminxFamilyMap = {
467
+ U: "FRL",
468
+ L: "FLD",
469
+ R: "FDR",
470
+ B: "DLR",
471
+ u: "frl",
472
+ l: "fld",
473
+ r: "fdr",
474
+ b: "dlr",
475
+ Uv: "FRLv",
476
+ Lv: "FLDv",
477
+ Rv: "FDRv",
478
+ Bv: "DLRv",
479
+ D: "D",
480
+ F: "F",
481
+ BL: "L",
482
+ BR: "R",
483
+ d: "d",
484
+ f: "f",
485
+ bl: "l",
486
+ br: "r"
487
+ };
488
+ var pyraminxFamilyMapWCA = {
489
+ U: "FRL",
490
+ L: "FLD",
491
+ R: "FDR",
492
+ B: "DLR"
493
+ };
494
+ var pyraminxExternalQuantumY = new QuantumMove("y");
495
+ var pyraminxInternalQuantumY = new QuantumMove("Dv");
496
+ var PyraminxNotationMapper = class {
497
+ constructor(child) {
498
+ this.child = child;
499
+ }
500
+ child;
501
+ wcaHack = false;
502
+ map = pyraminxFamilyMap;
503
+ notationToInternal(move) {
504
+ if (this.wcaHack && move.innerLayer === 2 && move.outerLayer === null) {
505
+ const newFamilyWCA = pyraminxFamilyMapWCA[move.family];
506
+ if (newFamilyWCA) {
507
+ return new Move(
508
+ new QuantumMove(newFamilyWCA, move.innerLayer, move.outerLayer),
509
+ move.amount
510
+ );
511
+ }
512
+ }
513
+ const newFamily = this.map[move.family];
514
+ if (newFamily) {
515
+ return new Move(
516
+ new QuantumMove(newFamily, move.innerLayer, move.outerLayer),
517
+ move.amount
518
+ );
519
+ } else if (pyraminxExternalQuantumY.isIdentical(move.quantum)) {
520
+ return new Move(pyraminxInternalQuantumY, -move.amount);
521
+ } else {
522
+ return null;
523
+ }
524
+ }
525
+ // we never rewrite click moves to these moves.
526
+ notationToExternal(move) {
527
+ if (this.wcaHack && move.innerLayer === 2 && move.outerLayer === null) {
528
+ for (const [external, internal] of Object.entries(pyraminxFamilyMapWCA)) {
529
+ if (this.child.spinmatch(move.family, internal)) {
530
+ return new Move(
531
+ new QuantumMove(external, move.innerLayer, move.outerLayer),
532
+ move.amount
533
+ );
534
+ }
535
+ }
536
+ }
537
+ for (const [external, internal] of Object.entries(this.map)) {
538
+ if (this.child.spinmatch(move.family, internal)) {
539
+ return new Move(
540
+ new QuantumMove(external, move.innerLayer, move.outerLayer),
541
+ move.amount
542
+ );
543
+ }
544
+ }
545
+ if (pyraminxInternalQuantumY.isIdentical(move.quantum)) {
546
+ return new Move(pyraminxExternalQuantumY, -move.amount);
547
+ } else {
548
+ return null;
549
+ }
550
+ }
551
+ };
552
+ var TetraminxNotationMapper = class extends PyraminxNotationMapper {
553
+ wcaHack = true;
554
+ constructor(child) {
555
+ super(child);
556
+ this.map = tetraminxFamilyMap;
557
+ }
558
+ };
559
+
560
+ // src/cubing/puzzle-geometry/notation-mapping/SkewbNotationMapper.ts
561
+ var skewbFamilyMap = {
562
+ U: "UBL",
563
+ UL: "ULF",
564
+ F: "UFR",
565
+ UR: "URB",
566
+ B: "DBL",
567
+ D: "DFR",
568
+ L: "DLF",
569
+ R: "DRB",
570
+ Uv: "UBLv",
571
+ ULv: "ULFv",
572
+ Fv: "UFRv",
573
+ URv: "URBv",
574
+ Bv: "DBLv",
575
+ Dv: "DFRv",
576
+ Lv: "DLFv",
577
+ Rv: "DRBv"
578
+ };
579
+ var skewbExternalQuantumX = new QuantumMove("x");
580
+ var skewbInternalQuantumX = new QuantumMove("Rv");
581
+ var skewbInternalQuantumXPrime = new QuantumMove("Lv");
582
+ var skewbExternalQuantumY = new QuantumMove("y");
583
+ var skewbInternalQuantumY = new QuantumMove("Uv");
584
+ var skewbInternalQuantumYPrime = new QuantumMove("Dv");
585
+ var skewbExternalQuantumZ = new QuantumMove("z");
586
+ var skewbInternalQuantumZ = new QuantumMove("Fv");
587
+ var skewbInternalQuantumZPrime = new QuantumMove("Bv");
588
+ var SkewbNotationMapper = class {
589
+ constructor(child) {
590
+ this.child = child;
591
+ }
592
+ child;
593
+ notationToInternal(move) {
594
+ if (move.innerLayer || move.outerLayer) {
595
+ return null;
596
+ }
597
+ const newFamily = skewbFamilyMap[move.family];
598
+ if (newFamily) {
599
+ return new Move(
600
+ new QuantumMove(newFamily, move.outerLayer, move.innerLayer),
601
+ move.amount
602
+ );
603
+ }
604
+ if (skewbExternalQuantumX.isIdentical(move.quantum)) {
605
+ return new Move(skewbInternalQuantumX, move.amount);
606
+ }
607
+ if (skewbExternalQuantumY.isIdentical(move.quantum)) {
608
+ return new Move(skewbInternalQuantumY, move.amount);
609
+ }
610
+ if (skewbExternalQuantumZ.isIdentical(move.quantum)) {
611
+ return new Move(skewbInternalQuantumZ, move.amount);
612
+ }
613
+ return null;
614
+ }
615
+ // we never rewrite click moves to these moves.
616
+ notationToExternal(move) {
617
+ for (const [external, internal] of Object.entries(skewbFamilyMap)) {
618
+ if (this.child.spinmatchv(move.family, internal)) {
619
+ return new Move(
620
+ new QuantumMove(external, move.innerLayer, move.outerLayer),
621
+ move.amount
622
+ );
623
+ }
624
+ }
625
+ if (skewbInternalQuantumX.isIdentical(move.quantum)) {
626
+ return new Move(skewbExternalQuantumX, move.amount);
627
+ }
628
+ if (skewbInternalQuantumXPrime.isIdentical(move.quantum)) {
629
+ return new Move(skewbExternalQuantumX, -move.amount);
630
+ }
631
+ if (skewbInternalQuantumY.isIdentical(move.quantum)) {
632
+ return new Move(skewbExternalQuantumY, move.amount);
633
+ }
634
+ if (skewbInternalQuantumYPrime.isIdentical(move.quantum)) {
635
+ return new Move(skewbExternalQuantumY, -move.amount);
636
+ }
637
+ if (skewbInternalQuantumZ.isIdentical(move.quantum)) {
638
+ return new Move(skewbExternalQuantumZ, move.amount);
639
+ }
640
+ if (skewbInternalQuantumZPrime.isIdentical(move.quantum)) {
641
+ return new Move(skewbExternalQuantumZ, -move.amount);
642
+ }
643
+ return null;
644
+ }
645
+ };
646
+
647
+ // src/cubing/puzzle-geometry/notation-mapping/NotationMapper.ts
648
+ function remapKPuzzleDefinition(internalDefinition, notationMapper) {
649
+ const externalDefinition = {
650
+ ...internalDefinition,
651
+ moves: {}
652
+ };
653
+ for (const [internalMoveName, transformationData] of Object.entries(
654
+ internalDefinition.moves
655
+ )) {
656
+ let prefix = internalMoveName;
657
+ let suffix = "";
658
+ if (["v", "w"].includes(internalMoveName.at(-1))) {
659
+ prefix = internalMoveName.slice(0, -1);
660
+ suffix = internalMoveName.slice(-1);
661
+ }
662
+ const externalPrefix = notationMapper.notationToExternal(
663
+ Move.fromString(prefix)
664
+ );
665
+ if (!externalPrefix) {
666
+ continue;
667
+ }
668
+ const externalMoveName = externalPrefix + suffix;
669
+ if (!externalMoveName) {
670
+ throw new Error(
671
+ `Missing external move name for: ${internalMoveName.toString()}`
672
+ );
673
+ }
674
+ externalDefinition.moves[externalMoveName.toString()] = transformationData;
675
+ }
676
+ return externalDefinition;
677
+ }
678
+
679
+ // src/cubing/puzzle-geometry/Options.ts
680
+ var PuzzleGeometryFullOptions = class {
681
+ verbosity = 0;
682
+ // verbosity (console.log)
683
+ allMoves = false;
684
+ // generate all slice moves in ksolve
685
+ outerBlockMoves = false;
686
+ // generate outer block moves
687
+ vertexMoves = false;
688
+ // generate vertex moves
689
+ addRotations = false;
690
+ // add symmetry information to ksolve output
691
+ moveList = null;
692
+ // move list to generate
693
+ fixedOrientation = false;
694
+ // eliminate any orientations
695
+ fixedPieceType = null;
696
+ // fix a piece?
697
+ orientCenters = false;
698
+ // orient centers?
699
+ // TODO: Group these into a single object?
700
+ includeCornerOrbits = true;
701
+ // include corner orbits
702
+ includeCenterOrbits = true;
703
+ // include center orbits
704
+ includeEdgeOrbits = true;
705
+ // include edge orbits
706
+ // Overrides the previous options.
707
+ excludeOrbits = [];
708
+ // exclude these orbits
709
+ optimizeOrbits = false;
710
+ // optimize PermOri
711
+ grayCorners = false;
712
+ // make corner sets gray
713
+ grayCenters = false;
714
+ // make center sets gray
715
+ grayEdges = false;
716
+ // make edge sets gray
717
+ puzzleOrientation = null;
718
+ // single puzzle orientation from options
719
+ puzzleOrientations = null;
720
+ // puzzle orientation override object from options // TODO: is this needed?
721
+ scrambleAmount = 0;
722
+ // scramble?
723
+ constructor(options = {}) {
724
+ const optionsWithoutUndefined = Object.fromEntries(
725
+ Object.entries(options).filter(
726
+ ([_, value]) => typeof value !== "undefined"
727
+ )
728
+ );
729
+ Object.assign(this, optionsWithoutUndefined);
730
+ }
731
+ };
732
+
733
+ // src/cubing/puzzle-geometry/Perm.ts
734
+ var zeroCache = [];
735
+ var iotaCache = [];
736
+ function zeros(n) {
737
+ if (!zeroCache[n]) {
738
+ const c = Array(n);
739
+ for (let i = 0; i < n; i++) {
740
+ c[i] = 0;
741
+ }
742
+ zeroCache[n] = c;
743
+ }
744
+ return zeroCache[n];
745
+ }
746
+ function iota(n) {
747
+ if (!iotaCache[n]) {
748
+ const c = Array(n);
749
+ for (let i = 0; i < n; i++) {
750
+ c[i] = i;
751
+ }
752
+ iotaCache[n] = c;
753
+ }
754
+ return iotaCache[n];
755
+ }
756
+ function identity(n) {
757
+ return new Perm(iota(n));
758
+ }
759
+ function factorial(a) {
760
+ let r = BigInt(1);
761
+ while (a > 1) {
762
+ r *= BigInt(a);
763
+ a--;
764
+ }
765
+ return r;
766
+ }
767
+ function gcd(a, b) {
768
+ if (a > b) {
769
+ const t = a;
770
+ a = b;
771
+ b = t;
772
+ }
773
+ while (a > 0) {
774
+ const m = b % a;
775
+ b = a;
776
+ a = m;
777
+ }
778
+ return b;
779
+ }
780
+ function lcm(a, b) {
781
+ return a / gcd(a, b) * b;
782
+ }
783
+ var Perm = class _Perm {
784
+ n;
785
+ // length
786
+ p;
787
+ // The permutation itself
788
+ constructor(a) {
789
+ this.n = a.length;
790
+ this.p = a;
791
+ }
792
+ toString() {
793
+ return `Perm[${this.p.join(" ")}]`;
794
+ }
795
+ mul(p2) {
796
+ const c = Array(this.n);
797
+ for (let i = 0; i < this.n; i++) {
798
+ c[i] = p2.p[this.p[i]];
799
+ }
800
+ return new _Perm(c);
801
+ }
802
+ rmul(p2) {
803
+ const c = Array(this.n);
804
+ for (let i = 0; i < this.n; i++) {
805
+ c[i] = this.p[p2.p[i]];
806
+ }
807
+ return new _Perm(c);
808
+ }
809
+ inv() {
810
+ const c = Array(this.n);
811
+ for (let i = 0; i < this.n; i++) {
812
+ c[this.p[i]] = i;
813
+ }
814
+ return new _Perm(c);
815
+ }
816
+ compareTo(p2) {
817
+ for (let i = 0; i < this.n; i++) {
818
+ if (this.p[i] !== p2.p[i]) {
819
+ return this.p[i] - p2.p[i];
820
+ }
821
+ }
822
+ return 0;
823
+ }
824
+ toGap() {
825
+ const cyc = [];
826
+ const seen = new Array(this.n);
827
+ for (let i = 0; i < this.p.length; i++) {
828
+ if (seen[i] || this.p[i] === i) {
829
+ continue;
830
+ }
831
+ const incyc = [];
832
+ for (let j = this.p[i]; !seen[j]; j = this.p[j]) {
833
+ incyc.push(1 + j);
834
+ seen[j] = true;
835
+ }
836
+ cyc.push(`(${incyc.reverse().join(",")})`);
837
+ }
838
+ return cyc.join("");
839
+ }
840
+ toMathematica() {
841
+ const cyc = [];
842
+ const seen = new Array(this.n);
843
+ for (let i = 0; i < this.p.length; i++) {
844
+ if (seen[i] || this.p[i] === i) {
845
+ continue;
846
+ }
847
+ const incyc = [];
848
+ for (let j = this.p[i]; !seen[j]; j = this.p[j]) {
849
+ incyc.push(1 + j);
850
+ seen[j] = true;
851
+ }
852
+ cyc.push(`{${incyc.reverse().join(",")}}`);
853
+ }
854
+ return `Cycles[{${cyc.join(",")}}]`;
855
+ }
856
+ order() {
857
+ let r = 1;
858
+ const seen = new Array(this.n);
859
+ for (let i = 0; i < this.p.length; i++) {
860
+ if (seen[i] || this.p[i] === i) {
861
+ continue;
862
+ }
863
+ let cs = 0;
864
+ for (let j = i; !seen[j]; j = this.p[j]) {
865
+ cs++;
866
+ seen[j] = true;
867
+ }
868
+ r = lcm(r, cs);
869
+ }
870
+ return r;
871
+ }
872
+ };
873
+
874
+ // src/cubing/puzzle-geometry/PermOriSet.ts
875
+ var PGOrbitDef = class {
876
+ constructor(size, mod) {
877
+ this.size = size;
878
+ this.mod = mod;
879
+ }
880
+ size;
881
+ mod;
882
+ reassemblySize() {
883
+ return factorial(this.size) * BigInt(this.mod) ** BigInt(this.size);
884
+ }
885
+ };
886
+ var lastGlobalDefinitionCounter = 0;
887
+ function externalName(mapper, moveString) {
888
+ const mv = Move.fromString(moveString);
889
+ const mv2 = mapper.notationToExternal(mv);
890
+ if (mv2 === null || mv === mv2) {
891
+ return moveString;
892
+ }
893
+ return mv2.toString();
894
+ }
895
+ var PGOrbitsDef = class _PGOrbitsDef {
896
+ constructor(orbitnames, orbitdefs, solved, movenames, moveops, isRotation, forcenames) {
897
+ this.orbitnames = orbitnames;
898
+ this.orbitdefs = orbitdefs;
899
+ this.solved = solved;
900
+ this.movenames = movenames;
901
+ this.moveops = moveops;
902
+ this.isRotation = isRotation;
903
+ this.forcenames = forcenames;
904
+ }
905
+ orbitnames;
906
+ orbitdefs;
907
+ solved;
908
+ movenames;
909
+ moveops;
910
+ isRotation;
911
+ forcenames;
912
+ toKTransformationData(t) {
913
+ const ktransformationData = {};
914
+ for (let i = 0; i < this.orbitnames.length; i++) {
915
+ ktransformationData[this.orbitnames[i]] = t.orbits[i].toKTransformationOrbitData();
916
+ }
917
+ return ktransformationData;
918
+ }
919
+ toKPatternData(t) {
920
+ const kpatternData = {};
921
+ for (let i = 0; i < this.orbitnames.length; i++) {
922
+ kpatternData[this.orbitnames[i]] = t.orbits[i].toKPatternOrbitData();
923
+ }
924
+ return kpatternData;
925
+ }
926
+ // TODO: remove this
927
+ static transformToKTransformationData(orbitnames, t) {
928
+ const mp = {};
929
+ for (let j = 0; j < orbitnames.length; j++) {
930
+ mp[orbitnames[j]] = t.orbits[j].toKTransformationOrbitData();
931
+ }
932
+ return mp;
933
+ }
934
+ describeSet(s, r, mapper) {
935
+ const n = this.orbitdefs[s].size;
936
+ const m = new Array(n);
937
+ for (let i = 0; i < n; i++) {
938
+ m[i] = [];
939
+ }
940
+ for (let i = 0; i < this.movenames.length; i++) {
941
+ if (this.isRotation[i]) {
942
+ continue;
943
+ }
944
+ let mvname = this.movenames[i];
945
+ if (!this.forcenames[i]) {
946
+ mvname = externalName(mapper, mvname);
947
+ if (mvname[mvname.length - 1] === "'") {
948
+ mvname = mvname.substring(0, mvname.length - 1);
949
+ }
950
+ }
951
+ const pd = this.moveops[i].orbits[s];
952
+ for (let j = 0; j < n; j++) {
953
+ if (pd.perm[j] !== j || pd.ori[j] !== 0) {
954
+ m[j].push(mvname);
955
+ }
956
+ }
957
+ }
958
+ for (let j = 0; j < n; j++) {
959
+ r.push(`# ${j + 1} ${m[j].join(" ")}`);
960
+ }
961
+ }
962
+ toKsolve(name, mapper = new NullMapper()) {
963
+ const result = [];
964
+ result.push(`Name ${name}`);
965
+ result.push("");
966
+ for (let i = 0; i < this.orbitnames.length; i++) {
967
+ result.push(
968
+ `Set ${this.orbitnames[i]} ${this.orbitdefs[i].size} ${this.orbitdefs[i].mod}`
969
+ );
970
+ this.describeSet(i, result, mapper);
971
+ }
972
+ result.push("");
973
+ result.push("Solved");
974
+ for (let i = 0; i < this.orbitnames.length; i++) {
975
+ this.solved.orbits[i].appendDefinition(
976
+ result,
977
+ this.orbitnames[i],
978
+ false,
979
+ false
980
+ );
981
+ }
982
+ result.push("End");
983
+ for (let i = 0; i < this.movenames.length; i++) {
984
+ result.push("");
985
+ let name2 = this.movenames[i];
986
+ if (!this.forcenames[i]) {
987
+ name2 = externalName(mapper, this.movenames[i]);
988
+ }
989
+ let doinv = false;
990
+ if (name2[name2.length - 1] === "'") {
991
+ doinv = true;
992
+ name2 = name2.substring(0, name2.length - 1);
993
+ }
994
+ result.push(`Move ${name2}`);
995
+ for (let j = 0; j < this.orbitnames.length; j++) {
996
+ if (doinv) {
997
+ this.moveops[i].orbits[j].inv().appendDefinition(result, this.orbitnames[j], true);
998
+ } else {
999
+ this.moveops[i].orbits[j].appendDefinition(
1000
+ result,
1001
+ this.orbitnames[j],
1002
+ true
1003
+ );
1004
+ }
1005
+ }
1006
+ result.push("End");
1007
+ }
1008
+ return result;
1009
+ }
1010
+ // TODO: return type.
1011
+ toKPuzzleDefinition(includemoves) {
1012
+ const orbits = [];
1013
+ const defaultPatternData = {};
1014
+ for (let i = 0; i < this.orbitnames.length; i++) {
1015
+ orbits.push({
1016
+ orbitName: this.orbitnames[i],
1017
+ numPieces: this.orbitdefs[i].size,
1018
+ numOrientations: this.orbitdefs[i].mod
1019
+ });
1020
+ const defaultPatternFrom = this.solved.orbits[i].toKTransformationOrbitData();
1021
+ defaultPatternData[this.orbitnames[i]] = {
1022
+ pieces: defaultPatternFrom.permutation,
1023
+ orientation: defaultPatternFrom.orientationDelta
1024
+ };
1025
+ }
1026
+ const moves = {};
1027
+ if (includemoves) {
1028
+ for (let i = 0; i < this.movenames.length; i++) {
1029
+ moves[this.movenames[i]] = this.toKTransformationData(this.moveops[i]);
1030
+ }
1031
+ }
1032
+ return {
1033
+ name: `PG3D #${++lastGlobalDefinitionCounter}`,
1034
+ orbits,
1035
+ defaultPattern: defaultPatternData,
1036
+ moves
1037
+ };
1038
+ }
1039
+ optimize() {
1040
+ const neworbitnames = [];
1041
+ const neworbitdefs = [];
1042
+ const newsolved = [];
1043
+ const newmoveops = [];
1044
+ for (let j = 0; j < this.moveops.length; j++) {
1045
+ newmoveops.push([]);
1046
+ }
1047
+ for (let i = 0; i < this.orbitdefs.length; i++) {
1048
+ const om = this.orbitdefs[i].mod;
1049
+ const n = this.orbitdefs[i].size;
1050
+ const du = new DisjointUnion(n);
1051
+ const changed = new Array(this.orbitdefs[i].size);
1052
+ for (let k = 0; k < n; k++) {
1053
+ changed[k] = false;
1054
+ }
1055
+ for (let j = 0; j < this.moveops.length; j++) {
1056
+ for (let k = 0; k < n; k++) {
1057
+ if (this.moveops[j].orbits[i].perm[k] !== k || this.moveops[j].orbits[i].ori[k] !== 0) {
1058
+ if (!this.isRotation[j]) {
1059
+ changed[k] = true;
1060
+ }
1061
+ du.union(k, this.moveops[j].orbits[i].perm[k]);
1062
+ }
1063
+ }
1064
+ }
1065
+ let keepori = true;
1066
+ if (om > 1) {
1067
+ keepori = false;
1068
+ const duo = new DisjointUnion(this.orbitdefs[i].size * om);
1069
+ for (let j = 0; j < this.moveops.length; j++) {
1070
+ for (let k = 0; k < n; k++) {
1071
+ if (this.moveops[j].orbits[i].perm[k] !== k || this.moveops[j].orbits[i].ori[k] !== 0) {
1072
+ for (let o = 0; o < om; o++) {
1073
+ duo.union(
1074
+ k * om + o,
1075
+ this.moveops[j].orbits[i].perm[k] * om + (o + this.moveops[j].orbits[i].ori[k]) % om
1076
+ );
1077
+ }
1078
+ }
1079
+ }
1080
+ }
1081
+ for (let j = 0; !keepori && j < n; j++) {
1082
+ for (let o = 1; o < om; o++) {
1083
+ if (duo.find(j * om) === duo.find(j * om + o)) {
1084
+ keepori = true;
1085
+ }
1086
+ }
1087
+ }
1088
+ for (let j = 0; !keepori && j < n; j++) {
1089
+ for (let k = 0; k < j; k++) {
1090
+ if (this.solved.orbits[i].perm[j] === this.solved.orbits[i].perm[k]) {
1091
+ keepori = true;
1092
+ }
1093
+ }
1094
+ }
1095
+ }
1096
+ let nontriv = -1;
1097
+ let multiple = false;
1098
+ for (let j = 0; j < this.orbitdefs[i].size; j++) {
1099
+ if (changed[j]) {
1100
+ const h = du.find(j);
1101
+ if (nontriv < 0) {
1102
+ nontriv = h;
1103
+ } else if (nontriv !== h) {
1104
+ multiple = true;
1105
+ }
1106
+ }
1107
+ }
1108
+ for (let j = 0; j < this.orbitdefs[i].size; j++) {
1109
+ if (!changed[j]) {
1110
+ continue;
1111
+ }
1112
+ const h = du.find(j);
1113
+ if (h !== j) {
1114
+ continue;
1115
+ }
1116
+ const no = [];
1117
+ const on = [];
1118
+ let nv = 0;
1119
+ for (let k = 0; k < this.orbitdefs[i].size; k++) {
1120
+ if (du.find(k) === j) {
1121
+ no[nv] = k;
1122
+ on[k] = nv;
1123
+ nv++;
1124
+ }
1125
+ }
1126
+ if (multiple) {
1127
+ neworbitnames.push(`${this.orbitnames[i]}_p${j}`);
1128
+ } else {
1129
+ neworbitnames.push(this.orbitnames[i]);
1130
+ }
1131
+ if (keepori) {
1132
+ neworbitdefs.push(new PGOrbitDef(nv, this.orbitdefs[i].mod));
1133
+ newsolved.push(this.solved.orbits[i].remapVS(no, nv));
1134
+ for (let k = 0; k < this.moveops.length; k++) {
1135
+ newmoveops[k].push(this.moveops[k].orbits[i].remap(no, on, nv));
1136
+ }
1137
+ } else {
1138
+ neworbitdefs.push(new PGOrbitDef(nv, 1));
1139
+ newsolved.push(this.solved.orbits[i].remapVS(no, nv).killOri());
1140
+ for (let k = 0; k < this.moveops.length; k++) {
1141
+ newmoveops[k].push(
1142
+ this.moveops[k].orbits[i].remap(no, on, nv).killOri()
1143
+ );
1144
+ }
1145
+ }
1146
+ }
1147
+ }
1148
+ return new _PGOrbitsDef(
1149
+ neworbitnames,
1150
+ neworbitdefs,
1151
+ new VisibleState(newsolved),
1152
+ this.movenames,
1153
+ newmoveops.map((_) => new PGTransform(_)),
1154
+ this.isRotation,
1155
+ this.forcenames
1156
+ );
1157
+ }
1158
+ // replace the solved state with a new scrambled state.
1159
+ scramble(n) {
1160
+ this.solved = this.solved.mul(this.getScrambleTransformation(n));
1161
+ }
1162
+ // generate a new "random" position based on an entropy pool
1163
+ // this should be significantly faster and more random than just
1164
+ // doing a large number of random moves, especially on big puzzles.
1165
+ getScrambleTransformation(n) {
1166
+ if (n < 100) {
1167
+ n = 100;
1168
+ }
1169
+ const pool = [];
1170
+ for (let i = 0; i < this.moveops.length; i++) {
1171
+ pool[i] = this.moveops[i];
1172
+ }
1173
+ for (let i = 0; i < pool.length; i++) {
1174
+ const j = Math.floor(Math.random() * pool.length);
1175
+ const t = pool[i];
1176
+ pool[i] = pool[j];
1177
+ pool[j] = t;
1178
+ }
1179
+ if (n < pool.length) {
1180
+ n = pool.length;
1181
+ }
1182
+ for (let i = 0; i < n; i++) {
1183
+ const ri = Math.floor(Math.random() * pool.length);
1184
+ const rj = Math.floor(Math.random() * pool.length);
1185
+ const rm = Math.floor(Math.random() * this.moveops.length);
1186
+ pool[ri] = pool[ri].mul(pool[rj]).mul(this.moveops[rm]);
1187
+ if (Math.random() < 0.1) {
1188
+ pool[ri] = pool[ri].mul(this.moveops[rm]);
1189
+ }
1190
+ }
1191
+ let s = pool[0];
1192
+ for (let i = 1; i < pool.length; i++) {
1193
+ s = s.mul(pool[i]);
1194
+ }
1195
+ return s;
1196
+ }
1197
+ reassemblySize() {
1198
+ let n = BigInt(1);
1199
+ for (let i = 0; i < this.orbitdefs.length; i++) {
1200
+ n *= this.orbitdefs[i].reassemblySize();
1201
+ }
1202
+ return n;
1203
+ }
1204
+ };
1205
+ var PGOrbit = class _PGOrbit {
1206
+ constructor(perm, ori, orimod) {
1207
+ this.perm = perm;
1208
+ this.ori = ori;
1209
+ this.orimod = orimod;
1210
+ }
1211
+ perm;
1212
+ ori;
1213
+ orimod;
1214
+ static ktransformationCache = [];
1215
+ static e(n, mod) {
1216
+ return new _PGOrbit(iota(n), zeros(n), mod);
1217
+ }
1218
+ mul(b) {
1219
+ const n = this.perm.length;
1220
+ const newPerm = new Array(n);
1221
+ if (this.orimod === 1) {
1222
+ for (let i = 0; i < n; i++) {
1223
+ newPerm[i] = this.perm[b.perm[i]];
1224
+ }
1225
+ return new _PGOrbit(newPerm, this.ori, this.orimod);
1226
+ } else {
1227
+ const newOri = new Array(n);
1228
+ for (let i = 0; i < n; i++) {
1229
+ newPerm[i] = this.perm[b.perm[i]];
1230
+ newOri[i] = (this.ori[b.perm[i]] + b.ori[i]) % this.orimod;
1231
+ }
1232
+ return new _PGOrbit(newPerm, newOri, this.orimod);
1233
+ }
1234
+ }
1235
+ inv() {
1236
+ const n = this.perm.length;
1237
+ const newPerm = new Array(n);
1238
+ const newOri = new Array(n);
1239
+ for (let i = 0; i < n; i++) {
1240
+ newPerm[this.perm[i]] = i;
1241
+ newOri[this.perm[i]] = (this.orimod - this.ori[i]) % this.orimod;
1242
+ }
1243
+ return new _PGOrbit(newPerm, newOri, this.orimod);
1244
+ }
1245
+ equal(b) {
1246
+ const n = this.perm.length;
1247
+ for (let i = 0; i < n; i++) {
1248
+ if (this.perm[i] !== b.perm[i] || this.ori[i] !== b.ori[i]) {
1249
+ return false;
1250
+ }
1251
+ }
1252
+ return true;
1253
+ }
1254
+ // in-place mutator
1255
+ killOri() {
1256
+ const n = this.perm.length;
1257
+ for (let i = 0; i < n; i++) {
1258
+ this.ori[i] = 0;
1259
+ }
1260
+ this.orimod = 1;
1261
+ return this;
1262
+ }
1263
+ toPerm() {
1264
+ const o = this.orimod;
1265
+ if (o === 1) {
1266
+ return new Perm(this.perm);
1267
+ }
1268
+ const n = this.perm.length;
1269
+ const newPerm = new Array(n * o);
1270
+ for (let i = 0; i < n; i++) {
1271
+ for (let j = 0; j < o; j++) {
1272
+ newPerm[i * o + j] = o * this.perm[i] + (this.ori[i] + j) % o;
1273
+ }
1274
+ }
1275
+ return new Perm(newPerm);
1276
+ }
1277
+ // returns tuple of sets of identical pieces in this orbit
1278
+ identicalPieces() {
1279
+ const done = [];
1280
+ const n = this.perm.length;
1281
+ const r = [];
1282
+ for (let i = 0; i < n; i++) {
1283
+ const v = this.perm[i];
1284
+ if (done[v] === void 0) {
1285
+ const s = [i];
1286
+ done[v] = true;
1287
+ for (let j = i + 1; j < n; j++) {
1288
+ if (this.perm[j] === v) {
1289
+ s.push(j);
1290
+ }
1291
+ }
1292
+ r.push(s);
1293
+ }
1294
+ }
1295
+ return r;
1296
+ }
1297
+ order() {
1298
+ return this.toPerm().order();
1299
+ }
1300
+ isIdentity() {
1301
+ const n = this.perm.length;
1302
+ if (this.perm === iota(n) && this.ori === zeros(n)) {
1303
+ return true;
1304
+ }
1305
+ for (let i = 0; i < n; i++) {
1306
+ if (this.perm[i] !== i || this.ori[i] !== 0) {
1307
+ return false;
1308
+ }
1309
+ }
1310
+ return true;
1311
+ }
1312
+ zeroOris() {
1313
+ const n = this.perm.length;
1314
+ if (this.ori === zeros(n)) {
1315
+ return true;
1316
+ }
1317
+ for (let i = 0; i < n; i++) {
1318
+ if (this.ori[i] !== 0) {
1319
+ return false;
1320
+ }
1321
+ }
1322
+ return true;
1323
+ }
1324
+ remap(no, on, nv) {
1325
+ const newPerm = new Array(nv);
1326
+ const newOri = new Array(nv);
1327
+ for (let i = 0; i < nv; i++) {
1328
+ newPerm[i] = on[this.perm[no[i]]];
1329
+ newOri[i] = this.ori[no[i]];
1330
+ }
1331
+ return new _PGOrbit(newPerm, newOri, this.orimod);
1332
+ }
1333
+ remapVS(no, nv) {
1334
+ const newPerm = new Array(nv);
1335
+ const newOri = new Array(nv);
1336
+ let nextNew = 0;
1337
+ const reassign = [];
1338
+ for (let i = 0; i < nv; i++) {
1339
+ const ov = this.perm[no[i]];
1340
+ if (reassign[ov] === void 0) {
1341
+ reassign[ov] = nextNew++;
1342
+ }
1343
+ newPerm[i] = reassign[ov];
1344
+ newOri[i] = this.ori[no[i]];
1345
+ }
1346
+ return new _PGOrbit(newPerm, newOri, this.orimod);
1347
+ }
1348
+ appendDefinition(result, name, useVS, concise = true) {
1349
+ if (concise && this.isIdentity()) {
1350
+ return;
1351
+ }
1352
+ result.push(name);
1353
+ result.push(this.perm.map((_) => _ + 1).join(" "));
1354
+ if (!this.zeroOris()) {
1355
+ if (useVS) {
1356
+ const newori = new Array(this.ori.length);
1357
+ for (let i = 0; i < newori.length; i++) {
1358
+ newori[this.perm[i]] = this.ori[i];
1359
+ }
1360
+ result.push(newori.join(" "));
1361
+ } else {
1362
+ result.push(this.ori.join(" "));
1363
+ }
1364
+ }
1365
+ }
1366
+ toKTransformationOrbitData() {
1367
+ const n = this.perm.length;
1368
+ if (this.isIdentity()) {
1369
+ if (!_PGOrbit.ktransformationCache[n]) {
1370
+ _PGOrbit.ktransformationCache[n] = {
1371
+ permutation: iota(n),
1372
+ orientationDelta: zeros(n)
1373
+ };
1374
+ }
1375
+ return _PGOrbit.ktransformationCache[n];
1376
+ } else {
1377
+ return { permutation: this.perm, orientationDelta: this.ori };
1378
+ }
1379
+ }
1380
+ toKPatternOrbitData() {
1381
+ const n = this.perm.length;
1382
+ return {
1383
+ pieces: this.perm,
1384
+ orientation: this.ori,
1385
+ orientationMod: zeros(n)
1386
+ };
1387
+ }
1388
+ };
1389
+ var PGTransformBase = class {
1390
+ constructor(orbits) {
1391
+ this.orbits = orbits;
1392
+ }
1393
+ orbits;
1394
+ internalMul(b) {
1395
+ const newOrbits = [];
1396
+ for (let i = 0; i < this.orbits.length; i++) {
1397
+ newOrbits.push(this.orbits[i].mul(b.orbits[i]));
1398
+ }
1399
+ return newOrbits;
1400
+ }
1401
+ internalInv() {
1402
+ const newOrbits = [];
1403
+ for (const orbit of this.orbits) {
1404
+ newOrbits.push(orbit.inv());
1405
+ }
1406
+ return newOrbits;
1407
+ }
1408
+ equal(b) {
1409
+ for (let i = 0; i < this.orbits.length; i++) {
1410
+ if (!this.orbits[i].equal(b.orbits[i])) {
1411
+ return false;
1412
+ }
1413
+ }
1414
+ return true;
1415
+ }
1416
+ killOri() {
1417
+ for (const orbit of this.orbits) {
1418
+ orbit.killOri();
1419
+ }
1420
+ return this;
1421
+ }
1422
+ toPerm() {
1423
+ const perms = [];
1424
+ let n = 0;
1425
+ for (const orbit of this.orbits) {
1426
+ const p = orbit.toPerm();
1427
+ perms.push(p);
1428
+ n += p.n;
1429
+ }
1430
+ const newPerm = new Array(n);
1431
+ n = 0;
1432
+ for (const p of perms) {
1433
+ for (let j = 0; j < p.n; j++) {
1434
+ newPerm[n + j] = n + p.p[j];
1435
+ }
1436
+ n += p.n;
1437
+ }
1438
+ return new Perm(newPerm);
1439
+ }
1440
+ identicalPieces() {
1441
+ const r = [];
1442
+ let n = 0;
1443
+ for (const orbit of this.orbits) {
1444
+ const o = orbit.orimod;
1445
+ const s = orbit.identicalPieces();
1446
+ for (let j = 0; j < s.length; j++) {
1447
+ r.push(s[j].map((_) => _ * o + n));
1448
+ }
1449
+ n += o * orbit.perm.length;
1450
+ }
1451
+ return r;
1452
+ }
1453
+ order() {
1454
+ let r = 1;
1455
+ for (const orbit of this.orbits) {
1456
+ r = lcm(r, orbit.order());
1457
+ }
1458
+ return r;
1459
+ }
1460
+ };
1461
+ var PGTransform = class _PGTransform extends PGTransformBase {
1462
+ mul(b) {
1463
+ return new _PGTransform(this.internalMul(b));
1464
+ }
1465
+ mulScalar(n) {
1466
+ if (n === 0) {
1467
+ return this.e();
1468
+ }
1469
+ let t = this;
1470
+ if (n < 0) {
1471
+ t = t.inv();
1472
+ n = -n;
1473
+ }
1474
+ while ((n & 1) === 0) {
1475
+ t = t.mul(t);
1476
+ n >>= 1;
1477
+ }
1478
+ if (n === 1) {
1479
+ return t;
1480
+ }
1481
+ let s = t;
1482
+ let r = this.e();
1483
+ while (n > 0) {
1484
+ if (n & 1) {
1485
+ r = r.mul(s);
1486
+ }
1487
+ if (n > 1) {
1488
+ s = s.mul(s);
1489
+ }
1490
+ n >>= 1;
1491
+ }
1492
+ return r;
1493
+ }
1494
+ inv() {
1495
+ return new _PGTransform(this.internalInv());
1496
+ }
1497
+ e() {
1498
+ return new _PGTransform(
1499
+ this.orbits.map((_) => PGOrbit.e(_.perm.length, _.orimod))
1500
+ );
1501
+ }
1502
+ };
1503
+ var VisibleState = class _VisibleState extends PGTransformBase {
1504
+ mul(b) {
1505
+ return new _VisibleState(this.internalMul(b));
1506
+ }
1507
+ };
1508
+ var DisjointUnion = class {
1509
+ constructor(n) {
1510
+ this.n = n;
1511
+ this.heads = new Array(n);
1512
+ for (let i = 0; i < n; i++) {
1513
+ this.heads[i] = i;
1514
+ }
1515
+ }
1516
+ n;
1517
+ heads;
1518
+ find(v) {
1519
+ let h = this.heads[v];
1520
+ if (this.heads[h] === h) {
1521
+ return h;
1522
+ }
1523
+ h = this.find(this.heads[h]);
1524
+ this.heads[v] = h;
1525
+ return h;
1526
+ }
1527
+ union(a, b) {
1528
+ const ah = this.find(a);
1529
+ const bh = this.find(b);
1530
+ if (ah < bh) {
1531
+ this.heads[bh] = ah;
1532
+ } else if (ah > bh) {
1533
+ this.heads[ah] = bh;
1534
+ }
1535
+ }
1536
+ };
1537
+ function showcanon(g, disp) {
1538
+ const n = g.moveops.length;
1539
+ if (n > 30) {
1540
+ throw new Error("Canon info too big for bitmask");
1541
+ }
1542
+ const orders = [];
1543
+ const commutes = [];
1544
+ for (let i = 0; i < n; i++) {
1545
+ const permA = g.moveops[i];
1546
+ orders.push(permA.order());
1547
+ let bits = 0;
1548
+ for (let j = 0; j < n; j++) {
1549
+ if (j === i) {
1550
+ continue;
1551
+ }
1552
+ const permB = g.moveops[j];
1553
+ if (permA.mul(permB).equal(permB.mul(permA))) {
1554
+ bits |= 1 << j;
1555
+ }
1556
+ }
1557
+ commutes.push(bits);
1558
+ }
1559
+ let curlev = {};
1560
+ curlev[0] = 1;
1561
+ for (let d = 0; d < 100; d++) {
1562
+ let sum = 0;
1563
+ const nextlev = {};
1564
+ let uniq = 0;
1565
+ for (const sti in curlev) {
1566
+ const st = +sti;
1567
+ const cnt = curlev[st];
1568
+ sum += cnt;
1569
+ uniq++;
1570
+ for (let mv = 0; mv < orders.length; mv++) {
1571
+ if ((st >> mv & 1) === 0 && (st & commutes[mv] & (1 << mv) - 1) === 0) {
1572
+ const nst = st & commutes[mv] | 1 << mv;
1573
+ if (nextlev[nst] === void 0) {
1574
+ nextlev[nst] = 0;
1575
+ }
1576
+ nextlev[nst] += (orders[mv] - 1) * cnt;
1577
+ }
1578
+ }
1579
+ }
1580
+ disp(`${d}: canonseq ${sum} states ${uniq}`);
1581
+ curlev = nextlev;
1582
+ }
1583
+ }
1584
+
1585
+ // src/cubing/puzzle-geometry/Quat.ts
1586
+ var eps = 1e-9;
1587
+ function centermassface(face) {
1588
+ let s = new Quat(0, 0, 0, 0);
1589
+ for (let i = 0; i < face.length; i++) {
1590
+ s = s.sum(face[i]);
1591
+ }
1592
+ return s.smul(1 / face.length);
1593
+ }
1594
+ function solvethreeplanes(p1, p2, p3, planes) {
1595
+ const p = planes[p1].intersect3(planes[p2], planes[p3]);
1596
+ if (!p) {
1597
+ return p;
1598
+ }
1599
+ for (let i = 0; i < planes.length; i++) {
1600
+ if (i !== p1 && i !== p2 && i !== p3) {
1601
+ const dt = planes[i].b * p.b + planes[i].c * p.c + planes[i].d * p.d;
1602
+ if (planes[i].a > 0 && dt > planes[i].a || planes[i].a < 0 && dt < planes[i].a) {
1603
+ return false;
1604
+ }
1605
+ }
1606
+ }
1607
+ return p;
1608
+ }
1609
+ var Quat = class _Quat {
1610
+ constructor(a, b, c, d) {
1611
+ this.a = a;
1612
+ this.b = b;
1613
+ this.c = c;
1614
+ this.d = d;
1615
+ }
1616
+ a;
1617
+ b;
1618
+ c;
1619
+ d;
1620
+ mul(q) {
1621
+ return new _Quat(
1622
+ this.a * q.a - this.b * q.b - this.c * q.c - this.d * q.d,
1623
+ this.a * q.b + this.b * q.a + this.c * q.d - this.d * q.c,
1624
+ this.a * q.c - this.b * q.d + this.c * q.a + this.d * q.b,
1625
+ this.a * q.d + this.b * q.c - this.c * q.b + this.d * q.a
1626
+ );
1627
+ }
1628
+ toString() {
1629
+ return `Q[${this.a},${this.b},${this.c},${this.d}]`;
1630
+ }
1631
+ dist(q) {
1632
+ return Math.hypot(this.a - q.a, this.b - q.b, this.c - q.c, this.d - q.d);
1633
+ }
1634
+ len() {
1635
+ return Math.hypot(this.a, this.b, this.c, this.d);
1636
+ }
1637
+ cross(q) {
1638
+ return new _Quat(
1639
+ 0,
1640
+ this.c * q.d - this.d * q.c,
1641
+ this.d * q.b - this.b * q.d,
1642
+ this.b * q.c - this.c * q.b
1643
+ );
1644
+ }
1645
+ dot(q) {
1646
+ return this.b * q.b + this.c * q.c + this.d * q.d;
1647
+ }
1648
+ normalize() {
1649
+ const d = Math.sqrt(this.dot(this));
1650
+ return new _Quat(this.a / d, this.b / d, this.c / d, this.d / d);
1651
+ }
1652
+ makenormal() {
1653
+ return new _Quat(0, this.b, this.c, this.d).normalize();
1654
+ }
1655
+ normalizeplane() {
1656
+ const d = Math.hypot(this.b, this.c, this.d);
1657
+ return new _Quat(this.a / d, this.b / d, this.c / d, this.d / d);
1658
+ }
1659
+ smul(m) {
1660
+ return new _Quat(this.a * m, this.b * m, this.c * m, this.d * m);
1661
+ }
1662
+ sum(q) {
1663
+ return new _Quat(this.a + q.a, this.b + q.b, this.c + q.c, this.d + q.d);
1664
+ }
1665
+ sub(q) {
1666
+ return new _Quat(this.a - q.a, this.b - q.b, this.c - q.c, this.d - q.d);
1667
+ }
1668
+ angle() {
1669
+ return 2 * Math.acos(this.a);
1670
+ }
1671
+ invrot() {
1672
+ return new _Quat(this.a, -this.b, -this.c, -this.d);
1673
+ }
1674
+ det3x3(a00, a01, a02, a10, a11, a12, a20, a21, a22) {
1675
+ return a00 * (a11 * a22 - a12 * a21) + a01 * (a12 * a20 - a10 * a22) + a02 * (a10 * a21 - a11 * a20);
1676
+ }
1677
+ rotateplane(q) {
1678
+ const t = q.mul(new _Quat(0, this.b, this.c, this.d)).mul(q.invrot());
1679
+ t.a = this.a;
1680
+ return t;
1681
+ }
1682
+ // return any vector orthogonal to the given one. Find the smallest
1683
+ // component (in absolute value) and return the cross product of that
1684
+ // axis with the given vector.
1685
+ orthogonal() {
1686
+ const ab = Math.abs(this.b);
1687
+ const ac = Math.abs(this.c);
1688
+ const ad = Math.abs(this.d);
1689
+ if (ab < ac && ab < ad) {
1690
+ return this.cross(new _Quat(0, 1, 0, 0)).normalize();
1691
+ } else if (ac < ab && ac < ad) {
1692
+ return this.cross(new _Quat(0, 0, 1, 0)).normalize();
1693
+ } else {
1694
+ return this.cross(new _Quat(0, 0, 0, 1)).normalize();
1695
+ }
1696
+ }
1697
+ // return the Quaternion that will rotate the this vector
1698
+ // to the b vector through rotatepoint.
1699
+ pointrotation(b) {
1700
+ const a = this.normalize();
1701
+ b = b.normalize();
1702
+ if (a.sub(b).len() < eps) {
1703
+ return new _Quat(1, 0, 0, 0);
1704
+ }
1705
+ let h = a.sum(b);
1706
+ if (h.len() < eps) {
1707
+ h = h.orthogonal();
1708
+ } else {
1709
+ h = h.normalize();
1710
+ }
1711
+ const r = a.cross(h);
1712
+ r.a = a.dot(h);
1713
+ return r;
1714
+ }
1715
+ // given two vectors, return the portion of the first that
1716
+ // is not in the direction of the second.
1717
+ unproject(b) {
1718
+ return this.sum(b.smul(-this.dot(b) / (this.len() * b.len())));
1719
+ }
1720
+ rotatepoint(q) {
1721
+ return q.mul(this).mul(q.invrot());
1722
+ }
1723
+ rotateface(face) {
1724
+ return face.map((_) => _.rotatepoint(this));
1725
+ }
1726
+ intersect3(p2, p3) {
1727
+ const det = this.det3x3(
1728
+ this.b,
1729
+ this.c,
1730
+ this.d,
1731
+ p2.b,
1732
+ p2.c,
1733
+ p2.d,
1734
+ p3.b,
1735
+ p3.c,
1736
+ p3.d
1737
+ );
1738
+ if (Math.abs(det) < eps) {
1739
+ return false;
1740
+ }
1741
+ return new _Quat(
1742
+ 0,
1743
+ this.det3x3(this.a, this.c, this.d, p2.a, p2.c, p2.d, p3.a, p3.c, p3.d) / det,
1744
+ this.det3x3(this.b, this.a, this.d, p2.b, p2.a, p2.d, p3.b, p3.a, p3.d) / det,
1745
+ this.det3x3(this.b, this.c, this.a, p2.b, p2.c, p2.a, p3.b, p3.c, p3.a) / det
1746
+ );
1747
+ }
1748
+ side(x) {
1749
+ if (x > eps) {
1750
+ return 1;
1751
+ }
1752
+ if (x < -eps) {
1753
+ return -1;
1754
+ }
1755
+ return 0;
1756
+ }
1757
+ /**
1758
+ * Cuts a face by this plane, or returns null if there
1759
+ * is no intersection.
1760
+ * @param face The face to cut.
1761
+ */
1762
+ cutface(face) {
1763
+ const d = this.a;
1764
+ let seen = 0;
1765
+ let r = null;
1766
+ for (let i = 0; i < face.length; i++) {
1767
+ seen |= 1 << this.side(face[i].dot(this) - d) + 1;
1768
+ }
1769
+ if ((seen & 5) === 5) {
1770
+ r = [];
1771
+ const inout = face.map((_) => this.side(_.dot(this) - d));
1772
+ for (let s = -1; s <= 1; s += 2) {
1773
+ const nface = [];
1774
+ for (let k = 0; k < face.length; k++) {
1775
+ if (inout[k] === s || inout[k] === 0) {
1776
+ nface.push(face[k]);
1777
+ }
1778
+ const kk = (k + 1) % face.length;
1779
+ if (inout[k] + inout[kk] === 0 && inout[k] !== 0) {
1780
+ const vk = face[k].dot(this) - d;
1781
+ const vkk = face[kk].dot(this) - d;
1782
+ const r2 = vk / (vk - vkk);
1783
+ const pt = face[k].smul(1 - r2).sum(face[kk].smul(r2));
1784
+ nface.push(pt);
1785
+ }
1786
+ }
1787
+ r.push(nface);
1788
+ }
1789
+ }
1790
+ return r;
1791
+ }
1792
+ cutfaces(faces) {
1793
+ const nfaces = [];
1794
+ for (let j = 0; j < faces.length; j++) {
1795
+ const face = faces[j];
1796
+ const t = this.cutface(face);
1797
+ if (t) {
1798
+ nfaces.push(t[0]);
1799
+ nfaces.push(t[1]);
1800
+ } else {
1801
+ nfaces.push(face);
1802
+ }
1803
+ }
1804
+ return nfaces;
1805
+ }
1806
+ faceside(face) {
1807
+ const d = this.a;
1808
+ for (let i = 0; i < face.length; i++) {
1809
+ const s = this.side(face[i].dot(this) - d);
1810
+ if (s !== 0) {
1811
+ return s;
1812
+ }
1813
+ }
1814
+ throw new Error("Could not determine side of plane in faceside");
1815
+ }
1816
+ sameplane(p) {
1817
+ const a = this.normalize();
1818
+ const b = p.normalize();
1819
+ return a.dist(b) < eps || a.dist(b.smul(-1)) < eps;
1820
+ }
1821
+ makecut(r) {
1822
+ return new _Quat(r, this.b, this.c, this.d);
1823
+ }
1824
+ };
1825
+
1826
+ // src/cubing/puzzle-geometry/PlatonicGenerator.ts
1827
+ var eps2 = 1e-9;
1828
+ function cube() {
1829
+ const s5 = Math.sqrt(0.5);
1830
+ return [new Quat(s5, s5, 0, 0), new Quat(s5, 0, s5, 0)];
1831
+ }
1832
+ function tetrahedron() {
1833
+ return [new Quat(0.5, 0.5, 0.5, 0.5), new Quat(0.5, 0.5, 0.5, -0.5)];
1834
+ }
1835
+ function dodecahedron() {
1836
+ const d36 = 2 * Math.PI / 10;
1837
+ let dx = 0.5 + 0.3 * Math.sqrt(5);
1838
+ let dy = 0.5 + 0.1 * Math.sqrt(5);
1839
+ const dd = Math.sqrt(dx * dx + dy * dy);
1840
+ dx /= dd;
1841
+ dy /= dd;
1842
+ return [
1843
+ new Quat(Math.cos(d36), dx * Math.sin(d36), dy * Math.sin(d36), 0),
1844
+ new Quat(0.5, 0.5, 0.5, 0.5)
1845
+ ];
1846
+ }
1847
+ function icosahedron() {
1848
+ let dx = 1 / 6 + Math.sqrt(5) / 6;
1849
+ let dy = 2 / 3 + Math.sqrt(5) / 3;
1850
+ const dd = Math.sqrt(dx * dx + dy * dy);
1851
+ dx /= dd;
1852
+ dy /= dd;
1853
+ const ang = 2 * Math.PI / 6;
1854
+ return [
1855
+ new Quat(Math.cos(ang), dx * Math.sin(ang), dy * Math.sin(ang), 0),
1856
+ new Quat(Math.cos(ang), -dx * Math.sin(ang), dy * Math.sin(ang), 0)
1857
+ ];
1858
+ }
1859
+ function octahedron() {
1860
+ const s5 = Math.sqrt(0.5);
1861
+ return [new Quat(0.5, 0.5, 0.5, 0.5), new Quat(s5, 0, 0, s5)];
1862
+ }
1863
+ function closure(g) {
1864
+ const q = [new Quat(1, 0, 0, 0)];
1865
+ for (let i = 0; i < q.length; i++) {
1866
+ for (let j = 0; j < g.length; j++) {
1867
+ const ns = g[j].mul(q[i]);
1868
+ const negns = ns.smul(-1);
1869
+ let wasseen = false;
1870
+ for (let k = 0; k < q.length; k++) {
1871
+ if (ns.dist(q[k]) < eps2 || negns.dist(q[k]) < eps2) {
1872
+ wasseen = true;
1873
+ break;
1874
+ }
1875
+ }
1876
+ if (!wasseen) {
1877
+ q.push(ns);
1878
+ }
1879
+ }
1880
+ }
1881
+ return q;
1882
+ }
1883
+ function uniqueplanes(p, g) {
1884
+ const planes = [];
1885
+ const planerot = [];
1886
+ for (let i = 0; i < g.length; i++) {
1887
+ const p2 = p.rotateplane(g[i]);
1888
+ let wasseen = false;
1889
+ for (let j = 0; j < planes.length; j++) {
1890
+ if (p2.dist(planes[j]) < eps2) {
1891
+ wasseen = true;
1892
+ break;
1893
+ }
1894
+ }
1895
+ if (!wasseen) {
1896
+ planes.push(p2);
1897
+ planerot.push(g[i]);
1898
+ }
1899
+ }
1900
+ return planerot;
1901
+ }
1902
+ function getface(planes) {
1903
+ const face = [];
1904
+ for (let i = 1; i < planes.length; i++) {
1905
+ for (let j = i + 1; j < planes.length; j++) {
1906
+ const p = solvethreeplanes(0, i, j, planes);
1907
+ if (p) {
1908
+ let wasseen = false;
1909
+ for (let k = 0; k < face.length; k++) {
1910
+ if (p.dist(face[k]) < eps2) {
1911
+ wasseen = true;
1912
+ break;
1913
+ }
1914
+ }
1915
+ if (!wasseen) {
1916
+ face.push(p);
1917
+ }
1918
+ }
1919
+ }
1920
+ }
1921
+ for (; ; ) {
1922
+ let changed = false;
1923
+ for (let i = 0; i < face.length; i++) {
1924
+ const j = (i + 1) % face.length;
1925
+ if (planes[0].dot(face[i].cross(face[j])) < 0) {
1926
+ const t = face[i];
1927
+ face[i] = face[j];
1928
+ face[j] = t;
1929
+ changed = true;
1930
+ }
1931
+ }
1932
+ if (!changed) {
1933
+ break;
1934
+ }
1935
+ }
1936
+ return face;
1937
+ }
1938
+
1939
+ // src/cubing/puzzle-geometry/pgPuzzles.ts
1940
+ var pgPuzzle = {
1941
+ "2x2x2": "c f 0",
1942
+ "3x3x3": "c f 0.333333333333333",
1943
+ "4x4x4": "c f 0.5 f 0",
1944
+ "5x5x5": "c f 0.6 f 0.2",
1945
+ "6x6x6": "c f 0.666666666666667 f 0.333333333333333 f 0",
1946
+ "7x7x7": "c f 0.714285714285714 f 0.428571428571429 f 0.142857142857143",
1947
+ "8x8x8": "c f 0.75 f 0.5 f 0.25 f 0",
1948
+ "9x9x9": "c f 0.777777777777778 f 0.555555555555556 f 0.333333333333333 f 0.111111111111111",
1949
+ "10x10x10": "c f 0.8 f 0.6 f 0.4 f 0.2 f 0",
1950
+ "11x11x11": "c f 0.818181818181818 f 0.636363636363636 f 0.454545454545455 f 0.272727272727273 f 0.0909090909090909",
1951
+ "12x12x12": "c f 0.833333333333333 f 0.666666666666667 f 0.5 f 0.333333333333333 f 0.166666666666667 f 0",
1952
+ "13x13x13": "c f 0.846153846153846 f 0.692307692307692 f 0.538461538461538 f 0.384615384615385 f 0.230769230769231 f 0.0769230769230769",
1953
+ "20x20x20": "c f 0 f .1 f .2 f .3 f .4 f .5 f .6 f .7 f .8 f .9",
1954
+ "30x30x30": "c f 0 f .066667 f .133333 f .2 f .266667 f .333333 f .4 f .466667 f .533333 f .6 f .666667 f .733333 f .8 f .866667 f .933333",
1955
+ "40x40x40": "c f 0 f .05 f .1 f .15 f .2 f .25 f .3 f .35 f .4 f .45 f .5 f .55 f .6 f .65 f .7 f .75 f .8 f .85 f .9 f .95",
1956
+ skewb: "c v 0",
1957
+ "master skewb": "c v 0.275",
1958
+ "professor skewb": "c v 0 v 0.38",
1959
+ "compy cube": "c v 0.915641442663986",
1960
+ helicopter: "c e 0.707106781186547",
1961
+ "curvy copter": "c e 0.83",
1962
+ dino: "c v 0.577350269189626",
1963
+ "little chop": "c e 0",
1964
+ pyramorphix: "t e 0",
1965
+ mastermorphix: "t e 0.346184634065199",
1966
+ pyraminx: "t v 0.333333333333333 v 1.66666666666667",
1967
+ tetraminx: "t v 0.333333333333333",
1968
+ "master pyraminx": "t v 0 v 1 v 2",
1969
+ "master tetraminx": "t v 0 v 1",
1970
+ "professor pyraminx": "t v -0.2 v 0.6 v 1.4 v 2.2",
1971
+ "professor tetraminx": "t v -0.2 v 0.6 v 1.4",
1972
+ "royal pyraminx": "t v -0.333333333333333 v 0.333333333333333 v 1 v 1.66666666666667 v 2.33333333333333",
1973
+ "royal tetraminx": "t v -0.333333333333333 v 0.333333333333333 v 1 v 1.66666666666667",
1974
+ "emperor pyraminx": "t v -0.428571428571429 v 0.142857142857143 v 0.714285714285714 v 1.28571428571429 v 1.85714285714286 v 2.42857142857143",
1975
+ "emperor tetraminx": "t v -0.428571428571429 v 0.142857142857143 v 0.714285714285714 v 1.28571428571429 v 1.85714285714286",
1976
+ "Jing pyraminx": "t f 0",
1977
+ "master pyramorphix": "t e 0.866025403784437",
1978
+ megaminx: "d f 0.7",
1979
+ gigaminx: "d f 0.64 f 0.82",
1980
+ teraminx: "d f 0.64 f 0.76 f 0.88",
1981
+ petaminx: "d f 0.64 f 0.73 f 0.82 f 0.91",
1982
+ examinx: "d f 0.64 f 0.712 f 0.784 f 0.856 f 0.928",
1983
+ zetaminx: "d f 0.64 f 0.7 f 0.76 f 0.82 f 0.88 f 0.94",
1984
+ yottaminx: "d f 0.64 f 0.6914 f 0.7429 f 0.7943 f 0.8457 f 0.8971 f 0.9486",
1985
+ pentultimate: "d f 0",
1986
+ "master pentultimate": "d f 0.1",
1987
+ "elite pentultimate": "d f 0 f 0.145905",
1988
+ // exact value for starminx is sqrt(5(5-2 sqrt(5))/3)
1989
+ starminx: "d v 0.937962370425399",
1990
+ "starminx 2": "d f 0.23606797749979",
1991
+ "pyraminx crystal": "d f 0.447213595499989",
1992
+ chopasaurus: "d v 0",
1993
+ "big chop": "d e 0",
1994
+ "skewb diamond": "o f 0",
1995
+ FTO: "o f 0.333333333333333",
1996
+ "master FTO": "o f 0.5 f 0",
1997
+ "Christopher's jewel": "o v 0.577350269189626",
1998
+ octastar: "o e 0",
1999
+ "Trajber's octahedron": "o v 0.433012701892219",
2000
+ "radio chop": "i f 0",
2001
+ icosamate: "i v 0",
2002
+ "Regular Astrominx": "i v 0.18759247376021",
2003
+ "Regular Astrominx + Big Chop": "i v 0.18759247376021 e 0",
2004
+ Redicosahedron: "i v 0.794654472291766",
2005
+ "Redicosahedron with centers": "i v 0.84",
2006
+ Icosaminx: "i v 0.73",
2007
+ "Eitan's star": "i f 0.61803398874989",
2008
+ "2x2x2 + dino": "c f 0 v 0.577350269189626",
2009
+ "2x2x2 + little chop": "c f 0 e 0",
2010
+ "dino + little chop": "c v 0.577350269189626 e 0",
2011
+ "2x2x2 + dino + little chop": "c f 0 v 0.577350269189626 e 0",
2012
+ "megaminx + chopasaurus": "d f 0.61803398875 v 0",
2013
+ "starminx combo": "d f 0.23606797749979 v 0.937962370425399"
2014
+ };
2015
+
2016
+ // src/cubing/puzzle-geometry/SchreierSims.ts
2017
+ var FactoredNumber = class {
2018
+ mult;
2019
+ constructor() {
2020
+ this.mult = [];
2021
+ }
2022
+ multiply(n) {
2023
+ for (let f = 2; f * f <= n; f++) {
2024
+ while (n % f === 0) {
2025
+ if (void 0 !== this.mult[f]) {
2026
+ this.mult[f]++;
2027
+ } else {
2028
+ this.mult[f] = 1;
2029
+ }
2030
+ n /= f;
2031
+ }
2032
+ }
2033
+ if (n > 1) {
2034
+ if (void 0 !== this.mult[n]) {
2035
+ this.mult[n]++;
2036
+ } else {
2037
+ this.mult[n] = 1;
2038
+ }
2039
+ }
2040
+ }
2041
+ toString() {
2042
+ let r = "";
2043
+ for (let i = 0; i < this.mult.length; i++) {
2044
+ if (void 0 !== this.mult[i]) {
2045
+ if (r !== "") {
2046
+ r += "*";
2047
+ }
2048
+ r += i;
2049
+ if (this.mult[i] > 1) {
2050
+ r += `^${this.mult[i]}`;
2051
+ }
2052
+ }
2053
+ }
2054
+ return r;
2055
+ }
2056
+ };
2057
+ function schreierSims(g, disp) {
2058
+ const n = g[0].p.length;
2059
+ const e = identity(n);
2060
+ let sgs = [];
2061
+ let sgsi = [];
2062
+ let sgslen = [];
2063
+ let Tk = [];
2064
+ let Tklen = [];
2065
+ function resolve(p) {
2066
+ for (let i = p.p.length - 1; i >= 0; i--) {
2067
+ const j = p.p[i];
2068
+ if (j !== i) {
2069
+ if (!sgs[i][j]) {
2070
+ return false;
2071
+ }
2072
+ p = p.mul(sgsi[i][j]);
2073
+ }
2074
+ }
2075
+ return true;
2076
+ }
2077
+ function knutha(k, p, len) {
2078
+ Tk[k].push(p);
2079
+ Tklen[k].push(len);
2080
+ for (let i = 0; i < sgs[k].length; i++) {
2081
+ if (sgs[k][i]) {
2082
+ knuthb(k, sgs[k][i].mul(p), len + sgslen[k][i]);
2083
+ }
2084
+ }
2085
+ }
2086
+ function knuthb(k, p, len) {
2087
+ const j = p.p[k];
2088
+ if (!sgs[k][j]) {
2089
+ sgs[k][j] = p;
2090
+ sgsi[k][j] = p.inv();
2091
+ sgslen[k][j] = len;
2092
+ for (let i = 0; i < Tk[k].length; i++) {
2093
+ knuthb(k, p.mul(Tk[k][i]), len + Tklen[k][i]);
2094
+ }
2095
+ return;
2096
+ }
2097
+ const p2 = p.mul(sgsi[k][j]);
2098
+ if (!resolve(p2)) {
2099
+ knutha(k - 1, p2, len + sgslen[k][j]);
2100
+ }
2101
+ }
2102
+ function getsgs() {
2103
+ sgs = [];
2104
+ sgsi = [];
2105
+ Tk = [];
2106
+ sgslen = [];
2107
+ Tklen = [];
2108
+ for (let i = 0; i < n; i++) {
2109
+ sgs.push([]);
2110
+ sgsi.push([]);
2111
+ sgslen.push([]);
2112
+ Tk.push([]);
2113
+ Tklen.push([]);
2114
+ sgs[i][i] = e;
2115
+ sgsi[i][i] = e;
2116
+ sgslen[i][i] = 0;
2117
+ }
2118
+ let none = 0;
2119
+ let sz = BigInt(1);
2120
+ for (let i = 0; i < g.length; i++) {
2121
+ knutha(n - 1, g[i], 1);
2122
+ sz = BigInt(1);
2123
+ let tks = 0;
2124
+ let sollen = 0;
2125
+ const avgs = [];
2126
+ const mults = new FactoredNumber();
2127
+ for (let j = 0; j < n; j++) {
2128
+ let cnt = 0;
2129
+ let lensum = 0;
2130
+ for (let k = 0; k < n; k++) {
2131
+ if (sgs[j][k]) {
2132
+ cnt++;
2133
+ lensum += sgslen[j][k];
2134
+ if (j !== k) {
2135
+ none++;
2136
+ }
2137
+ }
2138
+ }
2139
+ tks += Tk[j].length;
2140
+ sz *= BigInt(cnt);
2141
+ if (cnt > 1) {
2142
+ mults.multiply(cnt);
2143
+ }
2144
+ const avg = lensum / cnt;
2145
+ avgs.push(avg);
2146
+ sollen += avg;
2147
+ }
2148
+ disp(
2149
+ `${i}: sz ${sz} T ${tks} sol ${sollen} none ${none} mults ${mults.toString()}`
2150
+ );
2151
+ }
2152
+ return sz;
2153
+ }
2154
+ return getsgs();
2155
+ }
2156
+
2157
+ // src/cubing/puzzle-geometry/PuzzleGeometry.ts
2158
+ function tstart(s) {
2159
+ return s;
2160
+ }
2161
+ function tend(_) {
2162
+ }
2163
+ var Face = class _Face {
2164
+ coords;
2165
+ length;
2166
+ constructor(q) {
2167
+ this.coords = new Array(q.length * 3);
2168
+ for (let i = 0; i < q.length; i++) {
2169
+ this.coords[3 * i] = q[i].b;
2170
+ this.coords[3 * i + 1] = q[i].c;
2171
+ this.coords[3 * i + 2] = q[i].d;
2172
+ }
2173
+ this.length = q.length;
2174
+ }
2175
+ get(off) {
2176
+ return new Quat(
2177
+ 0,
2178
+ this.coords[3 * off],
2179
+ this.coords[3 * off + 1],
2180
+ this.coords[3 * off + 2]
2181
+ );
2182
+ }
2183
+ centermass() {
2184
+ let sx = 0;
2185
+ let sy = 0;
2186
+ let sz = 0;
2187
+ for (let i = 0; i < this.length; i++) {
2188
+ sx += this.coords[3 * i];
2189
+ sy += this.coords[3 * i + 1];
2190
+ sz += this.coords[3 * i + 2];
2191
+ }
2192
+ return new Quat(0, sx / this.length, sy / this.length, sz / this.length);
2193
+ }
2194
+ rotate(q) {
2195
+ const a = [];
2196
+ for (let i = 0; i < this.length; i++) {
2197
+ a.push(this.get(i).rotatepoint(q));
2198
+ }
2199
+ return new _Face(a);
2200
+ }
2201
+ rotateforward() {
2202
+ const a = [];
2203
+ for (let i = 1; i < this.length; i++) {
2204
+ a.push(this.get(i));
2205
+ }
2206
+ a.push(this.get(0));
2207
+ return new _Face(a);
2208
+ }
2209
+ };
2210
+ var FaceTree = class _FaceTree {
2211
+ constructor(face, left, right) {
2212
+ this.face = face;
2213
+ this.left = left;
2214
+ this.right = right;
2215
+ }
2216
+ face;
2217
+ left;
2218
+ right;
2219
+ split(q) {
2220
+ const t = q.cutface(this.face);
2221
+ if (t !== null) {
2222
+ if (this.left === void 0) {
2223
+ this.left = new _FaceTree(t[0]);
2224
+ this.right = new _FaceTree(t[1]);
2225
+ } else {
2226
+ this.left = this.left?.split(q);
2227
+ this.right = this.right?.split(q);
2228
+ }
2229
+ }
2230
+ return this;
2231
+ }
2232
+ collect(arr, leftfirst) {
2233
+ if (this.left === void 0) {
2234
+ arr.push(new Face(this.face));
2235
+ } else if (leftfirst) {
2236
+ this.left?.collect(arr, false);
2237
+ this.right?.collect(arr, true);
2238
+ } else {
2239
+ this.right?.collect(arr, false);
2240
+ this.left?.collect(arr, true);
2241
+ }
2242
+ return arr;
2243
+ }
2244
+ };
2245
+ function expandfaces(rots, faces) {
2246
+ const nfaces = [];
2247
+ for (const rot of rots) {
2248
+ for (const face of faces) {
2249
+ nfaces.push(face.rotate(rot));
2250
+ }
2251
+ }
2252
+ return nfaces;
2253
+ }
2254
+ var eps3 = 1e-9;
2255
+ var copyright = "PuzzleGeometry 0.1 Copyright 2018 Tomas Rokicki.";
2256
+ var permissivieMoveParsing = false;
2257
+ function defaultnets() {
2258
+ return {
2259
+ // four faces: tetrahedron
2260
+ 4: [["F", "D", "L", "R"]],
2261
+ // six faces: cube
2262
+ 6: [
2263
+ ["F", "D", "L", "U", "R"],
2264
+ ["R", "F", "", "B", ""]
2265
+ ],
2266
+ // eight faces: octahedron
2267
+ 8: [
2268
+ ["F", "D", "L", "R"],
2269
+ ["D", "F", "BR", ""],
2270
+ ["BR", "D", "", "BB"],
2271
+ ["BB", "BR", "U", "BL"]
2272
+ ],
2273
+ // twelve faces: dodecahedron; U/F/R/F/BL/BR from megaminx
2274
+ 12: [
2275
+ ["U", "F", "", "", "", ""],
2276
+ ["F", "U", "R", "C", "A", "L"],
2277
+ ["R", "F", "", "", "E", ""],
2278
+ ["E", "R", "", "BF", "", ""],
2279
+ ["BF", "E", "BR", "BL", "I", "D"]
2280
+ ],
2281
+ // twenty faces: icosahedron
2282
+ 20: [
2283
+ ["R", "C", "F", "E"],
2284
+ ["F", "R", "L", "U"],
2285
+ ["L", "F", "A", ""],
2286
+ ["E", "R", "G", "I"],
2287
+ ["I", "E", "S", "H"],
2288
+ ["S", "I", "J", "B"],
2289
+ ["B", "S", "K", "D"],
2290
+ ["K", "B", "M", "O"],
2291
+ ["O", "K", "P", "N"],
2292
+ ["P", "O", "Q", ""]
2293
+ ]
2294
+ };
2295
+ }
2296
+ var orientationDefaults = {
2297
+ 4: {
2298
+ v: ["DFR", "DLF", "DRL", "FLR"],
2299
+ e: ["FR", "LF", "DF", "DL", "RD", "RL"],
2300
+ c: ["DF", "FD", "RL", "LR"]
2301
+ },
2302
+ 6: {
2303
+ v: ["URF", "UBR", "ULB", "UFL", "DFR", "DRB", "DBL", "DLF"],
2304
+ e: ["UF", "UR", "UB", "UL", "DF", "DR", "DB", "DL", "FR", "FL", "BR", "BL"],
2305
+ c: ["UB", "LU", "FU", "RU", "BU", "DF"]
2306
+ },
2307
+ 8: {
2308
+ v: ["UBBBRR", "URFL", "ULBLBB", "DBRBBBL", "DBLLF", "DFRBR"],
2309
+ e: [
2310
+ "UL",
2311
+ "UBB",
2312
+ "UR",
2313
+ "BRD",
2314
+ "BLD",
2315
+ "FD",
2316
+ "BRR",
2317
+ "FR",
2318
+ "FL",
2319
+ "BLL",
2320
+ "BLBB",
2321
+ "BRBB"
2322
+ ],
2323
+ c: ["BBU", "LU", "RU", "BRD", "FD", "BLD", "DF", "UBB"]
2324
+ },
2325
+ 12: {
2326
+ v: [
2327
+ "URF",
2328
+ "UFL",
2329
+ "ULBL",
2330
+ "UBLBR",
2331
+ "UBRR",
2332
+ "DEBF",
2333
+ "DBFI",
2334
+ "DIA",
2335
+ "DAC",
2336
+ "DCE",
2337
+ "LAI",
2338
+ "ALF",
2339
+ "FCA",
2340
+ "CFR",
2341
+ "REC",
2342
+ "ERBR",
2343
+ "BRBFE",
2344
+ "BFBRBL",
2345
+ "BLIBF",
2346
+ "IBLL"
2347
+ ],
2348
+ e: [
2349
+ "UF",
2350
+ "UR",
2351
+ "UBR",
2352
+ "UBL",
2353
+ "UL",
2354
+ "ER",
2355
+ "EBR",
2356
+ "EBF",
2357
+ "ED",
2358
+ "EC",
2359
+ "IBF",
2360
+ "IBL",
2361
+ "IL",
2362
+ "IA",
2363
+ "ID",
2364
+ "AC",
2365
+ "CF",
2366
+ "FA",
2367
+ "BFBR",
2368
+ "BRBL",
2369
+ "BLBF",
2370
+ "CD",
2371
+ "AD",
2372
+ "AL",
2373
+ "FL",
2374
+ "FR",
2375
+ "CR",
2376
+ "BFD",
2377
+ "BRR",
2378
+ "BLL"
2379
+ ],
2380
+ c: [
2381
+ "UF",
2382
+ "FU",
2383
+ "DBF",
2384
+ "BFD",
2385
+ "AD",
2386
+ "CD",
2387
+ "BRU",
2388
+ "BLU",
2389
+ "LA",
2390
+ "RA",
2391
+ "EBR",
2392
+ "IBL"
2393
+ ]
2394
+ },
2395
+ 20: {
2396
+ v: [
2397
+ "FLPQU",
2398
+ "FUGER",
2399
+ "FRCAL",
2400
+ "HCREI",
2401
+ "ISBDH",
2402
+ "JSIEG",
2403
+ "BSJMK",
2404
+ "MQPOK",
2405
+ "ONDBK",
2406
+ "NOPLA",
2407
+ "UQMJG",
2408
+ "DNACH"
2409
+ ],
2410
+ e: [
2411
+ "FU",
2412
+ "FL",
2413
+ "FR",
2414
+ "EG",
2415
+ "ER",
2416
+ "EI",
2417
+ "SJ",
2418
+ "SI",
2419
+ "SB",
2420
+ "KM",
2421
+ "KB",
2422
+ "KO",
2423
+ "PQ",
2424
+ "PO",
2425
+ "PL",
2426
+ "UG",
2427
+ "JG",
2428
+ "MQ",
2429
+ "UQ",
2430
+ "HC",
2431
+ "HD",
2432
+ "ND",
2433
+ "NA",
2434
+ "JM",
2435
+ "CA",
2436
+ "AL",
2437
+ "CR",
2438
+ "HI",
2439
+ "DB",
2440
+ "NO"
2441
+ ],
2442
+ c: [
2443
+ "FU",
2444
+ "UF",
2445
+ "GE",
2446
+ "EG",
2447
+ "JS",
2448
+ "SJ",
2449
+ "MK",
2450
+ "KM",
2451
+ "QP",
2452
+ "PQ",
2453
+ "LA",
2454
+ "AL",
2455
+ "RC",
2456
+ "CR",
2457
+ "IH",
2458
+ "HI",
2459
+ "BD",
2460
+ "DB",
2461
+ "ON",
2462
+ "NO"
2463
+ ]
2464
+ }
2465
+ };
2466
+ function defaultOrientations() {
2467
+ return {
2468
+ 4: [
2469
+ ["FLR", [0, 1, 0]],
2470
+ ["F", [0, 0, 1]]
2471
+ ],
2472
+ // FLR towards viewer
2473
+ 6: [
2474
+ ["U", [0, 1, 0]],
2475
+ ["F", [0, 0, 1]]
2476
+ ],
2477
+ // URF towards viewer
2478
+ 8: [
2479
+ ["U", [0, 1, 0]],
2480
+ ["F", [0, 0, 1]]
2481
+ ],
2482
+ // FLUR towards viewer
2483
+ 12: [
2484
+ ["U", [0, 1, 0]],
2485
+ ["F", [0, 0, 1]]
2486
+ ],
2487
+ // F towards viewer
2488
+ 20: [
2489
+ ["GUQMJ", [0, 1, 0]],
2490
+ ["F", [0, 0, 1]]
2491
+ ]
2492
+ // F towards viewer
2493
+ };
2494
+ }
2495
+ function findelement(a, p) {
2496
+ for (let i = 0; i < a.length; i++) {
2497
+ if (a[i][0].dist(p) < eps3) {
2498
+ return i;
2499
+ }
2500
+ }
2501
+ throw Error("Element not found");
2502
+ }
2503
+ function getPG3DNamedPuzzles() {
2504
+ return pgPuzzle;
2505
+ }
2506
+ function getPuzzleDescriptionString(puzzleName) {
2507
+ return pgPuzzle[puzzleName];
2508
+ }
2509
+ var PUZZLE_BASE_SHAPES = ["c", "t", "o", "d", "i"];
2510
+ var PUZZLE_CUT_TYPES = ["f", "v", "e"];
2511
+ function parsePuzzleDescription(s) {
2512
+ const a = s.split(/ /).filter(Boolean);
2513
+ if (a.length % 2 === 0) {
2514
+ return null;
2515
+ }
2516
+ const shape = a[0];
2517
+ if (shape !== "o" && shape !== "c" && shape !== "i" && shape !== "d" && shape !== "t") {
2518
+ return null;
2519
+ }
2520
+ const cuts = [];
2521
+ for (let i = 1; i < a.length; i += 2) {
2522
+ if (a[i] !== "f" && a[i] !== "v" && a[i] !== "e") {
2523
+ return null;
2524
+ }
2525
+ cuts.push({
2526
+ cutType: a[i],
2527
+ distance: parseFloat(a[i + 1])
2528
+ });
2529
+ }
2530
+ return { shape, cuts };
2531
+ }
2532
+ function getPuzzleGeometryByDesc(desc, options = {}) {
2533
+ const parsed = parsePuzzleDescription(desc);
2534
+ if (parsed === null) {
2535
+ throw Error("Could not parse the puzzle description");
2536
+ }
2537
+ const pg = new PuzzleGeometry(
2538
+ parsed,
2539
+ Object.assign({}, { allMoves: true }, options)
2540
+ );
2541
+ pg.allstickers();
2542
+ pg.genperms();
2543
+ return pg;
2544
+ }
2545
+ function getPuzzleGeometryByName(puzzleName, options) {
2546
+ return getPuzzleGeometryByDesc(pgPuzzle[puzzleName], options);
2547
+ }
2548
+ function getmovename(geo, bits, slices) {
2549
+ let inverted = false;
2550
+ if (slices - bits[1] < bits[0]) {
2551
+ geo = [geo[2], geo[3], geo[0], geo[1]];
2552
+ bits = [slices - bits[1], slices - bits[0]];
2553
+ inverted = true;
2554
+ }
2555
+ let movenameFamily = geo[0];
2556
+ let movenamePrefix = "";
2557
+ if (bits[0] === 0 && bits[1] === slices) {
2558
+ movenameFamily = `${movenameFamily}v`;
2559
+ } else if (bits[0] === bits[1]) {
2560
+ if (bits[1] > 0) {
2561
+ movenamePrefix = String(bits[1] + 1);
2562
+ }
2563
+ } else if (bits[0] === 0) {
2564
+ movenameFamily = movenameFamily.toLowerCase();
2565
+ if (bits[1] > 1) {
2566
+ movenamePrefix = String(bits[1] + 1);
2567
+ }
2568
+ } else {
2569
+ throw Error(
2570
+ `We only support slice and outer block moves right now. ${bits}`
2571
+ );
2572
+ }
2573
+ return [movenamePrefix + movenameFamily, inverted];
2574
+ }
2575
+ function splitByFaceNames(s, facenames) {
2576
+ const r = [];
2577
+ let at = 0;
2578
+ while (at < s.length) {
2579
+ if (at > 0 && at < s.length && s[at] === "_") {
2580
+ at++;
2581
+ }
2582
+ let currentMatch = "";
2583
+ for (const facename of facenames) {
2584
+ if (s.substr(at).startsWith(facename[1]) && facename[1].length > currentMatch.length) {
2585
+ currentMatch = facename[1];
2586
+ }
2587
+ }
2588
+ if (currentMatch !== "") {
2589
+ r.push(currentMatch);
2590
+ at += currentMatch.length;
2591
+ } else {
2592
+ throw Error(`Could not split ${s} into face names.`);
2593
+ }
2594
+ }
2595
+ return r;
2596
+ }
2597
+ function toCoords(q, maxdist) {
2598
+ return [q.b / maxdist, -q.c / maxdist, q.d / maxdist];
2599
+ }
2600
+ function toFaceCoords(q, maxdist) {
2601
+ const r = [];
2602
+ const n = q.length;
2603
+ for (let i = 0; i < n; i++) {
2604
+ const pt = toCoords(q.get(n - i - 1), maxdist);
2605
+ r[3 * i] = pt[0];
2606
+ r[3 * i + 1] = pt[1];
2607
+ r[3 * i + 2] = pt[2];
2608
+ }
2609
+ return r;
2610
+ }
2611
+ var PuzzleGeometry = class {
2612
+ constructor(puzzleDescription, options) {
2613
+ this.puzzleDescription = puzzleDescription;
2614
+ const t1 = tstart("genperms");
2615
+ this.options = new PuzzleGeometryFullOptions(options);
2616
+ if (this.options.verbosity > 0) {
2617
+ console.log(this.header("# "));
2618
+ }
2619
+ const { shape, cuts } = puzzleDescription;
2620
+ this.movePlanes = [];
2621
+ this.movePlanesFiltered = [];
2622
+ this.faces = [];
2623
+ this.cubies = [];
2624
+ let g = null;
2625
+ switch (shape) {
2626
+ case "c": {
2627
+ g = cube();
2628
+ break;
2629
+ }
2630
+ case "o": {
2631
+ g = octahedron();
2632
+ break;
2633
+ }
2634
+ case "i": {
2635
+ g = icosahedron();
2636
+ break;
2637
+ }
2638
+ case "t": {
2639
+ g = tetrahedron();
2640
+ break;
2641
+ }
2642
+ case "d": {
2643
+ g = dodecahedron();
2644
+ break;
2645
+ }
2646
+ default:
2647
+ throw Error(`Bad shape argument: ${shape}`);
2648
+ }
2649
+ this.rotations = closure(g);
2650
+ if (this.options.verbosity) {
2651
+ console.log(`# Rotations: ${this.rotations.length}`);
2652
+ }
2653
+ const baseplane = g[0];
2654
+ this.basePlaneRotations = uniqueplanes(baseplane, this.rotations);
2655
+ const baseplanes = this.basePlaneRotations.map(
2656
+ (_) => baseplane.rotateplane(_)
2657
+ );
2658
+ this.basePlanes = baseplanes;
2659
+ this.baseFaceCount = baseplanes.length;
2660
+ const net = defaultnets()[baseplanes.length];
2661
+ this.net = net;
2662
+ this.colors = defaultPlatonicColorSchemes()[baseplanes.length];
2663
+ if (this.options.verbosity > 0) {
2664
+ console.log(`# Base planes: ${baseplanes.length}`);
2665
+ }
2666
+ const baseface = getface(baseplanes);
2667
+ const zero = new Quat(0, 0, 0, 0);
2668
+ if (this.options.verbosity > 0) {
2669
+ console.log(`# Face vertices: ${baseface.length}`);
2670
+ }
2671
+ const facenormal = baseplanes[0].makenormal();
2672
+ const edgenormal = baseface[0].sum(baseface[1]).makenormal();
2673
+ const vertexnormal = baseface[0].makenormal();
2674
+ const boundary = new Quat(1, facenormal.b, facenormal.c, facenormal.d);
2675
+ if (this.options.verbosity > 0) {
2676
+ console.log(`# Boundary is ${boundary}`);
2677
+ }
2678
+ const planerot = uniqueplanes(boundary, this.rotations);
2679
+ const planes = planerot.map((_) => boundary.rotateplane(_));
2680
+ const firstface = getface(planes);
2681
+ this.edgeDistance = firstface[0].sum(firstface[1]).smul(0.5).dist(zero);
2682
+ this.vertexDistance = firstface[0].dist(zero);
2683
+ const cutplanes = [];
2684
+ const intersects = [];
2685
+ let sawface = false;
2686
+ let sawedge = false;
2687
+ let sawvertex = false;
2688
+ for (const cut of cuts) {
2689
+ let normal = null;
2690
+ let distance = 0;
2691
+ switch (cut.cutType) {
2692
+ case "f": {
2693
+ normal = facenormal;
2694
+ distance = 1;
2695
+ sawface = true;
2696
+ break;
2697
+ }
2698
+ case "v": {
2699
+ normal = vertexnormal;
2700
+ distance = this.vertexDistance;
2701
+ sawvertex = true;
2702
+ break;
2703
+ }
2704
+ case "e": {
2705
+ normal = edgenormal;
2706
+ distance = this.edgeDistance;
2707
+ sawedge = true;
2708
+ break;
2709
+ }
2710
+ default:
2711
+ throw Error(`Bad cut argument: ${cut.cutType}`);
2712
+ }
2713
+ cutplanes.push(normal.makecut(cut.distance));
2714
+ intersects.push(cut.distance < distance);
2715
+ }
2716
+ if (this.options.addRotations) {
2717
+ if (!sawface) {
2718
+ cutplanes.push(facenormal.makecut(10));
2719
+ }
2720
+ if (!sawvertex) {
2721
+ cutplanes.push(vertexnormal.makecut(10));
2722
+ }
2723
+ if (!sawedge) {
2724
+ cutplanes.push(edgenormal.makecut(10));
2725
+ }
2726
+ }
2727
+ this.baseFaces = [];
2728
+ for (const baseplanerot of this.basePlaneRotations) {
2729
+ const face = baseplanerot.rotateface(firstface);
2730
+ this.baseFaces.push(new Face(face));
2731
+ }
2732
+ const facenames = [];
2733
+ const faceplanes = [];
2734
+ const vertexnames = [];
2735
+ const edgenames = [];
2736
+ const edgesperface = firstface.length;
2737
+ function searchaddelement(a, p, name) {
2738
+ for (const el of a) {
2739
+ if (el[0].dist(p) < eps3) {
2740
+ el.push(name);
2741
+ return;
2742
+ }
2743
+ }
2744
+ a.push([p, name]);
2745
+ }
2746
+ for (let i = 0; i < this.basePlaneRotations.length; i++) {
2747
+ const face = this.basePlaneRotations[i].rotateface(firstface);
2748
+ for (let j = 0; j < face.length; j++) {
2749
+ const jj = (j + 1) % face.length;
2750
+ const midpoint = face[j].sum(face[jj]).smul(0.5);
2751
+ searchaddelement(edgenames, midpoint, i);
2752
+ }
2753
+ }
2754
+ const otherfaces = [];
2755
+ for (let i = 0; i < this.basePlaneRotations.length; i++) {
2756
+ const face = this.basePlaneRotations[i].rotateface(firstface);
2757
+ const facelist = [];
2758
+ for (let j = 0; j < face.length; j++) {
2759
+ const jj = (j + 1) % face.length;
2760
+ const midpoint = face[j].sum(face[jj]).smul(0.5);
2761
+ const el = edgenames[findelement(edgenames, midpoint)];
2762
+ if (i === el[1]) {
2763
+ facelist.push(el[2]);
2764
+ } else if (i === el[2]) {
2765
+ facelist.push(el[1]);
2766
+ } else {
2767
+ throw Error("Could not find edge");
2768
+ }
2769
+ }
2770
+ otherfaces.push(facelist);
2771
+ }
2772
+ const facenametoindex = {};
2773
+ const faceindextoname = [];
2774
+ faceindextoname.push(net[0][0]);
2775
+ facenametoindex[net[0][0]] = 0;
2776
+ faceindextoname[otherfaces[0][0]] = net[0][1];
2777
+ facenametoindex[net[0][1]] = otherfaces[0][0];
2778
+ for (const neti of net) {
2779
+ const f0 = neti[0];
2780
+ const fi = facenametoindex[f0];
2781
+ if (fi === void 0) {
2782
+ throw Error("Bad edge description; first edge not connected");
2783
+ }
2784
+ let ii = -1;
2785
+ for (let j = 0; j < otherfaces[fi].length; j++) {
2786
+ const fn2 = faceindextoname[otherfaces[fi][j]];
2787
+ if (fn2 !== void 0 && fn2 === neti[1]) {
2788
+ ii = j;
2789
+ break;
2790
+ }
2791
+ }
2792
+ if (ii < 0) {
2793
+ throw Error("First element of a net not known");
2794
+ }
2795
+ for (let j = 2; j < neti.length; j++) {
2796
+ if (neti[j] === "") {
2797
+ continue;
2798
+ }
2799
+ const of = otherfaces[fi][(j + ii - 1) % edgesperface];
2800
+ const fn2 = faceindextoname[of];
2801
+ if (fn2 !== void 0 && fn2 !== neti[j]) {
2802
+ throw Error("Face mismatch in net");
2803
+ }
2804
+ faceindextoname[of] = neti[j];
2805
+ facenametoindex[neti[j]] = of;
2806
+ }
2807
+ }
2808
+ for (let i = 0; i < this.basePlaneRotations.length; i++) {
2809
+ const face = this.basePlaneRotations[i].rotateface(firstface);
2810
+ const faceplane = boundary.rotateplane(this.basePlaneRotations[i]);
2811
+ const facename = faceindextoname[i];
2812
+ facenames.push([face, facename]);
2813
+ faceplanes.push([faceplane, facename]);
2814
+ }
2815
+ for (let i = 0; i < this.basePlaneRotations.length; i++) {
2816
+ const face = this.basePlaneRotations[i].rotateface(firstface);
2817
+ const facename = faceindextoname[i];
2818
+ for (let j = 0; j < face.length; j++) {
2819
+ const jj = (j + 1) % face.length;
2820
+ const midpoint = face[j].sum(face[jj]).smul(0.5);
2821
+ const jjj = (j + 2) % face.length;
2822
+ const midpoint2 = face[jj].sum(face[jjj]).smul(0.5);
2823
+ const e1 = findelement(edgenames, midpoint);
2824
+ const e2 = findelement(edgenames, midpoint2);
2825
+ searchaddelement(vertexnames, face[jj], [facename, e2, e1]);
2826
+ }
2827
+ }
2828
+ this.swizzler = new FaceNameSwizzler(facenames.map((_) => _[1]));
2829
+ const sep = this.swizzler.prefixFree ? "" : "_";
2830
+ const oridata = orientationDefaults[this.baseFaceCount];
2831
+ const markedface = [];
2832
+ for (let i = 0; i < this.baseFaceCount; i++) {
2833
+ markedface[1 << i] = i;
2834
+ }
2835
+ {
2836
+ const oriprefs = oridata["v"];
2837
+ for (const name of oriprefs) {
2838
+ const fn = this.swizzler.splitByFaceNames(name);
2839
+ let bits = 0;
2840
+ for (const i of fn) {
2841
+ bits |= 1 << i;
2842
+ }
2843
+ markedface[bits] = fn[0];
2844
+ }
2845
+ }
2846
+ {
2847
+ const oriprefs = oridata["e"];
2848
+ for (const name of oriprefs) {
2849
+ const fn = this.swizzler.splitByFaceNames(name);
2850
+ let bits = 0;
2851
+ for (const i of fn) {
2852
+ bits |= 1 << i;
2853
+ }
2854
+ markedface[bits] = fn[0];
2855
+ }
2856
+ }
2857
+ {
2858
+ const oriprefs = oridata["c"];
2859
+ for (const name of oriprefs) {
2860
+ const fn = this.swizzler.splitByFaceNames(name);
2861
+ const bits = 1 << fn[0] | 1 << this.baseFaceCount;
2862
+ markedface[bits] = fn[1];
2863
+ }
2864
+ }
2865
+ for (let i = 0; i < edgenames.length; i++) {
2866
+ if (edgenames[i].length !== 3) {
2867
+ throw Error(`Bad length in edge names ${edgenames[i]}`);
2868
+ }
2869
+ const f1 = edgenames[i][1];
2870
+ const f2 = edgenames[i][2];
2871
+ let c1 = faceindextoname[f1];
2872
+ const c2 = faceindextoname[f2];
2873
+ const bits = 1 << f1 | 1 << f2;
2874
+ if (markedface[bits] === f1) {
2875
+ c1 = c1 + sep + c2;
2876
+ } else {
2877
+ c1 = c2 + sep + c1;
2878
+ }
2879
+ edgenames[i] = [edgenames[i][0], c1];
2880
+ }
2881
+ for (let i = 0; i < vertexnames.length; i++) {
2882
+ let bits = 0;
2883
+ if (vertexnames[i].length < 4) {
2884
+ throw Error("Bad length in vertex names");
2885
+ }
2886
+ for (let j = 1; j < vertexnames[i].length; j++) {
2887
+ bits |= 1 << facenametoindex[vertexnames[i][j][0]];
2888
+ }
2889
+ const fi = markedface[bits];
2890
+ let st = -1;
2891
+ for (let j = 1; j < vertexnames[i].length; j++) {
2892
+ if (fi === facenametoindex[vertexnames[i][j][0]]) {
2893
+ st = j;
2894
+ }
2895
+ }
2896
+ if (st < 0) {
2897
+ throw Error(
2898
+ "Internal error; couldn't find face name when fixing corners"
2899
+ );
2900
+ }
2901
+ let r = "";
2902
+ for (let j = 1; j < vertexnames[i].length; j++) {
2903
+ if (j === 1) {
2904
+ r = vertexnames[i][st][0];
2905
+ } else {
2906
+ r = r + sep + vertexnames[i][st][0];
2907
+ }
2908
+ for (let k = 1; k < vertexnames[i].length; k++) {
2909
+ if (vertexnames[i][st][1] === vertexnames[i][k][2]) {
2910
+ st = k;
2911
+ break;
2912
+ }
2913
+ }
2914
+ }
2915
+ vertexnames[i] = [vertexnames[i][0], r];
2916
+ }
2917
+ this.markedFaceLookup = markedface;
2918
+ if (this.options.verbosity > 1) {
2919
+ console.log(`# Face names: ${facenames.map((_) => _[1]).join(" ")}`);
2920
+ console.log(`# Edge names: ${edgenames.map((_) => _[1]).join(" ")}`);
2921
+ console.log(`# Vertex names: ${vertexnames.map((_) => _[1]).join(" ")}`);
2922
+ }
2923
+ const geonormals = [];
2924
+ for (const faceplane of faceplanes) {
2925
+ geonormals.push([faceplane[0].makenormal(), faceplane[1], "f"]);
2926
+ }
2927
+ for (const edgename of edgenames) {
2928
+ geonormals.push([edgename[0].makenormal(), edgename[1], "e"]);
2929
+ }
2930
+ for (const vertexname of vertexnames) {
2931
+ geonormals.push([vertexname[0].makenormal(), vertexname[1], "v"]);
2932
+ }
2933
+ this.faceNames = facenames;
2934
+ this.facePlanes = faceplanes;
2935
+ this.edgeNames = edgenames;
2936
+ this.vertexNames = vertexnames;
2937
+ this.geometryNormals = geonormals;
2938
+ const geonormalnames = geonormals.map((_) => _[1]);
2939
+ this.swizzler.setGripNames(geonormalnames);
2940
+ if (this.options.verbosity > 0) {
2941
+ console.log(
2942
+ `# Distances: face ${1} edge ${this.edgeDistance} vertex ${this.vertexDistance}`
2943
+ );
2944
+ }
2945
+ for (let c = 0; c < cutplanes.length; c++) {
2946
+ for (const rotation of this.rotations) {
2947
+ const q = cutplanes[c].rotateplane(rotation);
2948
+ let wasseen = false;
2949
+ for (const moveplane of this.movePlanes) {
2950
+ if (q.sameplane(moveplane)) {
2951
+ wasseen = true;
2952
+ break;
2953
+ }
2954
+ }
2955
+ if (!wasseen) {
2956
+ this.movePlanes.push(q);
2957
+ if (intersects[c]) {
2958
+ this.movePlanesFiltered.push(q);
2959
+ }
2960
+ }
2961
+ }
2962
+ }
2963
+ let ft = new FaceTree(firstface);
2964
+ const tar = this.movePlanesFiltered.slice();
2965
+ let rval = 31;
2966
+ for (let i = 0; i < tar.length; i++) {
2967
+ const j = i + Math.floor((tar.length - i) * (rval / 65536));
2968
+ ft = ft.split(tar[j]);
2969
+ tar[j] = tar[i];
2970
+ rval = (rval * 1657 + 101) % 65536;
2971
+ }
2972
+ const faces = ft.collect([], true);
2973
+ this.faces = faces;
2974
+ if (this.options.verbosity > 0) {
2975
+ console.log(`# Faces is now ${faces.length}`);
2976
+ }
2977
+ this.stickersPerFace = faces.length;
2978
+ const simplerot = [];
2979
+ const cm = centermassface(firstface);
2980
+ for (const rotation of this.rotations) {
2981
+ const f = rotation.rotateface(firstface);
2982
+ if (cm.dist(centermassface(f)) < eps3) {
2983
+ simplerot.push(rotation);
2984
+ }
2985
+ }
2986
+ const finished = new Array(faces.length);
2987
+ const sortme = [];
2988
+ for (let i = 0; i < faces.length; i++) {
2989
+ const cm2 = faces[i].centermass();
2990
+ sortme.push([cm.dist(cm2), cm2, i]);
2991
+ }
2992
+ sortme.sort((a, b) => a[0] - b[0]);
2993
+ for (let ii = 0; ii < faces.length; ii++) {
2994
+ const i = sortme[ii][2];
2995
+ if (!finished[i]) {
2996
+ finished[i] = true;
2997
+ for (const rot of simplerot) {
2998
+ const f2 = faces[i].rotate(rot);
2999
+ const cm2 = f2.centermass();
3000
+ for (let kk = ii + 1; kk < faces.length; kk++) {
3001
+ if (sortme[kk][0] - sortme[ii][0] > eps3) {
3002
+ break;
3003
+ }
3004
+ const k = sortme[kk][2];
3005
+ if (!finished[k] && cm2.dist(sortme[kk][1]) < eps3) {
3006
+ finished[k] = true;
3007
+ faces[k] = f2;
3008
+ break;
3009
+ }
3010
+ }
3011
+ }
3012
+ }
3013
+ }
3014
+ this.shortestEdge = 1e99;
3015
+ for (const face of faces) {
3016
+ for (let j = 0; j < face.length; j++) {
3017
+ const k = (j + 1) % face.length;
3018
+ const t = face.get(j).dist(face.get(k));
3019
+ if (t < this.shortestEdge) {
3020
+ this.shortestEdge = t;
3021
+ }
3022
+ }
3023
+ }
3024
+ if (this.options.verbosity > 0) {
3025
+ console.log(`# Short edge is ${this.shortestEdge}`);
3026
+ }
3027
+ if (shape === "c" && sawface && !sawedge && !sawvertex) {
3028
+ this.addNotationMapper = "NxNxNCubeMapper";
3029
+ this.setReidOrSpeffzOrder = true;
3030
+ }
3031
+ if (shape === "c" && sawvertex && !sawface && !sawedge) {
3032
+ this.addNotationMapper = "SkewbMapper";
3033
+ }
3034
+ if (shape === "t" && (sawvertex || sawface) && !sawedge) {
3035
+ this.addNotationMapper = "PyraminxOrTetraminxMapper";
3036
+ }
3037
+ if (shape === "o" && sawface) {
3038
+ this.notationMapper = new FaceRenamingMapper(
3039
+ this.swizzler,
3040
+ new FaceNameSwizzler(["F", "D", "L", "BL", "R", "U", "BR", "B"])
3041
+ );
3042
+ if (!(sawedge || sawvertex)) {
3043
+ this.addNotationMapper = "FTOMapper";
3044
+ }
3045
+ }
3046
+ if (shape === "d" && sawface) {
3047
+ this.addNotationMapper = "MegaminxMapper";
3048
+ this.notationMapper = new FaceRenamingMapper(
3049
+ this.swizzler,
3050
+ new FaceNameSwizzler([
3051
+ "U",
3052
+ "F",
3053
+ "L",
3054
+ "BL",
3055
+ "BR",
3056
+ "R",
3057
+ "FR",
3058
+ "FL",
3059
+ "DL",
3060
+ "B",
3061
+ "DR",
3062
+ "D"
3063
+ ])
3064
+ );
3065
+ }
3066
+ tend(t1);
3067
+ }
3068
+ puzzleDescription;
3069
+ rotations;
3070
+ // all members of the rotation group
3071
+ basePlaneRotations;
3072
+ // unique rotations of the baseplane
3073
+ basePlanes;
3074
+ // planes, corresponding to faces
3075
+ faceNames;
3076
+ // face names
3077
+ facePlanes;
3078
+ // face planes
3079
+ edgeNames;
3080
+ // edge names
3081
+ vertexNames;
3082
+ // vertexnames
3083
+ geometryNormals;
3084
+ // all geometric directions, with names and types
3085
+ movePlanes;
3086
+ // the planes that split moves
3087
+ movePlanesFiltered;
3088
+ // the planes that split moves, filtered
3089
+ movePlaneSets;
3090
+ // the move planes, in parallel sets
3091
+ movePlaneNormals;
3092
+ // one move plane
3093
+ movesetorders;
3094
+ // the order of rotations for each move set
3095
+ movesetgeos;
3096
+ // geometric feature information for move sets
3097
+ baseFaces;
3098
+ // polytope faces before cuts
3099
+ faces;
3100
+ // all the stickers
3101
+ faceCenterMass;
3102
+ // center of mass of all faces
3103
+ baseFaceCount;
3104
+ // number of base faces
3105
+ stickersPerFace;
3106
+ // number of stickers per face
3107
+ shortestEdge;
3108
+ markedFaceLookup;
3109
+ // given a bitmap of faces, identify the marked one
3110
+ cubies;
3111
+ // the cubies
3112
+ vertexDistance;
3113
+ // vertex distance
3114
+ edgeDistance;
3115
+ // edge distance
3116
+ faceToCubie;
3117
+ // map a face to a cubie index
3118
+ faceToCubieOrd;
3119
+ // map a face to a cubie ord
3120
+ moveRotations;
3121
+ // move rotations
3122
+ faceListHash;
3123
+ // face list by key
3124
+ cubieSetNames;
3125
+ // cubie set names
3126
+ cubieOrbitSizes;
3127
+ // the size of each orbit
3128
+ cubieSetNums;
3129
+ cubieOrdNums;
3130
+ orbitOrientations;
3131
+ // the orientation size of each orbit
3132
+ cubieValueMap;
3133
+ // the map for identical cubies
3134
+ cubieSetCubies;
3135
+ // cubies in each cubie set
3136
+ cmovesBySlice = [];
3137
+ // cmoves as perms by slice
3138
+ parsedMoveList;
3139
+ // parsed move list
3140
+ duplicatedFaces = [];
3141
+ // which faces are duplicated
3142
+ duplicatedCubies = [];
3143
+ // which cubies are duplicated
3144
+ fixedCubie = -1;
3145
+ // fixed cubie, if any
3146
+ net = [];
3147
+ colors = [];
3148
+ swizzler;
3149
+ notationMapper = new NullMapper();
3150
+ addNotationMapper = "";
3151
+ setReidOrSpeffzOrder = false;
3152
+ options;
3153
+ keyface(face) {
3154
+ return this.keyface2(face.centermass());
3155
+ }
3156
+ keyface2(cm) {
3157
+ let s = "";
3158
+ const sfcc = String.fromCharCode;
3159
+ for (const moveplaneset of this.movePlaneSets) {
3160
+ if (moveplaneset.length > 0) {
3161
+ const dv = cm.dot(moveplaneset[0]);
3162
+ let t = 0;
3163
+ let b = 1;
3164
+ while (b * 2 <= moveplaneset.length) {
3165
+ b *= 2;
3166
+ }
3167
+ for (; b > 0; b >>= 1) {
3168
+ if (t + b <= moveplaneset.length && dv > moveplaneset[t + b - 1].a) {
3169
+ t += b;
3170
+ }
3171
+ }
3172
+ if (t < 47) {
3173
+ s = s + sfcc(33 + t);
3174
+ } else if (t < 47 + 47 * 47) {
3175
+ s = s + sfcc(33 + 47 + Math.floor(t / 47) - 1) + sfcc(33 + t % 47);
3176
+ } else if (t < 47 + 47 * 47 + 47 * 47 * 47) {
3177
+ s = s + sfcc(33 + 47 + Math.floor((t - 47) / (47 * 47) - 1)) + sfcc(33 + 47 + Math.floor((t - 47) / 47) % 47) + sfcc(33 + t % 47);
3178
+ } else {
3179
+ throw Error("Too many slices for cubie encoding");
3180
+ }
3181
+ }
3182
+ }
3183
+ return s;
3184
+ }
3185
+ // same as above, but instead of returning an encoded string, return
3186
+ // an array with offsets.
3187
+ keyface3(face) {
3188
+ const cm = face.centermass();
3189
+ const r = [];
3190
+ for (const moveplaneset of this.movePlaneSets) {
3191
+ if (moveplaneset.length > 0) {
3192
+ const dv = cm.dot(moveplaneset[0]);
3193
+ let t = 0;
3194
+ let b = 1;
3195
+ while (b * 2 <= moveplaneset.length) {
3196
+ b *= 2;
3197
+ }
3198
+ for (; b > 0; b >>= 1) {
3199
+ if (t + b <= moveplaneset.length && dv > moveplaneset[t + b - 1].a) {
3200
+ t += b;
3201
+ }
3202
+ }
3203
+ r.push(t);
3204
+ }
3205
+ }
3206
+ return r;
3207
+ }
3208
+ findface(cm) {
3209
+ const key = this.keyface2(cm);
3210
+ const arr = this.faceListHash.get(key);
3211
+ if (arr.length === 1) {
3212
+ return arr[0];
3213
+ }
3214
+ for (let i = 0; i + 1 < arr.length; i++) {
3215
+ const face2 = this.faceListHash.get(key)[i];
3216
+ if (Math.abs(cm.dist(this.faceCenterMass[face2])) < eps3) {
3217
+ return face2;
3218
+ }
3219
+ }
3220
+ return arr[arr.length - 1];
3221
+ }
3222
+ project2d(facen, edgen, targvec) {
3223
+ const face = this.faceNames[facen][0];
3224
+ const edgen2 = (edgen + 1) % face.length;
3225
+ const plane = this.basePlanes[facen];
3226
+ let x0 = face[edgen2].sub(face[edgen]);
3227
+ const olen = x0.len();
3228
+ x0 = x0.normalize();
3229
+ const y0 = x0.cross(plane).normalize();
3230
+ let delta = targvec[1].sub(targvec[0]);
3231
+ const len = delta.len() / olen;
3232
+ delta = delta.normalize();
3233
+ const cosr = delta.b;
3234
+ const sinr = delta.c;
3235
+ const x1 = x0.smul(cosr).sub(y0.smul(sinr)).smul(len);
3236
+ const y1 = y0.smul(cosr).sum(x0.smul(sinr)).smul(len);
3237
+ const off = new Quat(
3238
+ 0,
3239
+ targvec[0].b - x1.dot(face[edgen]),
3240
+ targvec[0].c - y1.dot(face[edgen]),
3241
+ 0
3242
+ );
3243
+ return [x1, y1, off];
3244
+ }
3245
+ // Given an string of uppercase letters, make a bitmask
3246
+ // indicating what letters are in it. Cheap swizzling
3247
+ // for internal use.
3248
+ upperStringToBitSet(geo) {
3249
+ let r = 0;
3250
+ for (let i = 0; i < geo.length; i++) {
3251
+ r |= 1 << geo.charCodeAt(i) - 65;
3252
+ }
3253
+ return r;
3254
+ }
3255
+ allstickers() {
3256
+ const t1 = tstart("allstickers");
3257
+ this.faces = expandfaces(this.basePlaneRotations, this.faces);
3258
+ if (this.options.verbosity > 0) {
3259
+ console.log(`# Total stickers is now ${this.faces.length}`);
3260
+ }
3261
+ this.faceCenterMass = new Array(this.faces.length);
3262
+ for (let i = 0; i < this.faces.length; i++) {
3263
+ this.faceCenterMass[i] = this.faces[i].centermass();
3264
+ }
3265
+ const moveplanesets = [];
3266
+ const moveplanenormals = [];
3267
+ for (const q of this.movePlanes) {
3268
+ const qnormal = q.makenormal();
3269
+ let wasseen = false;
3270
+ for (const moveplanenormal of moveplanenormals) {
3271
+ if (qnormal.sameplane(moveplanenormal.makenormal())) {
3272
+ wasseen = true;
3273
+ }
3274
+ }
3275
+ if (!wasseen) {
3276
+ moveplanenormals.push(qnormal);
3277
+ moveplanesets.push([]);
3278
+ }
3279
+ }
3280
+ for (const q of this.movePlanesFiltered) {
3281
+ const qnormal = q.makenormal();
3282
+ for (let j = 0; j < moveplanenormals.length; j++) {
3283
+ if (qnormal.sameplane(moveplanenormals[j])) {
3284
+ moveplanesets[j].push(q);
3285
+ break;
3286
+ }
3287
+ }
3288
+ }
3289
+ for (let i = 0; i < moveplanesets.length; i++) {
3290
+ const q = moveplanesets[i].map((_) => _.normalizeplane());
3291
+ const goodnormal = moveplanenormals[i];
3292
+ for (let j = 0; j < q.length; j++) {
3293
+ if (q[j].makenormal().dist(goodnormal) > eps3) {
3294
+ q[j] = q[j].smul(-1);
3295
+ }
3296
+ }
3297
+ q.sort((a, b) => a.a - b.a);
3298
+ moveplanesets[i] = q;
3299
+ }
3300
+ this.movePlaneSets = moveplanesets;
3301
+ this.movePlaneNormals = moveplanenormals;
3302
+ const sizes = moveplanesets.map((_) => _.length);
3303
+ if (this.options.verbosity > 0) {
3304
+ console.log(`# Move plane sets: ${sizes}`);
3305
+ }
3306
+ const moverotations = [];
3307
+ for (let i = 0; i < moveplanesets.length; i++) {
3308
+ moverotations.push([]);
3309
+ }
3310
+ for (const q of this.rotations) {
3311
+ if (Math.abs(Math.abs(q.a) - 1) < eps3) {
3312
+ continue;
3313
+ }
3314
+ const qnormal = q.makenormal();
3315
+ for (let j = 0; j < moveplanesets.length; j++) {
3316
+ if (qnormal.sameplane(moveplanenormals[j])) {
3317
+ moverotations[j].push(q);
3318
+ break;
3319
+ }
3320
+ }
3321
+ }
3322
+ this.moveRotations = moverotations;
3323
+ for (let i = 0; i < moverotations.length; i++) {
3324
+ const r = moverotations[i];
3325
+ const goodnormal = r[0].makenormal();
3326
+ for (let j = 0; j < r.length; j++) {
3327
+ if (goodnormal.dist(r[j].makenormal()) > eps3) {
3328
+ r[j] = r[j].smul(-1);
3329
+ }
3330
+ }
3331
+ r.sort((a, b) => a.angle() - b.angle());
3332
+ if (moverotations[i][0].dot(moveplanenormals[i]) < 0) {
3333
+ r.reverse();
3334
+ }
3335
+ }
3336
+ const sizes2 = moverotations.map((_) => 1 + _.length);
3337
+ this.movesetorders = sizes2;
3338
+ const movesetgeos = [];
3339
+ let gtype = "?";
3340
+ for (let i = 0; i < moveplanesets.length; i++) {
3341
+ const p0 = moveplanenormals[i];
3342
+ let neg = null;
3343
+ let pos = null;
3344
+ for (const geonormal of this.geometryNormals) {
3345
+ const d = p0.dot(geonormal[0]);
3346
+ if (Math.abs(d - 1) < eps3) {
3347
+ pos = [geonormal[1], geonormal[2]];
3348
+ gtype = geonormal[2];
3349
+ } else if (Math.abs(d + 1) < eps3) {
3350
+ neg = [geonormal[1], geonormal[2]];
3351
+ gtype = geonormal[2];
3352
+ }
3353
+ }
3354
+ if (pos === null || neg === null) {
3355
+ throw Error("Saw positive or negative sides as null");
3356
+ }
3357
+ movesetgeos.push([
3358
+ pos[0],
3359
+ pos[1],
3360
+ neg[0],
3361
+ neg[1],
3362
+ 1 + moveplanesets[i].length
3363
+ ]);
3364
+ if (this.addNotationMapper === "NxNxNCubeMapper" && gtype === "f") {
3365
+ this.notationMapper = new NxNxNCubeMapper(1 + moveplanesets[i].length);
3366
+ this.addNotationMapper = "";
3367
+ }
3368
+ if (this.addNotationMapper === "SkewbMapper" && moveplanesets[0].length === 1) {
3369
+ this.notationMapper = new SkewbNotationMapper(this.swizzler);
3370
+ this.addNotationMapper = "";
3371
+ }
3372
+ if (this.addNotationMapper === "PyraminxOrTetraminxMapper") {
3373
+ if (moveplanesets[0].length === 2 && moveplanesets[0][0].a === 0.333333333333333 && moveplanesets[0][1].a === 1.66666666666667) {
3374
+ this.notationMapper = new PyraminxNotationMapper(this.swizzler);
3375
+ this.addNotationMapper = "";
3376
+ } else {
3377
+ this.notationMapper = new TetraminxNotationMapper(this.swizzler);
3378
+ this.addNotationMapper = "";
3379
+ }
3380
+ }
3381
+ if (this.addNotationMapper === "MegaminxMapper" && gtype === "f") {
3382
+ if (1 + moveplanesets[i].length === 3) {
3383
+ this.notationMapper = new MegaminxScramblingNotationMapper(
3384
+ this.notationMapper
3385
+ );
3386
+ }
3387
+ this.addNotationMapper = "";
3388
+ }
3389
+ if (this.addNotationMapper === "FTOMapper" && gtype === "f") {
3390
+ if (1 + moveplanesets[i].length === 3) {
3391
+ this.notationMapper = new FTONotationMapper(
3392
+ this.notationMapper,
3393
+ this.swizzler
3394
+ );
3395
+ }
3396
+ this.addNotationMapper = "";
3397
+ }
3398
+ }
3399
+ this.movesetgeos = movesetgeos;
3400
+ const facelisthash = /* @__PURE__ */ new Map();
3401
+ const faces = this.faces;
3402
+ for (let i = 0; i < faces.length; i++) {
3403
+ const face = faces[i];
3404
+ const s = this.keyface(face);
3405
+ if (!facelisthash.get(s)) {
3406
+ facelisthash.set(s, [i]);
3407
+ } else {
3408
+ const arr = facelisthash.get(s);
3409
+ arr.push(i);
3410
+ if (arr.length === this.baseFaceCount) {
3411
+ if (this.options.verbosity > 0) {
3412
+ console.log("# Splitting core.");
3413
+ }
3414
+ for (let suff = 0; suff < arr.length; suff++) {
3415
+ const s2 = `${s} ${suff}`;
3416
+ facelisthash.set(s2, [arr[suff]]);
3417
+ }
3418
+ }
3419
+ }
3420
+ }
3421
+ this.faceListHash = facelisthash;
3422
+ if (this.options.verbosity > 0) {
3423
+ console.log(`# Cubies: ${facelisthash.size}`);
3424
+ }
3425
+ const cubies = [];
3426
+ const facetocubie = [];
3427
+ const facetoord = [];
3428
+ for (const facelist of facelisthash.values()) {
3429
+ if (facelist.length === this.baseFaceCount) {
3430
+ continue;
3431
+ }
3432
+ if (facelist.length > 1) {
3433
+ const cm = facelist.map((_) => faces[_].centermass());
3434
+ const cmall = centermassface(cm);
3435
+ for (let looplimit = 0; facelist.length > 2; looplimit++) {
3436
+ let changed = false;
3437
+ for (let i = 0; i < facelist.length; i++) {
3438
+ const j = (i + 1) % facelist.length;
3439
+ if (cmall.dot(cm[i].cross(cm[j])) < 0) {
3440
+ const u = cm[i];
3441
+ cm[i] = cm[j];
3442
+ cm[j] = u;
3443
+ const v = facelist[i];
3444
+ facelist[i] = facelist[j];
3445
+ facelist[j] = v;
3446
+ changed = true;
3447
+ }
3448
+ }
3449
+ if (!changed) {
3450
+ break;
3451
+ }
3452
+ if (looplimit > 1e3) {
3453
+ throw Error("Bad epsilon math; too close to border");
3454
+ }
3455
+ }
3456
+ let bits = 0;
3457
+ for (const f of facelist) {
3458
+ bits |= 1 << Math.floor(f / this.stickersPerFace);
3459
+ }
3460
+ const markedface = this.markedFaceLookup[bits];
3461
+ let mini = -1;
3462
+ for (let i = 0; i < facelist.length; i++) {
3463
+ if (Math.floor(facelist[i] / this.stickersPerFace) === markedface) {
3464
+ mini = i;
3465
+ }
3466
+ }
3467
+ if (mini < 0) {
3468
+ throw Error("Could not find marked face in list");
3469
+ }
3470
+ if (mini !== 0) {
3471
+ const ofacelist = facelist.slice();
3472
+ for (let i = 0; i < facelist.length; i++) {
3473
+ facelist[i] = ofacelist[(mini + i) % facelist.length];
3474
+ }
3475
+ }
3476
+ }
3477
+ for (let j = 0; j < facelist.length; j++) {
3478
+ const k = facelist[j];
3479
+ facetocubie[k] = cubies.length;
3480
+ facetoord[k] = j;
3481
+ }
3482
+ cubies.push(facelist);
3483
+ }
3484
+ this.cubies = cubies;
3485
+ this.faceToCubie = facetocubie;
3486
+ this.faceToCubieOrd = facetoord;
3487
+ const typenames = ["?", "CENTERS", "EDGES", "CORNERS", "C4RNER", "C5RNER"];
3488
+ const cubiesetnames = [];
3489
+ const cubietypecounts = [0, 0, 0, 0, 0, 0];
3490
+ const orbitoris = [];
3491
+ const seen = [];
3492
+ let cubiesetnum = 0;
3493
+ const cubiesetnums = [];
3494
+ const cubieordnums = [];
3495
+ const cubieords = [];
3496
+ const cubievaluemap = [];
3497
+ const getcolorkey = (cubienum) => {
3498
+ return cubies[cubienum].map((_) => this.getfaceindex(_)).join(" ");
3499
+ };
3500
+ const cubiesetcubies = [];
3501
+ for (let i = 0; i < cubies.length; i++) {
3502
+ const cubie = cubies[i];
3503
+ if (cubie.length === 0) {
3504
+ continue;
3505
+ }
3506
+ if (seen[i]) {
3507
+ continue;
3508
+ }
3509
+ const cubiekeymap = {};
3510
+ let cubievalueid = 0;
3511
+ cubieords.push(0);
3512
+ cubiesetcubies.push([]);
3513
+ const facecnt = cubie.length;
3514
+ const typectr = cubietypecounts[facecnt]++;
3515
+ let typename = typenames[facecnt];
3516
+ if (typename === void 0 || facecnt === this.baseFaceCount) {
3517
+ typename = "CORE";
3518
+ }
3519
+ typename = typename + (typectr === 0 ? "" : typectr + 1);
3520
+ cubiesetnames[cubiesetnum] = typename;
3521
+ orbitoris[cubiesetnum] = facecnt;
3522
+ const queue = [i];
3523
+ let qg = 0;
3524
+ seen[i] = true;
3525
+ while (qg < queue.length) {
3526
+ const cind = queue[qg++];
3527
+ const cubiecolorkey = getcolorkey(cind);
3528
+ if (cubie.length > 1 || cubiekeymap[cubiecolorkey] === void 0) {
3529
+ cubiekeymap[cubiecolorkey] = cubievalueid++;
3530
+ }
3531
+ cubievaluemap[cind] = cubiekeymap[cubiecolorkey];
3532
+ cubiesetnums[cind] = cubiesetnum;
3533
+ cubiesetcubies[cubiesetnum].push(cind);
3534
+ cubieordnums[cind] = cubieords[cubiesetnum]++;
3535
+ if (queue.length < this.rotations.length) {
3536
+ const cm = this.faceCenterMass[cubies[cind][0]];
3537
+ for (const moverotation of moverotations) {
3538
+ const tq = this.faceToCubie[this.findface(cm.rotatepoint(moverotation[0]))];
3539
+ if (!seen[tq]) {
3540
+ queue.push(tq);
3541
+ seen[tq] = true;
3542
+ }
3543
+ }
3544
+ }
3545
+ }
3546
+ cubiesetnum++;
3547
+ }
3548
+ if (this.setReidOrSpeffzOrder && 4 <= this.stickersPerFace) {
3549
+ const reidorder = [
3550
+ [
3551
+ "UF",
3552
+ "UR",
3553
+ "UB",
3554
+ "UL",
3555
+ "DF",
3556
+ "DR",
3557
+ "DB",
3558
+ "DL",
3559
+ "FR",
3560
+ "FL",
3561
+ "BR",
3562
+ "BL"
3563
+ ],
3564
+ ["UFR", "URB", "UBL", "ULF", "DRF", "DFL", "DLB", "DBR"],
3565
+ ["U", "L", "F", "R", "B", "D"]
3566
+ ];
3567
+ const spefffaceorder = ["U", "L", "F", "R", "B", "D"];
3568
+ const speffcornerorder = [
3569
+ "UBL",
3570
+ "URB",
3571
+ "UFR",
3572
+ "ULF",
3573
+ "UBL",
3574
+ "ULF",
3575
+ "DFL",
3576
+ "DLB",
3577
+ "ULF",
3578
+ "UFR",
3579
+ "DRF",
3580
+ "DFL",
3581
+ "UFR",
3582
+ "URB",
3583
+ "DBR",
3584
+ "DRF",
3585
+ "URB",
3586
+ "UBL",
3587
+ "DLB",
3588
+ "DBR",
3589
+ "DFL",
3590
+ "DRF",
3591
+ "DBR",
3592
+ "DLB"
3593
+ ];
3594
+ const reidmap = {};
3595
+ for (const cubie of reidorder) {
3596
+ for (let j = 0; j < cubie.length; j++) {
3597
+ reidmap[this.upperStringToBitSet(cubie[j])] = j;
3598
+ }
3599
+ }
3600
+ const cornerloc = {};
3601
+ const spefffacelookup = {};
3602
+ const speffcornerlocs = [];
3603
+ const speffuncorner = [
3604
+ -1,
3605
+ 0,
3606
+ 1,
3607
+ 0,
3608
+ 2,
3609
+ -1,
3610
+ 1,
3611
+ -1,
3612
+ 3,
3613
+ 3,
3614
+ -1,
3615
+ -1,
3616
+ 2,
3617
+ -1,
3618
+ -1,
3619
+ -1
3620
+ ];
3621
+ if (this.stickersPerFace > 9) {
3622
+ for (const vertex of this.vertexNames) {
3623
+ cornerloc[this.upperStringToBitSet(vertex[1])] = vertex[0];
3624
+ }
3625
+ for (let i = 0; i < 6; i++) {
3626
+ spefffacelookup[spefffaceorder[i]] = i;
3627
+ }
3628
+ for (const co of speffcornerorder) {
3629
+ speffcornerlocs.push(cornerloc[this.upperStringToBitSet(co)]);
3630
+ }
3631
+ }
3632
+ for (const cubieset of cubiesetcubies) {
3633
+ for (const cubienum of cubieset) {
3634
+ if (cubies[cubienum].length === 3 || this.stickersPerFace <= 9) {
3635
+ let mask = 0;
3636
+ for (const cubie of cubies[cubienum]) {
3637
+ mask |= 1 << this.faceNames[this.getfaceindex(cubie)][1].charCodeAt(0) - 65;
3638
+ }
3639
+ cubieordnums[cubienum] = reidmap[mask];
3640
+ } else {
3641
+ if (cubies[cubienum].length <= 2) {
3642
+ const ordset = [];
3643
+ for (let k = 0; k < cubies[cubienum].length; k++) {
3644
+ const sticker = cubies[cubienum][k];
3645
+ const facekey = spefffacelookup[this.faceNames[this.getfaceindex(sticker)][1]];
3646
+ let bestdist = 1e20;
3647
+ const cubieloc = this.faces[sticker].centermass();
3648
+ let bestmask = 0;
3649
+ for (let i = 0; i < 4; i++) {
3650
+ const t = cubieloc.dist(speffcornerlocs[4 * facekey + i]);
3651
+ if (t + eps3 < bestdist) {
3652
+ bestdist = t;
3653
+ bestmask = 1 << i;
3654
+ } else if (t < bestdist + eps3) {
3655
+ bestmask |= 1 << i;
3656
+ }
3657
+ }
3658
+ bestmask = speffuncorner[bestmask];
3659
+ if (bestmask >= 0) {
3660
+ const speffind = 4 * facekey + bestmask;
3661
+ ordset.push([speffind, speffcornerorder[speffind]]);
3662
+ }
3663
+ }
3664
+ if (ordset.length > 0) {
3665
+ if (cubies[cubienum].length === 1) {
3666
+ cubieordnums[cubienum] = ordset[0][0];
3667
+ } else if (ordset.length === 2 && ordset[0][1] === ordset[1][1]) {
3668
+ let k = 0;
3669
+ const f0c = spefffaceorder[ordset[0][0] >> 2];
3670
+ const f1c = spefffaceorder[ordset[1][0] >> 2];
3671
+ const corn = ordset[0][1];
3672
+ while (k < 3 && (f0c === corn.charAt(k) || f1c === corn.charAt(k))) {
3673
+ k++;
3674
+ }
3675
+ if (k === 3) {
3676
+ throw Error("Internal error (2) in Speffz");
3677
+ }
3678
+ k = (k + 1) % 3;
3679
+ if (f0c === corn.charAt(k)) {
3680
+ cubieordnums[cubienum] = ordset[0][0];
3681
+ } else if (f1c === corn.charAt(k)) {
3682
+ cubieordnums[cubienum] = ordset[1][0];
3683
+ } else {
3684
+ console.log(f0c, f1c, corn, k, ordset);
3685
+ throw Error("Internal error (3) in Speffz");
3686
+ }
3687
+ }
3688
+ }
3689
+ }
3690
+ }
3691
+ }
3692
+ }
3693
+ }
3694
+ this.cubieSetNums = cubiesetnums;
3695
+ this.cubieOrdNums = cubieordnums;
3696
+ this.cubieSetNames = cubiesetnames;
3697
+ this.cubieOrbitSizes = cubieords;
3698
+ this.orbitOrientations = orbitoris;
3699
+ this.cubieValueMap = cubievaluemap;
3700
+ this.cubieSetCubies = cubiesetcubies;
3701
+ if (this.options.fixedPieceType !== null) {
3702
+ for (let i = 0; i < cubies.length; i++) {
3703
+ if (this.options.fixedPieceType === "v" && cubies[i].length > 2 || this.options.fixedPieceType === "e" && cubies[i].length === 2 || this.options.fixedPieceType === "f" && cubies[i].length === 1) {
3704
+ this.fixedCubie = i;
3705
+ break;
3706
+ }
3707
+ }
3708
+ if (this.fixedCubie < 0) {
3709
+ throw Error(
3710
+ `Could not find a cubie of type ${this.options.fixedPieceType} to fix.`
3711
+ );
3712
+ }
3713
+ }
3714
+ if (this.options.verbosity > 0) {
3715
+ console.log(`# Cubie orbit sizes ${cubieords}`);
3716
+ }
3717
+ tend(t1);
3718
+ }
3719
+ unswizzle(mv) {
3720
+ const newmv = this.notationMapper.notationToInternal(mv);
3721
+ if (newmv === null) {
3722
+ return null;
3723
+ }
3724
+ return newmv.modified({ family: this.swizzler.unswizzle(newmv.family) });
3725
+ }
3726
+ // TODO: remove this in favor of `Move.fromString(…)`
3727
+ // We use an extremely permissive parse here; any character but
3728
+ // digits are allowed in a family name.
3729
+ stringToBlockMove(mv) {
3730
+ const re = /^(([0-9]+)-)?([0-9]+)?([^0-9]+)([0-9]+'?)?$/;
3731
+ const p = mv.match(re);
3732
+ if (p === null) {
3733
+ throw Error(`Bad move passed ${mv}`);
3734
+ }
3735
+ const grip = p[4];
3736
+ let loslice;
3737
+ let hislice;
3738
+ if (p[2] !== void 0) {
3739
+ if (p[3] === void 0) {
3740
+ throw Error("Missing second number in range");
3741
+ }
3742
+ loslice = parseInt(p[2], 10);
3743
+ }
3744
+ if (p[3] !== void 0) {
3745
+ hislice = parseInt(p[3], 10);
3746
+ }
3747
+ let amountstr = "1";
3748
+ let amount = 1;
3749
+ if (p[5] !== void 0) {
3750
+ amountstr = p[5];
3751
+ if (amountstr[0] === "'") {
3752
+ amountstr = `-${amountstr.substring(1)}`;
3753
+ }
3754
+ amount = parseInt(amountstr, 10);
3755
+ }
3756
+ return new Move(new QuantumMove(grip, hislice, loslice), amount);
3757
+ }
3758
+ parseMove(move) {
3759
+ const bm = this.notationMapper.notationToInternal(move);
3760
+ if (bm === null) {
3761
+ throw Error(`Bad move ${move.family}`);
3762
+ }
3763
+ move = bm;
3764
+ let grip = move.family;
3765
+ let fullrotation = false;
3766
+ if (grip.endsWith("v") && grip[0] <= "Z") {
3767
+ if (move.innerLayer !== void 0 || move.outerLayer !== void 0) {
3768
+ throw Error("Cannot use a prefix with full cube rotations");
3769
+ }
3770
+ grip = grip.slice(0, -1);
3771
+ fullrotation = true;
3772
+ }
3773
+ if (grip.endsWith("w") && grip[0] <= "Z") {
3774
+ grip = grip.slice(0, -1).toLowerCase();
3775
+ }
3776
+ let geo;
3777
+ let msi = -1;
3778
+ const geoname = this.swizzler.unswizzle(grip);
3779
+ let firstgrip = false;
3780
+ for (let i = 0; i < this.movesetgeos.length; i++) {
3781
+ const g = this.movesetgeos[i];
3782
+ if (geoname === g[0]) {
3783
+ firstgrip = true;
3784
+ geo = g;
3785
+ msi = i;
3786
+ }
3787
+ if (geoname === g[2]) {
3788
+ firstgrip = false;
3789
+ geo = g;
3790
+ msi = i;
3791
+ }
3792
+ }
3793
+ let loslice = 1;
3794
+ let hislice = 1;
3795
+ if (grip.toUpperCase() !== grip) {
3796
+ hislice = 2;
3797
+ }
3798
+ if (geo === void 0) {
3799
+ throw Error(`Bad grip in move ${move.family}`);
3800
+ }
3801
+ if (move.outerLayer !== void 0) {
3802
+ loslice = move.outerLayer;
3803
+ }
3804
+ if (move.innerLayer !== void 0) {
3805
+ if (move.outerLayer === void 0) {
3806
+ hislice = move.innerLayer;
3807
+ if (grip <= "Z") {
3808
+ loslice = hislice;
3809
+ } else {
3810
+ loslice = 1;
3811
+ }
3812
+ } else {
3813
+ hislice = move.innerLayer;
3814
+ }
3815
+ }
3816
+ loslice--;
3817
+ hislice--;
3818
+ const movePlaneSets = this.movePlaneSets;
3819
+ if (fullrotation) {
3820
+ loslice = 0;
3821
+ hislice = movePlaneSets[msi].length;
3822
+ }
3823
+ if (loslice < 0 || loslice > movePlaneSets[msi].length || hislice < 0 || hislice > movePlaneSets[msi].length) {
3824
+ throw Error(
3825
+ `Bad slice spec ${loslice} ${hislice} vs ${movePlaneSets[msi].length}`
3826
+ );
3827
+ }
3828
+ if (!permissivieMoveParsing && loslice === 0 && hislice === movePlaneSets[msi].length && !fullrotation) {
3829
+ throw Error("! full puzzle rotations must be specified with v suffix.");
3830
+ }
3831
+ return [void 0, msi, loslice, hislice, firstgrip, move.amount];
3832
+ }
3833
+ parsemove(mv) {
3834
+ if (mv instanceof Move) {
3835
+ mv = mv.toString();
3836
+ }
3837
+ const r = this.parseMove(this.stringToBlockMove(mv));
3838
+ r[0] = mv;
3839
+ return r;
3840
+ }
3841
+ genperms() {
3842
+ const t1 = tstart("genperms");
3843
+ if (this.cmovesBySlice.length > 0) {
3844
+ return;
3845
+ }
3846
+ const cmovesbyslice = [];
3847
+ if (this.options.orientCenters) {
3848
+ for (let k = 0; k < this.cubies.length; k++) {
3849
+ if (this.cubies[k].length === 1) {
3850
+ const kk = this.cubies[k][0];
3851
+ const i = this.getfaceindex(kk);
3852
+ const center = this.baseFaces[i].centermass();
3853
+ if (center.dist(this.faceCenterMass[kk]) < eps3) {
3854
+ const bits = 1 << i | 1 << this.baseFaceCount;
3855
+ const towards = this.markedFaceLookup[bits];
3856
+ const normal = this.basePlanes[towards].makenormal();
3857
+ let hiv = -1;
3858
+ let hii = -1;
3859
+ for (let ii = 0; ii < this.faces[kk].length; ii++) {
3860
+ const pt = this.faces[kk].get(ii);
3861
+ const t = normal.dot(pt.sub(center));
3862
+ if (t > hiv) {
3863
+ hiv = t;
3864
+ hii = ii;
3865
+ }
3866
+ }
3867
+ const hii2 = (hii + 1) % this.faces[kk].length;
3868
+ if (Math.abs(normal.dot(this.faces[kk].get(hii2).sub(center)) - hiv) < eps3) {
3869
+ hii = hii2;
3870
+ }
3871
+ if (hii !== 0) {
3872
+ const qs = [];
3873
+ for (let ii = 0; ii < this.faces[kk].length; ii++) {
3874
+ qs.push(this.faces[kk].get((ii + hii) % this.faces[kk].length));
3875
+ }
3876
+ this.faces[kk] = new Face(qs);
3877
+ }
3878
+ const o = this.baseFaces[i].length;
3879
+ for (let m = 1; m < o; m++) {
3880
+ this.cubies[k].push(this.cubies[k][m - 1]);
3881
+ }
3882
+ this.duplicatedFaces[kk] = o;
3883
+ this.duplicatedCubies[k] = o;
3884
+ this.orbitOrientations[this.cubieSetNums[k]] = o;
3885
+ }
3886
+ }
3887
+ }
3888
+ }
3889
+ for (let k = 0; k < this.movePlaneSets.length; k++) {
3890
+ const moveplaneset = this.movePlaneSets[k];
3891
+ const slicenum = [];
3892
+ const slicecnts = [moveplaneset.length + 1, 0];
3893
+ let bhi = 1;
3894
+ while (bhi * 2 <= moveplaneset.length) {
3895
+ bhi *= 2;
3896
+ }
3897
+ for (let i = 0; i < this.faces.length; i++) {
3898
+ let t = 0;
3899
+ if (moveplaneset.length > 0) {
3900
+ const dv = this.faceCenterMass[i].dot(moveplaneset[0]);
3901
+ for (let b = bhi; b > 0; b >>= 1) {
3902
+ if (t + b <= moveplaneset.length && dv > moveplaneset[t + b - 1].a) {
3903
+ t += b;
3904
+ }
3905
+ }
3906
+ t = moveplaneset.length - t;
3907
+ }
3908
+ slicenum.push(t);
3909
+ while (slicecnts.length <= t) {
3910
+ slicecnts.push(0);
3911
+ }
3912
+ slicecnts[t]++;
3913
+ }
3914
+ const axiscmoves = new Array(slicecnts.length);
3915
+ for (let sc = 0; sc < slicecnts.length; sc++) {
3916
+ axiscmoves[sc] = [];
3917
+ }
3918
+ const cubiedone = [];
3919
+ for (let i = 0; i < this.faces.length; i++) {
3920
+ if (slicenum[i] < 0) {
3921
+ continue;
3922
+ }
3923
+ const b = [this.faceToCubie[i], this.faceToCubieOrd[i]];
3924
+ let cm = this.faceCenterMass[i];
3925
+ const ocm = cm;
3926
+ let fi2 = i;
3927
+ const sc = slicenum[fi2];
3928
+ for (; ; ) {
3929
+ slicenum[fi2] = -1;
3930
+ const cm2 = cm.rotatepoint(this.moveRotations[k][0]);
3931
+ if (cm2.dist(ocm) < eps3) {
3932
+ break;
3933
+ }
3934
+ fi2 = this.findface(cm2);
3935
+ b.push(this.faceToCubie[fi2], this.faceToCubieOrd[fi2]);
3936
+ cm = cm2;
3937
+ }
3938
+ if (b.length > 2 && this.options.orientCenters && (this.cubies[b[0]].length === 1 || this.duplicatedCubies[b[0]] > 1)) {
3939
+ if (
3940
+ // TODO: refactor to avoid non-null-assertion
3941
+ this.faceCenterMass[i].dist(
3942
+ this.baseFaces[this.getfaceindex(i)].centermass()
3943
+ ) < eps3
3944
+ ) {
3945
+ let face1 = this.faces[this.cubies[b[0]][0]];
3946
+ for (let ii = 0; ii < b.length; ii += 2) {
3947
+ const face0 = this.faces[this.cubies[b[ii]][0]];
3948
+ let o = -1;
3949
+ for (let jj = 0; jj < face1.length; jj++) {
3950
+ if (face0.get(jj).dist(face1.get(0)) < eps3) {
3951
+ o = jj;
3952
+ break;
3953
+ }
3954
+ }
3955
+ if (o < 0) {
3956
+ throw Error(
3957
+ "Couldn't find rotation of center faces; ignoring for now."
3958
+ );
3959
+ } else {
3960
+ b[ii + 1] = o;
3961
+ face1 = face1.rotate(this.moveRotations[k][0]);
3962
+ }
3963
+ }
3964
+ }
3965
+ }
3966
+ if (b.length === 2 && this.options.orientCenters) {
3967
+ const dir = this.faceCenterMass[i].dot(this.movePlaneNormals[k]);
3968
+ for (let ii = 1; ii < this.movesetorders[k]; ii++) {
3969
+ if (dir > 0) {
3970
+ b.push(b[0], ii);
3971
+ } else {
3972
+ b.push(
3973
+ b[0],
3974
+ (this.movesetorders[k] - ii) % this.movesetorders[k]
3975
+ // TODO: refactor to avoid non-null-assertion
3976
+ );
3977
+ }
3978
+ }
3979
+ }
3980
+ if (b.length > 2 && !cubiedone[b[0]]) {
3981
+ if (b.length !== 2 * this.movesetorders[k]) {
3982
+ throw Error("Bad length in perm gen");
3983
+ }
3984
+ for (const v of b) {
3985
+ axiscmoves[sc].push(v);
3986
+ }
3987
+ }
3988
+ for (let j = 0; j < b.length; j += 2) {
3989
+ cubiedone[b[j]] = true;
3990
+ }
3991
+ }
3992
+ for (let kk = 0; kk < axiscmoves.length; kk++) {
3993
+ axiscmoves[kk] = axiscmoves[kk].slice();
3994
+ }
3995
+ cmovesbyslice.push(axiscmoves);
3996
+ }
3997
+ this.cmovesBySlice = cmovesbyslice;
3998
+ if (this.options.moveList) {
3999
+ const parsedmovelist = [];
4000
+ for (const moveString of this.options.moveList) {
4001
+ parsedmovelist.push(this.parsemove(moveString));
4002
+ }
4003
+ this.parsedMoveList = parsedmovelist;
4004
+ }
4005
+ this.faceListHash.clear();
4006
+ this.faceCenterMass = [];
4007
+ tend(t1);
4008
+ }
4009
+ getboundarygeometry() {
4010
+ return {
4011
+ baseplanes: this.basePlanes,
4012
+ facenames: this.faceNames,
4013
+ faceplanes: this.facePlanes,
4014
+ vertexnames: this.vertexNames,
4015
+ edgenames: this.edgeNames,
4016
+ geonormals: this.geometryNormals
4017
+ };
4018
+ }
4019
+ getmovesets(k) {
4020
+ const slices = this.movePlaneSets[k].length;
4021
+ let r = [];
4022
+ if (this.parsedMoveList !== void 0) {
4023
+ for (const parsedmove of this.parsedMoveList) {
4024
+ if (parsedmove[1] !== k) {
4025
+ continue;
4026
+ }
4027
+ if (parsedmove[4]) {
4028
+ r.push([parsedmove[2], parsedmove[3]]);
4029
+ } else {
4030
+ r.push([slices - parsedmove[3], slices - parsedmove[2]]);
4031
+ }
4032
+ r.push(parsedmove[5]);
4033
+ }
4034
+ } else {
4035
+ const msg = this.movesetgeos[k];
4036
+ const isTetrahedron = msg[1] !== msg[3];
4037
+ if (this.options.vertexMoves && isTetrahedron && !this.options.allMoves) {
4038
+ if (msg[1] !== msg[3]) {
4039
+ for (let i = 0; i < slices; i++) {
4040
+ if (msg[1] !== "v") {
4041
+ if (this.options.outerBlockMoves) {
4042
+ r.push([i + 1, slices]);
4043
+ } else {
4044
+ r.push([i + 1, i + 1]);
4045
+ }
4046
+ r.push(1);
4047
+ } else {
4048
+ if (this.options.outerBlockMoves) {
4049
+ r.push([0, i]);
4050
+ } else {
4051
+ r.push([i, i]);
4052
+ }
4053
+ r.push(1);
4054
+ }
4055
+ }
4056
+ }
4057
+ } else {
4058
+ for (let i = 0; i <= slices; i++) {
4059
+ if (!this.options.allMoves && i + i === slices) {
4060
+ continue;
4061
+ }
4062
+ if (this.options.outerBlockMoves) {
4063
+ if (i + i > slices) {
4064
+ r.push([i, slices]);
4065
+ } else {
4066
+ r.push([0, i]);
4067
+ }
4068
+ } else {
4069
+ r.push([i, i]);
4070
+ }
4071
+ r.push(1);
4072
+ }
4073
+ }
4074
+ }
4075
+ if (this.fixedCubie >= 0) {
4076
+ const dep = this.keyface3(this.faces[this.cubies[this.fixedCubie][0]])[k];
4077
+ const newr = [];
4078
+ for (let i = 0; i < r.length; i += 2) {
4079
+ let o = r[i];
4080
+ if (dep >= o[0] && dep <= o[1]) {
4081
+ if (o[0] === 0) {
4082
+ o = [o[1] + 1, slices];
4083
+ } else if (slices === o[1]) {
4084
+ o = [0, o[0] - 1];
4085
+ } else {
4086
+ throw Error("fixed cubie option would disconnect move");
4087
+ }
4088
+ }
4089
+ let found = false;
4090
+ for (let j = 0; j < newr.length; j += 2) {
4091
+ if (newr[j][0] === o[0] && newr[j][1] === o[1] && newr[j + 1] === r[i + 1]) {
4092
+ found = true;
4093
+ break;
4094
+ }
4095
+ }
4096
+ if (!found) {
4097
+ newr.push(o);
4098
+ newr.push(r[i + 1]);
4099
+ }
4100
+ }
4101
+ r = newr;
4102
+ }
4103
+ return r;
4104
+ }
4105
+ graybyori(cubie) {
4106
+ let ori = this.cubies[cubie].length;
4107
+ if (this.duplicatedCubies[cubie]) {
4108
+ ori = 1;
4109
+ }
4110
+ return ori === 1 && (this.options.grayCenters || !this.options.includeCenterOrbits) || ori === 2 && (this.options.grayEdges || !this.options.includeEdgeOrbits) || ori > 2 && (this.options.grayCorners || !this.options.includeCornerOrbits);
4111
+ }
4112
+ skipbyori(cubie) {
4113
+ let ori = this.cubies[cubie].length;
4114
+ if (this.duplicatedCubies[cubie]) {
4115
+ ori = 1;
4116
+ }
4117
+ return ori === 1 && !this.options.includeCenterOrbits || ori === 2 && !this.options.includeEdgeOrbits || ori > 2 && !this.options.includeCornerOrbits;
4118
+ }
4119
+ skipcubie(fi) {
4120
+ return this.skipbyori(fi);
4121
+ }
4122
+ header(comment) {
4123
+ return `${comment + copyright}
4124
+ ${comment}
4125
+ `;
4126
+ }
4127
+ writegap() {
4128
+ const os = this.getOrbitsDef(false);
4129
+ const r = [];
4130
+ const mvs = [];
4131
+ for (let i = 0; i < os.moveops.length; i++) {
4132
+ let movename = os.movenames[i];
4133
+ if (!os.forcenames[i]) {
4134
+ movename = `M_${externalName(this.notationMapper, movename)}`;
4135
+ }
4136
+ movename = `M_${movename}`;
4137
+ let doinv = false;
4138
+ if (movename[movename.length - 1] === "'") {
4139
+ movename = movename.substring(0, movename.length - 1);
4140
+ doinv = true;
4141
+ }
4142
+ mvs.push(movename);
4143
+ if (doinv) {
4144
+ r.push(`${movename}:=${os.moveops[i].toPerm().inv().toGap()};`);
4145
+ } else {
4146
+ r.push(`${movename}:=${os.moveops[i].toPerm().toGap()};`);
4147
+ }
4148
+ }
4149
+ r.push("Gen:=[");
4150
+ r.push(mvs.join(","));
4151
+ r.push("];");
4152
+ const ip = os.solved.identicalPieces();
4153
+ r.push(
4154
+ `ip:=[${ip.map((_) => `[${_.map((__) => __ + 1).join(",")}]`).join(",")}];`
4155
+ );
4156
+ r.push("# Size(Group(Gen));");
4157
+ r.push("# Size(Stabilizer(Group(Gen), ip, OnTuplesSets));");
4158
+ r.push("");
4159
+ return this.header("# ") + r.join("\n");
4160
+ }
4161
+ writemathematica() {
4162
+ const os = this.getOrbitsDef(false);
4163
+ const r = [];
4164
+ const mvs = [];
4165
+ r.push(`(* ${this.header("").trim()} *)`);
4166
+ for (let i = 0; i < os.moveops.length; i++) {
4167
+ let movename = `m${externalName(this.notationMapper, os.movenames[i])}`;
4168
+ let doinv = false;
4169
+ if (movename[movename.length - 1] === "'") {
4170
+ movename = movename.substring(0, movename.length - 1);
4171
+ doinv = true;
4172
+ }
4173
+ mvs.push(movename);
4174
+ if (doinv) {
4175
+ r.push(`${movename}=${os.moveops[i].toPerm().inv().toMathematica()};`);
4176
+ } else {
4177
+ r.push(`${movename}=${os.moveops[i].toPerm().toMathematica()};`);
4178
+ }
4179
+ }
4180
+ r.push(`gen={${mvs.join(",")}};`);
4181
+ return r.join("\n");
4182
+ }
4183
+ writeksolve(name = "PuzzleGeometryPuzzle") {
4184
+ const od = this.getOrbitsDef(false);
4185
+ return this.header("# ") + od.toKsolve(name, this.notationMapper).join("\n");
4186
+ }
4187
+ getKPuzzleDefinition(fortwisty = true, includemoves = true) {
4188
+ const od = this.getOrbitsDef(fortwisty, includemoves);
4189
+ const internalDefinition = od.toKPuzzleDefinition(includemoves);
4190
+ internalDefinition.experimentalPuzzleDescription = this.puzzleDescription;
4191
+ if (!internalDefinition) {
4192
+ throw Error("Missing definition!");
4193
+ }
4194
+ return internalDefinition;
4195
+ }
4196
+ getMoveFromBits(moverange, amount, inverted, axiscmoves, setmoves, movesetorder) {
4197
+ const moveorbits = [];
4198
+ const perms = [];
4199
+ const oris = [];
4200
+ for (const len of this.cubieOrbitSizes) {
4201
+ perms.push(iota(len));
4202
+ oris.push(zeros(len));
4203
+ }
4204
+ for (let m = moverange[0]; m <= moverange[1]; m++) {
4205
+ const slicecmoves = axiscmoves[m];
4206
+ for (let j = 0; j < slicecmoves.length; j += 2 * movesetorder) {
4207
+ const mperm = slicecmoves.slice(j, j + 2 * movesetorder);
4208
+ const setnum = this.cubieSetNums[mperm[0]];
4209
+ for (let ii = 0; ii < mperm.length; ii += 2) {
4210
+ mperm[ii] = this.cubieOrdNums[mperm[ii]];
4211
+ }
4212
+ let inc = 2;
4213
+ let oinc = 3;
4214
+ if (inverted) {
4215
+ inc = mperm.length - 2;
4216
+ oinc = mperm.length - 1;
4217
+ }
4218
+ if (perms[setnum] === iota(this.cubieOrbitSizes[setnum])) {
4219
+ perms[setnum] = perms[setnum].slice();
4220
+ if (this.orbitOrientations[setnum] > 1 && // TODO: refactor to avoid non-null-assertion
4221
+ !this.options.fixedOrientation) {
4222
+ oris[setnum] = oris[setnum].slice();
4223
+ }
4224
+ }
4225
+ for (let ii = 0; ii < mperm.length; ii += 2) {
4226
+ perms[setnum][mperm[(ii + inc) % mperm.length]] = mperm[ii];
4227
+ if (this.orbitOrientations[setnum] > 1 && // TODO: refactor to avoid non-null-assertion
4228
+ !this.options.fixedOrientation) {
4229
+ oris[setnum][mperm[ii]] = (mperm[(ii + oinc) % mperm.length] - mperm[(ii + 1) % mperm.length] + 2 * this.orbitOrientations[setnum]) % // TODO: refactor to avoid non-null-assertion
4230
+ this.orbitOrientations[setnum];
4231
+ }
4232
+ }
4233
+ }
4234
+ }
4235
+ let lastId = new PGOrbit(iota(24), zeros(24), 1);
4236
+ for (let ii = 0; ii < this.cubieSetNames.length; ii++) {
4237
+ if (setmoves && !setmoves[ii]) {
4238
+ continue;
4239
+ }
4240
+ if (this.orbitOrientations[ii] === 1 || this.options.fixedOrientation) {
4241
+ if (perms[ii] === iota(lastId.perm.length)) {
4242
+ if (perms[ii] !== lastId.perm) {
4243
+ lastId = new PGOrbit(perms[ii], oris[ii], 1);
4244
+ }
4245
+ moveorbits.push(lastId);
4246
+ } else {
4247
+ moveorbits.push(new PGOrbit(perms[ii], oris[ii], 1));
4248
+ }
4249
+ } else {
4250
+ const no = new Array(oris[ii].length);
4251
+ for (let jj = 0; jj < perms[ii].length; jj++) {
4252
+ no[jj] = oris[ii][perms[ii][jj]];
4253
+ }
4254
+ moveorbits.push(
4255
+ new PGOrbit(perms[ii], no, this.orbitOrientations[ii])
4256
+ );
4257
+ }
4258
+ }
4259
+ let mv = new PGTransform(moveorbits);
4260
+ if (amount !== 1) {
4261
+ mv = mv.mulScalar(amount);
4262
+ }
4263
+ return mv;
4264
+ }
4265
+ omitSet(name) {
4266
+ for (const excludedSet of this.options.excludeOrbits) {
4267
+ if (excludedSet === name) {
4268
+ return true;
4269
+ }
4270
+ }
4271
+ return false;
4272
+ }
4273
+ diffmvsets(a, b, slices, neg) {
4274
+ for (let i = 0; i < a.length; i += 2) {
4275
+ let found = false;
4276
+ for (let j = 0; !found && j < b.length; j += 2) {
4277
+ if (neg) {
4278
+ if (a[i][0] + b[j][1] === slices && a[i][1] + b[j][0] === slices && a[i + 1] === b[j + 1]) {
4279
+ found = true;
4280
+ }
4281
+ } else {
4282
+ if (a[i][0] === b[j][0] && a[i][1] === b[j][1] && a[i + 1] === b[j + 1]) {
4283
+ found = true;
4284
+ }
4285
+ }
4286
+ }
4287
+ if (!found) {
4288
+ return true;
4289
+ }
4290
+ }
4291
+ return false;
4292
+ }
4293
+ // TODO: This is only public for testing; can we make it private again?
4294
+ getOrbitsDef(fortwisty, includemoves = true) {
4295
+ const setmoves = [];
4296
+ if (fortwisty) {
4297
+ for (let i = 0; i < this.cubieSetNames.length; i++) {
4298
+ setmoves.push(1);
4299
+ }
4300
+ }
4301
+ const setnames = [];
4302
+ const setdefs = [];
4303
+ const mps = [];
4304
+ const addrot = [];
4305
+ for (let k = 0; k < this.movePlaneSets.length; k++) {
4306
+ const moveset = this.getmovesets(k);
4307
+ mps.push(moveset);
4308
+ if (this.options.addRotations) {
4309
+ addrot.push(1);
4310
+ } else {
4311
+ addrot.push(0);
4312
+ }
4313
+ }
4314
+ const hasrotation = [];
4315
+ for (let k = 0; k < this.movePlaneSets.length; k++) {
4316
+ const slices = this.movePlaneSets[k].length;
4317
+ let sawone = false;
4318
+ const moveset = mps[k];
4319
+ for (let i = 0; i < moveset.length; i += 2) {
4320
+ if (moveset[i][0] === 0 && moveset[i][1] === slices) {
4321
+ sawone = true;
4322
+ }
4323
+ }
4324
+ hasrotation[k] = sawone;
4325
+ }
4326
+ if (this.options.addRotations && (this.options.moveList || this.options.fixedPieceType !== null)) {
4327
+ for (let i = 0; i < this.moveRotations.length; i++) {
4328
+ addrot[i] = 0;
4329
+ }
4330
+ for (let k = 0; k < this.movePlaneSets.length; k++) {
4331
+ if (hasrotation[k]) {
4332
+ addrot[k] = 3;
4333
+ continue;
4334
+ }
4335
+ for (let i = 0; i < this.moveRotations.length; i++) {
4336
+ let nn = this.movePlaneNormals[k];
4337
+ for (let ii = 1; ii * 2 <= this.movesetorders[i]; ii++) {
4338
+ nn = nn.rotatepoint(this.moveRotations[i][0]);
4339
+ if (addrot[i] & ii) {
4340
+ continue;
4341
+ }
4342
+ let found = -1;
4343
+ let neg = false;
4344
+ for (let j = 0; j < this.movePlaneNormals.length; j++) {
4345
+ if (nn.dist(this.movePlaneNormals[j]) < eps3) {
4346
+ found = j;
4347
+ break;
4348
+ } else if (nn.dist(this.movePlaneNormals[j].smul(-1)) < eps3) {
4349
+ found = j;
4350
+ neg = true;
4351
+ break;
4352
+ }
4353
+ }
4354
+ if (found < 0) {
4355
+ throw Error("Could not find rotation");
4356
+ }
4357
+ const cmp = mps[found];
4358
+ if (cmp.length !== mps[k].length || this.movePlaneSets[k].length !== // TODO
4359
+ this.movePlaneSets[found].length || // TODO
4360
+ this.diffmvsets(
4361
+ cmp,
4362
+ mps[k],
4363
+ this.movePlaneSets[found].length,
4364
+ // TODO
4365
+ neg
4366
+ )) {
4367
+ addrot[i] |= ii;
4368
+ }
4369
+ }
4370
+ }
4371
+ }
4372
+ for (let i = 0; i < this.moveRotations.length; i++) {
4373
+ if (addrot[i] === 0) {
4374
+ addrot[i] = 1;
4375
+ } else if (addrot[i] === 1) {
4376
+ if (this.movesetorders[i] > 3) {
4377
+ addrot[i] = 2;
4378
+ } else {
4379
+ addrot[i] = 0;
4380
+ }
4381
+ } else if (addrot[i] === 3) {
4382
+ addrot[i] = 0;
4383
+ } else {
4384
+ throw Error("Impossible addrot val");
4385
+ }
4386
+ }
4387
+ }
4388
+ for (let k = 0; k < this.movePlaneSets.length; k++) {
4389
+ if (addrot[k] !== 0 && !hasrotation[k]) {
4390
+ mps[k].push([0, this.movePlaneSets[k].length]);
4391
+ mps[k].push(addrot[k]);
4392
+ }
4393
+ }
4394
+ for (let k = 0; k < this.movePlaneSets.length; k++) {
4395
+ const moveset = mps[k];
4396
+ const movesetorder = this.movesetorders[k];
4397
+ for (let i = 0; i < moveset.length; i += 2) {
4398
+ for (let j = 0; j < i; j += 2) {
4399
+ if (moveset[i][0] === moveset[j][0] && moveset[i][1] === moveset[j][1]) {
4400
+ throw Error("Redundant moves in moveset.");
4401
+ }
4402
+ }
4403
+ }
4404
+ const allbits = [];
4405
+ for (let i = 0; i < moveset.length; i += 2) {
4406
+ for (let j = moveset[i][0]; j <= moveset[i][1]; j++) {
4407
+ allbits[j] = 1;
4408
+ }
4409
+ }
4410
+ const axiscmoves = this.cmovesBySlice[k];
4411
+ for (let i = 0; i < axiscmoves.length; i++) {
4412
+ if (allbits[i] !== 1) {
4413
+ continue;
4414
+ }
4415
+ const slicecmoves = axiscmoves[i];
4416
+ for (let j = 0; j < slicecmoves.length; j += 2 * movesetorder) {
4417
+ if (this.skipcubie(slicecmoves[j])) {
4418
+ continue;
4419
+ }
4420
+ const ind = this.cubieSetNums[slicecmoves[j]];
4421
+ setmoves[ind] = 1;
4422
+ }
4423
+ }
4424
+ }
4425
+ for (let i = 0; i < this.cubieSetNames.length; i++) {
4426
+ if (!setmoves[i]) {
4427
+ continue;
4428
+ }
4429
+ if (this.omitSet(this.cubieSetNames[i])) {
4430
+ setmoves[i] = 0;
4431
+ continue;
4432
+ }
4433
+ setnames.push(this.cubieSetNames[i]);
4434
+ setdefs.push(
4435
+ new PGOrbitDef(
4436
+ this.cubieOrbitSizes[i],
4437
+ // TODO: refactor to avoid non-null-assertion
4438
+ this.options.fixedOrientation ? 1 : this.orbitOrientations[i]
4439
+ // TODO: refactor to avoid non-null-assertion
4440
+ )
4441
+ );
4442
+ }
4443
+ const solved = [];
4444
+ for (let i = 0; i < this.cubieSetNames.length; i++) {
4445
+ if (!setmoves[i]) {
4446
+ continue;
4447
+ }
4448
+ if (this.omitSet(this.cubieSetNames[i])) {
4449
+ continue;
4450
+ }
4451
+ const p = Array(this.cubieOrbitSizes[i]).fill(-1);
4452
+ const o = [];
4453
+ for (let j = 0; j < this.cubieOrbitSizes[i]; j++) {
4454
+ if (fortwisty) {
4455
+ p[j] = j;
4456
+ } else {
4457
+ const cubie = this.cubieSetCubies[i][j];
4458
+ p[this.cubieOrdNums[cubie]] = this.cubieValueMap[cubie];
4459
+ }
4460
+ o.push(0);
4461
+ }
4462
+ solved.push(
4463
+ new PGOrbit(
4464
+ p,
4465
+ o,
4466
+ this.options.fixedOrientation ? 1 : this.orbitOrientations[i]
4467
+ // TODO: refactor to avoid non-null-assertion
4468
+ )
4469
+ );
4470
+ }
4471
+ const movenames = [];
4472
+ const forcenames = [];
4473
+ const moves = [];
4474
+ const isrots = [];
4475
+ if (includemoves) {
4476
+ for (let k = 0; k < this.movePlaneSets.length; k++) {
4477
+ const moveplaneset = this.movePlaneSets[k];
4478
+ const slices = moveplaneset.length;
4479
+ const moveset = mps[k];
4480
+ const movesetgeo = this.movesetgeos[k];
4481
+ for (let i = 0; i < moveset.length; i += 2) {
4482
+ const movebits = moveset[i];
4483
+ let nameoverride;
4484
+ let inverted = false;
4485
+ if (this.parsedMoveList !== void 0) {
4486
+ for (const parsedmove of this.parsedMoveList) {
4487
+ if (parsedmove[1] !== k) {
4488
+ continue;
4489
+ }
4490
+ let r2 = [];
4491
+ if (parsedmove[4]) {
4492
+ r2 = [parsedmove[2], parsedmove[3]];
4493
+ } else {
4494
+ r2 = [slices - parsedmove[3], slices - parsedmove[2]];
4495
+ }
4496
+ if (r2[0] === movebits[0] && r2[1] === movebits[1]) {
4497
+ nameoverride = parsedmove[0];
4498
+ inverted = !parsedmove[4];
4499
+ }
4500
+ }
4501
+ }
4502
+ if (nameoverride) {
4503
+ movenames.push(nameoverride);
4504
+ forcenames.push(true);
4505
+ } else {
4506
+ const mna = getmovename(movesetgeo, movebits, slices);
4507
+ inverted = mna[1];
4508
+ const movename = mna[0];
4509
+ if (moveset[i + 1] === 1) {
4510
+ movenames.push(movename);
4511
+ } else {
4512
+ movenames.push(movename + moveset[i + 1]);
4513
+ }
4514
+ forcenames.push(false);
4515
+ }
4516
+ isrots.push(movebits[0] === 0 && movebits[1] === slices);
4517
+ const mv = this.getMoveFromBits(
4518
+ movebits,
4519
+ moveset[i + 1],
4520
+ inverted,
4521
+ this.cmovesBySlice[k],
4522
+ setmoves,
4523
+ this.movesetorders[k]
4524
+ // TODO: refactor to avoid non-null-assertion
4525
+ );
4526
+ moves.push(mv);
4527
+ }
4528
+ }
4529
+ }
4530
+ let r = new PGOrbitsDef(
4531
+ setnames,
4532
+ setdefs,
4533
+ new VisibleState(solved),
4534
+ movenames,
4535
+ moves,
4536
+ isrots,
4537
+ forcenames
4538
+ );
4539
+ if (this.options.optimizeOrbits) {
4540
+ r = r.optimize();
4541
+ }
4542
+ if (this.options.scrambleAmount !== 0) {
4543
+ r.scramble(this.options.scrambleAmount);
4544
+ }
4545
+ return r;
4546
+ }
4547
+ getScramble(n = 0) {
4548
+ const od = this.getOrbitsDef(false);
4549
+ return od.toKTransformationData(od.getScrambleTransformation(n));
4550
+ }
4551
+ getMovesAsPerms() {
4552
+ return this.getOrbitsDef(false).moveops.map((_) => _.toPerm());
4553
+ }
4554
+ showcanon(disp) {
4555
+ showcanon(this.getOrbitsDef(false), disp);
4556
+ }
4557
+ getsolved() {
4558
+ const r = [];
4559
+ for (let i = 0; i < this.baseFaceCount; i++) {
4560
+ for (let j = 0; j < this.stickersPerFace; j++) {
4561
+ r.push(i);
4562
+ }
4563
+ }
4564
+ return new Perm(r);
4565
+ }
4566
+ // Given a rotation description that says to align feature1
4567
+ // with a given vector, and then as much as possible feature2
4568
+ // with another given vector, return a Quaternion that
4569
+ // performs this rotation.
4570
+ getOrientationRotation(desiredRotation) {
4571
+ const [feature1name, [x1, y1, z1]] = desiredRotation[0];
4572
+ const direction1 = new Quat(0, x1, -y1, z1);
4573
+ const [feature2name, [x2, y2, z2]] = desiredRotation[1];
4574
+ const direction2 = new Quat(0, x2, -y2, z2);
4575
+ let feature1 = null;
4576
+ let feature2 = null;
4577
+ const feature1geoname = this.swizzler.unswizzle(feature1name);
4578
+ const feature2geoname = this.swizzler.unswizzle(feature2name);
4579
+ for (const gn of this.geometryNormals) {
4580
+ if (feature1geoname === gn[1]) {
4581
+ feature1 = gn[0];
4582
+ }
4583
+ if (feature2geoname === gn[1]) {
4584
+ feature2 = gn[0];
4585
+ }
4586
+ }
4587
+ if (!feature1) {
4588
+ throw Error(`Could not find feature ${feature1name}`);
4589
+ }
4590
+ if (!feature2) {
4591
+ throw Error(`Could not find feature ${feature2name}`);
4592
+ }
4593
+ const r1 = feature1.pointrotation(direction1);
4594
+ const feature2rot = feature2.rotatepoint(r1);
4595
+ const r2 = feature2rot.unproject(direction1).pointrotation(direction2.unproject(direction1));
4596
+ return r2.mul(r1);
4597
+ }
4598
+ getInitial3DRotation() {
4599
+ const basefacecount = this.baseFaceCount;
4600
+ let orientationDescription = null;
4601
+ if (this.options.puzzleOrientation) {
4602
+ orientationDescription = this.options.puzzleOrientation;
4603
+ } else if (this.options.puzzleOrientations) {
4604
+ orientationDescription = this.options.puzzleOrientations[basefacecount];
4605
+ }
4606
+ if (!orientationDescription) {
4607
+ orientationDescription = defaultOrientations()[basefacecount];
4608
+ }
4609
+ if (!orientationDescription) {
4610
+ throw Error("No default orientation?");
4611
+ }
4612
+ return this.getOrientationRotation(orientationDescription);
4613
+ }
4614
+ generate2dmapping(w = 800, h = 500, trim = 10, threed = false, twodshrink = 0.92) {
4615
+ w -= 2 * trim;
4616
+ h -= 2 * trim;
4617
+ function extendedges(a, n) {
4618
+ let dx = a[1][0] - a[0][0];
4619
+ let dy = a[1][1] - a[0][1];
4620
+ const ang = 2 * Math.PI / n;
4621
+ const cosa = Math.cos(ang);
4622
+ const sina = Math.sin(ang);
4623
+ for (let i = 2; i < n; i++) {
4624
+ const ndx = dx * cosa + dy * sina;
4625
+ dy = dy * cosa - dx * sina;
4626
+ dx = ndx;
4627
+ a.push([a[i - 1][0] + dx, a[i - 1][1] + dy]);
4628
+ }
4629
+ }
4630
+ this.genperms();
4631
+ const boundarygeo = this.getboundarygeometry();
4632
+ const face0 = boundarygeo.facenames[0][0];
4633
+ const polyn = face0.length;
4634
+ const net = this.net;
4635
+ if (net === null) {
4636
+ throw Error("No net?");
4637
+ }
4638
+ const edges = {};
4639
+ let minx = 0;
4640
+ let miny = 0;
4641
+ let maxx = 1;
4642
+ let maxy = 0;
4643
+ edges[net[0][0]] = [
4644
+ [1, 0],
4645
+ [0, 0]
4646
+ ];
4647
+ extendedges(edges[net[0][0]], polyn);
4648
+ for (const neti of net) {
4649
+ const f0 = neti[0];
4650
+ if (!edges[f0]) {
4651
+ throw Error("Bad edge description; first edge not connected.");
4652
+ }
4653
+ for (let j = 1; j < neti.length; j++) {
4654
+ const f1 = neti[j];
4655
+ if (f1 === "" || edges[f1]) {
4656
+ continue;
4657
+ }
4658
+ edges[f1] = [edges[f0][j % polyn], edges[f0][(j + polyn - 1) % polyn]];
4659
+ extendedges(edges[f1], polyn);
4660
+ }
4661
+ }
4662
+ for (const f in edges) {
4663
+ const es = edges[f];
4664
+ for (const esi of es) {
4665
+ minx = Math.min(minx, esi[0]);
4666
+ maxx = Math.max(maxx, esi[0]);
4667
+ miny = Math.min(miny, esi[1]);
4668
+ maxy = Math.max(maxy, esi[1]);
4669
+ }
4670
+ }
4671
+ const sc = Math.min(w / (maxx - minx), h / (maxy - miny));
4672
+ const xoff = 0.5 * (w - sc * (maxx + minx));
4673
+ const yoff = 0.5 * (h - sc * (maxy + miny));
4674
+ const geos = {};
4675
+ const bg = this.getboundarygeometry();
4676
+ const edges2 = {};
4677
+ const initv = [
4678
+ [sc + xoff, yoff],
4679
+ [xoff, yoff]
4680
+ ];
4681
+ edges2[net[0][0]] = initv;
4682
+ extendedges(edges2[net[0][0]], polyn);
4683
+ geos[this.faceNames[0][1]] = this.project2d(0, 0, [
4684
+ new Quat(0, initv[0][0], initv[0][1], 0),
4685
+ new Quat(0, initv[1][0], initv[1][1], 0)
4686
+ ]);
4687
+ const connectat = [];
4688
+ connectat[0] = 0;
4689
+ for (const neti of net) {
4690
+ const f0 = neti[0];
4691
+ if (!edges2[f0]) {
4692
+ throw Error("Bad edge description; first edge not connected.");
4693
+ }
4694
+ let gfi = -1;
4695
+ for (let j = 0; j < bg.facenames.length; j++) {
4696
+ if (f0 === bg.facenames[j][1]) {
4697
+ gfi = j;
4698
+ break;
4699
+ }
4700
+ }
4701
+ if (gfi < 0) {
4702
+ throw Error(`Could not find first face name ${f0}`);
4703
+ }
4704
+ const thisface = bg.facenames[gfi][0];
4705
+ for (let j = 1; j < neti.length; j++) {
4706
+ const f1 = neti[j];
4707
+ if (f1 === "" || edges2[f1]) {
4708
+ continue;
4709
+ }
4710
+ edges2[f1] = [
4711
+ edges2[f0][j % polyn],
4712
+ edges2[f0][(j + polyn - 1) % polyn]
4713
+ ];
4714
+ extendedges(edges2[f1], polyn);
4715
+ const caf0 = connectat[gfi];
4716
+ const mp = thisface[(caf0 + j) % polyn].sum(thisface[(caf0 + j + polyn - 1) % polyn]).smul(0.5);
4717
+ const epi = findelement(bg.edgenames, mp);
4718
+ const edgename = bg.edgenames[epi][1];
4719
+ const el = splitByFaceNames(edgename, this.faceNames);
4720
+ const gf1 = el[f0 === el[0] ? 1 : 0];
4721
+ let gf1i = -1;
4722
+ for (let k = 0; k < bg.facenames.length; k++) {
4723
+ if (gf1 === bg.facenames[k][1]) {
4724
+ gf1i = k;
4725
+ break;
4726
+ }
4727
+ }
4728
+ if (gf1i < 0) {
4729
+ throw Error("Could not find second face name");
4730
+ }
4731
+ const otherface = bg.facenames[gf1i][0];
4732
+ for (let k = 0; k < otherface.length; k++) {
4733
+ const mp2 = otherface[k].sum(otherface[(k + 1) % polyn]).smul(0.5);
4734
+ if (mp2.dist(mp) <= eps3) {
4735
+ const p1 = edges2[f0][(j + polyn - 1) % polyn];
4736
+ const p2 = edges2[f0][j % polyn];
4737
+ connectat[gf1i] = k;
4738
+ geos[gf1] = this.project2d(gf1i, k, [
4739
+ new Quat(0, p2[0], p2[1], 0),
4740
+ new Quat(0, p1[0], p1[1], 0)
4741
+ ]);
4742
+ break;
4743
+ }
4744
+ }
4745
+ }
4746
+ }
4747
+ let hix = 0;
4748
+ let hiy = 0;
4749
+ const rot = this.getInitial3DRotation();
4750
+ for (let face of this.faces) {
4751
+ if (threed) {
4752
+ face = face.rotate(rot);
4753
+ }
4754
+ for (let j = 0; j < face.length; j++) {
4755
+ hix = Math.max(hix, Math.abs(face.get(j).b));
4756
+ hiy = Math.max(hiy, Math.abs(face.get(j).c));
4757
+ }
4758
+ }
4759
+ const sc2 = Math.min(h / hiy / 2, (w - trim) / hix / 4);
4760
+ const mappt2d = (fn, q) => {
4761
+ if (threed) {
4762
+ q = q.rotatepoint(rot);
4763
+ const xoff2 = 0.5 * trim + 0.25 * w;
4764
+ const xmul = this.basePlanes[fn].rotateplane(rot).d < 0 ? 1 : -1;
4765
+ return [
4766
+ trim + w * 0.5 + xmul * (xoff2 - q.b * sc2),
4767
+ trim + h * 0.5 + q.c * sc2
4768
+ ];
4769
+ } else {
4770
+ const g = geos[this.faceNames[fn][1]];
4771
+ return [
4772
+ trim + twodshrink * q.dot(g[0]) + g[2].b,
4773
+ trim + h - twodshrink * q.dot(g[1]) - g[2].c
4774
+ ];
4775
+ }
4776
+ };
4777
+ return mappt2d;
4778
+ }
4779
+ generatesvg(w = 800, h = 500, trim = 10, threed = false) {
4780
+ const mappt2d = this.generate2dmapping(w, h, trim, threed);
4781
+ function drawedges(id, pts, color) {
4782
+ return `<polygon id="${id}" class="sticker" style="fill: ${color}" points="${pts.map((p) => `${p[0]} ${p[1]}`).join(" ")}"/>
4783
+ `;
4784
+ }
4785
+ const pos = this.getsolved();
4786
+ const colormap = [];
4787
+ const facegeo = [];
4788
+ for (let i = 0; i < this.baseFaceCount; i++) {
4789
+ colormap[i] = this.colors[this.faceNames[i][1]];
4790
+ }
4791
+ for (let i = 0; i < this.faces.length; i++) {
4792
+ const face = this.faces[i];
4793
+ const facenum = Math.floor(i / this.stickersPerFace);
4794
+ const fg = [];
4795
+ for (let j = 0; j < face.length; j++) {
4796
+ fg.push(mappt2d(facenum, face.get(j)));
4797
+ }
4798
+ facegeo.push(fg);
4799
+ }
4800
+ const svg = [];
4801
+ for (let j = 0; j < this.baseFaceCount; j++) {
4802
+ svg.push("<g>");
4803
+ svg.push(`<title>${this.faceNames[j][1]}</title>
4804
+ `);
4805
+ for (let ii = 0; ii < this.stickersPerFace; ii++) {
4806
+ const i = j * this.stickersPerFace + ii;
4807
+ const cubie = this.faceToCubie[i];
4808
+ const cubieori = this.faceToCubieOrd[i];
4809
+ const cubiesetnum = this.cubieSetNums[cubie];
4810
+ const cubieord = this.cubieOrdNums[cubie];
4811
+ const color = this.graybyori(cubie) ? "#808080" : colormap[pos.p[i]];
4812
+ let id = `${this.cubieSetNames[cubiesetnum]}-l${cubieord}-o${cubieori}`;
4813
+ svg.push(drawedges(id, facegeo[i], color));
4814
+ if (this.duplicatedFaces[i]) {
4815
+ for (let jj = 1; jj < this.duplicatedFaces[i]; jj++) {
4816
+ id = `${this.cubieSetNames[cubiesetnum]}-l${cubieord}-o${jj}`;
4817
+ svg.push(drawedges(id, facegeo[i], color));
4818
+ }
4819
+ }
4820
+ }
4821
+ svg.push("</g>");
4822
+ }
4823
+ const html = `<svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 800 500">
4824
+ <style type="text/css"><![CDATA[.sticker { stroke: #000000; stroke-width: 1px; }]]></style>
4825
+ ${svg.join(
4826
+ ""
4827
+ )}</svg>`;
4828
+ return html;
4829
+ }
4830
+ // The colorfrac parameter says how much of the face should be
4831
+ // colored (vs dividing lines); we default to 0.77 which seems
4832
+ // to work pretty well. It should be a number between probably
4833
+ // 0.4 and 0.9.
4834
+ get3d(options) {
4835
+ const stickers = [];
4836
+ const rot = this.getInitial3DRotation();
4837
+ const faces = [];
4838
+ const maxdist = 0.52 * this.baseFaces[0].get(0).len();
4839
+ for (let i = 0; i < this.baseFaces.length; i++) {
4840
+ const coords = this.baseFaces[i].rotate(rot);
4841
+ const name = this.faceNames[i][1];
4842
+ faces.push({ coords: toFaceCoords(coords, maxdist), name });
4843
+ }
4844
+ for (let i = 0; i < this.faces.length; i++) {
4845
+ const facenum = Math.floor(i / this.stickersPerFace);
4846
+ const cubie = this.faceToCubie[i];
4847
+ const cubieori = this.faceToCubieOrd[i];
4848
+ const cubiesetnum = this.cubieSetNums[cubie];
4849
+ const cubieord = this.cubieOrdNums[cubie];
4850
+ let color = this.graybyori(cubie) ? options?.darkIgnoredOrbits ? "#222222" : "#808080" : this.colors[this.faceNames[facenum][1]];
4851
+ if (options?.stickerColors) {
4852
+ color = options.stickerColors[i];
4853
+ }
4854
+ const coords = this.faces[i].rotate(rot);
4855
+ stickers.push({
4856
+ coords: toFaceCoords(coords, maxdist),
4857
+ color,
4858
+ orbit: this.cubieSetNames[cubiesetnum],
4859
+ // TODO: refactor to avoid non-null-assertion
4860
+ ord: cubieord,
4861
+ ori: cubieori,
4862
+ face: facenum
4863
+ });
4864
+ let fcoords = coords;
4865
+ if (this.duplicatedFaces[i]) {
4866
+ const rotdist = fcoords.length / this.duplicatedFaces[i];
4867
+ for (let jj = 1; jj < this.duplicatedFaces[i]; jj++) {
4868
+ for (let k = 0; k < rotdist; k++) {
4869
+ fcoords = fcoords.rotateforward();
4870
+ }
4871
+ stickers.push({
4872
+ coords: toFaceCoords(fcoords, maxdist),
4873
+ color,
4874
+ orbit: this.cubieSetNames[cubiesetnum],
4875
+ // TODO: refactor to avoid non-null-assertion
4876
+ ord: cubieord,
4877
+ ori: jj,
4878
+ face: facenum,
4879
+ isDup: true
4880
+ });
4881
+ }
4882
+ }
4883
+ }
4884
+ const grips = [];
4885
+ for (let i = 0; i < this.movesetgeos.length; i++) {
4886
+ const msg = this.movesetgeos[i];
4887
+ const order = this.movesetorders[i];
4888
+ for (const gn of this.geometryNormals) {
4889
+ if (msg[0] === gn[1] && msg[1] === gn[2]) {
4890
+ grips.push({
4891
+ coordinates: toCoords(gn[0].rotatepoint(rot), 1),
4892
+ quantumMove: new Move(msg[0]),
4893
+ order
4894
+ });
4895
+ grips.push({
4896
+ coordinates: toCoords(gn[0].rotatepoint(rot).smul(-1), 1),
4897
+ quantumMove: new Move(msg[2]),
4898
+ order
4899
+ });
4900
+ }
4901
+ }
4902
+ }
4903
+ const twodmapper = this.generate2dmapping(2880, 2160, 0, false, 1);
4904
+ const g = (() => {
4905
+ const irot = rot.invrot();
4906
+ return (facenum, coords) => {
4907
+ let q = new Quat(
4908
+ 0,
4909
+ coords[0] * maxdist,
4910
+ -coords[1] * maxdist,
4911
+ coords[2] * maxdist
4912
+ );
4913
+ q = q.rotatepoint(irot);
4914
+ const x = twodmapper(facenum, q);
4915
+ x[0] /= 2880;
4916
+ x[1] = 1 - x[1] / 2160;
4917
+ return x;
4918
+ };
4919
+ })().bind(this);
4920
+ return {
4921
+ stickers,
4922
+ faces,
4923
+ axis: grips,
4924
+ unswizzle: this.unswizzle.bind(this),
4925
+ notationMapper: this.notationMapper,
4926
+ textureMapper: { getuv: g }
4927
+ };
4928
+ }
4929
+ // From the name of a geometric element (face, vertex, edge), get a
4930
+ // normal vector respecting the default orientation. This is useful
4931
+ // to define the initial position of the camera in a 3D scene. The
4932
+ // return value is normalized, so multiply it by the camera distance.
4933
+ // Returns undefined if no such geometric element.
4934
+ getGeoNormal(geoname) {
4935
+ const rot = this.getInitial3DRotation();
4936
+ const grip = this.swizzler.unswizzle(geoname);
4937
+ for (const gn of this.geometryNormals) {
4938
+ if (grip === gn[1]) {
4939
+ const r = toCoords(gn[0].rotatepoint(rot), 1);
4940
+ if (Math.abs(r[0]) < eps3 && Math.abs(r[2]) < eps3) {
4941
+ r[0] = 0;
4942
+ r[2] = 1e-6;
4943
+ }
4944
+ return r;
4945
+ }
4946
+ }
4947
+ return void 0;
4948
+ }
4949
+ getfaceindex(facenum) {
4950
+ const divid = this.stickersPerFace;
4951
+ return Math.floor(facenum / divid);
4952
+ }
4953
+ textForTwizzleExplorer() {
4954
+ return `Faces ${this.basePlaneRotations.length}
4955
+ Stickers per face ${this.stickersPerFace}
4956
+ Short edge ${this.shortestEdge}
4957
+ Cubies ${this.cubies.length}
4958
+ Edge distance ${this.edgeDistance}
4959
+ Vertex distance ${this.vertexDistance}`;
4960
+ }
4961
+ writeSchreierSims(tw) {
4962
+ const os = this.getOrbitsDef(false);
4963
+ const as = os.reassemblySize();
4964
+ tw(`Reassembly size is ${as}`);
4965
+ const ss = schreierSims(this.getMovesAsPerms(), tw);
4966
+ const r = as / ss;
4967
+ tw(`Ratio is ${r}`);
4968
+ }
4969
+ };
4970
+ var PGNotation = class {
4971
+ constructor(pg, od) {
4972
+ this.pg = pg;
4973
+ this.orbitNames = od.orbitnames;
4974
+ }
4975
+ pg;
4976
+ orbitNames;
4977
+ lookupMove(move) {
4978
+ const mv = this.pg.parseMove(move);
4979
+ if (this.pg.parsedMoveList) {
4980
+ let found = false;
4981
+ for (const parsedmove of this.pg.parsedMoveList) {
4982
+ if (parsedmove[1] === mv[1] && parsedmove[2] === mv[2] && parsedmove[3] === mv[3] && parsedmove[4] === mv[4]) {
4983
+ found = true;
4984
+ }
4985
+ }
4986
+ if (!found) {
4987
+ return null;
4988
+ }
4989
+ }
4990
+ let bits = [mv[2], mv[3]];
4991
+ if (!mv[4]) {
4992
+ const slices = this.pg.movePlaneSets[mv[1]].length;
4993
+ bits = [slices - mv[3], slices - mv[2]];
4994
+ }
4995
+ const pgmv = this.pg.getMoveFromBits(
4996
+ bits,
4997
+ mv[5],
4998
+ !mv[4],
4999
+ this.pg.cmovesBySlice[mv[1]],
5000
+ void 0,
5001
+ this.pg.movesetorders[mv[1]]
5002
+ // TODO: refactor to avoid non-null-assertion
5003
+ );
5004
+ const r = PGOrbitsDef.transformToKTransformationData(this.orbitNames, pgmv);
5005
+ return r;
5006
+ }
5007
+ remapKPuzzleDefinition(kpuzzleDefinition) {
5008
+ return remapKPuzzleDefinition(kpuzzleDefinition, this.pg.notationMapper);
5009
+ }
5010
+ };
5011
+ export {
5012
+ PUZZLE_BASE_SHAPES as EXPERIMENTAL_PUZZLE_BASE_SHAPES,
5013
+ PUZZLE_CUT_TYPES as EXPERIMENTAL_PUZZLE_CUT_TYPES,
5014
+ PGNotation as ExperimentalPGNotation,
5015
+ PuzzleGeometry,
5016
+ Quat,
5017
+ getPG3DNamedPuzzles,
5018
+ getPuzzleDescriptionString,
5019
+ getPuzzleGeometryByDesc,
5020
+ getPuzzleGeometryByName,
5021
+ parsePuzzleDescription,
5022
+ schreierSims
5023
+ };
5024
+ //# sourceMappingURL=index.js.map