summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2015-01-19 16:41:18 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2015-01-19 16:41:18 (GMT)
commitff86d690dc02d7dd495000331d378e7d8eb688ac (patch)
tree5214786f7e575952d3cba0919e5071f3a783050b
parent42437db418574f2a80d098e568b9498a21343800 (diff)
downloaduscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.zip
uscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.tar.gz
uscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.tar.bz2
Plenty of smaller fixes and adaptations
-rw-r--r--CMakeLists.txt14
-rw-r--r--apps/uscxml-analyze.cpp193
-rw-r--r--apps/uscxml-browser.cpp36
-rw-r--r--apps/uscxml-transform.cpp119
-rwxr-xr-xcontrib/local/annotate-xml-lineno.pl43
-rwxr-xr-xcontrib/local/create-random-scxml.pl214
-rw-r--r--src/bindings/swig/csharp/uscxml.i15
-rw-r--r--src/uscxml/Common.h24
-rw-r--r--src/uscxml/Convenience.cpp128
-rw-r--r--src/uscxml/Convenience.h90
-rw-r--r--src/uscxml/DOMUtils.cpp6
-rw-r--r--src/uscxml/DOMUtils.h3
-rw-r--r--src/uscxml/Interpreter.cpp97
-rw-r--r--src/uscxml/Interpreter.h2
-rw-r--r--src/uscxml/debug/InterpreterIssue.cpp59
-rw-r--r--src/uscxml/debug/SCXMLDotWriter.cpp2
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.cpp4
-rw-r--r--src/uscxml/interpreter/InterpreterRC.cpp6
-rw-r--r--src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp12
-rw-r--r--src/uscxml/plugins/datamodel/promela/PromelaDataModel.h1
-rw-r--r--src/uscxml/plugins/invoker/xhtml/template/xhtml-invoker.inc.h1376
-rw-r--r--src/uscxml/transform/ChartToCPP.cpp.todo (renamed from src/uscxml/transform/ChartToCPP.cpp)0
-rw-r--r--src/uscxml/transform/ChartToCPP.h.todo (renamed from src/uscxml/transform/ChartToCPP.h)0
-rw-r--r--src/uscxml/transform/ChartToFSM.cpp798
-rw-r--r--src/uscxml/transform/ChartToFSM.cpp.new1482
-rw-r--r--src/uscxml/transform/ChartToFSM.h115
-rw-r--r--src/uscxml/transform/ChartToFSM.h.new303
-rw-r--r--src/uscxml/transform/ChartToFlatSCXML.cpp139
-rw-r--r--src/uscxml/transform/ChartToMinimalSCXML.cpp35
-rw-r--r--src/uscxml/transform/ChartToMinimalSCXML.h3
-rw-r--r--src/uscxml/transform/ChartToPromela.cpp457
-rw-r--r--src/uscxml/transform/ChartToPromela.h31
-rw-r--r--src/uscxml/transform/ChartToTex.cpp284
-rw-r--r--src/uscxml/transform/ChartToTex.h61
-rw-r--r--src/uscxml/transform/FlatStateIdentifier.h20
-rw-r--r--test/uscxml/promela/test-complete.scxml154
-rw-r--r--test/uscxml/promela/test-event-source-auto.scxml22
-rwxr-xr-xtest/w3c/analyze_tests.pl21
-rw-r--r--test/w3c/run_promela_test.cmake5
39 files changed, 5171 insertions, 1203 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 789fb2b..edb8716 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1235,6 +1235,20 @@ if (NOT CMAKE_CROSSCOMPILING)
install_executable(TARGETS uscxml-transform COMPONENT tools)
if (WIN32)
+ add_executable(uscxml-analyze apps/uscxml-analyze.cpp ${PROJECT_SOURCE_DIR}/contrib/src/getopt/getopt.c)
+ else()
+ add_executable(uscxml-analyze apps/uscxml-analyze.cpp)
+ endif()
+ target_link_libraries(uscxml-analyze uscxml uscxml_transform)
+ if (NOT CMAKE_CROSSCOMPILING)
+ if (ENABLE_COTIRE)
+ set_target_properties(uscxml-analyze PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
+ endif()
+ endif()
+ set_target_properties(uscxml-analyze PROPERTIES FOLDER "Apps")
+ install_executable(TARGETS uscxml-analyze COMPONENT tools)
+
+ if (WIN32)
add_executable(uscxml-dot apps/uscxml-dot.cpp ${PROJECT_SOURCE_DIR}/contrib/src/getopt/getopt.c)
else()
add_executable(uscxml-dot apps/uscxml-dot.cpp)
diff --git a/apps/uscxml-analyze.cpp b/apps/uscxml-analyze.cpp
new file mode 100644
index 0000000..75ec48c
--- /dev/null
+++ b/apps/uscxml-analyze.cpp
@@ -0,0 +1,193 @@
+#include "uscxml/config.h"
+#include "uscxml/Interpreter.h"
+#include "uscxml/transform/ChartToFSM.h"
+#include "uscxml/DOMUtils.h"
+#include <glog/logging.h>
+#include <fstream>
+#include <iostream>
+
+#include "uscxml/Factory.h"
+#include "uscxml/server/HTTPServer.h"
+#include "getopt.h"
+
+#ifdef HAS_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAS_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#ifdef HAS_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#define ANNOTATE(envKey, annotationParam) \
+envVarIsTrue(envKey) || std::find(annotations.begin(), annotations.end(), annotationParam) != annotations.end()
+
+void printUsageAndExit(const char* progName) {
+ // remove path from program name
+ std::string progStr(progName);
+ if (progStr.find_last_of(PATH_SEPERATOR) != std::string::npos) {
+ progStr = progStr.substr(progStr.find_last_of(PATH_SEPERATOR) + 1, progStr.length() - (progStr.find_last_of(PATH_SEPERATOR) + 1));
+ }
+
+ printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progStr.c_str());
+ printf("Usage\n");
+ printf("\t%s", progStr.c_str());
+#ifdef BUILD_AS_PLUGINS
+ printf(" [-p pluginPath]");
+#endif
+ printf(" [URL]");
+ printf("\n");
+ exit(1);
+}
+
+int main(int argc, char** argv) {
+ using namespace uscxml;
+
+ std::string outType;
+ std::string pluginPath;
+ std::string inputFile;
+ std::string outputFile;
+ std::list<std::string> annotations;
+
+#if defined(HAS_SIGNAL_H) && !defined(WIN32)
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ // setup logging
+ google::LogToStderr();
+ google::InitGoogleLogging(argv[0]);
+
+ optind = 0;
+ opterr = 0;
+
+ struct option longOptions[] = {
+ {"help", required_argument, 0, 'p'},
+ {"plugin-path", required_argument, 0, 'p'},
+ {"loglevel", required_argument, 0, 'l'},
+ {0, 0, 0, 0}
+ };
+
+ // parse global options
+ int optionInd = 0;
+ int option;
+ for (;;) {
+ option = getopt_long_only(argc, argv, "p:l:h", longOptions, &optionInd);
+ if (option == -1) {
+ break;
+ }
+ switch(option) {
+ // cases without short option
+ case 0: {
+ break;
+ }
+ // cases with short-hand options
+ case 'p':
+ pluginPath = optarg;
+ break;
+ case 'l':
+ break;
+ case 'h':
+ case '?': {
+ printUsageAndExit(argv[0]);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ inputFile = argv[optind];
+ }
+
+ // register plugins
+ if (pluginPath.length() > 0) {
+ Factory::setDefaultPluginPath(pluginPath);
+ }
+
+ // start HTTP server
+ HTTPServer::getInstance(31444, 31445, NULL);
+
+ Interpreter interpreter;
+ try {
+ if (inputFile.size() == 0 || inputFile == "-") {
+ LOG(INFO) << "Reading SCXML from STDIN";
+ std::stringstream ss;
+ std::string line;
+ while (std::getline(std::cin, line)) {
+ ss << line;
+ }
+ URL tmp("anonymous.scxml");
+ tmp.toAbsoluteCwd();
+ interpreter = Interpreter::fromXML(ss.str(), tmp);
+ } else {
+ interpreter = Interpreter::fromURL(inputFile);
+ }
+ if (!interpreter) {
+ LOG(ERROR) << "Cannot create interpreter from " << inputFile;
+ exit(EXIT_FAILURE);
+ }
+
+ // analyze here
+ Arabica::XPath::NodeSet<std::string> states = interpreter.getNodeSetForXPath("//" + interpreter.getNameSpaceInfo().xpathPrefix + "state");
+ Arabica::XPath::NodeSet<std::string> final = interpreter.getNodeSetForXPath("//" + interpreter.getNameSpaceInfo().xpathPrefix + "final");
+ Arabica::XPath::NodeSet<std::string> parallels = interpreter.getNodeSetForXPath("//" + interpreter.getNameSpaceInfo().xpathPrefix + "parallel");
+ Arabica::XPath::NodeSet<std::string> shallowHistories = interpreter.getNodeSetForXPath("//" + interpreter.getNameSpaceInfo().xpathPrefix + "history[@type='shallow']");
+ shallowHistories.push_back(interpreter.getNodeSetForXPath("//" + interpreter.getNameSpaceInfo().xpathPrefix + "history[not(@type)]"));
+ Arabica::XPath::NodeSet<std::string> deepHistories = interpreter.getNodeSetForXPath("//" + interpreter.getNameSpaceInfo().xpathPrefix + "history[@type='deep']");
+ Arabica::XPath::NodeSet<std::string> transitions = interpreter.getNodeSetForXPath("//" + interpreter.getNameSpaceInfo().xpathPrefix + "transition");
+
+ std::cout << "# Number of elements" << std::endl;
+ std::cout << "nr_states: " << (states.size() + final.size() + parallels.size()) << std::endl;
+ std::cout << "nr_parallel: " << parallels.size() << std::endl;
+ std::cout << "nr_hist_flat: " << shallowHistories.size() << std::endl;
+ std::cout << "nr_hist_deep: " << deepHistories.size() << std::endl;
+ std::cout << "nr_trans: " << transitions.size() << std::endl;
+
+
+ std::cout << "# Transition Histogram: number of transitions, number of active configurations" << std::endl;
+
+ size_t numberOfLegalConfs = 0;
+ size_t lastBin = 0;
+ std::map<size_t, size_t> histogram = Complexity::getTransitionHistogramm(interpreter.getDocument().getDocumentElement());
+ for (std::map<size_t, size_t>::iterator binIter = histogram.begin(); binIter != histogram.end(); binIter++) {
+ while (binIter->first > lastBin) {
+ std::cout << "th: " << toStr(lastBin++) << ", 0" << std::endl;
+ }
+ std::cout << "th: " << toStr(binIter->first) << ", " << binIter->second << std::endl;
+ numberOfLegalConfs += binIter->second;
+ lastBin = binIter->first + 1;
+ }
+
+ std::stringstream transPowerSetSS;
+ std::string transPowerSetSeperator = "";
+ for (std::map<size_t, size_t>::reverse_iterator binIter = histogram.rbegin(); binIter != histogram.rend(); binIter++) {
+ transPowerSetSS << transPowerSetSeperator << binIter->second << " * " << "2**" << binIter->first;
+ transPowerSetSeperator = " + ";
+ }
+ std::cout << "# Sum of Powersets:" << std::endl;
+ std::cout << "ps_sum: " << transPowerSetSS.str() << std::endl;
+ std::cout << "# Upper bounds:" << std::endl;
+ std::cout << "# \tActive configurations: " << std::endl;
+ std::cout << "up_ac: " << numberOfLegalConfs << std::endl;
+ std::cout << "# \tGlobal configurations: " << std::endl;
+ std::cout << "up_gc: " << Complexity::stateMachineComplexity(interpreter.getDocument().getDocumentElement()) << std::endl;
+
+ std::cout << "# \tGlobal configurations (no history): " << std::endl;
+ std::cout << "up_gcnh: " << Complexity::stateMachineComplexity(interpreter.getDocument().getDocumentElement(), uscxml::Complexity::IGNORE_HISTORY) << std::endl;
+
+ std::cout << "# \tGlobal configurations (no nested data): " << std::endl;
+ std::cout << "up_gcnd: " << Complexity::stateMachineComplexity(interpreter.getDocument().getDocumentElement(), uscxml::Complexity::IGNORE_NESTED_DATA) << std::endl;
+
+ std::cout << "# \tGlobal configurations (no nested data, no history): " << std::endl;
+ std::cout << "up_gcnhd: " << Complexity::stateMachineComplexity(interpreter.getDocument().getDocumentElement(), uscxml::Complexity::IGNORE_HISTORY_AND_NESTED_DATA) << std::endl;
+
+ } catch (Event e) {
+ std::cout << e << std::endl;
+ }
+
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/apps/uscxml-browser.cpp b/apps/uscxml-browser.cpp
index 87b1b1d..0562ba7 100644
--- a/apps/uscxml-browser.cpp
+++ b/apps/uscxml-browser.cpp
@@ -22,40 +22,6 @@
#include <dlfcn.h>
#endif
-class VerboseMonitor : public uscxml::InterpreterMonitor {
- void onStableConfiguration(uscxml::Interpreter interpreter) {
- printConfig(interpreter.getConfiguration());
- }
-
- void beforeProcessingEvent(uscxml::Interpreter interpreter, const uscxml::Event& event) {
- switch (event.eventType) {
- case uscxml::Event::INTERNAL:
- std::cout << "Internal Event: " << event.name << std::endl;
- break;
- case uscxml::Event::EXTERNAL:
- std::cout << "External Event: " << event.name << std::endl;
- break;
- case uscxml::Event::PLATFORM:
- std::cout << "Platform Event: " << event.name << std::endl;
- break;
- }
- }
-
- void beforeCompletion(uscxml::Interpreter interpreter) {
- printConfig(interpreter.getConfiguration());
- }
-
- void printConfig(const Arabica::XPath::NodeSet<std::string>& config) {
- std::string seperator;
- std::cout << "Config: {";
- for (int i = 0; i < config.size(); i++) {
- std::cout << seperator << ATTR_CAST(config[i], "id");
- seperator = ", ";
- }
- std::cout << "}" << std::endl;
- }
-};
-
#ifdef CMAKE_BUILD_TYPE_DEBUG
#ifdef HAS_EXECINFO_H
@@ -206,7 +172,7 @@ int main(int argc, char** argv) {
interpreter.setCapabilities(options.getCapabilities());
if (options.verbose) {
- VerboseMonitor* vm = new VerboseMonitor();
+ StateTransitionMonitor* vm = new StateTransitionMonitor();
interpreter.addMonitor(vm);
}
diff --git a/apps/uscxml-transform.cpp b/apps/uscxml-transform.cpp
index d8a9013..055c1ae 100644
--- a/apps/uscxml-transform.cpp
+++ b/apps/uscxml-transform.cpp
@@ -1,6 +1,7 @@
#include "uscxml/config.h"
#include "uscxml/Interpreter.h"
#include "uscxml/transform/ChartToFlatSCXML.h"
+#include "uscxml/transform/ChartToTex.h"
#include "uscxml/transform/ChartToMinimalSCXML.h"
#include "uscxml/transform/ChartToPromela.h"
#include "uscxml/DOMUtils.h"
@@ -24,6 +25,9 @@
#include <dlfcn.h>
#endif
+#define ANNOTATE(envKey, annotationParam) \
+envVarIsTrue(envKey) || std::find(annotations.begin(), annotations.end(), annotationParam) != annotations.end()
+
class VerboseMonitor : public uscxml::InterpreterMonitor {
void onStableConfiguration(uscxml::Interpreter interpreter) {
printConfig(interpreter.getConfiguration());
@@ -58,20 +62,29 @@ void printUsageAndExit(const char* progName) {
printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progStr.c_str());
printf("Usage\n");
printf("\t%s", progStr.c_str());
- printf(" [-t pml|flat] [-v] [-lN]");
+ printf(" [-t pml|flat|min|tex] [-a {OPTIONS}] [-v] [-lN]");
#ifdef BUILD_AS_PLUGINS
printf(" [-p pluginPath]");
#endif
printf(" [-i URL] [-o FILE]");
printf("\n");
printf("Options\n");
- printf("\t-t flat : flatten to SCXML state-machine\n");
- printf("\t-t pml : convert to spin/promela program\n");
- printf("\t-t min : minimize SCXML state-chart\n");
- printf("\t-v : be verbose\n");
- printf("\t-lN : Set loglevel to N\n");
- printf("\t-i URL : Input file (defaults to STDIN)\n");
- printf("\t-o FILE : Output file (defaults to STDOUT)\n");
+ printf("\t-t flat : flatten to SCXML state-machine\n");
+ printf("\t-t pml : convert to spin/promela program\n");
+ printf("\t-t min : minimize SCXML state-chart\n");
+ printf("\t-t tex : write global state transition table as tex file\n");
+ printf("\t-a {OPTIONS} : annotate SCXML elements with comma seperated options\n");
+ printf("\t 'priority' - transitions with their priority for transition selection\n");
+ printf("\t 'step' - global states with their step identifier (-tflat only)\n");
+ printf("\t 'members' - global transitions with their member transitions per index (-tflat only)\n");
+ printf("\t 'sends' - transititve number of sends to external queue for global transitions (-tflat only)\n");
+ printf("\t 'raises' - transititve number of raises to internal queue for global transitions (-tflat only)\n");
+ printf("\t 'verbose' - comments detailling state changes and transitions for content selection (-tflat only)\n");
+ printf("\t 'progress' - insert comments documenting progress in dociment (-tmin only)\n");
+ printf("\t-v : be verbose\n");
+ printf("\t-lN : Set loglevel to N\n");
+ printf("\t-i URL : Input file (defaults to STDIN)\n");
+ printf("\t-o FILE : Output file (defaults to STDOUT)\n");
printf("\n");
exit(1);
}
@@ -84,7 +97,8 @@ int main(int argc, char** argv) {
std::string pluginPath;
std::string inputFile;
std::string outputFile;
-
+ std::list<std::string> annotations;
+
#if defined(HAS_SIGNAL_H) && !defined(WIN32)
signal(SIGPIPE, SIG_IGN);
#endif
@@ -99,9 +113,10 @@ int main(int argc, char** argv) {
struct option longOptions[] = {
{"verbose", no_argument, 0, 'v'},
{"type", required_argument, 0, 't'},
+ {"annotate", required_argument, 0, 'a'},
{"plugin-path", required_argument, 0, 'p'},
{"input-file", required_argument, 0, 'i'},
- {"output-file", required_argument, 0, 'o'},
+ {"output-file", required_argument, 0, 'o'},
{"loglevel", required_argument, 0, 'l'},
{0, 0, 0, 0}
};
@@ -110,7 +125,7 @@ int main(int argc, char** argv) {
int optionInd = 0;
int option;
for (;;) {
- option = getopt_long_only(argc, argv, "+vp:t:i:o:l:", longOptions, &optionInd);
+ option = getopt_long_only(argc, argv, "+vp:t:i:o:l:a:", longOptions, &optionInd);
if (option == -1) {
break;
}
@@ -132,6 +147,9 @@ int main(int argc, char** argv) {
case 'i':
inputFile = optarg;
break;
+ case 'a':
+ annotations = InterpreterImpl::tokenize(optarg, ',');
+ break;
case 'o':
outputFile = optarg;
break;
@@ -145,18 +163,45 @@ int main(int argc, char** argv) {
}
}
- if (outType.length() == 0 && outputFile.length() > 0) {
- // try to get type from outfile extension
- size_t dotPos = outputFile.find_last_of(".");
- if (dotPos != std::string::npos) {
- outType= outputFile.substr(dotPos + 1);
- }
- }
+ // make sure given annotation options are available in the environment
+ if(ANNOTATE("USCXML_ANNOTATE_GLOBAL_STATE_STEP", "step"))
+ setenv("USCXML_ANNOTATE_GLOBAL_STATE_STEP", "YES", 1);
+
+ if (ANNOTATE("USCXML_ANNOTATE_GLOBAL_TRANS_PRIO", "priority"))
+ setenv("USCXML_ANNOTATE_GLOBAL_TRANS_PRIO", "YES", 1);
+
+ if (ANNOTATE("USCXML_ANNOTATE_VERBOSE_COMMENTS", "verbose"))
+ setenv("USCXML_ANNOTATE_VERBOSE_COMMENTS", "YES", 1);
+
+ if(ANNOTATE("USCXML_ANNOTATE_GLOBAL_TRANS_MEMBERS", "members"))
+ setenv("USCXML_ANNOTATE_GLOBAL_TRANS_MEMBERS", "YES", 1);
+
+ if(ANNOTATE("USCXML_ANNOTATE_GLOBAL_TRANS_SENDS", "sends"))
+ setenv("USCXML_ANNOTATE_GLOBAL_TRANS_SENDS", "YES", 1);
+
+ if(ANNOTATE("USCXML_ANNOTATE_GLOBAL_TRANS_RAISES", "raises"))
+ setenv("USCXML_ANNOTATE_GLOBAL_TRANS_RAISES", "YES", 1);
+
+ if(ANNOTATE("USCXML_ANNOTATE_PROGRESS", "progress"))
+ setenv("USCXML_ANNOTATE_PROGRESS", "YES", 1);
+
+// if (outType.length() == 0 && outputFile.length() > 0) {
+// // try to get type from outfile extension
+// size_t dotPos = outputFile.find_last_of(".");
+// if (dotPos != std::string::npos) {
+// outType= outputFile.substr(dotPos + 1);
+// }
+// }
- if (outType.length() == 0)
- printUsageAndExit(argv[0]);
+// if (outType.length() == 0)
+// printUsageAndExit(argv[0]);
- if (outType != "flat" && outType != "scxml" && outType != "pml" && outType != "min")
+ if (outType != "flat" &&
+ outType != "scxml" &&
+ outType != "pml" &&
+ outType != "min" &&
+ outType != "tex" &&
+ std::find(annotations.begin(), annotations.end(), "priority") == annotations.end())
printUsageAndExit(argv[0]);
// register plugins
@@ -200,7 +245,19 @@ int main(int argc, char** argv) {
exit(EXIT_SUCCESS);
}
- if (outType == "scxml" || outType == "flat") {
+ if (outType == "tex") {
+ if (outputFile.size() == 0 || outputFile == "-") {
+ ChartToTex::transform(interpreter).writeTo(std::cout);
+ } else {
+ std::ofstream outStream;
+ outStream.open(outputFile.c_str());
+ ChartToTex::transform(interpreter).writeTo(outStream);
+ outStream.close();
+ }
+ exit(EXIT_SUCCESS);
+ }
+
+ if (outType == "flat") {
if (outputFile.size() == 0 || outputFile == "-") {
ChartToFlatSCXML::transform(interpreter).writeTo(std::cout);
} else {
@@ -224,6 +281,24 @@ int main(int argc, char** argv) {
exit(EXIT_SUCCESS);
}
+#if 1
+ if (annotations.size() > 0) {
+ ChartToFSM annotater(interpreter);
+ if (std::find(annotations.begin(), annotations.end(), "priority") != annotations.end())
+ annotater.indexTransitions();
+
+ if (outputFile.size() == 0 || outputFile == "-") {
+ std::cout << annotater.getDocument();
+ } else {
+ std::ofstream outStream;
+ outStream.open(outputFile.c_str());
+ outStream << annotater.getDocument();
+ outStream.close();
+ }
+ exit(EXIT_SUCCESS);
+ }
+#endif
+
} catch (Event e) {
std::cout << e << std::endl;
}
diff --git a/contrib/local/annotate-xml-lineno.pl b/contrib/local/annotate-xml-lineno.pl
new file mode 100755
index 0000000..f7e1a79
--- /dev/null
+++ b/contrib/local/annotate-xml-lineno.pl
@@ -0,0 +1,43 @@
+#!/usr/bin/perl -w
+
+use strict;
+use File::Spec;
+use File::Basename;
+use XML::LibXML;
+use Data::Dumper;
+
+my $xmlIn = shift or die("Expected *.xml file as input");
+
+# absolutize and split into components
+$xmlIn = File::Spec->rel2abs($xmlIn) or die($!);
+my($filename, $dirs, $suffix) = fileparse($xmlIn) or die($!);
+
+my $parser = XML::LibXML->new({'line_numbers' => 1 });
+# my $xml = $parser->parse_file($xmlIn);
+my $doc = $parser->load_xml('location' => $xmlIn, 'line_numbers' => 1) ;
+
+my $lineOffset = 0;
+
+sub lineNoNodes {
+ my $node = shift;
+
+ if ($node->nodeType == XML_ELEMENT_NODE) {
+ $node->setAttribute("line_start", $node->line_number() + $lineOffset);
+ }
+
+ my $prevElem;
+ for my $child ($node->childNodes()) {
+ lineNoNodes($child);
+ if ($prevElem) {
+ $prevElem->setAttribute("line_end", $child->line_number() - 1 + $lineOffset);
+ undef($prevElem);
+ }
+ if ($child->nodeType == XML_ELEMENT_NODE) {
+ $prevElem = $child;
+ }
+ }
+}
+
+&lineNoNodes($doc->getDocumentElement());
+
+print $doc->toString(); \ No newline at end of file
diff --git a/contrib/local/create-random-scxml.pl b/contrib/local/create-random-scxml.pl
new file mode 100755
index 0000000..7c6a3b8
--- /dev/null
+++ b/contrib/local/create-random-scxml.pl
@@ -0,0 +1,214 @@
+#!/usr/bin/perl -w
+
+use strict;
+use List::Util qw[min max sum];
+
+use Getopt::Long qw(GetOptions);
+use Data::Dumper;
+
+my %options = ();
+
+GetOptions(
+ \%options,
+ "depth-max=i",
+ "child-max=i",
+ "events-max=i",
+ "states-max=i",
+ "trans-max=i",
+ "random-seed=i"
+);
+
+my $seed = $options{'random-seed'} || int(rand(2**31));
+my $maxDepth = $options{'depth-max'} || 6;
+my $maxChilds = $options{'child-max'} || 6;
+my $maxStates = $options{'states-max'} || 60;
+my $maxTrans = $options{'trans-max'} || 6;
+my $maxEvents = $options{'trans-max'} || int($maxStates / 3) + 1;
+
+srand($seed);
+
+my $machine;
+my $stateId = 0;
+
+my $probs = {
+ 'state' => {
+ 'type' => {
+ 'history' => 1,
+ 'parallel' => 2,
+ 'state' => 6,
+ 'final' => 1
+ }
+ },
+ 'transition' => {
+ 'target' => 0.8,
+ 'event' => 0.7,
+ 'cond' => 0.9,
+ 'execContent' => 0.7,
+ },
+ 'history' => {
+ 'deep' => 0.2
+ }
+};
+
+my $sumChildProbs = sum( values(%{$probs->{'state'}->{'type'}}));
+
+sub putMachine {
+ my $where = shift;
+
+ $$where->{'name'} = 'test';
+ $$where->{'type'} = 'scxml';
+ $$where->{'datamodel'} = 'ecmascript';
+
+ putState(\$$where->{'children'}, 0);
+ putTransition(\$$where);
+}
+
+sub putTransition {
+ my $where = shift;
+
+ return if $$where->{'type'} eq 'final';
+
+ my $nrTrans = int(rand($maxTrans + 1));
+ $nrTrans = min($nrTrans, 1) if $$where->{'type'} eq 'history';
+
+ for (my $i = 0; $i < $nrTrans; $i++) {
+
+ my $trans;
+ if (rand(1) < $probs->{'transition'}->{'target'}) {
+ # has a target - pick one at random
+ $trans->{'target'} = 'id' . int(rand($stateId));
+ }
+
+ if (rand(1) < $probs->{'transition'}->{'event'}) {
+ # has an event
+ $trans->{'event'} = 'e' . int(rand($maxEvents + 1));
+ }
+
+ if (rand(1) < $probs->{'transition'}->{'cond'}) {
+ # has a condition
+ if (int(rand(2)) > 0) {
+ $trans->{'cond'} = 'true';
+ } else {
+ $trans->{'cond'} = 'false';
+ }
+ }
+
+ if (rand(1) < $probs->{'transition'}->{'execContent'}) {
+ # has a executable content
+ push @{$trans->{'execContent'}}, '<log label="foo" />';
+ }
+
+ push @{$$where->{'transitions'}}, $trans;
+ }
+
+ # continue with childs
+ foreach (@{$$where->{'children'}}) {
+ putTransition(\$_);
+ }
+}
+
+sub putState {
+ my $where = shift;
+ my $depth = shift;
+ my $minStates = shift || 0;
+ my $r;
+
+ return if ($stateId > $maxStates);
+ return if ($depth > $maxDepth);
+ my $nrChilds = int(rand($maxChilds + 1));
+ $nrChilds = max($minStates, $nrChilds);
+
+ for (my $i = 0; $i < $nrChilds; $i++) {
+ my $r = rand($sumChildProbs);
+
+ my $state;
+ foreach my $type (keys %{$probs->{'state'}->{'type'}}) {
+ my $prob = $probs->{'state'}->{'type'}->{$type};
+ if ($r < $prob) {
+ $state->{'type'} = $type;
+ last;
+ }
+ $r -= $prob;
+ }
+
+ $state->{'id'} = "id".$stateId++;
+
+ if ($state->{'type'} eq 'parallel') {
+ putState(\$state->{'children'}, $depth + 1, 2);
+ } elsif ($state->{'type'} eq 'state') {
+ putState(\$state->{'children'}, $depth + 1);
+ } elsif ($state->{'type'} eq 'history') {
+ if (rand(1) < $probs->{'history'}->{'deep'}) {
+ $state->{'deep'} = 1;
+ }
+ }
+
+ push @{$$where}, $state;
+ }
+};
+
+sub writeState {
+ my $state = shift;
+
+ print STDOUT '<'.$state->{'type'};
+ print STDOUT ' id="'.$state->{'id'} . '"';
+ print STDOUT ' type="deep"' if exists $state->{'deep'};
+ print STDOUT '>';
+
+ foreach (@{$state->{'children'}}) {
+ writeState($_);
+ }
+
+ foreach (@{$state->{'transitions'}}) {
+ writeTransition($_);
+ }
+
+ print STDOUT '</'.$state->{'type'} . '>';
+
+};
+
+sub writeTransition {
+ my $trans = shift;
+
+ print STDOUT '<transition';
+ print STDOUT ' target="' . $trans->{'target'} . '"' if $trans->{'target'};
+ print STDOUT ' event="' . $trans->{'event'} . '"' if $trans->{'event'};
+ print STDOUT ' cond="' . $trans->{'cond'} . '"' if $trans->{'cond'};
+
+ if ($trans->{'execContent'}) {
+ print STDOUT '>';
+ foreach (@{$trans->{'execContent'}}) {
+ print STDOUT $_;
+ }
+ print STDOUT '</transition>';
+ } else {
+ print STDOUT '/>';
+ }
+
+};
+
+sub writeMachine {
+ my $machine = shift;
+ print STDOUT '<scxml';
+ print STDOUT ' datamodel="' . $machine->{'datamodel'} . '"' if $machine->{'datamodel'};
+ print STDOUT ' seed="' . $seed . '"';
+ print STDOUT ' name="' . $machine->{'name'} . '"' if $machine->{'name'};
+ print STDOUT '>';
+
+ foreach (@{$machine->{'children'}}) {
+ writeState($_);
+ }
+
+ print STDOUT '</scxml>';
+}
+
+putMachine(\$machine);
+# print Dumper($machine);
+
+writeMachine($machine);
+
+
+#print Dumper($machine);
+
+
+# writeState($machine);
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;
}
diff --git a/test/uscxml/promela/test-complete.scxml b/test/uscxml/promela/test-complete.scxml
new file mode 100644
index 0000000..a96152b
--- /dev/null
+++ b/test/uscxml/promela/test-complete.scxml
@@ -0,0 +1,154 @@
+<scxml datamodel="promela">
+
+ <datamodel>
+ <data id="foreachArray1" type="int[3]">[1,2,3]</data>
+ <data id="parallelVar1" type="int" expr="0"/>
+ <data id="ifVar1">{ foo: 1, bar: 'baz' }</data>
+ <data id="counter">{ itemSum: 0, indexSum: 0 }</data>
+ <data id="sendVar1" type="int" expr="4"/>
+ <data id="histVar1" type="int">0</data>
+ <data id="finalizeVar1" type="int">0</data>
+ </datamodel>
+
+ <state id="s0">
+ <history id="s0.h0" type="deep" />
+ <parallel id="p0">
+ <state id="p0.s0">
+ <state id="p0.s0.s0">
+ <onentry>
+ <if cond="ifVar1.foo == 3">
+ <log label="if choosen" />
+ <log label="ifVar1.bar is" expr="ifVar1.bar" />
+ <foreach array="foreachArray1"
+ item="foreachItem1"
+ index="foreachIndex1">
+ <script>
+ counter.indexSum = counter.indexSum + foreachIndex1;
+ counter.itemSum = counter.itemSum + foreachItem1;
+ </script>
+ <log label="foreach counter.indexSum is"
+ expr="counter.indexSum" />
+ <log label="foreach counter.itemSum is"
+ expr="counter.itemSum" />
+ </foreach>
+ <raise event="if.choosen" />
+ <elseif cond="ifVar1.bar == 'baz'" />
+ <log label="elseif choosen" />
+ <log label="ifVar1.bar is" expr="ifVar1.bar" />
+ <assign location="ifVar1.foo" expr="3" />
+ <send event="elseif.choosen" namelist="sendVar1">
+ <param name="foo" expr="sendVar1 + 16" />
+ <param name="bar" expr="'a string literal'" />
+ </send>
+ <else />
+ <log label="else choosen" />
+ <log label="ifVar1.foo is" expr="ifVar1.foo" />
+ <log label="ifVar1.bar is" expr="ifVar1.bar" />
+ <raise event="else.choosen" />
+ </if>
+ <script>parallelVar1++</script>
+ </onentry>
+ <transition event="else.choosen" target="p0">
+ <assign location="ifVar1.bar" expr="'baz'" />
+ </transition>
+ <transition event="elseif.choosen" target="p0"
+ cond="_event.data.foo == 20 &amp;&amp;
+ _event.data.sendVar1 == 4 &amp;&amp;
+ _event.data.bar == 'a string literal'"
+ />
+ </state>
+ <state id="p0.s0.s1">
+ <onentry>
+ <if cond="_x.states['p0'] &amp;&amp; histVar1 == 1">
+ <raise event="to.s2" />
+ <else />
+ <raise event="to.s1" />
+ </if>
+ </onentry>
+ <transition event="to.s2" target="s2" />
+ <transition event="to.s1" target="s1" />
+ </state>
+ </state>
+ <state id="p0.s1">
+ <onexit>
+ <script>parallelVar1++</script>
+ </onexit>
+ </state>
+ <transition event="if.choosen"
+ cond="counter.itemSum == 6 &amp;&amp;
+ counter.indexSum == 3"
+ target="p0.s0.s1" />
+ </parallel>
+ </state>
+
+ <state id="s1">
+ <invoke type="scxml" autoforward="true">
+ <content>
+ <scxml datamodel="promela">
+ <state id="waitForEvent">
+ <transition event="trigger.child">
+ <send target="#_parent" event="back.to.history" />
+ </transition>
+ </state>
+ </scxml>
+ </content>
+ <finalize>
+ <script>finalizeVar1++;</script>
+ </finalize>
+ </invoke>
+ <onentry>
+ <send event="trigger.child" delay="1000" />
+ </onentry>
+ <transition event="back.to.history"
+ cond="finalizeVar1 == 1"
+ target="s0.h0">
+ <assign location="histVar1" expr="4-3" />
+ </transition>
+ </state>
+
+ <state id="s2" initial="s2.s0">
+ <onentry>
+ <send event="cancel.delayed" delay="3000" sendid="cancel.delayed" />
+ <cancel sendid="cancel.delayed" />
+ </onentry>
+ <transition event="done.state.s2"
+ cond="_event.data.Var1 == 'foo'"
+ target="s3.h0">
+ <assign location="histVar1" expr="8" />
+ </transition>
+ <transition event="done.state.s2" target="fail" />
+ <transition event="cancel.delayed" target="fail" />
+ <transition target="pass" cond="histVar1 == 8" />
+ <state id="s2.s0">
+ <transition target="s2.s1"/>
+ </state>
+ <final id="s2.s1">
+ <donedata>
+ <param name="Var1" expr="'foo'"/>
+ </donedata>
+ </final>
+ </state>
+
+ <state id="s3">
+ <history id="s3.h0" type="shallow">
+ <transition target="s3.s1">
+ <log label="history transition" />
+ <assign location="histVar1" expr="4" />
+ </transition>
+ </history>
+ <state id="s3.s1">
+ <transition target="s2" />
+ </state>
+ </state>
+
+ <final id="pass">
+ <onentry>
+ <log label="Outcome" expr="'pass'"/>
+ </onentry>
+ </final>
+ <final id="fail">
+ <onentry>
+ <log label="Outcome" expr="'fail'"/>
+ </onentry>
+ </final>
+</scxml> \ No newline at end of file
diff --git a/test/uscxml/promela/test-event-source-auto.scxml b/test/uscxml/promela/test-event-source-auto.scxml
new file mode 100644
index 0000000..ef0e26c
--- /dev/null
+++ b/test/uscxml/promela/test-event-source-auto.scxml
@@ -0,0 +1,22 @@
+<scxml datamodel="promela">
+ <!--
+ #promela-event-source-auto
+ { e1: [
+ { state: ['s0'],
+ data: { foo: 'some string' }}
+ { state: ['s0'],
+ data: { bar: 12 }}
+ { state: ['s1'],
+ data: { foo: 'some other string' }}
+ ]}
+ -->
+
+ <state id="s0">
+ <transition event="e1" cond="_event.data.foo == 'some string'" />
+ <transition event="e1" cond="_event.data.bar == 12" />
+ </state>
+ <state id="s1">
+ <transition event="e1" cond="_event.data.foo == 'some other string'" />
+ <transition event="e1" cond="_event.data.bar == 12" />
+ </state>
+</scxml> \ No newline at end of file
diff --git a/test/w3c/analyze_tests.pl b/test/w3c/analyze_tests.pl
index 111db5a..a14c129 100755
--- a/test/w3c/analyze_tests.pl
+++ b/test/w3c/analyze_tests.pl
@@ -119,17 +119,22 @@ while ($block = <FILE>) {
/
Approximate\sComplexity:\s(\d+)\n
Approximate\sActive\sComplexity:\s(\d+)\n
- Actual\sComplexity:\s(\d+)\n
- Actual\sActive\sComplexity:\s(\d+)\n
- Internal\sQueue:\s(\d+)\n
- External\sQueue:\s(\d+)\n
/x ) {
$test->{$currTest}->{'flat'}->{'cmplx'}->{'appr'} = $1;
$test->{$currTest}->{'flat'}->{'cmplx'}->{'apprActv'} = $2;
- $test->{$currTest}->{'flat'}->{'cmplx'}->{'actual'} = $3;
- $test->{$currTest}->{'flat'}->{'cmplx'}->{'actualActv'} = $4;
- $test->{$currTest}->{'flat'}->{'queue'}->{'internal'} = $5;
- $test->{$currTest}->{'flat'}->{'queue'}->{'external'} = $6;
+
+ if ($block =~
+ /
+ Actual\sComplexity:\s(\d+)\n
+ Actual\sActive\sComplexity:\s(\d+)\n
+ Internal\sQueue:\s(\d+)\n
+ External\sQueue:\s(\d+)\n
+ /x ) {
+ $test->{$currTest}->{'flat'}->{'cmplx'}->{'actual'} = $1;
+ $test->{$currTest}->{'flat'}->{'cmplx'}->{'actualActv'} = $2;
+ $test->{$currTest}->{'flat'}->{'queue'}->{'internal'} = $3;
+ $test->{$currTest}->{'flat'}->{'queue'}->{'external'} = $4;
+ }
if ($block =~ /State-vector (\d+) byte, depth reached (\d+), errors: (\d+)/) {
$test->{$currTest}->{'pml'}->{'states'}->{'stateSize'} = $1;
diff --git a/test/w3c/run_promela_test.cmake b/test/w3c/run_promela_test.cmake
index e19148f..e6d2418 100644
--- a/test/w3c/run_promela_test.cmake
+++ b/test/w3c/run_promela_test.cmake
@@ -3,7 +3,10 @@
get_filename_component(TEST_FILE_NAME ${TESTFILE} NAME)
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTDIR})
-execute_process(COMMAND ${USCXML_TRANSFORM_BIN} -i ${TESTFILE} -o ${OUTDIR}/${TEST_FILE_NAME}.pml RESULT_VARIABLE CMD_RESULT)
+set(ENV{USCXML_PROMELA_TRANSITION_TRACE} "TRUE")
+set(ENV{USCXML_PROMELA_TRANSITION_DEBUG} "TRUE")
+
+execute_process(COMMAND ${USCXML_TRANSFORM_BIN} -tpml -i ${TESTFILE} -o ${OUTDIR}/${TEST_FILE_NAME}.pml RESULT_VARIABLE CMD_RESULT)
if(CMD_RESULT)
message(FATAL_ERROR "Error running ${USCXML_TRANSFORM_BIN}: ${CMD_RESULT}")
endif()