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
input = DATA
  .readlines(chomp: true).flat_map.with_index {|row, y|
    row.chars.map.with_index {|i, x|
      [[y,x], i]
    }
  }.to_h

freqs = input.values.uniq - %w[.]
antennae = freqs.to_h {|freq|
  [freq, input.select { _2 == freq }.keys]
}

pp antennae.values.flat_map {|yxs|
  yxs.combination(2).flat_map {|(ay,ax),(by,bx)|
    dy = by - ay
    dx = bx - ax
    [[ay-dy, ax-dx], [by+dy, bx+dx]]
  }.select {|yx|
    input.has_key?(yx)
  }
}.uniq.length

def antinodes(input, a, b)
  return enum_for(__method__, input, a, b) unless block_given?

  max_y, max_x = input.keys.transpose.map(&:max)

  r = Rational(*b.zip(a).map { _1 - _2 })
  delta = r.numerator, r.denominator

  yx = a
  loop do
    break unless input.has_key?(yx)
    yield yx
    yx = yx.zip(delta).map { _1 - _2 }
  end

  yx = b
  loop do
    break unless input.has_key?(yx)
    yield yx
    yx = yx.zip(delta).map { _1 + _2 }
  end
end

pp antennae.values.flat_map {|yxs|
  yxs.combination(2).flat_map {|a,b|
    antinodes(input, a, b).to_a
  }
}.uniq.length

__END__