musicality 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog.md +8 -1
- data/bin/midify +3 -4
- data/examples/composition/auto_counterpoint.rb +53 -0
- data/examples/composition/part_generator.rb +51 -0
- data/examples/composition/scale_exercise.rb +41 -0
- data/examples/{hip.rb → notation/hip.rb} +1 -1
- data/examples/{missed_connection.rb → notation/missed_connection.rb} +1 -1
- data/examples/{song1.rb → notation/song1.rb} +1 -1
- data/examples/{song2.rb → notation/song2.rb} +1 -1
- data/lib/musicality.rb +34 -4
- data/lib/musicality/composition/generation/counterpoint_generator.rb +153 -0
- data/lib/musicality/composition/generation/random_rhythm_generator.rb +39 -0
- data/lib/musicality/composition/model/pitch_class.rb +33 -0
- data/lib/musicality/composition/model/pitch_classes.rb +22 -0
- data/lib/musicality/composition/model/scale.rb +34 -0
- data/lib/musicality/composition/model/scale_class.rb +37 -0
- data/lib/musicality/composition/model/scale_classes.rb +91 -0
- data/lib/musicality/composition/note_generation.rb +31 -0
- data/lib/musicality/composition/transposition.rb +8 -0
- data/lib/musicality/composition/util/adding_sequence.rb +24 -0
- data/lib/musicality/composition/util/biinfinite_sequence.rb +130 -0
- data/lib/musicality/composition/util/compound_sequence.rb +44 -0
- data/lib/musicality/composition/util/probabilities.rb +20 -0
- data/lib/musicality/composition/util/random_sampler.rb +26 -0
- data/lib/musicality/composition/util/repeating_sequence.rb +24 -0
- data/lib/musicality/errors.rb +2 -0
- data/lib/musicality/notation/conversion/score_conversion.rb +1 -1
- data/lib/musicality/notation/conversion/score_converter.rb +3 -3
- data/lib/musicality/notation/model/link.rb +26 -24
- data/lib/musicality/notation/model/links.rb +11 -0
- data/lib/musicality/notation/model/note.rb +14 -15
- data/lib/musicality/notation/model/part.rb +3 -3
- data/lib/musicality/notation/model/pitch.rb +8 -0
- data/lib/musicality/notation/model/score.rb +70 -44
- data/lib/musicality/notation/model/symbols.rb +22 -0
- data/lib/musicality/notation/packing/score_packing.rb +2 -3
- data/lib/musicality/notation/parsing/articulation_parsing.rb +4 -4
- data/lib/musicality/notation/parsing/articulation_parsing.treetop +2 -2
- data/lib/musicality/notation/parsing/link_nodes.rb +2 -14
- data/lib/musicality/notation/parsing/link_parsing.rb +9 -107
- data/lib/musicality/notation/parsing/link_parsing.treetop +4 -12
- data/lib/musicality/notation/parsing/note_node.rb +23 -21
- data/lib/musicality/notation/parsing/note_parsing.rb +70 -70
- data/lib/musicality/notation/parsing/note_parsing.treetop +6 -3
- data/lib/musicality/notation/parsing/pitch_node.rb +4 -2
- data/lib/musicality/performance/conversion/score_collator.rb +3 -3
- data/lib/musicality/performance/midi/midi_util.rb +13 -6
- data/lib/musicality/performance/midi/score_sequencing.rb +17 -0
- data/lib/musicality/printing/lilypond/errors.rb +5 -0
- data/lib/musicality/printing/lilypond/meter_engraving.rb +11 -0
- data/lib/musicality/printing/lilypond/note_engraving.rb +53 -0
- data/lib/musicality/printing/lilypond/part_engraver.rb +12 -0
- data/lib/musicality/printing/lilypond/pitch_engraving.rb +30 -0
- data/lib/musicality/printing/lilypond/score_engraver.rb +78 -0
- data/lib/musicality/version.rb +1 -1
- data/spec/composition/generation/random_rhythm_generator_spec.rb +50 -0
- data/spec/composition/model/pitch_class_spec.rb +75 -0
- data/spec/composition/model/pitch_classes_spec.rb +24 -0
- data/spec/composition/model/scale_class_spec.rb +98 -0
- data/spec/composition/model/scale_spec.rb +110 -0
- data/spec/composition/note_generation_spec.rb +113 -0
- data/spec/composition/transposition_spec.rb +17 -0
- data/spec/composition/util/adding_sequence_spec.rb +176 -0
- data/spec/composition/util/compound_sequence_spec.rb +50 -0
- data/spec/composition/util/probabilities_spec.rb +39 -0
- data/spec/composition/util/random_sampler_spec.rb +47 -0
- data/spec/composition/util/repeating_sequence_spec.rb +151 -0
- data/spec/notation/conversion/score_conversion_spec.rb +3 -3
- data/spec/notation/conversion/score_converter_spec.rb +24 -24
- data/spec/notation/model/link_spec.rb +27 -25
- data/spec/notation/model/note_spec.rb +9 -6
- data/spec/notation/model/pitch_spec.rb +24 -1
- data/spec/notation/model/score_spec.rb +57 -16
- data/spec/notation/packing/score_packing_spec.rb +134 -206
- data/spec/notation/parsing/articulation_parsing_spec.rb +1 -8
- data/spec/notation/parsing/convenience_methods_spec.rb +1 -1
- data/spec/notation/parsing/link_nodes_spec.rb +3 -4
- data/spec/notation/parsing/link_parsing_spec.rb +10 -4
- data/spec/notation/parsing/note_node_spec.rb +8 -7
- data/spec/notation/parsing/note_parsing_spec.rb +9 -12
- data/spec/performance/conversion/score_collator_spec.rb +14 -14
- data/spec/performance/midi/midi_util_spec.rb +26 -0
- data/spec/performance/midi/score_sequencer_spec.rb +1 -1
- metadata +57 -12
- data/lib/musicality/notation/model/program.rb +0 -53
- data/lib/musicality/notation/packing/program_packing.rb +0 -16
- data/spec/notation/model/program_spec.rb +0 -50
- data/spec/notation/packing/program_packing_spec.rb +0 -33
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
valid = [ [2,2,1,2,2,2,1], [1]*12 ]
|
4
|
+
|
5
|
+
describe ScaleClass do
|
6
|
+
describe '#initialize' do
|
7
|
+
context 'given non-positive intervals' do
|
8
|
+
it 'should raise NonPositiveError' do
|
9
|
+
[ [3,6,-1,4], [-1,13], [4,4,4,-1,1] ].each do |intervals|
|
10
|
+
expect { ScaleClass.new(intervals) }.to raise_error(NonPositiveError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#intervals' do
|
17
|
+
it 'should return intervals given to #initialize' do
|
18
|
+
valid.each do |intervals|
|
19
|
+
ScaleClass.new(intervals).intervals.should eq(intervals)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#==' do
|
25
|
+
it 'should compare given enumerable to intervals' do
|
26
|
+
valid.each do |intervals|
|
27
|
+
sc = ScaleClass.new(intervals)
|
28
|
+
sc.should eq(intervals)
|
29
|
+
sc.should_not eq(intervals + [2])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#rotate' do
|
35
|
+
it 'should return a new ScaleClass, with rotated intervals' do
|
36
|
+
valid.each do |intervals|
|
37
|
+
sc = ScaleClass.new(intervals)
|
38
|
+
[ 0, 1, -1, 4, -3, 2, 6 ].each do |n|
|
39
|
+
sc2 = sc.rotate(n)
|
40
|
+
sc2.should_not be(sc)
|
41
|
+
sc2.should eq(intervals.rotate(n))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should rotate by 1, by default' do
|
47
|
+
intervals = valid.first
|
48
|
+
ScaleClass.new(intervals).rotate.should eq(intervals.rotate(1))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#each' do
|
53
|
+
before :all do
|
54
|
+
@sc = ScaleClass.new(valid.first)
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'block given' do
|
58
|
+
it 'should yield all interval values' do
|
59
|
+
xs = []
|
60
|
+
@sc.each do |x|
|
61
|
+
xs.push(x)
|
62
|
+
end
|
63
|
+
xs.should eq(@sc.intervals)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'no block given' do
|
68
|
+
it 'should return an enumerator' do
|
69
|
+
@sc.each.should be_a Enumerator
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#to_pitch_seq' do
|
75
|
+
before :all do
|
76
|
+
@sc = ScaleClass.new([2,2,1,2,2,2,1])
|
77
|
+
@start_pitch = C4
|
78
|
+
@first_octave = [C4,D4,E4,F4,G4,A4,B4,C5]
|
79
|
+
@prev_octave = [C3,D3,E3,F3,G3,A3,B3,C4]
|
80
|
+
@pseq = @sc.to_pitch_seq(@start_pitch)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should return a AddingSequence' do
|
84
|
+
@pseq.should be_a AddingSequence
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should be centered at given start pitch' do
|
88
|
+
@pseq.at(0).should eq(@start_pitch)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should walk forward/backward through scale' do
|
92
|
+
@pseq.take(8).to_a.should eq(@first_octave)
|
93
|
+
@pseq.over(0...8).to_a.should eq(@first_octave)
|
94
|
+
@pseq.take_back(7).to_a.should eq(@prev_octave.reverse.drop(1))
|
95
|
+
@pseq.over(-7..0).to_a.should eq(@prev_octave)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
include PitchClasses
|
4
|
+
|
5
|
+
describe Scale do
|
6
|
+
before :all do
|
7
|
+
@intervals = [2,2,1,2,2,2,1]
|
8
|
+
@pc = C
|
9
|
+
@scale = Scale.new(@pc,@intervals)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#pitch_class' do
|
13
|
+
it 'should return the pitch class given to #initialize' do
|
14
|
+
@scale.pitch_class.should eq(@pc)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#size' do
|
19
|
+
it 'should return the size of the scale intervals' do
|
20
|
+
@scale.size.should eq(@intervals.size)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#transpose' do
|
25
|
+
before :all do
|
26
|
+
@diff2 = 3
|
27
|
+
@scale2 = @scale.transpose(@diff2)
|
28
|
+
@diff3 = -5
|
29
|
+
@scale3 = @scale.transpose(@diff3)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should return a new Scale' do
|
33
|
+
@scale2.should be_a Scale
|
34
|
+
@scale2.should_not be @scale
|
35
|
+
@scale3.should be_a Scale
|
36
|
+
@scale3.should_not be @scale
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should return a scale with same intervals' do
|
40
|
+
@scale2.intervals.should eq @scale.intervals
|
41
|
+
@scale3.intervals.should eq @scale.intervals
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should return a scale with a shifted pitch class' do
|
45
|
+
@scale2.pitch_class.should eq((@scale.pitch_class + @diff2).to_pc)
|
46
|
+
@scale3.pitch_class.should eq((@scale.pitch_class + @diff3).to_pc)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#rotate' do
|
51
|
+
before :all do
|
52
|
+
@n2 = 5
|
53
|
+
@scale2 = @scale.rotate(@n2)
|
54
|
+
@n3 = -3
|
55
|
+
@scale3 = @scale.rotate(@n3)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should return a new Scale' do
|
59
|
+
@scale2.should be_a Scale
|
60
|
+
@scale2.should_not be @scale
|
61
|
+
@scale3.should be_a Scale
|
62
|
+
@scale3.should_not be @scale
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should return a scale with rotated intervals' do
|
66
|
+
@scale2.intervals.should eq @scale.intervals.rotate(@n2)
|
67
|
+
@scale3.intervals.should eq @scale.intervals.rotate(@n3)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should return a scale with a shifted pitch class' do
|
71
|
+
pc2 = (AddingSequence.new(@scale.intervals).at(@n2) + @scale.pitch_class).to_pc
|
72
|
+
@scale2.pitch_class.should eq(pc2)
|
73
|
+
pc3 = (AddingSequence.new(@scale.intervals).at(@n3) + @scale.pitch_class).to_pc
|
74
|
+
@scale3.pitch_class.should eq(pc3)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#at_octave' do
|
79
|
+
before :all do
|
80
|
+
@octave = 2
|
81
|
+
@pitch_seq = @scale.at_octave(@octave)
|
82
|
+
@start_pitch = @pitch_seq.at(0)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should return a bi-infinite sequence' do
|
86
|
+
@pitch_seq.should be_a BiInfiniteSequence
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should start sequence at scale pitch class and given octave' do
|
90
|
+
@start_pitch.semitone.should eq(@scale.pitch_class)
|
91
|
+
@start_pitch.octave.should eq(@octave)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should make sequence that proceeds forwards along scale intervals' do
|
95
|
+
first_pitches = @pitch_seq.over(0..@scale.intervals.size).to_a
|
96
|
+
first_pitches[1..-1].each_with_index do |pitch,i|
|
97
|
+
diff = pitch.diff(first_pitches[i])
|
98
|
+
diff.should eq(@scale.intervals[i])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should make sequence that proceeds backwards along scale intervals' do
|
103
|
+
first_pitches = @pitch_seq.over(-@scale.intervals.size..0).to_a
|
104
|
+
first_pitches[1..-1].each_with_index do |pitch,i|
|
105
|
+
diff = pitch.diff(first_pitches[i])
|
106
|
+
diff.should eq(@scale.intervals[i])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe 'make_note' do
|
4
|
+
it 'should return a Note' do
|
5
|
+
make_note(0.3,C3).should be_a Note
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'given single pitch' do
|
9
|
+
it 'should have the given duration and pitch' do
|
10
|
+
n = make_note(0.3,C3)
|
11
|
+
n.duration.should eq(0.3)
|
12
|
+
n.pitches.should eq([C3])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'given pitch array' do
|
17
|
+
it 'should have the given duration and pitches' do
|
18
|
+
n = make_note(0.2,[C3,E3,Ab3])
|
19
|
+
n.duration.should eq(0.2)
|
20
|
+
n.pitches.should eq([C3,E3,Ab3])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'given negative duration' do
|
25
|
+
it 'should have duration with same magnitude' do
|
26
|
+
make_note(-0.3,C3).duration.should eq(0.3)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should make a rest note (no pitches)' do
|
30
|
+
make_note(-0.3,[C3,E3]).pitches.should be_empty
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'make_notes' do
|
36
|
+
context 'given empty rhythm or pitch_groups' do
|
37
|
+
it 'should raise EmptyError' do
|
38
|
+
expect do
|
39
|
+
make_notes([],[A3,B3,C3])
|
40
|
+
end.to raise_error(EmptyError)
|
41
|
+
expect do
|
42
|
+
make_notes([2,2],[])
|
43
|
+
end.to raise_error(EmptyError)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'given equal length rhtyhm and pitch_groups' do
|
48
|
+
it 'should produce same number of notes as both' do
|
49
|
+
make_notes([2,2,5],[A1,B1,C1]).size.should eq(3)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'given longer rhythm than pitch_groups' do
|
54
|
+
before :all do
|
55
|
+
@rhythm = [4,3,3,1]
|
56
|
+
@pitch_groups = [[C1],[E2,G2]]
|
57
|
+
@notes = make_notes(@rhythm,@pitch_groups)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should produce same number of notes as rhythm.size' do
|
61
|
+
@notes.size.should eq(@rhythm.size)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should follow entire rhythm once' do
|
65
|
+
@notes.map {|n| n.duration}.should eq(@rhythm)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should cycle through pitch groups as necesary' do
|
69
|
+
@notes.map {|n| n.pitches}.should eq(@pitch_groups*2)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'given longer pitch_groups than rhythm' do
|
74
|
+
before :all do
|
75
|
+
@rhythm = [4,3,1]
|
76
|
+
@pitch_groups = [[C1],[E2,G2],[F5,G5,A5],[F4],[Eb4],[G4]]
|
77
|
+
@notes = make_notes(@rhythm,@pitch_groups)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should produce same number of notes as pitch_groups.size' do
|
81
|
+
@notes.size.should eq(@pitch_groups.size)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should follow entire pitch_groups once' do
|
85
|
+
@notes.map {|n| n.pitches}.should eq(@pitch_groups)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should cycle through rhythm as necesary' do
|
89
|
+
@notes.map {|n| n.duration}.should eq(@rhythm*2)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'given same-length pitch_groups and rhythm' do
|
94
|
+
before :all do
|
95
|
+
@rhythm = [4,3,1]
|
96
|
+
@pitch_groups = [[F4],[Eb4],[G4]]
|
97
|
+
@notes = make_notes(@rhythm,@pitch_groups)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should produce same number of notes as rhythm.size and pitch_groups.size' do
|
101
|
+
@notes.size.should eq(@pitch_groups.size)
|
102
|
+
@notes.size.should eq(@rhythm.size)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should follow entire rhythm once' do
|
106
|
+
@notes.map {|n| n.pitches}.should eq(@pitch_groups)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should follow entire pitch groups once' do
|
110
|
+
@notes.map {|n| n.duration}.should eq(@rhythm)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe 'transpose' do
|
4
|
+
it 'should map given notes to new notes, transposing by given diff' do
|
5
|
+
notes = "/4A2,C2 /4D2,F2,Gb2 /8 /8E4".to_notes
|
6
|
+
semitones = 3
|
7
|
+
notes2 = transpose(notes,semitones)
|
8
|
+
|
9
|
+
notes2.size.should eq(notes.size)
|
10
|
+
notes2.each_index do |i|
|
11
|
+
notes2[i].pitches.each_with_index do |pitch2,j|
|
12
|
+
pitch = notes[i].pitches[j]
|
13
|
+
pitch2.diff(pitch).should eq(semitones)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe AddingSequence do
|
4
|
+
describe '#initialize' do
|
5
|
+
context 'given an empty pattern' do
|
6
|
+
it 'should raise EmptyError' do
|
7
|
+
expect do
|
8
|
+
AddingSequence.new([])
|
9
|
+
end.to raise_error(EmptyError)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#pattern_size' do
|
15
|
+
it 'should return the pattern size' do
|
16
|
+
AddingSequence.new([1,2,3,4,5]).pattern_size.should eq(5)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
before :all do
|
21
|
+
@pattern = [2,-1,5,-4,3]
|
22
|
+
@start_value = 13
|
23
|
+
@seq = AddingSequence.new(@pattern,@start_value)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#at' do
|
27
|
+
context 'given single offset' do
|
28
|
+
context 'given offset of 0' do
|
29
|
+
it 'should return the start value' do
|
30
|
+
[ 0, -3, 7].each do |start_val|
|
31
|
+
AddingSequence.new(@pattern,start_val).at(0).should eq(start_val)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'given offset > 0' do
|
37
|
+
it 'should keep adding on pattern elements to start_val until the given offset is reached' do
|
38
|
+
[1,2,3,5,8,15,45].each do |offset|
|
39
|
+
val = @seq.at(offset)
|
40
|
+
rep_seq = RepeatingSequence.new(@pattern)
|
41
|
+
val2 = rep_seq.take(offset).inject(@start_value,:+)
|
42
|
+
val.should eq(val2)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'given offset < 0' do
|
48
|
+
it 'should keep suctracting pattern elements from start_val until the given offset is reached' do
|
49
|
+
[-1,-2,-3,-5,-8,-15,-45].each do |offset|
|
50
|
+
val = @seq.at(offset)
|
51
|
+
rep_seq = RepeatingSequence.new(@pattern)
|
52
|
+
val2 = rep_seq.take_back(-offset).inject(@start_value,:-)
|
53
|
+
val.should eq(val2)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'given array of offsets' do
|
60
|
+
context 'not given block' do
|
61
|
+
it 'should return enumerator' do
|
62
|
+
@seq.at([1,2,3]).should be_a Enumerator
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'given block' do
|
67
|
+
it 'should yield sequence value for each offset' do
|
68
|
+
[ (0..@seq.pattern_size).to_a, (-@seq.pattern_size..0).to_a,
|
69
|
+
[-5,11,0,-33,2,15,-8] ].each do |offsets|
|
70
|
+
@seq.at(offsets).each_with_index do |val,i|
|
71
|
+
val.should eq(@seq.at(offsets[i]))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#take' do
|
80
|
+
context 'given negative integer' do
|
81
|
+
it 'should raise NegativeError' do
|
82
|
+
expect { @seq.take(-1) }.to raise_error(NegativeError)
|
83
|
+
expect { @seq.take(-10) }.to raise_error(NegativeError)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'given 0' do
|
88
|
+
it 'should return empty array' do
|
89
|
+
@seq.take(0).to_a.should eq([])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'given positive integer' do
|
94
|
+
context 'given block' do
|
95
|
+
it 'should yield the given number of sequence elements in forward direction (repeating as necessary)' do
|
96
|
+
i, m = 0, @pattern.size*2+3
|
97
|
+
@seq.take(m) do |n|
|
98
|
+
n.should eq(@seq.at(i))
|
99
|
+
i += 1
|
100
|
+
end
|
101
|
+
i.should eq(m)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'no block given' do
|
106
|
+
it 'should return an enumerator' do
|
107
|
+
@seq.take(20).should be_a Enumerator
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#take_back' do
|
114
|
+
context 'given negative integer' do
|
115
|
+
it 'should raise NegativeError' do
|
116
|
+
expect { @seq.take_back(-1) }.to raise_error(NegativeError)
|
117
|
+
expect { @seq.take_back(-10) }.to raise_error(NegativeError)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'given 0' do
|
122
|
+
it 'should return empty array' do
|
123
|
+
@seq.take_back(0).to_a.should eq([])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'given positive integer' do
|
128
|
+
context 'given block' do
|
129
|
+
it 'should yield the given number of pattern elements in backward direction (repeating as necessary)' do
|
130
|
+
i, m = 0, @pattern.size*2+3
|
131
|
+
@seq.take_back(m) do |n|
|
132
|
+
n.should eq(@seq.at(i-1))
|
133
|
+
i -= 1
|
134
|
+
end
|
135
|
+
(-i).should eq(m)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'no block given' do
|
140
|
+
it 'should return an enumerator' do
|
141
|
+
@seq.take_back(20).should be_a Enumerator
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#over' do
|
148
|
+
context 'given empty (invalid) range' do
|
149
|
+
it 'should raise EmptyError' do
|
150
|
+
[ 3...-2, 0...0, 5..2, -3..-5 ].each do |range|
|
151
|
+
expect { @seq.over(range) }.to raise_error(EmptyError)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'given range over positive indices' do
|
157
|
+
it 'should return seq values at all offsets in range' do
|
158
|
+
[ 0..0, 0..2, 1...10, 4..17 ].each do |range|
|
159
|
+
vals = @seq.over(range).to_a
|
160
|
+
vals2 = range.map {|i| @seq.at(i) }
|
161
|
+
vals.should eq(vals2)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'given negative min and/or max' do
|
167
|
+
it 'should return seq values at all offsets in range' do
|
168
|
+
[ -5..2, -10..-7, -1...1 ].each do |range|
|
169
|
+
vals = @seq.over(range).to_a
|
170
|
+
vals2 = range.map {|i| @seq.at(i) }
|
171
|
+
vals.should eq(vals2)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|