def __init__(self, token): from abjad.tools import pitchtools if isinstance(token, numbers.Number): sign = mathtools.sign(token) abs_token = abs(token) if abs_token % 12 == 0 and 12 <= abs_token: number = 12 else: number = abs_token % 12 number *= sign elif isinstance(token, pitchtools.Interval): number = token.semitones sign = mathtools.sign(number) abs_number = abs(number) if abs_number % 12 == 0 and 12 <= abs_number: number = 12 else: number = abs_number % 12 number *= sign elif isinstance(token, pitchtools.IntervalClass): number = token.number sign = mathtools.sign(number) abs_number = abs(number) if abs_number % 12 == 0 and 12 <= abs_number: number = 12 else: number = abs_number % 12 number *= sign else: raise ValueError('must be number, interval or interval-class.') self._number = number
def _is_neighbor_note(note): from abjad.tools import scoretools from abjad.tools import tonalanalysistools if not isinstance(note, scoretools.Note): message = 'must be note: {!r}.' message = message.format(note) raise TypeError(message) previous_note = note._get_in_my_logical_voice( -1, prototype=scoretools.Note) next_note = note._get_in_my_logical_voice( 1, prototype=scoretools.Note) if previous_note is None: return False if next_note is None: return False notes = [previous_note, note, next_note] selection = tonalanalysistools.select(notes) preceding_interval = note.written_pitch - previous_note.written_pitch preceding_interval_direction = \ mathtools.sign(preceding_interval.direction_number) following_interval = next_note.written_pitch - note.written_pitch following_interval_direction = \ mathtools.sign(following_interval.direction_number) if selection.are_stepwise_notes(): if preceding_interval_direction != following_interval_direction: return True return False
def __init__(self, token=None): from abjad.tools import pitchtools if isinstance(token, numbers.Number): sign = mathtools.sign(token) abs_token = abs(token) if abs_token % 12 == 0 and 12 <= abs_token: number = 12 else: number = abs_token % 12 number *= sign elif isinstance(token, pitchtools.Interval): number = token.semitones sign = mathtools.sign(number) abs_number = abs(number) if abs_number % 12 == 0 and 12 <= abs_number: number = 12 else: number = abs_number % 12 number *= sign elif isinstance(token, pitchtools.IntervalClass): number = token.number sign = mathtools.sign(number) abs_number = abs(number) if abs_number % 12 == 0 and 12 <= abs_number: number = 12 else: number = abs_number % 12 number *= sign elif token is None: number = 0 else: message = 'can not initialize {}: {!r}.' message = message.format(type(self).__name__, token) raise ValueError(message) self._number = number
def test_mathtools_sign_02(): r'''Test rational sign. ''' assert mathtools.sign(Duration(-3, 2)) == -1 assert mathtools.sign(Duration(0)) == 0 assert mathtools.sign(Duration(3, 2)) == 1
def test_mathtools_sign_01(): r'''Test integer sign. ''' assert mathtools.sign(-2) == -1 assert mathtools.sign(-1.5) == -1 assert mathtools.sign(0) == 0 assert mathtools.sign(1.5) == 1 assert mathtools.sign(2) == 1
def partition_integer_into_units(n): r'''Partitions positive integer into units: :: >>> mathtools.partition_integer_into_units(6) [1, 1, 1, 1, 1, 1] Partitions negative integer into units: :: >>> mathtools.partition_integer_into_units(-5) [-1, -1, -1, -1, -1] Partitions ``0`` into units: :: >>> mathtools.partition_integer_into_units(0) [] Returns list of zero or more parts with absolute value equal to ``1``. ''' from abjad.tools import mathtools result = abs(n) * [mathtools.sign(n) * 1] return result
def map_sequence_elements_to_numbered_sublists(sequence): """Map `sequence` elements to numbered sublists: :: >>> sequencetools.map_sequence_elements_to_numbered_sublists([1, 2, -3, -4, 5]) [[1], [2, 3], [-4, -5, -6], [-7, -8, -9, -10], [11, 12, 13, 14, 15]] :: >>> sequencetools.map_sequence_elements_to_numbered_sublists([1, 0, -3, -4, 5]) [[1], [], [-2, -3, -4], [-5, -6, -7, -8], [9, 10, 11, 12, 13]] Note that numbering starts at ``1``. Returns newly constructed list of lists. """ if not isinstance(sequence, list): raise TypeError if not all(isinstance(x, (int, long)) for x in sequence): raise ValueError result = [] cur = 1 for length in sequence: abs_length = abs(length) part = range(cur, cur + abs_length) part = [mathtools.sign(length) * x for x in part] result.append(part) cur += abs_length return result
def __new__(cls, *args): from abjad.tools import mathtools from abjad.tools import sequencetools if len(args) == 1 and hasattr(args[0], 'numerator') and \ hasattr(args[0], 'denominator'): numerator = args[0].numerator denominator = args[0].denominator elif len(args) == 1 and isinstance(args[0], int): numerator = args[0] denominator = 1 elif len(args) == 1 and mathtools.is_integer_singleton(args[0]): numerator = args[0][0] denominator = 1 elif len(args) == 1 and mathtools.is_integer_pair(args[0]): numerator, denominator = args[0] elif len(args) == 1 and isinstance(args[0], str): numerator, denominator = cls._parse_input_string(args[0]) elif mathtools.is_integer_pair(args): numerator = args[0] denominator = args[1] elif len(args) == 0: numerator = 0 denominator = 1 else: message = 'can not initialize {}: {!r}.' message = message.format(cls.__name__, args) raise ValueError(message) numerator *= mathtools.sign(denominator) denominator = abs(denominator) self = fractions.Fraction.__new__(cls, numerator, denominator) self._numerator = numerator self._denominator = denominator return self
def __init__(self, item=None): from abjad.tools import pitchtools if isinstance(item, numbers.Number): sign = mathtools.sign(item) abs_token = abs(item) if abs_token % 12 == 0 and 12 <= abs_token: number = 12 else: number = abs_token % 12 number *= sign elif isinstance(item, pitchtools.Interval): number = item.semitones sign = mathtools.sign(number) abs_number = abs(number) if abs_number % 12 == 0 and 12 <= abs_number: number = 12 else: number = abs_number % 12 number *= sign elif isinstance(item, pitchtools.IntervalClass): number = item.number sign = mathtools.sign(number) abs_number = abs(number) if abs_number % 12 == 0 and 12 <= abs_number: number = 12 else: number = abs_number % 12 number *= sign elif item is None: number = 0 elif isinstance(item, str): number = float(item) if mathtools.is_integer_equivalent_expr(number): number = int(number) sign = mathtools.sign(number) abs_token = abs(number) if abs_token % 12 == 0 and 12 <= abs_token: number = 12 else: number = abs_token % 12 number *= sign else: message = 'can not initialize {}: {!r}.' message = message.format(type(self).__name__, item) raise ValueError(message) self._number = number
def __init__(self, *args): from abjad.tools import pitchtools from abjad.tools import sequencetools if len(args) == 1 and \ isinstance(args[0], (pitchtools.NamedInterval, pitchtools.NamedIntervalClass)): quality_string = args[0]._quality_string number = args[0].number elif len(args) == 1 and isinstance(args[0], str): match = \ pitchtools.Interval._interval_name_abbreviation_regex.match( args[0]) if match is None: message = '{!r} does not have the form of an abbreviation.' message = message.format(args[0]) raise ValueError(message) direction_string, quality_abbreviation, number_string = \ match.groups() quality_string = \ NamedIntervalClass._quality_abbreviation_to_quality_string[ quality_abbreviation] number = int(direction_string + number_string) elif len(args) == 1 and sequencetools.is_pair(args[0]): quality_string, number = args[0] elif len(args) == 2: quality_string, number = args elif len(args) == 0: quality_string = 'perfect' number = 1 else: message = 'bad input: {!r}.' message = message.format(args) raise TypeError(message) if quality_string not in \ NamedIntervalClass._acceptable_quality_strings: message = 'not acceptable quality string: {!r}.' message = message.format(quality_string) raise ValueError(message) if not isinstance(number, int): message = 'must be integer: {!r}.' message = message.format(number) raise TypeError(message) if number == 0: message = 'must be nonzero: {!r}.' message = message.format(number) raise ValueError(number) sign = mathtools.sign(number) abs_number = abs(number) if abs_number % 7 == 1 and 8 <= abs_number: number = 8 else: number = abs_number % 7 if number == 0: number = 7 if not number == 1: number *= sign self._number = number self._quality_string = quality_string
def truncate_sequence_to_weight(sequence, weight): '''Truncate `sequence` to `weight`: :: >>> l = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] >>> for x in range(10): ... print x, sequencetools.truncate_sequence_to_weight(l, x) ... 0 [] 1 [-1] 2 [-1, 1] 3 [-1, 2] 4 [-1, 2, -1] 5 [-1, 2, -2] 6 [-1, 2, -3] 7 [-1, 2, -3, 1] 8 [-1, 2, -3, 2] 9 [-1, 2, -3, 3] Returns empty list when `weight` is ``0``: :: >>> sequencetools.truncate_sequence_to_weight([1, 2, 3, 4, 5], 0) [] Raise type error when `sequence` is not a list. Raise value error on negative `weight`. Returns new list. ''' if not isinstance(sequence, list): raise TypeError if weight < 0: raise ValueError result = [] if weight == 0: return result accumulation = 0 for x in sequence: accumulation += abs(x) if accumulation < weight: result.append(x) else: sign = mathtools.sign(x) trimmed_part = weight - mathtools.weight(result) trimmed_part *= sign result.append(trimmed_part) break return result
def direction_number(self): r'''Direction sign of numbered interval. :: >>> pitchtools.NumberedInterval(-14).direction_number -1 Returns integer. ''' return mathtools.sign(self.number)
def __init__(self, *args): from abjad.tools import pitchtools if len(args) == 1: if isinstance(args[0], type(self)): quality_string = args[0].quality_string number = args[0].number elif isinstance(args[0], str): match = \ pitchtools.Interval._interval_name_abbreviation_regex.match( args[0]) if match is None: message = '{!r} does not have the form of a mdi abbreviation.' message = message.format(args[0]) raise ValueError(message) direction_string, quality_abbreviation, number_string = \ match.groups() quality_string = self._quality_abbreviation_to_quality_string[ quality_abbreviation] number = int(direction_string + number_string) elif isinstance(args[0], pitchtools.NamedIntervalClass): quality_string = args[0].quality_string number = args[0].number elif isinstance(args[0], ( int, float, int, pitchtools.NumberedInterval, pitchtools.NumberedIntervalClass, )): number = int(args[0]) sign = mathtools.sign(number) octaves, semitones = divmod(abs(number), 12) quality_string, number = \ self._semitones_to_quality_string_and_number[semitones] number += abs(octaves) * 7 if sign == -1: number *= -1 else: message = 'can not initialize {}: {!r}' message = message.format(type(self).__init__, args) raise ValueError(message) elif len(args) == 2: quality_string, number = args elif len(args) == 0: quality_string = 'perfect' number = 1 else: message = 'can not initialize {}: {!r}' message = message.format(type(self).__init__, args) raise ValueError(message) self._quality_string = quality_string self._number = number
def cumulative_signed_weights(sequence): r'''Cumulative signed weights of `sequence`. :: >>> l = [1, -2, -3, 4, -5, -6, 7, -8, -9, 10] >>> mathtools.cumulative_signed_weights(l) [1, -3, -6, 10, -15, -21, 28, -36, -45, 55] Raises type error when `sequence` is not a list. Use ``mathtools.cumulative_sums([abs(x) for x in l])`` for cumulative (unsigned) weights Returns list. ''' from abjad.tools import mathtools if not isinstance(sequence, list): raise TypeError result = [] for x in sequence: try: next_element = abs(previous) + abs(x) previous_sign = mathtools.sign(previous) except NameError: next_element = abs(x) previous_sign = 0 sign_x = mathtools.sign(x) if sign_x == -1: next_element *= sign_x elif sign_x == 0: next_element *= previous_sign result.append(next_element) previous = next_element return result
def direction_number(self): r'''Direction number of named interval. :: >>> interval.direction_number 1 Returns ``-1``, ``0`` or ``1``. ''' if self.quality_string == 'perfect' and abs(self.number) == 1: return 0 else: return mathtools.sign(self.number)
def __init__(self, *args): from abjad.tools import pitchtools from abjad.tools import sequencetools if len(args) == 1 and\ isinstance(args[0], (pitchtools.NamedInterval, pitchtools.NamedIntervalClass)): quality_string = args[0]._quality_string number = args[0].number elif len(args) == 1 and isinstance(args[0], str): match = pitchtools.Interval._interval_name_abbreviation_regex.match( args[0]) if match is None: raise ValueError( '{!r} does not have the form of an abbreviation.'.format( args[0])) direction_string, quality_abbreviation, number_string = \ match.groups() quality_string = \ NamedIntervalClass._quality_abbreviation_to_quality_string[ quality_abbreviation] number = int(direction_string + number_string) elif len(args) == 1 and sequencetools.is_pair(args[0]): quality_string, number = args[0] elif len(args) == 2: quality_string, number = args else: raise TypeError('what type of instance is this?, {!r}'.format( args)) if quality_string not in \ NamedIntervalClass._acceptable_quality_strings: raise ValueError('not acceptable quality string.') if not isinstance(number, int): raise TypeError('must be integer.') if number == 0: raise ValueError('must be nonzero.') sign = mathtools.sign(number) abs_number = abs(number) if abs_number % 7 == 1 and 8 <= abs_number: number = 8 else: number = abs_number % 7 if number == 0: number = 7 if not number == 1: number *= sign self._number = number self._quality_string = quality_string
def repeat_sequence_to_weight_exactly(sequence, weight): '''Repeat `sequence` to `weight` exactly: :: >>> sequencetools.repeat_sequence_to_weight_exactly((5, -5, -5), 23) (5, -5, -5, 5, -3) Returns newly constructed `sequence` object. ''' # check input assert isinstance(weight, numbers.Number) assert 0 <= weight # repeat sequence and find overage sequence_weight = mathtools.weight(sequence) complete_repetitions = int(math.ceil(float(weight) / float(sequence_weight))) result = list(sequence) result = complete_repetitions * result overage = complete_repetitions * sequence_weight - weight # remove overage from result for element in reversed(result): if 0 < overage: element_weight = abs(element) candidate_overage = overage - element_weight if 0 <= candidate_overage: overage = candidate_overage result.pop() else: absolute_amount_to_keep = element_weight - overage assert 0 < absolute_amount_to_keep signed_amount_to_keep = absolute_amount_to_keep signed_amount_to_keep *= mathtools.sign(element) result.pop() result.append(signed_amount_to_keep) break else: break # return result return type(sequence)(result)
def get_sequence_element_at_cyclic_index(sequence, index): r'''Get `sequence` element at nonnegative cyclic `index`: :: >>> for index in range(10): ... print '%s\t%s' % (index, sequencetools.get_sequence_element_at_cyclic_index( ... 'string', index)) ... 0 s 1 t 2 r 3 i 4 n 5 g 6 s 7 t 8 r 9 i Get `sequence` element at negative cyclic `index`: :: >>> for index in range(1, 11): ... print '%s\t%s' % (-index, sequencetools.get_sequence_element_at_cyclic_index( ... 'string', -index)) ... -1 g -2 n -3 i -4 r -5 t -6 s -7 g -8 n -9 i -10 r Returns reference to `sequence` element. ''' return sequence[mathtools.sign(index) * (abs(index) % len(sequence))]
def partition_integer_by_ratio(n, ratio): r'''Partitions positive integer-equivalent `n` by `ratio`. .. container:: example >>> abjad.mathtools.partition_integer_by_ratio(10, [1, 2]) [3, 7] .. container:: example Partitions positive integer-equivalent `n` by `ratio` with negative parts: >>> abjad.mathtools.partition_integer_by_ratio(10, [1, -2]) [3, -7] .. container:: example Partitions negative integer-equivalent `n` by `ratio`: >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 2]) [-3, -7] .. container:: example Partitions negative integer-equivalent `n` by `ratio` with negative parts: >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, -2]) [-3, 7] .. container:: example More examples: >>> abjad.mathtools.partition_integer_by_ratio(10, [1]) [10] >>> abjad.mathtools.partition_integer_by_ratio(10, [1, 1]) [5, 5] >>> abjad.mathtools.partition_integer_by_ratio(10, [1, -1, -1]) [3, -4, -3] >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 1, 1, 1]) [-3, -2, -3, -2] >>> abjad.mathtools.partition_integer_by_ratio(-10, [1, 1, 1, 1, 1]) [-2, -2, -2, -2, -2] Returns result with weight equal to absolute value of `n`. Returns list of integers. ''' from abjad.tools import mathtools if not mathtools.is_integer_equivalent_number(n): message = 'is not integer-equivalent number: {!r}.' message = message.format(n) raise TypeError(message) ratio = mathtools.Ratio(ratio).numbers if not all(mathtools.is_integer_equivalent_number(part) for part in ratio): message = 'some parts in {!r} not integer-equivalent numbers.' message = message.format(ratio) raise TypeError(message) result = [0] divisions = [ float(abs(n)) * abs(part) / mathtools.weight(ratio) for part in ratio ] cumulative_divisions = mathtools.cumulative_sums(divisions, start=None) for division in cumulative_divisions: rounded_division = int(round(division)) - sum(result) if division - round(division) == 0.5: rounded_division += 1 result.append(rounded_division) result = result[1:] if mathtools.sign(n) == -1: result = [-x for x in result] ratio_signs = [mathtools.sign(x) for x in ratio] result = [pair[0] * pair[1] for pair in zip(ratio_signs, result)] return result
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 split_sequence(sequence, weights, cyclic=False, overhang=False): """Splits sequence by weights. .. container:: example **Example 1.** Split sequence cyclically by weights with overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=True, ... overhang=True, ... ) [(3,), (7, -8), (-2, 1), (3,), (6, -9), (-1,)] .. container:: example **Example 2.** Split sequence cyclically by weights without overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=True, ... overhang=False, ... ) [(3,), (7, -8), (-2, 1), (3,), (6, -9)] .. container:: example **Example 3.** Split sequence once by weights with overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=False, ... overhang=True, ... ) [(3,), (7, -8), (-2, 1), (9, -10)] .. container:: example **Example 4.** Split sequence once by weights without overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=False, ... overhang=False, ... ) [(3,), (7, -8), (-2, 1)] Returns list of sequence types. """ from abjad.tools import sequencetools result = [] current_index = 0 current_piece = [] if cyclic: weights = sequencetools.repeat_sequence_to_weight(weights, mathtools.weight(sequence), allow_total=Less) for weight in weights: current_piece_weight = mathtools.weight(current_piece) while current_piece_weight < weight: current_piece.append(sequence[current_index]) current_index += 1 current_piece_weight = mathtools.weight(current_piece) if current_piece_weight == weight: current_piece = type(sequence)(current_piece) result.append(current_piece) current_piece = [] elif weight < current_piece_weight: overage = current_piece_weight - weight current_last_element = current_piece.pop(-1) needed = abs(current_last_element) - overage needed *= mathtools.sign(current_last_element) current_piece.append(needed) current_piece = type(sequence)(current_piece) result.append(current_piece) overage *= mathtools.sign(current_last_element) current_piece = [overage] if overhang: last_piece = current_piece last_piece.extend(sequence[current_index:]) if last_piece: last_piece = type(sequence)(last_piece) result.append(last_piece) return result
def partition_integer_by_ratio(n, ratio): r'''Partitions positive integer-equivalent `n` by `ratio`. :: >>> mathtools.partition_integer_by_ratio(10, [1, 2]) [3, 7] Partitions positive integer-equivalent `n` by `ratio` with negative parts: :: >>> mathtools.partition_integer_by_ratio(10, [1, -2]) [3, -7] Partitions negative integer-equivalent `n` by `ratio`: :: >>> mathtools.partition_integer_by_ratio(-10, [1, 2]) [-3, -7] Partitions negative integer-equivalent `n` by `ratio` with negative parts: :: >>> mathtools.partition_integer_by_ratio(-10, [1, -2]) [-3, 7] Returns result with weight equal to absolute value of `n`. Raises type error on noninteger `n`. Returns list of integers. ''' from abjad.tools import mathtools if not mathtools.is_integer_equivalent_number(n): message = 'is not integer-equivalent number: {!r}.' message = message.format(n) raise TypeError(message) ratio = mathtools.Ratio(ratio).numbers if not all(mathtools.is_integer_equivalent_number(part) for part in ratio): message = 'some parts in {!r} not integer-equivalent numbers.' message = message.format(ratio) raise TypeError(message) result = [0] divisions = [ float(abs(n)) * abs(part) / mathtools.weight(ratio) for part in ratio ] cumulative_divisions = mathtools.cumulative_sums(divisions, start=None) for division in cumulative_divisions: rounded_division = int(round(division)) - sum(result) #This makes rounding behave like python 2. Would be good to remove # in the long run if sys.version_info[0] == 3: if division - round(division) == 0.5: rounded_division += 1 result.append(rounded_division) result = result[1:] # adjust signs of output elements if mathtools.sign(n) == -1: result = [-x for x in result] ratio_signs = [mathtools.sign(x) for x in ratio] result = [pair[0] * pair[1] for pair in zip(ratio_signs, result)] # return result return result
def to_named_interval(self, staff_positions): r'''Changes numbered interval to named interval that encompasses `staff_positions`. .. container:: example >>> abjad.NumberedInterval(0).to_named_interval(0) NamedInterval('aug0') >>> abjad.NumberedInterval(0).to_named_interval(1) NamedInterval('P1') >>> abjad.NumberedInterval(0).to_named_interval(2) NamedInterval('+dim2') .. container:: example >>> abjad.NumberedInterval(1).to_named_interval(1) NamedInterval('+aug1') >>> abjad.NumberedInterval(1).to_named_interval(2) NamedInterval('+m2') .. container:: example >>> abjad.NumberedInterval(-1).to_named_interval(1) NamedInterval('-aug1') >>> abjad.NumberedInterval(-1).to_named_interval(2) NamedInterval('-m2') .. container:: example >>> abjad.NumberedInterval(2).to_named_interval(2) NamedInterval('+M2') Returns named interval. ''' from abjad.tools import pitchtools direction_number = mathtools.sign(self.number) quality_string = None if staff_positions == 1: if self.number % 12 == 11: quality_string = 'augmented' elif self.number % 12 == 0: quality_string = 'perfect' elif self.number % 12 == 1: quality_string = 'augmented' if not direction_number == 0: staff_positions *= direction_number if quality_string is None: # TODO: handle double-augmented named intervals return pitchtools.NamedInterval(self.number) named_interval = pitchtools.NamedInterval.from_quality_and_number( quality_string, staff_positions, ) return named_interval named_interval_class_number = staff_positions % 7 numbered_interval_class_number = abs(self.number) % 12 if named_interval_class_number == 0: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 1: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' elif named_interval_class_number == 2: if numbered_interval_class_number == 0: quality_string = 'diminished' elif numbered_interval_class_number == 1: quality_string = 'minor' elif numbered_interval_class_number == 2: quality_string = 'major' elif numbered_interval_class_number == 3: quality_string = 'augmented' elif named_interval_class_number == 3: if numbered_interval_class_number == 2: quality_string = 'diminished' elif numbered_interval_class_number == 3: quality_string = 'minor' elif numbered_interval_class_number == 4: quality_string = 'major' elif numbered_interval_class_number == 5: quality_string = 'augmented' elif named_interval_class_number == 4: if numbered_interval_class_number == 4: quality_string = 'diminished' elif numbered_interval_class_number == 5: quality_string = 'perfect' elif numbered_interval_class_number == 6: quality_string = 'augmented' elif named_interval_class_number == 5: if numbered_interval_class_number == 6: quality_string = 'diminished' elif numbered_interval_class_number == 7: quality_string = 'perfect' elif numbered_interval_class_number == 8: quality_string = 'augmented' elif named_interval_class_number == 6: if numbered_interval_class_number == 7: quality_string = 'diminished' elif numbered_interval_class_number == 8: quality_string = 'minor' elif numbered_interval_class_number == 9: quality_string = 'major' elif numbered_interval_class_number == 10: quality_string = 'augmented' elif named_interval_class_number == 7: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 8: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' if not direction_number == 0: staff_positions *= direction_number if quality_string is None: # TODO: It is possible to for quality string to *never* get set to # anything, generally during inversion with double-sharps or # double-flats. This suite provides a sane result. # Don't remove it - fix whatever's allowing quality string to # remain unset. return pitchtools.NamedInterval(self.number) named_interval = pitchtools.NamedInterval.from_quality_and_number( quality_string, staff_positions, ) return named_interval
def tessalate_by_ratio(self, ratio, invert_on_negative=False, reflect_on_negative=False, y_center=None, ): r'''Concatenate copies of a BreakPointFunction, stretched by the weights in `ratio`: :: >>> bpf = interpolationtools.BreakPointFunction( ... {0.: 0., 0.25: 0.9, 1.: 1.}) :: >>> bpf.tessalate_by_ratio((1, 2, 3)) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0, 0.0), 1.5: (0.9,), 3.0: (1.0, 0.0), 3.75: (0.9,), 6.0: (1.0,) }) Negative ratio values are still treated as weights. If `invert_on_negative` is True, copies corresponding to negative ratio values will be inverted: :: >>> bpf.tessalate_by_ratio((1, -2, 3), invert_on_negative=True) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0,), 1.5: (0.09999999999999998,), 3.0: (0.0,), 3.75: (0.9,), 6.0: (1.0,) }) If `y_center` is not None, inversion will take `y_center` as the axis of inversion: :: >>> bpf.tessalate_by_ratio((1, -2, 3), ... invert_on_negative=True, y_center=0) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0, 0.0), 1.5: (-0.9,), 3.0: (-1.0, 0.0), 3.75: (0.9,), 6.0: (1.0,) }) If `reflect_on_negative` is True, copies corresponding to negative ratio values will be reflectd: :: >>> bpf.tessalate_by_ratio((1, -2, 3), reflect_on_negative=True) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0,), 2.5: (0.9,), 3.0: (0.0,), 3.75: (0.9,), 6.0: (1.0,) }) Inversion may be combined reflecting. Emit new `BreakPointFunction` instance. ''' ratio = mathtools.Ratio(ratio) tessalated_bpf = None for i, pair in enumerate(mathtools.cumulative_sums_pairwise( [abs(x) for x in ratio])): sign = mathtools.sign(ratio[i]) start, stop = pair bpf = self.scale_x_axis(start, stop) if sign < 0: if invert_on_negative: bpf = bpf.invert(y_center) if reflect_on_negative: bpf = bpf.reflect() if i == 0: tessalated_bpf = bpf else: tessalated_bpf = tessalated_bpf.concatenate(bpf) return tessalated_bpf
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
def to_named_interval(self, staff_positions): r'''Changes numbered interval to named interval that encompasses `staff_positions`. .. container:: example :: >>> numbered_interval = pitchtools.NumberedInterval(1) >>> numbered_interval.to_named_interval(2) NamedInterval('+m2') Returns named interval. ''' from abjad.tools import pitchtools direction_number = mathtools.sign(self.number) if staff_positions == 1: quality_string = None if self.number % 12 == 11: quality_string = 'augmented' elif self.number % 12 == 0: quality_string = 'perfect' elif self.number % 12 == 1: quality_string = 'augmented' if not direction_number == 0: staff_positions *= direction_number if quality_string is None: # TODO: handle double-augmented named intervals return pitchtools.NamedInterval(self.number) named_interval = pitchtools.NamedInterval(quality_string, staff_positions) return named_interval named_interval_class_number = staff_positions % 7 numbered_interval_class_number = abs(self.number) % 12 if named_interval_class_number == 0: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 1: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' elif named_interval_class_number == 2: if numbered_interval_class_number == 0: quality_string = 'diminished' elif numbered_interval_class_number == 1: quality_string = 'minor' elif numbered_interval_class_number == 2: quality_string = 'major' elif numbered_interval_class_number == 3: quality_string = 'augmented' elif named_interval_class_number == 3: if numbered_interval_class_number == 2: quality_string = 'diminished' elif numbered_interval_class_number == 3: quality_string = 'minor' elif numbered_interval_class_number == 4: quality_string = 'major' elif numbered_interval_class_number == 5: quality_string = 'augmented' elif named_interval_class_number == 4: if numbered_interval_class_number == 4: quality_string = 'diminished' elif numbered_interval_class_number == 5: quality_string = 'perfect' elif numbered_interval_class_number == 6: quality_string = 'augmented' elif named_interval_class_number == 5: if numbered_interval_class_number == 6: quality_string = 'diminished' elif numbered_interval_class_number == 7: quality_string = 'perfect' elif numbered_interval_class_number == 8: quality_string = 'augmented' elif named_interval_class_number == 6: if numbered_interval_class_number == 7: quality_string = 'diminished' elif numbered_interval_class_number == 8: quality_string = 'minor' elif numbered_interval_class_number == 9: quality_string = 'major' elif numbered_interval_class_number == 10: quality_string = 'augmented' elif named_interval_class_number == 7: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 8: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' if not direction_number == 0: staff_positions *= direction_number named_interval = pitchtools.NamedInterval( quality_string, staff_positions, ) return named_interval
def tessalate_by_ratio( self, ratio, invert_on_negative=False, reflect_on_negative=False, y_center=None, ): r'''Concatenate copies of a BreakPointFunction, stretched by the weights in `ratio`: :: >>> bpf = interpolationtools.BreakPointFunction( ... {0.: 0., 0.25: 0.9, 1.: 1.}) :: >>> bpf.tessalate_by_ratio((1, 2, 3)) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0, 0.0), 1.5: (0.9,), 3.0: (1.0, 0.0), 3.75: (0.9,), 6.0: (1.0,) }) Negative ratio values are still treated as weights. If `invert_on_negative` is True, copies corresponding to negative ratio values will be inverted: :: >>> bpf.tessalate_by_ratio((1, -2, 3), invert_on_negative=True) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0,), 1.5: (0.09999999999999998,), 3.0: (0.0,), 3.75: (0.9,), 6.0: (1.0,) }) If `y_center` is not None, inversion will take `y_center` as the axis of inversion: :: >>> bpf.tessalate_by_ratio((1, -2, 3), ... invert_on_negative=True, y_center=0) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0, 0.0), 1.5: (-0.9,), 3.0: (-1.0, 0.0), 3.75: (0.9,), 6.0: (1.0,) }) If `reflect_on_negative` is True, copies corresponding to negative ratio values will be reflectd: :: >>> bpf.tessalate_by_ratio((1, -2, 3), reflect_on_negative=True) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0,), 2.5: (0.9,), 3.0: (0.0,), 3.75: (0.9,), 6.0: (1.0,) }) Inversion may be combined reflecting. Emit new `BreakPointFunction` instance. ''' ratio = mathtools.Ratio(ratio) tessalated_bpf = None for i, pair in enumerate( mathtools.cumulative_sums_pairwise([abs(x) for x in ratio])): sign = mathtools.sign(ratio[i]) start, stop = pair bpf = self.scale_x_axis(start, stop) if sign < 0: if invert_on_negative: bpf = bpf.invert(y_center) if reflect_on_negative: bpf = bpf.reflect() if i == 0: tessalated_bpf = bpf else: tessalated_bpf = tessalated_bpf.concatenate(bpf) return tessalated_bpf
def spell_numbered_interval_number( named_interval_number, numbered_interval_number, ): '''Spell `numbered_interval_number` according to `named_interval_number`: :: >>> pitchtools.spell_numbered_interval_number(2, 1) NamedInterval('+m2') Returns named interval. ''' from abjad.tools import pitchtools if not isinstance(numbered_interval_number, int): message = 'can not determine diatonic interval from float.' raise TypeError(message) direction_number = mathtools.sign(numbered_interval_number) if named_interval_number == 1: if numbered_interval_number % 12 == 11: quality_string = 'augmented' elif numbered_interval_number % 12 == 0: quality_string = 'perfect' elif numbered_interval_number % 12 == 1: quality_string = 'augmented' if not direction_number == 0: named_interval_number *= direction_number named_interval = pitchtools.NamedInterval( quality_string, named_interval_number) return named_interval named_interval_class_number = named_interval_number % 7 numbered_interval_class_number = abs(numbered_interval_number) % 12 if named_interval_class_number == 0: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 1: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' elif named_interval_class_number == 2: if numbered_interval_class_number == 0: quality_string = 'diminished' elif numbered_interval_class_number == 1: quality_string = 'minor' elif numbered_interval_class_number == 2: quality_string = 'major' elif numbered_interval_class_number == 3: quality_string = 'augmented' elif named_interval_class_number == 3: if numbered_interval_class_number == 2: quality_string = 'diminished' elif numbered_interval_class_number == 3: quality_string = 'minor' elif numbered_interval_class_number == 4: quality_string = 'major' elif numbered_interval_class_number == 5: quality_string = 'augmented' elif named_interval_class_number == 4: if numbered_interval_class_number == 4: quality_string = 'diminished' elif numbered_interval_class_number == 5: quality_string = 'perfect' elif numbered_interval_class_number == 6: quality_string = 'augmented' elif named_interval_class_number == 5: if numbered_interval_class_number == 6: quality_string = 'diminished' elif numbered_interval_class_number == 7: quality_string = 'perfect' elif numbered_interval_class_number == 8: quality_string = 'augmented' elif named_interval_class_number == 6: if numbered_interval_class_number == 7: quality_string = 'diminished' elif numbered_interval_class_number == 8: quality_string = 'minor' elif numbered_interval_class_number == 9: quality_string = 'major' elif numbered_interval_class_number == 10: quality_string = 'augmented' elif named_interval_class_number == 7: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 8: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' if not direction_number == 0: named_interval_number *= direction_number named_interval = pitchtools.NamedInterval(quality_string, named_interval_number) return named_interval
def partition_integer_by_ratio(n, ratio): r'''Partitions positive integer-equivalent `n` by `ratio`. :: >>> mathtools.partition_integer_by_ratio(10, [1, 2]) [3, 7] Partitions positive integer-equivalent `n` by `ratio` with negative parts: :: >>> mathtools.partition_integer_by_ratio(10, [1, -2]) [3, -7] Partitions negative integer-equivalent `n` by `ratio`: :: >>> mathtools.partition_integer_by_ratio(-10, [1, 2]) [-3, -7] Partitions negative integer-equivalent `n` by `ratio` with negative parts: :: >>> mathtools.partition_integer_by_ratio(-10, [1, -2]) [-3, 7] Returns result with weight equal to absolute value of `n`. Raises type error on noninteger `n`. Returns list of integers. ''' from abjad.tools import mathtools if not mathtools.is_integer_equivalent_number(n): message = 'is not integer-equivalent number: {!r}.' message = message.format(n) raise TypeError(message) ratio = mathtools.Ratio(ratio).numbers if not all( mathtools.is_integer_equivalent_number(part) for part in ratio ): message = 'some parts in {!r} not integer-equivalent numbers.' message = message.format(ratio) raise TypeError(message) result = [0] divisions = [ float(abs(n)) * abs(part) / mathtools.weight(ratio) for part in ratio ] cumulative_divisions = mathtools.cumulative_sums(divisions, start=None) for division in cumulative_divisions: rounded_division = int(round(division)) - sum(result) #This makes rounding behave like python 2. Would be good to remove # in the long run if sys.version_info[0] == 3: if division - round(division) == 0.5: rounded_division += 1 result.append(rounded_division) result = result[1:] # adjust signs of output elements if mathtools.sign(n) == -1: result = [-x for x in result] ratio_signs = [mathtools.sign(x) for x in ratio] result = [pair[0] * pair[1] for pair in zip(ratio_signs, result)] # return result return result
def to_named_interval(self, staff_positions): r'''Changes numbered interval to named interval that encompasses `staff_positions`. .. container:: example :: >>> numbered_interval = pitchtools.NumberedInterval(1) >>> numbered_interval.to_named_interval(2) NamedInterval('+m2') Returns named interval. ''' from abjad.tools import pitchtools direction_number = mathtools.sign(self.number) if staff_positions == 1: quality_string = None if self.number % 12 == 11: quality_string = 'augmented' elif self.number % 12 == 0: quality_string = 'perfect' elif self.number % 12 == 1: quality_string = 'augmented' if not direction_number == 0: staff_positions *= direction_number if quality_string is None: # TODO: handle double-augmented named intervals return pitchtools.NamedInterval(self.number) named_interval = pitchtools.NamedInterval( quality_string, staff_positions) return named_interval named_interval_class_number = staff_positions % 7 numbered_interval_class_number = abs(self.number) % 12 if named_interval_class_number == 0: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 1: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' elif named_interval_class_number == 2: if numbered_interval_class_number == 0: quality_string = 'diminished' elif numbered_interval_class_number == 1: quality_string = 'minor' elif numbered_interval_class_number == 2: quality_string = 'major' elif numbered_interval_class_number == 3: quality_string = 'augmented' elif named_interval_class_number == 3: if numbered_interval_class_number == 2: quality_string = 'diminished' elif numbered_interval_class_number == 3: quality_string = 'minor' elif numbered_interval_class_number == 4: quality_string = 'major' elif numbered_interval_class_number == 5: quality_string = 'augmented' elif named_interval_class_number == 4: if numbered_interval_class_number == 4: quality_string = 'diminished' elif numbered_interval_class_number == 5: quality_string = 'perfect' elif numbered_interval_class_number == 6: quality_string = 'augmented' elif named_interval_class_number == 5: if numbered_interval_class_number == 6: quality_string = 'diminished' elif numbered_interval_class_number == 7: quality_string = 'perfect' elif numbered_interval_class_number == 8: quality_string = 'augmented' elif named_interval_class_number == 6: if numbered_interval_class_number == 7: quality_string = 'diminished' elif numbered_interval_class_number == 8: quality_string = 'minor' elif numbered_interval_class_number == 9: quality_string = 'major' elif numbered_interval_class_number == 10: quality_string = 'augmented' elif named_interval_class_number == 7: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 8: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' if not direction_number == 0: staff_positions *= direction_number named_interval = pitchtools.NamedInterval( quality_string, staff_positions, ) return named_interval
def partition_integer_into_canonic_parts(n, decrease_parts_monotonically=True): r'''Partitions integer `n` into canonic parts. Returns all parts positive on positive `n`: :: >>> for n in range(1, 11): ... print(n, mathtools.partition_integer_into_canonic_parts(n)) ... 1 (1,) 2 (2,) 3 (3,) 4 (4,) 5 (4, 1) 6 (6,) 7 (7,) 8 (8,) 9 (8, 1) 10 (8, 2) Returns all parts negative on negative `n`: :: >>> for n in reversed(range(-20, -10)): ... print(n, mathtools.partition_integer_into_canonic_parts(n)) ... -11 (-8, -3) -12 (-12,) -13 (-12, -1) -14 (-14,) -15 (-15,) -16 (-16,) -17 (-16, -1) -18 (-16, -2) -19 (-16, -3) -20 (-16, -4) Returns parts that increase monotonically: :: >>> for n in range(11, 21): ... print(n, mathtools.partition_integer_into_canonic_parts(n, ... decrease_parts_monotonically=False)) ... 11 (3, 8) 12 (12,) 13 (1, 12) 14 (14,) 15 (15,) 16 (16,) 17 (1, 16) 18 (2, 16) 19 (3, 16) 20 (4, 16) Returns tuple with parts that decrease monotonically. Raises type error on noninteger `n`. Returns tuple of one or more integers. ''' from abjad.tools import mathtools if not isinstance(n, int): raise TypeError if not isinstance(decrease_parts_monotonically, bool): raise ValueError if n == 0: return (0, ) result = [] previous_empty = True binary_n = mathtools.integer_to_binary_string(abs(n)) binary_length = len(binary_n) for i, x in enumerate(binary_n): if x == '1': place_value = 2 ** (binary_length - i - 1) if previous_empty: result.append(place_value) else: result[-1] += place_value previous_empty = False else: previous_empty = True sign_n = mathtools.sign(n) if mathtools.sign(n) == -1: result = [sign_n * x for x in result] if decrease_parts_monotonically: return tuple(result) else: return tuple(reversed(result))
def repeat_sequence_to_weight(sequence, weight, allow_total=Exact): '''Repeats `sequence` to `weight`. .. container:: example **Example 1.** Repeats sequence to weight of 23 exactly: :: >>> sequencetools.repeat_sequence_to_weight((5, -5, -5), 23) (5, -5, -5, 5, -3) Truncates last element when necessary. .. container:: example **Example 2.** Repeats sequence to weight of 23 more: :: >>> sequencetools.repeat_sequence_to_weight( ... (5, -5, -5), ... 23, ... allow_total=More, ... ) (5, -5, -5, 5, -5) Does not truncate last element. .. container:: example **Example 3.** Repeats sequence to weight of 23 or less: :: >>> sequencetools.repeat_sequence_to_weight( ... (5, -5, -5), ... 23, ... allow_total=Less, ... ) (5, -5, -5, 5) Discards last element when necessary. Returns new object of `sequence` type. ''' if not isinstance(sequence, collections.Sequence): message = 'must by sequence {!r}.' message = message.format(sequence) raise Exception(message) sequence_type = type(sequence) # check input assert isinstance(weight, numbers.Number), repr(weight) assert 0 <= weight if allow_total == Exact: # repeat sequence and find overage sequence_weight = mathtools.weight(sequence) complete_repetitions = int( math.ceil(float(weight) / float(sequence_weight)) ) result = list(sequence) result = complete_repetitions * result overage = complete_repetitions * sequence_weight - weight # remove overage from result for element in reversed(result): if 0 < overage: element_weight = abs(element) candidate_overage = overage - element_weight if 0 <= candidate_overage: overage = candidate_overage result.pop() else: absolute_amount_to_keep = element_weight - overage assert 0 < absolute_amount_to_keep signed_amount_to_keep = absolute_amount_to_keep signed_amount_to_keep *= mathtools.sign(element) result.pop() result.append(signed_amount_to_keep) break else: break elif allow_total == Less: # initialize result result = [sequence[0]] # iterate input i = 1 while mathtools.weight(result) < weight: result.append(sequence[i % len(sequence)]) i += 1 # remove overage if weight < mathtools.weight(result): result = result[:-1] # return result return type(sequence)(result) elif allow_total == More: # initialize result result = [sequence[0]] # iterate input i = 1 while mathtools.weight(result) < weight: result.append(sequence[i % len(sequence)]) i += 1 # return result return type(sequence)(result) else: message = 'is not an ordinal value constant: {!r}.' message = message.format(allow_total) raise ValueError(message) # return result result = sequence_type(result) return result
def spell_numbered_interval_number( named_interval_number, numbered_interval_number, ): '''Spell `numbered_interval_number` according to `named_interval_number`: :: >>> pitchtools.spell_numbered_interval_number(2, 1) NamedInterval('+m2') Returns named interval. ''' from abjad.tools import pitchtools if not isinstance(numbered_interval_number, int): message = 'can not determine diatonic interval from float.' raise TypeError(message) direction_number = mathtools.sign(numbered_interval_number) if named_interval_number == 1: if numbered_interval_number % 12 == 11: quality_string = 'augmented' elif numbered_interval_number % 12 == 0: quality_string = 'perfect' elif numbered_interval_number % 12 == 1: quality_string = 'augmented' if not direction_number == 0: named_interval_number *= direction_number named_interval = pitchtools.NamedInterval(quality_string, named_interval_number) return named_interval named_interval_class_number = named_interval_number % 7 numbered_interval_class_number = abs(numbered_interval_number) % 12 if named_interval_class_number == 0: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 1: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' elif named_interval_class_number == 2: if numbered_interval_class_number == 0: quality_string = 'diminished' elif numbered_interval_class_number == 1: quality_string = 'minor' elif numbered_interval_class_number == 2: quality_string = 'major' elif numbered_interval_class_number == 3: quality_string = 'augmented' elif named_interval_class_number == 3: if numbered_interval_class_number == 2: quality_string = 'diminished' elif numbered_interval_class_number == 3: quality_string = 'minor' elif numbered_interval_class_number == 4: quality_string = 'major' elif numbered_interval_class_number == 5: quality_string = 'augmented' elif named_interval_class_number == 4: if numbered_interval_class_number == 4: quality_string = 'diminished' elif numbered_interval_class_number == 5: quality_string = 'perfect' elif numbered_interval_class_number == 6: quality_string = 'augmented' elif named_interval_class_number == 5: if numbered_interval_class_number == 6: quality_string = 'diminished' elif numbered_interval_class_number == 7: quality_string = 'perfect' elif numbered_interval_class_number == 8: quality_string = 'augmented' elif named_interval_class_number == 6: if numbered_interval_class_number == 7: quality_string = 'diminished' elif numbered_interval_class_number == 8: quality_string = 'minor' elif numbered_interval_class_number == 9: quality_string = 'major' elif numbered_interval_class_number == 10: quality_string = 'augmented' elif named_interval_class_number == 7: if numbered_interval_class_number == 9: quality_string = 'diminished' elif numbered_interval_class_number == 10: quality_string = 'minor' elif numbered_interval_class_number == 11: quality_string = 'major' elif numbered_interval_class_number == 0: quality_string = 'augmented' elif named_interval_class_number == 8: if numbered_interval_class_number == 11: quality_string = 'diminished' elif numbered_interval_class_number == 0: quality_string = 'perfect' elif numbered_interval_class_number == 1: quality_string = 'augmented' if not direction_number == 0: named_interval_number *= direction_number named_interval = pitchtools.NamedInterval(quality_string, named_interval_number) return named_interval
def direction_number(self): if self.quality_string == 'perfect' and abs(self.number) == 1: return 0 else: return mathtools.sign(self.number)