def __init__(self, token):
     from abjad.tools import pitchtools
     if isinstance(token, numbers.Number):
         sign = mathtools.sign(token)
         abs_token = abs(token)
         if abs_token % 12 == 0 and 12 <= abs_token:
             number = 12
         else:
             number = abs_token % 12
         number *= sign
     elif isinstance(token, pitchtools.Interval):
         number = token.semitones
         sign = mathtools.sign(number)
         abs_number = abs(number)
         if abs_number % 12 == 0 and 12 <= abs_number:
             number = 12
         else:
             number = abs_number % 12
         number *= sign
     elif isinstance(token, pitchtools.IntervalClass):
         number = token.number
         sign = mathtools.sign(number)
         abs_number = abs(number)
         if abs_number % 12 == 0 and 12 <= abs_number:
             number = 12
         else:
             number = abs_number % 12
         number *= sign
     else:
         raise ValueError('must be number, interval or interval-class.')
     self._number = number
Beispiel #2
0
 def _is_neighbor_note(note):
     from abjad.tools import scoretools
     from abjad.tools import tonalanalysistools
     if not isinstance(note, scoretools.Note):
         message = 'must be note: {!r}.'
         message = message.format(note)
         raise TypeError(message)
     previous_note = note._get_in_my_logical_voice(
         -1, prototype=scoretools.Note)
     next_note = note._get_in_my_logical_voice(
         1, prototype=scoretools.Note)
     if previous_note is None:
         return False
     if next_note is None:
         return False
     notes = [previous_note, note, next_note]
     selection = tonalanalysistools.select(notes)
     preceding_interval = note.written_pitch - previous_note.written_pitch
     preceding_interval_direction = \
         mathtools.sign(preceding_interval.direction_number)
     following_interval = next_note.written_pitch - note.written_pitch
     following_interval_direction = \
         mathtools.sign(following_interval.direction_number)
     if selection.are_stepwise_notes():
         if preceding_interval_direction != following_interval_direction:
             return True
     return False
Beispiel #3
0
 def __init__(self, token=None):
     from abjad.tools import pitchtools
     if isinstance(token, numbers.Number):
         sign = mathtools.sign(token)
         abs_token = abs(token)
         if abs_token % 12 == 0 and 12 <= abs_token:
             number = 12
         else:
             number = abs_token % 12
         number *= sign
     elif isinstance(token, pitchtools.Interval):
         number = token.semitones
         sign = mathtools.sign(number)
         abs_number = abs(number)
         if abs_number % 12 == 0 and 12 <= abs_number:
             number = 12
         else:
             number = abs_number % 12
         number *= sign
     elif isinstance(token, pitchtools.IntervalClass):
         number = token.number
         sign = mathtools.sign(number)
         abs_number = abs(number)
         if abs_number % 12 == 0 and 12 <= abs_number:
             number = 12
         else:
             number = abs_number % 12
         number *= sign
     elif token is None:
         number = 0
     else:
         message = 'can not initialize {}: {!r}.'
         message = message.format(type(self).__name__, token)
         raise ValueError(message)
     self._number = number
def test_mathtools_sign_02():
    r'''Test rational sign.
    '''

    assert mathtools.sign(Duration(-3, 2)) == -1
    assert mathtools.sign(Duration(0)) == 0
    assert mathtools.sign(Duration(3, 2)) == 1
def test_mathtools_sign_01():
    r'''Test integer sign.
    '''

    assert mathtools.sign(-2) == -1
    assert mathtools.sign(-1.5) == -1
    assert mathtools.sign(0) == 0
    assert mathtools.sign(1.5) == 1
    assert mathtools.sign(2) == 1
def partition_integer_into_units(n):
    r'''Partitions positive integer into units:

    ::

        >>> mathtools.partition_integer_into_units(6)
        [1, 1, 1, 1, 1, 1]

    Partitions negative integer into units:

    ::

        >>> mathtools.partition_integer_into_units(-5)
        [-1, -1, -1, -1, -1]

    Partitions ``0`` into units:

    ::

        >>> mathtools.partition_integer_into_units(0)
        []

    Returns list of zero or more parts with absolute value equal to ``1``.
    '''
    from abjad.tools import mathtools

    result = abs(n) * [mathtools.sign(n) * 1]

    return result
def map_sequence_elements_to_numbered_sublists(sequence):
    """Map `sequence` elements to numbered sublists:

    ::

        >>> sequencetools.map_sequence_elements_to_numbered_sublists([1, 2, -3, -4, 5])
        [[1], [2, 3], [-4, -5, -6], [-7, -8, -9, -10], [11, 12, 13, 14, 15]]

    ::

        >>> sequencetools.map_sequence_elements_to_numbered_sublists([1, 0, -3, -4, 5])
        [[1], [], [-2, -3, -4], [-5, -6, -7, -8], [9, 10, 11, 12, 13]]

    Note that numbering starts at ``1``.

    Returns newly constructed list of lists.
    """

    if not isinstance(sequence, list):
        raise TypeError

    if not all(isinstance(x, (int, long)) for x in sequence):
        raise ValueError

    result = []
    cur = 1

    for length in sequence:
        abs_length = abs(length)
        part = range(cur, cur + abs_length)
        part = [mathtools.sign(length) * x for x in part]
        result.append(part)
        cur += abs_length

    return result
Beispiel #8
0
 def __new__(cls, *args):
     from abjad.tools import mathtools
     from abjad.tools import sequencetools
     if len(args) == 1 and hasattr(args[0], 'numerator') and \
         hasattr(args[0], 'denominator'):
         numerator = args[0].numerator
         denominator = args[0].denominator
     elif len(args) == 1 and isinstance(args[0], int):
         numerator = args[0]
         denominator = 1
     elif len(args) == 1 and mathtools.is_integer_singleton(args[0]):
         numerator = args[0][0]
         denominator = 1
     elif len(args) == 1 and mathtools.is_integer_pair(args[0]):
         numerator, denominator = args[0]
     elif len(args) == 1 and isinstance(args[0], str):
         numerator, denominator = cls._parse_input_string(args[0])
     elif mathtools.is_integer_pair(args):
         numerator = args[0]
         denominator = args[1]
     elif len(args) == 0:
         numerator = 0
         denominator = 1
     else:
         message = 'can not initialize {}: {!r}.'
         message = message.format(cls.__name__, args)
         raise ValueError(message)
     numerator *= mathtools.sign(denominator)
     denominator = abs(denominator)
     self = fractions.Fraction.__new__(cls, numerator, denominator)
     self._numerator = numerator
     self._denominator = denominator
     return self
 def __init__(self, item=None):
     from abjad.tools import pitchtools
     if isinstance(item, numbers.Number):
         sign = mathtools.sign(item)
         abs_token = abs(item)
         if abs_token % 12 == 0 and 12 <= abs_token:
             number = 12
         else:
             number = abs_token % 12
         number *= sign
     elif isinstance(item, pitchtools.Interval):
         number = item.semitones
         sign = mathtools.sign(number)
         abs_number = abs(number)
         if abs_number % 12 == 0 and 12 <= abs_number:
             number = 12
         else:
             number = abs_number % 12
         number *= sign
     elif isinstance(item, pitchtools.IntervalClass):
         number = item.number
         sign = mathtools.sign(number)
         abs_number = abs(number)
         if abs_number % 12 == 0 and 12 <= abs_number:
             number = 12
         else:
             number = abs_number % 12
         number *= sign
     elif item is None:
         number = 0
     elif isinstance(item, str):
         number = float(item)
         if mathtools.is_integer_equivalent_expr(number):
             number = int(number)
         sign = mathtools.sign(number)
         abs_token = abs(number)
         if abs_token % 12 == 0 and 12 <= abs_token:
             number = 12
         else:
             number = abs_token % 12
         number *= sign
     else:
         message = 'can not initialize {}: {!r}.'
         message = message.format(type(self).__name__, item)
         raise ValueError(message)
     self._number = number
Beispiel #10
0
 def __init__(self, *args):
     from abjad.tools import pitchtools
     from abjad.tools import sequencetools
     if len(args) == 1 and \
         isinstance(args[0], (pitchtools.NamedInterval,
             pitchtools.NamedIntervalClass)):
         quality_string = args[0]._quality_string
         number = args[0].number
     elif len(args) == 1 and isinstance(args[0], str):
         match = \
             pitchtools.Interval._interval_name_abbreviation_regex.match(
             args[0])
         if match is None:
             message = '{!r} does not have the form of an abbreviation.'
             message = message.format(args[0])
             raise ValueError(message)
         direction_string, quality_abbreviation, number_string = \
             match.groups()
         quality_string = \
             NamedIntervalClass._quality_abbreviation_to_quality_string[
                 quality_abbreviation]
         number = int(direction_string + number_string)
     elif len(args) == 1 and sequencetools.is_pair(args[0]):
         quality_string, number = args[0]
     elif len(args) == 2:
         quality_string, number = args
     elif len(args) == 0:
         quality_string = 'perfect'
         number = 1
     else:
         message = 'bad input: {!r}.'
         message = message.format(args)
         raise TypeError(message)
     if quality_string not in \
         NamedIntervalClass._acceptable_quality_strings:
         message = 'not acceptable quality string: {!r}.'
         message = message.format(quality_string)
         raise ValueError(message)
     if not isinstance(number, int):
         message = 'must be integer: {!r}.'
         message = message.format(number)
         raise TypeError(message)
     if number == 0:
         message = 'must be nonzero: {!r}.'
         message = message.format(number)
         raise ValueError(number)
     sign = mathtools.sign(number)
     abs_number = abs(number)
     if abs_number % 7 == 1 and 8 <= abs_number:
         number = 8
     else:
         number = abs_number % 7
         if number == 0:
             number = 7
     if not number == 1:
         number *= sign
     self._number = number
     self._quality_string = quality_string
def truncate_sequence_to_weight(sequence, weight):
    '''Truncate `sequence` to `weight`:

    ::

        >>> l = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
        >>> for x in range(10):
        ...     print x, sequencetools.truncate_sequence_to_weight(l, x)
        ...
        0 []
        1 [-1]
        2 [-1, 1]
        3 [-1, 2]
        4 [-1, 2, -1]
        5 [-1, 2, -2]
        6 [-1, 2, -3]
        7 [-1, 2, -3, 1]
        8 [-1, 2, -3, 2]
        9 [-1, 2, -3, 3]

    Returns empty list when `weight` is ``0``:

    ::

        >>> sequencetools.truncate_sequence_to_weight([1, 2, 3, 4, 5], 0)
        []

    Raise type error when `sequence` is not a list.

    Raise value error on negative `weight`.

    Returns new list.
    '''

    if not isinstance(sequence, list):
        raise TypeError

    if weight < 0:
        raise ValueError

    result = []

    if weight == 0:
        return result

    accumulation = 0
    for x in sequence:
        accumulation += abs(x)
        if accumulation < weight:
            result.append(x)
        else:
            sign = mathtools.sign(x)
            trimmed_part = weight - mathtools.weight(result)
            trimmed_part *= sign
            result.append(trimmed_part)
            break

    return result
Beispiel #12
0
    def direction_number(self):
        r'''Direction sign of numbered interval.

        ::

            >>> pitchtools.NumberedInterval(-14).direction_number
            -1

        Returns integer.
        '''
        return mathtools.sign(self.number)
Beispiel #13
0
 def __init__(self, *args):
     from abjad.tools import pitchtools
     if len(args) == 1:
         if isinstance(args[0], type(self)):
             quality_string = args[0].quality_string
             number = args[0].number
         elif isinstance(args[0], str):
             match = \
                 pitchtools.Interval._interval_name_abbreviation_regex.match(
                     args[0])
             if match is None:
                 message = '{!r} does not have the form of a mdi abbreviation.'
                 message = message.format(args[0])
                 raise ValueError(message)
             direction_string, quality_abbreviation, number_string = \
                 match.groups()
             quality_string = self._quality_abbreviation_to_quality_string[
                 quality_abbreviation]
             number = int(direction_string + number_string)
         elif isinstance(args[0], pitchtools.NamedIntervalClass):
             quality_string = args[0].quality_string
             number = args[0].number
         elif isinstance(args[0], (
             int,
             float,
             int,
             pitchtools.NumberedInterval,
             pitchtools.NumberedIntervalClass,
             )):
             number = int(args[0])
             sign = mathtools.sign(number)
             octaves, semitones = divmod(abs(number), 12)
             quality_string, number = \
                 self._semitones_to_quality_string_and_number[semitones]
             number += abs(octaves) * 7
             if sign == -1:
                 number *= -1
         else:
             message = 'can not initialize {}: {!r}'
             message = message.format(type(self).__init__, args)
             raise ValueError(message)
     elif len(args) == 2:
         quality_string, number = args
     elif len(args) == 0:
         quality_string = 'perfect'
         number = 1
     else:
         message = 'can not initialize {}: {!r}'
         message = message.format(type(self).__init__, args)
         raise ValueError(message)
     self._quality_string = quality_string
     self._number = number
def cumulative_signed_weights(sequence):
    r'''Cumulative signed weights of `sequence`.

    ::

        >>> l = [1, -2, -3, 4, -5, -6, 7, -8, -9, 10]
        >>> mathtools.cumulative_signed_weights(l)
        [1, -3, -6, 10, -15, -21, 28, -36, -45, 55]

    Raises type error when `sequence` is not a list.

    Use ``mathtools.cumulative_sums([abs(x) for x in l])``
    for cumulative (unsigned) weights

    Returns list.
    '''
    from abjad.tools import mathtools

    if not isinstance(sequence, list):
        raise TypeError

    result = []

    for x in sequence:
        try:
            next_element = abs(previous) + abs(x)
            previous_sign = mathtools.sign(previous)
        except NameError:
            next_element = abs(x)
            previous_sign = 0
        sign_x = mathtools.sign(x)
        if sign_x == -1:
            next_element *= sign_x
        elif sign_x == 0:
            next_element *= previous_sign
        result.append(next_element)
        previous = next_element

    return result
Beispiel #15
0
    def direction_number(self):
        r'''Direction number of named interval.

        ::

            >>> interval.direction_number
            1

        Returns ``-1``, ``0`` or ``1``.
        '''
        if self.quality_string == 'perfect' and abs(self.number) == 1:
            return 0
        else:
            return mathtools.sign(self.number)
 def __init__(self, *args):
     from abjad.tools import pitchtools
     from abjad.tools import sequencetools
     if len(args) == 1 and\
         isinstance(args[0], (pitchtools.NamedInterval,
             pitchtools.NamedIntervalClass)):
         quality_string = args[0]._quality_string
         number = args[0].number
     elif len(args) == 1 and isinstance(args[0], str):
         match = pitchtools.Interval._interval_name_abbreviation_regex.match(
             args[0])
         if match is None:
             raise ValueError(
                 '{!r} does not have the form of an abbreviation.'.format(
                 args[0]))
         direction_string, quality_abbreviation, number_string = \
             match.groups()
         quality_string = \
             NamedIntervalClass._quality_abbreviation_to_quality_string[
                 quality_abbreviation]
         number = int(direction_string + number_string)
     elif len(args) == 1 and sequencetools.is_pair(args[0]):
         quality_string, number = args[0]
     elif len(args) == 2:
         quality_string, number = args
     else:
         raise TypeError('what type of instance is this?, {!r}'.format(
             args))
     if quality_string not in \
         NamedIntervalClass._acceptable_quality_strings:
         raise ValueError('not acceptable quality string.')
     if not isinstance(number, int):
         raise TypeError('must be integer.')
     if number == 0:
         raise ValueError('must be nonzero.')
     sign = mathtools.sign(number)
     abs_number = abs(number)
     if abs_number % 7 == 1 and 8 <= abs_number:
         number = 8
     else:
         number = abs_number % 7
         if number == 0:
             number = 7
     if not number == 1:
         number *= sign
     self._number = number
     self._quality_string = quality_string
def repeat_sequence_to_weight_exactly(sequence, weight):
    '''Repeat `sequence` to `weight` exactly:

    ::

        >>> sequencetools.repeat_sequence_to_weight_exactly((5, -5, -5), 23)
        (5, -5, -5, 5, -3)

    Returns newly constructed `sequence` object.
    '''

    # check input
    assert isinstance(weight, numbers.Number)
    assert 0 <= weight

    # repeat sequence and find overage
    sequence_weight = mathtools.weight(sequence)
    complete_repetitions = int(math.ceil(float(weight) / float(sequence_weight)))
    result = list(sequence)
    result = complete_repetitions * result
    overage = complete_repetitions * sequence_weight - weight

    # remove overage from result
    for element in reversed(result):
        if 0 < overage:
            element_weight = abs(element)
            candidate_overage = overage - element_weight
            if 0 <= candidate_overage:
                overage = candidate_overage
                result.pop()
            else:
                absolute_amount_to_keep = element_weight - overage
                assert 0 < absolute_amount_to_keep
                signed_amount_to_keep = absolute_amount_to_keep
                signed_amount_to_keep *= mathtools.sign(element)
                result.pop()
                result.append(signed_amount_to_keep)
                break
        else:
            break

    # return result
    return type(sequence)(result)
def get_sequence_element_at_cyclic_index(sequence, index):
    r'''Get `sequence` element at nonnegative cyclic `index`:

    ::

        >>> for index in range(10):
        ...     print '%s\t%s' % (index, sequencetools.get_sequence_element_at_cyclic_index(
        ...     'string', index))
        ...
        0  s
        1  t
        2  r
        3  i
        4  n
        5  g
        6  s
        7  t
        8  r
        9  i

    Get `sequence` element at negative cyclic `index`:

    ::

        >>> for index in range(1, 11):
        ...     print '%s\t%s' % (-index, sequencetools.get_sequence_element_at_cyclic_index(
        ...     'string', -index))
        ...
        -1    g
        -2    n
        -3    i
        -4    r
        -5    t
        -6    s
        -7    g
        -8    n
        -9    i
        -10   r

    Returns reference to `sequence` element.
    '''

    return sequence[mathtools.sign(index) * (abs(index) % len(sequence))]
def partition_integer_by_ratio(n, ratio):
    r'''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.tools 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
Beispiel #20
0
def truncate_sequence(sequence, sum_=None, weight=None):
    '''Truncates `sequence`.

    ::

        >>> sequence = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

    ..  container:: example

        **Example 1.** Truncates sequence to weights ranging from 1 to 10:

        ::

            >>> for n in range(1, 11):
            ...     result = sequencetools.truncate_sequence(sequence, weight=n)
            ...     print(n, result)
            ... 
            1 [-1]
            2 [-1, 1]
            3 [-1, 2]
            4 [-1, 2, -1]
            5 [-1, 2, -2]
            6 [-1, 2, -3]
            7 [-1, 2, -3, 1]
            8 [-1, 2, -3, 2]
            9 [-1, 2, -3, 3]
            10 [-1, 2, -3, 4]

    ..  container:: example

        **Example 2.** Truncates sequence to sums ranging from 1 to 10:

        ::

            >>> for n in range(1, 11):
            ...     result = sequencetools.truncate_sequence(sequence, sum_=n)
            ...     print(n, result)
            ... 
            1 [-1, 2]
            2 [-1, 2, -3, 4]
            3 [-1, 2, -3, 4, -5, 6]
            4 [-1, 2, -3, 4, -5, 6, -7, 8]
            5 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            6 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            7 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            8 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            9 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            10 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

    ..  container:: example

        **Example 3.** Truncates sequence to zero weight:

        ::

            >>> sequencetools.truncate_sequence(sequence, weight=0)
            []

    ..  container:: example

        **Example 4.** Truncates sequence to zero sum:

        ::

            >>> sequencetools.truncate_sequence(sequence, sum_=0)
            []

    Ignores `sum_` when `weight` and `sum_` are both set.

    Raises type error when `sequence` is not a list.

    Raises value error on negative `sum_`.

    Returns new object of `sequence` type.
    '''

    if not isinstance(sequence, collections.Sequence):
        message = 'must by sequence {!r}.'
        message = message.format(sequence)
        raise Exception(message)
    
    sequence_type = type(sequence)

    if weight is not None:
        if weight < 0:
            raise ValueError
        result = []
        if 0 < weight:
            total = 0
            for element in sequence:
                total += abs(element)
                if total < weight:
                    result.append(element)
                else:
                    sign = mathtools.sign(element)
                    trimmed_part = weight - mathtools.weight(result)
                    trimmed_part *= sign
                    result.append(trimmed_part)
                    break
    elif sum_ is not None:
        sum_ = sum_
        if sum_ < 0:
            raise ValueError
        result = []
        if 0 < sum_:
            total = 0
            for element in sequence:
                total += element
                if total < sum_:
                    result.append(element)
                else:
                    result.append(sum_ - sum(result))
                    break

    result = sequence_type(result)
    return result
Beispiel #21
0
def split_sequence(sequence, weights, cyclic=False, overhang=False):
    """Splits sequence by weights.

    ..  container:: example

        **Example 1.** Split sequence cyclically by weights with overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            [(3,), (7, -8), (-2, 1), (3,), (6, -9), (-1,)]

    ..  container:: example

        **Example 2.** Split sequence cyclically by weights without overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            [(3,), (7, -8), (-2, 1), (3,), (6, -9)]

    ..  container:: example

        **Example 3.** Split sequence once by weights with overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [(3,), (7, -8), (-2, 1), (9, -10)]

    ..  container:: example

        **Example 4.** Split sequence once by weights without overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [(3,), (7, -8), (-2, 1)]

    Returns list of sequence types.
    """
    from abjad.tools import sequencetools

    result = []
    current_index = 0
    current_piece = []

    if cyclic:
        weights = sequencetools.repeat_sequence_to_weight(weights, mathtools.weight(sequence), allow_total=Less)

    for weight in weights:
        current_piece_weight = mathtools.weight(current_piece)
        while current_piece_weight < weight:
            current_piece.append(sequence[current_index])
            current_index += 1
            current_piece_weight = mathtools.weight(current_piece)
        if current_piece_weight == weight:
            current_piece = type(sequence)(current_piece)
            result.append(current_piece)
            current_piece = []
        elif weight < current_piece_weight:
            overage = current_piece_weight - weight
            current_last_element = current_piece.pop(-1)
            needed = abs(current_last_element) - overage
            needed *= mathtools.sign(current_last_element)
            current_piece.append(needed)
            current_piece = type(sequence)(current_piece)
            result.append(current_piece)
            overage *= mathtools.sign(current_last_element)
            current_piece = [overage]

    if overhang:
        last_piece = current_piece
        last_piece.extend(sequence[current_index:])
        if last_piece:
            last_piece = type(sequence)(last_piece)
            result.append(last_piece)

    return result
def partition_integer_by_ratio(n, ratio):
    r'''Partitions positive integer-equivalent `n` by `ratio`.

    ::

        >>> mathtools.partition_integer_by_ratio(10, [1, 2])
        [3, 7]

    Partitions positive integer-equivalent `n` by `ratio` with negative parts:

    ::

        >>> mathtools.partition_integer_by_ratio(10, [1, -2])
        [3, -7]

    Partitions negative integer-equivalent `n` by `ratio`:

    ::

        >>> mathtools.partition_integer_by_ratio(-10, [1, 2])
        [-3, -7]

    Partitions negative integer-equivalent `n` by `ratio` with negative parts:

    ::

        >>> mathtools.partition_integer_by_ratio(-10, [1, -2])
        [-3, 7]

    Returns result with weight equal to absolute value of `n`.

    Raises type error on noninteger `n`.

    Returns list of integers.
    '''
    from abjad.tools 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)
        #This makes rounding behave like python 2. Would be good to remove
        # in the long run
        if sys.version_info[0] == 3:
            if division - round(division) == 0.5:
                rounded_division += 1
        result.append(rounded_division)

    result = result[1:]

    # adjust signs of output elements
    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
    return result
Beispiel #23
0
    def to_named_interval(self, staff_positions):
        r'''Changes numbered interval to named interval that encompasses
        `staff_positions`.

        ..  container:: example

            >>> abjad.NumberedInterval(0).to_named_interval(0)
            NamedInterval('aug0')

            >>> abjad.NumberedInterval(0).to_named_interval(1)
            NamedInterval('P1')

            >>> abjad.NumberedInterval(0).to_named_interval(2)
            NamedInterval('+dim2')

        ..  container:: example

            >>> abjad.NumberedInterval(1).to_named_interval(1)
            NamedInterval('+aug1')

            >>> abjad.NumberedInterval(1).to_named_interval(2)
            NamedInterval('+m2')

        ..  container:: example

            >>> abjad.NumberedInterval(-1).to_named_interval(1)
            NamedInterval('-aug1')

            >>> abjad.NumberedInterval(-1).to_named_interval(2)
            NamedInterval('-m2')

        ..  container:: example

            >>> abjad.NumberedInterval(2).to_named_interval(2)
            NamedInterval('+M2')

        Returns named interval.
        '''
        from abjad.tools import pitchtools
        direction_number = mathtools.sign(self.number)
        quality_string = None
        if staff_positions == 1:
            if self.number % 12 == 11:
                quality_string = 'augmented'
            elif self.number % 12 == 0:
                quality_string = 'perfect'
            elif self.number % 12 == 1:
                quality_string = 'augmented'
            if not direction_number == 0:
                staff_positions *= direction_number
            if quality_string is None:
                # TODO: handle double-augmented named intervals
                return pitchtools.NamedInterval(self.number)
            named_interval = pitchtools.NamedInterval.from_quality_and_number(
                quality_string,
                staff_positions,
                )
            return named_interval
        named_interval_class_number = staff_positions % 7
        numbered_interval_class_number = abs(self.number) % 12
        if named_interval_class_number == 0:
            if numbered_interval_class_number == 9:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 10:
                quality_string = 'minor'
            elif numbered_interval_class_number == 11:
                quality_string = 'major'
            elif numbered_interval_class_number == 0:
                quality_string = 'augmented'
        elif named_interval_class_number == 1:
            if numbered_interval_class_number == 11:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 0:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 1:
                quality_string = 'augmented'
        elif named_interval_class_number == 2:
            if numbered_interval_class_number == 0:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 1:
                quality_string = 'minor'
            elif numbered_interval_class_number == 2:
                quality_string = 'major'
            elif numbered_interval_class_number == 3:
                quality_string = 'augmented'
        elif named_interval_class_number == 3:
            if numbered_interval_class_number == 2:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 3:
                quality_string = 'minor'
            elif numbered_interval_class_number == 4:
                quality_string = 'major'
            elif numbered_interval_class_number == 5:
                quality_string = 'augmented'
        elif named_interval_class_number == 4:
            if numbered_interval_class_number == 4:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 5:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 6:
                quality_string = 'augmented'
        elif named_interval_class_number == 5:
            if numbered_interval_class_number == 6:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 7:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 8:
                quality_string = 'augmented'
        elif named_interval_class_number == 6:
            if numbered_interval_class_number == 7:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 8:
                quality_string = 'minor'
            elif numbered_interval_class_number == 9:
                quality_string = 'major'
            elif numbered_interval_class_number == 10:
                quality_string = 'augmented'
        elif named_interval_class_number == 7:
            if numbered_interval_class_number == 9:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 10:
                quality_string = 'minor'
            elif numbered_interval_class_number == 11:
                quality_string = 'major'
            elif numbered_interval_class_number == 0:
                quality_string = 'augmented'
        elif named_interval_class_number == 8:
            if numbered_interval_class_number == 11:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 0:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 1:
                quality_string = 'augmented'
        if not direction_number == 0:
            staff_positions *= direction_number
        if quality_string is None:
            # TODO: It is possible to for quality string to *never* get set to
            #       anything, generally during inversion with double-sharps or
            #       double-flats. This suite provides a sane result.
            #       Don't remove it - fix whatever's allowing quality string to
            #       remain unset.
            return pitchtools.NamedInterval(self.number)
        named_interval = pitchtools.NamedInterval.from_quality_and_number(
            quality_string,
            staff_positions,
            )
        return named_interval
    def tessalate_by_ratio(self, 
        ratio, 
        invert_on_negative=False, 
        reflect_on_negative=False,
        y_center=None,
        ):
        r'''Concatenate copies of a BreakPointFunction, stretched by 
        the weights in `ratio`:

        ::

            >>> bpf = interpolationtools.BreakPointFunction(
            ...     {0.: 0., 0.25: 0.9, 1.: 1.})

        ::

            >>> bpf.tessalate_by_ratio((1, 2, 3))
            BreakPointFunction({
                0.0: (0.0,),
                0.25: (0.9,),
                1.0: (1.0, 0.0),
                1.5: (0.9,),
                3.0: (1.0, 0.0),
                3.75: (0.9,),
                6.0: (1.0,)
            })

        Negative ratio values are still treated as weights.

        If `invert_on_negative` is True, copies corresponding to 
        negative ratio values will be inverted:

        ::

            >>> bpf.tessalate_by_ratio((1, -2, 3), invert_on_negative=True)
            BreakPointFunction({
                0.0: (0.0,),
                0.25: (0.9,),
                1.0: (1.0,),
                1.5: (0.09999999999999998,),
                3.0: (0.0,),
                3.75: (0.9,),
                6.0: (1.0,)
            })

        If `y_center` is not None, inversion will take `y_center` as 
        the axis of inversion:

        ::

            >>> bpf.tessalate_by_ratio((1, -2, 3), 
            ...     invert_on_negative=True, y_center=0)
            BreakPointFunction({
                0.0: (0.0,),
                0.25: (0.9,),
                1.0: (1.0, 0.0),
                1.5: (-0.9,),
                3.0: (-1.0, 0.0),
                3.75: (0.9,),
                6.0: (1.0,)
            })

        If `reflect_on_negative` is True, copies corresponding to 
        negative ratio values will be reflectd:

        ::

            >>> bpf.tessalate_by_ratio((1, -2, 3), reflect_on_negative=True)
            BreakPointFunction({
                0.0: (0.0,),
                0.25: (0.9,),
                1.0: (1.0,),
                2.5: (0.9,),
                3.0: (0.0,),
                3.75: (0.9,),
                6.0: (1.0,)
            })

        Inversion may be combined reflecting.

        Emit new `BreakPointFunction` instance.
        '''
        ratio = mathtools.Ratio(ratio)
        tessalated_bpf = None
        for i, pair in enumerate(mathtools.cumulative_sums_pairwise(
            [abs(x) for x in ratio])):
            sign = mathtools.sign(ratio[i])
            start, stop = pair
            bpf = self.scale_x_axis(start, stop)
            if sign < 0:
                if invert_on_negative:
                    bpf = bpf.invert(y_center)
                if reflect_on_negative:
                    bpf = bpf.reflect()
            if i == 0:
                tessalated_bpf = bpf
            else:
                tessalated_bpf = tessalated_bpf.concatenate(bpf)
        return tessalated_bpf
Beispiel #25
0
def split_sequence(sequence, weights, cyclic=False, overhang=False):
    '''Splits sequence by weights.

    ..  container:: example

        **Example 1.** Splits sequence cyclically by weights with overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            ((3,), (7, -8), (-2, 1), (3,), (6, -9), (-1,))

    ..  container:: example

        **Example 2.** Splits sequence cyclically by weights without overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            ((3,), (7, -8), (-2, 1), (3,), (6, -9))

    ..  container:: example

        **Example 3.** Splits sequence once by weights with overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            ((3,), (7, -8), (-2, 1), (9, -10))

    ..  container:: example

        **Example 4.** Splits sequence once by weights without overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            ((3,), (7, -8), (-2, 1))

    ..  container:: example

        **Example 5.** Splits list once by weights without overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     [10, -10, 10, -10],
            ...     (3, 15, 3),
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [[3], [7, -8], [-2, 1]]

    Returns new object of `sequence` type with elements also of `sequence`
    type.
    '''
    from abjad.tools import sequencetools

    if not isinstance(sequence, collections.Sequence):
        message = 'must by sequence {!r}.'
        message = message.format(sequence)
        raise Exception(message)

    sequence_type = type(sequence)

    result = []
    current_index = 0
    current_piece = []

    if cyclic:
        weights = sequencetools.repeat_sequence_to_weight(
            weights,
            mathtools.weight(sequence),
            allow_total=Less,
            )

    for weight in weights:
        current_piece_weight = mathtools.weight(current_piece)
        while current_piece_weight < weight:
            current_piece.append(sequence[current_index])
            current_index += 1
            current_piece_weight = mathtools.weight(current_piece)
        if current_piece_weight == weight:
            current_piece = type(sequence)(current_piece)
            result.append(current_piece)
            current_piece = []
        elif weight < current_piece_weight:
            overage = current_piece_weight - weight
            current_last_element = current_piece.pop(-1)
            needed = abs(current_last_element) - overage
            needed *= mathtools.sign(current_last_element)
            current_piece.append(needed)
            current_piece = type(sequence)(current_piece)
            result.append(current_piece)
            overage *= mathtools.sign(current_last_element)
            current_piece = [overage]

    if overhang:
        last_piece = current_piece
        last_piece.extend(sequence[current_index:])
        if last_piece:
            last_piece = type(sequence)(last_piece)
            result.append(last_piece)

    result = sequence_type(result)
    return result
Beispiel #26
0
    def to_named_interval(self, staff_positions):
        r'''Changes numbered interval to named interval that encompasses
        `staff_positions`.

        ..  container:: example

            ::

                >>> numbered_interval = pitchtools.NumberedInterval(1)
                >>> numbered_interval.to_named_interval(2)
                NamedInterval('+m2')

        Returns named interval.
        '''
        from abjad.tools import pitchtools
        direction_number = mathtools.sign(self.number)
        if staff_positions == 1:
            quality_string = None
            if self.number % 12 == 11:
                quality_string = 'augmented'
            elif self.number % 12 == 0:
                quality_string = 'perfect'
            elif self.number % 12 == 1:
                quality_string = 'augmented'
            if not direction_number == 0:
                staff_positions *= direction_number
            if quality_string is None:
                # TODO: handle double-augmented named intervals
                return pitchtools.NamedInterval(self.number)
            named_interval = pitchtools.NamedInterval(quality_string,
                                                      staff_positions)
            return named_interval
        named_interval_class_number = staff_positions % 7
        numbered_interval_class_number = abs(self.number) % 12
        if named_interval_class_number == 0:
            if numbered_interval_class_number == 9:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 10:
                quality_string = 'minor'
            elif numbered_interval_class_number == 11:
                quality_string = 'major'
            elif numbered_interval_class_number == 0:
                quality_string = 'augmented'
        elif named_interval_class_number == 1:
            if numbered_interval_class_number == 11:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 0:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 1:
                quality_string = 'augmented'
        elif named_interval_class_number == 2:
            if numbered_interval_class_number == 0:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 1:
                quality_string = 'minor'
            elif numbered_interval_class_number == 2:
                quality_string = 'major'
            elif numbered_interval_class_number == 3:
                quality_string = 'augmented'
        elif named_interval_class_number == 3:
            if numbered_interval_class_number == 2:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 3:
                quality_string = 'minor'
            elif numbered_interval_class_number == 4:
                quality_string = 'major'
            elif numbered_interval_class_number == 5:
                quality_string = 'augmented'
        elif named_interval_class_number == 4:
            if numbered_interval_class_number == 4:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 5:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 6:
                quality_string = 'augmented'
        elif named_interval_class_number == 5:
            if numbered_interval_class_number == 6:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 7:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 8:
                quality_string = 'augmented'
        elif named_interval_class_number == 6:
            if numbered_interval_class_number == 7:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 8:
                quality_string = 'minor'
            elif numbered_interval_class_number == 9:
                quality_string = 'major'
            elif numbered_interval_class_number == 10:
                quality_string = 'augmented'
        elif named_interval_class_number == 7:
            if numbered_interval_class_number == 9:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 10:
                quality_string = 'minor'
            elif numbered_interval_class_number == 11:
                quality_string = 'major'
            elif numbered_interval_class_number == 0:
                quality_string = 'augmented'
        elif named_interval_class_number == 8:
            if numbered_interval_class_number == 11:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 0:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 1:
                quality_string = 'augmented'
        if not direction_number == 0:
            staff_positions *= direction_number
        named_interval = pitchtools.NamedInterval(
            quality_string,
            staff_positions,
        )
        return named_interval
Beispiel #27
0
    def tessalate_by_ratio(
        self,
        ratio,
        invert_on_negative=False,
        reflect_on_negative=False,
        y_center=None,
    ):
        r'''Concatenate copies of a BreakPointFunction, stretched by
        the weights in `ratio`:

        ::

            >>> bpf = interpolationtools.BreakPointFunction(
            ...     {0.: 0., 0.25: 0.9, 1.: 1.})

        ::

            >>> bpf.tessalate_by_ratio((1, 2, 3))
            BreakPointFunction({
                0.0: (0.0,),
                0.25: (0.9,),
                1.0: (1.0, 0.0),
                1.5: (0.9,),
                3.0: (1.0, 0.0),
                3.75: (0.9,),
                6.0: (1.0,)
            })

        Negative ratio values are still treated as weights.

        If `invert_on_negative` is True, copies corresponding to
        negative ratio values will be inverted:

        ::

            >>> bpf.tessalate_by_ratio((1, -2, 3), invert_on_negative=True)
            BreakPointFunction({
                0.0: (0.0,),
                0.25: (0.9,),
                1.0: (1.0,),
                1.5: (0.09999999999999998,),
                3.0: (0.0,),
                3.75: (0.9,),
                6.0: (1.0,)
            })

        If `y_center` is not None, inversion will take `y_center` as
        the axis of inversion:

        ::

            >>> bpf.tessalate_by_ratio((1, -2, 3),
            ...     invert_on_negative=True, y_center=0)
            BreakPointFunction({
                0.0: (0.0,),
                0.25: (0.9,),
                1.0: (1.0, 0.0),
                1.5: (-0.9,),
                3.0: (-1.0, 0.0),
                3.75: (0.9,),
                6.0: (1.0,)
            })

        If `reflect_on_negative` is True, copies corresponding to
        negative ratio values will be reflectd:

        ::

            >>> bpf.tessalate_by_ratio((1, -2, 3), reflect_on_negative=True)
            BreakPointFunction({
                0.0: (0.0,),
                0.25: (0.9,),
                1.0: (1.0,),
                2.5: (0.9,),
                3.0: (0.0,),
                3.75: (0.9,),
                6.0: (1.0,)
            })

        Inversion may be combined reflecting.

        Emit new `BreakPointFunction` instance.
        '''
        ratio = mathtools.Ratio(ratio)
        tessalated_bpf = None
        for i, pair in enumerate(
                mathtools.cumulative_sums_pairwise([abs(x) for x in ratio])):
            sign = mathtools.sign(ratio[i])
            start, stop = pair
            bpf = self.scale_x_axis(start, stop)
            if sign < 0:
                if invert_on_negative:
                    bpf = bpf.invert(y_center)
                if reflect_on_negative:
                    bpf = bpf.reflect()
            if i == 0:
                tessalated_bpf = bpf
            else:
                tessalated_bpf = tessalated_bpf.concatenate(bpf)
        return tessalated_bpf
def spell_numbered_interval_number(
    named_interval_number, 
    numbered_interval_number,
    ):
    '''Spell `numbered_interval_number` according to `named_interval_number`:

    ::

        >>> pitchtools.spell_numbered_interval_number(2, 1)
        NamedInterval('+m2')

    Returns named interval.
    '''
    from abjad.tools import pitchtools

    if not isinstance(numbered_interval_number, int):
        message = 'can not determine diatonic interval from float.'
        raise TypeError(message)

    direction_number = mathtools.sign(numbered_interval_number)

    if named_interval_number == 1:
        if numbered_interval_number % 12 == 11:
            quality_string = 'augmented'
        elif numbered_interval_number % 12 == 0:
            quality_string = 'perfect'
        elif numbered_interval_number % 12 == 1:
            quality_string = 'augmented'
        if not direction_number == 0:
            named_interval_number *= direction_number
        named_interval = pitchtools.NamedInterval(
            quality_string, named_interval_number)
        return named_interval

    named_interval_class_number = named_interval_number % 7
    numbered_interval_class_number = abs(numbered_interval_number) % 12

    if named_interval_class_number == 0:
        if numbered_interval_class_number == 9:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 10:
            quality_string = 'minor'
        elif numbered_interval_class_number == 11:
            quality_string = 'major'
        elif numbered_interval_class_number == 0:
            quality_string = 'augmented'
    elif named_interval_class_number == 1:
        if numbered_interval_class_number == 11:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 0:
            quality_string = 'perfect'
        elif numbered_interval_class_number == 1:
            quality_string = 'augmented'
    elif named_interval_class_number == 2:
        if numbered_interval_class_number == 0:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 1:
            quality_string = 'minor'
        elif numbered_interval_class_number == 2:
            quality_string = 'major'
        elif numbered_interval_class_number == 3:
            quality_string = 'augmented'
    elif named_interval_class_number == 3:
        if numbered_interval_class_number == 2:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 3:
            quality_string = 'minor'
        elif numbered_interval_class_number == 4:
            quality_string = 'major'
        elif numbered_interval_class_number == 5:
            quality_string = 'augmented'
    elif named_interval_class_number == 4:
        if numbered_interval_class_number == 4:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 5:
            quality_string = 'perfect'
        elif numbered_interval_class_number == 6:
            quality_string = 'augmented'
    elif named_interval_class_number == 5:
        if numbered_interval_class_number == 6:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 7:
            quality_string = 'perfect'
        elif numbered_interval_class_number == 8:
            quality_string = 'augmented'
    elif named_interval_class_number == 6:
        if numbered_interval_class_number == 7:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 8:
            quality_string = 'minor'
        elif numbered_interval_class_number == 9:
            quality_string = 'major'
        elif numbered_interval_class_number == 10:
            quality_string = 'augmented'
    elif named_interval_class_number == 7:
        if numbered_interval_class_number == 9:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 10:
            quality_string = 'minor'
        elif numbered_interval_class_number == 11:
            quality_string = 'major'
        elif numbered_interval_class_number == 0:
            quality_string = 'augmented'
    elif named_interval_class_number == 8:
        if numbered_interval_class_number == 11:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 0:
            quality_string = 'perfect'
        elif numbered_interval_class_number == 1:
            quality_string = 'augmented'

    if not direction_number == 0:
        named_interval_number *= direction_number

    named_interval = pitchtools.NamedInterval(quality_string, named_interval_number)

    return named_interval
def partition_integer_by_ratio(n, ratio):
    r'''Partitions positive integer-equivalent `n` by `ratio`.

    ::

        >>> mathtools.partition_integer_by_ratio(10, [1, 2])
        [3, 7]

    Partitions positive integer-equivalent `n` by `ratio` with negative parts:

    ::

        >>> mathtools.partition_integer_by_ratio(10, [1, -2])
        [3, -7]

    Partitions negative integer-equivalent `n` by `ratio`:

    ::

        >>> mathtools.partition_integer_by_ratio(-10, [1, 2])
        [-3, -7]

    Partitions negative integer-equivalent `n` by `ratio` with negative parts:

    ::

        >>> mathtools.partition_integer_by_ratio(-10, [1, -2])
        [-3, 7]

    Returns result with weight equal to absolute value of `n`.

    Raises type error on noninteger `n`.

    Returns list of integers.
    '''
    from abjad.tools 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)
        #This makes rounding behave like python 2. Would be good to remove
        # in the long run
        if sys.version_info[0] == 3:
            if division - round(division) == 0.5:
                rounded_division += 1
        result.append(rounded_division)

    result = result[1:]

    # adjust signs of output elements
    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
    return result
    def to_named_interval(self, staff_positions):
        r'''Changes numbered interval to named interval that encompasses
        `staff_positions`.

        ..  container:: example

            ::

                >>> numbered_interval = pitchtools.NumberedInterval(1)
                >>> numbered_interval.to_named_interval(2)
                NamedInterval('+m2')

        Returns named interval.
        '''
        from abjad.tools import pitchtools
        direction_number = mathtools.sign(self.number)
        if staff_positions == 1:
            quality_string = None
            if self.number % 12 == 11:
                quality_string = 'augmented'
            elif self.number % 12 == 0:
                quality_string = 'perfect'
            elif self.number % 12 == 1:
                quality_string = 'augmented'
            if not direction_number == 0:
                staff_positions *= direction_number
            if quality_string is None:
                # TODO: handle double-augmented named intervals
                return pitchtools.NamedInterval(self.number)
            named_interval = pitchtools.NamedInterval(
                quality_string, staff_positions)
            return named_interval
        named_interval_class_number = staff_positions % 7
        numbered_interval_class_number = abs(self.number) % 12
        if named_interval_class_number == 0:
            if numbered_interval_class_number == 9:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 10:
                quality_string = 'minor'
            elif numbered_interval_class_number == 11:
                quality_string = 'major'
            elif numbered_interval_class_number == 0:
                quality_string = 'augmented'
        elif named_interval_class_number == 1:
            if numbered_interval_class_number == 11:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 0:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 1:
                quality_string = 'augmented'
        elif named_interval_class_number == 2:
            if numbered_interval_class_number == 0:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 1:
                quality_string = 'minor'
            elif numbered_interval_class_number == 2:
                quality_string = 'major'
            elif numbered_interval_class_number == 3:
                quality_string = 'augmented'
        elif named_interval_class_number == 3:
            if numbered_interval_class_number == 2:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 3:
                quality_string = 'minor'
            elif numbered_interval_class_number == 4:
                quality_string = 'major'
            elif numbered_interval_class_number == 5:
                quality_string = 'augmented'
        elif named_interval_class_number == 4:
            if numbered_interval_class_number == 4:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 5:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 6:
                quality_string = 'augmented'
        elif named_interval_class_number == 5:
            if numbered_interval_class_number == 6:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 7:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 8:
                quality_string = 'augmented'
        elif named_interval_class_number == 6:
            if numbered_interval_class_number == 7:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 8:
                quality_string = 'minor'
            elif numbered_interval_class_number == 9:
                quality_string = 'major'
            elif numbered_interval_class_number == 10:
                quality_string = 'augmented'
        elif named_interval_class_number == 7:
            if numbered_interval_class_number == 9:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 10:
                quality_string = 'minor'
            elif numbered_interval_class_number == 11:
                quality_string = 'major'
            elif numbered_interval_class_number == 0:
                quality_string = 'augmented'
        elif named_interval_class_number == 8:
            if numbered_interval_class_number == 11:
                quality_string = 'diminished'
            elif numbered_interval_class_number == 0:
                quality_string = 'perfect'
            elif numbered_interval_class_number == 1:
                quality_string = 'augmented'
        if not direction_number == 0:
            staff_positions *= direction_number
        named_interval = pitchtools.NamedInterval(
            quality_string, 
            staff_positions,
            )
        return named_interval
def partition_integer_into_canonic_parts(n, decrease_parts_monotonically=True):
    r'''Partitions integer `n` into canonic parts.

    Returns all parts positive on positive `n`:

    ::

        >>> for n in range(1, 11):
        ...     print(n, mathtools.partition_integer_into_canonic_parts(n))
        ... 
        1 (1,)
        2 (2,)
        3 (3,)
        4 (4,)
        5 (4, 1)
        6 (6,)
        7 (7,)
        8 (8,)
        9 (8, 1)
        10 (8, 2)

    Returns all parts negative on negative `n`:

    ::

        >>> for n in reversed(range(-20, -10)):
        ...     print(n, mathtools.partition_integer_into_canonic_parts(n))
        ... 
        -11 (-8, -3)
        -12 (-12,)
        -13 (-12, -1)
        -14 (-14,)
        -15 (-15,)
        -16 (-16,)
        -17 (-16, -1)
        -18 (-16, -2)
        -19 (-16, -3)
        -20 (-16, -4)

    Returns parts that increase monotonically:

    ::

        >>> for n in range(11, 21):
        ...     print(n, mathtools.partition_integer_into_canonic_parts(n,
        ...         decrease_parts_monotonically=False))
        ... 
        11 (3, 8)
        12 (12,)
        13 (1, 12)
        14 (14,)
        15 (15,)
        16 (16,)
        17 (1, 16)
        18 (2, 16)
        19 (3, 16)
        20 (4, 16)

    Returns tuple with parts that decrease monotonically.

    Raises type error on noninteger `n`.

    Returns tuple of one or more integers.
    '''
    from abjad.tools import mathtools

    if not isinstance(n, int):
        raise TypeError

    if not isinstance(decrease_parts_monotonically, bool):
        raise ValueError

    if n == 0:
        return (0, )

    result = []
    previous_empty = True
    binary_n = mathtools.integer_to_binary_string(abs(n))
    binary_length = len(binary_n)

    for i, x in enumerate(binary_n):
        if x == '1':
            place_value = 2 ** (binary_length - i - 1)
            if previous_empty:
                result.append(place_value)
            else:
                result[-1] += place_value
            previous_empty = False
        else:
            previous_empty = True

    sign_n = mathtools.sign(n)
    if mathtools.sign(n) == -1:
        result = [sign_n * x for x in result]

    if decrease_parts_monotonically:
        return tuple(result)
    else:
        return tuple(reversed(result))
def repeat_sequence_to_weight(sequence, weight, allow_total=Exact):
    '''Repeats `sequence` to `weight`.

    ..  container:: example

        **Example 1.** Repeats sequence to weight of 23 exactly:

        ::

            >>> sequencetools.repeat_sequence_to_weight((5, -5, -5), 23)
            (5, -5, -5, 5, -3)

        Truncates last element when necessary.

    ..  container:: example

        **Example 2.** Repeats sequence to weight of 23 more:

        ::

            >>> sequencetools.repeat_sequence_to_weight(
            ...     (5, -5, -5),
            ...     23,
            ...     allow_total=More,
            ...     )
            (5, -5, -5, 5, -5)

        Does not truncate last element.

    ..  container:: example

        **Example 3.** Repeats sequence to weight of 23 or less:

        ::

            >>> sequencetools.repeat_sequence_to_weight(
            ...     (5, -5, -5),
            ...     23,
            ...     allow_total=Less,
            ...     )
            (5, -5, -5, 5)

        Discards last element when necessary.

    Returns new object of `sequence` type.
    '''

    if not isinstance(sequence, collections.Sequence):
        message = 'must by sequence {!r}.'
        message = message.format(sequence)
        raise Exception(message)

    sequence_type = type(sequence)

    # check input
    assert isinstance(weight, numbers.Number), repr(weight)
    assert 0 <= weight

    if allow_total == Exact:
        # repeat sequence and find overage
        sequence_weight = mathtools.weight(sequence)
        complete_repetitions = int(
            math.ceil(float(weight) / float(sequence_weight))
            )
        result = list(sequence)
        result = complete_repetitions * result
        overage = complete_repetitions * sequence_weight - weight
        # remove overage from result
        for element in reversed(result):
            if 0 < overage:
                element_weight = abs(element)
                candidate_overage = overage - element_weight
                if 0 <= candidate_overage:
                    overage = candidate_overage
                    result.pop()
                else:
                    absolute_amount_to_keep = element_weight - overage
                    assert 0 < absolute_amount_to_keep
                    signed_amount_to_keep = absolute_amount_to_keep
                    signed_amount_to_keep *= mathtools.sign(element)
                    result.pop()
                    result.append(signed_amount_to_keep)
                    break
            else:
                break
    elif allow_total == Less:
        # initialize result
        result = [sequence[0]]
        # iterate input
        i = 1
        while mathtools.weight(result) < weight:
            result.append(sequence[i % len(sequence)])
            i += 1
        # remove overage
        if weight < mathtools.weight(result):
            result = result[:-1]
        # return result
        return type(sequence)(result)
    elif allow_total == More:
        # initialize result
        result = [sequence[0]]
        # iterate input
        i = 1
        while mathtools.weight(result) < weight:
            result.append(sequence[i % len(sequence)])
            i += 1
        # return result
        return type(sequence)(result)
    else:
        message = 'is not an ordinal value constant: {!r}.'
        message = message.format(allow_total)
        raise ValueError(message)

    # return result
    result = sequence_type(result)
    return result
Beispiel #33
0
def truncate_sequence(sequence, sum_=None, weight=None):
    '''Truncates `sequence`.

    ::

        >>> sequence = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

    ..  container:: example

        **Example 1.** Truncates sequence to weights ranging from 1 to 10:

        ::

            >>> for n in range(1, 11):
            ...     result = sequencetools.truncate_sequence(sequence, weight=n)
            ...     print(n, result)
            ... 
            1 [-1]
            2 [-1, 1]
            3 [-1, 2]
            4 [-1, 2, -1]
            5 [-1, 2, -2]
            6 [-1, 2, -3]
            7 [-1, 2, -3, 1]
            8 [-1, 2, -3, 2]
            9 [-1, 2, -3, 3]
            10 [-1, 2, -3, 4]

    ..  container:: example

        **Example 2.** Truncates sequence to sums ranging from 1 to 10:

        ::

            >>> for n in range(1, 11):
            ...     result = sequencetools.truncate_sequence(sequence, sum_=n)
            ...     print(n, result)
            ... 
            1 [-1, 2]
            2 [-1, 2, -3, 4]
            3 [-1, 2, -3, 4, -5, 6]
            4 [-1, 2, -3, 4, -5, 6, -7, 8]
            5 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            6 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            7 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            8 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            9 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
            10 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

    ..  container:: example

        **Example 3.** Truncates sequence to zero weight:

        ::

            >>> sequencetools.truncate_sequence(sequence, weight=0)
            []

    ..  container:: example

        **Example 4.** Truncates sequence to zero sum:

        ::

            >>> sequencetools.truncate_sequence(sequence, sum_=0)
            []

    Ignores `sum_` when `weight` and `sum_` are both set.

    Raises type error when `sequence` is not a list.

    Raises value error on negative `sum_`.

    Returns new object of `sequence` type.
    '''

    if not isinstance(sequence, collections.Sequence):
        message = 'must by sequence {!r}.'
        message = message.format(sequence)
        raise Exception(message)

    sequence_type = type(sequence)

    if weight is not None:
        if weight < 0:
            raise ValueError
        result = []
        if 0 < weight:
            total = 0
            for element in sequence:
                total += abs(element)
                if total < weight:
                    result.append(element)
                else:
                    sign = mathtools.sign(element)
                    trimmed_part = weight - mathtools.weight(result)
                    trimmed_part *= sign
                    result.append(trimmed_part)
                    break
    elif sum_ is not None:
        sum_ = sum_
        if sum_ < 0:
            raise ValueError
        result = []
        if 0 < sum_:
            total = 0
            for element in sequence:
                total += element
                if total < sum_:
                    result.append(element)
                else:
                    result.append(sum_ - sum(result))
                    break

    result = sequence_type(result)
    return result
Beispiel #34
0
def spell_numbered_interval_number(
    named_interval_number,
    numbered_interval_number,
):
    '''Spell `numbered_interval_number` according to `named_interval_number`:

    ::

        >>> pitchtools.spell_numbered_interval_number(2, 1)
        NamedInterval('+m2')

    Returns named interval.
    '''
    from abjad.tools import pitchtools

    if not isinstance(numbered_interval_number, int):
        message = 'can not determine diatonic interval from float.'
        raise TypeError(message)

    direction_number = mathtools.sign(numbered_interval_number)

    if named_interval_number == 1:
        if numbered_interval_number % 12 == 11:
            quality_string = 'augmented'
        elif numbered_interval_number % 12 == 0:
            quality_string = 'perfect'
        elif numbered_interval_number % 12 == 1:
            quality_string = 'augmented'
        if not direction_number == 0:
            named_interval_number *= direction_number
        named_interval = pitchtools.NamedInterval(quality_string,
                                                  named_interval_number)
        return named_interval

    named_interval_class_number = named_interval_number % 7
    numbered_interval_class_number = abs(numbered_interval_number) % 12

    if named_interval_class_number == 0:
        if numbered_interval_class_number == 9:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 10:
            quality_string = 'minor'
        elif numbered_interval_class_number == 11:
            quality_string = 'major'
        elif numbered_interval_class_number == 0:
            quality_string = 'augmented'
    elif named_interval_class_number == 1:
        if numbered_interval_class_number == 11:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 0:
            quality_string = 'perfect'
        elif numbered_interval_class_number == 1:
            quality_string = 'augmented'
    elif named_interval_class_number == 2:
        if numbered_interval_class_number == 0:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 1:
            quality_string = 'minor'
        elif numbered_interval_class_number == 2:
            quality_string = 'major'
        elif numbered_interval_class_number == 3:
            quality_string = 'augmented'
    elif named_interval_class_number == 3:
        if numbered_interval_class_number == 2:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 3:
            quality_string = 'minor'
        elif numbered_interval_class_number == 4:
            quality_string = 'major'
        elif numbered_interval_class_number == 5:
            quality_string = 'augmented'
    elif named_interval_class_number == 4:
        if numbered_interval_class_number == 4:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 5:
            quality_string = 'perfect'
        elif numbered_interval_class_number == 6:
            quality_string = 'augmented'
    elif named_interval_class_number == 5:
        if numbered_interval_class_number == 6:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 7:
            quality_string = 'perfect'
        elif numbered_interval_class_number == 8:
            quality_string = 'augmented'
    elif named_interval_class_number == 6:
        if numbered_interval_class_number == 7:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 8:
            quality_string = 'minor'
        elif numbered_interval_class_number == 9:
            quality_string = 'major'
        elif numbered_interval_class_number == 10:
            quality_string = 'augmented'
    elif named_interval_class_number == 7:
        if numbered_interval_class_number == 9:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 10:
            quality_string = 'minor'
        elif numbered_interval_class_number == 11:
            quality_string = 'major'
        elif numbered_interval_class_number == 0:
            quality_string = 'augmented'
    elif named_interval_class_number == 8:
        if numbered_interval_class_number == 11:
            quality_string = 'diminished'
        elif numbered_interval_class_number == 0:
            quality_string = 'perfect'
        elif numbered_interval_class_number == 1:
            quality_string = 'augmented'

    if not direction_number == 0:
        named_interval_number *= direction_number

    named_interval = pitchtools.NamedInterval(quality_string,
                                              named_interval_number)

    return named_interval
Beispiel #35
0
def split_sequence(sequence, weights, cyclic=False, overhang=False):
    '''Splits sequence by weights.

    ..  container:: example

        **Example 1.** Splits sequence cyclically by weights with overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            ((3,), (7, -8), (-2, 1), (3,), (6, -9), (-1,))

    ..  container:: example

        **Example 2.** Splits sequence cyclically by weights without overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            ((3,), (7, -8), (-2, 1), (3,), (6, -9))

    ..  container:: example

        **Example 3.** Splits sequence once by weights with overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            ((3,), (7, -8), (-2, 1), (9, -10))

    ..  container:: example

        **Example 4.** Splits sequence once by weights without overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     (10, -10, 10, -10),
            ...     (3, 15, 3),
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            ((3,), (7, -8), (-2, 1))

    ..  container:: example

        **Example 5.** Splits list once by weights without overhang:

        ::

            >>> sequencetools.split_sequence(
            ...     [10, -10, 10, -10],
            ...     (3, 15, 3),
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [[3], [7, -8], [-2, 1]]

    Returns new object of `sequence` type with elements also of `sequence`
    type.
    '''
    from abjad.tools import sequencetools

    if not isinstance(sequence, collections.Sequence):
        message = 'must by sequence {!r}.'
        message = message.format(sequence)
        raise Exception(message)

    sequence_type = type(sequence)

    result = []
    current_index = 0
    current_piece = []

    if cyclic:
        weights = sequencetools.repeat_sequence_to_weight(
            weights,
            mathtools.weight(sequence),
            allow_total=Less,
        )

    for weight in weights:
        current_piece_weight = mathtools.weight(current_piece)
        while current_piece_weight < weight:
            current_piece.append(sequence[current_index])
            current_index += 1
            current_piece_weight = mathtools.weight(current_piece)
        if current_piece_weight == weight:
            current_piece = type(sequence)(current_piece)
            result.append(current_piece)
            current_piece = []
        elif weight < current_piece_weight:
            overage = current_piece_weight - weight
            current_last_element = current_piece.pop(-1)
            needed = abs(current_last_element) - overage
            needed *= mathtools.sign(current_last_element)
            current_piece.append(needed)
            current_piece = type(sequence)(current_piece)
            result.append(current_piece)
            overage *= mathtools.sign(current_last_element)
            current_piece = [overage]

    if overhang:
        last_piece = current_piece
        last_piece.extend(sequence[current_index:])
        if last_piece:
            last_piece = type(sequence)(last_piece)
            result.append(last_piece)

    result = sequence_type(result)
    return result
Beispiel #36
0
 def direction_number(self):
     if self.quality_string == 'perfect' and abs(self.number) == 1:
         return 0
     else:
         return mathtools.sign(self.number)