diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2015-01-19 16:41:18 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2015-01-19 16:41:18 (GMT) |
commit | ff86d690dc02d7dd495000331d378e7d8eb688ac (patch) | |
tree | 5214786f7e575952d3cba0919e5071f3a783050b /src | |
parent | 42437db418574f2a80d098e568b9498a21343800 (diff) | |
download | uscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.zip uscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.tar.gz uscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.tar.bz2 |
Plenty of smaller fixes and adaptations
Diffstat (limited to 'src')
29 files changed, 4416 insertions, 1137 deletions
diff --git a/src/bindings/swig/csharp/uscxml.i b/src/bindings/swig/csharp/uscxml.i index e218bf4..18b8cb8 100644 --- a/src/bindings/swig/csharp/uscxml.i +++ b/src/bindings/swig/csharp/uscxml.i @@ -36,12 +36,18 @@ typedef uscxml::InterpreterIssue InterpreterIssue; %feature("director") uscxml::WrappedExecutableContent; %feature("director") uscxml::WrappedInterpreterMonitor; -// disable warning related to unknown base class +// disable warnings + +// unknown base class #pragma SWIG nowarn=401 -// do not warn when we override symbols via extend +// override symbols via extend #pragma SWIG nowarn=302 -// do not warn when ignoring overrided method +// ignoring overrided method #pragma SWIG nowarn=516 +// pointer from director +#pragma SWIG nowarn=473 +// renamed params -> _params +#pragma SWIG nowarn=314 %csconst(1); @@ -160,6 +166,9 @@ WRAP_TO_STRING(uscxml::InterpreterIssue); %include "../uscxml_ignores.i" +// InterpreterMonitor -> StateTransitionMonitor +%ignore uscxml::StateTransitionMonitor; + //*********************************************** // Beautify important classes //*********************************************** diff --git a/src/uscxml/Common.h b/src/uscxml/Common.h index c261301..85729f2 100644 --- a/src/uscxml/Common.h +++ b/src/uscxml/Common.h @@ -20,6 +20,17 @@ #ifndef COMMON_H_YZ3CIYP #define COMMON_H_YZ3CIYP +#if __cplusplus >= 201402L +#define DEPRECATED [[deprecated]] +#elif defined(__GNUC__) +#define DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define DEPRECATED __declspec(deprecated) +#else +#pragma message("WARNING: You need to implement DEPRECATED for this compiler") +#define DEPRECATED(alternative) +#endif + #if defined(_WIN32) && !defined(USCXML_STATIC) # ifdef USCXML_EXPORT # define USCXML_API __declspec(dllexport) @@ -41,6 +52,19 @@ #include <sys/socket.h> #endif +#if defined(_WIN32) +inline int setenv(const char *name, const char *value, int overwrite) +{ + int errcode = 0; + if(!overwrite) { + size_t envsize = 0; + errcode = getenv_s(&envsize, NULL, 0, name); + if(errcode || envsize) return errcode; + } + return _putenv_s(name, value); +} +#endif + #define _USE_MATH_DEFINES #include <cmath> diff --git a/src/uscxml/Convenience.cpp b/src/uscxml/Convenience.cpp new file mode 100644 index 0000000..6013575 --- /dev/null +++ b/src/uscxml/Convenience.cpp @@ -0,0 +1,128 @@ +/** + * @file + * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include <inttypes.h> +#include <stdlib.h> +#include <boost/detail/endian.hpp> +#include <boost/lexical_cast.hpp> + +namespace uscxml { +bool isnan(double x) { + return x != x; +} + +bool isNumeric(const char* pszInput, int nNumberBase) { + std::string base = ".-0123456789ABCDEF"; + std::string input = pszInput; + return (input.find_first_not_of(base.substr(0, nNumberBase + 2)) == std::string::npos); +} + +bool isInteger(const char* pszInput, int nNumberBase) { + std::string base = "-0123456789ABCDEF"; + std::string input = pszInput; + return (input.find_first_not_of(base.substr(0, nNumberBase + 1)) == std::string::npos); +} + +bool iequals(const std::string& a, const std::string& b) { + // this impementation beats boost::iequals 2700ms vs 2100ms for test-performance.scxml - we don't care for non-ascii yet + unsigned int size = a.size(); + if (b.size() != size) + return false; + for (unsigned int i = 0; i < size; ++i) + if (tolower(a[i]) != tolower(b[i])) + return false; + return true; +} + +bool equals(const std::string& a, const std::string& b) { + unsigned int size = a.size(); + if (b.size() != size) + return false; + for (unsigned int i = 0; i < size; ++i) + if (a[i] != b[i]) + return false; + return true; +} + +bool stringIsTrue(const std::string& value) { + return (iequals(value, "on") || + iequals(value, "true") || + iequals(value, "1") || + iequals(value, "yes")); +} + +bool envVarIsTrue(const char* name) { + const char* value = getenv(name); + if (value == NULL) + return false; + return stringIsTrue(value); +} + +bool envVarIEquals(const char* name, const char* value) { + const char* envVarValue = getenv(name); + if (envVarValue == NULL) + return false; + return iequals(envVarValue, value); +} + +std::string unescape(const std::string& a) { + std::stringstream b; + // see http://en.cppreference.com/w/cpp/language/escape + + std::string::const_iterator it = a.begin(); + while (it != a.end()) { + char c = *it++; + if (c == '\\' && it != a.end()) { + switch (*it++) { + case '\\': + c = '\\'; + break; + case '0': + c = '\0'; + break; + case 'a': + c = '\a'; + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + } + } + b << c; + } + + return b.str(); +} + +} diff --git a/src/uscxml/Convenience.h b/src/uscxml/Convenience.h index 86a0b52..cbc38d9 100644 --- a/src/uscxml/Convenience.h +++ b/src/uscxml/Convenience.h @@ -21,14 +21,13 @@ #define CONVENIENCE_H_LU7GZ6CB #include <inttypes.h> +#include <stdlib.h> #include <boost/detail/endian.hpp> #include <boost/lexical_cast.hpp> namespace uscxml { -inline bool isnan(double x) { - return x != x; -} - +inline bool isnan(double x); + // see http://stackoverflow.com/questions/228005/alternative-to-itoa-for-converting-integer-to-string-c template <typename T> std::string toStr(T tmp) { std::ostringstream out; @@ -44,82 +43,15 @@ template <typename T> T strTo(std::string tmp) { return output; } -inline bool isNumeric( const char* pszInput, int nNumberBase) { - std::string base = ".-0123456789ABCDEF"; - std::string input = pszInput; - return (input.find_first_not_of(base.substr(0, nNumberBase + 2)) == std::string::npos); -} - -inline bool isInteger( const char* pszInput, int nNumberBase) { - std::string base = "-0123456789ABCDEF"; - std::string input = pszInput; - return (input.find_first_not_of(base.substr(0, nNumberBase + 1)) == std::string::npos); -} - -inline bool iequals(const std::string& a, const std::string& b) { - // this impementation beats boost::iequals 2700ms vs 2100ms for test-performance.scxml - we don't care for non-ascii yet - unsigned int size = a.size(); - if (b.size() != size) - return false; - for (unsigned int i = 0; i < size; ++i) - if (tolower(a[i]) != tolower(b[i])) - return false; - return true; -} +bool isNumeric(const char* pszInput, int nNumberBase); +bool isInteger( const char* pszInput, int nNumberBase); +bool iequals(const std::string& a, const std::string& b); +bool equals(const std::string& a, const std::string& b); +bool stringIsTrue(const std::string& value); +bool envVarIsTrue(const char* name); +bool envVarIEquals(const char* name, const char* value); -inline bool equals(const std::string& a, const std::string& b) { - unsigned int size = a.size(); - if (b.size() != size) - return false; - for (unsigned int i = 0; i < size; ++i) - if (a[i] != b[i]) - return false; - return true; -} - -inline std::string unescape(const std::string& a) { - std::stringstream b; - // see http://en.cppreference.com/w/cpp/language/escape - - std::string::const_iterator it = a.begin(); - while (it != a.end()) { - char c = *it++; - if (c == '\\' && it != a.end()) { - switch (*it++) { - case '\\': - c = '\\'; - break; - case '0': - c = '\0'; - break; - case 'a': - c = '\a'; - break; - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - } - } - b << c; - } - - return b.str(); -} +std::string unescape(const std::string& a); // see http://www.cplusplus.com/forum/general/27544/ diff --git a/src/uscxml/DOMUtils.cpp b/src/uscxml/DOMUtils.cpp index fa58759..2d3c3ea 100644 --- a/src/uscxml/DOMUtils.cpp +++ b/src/uscxml/DOMUtils.cpp @@ -26,12 +26,8 @@ namespace uscxml { - bool DOMUtils::attributeIsTrue(const::std::string& value) { - return (iequals(value, "on") || - iequals(value, "true") || - iequals(value, "1") || - iequals(value, "yes")); + return stringIsTrue(value.c_str()); } std::string DOMUtils::xPathForNode(const Arabica::DOM::Node<std::string>& node, const std::string& ns) { diff --git a/src/uscxml/DOMUtils.h b/src/uscxml/DOMUtils.h index 7fd291c..2264e64 100644 --- a/src/uscxml/DOMUtils.h +++ b/src/uscxml/DOMUtils.h @@ -44,7 +44,8 @@ class USCXML_API DOMUtils { public: static std::string xPathForNode(const Arabica::DOM::Node<std::string>& node, const std::string& ns = ""); static std::list<Arabica::DOM::Node<std::string> > getElementsByType(const Arabica::DOM::Node<std::string>& root, Arabica::DOM::Node_base::Type type); - static bool attributeIsTrue(const::std::string& value); + // deprecated, use stringIsTrue from Convenience.h instead + DEPRECATED static bool attributeIsTrue(const::std::string& value); }; class USCXML_API NumAttr { diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 6888d0e..060a397 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -330,29 +330,39 @@ void NameSpaceInfo::init(const std::map<std::string, std::string>& namespaceInfo } void StateTransitionMonitor::beforeTakingTransition(uscxml::Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing) { - std::cout << "Transition: " << uscxml::DOMUtils::xPathForNode(transition) << std::endl; + std::cerr << "Transition: " << uscxml::DOMUtils::xPathForNode(transition) << std::endl; } void StateTransitionMonitor::onStableConfiguration(uscxml::Interpreter interpreter) { - std::cout << "Config: {"; + std::cerr << "Config: {"; printNodeSet(interpreter.getConfiguration()); - std::cout << "}" << std::endl; + std::cerr << "}" << std::endl; } void StateTransitionMonitor::beforeProcessingEvent(uscxml::Interpreter interpreter, const uscxml::Event& event) { - std::cout << "Event: " << event.name << std::endl; + switch (event.eventType) { + case uscxml::Event::INTERNAL: + std::cerr << "Internal Event: " << event.name << std::endl; + break; + case uscxml::Event::EXTERNAL: + std::cerr << "External Event: " << event.name << std::endl; + break; + case uscxml::Event::PLATFORM: + std::cerr << "Platform Event: " << event.name << std::endl; + break; + } } void StateTransitionMonitor::beforeExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) { - std::cout << "Executable Content: " << DOMUtils::xPathForNode(element) << std::endl; + std::cerr << "Executable Content: " << DOMUtils::xPathForNode(element) << std::endl; } void StateTransitionMonitor::beforeExitingState(uscxml::Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { exitingStates.push_back(state); if (!moreComing) { - std::cout << "Exiting: {"; + std::cerr << "Exiting: {"; printNodeSet(exitingStates); - std::cout << "}" << std::endl; + std::cerr << "}" << std::endl; exitingStates = Arabica::XPath::NodeSet<std::string>(); } } @@ -360,9 +370,9 @@ void StateTransitionMonitor::beforeExitingState(uscxml::Interpreter interpreter, void StateTransitionMonitor::beforeEnteringState(uscxml::Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { enteringStates.push_back(state); if (!moreComing) { - std::cout << "Entering: {"; + std::cerr << "Entering: {"; printNodeSet(enteringStates); - std::cout << "}" << std::endl; + std::cerr << "}" << std::endl; enteringStates = Arabica::XPath::NodeSet<std::string>(); } @@ -371,7 +381,7 @@ void StateTransitionMonitor::beforeEnteringState(uscxml::Interpreter interpreter void StateTransitionMonitor::printNodeSet(const Arabica::XPath::NodeSet<std::string>& config) { std::string seperator; for (int i = 0; i < config.size(); i++) { - std::cout << seperator << ATTR_CAST(config[i], "id"); + std::cerr << seperator << ATTR_CAST(config[i], "id"); seperator = ", "; } } @@ -577,7 +587,7 @@ InterpreterImpl::~InterpreterImpl() { tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); stop(); // unset started bit } -// std::cout << "stopped " << this << std::endl; +// std::cerr << "stopped " << this << std::endl; // tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); if (_thread) { if (_thread->get_id() != tthread::this_thread::get_id()) { @@ -774,11 +784,11 @@ InterpreterState InterpreterImpl::step(int waitForMS) { NodeSet<std::string> initialTransitions = getDocumentInitialTransitions(); assert(initialTransitions.size() > 0); #if VERBOSE - std::cout << _name << ": initialTransitions: " << std::endl; + std::cerr << _name << ": initialTransitions: " << std::endl; for (int i = 0; i < initialTransitions.size(); i++) { - std::cout << initialTransitions[i] << std::endl; + std::cerr << initialTransitions[i] << std::endl; } - std::cout << std::endl; + std::cerr << std::endl; #endif enterStates(initialTransitions); @@ -856,7 +866,7 @@ InterpreterState InterpreterImpl::step(int waitForMS) { 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"))) { + if (!HAS_ATTR(invokeElem, "persist") || !stringIsTrue(ATTR(invokeElem, "persist"))) { invoke(invokeElem); } } @@ -1007,7 +1017,7 @@ void InterpreterImpl::stabilize() { _currEvent = _internalQueue.front(); _internalQueue.pop_front(); #if VERBOSE - std::cout << "Received internal event " << _currEvent.name << std::endl; + std::cerr << "Received internal event " << _currEvent.name << std::endl; #endif USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent) @@ -1032,7 +1042,7 @@ void InterpreterImpl::stabilize() { 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"))) { + if (!HAS_ATTR(invokeElem, "persist") || !stringIsTrue(ATTR(invokeElem, "persist"))) { invoke(invokeElem); } } @@ -1107,11 +1117,11 @@ LOOP: } #if VERBOSE - std::cout << "Enabled eventless transitions: " << std::endl; + std::cerr << "Enabled eventless transitions: " << std::endl; for (int i = 0; i < enabledTransitions.size(); i++) { - std::cout << enabledTransitions[i] << std::endl << "----" << std::endl; + std::cerr << enabledTransitions[i] << std::endl << "----" << std::endl; } - std::cout << std::endl; + std::cerr << std::endl; #endif enabledTransitions = removeConflictingTransitions(enabledTransitions); @@ -1522,7 +1532,7 @@ void InterpreterImpl::init() { // _running = true; #if VERBOSE - std::cout << "running " << this << std::endl; + std::cerr << "running " << this << std::endl; #endif if (_binding == EARLY) { @@ -1592,7 +1602,7 @@ void InterpreterImpl::initializeData(const Element<std::string>& data) { void InterpreterImpl::receiveInternal(const Event& event) { #if VERBOSE - std::cout << _name << " receiveInternal: " << event.name << std::endl; + std::cerr << _name << " receiveInternal: " << event.name << std::endl; #endif _internalQueue.push_back(event); // _condVar.notify_all(); @@ -1600,7 +1610,7 @@ void InterpreterImpl::receiveInternal(const Event& event) { void InterpreterImpl::receive(const Event& event, bool toFront) { #if VERBOSE - std::cout << _name << " receive: " << event.name << std::endl; + std::cerr << _name << " receive: " << event.name << std::endl; #endif if (toFront) { _externalQueue.push_front(event); @@ -1715,7 +1725,7 @@ void InterpreterImpl::processDOMorText(const Arabica::DOM::Element<std::string>& Node<std::string> child = element.getFirstChild(); while (child) { -// std::cout << child.getNodeType() << std::endl; +// std::cerr << child.getNodeType() << std::endl; if (child.getNodeType() == Node_base::TEXT_NODE || child.getNodeType() == Node_base::CDATA_SECTION_NODE) { std::string trimmed = child.getNodeValue(); @@ -2035,7 +2045,7 @@ void InterpreterImpl::invoke(const Arabica::DOM::Element<std::string>& element) if (HAS_ATTR(element, "id")) { invokeReq.invokeid = ATTR(element, "id"); } else { - if (HAS_ATTR(_scxml, "flat") && DOMUtils::attributeIsTrue(ATTR(_scxml, "flat")) && HAS_ATTR(element, "parent")) { + if (HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat")) && HAS_ATTR(element, "parent")) { invokeReq.invokeid = ATTR(element, "parent") + "." + UUID::getUUID(); } else { invokeReq.invokeid = ATTR(getParentState(element), "id") + "." + UUID::getUUID(); @@ -2381,7 +2391,7 @@ void InterpreterImpl::executeContent(const Arabica::DOM::Element<std::string>& c Arabica::DOM::Element<std::string> ifElem = (Arabica::DOM::Element<std::string>)content; #if VERBOSE if (HAS_ATTR(ifElem, "cond")) - std::cout << ATTR(ifElem, "cond") << std::endl; + std::cerr << ATTR(ifElem, "cond") << std::endl; #endif /** * A block is everything up to or between elseifs and else. Those element @@ -2446,15 +2456,15 @@ void InterpreterImpl::executeContent(const Arabica::DOM::Element<std::string>& c // --- LOG -------------------------- Arabica::DOM::Element<std::string> logElem = (Arabica::DOM::Element<std::string>)content; if (logElem.hasAttribute("label")) - std::cout << logElem.getAttribute("label") << ": "; + std::cerr << logElem.getAttribute("label") << ": "; if (logElem.hasAttribute("expr")) { try { - std::cout << _dataModel.evalAsString(logElem.getAttribute("expr")) << std::endl; + std::cerr << _dataModel.evalAsString(logElem.getAttribute("expr")) << std::endl; } CATCH_AND_DISTRIBUTE2("Syntax error in expr attribute of log element", content) } else { if (logElem.hasAttribute("label")) - std::cout << std::endl; + std::cerr << std::endl; } } else if (iequals(TAGNAME(content), _nsInfo.xmlNSPrefix + "assign")) { // --- ASSIGN -------------------------- @@ -2586,7 +2596,7 @@ void InterpreterImpl::finalizeAndAutoForwardCurrentEvent() { executeContent(finalizeElem); } } - if (HAS_ATTR(invokeIter->second.getElement(), "autoforward") && DOMUtils::attributeIsTrue(ATTR(invokeIter->second.getElement(), "autoforward"))) { + if (HAS_ATTR(invokeIter->second.getElement(), "autoforward") && stringIsTrue(ATTR(invokeIter->second.getElement(), "autoforward"))) { try { // do not autoforward to invokers that send to #_parent from the SCXML IO Processor! // Yes do so, see test229! @@ -2602,7 +2612,7 @@ void InterpreterImpl::finalizeAndAutoForwardCurrentEvent() { } void InterpreterImpl::returnDoneEvent(const Arabica::DOM::Node<std::string>& state) { -// std::cout << state << std::endl; +// std::cerr << state << std::endl; if (_parentQueue != NULL) { Event done; done.name = "done.invoke." + _sessionId; @@ -2695,11 +2705,11 @@ Arabica::DOM::Node<std::string> InterpreterImpl::getAncestorElement(const Arabic Arabica::DOM::Node<std::string> InterpreterImpl::findLCCA(const Arabica::XPath::NodeSet<std::string>& states) { #if VERBOSE_FIND_LCCA - std::cout << "findLCCA: "; + std::cerr << "findLCCA: "; for (int i = 0; i < states.size(); i++) { - std::cout << ATTR_CAST(states[i], "id") << ", "; + std::cerr << ATTR_CAST(states[i], "id") << ", "; } - std::cout << std::endl << std::flush; + std::cerr << std::endl << std::flush; #endif Arabica::XPath::NodeSet<std::string> ancestors = getProperAncestors(states[0], Arabica::DOM::Node<std::string>()); @@ -2711,7 +2721,7 @@ Arabica::DOM::Node<std::string> InterpreterImpl::findLCCA(const Arabica::XPath:: for (int j = 0; j < states.size(); j++) { #if VERBOSE_FIND_LCCA - std::cout << "Checking " << ATTR_CAST(states[j], "id") << " and " << ATTR_CAST(ancestors[i], "id") << std::endl; + std::cerr << "Checking " << ATTR_CAST(states[j], "id") << " and " << ATTR_CAST(ancestors[i], "id") << std::endl; #endif if (!isDescendant(states[j], ancestors[i])) @@ -2728,7 +2738,7 @@ NEXT_ANCESTOR: ancestor = _scxml; assert(ancestor); #if VERBOSE_FIND_LCCA - std::cout << " -> " << ATTR_CAST(ancestor, "id") << " " << ancestor.getLocalName() << std::endl; + std::cerr << " -> " << ATTR_CAST(ancestor, "id") << " " << ancestor.getLocalName() << std::endl; #endif return ancestor; } @@ -2814,7 +2824,7 @@ Arabica::XPath::NodeSet<std::string> InterpreterImpl::getInitialStates(Arabica:: } #if VERBOSE - std::cout << "Getting initial state of " << TAGNAME(state) << " " << ATTR(state, "id") << std::endl; + std::cerr << "Getting initial state of " << TAGNAME(state) << " " << ATTR(state, "id") << std::endl; #endif if (isAtomic(state)) { @@ -2940,7 +2950,7 @@ std::string InterpreterImpl::spaceNormalize(const std::string& text) { content << seperator << text.substr(start, i + 1 - start); } } -// std::cout << ">>" << content.str() << "<<" << std::endl; +// std::cerr << ">>" << content.str() << "<<" << std::endl; #else @@ -2979,7 +2989,7 @@ NodeSet<std::string> InterpreterImpl::filterChildElements(const std::string& tag for (unsigned int i = 0; i < childs.getLength(); i++) { if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE) continue; -// std::cout << TAGNAME(childs.item(i)) << std::endl; +// std::cerr << TAGNAME(childs.item(i)) << std::endl; if(iequals(TAGNAME_CAST(childs.item(i)), tagName)) { filteredChildElems.push_back(childs.item(i)); } @@ -3098,9 +3108,6 @@ bool InterpreterImpl::isInEmbeddedDocument(const Node<std::string>& node) { // a node is in an embedded document if there is a content element in its parents Node<std::string> parent = node; while(parent) { - if(parent == _scxml) { - return false; - } if(iequals(parent.getLocalName(), "content")) { return true; } @@ -3357,10 +3364,10 @@ bool InterpreterImpl::isLegalConfiguration(const NodeSet<std::string>& config) { Element<std::string> configElem(config[i]); if (!isAtomic(configElem) && !isParallel(configElem)) { Node<std::string> foundChildState; - //std::cout << config[i] << std::endl; + //std::cerr << config[i] << std::endl; NodeSet<std::string> childs = getChildStates(config[i]); for (int j = 0; j < childs.size(); j++) { - //std::cout << childs[j] << std::endl; + //std::cerr << childs[j] << std::endl; if (isMember(childs[j], config)) { if (foundChildState) { LOG(ERROR) << "Invalid configuration: Multiple childs of compound '" << ATTR_CAST(config[i], "id") @@ -3395,7 +3402,7 @@ bool InterpreterImpl::isLegalConfiguration(const NodeSet<std::string>& config) { } bool InterpreterImpl::isInState(const std::string& stateId) { - if (HAS_ATTR(_scxml, "flat") && DOMUtils::attributeIsTrue(ATTR(_scxml, "flat"))) { + if (HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat"))) { // extension for flattened SCXML documents if (_configuration.size() > 0 && HAS_ATTR_CAST(_configuration[0], "id")) { // all states are encoded in the current statename diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index c509684..7019eb9 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -423,7 +423,7 @@ public: static bool isParallel(const Arabica::DOM::Element<std::string>& state); static bool isCompound(const Arabica::DOM::Element<std::string>& state); static bool isDescendant(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2); - bool isInEmbeddedDocument(const Arabica::DOM::Node<std::string>& node); + static bool isInEmbeddedDocument(const Arabica::DOM::Node<std::string>& node); bool isInitial(const Arabica::DOM::Element<std::string>& state); static std::string stateToString(InterpreterState state); diff --git a/src/uscxml/debug/InterpreterIssue.cpp b/src/uscxml/debug/InterpreterIssue.cpp index 1bd416c..0bca74e 100644 --- a/src/uscxml/debug/InterpreterIssue.cpp +++ b/src/uscxml/debug/InterpreterIssue.cpp @@ -318,11 +318,43 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in std::string stateId = ATTR(state, "id"); - if (!InterpreterImpl::isMember(state, reachable)) { - issues.push_back(InterpreterIssue("State with id '" + stateId + "' is unreachable", state, InterpreterIssue::USCXML_ISSUE_FATAL)); + // check for valid transition with history states + if (LOCALNAME(state) == "history") { + NodeSet<std::string> transitions = InterpreterImpl::filterChildElements(_nsInfo.xmlNSPrefix + "transition", state, false); + if (transitions.size() > 1) { + issues.push_back(InterpreterIssue("History pseudo-state with id '" + stateId + "' has multiple transitions", state, InterpreterIssue::USCXML_ISSUE_FATAL)); + } else if (transitions.size() == 1) { + Element<std::string> transition = Element<std::string>(transitions[0]); + if (HAS_ATTR(transition, "cond")) { + issues.push_back(InterpreterIssue("Transition in history pseudo-state '" + stateId + "' must not have a condition", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + } + if (HAS_ATTR(transition, "event")) { + issues.push_back(InterpreterIssue("Transition in history pseudo-state '" + stateId + "' must not have an event attribute", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + } + if (!HAS_ATTR(transition, "target")) { + issues.push_back(InterpreterIssue("Transition in history pseudo-state '" + stateId + "' has no target", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + } else { + NodeSet<std::string> targetStates = interpreter->getTargetStates(transition); + for (int j = 0; j < targetStates.size(); j++) { + Element<std::string> target = Element<std::string>(targetStates[j]); + if (HAS_ATTR(state, "type") && ATTR(state, "type") == "deep") { + if (!InterpreterImpl::isDescendant(target, state.getParentNode())) { + issues.push_back(InterpreterIssue("Transition in deep history pseudo-state '" + stateId + "' has invalid target state '" + ATTR(target, "id") + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + } + } else { + if (target.getParentNode() != state.getParentNode()) { + issues.push_back(InterpreterIssue("Transition in shallow history pseudo-state '" + stateId + "' has invalid target state '" + ATTR(target, "id") + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + } + } + } + } + } } - + // check whether state is reachable + if (!InterpreterImpl::isMember(state, reachable) && !InterpreterImpl::isInEmbeddedDocument(state)) { + issues.push_back(InterpreterIssue("State with id '" + stateId + "' is unreachable", state, InterpreterIssue::USCXML_ISSUE_FATAL)); + } // check for uniqueness of id attribute if (seenStates.find(stateId) != seenStates.end()) { @@ -336,15 +368,17 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in Element<std::string> transition = Element<std::string>(transitions[i]); // check for valid target - std::list<std::string> targetIds = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "target")); - if (targetIds.size() == 0) { - issues.push_back(InterpreterIssue("Transition has empty target state list", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); - } - - for (std::list<std::string>::iterator targetIter = targetIds.begin(); targetIter != targetIds.end(); targetIter++) { - if (seenStates.find(*targetIter) == seenStates.end()) { - issues.push_back(InterpreterIssue("Transition has non-existant target state with id '" + *targetIter + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); - continue; + if (HAS_ATTR(transition, "target")) { + std::list<std::string> targetIds = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "target")); + if (targetIds.size() == 0) { + issues.push_back(InterpreterIssue("Transition has empty target state list", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + } + + for (std::list<std::string>::iterator targetIter = targetIds.begin(); targetIter != targetIds.end(); targetIter++) { + if (seenStates.find(*targetIter) == seenStates.end()) { + issues.push_back(InterpreterIssue("Transition has non-existant target state with id '" + *targetIter + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + continue; + } } } } @@ -413,6 +447,7 @@ NEXT_TRANSITION: } } + // check that all invokers exists { for (int i = 0; i < invokes.size(); i++) { diff --git a/src/uscxml/debug/SCXMLDotWriter.cpp b/src/uscxml/debug/SCXMLDotWriter.cpp index 4f0143f..82faaa4 100644 --- a/src/uscxml/debug/SCXMLDotWriter.cpp +++ b/src/uscxml/debug/SCXMLDotWriter.cpp @@ -45,7 +45,7 @@ SCXMLDotWriter::SCXMLDotWriter(Interpreter interpreter, NodeList<std::string > scxmlElems = interpreter.getDocument().getElementsByTagName("scxml"); _scxml = (Element<std::string>)scxmlElems.item(0); - _isFlat = HAS_ATTR(_scxml, "flat") && DOMUtils::attributeIsTrue(ATTR(_scxml, "flat")); + _isFlat = HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat")); if (_anchors.size() == 0) { StateAnchor anchor; diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp index 2bab937..6bbb7d8 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.cpp +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -248,7 +248,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); for (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"))) { + if (HAS_ATTR(invokeElem, "persist") && stringIsTrue(ATTR(invokeElem, "persist"))) { // extension for flattened SCXML documents, we will need an explicit uninvoke element } else { cancelInvoke(invokeElem); @@ -387,7 +387,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToEnter[k]); 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"))) { + if (HAS_ATTR(invokeElem, "persist") && stringIsTrue(ATTR(invokeElem, "persist"))) { invoke(invokeElem); } } diff --git a/src/uscxml/interpreter/InterpreterRC.cpp b/src/uscxml/interpreter/InterpreterRC.cpp index b58a236..0237618 100644 --- a/src/uscxml/interpreter/InterpreterRC.cpp +++ b/src/uscxml/interpreter/InterpreterRC.cpp @@ -151,7 +151,7 @@ void InterpreterRC::exitStates(const Arabica::XPath::NodeSet<std::string>& enabl NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]); for (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"))) { + if (HAS_ATTR(invokeElem, "persist") && stringIsTrue(ATTR(invokeElem, "persist"))) { } else { cancelInvoke(invokeElem); } @@ -271,12 +271,12 @@ void InterpreterRC::enterStates(const Arabica::XPath::NodeSet<std::string>& enab USCXML_MONITOR_CALLBACK3(afterEnteringState, s, i + 1 < statesToEnter.size()) - if (HAS_ATTR(_scxml, "flat") && DOMUtils::attributeIsTrue(ATTR(_scxml, "flat"))) { + if (HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat"))) { // extension for flattened interpreters NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", s); 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"))) { + if (HAS_ATTR(invokeElem, "persist") && stringIsTrue(ATTR(invokeElem, "persist"))) { invoke(invokeElem); } } diff --git a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp index 679177a..2621c66 100644 --- a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp +++ b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp @@ -211,6 +211,16 @@ bool PromelaDataModel::isLocation(const std::string& expr) { return true; } +bool PromelaDataModel::isValidSyntax(const std::string& expr) { + try { + PromelaParser parser(expr); + } catch (Event e) { + std::cerr << e << std::endl; + return false; + } + return true; +} + uint32_t PromelaDataModel::getLength(const std::string& expr) { if (!isDeclared(expr)) { ERROR_EXECUTION_THROW("Variable '" + expr + "' was not declared"); @@ -593,7 +603,7 @@ void PromelaDataModel::setVariable(void* ast, const Data& value) { if (value.compound.size() > 0 || value.atom.size() > 0) ERROR_EXECUTION_THROW("Variable " + node->value + " is an array"); - if (_variables[node->value].compound["size"] < value.array.size()) + if (strTo<size_t>(_variables[node->value].compound["size"].atom) < value.array.size()) ERROR_EXECUTION_THROW("Array assigned to " + node->value + " is too large"); } diff --git a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.h b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.h index 581c761..25fe536 100644 --- a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.h +++ b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.h @@ -47,6 +47,7 @@ public: virtual bool validate(const std::string& location, const std::string& schema); virtual bool isLocation(const std::string& expr); + virtual bool isValidSyntax(const std::string& expr); virtual uint32_t getLength(const std::string& expr); virtual void setForeach(const std::string& item, diff --git a/src/uscxml/plugins/invoker/xhtml/template/xhtml-invoker.inc.h b/src/uscxml/plugins/invoker/xhtml/template/xhtml-invoker.inc.h index 971260b..c5a5798 100644 --- a/src/uscxml/plugins/invoker/xhtml/template/xhtml-invoker.inc.h +++ b/src/uscxml/plugins/invoker/xhtml/template/xhtml-invoker.inc.h @@ -1,691 +1,691 @@ unsigned char template_xhtml_invoker_html[] = { - 0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, - 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, - 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, - 0x78, 0x68, 0x74, 0x6d, 0x6c, 0x22, 0x3e, 0x0a, 0x09, 0x3c, 0x68, 0x65, - 0x61, 0x64, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x20, - 0x68, 0x74, 0x74, 0x70, 0x2d, 0x65, 0x71, 0x75, 0x69, 0x76, 0x3d, 0x22, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x74, - 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x63, 0x68, 0x61, - 0x72, 0x73, 0x65, 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x22, 0x20, - 0x2f, 0x3e, 0x0a, 0x0a, 0x09, 0x09, 0x3c, 0x21, 0x2d, 0x2d, 0x20, 0x47, - 0x65, 0x74, 0x20, 0x64, 0x6f, 0x6d, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, - 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x63, 0x72, 0x6f, 0x73, - 0x73, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x73, 0x20, 0x2d, - 0x2d, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, - 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x3e, - 0x0a, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x65, 0x20, 0x68, - 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x6f, - 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x31, 0x32, - 0x30, 0x36, 0x39, 0x33, 0x37, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x2d, 0x64, 0x6f, 0x6d, 0x72, 0x65, 0x61, 0x64, - 0x79, 0x0a, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x64, 0x6f, 0x6d, - 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, - 0x63, 0x6b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2a, - 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x45, 0x78, - 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x2f, 0x2a, 0x40, 0x63, 0x63, 0x5f, 0x6f, 0x6e, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x40, 0x69, 0x66, 0x20, 0x28, 0x40, 0x5f, 0x77, 0x69, - 0x6e, 0x33, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x40, 0x5f, 0x77, 0x69, 0x6e, - 0x36, 0x34, 0x29, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x77, - 0x72, 0x69, 0x74, 0x65, 0x28, 0x27, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x69, 0x65, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x22, 0x20, 0x64, 0x65, 0x66, 0x65, - 0x72, 0x20, 0x73, 0x72, 0x63, 0x3d, 0x22, 0x2f, 0x2f, 0x3a, 0x22, 0x3e, - 0x3c, 0x5c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x27, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6f, 0x63, 0x75, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x69, 0x65, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x27, 0x29, 0x2e, 0x6f, - 0x6e, 0x72, 0x65, 0x61, 0x64, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, - 0x3d, 0x20, 0x27, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x27, - 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, - 0x61, 0x72, 0x20, 0x68, 0x65, 0x61, 0x64, 0x3d, 0x20, 0x64, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x54, 0x61, 0x67, 0x4e, 0x61, - 0x6d, 0x65, 0x28, 0x27, 0x68, 0x65, 0x61, 0x64, 0x27, 0x29, 0x5b, 0x30, - 0x5d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, - 0x72, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3d, 0x20, 0x64, 0x6f, - 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x27, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2e, 0x74, 0x79, - 0x70, 0x65, 0x3d, 0x20, 0x27, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x6a, 0x61, - 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x27, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2e, 0x73, 0x72, 0x63, 0x3d, 0x20, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, - 0x2f, 0x2f, 0x77, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x2d, 0x67, 0x6f, 0x6f, - 0x64, 0x2d, 0x78, 0x70, 0x61, 0x74, 0x68, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, - 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x77, 0x67, 0x78, 0x70, 0x61, 0x74, 0x68, - 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x2e, 0x6a, 0x73, 0x27, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x68, 0x65, 0x61, - 0x64, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, - 0x64, 0x28, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, 0x67, 0x78, 0x70, 0x61, 0x74, - 0x68, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x28, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x65, 0x20, 0x68, - 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x6f, - 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x31, 0x38, - 0x31, 0x31, 0x31, 0x31, 0x36, 0x2f, 0x69, 0x65, 0x2d, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x64, 0x6f, 0x6d, - 0x2d, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x6e, 0x6f, 0x64, 0x65, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6f, 0x63, 0x75, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4e, 0x6f, - 0x64, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x43, - 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x29, 0x20, 0x7b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, - 0x63, 0x68, 0x20, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, - 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x4c, - 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x3a, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x65, 0x77, 0x4e, 0x6f, 0x64, 0x65, - 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x4e, 0x53, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x55, 0x52, 0x49, 0x2c, 0x20, 0x6e, - 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x20, 0x26, - 0x26, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x20, 0x3e, 0x20, 0x30, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, - 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, - 0x69, 0x6c, 0x20, 0x3d, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x69, 0x6c, 0x3b, - 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, - 0x65, 0x77, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x73, 0x65, 0x74, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x6e, 0x6f, 0x64, 0x65, - 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5b, - 0x69, 0x5d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x2c, - 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5b, 0x69, - 0x5d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x29, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x61, 0x6c, 0x6c, 0x43, 0x68, 0x69, - 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x20, 0x26, 0x26, 0x20, 0x6e, 0x6f, 0x64, - 0x65, 0x2e, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, - 0x20, 0x26, 0x26, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x63, 0x68, 0x69, - 0x6c, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, - 0x2c, 0x20, 0x69, 0x6c, 0x20, 0x3d, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, - 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x2e, 0x6c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x69, - 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x6e, 0x65, 0x77, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x61, 0x70, 0x70, - 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x64, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x63, 0x68, - 0x69, 0x6c, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x2c, - 0x20, 0x61, 0x6c, 0x6c, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, - 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x6e, 0x65, 0x77, 0x4e, 0x6f, 0x64, 0x65, 0x3b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, - 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x64, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x4e, - 0x4f, 0x44, 0x45, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x64, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x44, 0x41, 0x54, 0x41, 0x5f, - 0x53, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x44, 0x45, - 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x4e, - 0x4f, 0x44, 0x45, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x78, 0x74, 0x4e, 0x6f, 0x64, 0x65, - 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x7d, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x40, 0x65, 0x6e, - 0x64, 0x20, 0x40, 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2a, - 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2c, 0x20, 0x43, 0x68, - 0x72, 0x6f, 0x6d, 0x65, 0x2c, 0x20, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x20, - 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x64, - 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x64, 0x64, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, - 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x27, - 0x44, 0x4f, 0x4d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4c, 0x6f, - 0x61, 0x64, 0x65, 0x64, 0x27, 0x2c, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, - 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x2f, 0x2a, 0x20, 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2c, 0x20, 0x69, - 0x43, 0x61, 0x62, 0x2c, 0x20, 0x4b, 0x6f, 0x6e, 0x71, 0x75, 0x65, 0x72, - 0x6f, 0x72, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, - 0x20, 0x28, 0x2f, 0x4b, 0x48, 0x54, 0x4d, 0x4c, 0x7c, 0x57, 0x65, 0x62, - 0x4b, 0x69, 0x74, 0x7c, 0x69, 0x43, 0x61, 0x62, 0x2f, 0x69, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x28, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x6f, - 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x29, - 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, - 0x20, 0x44, 0x4f, 0x4d, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, - 0x72, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x69, 0x66, 0x20, 0x28, 0x2f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x7c, - 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2f, 0x69, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x29, - 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x28, 0x44, 0x4f, 0x4d, 0x4c, - 0x6f, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x7d, 0x2c, 0x20, 0x31, 0x30, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2a, 0x20, 0x4f, 0x74, - 0x68, 0x65, 0x72, 0x20, 0x77, 0x65, 0x62, 0x20, 0x62, 0x72, 0x6f, 0x77, - 0x73, 0x65, 0x72, 0x73, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6f, 0x6e, 0x6c, 0x6f, 0x61, - 0x64, 0x20, 0x3d, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x3c, - 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0a, 0x0a, 0x09, 0x09, - 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x0a, - 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x43, 0x6f, 0x6d, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x28, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x20, 0x7b, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2a, 0x2a, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x20, 0x2a, 0x20, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x74, 0x77, 0x6f, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x20, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, - 0x6f, 0x75, 0x73, 0x20, 0x68, 0x74, 0x74, 0x70, 0x20, 0x63, 0x6f, 0x6d, - 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, - 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, - 0x69, 0x6e, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x20, - 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x68, 0x61, 0x73, 0x4f, - 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6b, - 0x65, 0x79, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, - 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x5d, - 0x20, 0x3d, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6b, - 0x65, 0x79, 0x5d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x7d, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x76, - 0x61, 0x72, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x3d, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x78, 0x68, 0x72, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x28, 0x77, 0x69, 0x6e, 0x64, 0x6f, - 0x77, 0x2e, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x58, - 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x41, 0x63, - 0x74, 0x69, 0x76, 0x65, 0x58, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, - 0x22, 0x4d, 0x53, 0x58, 0x4d, 0x4c, 0x32, 0x2e, 0x58, 0x4d, 0x4c, 0x48, - 0x54, 0x54, 0x50, 0x2e, 0x33, 0x2e, 0x30, 0x22, 0x29, 0x29, 0x3b, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, - 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x28, 0x77, 0x69, - 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x6e, 0x65, - 0x77, 0x20, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x6e, 0x65, 0x77, - 0x20, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x58, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x28, 0x22, 0x4d, 0x53, 0x58, 0x4d, 0x4c, 0x32, 0x2e, 0x58, - 0x4d, 0x4c, 0x48, 0x54, 0x54, 0x50, 0x2e, 0x33, 0x2e, 0x30, 0x22, 0x29, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, - 0x55, 0x49, 0x44, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x2f, 0x2f, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, - 0x77, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x72, - 0x66, 0x63, 0x2f, 0x72, 0x66, 0x63, 0x34, 0x31, 0x32, 0x32, 0x2e, 0x74, - 0x78, 0x74, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, - 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x76, 0x61, 0x72, 0x20, 0x68, 0x65, 0x78, 0x44, 0x69, 0x67, 0x69, - 0x74, 0x73, 0x20, 0x3d, 0x20, 0x22, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x22, 0x3b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, - 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, - 0x3c, 0x20, 0x33, 0x36, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x5b, 0x69, 0x5d, 0x20, - 0x3d, 0x20, 0x68, 0x65, 0x78, 0x44, 0x69, 0x67, 0x69, 0x74, 0x73, 0x2e, - 0x73, 0x75, 0x62, 0x73, 0x74, 0x72, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, - 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, - 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x29, 0x20, 0x2a, 0x20, 0x30, 0x78, - 0x31, 0x30, 0x29, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x5b, 0x31, - 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x34, 0x22, 0x3b, 0x20, 0x20, 0x2f, - 0x2f, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, 0x31, 0x32, 0x2d, 0x31, 0x35, - 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, - 0x5f, 0x68, 0x69, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x74, 0x6f, - 0x20, 0x30, 0x30, 0x31, 0x30, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, - 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x3d, 0x20, 0x68, 0x65, 0x78, 0x44, 0x69, - 0x67, 0x69, 0x74, 0x73, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x74, 0x72, 0x28, - 0x28, 0x73, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x26, 0x20, 0x30, 0x78, 0x33, - 0x29, 0x20, 0x7c, 0x20, 0x30, 0x78, 0x38, 0x2c, 0x20, 0x31, 0x29, 0x3b, - 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, 0x36, 0x2d, - 0x37, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x5f, 0x68, 0x69, 0x5f, 0x61, 0x6e, - 0x64, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x30, 0x31, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x5b, - 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x5b, 0x31, 0x33, 0x5d, 0x20, 0x3d, - 0x20, 0x73, 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x5b, 0x32, - 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x2d, 0x22, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x75, 0x75, 0x69, 0x64, 0x20, - 0x3d, 0x20, 0x73, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x22, 0x22, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x75, 0x75, 0x69, 0x64, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, - 0x68, 0x69, 0x73, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, 0x70, 0x6f, 0x6c, 0x6c, - 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, - 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, - 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, - 0x6f, 0x6e, 0x72, 0x65, 0x61, 0x64, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x65, 0x6c, 0x66, - 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x72, - 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d, - 0x3d, 0x20, 0x34, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, - 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x32, 0x30, 0x30, 0x29, - 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, - 0x65, 0x6c, 0x66, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, 0x70, 0x6f, 0x6c, 0x6c, - 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x6f, 0x6e, 0x52, 0x63, 0x76, 0x64, 0x28, - 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, - 0x6c, 0x6c, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, 0x70, 0x6f, 0x6c, - 0x6c, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x20, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x20, 0x77, 0x65, 0x20, - 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, - 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x6f, - 0x70, 0x65, 0x6e, 0x28, 0x22, 0x47, 0x45, 0x54, 0x22, 0x2c, 0x20, 0x73, - 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, - 0x20, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x20, 0x3f, 0x20, 0x22, 0x3f, 0x22, 0x20, 0x2b, 0x20, 0x73, 0x65, 0x6c, - 0x66, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x3a, 0x20, 0x22, 0x22, - 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, - 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, - 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x57, 0x69, 0x74, 0x68, 0x27, 0x2c, - 0x20, 0x27, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x6f, 0x6d, - 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x65, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, - 0x22, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x2c, 0x20, 0x22, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x22, 0x29, 0x3b, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x6f, - 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x65, 0x6e, 0x64, - 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, 0x6f, 0x70, 0x65, - 0x6e, 0x28, 0x22, 0x50, 0x4f, 0x53, 0x54, 0x22, 0x2c, 0x20, 0x73, 0x65, - 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, 0x20, - 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, - 0x3f, 0x20, 0x22, 0x3f, 0x22, 0x20, 0x2b, 0x20, 0x73, 0x65, 0x6c, 0x66, - 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x3a, 0x20, 0x22, 0x22, 0x29, - 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, - 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x57, 0x69, 0x74, 0x68, 0x27, 0x2c, - 0x20, 0x27, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, 0x73, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x53, 0x43, 0x58, 0x4d, 0x4c, 0x2d, - 0x4e, 0x61, 0x6d, 0x65, 0x27, 0x2c, 0x20, 0x22, 0x64, 0x6f, 0x6e, 0x65, - 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x22, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, - 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x4e, 0x55, 0x4c, 0x4c, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x6f, - 0x73, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, - 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x28, - 0x22, 0x50, 0x4f, 0x53, 0x54, 0x22, 0x2c, 0x20, 0x73, 0x65, 0x6c, 0x66, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, 0x20, 0x28, 0x73, - 0x65, 0x6c, 0x66, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x3f, 0x20, - 0x22, 0x3f, 0x22, 0x20, 0x2b, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x20, 0x3a, 0x20, 0x22, 0x22, 0x29, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, - 0x68, 0x72, 0x2e, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x57, 0x69, 0x74, - 0x68, 0x27, 0x2c, 0x20, 0x27, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, - 0x2e, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x53, 0x43, 0x58, - 0x4d, 0x4c, 0x2d, 0x4e, 0x61, 0x6d, 0x65, 0x27, 0x2c, 0x20, 0x6e, 0x61, - 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, - 0x20, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, - 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x28, 0x27, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, - 0x70, 0x65, 0x27, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, - 0x78, 0x68, 0x72, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x64, 0x61, 0x74, - 0x61, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x6f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, - 0x73, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, - 0x74, 0x68, 0x69, 0x6e, 0x67, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x3b, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x65, 0x20, - 0x61, 0x6c, 0x73, 0x6f, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, - 0x2f, 0x2f, 0x72, 0x61, 0x77, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x75, 0x67, 0x6c, 0x61, 0x73, - 0x63, 0x72, 0x6f, 0x63, 0x6b, 0x66, 0x6f, 0x72, 0x64, 0x2f, 0x4a, 0x53, - 0x4f, 0x4e, 0x2d, 0x6a, 0x73, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x2f, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x2e, 0x6a, 0x73, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x69, 0x6e, - 0x67, 0x79, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x68, 0x65, 0x72, 0x65, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, - 0x70, 0x65, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x3d, - 0x3d, 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x29, - 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, - 0x20, 0x73, 0x65, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x77, 0x69, 0x6c, - 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x6f, - 0x6e, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x20, 0x61, 0x73, 0x20, 0x74, - 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x79, 0x63, 0x6c, - 0x65, 0x73, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x4a, 0x53, - 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, - 0x28, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, - 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x69, 0x66, 0x20, 0x28, 0x69, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x76, - 0x61, 0x6c, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x61, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x6f, 0x66, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x64, 0x3a, 0x20, 0x76, 0x61, 0x6c, - 0x2e, 0x69, 0x64, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x74, 0x61, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x76, - 0x61, 0x6c, 0x2e, 0x74, 0x61, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x2c, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, - 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x2e, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x73, 0x57, 0x69, 0x6e, 0x64, 0x6f, - 0x77, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, - 0x70, 0x65, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x20, 0x3d, 0x3d, 0x3d, - 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x29, 0x20, 0x7b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, - 0x28, 0x73, 0x65, 0x65, 0x6e, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, - 0x66, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x29, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x73, 0x65, 0x65, 0x6e, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, - 0x76, 0x61, 0x6c, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x7d, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, - 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x74, 0x68, 0x69, - 0x6e, 0x67, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x6f, 0x73, - 0x74, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, - 0x2c, 0x20, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x22, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x20, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, - 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x77, 0x68, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, - 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x68, 0x74, 0x6d, - 0x6c, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x76, - 0x61, 0x72, 0x20, 0x69, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x29, 0x7b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x28, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x20, - 0x3d, 0x3d, 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, - 0x20, 0x3f, 0x20, 0x6f, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x6f, 0x66, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x20, 0x3a, 0x20, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x26, 0x26, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x20, 0x3d, 0x3d, - 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x20, 0x26, - 0x26, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x2e, 0x6e, - 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, - 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x26, 0x26, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x2e, 0x6e, 0x6f, 0x64, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x3d, 0x3d, 0x3d, 0x22, 0x73, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x22, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x2f, 0x2f, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x20, 0x66, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x64, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x77, 0x68, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, - 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x69, 0x6e, 0x64, - 0x6f, 0x77, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x69, - 0x73, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x66, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x29, 0x7b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x28, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, - 0x70, 0x65, 0x6f, 0x66, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, - 0x3d, 0x3d, 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, - 0x20, 0x3f, 0x20, 0x6f, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x6f, 0x66, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x3a, - 0x20, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, - 0x26, 0x26, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x20, - 0x3d, 0x3d, 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, - 0x20, 0x26, 0x26, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, - 0x2e, 0x6d, 0x65, 0x6e, 0x75, 0x62, 0x61, 0x72, 0x20, 0x3d, 0x3d, 0x3d, - 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x20, 0x20, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, - 0x09, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x3e, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, - 0x74, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x22, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x64, 0x6f, 0x6d, 0x4c, 0x6f, 0x61, - 0x64, 0x65, 0x64, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x73, 0x63, - 0x78, 0x6d, 0x6c, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x43, 0x6f, - 0x6d, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x7b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x24, 0x7b, 0x73, 0x63, 0x78, 0x6d, - 0x6c, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x49, 0x64, 0x7d, 0x22, - 0x29, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x55, 0x52, 0x4c, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x6f, - 0x6e, 0x52, 0x63, 0x76, 0x64, 0x20, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x58, 0x4d, 0x4c, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, - 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, - 0x22, 0x58, 0x2d, 0x53, 0x43, 0x58, 0x4d, 0x4c, 0x2d, 0x54, 0x79, 0x70, - 0x65, 0x22, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x22, 0x72, 0x65, 0x70, 0x6c, - 0x61, 0x63, 0x65, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, - 0x20, 0x64, 0x6f, 0x6d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x20, 0x3d, - 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, - 0x22, 0x58, 0x2d, 0x53, 0x43, 0x58, 0x4d, 0x4c, 0x2d, 0x58, 0x50, 0x61, - 0x74, 0x68, 0x22, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x22, 0x2f, 0x68, 0x74, - 0x6d, 0x6c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x64, 0x6f, 0x6d, - 0x41, 0x74, 0x74, 0x72, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x22, 0x58, 0x2d, 0x53, 0x43, 0x58, - 0x4d, 0x4c, 0x2d, 0x41, 0x74, 0x74, 0x72, 0x22, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, - 0x28, 0x64, 0x6f, 0x6d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2c, 0x20, - 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x6e, 0x75, - 0x6c, 0x6c, 0x2c, 0x20, 0x58, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x2e, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x45, 0x44, 0x5f, - 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, - 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x2c, 0x20, 0x6e, 0x75, 0x6c, 0x6c, - 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x66, - 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, - 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x2e, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, - 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, 0x6d, - 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x73, 0x6e, - 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x28, 0x69, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, - 0x61, 0x72, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x6f, - 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6d, 0x70, 0x6f, 0x72, - 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x58, 0x4d, 0x4c, 0x2e, 0x66, - 0x69, 0x72, 0x73, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x2c, 0x20, 0x74, - 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x28, 0x74, 0x79, - 0x70, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x66, 0x69, 0x72, - 0x73, 0x74, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x22, 0x3a, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, - 0x2e, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x42, 0x65, 0x66, 0x6f, 0x72, - 0x65, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, - 0x2e, 0x66, 0x69, 0x72, 0x73, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, - 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x6c, 0x61, 0x73, - 0x74, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, - 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, - 0x6e, 0x6f, 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, - 0x20, 0x22, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x73, 0x69, - 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x70, - 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x69, 0x6e, - 0x73, 0x65, 0x72, 0x74, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x28, 0x6e, - 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, - 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x73, - 0x69, 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x69, - 0x6e, 0x73, 0x65, 0x72, 0x74, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x28, - 0x6e, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x6e, - 0x65, 0x78, 0x74, 0x53, 0x69, 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, - 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x72, 0x65, 0x70, 0x6c, - 0x61, 0x63, 0x65, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x72, 0x65, 0x70, 0x6c, - 0x61, 0x63, 0x65, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x6e, 0x6f, 0x64, - 0x65, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, - 0x61, 0x73, 0x65, 0x20, 0x22, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, - 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4e, - 0x6f, 0x64, 0x65, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x68, - 0x69, 0x6c, 0x64, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, - 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x61, 0x64, 0x64, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, - 0x73, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x28, 0x64, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x2c, 0x20, 0x6e, 0x6f, - 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, - 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x63, 0x68, 0x69, 0x6c, 0x64, - 0x72, 0x65, 0x6e, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x69, 0x74, - 0x65, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x4e, - 0x6f, 0x64, 0x65, 0x73, 0x28, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, - 0x6d, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x68, 0x69, 0x6c, - 0x64, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x66, 0x69, 0x72, 0x73, 0x74, - 0x43, 0x68, 0x69, 0x6c, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x61, - 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x6e, - 0x6f, 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, - 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x73, 0x63, 0x78, 0x6d, 0x6c, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, - 0x70, 0x6f, 0x6c, 0x6c, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, - 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x63, - 0x78, 0x6d, 0x6c, 0x3b, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x77, 0x69, - 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6f, 0x6e, 0x62, 0x65, 0x66, 0x6f, 0x72, - 0x65, 0x75, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x29, 0x20, 0x7b, - 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x63, 0x78, 0x6d, 0x6c, 0x2e, - 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x28, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x27, 0x59, 0x6f, 0x75, 0x20, 0x68, 0x61, - 0x76, 0x65, 0x20, 0x75, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x64, 0x20, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x21, 0x27, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x29, 0x3b, 0x0a, - 0x09, 0x09, 0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0a, - 0x0a, 0x09, 0x3c, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x3e, 0x0a, 0x09, 0x3c, - 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e, - 0x0a, 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0x0a + 0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, + 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, + 0x78, 0x68, 0x74, 0x6d, 0x6c, 0x22, 0x3e, 0x0a, 0x09, 0x3c, 0x68, 0x65, + 0x61, 0x64, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x20, + 0x68, 0x74, 0x74, 0x70, 0x2d, 0x65, 0x71, 0x75, 0x69, 0x76, 0x3d, 0x22, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, + 0x22, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x74, + 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x63, 0x68, 0x61, + 0x72, 0x73, 0x65, 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x22, 0x20, + 0x2f, 0x3e, 0x0a, 0x0a, 0x09, 0x09, 0x3c, 0x21, 0x2d, 0x2d, 0x20, 0x47, + 0x65, 0x74, 0x20, 0x64, 0x6f, 0x6d, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, + 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x63, 0x72, 0x6f, 0x73, + 0x73, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x73, 0x20, 0x2d, + 0x2d, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, + 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x3e, + 0x0a, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x65, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x6f, + 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x31, 0x32, + 0x30, 0x36, 0x39, 0x33, 0x37, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x2d, 0x64, 0x6f, 0x6d, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x0a, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x64, 0x6f, 0x6d, + 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2a, + 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x45, 0x78, + 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x2f, 0x2a, 0x40, 0x63, 0x63, 0x5f, 0x6f, 0x6e, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x40, 0x69, 0x66, 0x20, 0x28, 0x40, 0x5f, 0x77, 0x69, + 0x6e, 0x33, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x40, 0x5f, 0x77, 0x69, 0x6e, + 0x36, 0x34, 0x29, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x28, 0x27, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x69, 0x65, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x22, 0x20, 0x64, 0x65, 0x66, 0x65, + 0x72, 0x20, 0x73, 0x72, 0x63, 0x3d, 0x22, 0x2f, 0x2f, 0x3a, 0x22, 0x3e, + 0x3c, 0x5c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x27, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6f, 0x63, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x69, 0x65, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x27, 0x29, 0x2e, 0x6f, + 0x6e, 0x72, 0x65, 0x61, 0x64, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, + 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, + 0x3d, 0x20, 0x27, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x27, + 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, + 0x61, 0x72, 0x20, 0x68, 0x65, 0x61, 0x64, 0x3d, 0x20, 0x64, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x54, 0x61, 0x67, 0x4e, 0x61, + 0x6d, 0x65, 0x28, 0x27, 0x68, 0x65, 0x61, 0x64, 0x27, 0x29, 0x5b, 0x30, + 0x5d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, + 0x72, 0x20, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3d, 0x20, 0x64, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x27, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x3d, 0x20, 0x27, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x6a, 0x61, + 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x27, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2e, 0x73, 0x72, 0x63, 0x3d, 0x20, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x2d, 0x67, 0x6f, 0x6f, + 0x64, 0x2d, 0x78, 0x70, 0x61, 0x74, 0x68, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x77, 0x67, 0x78, 0x70, 0x61, 0x74, 0x68, + 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x2e, 0x6a, 0x73, 0x27, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x68, 0x65, 0x61, + 0x64, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, + 0x64, 0x28, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, 0x67, 0x78, 0x70, 0x61, 0x74, + 0x68, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x28, 0x29, 0x3b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x65, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x6f, + 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x31, 0x38, + 0x31, 0x31, 0x31, 0x31, 0x36, 0x2f, 0x69, 0x65, 0x2d, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x64, 0x6f, 0x6d, + 0x2d, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x6e, 0x6f, 0x64, 0x65, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6f, 0x63, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x43, + 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x29, 0x20, 0x7b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, + 0x63, 0x68, 0x20, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x4c, + 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x3a, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x65, 0x77, 0x4e, 0x6f, 0x64, 0x65, + 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x4e, 0x53, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x55, 0x52, 0x49, 0x2c, 0x20, 0x6e, + 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x20, 0x26, + 0x26, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x20, 0x3e, 0x20, 0x30, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, + 0x69, 0x6c, 0x20, 0x3d, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x69, 0x6c, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, + 0x65, 0x77, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x73, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5b, + 0x69, 0x5d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x2c, + 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5b, 0x69, + 0x5d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x29, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x61, 0x6c, 0x6c, 0x43, 0x68, 0x69, + 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x20, 0x26, 0x26, 0x20, 0x6e, 0x6f, 0x64, + 0x65, 0x2e, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, + 0x20, 0x26, 0x26, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x63, 0x68, 0x69, + 0x6c, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, + 0x2c, 0x20, 0x69, 0x6c, 0x20, 0x3d, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x2e, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x69, + 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6e, 0x65, 0x77, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x61, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x64, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x63, 0x68, + 0x69, 0x6c, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x2c, + 0x20, 0x61, 0x6c, 0x6c, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, + 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x6e, 0x65, 0x77, 0x4e, 0x6f, 0x64, 0x65, 0x3b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x64, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x4e, + 0x4f, 0x44, 0x45, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x64, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x44, 0x41, 0x54, 0x41, 0x5f, + 0x53, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x44, 0x45, + 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x4e, + 0x4f, 0x44, 0x45, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x78, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x7d, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x29, 0x3b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x40, 0x65, 0x6e, + 0x64, 0x20, 0x40, 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2a, + 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2c, 0x20, 0x43, 0x68, + 0x72, 0x6f, 0x6d, 0x65, 0x2c, 0x20, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x20, + 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x64, + 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x64, 0x64, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, + 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x27, + 0x44, 0x4f, 0x4d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4c, 0x6f, + 0x61, 0x64, 0x65, 0x64, 0x27, 0x2c, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x2f, 0x2a, 0x20, 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2c, 0x20, 0x69, + 0x43, 0x61, 0x62, 0x2c, 0x20, 0x4b, 0x6f, 0x6e, 0x71, 0x75, 0x65, 0x72, + 0x6f, 0x72, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, + 0x20, 0x28, 0x2f, 0x4b, 0x48, 0x54, 0x4d, 0x4c, 0x7c, 0x57, 0x65, 0x62, + 0x4b, 0x69, 0x74, 0x7c, 0x69, 0x43, 0x61, 0x62, 0x2f, 0x69, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x28, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x29, + 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, + 0x20, 0x44, 0x4f, 0x4d, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, + 0x72, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x69, 0x66, 0x20, 0x28, 0x2f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x7c, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2f, 0x69, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x29, + 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x28, 0x44, 0x4f, 0x4d, 0x4c, + 0x6f, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x7d, 0x2c, 0x20, 0x31, 0x30, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2a, 0x20, 0x4f, 0x74, + 0x68, 0x65, 0x72, 0x20, 0x77, 0x65, 0x62, 0x20, 0x62, 0x72, 0x6f, 0x77, + 0x73, 0x65, 0x72, 0x73, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6f, 0x6e, 0x6c, 0x6f, 0x61, + 0x64, 0x20, 0x3d, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x3c, + 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0a, 0x0a, 0x09, 0x09, + 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x0a, + 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x43, 0x6f, 0x6d, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x20, 0x7b, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2a, 0x2a, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x20, 0x2a, 0x20, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x74, 0x77, 0x6f, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x20, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, + 0x6f, 0x75, 0x73, 0x20, 0x68, 0x74, 0x74, 0x70, 0x20, 0x63, 0x6f, 0x6d, + 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, + 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, + 0x69, 0x6e, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x20, + 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x68, 0x61, 0x73, 0x4f, + 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6b, + 0x65, 0x79, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x5d, + 0x20, 0x3d, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6b, + 0x65, 0x79, 0x5d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x7d, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x76, + 0x61, 0x72, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x3d, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x78, 0x68, 0x72, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x28, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x2e, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x58, + 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x58, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, + 0x22, 0x4d, 0x53, 0x58, 0x4d, 0x4c, 0x32, 0x2e, 0x58, 0x4d, 0x4c, 0x48, + 0x54, 0x54, 0x50, 0x2e, 0x33, 0x2e, 0x30, 0x22, 0x29, 0x29, 0x3b, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, + 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x28, 0x77, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x6e, 0x65, + 0x77, 0x20, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x6e, 0x65, 0x77, + 0x20, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x58, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x28, 0x22, 0x4d, 0x53, 0x58, 0x4d, 0x4c, 0x32, 0x2e, 0x58, + 0x4d, 0x4c, 0x48, 0x54, 0x54, 0x50, 0x2e, 0x33, 0x2e, 0x30, 0x22, 0x29, + 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, + 0x55, 0x49, 0x44, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x2f, 0x2f, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x72, + 0x66, 0x63, 0x2f, 0x72, 0x66, 0x63, 0x34, 0x31, 0x32, 0x32, 0x2e, 0x74, + 0x78, 0x74, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, + 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x76, 0x61, 0x72, 0x20, 0x68, 0x65, 0x78, 0x44, 0x69, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x3d, 0x20, 0x22, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x22, 0x3b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, + 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, + 0x3c, 0x20, 0x33, 0x36, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x5b, 0x69, 0x5d, 0x20, + 0x3d, 0x20, 0x68, 0x65, 0x78, 0x44, 0x69, 0x67, 0x69, 0x74, 0x73, 0x2e, + 0x73, 0x75, 0x62, 0x73, 0x74, 0x72, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, + 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, + 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x29, 0x20, 0x2a, 0x20, 0x30, 0x78, + 0x31, 0x30, 0x29, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x5b, 0x31, + 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x34, 0x22, 0x3b, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, 0x31, 0x32, 0x2d, 0x31, 0x35, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x68, 0x69, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x74, 0x6f, + 0x20, 0x30, 0x30, 0x31, 0x30, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, + 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x3d, 0x20, 0x68, 0x65, 0x78, 0x44, 0x69, + 0x67, 0x69, 0x74, 0x73, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x74, 0x72, 0x28, + 0x28, 0x73, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x26, 0x20, 0x30, 0x78, 0x33, + 0x29, 0x20, 0x7c, 0x20, 0x30, 0x78, 0x38, 0x2c, 0x20, 0x31, 0x29, 0x3b, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, 0x36, 0x2d, + 0x37, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x71, 0x5f, 0x68, 0x69, 0x5f, 0x61, 0x6e, + 0x64, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x20, 0x74, + 0x6f, 0x20, 0x30, 0x31, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x5b, + 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x5b, 0x31, 0x33, 0x5d, 0x20, 0x3d, + 0x20, 0x73, 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x5b, 0x32, + 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x2d, 0x22, 0x3b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x75, 0x75, 0x69, 0x64, 0x20, + 0x3d, 0x20, 0x73, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x22, 0x22, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x75, 0x75, 0x69, 0x64, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, + 0x68, 0x69, 0x73, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, 0x70, 0x6f, 0x6c, 0x6c, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, + 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, + 0x6f, 0x6e, 0x72, 0x65, 0x61, 0x64, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x65, 0x6c, 0x66, + 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d, + 0x3d, 0x20, 0x34, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, + 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x32, 0x30, 0x30, 0x29, + 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, + 0x65, 0x6c, 0x66, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, 0x70, 0x6f, 0x6c, 0x6c, + 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x6f, 0x6e, 0x52, 0x63, 0x76, 0x64, 0x28, + 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, + 0x6c, 0x6c, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, 0x70, 0x6f, 0x6c, + 0x6c, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x20, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x20, 0x77, 0x65, 0x20, + 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, + 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x28, 0x22, 0x47, 0x45, 0x54, 0x22, 0x2c, 0x20, 0x73, + 0x65, 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, + 0x20, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x20, 0x3f, 0x20, 0x22, 0x3f, 0x22, 0x20, 0x2b, 0x20, 0x73, 0x65, 0x6c, + 0x66, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x3a, 0x20, 0x22, 0x22, + 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, + 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, + 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x57, 0x69, 0x74, 0x68, 0x27, 0x2c, + 0x20, 0x27, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x6f, 0x6d, + 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x65, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, + 0x22, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x2c, 0x20, 0x22, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x22, 0x29, 0x3b, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x6f, + 0x6d, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x65, 0x6e, 0x64, + 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, 0x6f, 0x70, 0x65, + 0x6e, 0x28, 0x22, 0x50, 0x4f, 0x53, 0x54, 0x22, 0x2c, 0x20, 0x73, 0x65, + 0x6c, 0x66, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, 0x20, + 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, + 0x3f, 0x20, 0x22, 0x3f, 0x22, 0x20, 0x2b, 0x20, 0x73, 0x65, 0x6c, 0x66, + 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x3a, 0x20, 0x22, 0x22, 0x29, + 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, + 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x57, 0x69, 0x74, 0x68, 0x27, 0x2c, + 0x20, 0x27, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, 0x73, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x53, 0x43, 0x58, 0x4d, 0x4c, 0x2d, + 0x4e, 0x61, 0x6d, 0x65, 0x27, 0x2c, 0x20, 0x22, 0x64, 0x6f, 0x6e, 0x65, + 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x22, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, + 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x4e, 0x55, 0x4c, 0x4c, 0x29, 0x3b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x6f, + 0x73, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, + 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x28, + 0x22, 0x50, 0x4f, 0x53, 0x54, 0x22, 0x2c, 0x20, 0x73, 0x65, 0x6c, 0x66, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x2b, 0x20, 0x28, 0x73, + 0x65, 0x6c, 0x66, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x3f, 0x20, + 0x22, 0x3f, 0x22, 0x20, 0x2b, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x20, 0x3a, 0x20, 0x22, 0x22, 0x29, 0x29, 0x3b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, + 0x68, 0x72, 0x2e, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x57, 0x69, 0x74, + 0x68, 0x27, 0x2c, 0x20, 0x27, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, + 0x2e, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x27, 0x58, 0x2d, 0x53, 0x43, 0x58, + 0x4d, 0x4c, 0x2d, 0x4e, 0x61, 0x6d, 0x65, 0x27, 0x2c, 0x20, 0x6e, 0x61, + 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, + 0x20, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, + 0x65, 0x6c, 0x66, 0x2e, 0x78, 0x68, 0x72, 0x2e, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x28, 0x27, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, + 0x70, 0x65, 0x27, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, + 0x78, 0x68, 0x72, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x64, 0x61, 0x74, + 0x61, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, + 0x73, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x6e, 0x67, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x3b, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x65, 0x20, + 0x61, 0x6c, 0x73, 0x6f, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x72, 0x61, 0x77, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x75, 0x67, 0x6c, 0x61, 0x73, + 0x63, 0x72, 0x6f, 0x63, 0x6b, 0x66, 0x6f, 0x72, 0x64, 0x2f, 0x4a, 0x53, + 0x4f, 0x4e, 0x2d, 0x6a, 0x73, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x2f, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x2e, 0x6a, 0x73, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x69, 0x6e, + 0x67, 0x79, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x68, 0x65, 0x72, 0x65, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, + 0x70, 0x65, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x3d, + 0x3d, 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x29, + 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, + 0x20, 0x73, 0x65, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x77, 0x69, 0x6c, + 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x6f, + 0x6e, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x20, 0x61, 0x73, 0x20, 0x74, + 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x79, 0x63, 0x6c, + 0x65, 0x73, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x4a, 0x53, + 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, + 0x28, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, + 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x69, 0x66, 0x20, 0x28, 0x69, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x76, + 0x61, 0x6c, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x6f, 0x66, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x64, 0x3a, 0x20, 0x76, 0x61, 0x6c, + 0x2e, 0x69, 0x64, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x74, 0x61, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x76, + 0x61, 0x6c, 0x2e, 0x74, 0x61, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x2c, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x2e, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x73, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, + 0x70, 0x65, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x20, 0x3d, 0x3d, 0x3d, + 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x29, 0x20, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, + 0x28, 0x73, 0x65, 0x65, 0x6e, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, + 0x66, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x29, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x73, 0x65, 0x65, 0x6e, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, + 0x76, 0x61, 0x6c, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x7d, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x74, 0x68, 0x69, + 0x6e, 0x67, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x6f, 0x73, + 0x74, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x2c, 0x20, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x22, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, + 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x77, 0x68, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x68, 0x74, 0x6d, + 0x6c, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x76, + 0x61, 0x72, 0x20, 0x69, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x29, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x28, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, + 0x20, 0x3f, 0x20, 0x6f, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x6f, 0x66, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x20, 0x3a, 0x20, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, 0x26, 0x26, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x20, 0x3d, 0x3d, + 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x20, 0x26, + 0x26, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x2e, 0x6e, + 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, + 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x26, 0x26, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x2e, 0x6e, 0x6f, 0x64, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x3d, 0x3d, 0x3d, 0x22, 0x73, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x22, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x2f, 0x2f, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x77, 0x68, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, + 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x69, + 0x73, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x29, 0x7b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x28, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x6f, 0x66, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, + 0x20, 0x3f, 0x20, 0x6f, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x6f, 0x66, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x3a, + 0x20, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x20, + 0x26, 0x26, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, + 0x20, 0x26, 0x26, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, + 0x2e, 0x6d, 0x65, 0x6e, 0x75, 0x62, 0x61, 0x72, 0x20, 0x3d, 0x3d, 0x3d, + 0x20, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x20, 0x20, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, + 0x09, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x3e, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, + 0x74, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x22, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x64, 0x6f, 0x6d, 0x4c, 0x6f, 0x61, + 0x64, 0x65, 0x64, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x73, 0x63, + 0x78, 0x6d, 0x6c, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x43, 0x6f, + 0x6d, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x24, 0x7b, 0x73, 0x63, 0x78, 0x6d, + 0x6c, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x49, 0x64, 0x7d, 0x22, + 0x29, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x55, 0x52, 0x4c, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x6f, + 0x6e, 0x52, 0x63, 0x76, 0x64, 0x20, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x58, 0x4d, 0x4c, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, + 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, + 0x22, 0x58, 0x2d, 0x53, 0x43, 0x58, 0x4d, 0x4c, 0x2d, 0x54, 0x79, 0x70, + 0x65, 0x22, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x22, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, + 0x20, 0x64, 0x6f, 0x6d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x20, 0x3d, + 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, + 0x22, 0x58, 0x2d, 0x53, 0x43, 0x58, 0x4d, 0x4c, 0x2d, 0x58, 0x50, 0x61, + 0x74, 0x68, 0x22, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x22, 0x2f, 0x68, 0x74, + 0x6d, 0x6c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x3b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x64, 0x6f, 0x6d, + 0x41, 0x74, 0x74, 0x72, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x22, 0x58, 0x2d, 0x53, 0x43, 0x58, + 0x4d, 0x4c, 0x2d, 0x41, 0x74, 0x74, 0x72, 0x22, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, + 0x28, 0x64, 0x6f, 0x6d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2c, 0x20, + 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x2c, 0x20, 0x58, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x2e, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x45, 0x44, 0x5f, + 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x2c, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x66, + 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x2e, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, 0x6d, + 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x73, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x28, 0x69, + 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, + 0x61, 0x72, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x58, 0x4d, 0x4c, 0x2e, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x2c, 0x20, 0x74, + 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x28, 0x74, 0x79, + 0x70, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x22, 0x3a, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, + 0x2e, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x42, 0x65, 0x66, 0x6f, 0x72, + 0x65, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, + 0x2e, 0x66, 0x69, 0x72, 0x73, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x6c, 0x61, 0x73, + 0x74, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, + 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, + 0x6e, 0x6f, 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x22, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x73, 0x69, + 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x69, 0x6e, + 0x73, 0x65, 0x72, 0x74, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x28, 0x6e, + 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x6e, 0x65, 0x78, 0x74, 0x73, + 0x69, 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x69, + 0x6e, 0x73, 0x65, 0x72, 0x74, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x28, + 0x6e, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x6e, + 0x65, 0x78, 0x74, 0x53, 0x69, 0x62, 0x6c, 0x69, 0x6e, 0x67, 0x29, 0x3b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x6e, 0x6f, 0x64, + 0x65, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x22, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, + 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4e, + 0x6f, 0x64, 0x65, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x68, + 0x69, 0x6c, 0x64, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x61, 0x64, 0x64, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, + 0x73, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x28, 0x64, 0x6f, 0x6d, 0x41, 0x74, 0x74, 0x72, 0x2c, 0x20, 0x6e, 0x6f, + 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, + 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x63, 0x68, 0x69, 0x6c, 0x64, + 0x72, 0x65, 0x6e, 0x22, 0x3a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x69, 0x74, + 0x65, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x4e, + 0x6f, 0x64, 0x65, 0x73, 0x28, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, + 0x6d, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x68, 0x69, 0x6c, + 0x64, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x66, 0x69, 0x72, 0x73, 0x74, + 0x43, 0x68, 0x69, 0x6c, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x61, + 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x6e, + 0x6f, 0x64, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x29, 0x3b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x73, 0x63, 0x78, 0x6d, 0x6c, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, + 0x70, 0x6f, 0x6c, 0x6c, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x63, + 0x78, 0x6d, 0x6c, 0x3b, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x77, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6f, 0x6e, 0x62, 0x65, 0x66, 0x6f, 0x72, + 0x65, 0x75, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x29, 0x20, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x63, 0x78, 0x6d, 0x6c, 0x2e, + 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x28, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x27, 0x59, 0x6f, 0x75, 0x20, 0x68, 0x61, + 0x76, 0x65, 0x20, 0x75, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x64, 0x20, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x21, 0x27, 0x3b, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x7d, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x29, 0x3b, 0x0a, + 0x09, 0x09, 0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0a, + 0x0a, 0x09, 0x3c, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x3e, 0x0a, 0x09, 0x3c, + 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e, + 0x0a, 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0x0a }; unsigned int template_xhtml_invoker_html_len = 8253; diff --git a/src/uscxml/transform/ChartToCPP.cpp b/src/uscxml/transform/ChartToCPP.cpp.todo index 6b78374..6b78374 100644 --- a/src/uscxml/transform/ChartToCPP.cpp +++ b/src/uscxml/transform/ChartToCPP.cpp.todo diff --git a/src/uscxml/transform/ChartToCPP.h b/src/uscxml/transform/ChartToCPP.h.todo index 8cdebb9..8cdebb9 100644 --- a/src/uscxml/transform/ChartToCPP.h +++ b/src/uscxml/transform/ChartToCPP.h.todo diff --git a/src/uscxml/transform/ChartToFSM.cpp b/src/uscxml/transform/ChartToFSM.cpp index 8597211..38262db 100644 --- a/src/uscxml/transform/ChartToFSM.cpp +++ b/src/uscxml/transform/ChartToFSM.cpp @@ -36,21 +36,22 @@ #define UNDECIDABLE 2147483647 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) -#define DUMP_STATS(nrTrans) \ +#define DUMP_STATS(nrTrans, disregardTime) \ uint64_t now = tthread::chrono::system_clock::now(); \ -if (now - _lastTimeStamp > 1000) { \ +if (now - _lastTimeStamp > 1000 || disregardTime) { \ std::cerr << "## Transition: " << _perfTransUsed << " / " << _perfTransTotal << " [" << _perfTransProcessed << "/sec]"; \ if (nrTrans > 0) { \ std::cerr << " - 2**" << nrTrans << " = " << pow(2.0, static_cast<double>(nrTrans)); \ } \ std::cerr << std::endl; \ - std::cerr << "## State : " << _globalConf.size() << " [" << _perfStatesProcessed << "/sec]" << std::endl; \ + std::cerr << "## State : " << _globalConf.size() << " found / " << _perfStackSize << " stacked / " << _perfStatesTotal << " seen [" << _perfStatesProcessed << "/sec]" << std::endl; \ std::cerr << "## Microstep : " << _perfMicroStepTotal << " [" << _perfMicroStepProcessed << "/sec]" << std::endl; \ std::cerr << "## Cached : " << _perfStatesCachedTotal << " [" << _perfStatesCachedProcessed << "/sec]" << std::endl; \ std::cerr << "## Skipped : " << _perfStatesSkippedTotal << " [" << _perfStatesSkippedProcessed << "/sec]" << std::endl; \ std::cerr << "## Queues : " << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << " / " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; \ + std::cerr << "toFSM: "; \ std::cerr << _perfTransUsed << ", " << _perfTransTotal << ", " << _perfTransProcessed << ", "; \ - std::cerr << _globalConf.size() << ", " << _perfStatesProcessed << ", "; \ + std::cerr << _globalConf.size() << ", " << _perfStackSize << ", " << _perfStatesTotal << ", " << _perfStatesProcessed << ", "; \ std::cerr << _perfMicroStepTotal << ", " << _perfMicroStepProcessed << ", "; \ std::cerr << _perfStatesCachedTotal << ", " << _perfStatesCachedProcessed << ", " << _perfStatesSkippedTotal << ", " << _perfStatesSkippedProcessed << ", "; \ std::cerr << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << ", " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; \ @@ -60,7 +61,8 @@ if (now - _lastTimeStamp > 1000) { \ _perfStatesCachedProcessed = 0; \ _perfStatesSkippedProcessed = 0; \ _perfMicroStepProcessed = 0; \ - _lastTimeStamp = now; \ + if (!disregardTime)\ + _lastTimeStamp = now; \ } //std::cerr << "Q: " << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << " / " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; @@ -89,6 +91,83 @@ for (int i = 0; i < contents.size(); i++) { \ } \ std::cerr << ")"; +std::list<std::set<Element<std::string> > > Complexity::getAllConfigurations(const Arabica::DOM::Element<std::string>& root) { + + std::list<std::set<Element<std::string> > > allConfigurations; + std::string nsPrefix = (root.getPrefix().size() > 0 ? root.getPrefix() + ":" : ""); + std::string localName = root.getLocalName(); + bool isAtomic = true; + + NodeList<std::string> children = root.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE) + continue; + Element<std::string> childElem(children.item(i)); + if (childElem.getTagName() == nsPrefix + "state" || + childElem.getTagName() == nsPrefix + "parallel" || + childElem.getTagName() == nsPrefix + "final") { + // nested child state + std::list<std::set<Element<std::string> > > nestedConfigurations = getAllConfigurations(childElem); + isAtomic = false; + if (localName == "parallel" && allConfigurations.size() > 0) { + // for every nested configuration, every new nested is valid + std::list<std::set<Element<std::string> > > combinedConfigurations; + for (std::list<std::set<Element<std::string> > >::iterator existIter = allConfigurations.begin(); existIter != allConfigurations.end(); existIter++) { + std::set<Element<std::string> > existingConfig = *existIter; + + for (std::list<std::set<Element<std::string> > >::iterator newIter = nestedConfigurations.begin(); newIter != nestedConfigurations.end(); newIter++) { + + std::set<Element<std::string> > newConfig = *newIter; + std::set<Element<std::string> > combinedSet; + combinedSet.insert(existingConfig.begin(), existingConfig.end()); + combinedSet.insert(newConfig.begin(), newConfig.end()); + + combinedConfigurations.push_back(combinedSet); + } + } + allConfigurations = combinedConfigurations; + } else { + // just add nested configurations and this + for (std::list<std::set<Element<std::string> > >::iterator newIter = nestedConfigurations.begin(); newIter != nestedConfigurations.end(); newIter++) { + std::set<Element<std::string> > newConfig = *newIter; + if (localName != "scxml") + newConfig.insert(root); + allConfigurations.push_back(newConfig); + } + } + } + } + + if (isAtomic) { + std::set<Element<std::string> > soleConfig; + soleConfig.insert(root); + allConfigurations.push_back(soleConfig); + } + return allConfigurations; +} + +std::map<size_t, size_t> Complexity::getTransitionHistogramm(const Arabica::DOM::Element<std::string>& root) { + std::map<size_t, size_t> histogram; + std::string nameSpace; + + std::list<std::set<Element<std::string> > > allConfig = Complexity::getAllConfigurations(root); + + // for every legal configuration, count the transitions + for (std::list<std::set<Element<std::string> > >::iterator confIter = allConfig.begin(); confIter != allConfig.end(); confIter++) { + NodeSet<std::string> configNodeSet; + std::set<Element<std::string> > config = *confIter; + for (std::set<Element<std::string> >::iterator elemIter = config.begin(); elemIter != config.end(); elemIter++) { + configNodeSet.push_back(*elemIter); + if (nameSpace.size() == 0 && elemIter->getPrefix().size() > 0) + nameSpace = elemIter->getPrefix() + ":"; + } + NodeSet<std::string> transitions = InterpreterImpl::filterChildElements(nameSpace + "transition", configNodeSet); + histogram[transitions.size()]++; + } + + return histogram; +} + uint64_t Complexity::stateMachineComplexity(const Arabica::DOM::Element<std::string>& root, Variant variant) { Complexity complexity = calculateStateMachineComplexity(root); @@ -183,11 +262,15 @@ ChartToFSM::ChartToFSM(const Interpreter& other) { cloneFrom(other.getImpl()); + _transitionsFromTree = true; + _keepInvalidTransitions = false; _lastTimeStamp = tthread::chrono::system_clock::now(); _perfTransProcessed = 0; _perfTransTotal = 0; _perfTransUsed = 0; + _perfStatesTotal = 0; _perfStatesProcessed = 0; + _perfStackSize = 0; _perfStatesSkippedProcessed = 0; _perfStatesSkippedTotal = 0; _perfStatesCachedProcessed = 0; @@ -195,9 +278,13 @@ ChartToFSM::ChartToFSM(const Interpreter& other) { _perfMicroStepProcessed = 0; _perfMicroStepTotal = 0; + if (envVarIEquals("USCXML_TRANSFORM_TRANS_FROM", "powerset")) + _transitionsFromTree = false; + _start = NULL; _currGlobalTransition = NULL; - + _transTree = NULL; + _lastStateIndex = 0; _lastActiveIndex = 0; _lastTransIndex = 0; @@ -207,10 +294,6 @@ ChartToFSM::ChartToFSM(const Interpreter& other) { _doneEventRaiseTolerance = 0; _skipEventChainCalculations = false; - // create a _flatDoc for the FSM - DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); - _flatDoc = domFactory.createDocument(other.getDocument().getNamespaceURI(), "", 0); - addMonitor(this); } @@ -235,14 +318,38 @@ ChartToFSM::~ChartToFSM() { } Document<std::string> ChartToFSM::getDocument() const { - return _flatDoc; + if (_flatDoc) + return _flatDoc; + return _document; } InterpreterState ChartToFSM::interpret() { + // create a _flatDoc for the FSM + DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + _flatDoc = domFactory.createDocument(_document.getNamespaceURI(), "", 0); + init(); setupIOProcessors(); + { + std::list<std::set<Element<std::string> > > allConfig = Complexity::getAllConfigurations(_scxml); + for (std::list<std::set<Element<std::string> > >::iterator confIter = allConfig.begin(); confIter != allConfig.end(); confIter++) { + std::string seperator; + NodeSet<std::string> configNodeSet; + std::set<Element<std::string> > config = *confIter; + for (std::set<Element<std::string> >::iterator elemIter = config.begin(); elemIter != config.end(); elemIter++) { +// std::cerr << seperator << ATTR((*elemIter), "id"); + seperator = ","; + configNodeSet.push_back(*elemIter); + } + assert(isLegalConfiguration(configNodeSet)); +// std::cerr << std::endl; + } + } + std::map<size_t, size_t> histoGramm = Complexity::getTransitionHistogramm(_scxml); +// abort(); + uint64_t complexity = Complexity::stateMachineComplexity(_scxml) + 1; std::cerr << "Approximate Complexity: " << complexity << std::endl; std::cerr << "Approximate Active Complexity: " << Complexity::stateMachineComplexity(_scxml, Complexity::IGNORE_HISTORY_AND_NESTED_DATA) + 1 << std::endl; @@ -288,7 +395,7 @@ InterpreterState ChartToFSM::interpret() { } _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - _alreadyFlat = (HAS_ATTR(_scxml, "flat") && DOMUtils::attributeIsTrue(ATTR(_scxml, "flat"))); + _alreadyFlat = (HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat"))); if (_alreadyFlat) { reassembleFromFlat(); @@ -333,10 +440,7 @@ InterpreterState ChartToFSM::interpret() { // std::cout << _scxml << std::endl; - indexTransitions(_scxml); - - // reverse indices for most prior to be in front - std::reverse(indexedTransitions.begin(), indexedTransitions.end()); + indexTransitions(); // add initial transitions as least prior for (int i = 0; i < initialTransitions.size() ; i++) { @@ -345,6 +449,8 @@ InterpreterState ChartToFSM::interpret() { // set index attribute for transitions for (int i = 0; i < indexedTransitions.size(); i++) { +// std::cerr << toStr(i) << ":" << (HAS_ATTR(indexedTransitions[i], "line_start") ? ATTR(indexedTransitions[i], "line_start") : ""); +// std::cerr << "\t" << DOMUtils::xPathForNode(indexedTransitions[i]) << std::endl; indexedTransitions[i].setAttribute("index", toStr(i)); } @@ -385,6 +491,8 @@ InterpreterState ChartToFSM::interpret() { explode(); + DUMP_STATS(0, true); + #if 0 // print set of global configurations for(std::map<std::string, GlobalState*>::iterator globalConfIter = _globalConf.begin(); @@ -400,8 +508,8 @@ InterpreterState ChartToFSM::interpret() { std::cerr << "Internal Queue: " << _maxEventRaisedChain << std::endl; std::cerr << "External Queue: " << _maxEventSentChain << std::endl; - if (complexity < _globalConf.size()) - throw std::runtime_error("Upper bound for states exceeded"); +// if (complexity < _globalConf.size()) +// throw std::runtime_error("Upper bound for states exceeded"); return _state; } @@ -476,6 +584,7 @@ void ChartToFSM::internalDoneSend(const Arabica::DOM::Element<std::string>& stat if (parentIsScxmlState(state)) return; +// return; // std::cerr << "internalDoneSend: " << state << std::endl; // create onentry with a raise element @@ -505,7 +614,7 @@ void ChartToFSM::internalDoneSend(const Arabica::DOM::Element<std::string>& stat raise.setAttribute("event", "done.state." + ATTR_CAST(state.getParentNode(), "id")); // parent?! GlobalTransition::Action action; - action.onEntry = onentry; + action.raiseDone = onentry; // HERE! _currGlobalTransition->actions.push_back(action); if (!_skipEventChainCalculations && @@ -517,10 +626,10 @@ void ChartToFSM::internalDoneSend(const Arabica::DOM::Element<std::string>& stat static bool isSuperset(const GlobalTransition* t1, const GlobalTransition* t2) { bool isSuperset = true; - + if (t1->transitionRefs.size() >= t2->transitionRefs.size()) return false; - + NodeSet<std::string> t1Trans = t1->getTransitions(); NodeSet<std::string> t2Trans = t2->getTransitions(); @@ -551,6 +660,25 @@ bool ChartToFSM::filterSameState(const NodeSet<std::string>& transitions) { return true; } +static bool filterSameHierarchy(const NodeSet<std::string>& transitions) { + for (unsigned int i = 0; i < transitions.size(); i++) { + Node<std::string> t1 = transitions[i]; + Node<std::string> p1 = InterpreterImpl::getParentState(t1); + for (unsigned int j = i + 1; j < transitions.size(); j++) { + Node<std::string> t2 = transitions[j]; + Node<std::string> p2 = InterpreterImpl::getParentState(t2); + while(p2) { + if (p1 == p2) { + return false; + } + p2 = p2.getParentNode(); + } + } + } + return true; +} + + static bool filterChildEnabled(const NodeSet<std::string>& transitions) { // drop any transition that is already enabled by a child NodeSet<std::string> filteredTransitions; @@ -634,6 +762,19 @@ void ChartToFSM::annotateRaiseAndSend(const Arabica::DOM::Element<std::string>& } } +void ChartToFSM::indexTransitions() { + indexTransitions(_scxml); + + size_t index = 0; + for (std::vector<Arabica::DOM::Element<std::string> >::iterator transIter = indexedTransitions.begin(); transIter != indexedTransitions.end(); transIter++) { + transIter->setAttribute("priority", toStr(index)); + index++; + } + + // reverse indices for most prior to be in front + std::reverse(indexedTransitions.begin(), indexedTransitions.end()); +} + void ChartToFSM::indexTransitions(const Arabica::DOM::Element<std::string>& root) { // breadth first traversal of transitions Arabica::XPath::NodeSet<std::string> levelTransitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", root); @@ -676,7 +817,7 @@ template <typename T> bool PtrComp(const T * const & a, const T * const & b) { /** * subset only removes transitions without cond -> superset will always be enabled */ -bool hasUnconditionalSuperset (GlobalTransition* first, GlobalTransition* second) { +bool hasUnconditionalSuperset(GlobalTransition* first, GlobalTransition* second) { NodeSet<std::string> firstTransitions = first->getTransitions(); NodeSet<std::string> secondTransitions = first->getTransitions(); @@ -694,6 +835,9 @@ bool hasUnconditionalSuperset (GlobalTransition* first, GlobalTransition* second return false; //second can't be removed } +/** + * earlier transition is conditionless for same event + */ bool hasEarlierUnconditionalMatch(GlobalTransition* first, GlobalTransition* second) { if (first->eventDesc == second->eventDesc) { if (first->condition.size() == 0) @@ -702,9 +846,43 @@ bool hasEarlierUnconditionalMatch(GlobalTransition* first, GlobalTransition* sec return false; } -// for some reason, unique is not quite up to the task -std::list<GlobalTransition*> reapplyUniquePredicates(std::list<GlobalTransition*> list) { +std::list<GlobalTransition*> redundantRemove(std::list<GlobalTransition*> list) { +#if 1 + std::list<GlobalTransition*>::iterator outerIter; + std::list<GlobalTransition*>::iterator innerIter; + + outerIter = list.begin(); + while(outerIter != list.end()) { + innerIter = outerIter; + + while(innerIter != list.end()) { + if (innerIter == outerIter) { + innerIter++; + continue; + } + + GlobalTransition* t1 = *outerIter; + GlobalTransition* t2 = *innerIter; + + if (hasUnconditionalSuperset(t1, t2)) { + list.erase(innerIter++); + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { + list.erase(outerIter++); + break; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + list.erase(innerIter++); + continue; + } + innerIter++; + } + + outerIter++; + } +#else + for (std::list<GlobalTransition*>::iterator outerIter = list.begin(); outerIter != list.end(); outerIter++) { @@ -719,25 +897,425 @@ std::list<GlobalTransition*> reapplyUniquePredicates(std::list<GlobalTransition* GlobalTransition* t2 = *innerIter; if (hasUnconditionalSuperset(t1, t2)) { - list.erase(outerIter++); + innerIter = list.erase(innerIter); continue; } else if (hasUnconditionalSuperset(t2, t1)) { - list.erase(innerIter++); + outerIter = list.erase(outerIter); continue; } if (hasEarlierUnconditionalMatch(t1, t2)) { - list.erase(innerIter++); + innerIter = list.erase(innerIter); continue; } } } +#endif + return list; +} + +std::list<GlobalTransition*> redundantMark(std::list<GlobalTransition*> list) { +#if 1 + std::list<GlobalTransition*>::iterator outerIter; + std::list<GlobalTransition*>::iterator innerIter; + outerIter = list.begin(); + while(outerIter != list.end()) { + innerIter = outerIter; + + while(innerIter != list.end()) { + if (innerIter == outerIter) { + innerIter++; + continue; + } + + GlobalTransition* t1 = *outerIter; + GlobalTransition* t2 = *innerIter; + + if (!t1->isValid || !t2->isValid) { + innerIter++; + continue; + } + + if (hasUnconditionalSuperset(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Unconditional superset"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + innerIter++; + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { + t1->isValid = false; + t1->invalidMsg = "Unconditional superset"; + t1->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + outerIter++; + break; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Earlier unconditional match"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_MATCH; + innerIter++; + continue; + } + innerIter++; + } + + outerIter++; + } + +#else + + for (std::list<GlobalTransition*>::iterator outerIter = list.begin(); + outerIter != list.end(); + outerIter++) { + for (std::list<GlobalTransition*>::iterator innerIter = outerIter; + innerIter != list.end(); + innerIter++) { + + if (innerIter == outerIter) + continue; + + GlobalTransition* t1 = *outerIter; + GlobalTransition* t2 = *innerIter; + + if (!t1->isValid || !t2->isValid) + continue; + + if (hasUnconditionalSuperset(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Unconditional superset"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { + t1->isValid = false; + t1->invalidMsg = "Unconditional superset"; + t1->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + continue; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Earlier unconditional match"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_MATCH; + continue; + } + } + } +#endif return list; } -void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap) { + +void TransitionTreeNode::dump(int indent) { + std::string padding; + for (int i = 0; i + 1 < indent; i++) { + padding += "| "; + } + if (indent > 0) + padding += "|-"; + + std::string typeString; + switch (type) { + case TYPE_NESTED: + typeString = "NESTED"; break; + case TYPE_PARALLEL: + typeString = "PARALLEL"; break; + case TYPE_TRANSITION: + typeString = "TRANSITION"; break; + case TYPE_UNDEFINED: + typeString = "UNDEFINED"; break; + break; + default: + break; + } + + + if (transition) { + std::cerr << padding << "t" << ATTR(transition, "index") << " " << typeString << ": "; +// std::cerr << (prevTransition != NULL ? " (" + prevTransition->nodeId + ") <-" : ""); + std::cerr << "[" << nodeId << "]"; +// std::cerr << (nextTransition != NULL ? " -> (" + nextTransition->nodeId + ")" : ""); + std::cerr << std::endl; + } else { + std::cerr << padding << ATTR(state, "id") << " " << typeString << ": " << "[" << nodeId << "]"; +// std::cerr << (firstTransition != NULL ? " -> " + firstTransition->nodeId : ""); + std::cerr << std::endl; + } + + for (std::list<TransitionTreeNode*>::iterator childIter = children.begin(); childIter != children.end(); childIter++) { + (*childIter)->dump(indent + 1); + } +} + +void ChartToFSM::getPotentialTransitionsForConfFromTree(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap) { + if (_transTree == NULL) { + _transTree = buildTransTree(_scxml, "0"); +// _transTree->dump(); + } + std::string seperator; + + +// std::cerr << "--- "; + + // recursion start + std::set<TransitionTreeNode*> transLeafs; + + for (int i = 0; i < conf.size(); i++) { + DUMP_STATS(conf.size(), false); + + Element<std::string> confElem(conf[i]); + assert(_stateToTransTreeNode.find(confElem) != _stateToTransTreeNode.end()); + TransitionTreeNode* node = _stateToTransTreeNode[confElem]; + if (node->firstState == NULL) { // a leaf - ignore intermediates + // ascend to the first parent with transitions but stop at parallel nodes + while(node != NULL && node->firstTransition == NULL) { + if (node->parent && node->parent->type == TransitionTreeNode::TYPE_PARALLEL) + break; + node = node->parent; + } + if (node != NULL) { + transLeafs.insert(node); + } else { + //std::cerr << ATTR(confElem, "id") << " does not cause transitions" << std::endl; + } + } + } + + std::list<std::set<TransitionTreeNode*> > stack; + stack.push_back(transLeafs); // push follow-up configurations onto stack + + while (stack.size() > 0) { + // pop from front of stack + std::set<TransitionTreeNode*> stateList = stack.front(); + stack.pop_front(); + + DUMP_STATS(conf.size(), false); + +#if 0 + seperator = ""; + std::cerr << "Current set: "; + for (std::set<TransitionTreeNode*>::iterator transIter = stateList.begin(); transIter != stateList.end(); transIter++) { + std::cerr << seperator << (*transIter)->nodeId; + seperator = ", "; + } + std::cerr << std::endl; +#endif + + /* + * TransNodes contains a set of lists of transitions. + * In the inner stack we build every possible combination + * of picking at-most one from each list. + */ + + /* create global transitions for every n-tuple in current set of lists */ + std::list<std::pair<std::set<TransitionTreeNode*>, std::set<TransitionTreeNode*> > > innerStack; + innerStack.push_back(std::make_pair(std::set<TransitionTreeNode*>(), stateList)); + + while(innerStack.size() > 0) { + + // picking at-most one from each list + std::set<TransitionTreeNode*> remainingStates = innerStack.front().second; + std::set<TransitionTreeNode*> fixedTransitions = innerStack.front().first; + innerStack.pop_front(); + + if (remainingStates.size() > 0) { + // iterate for each first element fixed + TransitionTreeNode* firstRemainingState = *remainingStates.begin(); + remainingStates.erase(remainingStates.begin()); + + if (firstRemainingState->firstTransition == NULL) { + // no transitions at this state - reenqueue with NULL selection from this + innerStack.push_back(std::make_pair(fixedTransitions, remainingStates)); + continue; + } + + TransitionTreeNode* currTrans = firstRemainingState->firstTransition; + + // choose none from firstList + innerStack.push_back(std::make_pair(fixedTransitions, remainingStates)); + + while(currTrans != NULL) { + std::set<TransitionTreeNode*> fixedAndThis(fixedTransitions); + fixedAndThis.insert(currTrans); + innerStack.push_back(std::make_pair(fixedAndThis, remainingStates)); + currTrans = currTrans->nextTransition; + } + } else { + DUMP_STATS(conf.size(), false); + + if (fixedTransitions.size() > 0) { + + _perfTransTotal++; + _perfTransProcessed++; + + NodeSet<std::string> fixed; + +#if 0 + seperator = ""; + for (std::set<TransitionTreeNode*>::iterator itemIter = fixedTransitions.begin(); itemIter != fixedTransitions.end(); itemIter++) { + TransitionTreeNode* currItem = *itemIter; + std::cerr << seperator << currItem->nodeId; + seperator = ", "; + } + std::cerr << " ## "; +#endif + + seperator = ""; + for (std::set<TransitionTreeNode*>::iterator itemIter = fixedTransitions.begin(); itemIter != fixedTransitions.end(); itemIter++) { + TransitionTreeNode* currItem = *itemIter; + fixed.push_back(currItem->transition); +// std::cerr << seperator << ATTR(currItem->transition, "index"); + seperator = ", "; + } +// std::cerr << std::endl; + + // fixed contains a transiton set! + assert(filterSameState(fixed)); +// assert(filterChildEnabled(fixed)); + assert(filterSameHierarchy(fixed)); + // do not add if they preempt + if (fixed.size() != removeConflictingTransitions(fixed).size()) { +// std::cerr << " - PREEMPTS" << std::endl; + continue; + } + + GlobalTransition* transition = new GlobalTransition(fixed, _dataModel, this); + transition->index = _lastTransIndex++; + +// assert(outMap.find(transition->transitionId) == outMap.end()); + + if (!transition->isValid && !_keepInvalidTransitions) { + delete(transition); +// std::cerr << " - INVALID" << std::endl; + continue; + } + + _perfTransUsed++; + + outMap[transition->transitionId] = transition; +// std::cerr << " - GOOD" << std::endl; + } + } + } + + // create new set of transition lists by moving to parent states + for (std::set<TransitionTreeNode*>::iterator stateIter = stateList.begin(); stateIter != stateList.end(); stateIter++) { + TransitionTreeNode* origState = *stateIter; + TransitionTreeNode* currState = origState; + TransitionTreeNode* parentState = currState->parent; + + /** + * We ascend the current state via its parent and add the parent with transitions. + * However, we break if we reached the top or if we passed a parallel state for + * wich we are not the first child + */ + + while(parentState != NULL) { + if (parentState->type == TransitionTreeNode::TYPE_PARALLEL && parentState->firstState != currState) { + // the first child of the parallel state will continue this transition - we made sure to keep them + break; + } + + if (parentState->firstTransition != NULL) { +// std::cerr << "#### Adding new parent lists for " << origState->nodeId << std::endl; + + std::set<TransitionTreeNode*> newStateList; + newStateList.insert(parentState); + + // add all other states that are not a child of the parent state + for (std::set<TransitionTreeNode*>::iterator newlistIter = stateList.begin(); newlistIter != stateList.end(); newlistIter++) { + TransitionTreeNode* otherState = *newlistIter; + while(otherState != NULL && otherState != parentState) { + otherState = otherState->parent; + } + if (otherState == NULL) + newStateList.insert(*newlistIter); + } + if (newStateList.size() > 0) + stack.push_back(newStateList); + break; + } + + currState = currState->parent; + parentState = currState->parent; + } + } + } +} + +TransitionTreeNode* ChartToFSM::buildTransTree(const Arabica::DOM::Element<std::string>& root, const std::string& nodeId) { + TransitionTreeNode* stateNode = new TransitionTreeNode(); + stateNode->nodeId = nodeId; + stateNode->state = root; + + if (TAGNAME(root) == _nsInfo.xmlNSPrefix + "parallel") { + stateNode->type = TransitionTreeNode::TYPE_PARALLEL; + } else { + stateNode->type = TransitionTreeNode::TYPE_NESTED; + } + + // get all transitions and states from root without recursing + NodeSet<std::string> nested; + nested.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "transition", root)); + nested.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "state", root)); + nested.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "final", root)); + nested.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "parallel", root)); + nested.to_document_order(); + + TransitionTreeNode* lastNode = NULL; + + for (int i = 0; i < nested.size(); i++) { + Element<std::string> nestedElem(nested[i]); + if (TAGNAME(nestedElem) == _nsInfo.xmlNSPrefix + "transition") { + TransitionTreeNode* transNode = new TransitionTreeNode(); + transNode->transition = nestedElem; + transNode->parent = stateNode; + transNode->nodeId = nodeId + "-" + toStr(i); + transNode->type = TransitionTreeNode::TYPE_TRANSITION; + + if (stateNode->firstTransition == NULL) { + stateNode->firstTransition = transNode; + } + stateNode->children.push_back(transNode); + stateNode->lastTransition = transNode; + + if (lastNode != NULL) { + lastNode->nextTransition = transNode; + transNode->prevTransition = lastNode; + } + lastNode = transNode; + + + } else { + TransitionTreeNode* deeperNode = buildTransTree(nestedElem, nodeId + "-" + toStr(i)); + if (stateNode->firstState == NULL) { + stateNode->firstState = deeperNode; + } + + deeperNode->parent = stateNode; + stateNode->children.push_back(deeperNode); + } + } + + _stateToTransTreeNode[root] = stateNode; + + return stateNode; +} + +void ChartToFSM::getPotentialTransitionsForConfFromPowerSet(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap) { // get all transition elements from states in the current configuration NodeSet<std::string> allTransitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", conf); + + { + std::string seperator = ""; + for (int i = 0; i < allTransitions.size(); i++) { + std::cerr << seperator << ATTR_CAST(allTransitions[i], "index"); + seperator=", "; + } + std::cerr << std::endl; + } + +// if (true) { +// outMap = _confToTransitions[""]; +// } if (allTransitions.size() == 0) return; // no transitions @@ -747,6 +1325,12 @@ void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<st int* stack = (int*)malloc((nrElements + 1) * sizeof(int)); memset(stack, 0, (nrElements + 1) * sizeof(int)); + /** + * Powerset is too naive and takes too long! + * We have it up to 500k checks/sec and still 2**30 is + * 1G+ for 30minutes in a single state out of 50k+! + */ + while(1) { // create the power set of all potential transitions - this is expensive! // see: http://www.programminglogic.com/powerset-algorithm-in-c/ @@ -765,7 +1349,7 @@ void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<st break; NodeSet<std::string> transitions; - // std::cerr << globalState->stateId << " [" << nrElements << "]: " << std::endl; +// std::cerr << globalState->stateId << " [" << nrElements << "]: " << std::endl; for (int i = 1; i <= k; i++) { // std::cerr << stack[i] - 1 << ", "; transitions.push_back(allTransitions[stack[i] - 1]); @@ -788,32 +1372,59 @@ void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<st _perfTransTotal++; _perfTransProcessed++; - DUMP_STATS(nrElements); + DUMP_STATS(nrElements, false); - // remove transitions in the same state - if(!filterSameState(transitions)) - continue; - if (dump) DUMP_TRANSSET("after same state filtered"); - - // remove those transitions with a child transition - if(!filterChildEnabled(transitions)) - continue; - if (dump) DUMP_TRANSSET("after child enabled filtered"); + GlobalTransition* transition = NULL; // reduce to conflict-free subset // transitions.to_document_order(); - transitions = removeConflictingTransitions(transitions); - if (dump) DUMP_TRANSSET("after conflicting filtered"); - - // algorithm can never reduce to empty set - assert(transitions.size() > 0); - - // create a GlobalTransition object from the set - GlobalTransition* transition = new GlobalTransition(transitions, _dataModel, this); - if (!transition->isValid) { - // this set of transitions can not be enabled together - delete transition; - continue; + if (!_keepInvalidTransitions) { + // remove transitions in the same state + if(!filterSameState(transitions)) + continue; + if (dump) DUMP_TRANSSET("after same state filtered"); + + // remove those transitions with a child transition +// if(!filterChildEnabled(transitions)) + if(!filterSameHierarchy(transitions)) + continue; + if (dump) DUMP_TRANSSET("after child enabled filtered"); + + transitions = removeConflictingTransitions(transitions); + if (dump) DUMP_TRANSSET("after conflicting filtered"); + // algorithm can never reduce to empty set + assert(transitions.size() > 0); + + // create a GlobalTransition object from the set + transition = new GlobalTransition(transitions, _dataModel, this); + if (!transition->isValid) { + // this set of transitions can not be enabled together + delete transition; + continue; + } + } else { + transition = new GlobalTransition(transitions, _dataModel, this); + + // remove transitions in the same state + if(!filterSameState(transitions)) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::SAME_SOURCE_STATE; + transition->invalidMsg = "Same source state"; + +// } else if(!filterChildEnabled(transitions)) { + } else if(!filterSameHierarchy(transitions)) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::CHILD_ENABLED; + transition->invalidMsg = "Nested transitions"; + } else { + NodeSet<std::string> nonPreemptingTransitions = removeConflictingTransitions(transitions); + if (nonPreemptingTransitions.size() != transitions.size()) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::PREEMPTING_MEMBERS; + transition->invalidMsg = "Preempting members"; + } + } + } // two combinations might have projected onto the same conflict-free set @@ -830,6 +1441,7 @@ void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<st // std::cerr << "New conflict-free subset: " << transition->transitionId << ":" << transition->eventDesc << std::endl; outMap[transition->transitionId] = transition; } +// _confToTransitions[""] = outMap; return; } @@ -853,7 +1465,11 @@ void ChartToFSM::explode() { // append new global states and pop from front while(statesRemaining.size() > 0) { - DUMP_STATS(0); + _perfStackSize = statesRemaining.size(); + _perfStatesTotal++; + _perfStatesProcessed++; + + DUMP_STATS(0, false); GlobalState* globalState = statesRemaining.front().second; _currGlobalTransition = statesRemaining.front().first; @@ -875,8 +1491,6 @@ void ChartToFSM::explode() { continue; // we have already been here } - _perfStatesProcessed++; - _configuration = globalState->getActiveStates(); _alreadyEntered = globalState->getAlreadyEnteredStates(); _historyValue = globalState->getHistoryStates(); @@ -910,7 +1524,12 @@ void ChartToFSM::explode() { } else { // we need to calculate the potential optimal transition sets std::map<std::string, GlobalTransition*> transitionSets; - getPotentialTransitionsForConf(refsToStates(globalState->activeStatesRefs), transitionSets); + // std::cerr << globalState->stateId << std::endl; + if (_transitionsFromTree) { + getPotentialTransitionsForConfFromTree(refsToStates(globalState->activeStatesRefs), transitionSets); + } else { + getPotentialTransitionsForConfFromPowerSet(refsToStates(globalState->activeStatesRefs), transitionSets); + } // reduce and sort transition sets for(std::map<std::string, GlobalTransition*>::iterator transSetIter = transitionSets.begin(); @@ -920,10 +1539,20 @@ void ChartToFSM::explode() { } globalState->sortedOutgoing.sort(PtrComp<GlobalTransition>); - globalState->sortedOutgoing.unique(hasUnconditionalSuperset); - globalState->sortedOutgoing.unique(hasEarlierUnconditionalMatch); +// globalState->sortedOutgoing.unique(hasUnconditionalSuperset); +// globalState->sortedOutgoing.unique(hasEarlierUnconditionalMatch); // unique is not quite like what we need, but it was a start - globalState->sortedOutgoing = reapplyUniquePredicates(globalState->sortedOutgoing); + if (_keepInvalidTransitions) { + globalState->sortedOutgoing = redundantMark(globalState->sortedOutgoing); + } else { +// globalState->sortedOutgoing.unique(hasUnconditionalSuperset); +// globalState->sortedOutgoing.unique(hasEarlierUnconditionalMatch); + globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); + } +// globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); +// globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); +// +// std::cout << globalState->sortedOutgoing.size() << std::endl; assert(_activeConf.find(globalState->activeId) == _activeConf.end()); assert(globalState->activeIndex == -1); @@ -940,11 +1569,15 @@ void ChartToFSM::explode() { GlobalTransition* outgoingTrans = *transIter; outgoingTrans->source = globalState->stateId; + + if (_keepInvalidTransitions && !outgoingTrans->isValid) + continue; + _currGlobalTransition = outgoingTrans; microstep(refsToTransitions(outgoingTrans->transitionRefs)); - assert(isLegalConfiguration(_configuration)); - +// assert(isLegalConfiguration(_configuration)); + _perfMicroStepProcessed++; _perfMicroStepTotal++; @@ -1086,6 +1719,26 @@ void ChartToFSM::beforeEnteringState(Interpreter interpreter, const Arabica::DOM void ChartToFSM::beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing) { } +std::ostream& operator<< (std::ostream& os, const GlobalTransition::Action& action) { + if (action.onEntry) + os << "onEntry: " << action.onEntry; + if (action.onExit) + os << "onExit: " << action.onExit; + if (action.transition) + os << "transition: " << action.transition; + if (action.entered) + os << "entered: " << action.entered; + if (action.exited) + os << "exited: " << action.exited; + if (action.invoke) + os << "invoke: " << action.invoke; + if (action.uninvoke) + os << "uninvoke: " << action.uninvoke; + if (action.raiseDone) + os << "raiseDone: " << action.raiseDone; + return os; +} + GlobalState::GlobalState(const Arabica::XPath::NodeSet<std::string>& activeStates_, const Arabica::XPath::NodeSet<std::string>& alreadyEnteredStates_, // we need to remember for binding=late const std::map<std::string, Arabica::XPath::NodeSet<std::string> >& historyStates_, @@ -1181,6 +1834,11 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t bool foundWithTarget = false; bool foundTargetLess = false; + Arabica::DOM::Element<std::string> withEvent; + Arabica::DOM::Element<std::string> noneEvent; + Arabica::DOM::Element<std::string> withTarget; + Arabica::DOM::Element<std::string> noneTarget; + for (int i = 0; i < transitionSet.size(); i++) { Arabica::DOM::Element<std::string> transElem = Arabica::DOM::Element<std::string>(transitionSet[i]); if (HAS_ATTR(transElem, "eventexpr")) { @@ -1188,19 +1846,23 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t } if (HAS_ATTR(transElem, "event")) { foundWithEvent = true; + withEvent = transElem; if (foundEventLess) break; } else { foundEventLess = true; + noneEvent = transElem; if (foundWithEvent) break; } if (HAS_ATTR(transElem, "target")) { foundWithTarget = true; + withTarget = transElem; if (foundTargetLess) break; } else { foundTargetLess = true; + noneTarget = transElem; if (foundWithTarget) break; } @@ -1209,6 +1871,10 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t // do not mix eventless and event transitions if (foundEventLess && foundWithEvent) { + if (flattener->_keepInvalidTransitions) { + invalidReason = MIXES_EVENT_SPONTANEOUS; + invalidMsg = "Mixes (non-)spontaneous"; + } isValid = false; return; } @@ -1228,6 +1894,10 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t eventNames = getCommonEvents(transitionSet); if (eventNames.size() == 0) { // LOG(INFO) << "No event will activate this conflict-free subset" << std::endl; + if (flattener->_keepInvalidTransitions) { + invalidReason = NO_COMMON_EVENT; + invalidMsg = "No common event"; + } isValid = false; return; } else { @@ -1264,20 +1934,20 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t // std::cout << std::endl << std::endl; } - int index = 0; seperator = ""; for (std::vector<Element<std::string> >::iterator transIter = interpreter->indexedTransitions.begin(); transIter != interpreter->indexedTransitions.end(); transIter++) { const Element<std::string>& refTrans = *transIter; + if (!HAS_ATTR(refTrans, "priority")) + continue; if (InterpreterImpl::isMember(refTrans, transitionSet)) { - members += seperator + toStr(index); + members += seperator + ATTR(refTrans, "priority"); } else { members += seperator; - for (int i = 0; i < toStr(index).size(); i++) { + for (int i = 0; i < ATTR(refTrans, "priority").size(); i++) { members += " "; } } seperator = " "; - index++; } // if (members == " 4 6 7 ") diff --git a/src/uscxml/transform/ChartToFSM.cpp.new b/src/uscxml/transform/ChartToFSM.cpp.new new file mode 100644 index 0000000..1dcf16c --- /dev/null +++ b/src/uscxml/transform/ChartToFSM.cpp.new @@ -0,0 +1,1482 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "uscxml/transform/ChartToFSM.h" +#include "uscxml/transform/FlatStateIdentifier.h" +#include "uscxml/Convenience.h" +#include "uscxml/Factory.h" + +#include <DOM/io/Stream.hpp> +#include <glog/logging.h> + +#include <iostream> +#include "uscxml/UUID.h" +#include <math.h> +#include <string.h> +#include <algorithm> +#undef max +#include <limits> + +#define UNDECIDABLE 2147483647 +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +#define DUMP_STATS(nrTrans) \ +uint64_t now = tthread::chrono::system_clock::now(); \ +if (now - _lastTimeStamp > 1000) { \ + std::cerr << "## Transition: " << _perfTransUsed << " / " << _perfTransTotal << " [" << _perfTransProcessed << "/sec]"; \ + if (nrTrans > 0) { \ + std::cerr << " - 2**" << nrTrans << " = " << pow(2.0, static_cast<double>(nrTrans)); \ + } \ + std::cerr << std::endl; \ + std::cerr << "## State : " << _globalConf.size() << " [" << _perfStatesProcessed << "/sec]" << std::endl; \ + std::cerr << "## Microstep : " << _perfMicroStepTotal << " [" << _perfMicroStepProcessed << "/sec]" << std::endl; \ + std::cerr << "## Cached : " << _perfStatesCachedTotal << " [" << _perfStatesCachedProcessed << "/sec]" << std::endl; \ + std::cerr << "## Skipped : " << _perfStatesSkippedTotal << " [" << _perfStatesSkippedProcessed << "/sec]" << std::endl; \ + std::cerr << "## Queues : " << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << " / " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; \ + std::cerr << _perfTransUsed << ", " << _perfTransTotal << ", " << _perfTransProcessed << ", "; \ + std::cerr << _globalConf.size() << ", " << _perfStatesProcessed << ", "; \ + std::cerr << _perfMicroStepTotal << ", " << _perfMicroStepProcessed << ", "; \ + std::cerr << _perfStatesCachedTotal << ", " << _perfStatesCachedProcessed << ", " << _perfStatesSkippedTotal << ", " << _perfStatesSkippedProcessed << ", "; \ + std::cerr << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << ", " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; \ + std::cerr << std::endl; \ + _perfTransProcessed = 0; \ + _perfStatesProcessed = 0; \ + _perfStatesCachedProcessed = 0; \ + _perfStatesSkippedProcessed = 0; \ + _perfMicroStepProcessed = 0; \ + _lastTimeStamp = now; \ +} + +//std::cerr << "Q: " << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << " / " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; + +#define DUMP_TRANSSET(where) \ +{\ +std::cout << std::endl;\ +std::cout << "** " << transitions.size() << " ** " << where << std::endl;\ + for (int m = 0; m < transitions.size(); m++) {\ + std::cout << transitions[m] << std::endl;\ + }\ +} + +namespace uscxml { + + +using namespace Arabica::DOM; +using namespace Arabica::XPath; + + +#define DETAIL_EXEC_CONTENT(field, actPtr) \ +std::cerr << " " << #field << " / " << TAGNAME_CAST(actPtr->field) << " ("; \ +NodeSet<std::string> contents = filterChildType(Node_base::ELEMENT_NODE, actPtr->field, true); \ +for (int i = 0; i < contents.size(); i++) { \ + std::cerr << " " << TAGNAME_CAST(contents[i]); \ +} \ +std::cerr << ")"; + + +uint64_t Complexity::stateMachineComplexity(const Arabica::DOM::Element<std::string>& root, Variant variant) { + Complexity complexity = calculateStateMachineComplexity(root); + uint64_t value = complexity.value; + + if (variant != IGNORE_HISTORY_AND_NESTED_DATA && variant != IGNORE_HISTORY) { + for (std::list<uint64_t>::const_iterator histIter = complexity.history.begin(); histIter != complexity.history.end(); histIter++) { + value *= *histIter; + } + } + + if (variant != IGNORE_HISTORY_AND_NESTED_DATA && variant != IGNORE_NESTED_DATA) { + bool ignoreNestedData = false; + if (root.getLocalName() == "scxml" && (!HAS_ATTR_CAST(root, "binding") || boost::to_lower_copy(ATTR_CAST(root, "binding")) == "early")) { + ignoreNestedData = true; + } + + if (!ignoreNestedData) { + uint64_t power = complexity.nestedData; + while(power--) { + value *= 2; + } + } + } + + return value; +} + +Complexity Complexity::calculateStateMachineComplexity(const Arabica::DOM::Element<std::string>& root) { + Complexity complexity; + + bool hasFlatHistory = false; + bool hasDeepHistory = false; + bool hasNestedData = false; + + Arabica::DOM::NodeList<std::string> childElems = root.getChildNodes(); + for (int i = 0; i < childElems.getLength(); i++) { + if (childElems.item(i).getNodeType() != Node_base::ELEMENT_NODE) + continue; + Element<std::string> childElem = Element<std::string>(childElems.item(i)); + if (InterpreterImpl::isHistory(childElem)) { + if (HAS_ATTR(childElem, "type") && ATTR(childElem, "type") == "deep") { + hasDeepHistory = true; + } else { + hasFlatHistory = true; + } + } + if (!hasNestedData && childElem.getLocalName() == "datamodel") { + Arabica::DOM::NodeList<std::string> dataElemChilds = childElem.getChildNodes(); + for (int j = 0; j < dataElemChilds.getLength(); j++) { + if (dataElemChilds.item(j).getLocalName() == "data") + hasNestedData = true; + } + } + } + + if (hasNestedData) + complexity.nestedData++; + + if (InterpreterImpl::isCompound(root) || TAGNAME(root) == "scxml") { + // compounds can be in any of the child state -> add + NodeSet<std::string> childs = InterpreterImpl::getChildStates(root); + for (int i = 0; i < childs.size(); i++) { + complexity += calculateStateMachineComplexity(Element<std::string>(childs[i])); + } + if (hasFlatHistory) { + complexity.history.push_back(childs.size()); + } + if (hasDeepHistory) { + complexity.history.push_back(complexity.value); + } + } else if (InterpreterImpl::isParallel(root)) { + // parallels are in all states -> multiply + NodeSet<std::string> childs = InterpreterImpl::getChildStates(root); + complexity.value = 1; + for (int i = 0; i < childs.size(); i++) { + complexity *= calculateStateMachineComplexity(Element<std::string>(childs[i])); + } + if (hasDeepHistory) { + complexity.history.push_back(complexity.value); + } + + } else if (InterpreterImpl::isAtomic(root)) { + return 1; + } + + return complexity; +} + + +ChartToFSM::ChartToFSM(const Interpreter& other) { + + cloneFrom(other.getImpl()); + + _keepInvalidTransitions = false; + _lastTimeStamp = tthread::chrono::system_clock::now(); + _perfTransProcessed = 0; + _perfTransTotal = 0; + _perfTransUsed = 0; + _perfStatesProcessed = 0; + _perfStatesSkippedProcessed = 0; + _perfStatesSkippedTotal = 0; + _perfStatesCachedProcessed = 0; + _perfStatesCachedTotal = 0; + _perfMicroStepProcessed = 0; + _perfMicroStepTotal = 0; + + _start = NULL; + _currGlobalTransition = NULL; + + _lastStateIndex = 0; + _lastActiveIndex = 0; + _lastTransIndex = 0; + + _maxEventSentChain = 0; + _maxEventRaisedChain = 0; + _doneEventRaiseTolerance = 0; + _skipEventChainCalculations = false; + + addMonitor(this); +} + +ChartToFSM::~ChartToFSM() { + std::map<std::string, GlobalState*>::iterator confIter = _globalConf.begin(); + while(confIter != _globalConf.end()) { + std::list<GlobalTransition*>::iterator transIter = confIter->second->sortedOutgoing.begin(); + while (transIter != confIter->second->sortedOutgoing.end()) { + delete *transIter; + transIter++; + } + delete confIter->second; + confIter++; + } + + // tear down caches + Arabica::XPath::NodeSet<std::string> allTransitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _scxml, true); + for (int i = 0; i < allTransitions.size(); i++) { + _transParents.erase(allTransitions[i]); + } + +} + +Document<std::string> ChartToFSM::getDocument() const { + if (_flatDoc) + return _flatDoc; + return _document; +} + +InterpreterState ChartToFSM::interpret() { + + // create a _flatDoc for the FSM + DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + _flatDoc = domFactory.createDocument(_document.getNamespaceURI(), "", 0); + + init(); + setupIOProcessors(); + + uint64_t complexity = Complexity::stateMachineComplexity(_scxml) + 1; + std::cerr << "Approximate Complexity: " << complexity << std::endl; + std::cerr << "Approximate Active Complexity: " << Complexity::stateMachineComplexity(_scxml, Complexity::IGNORE_HISTORY_AND_NESTED_DATA) + 1 << std::endl; + + if (complexity > 1000) { + _skipEventChainCalculations = true; + _maxEventRaisedChain = UNDECIDABLE; + _maxEventSentChain = UNDECIDABLE; + } + // initialize the datamodel + std::string datamodelName; + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) + datamodelName = ATTR(_scxml, "datamodel"); + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel + datamodelName = ATTR(_scxml, "profile"); + if(datamodelName.length() > 0) { + _dataModel = _factory->createDataModel(datamodelName, this); + if (!_dataModel) { + Event e; + e.data.compound["cause"] = Data("Cannot instantiate datamodel", Data::VERBATIM); + throw e; + } + } else { + _dataModel = _factory->createDataModel("null", this); + } + if(datamodelName.length() > 0 && !_dataModel) { + LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; + } + + // setup caches + { + Arabica::XPath::NodeSet<std::string> allTransitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _scxml, true); + indexedTransitions.reserve(allTransitions.size()); + for (int i = 0; i < allTransitions.size(); i++) { + _transParents[allTransitions[i]] = InterpreterImpl::getParentState(allTransitions[i]); + } + } + + // identify all history elements + NodeSet<std::string> histories = filterChildElements(_nsInfo.xmlNSPrefix + "history", _scxml, true); + for (int i = 0; i < histories.size(); i++) { + _historyTargets[ATTR_CAST(histories[i], "id")] = Element<std::string>(histories[i]); + } + + _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); + _alreadyFlat = (HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat"))); + + if (_alreadyFlat) { + reassembleFromFlat(); + return _state; + } + + // set invokeid for all invokers to parent state if none given + NodeSet<std::string> invokers = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _scxml, true); + for (int i = 0; i < invokers.size(); i++) { + Element<std::string> invokerElem = Element<std::string>(invokers[i]); + invokerElem.setAttribute("parent", ATTR_CAST(invokerElem.getParentNode(), "id")); + } + // reset + _globalConf.clear(); + _currGlobalTransition = NULL; + + // very first state + _start = new GlobalState(_configuration, _alreadyEntered, _historyValue, _nsInfo.xmlNSPrefix, this); + _globalConf[_start->stateId] = _start; + _globalConf[_start->stateId]->index = _lastStateIndex++; + + NodeSet<std::string> initialTransitions; + + // enter initial configuration + Arabica::XPath::NodeSet<std::string> initialStates; + 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); + } + + if (!_skipEventChainCalculations) + annotateRaiseAndSend(_scxml); + +// std::cout << _scxml << std::endl; + + indexTransitions(); + + // add initial transitions as least prior + for (int i = 0; i < initialTransitions.size() ; i++) { + indexedTransitions.push_back(Element<std::string>(initialTransitions[i])); + } + + // set index attribute for transitions + for (int i = 0; i < indexedTransitions.size(); i++) { + std::cerr << toStr(i) << ":" << (HAS_ATTR(indexedTransitions[i], "line_start") ? ATTR(indexedTransitions[i], "line_start") : ""); + std::cerr << "\t" << DOMUtils::xPathForNode(indexedTransitions[i]) << std::endl; + indexedTransitions[i].setAttribute("index", toStr(i)); + } + +// int lastTransIndex = indexedTransitions.size(); +// for (int i = 0; i < initialTransitions.size() ; i++, lastTransIndex++) { +// indexedTransitions[i].setAttribute("index", toStr(indexedTransitions.size() - 1 - i)); +// } + + // gather and set index attribute o states + NodeSet<std::string> allStates = getAllStates(); + allStates.to_document_order(); + + indexedStates.resize(allStates.size()); + for (int i = 0; i < allStates.size(); i++) { + Element<std::string> state = Element<std::string>(allStates[i]); + + // while we are iterating, determine deepest nested level + size_t nrAncs = getProperAncestors(state, _scxml).size(); + if (_doneEventRaiseTolerance < nrAncs) + _doneEventRaiseTolerance = nrAncs; + + state.setAttribute("index", toStr(i)); + indexedStates[i] = state; + } + +// std::cerr << _scxml << std::endl; + + GlobalTransition* globalTransition = new GlobalTransition(initialTransitions, _dataModel, this); + globalTransition->index = _lastTransIndex++; + + _start->sortedOutgoing.push_back(globalTransition); + globalTransition->source = _start->stateId; + _currGlobalTransition = globalTransition; + + enterStates(initialTransitions); + globalTransition->destination = FlatStateIdentifier::toStateId(_configuration); + globalTransition->activeDestination = globalTransition->destination; + + explode(); + +#if 0 + // print set of global configurations + for(std::map<std::string, GlobalState*>::iterator globalConfIter = _globalConf.begin(); + globalConfIter != _globalConf.end(); + globalConfIter++) { + std::cerr << globalConfIter->first << std::endl; + } + std::cerr << _globalConf.size() << std::endl; +#endif + + std::cerr << "Actual Complexity: " << _globalConf.size() << std::endl; + std::cerr << "Actual Active Complexity: " << _activeConf.size() << std::endl; + std::cerr << "Internal Queue: " << _maxEventRaisedChain << std::endl; + std::cerr << "External Queue: " << _maxEventSentChain << std::endl; + + if (complexity < _globalConf.size()) + throw std::runtime_error("Upper bound for states exceeded"); + + return _state; +} + +void ChartToFSM::executeContent(const Arabica::DOM::Element<std::string>& content, bool rethrow) { +// std::cerr << content << std::endl; +// std::cerr << TAGNAME(content) << std::endl; + + GlobalTransition::Action action; + + NodeList<std::string> childs = content.getChildNodes(); + for (unsigned int i = 0; i < childs.getLength(); i++) { + Node_base::Type type = childs.item(i).getNodeType(); + if (type == Node_base::ELEMENT_NODE || type == Node_base::COMMENT_NODE || type == Node_base::TEXT_NODE) { + goto HAS_VALID_CHILDREN; + } + } + return; + +HAS_VALID_CHILDREN: + if (false) { + } else if (TAGNAME(content) == "transition") { + action.transition = content; + } else if (TAGNAME(content) == "onexit") { + action.onExit = content; + } else if (TAGNAME(content) == "onentry") { + action.onEntry = content; + } else if (TAGNAME(content) == "history") { + assert(false); + } else { // e.g. global script elements + return; + } + + if (!_skipEventChainCalculations && + (_maxEventRaisedChain != UNDECIDABLE || _maxEventSentChain != UNDECIDABLE)) { + assert(content.hasAttribute("raise") && content.hasAttribute("send")); + + std::string raiseAttr = content.getAttribute("raise"); + std::string sendAttr = content.getAttribute("send"); + + _currGlobalTransition->eventsRaised = (raiseAttr == "-1" ? UNDECIDABLE : _currGlobalTransition->eventsRaised + strTo<uint32_t>(raiseAttr)); + _currGlobalTransition->eventsSent = (sendAttr == "-1" ? UNDECIDABLE : _currGlobalTransition->eventsSent + strTo<uint32_t>(sendAttr)); + + if (_currGlobalTransition->eventsRaised > _maxEventRaisedChain) + _maxEventRaisedChain = _currGlobalTransition->eventsRaised; + if (_currGlobalTransition->eventsSent > _maxEventSentChain) + _maxEventSentChain = _currGlobalTransition->eventsSent; + } + + _currGlobalTransition->actions.push_back(action); + _currGlobalTransition->hasExecutableContent = true; +} + +void ChartToFSM::invoke(const Arabica::DOM::Element<std::string>& element) { + GlobalTransition::Action action; + action.invoke = element; + _currGlobalTransition->actions.push_back(action); + _currGlobalTransition->hasExecutableContent = true; +} + +void ChartToFSM::cancelInvoke(const Arabica::DOM::Element<std::string>& element) { + GlobalTransition::Action action; + action.uninvoke = element; + _currGlobalTransition->actions.push_back(action); + _currGlobalTransition->hasExecutableContent = true; +} + +void ChartToFSM::internalDoneSend(const Arabica::DOM::Element<std::string>& state) { + if (!isState(state)) + return; + + if (parentIsScxmlState(state)) + return; + +// std::cerr << "internalDoneSend: " << state << std::endl; + + // create onentry with a raise element + Element<std::string> onentry = _flatDoc.createElementNS(_nsInfo.nsURL, "onentry"); + _nsInfo.setPrefix(onentry); + + Element<std::string> raise = _flatDoc.createElementNS(_nsInfo.nsURL, "raise"); + _nsInfo.setPrefix(raise); + + onentry.appendChild(raise); + + Arabica::XPath::NodeSet<std::string> doneDatas = filterChildElements(_nsInfo.xmlNSPrefix + "donedata", state); + if (doneDatas.size() > 0) { + Arabica::DOM::Node<std::string> doneData = doneDatas[0]; + Arabica::XPath::NodeSet<std::string> contents = filterChildElements(_nsInfo.xmlNSPrefix + "content", doneDatas[0]); + if (contents.size() > 0) { + Node<std::string> imported = _flatDoc.importNode(contents[0], true); + raise.appendChild(imported); + } + Arabica::XPath::NodeSet<std::string> params = filterChildElements(_nsInfo.xmlNSPrefix + "param", doneDatas[0]); + if (params.size() > 0) { + Node<std::string> imported = _flatDoc.importNode(params[0], true); + raise.appendChild(imported); + } + } + + raise.setAttribute("event", "done.state." + ATTR_CAST(state.getParentNode(), "id")); // parent?! + + GlobalTransition::Action action; + action.onEntry = onentry; + + _currGlobalTransition->actions.push_back(action); + if (!_skipEventChainCalculations && + (_maxEventRaisedChain != UNDECIDABLE || _maxEventSentChain != UNDECIDABLE)) + _currGlobalTransition->eventsRaised++; + _currGlobalTransition->hasExecutableContent = true; + +} + +static bool isSuperset(const GlobalTransition* t1, const GlobalTransition* t2) { + bool isSuperset = true; + + if (t1->transitionRefs.size() >= t2->transitionRefs.size()) + return false; + + NodeSet<std::string> t1Trans = t1->getTransitions(); + NodeSet<std::string> t2Trans = t2->getTransitions(); + + for (int i = 0; i < t1Trans.size(); i++) { + if (!InterpreterImpl::isMember(t1Trans[i], t2Trans)) { + isSuperset = false; + } + } + return isSuperset; +} + +// return false if two transitions have the same source +std::map<Arabica::DOM::Node<std::string>, Arabica::DOM::Node<std::string> > ChartToFSM::_transParents; +bool ChartToFSM::filterSameState(const NodeSet<std::string>& transitions) { + + for (unsigned int i = 0; i < transitions.size(); i++) { + Node<std::string> p1 = _transParents[transitions[i]]; + + for (unsigned int j = i + 1; j < transitions.size(); j++) { +// if (i == j) +// continue; + Node<std::string> p2 = _transParents[transitions[j]]; + + if (p1 == p2) + return false; + } + } + return true; +} + +static bool filterChildEnabled(const NodeSet<std::string>& transitions) { + // drop any transition that is already enabled by a child + NodeSet<std::string> filteredTransitions; + for (unsigned int i = 0; i < transitions.size(); i++) { + Node<std::string> t1 = transitions[i]; + Node<std::string> p1 = InterpreterImpl::getParentState(t1); + for (unsigned int j = 0; j < transitions.size(); j++) { + if (i == j) + continue; + Node<std::string> t2 = transitions[j]; + Node<std::string> p2 = InterpreterImpl::getParentState(t2); + p2 = p2.getParentNode(); // TODO: think about again! + while(p2) { + if (p1 == p2) { + std::string eventDesc1 = ATTR_CAST(t1, "event"); + std::string eventDesc2 = ATTR_CAST(t2, "event"); + if (InterpreterImpl::nameMatch(eventDesc1, eventDesc2)) { + return false; + } + } + p2 = p2.getParentNode(); + } + } + filteredTransitions.push_back(t1); + ; + } + return true; +} + +bool ChartToFSM::hasForeachInBetween(const Arabica::DOM::Node<std::string>& ancestor, const Arabica::DOM::Node<std::string>& child) { + if (!ancestor || !child) + return false; + + Node<std::string> currChild = child; + while(currChild != ancestor) { + if (!currChild.getParentNode()) + return false; + if (TAGNAME_CAST(currChild) == "foreach") + return true; + currChild = currChild.getParentNode(); + } + return false; +} + +void ChartToFSM::annotateRaiseAndSend(const Arabica::DOM::Element<std::string>& root) { + NodeSet<std::string> execContent; + execContent.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "transition", _scxml, true)); + execContent.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "onentry", _scxml, true)); + execContent.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "onexit", _scxml, true)); + for (int i = 0; i < execContent.size(); i++) { + Element<std::string> execContentElem(execContent[i]); + + int nrRaise = 0; + NodeSet<std::string> raise = filterChildElements(_nsInfo.xmlNSPrefix + "raise", execContent[i], true); + for (int j = 0; j < raise.size(); j++) { + if (hasForeachInBetween(execContent[i], raise[j])) { + execContentElem.setAttribute("raise", "-1"); + goto DONE_COUNT_RAISE; + } else { + nrRaise++; + } + } + execContentElem.setAttribute("raise", toStr(nrRaise)); + + DONE_COUNT_RAISE: + + int nrSend = 0; + NodeSet<std::string> sends = filterChildElements(_nsInfo.xmlNSPrefix + "send", execContent[i], true); + for (int j = 0; j < sends.size(); j++) { + if (hasForeachInBetween(execContent[i], sends[j])) { + execContentElem.setAttribute("send", "-1"); + goto DONE_COUNT_SEND; + } else { + nrSend++; + } + } + execContentElem.setAttribute("send", toStr(nrSend)); + + DONE_COUNT_SEND: + ; + } +} + +void ChartToFSM::indexTransitions() { + indexTransitions(_scxml); + // reverse indices for most prior to be in front + std::reverse(indexedTransitions.begin(), indexedTransitions.end()); + + size_t index = 1; + for (std::vector<Arabica::DOM::Element<std::string> >::iterator transIter = indexedTransitions.begin(); transIter != indexedTransitions.end(); transIter++) { + transIter->setAttribute("priority", toStr(index)); + index++; + } + +} + +void ChartToFSM::indexTransitions(const Arabica::DOM::Element<std::string>& root) { + // breadth first traversal of transitions + Arabica::XPath::NodeSet<std::string> levelTransitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", root); + for (int i = levelTransitions.size() - 1; i >= 0; i--) { + // push into index starting with least prior + indexedTransitions.push_back(Element<std::string>(levelTransitions[i])); + } + + Arabica::XPath::NodeSet<std::string> nextLevel = filterChildType(Arabica::DOM::Node_base::ELEMENT_NODE, root); + for (int i = nextLevel.size() - 1; i >= 0; i--) { + Element<std::string> stateElem = Element<std::string>(nextLevel[i]); + if (isState(stateElem)) + indexTransitions(stateElem); + } +} + +bool GlobalTransition::operator< (const GlobalTransition& other) const { + const std::vector<Arabica::DOM::Element<std::string> >& indexedTransitions = interpreter->indexedTransitions; + NodeSet<std::string> transitions = getTransitions(); + + for (std::vector<Element<std::string> >::const_iterator transIter = indexedTransitions.begin(); transIter != indexedTransitions.end(); transIter++) { + const Element<std::string>& refTrans = *transIter; + NodeSet<std::string> otherTransitions = other.getTransitions(); + + if (InterpreterImpl::isMember(refTrans, transitions) && !InterpreterImpl::isMember(refTrans, otherTransitions)) { + return true; + } + if (!InterpreterImpl::isMember(refTrans, transitions) && InterpreterImpl::isMember(refTrans, otherTransitions)) { + return false; + } + } + return true; // actually, they are equal +} + +template <typename T> bool PtrComp(const T * const & a, const T * const & b) { + return *a < *b; +} + + +/** + * subset only removes transitions without cond -> superset will always be enabled + */ +bool hasUnconditionalSuperset (GlobalTransition* first, GlobalTransition* second) { + + NodeSet<std::string> firstTransitions = first->getTransitions(); + NodeSet<std::string> secondTransitions = first->getTransitions(); + + if (isSuperset(second, first)) { + for (int i = 0; i < firstTransitions.size(); i++) { + if (!InterpreterImpl::isMember(firstTransitions[i], secondTransitions)) { + if (HAS_ATTR_CAST(firstTransitions[i], "cond")) { + return false; // second can't be removed + } + } + } + return true; // remove second + } + return false; //second can't be removed +} + +bool hasEarlierUnconditionalMatch(GlobalTransition* first, GlobalTransition* second) { + if (first->eventDesc == second->eventDesc) { + if (first->condition.size() == 0) + return true; + } + return false; +} + +std::list<GlobalTransition*> redundantRemove(std::list<GlobalTransition*> list) { + for (std::list<GlobalTransition*>::iterator outerIter = list.begin(); + outerIter != list.end(); + outerIter++) { + for (std::list<GlobalTransition*>::iterator innerIter = outerIter; + innerIter != list.end(); + innerIter++) { + + if (innerIter == outerIter) + continue; + + GlobalTransition* t1 = *outerIter; + GlobalTransition* t2 = *innerIter; + + if (hasUnconditionalSuperset(t1, t2)) { + list.erase(innerIter++); + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { + list.erase(outerIter++); + continue; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + list.erase(innerIter++); + continue; + } + } + } + return list; +} + +std::list<GlobalTransition*> redundantMark(std::list<GlobalTransition*> list) { + for (std::list<GlobalTransition*>::iterator outerIter = list.begin(); + outerIter != list.end(); + outerIter++) { + for (std::list<GlobalTransition*>::iterator innerIter = outerIter; + innerIter != list.end(); + innerIter++) { + + if (innerIter == outerIter) + continue; + + GlobalTransition* t1 = *outerIter; + GlobalTransition* t2 = *innerIter; + + if (!t1->isValid || !t2->isValid) + continue; + + if (hasUnconditionalSuperset(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Unconditional superset"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { + t1->isValid = false; + t1->invalidMsg = "Unconditional superset"; + t1->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + continue; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Earlier unconditional match"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_MATCH; + continue; + } + } + } + return list; +} + +void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap) { + // get all transition elements from states in the current configuration + NodeSet<std::string> allTransitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", conf); + + + if (allTransitions.size() == 0) + return; // no transitions + + int nrElements = allTransitions.size(); + int k = 0; + int* stack = (int*)malloc((nrElements + 1) * sizeof(int)); + memset(stack, 0, (nrElements + 1) * sizeof(int)); + + while(1) { + // create the power set of all potential transitions - this is expensive! + // see: http://www.programminglogic.com/powerset-algorithm-in-c/ + + if (stack[k] < nrElements) { + stack[k+1] = stack[k] + 1; + k++; + } + + else { + stack[k-1]++; + k--; + } + + if (k==0) + break; + + NodeSet<std::string> transitions; + // std::cerr << globalState->stateId << " [" << nrElements << "]: " << std::endl; + for (int i = 1; i <= k; i++) { + // std::cerr << stack[i] - 1 << ", "; + transitions.push_back(allTransitions[stack[i] - 1]); + } + // std::cerr << std::endl; + + // transitions.push_back(allTransitions[0]); + // transitions.push_back(allTransitions[4]); + // transitions.push_back(allTransitions[5]); + // transitions.push_back(allTransitions[7]); + + bool dump = false; + + // if (k == 4 && stack[1] == 1 && stack[2] == 5 && stack[3] == 6 && stack[4] == 8) { + // dump = true; + // } + + if (dump) DUMP_TRANSSET("at start"); + + _perfTransTotal++; + _perfTransProcessed++; + + DUMP_STATS(nrElements); + + + GlobalTransition* transition = NULL; + + // reduce to conflict-free subset + // transitions.to_document_order(); + if (!_keepInvalidTransitions) { + // remove transitions in the same state + if(!filterSameState(transitions)) + continue; + if (dump) DUMP_TRANSSET("after same state filtered"); + + // remove those transitions with a child transition + if(!filterChildEnabled(transitions)) + continue; + if (dump) DUMP_TRANSSET("after child enabled filtered"); + + transitions = removeConflictingTransitions(transitions); + if (dump) DUMP_TRANSSET("after conflicting filtered"); + // algorithm can never reduce to empty set + assert(transitions.size() > 0); + + // create a GlobalTransition object from the set + transition = new GlobalTransition(transitions, _dataModel, this); + if (!transition->isValid) { + // this set of transitions can not be enabled together + delete transition; + continue; + } + } else { + transition = new GlobalTransition(transitions, _dataModel, this); + + // remove transitions in the same state + if(!filterSameState(transitions)) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::SAME_SOURCE_STATE; + transition->invalidMsg = "Same source state"; + + } else if(!filterChildEnabled(transitions)) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::CHILD_ENABLED; + transition->invalidMsg = "Nested transition enabled"; + + } else { + NodeSet<std::string> nonPreemptingTransitions = removeConflictingTransitions(transitions); + if (nonPreemptingTransitions.size() != transitions.size()) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::PREEMPTING_MEMBERS; + transition->invalidMsg = "Preempting members"; + } + } + + } + + + // two combinations might have projected onto the same conflict-free set + if (outMap.find(transition->transitionId) != outMap.end()) { + // std::cerr << "skipping as projected onto existing conflict-free subset" << std::endl; + delete transition; + continue; + } + + transition->index = _lastTransIndex++; + _perfTransUsed++; + + // remember this conflict-free set + // std::cerr << "New conflict-free subset: " << transition->transitionId << ":" << transition->eventDesc << std::endl; + outMap[transition->transitionId] = transition; + } + return; +} + +void ChartToFSM::explode() { + + std::list<std::pair<GlobalTransition*, GlobalState*> > statesRemaining; + statesRemaining.push_back(std::make_pair(_currGlobalTransition, new GlobalState(_configuration, _alreadyEntered, _historyValue, _nsInfo.xmlNSPrefix, this))); + + // add all invokers for initial transition + 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++) { + invoke(Element<std::string>(invokes[j])); + } + } + _statesToInvoke = NodeSet<std::string>(); + + /** + We need to resolve the recursion in order not to exhaust the stack + */ + + // append new global states and pop from front + while(statesRemaining.size() > 0) { + DUMP_STATS(0); + + GlobalState* globalState = statesRemaining.front().second; + _currGlobalTransition = statesRemaining.front().first; + statesRemaining.pop_front(); + + // used to be conditionalized, we will just assume + assert(_currGlobalTransition); + + if (_globalConf.find(globalState->stateId) != _globalConf.end()) { + if (_currGlobalTransition->isEventless && + !_skipEventChainCalculations && + (_maxEventRaisedChain != UNDECIDABLE || _maxEventSentChain != UNDECIDABLE)) { + // we arrived via a spontaneaous transition, do we need to update? + updateRaisedAndSendChains(_globalConf[globalState->stateId], _currGlobalTransition, std::set<GlobalTransition*>()); + } + delete globalState; + _perfStatesSkippedTotal++; + _perfStatesSkippedProcessed++; + continue; // we have already been here + } + + _perfStatesProcessed++; + + _configuration = globalState->getActiveStates(); + _alreadyEntered = globalState->getAlreadyEnteredStates(); + _historyValue = globalState->getHistoryStates(); + + // remember as global configuration + _globalConf[globalState->stateId] = globalState; + _globalConf[globalState->stateId]->index = _lastStateIndex++; + + if(_globalConf[globalState->stateId]->isFinal) { + if (_activeConf.find(globalState->activeId) == _activeConf.end()) { + assert(globalState->activeIndex == -1); + globalState->activeIndex = _lastActiveIndex++; + _activeConf[globalState->activeId] = globalState; // remember as active configuration + exitInterpreter(); + } + continue; // done in this branch + } + + if (_activeConf.find(globalState->activeId) != _activeConf.end()) { + // we already know these transition sets, just copy over + std::list<GlobalTransition*>::iterator sortTransIter = _activeConf[globalState->activeId]->sortedOutgoing.begin(); + while(sortTransIter != _activeConf[globalState->activeId]->sortedOutgoing.end()) { + globalState->sortedOutgoing.push_back(GlobalTransition::copyWithoutExecContent(*sortTransIter)); + globalState->sortedOutgoing.back()->index = _lastTransIndex++; + _perfTransUsed++; + sortTransIter++; + } + _perfStatesCachedTotal++; + _perfStatesCachedProcessed++; + + } else { + // we need to calculate the potential optimal transition sets + std::map<std::string, GlobalTransition*> transitionSets; + getPotentialTransitionsForConf(refsToStates(globalState->activeStatesRefs), transitionSets); + + // reduce and sort transition sets + for(std::map<std::string, GlobalTransition*>::iterator transSetIter = transitionSets.begin(); + transSetIter != transitionSets.end(); + transSetIter++) { + globalState->sortedOutgoing.push_back(transSetIter->second); + } + + globalState->sortedOutgoing.sort(PtrComp<GlobalTransition>); +// globalState->sortedOutgoing.unique(hasUnconditionalSuperset); +// globalState->sortedOutgoing.unique(hasEarlierUnconditionalMatch); + // unique is not quite like what we need, but it was a start + if (_keepInvalidTransitions) { + globalState->sortedOutgoing = redundantMark(globalState->sortedOutgoing); + } else { + globalState->sortedOutgoing.unique(hasUnconditionalSuperset); + globalState->sortedOutgoing.unique(hasEarlierUnconditionalMatch); + globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); + } +// globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); +// globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); + +// std::cout << globalState->sortedOutgoing.size() << std::endl; + + assert(_activeConf.find(globalState->activeId) == _activeConf.end()); + assert(globalState->activeIndex == -1); + globalState->activeIndex = _lastActiveIndex++; + _activeConf[globalState->activeId] = globalState; + } + + // take every transition set and append resulting new state + for(std::list<GlobalTransition*>::iterator transIter = globalState->sortedOutgoing.begin(); + transIter != globalState->sortedOutgoing.end(); + transIter++) { + + GlobalTransition* incomingTrans = _currGlobalTransition; + GlobalTransition* outgoingTrans = *transIter; + + outgoingTrans->source = globalState->stateId; + + if (_keepInvalidTransitions && !outgoingTrans->isValid) + continue; + + _currGlobalTransition = outgoingTrans; + + microstep(refsToTransitions(outgoingTrans->transitionRefs)); + assert(isLegalConfiguration(_configuration)); + + _perfMicroStepProcessed++; + _perfMicroStepTotal++; + + // if outgoing transition is spontaneous, add number of events to chain + if (outgoingTrans->isEventless && + !_skipEventChainCalculations && + (_maxEventRaisedChain != UNDECIDABLE || _maxEventSentChain != UNDECIDABLE)) { + outgoingTrans->eventsChainRaised = MIN(incomingTrans->eventsChainRaised + outgoingTrans->eventsRaised, UNDECIDABLE); + outgoingTrans->eventsChainSent = MIN(incomingTrans->eventsChainSent + outgoingTrans->eventsSent, UNDECIDABLE); + + if (outgoingTrans->eventsChainRaised > _maxEventRaisedChain) + _maxEventRaisedChain = outgoingTrans->eventsChainRaised; + if (outgoingTrans->eventsChainSent > _maxEventSentChain) + _maxEventSentChain = outgoingTrans->eventsChainSent; + + } + + statesRemaining.push_back(std::make_pair(outgoingTrans, new GlobalState(_configuration, _alreadyEntered, _historyValue, _nsInfo.xmlNSPrefix, this))); + + // add all invokers + 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++) { + invoke(Element<std::string>(invokes[j])); + } + } + _statesToInvoke = NodeSet<std::string>(); + + // remember that the last transition lead here + outgoingTrans->destination = statesRemaining.back().second->stateId; + outgoingTrans->activeDestination = statesRemaining.back().second->activeId; + // reset state for next transition set + _configuration = globalState->getActiveStates(); + _alreadyEntered = globalState->getAlreadyEnteredStates(); + _historyValue = globalState->getHistoryStates(); + } + } + +} + +void ChartToFSM::updateRaisedAndSendChains(GlobalState* state, GlobalTransition* source, std::set<GlobalTransition*> visited) { + for (std::list<GlobalTransition*>::iterator transIter = state->sortedOutgoing.begin(); transIter != state->sortedOutgoing.end(); transIter++) { + GlobalTransition* transition = *transIter; + + if (!transition->isEventless) + continue; // we do not care for eventful transitions + + // source leads to spontaneous transition -> update event chains + bool eventChainsNeedUpdated = false; + + if (visited.find(transition) != visited.end()) { + // potential spontaneous transition cycle! + if (transition->eventsChainRaised > 0) + _maxEventRaisedChain = UNDECIDABLE; + if (transition->eventsChainSent > 0) + _maxEventSentChain = UNDECIDABLE; + return; + } + + // UNDECIDABLE means "undecidable / endless" + + // will source increase our event chain? + if (transition->eventsChainRaised != UNDECIDABLE && + transition->eventsChainRaised < source->eventsChainRaised + transition->eventsRaised) { + // taking transition after source causes more events in chain + transition->eventsChainRaised = MIN(source->eventsChainRaised + transition->eventsRaised, UNDECIDABLE); + eventChainsNeedUpdated = true; + } + if (transition->eventsChainSent != UNDECIDABLE && + transition->eventsChainSent < source->eventsChainSent + transition->eventsSent) { + // taking transition after source causes more events in chain + transition->eventsChainSent = MIN(source->eventsChainSent + transition->eventsSent, UNDECIDABLE); + eventChainsNeedUpdated = true; + } + + if (eventChainsNeedUpdated && + transition->destination.length() > 0 && + _globalConf.find(transition->destination) != _globalConf.end()) { + + visited.insert(transition); + // iterate all spontaneous transitions in destination and update event chains + updateRaisedAndSendChains(_globalConf[transition->destination], transition, visited); + } + + if (transition->eventsChainRaised > _maxEventRaisedChain) + _maxEventRaisedChain = transition->eventsChainRaised; + if (transition->eventsChainSent > _maxEventSentChain) + _maxEventSentChain = transition->eventsChainSent; + } +} + +uint32_t ChartToFSM::getMinInternalQueueLength(uint32_t defaultVal) { + if (_maxEventRaisedChain != UNDECIDABLE) + return _maxEventRaisedChain + _doneEventRaiseTolerance; + return defaultVal; +} + +uint32_t ChartToFSM::getMinExternalQueueLength(uint32_t defaultVal) { + if (_maxEventSentChain != UNDECIDABLE) + return _maxEventSentChain; + return defaultVal; +} + +void ChartToFSM::reassembleFromFlat() { + LOG(ERROR) << "Cannot flatten flat SCXML document"; + abort(); +} + +Arabica::XPath::NodeSet<std::string> ChartToFSM::refsToStates(const std::set<int>& stateRefs) { + NodeSet<std::string> states; + for (std::set<int>::const_iterator stateIter = stateRefs.begin(); stateIter != stateRefs.end(); stateIter++) { + states.push_back(indexedStates[*stateIter]); + } + return states; +} + +Arabica::XPath::NodeSet<std::string> ChartToFSM::refsToTransitions(const std::set<int>& transRefs) { + NodeSet<std::string> transitions; + for (std::set<int>::const_iterator transIter = transRefs.begin(); transIter != transRefs.end(); transIter++) { + transitions.push_back(indexedTransitions[*transIter]); + } + return transitions; +} + +void ChartToFSM::beforeMicroStep(Interpreter interpreter) { +} +void ChartToFSM::onStableConfiguration(Interpreter interpreter) { +} +void ChartToFSM::beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { + GlobalTransition::Action action; + action.exited = state; + _currGlobalTransition->actions.push_back(action); +} +void ChartToFSM::beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { + GlobalTransition::Action action; + action.entered = state; + _currGlobalTransition->actions.push_back(action); +} +void ChartToFSM::beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing) { +} + +GlobalState::GlobalState(const Arabica::XPath::NodeSet<std::string>& activeStates_, + const Arabica::XPath::NodeSet<std::string>& alreadyEnteredStates_, // we need to remember for binding=late + const std::map<std::string, Arabica::XPath::NodeSet<std::string> >& historyStates_, + const std::string& xmlNSPrefix, + ChartToFSM* flattener) { + interpreter = flattener; + activeIndex = -1; + + // take references + for (int i = 0; i < activeStates_.size(); i++) { + activeStatesRefs.insert(strTo<int>(ATTR_CAST(activeStates_[i], "index"))); + } + + for (int i = 0; i < alreadyEnteredStates_.size(); i++) { + alreadyEnteredStatesRefs.insert(strTo<int>(ATTR_CAST(alreadyEnteredStates_[i], "index"))); + } + + for (std::map<std::string, Arabica::XPath::NodeSet<std::string> >::const_iterator histIter = historyStates_.begin(); histIter != historyStates_.end(); histIter++) { + for (int i = 0; i < histIter->second.size(); i++) { + historyStatesRefs[histIter->first].insert(strTo<int>(ATTR_CAST(histIter->second[i], "index"))); + } + } + + isFinal = false; + + // is state this final? + for(int i = 0; i < activeStates_.size(); i++) { + Arabica::DOM::Element<std::string> state = Arabica::DOM::Element<std::string>(activeStates_[i]); + Arabica::DOM::Element<std::string> parentElem = (Arabica::DOM::Element<std::string>)state.getParentNode(); + if(InterpreterImpl::isFinal(state) && iequals(parentElem.getTagName(), xmlNSPrefix + "scxml")) { + isFinal = true; + break; + } + } + + FlatStateIdentifier flatStateId(getActiveStates(), getAlreadyEnteredStates(), getHistoryStates()); + stateId = flatStateId.getStateId(); + activeId = flatStateId.getFlatActive(); +} + +GlobalTransition* GlobalTransition::copyWithoutExecContent(GlobalTransition* other) { + GlobalTransition* newTrans = new GlobalTransition(*other); + newTrans->actions.clear(); + newTrans->historyBase = other; + other->historyTrans.push_back(newTrans); + return newTrans; +} + +GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& transitionSet, DataModel dataModel, ChartToFSM* flattener) { + interpreter = flattener; + + eventsRaised = 0; + eventsSent = 0; + eventsChainRaised = 0; + eventsChainSent = 0; + historyBase = NULL; + + for (int i = 0; i < transitionSet.size(); i++) { + transitionRefs.insert(strTo<int>(ATTR_CAST(transitionSet[i], "index"))); + } + + std::ostringstream setId; // also build id for subset + std::string seperator = ""; + for (std::set<int>::iterator transIter = transitionRefs.begin(); transIter != transitionRefs.end(); transIter++) { + setId << seperator << *transIter; + seperator = "-"; + } + transitionId = setId.str(); + + hasExecutableContent = false; + isValid = true; + isEventless = true; + +#if 0 + std::cerr << "################" << std::endl; + for (int i = 0; i < transitions.size(); i++) { + std::cerr << transitions[i] << std::endl; + } + std::cerr << "################" << std::endl; +#endif + + // first establish whether this is a valid set + + /** + * Can these events event occur together? They can't if: + * 1. event / eventless is mixed + * 2. target / targetless is mixed (?) + * 3. there is no common prefix for their event attribute + */ + + bool foundWithEvent = false; + bool foundEventLess = false; + bool foundWithTarget = false; + bool foundTargetLess = false; + + Arabica::DOM::Element<std::string> withEvent; + Arabica::DOM::Element<std::string> noneEvent; + Arabica::DOM::Element<std::string> withTarget; + Arabica::DOM::Element<std::string> noneTarget; + + for (int i = 0; i < transitionSet.size(); i++) { + Arabica::DOM::Element<std::string> transElem = Arabica::DOM::Element<std::string>(transitionSet[i]); + if (HAS_ATTR(transElem, "eventexpr")) { + ERROR_EXECUTION_THROW("Cannot flatten document with eventexpr attributes"); + } + if (HAS_ATTR(transElem, "event")) { + foundWithEvent = true; + withEvent = transElem; + if (foundEventLess) + break; + } else { + foundEventLess = true; + noneEvent = transElem; + if (foundWithEvent) + break; + } + if (HAS_ATTR(transElem, "target")) { + foundWithTarget = true; + withTarget = transElem; + if (foundTargetLess) + break; + } else { + foundTargetLess = true; + noneTarget = transElem; + if (foundWithTarget) + break; + } + + } + + // do not mix eventless and event transitions + if (foundEventLess && foundWithEvent) { + if (flattener->_keepInvalidTransitions) { + invalidReason = MIXES_EVENT_SPONTANEOUS; + invalidMsg = "Mixes (non-)spontaneous"; + } + isValid = false; + return; + } + + // 403c vs 229 / 403b - solved via filterChildEnabled + if (foundTargetLess && foundWithTarget) { +// isValid = false; +// return; + } + + isEventless = foundEventLess; + isTargetless = !foundWithTarget; + + // is there a set of event names that would enable this conflict-free transition set? + if (foundWithEvent) { + // get the set of longest event descriptors that will enable this transition set + eventNames = getCommonEvents(transitionSet); + if (eventNames.size() == 0) { +// LOG(INFO) << "No event will activate this conflict-free subset" << std::endl; + if (flattener->_keepInvalidTransitions) { + invalidReason = NO_COMMON_EVENT; + invalidMsg = "No common event"; + } + isValid = false; + return; + } else { + std::string seperator = ""; + for (std::list<std::string>::iterator eventIter = eventNames.begin(); + eventIter != eventNames.end(); + eventIter++) { + eventDesc += seperator + *eventIter; + seperator = " "; + } + } + if (eventDesc.size() == 0) + eventDesc = "*"; + } + + // extract conditions and history targets + std::list<std::string> conditions; + for (int i = 0; i < transitionSet.size(); i++) { + Arabica::DOM::Element<std::string> transElem = Arabica::DOM::Element<std::string>(transitionSet[i]); + // gather conditions while we are iterating anyway + if (HAS_ATTR(transElem, "cond")) { + conditions.push_back(boost::trim_copy(ATTR(transElem, "cond"))); + } + + std::list<std::string> targets = InterpreterImpl::tokenizeIdRefs(ATTR(transElem, "target")); + std::list<std::string>::iterator targetIter = targets.begin(); + while(targetIter != targets.end()) { +// std::cout << "// " << *targetIter << std::endl; + if (flattener->_historyTargets.find(*targetIter) != flattener->_historyTargets.end()) { + histTargets.insert(*targetIter); + } + targetIter++; + } +// std::cout << std::endl << std::endl; + } + + seperator = ""; + for (std::vector<Element<std::string> >::iterator transIter = interpreter->indexedTransitions.begin(); transIter != interpreter->indexedTransitions.end(); transIter++) { + const Element<std::string>& refTrans = *transIter; + if (!HAS_ATTR(refTrans, "priority")) + continue; + if (InterpreterImpl::isMember(refTrans, transitionSet)) { + members += seperator + ATTR(refTrans, "priority"); + } else { + members += seperator; + for (int i = 0; i < ATTR(refTrans, "priority").size(); i++) { + members += " "; + } + } + seperator = " "; + } + + // if (members == " 4 6 7 ") + // std::cout << "asdfadf"; + + if (conditions.size() > 1) { + condition = dataModel.andExpressions(conditions); + if (condition.size() == 0) { + LOG(ERROR) << "Datamodel does not support to conjungate expressions!" << std::endl; + } + } else if (conditions.size() == 1) { + condition = conditions.front(); + } +} + +Arabica::XPath::NodeSet<std::string> GlobalState::getActiveStates() { + return interpreter->refsToStates(activeStatesRefs); +} + +Arabica::XPath::NodeSet<std::string> GlobalState::getAlreadyEnteredStates() { + return interpreter->refsToStates(alreadyEnteredStatesRefs); +} + +std::map<std::string, Arabica::XPath::NodeSet<std::string> > GlobalState::getHistoryStates() { + std::map<std::string, Arabica::XPath::NodeSet<std::string> > historyValue; + for (std::map<std::string, std::set<int> >::iterator histIter = historyStatesRefs.begin(); histIter != historyStatesRefs.end(); histIter++) { + historyValue[histIter->first] = interpreter->refsToStates(histIter->second); + } + return historyValue; +} + + +Arabica::XPath::NodeSet<std::string> GlobalTransition::getTransitions() const { + return interpreter->refsToTransitions(transitionRefs); +} + +std::list<std::string> GlobalTransition::getCommonEvents(const NodeSet<std::string>& transitions) { + std::list<std::string> prefixes; + std::list<std::string> longestPrefixes; + + for (int i = 0; i < transitions.size(); i++) { + // for every transition + std::list<std::string> eventNames = InterpreterImpl::tokenizeIdRefs(ATTR_CAST(transitions[i], "event")); + + for (std::list<std::string>::iterator eventNameIter = eventNames.begin(); + eventNameIter != eventNames.end(); + eventNameIter++) { + // for every event descriptor + std::string eventName = *eventNameIter; + + // remove trailing .* + if (eventName.find("*", eventName.size() - 1) != std::string::npos) + eventName = eventName.substr(0, eventName.size() - 1); + if (eventName.find(".", eventName.size() - 1) != std::string::npos) + eventName = eventName.substr(0, eventName.size() - 1); + + bool isMatching = true; + for (int j = 0; j < transitions.size(); j++) { + // check if token would activate all other transitions + if (i == j) + continue; + if (!InterpreterImpl::nameMatch(ATTR_CAST(transitions[j], "event"), eventName)) { + isMatching = false; + break; + } + } + if (isMatching) { + prefixes.push_back(eventName); + } + } + } + + // from the set of event names, remove those that are prefixes + for (std::list<std::string>::iterator outerEventNameIter = prefixes.begin(); + outerEventNameIter != prefixes.end(); + outerEventNameIter++) { + for (std::list<std::string>::iterator innerEventNameIter = prefixes.begin(); + innerEventNameIter != prefixes.end(); + innerEventNameIter++) { + if (!iequals(*outerEventNameIter, *innerEventNameIter) && InterpreterImpl::nameMatch(*outerEventNameIter, *innerEventNameIter)) { + goto IS_PREFIX; + } + } + longestPrefixes.push_back(*outerEventNameIter); +IS_PREFIX: + ; + } + return longestPrefixes; +} + +} diff --git a/src/uscxml/transform/ChartToFSM.h b/src/uscxml/transform/ChartToFSM.h index c4d2da3..1dc8813 100644 --- a/src/uscxml/transform/ChartToFSM.h +++ b/src/uscxml/transform/ChartToFSM.h @@ -61,6 +61,8 @@ public: } static uint64_t stateMachineComplexity(const Arabica::DOM::Element<std::string>& root, Complexity::Variant variant = IGNORE_NOTHING); + static std::list<std::set<Arabica::DOM::Element<std::string> > > getAllConfigurations(const Arabica::DOM::Element<std::string>& root); + static std::map<size_t, size_t> getTransitionHistogramm(const Arabica::DOM::Element<std::string>& root); protected: static Complexity calculateStateMachineComplexity(const Arabica::DOM::Element<std::string>& root); @@ -89,8 +91,8 @@ public: std::string stateId; std::string activeId; - long activeIndex; - long index; + unsigned long activeIndex; + unsigned long index; bool isFinal; ChartToFSM* interpreter; @@ -105,11 +107,40 @@ public: class USCXML_API GlobalTransition { public: + enum InvalidReason { + MIXES_EVENT_SPONTANEOUS, + NO_COMMON_EVENT, + CHILD_ENABLED, + SAME_SOURCE_STATE, + UNCONDITIONAL_SUPERSET, + UNCONDITIONAL_MATCH, + PREEMPTING_MEMBERS + }; + class Action { public: bool operator<(const Action& other) const { + if ((onEntry && !other.onEntry) || (!onEntry && other.onEntry)) + return true; + if ((raiseDone && !other.raiseDone) || (!raiseDone && other.raiseDone)) + return true; + if ((onExit && !other.onExit) || (!onExit && other.onExit)) + return true; + if ((transition && !other.transition) || (!transition && other.transition)) + return true; + if ((entered && !other.entered) || (!entered && other.entered)) + return true; + if ((exited && !other.exited) || (!exited && other.exited)) + return true; + if ((invoke && !other.invoke) || (!invoke && other.invoke)) + return true; + if ((uninvoke && !other.uninvoke) || (!uninvoke && other.uninvoke)) + return true; + if (onEntry < other.onEntry) return onEntry < other.onEntry; + if (raiseDone < other.raiseDone) + return raiseDone < other.raiseDone; if (onExit < other.onExit) return onExit < other.onExit; if (transition < other.transition) @@ -132,6 +163,8 @@ public: return !operator==(other); } + friend USCXML_API std::ostream& operator<< (std::ostream& os, const Action& action); + typedef std::list<GlobalTransition::Action>::iterator iter_t; Arabica::DOM::Element<std::string> onEntry; @@ -141,6 +174,7 @@ public: Arabica::DOM::Element<std::string> exited; Arabica::DOM::Element<std::string> invoke; Arabica::DOM::Element<std::string> uninvoke; + Arabica::DOM::Element<std::string> raiseDone; }; @@ -148,6 +182,9 @@ public: static GlobalTransition* copyWithoutExecContent(GlobalTransition* other); bool isValid; // constructor will determine, calling code will delete if not + std::string invalidMsg; + InvalidReason invalidReason; + bool isEventless; // whether or not all our transitions are eventless bool isTargetless; // whether or not all our transitions are eventless bool isSubset; // there is a superset to this set @@ -189,17 +226,66 @@ protected: std::list<std::string> getCommonEvents(const Arabica::XPath::NodeSet<std::string>& transitions); }; +USCXML_API std::ostream& operator<< (std::ostream& os, const GlobalTransition::Action& action); + +class TransitionTreeNode { +public: + enum TransitionTreeNodeType { + TYPE_UNDEFINED, + TYPE_PARALLEL, + TYPE_NESTED, + TYPE_TRANSITION + }; + + TransitionTreeNode() + : prevTransition(NULL), + nextTransition(NULL), + firstTransition(NULL), + firstState(NULL), + parent(NULL), + type(TYPE_UNDEFINED) {} + + virtual ~TransitionTreeNode() { + for (std::list<TransitionTreeNode*>::iterator childIter = children.begin(); childIter != children.end(); childIter++) { + delete(*childIter); + } + } + + void dump(int indent = 0); + + TransitionTreeNode* prevTransition; + TransitionTreeNode* nextTransition; + Arabica::DOM::Element<std::string> transition; + + Arabica::DOM::Element<std::string> state; + TransitionTreeNode* firstTransition; + TransitionTreeNode* lastTransition; + TransitionTreeNode* firstState; + + TransitionTreeNode* parent; + std::list<TransitionTreeNode*> children; + std::string nodeId; + + TransitionTreeNodeType type; + + bool operator<(const TransitionTreeNode& other) const { + return nodeId < other.nodeId; + } + +}; class USCXML_API ChartToFSM : public InterpreterRC, public InterpreterMonitor { public: + ChartToFSM(const Interpreter& other); virtual ~ChartToFSM(); + void indexTransitions(); + Arabica::DOM::Document<std::string> getDocument() const; // overwrite to return flat FSM + protected: - ChartToFSM(const Interpreter& other); - Arabica::DOM::Document<std::string> getDocument() const; // overwrite to return flat FSM InterpreterState interpret(); - + GlobalState* _start; Arabica::DOM::Document<std::string> _flatDoc; std::map<std::string, GlobalState*> _globalConf; @@ -209,13 +295,16 @@ protected: uint32_t getMinInternalQueueLength(uint32_t defaultVal); uint32_t getMinExternalQueueLength(uint32_t defaultVal); + bool _keepInvalidTransitions; + bool _transitionsFromTree; + + std::vector<Arabica::DOM::Element<std::string> > indexedTransitions; + std::vector<Arabica::DOM::Element<std::string> > indexedStates; + private: Arabica::XPath::NodeSet<std::string> refsToStates(const std::set<int>&); Arabica::XPath::NodeSet<std::string> refsToTransitions(const std::set<int>&); - std::vector<Arabica::DOM::Element<std::string> > indexedTransitions; - std::vector<Arabica::DOM::Element<std::string> > indexedStates; - // gather executable content per microstep void executeContent(const Arabica::DOM::Element<std::string>& content, bool rethrow = false); @@ -235,8 +324,10 @@ private: virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing); void explode(); - void getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap); + void getPotentialTransitionsForConfFromPowerSet(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap); + void getPotentialTransitionsForConfFromTree(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap); // void labelTransitions(); + TransitionTreeNode* buildTransTree(const Arabica::DOM::Element<std::string>& root, const std::string& nodeId); void indexTransitions(const Arabica::DOM::Element<std::string>& root); void annotateRaiseAndSend(const Arabica::DOM::Element<std::string>& root); @@ -256,12 +347,14 @@ private: uint64_t _perfTransTotal; uint64_t _perfTransUsed; uint64_t _perfStatesProcessed; + uint64_t _perfStatesTotal; uint64_t _perfStatesSkippedProcessed; uint64_t _perfStatesSkippedTotal; uint64_t _perfStatesCachedProcessed; uint64_t _perfStatesCachedTotal; uint64_t _perfMicroStepProcessed; uint64_t _perfMicroStepTotal; + uint64_t _perfStackSize; uint64_t _lastTimeStamp; size_t _lastTransientStateId; @@ -277,6 +370,10 @@ private: size_t _doneEventRaiseTolerance; GlobalTransition* _currGlobalTransition; + std::map<std::string, std::map<std::string, GlobalTransition*> > _confToTransitions; + + TransitionTreeNode* _transTree; + std::map<Arabica::DOM::Element<std::string>, TransitionTreeNode*> _stateToTransTreeNode; friend class GlobalTransition; friend class GlobalState; diff --git a/src/uscxml/transform/ChartToFSM.h.new b/src/uscxml/transform/ChartToFSM.h.new new file mode 100644 index 0000000..2ec2f44 --- /dev/null +++ b/src/uscxml/transform/ChartToFSM.h.new @@ -0,0 +1,303 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef CHARTTOFSM_H_IOKPYEBY +#define CHARTTOFSM_H_IOKPYEBY + +#include "uscxml/DOMUtils.h" +#include "uscxml/interpreter/InterpreterRC.h" +#include <DOM/Document.hpp> +#include <DOM/Node.hpp> +#include <XPath/XPath.hpp> +#include <ostream> +#include <list> + +namespace uscxml { +class GlobalState; +class GlobalTransition; +class ChartToFSM; + +class USCXML_API Complexity { +public: + + enum Variant { + IGNORE_NOTHING, + IGNORE_HISTORY, + IGNORE_NESTED_DATA, + IGNORE_HISTORY_AND_NESTED_DATA, + }; + + Complexity() : value(0), nestedData(0) {} + Complexity(uint64_t value) : value(value), nestedData(0) {} + + Complexity& operator+=(const Complexity& rhs) { + value += rhs.value; + nestedData += rhs.nestedData; + history.insert(history.end(), rhs.history.begin(), rhs.history.end()); + return *this; + } + + Complexity& operator*=(const Complexity& rhs) { + value *= rhs.value; + nestedData += rhs.nestedData; + history.insert(history.end(), rhs.history.begin(), rhs.history.end()); + return *this; + } + + static uint64_t stateMachineComplexity(const Arabica::DOM::Element<std::string>& root, Complexity::Variant variant = IGNORE_NOTHING); + +protected: + static Complexity calculateStateMachineComplexity(const Arabica::DOM::Element<std::string>& root); + + uint64_t value; + uint64_t nestedData; + std::list<uint64_t> history; +}; + +class USCXML_API GlobalState { +public: + + GlobalState() {} + GlobalState(const Arabica::DOM::Node<std::string>& globalState); + GlobalState(const Arabica::XPath::NodeSet<std::string>& activeStates, + const Arabica::XPath::NodeSet<std::string>& alreadyEnteredStates, // we need to remember for binding=late + const std::map<std::string, Arabica::XPath::NodeSet<std::string> >& historyStates, + const std::string& xmlNSPrefix, + ChartToFSM* flattener); + + std::set<int> activeStatesRefs; + std::set<int> alreadyEnteredStatesRefs; + std::map<std::string, std::set<int> > historyStatesRefs; + + std::list<GlobalTransition*> sortedOutgoing; + std::string stateId; + std::string activeId; + + unsigned long activeIndex; + unsigned long index; + bool isFinal; + + ChartToFSM* interpreter; + + Arabica::XPath::NodeSet<std::string> getActiveStates(); + Arabica::XPath::NodeSet<std::string> getAlreadyEnteredStates(); + std::map<std::string, Arabica::XPath::NodeSet<std::string> > getHistoryStates(); + +}; + + +class USCXML_API GlobalTransition { +public: + enum InvalidReason { + MIXES_EVENT_SPONTANEOUS, + NO_COMMON_EVENT, + CHILD_ENABLED, + SAME_SOURCE_STATE, + UNCONDITIONAL_SUPERSET, + UNCONDITIONAL_MATCH, + PREEMPTING_MEMBERS + }; + + class Action { + public: + bool operator<(const Action& other) const { + if (onEntry < other.onEntry) + return onEntry < other.onEntry; + if (onExit < other.onExit) + return onExit < other.onExit; + if (transition < other.transition) + return transition < other.transition; + if (entered < other.entered) + return entered < other.entered; + if (exited < other.exited) + return exited < other.exited; + if (invoke < other.invoke) + return invoke < other.invoke; + if (uninvoke < other.uninvoke) + return uninvoke < other.uninvoke; + return false; + } + + bool operator==(const Action& other) const { + return !(other < *this) && !(*this < other); + } + bool operator!=(const Action& other) const { + return !operator==(other); + } + + typedef std::list<GlobalTransition::Action>::iterator iter_t; + + Arabica::DOM::Element<std::string> onEntry; + Arabica::DOM::Element<std::string> onExit; + Arabica::DOM::Element<std::string> transition; + Arabica::DOM::Element<std::string> entered; + Arabica::DOM::Element<std::string> exited; + Arabica::DOM::Element<std::string> invoke; + Arabica::DOM::Element<std::string> uninvoke; + + }; + + GlobalTransition(const Arabica::XPath::NodeSet<std::string>& transitions, DataModel dataModel, ChartToFSM* flattener); + static GlobalTransition* copyWithoutExecContent(GlobalTransition* other); + + bool isValid; // constructor will determine, calling code will delete if not + std::string invalidMsg; + InvalidReason invalidReason; + + bool isEventless; // whether or not all our transitions are eventless + bool isTargetless; // whether or not all our transitions are eventless + bool isSubset; // there is a superset to this set + bool hasExecutableContent; + + uint32_t eventsRaised; // internal events this transition will raise + uint32_t eventsSent; // external events this transition will send + uint32_t eventsChainRaised; // maximum number of internal events raised when taking this transition in a chain + uint32_t eventsChainSent; // maximum number of external events raised when taking this transition in a chain + + std::set<int> startTransitionRefs; // indices of eventful transitions that might trigger this transition + + std::set<int> transitionRefs; // indizes of constituting transitions + Arabica::XPath::NodeSet<std::string> getTransitions() const; + + std::list<std::string> eventNames; // the list of longest event names that will enable this set + std::string eventDesc; // space-seperated eventnames for convenience + std::string condition; // conjunction of all the set's conditions + std::string members; // a convenience string listing all constituting transitions + + // executable content we gathered when we took the transition + std::list<Action> actions; + + std::string transitionId; + std::string source; + std::string destination; + std::string activeDestination; + + GlobalTransition* historyBase; // we have a base transition that left our source with no history (-> we are a history transition) + std::list<GlobalTransition*> historyTrans; // transitions from the same source but different histories + std::set<std::string> histTargets; // constituting targets to history states + + long index; + ChartToFSM* interpreter; + + bool operator< (const GlobalTransition& other) const; + +protected: + std::list<std::string> getCommonEvents(const Arabica::XPath::NodeSet<std::string>& transitions); +}; + + +class USCXML_API ChartToFSM : public InterpreterRC, public InterpreterMonitor { +public: + ChartToFSM(const Interpreter& other); + virtual ~ChartToFSM(); + + void indexTransitions(); + Arabica::DOM::Document<std::string> getDocument() const; // overwrite to return flat FSM + +protected: + + InterpreterState interpret(); + + GlobalState* _start; + Arabica::DOM::Document<std::string> _flatDoc; + std::map<std::string, GlobalState*> _globalConf; + std::map<std::string, GlobalState*> _activeConf; // potentially enabled transition sets per active configuration + std::map<std::string, Arabica::DOM::Element<std::string> > _historyTargets; // ids of all history states + + uint32_t getMinInternalQueueLength(uint32_t defaultVal); + uint32_t getMinExternalQueueLength(uint32_t defaultVal); + + bool _keepInvalidTransitions; + +private: + Arabica::XPath::NodeSet<std::string> refsToStates(const std::set<int>&); + Arabica::XPath::NodeSet<std::string> refsToTransitions(const std::set<int>&); + + std::vector<Arabica::DOM::Element<std::string> > indexedTransitions; + std::vector<Arabica::DOM::Element<std::string> > indexedStates; + + // gather executable content per microstep + void executeContent(const Arabica::DOM::Element<std::string>& content, bool rethrow = false); + + // invoke and uninvoke + virtual void invoke(const Arabica::DOM::Element<std::string>& element); + virtual void cancelInvoke(const Arabica::DOM::Element<std::string>& element); + + // override to do nothing + void send(const Arabica::DOM::Element<std::string>& element) {} + void internalDoneSend(const Arabica::DOM::Element<std::string>& state); + + // InterpreterMonitor + virtual void beforeMicroStep(Interpreter interpreter); + virtual void onStableConfiguration(Interpreter interpreter); + virtual void beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing); + virtual void beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing); + virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing); + + void explode(); + void getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap); +// void labelTransitions(); + + void indexTransitions(const Arabica::DOM::Element<std::string>& root); + void annotateRaiseAndSend(const Arabica::DOM::Element<std::string>& root); + bool hasForeachInBetween(const Arabica::DOM::Node<std::string>& ancestor, const Arabica::DOM::Node<std::string>& child); + void updateRaisedAndSendChains(GlobalState* state, GlobalTransition* source, std::set<GlobalTransition*> visited); + + void reassembleFromFlat(); + + std::list<GlobalTransition*> sortTransitions(std::list<GlobalTransition*> list); + + // we need this static as we use it in a sort function + static std::map<Arabica::DOM::Node<std::string>, Arabica::DOM::Node<std::string> > _transParents; + + static bool filterSameState(const Arabica::XPath::NodeSet<std::string>& transitions); + + uint64_t _perfTransProcessed; + uint64_t _perfTransTotal; + uint64_t _perfTransUsed; + uint64_t _perfStatesProcessed; + uint64_t _perfStatesSkippedProcessed; + uint64_t _perfStatesSkippedTotal; + uint64_t _perfStatesCachedProcessed; + uint64_t _perfStatesCachedTotal; + uint64_t _perfMicroStepProcessed; + uint64_t _perfMicroStepTotal; + uint64_t _lastTimeStamp; + + size_t _lastTransientStateId; + size_t _lastStateIndex; + size_t _lastActiveIndex; + size_t _lastTransIndex; + + bool _alreadyFlat; + + bool _skipEventChainCalculations; + size_t _maxEventSentChain; + size_t _maxEventRaisedChain; + size_t _doneEventRaiseTolerance; + + GlobalTransition* _currGlobalTransition; + + friend class GlobalTransition; + friend class GlobalState; +}; + +} + +#endif /* end of include guard: CHARTTOFSM_H_IOKPYEBY */ diff --git a/src/uscxml/transform/ChartToFlatSCXML.cpp b/src/uscxml/transform/ChartToFlatSCXML.cpp index d9e2586..c554218 100644 --- a/src/uscxml/transform/ChartToFlatSCXML.cpp +++ b/src/uscxml/transform/ChartToFlatSCXML.cpp @@ -18,6 +18,7 @@ */ #include "ChartToFlatSCXML.h" +#include "uscxml/Convenience.h" #include "uscxml/transform/FlatStateIdentifier.h" #define CREATE_TRANSIENT_STATE_WITH_CHILDS(stateId) \ @@ -25,6 +26,10 @@ if (childs.size() > 0) { \ Element<std::string> transientState = _flatDoc.createElementNS(_nsInfo.nsURL, "state"); \ _nsInfo.setPrefix(transientState);\ transientState.setAttribute("transient", "true"); \ + for (std::list<Arabica::DOM::Comment<std::string> >::iterator commentIter = pendingComments.begin(); commentIter != pendingComments.end(); commentIter++) {\ + transientState.appendChild(*commentIter); \ + }\ + pendingComments.clear(); \ if (stateId.length() > 0) \ transientState.setAttribute("id", stateId); \ for (int i = 0; i < childs.size(); i++) { \ @@ -41,7 +46,7 @@ using namespace Arabica::DOM; using namespace Arabica::XPath; ChartToFlatSCXML::operator Interpreter() { - if (!HAS_ATTR(_scxml, "flat") || !DOMUtils::attributeIsTrue(ATTR(_scxml, "flat"))) { + if (!HAS_ATTR(_scxml, "flat") || !stringIsTrue(ATTR(_scxml, "flat"))) { createDocument(); } @@ -53,36 +58,37 @@ Transformer ChartToFlatSCXML::transform(const Interpreter& other) { } void ChartToFlatSCXML::writeTo(std::ostream& stream) { - if (!HAS_ATTR(_scxml, "flat") || !DOMUtils::attributeIsTrue(ATTR(_scxml, "flat"))) { + if (!HAS_ATTR(_scxml, "flat") || !stringIsTrue(ATTR(_scxml, "flat"))) { createDocument(); } - - char* withDebugAttrs = getenv("USCXML_FLAT_WITH_DEBUG_ATTRS"); - if (withDebugAttrs == NULL || !DOMUtils::attributeIsTrue(withDebugAttrs)) { - // remove all debug attributes - NodeSet<std::string> elementNodes = filterChildType(Node_base::ELEMENT_NODE, _scxml, true); - for (int i = 0; i < elementNodes.size(); i++) { - Element<std::string> element(elementNodes[i]); - if (HAS_ATTR(element, "send")) - element.removeAttribute("send"); - if (HAS_ATTR(element, "raise")) - element.removeAttribute("raise"); - if (HAS_ATTR(element, "members")) - element.removeAttribute("members"); - if (HAS_ATTR(element, "ref")) - element.removeAttribute("ref"); - if (HAS_ATTR(element, "final-target")) - element.removeAttribute("final-target"); - } + + // remove all debug attributes + NodeSet<std::string> elementNodes = filterChildType(Node_base::ELEMENT_NODE, _scxml, true); + for (int i = 0; i < elementNodes.size(); i++) { + Element<std::string> element(elementNodes[i]); + if (!envVarIsTrue("USCXML_ANNOTATE_GLOBAL_TRANS_SENDS") && HAS_ATTR(element, "send")) + element.removeAttribute("send"); + if (!envVarIsTrue("USCXML_ANNOTATE_GLOBAL_TRANS_RAISES") && HAS_ATTR(element, "raise")) + element.removeAttribute("raise"); + if (!envVarIsTrue("USCXML_ANNOTATE_GLOBAL_TRANS_MEMBERS") && HAS_ATTR(element, "members")) + element.removeAttribute("members"); + if (!envVarIsTrue("USCXML_ANNOTATE_GLOBAL_TRANS_PRIO") && HAS_ATTR(element, "priority")) + element.removeAttribute("priority"); + if (!envVarIsTrue("USCXML_ANNOTATE_GLOBAL_STATE_STEP") && HAS_ATTR(element, "step")) + element.removeAttribute("step"); + if (HAS_ATTR(element, "final-target")) + element.removeAttribute("final-target"); } + if (envVarIsTrue("USCXML_FLAT_FSM_METRICS_ONLY")) + return; stream << _scxml; } void ChartToFlatSCXML::createDocument() { - if (HAS_ATTR(_scxml, "flat") && DOMUtils::attributeIsTrue(ATTR(_scxml, "flat"))) + if (HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat"))) return; { @@ -99,6 +105,9 @@ void ChartToFlatSCXML::createDocument() { if (_start == NULL) interpret(); // only if not already flat! + if (envVarIsTrue("USCXML_FLAT_FSM_METRICS_ONLY")) + return; + Element<std::string> _origSCXML = _scxml; _scxml = _flatDoc.createElementNS(_nsInfo.nsURL, "scxml"); @@ -190,7 +199,7 @@ void ChartToFlatSCXML::appendGlobalStateNode(GlobalState* globalState) { Element<std::string> state = _flatDoc.createElementNS(_nsInfo.nsURL, "state"); _nsInfo.setPrefix(state); - state.setAttribute("ref", toStr(globalState->index)); + state.setAttribute("step", toStr(globalState->index)); state.setAttribute("id", globalState->stateId); if (globalState->isFinal) @@ -247,13 +256,17 @@ Node<std::string> ChartToFlatSCXML::globalTransitionToNode(GlobalTransition* glo // gather content for new transient state NodeSet<std::string> childs; + + // aggregated entering / exiting to avoid states without childs while still labeling + std::list<Arabica::DOM::Comment<std::string> > pendingComments; + // iterate all actions taken during the transition for (std::list<GlobalTransition::Action>::iterator actionIter = globalTransition->actions.begin(); actionIter != globalTransition->actions.end(); actionIter++) { if (actionIter->transition) { - // DETAIL_EXEC_CONTENT(transition, actionIter); + // DETAIL_EXEC_CONTENT(transition, actionIter); Element<std::string> onexit = _flatDoc.createElementNS(_nsInfo.nsURL, "onexit"); _nsInfo.setPrefix(onexit); @@ -264,29 +277,48 @@ Node<std::string> ChartToFlatSCXML::globalTransitionToNode(GlobalTransition* glo child = child.getNextSibling(); } // only append if there is something done - if (onexit.hasChildNodes()) + std::stringstream commentSS; + commentSS << " Transition in '" << ATTR_CAST(getSourceState(actionIter->transition), "id") << "'"; + if (HAS_ATTR(actionIter->transition, "event")) + commentSS << " for event '" << ATTR(actionIter->transition, "event") << "'"; + if (HAS_ATTR(actionIter->transition, "cond")) + commentSS << " with condition '" << ATTR(actionIter->transition, "cond") << "'"; + if (HAS_ATTR(actionIter->transition, "target")) + commentSS << " to target '" << ATTR(actionIter->transition, "target") << "'"; + commentSS << " "; + + if (onexit.hasChildNodes()) { + if (envVarIsTrue("USCXML_ANNOTATE_VERBOSE_COMMENTS")) + childs.push_back(_flatDoc.createComment(commentSS.str())); childs.push_back(onexit); + } else { + if (envVarIsTrue("USCXML_ANNOTATE_VERBOSE_COMMENTS")) + pendingComments.push_back(_flatDoc.createComment(commentSS.str())); + } continue; } if (actionIter->onExit) { - // DETAIL_EXEC_CONTENT(onExit, actionIter); + // DETAIL_EXEC_CONTENT(onExit, actionIter); childs.push_back(actionIter->onExit); continue; } if (actionIter->onEntry) { - // DETAIL_EXEC_CONTENT(onEntry, actionIter); + // DETAIL_EXEC_CONTENT(onEntry, actionIter); childs.push_back(actionIter->onEntry); continue; } - + + if (actionIter->raiseDone) { + // DETAIL_EXEC_CONTENT(raiseDone, actionIter); + childs.push_back(actionIter->raiseDone); + continue; + } + if (actionIter->invoke) { - // DETAIL_EXEC_CONTENT(invoke, actionIter); - if (!globalTransition->isTargetless) { - // CREATE_TRANSIENT_STATE_WITH_CHILDS(FlatStateIdentifier::toStateId(currActiveStates)); - } + // DETAIL_EXEC_CONTENT(invoke, actionIter); Element<std::string> invokeElem = Element<std::string>(actionIter->invoke); invokeElem.setAttribute("persist", "true"); childs.push_back(invokeElem); @@ -294,7 +326,7 @@ Node<std::string> ChartToFlatSCXML::globalTransitionToNode(GlobalTransition* glo } if (actionIter->uninvoke) { - // DETAIL_EXEC_CONTENT(uninvoke, actionIter); + // DETAIL_EXEC_CONTENT(uninvoke, actionIter); Element<std::string> uninvokeElem = _flatDoc.createElementNS(_nsInfo.nsURL, "uninvoke"); _nsInfo.setPrefix(uninvokeElem); @@ -315,17 +347,27 @@ Node<std::string> ChartToFlatSCXML::globalTransitionToNode(GlobalTransition* glo } if (actionIter->exited) { - // std::cerr << " exited(" << ATTR_CAST(actionIter->exited, "id") << ")"; currActiveStates.remove(ATTR_CAST(actionIter->exited, "id")); if (childs.size() > 0) { + if (envVarIsTrue("USCXML_ANNOTATE_VERBOSE_COMMENTS")) + childs.push_back(_flatDoc.createComment(" Exiting " + ATTR_CAST(actionIter->exited, "id") + " ")); CREATE_TRANSIENT_STATE_WITH_CHILDS(FlatStateIdentifier::toStateId(currActiveStates)); // create a new transient state to update its id + } else { + // enqueue for next actual state + if (envVarIsTrue("USCXML_ANNOTATE_VERBOSE_COMMENTS")) + pendingComments.push_back(_flatDoc.createComment(" Exiting " + ATTR_CAST(actionIter->exited, "id") + " ")); } } if (actionIter->entered) { - // std::cerr << " entered(" << ATTR_CAST(actionIter->entered, "id") << ")"; - if (childs.size() > 0) + if (childs.size() > 0) { + if (envVarIsTrue("USCXML_ANNOTATE_VERBOSE_COMMENTS")) + childs.push_back(_flatDoc.createComment(" Entering " + ATTR_CAST(actionIter->entered, "id") + " ")); CREATE_TRANSIENT_STATE_WITH_CHILDS(FlatStateIdentifier::toStateId(currActiveStates)); // create a new transient state to update its id + } else { + if (envVarIsTrue("USCXML_ANNOTATE_VERBOSE_COMMENTS")) + pendingComments.push_back(_flatDoc.createComment(" Entering " + ATTR_CAST(actionIter->entered, "id") + " ")); + } currActiveStates.push_back(ATTR_CAST(actionIter->entered, "id")); // we entered a new child - check if it has a datamodel and we entered for the first time @@ -336,22 +378,8 @@ Node<std::string> ChartToFlatSCXML::globalTransitionToNode(GlobalTransition* glo } } } - if (!globalTransition->isTargetless) { - // CREATE_TRANSIENT_STATE_WITH_CHILDS(FlatStateIdentifier::toStateId(currActiveStates)) - } } - // std::cerr << std::endl; - - // if (globalTransition->isTargetless) { - // for (int i = 0; i < childs.size(); i++) { - // Node<std::string> imported = _flatDoc.importNode(childs[i], true); - // transition.appendChild(imported); - // // CREATE_TRANSIENT_STATE_WITH_CHILDS(FlatStateIdentifier::toStateId(currActiveStates)) - // } - // return transition; - // } - CREATE_TRANSIENT_STATE_WITH_CHILDS(FlatStateIdentifier::toStateId(currActiveStates)) if (transientStateChain.size() > 0) { @@ -383,7 +411,20 @@ Node<std::string> ChartToFlatSCXML::globalTransitionToNode(GlobalTransition* glo // last one points to actual target assert(prevExitTransitionElem); prevExitTransitionElem.setAttribute("target", globalTransition->destination); +#if 0 + } else if (transientStateChain.size() == 1) { + Element<std::string> transientStateElem = Element<std::string>(transientStateChain[0]); + transientStateElem.setAttribute("onlyOne", "yes!"); + Element<std::string> exitTransition = _flatDoc.createElementNS(_nsInfo.nsURL, "transition"); + _nsInfo.setPrefix(exitTransition); + exitTransition.setAttribute("target", globalTransition->destination); + + transientStateElem.appendChild(exitTransition); + + _scxml.appendChild(transientStateElem); + transition.setAttribute("target", transientStateElem.getAttribute("id")); +#endif } else { transition.setAttribute("target", globalTransition->destination); } diff --git a/src/uscxml/transform/ChartToMinimalSCXML.cpp b/src/uscxml/transform/ChartToMinimalSCXML.cpp index 270794d..ecfa12b 100644 --- a/src/uscxml/transform/ChartToMinimalSCXML.cpp +++ b/src/uscxml/transform/ChartToMinimalSCXML.cpp @@ -36,7 +36,7 @@ Transformer ChartToMinimalSCXML::transform(const Interpreter& other) { return boost::shared_ptr<TransformerImpl>(new ChartToMinimalSCXML(other)); } -ChartToMinimalSCXML::ChartToMinimalSCXML(const Interpreter& other) : TransformerImpl(), _retainAsComments(false) { +ChartToMinimalSCXML::ChartToMinimalSCXML(const Interpreter& other) : TransformerImpl(), _retainAsComments(false), _step(1) { cloneFrom(other.getImpl()); // a bit messy but needed for SCXML IO Processor with session id target @@ -85,10 +85,7 @@ void ChartToMinimalSCXML::writeTo(std::ostream& stream) { } char* waitForEnv = getenv("USCXML_MINIMIZE_WAIT_MS"); - char* waitForCmpl = getenv("USCXML_MINIMIZE_WAIT_FOR_COMPLETION"); - char* retainAsComments = getenv("USCXML_MINIMIZE_RETAIN_AS_COMMENTS"); - if (retainAsComments != NULL && DOMUtils::attributeIsTrue(retainAsComments)) - _retainAsComments = true; + _retainAsComments = envVarIsTrue("USCXML_MINIMIZE_RETAIN_AS_COMMENTS"); long waitFor = -1; @@ -100,7 +97,7 @@ void ChartToMinimalSCXML::writeTo(std::ostream& stream) { } } - if (waitForCmpl != NULL && DOMUtils::attributeIsTrue(waitForCmpl)) { + if (envVarIsTrue("USCXML_MINIMIZE_WAIT_FOR_COMPLETION")) { interpret(); } else { start(); @@ -181,14 +178,15 @@ void ChartToMinimalSCXML::removeUnvisited(Arabica::DOM::Node<std::string>& node) // detach unvisited nodes from DOM if (_visited.find(node) == _visited.end()) { - std::cout << DOMUtils::xPathForNode(node) << std::endl; + std::cerr << DOMUtils::xPathForNode(node) << std::endl; if (_retainAsComments) { std::stringstream oldContent; oldContent << node; node.getParentNode().replaceChild(_document.createComment(boost::replace_all_copy(oldContent.str(),"--", "-")), node); } else { // removeChildren is not working as expected - node.getParentNode().replaceChild(_document.createTextNode(""), node); +// node.getParentNode().replaceChild(_document.createTextNode(""), node); + node.getParentNode().removeChild(node); } return; } @@ -231,11 +229,29 @@ void ChartToMinimalSCXML::beforeTakingTransition(Interpreter interpreter, const markAsVisited(Arabica::DOM::Element<std::string>(targets[i])); } markAsVisited(transition); + + std::stringstream commentSS; + if (HAS_ATTR(transition, "event")) { + commentSS << " Step #" << _step++ << " - transition taken for event '" << _currEvent.name << "' "; + } else { + commentSS << " Step #" << _step++ << " - spontaneous transition taken "; + } + if (envVarIsTrue("USCXML_ANNOTATE_PROGRESS")) + transition.getParentNode().insertBefore(_document.createComment(commentSS.str()), transition); + StateTransitionMonitor::beforeTakingTransition(interpreter, transition, moreComing); } void ChartToMinimalSCXML::beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { markAsVisited(state); + + std::stringstream commentSS; + commentSS << " Step #" << _step++ << " - state entered "; + + Arabica::DOM::Element<std::string> ncState = const_cast<Arabica::DOM::Element<std::string>&>(state); + if (envVarIsTrue("USCXML_ANNOTATE_PROGRESS")) + ncState.insertBefore(_document.createComment(commentSS.str()), ncState.getFirstChild()); + StateTransitionMonitor::beforeEnteringState(interpreter, state, moreComing); } @@ -262,4 +278,7 @@ void ChartToMinimalSCXML::cancelInvoke(const Arabica::DOM::Element<std::string>& InterpreterRC::cancelInvoke(element); } +void ChartToMinimalSCXML::onStableConfiguration(uscxml::Interpreter interpreter) { +} + }
\ No newline at end of file diff --git a/src/uscxml/transform/ChartToMinimalSCXML.h b/src/uscxml/transform/ChartToMinimalSCXML.h index 31dfd64..5975cbc 100644 --- a/src/uscxml/transform/ChartToMinimalSCXML.h +++ b/src/uscxml/transform/ChartToMinimalSCXML.h @@ -45,6 +45,7 @@ public: virtual void beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing); virtual void beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); virtual void beforeCompletion(Interpreter interpreter); + virtual void onStableConfiguration(Interpreter interpreter); // gather executable content per microstep void executeContent(const Arabica::DOM::Element<std::string>& content, bool rethrow = false); @@ -66,6 +67,8 @@ protected: bool _retainAsComments; private: + size_t _step; + // we need this to register as an instance at Interpreter::_instances boost::shared_ptr<InterpreterImpl> _selfPtr; diff --git a/src/uscxml/transform/ChartToPromela.cpp b/src/uscxml/transform/ChartToPromela.cpp index 3e920be..b750409 100644 --- a/src/uscxml/transform/ChartToPromela.cpp +++ b/src/uscxml/transform/ChartToPromela.cpp @@ -99,6 +99,23 @@ for (int indentIndex = start; indentIndex < cols; indentIndex++) \ } \ } +#define DUMP_STATS(disregardTime) \ +uint64_t now = tthread::chrono::system_clock::now(); \ +if (now - _lastTimeStamp > 1000 || disregardTime) { \ + std::cerr << "## State : " << _perfStatesTotal << " [" << _perfStatesProcessed << "/sec]" << std::endl; \ + std::cerr << "## Transition: " << _perfTransTotal << " [" << _perfHistoryProcessed << "/sec]" << std::endl; \ + std::cerr << "## History : " << _perfHistoryTotal << " [" << _perfHistoryProcessed << "/sec]" << std::endl; \ + std::cerr << "toPML: "; \ + std::cerr << _perfStatesTotal << ", " << _perfStatesProcessed << ", "; \ + std::cerr << _perfTransTotal << ", " << _perfTransProcessed << ", "; \ + std::cerr << _perfHistoryTotal << ", " << _perfHistoryProcessed; \ + std::cerr << std::endl << std::endl; \ + _perfTransProcessed = 0; \ + _perfHistoryProcessed = 0; \ + _perfStatesProcessed = 0; \ + if (!disregardTime)\ + _lastTimeStamp = now; \ +} namespace uscxml { @@ -140,7 +157,6 @@ void PromelaEventSource::writeDeclarations(std::ostream& stream, int indent) { } void PromelaEventSource::writeBody(std::ostream& stream) { - stream << "proctype " << name << "EventSource() {" << std::endl; stream << " " << name << "EventSourceDone = 0;" << std::endl; @@ -321,9 +337,12 @@ void PromelaCodeAnalyzer::addCode(const std::string& code, ChartToPromela* inter PromelaParserNode* node = astNodes.front(); astNodes.pop_front(); +// node->dump(); + bool hasValue = false; int assignedValue = 0; + switch (node->type) { case PML_STRING: { std::string unquoted = node->value; @@ -344,10 +363,13 @@ void PromelaCodeAnalyzer::addCode(const std::string& code, ChartToPromela* inter // remember strings for later astNodes.push_back(node->operands.back()); } - if (node->operands.front()->type == PML_CMPND) + if (node->operands.front()->type == PML_CMPND) { node = node->operands.front(); - if (node->operands.front()->type != PML_NAME) - break; // this will skip array assignments + } else { + break; + } +// if (node->operands.front()->type != PML_NAME) +// break; // this will skip array assignments case PML_CMPND: { std::string nameOfType; std::list<PromelaParserNode*>::iterator opIter = node->operands.begin(); @@ -356,7 +378,6 @@ void PromelaCodeAnalyzer::addCode(const std::string& code, ChartToPromela* inter assert(false); } - _typeDefs.occurrences.insert(interpreter); PromelaTypedef* td = &_typeDefs; std::string seperator; @@ -364,6 +385,8 @@ void PromelaCodeAnalyzer::addCode(const std::string& code, ChartToPromela* inter switch ((*opIter)->type) { case PML_NAME: td = &td->types[(*opIter)->value]; + td->occurrences.insert(interpreter); + nameOfType += seperator + (*opIter)->value; if (nameOfType.compare("_x") == 0) _usesPlatformVars = true; @@ -374,6 +397,8 @@ void PromelaCodeAnalyzer::addCode(const std::string& code, ChartToPromela* inter PromelaParserNode* name = (*opIter)->operands.front(); PromelaParserNode* subscript = *(++(*opIter)->operands.begin()); td = &td->types[name->value]; + td->occurrences.insert(interpreter); + nameOfType += seperator + name->value; td->name = nameOfType + "_t"; @@ -433,7 +458,8 @@ void PromelaCodeAnalyzer::addCode(const std::string& code, ChartToPromela* inter // assert(false); } - astNodes.merge(node->operands); + astNodes.insert(astNodes.end(), node->operands.begin(), node->operands.end()); + } } @@ -659,7 +685,7 @@ std::list<std::pair<size_t, size_t> > PromelaCodeAnalyzer::getTokenPositions(con } for (std::list<PromelaParserNode*>::iterator opIter = ast->operands.begin(); opIter != ast->operands.end(); opIter++) { std::list<std::pair<size_t, size_t> > tmp = getTokenPositions(expr, type, *opIter); - posList.merge(tmp); + posList.insert(posList.end(), tmp.begin(), tmp.end()); } return posList; } @@ -940,9 +966,12 @@ std::string ChartToPromela::conditionalizeForHist(const std::set<GlobalTransitio memberSep = " || "; } - if (condition.str().size() > 0) + if (condition.str().size() > 0) { return "(" + condition.str() + ")"; - return ""; + } else { + assert(false); + } + return "true"; } //std::list<GlobalTransition::Action> ChartToPromela::getTransientContent(GlobalTransition* transition) { @@ -966,44 +995,76 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra for (int i = 0; i < indent; i++) { padding += " "; } - + std::list<GlobalTransition*>::const_iterator histIter; + stream << std::endl << _prefix << "t" << transition->index << ": /* ######################## " << std::endl; FlatStateIdentifier flatActiveSource(transition->source); stream << " from state: "; PRETTY_PRINT_LIST(stream, flatActiveSource.getActive()); stream << std::endl; - stream << " with history: " << flatActiveSource.getFlatHistory() << std::endl; +// stream << " with history: " << flatActiveSource.getFlatHistory() << std::endl; stream << " ----- on event: " << (transition->eventDesc.size() > 0 ? transition->eventDesc : "SPONTANEOUS") << " --" << std::endl; stream << " to state: "; - FlatStateIdentifier flatActiveDest(transition->destination); - PRETTY_PRINT_LIST(stream, flatActiveDest.getActive()); + std::set<FlatStateIdentifier> destinations; + destinations.insert(FlatStateIdentifier(transition->destination)); + histIter = transition->historyTrans.begin(); + while(histIter != transition->historyTrans.end()) { + destinations.insert(FlatStateIdentifier((*histIter)->destination)); + histIter++; + } + + std::string seperator = ""; + for (std::set<FlatStateIdentifier>::iterator destIter = destinations.begin(); destIter != destinations.end(); destIter++) { + stream << seperator; + PRETTY_PRINT_LIST(stream, destIter->getActive()); + stream << " with " << (destIter->getFlatHistory().size() > 0 ? destIter->getFlatHistory() : "no history"); + seperator = "\n "; + } stream << std::endl; - stream << " with history: " << flatActiveDest.getFlatHistory() << std::endl; stream << "############################### */" << std::endl; stream << std::endl; stream << padding << "skip;" << std::endl; stream << padding << "d_step {" << std::endl; - stream << padding << " printf(\"Taking Transition " << _prefix << "t" << transition->index << "\\n\");" << std::endl; + if (_writeTransitionPrintfs) + stream << padding << " printf(\"Taking Transition " << _prefix << "t" << transition->index << "\\n\");" << std::endl; + padding += " "; indent++; // iterators of history transitions executable content std::map<GlobalTransition*, std::pair<GlobalTransition::Action::iter_t, GlobalTransition::Action::iter_t> > actionIters; std::map<GlobalTransition*, std::set<GlobalTransition::Action> > actionsInTransition; - + typedef std::map<GlobalTransition*, std::pair<GlobalTransition::Action::iter_t, GlobalTransition::Action::iter_t> > actionIters_t; - std::list<GlobalTransition*>::const_iterator histIter = transition->historyTrans.begin(); + histIter = transition->historyTrans.begin(); while(histIter != transition->historyTrans.end()) { actionIters.insert(std::make_pair((*histIter), std::make_pair((*histIter)->actions.begin(), (*histIter)->actions.end()))); // add history transitions actions to the set - std::copy((*histIter)->actions.begin(), (*histIter)->actions.end(), std::inserter(actionsInTransition[*histIter], actionsInTransition[*histIter].begin())); + for (std::list<GlobalTransition::Action>::iterator actionIter = (*histIter)->actions.begin(); actionIter != (*histIter)->actions.end(); actionIter++) { + actionsInTransition[*histIter].insert(*actionIter); + } +// std::copy((*histIter)->actions.begin(), (*histIter)->actions.end(), std::inserter(actionsInTransition[*histIter], actionsInTransition[*histIter].begin())); histIter++; } - std::copy(transition->actions.begin(), transition->actions.end(), std::inserter(actionsInTransition[transition], actionsInTransition[transition].begin())); - +// std::cout << "###" << std::endl; + for (std::list<GlobalTransition::Action>::iterator actionIter = transition->actions.begin(); actionIter != transition->actions.end(); actionIter++) { +#if 0 + if (actionIter->onEntry) { + std::cout << "onEntry: " << actionIter->onEntry << std::endl; + } else if (actionIter->raiseDone) { + std::cout << "raiseDone: " << actionIter->raiseDone << std::endl; + } else { + std::cout << "#" << std::endl; + } + foo.insert(*actionIter); +#endif + actionsInTransition[transition].insert(*actionIter); + } +// std::copy(transition->actions.begin(), transition->actions.end(), std::inserter(actionsInTransition[transition], actionsInTransition[transition].begin())); + // GlobalTransition::Action action; std::set<GlobalTransition*> allBut; @@ -1017,10 +1078,17 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra for (actionIters_t::iterator histActionIter = actionIters.begin(); histActionIter != actionIters.end(); histActionIter++) { // iterate every history transition GlobalTransition* histTrans = histActionIter->first; + if (histActionIter->second.first == histActionIter->second.second) // TODO: is this correct? + continue; GlobalTransition::Action& histAction = *(histActionIter->second.first); - // is the current action identical? - if (baseAction != histAction) { + // is the current action identical or a generated raise for done.state.ID? +// std::cerr << baseAction << std::endl; +// std::cerr << histAction << std::endl; + if (baseAction != histAction && !baseAction.raiseDone) { +// std::cout << baseAction << std::endl; +// std::cout << histAction << std::endl; + // executable content differs - will given executable content appear later in history? if (actionsInTransition[histTrans].find(baseAction) != actionsInTransition[histTrans].end()) { // yes -> write all exec content exclusive to this history transition until base executable content @@ -1062,7 +1130,6 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra bool isConditionalized = false; bool wroteHistoryAssignments = false; - std::set<GlobalTransition*> condSet; for (std::list<ExecContentSeqItem>::const_iterator ecIter = ecSeq.begin(); ecIter != ecSeq.end(); ecIter++) { const GlobalTransition::Action& action = ecIter->action; @@ -1079,32 +1146,38 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra continue; } - if (ecIter->type == ExecContentSeqItem::EXEC_CONTENT_ONLY_FOR) { + if (!isConditionalized && ecIter->type == ExecContentSeqItem::EXEC_CONTENT_ONLY_FOR) { // assert(!wroteHistoryAssignments); // we need to move assignments after dispatching? - if (condSet != ecIter->transitions) { - stream << padding << "if" << std::endl; - stream << padding << ":: " << conditionalizeForHist(ecIter->transitions) << " -> {" << std::endl; - padding += " "; - indent++; - isConditionalized = true; - condSet = ecIter->transitions; - } - } else if (ecIter->type == ExecContentSeqItem::EXEC_CONTENT_ALL_BUT) { + stream << padding << "if" << std::endl; + stream << padding << ":: " << conditionalizeForHist(ecIter->transitions) << " -> {" << std::endl; + padding += " "; + indent++; + isConditionalized = true; + } else if (!isConditionalized && ecIter->type == ExecContentSeqItem::EXEC_CONTENT_ALL_BUT) { // assert(!wroteHistoryAssignments); // we need to move assignments after dispatching? - if (condSet != ecIter->transitions) { - stream << padding << "if" << std::endl; - stream << padding << ":: " << conditionalizeForHist(ecIter->transitions) << " -> skip;" << std::endl; - stream << padding << ":: else -> {" << std::endl; - padding += " "; - indent++; - isConditionalized = true; - condSet = ecIter->transitions; - } - } else { - isConditionalized = false; - condSet.clear(); + stream << padding << "if" << std::endl; + stream << padding << ":: " << conditionalizeForHist(ecIter->transitions) << " -> skip;" << std::endl; + stream << padding << ":: else -> {" << std::endl; + padding += " "; + indent++; + isConditionalized = true; } + switch (ecIter->type) { + case ExecContentSeqItem::EXEC_CONTENT_ALL_BUT: + std::cout << "ALL_BUT" << std::endl; + break; + case ExecContentSeqItem::EXEC_CONTENT_EVERY: + std::cout << "EVERY" << std::endl; + break; + case ExecContentSeqItem::EXEC_CONTENT_ONLY_FOR: + std::cout << "ONLY_FOR" << std::endl; + break; + + default: + break; + } + if (action.exited) { // we left a state stream << padding << _prefix << "_x.states[" << _analyzer->macroForLiteral(ATTR(action.exited, "id")) << "] = false; " << std::endl; @@ -1119,7 +1192,7 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra if (action.transition) { // this is executable content from a transition - stream << std::endl << "/* executable content for transition */" << std::endl; + stream << "/* executable content for transition */" << std::endl; writeExecutableContent(stream, action.transition, indent); // continue; } @@ -1128,7 +1201,7 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra // std::cout<< action.onExit << std::endl; // executable content from an onexit element if (action.onExit.getParentNode()) // this should not be necessary? - stream << std::endl << "/* executable content for exiting state " << ATTR_CAST(action.onExit.getParentNode(), "id") << " */" << std::endl; + stream << "/* executable content for exiting state " << ATTR_CAST(action.onExit.getParentNode(), "id") << " */" << std::endl; writeExecutableContent(stream, action.onExit, indent); // continue; } @@ -1136,11 +1209,19 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra if (action.onEntry) { // executable content from an onentry element if (action.onEntry.getParentNode()) // this should not be necessary? - stream << std::endl << "/* executable content for entering state " << ATTR_CAST(action.onEntry.getParentNode(), "id") << " */" << std::endl; + stream << "/* executable content for entering state " << ATTR_CAST(action.onEntry.getParentNode(), "id") << " */" << std::endl; writeExecutableContent(stream, action.onEntry, indent); // continue; } - + + if (action.raiseDone) { + // executable content from an onentry element + if (action.raiseDone.getParentNode()) // this should not be necessary? + stream << "/* raising done event for " << ATTR_CAST(action.raiseDone.getParentNode(), "id") << " */" << std::endl; + writeExecutableContent(stream, action.raiseDone, indent); + // continue; + } + if (action.invoke) { // an invoke element @@ -1228,17 +1309,28 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra } if (isConditionalized) { - padding = padding.substr(2); - indent--; - if (ecIter->type == ExecContentSeqItem::EXEC_CONTENT_ALL_BUT) { - stream << padding << "}" << std::endl; - stream << padding << "fi" << std::endl; - } else if(ecIter->type == ExecContentSeqItem::EXEC_CONTENT_ONLY_FOR) { - stream << padding << "}" << std::endl; - stream << padding << ":: else -> skip;" << std::endl; - stream << padding << "fi;" << std::endl; + // peek into next content and see if same conditions apply -> keep conditionalization + bool sameCondition = false; + std::list<ExecContentSeqItem>::const_iterator nextIter = ecIter; + nextIter++; + if (nextIter != ecSeq.end() && ecIter->type == nextIter->type && ecIter->transitions == nextIter->transitions) { + sameCondition = true; + } + + if (!sameCondition) { + padding = padding.substr(2); + indent--; + + if (ecIter->type == ExecContentSeqItem::EXEC_CONTENT_ALL_BUT) { + stream << padding << "}" << std::endl; + stream << padding << "fi" << std::endl << std::endl; + } else if(ecIter->type == ExecContentSeqItem::EXEC_CONTENT_ONLY_FOR) { + stream << padding << "}" << std::endl; + stream << padding << ":: else -> skip;" << std::endl; + stream << padding << "fi;" << std::endl << std::endl; + } + isConditionalized = false; } - isConditionalized = false; } } @@ -1268,39 +1360,40 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra if (histNewState == origNewState) continue; stream << padding << "if" << std::endl; - stream << padding << "::" << conditionalizeForHist(histTargetIter->second) << " -> {" << std::endl; - stream << std::endl << "/* via hist "; + + stream << "/* to state "; FlatStateIdentifier flatActiveDest(histNewState->activeId); PRETTY_PRINT_LIST(stream, flatActiveDest.getActive()); - stream << "*/" << std::endl; + stream << " via history */" << std::endl; - stream << padding << " " << _prefix << "s = s" << histNewState->activeIndex << ";" << std::endl; - + stream << padding << ":: " << conditionalizeForHist(histTargetIter->second) << " -> " << _prefix << "s = s" << histNewState->activeIndex << ";" << std::endl; // writeTransitionClosure(stream, *histTargetIter->second.begin(), histNewState, indent + 1); // is this correct for everyone in set? - stream << padding << "}" << std::endl; + hasHistoryTarget = true; } - if (hasHistoryTarget) { - stream << padding << ":: else {" << std::endl; - padding += " "; - indent++; - } - origNewState = _activeConf[transition->activeDestination]; + FlatStateIdentifier flatActiveDest(transition->activeDestination); assert(origNewState != NULL); - - stream << std::endl << "/* to state "; + + stream << "/* to state "; PRETTY_PRINT_LIST(stream, flatActiveDest.getActive()); stream << " */" << std::endl; + + if (hasHistoryTarget) { + stream << padding << ":: else -> "; + padding += " "; + indent++; + } stream << padding << _prefix << "s = s" << origNewState->activeIndex << ";" << std::endl; + if (hasHistoryTarget) { padding = padding.substr(2); indent--; - stream << padding << "}" << std::endl; +// stream << padding << "}" << std::endl; stream << padding << "fi;" << std::endl; } @@ -1310,6 +1403,11 @@ void ChartToPromela::writeTransition(std::ostream& stream, GlobalTransition* tra writeTransitionClosure(stream, transition, origNewState, indent-1); + _perfTransProcessed++; + _perfTransTotal++; + + DUMP_STATS(false); + } void ChartToPromela::writeHistoryAssignments(std::ostream& stream, GlobalTransition* transition, int indent) { @@ -1318,6 +1416,9 @@ void ChartToPromela::writeHistoryAssignments(std::ostream& stream, GlobalTransit padding += " "; } + if (transition->historyTrans.size() == 0) + return; + // GlobalState to *changed* history configuration std::list<HistoryTransitionClass> histClasses; @@ -1354,11 +1455,15 @@ void ChartToPromela::writeHistoryAssignments(std::ostream& stream, GlobalTransit if (outerClass.matches(innerClass)) { outerClass.merge(innerClass); - histClasses.erase(innerHistClassIter); + histClasses.erase(innerHistClassIter++); + } else { + innerHistClassIter++; } - - innerHistClassIter++; } + + _perfHistoryProcessed++; + _perfHistoryTotal++; + outerHistClassIter++; } // std::cout << histClasses.size() << std::endl; @@ -1441,6 +1546,9 @@ HistoryTransitionClass::HistoryTransitionClass(const std::string& from, const st } void HistoryTransitionClass::init(const std::string& from, const std::string& to) { + if (from == to) + return; + FlatStateIdentifier flatSource(from); FlatStateIdentifier flatTarget(to); @@ -1550,6 +1658,12 @@ void ChartToPromela::writeTransitionClosure(std::ostream& stream, GlobalTransiti padding += " "; } + if (_traceTransitions) { + for (std::set<int>::iterator transRefIter = transition->transitionRefs.begin(); transRefIter != transition->transitionRefs.end(); transRefIter++) { + stream << padding << _prefix << "transitions[" << *transRefIter << "] = false; " << std::endl; + } + } + if (state->isFinal) { stream << padding << "goto " << _prefix << "terminate;" << std::endl; } else { @@ -1770,9 +1884,9 @@ void ChartToPromela::writeExecutableContent(std::ostream& stream, const Arabica: } } else if(TAGNAME(nodeElem) == "cancel") { if (HAS_ATTR(nodeElem, "sendid")) { - stream << padding << "cancelSendId(" << _analyzer->macroForLiteral(ATTR(nodeElem, "sendid")) << "," << (_invokerid.size() > 0 ? _analyzer->macroForLiteral(_invokerid) : "0") << ");" << std::endl; + stream << padding << "cancelSendId(" << _analyzer->macroForLiteral(ATTR(nodeElem, "sendid")) << ", " << (_invokerid.size() > 0 ? _analyzer->macroForLiteral(_invokerid) : "0") << ");" << std::endl; } else if (HAS_ATTR(nodeElem, "sendidexpr")) { - stream << padding << "cancelSendId(" << ADAPT_SRC(ATTR(nodeElem, "sendidexpr")) << "," << (_invokerid.size() > 0 ? _analyzer->macroForLiteral(_invokerid) : "0") << ");" << std::endl; + stream << padding << "cancelSendId(" << ADAPT_SRC(ATTR(nodeElem, "sendidexpr")) << ", " << (_invokerid.size() > 0 ? _analyzer->macroForLiteral(_invokerid) : "0") << ");" << std::endl; } } else { std::cerr << "'" << TAGNAME(nodeElem) << "'" << std::endl << nodeElem << std::endl; @@ -2009,12 +2123,17 @@ void ChartToPromela::writeDeclarations(std::ostream& stream) { stream << "chan " << _prefix << "start = [" << _machines.size() << "] of {int} /* nested machines to start at next macrostep */" << std::endl; } - stream << "hidden int " << _prefix << "_index; /* helper for indexless foreach loops */" << std::endl; + if (_hasIndexLessLoops) + stream << "hidden int " << _prefix << "_index; /* helper for indexless foreach loops */" << std::endl; + stream << "hidden int " << _prefix << "procid; /* the process id running this machine */" << std::endl; stream << "bool " << _prefix << "spontaneous; /* whether to take spontaneous transitions */" << std::endl; stream << "bool " << _prefix << "done; /* is the state machine stopped? */" << std::endl; stream << "bool " << _prefix << "canceled; /* is the state machine canceled? */" << std::endl; + if (_traceTransitions) + stream << "bool " << _prefix << "transitions[" << indexedTransitions.size() << "]; /* transitions in the optimal transition set */" << std::endl; + if (_analyzer->getTypes().types.find("_ioprocessors") != _analyzer->getTypes().types.end()) { stream << "hidden _ioprocessors_t " << _prefix << "_ioprocessors;" << std::endl; _varInitializers.push_front("_ioprocessors.scxml.location = " + (_invokerid.size() > 0 ? _analyzer->macroForLiteral(_invokerid) : "1") + ";"); @@ -2137,7 +2256,12 @@ void ChartToPromela::writeDeclarations(std::ostream& stream) { typeIter++; continue; } - if (typeIter->first == "_event" || typeIter->first == "_ioprocessors" || typeIter->first == "_SESSIONID" || typeIter->first == "_NAME") { + + if (typeIter->first == "_event" || + typeIter->first == "_x" || + typeIter->first == "_ioprocessors" || + typeIter->first == "_SESSIONID" || + typeIter->first == "_NAME") { typeIter++; continue; } @@ -2245,6 +2369,12 @@ void ChartToPromela::writeFSM(std::ostream& stream) { // every other transition for (std::map<std::string, GlobalState*>::iterator stateIter = _activeConf.begin(); stateIter != _activeConf.end(); stateIter++) { for (std::list<GlobalTransition*>::iterator transIter = stateIter->second->sortedOutgoing.begin(); transIter != stateIter->second->sortedOutgoing.end(); transIter++) { + // don't write invalid transition + if (!(*transIter)->isValid) { + LOG(ERROR) << "Sorted outgoing transitions contains invalid transitions - did you instruct ChartToFSM to keep those?"; + abort(); + } + // don't write initial transition if (_start->sortedOutgoing.front() == *transIter) continue; @@ -2253,12 +2383,17 @@ void ChartToPromela::writeFSM(std::ostream& stream) { // if ((*transIter)->hasExecutableContent && (*transIter)->historyBase == NULL) writeTransition(stream, *transIter, 1); } + _perfStatesProcessed++; + _perfStatesTotal++; + + DUMP_STATS(false); } + DUMP_STATS(true); stream << std::endl; stream << _prefix << "macroStep: skip;" << std::endl; if (_allowEventInterleaving) { - stream << " /* push send events to external queue */" << std::endl; + stream << " /* push send events to external queue - this needs to be interleavable! */" << std::endl; stream << " do" << std::endl; if (_analyzer->usesEventField("delay")) { stream << " :: len(" << _prefix << "tmpQ) != 0 -> { " << _prefix << "tmpQ?" << _prefix << "_event; " << _prefix << "eQ!!" << _prefix << "_event }" << std::endl; @@ -2292,11 +2427,12 @@ void ChartToPromela::writeFSM(std::ostream& stream) { stream << " scheduleMachines();" << std::endl << std::endl; } - stream << " /* pop an event */" << std::endl; - stream << " if" << std::endl; - stream << " :: len(" << _prefix << "iQ) != 0 -> " << _prefix << "iQ ? " << _prefix << "_event /* from internal queue */" << std::endl; - stream << " :: else -> " << _prefix << "eQ ? " << _prefix << "_event /* from external queue */" << std::endl; - stream << " fi;" << std::endl << std::endl; + stream << " atomic {" << std::endl; + stream << " /* pop an event */" << std::endl; + stream << " if" << std::endl; + stream << " :: len(" << _prefix << "iQ) != 0 -> " << _prefix << "iQ ? " << _prefix << "_event /* from internal queue */" << std::endl; + stream << " :: else -> " << _prefix << "eQ ? " << _prefix << "_event /* from external queue */" << std::endl; + stream << " fi;" << std::endl << std::endl; #if 0 if (!_analyzer->usesComplexEventStruct()) { @@ -2312,16 +2448,16 @@ void ChartToPromela::writeFSM(std::ostream& stream) { } stream << std::endl; #endif - stream << " /* terminate if we are stopped */" << std::endl; - stream << " if" << std::endl; - stream << " :: " << _prefix << "done -> goto " << _prefix << "terminate;" << std::endl; + + stream << " /* terminate if we are stopped */" << std::endl; + stream << " if" << std::endl; + stream << " :: " << _prefix << "done -> goto " << _prefix << "terminate;" << std::endl; if (_parent != NULL) { - stream << " :: " << _prefix << "canceled -> goto " << _prefix << "cancel;" << std::endl; + stream << " :: " << _prefix << "canceled -> goto " << _prefix << "cancel;" << std::endl; } - stream << " :: else -> skip;" << std::endl; - stream << " fi;" << std::endl << std::endl; + stream << " :: else -> skip;" << std::endl; + stream << " fi;" << std::endl << std::endl; -#if 1 { bool finalizeFound = false; for (std::map<Arabica::DOM::Node<std::string>, ChartToPromela*>::iterator invIter = _machines.begin(); invIter != _machines.end(); invIter++) { @@ -2332,82 +2468,81 @@ void ChartToPromela::writeFSM(std::ostream& stream) { } } if (finalizeFound) { - stream << " /* <finalize> event */" << std::endl; - stream << " if" << std::endl; + stream << "/* <finalize> event */" << std::endl; + stream << " if" << std::endl; for (std::map<Arabica::DOM::Node<std::string>, ChartToPromela*>::iterator invIter = _machines.begin(); invIter != _machines.end(); invIter++) { NodeSet<std::string> finalizes = filterChildElements(_nsInfo.xmlNSPrefix + "finalize", invIter->first, false); if (finalizes.size() > 0) { - stream << " :: " << _prefix << "_event.invokeid == " << _analyzer->macroForLiteral(invIter->second->_invokerid) << " -> {" << std::endl; - writeExecutableContent(stream, finalizes[0], 2); - stream << " } " << std::endl; + stream << " :: " << _prefix << "_event.invokeid == " << _analyzer->macroForLiteral(invIter->second->_invokerid) << " -> {" << std::endl; + writeExecutableContent(stream, finalizes[0], 3); + stream << " } " << std::endl; } } - stream << " :: else -> skip;" << std::endl; - stream << " fi;" << std::endl << std::endl; + stream << " :: else -> skip;" << std::endl; + stream << " fi;" << std::endl << std::endl; } } -#endif for (std::map<Arabica::DOM::Node<std::string>, ChartToPromela*>::iterator invIter = _machines.begin(); invIter != _machines.end(); invIter++) { if (invIter->second == this) { continue; } //std::cout << invIter->first << std::endl; - if (DOMUtils::attributeIsTrue(ATTR_CAST(invIter->first, "autoforward"))) { - stream << " /* autoforward event to " << invIter->second->_invokerid << " invokers */" << std::endl; - stream << " if" << std::endl; - stream << " :: " << invIter->second->_prefix << "done -> skip;" << std::endl; - stream << " :: " << invIter->second->_prefix << "canceled -> skip;" << std::endl; - stream << " :: else -> { " << invIter->second->_prefix << "eQ!!" << _prefix << "_event" << " }" << std::endl; - stream << " fi;" << std::endl << std::endl; + if (stringIsTrue(ATTR_CAST(invIter->first, "autoforward"))) { + stream << "/* autoforward event to " << invIter->second->_invokerid << " invokers */" << std::endl; + stream << " if" << std::endl; + stream << " :: " << invIter->second->_prefix << "done -> skip;" << std::endl; + stream << " :: " << invIter->second->_prefix << "canceled -> skip;" << std::endl; + stream << " :: else -> { " << invIter->second->_prefix << "eQ!!" << _prefix << "_event" << " }" << std::endl; + stream << " fi;" << std::endl << std::endl; } } stream << std::endl; - stream << _prefix << "microStep:" << std::endl; - stream << " /* event dispatching per state */" << std::endl; - stream << " if" << std::endl; + stream << "/* event dispatching per state */" << std::endl; + stream << " if" << std::endl; writeEventDispatching(stream); stream << "/* this is an error as we dispatched all valid states */" << std::endl; - stream << " :: else -> assert(false);" << std::endl; - stream << " fi;" << std::endl; + stream << " :: else -> assert(false);" << std::endl; + stream << " fi;" << std::endl; stream << std::endl; stream << _prefix << "terminate: skip;" << std::endl; if (_parent != NULL) { - stream << " {" << std::endl; - stream << " _event_t tmpE;" << std::endl; - stream << " tmpE.name = " << _analyzer->macroForLiteral("done.invoke." + _invokerid) << ";" << std::endl; + stream << " {" << std::endl; + stream << " _event_t tmpE;" << std::endl; + stream << " tmpE.name = " << _analyzer->macroForLiteral("done.invoke." + _invokerid) << ";" << std::endl; if (_invokerid.length() > 0) { - stream << " tmpE.invokeid = " << _analyzer->macroForLiteral(_invokerid) << ";" << std::endl; + stream << " tmpE.invokeid = " << _analyzer->macroForLiteral(_invokerid) << ";" << std::endl; } if (_analyzer->usesEventField("delay")) { - stream << " _lastSeqId = _lastSeqId + 1;" << std::endl; - stream << " tmpE.seqNr = _lastSeqId;" << std::endl; - stream << " " << _parent->_prefix << "eQ!!tmpE;" << std::endl; + stream << " _lastSeqId = _lastSeqId + 1;" << std::endl; + stream << " tmpE.seqNr = _lastSeqId;" << std::endl; + stream << " " << _parent->_prefix << "eQ!!tmpE;" << std::endl; } else { - stream << " " << _parent->_prefix << "eQ!tmpE;" << std::endl; + stream << " " << _parent->_prefix << "eQ!tmpE;" << std::endl; } - stream << " }" << std::endl; + stream << " }" << std::endl; stream << _prefix << "cancel: skip;" << std::endl; if (_analyzer->usesEventField("delay")) - stream << " removePendingEventsForInvoker(" << _analyzer->macroForLiteral(this->_invokerid) << ")" << std::endl; + stream << " removePendingEventsForInvoker(" << _analyzer->macroForLiteral(this->_invokerid) << ")" << std::endl; } // stop all event sources if (_globalEventSource) - _globalEventSource.writeStop(stream, 1); + _globalEventSource.writeStop(stream, 2); std::map<std::string, PromelaEventSource>::iterator invIter = _invokers.begin(); while(invIter != _invokers.end()) { - invIter->second.writeStop(stream, 1); + invIter->second.writeStop(stream, 2); invIter++; } + stream << " }" << std::endl; stream << "}" << std::endl; } @@ -2422,7 +2557,7 @@ void ChartToPromela::writeRescheduleProcess(std::ostream& stream, int indent) { } else { stream << padding << "inline rescheduleProcess(smallestDelay, procId, internalQ, externalQ) {" << std::endl; } - stream << padding << " _event_t tmpEvent;" << std::endl; + stream << padding << " _event_t tmpE;" << std::endl; stream << padding << " set_priority(procId, 1);" << std::endl; stream << padding << " if" << std::endl; @@ -2431,18 +2566,18 @@ void ChartToPromela::writeRescheduleProcess(std::ostream& stream, int indent) { stream << padding << " if" << std::endl; stream << padding << " :: len(externalQ) > 0 -> {" << std::endl; - stream << padding << " externalQ?<tmpEvent>;" << std::endl; + stream << padding << " externalQ?<tmpE>;" << std::endl; stream << padding << " if" << std::endl; - stream << padding << " :: smallestDelay == tmpEvent.delay -> set_priority(procId, 10);" << std::endl; + stream << padding << " :: smallestDelay == tmpE.delay -> set_priority(procId, 10);" << std::endl; stream << padding << " :: else -> skip;" << std::endl; stream << padding << " fi;" << std::endl; stream << padding << " }" << std::endl; if (_allowEventInterleaving) { stream << padding << " :: len(tempQ) > 0 -> {" << std::endl; - stream << padding << " tempQ?<tmpEvent>;" << std::endl; + stream << padding << " tempQ?<tmpE>;" << std::endl; stream << padding << " if" << std::endl; - stream << padding << " :: smallestDelay == tmpEvent.delay -> set_priority(procId, 10);" << std::endl; + stream << padding << " :: smallestDelay == tmpE.delay -> set_priority(procId, 10);" << std::endl; stream << padding << " :: else -> skip;" << std::endl; stream << padding << " fi;" << std::endl; stream << padding << " }" << std::endl; @@ -2462,12 +2597,12 @@ void ChartToPromela::writeDetermineShortestDelay(std::ostream& stream, int inden } stream << padding << "inline determineSmallestDelay(smallestDelay, queue) {" << std::endl; - stream << padding << " _event_t tmpEvent;" << std::endl; + stream << padding << " _event_t tmpE;" << std::endl; stream << padding << " if" << std::endl; stream << padding << " :: len(queue) > 0 -> {" << std::endl; - stream << padding << " queue?<tmpEvent>;" << std::endl; + stream << padding << " queue?<tmpE>;" << std::endl; stream << padding << " if" << std::endl; - stream << padding << " :: (tmpEvent.delay < smallestDelay) -> { smallestDelay = tmpEvent.delay; }" << std::endl; + stream << padding << " :: (tmpE.delay < smallestDelay) -> { smallestDelay = tmpE.delay; }" << std::endl; stream << padding << " :: else -> skip;" << std::endl; stream << padding << " fi;" << std::endl; stream << padding << " }" << std::endl; @@ -2484,15 +2619,15 @@ void ChartToPromela::writeAdvanceTime(std::ostream& stream, int indent) { stream << padding << "inline advanceTime(increment, queue) {" << std::endl; stream << padding << " int tmpIndex = 0;" << std::endl; - stream << padding << " _event_t tmpEvent;" << std::endl; + stream << padding << " _event_t tmpE;" << std::endl; stream << padding << " do" << std::endl; stream << padding << " :: tmpIndex < len(queue) -> {" << std::endl; - stream << padding << " queue?tmpEvent;" << std::endl; + stream << padding << " queue?tmpE;" << std::endl; stream << padding << " if" << std::endl; - stream << padding << " :: tmpEvent.delay >= increment -> tmpEvent.delay = tmpEvent.delay - increment;" << std::endl; + stream << padding << " :: tmpE.delay >= increment -> tmpE.delay = tmpE.delay - increment;" << std::endl; stream << padding << " :: else -> skip;" << std::endl; stream << padding << " fi" << std::endl; - stream << padding << " queue!tmpEvent;" << std::endl; + stream << padding << " queue!tmpE;" << std::endl; stream << padding << " tmpIndex++;" << std::endl; stream << padding << " }" << std::endl; stream << padding << " :: else -> break;" << std::endl; @@ -2517,12 +2652,12 @@ void ChartToPromela::writeRemovePendingEventsFromInvoker(std::ostream& stream, i stream << "inline removePendingEventsForInvokerOnQueue(invokeIdentifier, queue) {" << std::endl; stream << " int tmpIndex = 0;" << std::endl; - stream << " _event_t tmpEvent;" << std::endl; + stream << " _event_t tmpE;" << std::endl; stream << " do" << std::endl; stream << " :: tmpIndex < len(queue) -> {" << std::endl; - stream << " queue?tmpEvent;" << std::endl; + stream << " queue?tmpE;" << std::endl; stream << " if" << std::endl; - stream << " :: tmpEvent.delay == 0 || tmpEvent.invokeid != invokeIdentifier -> queue!tmpEvent;" << std::endl; + stream << " :: tmpE.delay == 0 || tmpE.invokeid != invokeIdentifier -> queue!tmpE;" << std::endl; stream << " :: else -> skip;" << std::endl; stream << " fi" << std::endl; stream << " tmpIndex++;" << std::endl; @@ -2550,12 +2685,12 @@ void ChartToPromela::writeCancelEvents(std::ostream& stream, int indent) { stream << "inline cancelSendIdOnQueue(sendIdentifier, queue, invokerIdentifier) {" << std::endl; stream << " int tmpIndex = 0;" << std::endl; - stream << " _event_t tmpEvent;" << std::endl; + stream << " _event_t tmpE;" << std::endl; stream << " do" << std::endl; stream << " :: tmpIndex < len(queue) -> {" << std::endl; - stream << " queue?tmpEvent;" << std::endl; + stream << " queue?tmpE;" << std::endl; stream << " if" << std::endl; - stream << " :: tmpEvent.invokeid != invokerIdentifier || tmpEvent.sendid != sendIdentifier || tmpEvent.delay == 0 -> queue!tmpEvent;" << std::endl; + stream << " :: tmpE.invokeid != invokerIdentifier || tmpE.sendid != sendIdentifier || tmpE.delay == 0 -> queue!tmpE;" << std::endl; stream << " :: else -> skip;" << std::endl; stream << " fi" << std::endl; stream << " tmpIndex++;" << std::endl; @@ -2624,12 +2759,6 @@ void ChartToPromela::writeScheduleMachines(std::ostream& stream, int indent) { void ChartToPromela::writeEventDispatching(std::ostream& stream) { for (std::map<std::string, GlobalState*>::iterator stateIter = _activeConf.begin(); stateIter != _activeConf.end(); stateIter++) { -// if (_globalStates[i] == _startState) -// continue; - - // do not write state with history - we only iterate pure active -// if (stateIter->second->historyStatesRefs.size() > 0) -// continue; const std::string& stateId = stateIter->first; const GlobalState* state = stateIter->second; @@ -2639,11 +2768,10 @@ void ChartToPromela::writeEventDispatching(std::ostream& stream) { PRETTY_PRINT_LIST(stream, flatActiveSource.getActive()); stream << " ######################## */" << std::endl; - stream << " :: (" << _prefix << "s == s" << state->activeIndex << ") -> {" << std::endl; + stream << " :: (" << _prefix << "s == s" << state->activeIndex << ") -> {" << std::endl; - writeDispatchingBlock(stream, state->sortedOutgoing, 2); -// stream << " goto macroStep;"; - stream << " }" << std::endl; + writeDispatchingBlock(stream, state->sortedOutgoing, 3); + stream << " }" << std::endl; } } @@ -2728,11 +2856,16 @@ void ChartToPromela::writeDispatchingBlock(std::ostream& stream, std::list<Globa } if (currTrans->hasExecutableContent || currTrans->historyTrans.size() > 0) { stream << " -> { " << std::endl; - stream << "/* transition to "; FlatStateIdentifier flatActiveSource(currTrans->activeDestination); PRETTY_PRINT_LIST(stream, flatActiveSource.getActive()); stream << " */" << std::endl; + + if (_traceTransitions) { + for (std::set<int>::iterator transRefIter = currTrans->transitionRefs.begin(); transRefIter != currTrans->transitionRefs.end(); transRefIter++) { + stream << padding << " " << _prefix << "transitions[" << *transRefIter << "] = true; " << std::endl; + } + } stream << padding << " goto " << _prefix << "t" << currTrans->index << ";" << std::endl; stream << padding << "}" << std::endl; @@ -2931,6 +3064,9 @@ void ChartToPromela::initNodes() { } } +// _analyzer->addCode("bumpDownArrow = 1; _event.foo = 3; forgetSelectedServer = 1;", this); +// exit(0); + // transform data / assign json into PROMELA statements { NodeSet<std::string> asgn; @@ -3166,6 +3302,8 @@ void ChartToPromela::initNodes() { for (int i = 0; i < foreachs.size(); i++) { if (HAS_ATTR_CAST(foreachs[i], "index")) { allCode.insert(ATTR_CAST(foreachs[i], "index")); + } else { + _hasIndexLessLoops = true; } if (HAS_ATTR_CAST(foreachs[i], "item")) { allCode.insert(ATTR_CAST(foreachs[i], "item")); @@ -3235,6 +3373,9 @@ void PromelaInline::dump() { void ChartToPromela::writeProgram(std::ostream& stream) { + _traceTransitions = envVarIsTrue("USCXML_PROMELA_TRANSITION_TRACE"); + _writeTransitionPrintfs = envVarIsTrue("USCXML_PROMELA_TRANSITION_DEBUG"); + if (!HAS_ATTR(_scxml, "datamodel") || ATTR(_scxml, "datamodel") != "promela") { LOG(ERROR) << "Can only convert SCXML documents with \"promela\" datamodel"; return; @@ -3267,6 +3408,8 @@ void ChartToPromela::writeProgram(std::ostream& stream) { stream << std::endl; writeStates(stream); stream << std::endl; + writeStrings(stream); + stream << std::endl; if (_analyzer->usesInPredicate()) { writeStateMap(stream); stream << std::endl; @@ -3277,8 +3420,6 @@ void ChartToPromela::writeProgram(std::ostream& stream) { } writeTypeDefs(stream); stream << std::endl; - writeStrings(stream); - stream << std::endl; writeDeclarations(stream); stream << std::endl; @@ -3287,7 +3428,7 @@ void ChartToPromela::writeProgram(std::ostream& stream) { stream << std::endl; } - stream << std::endl << "/* Global inline functions */" << std::endl; + stream << std::endl << "/* global inline functions */" << std::endl; if (_analyzer->usesEventField("delay") && _machines.size() > 0) { diff --git a/src/uscxml/transform/ChartToPromela.h b/src/uscxml/transform/ChartToPromela.h index cfe5a78..9a8b0a3 100644 --- a/src/uscxml/transform/ChartToPromela.h +++ b/src/uscxml/transform/ChartToPromela.h @@ -280,7 +280,25 @@ public: void writeTo(std::ostream& stream); protected: - ChartToPromela(const Interpreter& other) : TransformerImpl(), ChartToFSM(other), _analyzer(NULL), _machinesAll(NULL), _parent(NULL), _parentTopMost(NULL), _machinesAllPerId(NULL) {} + ChartToPromela(const Interpreter& other) + : TransformerImpl(), + ChartToFSM(other), + _analyzer(NULL), + _allowEventInterleaving(false), + _hasIndexLessLoops(false), + _writeTransitionPrintfs(false), + _traceTransitions(false), + _machinesAll(NULL), + _parent(NULL), + _parentTopMost(NULL), + _machinesAllPerId(NULL), + _perfTransProcessed(0), + _perfTransTotal(0), + _perfHistoryProcessed(0), + _perfHistoryTotal(0), + _perfStatesProcessed(0), + _perfStatesTotal(0), + _lastTimeStamp(0) {} void initNodes(); @@ -341,6 +359,9 @@ protected: PromelaCodeAnalyzer* _analyzer; bool _allowEventInterleaving; + bool _hasIndexLessLoops; + bool _writeTransitionPrintfs; + bool _traceTransitions; uint32_t _externalQueueLength; uint32_t _internalQueueLength; @@ -362,6 +383,14 @@ protected: std::string _prefix; // our prefix in case of nested SCXML documents std::string _invokerid; + uint64_t _perfTransProcessed; + uint64_t _perfTransTotal; + uint64_t _perfHistoryProcessed; + uint64_t _perfHistoryTotal; + uint64_t _perfStatesProcessed; + uint64_t _perfStatesTotal; + uint64_t _lastTimeStamp; + friend class PromelaEventSource; }; diff --git a/src/uscxml/transform/ChartToTex.cpp b/src/uscxml/transform/ChartToTex.cpp new file mode 100644 index 0000000..35731a1 --- /dev/null +++ b/src/uscxml/transform/ChartToTex.cpp @@ -0,0 +1,284 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "uscxml/transform/ChartToFSM.h" +#include "uscxml/transform/ChartToTex.h" +#include "uscxml/transform/FlatStateIdentifier.h" + +#include <DOM/io/Stream.hpp> +#include <iostream> +#include "uscxml/UUID.h" +#include <math.h> +#include <boost/algorithm/string.hpp> +#include <glog/logging.h> + + +namespace uscxml { + +using namespace Arabica::DOM; +using namespace Arabica::XPath; + +ChartToTex::~ChartToTex() { +} + +Transformer ChartToTex::transform(const Interpreter& other) { + return boost::shared_ptr<TransformerImpl>(new ChartToTex(other)); +} + +void ChartToTex::writeTo(std::ostream& stream) { + writeTex(stream); +} + +void ChartToTex::writeTex(std::ostream& stream) { + _keepInvalidTransitions = true; + if (_start == NULL) { + interpret(); + } + + bool wroteRowStart = false; + std::string seperator; + + for (std::map<std::string, GlobalState*>::iterator stateIter = _globalConf.begin(); stateIter != _globalConf.end(); stateIter++) { + assert(_indexToState.find(stateIter->second->index) == _indexToState.end()); + _indexToState[stateIter->second->index] = stateIter->second; + } + + stream << "% " << _sourceURL.asString() << std::endl; + + stream << "%<*provideCommand>" << std::endl; + stream << "\\providecommand{\\globalStateListCell}[2][c]{%" << std::endl; + stream << " \\begin{tabular}[#1]{@{}l@{}}#2\\end{tabular}}" << std::endl; + stream << "%</provideCommand>" << std::endl; + + + stream << std::endl; + +// stream << "\\begin{table}[H]" << std::endl; +// stream << "\\centering" << std::endl; +// stream << "\\begin{tabular}{r | l | L{12em} | l}" << std::endl; + + stream << "\\begin{longtable}{| r | l | l | l |}" << std::endl; + + for (std::map<unsigned long, GlobalState*>::iterator stateIter = _indexToState.begin(); stateIter != _indexToState.end(); stateIter++) { + GlobalState* currState = stateIter->second; + + stream << "\\hline" << std::endl; + + if (!wroteRowStart) { + stream << "%<*tableRows>" << std::endl; + wroteRowStart = true; + } + + stream << "%<*globalState" << currState->index << ">" << std::endl; + + // state index + stream << "\\tikzmark{statename_" << currState->index << "}" << "$\\widetilde{s}(" << currState->index << ")$ & "; + + // members in active configuration + FlatStateIdentifier flatId(currState->stateId); + stream << "\\globalStateListCell[t]{"; + stream << "\\tikzmark{active_" << currState->index << "}"; + stream << "$\\widetilde{s}_a(" << currState->index << ")$: " << stateListToTex(flatId.getFlatActive(), flatId.getActive().size() == 0) << "\\\\"; + + // already visited states + stream << "\\tikzmark{visited_" << currState->index << "}"; + stream << "$\\widetilde{s}_d(" << currState->index << ")$: " << stateListToTex(flatId.getFlatVisited(), flatId.getVisited().size() == 0) << "\\\\"; + + // history assignments + stream << "\\tikzmark{history_" << currState->index << "}"; + stream << "$\\widetilde{s}_h(" << currState->index << ")$: " << stateListToTex(flatId.getFlatHistory(), flatId.getHistory().size() == 0) << "} & "; + + // all transitions + std::set<std::string> origTransitions; + for (std::list<GlobalTransition*>::iterator transIter = stateIter->second->sortedOutgoing.begin(); transIter != stateIter->second->sortedOutgoing.end(); transIter++) { + GlobalTransition* currTrans = *transIter; + Arabica::XPath::NodeSet<std::string> members = currTrans->getTransitions(); + for (int i = 0; i < members.size(); i++) { + Element<std::string> transElem(members[i]); + if (HAS_ATTR(transElem, "priority")) { + origTransitions.insert(ATTR(transElem, "priority")); + } else { + origTransitions.insert("initial"); + } + } + } + + if (origTransitions.size() > 0) { + stream << "$\\{ "; + seperator = ""; + for (std::set<std::string>::reverse_iterator transIter = origTransitions.rbegin(); transIter != origTransitions.rend(); transIter++) { + stream << seperator << "t_{" << *transIter << "}"; + seperator = ", "; + } + stream << " \\}$"; + } else { + stream << "$\\emptyset$"; + } + stream << "\\tikzmark{transitions_" << currState->index << "}"; + stream << " & \\\\ \\hline" << std::endl; + + if (stateIter->second->sortedOutgoing.size() > 0) { + stream << "$\\widetilde{\\mathcal{T}}(" << currState->index << ")$" << std::endl; + + size_t ecIndex = 0; + for (std::list<GlobalTransition*>::iterator transIter = stateIter->second->sortedOutgoing.begin(); transIter != stateIter->second->sortedOutgoing.end(); transIter++, ecIndex++) { + GlobalTransition* currTrans = *transIter; + stream << "& "; + stream << "\\tikzmark{trans_set" << currState->index << "_" << ecIndex << "}"; + + if (!currTrans->isValid) + stream << "\\sout{"; + + Arabica::XPath::NodeSet<std::string> members = currTrans->getTransitions(); + if (members.size() > 0) { + stream << "$\\{ "; + seperator = ""; + for (int i = 0; i < members.size(); i++) { + Element<std::string> transElem(members[i]); + if (HAS_ATTR(transElem, "priority")) { + stream << seperator << "t_{" << ATTR(transElem, "priority") << "}"; + } else { + stream << seperator << "t_{initial}"; + } + seperator = ", "; + } + stream << " \\}$"; + } else { + stream << "$\\emptyset$"; + } + // stream << "& \\sout{$\\{ t_2, t_0 \\}$}, & \\emph{$Inv_4$: nested source states} \\\\" << std::endl; + // stream << "& $\\{ t_2 \\}$ & & $\\widetilde{s}(2)$ \\\\" << std::endl; + // stream << "& $\\{ t_0 \\}$ & & $\\widetilde{s}(4)$ \\\\" << std::endl; + + if (!currTrans->isValid) { +#if 1 + stream << " } & \\emph{"; + switch(currTrans->invalidReason) { + case GlobalTransition::NO_COMMON_EVENT: + stream << "$Inv_1$: "; break; + case GlobalTransition::MIXES_EVENT_SPONTANEOUS: + stream << "$Inv_2$: "; break; + case GlobalTransition::SAME_SOURCE_STATE: + stream << "$Inv_3$: "; break; + case GlobalTransition::CHILD_ENABLED: + stream << "$Inv_4$: "; break; + case GlobalTransition::PREEMPTING_MEMBERS: + stream << "$Inv_5$: "; break; + case GlobalTransition::UNCONDITIONAL_MATCH: + stream << "$Opt_1$: "; break; + case GlobalTransition::UNCONDITIONAL_SUPERSET: + stream << "$Opt_2$: "; break; + } + stream << currTrans->invalidMsg << "} "; +#endif + stream << "\\tikzmark{exec_content" << currState->index << "_" << ecIndex << "}"; + stream << " & "; + } else { + stream << " & "; + std::stringstream execContentSS; + + seperator = ""; + for (std::list<GlobalTransition::Action>::iterator actionIter = currTrans->actions.begin(); actionIter != currTrans->actions.end(); actionIter++) { + Element<std::string> execContent; + + if (actionIter->onEntry) + execContent = actionIter->onEntry; + + if (actionIter->raiseDone) + execContent = actionIter->raiseDone; + + if (actionIter->onExit) + execContent = actionIter->onExit; + + if (actionIter->transition) + execContent = actionIter->transition; + + if (execContent) { + if (HAS_ATTR(execContent, "line_start") && HAS_ATTR(execContent, "line_end")) { + size_t lineStart = strTo<size_t>(ATTR(execContent, "line_start")); + size_t lineEnd = strTo<size_t>(ATTR(execContent, "line_end")); + lineStart++; + lineEnd--; + if (lineStart == lineEnd) { + execContentSS << seperator << "l_{" << lineStart << "}"; + } else { + execContentSS << seperator << "l_{" << lineStart << "-" << lineEnd << "}"; + } + } + seperator = ", "; + } + } + + if (execContentSS.str().size() > 0) { + stream << "$\\mathcal{X} := (" << execContentSS.str() << ")$"; + } else { + stream << "$\\emptyset$"; + } + stream << "\\tikzmark{exec_content" << currState->index << "_" << ecIndex << "}"; + + stream << " & $\\widetilde{s}(" << _globalConf[currTrans->destination]->index << ")$ "; + stream << "\\tikzmark{target" << currState->index << "_" << ecIndex << "}"; + } + + stream << "\\\\" << std::endl; + } + if (stateIter->second->sortedOutgoing.size() == 0) { + stream << " & & & \\\\" << std::endl; + } + + stream << "\\hline" << std::endl; + } + stream << "%</globalState" << currState->index << ">" << std::endl; + + } + if (wroteRowStart) { + stream << "%</tableRows>" << std::endl; + } + +// stream << "\\end{tabular}" << std::endl; +// stream << "\\end{table}" << std::endl << std::endl; + stream << "\\end{longtable}" << std::endl << std::endl; + +} + +std::string ChartToTex::stateListToTex(const std::string& input, bool isEmpty) { + std::string statesTex; + if (!isEmpty) { + statesTex = input; + boost::replace_all(statesTex, "active:", ""); + boost::replace_all(statesTex, "history:", ""); + boost::replace_all(statesTex, "visited:", ""); + statesTex = "\\texttt{" + texEscape(statesTex) + "}"; + } else { + statesTex = "$\\emptyset$"; + } + return statesTex; +} + +std::string ChartToTex::texEscape(const std::string& input) { + std::string texString(input); + boost::replace_all(texString, "\\", "\\\\"); + boost::replace_all(texString, "{", "\\{"); + boost::replace_all(texString, "}", "\\}"); + boost::replace_all(texString, ",", ", "); + return texString; +} + + +}
\ No newline at end of file diff --git a/src/uscxml/transform/ChartToTex.h b/src/uscxml/transform/ChartToTex.h new file mode 100644 index 0000000..b7542f4 --- /dev/null +++ b/src/uscxml/transform/ChartToTex.h @@ -0,0 +1,61 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef CHARTTOTEX_H_2B7D5889 +#define CHARTTOTEX_H_2B7D5889 + + +#include "Transformer.h" +#include "ChartToFSM.h" +#include "uscxml/Interpreter.h" +#include "uscxml/DOMUtils.h" +#include "uscxml/util/Trie.h" + +#include <DOM/Document.hpp> +#include <DOM/Node.hpp> +#include <XPath/XPath.hpp> +#include <ostream> + +namespace uscxml { + +class USCXML_API ChartToTex : public TransformerImpl, public ChartToFSM { +public: + + virtual ~ChartToTex(); + static Transformer transform(const Interpreter& other); + + void writeTo(std::ostream& stream); + +protected: + ChartToTex(const Interpreter& other) + : TransformerImpl(), + ChartToFSM(other) {} + + void writeTex(std::ostream& stream); + + std::map<unsigned long, GlobalState*> _indexToState; + +private: + static std::string stateListToTex(const std::string& input, bool isEmpty); + static std::string texEscape(const std::string& input); +}; + +} + +#endif /* end of include guard: CHARTTOTEX_H_2B7D5889 */ diff --git a/src/uscxml/transform/FlatStateIdentifier.h b/src/uscxml/transform/FlatStateIdentifier.h index 011888a..f082b18 100644 --- a/src/uscxml/transform/FlatStateIdentifier.h +++ b/src/uscxml/transform/FlatStateIdentifier.h @@ -42,6 +42,10 @@ public: return stateId.length() > 0; } + bool operator<( const FlatStateIdentifier& other) const { + return stateId < other.stateId; + } + FlatStateIdentifier(const Arabica::XPath::NodeSet<std::string>& activeStates, const Arabica::XPath::NodeSet<std::string>& alreadyEnteredStates, const std::map<std::string, Arabica::XPath::NodeSet<std::string> >& historyStates) { @@ -166,29 +170,31 @@ public: initStateId(); } - const std::string& getStateId() { + const std::string& getStateId() const { return stateId; } - const std::list<std::string>& getActive() { + const std::list<std::string>& getActive() const { return active; } - const std::string& getFlatActive() { + const std::string& getFlatActive() const { return flatActive; } - const std::string& getFlatHistory() { + + const std::string& getFlatHistory() const { return flatHistories; } - const std::list<std::string>& getVisited() { + const std::list<std::string>& getVisited() const { return visited; } - const std::string& getFlatVisited() { + const std::string& getFlatVisited() const { return flatVisited; } - const std::map<std::string, std::list<std::string> > & getHistory() { + + const std::map<std::string, std::list<std::string> > & getHistory() const { return histories; } |