package TestCase::Lib::SPVM::Util {
  use SPVM::Util(joino, split, copy_oarray, equals_oarray, sprintf);

  use SPVM::Unicode (uchar);
  use TestCase::Minimal;
  use SPVM::Math (fabsf, fabs);
  use SPVM::Util (equals_strarray, contains);
  use SPVM::Util (INT8_MIN, INT8_MAX, INT16_MIN, INT16_MAX, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX, FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX);
  use TestCase::Minimal;
  use SPVM::Time (time);
  use SPVM::EqualityChecker;
  use SPVM::Util;
  use SPVM::EqualityChecker::SameObject;

  use SPVM::Util (
    chomp_lf,
    chomp_crlf,
    copy_barray,
    copy_sarray,
    copy_iarray,
    copy_larray,
    copy_farray,
    copy_darray,
    E,
    copy_strarray,
    equals_barray,
    equals_sarray,
    equals_iarray,
    equals_larray,
    equals_farray,
    equals_darray,
    get_type_name,
    is_perl_space,
    is_perl_word,
    isalnum,
    isalpha,
    isblank,
    iscntrl,
    isdigit,
    isgraph,
    islower,
    isprint,
    ispunct,
    isspace,
    isupper,
    isxdigit,
    tolower,
    toupper,
    join,
    joinb,
    joins,
    joini,
    joinl,
    joinf,
    joind,
    lc,
    lcfirst,
    uc,
    ucfirst,
    PI,
    crand,
    srand,
    replace,
    replace_all,
    rindex,
    strtod,
    strtof,
    strtoi,
    strtol,
    reverseb,
    reverses,
    reversei,
    reversel,
    reversef,
    reversed,
    reverseo,
    sliceb,
    slices,
    slicei,
    slicel,
    slicef,
    sliced,
    copy_str,
    index,
    memcpyb,
    memcpys,
    memcpyi,
    memcpyl,
    memcpyf,
    memcpyd,
    memmoveb,
    memmoves,
    memmovei,
    memmovel,
    memmovef,
    memmoved,
    new_object_array_proto,
    sliceo,
  );

  sub test_equals_oarray : int () {
    my $minimal1 = TestCase::Minimal->new;
    my $minimal2 = TestCase::Minimal->new;
    my $minimal3 = TestCase::Minimal->new;
    my $minimal4 = TestCase::Minimal->new;
    my $nums1 = [$minimal1, $minimal2, $minimal3];
    my $nums2 = [$minimal1, $minimal2, $minimal3];
    my $nums3 = [$minimal1, $minimal2];
    my $nums4 = [$minimal1, $minimal2, $minimal4];
    my $nums5 = [(object)$minimal1, $minimal2, $minimal3];

    my $equality_checker = sub : int ($self : self, $obj1 : object, $obj2 : object) {
      my $minimal1 = (TestCase::Minimal)$obj1;
      my $minimal2 = (TestCase::Minimal)$obj2;

      if ($minimal1 == $minimal2) {
        return 1;
      }
      else {
        return 0;
      }
    };

    {
      my $is_equals = equals_oarray($nums1, $nums2, $equality_checker);
      unless ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_oarray($nums1, $nums3, $equality_checker);
      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_oarray($nums1, $nums4, $equality_checker);
      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_oarray($nums1, $nums5, $equality_checker);
      unless ($is_equals) {
        return 0;
      }
    }

    return 1;
  }

  sub test_copy_oarray : int () {
    my $objects = [(object)SPVM::Int->new(1), SPVM::Int->new(2), SPVM::Int->new(INT32_MIN())];
    my $objects_out = copy_oarray($objects, sub : object ($self : self, $obj : object) {
      my $int_obj = (SPVM::Int)$obj;
      my $new_int_obj = SPVM::Int->new($int_obj->val);
      return $new_int_obj;
    });

    if ($objects->[0] == $objects_out->[0]) {
      return 0;
    }

    if ($objects->[1] == $objects_out->[1]) {
      return 0;
    }

    if ($objects->[2] == $objects_out->[2]) {
      return 0;
    }

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

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

    unless ((int)$objects_out->[2] == INT32_MIN()) {
      return 0;
    }

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

    unless ($objects != $objects_out) {
      return 0;
    }

    return 1;
  }

  sub test_joino : int () {

    my $minimals = [
      TestCase::Minimal->newp(1, 2),
      TestCase::Minimal->newp(3, 4),
      TestCase::Minimal->newp(5, 6)
    ];

    my $join = joino(",", $minimals, sub : string ($self : self, $obj : object) {
      my $minimal = (TestCase::Minimal)$obj;
      my $x = $minimal->x;
      my $y = $minimal->y;
      my $str = "($x,$y)";
      return $str;
    });

    if ($join eq "(1,2),(3,4),(5,6)") {
      return 1;
    }

    return 0;
  }

  sub test_split : int () {
    {
      my $str = "foo,bar,baz";
      my $split_strs = split(",", $str);
      unless (equals_strarray($split_strs, ["foo", "bar", "baz"])) {
        return 0;
      }
    }

    {
      my $str = "foo,bar,";
      my $split_strs = split(",", $str);
      unless (equals_strarray($split_strs, ["foo", "bar", ""])) {
        return 0;
      }
    }

    {
      my $str = ",foo,,bar,,";
      my $split_strs = split(",", $str);
      unless (equals_strarray($split_strs, ["", "foo", "", "bar", "", ""])) {
        return 0;
      }
    }

    {
      my $str = "foo : bar : baz";
      my $split_strs = split(" : ", $str);
      unless (equals_strarray($split_strs, ["foo", "bar", "baz"])) {
        return 0;
      }
    }
    {
      my $str = "foo : bar : ";
      my $split_strs = split(" : ", $str);
      unless (equals_strarray($split_strs, ["foo", "bar", ""])) {
        return 0;
      }
    }
    {
      my $str = " : foo :  : bar :  : ";
      my $split_strs = split(" : ", $str);
      unless (equals_strarray($split_strs, ["", "foo", "", "bar", "", ""])) {
        return 0;
      }
    }

    {
      my $str = "foo---bar---baz";
      my $split_strs = split("---", $str);
      unless (equals_strarray($split_strs, ["foo", "bar", "baz"])) {
        return 0;
      }
    }
    {
      my $str = "foo---bar---";
      my $split_strs = split("---", $str);
      unless (equals_strarray($split_strs, ["foo", "bar", ""])) {
        return 0;
      }
    }
    {
      my $str = "---foo------bar------";
      my $split_strs = split("---", $str);
      unless (equals_strarray($split_strs, ["", "foo", "", "bar", "", ""])) {
        return 0;
      }
    }

    {
      my $str = "foo--!bar---baz";
      my $split_strs = split("---", $str);
      unless (equals_strarray($split_strs, ["foo--!bar", "baz"])) {
        return 0;
      }
    }
    return 1;
  }

  sub test_sprintf_d : int () {
    my $tests = [
        [ sprintf("abc%d", 123),          "abc123" ],
        [ sprintf("%dabc", 123),          "123abc" ],
        [ sprintf("%dabc%d", 1, 10),      "1abc10" ],
        [ sprintf("%d%d%d", 1, 10, 100),  "110100" ],
        [ sprintf("%d%d%d", 1, 10, 100),  "110100" ],
        [ sprintf("%05d", 123),           "00123"  ],
        [ sprintf("%+5d", 123),           " +123"  ],
        [ sprintf("%-5d", 123),           "123  "  ],
    ];
    for (my $i = 0; $i < @$tests; ++$i) {
      unless ($tests->[$i][0] eq $tests->[$i][1]) {
        warn("got: '" . $tests->[$i][0] . "', expected: '" . $tests->[$i][1] . "'");
        return 0;
      }
    }
    {
      eval {
        sprintf("%d", "str");
      };
      unless ($@ && contains($@, "Can't cast")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    return 1;
  }

  sub test_sprintf_ld : int () {
    my $tests = [
        [ sprintf("abc%ld", 10000000000L),                                "abc10000000000"                    ],
        [ sprintf("%ldabc", 10000000000L),                                "10000000000abc"                    ],
        [ sprintf("%ldabc%ld", 10000000000L, 20000000000L),               "10000000000abc20000000000"         ],
        [ sprintf("%ld%ld%ld", 10000000000L, 20000000000L, 30000000000L), "100000000002000000000030000000000" ],
        [ sprintf("%013ld", 12345678901L),                                "0012345678901"                     ],
        [ sprintf("%+13ld", 12345678901L),                                " +12345678901"                     ],
        [ sprintf("%-13ld", 12345678901L),                                "12345678901  "                     ],
    ];
    for (my $i = 0; $i < @$tests; ++$i) {
      unless ($tests->[$i][0] eq $tests->[$i][1]) {
        warn("got: '" . $tests->[$i][0] . "', expected: '" . $tests->[$i][1] . "'");
        return 0;
      }
    }
    {
      eval {
        sprintf("%ld", "str");
      };
      unless ($@ && contains($@, "Can't cast")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    {
      eval {
        sprintf("%l", 1L);
      };
      unless ($@ && contains($@, "Invalid conversion in sprintf: \"%l\"")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    return 1;
  }

  sub test_sprintf_f : int () {
    my $tests = [
        [ sprintf("abc%.2f", 3.14),                  "abc3.14"      ],
        [ sprintf("%.2fabc", 3.14),                  "3.14abc"      ],
        [ sprintf("%.2fabc%.2f", 3.14, 2.71),        "3.14abc2.71"  ],
        [ sprintf("%.2f%.2f%.2f", 3.14, 2.71, 2.67), "3.142.712.67" ],
        [ sprintf("%.10f", 3.14),                    "3.1400000000" ],
        [ sprintf("%012.6f", 3.14),                  "00003.140000" ],
        [ sprintf("%+12.6f", 3.14),                  "   +3.140000" ],
        [ sprintf("%-12.6f", 3.14),                  "3.140000    " ],
        [ sprintf("%+-12.6f", 3.14),                 "+3.140000   " ],
        [ sprintf("%g", 3.14),                       "3.14"         ],
    ];
    for (my $i = 0; $i < @$tests; ++$i) {
      unless ($tests->[$i][0] eq $tests->[$i][1]) {
        warn("got: '" . $tests->[$i][0] . "', expected: '" . $tests->[$i][1] . "'");
        return 0;
      }
    }
    {
      eval {
        sprintf("%f", "str");
      };
      unless ($@ && contains($@, "Can't cast")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    return 1;
  }

  sub test_sprintf_c : int () {
    my $tests = [
        [ sprintf("abc%c", 'x'),            "abcx"  ],
        [ sprintf("%cabc", 'x'),            "xabc"  ],
        [ sprintf("%cabc%c", 'x', 'y'),     "xabcy" ],
        [ sprintf("%c%c%c", 'x', 'y', 'z'), "xyz"   ],
        [ sprintf("%05c", 'x'),             "0000x" ],
        [ sprintf("%-5c", 'x'),             "x    " ],
    ];
    for (my $i = 0; $i < @$tests; ++$i) {
      unless ($tests->[$i][0] eq $tests->[$i][1]) {
        warn("got: '" . $tests->[$i][0] . "', expected: '" . $tests->[$i][1] . "'");
        return 0;
      }
    }
    {
      eval {
        sprintf("%c", "str");
      };
      unless ($@ && contains($@, "Can't cast")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    return 1;
  }

  sub test_sprintf_s : int () {
    my $tests = [
        [ sprintf("abc%s", "ABC"),                "abcABC"    ],
        [ sprintf("%sabc", "ABC"),                "ABCabc"    ],
        [ sprintf("%sabc%s", "ABC", "XYZ"),       "ABCabcXYZ" ],
        [ sprintf("%s%s%s", "ABC", "XYZ", "123"), "ABCXYZ123" ],
        [ sprintf("%05s", "str"),                 "00str"     ],
        [ sprintf("%-5s", "str"),                 "str  "     ],
    ];
    for (my $i = 0; $i < @$tests; ++$i) {
      unless ($tests->[$i][0] eq $tests->[$i][1]) {
        warn("got: '" . $tests->[$i][0] . "', expected: '" . $tests->[$i][1] . "'");
        return 0;
      }
    }
    {
      eval {
        sprintf("%s", 1);
      };
      unless ($@ && contains($@, "Can't cast")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    return 1;
  }

  private sub _first_uchar : int ($str : string) {
    my $uchar_pos = 0;
    return uchar($str, \$uchar_pos);
  }

  sub test_sprintf_U : int () {
    my $tests = [
        [ sprintf("abc%U", _first_uchar("あ")), "abcあ" ],
        [ sprintf("%Uabc", _first_uchar("あ")), "あabc" ],
        [ sprintf("%Uabc%U", _first_uchar("あ"), _first_uchar("い")), "あabcい" ],
        [ sprintf("%U%U%U", _first_uchar("あ"), _first_uchar("い"), _first_uchar("う")), "あいう" ],
    ];
    for (my $i = 0; $i < @$tests; ++$i) {
      unless ($tests->[$i][0] eq $tests->[$i][1]) {
        warn("got: '" . $tests->[$i][0] . "', expected: '" . $tests->[$i][1] . "'");
        return 0;
      }
    }
    {
      eval {
        sprintf("%U", "str");
      };
      unless ($@ && contains($@, "Can't cast")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    return 1;
  }

  sub test_sprintf_percent : int () {
    my $tests = [
        [ sprintf("%d%%",    1), "1%"    ],
        [ sprintf("%%%d",    1), "%1"    ],
        [ sprintf("%d%%str", 1), "1%str" ],
    ];
    for (my $i = 0; $i < @$tests; ++$i) {
      unless ($tests->[$i][0] eq $tests->[$i][1]) {
        warn("got: '" . $tests->[$i][0] . "', expected: '" . $tests->[$i][1] . "'");
        return 0;
      }
    }
    return 1;
  }

  sub test_sprintf_all : int () {
    {
      # Invalid conversion (end of string)
      eval {
        sprintf("%d%", 1);
      };
      unless ($@ && contains($@, "Invalid conversion in sprintf: end of string")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    {
      # Invalid conversion (unknown specifier)
      eval {
        sprintf("%d%k", 1, 2);
      };
      unless ($@ && contains($@, "Invalid conversion in sprintf: \"%k\"")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    {
      # Invalid conversion (no type)
      eval {
        sprintf("%012.3", 3.14);
      };
      unless ($@ && contains($@, "Invalid conversion in sprintf: \"%012.3\"")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    {
      # Missing argument
      eval {
        sprintf("%d%d", 1);
      };
      unless ($@ && contains($@, "Missing argument in sprintf")) {
        warn("got error: $@");
        return 0;
      }
      $@ = undef;
    }
    {
      my $str = "abc\n";
      my $ret = chomp_lf($str);
      unless ($ret eq "abc") {
        return 0;
      }
    }
    {
      my $str = "abc";
      my $ret = chomp_lf($str);
      unless ($ret eq "abc") {
        return 0;
      }
    }
    {
      my $str = "";
      my $ret = chomp_lf($str);
      unless ($ret eq "") {
        return 0;
      }
    }

    return 1;
  }

  sub test_chomp_lf : int () {
    {
      my $str = "abc\n";
      my $ret = chomp_lf($str);
      unless ($ret eq "abc") {
        return 0;
      }
    }
    {
      my $str = "abc";
      my $ret = chomp_lf($str);
      unless ($ret eq "abc") {
        return 0;
      }
    }
    {
      my $str = "";
      my $ret = chomp_lf($str);
      unless ($ret eq "") {
        return 0;
      }
    }

    return 1;
  }
  sub test_chomp_crlf : int () {
    {
      my $str = "abc\r\n";
      my $ret = chomp_crlf($str);
      unless ($ret eq "abc") {
        return 0;
      }
    }
    {
      my $str = "abc";
      my $ret = chomp_crlf($str);
      unless ($ret eq "abc") {
        return 0;
      }
    }
    {
      my $str = "";
      my $ret = chomp_crlf($str);
      unless ($ret eq "") {
        return 0;
      }
    }
    {
      my $str = "a";
      my $ret = chomp_crlf($str);
      unless ($ret eq "a") {
        return 0;
      }
    }

    return 1;
  }

  sub test_isalnum : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if (($char >= 'A' && $char <= 'Z') || ($char >= 'a' && $char <= 'z') || ($char >= '0' && $char <= '9')) {
        my $ret = isalnum($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isalnum($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_isalpha : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if (($char >= 'A' && $char <= 'Z') || ($char >= 'a' && $char <= 'z')) {
        my $ret = isalpha($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isalpha($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_isblank : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if ($char >= ' ' ||  $char <= '\t') {
        my $ret = isblank($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isblank($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_iscntrl : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if (($char >= 0x00 && $char <= 0x1f) || $char == 0x7f) {
        my $ret = iscntrl($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = iscntrl($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_isdigit : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if ($char >= '0' && $char <= '9') {
        my $ret = isdigit($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isdigit($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_isgraph : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if ($char >= 0x21 && $char <= 0x7e) {
        my $ret = isgraph($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isgraph($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_islower : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

    if ($char >= 'a' && $char <= 'z') {
        my $ret = islower($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = islower($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_isprint : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if ($char >= 0x20 && $char <= 0x7e) {
        my $ret = isprint($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isprint($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_ispunct : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if (($char >= 0x21 && $char <= 0x2f) || ($char >= 0x3a && $char <= 0x40) || ($char >= 0x5b && $char <= 0x60) || ($char >= 0x7b && $char <= 0x7e)) {
        my $ret = ispunct($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = ispunct($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_isspace : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if (($char >= 0x09 && $char <= 0x0d) || $char == 0x20) {
        my $ret = isspace($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isspace($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_isupper : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if ($char >= 'A' && $char <= 'Z') {
        my $ret = isupper($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isupper($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_isxdigit : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if (($char >= 'A' && $char <= 'F') || ($char >= 'a' && $char <= 'f') || ($char >= '0' && $char <= '9')) {
        my $ret = isxdigit($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = isxdigit($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_tolower : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if ($char >= 'A' && $char <= 'Z') {
        my $ret = tolower($char);
        unless ($ret == $char + 0x20) {
          $ok = 0;
        }
      }
      else {
        my $ret = tolower($char);
        unless ($ret == $char) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_toupper : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

    if ($char >= 'a' && $char <= 'z') {
        my $ret = toupper($char);
        unless ($ret == $char - 0x20) {
          $ok = 0;
        }
      }
      else {
        my $ret = toupper($char);
        unless ($ret == $char) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_is_perl_space : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if ($char == ' ' || $char == '\r' || $char == '\n' || $char == '\t' || $char == '\f') {
        my $ret = is_perl_space($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = is_perl_space($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_is_perl_word : int () {

    my $ok = 1;
    for (my $i = 0; $i < 128; $i++) {
      my $char = $i;

      if ($char >= 'a' && $char <= 'z' || $char >= 'A' && $char <= 'Z' || $char == '_' || $char >= '0' && $char <= '9') {
        my $ret = is_perl_word($char);
        unless ($ret == 1) {
          $ok = 0;
        }
      }
      else {
        my $ret = is_perl_word($char);
        unless ($ret == 0) {
          $ok = 0;
        }
      }
    }

    unless ($ok) {
      return 0;
    }

    return 1;
  }

  sub test_strtoi : int () {
    # 10 digit minimal and return type is int
    {
      my $string = "-2147483648";
      my $num = strtoi($string, 10);
      unless ($num isa int) {
        return 0;
      }

      unless ($num == -2147483648) {
        return 0;
      }
    }

    # 10 digit max
    {
      my $string = "2147483647";
      my $num = strtoi($string, 10);
      unless ($num isa int) {
        return 0;
      }

      unless ($num == 2147483647) {
        return 0;
      }
    }

    # 2 digit
    {
      my $string = "11";
      my $num = strtoi($string, 2);

      unless ($num == 3) {
        return 0;
      }
    }

    # 8 digit
    {
      my $string = "11";
      my $num = strtoi($string, 8);

      unless ($num == 9) {
        return 0;
      }
    }

    # 16 digit
    {
      my $string = "FF";
      my $num = strtoi($string, 16);

      unless ($num == 255) {
        return 0;
      }
    }

    # Invalid string format
    {
      my $string = "10oppp";
      eval {
        strtoi($string, 10);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    # Invalid digit
    {
      my $string = "10";
      eval {
        strtoi($string, 100);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    # Out of range max + 1
    {
      my $string = "2147483648";
      eval {
        strtoi($string, 10);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    # Out of range min - 1
    {
      my $string = "-2147483649";
      eval {
        strtoi($string, 10);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    return 1;
  }

  sub test_strtol : int () {
    # 10 digit minimal and return type is int
    {
      my $string = "-9223372036854775808";
      my $num = strtol($string, 10);
      unless ($num isa long) {
        return 0;
      }

      unless ($num == -9223372036854775808L) {
        return 0;
      }
    }

    # 10 digit max
    {
      my $string = "9223372036854775807";
      my $num = strtol($string, 10);

      unless ($num == 9223372036854775807L) {
        return 0;
      }
    }

    # 2 digit
    {
      my $string = "11";
      my $num = strtol($string, 2);

      unless ($num == 3) {
        return 0;
      }
    }

    # 8 digit
    {
      my $string = "11";
      my $num = strtol($string, 8);

      unless ($num == 9) {
        return 0;
      }
    }

    # 16 digit
    {
      my $string = "FF";
      my $num = strtol($string, 16);

      unless ($num == 255) {
        return 0;
      }
    }

    # Invalid string format
    {
      my $string = "10oppp";
      eval {
        strtol($string, 10);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    # Invalid digit
    {
      my $string = "10";
      eval {
        strtol($string, 100);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    # Out of range max + 1
    {
      my $string = "9223372036854775808";
      eval {
        strtol($string, 10);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    # Out of range min - 1
    {
      my $string = "-9223372036854775809";
      eval {
        strtol($string, 10);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    return 1;
  }

  sub test_strtof : int () {
    # 10 digit minimal and return type is int
    {
      my $string = "1.25";
      my $num = strtof($string);
      unless ($num isa float) {
        return 0;
      }

      unless ($num == 1.25) {
        return 0;
      }
    }

    # Invalid string format
    {
      my $string = "10.5oppp";
      eval {
        strtof($string);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    return 1;
  }

  sub test_strtod : int () {
    # 10 digit minimal and return type is int
    {
      my $string = "1.25";
      my $num = strtod($string);
      unless ($num isa double) {
        return 0;
      }

      unless ($num == 1.25) {
        return 0;
      }
    }

    # Invalid string format
    {
      my $string = "10.5oppp";
      eval {
        strtod($string);
      };
      unless ($@) {
        return 0;
      }
      $@ = undef;
    }

    return 1;
  }

  sub test_crand : int () {
    
    my $founds_plus = new int[10];
    my $founds_minus = new int[10];
    my $rand = 0;
    for (my $i = 0; $i < 100000; $i++) {
      $rand = crand();

      my $dig0 = $rand % 10;
      $founds_plus->[$dig0] = 1;
    }
    
    unless (equals_iarray($founds_plus, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])) {
      return 0;
    }

    return 1;
  }

  sub test_rand : int () {
    
    # 0 <= random_number < 1
    {
      my $rand = 0.0;
      my $invalid_range = 0;
      for (my $i = 0; $i < 100000; $i++) {
        $rand = SPVM::Util->rand();
        
        unless ($rand >= 0 && $rand < 1) {
          $invalid_range = 1;
          last;
        }
      }
      
      if ($invalid_range) {
        return 0;
      }
    }
    
    # Apper random numbers
    {
      my $founds_plus = new int[10];
      my $founds_minus = new int[10];
      my $rand = 0.0;
      for (my $i = 0; $i < 100000; $i++) {
        $rand = SPVM::Util->rand();
        
        # 0-9
        my $dig0 = (int)($rand * 10);
        $founds_plus->[$dig0] = 1;
      }
      
      unless (equals_iarray($founds_plus, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])) {
        return 0;
      }
    }

    return 1;
  }

  sub test_RAND_MAX : int () {
    
    unless (SPVM::Util->RAND_MAX > 0) {
      return 1;
    }

    return 1;
  }

  sub test_srand : int () {
    
    srand(10);
    
    my $rand1 = crand();
    my $rand2 = crand();
    my $rand3 = crand();
    
    srand(10);
    
    unless (crand() == $rand1) {
      return 0;
    }
    
    unless (crand() == $rand2) {
      return 0;
    }
    
    unless (crand() == $rand3) {
      return 0;
    }
    
    return 1;
  }

  sub test_ucfirst : int () {
    {
      my $str = "@abc";
      my $result_str = ucfirst($str);
      unless ($result_str eq "@abc") {
        return 0;
      }
    }
    {
      my $str = "[abc";
      my $result_str = ucfirst($str);
      unless ($result_str eq "[abc") {
        return 0;
      }
    }

    {
      my $str = "aabc";
      my $result_str = ucfirst($str);
      unless ($result_str eq "Aabc") {
        return 0;
      }
    }

    {
      my $str = "pabc";
      my $result_str = ucfirst($str);
      unless ($result_str eq "Pabc") {
        return 0;
      }
    }

    {
      my $str = "zabc";
      my $result_str = ucfirst($str);
      unless ($result_str eq "Zabc") {
        return 0;
      }
    }
    return 1;
  }

  sub test_uc : int () {
    {
      my $str = "@[apz[";
      my $result_str = uc($str);
      unless ($result_str eq "@[APZ[") {
        return 0;
      }
    }
    return 1;
  }

  sub test_lc : int () {
    {
      my $str = "@[APZ[";
      my $result_str = lc($str);
      unless ($result_str eq "@[apz[") {
        return 0;
      }
    }
    return 1;
  }

  sub test_lcfirst : int () {
    {
      my $str = "@ABC";
      my $result_str = lcfirst($str);
      unless ($result_str eq "@ABC") {
        return 0;
      }
    }
    {
      my $str = "[ABC";
      my $result_str = lcfirst($str);
      unless ($result_str eq "[ABC") {
        return 0;
      }
    }

    {
      my $str = "AABC";
      my $result_str = lcfirst($str);
      unless ($result_str eq "aABC") {
        return 0;
      }
    }

    {
      my $str = "PABC";
      my $result_str = lcfirst($str);
      unless ($result_str eq "pABC") {
        return 0;
      }
    }

    {
      my $str = "ZABC";
      my $result_str = lcfirst($str);
      unless ($result_str eq "zABC") {
        return 0;
      }
    }
    return 1;
  }
  sub test_replace_all : int () {

    {
      my $str = "foo bar foo bar foo";
      my $substr = "bar";
      my $replace = "AB";
      my $result_str = replace_all($str, $substr, $replace);
      unless ($result_str eq "foo AB foo AB foo") {
        return 0;
      }
    }

    {
      my $str = "foobarfoobarfoo";
      my $substr = "bar";
      my $replace = "";
      my $result_str = replace_all($str, $substr, $replace);
      unless ($result_str eq "foofoofoo") {
        return 0;
      }
    }

    return 1;
  }

  sub test_replace : int () {

    {
      my $str = "abcde";
      my $substr = "bcd";
      my $replace = "AB";
      my $found_offset = 0;
      my $result_str = replace($str, $substr, $replace, 0, \$found_offset);
      unless ($found_offset == 1) {
        return 0;
      }
      unless ($result_str eq "aABe") {
        return 0;
      }
    }

    {
      my $str = "abcde";
      my $substr = "bcd";
      my $replace = "AB";
      my $found_offset = 0;
      my $result_str = replace($str, $substr, $replace, 2, \$found_offset);
      unless ($found_offset == -1) {
        return 0;
      }
      unless ($result_str eq $str) {
        return 0;
      }
    }

    {
      my $str = "abcde";
      my $substr = "abc";
      my $replace = "ABCD";
      my $found_offset = 0;
      my $result_str = replace($str, $substr, $replace, 0, \$found_offset);
      unless ($found_offset == 0) {
        return 0;
      }
      unless ($result_str eq "ABCDde") {
        return 0;
      }
    }

    {
      my $str = "abcde";
      my $substr = "cde";
      my $replace = "ABCD";
      my $found_offset = 0;
      my $result_str = replace($str, $substr, $replace, 0, \$found_offset);
      unless ($found_offset == 2) {
        return 0;
      }
      unless ($result_str eq "abABCD") {
        return 0;
      }
    }

    {
      my $str = "abcdcd";
      my $substr = "cd";
      my $replace = "A";
      my $found_offset = 0;
      my $result_str = replace($str, $substr, $replace, 4, \$found_offset);
      unless ($found_offset == 4) {
        return 0;
      }
      unless ($result_str eq "abcdA") {
        return 0;
      }
    }

    {
      my $str = "abcd";
      my $substr = "bc";
      my $replace = "";
      my $found_offset = 0;
      my $result_str = replace($str, $substr, $replace, 0, \$found_offset);

      unless ($found_offset == 1) {
        return 0;
      }
      unless ($result_str eq "ad") {
        return 0;
      }
    }

    return 1;
  }

  sub test_memcpyb : int () {

    {
      my $target = (byte[])"abcde";
      my $search = (byte[])"ABC";
      memcpyb($target, 0, $search, 0, 3);
      unless ((string)$target eq (string)"ABCde") {
        return 0;
      }
    }

    {
      my $target = (byte[])"abcde";
      my $search = (byte[])"ABC";
      memcpyb($target, 2, $search, 0, 3);
      unless ((string)$target eq (string)"abABC") {
        return 0;
      }
    }

    {
      my $target = (byte[])"abcde";
      my $search = (byte[])"ABC";
      memcpyb($target, 2, $search, 0, 3);
      unless ((string)$target eq (string)"abABC") {
        return 0;
      }
    }

    return 1;
  }

  sub test_memmoveb : int () {

    {
      my $target = (byte[])"abcde";
      my $search = (byte[])"ABC";
      memmoveb($target, 0, $search, 0, 3);
      unless ((string)$target eq (string)"ABCde") {
        return 0;
      }
    }

    {
      my $target = (byte[])"abcde";
      my $search = (byte[])"ABC";
      memmoveb($target, 2, $search, 0, 3);
      unless ((string)$target eq (string)"abABC") {
        return 0;
      }
    }

    {
      my $target = (byte[])"abcde";
      my $search = (byte[])"ABC";
      memmoveb($target, 2, $search, 0, 3);
      unless ((string)$target eq (string)"abABC") {
        return 0;
      }
    }

    {
      my $target = (byte[])"abcde";
      memmoveb($target, 0, $target, 2, 2);
      unless ((string)$target eq (string)"cdcde") {
        return 0;
      }
    }

    return 1;
  }

  sub test_index : int () {

    {
      my $target = "abcde";
      my $search = "bcd";
      my $pos = index($target, $search, 0);
      unless ($pos == 1) {
        return 0;
      }
    }

    {
      my $target = "abcde";
      my $search = "bcd";
      my $pos = index($target, $search, 1);
      unless ($pos == 1) {
        return 0;
      }
    }

    {
      my $target = "abcde";
      my $search = "bcd";
      my $pos = index($target, $search, 2);
      unless ($pos == -1) {
        return 0;
      }
    }

    {
      my $target = "abcde";
      my $search = "pq";
      my $pos = index($target, $search, 2);
      unless ($pos == -1) {
        return 0;
      }
    }

    return 1;
  }

  sub test_rindex : int () {

    {
      my $target = "abab";
      my $search = "ab";
      my $pos = rindex($target, $search, 3);
      unless ($pos == 2) {
        return 0;
      }
    }

    {
      my $target = "abab";
      my $search = "ab";
      my $pos = rindex($target, $search, 2);
      unless ($pos == 2) {
        return 0;
      }
    }

    {
      my $target = "abab";
      my $search = "ab";
      my $pos = rindex($target, $search, 1);
      unless ($pos == 0) {
        return 0;
      }
    }

    {
      my $target = "abab";
      my $search = "ab";
      my $pos = rindex($target, $search, 0);
      unless ($pos == 0) {
        return 0;
      }
    }

    {
      my $target = "abab";
      my $search = "pq";
      my $pos = rindex($target, $search, 2);
      unless ($pos == -1) {
        return 0;
      }
    }

    return 1;
  }

  sub test_contains : int () {

    {
      my $target = "abcde";
      my $search = "bcd";
      my $ret = contains($target, $search);
      unless ($ret) {
        return 0;
      }
    }

    {
      my $target = "abcde";
      my $search = "ppp";
      my $ret = contains($target, $search);
      if ($ret) {
        return 0;
      }
    }

    return 1;
  }

  sub test_join : int () {
    my $strings = ["abc", "def", "hij"];

    my $join = join(",", $strings);

    if ($join eq "abc,def,hij") {
      return 1;
    }

    return 0;
  }

  sub test_joinb : int () {
    my $nums = [(byte)1, 2, INT8_MIN()];

    my $join = joinb(",", $nums);

    if ($join eq "1,2,-128") {
      return 1;
    }

    return 0;
  }

  sub test_joins : int () {
    my $nums = [(short)1, 2, INT16_MIN()];

    my $join = joins(",", $nums);

    if ($join eq "1,2,-32768") {
      return 1;
    }

    return 0;
  }

  sub test_joini : int () {
    my $nums = [(int)1, 2, INT32_MIN()];

    my $join = joini(",", $nums);

    if ($join eq "1,2,-2147483648") {
      return 1;
    }

    return 0;
  }

  sub test_joinl : int () {
    my $nums = [(long)1, 2, INT64_MIN()];

    my $join = joinl(",", $nums);

    if ($join eq "1,2,-9223372036854775808") {
      return 1;
    }

    return 0;
  }

  sub test_joinf : int () {
    my $nums = [(float)1, 2, 2.45f];

    my $join = joinf(",", $nums);

    if ($join eq "1,2,2.45") {
      return 1;
    }

    return 0;
  }
  sub test_joind : int () {
    my $nums = [(double)1, 2, 2.45];

    my $join = joind(",", $nums);

    if ($join eq "1,2,2.45") {
      return 1;
    }

    return 0;
  }

  sub test_byte_constant : int () {
    if ((int)INT8_MIN() == (int)(byte)-128) {
      if ((int)INT8_MAX() == (int)(byte)127) {
        return 1;
      }
    }

    return 0;
  }

  sub test_short_constant : int () {
    if ((int)INT16_MIN() == (int)(short)-32768) {
      if ((int)INT16_MAX() == (int)(short)32767) {
        return 1;
      }
    }

    return 0;
  }

  sub test_int_constant : int () {
    if (INT32_MIN() == -2147483648) {
      if (INT32_MAX() == 2147483647) {
        return 1;
      }
    }

    return 0;
  }

  sub test_long_constant : int () {
    if (INT64_MIN() == -9223372036854775808L) {
      if (INT64_MAX() == 9223372036854775807L) {
        return 1;
      }
    }

    return 0;
  }

  sub test_new_object_array_proto : int () {
    my $minimals = new TestCase::Minimal[1];

    my $new_object_array = new_object_array_proto($minimals, 2);
    unless ($new_object_array isa TestCase::Minimal[]) {
      return 0;
    }
    my $new_minimals = (TestCase::Minimal[])$new_object_array;
    unless (@$new_minimals == 2) {
      return 0;
    }

    return 1;
  }
  sub test_sliceb : int () {
    {
      my $nums = [(byte)5, -7, 9, INT8_MIN(), 127, 15, 19];
      my $slice = sliceb($nums, 0, 7);
      unless (equals_barray($slice, [(byte)5, -7, 9, INT8_MIN(), 127, 15, 19])) {
        return 0;
      }
    }

    {
      my $nums = [(byte)5, -7, 9, INT8_MIN(), 127, 15, 19];
      my $slice = sliceb($nums, 1, 3);
      unless (equals_barray($slice, [(byte)-7, 9, INT8_MIN()])) {
        return 0;
      }
    }

    return 1;
  }
  sub test_slices : int () {
    {
      my $nums = [(short)5, -7, 9, INT16_MIN(), 127, 15, 19];
      my $slice = slices($nums, 0, 7);
      unless (equals_sarray($slice, [(short)5, -7, 9, INT16_MIN(), 127, 15, 19])) {
        return 0;
      }
    }
    {
      my $nums = [(short)5, -7, 9, INT16_MIN(), 127, 15, 19];
      my $slice = slices($nums, 1, 3);
      unless (equals_sarray($slice, [(short)-7, 9, INT16_MIN()])) {
        return 0;
      }
    }

    return 1;
  }
  sub test_slicei : int () {
    {
      my $nums = [(int)5, -7, 9, INT32_MIN(), 127, 15, 19];
      my $slice = slicei($nums, 0, 7);
      unless (equals_iarray($slice, [(int)5, -7, 9, INT32_MIN(), 127, 15, 19])) {
        return 0;
      }
    }
    {
      my $nums = [(int)5, -7, 9, INT32_MIN(), 127, 15, 19];
      my $slice = slicei($nums, 1, 3);
      unless (equals_iarray($slice, [(int)-7, 9, INT32_MIN()])) {
        return 0;
      }
    }
    return 1;
  }
  sub test_slicel : int () {
    {
      my $nums = [(long)5, -7, 9, INT64_MIN(), 127, 15, 19];
      my $slice = slicel($nums, 0, 7);
      unless (equals_larray($slice, [(long)5, -7, 9, INT64_MIN(), 127, 15, 19])) {
        return 0;
      }
    }
    {
      my $nums = [(long)5, -7, 9, INT64_MIN(), 127, 15, 19];
      my $slice = slicel($nums, 1, 3);
      unless (equals_larray($slice, [(long)-7, 9, INT64_MIN()])) {
        return 0;
      }
    }

    return 1;
  }
  sub test_slicef : int () {
    {
      my $nums = [(float)5, -7, 9, FLT_MIN(), 127, 15, 19];
      my $slice = slicef($nums, 0, 7);
      unless (equals_farray($slice, [(float)5, -7, 9, FLT_MIN(), 127, 15, 19])) {
        return 0;
      }
    }
    {
      my $nums = [(float)5, -7, 9, FLT_MIN(), 127, 15, 19];
      my $slice = slicef($nums, 1, 3);
      unless (equals_farray($slice, [(float)-7, 9, FLT_MIN()])) {
        return 0;
      }
    }


    return 1;
  }
  sub test_sliced : int () {
    {
      my $nums = [(double)5, -7, 9, DBL_MIN(), 127, 15, 19];
      my $slice = sliced($nums, 0, 7);
      unless (equals_darray($slice, [(double)5, -7, 9, DBL_MIN(), 127, 15, 19])) {
        return 0;
      }
    }
    {
      my $nums = [(double)5, -7, 9, DBL_MIN(), 127, 15, 19];
      my $slice = sliced($nums, 1, 3);
      unless (equals_darray($slice, [(double)-7, 9, DBL_MIN()])) {
        return 0;
      }
    }
    return 1;
  }
  sub test_sliceo : int () {

    {
      my $minimal1 = TestCase::Minimal->new;
      my $minimal2 = TestCase::Minimal->new;
      my $minimal3 = TestCase::Minimal->new;
      my $minimal4 = TestCase::Minimal->new;

      my $elems = [$minimal1, $minimal2, $minimal3, $minimal4];
      my $slice = (TestCase::Minimal[])sliceo($elems, 0, 4);
      unless (SPVM::Util->equals_oarray($slice, [$minimal1, $minimal2, $minimal3, $minimal4], SPVM::EqualityChecker::SameObject->new)) {
        return 0;
      }
    }
    {
      my $minimal1 = TestCase::Minimal->new;
      my $minimal2 = TestCase::Minimal->new;
      my $minimal3 = TestCase::Minimal->new;
      my $minimal4 = TestCase::Minimal->new;

      my $elems = [$minimal1, $minimal2, $minimal3, $minimal4];
      my $slice = (TestCase::Minimal[])sliceo($elems, 1, 2);
      unless (SPVM::Util->equals_oarray($slice, [$minimal2, $minimal3], SPVM::EqualityChecker::SameObject->new)) {
        return 0;
      }
    }
    return 1;
  }

  sub test_reverseb : int () {
    {
      my $nums = [(byte)1, 3, INT8_MAX()];
      reverseb($nums);
      unless (equals_barray($nums, [(byte)INT8_MAX(), 3, 1])) {
        return 0;
      }
    }
    {
      my $nums = [(byte)1, 3, 5, INT8_MAX()];
      reverseb($nums);
      unless (equals_barray($nums, [(byte)INT8_MAX(), 5, 3, 1])) {
        return 0;
      }
    }

    return 1;
  }
  sub test_reverses : int () {
    {
      my $nums = [(short)1, 3, INT16_MAX()];
      reverses($nums);
      unless (equals_sarray($nums, [(short)INT16_MAX(), 3, 1])) {
        return 0;
      }
    }
    {
      my $nums = [(short)1, 3, 5, INT16_MAX()];
      reverses($nums);
      unless (equals_sarray($nums, [(short)INT16_MAX(), 5, 3, 1])) {
        return 0;
      }
    }

    return 1;
  }
  sub test_reversei : int () {
    {
      my $nums = [(int)1, 3, INT32_MAX()];
      reversei($nums);
      unless (equals_iarray($nums, [(int)INT32_MAX(), 3, 1])) {
        return 0;
      }
    }
    {
      my $nums = [(int)1, 3, 5, INT32_MAX()];
      reversei($nums);
      unless (equals_iarray($nums, [(int)INT32_MAX(), 5, 3, 1])) {
        return 0;
      }
    }
    return 1;
  }
  sub test_reversel : int () {
    {
      my $nums = [(long)1, 3, INT64_MAX()];
      reversel($nums);
      unless (equals_larray($nums, [(long)INT64_MAX(), 3, 1])) {
        return 0;
      }
    }
    {
      my $nums = [(long)1, 3, 5, INT64_MAX()];
      reversel($nums);
      unless (equals_larray($nums, [(long)INT64_MAX(), 5, 3, 1])) {
        return 0;
      }
    }


    return 1;
  }
  sub test_reversef : int () {
    {
      my $nums = [(float)1, 3, FLT_MAX()];
      reversef($nums);
      unless (equals_farray($nums, [(float)FLT_MAX(), 3, 1])) {
        return 0;
      }
    }
    {
      my $nums = [(float)1, 3, 5, FLT_MAX()];
      reversef($nums);
      unless (equals_farray($nums, [(float)FLT_MAX(), 5, 3, 1])) {
        return 0;
      }
    }

    return 1;
  }
  sub test_reversed : int () {
    {
      my $nums = [(double)1, 3, DBL_MAX()];
      reversed($nums);
      unless (equals_darray($nums, [(double)DBL_MAX(), 3, 1])) {
        return 0;
      }
    }
    {
      my $nums = [(double)1, 3, 5, DBL_MAX()];
      reversed($nums);
      unless (equals_darray($nums, [(double)DBL_MAX(), 5, 3, 1])) {
        return 0;
      }
    }
    return 1;
  }

  sub test_reverseo : int () {
    {
      my $minimal1 = TestCase::Minimal->new;
      my $minimal2 = TestCase::Minimal->new;
      my $minimal3 = TestCase::Minimal->new;

      my $nums = [$minimal1, $minimal2, $minimal3];
      reverseo($nums);
      unless (SPVM::Util->equals_oarray($nums, [$minimal3, $minimal2, $minimal1], SPVM::EqualityChecker::SameObject->new)) {
        return 0;
      }
    }
    return 1;
  }

  sub test_equals_barray : int () {
    my $nums1 = [(byte)0, 1, INT8_MIN()];
    my $nums2 = [(byte)0, 1, INT8_MIN()];
    my $nums3 = [(byte)0, 1];
    my $nums4 = [(byte)0, 1, 2];
    {
      my $is_equals = equals_barray($nums1, $nums2);
      unless ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_barray($nums1, $nums3);
      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_barray($nums1, $nums4);
      if ($is_equals) {
        return 0;
      }
    }

    return 1;
  }

  sub test_equals_sarray : int () {
    my $nums1 = [(short)0, 1, INT16_MIN()];
    my $nums2 = [(short)0, 1, INT16_MIN()];
    my $nums3 = [(short)0, 1];
    my $nums4 = [(short)0, 1, 2];

    {
      my $is_equals = equals_sarray($nums1, $nums2);

      unless ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_sarray($nums1, $nums3);

      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_sarray($nums1, $nums4);
      if ($is_equals) {
        return 0;
      }
    }

    return 1;
  }

  sub test_equals_iarray : int () {
    my $nums1 = [(int)0, 1, INT32_MIN()];
    my $nums2 = [(int)0, 1, INT32_MIN()];
    my $nums3 = [(int)0, 1];
    my $nums4 = [(int)0, 1, 2];

    {
      my $is_equals = equals_iarray($nums1, $nums2);
      unless ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_iarray($nums1, $nums3);
      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_iarray($nums1, $nums4);
      if ($is_equals) {
        return 0;
      }
    }

    return 1;
  }

  sub test_equals_larray : int () {
    my $nums1 = [(long)0, 1, INT64_MIN()];
    my $nums2 = [(long)0, 1, INT64_MIN()];
    my $nums3 = [(long)0, 1];
    my $nums4 = [(long)0, 1, 2];

    {
      my $is_equals = equals_larray($nums1, $nums2);
      unless ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_larray($nums1, $nums3);
      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_larray($nums1, $nums4);
      if ($is_equals) {
        return 0;
      }
    }

    return 1;
  }

  sub test_equals_farray : int () {
    my $nums1 = [0.0f, 1.5f, FLT_MIN()];
    my $nums2 = [0.0f, 1.5f, FLT_MIN()];
    my $nums3 = [0.0f, 1.5f];
    my $nums4 = [(float)0.0f, 1.5f, 0.5f];

    {
      my $is_equals = equals_farray($nums1, $nums2);
      unless ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_farray($nums1, $nums3);
      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_farray($nums1, $nums4);
      if ($is_equals) {
        return 0;
      }
    }

    return 1;
  }

  sub test_equals_darray : int () {
    my $nums1 = [0.0, 1.5, DBL_MIN()];
    my $nums2 = [0.0, 1.5, DBL_MIN()];
    my $nums3 = [0.0, 1.5];
    my $nums4 = [(double)0.0, 1.5, 0.5];

    {
      my $is_equals = equals_darray($nums1, $nums2);
      unless ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_darray($nums1, $nums3);
      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_darray($nums1, $nums4);
      if ($is_equals) {
        return 0;
      }
    }

    return 1;
  }

  sub test_equals_strarray : int () {
    my $strings1 = ["abc", "def", "ghi"];
    my $strings2 = ["abc", "def", "ghi"];
    my $strings3 = ["abc", "def"];
    my $strings4 = ["abc", "def", "xxx"];

    {
      my $is_equals = equals_strarray($strings1, $strings2);
      unless ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_strarray($strings1, $strings3);
      if ($is_equals) {
        return 0;
      }
    }
    {
      my $is_equals = equals_strarray($strings1, $strings4);
      if ($is_equals) {
        return 0;
      }
    }

    return 1;
  }

  sub test_E : int () {
    unless (E() == 2.7182818284590452354) {
      return 0;
    }
    return 1;
  }

  sub test_PI : int () {
    unless (PI() == 3.14159265358979323846) {
      return 0;
    }
    return 1;
  }

  sub test_get_type_name : int () {
    {
      my $minimal = TestCase::Minimal->new;
      my $type_name = get_type_name($minimal);
      
      unless ($type_name eq "TestCase::Minimal") {
        return 0;
      }
    }
    {
      my $obj = new TestCase::Minimal[3];
      my $type_name = get_type_name($obj);
      
      unless ($type_name eq "TestCase::Minimal[]") {
        return 0;
      }
    }
    {
      my $obj = new TestCase::Minimal[][3];
      my $type_name = get_type_name($obj);
      
      unless ($type_name eq "TestCase::Minimal[][]") {
        return 0;
      }
    }
    
    return 1;
  }

  sub test_copy_str : int () {
    my $string = "abc";
    my $string_out = copy_str($string);

    unless ($string_out eq "abc") {
      return 0;
    }

    return 1;
  }

  sub test_copy_barray : int () {
    my $nums = [(byte)1, 2, INT8_MIN()];
    my $nums_out = copy_barray($nums);

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

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

    unless ($nums->[2] == INT8_MIN()) {
      return 0;
    }

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

    return 1;
  }

  sub test_copy_sarray : int () {
    my $nums = [(short)1, 2, INT16_MIN()];
    my $nums_out = copy_sarray($nums);

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

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

    unless ($nums->[2] == INT16_MIN()) {
      return 0;
    }

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

    return 1;
  }

  sub test_copy_iarray : int () {
    my $nums = [(int)1, 2, INT32_MIN()];
    my $nums_out = copy_iarray($nums);

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

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

    unless ($nums->[2] == INT32_MIN()) {
      return 0;
    }

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

    return 1;
  }

  sub test_copy_larray : int () {
    my $nums = [(long)1, 2, INT64_MIN()];
    my $nums_out = copy_larray($nums);

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

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

    unless ($nums->[2] == INT64_MIN()) {
      return 0;
    }

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

    return 1;
  }

  sub test_copy_farray : int () {
    my $nums = [(float)0.5f, 0.25f, FLT_MIN()];
    my $nums_out = copy_farray($nums);

    unless ($nums->[0] == 0.5) {
      return 0;
    }

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

    unless ($nums->[2] == FLT_MIN()) {
      return 0;
    }

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

    return 1;
  }

  sub test_copy_darray : int () {
    my $nums = [(double)0.5, 0.25, DBL_MIN()];
    my $nums_out = copy_darray($nums);

    unless ($nums->[0] == 0.5) {
      return 0;
    }

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

    unless ($nums->[2] == DBL_MIN()) {
      return 0;
    }

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

    return 1;
  }

  sub test_copy_strarray : int () {
    my $strings = ["abc", "def", "hij"];
    my $strings_out = copy_strarray($strings);

    unless ($strings->[0] eq $strings_out->[0]) {
      return 0;
    }
    unless ($strings->[1] eq $strings_out->[1]) {
      return 0;
    }
    unless ($strings->[2] eq $strings_out->[2]) {
      return 0;
    }

    unless ($strings->[0] != $strings_out->[0]) {
      return 0;
    }

    unless ($strings->[1] != $strings_out->[1]) {
      return 0;
    }

    unless ($strings->[2] != $strings_out->[2]) {
      return 0;
    }

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

    return 1;
  }
}