
    rhOS                    ~   S SK Jr  S SKrS SKJr  S SKrS SKrS SKrS SK	J
r
  S SKJr  S SK	Jr  S SKJrJrJr  \R$                  " S5      r " S	 S
\R(                  5      r\\\4   r\\S4   r\\\S4   S4   rSr\" \5      r\" S5      SS j5       r\" S5      SS j5       r \" S5      SS j5       r!\" S5      S S j5       r"\" S5      S!S j5       r#\" S5      S"S j5       r$S#S jr%S#S jr&S$S jr'S$S jr(S%S jr)S$S jr*\" S5      S&S j5       r+\" S5      S&S j5       r,\-S:X  a  S SK	r	\	R\                  " 5         gg)'    )annotationsN)	lru_cache)common)MeterDivision)environment)MeterExceptionMusic21ExceptionTimeSignatureExceptionzmeter.toolsc                  4    \ rS rSr% S\S'   S\S'   S\S'   Srg)	MeterTerminalTuple   int	numeratordenominatorr   division N)__name__
__module____qualname____firstlineno____annotations____static_attributes__r       M/home/james-whalen/.local/lib/python3.13/site-packages/music21/meter/tools.pyr   r      s    Nr   r   .)                   @      i   c                $   [         R                  " U SS9u  pUR                  5       nU(       d  [        R                  nOjUR                  5       nSUR                  5       ;   a  [        R                  nO5SUR                  5       ;   a  [        R                  nO[        R                  n[        R                  " SU5      nUb@  [        UR                  S5      5      n[        UR                  S5      5      n[        XVU5      $ [        SU  35      e)	a  
Returns a three-element MeterTerminalTuple of numerator, denominator, and optional
division of the meter.

>>> meter.tools.slashToTuple('3/8')
MeterTerminalTuple(numerator=3, denominator=8, division=<MeterDivision.NONE>)
>>> meter.tools.slashToTuple('7/32')
MeterTerminalTuple(numerator=7, denominator=32, division=<MeterDivision.NONE>)
>>> meter.tools.slashToTuple('slow 6/8')
MeterTerminalTuple(numerator=6, denominator=8, division=<MeterDivision.SLOW>)
z0123456789/.)numbersslowfastz(\d+)/(\d+)r   r   z1slashToTuple() cannot find two part fraction for )r   getNumFromStrstripr   NONElowerSLOWFASTrematchr   groupr   r   )valuevalueNumbers
valueCharsr   matchesnds          r   slashToTupler6   )   s      &33E<J LL%%'L %%%%'
Z%%''$))Hz''))$))H$))Hhh~|4Ga !a !!!11
LUGT
UUr   c                    / nU R                  5       n U R                  S5      nU H6  n [        U5      nUR                  UR                  UR
                  45        M8     [        U5      $ ! [         a     MR  f = f)aP  
Change a compount meter into a list of simple numberator, demoninator values

>>> meter.tools.slashCompoundToFraction('3/8+2/8')
((3, 8), (2, 8))
>>> meter.tools.slashCompoundToFraction('5/8')
((5, 8),)
>>> meter.tools.slashCompoundToFraction('5/8+2/4+6/8')
((5, 8), (2, 4), (6, 8))

* Changed in v7: new location and returns a tuple.
+)r(   splitr6   appendr   r   r   tuple)r0   post	valueListpartms        r   slashCompoundToFractionr@   N   st     DKKMEC I	T"AKKamm45  ;  		s   2A++
A98A9c                R   / nSnU R                  5       R                  S5      nU H]  nSU;   a5   [        U5      nUR                  UR                  UR                  45        M>   UR                  [        U5      S45        SnM_     / n[        U5       HB  u  nu  pU
c%  XS-   S  H  u  pUc  M
  Un
  O   [        SU  35      eUR                  X45        MD     [        U5      U4$ ! [         a  n[	        SU  S35      UeSnAff = f! [         a    [        SS	U  S
3-   5      ef = f)a  
Given a mixture if possible meter fraction representations, return a tuple
of two elements: The first element is a tuple of pairs of numerator, denominators
that are implied by the time signature.

The second element is False if the value was a simple time signature (like 4/4)
or a composite meter where all numerators had their own denominators.

>>> meter.tools.slashMixedToFraction('4/4')
(((4, 4),), False)

>>> meter.tools.slashMixedToFraction('3/8+2/8')
(((3, 8), (2, 8)), False)

>>> meter.tools.slashMixedToFraction('3+2/8')
(((3, 8), (2, 8)), True)

>>> meter.tools.slashMixedToFraction('3+2+5/8')
(((3, 8), (2, 8), (5, 8)), True)

>>> meter.tools.slashMixedToFraction('3+2+5/8+3/4')
(((3, 8), (2, 8), (5, 8), (3, 4)), True)

>>> meter.tools.slashMixedToFraction('3+2+5/8+3/4+2+1+4/16')
(((3, 8), (2, 8), (5, 8), (3, 4), (2, 16), (1, 16), (4, 16)), True)

>>> meter.tools.slashMixedToFraction('3+2+5/8+3/4+2+1+4')
Traceback (most recent call last):
music21.exceptions21.MeterException: cannot match denominator to numerator in: 3+2+5/8+3/4+2+1+4

>>> meter.tools.slashMixedToFraction('3.0/4.0')
Traceback (most recent call last):
music21.exceptions21.TimeSignatureException: Cannot create time signature from "3.0/4.0"

* Changed in v7: new location and returns a tuple as first value.
Fr8   /z#Cannot create time signature from ""NTzCannot parse this file -- this error often comes up if the musicxml pickled file is out of date after a change in musicxml/__init__.py . Clear your temp directory of .p and .p.gz files and try again. zTime Signature:  r   z*cannot match denominator to numerator in: )r(   r9   r6   r   r
   r:   r   r   r   
ValueErrorr	   	enumerater;   )valueSrcpresummedNumeratorr0   r>   tupmer<   iintNumintDenom_	nextDenoms                r   slashMixedToFractionrQ   h   sg   L ,.CONN""3'E$;O"4( JJs78	6

CIt,-"& ( D "+3F"%!ef+((H #.
 %'QRZQ[%\]]V&' "0 ;''= " O,9(1EGLNOO  6&X )
!4	56 66s#   C'%D	'
D1DD	D&c                ^   / nU  HE  u  p#U(       a%  US   S   U:X  a  US   S   R                  U5        M1  UR                  U/U45        MG     / nU HI  nUS    Vs/ s H  n[        U5      PM     nnSR                  U5      nUS   n	UR                  X45        MK     [        U5      $ s  snf )aF  
Given a tuple of fraction values, compact numerators by sum if denominators
are the same

>>> from music21.meter.tools import fractionToSlashMixed
>>> fractionToSlashMixed(((3, 8), (2, 8), (5, 8), (3, 4), (2, 16), (1, 16), (4, 16)))
(('3+2+5', 8), ('3', 4), ('2+1+4', 16))

* Changed in v7: new location and returns a tuple.
r   r   r8   )r:   strjoinr;   )
fListrH   r4   r5   r<   r>   xnStrListnStrdInts
             r   fractionToSlashMixedr[      s     (*C3r71:?GAJa  JJQx   #%D$(G,GqCFG,xx!AwTL!	  ; -s   B*c                H   / n/ n[        5       nU  H8  u  pEUR                  U5        UR                  U5        UR                  U5        M:     [        U5      S:X  a  [	        U5      nUS   nXE4$ [
        R                  " U6 nSn[        X5       H  u  pXxXi-  -  -  nM     Xv4$ )af  
Given a tuple of tuples of numerator and denominator,
find the sum; does NOT reduce to its lowest terms.

>>> from music21.meter.tools import fractionSum
>>> fractionSum(((3, 8), (5, 8), (1, 8)))
(9, 8)
>>> fractionSum(((1, 6), (2, 3)))
(5, 6)
>>> fractionSum(((3, 4), (1, 2)))
(5, 4)
>>> fractionSum(((1, 13), (2, 17)))
(43, 221)
>>> fractionSum(())
(0, 1)

This method might seem like an easy place to optimize and simplify
by just doing a fractions.Fraction() sum (I tried!), but not reducing to
its lowest terms is a feature of this method. 3/8 + 3/8 = 6/8, not 3/4:

>>> fractionSum(((3, 8), (3, 8)))
(6, 8)
r   r   )setr:   addlensummathlcmzip)
numDenomTuplenListdListdListUniquer4   r5   dRednRednSrcdSrcs
             r   fractionSumrl      s    2 EE%KQQ 
 ;1J!Hvxx%e+JDDL))D ,|r   c                |    [         R                  " U 5      R                  S5      nUR                  UR                  4$ )a  
Given a floating point proportional value between 0 and 1, return the
best-fit slash-base fraction up to 16.

>>> from music21.meter.tools import proportionToFraction
>>> proportionToFraction(0.5)
(1, 2)
>>> proportionToFraction(0.25)
(1, 4)
>>> proportionToFraction(0.75)
(3, 4)
>>> proportionToFraction(0.125)
(1, 8)
>>> proportionToFraction(0.375)
(3, 8)
>>> proportionToFraction(0.625)
(5, 8)
>>> proportionToFraction(0.333)
(1, 3)
>>> proportionToFraction(0.83333)
(5, 6)
r   )	fractionsFractionlimit_denominatorr   r   )r0   fs     r   proportionToFractionrr     s2    0 	5!33B7AKK''r   c                    / nU[         S   :  aF  U S-  nUS-  nU[         S   ::  a/  UR                  U SU 35        US-  nUS-  nU[         S   ::  a  M/  [        U5      $ )ak  
This simply gets restatements of the same fraction in smaller units,
up to the largest valid denominator.

>>> meter.tools.divisionOptionsFractionsUpward(2, 4)
('4/8', '8/16', '16/32', '32/64', '64/128')
>>> meter.tools.divisionOptionsFractionsUpward(3, 4)
('6/8', '12/16', '24/32', '48/64', '96/128')

Note that this returns a tuple of strings not MeterOptions
rS   r   rB   validDenominatorsr:   r;   r4   r5   optsnModdMods        r   divisionOptionsFractionsUpwardrz   '  s|     DR  1u1u'++KK4&$()AIDAID '++ ;r   c                    / nU[         S   :  aY  U S-  S:X  aP  U S-  nUS-  nU[         S   :  a9  UR                  U SU 35        US-  S:w  a  OUS-  nUS-  nU[         S   :  a  M9  [        U5      $ )z
Get restatements of the same fraction in larger units

>>> meter.tools.divisionOptionsFractionsDownward(2, 4)
('1/2',)
>>> meter.tools.divisionOptionsFractionsDownward(12, 16)
('6/8', '3/4')

Note that this returns a tuple of strings not MeterOptions
r   r   rB   rt   rv   s        r    divisionOptionsFractionsDownwardr|   ?  s     DQAEQJAvAv'**KK4&$()ax1}19D19D '** ;r   c                    / nU[         S   :  aV  U S:X  aP  SnUS-  nU[         S   ::  a<  UR                  [        U  SU 3/U-  5      5        US-  nUS-  nU[         S   ::  a  M<  [        U5      $ )z
>>> meter.tools.divisionOptionsAdditiveMultiplesDownward(1, 16)
(('1/32', '1/32'), ('1/64', '1/64', '1/64', '1/64'),
 ('1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128'))
rS   r   r   rB   rt   )r4   r5   rw   rL   ry   s        r   (divisionOptionsAdditiveMultiplesDownwardr~   W  s     DR  Q!V1u'++KK!AdV}o123!8DFA '++ ;r   c                    / nU S:  aQ  U S-  S:X  aH  SnUnX-  nUS:  a:  [        U SU 3/U-  5      nXb;  a  UR                  U5        XS-  nXC-  nUS:  a  M:  [        U5      $ )z
Additive multiples with the same denominators.

>>> meter.tools.divisionOptionsAdditiveMultiples(4, 16)
(('2/16', '2/16'),)
>>> meter.tools.divisionOptionsAdditiveMultiples(6, 4)
(('3/4', '3/4'),)
   r   r   r   rB   )r;   r:   )r4   r5   rw   divrL   rx   seqs          r    divisionOptionsAdditiveMultiplesr   i  s     D1uQ!xQhD61#!+,CC ;DHA Qh ;r   c                   / nU S-  S:X  am  US-  S:  ad  U S-  nUS-  nUS:  aT  US:  aN  UR                  [        S SU 3/[        U5      -  5      5        US-  S:w  a  OUS-  nUS-  nUS:  a  US:  a  MN  [        U5      $ )z
>>> meter.tools.divisionOptionsAdditiveMultiplesEvenDivision(4, 16)
(('1/8', '1/8'),)
>>> meter.tools.divisionOptionsAdditiveMultiplesEvenDivision(4, 4)
(('1/2', '1/2'),)
>>> meter.tools.divisionOptionsAdditiveMultiplesEvenDivision(3, 4)
()
r   r   r   rB   )r:   r;   r   rv   s        r   ,divisionOptionsAdditiveMultiplesEvenDivisionr     s     D 	1uza1fkAvAvaiD1HKK!AdV}oD	9:;ax1}19D19D aiD1H ;r   c                    / nU S:  ah  US:  ab  UnU nU S:  a  U nOSnU[         S   ::  aF  XE::  aA  SU 3/U-  nUR                  [        U5      5        US-  nUS-  nU[         S   ::  a  XE::  a  MA  [        U5      $ )a  
>>> meter.tools.divisionOptionsAdditiveMultiplesUpward(4, 16)
(('1/16', '1/16', '1/16', '1/16'),
 ('1/32', '1/32', '1/32', '1/32', '1/32', '1/32', '1/32', '1/32'),
 ('1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64',
  '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64'))
>>> meter.tools.divisionOptionsAdditiveMultiplesUpward(3, 4)
(('1/4', '1/4', '1/4'),
 ('1/8', '1/8', '1/8', '1/8', '1/8', '1/8'),
 ('1/16', '1/16', '1/16', '1/16', '1/16', '1/16',
  '1/16', '1/16', '1/16', '1/16', '1/16', '1/16'))
r   r   rS   1/r   rt   )r4   r5   rw   dCurrentnCountnCountLimitr   s          r   &divisionOptionsAdditiveMultiplesUpwardr     s     D1uar6KK +B//F4Iz?#f,CKKc
#MHaKF +B//F4I ;r   c                   / nU S-  S:X  aT  U S:  aN  / n[        [        U S-  5      5       H  nUR                  SU 35        M     UR                  [        U5      5        U S:X  aD  S H>  n/ nU H  nUR                  U SU 35        M     UR                  [        U5      5        M@     U S:X  aD  S H>  n/ nU H  nUR                  U SU 35        M     UR                  [        U5      5        M@     U S	:X  aD  S
 H>  n/ nU H  nUR                  U SU 35        M     UR                  [        U5      5        M@     UR	                  [        X5      5        UR	                  [        X5      5        UR                  U  SU 345        UR	                  [        X5      5        UR	                  [        X5      5        UR	                  [        X5       Vs/ s H  ow4PM     sn5        UR	                  [        X5       Vs/ s H  ow4PM     sn5        [        [        R                  R                  S U 5       5      5      $ s  snf s  snf )a  
This is a primitive approach to algorithmic division production.
This can be extended.

It is assumed that these values are provided in order of priority

>>> meter.tools.divisionOptionsAlgo(4, 4)
(('1/4', '1/4', '1/4', '1/4'),
 ('1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8'),
 ('1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16',
  '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16'),
 ('1/2', '1/2'),
 ('4/4',),
 ('2/4', '2/4'),
 ('2/2',),
 ('1/1',),
 ('8/8',),
 ('16/16',),
 ('32/32',),
 ('64/64',),
 ('128/128',))

>>> meter.tools.divisionOptionsAlgo(1, 4)
(('1/4',),
 ('1/8', '1/8'),
 ('1/16', '1/16', '1/16', '1/16'),
 ('1/32', '1/32', '1/32', '1/32', '1/32', '1/32', '1/32', '1/32'),
 ('1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64',
  '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64'),
 ('1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128',
  '1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128',
  '1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128',
  '1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128', '1/128'),
 ('2/8',), ('4/16',), ('8/32',), ('16/64',), ('32/128',))

>>> meter.tools.divisionOptionsAlgo(2, 2)
(('1/2', '1/2'),
 ('1/4', '1/4', '1/4', '1/4'),
 ('1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8'),
 ('1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16',
  '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16'),
 ('2/2',),
 ('1/1',),
 ('4/4',), ('8/8',), ('16/16',), ('32/32',), ('64/64',), ('128/128',))

>>> meter.tools.divisionOptionsAlgo(3, 8)
(('1/8', '1/8', '1/8'), ('1/16', '1/16', '1/16', '1/16', '1/16', '1/16'),
 ('1/32', '1/32', '1/32', '1/32', '1/32', '1/32',
  '1/32', '1/32', '1/32', '1/32', '1/32', '1/32'),
 ('3/8',), ('6/16',), ('12/32',), ('24/64',), ('48/128',))

>>> meter.tools.divisionOptionsAlgo(6, 8)
(('3/8', '3/8'),
 ('1/8', '1/8', '1/8', '1/8', '1/8', '1/8'),
 ('1/16', '1/16', '1/16', '1/16', '1/16', '1/16',
  '1/16', '1/16', '1/16', '1/16', '1/16', '1/16'),
 ('1/4', '1/4', '1/4'),
 ('6/8',),
 ('3/4',),
 ('12/16',), ('24/32',), ('48/64',), ('96/128',))

>>> meter.tools.divisionOptionsAlgo(12, 8)
(('3/8', '3/8', '3/8', '3/8'),
 ('1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8', '1/8'),
 ('1/4', '1/4', '1/4', '1/4', '1/4', '1/4'),
 ('1/2', '1/2', '1/2'),
 ('12/8',),
 ('6/8', '6/8'),
 ('6/4',),
 ('3/2',),
 ('24/16',), ('48/32',), ('96/64',), ('192/128',))

>>> meter.tools.divisionOptionsAlgo(5, 8)
(('2/8', '3/8'),
 ('3/8', '2/8'),
 ('1/8', '1/8', '1/8', '1/8', '1/8'),
 ('1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16', '1/16'),
 ('5/8',),
 ('10/16',), ('20/32',), ('40/64',), ('80/128',))

>>> meter.tools.divisionOptionsAlgo(18, 4)
(('3/4', '3/4', '3/4', '3/4', '3/4', '3/4'),
 ('1/4', '1/4', '1/4', '1/4', '1/4', '1/4', '1/4', '1/4', '1/4', '1/4',
  '1/4', '1/4', '1/4', '1/4', '1/4', '1/4', '1/4', '1/4'),
 ('1/2', '1/2', '1/2', '1/2', '1/2', '1/2', '1/2', '1/2', '1/2'),
 ('18/4',),
 ('9/4', '9/4'),
 ('4/4', '4/4', '4/4', '4/4'),
 ('2/4', '2/4', '2/4', '2/4', '2/4', '2/4', '2/4', '2/4'),
 ('9/2',),
 ('36/8',),
 ('72/16',),
 ('144/32',),
 ('288/64',),
 ('576/128',))

>>> meter.tools.divisionOptionsAlgo(3, 128)
(('1/128', '1/128', '1/128'), ('3/128',))
r   r   z3/   ))r   r   )r   r   rB      ))r   r   r   )r   r   r   )r   r   r   
   ))r   r   r   r   c              3  4   #    U  H  oS :w  d  M
  Uv   M     g7f)r   Nr   ).0os     r   	<genexpr>&divisionOptionsAlgo.<locals>.<genexpr>Q  s     #?t!BwAAts   		)ranger   r:   r;   extendr   r   r   r~   r|   rz   r   miscunique)r4   r5   rw   r   jr/   rx   r   s           r   divisionOptionsAlgor     s   J D 	1uza!es1q5z"AJJA3x  #E#JAv%EC

dV1QC=) KKc
#	 &
 	Av6EC

dV1QC=) KKc
#	 7 	Bw$EC

dV1QC=) KKc
#	 % 	KK6q<= 	KK<QBCKKA3as 	KK067 	KK8>?KK?EFE!EFGKK=aCDC!CDE###?t#??@@ GDs   I Ic                    / nU S:X  a:  UR                  SU 3SU 3SU 345        UR                  SU 3SU 3SU 345        [        U5      $ )a  
Provide fixed set of meter divisions that will not be easily
obtained algorithmically.

Currently, does nothing except to allow partitioning 5/8 as 2/8, 2/8, 1/8 as a possibility
(sim for 5/16, etc.)

>>> meter.tools.divisionOptionsPreset(5, 8)
(('2/8', '2/8', '1/8'), ('2/8', '1/8', '2/8'))
>>> meter.tools.divisionOptionsPreset(3, 4)
()

>>> ms2 = meter.MeterSequence('5/32')
>>> ms2.getPartitionOptions()
(('2/32', '3/32'), ('3/32', '2/32'), ('1/32', '1/32', '1/32', '1/32', '1/32'),
 ('1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64', '1/64'),
 ('5/32',), ('10/64',), ('20/128',), ('2/32', '2/32', '1/32'), ('2/32', '1/32', '2/32'))
r   z2/r   )r:   r;   )r4   r5   rw   s      r   divisionOptionsPresetr   T  sd    ( DAvr!XA3x2aS23r!XA3x2aS23;r   __main__)r0   rT   returnr   )r0   rT   r   NumDenomTuple)rG   rT   r   ztuple[NumDenomTuple, bool])rV   r   r   ztuple[tuple[str, int], ...])rd   r   r   NumDenom)r0   floatr   r   )r4   r   r5   r   r   ztuple[str, ...])r4   r   r5   r   r   MeterOptions)r4   r   r5   r   r   ztuple[tuple[str, ...], ...])r   r   )/
__future__r   rn   	functoolsr   ra   r-   typingtmusic21r   music21.common.enumsr   r   music21.exceptions21r   r	   r
   EnvironmentenvironLocal
NamedTupler   r;   r   r   r   rT   r   ru   	frozensetvalidDenominatorsSetr6   r@   rQ   r[   rl   rr   rz   r|   r~   r   r   r   r   r   r   mainTestr   r   r   <module>r      s   #    	   .  Y Y&&}5  c?hm$U38_c)*1  !23  3!V !VH 3 2 3J( J(Z 3 @ 3, ,^ 3( (>00$.0@ 3XA XAv 3 6 z r   