class Engine
Public Class Methods
run(hits:, pulses:)
click to toggle source
The magickal euclidean algorithmical engine!
# File lib/euclidean_sequencer/engine.rb, line 4 def self.run(hits:, pulses:) euclid(hits, pulses) end
Private Class Methods
distribute_and_pop_sequence(sequence, zero_sets, remainder, denominator)
click to toggle source
# File lib/euclidean_sequencer/engine.rb, line 76 def self.distribute_and_pop_sequence(sequence, zero_sets, remainder, denominator) # Choose the approiate counter for the current size of the sequence: distributions = (remainder > denominator) ? denominator : remainder distributions.times do |distribution| zero_sets.times do sequence[distribution] << sequence.pop unless sequence[distribution].nil? end end sequence end
euclid(hits, pulses, sequence = nil)
click to toggle source
# File lib/euclidean_sequencer/engine.rb, line 10 def self.euclid(hits, pulses, sequence = nil) # Contruct the sequence if new otherwise continue with current sequence: sequence ||= generate_sequence(hits, pulses) # Determine if the algorithm has concluded or another iteration is neccessy: return sequence.sum('') if hits == 0 # Count the sequence element types (remainder or denominator) for distribution: remainder, denominator = find_remainder_and_denominator(sequence) # Determine how many sets of zeros can be nabbed from the pulses at a time, # insuring all the zeros get used up before the algorithm ends. zero_sets = find_zero_sets(hits, pulses, remainder, denominator, sequence.length) # Distribute the remainder into the denominator and pop the unnecessary elements: sequence = distribute_and_pop_sequence(sequence, zero_sets, remainder, denominator) # Recursion: return euclid(pulses % hits, hits, sequence) end
find_remainder_and_denominator(sequence)
click to toggle source
# File lib/euclidean_sequencer/engine.rb, line 38 def self.find_remainder_and_denominator(sequence) remainder, denominator = [0,0] sequence.each do |element| if element == sequence.last remainder = remainder + 1 else denominator = denominator + 1 end end [remainder,denominator] end
find_zero_sets(hits, pulses, remainder, denominator, sequence_length)
click to toggle source
# File lib/euclidean_sequencer/engine.rb, line 50 def self.find_zero_sets(hits, pulses, remainder, denominator, sequence_length) if pulses > hits * 2 # Must avoid division by zero: unless hits == 0 if denominator == 0 zero_sets = 1 else # Grab as many zeros as possible: zero_sets = remainder / denominator end # But don't grab as many zeros as impossible: if zero_sets > sequence_length zero_sets = 0 end # Scrapping the bottom of the zero barrel: if zero_sets == 0 and remainder > 1 zero_sets = 1 end end else # The ratio of hits to pulses isn't so low as to start hording zeros: zero_sets = 1 end zero_sets end
generate_sequence(hits, pulses)
click to toggle source
# File lib/euclidean_sequencer/engine.rb, line 31 def self.generate_sequence(hits, pulses) sequence = [] pulses.times {|pulse| sequence[pulse] = "0"} hits.times { |hit| sequence[hit] = "1" } sequence end