4141#ifndef MAXPLUS_BASE_FSM_FSM_H
4242#define MAXPLUS_BASE_FSM_FSM_H
4343
44+ #include " base/fsm/fsm.h"
4445#include " maxplus/base/basic_types.h"
4546#include " maxplus/base/exception/exception.h"
4647#include " maxplus/base/string/cstring.h"
@@ -150,6 +151,11 @@ class State : public WithUniqueID {
150151 return this ->outgoingEdges ;
151152 }
152153
154+ // as a reference
155+ [[nodiscard]] virtual const StateRef getReference () const {
156+ return this ;
157+ }
158+
153159 void insertOutgoingEdge (Edge &e) { this ->outgoingEdges .insert (&e); }
154160
155161 void removeOutgoingEdge (EdgeRef e) { this ->outgoingEdges .erase (e); }
@@ -174,13 +180,20 @@ struct StateRefCompareLessThan {
174180};
175181
176182// A set of references to states
177- class SetOfStateRefs : public std ::set<const State * , StateRefCompareLessThan> {
183+ class SetOfStateRefs : public std ::set<StateRef , StateRefCompareLessThan> {
178184public:
179- using CIter = SetOfEdgeRefs ::const_iterator;
185+ using CIter = SetOfStateRefs ::const_iterator;
180186 bool includesState (const State *s) { return this ->find (s) != this ->end (); }
181187 virtual ~SetOfStateRefs () = default ;
182188};
183189
190+ // A list of references to states
191+ class ListOfStateRefs : public std ::list<StateRef> {
192+ public:
193+ using CIter = ListOfStateRefs::const_iterator;
194+ virtual ~ListOfStateRefs () = default ;
195+ };
196+
184197// forward declaration of reachable states strategy
185198class ReachableStates ;
186199
@@ -199,8 +212,10 @@ class FiniteStateMachine {
199212 [[nodiscard]] virtual StateRef getInitialState () const = 0;
200213 [[nodiscard]] virtual const SetOfStateRefs &getInitialStates () const = 0;
201214 [[nodiscard]] virtual const SetOfStateRefs &getFinalStates () const = 0;
215+ [[nodiscard]] virtual const SetOfStates &getStates () const = 0;
202216};
203217
218+
204219//
205220// A generic DFS strategy on the target FSM
206221// overwrite the methods onEnterState, onLeaveState, onTransition and onSimpleCycle with
@@ -223,9 +238,9 @@ class DepthFirstSearch {
223238 };
224239
225240 // access state
226- inline const StateRef getState () { return this ->state ; }
241+ StateRef getState () { return this ->state ; }
227242
228- inline SetOfEdgeRefs::CIter getIter () { return this ->iter ; }
243+ SetOfEdgeRefs::CIter getIter () { return this ->iter ; }
229244
230245 // test if all outgoing edges have been done
231246 bool atEnd () { return this ->iter == this ->state ->getOutgoingEdges ().end (); }
@@ -238,7 +253,7 @@ class DepthFirstSearch {
238253 SetOfEdgeRefs::CIter iter;
239254 };
240255
241- private :
256+ protected :
242257 using DfsStack = std::list<DFSStackItem>;
243258 DfsStack dfsStack;
244259
@@ -257,12 +272,14 @@ class DepthFirstSearch {
257272 explicit DepthFirstSearch (FiniteStateMachine &targetFsm) : fsm(targetFsm){};
258273
259274 // Execute the depth first search
260- void DoDepthFirstSearch (bool fullDFS = false ) {
275+ void DoDepthFirstSearch (const StateRef &startingState, bool fullDFS = false ) {
261276 // store visited states
262277 SetOfStateRefs visitedStates;
278+ SetOfStateRefs statesOnStack;
263279
264280 // put initial state on the stack
265- dfsStack.emplace_back (this ->fsm .getInitialState ());
281+ dfsStack.emplace_back (startingState);
282+ this ->onEnterState (startingState);
266283
267284 while (!(dfsStack.empty ())) {
268285 DFSStackItem &si = dfsStack.back ();
@@ -271,35 +288,98 @@ class DepthFirstSearch {
271288 if (si.atEnd ()) {
272289 // pop it from stack
273290 this ->onLeaveState (si.getState ());
291+ const auto *const s = si.getState ();
292+ statesOnStack.erase (s);
274293 if (fullDFS) {
275- const auto s = si.getState ();
276294 assert (visitedStates.includesState (s));
277295 visitedStates.erase (s);
278296 }
279297 dfsStack.pop_back ();
280298 } else {
281299 // goto next edge
282- auto *e = *(si.getIter ());
300+ const auto *e = *(si.getIter ());
283301 si.advance ();
284- bool revisit = visitedStates.includesState (e->getDestination ());
302+ StateRef dest = e->getDestination ();
303+ bool revisit = statesOnStack.includesState (dest);
285304 if (revisit) {
286- // if target state not visited before
287- dfsStack.emplace_back (e->getDestination ());
288- this ->onTransition (*e);
289- this ->onEnterState (e->getDestination ());
290- visitedStates.insert (e->getDestination ());
291- } else {
292305 // cycle found
293306 this ->onSimpleCycle (dfsStack);
307+ } else {
308+ // if target state not visited before
309+ dfsStack.emplace_back (dest);
310+ this ->onTransition (*e);
311+ this ->onEnterState (dest);
312+ visitedStates.insert (dest);
313+ statesOnStack.insert (dest);
294314 }
295315 }
296316 }
297317 }
298318
299- private:
319+ // Execute the depth first search
320+ void DoDepthFirstSearch (bool fullDFS = false ) {
321+ this ->DoDepthFirstSearch (this ->fsm .getInitialState (), fullDFS);
322+ }
323+
324+ protected:
300325 FiniteStateMachine &fsm;
301326};
302327
328+ // Check for cycles
329+ class DetectCycle : public DepthFirstSearch {
330+ public:
331+ bool hasCycle = false ;
332+
333+ explicit DetectCycle (FiniteStateMachine &targetFsm) : DepthFirstSearch(targetFsm){};
334+
335+ ~DetectCycle () override = default ;
336+
337+ DetectCycle (const DetectCycle &) = delete ;
338+ DetectCycle &operator =(const DetectCycle &other) = delete ;
339+ DetectCycle (DetectCycle &&) = delete ;
340+ DetectCycle &operator =(DetectCycle &&) = delete ;
341+
342+ bool checkForCycles () {
343+ return this ->checkForCycles (nullptr );
344+ }
345+
346+ bool checkForCycles (ListOfStateRefs *cycle) {
347+ this ->visitedStates .clear ();
348+ this ->cycle = cycle;
349+ const SetOfStates &states = this ->fsm .getStates ();
350+ auto nextStartingState = states.begin ();
351+ while (nextStartingState != states.end ()) {
352+ this ->DoDepthFirstSearch ((*nextStartingState).second ->getReference ());
353+ if (this ->hasCycle ) {
354+ return true ;
355+ }
356+ while (nextStartingState != states.end () && this ->visitedStates .includesState ((*nextStartingState).second ->getReference ())) {
357+ nextStartingState++;
358+ }
359+ }
360+ return false ;
361+ }
362+
363+ private:
364+ SetOfStateRefs visitedStates;
365+ ListOfStateRefs *cycle = nullptr ;
366+
367+ void onEnterState (StateRef s) override {
368+ this ->visitedStates .insert (s);
369+ }
370+
371+ void onSimpleCycle (DfsStack &stack) override {
372+ if (!this ->hasCycle ) {
373+ if (this ->cycle != nullptr ) {
374+ for (auto si : stack) {
375+ this ->cycle ->push_back (si.getState ());
376+ }
377+ }
378+ this ->hasCycle = true ;
379+ }
380+ }
381+ };
382+
303383// Reachable states strategy based on DFS
304384class ReachableStates : public DepthFirstSearch {
305385public:
@@ -573,7 +653,7 @@ class FiniteStateMachine : public Abstract::FiniteStateMachine {
573653 return false ;
574654 };
575655
576- [[nodiscard]] const SetOfStates<StateLabelType, EdgeLabelType> &getStates () const {
656+ [[nodiscard]] const SetOfStates<StateLabelType, EdgeLabelType> &getStates () const override {
577657 return this ->states ;
578658 };
579659 Abstract::SetOfStateRefs getStateRefs () {
@@ -871,7 +951,7 @@ class FiniteStateMachine : public Abstract::FiniteStateMachine {
871951 const auto *s = *(cli->begin ());
872952 auto es = s->getOutgoingEdges ();
873953 // for every outgoing edge
874- for (auto *edi : es) {
954+ for (const auto *edi : es) {
875955 auto ed = dynamic_cast <EdgeRef<StateLabelType, EdgeLabelType>>(edi);
876956 result->addEdge (*(newStateMap[cli]),
877957 ed->getLabel (),
@@ -885,6 +965,11 @@ class FiniteStateMachine : public Abstract::FiniteStateMachine {
885965 return result;
886966 }
887967
968+ bool hasDirectedCycle () {
969+ FSM::Abstract::DetectCycle DC (*this );
970+ return DC.checkForCycles (nullptr );
971+ }
972+
888973private:
889974 void insertOutgoingLabels (const State<StateLabelType, EdgeLabelType> *s,
890975 std::set<EdgeLabelType> &labels) {
0 commit comments