Author: particle
Date: Tue Jan 10 13:17:49 2006
New Revision: 11062
Modified:
trunk/src/classes/resizablebooleanarray.pmc
trunk/t/pmc/resizablebooleanarray.t
Log:
PMC: ResizableBooleanArray
~ modified allocation to 1 bit per item
~ modified push/pop
~ added shift/unshift, freeze/thaw
Adapted from a patch courtesy of Dino Morelli.
Modified: trunk/src/classes/resizablebooleanarray.pmc
==============================================================================
--- trunk/src/classes/resizablebooleanarray.pmc (original)
+++ trunk/src/classes/resizablebooleanarray.pmc Tue Jan 10 13:17:49 2006
@@ -8,10 +8,10 @@ src/classes/resizablebooleanarray.pmc -
=head1 DESCRIPTION
-The C<ResizableBooleanArray PMC> implements an array of resizable
-size, which stores booleans.
+The C<ResizableBooleanArray PMC> implements an array of resizable size,
+which stores booleans.
It uses the C<Boolean PMC> for all conversions.
-C<ResizableBooleanArray PMC> extends C<FixedBooleanArray PMC>
+The C<ResizableBooleanArray PMC> extends the C<FixedBooleanArray PMC>.
=head2 Functions
@@ -25,6 +25,7 @@ C<ResizableBooleanArray PMC> extends C<F
#define BITS_PER_CHAR 8
+#define MIN_ALLOC 8 * BITS_PER_CHAR
pmclass ResizableBooleanArray extends FixedBooleanArray need_ext does array {
@@ -49,9 +50,17 @@ Returns the integer value of the element
*/
INTVAL get_integer_keyed_int (INTVAL key) {
+ /* Try to make negative index into a real index */
+ if (key < 0) key = SELF.elements() + key;
+
+ /* If it's still negative, we have a problem */
if (key < 0)
- internal_exception(OUT_OF_BOUNDS,
+ real_exception(interpreter, NULL, E_IndexError,
"ResizableBooleanArray: index out of bounds!");
+
+ /* Adjust key for the current head position */
+ key += PMC_int_val2(SELF);
+
if(key >= PMC_int_val(SELF))
DYNSELF.set_integer_native(key+1);
@@ -69,9 +78,17 @@ Sets the integer value of the element at
*/
void set_integer_keyed_int (INTVAL key, INTVAL value) {
+ /* Try to make negative index into a real index */
+ if (key < 0) key = SELF.elements() + key;
+
+ /* If it's still negative, we have a problem */
if (key < 0)
- internal_exception(OUT_OF_BOUNDS,
+ real_exception(interpreter, NULL, E_IndexError,
"ResizableBooleanArray: index out of bounds!");
+
+ /* Adjust key for the current head position */
+ key += PMC_int_val2(SELF);
+
if(key >= PMC_int_val(SELF))
DYNSELF.set_integer_native(key+1);
@@ -89,38 +106,232 @@ Resizes the array to C<size> elements.
*/
void set_integer_native (INTVAL size) {
+ Parrot_UInt1 *sd;
+ INTVAL newASize;
+ INTVAL currSize = PMC_int_val(SELF) - PMC_int_val2(SELF);
+
+ /* We are already at the requested size. Yay */
+ if(size == currSize) return;
+
if (size < 0)
- internal_exception(OUT_OF_BOUNDS,
- "ResizableBooleanArray: Can't resize!");
+ real_exception(interpreter, NULL, E_IndexError,
+ "ResizableBooleanArray: Can't resize!");
+
+ newASize = (size / MIN_ALLOC + 1) * MIN_ALLOC;
+ /* Nothing allocated yet */
if ( ! PMC_data(SELF) ) {
- SUPER(size);
- }
- else if (size <= PMC_int_val2(SELF)) {
- PMC_int_val(SELF) = size;
- /* we could shrink here if necessary */
- return;
+ PMC_data(SELF) = mem_sys_allocate_zeroed(newASize);
}
else {
- INTVAL cur, needed;
- cur = PMC_int_val2(SELF);
- if (cur < 8192) {
- cur = size < 2 * cur ? 2 * cur
- : (size/BITS_PER_CHAR+1)*BITS_PER_CHAR;
- }
- else {
- needed = size - cur;
- cur += needed + 4096;
- cur &= ~0xfff;
- }
- PMC_data(SELF) = mem_sys_realloc(PMC_data(SELF),
- cur/BITS_PER_CHAR);
- PMC_int_val(SELF) = size;
- PMC_int_val2(SELF) = cur;
+ sd = PMC_data(SELF);
+ PMC_data(SELF) = mem_sys_realloc(sd, newASize);
+ }
+
+ PMC_int_val2(SELF) = 0;
+ PMC_int_val(SELF) = size;
+ }
+
+/*
+
+=item C<void push_integer(INTVAL size)>
+
+Extends the array by adding an element of value C<value> to the end.
+
+=cut
+
+*/
+
+ void push_integer (INTVAL value) {
+ INTVAL size = PMC_int_val(SELF) - PMC_int_val2(SELF);
+ DYNSELF.set_integer_native(size + 1);
+ DYNSELF.set_integer_keyed_int(size, value);
+ }
+
+/*
+
+=item C<void pop_integer(INTVAL size)>
+
+Removes and returns the last element.
+
+=cut
+
+*/
+
+ INTVAL pop_integer () {
+ INTVAL size, value;
+
+ if (DYNSELF.elements() < 1)
+ real_exception(interpreter, NULL, E_IndexError,
+ "ResizableBooleanArray: Can't pop from an empty array!");
+
+ size = PMC_int_val(SELF) - PMC_int_val2(SELF);
+ value = DYNSELF.get_integer_keyed_int(size - 1);
+ DYNSELF.set_integer_native(size - 1);
+
+ return value;
+ }
+
+/*
+
+=item C<void unshift_integer(INTVAL size)>
+
+Extends the array by adding an element of value C<value> to the
+beginning.
+
+=cut
+
+*/
+
+ void unshift_integer (INTVAL value) {
+ Parrot_UInt1 *sdOld, *sdNew;
+
+ /* If int_val2 is smaller than 0, size this thing up */
+ if(PMC_int_val2(SELF) <= 0) {
+ sdOld = PMC_data(SELF);
+ sdNew = mem_sys_allocate_zeroed(
+ ((PMC_int_val2(SELF) / MIN_ALLOC) * MIN_ALLOC)
+ + PMC_int_val(SELF)
+ + ((PMC_int_val(SELF) / MIN_ALLOC + 1) * MIN_ALLOC)
+ );
+ mem_sys_memmove(sdNew, sdOld + PMC_int_val2(SELF),
+ PMC_int_val(SELF));
+ mem_sys_free(sdOld);
+ PMC_data(SELF) = sdNew;
+ PMC_int_val2(SELF) += MIN_ALLOC;
+ PMC_int_val(SELF) += MIN_ALLOC;
+ }
+
+ /* Move the head position */
+ PMC_int_val2(SELF)--;
+
+ /* Assign the new value as the first item */
+ DYNSELF.set_integer_keyed_int(0, value);
+ }
+
+/*
+
+=item C<void shift_integer(INTVAL size)>
+
+Removes and returns the first element.
+
+=cut
+
+*/
+
+ INTVAL shift_integer () {
+ INTVAL value;
+ Parrot_UInt1 *sdOld, *sdNew;
+
+ if (DYNSELF.elements() < 1)
+ real_exception(interpreter, NULL, E_IndexError,
+ "ResizableBooleanArray: Can't shift from an empty array!");
+
+ /* Get head value */
+ value = DYNSELF.get_integer_keyed_int(0);
+
+ /* Move the head position */
+ PMC_int_val2(SELF)++;
+
+ /* If int_val2 is bigger than our allocation unit size, size
+ this thing down */
+ if(PMC_int_val2(SELF) >= MIN_ALLOC) {
+ sdOld = PMC_data(SELF);
+ sdNew = mem_sys_allocate_zeroed(
+ ((PMC_int_val2(SELF) / MIN_ALLOC) * MIN_ALLOC)
+ + PMC_int_val(SELF)
+ + ((PMC_int_val(SELF) / MIN_ALLOC + 1) * MIN_ALLOC)
+ );
+ mem_sys_memmove(sdNew, sdOld + PMC_int_val2(SELF),
+ PMC_int_val(SELF));
+ mem_sys_free(sdOld);
+ PMC_data(SELF) = sdNew;
}
+
+ return value;
+ }
+
+/*
+
+=item C<INTVAL elements()>
+
+=cut
+
+*/
+
+ INTVAL elements () {
+ return PMC_int_val(SELF) - PMC_int_val2(SELF);
+ }
+
+/*
+
+=item C<INTVAL get_integer()>
+
+Returns the number of elements in the array.
+
+=cut
+
+*/
+
+ INTVAL get_integer () {
+ return SELF.elements();
}
-}
+/*
+
+=back
+
+=head2 Freeze/thaw Interface
+
+=over 4
+
+=item C<void freeze(visit_info *info)>
+
+Used to archive the string.
+
+=cut
+
+*/
+ void freeze(visit_info *info) {
+ /* XXX Dino - I'm concerned about freezing the entire
+ allocated block of memory, it's dependent on the
+ BITS_PER_CHAR value.
+ Maybe we need to store that during the freeze as well
+ and use it during thaw?
+ */
+
+ IMAGE_IO *io = info->image_io;
+ STRING *s;
+ INTVAL size = (PMC_int_val(SELF) / MIN_ALLOC + 1) * MIN_ALLOC;
+
+ io->vtable->push_integer(INTERP, io, PMC_int_val2(SELF));
+ io->vtable->push_integer(INTERP, io, PMC_int_val(SELF));
+ s = string_from_cstring(INTERP, PMC_data(SELF), size);
+ io->vtable->push_string(INTERP, io, s);
+ }
+
+/*
+
+=item C<void thaw(visit_info *info)>
+
+Used to unarchive the string.
+
+=cut
+
+*/
+ void thaw(visit_info *info) {
+ IMAGE_IO *io = info->image_io;
+ INTVAL headPos = io->vtable->shift_integer(INTERP, io);
+ INTVAL tailPos = io->vtable->shift_integer(INTERP, io);
+ STRING *s = io->vtable->shift_string(INTERP, io);
+
+ PMC_data(SELF) = mem_sys_allocate_zeroed(s->bufused);
+ mem_sys_memcopy(PMC_data(SELF), s->strstart, s->bufused);
+ PMC_int_val2(SELF) = headPos;
+ PMC_int_val(SELF) = tailPos;
+ }
+
+} /* pmclass */
/*
@@ -133,8 +344,15 @@ F<docs/pdds/pdd17_basic_types.pod>.
=head1 HISTORY
Initial version - Matt Fowles 2004-06-11
+
Changed allocator to double size - Matt Fowles 2004-06-15
+Added push_integer - Bernhard Schmalhofer 2004-10-17
+
+Changed allocation code, added - Dino Morelli 2005-06-10
+ push_, pop_, shift_,
+ unshift_integer, freeze, thaw
+
=cut
*/
Modified: trunk/t/pmc/resizablebooleanarray.t
==============================================================================
--- trunk/t/pmc/resizablebooleanarray.t (original)
+++ trunk/t/pmc/resizablebooleanarray.t Tue Jan 10 13:17:49 2006
@@ -6,7 +6,7 @@ use strict;
use warnings;
use lib qw( . lib ../lib ../../lib );
use Test::More;
-use Parrot::Test tests => 9;
+use Parrot::Test tests => 21;
=head1 NAME
@@ -66,6 +66,7 @@ my $fp_equality_macro = <<'ENDOFMACRO';
.endm
ENDOFMACRO
+
pasm_output_is(<<'CODE', <<'OUTPUT', "Setting array size");
new P0, .ResizableBooleanArray
@@ -106,6 +107,7 @@ ok 4
ok 5
OUTPUT
+
pasm_output_is(<<'CODE', <<'OUTPUT', "Setting first element");
new P0, .ResizableBooleanArray
set P0, 1
@@ -135,6 +137,7 @@ ok 2
ok 3
OUTPUT
+
pasm_output_is(<<'CODE', <<'OUTPUT', "Setting second element");
new P0, .ResizableBooleanArray
set P0, 2
@@ -166,6 +169,7 @@ OUTPUT
# TODO: Rewrite these properly when we have exceptions
+
pasm_output_is(<<'CODE', <<'OUTPUT', "Setting out-of-bounds elements");
new P0, .ResizableBooleanArray
@@ -194,6 +198,7 @@ ok 2
ok 3
OUTPUT
+
pasm_output_is(<<'CODE', <<'OUTPUT', "Getting out-of-bounds elements");
new P0, .ResizableBooleanArray
set P0, 1
@@ -242,6 +247,7 @@ ok 2
ok 3
OUTPUT
+
pasm_output_is(<<"CODE", <<'OUTPUT', "Set via INTs, access via PMC Keys");
@{[ $fp_equality_macro ]}
new P0, .ResizableBooleanArray
@@ -288,8 +294,8 @@ ok 3
ok 4
OUTPUT
-pir_output_is(<< 'CODE', << 'OUTPUT', "check whether interface is done");
+pir_output_is(<< 'CODE', << 'OUTPUT', "check whether interface is done");
.sub _main
.local pmc pmc1
pmc1 = new ResizableBooleanArray
@@ -311,8 +317,8 @@ CODE
0
OUTPUT
-pir_output_is(<< 'CODE', << 'OUTPUT', "push integer");
+pir_output_is(<< 'CODE', << 'OUTPUT', "push integer");
.sub _main
.local pmc pmc1
pmc1 = new ResizableBooleanArray
@@ -332,3 +338,603 @@ CODE
10001
1
OUTPUT
+
+
+output_is(<<'CODE', <<'OUTPUT', "creation");
+ new P0, .ResizableBooleanArray
+ set I0, P0
+ print "Created ResizableBooleanArray with "
+ print I0
+ print " elements to start with.\n"
+ end
+CODE
+Created ResizableBooleanArray with 0 elements to start with.
+OUTPUT
+
+
+pir_output_is(<< 'CODE', << 'OUTPUT', "push and pop");
+.sub test :main
+ .local int i, i_elem
+ .local pmc pmc_arr
+ .local int elements
+
+ i = 1
+ pmc_arr = new ResizableBooleanArray
+
+ print_num_elements( pmc_arr )
+
+ push pmc_arr, i
+ print i
+ print_num_elements( pmc_arr )
+
+ push pmc_arr, 0
+ print 0
+ print_num_elements( pmc_arr )
+
+ print_num_elements( pmc_arr )
+
+ i_elem= pop pmc_arr
+ print i_elem
+ print_num_elements( pmc_arr )
+
+ i_elem= pop pmc_arr
+ print i_elem
+ print_num_elements( pmc_arr )
+
+ pmc_arr = 62
+ push pmc_arr, 0
+ push pmc_arr, 1
+ push pmc_arr, 0
+ push pmc_arr, 1
+ i_elem = pop pmc_arr
+ i_elem = pop pmc_arr
+ i_elem = pop pmc_arr
+ print i_elem
+ print_num_elements(pmc_arr)
+.end
+
+.sub print_num_elements
+ .param pmc pmc_arr
+ .local int elements
+ elements= pmc_arr
+ print '['
+ print elements
+ print "]\n"
+ .return()
+.end
+
+CODE
+[0]
+1[1]
+0[2]
+[2]
+0[1]
+1[0]
+1[63]
+OUTPUT
+
+
+pir_output_like(<< 'CODE', << 'OUTPUT', "pop bounds checking");
+.sub 'test' :main
+ P0 = new .ResizableBooleanArray
+ pop I0, P0
+.end
+CODE
+/ResizableBooleanArray: Can't pop from an empty array!.*/
+OUTPUT
+#'
+
+
+pir_output_is(<< 'CODE', << 'OUTPUT', "unshift and shift");
+.sub test :main
+ .local int i, i_elem
+ .local pmc pmc_arr
+ .local int elements
+
+ i= 1
+ pmc_arr= new ResizableBooleanArray
+
+ print_num_elements( pmc_arr )
+
+ unshift pmc_arr, i
+ print i
+ print_num_elements( pmc_arr )
+
+ unshift pmc_arr, 0
+ print 0
+ print_num_elements( pmc_arr )
+
+ print_num_elements( pmc_arr )
+
+ i_elem= shift pmc_arr
+ print i_elem
+ print_num_elements( pmc_arr )
+
+ i_elem= shift pmc_arr
+ print i_elem
+ print_num_elements( pmc_arr )
+
+ pmc_arr = 62
+ unshift pmc_arr, 0
+ unshift pmc_arr, 1
+ unshift pmc_arr, 0
+ unshift pmc_arr, 1
+ i_elem = shift pmc_arr
+ i_elem = shift pmc_arr
+ i_elem = shift pmc_arr
+ print i_elem
+ print_num_elements(pmc_arr)
+
+ # Set same size array is currently
+ pmc_arr = 63
+ print_num_elements(pmc_arr)
+.end
+
+.sub print_num_elements
+ .param pmc pmc_arr
+ .local int elements
+ elements= pmc_arr
+ print '['
+ print elements
+ print "]\n"
+ .return()
+.end
+
+CODE
+[0]
+1[1]
+0[2]
+[2]
+0[1]
+1[0]
+1[63]
+[63]
+OUTPUT
+
+
+pir_output_like(<< 'CODE', << 'OUTPUT', "shift bounds checking");
+.sub 'test' :main
+ P0 = new .ResizableBooleanArray
+ shift I0, P0
+.end
+CODE
+/ResizableBooleanArray: Can't shift from an empty array!.*/
+OUTPUT
+#'
+
+
+output_is(<<'CODE', <<'OUTPUT', "aerobics");
+ new P0, .ResizableBooleanArray
+ set I10, 10000
+
+ set I1, 0
+ set I0, 0
+buildup:
+ ge I0, I10, postBuildUp
+
+ mod I4, I1, 2
+ push P0, I4
+ add I1, 1 # Push P0, mod I1++, 2
+ mod I4, I1, 2
+ push P0, I4
+ add I1, 1 # Push P0, mod I1++, 2
+ mod I4, I1, 2
+ push P0, I4
+ add I1, 1 # Push P0, mod I1++, 2
+
+ pop I2, P0
+ mul I3, I0, 3
+ add I3, 2
+ mod I3, 2
+ ne I2, I3, errFirstPop # fail if pop != mod I0 * 3 + 2, 2
+
+ pop I2, P0
+ mul I3, I0, 3
+ add I3, 1
+ mod I3, 2
+ ne I2, I3, errSecondPop # fail if pop != mod I0 * 3 + 1, 2
+
+ set I2, P0
+ add I3, I0, 1
+ ne I2, I3, errBuildLen # fail if length != I0 + 1
+
+ add I0, 1
+ branch buildup
+postBuildUp:
+
+ set I0, 0
+checkBuildUpLeft:
+ ge I0, I10, postCheckBuildUpLeft
+ set I2, P0[I0]
+ mul I3, I0, 3
+ mod I3, 2
+ ne I2, I3, errLeftGet
+ add I0, 1
+ branch checkBuildUpLeft
+postCheckBuildUpLeft:
+
+ mul I0, I10, -1
+checkBuildUpRight:
+ ge I0, 0, postCheckBuildUpRight
+ set I2, P0[I0]
+ add I3, I0, I10
+ mul I3, 3
+ mod I3, 2
+ ne I2, I3, errRightGet
+ add I0, 1
+ branch checkBuildUpRight
+postCheckBuildUpRight:
+
+ set I0, I10
+tearDown:
+ le I0, 0, postTearDown
+ pop I2, P0
+ sub I3, I0, 1
+ mod I3, 2
+ ne I2, I3, errTearDown
+
+ sub I0, 1
+ branch tearDown
+postTearDown:
+
+ print "I need a shower.\n"
+ end
+errFirstPop:
+ print "FAILED: first pop\n"
+ bsr info
+ end
+errSecondPop:
+ print "FAILED: second pop\n"
+ bsr info
+ end
+errBuildLen:
+ print "FAILED: buildup length\n"
+ bsr info
+ end
+errLeftGet:
+ print "FAILED: left get\n"
+ bsr info
+ end
+errRightGet:
+ print "FAILED: right get\n"
+ bsr info
+ end
+errTearDown:
+ print "FAILED: tear down cap\n"
+ bsr info
+ end
+info:
+ print "Found: "
+ print I2
+ print "\nWanted: "
+ print I3
+ print "\n"
+ ret
+CODE
+I need a shower.
+OUTPUT
+
+
+my $SPEEDUP = $ENV{RUNNING_MAKE_TEST} ? "gc_debug 0\n" : "";
+output_is($SPEEDUP . <<'CODE', <<'OUTPUT', "direct access");
+ new P0, .ResizableBooleanArray
+ set S0, ""
+ set S1, "abcdefghijklmnopqrst"
+ set I10, 100000
+ set I0, 0
+lp:
+ mod I2, I0, 2
+ set P0[I0], I2
+ inc I0
+ mod I9, I0, 100
+ ne I9, 0, lp1
+ # force GC => 142 DOD + 142 collects / 10^5 accesses
+ new P1, .PerlArray
+ set P1[I0], I0
+ concat S0, S1, S1
+ set S2, S0
+ set S0, S1
+ set S2, ""
+lp1:
+ le I0, I10, lp
+
+ set I0, 0
+lp2:
+ mod I2, I0, 2
+ set I1, P0[I0]
+ ne I2, I1, err
+ inc I0
+ le I0, I10, lp2
+ print "ok\n"
+ end
+err:
+ print "err: wanted "
+ print I0
+ print " got "
+ print I1
+ print "\n"
+ end
+CODE
+ok
+OUTPUT
+
+
+output_is(<<'CODE', <<'OUTPUT', "direct access 2");
+ #new P0, .IntList
+ new P0, .ResizableBooleanArray
+ set I10, 1100000
+ set I0, 1
+lp1:
+ add I1, I0, 5
+ mod I2, I1, 2
+ set P0[I0], I2
+ add I3, I1, I0
+ mod I2, I3, 2
+ push P0, I2
+ shl I0, I0, 1
+ inc I0
+ le I0, I10, lp1
+
+ set I0, 1
+lp2:
+ add I1, I0, 5
+ mod I5, I1, 2
+ # check at I0
+ set I2, P0[I0]
+ ne I2, I5, err
+ add I4, I0, 1
+ # and pushed value at I0+1
+ set I4, P0[I4]
+ add I3, I1, I0
+ mod I5, I3, 2
+ ne I5, I4, err
+
+ shl I0, I0, 1
+ inc I0
+ le I0, I10, lp2
+ print "ok\n"
+ end
+err:
+ print "not ok "
+ print I0
+ print " "
+ print I1
+ print " "
+ print I2
+ print " "
+ print I3
+ print " "
+ print I4
+ print " "
+ print I5
+ print " "
+ print I6
+ print " "
+ print I7
+ print "\n"
+
+ end
+CODE
+ok
+OUTPUT
+
+
+output_is(<<'CODE', <<'OUTPUT', "sparse access");
+ new P0, .ResizableBooleanArray
+ set I10, 110000
+ set I0, 1
+lp1:
+ add I1, I0, 5
+ mod I9, I1, 2
+ set P0[I0], I9
+ add I3, I1, I0
+ mod I9, I3, 2
+ push P0, I9
+ shl I0, I0, 1
+ inc I0
+ le I0, I10, lp1
+
+ set I0, 1
+lp2:
+ add I1, I0, 5
+ mod I9, I1, 2
+ # check at I0
+ set I2, P0[I0]
+ ne I2, I9, err
+ add I4, I0, 1
+ # and pushed value at I0+1
+ set I4, P0[I4]
+ add I3, I1, I0
+ mod I9, I3, 2
+ ne I9, I4, err
+
+ shl I0, I0, 1
+ inc I0
+ le I0, I10, lp2
+ print "ok 1\n"
+
+ # now repeat and fill some holes
+
+ set I0, 777
+lp3:
+ add I1, I0, 5
+ mod I9, I1, 2
+ set P0[I0], I9
+ add I0, I0, 666
+ le I0, I10, lp3
+
+ set I0, 777
+lp4:
+ add I1, I0, 5
+ mod I9, I1, 2
+ # check at I0
+ set I2, P0[I0]
+ ne I2, I9, err
+
+ add I0, I0, 666
+ le I0, I10, lp4
+ print "ok 2\n"
+ end
+err:
+ print "not ok "
+ print I0
+ print " "
+ print I1
+ print " "
+ print I2
+ print " "
+ print I3
+ print " "
+ print I4
+ print "\n"
+
+ end
+CODE
+ok 1
+ok 2
+OUTPUT
+
+TODO: {
+ local $TODO = "this is broken";
+
+output_is(<<'CODE', <<'OUTPUT', "check for zeroedness");
+ new P0, .ResizableBooleanArray
+ set I0, 0
+lp1:
+ push P0, 0
+ inc I0
+ lt I0, 100, lp1
+
+ set I2, 10000
+ #set I0, 100
+ set P0, I2
+lp2:
+ set I1, P0[I0]
+ ne I1, 0, err
+ inc I0
+ lt I0, I2, lp2
+ print "ok\n"
+ end
+err:
+ print "Found non-zero value "
+ print I1
+ print " at "
+ print I0
+ print "\n"
+ end
+CODE
+ok
+OUTPUT
+
+
+output_is(<<'CODE', <<'OUTPUT', "pop into sparse");
+ new P0, .ResizableBooleanArray
+ set I10, 100
+ set I0, 0
+ # push some values at start
+lp1:
+ mod I5, I0, 2
+ push P0, I5
+ inc I0
+ lt I0, I10, lp1
+
+ # create sparse
+ set I0, 100000
+ set I1, 1000
+ mod I5, I1, 2
+ #set P0[I0], I1
+ set P0[I0], I5
+ inc I1
+lp2:
+ # push some values after hole
+ mod I5, I1, 2
+ push P0, I5
+ inc I1
+ le I1, 1100, lp2
+ dec I1
+
+ set I3, P0
+lp3:
+ set I4, P0
+ ne I3, I4, err1
+ pop I2, P0
+ dec I3
+ mod I5, I1, 2
+ ne I2, I5, err2
+ gt I3, I0, cont1
+ lt I3, I10, cont1
+ set I1, 0
+
+ gt I3, I10, lp3
+ set I1, I10
+
+cont1:
+ dec I1
+ eq I1, 0, ok
+ branch lp3
+ok:
+ print "ok\n"
+ end
+err1: set S0, "len"
+ branch err
+err2:
+ set S0, "val"
+err:
+ print "nok "
+ print S0
+ print " "
+ print I0
+ print " "
+ print I1
+ print " "
+ print I2
+ print " "
+ print I3
+ print " "
+ print I4
+ print " "
+ print I5
+ end
+CODE
+ok
+OUTPUT
+
+
+output_is(<<'CODE', <<'OUTPUT', "clone");
+ new P0, .ResizableBooleanArray
+ set P0[0], 1
+ set P0[5000], 1
+ clone P1, P0
+
+ set I0, P0[5000]
+ eq I0, 1, ok_1
+ print "nok 1 "
+ok_1:
+ pop I0, P0
+ eq I0, 1, ok_2
+ print "nok 2 "
+ok_2:
+ set I0, P0
+ eq I0, 5000, ok_3
+ print "nok 3 "
+ok_3:
+ set I0, P1
+ eq I0, 5000, ok_4
+ print "nok 4 "
+ok_4:
+ set I0, P1[5000]
+ eq I0, 1, ok_5
+ print "nok 5 "
+ok_5:
+ pop I0, P1
+ eq I0, 1, ok_6
+ print "nok 6 "
+ end
+ok_6:
+ print "ok\n"
+ end
+CODE
+ok
+OUTPUT
+
+} # TODO