# Generate this huge right triangle

-
||
-|-
|---
|-|--
-|-|--
-|-----
|-|-----
|---|-|--
-|-----|--
---|-|-----
|---|-|-----
|---|-----|--
-----|---|-|--
-|-|---|-------
------|---|-----
|-|---------|-|--
---|-----|---|----
-|-----|-|---------
|-|---|-|-----------
|-----------|---|-|--
-|-----|-|---------|--
---|-----|-----|-|-----
|---|-|---------|-------
------|---|-|---|--------
-----|-----|---------|-|--
-|-----|-------|-----|-----
|---|-----|-------|---|-----
--|---------|-|---------|-|--
---|---|-----|-------|---|-|--
-|-----------|-------|---|-----
--|---|-----|-----------|-|-----
------------|-----|---------|----
-|-----|-|-----|---------|-----|--
---|-|-----|-----|---|-|-----------
|---------|-|---|-----|-----|-|-----
------|---|-----|-------|---------|--
-----|---------|-------|-----|-----|--
-|-------|-----|---|-------|---|-------
------|---------|-----------|-|---------


It at first look seems meaningless repititions of - and |, but it has a nice hidden pattern inside it. I won't reveal it now.

Binary matrix or 2D lists are not accepted as output. Exactly this text is required. But output can have trailing whitespace.

You can use any non-number character in place of - and |

Shortest code wins.

• Welcome to Code Golf! This looks like a reasonably well-specified challenge, but for future reference, we strongly recommend using the Sandbox to get feedback on challenge ideas before posting them to the main site. For example, posting challenges with "hidden" patterns is strongly discouraged. Jul 11 at 6:17
• (for those who haven't figured it out, the |s are at the prime positions, 1-indexed) Jul 11 at 6:19
• Is a leading newline ok? Jul 11 at 6:44
• can we take the number of lines as input? Jul 11 at 10:45

# Python 2, 70 bytes

p=n=i=1
while i<41:s='';exec"s+='-|'[p%n];p*=n*n;n+=1;"*i;i+=1;print s


Try it online!

The |s mark the position of primes.

The idea here, as with many prime-related tasks in Python, is to use Wilson's theorem. Essentially, $$\ (n - 1)!^2 \bmod n \$$ will equal 1 if $$\ n \$$ is prime, and 0 if it is not. @xnor gives a more in-depth explanation here if you're interested.

• Alwin left a message in chat with a bit of a spoiler as to how this works, as well as a request for a more in-depth explanation of the specific mathematical trick used here (I'm being deliberately vague, their chat message should make it clearer) Jul 12 at 1:51
• @cairdcoinheringaahing I've given a short explanation as to how this works, as requested. Jul 12 at 3:36

# J, 29 26 25 bytes

({&'-|'/.1 p:#\)@(#~i.41)


Try it online!

-3 thanks to an idea from Lynn!

-1 thanks to xash!

• Maybe something like (]/.'-|'{~1 p:#\)#~i.41 works (I don't understand the rules for posting J here well enough to know if that counts as a full answer or not, but the main idea is to use #\ ).
– Lynn
Jul 11 at 15:33
• Thanks Lynn! The rules are just that the submission must be assignable to a function like f=:<your submission>. In this case it meant we just had to surround in parenthesis and use the @ since the verb@noun creates another legal verb. Jul 11 at 15:56
• -1 with inlining to ({&'-|'/.…. Nice way to create the triangle.
– xash
Jul 11 at 16:44

# Jelly, 11 bytes

40RRȷR¤ṁẒỌY


Try It Online!

The |s are at prime indexes

I figured out the pattern by smash-printing the rows into one flat string and realizing that the distribution looked familiar :P I had another idea to use base conversion but I decided to flatten the string first and then noticed.

Leaky Nun has two tricks in their answer - firstly, that you can take the range up to 1000 (which is shorter) since mold will discard extras, and secondly, I didn't notice other characters were allowed, so the digraph nilad ØA is shorter. Jonathan Allan also noticed that we can just use chr and output the unprintables 0x00 and 0x01, so thanks to both of them for the byte saves. Please go upvote Leaky Nun's answer as well.

40RRȷR¤ṁẒỌY  Main Link (Niladic)
40           Start at 40
R          Range; [1, 2, ..., 40]
R         Range; [[1], [1, 2], [1, 2, 3], ...]
ṁ     Mold; to the shape of [[1], [1, 2], [1, 2, 3], ...], reshape
ȷR¤      Range of 1000 ([1, 2, 3, ..., 1000]) - extra elements are discarded
Ẓ    x => isprime(x)
Ọ   chr
Y  Join on newlines


There are a couple of ways to go about this idea - you can swap which list you generate first, you can index into a multitude of other strings, you can prime-check before or after molding since it vectorizes and thus doesn't really care what shape it is, etc.

• No need to index into anything, Ọ will do fine by the given rules. Jul 11 at 14:02
• @JonathanAllan ah, good find Jul 11 at 14:28

# Jelly, 13 bytes

ȷRẒṁ40RR¤ịØAY


Try it online!

Uses Z and A instead of - and |.

This is basically the same answer as @hyper-neutrino's answer, which was published 3 minutes before mine, but our answers arose independently.

• There doesn't seem to be anything wrong with ȷRẒṁ40RR¤ỌY which saves two. Jul 11 at 13:58

# Jelly, 8 bytes

ØYxJĠẒỌY


Try it online!

ØY         The string "BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz"
xJ       Repeat "B" once, "C" twice, "D" thrice... "z" 40 times.
Ġ      Group indices by values: [[1], [2,3], [4,5,6], …, […820]]
↑B   ↑C     ↑D          ↑z
Ẓ     Is prime?
Ọ    Translate 0,1 into "\x00","\x01"
Y   Join lines


# Excel, 110 bytes

=LET(a,SEQUENCE(820),CONCAT(IF(MMULT((MOD(a,TRANSPOSE(a))=0)*1,a^0)=2,"|","-")&IF(MOD(SQRT(8*a+1),1),"","
")))


=LET(a,SEQUENCE(820),                               a = (1..820)
CONCAT(                                             concatenate the following
IF(MMULT((MOD(a,TRANSPOSE(a))=0)*1,a^0)=2,"|","-")& if a is prime (has 2 factors) then "|" else "-"
IF(MOD(SQRT(8*a+1),1),"","                          if a is triangular (8a+1 is a perfect square) then add a line feed
")))


# 05AB1E, 14 bytes

A820ÅTüŸ€¦pèJ»


Try it online!

-1 byte thanks to ovs

A820ÅT>üŸ€¦pèJ»  Full Program
A                Push "abc...xyz"
820             Push 820
ÅT           Pop 820; Push [0, 1, 3, 6, 10, ...] (triangular numbers)
üŸ         Pairwise range; get [[0, 1], [1, 2, 3], [3, 4, 5, 6], ...]
€¦       For each sublist, remove the first element
p      Primality; get the 2D list of 1s and 0s
è     Index into "abc...xyz"
J    Join each row into a string
»  Join the result into a multiline string

• >üŸ€¨ -> üŸ€¦ for -1
– ovs
Jul 11 at 13:10
• And there a few other ways to get the list of triangular numbers, such as 41L2c and 820ÅT, but I couldn't find anything shorter.
– ovs
Jul 11 at 13:15
• @ovs Oh, oops, I was on the wrong branch's info.txt so I didn't see the triangular numbers built-in. Thanks! Although, isn't 820ÅT the same length as 40ÝÝO, or am I missing something? Jul 11 at 15:33
• Yeah it is the same length, I should've been a bit clearer in my previous comment ;). Just wanted to point out that builtin exists.
– ovs
Jul 11 at 15:38
• I've posted my 9-byter as a separated answer, since the approach is fairly different. It would have been 10 bytes if I'd use characters ab as well. (PS: There is a small error in your explanation: the Pop 40; ... should be Pop 820; .... Probably a left-over from your initial 40ÝÝO?) Aug 23 at 9:57

# Pyth, 19 bytes

jmsm@GP_hkrZ=+ZdS40


Try it online!

Uses a and b instead of - and |.

# brainfuck, 1249 bytes

++++++++++[>+>++++>++++++<<<-]>>.<.>>..<<.>.>.<.<.>>.<...<.>>.<.>.<..<.>.>.<.>.<..<.>.>.<.....<.>>.<.>.<.....<.>>.<...>.<.>.<..<.>.>.<.....>.<..<.>...>.<.>.<.....<.>>.<...>.<.>.<.....<.>>.<...>.<.....>.<..<.>.....>.<...>.<.>.<..<.>.>.<.>.<...>.<.......<.>......>.<...>.<.....<.>>.<.>.<.........>.<.>.<..<.>...>.<.....>.<...>.<....<.>.>.<.....>.<.>.<.........<.>>.<.>.<...>.<.>.<...........<.>>.<...........>.<...>.<.>.<..<.>.>.<.....>.<.>.<.........>.<..<.>...>.<.....>.<.....>.<.>.<.....<.>>.<...>.<.>.<.........>.<.......<.>......>.<...>.<.>.<...>.<........<.>.....>.<.....>.<.........>.<.>.<..<.>.>.<.....>.<.......>.<.....>.<.....<.>>.<...>.<.....>.<.......>.<...>.<.....<.>..>.<.........>.<.>.<.........>.<.>.<..<.>...>.<...>.<.....>.<.......>.<...>.<.>.<..<.>.>.<...........>.<.......>.<...>.<.....<.>..>.<...>.<.....>.<...........>.<.>.<.....<.>............>.<.....>.<.........>.<....<.>.>.<.....>.<.>.<.....>.<.........>.<.....>.<..<.>...>.<.>.<.....>.<.....>.<...>.<.>.<...........<.>>.<.........>.<.>.<...>.<.....>.<.....>.<.>.<.....<.>......>.<...>.<.....>.<.......>.<.........>.<..<.>.....>.<.........>.<.......>.<.....>.<.....>.<..<.>.>.<.......>.<.....>.<...>.<.......>.<...>.<.......<.>......>.<.........>.<...........>.<.>.<.........


Try it online!

Kind of boring; just a hardcoded solution. I'm trying to create a solution that computes it with the prime relation, but I'm not that experienced in brainfuck and also I'll need to find an interpreter that goes up to 820 if I don't want excessive pain and suffering.

• -11 bytes with a slightly shorter initializer.
– ovs
Jul 11 at 9:00

# Vyxalj, 16 bytes

40ɾƛʁƛkA¥›:£æi;ṅ


Try it Online!

-1 byte thanks to @hyper-neutrino

Replace all those characters with numbers from 1, 2 and so on. The |'s are at prime indices. So then it's easy.

(pxeger spoiled it, though I came to it independently)

• Actually, following a tip from Jonathan Allan, you can use \x00 and \x01 so you can just use chr instead of indexing: Try it Online! Jul 11 at 18:20
• 13 bytes Jul 12 at 1:18

# APL (Dyalog Extended), 23 19 bytes

‒1 thanks to Razetime.

Full program.

↑⎕UCS{1⍭⍳≢⍵}⍢∊,\⍳40


Try it online!

⍳40indices 1 through 40

,\ prefixes of that; [1,[1,2],[1,2,3],,39,40]]

{}⍢∊ apply the following anonymous lambda to the enlisted (flattened) data:

⍵ the argument; [1,1,2,1,2,3,,39,40]

≢ the length of that; 820

1⍭ indicate which elements are prime; [0,1,1,0,1,0,,0,0]

⎕UCS convert to characters in the Universal Character Set; [\x00,\x01,\x01,\x00,\x01,\x00,,\x00,\x00]

↑ mix the list of strings into a matrix, padding with spaces as necessary to make it rectangular; ["\x00 ","\x01\x01 ",\x00\x00\x00\x00\x00\x00\x00\x00"]

• may want to use ⎕A instead of '-|' Jul 11 at 8:29
• @Razetime Ah, right.
Jul 11 at 8:30
• @Shaggy Oh, right. I read that as not being able to return numbers. My bad; should be fixed now.
Jul 12 at 8:59

# brainfuck, 223 bytes

-[>-<-----]>[>++++[<[[->>>+<<<]>]<+>>>-]<-]>>-[--->+<]>[<<<<<[>+[[>]>]>>[->+>]>[[-<+>]<<[-<<<[[<]<]>>[-]-[[>]>]>]+>>>>]<<<<<<<[[<]<]<]>+[->>>]<[-<+>]<-]<<<+<+<--[>[[[-<<<+>>>]>]<<-[---<+>]<.[-]<+<-]++++++++++.[-]>[-<+>]+<<]


Try it online!

Uses trial division up to 86 to determine every prime up to 820. Outputs T for - and V for |.

Populate list of 820 numbers not yet considered composite
-[>-<-----]>[>++++[<[[->>>+<<<]>]<+>>>-]<-]

tape is 1 0 0 1 0 0 1 etc 1 0 0 1 0 (0)

Create 85 to test visibility by up to N=86 (which is high enough and short to create)
>>-[--->+<]>

working tape will be 1 0 0 1 0 0 1 etc 1 1 0 1 1 0 (0 or 1) x y 0 0
with x plus y = 85 in first iteration

For all numbers from 85 down to 1: (N is this number plus one)
[

For all numbers to sieve:
<<<<<[

Mark number seen
>+

Decrement x and increment y if x is nonzero
[[>]>]>>[->+>]

Otherwise, the last number was divisible by N
>[

Move y back to x
[-<+>]

If this isn't the first time we've seen a multiple of N:
<<[

Mark number composite
-<<<[[<]<]>>[-]-[[>]>]>

Mark that we've seen a multiple of N
]+>>>>

]

Go to next number (for loop termination condition)
<<<<<<<[[<]<]<

Repeat until all 820 numbers checked
]

Remove all "seen" markings and "seen multiple of N"
>+[->>>]

Copy whatever was left of y back to x and decrement
<[-<+>]<-
]

Now all numbers have been checked and marked composite if applicable
We are 5 cells right of first number (1) now

Set up counters for triangle shape (x=1 for first row; and y=1 so y is always nonzero)
<<<+<+

Mark 1 as nonprime
<--

For each number
[

While x is nonzero
>[

Move x and y left three cells
[[-<<<+>>>]>]

Add 85 to primality indicator to output ASCII
<<-[---<+>]

Output and remove number
<.[-]

Increment y and decrement x
<+<-

]

Output newline
++++++++++.[-]

Copy y to x (now one more than it was; restore y=1)
>[-<+>]+

<<]



# Python 3, 105 bytes

r=range
for i in r(1,41):
for j in r(i):n=i*~-i//2-~j;print(end="-|"[all(n%y for y in r(2,n))])
print()


Try it online!

One byte longer than wasif's, but no exec trick used here.

• You can save 2 bytes by doing some extra unnecessary work: Try it online! (: Aug 25 at 9:32

# Python 3.8 (pre-release), 98 bytes

r=range
for _ in r(n:=1,41):s="";exec("s+='-|'[all(n%y for y in r(2,n))and n>1];n+=1;"*_);print(s)


Try it online!

-6 bytes thanks to @hyper-neutrino and @pxeger

• why 46? (padding) Jul 11 at 6:39
• 101 bytes, and the 46 should be a 41 (consistent tip - if you want to output a single string without a trailing newline, end=x is shorter than x,end="") Jul 11 at 6:46
• 98 bytes Jul 11 at 6:49
• @hyper-neutrino sorry was a mistake Jul 11 at 7:07
• @pxeger thanks! Jul 11 at 7:07

# JavaScript (ES7), 78 bytes

A recursive function going from $$\k=820\$$ to $$\k=1\$$ and testing whether $$\\sqrt{8k+1}\$$ is an integer to detect triangular numbers and figure out where line-feeds have to be inserted.

f=(k=820)=>k?f(k-1)+'-|'[(g=n=>n%--d?g(n):+!~-d)(d=k)]+[
[(1+8*k)**.5%1]]:''


Try it online!

# Japt-R, 13 11 bytes

Uses f for composites and t for primes.

#(õ_ç Ëî°Tj


Test it

#(õ_ç Ëî°Tj
#(              :40
õ             :Range [1,40]
_            :Map each Z
ç           :  Z spaces (could be any character)
Ë         :  Map each character
î        :    Slice the following to length (always 1)
°T      :      Prefix increment T (initially 0)
j     :      Is prime? (Returns a Boolean which gets coerced to a string by î)
:Implicit output joined with newlines


# Japt-R, 13 bytes

Preserving this version as I quite liked the ú trick. Uses q for qomposites(!) and p for primes.

40õ_ç¬Ëú²g°Tj


Test it

40õ_ç¬Ëú²g°Tj
40õ_              :Map each Z in the range [1,40]
ç             :  Repeat to that length
¬            :    "q"
Ë           :  Map each character
²         :    With "p" to length 2
g        :    Get the character at index
°Tj     :      As above, with the Boolean this time coerced to 0 or 1
:Implicit output joined with newlines


# Vyxalj, 11 14 bytes

40ƛɽ÷Ṡnɾ+æ₁+Cṅ


Try it Online!

I'm posting this because it's significantly shorter than the other one. I didn't realise the non-number rule, but still fairly short.

40ƛ            # Map 1...40 to...
ɽ÷Ṡ         # Sum of 1...n-1
nɾ+      # Added to each of 1...n, this produces the correct indices
æ     # Is prime (vectorised)
₁+C  # Add 100 and get the corresponding character (d or e)
ṅ # Joined
# (j flag) join by newlines

• @Razetime Oops. Jul 12 at 8:53

# C (gcc), 101 $$\\cdots\$$ 99 98 bytes

p;r;d;f(n){for(n=r=0;++r<41;puts(""))for(;r*-~r>2*n;printf(L"|-"+!p))for(p=d=n++;d>1;p=n%d--&&p);}


Try it online!

Saved a byte (and got below $$\100\$$!) thanks to ceilingcat!!!
Saved another byte thanks to Shaggy!!!

• 98 bytes Jul 13 at 14:40
• @Shaggy Nioce one - thanks! :D Jul 13 at 21:13

# 05AB1E, 9 bytes

∞40L£pçJ»


Uses unprintables \0 and \1 instead of - and |.

Try it online.

Explanation:

∞         # Push an infinite positive list: [1,2,3,...]
40L      # Push list [1,2,3,...,38,39,40]
£     # Split the first list into parts with the lengths of the second list
# (removes any unused trailing items): [[1],[2,3],[4,5,6],...]
p    # Check for each integer whether it's a prime (1 if it's a prime; 0 otherwise)
ç   # Convert it to a character with this codepoint: \0 and \1
J  # Join each inner list together to a single string
» # And join those strings by newlines
# (after which the result is output implicitly)


# Factor, 75 bytes

820 [1,b] [ prime? 43 45 ? ] ""map-as 40 [1,b] [ cut swap print ] each drop


Try it online!

• 820 [1,b] Create a range from 1 to 820 inclusive. 820 is the number of elements in the triangle sans newlines.
• [ prime? 43 45 ? ] ""map-as Create a flat version of the triangle as a string, where 43 is the code point for + and 45 is the code point for -. |'s code point has 3 digits so I changed this (as it is allowed by the rules).
• 40 [1,b] [ cut swap print ] each drop Cut the sequence in two at ever-increasing indices, printing each line after it is cut off from the main sequence.

# Retina 0.8.2, 60 bytes


820$*| .(?<=^\1+(..+)) - ^. -¶ +(.)+¶(?=.+$)(?<-1>.)+.
$&¶  Try it online! Explanation:  820$*|


Insert 820 |s. (820 = 40 × 41 ÷ 2)

.(?<=^\1+(..+))
-


Change most of them to -s appropriately.

^.
-¶


Change the first one too, and start building the triangle.

+(.)+¶(?=.+$)(?<-1>.)+.$&¶


Build up each row of the triangle to be one character longer than the previous.

# Charcoal, 25 bytes

Ｇ↓→⁴⁰ψ¤⭆⁸²⁰§-|﹪ΠＥ⊕ι∨λ±¹⊕ι


Try it online! Link is to verbose version of code. Explanation:

Ｇ↓→⁴⁰ψ


Draw an empty triangle of side 40. (Unfortunately Charcoal doesn't have a 1-byte shortcut for a triangle in this orientation.) The y variable is the null byte, which has a special meaning for the Fill function, in that it counts as a fillable area.

¤⭆⁸²⁰§-|﹪ΠＥ⊕ι∨λ±¹⊕ι


Calculate the first 820 factorials, negate them, reduce them modulo the incremented index, and index the results into the string -|. Use this to fill the triangle drawn above. (Charcoal's Fill works by painting the string across the region to be filled, rather than repeating it horizontally and vertically. See @ASCII-only's answer to Bake a slice of Pi for another example of how it works.)

# Julia 1.0, 76 bytes

f(y=1)=1:40 .|>i->print.(y:(y+=i).|>j->"
-|"[j==y||2+(2==sum(j.%(1:j).<1))])


Try it online!

# Perl 5, 73 bytes

say join($/,map$"x$_,1..40)=~s/./!$n++|(grep$n%$_==0,2..$n-1)?"-":"|"/ger  Try it online! say # print triangle string, say adds a \n at the end join($/,map$"x$_,1..40) =~  # make multi-line triangle
# of $" (spaces) and$/ (newlines)
s/ /                        # replace spaces
!$p++ # count position, ! prevent first from becoming "|" | # bit-or instead of || or 'or' (grep$p%$_==0, 2..$p-1)  # check if $p position is a prime ? "-" # replace non-prime$p with -
: "|"                     # replace prime \$p with |
/ger                        # g=replace all, e=eval, r=return triangle to say


# Husk, 10 bytes

Cḣ40ṁȯs¬ṗN


Try it online!

Derived independently to Kevin Cruijssen's 05AB1E answer, but it turns-out to use almost exactly the same approach... Unfortunately Husk's prime-checker ṗ returns the index in the list of primes, rather than 1 or 0, costing one extra byte.

    ṁȯ       # map over
N   # the infinite list of integers:
s      # string value of
¬     # logical NOT of
ṗ    # index in list of primes;
C            # now cut into substrings
ḣ40         # with lengths of 1..40