A pascal matrix is a two-dimensional square matrix holding numbers from   Pascal's triangle,   also known as   binomial coefficients   and which can be shown as   nCr.

Shown below are truncated   5-by-5   matrices   M[i, j]   for   i,j   in range   0..4. 


A Pascal upper-triangular matrix that is populated with   jCi:

[[1, 1, 1, 1, 1],
 [0, 1, 2, 3, 4],
 [0, 0, 1, 3, 6],
 [0, 0, 0, 1, 4],
 [0, 0, 0, 0, 1]]

A Pascal lower-triangular matrix that is populated with   iCj   (the transpose of the upper-triangular matrix):

[[1, 0, 0, 0, 0],
 [1, 1, 0, 0, 0],
 [1, 2, 1, 0, 0],
 [1, 3, 3, 1, 0],
 [1, 4, 6, 4, 1]]

A Pascal symmetric matrix that is populated with   i+jCi:

[[1, 1, 1, 1, 1],
 [1, 2, 3, 4, 5],
 [1, 3, 6, 10, 15],
 [1, 4, 10, 20, 35],
 [1, 5, 15, 35, 70]]



Task

Write functions capable of generating each of the three forms of   n-by-n   matrices.

Use those functions to display upper, lower, and symmetric Pascal   5-by-5   matrices on this page.

The output should distinguish between different matrices and the rows of each matrix   (no showing a list of 25 numbers assuming the reader should split it into rows).



Note
 
The   Cholesky decomposition   of a Pascal symmetric matrix is the Pascal lower-triangle matrix of the same size.




-- PASCAL MATRIX -------------------------------------------------------------
 
-- pascalMatrix :: ((Int, Int) -> (Int, Int)) -> Int -> [[Int]]
on pascalMatrix(f, n)
    chunksOf(n, map(compose(my bc, f), range({{0, 0}, {n - 1, n - 1}})))
end pascalMatrix
 
-- Binomial coefficient
-- bc :: (Int, Int) -> Int
on bc(nk)
    set {n, k} to nk
    script bc_
        on |λ|(a, x)
            floor((a * (n - x + 1)) / x)
        end |λ|
    end script
    foldl(bc_, 1, enumFromTo(1, k))
end bc
 
 
-- TEST ----------------------------------------------------------------------
on run
    set matrixSize to 5
 
    script symm
        on |λ|(ab)
            set {a, b} to ab
            {a + b, a}
        end |λ|
    end script
 
    script format
        on |λ|(s, xs)
            unlines(concat({{s}, map(my show, xs), {""}}))
        end |λ|
    end script
 
    unlines(zipWith(format, ¬
        {"Lower", "Upper", "Symmetric"}, ¬
        |<*>|(map(curry(pascalMatrix), [|id|, swap, symm]), {matrixSize})))
end run
 
 
-- GENERIC FUNCTIONS ---------------------------------------------------------
 
-- A list of functions applied to a list of arguments
-- (<*> | ap) :: [(a -> b)] -> [a] -> [b]
on |<*>|(fs, xs)
    set {nf, nx} to {length of fs, length of xs}
    set acc to {}
    repeat with i from 1 to nf
        tell mReturn(item i of fs)
            repeat with j from 1 to nx
                set end of acc to |λ|(contents of (item j of xs))
            end repeat
        end tell
    end repeat
    return acc
end |<*>|
 
-- chunksOf :: Int -> [a] -> [[a]]
on chunksOf(k, xs)
    script
        on go(ys)
            set {a, b} to splitAt(k, ys)
            if isNull(a) then
                {}
            else
                {a} & go(b)
            end if
        end go
    end script
    result's go(xs)
end chunksOf
 
-- compose :: (b -> c) -> (a -> b) -> (a -> c)
on compose(f, g)
    script
        on |λ|(x)
            mReturn(f)'s |λ|(mReturn(g)'s |λ|(x))
        end |λ|
    end script
end compose
 
-- concat :: [[a]] -> [a] | [String] -> String
on concat(xs)
    if length of xs > 0 and class of (item 1 of xs) is string then
        set acc to ""
    else
        set acc to {}
    end if
    repeat with i from 1 to length of xs
        set acc to acc & item i of xs
    end repeat
    acc
end concat
 
-- cons :: a -> [a] -> [a]
on cons(x, xs)
    {x} & xs
end cons
 
-- curry :: (Script|Handler) -> Script
on curry(f)
    script
        on |λ|(a)
            script
                on |λ|(b)
                    |λ|(a, b) of mReturn(f)
                end |λ|
            end script
        end |λ|
    end script
end curry
 
-- enumFromTo :: Int -> Int -> [Int]
on enumFromTo(m, n)
    set lst to {}
    repeat with i from m to n
        set end of lst to i
    end repeat
    return lst
end enumFromTo
 
-- floor :: Num -> Int
on floor(x)
    if x < 0 and x mod 1 is not 0 then
        (x div 1) - 1
    else
        (x div 1)
    end if
end floor
 
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from 1 to lng
            set v to |λ|(v, item i of xs, i, xs)
        end repeat
        return v
    end tell
end foldl
 
-- foldr :: (b -> a -> a) -> a -> [b] -> a
on foldr(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from lng to 1 by -1
            set v to |λ|(item i of xs, v, i, xs)
        end repeat
        return v
    end tell
end foldr
 
-- id :: a -> a
on |id|(x)
    x
end |id|
 
-- intercalate :: Text -> [Text] -> Text
on intercalate(strText, lstText)
    set {dlm, my text item delimiters} to {my text item delimiters, strText}
    set strJoined to lstText as text
    set my text item delimiters to dlm
    return strJoined
end intercalate
 
-- isNull :: [a] -> Bool
on isNull(xs)
    if class of xs is string then
        xs = ""
    else
        xs = {}
    end if
end isNull
 
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map
 
-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min
 
-- Lift 2nd class handler function into 1st class script wrapper 
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn
 
-- quot :: Int -> Int -> Int
on quot(m, n)
    m div n
end quot
 
-- range :: Ix a => (a, a) -> [a]
on range({a, b})
    if class of a is list then
        set {xs, ys} to {a, b}
    else
        set {xs, ys} to {{a}, {b}}
    end if
    set lng to length of xs
 
    if lng = length of ys then
        if lng > 1 then
            script
                on |λ|(_, i)
                    enumFromTo(item i of xs, item i of ys)
                end |λ|
            end script
            sequence(map(result, xs))
        else
            enumFromTo(a, b)
        end if
    else
        {}
    end if
end range
 
-- sequence :: Monad m => [m a] -> m [a]
-- sequence :: [a] -> [[a]]
on sequence(xs)
    traverse(|id|, xs)
end sequence
 
-- show :: a -> String
on show(e)
    set c to class of e
    if c = list then
        script serialized
            on |λ|(v)
                show(v)
            end |λ|
        end script
 
        "[" & intercalate(", ", map(serialized, e)) & "]"
    else if c = record then
        script showField
            on |λ|(kv)
                set {k, ev} to kv
                "\"" & k & "\":" & show(ev)
            end |λ|
        end script
 
        "{" & intercalate(", ", ¬
            map(showField, zip(allKeys(e), allValues(e)))) & "}"
    else if c = date then
        "\"" & iso8601Z(e) & "\""
    else if c = text then
        "\"" & e & "\""
    else if (c = integer or c = real) then
        e as text
    else if c = class then
        "null"
    else
        try
            e as text
        on error
            ("«" & c as text) & "»"
        end try
    end if
end show
 
-- splitAt :: Int -> [a] -> ([a],[a])
on splitAt(n, xs)
    if n > 0 and n < length of xs then
        if class of xs is text then
            {items 1 thru n of xs as text, items (n + 1) thru -1 of xs as text}
        else
            {items 1 thru n of xs, items (n + 1) thru -1 of xs}
        end if
    else
        if n < 1 then
            {{}, xs}
        else
            {xs, {}}
        end if
    end if
end splitAt
 
-- swap :: (a, b) -> (b, a)
on swap(ab)
    set {a, b} to ab
    {b, a}
end swap
 
-- traverse :: (a -> [b]) -> [a] -> [[b]]
on traverse(f, xs)
    script
        property mf : mReturn(f)
        on |λ|(x, a)
            |<*>|(map(curry(cons), mf's |λ|(x)), a)
        end |λ|
    end script
    foldr(result, {{}}, xs)
end traverse
 
-- unlines :: [String] -> String
on unlines(xs)
    intercalate(linefeed, xs)
end unlines
 
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
    set lng to min(length of xs, length of ys)
    set lst to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, item i of ys)
        end repeat
        return lst
    end tell
end zipWith