Task:

 Generate a string with   N   opening brackets   [   and with   N   closing brackets   ],   in some arbitrary order. 
 Determine whether the generated string is balanced; that is, whether it consists entirely of pairs of opening/closing brackets (in that order), none of which mis-nest.


Examples

   (empty)      OK
   []           OK   
   [][]         OK   
   [[][]]       OK 
   ][         NOT OK
   ][][       NOT OK
   []][[]     NOT OK



bracketReport
=LAMBDA(bracketPair,
    LAMBDA(s,
        LET(
            depths, SCANLCOLS(
                LAMBDA(depth, charDelta, 
                    depth + charDelta
                )
            )(0)(
                codesFromBrackets(bracketPair)(
                    MID(s, 
                        SEQUENCE(1, LEN(s), 1, 1),
                        1
                    )
                )
            ),
            overClosePosn, IFNA(MATCH(-1, depths, 0), 0),
 
            IF(0 <> overClosePosn,
                "Overclosed at char " & (overClosePosn - 1),
                IF(0 <> LASTCOL(depths),
                    "Underclosed by end",
                    "OK"
                )
            )
        )
    )
)
 
 
testRandomBrackets
=LAMBDA(bracketPair,
    LAMBDA(n,
        LET(
            sample, CONCAT(
                bracketFromCode(bracketPair)(
                    RANDARRAY(1, n, -1, 1, TRUE)
                )
            ),
 
            APPENDCOLS(sample)(
                bracketReport(bracketPair)(sample)
            )
        )
    )
)
 
 
bracketFromCode
=LAMBDA(bracketPairString,
    LAMBDA(c,
        IF(0 <> c,
            IF(0 < c,
                MID(bracketPairString, 1, 1),
                MID(bracketPairString, 2, 1)
            ),
            "x"
        )
    )
)
 
 
codesFromBrackets
=LAMBDA(bracketPair,
    LAMBDA(s,
        LAMBDA(c,
            LET(
                posn, IFERROR(
                    FIND(c, bracketPair),
                    0
                ),
 
                rem, "0 for non-bracket chars, (1, -1) for (open, close)",
                IF(0 <> posn,
                    IF(1 < posn, -1, 1),
                    0
                )
            )
        )(
             MID(s,
                SEQUENCE(1, LEN(s), 1, 1),
                1
            )
        )
    )
)