1f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
2f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# iExploder Combination Scanner Library (used for subtesting)
3f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
4f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# Copyright 2010 Thomas Stromberg - All Rights Reserved.
5f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
6f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# Licensed under the Apache License, Version 2.0 (the "License");
7f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# you may not use this file except in compliance with the License.
8f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# You may obtain a copy of the License at
9f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
10f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#      http://www.apache.org/licenses/LICENSE-2.0
11f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch#
12f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# Unless required by applicable law or agreed to in writing, software
13f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# distributed under the License is distributed on an "AS IS" BASIS,
14f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# See the License for the specific language governing permissions and
16f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# limitations under the License.
17f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
18f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
19f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# This is a simple sequential combination creator with a constantly growing width
20f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochdef seq_combo_creator(total_lines, width, offset)
21f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  # Offsets start at 0 to total_lines-1
22f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  use_lines = []
23f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  offset.upto(offset+width-1) do |line_number|
24f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    use_lines << line_number
25f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
26f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
27f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  if use_lines[-1] == total_lines-1
28f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    width += 1
29f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    next_offset = 0
30f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  else
31f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    next_offset = offset + 1
32f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
33f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  return [width, next_offset, use_lines]
34f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochend
35f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
36f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# This tries all combinations, giving a small test-case, but requires lots of
37f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# subtests.
38f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochdef combine_combo_creator(total_lines, width, offsets)
39f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  #  puts "Asked: Total Lines: #{total_lines} Line Count: #{width} Offsets: #{offsets.join(',')}"
40f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  if not offsets or offsets.length == 0
41f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    #    puts "  Setting offsets to 0"
42f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    offsets = [0,]
43f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
44f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  if width < 1
45f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    width = 1
46f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
47f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
48f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  index = 0
49f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  lines = []
50f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  new_offsets = []
51f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  reset_next_offset = false
52f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
53f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  # Reverse the order from fastest to slowest
54f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  offsets.each_with_index do |offset, index|
55f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    0.upto(width-1) do |line_offset|
56f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      lines << (offset + line_offset)
57f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
58f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if lines[-1] >= total_lines - 1
59f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # If we are the slowest, that means we are done with this iteration.
60f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if index == offsets.length - 1
61f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        new_offset_count = offsets.length + 1
62f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        # Loosely follow the Fibonacci sequence when calculating width
63f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        width = (new_offset_count * 1.61803398).round
64f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        new_offsets = []
65f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        # 0 to offsets.length creates one additional offset
66f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        0.upto(offsets.length) do |new_offset_num|
67f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch          new_offsets << (new_offset_num * width)
68f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        end
69f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
70f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        # We need the lowest offset first. Oops.
71f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        new_offsets.reverse!
72f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      else
73f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        # Move to the next available slot.. next offset will take the one before.
74f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        new_offsets << offsets[index+1] + (width * 2)
75f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        reset_next_offset = true
76f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
77f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    elsif reset_next_offset
78f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      new_offsets << (offset + width)
79f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      reset_next_offset = false
80f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # The first one always rotates
81f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    elsif index == 0
82f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      new_offsets << (offset + width)
83f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      reset_next_offset = false
84f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      # The others stay still normally.
85f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    else
86f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      new_offsets << offset
87f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      reset_next_offset = false
88f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
89f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
90f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
91f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  return [width, new_offsets, lines]
92f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochend
93f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
94f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochdef avg(list)
95f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  sum = list.inject(0.0) { |sum, el| sum += el }
96f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  return sum / list.length
97f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochend
98f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
99f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
100f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch# for testing #################################################################
101f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochif $0 == __FILE__
102f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  line_count = ARGV[0].to_i || 100
103f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  try_count = ARGV[1].to_i || 10
104f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
105f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  seq_iterations = []
106f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  combine_iterations = []
107f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  seq_size = []
108f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  combine_size = []
109f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
110f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  1.upto(try_count) do |run|
111f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    puts "*" * 78
112f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    puts "# RUN #{run} (line-count: #{line_count})"
113f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    find_lines = []
114f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    0.upto(rand(4)) do |count|
115f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      choice = rand(line_count).to_i
116f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      if ! find_lines.include?(choice)
117f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch        find_lines << choice
118f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      end
119f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
120f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
121f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    lines = []
122f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    width = 1
123f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    offset = 0
124f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    attempts = 0
125f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    puts "Find lines: #{find_lines.join(',')}"
126f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    while not find_lines.all? { |x| lines.include?(x) }
127f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch     (width, offset, lines) = seq_combo_creator(line_count, width, offset)
128f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      attempts += 1
129f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
130f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    puts "Sequential found #{find_lines.join(',')} in #{attempts} attempts with #{lines.length} total lines."
131f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    seq_iterations << attempts
132f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    seq_size << lines.length
133f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
134f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    lines = []
135f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    width = 1
136f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    offsets = []
137f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    attempts = 0
138f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    while not find_lines.all? { |x| lines.include?(x) }
139f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      #    puts "Looking for #{find_lines.join(',')}"
140f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch       (width, offsets, lines) = combine_combo_creator(line_count, width, offsets)
141f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch      attempts += 1
142f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    end
143f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    puts "Combine found #{find_lines.join(',')} in #{attempts} attempts with #{lines.length} total lines."
144f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    combine_iterations << attempts
145f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    combine_size << lines.length
146f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  end
147f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  puts "-" * 78
148f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  puts "Seq avg iterations=#{avg(seq_iterations).to_i} length=#{avg(seq_size).to_i}"
149f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  puts "combine avg iterations=#{avg(combine_iterations).to_i} length=#{avg(combine_size).to_i}"
150f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  diff_iter = (avg(combine_iterations) / avg(seq_iterations)) * 100
151f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  diff_lines = (avg(combine_size) / avg(seq_size)) * 100
152f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  puts "Diff iterations: #{diff_iter}%"
153f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch  puts "Diff lines: #{diff_lines}%"
154f05b935882198ccf7d81675736e3aeb089c5113aBen Murdochend
155