class TestCase::Module::Hash {
  use Int;
  use Hash;
  use Comparator;
  use List;
  use Fn;
  use Array;
  use Fn;
  use Array;
  use Comparator;
  use Comparator::Int;
  use Sort;
  use Point;

  static method new : int () {
    # Without arguments
    {
      my $hash = Hash->new;
      my $keys = $hash->keys;
      unless (@$keys == 0) {
        return 0;
      }
    }
    
    # undef
    {
      my $hash = Hash->new((object[])undef);
      my $keys = $hash->keys;
      unless (@$keys == 0) {
        return 0;
      }
    }
    
    # Empty array
    {
      my $hash = Hash->new({});
      my $keys = $hash->keys;
      unless (@$keys == 0) {
        return 0;
      }
    }
    
    # int values
    {
      my $hash = Hash->new({foo => 1, bar => 3L});
      my $keys = $hash->keys;
      unless (@$keys == 2) {
        return 0;
      }
      
      {
        my $foo = (Int)$hash->get("foo");
        unless ($foo->value == 1) {
          return 0;
        }
        my $bar = (Long)$hash->get("bar");
        unless ($bar->value == 3) {
          return 0;
        }
      }

      {
        my $foo = (int)$hash->get("foo");
        unless ($foo == 1) {
          return 0;
        }
      }
    }
    

    # string values
    {
      my $hash = Hash->new({foo => "4px", bar => "5px"});
      my $keys = $hash->keys;
      unless (@$keys == 2) {
        return 0;
      }
      
      {
        my $foo = (string)$hash->get("foo");
        unless ($foo && $foo eq "4px") {
          return 0;
        }
        my $bar = (string)$hash->get("bar");
        unless ($bar && $bar eq "5px") {
          return 0;
        }
      }
    }

    # string and int values
    {
      my $hash = Hash->new({foo => "4px", bar => 3});
      my $keys = $hash->keys;
      unless (@$keys == 2) {
        return 0;
      }
      
      {
        my $foo = $hash->get_string("foo");
        unless ($foo && $foo eq "4px") {
          return 0;
        }
        my $bar = $hash->get_int("bar");
        unless ($bar && $bar == 3) {
          return 0;
        }
      }
    }
    
    # Exception
    {
      # odd number array
      {
        eval { Hash->new([(object)foo => "4px", "bar"]); };
        unless ($@) {
          return 0;
        }
      }
      
      # Key must be string
      {
        eval { Hash->new({1 => "4px"}); };
        unless ($@) {
          return 0;
        }
      }
      
    }
    
    
    
    return 1;
  }

  static method get_byte : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_byte(foo => Fn->INT8_MAX);
      unless ($hash->get("foo") isa Byte) {
        return 0;
      }
      my $value = $hash->get_byte("foo");
      
      unless ($value == Fn->INT8_MAX) {
        return 0;
      }
    }
    
    # Excetpions
    {
      my $hash = Hash->new;
      
      $hash->set(foo => Point->new);
      
      eval { $hash->get_byte("foo"); }
      
      unless ($@) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method get_short : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_short(foo => Fn->INT16_MAX);
      unless ($hash->get("foo") isa Short) {
        return 0;
      }
      my $value = $hash->get_short("foo");
      
      unless ($value == Fn->INT16_MAX) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      $hash->set_byte(foo => (byte)Fn->INT8_MAX);
      
      unless ($hash->get("foo") isa Byte) {
        return 0;
      }
      my $value = $hash->get_short("foo");
      
      unless ($value == Fn->INT8_MAX) {
        return 0;
      }
    }
    
    # Excetpions
    {
      my $hash = Hash->new;
      
      $hash->set(foo => Point->new);
      
      eval { $hash->get_short("foo"); }
      
      unless ($@) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method get_int : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_int(foo => Fn->INT32_MAX);
      unless ($hash->get("foo") isa Int) {
        return 0;
      }
      my $value = $hash->get_int("foo");
      
      unless ($value == Fn->INT32_MAX) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      $hash->set_short(foo => (short)Fn->SHORT_MAX);
      
      unless ($hash->get("foo") isa Short) {
        return 0;
      }
      my $value = $hash->get_int("foo");
      
      unless ($value == Fn->SHORT_MAX) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      $hash->set_byte(foo => (byte)Fn->INT8_MAX);
      
      unless ($hash->get("foo") isa Byte) {
        return 0;
      }
      my $value = $hash->get_int("foo");
      
      unless ($value == Fn->INT8_MAX) {
        return 0;
      }
    }
    
    # Excetpions
    {
      my $hash = Hash->new;
      
      $hash->set(foo => Point->new);
      
      eval { $hash->get_int("foo"); }
      
      unless ($@) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method get_long : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_long(foo => Fn->INT64_MAX);
      unless ($hash->get("foo") isa Long) {
        return 0;
      }
      my $value = $hash->get_long("foo");

      unless ($value == Fn->INT64_MAX) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      $hash->set_int(foo => (int)Fn->INT_MAX);
      
      unless ($hash->get("foo") isa Int) {
        return 0;
      }
      my $value = $hash->get_long("foo");
      
      unless ($value == Fn->INT_MAX) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      $hash->set_short(foo => (short)Fn->SHORT_MAX);
      
      unless ($hash->get("foo") isa Short) {
        return 0;
      }
      my $value = $hash->get_long("foo");
      
      unless ($value == Fn->SHORT_MAX) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      $hash->set_byte(foo => (byte)Fn->INT8_MAX);
      
      unless ($hash->get("foo") isa Byte) {
        return 0;
      }
      my $value = $hash->get_long("foo");
      
      unless ($value == Fn->INT8_MAX) {
        return 0;
      }
    }
    
    # Excetpions
    {
      my $hash = Hash->new;
      
      $hash->set(foo => Point->new);
      
      eval { $hash->get_long("foo"); }
      
      unless ($@) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method get_float : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_float(foo => Fn->FLT_MIN);
      unless ($hash->get("foo") isa Float) {
        return 0;
      }
      my $value = $hash->get_float("foo");

      unless ($value == Fn->FLT_MIN) {
        return 0;
      }
    }
    
    # Excetpions
    {
      my $hash = Hash->new;
      
      $hash->set(foo => Point->new);
      
      eval { $hash->get_float("foo"); }
      
      unless ($@) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method get_double : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_double(foo => Fn->DBL_MIN);
      unless ($hash->get("foo") isa Double) {
        return 0;
      }
      my $value = $hash->get_double("foo");

      unless ($value == Fn->DBL_MIN) {
        return 0;
      }
    }
    
    # Excetpions
    {
      my $hash = Hash->new;
      
      $hash->set(foo => Point->new);
      
      eval { $hash->get_double("foo"); }
      
      unless ($@) {
        return 0;
      }
    }
    
    
    
    return 1;
  }

  static method get_string : int () {
    
    {
      my $hash = Hash->new;
      
      my $str = "bar";
      $hash->set_string(foo => $str);
      unless ($hash->get_string("foo") isa string) {
        return 0;
      }
      my $value = $hash->get_string("foo");
      
      unless ($value == $str) {
        return 0;
      }
      
    }
    
    {
      my $hash = Hash->new;
      
      $hash->set_string(foo => undef);
      
      my $value = $hash->get_string("foo");
      
      if ($value) {
        return 0;
      }
    }
      
    # Excetpions
    {
      my $hash = Hash->new;
      
      $hash->set(foo => Point->new);
      
      eval { $hash->get_string("foo"); }
      
      unless ($@) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method set_byte : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_byte(foo => Fn->INT8_MAX);
      unless ($hash->get("foo") isa Byte) {
        return 0;
      }
      my $value = $hash->get_byte("foo");

      unless ($value == Fn->INT8_MAX) {
        return 0;
      }
    }

    return 1;
  }
  
  static method set_short : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_short(foo => Fn->INT16_MAX);
      unless ($hash->get("foo") isa Short) {
        return 0;
      }
      my $value = $hash->get_short("foo");

      unless ($value == Fn->INT16_MAX) {
        return 0;
      }
    }

    return 1;
  }
  
  static method set_int : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_int(foo => Fn->INT32_MAX);
      unless ($hash->get("foo") isa Int) {
        return 0;
      }
      my $value = $hash->get_int("foo");

      unless ($value == Fn->INT32_MAX) {
        return 0;
      }
    }

    return 1;
  }
  
  static method set_long : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_long(foo => Fn->INT64_MAX);
      unless ($hash->get("foo") isa Long) {
        return 0;
      }
      my $value = $hash->get_long("foo");

      unless ($value == Fn->INT64_MAX) {
        return 0;
      }
    }

    return 1;
  }
  
  static method set_float : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_float(foo => Fn->FLT_MIN);
      unless ($hash->get("foo") isa Float) {
        return 0;
      }
      my $value = $hash->get_float("foo");

      unless ($value == Fn->FLT_MIN) {
        return 0;
      }
    }

    return 1;
  }
  
  static method set_double : int () {
    
    {
      my $hash = Hash->new;
      
      $hash->set_double(foo => Fn->DBL_MIN);
      unless ($hash->get("foo") isa Double) {
        return 0;
      }
      my $value = $hash->get_double("foo");

      unless ($value == Fn->DBL_MIN) {
        return 0;
      }
    }

    return 1;
  }

  static method set_string : int () {
    
    {
      my $hash = Hash->new;
      
      my $str = "bar";
      $hash->set_string(foo => $str);
      unless ($hash->get_string("foo") isa string) {
        return 0;
      }
      my $value = $hash->get_string("foo");

      unless ($value == $str) {
        return 0;
      }
    }

    return 1;
  }
  
  static method murmur_hash : int () {
    my $seed = 123456789;
    # Testcase is created from https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/hash_bytes.cc#L72-L112
    # Change size_t to uint32_t to use 32bit integer.
    # Compile the program by the following command:
    # g++ -std=c++2a -D__SIZEOF_SIZE_T__=4 hash_bytes.cc
    my $strings = ["a", "<>", "ABC", "1234", "asdfg", "zxcvbn", "1qazxsw", "3edcvfr4", "1234567890-=\\][poiuytrewqasdfghjkl;'"];
    my $hashes = [846967266L, 200612280L, 4178773334L, 1870759112L, 61159236L, 623182920L, 1738266155L, 123403562L, 4243681504L];
    for (my $i = 0; $i < @$strings; ++$i) {
      unless (Hash->_murmur_hash($strings->[$i], $seed) == $hashes->[$i]) {
        return 0;
      }
    }
    return 1;
  }

  static method set : int () {
    my $keys = ["alice", "bob", "carol", "1234567890-="];
    my $vals = [Int->new(1), Int->new(2), undef, Int->new(3)];
    my $hash = Hash->new;
    for (my $i = 0; $i < @$keys; ++$i) {
      $hash->set($keys->[$i], $vals->[$i]);
    }
    unless ($hash->keys_length == @$keys) {
      return 0;
    }
    for (my $i = 0; $i < @$keys; ++$i) {
      unless ($hash->get($keys->[$i]) == $vals->[$i]) {
        return 0;
      }
    }
    return 1;
  }

  static method set_do_not_refer_caller_key : int () {
    my $hash = Hash->new;
    my $key = "a";
    $hash->set($key, Int->new(1));
    $key = "b";
    unless ($hash->exists("a")) {
      return 0;
    }
    unless ($hash->exists("b") == 0) {
      return 0;
    }
    return 1;
  }
  
  static method get : int () {
    my $hash = Hash->new;
    $hash->set_int("a" => 1);
    $hash->set("b" => undef);
    $hash->set("c" => "str");
    
    unless (((Int)($hash->get("a")))->value == 1) {
      return 0;
    }
    unless ($hash->get("b") == undef) {
      return 0;
    }
    unless (((string)$hash->get("c")) eq "str") {
      return 0;
    }
    unless ($hash->get("no_key") == undef) {
      return 0;
    }
    
    # Exceptions
    {
      my $hash = Hash->new;
      eval { $hash->get(undef); }
      
      unless (Fn->contains($@, "The key \$key must be defined.")) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method exists : int () {
    my $keys = ["alice", "bob"];
    my $vals = [Int->new(1), Int->new(2)];
    my $hash = Hash->new;
    for (my $i = 0; $i < @$keys; ++$i) {
      $hash->set($keys->[$i], $vals->[$i]);
    }
    for (my $i = 0; $i < @$keys; ++$i) {
      unless ($hash->exists($keys->[$i])) {
        return 0;
      }
    }
    unless ($hash->exists("carol") == 0) {
      return 0;
    }
    
    # Exceptions
    {
      my $hash = Hash->new;
      eval { $hash->exists(undef); }
      
      unless (Fn->contains($@, "The key \$key must be defined.")) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method delete : int () {
    
    {
      my $success = &delete_with_no_hash_collision;
      
      unless ($success) {
        return 0;
      }
    }
    
    {
      my $success = &delete_with_hash_collision;
      
      unless ($success) {
        return 0;
      }
    }
    
    # Exceptions
    {
      my $hash = Hash->new;
      eval { $hash->delete(undef); }
      
      unless (Fn->contains($@, "The key \$key must be defined.")) {
        return 0;
      }
    }
    
    
    
    return 1;
  }
  
  static method delete_with_no_hash_collision : int () {
    my $hash = Hash->new;
    $hash->set("alice", Int->new(1));
    $hash->set("bob", Int->new(2));
    unless (((Int)($hash->delete("alice")))->value == 1) {
      return 0;
    }
    unless ($hash->exists("alice") == 0) {
      return 0;
    }
    unless ($hash->keys_length == 1) {
      return 0;
    }
    unless ($hash->delete("alice") == undef) {
      return 0;
    }
    unless ($hash->delete("carol") == undef) {
      return 0;
    }
    unless ($hash->keys_length == 1) {
      return 0;
    }
    unless ($hash->exists("bob") == 1) {
      return 0;
    }
    return 1;
  }
  
  static method delete_with_hash_collision : int () {
    my $hash = Hash->new;
    $hash->set("alice", Int->new(1));
    $hash->set("bob", Int->new(2));
    unless (((Int)$hash->delete("alice"))->value == 1) {
      return 0;
    }
    unless ($hash->exists("alice") == 0) {
      return 0;
    }
    unless ($hash->keys_length == 1) {
      return 0;
    }
    unless ($hash->delete("alice") == undef) {
      return 0;
    }
    unless ($hash->keys_length == 1) {
      return 0;
    }

    unless ($hash->exists("bob") == 1) {
      return 0;
    }
    unless (((Int)($hash->delete("bob")))->value == 2) {
      return 0;
    }
    unless ($hash->keys_length == 0) {
      return 0;
    }
    unless ($hash->exists("bob") == 0) {
      return 0;
    }
    return 1;
  }

  static method rehash : int () {
    my $initial_capacity = 16;
    my $hash = Hash->new;
    for (my $i = 0; $i < 14; ++$i) {
      my $key = "key" . $i;
      $hash->set_int($key => $i);
    }

    unless (@{$hash->_entries} == $initial_capacity * 2) {
      return 0;
    }

    unless ($hash->get_int("key12") == 12) {
      return 0;
    }

    return 1;
  }

  static method keys : int () {
    my $hash = Hash->new;
    my $key0 = "a";
    my $key1 = "b";
    
    my $key2 = (mutable string)new_string_len(1);
    $key2->[0] = 'c';
    
    $hash->set_int($key0 => 1);
    $hash->set_int($key1 => 2);
    $hash->set_int($key2 => 3);
    
    my $keys = $hash->keys;
    
    unless (@$keys == 3) {
      return 0;
    }
    
    Sort->sort_string_asc($keys);
    
    unless ($keys->[0] == $key0) {
      return 0;
    }
    
    unless ($keys->[1] == $key1) {
      return 0;
    }
    
    unless ($keys->[2] eq $key2) {
      return 0;
    }
    unless ($keys->[2] != $key2) {
      return 0;
    }
    
    return 1;
  }
  
  static method has_keys : int () {
    my $hash = Hash->new;
    
    if ($hash->has_keys) {
      return 0;
    }
    
    $hash->set("key0" => 1);
    
    unless ($hash->has_keys) {
      return 0;
    }
    
    return 1;
  }
  
  static method values : int () {
    my $hash = Hash->new;
    my $key0 = "a";
    my $key1 = "b";
    my $key2 = "c";

    $hash->set_int($key0 => 1);
    $hash->set_int($key1 => 2);
    $hash->set_int($key2 => 3);

    my $values = $hash->values;

    unless (@$values == 3) {
      return 0;
    }

    my $int_values = new int[3];
    $int_values->[0] = (int)$values->[0];
    $int_values->[1] = (int)$values->[1];
    $int_values->[2] = (int)$values->[2];

    Sort->sort_int_asc($int_values);

    unless ($int_values->[0] == 1) {
      return 0;
    }

    unless ($int_values->[1] == 2) {
      return 0;
    }

    unless ($int_values->[2] == 3) {
      return 0;
    }

    return 1;
  }

  static method copy : int () {
    my $hash = Hash->new;
    for (my $i = 0; $i < 3; ++$i) {
      $hash->set("key$i", $i);
    }
    my $copied = $hash->copy;
    for (my $i = 0; $i < 3; ++$i) {
      unless (((Int)$copied->get("key$i"))->value == $i) {
        return 0;
      }
    }
    $copied->set("extra_key", 123);
    if ($hash->exists("extra_keys")) {
      return 0;
    }
    unless ($copied->exists("extra_key")) {
      return 0;
    }
    return 1;
  }

  static method clone : int () {
    my $hash = Hash->new;
    for (my $i = 0; $i < 3; ++$i) {
      $hash->set("key$i", $i);
    }
    my $clone = (Hash)$hash->(Cloneable)->clone;
    for (my $i = 0; $i < 3; ++$i) {
      unless (((Int)$clone->get("key$i"))->value == $i) {
        return 0;
      }
    }
    $clone->set("extra_key", 123);
    if ($hash->exists("extra_keys")) {
      return 0;
    }
    unless ($clone->exists("extra_key")) {
      return 0;
    }
    return 1;
  }

  static method to_array : int () {
    {
      my $hash = Hash->new;
      for (my $i = 0; $i < 3; ++$i) {
        $hash->set("key$i", $i);
      }
      
      my $array = $hash->to_array;
      
      my $new_hash = Hash->new($array);
      my $keys_new_hash = $new_hash->keys;
      unless (@$keys_new_hash == 3) {
        return 0;
      }
      
      unless ($new_hash->get_int("key0") == 0) {
        return 0;
      }
      unless ($new_hash->get_int("key1") == 1) {
        return 0;
      }
      unless ($new_hash->get_int("key2") == 2) {
        return 0;
      }
    }
    {
      my $hash = Hash->new;
      for (my $i = 0; $i < 3; ++$i) {
        $hash->set("key$i", $i);
      }
      
      {
        my $sort = 1;
        my $array = $hash->to_array($sort);
        
        unless ($array->[0]->(string) eq "key0") {
          return 0;
        }
        unless ($array->[1]->(int) == 0) {
          return 0;
        }
        unless ($array->[2]->(string) eq "key1") {
          return 0;
        }
        unless ($array->[3]->(int) == 1) {
          return 0;
        }
        unless ($array->[4]->(string) eq "key2") {
          return 0;
        }
        unless ($array->[5]->(int) == 2) {
          return 0;
        }
      }
      
      {
        my $sort = -1;
        my $array = $hash->to_array($sort);
        
        unless ($array->[4]->(string) eq "key0") {
          return 0;
        }
        unless ($array->[5]->(int) == 0) {
          return 0;
        }
        unless ($array->[2]->(string) eq "key1") {
          return 0;
        }
        unless ($array->[3]->(int) == 1) {
          return 0;
        }
        unless ($array->[0]->(string) eq "key2") {
          return 0;
        }
        unless ($array->[1]->(int) == 2) {
          return 0;
        }
      }
    }
    
    
    return 1;
  }

  static method get_or_default : int () {

    my $hash = Hash->new;

    {
      $hash->set_byte(foo => Fn->INT8_MAX);
      my $value = $hash->get_or_default_byte("foo", Fn->INT8_MIN);
      
      unless ($value == Fn->INT8_MAX) {
        return 0;
      }
      
      $hash->delete("foo");
      
      unless ($hash->get_or_default_byte("foo", Fn->INT8_MIN) == Fn->INT8_MIN) {
        return 0;
      }
    }
    
    {
      $hash->set_short(foo => Fn->INT16_MAX);
      my $value = $hash->get_or_default_short("foo", Fn->INT16_MIN);
      unless ($value == Fn->INT16_MAX) {
        return 0;
      }
      
      $hash->delete("foo");
      
      unless ($hash->get_or_default_short("foo", Fn->INT16_MIN) == Fn->INT16_MIN) {
        return 0;
      }
    }

    {
      $hash->set_int(foo => Fn->INT32_MAX);
      my $value = $hash->get_or_default_int("foo", Fn->INT32_MIN);

      unless ($value == Fn->INT32_MAX) {
        return 0;
      }
      
      $hash->delete("foo");
      
      unless ($hash->get_or_default_int("foo", Fn->INT32_MIN) == Fn->INT32_MIN) {
        return 0;
      }
    }

    {
      $hash->set_long(foo => Fn->INT64_MAX);
      my $value = $hash->get_or_default_long("foo", Fn->INT64_MIN);

      unless ($value == Fn->INT64_MAX) {
        return 0;
      }
      
      $hash->delete("foo");
      
      unless ($hash->get_or_default_long("foo", Fn->INT64_MIN) == Fn->INT64_MIN) {
        return 0;
      }
    }

    {
      $hash->set_float(foo => Fn->FLT_MIN);
      my $value = $hash->get_or_default_float("foo", Fn->FLT_MIN);

      unless ($value == Fn->FLT_MIN) {
        return 0;
      }
      
      $hash->delete("foo");
      
      unless ($hash->get_or_default_float("foo", Fn->FLT_MIN) == Fn->FLT_MIN) {
        return 0;
      }
    }

    {
      $hash->set_double(foo => Fn->DBL_MIN);
      my $value = $hash->get_or_default_double("foo", Fn->DBL_MIN);

      unless ($value == Fn->DBL_MIN) {
        return 0;
      }
      
      $hash->delete("foo");
      
      unless ($hash->get_or_default_double("foo", Fn->DBL_MIN) == Fn->DBL_MIN) {
        return 0;
      }
    }

    {
      my $str = "bar";
      $hash->set_string(foo => $str);

      my $default = "abc";

      my $value = $hash->get_or_default_string("foo", $default);

      unless ($value == $str) {
        return 0;
      }
      
      $hash->delete("foo");
      
      unless ($hash->get_or_default_string("foo", $default) == $default) {
        return 0;
      }
      unless ($hash->get_or_default_string("foo", $default) eq $default) {
        return 0;
      }
    }

    {
      my $str = "bar";
      $hash->set_string(foo => $str);
      
      my $default = "abc";
      
      my $value = $hash->get_or_default("foo", $default);
      
      unless ($value == $str) {
        return 0;
      }
      
      $hash->delete("foo");
      
      unless ($hash->get_or_default("foo", $default) == $default) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method delete_or_default : int () {

    my $hash = Hash->new;

    {
      $hash->set_byte(foo => Fn->INT8_MAX);
      my $value = $hash->delete_or_default_byte("foo", Fn->INT8_MIN);

      unless ($value == Fn->INT8_MAX) {
        return 0;
      }
      unless ($hash->delete_or_default_byte("foo", Fn->INT8_MIN) == Fn->INT8_MIN) {
        return 0;
      }
    }

    {
      $hash->set_short(foo => Fn->INT16_MAX);
      my $value = $hash->delete_or_default_short("foo", Fn->INT16_MIN);
      unless ($value == Fn->INT16_MAX) {
        return 0;
      }
      unless ($hash->delete_or_default_short("foo", Fn->INT16_MIN) == Fn->INT16_MIN) {
        return 0;
      }
    }

    {
      $hash->set_int(foo => Fn->INT32_MAX);
      my $value = $hash->delete_or_default_int("foo", Fn->INT32_MIN);

      unless ($value == Fn->INT32_MAX) {
        return 0;
      }
      unless ($hash->delete_or_default_int("foo", Fn->INT32_MIN) == Fn->INT32_MIN) {
        return 0;
      }
    }

    {
      $hash->set_long(foo => Fn->INT64_MAX);
      my $value = $hash->delete_or_default_long("foo", Fn->INT64_MIN);

      unless ($value == Fn->INT64_MAX) {
        return 0;
      }
      unless ($hash->delete_or_default_long("foo", Fn->INT64_MIN) == Fn->INT64_MIN) {
        return 0;
      }
    }

    {
      $hash->set_float(foo => Fn->FLT_MIN);
      my $value = $hash->delete_or_default_float("foo", Fn->FLT_MIN);

      unless ($value == Fn->FLT_MIN) {
        return 0;
      }
      unless ($hash->delete_or_default_float("foo", Fn->FLT_MIN) == Fn->FLT_MIN) {
        return 0;
      }
    }

    {
      $hash->set_double(foo => Fn->DBL_MIN);
      my $value = $hash->delete_or_default_double("foo", Fn->DBL_MIN);

      unless ($value == Fn->DBL_MIN) {
        return 0;
      }
      unless ($hash->delete_or_default_double("foo", Fn->DBL_MIN) == Fn->DBL_MIN) {
        return 0;
      }
    }

    {
      my $str = "bar";
      $hash->set_string(foo => $str);

      my $default = "abc";

      my $value = $hash->delete_or_default_string("foo", $default);

      unless ($value == $str) {
        return 0;
      }
      unless ($hash->delete_or_default_string("foo", $default) == $default) {
        return 0;
      }
      unless ($hash->delete_or_default_string("foo", $default) eq $default) {
        return 0;
      }
    }

    {
      my $str = "bar";
      $hash->set_string(foo => $str);
      
      my $default = "abc";
      
      my $value = $hash->delete_or_default("foo", $default);
      
      unless ($value == $str) {
        return 0;
      }
      unless ($hash->delete_or_default("foo", $default) == $default) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method weaken : int () {
    
    {
      my $hash = Hash->new;
      
      my $key = "key";
      my $value = Int->new(1);
      
      $hash->set($key, $value);
      
      $hash->weaken($key);
      
      unless ($hash->isweak($key)) {
        return 0;
      }
      
      $hash->unweaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      my $key = "key";
      
      $hash->weaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
      
      $hash->unweaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
    }
    
    # Exceptions
    {
      my $hash = Hash->new;
      eval { $hash->weaken(undef); }
      
      unless (Fn->contains($@, "The key \$key must be defined.")) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method unweaken : int () {
    
    {
      my $hash = Hash->new;
      
      my $key = "key";
      my $value = Int->new(1);
      
      $hash->set($key, $value);
      
      $hash->weaken($key);
      
      unless ($hash->isweak($key)) {
        return 0;
      }
      
      $hash->unweaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      my $key = "key";
      
      $hash->weaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
      
      $hash->unweaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
    }
    
    # Exceptions
    {
      my $hash = Hash->new;
      eval { $hash->unweaken(undef); }
      
      unless (Fn->contains($@, "The key \$key must be defined.")) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method isweak : int () {
    
    {
      my $hash = Hash->new;
      
      my $key = "key";
      my $value = Int->new(1);
      
      $hash->set($key, $value);
      
      $hash->weaken($key);
      
      unless ($hash->isweak($key)) {
        return 0;
      }
      
      $hash->unweaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
    }
    
    {
      my $hash = Hash->new;
      
      my $key = "key";
      
      $hash->weaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
      
      $hash->unweaken($key);
      
      if ($hash->isweak($key)) {
        return 0;
      }
    }
    
    # Exceptions
    {
      my $hash = Hash->new;
      eval { $hash->isweak(undef); }
      
      unless (Fn->contains($@, "The key \$key must be defined.")) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method extra : int () {
    
    # The type of a key is int type
    {
      my $hash = Hash->new([(string)
        100 => "a",
        200 => "b",
      ]);
      
      unless ($hash->get_string("100") eq "a") {
        return 0;
      }
    }
    
    return 1;
  }
}