1 module easyd.list;
2 
3 // (C) 2014-2021 by Matthias Rossmy
4 // This file is distributed under the "Fair Use License v2"
5 
6 import std.typecons;
7 import std.traits;
8 import std.bigint;
9 import std.stdio;
10 import std.conv;
11 import std.algorithm;
12 
13 import easyd.base;
14 
15 unittest
16 {
17 	class Person
18 	{
19 		string firstName;
20 		string lastName;
21 		string likes;
22 		
23 		this(string fn,string ln,string li)
24 		{
25 			firstName=fn;
26 			lastName=ln;
27 			likes=li;
28 		}
29 		
30 		string name()
31 		{
32 			return firstName~" "~lastName;
33 		}
34 	}
35 	
36 	List!Person persons;
37 	with(persons)
38 	{
39 		add(new Person("Walter","Bright","D"));
40 		add(new Person("Andrei","Alexandrescu","D"));
41 		add(new Person("Bjarne","Stroustrup","C++"));
42 	}
43 	
44 	auto selection = persons.selection.filter(p => p.likes=="D").sortAsc(p => p.firstName);
45 	assert(selection[0].name=="Andrei Alexandrescu");
46 	assert(selection[1].name=="Walter Bright");
47 	
48 	auto index = persons.createIndex(p => p.name);
49 	auto p = index["Andrei Alexandrescu"];
50 	assert(p.likes=="D");
51 }
52 
53 struct LinkedListDescriptor
54 {
55 	ptrdiff_t startLocation = -1;
56 	
57 	void del(TIterator)(ref TIterator it)
58 	{
59 		it.container.del(this,it);
60 	}
61 }
62 
63 struct LinkedListPointer(TValue)
64 {
65 	ptrdiff_t location;
66 	CLinkedListContainer!TValue container;
67 
68 	this(const CLinkedListContainer!TValue cont, LinkedListDescriptor list)
69 	{
70 		container = cast(CLinkedListContainer!TValue)cont;
71 		location = list.startLocation;
72 	}
73 	
74 	this(CLinkedListContainer!TValue cont, ptrdiff_t idx)
75 	{
76 		container = cont;
77 		location = idx;
78 	}
79 	
80 	bool hasValue()
81 	{
82 		return location>=0;
83 	}
84 	
85 	ref TValue value()
86 	{
87 		return container.values[location];
88 	}	
89 
90 	private ptrdiff_t nextLocation()
91 	{
92 		if(hasValue)
93 		{
94 			return container.nextLocations[location];
95 		}
96 		else
97 		{
98 			return -1;
99 		}
100 	}
101 	
102 	private ptrdiff_t prevLocation()
103 	{
104 		if(hasValue)
105 		{
106 			return container.prevLocations[location];
107 		}
108 		else
109 		{
110 			return -1;
111 		}
112 	}
113 }
114 
115 struct LinkedListIterator(TValue)
116 {
117 	LinkedListPointer!TValue ptr;
118 	alias ptr this;
119 
120 	bool skipNextInc=false;
121 	
122 	this(const CLinkedListContainer!TValue cont, LinkedListDescriptor list)
123 	{
124 		container = cast(CLinkedListContainer!TValue)cont;
125 		location = list.startLocation;
126 	}
127 	
128 	this(CLinkedListContainer!TValue cont, ptrdiff_t idx)
129 	{
130 		container = cont;
131 		location = idx;
132 	}
133 	
134 	void opUnary(string op)()
135 		if (op == "++")
136 	{
137 		if(!skipNextInc) location = nextLocation();
138 		skipNextInc = false;
139 	}
140 
141 	void opUnary(string op)()
142 		if (op == "--")
143 	{
144 		location = prevLocation();
145 	}
146 }
147 
148 class CLinkedListContainer(TValue)
149 {
150 	immutable static bool debugMode=false;
151 
152 	void delegate(LinkedListPointer!TValue)[] beforeDel;
153 	TValue[] values;
154 	protected ptrdiff_t[] nextLocations;
155 	protected ptrdiff_t[] prevLocations;
156 	protected ptrdiff_t freeLocation=-1;
157 
158 	void validate(string info="")
159 	{
160 		size_t[] hits;
161 		ptrdiff_t signedLength = values.length;
162 		hits.length = values.length;
163 		for(size_t i=0; i<hits.length; i++) hits[i]=0;
164 		size_t steps=0;
165 		//Check consistency of free list
166 		if(freeLocation>=0) //wenn keine freien Elemente im Container, ist freeLocation=-1
167 		{
168 			ptrdiff_t p = freeLocation;
169 			while(p>=0)
170 			{
171 				hits[p]++;
172 				if(nextLocations[p]>=signedLength)
173 				{
174 					throw new Exception("nextLocations["~p.to!string~"] points outside of the container  "~info);
175 				}
176 				p=nextLocations[p];
177 				if(++steps > values.length+100)
178 				{
179 					throw new Exception("Infinite loop in a linked list  "~info);
180 				}
181 			}
182 		}
183 		//Check consistency of real lists
184 		static if(debugMode) //without debugMode prevLocations is not maintained for free items,
185 		{					//so there would be false positive list starts
186 			for(size_t i=0; i<hits.length; i++) if(prevLocations[i]<0 && i!=freeLocation)
187 			{
188 				ptrdiff_t p = i;
189 				while(p>=0)
190 				{
191 					hits[p]++;
192 					if(nextLocations[p]>=signedLength)
193 					{
194 						throw new Exception("nextLocations["~p.to!string~"] points outside of the container  "~info);
195 					}
196 					p=nextLocations[p];
197 					if(++steps > values.length+100)
198 					{
199 						throw new Exception("Infinite loop in a linked list  "~info);
200 					}
201 				}
202 			}
203 		}
204 		//Check that every item belongs to exactly 1 list
205 		for(size_t i=0; i<hits.length; i++)
206 		{
207 			static if(debugMode) //without DebugMode the "lists test" must be skipped, so not every item may have been hit
208 			{
209 				if(hits[i]==0)
210 				{
211 					throw new Exception("Container item "~i.to!string~" is not reachable  "~info);
212 				}
213 			}
214 			if(hits[i]> 1)
215 			{
216 				throw new Exception("Container item "~i.to!string~" is used by multiple lists  "~info);
217 			}
218 		}
219 	}
220 	
221 	void clear()
222 	{
223 		if(values.length==0) return;
224 
225 		for(ptrdiff_t pos=0; pos<values.length; pos++)
226 		{
227 			static if(is(TValue:Object) || isDynamicArray!TValue || isAssociativeArray!TValue)
228 			{
229 				values[pos] = null;
230 			}
231 			nextLocations[pos] = pos + 1;
232 			static if(debugMode) prevLocations[pos] = pos - 1;
233 		}
234 		nextLocations[values.length-1] = -1;
235 
236 		freeLocation = 0;
237 
238 		static if(debugMode) validate;
239 	}
240 	
241 	void addAnywhere(ref LinkedListDescriptor list, TValue val)
242 	{
243 		//insert after 1st item, because that requires no searching
244 		if(freeLocation>=0 && list.startLocation==freeLocation)
245 		{
246 			throw new Exception("Start location of list conflicts with freeLocation ("~freeLocation.to!string~")");
247 		}
248 		addAfter(list,list.startLocation,val);
249 	}
250 	
251 	ptrdiff_t addAfter(ref LinkedListDescriptor list, ptrdiff_t prevLocation, TValue val)
252 	{
253 		//writeln("addAfter " ~ prevLocation.to!string);
254 		auto pos = addHelper(val);
255 		//writeln("DestPos: " ~ pos.to!string);
256 		
257 		if(list.startLocation<0)
258 		{   
259 			//writeln("create 1-item-list");
260 			nextLocations[pos] = -1;
261 			prevLocations[pos] = -1;
262 			list.startLocation = pos;
263 
264 			static if(debugMode) validate; //use 3 different validate calls in this function, so that the backtrace shows which case failed
265 		}
266 		else if (prevLocation<0)
267 		{	
268 			//writeln("insert at beginning");
269 			auto nextLocation = list.startLocation;
270 			link(pos,nextLocation);
271 			prevLocations[pos] = -1;
272 			list.startLocation = pos;
273 
274 			static if(debugMode) validate;
275 		}
276 		else
277 		{   
278 			//writeln("insert into list");
279 			auto nextLocation = nextLocations[prevLocation];
280 			link(prevLocation,pos);
281 			link(pos,nextLocation);
282 
283 			static if(debugMode) validate("prevLocation="~prevLocation.to!string~" pos="~pos.to!string~" nextLocation="~nextLocation.to!string);
284 		}
285 		
286 		//writeln("Finished addAfter");
287 		return pos;
288 	}
289 
290 	void link(ptrdiff_t pos1,ptrdiff_t pos2)
291 	{
292 		if(pos1>=0) nextLocations[pos1] = pos2;
293 		if(pos2>=0) prevLocations[pos2] = pos1;
294 	}
295 
296 	ptrdiff_t del(ref LinkedListDescriptor list, LinkedListPointer!TValue it) //returns location of next item
297 	{
298 		if(it.container!=this)
299 		{
300 			throw new Exception("LinkedListPointer not from this container");
301 		}
302 
303 		beforeDel.trigger(it);
304 		
305 		auto nextLocation = it.nextLocation;
306 		auto prevLocation = it.prevLocation;
307 
308 		static if(debugMode) if((it.location==list.startLocation)!=(prevLocation==-1))
309 		{
310 			if(prevLocation==-1)
311 			{
312 				throw new Exception("Iterator has no predecessor, but it is not at the start location of the list");
313 			}else{
314 				throw new Exception("Iterator has a predecessor, but it is at the start location of the list");
315 			}
316 		}
317 		
318 		//writeln("prev=",prevLocation," next=",nextLocation);
319 		
320 		//Wert zurücksetzen, damit der GC keine unnötigen Objekte behält
321 		static if(is(TValue==class) || isDynamicArray!TValue)
322 		{
323 			values[it.location] = null;
324 		}
325 		
326 		//eigene Liste wieder zusammenfügen
327 		if(it.location==list.startLocation)
328 		{
329 			//writeln("Change list start");
330 			list.startLocation = nextLocation;
331 			link(-1,nextLocation);
332 		}
333 		else
334 		{
335 			link(prevLocation,nextLocation);
336 		}
337 		
338 		//frei gewordenes Item am Anfang der Frei-Liste einfügen
339 		link(it.location,freeLocation);
340 		prevLocations[it.location] = -1;
341 		freeLocation = it.location;
342 
343 		static if(debugMode) validate;
344 
345 		return nextLocation;
346 	}
347 
348 	void del(ref LinkedListDescriptor list, ref LinkedListIterator!TValue it)
349 	{
350 		it.location = del(list,it.ptr);
351 		it.skipNextInc = true;
352 	}
353 	
354 	LinkedListIterator!TValue iterator(LinkedListDescriptor list) const
355 	{
356 		return LinkedListIterator!TValue(this,list);
357 	}
358 	
359 	protected ptrdiff_t addHelper(TValue val)
360 	{
361 		if(freeLocation<0) increaseSize();
362 		
363 		ptrdiff_t pos = freeLocation;
364 		freeLocation = nextLocations[pos];
365 		values[pos] = val;
366 		
367 		return pos;
368 	}
369 	
370 	protected void increaseSize()
371 	{
372 		immutable size_t maxInitialSize=4;
373 
374 		if(freeLocation>=0)
375 		{
376 			return;
377 		}
378 		
379 		auto oldsize = values.length;
380 		if(oldsize==0)
381 		{
382 			auto initialsize = 1024 / max(TValue.sizeof,ptrdiff_t.sizeof); //initial max. 1 KB pro Sub-Array
383 			if(initialsize<1) initialsize=1;
384 			if(initialsize>maxInitialSize) initialsize=maxInitialSize;
385 			values.length = initialsize;
386 			nextLocations.length = initialsize;
387 			prevLocations.length = initialsize;
388 		}
389 		else if(oldsize<=MEBI)
390 		{
391 			values.length *= 2;
392 			nextLocations.length *= 2;
393 			prevLocations.length *= 2;
394 		}
395 		else
396 		{
397 			values.length += MEBI;
398 			nextLocations.length += MEBI;
399 			prevLocations.length += MEBI;
400 		}
401 		
402 		for(ptrdiff_t pos=oldsize; pos<values.length; pos++)
403 		{
404 			nextLocations[pos] = pos + 1;
405 			static if(debugMode) prevLocations[pos] = pos - 1;
406 		}
407 		static if(debugMode) prevLocations[oldsize] = -1;
408 		nextLocations[values.length-1] = -1;
409 		
410 		freeLocation = oldsize;
411 
412 		static if(debugMode) validate;
413 	}
414 }
415 
416 struct LinkedListContainer(TValue)
417 {
418 	mixin ImplementStruct!(CLinkedListContainer!TValue);
419 }
420 
421 mixin template CListIndexOperations(T,TKey)
422 {
423 	ListIndex!(T,TKey) mainIndex;
424 
425 	LinkedListPointer!T pointerOf(TKey key)
426 	{
427 		if(mainIndex is null) mainIndex = createMainIndex;
428 		return mainIndex(key);
429 	}
430 
431 	Selection!T all(TKey key)
432 	{
433 		if(mainIndex is null) mainIndex = createMainIndex;
434 		return mainIndex.all(key);
435 	}
436 
437 	bool contains(TKey key)
438 	{
439 		return pointerOf(key).hasValue;
440 	}
441 	
442 	void delFirst(TKey key)
443 	{
444 		auto it = pointerOf(key);
445 		if(it.hasValue)
446 		{
447 			del(it);
448 		}
449 	}
450 
451 	void delAll(TKey key)
452 	{
453 		foreach(pos,it,val;all(key)) del(it);
454 	}
455 	
456 	static if(isAggregateType!T && !isTuple!T)
457 	{
458 		immutable string idfield = idField!T;
459 
460 		ref T opIndex(TKey key)
461 		{
462 			auto it = pointerOf(key);
463 			if(it.hasValue)
464 			{
465 				return it.value;
466 			}else{
467 				return mainIndex.defaultValue;
468 			}
469 		}
470 		
471 		static if(idfield!="")
472 		{
473 			ref T opIndex(TKey key, T def)
474 			{
475 				auto it = pointerOf(key);
476 				if(!it.hasValue)
477 				{
478 					it = add(def);
479 					auto ptr = &(it.value());
480 					__traits(getMember, *ptr, idfield) = key;
481 				}
482 				return it.value;
483 			}
484 		}
485 	}
486 }
487 
488 class CList(T) //: ISpecialSerialize
489 {
490 	size_t length = 0;
491 	bool valuesCanChange;
492 	CLinkedListContainer!T container;
493 	protected LinkedListDescriptor list;
494 	protected ptrdiff_t lastLocation;
495 	protected IListIndex[] indexes;
496 
497 	static if(isAggregateType!T && !isTuple!T)
498 	{
499 		static if(idField!T != "")
500 		{
501 			immutable string idfield = idField!T;
502 			alias typeof(__traits(getMember, T, idfield)) TKey;
503 
504 			ListIndex!(T, TKey) createMainIndex()
505 			{
506 				return createIndex(item=>__traits(getMember, item, idfield),!valuesCanChange);
507 			}
508 
509 			mixin CListIndexOperations!(T,TKey);
510 		}else{
511 			ListIndex!(T,T) createMainIndex()
512 			{
513 				return createIndex(item=>item,!valuesCanChange);
514 			}
515 
516 			mixin CListIndexOperations!(T,T);
517 
518 			void addIfNew(T item)
519 			{
520 				if(!pointerOf(item).hasValue) add(item);
521 			}
522 		}
523 	}else{ //simple type or tuple
524 		ListIndex!(T,T) createMainIndex()
525 		{
526 			return createIndex(item=>item,!valuesCanChange);
527 		}
528 
529 		mixin CListIndexOperations!(T,T);
530 
531 		void addIfNew(T item)
532 		{
533 			if(!pointerOf(item).hasValue) add(item);
534 		}
535 	}
536 	
537 	this(bool valuescanchange = true)
538 	{
539 		container.create;
540 		valuesCanChange = valuescanchange;
541 	}
542 
543 	void check()
544 	{
545 		ptrdiff_t last=-1;
546 		for(auto it=iterator;it.hasValue;it++)
547 		{
548 			if(it.prevLocation!=last) throw new Exception("Linked list broken, last item was "~last.to!string~" but prevLocation of "~it.location.to!string~" points to "~it.prevLocation.to!string);
549 			last = it.location;
550 		}
551 		writeln("List-links ok");
552 	}
553 	
554 	CList!T dup()
555 	{
556 		auto result = new CList!T;
557 		foreach(it,item;this) result.add(it.value);
558 		return result;
559 	}
560 
561 	void clear()
562 	{
563 		container.clear;
564 		list.startLocation = -1;
565 		length=0;
566 		foreach(index;indexes)
567 		{
568 			index.invalidate;
569 		}
570 	}
571 	
572 	LinkedListPointer!T add(T item)
573 	{
574 		lastLocation = container.addAfter(list,lastLocation,item);
575 		addHelper;
576 		return LinkedListPointer!T(container, lastLocation);
577 	}
578 	
579 	LinkedListPointer!T addBefore(LinkedListPointer!T before, T newItem)
580 	{
581 		//writeln("addBefore");
582 		if(!before.hasValue)
583 		{
584 			//writeln("Can't addBefore, doing normal add");
585 			add(newItem);
586 		}
587 
588 		auto result = LinkedListPointer!T(container, container.addAfter(list,before.prevLocation,newItem));
589 		addHelper;
590 		return result;
591 	}
592 	
593 	LinkedListPointer!T addAfter(LinkedListPointer!T after, T newItem)
594 	{
595 		//writeln("addAfter");
596 		if(!after.hasValue)
597 		{
598 			//writeln("Can't addAfter, doing normal add");
599 			add(newItem);
600 		}
601 		
602 		auto result = LinkedListPointer!T(container, container.addAfter(list,after.location,newItem));
603 		addHelper;
604 		return result;
605 	}
606 	
607 	static if(is(T==class) || is(T==interface))
608 	{
609 		T2 addNew(T2=T,P...)(P par)
610 		{
611 			T2 obj = new T2(par);
612 			add(obj);
613 			return obj;
614 		}
615 	}
616 	else
617 	{
618 		T* addNew()
619 		{
620 			add(T.init);
621 			return &(container.values[lastLocation]);
622 		}
623 	}
624 
625 	/*void replace(LinkedListPointer!T it, T newValue)
626 	{
627 		container.values[it.location] = newValue;
628 		//TODO: Indices aktualisieren
629 	}*/
630 	
631 	ptrdiff_t del(LinkedListPointer!T it) //returns location of next item
632 	{
633 		//writeln("list.del ",this," ",it.location);
634 		if(it.location<0)
635 		{
636 			return -1;
637 		}
638 		if(it.location==lastLocation)
639 		{
640 			lastLocation = it.prevLocation;
641 		}
642 		if(indexes !is null)
643 		{
644 			//writeln("Updating indexes...");
645 			foreach(index;indexes)
646 			{
647 				index.autoNotifyBeforeDelete(it.location);
648 			}
649 			//writeln("...finished updating indexes");
650 		}
651 		length--;
652 		return container.del(list,it);
653 	}
654 
655 	void del(ref LinkedListIterator!T it)
656 	{
657 		if(it.container!=container) return;
658 		it.location = del(it.ptr);
659 		it.skipNextInc = true;
660 	}
661 
662 	void delFirstMatch(bool delegate(T) sd)
663 	{
664 		for(auto it=iterator; it.hasValue; it++)
665 		{
666 			if(sd(it.value))
667 			{
668 				del(it);
669 				return;
670 			}
671 		}
672 	}
673 	
674 	void delAllMatches(bool delegate(T) sd)
675 	{
676 		for(auto it=iterator; it.hasValue; it++)
677 		{
678 			if(sd(it.value))
679 			{
680 				del(it);
681 			}
682 		}
683 	}
684 	
685 	LinkedListIterator!T iterator() const
686 	{
687 		return container.iterator(list);
688 	}
689 	
690 	LinkedListIterator!T lastIterator()
691 	{
692 		return LinkedListIterator!T(container,lastLocation);
693 	}
694 	
695 	ref T first()
696 	{
697 		return iterator.value;
698 	}
699 
700 	static if(isRefType!T)
701 	{
702 		T itemAt(size_t number)
703 		{
704 			if(number>=length) return null;
705 			auto result = iterator;
706 			for(size_t x=0; x<number; x++) result++;
707 			return result.value;
708 		}
709 	}else{
710 		ref T itemAt(size_t number)
711 		{
712 			if(number>=length) throw new Exception("Requesting item out of range");
713 			auto result = iterator;
714 			for(size_t x=0; x<number; x++) result++;
715 			return result.value;
716 		}
717 	}
718 	
719 	static if(is(T==class) || is(T==interface)) // class /////////////////////////////////////////////////
720 	{
721 		//pragma(msg,typeid(T)," is class");
722 
723 		int opApply(int delegate(T) dg) const
724 		{
725 			int result = 0;
726 			for (auto it = iterator; it.hasValue; it++)
727 			{
728 				result = dg(it.value);
729 				if (result) break;
730 			}
731 			return result;
732 		}
733 		
734 		int opApply(int delegate(ref LinkedListIterator!T, T) dg) const
735 		{
736 			int result = 0;
737 			for (auto it = iterator; it.hasValue; it++)
738 			{
739 				result = dg(it,it.value);
740 				if (result) break;
741 			}
742 			return result;
743 		}
744 	}
745 	else static if(__traits(hasMember, T, "tupleof")) // struct ////////////////////////////////////////////
746 	{
747 		//pragma(msg,typeid(T)," is struct");
748 		
749 		int opApply(int delegate(T*) dg) const
750 		{
751 			int result = 0;
752 			for (auto it = iterator; it.hasValue; it++)
753 			{
754 				result = dg(cast(T*)&(container.values[it.location]));
755 				if (result) break;
756 			}
757 			return result;
758 		}
759 		
760 		int opApply(int delegate(ref LinkedListIterator!T, T*) dg) const
761 		{
762 			int result = 0;
763 			for (auto it = iterator; it.hasValue; it++)
764 			{
765 				result = dg(it,cast(T*)&(container.values[it.location]));
766 				if (result) break;
767 			}
768 			return result;
769 		}
770 	}
771 	else // simple type ///////////////////////////////////////////////////////////////////////////////////
772 	{
773 		//pragma(msg,typeid(T)," is simple type");
774 		
775 		int opApply(int delegate(const T) dg) const
776 		{
777 			int result = 0;
778 			for (auto it = iterator; it.hasValue; it++)
779 			{
780 				result = dg(it.value);
781 				if (result) break;
782 			}
783 			return result;
784 		}
785 		
786 		int opApply(int delegate(ref LinkedListIterator!T, const T) dg) const
787 		{
788 			int result = 0;
789 			for (auto it = iterator; it.hasValue; it++)
790 			{
791 				result = dg(it,it.value);
792 				if (result) break;
793 			}
794 			return result;
795 		}
796 	}
797 	
798 	Selection!T selection() const
799 	{
800 		return new Selection!T(this,true);
801 	}
802 	//alias selection this;
803 	
804 	ListIndex!(T,TKey) createIndex(TKey)(TKey delegate(T) sd, bool constField=false, size_t estimListLength=0)
805 	{
806 		auto result = new ListIndex!(T,TKey)(this,sd,constField,estimListLength);
807 		indexes ~= result;
808 		return result;
809 	}
810 	
811 	bool found(bool delegate(T) sd) //darf nicht contains heißen wegen Konflikt mit CListIndexOperations
812 	{
813 		return find(sd).hasValue;
814 	}
815 	
816 	LinkedListPointer!T find(bool delegate(T) sd)
817 	{
818 		for(auto it=iterator; it.hasValue; it++)
819 		{
820 			if(sd(it.value)) return it;
821 		}
822 		return LinkedListPointer!T(container,-1);
823 	}
824 
825 	T2 find(T2)(bool delegate(T2) sd)
826 		if(is(T2==class))
827 	{
828 		foreach(item;this)
829 		{
830 			if(item.inherits!T2)
831 			{
832 				T2 t = cast(T2)item;
833 				if(sd(t)) return t;
834 			}
835 		}
836 		return T2.init;
837 	}
838 
839 	LinkedListPointer!T findMax(TMember)(TMember delegate(T) sd)
840 	{
841 		auto it=iterator;
842 		auto mx = sd(it.value);
843 		auto result = it.ptr;
844 		for(it++; it.hasValue; it++)
845 		{
846 			auto v = sd(it.value);
847 			if(v>mx)
848 			{
849 				mx = v;
850 				result = it.ptr;
851 			}
852 		}
853 		return result;
854 	}
855 	
856 	/* TODO for DocMan
857 	void bubbleUp(LinkedListPointer!T it)
858 	{
859 		container.bubbleUp(list,it);
860 	}
861 	
862 	void bubbleDown(LinkedListPointer!T it)
863 	{
864 		container.bubbleDown(list,it);
865 	}
866 	*/
867 
868 	CList!T opBinary(string op)(CList!T other)
869 	{
870 		static if (op == "~")
871 		{
872 			auto result = dup;
873 			foreach(item;other) result.add(item);
874 			return result;
875 		}else{
876 			assert(0, "Operator "~op~" not implemented");
877 		}
878 	}
879 
880 	/*void serialize(ISerializer destination) 
881 	{
882 		//writeln("List serialize");
883 		foreach(it,item;this)
884 		{
885 			destination.serializeSubItem("",it.value);
886 		}
887 	}
888 	
889 	bool deserialize(IDeserializer source)
890 	{
891 		foreach(child;source.current.children)
892 		{
893 			child.allowUnnamed;
894 			auto newitem = source.deserialize!T(child);
895 			static if(__traits(hasMember, T, "_first_member_inline_"))
896 			{
897 				newitem.tupleof[0] = StringConvert!(typeof(newitem.tupleof[0])).parse(child.value);
898 			}
899 			add(newitem);
900 		}
901 		return true;
902 	}*/
903 
904 	void compact() //ungenutzten Puffer abschneiden, nicht defragmentieren
905 	{
906 		ptrdiff_t maxloc=0;
907 		for(auto it=iterator;it.hasValue;it++) if(it.location>maxloc) maxloc=it.location;
908 		container.values.length = maxloc+1;
909 		container.nextLocations.length = maxloc+1;
910 		container.prevLocations.length = maxloc+1;
911 		if(container.freeLocation>=container.values.length) container.freeLocation=-1;
912 	}
913 	
914 	protected void addHelper()
915 	{
916 		length++;
917 		if(indexes !is null)
918 		{
919 			//writeln("Updating indexes...");
920 			foreach(index;indexes)
921 			{
922 				index.autoNotifyAdded(lastLocation);
923 			}
924 			//writeln("...finished updating indexes");
925 		}
926 	}
927 }
928 
929 struct List(T)
930 {
931 	mixin ImplementStruct!(CList!T);
932 }
933 
934 string concat(T)(CList!T l, string separator="", string delegate(T) tostr = item=>item.to!string)
935 {
936 	string result;
937 	for(auto it=l.iterator; it.hasValue; it++)
938 	{
939 		result ~= tostr(it.value);
940 		if(it.nextLocation>=0) result ~= separator;
941 	}
942 	return result;
943 }
944 
945 class Selection(T)
946 {
947 	bool sorted = false;
948 	protected T[] source;
949 	protected CList!T sourceList;
950 	protected size_t[] locations;
951 	
952 	this(const T[] src, bool addAll=false)
953 	{
954 		source = cast(T[])src;
955 		if(addAll)
956 		{
957 			for(size_t x=0; x<source.length; x++)
958 			{
959 				add(x);
960 			}
961 		}
962 	}
963 	
964 	this(const CList!T src, bool addAll=false)
965 	{
966 		source = cast(T[])(src.container.values);
967 		sourceList = cast(CList!T)src;
968 		if(addAll)
969 		{
970 			foreach(it,val;src)
971 			{
972 				add(it.location);
973 			}
974 		}
975 	}
976 	
977 	size_t length()
978 	{
979 		return locations.length;
980 	}
981 
982 	Selection!T filter(bool delegate(T) condition)
983 	{
984 		size_t[] newlocations;
985 		foreach(l;locations)
986 		{
987 			if(condition(source[l])) newlocations ~= l;
988 		}
989 		locations = newlocations;
990 		return this;
991 	}
992 	
993 	Selection!T sortAsc(TKey)(TKey delegate(T) select)
994 	{
995 		if(sorted)
996 		{
997 			sort!((i1,i2) => select(source[i1])<select(source[i2]), SwapStrategy.stable)(locations);
998 		}
999 		else
1000 		{
1001 			sort!((i1,i2) => select(source[i1])<select(source[i2]), SwapStrategy.unstable)(locations);
1002 		}
1003 		sorted = true;
1004 		return this;
1005 	}
1006 	
1007 	Selection!T sortDesc(TKey)(TKey delegate(T) select)
1008 	{
1009 		if(sorted)
1010 		{
1011 			sort!((i1,i2) => select(source[i1])>select(source[i2]), SwapStrategy.stable)(locations);
1012 		}
1013 		else
1014 		{
1015 			sort!((i1,i2) => select(source[i1])>select(source[i2]), SwapStrategy.unstable)(locations);
1016 		}
1017 		sorted = true;
1018 		return this;
1019 	}
1020 	
1021 	ref T opIndex(size_t pos)
1022 	{
1023 		return source[locations[pos]];
1024 	}
1025 	
1026 	static if(__traits(hasMember, T, "tupleof"))
1027 	{
1028 		static if(is(T==class) || is(T==interface) || isTuple!T)
1029 		{
1030 			int opApply(int delegate(T) dg)
1031 			{
1032 				int result = 0;
1033 				foreach(location;locations)
1034 				{
1035 					result = dg(source[location]);
1036 					if (result) break;
1037 				}
1038 				return result;
1039 			}
1040 			
1041 			int opApply(int delegate(ptrdiff_t,T) dg)
1042 			{
1043 				int result = 0;
1044 				foreach(index,location;locations)
1045 				{
1046 					result = dg(index,source[location]);
1047 					if (result) break;
1048 				}
1049 				return result;
1050 			}
1051 			
1052 			int opApply(int delegate(ptrdiff_t,LinkedListIterator!T, T) dg)
1053 			{
1054 				if(sourceList is null)
1055 				{
1056 					throw new Exception("Foreach with LinkedListIterator is not allowed on arrays");
1057 				}
1058 				int result = 0;
1059 				foreach(index,location;locations)
1060 				{
1061 					result = dg(index,LinkedListIterator!T(sourceList.container,location),source[location]);
1062 					if (result) break;
1063 				}
1064 				return result;
1065 			}
1066 		}
1067 		else //struct
1068 		{
1069 			int opApply(int delegate(T*) dg)
1070 			{
1071 				int result = 0;
1072 				foreach(location;locations)
1073 				{
1074 					result = dg(&(source[location]));
1075 					if (result) break;
1076 				}
1077 				return result;
1078 			}
1079 			
1080 			int opApply(int delegate(ptrdiff_t,T*) dg)
1081 			{
1082 				int result = 0;
1083 				foreach(index,location;locations)
1084 				{
1085 					result = dg(index,&(source[location]));
1086 					if (result) break;
1087 				}
1088 				return result;
1089 			}
1090 			
1091 			int opApply(int delegate(ptrdiff_t,LinkedListIterator!T, T*) dg)
1092 			{
1093 				if(sourceList is null)
1094 				{
1095 					throw new Exception("Foreach with LinkedListIterator is not allowed on arrays");
1096 				}
1097 				int result = 0;
1098 				foreach(index,location;locations)
1099 				{
1100 					result = dg(index,LinkedListIterator!T(sourceList.container,location),&(source[location]));
1101 					if (result) break;
1102 				}
1103 				return result;
1104 			}
1105 		}
1106 	}else{ //single value
1107 		int opApply(int delegate(const T) dg)
1108 		{
1109 			int result = 0;
1110 			foreach(location;locations)
1111 			{
1112 				result = dg(source[location]);
1113 				if (result) break;
1114 			}
1115 			return result;
1116 		}
1117 		
1118 		int opApply(int delegate(ptrdiff_t,const T) dg)
1119 		{
1120 			int result = 0;
1121 			foreach(index,location;locations)
1122 			{
1123 				result = dg(index,source[location]);
1124 				if (result) break;
1125 			}
1126 			return result;
1127 		}
1128 		
1129 		int opApply(int delegate(ptrdiff_t,LinkedListIterator!T, const T) dg)
1130 		{
1131 			if(sourceList is null)
1132 			{
1133 				throw new Exception("Foreach with LinkedListIterator is not allowed on arrays");
1134 			}
1135 			int result = 0;
1136 			foreach(index,location;locations)
1137 			{
1138 				result = dg(index,LinkedListIterator!T(sourceList.container,location),source[location]);
1139 				if (result) break;
1140 			}
1141 			return result;
1142 		}
1143 	}
1144 	
1145 	protected void add(size_t location)
1146 	{
1147 		locations ~= location;
1148 	}
1149 }
1150 
1151 abstract class IListIndex
1152 {
1153 	protected LinkedListDescriptor[] hashTable;
1154 	protected CLinkedListContainer!ptrdiff_t locationContainer;
1155 	
1156 	protected abstract ulong hashOfLocation(ptrdiff_t location);
1157 	
1158 	void autoNotifyAdded(ptrdiff_t location)
1159 	{
1160 		//writeln("autoNotifyAdded " ~ location.to!string);
1161 		if(hashTable !is null)
1162 		{
1163 			locationContainer.addAnywhere(hashTable[hashOfLocation(location) % hashTable.length],location);
1164 			//writeln("Added item at " ~ location.to!string ~ " to index");
1165 		}
1166 	}
1167 	
1168 	void autoNotifyBeforeDelete(ptrdiff_t location)
1169 	{
1170 		if(hashTable !is null)
1171 		{
1172 			auto loclist = hashTable[hashOfLocation(location) % hashTable.length];
1173 			for(auto it=locationContainer.iterator(loclist); it.hasValue; it++)
1174 			{
1175 				if(it.value == location)
1176 				{
1177 					locationContainer.del(loclist,it);
1178 					//writeln("Deleted item "~location.to!string~" from index");
1179 					return;
1180 				}
1181 			}
1182 		}
1183 	}
1184 
1185 	void invalidate()
1186 	{
1187 		hashTable = null;
1188 		locationContainer = null;
1189 	}
1190 }
1191 
1192 size_t hash(BigInt x)
1193 {
1194 	return x.toLong;
1195 }
1196 
1197 size_t hash(T)(T x)
1198 	if(!is(T:BigInt))
1199 {
1200 	return x.toHash;
1201 }
1202 
1203 class IConstHashObject //this is needed, because Object.toHash is based on the address, which might change on garbage collections
1204 {
1205 	private ulong hash; //TODO: make @DontSerialize
1206 	private static ulong nextHash=0;
1207 
1208 	this()
1209 	{
1210 		hash = nextHash++;
1211 	}
1212 
1213 	override ulong toHash()
1214 	{
1215 		return hash;
1216 	}
1217 }
1218 
1219 class ListIndex(TItem,TKey) : IListIndex
1220 {
1221 	static immutable size_t defaultHashTableSize = 10240;
1222 	size_t maxIterations = 10;
1223 	static immutable float hashTableBasicSizeFactor = 1;
1224 	static immutable float hashTableComplexSizeFactor = 3;
1225 	bool constField;
1226 	size_t estimatedListLength;
1227 	float hashTableSizeFactor;
1228 	TItem defaultValue;
1229 	protected CList!TItem list;
1230 	protected TKey delegate(TItem) select;
1231 
1232 	void _do_not_serialize_(){}
1233 	
1234 	protected this(CList!TItem li, TKey delegate(TItem) sd, bool constField=false, size_t estimListLength=0)
1235 	{
1236 		//writeln("Creating list index...");
1237 		list = li;
1238 		select = sd;
1239 		this.constField = constField;
1240 		estimatedListLength = estimListLength;
1241 		static if(isBasicType!TKey)
1242 		{
1243 			hashTableSizeFactor = hashTableBasicSizeFactor;
1244 		}
1245 		else
1246 		{
1247 			hashTableSizeFactor = hashTableComplexSizeFactor;
1248 		}
1249 	}
1250 	
1251 	LinkedListPointer!TItem opCall(TKey key)
1252 	{
1253 		//writeln("Searching for ",key);
1254 		checkHashTable;
1255 		
1256 		if(hashTable !is null)
1257 		{
1258 			//über hashTable suchen
1259 			//writeln("Searching via Hash-Table");
1260 			size_t hashentry = hash(key) % hashTable.length;
1261 			for (auto it=locationContainer.iterator(hashTable[hashentry]); it.hasValue; it++)
1262 			{
1263 				auto itemkey = select(list.container.values[it.value]);
1264 				if (itemkey == key)
1265 				{
1266 					return LinkedListPointer!TItem(list.container,it.value);
1267 				}
1268 				if(!constField)
1269 				{
1270 					if((hash(itemkey)%hashTable.length) != hashentry) //Key wurde geändert
1271 					{
1272 						writeln("Key wurde geändert und Item wird neu in ListIndex einsortiert");
1273 						autoNotifyAdded(it.value);
1274 						locationContainer.del(hashTable[hashentry],it);
1275 					}
1276 				}
1277 			}
1278 		}
1279 		
1280 		if(hashTable is null || !constField)
1281 		{
1282 			//iterieren
1283 			//writeln("Searching via Iteration over ",list.length," items");
1284 			for (auto it=list.iterator; it.hasValue; it++)
1285 			{
1286 				if (select(it.value) == key)
1287 				{
1288 					return it;
1289 				}
1290 			}
1291 		}
1292 		
1293 		//nicht gefunden => return -1;
1294 		//writeln("Not found");
1295 		return LinkedListPointer!TItem(list.container,-1);
1296 	}
1297 	
1298 	ref TItem opIndex(TKey key)
1299 	{
1300 		auto it = opCall(key);
1301 		if(it.hasValue)
1302 		{
1303 			return it.value;
1304 		}else{
1305 			return defaultValue;
1306 		}
1307 	}
1308 
1309 	ref TItem opIndex(TKey key, TItem def)
1310 	{
1311 		auto it = opCall(key);
1312 		if(it.hasValue)
1313 		{
1314 			return it.value;
1315 		}else{
1316 			return list.add(def).value;
1317 		}
1318 	}
1319 
1320 	bool contains(TKey key)
1321 	{
1322 		return opCall(key).hasValue;
1323 	}
1324 	
1325 	Selection!TItem all(TKey key)
1326 	{
1327 		if(constField) checkHashTable;
1328 		auto result = new Selection!TItem(list);
1329 		
1330 		if(hashTable is null || !constField)
1331 		{
1332 			//iterieren
1333 			for (auto it=list.iterator; it.hasValue; it++)
1334 			{
1335 				if (select(it.value) == key)
1336 				{
1337 					result.add(it.location);
1338 				}
1339 			}
1340 		}
1341 		else
1342 		{
1343 			//über hashTable suchen
1344 			auto hashCode = hash(key);
1345 			for (auto it=locationContainer.iterator(hashTable[hashCode % hashTable.length]); it.hasValue; it++)
1346 			{
1347 				if (select(list.container.values[it.value]) == key)
1348 				{
1349 					result.add(it.value);
1350 				}
1351 			}
1352 		}
1353 		
1354 		return result;
1355 	}
1356 
1357 	void update(TItem item)
1358 	{
1359 		auto it = opCall(select(item));
1360 		if(it.hasValue)
1361 		{
1362 			it.value = item;
1363 		}else{
1364 			list.add(item);
1365 		}
1366 	}
1367 	
1368 	TKey key(LinkedListPointer!TItem item)
1369 	{
1370 		return select(item.value);
1371 	}
1372 	
1373 	void notifyKeyChanged(TKey oldKey, LinkedListPointer!TItem item)
1374 	{
1375 		if(hashTable !is null)
1376 		{
1377 			auto hashtablepos = hash(oldKey) % hashTable.length;
1378 			for(auto it=locationContainer.iterator(hashTable[hashtablepos]); it.hasValue; it++)
1379 			{
1380 				if(it.value == item.location)
1381 				{
1382 					locationContainer.del(hashTable[hashtablepos],it);
1383 					//writeln("Refreshing item "~item.location.to!string~" in index");
1384 					break;
1385 				}
1386 			}
1387 			autoNotifyAdded(item.location);
1388 		}
1389 	}
1390 	
1391 	protected override ulong hashOfLocation(ptrdiff_t location)
1392 	{
1393 		//writeln("hashOfLocation...");
1394 		return hash(select(list.container.values[location]));
1395 	}
1396 	
1397 	protected void checkHashTable()
1398 	{
1399 		if(hashTable is null && list.length>maxIterations)
1400 		{
1401 			if(estimatedListLength>0)
1402 			{
1403 				hashTable.length = (max(estimatedListLength,list.length) * hashTableSizeFactor).to!size_t;
1404 			}
1405 			else
1406 			{
1407 				hashTable.length = max((list.length * hashTableSizeFactor).to!size_t, defaultHashTableSize);
1408 			}
1409 			//writeln("Created hash table with " ~ hashTable.length.to!string ~ " entries.");
1410 			locationContainer = new CLinkedListContainer!ptrdiff_t;
1411 			
1412 			for (auto it=list.iterator; it.hasValue; it++)
1413 			{
1414 				autoNotifyAdded(it.location);
1415 			}
1416 		}
1417 	}
1418 }