summaryrefslogtreecommitdiffstats
path: root/Tools/iExploder/iexploder-1.7.2/src/scanner.rb
blob: 9a8261cf72c023eb3b200beacf64b1fd10f37343 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#
# 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