farce 0 → 0.1.0
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1525 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +435 -8
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Common interface for concurrent sets backed by Farce maps.
|
|
8
|
+
# Membership changes are atomic per element. Operations involving several
|
|
9
|
+
# elements are not atomic as a whole. Iteration streams without first copying
|
|
10
|
+
# every element. Structural changes during traversal can invalidate it.
|
|
11
|
+
# Identity comparison and normalization are fixed at construction, so this
|
|
12
|
+
# class does not provide Set#compare_by_identity or Set#reset.
|
|
13
|
+
class Set < Collection
|
|
14
|
+
include Internal::MarshalSupport::Initialize
|
|
15
|
+
|
|
16
|
+
# A shareable, immutable key used by mode-backed sets. Structural keys
|
|
17
|
+
# contain an insertion-time snapshot. Identity keys contain either the
|
|
18
|
+
# shareable element itself or an opaque token.
|
|
19
|
+
class MembershipKey
|
|
20
|
+
attr_reader :value
|
|
21
|
+
|
|
22
|
+
def initialize(value, identity: false)
|
|
23
|
+
@value = value
|
|
24
|
+
@identity = identity
|
|
25
|
+
@hash = identity ? value.__id__.hash : value.hash
|
|
26
|
+
freeze
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def eql?(other)
|
|
30
|
+
return false unless other.instance_of?(self.class) && @identity == other.identity?
|
|
31
|
+
@identity ? @value.equal?(other.value) : @value.eql?(other.value)
|
|
32
|
+
end
|
|
33
|
+
alias == eql?
|
|
34
|
+
|
|
35
|
+
def hash = [self.class, @identity, @hash].hash
|
|
36
|
+
def identity? = @identity
|
|
37
|
+
end
|
|
38
|
+
private_constant :MembershipKey
|
|
39
|
+
|
|
40
|
+
# Keeps the manager that created a payload beside it. Derived sets can
|
|
41
|
+
# retain the stored representation without opening move envelopes.
|
|
42
|
+
class StoredEntry
|
|
43
|
+
include Shareable::Immutable
|
|
44
|
+
|
|
45
|
+
attr_reader :manager, :payload
|
|
46
|
+
|
|
47
|
+
def initialize(manager, payload)
|
|
48
|
+
@manager = manager
|
|
49
|
+
@payload = payload
|
|
50
|
+
super()
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
private_constant :StoredEntry
|
|
54
|
+
|
|
55
|
+
IDENTITY_TOKENS = Local::WeakKeyMap.new(compare_keys_by_identity: true)
|
|
56
|
+
MISSING_KEY = Object.new.freeze
|
|
57
|
+
private_constant :IDENTITY_TOKENS, :MISSING_KEY
|
|
58
|
+
|
|
59
|
+
# Construct a set, optionally transforming each initial element before normalization.
|
|
60
|
+
# @param enumerable [#each, nil] The initial elements, or nil for an empty set.
|
|
61
|
+
# @param normalize [Symbol, Proc, Hash, Farce::Abstract::Map, nil] The element normalizer.
|
|
62
|
+
# @param compare_by_identity [Boolean] Whether membership uses object identity.
|
|
63
|
+
# @param mode [Symbol] The default transfer mode for Farce::Set, Farce::SortedSet,
|
|
64
|
+
# and Farce::WeakSet. Defaults to :copy for strong sets and :raise for WeakSet.
|
|
65
|
+
# @param options [Hash] Additional options for the selected variant.
|
|
66
|
+
# @option options [Symbol] scope (:ractor) The scope used by Farce::Local variants.
|
|
67
|
+
# @yield [element] Optionally transform each initial element before storage.
|
|
68
|
+
# @yieldparam element [BasicObject] An element from enumerable.
|
|
69
|
+
# @yieldreturn [BasicObject] The element to normalize and store.
|
|
70
|
+
# @return [Farce::Abstract::Set] The new set.
|
|
71
|
+
def initialize(
|
|
72
|
+
enumerable = nil, normalize: nil, compare_by_identity: false, mode: UNDEFINED, **options, &transform
|
|
73
|
+
)
|
|
74
|
+
unknown = options.keys - [:scope]
|
|
75
|
+
|
|
76
|
+
unless unknown.empty?
|
|
77
|
+
label = unknown.size == 1 ? "keyword" : "keywords"
|
|
78
|
+
raise ArgumentError, "unknown #{label}: #{unknown.map(&:inspect).join(", ")}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
shareable = !is_a?(Unshareable)
|
|
82
|
+
|
|
83
|
+
validate_boolean!(:compare_by_identity, compare_by_identity)
|
|
84
|
+
|
|
85
|
+
@compare_by_identity = compare_by_identity
|
|
86
|
+
@normalizer = Internal::KeyNormalizer.build(normalize, shareable:)
|
|
87
|
+
restoring = Internal::KeyNormalizer.restoration?(normalize)
|
|
88
|
+
|
|
89
|
+
initialize_value_mode(mode)
|
|
90
|
+
elements = input_elements(enumerable) unless enumerable.nil?
|
|
91
|
+
elements.map! { transform.call(it) } if transform && elements
|
|
92
|
+
elements.map! { normalize_element(it) } if elements && @normalizer && !restoring
|
|
93
|
+
|
|
94
|
+
if value_modes?
|
|
95
|
+
@map = new_map(nil, compare_keys_by_identity: false, **options)
|
|
96
|
+
elements&.each { add_normalized(it) }
|
|
97
|
+
else
|
|
98
|
+
entries = elements&.map { [it, true] }
|
|
99
|
+
@map = new_map(entries, compare_keys_by_identity: compare_by_identity, **options)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
super()
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# @api private
|
|
106
|
+
def marshal_dump
|
|
107
|
+
normalizer = @normalizer && Internal::KeyNormalizer.dump(@normalizer)
|
|
108
|
+
contents =
|
|
109
|
+
if value_modes?
|
|
110
|
+
each_stored.map do |key, entry|
|
|
111
|
+
identity = identity_storage_key?(key)
|
|
112
|
+
snapshot = identity ? key.value : key
|
|
113
|
+
[identity, Internal::MarshalSupport.value(snapshot), entry.manager,
|
|
114
|
+
Internal::MarshalSupport.value(entry.payload)]
|
|
115
|
+
end
|
|
116
|
+
else
|
|
117
|
+
@map.marshal_dump
|
|
118
|
+
end
|
|
119
|
+
[1, contents, compare_by_identity?, normalizer, respond_to?(:mode) ? mode : nil, frozen?]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @api private
|
|
123
|
+
def marshal_load(data)
|
|
124
|
+
contents, identity, normalizer, mode, frozen = Internal::MarshalSupport.payload(data, 5)
|
|
125
|
+
@compare_by_identity = identity
|
|
126
|
+
@normalizer = Internal::KeyNormalizer.build(
|
|
127
|
+
normalizer && Internal::KeyNormalizer.restore(normalizer),
|
|
128
|
+
shareable: !is_a?(Unshareable),
|
|
129
|
+
)
|
|
130
|
+
initialize_value_mode(mode || UNDEFINED)
|
|
131
|
+
if value_modes?
|
|
132
|
+
@map = new_map(nil, compare_keys_by_identity: false)
|
|
133
|
+
publish_marshaled
|
|
134
|
+
contents.each do |identity_key, snapshot, manager, stored|
|
|
135
|
+
key = Internal::MarshalSupport.restore_value(snapshot)
|
|
136
|
+
key = MembershipKey.new(key, identity: true) if identity_key
|
|
137
|
+
value = Internal::MarshalSupport.restore_value(stored)
|
|
138
|
+
add_stored(key, StoredEntry.new(manager, value))
|
|
139
|
+
end
|
|
140
|
+
else
|
|
141
|
+
# Select the backing class locally without recording its private name.
|
|
142
|
+
@map = new_map(nil, compare_keys_by_identity: identity).class.allocate
|
|
143
|
+
@map.marshal_load(contents)
|
|
144
|
+
publish_marshaled
|
|
145
|
+
end
|
|
146
|
+
Internal::MarshalSupport.freeze(self, frozen)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @api private
|
|
150
|
+
def transaction_wrapper(transaction) = Transaction::Set.new(transaction, self, @map)
|
|
151
|
+
|
|
152
|
+
# Add an element and return self.
|
|
153
|
+
# @param element [BasicObject] The element to normalize and store.
|
|
154
|
+
# @param mode [Symbol, nil] Override the transfer mode for this insertion. Only
|
|
155
|
+
# Farce::Set, Farce::SortedSet, and Farce::WeakSet accept this option. Nil uses the set's default.
|
|
156
|
+
# @return [self] The set.
|
|
157
|
+
def add(element, mode: UNDEFINED)
|
|
158
|
+
check_frozen!
|
|
159
|
+
add_normalized(normalize_element(element), mode:)
|
|
160
|
+
self
|
|
161
|
+
end
|
|
162
|
+
alias << add
|
|
163
|
+
|
|
164
|
+
# Add an absent element and return self, or nil if it was already present.
|
|
165
|
+
# @param element [BasicObject] The element to normalize and store.
|
|
166
|
+
# @param mode [Symbol, nil] Override the transfer mode for this insertion. Only
|
|
167
|
+
# Farce::Set, Farce::SortedSet, and Farce::WeakSet accept this option. Nil uses the set's default.
|
|
168
|
+
# @return [self, nil] The set if added, otherwise nil.
|
|
169
|
+
def add?(element, mode: UNDEFINED) # rubocop:disable Naming/PredicateMethod
|
|
170
|
+
check_frozen!
|
|
171
|
+
added = add_normalized?(normalize_element(element), mode:)
|
|
172
|
+
self if added
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Remove an element and return self.
|
|
176
|
+
# @param element [BasicObject] The element to normalize and look up.
|
|
177
|
+
# @return [self] The set.
|
|
178
|
+
def delete(element)
|
|
179
|
+
check_frozen!
|
|
180
|
+
element = normalize_element(element)
|
|
181
|
+
if value_modes?
|
|
182
|
+
key = lookup_key(element)
|
|
183
|
+
@map.delete(key) unless MISSING_KEY.equal?(key)
|
|
184
|
+
else
|
|
185
|
+
@map.delete(element)
|
|
186
|
+
end
|
|
187
|
+
self
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Remove an element and return self, or nil if it was absent.
|
|
191
|
+
# @param element [BasicObject] The element to normalize and look up.
|
|
192
|
+
# @return [self, nil] The set if removed, otherwise nil.
|
|
193
|
+
def delete?(element) # rubocop:disable Naming/PredicateMethod
|
|
194
|
+
check_frozen!
|
|
195
|
+
element = normalize_element(element)
|
|
196
|
+
removed = if value_modes?
|
|
197
|
+
key = lookup_key(element)
|
|
198
|
+
@map.delete(key) unless MISSING_KEY.equal?(key)
|
|
199
|
+
else
|
|
200
|
+
@map.delete(element)
|
|
201
|
+
end
|
|
202
|
+
self if removed
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Return whether an element is present.
|
|
206
|
+
# @param element [BasicObject] The element to normalize and look up.
|
|
207
|
+
# @return [Boolean] Whether the normalized element is present.
|
|
208
|
+
def include?(element)
|
|
209
|
+
element = normalize_element(element)
|
|
210
|
+
return @map.key?(element) unless value_modes?
|
|
211
|
+
key = lookup_key(element)
|
|
212
|
+
!MISSING_KEY.equal?(key) && @map.key?(key)
|
|
213
|
+
end
|
|
214
|
+
alias member? include?
|
|
215
|
+
alias === include?
|
|
216
|
+
|
|
217
|
+
# Iterate over the elements currently present without first copying all elements.
|
|
218
|
+
# @yield [element] Visit each element. Returns an Enumerator without a block.
|
|
219
|
+
# @yieldparam element [BasicObject] The current element.
|
|
220
|
+
# @yieldreturn [void] The result is ignored.
|
|
221
|
+
# @return [self, Enumerator] The set, or an Enumerator without a block.
|
|
222
|
+
def each
|
|
223
|
+
return enum_for(__method__) { size } unless block_given?
|
|
224
|
+
each_element { yield it }
|
|
225
|
+
self
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Return the number of elements currently present.
|
|
229
|
+
# @return [Integer]
|
|
230
|
+
def size = @map.size
|
|
231
|
+
|
|
232
|
+
# Remove all elements and return self.
|
|
233
|
+
# @return [self]
|
|
234
|
+
def clear
|
|
235
|
+
check_frozen!
|
|
236
|
+
@map.clear
|
|
237
|
+
self
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Add all elements yielded by each enumerable.
|
|
241
|
+
# @param enumerables [Array<#each>] The collections of elements to add.
|
|
242
|
+
# @return [self] The set.
|
|
243
|
+
def merge(*enumerables)
|
|
244
|
+
check_frozen!
|
|
245
|
+
enumerables.each do |enumerable|
|
|
246
|
+
if canonical_compatible?(enumerable)
|
|
247
|
+
enumerable.each_stored { |key, value| add_stored(key, value) }
|
|
248
|
+
else
|
|
249
|
+
each_input(enumerable) { add(it) }
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
self
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Remove every element yielded by enumerable.
|
|
256
|
+
# @param enumerable [#each] The elements to remove.
|
|
257
|
+
# @return [self] The set.
|
|
258
|
+
def subtract(enumerable)
|
|
259
|
+
check_frozen!
|
|
260
|
+
if canonical_compatible?(enumerable)
|
|
261
|
+
retained = membership_index(enumerable)
|
|
262
|
+
@map.delete_if { |element, _| retained.key?(element) }
|
|
263
|
+
else
|
|
264
|
+
each_input(enumerable) do |element|
|
|
265
|
+
key = lookup_key(normalize_element(element))
|
|
266
|
+
@map.delete(key) unless MISSING_KEY.equal?(key)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
self
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Remove elements accepted by the block.
|
|
273
|
+
# @yield [element] Test each element. Returns an Enumerator without a block.
|
|
274
|
+
# @yieldparam element [BasicObject] The current element.
|
|
275
|
+
# @yieldreturn [BasicObject] A truthy value to remove the element.
|
|
276
|
+
# @return [self, Enumerator] The set, or an Enumerator without a block.
|
|
277
|
+
def delete_if
|
|
278
|
+
return enum_for(__method__) { size } unless block_given?
|
|
279
|
+
check_frozen!
|
|
280
|
+
each_stored { |key, value| @map.delete(key) if yield(public_stored(key, value)) }
|
|
281
|
+
self
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Keep elements accepted by the block.
|
|
285
|
+
# @yield [element] Test each element. Returns an Enumerator without a block.
|
|
286
|
+
# @yieldparam element [BasicObject] The current element.
|
|
287
|
+
# @yieldreturn [BasicObject] A truthy value to keep the element.
|
|
288
|
+
# @return [self, Enumerator] The set, or an Enumerator without a block.
|
|
289
|
+
def keep_if
|
|
290
|
+
return enum_for(__method__) { size } unless block_given?
|
|
291
|
+
check_frozen!
|
|
292
|
+
each_stored { |key, value| @map.delete(key) unless yield(public_stored(key, value)) }
|
|
293
|
+
self
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Return a same-kind set containing elements accepted by the block.
|
|
297
|
+
# @yield [element] Test each element. Returns an Enumerator without a block.
|
|
298
|
+
# @yieldparam element [BasicObject] The current element.
|
|
299
|
+
# @yieldreturn [BasicObject] A truthy value to keep the element.
|
|
300
|
+
# @return [Farce::Abstract::Set, Enumerator] A new set, or an Enumerator without a block.
|
|
301
|
+
def select
|
|
302
|
+
return enum_for(__method__) { size } unless block_given?
|
|
303
|
+
dup.filter_backend! { yield it }
|
|
304
|
+
end
|
|
305
|
+
alias filter select
|
|
306
|
+
|
|
307
|
+
# Return a same-kind set without elements accepted by the block.
|
|
308
|
+
# @yield [element] Test each element. Returns an Enumerator without a block.
|
|
309
|
+
# @yieldparam element [BasicObject] The current element.
|
|
310
|
+
# @yieldreturn [BasicObject] A truthy value to exclude the element.
|
|
311
|
+
# @return [Farce::Abstract::Set, Enumerator] A new set, or an Enumerator without a block.
|
|
312
|
+
def reject
|
|
313
|
+
return enum_for(__method__) { size } unless block_given?
|
|
314
|
+
dup.filter_backend! { !yield(it) }
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Remove elements accepted by the block, returning nil when unchanged.
|
|
318
|
+
# @yield [element] Test each element. Returns an Enumerator without a block.
|
|
319
|
+
# @yieldparam element [BasicObject] The current element.
|
|
320
|
+
# @yieldreturn [BasicObject] A truthy value to remove the element.
|
|
321
|
+
# @return [self, nil, Enumerator] The changed set, nil if unchanged, or an Enumerator without a block.
|
|
322
|
+
def reject!
|
|
323
|
+
return enum_for(__method__) { size } unless block_given?
|
|
324
|
+
check_frozen!
|
|
325
|
+
changed = false
|
|
326
|
+
each_stored do |key, value|
|
|
327
|
+
changed = true if yield(public_stored(key, value)) && @map.delete(key)
|
|
328
|
+
end
|
|
329
|
+
self if changed
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Keep elements accepted by the block, returning nil when unchanged.
|
|
333
|
+
# @yield [element] Test each element. Returns an Enumerator without a block.
|
|
334
|
+
# @yieldparam element [BasicObject] The current element.
|
|
335
|
+
# @yieldreturn [BasicObject] A truthy value to keep the element.
|
|
336
|
+
# @return [self, nil, Enumerator] The changed set, nil if unchanged, or an Enumerator without a block.
|
|
337
|
+
def select!
|
|
338
|
+
return enum_for(__method__) { size } unless block_given?
|
|
339
|
+
check_frozen!
|
|
340
|
+
changed = false
|
|
341
|
+
each_stored do |key, value|
|
|
342
|
+
changed = true if !yield(public_stored(key, value)) && @map.delete(key)
|
|
343
|
+
end
|
|
344
|
+
self if changed
|
|
345
|
+
end
|
|
346
|
+
alias filter! select!
|
|
347
|
+
|
|
348
|
+
# Return the elements in an Array.
|
|
349
|
+
# @return [Array<BasicObject>] The observed elements.
|
|
350
|
+
def to_a = each.to_a
|
|
351
|
+
|
|
352
|
+
# Convert to a Ruby Set, or to an explicitly requested set class.
|
|
353
|
+
# @param klass [Class] The target set class. Defaults to Ruby's ::Set.
|
|
354
|
+
# @param arguments [Array<BasicObject>] Additional positional arguments for its constructor.
|
|
355
|
+
# @param options [Hash] Keyword arguments for its constructor.
|
|
356
|
+
# @yield [element] Optionally transform elements through the target constructor.
|
|
357
|
+
# @yieldparam element [BasicObject] The current element.
|
|
358
|
+
# @yieldreturn [BasicObject] The element to store in the target set.
|
|
359
|
+
# @return [::Set, Farce::Abstract::Set] A new Ruby Set by default. An explicit
|
|
360
|
+
# target class matching self returns self when no arguments, options, or block
|
|
361
|
+
# are supplied. Otherwise, returns a new instance of klass.
|
|
362
|
+
def to_set(klass = ::Set, *arguments, **options, &)
|
|
363
|
+
return self if klass == self.class && arguments.empty? && options.empty? && !block_given?
|
|
364
|
+
klass.new(self, *arguments, **options, &)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Return a same-kind set containing elements from either operand.
|
|
368
|
+
# @param enumerables [Array<#each>] The collections of elements to include.
|
|
369
|
+
# @return [Farce::Abstract::Set] A new set of the same class with the same settings.
|
|
370
|
+
def union(*enumerables) = dup.merge(*enumerables)
|
|
371
|
+
alias | union
|
|
372
|
+
alias + union
|
|
373
|
+
|
|
374
|
+
# Return a same-kind set without elements from the enumerable.
|
|
375
|
+
# @param enumerable [#each] The elements to exclude.
|
|
376
|
+
# @return [Farce::Abstract::Set] A new set of the same class with the same settings.
|
|
377
|
+
def difference(enumerable) = dup.subtract(enumerable)
|
|
378
|
+
alias - difference
|
|
379
|
+
|
|
380
|
+
# Return a same-kind set containing elements also present in every enumerable.
|
|
381
|
+
# @param enumerables [Array<#each>] The collections whose members must be present.
|
|
382
|
+
# @return [Farce::Abstract::Set] A new set of the same class with the same settings.
|
|
383
|
+
def intersection(*enumerables)
|
|
384
|
+
copy = dup
|
|
385
|
+
indexes = enumerables.map { membership_index(it) }
|
|
386
|
+
copy.filter_stored! { |key, _| indexes.all? { it.key?(key) } }
|
|
387
|
+
copy
|
|
388
|
+
end
|
|
389
|
+
alias & intersection
|
|
390
|
+
|
|
391
|
+
# Return a same-kind set containing elements present in exactly one operand.
|
|
392
|
+
# @param other [#each] The other collection of elements.
|
|
393
|
+
# @return [Farce::Abstract::Set] A new set of the same class with the same settings.
|
|
394
|
+
def ^(other)
|
|
395
|
+
other = empty_copy.merge(other)
|
|
396
|
+
(self - other).merge(other - self)
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Return whether every element is present in the other set-like object.
|
|
400
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
401
|
+
# @return [Boolean] Whether the relation holds.
|
|
402
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
403
|
+
def subset?(other)
|
|
404
|
+
validate_set_like(other)
|
|
405
|
+
left = equality_index
|
|
406
|
+
right = relation_index(other)
|
|
407
|
+
left.size <= right.size && left.each_key.all? { right.key?(it) }
|
|
408
|
+
end
|
|
409
|
+
alias <= subset?
|
|
410
|
+
|
|
411
|
+
# Return whether this is a proper subset of the other set-like object.
|
|
412
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
413
|
+
# @return [Boolean] Whether the relation holds.
|
|
414
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
415
|
+
def proper_subset?(other)
|
|
416
|
+
validate_set_like(other)
|
|
417
|
+
left = equality_index
|
|
418
|
+
right = relation_index(other)
|
|
419
|
+
left.size < right.size && left.each_key.all? { right.key?(it) }
|
|
420
|
+
end
|
|
421
|
+
alias < proper_subset?
|
|
422
|
+
|
|
423
|
+
# Return whether all elements of the other set-like object are present.
|
|
424
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
425
|
+
# @return [Boolean] Whether the relation holds.
|
|
426
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
427
|
+
def superset?(other)
|
|
428
|
+
validate_set_like(other)
|
|
429
|
+
left = equality_index
|
|
430
|
+
right = relation_index(other)
|
|
431
|
+
left.size >= right.size && right.each_key.all? { left.key?(it) }
|
|
432
|
+
end
|
|
433
|
+
alias >= superset?
|
|
434
|
+
|
|
435
|
+
# Return whether this is a proper superset of the other set-like object.
|
|
436
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
437
|
+
# @return [Boolean] Whether the relation holds.
|
|
438
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
439
|
+
def proper_superset?(other)
|
|
440
|
+
validate_set_like(other)
|
|
441
|
+
left = equality_index
|
|
442
|
+
right = relation_index(other)
|
|
443
|
+
left.size > right.size && right.each_key.all? { left.key?(it) }
|
|
444
|
+
end
|
|
445
|
+
alias > proper_superset?
|
|
446
|
+
|
|
447
|
+
# Return whether this set and another set-like object share an element.
|
|
448
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
449
|
+
# @return [Boolean] Whether the relation holds.
|
|
450
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
451
|
+
def intersect?(other)
|
|
452
|
+
validate_set_like(other)
|
|
453
|
+
left = equality_index
|
|
454
|
+
right = relation_index(other)
|
|
455
|
+
left, right = right, left if right.size < left.size
|
|
456
|
+
left.each_key.any? { right.key?(it) }
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Return whether this set and another set-like object share no elements.
|
|
460
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
461
|
+
# @return [Boolean] Whether the relation holds.
|
|
462
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
463
|
+
def disjoint?(other) = !intersect?(other)
|
|
464
|
+
|
|
465
|
+
# Compare sets by the subset relation.
|
|
466
|
+
# @param other [BasicObject] The object to compare against.
|
|
467
|
+
# @return [Integer, nil] -1 for a proper subset, 0 for equal sets, 1 for a proper
|
|
468
|
+
# superset, or nil when neither relation holds or other is not a set.
|
|
469
|
+
def <=>(other)
|
|
470
|
+
return unless set_like?(other)
|
|
471
|
+
return 0 if self == other
|
|
472
|
+
return -1 if proper_subset?(other)
|
|
473
|
+
1 if proper_superset?(other)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# Group elements by the block result.
|
|
477
|
+
# @yield [element] Choose a group for each element. Returns an Enumerator without a block.
|
|
478
|
+
# @yieldparam element [BasicObject] The current element.
|
|
479
|
+
# @yieldreturn [BasicObject] The group key.
|
|
480
|
+
# @return [Hash{BasicObject => Farce::Abstract::Set}, Enumerator] Group keys mapped
|
|
481
|
+
# to same-kind subsets, or an Enumerator without a block.
|
|
482
|
+
def classify
|
|
483
|
+
return enum_for(__method__) { size } unless block_given?
|
|
484
|
+
groups = {}
|
|
485
|
+
each_stored do |stored_key, stored_value|
|
|
486
|
+
element = public_stored(stored_key, stored_value)
|
|
487
|
+
key = yield element
|
|
488
|
+
(groups[key] ||= empty_copy).add_stored(stored_key, stored_value)
|
|
489
|
+
end
|
|
490
|
+
groups
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
# Divide elements into same-kind subsets.
|
|
494
|
+
# The outer result is an Unshared::Set that holds the subsets strongly.
|
|
495
|
+
#
|
|
496
|
+
# @overload divide { |element| ... }
|
|
497
|
+
# Group elements that produce the same block result.
|
|
498
|
+
# @yield [element] Choose a group for each element.
|
|
499
|
+
# @yieldparam element [BasicObject] The current element.
|
|
500
|
+
# @yieldreturn [BasicObject] The group key.
|
|
501
|
+
# @return [Farce::Unshared::Set] A set containing the same-kind subsets.
|
|
502
|
+
# @overload divide { |left, right| ... }
|
|
503
|
+
# Compute strongly connected components using a two-argument block.
|
|
504
|
+
# @yield [left, right] Test the directed relation between two elements.
|
|
505
|
+
# @yieldparam left [BasicObject] The source element.
|
|
506
|
+
# @yieldparam right [BasicObject] The candidate related element.
|
|
507
|
+
# @yieldreturn [BasicObject] A truthy value when left is related to right.
|
|
508
|
+
# @return [Farce::Unshared::Set] A set containing the same-kind components.
|
|
509
|
+
# @overload divide
|
|
510
|
+
# @return [Enumerator] An Enumerator that divides the set when given a block.
|
|
511
|
+
def divide(&block)
|
|
512
|
+
return enum_for(__method__) { size } unless block
|
|
513
|
+
groups = if block.arity == 2
|
|
514
|
+
# Ruby 3.4 also requires TSort here, inside Set#divide.
|
|
515
|
+
require "tsort"
|
|
516
|
+
divide_by_relation(&block)
|
|
517
|
+
else
|
|
518
|
+
classify(&block).values
|
|
519
|
+
end
|
|
520
|
+
Farce::Unshared::Set.new(groups)
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
# Return a flattened same-kind set.
|
|
524
|
+
# @return [Farce::Abstract::Set] A new set containing the recursively expanded members.
|
|
525
|
+
# @raise [ArgumentError] If a nested set contains itself recursively.
|
|
526
|
+
def flatten
|
|
527
|
+
copy = empty_copy
|
|
528
|
+
flatten_into(self, copy, ::Set.new.compare_by_identity)
|
|
529
|
+
copy
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# Return whether membership uses object identity.
|
|
533
|
+
# @return [Boolean] Whether identity comparison is enabled.
|
|
534
|
+
def compare_by_identity? = @compare_by_identity
|
|
535
|
+
|
|
536
|
+
# Return whether elements are held weakly.
|
|
537
|
+
# @return [Boolean] Whether the set retains elements weakly.
|
|
538
|
+
def weak? = @map.weak_keys?
|
|
539
|
+
|
|
540
|
+
# Return whether this set has the same members as another set.
|
|
541
|
+
# @param other [BasicObject] The object to compare against.
|
|
542
|
+
# @return [Boolean] Whether other is a Farce set with compatible comparison settings and equal members.
|
|
543
|
+
def ==(other)
|
|
544
|
+
return true if equal?(other)
|
|
545
|
+
return false unless other.is_a?(Abstract::Set) && size == other.size
|
|
546
|
+
return false unless comparison_compatible?(other)
|
|
547
|
+
index = other.equality_index
|
|
548
|
+
equality_index.each_key.all? { index.key?(it) }
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# Compare membership using eql?.
|
|
552
|
+
# @param other [BasicObject] The object to compare against.
|
|
553
|
+
# @return [Boolean] Whether other is a Farce set with compatible comparison settings and equal members.
|
|
554
|
+
def eql?(other)
|
|
555
|
+
return true if equal?(other)
|
|
556
|
+
return false unless other.is_a?(Abstract::Set) && size == other.size
|
|
557
|
+
return false unless comparison_compatible?(other)
|
|
558
|
+
index = other.equality_index
|
|
559
|
+
equality_index.each_key.all? { index.key?(it) }
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# Return an order-independent hash derived from the members.
|
|
563
|
+
# @return [Integer] The hash code.
|
|
564
|
+
def hash
|
|
565
|
+
guard = recursion_guard(:farce_set_hash_guard)
|
|
566
|
+
return 0 if guard.key?(self)
|
|
567
|
+
guard[self] = true
|
|
568
|
+
entered = true
|
|
569
|
+
set = ::Set.new
|
|
570
|
+
set.merge(equality_index.keys).hash
|
|
571
|
+
ensure
|
|
572
|
+
guard&.delete(self) if entered
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
protected
|
|
576
|
+
|
|
577
|
+
def initialize_empty_copy(other)
|
|
578
|
+
initialize_copy(other, empty: true)
|
|
579
|
+
publish_shareable if is_a?(Shareable)
|
|
580
|
+
self
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def each_stored(&block)
|
|
584
|
+
return enum_for(__method__) { size } unless block
|
|
585
|
+
@map.each_live(&block)
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def filter_backend!(&keep)
|
|
589
|
+
check_frozen!
|
|
590
|
+
filter_stored! { |key, value| keep.call(public_stored(key, value)) }
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
def filter_stored!
|
|
594
|
+
check_frozen!
|
|
595
|
+
each_stored { |key, value| @map.delete(key) unless yield(key, value) }
|
|
596
|
+
self
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
def equality_index
|
|
600
|
+
each_stored.with_object({}) do |(key, _), index|
|
|
601
|
+
key = comparison_key_for_canonical(key) unless value_modes?
|
|
602
|
+
index[key] = true
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def add_stored(key, value)
|
|
607
|
+
@map.store_if_absent(key) { value }
|
|
608
|
+
self
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def public_stored(key, value) = value_modes? ? unwrap_entry(key, value) : key
|
|
612
|
+
|
|
613
|
+
def canonical_compatible?(other)
|
|
614
|
+
return false unless other.is_a?(Abstract::Set)
|
|
615
|
+
@normalizer.equal?(other.instance_variable_get(:@normalizer)) &&
|
|
616
|
+
compare_by_identity? == other.compare_by_identity? &&
|
|
617
|
+
value_modes? == other.value_modes? &&
|
|
618
|
+
ordered? == other.ordered?
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
def add_normalized(element, mode: UNDEFINED)
|
|
622
|
+
add_normalized?(element, mode:)
|
|
623
|
+
self
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def value_modes? = false
|
|
627
|
+
def map_backend = @map
|
|
628
|
+
def ordered? = false
|
|
629
|
+
|
|
630
|
+
private
|
|
631
|
+
|
|
632
|
+
def each_for_inspect
|
|
633
|
+
each_stored { |key, entry| yield(value_modes? ? entry : key) }
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def inspect_value(inspector, value)
|
|
637
|
+
if value_modes?
|
|
638
|
+
inspector.object(value.payload, value.manager)
|
|
639
|
+
else
|
|
640
|
+
super
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
def check_frozen! = Internal::Freeze.check(self)
|
|
645
|
+
def freeze_backend = @map
|
|
646
|
+
|
|
647
|
+
def initialize_copy(other, empty: false)
|
|
648
|
+
super(other)
|
|
649
|
+
@map = if empty
|
|
650
|
+
new_map(
|
|
651
|
+
nil,
|
|
652
|
+
compare_keys_by_identity: value_modes? ? false : other.compare_by_identity?,
|
|
653
|
+
**copy_map_options(other),
|
|
654
|
+
)
|
|
655
|
+
else
|
|
656
|
+
other.map_backend.dup
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def recursion_guard(key)
|
|
661
|
+
Internal::Storage.fiber.store_if_absent(key) { {}.compare_by_identity }
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def each_element
|
|
665
|
+
each_stored { |key, value| yield public_stored(key, value) }
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
def each_input(enumerable)
|
|
669
|
+
raise ArgumentError, "value must be enumerable" unless enumerable.respond_to?(:each)
|
|
670
|
+
enumerable.each { yield it }
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
def input_elements(enumerable)
|
|
674
|
+
raise ArgumentError, "value must be enumerable" unless enumerable.respond_to?(:each)
|
|
675
|
+
elements = []
|
|
676
|
+
enumerable.each { elements << it } # rubocop:disable Style/MapIntoArray
|
|
677
|
+
elements
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
def empty_copy
|
|
681
|
+
copy = self.class.allocate
|
|
682
|
+
instance_variables.each { |name| copy.instance_variable_set(name, instance_variable_get(name)) }
|
|
683
|
+
copy.initialize_empty_copy(self)
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
def map_canonical
|
|
687
|
+
copy = empty_copy
|
|
688
|
+
each { copy.add_normalized(yield(it)) }
|
|
689
|
+
copy
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
def membership_index(enumerable)
|
|
693
|
+
raise ArgumentError, "value must be enumerable" unless enumerable.respond_to?(:each)
|
|
694
|
+
index = {}
|
|
695
|
+
index.compare_by_identity if compare_by_identity? && !value_modes?
|
|
696
|
+
if canonical_compatible?(enumerable)
|
|
697
|
+
enumerable.each_stored { |key, _| index[key] = true }
|
|
698
|
+
else
|
|
699
|
+
enumerable.each do |element|
|
|
700
|
+
element = normalize_element(element)
|
|
701
|
+
if value_modes?
|
|
702
|
+
key = lookup_key(element)
|
|
703
|
+
index[key] = true unless MISSING_KEY.equal?(key)
|
|
704
|
+
else
|
|
705
|
+
index[element] = true
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
index
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
def normalize_element(element) = @normalizer ? @normalizer.call(element) : element
|
|
713
|
+
|
|
714
|
+
def initialize_value_mode(mode)
|
|
715
|
+
raise ArgumentError, "unknown keyword: :mode" unless UNDEFINED.equal?(mode)
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def validate_boolean!(name, value)
|
|
719
|
+
return if value.equal?(true) || value.equal?(false)
|
|
720
|
+
raise ArgumentError, "#{name} must be a boolean"
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
def add_normalized?(element, mode: UNDEFINED)
|
|
724
|
+
raise ArgumentError, "unknown keyword: :mode" unless value_modes? || UNDEFINED.equal?(mode)
|
|
725
|
+
if value_modes?
|
|
726
|
+
add_mode_value?(element, mode:)
|
|
727
|
+
else
|
|
728
|
+
added = false
|
|
729
|
+
@map.store_if_absent(element) do
|
|
730
|
+
added = true
|
|
731
|
+
true
|
|
732
|
+
end
|
|
733
|
+
added
|
|
734
|
+
end
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def add_mode_value?(element, mode:)
|
|
738
|
+
requested_mode = UNDEFINED.equal?(mode) || mode.nil? ? @manager.mode : mode
|
|
739
|
+
existing_key = lookup_key(element)
|
|
740
|
+
return false unless MISSING_KEY.equal?(existing_key) || !@map.key?(existing_key)
|
|
741
|
+
|
|
742
|
+
key, payload = prepare_mode_entry(element, requested_mode)
|
|
743
|
+
added = false
|
|
744
|
+
|
|
745
|
+
@map.store_if_absent(key) do
|
|
746
|
+
added = true
|
|
747
|
+
payload = @manager.wrap(element, mode: requested_mode) if UNDEFINED.equal?(payload)
|
|
748
|
+
StoredEntry.new(@manager, payload)
|
|
749
|
+
end
|
|
750
|
+
added
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
def prepare_mode_entry(element, requested_mode)
|
|
754
|
+
if identity_membership?(element) && !Ractor.shareable?(element)
|
|
755
|
+
case requested_mode
|
|
756
|
+
when :copy
|
|
757
|
+
raise ArgumentError, "identity comparison cannot copy a non-shareable element"
|
|
758
|
+
when :shareable_copy, :make_shareable
|
|
759
|
+
payload = @manager.wrap(element, mode: requested_mode)
|
|
760
|
+
return [insertion_key(payload), payload]
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
key = insertion_key(element)
|
|
765
|
+
[key, UNDEFINED]
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
def insertion_key(element)
|
|
769
|
+
return structural_key(element) unless identity_membership?(element)
|
|
770
|
+
token = IDENTITY_TOKENS[element]
|
|
771
|
+
return MembershipKey.new(token, identity: true) if token
|
|
772
|
+
return MembershipKey.new(element, identity: true) if Ractor.shareable?(element)
|
|
773
|
+
|
|
774
|
+
token = IDENTITY_TOKENS.store_if_absent(element) { Object.new.freeze }
|
|
775
|
+
MembershipKey.new(token, identity: true)
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
def lookup_key(element)
|
|
779
|
+
return element unless value_modes?
|
|
780
|
+
return structural_key(element) unless identity_membership?(element)
|
|
781
|
+
token = IDENTITY_TOKENS[element]
|
|
782
|
+
return MembershipKey.new(token, identity: true) if token
|
|
783
|
+
return MembershipKey.new(element, identity: true) if Ractor.shareable?(element)
|
|
784
|
+
|
|
785
|
+
MISSING_KEY
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
def structural_key(element)
|
|
789
|
+
return element if Ractor.shareable?(element)
|
|
790
|
+
|
|
791
|
+
snapshot = Ractor.make_shareable(element, copy: true)
|
|
792
|
+
unless snapshot.eql?(element) && element.eql?(snapshot) && snapshot.hash == element.hash
|
|
793
|
+
raise ArgumentError, "element cannot be represented by a stable equality snapshot"
|
|
794
|
+
end
|
|
795
|
+
snapshot
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
def identity_membership?(element)
|
|
799
|
+
compare_by_identity? || element.method(:eql?).owner == Kernel
|
|
800
|
+
rescue NameError
|
|
801
|
+
true
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
def unwrap_entry(key, entry)
|
|
805
|
+
element = entry.manager.unwrap(entry.payload)
|
|
806
|
+
if identity_storage_key?(key) && !Ractor.shareable?(element)
|
|
807
|
+
token = key.value
|
|
808
|
+
IDENTITY_TOKENS.store_if_absent(element) { token }
|
|
809
|
+
end
|
|
810
|
+
element
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
def stored_mode(entry)
|
|
814
|
+
case entry.payload
|
|
815
|
+
when Envelope::Copy then :copy
|
|
816
|
+
when Envelope::Local then :local
|
|
817
|
+
when Envelope::Move then :move
|
|
818
|
+
else @manager.mode
|
|
819
|
+
end
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
def identity_storage_key?(key)
|
|
823
|
+
key.instance_of?(MembershipKey) && key.identity?
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
def copy_map_options(other)
|
|
827
|
+
other.respond_to?(:scope) ? { scope: other.scope } : {}
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def validate_set_like(other)
|
|
831
|
+
return if set_like?(other)
|
|
832
|
+
raise ArgumentError, "value must be a set"
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
def set_like?(object) = object.is_a?(Abstract::Set) || object.is_a?(::Set)
|
|
836
|
+
|
|
837
|
+
def comparison_compatible?(other)
|
|
838
|
+
(!other.respond_to?(:compare_by_identity?) || compare_by_identity? == other.compare_by_identity?) &&
|
|
839
|
+
ordered? == other.is_a?(Abstract::SortedSet)
|
|
840
|
+
end
|
|
841
|
+
|
|
842
|
+
def relation_index(other)
|
|
843
|
+
return other.equality_index if other.is_a?(Abstract::Set)
|
|
844
|
+
membership_index(other)
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
def comparison_key_for_canonical(element)
|
|
848
|
+
return element unless identity_membership?(element)
|
|
849
|
+
token = IDENTITY_TOKENS[element]
|
|
850
|
+
return MembershipKey.new(token, identity: true) if token
|
|
851
|
+
return MembershipKey.new(element, identity: true) if Ractor.shareable?(element)
|
|
852
|
+
|
|
853
|
+
token = IDENTITY_TOKENS.store_if_absent(element) { Object.new.freeze }
|
|
854
|
+
MembershipKey.new(token, identity: true)
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
def flatten_into(source, target, seen)
|
|
858
|
+
raise ArgumentError, "tried to flatten recursive set" if seen.include?(source)
|
|
859
|
+
seen.add(source)
|
|
860
|
+
entered = true
|
|
861
|
+
if source.is_a?(Abstract::Set)
|
|
862
|
+
source.each_stored do |key, value|
|
|
863
|
+
element = source.public_stored(key, value)
|
|
864
|
+
if set_like?(element)
|
|
865
|
+
flatten_into(element, target, seen)
|
|
866
|
+
elsif target.canonical_compatible?(source)
|
|
867
|
+
target.add_stored(key, value)
|
|
868
|
+
else
|
|
869
|
+
target.add(element)
|
|
870
|
+
end
|
|
871
|
+
end
|
|
872
|
+
else
|
|
873
|
+
source.each do |element|
|
|
874
|
+
set_like?(element) ? flatten_into(element, target, seen) : target.add(element)
|
|
875
|
+
end
|
|
876
|
+
end
|
|
877
|
+
ensure
|
|
878
|
+
seen.delete(source) if entered
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
def divide_by_relation(&relation)
|
|
882
|
+
entries = @map.to_a
|
|
883
|
+
graph = {}
|
|
884
|
+
graph.compare_by_identity if compare_by_identity? && !value_modes?
|
|
885
|
+
entries.each do |key, value|
|
|
886
|
+
element = public_stored(key, value)
|
|
887
|
+
graph[key] = entries.filter_map do |candidate_key, candidate_value|
|
|
888
|
+
candidate = public_stored(candidate_key, candidate_value)
|
|
889
|
+
candidate_key if relation.call(element, candidate)
|
|
890
|
+
end
|
|
891
|
+
end
|
|
892
|
+
graph.extend(TSort)
|
|
893
|
+
graph.define_singleton_method(:tsort_each_node) { |&yield_node| each_key(&yield_node) }
|
|
894
|
+
graph.define_singleton_method(:tsort_each_child) { |node, &yield_child| fetch(node).each(&yield_child) }
|
|
895
|
+
graph.strongly_connected_components.map do |component|
|
|
896
|
+
empty_copy.tap do |group|
|
|
897
|
+
component.each { |key| group.add_stored(key, @map.fetch(key)) }
|
|
898
|
+
end
|
|
899
|
+
end
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
# simplecov:disable
|
|
903
|
+
def new_map(...)
|
|
904
|
+
raise "subclass failed to implement #new_map" unless instance_of?(Set)
|
|
905
|
+
raise NoMethodError, "Farce::Abstract::Set should not be instantiated directly. Use a subclass instead."
|
|
906
|
+
end
|
|
907
|
+
# simplecov:enable
|
|
908
|
+
end
|
|
909
|
+
end
|
|
910
|
+
end
|