pax_global_header00006660000000000000000000000064123666046370014527gustar00rootroot0000000000000052 comment=6072e872dbf14f39bbc3b846e55e642a01e3e026 ruby-avl-tree-1.1.3/000077500000000000000000000000001236660463700142275ustar00rootroot00000000000000ruby-avl-tree-1.1.3/README000066400000000000000000000006721236660463700151140ustar00rootroot00000000000000avl_tree - AVL tree and Red-black tree in Ruby Copyright (C) 2012 Hiroshi Nakamura == Author Name:: Hiroshi Nakamura E-mail:: nahi@ruby-lang.org Project web site:: http://github.com/nahi/avl_tree == License This program is copyrighted free software by Hiroshi Nakamura. You can redistribute it and/or modify it under the same terms of Ruby's license; either the dual license version in 2003, or any later version. ruby-avl-tree-1.1.3/bench/000077500000000000000000000000001236660463700153065ustar00rootroot00000000000000ruby-avl-tree-1.1.3/bench/bench.rb000066400000000000000000000013361236660463700167150ustar00rootroot00000000000000require 'benchmark' require 'avl_tree' require 'red_black_tree' require 'openssl' #random = Random.new(0) TIMES = 100000 key_size = 10 def aset(h, keys) keys.each do |k| h[k] = 1 end end def aref(h, keys) keys.each do |k| h[k] end end def delete(h, keys) keys.each do |k| h.delete(k) end end def run(bm, h, keys) name = h.class.name bm.report("#{name} aset") do aset(h, keys) end bm.report("#{name} aref") do aref(h, keys) end bm.report("#{name} delete") do delete(h, keys) end end keys = [] TIMES.times do keys << OpenSSL::Random.random_bytes(key_size) end Benchmark.bmbm do |bm| run(bm, Hash.new, keys) run(bm, AVLTree.new, keys) run(bm, RedBlackTree.new, keys) end ruby-avl-tree-1.1.3/bench/bench_element_size.rb000066400000000000000000000015411236660463700214560ustar00rootroot00000000000000require 'benchmark' require 'radix_tree' # gem install radix_tree require 'avl_tree' require 'openssl' times = 100000 key_size = 10 def aset(h, keys) keys.each do |k| h[k] = 1 end end def aref(h, keys) keys.each do |k| h[k] end end def delete(h, keys) keys.each do |k| h.delete(k) end end def run(bm, h, keys) name = h.class.name bm.report("#{name} aset (#{keys.size})") do aset(h, keys) end bm.report("#{name} aref (#{keys.size})") do aref(h, keys) end bm.report("#{name} delete (#{keys.size})") do delete(h, keys) end end keys = [] 1000000.times do keys << OpenSSL::Random.random_bytes(key_size) end 1.upto(100) do |idx| elements = idx * 10000 Benchmark.bm(30) do |bm| #run(bm, Hash.new, keys[0, elements]) #run(bm, RadixTree.new, keys) run(bm, AVLTree.new, keys[0, elements]) end end ruby-avl-tree-1.1.3/bench/profile.rb000066400000000000000000000004111236660463700172670ustar00rootroot00000000000000require File.expand_path('../lib/red_black_tree', File.dirname(__FILE__)) random = Random.new(0) TIMES = 50000 key_size = 10 h = RedBlackTree.new TIMES.times do h[random.bytes(key_size)] = 1 #h[random.bytes(key_size)] #h.delete(random.bytes(key_size)) end ruby-avl-tree-1.1.3/lib/000077500000000000000000000000001236660463700147755ustar00rootroot00000000000000ruby-avl-tree-1.1.3/lib/avl_tree.rb000066400000000000000000000163621236660463700171330ustar00rootroot00000000000000class AVLTree include Enumerable class Node UNDEFINED = Object.new attr_reader :key, :value, :height attr_reader :left, :right def initialize(key, value) @key, @value = key, value @left = @right = EMPTY @height = 1 end def empty? false end def size @left.size + 1 + @right.size end # inorder def each(&block) @left.each(&block) yield [@key, @value] @right.each(&block) end def each_key each do |k, v| yield k end end def each_value each do |k, v| yield v end end def keys collect { |k, v| k } end def values collect { |k, v| v } end # returns new_root def insert(key, value) case key <=> @key when -1 @left = @left.insert(key, value) when 0 @value = value when 1 @right = @right.insert(key, value) else raise TypeError, "cannot compare #{key} and #{@key} with <=>" end rotate end # returns value def retrieve(key) case key <=> @key when -1 @left.retrieve(key) when 0 @value when 1 @right.retrieve(key) else nil end end # returns [deleted_node, new_root] def delete(key) case key <=> @key when -1 deleted, @left = @left.delete(key) [deleted, self.rotate] when 0 [self, delete_self.rotate] when 1 deleted, @right = @right.delete(key) [deleted, self.rotate] else raise TypeError, "cannot compare #{key} and #{@key} with <=>" end end def delete_min if @left.empty? [self, delete_self] else deleted, @left = @left.delete_min [deleted, rotate] end end def delete_max if @right.empty? [self, delete_self] else deleted, @right = @right.delete_max [deleted, rotate] end end def dump_tree(io, indent = '') @right.dump_tree(io, indent + ' ') io << indent << sprintf("#<%s:0x%010x %d %s> => %s", self.class.name, __id__, height, @key.inspect, @value.inspect) << $/ @left.dump_tree(io, indent + ' ') end def dump_sexp left = @left.dump_sexp right = @right.dump_sexp if left or right '(' + [@key, left || '-', right].compact.join(' ') + ')' else @key end end # for debugging def check_height @left.check_height @right.check_height lh = @left.height rh = @right.height if (lh - rh).abs > 1 puts dump_tree(STDERR) raise "height unbalanced: #{lh} #{height} #{rh}" end if (lh > rh ? lh : rh) + 1 != height puts dump_tree(STDERR) raise "height calc failure: #{lh} #{height} #{rh}" end end protected def left=(left) @left = left end def right=(right) @right = right end def update_height @height = (@left.height > @right.height ? @left.height : @right.height) + 1 end def rotate case @left.height - @right.height when +2 if @left.left.height < @left.right.height @left = @left.rotate_left end root = rotate_right when -2 if @right.left.height > @right.right.height @right = @right.rotate_right end root = rotate_left else root = self end root.update_height root end # Right single rotation # (B a (D c E)) where D-a > 1 && E > c --> (D (B a c) E) # # B D # / \ / \ # a D -> B E # / \ / \ # c E a c # def rotate_left root = @right @right = root.left root.left = self root.left.update_height root end # Left single rotation # (D (B A c) e) where B-e > 1 && A > c --> (B A (D c e)) # # D B # / \ / \ # B e -> A D # / \ / \ # A c c e # def rotate_right root = @left @left = root.right root.right = self root.right.update_height root end private def delete_self if @left.empty? and @right.empty? deleted = EMPTY elsif @right.height < @left.height deleted, new_left = @left.delete_max deleted.left, deleted.right = new_left, @right else deleted, new_right = @right.delete_min deleted.left, deleted.right = @left, new_right end deleted end def collect pool = [] each do |key, value| pool << yield(key, value) end pool end class EmptyNode < Node def initialize @value = nil @height = 0 end def empty? true end def size 0 end def each(&block) # intentionally blank end # returns new_root def insert(key, value) Node.new(key, value) end # returns value def retrieve(key) UNDEFINED end # returns [deleted_node, new_root] def delete(key) [self, self] end def dump_tree(io, indent = '') # intentionally blank end def dump_sexp # intentionally blank end def rotate self end def update_height # intentionally blank end # for debugging def check_height # intentionally blank end end EMPTY = Node::EmptyNode.new.freeze end DEFAULT = Object.new attr_accessor :default attr_reader :default_proc def initialize(default = DEFAULT, &block) if block && default != DEFAULT raise ArgumentError, 'wrong number of arguments' end @root = Node::EMPTY @default = default @default_proc = block end def empty? @root == Node::EMPTY end def size @root.size end alias length size def each(&block) if block_given? @root.each(&block) self else Enumerator.new(@root) end end alias each_pair each def each_key if block_given? @root.each do |k, v| yield k end self else Enumerator.new(@root, :each_key) end end def each_value if block_given? @root.each do |k, v| yield v end self else Enumerator.new(@root, :each_value) end end def keys @root.keys end def values @root.values end def clear @root = Node::EMPTY end def []=(key, value) @root = @root.insert(key, value) end alias insert []= def key?(key) @root.retrieve(key) != Node::UNDEFINED end alias has_key? key? def [](key) value = @root.retrieve(key) if value == Node::UNDEFINED default_value else value end end def delete(key) deleted, @root = @root.delete(key) deleted.value end def dump_tree(io = '') @root.dump_tree(io) io << $/ io end def dump_sexp @root.dump_sexp || '' end def to_hash inject({}) { |r, (k, v)| r[k] = v; r } end private def default_value if @default != DEFAULT @default elsif @default_proc @default_proc.call else nil end end end ruby-avl-tree-1.1.3/lib/red_black_tree.rb000066400000000000000000000270411236660463700202530ustar00rootroot00000000000000class RedBlackTree include Enumerable class Node UNDEFINED = Object.new attr_reader :key, :value, :color attr_reader :left, :right def initialize(key, value) @key, @value = key, value @left = @right = EMPTY # new node is added as RED @color = :RED end def set_root @color = :BLACK end def red? @color == :RED end def black? @color == :BLACK end def empty? false end def size @left.size + 1 + @right.size end # inorder def each(&block) @left.each(&block) yield [@key, @value] @right.each(&block) end def each_key each do |k, v| yield k end end def each_value each do |k, v| yield v end end def keys collect { |k, v| k } end def values collect { |k, v| v } end # returns new_root def insert(key, value) ret = self case key <=> @key when -1 @left = @left.insert(key, value) if black? and @right.black? and @left.red? and !@left.children_both_black? ret = rebalance_for_left_insert end when 0 @value = value when 1 @right = @right.insert(key, value) if black? and @left.black? and @right.red? and !@right.children_both_black? ret = rebalance_for_right_insert end else raise TypeError, "cannot compare #{key} and #{@key} with <=>" end ret.pullup_red end # returns value def retrieve(key) case key <=> @key when -1 @left.retrieve(key) when 0 @value when 1 @right.retrieve(key) else nil end end # returns [deleted_node, new_root, is_rebalance_needed] def delete(key) ret = self case key <=> @key when -1 deleted, @left, rebalance = @left.delete(key) if rebalance ret, rebalance = rebalance_for_left_delete end when 0 deleted = self ret, rebalance = delete_self when 1 deleted, @right, rebalance = @right.delete(key) if rebalance ret, rebalance = rebalance_for_right_delete end else raise TypeError, "cannot compare #{key} and #{@key} with <=>" end [deleted, ret, rebalance] end def dump_tree(io, indent = '') @right.dump_tree(io, indent + ' ') io << indent << sprintf("#<%s:0x%010x %s %s> => %s", self.class.name, __id__, @color, @key.inspect, @value.inspect) << $/ @left.dump_tree(io, indent + ' ') end def dump_sexp left = @left.dump_sexp right = @right.dump_sexp if left or right '(' + [@key, left || '-', right].compact.join(' ') + ')' else @key end end # for debugging def check_height lh = @left.empty? ? 0 : @left.check_height rh = @right.empty? ? 0 : @right.check_height if red? if @left.red? or @right.red? puts dump_tree(STDERR) raise 'red/red assertion failed' end else if lh != rh puts dump_tree(STDERR) raise "black height unbalanced: #{lh} #{rh}" end end (lh > rh ? lh : rh) + (black? ? 1 : 0) end protected def children_both_black? @right.black? and @left.black? end def color=(color) @color = color end def left=(left) @left = left end def right=(right) @right = right end def color_flip(other) @color, other.color = other.color, @color end def node_flip(other) @left, other.left = other.left, @left @right, other.right = other.right, @right color_flip(other) end def delete_min if @left.empty? [self, *delete_self] else ret = self deleted, @left, rebalance = @left.delete_min if rebalance ret, rebalance = rebalance_for_left_delete end [deleted, ret, rebalance] end end # trying to rebalance when the left sub-tree is 1 level lower than the right def rebalance_for_left_delete ret = self rebalance = false if black? if @right.black? if @right.children_both_black? # make whole sub-tree 1 level lower and ask rebalance @right.color = :RED rebalance = true else # move 1 black from the right to the left by single/double rotation ret = balanced_rotate_left end else # flip this sub-tree into another type of 3-children node ret = rotate_left # try to rebalance in sub-tree ret.left, rebalance = ret.left.rebalance_for_left_delete raise 'should not happen' if rebalance end else # red if @right.children_both_black? # make right sub-tree 1 level lower color_flip(@right) else # move 1 black from the right to the left by single/double rotation ret = balanced_rotate_left end end [ret, rebalance] end # trying to rebalance when the right sub-tree is 1 level lower than the left # See rebalance_for_left_delete. def rebalance_for_right_delete ret = self rebalance = false if black? if @left.black? if @left.children_both_black? @left.color = :RED rebalance = true else ret = balanced_rotate_right end else ret = rotate_right ret.right, rebalance = ret.right.rebalance_for_right_delete raise 'should not happen' if rebalance end else # red if @left.children_both_black? color_flip(@left) else ret = balanced_rotate_right end end [ret, rebalance] end # move 1 black from the right to the left by single/double rotation def balanced_rotate_left if @right.left.red? and @right.right.black? @right = @right.rotate_right end ret = rotate_left ret.right.color = ret.left.color = :BLACK ret end # move 1 black from the left to the right by single/double rotation def balanced_rotate_right if @left.right.red? and @left.left.black? @left = @left.rotate_left end ret = rotate_right ret.right.color = ret.left.color = :BLACK ret end # Right single rotation # (b a (D c E)) where D and E are RED --> (d (B a c) E) # # b d # / \ / \ # a D -> B E # / \ / \ # c E a c # def rotate_left root = @right @right = root.left root.left = self root.color_flip(root.left) root end # Left single rotation # (d (B A c) e) where A and B are RED --> (b A (D c e)) # # d b # / \ / \ # B e -> A D # / \ / \ # A c c e # def rotate_right root = @left @left = root.right root.right = self root.color_flip(root.right) root end # Pull up red nodes # (b (A C)) where A and C are RED --> (B (a c)) # # b B # / \ -> / \ # A C a c # def pullup_red if black? and @left.red? and @right.red? @left.color = @right.color = :BLACK self.color = :RED end self end private # trying to rebalance when the left sub-tree is 1 level higher than the right # precondition: self is black and @left is red def rebalance_for_left_insert # move 1 black from the left to the right by single/double rotation if @left.right.red? @left = @left.rotate_left end rotate_right end # trying to rebalance when the right sub-tree is 1 level higher than the left # See rebalance_for_left_insert. def rebalance_for_right_insert if @right.left.red? @right = @right.rotate_right end rotate_left end def delete_self rebalance = false if @left.empty? and @right.empty? # just remove this node and ask rebalance to the parent new_root = EMPTY if black? rebalance = true end elsif @left.empty? or @right.empty? # pick the single children new_root = @left.empty? ? @right : @left if black? # keep the color black raise 'should not happen' unless new_root.red? color_flip(new_root) else # just remove the red node end else # pick the minimum node from the right sub-tree and replace self with it new_root, @right, rebalance = @right.delete_min new_root.node_flip(self) if rebalance new_root, rebalance = new_root.rebalance_for_right_delete end end [new_root, rebalance] end def collect pool = [] each do |key, value| pool << yield(key, value) end pool end class EmptyNode < Node def initialize @value = nil @color = :BLACK end def empty? true end def size 0 end def each(&block) # intentionally blank end # returns new_root def insert(key, value) Node.new(key, value) end # returns value def retrieve(key) UNDEFINED end # returns [deleted_node, new_root, is_rebalance_needed] def delete(key) [self, self, false] end def dump_tree(io, indent = '') # intentionally blank end def dump_sexp # intentionally blank end end EMPTY = Node::EmptyNode.new.freeze end DEFAULT = Object.new attr_accessor :default attr_reader :default_proc def initialize(default = DEFAULT, &block) if block && default != DEFAULT raise ArgumentError, 'wrong number of arguments' end @root = Node::EMPTY @default = default @default_proc = block end def empty? @root == Node::EMPTY end def size @root.size end alias length size def each(&block) if block_given? @root.each(&block) self else Enumerator.new(@root) end end alias each_pair each def each_key if block_given? @root.each do |k, v| yield k end self else Enumerator.new(@root, :each_key) end end def each_value if block_given? @root.each do |k, v| yield v end self else Enumerator.new(@root, :each_value) end end def keys @root.keys end def values @root.values end def clear @root = Node::EMPTY end def []=(key, value) @root = @root.insert(key, value) @root.set_root @root.check_height if $DEBUG end alias insert []= def key?(key) @root.retrieve(key) != Node::UNDEFINED end alias has_key? key? def [](key) value = @root.retrieve(key) if value == Node::UNDEFINED default_value else value end end def delete(key) deleted, @root, rebalance = @root.delete(key) unless empty? @root.set_root @root.check_height if $DEBUG end deleted.value end def dump_tree(io = '') @root.dump_tree(io) io << $/ io end def dump_sexp @root.dump_sexp || '' end def to_hash inject({}) { |r, (k, v)| r[k] = v; r } end private def default_value if @default != DEFAULT @default elsif @default_proc @default_proc.call else nil end end end ruby-avl-tree-1.1.3/metadata.yml000066400000000000000000000023251236660463700165340ustar00rootroot00000000000000--- !ruby/object:Gem::Specification name: avl_tree version: !ruby/object:Gem::Version version: 1.1.3 prerelease: platform: ruby authors: - Hiroshi Nakamura autorequire: bindir: bin cert_chain: [] date: 2012-05-09 00:00:00.000000000 Z dependencies: [] description: email: nahi@ruby-lang.org executables: [] extensions: [] extra_rdoc_files: [] files: - lib/red_black_tree.rb - lib/avl_tree.rb - bench/bench_element_size.rb - bench/profile.rb - bench/bench.rb - test/test_red_black_tree.rb - test/helper.rb - test/test_avl_tree.rb - README homepage: http://github.com/nahi/avl_tree licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' segments: - 0 hash: -4094052252433988634 required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' segments: - 0 hash: -4094052252433988634 requirements: [] rubyforge_project: rubygems_version: 1.8.23 signing_key: specification_version: 3 summary: AVL tree and Red-black tree in Ruby test_files: [] ruby-avl-tree-1.1.3/test/000077500000000000000000000000001236660463700152065ustar00rootroot00000000000000ruby-avl-tree-1.1.3/test/helper.rb000066400000000000000000000002031236660463700170050ustar00rootroot00000000000000begin require 'simplecov' SimpleCov.start rescue LoadError end require "test/unit" require "avl_tree" require "red_black_tree" ruby-avl-tree-1.1.3/test/test_avl_tree.rb000066400000000000000000000246771236660463700204130ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('./helper', File.dirname(__FILE__)) class TestAVLTree < Test::Unit::TestCase def test_tree_rotate_RR h = AVLTree.new assert_equal '', h.dump_sexp h['a'] = 1 assert_equal 'a', h.dump_sexp h['b'] = 2 assert_equal '(a - b)', h.dump_sexp h['c'] = 3 assert_equal '(b a c)', h.dump_sexp h['d'] = 4 assert_equal '(b a (c - d))', h.dump_sexp h['e'] = 5 assert_equal '(b a (d c e))', h.dump_sexp end def test_tree_rotate_LL h = AVLTree.new h['e'] = 1 h['d'] = 2 assert_equal '(e d)', h.dump_sexp h['c'] = 3 assert_equal '(d c e)', h.dump_sexp h['b'] = 4 assert_equal '(d (c b) e)', h.dump_sexp h['a'] = 5 assert_equal '(d (b a c) e)', h.dump_sexp end def test_tree_rotate_RL h = AVLTree.new h['b'] = 1 h['a'] = 2 h['e'] = 3 h['d'] = 4 h['f'] = 5 assert_equal '(b a (e d f))', h.dump_sexp h['c'] = 6 assert_equal '(d (b a c) (e - f))', h.dump_sexp end def test_tree_rotate_LR h = AVLTree.new h['g'] = 1 h['b'] = 2 h['h'] = 3 h['i'] = 4 h['a'] = 5 h['d'] = 6 h['0'] = 7 h['c'] = 8 h['e'] = 9 assert_equal '(g (b (a 0) (d c e)) (h - i))', h.dump_sexp h['f'] = 10 assert_equal '(d (b (a 0) c) (g (e - f) (h - i)))', h.dump_sexp end def test_aref_nil h = AVLTree.new h['abc'] = 1 assert_equal nil, h['def'] end def test_empty h = AVLTree.new h['abc'] = 0 assert_equal nil, h[''] h[''] = 1 assert_equal 1, h[''] h.delete('') assert_equal nil, h[''] end def test_aref_single h = AVLTree.new h['abc'] = 1 assert_equal 1, h['abc'] end def test_aref_double h = AVLTree.new h['abc'] = 1 h['def'] = 2 assert_equal 1, h['abc'] assert_equal 2, h['def'] end def test_aset_override h = AVLTree.new h['abc'] = 1 h['abc'] = 2 assert_equal 2, h['abc'] end def test_split h = AVLTree.new h['abcd'] = 1 assert_equal 1, h['abcd'] h['abce'] = 2 assert_equal 1, h['abcd'] assert_equal 2, h['abce'] h['abd'] = 3 assert_equal 1, h['abcd'] assert_equal 2, h['abce'] assert_equal 3, h['abd'] h['ac'] = 4 assert_equal 1, h['abcd'] assert_equal 2, h['abce'] assert_equal 3, h['abd'] assert_equal 4, h['ac'] end def test_split_and_assign h = AVLTree.new h['ab'] = 1 h['a'] = 2 assert_equal 1, h['ab'] assert_equal 2, h['a'] end def test_push h = AVLTree.new assert_equal 0, h.size h['a'] = 1 assert_equal 1, h['a'] h['ab'] = 2 assert_equal 1, h['a'] assert_equal 2, h['ab'] h['abc'] = 3 assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] h['abd'] = 4 assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] assert_equal 4, h['abd'] h['ac'] = 5 assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] assert_equal 4, h['abd'] assert_equal 5, h['ac'] h['b'] = 6 assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] assert_equal 4, h['abd'] assert_equal 5, h['ac'] assert_equal 6, h['b'] assert_equal ['a', 'ab', 'abc', 'abd', 'ac', 'b'].sort, h.keys.sort assert_equal 6, h.size end def test_different_type h = AVLTree.new h['a'] = 1 assert_raise(TypeError) do h[3.3] = 2 end assert_nil h[3.3] end def test_delete_leaf h = AVLTree.new h['b'] = 1 h['a'] = 2 h['c'] = 3 assert_equal 2, h['a'] h.delete('a') assert_equal nil, h['a'] end def test_delete_leaf_single_rotation h = AVLTree.new h['b'] = 1 h['a'] = 2 h['d'] = 3 h['c'] = 4 h['e'] = 5 assert_equal '(b a (d c e))', h.dump_sexp h.delete('a') assert_equal '(d (b - c) e)', h.dump_sexp end def test_delete_leaf_double_rotation h = AVLTree.new h['b'] = 1 h['a'] = 2 h['e'] = 3 h['0'] = 4 h['c'] = 5 h['f'] = 6 h['d'] = 7 assert_equal '(b (a 0) (e (c - d) f))', h.dump_sexp h.delete('0') assert_equal '(c (b a) (e d f))', h.dump_sexp end def test_delete_node_right h = AVLTree.new h['c'] = 1 h['b'] = 2 h['g'] = 3 h['a'] = 4 h['e'] = 5 h['i'] = 6 h['d'] = 7 h['f'] = 8 h['h'] = 9 h['j'] = 10 assert_equal '(c (b a) (g (e d f) (i h j)))', h.dump_sexp h.delete('g') assert_equal '(c (b a) (h (e d f) (i - j)))', h.dump_sexp end def test_delete_node_left h = AVLTree.new h['c'] = 1 h['b'] = 2 h['d'] = 3 h['a'] = 4 assert_equal '(c (b a) d)', h.dump_sexp h.delete('b') assert_equal '(c a d)', h.dump_sexp end def test_delete_root h = AVLTree.new h['b'] = 1 h['a'] = 2 h['c'] = 3 assert_equal 1, h['b'] assert_equal '(b a c)', h.dump_sexp h.delete('b') assert_equal '(c a)', h.dump_sexp assert_equal nil, h['b'] end def test_delete h = AVLTree.new h['a'] = 1 h['ab'] = 2 h['abc'] = 3 h['abd'] = 4 h['ac'] = 5 h['b'] = 6 assert_equal 6, h.size assert_equal nil, h.delete('XXX') # delete leaf assert_equal 4, h.delete('abd') assert_equal 5, h.size assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] assert_equal nil, h['abd'] assert_equal 5, h['ac'] assert_equal 6, h['b'] # delete single leaf node assert_equal 2, h.delete('ab') assert_equal 4, h.size assert_equal 1, h['a'] assert_equal nil, h['ab'] assert_equal 3, h['abc'] assert_equal nil, h['abd'] assert_equal 5, h['ac'] assert_equal 6, h['b'] # delete multiple leaf node assert_equal 1, h.delete('a') assert_equal 3, h.size assert_equal nil, h['a'] assert_equal nil, h['ab'] assert_equal 3, h['abc'] assert_equal nil, h['abd'] assert_equal 5, h['ac'] assert_equal 6, h['b'] assert_equal ['abc', 'ac', 'b'].sort, h.keys.sort # delete rest assert_equal 3, h.delete('abc') assert_equal 5, h.delete('ac') assert_equal 6, h.delete('b') assert_equal 0, h.size assert h.empty? end def test_delete_compaction_middle h = AVLTree.new h['a'] = 1 h['abc'] = 2 h['bb'] = 3 h['abcdefghi'] = 4 h['abcdefghijzz'] = 5 h['abcdefghikzz'] = 6 assert_equal 6, h.dump_tree.split($/).size h.delete('a') assert_equal 5, h.dump_tree.split($/).size h['a'] = 1 assert_equal 6, h.dump_tree.split($/).size end def test_delete_compaction_leaf h = AVLTree.new h['a'] = 1 h['abc'] = 2 h['bb'] = 3 h['abcdefghijzz'] = 4 assert_equal 4, h.dump_tree.split($/).size h['abcdefghikzz'] = 5 assert_equal 5, h.dump_tree.split($/).size h.delete('abcdefghijzz') assert_equal 4, h.dump_tree.split($/).size h['abcdefghijzz'] = 4 assert_equal 5, h.dump_tree.split($/).size end def test_delete_different_type h = AVLTree.new h['a'] = 1 h['abc'] = 2 h['bb'] = 3 assert_raise(TypeError) do h.delete(3.3) end end def test_each h = AVLTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s.to_a.sort_by { |k, v| k }, h.each.sort_by { |k, v| k } # values = [] h.each do |k, v| values << [k, v] end assert_equal s.to_a.sort_by { |k, v| k }, values.sort_by { |k, v| k } end def test_each_key h = AVLTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s.keys.sort, h.each_key.sort # values = [] h.each_key do |k| values << k end assert_equal s.keys.sort, values.sort end def test_each_value h = AVLTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6, 'azzzzz' => 6 } s.each do |k, v| h[k] = v end assert_equal s.values.sort, h.each_value.sort # values = [] h.each_value do |v| values << v end assert_equal s.values.sort, values.sort end def test_keys h = AVLTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s.keys.sort, h.keys.sort end def test_values h = AVLTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s.values.sort, h.values.sort end def test_to_s h = AVLTree.new h[5] = 1 assert_equal 1, h[5] assert_nil h["5"] end def test_key? h = AVLTree.new assert !h.key?('a') s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert h.key?('a') end def test_default assert_raise(ArgumentError) do AVLTree.new('both') { :not_allowed } end h = AVLTree.new('abc') assert_equal 'abc', h['foo'] assert_equal 'abc', h['bar'] assert h['baz'].object_id == h['qux'].object_id h = AVLTree.new { [1, 2] } assert_equal [1, 2], h['foo'] assert_equal [1, 2], h['bar'] assert h['baz'].object_id != h['qux'].object_id end def test_to_hash h = AVLTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s, h.to_hash end def test_clear h = AVLTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s, h.to_hash h.clear assert_equal 0, h.size assert h.to_hash.empty? end def test_non_string_keys h = AVLTree.new h[1.3] = 'a' h[4.3] = 'b' assert_equal [1.3, 'a' ], h.first end def test_values_for_empty_tree h = AVLTree.new assert_equal [], h.values end if RUBY_VERSION >= '1.9.0' # In contrast to RadixTree, AVLTree just uses String#<=> as-is def test_encoding h = AVLTree.new s = { '$B$"$"(B' => 1, '$B$"$$(B' => 2, '$B$$$$(B' => 3, '$B$$$&(B' => 4, '$B$"(B' => 5, '$B$"$$$&(B' => 6 } s.each do |k, v| h[k] = v end assert_equal 6, h.size s.each do |k, v| assert_equal v, h[k] end str = '$B$"$"(B' str.force_encoding('US-ASCII') # it's nil for RadixTree because RadixTree uses char-to-char comparison assert_equal 1, h[str] end end end ruby-avl-tree-1.1.3/test/test_red_black_tree.rb000066400000000000000000000343711236660463700215270ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('./helper', File.dirname(__FILE__)) class TestRedBlackTree < Test::Unit::TestCase def __test_random h = RedBlackTree.new 10000.times do |idx| key = rand(100) h[key] = key key = rand(100) h.delete(key) end end def test_tree_rotate_RR h = RedBlackTree.new assert_equal '', h.dump_sexp h['a'] = 1 assert_equal 'a', h.dump_sexp h['b'] = 2 assert_equal '(a - b)', h.dump_sexp h['c'] = 3 assert_equal '(b a c)', h.dump_sexp h['d'] = 4 assert_equal '(b a (c - d))', h.dump_sexp h['e'] = 5 assert_equal '(b a (d c e))', h.dump_sexp end def test_tree_rotate_LL h = RedBlackTree.new h['e'] = 1 h['d'] = 2 assert_equal '(e d)', h.dump_sexp h['c'] = 3 assert_equal '(d c e)', h.dump_sexp h['b'] = 4 assert_equal '(d (c b) e)', h.dump_sexp h['a'] = 5 assert_equal '(d (b a c) e)', h.dump_sexp end def test_tree_rotate_RL h = RedBlackTree.new h['b'] = 1 h['a'] = 2 h['g'] = 3 h['d'] = 4 h['h'] = 5 assert_equal '(b a (g d h))', h.dump_sexp h['c'] = 6 assert_equal '(b a (g (d c) h))', h.dump_sexp h['e'] = 6 assert_equal '(d (b a c) (g e h))', h.dump_sexp h['f'] = 6 assert_equal '(d (b a c) (g (e - f) h))', h.dump_sexp end def test_tree_rotate_LR h = RedBlackTree.new h['g'] = 1 h['b'] = 2 h['h'] = 3 h['i'] = 4 h['a'] = 5 h['d'] = 6 h['0'] = 7 h['c'] = 8 h['e'] = 9 assert_equal '(d (b (a 0) c) (g e (h - i)))', h.dump_sexp h['f'] = 10 assert_equal '(d (b (a 0) c) (g (e - f) (h - i)))', h.dump_sexp end def test_aref_nil h = RedBlackTree.new h['abc'] = 1 assert_equal nil, h['def'] end def test_empty h = RedBlackTree.new h['abc'] = 0 assert_equal nil, h[''] h[''] = 1 assert_equal 1, h[''] h.delete('') assert_equal nil, h[''] end def test_aref_single h = RedBlackTree.new h['abc'] = 1 assert_equal 1, h['abc'] end def test_aref_double h = RedBlackTree.new h['abc'] = 1 h['def'] = 2 assert_equal 1, h['abc'] assert_equal 2, h['def'] end def test_aset_override h = RedBlackTree.new h['abc'] = 1 h['abc'] = 2 assert_equal 2, h['abc'] end def test_split h = RedBlackTree.new h['abcd'] = 1 assert_equal 1, h['abcd'] h['abce'] = 2 assert_equal 1, h['abcd'] assert_equal 2, h['abce'] h['abd'] = 3 assert_equal 1, h['abcd'] assert_equal 2, h['abce'] assert_equal 3, h['abd'] h['ac'] = 4 assert_equal 1, h['abcd'] assert_equal 2, h['abce'] assert_equal 3, h['abd'] assert_equal 4, h['ac'] end def test_split_and_assign h = RedBlackTree.new h['ab'] = 1 h['a'] = 2 assert_equal 1, h['ab'] assert_equal 2, h['a'] end def test_push h = RedBlackTree.new assert_equal 0, h.size h['a'] = 1 assert_equal 1, h['a'] h['ab'] = 2 assert_equal 1, h['a'] assert_equal 2, h['ab'] h['abc'] = 3 assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] h['abd'] = 4 assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] assert_equal 4, h['abd'] h['ac'] = 5 assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] assert_equal 4, h['abd'] assert_equal 5, h['ac'] h['b'] = 6 assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] assert_equal 4, h['abd'] assert_equal 5, h['ac'] assert_equal 6, h['b'] assert_equal ['a', 'ab', 'abc', 'abd', 'ac', 'b'].sort, h.keys.sort assert_equal 6, h.size end def test_different_type h = RedBlackTree.new h['a'] = 1 assert_raise(TypeError) do h[3.3] = 2 end assert_nil h[3.3] end def test_delete_leaf h = RedBlackTree.new h['b'] = 1 h['a'] = 2 h['c'] = 3 assert_equal 2, h['a'] h.delete('a') assert_equal nil, h['a'] end def test_delete_leaf_single_rotation h = RedBlackTree.new h['b'] = 1 h['a'] = 2 h['d'] = 3 h['c'] = 4 h['e'] = 5 assert_equal '(b a (d c e))', h.dump_sexp h.delete('a') assert_equal '(d (b - c) e)', h.dump_sexp end def test_delete_leaf_single_rotation_right h = RedBlackTree.new h['d'] = 1 h['e'] = 2 h['b'] = 3 h['c'] = 4 h['a'] = 5 assert_equal '(d (b a c) e)', h.dump_sexp h.delete('e') assert_equal '(b a (d c))', h.dump_sexp end def test_delete_leaf_double_rotation h = RedBlackTree.new h['b'] = 1 h['a'] = 2 h['e'] = 3 h['0'] = 4 h['c'] = 5 h['f'] = 6 h['d'] = 7 assert_equal '(b (a 0) (e (c - d) f))', h.dump_sexp h.delete('0') assert_equal '(b a (e (c - d) f))', h.dump_sexp h.delete('a') assert_equal '(e (c b d) f)', h.dump_sexp end def test_delete_leaf_double_rotation_right h = RedBlackTree.new h['d'] = 1 h['e'] = 2 h['a'] = 3 h['f'] = 4 h['c'] = 5 h['0'] = 6 h['b'] = 7 assert_equal '(d (a 0 (c b)) (e - f))', h.dump_sexp h.delete('f') assert_equal '(d (a 0 (c b)) e)', h.dump_sexp h.delete('e') assert_equal '(a 0 (c b d))', h.dump_sexp end def test_delete_node_right h = RedBlackTree.new h['c'] = 1 h['b'] = 2 h['g'] = 3 h['a'] = 4 h['e'] = 5 h['i'] = 6 h['d'] = 7 h['f'] = 8 h['h'] = 9 h['j'] = 10 assert_equal '(e (c (b a) d) (g f (i h j)))', h.dump_sexp h.delete('g') assert_equal '(e (c (b a) d) (h f (i - j)))', h.dump_sexp end def test_delete_node_left h = RedBlackTree.new h['h'] = 1 h['i'] = 2 h['d'] = 3 h['j'] = 4 h['f'] = 5 h['b'] = 6 h['g'] = 7 h['e'] = 8 h['c'] = 9 h['a'] = 10 assert_equal '(f (d (b a c) e) (h g (i - j)))', h.dump_sexp h.delete('d') assert_equal '(f (b a (e c)) (h g (i - j)))', h.dump_sexp end def test_delete_root h = RedBlackTree.new h['b'] = 1 h['a'] = 2 h['c'] = 3 assert_equal 1, h['b'] assert_equal '(b a c)', h.dump_sexp h.delete('b') assert_equal '(c a)', h.dump_sexp assert_equal nil, h['b'] end def test_delete h = RedBlackTree.new h['a'] = 1 h['ab'] = 2 h['abc'] = 3 h['abd'] = 4 h['ac'] = 5 h['b'] = 6 assert_equal 6, h.size assert_equal nil, h.delete('XXX') # delete leaf assert_equal 4, h.delete('abd') assert_equal 5, h.size assert_equal 1, h['a'] assert_equal 2, h['ab'] assert_equal 3, h['abc'] assert_equal nil, h['abd'] assert_equal 5, h['ac'] assert_equal 6, h['b'] # delete single leaf node assert_equal 2, h.delete('ab') assert_equal 4, h.size assert_equal 1, h['a'] assert_equal nil, h['ab'] assert_equal 3, h['abc'] assert_equal nil, h['abd'] assert_equal 5, h['ac'] assert_equal 6, h['b'] # delete multiple leaf node assert_equal 1, h.delete('a') assert_equal 3, h.size assert_equal nil, h['a'] assert_equal nil, h['ab'] assert_equal 3, h['abc'] assert_equal nil, h['abd'] assert_equal 5, h['ac'] assert_equal 6, h['b'] assert_equal ['abc', 'ac', 'b'].sort, h.keys.sort # delete rest assert_equal 3, h.delete('abc') assert_equal 5, h.delete('ac') assert_equal 6, h.delete('b') assert_equal 0, h.size assert h.empty? end def test_delete_right h = RedBlackTree.new h['f'] = 1 h['e'] = 2 h['d'] = 3 h['c'] = 4 h['b'] = 5 h['a'] = 6 assert_equal 6, h.size assert_equal nil, h.delete('XXX') # delete leaf assert_equal 4, h.delete('c') assert_equal 5, h.size assert_equal 1, h['f'] assert_equal 2, h['e'] assert_equal 3, h['d'] assert_equal nil, h['c'] assert_equal 5, h['b'] assert_equal 6, h['a'] # delete single leaf node assert_equal 2, h.delete('e') assert_equal 4, h.size assert_equal 1, h['f'] assert_equal nil, h['e'] assert_equal 3, h['d'] assert_equal nil, h['c'] assert_equal 5, h['b'] assert_equal 6, h['a'] # delete multiple leaf node assert_equal 1, h.delete('f') assert_equal 3, h.size assert_equal nil, h['f'] assert_equal nil, h['e'] assert_equal 3, h['d'] assert_equal nil, h['c'] assert_equal 5, h['b'] assert_equal 6, h['a'] assert_equal ['a', 'b', 'd'].sort, h.keys.sort # delete rest assert_equal 3, h.delete('d') assert_equal 5, h.delete('b') assert_equal 6, h.delete('a') assert_equal 0, h.size assert h.empty? end def test_delete_compaction_middle h = RedBlackTree.new h['a'] = 1 h['abc'] = 2 h['bb'] = 3 h['abcdefghi'] = 4 h['abcdefghijzz'] = 5 h['abcdefghikzz'] = 6 assert_equal 6, h.dump_tree.split($/).size h.delete('a') assert_equal 5, h.dump_tree.split($/).size h['a'] = 1 assert_equal 6, h.dump_tree.split($/).size end def test_delete_compaction_leaf h = RedBlackTree.new h['a'] = 1 h['abc'] = 2 h['bb'] = 3 h['abcdefghijzz'] = 4 assert_equal 4, h.dump_tree.split($/).size h['abcdefghikzz'] = 5 assert_equal 5, h.dump_tree.split($/).size h.delete('abcdefghijzz') assert_equal 4, h.dump_tree.split($/).size h['abcdefghijzz'] = 4 assert_equal 5, h.dump_tree.split($/).size end def test_delete_balanced_rotate_left h = RedBlackTree.new h['f'] = 1 h['c'] = 100 h['l'] = 1 h['b'] = 100 h['e'] = 1 h['i'] = 1 h['m'] = 1 h['a'] = 100 h['d'] = 1 h['h'] = 1 h['k'] = 1 h['n'] = 1 h['j'] = 1 h['g'] = 1 assert_equal '(f (c (b a) (e d)) (l (i (h g) (k j)) (m - n)))', h.dump_sexp assert_equal 14, h.size # reduce black from the left node assert_equal 100, h.delete('b') assert_equal 100, h.delete('a') # double rotation at 'l' and 'f' node assert_equal 100, h.delete('c') assert_equal 11, h.size assert_equal '(i (f (d - e) (h g)) (l (k j) (m - n)))', h.dump_sexp end def test_delete_balanced_rotate_right h = RedBlackTree.new h['i'] = 1 h['l'] = 100 h['c'] = 1 h['m'] = 100 h['j'] = 1 h['f'] = 1 h['b'] = 1 h['n'] = 100 h['k'] = 1 h['g'] = 1 h['d'] = 1 h['a'] = 1 h['e'] = 1 h['h'] = 1 assert_equal '(i (c (b a) (f (d - e) (g - h))) (l (j - k) (m - n)))', h.dump_sexp assert_equal 14, h.size # reduce black from the left node assert_equal 100, h.delete('m') assert_equal 100, h.delete('n') # double rotation at 'c' and 'i' node assert_equal 100, h.delete('l') assert_equal 11, h.size assert_equal '(f (c (b a) (d - e)) (i (g - h) (k j)))', h.dump_sexp end def test_delete_different_type h = RedBlackTree.new h['a'] = 1 h['abc'] = 2 h['bb'] = 3 assert_raise(TypeError) do h.delete(3.3) end end def test_each h = RedBlackTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s.to_a.sort_by { |k, v| k }, h.each.sort_by { |k, v| k } # values = [] h.each do |k, v| values << [k, v] end assert_equal s.to_a.sort_by { |k, v| k }, values.sort_by { |k, v| k } end def test_each_key h = RedBlackTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s.keys.sort, h.each_key.sort # values = [] h.each_key do |k| values << k end assert_equal s.keys.sort, values.sort end def test_each_value h = RedBlackTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6, 'azzzzz' => 6 } s.each do |k, v| h[k] = v end assert_equal s.values.sort, h.each_value.sort # values = [] h.each_value do |v| values << v end assert_equal s.values.sort, values.sort end def test_keys h = RedBlackTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s.keys.sort, h.keys.sort end def test_values h = RedBlackTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s.values.sort, h.values.sort end def test_to_s h = RedBlackTree.new h[5] = 1 assert_equal 1, h[5] assert_nil h["5"] end def test_key? h = RedBlackTree.new assert !h.key?('a') s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert h.key?('a') end def test_default assert_raise(ArgumentError) do RedBlackTree.new('both') { :not_allowed } end h = RedBlackTree.new('abc') assert_equal 'abc', h['foo'] assert_equal 'abc', h['bar'] assert h['baz'].object_id == h['qux'].object_id h = RedBlackTree.new { [1, 2] } assert_equal [1, 2], h['foo'] assert_equal [1, 2], h['bar'] assert h['baz'].object_id != h['qux'].object_id end def test_to_hash h = RedBlackTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s, h.to_hash end def test_clear h = RedBlackTree.new s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 } s.each do |k, v| h[k] = v end assert_equal s, h.to_hash h.clear assert_equal 0, h.size assert h.to_hash.empty? end def test_non_string_keys h = RedBlackTree.new h[1.3] = 'a' h[4.3] = 'b' assert_equal [1.3, 'a' ], h.first end def test_values_for_empty_tree h = RedBlackTree.new assert_equal [], h.values end if RUBY_VERSION >= '1.9.0' # In contrast to RadixTree, RedBlackTree just uses String#<=> as-is def test_encoding h = RedBlackTree.new s = { '$B$"$"(B' => 1, '$B$"$$(B' => 2, '$B$$$$(B' => 3, '$B$$$&(B' => 4, '$B$"(B' => 5, '$B$"$$$&(B' => 6 } s.each do |k, v| h[k] = v end assert_equal 6, h.size s.each do |k, v| assert_equal v, h[k] end str = '$B$"$"(B' str.force_encoding('US-ASCII') # it's nil for RadixTree because RadixTree uses char-to-char comparison assert_equal 1, h[str] end end end