lite-containers 0.0.5

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +72 -0
  4. data/Gemfile +14 -0
  5. data/Gemfile.lock +127 -0
  6. data/README.md +145 -0
  7. data/bench/containers_bench.rb +122 -0
  8. data/lib/lite/containers/abstract/collection.rb +25 -0
  9. data/lib/lite/containers/abstract/deque.rb +23 -0
  10. data/lib/lite/containers/abstract/implicit_key.rb +23 -0
  11. data/lib/lite/containers/abstract/queue.rb +24 -0
  12. data/lib/lite/containers/abstract/sorted_map.rb +29 -0
  13. data/lib/lite/containers/avl_tree/balance.rb +74 -0
  14. data/lib/lite/containers/avl_tree/delete.rb +32 -0
  15. data/lib/lite/containers/avl_tree/find.rb +99 -0
  16. data/lib/lite/containers/avl_tree/implementation.rb +88 -0
  17. data/lib/lite/containers/avl_tree/insert.rb +27 -0
  18. data/lib/lite/containers/avl_tree/interfaces/bracket_assign.rb +33 -0
  19. data/lib/lite/containers/avl_tree/interfaces/key_extraction_strategy/abstract.rb +48 -0
  20. data/lib/lite/containers/avl_tree/interfaces/key_extraction_strategy/explicit.rb +34 -0
  21. data/lib/lite/containers/avl_tree/interfaces/key_extraction_strategy/implicit.rb +60 -0
  22. data/lib/lite/containers/avl_tree/node.rb +35 -0
  23. data/lib/lite/containers/avl_tree/traversal.rb +43 -0
  24. data/lib/lite/containers/avl_tree.rb +159 -0
  25. data/lib/lite/containers/error.rb +7 -0
  26. data/lib/lite/containers/heap.rb +169 -0
  27. data/lib/lite/containers/helpers/comparison.rb +106 -0
  28. data/lib/lite/containers/helpers/key_extractor.rb +29 -0
  29. data/lib/lite/containers/helpers/merge.rb +49 -0
  30. data/lib/lite/containers/sorted_array/binary_search.rb +42 -0
  31. data/lib/lite/containers/sorted_array.rb +188 -0
  32. data/lib/lite/containers/top_n/abstract.rb +56 -0
  33. data/lib/lite/containers/top_n/avl_tree.rb +25 -0
  34. data/lib/lite/containers/top_n/deque.rb +33 -0
  35. data/lib/lite/containers/top_n/error.rb +11 -0
  36. data/lib/lite/containers/top_n/heap.rb +32 -0
  37. data/lib/lite/containers/top_n.rb +31 -0
  38. data/lib/lite/containers/version.rb +7 -0
  39. data/lib/lite/containers.rb +3 -0
  40. data/lite_containers.gemspec +25 -0
  41. metadata +83 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ab4551d5c45aa8900a6f1efc501a564eab1a4a010a1c7ecaa309965a0541c272
4
+ data.tar.gz: d6fd375ee6d2eb8b0a127cd14190e8bf3f2cf596f2ca1247107a6b951c0d98da
5
+ SHA512:
6
+ metadata.gz: 35b991f983faff3278c9b86d02d77c1cb86cbd7b5b0ff9b2129aa3046d19c40322dc9134ac528b85676efafc4d490bf2b07364112cfa9f8a7d9aa366f3c08749
7
+ data.tar.gz: 600d9406293be9e0dae2680b8e7d25faa39ff4f54be3ef77c4f6fdfb1f3d45fb87ef583c2f08ac75c0800db1bc6e276a95f8d50448597cb19f5a63e96e544258
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .byebug_history
2
+ .ruby-version
3
+ *.gem
4
+ .idea/*
5
+ coverage/*
data/.rubocop.yml ADDED
@@ -0,0 +1,72 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+
7
+ Gemspec/RequireMFA:
8
+ Enabled: false
9
+
10
+ Metrics/ClassLength:
11
+ Exclude:
12
+ - 'spec/**/*.rb'
13
+
14
+ Metrics/AbcSize:
15
+ Exclude:
16
+ - 'spec/**/*.rb'
17
+
18
+ Metrics/BlockLength:
19
+ Exclude:
20
+ - 'spec/**/*.rb'
21
+
22
+ Metrics/CyclomaticComplexity:
23
+ Exclude:
24
+ - 'spec/**/*.rb'
25
+
26
+ Metrics/MethodLength:
27
+ Max: 20
28
+
29
+ Metrics/ModuleLength:
30
+ Exclude:
31
+ - 'spec/**/*.rb'
32
+
33
+ Metrics/PerceivedComplexity:
34
+ Exclude:
35
+ - 'spec/**/*.rb'
36
+
37
+ Naming/MethodParameterName:
38
+ Enabled: false
39
+
40
+ Naming/VariableNumber:
41
+ Exclude:
42
+ - 'spec/**/*.rb'
43
+
44
+ Lint/ShadowingOuterLocalVariable:
45
+ Enabled: false
46
+
47
+ Layout/EndAlignment:
48
+ EnforcedStyleAlignWith: start_of_line
49
+
50
+ RSpec/ContextWording:
51
+ Enabled: false
52
+
53
+ RSpec/ExampleLength:
54
+ Enabled: false
55
+
56
+ RSpec/IndexedLet:
57
+ Enabled: false
58
+
59
+ RSpec/MultipleExpectations:
60
+ Enabled: false
61
+
62
+ RSpec/MultipleMemoizedHelpers:
63
+ Enabled: false
64
+
65
+ Style/ArgumentsForwarding:
66
+ Enabled: false
67
+
68
+ Style/Documentation:
69
+ Enabled: false
70
+
71
+ Style/HashSyntax:
72
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ group :test do
6
+ gem 'byebug', '~> 11'
7
+ gem 'markly'
8
+ gem 'rspec', '~> 3'
9
+ gem 'rubocop'
10
+ gem 'rubocop-rspec'
11
+ gem 'rubycritic'
12
+ gem 'ruby-prof'
13
+ gem 'simplecov'
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,127 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.8.7)
5
+ public_suffix (>= 2.0.2, < 7.0)
6
+ ast (2.4.2)
7
+ axiom-types (0.1.1)
8
+ descendants_tracker (~> 0.0.4)
9
+ ice_nine (~> 0.11.0)
10
+ thread_safe (~> 0.3, >= 0.3.1)
11
+ byebug (11.1.3)
12
+ childprocess (5.1.0)
13
+ logger (~> 1.5)
14
+ coercible (1.0.0)
15
+ descendants_tracker (~> 0.0.1)
16
+ descendants_tracker (0.0.4)
17
+ thread_safe (~> 0.3, >= 0.3.1)
18
+ diff-lcs (1.5.1)
19
+ docile (1.4.1)
20
+ erubi (1.13.1)
21
+ flay (2.13.3)
22
+ erubi (~> 1.10)
23
+ path_expander (~> 1.0)
24
+ ruby_parser (~> 3.0)
25
+ sexp_processor (~> 4.0)
26
+ flog (4.8.0)
27
+ path_expander (~> 1.0)
28
+ ruby_parser (~> 3.1, > 3.1.0)
29
+ sexp_processor (~> 4.8)
30
+ ice_nine (0.11.2)
31
+ json (2.9.1)
32
+ kwalify (0.7.2)
33
+ language_server-protocol (3.17.0.3)
34
+ launchy (3.0.1)
35
+ addressable (~> 2.8)
36
+ childprocess (~> 5.0)
37
+ logger (1.6.4)
38
+ markly (0.12.1)
39
+ parallel (1.26.3)
40
+ parser (3.2.2.4)
41
+ ast (~> 2.4.1)
42
+ racc
43
+ path_expander (1.1.3)
44
+ public_suffix (5.1.1)
45
+ racc (1.8.1)
46
+ rainbow (3.1.1)
47
+ reek (6.1.4)
48
+ kwalify (~> 0.7.0)
49
+ parser (~> 3.2.0)
50
+ rainbow (>= 2.0, < 4.0)
51
+ regexp_parser (2.10.0)
52
+ rexml (3.4.0)
53
+ rspec (3.13.0)
54
+ rspec-core (~> 3.13.0)
55
+ rspec-expectations (~> 3.13.0)
56
+ rspec-mocks (~> 3.13.0)
57
+ rspec-core (3.13.2)
58
+ rspec-support (~> 3.13.0)
59
+ rspec-expectations (3.13.3)
60
+ diff-lcs (>= 1.2.0, < 2.0)
61
+ rspec-support (~> 3.13.0)
62
+ rspec-mocks (3.13.2)
63
+ diff-lcs (>= 1.2.0, < 2.0)
64
+ rspec-support (~> 3.13.0)
65
+ rspec-support (3.13.2)
66
+ rubocop (1.59.0)
67
+ json (~> 2.3)
68
+ language_server-protocol (>= 3.17.0)
69
+ parallel (~> 1.10)
70
+ parser (>= 3.2.2.4)
71
+ rainbow (>= 2.2.2, < 4.0)
72
+ regexp_parser (>= 1.8, < 3.0)
73
+ rexml (>= 3.2.5, < 4.0)
74
+ rubocop-ast (>= 1.30.0, < 2.0)
75
+ ruby-progressbar (~> 1.7)
76
+ unicode-display_width (>= 2.4.0, < 3.0)
77
+ rubocop-ast (1.30.0)
78
+ parser (>= 3.2.1.0)
79
+ rubocop-rspec (3.0.0)
80
+ rubocop (~> 1.40)
81
+ ruby-prof (1.6.3)
82
+ ruby-progressbar (1.13.0)
83
+ ruby_parser (3.21.1)
84
+ racc (~> 1.5)
85
+ sexp_processor (~> 4.16)
86
+ rubycritic (4.9.1)
87
+ flay (~> 2.13)
88
+ flog (~> 4.7)
89
+ launchy (>= 2.5.2)
90
+ parser (>= 3.2.2.4)
91
+ rainbow (~> 3.1.1)
92
+ reek (~> 6.0, < 6.2)
93
+ rexml
94
+ ruby_parser (~> 3.21)
95
+ simplecov (>= 0.22.0)
96
+ tty-which (~> 0.5.0)
97
+ virtus (~> 2.0)
98
+ sexp_processor (4.17.3)
99
+ simplecov (0.22.0)
100
+ docile (~> 1.1)
101
+ simplecov-html (~> 0.11)
102
+ simplecov_json_formatter (~> 0.1)
103
+ simplecov-html (0.13.1)
104
+ simplecov_json_formatter (0.1.4)
105
+ thread_safe (0.3.6)
106
+ tty-which (0.5.0)
107
+ unicode-display_width (2.6.0)
108
+ virtus (2.0.0)
109
+ axiom-types (~> 0.1)
110
+ coercible (~> 1.0)
111
+ descendants_tracker (~> 0.0, >= 0.0.3)
112
+
113
+ PLATFORMS
114
+ x86_64-linux
115
+
116
+ DEPENDENCIES
117
+ byebug (~> 11)
118
+ markly
119
+ rspec (~> 3)
120
+ rubocop
121
+ rubocop-rspec
122
+ ruby-prof
123
+ rubycritic
124
+ simplecov
125
+
126
+ BUNDLED WITH
127
+ 2.4.1
data/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # lite-containers
2
+
3
+ A collection of containers implemented in pure Ruby.
4
+
5
+ - `Heap` uses the well-known heap algorithm. The heap contract
6
+ guarantees that the highest ranking element is always on top
7
+ - `SortedArray` uses built-in quick sort and binary search to
8
+ keep elements ordered
9
+ - `AvlTree` keeps elements in a self-balancing binary search tree
10
+
11
+ ## Common features
12
+ All containers have the following in common:
13
+ - `empty?` and `size` methods to query the size
14
+ (along with the synonyms: `length`, `count`)
15
+ - the `drain!` method that empties the container and
16
+ returns all elements in descending order (top priority first).
17
+
18
+ ## Constructor parameters
19
+ To keep elements sorted the containers need certain parameters
20
+ and helpers provided by the client code.
21
+ - First parameter to the constructor is always `type`, taking values
22
+ of `min` and `max`, telling the container how to interpret priority.
23
+ - `key_extractor` is a proc that receives an element and produces
24
+ the key used for sorting. When key extractor is `nil`, the
25
+ value itself is considered the sort key.
26
+ - Sorted array and AVL tree can only operate on unique
27
+ entries. Their constructors expect `merge` parameter
28
+ to supply a merge strategy. Allowed values are `:replace`, `:keep`
29
+ or a proc that performs the merge. The proc should take two elements
30
+ as parameters and return a single one. The default strategy
31
+ for both containers is `:replace` meaning that in presence of duplicates
32
+ the new element replaces the old one.
33
+
34
+ Helpers must honor the following rules for the containers
35
+ to maintain consistent sort order:
36
+ - The key extractor must always return the same key for the same value
37
+ - The new merged element must resolve to the same key
38
+ as the original two.
39
+
40
+ Containers do not enforce these rules; it is the responsibility
41
+ of the client code to provide correctly implemented helpers.
42
+
43
+ ## Heap
44
+ Heap is the simplest of the three containers in this library.
45
+ It supports `push`/`<<` method that inserts an element, `top` to
46
+ return the top priority element, and `pop` to remove the top priority
47
+ element.
48
+
49
+ ```ruby rspec heap
50
+ require 'lite/containers/heap'
51
+
52
+ heap = Lite::Containers::Heap.instance(:max)
53
+ heap << 5 << 1 << 3
54
+
55
+ expect(heap.top).to eq(5)
56
+ expect(heap.pop).to eq(5)
57
+ expect(heap.drain!).to eq([3, 1])
58
+ ```
59
+
60
+ ## Sorted array
61
+ Useful for maintaining relatively stable lists that either never change
62
+ or change infrequently. Each insert or delete will reallocate
63
+ the underlying array making it really inefficient
64
+ when the churn is high.
65
+
66
+ Depending on the nature of the data, different constructors
67
+ can be used to build the sorted array. If the
68
+ data come in random order it is necessary to call `from_unsorted`
69
+ to have the container sort them.
70
+ If the data are already sorted, `from_sorted` could be used,
71
+ or even `from_sorted_unsafe`. The former checks whether the data
72
+ are properly sorted and raises an error when it discovers
73
+ unexpected ordering. The second one performs no check on
74
+ the data leaving that responsibility to the client code.
75
+
76
+ Sorted array includes the `Enumerable` module, giving
77
+ access to all its methods like `map`, `reduce` etc.
78
+
79
+
80
+ ```ruby rspec sorted_array
81
+ require 'lite/containers/sorted_array'
82
+
83
+ sorted_array = Lite::Containers::SortedArray.from_unsorted(
84
+ :max,
85
+ [{ income: 0, tax: 15 }, { income: 120_000, tax: 25 }, { income: 60_000, tax: 20 }],
86
+ key_extractor: proc { |elem| elem[:income] }
87
+ )
88
+
89
+ expect(sorted_array.front).to eq({ income: 120_000, tax: 25 })
90
+ expect(sorted_array.back).to eq({ income: 0, tax: 15 })
91
+ expect(sorted_array.find(60_000)).to eq({ income: 60_000, tax: 20 })
92
+ expect(sorted_array.find_or_nearest_forwards(80_000)).to eq({ income: 120_000, tax: 25 })
93
+ expect(sorted_array.find_or_nearest_backwards(80_000)).to eq({ income: 60_000, tax: 20 })
94
+ ```
95
+
96
+ ## AVL tree
97
+ Comes in two flavors – `ImplicitKey` and `ExplicitKey`.
98
+
99
+ - `ImplicitKey` implements `push`/`<<` with single parameter – the element.
100
+ It may need a *key extractor* to be supplied.
101
+
102
+ ```ruby rspec avl_tree_implicit
103
+ require 'lite/containers/avl_tree'
104
+
105
+ tree = Lite::Containers::AvlTree::ImplicitKey.instance(
106
+ :max,
107
+ key_extractor: proc { |elem| elem[:time] }
108
+ )
109
+
110
+ tree << { time: 0, t: 30 } << { time: 45, t: 600 } << { time: 60, t: 800 } << { time: 120, t: 750 }
111
+
112
+ expect(tree.front).to eq({ time: 120, t: 750 })
113
+ expect(tree.back).to eq({ time: 0, t: 30 })
114
+ expect(tree.find(45)).to eq({ time: 45, t: 600 })
115
+ expect(tree.find_or_nearest_forwards(55)).to eq({ time: 60, t: 800 })
116
+ expect(tree.find_or_nearest_backwards(55)).to eq({ time: 45, t: 600 })
117
+ ```
118
+
119
+ - `ExplicitKey` implements `insert` that takes two parameters – the key and the value.
120
+ Methods like `front`, `back`, `find` etc. return a key/value tuple instead
121
+ of a single element.
122
+
123
+ ```ruby rspec avl_tree_explicit
124
+ require 'lite/containers/avl_tree'
125
+
126
+ tree = Lite::Containers::AvlTree::ExplicitKey.instance(
127
+ :min,
128
+ merge: proc { |old, fresh| { count: old[:count] + fresh[:count] } }
129
+ )
130
+
131
+ tree.insert(0, { count: 1 })
132
+ .insert(10, { count: 1 })
133
+ .insert(10, { count: 1 })
134
+ .insert(20, { count: 1 })
135
+ .insert(30, { count: 1 })
136
+
137
+ expect(tree.pop_front).to eq([0, { count: 1 }])
138
+ expect(tree.pop_back).to eq([30, { count: 1 }])
139
+ expect(tree.drain!).to eq([[10, { count: 2 }], [20, { count: 1 }]])
140
+ ```
141
+
142
+ AVL tree also includes the `Enumerable` module.
143
+
144
+ ## License
145
+ This library is published under MIT license
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'benchmark'
4
+ require_relative '../lib/lite/containers/sorted_array'
5
+ require_relative '../lib/lite/containers/heap'
6
+ require_relative '../lib/lite/containers/avl_tree'
7
+
8
+ module Lite
9
+ module Containers
10
+ module AbstractBench
11
+ def measure(n)
12
+ warm_up
13
+ subjects = populate(n)
14
+ output_to_sorted_array(subjects)
15
+ end
16
+
17
+ def warm_up
18
+ puts ' warm up ...'
19
+
20
+ 100.times do
21
+ subject = initial_subject
22
+ 1000.times do
23
+ insert_single(subject, rand(1000))
24
+ output(subject)
25
+ end
26
+ end
27
+ end
28
+
29
+ def populate(n)
30
+ puts "\n populate ..."
31
+
32
+ subjects = []
33
+ times = 100_000 / n
34
+
35
+ result = Benchmark.measure do
36
+ times.times do
37
+ subject = initial_subject
38
+ n.times do
39
+ insert_single(subject, rand(n))
40
+ end
41
+ subjects << subject
42
+ end
43
+ end
44
+ puts result
45
+ subjects
46
+ end
47
+
48
+ def output_to_sorted_array(subjects)
49
+ puts "\n output from #{subjects.length} subjects to sorted array of #{subjects.first&.length || 0}"
50
+
51
+ result = Benchmark.measure do
52
+ subjects.each do |subject|
53
+ output(subject)
54
+ end
55
+ end
56
+ puts result
57
+ end
58
+ end
59
+
60
+ module ArrayBench
61
+ extend AbstractBench
62
+
63
+ def self.initial_subject
64
+ SortedArray.from_sorted_unsafe(:max, [])
65
+ end
66
+
67
+ def self.insert_single(array, value)
68
+ array << value
69
+ end
70
+
71
+ def self.output(array)
72
+ array.to_array
73
+ end
74
+ end
75
+
76
+ module HeapBench
77
+ extend AbstractBench
78
+
79
+ def self.initial_subject
80
+ Heap.instance(:max)
81
+ end
82
+
83
+ def self.insert_single(heap, value)
84
+ heap << value
85
+ end
86
+
87
+ def self.output(heap)
88
+ heap.drain!
89
+ end
90
+ end
91
+
92
+ module AvlTreeBench
93
+ extend AbstractBench
94
+
95
+ def self.initial_subject
96
+ AvlTree::ExplicitKey.instance :max
97
+ end
98
+
99
+ def self.insert_single(tree, value)
100
+ tree.insert(value, nil)
101
+ end
102
+
103
+ def self.output(tree)
104
+ tree.traverse.to_a
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ N = 10_000
111
+
112
+ puts "------------- ARRAY #{N} -------------"
113
+ Lite::Containers::ArrayBench.measure(N)
114
+ puts '--------------------------------------'
115
+ puts "------------- HEAP #{N} --------------"
116
+ Lite::Containers::HeapBench.measure(N)
117
+ puts '--------------------------------------'
118
+ puts "------------- AVL #{N} ---------------"
119
+ Lite::Containers::AvlTreeBench.measure(N)
120
+ puts '--------------------------------------'
121
+
122
+ # https://stackoverflow.com/questions/6531543/efficient-implementation-of-binary-heaps
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Containers
5
+ module Abstract
6
+ module Collection
7
+ def size
8
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
9
+ end
10
+
11
+ def length
12
+ size
13
+ end
14
+
15
+ def count
16
+ size
17
+ end
18
+
19
+ def empty?
20
+ size.zero?
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'queue'
4
+
5
+ module Lite
6
+ module Containers
7
+ module Abstract
8
+ module Deque
9
+ include Queue
10
+
11
+ # Removes and returns lowest priority element. Does nothing if empty
12
+ def back
13
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
14
+ end
15
+
16
+ # Removes and returns lowest priority element. Does nothing if empty
17
+ def pop_back
18
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'queue'
4
+
5
+ module Lite
6
+ module Containers
7
+ module Abstract
8
+ module ImplicitKey
9
+ # Inserts element at its corresponding place.
10
+ # In case element is already there applies merge strategy
11
+ def push(_element)
12
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
13
+ end
14
+
15
+ # Inserts element the way push does. Returns self
16
+ def <<(element)
17
+ push(element)
18
+ self
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Containers
5
+ module Abstract
6
+ module Queue
7
+ # Returns highest priority element or nil if empty
8
+ def front
9
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
10
+ end
11
+
12
+ # Removes and returns highest priority element or nil if empty
13
+ def pop_front
14
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
15
+ end
16
+
17
+ # Returns all elements in descending order by priority
18
+ def drain!
19
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Containers
5
+ module Abstract
6
+ module SortedMap
7
+ # Returns if key exists, false otherwise
8
+ def key?(key)
9
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
10
+ end
11
+
12
+ # Returns element under given key if exists
13
+ def find(_key)
14
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
15
+ end
16
+
17
+ # Returns element under given key or nearest element under lower priority key if exists
18
+ def find_or_nearest_backwards(_key)
19
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
20
+ end
21
+
22
+ # Returns value under given key or nearest element under higher priority key if exists
23
+ def find_or_nearest_forwards(_key)
24
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Containers
5
+ class AvlTree
6
+ module Balance
7
+ def rebalance(node) # rubocop:disable Metrics/AbcSize
8
+ case balance_factor(node)
9
+ when -2
10
+ if height(node.left.right) > height(node.left.left)
11
+ rotate_left_right(node)
12
+ else
13
+ rotate_right(node)
14
+ end
15
+ when 2
16
+ if height(node.right.left) > height(node.right.right)
17
+ rotate_right_left(node)
18
+ else
19
+ rotate_left(node)
20
+ end
21
+ else
22
+ update_height(node)
23
+ node
24
+ end
25
+ end
26
+
27
+ def rotate_left(node)
28
+ right = node.right
29
+ node.right = right.left
30
+ right.left = node
31
+ update_height node
32
+ update_height right
33
+ right
34
+ end
35
+
36
+ def rotate_right(node)
37
+ left = node.left
38
+ node.left = left.right
39
+ left.right = node
40
+ update_height node
41
+ update_height left
42
+ left
43
+ end
44
+
45
+ def rotate_right_left(node)
46
+ node.right = rotate_right(node.right)
47
+ rotate_left(node)
48
+ end
49
+
50
+ def rotate_left_right(node)
51
+ node.left = rotate_left(node.left)
52
+ rotate_right(node)
53
+ end
54
+
55
+ def update_height(node)
56
+ left_height = height(node.left)
57
+ right_height = height(node.right)
58
+ # rubocop:disable Style/MinMaxComparison
59
+ max = left_height > right_height ? left_height : right_height
60
+ # rubocop:enable Style/MinMaxComparison
61
+ node.height = max + 1
62
+ end
63
+
64
+ def balance_factor(node)
65
+ height(node.right) - height(node.left)
66
+ end
67
+
68
+ def height(node)
69
+ node&.height || 0
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end