class Object
  # This helper is defined here rather than in MSpec because
  # it is only used in #pack specs.
  def pack_format(count=nil, repeat=nil)
    format = instance_variable_get(:@method)
    format += count.to_s unless format == 'P' || format == 'p'
    format *= repeat if repeat
    format.dup # because it may then become tainted
  end
end

module ArraySpecs
  SampleRange = 0..1000
  SampleCount = 1000

  def self.frozen_array
    [1,2,3].freeze
  end

  def self.empty_frozen_array
    [].freeze
  end

  def self.recursive_array
    a = [1, 'two', 3.0]
    5.times { a << a }
    a
  end

  def self.head_recursive_array
    a =  []
    5.times { a << a }
    a << 1 << 'two' << 3.0
    a
  end

  def self.empty_recursive_array
    a = []
    a << a
    a
  end

  # Chi squared critical values for tests with n degrees of freedom at 99% confidence.
  # Values obtained from NIST Engineering Statistic Handbook at
  # https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm

  CHI_SQUARED_CRITICAL_VALUES = [
    0,
    6.635, 9.210, 11.345, 13.277, 15.086, 16.812, 18.475, 20.090, 21.666, 23.209,
    24.725, 26.217, 27.688, 29.141, 30.578, 32.000, 33.409, 34.805, 36.191, 37.566,
    38.932, 40.289, 41.638, 42.980, 44.314, 45.642, 46.963, 48.278, 49.588, 50.892,
    52.191, 53.486, 54.776, 56.061, 57.342, 58.619, 59.893, 61.162, 62.428, 63.691,
    64.950, 66.206, 67.459, 68.710, 69.957, 71.201, 72.443, 73.683, 74.919, 76.154,
    77.386, 78.616, 79.843, 81.069, 82.292, 83.513, 84.733, 85.950, 87.166, 88.379,
    89.591, 90.802, 92.010, 93.217, 94.422, 95.626, 96.828, 98.028, 99.228, 100.425,
    101.621, 102.816, 104.010, 105.202, 106.393, 107.583, 108.771, 109.958, 111.144, 112.329,
    113.512, 114.695, 115.876, 117.057, 118.236, 119.414, 120.591, 121.767, 122.942, 124.116,
    125.289, 126.462, 127.633, 128.803, 129.973, 131.141, 132.309, 133.476, 134.642, 135.807,
  ]

  def self.measure_sample_fairness(size, samples, iters)
    ary = Array.new(size) { |x| x }
    expected = iters.fdiv size
    (samples).times do |i|
      chi_results = []
      3.times do
        counts = Array.new(size, 0)
        iters.times do
          x = ary.sample(samples)[i]
          counts[x] += 1
        end
        chi_squared = counts.sum {|count| (count - expected) ** 2} / expected
        chi_results << chi_squared
        break if chi_squared <= CHI_SQUARED_CRITICAL_VALUES[size]
      end

      chi_results.min.should <= CHI_SQUARED_CRITICAL_VALUES[size]
    end
  end

  def self.measure_sample_fairness_large_sample_size(size, samples, iters)
    ary = Array.new(size) { |x| x }
    counts = Array.new(size, 0)
    expected = (iters * samples).fdiv size
    iters.times do
      ary.sample(samples).each do |sample|
        counts[sample] += 1
      end
    end
    chi_squared = counts.sum {|count| (count - expected) ** 2} / expected

    # Chi squared critical values for tests with 4 degrees of freedom
    # Values obtained from NIST Engineering Statistic Handbook at
    # https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm

    chi_squared.should <= CHI_SQUARED_CRITICAL_VALUES[size]
  end

  class MyArray < Array
    # The #initialize method has a different signature than Array to help
    # catch places in the specs that do not assert the #initialize is not
    # called when Array methods make new instances.
    def initialize(a, b)
      self << a << b
      ScratchPad.record :my_array_initialize
    end
  end

  class Sexp < Array
    def initialize(*args)
      super(args)
    end
  end

  # TODO: replace specs that use this with #should_not_receive(:to_ary)
  # expectations on regular objects (e.g. Array instances).
  class ToAryArray < Array
    def to_ary() ["to_ary", "was", "called!"] end
  end

  class MyRange < Range; end

  class AssocKey
    def ==(other); other == 'it'; end
  end

  class D
    def <=>(obj)
      return 4 <=> obj unless obj.class == D
      0
    end
  end

  class SubArray < Array
    def initialize(*args)
      ScratchPad.record args
    end
  end

  class ArrayConvertible
    attr_accessor :called
    def initialize(*values, &block)
      @values = values;
    end

    def to_a
      self.called = :to_a
      @values
    end

    def to_ary
      self.called = :to_ary
      @values
    end
  end

  class ArrayMethodMissing
    def initialize(*values, &block)
      @values = values;
    end

    def method_missing(name, *args)
      @values
    end
  end

  class SortSame
    def <=>(other); 0; end
    def ==(other); true; end
  end

  class UFOSceptic
    def <=>(other); raise "N-uh, UFO:s do not exist!"; end
  end

  class MockForCompared
    @@count = 0
    @@compared = false
    def initialize
      @@compared = false
      @order = (@@count += 1)
    end
    def <=>(rhs)
      @@compared = true
      return rhs.order <=> self.order
    end
    def self.compared?
      @@compared
    end

    protected
    attr_accessor :order
  end

  class ComparableWithInteger
    include Comparable
    def initialize(num)
      @num = num
    end

    def <=>(fixnum)
      @num <=> fixnum
    end
  end

  class Uncomparable
    def <=>(obj)
      nil
    end
  end

  def self.universal_pack_object
    obj = mock("string float int".freeze)
    obj.stub!(:to_int).and_return(1)
    obj.stub!(:to_str).and_return("1")
    obj.stub!(:to_f).and_return(1.0)
    obj
  end

  LargeArray = [
    "test_create_table_with_force_true_does_not_drop_nonexisting_table",
    "test_add_table",
    "assert_difference",
    "assert_operator",
    "instance_variables",
    "class",
    "instance_variable_get",
    "__class__",
    "expects",
    "assert_no_difference",
    "name",
    "assert_blank",
    "assert_not_same",
    "is_a?",
    "test_add_table_with_decimals",
    "test_create_table_with_timestamps_should_create_datetime_columns",
    "assert_present",
    "assert_no_match",
    "__instance_of__",
    "assert_deprecated",
    "assert",
    "assert_throws",
    "kind_of?",
    "try",
    "__instance_variable_get__",
    "object_id",
    "timeout",
    "instance_variable_set",
    "assert_nothing_thrown",
    "__instance_variable_set__",
    "copy_object",
    "test_create_table_with_timestamps_should_create_datetime_columns_with_options",
    "assert_not_deprecated",
    "assert_in_delta",
    "id",
    "copy_metaclass",
    "test_create_table_without_a_block",
    "dup",
    "assert_not_nil",
    "send",
    "__instance_variables__",
    "to_sql",
    "mock",
    "assert_send",
    "instance_variable_defined?",
    "clone",
    "require",
    "test_migrator",
    "__instance_variable_defined_eh__",
    "frozen?",
    "test_add_column_not_null_with_default",
    "freeze",
    "test_migrator_one_up",
    "test_migrator_one_down",
    "singleton_methods",
    "method_exists?",
    "create_fixtures",
    "test_migrator_one_up_one_down",
    "test_native_decimal_insert_manual_vs_automatic",
    "instance_exec",
    "__is_a__",
    "test_migrator_double_up",
    "stub",
    "private_methods",
    "stubs",
    "test_migrator_double_down",
    "fixture_path",
    "private_singleton_methods",
    "stub_everything",
    "test_migrator_one_up_with_exception_and_rollback",
    "sequence",
    "protected_methods",
    "enum_for",
    "test_finds_migrations",
    "run_before_mocha",
    "states",
    "protected_singleton_methods",
    "to_json",
    "instance_values",
    "==",
    "mocha_setup",
    "public_methods",
    "test_finds_pending_migrations",
    "mocha_verify",
    "assert_kind_of",
    "===",
    "=~",
    "test_relative_migrations",
    "mocha_teardown",
    "gem",
    "mocha",
    "test_only_loads_pending_migrations",
    "test_add_column_with_precision_and_scale",
    "require_or_load",
    "eql?",
    "require_dependency",
    "test_native_types",
    "test_target_version_zero_should_run_only_once",
    "extend",
    "to_matcher",
    "unloadable",
    "require_association",
    "hash",
    "__id__",
    "load_dependency",
    "equals",
    "test_migrator_db_has_no_schema_migrations_table",
    "test_migrator_verbosity",
    "kind_of",
    "to_yaml",
    "to_bool",
    "test_migrator_verbosity_off",
    "taint",
    "test_migrator_going_down_due_to_version_target",
    "tainted?",
    "mocha_inspect",
    "test_migrator_rollback",
    "vim",
    "untaint",
    "taguri=",
    "test_migrator_forward",
    "test_schema_migrations_table_name",
    "test_proper_table_name",
    "all_of",
    "test_add_drop_table_with_prefix_and_suffix",
    "_setup_callbacks",
    "setup",
    "Not",
    "test_create_table_with_binary_column",
    "assert_not_equal",
    "enable_warnings",
    "acts_like?",
    "Rational",
    "_removed_setup_callbacks",
    "Table",
    "bind",
    "any_of",
    "__method__",
    "test_migrator_with_duplicates",
    "_teardown_callbacks",
    "method",
    "test_migrator_with_duplicate_names",
    "_removed_teardown_callbacks",
    "any_parameters",
    "test_migrator_with_missing_version_numbers",
    "test_add_remove_single_field_using_string_arguments",
    "test_create_table_with_custom_sequence_name",
    "test_add_remove_single_field_using_symbol_arguments",
    "_one_time_conditions_valid_14?",
    "_one_time_conditions_valid_16?",
    "run_callbacks",
    "anything",
    "silence_warnings",
    "instance_variable_names",
    "_fixture_path",
    "copy_instance_variables_from",
    "fixture_path?",
    "has_entry",
    "__marshal__",
    "_fixture_table_names",
    "__kind_of__",
    "fixture_table_names?",
    "test_add_rename",
    "assert_equal",
    "_fixture_class_names",
    "fixture_class_names?",
    "has_entries",
    "_use_transactional_fixtures",
    "people",
    "test_rename_column_using_symbol_arguments",
    "use_transactional_fixtures?",
    "instance_eval",
    "blank?",
    "with_warnings",
    "__nil__",
    "load",
    "metaclass",
    "_use_instantiated_fixtures",
    "has_key",
    "class_eval",
    "present?",
    "test_rename_column",
    "teardown",
    "use_instantiated_fixtures?",
    "method_name",
    "silence_stderr",
    "presence",
    "test_rename_column_preserves_default_value_not_null",
    "silence_stream",
    "_pre_loaded_fixtures",
    "__metaclass__",
    "__fixnum__",
    "pre_loaded_fixtures?",
    "has_value",
    "suppress",
    "to_yaml_properties",
    "test_rename_nonexistent_column",
    "test_add_index",
    "includes",
    "find_correlate_in",
    "equality_predicate_sql",
    "assert_nothing_raised",
    "let",
    "not_predicate_sql",
    "test_rename_column_with_sql_reserved_word",
    "singleton_class",
    "test_rename_column_with_an_index",
    "display",
    "taguri",
    "to_yaml_style",
    "test_remove_column_with_index",
    "size",
    "current_adapter?",
    "test_remove_column_with_multi_column_index",
    "respond_to?",
    "test_change_type_of_not_null_column",
    "is_a",
    "to_a",
    "test_rename_table_for_sqlite_should_work_with_reserved_words",
    "require_library_or_gem",
    "setup_fixtures",
    "equal?",
    "teardown_fixtures",
    "nil?",
    "fixture_table_names",
    "fixture_class_names",
    "test_create_table_without_id",
    "use_transactional_fixtures",
    "test_add_column_with_primary_key_attribute",
    "repair_validations",
    "use_instantiated_fixtures",
    "instance_of?",
    "test_create_table_adds_id",
    "test_rename_table",
    "pre_loaded_fixtures",
    "to_enum",
    "test_create_table_with_not_null_column",
    "instance_of",
    "test_change_column_nullability",
    "optionally",
    "test_rename_table_with_an_index",
    "run",
    "test_change_column",
    "default_test",
    "assert_raise",
    "test_create_table_with_defaults",
    "assert_nil",
    "flunk",
    "regexp_matches",
    "duplicable?",
    "reset_mocha",
    "stubba_method",
    "filter_backtrace",
    "test_create_table_with_limits",
    "responds_with",
    "stubba_object",
    "test_change_column_with_nil_default",
    "assert_block",
    "__show__",
    "assert_date_from_db",
    "__respond_to_eh__",
    "run_in_transaction?",
    "inspect",
    "assert_sql",
    "test_change_column_with_new_default",
    "yaml_equivalent",
    "build_message",
    "to_s",
    "test_change_column_default",
    "assert_queries",
    "pending",
    "as_json",
    "assert_no_queries",
    "test_change_column_quotes_column_names",
    "assert_match",
    "test_keeping_default_and_notnull_constraint_on_change",
    "methods",
    "connection_allow_concurrency_setup",
    "connection_allow_concurrency_teardown",
    "test_create_table_with_primary_key_prefix_as_table_name_with_underscore",
    "__send__",
    "make_connection",
    "assert_raises",
    "tap",
    "with_kcode",
    "assert_instance_of",
    "test_create_table_with_primary_key_prefix_as_table_name",
    "assert_respond_to",
    "test_change_column_default_to_null",
    "assert_same",
    "__extend__",
  ]

  LargeTestArraySorted = [
    "test_add_column_not_null_with_default",
    "test_add_column_with_precision_and_scale",
    "test_add_column_with_primary_key_attribute",
    "test_add_drop_table_with_prefix_and_suffix",
    "test_add_index",
    "test_add_remove_single_field_using_string_arguments",
    "test_add_remove_single_field_using_symbol_arguments",
    "test_add_rename",
    "test_add_table",
    "test_add_table_with_decimals",
    "test_change_column",
    "test_change_column_default",
    "test_change_column_default_to_null",
    "test_change_column_nullability",
    "test_change_column_quotes_column_names",
    "test_change_column_with_new_default",
    "test_change_column_with_nil_default",
    "test_change_type_of_not_null_column",
    "test_create_table_adds_id",
    "test_create_table_with_binary_column",
    "test_create_table_with_custom_sequence_name",
    "test_create_table_with_defaults",
    "test_create_table_with_force_true_does_not_drop_nonexisting_table",
    "test_create_table_with_limits",
    "test_create_table_with_not_null_column",
    "test_create_table_with_primary_key_prefix_as_table_name",
    "test_create_table_with_primary_key_prefix_as_table_name_with_underscore",
    "test_create_table_with_timestamps_should_create_datetime_columns",
    "test_create_table_with_timestamps_should_create_datetime_columns_with_options",
    "test_create_table_without_a_block",
    "test_create_table_without_id",
    "test_finds_migrations",
    "test_finds_pending_migrations",
    "test_keeping_default_and_notnull_constraint_on_change",
    "test_migrator",
    "test_migrator_db_has_no_schema_migrations_table",
    "test_migrator_double_down",
    "test_migrator_double_up",
    "test_migrator_forward",
    "test_migrator_going_down_due_to_version_target",
    "test_migrator_one_down",
    "test_migrator_one_up",
    "test_migrator_one_up_one_down",
    "test_migrator_one_up_with_exception_and_rollback",
    "test_migrator_rollback",
    "test_migrator_verbosity",
    "test_migrator_verbosity_off",
    "test_migrator_with_duplicate_names",
    "test_migrator_with_duplicates",
    "test_migrator_with_missing_version_numbers",
    "test_native_decimal_insert_manual_vs_automatic",
    "test_native_types",
    "test_only_loads_pending_migrations",
    "test_proper_table_name",
    "test_relative_migrations",
    "test_remove_column_with_index",
    "test_remove_column_with_multi_column_index",
    "test_rename_column",
    "test_rename_column_preserves_default_value_not_null",
    "test_rename_column_using_symbol_arguments",
    "test_rename_column_with_an_index",
    "test_rename_column_with_sql_reserved_word",
    "test_rename_nonexistent_column",
    "test_rename_table",
    "test_rename_table_for_sqlite_should_work_with_reserved_words",
    "test_rename_table_with_an_index",
    "test_schema_migrations_table_name",
    "test_target_version_zero_should_run_only_once",
  ]

  class PrivateToAry
    private

    def to_ary
      [1, 2, 3]
    end
  end
end
