File Coverage

blib/lib/Music/CreatingRhythms.pm
Criterion Covered Total %
statement 361 378 95.5
branch 85 104 81.7
condition 38 51 74.5
subroutine 52 53 98.1
pod 29 29 100.0
total 565 615 91.8


line stmt bran cond sub pod time code
1             package Music::CreatingRhythms;
2             our $AUTHORITY = 'cpan:GENE';
3              
4             # ABSTRACT: Combinatorial algorithms to generate rhythms
5              
6             our $VERSION = '0.0806';
7              
8 2     2   539730 use strictures 2;
  2         2774  
  2         62  
9 2     2   1520 use Algorithm::Combinatorics qw(permutations);
  2         5727  
  2         134  
10 2     2   923 use Data::Munge qw(list2re);
  2         2723  
  2         106  
11 2     2   840 use Integer::Partition ();
  2         1723  
  2         58  
12 2     2   12 use List::Util qw(all any);
  2         3  
  2         164  
13 2     2   870 use Math::Sequence::DeBruijn qw(debruijn);
  2         4659  
  2         119  
14 2     2   1130 use Module::Load::Conditional qw(check_install);
  2         48114  
  2         153  
15 2     2   1212 use Moo;
  2         13879  
  2         10  
16 2     2   3850 use Music::AtonalUtil ();
  2         16428  
  2         115  
17              
18 2         11 use if defined check_install(module => 'Math::NumSeq::SqrtContinued'),
19 2     2   21 'Math::NumSeq::SqrtContinued';
  2         5  
20              
21 2     2   2026 use namespace::clean;
  2         29263  
  2         10  
22              
23              
24             has verbose => (
25             is => 'ro',
26             isa => sub { die "$_[0] is not a boolean" unless $_[0] =~ /^[01]$/ },
27             default => sub { 0 },
28             );
29              
30              
31              
32             sub b2int {
33 9     9 1 5600 my ($self, $sequences) = @_;
34 9         13 my @intervals;
35 9         14 for my $i (@$sequences) {
36 14         33 my $string = join '', @$i;
37 14         58 push @intervals, [ map { length $_ } grep { $_ } split /(10*)/, $string ];
  27         43  
  53         79  
38             }
39 9         26 return \@intervals;
40             }
41              
42              
43             sub cfcv {
44 5     5 1 2168 my ($self, @terms) = @_;
45              
46 5         7 my $p0 = 0;
47 5         6 my $p1 = 1;
48 5         5 my $p2;
49 5         5 my $q0 = 1;
50 5         4 my $q1 = 0;
51 5         5 my $q2;
52              
53 5         10 for my $t (@terms) {
54 17         18 $p2 = $t * $p1 + $p0;
55 17         16 $q2 = $t * $q1 + $q0;
56 17         14 $p0 = $p1;
57 17         14 $p1 = $p2;
58 17         15 $q0 = $q1;
59 17         17 $q1 = $q2;
60             }
61              
62 5         13 return [ $p2, $q2 ];
63             }
64              
65              
66             sub cfsqrt {
67 0     0 1 0 my ($self, $n, $m) = @_;
68 0   0     0 $m ||= $n;
69 0         0 my @terms;
70              
71 0         0 my $seq = Math::NumSeq::SqrtContinued->new(sqrt => $n);
72 0         0 for my $i (1 .. $m) {
73 0         0 my ($j, $value) = $seq->next;
74 0         0 push @terms, $value;
75             }
76 0         0 return \@terms;
77             }
78              
79              
80             sub chsequl {
81 16     16 1 7673 my ($self, $t, $p, $q, $n) = @_;
82 16 50 33     70 die "Usage: chsequl(\$type, \$numerator, \$denominator [\$terms])\n"
      33        
83             unless $t && defined $p && defined $q;
84 16   66     44 $n ||= $p + $q;
85 16         16 my @word;
86 16         17 my $i = 0;
87 16         27 while ($i < $n) {
88 18 100       35 push @word, $t eq 'u' ? 1 : 0;
89 18         18 $i++;
90 18         21 my ($x, $y) = ($p, $q);
91 18   100     43 while ($x != $y && $i < $n) {
92 40 100       43 if ($x > $y) {
93 28         29 push @word, 1;
94 28         28 $y += $q;
95             }
96             else {
97 12         14 push @word, 0;
98 12         12 $x += $p;
99             }
100 40         71 $i++;
101             }
102 18 100 66     41 if ($x == $y && $i < $n) {
103 12 100       18 push @word, $t eq 'u' ? 0 : 1;
104 12         20 $i++;
105             }
106             }
107 16         31 return \@word;
108             }
109              
110              
111             sub comp {
112 4     4 1 2637 my ($self, $n) = @_;
113 4         6 my @compositions;
114             my @parts;
115 4         5 my $i = 0;
116 4         48 _compose($n - 1, 1, 0, \$i, \@compositions, \@parts);
117 4         10 return \@compositions;
118             }
119              
120             sub _compose {
121 26     26   33 my ($n, $p, $k, $i, $compositions, $parts) = @_;
122 26 100       35 if ($n == 0) {
123 15         22 while ($n < $k) {
124 17         17 push @{ $compositions->[$$i] }, $parts->[$n];
  17         21  
125 17         32 $n++;
126             }
127 15         13 push @{ $compositions->[$$i] }, $p;
  15         20  
128 15         13 $$i++;
129 15         20 return;
130             }
131 11         14 $parts->[$k] = $p;
132 11         23 _compose($n - 1, 1, $k + 1, $i, $compositions, $parts);
133 11         17 _compose($n - 1, $p + 1, $k, $i, $compositions, $parts);
134             }
135              
136              
137             sub compa {
138 7     7 1 4355 my ($self, $n, @intervals) = @_;
139 7         8 my @compositions;
140             my @parts;
141 7         8 my $i = 0;
142 7         19 _composea($n - 1, 1, 0, \$i, \@compositions, \@parts, \@intervals);
143 7         18 return \@compositions;
144             }
145              
146             sub _composea {
147 49     49   66 my ($n, $p, $k, $i, $compositions, $parts, $intervals) = @_;
148 49 100       68 if ($n == 0) {
149 25 100       28 if (_allowed($p, $intervals)) {
150 16         24 while ($n < $k) {
151 27         24 push @{ $compositions->[$$i] }, $parts->[$n];
  27         37  
152 27         31 $n++;
153             }
154 16         16 push @{ $compositions->[$$i] }, $p;
  16         21  
155 16         17 $$i++;
156             }
157 25         42 return;
158             }
159 24 100       26 if (_allowed($p, $intervals)) {
160 18         19 $parts->[$k] = $p;
161 18         30 _composea($n - 1, 1, $k + 1, $i, $compositions, $parts, $intervals);
162             }
163 24         49 _composea($n - 1, $p + 1, $k, $i, $compositions, $parts, $intervals);
164             }
165              
166              
167             sub compam {
168 7     7 1 4107 my ($self, $n, $m, @intervals) = @_;
169 7         11 $m--;
170 7         9 my @compositions;
171             my @parts;
172 7         7 my $i = 0;
173 7         17 _composeam($n - 1, 1, 0, $m, \$i, \@compositions, \@parts, \@intervals);
174 7         38 return \@compositions;
175             }
176              
177             sub _composeam {
178 44     44   62 my ($n, $p, $k, $m, $i, $compositions, $parts, $intervals) = @_;
179 44 100       58 if ($n == 0) {
180 21 100 66     32 if ($k == $m && _allowed($p, $intervals)) {
181 10         49 while ($n < $k) {
182 15         14 push @{ $compositions->[$$i] }, $parts->[$n];
  15         40  
183 15         20 $n++;
184             }
185 10         11 push @{ $compositions->[$$i] }, $p;
  10         14  
186 10         11 $$i++;
187             }
188 21         40 return;
189             }
190 23 100 100     49 if ($k < $m && _allowed($p, $intervals)) {
191 14         16 $parts->[$k] = $p;
192 14         26 _composeam($n - 1, 1, $k + 1, $m, $i, $compositions, $parts, $intervals);
193             }
194 23         41 _composeam($n - 1, $p + 1, $k, $m, $i, $compositions, $parts, $intervals);
195             }
196              
197              
198             sub compm {
199 7     7 1 4643 my ($self, $n, $m) = @_;
200 7         12 $m--;
201 7         8 my @compositions;
202             my @parts;
203 7         7 my $i = 0;
204 7         17 _composem($n - 1, 1, 0, $m, \$i, \@compositions, \@parts);
205 7         16 return \@compositions;
206             }
207              
208             sub _composem {
209 57     57   77 my ($n, $p, $k, $m, $i, $compositions, $parts) = @_;
210 57 100       68 if ($n == 0) {
211 22 100       29 if ($k == $m) {
212 16         23 while ($n < $k) {
213 15         14 push @{ $compositions->[$$i] }, $parts->[$n];
  15         21  
214 15         23 $n++;
215             }
216 16         16 push @{ $compositions->[$$i] }, $p;
  16         17  
217 16         35 $$i++;
218             }
219 22         31 return;
220             }
221 35 100       42 if ($k < $m) {
222 15         18 $parts->[$k] = $p;
223 15         22 _composem($n - 1, 1, $k + 1, $m, $i, $compositions, $parts);
224             }
225 35         55 _composem($n - 1, $p + 1, $k, $m, $i, $compositions, $parts);
226             }
227              
228              
229             sub compmrnd {
230 3     3 1 1646 my ($self, $n, $m) = @_;
231 3 100       9 return [0] unless $n;
232 2         3 my @compositions;
233 2         4 my ($p, $j, $np);
234 2         6 for(my $mp = $m - 1, $np = $n - 1, $j = 1; $mp > 0; --$np) {
235 3         5 $p = $mp / $np;
236 3 50       8 if ($p % 2 == 0) {
237 3         4 push @compositions, $j;
238 3         13 $mp--;
239 3         6 $j = 1;
240             }
241             else {
242 0         0 $j++;
243             }
244             }
245 2         3 push @compositions, $j + $np;
246 2         5 return \@compositions;
247             }
248              
249              
250             sub comprnd {
251 3     3 1 1605 my ($self, $n) = @_;
252 3 100       8 return [0] unless $n;
253 2         3 my @compositions;
254 2         3 my $p = 1;
255 2         6 for my $i (1 .. $n - 1) {
256 15 100       71 if ((int rand 2) % 2 == 0) {
257 8         9 $p++;
258             }
259             else {
260 7         10 push @compositions, $p;
261 7         8 $p = 1;
262             }
263             }
264 2         3 push @compositions, $p;
265 2         6 return \@compositions;
266             }
267              
268              
269             sub count_digits {
270 12     12 1 18 my ($self, $digit, $n) = @_;
271 12         15 my $x = 0;
272 12 100       22 if (ref $n) {
273 6         12 for my $i (@$n) {
274 10 100       19 $x++ if $i == $digit;
275             }
276             }
277             else {
278 6 100       14 if ($digit == 0) { $x = $n =~ tr/0// }
  3 50       7  
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
279 3         7 elsif ($digit == 1) { $x = $n =~ tr/1// }
280 0         0 elsif ($digit == 2) { $x = $n =~ tr/2// }
281 0         0 elsif ($digit == 3) { $x = $n =~ tr/3// }
282 0         0 elsif ($digit == 4) { $x = $n =~ tr/4// }
283 0         0 elsif ($digit == 5) { $x = $n =~ tr/5// }
284 0         0 elsif ($digit == 6) { $x = $n =~ tr/6// }
285 0         0 elsif ($digit == 7) { $x = $n =~ tr/7// }
286 0         0 elsif ($digit == 8) { $x = $n =~ tr/8// }
287 0         0 elsif ($digit == 9) { $x = $n =~ tr/9// }
288             }
289 12         25 return $x;
290             }
291              
292              
293             sub count_ones {
294 6     6 1 3440 my ($self, $n) = @_;
295 6         16 return $self->count_digits(1, $n);
296             }
297              
298              
299             sub count_zeros {
300 6     6 1 3862 my ($self, $n) = @_;
301 6         14 return $self->count_digits(0, $n);
302             }
303              
304              
305             sub de_bruijn {
306 4     4 1 1946 my ($self, $n) = @_;
307 4 100       31 my $sequence = $n ? debruijn([1,0], $n) : 0;
308 4         211 return [ split //, $sequence ];
309             }
310              
311              
312             sub euclid {
313 7     7 1 3520 my ($self, $n, $m) = @_;
314 7         11 my $intercept = 1;
315 7         11 my $slope = $n / $m;
316 7         18 my @pattern = ('0') x $m;
317 7         15 for my $y ( 1 .. $n ) {
318 13         49 $pattern[ sprintf '%.0f', ( $y - $intercept ) / $slope ] = '1';
319             }
320 7         17 return \@pattern;
321             }
322              
323              
324             sub int2b {
325 2     2 1 1044 my ($self, $intervals) = @_;
326 2         4 my @sequences;
327 2         3 for my $i (@$intervals) {
328 4         5 my @bitstring;
329 4         6 for my $j (@$i) {
330 6         9 my $bits = '1' . '0' x ($j - 1);
331 6         15 push @bitstring, split //, $bits;
332             }
333 4         5 push @sequences, \@bitstring;
334             }
335 2         4 return \@sequences;
336             }
337              
338              
339             sub invert_at {
340 6     6 1 3041 my ($self, $n, $parts) = @_;
341 6         20 my @head = @$parts[ 0 .. $n - 1 ];
342 6 100       13 my @tail = map { $_ ? 0 : 1 } @$parts[ $n .. $#$parts ];
  15         27  
343 6         14 my @data = (@head, @tail);
344 6         16 return \@data;
345             }
346              
347              
348             sub neck {
349 4     4 1 3186 my ($self, $n) = @_;
350 4         5 my @necklaces;
351 4         8 my @parts = (1);
352 4         6 my $i = 0;
353 4         9 _neckbin($n, 1, 1, \$i, \@necklaces, \@parts);
354 4         11 return \@necklaces;
355             }
356              
357             sub _neckbin {
358 39     39   54 my ($n, $k, $l, $i, $necklaces, $parts) = @_;
359             # k = length of necklace
360             # l = length of longest prefix that is a lyndon word
361 39 100       49 if ($k > $n) {
362 18 100       31 if(($n % $l) == 0) {
363 15         20 for $k (1 .. $n) {
364 44         43 push @{ $necklaces->[$$i] }, $parts->[$k];
  44         66  
365             }
366 15         22 $$i++;
367             }
368             }
369             else {
370 21         25 $parts->[$k] = $parts->[ $k - $l ];
371 21 100       29 if ($parts->[$k] == 1) {
372 14         30 _neckbin($n, $k + 1, $l, $i, $necklaces, $parts);
373 14         16 $parts->[$k] = 0;
374 14         21 _neckbin($n, $k + 1, $k, $i, $necklaces, $parts);
375             }
376             else {
377 7         9 _neckbin($n, $k + 1, $l, $i, $necklaces, $parts);
378             }
379             }
380             }
381              
382              
383             sub necka {
384 7     7 1 4285 my ($self, $n, @intervals) = @_;
385 7         10 my @necklaces;
386 7         10 my @parts = (1);
387 7         8 my $i = 0;
388 7         16 _neckbina($n, 1, 1, 1, \$i, \@necklaces, \@parts, \@intervals);
389 7         23 return \@necklaces;
390             }
391              
392             sub _neckbina {
393 73     73   100 my ($n, $k, $l, $p, $i, $necklaces, $parts, $intervals) = @_;
394 73 100       91 if ($k > $n) {
395 30 100 100     55 if (($n % $l) == 0 && _allowed($p, $intervals) && $p <= $n) {
      100        
396 11         30 for $k (1 .. $n) {
397 38         35 push @{ $necklaces->[$$i] }, $parts->[$k];
  38         66  
398             }
399 11         16 $$i++;
400             }
401             }
402             else {
403 43         50 $parts->[$k] = $parts->[ $k - $l ];
404 43 100       72 if ($parts->[$k] == 1) {
405 29 100 100     36 if (_allowed($p, $intervals) || $k == 1) {
406 23         38 _neckbina($n, $k + 1, $l, 1, $i, $necklaces, $parts, $intervals);
407             }
408 29         68 $parts->[$k] = 0;
409 29         50 _neckbina($n, $k + 1, $k, $p + 1, $i, $necklaces, $parts, $intervals);
410             }
411             else {
412 14         21 _neckbina($n, $k + 1, $l, $p + 1, $i, $necklaces, $parts, $intervals);
413             }
414             }
415             }
416              
417              
418             sub neckam {
419 7     7 1 3957 my ($self, $n, $m, @intervals) = @_;
420 7         10 my @necklaces;
421 7         9 my @parts = (1);
422 7         8 my $i = 0;
423 7         30 _neckbinam($n, 1, 1, 0, 1, $m, \$i, \@necklaces, \@parts, \@intervals);
424 7         21 return \@necklaces;
425             }
426              
427             sub _neckbinam {
428 74     74   115 my ($n, $k, $l, $q, $p, $m, $i, $necklaces, $parts, $intervals) = @_;
429 74 100       92 if ($k > $n) {
430 31 100 100     64 if(($n % $l) == 0 && _allowed($p, $intervals) && $p <= $n && $q == $m) {
      66        
      100        
431 7         14 for $k (1 .. $n) {
432 22         21 push @{ $necklaces->[$$i] }, $parts->[$k];
  22         33  
433             }
434 7         13 $$i++;
435             }
436             }
437             else {
438 43         48 $parts->[$k] = $parts->[ $k - $l ];
439 43 100       51 if ($parts->[$k] == 1) {
440 29 100 66     35 if (_allowed($p, $intervals) || $k == 1) {
441 24         48 _neckbinam($n, $k + 1, $l, $q + 1, 1, $m, $i, $necklaces, $parts, $intervals);
442             }
443 29         59 $parts->[$k] = 0;
444 29         41 _neckbinam($n, $k + 1, $k, $q, $p + 1, $m, $i, $necklaces, $parts, $intervals);
445             }
446             else {
447 14         18 _neckbinam($n, $k + 1, $l, $q, $p + 1, $m, $i, $necklaces, $parts, $intervals);
448             }
449             }
450             }
451              
452              
453             sub neckm {
454 7     7 1 4272 my ($self, $n, $m) = @_;
455 7         10 my @necklaces;
456 7         9 my @parts = (1);
457 7         11 my $i = 0;
458 7         18 _neckbinm($n, 1, 1, 0, $m, \$i, \@necklaces, \@parts);
459 7         19 return \@necklaces;
460             }
461              
462             sub _neckbinm {
463 131     131   178 my ($n, $k, $l, $p, $m, $i, $necklaces, $parts) = @_;
464             # k = length of necklace
465             # l = length of longest prefix that is a lyndon word
466             # p = number of parts (ones)
467 131 100       152 if ($k > $n) {
468 57 100 100     149 if (($n % $l) == 0 && $p == $m) {
469 10         16 for $k (1 .. $n) {
470 42         35 push @{ $necklaces->[$$i] }, $parts->[$k];
  42         76  
471             }
472 10         16 $$i++;
473             }
474             }
475             else {
476 74         81 $parts->[$k] = $parts->[ $k - $l ];
477 74 100       82 if ($parts->[$k] == 1) {
478 50         103 _neckbinm($n, $k + 1, $l, $p + 1, $m, $i, $necklaces, $parts);
479 50         52 $parts->[$k] = 0;
480 50         97 _neckbinm($n, $k + 1, $k, $p, $m, $i, $necklaces, $parts);
481             }
482             else {
483 24         29 _neckbinm($n, $k + 1, $l, $p, $m, $i, $necklaces, $parts);
484             }
485             }
486             }
487              
488              
489             sub part {
490 4     4 1 2613 my ($self, $n) = @_;
491 4         21 my $i = Integer::Partition->new($n, { lexicographic => 1 });
492 4         90 my @partitions;
493 4         10 while (my $p = $i->next) {
494 11         144 push @partitions, [ sort { $a <=> $b } @$p ];
  14         28  
495             }
496 4         36 return \@partitions;
497             }
498              
499              
500             sub parta {
501 7     7 1 4293 my ($self, $n, @parts) = @_;
502 7         20 my $re = list2re @parts;
503 7         186 my $i = Integer::Partition->new($n, { lexicographic => 1 });
504 7         148 my @partitions;
505 7         15 while (my $p = $i->next) {
506 24         52 push @partitions, [ sort { $a <=> $b } @$p ]
507 22 100   40   288 if all { $_ =~ /^$re$/ } @$p;
  40         211  
508             }
509 7         85 return \@partitions;
510             }
511              
512              
513             sub partam {
514 7     7 1 3967 my ($self, $n, $m, @parts) = @_;
515 7         18 my $re = list2re @parts;
516 7         143 my $i = Integer::Partition->new($n);
517 7         131 my @partitions;
518 7         15 while (my $p = $i->next) {
519 13         36 push @partitions, [ sort { $a <=> $b } @$p ]
520 22 100 66 17   304 if @$p == $m && all { $_ =~ /^$re$/ } @$p;
  17         144  
521             }
522 7         82 return \@partitions;
523             }
524              
525              
526             sub partm {
527 7     7 1 4213 my ($self, $n, $m) = @_;
528 7         23 my $i = Integer::Partition->new($n);
529 7         132 my @partitions;
530 7         14 while (my $p = $i->next) {
531 30 100       346 push @partitions, [ sort { $a <=> $b } @$p ]
  9         25  
532             if @$p == $m;
533             }
534 7         72 return \@partitions;
535             }
536              
537              
538             sub permi {
539 1     1 1 367 my ($self, $parts) = @_;
540 1         7 my @permutations = permutations($parts);
541 1         141 return \@permutations;
542             }
543              
544              
545             sub pfold {
546 6     6 1 2963 my ($self, $n, $m, $f) = @_;
547 6         9 my @sequence;
548 6         8 my ($j, $k);
549 6         16 for (my $i = 1; $i <= $n; ++$i) {
550 40         63 _oddeven($i, \$k, \$j);
551 40         41 $k = $k % $m;
552 40 100       49 my $y = $f & (1 << $k) ? 1 : 0;
553 40 100       53 if ((2 * $j + 1) % 4 > 1) {
554 16         17 $y = 1 - $y;
555             }
556 40         56 push @sequence, $y;
557             }
558 6         14 return \@sequence;
559             }
560              
561             # find x and y such that n = 2^x * (2*y+1)
562             sub _oddeven {
563 40     40   92 my ($n, $x, $y) = @_;
564 40         36 my $k;
565             # two's complement of n = -n or ~n + 1
566 40         37 my $l = $n & -$n; # this is 2^a
567 40         51 $$y = ($n / $l - 1) / 2;
568 40         73 for ($k = 0; $l > 1; ++$k) {
569 27         33 $l >>= 1;
570             }
571 40         41 $$x = $k;
572              
573 40         44 return;
574             }
575              
576              
577             sub reverse_at {
578 5     5 1 4600 my ($self, $n, $parts) = @_;
579 5         22 my @head = @$parts[ 0 .. $n - 1 ];
580 5         19 my @tail = reverse @$parts[ $n .. $#$parts ];
581 5         17 my @data = (@head, @tail);
582 5         23 return \@data;
583             }
584              
585              
586             sub rotate_n {
587 6     6 1 5603 my ($self, $n, $parts) = @_;
588 6         40 my $atu = Music::AtonalUtil->new;
589 6         133 my $sequence = $atu->rotate($n, $parts);
590 6         238 return $sequence;
591             }
592              
593             sub _allowed { # is p one of the parts?
594 191     191   210 my ($p, $parts) = @_;
595 191     258   431 return any { $p == $_ } @$parts;
  258         508  
596             }
597              
598             1;
599              
600             __END__