#!perl
# Copyright (C) 2001-2006, The Perl Foundation.
# $Id: hash.t 16171 2006-12-17 19:06:36Z paultcochrane $

use strict;
use warnings;
use lib qw( . lib ../lib ../../lib );

use Test::More;
use Parrot::Test tests => 39;

=head1 NAME

t/pmc/hash.t - Test the Hash PMC

=head1 SYNOPSIS

    % prove t/pmc/hash.t

=head1 DESCRIPTION

Tests the C<Hash> PMC. Checks key access with various types of
normal and potentially hazardous keys. Does a bit of stress testing as
well.

=cut

pasm_output_is( <<CODE, <<OUTPUT, "Initial Hash tests" );
    new	P0, .Hash

    set	P0["foo"], -7
    set	P0["bar"], 3.5
    set	P0["baz"], "value"

    set	I0, P0["foo"]
    set	N0, P0["bar"]
    set	S0, P0["baz"]

    eq	I0,-7,OK_1
    print	"not "
OK_1:    print	"ok 1\\n"
    eq	N0,3.500000,OK_2
    print	N0
OK_2:    print	"ok 2\\n"
    eq	S0,"value",OK_3
    print	S0
OK_3:    print	"ok 3\\n"

        set     S1, "oof"
        set     S2, "rab"
        set     S3, "zab"

    set	P0[S1], 7
    set	P0[S2], -3.5
    set	P0[S3], "VALUE"

    set	I0, P0[S1]
    set	N0, P0[S2]
    set	S0, P0[S3]

    eq	I0,7,OK_4
    print	"not "
OK_4:    print	"ok 4\\n"
    eq	N0,-3.500000,OK_5
    print	N0
OK_5:    print	"ok 5\\n"
    eq	S0,"VALUE",OK_6
    print	S0
OK_6:    print	"ok 6\\n"

    end
CODE
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "more than one Hash" );
    new P0, .Hash
    set S0, "key"
    set P0[S0], 1

        new P1, .Hash
        set S1, "another_key"
        set P1[S1], 2

    set I0, P0[S0]
    set I1, P1[S1]

    print I0
    print "\n"
    print I1
    print "\n"
        end
CODE
1
2
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "hash keys with nulls in them" );
    new P0, .Hash
    set S0, "parp\0me"
    set S1, "parp\0you"

    set P0[S0], 1		# $P0{parp\0me} = 1
    set P0[S1], 2		# $P0{parp\0you} = 2

    set I0, P0[S0]
    set I1, P0[S1]

    print I0
    print "\n"
    print I1
    print "\n"
    end
CODE
1
2
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "nearly the same hash keys" );
    new P0, .Hash
    set S0, "a\0"
    set S1, "\0a"

    set P0[S0], 1
    set P0[S1], 2

    set I0, P0[S0]
    set I1, P0[S1]

    print I0
    print "\n"
    print I1
    print "\n"

    end
CODE
1
2
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "The same hash keys" );
    new P0, .Hash
    set S0, "Happy"
    set S1, "Happy"

    set P0[S0], 1
    set I0, P0[S0]
    print I0
    print "\n"

    set P0[S1], 2
    set I1, P0[S1]

    print I1
    print "\n"

    end
CODE
1
2
OUTPUT

# NB Next test depends on "key2" hashing to zero, which it does with
# the current algorithm; if the algorithm changes, change the test!

pasm_output_is( <<'CODE', <<OUTPUT, "key that hashes to zero" );
        new P0, .Hash
        set S0, "key2"
        set P0[S0], 1
        set I0, P0[S0]
    print I0
    print "\n"
    end
CODE
1
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "size of the hash" );
    new P0, .Hash

    set P0["0"], 1
    set I0, P0
    print I0
    print "\n"

    set P0["1"], 1
    set I0, P0
    print I0
    print "\n"

    set P0["0"], 1
    set I0, P0
    print I0
    print "\n"

    end
CODE
1
2
2
OUTPUT

pasm_output_is( <<CODE, <<OUTPUT, "stress test: loop(set, check)" );
    new	P0, .Hash

        set I0, 200
        set S0, "mikey"
        set P0[S0], "base"
        concat S1, S0, "s"
        set P0[S1], "bases"
        set S2, I0
        concat S1, S0, S2
        set P0[S1], "start"
        set S3, P0["mikey"]
        print S3
        print "\\n"
        set S3, P0["mikeys"]
        print S3
        print "\\n"
        set S3, P0["mikey200"]
        print S3
        print "\\n"
LOOP:
        eq I0, 0, DONE
        sub I0, I0, 1
        set S2, I0
        concat S1, S0, S2
        concat S4, S0, S2
        eq S1, S4, L1
        print "concat mismatch: "
        print S1
        print " vs "
        print S4
        print "\\n"
L1:
        set P0[S1], I0
        set I1, P0[S1]
        eq I0, I1, L2
        print "lookup mismatch: "
        print I0
        print " vs "
        print I1
        print "\\n"
L2:
        branch LOOP
DONE:
        set I0, P0["mikey199"]
        print I0
        print "\\n"
        set I0, P0["mikey117"]
        print I0
        print "\\n"
        set I0, P0["mikey1"]
        print I0
        print "\\n"
        set I0, P0["mikey23"]
        print I0
        print "\\n"
        set I0, P0["mikey832"]
        print I0
        print "\\n"
        end
CODE
base
bases
start
199
117
1
23
0
OUTPUT

## stuff them in, and check periodically that we can pull selected ones out.
pir_output_is( <<'CODE', <<OUTPUT, "stress test: lots of keys" );
.sub set_multiple_keys
    .param pmc hash
        .param int key_index
        .param int step
    .param int count

again:
    if count <= 0 goto ret
    S0 = key_index
    S1 = concat "key", S0
    S2 = concat "value", S0
    hash[S1] = S2
    key_index = key_index + step
    count = count - 1
    goto again
ret:
.end

.sub print_multiple_keys
    .param pmc hash
        .param int key_index
        .param int step
    .param int count

again:
    if count <= 0 goto ret
    S0 = key_index
    S1 = concat "key", S0
    print S1
    print " => "
    I2 = exists hash[S1]
    if I2 goto print_value
    print "(undef)"
    goto print_end
print_value:
    S2 = hash[S1]
    print S2
print_end:
    print "\n"
    key_index = key_index + step
    count = count - 1
    goto again
ret:
.end

.sub delete_multiple_keys
    .param pmc hash
    .param int key_index
    .param int step
    .param int count

again:
    if count <= 0 goto ret
    S0 = key_index
    S1 = concat "key", S0
    delete hash[S1]
    key_index = key_index + step
    count = count - 1
    goto again
ret:
.end

.sub _main :main
    new	P30, .Hash
    print "round 1\n"
    I29 = 1
    I30 = 1000
    I31 = 1000
    set_multiple_keys(P30, I29, I30, I31)
    I20 = 3
    print_multiple_keys(P30, I29, I30, I20)
    print "round 2\n"
    I21 = 100000
    set_multiple_keys(P30, I21, I30, I31)
    print_multiple_keys(P30, I29, I30, I20)
    print_multiple_keys(P30, I21, I30, I20)
    print "round 3\n"
    I22 = 50000
    set_multiple_keys(P30, I22, I29, I22)
    print_multiple_keys(P30, I29, I30, I20)
    print_multiple_keys(P30, I22, I30, I20)
    print "round 4\n"
    delete_multiple_keys(P30, I22, I29, I22)
    print_multiple_keys(P30, I29, I30, I20)
    print_multiple_keys(P30, I22, I30, I20)
    print "done.\n"
.end
CODE
round 1
key1 => value1
key1001 => value1001
key2001 => value2001
round 2
key1 => value1
key1001 => value1001
key2001 => value2001
key100000 => value100000
key101000 => value101000
key102000 => value102000
round 3
key1 => value1
key1001 => value1001
key2001 => value2001
key50000 => value50000
key51000 => value51000
key52000 => value52000
round 4
key1 => value1
key1001 => value1001
key2001 => value2001
key50000 => (undef)
key51000 => (undef)
key52000 => (undef)
done.
OUTPUT

# Check all values after setting all of them
pasm_output_is( <<CODE, <<OUTPUT, "stress test: loop(set), loop(check)" );
    new	P0, .Hash

        set I0, 200
        set S0, "mikey"
SETLOOP:
        eq I0, 0, DONE
        sub I0, I0, 1
        set S2, I0
        concat S1, S0, S2
        set P0[S1], I0
        branch SETLOOP

        set I0, 200
GETLOOP:
        eq I0, 0, DONE
        sub I0, I0, 1
        set S2, I0
        concat S1, S0, S2
        set I1, P0[S1]
        eq I0, I1, L2
        print "lookup mismatch: "
        print I0
        print " vs "
        print I1
        print "\\n"
L2:
        branch GETLOOP

DONE:
        print "done\\n"
        end
CODE
done
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Testing two hash indices with integers at a time" );
      new P0, .Hash

      set P0["foo"],37
      set P0["bar"],-15

      set I0,P0["foo"]
      eq I0,37,OK_1
      print "not "
OK_1: print "ok 1\n"

      set I0,P0["bar"]
      eq I0,-15,OK_2
      print "not "
OK_2: print "ok 2\n"

      set S1,"foo"
      set I0,P0[S1]
      eq I0,37,OK_3
      print "not "
OK_3: print "ok 3\n"

      set S1,"bar"
      set I0,P0[S1]
      eq I0,-15,OK_4
      print "not "
OK_4: print "ok 4\n"

      end
CODE
ok 1
ok 2
ok 3
ok 4
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Testing two hash indices with numbers at a time" );
      new P0, .Hash

      set P0["foo"],37.100000
      set P0["bar"],-15.100000

      set N0,P0["foo"]
      eq N0,37.100000,OK_1
      print "not "
OK_1: print "ok 1\n"

      set N0,P0["bar"]
      eq N0,-15.100000,OK_2
      print "not "
OK_2: print "ok 2\n"

      set S1,"foo"
      set N0,P0[S1]
      eq N0,37.100000,OK_3
      print "not "
OK_3: print "ok 3\n"

      set S1,"bar"
      set N0,P0[S1]
      eq N0,-15.100000,OK_4
      print "not "
OK_4: print "ok 4\n"

      end
CODE
ok 1
ok 2
ok 3
ok 4
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Testing two hash indices with strings at a time" );
      new P0, .Hash

      set P0["foo"],"baz"
      set P0["bar"],"qux"

      set S0,P0["foo"]
      eq S0,"baz",OK_1
      print "not "
OK_1: print "ok 1\n"

      set S0,P0["bar"]
      eq S0,"qux",OK_2
      print "not "
OK_2: print "ok 2\n"

      set S1,"foo"
      set S0,P0[S1]
      eq S0,"baz",OK_3
      print "not "
OK_3: print "ok 3\n"

      set S1,"bar"
      set S0,P0[S1]
      eq S0,"qux",OK_4
      print "not "
OK_4: print "ok 4\n"

      end
CODE
ok 1
ok 2
ok 3
ok 4
OUTPUT

# So far, we've only used INTVALs, FLOATVALs and STRINGs as values
# and/or keys. Now we try PMCs.

pasm_output_is( <<'CODE', <<OUTPUT, "Setting & getting scalar PMCs" );
      new P0, .Hash
      new P1, .Integer
      new P2, .Integer

      set S0, "non-PMC key"

      set P1, 10
      set P0[S0], P1
      set P2, P0[S0]
      eq P2, P1, OK1
      print "not "
OK1:  print "ok 1\n"

      set P1, -1234.000000
      set P0[S0], P1
      set P2, P0[S0]
      eq P2, P1, OK2
      print "not "
OK2:  print "ok 2\n"

      set P1, "abcdefghijklmnopq"
      set P0[S0], P1
      set P2, P0[S0]
      eq P2, P1, OK3
      print "not "
OK3:  print "ok 3\n"

      new P1, .Undef
      set P0[S0], P1
      set P2, P0[S0]
      typeof S1, P2
      eq S1, "Undef", OK4
      print "not "
OK4:  print "ok 4\n"

      end
CODE
ok 1
ok 2
ok 3
ok 4
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Setting scalar PMCs & getting scalar values" );
      new P0, .Hash
      new P1, .Integer

      set S0, "A rather large key"

      set I0, 10
      set P1, I0
      set P0[S0], P1
      set I1, P0[S0]
      eq I1, I0, OK1
      print "not "
OK1:  print "ok 1\n"

      set N0, -1234.000000
      set P1, N0
      set P0[S0], P1
      set N1, P0[S0]
      eq N1, N0, OK2
      print "not "
OK2:  print "ok 2\n"

      set S1, "abcdefghijklmnopq"
      set P1, S1
      set P0[S0], P1
      set S2, P0[S0]
      eq S2, S1, OK3
      print "not "
OK3:  print "ok 3\n"

      end
CODE
ok 1
ok 2
ok 3
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Getting values from undefined keys" );
      new P2, .Hash

      set I0, P2["qwerty"]
      set N0, P2["asdfgh"]
      set S0, P2["zxcvbn"]
      set P0, P2["123456"]

      eq I0, 0, OK1
      print "not "
OK1:  print "ok 1\n"

      eq N0, 0.0, OK2
      print "not "
OK2:  print "ok 2\n"

      eq S0, "", OK3
      print "not "
OK3:  print "ok 3\n"

      if_null P0, OK4
      print "not "
OK4:  print "ok 4\n"
      end
CODE
ok 1
ok 2
ok 3
ok 4
OUTPUT

pasm_output_is( <<CODE, <<OUTPUT, "Setting & getting non-scalar PMCs" );
        new P0,.Hash
        new P1,.ResizablePMCArray
        new P2,.ResizablePMCArray
        set P1[4],"string"
        set P0["one"],P1
        set P2,P0["one"]
        set S0,P2[4]
        print S0
        print "\\n"
        end
CODE
string
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Testing clone" );
    new P0, .Hash
    set S0, "a"
    set P0[S0], S0
    new P2, .ResizablePMCArray
    set P2, 2
    set P0["b"], P2

    # P0 = { a => "a", b => [undef, undef] }

    clone P1, P0
    set P0["c"], 4
    set P3, P0["b"]
    set P3, 3
    set P0["b"], P3
    set P1["a"], "A"

    # P0 = { a => "a", b => [undef, undef, undef], c => 4 }
    # P1 = { a => "A", b => [undef, undef] }

    set S0, P0["a"]
    eq S0, "a", ok1
    print "not "
ok1:
    print "ok 1\n"

    set P5, P0["b"]
    set I0, P5
    eq I0, 3, ok2
    print "not "
ok2:
    print "ok 2\n"

    set I0, P0["c"]
    eq I0, 4, ok3
    print "not "
ok3:
    print "ok 3\n"

    set S0, P1["a"]
    eq S0, "A", ok4
    print "not "
ok4:
    print "ok 4\n"

    set P5, P1["b"]
    set I0, P5
    eq I0, 2, ok5
    print "not ("
    print I0
    print ") "
ok5:
    print "ok 5\n"

# XXX: this should return undef or something, but it dies instead.
#     set P3, P0["c"]
#     unless P3, ok6
#     print "not "
# ok6:
#     print "ok 6\n"
     end
CODE
ok 1
ok 2
ok 3
ok 4
ok 5
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Compound keys" );
    new P0, .Hash
    new P1, .Hash
    new P2, .ResizablePMCArray
    set P1["b"], "ab"
    set P0["a"], P1
    set S0, P0["a";"b"]
    eq S0, "ab", ok1
    print "not "
ok1:
    print "ok 1\n"
    set P2[20], 77
    set P1["n"], P2
    set I0, P0["a";"n";20]
    eq I0, 77, ok2
    print "not "
ok2:
    print "ok 2\n"
    set S0, "a"
    set S1, "n"
    set I0, 20
    set I0, P0[S0;S1;I0]
    eq I0, 77, ok3
    print "not "
ok3:
    print "ok 3\n"
    set P0["c"], P2
    set P2[33], P1
    set S0, P0["c";33;"b"]
    eq S0, "ab", ok4
    print "not "
ok4:
    print "ok 4\n"
    set S0, "c"
    set I1, 33
    set S2, "b"
    set S0, P0[S0;I1;S2]
    eq S0, "ab", ok5
    print "not "
ok5:
    print "ok 5\n"
    set P1["b"], 47.11
    set N0, P0["c";I1;S2]
    eq N0, 47.11, ok6
    print "not "
ok6:
    print "ok 6\n"
    end
CODE
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Getting PMCs from compound keys" );
    new P0, .Hash
    new P1, .Hash
    new P2, .Integer
    set P2, 12
    set P1["b"], P2
    set P0["a"], P1
    set P3, P0["a";"b"]
    print P3
    print "\n"
    end
CODE
12
OUTPUT

pasm_output_is( << 'CODE', << 'OUTPUT', "Getting PMCs from string;int compound keys" );
    new P0, .Hash
    new P1, .Hash
    new P2, .Integer
    set P2, 4
    set P1[9], P2
    set I0, P1[9]
    print I0
    print "\n"
    set P0["a"], P1
    set I0, P0["a";9]
    print "Four is "
    print I0
    print "\n"
    end
CODE
4
Four is 4
OUTPUT

# A hash is only false if it has size 0

pasm_output_is( <<'CODE', <<OUTPUT, "if (Hash)" );
      new P0, .Hash

      if P0, BAD1
      print "ok 1\n"
      branch OK1
BAD1: print "not ok 1\n"
OK1:

      set P0["key"], "value"
      if P0, OK2
      print "not "
OK2:  print "ok 2\n"

      set P0["key"], ""
      if P0, OK3
      print "not "
OK3:  print "ok 3\n"

      new P1, .Undef
      set P0["key"], P1
      if P0, OK4
      print "not "
OK4:  print "ok 4\n"

      end
CODE
ok 1
ok 2
ok 3
ok 4
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "unless (Hash)" );
      new P0, .Hash

      unless P0, OK1
      print "not "
OK1:  print "ok 1\n"

      set P0["key"], "value"
      unless P0, BAD2
      print "ok 2\n"
      branch OK2
BAD2: print "not ok 2"
OK2:

      set P0["key"], "\0"
      unless P0, BAD3
      print "ok 3\n"
      branch OK3
BAD3: print "not ok 3"
OK3:

      new P1, .Undef
      set P0["key"], P1
      unless P0, BAD4
      print "ok 4\n"
      branch OK4
BAD4: print "not ok 4"
OK4:

      end
CODE
ok 1
ok 2
ok 3
ok 4
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "defined" );
    new P0, .Hash
    defined I0, P0
    print I0
    print "\n"
    defined I0, P1
    print I0
    print "\n"
    set P0["a"], 1
    defined I0, P0["a"]
    print I0
    print "\n"
    defined I0, P0["b"]
    print I0
    print "\n"
    new P1, .Undef
    set P0["c"], P1
    defined I0, P0["c"]
    print I0
    print "\n"
    end

CODE
1
0
1
0
0
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "exists" );
    new P0, .Hash
    set P0["a"], 1
    exists I0, P0["a"]
    print I0
    print "\n"
    exists I0, P0["b"]
    print I0
    print "\n"
    new P1, .Undef
    set P0["c"], P1
    exists I0, P0["c"]
    print I0
    print "\n"
    end

CODE
1
0
1
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "delete" );
    new P0, .Hash
    set P0["a"], 1
    exists I0, P0["a"]
    print I0
    print "\n"
    delete P0["a"]
    exists I0, P0["a"]
    print I0
    print "\n"
    end
CODE
1
0
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Cloning keys" );
    new P10, .Hash
    new P1, .Key

    set P1, "Bar"
    set P10[P1], "Food\n"
    clone P2, P1
    set S0, P10[P2]
    print S0

    set S1, "Baa"
    set P10[S1], "Sheep\n"
    clone S2, S1
    set S0, P10[S2]
    print S0

    end
CODE
Food
Sheep
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "Cloning PMC vals" );
    new P10, .Hash
    new P1, .Undef
    set P1, "value\n"
    set P10["str"], P1
    new P1, .Undef
    set P1, 42
    set P10["int"], P1
    clone P2, P10
    set P0, P2["int"]
    print P0
    set P0, P2["str"]
    print P0
    end
CODE
42value
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "entry types - type_keyed" );
.include "pmctypes.pasm"
    new P1, .Hash

    new P2, .Integer
    set P1["Integer"], P2
    typeof I0, P1["Integer"]
    eq I0, .Integer, ok1
    print "not "
ok1:print "Integer\n"

    new P3, .Integer
    set P1["Integer"], P3
    typeof I0, P1["Integer"]
    eq I0, .Integer, ok2
    print "not "
ok2:print "Integer\n"

    set P1["native int"], -123456
    typeof I0, P1["native int"]
    eq I0, .Integer, ok3
    print "not "
ok3:print "Integer\n"

    set P1["native float"], -123.456
    typeof I0, P1["native float"]
    eq I0, .Float, ok4
    print "not "
ok4:print "Float\n"

    set P1["native string"], "hello world\n"
    typeof I0, P1["native string"]
    eq I0, .String, ok5
    print "not "
ok5:print "String\n"

    end
CODE
Integer
Integer
Integer
Float
String
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "delete and free_list" );
    set I2, 10
    set I1, 1
    new P0, .SArray
    set P0, 1
    new P1, .Hash
outer:
    set P0[0], I1
    sprintf S0, "ok %vd\n", P0
    set P1[S0], S0
    set I0, 100
lp:
    set P1["key"], 1
    delete P1["key"]
    dec I0
    if I0, lp

    set S1, P1[S0]
    print S1
    inc I1
    le I1, I2, outer
    set I0, P1
    print I0
    print "\n"
    end

CODE
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6
ok 7
ok 8
ok 9
ok 10
10
OUTPUT

pasm_output_is( <<'CODE', <<OUTPUT, "exists with constant string key" );
    new P16, .Hash
    set P16["key1"], "value for key1\n"
    set S16, P16["key1"]
    print S16
    set I16, 777777777
    print I16
    print "\n"
    exists I17, P16["key1"]
    print I17
    print "\n"
    exists I17, P16["no such"]
    print I17
    print "\n"
    end

CODE
value for key1
777777777
1
0
OUTPUT

pir_output_is( << 'CODE', << 'OUTPUT', "Hash in PIR" );

.sub _main
    .local pmc hash1
    hash1 = new Hash
    hash1['X'] = 'U'
    .local string val1
    val1 = hash1['X']
    print val1
    print "\n"
    end
.end
CODE
U
OUTPUT

pir_output_is( << 'CODE', << 'OUTPUT', "Setting with compound keys" );

.sub _main
    .local pmc outer_hash
    outer_hash = new Hash
    .local pmc inner_hash
    inner_hash = new Hash
    .local pmc inner_array
    inner_array = new ResizablePMCArray
    .local string elem_string
    .local int    elem_int
    .local pmc    elem_pmc
    .local num    elem_num

    # setting and retrieving strings in an inner ResizablePMCArray
    inner_array[128] = 'inner_array:128'
    outer_hash['inner_array'] = inner_array
    elem_string = outer_hash['inner_array';128]
    print elem_string
    print "\n"
    outer_hash['inner_array';128] = 'changed inner_array:128'
    elem_string = outer_hash['inner_array';128]
    print elem_string
    print "\n"

    # setting and retrieving strings in an inner Hash
    inner_hash['129'] = 'inner_hash:129'
    outer_hash['inner_hash'] = inner_hash
    elem_string = outer_hash['inner_hash';'129']
    print elem_string
    print "\n"
    outer_hash['inner_hash';'129'] = 'changed inner_hash:129'
    elem_string = outer_hash['inner_hash';'129']
    print elem_string
    print "\n"

    # setting and retrieving integer in an inner ResizablePMCArray
    inner_array[130] = 130
    outer_hash['inner_array'] = inner_array
    elem_int = outer_hash['inner_array';130]
    print elem_int
    print "\n"
    outer_hash['inner_array';130] = -130
    elem_int = outer_hash['inner_array';130]
    print elem_int
    print "\n"

    # setting and retrieving integer in an inner Hash
    inner_hash['131'] = 131
    outer_hash['inner_hash'] = inner_hash
    elem_int = outer_hash['inner_hash';'131']
    print elem_int
    print "\n"
    outer_hash['inner_hash';'131'] = -131
    elem_int = outer_hash['inner_hash';'131']
    print elem_int
    print "\n"

    # setting and retrieving a PMC in an inner ResizablePMCArray
    .local pmc in_pmc
    in_pmc = new String
    in_pmc = 'inner_array:132'
    inner_array[132] = in_pmc
    outer_hash['inner_array'] = inner_array
    elem_pmc = outer_hash['inner_array';132]
    print elem_pmc
    print "\n"
    in_pmc = 'changed inner_array:132'
    outer_hash['inner_array';132] = in_pmc
    elem_pmc = outer_hash['inner_array';132]
    print elem_pmc
    print "\n"

    # setting and retrieving a PMC in an inner Hash
    in_pmc = 'inner_array:133'
    inner_hash['133'] = in_pmc
    outer_hash['inner_hash'] = inner_hash
    elem_string = outer_hash['inner_hash';'133']
    print elem_string
    print "\n"
    in_pmc = 'changed inner_hash:133'
    outer_hash['inner_hash';'133'] = in_pmc
    elem_string = outer_hash['inner_hash';'133']
    print elem_string
    print "\n"

    # setting and retrieving a float in an inner ResizablePMCArray
    inner_array[134] = 134.134
    outer_hash['inner_array'] = inner_array
    elem_num = outer_hash['inner_array';134]
    print elem_num
    print "\n"
    outer_hash['inner_array';134] = -134.134
    elem_num = outer_hash['inner_array';134]
    print elem_num
    print "\n"

    # setting and retrieving a float in an inner Hash
    inner_hash['135'] = 135.135
    outer_hash['inner_hash'] = inner_hash
    elem_num = outer_hash['inner_hash';'135']
    print elem_num
    print "\n"
    outer_hash['inner_hash';'135'] = -135.135
    elem_num = outer_hash['inner_hash';'135']
    print elem_num
    print "\n"

    end
.end
CODE
inner_array:128
changed inner_array:128
inner_hash:129
changed inner_hash:129
130
-130
131
-131
inner_array:132
changed inner_array:132
inner_array:133
changed inner_hash:133
134.134000
-134.134000
135.135000
-135.135000
OUTPUT

pasm_output_is( << 'CODE', << 'OUTPUT', "mutating the lookup string" );
    new P0, .Hash
    set P0["a"], "one"
    set P0["ab"], "two"
    set P0["abc"], "three"

    set S0, "a"
    set S1, P0[S0]
    print S1
    print "\n"

    concat S0, "b"
    set S1, P0[S0]
    print S1
    print "\n"

    concat S0, "c"
    set S1, P0[S0]
    print S1
    print "\n"

    end
CODE
one
two
three
OUTPUT

pir_output_is( << 'CODE', << 'OUTPUT', "check whether interface is done" );

.sub _main
    .local pmc pmc1
    pmc1 = new Hash
    .local int bool1
    does bool1, pmc1, "scalar"
    print bool1
    print "\n"
    does bool1, pmc1, "hash"
    print bool1
    print "\n"
    does bool1, pmc1, "no_interface"
    print bool1
    print "\n"
    end
.end
CODE
0
1
0
OUTPUT

pir_output_is( << 'CODE', << 'OUTPUT', "iter" );

.sub __main__ :main
    new P0, .Hash
    set P0['a'], 'x'
    iter P1, P0
    if P1 goto ok1
    print "Not empty?\n"
    shift P2, P1
    print P2
    print "\n"
ok1:
    iter P1, P0
    shift P2, P1
    print P2
    print "\n"
    unless P1 goto ok2
    print "Surprise!\n"
ok2:
    end
.end
CODE
a
OUTPUT

pir_output_is( << 'CODE', << 'OUTPUT', "broken delete, thx to azuroth on irc" );
.include "iterator.pasm"

.sub main :main
  .local pmc thash

  # just put in some dummy data...
  thash = new Hash
  thash["a"] = "b"
  thash["c"] = "d"
  thash["e"] = "f"

  .local pmc iter
  iter = new Iterator, thash
  iter = .ITERATE_FROM_START

  .local string key

  # go through the hash, print out all the keys: should be a c and e
preit_loop:
  unless iter goto preit_end

  key = shift iter
  print key
  print "\n"

  branch preit_loop
preit_end:

  # get rid of the c element?
  delete thash["c"]

  print "after deletion\n"

  iter = new Iterator, thash
  iter = .ITERATE_FROM_START

  # go through the hash, print out all the keys... I believe it should be a and e?
  # it actually outputs a, c and e.
postit_loop:
  unless iter goto postit_end

  key = shift iter
  print key
  print "\n"

  branch postit_loop
postit_end:

.end
CODE
a
c
e
after deletion
a
e
OUTPUT

pir_output_is( << 'CODE', << 'OUTPUT', "unicode keys (register) (RT #39249)" );
.sub test
  $P1 = new .Hash
  $S99 = unicode:"\u7777"
  $P1[$S99] = "ok"
  $S1 = $P1[$S99]
  say $S1
.end
CODE
ok
OUTPUT

pir_output_is( << 'CODE', << 'OUTPUT', "unicode keys (literal) (RT ##39249)" );
.sub test
  $P1 = new .Hash
  $P1[unicode:"\u7777"] = "ok"
  $S1 = $P1[unicode:"\u7777"]
  say $S1
  $S2 = unicode:"\u7777"
  $S1 = $P1[$S2]
  say $S1
.end
CODE
ok
ok
OUTPUT

# Local Variables:
#   mode: cperl
#   cperl-indent-level: 4
#   fill-column: 100
# End:
# vim: expandtab shiftwidth=4:
