Mercurial > projects > aid
changeset 1:5dd9f598bcd8
Update
author | revcompgeek |
---|---|
date | Sat, 29 Mar 2008 12:30:20 -0600 |
parents | 4b2e8e8a633e |
children | 9655c8362b25 |
files | trunk/aid/astar.d trunk/mintl/ChangeLog trunk/mintl/adapter.d trunk/mintl/all.d trunk/mintl/array.d trunk/mintl/arrayheap.d trunk/mintl/arraylist.d trunk/mintl/deque.d trunk/mintl/hashaa.d trunk/mintl/index.html trunk/mintl/linux.mak trunk/mintl/list.d trunk/mintl/locks.html trunk/mintl/mem.d trunk/mintl/multiaa.d trunk/mintl/queue.d trunk/mintl/set.d trunk/mintl/share.d trunk/mintl/slist.d trunk/mintl/sortedaa.d trunk/mintl/sorting.d trunk/mintl/stack.d trunk/mintl/unittest.d trunk/mintl/win32.mak |
diffstat | 24 files changed, 10170 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/trunk/aid/astar.d Mon Mar 03 19:28:10 2008 -0700 +++ b/trunk/aid/astar.d Sat Mar 29 12:30:20 2008 -0600 @@ -6,12 +6,14 @@ module aid.astar; import mintl.arrayheap; +import mintl.arraylist; class Node(DATA) { int xloc; int yloc; int fitness; - private int fitg; + private int g; + private int h; Node* parent = null; DATA data; @@ -35,7 +37,7 @@ alias Node!(DATA) Node; alias DATA[] delegate(DATA) getChildren; ArrayHeap!(Node) openList; - ArrayHeap!(Node) closedList; + ArrayList!(Node) closedList; DATA[] run(DATA start){ }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/ChangeLog Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,106 @@ +2.7.2 + * all: update to dmd.1.014 + +2.7.1 + * all: update to dmd.129 + * share, set, multiaa: add static make() that calls add(...) + * hashaa, sortedaa: add block allocated nodes and recycling. add trim() + +2.7 + * arraylist/deque: mixin RandomAccessSort + * list/hashaa: mixin SequentialSort + * sorting: new templates for sorting algorithms + * hashaa: fix insertion bug with rehashing + +2.6.2 + * hashaa/sortedaa: remove enum from 2.6.1 and make get() and put(). + * multiaa: re-enable and uncomment unittests + * set: clean up mixins were for builtin AA support + * all: shorten opApply code by relying on opApplyIter + * arraylist, deque: fix len wrapping code and next() bug + * all.d: added "no warrenty" clause + +2.6.1 + * deque/arraylist: switched to start/len instead of head/tail to + make wrapping easier + * hashaa/sortedaa: redo indexing api to allow NullOnMiss, ThrowOnMiss + and InsertOnMiss for get(). Replace opIn with contains. Remove lookup. + Add 'missing' property for lookups that miss. + +2.6 + * all: simplify mixins by adding type aliases ContainerType, ValueType + IndexType, IterType + * lists: add value getter/setter for one-item slices + * all: add opCmp + * all: add ReadOnly parameter and readonly/readwrite property + * lists: add opIn, count, swap, find, fill, copy algorithms + * hashaa: for consistency with other containers rename LinkedAA + to HashAA and make the default for adapters + * all: update to dmd.126 + +2.5 + * list.d: CList to CircularList and CSList to CircularSList + * all: change move(), moveHead(), moveTail() to next() + * all: added take() and takeHead/Tail to return value if any + * concurrent/aa.d: ConcurrentAA changes to implement Collection + * arraylist.d: grow ArrayList capacity geometrically + * all: remove toArray and replace with values + * all: remove toSeq since instantiating CFoo is equivalent + * linked/sortedaa.d: change fromHead, fromTail to head/tail + * lists: add head/tail properties to get one-item slices of head/tail + * list.d: remove length setter and make length==0 mean unknown length + that gets computed only when required + * list.d: make head null indicate empty list, tail hold cache + * sortedaa.d: add from(key) and to(key) + * deque.d: simplify code by not resizing block size and making it cyclic + * arraylist.d: fix copyBlock bug copying between arrays of different size + * adapter.d: mixins for adapters + * stack.d: adapter for stack + * queue.d: adapter for queue + * set.d: adapter for sets and multi-sets + * multiaa.d: adapter for multi-aa + * index.html: update for above and add class.html. + * mintl.cls: new package containing interfaces and classes - + separate download + * mem.d: new module for customer allocators + * all: add clear() to support custom allocators + +2.2 + * all: replaced length sizes with size_t instead of uint and be careful + about overflows + * arraylist.d: change ArrayList to dynamically grow instead of error + * util.d: removed and moved aliases to target's module + * seq.d: removed and moved contents to share.d + * all: unify bounds checking code into a shared fcn for each module + * opIn: return a pointer to the element or null. + * index.html: update for above and document MinTLNoIndexChecking. + +2.1.1 + * list.d: moved mixin in list.d and slist.d to match new dmd + behavior dmd.119 + * array.d: removed obsolete AA helper functions + * arraylist.d: added capacity, length setter and array to ArrayList + to let it + act more like an array-with-capacity. The only overhead is an extra + head value that is always 0. + * arraylist.d: fixed bug in ArrayList moveBlock when copying to end + of array + +2.1 + * concurrent: split out concurrent containers into a different + distribution + +2.02 + * libmintl_debug.a and mintl_debug.lib debug builds of library + +2.01 + * array.d: remove AA utilities by default since the new implementation + breaks the old routines. old code is available with version AllowAAUtils + * arraylist.d: update for int/uint implicit conversion rules + * win32.mak: update to build unittest with SRC instead of mintl.lib + * linux.mak: same + * concurrent/*.d: add volatile statements back in + * concurrent/dualstack.d: removeTail gets result from t not next + * concurrent/aa.d: add debug print statements + * concurrent/priorityqueue.d: add slower yields to unittest + * concurrent/linkedqueue.d: add slower yields to unittest
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/adapter.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,47 @@ +/** \file adapter.d + * \brief Mixins for adapter containers like stack, queue, set. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 1.1 + */ + +module mintl.adapter; + +template MAdaptBuiltin(alias impl, Container) { + size_t length() { return impl.length; } + int opEquals(Container c) { return impl == c.impl; } +} + +template MAdaptBasic(alias impl, Container) { + bool isEmpty() { return impl.isEmpty; } + Container.ValueType opIndex(Container.IndexType n) { return impl[n]; } + static if (!Container.isReadOnly) { + void opIndexAssign(Container.ValueType v, Container.IndexType n) { impl[n] = v; } + } + int opApply(int delegate(inout Container.ValueType x) dg){return impl.opApply(dg);} + int opApply(int delegate(inout Container.IndexType, inout Container.ValueType x) dg){return impl.opApply(dg);} + Container dup() { + Container res; + res.impl = impl.dup; + return res; + } +} + +template MAdaptList(alias impl, Container) { + static if (!Container.isReadOnly) { + void addHead(Container.ValueType v) {impl.addHead(v);} + void addHead(Container v) {impl.addHead(v.impl);} + void addTail(Container.ValueType v) {impl.addTail(v);} + void addTail(Container v) {impl.addTail(v.impl);} + Container.ValueType takeTail() {return impl.takeTail();} + void removeTail() {impl.removeTail();} + Container.ValueType takeHead() {return impl.takeHead();} + void removeHead() {impl.removeHead();} + void clear(){impl.clear();} + } + int opCmp(Container c) { return impl.opCmp(c.impl); } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/all.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,49 @@ +/** \file all.d + * \brief Minimal Template Library global import. For concurrent + * containers import mintl.concurrent.all. For class and interface + * API import mintl.cls.all. + * See index.html for documentation. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.7.1 + */ + +/* The MinTL library and sub-libraries are provided 'as-is', without + * any express or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of this software. + */ + +module mintl.all; + +// builtin array helper functions +import mintl.array; + +// linked lists +import mintl.list; +import mintl.slist; + +// special associative arrays +import mintl.hashaa; +import mintl.sortedaa; + +// circular buffer or array with capacity +import mintl.arraylist; + +// heap (complete binary tree) +import mintl.arrayheap; + +// deque (block allocated double-ended queue) +import mintl.deque; + +// adapter containers +import mintl.stack; +import mintl.queue; +import mintl.set; +import mintl.multiaa; + +// shared exceptions and definitions +import mintl.share; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/array.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,184 @@ +/** \file array.d + * \brief Utility functions for dynamic and associative arrays. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.6 + */ + +module mintl.array; + +// sort with custom compare delegate +template sort(Value:Value[]) { + void sort(Value[] data, int delegate(Value* l, Value* r) cmp = null) { + void swap( Value* t1, Value* t2 ) { + Value t = *t1; *t1 = *t2; *t2 = t; + } + void insertionSort(Value[] data) { + Value* head = &data[0]; + Value* tail = head+data.length; + Value* i = head+1; + while(i < tail) { + Value* j = i; + for (; j > head && cmp(j - 1,j) > 0; j--) { + swap(j - 1,j); + } + i++; + } + } + void dosort(Value[] data) { + if (data.length < 2) { + return; + } else if (data.length < 8) { + insertionSort(data); + return; + } + Value *head = &data[0]; + Value *tail = head+data.length-1; + Value *p = head+1; + Value *q = tail; + swap(head,head+data.length/2); + if (cmp(p,q) > 0) swap(p,q); + if (cmp(head,q) > 0) swap(head,q); + if (cmp(p,head) > 0) swap(p,head); + while (1) { + do p++; while (cmp(p, head) < 0); + do q--; while (cmp(q, head) > 0); + if (p > q) break; + swap(p,q); + } + swap(head,q); + if (head < q) + dosort(head[0 .. q-head+1]); + if (p < tail) + dosort(p[0 .. tail-p+1]); + } + TypeInfo ti = typeid(Value); + if (cmp is null) { + cmp = cast(typeof(cmp))&ti.compare; + } + dosort(data); + } +} + +/** Reserve a capacity for a dynamic array. If the array already has + * more elements or if the original length is zero it does nothing. + * Compiler-dependent. + * \param x the array to modify + * \param n the requested capacity + */ +template reserve(Value : Value[]) { + void reserve(inout Value[] x, size_t n) { + size_t oldlen = x.length; + if ((oldlen < n) && (oldlen > 0)) { + x.length = n; + x.length = oldlen; + } + } +} + +/** Iterate backwards over a dynamic array. This function should be + * used on the target array in a foreach statement or + * or as the target to a call to toSeq <tt>x.backwards.toSeq</tt> + * \param x the array to iterate over. + */ +template backwards(Value : Value[]) { + DArrayReverseIter!(Value) backwards(Value[] x) { + DArrayReverseIter!(Value) y; + y.x = x; + return y; + } +} + +/* Private helper for reverse iteration */ +private struct DArrayReverseIter(Value) { + Value[] x; + int opApply(int delegate(inout Value val) dg) { + int res = 0; + for (size_t n=x.length; n > 0; ) { + res = dg(x[--n]); + if (res) break; + } + return res; + } + int opApply(int delegate(inout size_t n, inout Value val) dg) { + int res = 0; + size_t cnt = 0; + for (size_t n=x.length; n > 0; cnt++) { + res = dg(cnt,x[--n]); + if (res) break; + } + return res; + } +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; +version (MinTLUnittest) { + private import std.random; + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.array unittest\n"); + + int[] x; + x.length = 1; + reserve!(int[])(x,100); + int[] y = x; + x.length = 90; + assert( cast(int*)x == cast(int*)y ); + version (MinTLVerboseUnittest) + printf("pass\n"); + + int[] t1,t2; + t1.length = 4; + t2.length = 4; + for(int k=0;k<4;k++) t1[k] = k*100; + foreach(size_t n, int val; backwards!(int[])(t1)) { + t2[n] = val; + } + assert( t1.reverse == t2 ); + version (MinTLVerboseUnittest) + printf("pass\n"); + + double[int] c; + c[100] = 1.1; + c[300] = 2.2; + c[-100] = 3.3; + double v; + assert( 100 in c ); + assert( !(200 in c) ); + assert( 300 in c ); + for (int k=0;k<1000;k++) { + c[k*100] = 1; + } + + // test simple sorting + static int[] data = [40,300,-20,100,400,200]; + int[] s1 = data.dup; + sort!(int[])(s1); + static int[] s2 = [-20,40,100,200,300,400]; + assert( s1 == s2 ); + + // test a large sort with default order + double[] s3; + for (int k=0;k<1000;k++) { + s3 ~= 1.0*rand()/100000.0 - 500000.0; + } + double[] s4 = s3.dup; + sort!(double[])(s3); + for (int k=0;k<999;k++) { + assert( s3[k] <= s3[k+1] ); + } + // test a large sort with custom order + int cmp(double*x,double*y){return *x>*y?-1:*x==*y?0:1;} + sort!(double[])(s4,&cmp); + for (int k=0;k<999;k++) { + assert( s4[k] >= s4[k+1] ); + } + + version (MinTLVerboseUnittest) + printf("finished mintl.array unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/arrayheap.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,333 @@ +/** \file arrayheap.d + * \brief A heap (complete binary tree) backed by an array + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.6 + */ + +module mintl.arrayheap; + +private { + import mintl.share; // for ~ and ~= + import mintl.mem; + //import std.string; +} + +/** \class ArrayHeap + * \brief A heap (complete binary tree) backed by an array + * + * An ArrayHeap!(Value) is a heap of data of type Value backed + * by an array. Adding to the tail and removing the head of the heap + * are O(log(n)) operations. The items in the heap are maintained + * in sorted order with the largest item at index 0 and for the nth item + * the items at index 2*n+1 and 2*n+2 are smaller (or equal to) + * item n. + * + * The optional allocator parameter ArrayHeap!(Value,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct ArrayHeap(Value, Alloc = GCAllocator) { + + alias ArrayHeap ContainerType; + alias Value ValueType; + alias size_t IndexType; + + Value[] data; ///< backing array. null by default, grows as needed + + invariant { + assert( tail <= data.length ); + } + + /** signature for a custom comparison function */ + alias int delegate(Value* a, Value* b) CompareFcn; + + /** Set custom comparison function. */ + void compareFcn(CompareFcn cmp) { + cmpFcn = cmp; + } + + /** Get heap contents as dynamic array slice of backing array. */ + Value[] values() { + return data[0..tail]; + } + + /** Adds an item to the heap. Increases capacity if needed. */ + void addTail(Value v) { + capacity(tail+1); + data[tail++] = v; + fixupTail(); + } + + /** Removes and returns the head item of the heap. If the target + * heap is empty an IndexOutOfBoundsException is thrown unless + * version=MinTLNoIndexChecking is set. + */ + Value takeHead() { + version (MinTLNoIndexChecking) { + // no error checking + } else { + if (tail == 0) + throw new IndexOutOfBoundsException(); + } + Value val = data[0]; + data[0] = data[--tail]; + data[tail] = Value.init; + fixupHead(); + return val; + } + + /** Removes the head item of the heap. */ + void removeHead() { + version (MinTLNoIndexChecking) { + // no error checking + } else { + if (tail == 0) + throw new IndexOutOfBoundsException(); + } + data[0] = data[--tail]; + data[tail] = Value.init; + fixupHead(); + } + + /** Get the length of heap. */ + size_t length() { + return tail; + } + + /** Test if container is empty. */ + bool isEmpty() { + return tail == 0; + } + + /** Clear all contents. */ + void clear() { + static if (is(Alloc == GCAllocator)) { + } else { + if (data.ptr) + Alloc.gcFree(data.ptr); + } + *this = ArrayHeap.init; + } + + /** Get the nth item in the heap from head. */ + Value opIndex(size_t n) { + return data[n]; + } + + /** Get a pointer to the nth item in the heap */ + Value* lookup(size_t n) { + return &data[n]; + } + + /** Set the nth item in the heap. */ + void opIndexAssign(Value val, size_t n) { + data[n] = val; + } + + /** Duplicates a heap. */ + ArrayHeap dup() { + ArrayHeap res; + static if (is(Alloc == GCAllocator)) { + res.data = data.dup; + } else { + Value* p = cast(Value*)Alloc.malloc(data.length * Value.sizeof); + res.data = p[0 .. data.length]; + res.data[] = data[]; + } + res.tail = tail; + res.cmpFcn = cmpFcn; + return res; + } + + /** Test for equality of two heaps. */ + int opEquals(ArrayHeap c) { + size_t len = length; + if (len !is c.length) + return 0; + size_t a,b; + a = 0; + b = 0; + TypeInfo ti = typeid(Value); + for (size_t k = 0; k < len; k++) { + if (!ti.equals(&data[a],&c.data[b])) + return 0; + a++; + b++; + } + return 1; + } + + /** Compare two heaps. */ + int opCmp(ArrayHeap c) { + size_t len = length; + if (len > c.length) + len = c.length; + size_t a,b; + a = 0; + b = 0; + TypeInfo ti = typeid(Value); + for (size_t k = 0; k < len; k++) { + int cmp = ti.compare(&data[a],&c.data[b]); + if (cmp) + return cmp; + a++; + b++; + } + return cast(int)length - cast(int)c.length; + } + + /** Returns a short string representation of the heap. */ + /+char[] toString() { + return "[ArrayHeap length " ~ std.string.toString(tail) ~ "]"; + }+/ + + /** Iterates over the heap from head to tail calling delegate to + * perform an action. The value is passed to the delegate. + */ + int opApplyNoKey(int delegate(inout Value x) dg){ + int res = 0; + for (size_t k=0; k < tail; k++) { + res = dg(data[k]); + if (res) break; + } + return res; + } + + /** Iterates over the heap from head to tail calling delegate to + * perform an action. The index from 0 and the value are passed + * to the delegate. + */ + int opApplyWithKey(int delegate(inout size_t n, inout Value x) dg){ + int res = 0; + for (size_t k=0; k < tail; k++) { + res = dg(k,data[k]); + if (res) break; + } + return res; + } + + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + + /** Ensure the minimum capacity of heap. */ + void capacity(size_t cap) { + if (cap > data.length) { + cap = (cap+1)*2; + static if (is(Alloc == GCAllocator)) { + data.length = cap; + } else { + Value* p = data.ptr; + p = cast(Value*)Alloc.gcRealloc(p,cap*Value.sizeof); + p[data.length .. cap] = Value.init; + data = p[0 .. cap]; + } + } + } + + // Helper functions + + // enforce heap invariant after a new head + private void fixupHead() { + size_t n = 0; + TypeInfo ti = typeid(Value); + if (cmpFcn is null) { + cmpFcn = cast(CompareFcn)&ti.compare; + } + for (;;) { + size_t n1 = 2*n+1; + if (n1 >= tail) break; + if ((n1 != tail-1) && (cmpFcn(&data[n1],&data[n1+1]) < 0)) + n1++; + if (cmpFcn(&data[n],&data[n1]) < 0) { + ti.swap(&data[n],&data[n1]); + n = n1; + } else { + break; + } + } + } + + // enforce heap invariant after a new tail + private void fixupTail() { + size_t n = tail-1; + TypeInfo ti = typeid(Value); + if (cmpFcn is null) { + cmpFcn = cast(CompareFcn)&ti.compare; + } + size_t n1 = (n-1)>>1; + while ((n > 0) && (cmpFcn(&data[n],&data[n1]) > 0)) { + ti.swap(&data[n],&data[n1]); + n = n1; + n1 = (n-1)>>1; + } + } + + // added by h3 + void eraseNoDelete() { + tail = 0; + } + + private CompareFcn cmpFcn; + private size_t tail; +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; +version (MinTLUnittest) { + private import std.string; + unittest { + version (MinTLVerboseUnittest) + printf("started mintl.arrayheap unittest\n"); + + ArrayHeap!(int) x,y,z; + x.data = new int[10]; + x.addTail(5); + x.addTail(3); + x.addTail(4); + assert( x.length == 3 ); + assert( x[0] == 5 ); + assert( x[x.length-1] == 4 ); + + y = x.dup; + + assert( x == y ); + + assert( x.takeHead == 5 ); + assert( x.takeHead == 4 ); + assert( x.takeHead == 3 ); + assert( x.length == 0 ); + + y.addTail(6); + int[10] y2; + int k=0; + foreach(int val; y) { + y2[k++] = val; + } + assert( y2[0] == 6 ); + assert( y2[1] == 5 ); + assert( y2[2] == 4 ); + assert( y2[3] == 3 ); + + k=0; + foreach(size_t n, int val; y) { + y2[n] = val; + } + assert( y2[0] == 6 ); + assert( y2[1] == 5 ); + assert( y2[2] == 4 ); + assert( y2[3] == 3 ); + + ArrayHeap!(int,MallocNoRoots) xm; + for (int k=0;k<100;k++) + xm.addTail(k); + for (int k=0;k<100;k++) + assert( xm.takeHead == 99-k ); + xm.clear(); + + version (MinTLVerboseUnittest) + printf("finished mintl.arrayheap unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/arraylist.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,858 @@ +/** \file arraylist.d + * \brief A list backed by an array. This container can + * also be used as an array with managed capacity by only + * inserting and removing from the tail and keeping the head fixed + * at 0. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.7.1 + */ + +module mintl.arraylist; + +private import mintl.share; // for ~ and ~= +private import mintl.sorting; +import mintl.mem; + +private extern(C) void *memmove(void *, void *, uint); + +//debug = dArrayList; // can also pass at command line + +/** \class ArrayList + * \brief A bounded list backed by an array + * + * An ArrayList!(Value) is a list of data of type Value backed + * by a circular array. The performance of ArrayLists is on the same + * order as for arrays except adding an element to the head of an + * ArrayList is constant. The backing array can be dynamic or static + * arrays and should be set prior to use by assigning to the + * <tt>data</tt> property or the <tt>capacity</tt> property. The + * ArrayList will automatically grow the backing array if needed. + * + * An ArrayList can also be used as an array with managed capacity. + * To do so only insert and remove from the tail and keep the head fixed + * at 0. + * + * The optional ReadOnly parameter ArrayList!(Value,ReadOnly) forbids + * operations that modify the container. The readonly() property returns + * a ReadOnly view of the container. + * + * The optional allocator parameter ArrayList!(Value,false,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct ArrayList(Value, bit ReadOnly = false, Alloc = GCAllocator) { + + alias ArrayList ContainerType; + alias ArrayList SliceType; + alias Value ValueType; + alias size_t IndexType; + alias ReadOnly isReadOnly; + + Value[] data; ///< backing array. null by default. + + invariant { + assert( data.length == 0 || start < data.length ); + assert( len <= data.length ); + } + + /** Get a ReadOnly view of the container */ + .ArrayList!(Value, true, Alloc) readonly() { + .ArrayList!(Value, true, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get a read-write view of the container */ + .ArrayList!(Value, false, Alloc) readwrite() { + .ArrayList!(Value, false, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + static if (!ReadOnly) { + + /** Appends an item to the tail of the list. If the target list is + * a sub-list call addAfter instead of addTail to insert an item + * after a sub-list. Increases capacity if needed. + */ + void addTail(Value v) { + capacity(length+1); + data[addi(start,len)] = v; + len++; + } + + /** Appends a list to the tail of the target list. If the target + * list is a sub-list call addAfter instead of addTail to insert + * another list after a sub-list. Increases capacity if needed. + */ + void addTail(ArrayList v) { + size_t vlen = v.length; + capacity(len+vlen); + copyBlock(v,data,addi(start,len),vlen); + len += vlen; + } + + /** overload ~ and ~= */ + mixin MListCatOperators!(ArrayList); + + /** Removes and returns the tail item of the list. If the target + * list is empty an IndexOutOfBoundsException is thrown unless + * version=MinTLNoIndexChecking is set. + */ + Value takeTail() { + boundsCheck(length-1); + len--; + size_t n = addi(start,len); + Value val = data[n]; + data[n] = Value.init; + return val; + } + + /** Removes the tail item of the list. */ + void removeTail() { + boundsCheck(length-1); + len--; + data[addi(start,len)] = Value.init; + } + + /** Prepends an item to the head of the target list. If the target + * list is a sub-list call addBefore instead of addHead to insert an + * after a sub-list. Increases capacity if needed. + */ + void addHead(Value v) { + debug(dArrayList) printf(" add %d %u\n",start-1,dec(start)); + capacity(len+1); + start = dec(start); + data[start] = v; + len++; + } + + /** Prepends a list to the head of the target list. If the target + * list is a sub-list call addBefore instead of addHead to insert a + * list before a sub-list. Increases capacity if needed. + */ + void addHead(ArrayList v) { + size_t vlen = v.length; + capacity(len+vlen); + size_t newhead = subi(start,vlen); + copyBlock(v,data,newhead,vlen); + start = newhead; + len += vlen; + } + + /** Removes and returns the head item of the list. If the target + * list is empty an IndexOutOfBoundsException is thrown unless + * version=MinTLNoIndexChecking is set. + */ + Value takeHead() { + boundsCheck(length-1); + Value val = data[start]; + data[start] = Value.init; + start = inc(start); + debug(dArrayList) printf("%d %d\n",start,val); + len--; + return val; + } + + /** Removes the head item of the list. */ + void removeHead() { + boundsCheck(len-1); + data[start] = Value.init; + start = inc(start); + len--; + debug(dArrayList) printf("%d\n",start); + } + + /** Insert a list before a sub-list. Increases capacity if needed. */ + void addBefore(ArrayList subv, ArrayList v) { + size_t vlen = v.length; + if (vlen == 0) return; + capacity(length+vlen); + size_t tlen = subv.start >= start ? subv.start-start : data.length-start+subv.start; + size_t newhead = subi(start,vlen); + moveBlockLeft(start,tlen,newhead); + copyBlock(v,data,subi(subv.start,vlen),vlen); + start = newhead; + len += vlen; + } + + /** Insert a list after a sub-list. Increases capacity if needed. */ + void addAfter(ArrayList subv, ArrayList v) { + size_t vlen = v.length; + if (vlen == 0) return; + capacity(length+vlen); + size_t tail = addi(start,len); + size_t stail = addi(subv.start,subv.len); + size_t tlen = stail <= tail ? tail-stail : data.length-stail+tail; + moveBlockRight(stail,tlen,addi(stail,vlen)); + copyBlock(v,data,stail,vlen); + len += vlen; + } + + /** Set the length of list. */ + void length(size_t len) { + capacity(len); + this.len = len; + } + + /** Clear all contents. */ + void clear() { + static if (is(Alloc == GCAllocator)) { + } else { + if (data.ptr) + Alloc.gcFree(data.ptr); + } + *this = ArrayList.init; + } + + /** Set the nth item in the list from head. Indexing out of bounds + * throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + void opIndexAssign(Value val, size_t n) { + boundsCheck(n); + data[addi(start,n)] = val; + } + + /** Set the value of one-item slice (more generally the head value). */ + void value(Value newValue) { + opIndexAssign(newValue,0); + } + + /** Removes a sub-list from the list. */ + void remove(ArrayList sublist) { + size_t tail = addi(start,len); + size_t slen = sublist.len; + size_t stail = addi(sublist.start,slen); + size_t tlen = stail <= tail ? tail-stail : data.length-stail+tail; + debug(dArrayList) printf("remove %d %d\n",sublist.start, sublist.length); + moveBlockLeft(stail, tlen, sublist.start); + fillBlock(subi(tail,slen),slen,Value.init); + len -= slen; + debug(dArrayList) printf("removed %d %d\n",start, len); + } + + /** Removes an item from the list, if present. */ + void remove(size_t index) { + ArrayList item = opSlice(index, index+1); + remove(item); + } + + /** Removes an item from the list and returns the value, if present. */ + Value take(size_t index) { + ArrayList item = opSlice(index, index+1); + Value val = item[0]; + remove(item); + return val; + } + + } // !ReadOnly + + /** Move a sub-list towards the tail by n items. If n is + * negative the sub-list moves towards the head. A positive end is + * the tail, negative the head and 0 is both. By default moves to + * to the next item. + */ + void next(int n = 1, int end = 0) { + if (end) + len += n<0?-n:n; + if (end <= 0) { + if (n<0) { + start = subi(start,-n); + } else { + start = addi(start,n); + } + } + } + + /** Get the length of list. */ + size_t length() { + return len; + } + + private const double GrowthRate = 1.5; + + /** Ensure the minimum capacity of list. */ + void capacity(size_t cap) { + if (data.length < cap) { + cap = cast(size_t)(cap*GrowthRate)+1; + if (start > data.length - len) { + size_t oldlen = data.length; + size_t oldheadlen = oldlen - start; + resizeData(cap); + moveBlockRight(start,oldheadlen, cap - oldheadlen); + start = cap-oldheadlen; + } else { + resizeData(cap); + } + } + } + + // helper for capacity + private void resizeData(size_t cap) { + static if (is(Alloc == GCAllocator)) { + data.length = cap; + } else { + Value* p = data.ptr; + p = cast(Value*)Alloc.gcRealloc(p,cap*Value.sizeof); + p[data.length .. cap] = Value.init; + data = p[0 .. cap]; + } + } + + /** Get the capacity of list. */ + size_t capacity() { + return data.length; + } + + /** Test if container is empty. */ + bool isEmpty() { + return len == 0; + } + + /** Get the nth item in the list from head. Indexing out of bounds + * throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + Value opIndex(size_t n) { + boundsCheck(n); + return data[addi(start,n)]; + } + + /** Get the value of one-item slice (more generally the head value). + * Useful for expressions like x.tail.value or x.head.value. */ + Value value() { + return opIndex(0); + } + + // helper function to check if the index is legal + private void boundsCheck(size_t n) { + version (MinTLNoIndexChecking) { + } else { + if (n >= len) { + throw new IndexOutOfBoundsException(); + } + } + } + + /** Create a one-item slice of the head. */ + ArrayList head() { + return opSlice(0,1); + } + + /** Create a one-item slice of the tail. */ + ArrayList tail() { + size_t len = length; + return opSlice(len-1,len); + } + + /** Reverse a list in-place. */ + ArrayList reverse() { + size_t tlen = len / 2; + size_t a,b; + a = start; + b = dec(addi(start,len)); + TypeInfo ti = typeid(Value); + for (size_t k = 0; k < tlen; k++) { + debug(dArrayList) printf("swapping %d %d\n",data[a],data[b]); + ti.swap(&data[a],&data[b]); + a = inc(a); + b = dec(b); + } + return *this; + } + + /** Get list contents as dynamic array (a slice if possible). */ + Value[] values() { + Value[] buffer; + if (start <= data.length-len) { + buffer = data[start .. start+len]; + } else { + buffer.length = len; + buffer[0 .. data.length-start] = data[start .. data.length]; + buffer[data.length-start .. buffer.length] = + data[0 .. len - data.length - start]; + } + return buffer; + } + + /** Duplicates a list. */ + ArrayList dup() { + ArrayList res; + static if (is(Alloc == GCAllocator)) { + res.data = data.dup; + } else { + Value* p = cast(Value*)Alloc.malloc(data.length * Value.sizeof); + res.data = p[0 .. data.length]; + res.data[] = data[]; + } + res.start = start; + res.len = len; + return res; + } + + /** Test for equality of two lists. */ + int opEquals(ArrayList c) { + if (len !is c.len) + return 0; + size_t a,b; + a = start; + b = c.start; + TypeInfo ti = typeid(Value); + for (size_t k = 0; k < len; k++) { + if (!ti.equals(&data[a],&c.data[b])) + return 0; + a = inc(a); + b = inc(b); + } + return 1; + } + + /** Compare two lists. */ + int opCmp(ArrayList c) { + size_t tlen = len; + if (tlen > c.len) + tlen = c.len; + size_t a,b; + a = start; + b = c.start; + TypeInfo ti = typeid(Value); + for (size_t k = 0; k < tlen; k++) { + int cmp = ti.compare(&data[a],&c.data[b]); + if (cmp) + return cmp; + a = inc(a); + b = inc(b); + } + return cast(int)len - cast(int)c.len; + } + + /** Create a sub-list from index a to b (exclusive). */ + ArrayList opSlice(size_t a, size_t b) { + ArrayList res; + res.data = data; + res.start = addi(start,a); + res.len = b-a; + debug(dArrayList) printf("slice %d %d\n",res.start,res.len); + return res; + } + + /** Create a sub-list from the head of a to the tail of b (inclusive). */ + ArrayList opSlice(ArrayList a, ArrayList b) { + ArrayList res; + res.data = data; + res.start = a.start; + if (b.start >= a.start) + res.len = b.start - a.start + b.len; + else + res.len = data.length - a.start + b.len + b.start; + return res; + } + + /** Iterates over the list from head to tail calling delegate to + * perform an action. The value is passed to the delegate. + */ + int opApplyNoKeyStep(int delegate(inout Value x) dg, int step = 1){ + int dg2(inout size_t n, inout Value x) { + return dg(x); + } + return opApplyWithKeyStep(&dg2,step); + } + + /** Iterates over the list from head to tail calling delegate to + * perform an action. The index from 0 and the value are passed + * to the delegate. + */ + int opApplyWithKeyStep(int delegate(inout size_t n, inout Value x) dg, int step = 1){ + if (len == 0) return 0; + int res = 0; + size_t tail = addi(start,len); + size_t istart = step>0 ? start : dec(tail); + size_t iend = step>0 ? tail : dec(start); + size_t n = step>0 ? 0 : len-1; + for (size_t k = istart; k != iend;) { + res = dg(n,data[k]); + if (res) break; + if (step < 0) + k = subi(k,-step); + else + k = addi(k,step); + n += step; + } + return res; + } + + /** Iterates over the list from head to tail calling delegate to + * perform an action. A one-item sub-list is passed to the delegate. + */ + int opApplyIterStep(int delegate(inout ArrayList n) dg, int step = 1){ + ArrayList itr; + itr.data = data; + itr.len = 1; + int dg2(inout size_t n, inout Value x) { + itr.start = addi(start,n); + return dg(itr); + } + return opApplyWithKeyStep(&dg2,step); + } + + /** Iterate backwards over the list (from tail to head). + * This should only be called as the + * iteration parameter in a <tt>foreach</tt> statement + */ + ArrayListReverseIter!(Value,ReadOnly,Alloc) backwards() { + ArrayListReverseIter!(Value,ReadOnly,Alloc) res; + res.list = this; + return res; + } + + /** Helper functions for opApply */ + mixin MOpApplyImpl!(ArrayList); + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + alias opApplyIter opApply; + + ArrayList getThis(){return *this;} + mixin MListAlgo!(ArrayList, getThis); + mixin MRandomAccessSort!(ArrayList, getThis); + + /** Get a pointer to the nth item in the list from head. Indexing + * out of bounds throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + Value* lookup(size_t n) { + boundsCheck(n); + return &data[addi(start,n)]; + } + + // Helper functions + + // helper function to copy sections of the backing buffer + private void moveBlockLeft(size_t srchead, size_t len, size_t desthead) { + size_t ns = srchead; + size_t nd = desthead; + while (len > 0) { + debug(dArrayList) printf("move left len %u\n",len); + size_t sz = len; + if (ns > data.length-sz) + sz = data.length-ns; + if (nd > data.length-sz) + sz = data.length-nd; + assert(sz != 0); + memmove(&data[nd],&data[ns],sz*Value.sizeof); + ns = addi(ns,sz); + nd = addi(nd,sz); + len -= sz; + } + } + + // helper function to copy sections of the backing buffer + private void moveBlockRight(size_t srchead, size_t len, size_t desthead) { + debug(dArrayList) printf("len %u\n",len); + size_t ns = addi(srchead,len-1)+1; + size_t nd = addi(desthead,len-1)+1; + while (len > 0) { + int sz = len; + if (ns < sz) + sz = ns; + if (nd < sz) + sz = nd; + assert(sz != 0); + memmove(&data[nd-sz],&data[ns-sz],sz*Value.sizeof); + ns = dec(subi(ns,sz))+1; + nd = dec(subi(nd,sz))+1; + len -= sz; + } + } + + // helper function to copy sections of the backing buffer + private void copyBlock(ArrayList src, + Value[] destdata,int desthead, + int len) { + Value[] srcdata = src.data; + int srchead = src.start; + int ns = srchead; + int nd = desthead; + while (len > 0) { + debug(dArrayList) printf("copy len %u %d %d len %d len %d\n", + len,ns,nd,srcdata.length,destdata.length); + int sz = len; + if (ns > srcdata.length-sz) + sz = srcdata.length-ns; + if (nd > destdata.length-sz) + sz = destdata.length-nd; + assert(sz != 0); + memmove(&destdata[nd],&srcdata[ns],sz*Value.sizeof); + ns = src.addi(ns,sz); + nd = addi(nd,sz); + len -= sz; + } + } + + // helper function to fill a section of the backing array + private void fillBlock(size_t srchead, size_t len, Value val) { + size_t ns = srchead; + while (len > 0) { + size_t sz = len; + if (ns > data.length-sz) + sz = data.length-ns; + assert(sz != 0); + data[ns .. ns+sz] = val; + ns = addi(ns,sz); + len -= sz; + } + } + + // move index n by 1 with wrapping + private size_t inc(size_t n) { + return (n == data.length-1) ? 0 : n+1; + } + + // move index n by -1 with wrapping + private size_t dec(size_t n) { + return (n == 0) ? data.length-1 : n-1; + } + + // move index n by -diff with wrapping + private size_t subi(size_t n, size_t diff) { + size_t res; + if (n < diff) + res = data.length - diff + n; + else + res = n - diff; + debug(dArrayList) printf("subi %d %d len %d got %d\n",n,diff,data.length,res); + return res; + } + + // move index n by diff with wrapping + private size_t addi(size_t n, size_t diff) { + size_t res; + if (data.length - n <= diff) + res = diff - (data.length - n); + else + res = n + diff; + debug(dArrayList) printf("addi %d %d len %d got %d\n",n,diff,data.length,res); + return res; + } + + private size_t start, len; +} + +// helper structure for backwards() +struct ArrayListReverseIter(Value,bit ReadOnly, Alloc=GCAllocator) { + mixin MReverseImpl!(ArrayList!(Value,ReadOnly,Alloc)); +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; +version (MinTLUnittest) { + private import std.string; + private import std.random; + unittest { + version (MinTLVerboseUnittest) + printf("started mintl.arraylist unittest\n"); + + ArrayList!(int) x,y,z; + x.data = new int[10]; + x.add(5,3,4); + assert( x[0] == 5 ); + assert( x[1] == 3 ); + assert( x[2] == 4 ); + assert( x.length == 3 ); + x.takeTail(); + x ~= 4; + assert( x[2] == 4 ); + + y = x.dup; + + assert( x == y ); + + x.addHead(-1); + x.addHead(-2); + // private bug + // assert( x.start == x.data.length - 2 ); + assert( x.head == x[0 .. 1] ); + assert( x.length == 5 ); + assert( x.tail == x[4 .. 5] ); + assert( x.data[x.data.length-1] == -1); + assert( x.data[x.data.length-2] == -2); + assert( x.takeHead == -2 ); + assert( x.takeHead == -1 ); + assert( x[0] == 5 ); + assert( x[x.length-1] == 4 ); + assert( x.takeHead == 5 ); + assert( x.takeHead == 3 ); + assert( x.takeHead == 4 ); + assert( x.length == 0 ); + + assert( y.length == 3 ); + assert( y[0] == 5 ); + assert( y[2] == 4 ); + y ~= 6; + debug(dArrayList) printf("%d %d %d %d\n",y.start,y.tail_,y[0],y[3]); + y = y.reverse; + debug(dArrayList) printf("%d %d %d %d\n",y.start,y.tail_,y[0],y[3]); + assert( y[0] == 6 ); + assert( y[1] == 4 ); + assert( y[2] == 3 ); + assert( y[3] == 5 ); + + int[10] y2; + int k=0; + foreach(int val; y) { + y2[k++] = val; + } + assert( y2[0] == 6 ); + assert( y2[1] == 4 ); + assert( y2[2] == 3 ); + assert( y2[3] == 5 ); + + k=0; + foreach(int val; y.backwards()) { + y2[k++] = val; + } + assert( y2[0] == 5 ); + assert( y2[1] == 3 ); + assert( y2[2] == 4 ); + assert( y2[3] == 6 ); + + k=0; + foreach(size_t n, int val; y) { + y2[n] = val; + } + assert( y2[0] == 6 ); + assert( y2[1] == 4 ); + assert( y2[2] == 3 ); + assert( y2[3] == 5 ); + + k=0; + foreach(ArrayList!(int) itr; y) { + y2[k++] = itr[0]; + } + assert( y2[0] == 6 ); + assert( y2[1] == 4 ); + assert( y2[2] == 3 ); + assert( y2[3] == 5 ); + + ArrayList!(int) y3 = y[2..4]; + assert( y3.length == 2 ); + assert( y3[0] == 3 ); + assert( y3[1] == 5 ); + y3[0..1].swap(y3[1..2]); + assert( y3[0] == 5 ); + assert( y3[1] == 3 ); + + y3[0] = 10; + assert( y[2] == 10 ); + + y3.next(-1); + assert( y3.length == 2 ); + assert( y3[0] == 4 ); + assert( y3[1] == 10 ); + y3.next(-1,-1); + assert( y3.length == 3 ); + assert( y3[0] == 6 ); + assert( y3[2] == 10 ); + y3.next(1,1); + assert( y3.length == 4 ); + assert( y3[0] == 6 ); + assert( y3[3] == 3 ); + + ArrayList!(char[]) c = ArrayList!(char[]).make("a","a","a","a"); + assert( c.opIn("a") == c.head ); + assert( c.count("a") == 4 ); + for (int kk=0;kk<100;kk++) { + c ~= toString(kk); + c.takeHead(); + } + + // test addAfter, addBefore and remove + ArrayList!(double) w; + w.data = new double[30]; + for (int j=0;j<20;j++) + w ~= j; + w.remove(w[10..15]); + assert( w.length == 15 ); + assert( w[10] == 15 ); + for (int j=0;j<5;j++) + w.addHead(j); + w.remove(w[2..7]); + assert( w.length == 15 ); + ArrayList!(double) w2; + w2.data = new double[30]; + for (k=0;k<20;k++) + w2 ~= k; + w.addBefore(w[5..7],w2[10..15]); + assert( w.length == 20 ); + assert( w[0] == 4 ); + foreach( double d; w) { + version (MinTLVerboseUnittest) + printf(" %g",d); + } + version (MinTLVerboseUnittest) + printf("\n"); + assert( w[5] == 10 ); + + ArrayList!(int) cda = ArrayList!(int).make(20,30); + cda.capacity = 20; + assert( cda.capacity >= 20 ); + assert( cda.length == 2 ); + assert( cda.values == cda.data[0..2] ); + cda.length = 4; + assert( cda.length == 4 ); + assert( cda[cda.length - 1] == 0 ); + cda.capacity = 40; + assert( cda.length == 4 ); + assert( cda.data.length >= 40 ); + cda.addHead(40); + cda.addHead(50); + cda.capacity = 50; + uint ss = cda.capacity; + assert( cda.length >= 6 ); + assert( cda[0] == 50 ); + assert( cda[1] == 40 ); + assert( cda[2] == 20 ); + assert( cda[3] == 30 ); + assert( cda[4] == 0 ); + + ArrayList!(int,false,Malloc) xm = + ArrayList!(int,false,Malloc).make(10,20,30); + assert( xm.takeTail == 30 ); + assert( xm.takeHead == 10 ); + for (int u;u<10000;u++) { + xm ~= u; + } + xm.clear(); + assert( xm.isEmpty ); + + // test simple sorting + ArrayList!(int) s1; + s1.add(40,300,-20,100,400,200); + s1.sort(); + ArrayList!(int) s2 = ArrayList!(int).make(-20,40,100,200,300,400); + assert( s1 == s2 ); + + // test a large sort with default order + ArrayList!(double) s3; + for (k=0;k<1000;k++) { + s3 ~= 1.0*rand()/100000.0 - 500000.0; + } + ArrayList!(double) s4 = s3.dup; + s3.sort(); + for (k=0;k<999;k++) { + assert( s3[k] <= s3[k+1] ); + } + // test a large sort with custom order + int cmp(double*x,double*y){return *x>*y?-1:*x==*y?0:1;} + s4.sort(&cmp); + for (k=0;k<999;k++) { + assert( s4[k] >= s4[k+1] ); + } + + version (MinTLVerboseUnittest) + printf("finished mintl.arraylist unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/deque.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,920 @@ +/** \file deque.d + * \brief A resizable double-ended queue stored in blocks with + * constant time insertion at the front and tail. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.7.1 + */ + +module mintl.deque; + +import mintl.mem; +private import mintl.share; // for ~ and ~= +private import mintl.sorting; + +private extern(C) void *memmove(void *, void *, uint); + +//debug = dDeque; // can also pass at command line + +/** \class Deque + * \brief A resizable double-ended queue stored in blocks with + * constant time insertion at the front and tail. + * + * A Deque!(Value) is a list of data of type Value backed by a + * block-allocated array. The size of the allocation blocks varies + * with the number of elements in the deque. The performance of Deques + * is on the same order as for arrays except adding an element to the + * head of a Deque is constant. + * + * The optional ReadOnly parameter Deque!(Value,ReadOnly) forbids + * operations that modify the container. The readonly() property returns + * a ReadOnly view of the container. + * + * The optional allocator parameter Deque!(Value,false,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct Deque(Value, bit ReadOnly = false, Alloc = GCAllocator) { + + alias Deque ContainerType; + alias Deque SliceType; + alias Value ValueType; + alias size_t IndexType; + alias ReadOnly isReadOnly; + + alias Value* Block; + const size_t psize = (void*).sizeof; + + invariant { + assert( total() == 0 || start < total() ); + assert( len <= total() ); + } + + /** Get a ReadOnly view of the container */ + .Deque!(Value, true, Alloc) readonly() { + .Deque!(Value, true, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get a read-write view of the container */ + .Deque!(Value, false, Alloc) readwrite() { + .Deque!(Value, false, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + static if (ReadOnly) { + /** Duplicates a deque. */ + /* private bug + Deque dup() { + .Deque!(Value,false,Alloc) res; + size_t cap = block(len)+1; + if (len == 0) return res.readonly; + static if (is(Alloc == GCAllocator)) { + res.data = new Block[cap]; + } else { + Block* p = cast(Block*)Alloc.malloc(cap * psize); + res.data = p[0..cap]; + res.data[] = null; + } + res.addTail(this.readwrite); + return res.readonly; + } + */ + } else { + + /** Appends an item to the tail of the deque. If the target deque is + * a slice call addAfter instead of addTail to insert an item + * after a slice. + */ + void addTail(Value v) { + capacity(len+1); + *plookup(addi(start,len)) = v; + len++; + } + + /** Appends a deque to the tail of the target deque. If the target + * deque is a slice call addAfter instead of addTail to insert + * another deque after a slice. + */ + void addTail(Deque v) { + size_t vlen = v.len; + if (vlen == 0) return; + capacity(len+vlen); + copyBlock(v, data, addi(start,len), vlen); + len += vlen; + } + + /** overload ~ and ~= */ + mixin MListCatOperators!(Deque); + + /** Removes and returns the tail item of the deque. If the target + * deque is empty an IndexOutOfBoundsException is thrown unless + * version=MinTLNoIndexChecking is set. + */ + Value takeTail() { + boundsCheck(len-1); + len--; + Value* pval = plookup(addi(start,len)); + Value val = *pval; + *pval = Value.init; + return val; + } + + /** Removes the tail item of the deque. */ + void removeTail() { + boundsCheck(len-1); + len--; + *plookup(addi(start,len)) = Value.init; + } + + /** Prepends an item to the head of the target deque. If the target + * deque is a slice call addBefore instead of addHead to insert an + * item before a slice. + */ + void addHead(Value v) { + capacity(len+1); + start = dec(start); + *plookup(start) = v; + len++; + } + + /** Prepends a deque to the head of the target deque. If the target + * deque is a slice call addBefore instead of addHead to insert a + * deque before a slice. + */ + void addHead(Deque v) { + size_t vlen = v.len; + if (vlen == 0) return; + capacity(len+vlen); + size_t newhead = subi(start,vlen); + copyBlock(v, data, newhead, vlen); + start = newhead; + len += vlen; + } + + /** Removes and returns the head item of the deque. If the target + * deque is empty an IndexOutOfBoundsException is thrown unless + * version=MinTLNoIndexChecking is set. + */ + Value takeHead() { + boundsCheck(len-1); + Value* pval = plookup(start); + start = inc(start); + Value val = *pval; + *pval = Value.init; + len--; + debug(dDeque) printf("%d %d\n",start,val); + return val; + } + + /** Removes the head item of the deque. */ + void removeHead() { + boundsCheck(len-1); + Value* pval = plookup(start); + start = inc(start); + len--; + *pval = Value.init; + } + + /** Insert a deque before a slice. */ + void addBefore(Deque subv, Deque v) { + size_t vlen = v.length; + if (vlen == 0) return; + capacity(len+vlen); + size_t tlen = subv.start >= start ? subv.start-start : total-start+subv.start; + size_t newhead = subi(start,vlen); + debug(dDeque)printf("about to moveBlockLeft %d\n",tlen); + moveBlockLeft(start,tlen,newhead); + copyBlock(v,data,subi(subv.start,vlen),vlen); + start = newhead; + len += vlen; + } + + /** Insert a deque after a slice. */ + void addAfter(Deque subv, Deque v) { + size_t vlen = v.length; + if (vlen == 0) return; + capacity(len+vlen); + size_t tail = addi(start,len); + size_t subtail = addi(subv.start,subv.len); + size_t tlen = subtail <= tail ? tail-subtail : total-subtail+tail; + moveBlockRight(subtail,tlen,addi(subtail,vlen)); + copyBlock(v,data,subtail,vlen); + len += vlen; + } + + /** Clear all contents. */ + void clear() { + static if (is(Alloc == GCAllocator)) { + } else { + foreach (Block b; data) { + Alloc.gcFree(b); + } + if (data.ptr) + Alloc.free(data.ptr); + } + *this = Deque.init; + } + + /** Set the nth item in the deque from head. Indexing out of bounds + * throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + void opIndexAssign(Value val, size_t n) { + boundsCheck(n); + *plookup(addi(start,n)) = val; + } + + /** Set the value of one-item slice (more generally the head value). */ + void value(Value newValue) { + opIndexAssign(newValue,0); + } + + /** Removes a slice from the deque. */ + void remove(Deque sublist) { + size_t tail = addi(start,len); + size_t slen = sublist.len; + size_t stail = addi(sublist.start,slen); + size_t tlen = stail <= tail ? tail-stail : data.length-stail+tail; + debug(dArrayList) printf("remove %d %d\n",sublist.start, sublist.length); + moveBlockLeft(stail, tlen, sublist.start); + fillBlock(subi(tail,slen),slen,Value.init); + len -= slen; + } + + /** Removes an item from the list and returns the value, if present. */ + Value take(size_t index) { + Deque item = opSlice(index, index+1); + Value val = item[0]; + remove(item); + return val; + } + + /** Removes an item from the deque if present. */ + void remove(size_t index) { + Deque item = opSlice(index, index+1); + remove(item); + } + + /** Reverse a deque in-place. */ + Deque reverse() { + size_t tlen = len / 2; + size_t a,b; + a = start; + b = dec(addi(start,len)); + TypeInfo ti = typeid(Value); + for (size_t k = 0; k < tlen; k++) { + ti.swap(plookup(a),plookup(b)); + a = inc(a); + b = dec(b); + } + return *this; + } + + /** Duplicates a deque. */ + Deque dup() { + Deque res; + if (len == 0) return res; + size_t cap = block(len)+1; + static if (is(Alloc == GCAllocator)) { + res.data = new Block[cap]; + } else { + Block* p = cast(Block*)Alloc.malloc(cap * psize); + res.data = p[0..cap]; + res.data[] = null; + } + res.addTail(*this); + return res; + } + + } // !ReadOnly + + /** Move a slice towards the tail by n items. If n is + * negative the slice moves towards the head. A positive end is + * the tail, negative the head and 0 is both. By default moves to + * to the next item. + */ + void next(int n = 1, int end = 0) { + if (end) + len += n<0?-n:n; + if (end <= 0) { + if (n<0) { + start = subi(start,-n); + } else { + start = addi(start,n); + } + } + } + + /** Get the length of deque. */ + size_t length() { + return len; + } + + /** Test if container is empty. */ + bool isEmpty() { + return len == 0; + } + + /** Get the nth item in the deque from head. Indexing out of bounds + * throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + Value opIndex(size_t n) { + boundsCheck(n); + return *plookup(addi(start,n)); + } + + // lookup an index and return a pointer to the slot + private Value* plookup(size_t n) { + debug(dDeque) printf("plookup for %d block %d offset %d blocklen %d\n", + n,block(n),offset(n),data.length); + debug(dDeque) printf(" plookup got %p\n", data[block(n)]); + size_t bn = block(n); + // if (bn >= data.length) bn -= data.length; + Block b = data[bn]; + if (b is null) + data[bn] = b = newBlock();; + return b+offset(n); + } + + // allocate a new block + private Block newBlock() { + static if (is(Alloc == GCAllocator)) { + return (new Value[BlockSize]).ptr; + } else { + Value* p = cast(Value*)Alloc.gcMalloc(BlockSize * Value.sizeof); + Value[] q = p[0..BlockSize]; + q[] = Value.init; + return p; + } + } + + /** Get the value of one-item slice (more generally the head value). + * Useful for expressions like x.tail.value or x.head.value. */ + Value value() { + return opIndex(0); + } + + // helper function to check if the index is legal + private void boundsCheck(size_t n) { + version (MinTLNoIndexChecking) { + } else { + if (n >= len) { + throw new IndexOutOfBoundsException(); + } + } + } + + /** Get deque contents as dynamic array. */ + Value[] values() { + Value[] res = new Value[len]; + foreach(size_t k, Value val; *this) + res[k] = val; + return res; + } + + /** Test for equality of two deques. */ + int opEquals(Deque c) { + if (len !is c.len) + return 0; + size_t a,b; + a = start; + b = c.start; + TypeInfo ti = typeid(Value); + for (size_t k = 0; k < len; k++) { + Value* bn = c.data[block(b)]+offset(b); + if (!ti.equals(plookup(a),bn)) + return 0; + a = inc(a); + b = inc(b); + } + return 1; + } + + /** Compare two lists. */ + int opCmp(Deque c) { + size_t tlen = len; + if (tlen > c.len) + tlen = c.len; + size_t a,b; + a = start; + b = c.start; + TypeInfo ti = typeid(Value); + for (size_t k = 0; k < tlen; k++) { + Value* bn = c.data[block(b)]+offset(b); + int cmp = ti.compare(plookup(a),bn); + if (cmp) + return cmp; + a = inc(a); + b = inc(b); + } + return cast(int)len - cast(int)c.len; + } + + /** Create a slice from index a to b (exclusive). */ + Deque opSlice(size_t a, size_t b) { + Deque res; + res.data = data; + res.start = addi(start,a); + res.len = b-a; + return res; + } + + /** Create a slice from the head of a to the tail of b (inclusive). */ + Deque opSlice(Deque a, Deque b) { + Deque res; + res.data = data; + res.start = a.start; + if (b.start >= a.start) + res.len = b.start - a.start + b.len; + else + res.len = data.length - a.start + b.len + b.start; + return res; + } + + /** Create a one-item slice of the head. */ + Deque head() { + return opSlice(0,1); + } + + /** Create a one-item slice of the tail. */ + Deque tail() { + return opSlice(len-1,len); + } + + /** Iterates over the deque from head to tail calling delegate to + * perform an action. The value is passed to the delegate. + */ + int opApplyNoKeyStep(int delegate(inout Value x) dg,int step=1){ + int dg2(inout size_t n, inout Value x) { + return dg(x); + } + return opApplyWithKeyStep(&dg2,step); + } + + /** Iterates over the deque from head to tail calling delegate to + * perform an action. The index from 0 and the value are passed + * to the delegate. + */ + int opApplyWithKeyStep(int delegate(inout size_t n, inout Value x) dg, + int step = 1){ + if (len == 0) return 0; + int res = 0; + size_t tail = addi(start,len); + size_t istart = step>0 ? start : dec(tail); + size_t iend = step>0 ? tail : dec(start); + size_t n = step>0 ? 0 : len-1; + for (size_t k = istart; k != iend;) { + res = dg(n,data[block(k)][offset(k)]); + if (res) break; + if (step < 0) + k = subi(k,-step); + else + k = addi(k,step); + n += step; + } + return res; + } + + /** Iterates over the deque from head to tail calling delegate to + * perform an action. A one-item slice is passed to the delegate. + */ + int opApplyIterStep(int delegate(inout Deque n) dg, int step = 1){ + Deque itr; + itr.data = data; + itr.len = 1; + int dg2(inout size_t n, inout Value x) { + itr.start = addi(start,n); + return dg(itr); + } + return opApplyWithKeyStep(&dg2,step); + } + + /** Iterate backwards over the deque (from tail to head). + * This should only be called as the + * iteration parameter in a <tt>foreach</tt> statement + */ + DequeReverseIter!(Value,ReadOnly,Alloc) backwards() { + DequeReverseIter!(Value,ReadOnly,Alloc) res; + res.list = this; + return res; + } + + /** Helper functions for opApply */ + mixin MOpApplyImpl!(Deque); + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + alias opApplyIter opApply; + + Deque getThis(){return *this;} + mixin MListAlgo!(Deque, getThis); + mixin MRandomAccessSort!(Deque, getThis); + + /** Get a pointer to the nth item in the deque from head. Indexing + * out of bounds throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + Value* lookup(size_t n) { + boundsCheck(n); + return plookup(addi(start,n)); + } + + // Helper functions + + // compute the block number for an index + private size_t block(size_t n) { + return n >> BlockShift; + } + + // compute the offset within a block for an index + private size_t offset(size_t n) { + return n & BlockMask; + } + + /** Ensure the minimum capacity of list. */ + void capacity(size_t cap) { + cap = block(cap)+1; + if (data.length <= cap) { + cap = cap*2; + debug(dDeque) printf("growing capacity from %d to %d\n",data.length, cap); + if (start + len > total) { + size_t oldlen = data.length; + size_t offs = cap-oldlen; + size_t h = block(start); + resizeData(cap); + memmove(&data[h+offs],&data[h],(oldlen-h)*psize); + if (h == block(start+len)%data.length) { + static if (is(Alloc == GCAllocator)) { + data[h+offs] = data[h+offs][0..BlockSize].dup.ptr; + } else { + Block p = newBlock(); + p[0 .. BlockSize] = data[h+offs][0 .. BlockSize]; + data[h+offs] = p; + } + data[h][offset(start) .. BlockSize] = Value.init; + data[h+offs][0 .. offset(start+len)] = Value.init; + data[h+1 .. h+offs] = null; + } else { + data[h .. h+offs] = null; + } + start += offs*BlockSize; + } else { + resizeData(cap); + } + } + } + + // helper for capacity + private void resizeData(size_t cap) { + static if (is(Alloc == GCAllocator)) { + data.length = cap; + } else { + Block* p = data.ptr; + p = cast(Block*)Alloc.realloc(p,cap*psize); + p[data.length .. cap] = null; + data = p[0 .. cap]; + } + } + + /** Get the capacity of list. */ + size_t capacity() { + return total(); + } + + private const size_t BlockShift = Value.sizeof>128 ? 1 : 7; + private const size_t BlockSize = 1 << BlockShift; + private const size_t BlockMask = BlockSize - 1; + + // Helper functions + + // helper function to copy sections of the backing buffer + private void moveBlockLeft(size_t srchead, size_t len, size_t desthead) { + size_t ns = srchead; + size_t nd = desthead; + debug(dDeque)printf("moveBlockLeft %d\n",len); + while (len > 0) { + size_t sz = len; + size_t offs = offset(ns); + size_t offd = offset(nd); + size_t bn = block(ns); + Block srcblock = data[bn]; + if (srcblock == null) + data[bn] = srcblock = newBlock(); + bn = block(nd); + Block destblock = data[bn]; + if (destblock == null) + data[bn] = destblock = newBlock(); + if (offs+sz > BlockSize) + sz = BlockSize-offs; + if (offd+sz > BlockSize) + sz = BlockSize-offd; + assert(sz != 0); + memmove(&destblock[offd],&srcblock[offs],sz*Value.sizeof); + ns = addi(ns,sz); + nd = addi(nd,sz); + len -= sz; + } + } + + // helper function to copy sections of the backing buffer + private void moveBlockRight(size_t srchead, size_t len, size_t desthead) { + size_t ns = addi(srchead,len-1)+1; + size_t nd = addi(desthead,len-1)+1; + while (len > 0) { + size_t sz = len; + size_t bn = block(ns); + size_t offs = offset(ns); + size_t offd = offset(nd); + Block srcblock = data[bn]; + if (srcblock == null) + data[bn] = srcblock = newBlock(); + bn = block(nd); + Block destblock = data[bn]; + if (destblock == null) + data[bn] = destblock = newBlock(); + if (offs < sz) + sz = offs; + if (offd < sz) + sz = offd; + assert(sz != 0); + memmove(&destblock[offd],&srcblock[offs],sz*Value.sizeof); + ns = dec(subi(ns,sz))+1; + nd = dec(subi(nd,sz))+1; + len -= sz; + debug(dDeque)printf("moveBlockRight %d\n",sz); + } + } + + // helper function to copy sections of the backing buffer + private void copyBlock(Deque src, + Block[] destdata, + uint desthead, + uint len) { + Block[] srcdata = src.data; + int srchead = src.start; + int ns = srchead; + int nd = desthead; + while (len > 0) { + int sz = len; + size_t offs = offset(ns); + size_t offd = offset(nd); + size_t bn = block(ns); + Block srcblock = srcdata[bn]; + if (srcblock == null) + srcdata[bn] = srcblock = newBlock(); + if (offs+sz > BlockSize) + sz = BlockSize-offs; + bn = block(nd); + Block destblock = destdata[bn]; + if (destblock == null) + destdata[bn] = destblock = newBlock(); + if (offd+sz > BlockSize) + sz = BlockSize-offd; + assert(sz != 0); + memmove(&destblock[offd],&srcblock[offs],sz*Value.sizeof); + ns = src.addi(ns,sz); + nd = addi(nd,sz); + len -= sz; + debug(dDeque)printf("copyBlock %d\n",sz); + } + } + + // helper function to fill a section of the backing array + private void fillBlock(size_t srchead, size_t len, Value val) { + size_t ns = srchead; + while (len > 0) { + size_t sz = len; + size_t bn = block(ns); + size_t off = offset(ns); + Block block = data[bn]; + if (block == null) + data[bn] = block = newBlock(); + if (off+sz > BlockSize) + sz = BlockSize-off; + assert(sz != 0); + block[off .. off+sz] = val; + ns = addi(ns,sz); + len -= sz; + debug(dDeque)printf("fillBlock %d\n",sz); + } + } + + // move index n by 1 with wrapping + private size_t inc(size_t n) { + return (n == total()-1) ? 0 : n+1; + } + + // move index n by -1 with wrapping + private size_t dec(size_t n) { + return (n == 0) ? total()-1 : n-1; + } + + // move index n by -diff with wrapping + private size_t subi(size_t n, size_t diff) { + size_t res; + if (n < diff) + res = total() - diff + n; + else + res = n - diff; + return res; + } + + // move index n by diff with wrapping + private size_t addi(size_t n, size_t diff) { + size_t res; + if (total() - n <= diff) { + res = diff - (total() - n); + } else + res = n + diff; + return res; + } + + private size_t total(){ return data.length << BlockShift; } + + private Block[] data; // array of blocks of data + private size_t start, len; +} + +// helper structure for backwards() +struct DequeReverseIter(Value,bit ReadOnly,Alloc) { + mixin MReverseImpl!(Deque!(Value,ReadOnly,Alloc)); +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; +version (MinTLUnittest) { + private import std.string; + private import std.random; + unittest { + version (MinTLVerboseUnittest) + printf("started mintl.deque unittest\n"); + + Deque!(int) x,y,z; + x.addTail(22); + x.addTail(33); + assert( x[0] == 22 ); + assert( x[1] == 33 ); + x.addHead(11); + assert( x[0] == 11 ); + assert( x[2] == 33 ); + + y = x.dup; + + assert( y.length == 3 ); + assert( y[0] == 11 ); + assert( y[2] == 33 ); + z = x.dup; + z.addTail(y); + assert( z.length == 6 ); + assert( z[0] == 11 ); + assert( z[2] == 33 ); + assert( z[3] == 11 ); + assert( z[4] == 22 ); + assert( z[5] == 33 ); + + Deque!(int,false,Malloc) mx; + mx.add(30,40,50); + assert( mx.takeHead == 30 ); + assert( mx.takeTail == 50 ); + for(int u = 0;u<10000;u++) { + mx~=u; + } + mx.clear(); + assert( mx.isEmpty ); + + x = x.init; + x ~= 5; + x ~= 3; + x ~= 4; + assert( x[0] == 5 ); + assert( x[1] == 3 ); + assert( x[2] == 4 ); + assert( x.length == 3 ); + + x.reverse(); + assert( x[2] == 5 ); + assert( x[1] == 3 ); + assert( x[0] == 4 ); + + y = x.dup; + + assert( x == y ); + + y.addHead(6); + + int[10] y2; + int k=0; + foreach(int val; y) { + y2[k++] = val; + } + assert( y2[0] == 6 ); + assert( y2[1] == 4 ); + assert( y2[2] == 3 ); + assert( y2[3] == 5 ); + + int[] w2 = y.values; + assert( w2[0] == 6 ); + assert( w2[1] == 4 ); + assert( w2[2] == 3 ); + assert( w2[3] == 5 ); + assert( w2.length == 4 ); + + k=0; + foreach(int val; y.backwards()) { + y2[k++] = val; + } + assert( y2[0] == 5 ); + assert( y2[1] == 3 ); + assert( y2[2] == 4 ); + assert( y2[3] == 6 ); + k=0; + foreach(size_t n, int val; y) { + y2[n] = val; + } + assert( y2[0] == 6 ); + assert( y2[1] == 4 ); + assert( y2[2] == 3 ); + assert( y2[3] == 5 ); + k=0; + foreach(Deque!(int) itr; y) { + y2[k++] = itr[0]; + } + assert( y2[0] == 6 ); + assert( y2[1] == 4 ); + assert( y2[2] == 3 ); + assert( y2[3] == 5 ); + + Deque!(int) y3 = y[2..4]; + assert( y3.length == 2 ); + assert( y3[0] == 3 ); + assert( y3[1] == 5 ); + + y3[0] = 10; + assert( y[2] == 10 ); + + Deque!(char[]) c; + c ~= "a"; + c ~= "a"; + c ~= "a"; + c ~= "a"; + assert( c.opIn("a") == c.head ); + assert( c.count("a") == 4 ); + for (int kk=3;kk<6000;kk++) { + c ~= toString(kk); + c ~= toString(kk+1); + char[] res = c.takeHead(); + if (kk > 10) { + assert( res == toString(kk/2) ); + } + } + + // test addAfter, addBefore and remove + Deque!(double) w; + for (k=0;k<20;k++) + w ~= k; + w.remove(w[10..15]); + assert( w.length == 15 ); + assert( w[10] == 15 ); + for (k=0;k<5;k++) + w.addHead(k); + w.remove(w[2..7]); + assert( w.length == 15 ); + Deque!(double) w3; + for (k=0;k<20;k++) + w3 ~= k; + w.addBefore(w[5..7],w3[10..15]); + assert( w.length == 20 ); + assert( w[0] == 4 ); + foreach( double d; w) { + version (MinTLVerboseUnittest) + printf(" %g",d); + } + version (MinTLVerboseUnittest) + printf("\n"); + assert( w[5] == 10 ); + + // test sorting + Deque!(int) s1,s2; + s1.add(40,300,-20,100,400,200); + s1.sort(); + s2.add(-20,40,100,200,300,400); + assert( s1 == s2 ); + + Deque!(double) s3; + for (k=0;k<1000;k++) { + s3 ~= 1.0*rand()/100000.0 - 500000.0; + } + s3.sort(); + for (k=0;k<999;k++) { + assert( s3[k] <= s3[k+1] ); + } + + version (MinTLVerboseUnittest) + printf("finished mintl.deque unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/hashaa.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,752 @@ +/** \file hashaa.d + * \brief A hash-based associative array that maintains elements in insertion order + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.7.1 + */ + +module mintl.hashaa; + +//debug = dHashAA; // can also pass at command line + +private { + import mintl.share; + import mintl.sorting; + import mintl.mem; +} + +/** \class HashAA + * \brief A hash-based associative array traversed in insertion order. + * + * A HashAA!(Key,Value) represents an associative array with keys of + * type Key and values of type Value that maintains the inserted items + * in a linked list sorted by insertion order. If <tt>key1</tt> is + * inserted into the array before <tt>key2</tt> then <tt>key1</tt> + * will appear before <tt>key2</tt> in foreach statements and in + * iterator traversals. + * + * The optional ReadOnly parameter HashAA!(Key,Value,ReadOnly) forbids + * operations that modify the container. The readonly() property returns + * a ReadOnly view of the container. + * + * The optional allocator parameter ArrayList!(Value,false,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct HashAA(Key,Value, bit ReadOnly = false, Alloc = GCAllocator) { + + alias HashAA ContainerType; + alias HashAA SliceType; + alias Value ValueType; + alias Key IndexType; + alias Key SortType; + alias ReadOnly isReadOnly; + + /** Get the kays in the array. The operation is O(n) where n is the number of + * elements in the array. + */ + Key[] keys() { + Key[] res; + if (head_ is null) return res; + res.length = length; + size_t n = 0; + foreach(Key k,Value v;*this) { + res[n++] = k; + } + return res; + } + + /** Get the values in the array. The operation is O(n) where n is + * the number of elements in the array. + */ + Value[] values() { + Value[] res; + if (head_ is null) return res; + res.length = length; + size_t n = 0; + foreach(Key k,Value v;*this) { + res[n++] = v; + } + return res; + } + + /** Property for the default value of the array when a key is missing. */ + void missing(Value val) { + if (data.length == 0) + initDataArray(); + data[0].val = val; + } + Value missing() { + if (data.length == 0) + return Value.init; + return data[0].val; + } + + /** Length of array. The operation is O(n) where n is the number of + * elements in the array. + */ + size_t length() { + if (head_ is null) return 0; + if (head_.prev is null && tail_.next is null) return dlength(); + Node* t = head_; + int n = 1; + while (t !is null && t !is tail_) { + t = t.next; + ++n; + } + return n; + } + + /** Return true if array is empty. */ + bool isEmpty() { + return head_ is null; + } + + private void initDataArray() { + size_t s = prime_list[0]+1; + static if (is(Alloc == GCAllocator)) { + data.length = s; + } else { + Node** p = cast(Node**)Alloc.malloc((Node*).sizeof*s); + data = p[0 .. s]; + } + data[0] = allocNode; + data[0].len = 0; + } + + private enum {InsertOnMiss, ThrowOnMiss, NullOnMiss} + + // helper functions for indexing. + private Node** getNode(Key key, int failureAction) { + Node* t; + TypeInfo ti = typeid(Key); + uint hash = ti.getHash(&key); + if (data.length == 0) { + switch (failureAction) { + case InsertOnMiss: + initDataArray(); + break; + case ThrowOnMiss: + GetActionThrow: + throw new IndexOutOfBoundsException("Key not in container"); + case NullOnMiss: + return null; + } + } + uint i = (hash % (data.length-1))+1; + Node**p = &data[i]; + while (*p !is null) { + if ((*p).hash == hash && ti.equals(&(*p).key,&key)) { + // found key + return p; + } + p = &(*p).nextHash; + } + if (failureAction == ThrowOnMiss) { + goto GetActionThrow; + } else if (failureAction == NullOnMiss) { + return null; + } + // lookup Node + *p = t = allocNode(); + t.hash = hash; + t.key = key; + if (head_ is null) { + head_ = t; + tail_ = t; + } else { + link(tail_,t); + tail_ = t; + } + data[0].len++; + static if (!ReadOnly) { + if (data[0].len > .75*data.length) { + this.rehash(); + p = getNode(key,NullOnMiss); + } + } + return p; + } + + /** Find the element with a given key and return a pointer to the + * value. If the key is not in the array null is returned or if + * throwOnMiss is true an exception is thrown. The target array can + * be a sub-array though the key may fall outside of the sub-array + * range. + */ + Value* get(Key key, bool throwOnMiss = false) { + Node** t = getNode(key,throwOnMiss ? ThrowOnMiss : NullOnMiss); + if (t) + return &(*t).val; + else + return null; + } + + /** Create a sub-array from key a to b (exclusive). */ + HashAA opSlice(Key a, Key b) { + HashAA res; + res.head_ = *getNode(a,ThrowOnMiss); + res.tail_ = (*getNode(b,ThrowOnMiss)).prev; // will at least have a in there + res.data = data; + return res; + } + + /** Create a sub-array from the first key in a to the last key in b (inclusive). */ + HashAA opSlice(HashAA a, HashAA b) { + HashAA res; + res.head_ = a.head_; + res.tail_ = b.tail_; + res.data = data; + return res; + } + + /** Get a ReadOnly view of the container */ + .HashAA!(Key,Value,true,Alloc) readonly() { + .HashAA!(Key,Value,true,Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get a read-write view of the container */ + .HashAA!(Key,Value,false,Alloc) readwrite() { + .HashAA!(Key,Value,false,Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + static if (ReadOnly) { + /** Duplicates the array. The operation is O(n) where n is length. */ + /* private bug + HashAA dup() { + .HashAA!(Key,Value,false) res; + res.data.length = data.length; + res.data[0] = cast(res.Node*)allocNode; + res.data[0].len = 0; + res.missing = missing; + foreach(Key k,Value v;*this) + res[k] = v; + return res.readonly; + } + */ + } else { + + /** Clear all contents. */ + void clear() { + static if (is(Alloc == GCAllocator)) { + } else { + foreach ( Node* t; data) { + while (t) { + Node* next = t.nextHash; + Alloc.gcFree(t); + t = next; + } + } + Alloc.free(data.ptr); + } + *this = HashAA.init; + } + + /** Duplicates the array. The operation is O(n) where n is length. */ + HashAA dup() { + HashAA res; + res.data.length = data.length; + res.data[0] = allocNode; + res.data[0].len = 0; + res.missing = missing; + foreach(Key k,Value v;*this) { + res[k] = v; + } + return res; + } + + /** Rehash the array. */ + HashAA rehash() { + uint k; + uint len = data.length; + if (len == 0) return *this; + for (k=0;k<prime_list.length;k++) { + if (prime_list[k] > len) + break; + } + Node* n = data[0]; + size_t s = prime_list[k]+1; + static if (is(Alloc == GCAllocator)) { + data = new Node*[s]; + } else { + Node** p = cast(Node**)Alloc.malloc((Node*).sizeof * s); + data = p[0 .. s]; + } + data[0] = n; + Node* t = head_; + while (t) { + uint i = (t.hash %(data.length-1))+1; + t.nextHash = data[i]; + data[i] = t; + t = t.next; + } + return *this; + } + + /** Find a key in the array and return a pointer to the associated value. + * Insert the key and initialize with Value.init if the key is not + * in the array. + */ + Value* put(Key key) { + debug (dHashAA) printf("put %d\n",key); + Node* t = *getNode(key, InsertOnMiss); + return &t.val; + } + + /** Store a value with a key, overwriting any previous value. The + * target array can be a sub-array though the key may fall outside of the + * sub-array range. + */ + void opIndexAssign(Value val, Key key) { + Node* t = *getNode(key, InsertOnMiss); + t.val = val; + } + + // helper for remove/take + private Node* takeHelper(Key key) { + Node** p = getNode(key,NullOnMiss); + if (!p) return null; + Node* n = *p; + if (n is head_) + head_ = n.next; + if (n is tail_) + tail_ = n.prev; + link(n.prev, n.next); + *p = n.nextHash; + return n; + } + + /** Remove a key from the array. The target array can be a sub-array though + * the key may fall outside of the sub-array range. + */ + void remove(Key key) { + Node* n = takeHelper(key); + if (n) { + static if (is(Alloc == GCAllocator)) { + static if (Node.sizeof < AllocBlockCutoff) { + n.next = data[0].next; + data[0].next = n; + n.prev = null; + n.val = Value.init; + n.key = Key.init; + } + } else { + Alloc.gcFree(n); + } + data[0].len--; + } + } + + /** Remove the value stored with the given key and return it, if present. + * If not present return the missing default. + */ + Value take(Key key) { + Node* n = takeHelper(key); + if (!n) return missing; + Value val = n.val; + static if (is(Alloc == GCAllocator)) { + static if (Node.sizeof <= AllocBlockCutoff) { + n.next = data[0].next; + data[0].next = n; + n.val = Value.init; + n.key = Key.init; + n.prev = null; + } + } else { + Alloc.gcFree(n); + } + data[0].len--; + return val; + } + + /** Remove a sub-array from the array. The operation is O(max(log(m),n)) + * where m is the size of the target array and n is the number of + * elements in the sub-array. + */ + void remove(HashAA subarray) { + if (subarray.head_ is subarray.tail_) { + remove(subarray.key); + } else { + Key[] keylist = subarray.keys; + foreach(Key key;keylist) + remove(key); + } + } + + mixin MAddAA!(HashAA); // mixin add function + + } // !ReadOnly + + private void link(Node* a, Node* b) { + if (a) a.next = b; + if (b) b.prev = a; + } + + // remove extra capacity + void trim() { + if (data.length > 0 && data[0]) + data[0].next = null; + } + + // Parameters for controlling block allocations + private const int AllocBlockSize = 10; // number of nodes in block + private const int AllocBlockCutoff = 96; // max node size to allow blocks + + private Node* allocNode() { + Node* p; + static if (is(Alloc == GCAllocator)) { + static if (Node.sizeof > AllocBlockCutoff) { + return new Node; + } else { + if (data[0] is null) return new Node; + p = data[0].next; + if (p) { + data[0].next = p.next; + p.next = null; + return p; + } + p = (new Node[AllocBlockSize]).ptr; + for (int k=1;k<AllocBlockSize-1;k++) + p[k].next = &p[k+1]; + data[0].next = &p[1]; + return &p[0]; + } + } else { + p = cast(Node*)Alloc.gcMalloc(Node.sizeof); + } + return p; + } + + /** Find the element with a given key and return the value. If the + * key is not in the map the default for the array is returned. The + * target array can be a sub-array though the key may fall outside + * of the sub-array range. + */ + Value opIndex(Key key) { + Value* t = get(key); + if (t) + return *t; + else + return missing; + } + + /** Returns the value of a one-item array. */ + Value value() { + if (head_ is null && tail_ is null) + return Value.init; + return head_.val; + } + + /** Returns the key of a one-item array. */ + Key key() { + if (head_ is null && tail_ is null) + return Key.init; + return head_.key; + } + + /** Return a one-item slice of the head (oldest insertion). */ + HashAA head() { + HashAA res = *this; + res.tail_ = res.head_; + return res; + } + + /** Return a one-item slice of the tail (more recent insertion). */ + HashAA tail() { + HashAA res = *this; + res.head_ = res.tail_; + return res; + } + + /** Move a slice by n. By default moves to the next item. */ + void next(int n = 1, int end = 0) { + void doNext(inout Node* node, int m) { + while (m--) + node = node.next; + } + void doPrev(inout Node* node, int m) { + while (m--) + node = node.prev; + } + if (n > 0) { + if (end >= 0) + doNext(tail_,n); + if (end <= 0) + doNext(head_,n); + } else { + n = -n; + if (end >= 0) + doPrev(tail_,n); + if (end <= 0) + doPrev(head_,n); + } + } + + /** Test for equality of two arrays. The operation is O(n) where n + * is length of the array. + */ + int opEquals(HashAA c) { + Node* i = head_; + Node* j = c.head_; + Node* end = tail_; + Node* cend = c.tail_; + TypeInfo ti_k = typeid(Key); + TypeInfo ti_v = typeid(Value); + int do_test(Node*p1,Node*p2) { + if (!ti_k.equals(&p1.key,&p2.key)) + return 0; + if (!ti_v.equals(&p1.val,&p2.val)) + return 0; + return 1; + } + while (i !is null && j !is null) { + if (!do_test(i,j)) + return 0; + if (i is end || j is cend) { + return (i is end && j is cend); + } + i = i.next; + j = j.next; + } + return (i is null && j is null); + } + + /** Test if a key is in the array. The target array can be a sub-array + * but the key may fall outside of the sub-array range. + */ + bool contains(Key key) { + return get(key) !is null; + } + + /** Test if a key is in the array and set value if it is. */ + bool contains(Key key,out Value value) { + Value* node = get(key); + if (node) + value = *node; + return node !is null; + } + + /** Iterate over the array calling delegate to perform an action. A + * one-element sub-array is passed to the delegate. + */ + int opApplyNoKeyStep(int delegate(inout Value val) dg, int step = 1) { + int dg2(inout HashAA itr) { + Value value = itr.value; + return dg(value); + } + return opApplyIterStep(&dg2,step); + } + + /** Iterate over the array calling delegate to perform an action. A + * one-element sub-array is passed to the delegate. + */ + int opApplyWithKeyStep(int delegate(inout Key key, inout Value val) dg, + int step = 1) { + int dg2(inout HashAA itr) { + Key key = itr.key; + Value value = itr.value; + return dg(key,value); + } + return opApplyIterStep(&dg2,step); + } + + /** Iterate over the array calling delegate to perform an action. A + * one-element sub-array is passed to the delegate. + */ + int opApplyIterStep(int delegate(inout HashAA itr) dg,int step=1) { + int res = 0; + HashAA itr; + Node* x = step>0?head_:tail_; + Node* end = step>0?tail_:head_; + while (x !is null) { + itr.head_ = itr.tail_ = x; + res = dg(itr); + if (res || x is end) return res; + x = step>0?x.next:x.prev; + } + return res; + } + + /** Iterate backwards over the array (from last to first key). The target + * array can be a sub-array. This should only be called as the + * iteration parameter in a <tt>foreach</tt> statement + */ + LAReverseIter!(Key,Value,ReadOnly,Alloc) backwards() { + LAReverseIter!(Key,Value,ReadOnly,Alloc) res; + res.list = this; + return res; + } + + /** Helper functions for opApply */ + mixin MOpApplyImpl!(HashAA); + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + alias opApplyIter opApply; + + Node* getHead(){return head_;} + Node* getTail(){return tail_;} + mixin MSequentialSort!(HashAA, getHead,getTail); + void sort(int delegate(Key*a, Key*b) cmp = null) { + Node* newhead, newtail; + dosort(newhead,newtail,cmp); + head_ = newhead; + tail_ = newtail; + } + + private { + struct Node { + Node* next, prev, nextHash; + union { + uint len; + uint hash; + } + Key key; + Value val; + Key* sortLookup(){return &key;} + } + Node*[] data; + Node* head_, tail_; + } + + private uint dlength() { return data[0].len; } + + // size primes from aaA.d and planetmath.org + private static uint[] prime_list = + [97u, 389u, 1543u, 6151u, + 24593u, 98317u, 393241u, 1572869u, + 6291469u, 25165843u, 100663319u, 402653189u, + 1610612741u, 4294967291u + ]; +} + +// internal helper struct for backwards iteration +struct LAReverseIter(Key,Value,bit ReadOnly,Alloc) { + mixin MReverseImpl!(HashAA!(Key,Value,ReadOnly,Alloc)); +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; +version (MinTLUnittest) { + private import std.random; + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.hashaa unittest\n"); + + HashAA!(int,int) m; + m[4] = 100; + m[-10] = 200; + m[17] = 300; + assert( m.length == 3 ); + assert( m[-10] == 200 ); + + HashAA!(int,int) mm; + mm.add(4,100, -10,200, 17,300); + assert( m == mm ); + + // test foreach + int[] res; + res.length = 3; + int n=0; + foreach(int val; m) { + res[n++] = val; + } + assert( res[0] == 100 ); + assert( res[1] == 200 ); + assert( res[2] == 300 ); + + // test removing an item + m.remove(-10); + n = 0; + foreach(int val; m) { + res[n++] = val; + } + assert( res[0] == 100 ); + assert( res[1] == 300 ); + + // test assigning to an item already in array + m[22] = 400; + m[17] = 500; + n = 0; + foreach(int val; m) { + res[n++] = val; + } + assert( res[0] == 100 ); + assert( res[1] == 500 ); + assert( res[2] == 400 ); + + // test backwards foreach + n = 0; + foreach(int k,int val; m.backwards()) { + res[n++] = val; + } + assert( res[0] == 400 ); + assert( res[1] == 500 ); + assert( res[2] == 100 ); + + // test slicing + HashAA!(int, int) m2 = + HashAA!(int,int).make(400,4,100,1,500,5,300,3, + 200,2,600,6); + HashAA!(int, int) m3; + m3 = m2[500 .. 600]; + assert( m3.length == 3 ); + n = 0; + foreach(int k,int val; m3) { + res[n++] = val; + } + assert( res[0] == 5 ); + assert( res[1] == 3 ); + assert( res[2] == 2 ); + + // test keys + int[] keys = m3.keys; + assert( keys[0] == 500 ); + assert( keys[1] == 300 ); + assert( keys[2] == 200 ); + + // test rehash + for (int k=0; k<1000; k++) + m3[k] = k; + HashAA!(int,int) m4 = m3.rehash; + assert( m4 == m3 ); + + // test simple sorting + HashAA!(int,int) s1,s12; + s1.add(40,1,300,2,-20,3,100,4,400,5,200,6); + s12 = s1.dup; + s1.sort(); + assert( s1 == HashAA!(int,int).make(-20,3,40,1,100,4,200,6,300,2,400,5) ); + // sort a slice in-place + HashAA!(int,int) slice1 = s12[300 .. 200]; + slice1.sort(); + assert( s12 == HashAA!(int,int).make(40,1,-20,3,100,4,300,2,400,5,200,6)); + + // test a large sort with default order + HashAA!(double,int) s3; + for (int k=0;k<1000;k++) { + s3[1.0*rand()/100000.0 - 500000.0] = k; + } + HashAA!(double,int) s4 = s3.dup; + s3.sort(); + double[] keys2 = s3.keys; + for (int k=0;k<999;k++) { + assert( keys2[k] <= keys2[k+1] ); + } + // test a large sort with custom order + int cmp(double*x,double*y){return *x>*y?-1:*x==*y?0:1;} + s4.sort(&cmp); + keys2 = s4.keys; + for (int k=0;k<999;k++) { + assert( keys2[k] >= keys2[k+1] ); + } + + version (MinTLVerboseUnittest) + printf("finished mintl.hashaa unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/index.html Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,1781 @@ +<HTML> <head> <TITLE>Minimal Template Library for D</TITLE> </head> +<body> +<h1>MinTL</h1> +MinTL is a "minimal template library" of containers for the D +programming language. For more info about D see <a +href="http://www.digitalmars.com/d/">DigitalMars D home page</a>. The +downloads are the <a href="mintl.zip">Core Library</a> and +the <a href="mintlc.zip">MinTL Concurrent Library</a> +(<a href="conc.html">mintlc web page</a>), which includes +a dependent <a href="locks.html">Synchronization Library</a>. +The current version is 2.7.1. +<p> +This library is in the public domain. +Written by <a href="http://home.comcast.net/~benhinkle"> +Ben Hinkle</a>, 2004. +Email comments and bug reports to ben.hinkle@gmail.com +<p> +<h3> Contents </h3> +<a href="#Overview">Overview</a></br> +<a href="#Lists">List Containers</a></br> +<a href="#Assoc">Associative Containers</a></br> +<a href="#Slicing">Slicing</a></br> +<a href="#Foreach">Foreach traversals</a></br> +<a href="#Sorting">Sorting</a></br> +<a href="#Allocators">Allocators</a></br> +<a href="#unmod">Unmodifiable Containers</a></br> +<a href="#Examples">Examples</a></br> +<a href="#API">API Reference by module</a></br> + +<a name="Overview"></a> +<h3> Overview </h3> + +The philosophy of the library is to be as simple and minimal as +possible: +<list> + +<li> The design is simple by keeping the number of classes to a +minimum and reusing concepts from builtin dynamic and associative +arrays. For example a slice of a container has the same type as the +container, just as the slice of a dynamic array is another dynamic +array. + +<li> +The memory footprint and the garbage generation is kept to a minimum +by relying on structs instead of classes and by implementing slicing +and traversals without allocating dynamic memory. Some containers +also recycle nodes. For example when the head or tail of a list is removed +the node is retained and reused the next time an item is added +to the head or tail. Optional Allocator parameters allow custom memory +management. + +<li> Closely related data structures share common naming conventions +and adapters reuse fundamental data structures like arrays, linked-lists +and sorted associative arrays for stacks, queues and sets. +Performance can be tuned for different uses by either choose +between several variations (eg. a linked list, a circular array or a +deque) or by supplying optional constructor arguments. + +</list> +<p> + +MinTL has the following containers +<p> +<table border="1"> +<caption>List containers</caption> +<tr> +<th>container <th> implementation <th> file <th> brief</th> +</tr> +<tr> +<td> <a href="#list">List</a> +<td> doubly linked list +<td> list.d +<td> sortable linked list with "previous" and "next" pointers +</td> +</tr> +<tr> +<td> <a href="#clist">CircularList</a> +<td> circular doubly linked list +<td> list.d +<td> doubly linked list where the head and tail are linked +</td> +</tr> +<tr> +<td> <a href="#slist">SList</a> +<td> singly linked list +<td> slist.d +<td> linked list with only "next" pointers +</td> +</tr> +<tr> +<td> <a href="#cslist">CircularSList</a> +<td> circular singly linked list +<td> slist.d +<td> singly linked list where the tail points to the head +</td> +</tr> +<tr> +<td> <a href="#arraylist">ArrayList</a> +<td> circular array +<td> arraylist.d +<td> sortable list backed by a resizable circular array +</td> +<tr> +<td> <a href="#deque">Deque</a> +<td> circular block-allocated array +<td> deque.d +<td> list backed by a resizable block-allocated circular array +</td> +<tr> +<td> <a href="#arrayheap">ArrayHeap</a> +<td> heap +<td> arrayheap.d +<td> complete binary tree backed by an array +</td> +<tr> +<td> <a href="#stack">Stack</a> +<td> adapter +<td> stack.d +<td> adapts a list container to be a stack +</td> +<tr> +<td> <a href="#arraystack">ArrayStack</a> +<td> ArrayList +<td> stack.d +<td> wraps an ArrayList with the stack adapter +</td> +<tr> +<td> <a href="#queue">Queue</a> +<td> adapter +<td> queue.d +<td> adapts a list container to be a queue +</td> +<tr> +<td> <a href="#arrayqueue">ArrayQueue</a> +<td> ArrayList +<td> queue.d +<td> wraps an ArrayList with the queue adapter +</td> +<tr> +<td> <a href="#pqueue">PriorityQueue</a> +<td> ArrayHeap +<td> queue.d +<td> wraps an ArrayHeap with the queue adapter +</td> +</table> +<p> +<table border="1"> +<caption>Associative containers</caption> +<tr> +<th>container <th> implementation <th> file <th> brief</th> +<tr> +<td> <a href="#hashaa">HashAA</a> +<td> linked hash table +<td> hashaa.d +<td> sortable associative array with nodes ordered by insertion order +</td> +<tr> +<td> <a href="#sortedaa">SortedAA </a> +<td> red-black tree +<td> sortedaa.d +<td> sorted associative array </td> +</tr> +<tr> +<td> <a href="#set">Set</a> +<td> adapter +<td> set.d +<td> adapts an associative container to be a set +</td> +<tr> +<td> <a href="#sortedset">SortedSet</a> +<td> SortedAA +<td> set.d +<td> wraps a SortedAA with the set adapter +</td> +<tr> +<td> <a href="#multiset">MultiSet</a> +<td> adapter +<td> set.d +<td> adapts an associative container to be a set with repeats +</td> +<tr> +<td> <a href="#sortedmultiset">SortedMultiSet</a> +<td> SortedAA +<td> set.d +<td> wraps a SortedAA with the multi-set adapter +</td> +<tr> +<td> <a href="#multiaa">MultiAA</a> +<td> adapter +<td> multiaa.d +<td> adapts an associative container to hold repeated keys +</td> +<tr> +<td> <a href="#sortedmultiaa">SortedMultiAA</a> +<td> SortedAA +<td> multiaa.d +<td> wraps a SortedAA with the multi-aa adapter +</td> +</table> +<p> + +The module mintl.array defines helper functions for builtin dynamic +and associative arrays. + +<a name="BuildInstall"> +<h3> Build and Install</h3> +To use MinTL first unpack the library in a directory on the D compiler +search path. There are pre-built debug and non-debug versions of the library. +To enable flexible <tt>add()</tt> datatype support uncomment the +<tt>version=WithBox</tt> statement in mintl.share and recompile MinTL. +To use std.boxer in a debug build you must also rebuild phobos with +debugging. +If you wish to rebuild MinTL enter the mintl directory and type +<tt>make -f win32.mak</tt> or <tt>make -f linux.mak</tt>. +In your source code <tt>import</tt> the desired modules and compile each +container used and the mintl static library into the application. +If a concurrent container is needed download the <tt>mintlc</tt> +sub-package and link that library and +the <a href="locks.html">Locks</a> library into the +application. For example on Linux to compile the program <tt>app.d</tt> +<pre> +import mintl.list; + +int main() { + List!(int) list = List!(int).make(10,20,30); + return 0; +} +</pre> +run in the directory above mintl the command +<pre> + dmd app.d mintl/libmintl_debug.a +</pre> +to build with asserts or +<pre> + dmd app.d -release mintl/libmintl.a +</pre> +to build without asserts. +On Windows run +<pre> + dmd app.d mintl\mintl_debug.lib +</pre> +or +<pre> + dmd app.d -release mintl\mintl.lib +</pre> +If the mintl directory is not in the current directory then use the -I flag +to add it to the search path +<pre> + dmd app.d -Ipath_to_mintl path_to_mintl/mintl/libmintl.a +</pre> +or modify the dmd\bin\sc.ini file (on Windows) or dmd.conf (on Linux) +to include the paths to mintl. For example if MinTL is unpacked in the +directory C:\d on Windows then sc.ini should be modified to include C:\d +in the include path and C:\d\mintl on the library search path: +<pre> +LIB="%@P%\..\lib";\dm\lib;C:\d\mintl +DFLAGS="-I%@P%\..\src\phobos";C:\d +</pre> +On Linux the static library can be put in a standard location like +/usr/lib if desired. + +<a name="Lists"> +<h3> List Containers </h3> + +The list containers <tt>List</tt>, <tt>SList</tt>, +<tt>CircularList</tt>, <tt>CircularSList</tt>, <tt>ArrayList</tt>, +<tt>Deque</tt>, <tt>ArrayHeap</tt> and the concurrent queues +and stacks share a naming convention +for adding and removing items. The <i>head</i> is the +first item in the container and the <i>tail</i> is the last item. +All list containers support constant-time access to the head +and tail. +The speed of accessing the <tt>length</tt> property depends on the +container. Array-based containers have constant-time access to length, +the linked-list containers have constant-time unless an operation +modifies the list to have unknown length, in which case the next +time the length is computed it is cached again. The singly-linked +lists have linear-time length access and it is not cached. +<p> +Some containers maintain nodes past the head and tail +of the list as extra capacity. To trim off any extra capacity +most containers support a <tt>trim</tt> function. +<p> +The circular lists +<tt>CircularList</tt> and <tt>CircularSList</tt> are the same as the non-circular +versions except that slicing a circular list returns a non-circular +list and node are not reused when adding or removing items. However +circular lists are useful when the objects being modeled do not have a +natural unique definition of a head or tail. +<p> +An <tt>ArrayList</tt> can also be used as a dynamic array with +managed capacity. Set the <tt>capacity</tt> property or allocate an array +with the desired capacity +and leave the head of the arraylist at 0. The arraylist will +automatically grow the capacity as required. + +<p> To add items to a container call <tt>add</tt> with any number of +items. For example +<pre> + List!(int) x,y,z; + y.add(10,20,30); + z.add(50,60,70); + x = y; x ~= 40; x ~= z; x ~= 80; +</pre> +results in the following linked list +<pre> + x[0],y[0] y[2] z[0] z[2] x[7] + 10 <-> 20 <-> 30 <-> 40 <-> 50 <-> 60 <-> 70 <-> 80 +</pre> +To add a single item or list call <tt>addTail</tt> or use +one of the concatenation operators. Some containers also support adding +items or lists at the head using <tt>addHead</tt> or at a position +in the interior of the list using <tt>addBefore</tt> or <tt>addAfter</tt>. +To create a list in an expression use the static <tt>make</tt> function +For example, +<pre> + List!(int) x; + x = List!(int).make(10,20,30) ~ List!(int).make(50,60,70); +</pre> + +<p> To remove items call one of the <tt>take</tt> or +<tt>remove</tt> functions. +All list containers support <tt>removeHead</tt> to remove +the head of the list and <tt>takeHead</tt> to remove and return +the stored value, if any. +Some containers also support <tt>takeTail</tt> and +<tt>removeTail</tt> to remove the tail and <tt>remove</tt> +to remove a slice. + +<p> +Stacks and queues are implemented as adapters of a list +container. By default they use a Deque as the backing container. +Stacks define aliases <tt>push</tt> for <tt>add</tt> +and <tt>pop</tt> for <tt>takeTail</tt>. Queues define an alias +<tt>take</tt> for <tt>takeHead</tt> (the function <tt>add</tt> is used +to add to the end of a queue). For example, +<pre> + Stack!(char[]) x; + x.push("first","second"); + assert( x.pop == "second" ); + assert( x.pop == "first" ); + + Queue!(char[]) x; + x.add("first","second"); + assert( x.take == "first" ); + assert( x.take == "second" ); +</pre> +A <tt>PriorityQueue</tt> is an ArrayHeap wrapped with the Queue adapter. To +set a custom comparison function access the <tt>impl</tt> property of the +adapter: +<pre> + PriorityQueue!(char[]) x; + x.impl.compareFcn = &fcn; + x.add("first","second"); +</pre> +<p> +The following table outlines the advantages and disadvantages of +each list container +<table border="1"> +<tr> +<th>container <th> advantages <th> disadvantages </th> +</tr> +<tr> +<td> List +<td> O(1) insertion at head/tail or before/after slices +<td> O(n) access to middle of list +</td> +</tr> +<tr> +<td> SList +<td> O(1) insertion at head/tail or after slices; less overhead +than <tt>List</tt> +<td> O(n) access to middle or near end of list +</td> +</tr> +<tr> +<td> ArrayList +<td> O(1) insertion at head/tail. O(1) access to any index +<td> O(n) insertion in middle +</td> +</tr> +<tr> +<td> Deque +<td> O(1) insertion at head/tail. O(1) access to any index. +Block allocated. +<td> O(n) insertion in middle; non-contiguous storage +</td> +</tr> +<tr> +<td> ArrayHeap +<td> maintains items in semi-sorted order; O(log(n)) add/remove. +<td> only allows <tt>addTail</tt> and <tt>takeHead</tt> +</td> +</tr> +</table> + +<a name="Assoc"> +<h3> Associative Containers </h3> + +The associative containers <tt>SortedAA</tt>,<tt>HashAA</tt>, and +<tt>ConcurrentAA</tt> are similar to builtin associative arrays but +with extra capabilities. The <tt>SortedAA</tt> maintains the keys in +some specific order as determined by a comparison function. By +default the keys are ordered by the type's default comparison +function. To specify a custom comparison function assign to the +<tt>compareFcn</tt> property. The <tt>HashAA</tt> maintains the keys +in insertion order, meaning if an indexing expression using key +<tt>x</tt> is evaluated before an indexing expression using key +<tt>y</tt> then <tt>x</tt> is traversed before <tt>y</tt> in +<tt>foreach</tt> traversals. Assigning to a key already in the array +does not change the insertion order. The other associative array +properties <tt>dup</tt>, <tt>length</tt>, <tt>keys</tt> and +<tt>values</tt> are also implemented. + +<p> +Elements are inserted or modified using the <tt>add</tt> function or +using indexing lvalue expressions +and retrieved using indexing rvalue expressions. To test if a key is in +the array use the overloaded <tt>contains</tt> functions. +An indexing expression that is not an assignment will return a +default missing value. The missing value defaults to Value.init +but can be set by assigning to the <tt>missing</tt> property. +The functions <tt>get</tt> and <tt>put</tt> allow more flexibility in handling +missing keys by allowing the user to either return null, throw or +insert. +To remove an item call the <tt>remove</tt> function. Both <tt>HashAA</tt> +and <tt>SortedAA</tt> maintain a freelist of removed nodes for future +reuse. To release the freelist for garbage collection call <tt>trim</tt>. +<p> +For example to define a sorted associative +array with three entries associating "first" with 10, "second" with +20 and "third" with 30 type +<pre> + SortedAA!(int,char[]) x; + x.add(10,"first", 20,"second", 30,"third"); +</pre> +or equivalently, +<pre> + SortedAA!(int,char[]) x; + x[10] = "first"; + x[20] = "second"; + x[30] = "third"; +</pre> +To create an associative array in an expression use the static <tt>make</tt> function +For example, +<pre> + foo(SortedAA!(int,char[]).make(10,"first",20,"second",30,"third")); +</pre> + +<p> +The number of elements in an associative container is computed by +the <tt>length</tt> property. + +<p> +Sets and multi-associative-arrays are implemented as adapters of an +associative container. +By default sets use a HashAA as the backing container. +Use <tt>add</tt> +to add items to a set and use an indexing expression +to check if an item is in the set. For example, +<pre> + Set!(char[]) x; + x.add("first","second","third"); + assert( x["first"] ); + assert( x["second"] ); + assert( x["third"] ); + assert( !x["fourth"] ); + + MultiSet!(char[]) mx; + xm.add("first","second","first","third","second"); + xm.remove("first"); + assert( x["first"] ); + + MultiAA!(int,char[]) y; + y.add(10,"first",20,"second",10,"third"); + assert( y[10].length == 2 ); +</pre> + +<a name="Slicing"> +<h3> Slicing </h3> + +In general slicing a container behaves like slicing a dynamic +array. For example, slicing between two indices in a List creates +another List with the head at the first index and the tail at the +element before the second index. The contents of the slice is shared +with the original list so assigning new values to elements in the list +will be visible in the oringal list. The resulting slice, or +sub-list, can be traversed using "foreach", duplicated, indexed into, +etc just like a slice of a dynamic array can be treated as a +"first-class" dyamic array. The following diagram illustrates the +relationships for <tt>x</tt> and <tt>y</tt> if +<tt>x</tt> is a list with 6 items and +<tt>y</tt> is the slice <tt>x[2..4]</tt>.<br> + +<pre> + x[0] y[0] y[1] x[5] + 0 <-> 1 <-> 2 <-> 3 <-> 4 <-> 5 +</pre> +Executing <tt>z = y.dup</tt> would result in +<pre> + z[0] z[1] + 2 <-> 3 +</pre> +<p> +One should be careful when resizing or writing to a slice. For example +do not (in general) append or prepend item to a sub-list using the addTail or addHead +functions or remove items using removeTail and removeHead unless the +original list is no longer needed. To insert an item or list at a +certain location create a slice with the head at the desired location +and call <tt>addBefore</tt> or create a slice with the tail at the desired +location and call <tt>addAfter</tt>. To remove a portion of a list create a +slice and call <tt>remove</tt>. Again one should always insert and remove from +the original list otherwise any variable referring to the original list +will become out of sync. +<p> +A sub-list can be moved towards the head or tail +of the original list by calling the <tt>next</tt> function. +This allows a sub-list to traverse up and down the original list efficiently. +Continuing the example from above, executing <tt>y.next(-1)</tt> would result +in +<pre> + x[0] y[0] y[1] x[5] + 0 <-> 1 <-> 2 <-> 3 <-> 4 <-> 5 +</pre> +and then executing <tt>x.remove(y)</tt> would result in +<pre> + x[0] x[3] + 0 <-> 3 <-> 4 <-> 5 + + y[0] y[1] + 1 <-> 2 +</pre> +The performance of inserting and removing from the interior +of an ArrayList or Deque can be significantly worse than that of a List +if the slices are near the middle of the container. + +<p> +The SortedAA slicing behavior is designed to chose slices without modifying +the underlying container. The functions <tt>from</tt> and <tt>to</tt> +can find a one-item slice starting from or up to a given key. +For example if <tt>words</tt> is a sorted associative array indexed by +<tt>char[]</tt> then +<pre> + words["a" .. "b"] +</pre> +or, equivalently, +<pre> + words[words.from("a") .. words.to("b")] +</pre> +is a slice containing all the items with keys that start with the character "a" +even if the strings "a" and "b" aren't in the container. + +<a name="Foreach"> +<h3> Foreach </h3> +Containers and slices of containers can be traversal in +<tt>foreach</tt> statements +by value, key-value pairs and one-element slices. Some containers +support moving a slice up and down the container. For these containers +a slice can be used for the same purposes as an iterator in C++ or +Java. +<p> +Each container also supports backwards traversals by calling +<tt>backwards</tt> in a foreach statement. For example, +<pre> + List!(int) x; + ... + foreach( int val; x.backwards() ) + printf("%d ",val); +</pre> +will print out the contents of x from tail to head. +Backwards traversals do not allocate any dynamic memory. + +<a name="Sorting"> +<h3> Sorting </h3> +MinTL supports sorting containers in two forms. The <tt>SortedAA</tt> +is a sorted associative container that maintains its elements in a +given sorted order at all times. The comparison delegate +used to determine the element order must be specified +before the first element is insert or the key's default comparison +function will be used and it cannot be changed during the lifetime +of the container. The <tt>SortedSet</tt> +and <tt>SortedMultiAA</tt> are derived from <tt>SortedAA</tt> and +have the same sorting semantics. +<p> +The other form of sorting is through calling the <tt>sort</tt> +methods of the containers <tt>ArrayList</tt>, <tt>Deque</tt>, <tt>List</tt>, +and <tt>HashAA</tt> or by using the <tt>sort</tt> template in +<tt>mintl.array</tt> to sort a dynamic array. These sort methods +take an optional comparison delegate. The default comparison +delegate is the sorting type's default comparison function. For +the list-like containers the sorting type is the value type of +the container. For <tt>HashAA</tt> the sorting type is the key +type. Sorting is done in-place and slices are preserved relative +to their surrounding container. For example, the following code +creates a short linked list, sorts and slice and compares it +with the expected end result: +<pre> + List!(int) list; + list.add(40,300,-20,100,400,200); + list[1 .. 5].sort(); // sort a slice in-place + assert( list == List!(int).make(40,-20,100,300,400,200)); +</pre> +<p> +Sorting a container does not change the behavior of adding new elements. +The sorting operation is performed once and the comparison function +is not remembered by the container or used in any other way. For example +sorting a <tt>HashAA</tt> which was previously maintained in insertion +order will sort the elements but adding any new elements will add them +to the end of the traversal order in insertion order again. + +<a name="Allocators"> +<h3> Allocators </h3> +Most containers accept an optional <tt>Allocator</tt> parameter to +customize memory management. The default allocator is the +<tt>GCAllocator</tt> which indicates to the container that the garbage +collector should be used for all allocations. If a custom allocator +is supplied the container will call the memory management functions in +the allocator to allocate and free memory. Users who supply a +non-garbage-collecting allocator need to call <tt>clear</tt> when done +with a container so that the memory can be released. +<p> + +An allocator is a type containing 8 symbols malloc, calloc, realloc, +free and the corresponding GC-aware versions gcMalloc, gcCalloc, +gcRealloc and gcFree. Containers will call the GC-aware functions on +blocks that may hold roots and otherwise will call the regular +functions. Allocators are expected to throw an <tt>OutOfMemory</tt> +exception if the allocation fails. +<p> + +The two predefined allocators <tt>Malloc</tt> and <tt>MallocNoRoots</tt> use +std.c.stdlib.malloc to perform allocations. The MallocNoRoots ignores +any requests by the container to register roots with the GC. The +MallocNoRoots allocator should only be used with containers that the +user knows will never contain any roots. For example, +<pre> + ArrayList!(int,MallocNoRoots) x; + x.add(10,20,30); + ... + x.clear(); +</pre> + +<a name="unmod"></a> +<h3> Unmodifiable Containers </h3> +The <tt>ReadOnly</tt> template parameter makes a container +unmodifiable. A read-only container does not have operations that add or +remove or change elements. However the underlying data is shared with +the original modifiable container so a read-only container only +guarantees that the elements will not be modified by that particular +view of the container. +A slice of a read-only container is also read-only. +The <tt>readonly</tt> property creates a read-only view of a container. +The <tt>readwrite</tt> property creates a read-write view. +For example +<pre> + void foo(List!(int,ReadOnly) y) { ... } + List!(int) x; + x.add(10,20,30); + foo(x.readonly); +</pre> + +<a name="Examples"> +<h3> Examples </h3> +The source files have examples and unittests +that one can use to get a feel for the behavior. The following example +walks through some uses of MinTL: +Create a list of the integers 0 through 10 +<pre> + List!(int) x; + x.add(0,1,2,3,4,5,6,7,8,9,10); +</pre> +and print out the items 5 though 8 +<pre> + foreach(int val; x[5..9]) + printf("%d ",val); +</pre> +to output +<pre> + 5 6 7 8 +</pre> +Assigning <tt>x</tt> to another variable <tt>y</tt> shares +the underlying list contents, so assigning through <tt>y</tt> +is reflected in <tt>x</tt>: +<pre> + List!(int) y = x; + y[0] = 100; + assert( x[0] == 100 ); +</pre> +When passing a container to a function that could add or remove +elements from the container be sure to use "inout" parameters +to be sure the original variable in the calling frame is +properly updated. +<p> + +<a name="API"> +<h3> API Reference</h3> +This section lists the public structs and functions in MinTL without +detailed explanation. For more information see the documentation before +the function or struct in the source file. Template functions are +written as +<pre> return-type fcn-name!(tmpl-param,...)(arg1, arg2,...); +</pre> +to mimic how it would appear in user code. The API is organized by +module:<br> +<dl> +<dt><a href="#array">mintl.array</a> +<dt><a href="#arrayheap">mintl.arrayheap</a> +<dt><a href="#arraylist">mintl.arraylist</a> +<dt><a href="#deque">mintl.deque</a> +<dt><a href="#hashaa">mintl.hashaa</a> +<dt><a href="#list">mintl.list</a> +<dt><a href="#mem">mintl.mem</a> +<dt><a href="#multiaa">mintl.multiaa</a> +<dt><a href="#queue">mintl.queue</a> +<dt><a href="#set">mintl.set</a> +<dt><a href="#share">mintl.share</a> +<dt><a href="#slist">mintl.slist</a> +<dt><a href="#sortedaa">mintl.sortedaa</a> +<dt><a href="#stack">mintl.stack</a> +</dl> + +<a name="array"></a> +<h4>mintl.array</h4> +<dl> +<dt>void <b>reserve</b>!(Value[])(inout Value[] x, size_t n); +<dd>Reserve capacity for a dynamic array +<dt>DArrayReverseIter <b>backwards</b>!(Value[])(Value[] x); +<dd>Reverse dynamic array "foreach" traversal +<dt>void <b>sort</b>!(Value[])(Value[] data, int delegate(Value* x, Value* y) cmp = null); +<dd>Sort the array in increasing order as defined by the given comparison +delegate. If no delegate is supplied the default comparison function of the +Value type is used. +</dl> + +<a name="arrayheap"></a> +<h4>mintl.arrayheap</h4> +<dl> +<dt>struct <b>ArrayHeap</b>(Value, Alloc = GCAllocator) +<dd>A heap (complete binary tree) backed by an array. x[n] is greater than +or equal to x[2*n+1] and x[2*n+2]. x[0] is the greatest item. +An optional allocator can customize memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>Value[] <b>data</b>; +<dd>Backing array +<dt>size_t <b>length</b>; +<dd>Return length of heap +<dt>alias int delegate(Value* a, Value* b) <b>CompareFcn</b>; +<dd>Signature of comparison functions +<dt>void <b>compareFcn</b>(CompareFcn cmp); +<dd>Set the comparison function +<dt>bool <b>isEmpty</b> +<dd>Return true if container is empty +<dt>Value <b>opIndex</b>(size_t n); +<dd>Return nth item where the head is item 0. +<dt>void <b>opIndexAssign</b>(Value val, size_t n); +<dd>Assign to the nth item +<dt>Value[] <b>values</b>; +<dd>Get heap contents as dynamic array slice of backing array +<dt>void <b>add</b>(...); +<dd>Add to heap +<dt>void <b>vadd</b>(TypeInfo[] ti, void* argptr); +<dd>Add to heap using va_arg inpurs +<dt>void <b>addTail</b>(Value v); +<dd>Add to heap +<dt>Value <b>takeHead</b>(); +<dd>Remove and return first item (greatest item) +<dt>void <b>removeHead</b>(); +<dd>Remove first item (greatest item) +<dt>Value* <b>lookup</b>(size_t n); +<dd>Return a pointer to the nth item +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal by values +<dt>int <b>opApply</b>(int delegate(inout size_t n, inout Value x) dg); +<dd>Foreach traversal by index-value pairs +<dt>ArrayHeap <b>dup</b>; +<dd>Duplicate array heap by duplicating backing array +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>int <b>opEquals</b>(ArrayHeap c); +<dd>Test heap equality +<dt>alias ArrayHeap <b>ContainerType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dd>Aliases for container types +</dl> +</dl> + +<a name="arraylist"></a> +<h4>mintl.arraylist</h4> +<dl> +<dt>struct <b>ArrayList</b>(Value, bit ReadOnly = false, Alloc = GCAllocator) +<dd>A list backed by an array. An ArrayList can also be used as +a dynamic array with managed capacity. The backing array is resized +when more space is required. +Compile with version=MinTLNoIndexChecking to disable bounds checking. +The optional ReadOnly parameter disallows container modifications. +The optional allocator customizes memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>Value[] <b>data</b>; +<dd>Backing array +<dt>mixin <b>MListCat</b>(ArrayList) +<dd>Mixin <a href="#listcat">list catenation</a>. +<dt>mixin <b>MListAlgo</b>(this,ArrayList) +<dd>Mixin <a href="#listalgo">list algorithms</a>. +<dt><b>MListCommon</b>(ArrayList) +<dd>Implement common <a href="#listcomm">list members</a>. +<dt>size_t <b>length</b> +<dd>Read/write property to return or set the length of the list. +<dt>size_t <b>capacity</b> +<dd>Read/write property for the minimum capacity of the backing array. The +capacity is the maximum number of elements the array can hold without +requiring a reallocation of the backing array. +<dt>Value[] <b>array</b>; +<dd>Get list contents as dynamic array slice of the backing array assuming +the list is contiguous. +<dt>ArrayListReverseIter!(Value) <b>backwards</b>(); +<dd>Backwards traversal for "foreach" +<dt>ArrayList!(Value,true,Alloc) <b>readonly</b>; +<dd>Property that returns a read-only view of the container. +<dt>ArrayList!(Value,false,Alloc) <b>readwrite</b>; +<dd>Property that returns a read-write view of the container. +<dt>void <b>sort</b>(int delegate(Value* x, Value* y) cmp = null); +<dd>Sort the list in increasing order as defined by the given comparison +delegate. If no delegate is supplied the default comparison function of the +Value type is used. +<dt>alias ArrayList <b>ContainerType</b>; +<dt>alias ArrayList <b>SliceType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dt>alias ReadOnly <b>isReadOnly</b>; +<dd>Aliases for container types +</dl> +</dl> + +<a name="deque"></a> +<h4>mintl.deque</h4> +<dl> +<dt>struct <b>Deque</b>(Value, bit ReadOnly = false, Alloc = GCAllocator) +<dd>A double-ended queue backed by a circular block-allocated array +Compile with version=MinTLNoIndexChecking to disable bounds checking. +The optional ReadOnly parameter disallows container modifications. +The optional allocator customizes memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>mixin <b>MListCat</b>(Deque) +<dd>Mixin <a href="#listcat">list catenation</a>. +<dt>mixin <b>MListAlgo</b>(this,Deque) +<dd>Mixin <a href="#listalgo">list algorithms</a>. +<dt><b>MListCommon</b>(Deque) +<dd>Implement common <a href="#listcomm">list members</a>. +<dt>size_t <b>length</b>; +<dd>Return length of deque. +<dt>DequeReverseIter!(Value) <b>backwards</b>(); +<dd>Backwards traversal for "foreach" +<dt>Deque!(Value,true,Alloc) <b>readonly</b>; +<dd>Property that returns a read-only view of the container. +<dt>Deque!(Value,false,Alloc) <b>readwrite</b>; +<dd>Property that returns a read-write view of the container. +<dt>void <b>sort</b>(int delegate(Value* x, Value* y) cmp = null); +<dd>Sort the list in increasing order as defined by the given comparison +delegate. If no delegate is supplied the default comparison function of the +Value type is used. +<dt>alias Deque <b>ContainerType</b>; +<dt>alias Deque <b>SliceType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dt>alias ReadOnly <b>isReadOnly</b>; +<dd>Aliases for container types +</dl> +</dl> + +<a name="hashaa"></a> +<h4>mintl.hashaa</h4> +<dl> +<dt>struct <b>HashAA</b>(Key,Value,bit ReadOnly = false, Alloc = GCAllocator) +<dd>An associative array linked by insertion order. +The optional ReadOnly parameter disallows container modifications. +The optional allocator customizes memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>void <b>add</b>(...); +<dd>Add key-value pairs to array +<dt>void <b>vadd</b>(TypeInfo[] ti, void* argptr); +<dd>Add using va_arg inpurs +<dt>static HashAA <b>make</b>(...) +<dd>Consruct a HashAA using add(...) +<dt>size_t <b>length</b>; +<dd>Return number of items in the array. +<dt>bool <b>isEmpty</b> +<dd>Return true if array is empty +<dt>Value* <b>get</b>(Key key, bit throwOnMiss = false); +<dd>Return a pointer to the value stored at the key. If the key is not +in the array then null is returned or if throwOnMiss is true an +exception is thrown. +<dt>Value* <b>put</b>(Key key) +<dd>Return a pointer to the value stored at the key and insert the +key with value Value.init if the key is not in the array. +<dt>bool <b>contains</b>(Key key) +<dd>Returns true if the array contains the key +<dt>bool <b>contains</b>(Key key, out Value value) +<dd>Returns true if the array contains the key and sets the out value if +present. +<dt>Value <b>opIndex</b>(Key key); +<dd>Return item with given key. Returns the default missing value if not present. +<dt>void <b>opIndexAssign</b>(Value val, Key key); +<dd>Assign a value to the given key +<dt>Value <b>missing</b> +<dd>Read/write property for the value to use on indexing a key not in +the array. Defaults to Value.init +<dt>HashAA <b>opSlice</b>(Key a, Key b); +<dd>Slice from item a to b (exclusive) +<dt>HashAA <b>opSlice</b>(HashAA a, HashAA b); +<dd>Slice from first key in a to last key in b +<dt>HashAA <b>head</b> +<dd>Return one-item slice of the head +<dt>HashAA <b>tail</b> +<dd>Return one-item slice of the tail +<dt>void <b>next</b>(int n = 1, int end = 0); +<dd>Move the head and tail to the next item. If n is negative move to +the previous item. If end <= 0 move the head of the slice and if +end >= 0 move the tail of the slice. +<dt>void <b>remove</b>(Key key); +<dd>Remove a key from array if present. The node used for key is reused in +future insert actions. +<dt>Value <b>take</b>(Key key); +<dd>Remove a key from array if present and return value. Returns the +default missing value if the key was not present. +<dt>void <b>remove</b>(HashAA subarray); +<dd>Remove a slice from array +<dt>Value[] <b>values</b>; +<dd>Get values as a dynamic array. The values are in insertion order. +<dt>Key[] <b>keys</b>; +<dd>Get keys as a dynamic array. The keys are in insertion order. +<dt>void <b>reserve</b>(size_t n); +<dd>Reserve a capacity for the array +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal by values +<dt>int <b>opApply</b>(int delegate(inout Key key, inout Value x) dg); +<dd>Foreach traversal by key-value pairs +<dt>int <b>opApply</b>(int delegate(inout HashAA n) dg); +<dd>Foreach traversal by one-item slices +<dt>HashAA <b>dup</b>; +<dd>Duplicate array +<dt>void <b>clear</b>; +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>void <b>trim</b>; +<dd>Remove references to extra nodes kept for reuse +<dt>int <b>opEquals</b>(HashAA c); +<dd>Test array equality +<dt>HashAAReverseIter!(Key,Value) <b>backwards</b>(); +<dd>Backwards traversal for "foreach" +<dt>HashAA <b>rehash</b>; +<dd>Rehash array to be more efficient +<dt>Value <b>value</b> +<dd>Return value of a one-item slices +<dt>Key <b>key</b>; +<dd>Return key of a one-item slices +<dt>HashAA!(Key,Value,true) <b>readonly</b>; +<dd>Property that returns a read-only view of the container. +<dt>HashAA!(Key,Value,false) <b>readwrite</b>; +<dd>Property that returns a read-write view of the container. +<dt>void <b>sort</b>(int delegate(Key* x, Key* y) cmp = null); +<dd>Sort the HashAA in increasing order as defined by the given comparison +delegate. If no delegate is supplied the default comparison function of the +Key type is used. Future insertions are added in insertion order at +the end of the traversal order. +<dt>alias HashAA <b>ContainerType</b>; +<dt>alias HashAA <b>SliceType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias Key <b>IndexType</b>; +<dt>alias ReadOnly <b>isReadOnly</b>; +<dd>Aliases for container types +</dl> + +</dl> +<a name="list"></a> +<h4>mintl.list</h4> +<dl> +<dt>struct <b>List</b>(Value, bit ReadOnly = false, Alloc = GCAllocator) +<dd>A doubly-linked list. +Compile with version=MinTLNoIndexChecking to disable bounds checking. +The optional ReadOnly parameter disallows container modifications. +The optional allocator customizes memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>mixin <b>MListCat</b>(List) +<dd>Mixin <a href="#listcat">list catenation</a>. +<dt>mixin <b>MListAlgo</b>(this,List) +<dd>Mixin <a href="#listalgo">list algorithms</a>. +<dt><b>MListCommon</b>(List) +<dd>Implement common <a href="#listcomm">list members</a>. +<dt>size_t <b>length</b>; +<dd>Return length of list. +<dt>void <b>trim</b>; +<dd>Remove references to extra nodes kept for reuse +<dt>ListReverseIter!(Value) <b>backwards</b>(); +<dd>Backwards traversal for "foreach" +<dt>List!(Value,true,Alloc) <b>readonly</b>; +<dd>Property that returns a read-only view of the container. +<dt>List!(Value,false,Alloc) <b>readwrite</b>; +<dd>Property that returns a read-write view of the container. +<dt>void <b>sort</b>(int delegate(Value* x, Value* y) cmp = null); +<dd>Sort the list in increasing order as defined by the given comparison +delegate. If no delegate is supplied the default comparison function of the +Value type is used. +<dt>alias List <b>ContainerType</b>; +<dt>alias List <b>SliceType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dt>alias ReadOnly <b>isReadOnly</b>; +<dd>Aliases for container types +</dl> +</dl> +<p> + +<a name="clist"></a> +<dl> +<dt>struct <b>CircularList</b>(Value, bit ReadOnly = false, Alloc = GCAllocator) +<dd>A circular doubly-linked list. +Compile with version=MinTLNoIndexChecking to disable bounds checking. +The optional ReadOnly parameter disallows container modifications. +The optional allocator customizes memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>mixin <b>MListCat</b>(CircularList) +<dd>Mixin <a href="#listcat">list catenation</a>. +<dt>mixin <b>MListAlgo</b>(this,CircularList) +<dd>Mixin <a href="#listalgo">list algorithms</a>. +<dt><b>MListCommon</b>(CircularList) +<dd>Implement common <a href="#listcomm">list members</a>. +<dt>size_t <b>length</b>; +<dd>Return length of list. +<dt>List <b>toList</b>; +<dd>Return the list as a non-circular List +<dt>void <b>rotate</b>(int n = 1); +<dd>Rotate the list by n steps (backwards if n is negative) +<dt>CircularListReverseIter!(Value) <b>backwards</b>(); +<dd>Backwards traversal for "foreach" +<dt>CircularList!(Value,true,Alloc) <b>readonly</b>; +<dd>Property that returns a read-only view of the container. +<dt>CircularList!(Value,false,Alloc) <b>readwrite</b>; +<dd>Property that returns a read-write view of the container. +<dt>alias CircularList <b>ContainerType</b>; +<dt>alias List!(Value,Alloc) <b>SliceType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dt>alias ReadOnly <b>isReadOnly</b>; +<dd>Aliases for container types +</dl> +</dl> + +<a name="mem"></a> +<h4>mintl.mem</h4> +<dl> +<dt>void* <b>mallocWithCheck</b>(size_t s) +<dd>Call std.c.stdlib.malloc and throw OutOfMemory if fails. +<dt>void* <b>callocWithCheck</b>(size_t n, size_t s) +<dd>Call std.c.stdlib.calloc and throw OutOfMemory if fails. +<dt>void* <b>reallocWithCheck</b>(void* p, size_t s) +<dd>Call std.c.stdlib.realloc and throw OutOfMemory if fails. +<dt>void <b>dfree</b>(void* p) +<dd>Call free. +<dt>void* <b>gcMalloc</b>(size_t s) +<dd>Call mallocWithCheck and register range with GC. +<dt>void* <b>gcCalloc</b>(size_t n, size_t s) +<dd>Call callocWithCheck and register range with GC. +<dt>void* <b>gcRealloc</b>(void* p, size_t s) +<dd>Call reallocWithCheck and register range with GC. +<dt>void <b>gcFree</b>(void* p) +<dd>Remove range and call free. +<p> + +<a name="GCAlloc"> +<dt>struct <b>GCAllocator</b></a> +<dd>The default allocator that indicates the garbage collector +should be used for memory management. +<dt>struct <b>Malloc</b> +<dd>An allocator that uses malloc for memory requests and registers +blocks with the GC when requested by the container. +<dt>struct <b>MallocNoRoots</b> +<dd>An allocator that uses malloc for memory requests and ignores requests +to register blocks with the GC. Use this allocator only when you know the +container will not contain any roots. +</dl> +</dl> + +<a name="multiaa"></a> +<h4>mintl.multiaa</h4> +<dl> +<dt>struct <b>MultiAA</b>!(Key,Value, ImplType = HashAA!(Key,Value[])) +<dd>An associative array which allows keys to be repeated. +Adapted from a customizable container type mapping keys to Value[]. +<p> +<dl> +<dt>ImplType <b>impl</b> +<dd>Read-write property holding the backing container +<dt>void <b>add</b>(...); +<dd>Add key-value pairs to the container +<dt>void <b>vadd</b>(TypeInfo[] ti, void* argptr); +<dd>Add using va_arg inpurs +<dt>static MultiAA <b>make</b>(...) +<dd>Consruct a MultiAA using add(...) +<dt>void <b>addItem</b>(Key key, Value item); +<dd>Add item to container. +<dt>size_t <b>length</b>; +<dd>Return number of items in the container. +<dt>bool <b>isEmpty</b> +<dd>Return true if container is empty. +<dt>Value[] <b>opIndex</b>(Key key); +<dd>Return the values for a given key. +<dt>void <b>remove</b>(Key key, Value value); +<dd>Remove an item from the container if present. +<dt>void <b>remove</b>(Key key); +<dd>Remove all the values with a given key if present. +<dt>Key[] <b>keys</b>; +<dd>Get keys as a dynamic array. Duplicates are removed. +<dt>Value[][] <b>values</b>; +<dd>Get values as a dynamic array. +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal of items in the container. If an item is repeated it +is passed multiple times consecutively to the delegate. +<dt>int <b>opApply</b>(int delegate(inout Key key, inout Value x) dg); +<dd>Foreach traversal of items in the container. If an item is repeated it +is passed multiple times consecutively to the delegate. +<dt>MultiAA <b>dup</b>; +<dd>Duplicate container +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>int <b>opEquals</b>(MultiAA c); +<dd>Test container equality +<dt>alias MultiAA <b>ContainerType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias Key <b>IndexType</b>; +<dt>alias ImplType <b>AdaptType</b>; +<dt>const bit <b>isReadOnly</b> = ImplType.isReadOnly; +<dd>Aliases for container types +</dl> +<p> +<a name="sortedmultiaa"></a> +<dt>alias <b>SortedMultiAA</b>!(Key,Value) +<dd>An alias for MultiAA!(Key,Value,SortedAA!(Key,Value[])) to implement a +sorted multi-aa. +</dl> + +<a name="queue"></a> +<h4>mintl.queue</h4> +<dl> +<dt>struct <b>Queue</b>!(Value, ImplType = Deque!(Value)) +<dd>A queue of items adapted from a customizable list container type. The +default backing container is a Deque. +<p> +<dl> +<dt>ImplType <b>impl</b> +<dd>Read-write property holding the backing container. +<dt>alias addTail <b>put</b>; +<dd>Alias to add items to the tail of the queue. +<dt>alias takeHead <b>take</b>; +<dd>Alias to take items to the head of the queue. +<dt>Value <b>peek</b> +<dd>Return the head of the queue or Value.init if empty. +<dt>mixin <b>MListCat</b>(Queue) +<dd>Mixin <a href="#listcat">list catenation</a>. +<dt>size_t <b>length</b>; +<dd>Return length of queue. +<dt>bool <b>isEmpty</b> +<dd>Return true if container is empty +<dt>Value <b>opIndex</b>(size_t n); +<dd>Return nth item where the head is item 0. +<dt>void <b>opIndexAssign</b>(Value val, size_t n); +<dd>Assign to the nth item +<dt>void <b>addTail</b>(Value v); +<dd>Add to tail of queue +<dt>void <b>addTail</b>(Queue v); +<dd>Append v to tail of queue +<dt>Value <b>takeHead</b>(); +<dd>Remove and return first item +<dt>void <b>removeHead</b>(); +<dd>Remove first item +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal by values +<dt>int <b>opApply</b>(int delegate(inout size_t n, inout Value x) dg); +<dd>Foreach traversal by index-value pairs +<dt>Queue <b>dup</b>; +<dd>Duplicate queue. +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>int <b>opEquals</b>(Queue c); +<dd>Test queue equality +<dt>int <b>opCmp</b>(Queue c); +<dd>Compare queues +<dt>alias Queue <b>ContainerType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dt>alias ImplType <b>AdaptType</b>; +<dt>const bit <b>isReadOnly</b> = ImplType.isReadOnly; +<dd>Aliases for container types +</dl> +<p> +<a name="arrayqueue"></a> +<dt>alias <b>ArrayQueue</b>!(Value) +<dd>An alias for Queue!(Value,ArrayList!(Value)) to adapt an ArrayList +to the queue interface. +<p> +<a name="pqueue"></a> +<dt>alias <b>PriorityQueue</b>!(Value) +<dd>An alias for Queue!(Value,ArrayHeap!(Value)) to adapt an ArrayHeap +to the queue interface. + +</dl> + +<a name="set"></a> +<h4>mintl.set</h4> +<dl> +<dt>struct <b>Set</b>!(Value, ImplType = HashAA!(Value,uint)) +<dd>A set of unique items adapted from a customizable container type +mapping items to 0 or 1. The default backing container type is a +builtin associative array. +<p> +<dl> +<dt>ImplType <b>impl</b> +<dd>Read-write property holding the backing container +<dt>void <b>add</b>(...); +<dd>Add items to set +<dt>void <b>vadd</b>(TypeInfo[] ti, void* argptr); +<dd>Add using va_arg inpurs +<dt>static Set <b>make</b>(...) +<dd>Consruct a Set using add(...) +<dt>void <b>addItem</b>(Value item); +<dd>Add item to set +<dt>size_t <b>length</b>; +<dd>Return number of items in the set. +<dt>bool <b>isEmpty</b> +<dd>Return true if set is empty +<dt>bool <b>opIndex</b>(Value item); +<dd>Return true if the item is in the set +<dt>void <b>remove</b>(Value item); +<dd>Remove an item from the set if present +<dt>Value[] <b>values</b>; +<dd>Get items as a dynamic set. +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal of items in the set +<dt>Set <b>dup</b>; +<dd>Duplicate set +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>int <b>opEquals</b>(Set c); +<dd>Test set equality +<dt>alias Set <b>ContainerType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias ImplType <b>AdaptType</b>; +<dt>const bit <b>isReadOnly</b> = ImplType.isReadOnly; +<dd>Aliases for container types +</dl> +<p> +<a name="sortedset"></a> +<dt>alias <b>SortedSet</b>!(Value) +<dd>An alias for Set!(Value,SortedAA!(Value,uint)) to implement a sorted set. +<p> +<a name="multiset"></a> +<dt>struct <b>MultiSet</b>!(Value, ImplType = HashAA!(uint,Value)) +<dd>A set which allows items to be repeated. Adapted from a customizable +container type mapping items to repeat counts. +<p> +<dl> +<dt>ImplType <b>impl</b> +<dd>Read-write property holding the backing container +<dt>void <b>add</b>(...); +<dd>Add items to set +<dt>void <b>vadd</b>(TypeInfo[] ti, void* argptr); +<dd>Add using va_arg inpurs +<dt>static MultiSet <b>make</b>(...) +<dd>Consruct a MultiSet using add(...) +<dt>void <b>addItem</b>(Value item); +<dd>Add item to set +<dt>size_t <b>length</b>; +<dd>Return number of items in the set. +<dt>bool <b>isEmpty</b> +<dd>Return true if set is empty +<dt>bool <b>opIndex</b>(Value item); +<dd>Return true if the item is in the set +<dt>void <b>remove</b>(Value item); +<dd>Remove an item from the set if present +<dt>Value[] <b>values</b>; +<dd>Get items as a dynamic set. Duplicates are removed. +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal of items in the set. If an item is repeated it +is passed multiple times consecutively to the delegate. +<dt>MultiSet <b>dup</b>; +<dd>Duplicate set +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>int <b>opEquals</b>(MultiSet c); +<dd>Test set equality +<dt>alias MultiSet <b>ContainerType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias ImplType <b>AdaptType</b>; +<dt>const bit <b>isReadOnly</b> = ImplType.isReadOnly; +<dd>Aliases for container types +</dl> +<p> +<a name="sortedmultiset"></a> +<dt>alias <b>SortedMultiSet</b>!(Value) +<dd>An alias for MultiSet!(Value,SortedAA!(Value,uint)) to implement a +sorted multi-set. +</dl> + +<a name="share"></a> +<h4>mintl.share</h4> +The mintl.share module is publically imported into all container modules +and stores shared defintions. +<dl> +<dt>const bit <b>ReadOnly</b> = true; +<dd>A named constant to improve the readability of code involving read-only +containers. See the section on <a href="#unmod">unmodifiable containers</a> for examples. +<dt>class <b>IndexOutOfBoundsException</b> : Exception +<dd>Exception thrown when attempting to access an invalid index or key. Checks for invalid indices can be disabled using version=MinTLNoIndexChecking. + +<a name="listcat"> +<dt>template <b>MListCatOperators</b>(List)</a> +<dd>Concatenation routines to be mixed into the list-like containers +<dl> +<dt>void <b>add</b>(...); +<dd>Add to list +<dt>void <b>vadd</b>(TypeInfo[] ti, void* argptr); +<dd>Add using va_arg inpurs +<dt>static List <b>make</b>(...) +<dd>Consruct a List using add(...) +<dt>void <b>addN</b>(uint n, Value v) +<dd>Add the value n times to the list +<dt>void <b>addBefore</b>(List.SliceType sublist, Value[] v) +<dd>Insert the values in the dynamic array v before sublist +<dt>void <b>addAfter</b>(List.SliceType sublist, Value[] v) +<dd>Insert the values in the dynamic array v after sublist +<dt>List <b>opCatAssign</b>(Value v); +<dd>Concatenation operator this ~= v +<dt>List <b>opCat</b>(Value v); +<dd>Concatenation operator this ~ v. copies this +<dt>List <b>opCat_r</b>(Value v); +<dd>Concatenation operator v ~ this. copies this +<dt>List <b>opCatAssign</b>(List v); +<dd>Concatenation operator this ~= v. copies v +<dt>List <b>opCat</b>(List v); +<dd>Concatenation operator this ~ v. copies both arguments +</dl> +<a name="listalgo"> +<dt>template <b>MListAlgo</b>(Container, alias list)</a> +<dd>List algorithms to be mixed into the list-like containers +<dl> +<dt>Container.SliceType <b>opIn</b>(Value v); +<dd>Return a one-item slice of the first occurrence of v in the list. +<dt>Container.SliceType <b>find</b>(int delegate(inout Value v) dg); +<dd>Return a one-item slice of the first occurrence where dg is true. +<dt>uint <b>count</b>(Value v); +<dd>Return the number of time v appears in the list. +<dt>void <b>swap</b>(Container v); +<dd>Swap the contents of the list with v (assumes non-overlapping). Extra +elements are ignored. +<dt>void <b>fill</b>(Value v); +<dd>Fill the container with v +<dt>void <b>copy</b>(Container v); +<dd>Copy the contents of v to this container. Extra elements are ignored. +</dl> +<a name="listcomm"> +<dt><b>MListCommon</b>(Container)</a> +<dd>Common list routines +<dl> +<dt>bool <b>isEmpty</b> +<dd>Return true if container is empty +<dt>Value <b>opIndex</b>(size_t n); +<dd>Return nth item where the head is item 0. +<dt>void <b>opIndexAssign</b>(Value val, size_t n); +<dd>Assign to the nth item +<dt>Container.SliceType <b>opSlice</b>(size_t a, size_t b); +<dd>Slice from item a to b (exclusive) +<dt>Container.SliceType <b>opSlice</b>(Container.SliceType a, Container.SliceType b); +<dd>Slice from head of a to tail of b +<dt>Container.SliceType <b>head</b> +<dd>Read-only property to get a one-item slice of the head. +<dt>Container.SliceType <b>tail</b> +<dd>Read-only property to get a one-item slice of the tail. +<dt>Value[] <b>values</b>; +<dd>Get list contents as dynamic array. +<dt>Value <b>value</b>; +<dd>Read/write property for the value of a one-item slice. +<dt>void <b>addTail</b>(Value v); +<dd>Add to tail of list +<dt>void <b>addTail</b>(Container v); +<dd>Copy v to tail of list +<dt>void <b>addHead</b>(Value v); +<dd>Add to head of list +<dt>void <b>addHead</b>(Container v); +<dd>Copy v to head of list +<dt>Value <b>takeTail</b>(); +<dd>Remove and return last item +<dt>Value <b>takeHead</b>(); +<dd>Remove and return first item +<dt>void <b>removeTail</b>(); +<dd>Remove last item +<dt>void <b>removeHead</b>(); +<dd>Remove first item +<dt>void <b>remove</b>(Container.SliceType sublist); +<dd>Remove sublist from list +<dt>void <b>addBefore</b>(Container.SliceType subv, Container.SliceType v); +<dd>Insert v before subv. +<dt>void <b>addAfter</b>(Container.SliceType subv, Container.SliceType v); +<dd>Insert v after subv. +<dt>void <b>next</b>(int n = 1, int end = 0); +<dd>Move the head and tail to the next item. If n is negative move to +the previous item. If end <= 0 move the head of the slice and if +end >= 0 move the tail of the slice. +<dt>Value* <b>lookup</b>(size_t n); +<dd>Return a pointer to the nth item +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal by values +<dt>int <b>opApply</b>(int delegate(inout size_t n, inout Value x) dg); +<dd>Foreach traversal by index-value pairs +<dt>int <b>opApply</b>(int delegate(inout Container.SliceType n) dg); +<dd>Foreach traversal by one-item slices +<dt>Container <b>reverse</b>; +<dd>Reverse list in-place. +<dt>Container <b>dup</b>; +<dd>Duplicate array list by duplicating backing array +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>int <b>opEquals</b>(Container c); +<dd>Test list equality +<dt>int <b>opCmp</b>(Container c); +<dd>Compare lists +</dl> +</dl> + +<a name="slist"></a> +<h4>mintl.slist</h4> +<dl> +<dt>struct <b>SList</b>(Value, bit ReadOnly = false, Alloc = GCAllocator) +<dd>A singly-linked list. +Compile with version=MinTLNoIndexChecking to disable bounds checking. +The optional ReadOnly parameter disallows container modifications. +The optional allocator customizes memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>mixin <b>MListCat</b>(SList) +<dd>Mixin <a href="#listcat">list catenation</a>. +<dt>size_t <b>length</b>; +<dd>Return length of list. +<dt>bool <b>isEmpty</b> +<dd>Return true if container is empty +<dt>SList <b>tailList</b>; +<dd>Return the tail of the list as a slice +<dt>Value <b>opIndex</b>(size_t n); +<dd>Return nth item where the head is item 0. +<dt>void <b>opIndexAssign</b>(Value val, size_t n); +<dd>Assign to the nth item +<dt>SList <b>opSlice</b>(size_t a, size_t b); +<dd>Slice from item a to b (exclusive) +<dt>SList <b>opSlice</b>(SList a, SList b); +<dd>Slice from head of a to tail of b +<dt>SList <b>head</b> +<dd>Read-only property to get a one-item slice of the head. +<dt>SList <b>tail</b> +<dd>Read-only property to get a one-item slice of the tail. +<dt>Value <b>value</b> +<dd>Read/write property for the value of a one-item slice. +<dt>Value[] <b>values</b>; +<dd>Get list contents as dynamic array. +<dt>void <b>addTail</b>(Value v); +<dd>Add to tail of list +<dt>void <b>addTail</b>(SList v); +<dd>Append v to tail of list +<dt>void <b>addHead</b>(Value v); +<dd>Add to head of list +<dt>void <b>addHead</b>(SList v); +<dd>Prepend v to head of list +<dt>Value <b>takeHead</b>(); +<dd>Remove and return first item +<dt>void <b>removeHead</b>(); +<dd>Remove first item +<dt>void <b>addAfter</b>(SList subv, SList v); +<dd>Insert v after subv. +<dt>void <b>removeAfter</b>(SList sublist, size_t n=1); +<dd>Remove n items following sublist +<dt>void <b>removeBetween</b>(SList a, SList b); +<dd>Remove items after the tail of a to the head of b (exclusive) +<dt>void <b>next</b>(int n = 1, int end = 0); +<dd>Move the head and tail to the next item. If n is negative move to +the previous item. If end <= 0 move the head of the slice and if +end >= 0 move the tail of the slice. +<dt>Value* <b>lookup</b>(size_t n); +<dd>Return a pointer to the nth item +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal by values +<dt>int <b>opApply</b>(int delegate(inout size_t n, inout Value x) dg); +<dd>Foreach traversal by index-value pairs +<dt>int <b>opApply</b>(int delegate(inout SList n) dg); +<dd>Foreach traversal by one-item slices +<dt>SList <b>dup</b>; +<dd>Duplicate list +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>void <b>trim</b>; +<dd>Remove references to extra nodes kept for reuse +<dt>int <b>opEquals</b>(SList c); +<dd>Test list equality +<dt>int <b>opCmp</b>(SList c); +<dd>Compare lists +<dt>mixin <b>MListAlgo</b>(this,SList) +<dd>Mixin <a href="#listalgo">list algorithms</a>. +<dt>SList!(Value,true,Alloc) <b>readonly</b>; +<dd>Property that returns a read-only view of the container. +<dt>SList!(Value,false,Alloc) <b>readwrite</b>; +<dd>Property that returns a read-write view of the container. +<dt>alias SList <b>ContainerType</b>; +<dt>alias SList <b>SliceType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dt>alias ReadOnly <b>isReadOnly</b>; +<dd>Aliases for container types +</dl> +</dl> +<p> +<a name="cslist"></a> +<dl> +<dt>struct <b>CircularSList</b>(Value, bit ReadOnly = false, Alloc = GCAllocator) +<dd>A circular singly-linked list. +Compile with version=MinTLNoIndexChecking to disable bounds checking. +The optional ReadOnly parameter disallows container modifications. +The optional allocator customizes memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>mixin <b>MListCat</b>(CircularSList) +<dd>Mixin <a href="#listcat">list catenation</a>. +<dt>size_t <b>length</b>; +<dd>Return length of list. +<dt>bool <b>isEmpty</b> +<dd>Return true if container is empty +<dt>SList <b>toSList</b>; +<dd>Return the list as a non-circular SList +<dt>Value <b>opIndex</b>(size_t n); +<dd>Return nth item where the head is item 0. +<dt>void <b>opIndexAssign</b>(Value val, size_t n); +<dd>Assign to the nth item +<dt>SList <b>opSlice</b>(size_t a, size_t b); +<dd>Slice from item a to b (exclusive) +<dt>SList <b>opSlice</b>(SList a, SList b); +<dd>Slice from head of a to tail of b +<dt>SList <b>head</b> +<dd>Read-only property to get a one-item slice of the head. +<dt>SList <b>tail</b> +<dd>Read-only property to get a one-item slice of the tail. +<dt>Value <b>value</b> +<dd>Read/write property for the value of a one-item slice. +<dt>void <b>addTail</b>(Value v); +<dd>Add to tail of list +<dt>void <b>addTail</b>(CircularSList v); +<dd>Append v to tail of list +<dt>void <b>addHead</b>(Value v); +<dd>Add to head of list +<dt>void <b>addHead</b>(CircularSList v); +<dd>Prepend v to head of list +<dt>Value <b>takeHead</b>(); +<dd>Remove and return first item +<dt>void <b>removeHead</b>(); +<dd>Remove first item +<dt>void <b>addAfter</b>(SList subv, SList v); +<dd>Insert v after subv. +<dt>void <b>removeAfter</b>(SList sublist, size_t n=1); +<dd>Remove n items following sublist +<dt>void <b>removeBetween</b>(SList a, SList b); +<dd>Remove items after the tail of a to the head of b (exclusive) +<dt>void <b>rotate</b>(int n = 1); +<dd>Rotate the list by n steps +<dt>Value* <b>lookup</b>(size_t n); +<dd>Return a pointer to the nth item +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal by values +<dt>int <b>opApply</b>(int delegate(inout size_t n, inout Value x) dg); +<dd>Foreach traversal by index-value pairs +<dt>int <b>opApply</b>(int delegate(inout SList n) dg); +<dd>Foreach traversal by one-item slices +<dt>CircularSList <b>dup</b>; +<dd>Duplicate list +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>int <b>opEquals</b>(CircularSList c); +<dd>Test list equality +<dt>int <b>opCmp</b>(CircularSList c); +<dd>Compare lists +<dt>mixin <b>MListAlgo</b>(this,CircularSList) +<dd>Mixin <a href="#listalgo">list algorithms</a>. +<dt>CircularSList!(Value,true,Alloc) <b>readonly</b>; +<dd>Property that returns a read-only view of the container. +<dt>CircularSList!(Value,false,Alloc) <b>readwrite</b>; +<dd>Property that returns a read-write view of the container. +<dt>alias CircularAList <b>ContainerType</b>; +<dt>alias SList!(Value,Alloc) <b>SliceType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dt>alias ReadOnly <b>isReadOnly</b>; +<dd>Aliases for container types +</dl> +</dl> + +<a name="sortedaa"></a> +<h4>mintl.sortedaa</h4> +<dl> +<dt>class <b>CompareFcnSetException</b>: Exception; +<dd>Exception thrown when trying to set the comparison function of +a non-empty array or an array that already had the comparison function +set. +</dl> +<p> +<dl> +<dt>struct <b>SortedAA</b>(Key, Value, bit ReadOnly = false, Alloc = GCAllocator) +<dd>A sorted associative array +The optional ReadOnly parameter disallows container modifications. +The optional allocator customizes memory management. The default allocator +is <a href="#GCAlloc">GCAllocator</a>. +<p> +<dl> +<dt>alias int delegate(Key* a, Key* b) <b>CompareFcn</b>; +<dd>Signature of comparison functions +<dt>void <b>compareFcn</b>(CompareFcn cmp); +<dd>Set the comparison function +<dt>void <b>add</b>(...); +<dd>Add key-value pairs to array +<dt>void <b>vadd</b>(TypeInfo[] ti, void* argptr); +<dd>Add using va_arg inpurs +<dt>static SortedAA <b>make</b>(...) +<dd>Consruct a SortedAA using add(...) +<dt>size_t <b>length</b>; +<dd>Return number of items in the array. +<dt>bool <b>isEmpty</b> +<dd>Return true if array is empty +<dt>Value* <b>get</b>(Key key, bit throwOnMiss = false); +<dd>Return a pointer to the value stored at the key. If the key is not +in the array then null is returned or if throwOnMiss is true an +exception is thrown. +<dt>Value* <b>put</b>(Key key) +<dd>Return a pointer to the value stored at the key and insert the +key with value Value.init if the key is not in the array. +<dt>bool <b>contains</b>(Key key) +<dd>Returns true if the array contains the key +<dt>bool <b>contains</b>(Key key, out Value value) +<dd>Returns true if the array contains the key and sets the out value if +present. +<dt>Value <b>opIndex</b>(Key key); +<dd>Return item with given key. Returns the default missing value if not present. +<dt>void <b>opIndexAssign</b>(Value val, Key key); +<dd>Assign a value to the given key +<dt>Value <b>missing</b> +<dd>Read/write property for the value to use on indexing a key not in +the array. Defaults to Value.init +<dt>SortedAA <b>head</b>() +<dd>Return one-item slice of the smallest item +<dt>SortedAA <b>tail</b>() +<dd>Return one-item slice of the greatest item +<dt>void <b>next</b>(int n = 1, int end = 0); +<dd>Move the head and tail to the next item. If n is negative move to +the previous item. If end <= 0 move the head of the slice and if +end >= 0 move the tail of the slice. +<dt>SortedAA <b>from</b>(Key a); +<dd>Return a one-item slice of the smallest item greater than or equal to a. +<dt>SortedAA <b>to</b>(Key b); +<dd>Return a one-item slice of the greatest item smaller than b. +<dt>SortedAA <b>opSlice</b>(Key a, Key b); +<dd>Slice from item a to b (exclusive). +<dt>SortedAA <b>opSlice</b>(SortedAA a, SortedAA b); +<dd>Slice from first key in a to last key in b. +<dt>Value <b>take</b>(Key key); +<dd>Remove a key from array if present and return value. Returns the +default missing value if the key was not present. +<dt>void <b>remove</b>(Key key); +<dd>Remove a key from array if present. +<dt>void <b>remove</b>(SortedAA subarray); +<dd>Remove a slice from array. +<dt>void <b>trim</b>; +<dd>Remove references to extra nodes kept for reuse +<dt>Value[] <b>values</b>; +<dd>Get values as a dynamic array. The values are in order. +<dt>Key[] <b>keys</b>; +<dd>Get keys as a dynamic array. The keys are in order. +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal by values +<dt>int <b>opApply</b>(int delegate(inout Key key, inout Value x) dg); +<dd>Foreach traversal by key-value pairs +<dt>int <b>opApply</b>(int delegate(inout SortedAA n) dg); +<dd>Foreach traversal by one-item slices +<dt>SortedAA <b>dup</b>; +<dd>Duplicate array +<dt>void <b>clear</b>() +<dd>Clear contents. Only needed if a non-GCAllocator is used. +<dt>int <b>opEquals</b>(SortedAA c); +<dd>Test array equality +<dt>SortedAAReverseIter!(Key,Value) <b>backwards</b>(); +<dd>Backwards traversal for "foreach" +<dt>Value <b>value</b>; +<dd>Return value of a one-item slices +<dt>Key <b>key</b>; +<dd>Return key of a one-item slices +<dt>SortedAA!(Key,Value,true,Alloc) <b>readonly</b>; +<dd>Property that returns a read-only view of the container. +<dt>SortedAA!(Key,Value,false,Alloc) <b>readwrite</b>; +<dd>Property that returns a read-write view of the container. +<dt>alias SortedAA <b>ContainerType</b>; +<dt>alias SortedAA <b>SliceType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias Key <b>IndexType</b>; +<dd>Aliases for container types +</dl> +</dl> + +<a name="stack"></a> +<h4>mintl.stack</h4> +<dl> +<dt>struct <b>Stack</b>!(Value, ImplType = Deque!(Value)) +<dd>A stack of items adapted from a customizable list container type. The +default backing container is a Deque. +<p> +<dl> +<dt>ImplType <b>impl</b> +<dd>Read-write property holding the backing container. +<dt>alias addTail <b>push</b>; +<dd>Alias to add items to the tail of the stack. +<dt>alias takeTail <b>pop</b>; +<dd>Alias to take items from the tail of the stack. +<dt>Value <b>peek</b> +<dd>Return the top of the stack or Value.init if empty. +<dt>mixin <b>MListCat</b>(Stack) +<dd>Mixin <a href="#listcat">list catenation</a>. +<dt>size_t <b>length</b>; +<dd>Return length of stack. +<dt>bool <b>isEmpty</b> +<dd>Return true if container is empty +<dt>Value <b>opIndex</b>(size_t n); +<dd>Return nth item where the head is item 0. +<dt>void <b>opIndexAssign</b>(Value val, size_t n); +<dd>Assign to the nth item +<dt>void <b>addTail</b>(Value v); +<dd>Add to tail of stack +<dt>void <b>addTail</b>(Stack v); +<dd>Append v to tail of stack +<dt>Value <b>takeHead</b>(); +<dd>Remove and return first item +<dt>void <b>removeHead</b>(); +<dd>Remove first item +<dt>int <b>opApply</b>(int delegate(inout Value x) dg); +<dd>Foreach traversal by values +<dt>int <b>opApply</b>(int delegate(inout size_t n, inout Value x) dg); +<dd>Foreach traversal by index-value pairs +<dt>Stack <b>dup</b>; +<dd>Duplicate stack. +<dt>int <b>opEquals</b>(Stack c); +<dd>Test stack equality +<dt>int <b>opCmp</b>(Stack c); +<dd>Compare stacks +<dt>alias Stack <b>ContainerType</b>; +<dt>alias Value <b>ValueType</b>; +<dt>alias size_t <b>IndexType</b>; +<dt>alias ImplType <b>AdaptType</b>; +<dt>const bit <b>isReadOnly</b> = ImplType.isReadOnly; +<dd>Aliases for container types +</dl> +<p> +<a name="arraystack"></a> +<dt>alias <b>ArrayStack</b>!(Value) +<dd>An alias for Stack!(Value,ArrayList!(Value)) to adapt an ArrayList +to the stack interface. +</dl> + +</BODY> +</HTML>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/linux.mak Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,72 @@ + +# To build libmintl.a type +# make -f linux.mak DFLAGS=-g LIBNAME=libmintl_debug.a +# or +# make -f linux.mak DFLAGS=-release LIBNAME=libmintl.a +# The libmintl.a and object files will be created in the source directory. + +# flags to use building unittest.exe +DUNITFLAGS=-g -unittest -I.. -version=MinTLUnittest -version=MinTLVerboseUnittest + +# flags to use when building the mintl.lib library +DLIBFLAGS=$(DFLAGS) -I.. +#DLIBFLAGS=-g -I.. + +DMD=dmd + +#LIBNAME = libmintl.a + +targets : unittest + +mintl : $(LIBNAME) + +SRC = all.d \ + array.d \ + arraylist.d \ + arrayheap.d \ + deque.d \ + hashaa.d \ + list.d \ + slist.d \ + share.d \ + adapter.d \ + stack.d \ + queue.d \ + set.d \ + multiaa.d \ + mem.d \ + sorting.d \ + sortedaa.d + +OBJS = all.o \ + array.o \ + arraylist.o \ + arrayheap.o \ + deque.o \ + hashaa.o \ + list.o \ + slist.o \ + share.o \ + adapter.o \ + stack.o \ + queue.o \ + set.o \ + multiaa.o \ + mem.o \ + sorting.o \ + sortedaa.o + +$(LIBNAME) : $(OBJS) $(SRC) + ar -r $@ $(OBJS) + +clean: + rm *.o + rm $(LIBNAME) + rm unittest + +%.o : %.d + $(DMD) -c $(DLIBFLAGS) $< -of$@ + +unittest : $(LIBNAME) $(OBJS) $(SRC) + $(DMD) $(DUNITFLAGS) unittest.d -ofunittest $(SRC) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/list.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,1420 @@ +/** \file list.d + * \brief A doubly-linked list and a circular doubly-linked list. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.7.1 + */ + +module mintl.list; + +private import mintl.share; // for ~ and ~= +private import mintl.sorting; +import mintl.mem; + +// shared data structure between List and CircularList +private struct DNode(Value) { + DNode* next, prev; + Value data; + Value* sortLookup(){return &data;} +} + +/** Template for member functions common to List and CircularList */ +template MCommonList(alias tail_, Container ) { + + /** Test if a container is empty. */ + bool isEmpty() { + return head_ is null; + } + + /** Get the length of list. The computation can be O(n) but the + * result is cached and the actively updated until another list + * of unknown length is concatenated or removed. + */ + size_t length() { + if (length_ == 0 && head_) { + Container.Node* n = head_; + length_ = 1; + Container.Node* end = tail_; + while (n !is end) { + length_++; + n = n.next; + } + } + return length_; + } + + // helper function to check if the index is legal + void boundsCheck(size_t n) { + version (MinTLNoIndexChecking) { + } else { + if (!(n == 0 && this.head_) && + (n >= length_ && n >= this.length)) { + throw new IndexOutOfBoundsException(); + } + } + } + + // Internal function to get the nth item of the list. + package Container.Node* getNode(size_t n) { + boundsCheck(n); + Container.Node* v; + if (n <= length_/2) { + v = head_; + while (n--) { + v = v.next; + } + } else { + n = length_-n-1; + v = tail_; + while (n--) { + v = v.prev; + } + } + return v; + } + + /** Get the nth item in the list from head. The operation is O(N(n)) + * where N(x) is the distance from x to either end of list. + * Indexing out of bounds throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + Container.ValueType opIndex(size_t n) { + return getNode(n).data; + } + + static if (!Container.isReadOnly) { + + /** Get a pointer to the nth item in the list from head. The + * operation is O(N(n)) where N(x) is the distance from x to either + * end of list. Indexing out of bounds throws an + * IndexOutOfBoundsException unless version=MinTLNoIndexChecking is + * set. + */ + Container.ValueType* lookup(size_t n) { + return &getNode(n).data; + } + + /** Set the nth item in the list from head. The operation is O(N(n)) + * where N(x) is the distance from x to either end of list. + * Indexing out of bounds throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + void opIndexAssign(Container.ValueType val, size_t n) { + getNode(n).data = val; + } + + /** Reverse a list in-place. The operation is O(n) where n is + * length of the list. + */ + Container reverse() { + if (this.isEmpty) + return *this; + Node* i = head_; + Node* j = tail_; + TypeInfo ti = typeid(Container.ValueType); + while (i !is j && i.next !is j) { + ti.swap(&i.data,&j.data); + i = i.next; + j = j.prev; + } + ti.swap(&i.data,&j.data); + return *this; + } + + } // !isReadOnly + + /** Copies the list contents to an array */ + Container.ValueType[] values() { + Container.ValueType[] buffer = new Container.ValueType[this.length]; + foreach(size_t n, Container.ValueType val; *this) { + buffer[n] = val; + } + return buffer; + } + + /** Test for equality of two lists. The operation is O(n) where n + * is length of the list. + */ + int opEquals(Container c) { + if (length_ && c.length_ && length_ != c.length_) + return 0; + Container.Node* i = head_; + Container.Node* j = c.head_; + Container.Node* t = tail_; + Container.Node* ct = c.tail_; + TypeInfo ti = typeid(Container.ValueType); + while (i !is null && j !is null) { + if (!ti.equals(&i.data,&j.data)) + return 0; + if (i !is t && j !is ct) + return 1; + i = i.next; + j = j.next; + } + return (i is null && j is null); + } + + /** Compare two lists. */ + int opCmp(Container c) { + Container.Node* i = head_; + Container.Node* j = c.head_; + Container.Node* t = tail_; + Container.Node* ct = c.tail_; + TypeInfo ti = typeid(Container.ValueType); + while (i !is null && j !is null) { + int cmp = ti.compare(&i.data,&j.data); + if (cmp) + return cmp; + if (i !is t && j !is ct) + return 0; + i = i.next; + j = j.next; + } + if (i is null && j is null) + return 0; + else + return (i is null) ? -1 : 1; + } + + /** Create a sub-list from index a to b (exclusive). The operation is + * O(max(N(a),N(b))) where N(x) is distance from x to either end of + * the target list. + */ + Container.SliceType opSlice(size_t a, size_t b) { + Container.SliceType res; + res.length_ = b-a; + if (res.length_ > 0) { + res.head_ = getNode(a); + if (this.length_ - b > b-a){ + Container.Node* v = res.head_; + b = b-a-1; + while (b--) + v = v.next; + res.tail_ = v; + } else { + res.tail_ = getNode(b-1); + } + } + return res; + } + + /** Create a sub-list from the head of a to the tail of b (inclusive). */ + Container.SliceType opSlice(Container.SliceType a, Container.SliceType b) { + if (a.isEmpty) + return b; + if (b.isEmpty) + return a; + Container.SliceType res; + Container.Node* i = a.head_; + res.head_ = i; + res.tail_ = b.tail_; + res.length_ = 0; // flag indicating unknown length + return res; + } + + /** Iterates over the list from head to tail calling delegate to + * perform an action. The value is passed to the delegate. + */ + int opApplyNoKeyStep(int delegate(inout Container.ValueType x) dg, int step = 1){ + int dg2(inout size_t count, inout Container.ValueType val) { + return dg(val); + } + return opApplyWithKeyStep(&dg2,step); + } + + /** Iterates over the list from head to tail calling delegate to + * perform an action. The index from 0 and the value are passed + * to the delegate. + */ + int opApplyWithKeyStep(int delegate(inout size_t n, inout Container.ValueType x) dg, + int step = 1){ + Container.Node* i = step>0 ? head_ : tail_; + Container.Node* end = step>0 ? tail_ : head_; + int res = 0; + size_t n = step>0 ? 0 : this.length-1; + while (i !is null) { + res = dg(n, i.data); + if (res || i is end) break; + n += step; + i = step>0 ? i.next : i.prev; + } + return res; + } + + /** Iterates over the list from head to tail calling delegate to + * perform an action. A one-item sub-list is passed to the delegate. + */ + int opApplyIterStep(int delegate(inout Container.SliceType n) dg, int step = 1){ + Container.Node* i = step>0 ? head_ : tail_; + Container.Node* end = step>0 ? tail_ : head_; + int res = 0; + Container.SliceType n; + n.length_ = 1; + while (i !is null) { + n.head_ = n.tail_ = i; + res = dg(n); + if (res || i is end) break; + i = step>0 ? i.next : i.prev; + } + return res; + } + +} + +/** \class List + * \brief A doubly-linked list. + * + * A List!(Value) is a linked list of data of type Value. A list is + * similar to a dynamic array except accessing an element in the + * middle of the list is O(n) and appending to the front or back is + * O(1). Any operation that is not constant-time will explicitly have + * the performance behavior documented. + * + * The optional ReadOnly parameter List!(Value,ReadOnly) forbids + * operations that modify the container. The readonly() property returns + * a ReadOnly view of the container. + * + * The optional allocator parameter List!(Value,false,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct List(Value, bit ReadOnly = false, Alloc = GCAllocator) { + + alias List ContainerType; + alias List SliceType; + alias Value ValueType; + alias size_t IndexType; + alias Value SortType; + alias DNode!(Value) Node; + alias ReadOnly isReadOnly; + + const int NodeAllocationBlockSize = 10; // allocate 10 nodes at a time + + /* length 0 means length is unknown. An empty list is indicated by + * a null head. The tail can be non-null in order to maintain the + * cached nodes. + */ + invariant { + assert( length_ == 0 || head_ !is null ); + } + + // private bug private { + size_t length_; + Node* head_; + Node* tail_; + // } + + /** Get a ReadOnly view of the container */ + .List!(Value, true, Alloc) readonly() { + .List!(Value, true, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get a read-write view of the container */ + .List!(Value, false, Alloc) readwrite() { + .List!(Value, false, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + static if (!ReadOnly) { + + /** Appends an item to the tail of the list. If the target list is + * a sub-list call addAfter instead of addTail to insert an item + * after a sub-list. + */ + void addTail(Value v) { + if (head_ is null && tail_ !is null) { + // empty list but with cache available + head_ = tail_; + tail_.data = v; + length_ = 1; + } else if (tail_ is null || tail_.next is null) { + if (head_ is null || head_.prev is null) { + // no available nodes so allocate a new one + List val; + val.head_ = val.tail_ = newNode(); + val.length_ = 1; + val.head_.data = v; + addTail(val); + } else { + // grab available node from front + Node* t = head_.prev; + if (t.prev !is null) t.prev.next = head_; + head_.prev = t.prev; + t.prev = tail_; + t.next = null; + tail_.next = t; + tail_ = t; + tail_.data = v; + if (length_) length_++; + } + } else { + // grab available node from end + tail_.next.prev = tail_; + tail_ = tail_.next; + tail_.data = v; + if (length_) length_++; + } + } + + /** Appends a list to the tail of the target list. If the target + * list is a sub-list call addAfter instead of addTail to insert + * another list after a sub-list. + */ + void addTail(List v) { + if (v.isEmpty) + return; + if (tail_ !is null) + tail_.next = v.head_; + v.head_.prev = tail_; + if (this.isEmpty) + length_ = v.length_; + else + length_ = increaseLength(length_, v.length_); + tail_ = v.tail_; + if (head_ is null) + head_ = v.head_; + } + + mixin MListCatOperators!(List); + + /** Clear all contents. */ + void clear() { + static if (is(Alloc == GCAllocator)) { + } else { + trim(); + Node* i = head_; + while (i !is null) { + Node* next = i.next; + Alloc.gcFree(i); + i = next; + } + } + *this = List.init; + } + + /** Set the value of one-item slice (more generally the head value). */ + void value(Value newValue) { + head_.data = newValue; + } + + // Helper function for take and remove + private Node* takeTailHelper() { + if (this.isEmpty) + throw new IndexOutOfBoundsException(); + Node* v = tail_; + if (head_ && head_ is tail_) { + head_ = null; + } else { + tail_ = tail_.prev; + } + return v; + } + + /** Removes and returns the tail item of the list. The node that + * contained the item may be reused in future additions to the + * list. To prevent the node from being reused call <tt>trim</tt> or + * call <tt>remove</tt> with a sublist containing the last item. If + * the target list is empty an IndexOutOfBoundsException is thrown + * unless version=MinTLNoIndexChecking is set. + */ + Value takeTail() { + Node* v = takeTailHelper(); + Value data = v.data; + v.data = Value.init; + if (length_) length_--; + return data; + } + + /** Removes the tail item of the list. */ + void removeTail() { + Node* v = takeTailHelper(); + v.data = Value.init; + if (length_) length_--; + } + + /** Prepends an item to the head of the target list. If the target + * list is a sub-list call addBefore instead of addHead to insert an + * item before a sub-list. + */ + void addHead(Value v) { + if (head_ is null && tail_ !is null) { + // empty list but with cache available + head_ = tail_; + tail_.data = v; + length_ = 1; + } else if (head_ is null || head_.prev is null) { + if (tail_ is null || tail_.next is null) { + // no available nodes so allocate a new one + List val; + val.head_ = val.tail_ = newNode(); + val.length_ = 1; + val.head_.data = v; + addHead(val); + } else { + // grab available node from end + Node* t = tail_.next; + if (t.next !is null) t.next.prev = tail_; + tail_.next = t.next; + t.next = head_; + t.prev = null; + head_.prev = t; + head_ = t; + head_.data = v; + if (length_) length_++; + } + } else { + // grab available node from front + head_.prev.next = head_; + head_ = head_.prev; + head_.data = v; + if (length_) length_++; + } + } + + /** Prepends a list to the head of the target list. If the target + * list is a sub-list call addBefore instead of addHead to insert a + * list before a sub-list. + */ + void addHead(List v) { + if (v.isEmpty) + return; + if (head_ !is null) + head_.prev = v.tail_; + v.tail_.next = head_; + if (this.isEmpty) + length_ = v.length_; + else + length_ = increaseLength(length_, v.length_); + head_ = v.head_; + if (tail_ is null) + tail_ = v.tail_; + } + + // Helper function for take and remove + private Node* takeHeadHelper() { + if (this.isEmpty) + throw new IndexOutOfBoundsException(); + Node* v = head_; + if (head_ && head_ is tail_) { + head_ = null; + } else { + head_ = head_.next; + } + return v; + } + + /** Removes and returns the head item of the list. The node that + * contained the item may be reused in future additions to the + * list. If the target list is empty an IndexOutOfBoundsException is + * thrown unless version=MinTLNoIndexChecking is set. + */ + Value takeHead() { + Node* v = takeHeadHelper(); + Value data = v.data; + v.data = Value.init; + if (length_) length_--; + return data; + } + + /** Removes the head item of the list. */ + void removeHead() { + Node* v = takeHeadHelper(); + v.data = Value.init; + if (length_) length_--; + } + + /** Insert a list before a sub-list. */ + void addBefore(List subv, List v) { + if (v.isEmpty) + return; + if (subv.isEmpty) + throw new IndexOutOfBoundsException(); + Node* t = subv.head_; + if (t.prev !is null) { + t.prev.next = v.head_; + } + v.head_.prev = t.prev; + v.tail_.next = t; + t.prev = v.tail_; + if (t is head_) + head_ = v.head_; + length_ = increaseLength(length_, v.length_); + } + + /** Insert a list after a sub-list. */ + void addAfter(List subv, List v) { + if (v.isEmpty) + return; + if (subv.isEmpty) + throw new IndexOutOfBoundsException(); + Node* t = subv.tail_; + if (t.next !is null) { + t.next.prev = v.tail_; + } + v.tail_.next = t.next; + v.head_.prev = t; + t.next = v.head_; + if (t is tail_) + tail_ = v.tail_; + length_ = increaseLength(length_, v.length_); + } + + + /** Removes a sub-list from the list entirely. */ + void remove(List sublist) { + if (sublist.isEmpty) + return; + Node* h = sublist.head_; + Node* t = sublist.tail_; + if (h is head_ && t is tail_) { + head_ = tail_ = null; + length_ = 0; + return; + } + Node* hp = h.prev; + Node* tn = t.next; + if (hp !is null) + hp.next = tn; + if (tn !is null) + tn.prev = hp; + if (h is head_) + head_ = tn; + if (t is tail_) + tail_ = hp; + length_ = decreaseLength(length_, sublist.length_); + } + + /** Removes an item from the list if present. */ + void remove(size_t index) { + List item = opSlice(index, index+1); + remove(item); + } + + /** Removes an item and return the value if any. */ + Value take(size_t index) { + List item = opSlice(index, index+1); + remove(item); + Value val = item[0]; + item.clear(); + return val; + } + + /** Trims off extra nodes that are not actively being used by the + * list but are available for recyling for future add operations. + * This function should be called after calling <tt>remove</tt> and + * there are other list or pointer references to the removed item. + */ + void trim() { + if (!this.isEmpty) { + Node* i; + if (tail_.next) { + i = tail_.next; + i.prev = null; + tail_.next = null; + static if (is(Alloc == GCAllocator)) { + } else { + while (i !is null) { + Node* next = i.next; + Alloc.gcFree(i); + i = next; + } + } + } + if (head_.prev) { + i = head_.prev; + i.next = null; + head_.prev = null; + static if (is(Alloc == GCAllocator)) { + } else { + while (i !is null) { + Node* prev = i.prev; + Alloc.gcFree(i); + i = prev; + } + } + } + } + } + + } // !ReadOnly + + /** Duplicates a list. The operation is O(n) where n is length of + * the list. + */ + List dup() { + .List!(Value,false,Alloc) res; + foreach(ValueType val; *this) { + res ~= val; + } + static if (ReadOnly) { + return res.readonly; + } else { + return res; + } + } + + /** Move a sub-list towards the head or tail by n items. If n is + * negative the sub-list moves towards the head. A positive end is + * the tail, negative the head and 0 is both. By default moves to + * the next item. + */ + void next(int n = 1, int end = 0) { + if (length_) + length_ -= n*end; + while (n-- > 0) { + if (end <= 0) + head_ = head_.next; + if (end >= 0) + tail_ = tail_.next; + } + while (++n < 0) { + if (end <= 0) + head_ = head_.prev; + if (end >= 0) + tail_ = tail_.prev; + } + } + + /** Get the length of list. The computation can be O(n) but the + * result is cached and the actively updated until another list + * of unknown length is concatenated. + */ + size_t length() { + if (length_ == 0 && !this.isEmpty) { + Node* n = head_; + length_ = 1; + while (n !is tail_) { + length_++; + n = n.next; + } + } + return length_; + } + + /** Create a one-item slice of the head. */ + List head() { + List res; + res.length_ = head_? 1 : 0; + res.head_ = res.tail_ = head_; + return res; + } + + /** Create a one-item slice of the tail. */ + List tail() { + List res; + res.length_ = tail_? 1 : 0; + res.head_ = res.tail_ = tail_; + return res; + } + + /** Get the value of one-item slice (more generally the head value). + * Useful for expressions like x.tail.value or x.head.value. */ + Value value() { + return head_.data; + } + + /** Iterate backwards over the list (from tail to head). This + * should be called as the iteration parameter in a + * <tt>foreach</tt> statement + */ + ListReverseIter!(Value,ReadOnly,Alloc) backwards() { + ListReverseIter!(Value,ReadOnly,Alloc) res; + res.list = this; + return res; + } + + /** Helper functions for opApply */ + mixin MOpApplyImpl!(ContainerType); + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + alias opApplyIter opApply; + + private Node* newNode() { + static if (is(Alloc == GCAllocator)) { + // allocate a block of nodes and return pointer to first one + Node[] block = new Node[NodeAllocationBlockSize]; + for (int k=1; k<NodeAllocationBlockSize; k++) { + block[k-1].next = &block[k]; + } + return &block[0]; + } else { + // can only allocate one at a time because we have to track each + Node* p = cast(Node*)Alloc.gcMalloc(Node.sizeof); + *p = Node.init; + return p; + } + } + + List getThis(){return *this;} + mixin MListAlgo!(List, getThis); + Node* getHead(){return head_;} + Node* getTail(){return tail_;} + mixin MSequentialSort!(List, getHead,getTail); + void sort(int delegate(Value*a, Value*b) cmp = null) { + Node* newhead, newtail; + dosort(newhead,newtail,cmp); + head_ = newhead; + tail_ = newtail; + } + mixin MCommonList!(tail_, List ); + void privateMake(Node* h, Node* t, size_t len) { + head_ = h; + tail_ = t; + length_ = len; + } +} + +// helper structure for backwards() +struct ListReverseIter(Value,bit ReadOnly,Alloc) { + mixin MReverseImpl!(List!(Value,ReadOnly,Alloc)); +} + +/** \class CircularList + * \brief A circular doubly-linked list. + * + * A CircularList!(Value) is a circular doubly linked list of data of type + * Value. A CircularList differs from a List in that the tail of the list + * is linked to the head. As a consequence no nodes are saved and + * reused between <tt>add</tt> and <rr>remove</tt> functions and + * slices can be moved forward around the list indefinitely. A CircularList + * also has a smaller memory footprint since it requires only one + * pointer for the head instead of two pointers for a tail and head. + * + * The optional ReadOnly parameter CircularList!(Value,ReadOnly) forbids + * operations that modify the container. The readonly() property returns + * a ReadOnly view of the container. + * + * The optional allocator parameter CircularList!(Value,false,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct CircularList(Value, bit ReadOnly = false, Alloc = GCAllocator) { + + alias CircularList ContainerType; + alias List!(Value,ReadOnly,Alloc) SliceType; + alias Value ValueType; + alias size_t IndexType; + alias DNode!(Value) Node; + alias ReadOnly isReadOnly; + + private { + size_t length_; + Node* head_; + } + + /* length 0 means length is unknown. */ + invariant { + assert( length_ == 0 || head_ !is null ); + } + + /** Return the circular list as a non-circular List. + * \return the list as a List + */ + SliceType toList() { + SliceType res; + if (this.isEmpty) + return res; + res.privateMake(head_,head_.prev,length_); + // res.tail_ = + // res.head_ = head_; + // res.length_ = length_; + return res; + } + + /** Get a ReadOnly view of the container */ + .CircularList!(Value, true, Alloc) readonly() { + .CircularList!(Value, true, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get a read-write view of the container */ + .CircularList!(Value, false, Alloc) readwrite() { + .CircularList!(Value, false, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + private Node* tail_() { + if (this.isEmpty) return null; + return head_.prev; + } + + static if (!ReadOnly) { + + /** Rotate the list by n items. If n is negative the rotation is + * reversed. + */ + void rotate(int n = 1) { + if (n >= 0) { + while (n-- > 0) + head_ = head_.next; + } else { + while (++n < 0) + head_ = head_.prev; + } + } + + /** Clear all contents. */ + void clear() { + static if (is(Alloc == GCAllocator)) { + } else { + Node* i = head_; + if (i !is null) + i.prev.next = null; + while (i !is null) { + Node* next = i.next; + Alloc.gcFree(i); + i = next; + } + } + *this = CircularList.init; + } + /** Appends an item to the tail of the list. If the target list is + * a sub-list call addAfter instead of addTail to insert an item + * after a sub-list. + */ + void addTail(Value v) { + Node* n = newNode(); + n.data = v; + addNode(n); + } + + private void link(Node* a, Node* b) { + a.next = b; + b.prev = a; + } + + /** Adds a node before head. */ + private void addNode(Node* n) { + if (this.isEmpty) { + link(n,n); + head_ = n; + length_ = 1; + } else { + link(tail_,n); + link(n,head_); + if (length_) length_++; + } + } + + /** Appends a list to the tail of the target list. If the target + * list is a sub-list call addAfter instead of addTail to insert + * another list after a sub-list. + */ + void addTail(CircularList v) { + addHead(v); + } + + mixin MListCatOperators!(CircularList); + + // Helper function for take and remove + private Node* takeTailHelper() { + if (this.isEmpty) + throw new IndexOutOfBoundsException(); + Node* v = tail_; + if (head_ && head_ is tail_) { + head_ = null; + } else { + link(v.prev,v.next); + } + return v; + } + + /** Removes and returns the tail item of the list. The node that + * contained the item may be reused in future additions to the + * list. To prevent the node from being reused call <tt>trim</tt> or + * call <tt>remove</tt> with a sublist containing the last item. If + * the target list is empty an IndexOutOfBoundsException is thrown + * unless version=MinTLNoIndexChecking is set. + */ + Value takeTail() { + Node* v = takeTailHelper(); + Value data = v.data; + freeNode(v); + if (length_) length_--; + return data; + } + + /** Removes the tail item of the list. */ + void removeTail() { + Node* v = takeTailHelper(); + if (length_) length_--; + freeNode(v); + } + + /** Prepends an item to the head of the target list. If the target + * list is a sub-list call addBefore instead of addHead to insert an + * item before a sub-list. + */ + void addHead(Value v) { + Node* n = new Node; + n.data = v; + addNode(n); + head_ = n; + } + + /** Prepends a list to the head of the target list. If the target + * list is a sub-list call addBefore instead of addHead to insert a + * list before a sub-list. + */ + void addHead(CircularList v) { + if (v.isEmpty) + return; + if (this.isEmpty) { + *this = v; + return; + } + Node* vt = v.tail_; + link(tail_,v.head_); + link(vt,head_); + head_ = v.head_; + length_ = increaseLength(length_, v.length_); + } + + // Helper function for take and remove + private Node* takeHeadHelper() { + if (this.isEmpty) + throw new IndexOutOfBoundsException(); + Node* v = head_; + if (head_ && head_ is tail_) { + head_ = null; + } else { + link(v.prev,v.next); + } + return v; + } + + /** Removes and returns the head item of the list. The node that + * contained the item may be reused in future additions to the + * list. To prevent the node from being reused call <tt>trim</tt> or + * call <tt>remove</tt> with a sublist containing the last item. If + * the target list is empty an IndexOutOfBoundsException is thrown + * unless version=MinTLNoIndexChecking is set. + */ + Value takeHead() { + Node* v = takeHeadHelper(); + Value data = v.data; + if (length_) length_--; + freeNode(v); + return data; + } + + /** Removes the head item of the list. */ + void removeHead() { + Node* v = takeHeadHelper(); + if (length_) length_--; + freeNode(v); + } + + /** Insert a list before a sub-list. */ + void addBefore(SliceType subv, SliceType v) { + if (v.isEmpty) + return; + if (subv.isEmpty) + throw new IndexOutOfBoundsException(); + Node* t = subv.head_; + link(t.prev,v.head_); + link(v.tail_,t); + if (t is head_) + head_ = v.head_; + length_ = increaseLength(length_, v.length_); + } + + /** Insert a list after a sub-list. */ + void addAfter(SliceType subv, SliceType v) { + if (v.isEmpty) + return; + if (subv.isEmpty) + throw new IndexOutOfBoundsException(); + Node* t = subv.tail_; + link(v.tail_,t.next); + link(t,v.head_); + length_ = increaseLength(length_, v.length_); + } + + + /** Removes a sub-list from the list entirely. */ + void remove(SliceType sublist) { + if (sublist.isEmpty) + return; + Node* h = sublist.head_; + Node* t = sublist.tail_; + if (h is head_ && t is tail_) { + head_ = null; + length_ = 0; + return; + } + if (h is head_) + head_ = t.next; + link(h.prev,t.next); + h.prev = null; + t.next = null; + length_ = decreaseLength(length_, sublist.length_); + } + + /** Removes an item if present. */ + void remove(size_t index) { + remove(opSlice(index, index+1)); + } + + /** Removes an item from the list and return its value, if present. */ + Value take(size_t index) { + SliceType item = opSlice(index, index+1); + remove(item); + Value val = item[0]; + item.clear(); + return val; + } + + } // !ReadOnly + + /** Duplicates a list. The operation is O(n) where n is length of + * the list. + */ + CircularList dup() { + .CircularList!(Value,false,Alloc) res; + foreach(ValueType val; *this) { + res ~= val; + } + static if (ReadOnly) { + return res.readonly; + } else { + return res; + } + } + + /** Create a one-item slice of the head. */ + SliceType head() { + SliceType res; + if (this.isEmpty) return res; + res.head_ = res.tail_ = head_; + res.length_ = 1; + return res; + } + + /** Create a one-item slice of the tail. */ + SliceType tail() { + SliceType res; + if (this.isEmpty) return res; + res.head_ = res.tail_ = head_.prev; + res.length_ = 1; + return res; + } + + /** Move a sub-list towards the tail by n items. */ + void next(int n = 1, int end = 0) { + if (length_) + length_ -= n*end; + while (n-- > 0) { + if (end <= 0) + head_ = head_.next; + } + } + + /** Iterate backwards over the list (from tail to head). This + * should be called as the iteration parameter in a + * <tt>foreach</tt> statement + */ + CircularListReverseIter!(Value,ReadOnly,Alloc) backwards() { + CircularListReverseIter!(Value,ReadOnly,Alloc) res; + res.list = this; + return res; + } + + /** + * Helper functions for opApply with/without keys and + * forward/backward order + */ + mixin MOpApplyImpl!(ContainerType); + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + alias opApplyIter opApply; + + private Node* newNode() { + static if (is(Alloc == GCAllocator)) { + return new Node; + } else { + Node* p = cast(Node*)Alloc.gcMalloc(Node.sizeof); + *p = Node.init; + return p; + } + } + + private void freeNode(Node* n) { + static if (is(Alloc == GCAllocator)) { + } else { + Alloc.gcFree(n); + } + } + + CircularList getThis(){return *this;} + mixin MListAlgo!(CircularList, getThis); + + mixin MCommonList!(tail_, CircularList ); +} + +// helper functions for adjusting length cache +private size_t increaseLength(size_t len, size_t x) { + return x ? (len? len+x : 0) : 0; +} +private size_t decreaseLength(size_t len, size_t x) { + return x ? (len? len-x : 0) : 0; +} + +// helper structure for backwards() +struct CircularListReverseIter(Value,bit ReadOnly,Alloc) { + mixin MReverseImpl!(List!(Value,ReadOnly,Alloc), + CircularList!(Value,ReadOnly,Alloc)); +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; + +version (MinTLUnittest) { + private import std.random; + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.list unittest\n"); + + List!(int) x; + x ~= 3; + x ~= 4; + assert( x[0] == 3 ); + assert( x[1] == 4 ); + assert( x.length == 2 ); + List!(int) x2 = List!(int).make(3,4); + assert( x == x2 ); + + List!(int) catt; + catt = List!(int).make(1,2,3) ~ List!(int).make(4,5,6); + assert( catt == List!(int).make(1,2,3,4,5,6) ); + + List!(int,false,MallocNoRoots) xm; + xm ~= 3; + xm ~= 4; + assert( xm[0] == 3 ); + assert( xm[1] == 4 ); + assert( xm.length == 2 ); + xm.clear(); + assert( xm.isEmpty ); + + List!(real) x2s; + x2s.add(cast(real)3,cast(real)4); + assert( x2s[0] == 3 ); + assert( x2s[1] == 4 ); + + // test addHead + List!(int) y; + y.addHead(4); + y.addHead(3); + + // test == + assert( x == y ); + List!(int) w = x.dup; + w ~= 5; + assert( x != w); + + // test remove/take + assert( w.takeTail() == 5 ); + size_t wlen = w.length; + w.addTail(6); + w.removeTail(); + assert( w.length() == wlen ); + assert( w == x ); + w.trim(); + w ~= 5; + + // test reverse lists + List!(int) z = y.dup; + z.reverse(); + assert( z[0] == 4 ); + assert( z[1] == 3 ); + + // test foreach iteration + foreach(size_t n, inout int val; z) { + val = n*10; + } + assert( z[0] == 0 ); + assert( z[1] == 10 ); + foreach(size_t n, int val; y.backwards()) { + assert(x[n] == val); + } + int n = 0; + foreach(List!(int) itr; y) { + assert(itr[0] == y[n++]); + } + + // test slicing + List!(int) v = w[1..3]; + assert( v.length == 2 ); + assert( v[0] == 4 ); + assert( v[1] == 5 ); + + // test readonly + List!(int,ReadOnly) rv = v.readonly; + assert( rv.length == 2 ); + assert( rv[0] == 4 ); + assert( rv[1] == 5 ); + assert( rv.head == rv[0 .. 1] ); + assert( rv.tail == rv[1 .. 2] ); + + // test algorithms + assert( v.opIn(5) == v.tail ); + assert( v.count(5) == 1 ); + assert( v.find(delegate int(inout int v){return v == 5;}) == v.tail ); + v[0 .. 1].swap(v[1..2]); + assert( v[0] == 5 ); + assert( v[1] == 4 ); + v.fill(10); + assert( v[0] == 10 ); + assert( v[1] == 10 ); + List!(int) vsub; + vsub.add(4,5); + v.copy(vsub); + assert( v[0] == 4 ); + assert( v[1] == 5 ); + + // test another node type + List!(char[]) str; + str.add("hello","world"); + assert( str[str.length-1] == "world" ); + + // test sub-list spanning + List!(int) tmp; + int[10] tmp2; + tmp2[3] = 100; + tmp2[8] = 200; + foreach(int xx;tmp2) + tmp ~= xx; + List!(int) a,b,c; + a = tmp[3..5]; + b = tmp[7..9]; + c = tmp[a..b]; + assert( c.length == 6 ); + assert( c[0] == 100 ); + assert( c[5] == 200 ); + + // CircularList testing + + CircularList!(int) cx; + cx ~= 3; + cx ~= 4; + assert( cx[0] == 3 ); + assert( cx[1] == 4 ); + assert( cx.length == 2 ); + + CircularList!(int) cx2; + cx2.add(3,4); + assert( cx == cx2 ); + + // test addHead + CircularList!(int) cy; + cy.addHead(4); + cy.addHead(3); + + // test == + assert( cx == cy ); + CircularList!(int) cw = cx.dup; + cw ~= 5; + assert( cx != cw); + + // test remove + assert( cw.takeTail() == 5 ); + wlen = cw.length; + cw.addTail(6); + cw.removeTail(); + assert( cw.length() == wlen ); + assert( cw == cx ); + cw ~= 5; + + // test reverse lists + CircularList!(int) cz = cy.dup; + cz.reverse(); + assert( cz[0] == 4 ); + assert( cz[1] == 3 ); + + // test foreach iteration + foreach(size_t n, inout int val; cz) { + val = n*10; + } + assert( cz[0] == 0 ); + assert( cz[1] == 10 ); + foreach(size_t n, int val; cy.backwards()) { + assert(cx[n] == val); + } + n = 0; + foreach(List!(int) itr; cy) { + assert(itr[0] == cy[n++]); + } + + // test slicing + List!(int) cv = w[1..3]; + assert( cv.length == 2 ); + assert( cv[0] == 4 ); + assert( cv[1] == 5 ); + + // test algorithms + assert( cv.opIn(5) == v.tail ); + assert( cv.count(5) == 1 ); + + // test another node type + CircularList!(char[]) cstr; + cstr.add("hello","world"); + assert( cstr[cstr.length-1] == "world" ); + + // test sub-list spanning + CircularList!(int,false,MallocNoRoots) ctmp; + tmp2[3] = 100; + tmp2[8] = 200; + foreach(int xx;tmp2) + ctmp ~= xx; + List!(int,false,MallocNoRoots) ca,cb,cc; + ca = ctmp[3..5]; + cb = ctmp[7..9]; + cc = ctmp[ca..cb]; + assert( cc.length == 6 ); + assert( cc[0] == 100 ); + assert( cc[5] == 200 ); + ctmp.clear(); + assert( ctmp.isEmpty ); + + // test simple sorting + List!(int) s1,s12; + s1.add(40,300,-20,100,400,200); + s12 = s1.dup; + s1.sort(); + List!(int) s2 = List!(int).make(-20,40,100,200,300,400); + //List!(int) s2 = s2.make(-20,40,100,200,300,400); + assert( s1 == s2 ); + // sort a slice in-place + s12[1..4].sort(); + s2.clear(); + s2.add(40,-20,100,300,400,200); + assert( s12 == s2 ); + + // test a large sort with default order + List!(double) s3; + for (int k=0;k<1000;k++) { + s3 ~= 1.0*rand()/100000.0 - 500000.0; + } + List!(double) s4 = s3.dup; + s3.sort(); + for (int k=0;k<999;k++) { + assert( s3[k] <= s3[k+1] ); + } + // test a large sort with custom order + int cmp(double*x,double*y){return *x>*y?-1:*x==*y?0:1;} + s4.sort(&cmp); + for (int k=0;k<999;k++) { + assert( s4[k] >= s4[k+1] ); + } + + version (MinTLVerboseUnittest) + printf("finished mintl.list unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/locks.html Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,490 @@ +<HTML> <head> <TITLE>Synchronization Locks Library for D</TITLE> </head> +<body> +<h1>Synchronization Locks for D</h1> +Locks is a library of synchronization constructs for the D +programming language based on the concurrent locks library +by Doug Lea. +For more info about D see <a +href="http://www.digitalmars.com/d/">DigitalMars D home page</a>. The +library can be downloaded +<a href="http://home.comcast.net/~benhinkle/locks/locks.zip">here</a> +or as part of +<a href="http://home.comcast.net/~benhinkle/mintl">MinTL</a>. +For more information about the Java library see +<a href="http://gee.cs.oswego.edu/dl/concurrency-interest/index.html"> +JSR-166</a>. For an initial port see +<a href="http://www.dsource.org/projects/concurrent"> dsource</a>. + +<p> +This library is in the public domain. +Portions written by Ben Hinkle, 2004, portions ported from code +written by Doug Lea. +Email comments and bug reports to ben.hinkle@gmail.com +<p> + +<h3> Overview</h3> + +The D language has builtin support for defining critical sections +using the <tt>synchronized</tt> statement but does not include +POSIX synchronization constructs like locks and condition variables. +The purpose of the Locks library is to extend the builtin D +capabilities to support not only POSIX constructs but also support +latches, barriers and exchangers. Concurrent containers like queues, +stacks and associative arrays are in the MinTL library in the +package <tt>mintl.concurrent</tt>. When using concurrent algorithms +be careful to use the <tt>volatile</tt> statement to ensure data +is properly updated in all the threads. + +<p> +The primary interface of the Locks library is the Lock interface. It +defines two methods, <tt>lock</tt> and <tt>unlock</tt>, that aquire +and release the lock. In general the Lock interface makes no +guarentee that a thread can <tt>lock</tt> a lock that it already +owns. The <tt>ReentrantLock</tt> class, which implements +<tt>Lock</tt>, does guarantee that the thread that hold the lock can +call <tt>lock</tt> without blocking. If a thread calls <tt>lock</tt> +and the lock is held by another thread then the calling thread is +parked until the lock is released. The <tt>tryLock</tt> functions +attempts to acquire the lock immediately or within a specified time interval. +For example a typical +class <tt>X</tt> that uses a <tt>ReentrantLock</tt> to control access +to function <tt>m</tt> uses try-finally blocks to insure the lock is +released before the function returns: + +<pre> + class X { + private ReentrantLock lock; + // ... + this() { + lock = new ReentrantLock; + } + void m() { + lock.lock(); // block until lock is available + try { + // ... method body + } finally { + lock.unlock() + } + } + } +</pre> +A <tt>ScopedLock</tt> can simplify the code around managing locks. +The class <tt>X</tt> could instead use a <tt>ScopedLock</tt> +in <tt>m</tt>: +<pre> + class X { + private ReentrantLock lock; + // ... + this() { + lock = new ReentrantLock; + } + void m() { + auto ScopedLock slock = new ScopedLock(lock); + // ... method body + } + } +</pre> + +The only difference between the two implementations is that the +<tt>ScopedLock</tt>, as written, will allocate memory from the GC each +time it is called. + +<p> +The Condition interface defines a condition variable for a given lock. +A condition variable allows two or more threads to hand-off ownership +of the lock atomically by calling <tt>wait</tt> and <tt>notify</tt>. +If a thread owns the lock and calls <tt>wait</tt> +on a condition variable then the thread releases the lock and +blocks until notified by the condition variable. +Once notified the thread attempts to acquire the lock and once successful +continues execution. The wait function accepts timeout values to stop +blocking after a certain amount of time. A thread that fails the timeout +still must reacquire the lock before proceeding. A typical use of condition +variables is to signal when an event has happened. +The function <tt>Lock.newCondition</tt> creates and returns a Condition +instance. +For example, the +condition below signals when the <tt>data</tt> variable has been set: +<pre> + int data; + Thread getter, setter; + ReentrantLock lock = new ReentrantLock; + Condition is_ready = lock.newCondition; + setter = new Thread( + delegate int() { + lock.lock(); + try { + data = 10; + is_ready.notify(); + } finally { + lock.unlock(); + } + return 0; + }); + getter = new Thread( + delegate int() { + lock.lock(); + try { + is_ready.wait(); + printf("%d\n",data); + } finally { + lock.unlock(); + } + return 0; + }); + getter.start(); + setter.start(); +</pre> + +<p> +To start several threads and have them wait until a signal from a +coordinating thread use a <tt>CountDownLatch</tt> with a count of +1. For example, +<pre> + CountDownLatch go = new CountDownLatch(1); + Thread[4] t; + for (int i=0; i < 4; i++) { + t[i] = new Thread( + delegate int() { + go.wait(); // wait for signal from main thread + // ... do something interesting ... + return 0; + }); + t[i].start(); + } + go.countDown(); // let worker threads go +</pre> +Conversely to signal a coordinating thread that the worker threads +are finished have each worker thread decrement another +<tt>CountDownLatch</tt>: +<pre> + CountDownLatch go = new CountDownLatch(1); + CountDownLatch allDone = new CountDownLatch(4); + Thread[4] t; + for (int i=0; i < 4; i++) { + t[i] = new Thread( + delegate int() { + go.wait(); // wait for signal from main thread + // ... do something interesting ... + allDone.countDown(); + return 0; + }); + t[i].start(); + } + go.countDown(); // let worker threads go + allDone.wait(); // wait for all workers to finish +</pre> + +<p> +A <tt>CyclicBarrier</tt> is similar to a <tt>CountDownLatch</tt> +except the cyclic barrier is used without a controlling thread. +A thread that reaches the barrier waits until the barrier count +is exhausted before continuing. Once the barrier is tripped it +optionally runs a function and resets to zero. Continuing the +example from the previous paragraph the worker threads might need +to rendezvous at a certain point mid-way through their task: +<pre> + CountDownLatch go = new CountDownLatch(1); + CountDownLatch allDone = new CountDownLatch(4); + CyclicBarrier barrier = new CyclicBarrier(4); + Thread[4] t; + for (int i=0; i < 4; i++) { + t[i] = new Thread( + delegate int() { + go.wait(); // wait for signal from main thread + // ... do something interesting ... + barrier.wait(); // wait for all workers to get to barrier + // ... do something else interesting ... + allDone.countDown(); + return 0; + }); + t[i].start(); + } + go.countDown(); // let worker threads go + allDone.wait(); // wait for all workers to finish +</pre> + +<p> +A <tt>Semaphore</tt> maintains a given number of permits. When a +thread acquires a permit the semaphore decremements the number of +available permits and when a thread releases the permit (any thread +can release the permit) the semaphore increments the number of +available permits. Semaphores don't have a concept of threads +owning permits - it only gives out and recieves permits atomically. +A typical use case for semaphores is to manage access by multiple +threads to a fixed collection of objects. + +<h3> API Reference</h3> +This section lists the public structs and functions in the library without +detailed explanation. For more information see the documentation before +the function or class in the source file. +The API is organized by module:<br> +<dl> +<dt><a href="#condition">locks.condition</a> +<dt><a href="#countdown">locks.countdown</a> +<dt><a href="#exchanger">locks.exchanger</a> +<dt><a href="#lock">locks.lock</a> +<dt><a href="#platformutils">locks.platformutils</a> +<dt><a href="#readwritelock">locks.readwritelock</a> +<dt><a href="#reentrantlock">locks.reentrantlock</a> +<dt><a href="#semaphore">locks.semaphore</a> +<dt><a href="#timeunit">locks.timeunit</a> +</dl> + + +<a name="condition"> +<h4>locks.condition</h4> +<dl> +<dt>interface <b>Condition</b> +<dd>A condition variable +<p> +<dl> +<dt>void <b>wait</b>() +<dd>Cause current thread to wait until notified +<dt>long <b>waitNanos</b>(long nanosTimeout) +<dd>Cause current thread to wait until notified or time expires +<dt>bool <b>wait</b>(long time, TimeUnit unit) +<dd>Cause current thread to wait until notified or time expires +<dt>void <b>notify</b>() +<dd>Wake up one waiting thread +<dt>void <b>notifyAll</b>() +<dd>Wake up all waiting threads +</dl> +</dl> + +<a name="countdown"> +<h4>locks.countdown</h4> +<dl> +<dt>class <b>CountDownLatch</b> +<dd>Allow one or more threads to wait for a set of other threads. +<p> +<dl> +<dt><b>this</b>(int count) +<dd>Construct the latch with the given count before releasing +<dt>void <b>wait</b>() +<dd>Causes the current thread to wait until the count reaches zero +<dt>void <b>wait</b>(long timeout, TimeUnit unit) +<dd>Causes the current thread to wait until the count reaches zero or time expires +<dt>void <b>countDown</b>() +<dd>Decrement count +<dt>long <b>count</b> +<dd>Get the current count +<dt>char[] <b>toString</b> +<dd>Return a string summary of the latch +</dl> +</dl> + +<a name="cyclicbarrier"> +<h4>locks.cyclicbarrier</h4> +<dl> +<dt>class <b>CyclicBarrier</b> +<dd>Allow a fixed group of threads to wait for each other +<p> +<dl> +<dt><b>this</b>(int parties, int delegate() barrierAction = null) +<dd>Construct the barrier with given number of parties and concluding action +<dt>int <b>parties</b> +<dd>Return number of parties for this barrier +<dt>int <b>wait</b>() +<dd>Causes the current thread to wait for all parties to reach the barrier +<dt>int <b>wait</b>(long timeout, TimeUnit unit) +<dd>Causes the current thread to wait only for the specified time +<dt>bool <b>isBroken</b>() +<dd>Returns true if the barrier has been broken +<dt>void <b>reset</b>() +<dd>Break the barrier for waiting parties and reset to initial state +<dt>int <b>getNumberWaiting</b> +<dd>Get the current number of waiting parties +</dl> +</dl> + +<a name="exchanger"> +<h4>locks.exchanger</h4> +<dl> +<dt>class <b>Exchanger</b>(Value) +<dd>Allow two threads to safely exchange values. +<p> +<dl> +<dt><b>this</b>() +<dd>Construct the exchanger +<dt>Value <b>exchange</b>(Value v) +<dd>Offer v for exchange and wait for response +<dt>Value <b>exchange</b>(Value v, long timeout, TimeUnit unit) +<dd>Offer v for exchange and wait for response with possible timeout +</dl> +</dl> + +<a name="lock"> +<h4>locks.lock</h4> +<dl> +<dt>interface <b>Lock</b> +<dd>The interface for all lock implementations. +<p> +<dl> +<dt>void <b>lock</b>() +<dd>Acquires the lock +<dt>bool <b>tryLock</b>() +<dd>Acquires the lock only if it is free at the time of invocation +<dt>bool <b>tryLock</b>(long time, TimeUnit unit) +<dd>Acquires the lock if it is free within the given waiting time +<dt>void <b>unlock</b>() +<dd>Releases the lock +<dt>Condition <b>newCondition</b> +<dd>Returns a new Condition instance that is bound to this lock instance +</dl> +</dl> +<dl> +<dt>auto final class <b>ScopedLock</b> +<dd>An auto class for aquiring and releasing a lock in a scope +<p> +<dl> +<dt><b>this</b>(Lock lock) +<dd>Initializes the ScopedLock and acquires the supplied lock +<dt><b>~this</b>() +<dd>Release the lock +</dl> +</dl> + +<a name="platformutils"> +<h4>locks.platformutils</h4> +<dl> +<dt>bit <b>compareAndSet32</b>(void* mem, void* expect, void* update) +<dd>Compare the 32 bit value expect with the value at *mem and if equal +set to update and return true. This assumes a pointer is 32 bits. +<dt>bit <b>compareAndSet32</b>(void* mem, int expect, int update) +<dd>Convenience overload for compareAndSet32 when the data are integers +instead of pointers +<dt>bit <b>compareAndSet64</b>(void* mem, void* expect, void* update) +<dd>Compare the 64 bit value at *expect with the value at *mem and if equal +set to *update and return true. +<dt>int <b>atomicAdd32</b>(int* val, int x); +<dd>Atomically add x to *val and return previous value of *val +<dt>int <b>atomicExchange32</b>(int* val, int x); +<dd>Atomically store x to *val and return previous value of *val +<dt>void <b>atomicInc32</b>(int* val); +<dd>Atomically increment *val +<dt>void <b>atomicDec32</b>(int* val); +<dd>Atomically decrement *val +<dt>long <b>currentTimeMillis</b>() +<dd>Return the current system time in milliseconds +<dt>long <b>currentTimeNanos</b>() +<dd>Return the current system time in nanoseconds +<dt>void <b>sleepNanos</b>(long duration) +<dd>Sleep the current thread for the specified duration in nanoseconds +</dl> + +<a name="readwritelock"> +<h4>locks.readwritelock</h4> +<dl> +<dt>interface <b>ReadWriteLock</b> +<dd>A pair of read-write locks +<p> +<dl> +<dt>Lock <b>readLock</b>() +<dd>Return the read lock +<dt>Lock <b>writeLock</b>() +<dd>Return the write lock +</dl> +</dl> +<p> +<dl> +<dt>class <b>ReentrantReadWriteLock</b> : ReadWriteLock +<dd>A pair of reentrant read-write locks +<p> +<dl> +<dt><b>this</b>(bool fair = false) +<dd>Construct the lock with specified fairness policy +<dt>Lock <b>readLock</b>() +<dd>Return the read lock +<dt>Lock <b>writeLock</b>() +<dd>Return the write lock +</dl> + +</dl> + +<a name="reentrantlock"> +<h4>locks.reentrantlock</h4> +<dl> +<dt>class <b>ReentrantLock</b> : Lock +<dd>A reentrant mutual exclusive lock with condition variables +<p> +<dl> +<dt><b>this</b>(bool fair = false) +<dd>Construct the lock with specified fairness policy +<dt>void <b>lock</b>() +<dd>Acquires the lock +<dt>bool <b>tryLock</b>() +<dd>Acquires the lock only if it is free at the time of invocation +<dt>bool <b>tryLock</b>(long time, TimeUnit unit) +<dd>Acquires the lock if it is free within the given waiting time +<dt>void <b>unlock</b>() +<dd>Releases the lock +<dt>Condition <b>newCondition</b> +<dd>Returns a new Condition instance that is bound to this lock instance +<dt>int <b>getHoldCount</b>() +<dd>Get the number of holds on this lock by the current thread +<dt>bool <b>isHeldByCurrentThread</b>() +<dd>Query if the lock is held by the current thread +<dt>bool <b>isLocked</b>() +<dd>Query if the lock is held by any thread +<dt>bool <b>isFair</b>() +<dd>Query if the lock is fair +<dt>char[] <b>toString</b>() +<dd>return a string representation of the lock +</dl> + +</dl> + +<a name="semaphore"> +<h4>locks.semaphore</h4> +<dl> +<dt>class <b>Semaphore</b> +<dd>A counting semaphore for maintaining a set of permits +<p> +<dl> +<dt><b>this</b>(int permits, bool fair = false) +<dd>Construct the semaphore with the given number of permits and fairness policy +<dt>void <b>acquire</b>(int permits = 1) +<dd>Acquires n permits, blocking until all are available +<dt>bool <b>tryAcquire</b>(int permits = 1) +<dd>Acquires n permit from this semaphore only if they are immediately available +<dt>bool <b>tryAcquire</b>(long timeout, TimeUnit unit, int permits = 1) +<dd>Attempt acquiring n permits within the specified time interval +<dt>void <b>release</b>(int permits = 1) +<dd>Release n permits +<dt>int <b>availablePermits</b> +<dd>Get the current number of available permits +<dt>bool <b>isFair</b>() +<dd>return true if the semaphore is fair +<dt>char[] <b>toString</b> +<dd>Return a string summary of the semaphore +</dl> + +</dl> + +<a name="timeunit"> +<h4>locks.timeunit</h4> +<dl> + <dt>enum <b>TimeUnit</b> + <dd>Time units common in synchronization + <dl> + <dt><b>NanoSeconds = 0</b> + <dt><b>MicroSeconds</b> + <dt><b>MilliSeconds</b> + <dt><b>Seconds</b> + </dl> + <p> +<dt>long <b>convert</b>(long duration, TimeUnit fromUnit, TimeUnit toUnit); +<dd>Convert the given time duration in the given unit to this unit. +<dt>long <b>toNanos</b>(long duration, TimeUnit fromUnit); +<dd>Convert to nanoseconds. +<dt>long <b>toMicros</b>(long duration, TimeUnit fromUnit); +<dd>Convert to microseconds. +<dt>long <b>toMillis</b>(long duration, TimeUnit fromUnit); +<dd>Convert to milliseconds. +<dt>long <b>toSeconds</b>(long duration, TimeUnit fromUnit); +<dd>Convert to seconds. +</dl> + +</BODY> +</HTML>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/mem.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,129 @@ +/** \file mem.d + * \brief Allocators for custom container memory management + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * version 1.0 + */ + +module mintl.mem; + +private { + import tango.stdc.stdlib; + import tango.core.Memory : GC; + import tango.core.Exception : onOutOfMemoryError; +} + +/** An Allocator is a type containing 8 symbols malloc, calloc, + * realloc, free and the corresponding GC-aware versions gcMalloc, + * gcCalloc, gcRealloc and gcFree. Containers will call the GC-aware + * functions on blocks that may hold roots and otherwise will call the + * regular functions. Allocators are expected to throw OutOfMemory if + * the allocation fails. Be aware than when using an allocator with + * a container one must call the container <tt>clear()</tt> function + * to free the memory. + * + * The two predefined allocators Malloc and MallocNoRoots use + * std.c.stdlib.malloc to perform allocations. The MallocNoRoots + * ignores any requests by the container to register roots with the + * GC. The MallocNoRoots allocator should only be used with containers + * that the user knows will never contain any roots (e.g. ArrayList!(int)) + */ + +/** Malloc and throw OutOfMemory if fails. */ +void* mallocWithCheck(size_t s) { + void* p = malloc(s); + if (!p) + onOutOfMemoryError(); + return p; +} + +/** Calloc and throw OutOfMemory if fails. */ +void* callocWithCheck(size_t n, size_t s) { + void* p = calloc(n,s); + if (!p) + onOutOfMemoryError(); + return p; +} + +/** Realloc and throw OutOfMemory if fails. */ +void* reallocWithCheck(void*p, size_t s) { + p = realloc(p,s); + if (!p) + onOutOfMemoryError(); + return p; +} + +/** Free pointer. */ +void dfree(void*p) { + free(p); +} + +/** Malloc and register the range with GC. */ +void* gcMalloc(size_t s) { + void* p = mallocWithCheck(s); + GC.addRange(p,s); + return p; +} + +/** Calloc and register the range with GC. */ +void* gcCalloc(size_t n, size_t s) { + void* p = callocWithCheck(n,s); + GC.addRange(p,n*s); + return p; +} + +/** Realloc and register the range with GC. */ +void* gcRealloc(void* p, size_t s) { + if (p) + GC.removeRange(p); + p = reallocWithCheck(p,s); + GC.addRange(p,s); + return p; +} + +/** Deregister the range with GC and free. */ +void gcFree(void* p) { + if (p) + GC.removeRange(p); + free(p); +} + +// Default Allocator +struct GCAllocator{ + alias void malloc; + alias void calloc; + alias void realloc; + alias void free; + alias void gcMalloc; + alias void gcCalloc; + alias void gcRealloc; + alias void gcFree; +} + +// An allocator that uses malloc +struct Malloc { + alias mallocWithCheck malloc; + alias callocWithCheck calloc; + alias reallocWithCheck realloc; + alias dfree free; + alias .gcMalloc gcMalloc; + alias .gcCalloc gcCalloc; + alias .gcRealloc gcRealloc; + alias .gcFree gcFree; +} + +// An allocator that uses malloc and assumes allocations have no roots +struct MallocNoRoots { + alias mallocWithCheck malloc; + alias callocWithCheck calloc; + alias reallocWithCheck realloc; + alias dfree free; + alias mallocWithCheck gcMalloc; + alias callocWithCheck gcCalloc; + alias reallocWithCheck gcRealloc; + alias dfree gcFree; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/multiaa.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,191 @@ +/** \file multiaa.d + * \brief An associative array that allows multiple values per key. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * The red-black tree code is by Thomas Niemann. + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 1.2 + */ + +module mintl.multiaa; + +private import mintl.share; +private import mintl.adapter; +private import mintl.sortedaa; +private import mintl.hashaa; +private import std.stdarg; +private import std.boxer; + +//version = WithBox; + +/** An associative array of items with duplicate keys. + * By default the backing container is a builtin associative array. + */ +struct MultiAA(Key, Value, ImplType = HashAA!(Key,Value[])) { + + alias MultiAA ContainerType; + alias Value ValueType; + alias Key IndexType; + alias ImplType AdaptType; + const bit isReadOnly = ImplType.isReadOnly; + + ImplType impl; + + size_t length() { + size_t total = 0; + foreach(Value[] val; impl) { + total += val.length; + } + return total; + } + int opEquals(MultiAA c) { return impl == c.impl; } + static if (!ImplType.isReadOnly) { + void remove(Key key) { + impl.remove(key); + } + void remove(Key key, Value val) { + Value[]* vals = impl.get(key); + if (vals) { + size_t k; + Value[] x = *vals; + for(k = 0; k<x.length;k++) { + if (x[k] == val) + break; + } + for(; k < x.length-1; k++) { + x[k] = x[k+1]; + } + *vals = x[0 .. k]; + } + } + void addItem(Key key, Value item) { + Value[]* vals = impl.put(key); + (*vals) ~= item; + } + void clear(){ impl.clear(); } + } // !isReadOnly + Value[] opIndex(Key key) {return impl[key];} + MultiAA dup() { + MultiAA res; + res.impl = impl.dup; + return res; + } + bool isEmpty() { return impl.isEmpty(); } + // mixin MMAASpecial!(impl,MultiAA,Key,Value,ImplType) mAA; + static if (!ImplType.isReadOnly) { + /** Inserts the specified items into the target AA by calling + * x[key]=value repeatedly. + */ + void add(...) { + vadd(_arguments,_argptr); + } + void vadd(TypeInfo[] arguments, void* argptr) { + for (int k=0;k<arguments.length;k++) { + TypeInfo tik = typeid(Key); + if (arguments[k] == tik) { + Key key = va_arg!(Key)(argptr); + k++; + addItem(key,va_arg!(Value)(argptr)); + } else { + version(WithBox) { + Box b = box(tik,argptr); + Key key = unbox!(Key)(b); + k++; + TypeInfo tiv = arguments[k]; + b = box(tiv,argptr); + addItem(key,unbox!(Value)(b)); + argptr += va_argumentLength(tiv.tsize()); + } + } + } + } + /** Construct a container with specified contents */ + static MultiAA make(...) { + MultiAA res; + res.vadd(_arguments,_argptr); + return res; + } + } + + Key[] keys() { return impl.keys; } + Value[][] values() { return impl.values; } + int opApply(int delegate(inout Value x) dg){ + int res; +L0: foreach(inout Value[] item; impl) { + foreach(inout Value val; item) { + res = dg(val); + if (res) break L0; + } + } + return res; + } + int opApply(int delegate(inout Key key, inout Value x) dg){ + int res; +L1: foreach(Key key, inout Value[] item; impl) { + foreach(inout Value val; item) { + res = dg(key,val); + if (res) break L1; + } + } + return res; + } +} + +// Adapter for a sorted MultiAA +template SortedMultiAA(Key,Value) { + alias MultiAA!(Key,Value,SortedAA!(Key,Value[])) SortedMultiAA; +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; +version (MinTLUnittest) { + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.multiaa unittest\n"); + + // test MultiSet + MultiAA!(int,char[]) ma2; + ma2.add(22,"hello",-100,"there",22,"world"); + static char[][2] res = ["hello","world"]; + static char[][1] res2 = ["there"]; + char[][] vv = ma2[22]; + assert( ma2[22].length == 2); + assert( ma2[22] == res); + assert( ma2[-100] == res2); + int count22; + int count; + foreach( int key, char[] item; ma2 ) { + if (key == 22) { + count22++; + } else { + count++; + } + } + assert( count22 == 2 ); + assert( count == 1 ); + ma2.remove(-100); + assert( ma2.length == 2 ); + ma2.remove(22,"hello"); + static char[][1] res3 = ["world"]; + assert( ma2[22] == res3); + assert( ma2.length == 1 ); + ma2.remove(22); + assert( ma2.length == 0 ); + assert( ma2.isEmpty ); + + // test SortedMultiSet + SortedMultiAA!(char[],int) s3; + s3.add("hello",10,"world",20); + s3.addItem("hello",40); + int[] vals = s3["hello"]; + assert( s3["hello"].length == 2); + assert( vals[0] == 10 && vals[1] == 40 ); + vals = s3["world"]; + assert( vals.length == 1 && vals[0] == 20 ); + + version (MinTLVerboseUnittest) + printf("finished mintl.multiaa unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/queue.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,97 @@ +/** \file queue.d + * \brief A queue container + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 1.1 + */ + +module mintl.queue; + +private import mintl.deque; +private import mintl.arraylist; +private import mintl.arrayheap; +import mintl.adapter; +import mintl.share; + +/** A queue of items of stype Value backed by a container of type ImplType. + * Aliases put and take allow queue operations. By default the queue is + * backed by a Deque. + */ +struct Queue(Value, ImplType = Deque!(Value)) { + + alias Queue ContainerType; + alias Value ValueType; + alias size_t IndexType; + alias ImplType AdaptType; + const bit isReadOnly = ImplType.isReadOnly; + + ImplType impl; + + mixin MAdaptBuiltin!(impl,Queue); + mixin MAdaptBasic!(impl,Queue); + mixin MAdaptList!(impl,Queue); + mixin MListCatOperators!(Queue); + + // Queue specific + static if (!ImplType.isReadOnly) { + alias addTail put; + alias takeHead take; + } + Value peek() { + return impl.isEmpty ? Value.init : impl[0]; + } +} + +/** Convenience alias for a queue backed by an array */ +template ArrayQueue(Value) { + alias Queue!(Value,ArrayList!(Value)) ArrayQueue; +} + +/** Convenience alias for a queue backed by a heap */ +template PriorityQueue(Value) { + alias Queue!(Value,ArrayHeap!(Value)) PriorityQueue; +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; + +version (MinTLUnittest) { + import mintl.list; + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.queue unittest\n"); + + Queue!(int) q; + q ~= 10; + q ~= 20; + assert( q.peek == 10 ); + assert( q.take == 10 ); + assert( q[0] == 20 ); + assert( q.take == 20 ); + assert( q.length == 0 ); + + ArrayQueue!(int) st2; + st2.put(10); + st2 ~= 20; + assert( st2.peek == 10 ); + assert( st2.take == 10 ); + assert( st2[0] == 20 ); + assert( st2.take == 20 ); + assert( st2.length == 0 ); + + Queue!(int,List!(int)) st3; + st3.put(10); + st3 ~= 20; + assert( st3.peek == 10 ); + assert( st3.take == 10 ); + assert( st3[0] == 20 ); + assert( st3.take == 20 ); + assert( st3.length == 0 ); + + version (MinTLVerboseUnittest) + printf("finished mintl.queue unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/set.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,223 @@ +/** \file set.d + * \brief Set, sorted set and multi-set containers. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.7.1 + */ + +module mintl.set; + +private import mintl.share; +private import mintl.adapter; +private import mintl.sortedaa; +private import mintl.hashaa; +private import std.stdarg; +private import std.boxer; + +//version = WithBox; + +template MAddSet(Container,Value) { + /** Inserts the specified items into the set. */ + void add(...) { + vadd(_arguments,_argptr); + } + void vadd(TypeInfo[] arguments, void* argptr) { + for (int k=0;k<arguments.length;k++) { + TypeInfo tik = typeid(Value); + if (arguments[k] == tik) { + addItem(va_arg!(Value)(argptr)); + } else { + version(WithBox) { + Box b = box(tik,argptr); + addItem(unbox!(Value)(b)); + argptr += va_argumentLength(tik.tsize()); + } + } + } + } + /** Construct a container with specified contents */ + static Container make(...) { + Container res; + res.vadd(_arguments,_argptr); + return res; + } +} + +/** A set of items. By default the backing container is a HashAA + * associative array. + */ +struct Set(Value, ImplType = HashAA!(Value,uint)) { + + alias Set ContainerType; + alias Value ValueType; + alias ImplType AdaptType; + const bit isReadOnly = ImplType.isReadOnly; + + ImplType impl; + + mixin MAdaptBuiltin!(impl,Set); + static if (!ImplType.isReadOnly) { + void remove(Value item) { impl.remove(item); } + } + bool opIndex(Value item) {return impl.contains(item); } + Set dup() { + Set res; + res.impl = impl.dup; + return res; + } + void clear(){ impl.clear(); } + bool isEmpty() { return impl.isEmpty(); } + static if (!ImplType.isReadOnly) { + mixin MAddSet!(Set,Value) mAdd; + } + + Value[] values() { return impl.keys; } + void addItem(Value item) { impl[item] = 1; } + int opApply(int delegate(inout Value x) dg){ + int res; + foreach(Value item, uint ignore; impl) { + res = dg(item); + if (res) break; + } + return res; + } +} + +/** Adapter for sorted set of items. */ +template SortedSet(Value) { + alias Set!(Value,SortedAA!(Value,uint)) SortedSet; +} + +/** A set of items with repeats. By default the backing container is a + * HashAA associative array. + */ +struct MultiSet(Value, ImplType = HashAA!(Value,uint)) { + + alias Set ContainerType; + alias Value ValueType; + alias ImplType AdaptType; + static if (is(ImplType:uint[Value])) { + const bit isReadOnly = false; + } else { + const bit isReadOnly = ImplType.isReadOnly; + } + + ImplType impl; + + size_t length() { + size_t total = 0; + foreach(uint val; impl) { + total += val; + } + return total; + } + int opEquals(MultiSet c) { return impl == c.impl; } + static if (!ImplType.isReadOnly) { + void remove(Value item) { + uint* val = impl.get(item); + if (val && (--(*val) == 0)) + impl.remove(item); + } + void addItem(Value item) { + (*impl.put(item))++; + } + } + bool opIndex(Value item) {return impl.get(item) !is null; } + MultiSet dup() { + MultiSet res; + res.impl = impl.dup; + return res; + } + void clear(){ impl.clear(); } + bool isEmpty() { return impl.isEmpty(); } + static if (!ImplType.isReadOnly) { + mixin MAddSet!(MultiSet, Value) mAdd; + } + + Value[] values() { return impl.keys; } + int opApply(int delegate(inout Value x) dg){ + int res; + foreach(Value item, uint val; impl) { + while (val--) { + res = dg(item); + if (res) break; + } + } + return res; + } +} + +/** Adapter for sorted multi-set. */ +template SortedMultiSet(Value) { + alias MultiSet!(Value,SortedAA!(Value,uint)) SortedMultiSet; +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; +version (MinTLUnittest) { + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.set unittest\n"); + + // test Set + Set!(char[]) s; + s.add("hello","world"); + assert( s["world"] ); + assert( s["hello"] ); + assert( !s["worldfoo"] ); + foreach(char[] val ; s) { + version (MinTLVerboseUnittest) + printf("%.*s\n",val); + } + + // test SortedSet + SortedSet!(char[]) s2; + s2.add("hello","world"); + assert( s2["world"] ); + assert( s2["hello"] ); + assert( !s2["worldfoo"] ); + foreach(char[] val ; s2) { + version (MinTLVerboseUnittest) + printf("%.*s\n",val); + } + assert( !s2.isEmpty ); + + // test MultiSet + MultiSet!(int) ma2; + ma2.add(22,-100,22); + assert( ma2[22] ); + assert( ma2[-100] ); + int count22; + int count; + foreach( int item; ma2 ) { + if (item == 22) { + count22++; + } else { + count++; + } + } + assert( count22 == 2 ); + assert( count == 1 ); + ma2.remove(-100); + assert( ma2.length == 2 ); + ma2.remove(22); + assert( ma2[22] ); + assert( ma2.length == 1 ); + ma2.remove(22); + assert( ma2.length == 0 ); + assert( ma2.isEmpty ); + + // test SortedMultiSet + SortedMultiSet!(char[]) s3; + s3.add("hello","world"); + assert( s3["world"] ); + assert( s3["hello"] ); + assert( !s3["worldfoo"] ); + + version (MinTLVerboseUnittest) + printf("finished mintl.set unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/share.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,289 @@ +/** \file share.d + * \brief Mixin templates and exceptions shared between modules. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + */ + +module mintl.share; + +public import tango.core.Vararg; +import tango.core.Traits : ParameterTupleOf; +//public import std.boxer; + +alias bool bit; +const bit ReadOnly = true; + +//version = WithBox; + +/** Return the length of an argument in bytes. */ +size_t va_argumentLength(size_t baseLength) +{ + return (baseLength + int.sizeof - 1) & ~(int.sizeof - 1); +} + +/** A mixin for overloading ~, ~=, and add + * Assumes List class implements dup, addTail, addHead + */ +template MListCatOperators(List) { + + /** Appends the specified items to the tail of the target list by + * calling <tt>addTail</tt> repeatedly. + */ + void add(...) { + vadd(_arguments,_argptr); + } + void vadd(TypeInfo[] arguments, void* argptr) { + for (int k=0;k<arguments.length;k++) { + TypeInfo tiv = typeid(List.ValueType); + TypeInfo tik = arguments[k]; + if (tik is tiv) { + addTail(va_arg!(List.ValueType)(argptr)); + } else { + version (WithBox) { + Box b = box(tik,argptr); + addTail(unbox!(List.ValueType)(b)); + argptr += va_argumentLength(tik.tsize()); + } else { + throw new Exception("illegal add argument"); + } + } + } + } + + /** Construct a list with specified contents */ + static List make(...) { + List res; + res.vadd(_arguments,_argptr); + return res; + } + + /** Add a value N times */ + void addN(uint n, List.ValueType v) { + while (n--) + addTail(v); + } + + /** Appends an item to the tail of the target list. If the target + * list is a sub-list call addAfter instead of ~= to insert an item + * after a sub-list. + */ + List opCatAssign(List.ValueType v) { + addTail(v); + return *this; + } + + /** Appends an item to the tail of a duplicate of the target list. */ + List opCat(List.ValueType v) { + List res = this.dup; + res.addTail(v); + return res; + } + + /** Appends a list to the tail of the target list. */ + List opCatAssign(List v) { + addTail(v.dup); + return *this; + } + + /** Appends a duplicate of the input list to the tail of a duplicate + * of the target list. + */ + List opCat(List v) { + List res = this.dup; + res.addTail(v.dup); + return res; + } + + /** Appends an item to the tail of a duplicate of the target list. */ + List opCat_r(List.ValueType v) { + List res = this.dup; + res.addHead(v); + return res; + } +} + +/** \class IndexOutOfBoundsException + * \brief An exception thrown when attempting to index past the head + * or tail of a list or when attempting to remove an element from an + * empty list. + */ +class IndexOutOfBoundsException: Exception { + this(char[] str) { super(str); } + this() { super("Index out of bounds"); } +} + +/** A mixin for associative array add function */ +template MAddAA(AA) { + + /** Inserts the specified items into the target AA by calling + * x[key]=value repeatedly. + */ + void add(...) { + vadd(_arguments,_argptr); + } + void vadd(TypeInfo[] arguments, void* argptr) { + for (int k=0;k<arguments.length;k++) { + TypeInfo tik = typeid(AA.IndexType); + if (arguments[k] == tik) { + // workaround for GNU compilers using std.c.stdarg + alias ParameterTupleOf!(va_arg!(AA.IndexType))[0] VaList; + VaList vaList = cast(VaList)argptr; + AA.IndexType key = va_arg!(AA.IndexType)(vaList); + k++; + (*this)[key] = va_arg!(AA.ValueType)(vaList); + argptr = cast(void*)vaList; + } else { + version (WithBox) { + Box b = box(tik,argptr); + AA.IndexType key = unbox!(AA.IndexType)(b); + k++; + TypeInfo tiv = arguments[k]; + b = box(tiv,argptr); + (*this)[key] = unbox!(AA.ValueType)(b); + argptr += va_argumentLength(tiv.tsize()); + } else { + throw new Exception("illegal add argument"); + } + } + } + } + + /** Construct a container with specified contents */ + static AA make(...) { + AA res; + res.vadd(_arguments,_argptr); + return res; + } +} + +/** Mixin template for defining opApply variations */ +template MOpApplyImpl(Container) { + + int opApplyNoKey(int delegate(inout Container.ValueType n) dg){ + return opApplyNoKeyStep(dg); + } + + int opApplyWithKey(int delegate(inout Container.IndexType n, inout Container.ValueType x) dg){ + return opApplyWithKeyStep(dg); + } + + int opApplyIter(int delegate(inout Container.SliceType n) dg){ + return opApplyIterStep(dg); + } + + int opApplyBackwards(int delegate(inout Container.ValueType x) dg){ + return opApplyNoKeyStep(dg,-1); + } + + int opApplyWithKeyBackwards(int delegate(inout Container.IndexType n, inout Container.ValueType x) dg){ + return opApplyWithKeyStep(dg,-1); + } + + int opApplyIterBackwards(int delegate(inout Container.SliceType x) dg){ + return opApplyIterStep(dg,-1); + } + +} + +/** Mixin template for defining opApply variations for + * backward iteration. Use in conjunction with mixing in + * MOpApplyHelpers into the primary structure. + */ +template MReverseImpl(Iter,Container = Iter) { + Container* list; + + int opApply(int delegate(inout Iter.ValueType x) dg){ + return list.opApplyNoKeyStep(dg,-1); + } + + int opApply(int delegate(inout Iter.IndexType n, inout Iter.ValueType x) dg){ + return list.opApplyWithKeyStep(dg,-1); + } + + int opApply(int delegate(inout Iter x) dg){ + return list.opApplyIterStep(dg,-1); + } +} + +/** Mixin for list algorithms */ +template MListAlgo(Container, alias list) { + + // return first occurrence of v + Container.SliceType opIn(Container.ValueType v) { + Container.SliceType res; + TypeInfo ti = typeid(Container.ValueType); + foreach(Container.SliceType i; list) { + Container.ValueType iv = i.value; + if (ti.equals(&v,&iv)) { + res = i; + break; + } + } + return res; + } + + // count number of occurrences of v + uint count(Container.ValueType v) { + uint res; + TypeInfo ti = typeid(Container.ValueType); + foreach(inout Container.ValueType val; list) { + if (ti.equals(&v,&val)) + res++; + } + return res; + } + + static if (!Container.isReadOnly) { + // swap values with v + void swap(Container v) { + if (v.isEmpty) return; + Container.SliceType jend = v.tail; + Container.SliceType j = v.head; + TypeInfo ti = typeid(Container.ValueType); + foreach(Container.SliceType i; list) { + Value v = i.value; + i.value = j.value; + j.value = v; + if (j == jend) break; + j.next(); + } + } + + // fill the container with a value + void fill(Container.ValueType v) { + foreach(inout Container.ValueType val; list) { + val = v; + } + } + + // copy the contents of v to this container + void copy(Container v) { + if (v.isEmpty) return; + Container.SliceType i = v.head; + Container.SliceType j = v.tail; + foreach(inout Container.ValueType val; list) { + val = i.value; + if (i == j) break; + i.next(); + } + } + + } // !isReadOnly + + // find first occurrence where delegate is true + Container.SliceType find(int delegate(inout Value v) dg) { + Container.SliceType res; + TypeInfo ti = typeid(Container.ValueType); + foreach(Container.SliceType i; list) { + Container.ValueType v = i.value; + if (dg(v)) { + res = i; + break; + } + } + return res; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/slist.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,882 @@ +/** \file slist.d + * \brief A singly-linked list and circular singly-linked list. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.7.1 + */ + +module mintl.slist; + +private import mintl.share; // for ~ and ~= and SNode +import mintl.mem; + +// shared data structure between SList and CircularSList +private struct SNode(Value) { + SNode* next; + Value data; +} + +/** Template for member functions common to SList and CircularSList */ +template MCommonSList(alias head_, Container ) { + + /** Get the length of list. This operation is O(n) where n is + * the resulting length. + */ + size_t length() { + Container.Node* p = head_; + if (p is null) + return 0; + size_t len = 1; + while (p !is tail_) { + p = p.next; + len++; + } + return len; + } + + /** Test if container is empty. */ + bool isEmpty() { + return head_ is null; + } + + /** helper function to check if the index is legal. */ + void boundsCheck(Container.Node* p) { + version (MinTLNoIndexChecking) { + } else { + if (p is null) { + throw new IndexOutOfBoundsException(); + } + } + } + + /* Internal function to get the nth item of the list. */ + package Container.Node* getNode(size_t n) { + boundsCheck(head_); + Container.Node* p = head_; + while (n--) { + p = p.next; + boundsCheck(p); + } + return p; + } + + /** Get the nth item in the list from head. The operation is O(n). + * To efficiently access the tail of the list use the <tt>tail</tt> + * property. + * Indexing out of bounds throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + Container.ValueType opIndex(size_t n) { + return getNode(n).data; + } + + static if (!Container.isReadOnly) { + + /** Get a pointer to the nth item in the list from head. The + * operation is O(n). To efficiently access the tail of the list + * use the <tt>tail</tt> property. Indexing out of bounds throws an + * IndexOutOfBoundsException unless version=MinTLNoIndexChecking is + * set. + */ + Container.ValueType* lookup(size_t n) { + return &getNode(n).data; + } + + /** Set the nth item in the list from head. The operation is O(n). + * To efficiently access the tail of the list use the <tt>tail</tt> + * property. + * Indexing out of bounds throws an IndexOutOfBoundsException unless + * version=MinTLNoIndexChecking is set. + */ + void opIndexAssign(Container.ValueType val, size_t n) { + getNode(n).data = val; + } + + } // !ReadOnly + + /** Iterates over the list from head to tail calling delegate to + * perform an action. The value is passed to the delegate. + */ + int opApplyNoKey(int delegate(inout Container.ValueType x) dg){ + int dg2(inout size_t count, inout Container.ValueType val) { + return dg(val); + } + return opApplyWithKey(&dg2); + } + + /** Iterates over the list from head to tail calling delegate to + * perform an action. The index from 0 and the value are passed + * to the delegate. + */ + int opApplyWithKey(int delegate(inout size_t n, inout Container.ValueType x) dg){ + Container.Node* i = head_; + Container.Node* end = tail_; + int res = 0; + size_t n = 0; + while (i !is null) { + res = dg(n, i.data); + if (res || i is end) break; + n++; + i = i.next; + } + return res; + } + + /** Iterates over the list from head to tail calling delegate to + * perform an action. A one-item sub-list is passed to the delegate. + */ + int opApplyIter(int delegate(inout Container.SliceType n) dg){ + Container.Node* i = head_; + Container.Node* end = tail_; + int res = 0; + Container.SliceType n; + while (i !is null) { + n.head_ = n.tail_ = i; + res = dg(n); + if (res || i is end) break; + i = i.next; + } + return res; + } + + /** Test for equality of two lists. The operation is O(n) where n + * is length of the list. + */ + int opEquals(Container c) { + Container.Node* i = head_; + Container.Node* j = c.head_; + Container.Node* t = tail_; + Container.Node* ct = c.tail_; + TypeInfo ti = typeid(Container.ValueType); + while (i !is null && j !is null) { + if (!ti.equals(&i.data,&j.data)) + return 0; + if (i is t && j is ct) + return 1; + i = i.next; + j = j.next; + } + return (i is null && j is null); + } + + /** Compare two lists. */ + int opCmp(Container c) { + Container.Node* i = head_; + Container.Node* j = c.head_; + Container.Node* t = tail_; + Container.Node* ct = c.tail_; + TypeInfo ti = typeid(Container.ValueType); + while (i !is null && j !is null) { + int cmp = ti.compare(&i.data,&j.data); + if (cmp) + return cmp; + if (i is t && j is ct) + return 0; + i = i.next; + j = j.next; + } + if (i is null && j is null) + return 0; + else + return (i is null) ? -1 : 1; + } + + /** Create a one-item slice of the head. */ + Container.SliceType head() { + return opSlice(0,1); + } + + /** Return a one-item slice at the tail. */ + Container.SliceType tail() { + Container.SliceType res; + res.head_ = res.tail_ = tail_; + return res; + } + + /** Create a sub-list from index a to b (exclusive). The operation is + * O(max(a,b)). */ + Container.SliceType opSlice(size_t a, size_t b) { + Container.SliceType res; + if (a != b) { + res.head_ = getNode(a); + Container.Node *v = res.head_; + b = b-a-1; + while (b--) + v = v.next; + res.tail_ = v; + } + return res; + } + + /** Create a sub-list from the head of a to the tail of b (inclusive). */ + Container.SliceType opSlice(Container.SliceType a, Container.SliceType b) { + if (a.head_ is null) + return b; + if (b.head_ is null) + return a; + Container.SliceType res; + res.head_ = a.head_; + res.tail_ = b.tail_; + return res; + } + + /** Copies the list contents to an array. */ + Container.ValueType[] values() { + Container.ValueType[] buffer = new Container.ValueType[length()]; + foreach(size_t n, Container.ValueType val; *this) { + buffer[n] = val; + } + return buffer; + } +} + +/** \class SList + * \brief A singly-linked list. + * + * A SList!(Value) is a singly linked list of data of type Value. A + * list is similar to a dynamic array except accessing an element in + * the middle or near the end of the list is O(n) and appending to the + * front or back is O(1). Any operation that is not constant-time will + * explicitly have the performance behavior documented. + * + * A singly-linked list differs from a doubly-linked list in the speed + * of accessing elements near the end of the list and the ability to + * <tt>reverse</tt>, <tt>addBefore</tt> iterate <tt>backwards</tt> and + * <tt>remove</tt> a sublist. The only operations supported in the + * middle of a singly-linked list are operations that modify the items + * that follow the sublist. This prevents manipulations in one sublist + * from invalidating an adjacent sublist. + * + * The optional ReadOnly parameter SList!(Value,ReadOnly) forbids + * operations that modify the container. The readonly() property returns + * a ReadOnly view of the container. + * + * The optional allocator parameter SList!(Value,false,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct SList(Value, bit ReadOnly = false, Alloc = GCAllocator) { + + alias SList ContainerType; + alias SList SliceType; + alias Value ValueType; + alias size_t IndexType; + alias SNode!(Value) Node; + alias ReadOnly isReadOnly; + + const int NodeAllocationBlockSize = 10; // allocate 10 nodes at a time + + // private bug private { + Node* head_; // head_ is first item + Node* tail_; // tail_ is last item + // } + + mixin MCommonSList!(head_, SList ); + + SList getThis(){return *this;} + mixin MListAlgo!(SList, getThis); + + /** Get a ReadOnly view of the container */ + .SList!(Value, true, Alloc) readonly() { + .SList!(Value, true, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get a read-write view of the container */ + .SList!(Value, false, Alloc) readwrite() { + .SList!(Value, false, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + static if (!ReadOnly) { + + /** Appends an item to the tail of the list. If the target list is + * a sub-list call addAfter instead of addTail to insert an item + * after a sub-list. + */ + void addTail(Value v) { + if (tail_ is null) { + // no available nodes so allocate a new one + tail_ = newNode(); + } else { + tail_ = tail_.next; + } + tail_.data = v; + if (head_ is null) + head_ = tail_; + } + + /** Appends a list to the tail of the target list. If the target + * list is a sub-list call addAfter instead of addTail to insert + * another list after a sub-list. + */ + void addTail(SList v) { + if (v.head_ is null) + return; + tail_.next = v.head_; + tail_ = v.tail_; + if (head_ is null) + head_ = v.head_; + } + + mixin MListCatOperators!(SList); + + /** Prepends an item to the head of the target list. */ + void addHead(Value v) { + if (head_ is null) { + addTail(v); + } else if (tail_.next is null) { + // no available nodes so allocate a new one + Node* t = head_; + head_ = new Node(); + head_.data = v; + head_.next = t; + } else { + // grab available node from end + Node* t = tail_.next; + tail_.next = t.next; + t.next = head_; + head_ = t; + t.data = v; + } + } + + /** Prepends a list to the head of the target list. */ + void addHead(SList v) { + if (v.head_ is null) + return; + Node* t = head_; + head_ = v.head_; + v.tail_.next = t; + if (tail_ is null) + tail_ = v.tail_; + } + + /** Removes and returns the head item of the list. The node that + * contained the item may be reused in future additions to the + * list. To prevent the node from being reused call <tt>trim</tt>. + * If the target list is empty an IndexOutOfBoundsException is thrown + * unless version=MinTLNoIndexChecking is set. + */ + Value takeHead() { + boundsCheck(head_); + Node* v = head_; + head_ = v.next; + // save node for future reuse + v.next = tail_.next; + tail_.next = v; + Value data = v.data; + v.data = Value.init; + return data; + } + + /** Removes the head item of the list. */ + void removeHead() { + boundsCheck(head_); + Node* v = head_; + head_ = v.next; + // save node for future reuse + v.next = tail_.next; + tail_.next = v; + v.data = Value.init; + } + + /** Insert a list after a sub-list. */ + void addAfter(SList subv, SList v) { + if (v.tail_ is null) + return; + Node* t = subv.tail_; + if (t is null) { + *this = v; + return; + } + v.tail_.next = t.next; + t.next = v.head_; + if (t is tail_) + tail_ = v.tail_; + } + + /** Trims off extra nodes that are not actively being used by the + * list but are available for recyling for future add operations. + */ + void trim() { + if (tail_ !is null) + tail_.next = null; + } + + /** Removes n items after a sublist. */ + void removeAfter(SList sublist, size_t n = 1) { + if (sublist.head_ is null) + return; + boundsCheck(head_); + Node* t = sublist.tail_; + Node* newt = t.next; + while (n--) + newt = newt.next; + t.next = newt; + if (newt is tail_) + tail_ = t; + } + + /** Removes items between the tail of a to the head to b (exclusive). */ + void removeBetween(SList a, SList b) { + // what to do if a or b is null? + boundsCheck(head_); + a.tail_.next = b.head_; + } + + /** Set the value of one-item slice (more generally the head value). */ + void value(Value newValue) { + head_.data = newValue; + } + + } // !ReadOnly + + /** Move a sub-list towards the tail by n items. By default moves + * to the next item. + */ + void next(int n = 1, int end = 0) { + while (n-- > 0) { + if (end <= 0) + head_ = head_.next; + if (end >= 0) + tail_ = tail_.next; + } + } + + /** Duplicates a list. */ + .SList!(Value,ReadOnly,Alloc) dup() { + .SList!(Value,false,Alloc) res; + foreach(ValueType val; *this) { + res ~= val; + } + static if (ReadOnly) { + return res.readonly; + } else { + return res; + } + } + + /** Get the value of one-item slice (more generally the head value). + * Useful for expressions like x.tail.value or x.head.value. */ + Value value() { + return head_.data; + } + + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + alias opApplyIter opApply; + + private Node* newNode() { + static if (is(Alloc == GCAllocator)) { + // allocate a block of nodes and return pointer to first one + Node[] block = new Node[NodeAllocationBlockSize]; + for (int k=1; k<NodeAllocationBlockSize; k++) { + block[k-1].next = &block[k]; + } + return &block[0]; + } else { + // can only allocate one at a time because we have to track each + Node* p = cast(Node*)Alloc.gcMalloc(Node.sizeof); + *p = Node.init; + return p; + } + } + + invariant { + assert( (head_ is null && tail_ is null) || + (head_ !is null && tail_ !is null) ); + } + +} + +/** \class CircularSList + * \brief A circular singly-linked list. + * + * A CircularSList!(Value) is a circular singly linked list of data of type + * Value. A CircularSList differs from an SList in that the tail of the list + * is linked to the head. As a consequence no nodes are saved and + * reused between <tt>add</tt> and <rr>remove</tt> functions and + * slices can be moved forward around the list indefinitely. A CircularSList + * also has a smaller memory footprint since it requires only one + * pointer for the tail instead of two pointers for a tail and head. + * + * The optional ReadOnly parameter CircularSList!(Value,ReadOnly) forbids + * operations that modify the container. The readonly() property returns + * a ReadOnly view of the container. + * + * The optional allocator parameter CircularSList!(Value,false,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct CircularSList(Value, bit ReadOnly = false, Alloc = GCAllocator) { + + alias CircularSList ContainerType; + alias SList!(Value,ReadOnly,Alloc) SliceType; + alias Value ValueType; + alias size_t IndexType; + alias SNode!(Value) Node; + alias ReadOnly isReadOnly; + + private { + Node* tail_; // tail_ is last item + } + + /** Return the circular list as a non-circular SList. */ + SliceType toSList() { + SliceType res; + if (tail_ is null) + return res; + res.head_ = tail_.next; + res.tail_ = tail_; + return res; + } + + /** Get a ReadOnly view of the container */ + .CircularSList!(Value, true, Alloc) readonly() { + .CircularSList!(Value, true, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get a read-write view of the container */ + .CircularSList!(Value, false, Alloc) readwrite() { + .CircularSList!(Value, false, Alloc) res; + res = *cast(typeof(&res))this; + return res; + } + + static if (!ReadOnly) { + + /** Appends an item to the tail of the list. If the target list is + * a sub-list call addAfter instead of addTail to insert an item + * after a sub-list. + */ + void addTail(Value v) { + Node* n = new Node; + n.data = v; + addNode(n); + tail_ = n; + } + + /** Adds a node after tail. */ + private void addNode(Node* n) { + if (tail_ is null) { + n.next = n; + tail_ = n; + } else { + n.next = tail_.next; + tail_.next = n; + } + } + + /** Appends a list to the tail of the target list. If the target + * list is a sub-list call addAfter instead of addTail to insert + * another list after a sub-list. + */ + void addTail(CircularSList v) { + addHead(v); + tail_ = v.tail_; + } + + mixin MListCatOperators!(CircularSList); + + /** Appends an item to the tail of the list. If the target list is + * a sub-list call addAfter instead of addTail to insert an item + * after a sub-list. + */ + void addHead(Value v) { + Node* n = newNode(); + n.data = v; + addNode(n); + } + + /** Appends a list to the tail of the target list. If the target + * list is a sub-list call addAfter instead of addTail to insert + * another list after a sub-list. + */ + void addHead(CircularSList v) { + if (v.tail_ is null) + return; + if (tail_ is null) { + tail_ = v.tail_; + return; + } + v.tail_.next = tail_.next; + tail_.next = v.tail_; + } + + /** Removes and returns the head item of the list. */ + Value takeHead() { + boundsCheck(tail_); + Node* v = tail_.next; + tail_.next = v.next; + Value val = v.data; + freeNode(v); + return val; + } + + /** Removes the head item of the list. */ + void removeHead() { + boundsCheck(tail_); + Node* v = tail_.next; + tail_.next = v.next; + freeNode(v); + } + + /** Clear all contents. */ + void clear() { + static if (is(Alloc == GCAllocator)) { + } else { + Node* i = head_; + if (i !is null) + tail_.next = null; + while (i !is null) { + Node* next = i.next; + Alloc.gcFree(i); + i = next; + } + } + *this = CircularSList.init; + } + + /** Insert a list after a sub-list. */ + void addAfter(SliceType subv, SliceType v) { + if (v.tail_ is null) + return; + Node* t = subv.tail_; + if (t is null) { + tail_ = v.tail_; + return; + } + v.tail_.next = t.next; + t.next = v.head_; + if (t is tail_) + tail_ = v.tail_; + } + + /** Removes n items after a sublist. */ + void removeAfter(SliceType sublist, size_t n = 1) { + if (sublist.head_ is null) + return; + boundsCheck(tail_); + Node* t = sublist.tail_; + Node* newt = t.next; + while (n--) { + Node* i = newt; + newt = newt.next; + freeNode(i); + } + t.next = newt; + if (newt is tail_) + tail_ = t; + } + + /** Removes items between the tail of a to the head to b (exclusive). + * If a custom allocator is used the memory is not freed automatically. + */ + void removeBetween(SliceType a, SliceType b) { + // what to do if a or b is null? + boundsCheck(tail_); + a.tail_.next = b.head_; + } + + } // !ReadOnly + + /** Duplicates a list. */ + .CircularSList!(Value,ReadOnly,Alloc) dup() { + .CircularSList!(Value,false,Alloc) res; + foreach(ValueType val; *this) { + res ~= val; + } + static if (ReadOnly) { + return res.readonly; + } else { + return res; + } + } + + /** Rotate the list. */ + void rotate(int n = 1) { + while (n-- > 0) + tail_ = tail_.next; + } + + private Node* head_() { + if (tail_ is null) return null; + return tail_.next; + } + + CircularSList getThis(){return *this;} + mixin MListAlgo!(CircularSList, getThis); + + mixin MCommonSList!(head_, CircularSList ); + + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + alias opApplyIter opApply; + + private Node* newNode() { + static if (is(Alloc == GCAllocator)) { + return new Node; + } else { + Node* p = cast(Node*)Alloc.gcMalloc(Node.sizeof); + *p = Node.init; + return p; + } + } + + private void freeNode(Node* n) { + static if (is(Alloc == GCAllocator)) { + } else { + Alloc.gcFree(n); + } + } +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; + +version (MinTLUnittest) { + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.slist unittest\n"); + SList!(int) x; + x.add(3,4); + assert( x[0] == 3 ); + assert( x[1] == 4 ); + assert( x.length == 2 ); + + // test addHead + SList!(int) y; + y.addHead(4); + y.addHead(3); + + // test == + assert( x == y ); + SList!(int) w = x.dup; + w ~= 5; + assert( x != w); + + // test remove + assert( w.takeHead() == 3 ); + w.trim(); + w.addHead(3); + + SList!(int) z = x.dup; + // test foreach iteration + foreach(size_t n, inout int val; z) { + val = n*10; + } + assert( z[0] == 0 ); + assert( z[1] == 10 ); + int n = 0; + foreach(SList!(int) itr; z) { + assert(itr[0] == z[n++]); + } + + // test slicing + SList!(int) v = w[1..3]; + assert( v.length == 2 ); + assert( v[0] == 4 ); + assert( v[1] == 5 ); + + // test algorithms + assert( v.opIn(5) == v.tail ); + assert( v.count(5) == 1 ); + + // test another node type + SList!(char[]) str; + str ~= "hello"; + str ~= "world"; + assert( str[str.length-1] == "world" ); + + // test sub-list spanning + SList!(int) tmp; + int[10] tmp2; + tmp2[3] = 100; + tmp2[8] = 200; + foreach(int xx;tmp2) + tmp ~= xx; + SList!(int) a,b,c; + a = tmp[3..5]; + b = tmp[7..9]; + c = tmp[a..b]; + assert( c.length == 6 ); + assert( c[0] == 100 ); + assert( c[5] == 200 ); + + // CircularSList + + CircularSList!(int) cx; + cx.add(3,4); + assert( cx[0] == 3 ); + assert( cx[1] == 4 ); + assert( cx.length == 2 ); + + // test addHead + CircularSList!(int) cy; + cy.addHead(4); + cy.addHead(3); + + // test == + assert( cx == cy ); + CircularSList!(int) cw = cx.dup; + cw ~= 5; + assert( cx != cw); + + // test remove + assert( cw.takeHead() == 3 ); + cw.addHead(3); + + CircularSList!(int) cz = cx.dup; + // test foreach iteration + foreach(size_t n, inout int val; cz) { + val = n*10; + } + assert( cz[0] == 0 ); + assert( cz[1] == 10 ); + n = 0; + foreach(SList!(int) itr; cz) { + assert(itr[0] == cz[n++]); + } + + // test slicing + SList!(int) cv = cw[1..3]; + assert( cv.length == 2 ); + assert( cv[0] == 4 ); + assert( cv[1] == 5 ); + + // test algorithms + assert( cv.opIn(5) == cv.tail ); + assert( cv.count(5) == 1 ); + + // test another node type + CircularSList!(char[]) cstr; + cstr ~= "hello"; + cstr ~= "world"; + assert( cstr[cstr.length-1] == "world" ); + + // test sub-list spanning + CircularSList!(int) ctmp; + int[10] ctmp2; + ctmp2[3] = 100; + ctmp2[8] = 200; + foreach(int xx; ctmp2) + ctmp ~= xx; + SList!(int) ca,cb,cc; + ca = ctmp[3..5]; + cb = ctmp[7..9]; + cc = ctmp[a..b]; + assert( cc.length == 6 ); + assert( cc[0] == 100 ); + assert( cc[5] == 200 ); + + version (MinTLVerboseUnittest) + printf("finished mintl.slist unittest\n"); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/sortedaa.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,1033 @@ +/** \file sortedaa.d + * \brief A sorted associative array. + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * The red-black tree code is by Thomas Niemann. + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 2.7.1 + */ + +module mintl.sortedaa; + +private import mintl.share; // for mixins +import mintl.mem; + +// debug = dSortedAA; // can also pass at command line +//debug(dSortedAA) { +// private import std.stdio; +//} + +/** \class CompareFcnSetException + * \brief An exception thrown when attempting to set the compare + * function twice. In particular it cannot be set on a non-empty + * SortedAA. + */ +class CompareFcnSetException: Exception { + this(char[] str) { super(str); } + this() { super("Cannot set the comparison function twice"); } +} + +/** \class SortedAA + * \brief A sorted associative array. + * + * A SortedAA!(Key,Value) represents a sorted associative array with + * keys of type Key and values of type Value. A sorted associative + * array is similar to a builtin associative array except accessing an + * elements is O(log(n)), where n is the number of elements in the + * array, instead of O(1) and the elements are sorted by key. Any + * operation that is not O(log(n)) will explicitly have the + * performance behavior documented. + * + * The array is sorted by default according to the key's TypeInfo compare + * function. To use a custom key order call the CompareFcn property setter + * with a delegate of the form int delegate(Key* a, Key* b). The comparison + * function cannot be set after any elements are inserted. + * + * The optional ReadOnly parameter SortedAA!(Key,Value,ReadOnly) forbids + * operations that modify the container. The readonly() property returns + * a ReadOnly view of the container. + * + * The optional allocator parameter SortedAA!(Key,Value,false,Allocator) is used + * to allocate and free memory. The GC is the default allocator. + */ +struct SortedAA(Key,Value, bit ReadOnly = false, Alloc = GCAllocator) { + + alias SortedAA ContainerType; + alias SortedAA SliceType; + alias Value ValueType; + alias Key IndexType; + alias ReadOnly isReadOnly; + + /** Get a ReadOnly view of the container */ + .SortedAA!(Key,Value,true) readonly() { + .SortedAA!(Key,Value,true) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get a read-write view of the container */ + .SortedAA!(Key,Value,false) readwrite() { + .SortedAA!(Key,Value,false) res; + res = *cast(typeof(&res))this; + return res; + } + + /** Get the kays in the array. The operation is O(n) where n is the number of + * elements in the array. + */ + Key[] keys() { + Key[] res; + foreach(Key k,Value v;*this) + res ~= k; + return res; + } + + /** Get the values in the array. The operation is O(n) where n is the number of + * elements in the array. + */ + Value[] values() { + Value[] res; + foreach(Key k,Value v;*this) + res ~= v; + return res; + } + + /** Property for the default value of the array when a key is missing. */ + void missing(Value val) { + fixupShared(); + shared.missing = val; + } + Value missing() { + if (!shared) + return Value.init; + return shared.missing; + } + + /** Length of array. The operation is O(n) where n is the number of + * elements in the array. + */ + size_t length() { + size_t len = 0; + foreach(Value val; *this) + len++; + return len; + } + + /** Test if array is empty. */ + bool isEmpty() { + return shared is null || shared.root is null; + } + + static if (ReadOnly) { + + /** Duplicates an array. */ + SortedAA dup() { + .SortedAA!(Key,Value,false) res; + if (shared) { + if (shared.cmpFcn) + res.compareFcn = shared.cmpFcn; + res.missing = missing; + } + foreach(Key k,Value v;*this) + res[k] = v; + return res.readonly; + } + + } else { + + /** Clear all contents. */ + void clear() { + static if (is(Alloc == GCAllocator)) { + } else { + if (shared) { + void freeNode(Node*p) { + if(p) { + freeNode(p.left); + freeNode(p.right); + Alloc.gcFree(p); + } + } + freeNode(shared.root); + Alloc.gcFree(shared); + } + } + *this = SortedAA.init; + } + + /** Remove a key from the array. The target array can be a sub-array though + * the key may fall outside of the sub-array range. The value stored for + * the key is returned, if present. + */ + Value take(Key key) { + // debug(dSortedAA) writefln("getAndRemove: %s",key); + Node* node = getNode(key,NullOnMiss); + if (!node) return missing; + Value value = node.val; + deleteNode(node); + return value; + } + + /** Remove a key from the array. The target array can be a sub-array though + * the key may fall outside of the sub-array range. + */ + void remove(Key key) { + // debug(dSortedAA) writefln("remove: %s",key); + deleteNode(getNode(key,NullOnMiss)); + } + + /** Remove a sub-array from the array. The operation is O(max(log(m),n)) + * where m is the size of the target array and n is the number of + * elements in the sub-array. + */ + void remove(SortedAA subarray) { + if (subarray.head_ is subarray.tail_) { + deleteNode(subarray.head_); + } else { + Key[] keylist = subarray.keys; + foreach(Key key;keylist) + remove(key); + } + } + + /** Duplicates an array. */ + SortedAA dup() { + SortedAA res; + if (shared) { + if (shared.cmpFcn) + res.compareFcn = shared.cmpFcn; + res.missing = missing; + } + foreach(Key k,Value v;*this) + res[k] = v; + return res; + } + + } // !ReadOnly + + /** signature for a custom comparison function */ + alias int delegate(Key* a, Key* b) CompareFcn; + + /** Set custom comparison function. If the array is non-empty or the + * comparison function has already been set a CompareFcnSetException + * is thrown. + */ + void compareFcn(CompareFcn cmp) { + allocShared(); + if (shared.cmpFcn !is null) + throw new CompareFcnSetException(); + else + shared.cmpFcn = cmp; + } + + /** Find (and insert if not present) the element with a given key + * and return the value. The target array can be a sub-array though + * the key may fall outside of the sub-array range. + */ + Value opIndex(Key key) { + Node* t = getNode(key,NullOnMiss); + if (t) + return t.val; + else + return missing; + } + + /** Store a value with a key, overwriting any previous value. The + * target array can be a sub-array though the key may fall outside of the + * sub-array range. + */ + void opIndexAssign(Value val, Key key) { + Node* t = getNode(key,InsertOnMiss); + t.val = val; + } + + /** Returns the value of the first item of a slice. In particular gets + * the value of a one-item slice. + */ + Value value() { + if (head_ is null && tail_ is null) + return Value.init; + return head_.val; + } + + /** Returns the key of the first item of a slice. In particular gets + * the key of a one-item slice. + */ + Key key() { + if (head_ is null && tail_ is null) + return Key.init; + return head_.key; + } + + /** Return the start of the sorted items (the min). */ + SortedAA head() { + Node* node = head_ is null ? minNode() : head_; + SortedAA res; + res.shared = shared; + res.head_ = res.tail_ = node; + return res; + } + + /** Return the end of the sorted items (the max). */ + SortedAA tail() { + Node* node = tail_ is null ? maxNode() : tail_; + SortedAA res; + res.shared = shared; + res.head_ = res.tail_ = node; + return res; + } + + /** Return a one-item slice of the item less than key */ + SortedAA to(Key key) { + SortedAA res; + res.shared = shared; + res.head_ = res.tail_ = lookupSide(key,false); + return res; + } + + /** Return a one-item slice of the item greater than or equal to key */ + SortedAA from(Key key) { + SortedAA res; + res.shared = shared; + res.head_ = res.tail_ = lookupSide(key,true); + return res; + } + + /** Move a slice towards the head or tail by n items. If n is + * negative the slice moves towards the head. A positive end is + * the tail, negative the head and 0 is both. By default moves to + * the next item. + */ + void next(int n = 1, int end = 0) { + void doNext(inout Node* node, int m) { + while (m--) + node = nextNode(node); + } + void doPrev(inout Node* node, int m) { + while (m--) + node = prevNode(node); + } + if (n > 0) { + if (end >= 0) + doNext(tail_,n); + if (end <= 0) + doNext(head_,n); + } else { + n = -n; + if (end >= 0) + doPrev(tail_,n); + if (end <= 0) + doPrev(head_,n); + } + } + + /** Find the element with a given key and return a pointer to the + * value. If the key is not in the array null is returned or if + * throwOnMiss is true an exception is thrown. The target array can + * be a sub-array though the key may fall outside of the sub-array + * range. + */ + Value* get(Key key, bool throwOnMiss = false) { + Node* t = getNode(key,throwOnMiss ? ThrowOnMiss : NullOnMiss); + if (t) + return &t.val; + else + return null; + } + + /** Find a key in the array and return a pointer to the associated value. + * Insert the key and initialize with Value.init if the key is not + * in the array. + */ + Value* put(Key key) { + Node* t = getNode(key, InsertOnMiss); + return &t.val; + } + + /** Create a slice from the head of a to the tail in b (inclusive). */ + SortedAA opSlice(SortedAA a, SortedAA b) { + SortedAA res; + res.head_ = a.head_ is null ? minNode() : a.head_; + res.tail_ = b.tail_ is null ? maxNode() : b.tail_; + res.shared = shared; + return res; + } + + /** Create a sub-array from key a to b (exclusive). */ + SortedAA opSlice(Key a, Key b) { + return (*this)[from(a) .. to(b)]; + } + + /** Create a sub-array from slice a to key b (exclusive). */ + SortedAA opSlice(SortedAA a, Key b) { + return (*this)[a .. to(b)]; + } + /** Create a sub-array from key a to slice b (inclusive). */ + SortedAA opSlice(Key a, SortedAA b) { + return (*this)[from(a) .. b]; + } + + /** Test for equality of two arrays. The operation is O(n) where n + * is length of the array. + */ + int opEquals(SortedAA c) { + fixupShared(); + c.fixupShared(); + Node* i = head_ ? head_ : minNode(); + Node* j = c.head_ ? c.head_ : c.minNode(); + Node* end = tail_ ? tail_ : maxNode(); + Node* cend = c.tail_ ? c.tail_ : c.maxNode(); + TypeInfo ti_k = typeid(Key); + TypeInfo ti_v = typeid(Value); + int do_test(Node*p1,Node*p2) { + if (p1 is null && p2 is null) + return 1; + if ((p1 is null && p2 !is null) || + (p1 !is null && p2 is null)) + return 0; + if (!ti_k.equals(&p1.key,&p2.key)) + return 0; + if (!ti_v.equals(&p1.val,&p2.val)) + return 0; + return 1; + } + while (i !is end && j !is cend) { + if (!do_test(i,j)) + return 0; + i = nextNode(i); + j = c.nextNode(j); + } + return do_test(i,j); + } + + /** Test if a key is in the array. The target array can be a sub-array + * but the key may fall outside of the sub-array range. + */ + bool contains(Key key) { + Value* node = get(key); + return node !is null; + } + + /** Test if a key is in the array and set value if it is. */ + bool contains(Key key,out Value value) { + Value* node = get(key); + if (node) + value = *node; + return node !is null; + } + + /** Iterate over the array calling delegate to perform an action. + * The value is passed to the delegate. + */ + int opApplyNoKeyStep(int delegate(inout Value val) dg, int step=1) { + int dg2(inout SortedAA itr) { + Value value = itr.value; + return dg(value); + } + return opApplyIterStep(&dg2,step); + } + + /** Iterate over the array calling delegate to perform an action. + * The key and value are passed to the delegate. + */ + int opApplyWithKeyStep(int delegate(inout Key key, inout Value val) dg, + int step = 1) { + int dg2(inout SortedAA itr) { + Key key = itr.key; + Value value = itr.value; + return dg(key,value); + } + return opApplyIterStep(&dg2,step); + } + + /** Iterate over the array calling delegate to perform an action. A + * one-element sub-array is passed to the delegate. + */ + int opApplyIterStep(int delegate(inout SortedAA itr) dg,int step=1) { + SortedAA itr; + itr = *this; + int res; + if (shared is null) return 0; + Node* i = head_ ? head_ : minNode(); + Node* j = tail_ ? tail_ : maxNode(); + Node* x = step>0?i:j; + Node* end = step>0?j:i; + while (x !is null) { + itr.head_ = itr.tail_ = x; + res = dg(itr); + if (res || x is end) return res; + x = step>0?nextNode(x):prevNode(x); + } + return res; + } + + /** Iterate backwards over the array (from last to first key). This + * should only be called as the iteration parameter in a + * <tt>foreach</tt> statement + */ + SortedAAReverseIter!(Key,Value,ReadOnly,Alloc) backwards() { + SortedAAReverseIter!(Key,Value,ReadOnly,Alloc) res; + res.list = this; + return res; + } + + /** Helper functions for opApply */ + mixin MOpApplyImpl!(SortedAA); + alias opApplyNoKey opApply; + alias opApplyWithKey opApply; + alias opApplyIter opApply; + mixin MAddAA!(SortedAA); // mixin add function + + // End of public interface + + private { + enum Color:int { Red, Black } + // share some data between array and sub-arrays to make updating + // easier and shrink the SortedAA footprint. + struct SharedArrayData { + Node* root; + CompareFcn cmpFcn; + Value missing; + Node* freelist; + } + struct Node { + Node* left, right, parent; + Color color; + Key key; + Value val; + } + SharedArrayData *shared; + Node* head_, tail_; + } + + debug(dSortedAA) { + private void dumpTree(Node* x, char[] str,int indent) { + if (x !is null) { + int n = indent; + while (n--) printf(" "); + printf("%.*s %p: %d %.*s\n",str,x,x.color,x.key); + dumpTree(x.left,"left",indent+1); + dumpTree(x.right,"right",indent+1); + } + } + } + + // lookup the smallest item greater than or equal to key (from) + // or lookup the largest item less than key + Node* lookupSide(Key key, bool from) { + Node* current; + Node* parent; + fixupShared(); + current = shared.root; + parent = current; + int cmpVal = 0; + CompareFcn cmp = shared.cmpFcn; + while (current !is null) { + cmpVal = cmp(&key,¤t.key); + if (cmpVal == 0) { + return from?current:prevNode(current); + } + parent = current; + current = cmpVal < 0 ? current.left : current.right; + } + if (!parent) throw new Exception("Invalid Index"); + if (from) + return cmpVal<0?prevNode(parent):parent; + else + return cmpVal>0?nextNode(parent):parent; + } + + // initialize shared data if null + private void allocShared() { + if (shared is null) { + static if (is(Alloc == GCAllocator)) { + shared = new SharedArrayData; + } else { + shared = cast(SharedArrayData*)Alloc.gcMalloc(SharedArrayData.sizeof); + *shared = SharedArrayData.init; + } + } + } + + // initialize shared data if null and initialize cmpFcn + private void fixupShared() { + allocShared(); + if (shared.cmpFcn is null) { + TypeInfo ti = typeid(Key); + shared.cmpFcn = cast(CompareFcn)&ti.compare; + } + } + + // return the next largest node or null if none + // used when we can't traverse the whole tree + private Node* nextNode(Node* x) { + if (x.right !is null) { + x = x.right; + while (x.left != null) x = x.left; + } else { + while (x.parent !is null && x.parent.right == x) + x = x.parent; + if (x.parent !is null && x.parent.left == x) + x = x.parent; + else + x = null; + } + return x; + } + + // return the previous node or null if none + // used when we can't traverse the whole tree + private Node* prevNode(Node* x) { + if (x.left !is null) { + x = x.left; + while (x.right != null) x = x.right; + } else { + while (x.parent !is null && x.parent.left == x) + x = x.parent; + if (x.parent !is null && x.parent.right == x) + x = x.parent; + else + x = null; + } + return x; + } + + // fixup Red-Black invariant + private void rotateLeft(Node* x) { + Node* y = x.right; + assert( y !is null ); + x.right = y.left; + if (y.left !is null) + y.left.parent = x; + y.parent = x.parent; + if (x.parent !is null) { + if (x is x.parent.left) + x.parent.left = y; + else + x.parent.right = y; + } else { + shared.root = y; + } + y.left = x; + if (x !is null) x.parent = y; + } + + // fixup Red-Black invariant + private void rotateRight(Node* x) { + Node* y = x.left; + assert( y !is null ); + x.left = y.right; + if (y.right !is null) + y.right.parent = x; + y.parent = x.parent; + if (x.parent !is null) { + if (x is x.parent.right) + x.parent.right = y; + else + x.parent.left = y; + } else { + shared.root = y; + } + y.right = x; + if (x !is null) x.parent = y; + } + + // fixup Red-Black invariant after an insert + private void insertFixup(Node* x) { + Node* root = shared.root; + while (x !is root && x.parent.color == Color.Red) { + debug(dSortedAA) printf("fixing up parent %p\n",x.parent); + if (x.parent is x.parent.parent.left) { + Node* y = x.parent.parent.right; + if (y !is null && y.color == Color.Red) { + x.parent.color = Color.Black; + y.color = Color.Black; + x.parent.parent.color = Color.Red; + x = x.parent.parent; + } else { + if (x is x.parent.right) { + x = x.parent; + debug(dSortedAA) printf("rotating left %p\n",x); + rotateLeft(x); + } + x.parent.color = Color.Black; + x.parent.parent.color = Color.Red; + debug(dSortedAA) printf("rotating right1 %s\n",x.parent.parent); + rotateRight(x.parent.parent); + } + } else { + Node* y = x.parent.parent.left; + if (y !is null && y.color == Color.Red) { + x.parent.color = Color.Black; + y.color = Color.Black; + x.parent.parent.color = Color.Red; + x = x.parent.parent; + } else { + if (x is x.parent.left) { + x = x.parent; + debug(dSortedAA) printf("rotating right %p\n",x); + rotateRight(x); + } + x.parent.color = Color.Black; + x.parent.parent.color = Color.Red; + debug(dSortedAA) printf("rotating left1 %p\n",x.parent.parent); + rotateLeft(x.parent.parent); + } + } + } + while (x.parent !is null) + x = x.parent; + x.color = Color.Black; + } + + private enum {InsertOnMiss, ThrowOnMiss, NullOnMiss} + + // returns node for a given key - even if the key is ouside the + // sub-array. + private Node* getNode(Key key, int failureAction) { + // debug(dSortedAA) writefln("lookup %s",key); + Node* current; + fixupShared(); + current = shared.root; + Node* parent = null; + int cmpVal = 0; + CompareFcn cmp = shared.cmpFcn; + while (current !is null) { + cmpVal = cmp(&key,¤t.key); + // debug(dSortedAA) writefln("comparing %s %s got %s",key,current.key,cmpVal); + if (cmpVal == 0) return current; + parent = current; + current = cmpVal < 0 ? current.left : current.right; + } + switch (failureAction) { + case NullOnMiss: return null; + case ThrowOnMiss: throw new IndexOutOfBoundsException("Key not in container"); + case InsertOnMiss: return insertNode(key, Value.init, parent, cmpVal); + } + } + + // remove extra capacity + void trim() { + if (shared) + shared.freelist = null; + } + + // Parameters for controlling block allocations + private const int NodeAllocBlockSize = 10; // number of nodes in block + private const int AllocBlockCutoff = 96; // max node size to allow blocks + + // helper function to allocate a node + private Node* newNode() { + static if (is(Alloc == GCAllocator)) { + static if (Node.sizeof > AllocBlockCutoff) { + return new Node; + } else { + if (shared.freelist) { + Node* t = shared.freelist; + shared.freelist = t.left; + t.left = null; + return t; + } + Node[] block = new Node[NodeAllocBlockSize]; + for(int k=1;k<NodeAllocBlockSize-1;k++) + block[k].left = &block[k+1]; + shared.freelist = &block[1]; + return &block[0]; + } + } else { + Node* p = cast(Node*)Alloc.gcMalloc(Node.sizeof); + *p = Node.init; + return p; + } + } + + // insert and return new node at the given parent + private Node* insertNode(Key key, Value data, + Node* parent, int cmpVal) { + Node* x = newNode; + x.key = key; + x.val = data; + x.parent = parent; + x.color = Color.Red; + if (parent !is null) { + if (cmpVal < 0) { + parent.left = x; + } else { + parent.right = x; + } + } else { + shared.root = x; + } + insertFixup(x); + return x; + } + + // fixup Red-Black invariant after a delete + private void deleteFixup(Node* x) { + Node* root = shared.root; + while (x !is root && x.color == Color.Black) { + if (x is x.parent.left) { + Node* w = x.parent.right; + if (w !is null && w.color == Color.Red) { + w.color = Color.Black; + x.parent.color = Color.Red; + rotateLeft(x.parent); + w = x.parent.right; + } + assert( w !is null ); + if ((w.left is null || w.left.color == Color.Black) && + (w.right is null || w.right.color == Color.Black)) { + w.color = Color.Red; + x = x.parent; + } else { + if (w.right is null || w.right.color == Color.Black) { + w.left.color = Color.Black; + w.color = Color.Red; + rotateRight(w); + w = x.parent.right; + } + w.color = x.parent.color; + x.parent.color = Color.Black; + w.right.color = Color.Black; + rotateLeft(x.parent); + x = root; + } + } else { + Node* w = x.parent.left; + assert( w !is null ); + if (w.color == Color.Red) { + w.color = Color.Black; + x.parent.color = Color.Red; + rotateRight(x.parent); + w = x.parent.left; + } + assert( w !is null ); + if ((w.left is null || w.left.color == Color.Black) && + (w.right is null || w.right.color == Color.Black)) { + w.color = Color.Red; + x = x.parent; + } else { + if (w.left is null || w.left.color == Color.Black) { + w.right.color = Color.Black; + w.color = Color.Red; + rotateLeft(w); + w = x.parent.left; + } + w.color = x.parent.color; + x.parent.color = Color.Black; + w.left.color = Color.Black; + rotateRight(x.parent); + x = root; + } + } + } + x.color = Color.Black; + } + + // get the miminum element of the array + private Node* minNode() { + Node* x = shared.root; + while (x !is null && x.left !is null) { + x = x.left; + } + return x; + } + + // get the maximum element of the array + private Node* maxNode() { + Node* x = shared.root; + while (x !is null && x.right !is null) { + x = x.right; + } + return x; + } + + // deletes a node from the array. + // This routine should probably not copy node contents around to be + // nice to other sub-arrays. Instead copy around pointers to parents + // and children. + private void deleteNode(Node* z) { + Node* x,y; + if (z is null) + return; + debug(dSortedAA) printf("zleft %p right %p\n",z.left,z.right); + if (z.left is null || z.right is null) { + y = z; + } else { + y = z.right; + while (y.left !is null) { + y = y.left; + } + } + debug(dSortedAA) printf("y.left %p y right %p\n",y.left,y.right); + if (y.left !is null) + x = y.left; + else + x = y.right; + bool useTempX = x is null; + Node tempX; + if (useTempX) { + debug(dSortedAA) printf("allocating tmpxnode\n"); + x = &tempX; + x.color = Color.Black; + } + x.parent = y.parent; + if (y.parent !is null) { + if (y is y.parent.left) + y.parent.left = x; + else + y.parent.right = x; + } else { + shared.root = x; + } + if (y !is z) { + debug(dSortedAA) printf("swapping %p with %p\n",y,z); + z.key = y.key; + z.val = y.val; + } + if (y.color == Color.Black) { + deleteFixup(x); + } + if (useTempX) { + // replace temporary "NIL" with nulls + if (x is shared.root) + shared.root = null; + else if (x is x.parent.left) + x.parent.left = null; + else if (x is x.parent.right) + x.parent.right = null; + } + static if (is(Alloc == GCAllocator)) { + static if (Node.sizeof <= AllocBlockCutoff) { + *y = Node.init; + y.left = shared.freelist; + shared.freelist = y; + } + } else { + Alloc.gcFree(y); + } + } +} + +// helper structure for backwards() +struct SortedAAReverseIter(Key,Value, bit ReadOnly, Alloc) { + mixin MReverseImpl!(SortedAA!(Key,Value,ReadOnly,Alloc)); +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; + +version (MinTLUnittest) { + private import std.random; + private import std.string; + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.sortedaa unittest\n"); + + SortedAA!(int,int) m; + m[4] = 100; + //private bug + // assert( m.shared.root !is null ); + // assert( m.shared.root.val == 100 ); + for (int k=1; k<1000; k++) { + int key = std.random.rand()%30; + if (m.contains(key)) + m.remove(key); + else + m[key] = 1; + } + SortedAA!(char[],char[]) m2; + for (int k=1; k<1000; k++) { + int key = rand()%300; + m2[toString(key)] = toString(key); + } + char[] prev; + foreach(char[] val; m2) { + assert( val > prev ); + prev = val; + } + /* private bug + SortedAA!(char[],char[]) m5 = m2; + m5.head_ = m5.minNode(); + m5.tail_ = m5.maxNode(); + prev = ""; + foreach(char[] val; m5) { + assert( val > prev ); + prev = val; + } + prev = m5.maxNode().val; + foreach(char[] val; m5.backwards()) { + assert( val <= prev ); + prev = val; + } + */ + SortedAA!(int,int) m3; + m3.compareFcn = delegate int(int* a, int* b) { + return *a-*b; + }; + m3[10] = -100; + m3[7] = 100; + m3[-10] = 200; + assert( m3.length == 3); + assert( m3[7] == 100 ); + assert( m3[-10] == 200 ); + assert( m3[10] == -100 ); + + SortedAA!(int,int) mm; + mm.add(10,-100, 7,100, -10,200); + assert( m3 == mm ); + + SortedAA!(int,int) m3a = m3.dup; + assert( m3a == m3 ); + assert( m3a !is m3 ); + assert( m3a.length == 3); + assert( m3a[7] == 100 ); + assert( m3a[-10] == 200 ); + assert( m3a[10] == -100 ); + + m3.remove(7); + m3.remove(10); + m3.remove(-10); + // assert( m3.shared.root is null ); + + int[] keys = m3a.keys; + assert( keys[0] == -10 ); + assert( keys[1] == 7 ); + assert( keys[2] == 10 ); + + // test slicing + SortedAA!(char[],int) m8 = + SortedAA!(char[],int).make("a",100,"c",300,"d",400,"b",200, + "f",600,"e",500); + SortedAA!(char[],int) msl,msl2,msl3; + // debug(dSortedAA) m8.dumpTree(m8.shared.root,"",0); + msl = m8["b".."d"]; + msl2 = m8[m8.from("b123") .. m8.to("e")]; + assert( msl.length == 2 ); + // assert( msl.head_.key == "b" ); + // assert( msl.tail_.key == "c" ); + msl2 = m8["c".."f"]; + assert( msl2.length == 3 ); + // assert( msl2.head_.key == "c" ); + // assert( msl2.tail_.key == "e" ); + msl3 = m8[msl..msl2]; + assert( msl3.length == 4 ); + // assert( msl3.head_.key == "b" ); + // assert( msl3.tail_.key == "e" ); + m8.remove(msl2); + assert( m8.length == 3 ); + debug(dSortedAA) printf("\nsize %d\n",m8.sizeof); + + SortedAA!(int,int,false,MallocNoRoots) mal; + mal[10] = 20; + mal[30] = 50; + assert( mal[10] == 20 ); + assert( mal[30] == 50 ); + mal.clear(); + assert( mal.isEmpty ); + + version (MinTLVerboseUnittest) + printf("starting mintl.sortedaa unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/sorting.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,139 @@ +/** \file sorting.d + * \brief Mixins for sorting random-access and sequential-access containers + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 1.0 + */ + +module mintl.sorting; + +// mixin for sorting random-access containers +// quicksort with insertion sort for short lists +template MRandomAccessSort(Container, alias list) { + void sort(int delegate(Container.ValueType* l, Container.ValueType* r) cmp = null) { + void swap(Container.ValueType* t1, Container.ValueType* t2 ) { + Container.ValueType t = *t1; *t1 = *t2; *t2 = t; + } + void insertionSort(Container data) { + size_t i = 1; + while(i < data.length) { + size_t j = i; + Container.ValueType* jp = data.lookup(j); + Container.ValueType* j1p; + while (j > 0 && cmp((j1p=data.lookup(j-1)),jp) > 0) { + swap(j1p,jp); + --j; + jp = j1p; + } + i++; + } + } + void dosort(Container data) { + if (data.length < 2) { + return; + } else if (data.length < 8) { + insertionSort(data); + return; + } + size_t tail = data.length-1; + size_t p = 1; + size_t q = tail; + Container.ValueType* headptr = data.lookup(0); + Container.ValueType* pptr = data.lookup(p); + Container.ValueType* qptr = data.lookup(q); + swap(headptr,data.lookup(data.length/2)); + if (cmp(pptr,qptr) > 0) swap(pptr,qptr); + if (cmp(headptr,qptr) > 0) swap(headptr,qptr); + if (cmp(pptr,headptr) > 0) swap(pptr,headptr); + while (1) { + do p++; while (cmp(data.lookup(p), headptr) < 0); + do q--; while (cmp(data.lookup(q), headptr) > 0); + if (p > q) break; + swap(data.lookup(p),data.lookup(q)); + } + swap(headptr,data.lookup(q)); + if (0 < q) + dosort(data[0 .. q+1]); + if (p < tail) + dosort(data[p .. tail+1]); + } + TypeInfo ti = typeid(Container.ValueType); + if (cmp is null) { + cmp = cast(typeof(cmp))&ti.compare; + } + dosort(list); + } +} + +// mixin for sorting sequential-access containers +// using mergesort customized for doublly-linked lists +// TODO: allow singly-linked lists, too +template MSequentialSort(Container, alias head_, alias tail_) { + void dosort(out Container.Node* newhead, + out Container.Node* newtail, + int delegate(Container.SortType* l, Container.SortType* r) cmp = null) { + void link(Container.Node* a, Container.Node* b) { + if (a) a.next = b; + if (b) b.prev = a; + } + if (cmp is null) { + TypeInfo ti = typeid(Container.SortType); + cmp = cast(typeof(cmp))&ti.compare; + } + Container.Node* head = head_; + Container.Node* tail = tail_; + Container.Node* headprev = head.prev; + Container.Node* i,j,e,itail; + i = tail; + tail = tail.next; // one past tail + i.next = null; + int depth; + size_t ilen, jlen, len = 1; + while (1) { + i = head; + depth = 0; + itail = null; + head = null; + while (i) { + depth++; + j = i; + ilen = 0; + for (size_t k = 0; k < len; k++) { + ilen++; + j = j.next; + if (!j) break; + } + jlen = len; + while (ilen > 0 || (jlen > 0 && j)) { + if (ilen == 0) { + e = j; j = j.next; jlen--; + } else if (jlen == 0 || !j || + cmp(i.sortLookup(),j.sortLookup()) <= 0) { + e = i; i = i.next; ilen--; + } else { + e = j; j = j.next; jlen--; + } + if (itail) { + link(itail,e); + } else { + head = e; + } + itail = e; + } + i = j; + } + itail.next = null; + if (depth <= 1) { + link(itail,tail); + newtail = itail; + link(headprev,head); + newhead = head; + return; + } + len *= 2; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/stack.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,91 @@ +/** \file stack.d + * \brief A stack container + * + * Written by Ben Hinkle and released to the public domain, as + * explained at http://creativecommons.org/licenses/publicdomain + * Email comments and bug reports to ben.hinkle@gmail.com + * + * revision 1.1 + */ + +module mintl.stack; + +private import mintl.deque; +private import mintl.arraylist; +import mintl.adapter; +import mintl.share; +import mintl.mem; + +/** A stack of items of stype Value backed by a container of type ImplType. + * Aliases push and pop allow stack operations. By default the stack is + * backed by a Deque. + */ +struct Stack(Value, ImplType = Deque!(Value)) { + + alias Stack ContainerType; + alias Value ValueType; + alias size_t IndexType; + alias ImplType AdaptType; + const bit isReadOnly = ImplType.isReadOnly; + + ImplType impl; + + mixin MAdaptBuiltin!(impl,Stack); + mixin MAdaptBasic!(impl,Stack); + mixin MAdaptList!(impl,Stack); + mixin MListCatOperators!(Stack); + + // Stack specific + static if (!ImplType.isReadOnly) { + alias add push; + alias takeTail pop; + } + Value peek() { + ImplType last = impl.tail; + return last.isEmpty ? Value.init : last[0]; + } +} + +/** Convenience alias for a stack backed by an array */ +template ArrayStack(Value) { + alias Stack!(Value,ArrayList!(Value)) ArrayStack; +} + +//version = MinTLVerboseUnittest; +//version = MinTLUnittest; + +version (MinTLUnittest) { + import mintl.list; + unittest { + version (MinTLVerboseUnittest) + printf("starting mintl.stack unittest\n"); + Stack!(int) st; + st.push(10, 20); + assert( st.peek == 20 ); + assert( st.pop == 20 ); + assert( st[st.length - 1] == 10 ); + assert( st.pop == 10 ); + assert( st.length == 0 ); + + ArrayStack!(int) st2; + st2.push(10); + st2 ~= 20; + assert( st2.peek == 20 ); + assert( st2.pop == 20 ); + assert( st2[st2.length - 1] == 10 ); + assert( st2.pop == 10 ); + assert( st2.length == 0 ); + + Stack!(int,List!(int)) st3; + st3.push(10); + st3 ~= 20; + assert( st3.peek == 20 ); + assert( st3.pop == 20 ); + assert( st3[st3.length - 1] == 10 ); + assert( st3.pop == 10 ); + assert( st3.length == 0 ); + + version (MinTLVerboseUnittest) + printf("finished mintl.stack unittest\n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/unittest.d Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,6 @@ + +import mintl.all; + +int main() { + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/mintl/win32.mak Sat Mar 29 12:30:20 2008 -0600 @@ -0,0 +1,74 @@ + +# To build mintl.lib type +# make -f win32.mak DFLAGS=-g LIBNAME=mintl_debug.lib +# or +# make -f win32.mak DFLAGS=-release LIBNAME=mintl.lib +# The mintl.lib and object files will be created in the source directory. + +# flags to use building unittest.exe +DUNITFLAGS=-g -v -unittest -I.. -version=MinTLUnittest -version=MinTLVerboseUnittest + +# flags to use when building the mintl.lib library +DLIBFLAGS=$(DFLAGS) -release -I.. + +DMD = dmd +LIB = lib + +targets : unittest + +unittest : unittest.exe + +LIBNAME = mintl.lib + +#mintl : $(LIBNAME) + +SRC = all.d \ + array.d \ + arraylist.d \ + arrayheap.d \ + deque.d \ + hashaa.d \ + list.d \ + slist.d \ + share.d \ + adapter.d \ + stack.d \ + queue.d \ + set.d \ + multiaa.d \ + mem.d \ + sorting.d \ + sortedaa.d + +OBJS = all.obj \ + array.obj \ + arraylist.obj \ + arrayheap.obj \ + deque.obj \ + hashaa.obj \ + list.obj \ + slist.obj \ + share.obj \ + adapter.obj \ + stack.obj \ + queue.obj \ + set.obj \ + multiaa.obj \ + mem.obj \ + sorting.obj \ + sortedaa.obj + +.d.obj : + $(DMD) -c $(DLIBFLAGS) -of$@ $< + +$(LIBNAME) : $(OBJS) $(SRC) + $(LIB) -c $@ $(OBJS) + + +unittest.exe : $(LIBNAME) $(SRC) + $(DMD) $(DUNITFLAGS) unittest.d -ofunittest.exe $(SRC) + +clean: + del *.obj + del $(LIBNAME) + IF EXIST unittest.exe del unittest.exe