From 72d7986b5ebdc40b8335dd76e74e1d373d8b4a23 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 5 Feb 2025 00:25:02 +0100 Subject: [PATCH] Invocation: build a DSL to verify connectivity (see #1377) This was a lot of intricate technical work, and is now verified in-depth, covering all possible cases. __We can now__ * build Nodes * verify in detail correct connectivity * read Node-IDs and processing specifications * maintain a symbolic spec for the arguments of a Port (and beyond that, we can also **invoke nodes**, which remains to be formally verified) --- src/lib/several.hpp | 4 + src/steam/engine/proc-node.cpp | 132 +++++++- src/steam/engine/proc-node.hpp | 57 +++- tests/46node.tests | 2 +- tests/core/steam/engine/node-builder-test.cpp | 3 + tests/core/steam/engine/node-link-test.cpp | 17 +- tests/core/steam/engine/node-meta-test.cpp | 202 ++++++++++++- wiki/thinkPad.ichthyo.mm | 282 +++++++++++------- 8 files changed, 564 insertions(+), 135 deletions(-) diff --git a/src/lib/several.hpp b/src/lib/several.hpp index 10d079dd0..f517168d2 100644 --- a/src/lib/several.hpp +++ b/src/lib/several.hpp @@ -227,6 +227,10 @@ namespace lib { friend auto begin (Several const& svl) { return svl.begin();} friend auto end (Several const& svl) { return svl.end(); } + using value_type = typename meta::RefTraits::Value; + using reference = typename meta::RefTraits::Reference; + using const_reference = value_type const&; + protected: size_t diff --git a/src/steam/engine/proc-node.cpp b/src/steam/engine/proc-node.cpp index 300bb9d7a..4d76406d2 100644 --- a/src/steam/engine/proc-node.cpp +++ b/src/steam/engine/proc-node.cpp @@ -13,9 +13,11 @@ /** @file proc-node.cpp - ** Translation unit to hold the actual implementation of node processing operations. - ** - ** @todo WIP-WIP-WIP 6/2024 not clear yet what goes here and what goes into turnout-system.cpp + ** Translation unit for implementation details regarding node-IDs and verification. + ** @remark \ref ProcNode itself is a shell to provide a node-ID and a high-level API + ** for Render Node invocation. The actual implementation of processing functionality + ** is located within the [Turnout](\ref turnout.hpp) and the individual »weaving patterns« + ** embedded therein. */ @@ -247,7 +249,7 @@ namespace engine { ProcID::genProcSpec() const { std::ostringstream buffer; - buffer << nodeName_ + buffer << genNodeSymbol() << genQualifier() << argLists_; return buffer.str(); @@ -501,17 +503,135 @@ namespace engine { { auto& leadPorts = srcPorts(); return input < leadPorts.size() - and isSameObject (leadPorts[input].get(), tarPort); + and leadPorts[input] == tarPort; } bool PortDiagnostic::verify_connected (Port& tarPort) { for (Port& port : srcPorts()) - if (isSameObject (port, tarPort)) + if (port == tarPort) return true; return false; } + /** + * @remark _ConCheck provides a fluent DSL notation to verify node connectivity. + * This is achieved by first collecting some counterparts and index specifications + * for the kind of connection to validate. Each qualifier just sets a parameter and + * returns the _ConCheck object by move. The final result is retrieved by this bool + * conversion — which in fact has to implement a collection of different evaluations. + * The proper kind of evaluation will be picked based on the actual arguments given; + * the more are present, the more constricted the evaluation becomes. The selection + * of the evaluation to take is thus ordered in reverse order, starting with the + * most constricted cases. Three different kinds of link validations are provided + * - compare two fully qualified Port objects for identity (same object) + * - check if a given Port object is present in a collection of Ports + * - exhaustive search for a match in the cross product of two Port collections. + * The last case is what allows to perform a high-level connectivity test between + * two nodes, which are considered as connected if any link is found. + * @see NodeMeta_test::verify_ID_connectivity() + */ + _ConCheck::operator bool() + { + auto validPort = [this](uint idx) { return idx < anchor.ports().size(); }; + auto validLead = [this](uint idx) { return idx < anchor.leads().size(); }; + auto validSrc = [this](uint pNo + ,uint sNo) { return sNo < anchor.watchPort(pNo).srcPorts().size(); }; + auto validSrcP = [this](ProcNode& lead + ,uint idx) { return idx < watch(lead).ports().size(); }; + + auto find_link = [](auto& seq1, auto& seq2) + { + return explore(seq1) + .transform([&](auto& elm){ return contains (seq2, *elm); }) + .has_any(); + }; + + // Determine case to handle, + // starting with the most constricted... + if (portNo and srcNo and srcNode and srcPNo) + return validPort(*portNo) // is_linked(node).port(portNo).asSrc(srcNo).to(srcNode).atPort(srcPNo) + and validSrc (*portNo, *srcNo) + and validSrcP(*srcNode,*srcPNo) + and anchor.watchPort(*portNo).srcPorts()[*srcNo] + == watch(*srcNode).ports()[*srcPNo]; + else + if (portNo and srcNo and leadNo and srcPNo) + return validPort(*portNo) // is_linked(node).port(portNo).asSrc(srcNo).toLead(leadNo).atPort(srcPNo) + and validSrc (*portNo,*srcNo) + and validLead(*leadNo) + and validSrcP(anchor.leads()[*leadNo], *srcPNo) + and anchor.watchPort(*portNo).srcPorts()[*srcNo] + == anchor.watchLead(*leadNo).ports()[*srcPNo]; + else + if (portNo and srcNo and srcPort) + return validPort(*portNo) // is_linked(node).port(portNo).asSrc(srcNo).to(srcPort) + and validSrc (*portNo,*srcNo) + and anchor.watchPort(*portNo).srcPorts()[*srcNo] + == *srcPort; + else + if (portNo and srcNo and srcNode) + return validPort(*portNo) // is_linked(node).port(portNo).asSrc(srcNo).to(srcNode) + and validSrc (*portNo,*srcNo) + and contains (watch(*srcNode).ports() + ,anchor.watchPort(*portNo).srcPorts()[*srcNo]); + else + if (portNo and srcNo and leadNo) + return validPort(*portNo) // is_linked(node).port(portNo).asSrc(srcNo).toLead(leadNo) + and validSrc (*portNo,*srcNo) + and validLead(*leadNo) + and contains (anchor.watchLead(*leadNo).ports() + ,anchor.watchPort(*portNo).srcPorts()[*srcNo]); + else + if (portNo and srcNo) + return validPort(*portNo) // is_linked(node).port(portNo).asSrc(srcNo) + and validSrc (*portNo,*srcNo); + else + if (portNo and srcNode and srcPNo) + return validPort(*portNo) // is_linked(node).port(portNo).to(srcNode).atPort(srcPNo) + and validSrcP(*srcNode,*srcPNo) + and contains (anchor.watchPort(*portNo).srcPorts() + ,watch(*srcNode).ports()[*srcPNo]); + else + if (portNo and leadNo and srcPNo) + return validPort(*portNo) // is_linked(node).port(portNo).toLead(leadNo).atPort(srcPNo) + and validLead(*leadNo) + and validSrcP(anchor.leads()[*leadNo], *srcPNo) + and contains (anchor.watchPort(*portNo).srcPorts() + ,anchor.watchLead(*leadNo).ports()[*srcPNo]); + else + if (portNo and srcPort) + return validPort(*portNo) // is_linked(node).port(portNo).to(srcPort) + and contains (anchor.watchPort(*portNo).srcPorts() + ,*srcPort); + else + if (portNo and srcNode) + return validPort(*portNo) // is_linked(node).port(portNo).to(srcNode) + and find_link(watch(*srcNode).ports() + ,anchor.watchPort(*portNo).srcPorts()); + else + if (portNo and leadNo) + return validPort(*portNo) // is_linked(node).port(portNo).toLead(leadNo) + and validLead(*leadNo) + and find_link(anchor.watchLead(*leadNo).ports() + ,anchor.watchPort(*portNo).srcPorts()); + else + if (portNo) + return validPort(*portNo) // is_linked(node).port(portNo) + and not anchor.watchPort(*portNo).isSrc(); + else + if (srcNode and leadNo) + return validLead(*leadNo) // is_linked(node).to(srcNode).asLead(leadNo) + and anchor.leads()[*leadNo] + == *srcNode; + else + if (srcNode) + return contains (anchor.leads() // is_linked(node).to(srcNode) + ,*srcNode); + return false; + } + + }} // namespace steam::engine diff --git a/src/steam/engine/proc-node.hpp b/src/steam/engine/proc-node.hpp index 3e848d406..5eda779ba 100644 --- a/src/steam/engine/proc-node.hpp +++ b/src/steam/engine/proc-node.hpp @@ -84,7 +84,7 @@ ** @remark A future extension to this scheme is conceivable, where common processing pipelines ** are pre-compiled in entirety, possibly combined with hardware acceleration. ** - ** @todo WIP-WIP 12/2024 Node-Invocation is reworked from ground up for the »Playback Vertical Slice« + ** @todo WIP 2/2025 Node-Invocation is reworked from ground up for the »Playback Vertical Slice« ** ** @see turnout.hpp ** @see turnout-system.hpp @@ -99,11 +99,8 @@ #include "lib/error.hpp" #include "lib/nocopy.hpp" #include "lib/hash-value.h" -//#include "steam/asset/proc.hpp" -//#include "steam/mobject/parameter.hpp" #include "steam/engine/buffhandle.hpp" #include "steam/engine/turnout-system.hpp" -#include "lib/ref-array.hpp" /////////////////////OOO phase out #include "lib/format-string.hpp" #include "lib/several.hpp" @@ -118,6 +115,8 @@ namespace engine { using std::move; using std::string; + using std::nullopt; + using std::optional; using lib::HashVal; using util::_Fmt; @@ -145,6 +144,10 @@ namespace engine { virtual BuffHandle weave (TurnoutSystem&, OptionalBuff =std::nullopt) =0; ProcID& procID; + + /// Port has reference semantics: all instances are distinct + friend bool operator== (Port const& pl, Port const& pr){ return & pl == & pr;} + friend bool operator!= (Port const& pl, Port const& pr){ return not (pl == pr); } }; using PortRef = std::reference_wrapper; @@ -232,6 +235,10 @@ namespace engine { return wiring_.ports[portIdx]; } + /// ProcNode has reference semantics: all instances are distinct + friend bool operator== (ProcNode const& nl, ProcNode const& nr){ return & nl == & nr;} + friend bool operator!= (ProcNode const& nl, ProcNode const& nr){ return not (nl == nr); } + /*************************************************************//** * Engine Core operation: render and pull output from this node. @@ -333,5 +340,47 @@ namespace engine { } + + + /** Helper for connectivity-checks in tests */ + class _ConCheck + : util::MoveOnly + { + ProcNodeDiagnostic anchor; + ProcNode* srcNode{nullptr}; + Port* srcPort{nullptr}; + optional leadNo{nullopt}; + optional portNo{nullopt}; + optional srcNo {nullopt}; + optional srcPNo{nullopt}; + + public: + _ConCheck (ProcNode& n) + : anchor{watch(n)} + { } + + /** implement decision logic based on context given. */ + operator bool(); + + _ConCheck to (ProcNode& n) { srcNode = &n; return move(*this); } + _ConCheck to (Port& p) { srcPort = &p; return move(*this); } + _ConCheck asLead (uint idx){ leadNo = idx; return move(*this); } + _ConCheck toLead (uint idx){ leadNo = idx; return move(*this); } + _ConCheck asSrc (uint idx){ srcNo = idx; return move(*this); } + _ConCheck port (uint idx){ portNo = idx; return move(*this); } + _ConCheck atPort (uint idx){ srcPNo = idx; return move(*this); } + }; + + /** + * start a DSL expression to verify node connectivity. + * @see NodeMeta_test::verify_ID_connectivity() + */ + inline _ConCheck + is_linked (ProcNode& n) + { + return _ConCheck{n}; + } + + }} // namespace steam::engine #endif /*STEAM_ENGINE_PROC_NODE_H*/ diff --git a/tests/46node.tests b/tests/46node.tests index 0f6d53ab6..869f76b55 100644 --- a/tests/46node.tests +++ b/tests/46node.tests @@ -22,7 +22,7 @@ PLANNED "Proc Node connectivity" NodeLink_test < -//using std::string; - namespace steam { namespace engine{ namespace test { using std::abs; -// using util::join; /***************************************************************//** * @test Render node metadata and hash identity keys. + * @todo 2/2025 hash computation is not yet specified... */ class NodeMeta_test : public Test { @@ -77,8 +73,8 @@ namespace test { CHECK (p2.genProcName() == "N2.+"_expect ); // domain omitted, qualifier joined with '.' CHECK (p2.genQualifier() == ".+"_expect ); // qualifier includes leading '.' CHECK (p3.genProcName() == "N3"_expect ); - CHECK (p2.genProcSpec() == "U:N2.+(a1,a2)"_expect ); - CHECK (p3.genProcSpec() == "O:N3(in/3)(o1,o2/2)"_expect ); + CHECK (p2.genProcSpec() == "N2.+(a1,a2)"_expect ); + CHECK (p3.genProcSpec() == "N3(in/3)(o1,o2/2)"_expect ); ProcID::ArgModel arg1 = p1.genArgModel(); ProcID::ArgModel arg2 = p2.genArgModel(); @@ -109,13 +105,31 @@ namespace test { } - /** @test TODO aspects of node definition relevant for the ProcID - * @todo WIP 2/25 🔁 define ⟶ 🔁 implement + + /** @test validate the interplay of node connectivity + * with reported properties at the ProcID and + * demonstrate tools to check connectivity. + * - Build a 3-node network with dummy operations, + * which however are built to mimic the very common + * situation where two sources are mixed + * - at exit side, three different »flavours« can be + * produced, which implies that there are three Ports. + * - the source at the »A-side« provided only two flavours, + * and thus an explicit wiring has to be made for the + * A-side connection of the third chain + * - In real usage, the node specification strings will be + * provided from the Media-Lib adapter plug-in. Here it is + * hard wired, and defined in a way to reflect structure. + * - various ways to drill-down into the structure are explored + * by verifying the ProcID specification visible at each point. + * - then the tools for verifying connectivity are demonstrated + * and covered with relevant positive and negative combinations. + * @todo 2/25 🔁 define ⟶ ✔ implement */ void verify_ID_connectivity() { - // This operation emulates a data source + // These operations emulate data sources auto src_opA = [](int param, int* res) { *res = param; }; auto src_opB = [](ulong param, ulong* res){ *res = param; }; @@ -214,7 +228,8 @@ namespace test { CHECK (watch(nM).watchPort(2).watchLead(1).getProcName() == "srcB.c"_expect ); CHECK (watch(nM).watchPort(2).watchLead(1).getProcSpec() == "srcB.c(ulong)"_expect); CHECK (watch(nM).watchPort(2).watchLead(1).isSrc() == true ); // the lead port itself is a source - CHECK (watch(nM).watchPort(2).watchLead(1).srcPorts().size() == 0 ); + CHECK (watch(nM).watchPort(2).watchLead(1).srcPorts().size() == 0 ); // ...and thus has an empty source-port-collection + // Helper predicate to verify connectedness to a specific Port given by reference CHECK (watch(nM).watchPort(2).verify_connected( watch(nA).ports()[0]) == false ); @@ -227,8 +242,169 @@ namespace test { CHECK (watch(nM).watchPort(2).verify_connected(0, watch(nB).ports()[2]) == false ); CHECK (watch(nM).watchPort(2).verify_connected(1, watch(nA).ports()[1]) == false ); // Node-nM.port#2 doesn't connect via source#1 to Node-nA.port#1 - ///////////////////////////////////////////////////////TODO WIP - UNIMPLEMENTED ("verify connectivity"); + + //__________________________________ + // Inspect Node and Port connectivity + + // High-level case: connections between nodes + CHECK (is_linked(nM).to(nA) == true ); + CHECK (is_linked(nM).to(nB) == true ); + CHECK (is_linked(nA).to(nB) == false); + + // additionally qualify the index position + // of the source node in the sequence of »Lead nodes« + CHECK (is_linked(nM).to(nA).asLead(0) == true ); // Node-nA is Lead-#0 + CHECK (is_linked(nM).to(nA).asLead(1) == false); + CHECK (is_linked(nM).to(nB).asLead(0) == false); + CHECK (is_linked(nM).to(nB).asLead(1) == true ); // Node-nB is Lead-#1 + + // Check if a specific Port is connected to a source node + CHECK (is_linked(nM).port(0).to(nA) == true ); + CHECK (is_linked(nM).port(0).to(nB) == true ); + CHECK (is_linked(nM).port(0).to(nM) == false); // never connected to itself + + // Similar, but now pick the source node from the »Leads« + CHECK (is_linked(nM).port(0).toLead(0) == true ); + CHECK (is_linked(nM).port(0).toLead(1) == true ); + CHECK (is_linked(nA).port(0).toLead(0) == false); // nA is a source node and thus has no further source-connections + + // Verify detailed port-to-port connectivity + CHECK (is_linked(nM).port(0).to(watch(nA).ports()[0]) == true ); // Node-nM connected within Port-0 to Port-0 of Node-nA + CHECK (is_linked(nM).port(0).to(watch(nA).ports()[1]) == false); // ......but not connected to Port-1 of Node-nA + CHECK (is_linked(nM).port(0).to(watch(nB).ports()[0]) == true ); + CHECK (is_linked(nM).port(0).to(watch(nB).ports()[1]) == false); + CHECK (is_linked(nM).port(0).to(watch(nB).ports()[2]) == false); + CHECK (is_linked(nM).port(2).to(watch(nA).ports()[0]) == false); + CHECK (is_linked(nM).port(2).to(watch(nA).ports()[1]) == true ); // this is the connection routed from port-2 to Node-nA, Port-1 + CHECK (is_linked(nM).port(2).to(watch(nB).ports()[0]) == false); + CHECK (is_linked(nM).port(2).to(watch(nB).ports()[1]) == false); + CHECK (is_linked(nM).port(2).to(watch(nB).ports()[2]) == true ); + CHECK (is_linked(nM).port(2).to(watch(nM).ports()[2]) == false); // a nonsensical check, nodes are never connected to themselves + + CHECK (is_linked(nM).port(0).to(nA).atPort(0) == true ); + CHECK (is_linked(nM).port(0).to(nA).atPort(1) == false); + CHECK (is_linked(nM).port(0).to(nB).atPort(0) == true ); + CHECK (is_linked(nM).port(0).to(nB).atPort(1) == false); + CHECK (is_linked(nM).port(0).to(nB).atPort(2) == false); + CHECK (is_linked(nM).port(2).to(nA).atPort(0) == false); + CHECK (is_linked(nM).port(2).to(nA).atPort(1) == true ); + CHECK (is_linked(nM).port(2).to(nB).atPort(0) == false); + CHECK (is_linked(nM).port(2).to(nB).atPort(1) == false); + CHECK (is_linked(nM).port(2).to(nB).atPort(2) == true ); + CHECK (is_linked(nM).port(2).to(nM).atPort(2) == false); + + CHECK (is_linked(nM).port(0).toLead(0).atPort(0) == true ); + CHECK (is_linked(nM).port(0).toLead(0).atPort(1) == false); + CHECK (is_linked(nM).port(0).toLead(1).atPort(0) == true ); + CHECK (is_linked(nM).port(0).toLead(1).atPort(1) == false); + CHECK (is_linked(nM).port(0).toLead(1).atPort(2) == false); + CHECK (is_linked(nM).port(2).toLead(0).atPort(0) == false); + CHECK (is_linked(nM).port(2).toLead(0).atPort(1) == true ); + CHECK (is_linked(nM).port(2).toLead(1).atPort(0) == false); + CHECK (is_linked(nM).port(2).toLead(1).atPort(1) == false); + CHECK (is_linked(nM).port(2).toLead(1).atPort(2) == true ); + + // additionally also qualify the «source slot« + // at which the connection is used as input for the processing-function + CHECK (is_linked(nM).port(0).asSrc(0).to(nA) == true ); // Node-nM, Port-0 uses as source-slot-0 a connection to Node-nA + CHECK (is_linked(nM).port(0).asSrc(1).to(nA) == false ); + CHECK (is_linked(nM).port(0).asSrc(0).to(nB) == false ); + CHECK (is_linked(nM).port(0).asSrc(1).to(nB) == true ); + CHECK (is_linked(nM).port(2).asSrc(0).to(nA) == true ); + CHECK (is_linked(nM).port(2).asSrc(1).to(nA) == false ); + CHECK (is_linked(nM).port(2).asSrc(0).to(nB) == false ); + CHECK (is_linked(nM).port(2).asSrc(1).to(nB) == true ); + CHECK (is_linked(nM).port(2).asSrc(1).to(nM) == false ); // never connected to itself + + CHECK (is_linked(nM).port(0).asSrc(0).toLead(0) == true ); // Lead-#0 is Node-nA + CHECK (is_linked(nM).port(0).asSrc(1).toLead(0) == false ); + CHECK (is_linked(nM).port(0).asSrc(0).toLead(1) == false ); + CHECK (is_linked(nM).port(0).asSrc(1).toLead(1) == true ); + CHECK (is_linked(nM).port(2).asSrc(0).toLead(0) == true ); + CHECK (is_linked(nM).port(2).asSrc(1).toLead(0) == false ); + CHECK (is_linked(nM).port(2).asSrc(0).toLead(1) == false ); + CHECK (is_linked(nM).port(2).asSrc(1).toLead(1) == true ); + + // Again detailed port-to-port connections, this time limited by «source slot» + CHECK (is_linked(nM).port(0).asSrc(0).to(watch(nA).ports()[0]) == true ); // Node-nA, Port-0 connects as src-#0 to node-nA at Port-0 + CHECK (is_linked(nM).port(0).asSrc(0).to(watch(nA).ports()[1]) == false); // ...and can thus not be connected to any other Port there + CHECK (is_linked(nM).port(0).asSrc(1).to(watch(nA).ports()[0]) == false); + CHECK (is_linked(nM).port(0).asSrc(1).to(watch(nA).ports()[1]) == false); + CHECK (is_linked(nM).port(0).asSrc(0).to(watch(nB).ports()[0]) == false); + CHECK (is_linked(nM).port(0).asSrc(0).to(watch(nB).ports()[1]) == false); + CHECK (is_linked(nM).port(0).asSrc(0).to(watch(nB).ports()[2]) == false); + CHECK (is_linked(nM).port(0).asSrc(1).to(watch(nB).ports()[0]) == true ); + CHECK (is_linked(nM).port(0).asSrc(1).to(watch(nB).ports()[1]) == false); + CHECK (is_linked(nM).port(0).asSrc(1).to(watch(nB).ports()[2]) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(watch(nA).ports()[0]) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(watch(nA).ports()[1]) == true ); + CHECK (is_linked(nM).port(2).asSrc(1).to(watch(nA).ports()[0]) == false); + CHECK (is_linked(nM).port(2).asSrc(1).to(watch(nA).ports()[1]) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(watch(nB).ports()[0]) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(watch(nB).ports()[1]) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(watch(nB).ports()[2]) == false); + CHECK (is_linked(nM).port(2).asSrc(1).to(watch(nB).ports()[0]) == false); + CHECK (is_linked(nM).port(2).asSrc(1).to(watch(nB).ports()[1]) == false); + CHECK (is_linked(nM).port(2).asSrc(1).to(watch(nB).ports()[2]) == true ); + CHECK (is_linked(nM).port(2).asSrc(1).to(watch(nM).ports()[2]) == false); // never connected to itself + + CHECK (is_linked(nM).port(0).asSrc(0).to(nA).atPort(0) == true ); + CHECK (is_linked(nM).port(0).asSrc(0).to(nA).atPort(1) == false); + CHECK (is_linked(nM).port(0).asSrc(1).to(nA).atPort(0) == false); + CHECK (is_linked(nM).port(0).asSrc(1).to(nA).atPort(1) == false); + CHECK (is_linked(nM).port(0).asSrc(0).to(nB).atPort(0) == false); + CHECK (is_linked(nM).port(0).asSrc(0).to(nB).atPort(1) == false); + CHECK (is_linked(nM).port(0).asSrc(0).to(nB).atPort(2) == false); + CHECK (is_linked(nM).port(0).asSrc(1).to(nB).atPort(0) == true ); + CHECK (is_linked(nM).port(0).asSrc(1).to(nB).atPort(1) == false); + CHECK (is_linked(nM).port(0).asSrc(1).to(nB).atPort(2) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(nA).atPort(0) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(nA).atPort(1) == true ); + CHECK (is_linked(nM).port(2).asSrc(1).to(nA).atPort(0) == false); + CHECK (is_linked(nM).port(2).asSrc(1).to(nA).atPort(1) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(nB).atPort(0) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(nB).atPort(1) == false); + CHECK (is_linked(nM).port(2).asSrc(0).to(nB).atPort(2) == false); + CHECK (is_linked(nM).port(2).asSrc(1).to(nB).atPort(0) == false); + CHECK (is_linked(nM).port(2).asSrc(1).to(nB).atPort(1) == false); + CHECK (is_linked(nM).port(2).asSrc(1).to(nB).atPort(2) == true ); + CHECK (is_linked(nM).port(2).asSrc(1).to(nM).atPort(2) == false); + + CHECK (is_linked(nM).port(0).asSrc(0).toLead(0).atPort(0) == true ); + CHECK (is_linked(nM).port(0).asSrc(0).toLead(0).atPort(1) == false); + CHECK (is_linked(nM).port(0).asSrc(1).toLead(0).atPort(0) == false); + CHECK (is_linked(nM).port(0).asSrc(1).toLead(0).atPort(1) == false); + CHECK (is_linked(nM).port(0).asSrc(0).toLead(1).atPort(0) == false); + CHECK (is_linked(nM).port(0).asSrc(0).toLead(1).atPort(1) == false); + CHECK (is_linked(nM).port(0).asSrc(0).toLead(1).atPort(2) == false); + CHECK (is_linked(nM).port(0).asSrc(1).toLead(1).atPort(0) == true ); + CHECK (is_linked(nM).port(0).asSrc(1).toLead(1).atPort(1) == false); + CHECK (is_linked(nM).port(0).asSrc(1).toLead(1).atPort(2) == false); + CHECK (is_linked(nM).port(2).asSrc(0).toLead(0).atPort(0) == false); + CHECK (is_linked(nM).port(2).asSrc(0).toLead(0).atPort(1) == true ); + CHECK (is_linked(nM).port(2).asSrc(1).toLead(0).atPort(0) == false); + CHECK (is_linked(nM).port(2).asSrc(1).toLead(0).atPort(1) == false); + CHECK (is_linked(nM).port(2).asSrc(0).toLead(1).atPort(0) == false); + CHECK (is_linked(nM).port(2).asSrc(0).toLead(1).atPort(1) == false); + CHECK (is_linked(nM).port(2).asSrc(0).toLead(1).atPort(2) == false); + CHECK (is_linked(nM).port(2).asSrc(1).toLead(1).atPort(0) == false); + CHECK (is_linked(nM).port(2).asSrc(1).toLead(1).atPort(1) == false); + CHECK (is_linked(nM).port(2).asSrc(1).toLead(1).atPort(2) == true ); + + // Some fallback-cases tested when given an incomplete chain: + // Specifying only a Port an source-slot just checks for valid index + CHECK (is_linked(nM).port(0).asSrc(0) == true ); + CHECK (is_linked(nM).port(0).asSrc(1) == true ); + CHECK (is_linked(nM).port(0).asSrc(2) == false); // has only 2 source-slots + CHECK (is_linked(nA).port(0).asSrc(0) == false); // node-nA is a source-node and thus has no source-slot at all + + // A port allone is checked for any incoming connections + CHECK (is_linked(nM).port(0) == true ); + CHECK (is_linked(nM).port(1) == true ); + CHECK (is_linked(nM).port(2) == true ); + CHECK (is_linked(nM).port(3) == false); // node-nM has only 3 ports, i.e. index [0...2] + CHECK (is_linked(nA).port(0) == false); // node-nA is a source node and thus no port can have an incoming connection + CHECK (is_linked(nB).port(0) == false); // same for node-nB } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 145cbcbba..15b777b2f 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -101858,7 +101858,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -101877,7 +101877,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -101891,7 +101891,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -101923,12 +101923,25 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + - - + + - + + + + + + + + + + + @@ -101936,33 +101949,75 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - - - - - + + + + + + + - - - - - + + + - - + + + + + + + + + + + + + - + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + - + @@ -101973,14 +102028,14 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - + + + - + - + @@ -102317,11 +102372,15 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - + + + + + @@ -102450,17 +102509,17 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + - + - + @@ -102577,14 +102636,15 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + + - - - + + + @@ -102601,8 +102661,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + + @@ -102653,7 +102714,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -102685,23 +102746,24 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - - + + + + - + + - + @@ -102718,12 +102780,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - - + + @@ -102771,8 +102833,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -102839,8 +102902,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -102989,8 +103052,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -103249,8 +103312,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -104934,10 +104997,11 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - + + @@ -104977,8 +105041,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -105146,7 +105210,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -105210,11 +105274,11 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - + @@ -105264,8 +105328,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -105303,8 +105367,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -105367,8 +105431,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -105394,7 +105458,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -105954,7 +106018,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -106388,9 +106452,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

ich glaube nicht, daß es sinnvoll ist, eine Attribut-Map als Ganzes zu de-duplizieren. Empirisch entscheiden können wir das aber leider erst viel später. Ja aber, was machen wir dann bloß? Wenn-wäre-hätte?? Die Node-Storage muß unbedingt klein gehalten werden. Kann aber derzeit überhaupt nicht entscheiden, ob die Vorraussetzungen für eine persistente Datenstruktur (im Sinne der funktionalen Programmierung) gegeben sind.... @@ -106416,8 +106478,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -106484,10 +106546,10 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - - + + + + @@ -106502,8 +106564,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -106521,9 +106584,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

...wird eigentlich erst für die Hash / Cache-Key-Berechnung relevant; dann aber wären erst einige kniffelige technische Probleme zu lösen, die ich im Moment nicht recht bestimmen kann... @@ -106533,16 +106594,21 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - - - + + + + + - + + + + + - - + + + @@ -107242,8 +107308,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + + @@ -107262,8 +107329,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -107274,16 +107341,19 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - - + + + + - - + + - - + + + + +