rubytree 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,7 @@
8
8
  # Author:: Anupam Sengupta (anupamsg@gmail.com)
9
9
  #
10
10
 
11
- # Copyright (c) 2007, 2008, 2009, 2010, 2012 Anupam Sengupta
11
+ # Copyright (c) 2007, 2008, 2009, 2010, 2012, 2013 Anupam Sengupta
12
12
  #
13
13
  # All rights reserved.
14
14
  #
@@ -51,21 +51,7 @@ module Tree
51
51
  #
52
52
  class BinaryTreeNode < TreeNode
53
53
 
54
- # Adds the specified child node to the receiver node. The child node's parent is set to be the receiver.
55
- #
56
- # The child nodes are added in the order of addition, i.e., the first child added becomes the left node, and the
57
- # second child added will be the second node.
58
- #
59
- # If only one child is present, then this will be the left child.
60
- #
61
- # @param [Tree::BinaryTreeNode] child The child to add.
62
- #
63
- # @raise [ArgumentError] This exception is raised if two children are already present.
64
- def add(child)
65
- raise ArgumentError, "Already has two child nodes" if @children.size == 2
66
-
67
- super(child)
68
- end
54
+ # @!group Core Attributes
69
55
 
70
56
  # @!attribute [rw] left_child
71
57
  # Left child of the receiver node. Note that left Child == first Child.
@@ -89,6 +75,78 @@ module Tree
89
75
  children[1]
90
76
  end
91
77
 
78
+ # @!attribute is_left_child?
79
+ # +true+ if the receiver node is the left child of its parent.
80
+ # Always returns +false+ if it is a root node.
81
+ #
82
+ # @return [Boolean] +true+ if this is the left child of its parent.
83
+ def is_left_child?
84
+ return false if is_root?
85
+ self == parent.left_child
86
+ end
87
+
88
+ # @!attribute [r] is_right_child?
89
+ # +true+ if the receiver node is the right child of its parent.
90
+ # Always returns +false+ if it is a root node.
91
+ #
92
+ # @return [Boolean] +true+ if this is the right child of its parent.
93
+ def is_right_child?
94
+ return false if is_root?
95
+ self == parent.right_child
96
+ end
97
+
98
+ # @!group Structure Modification
99
+
100
+ # Adds the specified child node to the receiver node. The child node's parent is set to be the receiver.
101
+ #
102
+ # The child nodes are added in the order of addition, i.e., the first child added becomes the left node, and the
103
+ # second child added will be the second node.
104
+ #
105
+ # If only one child is present, then this will be the left child.
106
+ #
107
+ # @param [Tree::BinaryTreeNode] child The child to add.
108
+ #
109
+ # @raise [ArgumentError] This exception is raised if two children are already present.
110
+ def add(child)
111
+ raise ArgumentError, "Already has two child nodes" if @children.size == 2
112
+
113
+ super(child)
114
+ end
115
+
116
+ # Performs inorder traversal (including this node).
117
+ #
118
+ # @yieldparam node [Tree::BinaryTreeNode] Each node (in-order).
119
+ #
120
+ # @return [Tree::BinaryTreeNode] This node, if a block is given
121
+ # @return [Enumerator] An enumerator on this tree, if a block is *not* given
122
+ #
123
+ # @since 0.9.0
124
+ #
125
+ # @see #each
126
+ # @see #preordered_each
127
+ # @see #postordered_each
128
+ def inordered_each(&block)
129
+
130
+ return self.to_enum unless block_given?
131
+
132
+ node_stack = []
133
+ current_node = self
134
+
135
+ until node_stack.empty? and current_node == nil
136
+ if current_node
137
+ node_stack.push(current_node)
138
+ current_node = current_node.left_child
139
+ else
140
+ current_node = node_stack.pop()
141
+ yield current_node
142
+ current_node = current_node.right_child
143
+ end
144
+ end
145
+
146
+ return self if block_given?
147
+
148
+ end
149
+
92
150
  # A protected method to allow insertion of child nodes at the specified location.
93
151
  # Note that this method allows insertion of +nil+ nodes.
94
152
  #
@@ -107,6 +165,8 @@ module Tree
107
165
  child
108
166
  end
109
167
 
168
+ protected :set_child_at
169
+
110
170
  # Sets the left child of the receiver node. If a previous child existed, it is replaced.
111
171
  #
112
172
  # @param [Tree::BinaryTreeNode] child The child to add as the left-side node.
@@ -131,31 +191,11 @@ module Tree
131
191
  set_child_at child, 1
132
192
  end
133
193
 
134
- # Returns +true+ if the receiver node is the left child of its parent.
135
- # Always returns +false+ if it is a root node.
136
- #
137
- # @return [Boolean] +true+ if this is the left child of its parent.
138
- def is_left_child?
139
- return false if is_root?
140
- self == parent.left_child
141
- end
142
-
143
- # Returns +true+ if the receiver node is the right child of its parent.
144
- # Always returns +false+ if it is a root node.
145
- #
146
- # @return [Boolean] +true+ if this is the right child of its parent.
147
- def is_right_child?
148
- return false if is_root?
149
- self == parent.right_child
150
- end
151
-
152
194
  # Swaps the left and right child nodes of the receiver node with each other.
153
195
  def swap_children
154
196
  self.left_child, self.right_child = self.right_child, self.left_child
155
197
  end
156
198
 
157
- protected :set_child_at
158
-
159
199
  end
160
200
 
161
201
  end
@@ -0,0 +1,78 @@
1
+ # camel_case_methods.rb - This file is part of the RubyTree package.
2
+ #
3
+ # = camel_case_methods.rb - Provides conversion from CamelCase to snake_case.
4
+ #
5
+ # Author:: Anupam Sengupta (anupamsg@gmail.com)
6
+ #
7
+ # Time-stamp: <2013-12-31 20:48:13 anupam>
8
+ #
9
+ # Copyright (C) 2012, 2013 Anupam Sengupta <anupamsg@gmail.com>
10
+ #
11
+ # All rights reserved.
12
+ #
13
+ # Redistribution and use in source and binary forms, with or without modification,
14
+ # are permitted provided that the following conditions are met:
15
+ #
16
+ # - Redistributions of source code must retain the above copyright notice, this
17
+ # list of conditions and the following disclaimer.
18
+ #
19
+ # - Redistributions in binary form must reproduce the above copyright notice, this
20
+ # list of conditions and the following disclaimer in the documentation and/or
21
+ # other materials provided with the distribution.
22
+ #
23
+ # - Neither the name of the organization nor the names of its contributors may
24
+ # be used to endorse or promote products derived from this software without
25
+ # specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
34
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+ #
38
+
39
+ require 'structured_warnings'
40
+
41
+ module Tree::Utils
42
+ # Provides utility functions to handle CamelCase methods, and redirect
43
+ # invocation of such methods to the snake_case equivalents.
44
+ module CamelCaseMethodHandler
45
+ def self.included(base)
46
+ # @!visibility private
47
+ # Allow the deprecated CamelCase method names. Display a warning.
48
+ # :nodoc:
49
+ def method_missing(meth, *args, &blk)
50
+ if self.respond_to?(new_method_name = to_snake_case(meth))
51
+ warn DeprecatedMethodWarning,
52
+ "The camelCased methods are deprecated. Please use #{new_method_name} instead of #{meth}"
53
+ return send(new_method_name, *args, &blk)
54
+ else
55
+ super
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ # @!visibility private
62
+ # Convert a CamelCasedWord to a underscore separated camel_cased_word.
63
+ #
64
+ # @param [String] camel_cased_word The word to be converted to snake_case.
65
+ # @return [String] the snake_cased_word.
66
+ def to_snake_case(camel_cased_word)
67
+ word = camel_cased_word.to_s.dup
68
+ word.gsub!(/::/, '/')
69
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
70
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
71
+ word.tr!("-", "_")
72
+ word.downcase!
73
+ word
74
+ end
75
+
76
+ end # self.included
77
+ end
78
+ end
@@ -0,0 +1,125 @@
1
+ # json.rb - This file is part of the RubyTree package.
2
+ #
3
+ # = json.rb - Provides conversion to and from JSON.
4
+ #
5
+ # Author:: Anupam Sengupta (anupamsg@gmail.com)
6
+ #
7
+ # Time-stamp: <2013-12-31 21:57:42 anupam>
8
+ #
9
+ # Copyright (C) 2012, 2013 Anupam Sengupta <anupamsg@gmail.com>
10
+ #
11
+ # All rights reserved.
12
+ #
13
+ # Redistribution and use in source and binary forms, with or without modification,
14
+ # are permitted provided that the following conditions are met:
15
+ #
16
+ # - Redistributions of source code must retain the above copyright notice, this
17
+ # list of conditions and the following disclaimer.
18
+ #
19
+ # - Redistributions in binary form must reproduce the above copyright notice, this
20
+ # list of conditions and the following disclaimer in the documentation and/or
21
+ # other materials provided with the distribution.
22
+ #
23
+ # - Neither the name of the organization nor the names of its contributors may
24
+ # be used to endorse or promote products derived from this software without
25
+ # specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
34
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+ require 'json'
39
+
40
+ # Provides utility methods to convert a {Tree::TreeNode} to and from
41
+ # JSON[http://flori.github.com/json/].
42
+ module Tree::Utils::JSONConverter
43
+
44
+ def self.included(base)
45
+ base.extend(ClassMethods)
46
+ end
47
+
48
+ # @!group Converting to/from JSON
49
+
50
+ # Creates a JSON ready Hash for the #to_json method.
51
+ #
52
+ # @author Eric Cline (https://github.com/escline)
53
+ # @since 0.8.3
54
+ #
55
+ # @return A hash based representation of the JSON
56
+ #
57
+ # Rails uses JSON in ActiveSupport, and all Rails JSON encoding goes through +as_json+.
58
+ #
59
+ # @see #to_json
60
+ # @see http://stackoverflow.com/a/6880638/273808
61
+ def as_json(options = {})
62
+
63
+ json_hash = {
64
+ "name" => name,
65
+ "content" => content,
66
+ JSON.create_id => self.class.name
67
+ }
68
+
69
+ if has_children?
70
+ json_hash["children"] = children
71
+ end
72
+
73
+ return json_hash
74
+
75
+ end
76
+
77
+ # Creates a JSON representation of this node including all it's children.
78
+ # This requires the JSON gem to be available, or else the operation fails with
79
+ # a warning message. Uses the Hash output of #as_json method.
80
+ #
81
+ # @author Dirk Breuer (http://github.com/railsbros-dirk)
82
+ # @since 0.7.0
83
+ #
84
+ # @return The JSON representation of this subtree.
85
+ #
86
+ # @see ClassMethods#json_create
87
+ # @see #as_json
88
+ # @see http://flori.github.com/json
89
+ def to_json(*a)
90
+ as_json.to_json(*a)
91
+ end
92
+
93
+ # ClassMethods for the {JSONConverter} module. Will become class methods in the +include+ target.
94
+ module ClassMethods
95
+ # Helper method to create a Tree::TreeNode instance from the JSON hash
96
+ # representation. Note that this method should *NOT* be called directly.
97
+ # Instead, to convert the JSON hash back to a tree, do:
98
+ #
99
+ # tree = JSON.parse(the_json_hash)
100
+ #
101
+ # This operation requires the {JSON gem}[http://flori.github.com/json/] to
102
+ # be available, or else the operation fails with a warning message.
103
+ #
104
+ # @author Dirk Breuer (http://github.com/railsbros-dirk)
105
+ # @since 0.7.0
106
+ #
107
+ # @param [Hash] json_hash The JSON hash to convert from.
108
+ #
109
+ # @return [Tree::TreeNode] The created tree.
110
+ #
111
+ # @see #to_json
112
+ # @see http://flori.github.com/json
113
+ def json_create(json_hash)
114
+
115
+ node = new(json_hash["name"], json_hash["content"])
116
+
117
+ json_hash["children"].each do |child|
118
+ node << child
119
+ end if json_hash["children"]
120
+
121
+ return node
122
+
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,172 @@
1
+ # metrics_methods.rb - This file is part of the RubyTree package.
2
+ #
3
+ # = metrics_methods.rb - Provides methods for various tree measurements
4
+ #
5
+ # Author:: Anupam Sengupta (anupamsg@gmail.com)
6
+ #
7
+ # Time-stamp: <2013-12-31 21:45:35 anupam>
8
+ #
9
+ # Copyright (C) 2013 Anupam Sengupta <anupamsg@gmail.com>
10
+ #
11
+ # All rights reserved.
12
+ #
13
+ # Redistribution and use in source and binary forms, with or without modification,
14
+ # are permitted provided that the following conditions are met:
15
+ #
16
+ # - Redistributions of source code must retain the above copyright notice, this
17
+ # list of conditions and the following disclaimer.
18
+ #
19
+ # - Redistributions in binary form must reproduce the above copyright notice, this
20
+ # list of conditions and the following disclaimer in the documentation and/or
21
+ # other materials provided with the distribution.
22
+ #
23
+ # - Neither the name of the organization nor the names of its contributors may
24
+ # be used to endorse or promote products derived from this software without
25
+ # specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
34
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+ #
38
+
39
+ require 'structured_warnings'
40
+
41
+ module Tree::Utils
42
+ # Provides utility functions to measure various tree metrics.
43
+ module TreeMetricsHandler
44
+ def self.included(base)
45
+
46
+ # @!group Metrics and Measures
47
+
48
+ # @!attribute [r] size
49
+ # Total number of nodes in this (sub)tree, including this node.
50
+ #
51
+ # Size of the tree is defined as:
52
+ #
53
+ # Size:: Total number nodes in the subtree including this node.
54
+ #
55
+ # @return [Integer] Total number of nodes in this (sub)tree.
56
+ def size
57
+ inject(0) {|sum, node| sum + 1 if node}
58
+ end
59
+
60
+ # @!attribute [r] length
61
+ # Convenience synonym for {#size}.
62
+ #
63
+ # @deprecated This method name is ambiguous and may be removed. Use {#size} instead.
64
+ #
65
+ # @return [Integer] The total number of nodes in this (sub)tree.
66
+ # @see #size
67
+ def length
68
+ size()
69
+ end
70
+
71
+ # @!attribute [r] node_height
72
+ # Height of the (sub)tree from this node. Height of a node is defined as:
73
+ #
74
+ # Height:: Length of the longest downward path to a leaf from the node.
75
+ #
76
+ # - Height from a root node is height of the entire tree.
77
+ # - The height of a leaf node is zero.
78
+ #
79
+ # @return [Integer] Height of the node.
80
+ def node_height
81
+ return 0 if is_leaf?
82
+ 1 + @children.collect { |child| child.node_height }.max
83
+ end
84
+
85
+ # @!attribute [r] node_depth
86
+ # Depth of this node in its tree. Depth of a node is defined as:
87
+ #
88
+ # Depth:: Length of the node's path to its root. Depth of a root node is zero.
89
+ #
90
+ # *Note* that the deprecated method {#depth} was incorrectly computing this value.
91
+ # Please replace all calls to the old method with {#node_depth} instead.
92
+ #
93
+ # {#level} is an alias for this method.
94
+ #
95
+ # @return [Integer] Depth of this node.
96
+ def node_depth
97
+ return 0 if is_root?
98
+ 1 + parent.node_depth
99
+ end
100
+
101
+ # @!attribute [r] level
102
+ # Alias for {#node_depth}
103
+ #
104
+ # @see #node_depth
105
+ def level
106
+ node_depth
107
+ end
108
+
109
+ # @!attribute [r] depth
110
+ # Depth of the tree from this node. A single leaf node has a depth of 1.
111
+ #
112
+ # This method is *DEPRECATED* and may be removed in the subsequent releases.
113
+ # Note that the value returned by this method is actually the:
114
+ #
115
+ # _height_ + 1 of the node, *NOT* the _depth_.
116
+ #
117
+ # For correct and conventional behavior, please use {#node_depth} and
118
+ # {#node_height} methods instead.
119
+ #
120
+ # @return [Integer] depth of the node.
121
+ # @deprecated This method returns an incorrect value. Use the {#node_depth} method instead.
122
+ #
123
+ # @see #node_depth
124
+ def depth
125
+ warn DeprecatedMethodWarning, 'This method is deprecated. Please use node_depth() or node_height() instead (bug # 22535)'
126
+
127
+ return 1 if is_leaf?
128
+ 1 + @children.collect { |child| child.depth }.max
129
+ end
130
+
131
+ # @!attribute [r] breadth
132
+ # Breadth of the tree at this node's level.
133
+ # A single node without siblings has a breadth of 1.
134
+ #
135
+ # Breadth is defined to be:
136
+ # Breadth:: Number of sibling nodes to this node + 1 (this node itself),
137
+ # i.e., the number of children the parent of this node has.
138
+ #
139
+ # @return [Integer] breadth of the node's level.
140
+ def breadth
141
+ is_root? ? 1 : parent.children.size
142
+ end
143
+
144
+ # @!attribute [r] in_degree
145
+ # The incoming edge-count of this node.
146
+ #
147
+ # In-degree is defined as:
148
+ # In-degree:: Number of edges arriving at the node (0 for root, 1 for all other nodes)
149
+ #
150
+ # - In-degree = 0 for a root or orphaned node
151
+ # - In-degree = 1 for a node which has a parent
152
+ #
153
+ # @return [Integer] The in-degree of this node.
154
+ def in_degree
155
+ is_root? ? 0 : 1
156
+ end
157
+
158
+ # @!attribute [r] out_degree
159
+ # The outgoing edge-count of this node.
160
+ #
161
+ # Out-degree is defined as:
162
+ # Out-degree:: Number of edges leaving the node (zero for leafs)
163
+ #
164
+ # @return [Integer] The out-degree of this node.
165
+ def out_degree
166
+ is_leaf? ? 0 : children.size
167
+ end
168
+
169
+ # @!endgroup
170
+ end # self.included
171
+ end
172
+ end