'REM $Include: 'SoftArrays.bi'

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Begin BM-component.
' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Processing
'

FUNCTION EvalAllSoftArrays (x AS INTEGER)
    DIM TheReturn AS LONG
    DIM k AS INTEGER
    FOR k = x TO UBOUND(SoftArrayRegister)
        IF (SoftArrayRegister(k) <> -1) THEN
            TheReturn = Evaluate(SoftArray(k).HeadNode)
        END IF
    NEXT
    EvalAllSoftArrays = TheReturn
END FUNCTION

FUNCTION Evaluate (x AS LONG)
    DIM TheReturn AS LONG
    DIM a AS LONG
    DIM b AS LONG
    a = x
    b = -1
    DO
        a = EvalStep(FirstEmbedded(a))
        IF (a = x) THEN EXIT DO
        IF (a = b) THEN
            IF (a = -1) THEN EXIT DO
            a = Nodes(a).South
            IF (a = -1) THEN EXIT DO
        ELSE
            b = a
        END IF
    LOOP
    TheReturn = a
    Evaluate = TheReturn
END SUB

FUNCTION EvalStep (x AS LONG)
    DIM TheReturn AS LONG
    DIM SouthernId AS LONG
    DIM NorthernId AS LONG
    DIM FunctionId AS LONG
    DIM i AS LONG
    DIM j AS LONG
    DIM k AS INTEGER
    DIM n AS LONG
    DIM s AS LONG
    DIM RefSpecies AS STRING
    DIM ReturnSpecies AS STRING
    DIM ReturnInteger AS INTEGER
    DIM ReturnText AS STRING
    DIM ReturnDouble AS DOUBLE
    DIM MultiPass AS INTEGER

    RefSpecies = ""
    FunctionId = x
    ReturnSpecies = ""
    ReturnInteger = 0
    ReturnText = ""
    ReturnDouble = 0

    ' Pre-evaluation
    IF (x <> -1) THEN
        SouthernId = x
        i = x
        DO
            n = Nodes(i).North
            IF (n <> -1) THEN
                i = n
            ELSE
                NorthernId = i
                EXIT DO
            END IF
        LOOP
        FunctionId = Nodes(NorthernId).West
        IF (FunctionId <> -1) THEN
            ReturnSpecies = Nodes(FunctionId).Species
            SELECT CASE ReturnSpecies
                CASE "integer"
                    ReturnInteger = IntegerData(Nodes(FunctionId).Reference)
                CASE "text"
                    ReturnText = TextData(Nodes(FunctionId).Reference)
                CASE "double"
                    ReturnDouble = DoubleData(Nodes(FunctionId).Reference)
            END SELECT
        END IF
    END IF

    ' Lambda substitution
    i = NorthernId
    DIM lf AS INTEGER
    lf = 0
    DO
        IF (Nodes(i).Species = "text") THEN
            IF (TextData(Nodes(i).Reference) = "[1]") THEN
                j = LambdaMatrix(LambdaIndex, 1)
                Nodes(i).Species = Nodes(j).Species
                Nodes(i).Reference = Nodes(j).Reference
                lf = 1
            END IF
            IF (TextData(Nodes(i).Reference) = "[2]") THEN
                j = LambdaMatrix(LambdaIndex, 2)
                Nodes(i).Species = Nodes(j).Species
                Nodes(i).Reference = Nodes(j).Reference
                lf = 1
            END IF
        END IF
        IF (i = SouthernId) THEN
            EXIT DO
        ELSE
            s = Nodes(i).South
            i = s
        END IF
    LOOP
    IF (lf = 1) THEN
        FOR k = 1 TO LambdaArgCount(LambdaIndex)
            j = Unlink(LambdaMatrix(LambdaIndex, k))
        NEXT
        LambdaArgCount(LambdaIndex) = 0
        LambdaIndex = LambdaIndex - 1
    END IF

    ' Determine return species
    i = NorthernId
    DO
        IF (i = -1) THEN EXIT DO
        IF (Nodes(i).Species = "text") THEN RefSpecies = "text"
        IF ((Nodes(i).Species = "double") AND (RefSpecies <> "text")) THEN RefSpecies = "double"
        IF ((Nodes(i).Species = "integer") AND (RefSpecies <> "text") AND (RefSpecies <> "double")) THEN RefSpecies = "integer"
        i = Nodes(i).South
    LOOP

    ' Single-pass evaluation
    MultiPass = 0
    SELECT CASE Literal$(FunctionId)
        CASE "*"
            MultiPass = 1
            ReturnSpecies = RefSpecies
            ReturnInteger = 1
            ReturnDouble = 1
        CASE "+"
            MultiPass = 1
            ReturnSpecies = RefSpecies
            ReturnInteger = 0
            ReturnDouble = 0
        CASE "/"
            MultiPass = 0
            SELECT CASE Nodes(NorthernId).Species
                CASE "integer"
                    ReturnSpecies = "double"
                    SELECT CASE Nodes(SouthernId).Species
                        CASE "integer"
                            ReturnDouble = IntegerData(Nodes(NorthernId).Reference) / IntegerData(Nodes(SouthernId).Reference)
                        CASE "double"
                            ReturnDouble = IntegerData(Nodes(NorthernId).Reference) / DoubleData(Nodes(SouthernId).Reference)
                    END SELECT
                CASE "text"
                    ReturnSpecies = "text"
                    ReturnText = Literal$(NorthernId) + "/" + Literal$(SouthernId)
                CASE "double"
                    ReturnSpecies = "double"
                    SELECT CASE Nodes(SouthernId).Species
                        CASE "integer"
                            ReturnDouble = DoubleData(Nodes(NorthernId).Reference) / IntegerData(Nodes(SouthernId).Reference)
                        CASE "double"
                            ReturnDouble = DoubleData(Nodes(NorthernId).Reference) / DoubleData(Nodes(SouthernId).Reference)
                    END SELECT
            END SELECT
            i = Unlink(NorthernId)
            i = Unlink(SouthernId)
        CASE "cos"
            IF (NorthernId = SouthernId) THEN
                MultiPass = 0
                i = NorthernId
                SELECT CASE Nodes(i).Species
                    CASE "integer"
                        ReturnSpecies = "double"
                        ReturnDouble = COS(IntegerData(Nodes(i).Reference))
                    CASE "text"
                        ReturnSpecies = "text"
                        ReturnText = "cos" + "(" + Literal$(i) + ")"
                    CASE "double"
                        ReturnSpecies = "double"
                        ReturnDouble = COS(DoubleData(Nodes(i).Reference))
                END SELECT
                i = Unlink(i)
            ELSE
                MultiPass = 1
                ReturnSpecies = "text"
                ReturnText = "(" + "cos" + ")"
            END IF
        CASE "lambda"
            MultiPass = 1
            LambdaIndex = LambdaIndex + 1
            ReturnSpecies = "text"
            ReturnText = "(" + "lambda" + ")"
        CASE "(" + "lambda" + ")"
            '
    END SELECT

    ' Multi-pass evaluation
    IF (MultiPass = 1) THEN
        i = NorthernId
        DO
            SELECT CASE Literal$(FunctionId)
                CASE "*"
                    SELECT CASE ReturnSpecies
                        CASE "integer"
                            SELECT CASE Nodes(i).Species
                                CASE "integer"
                                    ReturnInteger = ReturnInteger * IntegerData(Nodes(i).Reference)
                                CASE "double"
                                    ReturnInteger = ReturnInteger * DoubleData(Nodes(i).Reference)
                            END SELECT
                        CASE "text"
                            ReturnText = ReturnText + Literal$(i)
                        CASE "double"
                            SELECT CASE Nodes(i).Species
                                CASE "integer"
                                    ReturnDouble = ReturnDouble * IntegerData(Nodes(i).Reference)
                                CASE "double"
                                    ReturnDouble = ReturnDouble * DoubleData(Nodes(i).Reference)
                            END SELECT
                    END SELECT
                    IF (i = SouthernId) THEN
                        i = Unlink(i)
                        EXIT DO
                    ELSE
                        s = Nodes(i).South
                        i = Unlink(i)
                        i = s
                    END IF
                CASE "+"
                    SELECT CASE ReturnSpecies
                        CASE "integer"
                            SELECT CASE Nodes(i).Species
                                CASE "integer"
                                    ReturnInteger = ReturnInteger + IntegerData(Nodes(i).Reference)
                                CASE "double"
                                    ReturnInteger = ReturnInteger + DoubleData(Nodes(i).Reference)
                            END SELECT
                        CASE "text"
                            ReturnText = ReturnText + Literal$(i)
                        CASE "double"
                            SELECT CASE Nodes(i).Species
                                CASE "integer"
                                    ReturnDouble = ReturnDouble + IntegerData(Nodes(i).Reference)
                                CASE "double"
                                    ReturnDouble = ReturnDouble + DoubleData(Nodes(i).Reference)
                            END SELECT
                    END SELECT
                    IF (i = SouthernId) THEN
                        i = Unlink(i)
                        EXIT DO
                    ELSE
                        s = Nodes(i).South
                        i = Unlink(i)
                        i = s
                    END IF
                CASE "cos"
                    SELECT CASE Nodes(i).Species
                        CASE "integer"
                            Nodes(i).Species = "double"
                            Nodes(i).Reference = NewDoubleData(COS(IntegerData(Nodes(i).Reference)))
                        CASE "text"
                            Nodes(i).Reference = NewTextData("cos" + "(" + Literal$(i) + ")")
                        CASE "double"
                            Nodes(i).Species = "double"
                            Nodes(i).Reference = NewDoubleData(COS(DoubleData(Nodes(i).Reference)))
                    END SELECT
                    IF (i = SouthernId) THEN
                        EXIT DO
                    ELSE
                        s = Nodes(i).South
                        i = s
                    END IF
                CASE "lambda"
                    LambdaArgCount(LambdaIndex) = LambdaArgCount(LambdaIndex) + 1
                    LambdaMatrix(LambdaIndex, LambdaArgCount(LambdaIndex)) = i
                    IF (i = SouthernId) THEN
                        EXIT DO
                    ELSE
                        s = Nodes(i).South
                        i = s
                    END IF
            END SELECT
        LOOP
    END IF

    SELECT CASE ReturnSpecies
        CASE "integer"
            Nodes(FunctionId).Species = ReturnSpecies
            Nodes(FunctionId).Reference = NewIntegerData(ReturnInteger)
        CASE "text"
            Nodes(FunctionId).Species = ReturnSpecies
            Nodes(FunctionId).Reference = NewTextData(ReturnText)
        CASE "double"
            Nodes(FunctionId).Species = ReturnSpecies
            Nodes(FunctionId).Reference = NewDoubleData(ReturnDouble)
    END SELECT

    TheReturn = FunctionId
    EvalStep = TheReturn
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Seek and recall
'

FUNCTION Content (t AS STRING)
    DIM TheReturn AS LONG
    TheReturn = Nodes(HeadId(t)).East
    Content = TheReturn
END FUNCTION

FUNCTION Bottom (t AS STRING)
    DIM TheReturn AS LONG
    TheReturn = -1 + Measure(Content(t), "s")
    Bottom = TheReturn
END FUNCTION

FUNCTION SoftArrayID (t AS STRING)
    DIM TheReturn AS INTEGER ' Changed from long
    DIM k AS LONG
    TheReturn = -1
    FOR k = 1 TO UBOUND(SoftArray)
        IF (SoftArray(k).Label = t) THEN
            TheReturn = k
            EXIT FOR
        END IF
    NEXT
    SoftArrayID = TheReturn
END FUNCTION

FUNCTION HeadId (t AS STRING)
    DIM TheReturn AS LONG
    TheReturn = SoftArray(SoftArrayID(t)).HeadNode
    HeadId = TheReturn
END FUNCTION

FUNCTION FirstEmbedded (x AS LONG)
    DIM TheReturn AS LONG
    TheReturn = MostEmbeddedRecur(x, -1)
    FirstEmbedded = TheReturn
END FUNCTION

FUNCTION MostEmbeddedRecur (x AS LONG, y AS LONG)
    DIM TheReturn AS LONG
    DIM s AS LONG
    DIM e AS LONG
    s = Nodes(x).South
    e = Nodes(x).East
    IF (e <> -1) THEN
        TheReturn = MostEmbeddedRecur(e, y)
    END IF
    IF (s <> -1) THEN
        TheReturn = MostEmbeddedRecur(s, y)
    END IF
    IF ((e = -1) AND (s = -1) AND (y = -1)) THEN
        y = x
    END IF
    TheReturn = y
    MostEmbeddedRecur = TheReturn
END SUB

FUNCTION SeekText (t AS STRING, x AS LONG, r AS INTEGER)
    DIM TheReturn AS LONG
    DIM s AS LONG
    DIM e AS LONG
    TheReturn = -1
    s = Nodes(x).South
    e = Nodes(x).East
    IF (TextData(Nodes(x).Reference) = t) THEN
        TheReturn = x
        r = r - 1
    ELSE
        IF ((e <> -1) AND (r > 0)) THEN
            TheReturn = SeekText(t, e, r)
        END IF
        IF ((s <> -1) AND (r > 0)) THEN
            TheReturn = SeekText(t, s, r)
        END IF
    END IF
    SeekText = TheReturn
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Navigation
'
FUNCTION JumpFrom (x AS LONG, t AS STRING, r AS INTEGER)
    DIM TheReturn AS LONG
    TheReturn = x
    IF (r > 0) THEN
        SELECT CASE t
            CASE "n"
                TheReturn = JumpFrom(Nodes(x).North, "n", r - 1)
            CASE "s"
                TheReturn = JumpFrom(Nodes(x).South, "s", r - 1)
            CASE "e"
                TheReturn = JumpFrom(Nodes(x).East, "e", r - 1)
            CASE "w"
                TheReturn = JumpFrom(Nodes(x).West, "w", r - 1)
        END SELECT
    END IF
    JumpFrom = TheReturn
END FUNCTION

FUNCTION StepUsing (x AS LONG, t AS STRING)
    DIM TheReturn AS LONG
    DIM i AS LONG
    DIM j AS LONG
    DIM k AS INTEGER
    i = x
    FOR k = 1 TO LEN(t)
        SELECT CASE MID$(t, k, 1)
            CASE "n"
                j = Nodes(i).North
                IF (j <> -1) THEN i = j
            CASE "s"
                j = Nodes(i).South
                IF (j <> -1) THEN i = j
            CASE "e"
                j = Nodes(i).East
                IF (j <> -1) THEN i = j
            CASE "w"
                j = Nodes(i).West
                IF (j <> -1) THEN i = j
        END SELECT
    NEXT
    TheReturn = i
    StepUsing = TheReturn
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Internal metrics
'

FUNCTION SquareArrayHeight (x AS INTEGER)
    DIM TheReturn AS INTEGER
    TheReturn = Measure(Nodes(SoftArray(x).HeadNode).East, "s")
    SquareArrayHeight = TheReturn
END FUNCTION

FUNCTION SquareArrayWidth (x AS INTEGER)
    DIM TheReturn AS INTEGER
    TheReturn = Measure(Nodes(SoftArray(x).HeadNode).East, "e")
    SquareArrayWidth = TheReturn
END FUNCTION

FUNCTION Measure (x AS LONG, t AS STRING)
    DIM TheReturn AS INTEGER
    TheReturn = CountSteps(x, -1, t)
    Measure = TheReturn
END FUNCTION

FUNCTION CountSteps (x AS LONG, y AS LONG, t AS STRING)
    DIM TheReturn AS INTEGER
    DIM k AS LONG
    TheReturn = 0
    SELECT CASE t
        CASE "n"
            k = Nodes(x).North
        CASE "s"
            k = Nodes(x).South
        CASE "e"
            k = Nodes(x).East
        CASE "w"
            k = Nodes(x).West
    END SELECT
    IF (k = y) THEN
        TheReturn = TheReturn + 1
    ELSE
        IF (k <> -1) THEN
            TheReturn = TheReturn + 1 + CountSteps(k, y, t)
        END IF
    END IF
    CountSteps = TheReturn
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Printing and Reporting
'

FUNCTION MemoryProbe$ (t AS STRING)
    DIM TheReturn AS STRING
    DIM a AS LONG
    DIM k AS INTEGER
    TheReturn = t
    TheReturn = TheReturn + "Begin memory probe..." + CHR$(10)
    TheReturn = TheReturn + "Soft arrays:" + CHR$(10)
    FOR k = 1 TO UBOUND(SoftArrayRegister)
        IF (SoftArrayRegister(k) <> -1) THEN
            TheReturn = TheReturn + "  " + SoftArray(k).Label + " (@@" + STR$(SoftArrayRegister(k)) + ") (@" + STR$(SoftArray(k).HeadNode) + ")" + CHR$(10)
        END IF
    NEXT
    TheReturn = TheReturn + "Nodes:" + CHR$(10)
    FOR k = 1 TO UBOUND(IdentityRegister)
        IF (IdentityRegister(k) <> -1) THEN
            a = IdentityRegister(k)
            TheReturn = TheReturn + "  " + Literal$(a)
            TheReturn = TheReturn + " (@" + STR$(IdentityRegister(k)) + ")"
            TheReturn = TheReturn + " (" + LTRIM$(RTRIM$(STR$(Nodes(a).North))) + "," + LTRIM$(RTRIM$(STR$(Nodes(a).South))) + "," + LTRIM$(RTRIM$(STR$(Nodes(a).East))) + "," + LTRIM$(RTRIM$(STR$(Nodes(a).West))) + ")"
            TheReturn = TheReturn + CHR$(10)
        END IF
    NEXT
    TheReturn = TheReturn + "End memory probe..." + CHR$(10)
    MemoryProbe$ = TheReturn
END FUNCTION

FUNCTION PrintAllSoftArrays$ (x AS INTEGER)
    DIM TheReturn AS STRING
    DIM k AS INTEGER
    DIM j AS LONG
    TheReturn = ""
    FOR k = x TO UBOUND(SoftArrayRegister)
        IF (SoftArrayRegister(k) <> -1) THEN
            j = SoftArray(k).HeadNode
            TheReturn = TheReturn + PrintSoftArray$(j)
            IF (x < UBOUND(SoftArrayRegister)) THEN
                TheReturn = TheReturn + CHR$(10)
            END IF
        END IF
    NEXT
    PrintAllSoftArrays$ = TheReturn
END FUNCTION

FUNCTION PrintSoftArray$ (x AS LONG)
    DIM TheReturn AS STRING
    DIM t AS STRING
    t = ListNodesRecur$(0, x)
    TheReturn = LEFT$(t, LEN(t) - 1)
    PrintSoftArray$ = TheReturn
END SUB

FUNCTION ListNodesRecur$ (i AS INTEGER, x AS LONG)
    DIM TheReturn AS STRING
    DIM s AS LONG
    DIM e AS LONG
    s = Nodes(x).South
    e = Nodes(x).East
    TheReturn = TheReturn + Filler$(i, "  ") + Literal$(x) 'CHR$(9)
    'TheReturn = TheReturn  + " (" + STR$(Nodes(x).North) + "," + STR$(Nodes(x).South) + "," + STR$(Nodes(x).East) + "," + STR$(Nodes(x).West) + ")"
    TheReturn = TheReturn + CHR$(10)
    IF (e <> -1) THEN
        TheReturn = TheReturn + ListNodesRecur$(i + 1, e)
    END IF
    IF (s <> -1) THEN
        TheReturn = TheReturn + ListNodesRecur$(i, s)
    END IF
    ListNodesRecur$ = TheReturn
END FUNCTION

FUNCTION IdentityStackBuildRecur (x AS LONG, r AS INTEGER)
    DIM TheReturn AS LONG
    DIM n AS LONG
    DIM s AS LONG
    DIM e AS LONG
    DIM w AS LONG
    n = Nodes(x).North
    s = Nodes(x).South
    e = Nodes(x).East
    w = Nodes(x).West
    IdentityStackSize = IdentityStackSize + 1
    IdentityStack(IdentityStackSize, 1) = x
    IdentityStack(IdentityStackSize, 2) = n
    IdentityStack(IdentityStackSize, 3) = s
    IdentityStack(IdentityStackSize, 4) = e
    IdentityStack(IdentityStackSize, 5) = w
    IF (r <> -1) THEN
        IF (e <> -1) THEN
            TheReturn = IdentityStackBuildRecur(e, 1)
        END IF
        IF (r <> 0) THEN
            IF (s <> -1) THEN
                TheReturn = IdentityStackBuildRecur(s, 1)
            END IF
        END IF
    ELSE
        TheReturn = -1
    END IF
    IdentityStackBuildRecur = TheReturn
END FUNCTION

FUNCTION Literal$ (x AS LONG)
    DIM TheReturn AS STRING
    TheReturn = ""
    IF (x <> -1) THEN
        SELECT CASE Nodes(x).Species
            CASE "integer"
                TheReturn = LTRIM$(RTRIM$(STR$(IntegerData(Nodes(x).Reference))))
            CASE "text"
                TheReturn = TextData(Nodes(x).Reference)
            CASE "double"
                TheReturn = LTRIM$(RTRIM$(STR$(DoubleData(Nodes(x).Reference))))
        END SELECT
    END IF
    Literal$ = TheReturn
END FUNCTION

FUNCTION Filler$ (x AS INTEGER, t AS STRING)
    DIM TheReturn AS STRING
    DIM k AS INTEGER
    IF (x > 0) THEN
        FOR k = 1 TO x
            TheReturn = TheReturn + t
        NEXT
    ELSE
        TheReturn = ""
    END IF
    Filler$ = TheReturn
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Soft array construction
'

FUNCTION NewUnitArray (t AS STRING, x AS LONG)
    DIM TheReturn AS LONG
    DIM k AS LONG
    TheReturn = NewSoftArray(t)
    k = LinkEast(TheReturn, x)
    NewUnitArray = TheReturn
END FUNCTION

FUNCTION NewSoftArray (t AS STRING)
    DIM TheReturn AS LONG
    DIM i AS LONG
    TheReturn = NewTextNode(t)
    i = NextOpenSoftArray(1)
    SoftArrayRegister(i) = i
    SoftArray(i).Label = TextData(Nodes(TheReturn).Reference)
    SoftArray(i).HeadNode = TheReturn
    NewSoftArray = TheReturn
END FUNCTION

FUNCTION NextOpenSoftArray (x AS LONG)
    DIM TheReturn AS LONG
    DIM k AS LONG
    TheReturn = -1
    FOR k = x TO UBOUND(SoftArrayRegister)
        IF (SoftArrayRegister(k) = -1) THEN
            TheReturn = k
            EXIT FOR
        END IF
    NEXT
    NextOpenSoftArray = TheReturn
END FUNCTION

FUNCTION LinkSouth (n AS LONG, s AS LONG)
    DIM TheReturn AS LONG
    Nodes(s).North = n
    Nodes(n).South = s
    TheReturn = s
    LinkSouth = TheReturn
END FUNCTION

FUNCTION LinkEast (w AS LONG, e AS LONG)
    DIM TheReturn AS LONG
    Nodes(w).East = e
    Nodes(e).West = w
    TheReturn = e
    LinkEast = TheReturn
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Soft array editing
'

FUNCTION InsertSouth (n AS LONG, x AS LONG)
    DIM s AS LONG
    s = Nodes(n).South
    Nodes(n).South = x
    Nodes(x).North = n
    Nodes(x).South = s
    IF (s <> -1) THEN
        Nodes(s).North = x
    END IF
    InsertSouth = x
END FUNCTION

FUNCTION InsertEast (w AS LONG, x AS LONG)
    DIM e AS LONG
    e = Nodes(w).East
    Nodes(w).East = x
    Nodes(x).West = w
    Nodes(x).East = e
    IF (e <> -1) THEN
        Nodes(e).West = x
    END IF
    InsertEast = x
END FUNCTION

FUNCTION Unlink (x AS LONG)
    DIM n AS LONG
    DIM s AS LONG
    DIM e AS LONG
    DIM w AS LONG
    DIM k AS INTEGER
    n = Nodes(x).North
    s = Nodes(x).South
    e = Nodes(x).East
    w = Nodes(x).West
    IF (n = -1) AND (s = -1) AND (w = -1) THEN ' head node
        FOR k = 1 TO UBOUND(SoftArrayRegister)
            IF (SoftArray(k).HeadNode = x) THEN
                SoftArrayRegister(k) = -1
            END IF
        NEXT
    END IF
    IF (n <> -1) THEN Nodes(n).South = s
    IF (s <> -1) THEN Nodes(s).North = n
    IF (e <> -1) THEN Nodes(e).West = w
    IF (w <> -1) THEN Nodes(w).East = e
    IF ((n = -1) AND (w <> -1)) THEN ' content node
        IF (s <> -1) THEN
            Nodes(s).West = w
            Nodes(s).North = -1
            Nodes(w).East = s
        END IF
    END IF
    IdentityRegister(x) = -1
    Unlink = x
END FUNCTION

FUNCTION DeleteNodes (x AS LONG)
    DIM TheReturn AS LONG
    DIM e AS LONG
    e = Nodes(x).East
    IF (e <> -1) THEN
        TheReturn = DeleteNodesRecur(e)
    END IF
    TheReturn = Unlink(x)
    DeleteNodes = TheReturn
END SUB

FUNCTION DeleteNodesRecur (x AS LONG)
    DIM TheReturn AS LONG
    DIM s AS LONG
    DIM e AS LONG
    s = Nodes(x).South
    e = Nodes(x).East
    IF (e <> -1) THEN
        TheReturn = DeleteNodesRecur(e)
    END IF
    IF (s <> -1) THEN
        TheReturn = DeleteNodesRecur(s)
    END IF
    TheReturn = Unlink(x)
    DeleteNodesRecur = TheReturn
END FUNCTION

FUNCTION CopyNodes (x AS LONG)
    DIM TheReturn AS LONG
    DIM k AS INTEGER
    DIM j AS INTEGER
    DIM n AS INTEGER
    DIM i AS INTEGER
    DIM m AS INTEGER
    DIM a AS LONG
    DIM b AS LONG
    a = IdentityStackBuildRecur(x, 0)
    FOR k = 1 TO IdentityStackSize
        i = k + IdentityStackSize
        a = NewNode(NextOpenIdentity(IdentityStack(k, 1)), Nodes(IdentityStack(k, 1)).Species, Nodes(IdentityStack(k, 1)).Reference)
        IdentityStack(i, 1) = a
    NEXT
    FOR k = 1 TO IdentityStackSize
        i = k + IdentityStackSize
        FOR j = 2 TO 5
            a = IdentityStack(k, j)
            FOR n = 1 TO IdentityStackSize
                m = n + IdentityStackSize
                b = IdentityStack(n, 1)
                IF (b = a) THEN
                    SELECT CASE j
                        CASE 2
                            Nodes(IdentityStack(i, 1)).North = IdentityStack(m, 1)
                        CASE 3
                            Nodes(IdentityStack(i, 1)).South = IdentityStack(m, 1)
                        CASE 4
                            Nodes(IdentityStack(i, 1)).East = IdentityStack(m, 1)
                        CASE 5
                            Nodes(IdentityStack(i, 1)).West = IdentityStack(m, 1)
                    END SELECT
                END IF
            NEXT
        NEXT
    NEXT
    a = IdentityStack(1 + IdentityStackSize, 1)
    FOR k = 1 TO UBOUND(SoftArrayRegister)
        IF (SoftArray(k).HeadNode) = x THEN
            a = Nodes(a).East
            EXIT FOR
        END IF
    NEXT
    IdentityStackSize = 0
    TheReturn = NewSoftArray("copy" + LTRIM$(RTRIM$(STR$(INT(RND * 10000)))))
    b = LinkEast(TheReturn, a)
    CopyNodes = TheReturn
END FUNCTION

FUNCTION DeleteSoftArray (x AS INTEGER)
    DIM TheReturn AS LONG
    SoftArrayRegister(x) = -1
    TheReturn = DeleteNodesRecur(SoftArray(x).HeadNode)
    DeleteSoftArray = TheReturn
END FUNCTION

FUNCTION DeleteAllSoftArrays (x AS INTEGER)
    DIM TheReturn AS LONG
    DIM k AS INTEGER
    FOR k = x TO UBOUND(SoftArrayRegister)
        IF (SoftArrayRegister(k) <> -1) THEN
            TheReturn = DeleteSoftArray(k)
        END IF
    NEXT
    DeleteAllSoftArrays = TheReturn
END FUNCTION

FUNCTION SeverSoftArray (x AS INTEGER)
    DIM TheReturn AS LONG
    IF (Nodes(x).North <> -1) THEN
        Nodes(Nodes(x).North).South = -1
        Nodes(x).North = -1
    END IF
    IF (Nodes(x).West <> -1) THEN
        Nodes(Nodes(x).West).East = -1
    END IF
    TheReturn = NewSoftArray("sever" + LTRIM$(RTRIM$(STR$(INT(RND * 10000)))))
    TheReturn = LinkEast(TheReturn, x)
    SeverSoftArray = TheReturn
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Node creation
'

FUNCTION NextOpenIdentity (x AS LONG)
    DIM TheReturn AS LONG
    DIM k AS LONG
    TheReturn = -1
    FOR k = x TO UBOUND(IdentityRegister)
        IF (IdentityRegister(k) = -1) THEN
            TheReturn = k
            EXIT FOR
        END IF
    NEXT
    NextOpenIdentity = TheReturn
END FUNCTION

FUNCTION NewNode (x AS LONG, t AS STRING, r AS LONG)
    DIM i AS LONG
    i = NextOpenIdentity(x)
    IdentityRegister(i) = i
    Nodes(i).Identity = i
    Nodes(i).Species = t
    Nodes(i).Reference = r
    Nodes(i).North = -1
    Nodes(i).South = -1
    Nodes(i).East = -1
    Nodes(i).West = -1
    NewNode = i
END FUNCTION

FUNCTION CopyIntegerNode (x AS LONG)
    CopyIntegerNode = NewIntegerNode(IntegerData(Nodes(x).Reference))
END FUNCTION

FUNCTION ConvertToInteger (i AS LONG, x AS INTEGER)
    DIM TheReturn AS LONG
    Nodes(i).Species = "integer"
    Nodes(i).Reference = NewIntegerData(x)
    TheReturn = i
    ConvertToInteger = TheReturn
END FUNCTION

FUNCTION ConvertToText (i AS LONG, x AS STRING)
    DIM TheReturn AS LONG
    Nodes(i).Species = "text"
    Nodes(i).Reference = NewTextData(x)
    TheReturn = i
    ConvertToText = TheReturn
END FUNCTION

FUNCTION ConvertToDouble (i AS LONG, x AS DOUBLE)
    DIM TheReturn AS LONG
    Nodes(i).Species = "double"
    Nodes(i).Reference = NewDoubleData(x)
    TheReturn = i
    ConvertToDouble = TheReturn
END FUNCTION

FUNCTION NewIntegerNode (x AS INTEGER)
    NewIntegerNode = NewNode(1, "integer", NewIntegerData(x))
END FUNCTION

FUNCTION NewTextNode (x AS STRING)
    NewTextNode = NewNode(1, "text", NewTextData(x))
END FUNCTION

FUNCTION NewDoubleNode (x AS DOUBLE)
    NewDoubleNode = NewNode(1, "double", NewDoubleData(x))
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Node editing
'

FUNCTION EditIntegerReference (x AS LONG, i AS INTEGER)
    DIM z AS LONG
    z = NewIntegerData(i)
    Nodes(x).Reference = z
    EditIntegerReference = z
END FUNCTION

FUNCTION EditTextReference (x AS LONG, t AS STRING)
    DIM z AS LONG
    z = NewTextData(t)
    Nodes(x).Reference = z
    EditTextReference = z
END FUNCTION

FUNCTION EditDoubleReference (x AS LONG, d AS DOUBLE)
    DIM z AS LONG
    z = NewDoubleData(d)
    Nodes(x).Reference = z
    EditDoubleReference = z
END FUNCTION

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Data assimilation (prevents redundancy but can be disabled for more speed)
'

FUNCTION NewIntegerData (x AS INTEGER)
    DIM TheReturn AS LONG
    DIM k AS LONG
    TheReturn = -1
    FOR k = 1 TO UBOUND(IntegerData)
        IF (IntegerData(k) = x) THEN
            TheReturn = k
            EXIT FOR
        END IF
    NEXT
    IF (TheReturn = -1) THEN
        REDIM _PRESERVE IntegerData(UBOUND(IntegerData) + 1)
        IntegerData(UBOUND(IntegerData)) = x
        TheReturn = UBOUND(IntegerData)
    END IF
    NewIntegerData = TheReturn
END SUB

FUNCTION NewTextData (x AS STRING)
    DIM TheReturn AS LONG
    DIM k AS LONG
    TheReturn = -1
    FOR k = 1 TO UBOUND(TextData)
        IF (TextData(k) = x) THEN
            TheReturn = k
            EXIT FOR
        END IF
    NEXT
    IF (TheReturn = -1) THEN
        REDIM _PRESERVE TextData(UBOUND(TextData) + 1)
        TextData(UBOUND(Textdata)) = x
        TheReturn = UBOUND(TextData)
    END IF
    NewTextData = TheReturn
END SUB

FUNCTION NewDoubleData (x AS DOUBLE)
    DIM TheReturn AS LONG
    DIM k AS LONG
    TheReturn = -1
    FOR k = 1 TO UBOUND(DoubleData)
        IF (DoubleData(k) = x) THEN
            TheReturn = k
            EXIT FOR
        END IF
    NEXT
    IF (TheReturn = -1) THEN
        REDIM _PRESERVE DoubleData(UBOUND(DoubleData) + 1)
        DoubleData(UBOUND(DoubleData)) = x
        TheReturn = UBOUND(DoubleData)
    END IF
    NewDoubleData = TheReturn
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' End BM-component.
' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
