diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 78623dd..7602434 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,14 +1,14 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -polar: # Replace with a single Polar username -buy_me_a_coffee: # Replace with a single Buy Me a Coffee username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.ruby-version b/.ruby-version index 9c25013..3aa4e0c 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.6 +3.3.6 diff --git a/lib/array_of_hashes.rb b/lib/array_of_hashes.rb index 06d048d..32546e3 100644 --- a/lib/array_of_hashes.rb +++ b/lib/array_of_hashes.rb @@ -1,200 +1,200 @@ -# [Written By: ArityWolf; RhuxWolf; SkuruWolf - eigenfruit@gmail.com] # -# Array of hashes; a array wherein each indice's contents is a hash which can contain data. Serialized by Marshal. -require 'fileutils' -# last updated: 2017-12-13 - -# TODO: -# -2017-Dec-13 -# * Implemented reverse_add(hash) -- appends to the front of @collection instead of adding to the back as usual. -# -# -24-Sep-14 : most of the day -# * Implemented File saving methods: [aoh_object].save!(file_location: "test", aoh: nil) -# * As a side note: you may have to modify the get_id instance method to return a certain string (or false) -# if there is no id at that particular location. Also, will rem_id return an error if it can't -# find any data at that location. -# -# -20-Sep-14 : 10:40am- -# * Create an Exception library (inheriting StandardError) -# * Find a way to implement containers, where every function below can perform operations based on what type or -# What's inside the container <-- .map :p -# -# -20-Sep-14 : 5:37pm- -# * Make self.load and self.save more secure by using something like Base64.encode / Base64.decode -# -# -03-Oct-14 : 7:57am- -# * See if there's a way to make it so that get_id(n) doesn't return an array with hashes inside -# ...Albeit, this works fine if we're dealing with getting multiple id's at the same time -# - -# Array-of-hashes container -class AOH - def initialize(*args) - @collection = [] - @path_to_root = nil # we give a default path for some instance object of AOH, to make specifying where a file is to be saved with a bit more ease - args.map { |a_hash| add(a_hash) } - end - - def add(hash) - if hash.is_a? Hash # this is where you could check to see what kind of classN it is then react accordingly - @collection.push(hash) - else - raise "#{hash.inspect} is NOT a Hash..." - end - end - - def reverse_add(hash) - if hash.is_a? Hash - @collection.unshift(hash) - else - raise "#{hash.inspect} is NOT a Hash..." - end - end - - def collection_get - @collection - end - - def first - @collection[0] - end - - def last - @collection.last - end - - def max - @collection.size - 1 - end - - def swap_id(id_a:, id_b:) - if id_a.is_a?(Integer) && id_b.is_a?(Integer) && (!get_id(id_a).nil? || !get_id(id_b).nil?) - @collection[id_a], @collection[id_b] = @collection[id_b], @collection[id_a] - else - return false - end - - true - end - - def clear_then_save; end - - # or nil to get all ---- THIS IS WHERE you may have to make a modification on the return of @collection[id] - def get_id(id = nil) - if @collection.empty? - nil - elsif id.nil? - map { |i| i } - else - return nil if id.negative? || (id > @collection.size) - - @collection[id.to_i] - end - end - - def set_id(id, &block) - raise "Improper id: #{id}" unless id.is_a? Integer - raise 'No block given in set_id()' unless block_given? - - hash = get_id(id) - if hash.nil? # there is nothing here - raise "#{id} not in #{hash.inspect}" - else - block.call(get_id(id)) - end - end - - def rem_by_id(id) - raise 'id is not an integer' unless id.is_a? Integer - - @collection.delete_at(id) - end - - def each_with_index - raise 'block not given in each_with_index(&block)' unless block_given? - - # @collection.each_with_index.map {|a_hash, index| yield [a_hash, index]} #now figure out a nice way to add to the arguments... - @collection.each_with_index.map do |a_hash, index| - yield [a_hash, index] - end - end - - def map(&block) - raise 'block not given in map(&block)' unless block_given? - - # @collection.each_with_index.map {|a_hash, index| yield [a_hash, index]} #now figure out a nice way to add to the arguments... - @collection.map { |a_hash| block.call(a_hash) } # now figure out a nice way to add to the arguments... - end - - def each - @collection.map { |hash| hash } - end - - def print_all - loc = 0 - each do |hash| - hash.each_pair do |id, data| - puts "Loc[#{loc}]: #{id.inspect} => #{data.inspect}" # you could put a yield here one of these days - end - loc += 1 - puts "\n\n" - end - end - - def self.save(aoh: nil, file_location: 'test') - File.open(file_location, 'w') do |file_handle| - raise 'AOH undefined' unless aoh.instance_of?(AOH) - - Marshal.dump(aoh, file_handle) - end - - aoh - end - - def self.touch!(file_location: 'test') - unless file_location - FileUtils.touch(file_location) - load!(file_location: file_location) - end - end - - def save!(file_location: 'test', aoh: nil) - self.class.save(file_location: file_location, aoh: self) - end - - # save no matter what - def self.save!(aoh: AOH.new, file_location: nil) - raise "file_location not specified or is nil (file_location: #{file_location.inspect})" unless file_location - - File.open(file_location, 'w') do |file_handle| - Marshal.dump(aoh, file_handle) - end - end - - # factory method; returns an AOH object with those specific array-of-hashes (will create a new file if file does not exist; use iff certain that you know what file you're loading) - def self.load!(aoh: AOH.new, file_location: nil, s_self: nil) - raise "file_location not specified or is nil (file_location: #{file_location})" unless file_location - - loaded = false - unless File.exist? file_location - touch!(file_location: file_location) - save!(aoh: aoh, file_location: file_location) - end - - File.open(file_location) do |file_handle| - loaded = Marshal.load(file_handle) - end - - loaded - end - - # factory method; returns an AOH object with those specific array-of-hashes - def self.load(file_location: 'test') - loaded = false - - File.open(file_location) do |file_handle| - loaded = Marshal.load(file_handle) - end - - loaded - end -end +# [Written By: ArityWolf; RhuxWolf; SkuruWolf - eigenfruit@gmail.com] # +# Array of hashes; a array wherein each indice's contents is a hash which can contain data. Serialized by Marshal. +require 'fileutils' +# last updated: 2017-12-13 + +# TODO: +# -2017-Dec-13 +# * Implemented reverse_add(hash) -- appends to the front of @collection instead of adding to the back as usual. +# +# -24-Sep-14 : most of the day +# * Implemented File saving methods: [aoh_object].save!(file_location: "test", aoh: nil) +# * As a side note: you may have to modify the get_id instance method to return a certain string (or false) +# if there is no id at that particular location. Also, will rem_id return an error if it can't +# find any data at that location. +# +# -20-Sep-14 : 10:40am- +# * Create an Exception library (inheriting StandardError) +# * Find a way to implement containers, where every function below can perform operations based on what type or +# What's inside the container <-- .map :p +# +# -20-Sep-14 : 5:37pm- +# * Make self.load and self.save more secure by using something like Base64.encode / Base64.decode +# +# -03-Oct-14 : 7:57am- +# * See if there's a way to make it so that get_id(n) doesn't return an array with hashes inside +# ...Albeit, this works fine if we're dealing with getting multiple id's at the same time +# + +# Array-of-hashes container +class AOH + def initialize(*args) + @collection = [] + @path_to_root = nil # we give a default path for some instance object of AOH, to make specifying where a file is to be saved with a bit more ease + args.map { |a_hash| add(a_hash) } + end + + def add(hash) + if hash.is_a? Hash # this is where you could check to see what kind of classN it is then react accordingly + @collection.push(hash) + else + raise "#{hash.inspect} is NOT a Hash..." + end + end + + def reverse_add(hash) + if hash.is_a? Hash + @collection.unshift(hash) + else + raise "#{hash.inspect} is NOT a Hash..." + end + end + + def collection_get + @collection + end + + def first + @collection[0] + end + + def last + @collection.last + end + + def max + @collection.size - 1 + end + + def swap_id(id_a:, id_b:) + if id_a.is_a?(Integer) && id_b.is_a?(Integer) && (!get_id(id_a).nil? || !get_id(id_b).nil?) + @collection[id_a], @collection[id_b] = @collection[id_b], @collection[id_a] + else + return false + end + + true + end + + def clear_then_save; end + + # or nil to get all ---- THIS IS WHERE you may have to make a modification on the return of @collection[id] + def get_id(id = nil) + if @collection.empty? + nil + elsif id.nil? + map { |i| i } + else + return nil if id.negative? || (id > @collection.size) + + @collection[id.to_i] + end + end + + def set_id(id, &block) + raise "Improper id: #{id}" unless id.is_a? Integer + raise 'No block given in set_id()' unless block_given? + + hash = get_id(id) + if hash.nil? # there is nothing here + raise "#{id} not in #{hash.inspect}" + else + block.call(get_id(id)) + end + end + + def rem_by_id(id) + raise 'id is not an integer' unless id.is_a? Integer + + @collection.delete_at(id) + end + + def each_with_index + raise 'block not given in each_with_index(&block)' unless block_given? + + # @collection.each_with_index.map {|a_hash, index| yield [a_hash, index]} #now figure out a nice way to add to the arguments... + @collection.each_with_index.map do |a_hash, index| + yield [a_hash, index] + end + end + + def map(&block) + raise 'block not given in map(&block)' unless block_given? + + # @collection.each_with_index.map {|a_hash, index| yield [a_hash, index]} #now figure out a nice way to add to the arguments... + @collection.map { |a_hash| block.call(a_hash) } # now figure out a nice way to add to the arguments... + end + + def each + @collection.map { |hash| hash } + end + + def print_all + loc = 0 + each do |hash| + hash.each_pair do |id, data| + puts "Loc[#{loc}]: #{id.inspect} => #{data.inspect}" # you could put a yield here one of these days + end + loc += 1 + puts "\n\n" + end + end + + def self.save(aoh: nil, file_location: 'test') + File.open(file_location, 'w') do |file_handle| + raise 'AOH undefined' unless aoh.instance_of?(AOH) + + Marshal.dump(aoh, file_handle) + end + + aoh + end + + def self.touch!(file_location: 'test') + unless file_location + FileUtils.touch(file_location) + load!(file_location: file_location) + end + end + + def save!(file_location: 'test', aoh: nil) + self.class.save(file_location: file_location, aoh: self) + end + + # save no matter what + def self.save!(aoh: AOH.new, file_location: nil) + raise "file_location not specified or is nil (file_location: #{file_location.inspect})" unless file_location + + File.open(file_location, 'w') do |file_handle| + Marshal.dump(aoh, file_handle) + end + end + + # factory method; returns an AOH object with those specific array-of-hashes (will create a new file if file does not exist; use iff certain that you know what file you're loading) + def self.load!(aoh: AOH.new, file_location: nil, s_self: nil) + raise "file_location not specified or is nil (file_location: #{file_location})" unless file_location + + loaded = false + unless File.exist? file_location + touch!(file_location: file_location) + save!(aoh: aoh, file_location: file_location) + end + + File.open(file_location) do |file_handle| + loaded = Marshal.load(file_handle) + end + + loaded + end + + # factory method; returns an AOH object with those specific array-of-hashes + def self.load(file_location: 'test') + loaded = false + + File.open(file_location) do |file_handle| + loaded = Marshal.load(file_handle) + end + + loaded + end +end diff --git a/lib/array_of_hashes.rbs b/lib/array_of_hashes.rbs index fa0a106..9e685f9 100644 --- a/lib/array_of_hashes.rbs +++ b/lib/array_of_hashes.rbs @@ -1,54 +1,54 @@ -# Array-of-hashes container -class AOH - @collection: untyped - - @path_to_root: untyped - - def initialize: (*untyped args) -> void - - def add: (untyped hash) -> untyped - - def reverse_add: (untyped hash) -> untyped - - def collection_get: () -> untyped - - def first: () -> untyped - - def last: () -> untyped - - def max: () -> untyped - - def swap_id: (id_a: untyped, id_b: untyped) -> (false | true) - - def clear_then_save: () -> nil - - # or nil to get all ---- THIS IS WHERE you may have to make a modification on the return of @collection[id] - def get_id: (?untyped? id) -> (nil | untyped | nil | untyped) - - def set_id: (untyped id) ?{ (?) -> untyped } -> untyped - - def rem_by_id: (untyped id) -> untyped - - def each_with_index: () ?{ (untyped) -> untyped } -> untyped - - def map: () ?{ (?) -> untyped } -> untyped - - def each: () -> untyped - - def print_all: () -> untyped - - def self.save: (?aoh: untyped?, ?file_location: ::String) -> untyped - - def self.touch!: (?file_location: ::String) -> (untyped | nil) - - def save!: (?file_location: ::String, ?aoh: untyped?) -> untyped - - # save no matter what - def self.save!: (?aoh: untyped, ?file_location: untyped?) -> untyped - - # factory method; returns an AOH object with those specific array-of-hashes (will create a new file if file does not exist; use iff certain that you know what file you're loading) - def self.load!: (?aoh: untyped, ?file_location: untyped?, ?s_self: untyped?) -> untyped - - # factory method; returns an AOH object with those specific array-of-hashes - def self.load: (?file_location: ::String) -> untyped -end +# Array-of-hashes container +class AOH + @collection: untyped + + @path_to_root: untyped + + def initialize: (*untyped args) -> void + + def add: (untyped hash) -> untyped + + def reverse_add: (untyped hash) -> untyped + + def collection_get: () -> untyped + + def first: () -> untyped + + def last: () -> untyped + + def max: () -> untyped + + def swap_id: (id_a: untyped, id_b: untyped) -> (false | true) + + def clear_then_save: () -> nil + + # or nil to get all ---- THIS IS WHERE you may have to make a modification on the return of @collection[id] + def get_id: (?untyped? id) -> (nil | untyped | nil | untyped) + + def set_id: (untyped id) ?{ (?) -> untyped } -> untyped + + def rem_by_id: (untyped id) -> untyped + + def each_with_index: () ?{ (untyped) -> untyped } -> untyped + + def map: () ?{ (?) -> untyped } -> untyped + + def each: () -> untyped + + def print_all: () -> untyped + + def self.save: (?aoh: untyped?, ?file_location: ::String) -> untyped + + def self.touch!: (?file_location: ::String) -> (untyped | nil) + + def save!: (?file_location: ::String, ?aoh: untyped?) -> untyped + + # save no matter what + def self.save!: (?aoh: untyped, ?file_location: untyped?) -> untyped + + # factory method; returns an AOH object with those specific array-of-hashes (will create a new file if file does not exist; use iff certain that you know what file you're loading) + def self.load!: (?aoh: untyped, ?file_location: untyped?, ?s_self: untyped?) -> untyped + + # factory method; returns an AOH object with those specific array-of-hashes + def self.load: (?file_location: ::String) -> untyped +end diff --git a/lib/file_context_managed_partitioned_array.rbs b/lib/file_context_managed_partitioned_array.rbs index 189ef53..1c833c0 100644 --- a/lib/file_context_managed_partitioned_array.rbs +++ b/lib/file_context_managed_partitioned_array.rbs @@ -1,229 +1,229 @@ -# VERSION v1.1.0-release - CRITICAL BUG FIXES: -# @fcmpa_db_max_capacity was not being set correctly (it was @fcmpa_db__max_capacity) -# TODO: implement folder removal mechanism in FCMPA#delete_database! for DragonRuby -# VERSION v1.0.1a - organized, prettified, and corrected one bug, or possible bug (1/3/2022 3:08PM) -# TODO: Study this code a bit and describe the architecture further, its a bit confusing going down this low level with the partitioned arrays. -# DONE: Prettify code, reduce linter warnings, and organize variables -# VERSION v1.0.0a - organized, prettified, and corrected one bug, or possible bug -# version 1.0.0a is battle test ready, and is simply a bootstrap for the FCMPAM -# VERSION v0.2.8 - reduced redundancy that happened by accident -# VERSION v0.2.7a - organized variables, fixed database overwrite bug, which was just giving each database "table" in the file heiratchy the name of itself -# VERSION v0.2.6 - organized variables, some debugging to make sure everything is set correctly initially - 11:31AM -# VERSION v0.2.5 - 2022-12-05 10:10AM -# FileContextManagedPartitionedArray -# A ManagedPartitionedArray that uses a FileContext to store its data. -# Fixed variable definitions and usage locations; program works as it did in an earlier version without the variable misuse and collisions. -# VERSION v0.2.4 -# VERSION v0.2.3 - the basics are working -# VERSION v0.2.2 - left off working on start_database! -- turns out that -# this class requires more work before the partitioned array manager will. -# VERSION v0.2.1 -# VERSION v0.2.0a - release test run -# VERSION v0.2.3 -# VERSION v0.2.2a (11/27/2022 - 10:20am) -# VERSION v0.2.1a (11/27/2022 - 6:25am) -# VERSION v0.2.0a -# Refining before field testing -class FileContextManagedPartitionedArray - @traverse_hash: untyped - - @raise_on_no_db: untyped - - @db_max_capacity: untyped - - @db_partition_amount_and_offset: untyped - - @db_dynamically_allocates: untyped - - @db_has_capacity: untyped - - @db_endless_add: untyped - - @db_partition_addition_amount: untyped - - @db_size: untyped - - @db_path: untyped - - @db_name: untyped - - @db_partition_archive_id: untyped - - @fcmpa_db_dynamically_allocates: untyped - - @fcmpa_db_indexer_name: untyped - - @fcmpa_db_folder_name: untyped - - @fcmpa_db_size: untyped - - @fcmpa_db_endless_add: untyped - - @fcmpa_db_has_capacity: untyped - - @fcmpa_db_partition_addition_amount: untyped - - @fcmpa_db_max_capacity: untyped - - @fcmpa_db_partition_amount_and_offset: untyped - - @fcmpa_db_partition_archive_id: untyped - - @fcmpa_db_index_location: untyped - - @fcmpa_active_databases: untyped - - @fcmpa_db_indexer_db: untyped - - @timestamp_str: untyped - - @label_integer: untyped - - @label_ranges: untyped - - @active_database: untyped - - attr_accessor fcmpa_db_indexer_db: untyped - - attr_accessor fcmpa_active_databases: untyped - - attr_accessor active_database: untyped - - attr_accessor db_file_incrementor: untyped - - attr_accessor db_file_location: untyped - - attr_accessor db_path: untyped - - attr_accessor db_name: untyped - - attr_accessor db_size: untyped - - attr_accessor db_endless_add: untyped - - attr_accessor db_has_capacity: untyped - - attr_accessor fcmpa_db_indexer_name: untyped - - attr_accessor fcmpa_db_folder_name: untyped - - attr_accessor fcmpa_db_size: untyped - - attr_accessor fcmpa_partition_amount_and_offset: untyped - - attr_accessor db_partition_amount_and_offset: untyped - - attr_accessor partition_addition_amount: untyped - - attr_accessor db_dynamically_allocates: untyped - - attr_accessor timestamp_str: untyped - - # DB_SIZE > PARTITION_AMOUNT - DB_SIZE: 20 - - DB_MAX_CAPACITY: "data_arr_size" - - DB_PARTITION_AMOUNT: 9 - - DB_PARTITION_OFFSET: 1 - - DB_PARTITION_ADDITION_AMOUNT: 5 - - DB_NAME: "fcmpa_db" - - DB_PATH: "./DB/FCMPA_DB" - - DB_HAS_CAPACITY: false - - DB_DYNAMICALLY_ALLOCATES: true - - DB_ENDLESS_ADD: true - - DB_PARTITION_ARCHIVE_ID: 0 - - FCMPA_DB_SIZE: 20 - - FCMPA_DB_ENDLESS_ADD: true - - FCMPA_DB_DYNAMICALLY_ALLOCATES: true - - FCMPA_DB_PARTITION_ADDITION_AMOUNT: 5 - - FCMPA_DB_HAS_CAPACITY: false - - FCMPA_DB_INDEXER_NAME: "FCMPA_DB_INDEX" - - FCMPA_DB_FOLDER_NAME: "./DB/FCMPA" - - FCMPA_DB_MAX_CAPACITY: "data_arr_size" - - FCMPA_DB_PARTITION_ARCHIVE_ID: 0 - - FCMPA_DB_PARTITION_AMOUNT: 9 - - FCMPA_DB_OFFSET: 1 - - FCMPA_DB_INDEX_LOCATION: 0 - - LABEL_INTEGER: false - - LABEL_RANGES: false - - TRAVERSE_HASH: true - - DEBUG: true - - RAISE_ON_NO_DB: false - - def debug: (untyped say) -> (untyped | nil) - - def initialize: (?raise_on_no_db: untyped, ?traverse_hash: untyped, ?db_max_capacity: untyped, ?db_size: untyped, ?db_endless_add: untyped, ?db_has_capacity: untyped, ?db_name: untyped, ?db_path: untyped, ?db_partition_amount_and_offset: untyped, ?db_dynamically_allocates: untyped, ?db_partition_addition_amount: untyped, ?db_partition_archive_id: untyped, ?fcmpa_db_indexer_name: untyped, ?fcmpa_db_folder_name: untyped, ?fcmpa_db_size: untyped, ?fcmpa_db_partition_amount_and_offset: untyped, ?fcmpa_db_has_capacity: untyped, ?fcmpa_db_partition_addition_amount: untyped, ?fcmpa_db_dynamically_allocates: untyped, ?fcmpa_db_endless_add: untyped, ?fcmpa_db_max_capacity: untyped, ?fcmpa_db_partition_archive_id: untyped, ?fcmpa_db_index_location: untyped, ?label_integer: untyped, ?label_ranges: untyped) -> void - - def load_indexer_db!: () -> untyped - - def new_timestamp: () -> untyped - - # Create a new database to be stored and ran by the FCMPA - def new_database: (untyped database_index_name_str, ?fcmpa_db_index_location: untyped, ?db_name: untyped, ?db_path: untyped, ?initial_autosave: bool) -> (false | true) - - def []: (untyped database_index_name) -> untyped - - def delete_database_from_index!: (untyped database_index_name, ?fcmpa_db_index_location: untyped) -> untyped - - def add_database_to_index: (untyped database_index_name, untyped database_path, untyped database_name, ?fcmpa_db_index_location: untyped) -> untyped - - def db: (?untyped database_index_name) -> untyped - - def set_active_database: (untyped database_index_name) -> untyped - - def stop_database!: (untyped database_index_name) -> untyped - - # left off making it so that the database auto allocates and auto loads and saves on call - def start_database!: (untyped database_index_name, ?raise_on_no_db: bool, ?db_name: untyped, ?db_path: untyped) -> untyped - - # ! denotes that this is an action that will be performed on the database and not a query - def stop_databases!: () -> untyped - - def save_database!: (?untyped database_index_name) -> untyped - - def save_databases!: () -> untyped - - def load_database!: (?untyped database_index_name) -> untyped - - def load_databases!: () -> untyped - - def delete_database!: (?untyped database_index_name, ?delete_files: bool) -> untyped - - def get_databases_list: () -> untyped - - alias db_list get_databases_list - - def set_new_file_archive!: (?untyped database_index_name) -> untyped - - # traverses the database and yields every element in @data_arr, even nils - def each: (untyped database_index_name, ?hash: untyped) { (untyped) -> untyped } -> (false | untyped) - - # traverses the database and returns all elements that are not nil, across all of @data_arr - def each_not_nil: (untyped database_index_name, ?hash: untyped) { (untyped) -> untyped } -> untyped -end +# VERSION v1.1.0-release - CRITICAL BUG FIXES: +# @fcmpa_db_max_capacity was not being set correctly (it was @fcmpa_db__max_capacity) +# TODO: implement folder removal mechanism in FCMPA#delete_database! for DragonRuby +# VERSION v1.0.1a - organized, prettified, and corrected one bug, or possible bug (1/3/2022 3:08PM) +# TODO: Study this code a bit and describe the architecture further, its a bit confusing going down this low level with the partitioned arrays. +# DONE: Prettify code, reduce linter warnings, and organize variables +# VERSION v1.0.0a - organized, prettified, and corrected one bug, or possible bug +# version 1.0.0a is battle test ready, and is simply a bootstrap for the FCMPAM +# VERSION v0.2.8 - reduced redundancy that happened by accident +# VERSION v0.2.7a - organized variables, fixed database overwrite bug, which was just giving each database "table" in the file heiratchy the name of itself +# VERSION v0.2.6 - organized variables, some debugging to make sure everything is set correctly initially - 11:31AM +# VERSION v0.2.5 - 2022-12-05 10:10AM +# FileContextManagedPartitionedArray +# A ManagedPartitionedArray that uses a FileContext to store its data. +# Fixed variable definitions and usage locations; program works as it did in an earlier version without the variable misuse and collisions. +# VERSION v0.2.4 +# VERSION v0.2.3 - the basics are working +# VERSION v0.2.2 - left off working on start_database! -- turns out that +# this class requires more work before the partitioned array manager will. +# VERSION v0.2.1 +# VERSION v0.2.0a - release test run +# VERSION v0.2.3 +# VERSION v0.2.2a (11/27/2022 - 10:20am) +# VERSION v0.2.1a (11/27/2022 - 6:25am) +# VERSION v0.2.0a +# Refining before field testing +class FileContextManagedPartitionedArray + @traverse_hash: untyped + + @raise_on_no_db: untyped + + @db_max_capacity: untyped + + @db_partition_amount_and_offset: untyped + + @db_dynamically_allocates: untyped + + @db_has_capacity: untyped + + @db_endless_add: untyped + + @db_partition_addition_amount: untyped + + @db_size: untyped + + @db_path: untyped + + @db_name: untyped + + @db_partition_archive_id: untyped + + @fcmpa_db_dynamically_allocates: untyped + + @fcmpa_db_indexer_name: untyped + + @fcmpa_db_folder_name: untyped + + @fcmpa_db_size: untyped + + @fcmpa_db_endless_add: untyped + + @fcmpa_db_has_capacity: untyped + + @fcmpa_db_partition_addition_amount: untyped + + @fcmpa_db_max_capacity: untyped + + @fcmpa_db_partition_amount_and_offset: untyped + + @fcmpa_db_partition_archive_id: untyped + + @fcmpa_db_index_location: untyped + + @fcmpa_active_databases: untyped + + @fcmpa_db_indexer_db: untyped + + @timestamp_str: untyped + + @label_integer: untyped + + @label_ranges: untyped + + @active_database: untyped + + attr_accessor fcmpa_db_indexer_db: untyped + + attr_accessor fcmpa_active_databases: untyped + + attr_accessor active_database: untyped + + attr_accessor db_file_incrementor: untyped + + attr_accessor db_file_location: untyped + + attr_accessor db_path: untyped + + attr_accessor db_name: untyped + + attr_accessor db_size: untyped + + attr_accessor db_endless_add: untyped + + attr_accessor db_has_capacity: untyped + + attr_accessor fcmpa_db_indexer_name: untyped + + attr_accessor fcmpa_db_folder_name: untyped + + attr_accessor fcmpa_db_size: untyped + + attr_accessor fcmpa_partition_amount_and_offset: untyped + + attr_accessor db_partition_amount_and_offset: untyped + + attr_accessor partition_addition_amount: untyped + + attr_accessor db_dynamically_allocates: untyped + + attr_accessor timestamp_str: untyped + + # DB_SIZE > PARTITION_AMOUNT + DB_SIZE: 20 + + DB_MAX_CAPACITY: "data_arr_size" + + DB_PARTITION_AMOUNT: 9 + + DB_PARTITION_OFFSET: 1 + + DB_PARTITION_ADDITION_AMOUNT: 5 + + DB_NAME: "fcmpa_db" + + DB_PATH: "./DB/FCMPA_DB" + + DB_HAS_CAPACITY: false + + DB_DYNAMICALLY_ALLOCATES: true + + DB_ENDLESS_ADD: true + + DB_PARTITION_ARCHIVE_ID: 0 + + FCMPA_DB_SIZE: 20 + + FCMPA_DB_ENDLESS_ADD: true + + FCMPA_DB_DYNAMICALLY_ALLOCATES: true + + FCMPA_DB_PARTITION_ADDITION_AMOUNT: 5 + + FCMPA_DB_HAS_CAPACITY: false + + FCMPA_DB_INDEXER_NAME: "FCMPA_DB_INDEX" + + FCMPA_DB_FOLDER_NAME: "./DB/FCMPA" + + FCMPA_DB_MAX_CAPACITY: "data_arr_size" + + FCMPA_DB_PARTITION_ARCHIVE_ID: 0 + + FCMPA_DB_PARTITION_AMOUNT: 9 + + FCMPA_DB_OFFSET: 1 + + FCMPA_DB_INDEX_LOCATION: 0 + + LABEL_INTEGER: false + + LABEL_RANGES: false + + TRAVERSE_HASH: true + + DEBUG: true + + RAISE_ON_NO_DB: false + + def debug: (untyped say) -> (untyped | nil) + + def initialize: (?raise_on_no_db: untyped, ?traverse_hash: untyped, ?db_max_capacity: untyped, ?db_size: untyped, ?db_endless_add: untyped, ?db_has_capacity: untyped, ?db_name: untyped, ?db_path: untyped, ?db_partition_amount_and_offset: untyped, ?db_dynamically_allocates: untyped, ?db_partition_addition_amount: untyped, ?db_partition_archive_id: untyped, ?fcmpa_db_indexer_name: untyped, ?fcmpa_db_folder_name: untyped, ?fcmpa_db_size: untyped, ?fcmpa_db_partition_amount_and_offset: untyped, ?fcmpa_db_has_capacity: untyped, ?fcmpa_db_partition_addition_amount: untyped, ?fcmpa_db_dynamically_allocates: untyped, ?fcmpa_db_endless_add: untyped, ?fcmpa_db_max_capacity: untyped, ?fcmpa_db_partition_archive_id: untyped, ?fcmpa_db_index_location: untyped, ?label_integer: untyped, ?label_ranges: untyped) -> void + + def load_indexer_db!: () -> untyped + + def new_timestamp: () -> untyped + + # Create a new database to be stored and ran by the FCMPA + def new_database: (untyped database_index_name_str, ?fcmpa_db_index_location: untyped, ?db_name: untyped, ?db_path: untyped, ?initial_autosave: bool) -> (false | true) + + def []: (untyped database_index_name) -> untyped + + def delete_database_from_index!: (untyped database_index_name, ?fcmpa_db_index_location: untyped) -> untyped + + def add_database_to_index: (untyped database_index_name, untyped database_path, untyped database_name, ?fcmpa_db_index_location: untyped) -> untyped + + def db: (?untyped database_index_name) -> untyped + + def set_active_database: (untyped database_index_name) -> untyped + + def stop_database!: (untyped database_index_name) -> untyped + + # left off making it so that the database auto allocates and auto loads and saves on call + def start_database!: (untyped database_index_name, ?raise_on_no_db: bool, ?db_name: untyped, ?db_path: untyped) -> untyped + + # ! denotes that this is an action that will be performed on the database and not a query + def stop_databases!: () -> untyped + + def save_database!: (?untyped database_index_name) -> untyped + + def save_databases!: () -> untyped + + def load_database!: (?untyped database_index_name) -> untyped + + def load_databases!: () -> untyped + + def delete_database!: (?untyped database_index_name, ?delete_files: bool) -> untyped + + def get_databases_list: () -> untyped + + alias db_list get_databases_list + + def set_new_file_archive!: (?untyped database_index_name) -> untyped + + # traverses the database and yields every element in @data_arr, even nils + def each: (untyped database_index_name, ?hash: untyped) { (untyped) -> untyped } -> (false | untyped) + + # traverses the database and returns all elements that are not nil, across all of @data_arr + def each_not_nil: (untyped database_index_name, ?hash: untyped) { (untyped) -> untyped } -> untyped +end diff --git a/lib/file_context_managed_partitioned_array_manager.rbs b/lib/file_context_managed_partitioned_array_manager.rbs index 40e7a0f..4b4dc5f 100644 --- a/lib/file_context_managed_partitioned_array_manager.rbs +++ b/lib/file_context_managed_partitioned_array_manager.rbs @@ -1,223 +1,223 @@ -# FileContextManagedPartitionedArrayManager - manages the FileContextManagedPartitionedArray and its partitions, making the Partitioned Array a database with database IDs -# and table keys -class FileContextManagedPartitionedArrayManager - @fcmpa_db_partition_archive_id: untyped - - @fcmpa_db_endless_add: untyped - - @fcmpa_db_partition_amount_and_offset: untyped - - @fcmpa_db_max_capacity: untyped - - @fcmpa_db_index_location: untyped - - @fcmpa_db_size: untyped - - @fcmpa_db_indexer_name: untyped - - @fcmpa_db_folder_name: untyped - - @fcmpa_db_partition_addition_amount: untyped - - @fcmpa_db_has_capacity: untyped - - @fcmpa_db_dynamically_allocates: untyped - - @fcmpa_db_traverse_hash: untyped - - # The database which holds all the entries that the manager database manages - @db_has_capacity: untyped - - @db_endless_add: untyped - - @db_size: untyped - - @db_path: untyped - - @db_name: untyped - - @db_dynamically_allocates: untyped - - @db_partition_amount_and_offset: untyped - - @db_max_capacity: untyped - - @db_partition_addition_amount: untyped - - @db_partition_archive_id: untyped - - @db_traverse_hash: untyped - - @initial_autosave: untyped - - @active_table: untyped - - @active_database: untyped - - @label_integer: untyped - - @label_ranges: untyped - - @traverse_hash: untyped - - # puts @db_partition_addition_amount - @timestamp_str: untyped - - # p "FCMPA: #{@label_integer}" - @man_index: untyped - - # a man_db entry for every single database table, while man_index maintains the link between the manager database and the database tables - @man_db: untyped - - attr_accessor man_db: untyped - - attr_accessor man_index: untyped - - attr_accessor fcmpa_db_indexer_db: untyped - - attr_accessor fcmpa_active_databases: untyped - - attr_accessor db_file_incrementor: untyped - - attr_accessor db_file_location: untyped - - attr_accessor db_path: untyped - - attr_accessor db_name: untyped - - attr_accessor db_size: untyped - - attr_accessor db_endless_add: untyped - - attr_accessor db_has_capacity: untyped - - attr_accessor fcmpa_db_indexer_name: untyped - - attr_accessor fcmpa_db_folder_name: untyped - - attr_accessor fcmpa_db_size: untyped - - attr_accessor fcmpa_partition_amount_and_offset: untyped - - attr_accessor db_partition_amount_and_offset: untyped - - attr_accessor partition_addition_amount: untyped - - attr_accessor db_dynamically_allocates: untyped - - attr_accessor timestamp_str: untyped - - INDEX: 0 - - # DB_SIZE > PARTITION_AMOUNT - TRAVERSE_HASH: true - - FCMPA_PARTITION_AMOUNT: 9 - - FCMPA_OFFSET: 1 - - FCMPA_DB_ENDLESS_ADD: true - - FCMPA_DB_DYNAMICALLY_ALLOCATES: true - - FCMPA_DB_PARTITION_ADDITION_AMOUNT: 5 - - FCMPA_DB_HAS_CAPACITY: true - - FCMPA_DB_MAX_CAPACITY: "data_arr_size" - - FCMPA_DB_INDEXER_NAME: "FCMPA_DB_INDEX" - - FCMPA_DB_FOLDER_NAME: "./DB/FCMPAM_DB_INDEX" - - FCMPA_DB_PARTITION_ARCHIVE_ID: 0 - - FCMPA_DB_SIZE: 20 - - FCMPA_DB_INDEX_LOCATION: 0 - - FCMPA_DB_TRAVERSE_HASH: true - - DB_PARTITION_AMOUNT: 9 - - DB_PARTITION_OFFSET: 1 - - DB_NAME: "FCMPA_DB" - - DB_PATH: "./DB/FCMPAM_DB" - - DB_HAS_CAPACITY: true - - DB_DYNAMICALLY_ALLOCATES: true - - DB_ENDLESS_ADD: true - - DB_MAX_CAPACITY: "data_arr_size" - - DB_PARTITION_ARCHIVE_ID: 0 - - DB_SIZE: 20 - - DB_PARTITION_ADDITION_AMOUNT: 5 - - DB_TRAVERSE_HASH: true - - INITIAL_AUTOSAVE: true - - DATABASE_LIST_NAME: "_DATABASE_LIST_INDEX" - - LABEL_INTEGER: false - - LABEL_RANGES: false - - def initialize: (?db_max_capacity: untyped, ?db_size: untyped, ?db_endless_add: untyped, ?db_has_capacity: untyped, ?db_name: untyped, ?db_path: untyped, ?db_partition_addition_amount: untyped, ?db_dynamically_allocates: untyped, ?db_partition_amount_and_offset: untyped, ?db_partition_archive_id: untyped, ?db_traverse_hash: untyped, ?fcmpa_db_size: untyped, ?fcmpa_db_indexer_name: untyped, ?fcmpa_db_index_location: untyped, ?fcmpa_db_folder_name: untyped, ?fcmpa_db_partition_amount_and_offset: untyped, ?fcmpa_db_has_capacity: untyped, ?fcmpa_db_partition_addition_amount: untyped, ?fcmpa_db_dynamically_allocates: untyped, ?fcmpa_db_endless_add: untyped, ?fcmpa_db_max_capacity: untyped, ?fcmpa_db_partition_archive_id: untyped, ?fcmpa_db_traverse_hash: untyped, ?initial_autosave: untyped, ?active_database: untyped?, ?active_table: untyped?, ?traverse_hash: untyped, ?label_integer: untyped, ?label_ranges: untyped) -> void - - # gets the database object for the database_name (@man_index = database index; @man_db = database table) - def database: (?untyped database_name) -> untyped - - # gets the database table object for the database_table name, not needing a database x index pair - def table: (?untyped database_table) -> untyped - - # set the active database table variable to avoid redundant typing - def active_table: (untyped database_table) -> untyped - - # set the active database variable to avoid redundant typing - def active_database: (untyped active_database) -> untyped - - def existing_database_tables?: (?database_name: untyped) -> untyped - - def []: (?untyped database_name, ?untyped database_table) -> untyped - - # gets the database table object for the database_table name, needing a database x index pair - def database_table: (?database_name: untyped, ?database_table: untyped) -> (untyped | nil) - - # Lower level work that works with class variables within fcmpa_active_databases. In particular, the MPA within @man_db.fcmpa_active_databases[database_table] - def table_set_file_context!: (?database_table: untyped, ?database_name: untyped, ?file_context_id: untyped, ?save_prior: bool, ?save_after: bool) -> untyped - - # sets the particular MPA running within the database as database_table to the next file context - # lower level work that deals with class variables within fcmpa_active_databases - def table_next_file_context!: (?database_table: untyped, ?database_name: untyped, ?save_prior: bool, ?save_after: bool) -> untyped - - # update: left off worrying about the db_table_name entry having to contain an array of the table names so that the database knows which tables to look for and which ones belong to it - # left off working with new_table, and, setting the table apart from the database and placing them into independent folders (the problem is file locations) - def new_table!: (database_table: untyped, database_name: untyped, ?initial_autosave: untyped) -> untyped - - def delete_database_index_entry!: (untyped database_name) -> untyped - - # untested (1/12/2023 - 2:07PM) - def delete_database!: (untyped database_name) -> untyped - - # untested (1/12/2023 - 2:07PM) - def delete_table!: (untyped database_table) -> untyped - - # the index is the database name, and man_db maintains the databases defined by the index - def new_database!: (untyped database_name) -> untyped - - alias db_table database_table - - alias active_db active_database - - alias new_db! new_database! - - alias new_tbl! new_table! -end +# FileContextManagedPartitionedArrayManager - manages the FileContextManagedPartitionedArray and its partitions, making the Partitioned Array a database with database IDs +# and table keys +class FileContextManagedPartitionedArrayManager + @fcmpa_db_partition_archive_id: untyped + + @fcmpa_db_endless_add: untyped + + @fcmpa_db_partition_amount_and_offset: untyped + + @fcmpa_db_max_capacity: untyped + + @fcmpa_db_index_location: untyped + + @fcmpa_db_size: untyped + + @fcmpa_db_indexer_name: untyped + + @fcmpa_db_folder_name: untyped + + @fcmpa_db_partition_addition_amount: untyped + + @fcmpa_db_has_capacity: untyped + + @fcmpa_db_dynamically_allocates: untyped + + @fcmpa_db_traverse_hash: untyped + + # The database which holds all the entries that the manager database manages + @db_has_capacity: untyped + + @db_endless_add: untyped + + @db_size: untyped + + @db_path: untyped + + @db_name: untyped + + @db_dynamically_allocates: untyped + + @db_partition_amount_and_offset: untyped + + @db_max_capacity: untyped + + @db_partition_addition_amount: untyped + + @db_partition_archive_id: untyped + + @db_traverse_hash: untyped + + @initial_autosave: untyped + + @active_table: untyped + + @active_database: untyped + + @label_integer: untyped + + @label_ranges: untyped + + @traverse_hash: untyped + + # puts @db_partition_addition_amount + @timestamp_str: untyped + + # p "FCMPA: #{@label_integer}" + @man_index: untyped + + # a man_db entry for every single database table, while man_index maintains the link between the manager database and the database tables + @man_db: untyped + + attr_accessor man_db: untyped + + attr_accessor man_index: untyped + + attr_accessor fcmpa_db_indexer_db: untyped + + attr_accessor fcmpa_active_databases: untyped + + attr_accessor db_file_incrementor: untyped + + attr_accessor db_file_location: untyped + + attr_accessor db_path: untyped + + attr_accessor db_name: untyped + + attr_accessor db_size: untyped + + attr_accessor db_endless_add: untyped + + attr_accessor db_has_capacity: untyped + + attr_accessor fcmpa_db_indexer_name: untyped + + attr_accessor fcmpa_db_folder_name: untyped + + attr_accessor fcmpa_db_size: untyped + + attr_accessor fcmpa_partition_amount_and_offset: untyped + + attr_accessor db_partition_amount_and_offset: untyped + + attr_accessor partition_addition_amount: untyped + + attr_accessor db_dynamically_allocates: untyped + + attr_accessor timestamp_str: untyped + + INDEX: 0 + + # DB_SIZE > PARTITION_AMOUNT + TRAVERSE_HASH: true + + FCMPA_PARTITION_AMOUNT: 9 + + FCMPA_OFFSET: 1 + + FCMPA_DB_ENDLESS_ADD: true + + FCMPA_DB_DYNAMICALLY_ALLOCATES: true + + FCMPA_DB_PARTITION_ADDITION_AMOUNT: 5 + + FCMPA_DB_HAS_CAPACITY: true + + FCMPA_DB_MAX_CAPACITY: "data_arr_size" + + FCMPA_DB_INDEXER_NAME: "FCMPA_DB_INDEX" + + FCMPA_DB_FOLDER_NAME: "./DB/FCMPAM_DB_INDEX" + + FCMPA_DB_PARTITION_ARCHIVE_ID: 0 + + FCMPA_DB_SIZE: 20 + + FCMPA_DB_INDEX_LOCATION: 0 + + FCMPA_DB_TRAVERSE_HASH: true + + DB_PARTITION_AMOUNT: 9 + + DB_PARTITION_OFFSET: 1 + + DB_NAME: "FCMPA_DB" + + DB_PATH: "./DB/FCMPAM_DB" + + DB_HAS_CAPACITY: true + + DB_DYNAMICALLY_ALLOCATES: true + + DB_ENDLESS_ADD: true + + DB_MAX_CAPACITY: "data_arr_size" + + DB_PARTITION_ARCHIVE_ID: 0 + + DB_SIZE: 20 + + DB_PARTITION_ADDITION_AMOUNT: 5 + + DB_TRAVERSE_HASH: true + + INITIAL_AUTOSAVE: true + + DATABASE_LIST_NAME: "_DATABASE_LIST_INDEX" + + LABEL_INTEGER: false + + LABEL_RANGES: false + + def initialize: (?db_max_capacity: untyped, ?db_size: untyped, ?db_endless_add: untyped, ?db_has_capacity: untyped, ?db_name: untyped, ?db_path: untyped, ?db_partition_addition_amount: untyped, ?db_dynamically_allocates: untyped, ?db_partition_amount_and_offset: untyped, ?db_partition_archive_id: untyped, ?db_traverse_hash: untyped, ?fcmpa_db_size: untyped, ?fcmpa_db_indexer_name: untyped, ?fcmpa_db_index_location: untyped, ?fcmpa_db_folder_name: untyped, ?fcmpa_db_partition_amount_and_offset: untyped, ?fcmpa_db_has_capacity: untyped, ?fcmpa_db_partition_addition_amount: untyped, ?fcmpa_db_dynamically_allocates: untyped, ?fcmpa_db_endless_add: untyped, ?fcmpa_db_max_capacity: untyped, ?fcmpa_db_partition_archive_id: untyped, ?fcmpa_db_traverse_hash: untyped, ?initial_autosave: untyped, ?active_database: untyped?, ?active_table: untyped?, ?traverse_hash: untyped, ?label_integer: untyped, ?label_ranges: untyped) -> void + + # gets the database object for the database_name (@man_index = database index; @man_db = database table) + def database: (?untyped database_name) -> untyped + + # gets the database table object for the database_table name, not needing a database x index pair + def table: (?untyped database_table) -> untyped + + # set the active database table variable to avoid redundant typing + def active_table: (untyped database_table) -> untyped + + # set the active database variable to avoid redundant typing + def active_database: (untyped active_database) -> untyped + + def existing_database_tables?: (?database_name: untyped) -> untyped + + def []: (?untyped database_name, ?untyped database_table) -> untyped + + # gets the database table object for the database_table name, needing a database x index pair + def database_table: (?database_name: untyped, ?database_table: untyped) -> (untyped | nil) + + # Lower level work that works with class variables within fcmpa_active_databases. In particular, the MPA within @man_db.fcmpa_active_databases[database_table] + def table_set_file_context!: (?database_table: untyped, ?database_name: untyped, ?file_context_id: untyped, ?save_prior: bool, ?save_after: bool) -> untyped + + # sets the particular MPA running within the database as database_table to the next file context + # lower level work that deals with class variables within fcmpa_active_databases + def table_next_file_context!: (?database_table: untyped, ?database_name: untyped, ?save_prior: bool, ?save_after: bool) -> untyped + + # update: left off worrying about the db_table_name entry having to contain an array of the table names so that the database knows which tables to look for and which ones belong to it + # left off working with new_table, and, setting the table apart from the database and placing them into independent folders (the problem is file locations) + def new_table!: (database_table: untyped, database_name: untyped, ?initial_autosave: untyped) -> untyped + + def delete_database_index_entry!: (untyped database_name) -> untyped + + # untested (1/12/2023 - 2:07PM) + def delete_database!: (untyped database_name) -> untyped + + # untested (1/12/2023 - 2:07PM) + def delete_table!: (untyped database_table) -> untyped + + # the index is the database name, and man_db maintains the databases defined by the index + def new_database!: (untyped database_name) -> untyped + + alias db_table database_table + + alias active_db active_database + + alias new_db! new_database! + + alias new_tbl! new_table! +end diff --git a/lib/file_methods.rb b/lib/file_methods.rb index 9bc552f..54c6572 100644 --- a/lib/file_methods.rb +++ b/lib/file_methods.rb @@ -3,9 +3,9 @@ module FileMethods DB_LIST = './db/db_list.txt' DB_DIR = './db' - def make_db(db_dir = DB_DIR) - FileUtils.mkdir_p(db_dir) - FileUtils.touch(DB_LIST) + def make_db(db_dir: '/db', db_list: '/db/db_list.txt') + FileUtils.mkdir_p("#{Dir.pwd + db_dir}") + FileUtils.touch("#{Dir.pwd + db_list}") end def read_lines(array) diff --git a/lib/file_methods.rbs b/lib/file_methods.rbs index 31d2ba6..536598b 100644 --- a/lib/file_methods.rbs +++ b/lib/file_methods.rbs @@ -1,23 +1,23 @@ -module FileMethods - DB_LIST: "./db/db_list.txt" - - DB_DIR: "./db" - - def make_db: (?untyped db_dir) -> untyped - - def read_lines: (untyped array) -> untyped - - def read_multi_string: (untyped string) -> untyped - - def read_file_lines: (?untyped file_name) -> untyped - - def write_line: (untyped line, ?untyped filename) -> (nil | untyped) - - def remove_line: (untyped line, ?untyped filename) -> untyped - - def check_file_duplicates: (untyped check, ?untyped filename) -> untyped - - def array_duplicates?: (untyped check, untyped array_of_strings) -> untyped - - def write_lines: (untyped lines_array, ?untyped filename) -> untyped -end +module FileMethods + DB_LIST: "./db/db_list.txt" + + DB_DIR: "./db" + + def make_db: (?untyped db_dir) -> untyped + + def read_lines: (untyped array) -> untyped + + def read_multi_string: (untyped string) -> untyped + + def read_file_lines: (?untyped file_name) -> untyped + + def write_line: (untyped line, ?untyped filename) -> (nil | untyped) + + def remove_line: (untyped line, ?untyped filename) -> untyped + + def check_file_duplicates: (untyped check, ?untyped filename) -> untyped + + def array_duplicates?: (untyped check, untyped array_of_strings) -> untyped + + def write_lines: (untyped lines_array, ?untyped filename) -> untyped +end diff --git a/lib/line_database.rbs b/lib/line_database.rbs index 714a6c7..2e111d3 100644 --- a/lib/line_database.rbs +++ b/lib/line_database.rbs @@ -1,6 +1,6 @@ -# VERSION 1.0.0-release - First release -PARENT_FOLDER: "./PAD_db" - -class Object - def load_pad: (?parent_folder: untyped) -> untyped -end +# VERSION 1.0.0-release - First release +PARENT_FOLDER: "./PAD_db" + +class Object + def load_pad: (?parent_folder: untyped) -> untyped +end diff --git a/lib/line_db.rbs b/lib/line_db.rbs index d33dd2b..c874167 100644 --- a/lib/line_db.rbs +++ b/lib/line_db.rbs @@ -1,185 +1,185 @@ -# LineDB: Line Database - Create a database of PartitionedArrayDatabase objects; -class LineDB - @label_integer: untyped - - @label_ranges: untyped - - @parent_folder: untyped - - @database_folder_name: untyped - - @database_file_name: untyped - - @endless_add: untyped - - @database_partiton_amount: untyped - - @has_capacity: untyped - - @db_size: untyped - - @dynamically_allocates: untyped - - @traverse_hash: untyped - - @linelist: untyped - - @lambda_list: untyped - - @active_database: untyped - - attr_accessor parent_folder: untyped - - attr_accessor database_folder_name: untyped - - attr_accessor linelist: untyped - - attr_accessor database_file_name: untyped - - attr_accessor endless_add: untyped - - attr_accessor has_capacity: untyped - - attr_accessor db_size: untyped - - attr_accessor dynamically_allocates: untyped - - attr_accessor partition_amount: untyped - - attr_accessor traverse_hash: untyped - - include FileMethods - - PAD: untyped - - # # Fallback Constants; a database folder and a db_list.txt file in the database folder must be present. ### - # # db_list.txt must contain line separated sets of database names (see "lib/line_database_setup.rb") ### - PARENT_FOLDER: "./db/CGMFS_db" - - DATABASE_FOLDER_NAME: "db" - - DATABASE_FILE_NAME: "./db/db_list.txt" - - # # Suggested Constants ### - ENDLESS_ADD: true - - HAS_CAPACITY: true - - DATABASE_SIZE: 100 - - DYNAMICALLY_ALLOCATES: true - - DATABASE_PARTITION_AMOUNT: 20 - - TRAVERSE_HASH: true - - LABEL_INTEGER: false - - LABEL_RANGES: false - - # Initializes a new instance of the LineDB class. - # - # @param label_integer [Boolean] Whether to label integers. - # @param label_ranges [Boolean] Whether to label ranges. - # @param traverse_hash [Boolean] Whether to traverse the hash. - # @param database_partition_amount [Integer] The number of partitions in the database. - # @param database_file_name [String] The name of the database file. - # @param endless_add [Boolean] Whether to allow endless adding to the database. - # @param has_capacity [Boolean] Whether the database has a capacity. - # @param db_size [Integer] The size of the database. - # @param dynamically_allocates [Boolean] Whether the database dynamically allocates memory. - # @param parent_folder [String] The parent folder of the database. - # @param database_folder_name [String] The name of the database folder. - def initialize: (?label_integer: untyped, ?label_ranges: untyped, ?traverse_hash: untyped, ?database_partition_amount: untyped, ?database_file_name: untyped, ?endless_add: untyped, ?has_capacity: untyped, ?db_size: untyped, ?dynamically_allocates: untyped, ?parent_folder: untyped, ?database_folder_name: untyped) -> void - - # Adds an element to the database starting from the left hand side, and skipping over nils. - def lhs_add: () -> nil - - # Adds an element to the database starting from the right hand side, and skipping over nils going from left to right. - def rhs_add: () -> nil - - # Sets a subelement in a partition to nil. - # - # @param partition_number [Integer] The partition number. - # @param subelement_index [Integer] The subelement index. - # @return [Boolean] True if successful, false otherwise. - def nillize_partition_subelement!: (untyped partition_number, untyped subelement_index) -> (true | false) - - # Makes a partition and its subelements revenant. - # - # @param partition_number [Integer] The partition number. - # @return [Boolean] True if successful, false otherwise. - def revenant_partition!: (untyped partition_number) -> (true | false) - - # Deletes a partition and sets all its subelements to nil. - # - # @param partition_number [Integer] The partition number. - # @return [Boolean] True if successful, false otherwise. - def kill_partition!: (untyped partition_number) -> (true | false) - - # Sets the active database. - # - # @param db_name [String] The name of the database. - def active_database=: (untyped db_name) -> untyped - - # Checks if there is an active database. - # - # @return [PartitionedArrayDatabase, false] The active database if exists, false otherwise. - def active_database?: () -> (untyped | false) - - # Returns a list of active databases. - # - # @return [Array] The list of active databases. - def list_databases: () -> untyped - - def databases_list: () -> nil - - # Returns a list of databases. - # - # @return [Array] The list of databases. - def databases: () -> untyped - - # Updates the list of databases. - def update_databases: () -> untyped - - # Returns the specified databases. - # - # @param db_names [Array] The names of the databases. - # @return [PartitionedArrayDatabase, Array] The specified databases. - def []: (*untyped db_names) -> untyped - - # Reloads the database. - def reload: () -> untyped - - # Returns the specified database. - # - # @param db_name [String] The name of the database. - # @return [PartitionedArrayDatabase] The specified database. - def db: (untyped db_name) -> untyped - - # Removes a database. - # - # @param db_name [String] The name of the database. - def remove_db!: (untyped db_name) -> untyped - - # Deletes a database. - # - # @param db_name [String] The name of the database. - def delete_db!: (untyped db_name) -> untyped - - # Adds a database. - # - # @param db_name [String] The name of the database. - def add_db!: (untyped db_name) -> untyped - - # ## Low level methods (but we don't enforce it lol, because they can still have their uses) #### - # private - def load_pad_single: (untyped db_name, ?parent_folder: untyped) -> untyped - - def remove_pad_single: (untyped db_name) -> untyped - - # TODO: implement rm_rf for dragonruby on windows and linux, and maybe android - def delete_pad_single: (untyped db_name) -> untyped - - def load_pad: (?parent_folder: untyped) -> untyped -end +# LineDB: Line Database - Create a database of PartitionedArrayDatabase objects; +class LineDB + @label_integer: untyped + + @label_ranges: untyped + + @parent_folder: untyped + + @database_folder_name: untyped + + @database_file_name: untyped + + @endless_add: untyped + + @database_partiton_amount: untyped + + @has_capacity: untyped + + @db_size: untyped + + @dynamically_allocates: untyped + + @traverse_hash: untyped + + @linelist: untyped + + @lambda_list: untyped + + @active_database: untyped + + attr_accessor parent_folder: untyped + + attr_accessor database_folder_name: untyped + + attr_accessor linelist: untyped + + attr_accessor database_file_name: untyped + + attr_accessor endless_add: untyped + + attr_accessor has_capacity: untyped + + attr_accessor db_size: untyped + + attr_accessor dynamically_allocates: untyped + + attr_accessor partition_amount: untyped + + attr_accessor traverse_hash: untyped + + include FileMethods + + PAD: untyped + + # # Fallback Constants; a database folder and a db_list.txt file in the database folder must be present. ### + # # db_list.txt must contain line separated sets of database names (see "lib/line_database_setup.rb") ### + PARENT_FOLDER: "./db/CGMFS_db" + + DATABASE_FOLDER_NAME: "db" + + DATABASE_FILE_NAME: "./db/db_list.txt" + + # # Suggested Constants ### + ENDLESS_ADD: true + + HAS_CAPACITY: true + + DATABASE_SIZE: 100 + + DYNAMICALLY_ALLOCATES: true + + DATABASE_PARTITION_AMOUNT: 20 + + TRAVERSE_HASH: true + + LABEL_INTEGER: false + + LABEL_RANGES: false + + # Initializes a new instance of the LineDB class. + # + # @param label_integer [Boolean] Whether to label integers. + # @param label_ranges [Boolean] Whether to label ranges. + # @param traverse_hash [Boolean] Whether to traverse the hash. + # @param database_partition_amount [Integer] The number of partitions in the database. + # @param database_file_name [String] The name of the database file. + # @param endless_add [Boolean] Whether to allow endless adding to the database. + # @param has_capacity [Boolean] Whether the database has a capacity. + # @param db_size [Integer] The size of the database. + # @param dynamically_allocates [Boolean] Whether the database dynamically allocates memory. + # @param parent_folder [String] The parent folder of the database. + # @param database_folder_name [String] The name of the database folder. + def initialize: (?label_integer: untyped, ?label_ranges: untyped, ?traverse_hash: untyped, ?database_partition_amount: untyped, ?database_file_name: untyped, ?endless_add: untyped, ?has_capacity: untyped, ?db_size: untyped, ?dynamically_allocates: untyped, ?parent_folder: untyped, ?database_folder_name: untyped) -> void + + # Adds an element to the database starting from the left hand side, and skipping over nils. + def lhs_add: () -> nil + + # Adds an element to the database starting from the right hand side, and skipping over nils going from left to right. + def rhs_add: () -> nil + + # Sets a subelement in a partition to nil. + # + # @param partition_number [Integer] The partition number. + # @param subelement_index [Integer] The subelement index. + # @return [Boolean] True if successful, false otherwise. + def nillize_partition_subelement!: (untyped partition_number, untyped subelement_index) -> (true | false) + + # Makes a partition and its subelements revenant. + # + # @param partition_number [Integer] The partition number. + # @return [Boolean] True if successful, false otherwise. + def revenant_partition!: (untyped partition_number) -> (true | false) + + # Deletes a partition and sets all its subelements to nil. + # + # @param partition_number [Integer] The partition number. + # @return [Boolean] True if successful, false otherwise. + def kill_partition!: (untyped partition_number) -> (true | false) + + # Sets the active database. + # + # @param db_name [String] The name of the database. + def active_database=: (untyped db_name) -> untyped + + # Checks if there is an active database. + # + # @return [PartitionedArrayDatabase, false] The active database if exists, false otherwise. + def active_database?: () -> (untyped | false) + + # Returns a list of active databases. + # + # @return [Array] The list of active databases. + def list_databases: () -> untyped + + def databases_list: () -> nil + + # Returns a list of databases. + # + # @return [Array] The list of databases. + def databases: () -> untyped + + # Updates the list of databases. + def update_databases: () -> untyped + + # Returns the specified databases. + # + # @param db_names [Array] The names of the databases. + # @return [PartitionedArrayDatabase, Array] The specified databases. + def []: (*untyped db_names) -> untyped + + # Reloads the database. + def reload: () -> untyped + + # Returns the specified database. + # + # @param db_name [String] The name of the database. + # @return [PartitionedArrayDatabase] The specified database. + def db: (untyped db_name) -> untyped + + # Removes a database. + # + # @param db_name [String] The name of the database. + def remove_db!: (untyped db_name) -> untyped + + # Deletes a database. + # + # @param db_name [String] The name of the database. + def delete_db!: (untyped db_name) -> untyped + + # Adds a database. + # + # @param db_name [String] The name of the database. + def add_db!: (untyped db_name) -> untyped + + # ## Low level methods (but we don't enforce it lol, because they can still have their uses) #### + # private + def load_pad_single: (untyped db_name, ?parent_folder: untyped) -> untyped + + def remove_pad_single: (untyped db_name) -> untyped + + # TODO: implement rm_rf for dragonruby on windows and linux, and maybe android + def delete_pad_single: (untyped db_name) -> untyped + + def load_pad: (?parent_folder: untyped) -> untyped +end diff --git a/lib/managed_partitioned_array.rbs b/lib/managed_partitioned_array.rbs index ea5d473..26a9b61 100644 --- a/lib/managed_partitioned_array.rbs +++ b/lib/managed_partitioned_array.rbs @@ -1,241 +1,241 @@ -# VERSION v1.3.0a - endless add implementation in ManagedPartitionedArray#endless_add -# Allows the database to continuously add and allocate, as if it were a plain PartitionedArray -# NOTE: consider the idea that adding a partition may mean that it saves all variables and everything to disk -# SUGGESTION: working this out by only saving by partition, and not by the whole database -# SUGGESTION: this would mean that the database would have to be loaded from the beginning, but that's not a big deal -# VERSION v1.2.9 - fixes -# VERSION v1.2.8 - Regression and bug found and fixed; too many guard clauses at at_capacity? method (10/1/2022) -# VERSION v1.2.7 - fixed more bugs with dynamic allocation -# VERSION v1.2.6 - bug fix with array allocation given that you don't want to create file partitions (9/27/2022 - 7:09AM) -# VERSION v1.2.5 - bug fixes -# VERSION: v1.1.4 - seemed to have sorted out the file issues... but still need to test it out -# VERSION: v1.1.3 -# many bug fixes; variables are now in sync with file i/o -# Stopped: problem with @latest_id not being set correctly -# working on load_from_archive! and how it will get the latest archive database and load up all the variables, only it seems -# that there is something wrong with that. -# VERSION: v1.1.2 (9/13/2022 10:17 am) -# Prettified -# Added raise to ManagedPartitionedArray#at_capacity? if @has_capacity == false -# VERSION: v1.1.1a - got rid of class variables, and things seem to be fully working now -# VERSION: v1.1.0a - fully functional, but not yet tested--test in the real world -# VERSION: v1.0.2 - more bug fixes, added a few more tests -# VERSION: v1.0.1 - bug fixes -# VERSION: v1.0.0a - fully functional at a basic level; can load archives and the max id of the archive is always available -# VERSION: v0.1.3 - file saving and other methods properly working -# VERSION: v0.1.2 - added managed databases, but need to add more logic to make it work fully -# VERSION: v0.1.1 -# UPDATE 8/31/2022: @latest_id increments correctly now -# Manages the @last_entry variable, which tracks where the latest entry is, since PartitionedArray is dynamically allocated. -class ManagedPartitionedArray < PartitionedArray - @db_path: untyped - - @db_size: untyped - - @db_name: untyped - - @partition_amount_and_offset: untyped - - @partition_archive_id: untyped - - @original_db_name: untyped - - @db_name_with_archive: untyped - - @max_capacity: untyped - - @latest_id: untyped - - # @max_capacity = max_capacity_setup! # => commented out on 10/4/2022 1:32am - @has_capacity: untyped - - @max_partition_archive_id: untyped - - @partition_addition_amount: untyped - - @dynamically_allocates: untyped - - @endless_add: untyped - - @label_integer: untyped - - @label_ranges: untyped - - @basedir_created: untyped - - attr_accessor endless_add: untyped - - attr_accessor range_arr: untyped - - attr_accessor rel_arr: untyped - - attr_accessor db_size: untyped - - attr_accessor data_arr: untyped - - attr_accessor partition_amount_and_offset: untyped - - attr_accessor db_path: untyped - - attr_accessor db_name: untyped - - attr_accessor max_capacity: untyped - - attr_accessor has_capacity: untyped - - attr_accessor latest_id: untyped - - attr_accessor partition_archive_id: untyped - - attr_accessor max_partition_archive_id: untyped - - attr_accessor db_name_with_no_archive: untyped - - # DB_SIZE > PARTITION_AMOUNT - PARTITION_AMOUNT: 9 - - OFFSET: 1 - - DB_SIZE: 20 - - PARTITION_ARCHIVE_ID: 0 - - DEFAULT_PATH: "./DB_TEST" - - DEBUGGING: false - - PAUSE_DEBUG: false - - DB_NAME: "partitioned_array_slice" - - PARTITION_ADDITION_AMOUNT: 1 - - MAX_CAPACITY: "data_arr_size" - - HAS_CAPACITY: true - - DYNAMICALLY_ALLOCATES: true - - ENDLESS_ADD: false - - LABEL_INTEGER: false - - LABEL_RANGES: false - - def initialize: (?label_integer: untyped, ?label_ranges: untyped, ?endless_add: untyped, ?dynamically_allocates: untyped, ?partition_addition_amount: untyped, ?max_capacity: untyped, ?has_capacity: untyped, ?partition_archive_id: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped) -> void - - # iterates @data_arr - def iterate: (?yield_id: bool) { (untyped, untyped) -> untyped } -> untyped - - def rehasher!: () -> untyped - - def save_partition_by_id_to_file!: (untyped id) -> untyped - - # probably a shallow copy if the hashes go too deep - def replicate: () -> untyped - - # full copy but - def replicate2: () -> untyped - - # untested - 5/8/2024 9:00AM PST -07 - def get_partition_by_id: (untyped id) -> untyped - - # be sure to add to dragonruby CODE - # changed 2/19/2023 11:04AM - def save_everything_to_files!: () -> untyped - - # added 2/19/2023 - # be sure to add to dragonruby CODE - def create_basedir_once!: () -> (untyped | nil) - - # added 2/19/2023 - # be sure tto add to dragonruby library - def create_basedir!: () -> untyped - - # changes added on 2/19/2023 11:03AM - def initialize_max_partition_archive_id2!: () -> untyped - - def initialize_max_partition_archive_id!: () -> untyped - - # one keyword available: :data_arr_size - def max_capacity_setup!: () -> ("data_arr_size" | untyped) - - def save_data_arr_to_disk!: () -> nil - - def dump_all_variables: () -> untyped - - # Bug fixed 2/19/2023 - removed partition_archive_id from arguments - def archive_and_new_db!: (?label_integer: untyped, ?label_ranges: untyped, ?auto_allocate: bool, ?partition_addition_amount: untyped, ?max_capacity: untyped, ?has_capacity: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped) -> untyped - - # Bugs fixed in 2 below functions - # ex ManagedPartitionedArray#load_archive_no_auto_allocate!(partition_archive_id: partition_archive_id, ...) - def load_archive_no_auto_allocate!: (?label_integer: untyped, ?label_ranges: untyped, ?has_capacity: untyped, ?dynamically_allocates: untyped, ?endless_add: untyped, ?partition_archive_id: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped, ?max_capacity: untyped, ?partition_addition_amount: untyped) -> untyped - - # Bug Fixed below; be sure to add to dragonruby - # ex ManagedPartitionedArray#load_from_archive!(partition_archive_id: partition_archive_id, ...) - def load_from_archive!: (?label_integer: untyped, ?label_ranges: untyped, ?has_capacity: untyped, ?dynamically_allocates: untyped, ?endless_add: untyped, ?partition_archive_id: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped, ?max_capacity: untyped, ?partition_addition_amount: untyped) -> untyped - - # Added 2/19/2023 (FileUtils.mkdir_p); this is a bug fix--add to dragonruby - def save_last_entry_to_file!: () -> untyped - - # ### label_integer and label_ranges v2.1.1 changes end here - # ManagedPartitionedArray#at_capacity? checks to see if the partitioned array is at its capacity. It is imperative to use this when going through an iterator. - def at_capacity?: () -> (false | untyped) - - # added 3/11/2024 10:11PM - def add_at_last: (?return_added_element_id: bool, ?save_on_partition_add: bool, ?save_last_entry_to_file: bool) ?{ (?) -> untyped } -> untyped - - # runtime complexity: O(n) - def add: (?return_added_element_id: bool, ?save_on_partition_add: bool, ?save_last_entry_to_file: bool) { (?) -> untyped } -> (false | untyped) - - def load_everything_from_files!: () -> untyped - - def load_partition_from_file!: (untyped partition_id, ?load_variables: bool) -> untyped - - # save_variables: added 10/21/2022; all variables saved/loaded to disk by default, to work around an issue where I didn't realize I needed to save variables - def save_partition_to_file!: (untyped partition_id, ?save_variables: bool) -> untyped - - def save_variables_to_disk!: () -> untyped - - def load_variables_from_disk!: () -> untyped - - def increment_max_partition_archive_id!: () -> untyped - - def save_db_name_with_no_archive_to_file!: () -> untyped - - def load_db_name_with_no_archive_from_file!: () -> untyped - - def save_has_capacity_to_file!: () -> untyped - - def save_endless_add_to_file!: () -> untyped - - def load_endless_add_from_file!: () -> untyped - - def load_has_capacity_from_file!: () -> untyped - - def load_last_entry_from_file!: () -> untyped - - def save_partition_archive_id_to_file!: () -> untyped - - def load_partition_archive_id_from_file!: () -> untyped - - def save_max_partition_archive_id_to_file!: () -> untyped - - def load_max_partition_archive_id_from_file!: () -> (untyped | 0) - - def save_max_capacity_to_file!: () -> untyped - - def load_max_capacity_from_file!: () -> untyped - - def save_partition_addition_amount_to_file!: () -> untyped - - def load_partition_addition_amount_from_file!: () -> untyped - - def db_name_with_archive: (?db_name: untyped, ?partition_archive_id: untyped) -> ::String - - def strip_archived_db_name: (?db_name: untyped) -> untyped - - alias save! save_everything_to_files! - - alias load! load_everything_from_files! -end +# VERSION v1.3.0a - endless add implementation in ManagedPartitionedArray#endless_add +# Allows the database to continuously add and allocate, as if it were a plain PartitionedArray +# NOTE: consider the idea that adding a partition may mean that it saves all variables and everything to disk +# SUGGESTION: working this out by only saving by partition, and not by the whole database +# SUGGESTION: this would mean that the database would have to be loaded from the beginning, but that's not a big deal +# VERSION v1.2.9 - fixes +# VERSION v1.2.8 - Regression and bug found and fixed; too many guard clauses at at_capacity? method (10/1/2022) +# VERSION v1.2.7 - fixed more bugs with dynamic allocation +# VERSION v1.2.6 - bug fix with array allocation given that you don't want to create file partitions (9/27/2022 - 7:09AM) +# VERSION v1.2.5 - bug fixes +# VERSION: v1.1.4 - seemed to have sorted out the file issues... but still need to test it out +# VERSION: v1.1.3 +# many bug fixes; variables are now in sync with file i/o +# Stopped: problem with @latest_id not being set correctly +# working on load_from_archive! and how it will get the latest archive database and load up all the variables, only it seems +# that there is something wrong with that. +# VERSION: v1.1.2 (9/13/2022 10:17 am) +# Prettified +# Added raise to ManagedPartitionedArray#at_capacity? if @has_capacity == false +# VERSION: v1.1.1a - got rid of class variables, and things seem to be fully working now +# VERSION: v1.1.0a - fully functional, but not yet tested--test in the real world +# VERSION: v1.0.2 - more bug fixes, added a few more tests +# VERSION: v1.0.1 - bug fixes +# VERSION: v1.0.0a - fully functional at a basic level; can load archives and the max id of the archive is always available +# VERSION: v0.1.3 - file saving and other methods properly working +# VERSION: v0.1.2 - added managed databases, but need to add more logic to make it work fully +# VERSION: v0.1.1 +# UPDATE 8/31/2022: @latest_id increments correctly now +# Manages the @last_entry variable, which tracks where the latest entry is, since PartitionedArray is dynamically allocated. +class ManagedPartitionedArray < PartitionedArray + @db_path: untyped + + @db_size: untyped + + @db_name: untyped + + @partition_amount_and_offset: untyped + + @partition_archive_id: untyped + + @original_db_name: untyped + + @db_name_with_archive: untyped + + @max_capacity: untyped + + @latest_id: untyped + + # @max_capacity = max_capacity_setup! # => commented out on 10/4/2022 1:32am + @has_capacity: untyped + + @max_partition_archive_id: untyped + + @partition_addition_amount: untyped + + @dynamically_allocates: untyped + + @endless_add: untyped + + @label_integer: untyped + + @label_ranges: untyped + + @basedir_created: untyped + + attr_accessor endless_add: untyped + + attr_accessor range_arr: untyped + + attr_accessor rel_arr: untyped + + attr_accessor db_size: untyped + + attr_accessor data_arr: untyped + + attr_accessor partition_amount_and_offset: untyped + + attr_accessor db_path: untyped + + attr_accessor db_name: untyped + + attr_accessor max_capacity: untyped + + attr_accessor has_capacity: untyped + + attr_accessor latest_id: untyped + + attr_accessor partition_archive_id: untyped + + attr_accessor max_partition_archive_id: untyped + + attr_accessor db_name_with_no_archive: untyped + + # DB_SIZE > PARTITION_AMOUNT + PARTITION_AMOUNT: 9 + + OFFSET: 1 + + DB_SIZE: 20 + + PARTITION_ARCHIVE_ID: 0 + + DEFAULT_PATH: "./DB_TEST" + + DEBUGGING: false + + PAUSE_DEBUG: false + + DB_NAME: "partitioned_array_slice" + + PARTITION_ADDITION_AMOUNT: 1 + + MAX_CAPACITY: "data_arr_size" + + HAS_CAPACITY: true + + DYNAMICALLY_ALLOCATES: true + + ENDLESS_ADD: false + + LABEL_INTEGER: false + + LABEL_RANGES: false + + def initialize: (?label_integer: untyped, ?label_ranges: untyped, ?endless_add: untyped, ?dynamically_allocates: untyped, ?partition_addition_amount: untyped, ?max_capacity: untyped, ?has_capacity: untyped, ?partition_archive_id: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped) -> void + + # iterates @data_arr + def iterate: (?yield_id: bool) { (untyped, untyped) -> untyped } -> untyped + + def rehasher!: () -> untyped + + def save_partition_by_id_to_file!: (untyped id) -> untyped + + # probably a shallow copy if the hashes go too deep + def replicate: () -> untyped + + # full copy but + def replicate2: () -> untyped + + # untested - 5/8/2024 9:00AM PST -07 + def get_partition_by_id: (untyped id) -> untyped + + # be sure to add to dragonruby CODE + # changed 2/19/2023 11:04AM + def save_everything_to_files!: () -> untyped + + # added 2/19/2023 + # be sure to add to dragonruby CODE + def create_basedir_once!: () -> (untyped | nil) + + # added 2/19/2023 + # be sure tto add to dragonruby library + def create_basedir!: () -> untyped + + # changes added on 2/19/2023 11:03AM + def initialize_max_partition_archive_id2!: () -> untyped + + def initialize_max_partition_archive_id!: () -> untyped + + # one keyword available: :data_arr_size + def max_capacity_setup!: () -> ("data_arr_size" | untyped) + + def save_data_arr_to_disk!: () -> nil + + def dump_all_variables: () -> untyped + + # Bug fixed 2/19/2023 - removed partition_archive_id from arguments + def archive_and_new_db!: (?label_integer: untyped, ?label_ranges: untyped, ?auto_allocate: bool, ?partition_addition_amount: untyped, ?max_capacity: untyped, ?has_capacity: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped) -> untyped + + # Bugs fixed in 2 below functions + # ex ManagedPartitionedArray#load_archive_no_auto_allocate!(partition_archive_id: partition_archive_id, ...) + def load_archive_no_auto_allocate!: (?label_integer: untyped, ?label_ranges: untyped, ?has_capacity: untyped, ?dynamically_allocates: untyped, ?endless_add: untyped, ?partition_archive_id: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped, ?max_capacity: untyped, ?partition_addition_amount: untyped) -> untyped + + # Bug Fixed below; be sure to add to dragonruby + # ex ManagedPartitionedArray#load_from_archive!(partition_archive_id: partition_archive_id, ...) + def load_from_archive!: (?label_integer: untyped, ?label_ranges: untyped, ?has_capacity: untyped, ?dynamically_allocates: untyped, ?endless_add: untyped, ?partition_archive_id: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped, ?max_capacity: untyped, ?partition_addition_amount: untyped) -> untyped + + # Added 2/19/2023 (FileUtils.mkdir_p); this is a bug fix--add to dragonruby + def save_last_entry_to_file!: () -> untyped + + # ### label_integer and label_ranges v2.1.1 changes end here + # ManagedPartitionedArray#at_capacity? checks to see if the partitioned array is at its capacity. It is imperative to use this when going through an iterator. + def at_capacity?: () -> (false | untyped) + + # added 3/11/2024 10:11PM + def add_at_last: (?return_added_element_id: bool, ?save_on_partition_add: bool, ?save_last_entry_to_file: bool) ?{ (?) -> untyped } -> untyped + + # runtime complexity: O(n) + def add: (?return_added_element_id: bool, ?save_on_partition_add: bool, ?save_last_entry_to_file: bool) { (?) -> untyped } -> (false | untyped) + + def load_everything_from_files!: () -> untyped + + def load_partition_from_file!: (untyped partition_id, ?load_variables: bool) -> untyped + + # save_variables: added 10/21/2022; all variables saved/loaded to disk by default, to work around an issue where I didn't realize I needed to save variables + def save_partition_to_file!: (untyped partition_id, ?save_variables: bool) -> untyped + + def save_variables_to_disk!: () -> untyped + + def load_variables_from_disk!: () -> untyped + + def increment_max_partition_archive_id!: () -> untyped + + def save_db_name_with_no_archive_to_file!: () -> untyped + + def load_db_name_with_no_archive_from_file!: () -> untyped + + def save_has_capacity_to_file!: () -> untyped + + def save_endless_add_to_file!: () -> untyped + + def load_endless_add_from_file!: () -> untyped + + def load_has_capacity_from_file!: () -> untyped + + def load_last_entry_from_file!: () -> untyped + + def save_partition_archive_id_to_file!: () -> untyped + + def load_partition_archive_id_from_file!: () -> untyped + + def save_max_partition_archive_id_to_file!: () -> untyped + + def load_max_partition_archive_id_from_file!: () -> (untyped | 0) + + def save_max_capacity_to_file!: () -> untyped + + def load_max_capacity_from_file!: () -> untyped + + def save_partition_addition_amount_to_file!: () -> untyped + + def load_partition_addition_amount_from_file!: () -> untyped + + def db_name_with_archive: (?db_name: untyped, ?partition_archive_id: untyped) -> ::String + + def strip_archived_db_name: (?db_name: untyped) -> untyped + + alias save! save_everything_to_files! + + alias load! load_everything_from_files! +end diff --git a/lib/partitioned_array.rb b/lib/partitioned_array.rb index e578c79..ec8a66f 100644 --- a/lib/partitioned_array.rb +++ b/lib/partitioned_array.rb @@ -1,663 +1,663 @@ -# rubocop:disable Style/MutableConstant, Metrics/ParameterLists, Style/ParenthesesAroundCondition, Style/NegatedIf, Lint/UnusedBlockArgument, Lint/NonAtomicFileOperation, Style/FileWrite, Style/HashSyntax, Style/IfInsideElse, Style/RedundantReturn, Style/BlockComments, Style/SymbolProc, Style/FrozenStringLiteralComment -# rubocop:disable Style/GuardClause -# rubocop:disable Style/ConditionalAssignment -# rubocop:disable Style/StringLiterals -# rubocop:disable Metrics/ClassLength -# rubocop:disable Style/UnlessElse -# rubocop:disable Metrics/PerceivedComplexity -# rubocop:disable Metrics/CyclomaticComplexity -# rubocop:disable Metrics/AbcSize -# rubocop:disable Metrics/MethodLength -# rubocop:disable Style/IfUnlessModifier -# rubocop:disable Layout/LineLength -# Q: how do I fix git conflits with git commands? -# A: git checkout --ours lib/partitioned_array/lib/partitioned_array.rb - - -# CHANGELOG -# VERSION v4.0.4-release -# VERSION v4.0.3-release - prettify linter -# VERSION v4.0.2-release - 2023 - July - 4 ("Black Summer, Half Moon") [07/04/2023] - MAJOR UPDATE! -# MAJOR BUG FIXES: -# fixed another `gets` debug-bug in PartitionedArray -# added add_nosave to PartitionedArray -# add_nosave: adds an element to @data_arr, but does not save the partition to file (compare to add's bug fix) - -# VERSION v4.0.1-release - 2023 - June - 21 ("Simmer Down Solstice") [06/21/2023] - MAJOR UPDATE! -# MAJOR BUG FIXES: -# PartitionedArray#set(id, value, hash: true or false) -## Fixed a fundamentaa flaw caused by a bug where it was looking at `@db_size` instead of `@data_arr - 1` -# NOTICE: Partitioned Array and LineDB should be fully functional now, as in battle tested and a confirmed bug that -# was causing the data to not be #set or overwritten has been fixed. - -# VERSION v4.0.0-release - 5/9/2023 -# * fixed delete_partition!, aka kill_partition! - 2023-05-09 5:00PM -# * binary space partitioning now works correctly, or in the first place -# * use PartitionedArray#kill_partition! or PartitionedArray#delete_partition! to flip the bit of the partition to 0 -# * added a few more tests (revenant_partitions.rb) -# TODO: potential cleanup -# QUICK NOTES: -# * PartitionedArray#delete_partition! and PartitionedArray#kill_partition! are the same thing -# use the delete methods to remove a partition from the database in memory; -# be fully aware that if that partition was saved onto disk (.json), you can load it back -# up with ParititionedArray#load_partition_from_file! - - -# VERSION v3.0.0-release - 5/5/2023 -# * switched code around so that add only saves the partition of which the id is within: add(id, hash: false) - 5/5/2023 -# * see additional changes in ManagedPartitionedArray v3.0.0-release -# NOTE: dr-version has been synced with this version -# VERSION v2.0.1-release - remove :last and :first in favor of [:all].last and [:all].first; update in DRAGONRUBY ACCORDINGLY (2/7/2023 11:14AM) -# VERSION v2.0.0-release - add :all :last :first keywords to [] method, unrefined and untested -# VERSION v1.2.4-release - cleanup puts in add_partition -# VERSION v1.2.3-release - @label_integer and @label_ranges, sync with line_db, etc -# VERSION v1.2.2-release - @label_integer and @label_ranges -# VERSION v1.3.1-release -# VERSION v1.3.0-release - 1/13/2023 - 1:39AM -# VERSION v1.2.9-release - [](id ...) - accepts ranges, and integers as an argument splat -### Functionality: label the ranges and/or the integer values in the [] arguments with a hash argument -### --with keys pointing to the get values (for integers and/or ranges) -### label_ranges: true -### label_integer: true -# VERSION v1.2.8 - [](id, hash: false) - 1/13/2023 -# VERSION: v1.2.7 - implemented [](id) - uses get(id, hash: false) - 1/13/2023 -# VERSION: v1.2.6 - cleanup; plan on being able to save @data_arr to file directly (not by partitions) - 10/19/2022 2:03PM -# VERSION v1.2.5a - endless add implementation in ManagedPartitionedArray#endless_add -# Allows the database to continuously add and allocate, as if it were a plain PartitionedArray -# VERSION v1.2.4 - now its not "data_arr_size" if you set the variable correctly -# Note: The struct may require more work, but this struct in particular is not necessary in the managed partitioned array -# -- too many allocations are done when 'at capacity', but its because of the nature of the beast -# VERSION v1.2.4 - fixed a bug with allocations, also modified in ManagedPartitionedArray -# VERSION v1.2.3 - fully functional in tandem with ManagedPartitionedArray; added a few more tests (9/13/2022 - 5:48am) -# VERSION v1.2.2 - after adding partitions, every partition is saved to reflect the new changes on disk -# VERSION v1.2.1 - fixed PartitionedArray#get(id, &block) bugs -# - file cleanup -# VERSION v1.2.0 - Cleanup according to rubocop and solargraph linter; clarification in places added (8/11/2022 - 6:01am) -# VERSION v1.1.1 - Documenting code and scanning for bugs and any version wrap-ups -# - v1.1.2 - PartitionedArray#add(return_added_element_id: true, &block): return element id of the location of the addition -# VERSION v1.1.0a (8/11/2022 - 5:11am) -# -- PartitionedArray#add_partition now works correctly. -# Various bug fixes that lead to array variable inconsistencies - -# VERSION v1.0.5a (7/30/2022) -# -- So far the partitioned array works with allocation of partitions, saving to files, and loading from files. -# PROBLEM/BUG: PartitionedArray#add_partition does not allocate and manage the variables correctly -# IT: upon the need to add a new partition, it adds but the entry is added to the end of the @data_arr -# partition, extending its length by one which is not what we want. -# VERSION v1.0.5 (7/30/2022) -# More bug fixes, notable in saving and loading data from files. -# Example: - -# partition0: [{"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}] -# partition1: [{"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World", :log=>"Hello World"}] -# partition2: [{}, {}, {}, {}, {}]S -# partition3: [{}, {}, {}, {}, {}] -# partition4: [{}, {}, {}, {}, {}] - -# partition0 is the length that the partitions should be in this case, p_1 had an entry appended to itself somehow, and -# PartitionedArray won't add any more entries to it, once it reaches its max and has an additional element added. - -# VERSION v1.0.4 (7/30/2022 9:37PM) -# Various bug fixes and improvements. -# PartitionedArray#load_from_files! now works without throwing nil entries. - -# VERSION v1.0.3 (7/30/2022 2:42PM) -# Added ManagedPartitionedArray for CGMFS, which inherits from PartitionedArray and adds the following functionality: -# - last_entry -# => last_entry is a variable that keeps track of the last entry that was added to the database, in terms of a @data_arr -# since PartitionedArray is dynamically allocated, it is not possible to know the last entry that was added to the database - -# VERSION v1.0.2a -# All necessary instance methods have been implemented, ready for real world testing... - -# Ranged binary search, for use in CGMFS -# array_id = relative_id - db_size * (PARTITION_AMOUNT + 1) - -# When checking and adding, see if the index searched for in question - -# when starting out with the database project itself, check to see if the requested id is a member of something like rel_arr - -# 1) allocate range_arr and get the DB running -# 2) allocate rel_arr based on range_arr -# 3) allocate the actual array (@data_arr) -# VERSION: v1.0.2 -# VERSION: v1.0.1a - set_partition_subelement -# VERSION: v1.0.0 - all essential functions implemented -# 5/9/20 - 5:54PM -# TODO: -# def save_partition_element_to_file!(partition_id, element_id, db_folder: @db_folder) - -# VERSION: v1.0.0 (2022/03/30 - 3:46PM) -# Implemented all the necessary features for the PA to work, except for an add_from_lefr or add_from_right, which will -# attempt to fill in the gaps in the database. -# TODO: implement pure json parsing - -# * Notes: Have to manually convert all the string data to their proper data structure -# * HURDLE: converting strings to their proper data structures is non-trivial; could check stackoverflow for a solution -# VERSION v2.0 (4/22/2022) - added PartitionedArray#add(&block) function, to make additions to the array fast (fundamental method) -# VERSION v0.6 (2022/03/30) - finished functions necessary for the partitioned array to be useful -# VERSION: v0.5 (2022/03/14) -# Implemented -# PartitionedArray#get_part(partition_id) # => returns the partition specified by partition_id or nil if it doesn't exist -# PartitionedArray#add_part(void) # => adds a partition to the array, bookkeeping is done in the process -# VERSION: v0.4 (11:55PM) -# Fixed bugs, fixed array_id; allocate and get are fully implemented and working correctly for sure -# VERSION: v0.3 (3:46PM) -# allocate and get(id) are seemingly fully implemented and working correctly -# * start work on "allocate_file!" -# VERSION: v0.2 (02.28/2022 3:10PM) -# Currently working on get(id). -# VERSION: v0.1 (12/9/2021 12:31AM) -# Implementing allocate -# SYNOPSIS: An array system that is partitioned at a lower level, but functions as an almost-normal array at the high level -# DESCRIPTION: -# This is a system that is designed to be used as a database, but also as a normal array. -# NOTES: -# When loading a JSON database file (*_db.json), the related @ arr variables need to be set to what is within the JSON file database. -# This means the need to parse a file, and @allocated is set to true in the end. -require 'fileutils' -require 'json' -# PartitionedArray class, a data structure that is partitioned at a lower level, but functions as an almost-normal array at the high level -class PartitionedArray - # Access individual instance variables with caution... - attr_accessor :range_arr, :rel_arr, :db_size, :data_arr, :partition_amount_and_offset, :db_path, :db_name, :partition_addition_amount - - # DB_SIZE > PARTITION_AMOUNT - PARTITION_AMOUNT = 3 # The initial, + 1 - OFFSET = 1 # This came with the math, but you can just state the PARTITION_AMOUNT in total and not worry about the offset in the end - DB_SIZE = 10 # Caveat: The DB_SIZE is the total # of partitions, but you subtract it by one since the first partition is 0, in code. - DEFAULT_PATH = './CGMFS' # default fallSback/write to current path - DEBUGGING = false - PAUSE_DEBUG = false - DB_NAME = 'partitioned_array_slice' - PARTITION_ADDITION_AMOUNT = 1 # The amount of partitions to add when the array is full - DYNAMICALLY_ALLOCATES = true # If true, the array will dynamically allocate more partitions when it is full - LABEL_INTEGER = false - LABEL_RANGES = false - - def initialize(label_integer: LABEL_INTEGER, label_ranges: LABEL_RANGES, partition_addition_amount: PARTITION_ADDITION_AMOUNT, dynamically_allocates: DYNAMICALLY_ALLOCATES, db_size: DB_SIZE, partition_amount_and_offset: PARTITION_AMOUNT + OFFSET, db_path: DEFAULT_PATH, db_name: DB_NAME) -=begin -# Here is the explanation for the code below: -1. Partition addition amount: the amount of partitions that will be added once the database reaches its limit. For example, if the partition addition amount is 10, then the database will add 10 new partitions to the database once it reaches its limit. -2. Dynamically allocates: if the database is not full, then the database will not dynamically allocate new partitions. This is to prevent the database from creating too many partitions. -3. DB size: the size of the database. If the database reaches its limit, then the database will start to dynamically allocate new partitions. -4. Partition amount and offset: the amount of partitions and offset. For example, if the offset is 10 and the partition amount is 3, then the database will have 3 partitions, which are 10, 20, and 30. -5. DB path: the path of the database. -6. DB name: the name of the database. -7. Data array: the array that contains the data. -8. Range array: the array that contains the partition Range(s). -9. Rel array: @rel_arr: the array that contains the partition locations. -10. DB name: the name of the database. # -=end - @label_integer = label_integer - @label_ranges = label_ranges - @db_size = db_size - @partition_amount_and_offset = partition_amount_and_offset - @allocated = false - @db_path = db_path - @data_arr = [] # the data array which contains the data, has to be partitioned accordingly - @range_arr = [] # the range array which maintains the partition locations - @rel_arr = [] # a basic array from 1..n used in binary search - @db_name = db_name - @partition_addition_amount = partition_addition_amount - @dynamically_allocates = dynamically_allocates - end - - # by definition, when you revive one element, that entire partiton is also a revenant - def revenant_partition!(partition_number) - # possible code could go here that would keep tabs on the revenant partition(s) in question. - # for now, just delete the partition - # delete_partition!(partition_number) - load_partition_from_file!(partition_number) - end - - def kill_partition!(partition_number) - delete_partition!(partition_number) - end - - # SimulWolf: I'm not sure if this is a good idea, but I'm going to try it out - # ArityWolf: fast_set is a good idea, but it will remain untested for a while, - # because it is not used in the codebase yet. - # SimulWolf: I'm going to use it in the codebase - # ArityWolf: I'm going to use it in the codebase - # ArityWolf: But seriously, let's play some Everquest on the REAL LIVE server; so much to explore as a druid... - def fast_set(id, data_hash) - if id <= @data_arr.size - 1 - if @data_arr[id].nil? - @data_arr[id] = {} - elsif @data_arr[id].instance_of?(Hash) - # return @data_arr[id] - @data_arr[id] = data_hash - end - end - end - - def fast_get(id) - if id <= @data_arr.size - 1 - if @data_arr[id].nil? - @data_arr[id] = {} - elsif @data_arr[id].instance_of?(Hash) - return @data_arr[id] - # @data_arr[id] = data_hash - end - end - end - - # 2/7/2023 10:39AM - # examine closely later (also: this was never imnplemented in DragonRuby's PartitionedArray classes) - def [](*ids, hash: false, label_ranges: @label_ranges, label_integer: @label_integer) - # puts "ids: #{ids[0]}" - @elements = [] - if ids.first == "all" || ids.first == :all - # setup for "all" or :all as an only argument in the array subscript for now to get the entire @data_arr - 0.upto(@data_arr.size - 1) do |element_id| - # puts "element_id: #{element_id}" - @elements << @data_arr[element_id] - end - return @elements - end - return get(ids.first, hash: hash) if ids.size == 1 && ids.first.is_a?(Integer) && !label_integer - - return { ids.first => get(ids.first, hash: hash) } if ids.size == 1 && ids.first.is_a?(Integer) && label_integer - - # return get(id, hash: hash) if id.is_a? Integer - ids.map do |id| - case id - when Integer - if label_integer - { id => get(id, hash: hash) } if label_integer - else - get(id, hash: hash) if !label_integer - end - - when Range - if label_ranges - id.to_a.map { |i| { i => get(i, hash: hash) } } if label_ranges - else - id.to_a.map { |i| get(i, hash: hash) } if !label_ranges - end - else - raise "Invalid id type: #{id.class}" - end - end - # return id.to_a.map { |i| { i => get(i, hash: hash)} } if id.is_a? Range - end - ### LABEL_INTEGER LABEL_RANGES end here - - def range_db_get(range_arr, db_num) - range_arr[db_num] - end - - def debug(string) - p string if DEBUGGING - end - - def delete(id) - deleted = false - if id <= (@data_arr.size - 1) - @data_arr[id] = nil - deleted = true - end - deleted - end - - def load_dynamically_allocates_from_file! - File.open("#{@db_path}/dynamically_allocates.json", 'r') do |file| - @dynamically_allocates = JSON.parse(file.read) - end - end - - def save_dynamically_allocates_to_file! - # puts "in PA: @dynamically_allocates = #{@dynamically_allocates}" - FileUtils.touch("#{@db_path}/dynamically_allocates.json") - File.open("#{@db_path}/dynamically_allocates.json", 'w') do |file| - file.write(@dynamically_allocates.to_json) - end - end - - def delete_partition_subelement(id, partition_id) - # delete the partition's array element to what is specified - @data_arr[@range_arr[partition_id].to_a[id]] = nil - end - # set the partition's array element to what is specified - - def set_partition_subelement(id, partition_id, &block) - set_successfully = false - # set the partition's array element to what is specified - partition = get_partition(partition_id) - if id <= (@data_arr.size - 1) && partition - if block_given? && partition[id].instance_of?(Hash) - block.call(partition[id]) - set_successfully = true - elsif (id <= @data_arr.size - 1) && partition[id].nil? && block_given? - @data_arr[@range_arr[partition_id].to_a[id]] = {} - debug "d_arr: #{@data_arr[@range_arr[partition_id].to_a[id]]}" - block.call(@data_arr[@range_arr[partition_id].to_a[id]]) - set_successfully = true - end - end - set_successfully - end - - # delete the partition's array named partition, by id - # loopy, non-trivial code that may not work as intended, - # but works for binary space partitioning - def delete_partition!(partition_id) - # delete the partition id data - # debug "Partition ID: #{partition_id}" - a = @data_arr[range_db_get(@range_arr, partition_id)] - if (a.all? { |x| x.nil? } == false) - a.each_with_index do |item, index| - @data_arr[a[index]["id"]] = nil - end - end - end - - def get_partition(partition_id) - # get the partition id data - debug "Partition ID: #{partition_id}" - if partition_id <= @db_size - 1 - @data_arr[@range_arr[partition_id]] - else - debug("SliceID #{partition_id} is out of bounds.") - nil - end - end - - # TODO: Class#set(integer id) -> boolean - # Will yield a hash to some element id within the data array, to which you could use a block and modify said data - def set(id, &block) - if id <= @data_arr.size - 1 - if block_given? && @data_arr[id].instance_of?(Hash) - block.call(@data_arr[id]) # put the openstruct into the block as an argument - # @data_arr[id] = data - debug "block given" - elsif block_given? && @data_arr[id].nil? - # this element in particular must have been turned off; every initial element is an OpenStruct and nil if that partition was deactivated - debug "Loading array element #{id} from nil" - @data_arr[id] = {} - block.call(@data_arr[id]) - else - raise "No block given for element #{id}" - end - end - end - - # We define the add_left routine as starting from the end of @data_arr, and working the way back - # until we find the first element that is nilm if no elements return nil, then return nil as well - def add(return_added_element_id: true, &block) - # figure out what to have add return, but for now its technically in a programming sense "void", because this - # function always works, so long as you have a block - # Proposal: have add return the id of the element that was added, and have the caller know if it was added successfully - # What's currently implemented: since its pointless to check for truthiness, we set everything up to generally only return the added element id - # Why? This could become useful as a shortcut in game development, where you can just use the id to reference the element in the array - # and you use the block to modify the element if you want to - - # add the data to the array, searching until an empty hash elemet is found - - element_id_to_return = nil - @data_arr.each_with_index do |element, element_index| - # puts "element: #{element}" - if element == {} && block_given? # (if element is nill, no data is added because the partition is "offline") - block.call(@data_arr[element_index]) # seems == to block.call(element) - if @dynamically_allocates && (element_index == @db_size - 1 && at_capacity?) - @partition_addition_amount.times { add_partition } # if element_index == @data_arr.size - 1 # easier code; add if you reach the end of the array - save_all_to_files! # save the data to the files -- superceded by save_partition_to_file! below - # ... - # WTF!? This is a bug! It should be saving the partition that was just added, not all of them! - #partition_id = get_partition_id(element_index) - #save_partition_to_file!(partition_id) # save the data to the files; needs to be optimized - - end - element_id_to_return = element_index - break - elsif !block_given? - raise "No block given for element #{element}" - end - end - return element_id_to_return if return_added_element_id - end - - def add_nosave(return_added_element_id: true, &block) - element_id_to_return = nil - @data_arr.each_with_index do |element, element_index| - # puts "element: #{element}" - if element == {} && block_given? # (if element is nill, no data is added because the partition is "offline") - block.call(@data_arr[element_index]) # seems == to block.call(element) - if @dynamically_allocates && (element_index == @db_size - 1 && at_capacity?) - @partition_addition_amount.times { add_partition } # if element_index == @data_arr.size - 1 # easier code; add if you reach the end of the array - # save_all_to_files! # save the data to the files -- superceded by save_partition_to_file! below - # ... - # WTF!? This is a bug! It should be saving the partition that was just added, not all of them! - # partition_id = get_partition_id(element_index) - # save_partition_to_file!(partition_id) # save the data to the files; needs to be optimized - end - element_id_to_return = element_index - break - elsif !block_given? - raise "No block given for element #{element}" - end - end - return element_id_to_return if return_added_element_id - end - - # add_immediate - # adds directly to @data_arr; PartitionedArray loses its place and nothing will work right unless the data structure is patched live - def add_imm(return_added_element_id: true, &block); end - - # create an initial database (instance variable) - # later on, make this so it will load a database if its there, and if there's no data, create a standard database then save it - # Set to work with the default constants - def allocate(db_size: @db_size, partition_amount_and_offset: @partition_amount_and_offset, override: false) - if !@allocated || override - @allocated = false - partition = 0 - - db_size.times do - if @range_arr.empty? - partition += partition_amount_and_offset - @range_arr << (0..partition) - next - end - partition += partition_amount_and_offset - @range_arr << ((partition - partition_amount_and_offset + 1)..partition) - end - - x = 0 # offset test, for debug purposes - @data_arr = (0..(db_size * (partition_amount_and_offset - x))).to_a.map { {} } # for an array that fills to less than the max of @rel_arr - @rel_arr = @data_arr.size.times.map { |i| i } # for an array that fills to less than the max of @rel_arr - @allocated = true - else - puts 'Initial database has been already allocated.' - end - # debug "@data_arr element count: #{@data_arr.flatten.count - 1}" - # debug "@data_arr: #{@data_arr}" - @allocated = true - end - - # Returns a hash based on the array element you are searching for. - # Haven't done much research afterwards on the idea of - # whether the binary search is necessary, but it seems like it is. - # The idea first came up during the research of the partitioned array equation. - def get(id, hash: false) - return nil unless @allocated # if the database has not been allocated, return nil - - # return nil if id.nil? # if the id is nil, return nil. This is a safety check. - - ### Commented this code out on 2022-08-11 in light of fixing add_partition - # return @data_arr.first if id.zero? - # return @data_arr.last if id == @data_arr.count - 1 - # If you do this, you will not be able to request a hash in these cases - - data = nil - db_index = nil - relative_id = nil - - @range_arr.each_with_index do |range, index| - relative_id = range.bsearch { |element| @rel_arr[element] >= id } - if relative_id - db_index = index - if range_db_get(@range_arr, db_index).member? @rel_arr[relative_id] - break - end - else - db_index = nil - relative_id = nil - end - end - - unless relative_id && db_index - debug 'The value could not be found' - else - debug "db_index: #{db_index}" - - # Special case; composing the array_id - if db_index.zero? - array_id = relative_id - (db_index * @partition_amount_and_offset) - else - array_id = (relative_id - (db_index * @partition_amount_and_offset)) - 1 - end - - debug "The value was found at #{array_id} in the array (data_arr)" - if hash - data = { "data_partition" => @data_arr[@range_arr[db_index]], "db_index" => db_index, "array_id" => array_id, "data" => @data_arr[@range_arr[db_index].to_a[array_id]], "db_size" => @db_size } - else - data = @data_arr[@range_arr[db_index].to_a[array_id]] - end - end - data - end - - def add_partition - last_range_num = @range_arr.last.to_a.last + 1 - debug "last_range_num: #{last_range_num}" - @range_arr << (last_range_num..(last_range_num + @partition_amount_and_offset - 1)) # works - - (last_range_num..(last_range_num + @partition_amount_and_offset - 1)).to_a.each do |i| - @data_arr << {} - @rel_arr << i - end - - @db_size += 1 - debug "Partition added successfully; data allocated" - debug "@data_arr: #{@data_arr}" - end - - def string2range(range_string) - split_range = range_string.split("..") - split_range.first.to_i..split_range[1].to_i - end - - def debug_and_pause(message) - puts message if PAUSE_DEBUG - gets if PAUSE_DEBUG - end - - # loads the files within the directory CGMFS/partitioned_array_slice - # needed things - # range_arr => it is the array of ranges that the database is divided into - # data_arr => it is output to json - # rel_arr => it is output to json - # part_} => it is taken from the range_arr subdivisions; perform a map and load it into the database, one by one - def load_from_files! - load_partition_addition_amount_from_file! - load_dynamically_allocates_from_file! - # @db_size needs to be taken into account and changed accordingly - path = "#{@db_path}/#{@db_name}" - @partition_amount_and_offset = File.open("#{path}/partition_amount_and_offset.json", 'r') { |f| JSON.parse(f.read) } - @range_arr = File.open("#{path}/range_arr.json", 'r') { |f| JSON.parse(f.read) } - @range_arr.map! { |range_element| string2range(range_element) } - @rel_arr = File.open("#{path}/rel_arr.json", 'r') { |f| JSON.parse(f.read) } - @db_size = File.open("#{path}/db_size.json", 'r') { |f| JSON.parse(f.read) } - data_arr_set_partitions = [] - 0.upto(@db_size - 1) do |partition_element_index| - data_arr_set_partitions << File.open("#{path}/#{@db_name}_part_#{partition_element_index}.json", 'r') { |f| JSON.parse(f.read) } - @data_arr = data_arr_set_partitions.flatten # side effect: if you don't flatten it, you get an array with partitioned array elements - end - @allocated = true - end - - def all_empty_partitions? - @data_arr.all?(&:nil?) - end - - def dup_data_arr! - @data_arr.clone - end - - # Plan: be able to dump @data_arr to disk anytime you want - def load_partition_from_file!(partition_id) - path = "#{@db_path}/#{@db_name}" - # @data_arr = File.open("#{path}/data_arr.json", 'r') { |f| JSON.parse(f.read) } # can write the entire array to disk for certain operations - @partition_amount_and_offset = File.open("#{path}/partition_amount_and_offset.json", 'r') { |f| JSON.parse(f.read) } - @range_arr = File.open("#{path}/range_arr.json", 'r') { |f| JSON.parse(f.read) } - @range_arr.map! { |range_element| string2range(range_element) } - @rel_arr = File.open("#{path}/rel_arr.json", 'r') { |f| JSON.parse(f.read) } - sliced_range_arr = @range_arr[partition_id].to_a.map { |range_element| range_element } - partition_data = File.open("#{path}/#{@db_name}_part_#{partition_id}.json", 'r') { |f| JSON.parse(f.read) } - ((sliced_range_arr.first.to_i)..(sliced_range_arr.last.to_i)).to_a.each do |range_element| - @data_arr[range_element] = partition_data[range_element] - end - # p @data_arr[@range_arr[partition_id]].to_s - end - - def save_partition_to_file!(partition_id, db_folder: @db_folder) - partition_data = get_partition(partition_id) - path = "#{@db_path}/#{@db_name}" - File.open("#{path}/#{db_folder}/#{@db_name}_part_#{partition_id}.json", 'w') { |f| f.write(partition_data.to_json) } - end - - def save_partition_addition_amount_to_file!(db_folder: @db_folder) - path = "#{@db_path}/#{@db_name}" - FileUtils.touch("#{path}/#{db_folder}/partition_addition_amount.json") - File.open("#{path}/#{db_folder}/partition_addition_amount.json", 'w') { |f| f.write(@partition_amount_and_offset.to_json) } - end - - def load_partition_addition_amount_from_file!(db_folder: @db_folder) - path = "#{@db_path}/#{@db_name}" - @partition_addition_amount = File.open("#{path}/#{db_folder}/partition_addition_amount.json", 'r') { |f| JSON.parse(f.read) } - end - - def save_all_to_files!(db_folder: @db_folder, db_path: @db_path, db_name: @db_name) - # Bug: files are not being written correctly. - # Fix: (8/11/2022 - 5:55am) - add db_size.json - - unless Dir.exist?(db_path) - Dir.mkdir(db_path) - end - - path = "#{db_path}/#{db_name}" - - unless Dir.exist?(path) - Dir.mkdir(path) - end - - save_partition_addition_amount_to_file! - save_dynamically_allocates_to_file! - File.open("#{path}/#{db_folder}/partition_amount_and_offset.json", 'w') { |f| f.write(@partition_amount_and_offset.to_json) } - File.open("#{path}/#{db_folder}/range_arr.json", 'w') { |f| f.write(@range_arr.to_json) } - File.open("#{path}/#{db_folder}/rel_arr.json", 'w') { |f| f.write(@rel_arr.to_json) } - File.open("#{path}/#{db_folder}/db_size.json", 'w') { |f| f.write(@db_size.to_json) } - # debug path - 0.upto(@db_size - 1) do |index| - FileUtils.touch("#{path}/#{db_folder}/#{@db_name}_part_#{index}.json") - File.open("#{path}/#{@db_name}_part_#{index}.json", 'w') do |f| - partition = get_partition(index) - f.write(partition.to_json) - end - end - end -end - -# rubocop:enable Layout/LineLength -# rubocop:enable Style/IfUnlessModifier -# rubocop:enable Metrics/MethodLength -# rubocop:enable Metrics/AbcSize -# rubocop:enable Metrics/CyclomaticComplexity -# rubocop:enable Metrics/PerceivedComplexity -# rubocop:enable Style/UnlessElse -# rubocop:enable Metrics/ClassLength -# rubocop:enable Style/StringLiterals -# rubocop:enable Style/ConditionalAssignment -# rubocop:enable Style/GuardClause -# rubocop:enable Style/MutableConstant, Metrics/ParameterLists, Style/ParenthesesAroundCondition, Style/NegatedIf, Lint/UnusedBlockArgument, Lint/NonAtomicFileOperation, Style/FileWrite, Style/HashSyntax, Style/IfInsideElse, Style/RedundantReturn, Style/BlockComments, Style/SymbolProc, Style/FrozenStringLiteralComment +# rubocop:disable Style/MutableConstant, Metrics/ParameterLists, Style/ParenthesesAroundCondition, Style/NegatedIf, Lint/UnusedBlockArgument, Lint/NonAtomicFileOperation, Style/FileWrite, Style/HashSyntax, Style/IfInsideElse, Style/RedundantReturn, Style/BlockComments, Style/SymbolProc, Style/FrozenStringLiteralComment +# rubocop:disable Style/GuardClause +# rubocop:disable Style/ConditionalAssignment +# rubocop:disable Style/StringLiterals +# rubocop:disable Metrics/ClassLength +# rubocop:disable Style/UnlessElse +# rubocop:disable Metrics/PerceivedComplexity +# rubocop:disable Metrics/CyclomaticComplexity +# rubocop:disable Metrics/AbcSize +# rubocop:disable Metrics/MethodLength +# rubocop:disable Style/IfUnlessModifier +# rubocop:disable Layout/LineLength +# Q: how do I fix git conflits with git commands? +# A: git checkout --ours lib/partitioned_array/lib/partitioned_array.rb + + +# CHANGELOG +# VERSION v4.0.4-release +# VERSION v4.0.3-release - prettify linter +# VERSION v4.0.2-release - 2023 - July - 4 ("Black Summer, Half Moon") [07/04/2023] - MAJOR UPDATE! +# MAJOR BUG FIXES: +# fixed another `gets` debug-bug in PartitionedArray +# added add_nosave to PartitionedArray +# add_nosave: adds an element to @data_arr, but does not save the partition to file (compare to add's bug fix) + +# VERSION v4.0.1-release - 2023 - June - 21 ("Simmer Down Solstice") [06/21/2023] - MAJOR UPDATE! +# MAJOR BUG FIXES: +# PartitionedArray#set(id, value, hash: true or false) +## Fixed a fundamentaa flaw caused by a bug where it was looking at `@db_size` instead of `@data_arr - 1` +# NOTICE: Partitioned Array and LineDB should be fully functional now, as in battle tested and a confirmed bug that +# was causing the data to not be #set or overwritten has been fixed. + +# VERSION v4.0.0-release - 5/9/2023 +# * fixed delete_partition!, aka kill_partition! - 2023-05-09 5:00PM +# * binary space partitioning now works correctly, or in the first place +# * use PartitionedArray#kill_partition! or PartitionedArray#delete_partition! to flip the bit of the partition to 0 +# * added a few more tests (revenant_partitions.rb) +# TODO: potential cleanup +# QUICK NOTES: +# * PartitionedArray#delete_partition! and PartitionedArray#kill_partition! are the same thing +# use the delete methods to remove a partition from the database in memory; +# be fully aware that if that partition was saved onto disk (.json), you can load it back +# up with ParititionedArray#load_partition_from_file! + + +# VERSION v3.0.0-release - 5/5/2023 +# * switched code around so that add only saves the partition of which the id is within: add(id, hash: false) - 5/5/2023 +# * see additional changes in ManagedPartitionedArray v3.0.0-release +# NOTE: dr-version has been synced with this version +# VERSION v2.0.1-release - remove :last and :first in favor of [:all].last and [:all].first; update in DRAGONRUBY ACCORDINGLY (2/7/2023 11:14AM) +# VERSION v2.0.0-release - add :all :last :first keywords to [] method, unrefined and untested +# VERSION v1.2.4-release - cleanup puts in add_partition +# VERSION v1.2.3-release - @label_integer and @label_ranges, sync with line_db, etc +# VERSION v1.2.2-release - @label_integer and @label_ranges +# VERSION v1.3.1-release +# VERSION v1.3.0-release - 1/13/2023 - 1:39AM +# VERSION v1.2.9-release - [](id ...) - accepts ranges, and integers as an argument splat +### Functionality: label the ranges and/or the integer values in the [] arguments with a hash argument +### --with keys pointing to the get values (for integers and/or ranges) +### label_ranges: true +### label_integer: true +# VERSION v1.2.8 - [](id, hash: false) - 1/13/2023 +# VERSION: v1.2.7 - implemented [](id) - uses get(id, hash: false) - 1/13/2023 +# VERSION: v1.2.6 - cleanup; plan on being able to save @data_arr to file directly (not by partitions) - 10/19/2022 2:03PM +# VERSION v1.2.5a - endless add implementation in ManagedPartitionedArray#endless_add +# Allows the database to continuously add and allocate, as if it were a plain PartitionedArray +# VERSION v1.2.4 - now its not "data_arr_size" if you set the variable correctly +# Note: The struct may require more work, but this struct in particular is not necessary in the managed partitioned array +# -- too many allocations are done when 'at capacity', but its because of the nature of the beast +# VERSION v1.2.4 - fixed a bug with allocations, also modified in ManagedPartitionedArray +# VERSION v1.2.3 - fully functional in tandem with ManagedPartitionedArray; added a few more tests (9/13/2022 - 5:48am) +# VERSION v1.2.2 - after adding partitions, every partition is saved to reflect the new changes on disk +# VERSION v1.2.1 - fixed PartitionedArray#get(id, &block) bugs +# - file cleanup +# VERSION v1.2.0 - Cleanup according to rubocop and solargraph linter; clarification in places added (8/11/2022 - 6:01am) +# VERSION v1.1.1 - Documenting code and scanning for bugs and any version wrap-ups +# - v1.1.2 - PartitionedArray#add(return_added_element_id: true, &block): return element id of the location of the addition +# VERSION v1.1.0a (8/11/2022 - 5:11am) +# -- PartitionedArray#add_partition now works correctly. +# Various bug fixes that lead to array variable inconsistencies + +# VERSION v1.0.5a (7/30/2022) +# -- So far the partitioned array works with allocation of partitions, saving to files, and loading from files. +# PROBLEM/BUG: PartitionedArray#add_partition does not allocate and manage the variables correctly +# IT: upon the need to add a new partition, it adds but the entry is added to the end of the @data_arr +# partition, extending its length by one which is not what we want. +# VERSION v1.0.5 (7/30/2022) +# More bug fixes, notable in saving and loading data from files. +# Example: + +# partition0: [{"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}] +# partition1: [{"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World"}, {"log"=>"Hello World", :log=>"Hello World"}] +# partition2: [{}, {}, {}, {}, {}]S +# partition3: [{}, {}, {}, {}, {}] +# partition4: [{}, {}, {}, {}, {}] + +# partition0 is the length that the partitions should be in this case, p_1 had an entry appended to itself somehow, and +# PartitionedArray won't add any more entries to it, once it reaches its max and has an additional element added. + +# VERSION v1.0.4 (7/30/2022 9:37PM) +# Various bug fixes and improvements. +# PartitionedArray#load_from_files! now works without throwing nil entries. + +# VERSION v1.0.3 (7/30/2022 2:42PM) +# Added ManagedPartitionedArray for CGMFS, which inherits from PartitionedArray and adds the following functionality: +# - last_entry +# => last_entry is a variable that keeps track of the last entry that was added to the database, in terms of a @data_arr +# since PartitionedArray is dynamically allocated, it is not possible to know the last entry that was added to the database + +# VERSION v1.0.2a +# All necessary instance methods have been implemented, ready for real world testing... + +# Ranged binary search, for use in CGMFS +# array_id = relative_id - db_size * (PARTITION_AMOUNT + 1) + +# When checking and adding, see if the index searched for in question + +# when starting out with the database project itself, check to see if the requested id is a member of something like rel_arr + +# 1) allocate range_arr and get the DB running +# 2) allocate rel_arr based on range_arr +# 3) allocate the actual array (@data_arr) +# VERSION: v1.0.2 +# VERSION: v1.0.1a - set_partition_subelement +# VERSION: v1.0.0 - all essential functions implemented +# 5/9/20 - 5:54PM +# TODO: +# def save_partition_element_to_file!(partition_id, element_id, db_folder: @db_folder) + +# VERSION: v1.0.0 (2022/03/30 - 3:46PM) +# Implemented all the necessary features for the PA to work, except for an add_from_lefr or add_from_right, which will +# attempt to fill in the gaps in the database. +# TODO: implement pure json parsing + +# * Notes: Have to manually convert all the string data to their proper data structure +# * HURDLE: converting strings to their proper data structures is non-trivial; could check stackoverflow for a solution +# VERSION v2.0 (4/22/2022) - added PartitionedArray#add(&block) function, to make additions to the array fast (fundamental method) +# VERSION v0.6 (2022/03/30) - finished functions necessary for the partitioned array to be useful +# VERSION: v0.5 (2022/03/14) +# Implemented +# PartitionedArray#get_part(partition_id) # => returns the partition specified by partition_id or nil if it doesn't exist +# PartitionedArray#add_part(void) # => adds a partition to the array, bookkeeping is done in the process +# VERSION: v0.4 (11:55PM) +# Fixed bugs, fixed array_id; allocate and get are fully implemented and working correctly for sure +# VERSION: v0.3 (3:46PM) +# allocate and get(id) are seemingly fully implemented and working correctly +# * start work on "allocate_file!" +# VERSION: v0.2 (02.28/2022 3:10PM) +# Currently working on get(id). +# VERSION: v0.1 (12/9/2021 12:31AM) +# Implementing allocate +# SYNOPSIS: An array system that is partitioned at a lower level, but functions as an almost-normal array at the high level +# DESCRIPTION: +# This is a system that is designed to be used as a database, but also as a normal array. +# NOTES: +# When loading a JSON database file (*_db.json), the related @ arr variables need to be set to what is within the JSON file database. +# This means the need to parse a file, and @allocated is set to true in the end. +require 'fileutils' +require 'json' +# PartitionedArray class, a data structure that is partitioned at a lower level, but functions as an almost-normal array at the high level +class PartitionedArray + # Access individual instance variables with caution... + attr_accessor :range_arr, :rel_arr, :db_size, :data_arr, :partition_amount_and_offset, :db_path, :db_name, :partition_addition_amount + + # DB_SIZE > PARTITION_AMOUNT + PARTITION_AMOUNT = 3 # The initial, + 1 + OFFSET = 1 # This came with the math, but you can just state the PARTITION_AMOUNT in total and not worry about the offset in the end + DB_SIZE = 10 # Caveat: The DB_SIZE is the total # of partitions, but you subtract it by one since the first partition is 0, in code. + DEFAULT_PATH = './CGMFS' # default fallSback/write to current path + DEBUGGING = false + PAUSE_DEBUG = false + DB_NAME = 'partitioned_array_slice' + PARTITION_ADDITION_AMOUNT = 1 # The amount of partitions to add when the array is full + DYNAMICALLY_ALLOCATES = true # If true, the array will dynamically allocate more partitions when it is full + LABEL_INTEGER = false + LABEL_RANGES = false + + def initialize(label_integer: LABEL_INTEGER, label_ranges: LABEL_RANGES, partition_addition_amount: PARTITION_ADDITION_AMOUNT, dynamically_allocates: DYNAMICALLY_ALLOCATES, db_size: DB_SIZE, partition_amount_and_offset: PARTITION_AMOUNT + OFFSET, db_path: DEFAULT_PATH, db_name: DB_NAME) +=begin +# Here is the explanation for the code below: +1. Partition addition amount: the amount of partitions that will be added once the database reaches its limit. For example, if the partition addition amount is 10, then the database will add 10 new partitions to the database once it reaches its limit. +2. Dynamically allocates: if the database is not full, then the database will not dynamically allocate new partitions. This is to prevent the database from creating too many partitions. +3. DB size: the size of the database. If the database reaches its limit, then the database will start to dynamically allocate new partitions. +4. Partition amount and offset: the amount of partitions and offset. For example, if the offset is 10 and the partition amount is 3, then the database will have 3 partitions, which are 10, 20, and 30. +5. DB path: the path of the database. +6. DB name: the name of the database. +7. Data array: the array that contains the data. +8. Range array: the array that contains the partition Range(s). +9. Rel array: @rel_arr: the array that contains the partition locations. +10. DB name: the name of the database. # +=end + @label_integer = label_integer + @label_ranges = label_ranges + @db_size = db_size + @partition_amount_and_offset = partition_amount_and_offset + @allocated = false + @db_path = db_path + @data_arr = [] # the data array which contains the data, has to be partitioned accordingly + @range_arr = [] # the range array which maintains the partition locations + @rel_arr = [] # a basic array from 1..n used in binary search + @db_name = db_name + @partition_addition_amount = partition_addition_amount + @dynamically_allocates = dynamically_allocates + end + + # by definition, when you revive one element, that entire partiton is also a revenant + def revenant_partition!(partition_number) + # possible code could go here that would keep tabs on the revenant partition(s) in question. + # for now, just delete the partition + # delete_partition!(partition_number) + load_partition_from_file!(partition_number) + end + + def kill_partition!(partition_number) + delete_partition!(partition_number) + end + + # SimulWolf: I'm not sure if this is a good idea, but I'm going to try it out + # ArityWolf: fast_set is a good idea, but it will remain untested for a while, + # because it is not used in the codebase yet. + # SimulWolf: I'm going to use it in the codebase + # ArityWolf: I'm going to use it in the codebase + # ArityWolf: But seriously, let's play some Everquest on the REAL LIVE server; so much to explore as a druid... + def fast_set(id, data_hash) + if id <= @data_arr.size - 1 + if @data_arr[id].nil? + @data_arr[id] = {} + elsif @data_arr[id].instance_of?(Hash) + # return @data_arr[id] + @data_arr[id] = data_hash + end + end + end + + def fast_get(id) + if id <= @data_arr.size - 1 + if @data_arr[id].nil? + @data_arr[id] = {} + elsif @data_arr[id].instance_of?(Hash) + return @data_arr[id] + # @data_arr[id] = data_hash + end + end + end + + # 2/7/2023 10:39AM + # examine closely later (also: this was never imnplemented in DragonRuby's PartitionedArray classes) + def [](*ids, hash: false, label_ranges: @label_ranges, label_integer: @label_integer) + # puts "ids: #{ids[0]}" + @elements = [] + if ids.first == "all" || ids.first == :all + # setup for "all" or :all as an only argument in the array subscript for now to get the entire @data_arr + 0.upto(@data_arr.size - 1) do |element_id| + # puts "element_id: #{element_id}" + @elements << @data_arr[element_id] + end + return @elements + end + return get(ids.first, hash: hash) if ids.size == 1 && ids.first.is_a?(Integer) && !label_integer + + return { ids.first => get(ids.first, hash: hash) } if ids.size == 1 && ids.first.is_a?(Integer) && label_integer + + # return get(id, hash: hash) if id.is_a? Integer + ids.map do |id| + case id + when Integer + if label_integer + { id => get(id, hash: hash) } if label_integer + else + get(id, hash: hash) if !label_integer + end + + when Range + if label_ranges + id.to_a.map { |i| { i => get(i, hash: hash) } } if label_ranges + else + id.to_a.map { |i| get(i, hash: hash) } if !label_ranges + end + else + raise "Invalid id type: #{id.class}" + end + end + # return id.to_a.map { |i| { i => get(i, hash: hash)} } if id.is_a? Range + end + ### LABEL_INTEGER LABEL_RANGES end here + + def range_db_get(range_arr, db_num) + range_arr[db_num] + end + + def debug(string) + p string if DEBUGGING + end + + def delete(id) + deleted = false + if id <= (@data_arr.size - 1) + @data_arr[id] = nil + deleted = true + end + deleted + end + + def load_dynamically_allocates_from_file! + File.open("#{@db_path}/dynamically_allocates.json", 'r') do |file| + @dynamically_allocates = JSON.parse(file.read) + end + end + + def save_dynamically_allocates_to_file! + # puts "in PA: @dynamically_allocates = #{@dynamically_allocates}" + FileUtils.touch("#{@db_path}/dynamically_allocates.json") + File.open("#{@db_path}/dynamically_allocates.json", 'w') do |file| + file.write(@dynamically_allocates.to_json) + end + end + + def delete_partition_subelement(id, partition_id) + # delete the partition's array element to what is specified + @data_arr[@range_arr[partition_id].to_a[id]] = nil + end + # set the partition's array element to what is specified + + def set_partition_subelement(id, partition_id, &block) + set_successfully = false + # set the partition's array element to what is specified + partition = get_partition(partition_id) + if id <= (@data_arr.size - 1) && partition + if block_given? && partition[id].instance_of?(Hash) + block.call(partition[id]) + set_successfully = true + elsif (id <= @data_arr.size - 1) && partition[id].nil? && block_given? + @data_arr[@range_arr[partition_id].to_a[id]] = {} + debug "d_arr: #{@data_arr[@range_arr[partition_id].to_a[id]]}" + block.call(@data_arr[@range_arr[partition_id].to_a[id]]) + set_successfully = true + end + end + set_successfully + end + + # delete the partition's array named partition, by id + # loopy, non-trivial code that may not work as intended, + # but works for binary space partitioning + def delete_partition!(partition_id) + # delete the partition id data + # debug "Partition ID: #{partition_id}" + a = @data_arr[range_db_get(@range_arr, partition_id)] + if (a.all? { |x| x.nil? } == false) + a.each_with_index do |item, index| + @data_arr[a[index]["id"]] = nil + end + end + end + + def get_partition(partition_id) + # get the partition id data + debug "Partition ID: #{partition_id}" + if partition_id <= @db_size - 1 + @data_arr[@range_arr[partition_id]] + else + debug("SliceID #{partition_id} is out of bounds.") + nil + end + end + + # TODO: Class#set(integer id) -> boolean + # Will yield a hash to some element id within the data array, to which you could use a block and modify said data + def set(id, &block) + if id <= @data_arr.size - 1 + if block_given? && @data_arr[id].instance_of?(Hash) + block.call(@data_arr[id]) # put the openstruct into the block as an argument + # @data_arr[id] = data + debug "block given" + elsif block_given? && @data_arr[id].nil? + # this element in particular must have been turned off; every initial element is an OpenStruct and nil if that partition was deactivated + debug "Loading array element #{id} from nil" + @data_arr[id] = {} + block.call(@data_arr[id]) + else + raise "No block given for element #{id}" + end + end + end + + # We define the add_left routine as starting from the end of @data_arr, and working the way back + # until we find the first element that is nilm if no elements return nil, then return nil as well + def add(return_added_element_id: true, &block) + # figure out what to have add return, but for now its technically in a programming sense "void", because this + # function always works, so long as you have a block + # Proposal: have add return the id of the element that was added, and have the caller know if it was added successfully + # What's currently implemented: since its pointless to check for truthiness, we set everything up to generally only return the added element id + # Why? This could become useful as a shortcut in game development, where you can just use the id to reference the element in the array + # and you use the block to modify the element if you want to + + # add the data to the array, searching until an empty hash elemet is found + + element_id_to_return = nil + @data_arr.each_with_index do |element, element_index| + # puts "element: #{element}" + if element == {} && block_given? # (if element is nill, no data is added because the partition is "offline") + block.call(@data_arr[element_index]) # seems == to block.call(element) + if @dynamically_allocates && (element_index == @db_size - 1 && at_capacity?) + @partition_addition_amount.times { add_partition } # if element_index == @data_arr.size - 1 # easier code; add if you reach the end of the array + save_all_to_files! # save the data to the files -- superceded by save_partition_to_file! below + # ... + # WTF!? This is a bug! It should be saving the partition that was just added, not all of them! + #partition_id = get_partition_id(element_index) + #save_partition_to_file!(partition_id) # save the data to the files; needs to be optimized + + end + element_id_to_return = element_index + break + elsif !block_given? + raise "No block given for element #{element}" + end + end + return element_id_to_return if return_added_element_id + end + + def add_nosave(return_added_element_id: true, &block) + element_id_to_return = nil + @data_arr.each_with_index do |element, element_index| + # puts "element: #{element}" + if element == {} && block_given? # (if element is nill, no data is added because the partition is "offline") + block.call(@data_arr[element_index]) # seems == to block.call(element) + if @dynamically_allocates && (element_index == @db_size - 1 && at_capacity?) + @partition_addition_amount.times { add_partition } # if element_index == @data_arr.size - 1 # easier code; add if you reach the end of the array + # save_all_to_files! # save the data to the files -- superceded by save_partition_to_file! below + # ... + # WTF!? This is a bug! It should be saving the partition that was just added, not all of them! + # partition_id = get_partition_id(element_index) + # save_partition_to_file!(partition_id) # save the data to the files; needs to be optimized + end + element_id_to_return = element_index + break + elsif !block_given? + raise "No block given for element #{element}" + end + end + return element_id_to_return if return_added_element_id + end + + # add_immediate + # adds directly to @data_arr; PartitionedArray loses its place and nothing will work right unless the data structure is patched live + def add_imm(return_added_element_id: true, &block); end + + # create an initial database (instance variable) + # later on, make this so it will load a database if its there, and if there's no data, create a standard database then save it + # Set to work with the default constants + def allocate(db_size: @db_size, partition_amount_and_offset: @partition_amount_and_offset, override: false) + if !@allocated || override + @allocated = false + partition = 0 + + db_size.times do + if @range_arr.empty? + partition += partition_amount_and_offset + @range_arr << (0..partition) + next + end + partition += partition_amount_and_offset + @range_arr << ((partition - partition_amount_and_offset + 1)..partition) + end + + x = 0 # offset test, for debug purposes + @data_arr = (0..(db_size * (partition_amount_and_offset - x))).to_a.map { {} } # for an array that fills to less than the max of @rel_arr + @rel_arr = @data_arr.size.times.map { |i| i } # for an array that fills to less than the max of @rel_arr + @allocated = true + else + puts 'Initial database has been already allocated.' + end + # debug "@data_arr element count: #{@data_arr.flatten.count - 1}" + # debug "@data_arr: #{@data_arr}" + @allocated = true + end + + # Returns a hash based on the array element you are searching for. + # Haven't done much research afterwards on the idea of + # whether the binary search is necessary, but it seems like it is. + # The idea first came up during the research of the partitioned array equation. + def get(id, hash: false) + return nil unless @allocated # if the database has not been allocated, return nil + + # return nil if id.nil? # if the id is nil, return nil. This is a safety check. + + ### Commented this code out on 2022-08-11 in light of fixing add_partition + # return @data_arr.first if id.zero? + # return @data_arr.last if id == @data_arr.count - 1 + # If you do this, you will not be able to request a hash in these cases + + data = nil + db_index = nil + relative_id = nil + + @range_arr.each_with_index do |range, index| + relative_id = range.bsearch { |element| @rel_arr[element] >= id } + if relative_id + db_index = index + if range_db_get(@range_arr, db_index).member? @rel_arr[relative_id] + break + end + else + db_index = nil + relative_id = nil + end + end + + unless relative_id && db_index + debug 'The value could not be found' + else + debug "db_index: #{db_index}" + + # Special case; composing the array_id + if db_index.zero? + array_id = relative_id - (db_index * @partition_amount_and_offset) + else + array_id = (relative_id - (db_index * @partition_amount_and_offset)) - 1 + end + + debug "The value was found at #{array_id} in the array (data_arr)" + if hash + data = { "data_partition" => @data_arr[@range_arr[db_index]], "db_index" => db_index, "array_id" => array_id, "data" => @data_arr[@range_arr[db_index].to_a[array_id]], "db_size" => @db_size } + else + data = @data_arr[@range_arr[db_index].to_a[array_id]] + end + end + data + end + + def add_partition + last_range_num = @range_arr.last.to_a.last + 1 + debug "last_range_num: #{last_range_num}" + @range_arr << (last_range_num..(last_range_num + @partition_amount_and_offset - 1)) # works + + (last_range_num..(last_range_num + @partition_amount_and_offset - 1)).to_a.each do |i| + @data_arr << {} + @rel_arr << i + end + + @db_size += 1 + debug "Partition added successfully; data allocated" + debug "@data_arr: #{@data_arr}" + end + + def string2range(range_string) + split_range = range_string.split("..") + split_range.first.to_i..split_range[1].to_i + end + + def debug_and_pause(message) + puts message if PAUSE_DEBUG + gets if PAUSE_DEBUG + end + + # loads the files within the directory CGMFS/partitioned_array_slice + # needed things + # range_arr => it is the array of ranges that the database is divided into + # data_arr => it is output to json + # rel_arr => it is output to json + # part_} => it is taken from the range_arr subdivisions; perform a map and load it into the database, one by one + def load_from_files! + load_partition_addition_amount_from_file! + load_dynamically_allocates_from_file! + # @db_size needs to be taken into account and changed accordingly + path = "#{@db_path}/#{@db_name}" + @partition_amount_and_offset = File.open("#{path}/partition_amount_and_offset.json", 'r') { |f| JSON.parse(f.read) } + @range_arr = File.open("#{path}/range_arr.json", 'r') { |f| JSON.parse(f.read) } + @range_arr.map! { |range_element| string2range(range_element) } + @rel_arr = File.open("#{path}/rel_arr.json", 'r') { |f| JSON.parse(f.read) } + @db_size = File.open("#{path}/db_size.json", 'r') { |f| JSON.parse(f.read) } + data_arr_set_partitions = [] + 0.upto(@db_size - 1) do |partition_element_index| + data_arr_set_partitions << File.open("#{path}/#{@db_name}_part_#{partition_element_index}.json", 'r') { |f| JSON.parse(f.read) } + @data_arr = data_arr_set_partitions.flatten # side effect: if you don't flatten it, you get an array with partitioned array elements + end + @allocated = true + end + + def all_empty_partitions? + @data_arr.all?(&:nil?) + end + + def dup_data_arr! + @data_arr.clone + end + + # Plan: be able to dump @data_arr to disk anytime you want + def load_partition_from_file!(partition_id) + path = "#{@db_path}/#{@db_name}" + # @data_arr = File.open("#{path}/data_arr.json", 'r') { |f| JSON.parse(f.read) } # can write the entire array to disk for certain operations + @partition_amount_and_offset = File.open("#{path}/partition_amount_and_offset.json", 'r') { |f| JSON.parse(f.read) } + @range_arr = File.open("#{path}/range_arr.json", 'r') { |f| JSON.parse(f.read) } + @range_arr.map! { |range_element| string2range(range_element) } + @rel_arr = File.open("#{path}/rel_arr.json", 'r') { |f| JSON.parse(f.read) } + sliced_range_arr = @range_arr[partition_id].to_a.map { |range_element| range_element } + partition_data = File.open("#{path}/#{@db_name}_part_#{partition_id}.json", 'r') { |f| JSON.parse(f.read) } + ((sliced_range_arr.first.to_i)..(sliced_range_arr.last.to_i)).to_a.each do |range_element| + @data_arr[range_element] = partition_data[range_element] + end + # p @data_arr[@range_arr[partition_id]].to_s + end + + def save_partition_to_file!(partition_id, db_folder: @db_folder) + partition_data = get_partition(partition_id) + path = "#{@db_path}/#{@db_name}" + File.open("#{path}/#{db_folder}/#{@db_name}_part_#{partition_id}.json", 'w') { |f| f.write(partition_data.to_json) } + end + + def save_partition_addition_amount_to_file!(db_folder: @db_folder) + path = "#{@db_path}/#{@db_name}" + FileUtils.touch("#{path}/#{db_folder}/partition_addition_amount.json") + File.open("#{path}/#{db_folder}/partition_addition_amount.json", 'w') { |f| f.write(@partition_amount_and_offset.to_json) } + end + + def load_partition_addition_amount_from_file!(db_folder: @db_folder) + path = "#{@db_path}/#{@db_name}" + @partition_addition_amount = File.open("#{path}/#{db_folder}/partition_addition_amount.json", 'r') { |f| JSON.parse(f.read) } + end + + def save_all_to_files!(db_folder: @db_folder, db_path: @db_path, db_name: @db_name) + # Bug: files are not being written correctly. + # Fix: (8/11/2022 - 5:55am) - add db_size.json + + unless Dir.exist?(db_path) + Dir.mkdir(db_path) + end + + path = "#{db_path}/#{db_name}" + + unless Dir.exist?(path) + Dir.mkdir(path) + end + + save_partition_addition_amount_to_file! + save_dynamically_allocates_to_file! + File.open("#{path}/#{db_folder}/partition_amount_and_offset.json", 'w') { |f| f.write(@partition_amount_and_offset.to_json) } + File.open("#{path}/#{db_folder}/range_arr.json", 'w') { |f| f.write(@range_arr.to_json) } + File.open("#{path}/#{db_folder}/rel_arr.json", 'w') { |f| f.write(@rel_arr.to_json) } + File.open("#{path}/#{db_folder}/db_size.json", 'w') { |f| f.write(@db_size.to_json) } + # debug path + 0.upto(@db_size - 1) do |index| + FileUtils.touch("#{path}/#{db_folder}/#{@db_name}_part_#{index}.json") + File.open("#{path}/#{@db_name}_part_#{index}.json", 'w') do |f| + partition = get_partition(index) + f.write(partition.to_json) + end + end + end +end + +# rubocop:enable Layout/LineLength +# rubocop:enable Style/IfUnlessModifier +# rubocop:enable Metrics/MethodLength +# rubocop:enable Metrics/AbcSize +# rubocop:enable Metrics/CyclomaticComplexity +# rubocop:enable Metrics/PerceivedComplexity +# rubocop:enable Style/UnlessElse +# rubocop:enable Metrics/ClassLength +# rubocop:enable Style/StringLiterals +# rubocop:enable Style/ConditionalAssignment +# rubocop:enable Style/GuardClause +# rubocop:enable Style/MutableConstant, Metrics/ParameterLists, Style/ParenthesesAroundCondition, Style/NegatedIf, Lint/UnusedBlockArgument, Lint/NonAtomicFileOperation, Style/FileWrite, Style/HashSyntax, Style/IfInsideElse, Style/RedundantReturn, Style/BlockComments, Style/SymbolProc, Style/FrozenStringLiteralComment diff --git a/lib/partitioned_array.rbs b/lib/partitioned_array.rbs index 424ac7e..a716fe6 100644 --- a/lib/partitioned_array.rbs +++ b/lib/partitioned_array.rbs @@ -1,172 +1,172 @@ -# PartitionedArray class, a data structure that is partitioned at a lower level, but functions as an almost-normal array at the high level -class PartitionedArray - @label_integer: untyped - - @label_ranges: untyped - - @db_size: untyped - - @partition_amount_and_offset: untyped - - @allocated: untyped - - @db_path: untyped - - @data_arr: untyped - - @range_arr: untyped - - @rel_arr: untyped - - @db_name: untyped - - @partition_addition_amount: untyped - - @dynamically_allocates: untyped - - # puts "ids: #{ids[0]}" - @elements: untyped - - # Access individual instance variables with caution... - attr_accessor range_arr: untyped - - # Access individual instance variables with caution... - attr_accessor rel_arr: untyped - - # Access individual instance variables with caution... - attr_accessor db_size: untyped - - # Access individual instance variables with caution... - attr_accessor data_arr: untyped - - # Access individual instance variables with caution... - attr_accessor partition_amount_and_offset: untyped - - # Access individual instance variables with caution... - attr_accessor db_path: untyped - - # Access individual instance variables with caution... - attr_accessor db_name: untyped - - # Access individual instance variables with caution... - attr_accessor partition_addition_amount: untyped - - # DB_SIZE > PARTITION_AMOUNT - PARTITION_AMOUNT: 3 - - OFFSET: 1 - - DB_SIZE: 10 - - DEFAULT_PATH: "./CGMFS" - - DEBUGGING: false - - PAUSE_DEBUG: false - - DB_NAME: "partitioned_array_slice" - - PARTITION_ADDITION_AMOUNT: 1 - - DYNAMICALLY_ALLOCATES: true - - LABEL_INTEGER: false - - LABEL_RANGES: false - - def initialize: (?label_integer: untyped, ?label_ranges: untyped, ?partition_addition_amount: untyped, ?dynamically_allocates: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped) -> void - - # by definition, when you revive one element, that entire partiton is also a revenant - def revenant_partition!: (untyped partition_number) -> untyped - - def kill_partition!: (untyped partition_number) -> untyped - - # SimulWolf: I'm not sure if this is a good idea, but I'm going to try it out - # ArityWolf: fast_set is a good idea, but it will remain untested for a while, - # because it is not used in the codebase yet. - # SimulWolf: I'm going to use it in the codebase - # ArityWolf: I'm going to use it in the codebase - # ArityWolf: But seriously, let's play some Everquest on the REAL LIVE server; so much to explore as a druid... - def fast_set: (untyped id, untyped data_hash) -> (untyped | untyped | nil | nil) - - def fast_get: (untyped id) -> (untyped | untyped | nil | nil) - - # 2/7/2023 10:39AM - # examine closely later (also: this was never imnplemented in DragonRuby's PartitionedArray classes) - def []: (*untyped ids, ?hash: bool, ?label_ranges: untyped, ?label_integer: untyped) -> (untyped | ::Hash[untyped, untyped]) - - def range_db_get: (untyped range_arr, untyped db_num) -> untyped - - def debug: (untyped string) -> (untyped | nil) - - def delete: (untyped id) -> untyped - - def load_dynamically_allocates_from_file!: () -> untyped - - def save_dynamically_allocates_to_file!: () -> untyped - - def delete_partition_subelement: (untyped id, untyped partition_id) -> untyped - - def set_partition_subelement: (untyped id, untyped partition_id) ?{ (?) -> untyped } -> untyped - - # delete the partition's array named partition, by id - # loopy, non-trivial code that may not work as intended, - # but works for binary space partitioning - def delete_partition!: (untyped partition_id) -> untyped - - def get_partition: (untyped partition_id) -> untyped - - # TODO: Class#set(integer id) -> boolean - # Will yield a hash to some element id within the data array, to which you could use a block and modify said data - def set: (untyped id) ?{ (?) -> untyped } -> (untyped | nil) - - # We define the add_left routine as starting from the end of @data_arr, and working the way back - # until we find the first element that is nilm if no elements return nil, then return nil as well - def add: (?return_added_element_id: bool) ?{ (?) -> untyped } -> untyped - - def add_nosave: (?return_added_element_id: bool) ?{ (?) -> untyped } -> untyped - - # add_immediate - # adds directly to @data_arr; PartitionedArray loses its place and nothing will work right unless the data structure is patched live - def add_imm: (?return_added_element_id: bool) { (?) -> untyped } -> nil - - # create an initial database (instance variable) - # later on, make this so it will load a database if its there, and if there's no data, create a standard database then save it - # Set to work with the default constants - def allocate: (?db_size: untyped, ?partition_amount_and_offset: untyped, ?override: bool) -> untyped - - # Returns a hash based on the array element you are searching for. - # Haven't done much research afterwards on the idea of - # whether the binary search is necessary, but it seems like it is. - # The idea first came up during the research of the partitioned array equation. - def get: (untyped id, ?hash: bool) -> (nil | untyped) - - def add_partition: () -> untyped - - def string2range: (untyped range_string) -> ::Range[untyped] - - def debug_and_pause: (untyped message) -> untyped - - # loads the files within the directory CGMFS/partitioned_array_slice - # needed things - # range_arr => it is the array of ranges that the database is divided into - # data_arr => it is output to json - # rel_arr => it is output to json - # part_} => it is taken from the range_arr subdivisions; perform a map and load it into the database, one by one - def load_from_files!: () -> untyped - - def all_empty_partitions?: () -> untyped - - def dup_data_arr!: () -> untyped - - # Plan: be able to dump @data_arr to disk anytime you want - def load_partition_from_file!: (untyped partition_id) -> untyped - - def save_partition_to_file!: (untyped partition_id, ?db_folder: untyped) -> untyped - - def save_partition_addition_amount_to_file!: (?db_folder: untyped) -> untyped - - def load_partition_addition_amount_from_file!: (?db_folder: untyped) -> untyped - - def save_all_to_files!: (?db_folder: untyped, ?db_path: untyped, ?db_name: untyped) -> untyped -end +# PartitionedArray class, a data structure that is partitioned at a lower level, but functions as an almost-normal array at the high level +class PartitionedArray + @label_integer: untyped + + @label_ranges: untyped + + @db_size: untyped + + @partition_amount_and_offset: untyped + + @allocated: untyped + + @db_path: untyped + + @data_arr: untyped + + @range_arr: untyped + + @rel_arr: untyped + + @db_name: untyped + + @partition_addition_amount: untyped + + @dynamically_allocates: untyped + + # puts "ids: #{ids[0]}" + @elements: untyped + + # Access individual instance variables with caution... + attr_accessor range_arr: untyped + + # Access individual instance variables with caution... + attr_accessor rel_arr: untyped + + # Access individual instance variables with caution... + attr_accessor db_size: untyped + + # Access individual instance variables with caution... + attr_accessor data_arr: untyped + + # Access individual instance variables with caution... + attr_accessor partition_amount_and_offset: untyped + + # Access individual instance variables with caution... + attr_accessor db_path: untyped + + # Access individual instance variables with caution... + attr_accessor db_name: untyped + + # Access individual instance variables with caution... + attr_accessor partition_addition_amount: untyped + + # DB_SIZE > PARTITION_AMOUNT + PARTITION_AMOUNT: 3 + + OFFSET: 1 + + DB_SIZE: 10 + + DEFAULT_PATH: "./CGMFS" + + DEBUGGING: false + + PAUSE_DEBUG: false + + DB_NAME: "partitioned_array_slice" + + PARTITION_ADDITION_AMOUNT: 1 + + DYNAMICALLY_ALLOCATES: true + + LABEL_INTEGER: false + + LABEL_RANGES: false + + def initialize: (?label_integer: untyped, ?label_ranges: untyped, ?partition_addition_amount: untyped, ?dynamically_allocates: untyped, ?db_size: untyped, ?partition_amount_and_offset: untyped, ?db_path: untyped, ?db_name: untyped) -> void + + # by definition, when you revive one element, that entire partiton is also a revenant + def revenant_partition!: (untyped partition_number) -> untyped + + def kill_partition!: (untyped partition_number) -> untyped + + # SimulWolf: I'm not sure if this is a good idea, but I'm going to try it out + # ArityWolf: fast_set is a good idea, but it will remain untested for a while, + # because it is not used in the codebase yet. + # SimulWolf: I'm going to use it in the codebase + # ArityWolf: I'm going to use it in the codebase + # ArityWolf: But seriously, let's play some Everquest on the REAL LIVE server; so much to explore as a druid... + def fast_set: (untyped id, untyped data_hash) -> (untyped | untyped | nil | nil) + + def fast_get: (untyped id) -> (untyped | untyped | nil | nil) + + # 2/7/2023 10:39AM + # examine closely later (also: this was never imnplemented in DragonRuby's PartitionedArray classes) + def []: (*untyped ids, ?hash: bool, ?label_ranges: untyped, ?label_integer: untyped) -> (untyped | ::Hash[untyped, untyped]) + + def range_db_get: (untyped range_arr, untyped db_num) -> untyped + + def debug: (untyped string) -> (untyped | nil) + + def delete: (untyped id) -> untyped + + def load_dynamically_allocates_from_file!: () -> untyped + + def save_dynamically_allocates_to_file!: () -> untyped + + def delete_partition_subelement: (untyped id, untyped partition_id) -> untyped + + def set_partition_subelement: (untyped id, untyped partition_id) ?{ (?) -> untyped } -> untyped + + # delete the partition's array named partition, by id + # loopy, non-trivial code that may not work as intended, + # but works for binary space partitioning + def delete_partition!: (untyped partition_id) -> untyped + + def get_partition: (untyped partition_id) -> untyped + + # TODO: Class#set(integer id) -> boolean + # Will yield a hash to some element id within the data array, to which you could use a block and modify said data + def set: (untyped id) ?{ (?) -> untyped } -> (untyped | nil) + + # We define the add_left routine as starting from the end of @data_arr, and working the way back + # until we find the first element that is nilm if no elements return nil, then return nil as well + def add: (?return_added_element_id: bool) ?{ (?) -> untyped } -> untyped + + def add_nosave: (?return_added_element_id: bool) ?{ (?) -> untyped } -> untyped + + # add_immediate + # adds directly to @data_arr; PartitionedArray loses its place and nothing will work right unless the data structure is patched live + def add_imm: (?return_added_element_id: bool) { (?) -> untyped } -> nil + + # create an initial database (instance variable) + # later on, make this so it will load a database if its there, and if there's no data, create a standard database then save it + # Set to work with the default constants + def allocate: (?db_size: untyped, ?partition_amount_and_offset: untyped, ?override: bool) -> untyped + + # Returns a hash based on the array element you are searching for. + # Haven't done much research afterwards on the idea of + # whether the binary search is necessary, but it seems like it is. + # The idea first came up during the research of the partitioned array equation. + def get: (untyped id, ?hash: bool) -> (nil | untyped) + + def add_partition: () -> untyped + + def string2range: (untyped range_string) -> ::Range[untyped] + + def debug_and_pause: (untyped message) -> untyped + + # loads the files within the directory CGMFS/partitioned_array_slice + # needed things + # range_arr => it is the array of ranges that the database is divided into + # data_arr => it is output to json + # rel_arr => it is output to json + # part_} => it is taken from the range_arr subdivisions; perform a map and load it into the database, one by one + def load_from_files!: () -> untyped + + def all_empty_partitions?: () -> untyped + + def dup_data_arr!: () -> untyped + + # Plan: be able to dump @data_arr to disk anytime you want + def load_partition_from_file!: (untyped partition_id) -> untyped + + def save_partition_to_file!: (untyped partition_id, ?db_folder: untyped) -> untyped + + def save_partition_addition_amount_to_file!: (?db_folder: untyped) -> untyped + + def load_partition_addition_amount_from_file!: (?db_folder: untyped) -> untyped + + def save_all_to_files!: (?db_folder: untyped, ?db_path: untyped, ?db_name: untyped) -> untyped +end diff --git a/lib/partitioned_array_database.rbs b/lib/partitioned_array_database.rbs index 3fd0ce8..dbc9daa 100644 --- a/lib/partitioned_array_database.rbs +++ b/lib/partitioned_array_database.rbs @@ -1,71 +1,71 @@ -class PartitionedArrayDatabase - @database_folder_name: untyped - - @endless_add: untyped - - @has_capacity: untyped - - @db_size: untyped - - @partition_amount: untyped - - @dynamically_allocates: untyped - - @traverse_hash: untyped - - @label_integer: untyped - - @label_ranges: untyped - - @partition_addition_amount: untyped - - # puts @partition_addition_amount - @pad: untyped - - attr_accessor database_folder_name: untyped - - attr_accessor pad: untyped - - FCMPAM_DB_INDEX_NAME: "FCMPAM_DB_INDEX" - - DB_NAME: "FCMPAM_DB" - - PARTITION_AMOUNT: 20 - - ENDLESS_ADD: true - - HAS_CAPACITY: true - - DB_SIZE: 200 - - DYNAMICALLY_ALLOCATES: true - - TRAVERSE_HASH: true - - PARTITION_ADDITION_AMOUNT: 15 - - LABEL_INTEGER: false - - LABEL_RANGES: false - - DATABASE_FOLDER_NAME: "./default" - - FCMPAM: untyped - - # For a change of database variables, check the file constants in the file_context_managed_partitioned_array_manager.rb library, etc. - def initialize: (?partition_addition_amount: untyped, ?label_integer: untyped, ?label_ranges: untyped, ?traverse_hash: untyped, ?partition_amount: untyped, ?database_folder_name: untyped, ?endless_add: untyped, ?has_capacity: untyped, ?dynamically_allocates: untyped, ?db_size: untyped) -> void - - def pad: () -> untyped - - alias db pad - - alias DB pad - - alias PAD pad - - alias d pad - - alias D pad -end - -PAD: untyped +class PartitionedArrayDatabase + @database_folder_name: untyped + + @endless_add: untyped + + @has_capacity: untyped + + @db_size: untyped + + @partition_amount: untyped + + @dynamically_allocates: untyped + + @traverse_hash: untyped + + @label_integer: untyped + + @label_ranges: untyped + + @partition_addition_amount: untyped + + # puts @partition_addition_amount + @pad: untyped + + attr_accessor database_folder_name: untyped + + attr_accessor pad: untyped + + FCMPAM_DB_INDEX_NAME: "FCMPAM_DB_INDEX" + + DB_NAME: "FCMPAM_DB" + + PARTITION_AMOUNT: 20 + + ENDLESS_ADD: true + + HAS_CAPACITY: true + + DB_SIZE: 200 + + DYNAMICALLY_ALLOCATES: true + + TRAVERSE_HASH: true + + PARTITION_ADDITION_AMOUNT: 15 + + LABEL_INTEGER: false + + LABEL_RANGES: false + + DATABASE_FOLDER_NAME: "./default" + + FCMPAM: untyped + + # For a change of database variables, check the file constants in the file_context_managed_partitioned_array_manager.rb library, etc. + def initialize: (?partition_addition_amount: untyped, ?label_integer: untyped, ?label_ranges: untyped, ?traverse_hash: untyped, ?partition_amount: untyped, ?database_folder_name: untyped, ?endless_add: untyped, ?has_capacity: untyped, ?dynamically_allocates: untyped, ?db_size: untyped) -> void + + def pad: () -> untyped + + alias db pad + + alias DB pad + + alias PAD pad + + alias d pad + + alias D pad +end + +PAD: untyped diff --git a/lib/rbs_dir.rb b/lib/rbs_dir.rb index d04ae86..3e44b54 100644 --- a/lib/rbs_dir.rb +++ b/lib/rbs_dir.rb @@ -1,19 +1,19 @@ -require 'fileutils' - -# Define the directory to glob -directory = './' - -# Get all Ruby files in the directory -ruby_files = Dir.glob("#{directory}/**/*.rb") - -# Create a directory for RBS files if it doesn't exist -rbs_directory = './' -FileUtils.mkdir_p(rbs_directory) - -# Iterate over each Ruby file and generate RBS files -ruby_files.each do |file| - rbs_file = File.join(rbs_directory, "#{File.basename(file, '.rb')}.rbs") - system("rbs prototype rb #{file} > #{rbs_file}") -end - -puts "RBS files generated in #{rbs_directory}" +require 'fileutils' + +# Define the directory to glob +directory = './' + +# Get all Ruby files in the directory +ruby_files = Dir.glob("#{directory}/**/*.rb") + +# Create a directory for RBS files if it doesn't exist +rbs_directory = './' +FileUtils.mkdir_p(rbs_directory) + +# Iterate over each Ruby file and generate RBS files +ruby_files.each do |file| + rbs_file = File.join(rbs_directory, "#{File.basename(file, '.rb')}.rbs") + system("rbs prototype rb #{file} > #{rbs_file}") +end + +puts "RBS files generated in #{rbs_directory}" diff --git a/lib/red_black_trees_algorithm.rb b/lib/red_black_trees_algorithm.rb index 8c3080e..15d555b 100644 --- a/lib/red_black_trees_algorithm.rb +++ b/lib/red_black_trees_algorithm.rb @@ -1,190 +1,190 @@ -# Node structure for the Red-Black Tree -class Node - attr_accessor :data, :color, :left, :right, :parent, :object - - def initialize(data) - @data = data - @color = "RED" - @object = nil - @left = nil - @right = nil - @parent = nil - end -end - -# Red-Black Tree class -class RedBlackTree - def initialize - @nil = Node.new(0) - @nil.color = "BLACK" - @nil.left = @nil - @nil.right = @nil - @root = @nil - end - - # Utility function to perform left rotation - def left_rotate(x) - y = x.right - x.right = y.left - if y.left != @nil - y.left.parent = x - end - y.parent = x.parent - if x.parent == nil - @root = y - elsif x == x.parent.left - x.parent.left = y - else - x.parent.right = y - end - y.left = x - x.parent = y - end - - # Utility function to perform right rotation - def right_rotate(x) - y = x.left - x.left = y.right - if y.right != @nil - y.right.parent = x - end - y.parent = x.parent - if x.parent == nil - @root = y - elsif x == x.parent.right - x.parent.right = y - else - x.parent.left = y - end - y.right = x - x.parent = y - end - - # Function to fix Red-Black Tree properties after - # insertion - def fix_insert(k) - while k != @root && k.parent.color == "RED" - if k.parent == k.parent.parent.left - u = k.parent.parent.right # uncle - if u.color == "RED" - k.parent.color = "BLACK" - u.color = "BLACK" - k.parent.parent.color = "RED" - k = k.parent.parent - else - if k == k.parent.right - k = k.parent - left_rotate(k) - end - k.parent.color = "BLACK" - k.parent.parent.color = "RED" - right_rotate(k.parent.parent) - end - else - u = k.parent.parent.left # uncle - if u.color == "RED" - k.parent.color = "BLACK" - u.color = "BLACK" - k.parent.parent.color = "RED" - k = k.parent.parent - else - if k == k.parent.left - k = k.parent - right_rotate(k) - end - k.parent.color = "BLACK" - k.parent.parent.color = "RED" - left_rotate(k.parent.parent) - end - end - end - @root.color = "BLACK" - end - - # Inorder traversal helper function - def inorder_helper(node) - if node != @nil - inorder_helper(node.left) - puts node.data.to_s + " " - inorder_helper(node.right) - end - end - - # Search helper function - def search_helper(node, data) - if node == @nil || data == node.data - return node - end - if data < node.data - return search_helper(node.left, data) - end - return search_helper(node.right, data) - end - - # Insert function - def insert(data) - new_node = Node.new(data) - new_node.left = @nil - new_node.right = @nil - - parent = nil - current = @root - - # BST insert - while current != @nil - parent = current - if new_node.data < current.data - current = current.left - else - current = current.right - end - end - - new_node.parent = parent - - if parent == nil - @root = new_node - elsif new_node.data < parent.data - parent.left = new_node - else - parent.right = new_node - end - - if new_node.parent == nil - new_node.color = "BLACK" - return - end - - if new_node.parent.parent == nil - return - end - - fix_insert(new_node) - end - - # Inorder traversal - def inorder - inorder_helper(@root) - end - - # Search function - def search(data) - search_helper(@root, data) - end -end - -rbt = RedBlackTree.new - -# Inserting elements -rbt.insert(10) -rbt.insert(20) -rbt.insert(30) -rbt.insert(15) - -# Inorder traversal -puts "Inorder traversal:" -rbt.inorder # Output: 10 15 20 30 - -# Search for a node -puts "\nSearch for 15: " + (rbt.search(15) != rbt.search(0)).to_s # Output: true -puts "Search for 25: " + (rbt.search(25) != rbt.search(0)).to_s # Output: false +# Node structure for the Red-Black Tree +class Node + attr_accessor :data, :color, :left, :right, :parent, :object + + def initialize(data) + @data = data + @color = "RED" + @object = nil + @left = nil + @right = nil + @parent = nil + end +end + +# Red-Black Tree class +class RedBlackTree + def initialize + @nil = Node.new(0) + @nil.color = "BLACK" + @nil.left = @nil + @nil.right = @nil + @root = @nil + end + + # Utility function to perform left rotation + def left_rotate(x) + y = x.right + x.right = y.left + if y.left != @nil + y.left.parent = x + end + y.parent = x.parent + if x.parent == nil + @root = y + elsif x == x.parent.left + x.parent.left = y + else + x.parent.right = y + end + y.left = x + x.parent = y + end + + # Utility function to perform right rotation + def right_rotate(x) + y = x.left + x.left = y.right + if y.right != @nil + y.right.parent = x + end + y.parent = x.parent + if x.parent == nil + @root = y + elsif x == x.parent.right + x.parent.right = y + else + x.parent.left = y + end + y.right = x + x.parent = y + end + + # Function to fix Red-Black Tree properties after + # insertion + def fix_insert(k) + while k != @root && k.parent.color == "RED" + if k.parent == k.parent.parent.left + u = k.parent.parent.right # uncle + if u.color == "RED" + k.parent.color = "BLACK" + u.color = "BLACK" + k.parent.parent.color = "RED" + k = k.parent.parent + else + if k == k.parent.right + k = k.parent + left_rotate(k) + end + k.parent.color = "BLACK" + k.parent.parent.color = "RED" + right_rotate(k.parent.parent) + end + else + u = k.parent.parent.left # uncle + if u.color == "RED" + k.parent.color = "BLACK" + u.color = "BLACK" + k.parent.parent.color = "RED" + k = k.parent.parent + else + if k == k.parent.left + k = k.parent + right_rotate(k) + end + k.parent.color = "BLACK" + k.parent.parent.color = "RED" + left_rotate(k.parent.parent) + end + end + end + @root.color = "BLACK" + end + + # Inorder traversal helper function + def inorder_helper(node) + if node != @nil + inorder_helper(node.left) + puts node.data.to_s + " " + inorder_helper(node.right) + end + end + + # Search helper function + def search_helper(node, data) + if node == @nil || data == node.data + return node + end + if data < node.data + return search_helper(node.left, data) + end + return search_helper(node.right, data) + end + + # Insert function + def insert(data) + new_node = Node.new(data) + new_node.left = @nil + new_node.right = @nil + + parent = nil + current = @root + + # BST insert + while current != @nil + parent = current + if new_node.data < current.data + current = current.left + else + current = current.right + end + end + + new_node.parent = parent + + if parent == nil + @root = new_node + elsif new_node.data < parent.data + parent.left = new_node + else + parent.right = new_node + end + + if new_node.parent == nil + new_node.color = "BLACK" + return + end + + if new_node.parent.parent == nil + return + end + + fix_insert(new_node) + end + + # Inorder traversal + def inorder + inorder_helper(@root) + end + + # Search function + def search(data) + search_helper(@root, data) + end +end + +rbt = RedBlackTree.new + +# Inserting elements +rbt.insert(10) +rbt.insert(20) +rbt.insert(30) +rbt.insert(15) + +# Inorder traversal +puts "Inorder traversal:" +rbt.inorder # Output: 10 15 20 30 + +# Search for a node +puts "\nSearch for 15: " + (rbt.search(15) != rbt.search(0)).to_s # Output: true +puts "Search for 25: " + (rbt.search(25) != rbt.search(0)).to_s # Output: false diff --git a/lib/red_black_trees_algorithm.rbs b/lib/red_black_trees_algorithm.rbs index 9604074..841b8b7 100644 --- a/lib/red_black_trees_algorithm.rbs +++ b/lib/red_black_trees_algorithm.rbs @@ -1,62 +1,62 @@ -# Node structure for the Red-Black Tree -class Node - @data: untyped - - @color: untyped - - @object: untyped - - @left: untyped - - @right: untyped - - @parent: untyped - - attr_accessor data: untyped - - attr_accessor color: untyped - - attr_accessor left: untyped - - attr_accessor right: untyped - - attr_accessor parent: untyped - - attr_accessor object: untyped - - def initialize: (untyped data) -> void -end - -# Red-Black Tree class -class RedBlackTree - @nil: untyped - - @root: untyped - - def initialize: () -> void - - # Utility function to perform left rotation - def left_rotate: (untyped x) -> untyped - - # Utility function to perform right rotation - def right_rotate: (untyped x) -> untyped - - # Function to fix Red-Black Tree properties after - # insertion - def fix_insert: (untyped k) -> untyped - - # Inorder traversal helper function - def inorder_helper: (untyped node) -> (untyped | nil) - - # Search helper function - def search_helper: (untyped node, untyped data) -> untyped - - # Insert function - def insert: (untyped data) -> (nil | untyped) - - # Inorder traversal - def inorder: () -> untyped - - # Search function - def search: (untyped data) -> untyped -end +# Node structure for the Red-Black Tree +class Node + @data: untyped + + @color: untyped + + @object: untyped + + @left: untyped + + @right: untyped + + @parent: untyped + + attr_accessor data: untyped + + attr_accessor color: untyped + + attr_accessor left: untyped + + attr_accessor right: untyped + + attr_accessor parent: untyped + + attr_accessor object: untyped + + def initialize: (untyped data) -> void +end + +# Red-Black Tree class +class RedBlackTree + @nil: untyped + + @root: untyped + + def initialize: () -> void + + # Utility function to perform left rotation + def left_rotate: (untyped x) -> untyped + + # Utility function to perform right rotation + def right_rotate: (untyped x) -> untyped + + # Function to fix Red-Black Tree properties after + # insertion + def fix_insert: (untyped k) -> untyped + + # Inorder traversal helper function + def inorder_helper: (untyped node) -> (untyped | nil) + + # Search helper function + def search_helper: (untyped node, untyped data) -> untyped + + # Insert function + def insert: (untyped data) -> (nil | untyped) + + # Inorder traversal + def inorder: () -> untyped + + # Search function + def search: (untyped data) -> untyped +end diff --git a/lib/support_modules.rb b/lib/support_modules.rb index 0143a1c..6466c10 100644 --- a/lib/support_modules.rb +++ b/lib/support_modules.rb @@ -1,10 +1,10 @@ -# Managers -module LineDatabaseFactory - def self.line_db - line_db = LineDB.new - end -end - -class FileMethodsManager - include FileMethods +# Managers +module LineDatabaseFactory + def self.line_db + line_db = LineDB.new + end +end + +class FileMethodsManager + include FileMethods end \ No newline at end of file diff --git a/lib/support_modules.rbs b/lib/support_modules.rbs index 2a5b72c..4acac2b 100644 --- a/lib/support_modules.rbs +++ b/lib/support_modules.rbs @@ -1,8 +1,8 @@ -# Managers -module LineDatabaseFactory - def self.line_db: () -> untyped -end - -class FileMethodsManager - include FileMethods -end +# Managers +module LineDatabaseFactory + def self.line_db: () -> untyped +end + +class FileMethodsManager + include FileMethods +end diff --git a/rehash.rb b/rehash.rb index 4abd21b..d48be9d 100644 --- a/rehash.rb +++ b/rehash.rb @@ -1,12 +1,12 @@ -require_relative "lib/line_db" - - -test = LineDB.new -test["test"].pad.new_table!(database_name: "test", database_table: "test") - -test["test"].pad["test", "test"].add do |hash| - hash["test"] = "test" - end - - test["test"].pad["test", "test"].rehasher! - test["test"].pad["test", "test"].save_everything_to_files! +require_relative "lib/line_db" + + +test = LineDB.new +test["test"].pad.new_table!(database_name: "test", database_table: "test") + +test["test"].pad["test", "test"].add do |hash| + hash["test"] = "test" + end + + test["test"].pad["test", "test"].rehasher! + test["test"].pad["test", "test"].save_everything_to_files!