#![allow(non_snake_case)]
#![allow(dead_code)]
use core::ffi::{c_int, c_uint};
use crate::ported::object::{Object, ObjectClass, Object_isA};
pub const VECTOR_DEFAULT_SIZE: c_int = 10;
fn swap<T>(array: &mut [T], indexA: isize, indexB: isize) {
array.swap(indexA as usize, indexB as usize);
}
fn partition<T>(
array: &mut [T],
left: isize,
right: isize,
pivotIndex: isize,
compare: &impl Fn(&T, &T) -> i32,
) -> isize {
swap(array, pivotIndex, right);
let mut storeIndex = left;
let mut i = left;
while i < right {
if compare(&array[i as usize], &array[right as usize]) <= 0 {
swap(array, i, storeIndex);
storeIndex += 1;
}
i += 1;
}
swap(array, storeIndex, right);
storeIndex
}
pub fn quickSort<T>(array: &mut [T], left: isize, right: isize, compare: &impl Fn(&T, &T) -> i32) {
if left >= right {
return;
}
let pivotIndex = left + (right - left) / 2;
let pivotNewIndex = partition(array, left, right, pivotIndex, compare);
quickSort(array, left, pivotNewIndex - 1, compare);
quickSort(array, pivotNewIndex + 1, right, compare);
}
pub fn insertionSort<T>(
array: &mut [T],
left: isize,
right: isize,
compare: &impl Fn(&T, &T) -> i32,
) {
let mut i = left + 1;
while i <= right {
let mut j = i - 1;
while j >= left && compare(&array[j as usize], &array[i as usize]) > 0 {
j -= 1;
}
array[(j + 1) as usize..=i as usize].rotate_right(1);
i += 1;
}
}
pub fn Vector_indexOf<T>(array: &[T], search: &T, compare: &impl Fn(&T, &T) -> i32) -> isize {
let mut i: isize = 0;
while (i as usize) < array.len() {
if compare(search, &array[i as usize]) == 0 {
return i;
}
i += 1;
}
-1
}
pub struct Vector {
pub array: Vec<Option<Box<dyn Object>>>,
pub type_: &'static ObjectClass,
pub owner: bool,
pub isDirty: bool,
}
pub fn Vector_new(type_: &'static ObjectClass, owner: bool, size: c_int) -> Vector {
debug_assert!(size > 0);
Vector {
array: Vec::with_capacity(size as usize),
type_,
owner,
isDirty: false,
}
}
pub fn Vector_delete(this: Vector) {
let _ = this;
}
fn Vector_isConsistent(this: &Vector) -> bool {
debug_assert!(this.array.len() <= this.array.capacity());
debug_assert!(!this.isDirty);
true
}
pub fn Vector_countEquals(this: &Vector, expectedCount: c_uint) -> bool {
let n = this.array.iter().filter(|o| o.is_some()).count();
n as c_uint == expectedCount
}
pub fn Vector_get(this: &Vector, idx: usize) -> &dyn Object {
let o = this.array[idx]
.as_deref()
.expect("Vector_get: NULL slot (C asserts this->array[idx])");
debug_assert!(Object_isA(Some(o), this.type_));
o
}
pub fn Vector_size(this: &Vector) -> c_int {
this.array.len() as c_int
}
pub fn Vector_prune(this: &mut Vector) {
if this.owner {
this.array.clear();
} else {
for slot in this.array.drain(..) {
std::mem::forget(slot);
}
}
this.isDirty = false;
}
pub fn Vector_quickSortCustomCompare(
this: &mut Vector,
compare: impl Fn(&dyn Object, &dyn Object) -> i32,
) {
let n = this.array.len() as isize;
let cmp = |a: &Option<Box<dyn Object>>, b: &Option<Box<dyn Object>>| -> i32 {
compare(
a.as_deref().expect("quickSort over a hole"),
b.as_deref().expect("quickSort over a hole"),
)
};
quickSort(&mut this.array, 0, n - 1, &cmp);
}
pub fn Vector_insertionSort(this: &mut Vector) {
let n = this.array.len() as isize;
let cmp = |a: &Option<Box<dyn Object>>, b: &Option<Box<dyn Object>>| -> i32 {
a.as_deref()
.expect("insertionSort over a hole")
.compare(b.as_deref().expect("insertionSort over a hole"))
};
insertionSort(&mut this.array, 0, n - 1, &cmp);
}
pub fn Vector_insert(this: &mut Vector, idx: c_int, data: Box<dyn Object>) {
debug_assert!(idx >= 0);
debug_assert!(Object_isA(Some(&*data), this.type_));
let mut idx = idx as usize;
if idx > this.array.len() {
idx = this.array.len();
}
this.array.insert(idx, Some(data));
}
pub fn Vector_take(this: &mut Vector, idx: c_int) -> Box<dyn Object> {
debug_assert!(idx >= 0 && (idx as usize) < this.array.len());
this.array
.remove(idx as usize)
.expect("Vector_take: NULL slot (C asserts removed)")
}
pub fn Vector_remove(this: &mut Vector, idx: c_int) -> Option<Box<dyn Object>> {
let removed = Vector_take(this, idx);
if this.owner {
drop(removed);
None
} else {
Some(removed)
}
}
pub fn Vector_softRemove(this: &mut Vector, idx: c_int) -> Option<Box<dyn Object>> {
debug_assert!(idx >= 0 && (idx as usize) < this.array.len());
let removed = this.array[idx as usize]
.take()
.expect("Vector_softRemove: NULL slot (C asserts removed)");
this.isDirty = true;
if this.owner {
drop(removed);
None
} else {
Some(removed)
}
}
pub fn Vector_compact(this: &mut Vector, dirtyIndex: c_int) {
if !this.isDirty {
return;
}
debug_assert!(0 <= dirtyIndex);
let dirtyIndex = dirtyIndex as usize;
if dirtyIndex >= this.array.len() {
return;
}
debug_assert!(this.array[dirtyIndex].is_none());
let items = this.array.len();
let mut write = dirtyIndex;
for i in (dirtyIndex + 1)..items {
if this.array[i].is_some() {
let val = this.array[i].take();
this.array[write] = val;
write += 1;
}
}
this.array.truncate(write);
this.isDirty = false;
}
pub fn Vector_moveUp(this: &mut Vector, idx: c_int) {
debug_assert!(idx >= 0 && (idx as usize) < this.array.len());
if idx == 0 {
return;
}
this.array.swap(idx as usize, (idx - 1) as usize);
}
pub fn Vector_moveDown(this: &mut Vector, idx: c_int) {
debug_assert!(idx >= 0 && (idx as usize) < this.array.len());
if idx as usize == this.array.len() - 1 {
return;
}
this.array.swap(idx as usize, (idx + 1) as usize);
}
pub fn Vector_set(this: &mut Vector, idx: c_int, data: Box<dyn Object>) {
debug_assert!(idx >= 0);
debug_assert!(Object_isA(Some(&*data), this.type_));
let idx = idx as usize;
if idx >= this.array.len() {
while this.array.len() < idx {
this.array.push(None);
}
this.array.push(Some(data));
} else if this.owner {
this.array[idx] = Some(data);
} else {
let old = this.array[idx].replace(data);
std::mem::forget(old);
}
}
pub fn Vector_merge() {
todo!("port of Vector.c:329 — commented-out (dead) code in the C source")
}
pub fn Vector_add(this: &mut Vector, data: Box<dyn Object>) {
debug_assert!(Object_isA(Some(&*data), this.type_));
let i = this.array.len();
Vector_set(this, i as c_int, data);
debug_assert!(this.array.len() == i + 1);
}
pub fn Vector_splice(this: &mut Vector, from: Vector) {
debug_assert!(Vector_isConsistent(this));
debug_assert!(Vector_isConsistent(&from));
debug_assert!(!this.owner);
for slot in from.array {
this.array.push(slot);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn asc(a: &i32, b: &i32) -> i32 {
a.cmp(b) as i32
}
fn desc(a: &i32, b: &i32) -> i32 {
b.cmp(a) as i32
}
fn qsorted(mut v: Vec<i32>, compare: &impl Fn(&i32, &i32) -> i32) -> Vec<i32> {
let n = v.len() as isize;
quickSort(&mut v, 0, n - 1, compare);
v
}
fn isorted(mut v: Vec<i32>, compare: &impl Fn(&i32, &i32) -> i32) -> Vec<i32> {
let n = v.len() as isize;
insertionSort(&mut v, 0, n - 1, compare);
v
}
#[test]
fn both_sorts_match_reference_ascending() {
let inputs: Vec<Vec<i32>> = vec![
vec![],
vec![42],
vec![2, 1],
vec![1, 2],
vec![5, 3, 8, 1, 9, 2, 7],
vec![3, 3, 3, 3],
vec![9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
vec![-5, 10, -3, 0, 7, -1, 2, 2, -5],
vec![i32::MIN, i32::MAX, 0, -1, 1],
];
for input in inputs {
let mut reference = input.clone();
reference.sort();
assert_eq!(
qsorted(input.clone(), &asc),
reference,
"quickSort {input:?}"
);
assert_eq!(
isorted(input.clone(), &asc),
reference,
"insertionSort {input:?}"
);
assert_eq!(qsorted(input.clone(), &asc), isorted(input, &asc));
}
}
#[test]
fn descending_comparator_reverses_order() {
let input = vec![5, 3, 8, 1, 9, 2, 7];
let mut reference = input.clone();
reference.sort_by(|a, b| b.cmp(a));
assert_eq!(qsorted(input.clone(), &desc), reference);
assert_eq!(isorted(input, &desc), reference);
}
#[test]
fn empty_and_single_are_noops() {
assert_eq!(qsorted(vec![], &asc), Vec::<i32>::new());
assert_eq!(isorted(vec![], &asc), Vec::<i32>::new());
assert_eq!(qsorted(vec![7], &asc), vec![7]);
assert_eq!(isorted(vec![7], &asc), vec![7]);
}
#[test]
fn index_of_returns_first_match() {
let v = vec![10, 20, 30, 20, 40];
assert_eq!(Vector_indexOf(&v, &20, &asc), 1);
assert_eq!(Vector_indexOf(&v, &10, &asc), 0);
assert_eq!(Vector_indexOf(&v, &40, &asc), 4);
}
#[test]
fn index_of_absent_returns_minus_one() {
let v = vec![10, 20, 30];
assert_eq!(Vector_indexOf(&v, &99, &asc), -1);
assert_eq!(Vector_indexOf::<i32>(&[], &1, &asc), -1);
}
#[test]
fn index_of_uses_comparator_equality_not_identity() {
let v = vec![1, 2, 3];
assert_eq!(Vector_indexOf(&v, &999, &|_, _| 0), 0);
assert_eq!(Vector_indexOf(&v, &1, &|_, _| 1), -1);
}
use crate::ported::object::Object_class;
use core::any::Any;
static TEST_CLASS: ObjectClass = ObjectClass {
extends: Some(&Object_class),
};
struct TestObj {
n: i32,
}
impl Object for TestObj {
fn klass(&self) -> &'static ObjectClass {
&TEST_CLASS
}
fn compare(&self, other: &dyn Object) -> i32 {
let any: &dyn Any = other;
let o = any
.downcast_ref::<TestObj>()
.expect("compare across incompatible classes");
(self.n > o.n) as i32 - (self.n < o.n) as i32
}
}
fn obj(n: i32) -> Box<dyn Object> {
Box::new(TestObj { n })
}
fn val(o: &dyn Object) -> i32 {
let any: &dyn Any = o;
any.downcast_ref::<TestObj>().unwrap().n
}
fn owning() -> Vector {
Vector_new(&TEST_CLASS, true, 10)
}
fn borrowing() -> Vector {
Vector_new(&TEST_CLASS, false, 10)
}
#[test]
fn new_is_empty_and_records_flags() {
let v = Vector_new(&TEST_CLASS, true, 8);
assert_eq!(Vector_size(&v), 0);
assert!(v.owner);
assert!(!v.isDirty);
assert!(v.array.is_empty());
}
#[test]
fn add_size_and_get() {
let mut v = owning();
Vector_add(&mut v, obj(10));
Vector_add(&mut v, obj(20));
Vector_add(&mut v, obj(30));
assert_eq!(Vector_size(&v), 3);
assert_eq!(val(Vector_get(&v, 0)), 10);
assert_eq!(val(Vector_get(&v, 1)), 20);
assert_eq!(val(Vector_get(&v, 2)), 30);
assert!(Vector_countEquals(&v, 3));
}
#[test]
fn insert_shifts_and_clamps_past_end() {
let mut v = owning();
Vector_add(&mut v, obj(1));
Vector_add(&mut v, obj(3));
Vector_insert(&mut v, 1, obj(2));
assert_eq!(Vector_size(&v), 3);
assert_eq!(val(Vector_get(&v, 0)), 1);
assert_eq!(val(Vector_get(&v, 1)), 2);
assert_eq!(val(Vector_get(&v, 2)), 3);
Vector_insert(&mut v, 99, obj(4));
assert_eq!(Vector_size(&v), 4);
assert_eq!(val(Vector_get(&v, 3)), 4);
}
#[test]
fn take_removes_and_returns_without_freeing() {
let mut v = borrowing();
Vector_add(&mut v, obj(10));
Vector_add(&mut v, obj(20));
Vector_add(&mut v, obj(30));
let taken = Vector_take(&mut v, 1);
assert_eq!(val(taken.as_ref()), 20);
assert_eq!(Vector_size(&v), 2);
assert_eq!(val(Vector_get(&v, 0)), 10);
assert_eq!(val(Vector_get(&v, 1)), 30);
}
#[test]
fn remove_frees_when_owner_returns_when_not() {
let mut owned = owning();
Vector_add(&mut owned, obj(1));
Vector_add(&mut owned, obj(2));
assert!(Vector_remove(&mut owned, 0).is_none());
assert_eq!(Vector_size(&owned), 1);
assert_eq!(val(Vector_get(&owned, 0)), 2);
let mut shared = borrowing();
Vector_add(&mut shared, obj(7));
Vector_add(&mut shared, obj(8));
let got = Vector_remove(&mut shared, 1).expect("non-owner returns the element");
assert_eq!(val(got.as_ref()), 8);
assert_eq!(Vector_size(&shared), 1);
}
#[test]
fn soft_remove_punches_hole_then_compact_reclaims() {
let mut v = borrowing();
for n in [10, 20, 30, 40] {
Vector_add(&mut v, obj(n));
}
let removed = Vector_softRemove(&mut v, 1).expect("non-owner returns the element");
assert_eq!(val(removed.as_ref()), 20);
assert!(v.isDirty);
assert_eq!(Vector_size(&v), 4); assert!(v.array[1].is_none());
assert!(Vector_countEquals(&v, 3));
Vector_compact(&mut v, 1);
assert!(!v.isDirty);
assert_eq!(Vector_size(&v), 3);
assert_eq!(val(Vector_get(&v, 0)), 10);
assert_eq!(val(Vector_get(&v, 1)), 30);
assert_eq!(val(Vector_get(&v, 2)), 40);
}
#[test]
fn compact_is_noop_when_not_dirty() {
let mut v = borrowing();
Vector_add(&mut v, obj(1));
Vector_add(&mut v, obj(2));
Vector_compact(&mut v, 0); assert_eq!(Vector_size(&v), 2);
}
#[test]
fn move_up_and_down_swap_neighbors_with_edge_noops() {
let mut v = owning();
for n in [10, 20, 30] {
Vector_add(&mut v, obj(n));
}
Vector_moveUp(&mut v, 2);
assert_eq!(val(Vector_get(&v, 1)), 30);
assert_eq!(val(Vector_get(&v, 2)), 20);
Vector_moveUp(&mut v, 0);
assert_eq!(val(Vector_get(&v, 0)), 10);
Vector_moveDown(&mut v, 0);
assert_eq!(val(Vector_get(&v, 0)), 30);
assert_eq!(val(Vector_get(&v, 1)), 10);
Vector_moveDown(&mut v, 2);
assert_eq!(val(Vector_get(&v, 2)), 20);
}
#[test]
fn set_replaces_in_range_and_extends_with_holes_beyond_items() {
let mut v = owning();
Vector_add(&mut v, obj(10));
Vector_add(&mut v, obj(20));
Vector_set(&mut v, 1, obj(99));
assert_eq!(Vector_size(&v), 2);
assert_eq!(val(Vector_get(&v, 1)), 99);
Vector_set(&mut v, 4, obj(500));
assert_eq!(Vector_size(&v), 5);
assert!(v.array[2].is_none());
assert!(v.array[3].is_none());
assert_eq!(val(Vector_get(&v, 4)), 500);
assert!(Vector_countEquals(&v, 3));
}
#[test]
fn index_of_over_container_reuses_generic_helper() {
let mut v = owning();
for n in [10, 20, 30, 20, 40] {
Vector_add(&mut v, obj(n));
}
let cmp = |a: &Option<Box<dyn Object>>, b: &Option<Box<dyn Object>>| -> i32 {
a.as_deref().unwrap().compare(b.as_deref().unwrap())
};
let needle: Option<Box<dyn Object>> = Some(obj(20));
assert_eq!(Vector_indexOf(&v.array, &needle, &cmp), 1); let miss: Option<Box<dyn Object>> = Some(obj(99));
assert_eq!(Vector_indexOf(&v.array, &miss, &cmp), -1);
}
#[test]
fn splice_appends_source_elements_consuming_it() {
let mut dst = borrowing();
Vector_add(&mut dst, obj(1));
Vector_add(&mut dst, obj(2));
let mut src = owning();
Vector_add(&mut src, obj(10));
Vector_add(&mut src, obj(20));
Vector_splice(&mut dst, src);
assert_eq!(Vector_size(&dst), 4);
assert_eq!(val(Vector_get(&dst, 0)), 1);
assert_eq!(val(Vector_get(&dst, 1)), 2);
assert_eq!(val(Vector_get(&dst, 2)), 10);
assert_eq!(val(Vector_get(&dst, 3)), 20);
}
#[test]
fn splice_from_empty_source_is_a_noop() {
let mut dst = borrowing();
Vector_add(&mut dst, obj(7));
Vector_splice(&mut dst, borrowing());
assert_eq!(Vector_size(&dst), 1);
assert_eq!(val(Vector_get(&dst, 0)), 7);
}
#[test]
fn quicksort_and_insertion_sort_order_the_container() {
let mut v = owning();
for n in [3, 1, 4, 1, 5, 9, 2, 6] {
Vector_add(&mut v, obj(n));
}
Vector_quickSortCustomCompare(&mut v, |a, b| {
b.compare(a)
});
let got: Vec<i32> = (0..Vector_size(&v))
.map(|i| val(Vector_get(&v, i as usize)))
.collect();
assert_eq!(got, vec![9, 6, 5, 4, 3, 2, 1, 1]);
let mut w = owning();
for n in [3, 1, 4, 1, 5, 9, 2, 6] {
Vector_add(&mut w, obj(n));
}
Vector_insertionSort(&mut w);
let got2: Vec<i32> = (0..Vector_size(&w))
.map(|i| val(Vector_get(&w, i as usize)))
.collect();
assert_eq!(got2, vec![1, 1, 2, 3, 4, 5, 6, 9]);
}
}