-- File: [Cherry]<Thyme>System>CSIM01>spAnalysis.mesa
-- Last editted: 
-- Wilhelm February 24, 1982  3:08 PM, reformated by Barth and stored under
--   [Cherry]<Barth>Thyme>1.97> .
DIRECTORY spGlobals;
spAnalysis:  PROGRAM
  IMPORTS spGlobals
  EXPORTS spGlobals =
  BEGIN
    OPEN spGlobals;

    checkNode:  PROCEDURE[n:  nodePtr] =
      BEGIN
        IF n↑.integrate OR n = gndNode OR n↑.curPtr # NIL THEN RETURN
        ELSE
          BEGIN
            n↑.integrate ← TRUE;
            n↑.nextIntNode ← intNodeList;
            intNodeList ← n
          END
      END;

    assignIntNodes:  PROCEDURE =
      BEGIN
        caps:  capacitorPtr ← capacitorList;

        UNTIL caps = NIL DO
          checkNode[caps↑.posNode];
          checkNode[caps↑.negNode];
          caps ← caps↑.nextCapacitor
        ENDLOOP
      END;

    allocateCurrent:  PROCEDURE[v:  vSourcePtr] =
      BEGIN
        pCount, nCount:  CARDINAL ← 0;
        pAvail, nAvail:  BOOLEAN ← FALSE;
        pNode:  nodePtr ← v↑.posNode;
        nNode:  nodePtr ← v↑.negNode;
        blP:  branchLinkPtr;

        IF pNode = gndNode THEN
          IF nNode↑.curPtr # v THEN ErrorAtNB[490, NIL, v]
          ELSE NULL
        ELSE
          IF nNode = gndNode THEN
            IF pNode↑.curPtr # v THEN ErrorAtNB[490, NIL, v]
            ELSE NULL
          ELSE
            BEGIN
              pAvail ← pNode↑.curPtr = NIL;
              nAvail ← nNode↑.curPtr = NIL;
              IF ~pAvail AND ~nAvail THEN ErrorAtNB[490, NIL, v]
              ELSE
                IF pAvail AND ~nAvail THEN pNode↑.curPtr ← v
                ELSE
                  IF ~pAvail AND nAvail THEN nNode↑.curPtr ← v
                  ELSE
                    BEGIN
                      blP ← pNode↑.branches;
                      UNTIL blP = NIL DO
                        IF blP↑.branch↑.eType = vSource THEN
                          pCount ← pCount + 1;
                        blP ← blP↑.nextLink
                      ENDLOOP;
                      blP ← nNode↑.branches;
                      UNTIL blP = NIL DO
                        IF blP↑.branch↑.eType = vSource THEN
                          nCount ← nCount + 1;
                        blP ← blP↑.nextLink
                      ENDLOOP;
                      IF pCount < nCount THEN pNode↑.curPtr ← v
                      ELSE nNode↑.curPtr ← v
                    END
            END 
      END;

    assignCurrents:  PROCEDURE =
      BEGIN
        branchP:  branchPtr;
        blP:  branchLinkPtr;
        vP:  vSourcePtr;

      -- Check connections to ground node first.

        blP ← gndNode↑.branches;
        UNTIL blP = NIL DO
          branchP ← blP↑.branch;
          WITH v:  branchP↑ SELECT FROM
            vSource =>
              IF v.posNode # gndNode THEN v.posNode↑.curPtr ← @v
              ELSE v.negNode↑.curPtr ← @v
          ENDCASE;
          blP ← blP↑.nextLink
        ENDLOOP;

      -- Do remaining branches.

        vP ← vSourceList;
        UNTIL vP = NIL DO
          allocateCurrent[vP];
          vP ← vP↑.nextvSource
        ENDLOOP;
      END;

    dcPath:  PROCEDURE[n:  nodePtr] =
      BEGIN
        bLink:  branchLinkPtr;
        b:  branchPtr;

        IF ~n↑.marked THEN
          BEGIN
            n↑.marked ← TRUE;
            bLink ← n↑.branches;
            UNTIL bLink = NIL DO
              b ← bLink↑.branch;
              -- IF b↑.eType # capacitor AND b↑.eType # iSource THEN
              IF b↑.eType # capacitor THEN
                IF b↑.posNode = n THEN dcPath[b↑.negNode]
                ELSE dcPath[b↑.posNode];
              bLink ← bLink↑.nextLink
            ENDLOOP
          END
      END;

    forceDcPaths:  PROCEDURE =
      BEGIN
        n:  nodePtr;
        dcPath[gndNode];
        n ← nodeList;
        UNTIL n = NIL DO
          IF ~n↑.marked THEN ErrorAtNB[401, n, NIL];
          n ← n↑.nextNode
        ENDLOOP
      END;

    vsXPath:  PROCEDURE[n:  nodePtr, skip:  branchPtr, type:  elements] =
      BEGIN
        bLink:  branchLinkPtr;
        b:  branchPtr;

        IF ~n↑.marked THEN
          BEGIN
            n↑.marked ← TRUE;
            bLink ← n↑.branches;
            UNTIL bLink = NIL DO
              b ← bLink↑.branch;
              IF b # skip THEN
                IF b↑.eType = vSource OR b↑.eType = type THEN
                  IF b↑.posNode = n THEN vsXPath[b↑.negNode, NIL, type]
                  ELSE vsXPath[b↑.posNode, NIL, type];
              bLink ← bLink↑.nextLink
            ENDLOOP
          END
      END;

    vsIndLoops:  PROCEDURE =
      BEGIN
        n:  nodePtr;
        v:  vSourcePtr;

        v ← vSourceList;
        UNTIL v = NIL DO
          n ← nodeList;
          UNTIL n = NIL DO
            n↑.marked ← FALSE;
            n ← n↑.nextNode
          ENDLOOP;
          vsXPath[v↑.posNode, v, inductor];
          IF v↑.negNode↑.marked THEN ErrorAtNB[402, NIL, v];
          v ← v↑.nextvSource
        ENDLOOP
      END;

    indNodes:  PROCEDURE =
      BEGIN
        bLink:  branchLinkPtr;
        i:  inductorPtr;
        posOk, negOk:  BOOLEAN;

        i ← inductorList;
        UNTIL i = NIL DO
          bLink ← i↑.posNode↑.branches;
          posOk ← FALSE;
          UNTIL bLink = NIL DO
            posOk ← posOk OR bLink↑.branch↑.eType # inductor;
            bLink ← bLink↑.nextLink
          ENDLOOP;
          bLink ← i↑.negNode↑.branches;
          negOk ← FALSE;
          UNTIL bLink = NIL DO
            negOk ← negOk OR bLink↑.branch↑.eType # inductor;
            bLink ← bLink↑.nextLink
          ENDLOOP;
          IF ~posOk OR ~negOk THEN ErrorAtNB[403, NIL, i];
          i ← i↑.nextInductor
        ENDLOOP
      END;

    separateFunctions:  PROCEDURE =
      BEGIN
        nodes:     nodePtr ← nodeList;
        state:     REAL = 1.0;
        other:     REAL = 0.0;
        mf:        modFuncPtr;
        nonState:  BOOLEAN;

        UNTIL nodes = NIL DO
          OPEN nodes↑;
          nHist.y ← IF integrate OR nodes = gndNode THEN state
                    ELSE
                       IF curPtr # NIL THEN
                         IF curPtr↑.controller = NIL THEN
                           IF curPtr↑.posNode=gndNode OR
                              curPtr↑.negNode=gndNode THEN state
                           ELSE other
                         ELSE
                           IF LENGTH[curPtr↑.controller↑.argVector] = 0
                           THEN
                             IF curPtr↑.posNode=gndNode OR
                                curPtr↑.negNode=gndNode THEN state
                             ELSE other
                           ELSE other
                       ELSE other;
          nHist.f0 ← other;
          nodes ← nextNode
        ENDLOOP;
        UNTIL functionList = NIL DO
          nonState ← FALSE;
          FOR i:  CARDINAL IN [0..LENGTH[functionList↑.arguments]) DO
            nonState ← functionList↑.arguments[i]↑.nHist.y = other;
            IF nonState THEN EXIT
          ENDLOOP;
          mf ← functionList↑.nextFunction;
          IF nonState THEN
            BEGIN
              functionList↑.nextFunction ← otherModFunc;
              otherModFunc ← functionList
            END
          ELSE
            BEGIN
              functionList↑.nextFunction ← intNodeModFunc;
              intNodeModFunc ← functionList
            END;
          functionList ← mf
        ENDLOOP
      END;

    topoAnalysis:  PUBLIC PROCEDURE =
      BEGIN
        forceDcPaths[];
        vsIndLoops[];
        indNodes[];
        assignCurrents[];
        assignIntNodes[];
        separateFunctions[];
        IF otherModFunc # NIL THEN
          printSysWindow["Non-state controlled functions."]
      END;
  END.