immutable-ruby 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.
- checksums.yaml +7 -0
- data/lib/immutable.rb +9 -0
- data/lib/immutable/core_ext.rb +2 -0
- data/lib/immutable/core_ext/enumerable.rb +11 -0
- data/lib/immutable/core_ext/io.rb +21 -0
- data/lib/immutable/deque.rb +254 -0
- data/lib/immutable/enumerable.rb +152 -0
- data/lib/immutable/hash.rb +841 -0
- data/lib/immutable/list.rb +1595 -0
- data/lib/immutable/nested.rb +75 -0
- data/lib/immutable/set.rb +583 -0
- data/lib/immutable/sorted_set.rb +1464 -0
- data/lib/immutable/trie.rb +338 -0
- data/lib/immutable/undefined.rb +5 -0
- data/lib/immutable/vector.rb +1539 -0
- data/lib/immutable/version.rb +5 -0
- data/spec/fixtures/io_spec.txt +3 -0
- data/spec/lib/immutable/core_ext/array_spec.rb +13 -0
- data/spec/lib/immutable/core_ext/enumerable_spec.rb +29 -0
- data/spec/lib/immutable/core_ext/io_spec.rb +28 -0
- data/spec/lib/immutable/deque/clear_spec.rb +33 -0
- data/spec/lib/immutable/deque/construction_spec.rb +29 -0
- data/spec/lib/immutable/deque/copying_spec.rb +19 -0
- data/spec/lib/immutable/deque/dequeue_spec.rb +34 -0
- data/spec/lib/immutable/deque/empty_spec.rb +39 -0
- data/spec/lib/immutable/deque/enqueue_spec.rb +27 -0
- data/spec/lib/immutable/deque/first_spec.rb +17 -0
- data/spec/lib/immutable/deque/inspect_spec.rb +23 -0
- data/spec/lib/immutable/deque/last_spec.rb +17 -0
- data/spec/lib/immutable/deque/marshal_spec.rb +33 -0
- data/spec/lib/immutable/deque/new_spec.rb +43 -0
- data/spec/lib/immutable/deque/pop_spec.rb +36 -0
- data/spec/lib/immutable/deque/pretty_print_spec.rb +23 -0
- data/spec/lib/immutable/deque/push_spec.rb +36 -0
- data/spec/lib/immutable/deque/random_modification_spec.rb +33 -0
- data/spec/lib/immutable/deque/shift_spec.rb +29 -0
- data/spec/lib/immutable/deque/size_spec.rb +19 -0
- data/spec/lib/immutable/deque/to_a_spec.rb +26 -0
- data/spec/lib/immutable/deque/to_ary_spec.rb +35 -0
- data/spec/lib/immutable/deque/to_list_spec.rb +24 -0
- data/spec/lib/immutable/deque/unshift_spec.rb +30 -0
- data/spec/lib/immutable/hash/all_spec.rb +53 -0
- data/spec/lib/immutable/hash/any_spec.rb +53 -0
- data/spec/lib/immutable/hash/assoc_spec.rb +51 -0
- data/spec/lib/immutable/hash/clear_spec.rb +42 -0
- data/spec/lib/immutable/hash/construction_spec.rb +38 -0
- data/spec/lib/immutable/hash/copying_spec.rb +13 -0
- data/spec/lib/immutable/hash/default_proc_spec.rb +72 -0
- data/spec/lib/immutable/hash/delete_spec.rb +39 -0
- data/spec/lib/immutable/hash/each_spec.rb +77 -0
- data/spec/lib/immutable/hash/each_with_index_spec.rb +29 -0
- data/spec/lib/immutable/hash/empty_spec.rb +43 -0
- data/spec/lib/immutable/hash/eql_spec.rb +69 -0
- data/spec/lib/immutable/hash/except_spec.rb +42 -0
- data/spec/lib/immutable/hash/fetch_spec.rb +57 -0
- data/spec/lib/immutable/hash/find_spec.rb +43 -0
- data/spec/lib/immutable/hash/flat_map_spec.rb +35 -0
- data/spec/lib/immutable/hash/flatten_spec.rb +98 -0
- data/spec/lib/immutable/hash/get_spec.rb +79 -0
- data/spec/lib/immutable/hash/has_key_spec.rb +31 -0
- data/spec/lib/immutable/hash/has_value_spec.rb +27 -0
- data/spec/lib/immutable/hash/hash_spec.rb +29 -0
- data/spec/lib/immutable/hash/inspect_spec.rb +30 -0
- data/spec/lib/immutable/hash/invert_spec.rb +30 -0
- data/spec/lib/immutable/hash/key_spec.rb +27 -0
- data/spec/lib/immutable/hash/keys_spec.rb +15 -0
- data/spec/lib/immutable/hash/map_spec.rb +45 -0
- data/spec/lib/immutable/hash/marshal_spec.rb +28 -0
- data/spec/lib/immutable/hash/merge_spec.rb +82 -0
- data/spec/lib/immutable/hash/min_max_spec.rb +45 -0
- data/spec/lib/immutable/hash/new_spec.rb +70 -0
- data/spec/lib/immutable/hash/none_spec.rb +48 -0
- data/spec/lib/immutable/hash/partition_spec.rb +35 -0
- data/spec/lib/immutable/hash/pretty_print_spec.rb +34 -0
- data/spec/lib/immutable/hash/put_spec.rb +102 -0
- data/spec/lib/immutable/hash/reduce_spec.rb +35 -0
- data/spec/lib/immutable/hash/reject_spec.rb +61 -0
- data/spec/lib/immutable/hash/reverse_each_spec.rb +27 -0
- data/spec/lib/immutable/hash/sample_spec.rb +13 -0
- data/spec/lib/immutable/hash/select_spec.rb +57 -0
- data/spec/lib/immutable/hash/size_spec.rb +51 -0
- data/spec/lib/immutable/hash/slice_spec.rb +44 -0
- data/spec/lib/immutable/hash/sort_spec.rb +26 -0
- data/spec/lib/immutable/hash/store_spec.rb +75 -0
- data/spec/lib/immutable/hash/take_spec.rb +35 -0
- data/spec/lib/immutable/hash/to_a_spec.rb +13 -0
- data/spec/lib/immutable/hash/to_hash_spec.rb +21 -0
- data/spec/lib/immutable/hash/update_in_spec.rb +79 -0
- data/spec/lib/immutable/hash/values_at_spec.rb +13 -0
- data/spec/lib/immutable/hash/values_spec.rb +23 -0
- data/spec/lib/immutable/list/add_spec.rb +25 -0
- data/spec/lib/immutable/list/all_spec.rb +57 -0
- data/spec/lib/immutable/list/any_spec.rb +49 -0
- data/spec/lib/immutable/list/append_spec.rb +38 -0
- data/spec/lib/immutable/list/at_spec.rb +29 -0
- data/spec/lib/immutable/list/break_spec.rb +69 -0
- data/spec/lib/immutable/list/cadr_spec.rb +38 -0
- data/spec/lib/immutable/list/chunk_spec.rb +28 -0
- data/spec/lib/immutable/list/clear_spec.rb +24 -0
- data/spec/lib/immutable/list/combination_spec.rb +33 -0
- data/spec/lib/immutable/list/compact_spec.rb +34 -0
- data/spec/lib/immutable/list/compare_spec.rb +30 -0
- data/spec/lib/immutable/list/cons_spec.rb +25 -0
- data/spec/lib/immutable/list/construction_spec.rb +110 -0
- data/spec/lib/immutable/list/copying_spec.rb +19 -0
- data/spec/lib/immutable/list/count_spec.rb +36 -0
- data/spec/lib/immutable/list/cycle_spec.rb +28 -0
- data/spec/lib/immutable/list/delete_at_spec.rb +18 -0
- data/spec/lib/immutable/list/delete_spec.rb +16 -0
- data/spec/lib/immutable/list/drop_spec.rb +30 -0
- data/spec/lib/immutable/list/drop_while_spec.rb +38 -0
- data/spec/lib/immutable/list/each_slice_spec.rb +51 -0
- data/spec/lib/immutable/list/each_spec.rb +40 -0
- data/spec/lib/immutable/list/each_with_index_spec.rb +28 -0
- data/spec/lib/immutable/list/empty_spec.rb +23 -0
- data/spec/lib/immutable/list/eql_spec.rb +61 -0
- data/spec/lib/immutable/list/fill_spec.rb +49 -0
- data/spec/lib/immutable/list/find_all_spec.rb +70 -0
- data/spec/lib/immutable/list/find_index_spec.rb +35 -0
- data/spec/lib/immutable/list/find_spec.rb +42 -0
- data/spec/lib/immutable/list/flat_map_spec.rb +51 -0
- data/spec/lib/immutable/list/flatten_spec.rb +30 -0
- data/spec/lib/immutable/list/grep_spec.rb +46 -0
- data/spec/lib/immutable/list/group_by_spec.rb +41 -0
- data/spec/lib/immutable/list/hash_spec.rb +21 -0
- data/spec/lib/immutable/list/head_spec.rb +19 -0
- data/spec/lib/immutable/list/include_spec.rb +35 -0
- data/spec/lib/immutable/list/index_spec.rb +33 -0
- data/spec/lib/immutable/list/indices_spec.rb +61 -0
- data/spec/lib/immutable/list/init_spec.rb +28 -0
- data/spec/lib/immutable/list/inits_spec.rb +28 -0
- data/spec/lib/immutable/list/insert_spec.rb +46 -0
- data/spec/lib/immutable/list/inspect_spec.rb +29 -0
- data/spec/lib/immutable/list/intersperse_spec.rb +28 -0
- data/spec/lib/immutable/list/join_spec.rb +63 -0
- data/spec/lib/immutable/list/last_spec.rb +23 -0
- data/spec/lib/immutable/list/ltlt_spec.rb +19 -0
- data/spec/lib/immutable/list/map_spec.rb +45 -0
- data/spec/lib/immutable/list/maximum_spec.rb +39 -0
- data/spec/lib/immutable/list/merge_by_spec.rb +51 -0
- data/spec/lib/immutable/list/merge_spec.rb +59 -0
- data/spec/lib/immutable/list/minimum_spec.rb +39 -0
- data/spec/lib/immutable/list/multithreading_spec.rb +47 -0
- data/spec/lib/immutable/list/none_spec.rb +47 -0
- data/spec/lib/immutable/list/one_spec.rb +49 -0
- data/spec/lib/immutable/list/partition_spec.rb +115 -0
- data/spec/lib/immutable/list/permutation_spec.rb +55 -0
- data/spec/lib/immutable/list/pop_spec.rb +25 -0
- data/spec/lib/immutable/list/product_spec.rb +23 -0
- data/spec/lib/immutable/list/reduce_spec.rb +53 -0
- data/spec/lib/immutable/list/reject_spec.rb +45 -0
- data/spec/lib/immutable/list/reverse_spec.rb +34 -0
- data/spec/lib/immutable/list/rotate_spec.rb +36 -0
- data/spec/lib/immutable/list/sample_spec.rb +13 -0
- data/spec/lib/immutable/list/select_spec.rb +70 -0
- data/spec/lib/immutable/list/size_spec.rb +25 -0
- data/spec/lib/immutable/list/slice_spec.rb +229 -0
- data/spec/lib/immutable/list/sorting_spec.rb +46 -0
- data/spec/lib/immutable/list/span_spec.rb +76 -0
- data/spec/lib/immutable/list/split_at_spec.rb +43 -0
- data/spec/lib/immutable/list/subsequences_spec.rb +23 -0
- data/spec/lib/immutable/list/sum_spec.rb +23 -0
- data/spec/lib/immutable/list/tail_spec.rb +30 -0
- data/spec/lib/immutable/list/tails_spec.rb +28 -0
- data/spec/lib/immutable/list/take_spec.rb +30 -0
- data/spec/lib/immutable/list/take_while_spec.rb +46 -0
- data/spec/lib/immutable/list/to_a_spec.rb +39 -0
- data/spec/lib/immutable/list/to_ary_spec.rb +41 -0
- data/spec/lib/immutable/list/to_list_spec.rb +19 -0
- data/spec/lib/immutable/list/to_set_spec.rb +17 -0
- data/spec/lib/immutable/list/transpose_spec.rb +19 -0
- data/spec/lib/immutable/list/union_spec.rb +31 -0
- data/spec/lib/immutable/list/uniq_spec.rb +35 -0
- data/spec/lib/immutable/list/zip_spec.rb +23 -0
- data/spec/lib/immutable/nested/construction_spec.rb +95 -0
- data/spec/lib/immutable/set/add_spec.rb +75 -0
- data/spec/lib/immutable/set/all_spec.rb +51 -0
- data/spec/lib/immutable/set/any_spec.rb +51 -0
- data/spec/lib/immutable/set/clear_spec.rb +33 -0
- data/spec/lib/immutable/set/compact_spec.rb +30 -0
- data/spec/lib/immutable/set/construction_spec.rb +18 -0
- data/spec/lib/immutable/set/copying_spec.rb +13 -0
- data/spec/lib/immutable/set/count_spec.rb +36 -0
- data/spec/lib/immutable/set/delete_spec.rb +71 -0
- data/spec/lib/immutable/set/difference_spec.rb +49 -0
- data/spec/lib/immutable/set/disjoint_spec.rb +25 -0
- data/spec/lib/immutable/set/each_spec.rb +45 -0
- data/spec/lib/immutable/set/empty_spec.rb +44 -0
- data/spec/lib/immutable/set/eqeq_spec.rb +103 -0
- data/spec/lib/immutable/set/eql_spec.rb +109 -0
- data/spec/lib/immutable/set/exclusion_spec.rb +47 -0
- data/spec/lib/immutable/set/find_spec.rb +35 -0
- data/spec/lib/immutable/set/first_spec.rb +28 -0
- data/spec/lib/immutable/set/flatten_spec.rb +46 -0
- data/spec/lib/immutable/set/grep_spec.rb +57 -0
- data/spec/lib/immutable/set/group_by_spec.rb +59 -0
- data/spec/lib/immutable/set/hash_spec.rb +22 -0
- data/spec/lib/immutable/set/include_spec.rb +60 -0
- data/spec/lib/immutable/set/inspect_spec.rb +47 -0
- data/spec/lib/immutable/set/intersect_spec.rb +25 -0
- data/spec/lib/immutable/set/intersection_spec.rb +52 -0
- data/spec/lib/immutable/set/join_spec.rb +64 -0
- data/spec/lib/immutable/set/map_spec.rb +59 -0
- data/spec/lib/immutable/set/marshal_spec.rb +28 -0
- data/spec/lib/immutable/set/maximum_spec.rb +36 -0
- data/spec/lib/immutable/set/minimum_spec.rb +36 -0
- data/spec/lib/immutable/set/new_spec.rb +53 -0
- data/spec/lib/immutable/set/none_spec.rb +47 -0
- data/spec/lib/immutable/set/one_spec.rb +47 -0
- data/spec/lib/immutable/set/partition_spec.rb +52 -0
- data/spec/lib/immutable/set/product_spec.rb +23 -0
- data/spec/lib/immutable/set/reduce_spec.rb +55 -0
- data/spec/lib/immutable/set/reject_spec.rb +50 -0
- data/spec/lib/immutable/set/reverse_each_spec.rb +38 -0
- data/spec/lib/immutable/set/sample_spec.rb +13 -0
- data/spec/lib/immutable/set/select_spec.rb +73 -0
- data/spec/lib/immutable/set/size_spec.rb +17 -0
- data/spec/lib/immutable/set/sorting_spec.rb +59 -0
- data/spec/lib/immutable/set/subset_spec.rb +51 -0
- data/spec/lib/immutable/set/sum_spec.rb +23 -0
- data/spec/lib/immutable/set/superset_spec.rb +51 -0
- data/spec/lib/immutable/set/to_a_spec.rb +30 -0
- data/spec/lib/immutable/set/to_list_spec.rb +35 -0
- data/spec/lib/immutable/set/to_set_spec.rb +19 -0
- data/spec/lib/immutable/set/union_spec.rb +63 -0
- data/spec/lib/immutable/sorted_set/above_spec.rb +51 -0
- data/spec/lib/immutable/sorted_set/add_spec.rb +62 -0
- data/spec/lib/immutable/sorted_set/at_spec.rb +24 -0
- data/spec/lib/immutable/sorted_set/below_spec.rb +51 -0
- data/spec/lib/immutable/sorted_set/between_spec.rb +51 -0
- data/spec/lib/immutable/sorted_set/clear_spec.rb +43 -0
- data/spec/lib/immutable/sorted_set/copying_spec.rb +20 -0
- data/spec/lib/immutable/sorted_set/delete_at_spec.rb +18 -0
- data/spec/lib/immutable/sorted_set/delete_spec.rb +89 -0
- data/spec/lib/immutable/sorted_set/difference_spec.rb +22 -0
- data/spec/lib/immutable/sorted_set/disjoint_spec.rb +25 -0
- data/spec/lib/immutable/sorted_set/drop_spec.rb +55 -0
- data/spec/lib/immutable/sorted_set/drop_while_spec.rb +34 -0
- data/spec/lib/immutable/sorted_set/each_spec.rb +28 -0
- data/spec/lib/immutable/sorted_set/empty_spec.rb +34 -0
- data/spec/lib/immutable/sorted_set/eql_spec.rb +120 -0
- data/spec/lib/immutable/sorted_set/exclusion_spec.rb +22 -0
- data/spec/lib/immutable/sorted_set/fetch_spec.rb +64 -0
- data/spec/lib/immutable/sorted_set/find_index_spec.rb +40 -0
- data/spec/lib/immutable/sorted_set/first_spec.rb +18 -0
- data/spec/lib/immutable/sorted_set/from_spec.rb +51 -0
- data/spec/lib/immutable/sorted_set/group_by_spec.rb +57 -0
- data/spec/lib/immutable/sorted_set/include_spec.rb +23 -0
- data/spec/lib/immutable/sorted_set/inspect_spec.rb +37 -0
- data/spec/lib/immutable/sorted_set/intersect_spec.rb +25 -0
- data/spec/lib/immutable/sorted_set/intersection_spec.rb +28 -0
- data/spec/lib/immutable/sorted_set/last_spec.rb +36 -0
- data/spec/lib/immutable/sorted_set/map_spec.rb +43 -0
- data/spec/lib/immutable/sorted_set/marshal_spec.rb +36 -0
- data/spec/lib/immutable/sorted_set/maximum_spec.rb +36 -0
- data/spec/lib/immutable/sorted_set/minimum_spec.rb +19 -0
- data/spec/lib/immutable/sorted_set/new_spec.rb +71 -0
- data/spec/lib/immutable/sorted_set/reverse_each_spec.rb +28 -0
- data/spec/lib/immutable/sorted_set/sample_spec.rb +13 -0
- data/spec/lib/immutable/sorted_set/select_spec.rb +61 -0
- data/spec/lib/immutable/sorted_set/size_spec.rb +17 -0
- data/spec/lib/immutable/sorted_set/slice_spec.rb +256 -0
- data/spec/lib/immutable/sorted_set/sorting_spec.rb +44 -0
- data/spec/lib/immutable/sorted_set/subset_spec.rb +47 -0
- data/spec/lib/immutable/sorted_set/superset_spec.rb +47 -0
- data/spec/lib/immutable/sorted_set/take_spec.rb +54 -0
- data/spec/lib/immutable/sorted_set/take_while_spec.rb +33 -0
- data/spec/lib/immutable/sorted_set/to_set_spec.rb +17 -0
- data/spec/lib/immutable/sorted_set/union_spec.rb +27 -0
- data/spec/lib/immutable/sorted_set/up_to_spec.rb +52 -0
- data/spec/lib/immutable/sorted_set/values_at_spec.rb +33 -0
- data/spec/lib/immutable/vector/add_spec.rb +67 -0
- data/spec/lib/immutable/vector/any_spec.rb +69 -0
- data/spec/lib/immutable/vector/assoc_spec.rb +45 -0
- data/spec/lib/immutable/vector/bsearch_spec.rb +65 -0
- data/spec/lib/immutable/vector/clear_spec.rb +33 -0
- data/spec/lib/immutable/vector/combination_spec.rb +81 -0
- data/spec/lib/immutable/vector/compact_spec.rb +29 -0
- data/spec/lib/immutable/vector/compare_spec.rb +31 -0
- data/spec/lib/immutable/vector/concat_spec.rb +34 -0
- data/spec/lib/immutable/vector/copying_spec.rb +20 -0
- data/spec/lib/immutable/vector/count_spec.rb +17 -0
- data/spec/lib/immutable/vector/delete_at_spec.rb +53 -0
- data/spec/lib/immutable/vector/delete_spec.rb +30 -0
- data/spec/lib/immutable/vector/drop_spec.rb +41 -0
- data/spec/lib/immutable/vector/drop_while_spec.rb +54 -0
- data/spec/lib/immutable/vector/each_index_spec.rb +40 -0
- data/spec/lib/immutable/vector/each_spec.rb +44 -0
- data/spec/lib/immutable/vector/each_with_index_spec.rb +39 -0
- data/spec/lib/immutable/vector/empty_spec.rb +41 -0
- data/spec/lib/immutable/vector/eql_spec.rb +76 -0
- data/spec/lib/immutable/vector/fetch_spec.rb +64 -0
- data/spec/lib/immutable/vector/fill_spec.rb +88 -0
- data/spec/lib/immutable/vector/first_spec.rb +18 -0
- data/spec/lib/immutable/vector/flat_map_spec.rb +50 -0
- data/spec/lib/immutable/vector/flatten_spec.rb +58 -0
- data/spec/lib/immutable/vector/get_spec.rb +74 -0
- data/spec/lib/immutable/vector/group_by_spec.rb +57 -0
- data/spec/lib/immutable/vector/include_spec.rb +30 -0
- data/spec/lib/immutable/vector/insert_spec.rb +68 -0
- data/spec/lib/immutable/vector/inspect_spec.rb +49 -0
- data/spec/lib/immutable/vector/join_spec.rb +58 -0
- data/spec/lib/immutable/vector/last_spec.rb +45 -0
- data/spec/lib/immutable/vector/length_spec.rb +45 -0
- data/spec/lib/immutable/vector/ltlt_spec.rb +65 -0
- data/spec/lib/immutable/vector/map_spec.rb +51 -0
- data/spec/lib/immutable/vector/marshal_spec.rb +31 -0
- data/spec/lib/immutable/vector/maximum_spec.rb +33 -0
- data/spec/lib/immutable/vector/minimum_spec.rb +33 -0
- data/spec/lib/immutable/vector/multiply_spec.rb +47 -0
- data/spec/lib/immutable/vector/new_spec.rb +50 -0
- data/spec/lib/immutable/vector/partition_spec.rb +52 -0
- data/spec/lib/immutable/vector/permutation_spec.rb +91 -0
- data/spec/lib/immutable/vector/pop_spec.rb +26 -0
- data/spec/lib/immutable/vector/product_spec.rb +70 -0
- data/spec/lib/immutable/vector/reduce_spec.rb +55 -0
- data/spec/lib/immutable/vector/reject_spec.rb +43 -0
- data/spec/lib/immutable/vector/repeated_combination_spec.rb +77 -0
- data/spec/lib/immutable/vector/repeated_permutation_spec.rb +93 -0
- data/spec/lib/immutable/vector/reverse_each_spec.rb +31 -0
- data/spec/lib/immutable/vector/reverse_spec.rb +21 -0
- data/spec/lib/immutable/vector/rindex_spec.rb +36 -0
- data/spec/lib/immutable/vector/rotate_spec.rb +73 -0
- data/spec/lib/immutable/vector/sample_spec.rb +13 -0
- data/spec/lib/immutable/vector/select_spec.rb +63 -0
- data/spec/lib/immutable/vector/set_spec.rb +174 -0
- data/spec/lib/immutable/vector/shift_spec.rb +27 -0
- data/spec/lib/immutable/vector/shuffle_spec.rb +43 -0
- data/spec/lib/immutable/vector/slice_spec.rb +240 -0
- data/spec/lib/immutable/vector/sorting_spec.rb +56 -0
- data/spec/lib/immutable/vector/sum_spec.rb +17 -0
- data/spec/lib/immutable/vector/take_spec.rb +42 -0
- data/spec/lib/immutable/vector/take_while_spec.rb +34 -0
- data/spec/lib/immutable/vector/to_a_spec.rb +41 -0
- data/spec/lib/immutable/vector/to_ary_spec.rb +34 -0
- data/spec/lib/immutable/vector/to_list_spec.rb +30 -0
- data/spec/lib/immutable/vector/to_set_spec.rb +21 -0
- data/spec/lib/immutable/vector/transpose_spec.rb +48 -0
- data/spec/lib/immutable/vector/uniq_spec.rb +76 -0
- data/spec/lib/immutable/vector/unshift_spec.rb +28 -0
- data/spec/lib/immutable/vector/update_in_spec.rb +82 -0
- data/spec/lib/immutable/vector/values_at_spec.rb +33 -0
- data/spec/lib/immutable/vector/zip_spec.rb +57 -0
- data/spec/lib/load_spec.rb +42 -0
- data/spec/spec_helper.rb +92 -0
- metadata +830 -0
@@ -0,0 +1,1464 @@
|
|
1
|
+
require "immutable/enumerable"
|
2
|
+
|
3
|
+
module Immutable
|
4
|
+
|
5
|
+
# A `SortedSet` is a collection of ordered values with no duplicates. Unlike a
|
6
|
+
# {Vector}, in which items can appear in any arbitrary order, a `SortedSet` always
|
7
|
+
# keeps items either in their natural order, or in an order defined by a comparator
|
8
|
+
# block which is provided at initialization time.
|
9
|
+
#
|
10
|
+
# `SortedSet` uses `#<=>` (or its comparator block) to determine which items are
|
11
|
+
# equivalent. If the comparator indicates that an existing item and a new item are
|
12
|
+
# equal, any attempt to insert the new item will have no effect.
|
13
|
+
#
|
14
|
+
# This means that *all* the items inserted into any one `SortedSet` must all be
|
15
|
+
# comparable. For example, you cannot put `String`s and `Integer`s in the same
|
16
|
+
# `SortedSet`. This is unlike {Set}, which can store items of any type, as long
|
17
|
+
# as they all support `#hash` and `#eql?`.
|
18
|
+
#
|
19
|
+
# A `SortedSet` can be created in either of the following ways:
|
20
|
+
#
|
21
|
+
# Immutable::SortedSet.new([1, 2, 3]) # any Enumerable can be used to initialize
|
22
|
+
# Immutable::SortedSet['A', 'B', 'C', 'D']
|
23
|
+
#
|
24
|
+
# Or if you want to use a custom ordering:
|
25
|
+
#
|
26
|
+
# Immutable::SortedSet.new([1,2,3]) { |a, b| -a <=> -b }
|
27
|
+
# Immutable::SortedSet.new([1, 2, 3]) { |num| -num }
|
28
|
+
#
|
29
|
+
# `SortedSet` can use a 2-parameter block which returns 0, 1, or -1
|
30
|
+
# as a comparator (like `Array#sort`), *or* use a 1-parameter block to derive sort
|
31
|
+
# keys (like `Array#sort_by`) which will be compared using `#<=>`.
|
32
|
+
#
|
33
|
+
# Like all `immutable-ruby` collections, `SortedSet`s are immutable. Any operation
|
34
|
+
# which you might expect to "modify" a `SortedSet` will actually return a new
|
35
|
+
# collection and leave the existing one unchanged.
|
36
|
+
#
|
37
|
+
# `SortedSet` supports the same basic set-theoretic operations as {Set}, including
|
38
|
+
# {#union}, {#intersection}, {#difference}, and {#exclusion}, as well as {#subset?},
|
39
|
+
# {#superset?}, and so on. Unlike {Set}, it does not define comparison operators like
|
40
|
+
# `#>` or `#<` as aliases for the superset/subset predicates. Instead, these comparison
|
41
|
+
# operators do a item-by-item comparison between the `SortedSet` and another sequential
|
42
|
+
# collection. (See `Array#<=>` for details.)
|
43
|
+
#
|
44
|
+
# Additionally, since `SortedSet`s are ordered, they also support indexed retrieval
|
45
|
+
# of items using {#at} or {#[]}. Like {Vector},
|
46
|
+
# negative indices count back from the end of the `SortedSet`.
|
47
|
+
#
|
48
|
+
# Getting the {#max} or {#min} item from a `SortedSet`, as defined by its comparator,
|
49
|
+
# is a constant time operation.
|
50
|
+
#
|
51
|
+
class SortedSet
|
52
|
+
include Immutable::Enumerable
|
53
|
+
|
54
|
+
class << self
|
55
|
+
# Create a new `SortedSet` populated with the given items. This method does not
|
56
|
+
# accept a comparator block.
|
57
|
+
#
|
58
|
+
# @return [SortedSet]
|
59
|
+
def [](*items)
|
60
|
+
new(items)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return an empty `SortedSet`. If used on a subclass, returns an empty instance
|
64
|
+
# of that class.
|
65
|
+
#
|
66
|
+
# @return [SortedSet]
|
67
|
+
def empty
|
68
|
+
@empty ||= self.alloc(PlainAVLNode::EmptyNode)
|
69
|
+
end
|
70
|
+
|
71
|
+
# "Raw" allocation of a new `SortedSet`. Used internally to create a new
|
72
|
+
# instance quickly after obtaining a modified binary tree.
|
73
|
+
#
|
74
|
+
# @return [Set]
|
75
|
+
# @private
|
76
|
+
def alloc(node)
|
77
|
+
allocate.tap { |s| s.instance_variable_set(:@node, node) }.freeze
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def initialize(items=[], &block)
|
82
|
+
items = items.to_a
|
83
|
+
if block
|
84
|
+
if block.arity == 1 || block.arity == -1
|
85
|
+
comparator = lambda { |a,b| block.call(a) <=> block.call(b) }
|
86
|
+
items = items.sort_by(&block)
|
87
|
+
else
|
88
|
+
comparator = block
|
89
|
+
items = items.sort(&block)
|
90
|
+
end
|
91
|
+
@node = AVLNode.from_items(items, comparator)
|
92
|
+
else
|
93
|
+
@node = PlainAVLNode.from_items(items.sort)
|
94
|
+
end
|
95
|
+
freeze
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return `true` if this `SortedSet` contains no items.
|
99
|
+
#
|
100
|
+
# @return [Boolean]
|
101
|
+
def empty?
|
102
|
+
@node.empty?
|
103
|
+
end
|
104
|
+
|
105
|
+
# Return the number of items in this `SortedSet`.
|
106
|
+
#
|
107
|
+
# @example
|
108
|
+
# Immutable::SortedSet["A", "B", "C"].size # => 3
|
109
|
+
#
|
110
|
+
# @return [Integer]
|
111
|
+
def size
|
112
|
+
@node.size
|
113
|
+
end
|
114
|
+
alias :length :size
|
115
|
+
|
116
|
+
# Return a new `SortedSet` with `item` added. If `item` is already in the set,
|
117
|
+
# return `self`.
|
118
|
+
#
|
119
|
+
# @example
|
120
|
+
# Immutable::SortedSet["Dog", "Lion"].add("Elephant")
|
121
|
+
# # => Immutable::SortedSet["Dog", "Elephant", "Lion"]
|
122
|
+
#
|
123
|
+
# @param item [Object] The object to add
|
124
|
+
# @return [SortedSet]
|
125
|
+
def add(item)
|
126
|
+
catch :present do
|
127
|
+
node = @node.insert(item)
|
128
|
+
return self.class.alloc(node)
|
129
|
+
end
|
130
|
+
self
|
131
|
+
end
|
132
|
+
alias :<< :add
|
133
|
+
|
134
|
+
# If `item` is not a member of this `SortedSet`, return a new `SortedSet` with
|
135
|
+
# `item` added. Otherwise, return `false`.
|
136
|
+
#
|
137
|
+
# @example
|
138
|
+
# Immutable::SortedSet["Dog", "Lion"].add?("Elephant")
|
139
|
+
# # => Immutable::SortedSet["Dog", "Elephant", "Lion"]
|
140
|
+
# Immutable::SortedSet["Dog", "Lion"].add?("Lion")
|
141
|
+
# # => false
|
142
|
+
#
|
143
|
+
# @param item [Object] The object to add
|
144
|
+
# @return [SortedSet, false]
|
145
|
+
def add?(item)
|
146
|
+
!include?(item) && add(item)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Return a new `SortedSet` with `item` removed. If `item` is not a member of the set,
|
150
|
+
# return `self`.
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
# Immutable::SortedSet["A", "B", "C"].delete("B")
|
154
|
+
# # => Immutable::SortedSet["A", "C"]
|
155
|
+
#
|
156
|
+
# @param item [Object] The object to remove
|
157
|
+
# @return [SortedSet]
|
158
|
+
def delete(item)
|
159
|
+
catch :not_present do
|
160
|
+
node = @node.delete(item)
|
161
|
+
if node.empty? && node.natural_order?
|
162
|
+
return self.class.empty
|
163
|
+
else
|
164
|
+
return self.class.alloc(node)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
# If `item` is a member of this `SortedSet`, return a new `SortedSet` with
|
171
|
+
# `item` removed. Otherwise, return `false`.
|
172
|
+
#
|
173
|
+
# @example
|
174
|
+
# Immutable::SortedSet["A", "B", "C"].delete?("B")
|
175
|
+
# # => Immutable::SortedSet["A", "C"]
|
176
|
+
# Immutable::SortedSet["A", "B", "C"].delete?("Z")
|
177
|
+
# # => false
|
178
|
+
#
|
179
|
+
# @param item [Object] The object to remove
|
180
|
+
# @return [SortedSet, false]
|
181
|
+
def delete?(item)
|
182
|
+
include?(item) && delete(item)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Return a new `SortedSet` with the item at `index` removed. If the given `index`
|
186
|
+
# does not exist (if it is too high or too low), return `self`.
|
187
|
+
#
|
188
|
+
# @example
|
189
|
+
# Immutable::SortedSet["A", "B", "C", "D"].delete_at(2)
|
190
|
+
# # => Immutable::SortedSet["A", "B", "D"]
|
191
|
+
#
|
192
|
+
# @param index [Integer] The index to remove
|
193
|
+
# @return [SortedSet]
|
194
|
+
def delete_at(index)
|
195
|
+
(item = at(index)) ? delete(item) : self
|
196
|
+
end
|
197
|
+
|
198
|
+
# Retrieve the item at `index`. If there is none (either the provided index
|
199
|
+
# is too high or too low), return `nil`.
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# s = Immutable::SortedSet["A", "B", "C", "D", "E", "F"]
|
203
|
+
# s.at(2) # => "C"
|
204
|
+
# s.at(-2) # => "E"
|
205
|
+
# s.at(6) # => nil
|
206
|
+
#
|
207
|
+
# @param index [Integer] The index to retrieve
|
208
|
+
# @return [Object]
|
209
|
+
def at(index)
|
210
|
+
index += @node.size if index < 0
|
211
|
+
return nil if index >= @node.size || index < 0
|
212
|
+
@node.at(index)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Retrieve the value at `index` with optional default.
|
216
|
+
#
|
217
|
+
# @overload fetch(index)
|
218
|
+
# Retrieve the value at the given index, or raise an `IndexError` if not
|
219
|
+
# found.
|
220
|
+
#
|
221
|
+
# @param index [Integer] The index to look up
|
222
|
+
# @raise [IndexError] if index does not exist
|
223
|
+
# @example
|
224
|
+
# s = Immutable::SortedSet["A", "B", "C", "D"]
|
225
|
+
# s.fetch(2) # => "C"
|
226
|
+
# s.fetch(-1) # => "D"
|
227
|
+
# s.fetch(4) # => IndexError: index 4 outside of vector bounds
|
228
|
+
#
|
229
|
+
# @overload fetch(index) { |index| ... }
|
230
|
+
# Retrieve the value at the given index, or return the result of yielding
|
231
|
+
# the block if not found.
|
232
|
+
#
|
233
|
+
# @yield Once if the index is not found.
|
234
|
+
# @yieldparam [Integer] index The index which does not exist
|
235
|
+
# @yieldreturn [Object] Default value to return
|
236
|
+
# @param index [Integer] The index to look up
|
237
|
+
# @example
|
238
|
+
# s = Immutable::SortedSet["A", "B", "C", "D"]
|
239
|
+
# s.fetch(2) { |i| i * i } # => "C"
|
240
|
+
# s.fetch(4) { |i| i * i } # => 16
|
241
|
+
#
|
242
|
+
# @overload fetch(index, default)
|
243
|
+
# Retrieve the value at the given index, or return the provided `default`
|
244
|
+
# value if not found.
|
245
|
+
#
|
246
|
+
# @param index [Integer] The index to look up
|
247
|
+
# @param default [Object] Object to return if the key is not found
|
248
|
+
# @example
|
249
|
+
# s = Immutable::SortedSet["A", "B", "C", "D"]
|
250
|
+
# s.fetch(2, "Z") # => "C"
|
251
|
+
# s.fetch(4, "Z") # => "Z"
|
252
|
+
#
|
253
|
+
# @return [Object]
|
254
|
+
def fetch(index, default = (missing_default = true))
|
255
|
+
if index >= -@node.size && index < @node.size
|
256
|
+
at(index)
|
257
|
+
elsif block_given?
|
258
|
+
yield(index)
|
259
|
+
elsif !missing_default
|
260
|
+
default
|
261
|
+
else
|
262
|
+
raise IndexError, "index #{index} outside of sorted set bounds"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Return specific objects from the `Vector`. All overloads return `nil` if
|
267
|
+
# the starting index is out of range.
|
268
|
+
#
|
269
|
+
# @overload set.slice(index)
|
270
|
+
# Returns a single object at the given `index`. If `index` is negative,
|
271
|
+
# count backwards from the end.
|
272
|
+
#
|
273
|
+
# @param index [Integer] The index to retrieve. May be negative.
|
274
|
+
# @return [Object]
|
275
|
+
# @example
|
276
|
+
# s = Immutable::SortedSet["A", "B", "C", "D", "E", "F"]
|
277
|
+
# s[2] # => "C"
|
278
|
+
# s[-1] # => "F"
|
279
|
+
# s[6] # => nil
|
280
|
+
#
|
281
|
+
# @overload set.slice(index, length)
|
282
|
+
# Return a subset starting at `index` and continuing for `length`
|
283
|
+
# elements or until the end of the `SortedSet`, whichever occurs first.
|
284
|
+
#
|
285
|
+
# @param start [Integer] The index to start retrieving items from. May be
|
286
|
+
# negative.
|
287
|
+
# @param length [Integer] The number of items to retrieve.
|
288
|
+
# @return [SortedSet]
|
289
|
+
# @example
|
290
|
+
# s = Immutable::SortedSet["A", "B", "C", "D", "E", "F"]
|
291
|
+
# s[2, 3] # => Immutable::SortedSet["C", "D", "E"]
|
292
|
+
# s[-2, 3] # => Immutable::SortedSet["E", "F"]
|
293
|
+
# s[20, 1] # => nil
|
294
|
+
#
|
295
|
+
# @overload set.slice(index..end)
|
296
|
+
# Return a subset starting at `index` and continuing to index
|
297
|
+
# `end` or the end of the `SortedSet`, whichever occurs first.
|
298
|
+
#
|
299
|
+
# @param range [Range] The range of indices to retrieve.
|
300
|
+
# @return [SortedSet]
|
301
|
+
# @example
|
302
|
+
# s = Immutable::SortedSet["A", "B", "C", "D", "E", "F"]
|
303
|
+
# s[2..3] # => Immutable::SortedSet["C", "D"]
|
304
|
+
# s[-2..100] # => Immutable::SortedSet["E", "F"]
|
305
|
+
# s[20..21] # => nil
|
306
|
+
def slice(arg, length = (missing_length = true))
|
307
|
+
if missing_length
|
308
|
+
if arg.is_a?(Range)
|
309
|
+
from, to = arg.begin, arg.end
|
310
|
+
from += @node.size if from < 0
|
311
|
+
to += @node.size if to < 0
|
312
|
+
to += 1 if !arg.exclude_end?
|
313
|
+
length = to - from
|
314
|
+
length = 0 if length < 0
|
315
|
+
subsequence(from, length)
|
316
|
+
else
|
317
|
+
at(arg)
|
318
|
+
end
|
319
|
+
else
|
320
|
+
arg += @node.size if arg < 0
|
321
|
+
subsequence(arg, length)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
alias :[] :slice
|
325
|
+
|
326
|
+
# Return a new `SortedSet` with only the elements at the given `indices`.
|
327
|
+
# If any of the `indices` do not exist, they will be skipped.
|
328
|
+
#
|
329
|
+
# @example
|
330
|
+
# s = Immutable::SortedSet["A", "B", "C", "D", "E", "F"]
|
331
|
+
# s.values_at(2, 4, 5) # => Immutable::SortedSet["C", "E", "F"]
|
332
|
+
#
|
333
|
+
# @param indices [Array] The indices to retrieve and gather into a new `SortedSet`
|
334
|
+
# @return [SortedSet]
|
335
|
+
def values_at(*indices)
|
336
|
+
indices.select! { |i| i >= -@node.size && i < @node.size }
|
337
|
+
self.class.new(indices.map! { |i| at(i) })
|
338
|
+
end
|
339
|
+
|
340
|
+
# Call the given block once for each item in the set, passing each
|
341
|
+
# item from first to last successively to the block. If no block is
|
342
|
+
# provided, returns an `Enumerator`.
|
343
|
+
#
|
344
|
+
# @example
|
345
|
+
# Immutable::SortedSet["A", "B", "C"].each { |e| puts "Element: #{e}" }
|
346
|
+
#
|
347
|
+
# Element: A
|
348
|
+
# Element: B
|
349
|
+
# Element: C
|
350
|
+
# # => Immutable::SortedSet["A", "B", "C"]
|
351
|
+
#
|
352
|
+
# @yield [item]
|
353
|
+
# @return [self, Enumerator]
|
354
|
+
def each(&block)
|
355
|
+
return @node.to_enum if not block_given?
|
356
|
+
@node.each(&block)
|
357
|
+
self
|
358
|
+
end
|
359
|
+
|
360
|
+
# Call the given block once for each item in the set, passing each
|
361
|
+
# item starting from the last, and counting back to the first, successively to
|
362
|
+
# the block.
|
363
|
+
#
|
364
|
+
# @example
|
365
|
+
# Immutable::SortedSet["A", "B", "C"].reverse_each { |e| puts "Element: #{e}" }
|
366
|
+
#
|
367
|
+
# Element: C
|
368
|
+
# Element: B
|
369
|
+
# Element: A
|
370
|
+
# # => Immutable::SortedSet["A", "B", "C"]
|
371
|
+
#
|
372
|
+
# @return [self]
|
373
|
+
def reverse_each(&block)
|
374
|
+
return @node.enum_for(:reverse_each) if not block_given?
|
375
|
+
@node.reverse_each(&block)
|
376
|
+
self
|
377
|
+
end
|
378
|
+
|
379
|
+
# Return the "lowest" element in this set, as determined by its sort order.
|
380
|
+
# Or, if a block is provided, use the block as a comparator to find the
|
381
|
+
# "lowest" element. (See `Enumerable#min`.)
|
382
|
+
#
|
383
|
+
# @example
|
384
|
+
# Immutable::SortedSet["A", "B", "C"].min # => "A"
|
385
|
+
#
|
386
|
+
# @return [Object]
|
387
|
+
# @yield [a, b] Any number of times with different pairs of elements.
|
388
|
+
def min
|
389
|
+
block_given? ? super : @node.min
|
390
|
+
end
|
391
|
+
|
392
|
+
# Return the "lowest" element in this set, as determined by its sort order.
|
393
|
+
# @return [Object]
|
394
|
+
def first
|
395
|
+
@node.min
|
396
|
+
end
|
397
|
+
|
398
|
+
# Return the "highest" element in this set, as determined by its sort order.
|
399
|
+
# Or, if a block is provided, use the block as a comparator to find the
|
400
|
+
# "highest" element. (See `Enumerable#max`.)
|
401
|
+
#
|
402
|
+
# @example
|
403
|
+
# Immutable::SortedSet["A", "B", "C"].max # => "C"
|
404
|
+
#
|
405
|
+
# @yield [a, b] Any number of times with different pairs of elements.
|
406
|
+
# @return [Object]
|
407
|
+
def max
|
408
|
+
block_given? ? super : @node.max
|
409
|
+
end
|
410
|
+
|
411
|
+
# Return the "highest" element in this set, as determined by its sort order.
|
412
|
+
# @return [Object]
|
413
|
+
def last
|
414
|
+
@node.max
|
415
|
+
end
|
416
|
+
|
417
|
+
# Return a new `SortedSet` containing all elements for which the given block returns
|
418
|
+
# true.
|
419
|
+
#
|
420
|
+
# @example
|
421
|
+
# Immutable::SortedSet["Bird", "Cow", "Elephant"].select { |e| e.size >= 4 }
|
422
|
+
# # => Immutable::SortedSet["Bird", "Elephant"]
|
423
|
+
#
|
424
|
+
# @return [SortedSet]
|
425
|
+
# @yield [item] Once for each item.
|
426
|
+
def select
|
427
|
+
return enum_for(:select) unless block_given?
|
428
|
+
items_to_delete = []
|
429
|
+
each { |item| items_to_delete << item unless yield(item) }
|
430
|
+
derive_new_sorted_set(@node.bulk_delete(items_to_delete))
|
431
|
+
end
|
432
|
+
alias :find_all :select
|
433
|
+
alias :keep_if :select
|
434
|
+
|
435
|
+
# Invoke the given block once for each item in the set, and return a new
|
436
|
+
# `SortedSet` containing the values returned by the block. If no block is
|
437
|
+
# given, returns an `Enumerator`.
|
438
|
+
#
|
439
|
+
# @example
|
440
|
+
# Immutable::SortedSet[1, 2, 3].map { |e| -(e * e) }
|
441
|
+
# # => Immutable::SortedSet[-9, -4, -1]
|
442
|
+
#
|
443
|
+
# @return [SortedSet, Enumerator]
|
444
|
+
# @yield [item] Once for each item.
|
445
|
+
def map
|
446
|
+
return enum_for(:map) if not block_given?
|
447
|
+
return self if empty?
|
448
|
+
self.class.alloc(@node.from_items(super))
|
449
|
+
end
|
450
|
+
alias :collect :map
|
451
|
+
|
452
|
+
# Return `true` if the given item is present in this `SortedSet`. More precisely,
|
453
|
+
# return `true` if an object which compares as "equal" using this set's
|
454
|
+
# comparator is present.
|
455
|
+
#
|
456
|
+
# @example
|
457
|
+
# Immutable::SortedSet["A", "B", "C"].include?("B") # => true
|
458
|
+
#
|
459
|
+
# @param item [Object] The object to check for
|
460
|
+
# @return [Boolean]
|
461
|
+
def include?(item)
|
462
|
+
@node.include?(item)
|
463
|
+
end
|
464
|
+
alias :member? :include?
|
465
|
+
|
466
|
+
# Return a new `SortedSet` with the same items, but a sort order determined
|
467
|
+
# by the given block.
|
468
|
+
#
|
469
|
+
# @example
|
470
|
+
# Immutable::SortedSet["Bird", "Cow", "Elephant"].sort { |a, b| a.size <=> b.size }
|
471
|
+
# # => Immutable::SortedSet["Cow", "Bird", "Elephant"]
|
472
|
+
# Immutable::SortedSet["Bird", "Cow", "Elephant"].sort_by { |e| e.size }
|
473
|
+
# # => Immutable::SortedSet["Cow", "Bird", "Elephant"]
|
474
|
+
#
|
475
|
+
# @return [SortedSet]
|
476
|
+
def sort(&block)
|
477
|
+
if block
|
478
|
+
self.class.new(self.to_a, &block)
|
479
|
+
else
|
480
|
+
self.class.new(self.to_a.sort)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
alias :sort_by :sort
|
484
|
+
|
485
|
+
# Find the index of a given object or an element that satisfies the given
|
486
|
+
# block.
|
487
|
+
#
|
488
|
+
# @overload find_index(obj)
|
489
|
+
# Return the index of the first object in this set which is equal to
|
490
|
+
# `obj`. Rather than using `#==`, we use `#<=>` (or our comparator block)
|
491
|
+
# for comparisons. This means we can find the index in `O(log N)` time,
|
492
|
+
# rather than `O(N)`.
|
493
|
+
# @param obj [Object] The object to search for
|
494
|
+
# @example
|
495
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
496
|
+
# s.find_index(8) # => 3
|
497
|
+
# @overload find_index
|
498
|
+
# Return the index of the first object in this sorted set for which the
|
499
|
+
# block returns to true. This takes `O(N)` time.
|
500
|
+
# @yield [element] An element in the sorted set
|
501
|
+
# @yieldreturn [Boolean] True if this is element matches
|
502
|
+
# @example
|
503
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
504
|
+
# s.find_index { |e| e > 7 } # => 3
|
505
|
+
#
|
506
|
+
# @return [Integer] The index of the object, or `nil` if not found.
|
507
|
+
def find_index(obj = (missing_obj = true), &block)
|
508
|
+
if !missing_obj
|
509
|
+
# Enumerable provides a default implementation, but this is more efficient
|
510
|
+
node = @node
|
511
|
+
index = node.left.size
|
512
|
+
while !node.empty?
|
513
|
+
direction = node.direction(obj)
|
514
|
+
if direction > 0
|
515
|
+
node = node.right
|
516
|
+
index += (node.left.size + 1)
|
517
|
+
elsif direction < 0
|
518
|
+
node = node.left
|
519
|
+
index -= (node.right.size + 1)
|
520
|
+
else
|
521
|
+
return index
|
522
|
+
end
|
523
|
+
end
|
524
|
+
nil
|
525
|
+
else
|
526
|
+
super(&block)
|
527
|
+
end
|
528
|
+
end
|
529
|
+
alias :index :find_index
|
530
|
+
|
531
|
+
# Drop the first `n` elements and return the rest in a new `SortedSet`.
|
532
|
+
#
|
533
|
+
# @example
|
534
|
+
# Immutable::SortedSet["A", "B", "C", "D", "E", "F"].drop(2)
|
535
|
+
# # => Immutable::SortedSet["C", "D", "E", "F"]
|
536
|
+
#
|
537
|
+
# @param n [Integer] The number of elements to remove
|
538
|
+
# @return [SortedSet]
|
539
|
+
def drop(n)
|
540
|
+
derive_new_sorted_set(@node.drop(n))
|
541
|
+
end
|
542
|
+
|
543
|
+
# Return only the first `n` elements in a new `SortedSet`.
|
544
|
+
#
|
545
|
+
# @example
|
546
|
+
# Immutable::SortedSet["A", "B", "C", "D", "E", "F"].take(4)
|
547
|
+
# # => Immutable::SortedSet["A", "B", "C", "D"]
|
548
|
+
#
|
549
|
+
# @param n [Integer] The number of elements to retain
|
550
|
+
# @return [SortedSet]
|
551
|
+
def take(n)
|
552
|
+
derive_new_sorted_set(@node.take(n))
|
553
|
+
end
|
554
|
+
|
555
|
+
# Drop elements up to, but not including, the first element for which the
|
556
|
+
# block returns `nil` or `false`. Gather the remaining elements into a new
|
557
|
+
# `SortedSet`. If no block is given, an `Enumerator` is returned instead.
|
558
|
+
#
|
559
|
+
# @example
|
560
|
+
# Immutable::SortedSet[2, 4, 6, 7, 8, 9].drop_while { |e| e.even? }
|
561
|
+
# # => Immutable::SortedSet[7, 8, 9]
|
562
|
+
#
|
563
|
+
# @yield [item]
|
564
|
+
# @return [SortedSet, Enumerator]
|
565
|
+
def drop_while
|
566
|
+
return enum_for(:drop_while) if not block_given?
|
567
|
+
n = 0
|
568
|
+
each do |item|
|
569
|
+
break unless yield item
|
570
|
+
n += 1
|
571
|
+
end
|
572
|
+
drop(n)
|
573
|
+
end
|
574
|
+
|
575
|
+
# Gather elements up to, but not including, the first element for which the
|
576
|
+
# block returns `nil` or `false`, and return them in a new `SortedSet`. If no block
|
577
|
+
# is given, an `Enumerator` is returned instead.
|
578
|
+
#
|
579
|
+
# @example
|
580
|
+
# Immutable::SortedSet[2, 4, 6, 7, 8, 9].take_while { |e| e.even? }
|
581
|
+
# # => Immutable::SortedSet[2, 4, 6]
|
582
|
+
#
|
583
|
+
# @return [SortedSet, Enumerator]
|
584
|
+
# @yield [item]
|
585
|
+
def take_while
|
586
|
+
return enum_for(:take_while) if not block_given?
|
587
|
+
n = 0
|
588
|
+
each do |item|
|
589
|
+
break unless yield item
|
590
|
+
n += 1
|
591
|
+
end
|
592
|
+
take(n)
|
593
|
+
end
|
594
|
+
|
595
|
+
# Return a new `SortedSet` which contains all the members of both this set and `other`.
|
596
|
+
# `other` can be any `Enumerable` object.
|
597
|
+
#
|
598
|
+
# @example
|
599
|
+
# Immutable::SortedSet[1, 2] | Immutable::SortedSet[2, 3]
|
600
|
+
# # => Immutable::SortedSet[1, 2, 3]
|
601
|
+
#
|
602
|
+
# @param other [Enumerable] The collection to merge with
|
603
|
+
# @return [SortedSet]
|
604
|
+
def union(other)
|
605
|
+
self.class.alloc(@node.bulk_insert(other))
|
606
|
+
end
|
607
|
+
alias :| :union
|
608
|
+
alias :+ :union
|
609
|
+
alias :merge :union
|
610
|
+
|
611
|
+
# Return a new `SortedSet` which contains all the items which are members of both
|
612
|
+
# this set and `other`. `other` can be any `Enumerable` object.
|
613
|
+
#
|
614
|
+
# @example
|
615
|
+
# Immutable::SortedSet[1, 2] & Immutable::SortedSet[2, 3]
|
616
|
+
# # => Immutable::SortedSet[2]
|
617
|
+
#
|
618
|
+
# @param other [Enumerable] The collection to intersect with
|
619
|
+
# @return [SortedSet]
|
620
|
+
def intersection(other)
|
621
|
+
self.class.alloc(@node.keep_only(other))
|
622
|
+
end
|
623
|
+
alias :& :intersection
|
624
|
+
|
625
|
+
# Return a new `SortedSet` with all the items in `other` removed. `other` can be
|
626
|
+
# any `Enumerable` object.
|
627
|
+
#
|
628
|
+
# @example
|
629
|
+
# Immutable::SortedSet[1, 2] - Immutable::SortedSet[2, 3]
|
630
|
+
# # => Immutable::SortedSet[1]
|
631
|
+
#
|
632
|
+
# @param other [Enumerable] The collection to subtract from this set
|
633
|
+
# @return [SortedSet]
|
634
|
+
def difference(other)
|
635
|
+
self.class.alloc(@node.bulk_delete(other))
|
636
|
+
end
|
637
|
+
alias :subtract :difference
|
638
|
+
alias :- :difference
|
639
|
+
|
640
|
+
# Return a new `SortedSet` with all the items which are members of this
|
641
|
+
# set or of `other`, but not both. `other` can be any `Enumerable` object.
|
642
|
+
#
|
643
|
+
# @example
|
644
|
+
# Immutable::SortedSet[1, 2] ^ Immutable::SortedSet[2, 3]
|
645
|
+
# # => Immutable::SortedSet[1, 3]
|
646
|
+
#
|
647
|
+
# @param other [Enumerable] The collection to take the exclusive disjunction of
|
648
|
+
# @return [SortedSet]
|
649
|
+
def exclusion(other)
|
650
|
+
((self | other) - (self & other))
|
651
|
+
end
|
652
|
+
alias :^ :exclusion
|
653
|
+
|
654
|
+
# Return `true` if all items in this set are also in `other`.
|
655
|
+
#
|
656
|
+
# @example
|
657
|
+
# Immutable::SortedSet[2, 3].subset?(Immutable::SortedSet[1, 2, 3]) # => true
|
658
|
+
#
|
659
|
+
# @param other [Enumerable]
|
660
|
+
# @return [Boolean]
|
661
|
+
def subset?(other)
|
662
|
+
return false if other.size < size
|
663
|
+
all? { |item| other.include?(item) }
|
664
|
+
end
|
665
|
+
|
666
|
+
# Return `true` if all items in `other` are also in this set.
|
667
|
+
#
|
668
|
+
# @example
|
669
|
+
# Immutable::SortedSet[1, 2, 3].superset?(Immutable::SortedSet[2, 3]) # => true
|
670
|
+
#
|
671
|
+
# @param other [Enumerable]
|
672
|
+
# @return [Boolean]
|
673
|
+
def superset?(other)
|
674
|
+
other.subset?(self)
|
675
|
+
end
|
676
|
+
|
677
|
+
# Returns `true` if `other` contains all the items in this set, plus at least
|
678
|
+
# one item which is not in this set.
|
679
|
+
#
|
680
|
+
# @example
|
681
|
+
# Immutable::SortedSet[2, 3].proper_subset?(Immutable::SortedSet[1, 2, 3]) # => true
|
682
|
+
# Immutable::SortedSet[1, 2, 3].proper_subset?(Immutable::SortedSet[1, 2, 3]) # => false
|
683
|
+
#
|
684
|
+
# @param other [Enumerable]
|
685
|
+
# @return [Boolean]
|
686
|
+
def proper_subset?(other)
|
687
|
+
return false if other.size <= size
|
688
|
+
all? { |item| other.include?(item) }
|
689
|
+
end
|
690
|
+
|
691
|
+
# Returns `true` if this set contains all the items in `other`, plus at least
|
692
|
+
# one item which is not in `other`.
|
693
|
+
#
|
694
|
+
# @example
|
695
|
+
# Immutable::SortedSet[1, 2, 3].proper_superset?(Immutable::SortedSet[2, 3]) # => true
|
696
|
+
# Immutable::SortedSet[1, 2, 3].proper_superset?(Immutable::SortedSet[1, 2, 3]) # => false
|
697
|
+
#
|
698
|
+
# @param other [Enumerable]
|
699
|
+
# @return [Boolean]
|
700
|
+
def proper_superset?(other)
|
701
|
+
other.proper_subset?(self)
|
702
|
+
end
|
703
|
+
|
704
|
+
# Return `true` if this set and `other` do not share any items.
|
705
|
+
#
|
706
|
+
# @example
|
707
|
+
# Immutable::SortedSet[1, 2].disjoint?(Immutable::SortedSet[3, 4]) # => true
|
708
|
+
#
|
709
|
+
# @param other [Enumerable]
|
710
|
+
# @return [Boolean]
|
711
|
+
def disjoint?(other)
|
712
|
+
if size < other.size
|
713
|
+
each { |item| return false if other.include?(item) }
|
714
|
+
else
|
715
|
+
other.each { |item| return false if include?(item) }
|
716
|
+
end
|
717
|
+
true
|
718
|
+
end
|
719
|
+
|
720
|
+
# Return `true` if this set and `other` have at least one item in common.
|
721
|
+
#
|
722
|
+
# @example
|
723
|
+
# Immutable::SortedSet[1, 2].intersect?(Immutable::SortedSet[2, 3]) # => true
|
724
|
+
#
|
725
|
+
# @param other [Enumerable]
|
726
|
+
# @return [Boolean]
|
727
|
+
def intersect?(other)
|
728
|
+
!disjoint?(other)
|
729
|
+
end
|
730
|
+
|
731
|
+
alias :group :group_by
|
732
|
+
alias :classify :group_by
|
733
|
+
|
734
|
+
# Select elements greater than a value.
|
735
|
+
#
|
736
|
+
# @overload above(item)
|
737
|
+
# Return a new `SortedSet` containing all items greater than `item`.
|
738
|
+
# @return [SortedSet]
|
739
|
+
# @example
|
740
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
741
|
+
# s.above(6)
|
742
|
+
# # => Immutable::SortedSet[8, 10]
|
743
|
+
#
|
744
|
+
# @overload above(item)
|
745
|
+
# @yield [item] Once for each item greater than `item`, in order from
|
746
|
+
# lowest to highest.
|
747
|
+
# @return [nil]
|
748
|
+
# @example
|
749
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
750
|
+
# s.above(6) { |e| puts "Element: #{e}" }
|
751
|
+
#
|
752
|
+
# Element: 8
|
753
|
+
# Element: 10
|
754
|
+
# # => nil
|
755
|
+
#
|
756
|
+
# @param item [Object]
|
757
|
+
def above(item, &block)
|
758
|
+
if block_given?
|
759
|
+
@node.each_greater(item, false, &block)
|
760
|
+
else
|
761
|
+
self.class.alloc(@node.suffix(item, false))
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
# Select elements less than a value.
|
766
|
+
#
|
767
|
+
# @overload below(item)
|
768
|
+
# Return a new `SortedSet` containing all items less than `item`.
|
769
|
+
# @return [SortedSet]
|
770
|
+
# @example
|
771
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
772
|
+
# s.below(6)
|
773
|
+
# # => Immutable::SortedSet[2, 4]
|
774
|
+
#
|
775
|
+
# @overload below(item)
|
776
|
+
# @yield [item] Once for each item less than `item`, in order from lowest
|
777
|
+
# to highest.
|
778
|
+
# @return [nil]
|
779
|
+
# @example
|
780
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
781
|
+
# s.below(6) { |e| puts "Element: #{e}" }
|
782
|
+
#
|
783
|
+
# Element: 2
|
784
|
+
# Element: 4
|
785
|
+
# # => nil
|
786
|
+
#
|
787
|
+
# @param item [Object]
|
788
|
+
def below(item, &block)
|
789
|
+
if block_given?
|
790
|
+
@node.each_less(item, false, &block)
|
791
|
+
else
|
792
|
+
self.class.alloc(@node.prefix(item, false))
|
793
|
+
end
|
794
|
+
end
|
795
|
+
|
796
|
+
# Select elements greater than or equal to a value.
|
797
|
+
#
|
798
|
+
# @overload from(item)
|
799
|
+
# Return a new `SortedSet` containing all items greater than or equal `item`.
|
800
|
+
# @return [SortedSet]
|
801
|
+
# @example
|
802
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
803
|
+
# s.from(6)
|
804
|
+
# # => Immutable::SortedSet[6, 8, 10]
|
805
|
+
#
|
806
|
+
# @overload from(item)
|
807
|
+
# @yield [item] Once for each item greater than or equal to `item`, in
|
808
|
+
# order from lowest to highest.
|
809
|
+
# @return [nil]
|
810
|
+
# @example
|
811
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
812
|
+
# s.from(6) { |e| puts "Element: #{e}" }
|
813
|
+
#
|
814
|
+
# Element: 6
|
815
|
+
# Element: 8
|
816
|
+
# Element: 10
|
817
|
+
# # => nil
|
818
|
+
#
|
819
|
+
# @param item [Object]
|
820
|
+
def from(item, &block)
|
821
|
+
if block_given?
|
822
|
+
@node.each_greater(item, true, &block)
|
823
|
+
else
|
824
|
+
self.class.alloc(@node.suffix(item, true))
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
# Select elements less than or equal to a value.
|
829
|
+
#
|
830
|
+
# @overload up_to(item)
|
831
|
+
# Return a new `SortedSet` containing all items less than or equal to
|
832
|
+
# `item`.
|
833
|
+
#
|
834
|
+
# @return [SortedSet]
|
835
|
+
# @example
|
836
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
837
|
+
# s.upto(6)
|
838
|
+
# # => Immutable::SortedSet[2, 4, 6]
|
839
|
+
#
|
840
|
+
# @overload up_to(item)
|
841
|
+
# @yield [item] Once for each item less than or equal to `item`, in order
|
842
|
+
# from lowest to highest.
|
843
|
+
# @return [nil]
|
844
|
+
# @example
|
845
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
846
|
+
# s.up_to(6) { |e| puts "Element: #{e}" }
|
847
|
+
#
|
848
|
+
# Element: 2
|
849
|
+
# Element: 4
|
850
|
+
# Element: 6
|
851
|
+
# # => nil
|
852
|
+
#
|
853
|
+
# @param item [Object]
|
854
|
+
def up_to(item, &block)
|
855
|
+
if block_given?
|
856
|
+
@node.each_less(item, true, &block)
|
857
|
+
else
|
858
|
+
self.class.alloc(@node.prefix(item, true))
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
# Select elements between two values.
|
863
|
+
#
|
864
|
+
# @overload between(from, to)
|
865
|
+
# Return a new `SortedSet` containing all items less than or equal to
|
866
|
+
# `to` and greater than or equal to `from`.
|
867
|
+
#
|
868
|
+
# @return [SortedSet]
|
869
|
+
# @example
|
870
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
871
|
+
# s.between(5, 8)
|
872
|
+
# # => Immutable::SortedSet[6, 8]
|
873
|
+
#
|
874
|
+
# @overload between(item)
|
875
|
+
# @yield [item] Once for each item less than or equal to `to` and greater
|
876
|
+
# than or equal to `from`, in order from lowest to highest.
|
877
|
+
# @return [nil]
|
878
|
+
# @example
|
879
|
+
# s = Immutable::SortedSet[2, 4, 6, 8, 10]
|
880
|
+
# s.between(5, 8) { |e| puts "Element: #{e}" }
|
881
|
+
#
|
882
|
+
# Element: 6
|
883
|
+
# Element: 8
|
884
|
+
# # => nil
|
885
|
+
#
|
886
|
+
# @param from [Object]
|
887
|
+
# @param to [Object]
|
888
|
+
def between(from, to, &block)
|
889
|
+
if block_given?
|
890
|
+
@node.each_between(from, to, &block)
|
891
|
+
else
|
892
|
+
self.class.alloc(@node.between(from, to))
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
# Return a randomly chosen item from this set. If the set is empty, return `nil`.
|
897
|
+
#
|
898
|
+
# @example
|
899
|
+
# Immutable::SortedSet[1, 2, 3, 4, 5].sample # => 2
|
900
|
+
#
|
901
|
+
# @return [Object]
|
902
|
+
def sample
|
903
|
+
@node.at(rand(@node.size))
|
904
|
+
end
|
905
|
+
|
906
|
+
# Return an empty `SortedSet` instance, of the same class as this one. Useful if you
|
907
|
+
# have multiple subclasses of `SortedSet` and want to treat them polymorphically.
|
908
|
+
#
|
909
|
+
# @return [SortedSet]
|
910
|
+
def clear
|
911
|
+
if @node.natural_order?
|
912
|
+
self.class.empty
|
913
|
+
else
|
914
|
+
self.class.alloc(@node.clear)
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
# Return true if `other` has the same type and contents as this `SortedSet`.
|
919
|
+
#
|
920
|
+
# @param other [Object] The object to compare with
|
921
|
+
# @return [Boolean]
|
922
|
+
def eql?(other)
|
923
|
+
return true if other.equal?(self)
|
924
|
+
return false if not instance_of?(other.class)
|
925
|
+
return false if size != other.size
|
926
|
+
a, b = self.to_enum, other.to_enum
|
927
|
+
while true
|
928
|
+
return false if !a.next.eql?(b.next)
|
929
|
+
end
|
930
|
+
rescue StopIteration
|
931
|
+
true
|
932
|
+
end
|
933
|
+
|
934
|
+
# See `Object#hash`.
|
935
|
+
# @return [Integer]
|
936
|
+
def hash
|
937
|
+
reduce(0) { |hash, item| (hash << 5) - hash + item.hash }
|
938
|
+
end
|
939
|
+
|
940
|
+
# Return `self`. Since this is an immutable object duplicates are
|
941
|
+
# equivalent.
|
942
|
+
# @return [SortedSet]
|
943
|
+
def dup
|
944
|
+
self
|
945
|
+
end
|
946
|
+
alias :clone :dup
|
947
|
+
|
948
|
+
# @return [::Array]
|
949
|
+
# @private
|
950
|
+
def marshal_dump
|
951
|
+
if @node.natural_order?
|
952
|
+
to_a
|
953
|
+
else
|
954
|
+
raise TypeError, "can't dump SortedSet with custom sort order"
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
# @private
|
959
|
+
def marshal_load(array)
|
960
|
+
initialize(array)
|
961
|
+
end
|
962
|
+
|
963
|
+
private
|
964
|
+
|
965
|
+
def subsequence(from, length)
|
966
|
+
return nil if from > @node.size || from < 0 || length < 0
|
967
|
+
length = @node.size - from if @node.size < from + length
|
968
|
+
if length == 0
|
969
|
+
if @node.natural_order?
|
970
|
+
return self.class.empty
|
971
|
+
else
|
972
|
+
return self.class.alloc(@node.clear)
|
973
|
+
end
|
974
|
+
end
|
975
|
+
self.class.alloc(@node.slice(from, length))
|
976
|
+
end
|
977
|
+
|
978
|
+
# Return a new `SortedSet` which is derived from this one, using a modified
|
979
|
+
# {AVLNode}. The new `SortedSet` will retain the existing comparator, if
|
980
|
+
# there is one.
|
981
|
+
def derive_new_sorted_set(node)
|
982
|
+
if node.equal?(@node)
|
983
|
+
self
|
984
|
+
elsif node.empty?
|
985
|
+
clear
|
986
|
+
else
|
987
|
+
self.class.alloc(node)
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
# @private
|
992
|
+
class AVLNode
|
993
|
+
def self.from_items(items, comparator, from = 0, to = items.size-1) # items must be sorted
|
994
|
+
size = to - from + 1
|
995
|
+
if size >= 3
|
996
|
+
middle = (to + from) / 2
|
997
|
+
AVLNode.new(items[middle], comparator, AVLNode.from_items(items, comparator, from, middle-1), AVLNode.from_items(items, comparator, middle+1, to))
|
998
|
+
elsif size == 2
|
999
|
+
empty = AVLNode::Empty.new(comparator)
|
1000
|
+
AVLNode.new(items[from], comparator, empty, AVLNode.new(items[from+1], comparator, empty, empty))
|
1001
|
+
elsif size == 1
|
1002
|
+
empty = AVLNode::Empty.new(comparator)
|
1003
|
+
AVLNode.new(items[from], comparator, empty, empty)
|
1004
|
+
elsif size == 0
|
1005
|
+
AVLNode::Empty.new(comparator)
|
1006
|
+
end
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
def initialize(item, comparator, left, right)
|
1010
|
+
@item, @comparator, @left, @right = item, comparator, left, right
|
1011
|
+
@height = ((right.height > left.height) ? right.height : left.height) + 1
|
1012
|
+
@size = right.size + left.size + 1
|
1013
|
+
end
|
1014
|
+
attr_reader :item, :left, :right, :height, :size
|
1015
|
+
|
1016
|
+
def from_items(items)
|
1017
|
+
AVLNode.from_items(items.sort(&@comparator), @comparator)
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def natural_order?
|
1021
|
+
false
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
def empty?
|
1025
|
+
false
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
def clear
|
1029
|
+
AVLNode::Empty.new(@comparator)
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
def derive(item, left, right)
|
1033
|
+
AVLNode.new(item, @comparator, left, right)
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
def insert(item)
|
1037
|
+
dir = direction(item)
|
1038
|
+
if dir == 0
|
1039
|
+
throw :present
|
1040
|
+
elsif dir > 0
|
1041
|
+
rebalance_right(@left, @right.insert(item))
|
1042
|
+
else
|
1043
|
+
rebalance_left(@left.insert(item), @right)
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
def bulk_insert(items)
|
1048
|
+
return self if items.empty?
|
1049
|
+
if items.size == 1
|
1050
|
+
catch :present do
|
1051
|
+
return insert(items.first)
|
1052
|
+
end
|
1053
|
+
return self
|
1054
|
+
end
|
1055
|
+
left, right = partition(items)
|
1056
|
+
|
1057
|
+
if right.size > left.size
|
1058
|
+
rebalance_right(@left.bulk_insert(left), @right.bulk_insert(right))
|
1059
|
+
else
|
1060
|
+
rebalance_left(@left.bulk_insert(left), @right.bulk_insert(right))
|
1061
|
+
end
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
def delete(item)
|
1065
|
+
dir = direction(item)
|
1066
|
+
if dir == 0
|
1067
|
+
if @right.empty?
|
1068
|
+
return @left # replace this node with its only child
|
1069
|
+
elsif @left.empty?
|
1070
|
+
return @right # likewise
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
if balance > 0
|
1074
|
+
# tree is leaning to the left. replace with highest node on that side
|
1075
|
+
replace_with = @left.max
|
1076
|
+
derive(replace_with, @left.delete(replace_with), @right)
|
1077
|
+
else
|
1078
|
+
# tree is leaning to the right. replace with lowest node on that side
|
1079
|
+
replace_with = @right.min
|
1080
|
+
derive(replace_with, @left, @right.delete(replace_with))
|
1081
|
+
end
|
1082
|
+
elsif dir > 0
|
1083
|
+
rebalance_left(@left, @right.delete(item))
|
1084
|
+
else
|
1085
|
+
rebalance_right(@left.delete(item), @right)
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
def bulk_delete(items)
|
1090
|
+
return self if items.empty?
|
1091
|
+
if items.size == 1
|
1092
|
+
catch :not_present do
|
1093
|
+
return delete(items.first)
|
1094
|
+
end
|
1095
|
+
return self
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
left, right, keep_item = [], [], true
|
1099
|
+
items.each do |item|
|
1100
|
+
dir = direction(item)
|
1101
|
+
if dir > 0
|
1102
|
+
right << item
|
1103
|
+
elsif dir < 0
|
1104
|
+
left << item
|
1105
|
+
else
|
1106
|
+
keep_item = false
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
left = @left.bulk_delete(left)
|
1111
|
+
right = @right.bulk_delete(right)
|
1112
|
+
finish_removal(keep_item, left, right)
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
def keep_only(items)
|
1116
|
+
return clear if items.empty?
|
1117
|
+
|
1118
|
+
left, right, keep_item = [], [], false
|
1119
|
+
items.each do |item|
|
1120
|
+
dir = direction(item)
|
1121
|
+
if dir > 0
|
1122
|
+
right << item
|
1123
|
+
elsif dir < 0
|
1124
|
+
left << item
|
1125
|
+
else
|
1126
|
+
keep_item = true
|
1127
|
+
end
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
left = @left.keep_only(left)
|
1131
|
+
right = @right.keep_only(right)
|
1132
|
+
finish_removal(keep_item, left, right)
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
def finish_removal(keep_item, left, right)
|
1136
|
+
# deletion of items may have occurred on left and right sides
|
1137
|
+
# now we may also need to delete the current item
|
1138
|
+
if keep_item
|
1139
|
+
rebalance(left, right) # no need to delete the current item
|
1140
|
+
elsif left.empty?
|
1141
|
+
right
|
1142
|
+
elsif right.empty?
|
1143
|
+
left
|
1144
|
+
elsif left.height > right.height
|
1145
|
+
replace_with = left.max
|
1146
|
+
derive(replace_with, left.delete(replace_with), right)
|
1147
|
+
else
|
1148
|
+
replace_with = right.min
|
1149
|
+
derive(replace_with, left, right.delete(replace_with))
|
1150
|
+
end
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
def prefix(item, inclusive)
|
1154
|
+
dir = direction(item)
|
1155
|
+
if dir > 0 || (inclusive && dir == 0)
|
1156
|
+
rebalance_left(@left, @right.prefix(item, inclusive))
|
1157
|
+
else
|
1158
|
+
@left.prefix(item, inclusive)
|
1159
|
+
end
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
def suffix(item, inclusive)
|
1163
|
+
dir = direction(item)
|
1164
|
+
if dir < 0 || (inclusive && dir == 0)
|
1165
|
+
rebalance_right(@left.suffix(item, inclusive), @right)
|
1166
|
+
else
|
1167
|
+
@right.suffix(item, inclusive)
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
def between(from, to)
|
1172
|
+
if direction(from) > 0 # all on the right
|
1173
|
+
@right.between(from, to)
|
1174
|
+
elsif direction(to) < 0 # all on the left
|
1175
|
+
@left.between(from, to)
|
1176
|
+
else
|
1177
|
+
left = @left.suffix(from, true)
|
1178
|
+
right = @right.prefix(to, true)
|
1179
|
+
rebalance(left, right)
|
1180
|
+
end
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
def each_less(item, inclusive, &block)
|
1184
|
+
dir = direction(item)
|
1185
|
+
if dir > 0 || (inclusive && dir == 0)
|
1186
|
+
@left.each(&block)
|
1187
|
+
yield @item
|
1188
|
+
@right.each_less(item, inclusive, &block)
|
1189
|
+
else
|
1190
|
+
@left.each_less(item, inclusive, &block)
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
def each_greater(item, inclusive, &block)
|
1195
|
+
dir = direction(item)
|
1196
|
+
if dir < 0 || (inclusive && dir == 0)
|
1197
|
+
@left.each_greater(item, inclusive, &block)
|
1198
|
+
yield @item
|
1199
|
+
@right.each(&block)
|
1200
|
+
else
|
1201
|
+
@right.each_greater(item, inclusive, &block)
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
def each_between(from, to, &block)
|
1206
|
+
if direction(from) > 0 # all on the right
|
1207
|
+
@right.each_between(from, to, &block)
|
1208
|
+
elsif direction(to) < 0 # all on the left
|
1209
|
+
@left.each_between(from, to, &block)
|
1210
|
+
else
|
1211
|
+
@left.each_greater(from, true, &block)
|
1212
|
+
yield @item
|
1213
|
+
@right.each_less(to, true, &block)
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
|
1217
|
+
def each(&block)
|
1218
|
+
@left.each(&block)
|
1219
|
+
yield @item
|
1220
|
+
@right.each(&block)
|
1221
|
+
end
|
1222
|
+
|
1223
|
+
def reverse_each(&block)
|
1224
|
+
@right.reverse_each(&block)
|
1225
|
+
yield @item
|
1226
|
+
@left.reverse_each(&block)
|
1227
|
+
end
|
1228
|
+
|
1229
|
+
def drop(n)
|
1230
|
+
if n >= @size
|
1231
|
+
clear
|
1232
|
+
elsif n <= 0
|
1233
|
+
self
|
1234
|
+
elsif @left.size >= n
|
1235
|
+
rebalance_right(@left.drop(n), @right)
|
1236
|
+
elsif @left.size + 1 == n
|
1237
|
+
@right
|
1238
|
+
else
|
1239
|
+
@right.drop(n - @left.size - 1)
|
1240
|
+
end
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
def take(n)
|
1244
|
+
if n >= @size
|
1245
|
+
self
|
1246
|
+
elsif n <= 0
|
1247
|
+
clear
|
1248
|
+
elsif @left.size >= n
|
1249
|
+
@left.take(n)
|
1250
|
+
else
|
1251
|
+
rebalance_left(@left, @right.take(n - @left.size - 1))
|
1252
|
+
end
|
1253
|
+
end
|
1254
|
+
|
1255
|
+
def include?(item)
|
1256
|
+
dir = direction(item)
|
1257
|
+
if dir == 0
|
1258
|
+
true
|
1259
|
+
elsif dir > 0
|
1260
|
+
@right.include?(item)
|
1261
|
+
else
|
1262
|
+
@left.include?(item)
|
1263
|
+
end
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
def at(index)
|
1267
|
+
if index < @left.size
|
1268
|
+
@left.at(index)
|
1269
|
+
elsif index > @left.size
|
1270
|
+
@right.at(index - @left.size - 1)
|
1271
|
+
else
|
1272
|
+
@item
|
1273
|
+
end
|
1274
|
+
end
|
1275
|
+
|
1276
|
+
def max
|
1277
|
+
@right.empty? ? @item : @right.max
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
def min
|
1281
|
+
@left.empty? ? @item : @left.min
|
1282
|
+
end
|
1283
|
+
|
1284
|
+
def balance
|
1285
|
+
@left.height - @right.height
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
def slice(from, length)
|
1289
|
+
if length <= 0
|
1290
|
+
clear
|
1291
|
+
elsif from + length <= @left.size
|
1292
|
+
@left.slice(from, length)
|
1293
|
+
elsif from > @left.size
|
1294
|
+
@right.slice(from - @left.size - 1, length)
|
1295
|
+
else
|
1296
|
+
left = @left.slice(from, @left.size - from)
|
1297
|
+
right = @right.slice(0, from + length - @left.size - 1)
|
1298
|
+
rebalance(left, right)
|
1299
|
+
end
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
def partition(items)
|
1303
|
+
left, right = [], []
|
1304
|
+
items.each do |item|
|
1305
|
+
dir = direction(item)
|
1306
|
+
if dir > 0
|
1307
|
+
right << item
|
1308
|
+
elsif dir < 0
|
1309
|
+
left << item
|
1310
|
+
end
|
1311
|
+
end
|
1312
|
+
[left, right]
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
def rebalance(left, right)
|
1316
|
+
if left.height > right.height
|
1317
|
+
rebalance_left(left, right)
|
1318
|
+
else
|
1319
|
+
rebalance_right(left, right)
|
1320
|
+
end
|
1321
|
+
end
|
1322
|
+
|
1323
|
+
def rebalance_left(left, right)
|
1324
|
+
# the tree might be unbalanced to the left (paths on the left too long)
|
1325
|
+
balance = left.height - right.height
|
1326
|
+
if balance >= 2
|
1327
|
+
if left.balance > 0
|
1328
|
+
# single right rotation
|
1329
|
+
derive(left.item, left.left, derive(@item, left.right, right))
|
1330
|
+
else
|
1331
|
+
# left rotation, then right
|
1332
|
+
derive(left.right.item, derive(left.item, left.left, left.right.left), derive(@item, left.right.right, right))
|
1333
|
+
end
|
1334
|
+
else
|
1335
|
+
derive(@item, left, right)
|
1336
|
+
end
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
def rebalance_right(left, right)
|
1340
|
+
# the tree might be unbalanced to the right (paths on the right too long)
|
1341
|
+
balance = left.height - right.height
|
1342
|
+
if balance <= -2
|
1343
|
+
if right.balance > 0
|
1344
|
+
# right rotation, then left
|
1345
|
+
derive(right.left.item, derive(@item, left, right.left.left), derive(right.item, right.left.right, right.right))
|
1346
|
+
else
|
1347
|
+
# single left rotation
|
1348
|
+
derive(right.item, derive(@item, left, right.left), right.right)
|
1349
|
+
end
|
1350
|
+
else
|
1351
|
+
derive(@item, left, right)
|
1352
|
+
end
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
def direction(item)
|
1356
|
+
@comparator.call(item, @item)
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
# @private
|
1360
|
+
class Empty
|
1361
|
+
def initialize(comparator); @comparator = comparator; end
|
1362
|
+
def natural_order?; false; end
|
1363
|
+
def left; self; end
|
1364
|
+
def right; self; end
|
1365
|
+
def height; 0; end
|
1366
|
+
def size; 0; end
|
1367
|
+
def min; nil; end
|
1368
|
+
def max; nil; end
|
1369
|
+
def each; end
|
1370
|
+
def reverse_each; end
|
1371
|
+
def at(index); nil; end
|
1372
|
+
def insert(item)
|
1373
|
+
AVLNode.new(item, @comparator, self, self)
|
1374
|
+
end
|
1375
|
+
def bulk_insert(items)
|
1376
|
+
items = items.to_a if !items.is_a?(Array)
|
1377
|
+
AVLNode.from_items(items.sort(&@comparator), @comparator)
|
1378
|
+
end
|
1379
|
+
def bulk_delete(items); self; end
|
1380
|
+
def keep_only(items); self; end
|
1381
|
+
def delete(item); throw :not_present; end
|
1382
|
+
def include?(item); false; end
|
1383
|
+
def prefix(item, inclusive); self; end
|
1384
|
+
def suffix(item, inclusive); self; end
|
1385
|
+
def between(from, to); self; end
|
1386
|
+
def each_greater(item, inclusive); end
|
1387
|
+
def each_less(item, inclusive); end
|
1388
|
+
def each_between(item, inclusive); end
|
1389
|
+
def drop(n); self; end
|
1390
|
+
def take(n); self; end
|
1391
|
+
def empty?; true; end
|
1392
|
+
def slice(from, length); self; end
|
1393
|
+
end
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
# @private
|
1397
|
+
# AVL node which does not use a comparator function; it keeps items sorted
|
1398
|
+
# in their natural order
|
1399
|
+
class PlainAVLNode < AVLNode
|
1400
|
+
def self.from_items(items, from = 0, to = items.size-1) # items must be sorted
|
1401
|
+
size = to - from + 1
|
1402
|
+
if size >= 3
|
1403
|
+
middle = (to + from) / 2
|
1404
|
+
PlainAVLNode.new(items[middle], PlainAVLNode.from_items(items, from, middle-1), PlainAVLNode.from_items(items, middle+1, to))
|
1405
|
+
elsif size == 2
|
1406
|
+
PlainAVLNode.new(items[from], PlainAVLNode::EmptyNode, PlainAVLNode.new(items[from+1], PlainAVLNode::EmptyNode, PlainAVLNode::EmptyNode))
|
1407
|
+
elsif size == 1
|
1408
|
+
PlainAVLNode.new(items[from], PlainAVLNode::EmptyNode, PlainAVLNode::EmptyNode)
|
1409
|
+
elsif size == 0
|
1410
|
+
PlainAVLNode::EmptyNode
|
1411
|
+
end
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
def initialize(item, left, right)
|
1415
|
+
@item, @left, @right = item, left, right
|
1416
|
+
@height = ((right.height > left.height) ? right.height : left.height) + 1
|
1417
|
+
@size = right.size + left.size + 1
|
1418
|
+
end
|
1419
|
+
attr_reader :item, :left, :right, :height, :size
|
1420
|
+
|
1421
|
+
def from_items(items)
|
1422
|
+
PlainAVLNode.from_items(items.sort)
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
def natural_order?
|
1426
|
+
true
|
1427
|
+
end
|
1428
|
+
|
1429
|
+
def clear
|
1430
|
+
PlainAVLNode::EmptyNode
|
1431
|
+
end
|
1432
|
+
|
1433
|
+
def derive(item, left, right)
|
1434
|
+
PlainAVLNode.new(item, left, right)
|
1435
|
+
end
|
1436
|
+
|
1437
|
+
def direction(item)
|
1438
|
+
item <=> @item
|
1439
|
+
end
|
1440
|
+
|
1441
|
+
# @private
|
1442
|
+
class Empty < AVLNode::Empty
|
1443
|
+
def initialize; end
|
1444
|
+
def natural_order?; true; end
|
1445
|
+
def insert(item)
|
1446
|
+
PlainAVLNode.new(item, self, self)
|
1447
|
+
end
|
1448
|
+
def bulk_insert(items)
|
1449
|
+
items = items.to_a if !items.is_a?(Array)
|
1450
|
+
PlainAVLNode.from_items(items.sort)
|
1451
|
+
end
|
1452
|
+
end
|
1453
|
+
|
1454
|
+
EmptyNode = PlainAVLNode::Empty.new
|
1455
|
+
end
|
1456
|
+
end
|
1457
|
+
|
1458
|
+
# The canonical empty `SortedSet`. Returned by `SortedSet[]`
|
1459
|
+
# when invoked with no arguments; also returned by `SortedSet.empty`. Prefer using
|
1460
|
+
# this one rather than creating many empty sorted sets using `SortedSet.new`.
|
1461
|
+
#
|
1462
|
+
# @private
|
1463
|
+
EmptySortedSet = Immutable::SortedSet.empty
|
1464
|
+
end
|