Author: leo
Date: Wed Jan 11 08:18:15 2006
New Revision: 11087
Modified:
trunk/src/classes/key.pmc
trunk/src/classes/orderedhash.pmc
trunk/src/classes/unmanagedstruct.pmc
trunk/t/pmc/orderedhash.t
Log:
OrderedHash - total rewrite
* base OrderedHash on Hash, not PerlArray + Hash
* use hash functionality, as it preserves insertion order already
* adapt tests a bit, unskip one
Modified: trunk/src/classes/key.pmc
==============================================================================
--- trunk/src/classes/key.pmc (original)
+++ trunk/src/classes/key.pmc Wed Jan 11 08:18:15 2006
@@ -305,7 +305,8 @@ PMC_int_val(-1) means end of iteration.
* with an Integer KEY
*/
_hash = CONST_STRING(interpreter, "Hash");
- if (VTABLE_isa(INTERP, agg, _hash))
+ if (VTABLE_isa(INTERP, agg, _hash) &&
+ agg->vtable->base_type != enum_class_OrderedHash)
PObj_get_FLAGS(ret) |= KEY_hash_iterator_FLAGS;
init:
PMC_int_val(ret) = 0;
Modified: trunk/src/classes/orderedhash.pmc
==============================================================================
--- trunk/src/classes/orderedhash.pmc (original)
+++ trunk/src/classes/orderedhash.pmc Wed Jan 11 08:18:15 2006
@@ -8,26 +8,30 @@ src/classes/orderedhash.pmc - Ordered Ha
=head1 DESCRIPTION
-C<OrderedHash> extends C<PerlArray> to provide the functionality of
-C<PerlArray> (list in C<data>) and a C<Hash> (hash in
-C<struct_val>). The list holds the PMC values, the hash keys point to
-the index of the value in the list.
+C<OrderedHash> extends C<Hash> to provide the interfaces of
+C<array> and C<hash>. To achieve the functionality of an ordered
+hash there are a few restrictions though: C<delete_keyed> never removes
+items, they are just nulled.
+
+Please note that if values are set via integer idx, these indices
+have to be in strict order. Using C<push_xx> simplifies this task.
+This creates a key "\1idx" for C<idx> and is therefore not fully transparent.
There are 2 iterator interfaces:
=over 4
-=item * retrieve value (in creation order)
+=item * retrieve values (in creation order)
-=item * retrieve key (no special order)
+Please note that after a C<delete_keyed> operation, iterating over
+values doesn't work any more, you'll get an error 'No such key'.
+
+=item * retrieve keys (in creation order)
=back
See F<t/pmc/orderedhash.t>.
-If values are set by numeric index only, there is no hash key. Iterating
-over the hash doesn't show these values.
-
=head2 Methods
=over 4
@@ -39,326 +43,364 @@ over the hash doesn't show these values.
#include "parrot/parrot.h"
#include "pmc_hash.h"
-/*
-
-=item C<static PMC *undef(Interp *interpreter)>
-
-Returns the C<PerlUndef> PMC.
-
-=cut
-
-*/
-
-static PMC* undef(Interp* interpreter)
-{
- return pmc_new(interpreter, enum_class_PerlUndef);
-}
-pmclass OrderedHash extends PerlArray need_ext does array does hash {
+pmclass OrderedHash extends Hash need_ext does array does hash {
/*
-=item C<void init()>
-
-Initializes the ordered hash.
+=item C<PMC *get_pmc_keyed(PMC *key)>
-=item C<void destroy()>
+=item C<PMC *get_pmc_keyed_int(INTVAL key)>
-Destroys the ordered hash.
+=item C<PMC *get_pmc_keyed_str(STRING *key)>
=cut
*/
- void init () {
- /* clear the Hash ptr - its not yet existing */
- PMC_struct_val(SELF) = NULL;
- SUPER();
- Hash.SUPER();
+ PMC* get_pmc_keyed_int (INTVAL idx) {
+ Hash *h = PMC_struct_val(SELF);
+ INTVAL n;
+ HashBucket *b;
+
+ n = h->entries;
+ if (idx < 0)
+ idx += n;
+ if (idx < 0 || idx >= n) {
+ real_exception(INTERP, NULL, KEY_NOT_FOUND,
+ "OrderedHash: index out of bounds!");
+ }
+ b = h->bs + idx;
+ if (b->key)
+ return (PMC*) b->value;
+ real_exception(INTERP, NULL, KEY_NOT_FOUND,
+ "OrderedHash: No such key");
+ return PMCNULL;
}
- void destroy () {
- Hash.SUPER();
+ PMC* get_pmc_keyed (PMC* key) {
+ switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
+ case KEY_hash_iterator_FLAGS:
+ return SUPER(key);
+ case KEY_integer_FLAG:
+ return SELF.get_pmc_keyed_int(key_integer(INTERP, key));
+ default:
+ return SUPER(key);
+ }
}
/*
-=item C<void mark()>
-
-Marks the ordered hash as live.
-
-=cut
-
-*/
-
- void mark () {
- SUPER();
- Hash.SUPER();
- }
-
-/*
+=item C<STRING *get_string_keyed(PMC *key)>
-=item C<PMC *clone()>
+=item C<STRING *get_string_keyed_int(INTVAL key)>
-Creates and returns a clone of the ordered hash.
+=item C<STRING *get_string_keyed_str(STRING *key)>
=cut
*/
- PMC* clone () {
- PMC* dest = SUPER();
- hash_clone(INTERP, (Hash *)PMC_struct_val(SELF),
- (Hash**)&PMC_struct_val(dest));
- return dest;
- }
-
-/*
-
-=item C<PMC *get_pmc_keyed(PMC *key)>
-
-=cut
-
-*/
+ STRING* get_string_keyed_int (INTVAL idx) {
+ Hash *h = PMC_struct_val(SELF);
+ INTVAL n;
+ HashBucket *b;
- PMC* get_pmc_keyed (PMC* key) {
- if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
- return SUPER(key);
- }
- else {
- INTVAL n = Hash.SELF.get_integer_keyed(key);
- return DYNSELF.get_pmc_keyed_int(n);
+ n = h->entries;
+ if (idx < 0)
+ idx += n;
+ if (idx < 0 || idx >= n) {
+ real_exception(INTERP, NULL, KEY_NOT_FOUND,
+ "OrderedHash: index out of bounds!");
}
+ b = h->bs + idx;
+ if (b->key)
+ return VTABLE_get_string(INTERP, (PMC*) b->value);
+ real_exception(INTERP, NULL, KEY_NOT_FOUND,
+ "OrderedHash: No such key");
+ return NULL;
}
+ STRING* get_string_keyed (PMC* key) {
+ switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
+ case KEY_hash_iterator_FLAGS:
+ return SUPER(key);
+ case KEY_integer_FLAG:
+ return SELF.get_string_keyed_int(key_integer(INTERP, key));
+ default:
+ return SUPER(key);
+ }
+ }
/*
-=item C<PMC *get_pmc_keyed_str(STRING *key)>
-
-Returns the PMC value associated with C<*key>.
-
-=cut
-
-*/
+=item C<INTVAL get_integer_keyed(PMC *key)>
- PMC* get_pmc_keyed_str (STRING* key) {
- INTVAL n = Hash.SELF.get_integer_keyed_str(key);
- return DYNSELF.get_pmc_keyed_int(n);
- }
+=item C<INTVAL get_integer_keyed_str(STRING *key)>
-/*
+=item C<INTVAL get_integer_keyed_int(INTVAL key)>
-=item C<INTVAL get_integer_keyed_str(STRING *key)>
+Returns the integer value associated with C<key>.
=cut
*/
- INTVAL get_integer_keyed_str (STRING* key) {
- INTVAL n = Hash.SELF.get_integer_keyed_str(key);
- return DYNSELF.get_integer_keyed_int(n);
- }
-
-/*
-
-=item C<INTVAL get_integer_keyed(PMC *key)>
-
-Returns the integer value associated with C<*key>.
-=cut
+ INTVAL get_integer_keyed_int (INTVAL idx) {
+ Hash *h = PMC_struct_val(SELF);
+ INTVAL n;
+ HashBucket *b;
-*/
+ n = h->entries;
+ if (idx < 0)
+ idx += n;
+ if (idx < 0 || idx >= n) {
+ real_exception(INTERP, NULL, OUT_OF_BOUNDS,
+ "OrderedHash: index out of bounds!");
+ }
+ b = h->bs + idx;
+ if (b->key)
+ return VTABLE_get_integer(INTERP, (PMC*) b->value);
+ real_exception(INTERP, NULL, KEY_NOT_FOUND,
+ "OrderedHash: No such key");
+ return 0;
+ }
INTVAL get_integer_keyed (PMC* key) {
- INTVAL n = Hash.SELF.get_integer_keyed(key);
- return DYNSELF.get_integer_keyed_int(n);
+ switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
+ case KEY_hash_iterator_FLAGS:
+ return SUPER(key);
+ case KEY_integer_FLAG:
+ return SELF.get_integer_keyed_int(key_integer(INTERP, key));
+ default:
+ return SUPER(key);
+ }
}
/*
=item C<FLOATVAL get_number_keyed(PMC *key)>
-Returns the floating-point value for the element at C<*key>.
+=item C<FLOATVAL get_number_keyed_int(INTVAL key)>
+
+=item C<FLOATVAL get_number_keyed_str(STRING* key)>
+
+Returns the floating-point value for the element at C<key>.
=cut
*/
+ FLOATVAL get_number_keyed_int (INTVAL idx) {
+ Hash *h = PMC_struct_val(SELF);
+ INTVAL n;
+ HashBucket *b;
+
+ n = h->entries;
+ if (idx < 0)
+ idx += n;
+ if (idx < 0 || idx >= n) {
+ real_exception(INTERP, NULL, OUT_OF_BOUNDS,
+ "OrderedHash: index out of bounds!");
+ }
+ b = h->bs + idx;
+ if (b->key)
+ return VTABLE_get_number(INTERP, (PMC*) b->value);
+ real_exception(INTERP, NULL, KEY_NOT_FOUND,
+ "OrderedHash: No such key");
+ return 0.0;
+ }
+
FLOATVAL get_number_keyed (PMC* key) {
- INTVAL n = Hash.SELF.get_integer_keyed(key);
- return DYNSELF.get_number_keyed_int(n);
+ switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
+ case KEY_hash_iterator_FLAGS:
+ return SUPER(key);
+ case KEY_integer_FLAG:
+ return SELF.get_number_keyed_int(key_integer(INTERP, key));
+ default:
+ return SUPER(key);
+ }
}
/*
-=item C<void set_pmc_keyed(PMC *key, PMC *value)>
-
-Associates C<*value> with C<*key>.
-
-=cut
-
-*/
+=item C<void set_pmc_keyed_int(INTVAL idx, PMC *val)>
- void set_pmc_keyed (PMC* key, PMC* value) {
- INTVAL n = DYNSELF.elements();
- DYNSELF.set_pmc_keyed_int(n, value);
- Hash.SELF.set_integer_keyed(key, n);
- }
+=item C<void set_integer_keyed_int(INTVAL key, INTVAL value)>
-/*
+=item C<void set_number_keyed_int(INTVAL key, FLOATVAL value)>
-=item C<void set_integer_keyed(PMC *key, INTVAL value)>
+=item C<void set_string_keyed_int(INTVAL key, STRING *value)>
-Associates C<value> with C<*key>.
+Sets the PMC value of the element at index C<key> to C<val>.
+The created key = "\1idx".
=cut
*/
- void set_integer_keyed (PMC* key, INTVAL value) {
- INTVAL n = DYNSELF.elements();
- DYNSELF.set_integer_keyed_int(n, value);
- Hash.SELF.set_integer_keyed(key, n);
- }
-
-/*
-
-=item C<void set_string_keyed(PMC *key, STRING *value)>
+ void set_pmc_keyed_int (INTVAL idx, PMC* val) {
+ STRING *key, *fmt;
+ Hash *h = PMC_struct_val(SELF);
+ INTVAL n;
+ HashBucket *b;
-Associates C<value> with C<*key>.
-
-=cut
+ n = h->entries;
+ if (idx < -n)
+ idx = -idx - n - 1;
+ else if (idx < 0)
+ idx += n;
+ fmt = CONST_STRING(INTERP, "\1%d");
+ if (idx >= n) {
+ /* TODO warn or fill if there are holes */
+ key = Parrot_sprintf_s(INTERP, fmt, idx);
+ DYNSELF.set_pmc_keyed_str(key, val);
+ }
+ else {
+ b = h->bs + idx;
+ if (!b->key) {
+ b->key = Parrot_sprintf_s(INTERP, fmt, idx);
+ }
+ b->value = val;
+ }
+ }
-*/
+ void set_integer_keyed_int (INTVAL idx, INTVAL value) {
+ PMC *v = pmc_new(INTERP, Parrot_get_ctx_HLL_type(INTERP,
+ enum_class_Integer));
+ VTABLE_set_integer_native(INTERP, v, value);
+ SELF.set_pmc_keyed_int(idx, v);
+ }
- void set_string_keyed (PMC* key, STRING* value) {
- INTVAL n = DYNSELF.elements();
- DYNSELF.set_string_keyed_int(n, value);
- Hash.SELF.set_integer_keyed(key, n);
+ void set_number_keyed_int (INTVAL idx, FLOATVAL value) {
+ PMC *v = pmc_new(INTERP, Parrot_get_ctx_HLL_type(INTERP,
+ enum_class_Float));
+ VTABLE_set_number_native(INTERP, v, value);
+ SELF.set_pmc_keyed_int(idx, v);
}
+ void set_string_keyed_int (INTVAL idx, STRING* value) {
+ PMC *v = pmc_new(INTERP, Parrot_get_ctx_HLL_type(INTERP,
+ enum_class_String));
+ VTABLE_set_string_native(INTERP, v, value);
+ SELF.set_pmc_keyed_int(idx, v);
+ }
/*
-=item C<void set_pmc_keyed_str(STRING *key, PMC *value)>
-
-Associates C<*value> with C<*key>.
-
-=cut
-
-*/
-
- void set_pmc_keyed_str (STRING* key, PMC* value) {
- INTVAL n = DYNSELF.elements();
- DYNSELF.set_pmc_keyed_int(n, value);
- Hash.SELF.set_integer_keyed_str(key, n);
- }
+=item C<void push_float(FLOATVAL value)>
-/*
+=item C<void push_integer(INTVAL value)>
-=item C<void set_integer_keyed_str(STRING *key, INTVAL value)>
+=item C<void push_pmc(PMC* value)>
-Associates C<value> with C<*key>.
+=item C<void push_string(STRING* value)>
=cut
*/
- void set_integer_keyed_str (STRING* key, INTVAL value) {
+ void push_pmc(PMC* value) {
INTVAL n = DYNSELF.elements();
- DYNSELF.set_integer_keyed_int(n, value);
- Hash.SELF.set_integer_keyed_str(key, n);
+ SELF.set_pmc_keyed_int(n, value);
}
-/*
-
-=item C<void set_number_keyed (PMC *key, FLOATVAL value)>
-
-=cut
-
-*/
-
- void set_number_keyed (PMC* key, FLOATVAL value) {
+ void push_float(FLOATVAL value) {
INTVAL n = DYNSELF.elements();
- DYNSELF.set_number_keyed_int(n, value);
- Hash.SELF.set_integer_keyed(key, n);
+ SELF.set_number_keyed_int(n, value);
}
-/*
-
-=item C<void set_string_keyed_str(STRING *key, STRING *value)>
-
-=cut
-
-*/
+ void push_integer(INTVAL value) {
+ INTVAL n = DYNSELF.elements();
+ SELF.set_integer_keyed_int(n, value);
+ }
- void set_string_keyed_str (STRING* key, STRING* value) {
+ void push_string(STRING* value) {
INTVAL n = DYNSELF.elements();
- DYNSELF.set_string_keyed_int(n, value);
- Hash.SELF.set_integer_keyed_str(key, n);
+ SELF.set_string_keyed_int(n, value);
}
/*
=item C<INTVAL exists_keyed(PMC *key)>
+=item C<INTVAL exists_keyed_str(STRING *key)>
+
+=item C<INTVAL exists_keyed_int(INTVAL key)>
+
=cut
*/
+ INTVAL exists_keyed_int(INTVAL idx) {
+ Hash *h = PMC_struct_val(SELF);
+ INTVAL n;
+ HashBucket *b;
+
+ n = h->entries;
+ if (idx < 0)
+ idx += n;
+ if (idx < 0 || idx >= n) {
+ return 0;
+ }
+ b = h->bs + idx;
+ if (b->key)
+ return 1;
+ return 0;
+ }
INTVAL exists_keyed(PMC* key) {
if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
- return SUPER(key);
+ return SELF.exists_keyed_int(key_integer(INTERP, key));
}
else {
- return Hash.SUPER(key);
+ /* TODO handle composite keys */
+ return SELF.exists_keyed_str(key_string(INTERP, key));
}
}
-/*
-
-=item C<INTVAL exists_keyed_str(STRING *key)>
-
-Returns whether the key C<*key> exists in the hash.
-
-=cut
-
-*/
-
INTVAL exists_keyed_str(STRING* key) {
- return Hash.SUPER(key);
+ Hash *h = PMC_struct_val(SELF);
+ HashBucket *b;
+ b = hash_get_bucket(INTERP, h, key);
+ if (b && b->key)
+ return 1;
+ return 0;
}
/*
=item C<INTVAL defined_keyed(PMC *key)>
+=item C<INTVAL defined_keyed_str(STRING *key)>
+
+=item C<INTVAL defined_keyed_int(INTVAL key)>
+
=cut
*/
- INTVAL defined_keyed(PMC* key) {
+
+ INTVAL defined_keyed(PMC *key) {
if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
- return SUPER(key);
- }
- else {
- INTVAL n = Hash.SELF.get_integer_keyed(key);
- return DYNSELF.defined_keyed_int(n);
+ return SELF.defined_keyed_int(key_integer(INTERP, key));
}
+ return SUPER(key);
}
-/*
-
-=item C<INTVAL defined_keyed_str(STRING *key)>
-
-Returns whether the value for key C<*key> is defined in the hash.
-
-=cut
-
-*/
-
- INTVAL defined_keyed_str(STRING* key) {
- INTVAL n = Hash.SELF.get_integer_keyed_str(key);
- return DYNSELF.defined_keyed_int(n);
+ INTVAL defined_keyed_int(INTVAL idx) {
+ Hash *h = PMC_struct_val(SELF);
+ INTVAL n;
+ HashBucket *b;
+
+ n = h->entries;
+ if (idx < 0)
+ idx += n;
+ if (idx < 0 || idx >= n) {
+ return 0;
+ }
+ b = h->bs + idx;
+ if (b->key)
+ return VTABLE_defined(INTERP, (PMC*)b->value);
+ return 0;
}
/*
@@ -367,6 +409,8 @@ Returns whether the value for key C<*key
=item C<void delete_keyed_str(STRING *key)>
+=item C<void delete_keyed_int(INTVAL key)>
+
Deletes the key C<*key> from the hash.
=cut
@@ -375,45 +419,72 @@ Deletes the key C<*key> from the hash.
void delete_keyed(PMC* key) {
if (PObj_get_FLAGS(key) & KEY_integer_FLAG) {
- SUPER(key);
+ SELF.delete_keyed_int(key_integer(INTERP, key));
}
else {
- PMC *new_undef = undef(INTERP);
- /* fetch index into the PerlArray
- and delete the key in the Hash afterward */
- INTVAL n = Hash.SELF.get_integer_keyed(key);
- Hash.SUPER(key);
- /* remove the value from the PerlArray,
- but don't splice the PerlArray. We don't want to mess up
- the mapping from the Hash to the PerlArray */
- DYNSELF.set_pmc_keyed_int(n,new_undef);
+ /* TODO handle composite keys */
+ SELF.delete_keyed_str(key_string(INTERP, key));
}
}
+ void delete_keyed_int(INTVAL idx) {
+ Hash *h = PMC_struct_val(SELF);
+ INTVAL n;
+ HashBucket *b;
+
+ n = h->entries;
+ if (idx < 0)
+ idx += n;
+ if (idx < 0 || idx >= n) {
+ return;
+ }
+ b = h->bs + idx;
+ if (b)
+ b->key = NULL;
+ }
+
void delete_keyed_str(STRING* key) {
- PMC *new_undef = undef(INTERP);
- /* fetch index into the PerlArray
- and delete the key in the Hash afterward */
- INTVAL n = Hash.SELF.get_integer_keyed_str(key);
- Hash.SUPER(key);
- /* remove the value from the PerlArray,
- but don't splice the PerlArray. We don't want to mess up
- the mapping from the Hash to the PerlArray */
- DYNSELF.set_pmc_keyed_int(n,new_undef);
+ Hash *h = PMC_struct_val(SELF);
+ HashBucket *b;
+ b = hash_get_bucket(INTERP, h, key);
+ if (b)
+ b->key = NULL;
}
/*
-=item C<STRING *get_string_keyed(PMC *key)>
+=item C<PMC* clone()>
-Returns the Parrot string associated with C<*key>.
+Create a clone of the OrderedHash. Non-existent keys are compacted.
+Accessing the clone via integers has different indices, if items
+were deleted
=cut
*/
- STRING* get_string_keyed (PMC* key) {
- return Hash.SUPER(key);
+ PMC* clone() {
+ PMC *dest;
+ Hash *hash = PMC_struct_val(SELF), *h_dest;
+
+ UINTVAL i;
+ HashBucket *b;
+ void *key;
+ void *valtmp;
+
+ dest = pmc_new_noinit(INTERP, SELF->vtable->base_type);
+ new_pmc_hash_x(INTERP, dest, hash->entry_type,
+ hash->key_type, hash->compare, hash->hash_val);
+ h_dest = PMC_struct_val(dest);
+ for (i = 0; i <= hash->mask; i++) {
+ b = hash->bs + i;
+ key = b->key;
+ if (!key)
+ continue;
+ valtmp = VTABLE_clone(INTERP, (PMC*)b->value);
+ hash_put(INTERP, h_dest, key, valtmp);
+ }
+ return dest;
}
}
Modified: trunk/src/classes/unmanagedstruct.pmc
==============================================================================
--- trunk/src/classes/unmanagedstruct.pmc (original)
+++ trunk/src/classes/unmanagedstruct.pmc Wed Jan 11 08:18:15 2006
@@ -89,7 +89,7 @@ key_2_idx(Interp* interpreter, PMC *pmc,
key_string(interpreter, key));
if (!b)
internal_exception(1, "key doesn't exist");
- ix = VTABLE_get_integer(interpreter, (PMC*) b->value);
+ ix = b - hash->bs;
}
else
internal_exception(1, "unhandled type aggregate");
Modified: trunk/t/pmc/orderedhash.t
==============================================================================
--- trunk/t/pmc/orderedhash.t (original)
+++ trunk/t/pmc/orderedhash.t Wed Jan 11 08:18:15 2006
@@ -119,11 +119,11 @@ OUT
output_is(<<'CODE', <<OUT, "idx only");
new P0, .OrderedHash
new P1, .String
- set P1, "ok 2\n"
- set P0[1], P1
- new P1, .String
set P1, "ok 1\n"
set P0[0], P1
+ new P1, .String
+ set P1, "ok 2\n"
+ set P0[1], P1
set P2, P0[0]
print P2
@@ -268,20 +268,22 @@ output_is(<<'CODE', <<OUT, "delete");
delete P0["a"]
new P2, .Iterator, P0
- set P2, .ITERATE_FROM_START
+ set P2, .ITERATE_FROM_START_KEYS
iter_loop:
unless P2, end_iter
- shift P3, P2
+ shift S3, P2
+ set P3, P2[S3]
print P3
branch iter_loop
end_iter:
delete P0[0]
- set P2, .ITERATE_FROM_START
+ set P2, .ITERATE_FROM_START_KEYS
iter_loop2:
unless P2, end_iter2
- shift P3, P2
+ shift S3, P2
+ set P3, P2[S3]
print P3
branch iter_loop2
end_iter2:
@@ -293,8 +295,6 @@ ok 3
ok 3
OUT
-SKIP: {
- skip("Mixing keyed & indexed access is broken - see ticket 33641", 1);
output_is(<<'CODE', <<'OUTPUT', "delete with int keys");
new P0, .OrderedHash
set P0["abc"], "Foo"
@@ -315,11 +315,11 @@ output_is(<<'CODE', <<'OUTPUT', "delete
print I0
exists I0, P0[2]
print I0
+ print "\n"
end
CODE
-101110
+101101
OUTPUT
-}
output_like(<<'CODE', '/[axj]/', "iterate over keys");
.include "iterator.pasm"
@@ -493,7 +493,7 @@ pir_output_is(<< 'CODE', << 'OUTPUT', "O
end
.end
CODE
--16.160000
+-16.16
OUTPUT
pir_output_is(<< 'CODE', << 'OUTPUT', "OrderedHash get_integer");