Exemple #1
0
    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
Exemple #2
0
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
Exemple #3
0
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
Exemple #4
0
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
Exemple #5
0
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
Exemple #7
0
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
Exemple #8
0
    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
Exemple #11
0
    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
Exemple #12
0
    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