/** * The MIT License (MIT) * Copyright (c) 2017-present Dmitry Soshnikov */ 'use strict'; // DFA minization. /** * Map from state to current set it goes. */ 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"); } }; }(); function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); } 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); } } var currentTransitionMap = null; /** * Takes a DFA, and returns a minimized version of it * compressing some states to groups (using standard, 0-, 1-, * 2-, ... N-equivalence algorithm). */ function minimize(dfa) { var table = dfa.getTransitionTable(); var allStates = Object.keys(table); var alphabet = dfa.getAlphabet(); var accepting = dfa.getAcceptingStateNumbers(); currentTransitionMap = {}; var nonAccepting = new Set(); allStates.forEach(function (state) { state = Number(state); var isAccepting = accepting.has(state); if (isAccepting) { currentTransitionMap[state] = accepting; } else { nonAccepting.add(state); currentTransitionMap[state] = nonAccepting; } }); // --------------------------------------------------------------------------- // Step 1: build equivalent sets. // All [1..N] equivalent sets. var all = [ // 0-equivalent sets. [nonAccepting, accepting].filter(function (set) { return set.size > 0; })]; var current = void 0; var previous = void 0; // Top of the stack is the current list of sets to analyze. current = all[all.length - 1]; // Previous set (to check whether we need to stop). previous = all[all.length - 2]; // Until we'll not have the same N and N-1 equivalent rows. var _loop = function _loop() { var newTransitionMap = {}; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = current[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var _set = _step3.value; // Handled states for this set. var handledStates = {}; var _set2 = _toArray(_set), first = _set2[0], rest = _set2.slice(1); handledStates[first] = new Set([first]); // Have to compare each from the rest states with // the already handled states, and see if they are equivalent. var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { restSets: for (var _iterator4 = rest[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var state = _step4.value; var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = Object.keys(handledStates)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var handledState = _step5.value; // This and some previously handled state are equivalent -- // just append this state to the same set. if (areEquivalent(state, handledState, table, alphabet)) { handledStates[handledState].add(state); handledStates[state] = handledStates[handledState]; continue restSets; } } // Else, this state is not equivalent to any of the // handled states -- allocate a new set for it. } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5.return) { _iterator5.return(); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } handledStates[state] = new Set([state]); } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } // Add these handled states to all states map. Object.assign(newTransitionMap, handledStates); } // Update current transition map for the handled row. } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } currentTransitionMap = newTransitionMap; var newSets = new Set(Object.keys(newTransitionMap).map(function (state) { return newTransitionMap[state]; })); all.push([].concat(_toConsumableArray(newSets))); // Top of the stack is the current. current = all[all.length - 1]; // Previous set. previous = all[all.length - 2]; }; while (!sameRow(current, previous)) { _loop(); } // --------------------------------------------------------------------------- // Step 2: build minimized table from the equivalent sets. // Remap state numbers from sets to index-based. var remaped = new Map(); var idx = 1; current.forEach(function (set) { return remaped.set(set, idx++); }); // Build the minimized table from the calculated equivalent sets. var minimizedTable = {}; var minimizedAcceptingStates = new Set(); var updateAcceptingStates = function updateAcceptingStates(set, idx) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = set[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var state = _step.value; if (accepting.has(state)) { minimizedAcceptingStates.add(idx); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = remaped.entries()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _ref = _step2.value; var _ref2 = _slicedToArray(_ref, 2); var set = _ref2[0]; var _idx = _ref2[1]; minimizedTable[_idx] = {}; var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; try { for (var _iterator6 = alphabet[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { var symbol = _step6.value; updateAcceptingStates(set, _idx); // Determine original transition for this symbol from the set. var originalTransition = void 0; var _iteratorNormalCompletion7 = true; var _didIteratorError7 = false; var _iteratorError7 = undefined; try { for (var _iterator7 = set[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { var originalState = _step7.value; originalTransition = table[originalState][symbol]; if (originalTransition) { break; } } } catch (err) { _didIteratorError7 = true; _iteratorError7 = err; } finally { try { if (!_iteratorNormalCompletion7 && _iterator7.return) { _iterator7.return(); } } finally { if (_didIteratorError7) { throw _iteratorError7; } } } if (originalTransition) { minimizedTable[_idx][symbol] = remaped.get(currentTransitionMap[originalTransition]); } } } catch (err) { _didIteratorError6 = true; _iteratorError6 = err; } finally { try { if (!_iteratorNormalCompletion6 && _iterator6.return) { _iterator6.return(); } } finally { if (_didIteratorError6) { throw _iteratorError6; } } } } // Update the table, and accepting states on the original DFA. } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } dfa.setTransitionTable(minimizedTable); dfa.setAcceptingStateNumbers(minimizedAcceptingStates); return dfa; } function sameRow(r1, r2) { if (!r2) { return false; } if (r1.length !== r2.length) { return false; } for (var i = 0; i < r1.length; i++) { var s1 = r1[i]; var s2 = r2[i]; if (s1.size !== s2.size) { return false; } if ([].concat(_toConsumableArray(s1)).sort().join(',') !== [].concat(_toConsumableArray(s2)).sort().join(',')) { return false; } } return true; } /** * Checks whether two states are N-equivalent, i.e. whether they go * to the same set on a symbol. */ function areEquivalent(s1, s2, table, alphabet) { var _iteratorNormalCompletion8 = true; var _didIteratorError8 = false; var _iteratorError8 = undefined; try { for (var _iterator8 = alphabet[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { var symbol = _step8.value; if (!goToSameSet(s1, s2, table, symbol)) { return false; } } } catch (err) { _didIteratorError8 = true; _iteratorError8 = err; } finally { try { if (!_iteratorNormalCompletion8 && _iterator8.return) { _iterator8.return(); } } finally { if (_didIteratorError8) { throw _iteratorError8; } } } return true; } /** * Checks whether states go to the same set. */ function goToSameSet(s1, s2, table, symbol) { if (!currentTransitionMap[s1] || !currentTransitionMap[s2]) { return false; } var originalTransitionS1 = table[s1][symbol]; var originalTransitionS2 = table[s2][symbol]; // If no actual transition on this symbol, treat it as positive. if (!originalTransitionS1 && !originalTransitionS2) { return true; } // Otherwise, check if they are in the same sets. return currentTransitionMap[s1].has(originalTransitionS1) && currentTransitionMap[s2].has(originalTransitionS2); } module.exports = { minimize: minimize };