phobos/std/slist.d
2007-10-18 21:30:09 +00:00

856 lines
20 KiB
D

module std.slist;
import std.contracts; // enforce
import std.stdio;
import std.conv; // testing
/////////////////////////////////////////////////////////////////////////////////
/**
* Singly-linked list. The intent is to make it as efficient as the
* simplest hand-coded list. Therefore, no tricks are employed - no
* phantom node, no circularity, no fat iterators, just a straight
* list of nodes terminated by $(D_PARAM null). Therefore, $(D_PARAM
* SList) only supports forward iteration, does not have access to its
* last element, and only supports adding elements to the front or
* after the current iterator.
*/
struct SList(T) {
private:
struct Node
{
T value;
//node* prev;
Node* next;
}
Node *root;
public:
alias T Value;
//Alias T* Pointer;
//Alias T Reference;
//Alias T ConstReference;
alias .Iterator!(SList) Iterator;
/**
* Builds a list of $(D_PARAM length) elements, initialized with
* $(D_PARAM value). */
static SList opCall(size_t length, T value)
{
auto nodes = new Node[length];
nodes[0].value = value;
foreach (ref e; nodes[1 .. $])
{
e.value = value;
(&e)[-1].next = &e;
}
SList result;
result.root = &nodes[0];
return result;
}
// todo: eliminate this duplication
/**
* Build a list of $(D_PARAM length) elements, initialized with
* $(D_PARAM T.init). */
static SList opCall(size_t length = 0)
{
if (!length)
{
SList result;
return result;
}
auto nodes = new Node[length];
foreach (ref e; nodes[1 .. $])
{
(&e)[-1].next = &e;
}
SList result;
result.root = &nodes[0];
return result;
}
// static SList opCall(U)(Iterator!(U) b, Iterator!(U) e)
// {
// }
// void opAssign(U)(List!(U) rhs)
// {
// for (auto i = begin(rhs); i != end(rhs); next(i))
// {
// }
// }
/**
* Compares a list with an array for element-wise equality.
*/
int opEquals(U)(const(U)[] v)
{
size_t j = 0;
for (auto i = begin(*this); i != end(*this); next(i))
{
if (v.length <= j || v[j++] != get(i)) return false;
}
return true;
}
}
template Ref(T)
{
alias T Ref;
}
/**
* Returns an iterator to the head of the list.
*/
Iterator!(SList!(T)) begin(T)(ref SList!(T) lst)
{
return Iterator!(SList!(T))(lst.root);
}
/**
* Returns an iterator after the end of the list ($(D_PARAM null) in fact).
*/
Iterator!(SList!(T)) end(T)(ref SList!(T) lst)
{
return Iterator!(SList!(T))(null);
}
/**
* Returns the first element in the list. Precondition: $(D_PARAM
* !isEmpty(lst)).
*/
Ref!(T) front(T)(ref SList!(T) lst)
in
{
enforce(lst.root, "Attempting to fetch the front of an empty list");
}
body
{
return lst.root.value;
}
/**
* Pushes $(D_PARAM elems) to the front of the list $(D_PARAM lst),
* and returns an iterator to the root of the list.
*/
Iterator!(SList!(T)) pushFront(T, U...)(ref SList!(T) lst, U elems)
{
auto newNodes = new lst.Node[elems.length];
foreach (i, e; elems)
{
newNodes[i].value = e;
if (i + 1 < elems.length) newNodes[i].next = &newNodes[i + 1];
}
newNodes[$ - 1].next = lst.root;
return Iterator!(SList!(T))(lst.root = newNodes.ptr);
}
/**
* True if and only if $(D_PARAM lst) is empty.
*/
bool isEmpty(T)(ref SList!(T) lst)
{
return lst.root == null;
}
unittest
{
SList!(int) lst;
assert(isEmpty(lst));
pushFront(lst, 42);
assert(!isEmpty(lst));
assert(front(lst) == 42);
}
/**
* List iterator. Models forward iteration.
*/
struct Iterator(L : SList!(T), T)
{
private L.Node* crt;
static Iterator opCall(L.Node* n) {
Iterator r;
r.crt = n;
return r;
}
static Iterator opCall(L lst) {
Iterator r;
r.crt = lst.root;
return r;
}
bool opEquals(Iterator rhs) { return crt == rhs.crt; }
}
/**
* Read the value pointed to by the iterator.
*/
T get(T)(Iterator!(SList!(T)) i)
{
return assert(i.crt), i.crt.value;
}
/**
* Assign to the value pointed to by the iterator.
*/
T set(T, U)(ref Iterator!(SList!(T)) i, U rhs)
{
return assert(i.crt), i.crt.value = rhs;
}
/**
* Increment the iterator.
*/
void next(T)(ref Iterator!(SList!(T)) i)
{
assert(i.crt);
i.crt = i.crt.next;
}
unittest
{
{
// test uninitialized list
auto lst = SList!(double)(42);
uint counter = 0;
for (auto i = begin(lst); i != end(lst); next(i))
{
assert(get(i) != get(i));
++counter;
}
}
auto lst = SList!(double)(42, 5);
uint counter = 0;
for (auto i = begin(lst); i != end(lst); next(i))
{
assert(get(i) == 5);
++counter;
}
counter = 0;
for (auto i = begin(lst); i != end(lst); next(i))
{
assert(get(i) == 5);
if (counter & 1) set(i, 234);
++counter;
}
assert(counter == 42);
counter = 0;
for (auto i = begin(lst); i != end(lst); next(i))
{
if (counter & 1) assert(get(i) == 234);
else assert(get(i) == 5);
++counter;
}
assert(counter == 42);
}
/**
* Pops the element in the front of the list $(D_PARAM lst).
*/
void popFront(T)(ref SList!(T) lst)
{
assert(lst.root);
lst.root = lst.root.next;
}
unittest
{
auto lst = SList!(double)(1, 5);
assert(front(lst) == 5);
popFront(lst);
assert(isEmpty(lst));
}
/**
* Inserts an element with value $(D_PARAM value) in $(D_PARAM lst)
* after $(D_PARAM i). Returns an iterator to the element just inserted.
*/
Iterator!(SList!(T)) insertAfter(T, U)(ref SList!(T) lst,
Iterator!(SList!(T)) i, U value)
{
assert(i.crt);
auto newNode = new lst.Node;
newNode.value = value;
newNode.next = i.crt.next;
i.crt.next = newNode;
return Iterator!(SList!(T))(newNode);
}
unittest
{
auto lst = SList!(double)(1, 5);
assert(front(lst) == 5);
popFront(lst);
assert(isEmpty(lst));
pushFront(lst, 6);
auto i = begin(lst);
insertAfter(lst, i, 43);
assert(get(i) == 6);
next(i);
assert(get(i) == 43);
next(i);
assert(i == end(lst));
}
/**
* Pushes a range to the front of the list $(D_PARAM lst), and returns
* an iterator to the last element inserted.
*/
void insertAfter(T, U)(ref SList!(T) lst, Iterator!(SList!(T)) i,
Iterator!(U) begin, Iterator!(U) end)
{
for (; begin != end; next(begin))
{
i = insertAfter(lst, i, get(begin));
}
}
unittest
{
auto lst = SList!(double)(2, 5);
auto lst1 = SList!(int)(2, 44);
auto i = begin(lst);
//next(i);
insertAfter(lst, i, begin(lst1), end(lst1));
assert(get(i) == 5);
next(i);
assert(get(i) == 44);
next(i);
assert(get(i) == 44);
next(i);
assert(get(i) == 5);
next(i);
assert(i == end(lst));
}
/**
* Pushes $(D_PARAM n) copies of $(D_PARAM value) to the front of the
* list $(D_PARAM lst), and returns an iterator to the last element
* inserted.
*/
void insertAfter(T, U)(ref SList!(T) lst, Iterator!(SList!(T)) i,
size_t n, U value)
{
for (; n-- > 0; )
{
i = insertAfter(lst, i, value);
}
}
unittest
{
auto lst = SList!(double)(2, 5);
auto lst1 = SList!(int)(2, 44);
auto i = begin(lst);
//next(i);
insertAfter(lst, i, 2u, 44);
assert(get(i) == 5);
next(i);
assert(get(i) == 44);
next(i);
assert(get(i) == 44);
next(i);
assert(get(i) == 5);
next(i);
assert(i == end(lst));
}
/**
* Erases the element after the one pointed to by $(D_PARAM i).
*/
Iterator!(SList!(T)) eraseAfter(T)(ref SList!(T), Iterator!(SList!(T)) i)
{
assert(i.crt);
assert(i.crt.next);
i.crt.next = i.crt.next.next;
return i;
}
unittest
{
auto lst = SList!(double)();
pushFront(lst, 1, 2, 3);
auto i = begin(lst);
eraseAfter(lst, i);
assert(get(i) == 1);
next(i);
assert(get(i) == 3);
next(i);
assert(i == end(lst));
}
/**
* Erases the elements from the element after the one pointed to by
* $(D_PARAM begin), up to (and including) the one pointed to by
* $(D_PARAM end).
*/
Iterator!(SList!(T)) eraseAfter(T)(ref SList!(T) lst, Iterator!(SList!(T)) begin,
Iterator!(SList!(T)) end)
{
begin.crt.next = end.crt.next;
return begin;
}
unittest
{
auto lst = SList!(double)();
pushFront(lst, 1, 2, 3, 4);
auto i = begin(lst), j = i;
eraseAfter(lst, i, j);
assert(lst == [ 1, 2, 3, 4 ], to!(string)(front(lst)));
next(j);
eraseAfter(lst, i, j);
assert(lst == [ 1, 3, 4 ], to!(string)(front(lst)));
}
/**
* Erases the elements from the element after the one pointed to by
* $(D_PARAM begin), up to (and including) the one pointed to by
* $(D_PARAM end).
*/
void clear(T)(ref SList!(T) lst)
{
lst.root = null;
}
unittest
{
auto lst = SList!(double)();
pushFront(lst, 1, 2, 3);
clear(lst);
assert(isEmpty(lst));
}
/**
* Inserts or erases elements at the front of $(D_PARAM lst) such that
* the size becomes $(D_PARAM n).
*/
void resize(T)(ref SList!(T) lst, size_t n)
{
resize(lst, n, T.init);
}
/** ditto
*/
void resize(T, U)(ref SList!(T) lst, size_t n, U value)
{
auto b = begin(lst);
if (b == end(lst))
{
// empty list
lst = SList!(T)(n, value);
}
else
{
size_t length = 1;
for (; b.crt.next !is null; next(b))
{
++length;
}
if (length < n)
{
n -= length;
auto tail = SList!(T)(n, value);
b.crt.next = tail.root;
}
}
}
unittest
{
auto lst = SList!(double)();
pushFront(lst, 1, 2, 3);
// @@@ BUG IN COMPILER: SHOULD ACCEPT CALL WITHOUT 'u'
resize(lst, 2u);
assert(lst == [ 1, 2, 3 ]);
resize(lst, 3u);
assert(lst == [ 1, 2, 3 ]);
resize(lst, 5u, 42);
assert(lst == [ 1.0, 2, 3, 42, 42 ]);
}
/**
* Computes the distance between two iterators in linear or better time.
*/
size_t distanceLin(T)(Iterator!(T) from, Iterator!(T) to)
{
size_t result;
for (; from != to; next(from))
{
++result;
}
return result;
}
unittest
{
auto lst = SList!(double)(5, 667.45);
assert(distanceLin(begin(lst), end(lst)) == 5);
}
/**
* Computes the last valid iterator before $(D_PARAM to) in linear or
* better time.
*/
Iterator!(T) lastLin(T)(Iterator!(T) from, Iterator!(T) to)
{
if (from == to) return from;
for (;;)
{
auto save = from;
next(from);
if (from == to) return save;
}
}
unittest
{
SList!(double) lst;
pushFront(lst, 5, 10, 23, 667.45);
assert(get(lastLin(begin(lst), end(lst))) == 667.45);
}
/**
* Reads the value pointed to by the iterator, and increments the iterator.
*/
bool getNext(It : Iterator!(List!(T)))(It i, ref T target)
{
if (!i.crt) return false;
target = i.crt.value;
next(i);
return true;
}
/+
/////////////////////////////////////////////////////////////////////////////////
size_t size(T)(inout SList!(T) lst) {
//if (lst.size != lst.size.max) return lst.size;
// compute the size
//lst.size = 0;
auto size = 0;
for (auto n = lst.root; n; n = n.next) {
// ++lst.size;
++size;
}
//return lst.size;
return size;
}
unittest {
SList!(int) list;
assert(size(list) == 0);
push_back(list, 1);
assert(size(list) == 1);
}
/////////////////////////////////////////////////////////////////////////////////
SList!(T).iterator begin(T)(inout SList!(T) list) {
return list.iterator(list.root);
}
/////////////////////////////////////////////////////////////////////////////////
SList!(T).iterator end(T)(inout SList!(T) list) {
return list.iterator(null);
}
/////////////////////////////////////////////////////////////////////////////////
bool is_empty(T)(inout SList!(T) lst) {
return lst.root == null;
}
unittest {
SList!(int) list;
assert(is_empty(list));
list.iterator i = begin(list);
assert(i.done());
}
/////////////////////////////////////////////////////////////////////////////////
SList!(T).iterator push_front(T, U)(inout SList!(T) lst, U elem) {
auto newNode = new lst.node;
newNode.value = elem;
if (!lst.root) {
newNode.prev = newNode;
newNode.next = null;
} else {
assert(lst.root.prev);
newNode.prev = lst.root.prev;
newNode.next = lst.root;
lst.root.prev = newNode;
}
return lst.iterator(lst.root = newNode);
}
unittest {
SList!(int) list;
assert(is_empty(list));
//
list.iterator i = begin(list);
assert(i.done());
//
push_front(list, 1);
i = begin(list);
assert(!i.done);
assert(i.get == 1);
i.next();
assert(i.done);
}
/////////////////////////////////////////////////////////////////////////////////
SList!(T).iterator push_back(T, U)(inout SList!(T) lst, U elem) {
if (!lst.root) return push_front(lst, elem);
auto newNode = new lst.node;
newNode.value = elem;
auto ante = lst.root.prev;
assert(ante && ante.next == null);
newNode.prev = ante;
ante.next = newNode;
return lst.iterator(lst.root.prev = newNode);
}
unittest {
SList!(int) list;
assert(size(list) == 0);
assert(is_empty(list));
push_back(list, 1);
assert(size(list) == 1);
assert(!is_empty(list));
}
/////////////////////////////////////////////////////////////////////////////////
T back(T)(inout SList!(T) lst) {
assert(lst.root && lst.root.prev);
return lst.root.prev.value;
}
unittest {
SList!(int) list;
push_back(list, 1);
assert(size(list) == 1);
assert(!is_empty(list));
assert(back(list) == 1);
}
/////////////////////////////////////////////////////////////////////////////////
T front(T)(inout SList!(T) lst) {
assert(lst.root);
return lst.root.value;
}
unittest {
SList!(int) list;
push_back(list, 1);
assert(front(list) == 1);
push_back(list, 2);
assert(front(list) == 1);
}
/////////////////////////////////////////////////////////////////////////////////
void pop_front(T)(inout SList!(T) lst) {
enforce(!is_empty(lst));
auto yank = lst.root;
auto newRoot = yank.next;
if (!newRoot) {
// last mohican
lst.root = null;
} else {
// two or more elements
newRoot.prev = yank.prev;
lst.root = newRoot;
}
}
unittest {
SList!(int) list;
push_back(list, 1);
push_back(list, 2);
pop_front(list);
assert(front(list) == 2 && back(list) == 2);
}
/////////////////////////////////////////////////////////////////////////////////
void pop_back(T)(inout SList!(T) lst) {
enforce(!is_empty(lst));
auto yank = lst.root.prev;
auto newBack = yank.prev;
if (yank == newBack) {
// remove the last element
lst.root = null;
return;
}
lst.root.prev = newBack;
newBack.next = null;
}
/////////////////////////////////////////////////////////////////////////////////
SList!(T).iterator insert(T, Iter, U)
(inout SList!(T) lst, Iter i, U elem) {
if (lst.root == i.crt) return push_front(lst, elem);
if (!i.crt) return push_back(lst, elem);
// Now for the "real" case: i is somewhere in the middle of the list
auto newNode = new lst.node;
// fill the new node correctly for insertion _before_ i.crt
auto post = i.crt;
auto ante = post.prev;
newNode.value = elem;
newNode.next = post;
newNode.prev = ante;
post.prev = newNode;
ante.next = newNode;
return lst.iterator(newNode);
}
/////////////////////////////////////////////////////////////////////////////////
SList!(T).iterator erase(T, Iter)
(inout SList!(T) lst, Iter i) {
enforce(lst.root && i.crt);
if (lst.root == i.crt) {
pop_front(lst);
return lst.iterator(lst.root);
}
if (i.crt == lst.root.prev) {
pop_back(lst);
return lst.iterator(null);
}
// Now for the "real" case: i is somewhere in the middle of the list
auto post = i.crt.next;
auto ante = i.crt.prev;
post.prev = ante;
ante.next = post;
return lst.iterator(post);
}
struct CircularQueue(T) {
private SList!(T) store;
private size_t size;
//
static CircularQueue opCall(uint size, T init = T.init) {
CircularQueue!(T) result;
result.store = SList!(T)(size, init);
result.size = size;
return result;
}
struct iterator {
SList!(T).iterator val;
void next() {
val.next();
}
T get() {
return val.get;
}
}
iterator begin() {
iterator result;
result.val = .begin(this.store);
return result;
}
iterator end() {
iterator result;
result.val = .end(this.store);
return result;
}
}
T front(T, U=void)(inout CircularQueue!(T) lst) {
return front(lst.store);
}
void push(T)(inout CircularQueue!(T) lst, T element) {
// just move the root!
lst.store.root.value = element;
// fix the asymmetry of the last element
assert(!lst.store.root.prev.next);
lst.store.root.prev.next = lst.store.root;
lst.store.root = lst.store.root.next;
lst.store.root.prev.next = null;
lst.store.check();
}
size_t size(T, U=void)(inout CircularQueue!(T) lst) {
return lst.size;
}
/////////////////////////////////////////////////////////////////////////////////
import std.random;
import std.string;
import std.stdio;
unittest {
SList!(uint) list;
uint[] witness;
void PositionRandomly(out list.iterator iter, out uint advance) {
if (!witness.length) return;
advance = rand() % witness.length;
iter = begin(list);
for (auto j = 0; j != advance; ++j) {
iter.next;
assert(!iter.done);
}
assert(iter.get == witness[advance], toString(advance));
}
for (auto i = 1_000_000; i > 0; --i) {
list.check();
list.iterator iter2 = begin(list);
for (uint j = 0; j != witness.length; ++j, iter2.next) {
assert(witness[j] == iter2.get);
}
writef(i, "\r");
switch (rand() & 7) {
case 0:
witness ~= rand();
push_back(list, witness[$-1]);
break;
case 1:
witness = rand() ~ witness;
push_front(list, witness[0]);
break;
case 2:
if (witness.length) {
witness = witness[0 .. $ - 1];
pop_back(list);
}
break;
case 3:
if (witness.length) {
witness = witness[1 .. $];
pop_front(list);
}
break;
case 4:
if (witness.length) {
list.iterator iter;
uint advance;
PositionRandomly(iter, advance);
witness = witness[0 .. advance] ~ rand() ~ witness[advance .. $];
insert(list, iter, witness[advance]);
}
break;
case 5:
if (witness.length) {
list.iterator iter;
uint advance;
PositionRandomly(iter, advance);
witness = witness[0 .. advance] ~ witness[advance + 1 .. $];
erase(list, iter);
}
break;
case 6:
case 7:
default: ;//assert(false);
}
}
writefln("wyda ");
}
// T enforce(T)(T expression, char[] message = "Enforcement failed") {
// if (!expression) throw new Exception(message);
// return expression;
// }
version (selftest) {
void main() {}
}
+/