BPL, APL and BQN side by side

See also the language principles, the language rules and the glyph index.

Each entry shows one task in Dyalog APL, BPL and BQN. Most entries come from existing code: APLcart, the dfns workspace, the ngn/apl and April test suites, and BQN’s documentation. A few were written for this page. An entry leaves out a language that has no equivalent.

The Dyalog lines use Dyalog’s default index origin, ⎕IO←1, unless the entry says otherwise. BPL and BQN count positions from 0. Where an entry builds sample data with ⍳ or ↕, Dyalog’s data starts at 1 and the others’ data starts at 0. Their results differ for that reason.

The BPL blocks set sample values and show each result after ⍝. The page tests run them. The BQN lines were run with BQN’s JavaScript implementation, with sample values for their free names. BQN reserves uppercase names for functions, and its lines use lowercase names for arrays.

Selecting items

Sort

BPL selects with Index, ⌷, which takes the positions on its left. Brackets always write a vector. [⍋⍵] holds one item, the positions along the one axis of ⍵. BQN sorts with one glyph, ∧.

{⍵[⍋⍵]}
v←3 1 2
{[⍋⍵]⌷⍵} v             ⍝ 1 2 3
∧

Sort by a computed key

Source: APLcart. BPL’s ⌷ and BQN’s ⊏ both take the positions on the left.

Av{⍵[⍋⍺++\⍺]}Bv
Av←3 1 2 ⋄ Bv←"abc"
Av{[⍋⍺++\⍺]⌷⍵}Bv       ⍝ "bac"
av {(⍋𝕨++`𝕨)⊏𝕩} bv

Sort rows by a column

Source: APLcart. [∞ ⍺]⌷⍵ is column ⍺ of ⍵. Index takes one position for each axis, with ∞ for a whole axis. The outer ⌷ has one item, the grade, so it selects rows.

I{⍵[⍋⍵[;⍺];]}Ym
I←1 ⋄ Ym←[5 3 ⋄ 1 9 ⋄ 4 2]
I{[⍋[∞ ⍺]⌷⍵]⌷⍵}Ym      ⍝ [4 2 ⋄ 5 3 ⋄ 1 9]
i {(⍋𝕨⊏˘𝕩)⊏𝕩} ym

Lookup table

Source: APLcart. The 1+ goes, because positions count from 0. ⁻¹ inverts ⊥ without a count, so nothing needs to separate it from the argument. ⌷ takes the table •d,•a on its right, and the brackets make the digits one item of positions. BQN has no encode primitive. Its line writes out the base conversion.

{(⎕D,⎕A)[1+16⊥⍣¯1⊢⍵]}
{[16⊥⁻¹⍵]⌷•d,•a} 255    ⍝ "FF"
{"0123456789ABCDEF"⊏˜16{⌽𝕗|⌊∘÷⟜𝕗⍟(↕1+·⌊𝕗⋆⁼1⌈⊢)𝕩}𝕩}

Translate characters

Source: lcase in the dfns workspace. The table is lc,,⍵, and the positions are (uc,,⍵)⍳⍵. A selection has the shape of its positions. Here that is the shape of ⍵. The BPL spelling leaves out the original’s (⍴⍵)⍴.

(⍴⍵)⍴(lc,,⍵)[(uc,,⍵)⍳⍵]
lc←"abcdefghijklmnopqrstuvwxyz" ⋄ uc←•a
{[(uc,,⍵)⍳⍵]⌷lc,,⍵} "HeLLo!"   ⍝ "hello!"
{(lc∾⥊𝕩)⊏˜(uc∾⥊𝕩)⊐𝕩}

An index that assigns the array’s name

Source: the ngn/apl tests. ⌷ evaluates its right argument first, so the assignment moves to the right. x←6?49 runs before [⍋x] reads x. BQN sorts with ∧ and needs no index.

⍴x[⍋x←6?49]
⍴[⍋x]⌷x←6?49           ⍝ [6]ₓ
≢∧6 •rand.Deal 49

Indexing inside arithmetic

Spaces replace the parentheses. 0⌷v*2, + and 1⌷v*2 are three runs, and each run is evaluated first. ⌷ takes everything to its right, so each run squares v and then selects one item. The BQN line sums the squares of the first two items.

(v[1]*2)+v[2]*2
v←3 4 5
0⌷v*2 + 1⌷v*2          ⍝ 25
+´×˜2↑v

Indexing inside a longer expression

Source: path in the dfns workspace. ⍺ is the wave front, a vector of vertices, and [⍺]⌷graph selects their lists of neighbours. The brackets make ⍺ one item, the positions along the only axis. The selection needs parentheses, because ⌷ takes everything to its right as its right argument.

next←graph[⍺]∩¨⊂⍸⍵=¯2
graph←[[1 2] [2] [0] [1]]
0 1 {([⍺]⌷graph)∩¨⊂⍸⍵=¯2} ¯2 0 ¯2 5   ⍝ (2 ⋄ 2)
next←(𝕨⊏graph)(∊/⊣)¨</𝕩=¯2

Chained indexing

Source: the April tests. ⌷ takes one item for each axis, like APL’s index fields. ; separates the items. ∞ takes a whole axis, in place of an empty field.

(6 8 5⍴⍳9)[1 4;;2 1][1;2 4 5;]
[0;1 3 4]⌷[0 3;∞;1 0]⌷6 8 5⍴⍳9     ⍝ [6 5 ⋄ 7 6 ⋄ 3 2]
⟨<0,1‿3‿4⟩⊏⟨0‿3,↕8,1‿0⟩⊏6‿8‿5⥊↕9

Choose indexing

Source: the April tests. When the positions are vectors, ⌷ reads each one as a coordinate. [0 1;2 0] is a list of two coordinates, and the outer brackets make it one item. ; separates items that contain spaces.

(3 4⍴⍳9)[(1 2)(3 1)]
[[0 1;2 0]]⌷3 4⍴⍳9    ⍝ 1 8
⟨0‿1,2‿0⟩⊑3‿4⥊↕9

Two sorts, each enclosed

Source: the dfns string examples. Brackets write the pair directly, where APL builds it with ⊂ and ,. Inside brackets, each unspaced expression is one item. lcase⍵ needs no space, because ⍵ is a glyph, not a name.

{(⊂⍵[⍋lcase ⍵]),⊂⍵[⍋⍵]}
lcase←{[(•a,,⍵)⍳⍵]⌷"abcdefghijklmnopqrstuvwxyz",,⍵}
{[[⍋lcase⍵]⌷⍵ [⍋⍵]⌷⍵]} "baC"   ⍝ "abC" "Cab"
{⟨𝕩⊏˜⍋Lcase 𝕩, ∧𝕩⟩}

Dividing a row by its pivot

Source: Gauss-Jordan elimination in APLcart. The spaces make [⍺ ⍺]⌷swap, the pivot, the left argument of ÷⍨@⍺. The operand ⍺ ends at the space, and no ⊢ is needed before the right argument.

mat←swap[⍺;⍺]÷⍨@⍺⊢swap
swap←[2 4 ⋄ 6 8]
0 {[⍺ ⍺]⌷swap ÷⍨@⍺ swap} 0   ⍝ [1 2 ⋄ 6 8]
mat←(÷⟜(𝕨‿𝕨⊑swap))⌾(𝕨⊸⊏)swap

Assigning

Assigning to a block of a matrix

Source: box in the dfns workspace. Dot indexing takes one item for each axis, like APL’s bracket index. 0,h and 0,w have no spaces, and each is one item. The target of ← is the run before it. BQN assigns through a selection with Under, ⌾.

q[1,h;1,w]←2 2⍴ch
q←5 5⍴0 ⋄ h←4 ⋄ w←4 ⋄ ch←1 2 3 4
q.[0,h 0,w]←2 2⍴ch
q   ⍝ [1 0 0 0 2 ⋄ 0 0 0 0 0 ⋄ 0 0 0 0 0 ⋄ 0 0 0 0 0 ⋄ 3 0 0 0 4]
q↩(2‿2⥊ch)⌾(⟨0∾h,0∾w⟩⊸⊏)q

Updating selected items from other items

Source: sudoku in the dfns workspace. [j]⌷q selects the items at the positions j, and the target q.[j] assigns to them. The spaces end the run [j]⌷q, because ⌷ takes everything to its right. The target needs no parentheses, because the target of ← is the run before it. BQN writes “without” as ¬∘∊/⊣. A vector of vectors prints in parentheses, with ⋄ between its items: (4 ⋄ 1 2) is [[4] [1 2]].

q[j]←q[j]~¨⊂,/q[i~j]
q←[[1 2 3] [1 2] [2 4] [3 4 5]] ⋄ i←0 1 2 ⋄ j←0 2
q.[j]←[j]⌷q ~¨ ⊂,/[i~j]⌷q
q   ⍝ (3 ⋄ 1 2 ⋄ 4 ⋄ 3 4 5)
q↩((¬∘∊/⊣)⟜(∾(i(¬∘∊/⊣)j)⊏q))¨⌾(j⊸⊏)q

Keys and paths

Dyalog and BQN have no keyed arrays. Most entries here show BPL alone. See axis keys for the full rules.

Reading by keys

Keys on an axis name its positions. ⌷ takes one key for each axis.

sales←["city":["NY" "LA"] "month":["Jan" "Feb" "Mar"]]:[10 20 30 ⋄ 40 50 60]
"LA" "Feb"⌷sales       ⍝ 50

Reducing along a named axis

⍠ applies a function along the axes its operand names. The result keeps the keys and the name of the remaining axis. BQN has no axis names. It reduces by position, as +´˘ does along the last axis.

sales←["city":["NY" "LA"] "month":["Jan" "Feb" "Mar"]]:[10 20 30 ⋄ 40 50 60]
+/⍠"month" sales       ⍝ ["city":2]⍴["NY":60 "LA":150]

Selecting from a result

⌷ takes everything to its right, so "NY"⌷+/sales totals each row and then selects the row for NY. Pandas writes the selection after the sum: sales.sum(axis=1)["NY"].

sales←["city":["NY" "LA"] "month":["Jan" "Feb" "Mar"]]:[10 20 30 ⋄ 40 50 60]
"NY"⌷+/sales           ⍝ 60

Assigning to new keys

Assigning to a key that doesn’t exist adds it. The inner brackets hold the two keys as one item, for the vector’s one axis. The target needs no parentheses, because the target of ← is the run before it.

T←["name":"Ann"]
T.[["age" "city"]]←37 "London"
T   ⍝ ["name":"Ann" "age":37 "city":"London"]

Assigning through a path

Source: the XML guide. A path of names and dot indexes reads and writes nested keyed values. Assignment through a path creates any record missing along it. A BQN namespace has a fixed set of fields, and code outside it can’t assign them.

pic←["tag":"svg" "children":[["tag":"circle" "attrs":["fill":"red"]]]]
pic.children.[0].attrs.fill←"steelblue"
pic.children.[0].attrs.fill   ⍝ "steelblue"

Looking up a value in an association list

Source: lisp in the dfns workspace. The Dyalog code keeps the list as a two-column matrix of names and values. BPL keeps it as a keyed vector, and ⍵⌷⍺ selects the value for the key ⍵. BQN keeps the keys and the values as two lists.

⍺[⍺[;1]⍳⊂⍵;2]
["x":1 "y":2] {⍵⌷⍺} "y"   ⍝ 2
(⊑k⊐<𝕩)⊑v

Plotting a keyed matrix

Source: the home page. Each row is one series. The column keys label the x axis. "legend":"end" names each line by its row key. See plots.

sales←["city":["NY" "LA"] "month":["Jan" "Feb" "Mar"]]:[10 20 30 ⋄ 40 50 60]
["legend":"end"] •plot sales

Runs, trains and operands

Rank with a numeric operand

An operator takes one item to its right. In APL, ⊢ separates the operand 1 from the argument, because arrays side by side strand into one operand. BPL has the same problem only with literals: 1 2 3 is one strand. A space ends the operand. Brackets keep a literal argument out of it. With a name as the argument, the space is enough: +/⍤1 m.

+/⍤1⊢2 3⍴⍳6
+/⍤1 [2 3]⍴⍳6          ⍝ 3 12
+´˘2‿3⥊↕6

A named operator with a numeric operand

Source: Depth in the dfns workspace. It applies its left operand at the depths that its right operand gives, as BQN’s ⚇ does. A run that ends in an operator takes the next run as its right operand, so +Depth 0 is one function with 1 2 as its left argument. The brackets keep 3 4 out of the operand.

1 2(+Depth 0)3 4
•load "lib/dyalog.bpl"
1 2 +Depth 0 [3 4]     ⍝ 4 6
1‿2 +⚇0 3‿4

Reducing several axes

⍠ applies a function along the axes its operand lists. The operand 1 2 is a strand, and the name n doesn’t join it. Dyalog and BQN ravel each cell first.

+/,⍤2⊢n
n←2 3 4⍴⍳24
+/⍠1 2 n               ⍝ 66 210
+´∘⥊˘n

Stencil with a numeric operand

A space ends the operand. Brackets keep 1 2 3 4 out of it. +/∘⊢ reduces each window and ignores the padding counts that Stencil passes as its left argument. Windows with a negative size pad the ends, and they give the same result without Stencil. BQN has no Stencil. Its line pads with zeros.

{+/,⍵}⌺3⊢1 2 3 4
+/∘⊢⌺3 [1 2 3 4]       ⍝ 3 6 9 7
+/¯3↕1 2 3 4           ⍝ 3 6 9 7
+´˘3↕0∾1‿2‿3‿4∾0

Power with a list of counts

A list of counts gives the result for each count, as BQN’s Repeat does. The parentheses make ⍳5 one operand, because an operator takes one item to its right. 2 needs no Bind. Dyadic Power passes its left argument to × at each step. Dyalog has no list form, and its line applies Power once for each count.

{(2∘×⍣⍵)1}¨0,⍳4
2×⍣(⍳5) 1              ⍝ 1 2 4 8 16
2⊸×⍟(↕5)1

Mean

A run that ends in a function is a train. The space before 2 4 9 ends the run +/÷≢. The train needs no parentheses. Without the space, +/÷≢x is an expression: +/(÷(≢x)).

(+/÷≢)2 4 9
+/÷≢ 2 4 9             ⍝ 5
x←2 4 9
+/÷≢x                  ⍝ 1r3
(+´÷≠)2‿4‿9

A train that binds arrays

A subject directly before a function in a train binds to it: 1.8× is 1.8⍃×. 32 is the left part of the fork 32 + 1.8⍃×, as in Dyalog. Dyalog and BQN need ∘ and ⍃ to bind 1.8.

f←32+1.8∘× ⋄ f 100
f←32+1.8× ⋄ f100       ⍝ 212
F←32+1.8⊸× ⋄ F 100

Quadratic root

Runs replace two of the three pairs of parentheses. -b is one run, negated before the addition. b² - 4×a×c is three runs, and √ applies to the whole difference. Dyalog has no √.

((-b)+((b*2)-4×a×c)*0.5)÷2×a
a←1 ⋄ b←¯3 ⋄ c←2
(-b + √ b² - 4×a×c)÷2×a   ⍝ 2
((-b)+√(b⋆2)-4×a×c)÷2×a

A two-number operand before a named argument

Source: “Why not whitespace?” in BQN’s documentation. Without BQN’s ligature ‿, nothing says which of 1, ∞ and b belong together. In BPL, 1 ∞ is a strand, and the name b never joins it. BPL spells Atop ∘, as BQN does. A rank of ∞ takes the whole argument, as in BQN.

a +˝∘×⎉1‿∞ b
a←2 3⍴⍳6 ⋄ b←3 2⍴⍳6
a +⌿∘×⍤1 ∞ b           ⍝ [10 13 ⋄ 28 40]

A vector bound into a composition

Source: “Why not whitespace?” in BQN’s documentation. BQN tells the two readings apart with its ligature. In BPL and Dyalog, 3 1 is one operand, and the other reading needs parentheses. BPL writes BQN’s ⊸ as ⍃.

3 1⊸+⊸× 5    # 20
3‿1⊸+⊸× 5    # 40 30
3(1∘+⍛×)5
3 1∘+⍛×5
3 (1⍃+⍃×) 5            ⍝ 20
3 1⍃+⍃× 5              ⍝ 40 30

Lists

A list that mixes literals and names

Source: BQN’s documentation, on stranding in J and K. Only literals join a strand. A list that holds a name needs brackets. APL strands any arrays written side by side, and BQN joins them with ‿.

2‿n‿5+1
2 n 5+1
n←7
[2 n 5]+1              ⍝ 3 8 6

A list of computed values

Each unspaced expression inside brackets is one item. The list needs no parentheses round its items.

(⌊/x)(⌈/x)(+/x÷≢x)
x←2 4 9
[⌊/x ⌈/x +/x÷≢x]       ⍝ 2 9 5
⟨⌊´x, ⌈´x, +´x÷≠x⟩

Lists that hold functions

Source: BQN’s documentation. Inside brackets, every space separates items, and an item can be a function. [2 + 3] is a list of three items. [c t f]← destructures, as BQN’s header does. A Boolean selects from [f t] directly, because positions count from 0. Dyalog arrays can’t hold functions.

2‿+‿3
IfElse ← {c‿T‿F: c◶F‿T@}
≢[2 + 3]               ⍝ 3ₓ
ifelse←{[c t f]←⍵ ⋄ c⍚[f t]0}
ifelse [1 {⍵+10} {⍵+20}]   ⍝ 10

Long list items across lines

Source: the binary search in BQN’s documentation on control flow. A line break inside brackets counts as a space. Each item here has no spaces, and the list holds three items. BPL has no ↩︎. hi⊢←mid modifies the hi of the enclosing function.

IfElse (𝕩<mid⊑𝕨)‿{𝕤
  hi↩mid
}‿{𝕤
  lo↩mid
}
ifelse←{[c t f]←⍵ ⋄ c⍚[f t]0}
step←{mid←3 ⋄ hi←9 ⋄ lo←0
  _←ifelse [⍵<mid⊃⍺
    {hi⊢←mid}
    {lo⊢←mid}]
  [lo hi]}
0 1 2 3 4 5 step 1     ⍝ 0 3
0 1 2 3 4 5 step 4     ⍝ 3 9

A new leading axis

Array notation writes a new leading axis, for computed rows as well as literal ones. [x ⋄ y] stacks x and y as the rows of a matrix. BPL has no laminate.

1 2,[0.5]3 4
[1 2 ⋄ 3 4]            ⍝ 2 2⍴1 2 3 4
x←1 2 ⋄ y←3 4
[x ⋄ y]                ⍝ [1 2 ⋄ 3 4]
1‿2≍3‿4

Searching for one string

⊂"ab" encloses the string, and ⍳ searches for it as one item, as in Dyalog. A search for one item gives a plain number in BPL and Dyalog. BQN’s search functions always return an array, and BQN’s documentation suggests list⊸⊐⌾<elt to get a plain number.

names⍳⊂'ab'
names←"cd" "ab" "ef"
names⍳⊂"ab"            ⍝ 1ₓ
names⊸⊐⌾<"ab"

Counting from 0

Selecting by a Boolean

A Boolean is a valid position, and it selects without 1+. (x>0)⌷"no" "yes" selects one string from the strand. For an array of Booleans, BQN uses ⊏ in place of ⊑.

('no' 'yes')[1+x>0]
x←5
(x>0)⌷"no" "yes"       ⍝ "yes"
(x>0)⊑"no"‿"yes"

Caesar cipher

26| wraps a position round the alphabet directly.

a[1+26|2+a⍳t]
a←"abcdefghijklmnopqrstuvwxyz" ⋄ t←"hal"
[26|3+a⍳t]⌷a           ⍝ "kdo"
a⊏˜26|3+a⊐t

Programs

Determinant

Source: det in the dfns workspace. Each line shows the recursive step. The step takes the largest element as the pivot, then recurses on the reduced matrix. In BPL, each spaced part is one run: the left argument of ∇, then the terms of the update. The left argument needs no parentheses.

(⍺×⍵[i;j]ׯ1*i+j)∇ ⍵[k~i;k~j]-⍵[k~i;j]∘.×⍵[i;k~j]÷⍵[i;j]
det←{⍺←1 ⋄ 0=n←≢⍵:⍺ ⋄ [i j]←(⍴⍵)⊤{⍵⍳⌈/⍵}|,⍵ ⋄ k←⍳n ⋄ ⍺×⍵.[i j]ׯ1*i+j ∇ [k~[i] k~[j]]⌷⍵ - [k~[i] j]⌷⍵ ×⊗ [i k~[j]]⌷⍵ ÷ [i j]⌷⍵}
det [1 2 ⋄ 3 4]        ⍝ ¯2
det [2 0 1 ⋄ 1 3 2 ⋄ 1 1 2]   ⍝ 6
(𝕨×(⟨i,j⟩⊑𝕩)ׯ1⋆i+j)𝕊(⟨(k≠i)/k,(k≠j)/k⟩⊏𝕩)-(⟨(k≠i)/k,<j⟩⊏𝕩)×⌜(⟨<i,(k≠j)/k⟩⊏𝕩)÷⟨i,j⟩⊑𝕩

Roman numerals

Source: roman in the dfns workspace. The original sets ⎕IO←0, as BPL always counts. ⌷ selects from each table, with the positions on its left. ⊤ puts its digits on the last axis, so the encoded table in fmt has a row for each decimal digit, and [⍵⊤⍨4⍴10]⌷ selects a row for each digit of ⍵. Those rows form a matrix of positions, and ⊂ makes it one item for the outer ⌷. ~ searches the major cells of its right argument, so the blank to remove is the string " ". num uses windows, -/2↕⍵,0, in place of ⍵-1↓⍵,0. BQN has no encode primitive. Its Fmt lists the digit patterns directly.

num←{⎕IO←0 ⋄ {⍵+.××0.5+×⍵-1↓⍵,0}(,⍉1 5∘.×10*⍳4)[7|'IVXLCDMivxlcdm'⍳⍵]}
fmt←{⎕IO←0 ⋄ ~∘' ',1 0 0⍉(' '⍪3 4⍴'MCXI DLV ')[(0 4 2 2⊤0 16 20 22 24 32 36 38 39 28)[;⍵⊤⍨4⍴10];]}
num←{{⍵+.××0.5+×-/2↕⍵,0}[7|"IVXLCDMivxlcdm"⍳⍵]⌷,⍉1 5×⊗10*⍳4}
fmt←{~⍄" ",0 1 0⍉(⊂[⍵⊤⍨4⍴10]⌷0 4 2 2⊤0 16 20 22 24 32 36 38 39 28)⌷' '⍪3 4⍴"MCXI DLV "}
num "MCMXCIV"          ⍝ 1994
fmt1994                ⍝ "MCMXCIV"
Num←{{+´𝕩××0.5+×-´˘2↕𝕩∾0}(⥊⍉1‿5×⌜10⋆↕4)⊏˜7|"IVXLCDMivxlcdm"⊐𝕩}
Fmt←{∾"MMM"‿"CDM"‿"XLC"‿"IVX"⊏¨˜⟨⟨⟩,⟨0⟩,0‿0,0‿0‿0,0‿1,⟨1⟩,1‿0,1‿0‿0,1‿0‿0‿0,0‿2⟩⊏˜10|⌊𝕩÷1000‿100‿10‿1}

Value of a Roman numeral

Source: APLcart. The table ×\1,6⍴5 2 is the right argument of ⌷, where it needs no parentheses. BPL uses windows, </2↕, in place of 2</.

{(⊢+.ׯ1*2</,∘0)(×\1,6⍴5 2)['IVXLCDM'⍳⍵]}
{v←["IVXLCDM"⍳⍵]⌷×\1,6⍴5 2 ⋄ v+.ׯ1*</2↕v,0} "MCMXCIV"   ⍝ 1994
{v←(×`1∾6⥊5‿2)⊏˜"IVXLCDM"⊐𝕩 ⋄ +´vׯ1⋆<´˘2↕v∾0}

Rule 30

Source: the ngn/apl examples. ⊥3↕… decodes every window at once, in place of 2⊥¨3,/…. Each window is a row, and ⊥ decodes along the last axis. The lookup table " #" is the right argument of ⌷, and the brackets make the whole picture one item of positions. Inside the loop, [⊥…]⌷t selects from t. Positions count from 0, and both of the original’s 1+ go.

r←30 ⋄ n←8 ⋄ t←⌽r⊤⍨8⍴2 ⋄ ' #'[1+↑⌽{⍵,⍨⊂t[1+2⊥¨3,/0,0,⍨⊃⍵]}⍣n⊂z,1,z←n⍴0]
r←30 ⋄ n←8 ⋄ t←⌽r⊤⍨8⍴2
picture←[⊃⌽{⍵,⍨⊂[⊥3↕0,0,⍨↑⍵]⌷t}⍣n⊂z,1,z←n⍴0]⌷" #"
⍴picture               ⍝ [9 17]ₓ
r←30 ⋄ n←8 ⋄ t←2|⌊r÷2⋆↕8 ⋄ " #"⊏˜>{t⊏˜4‿2‿1+´∘×⎉1 3↕0∾𝕩∾0}⍟(↕n+1)z∾1∾z←n⥊0

The picture:

        #        
       ###       
      ##  #      
     ## ####     
    ##  #   #    
   ## #### ###   
  ##  #    #  #  
 ## ####  ###### 
##  #   ###     #