ruby-anagrams 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b4354f4fdcda04c67e8f3b2e11afbe9b6999fc22
4
+ data.tar.gz: 7cf6680ff7f4de0aa5aff21768852fab28e51d7a
5
+ SHA512:
6
+ metadata.gz: 28216d0fefce0b01b630e56e7eec4d9cf330183c7e76666cd94db7bd17fd8bd8dc5b7a14d7c6121069ec35a70bfce2952ca5aac6780f20b27582e192339b3ada
7
+ data.tar.gz: dceb898b96b5f0b67cdbd014fb1d8bde14586725ed18c35f4f5d6128037c725cf3c3f027067def25a37502b4e26f5be3c131cdbf588c62cf110c0ccb549b01b9
@@ -0,0 +1,91 @@
1
+ module RubyAnagrams
2
+ # Provides anagram solver behavior to the trie data structure.
3
+ # @author Connor Lay
4
+ module Anagrams
5
+ require 'prime'
6
+
7
+ # A Hash associating symbols with unique prime numbers.
8
+ SYM_PRIMES = (:a..:z).to_a.zip(Prime.first(26)).to_h
9
+
10
+ # Returns all words in the trie data structure that are anagrams
11
+ # of the string provided. "*" indicates a wildcard.
12
+ # @note Default behavior finds complete anagrams that use every character
13
+ # in the string. Partial anagrams can be included by setting include_partial
14
+ # to true.
15
+ # @example without partial anagrams
16
+ # root << "rise"
17
+ # root << "sire"
18
+ # root << "rie"
19
+ # #anagrams "rise" #=> ['rise', 'sire']
20
+ # @example with partial anagrams
21
+ # root << "rise"
22
+ # root << "sire"
23
+ # root << "rie"
24
+ # #anagrams "rise", include_partial: true #=> ['rie', 'rise', 'sire']
25
+ # @example with wildcards
26
+ # root << "bin"
27
+ # root << "ban"
28
+ # root << "bun"
29
+ # #anagrams "b*n" #=> ['ban', 'bin', 'bun']
30
+ # @param string [String] the string to find anagrams of.
31
+ # @param include_partial [Boolean] include partial anagrams?
32
+ # @return [Array<String>] all anagrams of the given string.
33
+ def anagrams string, include_partial: false
34
+ symbols = str_to_sym_a string
35
+ anagrams = []
36
+ find_symbol_permutations(symbols).each do |permutation|
37
+ anagrams.concat find_anagrams(as_product(permutation), include_partial: include_partial)
38
+ end
39
+ anagrams.uniq.sort
40
+ end
41
+
42
+ protected
43
+ # Returns anagrams in the trie data structure by performing depth-first search,
44
+ # following subtrees whose symbol's associated prime number is a prime factor
45
+ # of the product representing a string. The words of visited terminal nodes
46
+ # are returned as an array.
47
+ # @note Default behavior finds words that consume all prime factors
48
+ # of the given product. Words that do not consume all prime factors can
49
+ # be included by setting include_partial to true.
50
+ # @param product [Integer] the product representing the string to find
51
+ # anagrams of.
52
+ # @param include_partial [Boolean] include partial anagrams?
53
+ # @return [Array<String>] all anagrams whose product divides into the given
54
+ # product.
55
+ def find_anagrams product, include_partial: false
56
+ anagrams = []
57
+ anagrams << word if terminal? && (include_partial ? true : product == 1)
58
+ @children.each do |symbol,child|
59
+ if product % SYM_PRIMES[symbol] == 0
60
+ anagrams += child.find_anagrams(product / SYM_PRIMES[symbol], include_partial: include_partial)
61
+ end
62
+ end
63
+ anagrams
64
+ end
65
+
66
+ # Returns all permutations of the given set of symbols, where "*" wildcards
67
+ # are replaced with all possible permutations of symbols in the trie's
68
+ # alphabet.
69
+ # @example with 1 wildcard
70
+ # alphabet = [:a, :b, :c]
71
+ # #find_symbol_permutations [:a, :*, :c] #=> [[:a, :a, :c], [:a, :b, :c], [:a, :c, :c]]
72
+ # @param symbols [Array<Symbol>] the symbols to permute.
73
+ # @return [Array<Array<Symbol>>] all permutations of the symbols.
74
+ def find_symbol_permutations symbols
75
+ symbol_permutations = []
76
+ non_wild_symbols = symbols.reject{|s| s == :*}
77
+ (:a..:z).to_a.repeated_permutation(symbols.count :*).each do |permutation|
78
+ symbol_permutations << non_wild_symbols + permutation
79
+ end
80
+ symbol_permutations.empty? ? [symbols] : symbol_permutations
81
+ end
82
+
83
+ # Returns the product of each symbol's associated prime number.
84
+ # @param symbols [Array<Symbol>] the symbols to multiply.
85
+ # @return [Integer] the product of each symbol's associated prime number.
86
+ def as_product symbols
87
+ symbols.inject(1) { |acc,sym| acc * SYM_PRIMES[sym] }
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,25 @@
1
+ module RubyAnagrams
2
+ # Provides enumerable behavior to the trie data structure.
3
+ # @author Connor Lay
4
+ module Enumerable
5
+
6
+ include ::Enumerable
7
+
8
+ # Calls a block for each word in the trie data strucutre. If no block is given,
9
+ # an Enumerator is returned.
10
+ # @return [Enumerator] the enumerator for the words in the trie data structure.
11
+ def each &block
12
+ enumerator = Enumerator.new do |y|
13
+ y << word if terminal?
14
+ @children.each_value do |child|
15
+ child.each { |word| y << word }
16
+ end
17
+ end
18
+ block.nil? ? enumerator : enumerator.each(&block)
19
+ end
20
+
21
+ alias :size :count
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,71 @@
1
+ module RubyAnagrams
2
+ # A representation of a Node in the trie data structure.
3
+ # @author Connor Lay
4
+ class Node
5
+
6
+ include Subtrees
7
+ include Enumerable
8
+ include Anagrams
9
+
10
+ attr_reader :symbol, :children, :parent
11
+
12
+ # Creates a new, non-terminal node with no children.
13
+ # @param symbol [Symbol, nil] the symbol the node represents.
14
+ # @param parent [Node, nil] the parent of the node.
15
+ # @return [Node] the node just created.
16
+ def initialize symbol = nil, parent = nil
17
+ @symbol = symbol
18
+ @parent = parent
19
+ @children = {}
20
+ @terminal = false
21
+ end
22
+
23
+ # Adds a new child node representing a symbol to this node's children.
24
+ # @param symbol [Symbol] the symbol the node represents.
25
+ # @param child [Node] the node to add as a child.
26
+ # @return [Node] the node just added.
27
+ def []= symbol, child
28
+ @children[symbol] = child
29
+ end
30
+
31
+ # Returns the child node associated with a symbol.
32
+ # @param symbol [Symbol]
33
+ # @return [Node] the child node associated with the symbol.
34
+ def [] symbol
35
+ @children[symbol]
36
+ end
37
+
38
+ # Sets the node as a terminal.
39
+ def terminal!
40
+ @terminal = true
41
+ end
42
+
43
+ # Is the node a terminal?
44
+ # @return [Boolean] true if the node is a terminal, false otherwise
45
+ def terminal?
46
+ @terminal
47
+ end
48
+
49
+ # Returns the word the node represents based on its symbol and
50
+ # the symbols of its parents.
51
+ # @example called on the node representing :e
52
+ # :a
53
+ # :p
54
+ # :p
55
+ # :l
56
+ # :e
57
+ # #word #=> "apple"
58
+ # @return [String] the word the node represents.
59
+ def word
60
+ @parent ? "#{@parent.word}#{@symbol}" : "#{@symbol}"
61
+ end
62
+
63
+ def inspect
64
+ "<#{self.class}:#{self.object_id}, "\
65
+ "@symbol=#{@symbol ? ":#{@symbol}" : "nil"}, "\
66
+ "@word=#{terminal? ? word : "nil"}, "\
67
+ "@children=#{@children.each_key.to_a}>"
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,44 @@
1
+ # Namespace for the Ruby-Anagrams gem.
2
+ module RubyAnagrams
3
+ # A representation of the Root node of the trie data structure.
4
+ # @author Connor Lay
5
+ class Root < Node
6
+ # Creates a new trie.
7
+ # @param path [String, nil] the path to a dictionary text file.
8
+ # @return [Root] the Root node of the trie just created.
9
+ def initialize path = nil
10
+ super()
11
+ if path
12
+ file = File.open path, "r"
13
+ file.each_line { |line| self << line.downcase.strip }
14
+ file.close
15
+ end
16
+ end
17
+
18
+ # Adds a word to the trie.
19
+ # @param word [String] the new word to add to the trie.
20
+ # @return [String] the word just added to the trie.
21
+ def << word
22
+ symbols = str_to_sym_a word
23
+ add_to_subtree symbols
24
+ end
25
+
26
+ # If the trie contains the word.
27
+ # @param word [String] the word to search for.
28
+ # @return [Boolean] true if the word is found, false otherwise.
29
+ def include? word
30
+ symbols = str_to_sym_a word
31
+ search_subtree symbols
32
+ end
33
+
34
+ # Returns a symbol array based on the chatacters of a string.
35
+ # @example
36
+ # str_to_sym_a "apple" #=> [:a, :p, :p, :l, :e]
37
+ # @param string [String] the string to process
38
+ # @return [Array<Symbol>] an array of symbols
39
+ def str_to_sym_a string
40
+ string.chars.map { |char| char.to_sym }
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ module RubyAnagrams
2
+ # Provides subtree branching and searching behavior to the trie data strucutre.
3
+ # @author Connor Lay
4
+ module Subtrees
5
+
6
+ # Returns the number of nodes in the subtree. When called on Root, returns
7
+ # the number of nodes in the entire trie data structure.
8
+ # @return [Integer] the number of nodes in the subtree.
9
+ def node_count
10
+ nodes = 1
11
+ @children.each_value do |child|
12
+ nodes += child.node_count
13
+ end
14
+ nodes
15
+ end
16
+
17
+ protected
18
+ # Descends the trie data structure, adding nodes when needed, for a given
19
+ # sequence of symbols. The last node visited is designated as a terminal.
20
+ # @param symbols [Array<Symbol>] the symbol sequence to follow.
21
+ # @return [String] the word represented by the last node visited.
22
+ def add_to_subtree symbols
23
+ if symbols.empty?
24
+ terminal!
25
+ return word
26
+ end
27
+ s = symbols.slice! 0
28
+ unless child = @children[s]
29
+ child = Node.new s, self
30
+ @children[s] = child
31
+ end
32
+ child.add_to_subtree symbols
33
+ end
34
+
35
+ # Performs depth-first search, following a sequence of symbols, on the trie
36
+ # data structure. Returns true if the last node visited is a terminal.
37
+ # @param symbols [Array<Symbol>] the symbol sequence to follow.
38
+ # @return [Boolean] true if the last node visited is a terminal, false otherwise.
39
+ def search_subtree symbols
40
+ return true if symbols.empty? && terminal?
41
+ s = symbols.slice! 0
42
+ return false unless child = @children[s]
43
+ child.search_subtree symbols
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'anagrams/subtrees'
2
+ require_relative 'anagrams/enumerable'
3
+ require_relative 'anagrams/anagrams'
4
+ require_relative 'anagrams/node'
5
+ require_relative 'anagrams/root'
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-anagrams
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Connor Lay
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-its
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A trie-based anagram solver.
70
+ email: connor.lay@me.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/anagrams/anagrams.rb
76
+ - lib/anagrams/enumerable.rb
77
+ - lib/anagrams/node.rb
78
+ - lib/anagrams/root.rb
79
+ - lib/anagrams/subtrees.rb
80
+ - lib/ruby-anagrams.rb
81
+ homepage: https://github.com/connorlay/ruby-anagrams
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.4.6
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Ruby Anagrams
105
+ test_files: []
106
+ has_rdoc: