1 /++
2 	This module contains classes that are related to data storage
3 +/
4 module utils.lists;
5 
6 import std.file;
7 import std.stdio;
8 import utils.misc;
9 
10 /// Use to manage dynamic arrays that frequently change lengths
11 /// 
12 /// Provides more functionality for arrays, like searching in arrays, removing elements...
13 class List(T){
14 private:
15 	T[] list;
16 	uinteger taken=0;
17 	uinteger extraAlloc;
18 public:
19 	/// constructor
20 	/// 
21 	/// extraCount is the number of extra space to make for new elements when making "room" for new elements
22 	this(uinteger extraCount = 4){
23 		extraAlloc = extraCount;
24 	}
25 	/// appends an element to the list
26 	void add(T dat){
27 		if (taken==list.length){
28 			list.length+=extraAlloc;
29 		}
30 		taken++;
31 		list[taken-1] = dat;
32 	}
33 	/// appends an array to the list
34 	void addArray(T[] dat){
35 		list.length = taken;
36 		list ~= dat;
37 		taken += dat.length;
38 	}
39 	/// Changes the value of element at an index.
40 	/// 
41 	/// `dat` is the new data
42 	void set(uinteger index, T dat){
43 		list[index]=dat;
44 	}
45 	/// Removes last elements(s) starting from an index; number of elements to remove is in `count`
46 	void remove(uinteger index, uinteger count=1){
47 		integer i;
48 		integer till=taken-count;
49 		for (i=index;i<till;i++){
50 			list[i] = list[i+count];
51 		}
52 		list.length-=count;
53 		taken-=count;
54 	}
55 	/// Removes last elements(s); number of elements to remove is in `count`
56 	void removeLast(uinteger count = 1){
57 		taken -= count;
58 		if (list.length-taken>extraAlloc){
59 			list.length=taken;
60 		}
61 	}
62 	/// shrinks the size of the list, removing last elements.
63 	void shrink(uinteger newSize){
64 		if (newSize < taken){
65 			list.length=newSize;
66 			taken = list.length;
67 		}
68 	}
69 	/// Inserts an array into this list
70 	void insert(uinteger index, T[] dat){
71 		integer i;
72 		T[] ar,ar2;
73 		ar=list[0..index];
74 		ar2=list[index..taken];
75 		list.length=0;
76 		list=ar~dat~ar2;
77 		taken+=dat.length;
78 	}
79 	/// Inserts an element into this list
80 	void insert(uinteger index, T dat){
81 		integer i;
82 		T[] ar,ar2;
83 		ar=list[0..index];
84 		ar2=list[index..taken];
85 		list=(ar~[dat]~ar2).dup;
86 		taken++;
87 	}
88 	/// Writes the list to a file.
89 	/// 
90 	/// `sp` is the line separator. In case of strings, you want it to be `"\n"`;
91 	void saveFile(string s, T sp){
92 		File f = File(s,"w");
93 		uinteger i;
94 		for (i=0;i<taken;i++){
95 			f.write(list[i],sp);
96 		}
97 		f.close;
98 	}
99 	/// Reads an element at an index
100 	T read(uinteger index){
101 		return list[index];
102 	}
103 	/// Read a slice from the list.
104 	/// 
105 	/// The slice is copied to avoid data in list from getting changed
106 	T[] readRange(uinteger index,uinteger i2){
107 		T[] r;
108 		r = list[index .. i2].dup;
109 		return r;
110 	}
111 	/// Reads the last element in list.
112 	T readLast(){
113 		return list[taken-1];
114 	}
115 	/// returns last elements in list. number of elements to return is specified in `count`
116 	T[] readLast(uinteger count){
117 		T[] r;
118 		r = list[taken-count..taken].dup;
119 		return r;
120 	}
121 	/// length of the list
122 	@property integer length(){
123 		return taken;
124 	}
125 	/// Exports this list into a array
126 	T[] toArray(){
127 		return list.dup;
128 	}
129 	/// Loads array into this list
130 	void loadArray(T[] newList){
131 		uinteger i;
132 		list = newList.dup;
133 		taken = newList.length;
134 	}
135 	/// empties the list
136 	void clear(){
137 		list = [];
138 		taken = 0;
139 	}
140 	/// Returns index of the first matching element. -1 if not found
141 	/// 
142 	/// `dat` is the element to search for
143 	/// `i` is the index from where to start, default is 0
144 	/// `forward` if true, searches in a forward direction, from lower index to higher
145 	integer indexOf(bool forward=true)(T dat, integer i=0){
146 		static if (forward){
147 			for (;i<taken;i++){
148 				if (list[i]==dat){break;}
149 				if (i==taken-1){i=-1;break;}
150 			}
151 		}else{
152 			for (;i>=0;i--){
153 				if (list[i]==dat){break;}
154 				if (i==0){i=-1;break;}
155 			}
156 		}
157 		if (taken==0){
158 			i=-1;
159 		}
160 		return i;
161 	}
162 }
163 /// Unittest for List
164 unittest{
165 	List!ubyte list = new List!ubyte;
166 	//`List.insert` and `List.add` and `List.toArray`
167 	list.add(0);
168 	list.add(1);
169 	list.insert(1, 2);
170 	assert(list.toArray() == [0, 2, 1]);
171 	//`List.indexOf`
172 	assert(list.indexOf(1) == 2);
173 	//`List.clear`
174 	list.clear;
175 	assert(list.length == 0);
176 	//`List.loadArray`
177 	list.loadArray([0, 1, 2, 3]);
178 	assert(list.length == 4);
179 	assert(list.indexOf(3) == 3);
180 	//`List.addArray`
181 	list.addArray([4, 5, 6, 7, 8]);
182 	assert(list.length == 9);
183 	//`List.set` and `List.read`
184 	list.set(0, 1);
185 	assert(list.read(0) == 1);
186 	//`List.readLast`
187 	assert(list.readLast() == 8);
188 	assert(list.readLast(2) == [7, 8]);
189 	//`List.readRange`
190 	assert(list.readRange(0, 2) == [1, 1]);
191 	//`List.remove`
192 	list.remove(0, 2);
193 	assert(list.read(0) == 2);
194 	//`List.removeLast`
195 	list.removeLast(2);
196 	assert(list.readLast() == 6);
197 
198 	destroy(list);
199 }
200 
201 /// A basic stack with push, and pop
202 class Stack(T){
203 private:
204 	struct stackItem(T){
205 		T data; /// the data this item holds
206 		stackItem* prev; /// pointer to previous stackItem
207 	}
208 	stackItem!(T)* lastItemPtr;
209 	uinteger itemCount;
210 public:
211 	this(){
212 		lastItemPtr = null;
213 		itemCount = 0;
214 	}
215 	~this(){
216 		clear;
217 	}
218 	/// Appends an item to the stack
219 	void push(T item){
220 		stackItem!(T)* newItem = new stackItem!T;
221 		(*newItem).data = item;
222 		(*newItem).prev = lastItemPtr;
223 		lastItemPtr = newItem;
224 		//increase count
225 		itemCount ++;
226 	}
227 	/// Appends an array of items to the stack
228 	void push(T[] items){
229 		// put them all in stackItem[]
230 		stackItem!(T)*[] newItems;
231 		newItems.length = items.length;
232 		for (uinteger i = 0; i < items.length; i ++){
233 			newItems[i] = new stackItem!T;
234 			(*newItems[i]).data = items[i];
235 		}
236 		// make them all point to their previous item, except for the first one, which should point to `lastItemPtr`
237 		for (uinteger i = newItems.length - 1; i > 0; i --){
238 			(*newItems[i]).prev = newItems[i-1];
239 		}
240 		(*newItems[0]).prev = lastItemPtr;
241 		lastItemPtr = newItems[newItems.length - 1];
242 		//increase count
243 		itemCount += newItems.length;
244 	}
245 	/// Reads and removes an item from the stack, if no more items are present, throws Exception
246 	T pop(){
247 		// make sure its not null
248 		if (lastItemPtr !is null){
249 			T r = (*lastItemPtr).data;
250 			// delete it from stack
251 			stackItem!(T)* prevItem = (*lastItemPtr).prev;
252 			destroy(*lastItemPtr);
253 			lastItemPtr = prevItem;
254 			//decrease count
255 			itemCount --;
256 			return r;
257 		}else{
258 			throw new Exception("Cannot pop from empty stack");
259 		}
260 	}
261 	/// Reads and removes an array of items from the stack,
262 	/// if not enough items are left, throws Exception
263 	/// 
264 	/// count is the number of elements to return
265 	/// `reverse`, if true, elements are read in reverse, last-pushed is last in array
266 	T[] pop(bool reverse=false)(uinteger count){
267 		//make sure there are enough items
268 		if (itemCount >= count){
269 			T[] r;
270 			r.length = count;
271 			stackItem!(T)* ptr = lastItemPtr;
272 			static if (reverse){
273 				for (integer i = count-1; i >= 0; i --){
274 					r[i] = (*ptr).data;
275 					ptr = (*ptr).prev;
276 					// delete this item
277 					.destroy(*lastItemPtr);
278 					lastItemPtr = ptr;
279 				}
280 			}else{
281 				for (uinteger i = 0; i < count; i ++){
282 					r[i] = (*ptr).data;
283 					ptr = (*ptr).prev;
284 					//delete it
285 					.destroy(*lastItemPtr);
286 					lastItemPtr = ptr;
287 				}
288 			}
289 			//decrease count
290 			itemCount -= r.length;
291 			return r;
292 		}else{
293 			throw new Exception("Not enough items in stack");
294 		}
295 	}
296 	/// Empties the stack
297 	void clear(){
298 		// go through all items and delete em
299 		stackItem!(T)* ptr;
300 		ptr = lastItemPtr;
301 		while (ptr !is null){
302 			stackItem!(T)* prevPtr = (*ptr).prev;
303 			destroy(*ptr);
304 			ptr = prevPtr;
305 		}
306 		lastItemPtr = null;
307 		itemCount = 0;
308 	}
309 	/// Number of items in stack
310 	@property uinteger count(){
311 		return itemCount;
312 	}
313 }
314 /// Unittests for Stack
315 unittest{
316 	Stack!ubyte stack = new Stack!ubyte;
317 	//`Stack.push` and `Stack.pop`
318 	stack.push(0);
319 	stack.push([1, 2]);
320 	assert(stack.pop == 2);
321 	assert(stack.pop(2) == [1, 0]);
322 	stack.push([1, 0]);
323 	assert(stack.pop!(true)(2) == [1, 0]);
324 	//`Stack.clear` && `Stack.count`
325 	stack.push(0);
326 	assert(stack.count == 1);
327 	stack.clear;
328 	assert(stack.count == 0);
329 	stack.destroy;
330 }
331 
332 /// A linked list, used where only reading in the forward direction is required
333 class LinkedList(T){
334 private:
335 	///represents an item in a linked list. contains the item, and pointer to the next item's container
336 	struct LinkedItem(T){
337 		T data;
338 		LinkedItem!(T)* next = null;//mark it null to show the list has ended
339 	}
340 	LinkedItem!(T)* firstItemPtr;
341 	LinkedItem!(T)* lastItemPtr;//the pointer of the last item, used for appending new items
342 	LinkedItem!(T)* nextReadPtr;//the pointer of the next item to be read
343 	LinkedItem!(T)* lastReadPtr;//the pointer to the last item that was read
344 
345 	uinteger itemCount;//stores the total number of items
346 
347 	LinkedItem!(T)*[uinteger] bookmarks;
348 public:
349 	this(){
350 		firstItemPtr = null;
351 		lastItemPtr = null;
352 		nextReadPtr = null;
353 		lastReadPtr = null;
354 		itemCount = 0;
355 	}
356 	~this(){
357 		//free all the memory occupied
358 		clear();
359 	}
360 	///clears/resets the list. Frees all the occupied memory, & removes all items
361 	void clear(){
362 		//make sure that the list is populated
363 		if (firstItemPtr !is null){
364 			LinkedItem!(T)* nextPtr;
365 			for (nextReadPtr = firstItemPtr; nextReadPtr !is null; nextReadPtr = nextPtr){
366 				nextPtr = (*nextReadPtr).next;
367 				destroy(*nextReadPtr);
368 			}
369 			//reset all variables
370 			firstItemPtr = null;
371 			lastItemPtr = null;
372 			nextReadPtr = null;
373 			lastReadPtr = null;
374 			itemCount = 0;
375 		}
376 	}
377 	///adds a new node at the end of the list
378 	void append(T item){
379 		LinkedItem!(T)* ptr = new LinkedItem!(T);
380 		(*ptr).data = item;
381 		(*ptr).next = null;
382 		//add it to the list
383 		if (firstItemPtr is null){
384 			firstItemPtr = ptr;
385 			nextReadPtr = firstItemPtr;
386 		}else{
387 			(*lastItemPtr).next = ptr;
388 		}
389 		//mark this item as last
390 		lastItemPtr = ptr;
391 		//increase item count
392 		itemCount ++;
393 	}
394 	///adds new nodes at end of list from an array
395 	void append(T[] items){
396 		if (items.length > 0){
397 			LinkedItem!(T)*[] newNodes;
398 			newNodes.length = items.length;
399 			// put nodes inside the LinkedItem list
400 			for (uinteger i = 0; i < items.length; i++){
401 				newNodes[i] = new LinkedItem!T;
402 				(*newNodes[i]).data = items[i];
403 			}
404 			// make them point to their next node
405 			for (uinteger i = 0, end = newNodes.length-1; i < end; i ++){
406 				(*newNodes[i]).next = newNodes[i+1];
407 			}
408 			// make last item from newNodes point to null
409 			(*newNodes[newNodes.length-1]).next = null;
410 			// make the last item point to first item in newNodes
411 			if (firstItemPtr is null){
412 				firstItemPtr = newNodes[0];
413 				nextReadPtr = newNodes[0];
414 			}else{
415 				(*lastItemPtr).next = newNodes[0];
416 			}
417 			// mark the last item in newNodes as last in list
418 			lastItemPtr = newNodes[newNodes.length-1];
419 			//increase count
420 			itemCount += newNodes.length;
421 		}
422 	}
423 	///removes the first node in list
424 	void removeFirst(){
425 		//make sure list is populated
426 		if (firstItemPtr !is null){
427 			LinkedItem!(T)* first;
428 			first = firstItemPtr;
429 			//mark the second item as first, if there isn't a second item, it'll automatically be marked null
430 			firstItemPtr = (*firstItemPtr).next;
431 			//if nextReadPtr is firstItemPtr, move it to next as well
432 			if (nextReadPtr is first){
433 				nextReadPtr = firstItemPtr;
434 			}
435 			// if the last-read is pointing to first item, null it
436 			if (lastReadPtr is first){
437 				lastItemPtr = null;
438 			}
439 			//free memory occupied by first
440 			destroy(*first);
441 			//decrease count
442 			itemCount --;
443 		}
444 	}
445 	///removes the node that was last read using `LinkedList.read`. The last node cannot be removed using this.
446 	///returns true on success
447 	///
448 	///It works by moving contents of next item into the last-read one, and removing the next node
449 	bool removeLastRead(){
450 		bool r = false;
451 		if (lastReadPtr !is null){
452 			LinkedItem!(T)* thisItem = lastReadPtr;// the item to delete
453 			LinkedItem!(T)* nextItem = (*thisItem).next;// the item after last read
454 			// make sure that the item to be deleted isn't last
455 			if (nextItem !is null){
456 				// move contents of next to this item
457 				thisItem.data = nextItem.data;
458 				// set the pointer to the item after next
459 				thisItem.next = nextItem.next;
460 				// if nextItem is last item, move last item pointer to thisItem
461 				if (nextItem is lastItemPtr){
462 					lastItemPtr = thisItem;
463 				}
464 				// delete nextItem
465 				destroy(*nextItem);
466 
467 				r = true;
468 			}else{
469 				// if there is only one item, or the pointer to second-last item is available, then it can be deleted
470 				if (itemCount == 1){
471 					// just clearing the list will do the job
472 					this.clear();
473 					// but we must increase the item count because at the end, it will be deceased by one
474 					itemCount ++;// a workaround...
475 					r = true;
476 				}else{
477 					//we'll have to read till second-last item to get be able to remove the last item
478 					LinkedItem!(T)* item = firstItemPtr;
479 					for (uinteger i = 0, end = itemCount-2; i < end; i ++){
480 						item = item.next;
481 					}
482 					// now `item` is pointing to second last item, make sure this is true
483 					if (item.next == lastItemPtr){
484 						//make the list end here
485 						item.next = null;
486 						// destroy last one
487 						destroy(*lastItemPtr);
488 						lastItemPtr = item;
489 
490 						r = true;
491 					}/*else{
492 						something that shouldn't have gone wrong went wrong with `LinkedList.itemCount`
493 					}*/
494 
495 				}
496 			}
497 		}
498 		//decrease count
499 		if (r){
500 			itemCount --;
501 			//since the last-read has been removed, null that pointer, to prevent segFault
502 			lastReadPtr = null;
503 
504 		}
505 		return r;
506 	}
507 	///number of items that the list is holding
508 	@property uinteger count(){
509 		return itemCount;
510 	}
511 	///resets the read position, i.e: set reading position to first node, and nulls the last-read-ptr
512 	void resetRead(){
513 		nextReadPtr = firstItemPtr;
514 		lastReadPtr = null;
515 	}
516 	///returns pointer of next node to be read, null if there are no more nodes
517 	T* read(){
518 		T* r;
519 		if (nextReadPtr !is null){
520 			r = &((*nextReadPtr).data);
521 			//mark this item as last read
522 			lastReadPtr = nextReadPtr;
523 			//move read position
524 			nextReadPtr = (*nextReadPtr).next;
525 		}else{
526 			r = null;
527 			lastReadPtr = null;
528 		}
529 		return r;
530 	}
531 	/// Returns the pointer to the first node in the list
532 	T* readFirst(){
533 		if (firstItemPtr !is null){
534 			lastReadPtr = firstItemPtr;
535 			return &((*firstItemPtr).data);
536 		}else{
537 			return null;
538 		}
539 	}
540 	/// Returns the pointer to the last node in the list
541 	T* readLast(){
542 		if (lastItemPtr !is null){
543 			lastReadPtr = lastItemPtr;
544 			return &((*lastItemPtr).data);
545 		}else{
546 			return null;
547 		}
548 	}
549 	/// Reads the list into an array, and returns the array
550 	T[] toArray(){
551 		LinkedItem!(T)* currentNode = firstItemPtr;
552 		uinteger i = 0;
553 		T[] r;
554 		r.length = itemCount;
555 		while (currentNode !is null){
556 			r[i] = (*currentNode).data;
557 			// move to next node
558 			currentNode = (*currentNode).next;
559 			i ++;
560 		}
561 		return r;
562 	}
563 	/// Inserts a node after the position of last-read-node
564 	/// To insert at beginning, call `resetRead` before inserting
565 	/// 
566 	/// For inserting more than one nodes, use `LinkedList.insertNodes`
567 	void insert(T node){
568 		LinkedItem!(T)* newNode = new LinkedItem!T;
569 		(*newNode).data = node;
570 		// check if has to insert at beginning or at after last-read
571 		if (lastReadPtr !is null){
572 			// make new node point to the current next-to-be-read node
573 			(*newNode).next = lastReadPtr.next;
574 			// make last read node point to new node
575 			(*lastReadPtr).next = newNode;
576 		}else{
577 			// make this item point to first-item
578 			(*newNode).next = firstItemPtr;
579 			// mark this as first item
580 			firstItemPtr = newNode;
581 		}
582 		// make next read point to this node now
583 		nextReadPtr = newNode;
584 		//increase count
585 		itemCount ++;
586 	}
587 	/// Inserts an array of nodes after the position of last-read-node
588 	/// If there is no last-read-item, the item is inserted at beginning. To do this, call `resetRead` before inserting
589 	/// Returns true on success, false on failure
590 	void insert(T[] nodes){
591 		if (nodes.length > 0){
592 			LinkedItem!(T)*[] newNodes;
593 			newNodes.length = nodes.length;
594 			// put nodes inside the LinkedItem list
595 			for (uinteger i = 0; i < nodes.length; i++){
596 				newNodes[i] = new LinkedItem!T;
597 				(*newNodes[i]).data = nodes[i];
598 			}
599 			// make them point to their next node
600 			for (uinteger i = 0, end = newNodes.length-1; i < end; i ++){
601 				(*newNodes[i]).next = newNodes[i+1];
602 			}
603 			// check if has to insert at beginning or at after last-read
604 			if (lastReadPtr !is null && nodes.length > 0){
605 				// and make the last node in list point to the node after last-read
606 				(*newNodes[newNodes.length-1]).next = (*lastReadPtr).next;
607 				// make last read node point to the first new-node
608 				(*lastReadPtr).next = newNodes[0];
609 			}else{
610 				// insert at beginning
611 				(*newNodes[newNodes.length-1]).next = firstItemPtr;
612 				// make this the first node
613 				firstItemPtr = newNodes[0];
614 			}
615 			//make next read point to this
616 			nextReadPtr = newNodes[0];
617 			//increase count
618 			itemCount += nodes.length;
619 		}
620 	}
621 	/// Returns true if list contains a node, i.e searches for a node and returns true if found
622 	bool hasElement(T node){
623 		bool r = false;
624 		LinkedItem!(T)* currentNode = firstItemPtr;
625 		while (currentNode !is null){
626 			if ((*currentNode).data == node){
627 				r = true;
628 				break;
629 			}
630 			// move to next node
631 			currentNode = (*currentNode).next;
632 		}
633 		return r;
634 	}
635 	/// Returns true if list contains all elements provided in an array, else, false
636 	/// 
637 	/// returns false if the array contains the same elements at more than one index
638 	bool hasElements(T[] nodes){
639 		bool r = false;
640 		nodes = nodes.dup;
641 		// go through the list and match as many elements as possible
642 		LinkedItem!(T)* currentNode = firstItemPtr;
643 		while (currentNode !is null){
644 			// check if current node matches any in array
645 			integer index = nodes.indexOf((*currentNode).data);
646 			if (index >= 0){
647 				// this node matched, so remove it from the array
648 				nodes = nodes.deleteElement(index);
649 			}
650 			// check if all elements have been checked against
651 			if (nodes.length == 0){
652 				break;
653 			}
654 			// move to next node
655 			currentNode = (*currentNode).next;
656 		}
657 		// Now check if the nodes array is empty, if yes, then all nodes were matched
658 		if (nodes.length == 0){
659 			r = true;
660 		}
661 		return r;
662 	}
663 	/// Sets a "bookmark", and returns the bookmark-ID, throws Exception if there is no last-read-item to place bookmark on
664 	/// 
665 	/// this ID can later be used to go back to the reading position at which the bookmark was placed
666 	uinteger placeBookmark(){
667 		if (lastReadPtr is null){
668 			throw new Exception("no last-read-item to place bookmark on");
669 		}else{
670 			// go through bookmarks list to find empty slot, or create a new one
671 			uinteger id = 0;
672 			while (true){
673 				if (id in bookmarks){
674 					id ++;
675 				}else{
676 					break;
677 				}
678 			}
679 			bookmarks[id] = lastReadPtr.next;
680 			return id;
681 		}
682 	}
683 	/// moves read/insert position back to a bookmark using the bookmark ID
684 	/// Does NOT delete the bookmark. Use `LinkedList.removeBookmark` to delete
685 	/// Retutns true if successful
686 	/// false if the bookmark ID no longer exists
687 	bool moveToBookmark(uinteger id){
688 		if (id !in bookmarks){
689 			return false;
690 		}else{
691 			nextReadPtr = bookmarks[id];
692 			return true;
693 		}
694 	}
695 	/// removes a bookmark using the bookmark id
696 	/// returns true if successful
697 	/// false if the bookmark doesn't exist
698 	bool removeBookmark(uinteger id){
699 		if (id !in bookmarks){
700 			return false;
701 		}else{
702 			bookmarks.remove(id);
703 			return true;
704 		}
705 	}
706 	/// Removes all bookmarks
707 	void clearBookmarks(){
708 		foreach(key; bookmarks.keys){
709 			bookmarks.remove(key);
710 		}
711 	}
712 }
713 /// Unittests for `utils.lists.LinkedList`
714 unittest{
715 	LinkedList!ubyte list = new LinkedList!ubyte;
716 	//`LinkedList.append` and `LinkedList.read` and `LinkedList.readFirst` and `LinkedList.readLast` and `LinkedList.resetRead`
717 	list.append(0);
718 	list.append(1);
719 	list.append(2);
720 	assert(*(list.readFirst()) == 0);
721 	assert(*(list.readLast()) == 2);
722 	assert(list.count == 3);
723 	list.read();// to skip, we wanna read the node at index 1 (2nd node)
724 	assert(*(list.read()) == 1);
725 	list.resetRead();
726 	assert(*(list.read()) == 0);
727 	// `LinkedList.append(T[])`:
728 	list.clear();
729 	list.append(0);
730 	list.append([1, 2, 3]);
731 	assert(list.count == 4);
732 	assert(list.toArray ==[0, 1, 2, 3]);
733 	list.clear;
734 	list.append([0, 1, 2]);
735 	list.append(3);
736 	assert(list.count == 4);
737 	assert(list.toArray == [0, 1, 2, 3]);
738 	//`LinkedList.clear`
739 	list.clear();
740 	list.append(3);
741 	list.append(4);
742 	assert(*(list.read()) == 3);
743 	assert(list.count == 2);
744 	list.clear();
745 	//`LinkedList.removeLastRead` and `Linkedlist.removeFirst`
746 	list.append(0);
747 	list.append(1);
748 	list.append(2);
749 	list.read();
750 	list.read();
751 	list.removeLastRead();
752 	list.resetRead();
753 	assert(*(list.read()) == 0);
754 	assert(*(list.read()) == 2);
755 	assert(list.count == 2);
756 	list.removeFirst();
757 	list.resetRead();
758 	assert(*(list.read()) == 2);
759 	assert(list.count == 1);
760 	list.removeLastRead();
761 	assert(list.count == 0);
762 	//`LinkedList.toArray` and `LinkedList.insertNode` and `LinkedList.insertNodes`
763 	list.clear();// to reset stuff
764 	list.append(0);
765 	list.append(4);
766 	list.read();
767 	list.insert(1);
768 	assert(*(list.read()) == 1);
769 	list.insert([2, 3]);
770 	list.resetRead();
771 	assert(list.count == 5);
772 	assert(list.toArray == [0, 1, 2, 3, 4]);
773 	//`Linkedlist.hasElement` and `LinkedList.hasElements`
774 	assert(list.hasElement(0) == true);
775 	assert(list.hasElement(4) == true);
776 	assert(list.hasElement(5) == false);
777 	assert(list.hasElement(7) == false);
778 	assert(list.hasElements([3, 1, 2, 0, 4]) == true);
779 	assert(list.hasElements([0, 1, 2, 6]) == false);
780 	// `LinkedList.insert` at beginning
781 	list.clear;
782 	list.insert([1, 2]);
783 	list.insert(0);
784 	assert(list.count == 3);
785 	assert(list.toArray == [0, 1, 2]);
786 	//destroying last item
787 	list.clear();
788 	list.append(0);
789 	list.append(1);
790 	list.append(2);
791 	list.read();
792 	list.read();
793 	list.read();
794 	assert(list.removeLastRead() == true);
795 	assert(list.toArray() == [0, 1]);
796 	//bookmarks
797 	list.clear;
798 	list.append([0, 1, 2, 3, 4, 5]);
799 	assert(*list.read == 0);
800 	assert(*list.read == 1);
801 	assert(*list.read == 2);
802 	{
803 		uinteger id = list.placeBookmark;
804 		assert(*list.read == 3);
805 		assert(list.moveToBookmark(id + 1) == false);
806 		assert(list.moveToBookmark(id) == true);
807 		assert(*list.read == 3);
808 		assert(list.removeBookmark(id) == true);
809 	}
810 	destroy(list);
811 }
812 
813 /// Used in logging widgets. Holds upto certain number of elements, after which older elements are over-written
814 class LogList(T){
815 private:
816 	List!T list;
817 	uinteger readFrom, maxLen;
818 public:
819 	this(uinteger maxLength=100){
820 		list = new List!T;
821 		readFrom = 0;
822 		maxLen = maxLength;
823 	}
824 	~this(){
825 		delete list;
826 	}
827 	///adds an item to the log
828 	void add(T dat){
829 		if (list.length>=maxLen){
830 			list.set(readFrom,dat);
831 			readFrom++;
832 		}else{
833 			list.add(dat);
834 		}
835 	}
836 	///Returns array containing items, in first-added-last order
837 	T[] read(uinteger count=0){
838 		T[] r;
839 		if (count>list.length){
840 			count = list.length;
841 		}
842 		if (count > 0){
843 			uinteger i;
844 			if (count>list.length){
845 				count = list.length;
846 			}
847 			r.length = count;
848 			for (i = readFrom; i < count; i++){
849 				r[i] = list.read((readFrom+i)%count);
850 			}
851 		}else{
852 			r = null;
853 		}
854 		return r;
855 	}
856 	///resets and clears the log
857 	void reset(){
858 		list.clear;
859 		readFrom = 0;
860 	}
861 	///returns the max number of items that can be stored
862 	@property uinteger maxCapacity(){
863 		return maxLen;
864 	}
865 }
866 
867 /// For reading files. Can also be used for writing
868 class FileReader{
869 private:
870 	ubyte[] stream = null;
871 	uinteger seekPos = 0;
872 public:
873 	/// If filename is not null, attempts to load file into memory
874 	this(string filename=null){
875 		if (filename != null){
876 			this.loadFile(filename);
877 		}
878 	}
879 	~this(){
880 		// destructor, nothing to do yet
881 	}
882 	/// loads file into memory, throws exception if fails
883 	void loadFile(string filename){
884 		stream = cast(ubyte[])std.file.read(filename);
885 		seekPos = 0;
886 	}
887 	/// Writes the stream to a file, throws exception if fails
888 	void saveFile(string filename){
889 		std.file.write(filename, cast(void[])stream.dup);
890 	}
891 
892 	/// reads and returns `size` number of bytes from file starting from seek-position
893 	/// If not enough bytes are left, the array returned will be smaller than `size`
894 	/// Returns null if the seek-position is at end, or if there are no bytes to be read
895 	void[] read(uinteger size=1){
896 		// check if `size` number of chars are left
897 		if (size + seekPos <= stream.length){
898 			void[] r = stream[seekPos .. seekPos+size].dup;
899 			seekPos += size;
900 			return r;
901 		}else if (seekPos < stream.length){
902 			void[] r = stream[seekPos .. stream.length].dup;
903 			seekPos = stream.length;
904 			return r;
905 		}else{
906 			// nothing left to read, return null
907 			return null;
908 		}
909 	}
910 	/// Reads and returns bytes starting from seek till `terminate` byte, or if EOF is reached
911 	void[] read(ubyte terminate){
912 		uinteger readFrom = seekPos;
913 		while (seekPos < stream.length){
914 			if (stream[seekPos] == terminate){
915 				seekPos ++;// to include the terminate byte in result
916 				break;
917 			}
918 			seekPos ++;
919 		}
920 		return stream[readFrom .. seekPos].dup;
921 	}
922 	/// Writes an array at the seek-position, and moves seek to end of the written data
923 	void write(ubyte[] t){
924 		if (seekPos > stream.length){
925 			throw new Exception("failed to write data to stream. Seek is out of stream.length");
926 		}else if (seekPos == stream.length){
927 			// just append to end of stream
928 			stream = stream ~ t.dup;
929 			seekPos = stream.length;
930 		}else{
931 			// insert it in the middle
932 			stream = stream[0 .. seekPos] ~ t ~ stream[seekPos .. stream.length];
933 			seekPos += t.length;
934 		}
935 	}
936 	/// Writes a byte at the seek-position, and moves seek to end of the written data
937 	void write(ubyte t){
938 		write([t]);
939 	}
940 	/// Removes a number of bytes starting from the seek-position
941 	/// 
942 	/// Returns true if it was able to remove some bytes, false if not
943 	bool remove(uinteger count = 1){
944 		// make sure there are enough bytes to remove
945 		if (seekPos + count > stream.length){
946 			// remove as much as possible
947 			if (seekPos >= stream.length){
948 				return false;
949 			}else{
950 				stream = stream[0 .. seekPos-1];
951 			}
952 		}else{
953 			stream = stream[0 .. seekPos-1] ~ stream[seekPos + count .. stream.length];
954 		}
955 		return true;
956 	}
957 	/// Clears the stream, resets the stream
958 	void clear(){
959 		stream.length = 0;
960 		seekPos = 0;
961 	}
962 	/// The seek position, from where the next char(s) will be read, or written to
963 	@property uinteger seek(){
964 		return seekPos;
965 	}
966 	/// The seek position, from where the next char(s) will be read, or written to
967 	@property uinteger seek(uinteger newSeek){
968 		if (newSeek > stream.length){
969 			return seekPos = stream.length;
970 		}else{
971 			return seekPos = newSeek;
972 		}
973 	}
974 	/// The size of file in bytes, read-only
975 	@property uinteger size(){
976 		return stream.length;
977 	}
978 }
979 /// unittests for FileReader
980 unittest{
981 	// file loading/ saving not tested
982 	FileReader stream = new FileReader();
983 	assert(stream.seek == 0);
984 	// write & read
985 	stream.write(cast(ubyte[])"ABCE");
986 	assert(stream.seek == 4);
987 	assert(stream.size == 4);
988 	stream.seek = 3;
989 	stream.write(cast(ubyte)'D');
990 	assert(stream.size == 5);
991 	assert(stream.seek == 4);
992 	stream.seek = 0;
993 	assert(stream.read(stream.size) == cast(ubyte[])"ABCDE");
994 	stream.seek = 0;
995 	assert(stream.read(cast(ubyte)'C') == cast(ubyte[])"ABC");
996 	stream.seek = 0;
997 	assert(stream.read(cast(ubyte)'Z') == cast(ubyte[])"ABCDE");
998 	// clear
999 	stream.clear;
1000 	assert(stream.size == 0);
1001 	assert(stream.seek == 0);
1002 	// remove
1003 	stream.write(cast(ubyte[])"ABCDE");
1004 	stream.seek = 3;
1005 	assert(stream.remove(99) == true);
1006 	stream.seek = 0;
1007 	assert(stream.read(cast(ubyte)'Z') == cast(ubyte[])"AB");
1008 	stream.write(cast(ubyte[])"CDE");
1009 	stream.seek = 1;
1010 	assert(stream.remove(2) == true);
1011 	stream.seek = 0;
1012 	assert(stream.read(cast(ubyte)'Z') == "DE");
1013 }