summaryrefslogtreecommitdiff
path: root/test/did_you_mean/tree_spell/human_typo.rb
blob: 9d410a17c427d3abd0bcabb761ba2e13cd6c58f0 (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
# module for classes needed to test TreeSpellChecker
module TreeSpell
  require_relative 'change_word'
  # Simulate an error prone human typist
  # see doc/human_typo_api.md for the api description
  class HumanTypo
    POPULAR_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?<>,.!`+=-_":;@#$%^&*()'.split("").freeze
    ACTION_TYPES  = %i(insert transpose delete substitute).freeze

    def initialize(input, lambda: 0.05)
      @input = input
      check_input
      @len = input.length
      @lambda = lambda
    end

    def call
      @word = input.dup
      i_place = initialize_i_place
      loop do
        action = ACTION_TYPES.sample
        @word = make_change action, i_place
        @len = word.length
        i_place += exponential
        break if i_place >= len
      end
      word
    end

    private

    attr_accessor :input, :word, :len, :lambda

    def initialize_i_place
      i_place = nil
      loop do
        i_place = exponential
        break if i_place < len
      end
      i_place
    end

    def exponential
      (rand / (lambda / 2)).to_i
    end

    def make_change(action, i_place)
      cw = ChangeWord.new(word)
      case action
      when :delete
        cw.deletion(i_place)
      when :insert
        cw.insertion(i_place, POPULAR_CHARS.sample)
      when :substitute
        cw.substitution(i_place, POPULAR_CHARS.sample)
      when :transpose
        cw.transposition(i_place, rand >= 0.5 ? +1 : -1)
      end
    end

    def check_input
      fail check_input_message if input.nil? || input.length < 5
    end

    def check_input_message
      "input length must be greater than 5 characters: #{input}"
    end
  end
end