# Help me with my polyrhythms

I'm a musician, and I need more polyrhythms in my life!

A polyrhythm occurs in music (and in nature) when two events (claps, notes, fireflies flashing etc.) are occurring at two different regular intervals. The two kinds of event happen a different number of times in the same interval.

If I tap with my left hand twice, and with my right hand 3 times, in the same space of time, it looks a little bit like this:

  ------
R . . .
L .  .


The hyphens at the top denote the length of the polyrthmic pattern, which is the lowest common multiple or 2 and 3. This can be understood as the point at which the pattern repeats.

There's also a 'metarhythm', which is the pattern produced when either hand is tapping:

  ------
R . . .
L .  .
M . ...


This is a simple, and very common polyrhythm, with a ratio of 3:2.

Let's just say I don't want to do a simple polyrhythm that I can work out in my head, so I need something to work it out for me. I could do it long-form on paper, or...

Rules:

• Write some code to generate and display a polyrhythm diagram, as described above.
• Any old language, try for the fewest bytes.
• Your code takes two arguments:
• Number of taps with the Left hand (positive integer)
• Number of taps with the right hand (positive integer)
• It will work out the length, which is the lowest common multiple for the two arguments.
• The top line will consist of two whitespace characters followed by hyphens displaying the length (length * '-')
• The second and third lines will show the pattern for the right and left hands:
• It will start with an R or L, do denote which hand it is, followed by a space.
• The interval for that hand is the length divided by it's argument.
• The taps will start at the third character, denoted by any character you choose. From then on it will display the same character 'interval' characters apart.
• It will not be longer than the length line.
• The fourth line is the metarhythm:
• It will start with an upper case M, followed by a space.
• From the third character onwards, it will show a character (any character you choose) in every position where there's a tap on either the right or the left hand.
• Trailing whitespace is irrelevant.

Test cases:

r = 3, l = 2

  ------
R . . .
L .  .
M . ...


r = 4, l = 3

  ------------
R .  .  .  .
L .   .   .
M .  .. . ..


r = 4, l = 5

  --------------------
R .    .    .    .
L .   .   .   .   .
M .   ..  . . .  ..


r = 4, l = 7

  ----------------------------
R .      .      .      .
L .   .   .   .   .   .   .
M .   .  ..   . . .   ..  .


r = 4, l = 8

  --------
R . . . .
L ........
M ........


Happy golfing!

• Your test cases include lots of trailing whitespace, can we omit them/add more? – wastl Aug 1 '18 at 11:57
• Do we have to accept r and l as two separate values? Could we accept a two-element array instead, for example? How about the order of them, is that strictly r followed by l? – Sok Aug 1 '18 at 15:00
• @Sok That's acceptable as an interpretation of 'two arguments' – AJFaraday Aug 1 '18 at 15:02
• Does it need to actually print the diagram out, or can it simply return it? – Reinstate Monica -- notmaynard Aug 1 '18 at 17:15
• @iamnotmaynard returning is fine. – AJFaraday Aug 1 '18 at 18:26

# JavaScript (ES6), 131 bytes

Outputs 0 as the tap character.

r=>l=>  ${g=n=>n?s.replace(/./g,(_,x)=>[,a=x%(k/r),x%=k/l,a*x][n]&&' '):++k%l|k%r?'-'+g():- ,s=g(k=0)}R${g(1)}L ${g(2)}M +g(3)  Try it online! ### How? We use the same helper function $g()$ for two different purposes. When $g()$ is called with no argument or an argument equal to $0$, it recursively builds the hyphen string of length $k=\operatorname{lcm}(l,r)$ with a trailing linefeed: g = _ => ++k % l | k % r ? '-' + g() : -\n  This string is saved in $s$. When $g()$ is called with $1 \le n \le 3$, it generates a tap string by replacing each hyphen at position $x$ in $s$ with either a space or $0$: g = n => s.replace(/./g, (_, x) => [, a = x % (k / r), x %= k / l, a * x][n] && ' ')  # Java 11, 226234233 219 bytes String h(int r,int l,int m){var s="";for(;m>0;)s+=m%r*(m--%l)<1?'.':32;return s;} r->l->{int a=r,b=l,m;for(;b>0;b=a%b,a=m)m=b;m=r*l/a;return" "+repeat("-",m)+"\nR "+h(m/r,m+1,m)+"\nL "+h(m/l,m+1,m)+"\nM "+h(m/r,m/l,m);}  Kind of lengthy; too bad Java does not have an lcm() function. Try it online here (TIO does not have Java 11 yet, so this uses a helper method instead of String.repeat()). My initial version took the interval between taps instead of the number of taps. Fixed now. Thanks to Kevin Cruijssen for golfing 1 byte. Ungolfed: String h(int r, int l, int m) { // helper function returning a line of metarhythm; parameters are: tap interval (right hand), tap interval (left hand), length var s = ""; // start with an empty String for(; m > 0; ) // repeat until the length is reached s += m % r * (m-- % l) < 1 ? '.' : 32; // if at least one of the hands taps, add a dot, otherwise add a space (ASCII code 32 is ' ') return s; // return the constructed line } r -> l -> { // lambda taking two integers in currying syntax and returning a String int a = r, b = l, m; // duplicate the inputs for(; b > 0; b = a % b, a = m) // calculate the GCD of r,l using Euclid's algorithm: m=b; // swap and replace one of the inputs by the remainder of their division; stop once it hits zero m = r * l / a; // calculate the length: LCM of r,l using a=GCD(r,l) return // build and return the output: " " + "-".repeat(m) // first line, m dashes preceded by two spaces + "\nR " + h(m / r, m + 1, m) // second line, create the right-hand rhythm; by setting l = m + 1 for a metarhythm, we ensure there will be no left-hand taps + "\nL " + h(m / l, m + 1, m) // third line, create the left-hand rhythm the same way; also note that we pass the tap interval instead of the number of taps + "\nM " + h(m / r, m / l, m); // fourth line, create the actual metarhythm }  • It's not much, but -1 byte by changing ?".":" " to ?'.':32. – Kevin Cruijssen Aug 1 '18 at 14:42 • @KevinCruijssen Every byte counts :-) Thanks! – O.O.Balance Aug 1 '18 at 14:49 # Python 2, 187185183174166156148147 145 bytes Uses - as the tap character a,b=r,l=input() while b:a,b=b,a%b w=r*l/a for x,y,z in zip(' RLM',(w,r,l,r),(w,r,l,l)):print x,''.join('- '[i%(w/y)!=0<i%(w/z)]for i in range(w))  Try it online! Saved: • -2 bytes, thanks to Jonathan Frech • [i%(w/y)and i%(w/z)>0] could be [i%(w/y)!=0<i%(w/z)]. – Jonathan Frech Aug 1 '18 at 17:27 • @JonathanFrech Thanks :) – TFeld Aug 2 '18 at 6:57 # Perl 6, 8580 78 bytes -2 bytes thanks to Jo King. {«' 'R L M»Z~'-'x($!=[lcm] @_),|(@_.=map:{' '~(0~' 'x$!/$_-1)x$_}),[~|] @_}  Try it online! Returns a list of four lines. # Python 2, 185 228 223 234 249 bytes def f(r,l): c='.';d=' ';M,R,L=[r*l*[d]for _ in d*3] for i in range(r*l): if i%r<1:L[i]=M[i]=c if i%l<1:R[i]=M[i]=c if r<R.count(c)and l<L.count(c):R[i]=L[i]=M[i]=d;break print d,i*'-','\nR',''.join(R),'\nL',''.join(L),'\nM',''.join(M)  Try it online! • I've just copy-pasted this into TIO and taken the generated format from there. Turns out it's done in fewer bytes than you thought ;) – AJFaraday Aug 1 '18 at 14:05 • @Tfeld r=4, l=8 works fine for me – sonrad10 Aug 1 '18 at 16:37 • The length is supposed to be the lowest common multiple. With r=4, l=8, that should be 8, but it appears your output is much longer (8*4?). – O.O.Balance Aug 1 '18 at 16:57 • That still does not give the LCM; eg for 15,25, it gives 375, but it should be 75. – O.O.Balance Aug 1 '18 at 19:08 • I believe the last check can be replaced by i%r+i%l+0**i<1. Also, you can remove previous versions of code, as they will be preserved in your edit history of anyone wants to see them – Jo King Aug 3 '18 at 15:14 # Jelly, 32 bytes æl/Ḷ%Ɱµa/ṭ=0ị⁾. Z”-;ⱮZ“ RLM”żK€Y  Try it online! Takes input as a list [L,R]. æl/ Get LCM of this list. Ḷ Range [0..LCM-1] %Ɱ Modulo by-each-right (implicitly the input, [L,R]): [[0%L ... (LCM-1)%L], [0%R ... (LCM-1)%R]] µ Take this pair of lists, and: a/ṭ Append their pairwise AND to the pair. =0 Is zero? Now we have a result like: [[1 0 0 1 0 0 1 0 0 1 0 0 1 0 0] [1 0 0 0 0 1 0 0 0 0 1 0 0 0 0] [1 0 0 1 0 1 1 0 0 1 1 0 1 0 0]] ị⁾. Convert this into dots and spaces. Z”-;ⱮZ Transpose, prepend a dash to each, transpose. Now we have ['---------------' '. . . . . ' '. . . ' '. . .. .. . '] “ RLM”ż zip(' RLM', this) K€ Join each by spaces. Y Join the whole thing by newlines.  # C (gcc), 204 bytes p(s){printf(s);} g(a,b){a=b?g(b,a%b):a;} h(r,l,m){for(;m;)p(m%r*(m--%l)?" ":".");} f(r,l,m,i){m=r*l/g(r,l);p(" ");for(i=m;i-->0;)p("-");p("\nR ");h(m/r,m+1,m);p("\nL ");h(m/l,m+1,m);p("\nM ");h(m/r,m/l,m);}  Port of my Java answer. Call with f(number_of_right_hand_taps, number_of_left_hand_taps). Try it online here. # Pyth, 53 bytes j.b+NYc" L R M "2++*\-J/*FQiFQKm*d+N*\ t/JdQsmeSd.TK  Definitely room to golf. Will do so when I have time. Try it here ### Explanation j.b+NYc" L R M "2++*\-J/*FQiFQKm*d+N*\ t/JdQsmeSd.TK J/*FQiFQ Get the LCM. *\- Take that many '-'s. Km*d+N*\ t/dJQ Fill in the taps. smeSd.TK Get the metarhythm. ++ Append them all. c" L R M "2 Get the prefixes. .b+NY Prepend the prefixes. j Join with newlines.  # C# (Visual C# Interactive Compiler), 254 bytes Golfed Try it online! (r,l)=>{int s=l>r?l:r,S=s;while(S%l>0|S%r>0)S+=s;string q(int a){return"".PadRight(S/a,'.').Replace(".",".".PadRight(a,' '));}string R=q(S/r),L=q(S/l),M="";s=S;while(S-->0)M=(R[S]+L[S]>64?".":" ")+M;return" ".PadRight(s+2,'-')+$"\nR {R}\nL {L}\nM {M}";}


Ungolfed

( r, l ) => {
int
s = l > r ? l : r,
S = s;

while( S % l > 0 | S % r > 0 )
S += s;

string q( int a ) {
return "".PadRight( S / a, '.' ).Replace( ".", ".".PadRight( a, ' ' ) );
}

string
R = q( S / r ),
L = q( S / l ),
M = "";

s = S;

while( S-- > 0 )
M = ( R[ S ] + L[ S ] > 64 ? "." : " " ) + M;

return "  ".PadRight( s + 2, '-') + $"\nR {R}\nL {L}\nM {M}"; }  Full code Func<Int32, Int32, String> f = ( r, l ) => { int s = l > r ? l : r, S = s; while( S % l > 0 | S % r > 0 ) S += s; string q( int a ) { return "".PadRight( S / a, '.' ).Replace( ".", ".".PadRight( a, ' ' ) ); } string R = q( S / r ), L = q( S / l ), M = ""; s = S; while( S-- > 0 ) M = ( R[ S ] + L[ S ] > 64 ? "." : " " ) + M; return " ".PadRight( s + 2, '-') +$"\nR {R}\nL {L}\nM {M}";
};

Int32[][]
testCases = new Int32[][] {
new []{ 3, 2 },
new []{ 4, 3 },
new []{ 4, 5 },
new []{ 4, 7 },
new []{ 4, 8 },
};

foreach( Int32[] testCase in testCases ) {
Console.Write( $" Input: R: {testCase[0]}, L: {testCase[1]}\nOutput:\n{f(testCase[0], testCase[1])}" ); Console.WriteLine("\n"); } Console.ReadLine();  Releases • v1.0 - 254 bytes - Initial solution. Notes • None # Charcoal, 52 bytes ≔θζＷ﹪ζη≧⁺θζζ↙≔⮌Ｅζ⟦¬﹪×ιθζ¬﹪×ιηζ⟧ζＦζ⊞ι⌈ι↓Ｅζ⭆ι§ .λ←↓RLM  Try it online! Link is to verbose version of code. Explanation: ≔θζＷ﹪ζη≧⁺θζ  Calculate the LCM of the inputs by taking the first multiple of R that's divisible by L. ζ↙  Print the LCM, which automatically outputs the necessary row of -s. Then move to print the rhythm from right to left. ≔⮌Ｅζ⟦¬﹪×ιθζ¬﹪×ιηζ⟧ζ  Loop over the numbers from the LCM down to 0 and create an array of lists representing the beats of the right and left hands. Ｆζ⊞ι⌈ι  Loop over the beats and add the metarhythm. ↓Ｅζ⭆ι§ .λ  Print the reversed beats downwards, but as this is an array they end up leftwards. ←↓RLM  Print the header. # Ruby, 130 126 bytes ->*a{puts" "+?-*s=a[0].lcm(a[1]) r,l=a.map!{|e|(?.+' '*(s/e-1))*e} [?R,?L,?M].zip(a<<r.gsub(/ /){l[$.size]}){|e|puts e*" "}}


Try it online!

# Python 2, 117 bytes

a,b=input();n=a
while n%b:n+=a
for i in-1,1,2,3:print'_RLM '[i],''.join(' -'[i%2>>m*a%n|i/2>>m*b%n]for m in range(n))


Try it online!

# Pyth, 49 bytes

J/*FQiFQjC+c2" RLM    "ms@L" -"!M++0d*Fdm%Ld/LJQJ


Expects input in the form [r,l]. Uses - to display taps. Try it online here, or verify all test cases at once here.

J/*FQiFQjC+c2" RLM    "ms@L" -"!M++0d*Fdm%Ld/LJQJ   Implicit: Q=eval(input())
/*FQiFQ                                            Compute LCM: (a*b)/(GCD(a,b))
J                                                   Store in J
m       J   Map d in [0-LCM) using:
/LJQ      Get number of beats between taps for each hand
%Ld          Take d mod each of the above
This gives a pair for each beat, with 0 indicating a tap
m                            Map d in the above using:
*Fd              Multiply each pair (effecively an AND)
++0d                 Prepend 0 and the original pair
!M                     NOT each element
s@L" -"                       Map [false, true] to [' ', '-'], concatenate strings
This gives each column of the output
c2" RLM    "                             [' RLM','    ']
+                                         Prepend the above to the rest of the output
C                                          Transpose
j                                           Join on newlines, implicit print


# R, 161149 146 bytes

function(a,b){l=numbers::LCM(a,b)
d=c(0,' ')
cat('  ',strrep('-',l),'\nR ',d[(x<-l:1%%a>0)+1],'\nL ',d[(y<-l:1%%b>0)+1],'\nM ',d[(x&y)+1],sep='')}


Try it online!

I definitely feel like there's room for improvement here, but I tried a few different approaches and this is the only one that stuck. Getting rid of the internal function definition would make me quite happy, and I tried a bunch of restructures of the cat() to make it happen. Nevermind, as soon as I posted I realised what I could do. Still definitely some efficiency savings to be found.

There are other LCM functions in libraries with shorter names, but TIO has numbers and I considered that more valuable at this point.

# C++ (gcc), 197 bytes

int f(int a,int b){std::string t="  ",l="\nL ",r="\nR ",m="\nM ";int c=-1,o,p=0;for(;++p%a||p%b;);for(;o=++c<p;t+="-")l+=a*c%p&&++o?" ":".",r+=b*c%p&&++o?" ":".",m+=o-3?".":" ";std::cout<<t+l+r+m;}


Try it online!

• Suggest ++p%a+p%b instead of ++p%a||p%b` – ceilingcat Sep 22 '18 at 16:06