Beispiel #1
0
 def _burnish_all_division_parts(self, divisions, quintuplet):
     lefts, middles, rights, left_lengths, right_lengths=quintuplet
     lefts_index, rights_index = 0, 0
     burnished_divisions = []
     for division_index, division in enumerate(divisions):
         left_length = left_lengths[division_index]
         left = lefts[lefts_index:lefts_index+left_length]
         lefts_index += left_length
         right_length = right_lengths[division_index]
         right = rights[rights_index:rights_index+right_length]
         rights_index += right_length
         available_left_length = len(division)
         left_length = min([left_length, available_left_length])
         available_right_length = len(division) - left_length
         right_length = min([right_length, available_right_length])
         middle_length = len(division) - left_length - right_length
         left = left[:left_length]
         middle = middle_length * [middles[division_index]]
         right = right[:right_length]
         left_part, middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
             division, 
             [left_length, middle_length, right_length], 
             cyclic=False, 
             overhang=False,
             )
         left_part = self._burnish_division_part(left_part, left)
         middle_part = self._burnish_division_part(middle_part, middle)
         right_part = self._burnish_division_part(right_part, right)
         burnished_division = left_part + middle_part + right_part
         burnished_divisions.append(burnished_division)
     unburnished_weights = [mathtools.weight(x) for x in divisions]
     burnished_weights = [mathtools.weight(x) for x in burnished_divisions]
     assert burnished_weights == unburnished_weights
     return burnished_divisions
Beispiel #2
0
 def _make_numeric_map_part(
     self,
     numerator,
     prefix,
     suffix,
     is_note_filled=True,
     ):
     prefix_weight = mathtools.weight(prefix)
     suffix_weight = mathtools.weight(suffix)
     middle = numerator - prefix_weight - suffix_weight
     if numerator < prefix_weight:
         weights = [numerator]
         prefix = sequencetools.split_sequence(
             prefix, weights, cyclic=False, overhang=False)[0]
     middle = self._make_middle_of_numeric_map_part(middle)
     suffix_space = numerator - prefix_weight
     if suffix_space <= 0:
         suffix = ()
     elif suffix_space < suffix_weight:
         weights = [suffix_space]
         suffix = sequencetools.split_sequence(
             suffix,
             weights,
             cyclic=False,
             overhang=False,
             )[0]
     numeric_map_part = prefix + middle + suffix
     return [durationtools.Duration(x) for x in numeric_map_part]
def split_sequence_extended_to_weights(sequence, weights, overhang=True):
    '''Split sequence extended to weights.

    ..  container:: example

        **Example 1.** Split sequence extended to weights with overhang:

        ::

            >>> sequencetools.split_sequence_extended_to_weights(
            ...     [1, 2, 3, 4, 5], [7, 7, 7], overhang=True)
            [[1, 2, 3, 1], [3, 4], [1, 1, 2, 3], [4, 5]]

    ..  container:: example

        **Example 2.** Split sequence extended to weights without overhang:

        ::

            >>> sequencetools.split_sequence_extended_to_weights(
            ...     [1, 2, 3, 4, 5], [7, 7, 7], overhang=False)
            [[1, 2, 3, 1], [3, 4], [1, 1, 2, 3]]

    Returns sequence of sequence objects.
    '''
    from abjad.tools import sequencetools

    n = int(math.ceil(float(mathtools.weight(weights)) / mathtools.weight(sequence)))

    sequence = sequencetools.repeat_sequence_n_times(sequence, n)

    return sequencetools.split_sequence_by_weights(sequence, weights, cyclic=False, overhang=overhang)
def repeat_sequence_to_weight_at_most(sequence, weight):
    '''Repeat `sequence` to `weight` at most:

    ::

        >>> sequencetools.repeat_sequence_to_weight_at_most((5, -5, -5), 23)
        (5, -5, -5, 5)

    Returns newly constructed `sequence` object.
    '''

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


    # 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)
Beispiel #5
0
 def _make_numeric_map_part(
     self,
     numerator,
     prefix,
     suffix,
     is_note_filled=True,
 ):
     prefix_weight = mathtools.weight(prefix)
     suffix_weight = mathtools.weight(suffix)
     middle = numerator - prefix_weight - suffix_weight
     if numerator < prefix_weight:
         weights = [numerator]
         prefix = sequencetools.split_sequence(prefix,
                                               weights,
                                               cyclic=False,
                                               overhang=False)[0]
     middle = self._make_middle_of_numeric_map_part(middle)
     suffix_space = numerator - prefix_weight
     if suffix_space <= 0:
         suffix = ()
     elif suffix_space < suffix_weight:
         weights = [suffix_space]
         suffix = sequencetools.split_sequence(
             suffix,
             weights,
             cyclic=False,
             overhang=False,
         )[0]
     numeric_map_part = prefix + middle + suffix
     return [durationtools.Duration(x) for x in numeric_map_part]
def _partition_sequence_cyclically_by_weights_at_least(
    sequence, 
    weights, 
    overhang=False,
    ):

    l_copy = sequence[:]
    result = []
    current_part = []
    target_weight_index = 0
    len_weights = len(weights)

    while l_copy:
        target_weight = weights[target_weight_index % len_weights]
        x = l_copy.pop(0)
        current_part.append(x)
        if target_weight <= mathtools.weight(current_part):
            result.append(current_part)
            current_part = []
            target_weight_index += 1

    assert not l_copy

    if current_part:
        if overhang:
            result.append(current_part)

    return result
def _partition_sequence_once_by_weights_at_least(
    sequence, 
    weights, 
    overhang=False,
    ):

    result = []
    current_part = []
    l_copy = sequence[:]

    for num_weight, target_weight in enumerate(weights):
        while True:
            try:
                x = l_copy.pop(0)
            except IndexError:
                if num_weight + 1 == len(weights):
                    if current_part:
                        result.append(current_part)
                        break
                raise PartitionError('too few elements in sequence.')
            current_part.append(x)
            if target_weight <= mathtools.weight(current_part):
                result.append(current_part)
                current_part = []
                break
    if l_copy:
        if overhang:
            result.append(l_copy)
    return result
def _partition_sequence_once_by_weights_at_least(
    sequence,
    weights,
    overhang=False,
):
    result = []
    current_part = []
    l_copy = sequence[:]
    for num_weight, target_weight in enumerate(weights):
        while True:
            try:
                x = l_copy.pop(0)
            except IndexError:
                if num_weight + 1 == len(weights):
                    if current_part:
                        result.append(current_part)
                        break
                message = 'too few elements in sequence.'
                raise Exception(message)
            current_part.append(x)
            if target_weight <= mathtools.weight(current_part):
                result.append(current_part)
                current_part = []
                break
    if l_copy:
        if overhang:
            result.append(l_copy)
    return result
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 #10
0
 def _burnish_each_division(class_, input_, divisions):
     left_classes = input_['left_classes']
     middle_classes = input_['middle_classes']
     right_classes = input_['right_classes']
     left_counts = input_['left_counts']
     left_counts = left_counts or datastructuretools.CyclicTuple([0])
     right_counts = input_['right_counts']
     right_counts = right_counts or datastructuretools.CyclicTuple([0])
     lefts_index, rights_index = 0, 0
     burnished_divisions = []
     for division_index, division in enumerate(divisions):
         left_count = left_counts[division_index]
         left = left_classes[lefts_index:lefts_index + left_count]
         lefts_index += left_count
         right_count = right_counts[division_index]
         right = right_classes[rights_index:rights_index + right_count]
         rights_index += right_count
         available_left_count = len(division)
         left_count = min([left_count, available_left_count])
         available_right_count = len(division) - left_count
         right_count = min([right_count, available_right_count])
         middle_count = len(division) - left_count - right_count
         left = left[:left_count]
         if middle_classes:
             middle = middle_count * [middle_classes[division_index]]
         else:
             middle = middle_count * [0]
         right = right[:right_count]
         left_part, middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
                 division,
                 [left_count, middle_count, right_count],
                 cyclic=False,
                 overhang=False,
                 )
         left_part = class_._burnish_division_part(left_part, left)
         middle_part = class_._burnish_division_part(middle_part, middle)
         right_part = class_._burnish_division_part(right_part, right)
         burnished_division = left_part + middle_part + right_part
         burnished_divisions.append(burnished_division)
     unburnished_weights = [mathtools.weight(x) for x in divisions]
     burnished_weights = [mathtools.weight(x) for x in burnished_divisions]
     assert burnished_weights == unburnished_weights
     return burnished_divisions
Beispiel #11
0
 def _burnish_each_division(class_, input_, divisions):
     left_classes = input_['left_classes']
     middle_classes = input_['middle_classes']
     right_classes = input_['right_classes']
     left_counts = input_['left_counts']
     left_counts = left_counts or datastructuretools.CyclicTuple([0])
     right_counts = input_['right_counts']
     right_counts = right_counts or datastructuretools.CyclicTuple([0])
     lefts_index, rights_index = 0, 0
     burnished_divisions = []
     for division_index, division in enumerate(divisions):
         left_count = left_counts[division_index]
         left = left_classes[lefts_index:lefts_index + left_count]
         lefts_index += left_count
         right_count = right_counts[division_index]
         right = right_classes[rights_index:rights_index + right_count]
         rights_index += right_count
         available_left_count = len(division)
         left_count = min([left_count, available_left_count])
         available_right_count = len(division) - left_count
         right_count = min([right_count, available_right_count])
         middle_count = len(division) - left_count - right_count
         left = left[:left_count]
         if middle_classes:
             middle = middle_count * [middle_classes[division_index]]
         else:
             middle = middle_count * [0]
         right = right[:right_count]
         left_part, middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
                 division,
                 [left_count, middle_count, right_count],
                 cyclic=False,
                 overhang=False,
                 )
         left_part = class_._burnish_division_part(left_part, left)
         middle_part = class_._burnish_division_part(middle_part, middle)
         right_part = class_._burnish_division_part(right_part, right)
         burnished_division = left_part + middle_part + right_part
         burnished_divisions.append(burnished_division)
     unburnished_weights = [mathtools.weight(x) for x in divisions]
     burnished_weights = [mathtools.weight(x) for x in burnished_divisions]
     assert burnished_weights == unburnished_weights
     return burnished_divisions
def _partition_sequence_once_by_weights_at_most(
    sequence, 
    weights, 
    overhang=False,
    ):

    l_copy = sequence[:]
    result = []
    current_part = []

    for target_weight in weights:
        while True:
            try:
                x = l_copy.pop(0)
            except IndexError:
                raise PartitionError('too few elements in sequence.')
            current_weight = mathtools.weight(current_part)
            candidate_weight = current_weight + mathtools.weight([x])
            if candidate_weight < target_weight:
                current_part.append(x)
            elif candidate_weight == target_weight:
                current_part.append(x)
                result.append(current_part)
                current_part = []
                break
            elif target_weight < candidate_weight:
                if current_part:
                    result.append(current_part)
                    current_part = []
                    l_copy.insert(0, x)
                    break
                else:
                    raise PartitionError('Elements in sequence too big.')
            else:
                raise ValueError('candidate and target weights must compare.')

    if overhang:
        left_over = current_part + l_copy
        if left_over:
            result.append(left_over)

    return result
def _partition_sequence_once_by_weights_at_most(
    sequence,
    weights,
    overhang=False,
):
    l_copy = sequence[:]
    result = []
    current_part = []
    for target_weight in weights:
        while True:
            try:
                x = l_copy.pop(0)
            except IndexError:
                message = 'too few elements in sequence.'
                raise Exception(message)
            current_weight = mathtools.weight(current_part)
            candidate_weight = current_weight + mathtools.weight([x])
            if candidate_weight < target_weight:
                current_part.append(x)
            elif candidate_weight == target_weight:
                current_part.append(x)
                result.append(current_part)
                current_part = []
                break
            elif target_weight < candidate_weight:
                if current_part:
                    result.append(current_part)
                    current_part = []
                    l_copy.insert(0, x)
                    break
                else:
                    message = 'elements in sequence too big.'
                    raise Exception(message)
            else:
                message = 'candidate and target weights must compare.'
                raise ValueError(message)
    if overhang:
        left_over = current_part + l_copy
        if left_over:
            result.append(left_over)
    return result
def _partition_sequence_cyclically_by_weights_at_most(
    sequence, 
    weights, 
    overhang=False,
    ):

    result = []
    current_part = []
    current_target_weight_index = 0
    current_target_weight = weights[current_target_weight_index]
    l_copy = sequence[:]

    while l_copy:
        current_target_weight = weights[current_target_weight_index % len(weights)]
        x = l_copy.pop(0)
        current_part_weight = mathtools.weight(current_part)
        candidate_part_weight = current_part_weight + mathtools.weight([x])
        if candidate_part_weight < current_target_weight:
            current_part.append(x)
        elif candidate_part_weight == current_target_weight:
            current_part.append(x)
            result.append(current_part)
            current_part = []
            current_target_weight_index += 1
        elif current_target_weight < candidate_part_weight:
            if current_part:
                l_copy.insert(0, x)
                result.append(current_part)
                current_part = []
                current_target_weight_index += 1
            else:
                raise PartitionError('elements in sequence too big.')
        else:
            raise ValueError('candidate and target rates must compare.')

    if current_part:
        if overhang:
            result.append(current_part)

    return result
def _partition_sequence_cyclically_by_weights_at_most(
    sequence,
    weights,
    overhang=False,
):
    result = []
    current_part = []
    current_target_weight_index = 0
    current_target_weight = weights[current_target_weight_index]
    l_copy = sequence[:]
    while l_copy:
        current_target_weight = \
            weights[current_target_weight_index % len(weights)]
        x = l_copy.pop(0)
        current_part_weight = mathtools.weight(current_part)
        candidate_part_weight = current_part_weight + mathtools.weight([x])
        if candidate_part_weight < current_target_weight:
            current_part.append(x)
        elif candidate_part_weight == current_target_weight:
            current_part.append(x)
            result.append(current_part)
            current_part = []
            current_target_weight_index += 1
        elif current_target_weight < candidate_part_weight:
            if current_part:
                l_copy.insert(0, x)
                result.append(current_part)
                current_part = []
                current_target_weight_index += 1
            else:
                message = 'elements in sequence too big.'
                raise Exception(message)
        else:
            message = 'candidate and target rates must compare.'
            raise ValueError(message)
    if current_part:
        if overhang:
            result.append(current_part)
    return result
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 _partition_sequence_cyclically_by_weights_at_least(
    sequence,
    weights,
    overhang=False,
):
    l_copy = sequence[:]
    result = []
    current_part = []
    target_weight_index = 0
    len_weights = len(weights)
    while l_copy:
        target_weight = weights[target_weight_index % len_weights]
        x = l_copy.pop(0)
        current_part.append(x)
        if target_weight <= mathtools.weight(current_part):
            result.append(current_part)
            current_part = []
            target_weight_index += 1
    assert not l_copy
    if current_part:
        if overhang:
            result.append(current_part)
    return result
Beispiel #18
0
def test_mathtools_weight_01():
    r'''Weight of nonempty sequence.
    '''

    assert mathtools.weight([-1, -2, 3, 4, 5]) == 15
Beispiel #19
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
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
def partition_sequence_by_backgrounded_weights(sequence, weights):
    r'''Partition `sequence` by backgrounded `weights`:

    ::

        >>> sequencetools.partition_sequence_by_backgrounded_weights(
        ...     [-5, -15, -10], [20, 10])
        [[-5, -15], [-10]]

    Further examples:

    ::

        >>> sequencetools.partition_sequence_by_backgrounded_weights(
        ...     [-5, -15, -10], [5, 5, 5, 5, 5, 5])
        [[-5], [-15], [], [], [-10], []]

    ::

        >>> sequencetools.partition_sequence_by_backgrounded_weights(
        ...     [-5, -15, -10], [1, 29])
        [[-5], [-15, -10]]

    ::

        >>> sequencetools.partition_sequence_by_backgrounded_weights(
        ...     [-5, -15, -10], [2, 28])
        [[-5], [-15, -10]]

    ::

        >>> sequencetools.partition_sequence_by_backgrounded_weights(
        ...     [-5, -15, -10], [1, 1, 1, 1, 1, 25])
        [[-5], [], [], [], [], [-15, -10]]

    The term `backgrounded` is a short-hand concocted specifically
    for this function; rely on the formal definition to understand
    the function actually does.

    Input constraint: the weight of `sequence` must equal the weight
    of `weights` exactly.

    The signs of the elements in `sequence` are ignored.

    Formal definition: partition `sequence` into `parts` such that
    (1.) the length of `parts` equals the length of `weights`;
    (2.) the elements in `sequence` appear in order in `parts`; and
    (3.) some final condition that is difficult to formalize.

    Notionally what's going on here is that the elements of `weights`
    are acting as a list of successive time intervals into which the
    elements of `sequence` are being fit in accordance with the start
    offset of each `sequence` element.

    The function models the grouping together of successive timespans
    according to which of an underlying sequence of time intervals
    it is in which each time span begins.

    Note that, for any input to this function, the flattened output
    of this function always equals `sequence` exactly.

    Note too that while `partition` is being used here in the sense of
    the other partitioning functions in the API, the distinguishing feature
    is this funciton is its ability to produce empty lists as output.

    Returns list of `sequence` objects.
    '''
    from abjad.tools import sequencetools

    assert all(0 < x for x in weights)
    assert mathtools.weight(sequence) == mathtools.weight(weights)

    start_offsets = \
        mathtools.cumulative_sums([abs(x) for x in sequence])[:-1]
    indicator = zip(start_offsets, sequence)

    result = []
    for interval_start, interval_stop in \
        mathtools.cumulative_sums_pairwise(weights):
        part = []
        for pair in indicator[:]:
            if interval_start <= pair[0] < interval_stop:
                part.append(pair[1])
                indicator.remove(pair)
        result.append(part)

    return result
Beispiel #22
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
Beispiel #23
0
def partition_sequence_by_counts(
    sequence,
    counts,
    cyclic=False,
    overhang=False,
    reversed_=False,
    ):
    r'''Partitions `sequence` by `counts`.

    ..  container:: example

        **Example 1.** Partitions sequence once by counts without overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [[0, 1, 2]]

        **Example 2.** Partitions sequence once by counts without overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6]]

    ..  container:: example

        **Example 3.** Partitions sequence cyclically by counts without
        overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            [[0, 1, 2], [3, 4, 5], [6, 7, 8]]

        **Example 4.** Partitions sequence cyclically by counts without
        overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13]]

    ..  container:: example

        **Example 5.** Partitions sequence once by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]]

        **Example 6.** Partitions sequence once by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]]

    ..  container:: example

        **Example 7.** Partitions sequence cyclically by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

        **Example 8.** Partitions sequence cyclically by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13], [14, 15]]

    ..  container:: example

        **Example 9.** Partitions sequence once by counts and asserts
        that sequence partitions exactly (with no overhang):

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [2, 3, 5],
            ...     cyclic=False,
            ...     overhang=Exact,
            ...     )
            [[0, 1], [2, 3, 4], [5, 6, 7, 8, 9]]

        **Example 10.** Partitions sequence cyclically by counts and asserts
        that sequence partitions exactly (with no overhang):

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [2],
            ...     cyclic=True,
            ...     overhang=Exact,
            ...     )
            [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]

    ..  container:: example

        **Example 11.** Partitions list:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]]

        **Example 12.** Partitions tuple:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     tuple(range(10)),
            ...     [3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [(0, 1, 2), (3, 4, 5, 6, 7, 8, 9)]

        **Example 13.** Partitions string:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     'some text',
            ...     [3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            ['som', 'e text']

    ..  container:: example

        **Example 14.** Reverses cyclic partition with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            ...     [3],
            ...     cyclic=True,
            ...     overhang=True,
            ...     reversed_=True,
            ...     )
            [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]]

        **Example 15.** Reverses cyclic partition without overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            ...     [3],
            ...     cyclic=True,
            ...     overhang=False,
            ...     reversed_=True,
            ...     )
            [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

        **Example 16.** Reverses acyclic partition with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            ...     [3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     reversed_=True,
            ...     )
            [[0, 1, 2, 3, 4, 5, 6], [7, 8, 9]]

        **Example 17.** Reverses acyclic partition without overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            ...     [3],
            ...     cyclic=False,
            ...     overhang=False,
            ...     reversed_=True,
            ...     )
            [[7, 8, 9]]

    Returns list of objects with type equal to that of `sequence`.
    '''
    from abjad.tools import sequencetools

    if not isinstance(sequence, collections.Sequence):
        message = 'must be sequence: {!r}.'
        message = message.format(sequence)
        raise TypeError(message)
    if not isinstance(counts, collections.Iterable):
        message = 'must be iterable: {!r}.'
        message = message.format(counts)
        raise TypeError(message)

    sequence_type = type(sequence)
    if reversed_:
        sequence = reversed(sequence)    
        sequence = sequence_type(sequence)

    if overhang == Exact:
        result_with_overhang = partition_sequence_by_counts(
            sequence,
            counts,
            cyclic=cyclic,
            overhang=True,
            )
        result_without_overhang = partition_sequence_by_counts(
            sequence,
            counts,
            cyclic=cyclic,
            overhang=False,
            )
        if result_with_overhang == result_without_overhang:
            return result_without_overhang
        else:
            message = 'sequence does not partition exactly.'
            raise Exception(message)

    result = []
    if cyclic:
        if overhang:
            counts = sequencetools.repeat_sequence_to_weight(
                counts,
                len(sequence),
                )
        else:
            counts = sequencetools.repeat_sequence_to_weight(
                counts, 
                len(sequence), 
                allow_total=Less,
                )
    elif overhang:
        weight_counts = mathtools.weight(counts)
        len_sequence = len(sequence)
        if weight_counts < len_sequence:
            counts = list(counts)
            counts.append(len(sequence) - weight_counts)
    for start, stop in mathtools.cumulative_sums_pairwise(counts):
        part = sequence[start:stop]
        result.append(part)

    if reversed_:
        result_ = []
        for part in reversed(result):
            part_type = type(part)
            part = reversed(part)
            part = part_type(part)
            result_.append(part)
        result = result_

    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 #25
0
 def _burnish_first_and_last_division_parts(self, divisions, quintuplet):
     lefts, middles, rights, left_lengths, right_lengths = quintuplet
     burnished_divisions = []
     left_length = left_lengths[0]
     left = lefts[:left_length]
     right_length = right_lengths[0]
     right = rights[:right_length]
     if len(divisions) == 1:
         available_left_length = len(divisions[0])
         left_length = min([left_length, available_left_length])
         available_right_length = len(divisions[0]) - left_length
         right_length = min([right_length, available_right_length])
         middle_length = len(divisions[0]) - left_length - right_length
         left = left[:left_length]
         middle = middle_length * [middles[0]]
         right = right[:right_length]
         left_part, middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
             divisions[0],
             [left_length, middle_length, right_length], 
             cyclic=False,
             overhang=False)
         left_part = self._burnish_division_part(left_part, left)
         middle_part = self._burnish_division_part(middle_part, middle)
         right_part = self._burnish_division_part(right_part, right)
         burnished_division = left_part + middle_part + right_part
         burnished_divisions.append(burnished_division)
     else:
         # first division
         available_left_length = len(divisions[0])
         left_length = min([left_length, available_left_length])
         middle_length = len(divisions[0]) - left_length
         left = left[:left_length]
         middle = middle_length * [middles[0]]
         left_part, middle_part = \
             sequencetools.partition_sequence_by_counts(
             divisions[0], 
             [left_length, middle_length],
             cyclic=False,
             overhang=False)
         left_part = self._burnish_division_part(left_part, left)
         middle_part = self._burnish_division_part(middle_part, middle)
         burnished_division = left_part + middle_part
         burnished_divisions.append(burnished_division)
         # middle divisions
         for division in divisions[1:-1]:
             middle_part = division
             middle = len(division) * [middles[0]]
             middle_part = self._burnish_division_part(middle_part, middle)
             burnished_division = middle_part
             burnished_divisions.append(burnished_division)
         # last division:
         available_right_length = len(divisions[-1])
         right_length = min([right_length, available_right_length])
         middle_length = len(divisions[-1]) - right_length
         right = right[:right_length]
         middle = middle_length * [middles[0]]
         middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
             divisions[-1], 
             [middle_length, right_length], 
             cyclic=False,
             overhang=False)
         middle_part = self._burnish_division_part(middle_part, middle)
         right_part = self._burnish_division_part(right_part, right)
         burnished_division = middle_part + right_part
         burnished_divisions.append(burnished_division)
     unburnished_weights = [mathtools.weight(x) for x in divisions]
     burnished_weights = [mathtools.weight(x) for x in burnished_divisions]
     assert burnished_weights == unburnished_weights
     return burnished_divisions
Beispiel #26
0
def partition_sequence_by_counts(
    sequence,
    counts,
    cyclic=False,
    overhang=False,
    copy_elements=False,
):
    r'''Partitions sequence by counts.

    ..  container:: example

        **Example 1a.** Partition sequence once by counts without overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [[0, 1, 2]]

    ..  container:: example

        **Example 1b.** Partition sequence once by counts without overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6]]

    ..  container:: example

        **Example 2a.** Partition sequence cyclically by counts without
        overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            [[0, 1, 2], [3, 4, 5], [6, 7, 8]]

    ..  container:: example

        **Example 2b.** Partition sequence cyclically by counts without
        overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13]]

    ..  container:: example

        **Example 3a.** Partition sequence once by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]]

    ..  container:: example

        **Example 3b.** Partition sequence once by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]]

    ..  container:: example

        **Example 4a.** Partition sequence cyclically by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

    ..  container:: example

        **Example 4b.** Partition sequence cyclically by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13], [14, 15]]

    Returns list of sequence objects.
    '''
    from abjad.tools import sequencetools

    if not isinstance(counts, (tuple, list)):
        message = 'must be list or tuple: {!r}.'
        message = message.format(counts)
        raise TypeError(message)

    result = []

    if cyclic:
        if overhang:
            counts = sequencetools.repeat_sequence_to_weight(
                counts,
                len(sequence),
            )
        else:
            counts = sequencetools.repeat_sequence_to_weight(
                counts,
                len(sequence),
                allow_total=Less,
            )
    elif overhang:
        weight_counts = mathtools.weight(counts)
        len_sequence = len(sequence)
        if weight_counts < len_sequence:
            counts = list(counts)
            counts.append(len(sequence) - weight_counts)

    for start, stop in mathtools.cumulative_sums_pairwise(counts):
        result.append(type(sequence)(sequence[start:stop]))

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

    ..  container:: example

        Example sequence:

        ::

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

    ..  container:: example

        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

        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

        Truncates sequence to zero weight:

        ::

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

    ..  container:: example

        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, list):
        raise TypeError

    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 = type(sequence)(result)
    return result
def partition_sequence_by_counts(
    sequence,
    counts,
    cyclic=False,
    overhang=False,
    copy_elements=False,
    ):
    r'''Partitions sequence by counts.

    ..  container:: example

        **Example 1a.** Partition sequence once by counts without overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [[0, 1, 2]]

    ..  container:: example

        **Example 1b.** Partition sequence once by counts without overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=False,
            ...     overhang=False,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6]]

    ..  container:: example

        **Example 2a.** Partition sequence cyclically by counts without
        overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            [[0, 1, 2], [3, 4, 5], [6, 7, 8]]

    ..  container:: example

        **Example 2b.** Partition sequence cyclically by counts without
        overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=True,
            ...     overhang=False,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13]]

    ..  container:: example

        **Example 3a.** Partition sequence once by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]]

    ..  container:: example

        **Example 3b.** Partition sequence once by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=False,
            ...     overhang=True,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]]

    ..  container:: example

        **Example 4a.** Partition sequence cyclically by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(10)),
            ...     [3],
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

    ..  container:: example

        **Example 4b.** Partition sequence cyclically by counts with overhang:

        ::

            >>> sequencetools.partition_sequence_by_counts(
            ...     list(range(16)),
            ...     [4, 3],
            ...     cyclic=True,
            ...     overhang=True,
            ...     )
            [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13], [14, 15]]

    Returns list of sequence objects.
    '''
    from abjad.tools import sequencetools

    result = []

    if cyclic:
        if overhang:
            counts = sequencetools.repeat_sequence_to_weight(
                counts, len(sequence))
        else:
            counts = sequencetools.repeat_sequence_to_weight(
                counts, len(sequence), allow_total=Less)
    elif overhang:
        weight_counts = mathtools.weight(counts)
        len_sequence = len(sequence)
        if weight_counts < len_sequence:
            counts = list(counts)
            counts.append(len(sequence) - weight_counts)

    for start, stop in mathtools.cumulative_sums_pairwise(counts):
        result.append(type(sequence)(sequence[start:stop]))

    return result
Beispiel #29
0
 def _burnish_outer_divisions(class_, input_, divisions):
     for list_ in divisions:
         assert all(isinstance(_, int) for _ in list_), repr(list_)
     left_classes = input_['left_classes']
     middle_classes = input_['middle_classes']
     right_classes = input_['right_classes']
     left_counts = input_['left_counts']
     left_counts = left_counts or datastructuretools.CyclicTuple([0])
     right_counts = input_['right_counts']
     right_counts = right_counts or datastructuretools.CyclicTuple([0])
     burnished_divisions = []
     left_count = 0
     if left_counts:
         left_count = left_counts[0]
     left = left_classes[:left_count]
     right_count = 0
     if right_counts:
         right_count = right_counts[0]
     right = right_classes[:right_count]
     if len(divisions) == 1:
         available_left_count = len(divisions[0])
         left_count = min([left_count, available_left_count])
         available_right_count = len(divisions[0]) - left_count
         right_count = min([right_count, available_right_count])
         middle_count = len(divisions[0]) - left_count - right_count
         left = left[:left_count]
         if not middle_classes:
             middle_classes = [1]
         middle = [middle_classes[0]]
         middle = middle_count * middle
         right = right[:right_count]
         left_part, middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
                 divisions[0],
                 [left_count, middle_count, right_count],
                 cyclic=False,
                 overhang=Exact,
                 )
         left_part = class_._burnish_division_part(left_part, left)
         middle_part = class_._burnish_division_part(middle_part, middle)
         right_part = class_._burnish_division_part(right_part, right)
         burnished_division = left_part + middle_part + right_part
         burnished_divisions.append(burnished_division)
     else:
         # first division
         available_left_count = len(divisions[0])
         left_count = min([left_count, available_left_count])
         middle_count = len(divisions[0]) - left_count
         left = left[:left_count]
         if not middle_classes:
             middle_classes = [1]
         middle = [middle_classes[0]]
         middle = middle_count * middle
         left_part, middle_part = \
             sequencetools.partition_sequence_by_counts(
                 divisions[0],
                 [left_count, middle_count],
                 cyclic=False,
                 overhang=Exact,
                 )
         left_part = class_._burnish_division_part(left_part, left)
         middle_part = class_._burnish_division_part(middle_part, middle)
         burnished_division = left_part + middle_part
         burnished_divisions.append(burnished_division)
         # middle divisions
         for division in divisions[1:-1]:
             middle_part = division
             middle = len(division) * [middle_classes[0]]
             middle_part = class_._burnish_division_part(
                 middle_part,
                 middle,
                 )
             burnished_division = middle_part
             burnished_divisions.append(burnished_division)
         # last division:
         available_right_count = len(divisions[-1])
         right_count = min([right_count, available_right_count])
         middle_count = len(divisions[-1]) - right_count
         right = right[:right_count]
         middle = middle_count * [middle_classes[0]]
         middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
                 divisions[-1],
                 [middle_count, right_count],
                 cyclic=False,
                 overhang=Exact,
                 )
         middle_part = class_._burnish_division_part(middle_part, middle)
         right_part = class_._burnish_division_part(right_part, right)
         burnished_division = middle_part + right_part
         burnished_divisions.append(burnished_division)
     unburnished_weights = [mathtools.weight(x) for x in divisions]
     burnished_weights = [mathtools.weight(x) for x in burnished_divisions]
     #assert burnished_weights == unburnished_weights
     # TODO: make the following work on Python 3:
     #assert tuple(burnished_weights) == tuple(unburnished_weights)
     return burnished_divisions
Beispiel #30
0
def test_mathtools_weight_02():
    r'''Weight of empty sequence.
    '''

    assert mathtools.weight([]) == 0
Beispiel #31
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
Beispiel #32
0
def partition_sequence_by_ratio_of_weights(sequence, weights):
    '''Partitions `sequence` by ratio of `weights`.

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1] * 10, [1, 1, 1])
        [[1, 1, 1], [1, 1, 1, 1], [1, 1, 1]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1] * 10, [1, 1, 1, 1])
        [[1, 1, 1], [1, 1], [1, 1, 1], [1, 1]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1] * 10, [2, 2, 3])
        [[1, 1, 1], [1, 1, 1], [1, 1, 1, 1]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1] * 10, [3, 2, 2])
        [[1, 1, 1, 1], [1, 1, 1], [1, 1, 1]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2], [1, 1])
        [[1, 1, 1, 1, 1, 1, 2, 2], [2, 2, 2, 2]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2], [1, 1, 1])
        [[1, 1, 1, 1, 1, 1], [2, 2, 2], [2, 2, 2]]


    Weights of parts of returned list equal `weights_ratio` proportions
    with some rounding magic.

    Returns list of lists.
    '''
    from abjad.tools import sequencetools

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

    list_weight = mathtools.weight(sequence)
    weights_parts = mathtools.partition_integer_by_ratio(list_weight, weights)
    cumulative_weights = mathtools.cumulative_sums(weights_parts, start=None)

    result = []
    sublist = []
    result.append(sublist)
    current_cumulative_weight = cumulative_weights.pop(0)
    for n in sequence:
        if not isinstance(n, (int, float, fractions.Fraction)):
            message = 'must be number: {!r}.'
            message = message.format(n)
            raise TypeError(message)
        sublist.append(n)
        while current_cumulative_weight <= \
            mathtools.weight(sequencetools.flatten_sequence(result)):
            try:
                current_cumulative_weight = cumulative_weights.pop(0)
                sublist = []
                result.append(sublist)
            except IndexError:
                break

    return result
Beispiel #33
0
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 partition_sequence_by_ratio_of_weights(sequence, weights):
    '''Partitions `sequence` by ratio of `weights`.

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1] * 10, [1, 1, 1])
        [[1, 1, 1], [1, 1, 1, 1], [1, 1, 1]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1] * 10, [1, 1, 1, 1])
        [[1, 1, 1], [1, 1], [1, 1, 1], [1, 1]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1] * 10, [2, 2, 3])
        [[1, 1, 1], [1, 1, 1], [1, 1, 1, 1]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1] * 10, [3, 2, 2])
        [[1, 1, 1, 1], [1, 1, 1], [1, 1, 1]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2], [1, 1])
        [[1, 1, 1, 1, 1, 1, 2, 2], [2, 2, 2, 2]]

    ::

        >>> sequencetools.partition_sequence_by_ratio_of_weights(
        ...     [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2], [1, 1, 1])
        [[1, 1, 1, 1, 1, 1], [2, 2, 2], [2, 2, 2]]


    Weights of parts of returned list equal `weights_ratio` proportions
    with some rounding magic.

    Returns list of lists.
    '''
    from abjad.tools import sequencetools

    list_weight = mathtools.weight(sequence)
    weights_parts = mathtools.partition_integer_by_ratio(list_weight, weights)
    cumulative_weights = mathtools.cumulative_sums(weights_parts, start=None)

    result = []
    sublist = []
    result.append(sublist)
    current_cumulative_weight = cumulative_weights.pop(0)
    for n in sequence:
        if not isinstance(n, (int, long, float, fractions.Fraction)):
            message = 'must be number: {!r}.'
            message = message.format(n)
            raise TypeError(message)
        sublist.append(n)
        while current_cumulative_weight <= \
            mathtools.weight(sequencetools.flatten_sequence(result)):
            try:
                current_cumulative_weight = cumulative_weights.pop(0)
                sublist = []
                result.append(sublist)
            except IndexError:
                break

    return result
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 #36
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 #37
0
 def _burnish_outer_divisions(class_, input_, divisions):
     for list_ in divisions:
         assert all(isinstance(_, int) for _ in list_), repr(list_)
     left_classes = input_['left_classes']
     middle_classes = input_['middle_classes']
     right_classes = input_['right_classes']
     left_counts = input_['left_counts']
     left_counts = left_counts or datastructuretools.CyclicTuple([0])
     right_counts = input_['right_counts']
     right_counts = right_counts or datastructuretools.CyclicTuple([0])
     burnished_divisions = []
     left_count = 0
     if left_counts:
         left_count = left_counts[0]
     left = left_classes[:left_count]
     right_count = 0
     if right_counts:
         right_count = right_counts[0]
     right = right_classes[:right_count]
     if len(divisions) == 1:
         available_left_count = len(divisions[0])
         left_count = min([left_count, available_left_count])
         available_right_count = len(divisions[0]) - left_count
         right_count = min([right_count, available_right_count])
         middle_count = len(divisions[0]) - left_count - right_count
         left = left[:left_count]
         if not middle_classes:
             middle_classes = [1]
         middle = [middle_classes[0]]
         middle = middle_count * middle
         right = right[:right_count]
         left_part, middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
                 divisions[0],
                 [left_count, middle_count, right_count],
                 cyclic=False,
                 overhang=Exact,
                 )
         left_part = class_._burnish_division_part(left_part, left)
         middle_part = class_._burnish_division_part(middle_part, middle)
         right_part = class_._burnish_division_part(right_part, right)
         burnished_division = left_part + middle_part + right_part
         burnished_divisions.append(burnished_division)
     else:
         # first division
         available_left_count = len(divisions[0])
         left_count = min([left_count, available_left_count])
         middle_count = len(divisions[0]) - left_count
         left = left[:left_count]
         if not middle_classes:
             middle_classes = [1]
         middle = [middle_classes[0]]
         middle = middle_count * middle
         left_part, middle_part = \
             sequencetools.partition_sequence_by_counts(
                 divisions[0],
                 [left_count, middle_count],
                 cyclic=False,
                 overhang=Exact,
                 )
         left_part = class_._burnish_division_part(left_part, left)
         middle_part = class_._burnish_division_part(middle_part, middle)
         burnished_division = left_part + middle_part
         burnished_divisions.append(burnished_division)
         # middle divisions
         for division in divisions[1:-1]:
             middle_part = division
             middle = len(division) * [middle_classes[0]]
             middle_part = class_._burnish_division_part(
                 middle_part,
                 middle,
                 )
             burnished_division = middle_part
             burnished_divisions.append(burnished_division)
         # last division:
         available_right_count = len(divisions[-1])
         right_count = min([right_count, available_right_count])
         middle_count = len(divisions[-1]) - right_count
         right = right[:right_count]
         middle = middle_count * [middle_classes[0]]
         middle_part, right_part = \
             sequencetools.partition_sequence_by_counts(
                 divisions[-1],
                 [middle_count, right_count],
                 cyclic=False,
                 overhang=Exact,
                 )
         middle_part = class_._burnish_division_part(middle_part, middle)
         right_part = class_._burnish_division_part(right_part, right)
         burnished_division = middle_part + right_part
         burnished_divisions.append(burnished_division)
     unburnished_weights = [mathtools.weight(x) for x in divisions]
     burnished_weights = [mathtools.weight(x) for x in burnished_divisions]
     #assert burnished_weights == unburnished_weights
     # TODO: make the following work on Python 3:
     #assert tuple(burnished_weights) == tuple(unburnished_weights)
     return burnished_divisions
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 #39
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