From 76eec5336efc35cbbe4880f3f537ba11db19bf74 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Tue, 29 Jan 2019 16:59:38 -0800
Subject: [PATCH 01/50] Draft implementation of concurrent modification guard

---
 src/common.h                            | 23 +++++++++++++++
 src/php/classes/php_map_ce.c            |  3 ++
 src/php/classes/php_priority_queue_ce.c |  3 ++
 src/php/classes/php_queue_ce.c          |  3 ++
 src/php/classes/php_set_ce.c            |  5 ++++
 src/php/classes/php_stack_ce.c          |  3 ++
 src/php/classes/php_vector_ce.c         | 13 +++++----
 src/php/handlers/php_deque_handlers.c   |  3 ++
 src/php/handlers/php_map_handlers.c     |  3 ++
 src/php/handlers/php_queue_handlers.c   |  1 +
 src/php/handlers/php_set_handlers.c     |  3 +-
 src/php/handlers/php_stack_handlers.c   |  1 +
 src/php/handlers/php_vector_handlers.c  |  3 ++
 src/php/iterators/php_htable_iterator.c | 38 +++++++++++++++++++------
 src/php/iterators/php_htable_iterator.h | 15 ++++++----
 src/php/iterators/php_map_iterator.c    | 12 +++++++-
 src/php/iterators/php_set_iterator.c    | 12 +++++++-
 src/php/iterators/php_stack_iterator.c  |  5 +++-
 src/php/iterators/php_vector_iterator.c |  5 ++++
 src/php/iterators/php_vector_iterator.h |  4 +--
 src/php/objects/php_deque.c             |  8 ++++--
 src/php/objects/php_deque.h             | 10 +++++--
 src/php/objects/php_map.c               |  8 ++++--
 src/php/objects/php_map.h               | 10 +++++--
 src/php/objects/php_priority_queue.c    | 10 ++++---
 src/php/objects/php_priority_queue.h    |  8 +++++-
 src/php/objects/php_queue.c             |  7 +++--
 src/php/objects/php_queue.h             |  8 +++++-
 src/php/objects/php_set.c               |  8 ++++--
 src/php/objects/php_set.h               |  8 +++++-
 src/php/objects/php_stack.c             |  8 ++++--
 src/php/objects/php_stack.h             |  8 +++++-
 src/php/objects/php_vector.c            |  7 +++--
 src/php/objects/php_vector.h            |  8 +++++-
 34 files changed, 223 insertions(+), 51 deletions(-)

diff --git a/src/common.h b/src/common.h
index 252ebfa..d61021d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -42,6 +42,25 @@
 #define PHP_DS_ME(cls, name) \
     PHP_ME(cls, name, arginfo_##cls##_##name, ZEND_ACC_PUBLIC)
 
+/**
+ *
+ */
+#define PHP_DS_NO_ITERATOR_OR_RETURN(obj) do { \
+    if (UNEXPECTED(obj->iteratorCount)) { \
+        MODIFIED_DURING_ITERATION(); \
+    } \
+} while (0)
+
+/**
+ * Increases the object's iterator count by 1.
+ */
+#define PHP_DS_INCR_ITERATOR(obj) (obj)->iteratorCount++
+
+/**
+ * Decreases the object's iterator count by 1.
+ */
+#define PHP_DS_DECR_ITERATOR(obj) (obj)->iteratorCount--
+
 /**
  *
  */
@@ -222,6 +241,10 @@ int name##_unserialize(                 \
     zend_ce_error, \
     "Immutable objects may not be changed")
 
+#define MODIFIED_DURING_ITERATION() ds_throw_exception( \
+    spl_ce_LogicException, \
+    "Modification during iteration is not supported")
+
 /**
  *
  */
diff --git a/src/php/classes/php_map_ce.c b/src/php/classes/php_map_ce.c
index ec9efd6..86deb72 100644
--- a/src/php/classes/php_map_ce.c
+++ b/src/php/classes/php_map_ce.c
@@ -48,12 +48,14 @@ METHOD(capacity)
 METHOD(put)
 {
     PARSE_ZVAL_ZVAL(key, value);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_MAP_OBJ());
     ds_map_put(THIS_DS_MAP(), key, value);
 }
 
 METHOD(putAll)
 {
     PARSE_ZVAL(values);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_MAP_OBJ());
     ds_map_put_all(THIS_DS_MAP(), values);
 }
 
@@ -96,6 +98,7 @@ METHOD(diff)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_MAP_OBJ());
     ds_map_clear(THIS_DS_MAP());
 }
 
diff --git a/src/php/classes/php_priority_queue_ce.c b/src/php/classes/php_priority_queue_ce.c
index 22da404..edd50f8 100644
--- a/src/php/classes/php_priority_queue_ce.c
+++ b/src/php/classes/php_priority_queue_ce.c
@@ -40,12 +40,14 @@ METHOD(copy)
 METHOD(push)
 {
     PARSE_ZVAL_ZVAL(value, priority);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_PRIORITY_QUEUE_OBJ());
     ds_priority_queue_push(THIS_DS_PRIORITY_QUEUE(), value, priority);
 }
 
 METHOD(pop)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_PRIORITY_QUEUE_OBJ());
     ds_priority_queue_pop(THIS_DS_PRIORITY_QUEUE(), return_value);
 }
 
@@ -76,6 +78,7 @@ METHOD(count)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_PRIORITY_QUEUE_OBJ());
     ds_priority_queue_clear(THIS_DS_PRIORITY_QUEUE());
 }
 
diff --git a/src/php/classes/php_queue_ce.c b/src/php/classes/php_queue_ce.c
index d69988a..653b034 100644
--- a/src/php/classes/php_queue_ce.c
+++ b/src/php/classes/php_queue_ce.c
@@ -38,12 +38,14 @@ METHOD(capacity)
 METHOD(push)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_QUEUE_OBJ());
     ds_queue_push(THIS_DS_QUEUE(), argc, argv);
 }
 
 METHOD(pop)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_QUEUE_OBJ());
     ds_queue_pop_throw(THIS_DS_QUEUE(), return_value);
 }
 
@@ -68,6 +70,7 @@ METHOD(count)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_QUEUE_OBJ());
     ds_queue_clear(THIS_DS_QUEUE());
 }
 
diff --git a/src/php/classes/php_set_ce.c b/src/php/classes/php_set_ce.c
index d961837..099f4b4 100644
--- a/src/php/classes/php_set_ce.c
+++ b/src/php/classes/php_set_ce.c
@@ -49,12 +49,14 @@ METHOD(capacity)
 METHOD(add)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_SET_OBJ());
     ds_set_add_va(THIS_DS_SET(), argc, argv);
 }
 
 METHOD(remove)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_SET_OBJ());
     ds_set_remove_va(THIS_DS_SET(), argc, argv);
 }
 
@@ -121,6 +123,7 @@ METHOD(union)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_SET_OBJ());
     ds_set_clear(THIS_DS_SET());
 }
 
@@ -144,6 +147,8 @@ METHOD(isEmpty)
 
 METHOD(sort)
 {
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_SET_OBJ());
+
     if (ZEND_NUM_ARGS()) {
         PARSE_COMPARE_CALLABLE();
         ds_set_sort_callback(THIS_DS_SET());
diff --git a/src/php/classes/php_stack_ce.c b/src/php/classes/php_stack_ce.c
index b44905b..8b4d45a 100644
--- a/src/php/classes/php_stack_ce.c
+++ b/src/php/classes/php_stack_ce.c
@@ -39,12 +39,14 @@ METHOD(capacity)
 METHOD(push)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_STACK_OBJ());
     ds_stack_push_va(THIS_DS_STACK(), argc, argv);
 }
 
 METHOD(pop)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_STACK_OBJ());
     ds_stack_pop_throw(THIS_DS_STACK(), return_value);
 }
 
@@ -69,6 +71,7 @@ METHOD(copy)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_STACK_OBJ());
     ds_stack_clear(THIS_DS_STACK());
 }
 
diff --git a/src/php/classes/php_vector_ce.c b/src/php/classes/php_vector_ce.c
index d590215..27af77f 100644
--- a/src/php/classes/php_vector_ce.c
+++ b/src/php/classes/php_vector_ce.c
@@ -45,6 +45,7 @@ METHOD(capacity)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_clear(THIS_DS_VECTOR());
 }
 
@@ -97,6 +98,7 @@ METHOD(get)
 METHOD(insert)
 {
     PARSE_LONG_AND_VARIADIC_ZVAL(index);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_insert_va(THIS_DS_VECTOR(), index, argc, argv);
 }
 
@@ -143,21 +145,17 @@ METHOD(merge)
 METHOD(pop)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_pop_throw(THIS_DS_VECTOR(), return_value);
 }
 
 METHOD(push)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_push_va(THIS_DS_VECTOR(), argc, argv);
 }
 
-METHOD(push_one)
-{
-    PARSE_ZVAL(value);
-    ds_vector_push(THIS_DS_VECTOR(), value);
-}
-
 METHOD(reduce)
 {
     PARSE_CALLABLE_AND_OPTIONAL_ZVAL(initial);
@@ -167,6 +165,7 @@ METHOD(reduce)
 METHOD(remove)
 {
     PARSE_LONG(index);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_remove(THIS_DS_VECTOR(), index, return_value);
 }
 
@@ -197,6 +196,7 @@ METHOD(set)
 METHOD(shift)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_shift_throw(THIS_DS_VECTOR(), return_value);
 }
 
@@ -254,6 +254,7 @@ METHOD(toArray)
 METHOD(unshift)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_unshift_va(THIS_DS_VECTOR(), argc, argv);
 }
 
diff --git a/src/php/handlers/php_deque_handlers.c b/src/php/handlers/php_deque_handlers.c
index 2cb85be..c2124dc 100644
--- a/src/php/handlers/php_deque_handlers.c
+++ b/src/php/handlers/php_deque_handlers.c
@@ -44,6 +44,7 @@ static void php_ds_deque_write_dimension(zval *obj, zval *offset, zval *value)
     ds_deque_t *deque = Z_DS_DEQUE_P(obj);
 
     if (offset == NULL) { /* $v[] = ... */
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_DEQUE_OBJ_P(obj));
         ds_deque_push(deque, value);
 
     } else {
@@ -72,6 +73,8 @@ static void php_ds_deque_unset_dimension(zval *obj, zval *offset)
 {
     ZVAL_DEREF(offset);
 
+    PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_DEQUE_OBJ_P(obj));
+
     if (Z_TYPE_P(offset) != IS_LONG) {
         return;
 
diff --git a/src/php/handlers/php_map_handlers.c b/src/php/handlers/php_map_handlers.c
index ef44e4d..c93af9b 100644
--- a/src/php/handlers/php_map_handlers.c
+++ b/src/php/handlers/php_map_handlers.c
@@ -49,6 +49,7 @@ static void php_ds_map_write_dimension(zval *obj, zval *offset, zval *value)
 
     ZVAL_DEREF(offset);
 
+    PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_MAP_OBJ_P(obj));
     ds_htable_put(map->table, offset, value);
 }
 
@@ -67,6 +68,8 @@ static void php_ds_map_unset_dimension(zval *obj, zval *offset)
 
     ZVAL_DEREF(offset);
 
+
+    PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_MAP_OBJ_P(obj));
     ds_htable_remove(map->table, offset, NULL);
 }
 
diff --git a/src/php/handlers/php_queue_handlers.c b/src/php/handlers/php_queue_handlers.c
index 4210338..63d1d52 100644
--- a/src/php/handlers/php_queue_handlers.c
+++ b/src/php/handlers/php_queue_handlers.c
@@ -11,6 +11,7 @@ static void php_ds_queue_write_dimension(zval *obj, zval *offset, zval *value)
     ds_queue_t *queue = Z_DS_QUEUE_P(obj);
 
     if (offset == NULL) {
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_QUEUE_OBJ_P(obj));
         ds_queue_push_one(queue, value);
         return;
     }
diff --git a/src/php/handlers/php_set_handlers.c b/src/php/handlers/php_set_handlers.c
index dcd57f5..6e398a0 100644
--- a/src/php/handlers/php_set_handlers.c
+++ b/src/php/handlers/php_set_handlers.c
@@ -31,6 +31,7 @@ static zval *php_ds_set_read_dimension(zval *obj, zval *offset, int type, zval *
 static void php_ds_set_write_dimension(zval *obj, zval *offset, zval *value)
 {
     if (offset == NULL) {
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_SET_OBJ_P(obj));
         ds_set_add(Z_DS_SET_P(obj), value);
         return;
     }
@@ -87,7 +88,7 @@ void php_ds_register_set_handlers()
 {
     memcpy(&php_ds_set_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 
-    php_ds_set_handlers.offset = 0; // XtOffsetOf(php_ds_set_t, std);
+    php_ds_set_handlers.offset = 0;
 
     php_ds_set_handlers.cast_object     = php_ds_default_cast_object;
     php_ds_set_handlers.clone_obj       = php_ds_set_clone_obj;
diff --git a/src/php/handlers/php_stack_handlers.c b/src/php/handlers/php_stack_handlers.c
index c06fc0f..b50c447 100644
--- a/src/php/handlers/php_stack_handlers.c
+++ b/src/php/handlers/php_stack_handlers.c
@@ -10,6 +10,7 @@ static void php_ds_stack_write_dimension(zval *obj, zval *offset, zval *value)
     ds_stack_t *stack = Z_DS_STACK_P(obj);
 
     if (offset == NULL) {
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_STACK_OBJ_P(obj));
         ds_stack_push(stack, value);
         return;
     }
diff --git a/src/php/handlers/php_vector_handlers.c b/src/php/handlers/php_vector_handlers.c
index 5817feb..03fe45e 100644
--- a/src/php/handlers/php_vector_handlers.c
+++ b/src/php/handlers/php_vector_handlers.c
@@ -45,6 +45,7 @@ static void php_ds_vector_write_dimension(zval *obj, zval *offset, zval *value)
 
     /* $v[] = ... */
     if (offset == NULL) {
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_VECTOR_OBJ_P(obj));
         ds_vector_push(vector, value);
 
     } else {
@@ -74,6 +75,8 @@ static void php_ds_vector_unset_dimension(zval *obj, zval *offset)
     zend_long index;
     ds_vector_t *vector = Z_DS_VECTOR_P(obj);
 
+    PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_VECTOR_OBJ_P(obj));
+
     ZVAL_DEREF(offset);
 
     if (Z_TYPE_P(offset) == IS_LONG) {
diff --git a/src/php/iterators/php_htable_iterator.c b/src/php/iterators/php_htable_iterator.c
index 74e7dbf..a94e486 100644
--- a/src/php/iterators/php_htable_iterator.c
+++ b/src/php/iterators/php_htable_iterator.c
@@ -24,6 +24,10 @@ static void php_ds_htable_iterator_dtor(zend_object_iterator *iter)
 
     OBJ_RELEASE(iterator->obj);
     DTOR_AND_UNDEF(&iterator->intern.data);
+
+    if (iterator->dtor) {
+        iterator->dtor((zend_object_iterator *) iterator);
+    }
 }
 
 static int php_ds_htable_iterator_valid(zend_object_iterator *iter)
@@ -160,7 +164,9 @@ static zend_object_iterator *php_ds_htable_create_htable_iterator(
     zval *obj,
     ds_htable_t *table,
     zend_object_iterator_funcs *funcs,
-    int by_ref
+    int by_ref,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ) {
     ds_htable_iterator_t *iterator;
 
@@ -178,6 +184,8 @@ static zend_object_iterator *php_ds_htable_create_htable_iterator(
     iterator->intern.funcs  = funcs;
     iterator->table         = table;
     iterator->obj           = Z_OBJ_P(obj);
+    iterator->ctor          = ctor;
+    iterator->dtor          = dtor;
 
     // Add a reference to the object so that it doesn't get collected when
     // the iterated object is implict, eg. foreach ($obj->getInstance() as $value){ ... }
@@ -187,6 +195,10 @@ static zend_object_iterator *php_ds_htable_create_htable_iterator(
     ++GC_REFCOUNT(iterator->obj);
 #endif
 
+    if (iterator->ctor) {
+        iterator->ctor((zend_object_iterator *) iterator);
+    }
+
     return (zend_object_iterator *) iterator;
 }
 
@@ -194,38 +206,46 @@ zend_object_iterator *php_ds_htable_get_value_iterator_ex(
     zend_class_entry *ce,
     zval *obj,
     int by_ref,
-    ds_htable_t *table
+    ds_htable_t *table,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ){
     return php_ds_htable_create_htable_iterator(
-        obj, table, &php_ds_htable_get_value_iterator_funcs, by_ref);
+        obj, table, &php_ds_htable_get_value_iterator_funcs, by_ref, ctor, dtor);
 }
 
 zend_object_iterator *php_ds_htable_get_key_iterator_ex(
     zend_class_entry *ce,
     zval *obj,
     int by_ref,
-    ds_htable_t *table
+    ds_htable_t *table,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ){
     return php_ds_htable_create_htable_iterator(
-        obj, table, &php_ds_htable_get_key_iterator_funcs, by_ref);
+        obj, table, &php_ds_htable_get_key_iterator_funcs, by_ref, ctor, dtor);
 }
 
 zend_object_iterator *php_ds_htable_get_pair_iterator_ex(
     zend_class_entry *ce,
     zval *obj,
     int by_ref,
-    ds_htable_t *table
+    ds_htable_t *table,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ){
     return php_ds_htable_create_htable_iterator(
-        obj, table, &php_ds_htable_get_pair_iterator_funcs, by_ref);
+        obj, table, &php_ds_htable_get_pair_iterator_funcs, by_ref, ctor, dtor);
 }
 
 zend_object_iterator *php_ds_htable_get_assoc_iterator_ex(
     zend_class_entry *ce,
     zval *obj,
     int by_ref,
-    ds_htable_t *table
+    ds_htable_t *table,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ){
     return php_ds_htable_create_htable_iterator(
-        obj, table, &php_ds_htable_get_assoc_iterator_funcs, by_ref);
+        obj, table, &php_ds_htable_get_assoc_iterator_funcs, by_ref, ctor, dtor);
 }
diff --git a/src/php/iterators/php_htable_iterator.h b/src/php/iterators/php_htable_iterator.h
index cb2eec0..d9205d0 100644
--- a/src/php/iterators/php_htable_iterator.h
+++ b/src/php/iterators/php_htable_iterator.h
@@ -4,19 +4,24 @@
 #include "php.h"
 #include "../../ds/ds_htable.h"
 
+typedef void (*ds_htable_iterator_ctor)(zend_object_iterator *iterator);
+typedef void (*ds_htable_iterator_dtor)(zend_object_iterator *iterator);
+
 typedef struct ds_htable_iterator {
 
     zend_object_iterator     intern;
     uint32_t                 position;
     ds_htable_bucket_t      *bucket;
     ds_htable_t             *table;
-    zend_object             *obj;
+    zend_object             *obj; // TODO this already exists on intern?
+    ds_htable_iterator_ctor  ctor;
+    ds_htable_iterator_dtor  dtor;
 
 } ds_htable_iterator_t;
 
-zend_object_iterator *php_ds_htable_get_value_iterator_ex(zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h);
-zend_object_iterator *php_ds_htable_get_key_iterator_ex  (zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h);
-zend_object_iterator *php_ds_htable_get_pair_iterator_ex (zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h);
-zend_object_iterator *php_ds_htable_get_assoc_iterator_ex(zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h);
+zend_object_iterator *php_ds_htable_get_value_iterator_ex(zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h, ds_htable_iterator_ctor ctor, ds_htable_iterator_dtor dtor);
+zend_object_iterator *php_ds_htable_get_key_iterator_ex  (zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h, ds_htable_iterator_ctor ctor, ds_htable_iterator_dtor dtor);
+zend_object_iterator *php_ds_htable_get_pair_iterator_ex (zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h, ds_htable_iterator_ctor ctor, ds_htable_iterator_dtor dtor);
+zend_object_iterator *php_ds_htable_get_assoc_iterator_ex(zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h, ds_htable_iterator_ctor ctor, ds_htable_iterator_dtor dtor);
 
 #endif
diff --git a/src/php/iterators/php_map_iterator.c b/src/php/iterators/php_map_iterator.c
index e32ad92..41fead3 100644
--- a/src/php/iterators/php_map_iterator.c
+++ b/src/php/iterators/php_map_iterator.c
@@ -7,8 +7,18 @@
 #include "php_map_iterator.h"
 #include "php_htable_iterator.h"
 
+static void php_ds_map_iterator_ctor(zend_object_iterator *iterator)
+{
+    PHP_DS_INCR_ITERATOR((php_ds_map_t *) &iterator->std);
+}
+
+static void php_ds_map_iterator_dtor(zend_object_iterator *iterator)
+{
+    PHP_DS_DECR_ITERATOR((php_ds_map_t *) &iterator->std);
+}
+
 zend_object_iterator *php_ds_map_get_iterator(zend_class_entry *ce, zval *obj, int by_ref)
 {
     ds_htable_t *table = Z_DS_MAP_P(obj)->table;
-    return php_ds_htable_get_assoc_iterator_ex(ce, obj, by_ref, table);
+    return php_ds_htable_get_assoc_iterator_ex(ce, obj, by_ref, table, php_ds_map_iterator_ctor, php_ds_map_iterator_dtor);
 }
diff --git a/src/php/iterators/php_set_iterator.c b/src/php/iterators/php_set_iterator.c
index 170c9fe..cf0ee85 100644
--- a/src/php/iterators/php_set_iterator.c
+++ b/src/php/iterators/php_set_iterator.c
@@ -7,8 +7,18 @@
 #include "php_set_iterator.h"
 #include "php_htable_iterator.h"
 
+static void php_ds_set_iterator_ctor(zend_object_iterator *iterator)
+{
+    PHP_DS_INCR_ITERATOR((php_ds_set_t *) (&iterator->std));
+}
+
+static void php_ds_set_iterator_dtor(zend_object_iterator *iterator)
+{
+    PHP_DS_DECR_ITERATOR((php_ds_set_t *) (&iterator->std));
+}
+
 zend_object_iterator *php_ds_set_get_iterator(zend_class_entry *ce, zval *obj, int by_ref)
 {
     ds_htable_t *table = (Z_DS_SET_P(obj))->table;
-    return php_ds_htable_get_key_iterator_ex(ce, obj, by_ref, table);
+    return php_ds_htable_get_key_iterator_ex(ce, obj, by_ref, table, php_ds_set_iterator_ctor, php_ds_set_iterator_dtor);
 }
diff --git a/src/php/iterators/php_stack_iterator.c b/src/php/iterators/php_stack_iterator.c
index 999a429..2e3d79d 100644
--- a/src/php/iterators/php_stack_iterator.c
+++ b/src/php/iterators/php_stack_iterator.c
@@ -4,7 +4,7 @@
 
 static void php_ds_stack_iterator_dtor(zend_object_iterator *iter)
 {
-
+    PHP_DS_DECR_ITERATOR((php_ds_stack_t *) (&iter->std));
 }
 
 static int php_ds_stack_iterator_valid(zend_object_iterator *iter)
@@ -63,6 +63,7 @@ zend_object_iterator *php_ds_stack_get_iterator(zend_class_entry *ce, zval *obje
         return NULL;
     }
 
+
     iterator = ecalloc(1, sizeof(php_ds_stack_iterator_t));
     zend_iterator_init((zend_object_iterator*) iterator);
 
@@ -70,5 +71,7 @@ zend_object_iterator *php_ds_stack_get_iterator(zend_class_entry *ce, zval *obje
     iterator->stack        = Z_DS_STACK_P(object);
     iterator->position     = 0;
 
+    PHP_DS_INCR_ITERATOR(Z_DS_STACK_OBJ_P(object));
+
     return (zend_object_iterator *) iterator;
 }
diff --git a/src/php/iterators/php_vector_iterator.c b/src/php/iterators/php_vector_iterator.c
index 222fdf6..ebd7af0 100644
--- a/src/php/iterators/php_vector_iterator.c
+++ b/src/php/iterators/php_vector_iterator.c
@@ -8,6 +8,8 @@ static void php_ds_vector_iterator_dtor(zend_object_iterator *iter)
 {
     php_ds_vector_iterator_t *iterator = (php_ds_vector_iterator_t *) iter;
 
+    PHP_DS_DECR_ITERATOR((php_ds_vector_t *) iterator->object);
+
     OBJ_RELEASE(iterator->object);
 }
 
@@ -53,6 +55,7 @@ static zend_object_iterator *php_ds_vector_create_iterator(zval *obj, int by_ref
 {
     php_ds_vector_iterator_t *iterator;
 
+// TODO: if we disable modification during iteration, could be reconsider this?
     if (by_ref) {
         ITERATION_BY_REF_NOT_SUPPORTED();
         return NULL;
@@ -67,6 +70,8 @@ static zend_object_iterator *php_ds_vector_create_iterator(zval *obj, int by_ref
     iterator->object        = Z_OBJ_P(obj);
     iterator->position      = 0;
 
+    PHP_DS_INCR_ITERATOR((php_ds_vector_t *) iterator->object);
+
     // Add a reference to the object so that it doesn't get collected when
     // the iterated object is implict, eg. foreach ($obj->getInstance() as $value){ ... }
 #if PHP_VERSION_ID >= 70300
diff --git a/src/php/iterators/php_vector_iterator.h b/src/php/iterators/php_vector_iterator.h
index 380cc20..c3dffcb 100644
--- a/src/php/iterators/php_vector_iterator.h
+++ b/src/php/iterators/php_vector_iterator.h
@@ -6,8 +6,8 @@
 
 typedef struct php_ds_vector_iterator {
     zend_object_iterator     intern;
-    zend_object             *object;
-    ds_vector_t             *vector;
+    zend_object             *object; // TODO we can cast intern's std to php_ds_vector_t because offset is zero.
+    ds_vector_t             *vector; // TODO isn't this on the object above?
     zend_long                position;
 } php_ds_vector_iterator_t;
 
diff --git a/src/php/objects/php_deque.c b/src/php/objects/php_deque.c
index e8633bf..019ad46 100644
--- a/src/php/objects/php_deque.c
+++ b/src/php/objects/php_deque.c
@@ -7,9 +7,13 @@
 zend_object *php_ds_deque_create_object_ex(ds_deque_t *deque)
 {
     php_ds_deque_t *obj = ecalloc(1, sizeof(php_ds_deque_t));
+
     zend_object_std_init(&obj->std, php_ds_deque_ce);
-    obj->std.handlers = &php_deque_handlers;
-    obj->deque = deque;
+
+    obj->std.handlers  = &php_deque_handlers;
+    obj->deque         = deque;
+    obj->iteratorCount = 0;
+
     return &obj->std;
 }
 
diff --git a/src/php/objects/php_deque.h b/src/php/objects/php_deque.h
index 5c95cfe..6034f52 100644
--- a/src/php/objects/php_deque.h
+++ b/src/php/objects/php_deque.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_deque.h"
 
-#define Z_DS_DEQUE(z)   ((php_ds_deque_t*) Z_OBJ(z))->deque
+#define Z_DS_DEQUE_OBJ(z)   ((php_ds_deque_t*) (Z_OBJ(z)))
+#define Z_DS_DEQUE_OBJ_P(z) Z_DS_DEQUE_OBJ(*z)
+
+#define Z_DS_DEQUE(z)   ((Z_DS_DEQUE_OBJ(z))->deque)
 #define Z_DS_DEQUE_P(z) Z_DS_DEQUE(*z)
-#define THIS_DS_DEQUE() Z_DS_DEQUE_P(getThis())
+
+#define THIS_DS_DEQUE()     Z_DS_DEQUE_P(getThis())
+#define THIS_DS_DEQUE_OBJ() Z_DS_DEQUE_OBJ_P(getThis())
 
 #define ZVAL_DS_DEQUE(z, d)  ZVAL_OBJ(z, php_ds_deque_create_object_ex(d))
 
@@ -27,6 +32,7 @@ do {                                        \
 typedef struct php_ds_deque {
     zend_object  std;
     ds_deque_t  *deque;
+    zend_ulong   iteratorCount;
 } php_ds_deque_t;
 
 /**
diff --git a/src/php/objects/php_map.c b/src/php/objects/php_map.c
index 820f452..bdf19ab 100644
--- a/src/php/objects/php_map.c
+++ b/src/php/objects/php_map.c
@@ -7,9 +7,13 @@
 zend_object *php_ds_map_create_object_ex(ds_map_t *map)
 {
     php_ds_map_t *obj = ecalloc(1, sizeof(php_ds_map_t));
+
     zend_object_std_init(&obj->std, php_ds_map_ce);
-    obj->std.handlers = &php_map_handlers;
-    obj->map = map;
+
+    obj->std.handlers  = &php_map_handlers;
+    obj->map           = map;
+    obj->iteratorCount = 0;
+
     return &obj->std;
 }
 
diff --git a/src/php/objects/php_map.h b/src/php/objects/php_map.h
index 8d5ef0a..47218ae 100644
--- a/src/php/objects/php_map.h
+++ b/src/php/objects/php_map.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_map.h"
 
-#define Z_DS_MAP(z)   (((php_ds_map_t*)(Z_OBJ(z)))->map)
+#define Z_DS_MAP_OBJ(z)   ((php_ds_map_t*)(Z_OBJ(z)))
+#define Z_DS_MAP_OBJ_P(z) Z_DS_MAP_OBJ(*z)
+
+#define Z_DS_MAP(z)   ((Z_DS_MAP_OBJ(z))->map)
 #define Z_DS_MAP_P(z) Z_DS_MAP(*z)
-#define THIS_DS_MAP() Z_DS_MAP_P(getThis())
+
+#define THIS_DS_MAP()     Z_DS_MAP_P(getThis())
+#define THIS_DS_MAP_OBJ() Z_DS_MAP_OBJ_P(getThis())
 
 #define ZVAL_DS_MAP(z, map) ZVAL_OBJ(z, php_ds_map_create_object_ex(map))
 
@@ -23,6 +28,7 @@ do {                                        \
 typedef struct _php_ds_map_t {
     zend_object  std;
     ds_map_t    *map;
+    zend_ulong   iteratorCount;
 } php_ds_map_t;
 
 zend_object *php_ds_map_create_object_ex(ds_map_t *map);
diff --git a/src/php/objects/php_priority_queue.c b/src/php/objects/php_priority_queue.c
index 7fa50c0..4e90b6d 100644
--- a/src/php/objects/php_priority_queue.c
+++ b/src/php/objects/php_priority_queue.c
@@ -7,12 +7,14 @@
 zend_object *php_ds_priority_queue_create_object_ex(ds_priority_queue_t *queue)
 {
     php_ds_priority_queue_t *obj = ecalloc(1, sizeof(php_ds_priority_queue_t));
+
     zend_object_std_init(&obj->std, php_ds_priority_queue_ce);
-    obj->std.handlers = &php_priority_queue_handlers;
 
-    obj->queue   = queue;
-    obj->gc_data = NULL;
-    obj->gc_size = 0;
+    obj->std.handlers  = &php_priority_queue_handlers;
+    obj->queue         = queue;
+    obj->gc_data       = NULL;
+    obj->gc_size       = 0;
+    obj->iteratorCount = 0;
 
     return &obj->std;
 }
diff --git a/src/php/objects/php_priority_queue.h b/src/php/objects/php_priority_queue.h
index f7a38dc..8e71711 100644
--- a/src/php/objects/php_priority_queue.h
+++ b/src/php/objects/php_priority_queue.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_priority_queue.h"
 
-#define Z_DS_PRIORITY_QUEUE(z)   (((php_ds_priority_queue_t*)(Z_OBJ(z)))->queue)
+#define Z_DS_PRIORITY_QUEUE_OBJ(z)   ((php_ds_priority_queue_t*) (Z_OBJ(z)))
+#define Z_DS_PRIORITY_QUEUE_OBJ_P(z) Z_DS_PRIORITY_QUEUE_OBJ(*z)
+
+#define Z_DS_PRIORITY_QUEUE(z)   ((Z_DS_PRIORITY_QUEUE_OBJ(z))->queue)
 #define Z_DS_PRIORITY_QUEUE_P(z) Z_DS_PRIORITY_QUEUE(*z)
+
 #define THIS_DS_PRIORITY_QUEUE() Z_DS_PRIORITY_QUEUE_P(getThis())
+#define THIS_DS_PRIORITY_QUEUE_OBJ() Z_DS_PRIORITY_QUEUE_OBJ_P(getThis())
 
 #define ZVAL_DS_PRIORITY_QUEUE(z, queue) \
     ZVAL_OBJ(z, (php_ds_priority_queue_create_object_ex(queue)))
@@ -24,6 +29,7 @@ do {                                                    \
 typedef struct _php_ds_priority_queue_t {
     zend_object          std;
     ds_priority_queue_t *queue;
+    zend_ulong           iteratorCount;
     zval                *gc_data;
     int                  gc_size;
 
diff --git a/src/php/objects/php_queue.c b/src/php/objects/php_queue.c
index 7de656d..dcf4231 100644
--- a/src/php/objects/php_queue.c
+++ b/src/php/objects/php_queue.c
@@ -7,9 +7,12 @@
 zend_object *php_ds_queue_create_object_ex(ds_queue_t *queue)
 {
     php_ds_queue_t *obj = ecalloc(1, sizeof(php_ds_queue_t));
+
     zend_object_std_init(&obj->std, php_ds_queue_ce);
-    obj->std.handlers = &php_queue_handlers;
-    obj->queue = queue;
+
+    obj->std.handlers  = &php_queue_handlers;
+    obj->queue         = queue;
+    obj->iteratorCount = 0;
 
     return &obj->std;
 }
diff --git a/src/php/objects/php_queue.h b/src/php/objects/php_queue.h
index dc76385..a6506c2 100644
--- a/src/php/objects/php_queue.h
+++ b/src/php/objects/php_queue.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_queue.h"
 
-#define Z_DS_QUEUE(z)   (((php_ds_queue_t*)(Z_OBJ(z)))->queue)
+#define Z_DS_QUEUE_OBJ(z)   ((php_ds_queue_t*) (Z_OBJ(z)))
+#define Z_DS_QUEUE_OBJ_P(z) Z_DS_QUEUE_OBJ(*z)
+
+#define Z_DS_QUEUE(z)   ((Z_DS_QUEUE_OBJ(z))->queue)
 #define Z_DS_QUEUE_P(z) Z_DS_QUEUE(*z)
+
 #define THIS_DS_QUEUE() Z_DS_QUEUE_P(getThis())
+#define THIS_DS_QUEUE_OBJ() Z_DS_QUEUE_OBJ_P(getThis())
 
 #define ZVAL_DS_QUEUE(z, queue) \
     ZVAL_OBJ(z, php_ds_queue_create_object_ex(queue))
@@ -24,6 +29,7 @@ do {                                        \
 typedef struct _php_ds_queue_t {
     zend_object    std;
     ds_queue_t    *queue;
+    zend_ulong     iteratorCount;
 } php_ds_queue_t;
 
 zend_object *php_ds_queue_create_object_ex(ds_queue_t *queue);
diff --git a/src/php/objects/php_set.c b/src/php/objects/php_set.c
index 70d82c0..765335a 100644
--- a/src/php/objects/php_set.c
+++ b/src/php/objects/php_set.c
@@ -7,9 +7,13 @@
 zend_object *php_ds_set_create_object_ex(ds_set_t *set)
 {
     php_ds_set_t *obj = ecalloc(1, sizeof(php_ds_set_t));
+
     zend_object_std_init(&obj->std, php_ds_set_ce);
-    obj->std.handlers = &php_ds_set_handlers;
-    obj->set = set;
+
+    obj->std.handlers  = &php_ds_set_handlers;
+    obj->set           = set;
+    obj->iteratorCount = 0;
+
     return &obj->std;
 }
 
diff --git a/src/php/objects/php_set.h b/src/php/objects/php_set.h
index 8f94b33..ceaac38 100644
--- a/src/php/objects/php_set.h
+++ b/src/php/objects/php_set.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_set.h"
 
-#define Z_DS_SET(z)   (((php_ds_set_t*)(Z_OBJ(z)))->set)
+#define Z_DS_SET_OBJ(z)   ((php_ds_set_t*) (Z_OBJ(z)))
+#define Z_DS_SET_OBJ_P(z) Z_DS_SET_OBJ(*z)
+
+#define Z_DS_SET(z)   ((Z_DS_SET_OBJ(z))->set)
 #define Z_DS_SET_P(z) Z_DS_SET(*z)
+
 #define THIS_DS_SET() Z_DS_SET_P(getThis())
+#define THIS_DS_SET_OBJ() Z_DS_SET_OBJ_P(getThis())
 
 #define ZVAL_DS_SET(z, set) ZVAL_OBJ(z, php_ds_set_create_object_ex(set))
 
@@ -23,6 +28,7 @@ do {                                        \
 typedef struct _php_ds_set_t {
     zend_object    std;
     ds_set_t      *set;
+    zend_ulong     iteratorCount;
 } php_ds_set_t;
 
 zend_object *php_ds_set_create_object_ex(ds_set_t *set);
diff --git a/src/php/objects/php_stack.c b/src/php/objects/php_stack.c
index 8b2ac61..0bd97b4 100644
--- a/src/php/objects/php_stack.c
+++ b/src/php/objects/php_stack.c
@@ -7,9 +7,13 @@
 zend_object *php_ds_stack_create_object_ex(ds_stack_t *stack)
 {
     php_ds_stack_t *obj = ecalloc(1, sizeof(php_ds_stack_t));
+
     zend_object_std_init(&obj->std, php_ds_stack_ce);
-    obj->std.handlers = &php_ds_stack_handlers;
-    obj->stack = stack;
+
+    obj->std.handlers  = &php_ds_stack_handlers;
+    obj->stack         = stack;
+    obj->iteratorCount = 0;
+
     return &obj->std;
 }
 
diff --git a/src/php/objects/php_stack.h b/src/php/objects/php_stack.h
index c34934b..cb4285d 100644
--- a/src/php/objects/php_stack.h
+++ b/src/php/objects/php_stack.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_stack.h"
 
-#define Z_DS_STACK(z)   (((php_ds_stack_t*)(Z_OBJ(z)))->stack)
+#define Z_DS_STACK_OBJ(z)   ((php_ds_stack_t*) (Z_OBJ(z)))
+#define Z_DS_STACK_OBJ_P(z) Z_DS_STACK_OBJ(*z)
+
+#define Z_DS_STACK(z)   ((Z_DS_STACK_OBJ(z))->stack)
 #define Z_DS_STACK_P(z) Z_DS_STACK(*z)
+
 #define THIS_DS_STACK() Z_DS_STACK_P(getThis())
+#define THIS_DS_STACK_OBJ() Z_DS_STACK_OBJ_P(getThis())
 
 #define ZVAL_DS_STACK(z, s)  ZVAL_OBJ(z, php_ds_stack_create_object_ex(s))
 
@@ -23,6 +28,7 @@ do {                                        \
 typedef struct _php_ds_stack_t {
     zend_object      std;
     ds_stack_t      *stack;
+    zend_ulong       iteratorCount;
 } php_ds_stack_t;
 
 zend_object *php_ds_stack_create_object_ex(ds_stack_t *stack);
diff --git a/src/php/objects/php_vector.c b/src/php/objects/php_vector.c
index f415c43..d1ce490 100644
--- a/src/php/objects/php_vector.c
+++ b/src/php/objects/php_vector.c
@@ -7,9 +7,12 @@
 zend_object *php_ds_vector_create_object_ex(ds_vector_t *vector)
 {
     php_ds_vector_t *obj = ecalloc(1, sizeof(php_ds_vector_t));
+
     zend_object_std_init(&obj->std, php_ds_vector_ce);
-    obj->std.handlers = &php_vector_handlers;
-    obj->vector = vector;
+
+    obj->std.handlers  = &php_vector_handlers;
+    obj->vector        = vector;
+    obj->iteratorCount = 0;
 
     return &obj->std;
 }
diff --git a/src/php/objects/php_vector.h b/src/php/objects/php_vector.h
index 63b4b7f..9d28706 100644
--- a/src/php/objects/php_vector.h
+++ b/src/php/objects/php_vector.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_vector.h"
 
-#define Z_DS_VECTOR(z)   (((php_ds_vector_t*)(Z_OBJ(z)))->vector)
+#define Z_DS_VECTOR_OBJ(z)   ((php_ds_vector_t*) (Z_OBJ(z)))
+#define Z_DS_VECTOR_OBJ_P(z) Z_DS_VECTOR_OBJ(*z)
+
+#define Z_DS_VECTOR(z)   ((Z_DS_VECTOR_OBJ(z))->vector)
 #define Z_DS_VECTOR_P(z) Z_DS_VECTOR(*z)
+
 #define THIS_DS_VECTOR() Z_DS_VECTOR_P(getThis())
+#define THIS_DS_VECTOR_OBJ() Z_DS_VECTOR_OBJ_P(getThis())
 
 #define ZVAL_DS_VECTOR(z, v) ZVAL_OBJ(z, php_ds_vector_create_object_ex(v))
 
@@ -23,6 +28,7 @@ do {                                        \
 typedef struct php_ds_vector {
     zend_object      std;
     ds_vector_t     *vector;
+    zend_ulong       iteratorCount;
 } php_ds_vector_t;
 
 zend_object *php_ds_vector_create_object_ex(ds_vector_t *vector);

From 9f5d5d7207afeb20e86701dd3a8bf9b1a76c67a8 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Fri, 1 Feb 2019 17:53:41 -0800
Subject: [PATCH 02/50] Add 2.0 API draft

---
 DRAFT.php | 871 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 871 insertions(+)
 create mode 100644 DRAFT.php

diff --git a/DRAFT.php b/DRAFT.php
new file mode 100644
index 0000000..bb2098f
--- /dev/null
+++ b/DRAFT.php
@@ -0,0 +1,871 @@
+<?php
+/*
+|--------------------------------------------------------------------------
+| PHP Data Structures - API Draft - v2.0
+|
+| https://pecl.php.net/package/ds
+|
+|--------------------------------------------------------------------------
+|
+| This file is will be used for planning the API of the next major version of
+| the data structures extension. Please feel free to suggest modifications.
+*/
+namespace Ds;
+
+
+/**
+ * Base exception class, to allow catching any library-related exception.
+ */
+class Exception extends \Exception {}
+
+
+/**
+ * Should be thrown when a structure is accessed unexpectedly, or in a way that
+ * is undefined, or to avoid ambiguous results.
+ */
+class AccessException extends Exception {}
+
+
+/**
+ * Indicates that the order of the elements of a structure can be reversed.
+ */
+interface Reversable
+{
+    /**
+     * @return static A reversed copy or this structure reversed in-place.
+     */
+    function reverse();
+}
+
+
+/**
+ * Indicates that the elements of a structure can be sorted.
+ *
+ * This interface does not guarantee that the sorting algorithm will be stable.
+ * Implementations should use a domain-specific tiebreaker when required.
+ */
+interface Sortable
+{
+    /**
+     * @return static A sorted copy or this structure sorted in-place.
+     */
+    function sort(callable $comparator = null);
+}
+
+
+/**
+ * Indicates that the elements of a structure can be represented by an array
+ * without altering the structure of the data, without any warnings or errors.
+ */
+interface Arrayable
+{
+    function toArray(): array;
+}
+
+
+/**
+ * Indicates that a structure can be duplicated without loss of data.
+ */
+interface Cloneable
+{
+    /**
+     * @return static a copy of this structure, consistent with `clone`.
+     */
+    function clone();
+}
+
+
+/**
+ * Indicates that a structure can be cleared.
+ */
+interface Clearable
+{
+    function clear();
+}
+
+
+/**
+ * Indicates that a structure can be accessed using a zero-based integer index.
+ *
+ * @todo Maybe a better name? Hints at linear-time access a bit.
+ */
+interface LinearAccess
+{
+    /**
+     * @return mixed The first element, which could be a key or value.
+     *               Implementations should specify what happens when there are
+     *               no elements (eg. return NULL or throw AccessException).
+     */
+    function first();
+
+    /**
+     * @return mixed The last element, which could be a key or value.
+     *               Implementations should specify what happens when there are
+     *               no elements (eg. return NULL or throw AccessException).
+     */
+    function last();
+
+    /**
+     * @return mixed The value at the
+     *
+     * @throws AccessException if the index is out of range.
+     *
+     * @todo Potential for a better name, like "skip" or "atIndex"?
+     *
+     * @todo We could change this to `get`, but that conflicts with structures
+     *       like HashMap in which `get` does a table lookup by key. If we do
+     *       want to change this to `get`, it would mean that HashMap can't
+     *       implement it, which may not be such a bad thing? Feels _slightly_
+     *       odd for HashMap to implement linear access, but because it already
+     *       does (similar internal structure as arrays, insertion order pres.),
+     *       it's an easy value-add at no cost. Will need some help with this.
+     */
+    function at(int $index);
+
+    /**
+     * @return int the index of the value, or NULL if it could not be found.
+     */
+    function indexOf($value): ?int;
+}
+
+
+/**
+ * Indicates that a structure contains elements and is aware of the number of
+ * items contained at any point in time. How a container must determine the
+ * count is implementation-specific.
+ *
+ * This interface does not imply that a structure is traversable.
+ */
+interface Container extends \Countable
+{
+    /**
+     * @return bool whether the container is empty, which can be TRUE even when
+     *              the number of elements is non-zero.
+     */
+    function isEmpty(): bool;
+}
+
+
+/**
+ * Indicates that a structure is designed to process all elements to produce a
+ * new value as a result.
+ *
+ * @todo this feels like it belongs in an iterator library, but we get some nice
+ *       wins doing these internally, because we have low-level access to the
+ *       structures without the need to invoke iterators or class lookups to
+ *       determine the type of the result (for cases like map, filter etc).
+ *       How else would you know how to create the instance? Clone?
+ */
+interface Collection
+{
+
+    /**
+     * @param callable $callback Called with value and key, should return what
+     *                           should replace the value. Keys are preserved.
+     *
+     * @return static A copy of $this with each value replaced by the result of
+     *                called the callback with that value (and its index/key).
+     *                Keys are always preserved.
+     */
+    function map(callable $callback): Collection;
+
+    /**
+     * @param callback $predicate Called with value and key
+     *
+     * @param mixed|null $initial The initial value of the result argument.
+     *
+     * @return mixed The final value of the result.
+     */
+
+    function filter(callable $predicate): Collection;
+
+    /**
+     * Reduces the collection to a single value using a callback function
+     *
+     * @param callback $callback Called with the current value of the result and
+     *                           the value and key of the current iteration.
+     *                           The result is set to $initial at the start.
+     *
+     * @param mixed|null $initial The initial value of the result argument.
+     *
+     * @return mixed The final value of the result.
+     */
+    function reduce(callable $callback, $initial = null);
+
+    /**
+     * @return bool TRUE if empty pr the predicate returns TRUE for any element
+     *              in the collection, FALSE otherwise.
+     *
+     * @todo Many other implementations (lodash, js) use `some` here, but I have
+     *       always liked the phrasing of "any" and "all". PHP doesn't have an
+     *       array equivalent for these anyway, so we have no reference for
+     *       consistency. Always be innovating, or accept the convention?
+     */
+    function any(callable $predicate): bool;
+
+    /**
+     * @return bool TRUE if not empty and the predicate returns TRUE for all
+     *              elements in the collection, FALSE otherwise.
+     */
+    function all(callable $predicate): bool;
+
+    /**
+     * @return string The result of concatenating the string representations of
+     *                all values, separated by the given $glue.
+     */
+    function join(string $glue = ""): string;
+}
+
+
+/**
+ * Indicates that a structure contains a series of contiguous elements.
+ */
+interface Sequence
+{
+    /**
+     * @return mixed The value at the given position.
+     *
+     * @throws AccessException if the index is out of range [0, size)
+     */
+    function get(int $index);
+
+    /**
+     * Sets the value at the given position.
+     *
+     * @throws AccessException if the index is out of range [0, size)
+     */
+    function set(int $index, $value);
+
+    /**
+     * Removes the value at the given position, moving all successive
+     * values one position towards the front.
+     *
+     * @throws AccessException if the index is out of range [0, size)
+     */
+    function unset(int $index);
+
+    /**
+     * Moves all values between the given index and the end of the sequence one
+     * position towards the back, then inserts the given value into the gap.
+     *
+     * @throws AccessException if the index is out of range [0, size]
+     */
+    function insert(int $index, $value);
+}
+
+
+/**
+ * Indicates that a structure is designed to operate efficiently at either end.
+ */
+interface Deque
+{
+    /**
+     * Appends the given value to the end of the structure.
+     */
+    function push($value);
+
+    /**
+     * Removes and returns the value at the end of the structure.
+     *
+     * @return mixed The value that was removed from the end of the structure.
+     *
+     * @throws AccessException if the structure is empty.
+     *
+     * @todo should we leave it up to the impl to throw or return NULL? I think
+     *       we should aim for consistency by requiring a throw when empty. This
+     *       should be consistent with LinearAccess' first and last.
+     */
+    function pop();
+
+    /**
+     * Prepends the given value to the front of the structure.
+     */
+    function unshift($value);
+
+    /**
+     * Removes and returns the first value of the structure.
+     *
+     * @return mixed The value that was removed from the front of the structure.
+     */
+    function shift();
+}
+
+/**
+ * A first-in-last-out structure.
+ *
+ * @todo Should stacks be iterable, and if so, do they have to be destructive?
+ *
+ *       It gets tricky because stacks yield from the back as values as popped
+ *       off the stack, but implementing structures like a Vector would iterate
+ *       from the front, which would not be intuitive at all. Perhaps the best
+ *       solution here is that implementations must iterate in reverse.
+ *
+ *       We could potentially do some internal magic to force reverse iteration?
+ *       (Override iterator creation when a userland object implements Stack)
+ *
+ * @todo If we decide that this interface must be traversed in FILO order, then
+ *       Vector can not implement it. We could instead make this a final class,
+ *       using a vector internally? A stack is a stack, no?
+ *
+ *       Vector would need to find `push` and `pop` elsewhere, which are both
+ *       constant time operations at the end of the buffer. LinkedList has these
+ *       too, but they come as part of Deque, which Vector does not implement.
+ *
+ *       Maybe Deque can be a composite of two separate interfaces? Or we make
+ *       `push` and `pop` part of Sequence? That doesn't feel quite right because
+ *       a sequence of values does not necessarily imply that end-ops are O(1).
+ *
+ *
+ *
+ *
+ */
+interface Stack
+{
+    function push($value);
+
+    /**
+     * @todo What happens when the stack is empty? AccessException?
+     *       We need a way to ensure that a stack can know when it is empty,
+     *       otherwise we can't implement iteration:
+     *
+     *           while (!$this->isEmpty()) {
+     *               yield $this->pop();
+     *           }
+     *
+     *       Maybe Stack can extend Container? Or at least Countable? What does
+     *       that mean for other structures that could extend Container? We can
+     *       define a policy like "only extend core interfaces" but I'm not sure
+     *       that that will always be wise. I don't see an issue with extension,
+     *       but I wanted to attept composition first and see how far we get.
+     *
+     *       We could also extend IteratorAggregate here and require that it
+     *       returns an iterator that is seekable which we can wrap, or simply
+     *       an instance of a reverse iterator interface? Or Stack can extend
+     *       something like "ReverseIterator"?
+     *
+     *       I think we should avoid IteratorAggregate as an unnecessary layer.
+     *       We could create our own interface which has methods like end(),
+     *       prev() and valid()? Create the iterator internally when implemented.
+     *
+     *       I'm starting to see more and more iterator-domain stuff creeping in
+     *       here. Is it counter-intuitive to separate iterators from ds? Why
+     *       can't iterators be considered data structures? Or why can't a ds
+     *       library also provide some iterators that are designed to work with
+     *       the structures. @Levi I think this is what you meant by external
+     *       iterators, so potentially on the same page here?
+     */
+    function pop();
+
+    function peek();
+}
+
+/**
+ * A first-in-first-out structure.
+ *
+ * @todo Should queues be iterable, and if so, do they have to be destructive.
+ *
+ * @todo If Stack becomes a class, should Queue remain an interface?
+ */
+interface Queue
+{
+    function push($value);
+
+    /**
+     * See notes on Stack::pop -^
+     */
+    function pop();
+
+    function peek();
+}
+
+/**
+ * Indicates that a structure is designed to quickly determine whether a given
+ * value is already contained by it.
+ *
+ * Note: The intention will be that all implementations of Set will use a
+ *       do_operation handler to override |, ^, -, &.
+ *
+ * @todo I'm not convinced that we need the four set operation methods, but we
+ *       could assume that most applications that use Set will want to remove
+ *       values from another set, or determine an intersection at some stage.
+ *       I would rather implement them here, and maybe with operator support,
+ *       and make use of the advantages we have internally.
+ *
+ *       We could also implement a set operation interface, which provides the
+ *       four methods and operator overloading, but that would mean that not all
+ *       implementations of Set support set operations.
+ *
+ *       What does this mean for Map? Should we not support the same use cases
+ *       as array_key_intersect? If Set has these operations, should Map have
+ *       methods like `diff`, `intersect`, etc to be consistent with arrays?
+ *       If so, should the set operation methods share the same naming?
+ */
+interface Set
+{
+    /**
+     * Adds the given value to $this set if it is not already in $this set.
+     */
+    function add($value);
+
+    /**
+     * @todo what happens when the value could not be found? Silent no-op?
+     */
+    function remove($value);
+
+    /**
+     * @return bool TRUE if the value is in $this set, FALSE otherwise.
+     */
+    function has($value): bool;
+
+    /**
+     * @return Set A set from all values in $this, and all values from $other.
+     *
+     * @todo Naming - or, union, merge
+     */
+    function or(Set $other): Set;
+
+    /**
+     * @return Set A set from values in either $this or $other, but not in both.
+     */
+    function xor(Set $other): Set;
+
+    /**
+     * @return Set A set from values in $this that are not in $other.
+     *
+     * @todo Naming - not, diff, without
+     */
+    function not(Set $other): Set;
+
+    /**
+     * @return Set A set from values in $this that are not in $other.
+     *
+     * @todo Naming - and, intersect
+     */
+    function and(Set $other): Set;
+}
+
+
+/**
+ * A structure that associates one value with another and provides the ability
+ * to query or adjust associations efficiently.
+ */
+interface Map
+{
+    /**
+     * Associates the $key with the $value, overriding any previous association.
+     *
+     * Note: Might not be able to support NULL keys. (See `find`)
+     *
+     * @todo Naming - set/put (set/unset symmetry, add/remove, put/pull?)
+     */
+    function set($key, $value);
+
+    /**
+     * @return mixed The value associated with the $key, or $default if the key
+     *               could not be found and a $default was provided.
+     *
+     * @throws AccessException if the $key was not found with default given.
+     */
+    function get($key, $default = null);
+
+    /**
+     * Removes the given $key from the map, and does nothing if they key could
+     * not be found.
+     *
+     * @todo Should we return the removed value? (No, ambiguous and two-in-one)
+     *
+     * @todo Key not found: should we throw, do nothing, or not enforce either?
+     */
+    function unset($key);
+
+    /**
+     * @return bool TRUE if $this map has an association for the given $key,
+     *              FALSE otherwise.
+     *
+     * @todo "This is ambiguous! 'has' ...what? has key or has value?"
+     *       What about `$map->keys()->has(x)` or `$map->values()->has(x)` if we
+     *       can achieve that without intermediaries? That would make the `has`
+     *       here a bit redundant, where we would not get much value for the
+     *       cost of the ambiguity. The other alternative is hasKey + hasValue,
+     *       but that feels more specific than it needs to be, or just me?
+     *
+     *       This also has a very "set" vibe to it. What other structures have a
+     *       `has` as part of its semantics? Lists usually opt for `contains` or
+     *       have `indexOf` return something nonesense to indicate no result.
+     *
+     *       "What if we implement Set" would be weird because we would also get
+     *       `add` and `remove` and all the potential set operations. But is it
+     *       that bad really? The `add`, yes. The `remove`, a bit yes (ambig.)
+     *       But we want the `has` and we don't care for the operations much.
+     *       So we could extract `has` to another interface which Set extends,
+     *       allowing us to implement that here?
+     *
+     *       Or we _do_ care about the set operations, if we can get the naming
+     *       right we can also intersect values, diff, etc. Works for arrays.
+     *       In that case the Set interface only has `add` and `remove` that
+     *       are throwing us off. We could move `add` and `remove` to another
+     *       interface like a `Bag` or merge into `Container`?
+     */
+    function has($key): bool;
+
+    /**
+     * @return mixed The first key that is associated with the given value, or
+     *               NULL if the value could not be found.
+     *
+     * @todo This could potentially double as a `has`, just like `indexOf` can.
+     *
+     * @todo The NULL return here forces the contraint that maps can't have NULL
+     *       values, which implementations should enforce themselves. Ideally we
+     *       should be able to do reverse lookups like these, but we have no way
+     *       to indicate "no key" without being ambiguous.
+     *
+     *       We could choose not to support lookups by value. It's a very simple
+     *       thing to implement (foreach, if value equal value, return key), so
+     *       there wouldn't be a large capability gap. We could instead look at
+     *       bi-directional later on? Or leave this to iteration-based projects.
+     *
+     * @todo Naming - find, lookup,
+     */
+    function find($value);
+
+    /**
+     * @return Set All keys from the map added in traversal order.
+     *
+     * @todo We have to come up with a clever way to make this lazy, so we can
+     *       expose a view on the structure's keys rather than an eager copy.
+     */
+    function keys(): Set;
+
+    /**
+     * @return Set All keys from the map added in traversal order.
+     *
+     * @todo We have to come up with a clever way to make this lazy, so we can
+     *       expose a view on the structure's values rather than an eager copy.
+     */
+    function values(): Sequence;
+
+    /**
+     * @return Map The result of adding all associations from $map to $this.
+     *
+     * @todo Feels a bit isolated here - isn't this part of the set operations
+     *       discussion? If so, this is already the equivalent of or/union, so
+     *       it is probably smart to consider unified naming that applies to the
+     *       semantics of both structures.
+     */
+    function merge(Map $map): Map;
+}
+
+
+/**
+ * A low-level memory allocation abstraction which could be useful to build
+ * structures in userland that do not want to use arrays or other structures
+ * as a buffer.
+ *
+ * We have to be very careful to not go too far with this. Memory access should
+ * be extremely well guarded here. We MUST have a sensible maximum capacity that
+ * can be allocated, so that we do not expose the potential to crash anything.
+ *
+ * @todo is it safe to use undefined or should we set all elements to NULL?
+ *       or we can convert to NULL when undefined is read? Would be strange to
+ *       expose undefined to userland. Attempting to read an undefined value is
+ *       almost certainly a bug though, so we could throw an exception if this
+ *       happens rather than magically converting it to NULL (which would also
+ *       make it ambiguous because you can't distinguish between null and undef)
+ *
+ * @todo What should clear do? I think that capacity should be preserved, but
+ *       all values should be set the their initial state, ie. undefined
+ */
+final class Allocation implements \ArrayAccess, Cloneable, Clearable
+{
+    public function __construct(int $capacity) {}
+
+    // capacity
+    // realloc
+
+    // clone
+    // clear
+}
+
+/**
+ * Basic list, dynamic size, contiguous, no ops at the front.
+ */
+final class Vector implements \ArrayAccess, Container, Cloneable, Clearable, Sortable, Reversable, LinearAccess, Collection, Sequence, Stack
+{
+    // get
+    // set
+    // unset
+    // insert
+
+    // push
+    // pop
+    // peek
+
+    // map
+    // reduce
+    // filter
+    // any
+    // all
+    // join
+
+    // at
+    // indexOf
+    // first
+    // last
+
+    // sort
+    // reverse
+
+    // clone
+    // clear
+    // count
+    // isEmpty
+}
+
+/**
+ * Not necessarily literally a linked list, but has the same theoretical props.
+ *
+ * Very similar to vector, but does support operations at the front. Will most
+ * likely be implemented as a circular buffer / "deque", but is an important
+ * distinction because the go-to will be a Vector, except when you want to do
+ * operations at the front, in which case you know you must use a LinkedList.
+ *
+ * This could be a bit annoying when you have `Sequence` throughout your app but
+ * you really just want to remove the first element in a rare special case. You
+ * have to either be able to slice from an index, or shift. Your Sequence is no
+ * good because it can't be shifted. You would have to use the Deque interface
+ * which Vectors don't support, so you go all the way up the chain to create a
+ * LinkedList. This is either fine, or we should allow Vector to be a Deque also,
+ * which enables poor performance and raises the question of "when should I use
+ * LinkedList rather than Vector?"
+ *
+ * This structure (the deque, not the list) is my favourite and it would be hard
+ * to delete all the work that was put into ext-ds. I believe that it is a core
+ * structure because nothing else provides constant time add and remove at both
+ * ends of the buffer. If we can agree that this structure has a place, all we
+ * have to do is present it clearly. A linked list is fundamental, easy to under-
+ * stand, and communicates the behaviour of the structure. If there is any
+ * objection to effectively lying about the name, we should come up with another,
+ * like... *no ideas*
+ */
+final class LinkedList implements \ArrayAccess, Container, Cloneable, Clearable, Sortable, Reversable, LinearAccess, Collection, Sequence, Stack, Deque
+{
+    // get
+    // set
+    // unset
+    // insert
+
+    // push
+    // pop
+    // peek
+
+    // shift
+    // unshift
+
+    // map
+    // reduce
+    // filter
+    // any
+    // all
+    // join
+
+    // at
+    // indexOf
+    // first
+    // last
+
+    // sort
+    // reverse
+
+    // clone
+    // clear
+    // count
+    // isEmpty
+}
+
+/**
+ * The standard hash table, implemented as Map in ext-ds currently. It is modelled
+ * after the PHP array but is not identical. We can implement LinearAccess because
+ * insertion order is preserved. I don't believe we need a LinkedMap or anything
+ * like that because the existing implementation is already very, very good. I did
+ * experiment with open addressing for  a long time but I could not out-perform
+ * the existing model.
+ *
+ * Keys can be any scalar value (@todo potentially not NULL though), or object
+ * that implements Hashable. I don't think we should support object keys that do
+ * not implement Hashable, which is a big side-step from ext-ds currently which
+ * falls back to spl_object_hash. If non-hashable objects must be used as keys,
+ * the implementation should use a BST instead, which does not rely on hashing.
+ *
+ * Alternatively, we could impelement a SortedMap using BST internally, which
+ * removes the need for hashing and has the added benefit of sorted keys. This
+ * could pretty easily be implemented in userland because it's such a simple
+ * composition - I'm not convinced that we need to implement it internally. A
+ * counter argument might be that we should standardise here while we can,
+ * provide a dedicated implementation of a map that does not require hashing,
+ * and is sorted by key. We could also accept an optional comparator.
+ *
+ * Something to consider is a SortedMap interface, implemented as TreeMap,
+ * Java style, but I think a SortedMap implementation is sufficient. I can't
+ * imagine many valid alternative implementations. We could also call the impl
+ * TreeMap and not have "SortedMap" be anything at all.
+ */
+final class HashMap implements \ArrayAccess, Container, Cloneable, Clearable, Sortable, Reversable, LinearAccess, Collection, Map
+{
+    // get
+    // set/put <Hashable | scalar, *>
+    // unset
+    // has
+    // find (null if value not found)
+
+    // keys
+    // values
+
+    // merge
+
+    // map
+    // reduce
+    // filter
+    // any
+    // all
+    // join
+
+    // at
+    // indexOf
+    // first
+    // last
+
+    // sort
+    // reverse
+
+    // clone
+    // clear
+    // count
+    // isEmpty
+}
+
+/**
+ * The Set equivalent of HashMap.
+ */
+final class HashSet implements \ArrayAccess, Container, Cloneable, Clearable, Sortable, Reversable, LinearAccess, Collection, Set
+{
+    // add <Hashable | scalar>
+    // remove
+    // has
+
+    // or
+    // xor
+    // not
+    // and
+
+    // map
+    // reduce
+    // filter
+    // any
+    // all
+    // join
+
+    // at
+    // indexOf
+    // first
+    // last
+
+    // sort
+    // reverse
+
+    // clone
+    // clear
+    // count
+    // isEmpty
+}
+
+/**
+ * Binary heap-based queue with a mixed-type priority value associated with the
+ * key. Iteration is destructive because that is how a heap works, and matches
+ * the declared behaviour by Queue (destructive iteration).
+ *
+ * @todo Should we provide the ability to adjust the priority of a value? I think
+ *       it would be O(N) in a heap, and introduces ambiguity around duplicate
+ *       values. Should it adjust them all, or just the first?
+ *
+ * @todo Should we support duplicate values at all?
+ *
+ * @todo Why do we need this if we already have a heap?
+ *       Heap supports a comparator, and is guaranteed to be stable. However,
+ *       in order to impelement Queue we would HAVE to pass a callable that
+ *       always returns 0, falling back to insertion order tiebreak. This feels
+ *       hacky, and I think the semantics are stronger with a dedicated type.
+ *       In other words it should follow insertion order if no comparator was
+ *       given, which Heap would not do.
+ */
+final class PriorityQueue implements Iterator, Container, Cloneable, Clearable, Queue
+{
+    // push (value, priority = null)
+    // pop
+    // peek
+
+    // clone
+    // clear
+    // count
+    // isEmpty
+}
+
+/**
+ * A queue implementation that does not require hashing, a heap, or comparison.
+ *
+ * @todo This will be based on LinkedList, but LinkedList can't implement Queue
+ *       because it might have to implement destructive traversal. There are
+ *       some notes on Stack and Queue that would need to be resolved before we
+ *       can decide whether this structure is necessary at all.
+ */
+final class LinkedQueue implements Container, Cloneable, Clearable, Queue
+{
+    // push
+    // pop
+    // peek
+
+    // clone
+    // clear
+    // count
+
+    // isEmpty
+}
+
+/**
+ * A structure that is always sorted and does not allow duplicate elements.
+ *
+ * See: https://github.com/php-ds/ext-ds/issues/121
+ */
+final class BinarySearchTree implements Iterator, Container, Cloneable, Clearable
+{
+    // add
+    // remove
+    // has
+
+    // clone
+    // clear
+    // count
+    // isEmpty
+
+    public function inorder() : Iterator {}   // <-- default iterator?
+    public function preorder() : Iterator {}
+    public function postorder() : Iterator {}
+}
+
+/**
+ * Stable binary heap with an optional comparator, defaulting to minimum. Could
+ * potentially remove the nullable which clears up the min/max ambiguity.
+ */
+final class Heap implements Iterator, Container, Cloneable, Clearable
+{
+    public function __construct(callable $comparator = null) {}
+
+    // push
+    // pop
+    // peek
+
+    // clone
+    // clear
+    // count
+    // isEmpty
+}
+

From ce9757bc37b293aef9ad4f29e84f87cb6850529c Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Fri, 1 Feb 2019 18:23:41 -0800
Subject: [PATCH 03/50] Fix bad copy/paste in Collection::filter

---
 DRAFT.php | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index bb2098f..bd2d381 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -88,6 +88,7 @@ function clear();
  * Indicates that a structure can be accessed using a zero-based integer index.
  *
  * @todo Maybe a better name? Hints at linear-time access a bit.
+ *       OffsetAccess, OrdinalAccess, ...
  */
 interface LinearAccess
 {
@@ -170,11 +171,13 @@ interface Collection
     function map(callable $callback): Collection;
 
     /**
-     * @param callback $predicate Called with value and key
+     * @param callback $predicate Called with value and key, determines whether
+     *                            the value should be includedin the result.
+     *                            Should return TRUE if the value should be
+     *                            included, FALSE otherwise. Keys are preserved.
      *
-     * @param mixed|null $initial The initial value of the result argument.
-     *
-     * @return mixed The final value of the result.
+     * @return Collection A collection of the same type, containing all keys and
+     *                    values for which the predicate returned TRUE.
      */
 
     function filter(callable $predicate): Collection;
@@ -868,4 +871,3 @@ public function __construct(callable $comparator = null) {}
     // count
     // isEmpty
 }
-

From b2877a04c706dfefced98ab440c49670eb3cf805 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Fri, 1 Feb 2019 18:26:35 -0800
Subject: [PATCH 04/50] Add missing space / typo

---
 DRAFT.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/DRAFT.php b/DRAFT.php
index bd2d381..d372adf 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -172,7 +172,7 @@ function map(callable $callback): Collection;
 
     /**
      * @param callback $predicate Called with value and key, determines whether
-     *                            the value should be includedin the result.
+     *                            the value should be included in the result.
      *                            Should return TRUE if the value should be
      *                            included, FALSE otherwise. Keys are preserved.
      *

From 939a12dac2bde5f713b65b62307fe2e9c264f101 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Fri, 1 Feb 2019 19:14:25 -0800
Subject: [PATCH 05/50] Mention variadics and constructor syntax

---
 DRAFT.php | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/DRAFT.php b/DRAFT.php
index d372adf..c8dbbe1 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -251,6 +251,13 @@ function unset(int $index);
      * Moves all values between the given index and the end of the sequence one
      * position towards the back, then inserts the given value into the gap.
      *
+     * @todo "How do I insert the values of one sequence into another?"
+     *       I think we should consider variadic functions across the board,
+     *       so that we can do things like `$seq->insert(5, ...$other)` instead
+     *       of userland forloops passing a single argument every time.
+     *
+     * @todo We should also consider constructor syntax, like `new Vector(...?)`
+     *
      * @throws AccessException if the index is out of range [0, size]
      */
     function insert(int $index, $value);

From 95eab19a777d79774063d053947f4258821b4078 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Fri, 1 Feb 2019 19:50:02 -0800
Subject: [PATCH 06/50] Add Rust references for any and all

---
 DRAFT.php | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index c8dbbe1..e47c305 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -196,19 +196,23 @@ function filter(callable $predicate): Collection;
     function reduce(callable $callback, $initial = null);
 
     /**
-     * @return bool TRUE if empty pr the predicate returns TRUE for any element
+     * @return bool TRUE if not empty and the predicate returns TRUE for any element
      *              in the collection, FALSE otherwise.
      *
      * @todo Many other implementations (lodash, js) use `some` here, but I have
      *       always liked the phrasing of "any" and "all". PHP doesn't have an
      *       array equivalent for these anyway, so we have no reference for
      *       consistency. Always be innovating, or accept the convention?
+     *
+     *       Rust refers to these as "any" and "all" too:
+     *         - https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any
+     *         - https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all
      */
     function any(callable $predicate): bool;
 
     /**
      * @return bool TRUE if not empty and the predicate returns TRUE for all
-     *              elements in the collection, FALSE otherwise.
+     *              elements in the collection, otherwise FALSE.
      */
     function all(callable $predicate): bool;
 

From 933362c871eba21d0e6259ceee652ebff29dff63 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 9 Feb 2019 02:00:21 -0800
Subject: [PATCH 07/50] Rewrite, remove collection methods

---
 DRAFT.php | 599 ++++++++++++++++++------------------------------------
 1 file changed, 197 insertions(+), 402 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index c8dbbe1..fc6827f 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -14,43 +14,31 @@
 
 
 /**
- * Base exception class, to allow catching any library-related exception.
+ * Should be thrown when a structure is accessed unexpectedly, or in a way that
+ * is undefined, or to avoid ambiguous results. This is a marker interface to
+ * support catching all access exceptions.
  */
-class Exception extends \Exception {}
+interface AccessException {}
 
 
 /**
- * Should be thrown when a structure is accessed unexpectedly, or in a way that
- * is undefined, or to avoid ambiguous results.
+ * Should be thrown when an index or key is not within the given access bounds
+ * of a structure, such as a negative index on a list or
  */
-class AccessException extends Exception {}
+class IndexOutOfBoundsException extends \OutOfBoundsException implements AccessException  {}
 
 
 /**
- * Indicates that the order of the elements of a structure can be reversed.
+ * Should be thrown when an empty container is accessed a clear, obvious result.
  */
-interface Reversable
-{
-    /**
-     * @return static A reversed copy or this structure reversed in-place.
-     */
-    function reverse();
-}
+class EmptyContainerException extends \LogicException implements AccessException {}
 
 
 /**
- * Indicates that the elements of a structure can be sorted.
- *
- * This interface does not guarantee that the sorting algorithm will be stable.
- * Implementations should use a domain-specific tiebreaker when required.
+ * Should be thrown when an undefined zval is accessed. I do not except any user
+ * classes to throw this, but it might be useful to catch it.
  */
-interface Sortable
-{
-    /**
-     * @return static A sorted copy or this structure sorted in-place.
-     */
-    function sort(callable $comparator = null);
-}
+class UndefinedValueException extends \LogicException implements AccessException {}
 
 
 /**
@@ -64,54 +52,55 @@ function toArray(): array;
 
 
 /**
- * Indicates that a structure can be duplicated without loss of data.
+ * Indicates that a structure can be cleared.
  */
-interface Cloneable
+interface Clearable
 {
-    /**
-     * @return static a copy of this structure, consistent with `clone`.
-     */
-    function clone();
+    function clear();
 }
 
 
 /**
- * Indicates that a structure can be cleared.
+ * Indicates that the order of the elements of a structure can be reversed.
  */
-interface Clearable
+interface Reversable
 {
-    function clear();
+    /**
+     * @return static A reversed copy or this structure reversed in-place.
+     */
+    function reverse();
 }
 
 
 /**
- * Indicates that a structure can be accessed using a zero-based integer index.
+ * Indicates that the elements of a structure can be sorted.
  *
- * @todo Maybe a better name? Hints at linear-time access a bit.
- *       OffsetAccess, OrdinalAccess, ...
+ * This interface does not guarantee that the sorting algorithm will be stable.
+ * Implementations should use a domain-specific tiebreaker when required.
  */
-interface LinearAccess
+interface Sortable
 {
     /**
-     * @return mixed The first element, which could be a key or value.
-     *               Implementations should specify what happens when there are
-     *               no elements (eg. return NULL or throw AccessException).
+     * @return static A sorted copy, or $this structure sorted in-place.
      */
-    function first();
+    function sort(callable $comparator = null);
+}
 
-    /**
-     * @return mixed The last element, which could be a key or value.
-     *               Implementations should specify what happens when there are
-     *               no elements (eg. return NULL or throw AccessException).
-     */
-    function last();
 
+/**
+ * Indicates that a structure can be accessed using a zero-based integer index
+ * indicating the position of an element from the beginning of the structure.
+ *
+ * @todo Naming - OrdinalAccess, LinearAccess, RandomAccess...
+ */
+interface OffsetAccess
+{
     /**
      * @return mixed The value at the
      *
-     * @throws AccessException if the index is out of range.
+     * @throws IndexOutOfBoundsException if the index is not within [0, size).
      *
-     * @todo Potential for a better name, like "skip" or "atIndex"?
+     * @todo Potential for a better name, like "skip" or "atIndex" or "at"?
      *
      * @todo We could change this to `get`, but that conflicts with structures
      *       like HashMap in which `get` does a table lookup by key. If we do
@@ -121,12 +110,7 @@ function last();
      *       does (similar internal structure as arrays, insertion order pres.),
      *       it's an easy value-add at no cost. Will need some help with this.
      */
-    function at(int $index);
-
-    /**
-     * @return int the index of the value, or NULL if it could not be found.
-     */
-    function indexOf($value): ?int;
+    function offset(int $index);
 }
 
 
@@ -148,94 +132,32 @@ function isEmpty(): bool;
 
 
 /**
- * Indicates that a structure is designed to process all elements to produce a
- * new value as a result.
- *
- * @todo this feels like it belongs in an iterator library, but we get some nice
- *       wins doing these internally, because we have low-level access to the
- *       structures without the need to invoke iterators or class lookups to
- *       determine the type of the result (for cases like map, filter etc).
- *       How else would you know how to create the instance? Clone?
+ * Indicates that a structure contains a series of contiguous elements.
  */
-interface Collection
+interface Sequence
 {
-
-    /**
-     * @param callable $callback Called with value and key, should return what
-     *                           should replace the value. Keys are preserved.
-     *
-     * @return static A copy of $this with each value replaced by the result of
-     *                called the callback with that value (and its index/key).
-     *                Keys are always preserved.
-     */
-    function map(callable $callback): Collection;
-
-    /**
-     * @param callback $predicate Called with value and key, determines whether
-     *                            the value should be included in the result.
-     *                            Should return TRUE if the value should be
-     *                            included, FALSE otherwise. Keys are preserved.
-     *
-     * @return Collection A collection of the same type, containing all keys and
-     *                    values for which the predicate returned TRUE.
-     */
-
-    function filter(callable $predicate): Collection;
-
-    /**
-     * Reduces the collection to a single value using a callback function
-     *
-     * @param callback $callback Called with the current value of the result and
-     *                           the value and key of the current iteration.
-     *                           The result is set to $initial at the start.
-     *
-     * @param mixed|null $initial The initial value of the result argument.
-     *
-     * @return mixed The final value of the result.
-     */
-    function reduce(callable $callback, $initial = null);
-
     /**
-     * @return bool TRUE if empty pr the predicate returns TRUE for any element
-     *              in the collection, FALSE otherwise.
+     * @return mixed The value at the given position.
      *
-     * @todo Many other implementations (lodash, js) use `some` here, but I have
-     *       always liked the phrasing of "any" and "all". PHP doesn't have an
-     *       array equivalent for these anyway, so we have no reference for
-     *       consistency. Always be innovating, or accept the convention?
+     * @throws IndexOutOfBoundsException if the index is not within [0, size)
      */
-    function any(callable $predicate): bool;
+    function get(int $index);
 
-    /**
-     * @return bool TRUE if not empty and the predicate returns TRUE for all
-     *              elements in the collection, FALSE otherwise.
-     */
-    function all(callable $predicate): bool;
 
     /**
-     * @return string The result of concatenating the string representations of
-     *                all values, separated by the given $glue.
+     * @return int the index of the value, or NULL if it could not be found.
      */
-    function join(string $glue = ""): string;
-}
-
+    function indexOf($value): ?int;
 
-/**
- * Indicates that a structure contains a series of contiguous elements.
- */
-interface Sequence
-{
     /**
-     * @return mixed The value at the given position.
      *
-     * @throws AccessException if the index is out of range [0, size)
      */
-    function get(int $index);
+    function slice(int $offset, int $length): Sequence;
 
     /**
      * Sets the value at the given position.
      *
-     * @throws AccessException if the index is out of range [0, size)
+     * @throws IndexOutOfBoundsException if the index is not within [0, size)
      */
     function set(int $index, $value);
 
@@ -243,7 +165,9 @@ function set(int $index, $value);
      * Removes the value at the given position, moving all successive
      * values one position towards the front.
      *
-     * @throws AccessException if the index is out of range [0, size)
+     * @todo Naming - remove, pull?
+     *
+     * @throws IndexOutOfBoundsException if the index is not within [0, size)
      */
     function unset(int $index);
 
@@ -258,14 +182,15 @@ function unset(int $index);
      *
      * @todo We should also consider constructor syntax, like `new Vector(...?)`
      *
-     * @throws AccessException if the index is out of range [0, size]
+     * @throws IndexOutOfBoundsException if the index is out of range [0, size]
      */
     function insert(int $index, $value);
 }
 
 
 /**
- * Indicates that a structure is designed to operate efficiently at either end.
+ * Indicates that a structure is designed to operate efficiently at both the
+ * front and the back of a linear dataset.
  */
 interface Deque
 {
@@ -279,11 +204,11 @@ function push($value);
      *
      * @return mixed The value that was removed from the end of the structure.
      *
-     * @throws AccessException if the structure is empty.
+     * @throws EmptyContainerException if the structure is empty.
      *
      * @todo should we leave it up to the impl to throw or return NULL? I think
      *       we should aim for consistency by requiring a throw when empty. This
-     *       should be consistent with LinearAccess' first and last.
+     *       should be consistent with OffsetAccess' first and last.
      */
     function pop();
 
@@ -296,10 +221,13 @@ function unshift($value);
      * Removes and returns the first value of the structure.
      *
      * @return mixed The value that was removed from the front of the structure.
+     *
+     * @throws EmptyContainerException if the structure is empty.
      */
     function shift();
 }
 
+
 /**
  * A first-in-last-out structure.
  *
@@ -324,51 +252,23 @@ function shift();
  *       Maybe Deque can be a composite of two separate interfaces? Or we make
  *       `push` and `pop` part of Sequence? That doesn't feel quite right because
  *       a sequence of values does not necessarily imply that end-ops are O(1).
- *
- *
- *
- *
  */
 interface Stack
 {
     function push($value);
 
     /**
-     * @todo What happens when the stack is empty? AccessException?
-     *       We need a way to ensure that a stack can know when it is empty,
-     *       otherwise we can't implement iteration:
-     *
-     *           while (!$this->isEmpty()) {
-     *               yield $this->pop();
-     *           }
-     *
-     *       Maybe Stack can extend Container? Or at least Countable? What does
-     *       that mean for other structures that could extend Container? We can
-     *       define a policy like "only extend core interfaces" but I'm not sure
-     *       that that will always be wise. I don't see an issue with extension,
-     *       but I wanted to attept composition first and see how far we get.
-     *
-     *       We could also extend IteratorAggregate here and require that it
-     *       returns an iterator that is seekable which we can wrap, or simply
-     *       an instance of a reverse iterator interface? Or Stack can extend
-     *       something like "ReverseIterator"?
-     *
-     *       I think we should avoid IteratorAggregate as an unnecessary layer.
-     *       We could create our own interface which has methods like end(),
-     *       prev() and valid()? Create the iterator internally when implemented.
-     *
-     *       I'm starting to see more and more iterator-domain stuff creeping in
-     *       here. Is it counter-intuitive to separate iterators from ds? Why
-     *       can't iterators be considered data structures? Or why can't a ds
-     *       library also provide some iterators that are designed to work with
-     *       the structures. @Levi I think this is what you meant by external
-     *       iterators, so potentially on the same page here?
+     * @throws EmptyContainerException if the stack is empty.
      */
     function pop();
 
+    /**
+     * @throws EmptyContainerException if the stack is empty.
+     */
     function peek();
 }
 
+
 /**
  * A first-in-first-out structure.
  *
@@ -381,13 +281,17 @@ interface Queue
     function push($value);
 
     /**
-     * See notes on Stack::pop -^
+     * @throws EmptyContainerException if the stack is empty.
      */
     function pop();
 
+    /**
+     * @throws EmptyContainerException if the stack is empty.
+     */
     function peek();
 }
 
+
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
@@ -428,26 +332,26 @@ function remove($value);
     function has($value): bool;
 
     /**
-     * @return Set A set from all values in $this, and all values from $other.
+     * @return static
      *
      * @todo Naming - or, union, merge
      */
     function or(Set $other): Set;
 
     /**
-     * @return Set A set from values in either $this or $other, but not in both.
+     * @return static
      */
     function xor(Set $other): Set;
 
     /**
-     * @return Set A set from values in $this that are not in $other.
+     * @return static
      *
      * @todo Naming - not, diff, without
      */
     function not(Set $other): Set;
 
     /**
-     * @return Set A set from values in $this that are not in $other.
+     * @return static
      *
      * @todo Naming - and, intersect
      */
@@ -458,6 +362,8 @@ function and(Set $other): Set;
 /**
  * A structure that associates one value with another and provides the ability
  * to query or adjust associations efficiently.
+ *
+ * @todo Naming - dictionary
  */
 interface Map
 {
@@ -471,6 +377,8 @@ interface Map
     function set($key, $value);
 
     /**
+     * @todo if NULL keys are not allowed, should we throw if $key is NULL?
+     *
      * @return mixed The value associated with the $key, or $default if the key
      *               could not be found and a $default was provided.
      *
@@ -553,83 +461,51 @@ function keys(): Set;
      *       expose a view on the structure's values rather than an eager copy.
      */
     function values(): Sequence;
-
-    /**
-     * @return Map The result of adding all associations from $map to $this.
-     *
-     * @todo Feels a bit isolated here - isn't this part of the set operations
-     *       discussion? If so, this is already the equivalent of or/union, so
-     *       it is probably smart to consider unified naming that applies to the
-     *       semantics of both structures.
-     */
-    function merge(Map $map): Map;
 }
 
 
 /**
- * A low-level memory allocation abstraction which could be useful to build
- * structures in userland that do not want to use arrays or other structures
- * as a buffer.
- *
- * We have to be very careful to not go too far with this. Memory access should
- * be extremely well guarded here. We MUST have a sensible maximum capacity that
- * can be allocated, so that we do not expose the potential to crash anything.
- *
- * @todo is it safe to use undefined or should we set all elements to NULL?
- *       or we can convert to NULL when undefined is read? Would be strange to
- *       expose undefined to userland. Attempting to read an undefined value is
- *       almost certainly a bug though, so we could throw an exception if this
- *       happens rather than magically converting it to NULL (which would also
- *       make it ambiguous because you can't distinguish between null and undef)
- *
- * @todo What should clear do? I think that capacity should be preserved, but
- *       all values should be set the their initial state, ie. undefined
+ * Indicates that a structure supports tree traversals.
  */
-final class Allocation implements \ArrayAccess, Cloneable, Clearable
+interface TreeTraversal
 {
-    public function __construct(int $capacity) {}
+    /**
+     * Root, Left, Right
+     */
+    function preorder(): Traversable;
+
+    /**
+     * Left, Root, Right
+     */
+    function inorder(): Traversable;
 
-    // capacity
-    // realloc
+    /**
+     * Left, Right, Root
+     */
+    function postorder(): Traversable;
 
-    // clone
-    // clear
+    /**
+     * Right, Root, Left
+     */
+    function outorder(): Traversable;
 }
 
+
 /**
  * Basic list, dynamic size, contiguous, no ops at the front.
  */
-final class Vector implements \ArrayAccess, Container, Cloneable, Clearable, Sortable, Reversable, LinearAccess, Collection, Sequence, Stack
-{
-    // get
-    // set
-    // unset
-    // insert
-
-    // push
-    // pop
-    // peek
+final class Vector implements
+    Traversable,
+    ArrayAccess,
+    Container,
+    Clearable,
+    Sortable,
+    Reversable,
+    OffsetAccess,
+    Sequence,
+    Stack
+    {}
 
-    // map
-    // reduce
-    // filter
-    // any
-    // all
-    // join
-
-    // at
-    // indexOf
-    // first
-    // last
-
-    // sort
-    // reverse
-
-    // clone
-    // clear
-    // count
-    // isEmpty
-}
 
 /**
  * Not necessarily literally a linked list, but has the same theoretical props.
@@ -657,136 +533,83 @@ final class Vector implements \ArrayAccess, Container, Cloneable, Clearable, Sor
  * objection to effectively lying about the name, we should come up with another,
  * like... *no ideas*
  */
-final class LinkedList implements \ArrayAccess, Container, Cloneable, Clearable, Sortable, Reversable, LinearAccess, Collection, Sequence, Stack, Deque
-{
-    // get
-    // set
-    // unset
-    // insert
-
-    // push
-    // pop
-    // peek
+final class LinkedList implements
+    Traversable,
+    ArrayAccess,
+    Container,
+    Clearable,
+    Sortable,
+    Reversable,
+    OffsetAccess,
+    Sequence,
+    Stack,
+    Deque
+    {}
 
-    // shift
-    // unshift
 
-    // map
-    // reduce
-    // filter
-    // any
-    // all
-    // join
+/**
+ * The standard hashtable, implemented as Map in ext-ds currently. It is based
+ * on the PHP array but is not identical. We can implement OffsetAccess because
+ * insertion order is preserved.
+ */
+final class HashMap implements
+    Traversable,
+    ArrayAccess,
+    Container,
+    Clearable,
+    Sortable,
+    Reversable,
+    OffsetAccess,
+    Collection,
+    Map
+    {}
 
-    // at
-    // indexOf
-    // first
-    // last
 
-    // sort
-    // reverse
+/**
+ * The Set equivalent of HashMap. Might not use the same internals as HashMap.
+ */
+final class HashSet implements
+    Traversable,
+    ArrayAccess,
+    Container,
+    Clearable,
+    Sortable,
+    Reversable,
+    OffsetAccess,
+    Collection,
+    Set
+    {}
 
-    // clone
-    // clear
-    // count
-    // isEmpty
-}
 
 /**
- * The standard hash table, implemented as Map in ext-ds currently. It is modelled
- * after the PHP array but is not identical. We can implement LinearAccess because
- * insertion order is preserved. I don't believe we need a LinkedMap or anything
- * like that because the existing implementation is already very, very good. I did
- * experiment with open addressing for  a long time but I could not out-perform
- * the existing model.
- *
- * Keys can be any scalar value (@todo potentially not NULL though), or object
- * that implements Hashable. I don't think we should support object keys that do
- * not implement Hashable, which is a big side-step from ext-ds currently which
- * falls back to spl_object_hash. If non-hashable objects must be used as keys,
- * the implementation should use a BST instead, which does not rely on hashing.
- *
- * Alternatively, we could impelement a SortedMap using BST internally, which
- * removes the need for hashing and has the added benefit of sorted keys. This
- * could pretty easily be implemented in userland because it's such a simple
- * composition - I'm not convinced that we need to implement it internally. A
- * counter argument might be that we should standardise here while we can,
- * provide a dedicated implementation of a map that does not require hashing,
- * and is sorted by key. We could also accept an optional comparator.
+ * A structure that is always sorted and does not allow duplicate elements.
  *
- * Something to consider is a SortedMap interface, implemented as TreeMap,
- * Java style, but I think a SortedMap implementation is sufficient. I can't
- * imagine many valid alternative implementations. We could also call the impl
- * TreeMap and not have "SortedMap" be anything at all.
+ * See: https://github.com/php-ds/ext-ds/issues/121
  */
-final class HashMap implements \ArrayAccess, Container, Cloneable, Clearable, Sortable, Reversable, LinearAccess, Collection, Map
-{
-    // get
-    // set/put <Hashable | scalar, *>
-    // unset
-    // has
-    // find (null if value not found)
-
-    // keys
-    // values
-
-    // merge
-
-    // map
-    // reduce
-    // filter
-    // any
-    // all
-    // join
-
-    // at
-    // indexOf
-    // first
-    // last
-
-    // sort
-    // reverse
-
-    // clone
-    // clear
-    // count
-    // isEmpty
-}
+final class BinarySearchTree implements
+    Traversable,
+    TreeTraversal,
+    Container,
+    Clearable,
+    Set
+    {}
+
 
 /**
- * The Set equivalent of HashMap.
+ * A queue implementation that does not require hashing, a heap, or comparison.
+ *
+ * @todo This will be based on LinkedList, but LinkedList can't implement Queue
+ *       because it might have to implement destructive traversal. There are
+ *       some notes on Stack and Queue that would need to be resolved before we
+ *       can decide whether this structure is necessary at all.
  */
-final class HashSet implements \ArrayAccess, Container, Cloneable, Clearable, Sortable, Reversable, LinearAccess, Collection, Set
-{
-    // add <Hashable | scalar>
-    // remove
-    // has
-
-    // or
-    // xor
-    // not
-    // and
-
-    // map
-    // reduce
-    // filter
-    // any
-    // all
-    // join
-
-    // at
-    // indexOf
-    // first
-    // last
-
-    // sort
-    // reverse
-
-    // clone
-    // clear
-    // count
-    // isEmpty
-}
+final class LinkedQueue implements
+    Traversable,
+    Container,
+    Clearable,
+    Queue
+    {}
+
 
 /**
  * Binary heap-based queue with a mixed-type priority value associated with the
@@ -807,74 +630,46 @@ final class HashSet implements \ArrayAccess, Container, Cloneable, Clearable, So
  *       In other words it should follow insertion order if no comparator was
  *       given, which Heap would not do.
  */
-final class PriorityQueue implements Iterator, Container, Cloneable, Clearable, Queue
+final class PriorityQueue implements Traversable, Container, Clearable, Queue
 {
-    // push (value, priority = null)
-    // pop
-    // peek
-
-    // clone
-    // clear
-    // count
-    // isEmpty
+    // push(value, priority = null)
 }
 
+
 /**
- * A queue implementation that does not require hashing, a heap, or comparison.
+ * Stable binary heap with an optional comparator, defaulting to minimum. Could
+ * potentially remove the nullable which clears up the min/max ambiguity.
  *
- * @todo This will be based on LinkedList, but LinkedList can't implement Queue
- *       because it might have to implement destructive traversal. There are
- *       some notes on Stack and Queue that would need to be resolved before we
- *       can decide whether this structure is necessary at all.
+ * -> __construct(callable $comparator = null)
  */
-final class LinkedQueue implements Container, Cloneable, Clearable, Queue
+final class Heap implements Traversable, Container, Clearable
 {
     // push
     // pop
     // peek
-
-    // clone
-    // clear
-    // count
-
-    // isEmpty
 }
 
-/**
- * A structure that is always sorted and does not allow duplicate elements.
- *
- * See: https://github.com/php-ds/ext-ds/issues/121
- */
-final class BinarySearchTree implements Iterator, Container, Cloneable, Clearable
-{
-    // add
-    // remove
-    // has
-
-    // clone
-    // clear
-    // count
-    // isEmpty
-
-    public function inorder() : Iterator {}   // <-- default iterator?
-    public function preorder() : Iterator {}
-    public function postorder() : Iterator {}
-}
 
 /**
- * Stable binary heap with an optional comparator, defaulting to minimum. Could
- * potentially remove the nullable which clears up the min/max ambiguity.
+ * A low-level memory allocation abstraction which could be useful to build
+ * structures in userland that do not want to use arrays or other structures
+ * as a buffer.
+ *
+ * @todo how should we handle elements that are undefined, ie. a new allocation
+ *       will have undefined values. Internally these will be IS_UNDEF, but we
+ *       should not expose that to userland. Attempting to access an undefined
+ *       value will throw an exception.
+ *
+ * @todo What should clear do? I think that capacity should be preserved, but
+ *       all values should be set the their initial state, ie. undefined
  */
-final class Heap implements Iterator, Container, Cloneable, Clearable
+final class Allocation implements ArrayAccess, Clearable
 {
-    public function __construct(callable $comparator = null) {}
+    // __construct(int $capacity)
 
-    // push
-    // pop
-    // peek
+    // capacity(): int
+    // realloc(int): void
 
-    // clone
-    // clear
-    // count
-    // isEmpty
+    // clone(): self
+    // clear(): void
 }

From 33c40539732570e4ea7a4123a58fee1eee1bc395 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 9 Feb 2019 02:08:32 -0800
Subject: [PATCH 08/50] Add comment for slice

---
 DRAFT.php | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/DRAFT.php b/DRAFT.php
index 8b6b4dc..d2e660b 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -149,7 +149,10 @@ function get(int $index);
     function indexOf($value): ?int;
 
     /**
+     * Returns a subsection of the sequence.
      *
+     * Note: We can do this internally without creating a copy of the slice by
+     *       sharing the same contiguous memory with an offset and length.
      */
     function slice(int $offset, int $length): Sequence;
 

From efacf72ee03f6af5f72e41ac46dac66b72f7547b Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 9 Feb 2019 03:55:49 -0800
Subject: [PATCH 09/50] Update DRAFT.php

---
 DRAFT.php | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index d2e660b..a0b07ed 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -148,14 +148,6 @@ function get(int $index);
      */
     function indexOf($value): ?int;
 
-    /**
-     * Returns a subsection of the sequence.
-     *
-     * Note: We can do this internally without creating a copy of the slice by
-     *       sharing the same contiguous memory with an offset and length.
-     */
-    function slice(int $offset, int $length): Sequence;
-
     /**
      * Sets the value at the given position.
      *
@@ -184,9 +176,19 @@ function unset(int $index);
      *
      * @todo We should also consider constructor syntax, like `new Vector(...?)`
      *
+     * @todo Should $value be ...$values? (This applies to all push, unshift etc)
+     *
      * @throws IndexOutOfBoundsException if the index is out of range [0, size]
      */
     function insert(int $index, $value);
+
+    /**
+     * Returns a subsection of the sequence.
+     *
+     * Note: We can do this internally without creating a copy of the slice by
+     *       sharing the same contiguous memory with an offset and length.
+     */
+    function slice(int $offset, int $length): Sequence;
 }
 
 

From 017b6f2ca4257944d060be4766972ec9dfbac298 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 9 Feb 2019 11:44:41 -0800
Subject: [PATCH 10/50] Remove Stack and Queue interfaces, Deque extends
 Sequence

---
 DRAFT.php | 186 +++++++++++++++++++-----------------------------------
 1 file changed, 64 insertions(+), 122 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index a0b07ed..0f2cf1a 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -136,6 +136,24 @@ function isEmpty(): bool;
  */
 interface Sequence
 {
+    /**
+     * Appends the given value to the end of the structure.
+     */
+    function push($value);
+
+    /**
+     * Removes and returns the value at the end of the structure.
+     *
+     * @return mixed The value that was removed from the end of the structure.
+     *
+     * @throws EmptyContainerException if the structure is empty.
+     *
+     * @todo should we leave it up to the impl to throw or return NULL? I think
+     *       we should aim for consistency by requiring a throw when empty. This
+     *       should be consistent with OffsetAccess' first and last.
+     */
+    function pop();
+    
     /**
      * @return mixed The value at the given position.
      *
@@ -143,11 +161,6 @@ interface Sequence
      */
     function get(int $index);
 
-    /**
-     * @return int the index of the value, or NULL if it could not be found.
-     */
-    function indexOf($value): ?int;
-
     /**
      * Sets the value at the given position.
      *
@@ -164,7 +177,12 @@ function set(int $index, $value);
      * @throws IndexOutOfBoundsException if the index is not within [0, size)
      */
     function unset(int $index);
-
+    
+    /**
+     * @return int the index of the value, or NULL if it could not be found.
+     */
+    function indexOf($value): ?int;
+    
     /**
      * Moves all values between the given index and the end of the sequence one
      * position towards the back, then inserts the given value into the gap.
@@ -196,26 +214,8 @@ function slice(int $offset, int $length): Sequence;
  * Indicates that a structure is designed to operate efficiently at both the
  * front and the back of a linear dataset.
  */
-interface Deque
+interface Deque extends Sequence
 {
-    /**
-     * Appends the given value to the end of the structure.
-     */
-    function push($value);
-
-    /**
-     * Removes and returns the value at the end of the structure.
-     *
-     * @return mixed The value that was removed from the end of the structure.
-     *
-     * @throws EmptyContainerException if the structure is empty.
-     *
-     * @todo should we leave it up to the impl to throw or return NULL? I think
-     *       we should aim for consistency by requiring a throw when empty. This
-     *       should be consistent with OffsetAccess' first and last.
-     */
-    function pop();
-
     /**
      * Prepends the given value to the front of the structure.
      */
@@ -232,70 +232,6 @@ function shift();
 }
 
 
-/**
- * A first-in-last-out structure.
- *
- * @todo Should stacks be iterable, and if so, do they have to be destructive?
- *
- *       It gets tricky because stacks yield from the back as values as popped
- *       off the stack, but implementing structures like a Vector would iterate
- *       from the front, which would not be intuitive at all. Perhaps the best
- *       solution here is that implementations must iterate in reverse.
- *
- *       We could potentially do some internal magic to force reverse iteration?
- *       (Override iterator creation when a userland object implements Stack)
- *
- * @todo If we decide that this interface must be traversed in FILO order, then
- *       Vector can not implement it. We could instead make this a final class,
- *       using a vector internally? A stack is a stack, no?
- *
- *       Vector would need to find `push` and `pop` elsewhere, which are both
- *       constant time operations at the end of the buffer. LinkedList has these
- *       too, but they come as part of Deque, which Vector does not implement.
- *
- *       Maybe Deque can be a composite of two separate interfaces? Or we make
- *       `push` and `pop` part of Sequence? That doesn't feel quite right because
- *       a sequence of values does not necessarily imply that end-ops are O(1).
- */
-interface Stack
-{
-    function push($value);
-
-    /**
-     * @throws EmptyContainerException if the stack is empty.
-     */
-    function pop();
-
-    /**
-     * @throws EmptyContainerException if the stack is empty.
-     */
-    function peek();
-}
-
-
-/**
- * A first-in-first-out structure.
- *
- * @todo Should queues be iterable, and if so, do they have to be destructive.
- *
- * @todo If Stack becomes a class, should Queue remain an interface?
- */
-interface Queue
-{
-    function push($value);
-
-    /**
-     * @throws EmptyContainerException if the stack is empty.
-     */
-    function pop();
-
-    /**
-     * @throws EmptyContainerException if the stack is empty.
-     */
-    function peek();
-}
-
-
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
@@ -332,6 +268,8 @@ function remove($value);
 
     /**
      * @return bool TRUE if the value is in $this set, FALSE otherwise.
+     *
+     * @todo Naming - contains?
      */
     function has($value): bool;
 
@@ -506,13 +444,12 @@ final class Vector implements
     Sortable,
     Reversable,
     OffsetAccess,
-    Sequence,
-    Stack
+    Sequence
     {}
 
 
 /**
- * Not necessarily literally a linked list, but has the same theoretical props.
+ * Not necessarily literally a linked list, but has the same semantics.
  *
  * Very similar to vector, but does support operations at the front. Will most
  * likely be implemented as a circular buffer / "deque", but is an important
@@ -545,8 +482,6 @@ final class LinkedList implements
     Sortable,
     Reversable,
     OffsetAccess,
-    Sequence,
-    Stack,
     Deque
     {}
 
@@ -588,6 +523,8 @@ final class HashSet implements
 /**
  * A structure that is always sorted and does not allow duplicate elements.
  *
+ * This would be useful for building sorted datasets, or sets without hashing.
+ *
  * See: https://github.com/php-ds/ext-ds/issues/121
  */
 final class BinarySearchTree implements
@@ -600,25 +537,30 @@ final class BinarySearchTree implements
 
 
 /**
- * A queue implementation that does not require hashing, a heap, or comparison.
- *
- * @todo This will be based on LinkedList, but LinkedList can't implement Queue
- *       because it might have to implement destructive traversal. There are
- *       some notes on Stack and Queue that would need to be resolved before we
- *       can decide whether this structure is necessary at all.
+ * A first-in-last-out structure. Destructive traversal.
  */
-final class LinkedQueue implements
-    Traversable,
-    Container,
-    Clearable,
-    Queue
-    {}
+final class Stack implements Traversable, Container, Clearable
+{
+    public function push($value) {}
+    public function pop() {}
+    public function peek() {}
+}
+
+
+/**
+ * A first-in-first-out structure. Destructive traversal.
+ */
+final class Queue implements Traversable, Container, Clearable
+{
+    public function push($value) {}
+    public function pop() {}
+    public function peek() {}
+}
 
 
 /**
  * Binary heap-based queue with a mixed-type priority value associated with the
- * key. Iteration is destructive because that is how a heap works, and matches
- * the declared behaviour by Queue (destructive iteration).
+ * key. Iteration is destructive.
  *
  * @todo Should we provide the ability to adjust the priority of a value? I think
  *       it would be O(N) in a heap, and introduces ambiguity around duplicate
@@ -634,23 +576,25 @@ final class LinkedQueue implements
  *       In other words it should follow insertion order if no comparator was
  *       given, which Heap would not do.
  */
-final class PriorityQueue implements Traversable, Container, Clearable, Queue
+final class PriorityQueue implements Traversable, Container, Clearable
 {
-    // push(value, priority = null)
+    public function push($value, $priority = 0) {}
+    public function pop() {}
+    public function peek() {}
 }
 
 
 /**
  * Stable binary heap with an optional comparator, defaulting to minimum. Could
  * potentially remove the nullable which clears up the min/max ambiguity.
- *
- * -> __construct(callable $comparator = null)
  */
 final class Heap implements Traversable, Container, Clearable
 {
-    // push
-    // pop
-    // peek
+    public function __construct(callable $comparator = null) {}
+    
+    public function push($value) {}
+    public function pop() {}
+    public function peek() {}
 }
 
 
@@ -669,11 +613,9 @@ final class Heap implements Traversable, Container, Clearable
  */
 final class Allocation implements ArrayAccess, Clearable
 {
-    // __construct(int $capacity)
-
-    // capacity(): int
-    // realloc(int): void
-
-    // clone(): self
-    // clear(): void
+    public function __construct(int $capacity) {}
+    
+    public function capacity(): int {}
+    public function realloc(int): void {}
+    public function clear(): void {}
 }

From ecedaed1218a9943d269469c9ace4aa9c60d2bed Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 9 Feb 2019 12:04:09 -0800
Subject: [PATCH 11/50] Update DRAFT.php

---
 DRAFT.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/DRAFT.php b/DRAFT.php
index 0f2cf1a..dbb3a73 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -3,6 +3,8 @@
 |--------------------------------------------------------------------------
 | PHP Data Structures - API Draft - v2.0
 |
+| Target PHP Version: 7.3.1
+|
 | https://pecl.php.net/package/ds
 |
 |--------------------------------------------------------------------------

From b0fede768b092860f8de5c79803572b4fc2d53e3 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sun, 10 Feb 2019 10:12:39 -0800
Subject: [PATCH 12/50] Add Hashable interface to draft

---
 DRAFT.php | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index dbb3a73..bfbb134 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -89,6 +89,18 @@ function sort(callable $comparator = null);
 }
 
 
+/**
+ * Indicates that an object is designed to be used in hash-based structures.
+ */
+interface Hashable
+{
+    /**
+     * @return mixed A scalar value (or hashable delegate) that will be hashed.
+     */
+    function getHashSource();
+}
+
+
 /**
  * Indicates that a structure can be accessed using a zero-based integer index
  * indicating the position of an element from the beginning of the structure.
@@ -155,7 +167,7 @@ function push($value);
      *       should be consistent with OffsetAccess' first and last.
      */
     function pop();
-    
+
     /**
      * @return mixed The value at the given position.
      *
@@ -179,12 +191,12 @@ function set(int $index, $value);
      * @throws IndexOutOfBoundsException if the index is not within [0, size)
      */
     function unset(int $index);
-    
+
     /**
      * @return int the index of the value, or NULL if it could not be found.
      */
     function indexOf($value): ?int;
-    
+
     /**
      * Moves all values between the given index and the end of the sequence one
      * position towards the back, then inserts the given value into the gap.
@@ -593,7 +605,7 @@ public function peek() {}
 final class Heap implements Traversable, Container, Clearable
 {
     public function __construct(callable $comparator = null) {}
-    
+
     public function push($value) {}
     public function pop() {}
     public function peek() {}
@@ -616,7 +628,7 @@ public function peek() {}
 final class Allocation implements ArrayAccess, Clearable
 {
     public function __construct(int $capacity) {}
-    
+
     public function capacity(): int {}
     public function realloc(int): void {}
     public function clear(): void {}

From f9747aa3345a3ee1832034695b9ebc03a34aeb67 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 11 Feb 2019 03:38:08 -0800
Subject: [PATCH 13/50] Add iterators, mutable extensions, other adjustments

---
 DRAFT.php | 408 ++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 275 insertions(+), 133 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index bfbb134..d634e46 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -14,6 +14,13 @@
 */
 namespace Ds;
 
+/**
+ * @todo How do we handle comparisons? Proposal: == for objects, === otherwise.
+ */
+
+/******************************************************************************/
+/*                                EXCEPTIONS                                  */
+/******************************************************************************/
 
 /**
  * Should be thrown when a structure is accessed unexpectedly, or in a way that
@@ -22,26 +29,196 @@
  */
 interface AccessException {}
 
-
 /**
  * Should be thrown when an index or key is not within the given access bounds
  * of a structure, such as a negative index on a list or
  */
 class IndexOutOfBoundsException extends \OutOfBoundsException implements AccessException  {}
 
-
 /**
  * Should be thrown when an empty container is accessed a clear, obvious result.
  */
 class EmptyContainerException extends \LogicException implements AccessException {}
 
-
 /**
  * Should be thrown when an undefined zval is accessed. I do not except any user
  * classes to throw this, but it might be useful to catch it.
  */
 class UndefinedValueException extends \LogicException implements AccessException {}
 
+/**
+ * Should be thrown when a key is not supported, eg. when an attempt is made to
+ * associate a value with NULL in Map, causing ambiguity in `find`.
+ */
+class InvalidKeyException extends \LogicException implements AccessException {}
+
+
+/******************************************************************************/
+/*                                 ITERATORS                                  */
+/******************************************************************************/
+
+/**
+ * Yields each key and value from $iter, after applying $mapper to the value.
+ */
+function map(iterable $iter, callable $mapper): \Iterator
+{
+    foreach ($iter as $key => $val) {
+        yield $key => $mapper($val, $key, $iter);
+    }
+}
+
+/**
+ * Yields each key and value from $iter for which $predicate returns TRUE.
+ */
+function filter(iterable $iter, callable $predicate): \Iterator
+{
+    foreach ($iter as $key => $val) {
+        if ($predicate($val, $key, $iter)) {
+            yield $key => $val;
+        }
+    }
+}
+
+/**
+ * Yields each key and value from $iter for which $predicate returns FALSE.
+ */
+function reject(iterable $iter, callable $predicate): \Iterator
+{
+    foreach ($iter as $key => $val) {
+        if (!$predicate($val, $key, $iter)) {
+            yield $key => $val;
+        }
+    }
+}
+
+/**
+ * @return mixed|null The key of the first iteration for which $predicate
+ *                    returns TRUE, or NULL if none were found.
+ */
+function find(iterable $iter, callable $predicate)
+{
+    foreach (filter($iter, $predicate) as $key => $val) {
+        return $key;
+    }
+
+    return null;
+}
+
+/**
+ * Reduces the keys and values of $iter to a single result, carrying forward
+ * intermediate results until the final iteration returns the final result.
+ */
+function reduce(iterable $iter, callable $reducer, $initial = null)
+{
+    $result = $initial;
+
+    foreach ($iter as $key => $val) {
+        $result = $reducer($result, $value, $key, $iter);
+    }
+
+    return $result;
+}
+
+/**
+ * @return bool TRUE if the $predicate returns TRUE for any iteration of $iter.
+ *
+ */
+function any(iterable $iter, callable $predicate = null): bool
+{
+    foreach (filter($iter) as $key => $val) {
+        return true;
+    }
+
+    return false;
+}
+
+/**
+ * @return bool TRUE if the $predicate returns TRUE for all iterations of $iter,
+ *                   or if $iter did not produce any iterations at all (empty).
+ */
+function all(iterable $iter, callable $predicate = null): bool
+{
+    foreach (reject($iter) as $key => $val) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * @todo I am not sure how viable this is, but it could be useful to find the
+ *       kth smallest value without having to sort the entire container. We must
+ *       be able to access the structure by offset in order to implement partial
+ *       algorithms like quick-select.
+ *
+ * See: https://en.wikipedia.org/wiki/Partial_sorting
+ */
+function sort(OffsetAccess $iter, callable $comparator = null): \Iterator
+{
+    // Partial sort...
+}
+
+
+/******************************************************************************/
+/*                                INTERFACES                                  */
+/******************************************************************************/
+
+/**
+ * Indicates that a structure contains elements and is aware of the number of
+ * items contained at any point in time. How a container must determine the
+ * count is implementation-specific.
+ *
+ * This interface does not imply that a structure is traversable.
+ */
+interface Container extends \Countable
+{
+    /**
+     * @return bool whether the container is empty, which can be TRUE even when
+     *              the number of elements is non-zero.
+     */
+    function isEmpty(): bool;
+}
+
+/**
+ * An interface which mirrors some of the iterator functions.
+ */
+interface Collection extends \Traversable
+{
+    /**
+     * @return static
+     */
+    function map(callable $mapper): Collection;
+
+    /**
+     * @return static
+     */
+    function filter(callable $predicate): Collection;
+
+    /**
+     * @return static
+     */
+    function reject(callable $predicate): Collection;
+
+    /**
+     * @return mixed|null
+     */
+    function find(callable $predicate);
+
+    /**
+     * @return mixed
+     */
+    function reduce(callable $reducer, $initial = null);
+
+    /**
+     * @return bool
+     */
+    function any(callable $predicate = null): bool;
+
+    /**
+     * @return bool
+     */
+    function all(callable $predicate = null): bool;
+}
 
 /**
  * Indicates that the elements of a structure can be represented by an array
@@ -49,10 +226,14 @@ class UndefinedValueException extends \LogicException implements AccessException
  */
 interface Arrayable
 {
+    /**
+     * This method should not fail. There is an implicit expectation that any
+     * structure converted to an array must be able to be converted back to
+     * the structure in such a way that it is considered equal to the initial.
+     */
     function toArray(): array;
 }
 
-
 /**
  * Indicates that a structure can be cleared.
  */
@@ -61,7 +242,6 @@ interface Clearable
     function clear();
 }
 
-
 /**
  * Indicates that the order of the elements of a structure can be reversed.
  */
@@ -73,7 +253,6 @@ interface Reversable
     function reverse();
 }
 
-
 /**
  * Indicates that the elements of a structure can be sorted.
  *
@@ -88,7 +267,6 @@ interface Sortable
     function sort(callable $comparator = null);
 }
 
-
 /**
  * Indicates that an object is designed to be used in hash-based structures.
  */
@@ -100,14 +278,15 @@ interface Hashable
     function getHashSource();
 }
 
-
 /**
  * Indicates that a structure can be accessed using a zero-based integer index
  * indicating the position of an element from the beginning of the structure.
  *
+ * We extend Countable because it should always be possible to determine bounds.
+ *
  * @todo Naming - OrdinalAccess, LinearAccess, RandomAccess...
  */
-interface OffsetAccess
+interface OffsetAccess extends \Countable
 {
     /**
      * @return mixed The value at the
@@ -115,40 +294,35 @@ interface OffsetAccess
      * @throws IndexOutOfBoundsException if the index is not within [0, size).
      *
      * @todo Potential for a better name, like "skip" or "atIndex" or "at"?
-     *
-     * @todo We could change this to `get`, but that conflicts with structures
-     *       like HashMap in which `get` does a table lookup by key. If we do
-     *       want to change this to `get`, it would mean that HashMap can't
-     *       implement it, which may not be such a bad thing? Feels _slightly_
-     *       odd for HashMap to implement linear access, but because it already
-     *       does (similar internal structure as arrays, insertion order pres.),
-     *       it's an easy value-add at no cost. Will need some help with this.
      */
     function offset(int $index);
 }
 
-
 /**
- * Indicates that a structure contains elements and is aware of the number of
- * items contained at any point in time. How a container must determine the
- * count is implementation-specific.
- *
- * This interface does not imply that a structure is traversable.
+ * Indicates that a structure contains a series of contiguous elements.
  */
-interface Container extends \Countable
+interface Sequence extends OffsetAccess
 {
     /**
-     * @return bool whether the container is empty, which can be TRUE even when
-     *              the number of elements is non-zero.
+     * @return mixed The first value in $this sequence.
      */
-    function isEmpty(): bool;
-}
+    function first();
 
+    /**
+     * @return mixed The last value in $this sequence.
+     */
+    function last();
+
+    /**
+     * @return Sequence A partial sequence of $this.
+     */
+    function slice(int $offset, int $length): Sequence;
+}
 
 /**
- * Indicates that a structure contains a series of contiguous elements.
+ * A sequence which can be modified, either in-place or as a copy.
  */
-interface Sequence
+interface MutableSequence extends Sequence
 {
     /**
      * Appends the given value to the end of the structure.
@@ -167,14 +341,6 @@ function push($value);
      *       should be consistent with OffsetAccess' first and last.
      */
     function pop();
-
-    /**
-     * @return mixed The value at the given position.
-     *
-     * @throws IndexOutOfBoundsException if the index is not within [0, size)
-     */
-    function get(int $index);
-
     /**
      * Sets the value at the given position.
      *
@@ -192,11 +358,6 @@ function set(int $index, $value);
      */
     function unset(int $index);
 
-    /**
-     * @return int the index of the value, or NULL if it could not be found.
-     */
-    function indexOf($value): ?int;
-
     /**
      * Moves all values between the given index and the end of the sequence one
      * position towards the back, then inserts the given value into the gap.
@@ -213,22 +374,13 @@ function indexOf($value): ?int;
      * @throws IndexOutOfBoundsException if the index is out of range [0, size]
      */
     function insert(int $index, $value);
-
-    /**
-     * Returns a subsection of the sequence.
-     *
-     * Note: We can do this internally without creating a copy of the slice by
-     *       sharing the same contiguous memory with an offset and length.
-     */
-    function slice(int $offset, int $length): Sequence;
 }
 
-
 /**
  * Indicates that a structure is designed to operate efficiently at both the
  * front and the back of a linear dataset.
  */
-interface Deque extends Sequence
+interface Deque extends MutableSequence
 {
     /**
      * Prepends the given value to the front of the structure.
@@ -245,7 +397,6 @@ function unshift($value);
     function shift();
 }
 
-
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
@@ -270,16 +421,6 @@ function shift();
  */
 interface Set
 {
-    /**
-     * Adds the given value to $this set if it is not already in $this set.
-     */
-    function add($value);
-
-    /**
-     * @todo what happens when the value could not be found? Silent no-op?
-     */
-    function remove($value);
-
     /**
      * @return bool TRUE if the value is in $this set, FALSE otherwise.
      *
@@ -314,6 +455,21 @@ function not(Set $other): Set;
     function and(Set $other): Set;
 }
 
+/**
+ * A set which can be modified, either in-place or as a copy.
+ */
+interface MutableSet extends Set
+{
+    /**
+     * Adds the given value to $this set if it is not already in $this set.
+     */
+    function add($value);
+
+    /**
+     * @todo what happens when the value could not be found? Silent no-op?
+     */
+    function remove($value);
+}
 
 /**
  * A structure that associates one value with another and provides the ability
@@ -323,15 +479,6 @@ function and(Set $other): Set;
  */
 interface Map
 {
-    /**
-     * Associates the $key with the $value, overriding any previous association.
-     *
-     * Note: Might not be able to support NULL keys. (See `find`)
-     *
-     * @todo Naming - set/put (set/unset symmetry, add/remove, put/pull?)
-     */
-    function set($key, $value);
-
     /**
      * @todo if NULL keys are not allowed, should we throw if $key is NULL?
      *
@@ -342,16 +489,6 @@ function set($key, $value);
      */
     function get($key, $default = null);
 
-    /**
-     * Removes the given $key from the map, and does nothing if they key could
-     * not be found.
-     *
-     * @todo Should we return the removed value? (No, ambiguous and two-in-one)
-     *
-     * @todo Key not found: should we throw, do nothing, or not enforce either?
-     */
-    function unset($key);
-
     /**
      * @return bool TRUE if $this map has an association for the given $key,
      *              FALSE otherwise.
@@ -382,26 +519,6 @@ function unset($key);
      */
     function has($key): bool;
 
-    /**
-     * @return mixed The first key that is associated with the given value, or
-     *               NULL if the value could not be found.
-     *
-     * @todo This could potentially double as a `has`, just like `indexOf` can.
-     *
-     * @todo The NULL return here forces the contraint that maps can't have NULL
-     *       values, which implementations should enforce themselves. Ideally we
-     *       should be able to do reverse lookups like these, but we have no way
-     *       to indicate "no key" without being ambiguous.
-     *
-     *       We could choose not to support lookups by value. It's a very simple
-     *       thing to implement (foreach, if value equal value, return key), so
-     *       there wouldn't be a large capability gap. We could instead look at
-     *       bi-directional later on? Or leave this to iteration-based projects.
-     *
-     * @todo Naming - find, lookup,
-     */
-    function find($value);
-
     /**
      * @return Set All keys from the map added in traversal order.
      *
@@ -419,11 +536,37 @@ function keys(): Set;
     function values(): Sequence;
 }
 
+/**
+ * A map which can be modified, either in-place or as a copy.
+ */
+interface MutableMap extends Map
+{
+    /**
+     * Associates the $key with the $value, overriding any previous association.
+     *
+     * @throws InvalidKeyException if the map implementation does not support
+     *                             the given key, eg. NULL
+     *
+     * @todo Naming - set/put (set/unset symmetry, add/remove, put/pull?)
+     */
+    function set($key, $value);
+
+
+    /**
+     * Removes the given $key from the map, and does nothing if they key could
+     * not be found.
+     *
+     * @todo Should we return the removed value? (No, ambiguous and two-in-one)
+     *
+     * @todo Key not found: should we throw, do nothing, or not enforce either?
+     */
+    function unset($key);
+}
 
 /**
  * Indicates that a structure supports tree traversals.
  */
-interface TreeTraversal
+interface Tree
 {
     /**
      * Root, Left, Right
@@ -447,21 +590,24 @@ function outorder(): Traversable;
 }
 
 
+/******************************************************************************/
+/*                                 CLASSES                                    */
+/******************************************************************************/
+
 /**
  * Basic list, dynamic size, contiguous, no ops at the front.
  */
 final class Vector implements
-    Traversable,
     ArrayAccess,
+    Arrayable,
+    Collection,
     Container,
     Clearable,
     Sortable,
     Reversable,
-    OffsetAccess,
-    Sequence
+    MutableSequence
     {}
 
-
 /**
  * Not necessarily literally a linked list, but has the same semantics.
  *
@@ -489,51 +635,51 @@ final class Vector implements
  * like... *no ideas*
  */
 final class LinkedList implements
-    Traversable,
     ArrayAccess,
+    Arrayable,
+    Collection,
     Container,
     Clearable,
     Sortable,
     Reversable,
-    OffsetAccess,
     Deque
     {}
 
-
 /**
  * The standard hashtable, implemented as Map in ext-ds currently. It is based
  * on the PHP array but is not identical. We can implement OffsetAccess because
  * insertion order is preserved.
+ *
+ * @todo Should we implement Arrayable here? We would have to throw if an object
+ *       is used as a key, and there will also be inconsistencies eg. "1" and 1.
  */
 final class HashMap implements
-    Traversable,
     ArrayAccess,
+    Collection,
     Container,
     Clearable,
     Sortable,
     Reversable,
     OffsetAccess,
-    Collection,
-    Map
+    MutableMap
     {}
 
-
 /**
- * The Set equivalent of HashMap. Might not use the same internals as HashMap.
+ * The Set equivalent of HashMap, which might not use a HashMap internally, but
+ * will always preserve insertion order.
  */
 final class HashSet implements
-    Traversable,
     ArrayAccess,
+    Arrayable,
+    Collection,
     Container,
     Clearable,
     Sortable,
     Reversable,
-    OffsetAccess,
-    Collection,
-    Set
+    Sequence,
+    MutableSet
     {}
 
-
 /**
  * A structure that is always sorted and does not allow duplicate elements.
  *
@@ -542,36 +688,34 @@ final class HashSet implements
  * See: https://github.com/php-ds/ext-ds/issues/121
  */
 final class BinarySearchTree implements
-    Traversable,
-    TreeTraversal,
+    Arrayable,
+    Collection,
     Container,
     Clearable,
-    Set
+    MutableSet,
+    Tree,
     {}
 
-
 /**
  * A first-in-last-out structure. Destructive traversal.
  */
-final class Stack implements Traversable, Container, Clearable
+final class Stack implements Traversable, Arrayable, Container, Clearable
 {
     public function push($value) {}
     public function pop() {}
     public function peek() {}
 }
 
-
 /**
  * A first-in-first-out structure. Destructive traversal.
  */
-final class Queue implements Traversable, Container, Clearable
+final class Queue implements Traversable, Arrayable, Container, Clearable
 {
     public function push($value) {}
     public function pop() {}
     public function peek() {}
 }
 
-
 /**
  * Binary heap-based queue with a mixed-type priority value associated with the
  * key. Iteration is destructive.
@@ -590,19 +734,18 @@ public function peek() {}
  *       In other words it should follow insertion order if no comparator was
  *       given, which Heap would not do.
  */
-final class PriorityQueue implements Traversable, Container, Clearable
+final class PriorityQueue implements Traversable, Arrayable, Container, Clearable
 {
     public function push($value, $priority = 0) {}
     public function pop() {}
     public function peek() {}
 }
 
-
 /**
  * Stable binary heap with an optional comparator, defaulting to minimum. Could
  * potentially remove the nullable which clears up the min/max ambiguity.
  */
-final class Heap implements Traversable, Container, Clearable
+final class Heap implements Traversable, Arrayable, Container, Clearable
 {
     public function __construct(callable $comparator = null) {}
 
@@ -611,7 +754,6 @@ public function pop() {}
     public function peek() {}
 }
 
-
 /**
  * A low-level memory allocation abstraction which could be useful to build
  * structures in userland that do not want to use arrays or other structures

From a69b21a23844855c6064667b2cf706ee3b6393c0 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 11 Feb 2019 03:48:25 -0800
Subject: [PATCH 14/50] Add note about potential for Map to extend Set

---
 DRAFT.php | 26 +++++++-------------------
 1 file changed, 7 insertions(+), 19 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index d634e46..2597a15 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -15,7 +15,7 @@
 namespace Ds;
 
 /**
- * @todo How do we handle comparisons? Proposal: == for objects, === otherwise.
+ * @todo How do we handle comparison? Proposal: == for objects, === otherwise.
  */
 
 /******************************************************************************/
@@ -498,24 +498,13 @@ function get($key, $default = null);
      *       can achieve that without intermediaries? That would make the `has`
      *       here a bit redundant, where we would not get much value for the
      *       cost of the ambiguity. The other alternative is hasKey + hasValue,
-     *       but that feels more specific than it needs to be, or just me?
+     *       but that feels more specific than it needs to be.
      *
-     *       This also has a very "set" vibe to it. What other structures have a
-     *       `has` as part of its semantics? Lists usually opt for `contains` or
-     *       have `indexOf` return something nonesense to indicate no result.
-     *
-     *       "What if we implement Set" would be weird because we would also get
-     *       `add` and `remove` and all the potential set operations. But is it
-     *       that bad really? The `add`, yes. The `remove`, a bit yes (ambig.)
-     *       But we want the `has` and we don't care for the operations much.
-     *       So we could extract `has` to another interface which Set extends,
-     *       allowing us to implement that here?
-     *
-     *       Or we _do_ care about the set operations, if we can get the naming
-     *       right we can also intersect values, diff, etc. Works for arrays.
-     *       In that case the Set interface only has `add` and `remove` that
-     *       are throwing us off. We could move `add` and `remove` to another
-     *       interface like a `Bag` or merge into `Container`?
+     * @todo What if we extend "Set" here? `has` would refer to the keys here
+     *       though, but I think that is valid and intuitive enough. We would
+     *       need to make sure that the set operation methods have names that
+     *       also apply to Map, without ambiguity or confusion. I get that this
+     *       is a bit strange but it makes sense to me contractually.
      */
     function has($key): bool;
 
@@ -551,7 +540,6 @@ interface MutableMap extends Map
      */
     function set($key, $value);
 
-
     /**
      * Removes the given $key from the map, and does nothing if they key could
      * not be found.

From 9ea5221698cf8da80aba0fc667ee25e7b712b463 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 11 Feb 2019 04:06:11 -0800
Subject: [PATCH 15/50] Invert mutable/immutable convension

---
 DRAFT.php | 47 +++++++++++++++++++++++++++++------------------
 1 file changed, 29 insertions(+), 18 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 2597a15..165e2fa 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -301,7 +301,7 @@ function offset(int $index);
 /**
  * Indicates that a structure contains a series of contiguous elements.
  */
-interface Sequence extends OffsetAccess
+interface ReadOnlySequence extends OffsetAccess
 {
     /**
      * @return mixed The first value in $this sequence.
@@ -314,15 +314,15 @@ function first();
     function last();
 
     /**
-     * @return Sequence A partial sequence of $this.
+     * @return static A partial sequence of $this.
      */
-    function slice(int $offset, int $length): Sequence;
+    function slice(int $offset, int $length): ReadOnlySequence;
 }
 
 /**
  * A sequence which can be modified, either in-place or as a copy.
  */
-interface MutableSequence extends Sequence
+interface Sequence extends ReadOnlySequence
 {
     /**
      * Appends the given value to the end of the structure.
@@ -380,7 +380,7 @@ function insert(int $index, $value);
  * Indicates that a structure is designed to operate efficiently at both the
  * front and the back of a linear dataset.
  */
-interface Deque extends MutableSequence
+interface Deque extends Sequence
 {
     /**
      * Prepends the given value to the front of the structure.
@@ -419,7 +419,7 @@ function shift();
  *       methods like `diff`, `intersect`, etc to be consistent with arrays?
  *       If so, should the set operation methods share the same naming?
  */
-interface Set
+interface ReadOnlySet
 {
     /**
      * @return bool TRUE if the value is in $this set, FALSE otherwise.
@@ -429,26 +429,37 @@ interface Set
     function has($value): bool;
 
     /**
-     * @return static
+     * Operator: |
+     *
+     * @todo what about the + operator?
+     *
+     * @return static A set containing values in both $this and $other.
      *
      * @todo Naming - or, union, merge
      */
     function or(Set $other): Set;
 
     /**
-     * @return static
+     * Operator: ^
+     *
+     * @return static A set containing values in either $this or $other,
+     *                but not in both.
      */
     function xor(Set $other): Set;
 
     /**
-     * @return static
+     * Operator: -
+     *
+     * @return static A set containing values in $this but not in $other.
      *
      * @todo Naming - not, diff, without
      */
     function not(Set $other): Set;
 
     /**
-     * @return static
+     * Operator: &
+     *
+     * @return static A set containing values in $this that are also in $other.
      *
      * @todo Naming - and, intersect
      */
@@ -458,7 +469,7 @@ function and(Set $other): Set;
 /**
  * A set which can be modified, either in-place or as a copy.
  */
-interface MutableSet extends Set
+interface Set extends ReadOnlySet
 {
     /**
      * Adds the given value to $this set if it is not already in $this set.
@@ -477,7 +488,7 @@ function remove($value);
  *
  * @todo Naming - dictionary
  */
-interface Map
+interface ReadOnlyMap
 {
     /**
      * @todo if NULL keys are not allowed, should we throw if $key is NULL?
@@ -528,7 +539,7 @@ function values(): Sequence;
 /**
  * A map which can be modified, either in-place or as a copy.
  */
-interface MutableMap extends Map
+interface Map extends ReadOnlyMap
 {
     /**
      * Associates the $key with the $value, overriding any previous association.
@@ -593,7 +604,7 @@ final class Vector implements
     Clearable,
     Sortable,
     Reversable,
-    MutableSequence
+    Sequence
     {}
 
 /**
@@ -649,7 +660,7 @@ final class HashMap implements
     Sortable,
     Reversable,
     OffsetAccess,
-    MutableMap
+    Map
     {}
 
 /**
@@ -664,8 +675,8 @@ final class HashSet implements
     Clearable,
     Sortable,
     Reversable,
-    Sequence,
-    MutableSet
+    ReadOnlySequence,
+    Set
     {}
 
 /**
@@ -680,7 +691,7 @@ final class BinarySearchTree implements
     Collection,
     Container,
     Clearable,
-    MutableSet,
+    Set,
     Tree,
     {}
 

From 38f1b88a632b0b46891cae735fbe6244efa6684d Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 11 Feb 2019 22:30:44 -0800
Subject: [PATCH 16/50] Remove collection methods

---
 DRAFT.php | 165 +++---------------------------------------------------
 1 file changed, 9 insertions(+), 156 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 165e2fa..d7e7038 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -53,112 +53,6 @@ class UndefinedValueException extends \LogicException implements AccessException
 class InvalidKeyException extends \LogicException implements AccessException {}
 
 
-/******************************************************************************/
-/*                                 ITERATORS                                  */
-/******************************************************************************/
-
-/**
- * Yields each key and value from $iter, after applying $mapper to the value.
- */
-function map(iterable $iter, callable $mapper): \Iterator
-{
-    foreach ($iter as $key => $val) {
-        yield $key => $mapper($val, $key, $iter);
-    }
-}
-
-/**
- * Yields each key and value from $iter for which $predicate returns TRUE.
- */
-function filter(iterable $iter, callable $predicate): \Iterator
-{
-    foreach ($iter as $key => $val) {
-        if ($predicate($val, $key, $iter)) {
-            yield $key => $val;
-        }
-    }
-}
-
-/**
- * Yields each key and value from $iter for which $predicate returns FALSE.
- */
-function reject(iterable $iter, callable $predicate): \Iterator
-{
-    foreach ($iter as $key => $val) {
-        if (!$predicate($val, $key, $iter)) {
-            yield $key => $val;
-        }
-    }
-}
-
-/**
- * @return mixed|null The key of the first iteration for which $predicate
- *                    returns TRUE, or NULL if none were found.
- */
-function find(iterable $iter, callable $predicate)
-{
-    foreach (filter($iter, $predicate) as $key => $val) {
-        return $key;
-    }
-
-    return null;
-}
-
-/**
- * Reduces the keys and values of $iter to a single result, carrying forward
- * intermediate results until the final iteration returns the final result.
- */
-function reduce(iterable $iter, callable $reducer, $initial = null)
-{
-    $result = $initial;
-
-    foreach ($iter as $key => $val) {
-        $result = $reducer($result, $value, $key, $iter);
-    }
-
-    return $result;
-}
-
-/**
- * @return bool TRUE if the $predicate returns TRUE for any iteration of $iter.
- *
- */
-function any(iterable $iter, callable $predicate = null): bool
-{
-    foreach (filter($iter) as $key => $val) {
-        return true;
-    }
-
-    return false;
-}
-
-/**
- * @return bool TRUE if the $predicate returns TRUE for all iterations of $iter,
- *                   or if $iter did not produce any iterations at all (empty).
- */
-function all(iterable $iter, callable $predicate = null): bool
-{
-    foreach (reject($iter) as $key => $val) {
-        return false;
-    }
-
-    return true;
-}
-
-/**
- * @todo I am not sure how viable this is, but it could be useful to find the
- *       kth smallest value without having to sort the entire container. We must
- *       be able to access the structure by offset in order to implement partial
- *       algorithms like quick-select.
- *
- * See: https://en.wikipedia.org/wiki/Partial_sorting
- */
-function sort(OffsetAccess $iter, callable $comparator = null): \Iterator
-{
-    // Partial sort...
-}
-
-
 /******************************************************************************/
 /*                                INTERFACES                                  */
 /******************************************************************************/
@@ -179,47 +73,6 @@ interface Container extends \Countable
     function isEmpty(): bool;
 }
 
-/**
- * An interface which mirrors some of the iterator functions.
- */
-interface Collection extends \Traversable
-{
-    /**
-     * @return static
-     */
-    function map(callable $mapper): Collection;
-
-    /**
-     * @return static
-     */
-    function filter(callable $predicate): Collection;
-
-    /**
-     * @return static
-     */
-    function reject(callable $predicate): Collection;
-
-    /**
-     * @return mixed|null
-     */
-    function find(callable $predicate);
-
-    /**
-     * @return mixed
-     */
-    function reduce(callable $reducer, $initial = null);
-
-    /**
-     * @return bool
-     */
-    function any(callable $predicate = null): bool;
-
-    /**
-     * @return bool
-     */
-    function all(callable $predicate = null): bool;
-}
-
 /**
  * Indicates that the elements of a structure can be represented by an array
  * without altering the structure of the data, without any warnings or errors.
@@ -597,9 +450,9 @@ function outorder(): Traversable;
  * Basic list, dynamic size, contiguous, no ops at the front.
  */
 final class Vector implements
+    IteratorAggregate,
     ArrayAccess,
     Arrayable,
-    Collection,
     Container,
     Clearable,
     Sortable,
@@ -634,9 +487,9 @@ final class Vector implements
  * like... *no ideas*
  */
 final class LinkedList implements
+    IteratorAggregate,
     ArrayAccess,
     Arrayable,
-    Collection,
     Container,
     Clearable,
     Sortable,
@@ -653,8 +506,8 @@ final class LinkedList implements
  *       is used as a key, and there will also be inconsistencies eg. "1" and 1.
  */
 final class HashMap implements
+    IteratorAggregate,
     ArrayAccess,
-    Collection,
     Container,
     Clearable,
     Sortable,
@@ -668,9 +521,9 @@ final class HashMap implements
  * will always preserve insertion order.
  */
 final class HashSet implements
+    IteratorAggregate,
     ArrayAccess,
     Arrayable,
-    Collection,
     Container,
     Clearable,
     Sortable,
@@ -687,8 +540,8 @@ final class HashSet implements
  * See: https://github.com/php-ds/ext-ds/issues/121
  */
 final class BinarySearchTree implements
+    IteratorAggregate,
     Arrayable,
-    Collection,
     Container,
     Clearable,
     Set,
@@ -698,7 +551,7 @@ final class BinarySearchTree implements
 /**
  * A first-in-last-out structure. Destructive traversal.
  */
-final class Stack implements Traversable, Arrayable, Container, Clearable
+final class Stack implements IteratorAggregate, Arrayable, Container, Clearable
 {
     public function push($value) {}
     public function pop() {}
@@ -708,7 +561,7 @@ public function peek() {}
 /**
  * A first-in-first-out structure. Destructive traversal.
  */
-final class Queue implements Traversable, Arrayable, Container, Clearable
+final class Queue implements IteratorAggregate, Arrayable, Container, Clearable
 {
     public function push($value) {}
     public function pop() {}
@@ -733,7 +586,7 @@ public function peek() {}
  *       In other words it should follow insertion order if no comparator was
  *       given, which Heap would not do.
  */
-final class PriorityQueue implements Traversable, Arrayable, Container, Clearable
+final class PriorityQueue implements IteratorAggregate, Arrayable, Container, Clearable
 {
     public function push($value, $priority = 0) {}
     public function pop() {}
@@ -744,7 +597,7 @@ public function peek() {}
  * Stable binary heap with an optional comparator, defaulting to minimum. Could
  * potentially remove the nullable which clears up the min/max ambiguity.
  */
-final class Heap implements Traversable, Arrayable, Container, Clearable
+final class Heap implements IteratorAggregate, Arrayable, Container, Clearable
 {
     public function __construct(callable $comparator = null) {}
 

From bc577a6863d00fedbd77c44384953a31119a3024 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 11 Feb 2019 22:31:00 -0800
Subject: [PATCH 17/50] Delete COW.php

---
 COW.php | 282 --------------------------------------------------------
 1 file changed, 282 deletions(-)
 delete mode 100644 COW.php

diff --git a/COW.php b/COW.php
deleted file mode 100644
index 065f682..0000000
--- a/COW.php
+++ /dev/null
@@ -1,282 +0,0 @@
-<?php
-
-interface Mappable
-{
-    /**
-     * @return static
-     */
-    function map(callable cb): Mappable;
-}
-
-
-/**
- * An iterator that yields values modified by a callback, preserving keys.
- */
-class MapIterator implements IteratorAggregate
-{
-    private $collection;
-    private $callback;
-
-    public function __construct(Mappable $collection, callable $callback)
-    {
-        $this->collection = $collection;
-        $this->callback   = $callback;
-    }
-
-    public function getIterator()
-    {
-        foreach ($this->collection as $key => $value) {
-            yield $key => $this->callback($value);
-        }
-    }
-}
-
-
-/**
- * An iterator that yields no values.
- */
-class EmptyIterator implements IteratorAggregate
-{
-    public function getIterator()
-    {
-        yield from [];
-    }
-}
-
-
-/**
- * A collection that is backed by an iterator which is only evaluated as needed.
- */
-abstract class LazyCollection implements IteratorAggregate, Mappable
-{
-    protected $iter;
-
-    public function __construct(Iterator $iter = null)
-    {
-        $this->iter = $iter ?? (new EmptyIterator);
-    }
-
-    /**
-     * Evaluates the remaining values in the iterator.
-     *
-     * @todo What happens when we rewind the iterator?
-     * @todo When we clone $this, what happens to the iterator?
-     */
-    abstract protected function evaluate(): Iterator;
-
-    /**
-     * Create a copy if referenced more than once, otherwise return self.
-     */
-    protected function copyOnWrite()
-    {
-        return (internal_refcount_func($this) > 1) ? clone $this : $this;
-    }
-
-    /**
-     * Generates a new collection from this collection keys and mapped values.
-     *
-     * @return static
-     */
-    public function map(callable $cb): Collection
-    {
-        /* External */
-        $MapIterator = function (Collection $c) {
-            foreach ($collection as $index => $value) {
-                yield $index => cb($value);
-            }
-        };
-
-        return new static(new MapIterator($this));
-    }
-}
-
-
-/**
- * Basic list, but lazy.
- */
-final class LazyList extends LazyCollection
-{
-    /**
-     * Internal storage.
-     */
-    private $data = [];
-
-    /**
-     * Evaluates the remaining values in the iterator.
-     */
-    protected function evaluate(): Iterator
-    {
-        for ($iter = $this->iter; $iter->valid(); $iter->next()) {
-            $value = $iter->current();
-
-            /* Add to the cache. */
-            $this->data[] = $value;
-
-            yield $value;
-        }
-    }
-
-    /**
-     * Appends a single value and returns the resulting Sequence, which could
-     * either be this instance or a clone.
-     */
-    public function push($value): Sequence
-    {
-        $result = $this->copyOnWrite();
-
-        $result->data[] = $value;
-
-        return $result;
-    }
-
-    /**
-     * Yield from cached data first, then from the iterator, caching as we go.
-     */
-    public function getIterator()
-    {
-        $index = 0;
-
-        foreach ($this->data as $value) {
-            yield $index++ => $value;
-        }
-
-        /* TODO: What happens when we rewind the iterator? */
-        foreach ($this->evaluate() as $value) {
-            yield $index++ => $value;
-        }
-    }
-}
-
-
-/**
- * Basic set of distinct values.
- */
-final class LazySet extends LazyCollection
-{
-    /**
-     * Internal storage.
-     */
-    private $data = [];
-
-    /**
-     * Evaluates the remaining values in the iterator.
-     */
-    protected function evaluate(): Iterator
-    {
-        for ($iter = $this->iter; $iter->valid(); $iter->next()) {
-            $value = $iter->current();
-
-            /* Do not include the value if already cached. */
-            if (isset($this->data[$value])) {
-                continue;
-            }
-
-            /* Add to the cache. */
-            $this->data[$value] = true;
-
-            yield $value;
-        }
-    }
-
-    /**
-     * Adds a value to this Set if the value is not already in this Set.
-     */
-    public function add($value): Sequence
-    {
-        /* Do not do anything at all if the operation has no effect. */
-        if (isset($this->data[$value])) {
-            return $this;
-        }
-
-        /* Evaluate the remaining values in the iterator */
-        foreach ($this->evaluate() as $candidate) {
-            if ($value === $candidate) {
-                return $this;
-            }
-        }
-
-        /* We are about to make an update, so check if we need to copy first. */
-        $result = $this->copyOnWrite();
-
-        /* Add the value to the set. */
-        $result->data[$value] = true;
-
-        return $result;
-    }
-
-    /**
-     * Determines if this Set contains the given value.
-     */
-    public function has($value): bool
-    {
-        if (isset($this->data[$value])) {
-            return true;
-        }
-
-        /* Evaluate the remaining values in the iterator */
-        foreach ($this->evaluate() as $candidate) {
-            if ($value === $candidate) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Iterator using a sequential index of the data store's keys as values.
-     */
-    public function getIterator()
-    {
-        $index = 0;
-
-        foreach ($this->data as $value => $_) {
-            yield $index++ => $value;
-        }
-
-        foreach ($this->evaluate() as $value) {
-            yield $index++ => $value;
-        }
-    }
-
-    /**
-     * Returns the values of this set as a list.
-     */
-    public function toList(): LazyList
-    {
-        return new LazyList($this);
-    }
-}
-
-
-$set = new LazySet();
-$set->add(1);
-$set->add(2);
-$set->add(3);
-
-// $set has not been copied at all because nothing else is referencing it yet.
-
-// This creates a list of all odd values in the set.
-// `toList` and `filter` are lazy (decorated iterators, no eager evaluation)
-$odds = $set->toList()->filter('isodd');
-// Refcount of $set is now 2.
-
-$copy = $set;
-// Refcount of $set is now 3.
-
-// When we update the copy, we are actually updating $set. But its refcount is 3,
-// so others depend on it. We can therefore not update the original, so we create
-// a copy instead by separating $other from $map (and copying the internal store).
-//
-// $odds refcount is 2
-// $copy refcount is 1
-$copy = $copy->add(5);
-
-// If we evaluate $odds, it builds the internal cache using the iterator.
-foreach ($odds as $odd) {} // 1, 3
-
-// Once all values have been cached by the list, the iterator is cleared.
-// This brings $odds' refcount back to 1.
-
-// We could have achieved this by using `$set->filter('isodd')` as well,
-

From c7969d3081085f4ceaba9f1b47a9cbf87b07b1ee Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Feb 2019 10:01:36 -0800
Subject: [PATCH 18/50] Map -> Dictionary, shortcuts, Pushable, Shiftable

---
 DRAFT.php | 643 +++++++++++++++++++++++++++---------------------------
 1 file changed, 317 insertions(+), 326 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 165e2fa..09c5cc2 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -18,6 +18,7 @@
  * @todo How do we handle comparison? Proposal: == for objects, === otherwise.
  */
 
+
 /******************************************************************************/
 /*                                EXCEPTIONS                                  */
 /******************************************************************************/
@@ -38,125 +39,25 @@ class IndexOutOfBoundsException extends \OutOfBoundsException implements AccessE
 /**
  * Should be thrown when an empty container is accessed a clear, obvious result.
  */
-class EmptyContainerException extends \LogicException implements AccessException {}
+class EmptyContainerException extends \RuntimeException implements AccessException {}
 
 /**
  * Should be thrown when an undefined zval is accessed. I do not except any user
  * classes to throw this, but it might be useful to catch it.
  */
-class UndefinedValueException extends \LogicException implements AccessException {}
+class UndefinedValueException extends \RuntimeException implements AccessException {}
 
 /**
  * Should be thrown when a key is not supported, eg. when an attempt is made to
- * associate a value with NULL in Map, causing ambiguity in `find`.
- */
-class InvalidKeyException extends \LogicException implements AccessException {}
-
-
-/******************************************************************************/
-/*                                 ITERATORS                                  */
-/******************************************************************************/
-
-/**
- * Yields each key and value from $iter, after applying $mapper to the value.
- */
-function map(iterable $iter, callable $mapper): \Iterator
-{
-    foreach ($iter as $key => $val) {
-        yield $key => $mapper($val, $key, $iter);
-    }
-}
-
-/**
- * Yields each key and value from $iter for which $predicate returns TRUE.
- */
-function filter(iterable $iter, callable $predicate): \Iterator
-{
-    foreach ($iter as $key => $val) {
-        if ($predicate($val, $key, $iter)) {
-            yield $key => $val;
-        }
-    }
-}
-
-/**
- * Yields each key and value from $iter for which $predicate returns FALSE.
- */
-function reject(iterable $iter, callable $predicate): \Iterator
-{
-    foreach ($iter as $key => $val) {
-        if (!$predicate($val, $key, $iter)) {
-            yield $key => $val;
-        }
-    }
-}
-
-/**
- * @return mixed|null The key of the first iteration for which $predicate
- *                    returns TRUE, or NULL if none were found.
- */
-function find(iterable $iter, callable $predicate)
-{
-    foreach (filter($iter, $predicate) as $key => $val) {
-        return $key;
-    }
-
-    return null;
-}
-
-/**
- * Reduces the keys and values of $iter to a single result, carrying forward
- * intermediate results until the final iteration returns the final result.
- */
-function reduce(iterable $iter, callable $reducer, $initial = null)
-{
-    $result = $initial;
-
-    foreach ($iter as $key => $val) {
-        $result = $reducer($result, $value, $key, $iter);
-    }
-
-    return $result;
-}
-
-/**
- * @return bool TRUE if the $predicate returns TRUE for any iteration of $iter.
- *
- */
-function any(iterable $iter, callable $predicate = null): bool
-{
-    foreach (filter($iter) as $key => $val) {
-        return true;
-    }
-
-    return false;
-}
-
-/**
- * @return bool TRUE if the $predicate returns TRUE for all iterations of $iter,
- *                   or if $iter did not produce any iterations at all (empty).
+ * associate a value with NULL in Dictionary, causing ambiguity in `find`.
  */
-function all(iterable $iter, callable $predicate = null): bool
-{
-    foreach (reject($iter) as $key => $val) {
-        return false;
-    }
-
-    return true;
-}
+class InvalidKeyException extends \RuntimeException implements AccessException {}
 
 /**
- * @todo I am not sure how viable this is, but it could be useful to find the
- *       kth smallest value without having to sort the entire container. We must
- *       be able to access the structure by offset in order to implement partial
- *       algorithms like quick-select.
- *
- * See: https://en.wikipedia.org/wiki/Partial_sorting
+ * Should be thrown when a value could not be found in a context where it was
+ * expected. This should only be used if an operation is undefined if not found.
  */
-function sort(OffsetAccess $iter, callable $comparator = null): \Iterator
-{
-    // Partial sort...
-}
+class ValueNotFoundException extends \RuntimeException implements AccessException {}
 
 
 /******************************************************************************/
@@ -179,47 +80,6 @@ interface Container extends \Countable
     function isEmpty(): bool;
 }
 
-/**
- * An interface which mirrors some of the iterator functions.
- */
-interface Collection extends \Traversable
-{
-    /**
-     * @return static
-     */
-    function map(callable $mapper): Collection;
-
-    /**
-     * @return static
-     */
-    function filter(callable $predicate): Collection;
-
-    /**
-     * @return static
-     */
-    function reject(callable $predicate): Collection;
-
-    /**
-     * @return mixed|null
-     */
-    function find(callable $predicate);
-
-    /**
-     * @return mixed
-     */
-    function reduce(callable $reducer, $initial = null);
-
-    /**
-     * @return bool
-     */
-    function any(callable $predicate = null): bool;
-
-    /**
-     * @return bool
-     */
-    function all(callable $predicate = null): bool;
-}
-
 /**
  * Indicates that the elements of a structure can be represented by an array
  * without altering the structure of the data, without any warnings or errors.
@@ -254,7 +114,7 @@ function reverse();
 }
 
 /**
- * Indicates that the elements of a structure can be sorted.
+ * Indicates that a structure can be sorted by value.
  *
  * This interface does not guarantee that the sorting algorithm will be stable.
  * Implementations should use a domain-specific tiebreaker when required.
@@ -267,6 +127,20 @@ interface Sortable
     function sort(callable $comparator = null);
 }
 
+/**
+ * Indicates that a structure can be sorted by key.
+ *
+ * This interface does not guarantee that the sorting algorithm will be stable.
+ * Implementations should use a domain-specific tiebreaker when required.
+ */
+interface SortableKeys
+{
+    /**
+     * @return static A sorted copy, or $this structure sorted in-place.
+     */
+    function ksort(callable $comparator = null);
+}
+
 /**
  * Indicates that an object is designed to be used in hash-based structures.
  */
@@ -299,48 +173,93 @@ function offset(int $index);
 }
 
 /**
- * Indicates that a structure contains a series of contiguous elements.
+ * Indicates that a structure can be appended to, ie. add or remove values at
+ * the end of the structure.
  */
-interface ReadOnlySequence extends OffsetAccess
+interface Pushable
 {
     /**
-     * @return mixed The first value in $this sequence.
-     */
-    function first();
-
-    /**
-     * @return mixed The last value in $this sequence.
+     * Appends the given value to the end of the structure.
      */
-    function last();
+    function push($value);
 
     /**
-     * @return static A partial sequence of $this.
+     * Removes and returns the value at the end of the structure.
+     *
+     * @return mixed The value that was removed from the end of the structure.
+     *
+     * @throws EmptyContainerException if the structure is empty.
+     *
+     * @todo should we leave it up to the impl to throw or return NULL? I think
+     *       we should aim for consistency by requiring a throw when empty. This
+     *       should be consistent with OffsetAccess' first and last.
      */
-    function slice(int $offset, int $length): ReadOnlySequence;
+    function pop();
 }
 
 /**
- * A sequence which can be modified, either in-place or as a copy.
+ * Indicates that a structure can be prepended to, ie. add or remove values at
+ * the start of the structure.
  */
-interface Sequence extends ReadOnlySequence
+interface Shiftable
 {
     /**
-     * Appends the given value to the end of the structure.
+     * Prepends the given value to the front of the structure.
      */
-    function push($value);
+    function unshift($value);
 
     /**
-     * Removes and returns the value at the end of the structure.
+     * Removes and returns the first value of the structure.
      *
-     * @return mixed The value that was removed from the end of the structure.
+     * @return mixed The value that was removed from the front of the structure.
      *
      * @throws EmptyContainerException if the structure is empty.
+     */
+    function shift();
+}
+
+/**
+ * Indicates that a structure has a defined "next" value, and that it can be
+ * produces without modifying the contents of the structure.
+ */
+interface Peekable
+{
+    /**
+     * Returns what is considered the "next" value in a structure.
      *
-     * @todo should we leave it up to the impl to throw or return NULL? I think
-     *       we should aim for consistency by requiring a throw when empty. This
-     *       should be consistent with OffsetAccess' first and last.
+     * @throws EmptyContainerException
      */
-    function pop();
+    function peek();
+}
+
+/**
+ * Indicates that a structure contains a series of contiguous elements.
+ */
+interface Sequence
+{
+    /**
+     * Returns the value at the given position.
+     *
+     * @throws IndexOutOfBoundsException if the index is not within [0, size)
+     */
+    function get(int $index);
+
+    /**
+     * @return mixed The first value in $this sequence.
+     */
+    function first();
+
+    /**
+     * @return mixed The last value in $this sequence.
+     */
+    function last();
+}
+
+/**
+ * A sequence which can be modified, either in-place or as a copy.
+ */
+interface MutableSequence extends Sequence
+{
     /**
      * Sets the value at the given position.
      *
@@ -362,64 +281,19 @@ function unset(int $index);
      * Moves all values between the given index and the end of the sequence one
      * position towards the back, then inserts the given value into the gap.
      *
-     * @todo "How do I insert the values of one sequence into another?"
-     *       I think we should consider variadic functions across the board,
-     *       so that we can do things like `$seq->insert(5, ...$other)` instead
-     *       of userland forloops passing a single argument every time.
-     *
-     * @todo We should also consider constructor syntax, like `new Vector(...?)`
-     *
-     * @todo Should $value be ...$values? (This applies to all push, unshift etc)
-     *
      * @throws IndexOutOfBoundsException if the index is out of range [0, size]
      */
     function insert(int $index, $value);
 }
 
-/**
- * Indicates that a structure is designed to operate efficiently at both the
- * front and the back of a linear dataset.
- */
-interface Deque extends Sequence
-{
-    /**
-     * Prepends the given value to the front of the structure.
-     */
-    function unshift($value);
-
-    /**
-     * Removes and returns the first value of the structure.
-     *
-     * @return mixed The value that was removed from the front of the structure.
-     *
-     * @throws EmptyContainerException if the structure is empty.
-     */
-    function shift();
-}
-
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
  *
  * Note: The intention will be that all implementations of Set will use a
  *       do_operation handler to override |, ^, -, &.
- *
- * @todo I'm not convinced that we need the four set operation methods, but we
- *       could assume that most applications that use Set will want to remove
- *       values from another set, or determine an intersection at some stage.
- *       I would rather implement them here, and maybe with operator support,
- *       and make use of the advantages we have internally.
- *
- *       We could also implement a set operation interface, which provides the
- *       four methods and operator overloading, but that would mean that not all
- *       implementations of Set support set operations.
- *
- *       What does this mean for Map? Should we not support the same use cases
- *       as array_key_intersect? If Set has these operations, should Map have
- *       methods like `diff`, `intersect`, etc to be consistent with arrays?
- *       If so, should the set operation methods share the same naming?
  */
-interface ReadOnlySet
+interface Set
 {
     /**
      * @return bool TRUE if the value is in $this set, FALSE otherwise.
@@ -469,7 +343,7 @@ function and(Set $other): Set;
 /**
  * A set which can be modified, either in-place or as a copy.
  */
-interface Set extends ReadOnlySet
+interface MutableSet extends Set
 {
     /**
      * Adds the given value to $this set if it is not already in $this set.
@@ -485,10 +359,8 @@ function remove($value);
 /**
  * A structure that associates one value with another and provides the ability
  * to query or adjust associations efficiently.
- *
- * @todo Naming - dictionary
  */
-interface ReadOnlyMap
+interface Dictionary
 {
     /**
      * @todo if NULL keys are not allowed, should we throw if $key is NULL?
@@ -510,12 +382,6 @@ function get($key, $default = null);
      *       here a bit redundant, where we would not get much value for the
      *       cost of the ambiguity. The other alternative is hasKey + hasValue,
      *       but that feels more specific than it needs to be.
-     *
-     * @todo What if we extend "Set" here? `has` would refer to the keys here
-     *       though, but I think that is valid and intuitive enough. We would
-     *       need to make sure that the set operation methods have names that
-     *       also apply to Map, without ambiguity or confusion. I get that this
-     *       is a bit strange but it makes sense to me contractually.
      */
     function has($key): bool;
 
@@ -539,7 +405,7 @@ function values(): Sequence;
 /**
  * A map which can be modified, either in-place or as a copy.
  */
-interface Map extends ReadOnlyMap
+interface MutableDictionary extends Dictionary
 {
     /**
      * Associates the $key with the $value, overriding any previous association.
@@ -565,7 +431,7 @@ function unset($key);
 /**
  * Indicates that a structure supports tree traversals.
  */
-interface Tree
+interface TreeTraversal
 {
     /**
      * Root, Left, Right
@@ -594,55 +460,80 @@ function outorder(): Traversable;
 /******************************************************************************/
 
 /**
- * Basic list, dynamic size, contiguous, no ops at the front.
+ * A low-level memory allocation abstraction which could be useful to build
+ * structures in userland that do not want to use arrays or other structures
+ * as a buffer.
+ *
+ * @todo how should we handle elements that are undefined, ie. a new allocation
+ *       will have undefined values. Internally these will be IS_UNDEF, but we
+ *       should not expose that to userland. Attempting to access an undefined
+ *       value will throw an exception.
+ *
+ * @todo What should clear do? I think that capacity should be preserved, but
+ *       all values should be set the their initial state, ie. undefined
+ */
+final class Allocation implements
+    ArrayAccess,
+    Clearable
+    {
+        /**
+         * Creates a new allocation using a given capacity.
+         */
+        public function __construct(int $capacity) {}
+
+        /**
+         * @return int The capacity of $this allocation.
+         */
+        public function capacity(): int {}
+
+        /**
+         * Adjusts the capacity of $this allocation, regardless of current capacity.
+         *
+         * @param int $capacity Resulting capacity, > 0
+         */
+        public function realloc(int $capacity): void {}
+    }
+
+/**
+ * List structure, dynamic size, contiguous, no ops at the front.
  */
 final class Vector implements
     ArrayAccess,
     Arrayable,
-    Collection,
     Container,
     Clearable,
     Sortable,
     Reversable,
-    Sequence
-    {}
+    Pushable,
+    OffsetAccess,
+    MutableSequence
+    {
+        /**
+         * Creates a new vector using values from $iter, if any.
+         */
+        public function __construct(iterable $iter = []) {}
+    }
 
 /**
- * Not necessarily literally a linked list, but has the same semantics.
- *
- * Very similar to vector, but does support operations at the front. Will most
- * likely be implemented as a circular buffer / "deque", but is an important
- * distinction because the go-to will be a Vector, except when you want to do
- * operations at the front, in which case you know you must use a LinkedList.
- *
- * This could be a bit annoying when you have `Sequence` throughout your app but
- * you really just want to remove the first element in a rare special case. You
- * have to either be able to slice from an index, or shift. Your Sequence is no
- * good because it can't be shifted. You would have to use the Deque interface
- * which Vectors don't support, so you go all the way up the chain to create a
- * LinkedList. This is either fine, or we should allow Vector to be a Deque also,
- * which enables poor performance and raises the question of "when should I use
- * LinkedList rather than Vector?"
- *
- * This structure (the deque, not the list) is my favourite and it would be hard
- * to delete all the work that was put into ext-ds. I believe that it is a core
- * structure because nothing else provides constant time add and remove at both
- * ends of the buffer. If we can agree that this structure has a place, all we
- * have to do is present it clearly. A linked list is fundamental, easy to under-
- * stand, and communicates the behaviour of the structure. If there is any
- * objection to effectively lying about the name, we should come up with another,
- * like... *no ideas*
- */
-final class LinkedList implements
+ * Double-ended-queue, supports prepend and append, but nothing in-between.
+ */
+final class Deque implements
     ArrayAccess,
     Arrayable,
-    Collection,
     Container,
     Clearable,
     Sortable,
     Reversable,
-    Deque
-    {}
+    Pushable,
+    Shiftable,
+    OffsetAccess,
+    Sequence
+    {
+        /**
+         * Creates a new deque using values from $iter, if any.
+         */
+        public function __construct(iterable $iter = []) {}
+    }
 
 /**
  * The standard hashtable, implemented as Map in ext-ds currently. It is based
@@ -654,14 +545,19 @@ final class LinkedList implements
  */
 final class HashMap implements
     ArrayAccess,
-    Collection,
     Container,
     Clearable,
     Sortable,
+    SortableKeys,
     Reversable,
     OffsetAccess,
-    Map
-    {}
+    MutableDictionary
+    {
+        /**
+         * Creates a new map using keys and values from $iter, if any.
+         */
+        public function __construct(iterable $iter = []) {}
+    }
 
 /**
  * The Set equivalent of HashMap, which might not use a HashMap internally, but
@@ -670,14 +566,19 @@ final class HashMap implements
 final class HashSet implements
     ArrayAccess,
     Arrayable,
-    Collection,
     Container,
     Clearable,
     Sortable,
     Reversable,
-    ReadOnlySequence,
-    Set
-    {}
+    OffsetAccess,
+    Sequence,
+    MutableSet
+    {
+        /**
+         * Creates a new set using values from $iter, if any.
+         */
+        public function __construct(iterable $iter = []) {}
+    }
 
 /**
  * A structure that is always sorted and does not allow duplicate elements.
@@ -688,89 +589,179 @@ final class HashSet implements
  */
 final class BinarySearchTree implements
     Arrayable,
-    Collection,
     Container,
     Clearable,
-    Set,
-    Tree,
-    {}
+    OffsetAccess,
+    TreeTraversal,
+    MutableSet,
+    {
+        /**
+         * Creates a new bst using values from $iter, if any.
+         */
+        public function __construct(iterable $iter = []) {}
+    }
+
+/**
+ * A first-in-last-out structure.
+ */
+final class Stack implements
+    Container,
+    Clearable,
+    Pushable,
+    Peekable
+    {
+        /**
+         * Creates a new stack using values from $iter, if any.
+         */
+        public function __construct(iterable $iter = []) {}
+    }
+
+/**
+ * A first-in-first-out structure.
+ */
+final class Queue implements
+    Container,
+    Clearable,
+    Pushable,
+    Peekable
+    {
+        /**
+         * Creates a new queue using values from $iter, if any.
+         */
+        public function __construct(iterable $iter = []) {}
+    }
+
+/**
+ * Heap-based queue with a mixed-type priority value associated with the key.
+ *
+ * @todo Should we support duplicate values?
+ */
+final class PriorityQueue implements
+    Container,
+    Clearable,
+    Pushable,
+    Peekable
+    {
+        /**
+         * Creates a new priority queue using values from $iter, if any.
+         */
+        public function __construct(iterable $iter = []) {}
+
+        /**
+         * Adds a value to the queue, using a given priority.
+         */
+        public function push($value, $priority = null) {}
+
+        /**
+         * Updates the priority of the first encountered instance of $value.
+         *
+         * @throws ValueNotFoundException
+         */
+        public function update($value, $priority) {}
+    }
+
+/**
+ * Stable heap with an optional comparator, defaulting to minimum. We could
+ * potentially remove the nullable which clears up the min/max ambiguity.
+ */
+final class Heap implements
+    Arrayable,
+    Container,
+    Clearable,
+    Pushable,
+    Peekable
+    {
+        /**
+         * Creates a new heap using values from $iter, if any.
+         */
+        public function __construct(iterable $iter = [], callable $comparator = null) {}
+    }
+
+
+/******************************************************************************/
+/*                                 FUNCTIONS                                  */
+/******************************************************************************/
 
 /**
- * A first-in-last-out structure. Destructive traversal.
+ * Shortcut to create a new allocation.
  */
-final class Stack implements Traversable, Arrayable, Container, Clearable
+function alloc(int $capacity): Allocation
 {
-    public function push($value) {}
-    public function pop() {}
-    public function peek() {}
+    return new Allocation($capacity);
 }
 
 /**
- * A first-in-first-out structure. Destructive traversal.
+ * Shortcut to create a new sequence.
  */
-final class Queue implements Traversable, Arrayable, Container, Clearable
+function seq(...$v): MutableSequence
 {
-    public function push($value) {}
-    public function pop() {}
-    public function peek() {}
+    return new Vector($v);
 }
 
 /**
- * Binary heap-based queue with a mixed-type priority value associated with the
- * key. Iteration is destructive.
- *
- * @todo Should we provide the ability to adjust the priority of a value? I think
- *       it would be O(N) in a heap, and introduces ambiguity around duplicate
- *       values. Should it adjust them all, or just the first?
- *
- * @todo Should we support duplicate values at all?
- *
- * @todo Why do we need this if we already have a heap?
- *       Heap supports a comparator, and is guaranteed to be stable. However,
- *       in order to impelement Queue we would HAVE to pass a callable that
- *       always returns 0, falling back to insertion order tiebreak. This feels
- *       hacky, and I think the semantics are stronger with a dedicated type.
- *       In other words it should follow insertion order if no comparator was
- *       given, which Heap would not do.
- */
-final class PriorityQueue implements Traversable, Arrayable, Container, Clearable
+ * Shortcut to create a new deque.
+ */
+function deque(...$v): Deque
 {
-    public function push($value, $priority = 0) {}
-    public function pop() {}
-    public function peek() {}
+    return new Deque($v);
 }
 
 /**
- * Stable binary heap with an optional comparator, defaulting to minimum. Could
- * potentially remove the nullable which clears up the min/max ambiguity.
+ * Shortcut to create a new set.
  */
-final class Heap implements Traversable, Arrayable, Container, Clearable
+function set(...$v): MutableSet
 {
-    public function __construct(callable $comparator = null) {}
+    return new HashSet($v);
+}
 
-    public function push($value) {}
-    public function pop() {}
-    public function peek() {}
+/**
+ * Shortcut to create a new dictionary.
+ */
+function dict(...$v): MutableDictionary
+{
+    return new HashMap($v);
 }
 
 /**
- * A low-level memory allocation abstraction which could be useful to build
- * structures in userland that do not want to use arrays or other structures
- * as a buffer.
- *
- * @todo how should we handle elements that are undefined, ie. a new allocation
- *       will have undefined values. Internally these will be IS_UNDEF, but we
- *       should not expose that to userland. Attempting to access an undefined
- *       value will throw an exception.
- *
- * @todo What should clear do? I think that capacity should be preserved, but
- *       all values should be set the their initial state, ie. undefined
+ * Shortcut to create a new binary search tree.
+ */
+function bst(...$v): BinarySearchTree
+{
+    return new BinarySearchTree($v);
+}
+
+/**
+ * Shortcut to create a new stack.
+ */
+function stack(...$v): Stack
+{
+    return new Stack($v);
+}
+
+/**
+ * Shortcut to create a new queue.
+ */
+function queue(...$v): Queue
+{
+    return new Queue($v);
+}
+
+/**
+ * Shortcut to create a new priority queue.
  */
-final class Allocation implements ArrayAccess, Clearable
+function pqueue(...$v): PriorityQueue
 {
-    public function __construct(int $capacity) {}
+    return new PriorityQueue($v);
+}
 
-    public function capacity(): int {}
-    public function realloc(int): void {}
-    public function clear(): void {}
+/**
+ * Shortcut to create a new heap.
+ *
+ * @todo We can't support variadic values before the comparator, so either we
+ *       do not provide this shortcut, or we do not provide a way to initialize.
+ */
+function heap(callable $comparator = null): Heap
+{
+    return new Heap([], $comparator);
 }
+

From 22cbd4bef6cd4601d68439fcbf84ebd397e6b33f Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Feb 2019 16:34:04 -0800
Subject: [PATCH 19/50] Deque impl Queue, Vector impl Stack

---
 DRAFT.php | 515 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 300 insertions(+), 215 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 09c5cc2..8928f52 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -16,9 +16,10 @@
 
 /**
  * @todo How do we handle comparison? Proposal: == for objects, === otherwise.
+ * @todo send/poll interface?
+ * @todo Which methods should accept variadic arguments?
  */
 
-
 /******************************************************************************/
 /*                                EXCEPTIONS                                  */
 /******************************************************************************/
@@ -39,7 +40,7 @@ class IndexOutOfBoundsException extends \OutOfBoundsException implements AccessE
 /**
  * Should be thrown when an empty container is accessed a clear, obvious result.
  */
-class EmptyContainerException extends \RuntimeException implements AccessException {}
+class EmptyContainerException extends \UnderflowException implements AccessException {}
 
 /**
  * Should be thrown when an undefined zval is accessed. I do not except any user
@@ -168,74 +169,25 @@ interface OffsetAccess extends \Countable
      * @throws IndexOutOfBoundsException if the index is not within [0, size).
      *
      * @todo Potential for a better name, like "skip" or "atIndex" or "at"?
+     *       Because it clashes with ArrayAccess a little bit here.
      */
     function offset(int $index);
-}
-
-/**
- * Indicates that a structure can be appended to, ie. add or remove values at
- * the end of the structure.
- */
-interface Pushable
-{
-    /**
-     * Appends the given value to the end of the structure.
-     */
-    function push($value);
 
     /**
-     * Removes and returns the value at the end of the structure.
-     *
-     * @return mixed The value that was removed from the end of the structure.
-     *
-     * @throws EmptyContainerException if the structure is empty.
-     *
-     * @todo should we leave it up to the impl to throw or return NULL? I think
-     *       we should aim for consistency by requiring a throw when empty. This
-     *       should be consistent with OffsetAccess' first and last.
-     */
-    function pop();
-}
-
-/**
- * Indicates that a structure can be prepended to, ie. add or remove values at
- * the start of the structure.
- */
-interface Shiftable
-{
-    /**
-     * Prepends the given value to the front of the structure.
-     */
-    function unshift($value);
-
-    /**
-     * Removes and returns the first value of the structure.
-     *
-     * @return mixed The value that was removed from the front of the structure.
-     *
-     * @throws EmptyContainerException if the structure is empty.
+     * @return mixed The first value in $this structure.
      */
-    function shift();
-}
+    function first();
 
-/**
- * Indicates that a structure has a defined "next" value, and that it can be
- * produces without modifying the contents of the structure.
- */
-interface Peekable
-{
     /**
-     * Returns what is considered the "next" value in a structure.
-     *
-     * @throws EmptyContainerException
+     * @return mixed The last value in $this structure.
      */
-    function peek();
+    function last();
 }
 
 /**
  * Indicates that a structure contains a series of contiguous elements.
  */
-interface Sequence
+interface Sequence extends OffsetAccess
 {
     /**
      * Returns the value at the given position.
@@ -243,16 +195,6 @@ interface Sequence
      * @throws IndexOutOfBoundsException if the index is not within [0, size)
      */
     function get(int $index);
-
-    /**
-     * @return mixed The first value in $this sequence.
-     */
-    function first();
-
-    /**
-     * @return mixed The last value in $this sequence.
-     */
-    function last();
 }
 
 /**
@@ -428,6 +370,40 @@ function set($key, $value);
     function unset($key);
 }
 
+/**
+ * Indicates that a structure has first-in-first-out semantics.
+ */
+interface Queue
+{
+    /**
+     * Adds a value to the queue.
+     */
+    function push($value);
+
+    /**
+     * Removes and returns the next value in the queue.
+     */
+    function shift();
+}
+
+/**
+ * Indicates that a structure has first-in-last-out semantics.
+ */
+interface Stack
+{
+    /**
+     * Adds a value to the top of the stack.
+     */
+    public function push($value) {}
+
+    /**
+     * Removes and returns the value at the top of the stack.
+     *
+     * @throws EmptyContainerException
+     */
+    public function pop() {}
+}
+
 /**
  * Indicates that a structure supports tree traversals.
  */
@@ -492,6 +468,15 @@ public function capacity(): int {}
          * @param int $capacity Resulting capacity, > 0
          */
         public function realloc(int $capacity): void {}
+
+        /* ArrayAccess */
+        // offsetGet
+        // offsetSet
+        // offsetUnset
+        // offsetExists
+
+        /* Clearable */
+        // clear
     }
 
 /**
@@ -504,14 +489,49 @@ final class Vector implements
     Clearable,
     Sortable,
     Reversable,
-    Pushable,
-    OffsetAccess,
-    MutableSequence
+    MutableSequence,
+    Stack
     {
         /**
-         * Creates a new vector using values from $iter, if any.
+         * Creates a new vector using values from $iter.
          */
-        public function __construct(iterable $iter = []) {}
+        public static function from(iterable $iter): self {}
+
+        /* ArrayAccess */
+        // offsetGet
+        // offsetSet
+        // offsetUnset
+        // offsetExists
+
+        /* Arrayable */
+        // toArray
+
+        /* Countable | Container */
+        // count
+        // isEmpty
+
+        /* Clearable */
+        // clear
+
+        /* Sortable */
+        // sort
+
+        /* Reversable */
+        // reverse
+
+        /* OffsetAccess | Sequence | MutableSequence */
+        // offset
+        // first
+        // last
+        //
+        // get
+        // set
+        // unset
+        // insert
+
+        /* Stack */
+        // push
+        // pop
     }
 
 /**
@@ -522,17 +542,58 @@ final class Deque implements
     Arrayable,
     Container,
     Clearable,
-    Sortable,
-    Reversable,
-    Pushable,
-    Shiftable,
-    OffsetAccess,
-    Sequence
+    Sequence,
+    Queue
     {
         /**
-         * Creates a new deque using values from $iter, if any.
+         * Creates a new deque using values from $iter.
+         */
+        public static function from(iterable $iter): self {}
+
+        /**
+         * Removes and returns the value at the back of the deque.
+         *
+         * @throws EmptyContainerException
+         */
+        public function pop();
+
+        /**
+         * Adds a value to the front of the deque.
          */
-        public function __construct(iterable $iter = []) {}
+        public function unshift($value);
+
+        /* ArrayAccess */
+        // offsetGet
+        // offsetSet
+        // offsetUnset
+        // offsetExists
+
+        /* Arrayable */
+        // toArray
+
+        /* Countable | Container */
+        // count
+        // isEmpty
+
+        /* Clearable */
+        // clear
+
+        /* Sortable */
+        // sort
+
+        /* Reversable */
+        // reverse
+
+        /* OffsetAccess | Sequence */
+        // offset
+        // first
+        // last
+        //
+        // get
+
+        /* Queue */
+        // push
+        // shift
     }
 
 /**
@@ -554,9 +615,45 @@ final class HashMap implements
     MutableDictionary
     {
         /**
-         * Creates a new map using keys and values from $iter, if any.
+         * Creates a new dictionary using keys and values from $iter.
          */
-        public function __construct(iterable $iter = []) {}
+        public static function from(iterable $iter): self {}
+
+        /* ArrayAccess */
+        // offsetGet
+        // offsetSet
+        // offsetUnset
+        // offsetExists
+
+        /* Countable | Container */
+        // count
+        // isEmpty
+
+        /* Clearable */
+        // clear
+
+        /* Sortable */
+        // sort
+
+        /* SortableKeys */
+        // ksort
+
+        /* Reversable */
+        // reverse
+
+        /* OffsetAccess */
+        // offset
+        // first
+        // last
+
+        /* Dictionary | MutableDictionary */
+        // get
+        // has
+        // keys
+        // values
+        //
+        // set
+        // unset
     }
 
 /**
@@ -571,13 +668,49 @@ final class HashSet implements
     Sortable,
     Reversable,
     OffsetAccess,
-    Sequence,
     MutableSet
     {
         /**
-         * Creates a new set using values from $iter, if any.
+         * Creates a new set using values from $iter.
          */
-        public function __construct(iterable $iter = []) {}
+        public static function from(iterable $iter): self {}
+
+        /* ArrayAccess */
+        // offsetGet
+        // offsetSet
+        // offsetUnset
+        // offsetExists
+
+        /* Arrayable */
+        // toArray
+
+        /* Container | Countable */
+        // count
+        // isEmpty
+
+        /* Clearable */
+        // clear
+
+        /* Sortable */
+        // sort
+
+        /* Reversable */
+        // reverse
+
+        /* OffsetAccess */
+        // offset
+        // first
+        // last
+
+        /* Set | MutableSet */
+        // has
+        // or
+        // xor
+        // not
+        // and
+        //
+        // add
+        // remove
     }
 
 /**
@@ -591,44 +724,39 @@ final class BinarySearchTree implements
     Arrayable,
     Container,
     Clearable,
-    OffsetAccess,
     TreeTraversal,
     MutableSet,
     {
         /**
-         * Creates a new bst using values from $iter, if any.
+         * Creates a new bst using values from $iter.
          */
-        public function __construct(iterable $iter = []) {}
-    }
-
-/**
- * A first-in-last-out structure.
- */
-final class Stack implements
-    Container,
-    Clearable,
-    Pushable,
-    Peekable
-    {
-        /**
-         * Creates a new stack using values from $iter, if any.
-         */
-        public function __construct(iterable $iter = []) {}
-    }
-
-/**
- * A first-in-first-out structure.
- */
-final class Queue implements
-    Container,
-    Clearable,
-    Pushable,
-    Peekable
-    {
-        /**
-         * Creates a new queue using values from $iter, if any.
-         */
-        public function __construct(iterable $iter = []) {}
+        public static function from(iterable $iter): self {}
+
+        /* Arrayable */
+        // toArray
+
+        /* Container | Countable */
+        // count
+        // isEmpty
+
+        /* Clearable */
+        // clear
+
+        /* TreeTraversal */
+        // preorder
+        // inorder
+        // postorder
+        // outorder
+
+        /* Set | MutableSet */
+        // has
+        // or
+        // xor
+        // not
+        // and
+        //
+        // add
+        // remove
     }
 
 /**
@@ -638,26 +766,44 @@ public function __construct(iterable $iter = []) {}
  */
 final class PriorityQueue implements
     Container,
-    Clearable,
-    Pushable,
-    Peekable
+    Clearable
     {
         /**
-         * Creates a new priority queue using values from $iter, if any.
+         * Creates a new queue using values from $iter.
          */
-        public function __construct(iterable $iter = []) {}
+        public static function from(iterable $iter): self {}
 
         /**
          * Adds a value to the queue, using a given priority.
          */
-        public function push($value, $priority = null) {}
+        public function push($value, $priority) {}
 
         /**
-         * Updates the priority of the first encountered instance of $value.
+         * Updates the priority of all found instances of $value.
          *
-         * @throws ValueNotFoundException
+         * @throws ValueNotFoundException if none were found.
          */
         public function update($value, $priority) {}
+
+        /**
+         * Removes and returns the next value in the queue, ie. the value with
+         * the next highest priority.
+         */
+        public function shift() {}
+
+        /**
+         * Returns the next value in the queue without removing it.
+         *
+         * @throws EmptyContainerException
+         */
+        public function peek() {}
+
+        /* Container | Countable */
+        // count
+        // isEmpty
+
+        /* Clearable */
+        // clear
     }
 
 /**
@@ -665,103 +811,42 @@ public function update($value, $priority) {}
  * potentially remove the nullable which clears up the min/max ambiguity.
  */
 final class Heap implements
-    Arrayable,
     Container,
-    Clearable,
-    Pushable,
-    Peekable
+    Clearable
     {
         /**
          * Creates a new heap using values from $iter, if any.
          */
-        public function __construct(iterable $iter = [], callable $comparator = null) {}
-    }
-
-
-/******************************************************************************/
-/*                                 FUNCTIONS                                  */
-/******************************************************************************/
+        public function __construct(callable $comparator = null) {}
 
-/**
- * Shortcut to create a new allocation.
- */
-function alloc(int $capacity): Allocation
-{
-    return new Allocation($capacity);
-}
-
-/**
- * Shortcut to create a new sequence.
- */
-function seq(...$v): MutableSequence
-{
-    return new Vector($v);
-}
-
-/**
- * Shortcut to create a new deque.
- */
-function deque(...$v): Deque
-{
-    return new Deque($v);
-}
-
-/**
- * Shortcut to create a new set.
- */
-function set(...$v): MutableSet
-{
-    return new HashSet($v);
-}
-
-/**
- * Shortcut to create a new dictionary.
- */
-function dict(...$v): MutableDictionary
-{
-    return new HashMap($v);
-}
-
-/**
- * Shortcut to create a new binary search tree.
- */
-function bst(...$v): BinarySearchTree
-{
-    return new BinarySearchTree($v);
-}
+        /**
+         * Creates a new heap using values from $iter.
+         */
+        public static function from(iterable $iter, callable $comparator = null): self {}
 
-/**
- * Shortcut to create a new stack.
- */
-function stack(...$v): Stack
-{
-    return new Stack($v);
-}
+        /**
+         * Adds a value to the heap.
+         */
+        public function push($value) {}
 
-/**
- * Shortcut to create a new queue.
- */
-function queue(...$v): Queue
-{
-    return new Queue($v);
-}
+        /**
+         * Removes and returns the value at the top of the heap.
+         *
+         * @throws EmptyContainerException
+         */
+        public function shift() {}
 
-/**
- * Shortcut to create a new priority queue.
- */
-function pqueue(...$v): PriorityQueue
-{
-    return new PriorityQueue($v);
-}
+        /**
+         * Returns the value at the top of the heap without removing it.
+         *
+         * @throws EmptyContainerException
+         */
+        public function peek() {}
 
-/**
- * Shortcut to create a new heap.
- *
- * @todo We can't support variadic values before the comparator, so either we
- *       do not provide this shortcut, or we do not provide a way to initialize.
- */
-function heap(callable $comparator = null): Heap
-{
-    return new Heap([], $comparator);
-}
+        /* Container | Countable */
+        // count
+        // isEmpty
 
+        /* Clearable */
+        // clear
+    }

From 091cba2cbde2a222bba1a9a56f6ed1b8b94ade15 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Feb 2019 16:41:57 -0800
Subject: [PATCH 20/50] Add variadics, remove Sequence from Deque

---
 DRAFT.php | 48 ++++++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 26 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 8928f52..4965dee 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -16,8 +16,7 @@
 
 /**
  * @todo How do we handle comparison? Proposal: == for objects, === otherwise.
- * @todo send/poll interface?
- * @todo Which methods should accept variadic arguments?
+ * @todo give/take interface?
  */
 
 /******************************************************************************/
@@ -32,21 +31,26 @@
 interface AccessException {}
 
 /**
- * Should be thrown when an index or key is not within the given access bounds
- * of a structure, such as a negative index on a list or
+ * Should be thrown when a structure can not contain more values (is full).
  */
-class IndexOutOfBoundsException extends \OutOfBoundsException implements AccessException  {}
+class FullContainerException extends \OverflowException {}
 
 /**
  * Should be thrown when an empty container is accessed a clear, obvious result.
  */
 class EmptyContainerException extends \UnderflowException implements AccessException {}
 
+/**
+ * Should be thrown when an index or key is not within the given access bounds
+ * of a structure, such as a negative index on a list or
+ */
+class IndexOutOfBoundsException extends \OutOfBoundsException implements AccessException  {}
+
 /**
  * Should be thrown when an undefined zval is accessed. I do not except any user
  * classes to throw this, but it might be useful to catch it.
  */
-class UndefinedValueException extends \RuntimeException implements AccessException {}
+class InvalidAccessException extends \RuntimeException implements AccessException {}
 
 /**
  * Should be thrown when a key is not supported, eg. when an attempt is made to
@@ -220,12 +224,12 @@ function set(int $index, $value);
     function unset(int $index);
 
     /**
-     * Moves all values between the given index and the end of the sequence one
-     * position towards the back, then inserts the given value into the gap.
+     * Moves all values between the given index and the end of the sequence
+     * towards the back, then inserts the given values into the gap.
      *
      * @throws IndexOutOfBoundsException if the index is out of range [0, size]
      */
-    function insert(int $index, $value);
+    function insert(int $index, ...$values);
 }
 
 /**
@@ -290,10 +294,10 @@ interface MutableSet extends Set
     /**
      * Adds the given value to $this set if it is not already in $this set.
      */
-    function add($value);
+    function add(...$values);
 
     /**
-     * @todo what happens when the value could not be found? Silent no-op?
+     * @todo what happens when the value could not be found? Silent no-op? bool?
      */
     function remove($value);
 }
@@ -378,7 +382,7 @@ interface Queue
     /**
      * Adds a value to the queue.
      */
-    function push($value);
+    function push(...$values);
 
     /**
      * Removes and returns the next value in the queue.
@@ -394,14 +398,14 @@ interface Stack
     /**
      * Adds a value to the top of the stack.
      */
-    public function push($value) {}
+    function push(...$values) {}
 
     /**
      * Removes and returns the value at the top of the stack.
      *
      * @throws EmptyContainerException
      */
-    public function pop() {}
+    function pop() {}
 }
 
 /**
@@ -542,7 +546,7 @@ final class Deque implements
     Arrayable,
     Container,
     Clearable,
-    Sequence,
+    OffsetAccess,
     Queue
     {
         /**
@@ -560,7 +564,7 @@ public function pop();
         /**
          * Adds a value to the front of the deque.
          */
-        public function unshift($value);
+        public function unshift(...$values);
 
         /* ArrayAccess */
         // offsetGet
@@ -578,18 +582,10 @@ public function unshift($value);
         /* Clearable */
         // clear
 
-        /* Sortable */
-        // sort
-
-        /* Reversable */
-        // reverse
-
-        /* OffsetAccess | Sequence */
+        /* OffsetAccess */
         // offset
         // first
         // last
-        //
-        // get
 
         /* Queue */
         // push
@@ -827,7 +823,7 @@ public static function from(iterable $iter, callable $comparator = null): self {
         /**
          * Adds a value to the heap.
          */
-        public function push($value) {}
+        public function push(...$values) {}
 
         /**
          * Removes and returns the value at the top of the heap.

From 155569f351be0b156ea8e7920eaa321c7bd08d9d Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Feb 2019 16:44:24 -0800
Subject: [PATCH 21/50] Remove ArrayAccess from Deque

---
 DRAFT.php | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 4965dee..8fb50f6 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -542,7 +542,6 @@ public static function from(iterable $iter): self {}
  * Double-ended-queue, supports prepend and append, but nothing in-between.
  */
 final class Deque implements
-    ArrayAccess,
     Arrayable,
     Container,
     Clearable,
@@ -566,12 +565,6 @@ public function pop();
          */
         public function unshift(...$values);
 
-        /* ArrayAccess */
-        // offsetGet
-        // offsetSet
-        // offsetUnset
-        // offsetExists
-
         /* Arrayable */
         // toArray
 

From 66ffdcbd0e2049fbf6a16af2c257daf57b3a669f Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Feb 2019 18:19:45 -0800
Subject: [PATCH 22/50] Remove FullContainerException

---
 DRAFT.php | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 8fb50f6..402aee5 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -30,11 +30,6 @@
  */
 interface AccessException {}
 
-/**
- * Should be thrown when a structure can not contain more values (is full).
- */
-class FullContainerException extends \OverflowException {}
-
 /**
  * Should be thrown when an empty container is accessed a clear, obvious result.
  */

From 1198594e77c4633cc18548ec79e7d7aeb8c2f9b3 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 16 Feb 2019 00:10:31 -0800
Subject: [PATCH 23/50] Add SortedSet marker

---
 DRAFT.php | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/DRAFT.php b/DRAFT.php
index 402aee5..b934cc1 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -297,6 +297,14 @@ function add(...$values);
     function remove($value);
 }
 
+/**
+ * Indicates that a structure is a set that is always sorted. 
+ */
+interface SortedSet extends Set {} 
+
+
+
+
 /**
  * A structure that associates one value with another and provides the ability
  * to query or adjust associations efficiently.
@@ -710,6 +718,7 @@ final class BinarySearchTree implements
     Clearable,
     TreeTraversal,
     MutableSet,
+    SortedSet
     {
         /**
          * Creates a new bst using values from $iter.

From 17a089aef21bc6ffe5e76566c29987a9581ae515 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 16 Feb 2019 21:37:00 -0800
Subject: [PATCH 24/50] Map to Dictionary, inline tree traversals, add Tuple,
 Immutable, SortedSet

---
 DRAFT.php | 143 ++++++++++++++++++++++++++++++++----------------------
 1 file changed, 85 insertions(+), 58 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index b934cc1..f938ac9 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -14,6 +14,13 @@
 */
 namespace Ds;
 
+use ArrayAccess;
+use Countable;
+use OutOfBoundsException;
+use RuntimeException;
+use Traversable;
+use UnderflowException;
+
 /**
  * @todo How do we handle comparison? Proposal: == for objects, === otherwise.
  * @todo give/take interface?
@@ -33,31 +40,31 @@ interface AccessException {}
 /**
  * Should be thrown when an empty container is accessed a clear, obvious result.
  */
-class EmptyContainerException extends \UnderflowException implements AccessException {}
+class EmptyContainerException extends UnderflowException implements AccessException {}
 
 /**
  * Should be thrown when an index or key is not within the given access bounds
  * of a structure, such as a negative index on a list or
  */
-class IndexOutOfBoundsException extends \OutOfBoundsException implements AccessException  {}
+class IndexOutOfBoundsException extends OutOfBoundsException implements AccessException  {}
 
 /**
  * Should be thrown when an undefined zval is accessed. I do not except any user
  * classes to throw this, but it might be useful to catch it.
  */
-class InvalidAccessException extends \RuntimeException implements AccessException {}
+class InvalidAccessException extends RuntimeException implements AccessException {}
 
 /**
  * Should be thrown when a key is not supported, eg. when an attempt is made to
- * associate a value with NULL in Dictionary, causing ambiguity in `find`.
+ * associate a value with NULL in Map, causing ambiguity in `find`.
  */
-class InvalidKeyException extends \RuntimeException implements AccessException {}
+class InvalidKeyException extends RuntimeException implements AccessException {}
 
 /**
  * Should be thrown when a value could not be found in a context where it was
  * expected. This should only be used if an operation is undefined if not found.
  */
-class ValueNotFoundException extends \RuntimeException implements AccessException {}
+class ValueNotFoundException extends RuntimeException implements AccessException {}
 
 
 /******************************************************************************/
@@ -65,20 +72,9 @@ class ValueNotFoundException extends \RuntimeException implements AccessExceptio
 /******************************************************************************/
 
 /**
- * Indicates that a structure contains elements and is aware of the number of
- * items contained at any point in time. How a container must determine the
- * count is implementation-specific.
- *
- * This interface does not imply that a structure is traversable.
+ * Marker interface to indicate that a class is immutable.
  */
-interface Container extends \Countable
-{
-    /**
-     * @return bool whether the container is empty, which can be TRUE even when
-     *              the number of elements is non-zero.
-     */
-    function isEmpty(): bool;
-}
+interface Immutable {}
 
 /**
  * Indicates that the elements of a structure can be represented by an array
@@ -144,7 +140,7 @@ function ksort(callable $comparator = null);
 /**
  * Indicates that an object is designed to be used in hash-based structures.
  */
-interface Hashable
+interface Hashable extends Immutable
 {
     /**
      * @return mixed A scalar value (or hashable delegate) that will be hashed.
@@ -160,7 +156,7 @@ function getHashSource();
  *
  * @todo Naming - OrdinalAccess, LinearAccess, RandomAccess...
  */
-interface OffsetAccess extends \Countable
+interface OffsetAccess extends Countable
 {
     /**
      * @return mixed The value at the
@@ -183,6 +179,22 @@ function first();
     function last();
 }
 
+/**
+ * Indicates that a structure contains elements and is aware of the number of
+ * items contained at any point in time. How a container must determine the
+ * count is implementation-specific.
+ *
+ * This interface does not imply that a structure is traversable.
+ */
+interface Container extends Countable
+{
+    /**
+     * @return bool whether the container is empty, which can be TRUE even when
+     *              the number of elements is non-zero.
+     */
+    function isEmpty(): bool;
+}
+
 /**
  * Indicates that a structure contains a series of contiguous elements.
  */
@@ -302,14 +314,11 @@ function remove($value);
  */
 interface SortedSet extends Set {} 
 
-
-
-
 /**
  * A structure that associates one value with another and provides the ability
  * to query or adjust associations efficiently.
  */
-interface Dictionary
+interface Map
 {
     /**
      * @todo if NULL keys are not allowed, should we throw if $key is NULL?
@@ -354,7 +363,7 @@ function values(): Sequence;
 /**
  * A map which can be modified, either in-place or as a copy.
  */
-interface MutableDictionary extends Dictionary
+interface MutableMap extends Map
 {
     /**
      * Associates the $key with the $value, overriding any previous association.
@@ -401,40 +410,14 @@ interface Stack
     /**
      * Adds a value to the top of the stack.
      */
-    function push(...$values) {}
+    function push(...$values);
 
     /**
      * Removes and returns the value at the top of the stack.
      *
      * @throws EmptyContainerException
      */
-    function pop() {}
-}
-
-/**
- * Indicates that a structure supports tree traversals.
- */
-interface TreeTraversal
-{
-    /**
-     * Root, Left, Right
-     */
-    function preorder(): Traversable;
-
-    /**
-     * Left, Root, Right
-     */
-    function inorder(): Traversable;
-
-    /**
-     * Left, Right, Root
-     */
-    function postorder(): Traversable;
-
-    /**
-     * Right, Root, Left
-     */
-    function outorder(): Traversable;
+    function pop();
 }
 
 
@@ -486,11 +469,31 @@ public function realloc(int $capacity): void {}
         // clear
     }
 
+/**
+ * A fixed-size immutable sequence.
+ */
+final class Tuple implements
+    Traversable,
+    Arrayable,
+    Container,
+    Sequence,
+    Hashable
+    {
+        /**
+         * Creats a new tuple using values from $iter. 
+         * 
+         * @todo  If the number of items is not known (not array or countable), 
+         *        should we use `iterator_count`?
+         */
+        public static function from(iterable $iter): self {}
+    }
+
 /**
  * List structure, dynamic size, contiguous, no ops at the front.
  */
 final class Vector implements
     ArrayAccess,
+    Traversable,
     Arrayable,
     Container,
     Clearable,
@@ -545,6 +548,7 @@ public static function from(iterable $iter): self {}
  * Double-ended-queue, supports prepend and append, but nothing in-between.
  */
 final class Deque implements
+    Traversable,
     Arrayable,
     Container,
     Clearable,
@@ -561,12 +565,12 @@ public static function from(iterable $iter): self {}
          *
          * @throws EmptyContainerException
          */
-        public function pop();
+        public function pop() {}
 
         /**
          * Adds a value to the front of the deque.
          */
-        public function unshift(...$values);
+        public function unshift(...$values) {}
 
         /* Arrayable */
         // toArray
@@ -598,13 +602,14 @@ public function unshift(...$values);
  */
 final class HashMap implements
     ArrayAccess,
+    Traversable,
     Container,
     Clearable,
     Sortable,
     SortableKeys,
     Reversable,
     OffsetAccess,
-    MutableDictionary
+    MutableMap
     {
         /**
          * Creates a new dictionary using keys and values from $iter.
@@ -638,7 +643,7 @@ public static function from(iterable $iter): self {}
         // first
         // last
 
-        /* Dictionary | MutableDictionary */
+        /* Map | MutableMap */
         // get
         // has
         // keys
@@ -654,6 +659,7 @@ public static function from(iterable $iter): self {}
  */
 final class HashSet implements
     ArrayAccess,
+    Traversable,
     Arrayable,
     Container,
     Clearable,
@@ -713,6 +719,7 @@ public static function from(iterable $iter): self {}
  * See: https://github.com/php-ds/ext-ds/issues/121
  */
 final class BinarySearchTree implements
+    Traversable,
     Arrayable,
     Container,
     Clearable,
@@ -725,6 +732,26 @@ final class BinarySearchTree implements
          */
         public static function from(iterable $iter): self {}
 
+        /**
+         * Root, Left, Right
+         */
+        public function preorder(): Traversable {}
+
+        /**
+         * Left, Root, Right
+         */
+        public function inorder(): Traversable {}
+
+        /**
+         * Left, Right, Root
+         */
+        public function postorder(): Traversable {}
+
+        /**
+         * Right, Root, Left
+         */
+        public function outorder(): Traversable {}
+
         /* Arrayable */
         // toArray
 

From 8a9cfd37e9305e7841a4dd5c91bfdc10698122b4 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Tue, 19 Feb 2019 18:11:51 -0800
Subject: [PATCH 25/50] Add some todos

---
 DRAFT.php | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index f938ac9..eaa68ff 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -310,13 +310,16 @@ function remove($value);
 }
 
 /**
- * Indicates that a structure is a set that is always sorted. 
+ * Indicates that a structure is a set that is always sorted.
  */
-interface SortedSet extends Set {} 
+interface SortedSet extends Set {}
 
 /**
  * A structure that associates one value with another and provides the ability
  * to query or adjust associations efficiently.
+ *
+ * @todo Should Map extend Set? What about the set operation methods then?
+ * @todo Should Map require some kind of merge, diff, and intersection support?
  */
 interface Map
 {
@@ -334,12 +337,9 @@ function get($key, $default = null);
      * @return bool TRUE if $this map has an association for the given $key,
      *              FALSE otherwise.
      *
-     * @todo "This is ambiguous! 'has' ...what? has key or has value?"
-     *       What about `$map->keys()->has(x)` or `$map->values()->has(x)` if we
-     *       can achieve that without intermediaries? That would make the `has`
-     *       here a bit redundant, where we would not get much value for the
-     *       cost of the ambiguity. The other alternative is hasKey + hasValue,
-     *       but that feels more specific than it needs to be.
+     * @todo if we don't extend Set, should we remove this in favour of
+     *       $map->keys()->has()? This would only work if keys() is O(1), which
+     *       would not always be the case.
      */
     function has($key): bool;
 
@@ -480,9 +480,9 @@ final class Tuple implements
     Hashable
     {
         /**
-         * Creats a new tuple using values from $iter. 
-         * 
-         * @todo  If the number of items is not known (not array or countable), 
+         * Creates a new tuple using values from $iter.
+         *
+         * @todo  If the number of items is not known (not array or countable),
          *        should we use `iterator_count`?
          */
         public static function from(iterable $iter): self {}

From 38c0e1d13662fc96ce8f25f809886524bc843bf3 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Tue, 19 Feb 2019 23:13:18 -0800
Subject: [PATCH 26/50] Remove allocation, pqueue, key sortable, and two
 exceptions

---
 DRAFT.php | 154 +++++++-----------------------------------------------
 1 file changed, 19 insertions(+), 135 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index eaa68ff..e00e18b 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -48,24 +48,12 @@ class EmptyContainerException extends UnderflowException implements AccessExcept
  */
 class IndexOutOfBoundsException extends OutOfBoundsException implements AccessException  {}
 
-/**
- * Should be thrown when an undefined zval is accessed. I do not except any user
- * classes to throw this, but it might be useful to catch it.
- */
-class InvalidAccessException extends RuntimeException implements AccessException {}
-
 /**
  * Should be thrown when a key is not supported, eg. when an attempt is made to
  * associate a value with NULL in Map, causing ambiguity in `find`.
  */
 class InvalidKeyException extends RuntimeException implements AccessException {}
 
-/**
- * Should be thrown when a value could not be found in a context where it was
- * expected. This should only be used if an operation is undefined if not found.
- */
-class ValueNotFoundException extends RuntimeException implements AccessException {}
-
 
 /******************************************************************************/
 /*                                INTERFACES                                  */
@@ -123,20 +111,6 @@ interface Sortable
     function sort(callable $comparator = null);
 }
 
-/**
- * Indicates that a structure can be sorted by key.
- *
- * This interface does not guarantee that the sorting algorithm will be stable.
- * Implementations should use a domain-specific tiebreaker when required.
- */
-interface SortableKeys
-{
-    /**
-     * @return static A sorted copy, or $this structure sorted in-place.
-     */
-    function ksort(callable $comparator = null);
-}
-
 /**
  * Indicates that an object is designed to be used in hash-based structures.
  */
@@ -425,50 +399,6 @@ function pop();
 /*                                 CLASSES                                    */
 /******************************************************************************/
 
-/**
- * A low-level memory allocation abstraction which could be useful to build
- * structures in userland that do not want to use arrays or other structures
- * as a buffer.
- *
- * @todo how should we handle elements that are undefined, ie. a new allocation
- *       will have undefined values. Internally these will be IS_UNDEF, but we
- *       should not expose that to userland. Attempting to access an undefined
- *       value will throw an exception.
- *
- * @todo What should clear do? I think that capacity should be preserved, but
- *       all values should be set the their initial state, ie. undefined
- */
-final class Allocation implements
-    ArrayAccess,
-    Clearable
-    {
-        /**
-         * Creates a new allocation using a given capacity.
-         */
-        public function __construct(int $capacity) {}
-
-        /**
-         * @return int The capacity of $this allocation.
-         */
-        public function capacity(): int {}
-
-        /**
-         * Adjusts the capacity of $this allocation, regardless of current capacity.
-         *
-         * @param int $capacity Resulting capacity, > 0
-         */
-        public function realloc(int $capacity): void {}
-
-        /* ArrayAccess */
-        // offsetGet
-        // offsetSet
-        // offsetUnset
-        // offsetExists
-
-        /* Clearable */
-        // clear
-    }
-
 /**
  * A fixed-size immutable sequence.
  */
@@ -481,11 +411,8 @@ final class Tuple implements
     {
         /**
          * Creates a new tuple using values from $iter.
-         *
-         * @todo  If the number of items is not known (not array or countable),
-         *        should we use `iterator_count`?
          */
-        public static function from(iterable $iter): self {}
+        public function __construct(iterable $iter = null) {}
     }
 
 /**
@@ -505,7 +432,7 @@ final class Vector implements
         /**
          * Creates a new vector using values from $iter.
          */
-        public static function from(iterable $iter): self {}
+        public function __construct(iterable $iter = null) {}
 
         /* ArrayAccess */
         // offsetGet
@@ -558,7 +485,7 @@ final class Deque implements
         /**
          * Creates a new deque using values from $iter.
          */
-        public static function from(iterable $iter): self {}
+        public function __construct(iterable $iter = null) {}
 
         /**
          * Removes and returns the value at the back of the deque.
@@ -606,7 +533,6 @@ final class HashMap implements
     Container,
     Clearable,
     Sortable,
-    SortableKeys,
     Reversable,
     OffsetAccess,
     MutableMap
@@ -614,7 +540,7 @@ final class HashMap implements
         /**
          * Creates a new dictionary using keys and values from $iter.
          */
-        public static function from(iterable $iter): self {}
+        public function __construct(iterable $iter = null) {}
 
         /* ArrayAccess */
         // offsetGet
@@ -632,9 +558,6 @@ public static function from(iterable $iter): self {}
         /* Sortable */
         // sort
 
-        /* SortableKeys */
-        // ksort
-
         /* Reversable */
         // reverse
 
@@ -671,7 +594,7 @@ final class HashSet implements
         /**
          * Creates a new set using values from $iter.
          */
-        public static function from(iterable $iter): self {}
+        public function __construct(iterable $iter = null) {}
 
         /* ArrayAccess */
         // offsetGet
@@ -730,7 +653,7 @@ final class BinarySearchTree implements
         /**
          * Creates a new bst using values from $iter.
          */
-        public static function from(iterable $iter): self {}
+        public function __construct(iterable $iter = null) {}
 
         /**
          * Root, Left, Right
@@ -779,53 +702,6 @@ public function outorder(): Traversable {}
         // remove
     }
 
-/**
- * Heap-based queue with a mixed-type priority value associated with the key.
- *
- * @todo Should we support duplicate values?
- */
-final class PriorityQueue implements
-    Container,
-    Clearable
-    {
-        /**
-         * Creates a new queue using values from $iter.
-         */
-        public static function from(iterable $iter): self {}
-
-        /**
-         * Adds a value to the queue, using a given priority.
-         */
-        public function push($value, $priority) {}
-
-        /**
-         * Updates the priority of all found instances of $value.
-         *
-         * @throws ValueNotFoundException if none were found.
-         */
-        public function update($value, $priority) {}
-
-        /**
-         * Removes and returns the next value in the queue, ie. the value with
-         * the next highest priority.
-         */
-        public function shift() {}
-
-        /**
-         * Returns the next value in the queue without removing it.
-         *
-         * @throws EmptyContainerException
-         */
-        public function peek() {}
-
-        /* Container | Countable */
-        // count
-        // isEmpty
-
-        /* Clearable */
-        // clear
-    }
-
 /**
  * Stable heap with an optional comparator, defaulting to minimum. We could
  * potentially remove the nullable which clears up the min/max ambiguity.
@@ -835,19 +711,27 @@ final class Heap implements
     Clearable
     {
         /**
-         * Creates a new heap using values from $iter, if any.
+         * Creates a new heap using an optional comparator.
          */
         public function __construct(callable $comparator = null) {}
 
         /**
-         * Creates a new heap using values from $iter.
+         * Adds a value to the heap.
          */
-        public static function from(iterable $iter, callable $comparator = null): self {}
+        public function push(...$values) {}
 
         /**
-         * Adds a value to the heap.
+         * Applies a given callback function to all instances of value in the
+         * heap. Each instance will be replaced by the value returned by its
+         * invocation of the mutator.
+         *
+         * This provides a safe way to update elements in the heap because it
+         * knows to correct itself after values have been updated. Otherwise an
+         * object can change without the heap knowing about it.
+         *
+         * @todo Should we consider observables instead?
          */
-        public function push(...$values) {}
+        public function update($value, callable $mutator) {}
 
         /**
          * Removes and returns the value at the top of the heap.

From 90c80ceba6f6f98a295257756145d4f3ed516f00 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Tue, 19 Feb 2019 23:23:10 -0800
Subject: [PATCH 27/50] Remove TreeTraversal, iterable constructors

---
 DRAFT.php | 48 ++++++++++++++++--------------------------------
 1 file changed, 16 insertions(+), 32 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index e00e18b..ae70711 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -409,10 +409,21 @@ final class Tuple implements
     Sequence,
     Hashable
     {
-        /**
-         * Creates a new tuple using values from $iter.
-         */
-        public function __construct(iterable $iter = null) {}
+        /* Arrayable */
+        // toArray
+
+        /* Countable | Container */
+        // count
+        // isEmpty
+
+        /* OffsetAccess | Sequence */
+        // offset
+        // first
+        // last
+        // get
+
+        /* Hashable | Immutable */
+        // getHashSource
     }
 
 /**
@@ -429,11 +440,6 @@ final class Vector implements
     MutableSequence,
     Stack
     {
-        /**
-         * Creates a new vector using values from $iter.
-         */
-        public function __construct(iterable $iter = null) {}
-
         /* ArrayAccess */
         // offsetGet
         // offsetSet
@@ -482,11 +488,6 @@ final class Deque implements
     OffsetAccess,
     Queue
     {
-        /**
-         * Creates a new deque using values from $iter.
-         */
-        public function __construct(iterable $iter = null) {}
-
         /**
          * Removes and returns the value at the back of the deque.
          *
@@ -537,11 +538,6 @@ final class HashMap implements
     OffsetAccess,
     MutableMap
     {
-        /**
-         * Creates a new dictionary using keys and values from $iter.
-         */
-        public function __construct(iterable $iter = null) {}
-
         /* ArrayAccess */
         // offsetGet
         // offsetSet
@@ -591,11 +587,6 @@ final class HashSet implements
     OffsetAccess,
     MutableSet
     {
-        /**
-         * Creates a new set using values from $iter.
-         */
-        public function __construct(iterable $iter = null) {}
-
         /* ArrayAccess */
         // offsetGet
         // offsetSet
@@ -646,14 +637,13 @@ final class BinarySearchTree implements
     Arrayable,
     Container,
     Clearable,
-    TreeTraversal,
     MutableSet,
     SortedSet
     {
         /**
          * Creates a new bst using values from $iter.
          */
-        public function __construct(iterable $iter = null) {}
+        public function __construct(callable $comparator = null) {}
 
         /**
          * Root, Left, Right
@@ -685,12 +675,6 @@ public function outorder(): Traversable {}
         /* Clearable */
         // clear
 
-        /* TreeTraversal */
-        // preorder
-        // inorder
-        // postorder
-        // outorder
-
         /* Set | MutableSet */
         // has
         // or

From 4b4bed63b04ebfcd9ca60b62170753e7688300a6 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Tue, 19 Feb 2019 23:44:13 -0800
Subject: [PATCH 28/50] Remove update from heap..

---
 DRAFT.php | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index ae70711..c62a9c2 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -704,19 +704,6 @@ public function __construct(callable $comparator = null) {}
          */
         public function push(...$values) {}
 
-        /**
-         * Applies a given callback function to all instances of value in the
-         * heap. Each instance will be replaced by the value returned by its
-         * invocation of the mutator.
-         *
-         * This provides a safe way to update elements in the heap because it
-         * knows to correct itself after values have been updated. Otherwise an
-         * object can change without the heap knowing about it.
-         *
-         * @todo Should we consider observables instead?
-         */
-        public function update($value, callable $mutator) {}
-
         /**
          * Removes and returns the value at the top of the heap.
          *

From 94afd1b717027f8b97dbb8962e56b8c32434295b Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 20 Feb 2019 10:00:48 -0800
Subject: [PATCH 29/50] Transfers, no offsetaccess for deque

---
 DRAFT.php | 204 +++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 131 insertions(+), 73 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index c62a9c2..72c23d8 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -37,6 +37,16 @@
  */
 interface AccessException {}
 
+/**
+ * Marker interface for all state-related exceptions, such as heap corruption.
+ */
+interface StateException {}
+
+/**
+ * Marker interface for all transfer-related exceptions.
+ */
+interface TransferException {}
+
 /**
  * Should be thrown when an empty container is accessed a clear, obvious result.
  */
@@ -54,6 +64,18 @@ class IndexOutOfBoundsException extends OutOfBoundsException implements AccessEx
  */
 class InvalidKeyException extends RuntimeException implements AccessException {}
 
+/**
+ * Should be thrown whenever a structure is in an unexpected state. This state
+ * should not be resolvable without ambiguity, so an exception is thrown.
+ */
+class InvalidStateException extends RuntimeException implements StateException {}
+
+/**
+ * Should be thrown when a transfer is polled but there were no values available,
+ * or
+ */
+class InvalidPollException extends UnderflowException implements TransferException {}
+
 
 /******************************************************************************/
 /*                                INTERFACES                                  */
@@ -135,7 +157,7 @@ interface OffsetAccess extends Countable
     /**
      * @return mixed The value at the
      *
-     * @throws IndexOutOfBoundsException if the index is not within [0, size).
+     * @throws OutOfBoundsException if the index is not within [0, size).
      *
      * @todo Potential for a better name, like "skip" or "atIndex" or "at"?
      *       Because it clashes with ArrayAccess a little bit here.
@@ -177,7 +199,7 @@ interface Sequence extends OffsetAccess
     /**
      * Returns the value at the given position.
      *
-     * @throws IndexOutOfBoundsException if the index is not within [0, size)
+     * @throws OutOfBoundsException if the index is not within [0, size)
      */
     function get(int $index);
 }
@@ -190,7 +212,7 @@ interface MutableSequence extends Sequence
     /**
      * Sets the value at the given position.
      *
-     * @throws IndexOutOfBoundsException if the index is not within [0, size)
+     * @throws OutOfBoundsException if the index is not within [0, size)
      */
     function set(int $index, $value);
 
@@ -200,7 +222,7 @@ function set(int $index, $value);
      *
      * @todo Naming - remove, pull?
      *
-     * @throws IndexOutOfBoundsException if the index is not within [0, size)
+     * @throws OutOfBoundsException if the index is not within [0, size)
      */
     function unset(int $index);
 
@@ -208,7 +230,7 @@ function unset(int $index);
      * Moves all values between the given index and the end of the sequence
      * towards the back, then inserts the given values into the gap.
      *
-     * @throws IndexOutOfBoundsException if the index is out of range [0, size]
+     * @throws OutOfBoundsException if the index is out of range [0, size]
      */
     function insert(int $index, ...$values);
 }
@@ -320,16 +342,14 @@ function has($key): bool;
     /**
      * @return Set All keys from the map added in traversal order.
      *
-     * @todo We have to come up with a clever way to make this lazy, so we can
-     *       expose a view on the structure's keys rather than an eager copy.
+     * @todo Should this return an iterator?
      */
     function keys(): Set;
 
     /**
      * @return Set All keys from the map added in traversal order.
      *
-     * @todo We have to come up with a clever way to make this lazy, so we can
-     *       expose a view on the structure's values rather than an eager copy.
+     * @todo Should this return an iterator?
      */
     function values(): Sequence;
 }
@@ -389,16 +409,56 @@ function push(...$values);
     /**
      * Removes and returns the value at the top of the stack.
      *
-     * @throws EmptyContainerException
+     * @throws UnderflowException
      */
     function pop();
 }
 
+/**
+ * Indicates that a structure can receive and produce values in a semantically
+ * defined order.
+ */
+interface Transferable
+{
+    /**
+     * Offers one or more values to the transfer.
+     */
+    function send(...$values);
+
+    /**
+     * @throws UnderflowException
+     */
+    function poll();
+}
 
 /******************************************************************************/
 /*                                 CLASSES                                    */
 /******************************************************************************/
 
+/**
+ * A transfer adapter for stacks.
+ */
+final class StackTransfer implements Transferable
+{
+    /* Transferable */
+    // send (stack->push)
+    // poll (stack->pop)
+
+    public function __construct(Stack $stack) {}
+}
+
+/**
+ * A transfer adapter for queues.
+ */
+final class QueueTransfer implements Transferable
+{
+    /* Transferable */
+    // send (queue->push)
+    // poll (queue->shift)
+
+    public function __construct(Queue $queue) {}
+}
+
 /**
  * A fixed-size immutable sequence.
  */
@@ -481,28 +541,11 @@ final class Vector implements
  * Double-ended-queue, supports prepend and append, but nothing in-between.
  */
 final class Deque implements
-    Traversable,
-    Arrayable,
     Container,
     Clearable,
-    OffsetAccess,
-    Queue
+    Queue,
+    Stack
     {
-        /**
-         * Removes and returns the value at the back of the deque.
-         *
-         * @throws EmptyContainerException
-         */
-        public function pop() {}
-
-        /**
-         * Adds a value to the front of the deque.
-         */
-        public function unshift(...$values) {}
-
-        /* Arrayable */
-        // toArray
-
         /* Countable | Container */
         // count
         // isEmpty
@@ -510,14 +553,30 @@ public function unshift(...$values) {}
         /* Clearable */
         // clear
 
-        /* OffsetAccess */
-        // offset
-        // first
-        // last
-
         /* Queue */
         // push
         // shift
+
+        /* Stack */
+        // push
+        // pop
+
+        /**
+         * Adds one or more values to the front of the deque.
+         */
+        public function unshift(...$values);
+
+        /**
+         * Returns the value at the front of the deque. This would be the next
+         * value in a queue, or the last value in a stack.
+         */
+        public function front();
+
+        /**
+         * Returns the value at the back of the deque. This would be the next
+         * value in a stack, or the last value in a queue.
+         */
+        public function back();
     }
 
 /**
@@ -640,30 +699,6 @@ final class BinarySearchTree implements
     MutableSet,
     SortedSet
     {
-        /**
-         * Creates a new bst using values from $iter.
-         */
-        public function __construct(callable $comparator = null) {}
-
-        /**
-         * Root, Left, Right
-         */
-        public function preorder(): Traversable {}
-
-        /**
-         * Left, Root, Right
-         */
-        public function inorder(): Traversable {}
-
-        /**
-         * Left, Right, Root
-         */
-        public function postorder(): Traversable {}
-
-        /**
-         * Right, Root, Left
-         */
-        public function outorder(): Traversable {}
 
         /* Arrayable */
         // toArray
@@ -684,6 +719,31 @@ public function outorder(): Traversable {}
         //
         // add
         // remove
+
+        /**
+         * Creates a new bst using values from $iter.
+         */
+        public function __construct(callable $comparator = null) {}
+
+        /**
+         * Root, Left, Right
+         */
+        public function preorder(): iterable {}
+
+        /**
+         * Left, Root, Right
+         */
+        public function inorder(): iterable {}
+
+        /**
+         * Left, Right, Root
+         */
+        public function postorder(): iterable {}
+
+        /**
+         * Right, Root, Left
+         */
+        public function outorder(): iterable {}
     }
 
 /**
@@ -692,8 +752,20 @@ public function outorder(): Traversable {}
  */
 final class Heap implements
     Container,
-    Clearable
+    Clearable,
+    Transferable
     {
+        /* Container | Countable */
+        // count
+        // isEmpty
+
+        /* Clearable */
+        // clear
+
+        /* Transferable */
+        // send (push)
+        // poll (shift)
+
         /**
          * Creates a new heap using an optional comparator.
          */
@@ -710,18 +782,4 @@ public function push(...$values) {}
          * @throws EmptyContainerException
          */
         public function shift() {}
-
-        /**
-         * Returns the value at the top of the heap without removing it.
-         *
-         * @throws EmptyContainerException
-         */
-        public function peek() {}
-
-        /* Container | Countable */
-        // count
-        // isEmpty
-
-        /* Clearable */
-        // clear
     }

From 807812c2b4647666af7f7bcdb58827a75924895b Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 20 Feb 2019 10:18:51 -0800
Subject: [PATCH 30/50] Remove poll exception, arrayable

---
 DRAFT.php | 40 ----------------------------------------
 1 file changed, 40 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 72c23d8..5584a39 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -70,12 +70,6 @@ class InvalidKeyException extends RuntimeException implements AccessException {}
  */
 class InvalidStateException extends RuntimeException implements StateException {}
 
-/**
- * Should be thrown when a transfer is polled but there were no values available,
- * or
- */
-class InvalidPollException extends UnderflowException implements TransferException {}
-
 
 /******************************************************************************/
 /*                                INTERFACES                                  */
@@ -86,20 +80,6 @@ class InvalidPollException extends UnderflowException implements TransferExcepti
  */
 interface Immutable {}
 
-/**
- * Indicates that the elements of a structure can be represented by an array
- * without altering the structure of the data, without any warnings or errors.
- */
-interface Arrayable
-{
-    /**
-     * This method should not fail. There is an implicit expectation that any
-     * structure converted to an array must be able to be converted back to
-     * the structure in such a way that it is considered equal to the initial.
-     */
-    function toArray(): array;
-}
-
 /**
  * Indicates that a structure can be cleared.
  */
@@ -464,14 +444,10 @@ public function __construct(Queue $queue) {}
  */
 final class Tuple implements
     Traversable,
-    Arrayable,
     Container,
     Sequence,
     Hashable
     {
-        /* Arrayable */
-        // toArray
-
         /* Countable | Container */
         // count
         // isEmpty
@@ -492,7 +468,6 @@ final class Tuple implements
 final class Vector implements
     ArrayAccess,
     Traversable,
-    Arrayable,
     Container,
     Clearable,
     Sortable,
@@ -506,9 +481,6 @@ final class Vector implements
         // offsetUnset
         // offsetExists
 
-        /* Arrayable */
-        // toArray
-
         /* Countable | Container */
         // count
         // isEmpty
@@ -583,9 +555,6 @@ public function back();
  * The standard hashtable, implemented as Map in ext-ds currently. It is based
  * on the PHP array but is not identical. We can implement OffsetAccess because
  * insertion order is preserved.
- *
- * @todo Should we implement Arrayable here? We would have to throw if an object
- *       is used as a key, and there will also be inconsistencies eg. "1" and 1.
  */
 final class HashMap implements
     ArrayAccess,
@@ -638,7 +607,6 @@ final class HashMap implements
 final class HashSet implements
     ArrayAccess,
     Traversable,
-    Arrayable,
     Container,
     Clearable,
     Sortable,
@@ -652,9 +620,6 @@ final class HashSet implements
         // offsetUnset
         // offsetExists
 
-        /* Arrayable */
-        // toArray
-
         /* Container | Countable */
         // count
         // isEmpty
@@ -693,16 +658,11 @@ final class HashSet implements
  */
 final class BinarySearchTree implements
     Traversable,
-    Arrayable,
     Container,
     Clearable,
     MutableSet,
     SortedSet
     {
-
-        /* Arrayable */
-        // toArray
-
         /* Container | Countable */
         // count
         // isEmpty

From a86348c8e663b072a80d15b388acdc19d32e95b7 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 20 Feb 2019 11:10:28 -0800
Subject: [PATCH 31/50] Exception rework, some todo resolved

---
 DRAFT.php | 91 ++++++++++++++++++++++---------------------------------
 1 file changed, 37 insertions(+), 54 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 5584a39..7435147 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -14,16 +14,12 @@
 */
 namespace Ds;
 
-use ArrayAccess;
 use Countable;
-use OutOfBoundsException;
 use RuntimeException;
 use Traversable;
-use UnderflowException;
 
 /**
  * @todo How do we handle comparison? Proposal: == for objects, === otherwise.
- * @todo give/take interface?
  */
 
 /******************************************************************************/
@@ -42,33 +38,22 @@ interface AccessException {}
  */
 interface StateException {}
 
-/**
- * Marker interface for all transfer-related exceptions.
- */
-interface TransferException {}
-
 /**
  * Should be thrown when an empty container is accessed a clear, obvious result.
  */
-class EmptyContainerException extends UnderflowException implements AccessException {}
+class EmptyStateException implements StateException {}
 
 /**
  * Should be thrown when an index or key is not within the given access bounds
- * of a structure, such as a negative index on a list or
+ * of a structure, such as attempting to access beyond the length.
  */
-class IndexOutOfBoundsException extends OutOfBoundsException implements AccessException  {}
+class OffsetException extends RuntimeException implements AccessException  {}
 
 /**
  * Should be thrown when a key is not supported, eg. when an attempt is made to
  * associate a value with NULL in Map, causing ambiguity in `find`.
  */
-class InvalidKeyException extends RuntimeException implements AccessException {}
-
-/**
- * Should be thrown whenever a structure is in an unexpected state. This state
- * should not be resolvable without ambiguity, so an exception is thrown.
- */
-class InvalidStateException extends RuntimeException implements StateException {}
+class KeyException extends RuntimeException implements AccessException {}
 
 
 /******************************************************************************/
@@ -129,28 +114,27 @@ function getHashSource();
  * indicating the position of an element from the beginning of the structure.
  *
  * We extend Countable because it should always be possible to determine bounds.
- *
- * @todo Naming - OrdinalAccess, LinearAccess, RandomAccess...
  */
 interface OffsetAccess extends Countable
 {
     /**
      * @return mixed The value at the
      *
-     * @throws OutOfBoundsException if the index is not within [0, size).
-     *
-     * @todo Potential for a better name, like "skip" or "atIndex" or "at"?
-     *       Because it clashes with ArrayAccess a little bit here.
+     * @throws OffsetException if the index is not within [0, size).
      */
-    function offset(int $index);
+    function offset(int $offset);
 
     /**
      * @return mixed The first value in $this structure.
+     *
+     * @throws EmptyStateException
      */
     function first();
 
     /**
      * @return mixed The last value in $this structure.
+     *
+     * @throws EmptyStateException
      */
     function last();
 }
@@ -177,11 +161,11 @@ function isEmpty(): bool;
 interface Sequence extends OffsetAccess
 {
     /**
-     * Returns the value at the given position.
+     * Returns the value at the given offset.
      *
-     * @throws OutOfBoundsException if the index is not within [0, size)
+     * @throws OffsetException if the index is not within [0, size)
      */
-    function get(int $index);
+    function get(int $offset);
 }
 
 /**
@@ -190,29 +174,27 @@ function get(int $index);
 interface MutableSequence extends Sequence
 {
     /**
-     * Sets the value at the given position.
+     * Sets the value at the given offset.
      *
-     * @throws OutOfBoundsException if the index is not within [0, size)
+     * @throws OffsetException if the index is not within [0, size)
      */
-    function set(int $index, $value);
+    function set(int $offset, $value);
 
     /**
-     * Removes the value at the given position, moving all successive
+     * Removes the value at the given offset, moving all successive
      * values one position towards the front.
      *
-     * @todo Naming - remove, pull?
-     *
-     * @throws OutOfBoundsException if the index is not within [0, size)
+     * @throws OffsetException if the index is not within [0, size)
      */
-    function unset(int $index);
+    function unset(int $offset);
 
     /**
      * Moves all values between the given index and the end of the sequence
      * towards the back, then inserts the given values into the gap.
      *
-     * @throws OutOfBoundsException if the index is out of range [0, size]
+     * @throws OffsetException if the index is out of range [0, size]
      */
-    function insert(int $index, ...$values);
+    function insert(int $offset, ...$values);
 }
 
 /**
@@ -226,8 +208,6 @@ interface Set
 {
     /**
      * @return bool TRUE if the value is in $this set, FALSE otherwise.
-     *
-     * @todo Naming - contains?
      */
     function has($value): bool;
 
@@ -280,7 +260,10 @@ interface MutableSet extends Set
     function add(...$values);
 
     /**
-     * @todo what happens when the value could not be found? Silent no-op? bool?
+     * Removes a given value from $this set, or does nothing if that value could
+     * not be found. The caller can use `has` to determine membership before
+     * removal. This method therefore promises only that the given value is not
+     * a member of the set on return.
      */
     function remove($value);
 }
@@ -294,7 +277,9 @@ interface SortedSet extends Set {}
  * A structure that associates one value with another and provides the ability
  * to query or adjust associations efficiently.
  *
- * @todo Should Map extend Set? What about the set operation methods then?
+ * @todo Should Map extend Set (not mutable)?
+ *       What about the set operation methods then?
+ *
  * @todo Should Map require some kind of merge, diff, and intersection support?
  */
 interface Map
@@ -342,20 +327,14 @@ interface MutableMap extends Map
     /**
      * Associates the $key with the $value, overriding any previous association.
      *
-     * @throws InvalidKeyException if the map implementation does not support
+     * @throws KeyException if the map implementation does not support
      *                             the given key, eg. NULL
-     *
-     * @todo Naming - set/put (set/unset symmetry, add/remove, put/pull?)
      */
     function set($key, $value);
 
     /**
      * Removes the given $key from the map, and does nothing if they key could
-     * not be found.
-     *
-     * @todo Should we return the removed value? (No, ambiguous and two-in-one)
-     *
-     * @todo Key not found: should we throw, do nothing, or not enforce either?
+     * not be found. This emulates `unset`
      */
     function unset($key);
 }
@@ -389,7 +368,7 @@ function push(...$values);
     /**
      * Removes and returns the value at the top of the stack.
      *
-     * @throws UnderflowException
+     * @throws EmptyStateException
      */
     function pop();
 }
@@ -406,7 +385,7 @@ interface Transferable
     function send(...$values);
 
     /**
-     * @throws UnderflowException
+     * @throws EmptyStateException
      */
     function poll();
 }
@@ -541,12 +520,16 @@ public function unshift(...$values);
         /**
          * Returns the value at the front of the deque. This would be the next
          * value in a queue, or the last value in a stack.
+         *
+         * @throws EmptyStateException
          */
         public function front();
 
         /**
          * Returns the value at the back of the deque. This would be the next
          * value in a stack, or the last value in a queue.
+         *
+         * @throws EmptyStateException
          */
         public function back();
     }
@@ -739,7 +722,7 @@ public function push(...$values) {}
         /**
          * Removes and returns the value at the top of the heap.
          *
-         * @throws EmptyContainerException
+         * @throws EmptyStateException
          */
         public function shift() {}
     }

From 9f1397b50f5dd3758ab9cb707c30049f992f2d46 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 20 Feb 2019 13:17:00 -0800
Subject: [PATCH 32/50] Remove stack and queue interfaces, deque get oa back
 \o/

---
 DRAFT.php | 146 ++++++++++++++++++++++++++++--------------------------
 1 file changed, 77 insertions(+), 69 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 7435147..2ec2b52 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -340,42 +340,8 @@ function unset($key);
 }
 
 /**
- * Indicates that a structure has first-in-first-out semantics.
- */
-interface Queue
-{
-    /**
-     * Adds a value to the queue.
-     */
-    function push(...$values);
-
-    /**
-     * Removes and returns the next value in the queue.
-     */
-    function shift();
-}
-
-/**
- * Indicates that a structure has first-in-last-out semantics.
- */
-interface Stack
-{
-    /**
-     * Adds a value to the top of the stack.
-     */
-    function push(...$values);
-
-    /**
-     * Removes and returns the value at the top of the stack.
-     *
-     * @throws EmptyStateException
-     */
-    function pop();
-}
-
-/**
- * Indicates that a structure can receive and produce values in a semantically
- * defined order.
+ * Indicates that a structure can receive and produce values efficiently in a
+ * semantically defined order. Both operations should be O(1).
  */
 interface Transferable
 {
@@ -385,6 +351,8 @@ interface Transferable
     function send(...$values);
 
     /**
+     * Removes and returns the next value.
+     *
      * @throws EmptyStateException
      */
     function poll();
@@ -397,26 +365,48 @@ function poll();
 /**
  * A transfer adapter for stacks.
  */
-final class StackTransfer implements Transferable
-{
-    /* Transferable */
-    // send (stack->push)
-    // poll (stack->pop)
+final class Stack implements
+    Container,
+    Transferable
+    {
+        /* Transferable */
+        // send (push)
+        // poll (pop)
 
-    public function __construct(Stack $stack) {}
-}
+        /**
+         * Adds a value to the top of the stack.
+         */
+        function push(...$values);
+
+        /**
+         * Removes and returns the value at the top of the stack.
+         *
+         * @throws EmptyStateException
+         */
+        function pop();
+    }
 
 /**
  * A transfer adapter for queues.
  */
-final class QueueTransfer implements Transferable
-{
-    /* Transferable */
-    // send (queue->push)
-    // poll (queue->shift)
+final class Queue implements
+    Container,
+    Transferable
+    {
+        /* Transferable */
+        // send (push)
+        // poll (shift)
 
-    public function __construct(Queue $queue) {}
-}
+        /**
+         * Adds a value to the queue.
+         */
+        function push(...$values);
+
+        /**
+         * Removes and returns the next value in the queue.
+         */
+        function shift();
+    }
 
 /**
  * A fixed-size immutable sequence.
@@ -483,9 +473,17 @@ final class Vector implements
         // unset
         // insert
 
-        /* Stack */
-        // push
-        // pop
+        /**
+         * Adds one or more values to the end of the vector.
+         */
+        function push(...$values);
+
+        /**
+         * Removes and returns the value at the end of the vector.
+         *
+         * @throws EmptyStateException
+         */
+        function pop();
     }
 
 /**
@@ -494,8 +492,8 @@ final class Vector implements
 final class Deque implements
     Container,
     Clearable,
-    Queue,
-    Stack
+    Transferable,
+    Sequence
     {
         /* Countable | Container */
         // count
@@ -504,34 +502,39 @@ final class Deque implements
         /* Clearable */
         // clear
 
-        /* Queue */
-        // push
-        // shift
+        /* Transferable */
+        // send (push)
+        // poll (shift)
 
-        /* Stack */
-        // push
-        // pop
+        /* OffsetAccess | Sequence */
+        // offset
+        // first
+        // last
+        // get
 
         /**
-         * Adds one or more values to the front of the deque.
+         * Adds one or more values to the end of the deque.
          */
-        public function unshift(...$values);
+        function push(...$values);
 
         /**
-         * Returns the value at the front of the deque. This would be the next
-         * value in a queue, or the last value in a stack.
+         * Removes and returns the value at the end of the deque.
          *
          * @throws EmptyStateException
          */
-        public function front();
+        function pop();
 
         /**
-         * Returns the value at the back of the deque. This would be the next
-         * value in a stack, or the last value in a queue.
+         * Adds one or more values to the start of the deque.
+         */
+        public function unshift(...$values);
+
+        /**
+         * Removes and returns the value at the start of the deque.
          *
          * @throws EmptyStateException
          */
-        public function back();
+        function shift();
     }
 
 /**
@@ -594,6 +597,7 @@ final class HashSet implements
     Clearable,
     Sortable,
     Reversable,
+    Transferable,
     OffsetAccess,
     MutableSet
     {
@@ -616,6 +620,10 @@ final class HashSet implements
         /* Reversable */
         // reverse
 
+        /* Transferable */
+        // send (add)
+        // poll (remove[0])
+
         /* OffsetAccess */
         // offset
         // first

From 8b068aafeb4cb136747a5c8ee2fb38bfe99e8f56 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 20 Feb 2019 16:59:25 -0800
Subject: [PATCH 33/50] Remove reversable

---
 DRAFT.php | 36 ++++++++----------------------------
 1 file changed, 8 insertions(+), 28 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 2ec2b52..1cc5aba 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -18,8 +18,12 @@
 use RuntimeException;
 use Traversable;
 
+/******************************************************************************/
+/*                           ORDERING & EQUALITY                              */
+/******************************************************************************/
+
 /**
- * @todo How do we handle comparison? Proposal: == for objects, === otherwise.
+ * @todo Proposal: == for objects, === otherwise.
  */
 
 /******************************************************************************/
@@ -73,17 +77,6 @@ interface Clearable
     function clear();
 }
 
-/**
- * Indicates that the order of the elements of a structure can be reversed.
- */
-interface Reversable
-{
-    /**
-     * @return static A reversed copy or this structure reversed in-place.
-     */
-    function reverse();
-}
-
 /**
  * Indicates that a structure can be sorted by value.
  *
@@ -346,12 +339,12 @@ function unset($key);
 interface Transferable
 {
     /**
-     * Offers one or more values to the transfer.
+     * Offers one or more values to this transferable.
      */
     function send(...$values);
 
     /**
-     * Removes and returns the next value.
+     * Removes and returns the next value produced by this transferable.
      *
      * @throws EmptyStateException
      */
@@ -440,9 +433,7 @@ final class Vector implements
     Container,
     Clearable,
     Sortable,
-    Reversable,
-    MutableSequence,
-    Stack
+    MutableSequence
     {
         /* ArrayAccess */
         // offsetGet
@@ -460,9 +451,6 @@ final class Vector implements
         /* Sortable */
         // sort
 
-        /* Reversable */
-        // reverse
-
         /* OffsetAccess | Sequence | MutableSequence */
         // offset
         // first
@@ -548,7 +536,6 @@ final class HashMap implements
     Container,
     Clearable,
     Sortable,
-    Reversable,
     OffsetAccess,
     MutableMap
     {
@@ -568,9 +555,6 @@ final class HashMap implements
         /* Sortable */
         // sort
 
-        /* Reversable */
-        // reverse
-
         /* OffsetAccess */
         // offset
         // first
@@ -596,7 +580,6 @@ final class HashSet implements
     Container,
     Clearable,
     Sortable,
-    Reversable,
     Transferable,
     OffsetAccess,
     MutableSet
@@ -617,9 +600,6 @@ final class HashSet implements
         /* Sortable */
         // sort
 
-        /* Reversable */
-        // reverse
-
         /* Transferable */
         // send (add)
         // poll (remove[0])

From 886354d4b39a2327176011b049093927e19a6518 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Fri, 22 Feb 2019 17:10:35 -0800
Subject: [PATCH 34/50] add multiset, ord sequence, remove offsetaccess, tree
 map, tree set

---
 DRAFT.php | 315 ++++++++++++++----------------------------------------
 1 file changed, 81 insertions(+), 234 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 1cc5aba..6260a8f 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -102,13 +102,29 @@ interface Hashable extends Immutable
     function getHashSource();
 }
 
+/**
+ * Indicates that a structure contains elements and is aware of the number of
+ * items contained at any point in time. How a container must determine the
+ * count is implementation-specific.
+ *
+ * This interface does not imply that a structure is traversable.
+ */
+interface Container extends Countable
+{
+    /**
+     * @return bool whether the container is empty, which can be TRUE even when
+     *              the number of elements is non-zero.
+     */
+    function isEmpty(): bool;
+}
+
 /**
  * Indicates that a structure can be accessed using a zero-based integer index
  * indicating the position of an element from the beginning of the structure.
  *
  * We extend Countable because it should always be possible to determine bounds.
  */
-interface OffsetAccess extends Countable
+interface Sequence extends Countable
 {
     /**
      * @return mixed The value at the
@@ -132,35 +148,6 @@ function first();
     function last();
 }
 
-/**
- * Indicates that a structure contains elements and is aware of the number of
- * items contained at any point in time. How a container must determine the
- * count is implementation-specific.
- *
- * This interface does not imply that a structure is traversable.
- */
-interface Container extends Countable
-{
-    /**
-     * @return bool whether the container is empty, which can be TRUE even when
-     *              the number of elements is non-zero.
-     */
-    function isEmpty(): bool;
-}
-
-/**
- * Indicates that a structure contains a series of contiguous elements.
- */
-interface Sequence extends OffsetAccess
-{
-    /**
-     * Returns the value at the given offset.
-     *
-     * @throws OffsetException if the index is not within [0, size)
-     */
-    function get(int $offset);
-}
-
 /**
  * A sequence which can be modified, either in-place or as a copy.
  */
@@ -190,6 +177,11 @@ function unset(int $offset);
     function insert(int $offset, ...$values);
 }
 
+/**
+ * Indicates that a structure is a sequence that is always sorted.
+ */
+interface SortedSequence extends Sequence {}
+
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
@@ -332,6 +324,11 @@ function set($key, $value);
     function unset($key);
 }
 
+/**
+ * Indicates that a structure is a map that is always sorted.
+ */
+interface SortedMap extends Map {}
+
 /**
  * Indicates that a structure can receive and produce values efficiently in a
  * semantically defined order. Both operations should be O(1).
@@ -359,13 +356,9 @@ function poll();
  * A transfer adapter for stacks.
  */
 final class Stack implements
-    Container,
+    Container, /* Countable */
     Transferable
     {
-        /* Transferable */
-        // send (push)
-        // poll (pop)
-
         /**
          * Adds a value to the top of the stack.
          */
@@ -383,13 +376,9 @@ function pop();
  * A transfer adapter for queues.
  */
 final class Queue implements
-    Container,
+    Container, /* Countable */
     Transferable
     {
-        /* Transferable */
-        // send (push)
-        // poll (shift)
-
         /**
          * Adds a value to the queue.
          */
@@ -406,23 +395,10 @@ function shift();
  */
 final class Tuple implements
     Traversable,
-    Container,
-    Sequence,
-    Hashable
-    {
-        /* Countable | Container */
-        // count
-        // isEmpty
-
-        /* OffsetAccess | Sequence */
-        // offset
-        // first
-        // last
-        // get
-
-        /* Hashable | Immutable */
-        // getHashSource
-    }
+    Container, /* Countable */
+    Sequence,  /* Countable */
+    Hashable   /* Immutable */
+    {}
 
 /**
  * List structure, dynamic size, contiguous, no ops at the front.
@@ -430,37 +406,11 @@ final class Tuple implements
 final class Vector implements
     ArrayAccess,
     Traversable,
-    Container,
+    Container,      /* Countable */
     Clearable,
     Sortable,
-    MutableSequence
+    MutableSequence /* Sequence, Countable */
     {
-        /* ArrayAccess */
-        // offsetGet
-        // offsetSet
-        // offsetUnset
-        // offsetExists
-
-        /* Countable | Container */
-        // count
-        // isEmpty
-
-        /* Clearable */
-        // clear
-
-        /* Sortable */
-        // sort
-
-        /* OffsetAccess | Sequence | MutableSequence */
-        // offset
-        // first
-        // last
-        //
-        // get
-        // set
-        // unset
-        // insert
-
         /**
          * Adds one or more values to the end of the vector.
          */
@@ -478,28 +428,11 @@ function pop();
  * Double-ended-queue, supports prepend and append, but nothing in-between.
  */
 final class Deque implements
-    Container,
+    Container,      /* Countable */
     Clearable,
     Transferable,
-    Sequence
+    SortedSequence  /* Sequence, Countable */
     {
-        /* Countable | Container */
-        // count
-        // isEmpty
-
-        /* Clearable */
-        // clear
-
-        /* Transferable */
-        // send (push)
-        // poll (shift)
-
-        /* OffsetAccess | Sequence */
-        // offset
-        // first
-        // last
-        // get
-
         /**
          * Adds one or more values to the end of the deque.
          */
@@ -526,49 +459,17 @@ function shift();
     }
 
 /**
- * The standard hashtable, implemented as Map in ext-ds currently. It is based
- * on the PHP array but is not identical. We can implement OffsetAccess because
- * insertion order is preserved.
+ * A set that allows duplicate values
  */
-final class HashMap implements
+final class MultiSet implements
     ArrayAccess,
     Traversable,
-    Container,
+    Container,      /* Countable */
     Clearable,
-    Sortable,
-    OffsetAccess,
-    MutableMap
-    {
-        /* ArrayAccess */
-        // offsetGet
-        // offsetSet
-        // offsetUnset
-        // offsetExists
-
-        /* Countable | Container */
-        // count
-        // isEmpty
-
-        /* Clearable */
-        // clear
-
-        /* Sortable */
-        // sort
-
-        /* OffsetAccess */
-        // offset
-        // first
-        // last
-
-        /* Map | MutableMap */
-        // get
-        // has
-        // keys
-        // values
-        //
-        // set
-        // unset
-    }
+    Transferable,
+    SortedSequence, /* Sequence */
+    MutableSet      /* Set */
+    {}
 
 /**
  * The Set equivalent of HashMap, which might not use a HashMap internally, but
@@ -577,48 +478,13 @@ final class HashMap implements
 final class HashSet implements
     ArrayAccess,
     Traversable,
-    Container,
+    Container,  /* Countable */
     Clearable,
     Sortable,
     Transferable,
-    OffsetAccess,
-    MutableSet
-    {
-        /* ArrayAccess */
-        // offsetGet
-        // offsetSet
-        // offsetUnset
-        // offsetExists
-
-        /* Container | Countable */
-        // count
-        // isEmpty
-
-        /* Clearable */
-        // clear
-
-        /* Sortable */
-        // sort
-
-        /* Transferable */
-        // send (add)
-        // poll (remove[0])
-
-        /* OffsetAccess */
-        // offset
-        // first
-        // last
-
-        /* Set | MutableSet */
-        // has
-        // or
-        // xor
-        // not
-        // and
-        //
-        // add
-        // remove
-    }
+    Sequence,   /* Countable */
+    MutableSet  /* Set */
+    {}
 
 /**
  * A structure that is always sorted and does not allow duplicate elements.
@@ -627,76 +493,57 @@ final class HashSet implements
  *
  * See: https://github.com/php-ds/ext-ds/issues/121
  */
-final class BinarySearchTree implements
+final class TreeSet implements
     Traversable,
-    Container,
+    Container,  /* Countable */
     Clearable,
-    MutableSet,
-    SortedSet
+    MutableSet, /* Set */
+    SortedSet   /* Set */
     {
-        /* Container | Countable */
-        // count
-        // isEmpty
-
-        /* Clearable */
-        // clear
-
-        /* Set | MutableSet */
-        // has
-        // or
-        // xor
-        // not
-        // and
-        //
-        // add
-        // remove
-
         /**
          * Creates a new bst using values from $iter.
          */
         public function __construct(callable $comparator = null) {}
+    }
 
-        /**
-         * Root, Left, Right
-         */
-        public function preorder(): iterable {}
-
-        /**
-         * Left, Root, Right
-         */
-        public function inorder(): iterable {}
-
-        /**
-         * Left, Right, Root
-         */
-        public function postorder(): iterable {}
+/**
+ * This structure is based on the same internal structure of the PHP array,
+ * but is not identical. We can use hashable objects as keys, and insertion
+ * order is still preserved.
+ */
+final class HashMap implements
+    ArrayAccess,
+    Traversable,
+    Container,  /* Countable */
+    Clearable,
+    Sortable,
+    Sequence,   /* Countable */
+    MutableMap  /* Map */
+    {}
 
-        /**
-         * Right, Root, Left
-         */
-        public function outorder(): iterable {}
-    }
+/**
+ *
+ */
+final class TreeMap implements
+    ArrayAccess,
+    Traversable,
+    Container,  /* Countable */
+    Clearable,
+    Sortable,
+    Sequence,   /* Countable */
+    MutableMap, /* Map */
+    SortedMap   /* Map */
+    {}
 
 /**
  * Stable heap with an optional comparator, defaulting to minimum. We could
  * potentially remove the nullable which clears up the min/max ambiguity.
  */
 final class Heap implements
-    Container,
+    Container, /* Countable */
     Clearable,
     Transferable
     {
-        /* Container | Countable */
-        // count
-        // isEmpty
-
-        /* Clearable */
-        // clear
-
-        /* Transferable */
-        // send (push)
-        // poll (shift)
-
         /**
          * Creates a new heap using an optional comparator.
          */

From 33c306d67916604c82d7d4bcc88bd9f3348c1b30 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 23 Feb 2019 02:05:18 -0800
Subject: [PATCH 35/50] Remove sorted sequence - is no good

---
 DRAFT.php | 18 ------------------
 1 file changed, 18 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 6260a8f..d74577f 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -177,11 +177,6 @@ function unset(int $offset);
     function insert(int $offset, ...$values);
 }
 
-/**
- * Indicates that a structure is a sequence that is always sorted.
- */
-interface SortedSequence extends Sequence {}
-
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
@@ -458,19 +453,6 @@ public function unshift(...$values);
         function shift();
     }
 
-/**
- * A set that allows duplicate values
- */
-final class MultiSet implements
-    ArrayAccess,
-    Traversable,
-    Container,      /* Countable */
-    Clearable,
-    Transferable,
-    SortedSequence, /* Sequence */
-    MutableSet      /* Set */
-    {}
-
 /**
  * The Set equivalent of HashMap, which might not use a HashMap internally, but
  * will always preserve insertion order.

From 3bfb198829585f9e7e302c119816d8691a67db54 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 25 Feb 2019 03:07:14 -0800
Subject: [PATCH 36/50] Multiset, sorted sequence, priority queue

---
 DRAFT.php | 169 +++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 128 insertions(+), 41 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index d74577f..7f8db56 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -177,6 +177,11 @@ function unset(int $offset);
     function insert(int $offset, ...$values);
 }
 
+/**
+ * Indicates that a structure is a sequence that is always sorted.
+ */
+interface SortedSequence extends Sequence {}
+
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
@@ -343,48 +348,11 @@ function send(...$values);
     function poll();
 }
 
+
 /******************************************************************************/
 /*                                 CLASSES                                    */
 /******************************************************************************/
 
-/**
- * A transfer adapter for stacks.
- */
-final class Stack implements
-    Container, /* Countable */
-    Transferable
-    {
-        /**
-         * Adds a value to the top of the stack.
-         */
-        function push(...$values);
-
-        /**
-         * Removes and returns the value at the top of the stack.
-         *
-         * @throws EmptyStateException
-         */
-        function pop();
-    }
-
-/**
- * A transfer adapter for queues.
- */
-final class Queue implements
-    Container, /* Countable */
-    Transferable
-    {
-        /**
-         * Adds a value to the queue.
-         */
-        function push(...$values);
-
-        /**
-         * Removes and returns the next value in the queue.
-         */
-        function shift();
-    }
-
 /**
  * A fixed-size immutable sequence.
  */
@@ -393,7 +361,9 @@ final class Tuple implements
     Container, /* Countable */
     Sequence,  /* Countable */
     Hashable   /* Immutable */
-    {}
+    {
+        public function __construct(...$values) {}
+    }
 
 /**
  * List structure, dynamic size, contiguous, no ops at the front.
@@ -488,6 +458,54 @@ final class TreeSet implements
         public function __construct(callable $comparator = null) {}
     }
 
+ /**
+ * A set that allows duplicate values, and keeps a count of the number of times
+ * a value appears in the set. This is a set because we can determine whether
+ * it contains a value _at all_, and it is also a sequence because the traversal
+ * can yield a value more than once.
+ */
+final class MultiSet implements
+    ArrayAccess,
+    Traversable,
+    Container,      /* Countable */
+    Clearable,
+    Transferable,
+    SortedSequence,/* Sequence */
+    SortedSet,     /* Set */
+    MutableSet     /* Set */
+    {
+        /**
+         * Adjusts the count of a value by a given number:
+         *   > 0: Add
+         *   < 0: Remove
+         *
+         * @return The resulting frequency after the adjustment.
+         */
+        public function adjust($value, int $count = 1): int {}
+
+        /**
+         * @return int The number of instances of a given value that $this set
+         *             contains, which could be 0 if not in the set at all.
+         */
+        public function freq($value): int {}
+
+        /**
+         * Returns an iterator of the values with the highest frequencies, where
+         * the key is the element and the value is the frequency.
+         *
+         * This uses a heap internally.
+         *   - Building the heap is O(n)
+         *   - Every iteration is O(log n)
+         */
+        public function rank(): iterable {}
+
+        /**
+         * Creates an iterable where the key is the element in the set and the
+         * value is the frequency / multiplicity.
+         */
+        public function enumerate(): iterable {}
+    }
+
 /**
  * This structure is based on the same internal structure of the PHP array,
  * but is not identical. We can use hashable objects as keys, and insertion
@@ -518,8 +536,45 @@ final class TreeMap implements
     {}
 
 /**
- * Stable heap with an optional comparator, defaulting to minimum. We could
- * potentially remove the nullable which clears up the min/max ambiguity.
+ * A transfer adapter for stacks.
+ */
+final class Stack implements
+    Container, /* Countable */
+    Transferable
+    {
+        /**
+         * Adds a value to the top of the stack.
+         */
+        function push(...$values);
+
+        /**
+         * Removes and returns the value at the top of the stack.
+         *
+         * @throws EmptyStateException
+         */
+        function pop();
+    }
+
+/**
+ * A transfer adapter for queues.
+ */
+final class Queue implements
+    Container, /* Countable */
+    Transferable
+    {
+        /**
+         * Adds a value to the queue.
+         */
+        function push(...$values);
+
+        /**
+         * Removes and returns the next value in the queue.
+         */
+        function shift();
+    }
+
+/**
+ * Stable heap with an optional comparator, defaulting to minimum.
  */
 final class Heap implements
     Container, /* Countable */
@@ -543,3 +598,35 @@ public function push(...$values) {}
          */
         public function shift() {}
     }
+
+/**
+ * A queue that yields value in order of priority, from high to low.
+ */
+final class PriorityQueue implements
+    Container, /* Countable */
+    Clearable,
+    Transferable
+    {
+        /**
+         * Creates a new priority queue using an optional comparator.
+         */
+        public function __construct(callable $comparator = null) {}
+
+        /**
+         * Adjusts the priority of a given value, setting it to the return value
+         * of the given mutator, then ensures that heap invariants are resolved.
+         */
+        public function adjust($value, callable $mutator) {}
+
+        /**
+         * Adds a value to the priority queue, using a given initial priority.
+         */
+        public function push($value, $priority) {}
+
+        /**
+         * Removes and returns the value at the front of the priority queue.
+         *
+         * @throws EmptyStateException
+         */
+        public function shift() {}
+    }

From 46dfd987353785af3058cc3023c95b15226e2c4b Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 25 Feb 2019 11:07:18 -0800
Subject: [PATCH 37/50] Add buffer class

---
 DRAFT.php | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/DRAFT.php b/DRAFT.php
index 7f8db56..ae0ae67 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -353,6 +353,93 @@ function poll();
 /*                                 CLASSES                                    */
 /******************************************************************************/
 
+/**
+ *
+ */
+final class Buffer implements
+    ArrayAccess,
+    Clearable,
+    Sortable
+    {
+        public const TYPE_BOOL      = 0; // bitset
+        public const TYPE_INTEGER   = 1;
+        public const TYPE_FLOAT     = 2;
+        public const TYPE_STRING    = 3;
+        public const TYPE_ARRAY     = 4;
+        public const TYPE_OBJECT    = 5;
+
+        /**
+         * Allow buffers to be truncated to 0.
+         */
+        public const MIN_CAPACITY = 0;
+
+        /**
+         * We must be able to accept and return capacity as integer, so we can
+         * only support up to max int values. This is absolutely massive though
+         * so should be incredibly rare to approach.
+         *
+         * @todo Should we consider when USE_ZEND_ALLOC might be false? Is this
+         *       a potential security or buffer overflow issue? I'm not sure...
+         */
+        public const MAX_CAPACITY = PHP_INT_MAX;
+
+        /**
+         * @throws UnexpectedValueException if the type is not a valid constant.
+         */
+        public function __construct(int $capacity, int $type) {}
+
+        /**
+         * @return int The type of the values in this buffer.
+         */
+        public function type(): int {}
+
+        /**
+         * @return int The current capacity of this buffer.
+         */
+        public function capacity(): int {}
+
+        /**
+         * Re-allocates this buffer to a new capacity, which may truncate the
+         * current buffer. It is up to user to manage this case.
+         *
+         * @throws RangeException if the capacity is not within the valid range:
+         *                        MIN_CAPACITY <= $capacity <= MAX_CAPACITY
+         */
+        public function realloc(int $capacity) {}
+
+        /**
+         * Accesses and casts the value at the given offset to the value type
+         * that this buffer stores. Type validation is done on write. If nothing
+         * has been stored at the given offset, NULL will be returned.
+         *
+         * @throws OffsetException if the offset is not within [0, capacity)
+         */
+        public function offsetGet($offset) {}
+
+        /**
+         * Sets the value at the given offset after validating its value type.
+         *
+         * Note: NULL is supported and will be returned as NULL.
+         *
+         * @throws OffsetException if the offset is not within [0, capacity)
+         */
+        public function offsetSet($offset, $value) {}
+
+        /**
+         * Sets the value at the given offset to NULL.
+         *
+         * @throws OffsetException if the offset is not within [0, capacity),
+         *                         unless called as part of a silent `unset`.
+         */
+        public function offsetUnset($offset) {}
+
+        /**
+         * Returns whether there is a non-NULL value at the given offset. This
+         * method returns FALSE if the offset is not within [0, capacity).
+         */
+        public function offsetExists($offset) {}
+    }
+
 /**
  * A fixed-size immutable sequence.
  */

From 4479adec9dc33356faf8a25ac611a4314c3e7cf7 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 25 Feb 2019 11:17:33 -0800
Subject: [PATCH 38/50] Adjust buffer access policy

---
 DRAFT.php | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index ae0ae67..75326fd 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -361,12 +361,12 @@ final class Buffer implements
     Clearable,
     Sortable
     {
-        public const TYPE_BOOL      = 0; // bitset
-        public const TYPE_INTEGER   = 1;
-        public const TYPE_FLOAT     = 2;
-        public const TYPE_STRING    = 3;
-        public const TYPE_ARRAY     = 4;
-        public const TYPE_OBJECT    = 5;
+        public const TYPE_BOOL      = 0; // undefined => false
+        public const TYPE_INTEGER   = 1; // undefined => 0
+        public const TYPE_FLOAT     = 2; // undefined => 0.0
+        public const TYPE_STRING    = 3; // undefined => NULL
+        public const TYPE_ARRAY     = 4; // undefined => NULL
+        public const TYPE_OBJECT    = 5; // undefined => NULL
 
         /**
          * Allow buffers to be truncated to 0.
@@ -378,8 +378,8 @@ final class Buffer implements
          * only support up to max int values. This is absolutely massive though
          * so should be incredibly rare to approach.
          *
-         * @todo Should we consider when USE_ZEND_ALLOC might be false? Is this
-         *       a potential security or buffer overflow issue? I'm not sure...
+         * @todo Should we consider when USE_ZEND_ALLOC might be 0? Is this a
+         *       potential security or buffer overflow issue? I'm not sure...
          */
         public const MAX_CAPACITY = PHP_INT_MAX;
 
@@ -410,7 +410,8 @@ public function realloc(int $capacity) {}
         /**
          * Accesses and casts the value at the given offset to the value type
          * that this buffer stores. Type validation is done on write. If nothing
-         * has been stored at the given offset, NULL will be returned.
+         * has been stored at the given offset, this method will return an
+         * appropriate "null" value for the type (see notes on constants).
          *
          * @throws OffsetException if the offset is not within [0, capacity)
          */
@@ -419,7 +420,7 @@ public function offsetGet($offset) {}
         /**
          * Sets the value at the given offset after validating its value type.
          *
-         * Note: NULL is supported and will be returned as NULL.
+         * Note: NULL is not supported. Use `unset` to clear a value by offset.
          *
          * @throws OffsetException if the offset is not within [0, capacity)
          */

From c2acf100ad3d29b8e6e978d9c91a9d83db846fcc Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 25 Feb 2019 11:19:39 -0800
Subject: [PATCH 39/50] Remove mention of cast when accessing buffer

---
 DRAFT.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/DRAFT.php b/DRAFT.php
index 75326fd..91c522d 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -408,7 +408,7 @@ public function capacity(): int {}
         public function realloc(int $capacity) {}
 
         /**
-         * Accesses and casts the value at the given offset to the value type
+         * Returns the value at the given offset as a standard value of the type
          * that this buffer stores. Type validation is done on write. If nothing
          * has been stored at the given offset, this method will return an
          * appropriate "null" value for the type (see notes on constants).

From 074f9198aa2b8999c4f2c09a2b8db7506945d685 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Mon, 25 Feb 2019 14:56:18 -0800
Subject: [PATCH 40/50] Rename buffer to allocation

---
 DRAFT.php | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 91c522d..44fcabe 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -356,7 +356,7 @@ function poll();
 /**
  *
  */
-final class Buffer implements
+final class Allocation implements
     ArrayAccess,
     Clearable,
     Sortable
@@ -580,10 +580,6 @@ public function freq($value): int {}
         /**
          * Returns an iterator of the values with the highest frequencies, where
          * the key is the element and the value is the frequency.
-         *
-         * This uses a heap internally.
-         *   - Building the heap is O(n)
-         *   - Every iteration is O(log n)
          */
         public function rank(): iterable {}
 

From 01e603bd6d9aebbe8e3250c1a75d20d552071a3e Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Sat, 2 Mar 2019 09:27:50 -0800
Subject: [PATCH 41/50] Add set sequence, adjust sorted sequence

---
 DRAFT.php | 123 +++++++++++++++++++++++++-----------------------------
 1 file changed, 57 insertions(+), 66 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 44fcabe..280a48a 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -51,13 +51,13 @@ class EmptyStateException implements StateException {}
  * Should be thrown when an index or key is not within the given access bounds
  * of a structure, such as attempting to access beyond the length.
  */
-class OffsetException extends RuntimeException implements AccessException  {}
+class InvalidOffsetException extends RuntimeException implements AccessException  {}
 
 /**
  * Should be thrown when a key is not supported, eg. when an attempt is made to
  * associate a value with NULL in Map, causing ambiguity in `find`.
  */
-class KeyException extends RuntimeException implements AccessException {}
+class InvalidKeyException extends RuntimeException implements AccessException {}
 
 
 /******************************************************************************/
@@ -124,12 +124,12 @@ function isEmpty(): bool;
  *
  * We extend Countable because it should always be possible to determine bounds.
  */
-interface Sequence extends Countable
+interface Sequence extends Container
 {
     /**
      * @return mixed The value at the
      *
-     * @throws OffsetException if the index is not within [0, size).
+     * @throws InvalidOffsetException if the index is not within [0, size).
      */
     function offset(int $offset);
 
@@ -156,7 +156,7 @@ interface MutableSequence extends Sequence
     /**
      * Sets the value at the given offset.
      *
-     * @throws OffsetException if the index is not within [0, size)
+     * @throws InvalidOffsetException if the index is not within [0, size)
      */
     function set(int $offset, $value);
 
@@ -164,7 +164,7 @@ function set(int $offset, $value);
      * Removes the value at the given offset, moving all successive
      * values one position towards the front.
      *
-     * @throws OffsetException if the index is not within [0, size)
+     * @throws InvalidOffsetException if the index is not within [0, size)
      */
     function unset(int $offset);
 
@@ -172,7 +172,7 @@ function unset(int $offset);
      * Moves all values between the given index and the end of the sequence
      * towards the back, then inserts the given values into the gap.
      *
-     * @throws OffsetException if the index is out of range [0, size]
+     * @throws InvalidOffsetException if the index is out of range [0, size]
      */
     function insert(int $offset, ...$values);
 }
@@ -183,53 +183,41 @@ function insert(int $offset, ...$values);
 interface SortedSequence extends Sequence {}
 
 /**
- * Indicates that a structure is designed to quickly determine whether a given
- * value is already contained by it.
- *
- * Note: The intention will be that all implementations of Set will use a
- *       do_operation handler to override |, ^, -, &.
+ * A structure to supports an efficient way to determine whether a given value
+ * is contained within the structure.
  */
-interface Set
+interface Searchable
 {
     /**
-     * @return bool TRUE if the value is in $this set, FALSE otherwise.
+     * @return bool TRUE if the value is in $this structure, FALSE otherwise.
      */
     function has($value): bool;
+}
 
+/**
+ * Indicates that a structure is designed to quickly determine whether a given
+ * value is already contained by it.
+ */
+interface Set extends Searchable, Container
+{
     /**
-     * Operator: |
-     *
-     * @todo what about the + operator?
-     *
      * @return static A set containing values in both $this and $other.
-     *
-     * @todo Naming - or, union, merge
      */
     function or(Set $other): Set;
 
     /**
-     * Operator: ^
-     *
      * @return static A set containing values in either $this or $other,
      *                but not in both.
      */
     function xor(Set $other): Set;
 
     /**
-     * Operator: -
-     *
      * @return static A set containing values in $this but not in $other.
-     *
-     * @todo Naming - not, diff, without
      */
     function not(Set $other): Set;
 
     /**
-     * Operator: &
-     *
      * @return static A set containing values in $this that are also in $other.
-     *
-     * @todo Naming - and, intersect
      */
     function and(Set $other): Set;
 }
@@ -267,7 +255,7 @@ interface SortedSet extends Set {}
  *
  * @todo Should Map require some kind of merge, diff, and intersection support?
  */
-interface Map
+interface Map extends Container
 {
     /**
      * @todo if NULL keys are not allowed, should we throw if $key is NULL?
@@ -279,16 +267,6 @@ interface Map
      */
     function get($key, $default = null);
 
-    /**
-     * @return bool TRUE if $this map has an association for the given $key,
-     *              FALSE otherwise.
-     *
-     * @todo if we don't extend Set, should we remove this in favour of
-     *       $map->keys()->has()? This would only work if keys() is O(1), which
-     *       would not always be the case.
-     */
-    function has($key): bool;
-
     /**
      * @return Set All keys from the map added in traversal order.
      *
@@ -312,7 +290,7 @@ interface MutableMap extends Map
     /**
      * Associates the $key with the $value, overriding any previous association.
      *
-     * @throws KeyException if the map implementation does not support
+     * @throws InvalidKeyException if the map implementation does not support
      *                             the given key, eg. NULL
      */
     function set($key, $value);
@@ -413,7 +391,7 @@ public function realloc(int $capacity) {}
          * has been stored at the given offset, this method will return an
          * appropriate "null" value for the type (see notes on constants).
          *
-         * @throws OffsetException if the offset is not within [0, capacity)
+         * @throws InvalidOffsetException if the offset is not within [0, capacity)
          */
         public function offsetGet($offset) {}
 
@@ -422,14 +400,14 @@ public function offsetGet($offset) {}
          *
          * Note: NULL is not supported. Use `unset` to clear a value by offset.
          *
-         * @throws OffsetException if the offset is not within [0, capacity)
+         * @throws InvalidOffsetException if the offset is not within [0, capacity)
          */
         public function offsetSet($offset, $value) {}
 
         /**
          * Sets the value at the given offset to NULL.
          *
-         * @throws OffsetException if the offset is not within [0, capacity),
+         * @throws InvalidOffsetException if the offset is not within [0, capacity),
          *                         unless called as part of a silent `unset`.
          */
         public function offsetUnset($offset) {}
@@ -446,11 +424,10 @@ public function offsetExists($offset) {}
  */
 final class Tuple implements
     Traversable,
-    Container, /* Countable */
-    Sequence,  /* Countable */
+    Sequence,  /* Container, Countable */
     Hashable   /* Immutable */
     {
-        public function __construct(...$values) {}
+        public function __construct(iterable $iter) {}
     }
 
 /**
@@ -459,10 +436,9 @@ public function __construct(...$values) {}
 final class Vector implements
     ArrayAccess,
     Traversable,
-    Container,      /* Countable */
     Clearable,
     Sortable,
-    MutableSequence /* Sequence, Countable */
+    MutableSequence /* Sequence, Container, Countable */
     {
         /**
          * Adds one or more values to the end of the vector.
@@ -481,10 +457,9 @@ function pop();
  * Double-ended-queue, supports prepend and append, but nothing in-between.
  */
 final class Deque implements
-    Container,      /* Countable */
     Clearable,
     Transferable,
-    SortedSequence  /* Sequence, Countable */
+    SortedSequence  /* Sequence, Container, Countable */
     {
         /**
          * Adds one or more values to the end of the deque.
@@ -511,6 +486,19 @@ public function unshift(...$values);
         function shift();
     }
 
+/**
+ * An immutable sequence that is evaluated and sorted on first access.
+ */
+final class SetSequence implements
+    ArrayAccess,
+    Traversable,
+    Immutable,
+    Searchable,
+    SortedSequence, /* Sequence, Container, Countable */
+    {
+        public function __construct(iterable $iter) {}
+    }
+
 /**
  * The Set equivalent of HashMap, which might not use a HashMap internally, but
  * will always preserve insertion order.
@@ -518,12 +506,11 @@ function shift();
 final class HashSet implements
     ArrayAccess,
     Traversable,
-    Container,  /* Countable */
     Clearable,
     Sortable,
     Transferable,
-    Sequence,   /* Countable */
-    MutableSet  /* Set */
+    Sequence,   /* Countable, Container, Countable */
+    MutableSet  /* Set, Container, Countable2 */
     {}
 
 /**
@@ -541,7 +528,7 @@ final class TreeSet implements
     SortedSet   /* Set */
     {
         /**
-         * Creates a new bst using values from $iter.
+         * Creates a new tree set using an optional comparator.
          */
         public function __construct(callable $comparator = null) {}
     }
@@ -558,12 +545,18 @@ final class MultiSet implements
     Container,      /* Countable */
     Clearable,
     Transferable,
-    SortedSequence,/* Sequence */
-    SortedSet,     /* Set */
     MutableSet     /* Set */
     {
         /**
-         * Adjusts the count of a value by a given number:
+         * Creates a new multiset using values from $iter.
+         */
+        public function __construct(callable $comparator = null) {}
+
+        /**
+         * Adjusts the count of a value by a given count. If the resulting count
+         * becomes <= 0, the value will be removed. If a value is not already in
+         * the set, it will be added before the count is adjusted.
+         *
          *   > 0: Add
          *   < 0: Remove
          *
@@ -598,10 +591,9 @@ public function enumerate(): iterable {}
 final class HashMap implements
     ArrayAccess,
     Traversable,
-    Container,  /* Countable */
     Clearable,
     Sortable,
-    Sequence,   /* Countable */
+    Sequence,   /* Container, Countable */
     MutableMap  /* Map */
     {}
 
@@ -611,16 +603,15 @@ final class HashMap implements
 final class TreeMap implements
     ArrayAccess,
     Traversable,
-    Container,  /* Countable */
     Clearable,
     Sortable,
-    Sequence,   /* Countable */
+    Sequence,   /* Container, Countable */
     MutableMap, /* Map */
     SortedMap   /* Map */
     {}
 
 /**
- * A transfer adapter for stacks.
+ * A basic first-in-last-out structure.
  */
 final class Stack implements
     Container, /* Countable */
@@ -640,7 +631,7 @@ function pop();
     }
 
 /**
- * A transfer adapter for queues.
+ * A basic first-in-first-out structure.
  */
 final class Queue implements
     Container, /* Countable */
@@ -684,7 +675,7 @@ public function shift() {}
     }
 
 /**
- * A queue that yields value in order of priority, from high to low.
+ * A queue that yields values in order of priority, from high to low.
  */
 final class PriorityQueue implements
     Container, /* Countable */

From 2aecc48dfbeb5f502418c9ab64f91ab4e60cd9d6 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Mar 2019 12:29:20 -0700
Subject: [PATCH 42/50] WIP update of countable/container and sequences

---
 DRAFT.php | 108 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 63 insertions(+), 45 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 280a48a..ae76fe2 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -67,7 +67,9 @@ class InvalidKeyException extends RuntimeException implements AccessException {}
 /**
  * Marker interface to indicate that a class is immutable.
  */
-interface Immutable {}
+interface Immutable 
+{
+}
 
 /**
  * Indicates that a structure can be cleared.
@@ -77,6 +79,21 @@ interface Clearable
     function clear();
 }
 
+/**
+ * Indicates that a structure contains elements and is aware of the number of
+ * items contained at any point in time. How a container must determine the
+ * count is implementation-specific.
+ *
+ * This interface does not imply that a structure is traversable.
+ */
+interface Countable extends \Countable
+{
+    /**
+     *
+     */
+    function count(): int;
+}
+
 /**
  * Indicates that a structure can be sorted by value.
  *
@@ -97,23 +114,25 @@ function sort(callable $comparator = null);
 interface Hashable extends Immutable
 {
     /**
-     * @return mixed A scalar value (or hashable delegate) that will be hashed.
+     * @return mixed A scalar value to be used when generating a hashcode.
      */
-    function getHashSource();
+    function getHashSource(): string;
 }
 
 /**
  * Indicates that a structure contains elements and is aware of the number of
  * items contained at any point in time. How a container must determine the
  * count is implementation-specific.
- *
- * This interface does not imply that a structure is traversable.
  */
-interface Container extends Countable
+interface Container
 {
     /**
-     * @return bool whether the container is empty, which can be TRUE even when
-     *              the number of elements is non-zero.
+     * @return bool TRUE if the value is in $this structure, FALSE otherwise.
+     */
+    function contains($value): bool;
+
+    /**
+     *
      */
     function isEmpty(): bool;
 }
@@ -124,14 +143,19 @@ function isEmpty(): bool;
  *
  * We extend Countable because it should always be possible to determine bounds.
  */
-interface Sequence extends Container
+interface Sequence extends Countable, Container
 {
     /**
      * @return mixed The value at the
      *
-     * @throws InvalidOffsetException if the index is not within [0, size).
+     * @throws InvalidOffsetException if the $offset is not within [0, size).
+     */
+    function get(int $offset);
+    
+    /**
+     * @return int The offset of the given value, or -1 if the value could not be found.
      */
-    function offset(int $offset);
+    function indexOf($value): int;
 
     /**
      * @return mixed The first value in $this structure.
@@ -180,25 +204,15 @@ function insert(int $offset, ...$values);
 /**
  * Indicates that a structure is a sequence that is always sorted.
  */
-interface SortedSequence extends Sequence {}
-
-/**
- * A structure to supports an efficient way to determine whether a given value
- * is contained within the structure.
- */
-interface Searchable
+interface SortedSequence extends Sequence 
 {
-    /**
-     * @return bool TRUE if the value is in $this structure, FALSE otherwise.
-     */
-    function has($value): bool;
 }
 
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
  */
-interface Set extends Searchable, Container
+interface Set extends Container
 {
     /**
      * @return static A set containing values in both $this and $other.
@@ -244,7 +258,9 @@ function remove($value);
 /**
  * Indicates that a structure is a set that is always sorted.
  */
-interface SortedSet extends Set {}
+interface SortedSet extends Set 
+{
+}
 
 /**
  * A structure that associates one value with another and provides the ability
@@ -255,7 +271,7 @@ interface SortedSet extends Set {}
  *
  * @todo Should Map require some kind of merge, diff, and intersection support?
  */
-interface Map extends Container
+interface Map
 {
     /**
      * @todo if NULL keys are not allowed, should we throw if $key is NULL?
@@ -305,7 +321,9 @@ function unset($key);
 /**
  * Indicates that a structure is a map that is always sorted.
  */
-interface SortedMap extends Map {}
+interface SortedMap extends Map 
+{
+}
 
 /**
  * Indicates that a structure can receive and produce values efficiently in a
@@ -424,7 +442,7 @@ public function offsetExists($offset) {}
  */
 final class Tuple implements
     Traversable,
-    Sequence,  /* Container, Countable */
+    Sequence,  /* Countable, Container */
     Hashable   /* Immutable */
     {
         public function __construct(iterable $iter) {}
@@ -438,7 +456,7 @@ final class Vector implements
     Traversable,
     Clearable,
     Sortable,
-    MutableSequence /* Sequence, Container, Countable */
+    MutableSequence /* Sequence, Countable, Container */
     {
         /**
          * Adds one or more values to the end of the vector.
@@ -459,7 +477,7 @@ function pop();
 final class Deque implements
     Clearable,
     Transferable,
-    SortedSequence  /* Sequence, Container, Countable */
+    MutableSequence  /* Sequence, Countable, Container */
     {
         /**
          * Adds one or more values to the end of the deque.
@@ -493,8 +511,8 @@ final class SetSequence implements
     ArrayAccess,
     Traversable,
     Immutable,
-    Searchable,
-    SortedSequence, /* Sequence, Container, Countable */
+    SortedSet,      /* Set, Container */
+    SortedSequence, /* Sequence, Countable, Container */
     {
         public function __construct(iterable $iter) {}
     }
@@ -506,11 +524,12 @@ public function __construct(iterable $iter) {}
 final class HashSet implements
     ArrayAccess,
     Traversable,
+    Countable,
     Clearable,
-    Sortable,
     Transferable,
-    Sequence,   /* Countable, Container, Countable */
-    MutableSet  /* Set, Container, Countable2 */
+    Sortable,
+    Sequence,     /* Countable, Container */
+    MutableSet    /* Set, Container */
     {}
 
 /**
@@ -522,10 +541,10 @@ final class HashSet implements
  */
 final class TreeSet implements
     Traversable,
-    Container,  /* Countable */
+    Countable,
     Clearable,
-    MutableSet, /* Set */
-    SortedSet   /* Set */
+    MutableSet, /* Set, Container */
+    SortedSet   /* Set, Container */
     {
         /**
          * Creates a new tree set using an optional comparator.
@@ -542,7 +561,7 @@ public function __construct(callable $comparator = null) {}
 final class MultiSet implements
     ArrayAccess,
     Traversable,
-    Container,      /* Countable */
+    Countable,
     Clearable,
     Transferable,
     MutableSet     /* Set */
@@ -591,9 +610,9 @@ public function enumerate(): iterable {}
 final class HashMap implements
     ArrayAccess,
     Traversable,
+    Countable,
     Clearable,
     Sortable,
-    Sequence,   /* Container, Countable */
     MutableMap  /* Map */
     {}
 
@@ -603,9 +622,8 @@ final class HashMap implements
 final class TreeMap implements
     ArrayAccess,
     Traversable,
+    Countable,
     Clearable,
-    Sortable,
-    Sequence,   /* Container, Countable */
     MutableMap, /* Map */
     SortedMap   /* Map */
     {}
@@ -614,7 +632,7 @@ final class TreeMap implements
  * A basic first-in-last-out structure.
  */
 final class Stack implements
-    Container, /* Countable */
+    Countable,
     Transferable
     {
         /**
@@ -634,7 +652,7 @@ function pop();
  * A basic first-in-first-out structure.
  */
 final class Queue implements
-    Container, /* Countable */
+    Countable,
     Transferable
     {
         /**
@@ -652,7 +670,7 @@ function shift();
  * Stable heap with an optional comparator, defaulting to minimum.
  */
 final class Heap implements
-    Container, /* Countable */
+    Countable,
     Clearable,
     Transferable
     {
@@ -678,7 +696,7 @@ public function shift() {}
  * A queue that yields values in order of priority, from high to low.
  */
 final class PriorityQueue implements
-    Container, /* Countable */
+    Countable,
     Clearable,
     Transferable
     {

From 90150e2f56b6bcb61478f5ad294758c5d7936848 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Mar 2019 16:49:03 -0700
Subject: [PATCH 43/50] Changes to exceptions

---
 DRAFT.php | 147 +++++++++++++++++++++++++-----------------------------
 1 file changed, 67 insertions(+), 80 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index ae76fe2..ae1ac98 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -15,50 +15,28 @@
 namespace Ds;
 
 use Countable;
-use RuntimeException;
+use LogicException;
 use Traversable;
 
-/******************************************************************************/
-/*                           ORDERING & EQUALITY                              */
-/******************************************************************************/
-
-/**
- * @todo Proposal: == for objects, === otherwise.
- */
-
 /******************************************************************************/
 /*                                EXCEPTIONS                                  */
 /******************************************************************************/
 
 /**
- * Should be thrown when a structure is accessed unexpectedly, or in a way that
- * is undefined, or to avoid ambiguous results. This is a marker interface to
- * support catching all access exceptions.
+ * Should be thrown when an empty container is polled in an empty state.
  */
-interface AccessException {}
+class EmptyContainerException extends LogicException {}
 
 /**
- * Marker interface for all state-related exceptions, such as heap corruption.
+ * Should be thrown when a key is not supported.
  */
-interface StateException {}
-
-/**
- * Should be thrown when an empty container is accessed a clear, obvious result.
- */
-class EmptyStateException implements StateException {}
+class InvalidKeyException extends LogicException {}
 
 /**
  * Should be thrown when an index or key is not within the given access bounds
  * of a structure, such as attempting to access beyond the length.
  */
-class InvalidOffsetException extends RuntimeException implements AccessException  {}
-
-/**
- * Should be thrown when a key is not supported, eg. when an attempt is made to
- * associate a value with NULL in Map, causing ambiguity in `find`.
- */
-class InvalidKeyException extends RuntimeException implements AccessException {}
-
+class InvalidOffsetException extends LogicException {}
 
 /******************************************************************************/
 /*                                INTERFACES                                  */
@@ -80,38 +58,35 @@ function clear();
 }
 
 /**
- * Indicates that a structure contains elements and is aware of the number of
- * items contained at any point in time. How a container must determine the
- * count is implementation-specific.
+ * Indicates that a structure can be sorted by value.
  *
- * This interface does not imply that a structure is traversable.
+ * This interface does not guarantee that the sorting algorithm will be stable.
+ * Implementations should use a domain-specific tiebreaker when required.
  */
-interface Countable extends \Countable
+interface Sortable
 {
     /**
-     *
+     * @return static A sorted copy, or $this structure sorted in-place.
      */
-    function count(): int;
+    function sort(callable $comparator = null);
 }
 
 /**
- * Indicates that a structure can be sorted by value.
- *
- * This interface does not guarantee that the sorting algorithm will be stable.
- * Implementations should use a domain-specific tiebreaker when required.
+ * Indicates that an object is designed to be equated using `==`.
+ * The operator will be overloaded for all classes that implement Equatable.
  */
-interface Sortable
+interface Equatable 
 {
     /**
-     * @return static A sorted copy, or $this structure sorted in-place.
+     * @param static $other 
      */
-    function sort(callable $comparator = null);
+    function equals($other): bool;
 }
 
 /**
  * Indicates that an object is designed to be used in hash-based structures.
  */
-interface Hashable extends Immutable
+interface Hashable extends Equatable, Immutable
 {
     /**
      * @return mixed A scalar value to be used when generating a hashcode.
@@ -121,18 +96,17 @@ function getHashSource(): string;
 
 /**
  * Indicates that a structure contains elements and is aware of the number of
- * items contained at any point in time. How a container must determine the
- * count is implementation-specific.
+ * items contained at any point in time.
  */
-interface Container
+interface Container extends \Countable
 {
     /**
-     * @return bool TRUE if the value is in $this structure, FALSE otherwise.
+     * @return int The number of items in this container.
      */
-    function contains($value): bool;
+    function count(): int;
 
     /**
-     *
+     * @return bool Whether this container is empty.
      */
     function isEmpty(): bool;
 }
@@ -141,9 +115,9 @@ function isEmpty(): bool;
  * Indicates that a structure can be accessed using a zero-based integer index
  * indicating the position of an element from the beginning of the structure.
  *
- * We extend Countable because it should always be possible to determine bounds.
+ * We extend Container because it should always be possible to determine bounds.
  */
-interface Sequence extends Countable, Container
+interface Sequence extends Container
 {
     /**
      * @return mixed The value at the
@@ -157,17 +131,22 @@ function get(int $offset);
      */
     function indexOf($value): int;
 
+    /**
+     * @return bool TRUE if this sequence contains the given value, FALSE otherwise.
+     */
+    function contains($value): bool;
+
     /**
      * @return mixed The first value in $this structure.
      *
-     * @throws EmptyStateException
+     * @throws EmptyContainerException
      */
     function first();
 
     /**
      * @return mixed The last value in $this structure.
      *
-     * @throws EmptyStateException
+     * @throws EmptyContainerException
      */
     function last();
 }
@@ -212,8 +191,13 @@ interface SortedSequence extends Sequence
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
  */
-interface Set extends Container
+interface Set
 {
+    /**
+     * @return bool TRUE if this set contains the given value, FALSE otherwise.
+     */
+    function contains($value): bool;
+
     /**
      * @return static A set containing values in both $this and $other.
      */
@@ -339,7 +323,7 @@ function send(...$values);
     /**
      * Removes and returns the next value produced by this transferable.
      *
-     * @throws EmptyStateException
+     * @throws EmptyContainerException
      */
     function poll();
 }
@@ -442,7 +426,7 @@ public function offsetExists($offset) {}
  */
 final class Tuple implements
     Traversable,
-    Sequence,  /* Countable, Container */
+    Sequence,  /* Container, \Countable */
     Hashable   /* Immutable */
     {
         public function __construct(iterable $iter) {}
@@ -455,8 +439,9 @@ final class Vector implements
     ArrayAccess,
     Traversable,
     Clearable,
+    Container,
     Sortable,
-    MutableSequence /* Sequence, Countable, Container */
+    MutableSequence /* Sequence, Container, \Countable */
     {
         /**
          * Adds one or more values to the end of the vector.
@@ -466,7 +451,7 @@ function push(...$values);
         /**
          * Removes and returns the value at the end of the vector.
          *
-         * @throws EmptyStateException
+         * @throws EmptyContainerException
          */
         function pop();
     }
@@ -476,8 +461,9 @@ function pop();
  */
 final class Deque implements
     Clearable,
+    Container,
     Transferable,
-    MutableSequence  /* Sequence, Countable, Container */
+    MutableSequence  /* Sequence, Container, \Countable */
     {
         /**
          * Adds one or more values to the end of the deque.
@@ -487,7 +473,7 @@ function push(...$values);
         /**
          * Removes and returns the value at the end of the deque.
          *
-         * @throws EmptyStateException
+         * @throws EmptyContainerException
          */
         function pop();
 
@@ -499,7 +485,7 @@ public function unshift(...$values);
         /**
          * Removes and returns the value at the start of the deque.
          *
-         * @throws EmptyStateException
+         * @throws EmptyContainerException
          */
         function shift();
     }
@@ -510,9 +496,10 @@ function shift();
 final class SetSequence implements
     ArrayAccess,
     Traversable,
+    Container,
     Immutable,
-    SortedSet,      /* Set, Container */
-    SortedSequence, /* Sequence, Countable, Container */
+    SortedSet,      /* Set */
+    SortedSequence, /* Sequence, Container, \Countable */
     {
         public function __construct(iterable $iter) {}
     }
@@ -524,12 +511,12 @@ public function __construct(iterable $iter) {}
 final class HashSet implements
     ArrayAccess,
     Traversable,
-    Countable,
     Clearable,
+    Container,
     Transferable,
     Sortable,
-    Sequence,     /* Countable, Container */
-    MutableSet    /* Set, Container */
+    Sequence,     /* Container, \Countable */
+    MutableSet    /* Set */
     {}
 
 /**
@@ -541,10 +528,10 @@ final class HashSet implements
  */
 final class TreeSet implements
     Traversable,
-    Countable,
     Clearable,
-    MutableSet, /* Set, Container */
-    SortedSet   /* Set, Container */
+    Container,
+    MutableSet, /* Set */
+    SortedSet   /* Set */
     {
         /**
          * Creates a new tree set using an optional comparator.
@@ -561,9 +548,9 @@ public function __construct(callable $comparator = null) {}
 final class MultiSet implements
     ArrayAccess,
     Traversable,
-    Countable,
-    Clearable,
     Transferable,
+    Container,
+    Clearable,
     MutableSet     /* Set */
     {
         /**
@@ -610,7 +597,7 @@ public function enumerate(): iterable {}
 final class HashMap implements
     ArrayAccess,
     Traversable,
-    Countable,
+    Container,
     Clearable,
     Sortable,
     MutableMap  /* Map */
@@ -622,7 +609,7 @@ final class HashMap implements
 final class TreeMap implements
     ArrayAccess,
     Traversable,
-    Countable,
+    Container,
     Clearable,
     MutableMap, /* Map */
     SortedMap   /* Map */
@@ -632,7 +619,7 @@ final class TreeMap implements
  * A basic first-in-last-out structure.
  */
 final class Stack implements
-    Countable,
+    Container,
     Transferable
     {
         /**
@@ -643,7 +630,7 @@ function push(...$values);
         /**
          * Removes and returns the value at the top of the stack.
          *
-         * @throws EmptyStateException
+         * @throws EmptyContainerException
          */
         function pop();
     }
@@ -652,7 +639,7 @@ function pop();
  * A basic first-in-first-out structure.
  */
 final class Queue implements
-    Countable,
+    Container,
     Transferable
     {
         /**
@@ -670,7 +657,7 @@ function shift();
  * Stable heap with an optional comparator, defaulting to minimum.
  */
 final class Heap implements
-    Countable,
+    Container,
     Clearable,
     Transferable
     {
@@ -687,7 +674,7 @@ public function push(...$values) {}
         /**
          * Removes and returns the value at the top of the heap.
          *
-         * @throws EmptyStateException
+         * @throws EmptyContainerException
          */
         public function shift() {}
     }
@@ -696,7 +683,7 @@ public function shift() {}
  * A queue that yields values in order of priority, from high to low.
  */
 final class PriorityQueue implements
-    Countable,
+    Container,
     Clearable,
     Transferable
     {
@@ -719,7 +706,7 @@ public function push($value, $priority) {}
         /**
          * Removes and returns the value at the front of the priority queue.
          *
-         * @throws EmptyStateException
+         * @throws EmptyContainerException
          */
         public function shift() {}
     }

From 0028bed3c1a3993be26e1526f78bc2eed53a4382 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Mar 2019 17:19:20 -0700
Subject: [PATCH 44/50] Implement Equatable

---
 DRAFT.php | 34 +++++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index ae1ac98..dcc729f 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -22,21 +22,26 @@
 /*                                EXCEPTIONS                                  */
 /******************************************************************************/
 
+/**
+ * A marker interface that is implemented by all exceptions within this library. 
+ */
+interface Exception implements Throwable {}
+
 /**
  * Should be thrown when an empty container is polled in an empty state.
  */
-class EmptyContainerException extends LogicException {}
+class EmptyContainerException extends RuntimeException implements Exception {}
 
 /**
  * Should be thrown when a key is not supported.
  */
-class InvalidKeyException extends LogicException {}
+class InvalidKeyException extends LogicException implements Exception {}
 
 /**
  * Should be thrown when an index or key is not within the given access bounds
  * of a structure, such as attempting to access beyond the length.
  */
-class InvalidOffsetException extends LogicException {}
+class InvalidOffsetException extends LogicException implements Exception {}
 
 /******************************************************************************/
 /*                                INTERFACES                                  */
@@ -114,8 +119,6 @@ function isEmpty(): bool;
 /**
  * Indicates that a structure can be accessed using a zero-based integer index
  * indicating the position of an element from the beginning of the structure.
- *
- * We extend Container because it should always be possible to determine bounds.
  */
 interface Sequence extends Container
 {
@@ -190,6 +193,8 @@ interface SortedSequence extends Sequence
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
+ * 
+ * 
  */
 interface Set
 {
@@ -338,6 +343,7 @@ function poll();
  */
 final class Allocation implements
     ArrayAccess,
+    Equatable,  /* Same type, capacity and values at each offset. */
     Clearable,
     Sortable
     {
@@ -426,8 +432,9 @@ public function offsetExists($offset) {}
  */
 final class Tuple implements
     Traversable,
+    Equatable,
     Sequence,  /* Container, \Countable */
-    Hashable   /* Immutable */
+    Hashable   /* Equatable, Immutable */
     {
         public function __construct(iterable $iter) {}
     }
@@ -438,6 +445,7 @@ public function __construct(iterable $iter) {}
 final class Vector implements
     ArrayAccess,
     Traversable,
+    Equatable,      /* Same values in the same order. */
     Clearable,
     Container,
     Sortable,
@@ -460,10 +468,12 @@ function pop();
  * Double-ended-queue, supports prepend and append, but nothing in-between.
  */
 final class Deque implements
+    Equatable,        /* Same values in the same order. */
     Clearable,
     Container,
-    Transferable,
-    MutableSequence  /* Sequence, Container, \Countable */
+    Sortable,
+    MutableSequence,  /* Sequence, Container, \Countable */
+    Transferable
     {
         /**
          * Adds one or more values to the end of the deque.
@@ -496,6 +506,7 @@ function shift();
 final class SetSequence implements
     ArrayAccess,
     Traversable,
+    Equatable,      /* Same values in the same order. */
     Container,
     Immutable,
     SortedSet,      /* Set */
@@ -511,6 +522,7 @@ public function __construct(iterable $iter) {}
 final class HashSet implements
     ArrayAccess,
     Traversable,
+    Equatable,    /* Same values in the same order. */
     Clearable,
     Container,
     Transferable,
@@ -528,6 +540,7 @@ final class HashSet implements
  */
 final class TreeSet implements
     Traversable,
+    Equatable,  /* Same values in the same order. */
     Clearable,
     Container,
     MutableSet, /* Set */
@@ -548,10 +561,11 @@ public function __construct(callable $comparator = null) {}
 final class MultiSet implements
     ArrayAccess,
     Traversable,
+    Equatable,    /* Same values and frequencies. */
     Transferable,
     Container,
     Clearable,
-    MutableSet     /* Set */
+    MutableSet    /* Set */
     {
         /**
          * Creates a new multiset using values from $iter.
@@ -597,6 +611,7 @@ public function enumerate(): iterable {}
 final class HashMap implements
     ArrayAccess,
     Traversable,
+    Equatable,  /* Same key => value associations, ordering not considered. */
     Container,
     Clearable,
     Sortable,
@@ -609,6 +624,7 @@ final class HashMap implements
 final class TreeMap implements
     ArrayAccess,
     Traversable,
+    Equatable,  /* Same key => value associations. */
     Container,
     Clearable,
     MutableMap, /* Map */

From daeca5bc2f79d6cba4b88c1ae9a329a83dff1d0a Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rudolf.theunissen@gmail.com>
Date: Wed, 13 Mar 2019 18:01:49 -0700
Subject: [PATCH 45/50] Update DRAFT.php

---
 DRAFT.php | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index dcc729f..dce11cc 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -63,17 +63,14 @@ function clear();
 }
 
 /**
- * Indicates that a structure can be sorted by value.
- *
- * This interface does not guarantee that the sorting algorithm will be stable.
- * Implementations should use a domain-specific tiebreaker when required.
+ * Indicates that a structure can be sorted.
  */
 interface Sortable
 {
     /**
-     * @return static A sorted copy, or $this structure sorted in-place.
+     * Sorts the keys and/or values of $this structure in place.
      */
-    function sort(callable $comparator = null);
+    function sort(callable $comparator = null): void;
 }
 
 /**

From 2027710b468d01ff366332eb64b2b922a122d7e3 Mon Sep 17 00:00:00 2001
From: Michael Moravec <me@majkl.me>
Date: Thu, 14 Mar 2019 00:25:54 +0100
Subject: [PATCH 46/50] Clearly indicate void returns

---
 DRAFT.php | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index dce11cc..7af5b9c 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -59,7 +59,7 @@ interface Immutable
  */
 interface Clearable
 {
-    function clear();
+    function clear(): void;
 }
 
 /**
@@ -161,7 +161,7 @@ interface MutableSequence extends Sequence
      *
      * @throws InvalidOffsetException if the index is not within [0, size)
      */
-    function set(int $offset, $value);
+    function set(int $offset, $value): void;
 
     /**
      * Removes the value at the given offset, moving all successive
@@ -169,7 +169,7 @@ function set(int $offset, $value);
      *
      * @throws InvalidOffsetException if the index is not within [0, size)
      */
-    function unset(int $offset);
+    function unset(int $offset): void;
 
     /**
      * Moves all values between the given index and the end of the sequence
@@ -177,7 +177,7 @@ function unset(int $offset);
      *
      * @throws InvalidOffsetException if the index is out of range [0, size]
      */
-    function insert(int $offset, ...$values);
+    function insert(int $offset, ...$values): void;
 }
 
 /**
@@ -230,7 +230,7 @@ interface MutableSet extends Set
     /**
      * Adds the given value to $this set if it is not already in $this set.
      */
-    function add(...$values);
+    function add(...$values): void;
 
     /**
      * Removes a given value from $this set, or does nothing if that value could
@@ -238,7 +238,7 @@ function add(...$values);
      * removal. This method therefore promises only that the given value is not
      * a member of the set on return.
      */
-    function remove($value);
+    function remove($value): void;
 }
 
 /**
@@ -295,13 +295,13 @@ interface MutableMap extends Map
      * @throws InvalidKeyException if the map implementation does not support
      *                             the given key, eg. NULL
      */
-    function set($key, $value);
+    function set($key, $value): void;
 
     /**
      * Removes the given $key from the map, and does nothing if they key could
      * not be found. This emulates `unset`
      */
-    function unset($key);
+    function unset($key): void;
 }
 
 /**
@@ -320,7 +320,7 @@ interface Transferable
     /**
      * Offers one or more values to this transferable.
      */
-    function send(...$values);
+    function send(...$values): void;
 
     /**
      * Removes and returns the next value produced by this transferable.
@@ -388,7 +388,7 @@ public function capacity(): int {}
          * @throws RangeException if the capacity is not within the valid range:
          *                        MIN_CAPACITY <= $capacity <= MAX_CAPACITY
          */
-        public function realloc(int $capacity) {}
+        public function realloc(int $capacity): void {}
 
         /**
          * Returns the value at the given offset as a standard value of the type
@@ -407,7 +407,7 @@ public function offsetGet($offset) {}
          *
          * @throws InvalidOffsetException if the offset is not within [0, capacity)
          */
-        public function offsetSet($offset, $value) {}
+        public function offsetSet($offset, $value): void {}
 
         /**
          * Sets the value at the given offset to NULL.
@@ -415,7 +415,7 @@ public function offsetSet($offset, $value) {}
          * @throws InvalidOffsetException if the offset is not within [0, capacity),
          *                         unless called as part of a silent `unset`.
          */
-        public function offsetUnset($offset) {}
+        public function offsetUnset($offset): void {}
 
         /**
          * Returns whether there is a non-NULL value at the given offset. This
@@ -451,7 +451,7 @@ final class Vector implements
         /**
          * Adds one or more values to the end of the vector.
          */
-        function push(...$values);
+        function push(...$values): void;
 
         /**
          * Removes and returns the value at the end of the vector.
@@ -475,7 +475,7 @@ final class Deque implements
         /**
          * Adds one or more values to the end of the deque.
          */
-        function push(...$values);
+        function push(...$values): void;
 
         /**
          * Removes and returns the value at the end of the deque.
@@ -487,7 +487,7 @@ function pop();
         /**
          * Adds one or more values to the start of the deque.
          */
-        public function unshift(...$values);
+        public function unshift(...$values): void;
 
         /**
          * Removes and returns the value at the start of the deque.
@@ -638,7 +638,7 @@ final class Stack implements
         /**
          * Adds a value to the top of the stack.
          */
-        function push(...$values);
+        function push(...$values): void;
 
         /**
          * Removes and returns the value at the top of the stack.
@@ -658,7 +658,7 @@ final class Queue implements
         /**
          * Adds a value to the queue.
          */
-        function push(...$values);
+        function push(...$values): void;
 
         /**
          * Removes and returns the next value in the queue.
@@ -682,7 +682,7 @@ public function __construct(callable $comparator = null) {}
         /**
          * Adds a value to the heap.
          */
-        public function push(...$values) {}
+        public function push(...$values): void {}
 
         /**
          * Removes and returns the value at the top of the heap.
@@ -709,12 +709,12 @@ public function __construct(callable $comparator = null) {}
          * Adjusts the priority of a given value, setting it to the return value
          * of the given mutator, then ensures that heap invariants are resolved.
          */
-        public function adjust($value, callable $mutator) {}
+        public function adjust($value, callable $mutator): void {}
 
         /**
          * Adds a value to the priority queue, using a given initial priority.
          */
-        public function push($value, $priority) {}
+        public function push($value, $priority): void {}
 
         /**
          * Removes and returns the value at the front of the priority queue.

From ececd3986b2956216cf4b71032448677acbfee2f Mon Sep 17 00:00:00 2001
From: Michael Moravec <me@majkl.me>
Date: Thu, 14 Mar 2019 00:56:29 +0100
Subject: [PATCH 47/50] Complete documentation for parameter and return types

---
 DRAFT.php | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 124 insertions(+), 7 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 7af5b9c..89daeef 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -69,6 +69,8 @@ interface Sortable
 {
     /**
      * Sorts the keys and/or values of $this structure in place.
+     *
+     * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this this sortable.
      */
     function sort(callable $comparator = null): void;
 }
@@ -80,7 +82,9 @@ function sort(callable $comparator = null): void;
 interface Equatable 
 {
     /**
-     * @param static $other 
+     * @param static $other The other structure to compare with.
+     *
+     * @return bool Whether this structure equates to the given one.
      */
     function equals($other): bool;
 }
@@ -120,18 +124,24 @@ function isEmpty(): bool;
 interface Sequence extends Container
 {
     /**
-     * @return mixed The value at the
+     * @param mixed $value The value to look for.
+     *
+     * @return mixed The value at the given offset.
      *
      * @throws InvalidOffsetException if the $offset is not within [0, size).
      */
     function get(int $offset);
     
     /**
+     * @param mixed $value The value to search for.
+     *
      * @return int The offset of the given value, or -1 if the value could not be found.
      */
     function indexOf($value): int;
 
     /**
+     * @param mixed $value The value to search for.
+     *
      * @return bool TRUE if this sequence contains the given value, FALSE otherwise.
      */
     function contains($value): bool;
@@ -159,6 +169,9 @@ interface MutableSequence extends Sequence
     /**
      * Sets the value at the given offset.
      *
+     * @param int   $offset The offset at which the value should be set.
+     * @param mixed $value  The value to set.
+     *
      * @throws InvalidOffsetException if the index is not within [0, size)
      */
     function set(int $offset, $value): void;
@@ -167,6 +180,8 @@ function set(int $offset, $value): void;
      * Removes the value at the given offset, moving all successive
      * values one position towards the front.
      *
+     * @param int The offset to be removed.
+     *
      * @throws InvalidOffsetException if the index is not within [0, size)
      */
     function unset(int $offset): void;
@@ -175,6 +190,9 @@ function unset(int $offset): void;
      * Moves all values between the given index and the end of the sequence
      * towards the back, then inserts the given values into the gap.
      *
+     * @param int   $offset    The offset to insert values at.
+     * @param mixed ...$values The value to insert at the specified offset.
+     *
      * @throws InvalidOffsetException if the index is out of range [0, size]
      */
     function insert(int $offset, ...$values): void;
@@ -196,27 +214,37 @@ interface SortedSequence extends Sequence
 interface Set
 {
     /**
+     * @param mixed $value The value to search for.
+     *
      * @return bool TRUE if this set contains the given value, FALSE otherwise.
      */
     function contains($value): bool;
 
     /**
+     * @param self $other The other set to union with.
+     *
      * @return static A set containing values in both $this and $other.
      */
     function or(Set $other): Set;
 
     /**
+     * @param self $other The other set to xor with.
+     *
      * @return static A set containing values in either $this or $other,
      *                but not in both.
      */
     function xor(Set $other): Set;
 
     /**
+     * @param self $other The other set to diff against.
+     *
      * @return static A set containing values in $this but not in $other.
      */
     function not(Set $other): Set;
 
     /**
+     * @param self $other The other set to intersect with.
+     *
      * @return static A set containing values in $this that are also in $other.
      */
     function and(Set $other): Set;
@@ -228,11 +256,15 @@ function and(Set $other): Set;
 interface MutableSet extends Set
 {
     /**
+     * @param mixed ...$values The values to be added.
+     *
      * Adds the given value to $this set if it is not already in $this set.
      */
     function add(...$values): void;
 
     /**
+     * @param mixed $value The value to be removed.
+     *
      * Removes a given value from $this set, or does nothing if that value could
      * not be found. The caller can use `has` to determine membership before
      * removal. This method therefore promises only that the given value is not
@@ -262,6 +294,9 @@ interface Map
     /**
      * @todo if NULL keys are not allowed, should we throw if $key is NULL?
      *
+     * @param mixed $key     The key at which the value should be looked for.
+     * @param mixed $default The fallback value of no value exists at specified key.
+     *
      * @return mixed The value associated with the $key, or $default if the key
      *               could not be found and a $default was provided.
      *
@@ -292,6 +327,9 @@ interface MutableMap extends Map
     /**
      * Associates the $key with the $value, overriding any previous association.
      *
+     * @param mixed $key   The key to set the value for.
+     * @param mixed $value The value to set.
+     *
      * @throws InvalidKeyException if the map implementation does not support
      *                             the given key, eg. NULL
      */
@@ -300,6 +338,8 @@ function set($key, $value): void;
     /**
      * Removes the given $key from the map, and does nothing if they key could
      * not be found. This emulates `unset`
+     *
+     * @param mixed $key The key at which the value should be unset.
      */
     function unset($key): void;
 }
@@ -319,12 +359,16 @@ interface Transferable
 {
     /**
      * Offers one or more values to this transferable.
+     *
+     * @param mixed ...$values The values that should be transferred.
      */
     function send(...$values): void;
 
     /**
      * Removes and returns the next value produced by this transferable.
      *
+     * @return mixed The transferred value.
+     *
      * @throws EmptyContainerException
      */
     function poll();
@@ -367,6 +411,9 @@ final class Allocation implements
         public const MAX_CAPACITY = PHP_INT_MAX;
 
         /**
+         * @param int $capacity Initial capacity of the structure.
+         * @param int $type     The type for which the allocation is being made (one of Allocation::Type_* constants).
+         *
          * @throws UnexpectedValueException if the type is not a valid constant.
          */
         public function __construct(int $capacity, int $type) {}
@@ -385,6 +432,8 @@ public function capacity(): int {}
          * Re-allocates this buffer to a new capacity, which may truncate the
          * current buffer. It is up to user to manage this case.
          *
+         * @param int $capacity The new capacity to which this buffer should be resized.
+         *
          * @throws RangeException if the capacity is not within the valid range:
          *                        MIN_CAPACITY <= $capacity <= MAX_CAPACITY
          */
@@ -396,6 +445,10 @@ public function realloc(int $capacity): void {}
          * has been stored at the given offset, this method will return an
          * appropriate "null" value for the type (see notes on constants).
          *
+         * @param mixed $offset The offset at which to look for a value.
+         *
+         * @return mixed The value at the specified offset.
+         *
          * @throws InvalidOffsetException if the offset is not within [0, capacity)
          */
         public function offsetGet($offset) {}
@@ -405,6 +458,9 @@ public function offsetGet($offset) {}
          *
          * Note: NULL is not supported. Use `unset` to clear a value by offset.
          *
+         * @param mixed $offset The offset at which to set the new value.
+         * @param mixed $value  The value to set at the specified offset.
+         *
          * @throws InvalidOffsetException if the offset is not within [0, capacity)
          */
         public function offsetSet($offset, $value): void {}
@@ -412,6 +468,8 @@ public function offsetSet($offset, $value): void {}
         /**
          * Sets the value at the given offset to NULL.
          *
+         * @param mixed $offset The offset at which to unset a value.
+         *
          * @throws InvalidOffsetException if the offset is not within [0, capacity),
          *                         unless called as part of a silent `unset`.
          */
@@ -420,8 +478,12 @@ public function offsetUnset($offset): void {}
         /**
          * Returns whether there is a non-NULL value at the given offset. This
          * method returns FALSE if the offset is not within [0, capacity).
+         *
+         * @param mixed $offset The offset at which to look for a value.
+         *
+         * @return bool Whether the specified offset contains a value or not.
          */
-        public function offsetExists($offset) {}
+        public function offsetExists($offset): bool {}
     }
 
 /**
@@ -433,7 +495,10 @@ final class Tuple implements
     Sequence,  /* Container, \Countable */
     Hashable   /* Equatable, Immutable */
     {
-        public function __construct(iterable $iter) {}
+        /**
+         * @param mixed[] $source The values used to fill this tuple.
+         */
+        public function __construct(iterable $source) {}
     }
 
 /**
@@ -450,12 +515,16 @@ final class Vector implements
     {
         /**
          * Adds one or more values to the end of the vector.
+         *
+         * @param mixed ...$values The values to append to the vector.
          */
         function push(...$values): void;
 
         /**
          * Removes and returns the value at the end of the vector.
          *
+         * @return mixed The value at the end of the vector.
+         *
          * @throws EmptyContainerException
          */
         function pop();
@@ -474,24 +543,32 @@ final class Deque implements
     {
         /**
          * Adds one or more values to the end of the deque.
+         *
+         * @param mixed ...$values The values to push at the end of this deque.
          */
         function push(...$values): void;
 
         /**
          * Removes and returns the value at the end of the deque.
          *
+         * @return mixed The value at the end of this deque.
+         *
          * @throws EmptyContainerException
          */
         function pop();
 
         /**
          * Adds one or more values to the start of the deque.
+         *
+         * @param mixed ...$values The values to insert to the start of this deque.
          */
         public function unshift(...$values): void;
 
         /**
          * Removes and returns the value at the start of the deque.
          *
+         * @return mixed The value at the beginning of this deque.
+         *
          * @throws EmptyContainerException
          */
         function shift();
@@ -509,7 +586,10 @@ final class SetSequence implements
     SortedSet,      /* Set */
     SortedSequence, /* Sequence, Container, \Countable */
     {
-        public function __construct(iterable $iter) {}
+        /**
+         * @param mixed[] $source The values used to fill this sequence.
+         */
+        public function __construct(iterable $source) {}
     }
 
 /**
@@ -545,6 +625,8 @@ final class TreeSet implements
     {
         /**
          * Creates a new tree set using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this tree set.
          */
         public function __construct(callable $comparator = null) {}
     }
@@ -566,6 +648,8 @@ final class MultiSet implements
     {
         /**
          * Creates a new multiset using values from $iter.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this multi set.
          */
         public function __construct(callable $comparator = null) {}
 
@@ -577,11 +661,16 @@ public function __construct(callable $comparator = null) {}
          *   > 0: Add
          *   < 0: Remove
          *
-         * @return The resulting frequency after the adjustment.
+         * @param mixed $value The value to be adjusted.
+         * @param int   $count The amount by which the value should be adjusted.
+         *
+         * @return int The resulting frequency after the adjustment.
          */
         public function adjust($value, int $count = 1): int {}
 
         /**
+         * @param mixed $value The value of which the frequency is to be inspected.
+         *
          * @return int The number of instances of a given value that $this set
          *             contains, which could be 0 if not in the set at all.
          */
@@ -590,12 +679,16 @@ public function freq($value): int {}
         /**
          * Returns an iterator of the values with the highest frequencies, where
          * the key is the element and the value is the frequency.
+         *
+         * @return iterable<mixed, int> The values as keys and their frequency/multiplicity as values.
          */
         public function rank(): iterable {}
 
         /**
          * Creates an iterable where the key is the element in the set and the
          * value is the frequency / multiplicity.
+         *
+         * @return iterable<mixed, int> The values as keys and their frequency/multiplicity as values.
          */
         public function enumerate(): iterable {}
     }
@@ -637,12 +730,16 @@ final class Stack implements
     {
         /**
          * Adds a value to the top of the stack.
+         *
+         * @param mixed ...$values The values to be pushed onto the stack.
          */
         function push(...$values): void;
 
         /**
          * Removes and returns the value at the top of the stack.
          *
+         * @return mixed The value at the top.
+         *
          * @throws EmptyContainerException
          */
         function pop();
@@ -657,11 +754,15 @@ final class Queue implements
     {
         /**
          * Adds a value to the queue.
+         *
+         * @param mixed ...$values The values to be pushed into the queue.
          */
         function push(...$values): void;
 
         /**
          * Removes and returns the next value in the queue.
+         *
+         * @return mixed The next value.
          */
         function shift();
     }
@@ -676,17 +777,23 @@ final class Heap implements
     {
         /**
          * Creates a new heap using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this heap.
          */
         public function __construct(callable $comparator = null) {}
 
         /**
          * Adds a value to the heap.
+         *
+         * @param mixed ...$values The values to be pushed onto the heap.
          */
         public function push(...$values): void {}
 
         /**
          * Removes and returns the value at the top of the heap.
          *
+         * @return mixed The value at the top.
+         *
          * @throws EmptyContainerException
          */
         public function shift() {}
@@ -702,23 +809,33 @@ final class PriorityQueue implements
     {
         /**
          * Creates a new priority queue using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this heap.
          */
         public function __construct(callable $comparator = null) {}
 
         /**
          * Adjusts the priority of a given value, setting it to the return value
          * of the given mutator, then ensures that heap invariants are resolved.
+         *
+         * @param mixed           $value   The value of which the priority should be adjusted.
+         * @param callable(): int $mutator The mutator returning the new priority.
          */
         public function adjust($value, callable $mutator): void {}
 
         /**
          * Adds a value to the priority queue, using a given initial priority.
+         *
+         * @param mixed $value    The value to be pushed into the queue.
+         * @param int   $priority The priority of the inserted value.
          */
-        public function push($value, $priority): void {}
+        public function push($value, int $priority): void {}
 
         /**
          * Removes and returns the value at the front of the priority queue.
          *
+         * @return mixed The next value.
+         *
          * @throws EmptyContainerException
          */
         public function shift() {}

From 438e01f2c9c8c5c5995ea86f89ab3ba03f95e1a7 Mon Sep 17 00:00:00 2001
From: Michael Moravec <me@majkl.me>
Date: Thu, 14 Mar 2019 10:24:37 +0100
Subject: [PATCH 48/50] Rename Allocation::realloc() -> Allocation::resize()

---
 DRAFT.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 89daeef..9ed1798 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -429,7 +429,7 @@ public function type(): int {}
         public function capacity(): int {}
 
         /**
-         * Re-allocates this buffer to a new capacity, which may truncate the
+         * Re-sizes this buffer to a new capacity, which may truncate the
          * current buffer. It is up to user to manage this case.
          *
          * @param int $capacity The new capacity to which this buffer should be resized.
@@ -437,7 +437,7 @@ public function capacity(): int {}
          * @throws RangeException if the capacity is not within the valid range:
          *                        MIN_CAPACITY <= $capacity <= MAX_CAPACITY
          */
-        public function realloc(int $capacity): void {}
+        public function resize(int $capacity): void {}
 
         /**
          * Returns the value at the given offset as a standard value of the type

From 88c1f6f00e6e921fbbf02b2aa596aae8008be946 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rtheunissen@php.net>
Date: Thu, 21 Mar 2019 23:30:09 -0700
Subject: [PATCH 49/50] Immutable type considerations, early days

---
 DRAFT.php | 641 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 528 insertions(+), 113 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 9ed1798..95b55fa 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -23,7 +23,7 @@
 /******************************************************************************/
 
 /**
- * A marker interface that is implemented by all exceptions within this library. 
+ * A marker interface that is implemented by all exceptions within this library.
  */
 interface Exception implements Throwable {}
 
@@ -50,7 +50,7 @@ class InvalidOffsetException extends LogicException implements Exception {}
 /**
  * Marker interface to indicate that a class is immutable.
  */
-interface Immutable 
+interface Immutable
 {
 }
 
@@ -68,18 +68,21 @@ function clear(): void;
 interface Sortable
 {
     /**
-     * Sorts the keys and/or values of $this structure in place.
+     * Sorts the keys and/or values of $this structure.
      *
-     * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this this sortable.
+     * This interface does not specify whether sorting is done in-place or not.
+     *
+     * @return static A sorted version of $this structure,
+     *                which could be itself or a sorted copy.
      */
-    function sort(callable $comparator = null): void;
+    function sort(callable $comparator = null): self;
 }
 
 /**
  * Indicates that an object is designed to be equated using `==`.
  * The operator will be overloaded for all classes that implement Equatable.
  */
-interface Equatable 
+interface Equatable
 {
     /**
      * @param static $other The other structure to compare with.
@@ -97,7 +100,7 @@ interface Hashable extends Equatable, Immutable
     /**
      * @return mixed A scalar value to be used when generating a hashcode.
      */
-    function getHashSource(): string;
+    function hashSource(): string;
 }
 
 /**
@@ -121,7 +124,7 @@ function isEmpty(): bool;
  * Indicates that a structure can be accessed using a zero-based integer index
  * indicating the position of an element from the beginning of the structure.
  */
-interface Sequence extends Container
+interface Sequence extends \Countable
 {
     /**
      * @param mixed $value The value to look for.
@@ -131,7 +134,7 @@ interface Sequence extends Container
      * @throws InvalidOffsetException if the $offset is not within [0, size).
      */
     function get(int $offset);
-    
+
     /**
      * @param mixed $value The value to search for.
      *
@@ -198,18 +201,55 @@ function unset(int $offset): void;
     function insert(int $offset, ...$values): void;
 }
 
+/**
+ * A sequence which can be modified, either in-place or as a copy.
+ */
+interface ImmutableSequence extends Sequence, Immutable
+{
+    /**
+     * Sets the value at the given offset.
+     *
+     * @param int   $offset The offset at which the value should be set.
+     * @param mixed $value  The value to set.
+     *
+     * @throws InvalidOffsetException if the index is not within [0, size)
+     */
+    function set(int $offset, $value): ImmutableSequence;
+
+    /**
+     * Removes the value at the given offset, moving all successive
+     * values one position towards the front.
+     *
+     * @param int The offset to be removed.
+     *
+     * @throws InvalidOffsetException if the index is not within [0, size)
+     */
+    function unset(int $offset): ImmutableSequence;
+
+    /**
+     * Moves all values between the given index and the end of the sequence
+     * towards the back, then inserts the given values into the gap.
+     *
+     * @param int   $offset    The offset to insert values at.
+     * @param mixed ...$values The value to insert at the specified offset.
+     *
+     * @throws InvalidOffsetException if the index is out of range [0, size]
+     */
+    function insert(int $offset, ...$values): ImmutableSequence;
+}
+
 /**
  * Indicates that a structure is a sequence that is always sorted.
  */
-interface SortedSequence extends Sequence 
+interface SortedSequence extends Sequence
 {
 }
 
 /**
  * Indicates that a structure is designed to quickly determine whether a given
  * value is already contained by it.
- * 
- * 
+ *
+ *
  */
 interface Set
 {
@@ -273,13 +313,81 @@ function add(...$values): void;
     function remove($value): void;
 }
 
+/**
+ * A set which can be modified, either in-place or as a copy.
+ */
+interface ImmutableSet extends Set, Immutable
+{
+    /**
+     * @param mixed ...$values The values to be added.
+     *
+     * Adds the given value to $this set if it is not already in $this set.
+     */
+    function add(...$values): ImmutableSet;
+
+    /**
+     * @param mixed $value The value to be removed.
+     *
+     * Removes a given value from $this set, or does nothing if that value could
+     * not be found. The caller can use `has` to determine membership before
+     * removal. This method therefore promises only that the given value is not
+     * a member of the set on return.
+     */
+    function remove($value): ImmutableSet;
+}
+
 /**
  * Indicates that a structure is a set that is always sorted.
  */
-interface SortedSet extends Set 
+interface SortedSet extends Set
 {
 }
 
+/**
+ * A set that allows duplicate values, and keeps a count of the number of times
+ * a value appears in the set.
+ */
+interface MultiSet extends Set
+{
+    /**
+     * @param mixed $value The value of which the frequency is to be inspected.
+     *
+     * @return int The number of instances of a given value that $this set
+     *             contains, which could be 0 if not in the set at all.
+     */
+    public function countOf($value): int {}
+
+    /**
+     * Adjusts the count of a value by a given count. If the resulting count
+     * becomes <= 0, the value will be removed. If a value is not already in
+     * the set, it will be added before the count is adjusted.
+     *
+     *   > 0: Add
+     *   < 0: Remove
+     *
+     * @param mixed $value The value to be adjusted.
+     * @param int   $count The amount by which the value should be adjusted.
+     *
+     * @return int The resulting frequency after the adjustment.
+     */
+    public function adjust($value, int $count): int {}
+
+    /**
+     * Returns an iterator of the values with the highest frequencies, where
+     * the key is the element and the value is the frequency.
+     *
+     * @return iterable<mixed, int> The values as keys and their frequency as values.
+     */
+    public function rank(): iterable {}
+
+    /**
+     * Creates an iterable where keys are produced multiple times based on their frequency.
+     *
+     * @return iterable<int, mixed> Keys as values, possibly multiple times.
+     */
+    public function enumerate(): iterable {}
+}
+
 /**
  * A structure that associates one value with another and provides the ability
  * to query or adjust associations efficiently.
@@ -345,9 +453,34 @@ function unset($key): void;
 }
 
 /**
- * Indicates that a structure is a map that is always sorted.
+ * A map which can be modified, either in-place or as a copy.
+ */
+interface ImmutableMap extends Map, Immutable
+{
+    /**
+     * Associates the $key with the $value, overriding any previous association.
+     *
+     * @param mixed $key   The key to set the value for.
+     * @param mixed $value The value to set.
+     *
+     * @throws InvalidKeyException if the map implementation does not support
+     *                             the given key, eg. NULL
+     */
+    function set($key, $value): ImmutableMap;
+
+    /**
+     * Removes the given $key from the map, and does nothing if they key could
+     * not be found. This emulates `unset`
+     *
+     * @param mixed $key The key at which the value should be unset.
+     */
+    function unset($key): ImmutableMap;
+}
+
+/**
+ * Indicates that a structure is a map that is always sorted by key.
  */
-interface SortedMap extends Map 
+interface SortedMap extends Map
 {
 }
 
@@ -382,9 +515,9 @@ function poll();
 /**
  *
  */
-final class Allocation implements
+final class Buffer implements
     ArrayAccess,
-    Equatable,  /* Same type, capacity and values at each offset. */
+    Equatable,  /* Same type, size and values at each offset. */
     Clearable,
     Sortable
     {
@@ -398,25 +531,26 @@ final class Allocation implements
         /**
          * Allow buffers to be truncated to 0.
          */
-        public const MIN_CAPACITY = 0;
+        public const MIN_SIZE = 0;
 
         /**
-         * We must be able to accept and return capacity as integer, so we can
-         * only support up to max int values. This is absolutely massive though
-         * so should be incredibly rare to approach.
+         * We must be able to accept and return size as integer, so we can only
+         * support up to max int values. This is absolutely massive though so
+         * should be incredibly rare to approach.
          *
          * @todo Should we consider when USE_ZEND_ALLOC might be 0? Is this a
          *       potential security or buffer overflow issue? I'm not sure...
          */
-        public const MAX_CAPACITY = PHP_INT_MAX;
+        public const MAX_SIZE = PHP_INT_MAX;
 
         /**
          * @param int $capacity Initial capacity of the structure.
-         * @param int $type     The type for which the allocation is being made (one of Allocation::Type_* constants).
+         * @param int $type     Value type of the buffer, which must be one of
+         *                      the Buffer::Type_* constants.
          *
          * @throws UnexpectedValueException if the type is not a valid constant.
          */
-        public function __construct(int $capacity, int $type) {}
+        public function __construct(int $size, int $type) {}
 
         /**
          * @return int The type of the values in this buffer.
@@ -424,20 +558,20 @@ public function __construct(int $capacity, int $type) {}
         public function type(): int {}
 
         /**
-         * @return int The current capacity of this buffer.
+         * @return int The current size of this buffer.
          */
-        public function capacity(): int {}
+        public function size(): int {}
 
         /**
-         * Re-sizes this buffer to a new capacity, which may truncate the
-         * current buffer. It is up to user to manage this case.
+         * Re-sizes this buffer to a new size, which may truncate the current buffer.
+         * It is up to user to manage this case.
          *
-         * @param int $capacity The new capacity to which this buffer should be resized.
+         * @param int $size The new size to which this buffer should be resized.
          *
-         * @throws RangeException if the capacity is not within the valid range:
-         *                        MIN_CAPACITY <= $capacity <= MAX_CAPACITY
+         * @throws RangeException if the size is not within the valid range:
+         *                        MIN_SIZE <= $size <= MAX_SIZE
          */
-        public function resize(int $capacity): void {}
+        public function resize(int $size): void {}
 
         /**
          * Returns the value at the given offset as a standard value of the type
@@ -449,7 +583,7 @@ public function resize(int $capacity): void {}
          *
          * @return mixed The value at the specified offset.
          *
-         * @throws InvalidOffsetException if the offset is not within [0, capacity)
+         * @throws InvalidOffsetException if the offset is not within [0, size)
          */
         public function offsetGet($offset) {}
 
@@ -461,7 +595,7 @@ public function offsetGet($offset) {}
          * @param mixed $offset The offset at which to set the new value.
          * @param mixed $value  The value to set at the specified offset.
          *
-         * @throws InvalidOffsetException if the offset is not within [0, capacity)
+         * @throws InvalidOffsetException if the offset is not within [0, size)
          */
         public function offsetSet($offset, $value): void {}
 
@@ -470,14 +604,14 @@ public function offsetSet($offset, $value): void {}
          *
          * @param mixed $offset The offset at which to unset a value.
          *
-         * @throws InvalidOffsetException if the offset is not within [0, capacity),
+         * @throws InvalidOffsetException if the offset is not within [0, size),
          *                         unless called as part of a silent `unset`.
          */
         public function offsetUnset($offset): void {}
 
         /**
          * Returns whether there is a non-NULL value at the given offset. This
-         * method returns FALSE if the offset is not within [0, capacity).
+         * method returns FALSE if the offset is not within [0, size).
          *
          * @param mixed $offset The offset at which to look for a value.
          *
@@ -491,8 +625,10 @@ public function offsetExists($offset): bool {}
  */
 final class Tuple implements
     Traversable,
+    Immutable,
     Equatable,
-    Sequence,  /* Container, \Countable */
+    Container, /* \Countable */
+    Sequence,  /* \Countable */
     Hashable   /* Equatable, Immutable */
     {
         /**
@@ -509,16 +645,16 @@ final class Vector implements
     Traversable,
     Equatable,      /* Same values in the same order. */
     Clearable,
-    Container,
+    Container,      /* \Countable */
     Sortable,
-    MutableSequence /* Sequence, Container, \Countable */
+    MutableSequence /* Sequence, \Countable */
     {
         /**
          * Adds one or more values to the end of the vector.
          *
          * @param mixed ...$values The values to append to the vector.
          */
-        function push(...$values): void;
+        public function push(...$values): void;
 
         /**
          * Removes and returns the value at the end of the vector.
@@ -527,26 +663,64 @@ function push(...$values): void;
          *
          * @throws EmptyContainerException
          */
-        function pop();
+        public function pop();
+    }
+
+/**
+ * List structure, dynamic size, contiguous, no ops at the front.
+ */
+final class ImmutableVector implements
+    ArrayAccess,
+    Traversable,
+    Equatable,        /* Same values in the same order. */
+    Container,        /* \Countable */
+    Sortable,
+    ImmutableSequence /* Sequence, \Countable, Immutable */
+    {
+        /**
+         * Adds one or more values to the end of the vector.
+         *
+         * @param mixed ...$values The values to append to the vector.
+         */
+        public function push(...$values): ImmutableVector;
+
+        /**
+         * Returns the value at the end of the vector.
+         *
+         * @return mixed The value at the end of the vector.
+         *
+         * @throws EmptyContainerException
+         */
+        public function peek();
+
+        /**
+         * Removes and returns the value at the end of the vector.
+         *
+         * @return mixed The value at the end of the vector.
+         *
+         * @throws EmptyContainerException
+         */
+        public function pop(): ImmutableVector;
     }
 
 /**
  * Double-ended-queue, supports prepend and append, but nothing in-between.
  */
 final class Deque implements
+    Traversable,
     Equatable,        /* Same values in the same order. */
     Clearable,
-    Container,
+    Container,        /* \Countable */
     Sortable,
-    MutableSequence,  /* Sequence, Container, \Countable */
-    Transferable
+    Sequence,         /* \Countable */
+    Transferable,
     {
         /**
          * Adds one or more values to the end of the deque.
          *
          * @param mixed ...$values The values to push at the end of this deque.
          */
-        function push(...$values): void;
+        public function push(...$values): void {}
 
         /**
          * Removes and returns the value at the end of the deque.
@@ -555,14 +729,14 @@ function push(...$values): void;
          *
          * @throws EmptyContainerException
          */
-        function pop();
+        public function pop() {}
 
         /**
          * Adds one or more values to the start of the deque.
          *
          * @param mixed ...$values The values to insert to the start of this deque.
          */
-        public function unshift(...$values): void;
+        public function unshift(...$values): void {}
 
         /**
          * Removes and returns the value at the start of the deque.
@@ -571,7 +745,53 @@ public function unshift(...$values): void;
          *
          * @throws EmptyContainerException
          */
-        function shift();
+        public function shift() {}
+    }
+
+/**
+ * Double-ended-queue.
+ */
+final class ImmutableDeque implements
+    Traversable,
+    Equatable,          /* Same values in the same order. */
+    Clearable,
+    Container,          /* \Countable */
+    Sortable,
+    Sequence,           /* \Countable */
+    Immutable,
+    Transferable
+    {
+        /**
+         * Adds one or more values to the end of the deque.
+         *
+         * @param mixed ...$values The values to push at the end of this deque.
+         */
+        public function push(...$values): ImmutableDeque {}
+
+        /**
+         * Removes and returns the value at the end of the deque.
+         *
+         * @return ImmutableDeque
+         *
+         * @throws EmptyContainerException
+         */
+        public function pop(): ImmutableDeque {}
+
+        /**
+         * Adds one or more values to the start of the deque.
+         *
+         * @param mixed ...$values The values to insert to the start of this deque.
+         */
+        public function unshift(...$values): ImmutableDeque {}
+
+        /**
+         * Removes and returns the value at the start of the deque.
+         *
+         * @return ImmutableDeque
+         *
+         * @throws EmptyContainerException
+         */
+        public function shift(): ImmutableDeque {}
     }
 
 /**
@@ -581,10 +801,10 @@ final class SetSequence implements
     ArrayAccess,
     Traversable,
     Equatable,      /* Same values in the same order. */
-    Container,
+    Container,      /* \Countable */
     Immutable,
     SortedSet,      /* Set */
-    SortedSequence, /* Sequence, Container, \Countable */
+    SortedSequence, /* Sequence, \Countable */
     {
         /**
          * @param mixed[] $source The values used to fill this sequence.
@@ -599,15 +819,29 @@ public function __construct(iterable $source) {}
 final class HashSet implements
     ArrayAccess,
     Traversable,
+    Transferable,
     Equatable,    /* Same values in the same order. */
     Clearable,
-    Container,
-    Transferable,
+    Container,    /* \Countable */
     Sortable,
-    Sequence,     /* Container, \Countable */
+    Sequence,     /* \Countable */
     MutableSet    /* Set */
     {}
 
+/**
+ *
+ */
+final class ImmutableHashSet implements
+    ArrayAccess,
+    Traversable,
+    Transferable,
+    Equatable,    /* Same values in the same order. */
+    Clearable,
+    Container,    /* \Countable */
+    Sortable,
+    Sequence,     /* \Countable */
+    ImmutableSet  /* Set, Immutable */
+    {}
 /**
  * A structure that is always sorted and does not allow duplicate elements.
  *
@@ -619,7 +853,7 @@ final class TreeSet implements
     Traversable,
     Equatable,  /* Same values in the same order. */
     Clearable,
-    Container,
+    Container,  /* \Countable */
     MutableSet, /* Set */
     SortedSet   /* Set */
     {
@@ -631,20 +865,54 @@ final class TreeSet implements
         public function __construct(callable $comparator = null) {}
     }
 
- /**
+/**
+ *
+ */
+final class ImmutableTreeSet implements
+    Traversable,
+    Equatable,    /* Same values in the same order. */
+    Clearable,
+    Container,    /* \Countable */
+    ImmutableSet, /* Set, Immutable */
+    SortedSet     /* Set */
+    {
+        /**
+         * Creates a new tree set using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this tree set.
+         */
+        public function __construct(callable $comparator = null) {}
+    }
+
+/**
+ * A set that allows duplicate values, and keeps a count of the number of times
+ * a value appears in the set.
+ */
+final class HashMultiSet implements
+    ArrayAccess,
+    Traversable,
+    Transferable,
+    Equatable,    /* Same values and frequencies. */
+    Clearable,
+    Container,    /* \Countable */
+    MutableSet,   /* Set */
+    MultiSet      /* Set */
+    {}
+
+/**
  * A set that allows duplicate values, and keeps a count of the number of times
- * a value appears in the set. This is a set because we can determine whether
- * it contains a value _at all_, and it is also a sequence because the traversal
- * can yield a value more than once.
+ * a value appears in the set.
  */
-final class MultiSet implements
+final class TreeMultiSet implements
     ArrayAccess,
     Traversable,
     Equatable,    /* Same values and frequencies. */
     Transferable,
-    Container,
+    Container,    /* \Countable */
     Clearable,
-    MutableSet    /* Set */
+    MutableSet,   /* Set */
+    SortedSet,    /* Set */
+    MultiSet      /* Set */
     {
         /**
          * Creates a new multiset using values from $iter.
@@ -652,45 +920,6 @@ final class MultiSet implements
          * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this multi set.
          */
         public function __construct(callable $comparator = null) {}
-
-        /**
-         * Adjusts the count of a value by a given count. If the resulting count
-         * becomes <= 0, the value will be removed. If a value is not already in
-         * the set, it will be added before the count is adjusted.
-         *
-         *   > 0: Add
-         *   < 0: Remove
-         *
-         * @param mixed $value The value to be adjusted.
-         * @param int   $count The amount by which the value should be adjusted.
-         *
-         * @return int The resulting frequency after the adjustment.
-         */
-        public function adjust($value, int $count = 1): int {}
-
-        /**
-         * @param mixed $value The value of which the frequency is to be inspected.
-         *
-         * @return int The number of instances of a given value that $this set
-         *             contains, which could be 0 if not in the set at all.
-         */
-        public function freq($value): int {}
-
-        /**
-         * Returns an iterator of the values with the highest frequencies, where
-         * the key is the element and the value is the frequency.
-         *
-         * @return iterable<mixed, int> The values as keys and their frequency/multiplicity as values.
-         */
-        public function rank(): iterable {}
-
-        /**
-         * Creates an iterable where the key is the element in the set and the
-         * value is the frequency / multiplicity.
-         *
-         * @return iterable<mixed, int> The values as keys and their frequency/multiplicity as values.
-         */
-        public function enumerate(): iterable {}
     }
 
 /**
@@ -702,12 +931,25 @@ final class HashMap implements
     ArrayAccess,
     Traversable,
     Equatable,  /* Same key => value associations, ordering not considered. */
-    Container,
+    Container,  /* \Countable */
     Clearable,
     Sortable,
     MutableMap  /* Map */
     {}
 
+/**
+ *
+ */
+final class ImmutableHashMap implements
+    ArrayAccess,
+    Traversable,
+    Equatable,  /* Same key => value associations, ordering not considered. */
+    Container,  /* \Countable */
+    Clearable,
+    Sortable,
+    ImmutableMap  /* Map, Immutable */
+    {}
+
 /**
  *
  */
@@ -715,17 +957,30 @@ final class TreeMap implements
     ArrayAccess,
     Traversable,
     Equatable,  /* Same key => value associations. */
-    Container,
+    Container,  /* \Countable */
     Clearable,
     MutableMap, /* Map */
     SortedMap   /* Map */
     {}
 
+/**
+ *
+ */
+final class ImmutableTreeMap implements
+    ArrayAccess,
+    Traversable,
+    Equatable,    /* Same key => value associations. */
+    Container,    /* \Countable */
+    Clearable,
+    ImmutableMap, /* Map, Immutable */
+    SortedMap     /* Map */
+    {}
+
 /**
  * A basic first-in-last-out structure.
  */
 final class Stack implements
-    Container,
+    Container,   /* \Countable */
     Transferable
     {
         /**
@@ -733,7 +988,7 @@ final class Stack implements
          *
          * @param mixed ...$values The values to be pushed onto the stack.
          */
-        function push(...$values): void;
+        public function push(...$values): void {}
 
         /**
          * Removes and returns the value at the top of the stack.
@@ -742,36 +997,101 @@ function push(...$values): void;
          *
          * @throws EmptyContainerException
          */
-        function pop();
+        public function pop() {}
+    }
+
+/**
+ * A basic first-in-last-out structure.
+ */
+final class ImmutableStack implements
+    Container,   /* \Countable */
+    Transferable,
+    Immutable
+    {
+        /**
+         * Adds a value to the top of the stack.
+         *
+         * @param mixed ...$values The values to be pushed onto the stack.
+         */
+        public function push(...$values): ImmutableStack {}
+
+        /**
+         * Returns the value at the top of the stack.
+         *
+         * @return mixed The value at the top.
+         *
+         * @throws EmptyContainerException
+         */
+        public function peek() {}
+
+        /**
+         *
+         *
+         * @return ImmutableStack
+         *
+         * @throws EmptyContainerException
+         */
+        public function pop(): ImmutableStack {}
     }
 
 /**
  * A basic first-in-first-out structure.
  */
 final class Queue implements
-    Container,
-    Transferable
+    Container,   /* \Countable */
+    Transferable,
+    Immutable
     {
         /**
          * Adds a value to the queue.
          *
          * @param mixed ...$values The values to be pushed into the queue.
          */
-        function push(...$values): void;
+        public function push(...$values): void {}
 
         /**
          * Removes and returns the next value in the queue.
          *
          * @return mixed The next value.
          */
-        function shift();
+        public function shift();
+    }
+
+/**
+ * A basic first-in-first-out structure.
+ */
+final class ImmutableQueue implements
+    Container,   /* \Countable */
+    Transferable,
+    Immutable
+    {
+        /**
+         * Adds a value to the queue.
+         *
+         * @param mixed ...$values The values to be pushed into the queue.
+         */
+        public function push(...$values): ImmutableQueue {}
+
+        /**
+         * Returns the next value in the queue.
+         *
+         * @return mixed The next value.
+         */
+        public function peek();
+
+        /**
+         *
+         *
+         * @return ImmutableQueue
+         */
+        public function shift(): ImmutableQueue;
     }
 
 /**
  * Stable heap with an optional comparator, defaulting to minimum.
  */
 final class Heap implements
-    Container,
+    Container,   /* \Countable */
     Clearable,
     Transferable
     {
@@ -799,11 +1119,53 @@ public function push(...$values): void {}
         public function shift() {}
     }
 
+/**
+ * Stable heap with an optional comparator, defaulting to minimum.
+ */
+final class ImmutableHeap implements
+    Container,   /* \Countable */
+    Clearable,
+    Transferable,
+    Immutable
+    {
+        /**
+         * Creates a new heap using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this heap.
+         */
+        public function __construct(callable $comparator = null) {}
+
+        /**
+         * Adds a value to the heap.
+         *
+         * @param mixed ...$values The values to be pushed onto the heap.
+         */
+        public function push(...$values): ImmutableHeap {}
+
+        /**
+         * Returns the value at the top of the heap.
+         *
+         * @return mixed
+         *
+         * @throws EmptyContainerException
+         */
+        public function peek() {}
+
+        /**
+         *
+         *
+         * @return ImmutableHeap
+         *
+         * @throws EmptyContainerException
+         */
+        public function shift(): ImmutableHeap {}
+    }
+
 /**
  * A queue that yields values in order of priority, from high to low.
  */
 final class PriorityQueue implements
-    Container,
+    Container,   /* \Countable */
     Clearable,
     Transferable
     {
@@ -827,9 +1189,9 @@ public function adjust($value, callable $mutator): void {}
          * Adds a value to the priority queue, using a given initial priority.
          *
          * @param mixed $value    The value to be pushed into the queue.
-         * @param int   $priority The priority of the inserted value.
+         * @param mixed $priority The priority of the inserted value.
          */
-        public function push($value, int $priority): void {}
+        public function push($value, $priority): void {}
 
         /**
          * Removes and returns the value at the front of the priority queue.
@@ -840,3 +1202,56 @@ public function push($value, int $priority): void {}
          */
         public function shift() {}
     }
+
+
+/**
+ * A queue that yields values in order of priority, from high to low.
+ */
+final class ImmutablePriorityQueue implements
+    Container,   /* \Countable */
+    Clearable,
+    Transferable,
+    Immutable
+    {
+        /**
+         * Creates a new priority queue using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this heap.
+         */
+        public function __construct(callable $comparator = null) {}
+
+        /**
+         * Adjusts the priority of a given value, setting it to the return value
+         * of the given mutator, then ensures that heap invariants are resolved.
+         *
+         * @param mixed           $value   The value of which the priority should be adjusted.
+         * @param callable(): int $mutator The mutator returning the new priority.
+         */
+        public function adjust($value, callable $mutator): ImmutablePriorityQueue {}
+
+        /**
+         * Adds a value to the priority queue, using a given initial priority.
+         *
+         * @param mixed $value    The value to be pushed into the queue.
+         * @param mixed $priority The priority of the inserted value.
+         */
+        public function push($value, $priority): ImmutablePriorityQueue {}
+
+        /**
+         * Returns the value at the front of the priority queue.
+         *
+         * @return mixed The next value.
+         *
+         * @throws EmptyContainerException
+         */
+        public function peek() {}
+
+        /**
+         *
+         *
+         * @return ImmutablePriorityQueue
+         *
+         * @throws EmptyContainerException
+         */
+        public function shift(): ImmutablePriorityQueue {}
+    }

From 6df465346f17f01f4ac29b5d59306f2123132592 Mon Sep 17 00:00:00 2001
From: Rudi Theunissen <rtheunissen@php.net>
Date: Fri, 26 Apr 2019 13:53:05 -0700
Subject: [PATCH 50/50] Remove immutables, deque and set sequence

---
 DRAFT.php | 484 +-----------------------------------------------------
 1 file changed, 8 insertions(+), 476 deletions(-)

diff --git a/DRAFT.php b/DRAFT.php
index 95b55fa..c7ad66f 100644
--- a/DRAFT.php
+++ b/DRAFT.php
@@ -47,13 +47,6 @@ class InvalidOffsetException extends LogicException implements Exception {}
 /*                                INTERFACES                                  */
 /******************************************************************************/
 
-/**
- * Marker interface to indicate that a class is immutable.
- */
-interface Immutable
-{
-}
-
 /**
  * Indicates that a structure can be cleared.
  */
@@ -95,12 +88,12 @@ function equals($other): bool;
 /**
  * Indicates that an object is designed to be used in hash-based structures.
  */
-interface Hashable extends Equatable, Immutable
+interface Hashable extends Equatable
 {
     /**
-     * @return mixed A scalar value to be used when generating a hashcode.
+     * @return int|string A scalar value to be used when generating a hashcode.
      */
-    function hashSource(): string;
+    function hashCode();
 }
 
 /**
@@ -140,14 +133,14 @@ function get(int $offset);
      *
      * @return int The offset of the given value, or -1 if the value could not be found.
      */
-    function indexOf($value): int;
+    // function indexOf($value): int;
 
     /**
      * @param mixed $value The value to search for.
      *
      * @return bool TRUE if this sequence contains the given value, FALSE otherwise.
      */
-    function contains($value): bool;
+    // function contains($value): bool;
 
     /**
      * @return mixed The first value in $this structure.
@@ -202,54 +195,7 @@ function insert(int $offset, ...$values): void;
 }
 
 /**
- * A sequence which can be modified, either in-place or as a copy.
- */
-interface ImmutableSequence extends Sequence, Immutable
-{
-    /**
-     * Sets the value at the given offset.
-     *
-     * @param int   $offset The offset at which the value should be set.
-     * @param mixed $value  The value to set.
-     *
-     * @throws InvalidOffsetException if the index is not within [0, size)
-     */
-    function set(int $offset, $value): ImmutableSequence;
-
-    /**
-     * Removes the value at the given offset, moving all successive
-     * values one position towards the front.
-     *
-     * @param int The offset to be removed.
-     *
-     * @throws InvalidOffsetException if the index is not within [0, size)
-     */
-    function unset(int $offset): ImmutableSequence;
-
-    /**
-     * Moves all values between the given index and the end of the sequence
-     * towards the back, then inserts the given values into the gap.
-     *
-     * @param int   $offset    The offset to insert values at.
-     * @param mixed ...$values The value to insert at the specified offset.
-     *
-     * @throws InvalidOffsetException if the index is out of range [0, size]
-     */
-    function insert(int $offset, ...$values): ImmutableSequence;
-}
-
-/**
- * Indicates that a structure is a sequence that is always sorted.
- */
-interface SortedSequence extends Sequence
-{
-}
-
-/**
- * Indicates that a structure is designed to quickly determine whether a given
- * value is already contained by it.
- *
- *
+ * 
  */
 interface Set
 {
@@ -313,29 +259,6 @@ function add(...$values): void;
     function remove($value): void;
 }
 
-/**
- * A set which can be modified, either in-place or as a copy.
- */
-interface ImmutableSet extends Set, Immutable
-{
-    /**
-     * @param mixed ...$values The values to be added.
-     *
-     * Adds the given value to $this set if it is not already in $this set.
-     */
-    function add(...$values): ImmutableSet;
-
-    /**
-     * @param mixed $value The value to be removed.
-     *
-     * Removes a given value from $this set, or does nothing if that value could
-     * not be found. The caller can use `has` to determine membership before
-     * removal. This method therefore promises only that the given value is not
-     * a member of the set on return.
-     */
-    function remove($value): ImmutableSet;
-}
-
 /**
  * Indicates that a structure is a set that is always sorted.
  */
@@ -452,31 +375,6 @@ function set($key, $value): void;
     function unset($key): void;
 }
 
-/**
- * A map which can be modified, either in-place or as a copy.
- */
-interface ImmutableMap extends Map, Immutable
-{
-    /**
-     * Associates the $key with the $value, overriding any previous association.
-     *
-     * @param mixed $key   The key to set the value for.
-     * @param mixed $value The value to set.
-     *
-     * @throws InvalidKeyException if the map implementation does not support
-     *                             the given key, eg. NULL
-     */
-    function set($key, $value): ImmutableMap;
-
-    /**
-     * Removes the given $key from the map, and does nothing if they key could
-     * not be found. This emulates `unset`
-     *
-     * @param mixed $key The key at which the value should be unset.
-     */
-    function unset($key): ImmutableMap;
-}
-
 /**
  * Indicates that a structure is a map that is always sorted by key.
  */
@@ -625,11 +523,10 @@ public function offsetExists($offset): bool {}
  */
 final class Tuple implements
     Traversable,
-    Immutable,
     Equatable,
     Container, /* \Countable */
     Sequence,  /* \Countable */
-    Hashable   /* Equatable, Immutable */
+    Hashable   /* Equatable */
     {
         /**
          * @param mixed[] $source The values used to fill this tuple.
@@ -666,152 +563,6 @@ public function push(...$values): void;
         public function pop();
     }
 
-/**
- * List structure, dynamic size, contiguous, no ops at the front.
- */
-final class ImmutableVector implements
-    ArrayAccess,
-    Traversable,
-    Equatable,        /* Same values in the same order. */
-    Container,        /* \Countable */
-    Sortable,
-    ImmutableSequence /* Sequence, \Countable, Immutable */
-    {
-        /**
-         * Adds one or more values to the end of the vector.
-         *
-         * @param mixed ...$values The values to append to the vector.
-         */
-        public function push(...$values): ImmutableVector;
-
-        /**
-         * Returns the value at the end of the vector.
-         *
-         * @return mixed The value at the end of the vector.
-         *
-         * @throws EmptyContainerException
-         */
-        public function peek();
-
-        /**
-         * Removes and returns the value at the end of the vector.
-         *
-         * @return mixed The value at the end of the vector.
-         *
-         * @throws EmptyContainerException
-         */
-        public function pop(): ImmutableVector;
-    }
-
-/**
- * Double-ended-queue, supports prepend and append, but nothing in-between.
- */
-final class Deque implements
-    Traversable,
-    Equatable,        /* Same values in the same order. */
-    Clearable,
-    Container,        /* \Countable */
-    Sortable,
-    Sequence,         /* \Countable */
-    Transferable,
-    {
-        /**
-         * Adds one or more values to the end of the deque.
-         *
-         * @param mixed ...$values The values to push at the end of this deque.
-         */
-        public function push(...$values): void {}
-
-        /**
-         * Removes and returns the value at the end of the deque.
-         *
-         * @return mixed The value at the end of this deque.
-         *
-         * @throws EmptyContainerException
-         */
-        public function pop() {}
-
-        /**
-         * Adds one or more values to the start of the deque.
-         *
-         * @param mixed ...$values The values to insert to the start of this deque.
-         */
-        public function unshift(...$values): void {}
-
-        /**
-         * Removes and returns the value at the start of the deque.
-         *
-         * @return mixed The value at the beginning of this deque.
-         *
-         * @throws EmptyContainerException
-         */
-        public function shift() {}
-    }
-
-/**
- * Double-ended-queue.
- */
-final class ImmutableDeque implements
-    Traversable,
-    Equatable,          /* Same values in the same order. */
-    Clearable,
-    Container,          /* \Countable */
-    Sortable,
-    Sequence,           /* \Countable */
-    Immutable,
-    Transferable
-    {
-        /**
-         * Adds one or more values to the end of the deque.
-         *
-         * @param mixed ...$values The values to push at the end of this deque.
-         */
-        public function push(...$values): ImmutableDeque {}
-
-        /**
-         * Removes and returns the value at the end of the deque.
-         *
-         * @return ImmutableDeque
-         *
-         * @throws EmptyContainerException
-         */
-        public function pop(): ImmutableDeque {}
-
-        /**
-         * Adds one or more values to the start of the deque.
-         *
-         * @param mixed ...$values The values to insert to the start of this deque.
-         */
-        public function unshift(...$values): ImmutableDeque {}
-
-        /**
-         * Removes and returns the value at the start of the deque.
-         *
-         * @return ImmutableDeque
-         *
-         * @throws EmptyContainerException
-         */
-        public function shift(): ImmutableDeque {}
-    }
-
-/**
- * An immutable sequence that is evaluated and sorted on first access.
- */
-final class SetSequence implements
-    ArrayAccess,
-    Traversable,
-    Equatable,      /* Same values in the same order. */
-    Container,      /* \Countable */
-    Immutable,
-    SortedSet,      /* Set */
-    SortedSequence, /* Sequence, \Countable */
-    {
-        /**
-         * @param mixed[] $source The values used to fill this sequence.
-         */
-        public function __construct(iterable $source) {}
-    }
-
 /**
  * The Set equivalent of HashMap, which might not use a HashMap internally, but
  * will always preserve insertion order.
@@ -828,20 +579,6 @@ final class HashSet implements
     MutableSet    /* Set */
     {}
 
-/**
- *
- */
-final class ImmutableHashSet implements
-    ArrayAccess,
-    Traversable,
-    Transferable,
-    Equatable,    /* Same values in the same order. */
-    Clearable,
-    Container,    /* \Countable */
-    Sortable,
-    Sequence,     /* \Countable */
-    ImmutableSet  /* Set, Immutable */
-    {}
 /**
  * A structure that is always sorted and does not allow duplicate elements.
  *
@@ -865,25 +602,6 @@ final class TreeSet implements
         public function __construct(callable $comparator = null) {}
     }
 
-/**
- *
- */
-final class ImmutableTreeSet implements
-    Traversable,
-    Equatable,    /* Same values in the same order. */
-    Clearable,
-    Container,    /* \Countable */
-    ImmutableSet, /* Set, Immutable */
-    SortedSet     /* Set */
-    {
-        /**
-         * Creates a new tree set using an optional comparator.
-         *
-         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this tree set.
-         */
-        public function __construct(callable $comparator = null) {}
-    }
-
 /**
  * A set that allows duplicate values, and keeps a count of the number of times
  * a value appears in the set.
@@ -937,19 +655,6 @@ final class HashMap implements
     MutableMap  /* Map */
     {}
 
-/**
- *
- */
-final class ImmutableHashMap implements
-    ArrayAccess,
-    Traversable,
-    Equatable,  /* Same key => value associations, ordering not considered. */
-    Container,  /* \Countable */
-    Clearable,
-    Sortable,
-    ImmutableMap  /* Map, Immutable */
-    {}
-
 /**
  *
  */
@@ -963,19 +668,6 @@ final class TreeMap implements
     SortedMap   /* Map */
     {}
 
-/**
- *
- */
-final class ImmutableTreeMap implements
-    ArrayAccess,
-    Traversable,
-    Equatable,    /* Same key => value associations. */
-    Container,    /* \Countable */
-    Clearable,
-    ImmutableMap, /* Map, Immutable */
-    SortedMap     /* Map */
-    {}
-
 /**
  * A basic first-in-last-out structure.
  */
@@ -1000,47 +692,12 @@ public function push(...$values): void {}
         public function pop() {}
     }
 
-/**
- * A basic first-in-last-out structure.
- */
-final class ImmutableStack implements
-    Container,   /* \Countable */
-    Transferable,
-    Immutable
-    {
-        /**
-         * Adds a value to the top of the stack.
-         *
-         * @param mixed ...$values The values to be pushed onto the stack.
-         */
-        public function push(...$values): ImmutableStack {}
-
-        /**
-         * Returns the value at the top of the stack.
-         *
-         * @return mixed The value at the top.
-         *
-         * @throws EmptyContainerException
-         */
-        public function peek() {}
-
-        /**
-         *
-         *
-         * @return ImmutableStack
-         *
-         * @throws EmptyContainerException
-         */
-        public function pop(): ImmutableStack {}
-    }
-
 /**
  * A basic first-in-first-out structure.
  */
 final class Queue implements
     Container,   /* \Countable */
-    Transferable,
-    Immutable
+    Transferable
     {
         /**
          * Adds a value to the queue.
@@ -1057,36 +714,6 @@ public function push(...$values): void {}
         public function shift();
     }
 
-/**
- * A basic first-in-first-out structure.
- */
-final class ImmutableQueue implements
-    Container,   /* \Countable */
-    Transferable,
-    Immutable
-    {
-        /**
-         * Adds a value to the queue.
-         *
-         * @param mixed ...$values The values to be pushed into the queue.
-         */
-        public function push(...$values): ImmutableQueue {}
-
-        /**
-         * Returns the next value in the queue.
-         *
-         * @return mixed The next value.
-         */
-        public function peek();
-
-        /**
-         *
-         *
-         * @return ImmutableQueue
-         */
-        public function shift(): ImmutableQueue;
-    }
-
 /**
  * Stable heap with an optional comparator, defaulting to minimum.
  */
@@ -1119,48 +746,6 @@ public function push(...$values): void {}
         public function shift() {}
     }
 
-/**
- * Stable heap with an optional comparator, defaulting to minimum.
- */
-final class ImmutableHeap implements
-    Container,   /* \Countable */
-    Clearable,
-    Transferable,
-    Immutable
-    {
-        /**
-         * Creates a new heap using an optional comparator.
-         *
-         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this heap.
-         */
-        public function __construct(callable $comparator = null) {}
-
-        /**
-         * Adds a value to the heap.
-         *
-         * @param mixed ...$values The values to be pushed onto the heap.
-         */
-        public function push(...$values): ImmutableHeap {}
-
-        /**
-         * Returns the value at the top of the heap.
-         *
-         * @return mixed
-         *
-         * @throws EmptyContainerException
-         */
-        public function peek() {}
-
-        /**
-         *
-         *
-         * @return ImmutableHeap
-         *
-         * @throws EmptyContainerException
-         */
-        public function shift(): ImmutableHeap {}
-    }
-
 /**
  * A queue that yields values in order of priority, from high to low.
  */
@@ -1202,56 +787,3 @@ public function push($value, $priority): void {}
          */
         public function shift() {}
     }
-
-
-/**
- * A queue that yields values in order of priority, from high to low.
- */
-final class ImmutablePriorityQueue implements
-    Container,   /* \Countable */
-    Clearable,
-    Transferable,
-    Immutable
-    {
-        /**
-         * Creates a new priority queue using an optional comparator.
-         *
-         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this heap.
-         */
-        public function __construct(callable $comparator = null) {}
-
-        /**
-         * Adjusts the priority of a given value, setting it to the return value
-         * of the given mutator, then ensures that heap invariants are resolved.
-         *
-         * @param mixed           $value   The value of which the priority should be adjusted.
-         * @param callable(): int $mutator The mutator returning the new priority.
-         */
-        public function adjust($value, callable $mutator): ImmutablePriorityQueue {}
-
-        /**
-         * Adds a value to the priority queue, using a given initial priority.
-         *
-         * @param mixed $value    The value to be pushed into the queue.
-         * @param mixed $priority The priority of the inserted value.
-         */
-        public function push($value, $priority): ImmutablePriorityQueue {}
-
-        /**
-         * Returns the value at the front of the priority queue.
-         *
-         * @return mixed The next value.
-         *
-         * @throws EmptyContainerException
-         */
-        public function peek() {}
-
-        /**
-         *
-         *
-         * @return ImmutablePriorityQueue
-         *
-         * @throws EmptyContainerException
-         */
-        public function shift(): ImmutablePriorityQueue {}
-    }
