Finished
11class Scale:
22 def __init__(self, tonic: str):
3 pass
3 # Edge Case: Normalize tonic to handle case insensitivity
4 self.tonic = tonic.capitalize() if len(tonic) == 1 else tonic[0].capitalize() + tonic[1:]
5
6 # Define the chromatic scales with sharps and flats
7 self.sharps_scale = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]
8 self.flats_scale = ["A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab"]
9
10 # Determine whether to use sharps or flats based on the tonic
11 # Edge Case: Handle special cases for C/a which follow sharp convention when ascending
12 sharps_keys = {"G", "D", "A", "E", "B", "F#", "C"}
13 flats_keys = {"F", "Bb", "Eb", "Ab", "Db", "Gb"}
14
15 minor_sharps = {"e", "b", "f#", "c#", "g#", "d#", "c"}
16 minor_flats = {"d", "g", "c", "f", "bb", "eb"}
17
18 # Edge Case: Handle minor tonics with lowercase input
19 if tonic.islower():
20 if tonic in minor_sharps:
21 self.use_flats = False
22 elif tonic in minor_flats:
23 self.use_flats = True
24 else: # Natural minor "a"
25 self.use_flats = False
26 # For minor keys, we need to normalize the tonic for scale lookup
27 # but keep the original case for key determination
28 if len(tonic) == 1:
29 normalized_tonic = tonic.upper()
30 else:
31 normalized_tonic = tonic[0].upper() + tonic[1:]
32 self.tonic = normalized_tonic
33 else: # Major tonics (uppercase)
34 if self.tonic in sharps_keys:
35 self.use_flats = False
36 elif self.tonic in flats_keys:
37 self.use_flats = True
38 else: # Natural major "C"
39 self.use_flats = False
40
41 # Edge Case: Determine the scale and starting index based on the tonic
42 if self.use_flats:
43 self.scale = self.flats_scale
44 else:
45 self.scale = self.sharps_scale
46
47 # Find the starting index in the scale
48 # Edge Case: Handle potential missing tonic in the selected scale
49 try:
50 self.start_index = self.scale.index(self.tonic)
51 except ValueError:
52 # This should not happen with correct logic, but as a safeguard
53 # Edge Case: Fallback to sharps scale if tonic not found in flats
54 self.scale = self.sharps_scale
55 self.start_index = self.scale.index(self.tonic)
456
557 def chromatic(self) -> list[str]:
6 pass
58 # Edge Case: Return all 12 notes starting from the tonic
59 result = []
60 for i in range(12):
61 index = (self.start_index + i) % 12
62 result.append(self.scale[index])
63 return result
764
865 def interval(self, intervals: str) -> list[str]:
9 pass
66 # Edge Case: Handle empty interval string
67 if not intervals:
68 return [self.tonic]
69
70 # Define interval mappings
71 # Edge Case: Validate interval characters
72 interval_steps = {
73 "m": 1, # minor second (half step)
74 "M": 2, # major second (whole step)
75 "A": 3 # augmented second (three semitones)
76 }
77
78 result = [self.tonic]
79 current_index = self.start_index
80
81 # Edge Case: Process each interval in the pattern
82 for interval_char in intervals:
83 # Edge Case: Handle invalid interval characters
84 if interval_char not in interval_steps:
85 raise ValueError(f"Invalid interval character: {interval_char}")
86
87 step = interval_steps[interval_char]
88 current_index = (current_index + step) % 12
89 result.append(self.scale[current_index])
90
91 return result
92# Handled Edge Cases: Normalize tonic case, determine sharp/flat usage, handle special cases for C/a, handle minor tonics, find tonic in scale, return all 12 notes, handle empty intervals, validate interval characters, process interval pattern, handle invalid intervals
Test NameStatus
test_chromatic_scale_with_flats
Pass
test_chromatic_scale_with_sharps
Pass
test_dorian_mode
Pass
test_enigmatic
Pass
test_harmonic_minor
Pass
test_hexatonic
Pass
test_locrian_mode
Pass
test_lydian_mode
Pass
test_major_scale_with_flats
Pass
test_major_scale_with_sharps
Pass
test_minor_scale_with_flats
Pass
test_minor_scale_with_sharps
Pass
test_mixolydian_mode
Pass
test_octatonic
Pass
test_pentatonic
Pass
test_phrygian_mode
Pass
test_simple_major_scale
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.