00001 #include "algorithms.h"
00002
00003 #include "interfaces/iautomaton.h"
00004 #include "interfaces/istate.h"
00005 #include "interfaces/itransition.h"
00006
00007 #include <QSet>
00008 #include <QQueue>
00009 #include <QStringList>
00010 #include <QMap>
00011
00012 #include <QDialog>
00013 #include <QGridLayout>
00014 #include <QCheckBox>
00015 #include <QPushButton>
00016
00017 #include <QtPlugin>
00018
00019 #include "utility/dbglog.h"
00020 #include "utility/utils.h"
00021
00022 #define DBGLOG_ALGO(x) DBGLOG_("ALGORITHMS", x)
00023
00024
00025
00026 class SettingsDialog : public QDialog
00027 {
00028 public:
00029 SettingsDialog(BasicAlgorithmWithSettings &algorithm, QWidget *parent)
00030 : QDialog(parent), m_algorithm(algorithm)
00031 {
00032 m_checkPreserveNames = new QCheckBox(tr("Preserve state names"), this);
00033 m_checkPreserveNames->setChecked(m_algorithm.preserveNames());
00034
00035 m_checkPreserveLabels = new QCheckBox(tr("Preserve state labels"), this);
00036 m_checkPreserveLabels->setChecked(m_algorithm.preserveLabels());
00037
00038
00039 QWidget *hBox = new QWidget(this);
00040 QPushButton *buttonOk = new QPushButton("Ok");
00041 QPushButton *buttonCancel = new QPushButton("Cancel");
00042 QHBoxLayout *hBoxLayout = new QHBoxLayout;
00043 hBoxLayout->addWidget(buttonOk);
00044 hBoxLayout->addWidget(buttonCancel);
00045 hBox->setLayout(hBoxLayout);
00046 hBox->setMaximumWidth(300);
00047 connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept()));
00048 connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
00049
00050 QGridLayout *gridLayout = new QGridLayout(this);
00051 gridLayout->addWidget(m_checkPreserveNames, 0, 0);
00052 gridLayout->addWidget(m_checkPreserveLabels, 1, 0);
00053 gridLayout->addWidget(hBox, 2, 0);
00054
00055 QString s = m_algorithm.getName() + " - settings";
00056 setWindowTitle(s);
00057 setWindowIcon(QIcon(":images/grid.xpm"));
00058 }
00059
00060 bool preserveNames() const { return m_checkPreserveNames->checkState() == Qt::Checked; }
00061 bool preserveLabels() const { return m_checkPreserveLabels->checkState() == Qt::Checked; }
00062
00063 private:
00064 QCheckBox *m_checkPreserveNames;
00065 QCheckBox *m_checkPreserveLabels;
00066 BasicAlgorithmWithSettings &m_algorithm;
00067 };
00068
00069
00070
00071
00072
00073
00074
00075 BasicAlgorithm::BasicAlgorithm()
00076 : m_creator(QSharedPointer<IAutomataCreator>(NULL))
00077 {}
00078
00079 void BasicAlgorithm::setAutomataCreator(const QSharedPointer<IAutomataCreator> &creator)
00080 {
00081 m_creator = creator;
00082 }
00083
00084 bool BasicAlgorithm::run(const IAutomaton::TAutomataList &input,
00085 QSharedPointer<IAutomaton> &result,
00086 QString *report) const
00087 {
00088 m_report = "";
00089
00090 if (!m_creator)
00091 {
00092 addReport("Creator is not set, contact plugin's developer");
00093 RELLOG(getName() << " -> creator is not set!!!");
00094 return false;
00095 }
00096
00097 DBGLOG_ALGO(getName() << " -> " << input.count()
00098 << (input.count() != 1 ? "automata" : "automaton") << "on input");
00099
00100 bool ret = runInternal(input, result);
00101
00102
00103 if (report)
00104 {
00105 *report = m_report;
00106 }
00107
00108 return ret;
00109 }
00110
00111 IState::TIStateNameSet BasicAlgorithm::getEpsilonClosure
00112 (const QSharedPointer<IAutomaton> &automaton, const QSharedPointer<IState> &state) const
00113 {
00114 IState::TIStateNameSet result;
00115
00116 QQueue<QSharedPointer<IState> > openedStates;
00117 openedStates.enqueue(state);
00118
00119 QSharedPointer<IState> currentState;
00120 QString workState;
00121
00122 while(!openedStates.empty())
00123 {
00124 currentState = openedStates.dequeue();
00125 result << currentState->getName();
00126
00127 foreach(const QSharedPointer<ITransition> &tr, currentState->getTransitions())
00128 {
00129 if (tr->getCharacters().contains(automaton->getEpsilonSymbol()))
00130 {
00131 workState = tr->getDestinationState();
00132
00133 if (result.contains(workState)) continue;
00134 result << workState;
00135
00136 openedStates << automaton->getState(workState);
00137 }
00138 }
00139 }
00140
00141 return result;
00142 }
00143
00144 bool BasicAlgorithm::removeEpsilonTransitions(QSharedPointer<IAutomaton> &automaton) const
00145 {
00146 QSharedPointer<IAlgorithm> epsAlgorithm(new RemoveEpsilonAlgorithm());
00147 epsAlgorithm->setAutomataCreator(m_creator);
00148
00149 IAutomaton::TAutomataList automata;
00150 automata << automaton;
00151
00152 QString report;
00153 if (!epsAlgorithm->run(automata, automaton, &report))
00154 {
00155 addReport(report);
00156 return false;
00157 }
00158
00159 return true;
00160 }
00161
00162 bool BasicAlgorithm::removeMultipleInitials(QSharedPointer<IAutomaton> &automaton) const
00163 {
00164 QSharedPointer<IAlgorithm> miAlgorithm(new RemoveMultipleInitialsAlgorithm());
00165 miAlgorithm->setAutomataCreator(m_creator);
00166
00167 IAutomaton::TAutomataList automata;
00168 automata << automaton;
00169
00170 QString report;
00171 if (!miAlgorithm->run(automata, automaton, &report))
00172 {
00173 addReport(report);
00174 return false;
00175 }
00176
00177 return true;
00178 }
00179
00180 bool BasicAlgorithm::determinize(QSharedPointer<IAutomaton> &automaton) const
00181 {
00182 QSharedPointer<IAlgorithm> determinizeAlg(new DeterminizeAlgorithm());
00183 determinizeAlg->setAutomataCreator(m_creator);
00184
00185 IAutomaton::TAutomataList automata;
00186 automata << automaton;
00187
00188 QString report;
00189 if (!determinizeAlg->run(automata, automaton, &report))
00190 {
00191 addReport(report);
00192 return false;
00193 }
00194
00195 return true;
00196 }
00197
00198 void BasicAlgorithm::makeSureNameUnique(const QSharedPointer<IAutomaton> &automaton,
00199 QString &name) const
00200 {
00201 bool changed = false;
00202 while(automaton->hasState(name))
00203 {
00204 name += "*";
00205 changed = true;
00206 }
00207 }
00208
00209 void BasicAlgorithm::makeSureNamesUnique(const QSharedPointer<IAutomaton> &automaton1,
00210 const QSharedPointer<IAutomaton> &automaton2) const
00211 {
00212 IState::TIStateList a2States = automaton2->getStates();
00213 foreach(const QSharedPointer<IState> &state, a2States)
00214 {
00215 bool changed = false;
00216 QString newName = state->getName();
00217 while(automaton1->hasState(newName) ||
00218 (changed && automaton2->hasState(newName)))
00219 {
00220 newName += "*";
00221 changed = true;
00222 }
00223 if (changed)
00224 automaton2->renameState(state, newName);
00225 }
00226 }
00227
00228 void BasicAlgorithm::addReport(const QString &text) const
00229 {
00230 m_report += text;
00231 }
00232
00233 bool BasicAlgorithm::tryMergeAlphabet(const ITransition::TCharSet &alphabet1,
00234 const ITransition::TCharSet &alphabet2,
00235 ITransition::TCharSet &alphabet) const
00236 {
00237
00238 ITransition::TCharSet intersection = alphabet1 & alphabet2;
00239 if (!(alphabet1 - intersection).empty() &&
00240 !(alphabet2 - intersection).empty())
00241 {
00242 return false;
00243 }
00244
00245 alphabet = alphabet1;
00246 alphabet |= alphabet2;
00247 return true;
00248 }
00249
00250
00251
00252
00253
00254
00255
00256 BasicAlgorithmWithSettings::BasicAlgorithmWithSettings(bool preserveNames, bool preserveLabels)
00257 : m_preserveNames(preserveNames), m_preserveLabels(preserveLabels)
00258 {}
00259
00260 void BasicAlgorithmWithSettings::runSettingsDialog(QWidget *parent)
00261 {
00262 SettingsDialog sd(*this, parent);
00263
00264 if (sd.exec() == QDialog::Rejected) return;
00265
00266 setPreserveNames(sd.preserveNames());
00267 setPreserveLabels(sd.preserveLabels());
00268
00269
00270 }
00271
00272 QString BasicAlgorithmWithSettings::chooseStateName(QSharedPointer<IAutomaton> &automaton,
00273 const QStringList &nameList, int num) const
00274 {
00275 QString name;
00276 if (preserveNames())
00277 name = nameList.join("");
00278 else
00279 name = QString("Q%1").arg(num);
00280
00281 makeSureNameUnique(automaton, name);
00282 return name;
00283 }
00284
00285 QString BasicAlgorithmWithSettings::chooseStateLabel(const QStringList &labelList, int num) const
00286 {
00287 QString label;
00288 if (preserveLabels())
00289 label = labelList.join("");
00290 else
00291 label = QString("%1").arg(num);
00292 return label;
00293 }
00294
00295
00296
00297
00298
00299
00300
00301 RemoveEpsilonAlgorithm::~RemoveEpsilonAlgorithm()
00302 {
00303 DBGLOG_ALGO("deleted");
00304 }
00305
00306 QString RemoveEpsilonAlgorithm::getName() const
00307 {
00308 return "Remove epsilon transitions";
00309 }
00310
00311 bool RemoveEpsilonAlgorithm::runInternal(const IAutomaton::TAutomataList &input,
00312 QSharedPointer<IAutomaton> &result) const
00313 {
00314 if (input.empty())
00315 {
00316 RELLOG("nothing on input");
00317 addReport("nothing on input");
00318 return false;
00319 }
00320
00321 QSharedPointer<IAutomaton> automaton = input[0];
00322
00323 typedef QMap<QString, IState::TIStateNameSet> TEpsilonClosureMap;
00324 TEpsilonClosureMap closureMap;
00325
00326 foreach(const QSharedPointer<IState> &state, automaton->getStates())
00327 {
00328 closureMap[state->getName()] = getEpsilonClosure(automaton, state);
00329 DBGLOG_ALGO("epsilonClosure(" << state->getName() << ")=" << closureMap[state->getName()]);
00330 }
00331
00332 QSharedPointer<IAutomaton> newAutomaton =
00333 m_creator->createAutomaton(automaton->getAlphabet(), automaton->getAlphabetSymbol(),
00334 automaton->getEpsilonSymbol());
00335
00336
00337
00338 foreach(const QSharedPointer<IState> ¤tState, automaton->getStates())
00339 {
00340 newAutomaton->createState(currentState->getName(), currentState->getLabel(),
00341 currentState->isInitial(), currentState->isFinal());
00342 }
00343
00344 bool ok = true;
00345 foreach(const QSharedPointer<IState> ¤tState, newAutomaton->getStates())
00346 {
00347 bool isFinal = false;
00348
00349 foreach(const QString &character, automaton->getAlphabet())
00350 {
00351 IState::TIStateNameSet destinations;
00352
00353 ITransition::TCharSet charset;
00354 charset << character;
00355
00356 foreach(const QString &stateName, closureMap[currentState->getName()])
00357 {
00358 const QSharedPointer<IState> &state= automaton->getState(stateName);
00359 isFinal = isFinal || state->isFinal();
00360
00361 destinations |= state->getStatesOn(character);
00362 }
00363
00364 foreach(const QString &stateName, destinations)
00365 {
00366 newAutomaton->createTransition(currentState->getName(), stateName, charset);
00367 }
00368 }
00369
00370 currentState->setFinal(isFinal);
00371 }
00372
00373 if (ok)
00374 {
00375 result = newAutomaton;
00376 return true;
00377 }
00378
00379 return false;
00380 }
00381
00382
00383
00384
00385
00386
00387
00388 RemoveInaccessibleAlgorithm::~RemoveInaccessibleAlgorithm()
00389 {
00390 DBGLOG_ALGO("deleted");
00391 }
00392
00393 QString RemoveInaccessibleAlgorithm::getName() const
00394 {
00395 return "Remove inaccessible states";
00396 }
00397
00398 bool RemoveInaccessibleAlgorithm::runInternal(const IAutomaton::TAutomataList &input,
00399 QSharedPointer<IAutomaton> &result) const
00400 {
00401 if (input.empty())
00402 {
00403 RELLOG("nothing on input");
00404 addReport("nothing on input");
00405 return false;
00406 }
00407
00408 QSharedPointer<IAutomaton> automaton = input[0];
00409
00410 QSharedPointer<IAutomaton> newAutomaton =
00411 m_creator->createAutomaton(automaton->getAlphabet(), automaton->getAlphabetSymbol(),
00412 automaton->getEpsilonSymbol());
00413
00414
00415 IState::TIStateNameSet accessibleSet;
00416 IState::TIStateList accessibleList;
00417 foreach(const QSharedPointer<IState> &state, automaton->getInitialStates())
00418 {
00419 accessibleSet << state->getName();
00420 accessibleList << state;
00421
00422 newAutomaton->createState(state->getName(), state->getLabel(), state->isInitial(), state->isFinal());
00423 }
00424
00425 IState::TIStateList newListQi;
00426 IState::TIStateList listQi = accessibleList;
00427 QSharedPointer<IState> newState;
00428 QSharedPointer<IState> currentState;
00429
00430 bool ok = true;
00431
00432
00433 do
00434 {
00435 foreach(const QSharedPointer<IState> &state, listQi)
00436 {
00437 foreach(const QSharedPointer<ITransition> &tr, state->getTransitions())
00438 {
00439 QString workState = tr->getDestinationState();
00440 if (accessibleSet.contains(workState)) continue;
00441
00442 accessibleSet << workState;
00443
00444 currentState = automaton->getState(workState);
00445 accessibleList << currentState;
00446 newListQi << currentState;
00447
00448 ok = ok && (newAutomaton->createState(currentState->getName(),
00449 currentState->getLabel(),
00450 currentState->isInitial(),
00451 currentState->isFinal()) != NULL);
00452 }
00453 }
00454
00455 listQi = newListQi;
00456 newListQi.clear();
00457 }
00458 while(!listQi.empty());
00459
00460 if (!ok) return false;
00461
00462 foreach(const QSharedPointer<IState> &state, accessibleList)
00463 {
00464 foreach(const QSharedPointer<ITransition> &tr, state->getTransitions())
00465 {
00466 if (!accessibleSet.contains(tr->getDestinationState())) continue;
00467
00468 ok = ok &&
00469 (newAutomaton->createTransition(tr->getSourceState(),
00470 tr->getDestinationState(),
00471 tr->getCharacters()) != NULL);
00472 }
00473 }
00474
00475 if (ok)
00476 {
00477 result = newAutomaton;
00478 return true;
00479 }
00480
00481 return false;
00482 }
00483
00484
00485
00486
00487
00488
00489
00490 RemoveUselessAlgorithm::~RemoveUselessAlgorithm()
00491 {
00492 DBGLOG_ALGO("deleted");
00493 }
00494
00495 QString RemoveUselessAlgorithm::getName() const
00496 {
00497 return "Remove useless states";
00498 }
00499
00500 bool RemoveUselessAlgorithm::runInternal(const IAutomaton::TAutomataList &input,
00501 QSharedPointer<IAutomaton> &result) const
00502 {
00503 if (input.empty())
00504 {
00505 RELLOG("nothing on input");
00506 addReport("nothing on input");
00507 return false;
00508 }
00509
00510 QSharedPointer<IAutomaton> automaton = input[0];
00511
00512 QSharedPointer<IAutomaton> newAutomaton =
00513 m_creator->createAutomaton(automaton->getAlphabet(), automaton->getAlphabetSymbol(),
00514 automaton->getEpsilonSymbol());
00515
00516
00517 IState::TIStateNameSet usefulSet;
00518 IState::TIStateList usefulList;
00519 foreach(const QSharedPointer<IState> &state, automaton->getFinalStates())
00520 {
00521 usefulSet << state->getName();
00522 usefulList << state;
00523 newAutomaton->createState(state->getName(),
00524 state->getLabel(),
00525 state->isInitial(),
00526 state->isFinal());
00527 }
00528
00529 IState::TIStateList newListQi;
00530 IState::TIStateList listQi = usefulList;
00531 QSharedPointer<IState> newState;
00532 QSharedPointer<IState> currentState;
00533
00534
00535 do
00536 {
00537 foreach(const QSharedPointer<IState> &state, listQi)
00538 {
00539 foreach(const QSharedPointer<ITransition> &tr, state->getTransitionsTo())
00540 {
00541 QString workState = tr->getSourceState();
00542 if (usefulSet.contains(workState)) continue;
00543
00544 usefulSet << workState;
00545
00546 currentState = automaton->getState(workState);
00547 usefulList << currentState;
00548 newListQi << currentState;
00549
00550 newAutomaton->createState(currentState->getName(),
00551 currentState->getLabel(),
00552 currentState->isInitial(),
00553 currentState->isFinal());
00554 }
00555 }
00556
00557 listQi = newListQi;
00558 newListQi.clear();
00559 }
00560 while (!listQi.empty());
00561
00562 bool ok = true;
00563 foreach(const QSharedPointer<IState> &state, usefulList)
00564 {
00565 foreach(const QSharedPointer<ITransition> &tr, state->getTransitionsTo())
00566 {
00567 if (!usefulSet.contains(tr->getSourceState())) continue;
00568
00569 ok = ok && (newAutomaton->createTransition(tr->getSourceState(),
00570 tr->getDestinationState(),
00571 tr->getCharacters()) != NULL);
00572 }
00573 }
00574
00575 if (ok)
00576 {
00577 result = newAutomaton;
00578 return true;
00579 }
00580
00581 return false;
00582 }
00583
00584
00585
00586
00587
00588
00589
00590 RemoveMultipleInitialsAlgorithm::~RemoveMultipleInitialsAlgorithm()
00591 {
00592 DBGLOG_ALGO("deleted");
00593 }
00594
00595 QString RemoveMultipleInitialsAlgorithm::getName() const
00596 {
00597 return "Remove multiple inital states";
00598 }
00599
00600 bool RemoveMultipleInitialsAlgorithm::runInternal(const IAutomaton::TAutomataList &input,
00601 QSharedPointer<IAutomaton> &result) const
00602 {
00603 if (input.empty())
00604 {
00605 RELLOG("nothing on input");
00606 addReport("nothing on input");
00607 return false;
00608 }
00609
00610 QSharedPointer<IAutomaton> automaton = input[0];
00611
00612
00613 IState::TIStateList initialStates = automaton->getInitialStates();
00614
00615 if (initialStates.empty())
00616 return false;
00617
00618 if (initialStates.count() == 1)
00619 {
00620 result = automaton;
00621 return true;
00622 }
00623
00624 QStringList q0NameList, q0LabelList;
00625 bool q0final = false;
00626 foreach(const QSharedPointer<IState> &state, initialStates)
00627 {
00628 q0NameList << state->getName();
00629 q0LabelList << state->getLabel();
00630 state->setInitial(false);
00631 q0final = q0final || state->isFinal();
00632 }
00633
00634 QString q0Name = "["+q0NameList.join(",")+"]";
00635 makeSureNameUnique(automaton, q0Name);
00636
00637
00638 automaton->createState(q0Name,
00639 q0LabelList.join(""),
00640 true,
00641
00642 q0final);
00643
00644
00645 foreach(const QSharedPointer<IState> &state, initialStates)
00646 {
00647 foreach(const QSharedPointer<ITransition> &tr, state->getTransitions())
00648 {
00649 automaton->createTransition(q0Name, tr->getDestinationState(), tr->getCharacters());
00650 }
00651 }
00652
00653
00654
00655 foreach(const QSharedPointer<IState> &state, initialStates)
00656 {
00657 bool accessible = false;
00658 foreach(const QSharedPointer<ITransition> &tr, state->getTransitionsTo())
00659 {
00660 accessible |= (tr->getSourceState() == q0Name);
00661 }
00662
00663 if (!accessible)
00664 automaton->removeState(state);
00665 }
00666
00667 result = automaton;
00668 return true;
00669 }
00670
00671
00672
00673
00674
00675
00676
00677 DeterminizeAlgorithm::DeterminizeAlgorithm()
00678 : BasicAlgorithmWithSettings(false, true)
00679 {}
00680
00681 DeterminizeAlgorithm::~DeterminizeAlgorithm()
00682 {
00683 DBGLOG_ALGO("deleted");
00684 }
00685
00686 QString DeterminizeAlgorithm::getName() const
00687 {
00688 return "Determinize automaton";
00689 }
00690
00691 bool DeterminizeAlgorithm::runInternal(const IAutomaton::TAutomataList &input,
00692 QSharedPointer<IAutomaton> &result) const
00693 {
00694 if (input.empty())
00695 {
00696 RELLOG("nothing on input");
00697 addReport("nothing on input");
00698 return false;
00699 }
00700
00701 QSharedPointer<IAutomaton> automaton = input[0];
00702 if (automaton->hasEpsilonTransitions())
00703 {
00704 if (!removeEpsilonTransitions(automaton))
00705 return false;
00706 }
00707
00708 if (automaton->hasMultipleInitials())
00709 {
00710 if (!removeMultipleInitials(automaton))
00711 return false;
00712 }
00713
00714
00715 Q_ASSERT(automaton->getInitialStates().count() == 1);
00716
00717 QSharedPointer<IAutomaton> automatonDFA(m_creator->createAutomaton(automaton->getAlphabet(),
00718 automaton->getAlphabetSymbol(),
00719 automaton->getEpsilonSymbol()));
00720
00721 int stateNum = 0;
00722
00723
00724 QSharedPointer<IState> newState = (automaton->getInitialStates())[0];
00725 QStringList newStateNameList(newState->getName());
00726
00727 QString newStateName = chooseStateName(automatonDFA, newStateNameList, stateNum);
00728 QString newStateLabel = chooseStateLabel(QStringList(newState->getLabel()), stateNum);
00729 newState = automatonDFA->createState(newStateName, newStateLabel, true, newState->isFinal());
00730 stateNum++;
00731
00732
00733 QQueue<QSharedPointer<IState> > openedStates;
00734 openedStates.enqueue(newState);
00735
00736 QMap<QString, QStringList> nameToNameListMap;
00737 nameToNameListMap[newStateName] = newStateNameList;
00738
00739
00740 while (!openedStates.empty())
00741 {
00742 QSharedPointer<IState> currentState = openedStates.dequeue();
00743
00744
00745 ITransition::TCharSet alphabet = automaton->getAlphabet();
00746 foreach(const QString &character, alphabet)
00747 {
00748 IState::TIStateNameSet newStateNameSet;
00749
00750 Q_ASSERT(nameToNameListMap.contains(currentState->getName()));
00751 QStringList origNameList = nameToNameListMap[currentState->getName()];
00752 foreach(const QString &stateName, origNameList)
00753 {
00754 QSharedPointer<IState> state = automaton->getState(stateName);
00755 newStateNameSet |= state->getStatesOn(character);
00756 }
00757
00758 if (newStateNameSet.empty()) continue;
00759
00760 newStateNameList = newStateNameSet.toList();
00761 qSort(newStateNameList);
00762
00763 QStringList newStateLabelList;
00764 bool newStateIsFinal = false;
00765
00766 foreach(const QString &partName, newStateNameSet)
00767 {
00768 QSharedPointer<IState> state = automaton->getState(partName);
00769 newStateIsFinal = newStateIsFinal || state->isFinal();
00770 newStateLabelList << state->getLabel();
00771 }
00772
00773 if (nameToNameListMap.values().contains(newStateNameList))
00774 {
00775 newState = automatonDFA->getState(nameToNameListMap.key(newStateNameList));
00776
00777
00778 automatonDFA->createTransition(currentState->getName(), newState->getName(),
00779 (ITransition::TCharSet() << character));
00780
00781 continue;
00782 }
00783
00784 newStateName = chooseStateName(automatonDFA, newStateNameList, stateNum);
00785 newStateLabel = chooseStateLabel(newStateLabelList, stateNum);
00786
00787
00788 newState = automatonDFA->createState(newStateName, newStateLabel, false, newStateIsFinal);
00789 stateNum++;
00790
00791 openedStates.enqueue(newState);
00792 nameToNameListMap[newStateName] = newStateNameList;
00793
00794
00795 automatonDFA->createTransition(currentState->getName(), newStateName,
00796 (ITransition::TCharSet() << character));
00797 }
00798 }
00799
00800 result = automatonDFA;
00801
00802 return true;
00803 }
00804
00805
00806
00807
00808
00809
00810
00811 class GroupItem
00812 {
00813 public:
00814 typedef QStringList TTrList;
00815
00816 explicit GroupItem(const QString &state, const TTrList &trList = TTrList(), bool initial = false)
00817 : m_state(state), m_trList(trList), m_initial(initial)
00818 {}
00819
00820 QString getState() const { return m_state; }
00821 TTrList getTrList() const { return m_trList; }
00822 bool isInitial() const { return m_initial; }
00823
00824 private:
00825 QString m_state;
00826 TTrList m_trList;
00827 bool m_initial;
00828 };
00829
00830 inline bool operator==(const GroupItem &lhs, const GroupItem &rhs)
00831 {
00832 return lhs.getState() == rhs.getState();
00833 }
00834
00835 inline uint qHash(const GroupItem &key)
00836 {
00837 return qHash(key.getState());
00838 }
00839
00840 class Group
00841 {
00842 public:
00843 typedef QVector<QSharedPointer<Group> > TGroupList;
00844 typedef QSet<GroupItem> TItemSet;
00845
00846 Group(int id)
00847 : m_id(id)
00848 {}
00849
00850 int id() const { return m_id; }
00851
00852 int count() const { return m_states.count(); }
00853
00854 bool contains(const QString &state) const { return m_states.contains(GroupItem(state)); }
00855
00856 TItemSet items() const { return m_states; }
00857
00858 void insert(const GroupItem &state) { m_states << state; }
00859
00860 bool initial() const
00861 {
00862 foreach(const GroupItem &state, m_states)
00863 {
00864 if (state.isInitial()) return true;
00865 }
00866 return false;
00867 }
00868
00869
00870
00871 TGroupList computeAndSplit(const TGroupList &groups)
00872 {
00873 QList<QVector<int> > trGroupLists;
00874 TGroupList newGroups;
00875
00876 TItemSet::Iterator stateIt = m_states.begin();
00877 while(stateIt != m_states.end())
00878 {
00879 QVector<int> trGroupList = computeTransitionGroupList(groups, stateIt->getTrList());
00880
00881 int idx = -1;
00882 if ((idx = trGroupLists.indexOf(trGroupList)) != -1)
00883 {
00884 Q_ASSERT(newGroups.count() > idx && trGroupLists.count() > idx);
00885 newGroups[idx]->insert(*stateIt);
00886 }
00887 else
00888 {
00889 trGroupLists << trGroupList;
00890 newGroups << QSharedPointer<Group>(new Group(newGroups.count()));
00891 newGroups.last()->insert(*stateIt);
00892 }
00893
00894 ++stateIt;
00895 }
00896
00897 return newGroups;
00898 }
00899
00900 protected:
00901 typedef QVector<int> TTrGroupList;
00902 TTrGroupList computeTransitionGroupList(const TGroupList &groups,
00903 const GroupItem::TTrList &trList)
00904 {
00905 TTrGroupList trGroupList;
00906 trGroupList.reserve(trList.count());
00907 for (GroupItem::TTrList::ConstIterator trIt = trList.begin();
00908 trIt != trList.end();
00909 ++trIt)
00910 {
00911 if (*trIt == "")
00912 {
00913 trGroupList << -1;
00914 continue;
00915 }
00916
00917 for (TGroupList::ConstIterator groupIt = groups.begin();
00918 groupIt != groups.end();
00919 ++groupIt)
00920 {
00921 if ((*groupIt)->contains(*trIt))
00922 {
00923 trGroupList << (*groupIt)->id();
00924 break;
00925 }
00926 }
00927 }
00928
00929 Q_ASSERT(trGroupList.count() == trList.count());
00930 return trGroupList;
00931 }
00932
00933 private:
00934 TItemSet m_states;
00935 int m_id;
00936 };
00937
00938 #ifndef QT_NO_DEBUG_OUTPUT
00939
00940 QDebug operator<<(QDebug dbg, const Group &g)
00941 {
00942 dbg.nospace() << "Group "<< g.id() << ": (";
00943 Group::TItemSet items = g.items();
00944 for (Group::TItemSet::ConstIterator itemIt = items.begin();
00945 itemIt != items.end();
00946 ++itemIt)
00947 {
00948 if (itemIt != items.begin())
00949 dbg.nospace() << ",";
00950 dbg.nospace() << itemIt->getState();
00951 }
00952 dbg.nospace() << ")";
00953 return dbg.nospace();
00954 }
00955
00956 #endif
00957
00958 MinimalizeAlgorithm::~MinimalizeAlgorithm()
00959 {
00960 DBGLOG_ALGO("deleted");
00961 }
00962
00963 QString MinimalizeAlgorithm::getName() const
00964 {
00965 return "Minimalize automaton";
00966 }
00967
00968 bool MinimalizeAlgorithm::runInternal(const IAutomaton::TAutomataList &input,
00969 QSharedPointer<IAutomaton> &result) const
00970 {
00971 if (input.empty())
00972 {
00973 RELLOG("nothing on input");
00974 addReport("nothing on input");
00975 return false;
00976 }
00977
00978 QSharedPointer<IAutomaton> automaton = input[0];
00979
00980 if (!automaton->isDeterministic())
00981 {
00982 if (!determinize(automaton))
00983 return false;
00984 }
00985
00986
00987 Q_ASSERT(automaton->getInitialStates().count() == 1);
00988
00989 QStringList alphabet = automaton->getAlphabet().toList();
00990 qSort(alphabet);
00991
00992 Group::TGroupList groups;
00993
00994 groups << QSharedPointer<Group>(new Group(groups.count()));
00995 groups << QSharedPointer<Group>(new Group(groups.count()));
00996 foreach(const QSharedPointer<IState> &state, automaton->getStates())
00997 {
00998 GroupItem::TTrList trList;
00999 foreach(const QString &character, alphabet)
01000 {
01001 IState::TIStateNameSet statesOn = state->getStatesOn(character);
01002 Q_ASSERT(statesOn.count() < 2);
01003 if (statesOn.isEmpty())
01004 trList << "";
01005 else
01006 trList << *statesOn.begin();
01007 }
01008
01009 if (state->isFinal()) groups[0]->insert(GroupItem(state->getName(), trList, state->isInitial()));
01010 else groups[1]->insert(GroupItem(state->getName(), trList, state->isInitial()));
01011 }
01012
01013
01014 if (groups[0]->count() == 0)
01015 {
01016 groups.remove(0);
01017 }
01018
01019 while (true)
01020 {
01021 Group::TGroupList newGroups;
01022 for (Group::TGroupList::ConstIterator groupIt = groups.begin();
01023 groupIt != groups.end();
01024 ++groupIt)
01025 {
01026 newGroups << (*groupIt)->computeAndSplit(groups);
01027 }
01028
01029 if (newGroups.count() == groups.count())
01030 break;
01031
01032 groups = newGroups;
01033 }
01034
01035 #ifndef QT_NO_DEBUG_OUTPUT
01036 for (Group::TGroupList::ConstIterator groupIt = groups.begin();
01037 groupIt != groups.end();
01038 ++groupIt)
01039 {
01040 DBGLOG_ALGO(**groupIt);
01041 }
01042 #endif
01043
01044 IState::TIStateNameSet removedStates;
01045
01046 for (Group::TGroupList::ConstIterator groupIt = groups.begin();
01047 groupIt != groups.end();
01048 ++groupIt)
01049 {
01050 Q_ASSERT((*groupIt)->count() != 0 && "shouldn't be empty!");
01051
01052 Group::TItemSet items = (*groupIt)->items();
01053
01054 Group::TItemSet::ConstIterator itemIt = items.begin();
01055 QString firstStateName = itemIt->getState();
01056
01057 if ((*groupIt)->initial())
01058 {
01059 QSharedPointer<IState> state = automaton->getState(firstStateName);
01060 state->setInitial(true);
01061 }
01062
01063 ++itemIt;
01064 while (itemIt != items.end())
01065 {
01066 QSharedPointer<IState> state = automaton->getState(itemIt->getState());
01067 ITransition::TITransitionList transitionsTo = state->getTransitionsTo();
01068 foreach(const QSharedPointer<ITransition> &tr, transitionsTo)
01069 {
01070 if (removedStates.contains(tr->getSourceState()))
01071 continue;
01072 if (tr->getSourceState() == tr->getDestinationState())
01073 continue;
01074 automaton->createTransition(tr->getSourceState(), firstStateName, tr->getCharacters());
01075 }
01076
01077 automaton->removeState(state);
01078 removedStates << state->getName();
01079 ++itemIt;
01080 }
01081 }
01082
01083 result = automaton;
01084
01085 return true;
01086 }
01087
01088
01089
01090
01091
01092
01093
01094 UniteParallelAlgorithm::UniteParallelAlgorithm()
01095 : BasicAlgorithmWithSettings(false, true)
01096 {}
01097
01098 UniteParallelAlgorithm::~UniteParallelAlgorithm()
01099 {
01100 DBGLOG_ALGO("deleted");
01101 }
01102
01103 QString UniteParallelAlgorithm::getName() const
01104 {
01105 return "Unite automata - parallel running";
01106 }
01107
01108 bool UniteParallelAlgorithm::runInternal(const IAutomaton::TAutomataList &input,
01109 QSharedPointer<IAutomaton> &result) const
01110 {
01111 if (input.count() < 2)
01112 {
01113 RELLOG("not enough automata on input");
01114 addReport("not enough automata on input, required 2");
01115 return false;
01116 }
01117
01118 QSharedPointer<IAutomaton> automaton1 = input[0];
01119 QSharedPointer<IAutomaton> automaton2 = input[1];
01120
01121 ITransition::TCharSet alphabet = automaton1->getAlphabet();
01122 if (automaton1->getAlphabet() != automaton2->getAlphabet())
01123 {
01124 if (!tryMergeAlphabet(automaton1->getAlphabet(), automaton2->getAlphabet(), alphabet))
01125 {
01126 addReport("inconsistent alphabets on input, alphabets have to be same or one has to be a subset of the other!\n"
01127 "try to set alphabet manually ...");
01128 RELLOG("not same alphabets");
01129 return false;
01130 }
01131 }
01132
01133 if (automaton1->getAlphabetSymbol() != automaton2->getAlphabetSymbol() ||
01134 automaton1->getEpsilonSymbol() != automaton2->getEpsilonSymbol())
01135 {
01136 addReport("not same automata symbols settings, use same epsilon and alphabet symbol!");
01137 RELLOG("not same automata symbols settings");
01138 return false;
01139 }
01140
01141
01142 if (automaton1->hasEpsilonTransitions())
01143 if (!removeEpsilonTransitions(automaton1))
01144 return false;
01145 if (automaton2->hasEpsilonTransitions())
01146 if (!removeEpsilonTransitions(automaton2))
01147 return false;
01148
01149 if (automaton1->hasMultipleInitials())
01150 if (!removeMultipleInitials(automaton1))
01151 return false;
01152 if (automaton2->hasMultipleInitials())
01153 if (!removeMultipleInitials(automaton2))
01154 return false;
01155
01156 QSharedPointer<IAutomaton> automatonUnited(m_creator->createAutomaton(alphabet,
01157 automaton1->getAlphabetSymbol(),
01158 automaton1->getEpsilonSymbol()));
01159
01160 makeSureNamesUnique(automaton1, automaton2);
01161
01162 Q_ASSERT(automaton1->getInitialStates().count() == 1);
01163 Q_ASSERT(automaton2->getInitialStates().count() == 1);
01164
01165 QSharedPointer<IState> state1 = automaton1->getInitialStates()[0];
01166 QSharedPointer<IState> state2 = automaton2->getInitialStates()[0];
01167
01168 int stateNum = 0;
01169
01170 QStringList newStateNameList = QStringList() << state1->getName() << state2->getName();
01171 QString newStateName = chooseStateName(automatonUnited, newStateNameList, stateNum);
01172 QString newStateLabel = chooseStateLabel((QStringList() << state1->getLabel() << state2->getLabel()), stateNum);
01173 QSharedPointer<IState> newState =
01174 automatonUnited->createState(newStateName, newStateLabel, true, state1->isFinal() || state2->isFinal());
01175 stateNum++;
01176
01177 QQueue<QSharedPointer<IState> > openedStates;
01178 openedStates.enqueue(newState);
01179
01180 QMap<QString, QStringList> nameToNameListMap;
01181 nameToNameListMap[newState->getName()] = newStateNameList;
01182
01183 while (!openedStates.empty())
01184 {
01185 QSharedPointer<IState> currentState = openedStates.dequeue();
01186
01187 foreach(const QString &character, automatonUnited->getAlphabet())
01188 {
01189 IState::TIStateNameSet newStateNameSet;
01190
01191 Q_ASSERT(nameToNameListMap.contains(currentState->getName()));
01192 QStringList origNameList = nameToNameListMap[currentState->getName()];
01193 foreach(const QString &stateName, origNameList)
01194 {
01195 Q_ASSERT(automaton1->hasState(stateName) || automaton2->hasState(stateName));
01196
01197 QSharedPointer<IState> state =
01198 automaton1->hasState(stateName) ? automaton1->getState(stateName)
01199 : automaton2->getState(stateName);
01200
01201 newStateNameSet |= state->getStatesOn(character);
01202 }
01203
01204 if (newStateNameSet.empty()) continue;
01205
01206 newStateNameList = newStateNameSet.toList();
01207 qSort(newStateNameList);
01208
01209 bool newStateIsFinal = false;
01210 QStringList newStateLabelList;
01211 foreach(const QString &partName, newStateNameList)
01212 {
01213 Q_ASSERT(automaton1->hasState(partName) || automaton2->hasState(partName));
01214 const QSharedPointer<IState> state =
01215 automaton1->hasState(partName) ? automaton1->getState(partName)
01216 : automaton2->getState(partName);
01217
01218 newStateIsFinal = newStateIsFinal || state->isFinal();
01219 newStateLabelList << state->getLabel();
01220 }
01221
01222 if (nameToNameListMap.values().contains(newStateNameList))
01223 {
01224 newState = automatonUnited->getState(nameToNameListMap.key(newStateNameList));
01225
01226 automatonUnited->createTransition(currentState->getName(), newState->getName(),
01227 (ITransition::TCharSet() << character));
01228
01229 continue;
01230 }
01231
01232 newStateName = chooseStateName(automatonUnited, newStateNameList, stateNum);
01233 newStateLabel = chooseStateLabel(newStateLabelList, stateNum);
01234
01235 newState = automatonUnited->createState(newStateName, newStateLabel, false, newStateIsFinal);
01236 stateNum++;
01237
01238 openedStates.enqueue(newState);
01239 nameToNameListMap[newStateName] = newStateNameList;
01240
01241 automatonUnited->createTransition(currentState->getName(), newStateName,
01242 (ITransition::TCharSet() << character));
01243 }
01244 }
01245
01246 result = automatonUnited;
01247
01248 addReport("Automata united sucessfully\n");
01249
01250 return true;
01251 }
01252
01253
01254
01255
01256
01257
01258
01259 IntersectionParallelAlgorithm::IntersectionParallelAlgorithm()
01260 : BasicAlgorithmWithSettings(false, true)
01261 {}
01262
01263 IntersectionParallelAlgorithm::~IntersectionParallelAlgorithm()
01264 {
01265 DBGLOG_ALGO("deleted");
01266 }
01267
01268 QString IntersectionParallelAlgorithm::getName() const
01269 {
01270 return "Intersect automata - parallel running";
01271 }
01272
01273 bool IntersectionParallelAlgorithm::runInternal(const IAutomaton::TAutomataList &input,
01274 QSharedPointer<IAutomaton> &result) const
01275 {
01276 if (input.count() < 2)
01277 {
01278 RELLOG("not enough automata on input");
01279 addReport("not enough automata on input - required 2");
01280 return false;
01281 }
01282
01283 QSharedPointer<IAutomaton> automaton1 = input[0];
01284 QSharedPointer<IAutomaton> automaton2 = input[1];
01285
01286 ITransition::TCharSet alphabet = automaton1->getAlphabet();
01287 if (automaton1->getAlphabet() != automaton2->getAlphabet())
01288 {
01289 if (!tryMergeAlphabet(automaton1->getAlphabet(), automaton2->getAlphabet(), alphabet))
01290 {
01291 addReport("inconsistent alphabets on input, alphabets have to be same or one has to be a subset of the other!");
01292 RELLOG("not same alphabets");
01293 return false;
01294 }
01295 }
01296
01297 if (automaton1->getAlphabetSymbol() != automaton2->getAlphabetSymbol() ||
01298 automaton1->getEpsilonSymbol() != automaton2->getEpsilonSymbol())
01299 {
01300 addReport("not same automata symbols settings, use same epsilon and alphabet symbol!");
01301 RELLOG("not same automata symbols settings");
01302 return false;
01303 }
01304
01305
01306 if (automaton1->hasEpsilonTransitions())
01307 if (!removeEpsilonTransitions(automaton1))
01308 return false;
01309 if (automaton2->hasEpsilonTransitions())
01310 if (!removeEpsilonTransitions(automaton2))
01311 return false;
01312
01313 if (automaton1->hasMultipleInitials())
01314 if (!removeMultipleInitials(automaton1))
01315 return false;
01316 if (automaton2->hasMultipleInitials())
01317 if (!removeMultipleInitials(automaton2))
01318 return false;
01319
01320 QSharedPointer<IAutomaton> automatonIntersect(m_creator->createAutomaton(alphabet,
01321 automaton1->getAlphabetSymbol(),
01322 automaton1->getEpsilonSymbol()));
01323
01324 Q_ASSERT(automaton1->getInitialStates().count() == 1);
01325 Q_ASSERT(automaton2->getInitialStates().count() == 1);
01326
01327 makeSureNamesUnique(automaton1, automaton2);
01328
01329
01330 QSharedPointer<IState> state1 = automaton1->getInitialStates()[0];
01331 QSharedPointer<IState> state2 = automaton2->getInitialStates()[0];
01332
01333 int stateNum = 0;
01334
01335 QStringList newStateNameList = QStringList() << state1->getName() << state2->getName();
01336 QString newStateName = chooseStateName(automatonIntersect, newStateNameList, stateNum);
01337 QString newStateLabel = chooseStateLabel((QStringList() << state1->getLabel() << state2->getLabel()), stateNum);
01338 QSharedPointer<IState> newState =
01339 automatonIntersect->createState(newStateName, newStateLabel, true, state1->isFinal() && state2->isFinal());
01340 stateNum++;
01341
01342 QQueue<QSharedPointer<IState> > openedStates;
01343 openedStates.enqueue(newState);
01344
01345 QMap<QString, QStringList> nameToNameListMap;
01346 nameToNameListMap[newState->getName()] = newStateNameList;
01347
01348 while (!openedStates.empty())
01349 {
01350 QSharedPointer<IState> currentState = openedStates.dequeue();
01351
01352 foreach(const QString &character, automatonIntersect->getAlphabet())
01353 {
01354 IState::TIStateNameSet newStateNameSet;
01355 QStringList origNameList = nameToNameListMap[currentState->getName()];
01356 foreach(const QString &stateName, origNameList)
01357 {
01358 Q_ASSERT(automaton1->hasState(stateName) || automaton2->hasState(stateName));
01359 QSharedPointer<IState> state =
01360 automaton1->hasState(stateName) ? automaton1->getState(stateName)
01361 : automaton2->getState(stateName);
01362
01363 newStateNameSet |= state->getStatesOn(character);
01364 }
01365
01366 if (newStateNameSet.empty()) continue;
01367
01368 newStateNameList = newStateNameSet.toList();
01369 qSort(newStateNameList);
01370
01371 unsigned char belongsTo = 0;
01372 bool newStateIsFinal = true;
01373 QStringList newStateLabelList;
01374 foreach(const QString &partName, newStateNameList)
01375 {
01376 Q_ASSERT(automaton1->hasState(partName) || automaton2->hasState(partName));
01377 QSharedPointer<IState> state;
01378 if (automaton1->hasState(partName))
01379 {
01380 state = automaton1->getState(partName);
01381 belongsTo |= 1;
01382 }
01383 else
01384 {
01385 state = automaton2->getState(partName);
01386 belongsTo |= 2;
01387 }
01388
01389 newStateIsFinal = newStateIsFinal && state->isFinal();
01390 newStateLabelList << state->getLabel();
01391 }
01392
01393 newStateIsFinal &= belongsTo == 3;
01394
01395 if (nameToNameListMap.values().contains(newStateNameList))
01396 {
01397 newState = automatonIntersect->getState(nameToNameListMap.key(newStateNameList));
01398
01399 automatonIntersect->createTransition(currentState->getName(), newState->getName(),
01400 (ITransition::TCharSet() << character));
01401
01402 continue;
01403 }
01404
01405 newStateName = chooseStateName(automatonIntersect, newStateNameList, stateNum);
01406 newStateLabel = chooseStateLabel(newStateLabelList, stateNum);
01407
01408
01409 newState = automatonIntersect->createState(newStateName, newStateLabel, false, newStateIsFinal);
01410 stateNum++;
01411
01412 openedStates.enqueue(newState);
01413 nameToNameListMap[newStateName] = newStateNameList;
01414
01415 automatonIntersect->createTransition(currentState->getName(), newStateName,
01416 (ITransition::TCharSet() << character));
01417 }
01418 }
01419
01420 result = automatonIntersect;
01421
01422 addReport("Automata intersected sucessfully\n");
01423
01424 return true;
01425 }
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435 AlgorithmHolder::AlgorithmHolder()
01436 {}
01437
01438 IAlgorithm::TAlgorithmList AlgorithmHolder::getAlgorithms() const
01439 {
01440 IAlgorithm::TAlgorithmList algorithmList;
01441
01442
01443 algorithmList << QSharedPointer<IAlgorithm>(new RemoveEpsilonAlgorithm());
01444 algorithmList << QSharedPointer<IAlgorithm>(new RemoveInaccessibleAlgorithm());
01445 algorithmList << QSharedPointer<IAlgorithm>(new RemoveUselessAlgorithm());
01446 algorithmList << QSharedPointer<IAlgorithm>(new RemoveMultipleInitialsAlgorithm());
01447
01448 algorithmList << QSharedPointer<IAlgorithm>(new DeterminizeAlgorithm());
01449 algorithmList << QSharedPointer<IAlgorithm>(new MinimalizeAlgorithm());
01450
01451
01452 algorithmList << QSharedPointer<IAlgorithm>(new UniteParallelAlgorithm());
01453 algorithmList << QSharedPointer<IAlgorithm>(new IntersectionParallelAlgorithm());
01454
01455 return algorithmList;
01456 }
01457
01458 AlgorithmHolder::~AlgorithmHolder()
01459 {
01460 DBGLOG_ALGO("deleted");
01461 }
01462
01463 Q_EXPORT_PLUGIN2(algorithms, AlgorithmHolder)