# Challenge

Write a program or function that takes in 4 non-negative integers, A, B, C, and D, that represent two fractions, A/B and C/D, where B and D are non-zero and A <= B and C <= D.

Output an ASCII art depiction of the fractions made of |x- characters on two lines as follows:

• The lines will always be the same, minimal length for the input, and both always start and end with |.

• The top line will depict A/B by having B sections of -'s separated by |'s with A of the sections replaced by x's as if they are filling up the 1 whole of the full line.

• Likewise, the bottom line will depict C/D by having D sections of -'s separated by |'s with C of the sections replaced by x's as if they are filling up the 1 whole of the full line.

It will probably make the most sense if I show an example. Suppose A/B = 2/5 and C/D = 1/3, then the output should be:

|xx|xx|--|--|--|
|xxxx|----|----|


Here the lines are the same length, as required, and the top one depicts 2 out of 5 sections filled with x's while the bottom one depicts 1 out of 3 sections filled with x's.

As another example, A/B = 1/2 and C/D = 5/8 would give:

|xxxxxxx|-------|
|x|x|x|x|x|-|-|-|


Notice how the number of x's or -'s in each section between |'s is the same in any one line but can vary depending on the fractions and the requirement that the two lines are the same length overall.

In the first example it'd be impossible to just have 1 character between the |'s for 2/5

|x|x|-|-|-|    (2/5 with 1 character between bars)
|xx|--|--|     (invalid for 1/3 paired with 2/5 since line too short)
|xxx|---|---|  (invalid for 1/3 paired with 2/5 since line too long)


but with 2 characters between |'s it works as shown above.

Your program must use the shortest lines possible. This keeps them easy to read.

The cool idea here is that it's super easy to look as these ASCII art depictions of fractions and see which one is greater or if they are equal, just by how the sections line up.

So for the first A/B = 2/5 and C/D = 1/3 example, this output

|xxxxx|xxxxx|-----|-----|-----|
|xxxxxxxxx|---------|---------|


would be invalid, as, even though the lines are the same length and depict the correct fractions, they can be shorter as shown above.

# Scoring

This is a challenge so the shortest program wins!

• As stated, B and D will be positive, and A will be from 0 to B inclusive, and C will be from 0 to D inclusive.

• There must be at least one x or - between each pair of | as otherwise it's impossible to tell how much of the fraction is filled.

• The output can have a trailing newline or not, doesn't matter.

• You can take in two fractions directly instead of 4 integers if it makes sense for your language.

# Testcases

Each testcase is 3 lines, the input A B C D on one line, followed by the two lines of the output. Empty lines separate testcases.

2 5 1 3
|xx|xx|--|--|--|
|xxxx|----|----|

1 2 5 8
|xxxxxxx|-------|
|x|x|x|x|x|-|-|-|

0 1 0 1
|-|
|-|

0 1 0 2
|---|
|-|-|

1 3 1 2
|x|-|-|
|xx|--|

1 2 1 3
|xx|--|
|x|-|-|

1 2 2 4
|xxx|---|
|x|x|-|-|

1 2 2 2
|x|-|
|x|x|

3 3 1 9
|xxxxx|xxxxx|xxxxx|
|x|-|-|-|-|-|-|-|-|

3 5 4 7
|xxxxxx|xxxxxx|xxxxxx|------|------|
|xxxx|xxxx|xxxx|xxxx|----|----|----|

28 30 29 30
|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|-|-|
|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|-|

7 28 2 8
|x|x|x|x|x|x|x|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
|xxxxxx|xxxxxx|------|------|------|------|------|------|

1 7 2 13
|xxxxxxxxxxxx|------------|------------|------------|------------|------------|------------|
|xxxxxx|xxxxxx|------|------|------|------|------|------|------|------|------|------|------|

1 7 2 14
|xxx|---|---|---|---|---|---|
|x|x|-|-|-|-|-|-|-|-|-|-|-|-|

0 10 0 11
|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|
|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|

3 10 4 11
|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|----------|----------|----------|----------|----------|----------|----------|
|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|---------|---------|---------|---------|---------|---------|---------|

10 10 11 11
|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxx|
|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|xxxxxxxxx|

• Related May 10 at 15:43
• Very nice first challenges!
– xnor
May 10 at 17:49

# Excel (ms365), 134 bytes

Credits to @JosWooley for this is essentially his answer.

Formula in E2:

="|"&TEXTJOIN({"";"
|"},0,REPT(REPT({"x","-"},(1+(MOD(D1,B1)=0))*LCM(B1,D1)/VSTACK(B1,D1)-1)&"|",CHOOSE({1,2;3,4},A1,B1-A1,C1,D1-C1)))


2nd Answer: 74 + 81 = 155 bytes

Credits to @EngineerToast for this is essentially his answer.

Formula in E2:

=LET(x,2-SIGN(MOD(D1,B1)),z(A1,B1,LCM(B1,D1)*x)&"
"&z(C1,D1,LCM(B1,D1)*x))


Note that there should be a literal newline character pieces this together, where z() refers to a named function:

=LAMBDA(a,b,m,LET(x,m/b-1,"|"&REPT(REPT("x",x)&"|",a)&REPT(REPT("-",x)&"|",b-a)))


Original Answer: 118 + 138 = 256 bytes

Formula in E2:

=z(TEXTJOIN("|",0,,IF(SEQUENCE(B1)<=A1,"x","-"),),TEXTJOIN("|",0,,IF(SEQUENCE(D1)<=C1,"x","-"),))


This refers to a named function called 'z':

=LAMBDA(a,b,LET(x,LEN(a),y,LEN(b),q,SUBSTITUTE(SUBSTITUTE(IF(x<y,a,b),"|-","|--"),"|x","|xx"),IF(x=y,a&CHAR(10)&b,IF(x<y,z(q,b),z(a,q)))))


The idea here is that we create two strings with single 'x' and '-' between pipe-symbols. Z is a named function that will now check the length of both strings and will replace '|x' and '|-' with '|xx' and '|--' respectively for the shortest of the two. It will do so untill the strings have the same length. When found the same length, just concat them together with a newline.

• You can use LCM() to find the least common multiplier of the denominators and save a lot of bytes. Using the same scheme as you have outlined, your function can be: =z(A1,A2,LCM(A2,A4))&" "&z(A3,A4,LCM(A2,A4)) (with a literal newline in the middle) and a named function z set to =LAMBDA(a,b,m,LET(x,m/b-1,"|"&REPT(REPT("x",x)&"|",a)&REPT(REPT("-",x)&"|",b-a))). Score would be 44+81=125 bytes. There may be more optimization to do in there. May 10 at 15:10
• @EngineerToast, that would be a great improvement. Once I get back to the pc I'll have a look. Thanks for thinking along.
– JvdV
May 10 at 16:15
• Fixed by accounting for the cases where one divisor is a factor of the other. The lambda function is the same but change the cell function to be =LET(x,2-SIGN(MOD(A4,A2)),z(A1,A2,LCM(A2,A4)*x)&" "&z(A3,A4,LCM(A2,A4)*x)) (still with the literal line break). Score is 74+81=155. I never saw the issue with it switching lines, though. May 11 at 18:30
• 134 bytes with a single function if you use a bit of array manipulation: ="|"&TEXTJOIN({"";" |"},0,REPT(REPT({"x","-"},(1+(MOD(D1,B1)=0))*LCM(B1,D1)/VSTACK(B1,D1)-1)&"|",CHOOSE({1,2;3,4},A1,B1-A1,C1,D1-C1))) May 13 at 12:51
• @JvdV Er, no idea! May 15 at 7:16

# Python, 152 bytes

Here's the program I used to generate the testcases. I'm sure someone more skilled could golf it down more (and I'd be interested to see!).

import math
def p(a,b,c,d):
L=math.lcm(b,d)
if L==max(b,d):L*=2
def f(l,x,d):print('|'+f'{"x"*l}|'*x+f'{"-"*l}|'*d)
f(L//b-1,a,b-a)
f(L//d-1,c,d-c)


It defines the function p so calling p(A, B, C, D) produces the output.

The idea is to imagine all but the first | in each line as part of the x or - sections to simplify the math, and then put the |'s back in later.

The lcm of the denominators L is the length of the lines, not counting the first |. When L is equal to the larger of B or D then each section would be length 1 which would end up looking like |||||| so the if L==max(b,d):L*=2 line fixes that situation. Other than that trick it's mostly doing some f-string fanciness to print the lines with the correct values.

• 147 bytes May 10 at 6:27
• @Arnauld Woops.. Somehow completely glanced over that. :/ I'll fix my 05AB1E answer, and delete my comments above. It could still be slightly golfed to 144 bytes by returning as a pair. May 10 at 9:28
• 133 bytes
– xnor
May 10 at 18:05

# 05AB1E, 32 bytes

ø©.¿D®àQ>*®÷<„x-Sδ×'|ìsIíÆø×J€Ć


Port of @blaketyro's Python answer, so make sure to upvote him/her as well!

Input as a pair of pairs [[a,b],[c,d]], output as a pair of strings.

Explanation:

ø              # Zip/transpose the (implicit) input-matrix, swapping rows/columns:
#  [[a,b],[c,d]] → [[a,c],[b,d]]
# Pop and push both rows separated to the stack
©            # Store the top pack [b,d] in variable ® (without popping)
.¿          # Pop and get its Least Common Multiple
D         # Duplicate that
®à       # Push the maximum from pair [b,d]
Q      # Check if it's equal to the LCM
>     # Increase that 0 or 1 by 1
*    # Multiply it to the LCM
®/  # Divide it by the values of pair ®
< # Decrease both by 1
„x-          # Push string ["x-"]
S         # Convert it to a pair ["x","-"]
δ      # Apply double-vectorized on the two pairs:
×     #  Repeat the string the integer amount of times
'|ì '# Then prepend a "|" in front of each of the four strings
s            # Swap so the pair of numerators is at the top of the stack
I           # Push the input-matrix again
í          # Reverse each inner row
Æ         # Reduce each inner list by subtracting
ø        # Create pairs of the two top pairs
#  [a,c] and [b-a,d-c] → [[a,b-a],[c,d-c]]
×       # Repeat the earlier strings that many times
J              # Join the inner pair of strings together
€             # Map over both strings in this pair:
Ć            #  Enclose it; appending its own head (the "|")
# (after which the pair of strings is output implicitly as result)


# Vyxalj, 31 29 27 bytes

yD∆Ŀ~cßdεȮ/‟ʁ≤‛x-i*ƛ\|j\|ø.


-2 bytes thanks to lyxal :)

I'm aware of Vyncode but I'm probably going to stick to regular SBCS bytes in my answers for the time being.

Code Stack (bottom to top)
Implicit input (3 | 5 | 2 | 3)
y Every other, and the rest (3 | 2) (5 | 3)
D Push top of stack twice (3 | 2) (5 | 3) (5 | 3) (5 | 3)
∆Ŀ Least Common Multiple (3 | 2) (5 | 3) (5 | 3) 15
~c Execute without pop: Is A in B? (3 | 2) (5 | 3) (5 | 3) 15 0
ßd If so, double, otherwise don't (3 | 2) (5 | 3) (5 | 3) 15
ε Absolute difference (3 | 2) (5 | 3) (10 | 12)
Ȯ Carry second-to-top over (3 | 2) (5 | 3) (10 | 12) (5 | 3)
/ Divide (3 | 2) (5 | 3) (2 | 4)
‟ Rotate stack right (2 | 4) (3 | 2) (5 | 3)
ʁ Range [0, n) (2 | 4) (3 | 2) ((0 | 1 | 2 | 3 | 4) | (0 | 1 | 2))
≤ Greater than or equal to? (2 | 4) ((0 | 0 | 0 | 1 | 1) | (0 | 0 | 1))
‛x- String literal "x-" (2 | 4) ((0 | 0 | 0 | 1 | 1) | (0 | 0 | 1)) "x-"
i Index into (2 | 4) (("x" | "x" | "x" | "-" | "-") | ("x" | "x" | "-"))
* Repeat string n times (("xx" | "xx" | "xx" | "--" | "--") | ("xxxx" | "xxxx" | "----"))
ƛ Map over each item: Showing first pass through:
\|j Join by "|" "xx|xx|xx|--|--"
\|ø. Enclose in "|" "|xx|xx|xx|--|--|"
j flag: Join final result by newlines Implicit output of result

Let me know if this format of explanation is good, by the way. I'm trying to experiment with doing it in a method that is more obvious what is happening, but it does take up a lot of space.

• Try it Online! for 27 May 12 at 13:15
• @lyxal I had a feeling the stack manipulation could be reduced. Too bad the smiley is gone now D: I’m not at my laptop right now but I will update it later today May 12 at 13:29

# JavaScript (ES6), 113 bytes

(p,q,P,Q)=>(F=w=>(n=q*w-Q*W)?F(w+=n<0||!++W):(g=w=>n>q*w?n=
:'x-|'[n++%w?n>p*w|0:2]+g(w))(w)+g(W,p=P,q=Q))(W=2)


Try it online!

### Commented

( p, q,                // p / q = 1st fraction
P, Q                 // P / Q = 2nd fraction
) => (                 //
F = w =>             // F is a recursive function taking a first width w
// explicitly and a second width W implicitly
(n = q * w - Q * W)  // let n be the difference between q * w and Q * W
// (NB: n is reused as a counter in the 2nd phase)
?                    // if n is not equal to 0:
F(                 //   do a recursive call to F:
w += n < 0       //     increment w if n is negative
|| !++W     //     increment W if n is positive
)                  //   end of recursive call
:                    // else (n = 0):
( g = w =>         //   g is a recursive function taking w
n > q * w ?      //   if n is greater than q * w:
n = \n       //     append a linefeed and reset n to a 0'ish value
:                //   else:
'x-|'[         //     append one of these characters:
n++ % w      //       if w is not a divisor of n (which is
?            //       incremented afterwards):
n > p * w  //         append '-' if n > p * w, or 'x' otherwise
| 0        //
:            //       else:
2          //         append the separator '|'
] + g(w)       //     append the result of a recursive call
)(w) +             //   1st call to g, using (w, p, q)
g(W, p = P, q = Q) //   2nd call to g, using (W, P, Q)
)(W = 2)               // initial call to F with w = W = 2


# APL (Dyalog APL), 45 bytes

Call as A C f B D.

{'-x|'[2,⍨↑⍺(>⌈2×1,2≠/⊢)¨⌊⍵×⊂÷⍨∘⍳⍨(∧⌈2×⌈)/⍵]}


Attempt This Online!

Or, for the same length:

{'-x|'[2,⍨↑,¨2,¨(¯1+⊃(,÷⍨∧⌈2×⌈)/⍵)/¨⍪¨⍺>⍳¨⍵]}


Attempt This Online!

# AWK, 265 253 248 239 bytes

-9 bytes thanks to @ceilingcat

function g(p,q){return q?g(q,p%q):p}function l(s,j){a=s;for(i=1;i++<M;)a=a s;b=a=j?a"|":"";for(i=1;i++<j;)b=b a;return b}{B=$2;D=$4;L=B*D/g(B,D);L+=L==(B<D?D:B)?L:0;M=L/B-1;print"|"l("x",$1)l("-",B-$1);M=L/D-1;print"|"l("x",$3)l("-",D-$3)}


Try it online!

This use POSIX awk, and not gawk.
In gawk you can use the abbreviation func for function.
GAWK function :

In many awk implementations, including gawk, the keyword function may be abbreviated func. (c.e.) However, POSIX only specifies the use of the keyword function. This actually has some practical implications. If gawk is in POSIX-compatibility mode (see Command-Line Options), then the following statement does not define a function:

func foo() { a = sqrt($1) ; print a }  • Welcome to Code Golf, and nice answer! May 11 at 14:59 • @ceilingcat nice ! That was my first time golfing so I missed some optimization. And my math skill aren't the best ones, so I also missed that x*2 was equal to x+x... Oct 23 at 6:58 # Julia, 77 bytes n|d=(l=lcm(d...);x=l*2^(l∈d).÷d.-1;p='|';@. p*('x'^x*p)^n*('-'^x*p)^(d-n))  Attempt This Online! Accepts input as two vectors: numerators n and denominators d. # Pyth, 45 bytes =GyW}J/*FKeMQiFKKJjmsm?%k/Ged?>cFdckG\x\-\|hG  Try it online! Takes input as a list of lists [[a, b], [c, d]]. ### Explanation First we compute the length of the lines (minus 1). This is the least common multiple of b and d, multiplied by 2 if it is exactly b or d. We save this number in the variable G.  # implicitly assign Q = eval(input()) KeMQ # assign K to [b, d] *FK # multiply the elements of K / iFK # divide by the gcd of K (to obtain lcm) J # assign this to J yW J # conditional apply doubling to J }J K # if J is in K =G # assign this to G  Next, we loop through the input and G+1 choosing the correct character for each position through two conditional operators. jmsm?%k/Ged?>cFdckG\x\-\|hGQ # implicitly add Q m Q # map d over Q m hG # map k over G+1 ?%k/Ged # if (k % (G / d[-1])): ?>cFdckG # if (d[0] / d[1] > k / G): \x # "x" # else: \- # "-" # else: \| # "|" s # sum the list of chars into a string j # join on newlines and print  # Arturo, 113 105 bytes $->a[e:lcm@[a\1a\3]if∨e=a\1e=a\3->'e*2map a[x y]->[124]++map
e'z->(0=z%e/y)?->124->(z>x*e/y)?->45->120]


Try it

$->a[ ; a function taking a list of four integers named a e:lcm@[a\1a\3] ; assign lcm of 2nd and 4th args to e if∨e=a\1e=a\3-> ; if e is equal to 2nd or 4th arg... 'e*2 ; double e in place; this ensures no zero-width sections map a[x y]-> ; map over input in pairs, assign to x and y [124]++ ; prepend pipe character to... map e'z-> ; map over [1..e] and assign current elt to z (0=z%e/y)?-> ; does e/y divide z? Then... 124 ; pipe -> ; otherwise (z>x*e/y)?-> ; is z greater than x times e/y? Then... 45 ; x -> ; otherwise 120 ; hyphen ] ; end function  # Ruby, 98 94 93 bytes ->a,b,c,d{[[a,b],[c,d]].map{|x,y|[p,*[?x*~k=b.lcm(d)/y*~1[b%d*(d%b)]]*x,*[?-*~k]*y-=x,p]*?|}}  Try it online! • k=[b.lcm(d),2*b,2*d].max/y-1 saves a byte. And then I think you could save two more by taking input as a single array [[a,b],[c,d]]. – ovs May 11 at 9:09 # Java (JDK), 166 172 bytes -6 bytes thanks to @KevinCruijssen ! Thank you for the challenge, it was very cool! (a,b,c,d)->{var r="";int i,j=0,p=b,q=d;while(p!=q|p<=b|q<=d)p+=p<q?b:0*(q+=d);for(;j++<2;r+="|\n",b=d,a=c)for(i=0;i++<b;)r+="|"+(i>a?"-":"x").repeat(p/b-1);return r;}  Try it online! A little explanation of my answer: first we calculate the "Least Common Multiple strictly greater than the b and d parameters" using the following: p=b,q=d;while(p!=q|p<=b|q<=d)p+=p<q?b:0*(q+=d); which leaves the result in p. Then we draw the 2 lines (using a small loop) with an inner loop that decides using its index wether we should draw a x or -, and we repeat this character a number of times using: the "greater-LCM", divided by the b or d parameter (depending on the current line), minus 1. And the | and \n are put where they should be during the process. • Nice answer, +1 from me! You can save two bytes by changing for(;j++<2;r+="|\n"){for...;b=d;a=c;} to for(;j++<2;r+="|\n",b=d,a=c)for...; and four more bytes by changing if(p<q)p+=b;else q+=d; to p+=p<q?b:0*(q+=d); - try it online - 166 bytes. May 12 at 8:35 • @KevinCruijssen Thank you! And nice optimization :) I couldn't see how to make my if/else into a ternary because of the 2 variables, but i should have known for the use of , especially since it's not the first time i miss it! May 12 at 9:34 # SAS, 181 (73+108) bytes 1. The code: Macro (73): %macro m(x,n,k);s=repeat("&k",&n-2);do i=1to &x;put s+(-1)"|"@;end;%mend;  Data step (108): data;set;L=LCM(b,d);L=L+(L=b<>d)*L;put"|"@;%m(a,L/b,*)%m(b-a,L/b,-)put/"|"@;%m(c,L/d,*)%m(d-c,L/d,-)put;run;  Process: Process assumes that input data are passed in a SAS dataset with variables a, b, c, and d. Dataset has to be created just before executing the code. Example data (extending test cases): data i; input A B C D; cards; 2 5 1 3 1 2 1 3 1 2 2 4 1 2 2 2 3 3 1 9 1 3 1 9 1 3 3 9 3 9 5 15 3 5 4 7 28 30 29 30 7 28 2 8 1 7 2 13 1 7 2 14 0 10 0 11 3 10 4 11 10 10 11 11 2 3 11 17 1 10 2 10 10 100 20 100 1 10 10 100 1 10 1 1 1 1 1 10 3 3 1 9 1 1 1 1 0 1 0 1 ; run;  1. Human readable code: Macro: %macro m(x,n,k); s = repeat("&k", &n-2); do i = 1 to &x; put s +(-1) "|" @; end; %mend;  Data step: data ; set ; L = LCM(b, d); L = L + (L = b<>d)*L; /* put / / a +(-1)"/" b " " c +(-1)"/" d; */ put "|" @; %m( a, L/b, *) %m(b-a, L/b, -) put / "|" @; %m( c, L/d, *) %m(d-c, L/d, -) put ; run;  Uncoment the put / / a +(-1)"/" b " " c +(-1)"/" d; line to have nicer looking result in the log. # Charcoal, 50 bytes Ｎθ⊞υＮＮη⊞υＮ≔ΣυζＷΣ﹪ζυ≦⊕ζＦυ«⟦⭆⊕ζ⎇﹪κ÷ζι§-x‹κ×θ÷ζι|⟧≔ηθ  Try it online! Link is to verbose version of code. Explanation: Ｎθ⊞υＮＮη⊞υＮ  Input the four integers, but push the denominators to the predefined empty list. ≔ΣυζＷΣ﹪ζυ≦⊕ζ  Calculate the adjusted LCM of the denominators, which is like the LCM execpt that it can't equal either denominator. This is achieved by starting the search with the sum of the denominators. Ｆυ«  Loop over the denominators. ⟦⭆⊕ζ⎇﹪κ÷ζι§-x‹κ×θ÷ζι|⟧  Calculate the display for this denominator. ≔ηθ  Copy the second numerator to the first so that the second loop calculates the display for the second denominator correctly. 43 bytes by taking the inputs in the order B, D, A, C: Ｆ²⊞υＮ≔ΣυζＷΣ﹪ζυ≦⊕ζＦυ«Ｎθ⟦⭆⊕ζ⎇﹪κ÷ζι§-x‹κ×θ÷ζι|  Try it online! Link is to verbose version of code. # Thunno 2N, 30 bytes zẸÐDŀƁƇ⁺×_\µ«L©ıx-si;×ı'|j'|ṛ  Requires Thunno 2.2.0+ for stack rotation. ATO is on v2.1.9. Port of Jacob's Vyxal answer. See that answer for a more detailed explanation. #### Explanation zẸ # Uninterleave into two pieces ÐD # Quadruplicate so four copies are on the stack ŀ # Pop one copy and push the LCM ƁƇ # Without popping anything, check for containment ⁺× # Increment and multiply (i.e. double if true) _ # Subtract the list from this number \ # Divide the two lists µ« # Rotate the stack left L # Lowered range of each number © # Less than or equal to ı # Map over this list of lists: x- # Push the string "x-" s # Swap so the list is on top i # Index into the string ; # End map × # Multiply each string by each number ı # Map over this list of lists: '|j # Join the list by the character "|" '|ṛ # And surround this string by the character "|" # N flag joins by newlines # Implicit output  #### Screenshot • I wanted to write this port but the stack manip stuff didn’t exist yet 😭🤕🤬🍜 May 26 at 17:07 • Man's really turned into noodles while trynna write a port. May 27 at 3:27 • @lyxal My name’s noodle man. From now on you will call me noodle man. 🍜🍜🍝🍝 May 27 at 14:42 • @Jacob Half of those are spaghetti, noodle brain. May 29 at 5:48 • @lyxal What is spaghetti but Italian noodles? 🍜🍜🍝🍝 May 29 at 13:01 # Jelly, 30 bytes :g/He¡$$ṚṬ€ḷⱮ"Ʋ~R}¦"ṭ€1ị“|x-”Y  A full program that accepts the denominators, [B, D] and the numerators, [A, C], and prints the result. Try it online! Or see the test-suite. ### How? :g/He¡$$ṚṬ€ḷⱮ"Ʋ~R}¦"ṭ€1ị“|x-”Y - Main Link: [B, D]; [A, C] Ʋ - last four links as a monad - f([B, D]):$                       -   last two links as a monad - f([B, D]):
/                            -       reduce by:
g                             -         greatest common divisor (GCD)
¡                         -       repeat...
e                          -       ...number of times: exists in?
- i.e. if the GCD is one B or D:
H                           -       ...action: halve (the GCD)
:                              -     ([B, D]) integer divide (that) (vectorises)
Ṛ                      -   reverse
Ṭ€                    -   untruth each - e.g. 4 -> Patten = [0,0,0,1]
"                 -   zip with ([B, D]) applying:
Ɱ                  -     map (across implicit range [1..x]):
ḷ                   -       left argument
- i.e. B and D copies of the respective Patterns
"           - zip with ([A, C]) applying:
¦            -   sparse application...
R}             -   ...to indices: range of right -> [1..A] or [1..C]
~               -   ...action: bitwise NOT -> 0:-1 and 1:-2
ṭ€1        - tack each to a one - handles the leading "|"
“|x-”  - list of characters = "|x-"
ị       - (repeated, augmented Patterns) index into (those)
- 1-based and modular so -2 & 1 are '|',
0 is '-', and -1 is 'x'.
Y - join with a newline
- implicit, smashing print


# J, 55 52 51 bytes

'-x|'{~(*.(%*1+e.),)&{:(2,~&,2,.}.@#"+)&>;&((>i.)/)


Try it online!

Great challenge, surprisingly difficult to golf, and tried a few different approaches just to get this:

The basic idea is:

1. First create each fraction with no dividing bars, using 0 for "empty" and 1 for "full". Eg, $$\ \frac{2}{3}\$$ is 1 1 0 and $$\\frac{1}{5}\$$ is 1 0 0 0 0.

2. Next we compute the amount each number needs to "puff up", so that the final length is the modified LCM -- 15 in this case. The modification, described in many other answers, is to double the LCM when one of the denominators is the LCM.

3. "Puff up" each number as needed -- 5x for $$\ \frac{2}{3}\$$ and 3x for $$\\frac{1}{5}\$$ (extra spaces for clarity):

1 1 1 1 1   1 1 1 1 1   0 0 0 0 0
1 1 1  0 0 0  0 0 0  0 0 0  0 0 0

4. Change the first element in each group to a 2, to represent the divider, and append and extra 2 at the end:

2 1 1 1 1   2 1 1 1 1   2 0 0 0 0  2
2 1 1  2 0 0  2 0 0  2 0 0  2 0 0  2

5. Convert to ascii:

|xx|--|--|--|--|
|xxxx|xxxx|----|