# Copyright (c) 2023 Yuki Kimoto
# MIT License
class IntList {
use Fn;
use Array;
# Enumerations
private enum {
DEFAULT_CAPACITY = 4,
}
# Fields
has capacity : ro int;
has length : ro int;
has array : int[];
# Class methods
static method new : IntList ($array : int[] = undef, $capacity : int = -1) {
my $length : int;
if ($array) {
$length = @$array;
}
else {
$length = 0;
}
my $self = &new_len($length, $capacity);
if ($array) {
Array->memcpy_int($self->{array}, 0, $array, 0, $length);
}
return $self;
}
static method new_len : IntList ($length : int, $capacity : int = -1) {
my $self = new IntList;
unless ($length >= 0) {
die "The length \$length must be greater than or equal to 0.";
}
if ($capacity < 0) {
$capacity = &DEFAULT_CAPACITY;
}
if ($length > $capacity) {
$capacity = $length;
}
$self->{capacity} = $capacity;
$self->{length} = $length;
$self->{array} = new int[$capacity];
return $self;
}
# Instance methods
method get : int ($index : int) {
my $length = $self->length;
unless ($index >= 0) {
die "The index \$index must be greater than or equal to 0.";
}
unless ($index < $length) {
die "The index \$index must be less than the length of \$list.";
}
my $element = $self->{array}[$index];
return $element;
}
method insert : void ($index : int, $element : int) {
my $length = $self->{length};
unless ($index >= 0) {
die "The index \$index must be greater than or equal to 0.";
}
unless ($index <= $length) {
die "The index \$index must be less than or equal to the length of \$list.";
}
my $new_length = $length + 1;
$self->_maybe_extend($new_length);
my $elements = $self->{array};
if ($index != $length) {
my $dist_index = $index + 1;
my $move_length = $length - $index;
Array->memmove_int($elements, $dist_index, $elements, $index, $move_length);
}
$elements->[$index] = $element;
$self->{length} = $new_length;
}
method pop : int () {
my $length = $self->length;
unless ($length > 0) {
die "The length of the list \$list must be greater than 0.";
}
my $element = $self->{array}[$length - 1];
$self->{array}[$length - 1] = 0;
$self->{length}--;
return $element;
}
method push : void ($element : int) {
my $length = $self->{length};
my $new_length = $length + 1;
$self->_maybe_extend($new_length);
$self->{array}[$length] = $element;
$self->{length} = $new_length;
}
method remove : int ($index : int) {
my $length = $self->{length};
unless ($index >= 0) {
die "The index \$index must be greater than or equal to 0.";
}
unless ($index < $length) {
die "The index \$index must be less than the length of \$list.";
}
my $elements = $self->{array};
my $remove_value = $elements->[$index];
my $dist_index = $index;
my $src_index = $index + 1;
my $move_length = $length - $index - 1;
Array->memmove_int($elements, $dist_index, $elements, $src_index, $move_length);
$self->{length}--;
return $remove_value;
}
method replace : void ($offset : int, $remove_length : int, $replace : int[]) {
unless ($offset >= 0) {
die "The offset \$offset must be greater than or equal to 0.";
}
unless ($remove_length >= 0) {
die "The removal length \$remove_length must be greater than or equal to 0.";
}
unless ($offset + $remove_length <= $self->{length}) {
die "\$offset + \$removing length must be less than or equal to the length of \$list.";
}
my $replace_length = 0;
if ($replace) {
$replace_length = @$replace;
}
my $new_length = $self->{length} - $remove_length + $replace_length;
$self->_maybe_extend($new_length);
my $move_length = $self->{length} - $offset - $remove_length;
Array->memmove_int($self->{array}, $offset + $replace_length, $self->{array}, $offset + $remove_length, $move_length);
if ($replace) {
Array->memcpy_int($self->{array}, $offset, $replace, 0, $replace_length);
}
$self->{length} = $new_length;
}
method reserve : void ($new_capacity : int) {
unless ($new_capacity >= 0) {
die "The new capacity \$new_capacity must be greater than or equal to 0.";
}
my $capacity = $self->{capacity};
if ($new_capacity > $capacity) {
my $length = $self->{length};
my $new_array = new int[$new_capacity];
Array->memcpy_int($new_array, 0, $self->{array}, 0, $length);
$self->{array} = $new_array;
$self->{capacity} = $new_capacity;
}
}
method resize : void ($new_length : int) {
unless ($new_length >= 0) {
die "The new length \$new_length must be greater than or equal to 0.";
}
my $length = $self->{length};
if ($new_length > $length) {
$self->_maybe_extend($new_length);
Array->memset_int($self->{array}, 0, $length, $new_length - $length);
}
elsif ($new_length < $length) {
Array->memset_int($self->{array}, 0, $new_length, $length - $new_length);
}
$self->{length} = $new_length;
}
method set : void ($index : int, $element : int) {
my $length = $self->length;
unless ($index >= 0) {
die "The index \$index must be greater than or equal to 0.";
}
unless ($index < $length) {
die "The index \$index must be less than the length of \$list.";
}
$self->{array}[$index] = $element;
}
method shift : int () {
my $length = $self->{length};
unless ($length > 0) {
die "The length of the list \$list must be greater than 0.";
}
my $elements = $self->{array};
my $element = $elements->[0];
Array->memmove_int($elements, 0, $elements, 1, $length - 1);
$elements->[$length - 1] = 0;
$self->{length}--;
return $element;
}
method to_array : int[] () {
my $length = $self->length;
my $new_array = new int[$length];
my $elements = $self->{array};
Array->memcpy_int($new_array, 0, $elements, 0, $length);
return $new_array;
}
method get_array_unsafe : int[] () {
return $self->{array};
}
method unshift : void ($element : int) {
my $length = $self->{length};
my $new_length = $length + 1;
$self->_maybe_extend($new_length);
my $elements = $self->{array};
Array->memmove_int($elements, 1, $elements, 0, $length);
$elements->[0] = $element;
$self->{length} = $new_length;
}
private method _maybe_extend : void ($min_capacity : int) {
my $capacity = $self->{capacity};
unless ($min_capacity > $capacity) {
return;
}
if ($capacity < $min_capacity) {
$capacity = $min_capacity;
}
my $new_capacity = $capacity * 2;
my $new_array = new int[$new_capacity];
my $length = $self->{length};
my $elements = $self->{array};
Array->memcpy_int($new_array, 0, $elements, 0, $length);
$self->{array} = $new_array;
$self->{capacity} = $new_capacity;
}
}