Visualize the greatest common divisor Background

The greatest common divisor (gcd for short) is a convenient mathematical function, since it has many useful properties.
One of them is Bézout's identity: if d = gcd(a, b), then there exist integers x and y such that d = x*a + y*b.
In this challenge, your task is to visualize this property with simple ASCII art.

Input

Your inputs are two positive integers a and b, given in any reasonable format.
You may also take unary inputs (repetitions of a single printable ASCII character of your choice), but you must be consistent and use the same format for both inputs.
The inputs may be in any order, and they may be equal.

Output

Your output is a string s of length lcm(a, b) + 1 (lcm stands for lowest common multiple).
The characters of s represent integers from 0 to lcm(a, b).
The character s[i] is a lowercase o if i is a multiple of a or b, and a period . otherwise.
Note that zero is a multiple of every number.
Now, because of Bézout's identity, there will be at least one pair of characters o in s whose distance is exactly gcd(a, b).
The leftmost such pair is to be replaced by uppercase Os; this is the final output.

Example

Consider the inputs a = 4 and b = 6.
Then we have gcd(a, b) = 2 and lcm(a, b) = 12, so the length of s will be 13.
The multiples of a and b are highlighted as follows:

0  1  2  3  4  5  6  7  8  9 10 11 12
o  .  .  .  o  .  o  .  o  .  .  .  o


There are two pairs of os with distance two, but we will only replace the leftmost ones with Os, so the final output is

o...O.O.o...o


Rules and scoring

You can write a full program or a function.
The lowest byte count wins, and standard loopholes are disallowed.

Test cases

 1  1 -&gt; OO
 2  2 -&gt; O.O
 1  3 -&gt; OOoo
 4  1 -&gt; OOooo
 2  6 -&gt; O.O.o.o
 2  3 -&gt; o.OOo.o
10  2 -&gt; O.O.o.o.o.o
 4  5 -&gt; o...OO..o.o.o..oo...o
 8  6 -&gt; o.....O.O...o...o.o.....o
12 15 -&gt; o...........O..O........o.....o.....o........o..o...........o
19 15 -&gt; o..............o...o..........o.......o......o...........o..o..............OO.............o....o.........o........o.....o............o.o..............o.o............o.....o........o.........o....o.............oo..............o..o...........o......o.......o..........o...o..............o
Retina, 112 109 99 94 91 bytes

^
. 
+r`(?&lt;!^\1+). (.+) 
$'$0
.(?=.* (.+) (.+))(?=\1* |\2* )
o
o(\.*)o((\1\.*o)*) .*
O$1O$2


Not very competitive, I think, but number theory in Retina is always quite fun. :)

Takes input as unary numbers using . as the unary digit.

Try it online.

Explanation

^
. 


This inserts a . and a space in front of the input. This will ultimately become the output.

+r`(?&lt;!^\1+). (.+) 
$'$0


This prepends the LCM of a and b to the string. Since we already have a . there, we'll end up with lcm(a,b)+1. This is accomplished by repeatedly prepending b as long as a does not divide this new prefix. We capture a into a group one and then check if we can reach the beginning of the string by matching that capture at least once. b is then inserted into the string via the rarely used $' which inserts everything after the match into the substitution.

.(?=.* (.+) (.+))(?=\1* |\2* )
o


This one matches characters at positions which are divided by a or b. It makes use of the fact that the result is symmetric: since lcm(a,b) is divided by both a and b going left by subtracting instances of a or b yields the same pattern as going right from 0 by adding them. The first lookahead simply captures a and b. The second lookahead checks that there is a multiple of each a or b characters before the first space.

o(\.*)o((\1\.*o)*) .*
O$1O$2


As stated on Wikipedia, in addition to Bézout's identity it is also true that


  The greatest common divisor d is the smallest positive integer that can be written as ax + by.


This implies that the GCD will correspond to the shortest gap between two os in the output. So we don't have to bother finding the GCD at all. Instead we just look for first instance of the shortest gap. o(\.*)o matches a candidate gap and captures its width into group 1. Then we try to reach the first space by alternating between a backreference to group 1 and os (with optional additional .s). If there is a shorter gap further to the right, this will fail to match, because we cannot get past that gap with the backreference. As soon as all further gaps are at least as wide as the current one, this matches. We capture the end of the LCM-string into group 2 and match the remainder of the string with .*. We write back the uppercase Os (with the gap in between) as well as the remainder of the LCM string, but discard everything starting from the space, to remove a and b from final result.