# Illustrate the Least Common Multiple

Given two positive integers, $$\A\$$ and $$\B\$$, illustrate their least common multiple by outputting two lines of dashes (-) with length $$\\text{lcm}\$$$$\(A, B)\$$ after replacing every $$\A\$$th dash in the first line and every $$\B\$$th dash in the second line with vertical bars (|).

In this way, the end of each line will be the only place two |'s line up.

For example, if $$\A = 6\$$ and $$\B = 4\$$, $$\\text{lcm}(6, 4) = 12\$$, so:

two lines of 12 dashes:
------------
------------

replace every 6th dash in the first line with a vertical bar:
-----|-----|
------------

replace every 4th dash in the second line with a vertical bar:
-----|-----|
---|---|---|


Thus the final output would be

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


The order of the input numbers should correspond to the order of the lines.

The shortest code in bytes wins.

# Testcases

A B
line for A
line for B

1 1
|
|

1 2
||
-|

2 1
-|
||

2 2
-|
-|

6 4
-----|-----|
---|---|---|

4 6
---|---|---|
-----|-----|

2 3
-|-|-|
--|--|

3 2
--|--|
-|-|-|

3 6
--|--|
-----|

2 5
-|-|-|-|-|
----|----|

4 3
---|---|---|
--|--|--|--|

10 10
---------|
---------|

10 5
---------|
----|----|

10 6
---------|---------|---------|
-----|-----|-----|-----|-----|

24 8
-----------------------|
-------|-------|-------|

7 8
------|------|------|------|------|------|------|------|
-------|-------|-------|-------|-------|-------|-------|

6 8
-----|-----|-----|-----|
-------|-------|-------|

13 11
------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|
----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|

• @LeakyNun Extending an answer from codegolf.stackexchange.com/q/94999 seems easier than from that one. Either way, people will have fun doing this one which is a decent reason imo. Commented Sep 25, 2017 at 19:02
• Can I output an array with two strings, one for each line? Commented Sep 25, 2017 at 19:05
• @BlackCap No. Print the strings to stdout or a file or return the whole multiline string. Commented Sep 25, 2017 at 19:06
• Bonus for handling arbitrary number of inputs?
Commented Sep 25, 2017 at 23:49
• @HelkaHomba Ok thanks; saved 1 more byte. :) (As if there is any other reason to ask such questions on codegolf challenges. ;p) Commented Sep 28, 2017 at 6:58

# Python 3, 80 bytes

Saved 1 byte thanks to Halvard Hummel and 1 byte thanks to Jonathan Allan.

import math
def f(*l):
for k in 0,1:print(l[~k]//math.gcd(*l)*(~-l[k]*"-"+"|"))


Test it online!

lambda*l:"\n".join(l[0]*l[1]//math.gcd(*l)//k*(~-k*"-"+"|")for k in l)
import math


Test it online! (82 bytes - initial answer)

This is the best I could do in Python 2 (81 bytes). It seems like I cannot comment on that answer, I'll just post this here instead:

from fractions import*
l=a,b=input()
for k in l:print a*b/gcd(*l)/k*(~-k*"-"+"|")


Test it online!

First attempt here, probably sub-optimal!

• Welcome to PPCG! Commented Sep 25, 2017 at 19:46
• @Laikoni Thank you! This seems like a fun community :-)
– user74686
Commented Sep 25, 2017 at 19:47
• 81 bytes Commented Sep 25, 2017 at 20:02
• @HalvardHummel Thanks, will post as an alternative!
– user74686
Commented Sep 25, 2017 at 20:03
• While trying a diferent approach entirely I realised your version could be done in 80. Commented Sep 25, 2017 at 21:20

x%y=unlines[["-|"!!(0^mod a b)|a<-[1..lcm x y]]|b<-[x,y]]


Try it online!

• I've never seen that 0^0=1 trick before - clever Commented Sep 25, 2017 at 19:36
• @BlackCap I can't claim it myself because I have seen it a few times before, though I don't remember where I saw the trick first. Commented Sep 25, 2017 at 19:42

# Jelly, 12 bytes

Ṭ€ị⁾|-ṁ€æl/Y


Try it online!

# MATL, 16 15 bytes

'-|'!i&Zm:G\go)


Input is a column vector with the two numbers. Try it online!

As a bonus, the input can contain more than two numbers. Try it online!

### Explanation

'-|'   % Push this string
!      % Transpose. This is needed because of input [1; 1]
i      % Input column vector of 2 (or N) numbers
&Zm    % LCM of the 2 (or N) numbers, say L
:      % Range
G      % Push input again
\      % Modulus, element-wise with broadcast. Gives a 2×L (or N×L) matrix
g      % Convert to logical: gives false for zeros, true for nonzeros
o      % Convert to double: gives 0 for false, 1 for true
)      % Index into string (modular, 1-based). Implicitly display

• I think you left in a stray He? Commented Sep 28, 2017 at 8:27
• @Sanchises Thanks! Yes, it was in the previous version, but it's not necessary Commented Sep 28, 2017 at 8:50
• Also, this seems to work just fine without the transpose? You've been overthinking things... ;) Commented Sep 28, 2017 at 9:37
• @Sanchises Without the transpose it doesn't work for input [1; 1], due to how MATL(AB) handles array shape with indexing. (Alternatively, the transpose could be replaced by He at the end, which is why it was initially there) Commented Sep 28, 2017 at 10:21
• Ah yes I figured that it was there because of row behaviour but I didn't think of this edge case. Commented Sep 28, 2017 at 10:49

# R, 109 105 bytes

function(a,b){q=1:a*b
l=min(q[!q%%a])
x=rep("-",l*2)
x[c(seq(0,l,a),l+seq(0,l,b))]="|"
write(x,"",l,,"")}


Try it online!

Anonymous function. Computes l=lcm(a,b), then generates a range from 0 to l by a, then from l to 2*l by b, setting the indices to | and printing as a matrix with l columns.

# Python 2, 66 bytes

l=a,b=input()
while a%b:a+=l[0]
for x in l:print a/x*('-'*~-x+'|')


Try it online!

# Husk, 12 bytes

†?'-'|TUṪ%N


Try it online!

Yeah, there is a lcm builtin in Husk. No, I don't need it.

Bonus: works with any number of input values

### Explanation

†?'-'|TUṪ%N    input:[2,3]
Ṫ%N    table of all remainders of positive naturals divided by
input numbers:
[[1,1],[0,2],[1,0],[0,1],[1,2],[0,0],[1,1],[0,2],...
U        get all elements before the first repeated one:
[[1,1],[0,2],[1,0],[0,1],[1,2],[0,0]]
T         transpose:
[[1,0,1,0,1,0],[1,2,0,1,2,0]]
†?'-'|          replace all truthy elements with '-' and all falsy elements
with '|':
["-|-|-|","--|--|"]
implicit: since this is a full program, join the resulting array
of strings with newlines, and print to stdout


# C, 72 bytes

i;f(x,y){for(i=1;i%y|i%x;)putchar(i++%x?45:124);puts("|");y>0&&f(y,-x);}


# Mathematica, 63 bytes

(s=LCM@##;Print[""<>If[i~Mod~#<1,"|","-"]~Table~{i,s}]&/@{##})&


Try it online!

and another version which user202729 really, really, really wants to see posted

# Mathematica, 59 bytes

(s=LCM@##;Print[""<>If[#∣i,"|","-"]~Table~{i,s}]&/@{##})&


this one uses special character \[Divides] ∣

• If this is Mathematica you can probably use \[Divides] instead of Mod operator to represent divisibility, which saves 4 bytes. Also Mathics TIO should not print the {Null, Null}. Commented Sep 27, 2017 at 10:04
• @user202729 I fixed the Mathics print. Commented Sep 27, 2017 at 10:14
• I think that your first comment was clear enough. Please stop pressing me in order to make the changes that you want, the exact time you want. Give the users some hours to respond. Some of us have a life out of this place Commented Sep 27, 2017 at 12:51

# 05AB1E, 13 bytes

ʒ<'-×'|«¹.¿∍,


Uses the 05AB1E encoding. Try it online!

• Filter was a good idea, I got 17 doing it entirely different .¿Lε²¹‚%_„-|è}øJ». Commented Oct 4, 2017 at 16:22

# APL (Dyalog), 22 bytes

Assumes ⎕IO←0. Takes A,B as right argument. Bonus: handles input list of any length!

{'|-'[⌽×⍵∘.|⍳∧/⍵]}


Try it online!

{} anonymous lambda where ⍵ represents the right argument

'|-'[] index the string with:

∧/ LCM across the input

⍳ first that many ɩntegers (0 through N-1)

⍵∘.| division remainder table with the input vertically and that horizontally

× signum

⌽ flip horizontally

• What does that first assumption mean? Commented Sep 26, 2017 at 1:00
• @HelkaHomba It means that arrays index starting at 0, a default on APL interpreters, I believe. Commented Sep 26, 2017 at 1:04
• @HelkaHomba Since APL systems come in both 0-based and 1-based flavours, I just write the assumption. Otherwise one would have to have two APLs. E.g. ngn/apl can run this very same code without specifying ⎕IO←0, as that is the default there.
Commented Sep 26, 2017 at 6:19

# Octave, 46 38 bytes

-8 bytes thanks to several suggestions by Luis Mendo

@(a,b)'-|'.'(~mod(1:lcm(a,b),[a;b])+1)


Try it online!

# JavaScript (ES6), 69 bytes

f=(a,b,S,A=1)=>(A%a?'-':'|')+(A%a|A%b?f(a,b,S,A+1):S?'':
+f(b,a,1))


Recursively runs until A is divisible by both a and b – outputting a dash or pipe based on a's divisibility by A.

The function then calls itself, swapping a and b.

The S variable prevents the function from calling itself infinitely.

Test Cases:

f=(a,b,S,A=1)=>(A%a?'-':'|')+(A%a|A%b?f(a,b,S,A+1):S?'':
+f(b,a,1))

console.log(f(1,1));
console.log(f(1,2));
console.log(f(2,1));
console.log(f(2,2));
console.log(f(6,4));
console.log(f(4,6));
console.log(f(2,3));
console.log(f(3,2));
console.log(f(10,10));
console.log(f(3, 6));
console.log(f(2, 5));
console.log(f(4, 3));
console.log(f(10, 10));
console.log(f(10, 5));
console.log(f(10, 6));
console.log(f(24, 8));
console.log(f(7, 8));
console.log(f(6, 8));
console.log(f(13, 11));

# JavaScript (ES8), 91 bytes

f=(a,b,i=2,g=(c,d)=>d?g(d,c%d):c)=>i?'|'.padStart(a,'-').repeat(b/g(a,b))+
+f(b,a,i-1):''


Uses the algorithms:

lcm(a, b) = ab / gcd(a, b)
gcd(c, d) = d ? gcd(d, c%d) : c


Recursively calls itself just once to output the second line.

Test Cases:

f=(a,b,i=2,g=(c,d)=>d?g(d,c%d):c)=>i?'|'.padStart(a,'-').repeat(b/g(a,b))+
+f(b,a,i-1):''

console.log(f(1,1));
console.log(f(1,2));
console.log(f(2,1));
console.log(f(2,2));
console.log(f(6,4));
console.log(f(4,6));
console.log(f(2,3));
console.log(f(3,2));
console.log(f(10,10));
console.log(f(3, 6));
console.log(f(2, 5));
console.log(f(4, 3));
console.log(f(10, 10));
console.log(f(10, 5));
console.log(f(10, 6));
console.log(f(24, 8));
console.log(f(7, 8));
console.log(f(6, 8));
console.log(f(13, 11));

# JavaScript (ES6), 93 bytes

f=(a,b,i=2,g=(c,d)=>!d=>d?c:g(d,c%d):c)=>i?('-'.repeat(a-1)+'|').repeat(a*bb/g(a,b)/a)+
+f(b,a,i-1):''


Same algorithm as before, using repeat instead of padStart.

• I thought padStart was ES8?
– Neil
Commented Sep 25, 2017 at 23:34
• f=(a,b,A=1)=>(A%a?'-':'|')+(A%a|A%b?f(a,b,A+1):a<0?'':\n+f(-b,a))
– l4m2
Commented Oct 22, 2018 at 6:05
• @l4m2, I can barely understand code that I wrote a year ago, but it does look like yours does shave off some bytes, thanks! Commented Oct 22, 2018 at 19:24

# Scala, 98 bytes

print((a to a*b).find(l=>l%a+l%b==0).map(l=>("-"*(a-1)+"|")*(l/a)+"\n"+("-"*(b-1)+"|")*(l/b)).get)


Try it online

• Hi, welcome to PPCG! This looks like a great first answer, so +1 from me. I'm not sure, since I've never programmed in Scala, but can *(a-1) be golfed to *~-a and *(b-1) to *~-b? Also, could you perhaps add a TIO link with test code? (Oh, and that avatar doesn't seem very cubic to me. ;p) Commented Sep 26, 2017 at 14:31
• Thanks! The trick with *~-a is great, but unfortunately Scala requires more brackets: *(~(-a)) to make clear that the concatenations *~-, *~, ~- are not fancy function names. I added a TIO link. Commented Sep 27, 2017 at 7:12
• Ah yes, ~- can be function names in Scala. I remember someone mentioning that before quite a while ago. That's unfortunate regarding golfing. Again welcome, and nice first answer. Commented Sep 27, 2017 at 7:37

# Java (OpenJDK 8), 103 bytes

a->b->{String l="",r="|\n";for(int m=0;(++m%a|m%b)>0;r+=m%b<1?'|':'-')l+=m%a<1?'|':'-';return l+r+'|';}


Try it online!

### 110 bytes, n input values

a->{String s="";for(int v:a){for(int i=1,z=1;z>(z=0);s+=i++%v<1?'|':'-')for(int k:a)z|=i%k;s+='\n';}return s;}


Try it online!

# Java 8, 125118 117 bytes

a->b->{String A="\n",B=A,t="|";for(int i=1;!A.endsWith(t)|!B.endsWith(t);B+=i++%b<1?t:"-")A+=i%a<1?t:"-";return A+B;}


-7 bytes thanks to @Nevay.
-1 byte by starting with a trailing new-line (A="",B="\n" replaced with A="\n",B=A).

Explanation:

Try it here.

a->b->{             // Method with two integer parameters and String return-type
String A="\n",    //  String top line (starting with a trailing new-line)
B=A,       //  String bottom-line (starting with a new-line)
t="|";     //  Temp String "|" which is used multiple times
for(int i=1;      //  Index-integer, starting at 1
!A.endsWith(t)|!B.endsWith(t);
//  Loop as long as both Strings aren't ending with "|"
B+=           //    After every iteration: append B with:
i++%b<1?   //     If i is divisible by b:
//     (and increase i by 1 in the process)
t         //      t (holding "|")
:          //     Else:
"-")      //      A literal "-"
A+=             //   Append A with:
i%a<1?       //    If i is divisible by a
t           //     t (holding "|")
:            //    Else:
"-";        //     A literal "-"
//  End of loop (implicit / single-line body)
return A+B;       //  Return both lines, separated by the new-line B started with
}                   // End of method

• 118 bytes: a->b->{String A="",B="\n",k="|";for(int i=0;!A.endsWith(k)|!B.endsWith(k);B+=i%b<1?k:"-")A+=++i%a<1?k:"-";return A+B;} Commented Sep 27, 2017 at 17:16
• @Nevay Thanks. Can't believe I missed the most obvious thing !A.endsWith(t)|!B.endsWith(t) when I was looking for a short way to check whether both are ending with |.. And starting B with a new-line instead of putting it between them at the return is also smart. Commented Sep 27, 2017 at 17:24

# Python 2, 96 88 bytes

Edit: Saved 4 bytes thanks to @Leaky Nun

Edit: Saved 4 bytes thanks to @Rod

lambda a,b:b/gcd(a,b)*("-"*~-a+"|")+"\n"+a/gcd(a,b)*("-"*~-b+"|")
from fractions import*


Try it online!

• 88 bytes or 77 bytes for a more flexible output
– Rod
Commented Sep 25, 2017 at 18:56
• 88 bytes
– ovs
Commented Sep 25, 2017 at 18:57
• Apparently outputting a list of strings isn't acceptable. :I Replace [...] with '\n'.join(...) to fix. Commented Sep 25, 2017 at 19:22

# Python 2, 89 bytes

Not the shortest Python 2 entry, but a different approach than gcd which may still be golfable.

a,b=input()
h,p='-|'
x=b*(h*~-a+p),a*(h*~-b+p)
for v in x:print v[~zip(*x).index((p,p)):]


Try it online!

a#b=do x<-[a,b];lcm a btakecycle(([2..x]>>"-")++"|")++"\n"


Try it online!

Same length:

a#b=unlines[take(lcm a b)$cycle$([2..x]>>"-")++"|"|x<-[a,b]]


Old solution:

l!x=[1..div l x]>>([2..x]>>"-")++"|"
a#b|l<-lcm a b=l!a++'\n':l!b

• You can save a byte with '\n':. Commented Sep 25, 2017 at 21:05
• @Laikoni Careful, I am closing in on you Commented Sep 26, 2017 at 9:54

# Whispers v2, 131 bytes

> Input
> Input
>> 1⊔2
>> (3]
>> L∣1
>> L∣2
>> Each 5 4
>> Each 6 4
> '-|'
>> 9ⁿL
>> Each 10 7
>> Each 10 8
>> Output 11 12


Try it online!

## How it works

If you're unfamiliar with Whispers' program structure, I'd recommend reading the first part of this post.

In this explanation, we'll refer to the two inputs as $$\x\$$ and $$\y\$$ respectively. Our first two lines simply take the inputs in, and store them on lines 1 ($$\x\$$) and 2 ($$\y\$$). We then move to line 3, which returns $$\\alpha = \mathrm{lcm}(x, y)\$$ and to line 4, which returns the range $$\A = [1, 2, ..., \alpha]\$$.

Next, we reach our first two Each statements, operating on each of the inputs:

>> L∣1
>> L∣2
>> Each 5 4
>> Each 6 4


These four lines both operate on $$\A\$$, but return two different arrays, which we will call $$\A_x\$$ and $$\A_y\$$. While being different arrays, they are both formed in similar ways, as can be noted from the similarities in the two pairs of lines. In fact, we can define $$\A_x\$$ and $$\A_y\$$ as

$$A_x := [(i \div x) \in \mathbb{Z} \: | \: i \in A]$$ $$A_y := [(i \div y) \in \mathbb{Z} \: | \: i \in A]$$

This leaves us with two lists consisting of a $$\1\$$ where we'd expect there to be a | character, and a $$\0\$$ where there should be a -. This takes us to the next section of our code:

> '-|'
>> 9ⁿL
>> Each 10 7
>> Each 10 8


First, we yield the string -|, then we create our next two arrays $$\B_x\$$ and $$\B_y\$$. Helpfully, we can use the same function to map $$\A_x\$$ to $$\B_x\$$ and $$\A_y\$$ to $$\B_y\$$, namely 9ⁿL. This function yields the $$\n^{th}\$$ element of the string on line 9 i.e. -|, where $$\n\$$ is either $$\0\$$ or $$\1\$$, depending on the element from the respective $$\A\$$ arrays. This yields the two arrays $$\B_x\$$ and $$\B_y\$$ as defined below:

$$(B_x)_i = \begin{cases} \text{"-"}, & (A_x)_i = 0 \\ \text{"|"}, & (A_x)_i = 1 \end{cases}$$

$$(B_y)_i = \begin{cases} \text{"-"}, & (A_y)_i = 0 \\ \text{"|"}, & (A_y)_i = 1 \end{cases}$$

The Each command is special-cased for when yielding an array of strings, where it returns a single string, rather than an array. Finally, we reach the statement

>> Output 11 12


which outputs $$\B_x\$$, then a newline, then $$\B_y\$$

• I believe … is one byte shorter than Each, Try it online!, though I can't shake the feeling that there's some way to combine the processes
– Jo King
Commented Jan 13, 2020 at 5:59

# C (gcc), 121999392 89 bytes

This should be much shorter, hmmmm....

#define L(x)for(j=-1,i=a;j<i;i+=i%b||i%a)putchar(++j?j%x?45:124:10);
i,j;f(a,b){L(a)L(b)}


Try it online!

# J, 20 bytes

'-|'{~*.$&>;&(<:=i.)  Try it online! • this is pretty J Commented Sep 26, 2017 at 1:33 # SOGL V0.12, 19 16 bytes 2{H┌*┐+..*..g/mP  Try it Here! Explanation: 2{ two times do H decreate ToS - input - by 1 ┌* get that many dashes ┐+ append a vertical bar ..* push both inputs multiplied \ ..g push gcd(input1, input2) | LCM - 7 bytes :/ / divide the multiblication by the GCD / m mold the string to that length P print that in a new line  • You documented before implementing? o0 Commented Sep 25, 2017 at 19:23 • @icrieverytim SOGL has many, many documentated things that aren't implemented. :p The documentation is basically my TODO list (which I rarely do :p) Commented Sep 25, 2017 at 19:24 # Stacked, 42 38 bytes [:...lcm@z:[:z\/\#-'-'*\rep'|'+out]"!]  Try it online! Input in the form of a pair of numbers. All the test cases put together look kinda like buildings. ## Explanation This first takes the lcm of the two input numbers into z. Then, for each number k, we generate z / k strings of - of length k - 1, adding | to the end of each, and outputting each. ## Previous counted attempts 42 bytes: [:...lcm@z:[:z\/\#-'-'*\rep'|'+''#out]"!] ## Other attempts 43 bytes: [:...lcm@z:[:z\/\#-'-'*\rep'|'#'|'+out]"!] 45 bytes: ['@lcm'!#~@z,[:z\/\#-'-'*\rep'|'#'|'+out]"!] 45 bytes: [:...lcm@x[x'-'*\#<$betailmap'|'#'|'+out]"!]

53 bytes: [:...lcm'-'*@z#-'.'*'('\+')'+'.'+[z\'$1|'repl out]"!] 54 bytes: [:...lcm@x{!x'-'*('('n#-'.'*').')''#'$1|'repl out}"!]

# JavaScript (ES6), 89

f=(a,b,t=
,l=0,R=n=>'-'.repeat(n-1)+'|')=>l||1/t?f(a,b,l<0?t+R(b,l+=b):R(a,l-=a)+t,l):t


Evaluating the LCM with repeated addictions.

Less golfed

F=(a,b, sa='', sb='', la=0, lb=0)=>
{
var R=n=>'-'.repeat(n-1)+'|'
if (la != lb || la == 0)
{
if (la < lb) {
sa += R(a)
la += a
}
else
{
sb += R(b)
lb += b
}
return F(a, b, sa, sb, la, lb)
}
else
return sa+'\n'+sb
}


Test

f=(a,b,t=
,l=0,R=n=>'-'.repeat(n-1)+'|')=>l||1/t?f(a,b,l<0?t+R(b,l+=b):R(a,l-=a)+t,l):t

function update()
{
var [a,b]=I.value.match(/\d+/g)
R.textContent = f(+a,+b)
}

update()
<input id=I oninput='update()' value='4 6'>
<pre id=R></pre>

VBA (Excel) , 144 142 bytes

Sub q()
a=[a1]
b=[a2]
Do Until c=d And d="|"
e=e+1
c=IIf(e Mod a,"-","|")
d=IIf(e Mod b,"-","|")
f=f& c
g=g& d
Loop
Debug.? f& vbCr& g
End Sub


-2 bytes. thanks Sir Washington Guedes.

# J, 20 bytes

*./(\$'-|'#~<:,1:)"0]


Try it online!

# Ruby, 64 57 bytes

->a,b{[a,b].map{|n|(1..a.lcm(b)).map{|x|x%n>0??-:?|}*''}}


-7 bytes thanks to G B.

Try it online!

• You don't need to include the 'puts', if the function returns 2 strings it's ok. And you can make it shorter by using the array * operator (array*'' is equivalent to array.join)
– G B
Commented Sep 29, 2017 at 6:16
• @GB thanks for your help! Commented Sep 29, 2017 at 17:07

# Charcoal, 3230 29 bytes

ＮθＮη≔θζＷ﹪ζη≦⁺θζＥ⟦θη⟧…⁺×-⁻ι¹|ζ


Try it online! Link is to verbose version of code. Edit: Saved 1 byte thanks to @ASCII-only.

Anonymous worksheet formula that takes input from range A1:B1 and outputs to the calling cell
=REPT(REPT("-",A1-1)&"|",LCM(1:1)/A1)&"

• Can you presume that the nothing else is input to row 1? If so, you can shorted LCM(A1,B1) to just LCM(1:1) to save 4 bytes. I think it's reasonable to presume a blank starting sheet and specify where both the inputs and formula are. Commented Oct 19, 2018 at 20:41