blob: 9a8261cf72c023eb3b200beacf64b1fd10f37343 [file] [log] [blame]
#
# iExploder Combination Scanner Library (used for subtesting)
#
# Copyright 2010 Thomas Stromberg - All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This is a simple sequential combination creator with a constantly growing width
def seq_combo_creator(total_lines, width, offset)
# Offsets start at 0 to total_lines-1
use_lines = []
offset.upto(offset+width-1) do |line_number|
use_lines << line_number
end
if use_lines[-1] == total_lines-1
width += 1
next_offset = 0
else
next_offset = offset + 1
end
return [width, next_offset, use_lines]
end
# This tries all combinations, giving a small test-case, but requires lots of
# subtests.
def combine_combo_creator(total_lines, width, offsets)
# puts "Asked: Total Lines: #{total_lines} Line Count: #{width} Offsets: #{offsets.join(',')}"
if not offsets or offsets.length == 0
# puts " Setting offsets to 0"
offsets = [0,]
end
if width < 1
width = 1
end
index = 0
lines = []
new_offsets = []
reset_next_offset = false
# Reverse the order from fastest to slowest
offsets.each_with_index do |offset, index|
0.upto(width-1) do |line_offset|
lines << (offset + line_offset)
end
if lines[-1] >= total_lines - 1
# If we are the slowest, that means we are done with this iteration.
if index == offsets.length - 1
new_offset_count = offsets.length + 1
# Loosely follow the Fibonacci sequence when calculating width
width = (new_offset_count * 1.61803398).round
new_offsets = []
# 0 to offsets.length creates one additional offset
0.upto(offsets.length) do |new_offset_num|
new_offsets << (new_offset_num * width)
end
# We need the lowest offset first. Oops.
new_offsets.reverse!
else
# Move to the next available slot.. next offset will take the one before.
new_offsets << offsets[index+1] + (width * 2)
reset_next_offset = true
end
elsif reset_next_offset
new_offsets << (offset + width)
reset_next_offset = false
# The first one always rotates
elsif index == 0
new_offsets << (offset + width)
reset_next_offset = false
# The others stay still normally.
else
new_offsets << offset
reset_next_offset = false
end
end
return [width, new_offsets, lines]
end
def avg(list)
sum = list.inject(0.0) { |sum, el| sum += el }
return sum / list.length
end
# for testing #################################################################
if $0 == __FILE__
line_count = ARGV[0].to_i || 100
try_count = ARGV[1].to_i || 10
seq_iterations = []
combine_iterations = []
seq_size = []
combine_size = []
1.upto(try_count) do |run|
puts "*" * 78
puts "# RUN #{run} (line-count: #{line_count})"
find_lines = []
0.upto(rand(4)) do |count|
choice = rand(line_count).to_i
if ! find_lines.include?(choice)
find_lines << choice
end
end
lines = []
width = 1
offset = 0
attempts = 0
puts "Find lines: #{find_lines.join(',')}"
while not find_lines.all? { |x| lines.include?(x) }
(width, offset, lines) = seq_combo_creator(line_count, width, offset)
attempts += 1
end
puts "Sequential found #{find_lines.join(',')} in #{attempts} attempts with #{lines.length} total lines."
seq_iterations << attempts
seq_size << lines.length
lines = []
width = 1
offsets = []
attempts = 0
while not find_lines.all? { |x| lines.include?(x) }
# puts "Looking for #{find_lines.join(',')}"
(width, offsets, lines) = combine_combo_creator(line_count, width, offsets)
attempts += 1
end
puts "Combine found #{find_lines.join(',')} in #{attempts} attempts with #{lines.length} total lines."
combine_iterations << attempts
combine_size << lines.length
end
puts "-" * 78
puts "Seq avg iterations=#{avg(seq_iterations).to_i} length=#{avg(seq_size).to_i}"
puts "combine avg iterations=#{avg(combine_iterations).to_i} length=#{avg(combine_size).to_i}"
diff_iter = (avg(combine_iterations) / avg(seq_iterations)) * 100
diff_lines = (avg(combine_size) / avg(seq_size)) * 100
puts "Diff iterations: #{diff_iter}%"
puts "Diff lines: #{diff_lines}%"
end