def __str__(self) -> str: """ Gets string representation of metronome mark. .. container:: example Integer-valued metronome mark: >>> mark = abjad.MetronomeMark((1, 4), 90) >>> str(mark) '4=90' .. container:: example Rational-valued metronome mark: >>> mark = abjad.MetronomeMark((1, 4), (272, 3)) >>> str(mark) '4=3-272' """ if self.textual_indication is not None: string = self.textual_indication elif isinstance(self.units_per_minute, (int, float)): string = f"{self._dotted}={self.units_per_minute}" elif isinstance( self.units_per_minute, Fraction) and not mathtools.is_integer_equivalent_number( self.units_per_minute): integer_part = int(float(self.units_per_minute)) remainder = self.units_per_minute - integer_part remainder = Fraction(remainder) string = f"{self._dotted}={integer_part}+{remainder}" elif isinstance(self.units_per_minute, Fraction) and mathtools.is_integer_equivalent_number( self.units_per_minute): string = "{}={}" integer = int(float(self.units_per_minute)) string = string.format(self._dotted, integer) elif isinstance(self.units_per_minute, tuple): string = "{}={}-{}" string = string.format( self._dotted, self.units_per_minute[0], self.units_per_minute[1], ) else: raise TypeError(f"unknown: {self.units_per_minute!r}.") return string
def is_integer_equivalent(argument): """ Is true when ``argument`` is an integer-equivalent number. .. container:: example >>> abjad.mathtools.is_integer_equivalent(12.0) True >>> abjad.mathtools.is_integer_equivalent('12') True >>> abjad.mathtools.is_integer_equivalent('foo') False Returns true or false. """ from abjad import mathtools if isinstance(argument, numbers.Number): return mathtools.is_integer_equivalent_number(argument) try: int(argument) return True except (TypeError, ValueError): return False
def integer_equivalent_number_to_integer(number): """ Changes integer-equivalent ``number`` to integer. .. container:: example Returns integer-equivalent number as integer: >>> abjad.mathtools.integer_equivalent_number_to_integer(17.0) 17 .. container:: example Returns noninteger-equivalent number unchanged: >>> abjad.mathtools.integer_equivalent_number_to_integer(17.5) 17.5 Returns number. """ from abjad import mathtools if not isinstance(number, numbers.Number): message = 'must be number: {!r}.' message = message.format(number) raise TypeError(message) if mathtools.is_integer_equivalent_number(number): return int(number) else: return number
def is_nonnegative_integer_equivalent_number(argument): """ Is true when ``argument`` is a nonnegative integer-equivalent number. .. container:: example >>> duration = abjad.Duration(4, 2) >>> abjad.mathtools.is_nonnegative_integer_equivalent_number(duration) True Returns true or false. """ from abjad import mathtools return mathtools.is_integer_equivalent_number(argument) and 0 <= argument
def is_positive_integer_equivalent_number(argument): """ Is true when ``argument`` is a positive integer-equivalent number. .. container:: example >>> abjad.mathtools.is_positive_integer_equivalent_number( ... abjad.Duration(4, 2) ... ) True Returns true or false. """ from abjad import mathtools try: return (0 < argument and mathtools.is_integer_equivalent_number(argument)) except TypeError: # Python 3 comparisons with non-numbers return False
def is_positive_integer_equivalent_number(argument): """ Is true when ``argument`` is a positive integer-equivalent number. .. container:: example >>> abjad.mathtools.is_positive_integer_equivalent_number( ... abjad.Duration(4, 2) ... ) True Returns true or false. """ from abjad import mathtools try: return 0 < argument and mathtools.is_integer_equivalent_number( argument ) except TypeError: # Python 3 comparisons with non-numbers return False
def all_are_integer_equivalent_numbers(argument): """ Is true when ``argument`` is an iterable collection with integer-equivalent items. .. container:: example >>> items = [1, 2, 3.0, abjad.Fraction(4, 1)] >>> abjad.mathtools.all_are_integer_equivalent_numbers(items) True >>> abjad.mathtools.all_are_integer_equivalent_numbers([1, 2, 3.5, 4]) False Returns true or false. """ from abjad import mathtools try: return all(mathtools.is_integer_equivalent_number(_) for _ in argument) except TypeError: return False
def __div__(self, argument) -> "MetronomeMark": """ Divides metronome mark by ``argument``. .. container:: example Divides metronome mark by number: >>> abjad.MetronomeMark((1, 4), 60) / 2 MetronomeMark(reference_duration=Duration(1, 4), units_per_minute=30) .. container:: example Divides metronome mark by other metronome mark: >>> abjad.MetronomeMark((1, 4), 60) / abjad.MetronomeMark((1, 4), 40) Multiplier(3, 2) """ if self.is_imprecise: raise exceptions.ImpreciseMetronomeMarkError if getattr(argument, "is_imprecise", False): raise exceptions.ImpreciseMetronomeMarkError assert isinstance(self.quarters_per_minute, Fraction) if isinstance(argument, type(self)): assert isinstance(argument.quarters_per_minute, Fraction) result = self.quarters_per_minute / argument.quarters_per_minute return Multiplier(result) elif isinstance(argument, (int, Fraction)): assert isinstance(self.units_per_minute, (int, Fraction)) units_per_minute = self.units_per_minute / argument if mathtools.is_integer_equivalent_number(units_per_minute): units_per_minute = int(units_per_minute) else: units_per_minute = Fraction(units_per_minute) result = new(self, units_per_minute=units_per_minute) return result else: raise TypeError(f"must be number or metronome mark: {argument!r}.")
def all_are_integer_equivalent_numbers(argument): """ Is true when ``argument`` is an iterable collection with integer-equivalent items. .. container:: example >>> items = [1, 2, 3.0, abjad.Fraction(4, 1)] >>> abjad.mathtools.all_are_integer_equivalent_numbers(items) True >>> abjad.mathtools.all_are_integer_equivalent_numbers([1, 2, 3.5, 4]) False Returns true or false. """ from abjad import mathtools try: return all(mathtools.is_integer_equivalent_number(_) for _ in argument) except TypeError: return False
def partition_integer_by_ratio(n, ratio): """ Partitions positive integer-equivalent ``n`` by ``ratio``. .. container:: example >>> abjad.mathtools.partition_integer_by_ratio(10, [1, 2]) [3, 7] .. container:: example Partitions positive integer-equivalent ``n`` by ``ratio`` with negative parts: >>> abjad.mathtools.partition_integer_by_ratio(10, [1, -2]) [3, -7] .. container:: example Partitions negative integer-equivalent ``n`` by ``ratio``: >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 2]) [-3, -7] .. container:: example Partitions negative integer-equivalent ``n`` by ``ratio`` with negative parts: >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, -2]) [-3, 7] .. container:: example More examples: >>> abjad.mathtools.partition_integer_by_ratio(10, [1]) [10] >>> abjad.mathtools.partition_integer_by_ratio(10, [1, 1]) [5, 5] >>> abjad.mathtools.partition_integer_by_ratio(10, [1, -1, -1]) [3, -4, -3] >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 1, 1, 1]) [-3, -2, -3, -2] >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 1, 1, 1, 1]) [-2, -2, -2, -2, -2] Returns result with weight equal to absolute value of ``n``. Returns list of integers. """ from abjad import mathtools if not mathtools.is_integer_equivalent_number(n): message = 'is not integer-equivalent number: {!r}.' message = message.format(n) raise TypeError(message) ratio = mathtools.Ratio(ratio).numbers if not all(mathtools.is_integer_equivalent_number(part) for part in ratio): message = 'some parts in {!r} not integer-equivalent numbers.' message = message.format(ratio) raise TypeError(message) result = [0] divisions = [ float(abs(n)) * abs(part) / mathtools.weight(ratio) for part in ratio ] cumulative_divisions = mathtools.cumulative_sums(divisions, start=None) for division in cumulative_divisions: rounded_division = int(round(division)) - sum(result) if division - round(division) == 0.5: rounded_division += 1 result.append(rounded_division) result = result[1:] if mathtools.sign(n) == -1: result = [-x for x in result] ratio_signs = [mathtools.sign(x) for x in ratio] result = [pair[0] * pair[1] for pair in zip(ratio_signs, result)] return result
def make_tempo_equation_markup( reference_duration, units_per_minute, *, decimal=None ) -> Markup: r""" Makes tempo equation markup. .. container:: example Integer-valued metronome mark: >>> markup = abjad.MetronomeMark.make_tempo_equation_markup( ... (1, 4), ... 90, ... ) >>> abjad.show(markup) # doctest: +SKIP .. docs:: >>> print(format(markup)) \markup \abjad-metronome-mark-markup #2 #0 #1 #"90" .. container:: example Float-valued metronome mark: >>> markup = abjad.MetronomeMark.make_tempo_equation_markup( ... (1, 4), ... 90.1, ... ) >>> abjad.show(markup) # doctest: +SKIP .. docs:: >>> print(format(markup)) \markup \abjad-metronome-mark-markup #2 #0 #1 #"90.1" .. container:: example Rational-valued metronome mark: >>> markup = abjad.MetronomeMark.make_tempo_equation_markup( ... abjad.Duration(1, 4), ... abjad.Fraction(272, 3), ... ) >>> abjad.show(markup) # doctest: +SKIP .. docs:: >>> print(format(markup)) \markup \abjad-metronome-mark-mixed-number-markup #2 #0 #1 #"90" #"2" #"3" """ reference_duration_ = Duration(reference_duration) log = reference_duration_.exponent dots = reference_duration_.dot_count stem = 1 if isinstance( units_per_minute, Fraction ) and not mathtools.is_integer_equivalent_number(units_per_minute): if decimal: decimal_: typing.Union[float, str] if decimal is True: decimal_ = float(units_per_minute) else: assert isinstance(decimal, str), repr(decimal) decimal_ = decimal markup = Markup( r"\markup \abjad-metronome-mark-markup" f' #{log} #{dots} #{stem} #"{decimal_}"', literal=True, ) else: nonreduced = NonreducedFraction(units_per_minute) base = int(nonreduced) remainder = nonreduced - base n, d = remainder.pair markup = Markup( r"\markup \abjad-metronome-mark-mixed-number-markup" f" #{log} #{dots} #{stem}" f' #"{base}" #"{n}" #"{d}"', literal=True, ) else: markup = Markup( r"\markup \abjad-metronome-mark-markup" f' #{log} #{dots} #{stem} #"{units_per_minute}"', literal=True, ) return markup
def list_related_tempos( self, maximum_numerator=None, maximum_denominator=None, integer_tempos_only=False, ) -> typing.List[typing.Tuple["MetronomeMark", "Ratio"]]: r""" Lists related tempos. .. container:: example Rewrites tempo ``4=58`` by ratios ``n:d`` such that ``1 <= n <= 8`` and ``1 <= d <= 8``. >>> mark = abjad.MetronomeMark((1, 4), 58) >>> pairs = mark.list_related_tempos( ... maximum_numerator=8, ... maximum_denominator=8, ... ) >>> for tempo, ratio in pairs: ... string = f'{tempo!s}\t{ratio!s}' ... print(string) 4=29 1:2 4=33+1/7 4:7 4=34+4/5 3:5 4=36+1/4 5:8 4=38+2/3 2:3 4=41+3/7 5:7 4=43+1/2 3:4 4=46+2/5 4:5 4=48+1/3 5:6 4=49+5/7 6:7 4=50+3/4 7:8 4=58 1:1 4=66+2/7 8:7 4=67+2/3 7:6 4=69+3/5 6:5 4=72+1/2 5:4 4=77+1/3 4:3 4=81+1/5 7:5 4=87 3:2 4=92+4/5 8:5 4=96+2/3 5:3 4=101+1/2 7:4 4=116 2:1 .. container:: example Integer-valued tempos only: >>> mark = abjad.MetronomeMark((1, 4), 58) >>> pairs = mark.list_related_tempos( ... maximum_numerator=16, ... maximum_denominator=16, ... integer_tempos_only=True, ... ) >>> for tempo, ratio in pairs: ... string = f'{tempo!s}\t{ratio!s}' ... print(string) 4=29 1:2 4=58 1:1 4=87 3:2 4=116 2:1 Constrains ratios such that ``1:2 <= n:d <= 2:1``. """ allowable_numerators = range(1, maximum_numerator + 1) allowable_denominators = range(1, maximum_denominator + 1) numbers = [allowable_numerators, allowable_denominators] enumerator = Enumerator(numbers) pairs = enumerator.yield_outer_product() multipliers = [Multiplier(_) for _ in pairs] multipliers = [_ for _ in multipliers if Fraction(1, 2) <= _ <= Fraction(2)] multipliers.sort() multipliers = sequence(multipliers).remove_repeats() pairs = [] for multiplier in multipliers: new_units_per_minute = multiplier * self.units_per_minute if integer_tempos_only and not mathtools.is_integer_equivalent_number( new_units_per_minute ): continue metronome_mark = type(self)( reference_duration=self.reference_duration, units_per_minute=new_units_per_minute, ) ratio = Ratio(multiplier.pair) pair = (metronome_mark, ratio) pairs.append(pair) return pairs
def partition_integer_by_ratio(n, ratio): """ Partitions positive integer-equivalent ``n`` by ``ratio``. .. container:: example >>> abjad.mathtools.partition_integer_by_ratio(10, [1, 2]) [3, 7] .. container:: example Partitions positive integer-equivalent ``n`` by ``ratio`` with negative parts: >>> abjad.mathtools.partition_integer_by_ratio(10, [1, -2]) [3, -7] .. container:: example Partitions negative integer-equivalent ``n`` by ``ratio``: >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 2]) [-3, -7] .. container:: example Partitions negative integer-equivalent ``n`` by ``ratio`` with negative parts: >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, -2]) [-3, 7] .. container:: example More examples: >>> abjad.mathtools.partition_integer_by_ratio(10, [1]) [10] >>> abjad.mathtools.partition_integer_by_ratio(10, [1, 1]) [5, 5] >>> abjad.mathtools.partition_integer_by_ratio(10, [1, -1, -1]) [3, -4, -3] >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 1, 1, 1]) [-3, -2, -3, -2] >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 1, 1, 1, 1]) [-2, -2, -2, -2, -2] Returns result with weight equal to absolute value of ``n``. Returns list of integers. """ from abjad import mathtools if not mathtools.is_integer_equivalent_number(n): message = "is not integer-equivalent number: {!r}." message = message.format(n) raise TypeError(message) ratio = mathtools.Ratio(ratio).numbers if not all(mathtools.is_integer_equivalent_number(part) for part in ratio): message = "some parts in {!r} not integer-equivalent numbers." message = message.format(ratio) raise TypeError(message) result = [0] divisions = [ float(abs(n)) * abs(part) / mathtools.weight(ratio) for part in ratio ] cumulative_divisions = mathtools.cumulative_sums(divisions, start=None) for division in cumulative_divisions: rounded_division = int(round(division)) - sum(result) if division - round(division) == 0.5: rounded_division += 1 result.append(rounded_division) result = result[1:] if mathtools.sign(n) == -1: result = [-x for x in result] ratio_signs = [mathtools.sign(x) for x in ratio] result = [pair[0] * pair[1] for pair in zip(ratio_signs, result)] return result