summaryrefslogtreecommitdiffstats
path: root/src/uscxml/interpreter/InterpreterDraft6.cpp
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2014-08-26 08:54:58 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2014-08-26 08:54:58 (GMT)
commitd90b15c96271d2afd45d649e3e22030004359d6e (patch)
treedb773868e71fd51fb05e5160e1138bec8087eac9 /src/uscxml/interpreter/InterpreterDraft6.cpp
parent93802c6eab1f798ccf57e863127aef6e7f9d073c (diff)
downloaduscxml-d90b15c96271d2afd45d649e3e22030004359d6e.zip
uscxml-d90b15c96271d2afd45d649e3e22030004359d6e.tar.gz
uscxml-d90b15c96271d2afd45d649e3e22030004359d6e.tar.bz2
API changes with receiveEvent
- receiveEvent will deliver to external queue now per default - switched Interpreter implementations to the new algorithm from spec
Diffstat (limited to 'src/uscxml/interpreter/InterpreterDraft6.cpp')
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.cpp533
1 files changed, 11 insertions, 522 deletions
diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp
index aedf516..7dcb768 100644
--- a/src/uscxml/interpreter/InterpreterDraft6.cpp
+++ b/src/uscxml/interpreter/InterpreterDraft6.cpp
@@ -33,460 +33,8 @@ using namespace Arabica::DOM;
// see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation
-InterpreterState InterpreterDraft6::interpret() {
- InterpreterState state;
- while(true) {
- state = step(-1);
-
- switch (state) {
- case uscxml::USCXML_FINISHED:
- case uscxml::USCXML_DESTROYED:
- // return as we finished
- return state;
- default:
-
- // process invokers on main thread
- if(_thread == NULL) {
- runOnMainThread(200);
- }
-
- // process next step
- break;
- }
- }
- return state;
-}
-
-// setup / fetch the documents initial transitions
-NodeSet<std::string> InterpreterDraft6::getDocumentInitialTransitions() {
- NodeSet<std::string> initialTransitions;
-
- if (_startConfiguration.size() > 0) {
- // we emulate entering a given configuration by creating a pseudo deep history
- Element<std::string> initHistory = _document.createElementNS(_nsInfo.nsURL, "history");
- _nsInfo.setPrefix(initHistory);
-
- initHistory.setAttribute("id", UUID::getUUID());
- initHistory.setAttribute("type", "deep");
- _scxml.insertBefore(initHistory, _scxml.getFirstChild());
-
- std::string histId = ATTR(initHistory, "id");
- NodeSet<std::string> histStates;
- for (std::list<std::string>::const_iterator stateIter = _startConfiguration.begin(); stateIter != _startConfiguration.end(); stateIter++) {
- histStates.push_back(getState(*stateIter));
- }
- _historyValue[histId] = histStates;
-
- Element<std::string> initialElem = _document.createElementNS(_nsInfo.nsURL, "initial");
- _nsInfo.setPrefix(initialElem);
-
- initialElem.setAttribute("generated", "true");
- Element<std::string> transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition");
- _nsInfo.setPrefix(transitionElem);
-
- transitionElem.setAttribute("target", histId);
- initialElem.appendChild(transitionElem);
- _scxml.appendChild(initialElem);
- initialTransitions.push_back(transitionElem);
-
- } else {
- // try to get initial transition from initial element
- initialTransitions = _xpath.evaluate("/" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", _scxml).asNodeSet();
- if (initialTransitions.size() == 0) {
- Arabica::XPath::NodeSet<std::string> initialStates;
- // fetch per draft
- initialStates = getInitialStates();
- assert(initialStates.size() > 0);
- for (int i = 0; i < initialStates.size(); i++) {
- Element<std::string> initialElem = _document.createElementNS(_nsInfo.nsURL, "initial");
- _nsInfo.setPrefix(initialElem);
-
- initialElem.setAttribute("generated", "true");
- Element<std::string> transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition");
- _nsInfo.setPrefix(transitionElem);
-
- transitionElem.setAttribute("target", ATTR_CAST(initialStates[i], "id"));
- initialElem.appendChild(transitionElem);
- _scxml.appendChild(initialElem);
- initialTransitions.push_back(transitionElem);
- }
- }
- }
- return initialTransitions;
-}
-
-InterpreterState InterpreterDraft6::step(int waitForMS = 0) {
- try {
- tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
-
- if (_state == USCXML_FINISHED || _state == USCXML_DESTROYED) {
- return _state;
- }
-
- NodeSet<std::string> enabledTransitions;
-
- // setup document and interpreter
- if (!_isInitialized) {
- init(); // will throw
- }
-
- if (_configuration.size() == 0) {
- // goto initial configuration
- NodeSet<std::string> initialTransitions = getDocumentInitialTransitions();
- assert(initialTransitions.size() > 0);
- enterStates(initialTransitions);
- setInterpreterState(USCXML_MICROSTEPPED);
- }
-
- assert(isLegalConfiguration(_configuration));
-
- // are there spontaneous transitions?
- if (!_stable) {
- enabledTransitions = selectEventlessTransitions();
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
-
- setInterpreterState(USCXML_MICROSTEPPED);
- return _state;
- }
- _stable = true;
- }
-
- // test415
- if (_topLevelFinalReached)
- goto EXIT_INTERPRETER;
-
- // process internal event
- if (!_internalQueue.empty()) {
- _currEvent = _internalQueue.front();
- _internalQueue.pop_front();
- _stable = false;
-
- USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
-
- _dataModel.setEvent(_currEvent);
- enabledTransitions = selectTransitions(_currEvent.name);
-
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
- }
-
- // test 319 - even if we do not enable transitions, consider it a microstep
- setInterpreterState(USCXML_MICROSTEPPED);
- return _state;
-
- } else {
- _stable = true;
- }
-
- if (_state != USCXML_MACROSTEPPED && _state != USCXML_IDLE)
- USCXML_MONITOR_CALLBACK(onStableConfiguration)
-
- setInterpreterState(USCXML_MACROSTEPPED);
-
- if (_topLevelFinalReached)
- goto EXIT_INTERPRETER;
-
-
- // when we reach a stable configuration, invoke
- for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
- NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = Element<std::string>(invokes[j]);
- if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) {
- invoke(invokeElem);
- }
- }
- }
- _statesToInvoke = NodeSet<std::string>();
-
- if (_externalQueue.isEmpty()) {
- setInterpreterState(USCXML_IDLE);
-
- if (waitForMS < 0) {
- // wait blockingly for an event forever
- while(_externalQueue.isEmpty()) {
- _condVar.wait(_mutex);
- }
- }
-
- if (waitForMS > 0) {
- // wait given number of milliseconds max
- uint64_t now = tthread::chrono::system_clock::now();
- uint64_t then = now + waitForMS;
- while(_externalQueue.isEmpty() && now < then) {
- _condVar.wait_for(_mutex, then - now);
- now = tthread::chrono::system_clock::now();
- }
- }
-
- if (_externalQueue.isEmpty()) {
- return _state;
- }
-
- setInterpreterState(USCXML_MACROSTEPPED);
- }
-
- _currEvent = _externalQueue.pop();
- _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external
- _stable = false;
-
- if (_topLevelFinalReached)
- goto EXIT_INTERPRETER;
-
- USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
-
- if (iequals(_currEvent.name, "cancel.invoke." + _sessionId)) {
- goto EXIT_INTERPRETER;
- }
-
- try {
- _dataModel.setEvent(_currEvent);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent;
- }
-
- finalizeAndAutoForwardCurrentEvent();
-
- // run internal processing until we reach a stable configuration again
- enabledTransitions = selectTransitions(_currEvent.name);
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
- }
-
- if (_topLevelFinalReached)
- goto EXIT_INTERPRETER;
-
- return _state;
-
-EXIT_INTERPRETER:
- USCXML_MONITOR_CALLBACK(beforeCompletion)
-
- exitInterpreter();
- if (_sendQueue) {
- std::map<std::string, std::pair<InterpreterImpl*, SendRequest> >::iterator sendIter = _sendIds.begin();
- while(sendIter != _sendIds.end()) {
- _sendQueue->cancelEvent(sendIter->first);
- sendIter++;
- }
- }
-
- USCXML_MONITOR_CALLBACK(afterCompletion)
-
-// assert(hasLegalConfiguration());
- _mutex.unlock();
-
- // remove datamodel
- if(!_userSuppliedDataModel)
- _dataModel = DataModel();
-
- setInterpreterState(USCXML_FINISHED);
- return _state;
- } catch (boost::bad_weak_ptr e) {
- LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl;
- setInterpreterState(USCXML_DESTROYED);
- return _state;
- }
-
- // set datamodel to null from this thread
- if(_dataModel)
- _dataModel = DataModel();
-
-}
-
-// process transitions until we are in a stable configuration again
-void InterpreterDraft6::stabilize() {
-
- NodeSet<std::string> enabledTransitions;
- _stable = false;
-
- if (_configuration.size() == 0) {
- // goto initial configuration
- NodeSet<std::string> initialTransitions = getDocumentInitialTransitions();
- assert(initialTransitions.size() > 0);
- enterStates(initialTransitions);
- }
-
- do { // process microsteps for enabled transitions until there are no more left
-
- enabledTransitions = selectEventlessTransitions();
-
- if (enabledTransitions.size() == 0) {
- if (_internalQueue.size() == 0) {
- _stable = true;
- } else {
- _currEvent = _internalQueue.front();
- _internalQueue.pop_front();
-#if VERBOSE
- std::cout << "Received internal event " << _currEvent.name << std::endl;
-#endif
-
- USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
-
- if (_dataModel)
- _dataModel.setEvent(_currEvent);
- enabledTransitions = selectTransitions(_currEvent.name);
- }
- }
-
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
- }
- } while(!_internalQueue.empty() || !_stable);
-
- USCXML_MONITOR_CALLBACK(onStableConfiguration)
-
- // when we reach a stable configuration, invoke
- for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
- NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = Element<std::string>(invokes[j]);
- if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) {
- invoke(invokeElem);
- }
- }
- }
- _statesToInvoke = NodeSet<std::string>();
-
-}
-
-Arabica::XPath::NodeSet<std::string> InterpreterDraft6::selectTransitions(const std::string& event) {
- Arabica::XPath::NodeSet<std::string> enabledTransitions;
-
- NodeSet<std::string> states;
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- if (isAtomic(Element<std::string>(_configuration[i])))
- states.push_back(_configuration[i]);
- }
- states.to_document_order();
-
-#if 0
- std::cout << "Atomic states: " << std::endl;
- for (int i = 0; i < states.size(); i++) {
- std::cout << states[i] << std::endl << "----" << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- unsigned int index = 0;
- while(states.size() > index) {
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- if (isEnabledTransition(Element<std::string>(transitions[k]), event)) {
- enabledTransitions.push_back(transitions[k]);
- goto LOOP;
- }
- }
- {
- Node<std::string> parent = states[index].getParentNode();
- if (parent) {
- states.push_back(parent);
- }
- }
-LOOP:
- index++;
- }
- enabledTransitions = filterPreempted(enabledTransitions);
-
-#if 0
- std::cout << "Enabled transitions: " << std::endl;
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << DOMUtils::xPathForNode(enabledTransitions[i]) << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- return enabledTransitions;
-}
-
-bool InterpreterDraft6::isEnabledTransition(const Element<std::string>& transition, const std::string& event) {
- std::string eventName;
- if (HAS_ATTR(transition, "event")) {
- eventName = ATTR(transition, "event");
- } else if(HAS_ATTR(transition, "eventexpr")) {
- if (_dataModel) {
- eventName = _dataModel.evalAsString(ATTR(transition, "eventexpr"));
- } else {
- LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined";
- return false;
- }
- } else {
- return false;
- }
-
- std::list<std::string> eventNames = tokenizeIdRefs(eventName);
- std::list<std::string>::iterator eventIter = eventNames.begin();
- while(eventIter != eventNames.end()) {
- if(nameMatch(*eventIter, event) && hasConditionMatch(transition)) {
- return true;
- }
- eventIter++;
- }
- return false;
-}
-
-Arabica::XPath::NodeSet<std::string> InterpreterDraft6::selectEventlessTransitions() {
- Arabica::XPath::NodeSet<std::string> enabledTransitions;
-
- NodeSet<std::string> states;
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- if (isAtomic(Element<std::string>(_configuration[i])))
- states.push_back(_configuration[i]);
- }
- states.to_document_order();
-
-#if 0
- std::cout << "Atomic States: ";
- for (int i = 0; i < atomicStates.size(); i++) {
- std::cout << ATTR(atomicStates[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- unsigned int index = 0;
- while(states.size() > index) {
- bool foundTransition = false;
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- Element<std::string> transElem(transitions[k]);
- if (!HAS_ATTR(transElem, "event") && hasConditionMatch(transElem)) {
- enabledTransitions.push_back(transitions[k]);
- foundTransition = true;
- goto LOOP;
- }
- }
- if (!foundTransition) {
- Node<std::string> parent = states[index].getParentNode();
- if (parent) {
- states.push_back(parent);
- }
- }
-LOOP:
- index++;
- }
-
-#if 0
- std::cout << "Enabled eventless transitions: " << std::endl;
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << enabledTransitions[i] << std::endl << "----" << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- enabledTransitions = filterPreempted(enabledTransitions);
- return enabledTransitions;
-}
-
-Arabica::XPath::NodeSet<std::string> InterpreterDraft6::filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+Arabica::XPath::NodeSet<std::string> InterpreterDraft6::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
Arabica::XPath::NodeSet<std::string> filteredTransitions;
for (unsigned int i = 0; i < enabledTransitions.size(); i++) {
Element<std::string> t(enabledTransitions[i]);
@@ -566,7 +114,6 @@ bool InterpreterDraft6::isWithinParallel(const Element<std::string>& transition)
_transWithinParallel[transition] = lcpa;
return _transWithinParallel[transition];
-
}
Node<std::string> InterpreterDraft6::findLCPA(const Arabica::XPath::NodeSet<std::string>& states) {
@@ -587,64 +134,6 @@ NEXT_ANCESTOR:
return ancestor;
}
-void InterpreterDraft6::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
-#if VERBOSE
- std::cout << "Transitions: ";
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << ((Element<std::string>)getSourceState(enabledTransitions[i])).getAttribute("id") << " -> " << std::endl;
- NodeSet<std::string> targetSet = getTargetStates(enabledTransitions[i]);
- for (int j = 0; j < targetSet.size(); j++) {
- std::cout << " " << ((Element<std::string>)targetSet[j]).getAttribute("id") << std::endl;
- }
- }
- std::cout << std::endl;
-#endif
-
- USCXML_MONITOR_CALLBACK(beforeMicroStep)
-
- exitStates(enabledTransitions);
-
- for (int i = 0; i < enabledTransitions.size(); i++) {
- Element<std::string> transition(enabledTransitions[i]);
-
- USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size()))
-
- executeContent(transition);
-
- USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size()))
- }
-
- enterStates(enabledTransitions);
-
- USCXML_MONITOR_CALLBACK(afterMicroStep)
-
-}
-
-void InterpreterDraft6::exitInterpreter() {
-#if VERBOSE
- std::cout << "Exiting interpreter " << _name << std::endl;
-#endif
- NodeSet<std::string> statesToExit = _configuration;
- statesToExit.forward(false);
- statesToExit.sort();
-
- for (int i = 0; i < statesToExit.size(); i++) {
- Arabica::XPath::NodeSet<std::string> onExitElems = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", statesToExit[i]);
- for (int j = 0; j < onExitElems.size(); j++) {
- executeContent(Element<std::string>(onExitElems[j]));
- }
- Arabica::XPath::NodeSet<std::string> invokeElems = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]);
- // TODO: we ought to cancel all remaining invokers just to be sure with the persist extension
- for (int j = 0; j < invokeElems.size(); j++) {
- cancelInvoke(Element<std::string>(invokeElems[j]));
- }
- Element<std::string> stateElem(statesToExit[i]);
- if (isFinal(stateElem) && parentIsScxmlState(stateElem)) {
- returnDoneEvent(statesToExit[i]);
- }
- }
- _configuration = NodeSet<std::string>();
-}
void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
@@ -737,7 +226,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e
#if VERBOSE
std::cout << _name << ": History node " << ATTR(historyElem, "id") << " contains: ";
for (int i = 0; i < historyNodes.size(); i++) {
- std::cout << ATTR(historyNodes[i], "id") << ", ";
+ std::cout << ATTR_CAST(historyNodes[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -801,7 +290,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
#if VERBOSE
std::cout << _name << ": Target States: ";
for (int i = 0; i < tStates.size(); i++) {
- std::cout << ATTR(tStates[i], "id") << ", ";
+ std::cout << ATTR_CAST(tStates[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -809,7 +298,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
Node<std::string> ancestor;
Node<std::string> source = getSourceState(transition);
#if VERBOSE
- std::cout << _name << ": Source States: " << ATTR(source, "id") << std::endl;
+ std::cout << _name << ": Source States: " << ATTR_CAST(source, "id") << std::endl;
#endif
assert(source);
@@ -833,7 +322,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
}
#if VERBOSE
- std::cout << _name << ": Ancestor: " << ATTR(ancestor, "id") << std::endl;
+ std::cout << _name << ": Ancestor: " << ATTR_CAST(ancestor, "id") << std::endl;
#endif
for (int j = 0; j < tStates.size(); j++) {
@@ -843,7 +332,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
#if VERBOSE
std::cout << _name << ": States to enter: ";
for (int i = 0; i < statesToEnter.size(); i++) {
- std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR(statesToEnter[i], "id") << ", ";
+ std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR_CAST(statesToEnter[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -852,9 +341,9 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
NodeSet<std::string> ancestors = getProperAncestors(tStates[j], ancestor);
#if VERBOSE
- std::cout << _name << ": Proper Ancestors of " << ATTR(tStates[j], "id") << " and " << ATTR(ancestor, "id") << ": ";
+ std::cout << _name << ": Proper Ancestors of " << ATTR_CAST(tStates[j], "id") << " and " << ATTR_CAST(ancestor, "id") << ": ";
for (int i = 0; i < ancestors.size(); i++) {
- std::cout << ATTR(ancestors[i], "id") << ", ";
+ std::cout << ATTR_CAST(ancestors[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -885,7 +374,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
#if VERBOSE
std::cout << _name << ": States to enter: ";
for (int i = 0; i < statesToEnter.size(); i++) {
- std::cout << ATTR(statesToEnter[i], "id") << ", ";
+ std::cout << ATTR_CAST(statesToEnter[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -998,7 +487,7 @@ void InterpreterDraft6::addStatesToEnter(const Element<std::string>& state,
#if VERBOSE
std::cout << "History State " << ATTR(state, "id") << ": ";
for (int i = 0; i < historyValue.size(); i++) {
- std::cout << ATTR(historyValue[i], "id") << ", ";
+ std::cout << ATTR_CAST(historyValue[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -1010,7 +499,7 @@ void InterpreterDraft6::addStatesToEnter(const Element<std::string>& state,
#if VERBOSE
std::cout << "Proper Ancestors: ";
for (int i = 0; i < ancestors.size(); i++) {
- std::cout << ATTR(ancestors[i], "id") << ", ";
+ std::cout << ATTR_CAST(ancestors[i], "id") << ", ";
}
std::cout << std::endl;
#endif