dfa-minimizer.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. /**
  2. * The MIT License (MIT)
  3. * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
  4. */
  5. 'use strict';
  6. // DFA minization.
  7. /**
  8. * Map from state to current set it goes.
  9. */
  10. var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
  11. function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }
  12. function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
  13. var currentTransitionMap = null;
  14. /**
  15. * Takes a DFA, and returns a minimized version of it
  16. * compressing some states to groups (using standard, 0-, 1-,
  17. * 2-, ... N-equivalence algorithm).
  18. */
  19. function minimize(dfa) {
  20. var table = dfa.getTransitionTable();
  21. var allStates = Object.keys(table);
  22. var alphabet = dfa.getAlphabet();
  23. var accepting = dfa.getAcceptingStateNumbers();
  24. currentTransitionMap = {};
  25. var nonAccepting = new Set();
  26. allStates.forEach(function (state) {
  27. state = Number(state);
  28. var isAccepting = accepting.has(state);
  29. if (isAccepting) {
  30. currentTransitionMap[state] = accepting;
  31. } else {
  32. nonAccepting.add(state);
  33. currentTransitionMap[state] = nonAccepting;
  34. }
  35. });
  36. // ---------------------------------------------------------------------------
  37. // Step 1: build equivalent sets.
  38. // All [1..N] equivalent sets.
  39. var all = [
  40. // 0-equivalent sets.
  41. [nonAccepting, accepting].filter(function (set) {
  42. return set.size > 0;
  43. })];
  44. var current = void 0;
  45. var previous = void 0;
  46. // Top of the stack is the current list of sets to analyze.
  47. current = all[all.length - 1];
  48. // Previous set (to check whether we need to stop).
  49. previous = all[all.length - 2];
  50. // Until we'll not have the same N and N-1 equivalent rows.
  51. var _loop = function _loop() {
  52. var newTransitionMap = {};
  53. var _iteratorNormalCompletion3 = true;
  54. var _didIteratorError3 = false;
  55. var _iteratorError3 = undefined;
  56. try {
  57. for (var _iterator3 = current[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
  58. var _set = _step3.value;
  59. // Handled states for this set.
  60. var handledStates = {};
  61. var _set2 = _toArray(_set),
  62. first = _set2[0],
  63. rest = _set2.slice(1);
  64. handledStates[first] = new Set([first]);
  65. // Have to compare each from the rest states with
  66. // the already handled states, and see if they are equivalent.
  67. var _iteratorNormalCompletion4 = true;
  68. var _didIteratorError4 = false;
  69. var _iteratorError4 = undefined;
  70. try {
  71. restSets: for (var _iterator4 = rest[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
  72. var state = _step4.value;
  73. var _iteratorNormalCompletion5 = true;
  74. var _didIteratorError5 = false;
  75. var _iteratorError5 = undefined;
  76. try {
  77. for (var _iterator5 = Object.keys(handledStates)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
  78. var handledState = _step5.value;
  79. // This and some previously handled state are equivalent --
  80. // just append this state to the same set.
  81. if (areEquivalent(state, handledState, table, alphabet)) {
  82. handledStates[handledState].add(state);
  83. handledStates[state] = handledStates[handledState];
  84. continue restSets;
  85. }
  86. }
  87. // Else, this state is not equivalent to any of the
  88. // handled states -- allocate a new set for it.
  89. } catch (err) {
  90. _didIteratorError5 = true;
  91. _iteratorError5 = err;
  92. } finally {
  93. try {
  94. if (!_iteratorNormalCompletion5 && _iterator5.return) {
  95. _iterator5.return();
  96. }
  97. } finally {
  98. if (_didIteratorError5) {
  99. throw _iteratorError5;
  100. }
  101. }
  102. }
  103. handledStates[state] = new Set([state]);
  104. }
  105. } catch (err) {
  106. _didIteratorError4 = true;
  107. _iteratorError4 = err;
  108. } finally {
  109. try {
  110. if (!_iteratorNormalCompletion4 && _iterator4.return) {
  111. _iterator4.return();
  112. }
  113. } finally {
  114. if (_didIteratorError4) {
  115. throw _iteratorError4;
  116. }
  117. }
  118. }
  119. // Add these handled states to all states map.
  120. Object.assign(newTransitionMap, handledStates);
  121. }
  122. // Update current transition map for the handled row.
  123. } catch (err) {
  124. _didIteratorError3 = true;
  125. _iteratorError3 = err;
  126. } finally {
  127. try {
  128. if (!_iteratorNormalCompletion3 && _iterator3.return) {
  129. _iterator3.return();
  130. }
  131. } finally {
  132. if (_didIteratorError3) {
  133. throw _iteratorError3;
  134. }
  135. }
  136. }
  137. currentTransitionMap = newTransitionMap;
  138. var newSets = new Set(Object.keys(newTransitionMap).map(function (state) {
  139. return newTransitionMap[state];
  140. }));
  141. all.push([].concat(_toConsumableArray(newSets)));
  142. // Top of the stack is the current.
  143. current = all[all.length - 1];
  144. // Previous set.
  145. previous = all[all.length - 2];
  146. };
  147. while (!sameRow(current, previous)) {
  148. _loop();
  149. }
  150. // ---------------------------------------------------------------------------
  151. // Step 2: build minimized table from the equivalent sets.
  152. // Remap state numbers from sets to index-based.
  153. var remaped = new Map();
  154. var idx = 1;
  155. current.forEach(function (set) {
  156. return remaped.set(set, idx++);
  157. });
  158. // Build the minimized table from the calculated equivalent sets.
  159. var minimizedTable = {};
  160. var minimizedAcceptingStates = new Set();
  161. var updateAcceptingStates = function updateAcceptingStates(set, idx) {
  162. var _iteratorNormalCompletion = true;
  163. var _didIteratorError = false;
  164. var _iteratorError = undefined;
  165. try {
  166. for (var _iterator = set[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
  167. var state = _step.value;
  168. if (accepting.has(state)) {
  169. minimizedAcceptingStates.add(idx);
  170. }
  171. }
  172. } catch (err) {
  173. _didIteratorError = true;
  174. _iteratorError = err;
  175. } finally {
  176. try {
  177. if (!_iteratorNormalCompletion && _iterator.return) {
  178. _iterator.return();
  179. }
  180. } finally {
  181. if (_didIteratorError) {
  182. throw _iteratorError;
  183. }
  184. }
  185. }
  186. };
  187. var _iteratorNormalCompletion2 = true;
  188. var _didIteratorError2 = false;
  189. var _iteratorError2 = undefined;
  190. try {
  191. for (var _iterator2 = remaped.entries()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
  192. var _ref = _step2.value;
  193. var _ref2 = _slicedToArray(_ref, 2);
  194. var set = _ref2[0];
  195. var _idx = _ref2[1];
  196. minimizedTable[_idx] = {};
  197. var _iteratorNormalCompletion6 = true;
  198. var _didIteratorError6 = false;
  199. var _iteratorError6 = undefined;
  200. try {
  201. for (var _iterator6 = alphabet[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
  202. var symbol = _step6.value;
  203. updateAcceptingStates(set, _idx);
  204. // Determine original transition for this symbol from the set.
  205. var originalTransition = void 0;
  206. var _iteratorNormalCompletion7 = true;
  207. var _didIteratorError7 = false;
  208. var _iteratorError7 = undefined;
  209. try {
  210. for (var _iterator7 = set[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
  211. var originalState = _step7.value;
  212. originalTransition = table[originalState][symbol];
  213. if (originalTransition) {
  214. break;
  215. }
  216. }
  217. } catch (err) {
  218. _didIteratorError7 = true;
  219. _iteratorError7 = err;
  220. } finally {
  221. try {
  222. if (!_iteratorNormalCompletion7 && _iterator7.return) {
  223. _iterator7.return();
  224. }
  225. } finally {
  226. if (_didIteratorError7) {
  227. throw _iteratorError7;
  228. }
  229. }
  230. }
  231. if (originalTransition) {
  232. minimizedTable[_idx][symbol] = remaped.get(currentTransitionMap[originalTransition]);
  233. }
  234. }
  235. } catch (err) {
  236. _didIteratorError6 = true;
  237. _iteratorError6 = err;
  238. } finally {
  239. try {
  240. if (!_iteratorNormalCompletion6 && _iterator6.return) {
  241. _iterator6.return();
  242. }
  243. } finally {
  244. if (_didIteratorError6) {
  245. throw _iteratorError6;
  246. }
  247. }
  248. }
  249. }
  250. // Update the table, and accepting states on the original DFA.
  251. } catch (err) {
  252. _didIteratorError2 = true;
  253. _iteratorError2 = err;
  254. } finally {
  255. try {
  256. if (!_iteratorNormalCompletion2 && _iterator2.return) {
  257. _iterator2.return();
  258. }
  259. } finally {
  260. if (_didIteratorError2) {
  261. throw _iteratorError2;
  262. }
  263. }
  264. }
  265. dfa.setTransitionTable(minimizedTable);
  266. dfa.setAcceptingStateNumbers(minimizedAcceptingStates);
  267. return dfa;
  268. }
  269. function sameRow(r1, r2) {
  270. if (!r2) {
  271. return false;
  272. }
  273. if (r1.length !== r2.length) {
  274. return false;
  275. }
  276. for (var i = 0; i < r1.length; i++) {
  277. var s1 = r1[i];
  278. var s2 = r2[i];
  279. if (s1.size !== s2.size) {
  280. return false;
  281. }
  282. if ([].concat(_toConsumableArray(s1)).sort().join(',') !== [].concat(_toConsumableArray(s2)).sort().join(',')) {
  283. return false;
  284. }
  285. }
  286. return true;
  287. }
  288. /**
  289. * Checks whether two states are N-equivalent, i.e. whether they go
  290. * to the same set on a symbol.
  291. */
  292. function areEquivalent(s1, s2, table, alphabet) {
  293. var _iteratorNormalCompletion8 = true;
  294. var _didIteratorError8 = false;
  295. var _iteratorError8 = undefined;
  296. try {
  297. for (var _iterator8 = alphabet[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
  298. var symbol = _step8.value;
  299. if (!goToSameSet(s1, s2, table, symbol)) {
  300. return false;
  301. }
  302. }
  303. } catch (err) {
  304. _didIteratorError8 = true;
  305. _iteratorError8 = err;
  306. } finally {
  307. try {
  308. if (!_iteratorNormalCompletion8 && _iterator8.return) {
  309. _iterator8.return();
  310. }
  311. } finally {
  312. if (_didIteratorError8) {
  313. throw _iteratorError8;
  314. }
  315. }
  316. }
  317. return true;
  318. }
  319. /**
  320. * Checks whether states go to the same set.
  321. */
  322. function goToSameSet(s1, s2, table, symbol) {
  323. if (!currentTransitionMap[s1] || !currentTransitionMap[s2]) {
  324. return false;
  325. }
  326. var originalTransitionS1 = table[s1][symbol];
  327. var originalTransitionS2 = table[s2][symbol];
  328. // If no actual transition on this symbol, treat it as positive.
  329. if (!originalTransitionS1 && !originalTransitionS2) {
  330. return true;
  331. }
  332. // Otherwise, check if they are in the same sets.
  333. return currentTransitionMap[s1].has(originalTransitionS1) && currentTransitionMap[s2].has(originalTransitionS2);
  334. }
  335. module.exports = {
  336. minimize: minimize
  337. };