Преглед изворни кода

brl.objectlist. Initial Import.

woollybah пре 5 година
родитељ
комит
229be12ad0
2 измењених фајлова са 1055 додато и 0 уклоњено
  1. 579 0
      objectlist.mod/benchmark/benchmark.bmx
  2. 476 0
      objectlist.mod/objectlist.bmx

+ 579 - 0
objectlist.mod/benchmark/benchmark.bmx

@@ -0,0 +1,579 @@
+SuperStrict
+
+Framework brl.standardio
+Import brl.linkedlist
+Import brl.objectlist
+
+Global words:String[] = ["ring", "white", "infection", "calorie", "wage", "trust", "adventure", "ribbon", "assumption", "marble", ..
+	"favorable", "gun", "bark", "lick", "frank", "idea", "game", "white", "generation", "perfect", "leash", "sniff", "formal", ..
+	"hay", "pavement", "treaty", "user", "outer", "heir", "looting", "plaintiff", "welfare", "pier", "adventure", "ribbon", ..
+	"assumption", "precision", "image", "review", "idea", "game", "white", "generation", "perfect", "move", "establish", "lamp",
+	"fool", "ridge", "fortune", "psychology", "idea", "matter", "appearance", "fruit", "velvet", "white", "thread", "bless", "scream", "heaven"]
+
+Global sorted_words:String[] = ["adventure", "adventure", "appearance", "assumption", "assumption", "bark", "bless", "calorie", ..
+	"establish", "favorable", "fool", "formal", "fortune", "frank", "fruit", "game", "game", "generation", "generation", "gun", ..
+	"hay", "heaven", "heir", "idea", "idea", "idea", "image", "infection", "lamp", "leash", "lick", "looting", "marble", "matter", ..
+	"move", "outer", "pavement", "perfect", "perfect", "pier", "plaintiff", "precision", "psychology", "review", "ribbon", "ribbon", ..
+	"ridge", "ring", "scream", "sniff", "thread", "treaty", "trust", "user", "velvet", "wage", "welfare", "white", "white", "white", "white" ]
+	
+Global reversed_words:String[] = ["white", "white", "white", "white", "welfare", "wage", "velvet", "user", "trust", "treaty", ..
+	"thread", "sniff", "scream", "ring", "ridge", "ribbon", "ribbon", "review", "psychology", "precision", "plaintiff", "pier", ..
+	"perfect", "perfect", "pavement", "outer", "move", "matter", "marble", "looting", "lick", "leash", "lamp", "infection", "image", ..
+	"idea", "idea", "idea", "heir", "heaven", "hay", "gun", "generation", "generation", "game", "game", "fruit", "frank", "fortune", ..
+	"formal", "fool", "favorable", "establish", "calorie", "bless", "bark", "assumption", "assumption", "appearance", "adventure", "adventure"]
+
+Const COUNT:Int = 10000
+
+Print "Using " + COUNT + " iterations with " + words.length + " words list :"
+
+Local log1:TTestLogger = testTList()
+Local log2:TTestLogger = testTObjectList()
+
+MergeLogs([log1, log2])
+
+Function testTList:TTestLogger()
+	Local out:TTestLogger = New TTestLogger
+
+	out.Print " "
+	out.Print "TList"
+	Local list:TList = New TList
+	
+	out.Print "  AddLast/Clear"	
+	Local ts:Int = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+		If list.Count() <> words.length Throw "Wrong count"
+		list.Clear()
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	Local te:Int = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddFirst/Clear"	
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		For Local s:String = EachIn words
+			list.AddFirst(s)
+		Next
+		If list.Count() <> words.length Throw "Wrong count"
+		list.Clear()
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Contains"	
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		Local total:Int
+		For Local s:String = EachIn words
+			If list.Contains(s) Then
+				total :+ 1
+			End If
+		Next
+		If total <> words.length Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  ValueAtIndex"	
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		For Local n:Int = 0 Until words.length
+			Local s:String = String(list.ValueAtIndex(n))
+			If s <> words[n] Then Throw "Wrong value : " + s + "/" + words[n]
+		Next
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  First/Last"	
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT * 10
+		If list.First() <> words[0] Then Throw "Wrong value"
+		If list.Last() <> words[words.length - 1] Then Throw "Wrong value"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddLast/RemoveFirst"	
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		For Local s:String = EachIn words
+			Local v:String = String(list.RemoveFirst())
+			If v <> s Then Throw "Wrong value : " + v + "/" + s
+		Next
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddLast/RemoveLast"
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		Local n:Int = words.length - 1
+		While n >= 0
+			Local s:String = words[n]
+			Local v:String = String(list.RemoveLast())
+			If v <> s Then Throw "Wrong value : " + v + "/" + s
+			n :- 1
+		Wend
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddLast/Contains/Remove"	
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		Local toRemove:String[] = ["white", "idea", "game"]
+
+		For Local s:String = EachIn toRemove
+			While list.Contains(s)
+				If Not list.Remove(s) Then Throw "nothing to remove"
+			Wend
+		Next
+		
+		If list.Count() <> 52 Throw "Wrong count : " + list.Count() + "/" + 52
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddLast/Contains/Remove+/Compact"	
+	out.Print "    n/a"
+
+	out.Print "  enumerating"
+	ts = MilliSecs()
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	For Local i:Int = 0 Until COUNT
+		Local n:Int
+		For Local s:String = EachIn list
+			Local v:String = words[n]
+			If s <> v Then Throw "Wrong value : " + s + "/" + v
+			n :+ 1
+		Next
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Clear/AddLast/enumerate/Remove"
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+		
+		For Local s:String = EachIn list
+			list.Remove(s)
+		Next
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Reverse"
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	Local toggle:Int
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT * 5
+		list.Reverse()
+		If toggle Then
+			If list.First() <> words[0] Throw "Wrong Value"
+			If list.Last() <> words[words.length-1] Throw "Wrong Value"
+		Else
+			If list.Last() <> words[0] Throw "Wrong Value"
+			If list.First() <> words[words.length-1] Throw "Wrong Value"
+		End If
+		toggle = 1 - toggle
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Copy"
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		Local list2:TList = list.Copy()
+		If list2.Count() <> words.length Throw "wrong size"
+		If list2.First() <> words[0] Throw "Wrong Value"
+		If list2.Last() <> words[words.length-1] Throw "Wrong Value"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Clear/AddLast/Sort"
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		list.Sort()
+		
+	Next
+	te = MilliSecs()
+	For Local i:Int = 0 Until sorted_words.length
+		If String(list.ValueAtIndex(i)) <> sorted_words[i] Throw "Wrong Value"
+	Next
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Clear/AddLast/Sort (reversed)"
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		list.Sort(False)
+	Next
+	te = MilliSecs()
+	For Local i:Int = 0 Until reversed_words.length
+		If String(list.ValueAtIndex(i)) <> reversed_words[i] Throw "Wrong Value"
+	Next
+	out.Print "    took " + (te - ts) + "ms"
+
+
+	Return out
+End Function
+
+Function testTObjectList:TTestLogger()
+	Local out:TTestLogger = New TTestLogger
+
+	out.Print " "
+	out.Print "TObjectList"
+	Local list:TObjectList = New TObjectList
+	
+	out.Print "  AddLast/Clear"	
+	Local ts:Int = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+		If list.Count() <> words.length Throw "Wrong count"
+		list.Clear()
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	Local te:Int = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddFirst/Clear"	
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		For Local s:String = EachIn words
+			list.AddFirst(s)
+		Next
+		If list.Count() <> words.length Throw "Wrong count"
+		list.Clear()
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Contains"	
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		Local total:Int
+		For Local s:String = EachIn words
+			If list.Contains(s) Then
+				total :+ 1
+			End If
+		Next
+		If total <> words.length Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  ValueAtIndex"	
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		For Local n:Int = 0 Until words.length
+			Local s:String = String(list.ValueAtIndex(n))
+			If s <> words[n] Then Throw "Wrong value : " + s + "/" + words[n]
+		Next
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  First/Last"	
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT * 10
+		If list.First() <> words[0] Then Throw "Wrong value"
+		If list.Last() <> words[words.length - 1] Then Throw "Wrong value"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddLast/RemoveFirst"	
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		For Local s:String = EachIn words
+			Local v:String = String(list.RemoveFirst())
+			If v <> s Then Throw "Wrong value : " + v + "/" + s
+		Next
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddLast/RemoveLast"
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		Local n:Int = words.length - 1
+		While n >= 0
+			Local s:String = words[n]
+			Local v:String = String(list.RemoveLast())
+			If v <> s Then Throw "Wrong value : " + v + "/" + s
+			n :- 1
+		Wend
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddLast/Contains/Remove"	
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		Local toRemove:String[] = ["white", "idea", "game"]
+
+		For Local s:String = EachIn toRemove
+			While list.Contains(s)
+				If Not list.Remove(s) Then Throw "nothing to remove"
+			Wend
+		Next
+		
+		list.Compact()
+		
+		If list.Count() <> 52 Throw "Wrong count : " + list.Count() + "/" + 52
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  AddLast/Contains/Remove+/Compact"	
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		Local toRemove:String[] = ["white", "idea", "game"]
+
+		For Local s:String = EachIn toRemove
+			While list.Contains(s)
+				If Not list.Remove(s, True, False) Then Throw "nothing to remove"
+			Wend
+		Next
+		
+		If list.Count() <> 52 Throw "Wrong count : " + list.Count() + "/" + 52
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  enumerating"
+	ts = MilliSecs()
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	For Local i:Int = 0 Until COUNT
+		Local n:Int
+		For Local s:String = EachIn list
+			Local v:String = words[n]
+			If s <> v Then Throw "Wrong value : " + s + "/" + v
+			n :+ 1
+		Next
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Clear/AddLast/enumerate/Remove"
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+		
+		For Local s:String = EachIn list
+			list.Remove(s, False, False)
+		Next
+		If list.Count() <> 0 Throw "Wrong count"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Reverse"
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	Local toggle:Int
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT * 5
+		list.Reverse()
+		If toggle Then
+			If list.First() <> words[0] Throw "Wrong Value"
+			If list.Last() <> words[words.length-1] Throw "Wrong Value"
+		Else
+			If list.Last() <> words[0] Throw "Wrong Value"
+			If list.First() <> words[words.length-1] Throw "Wrong Value"
+		End If
+		toggle = 1 - toggle
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Copy"
+	list.Clear()
+	For Local s:String = EachIn words
+		list.AddLast(s)
+	Next
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		Local list2:TObjectList = list.Copy()
+		If list2.Count() <> words.length Throw "wrong size"
+		If list2.First() <> words[0] Throw "Wrong Value"
+		If list2.Last() <> words[words.length-1] Throw "Wrong Value"
+	Next
+	te = MilliSecs()
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Clear/AddLast/Sort"
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		list.Sort()
+	Next
+	te = MilliSecs()
+	For Local i:Int = 0 Until sorted_words.length
+		If String(list.ValueAtIndex(i)) <> sorted_words[i] Throw "Wrong Value"
+	Next
+	out.Print "    took " + (te - ts) + "ms"
+
+	out.Print "  Clear/AddLast/Sort (reversed)"
+	ts = MilliSecs()
+	For Local i:Int = 0 Until COUNT
+		list.Clear()
+		For Local s:String = EachIn words
+			list.AddLast(s)
+		Next
+
+		list.Sort(False)
+	Next
+	te = MilliSecs()
+	For Local i:Int = 0 Until reversed_words.length
+		If String(list.ValueAtIndex(i)) <> reversed_words[i] Throw "Wrong Value"
+	Next
+	out.Print "    took " + (te - ts) + "ms"
+
+	Return out
+End Function
+
+Type TTestLogger
+	Field LINES:TList = New TList
+	Method Print(s:String)
+		LINES.AddLast(s)
+	End Method
+End Type
+
+Function MergeLogs(logs:TTestLogger[])
+
+'	For Local n:Int = 0 Until logs.length
+'		Print "log " + n + " LINES = " + logs[n].LINES.count()
+'	Next
+
+	Local widest:Int
+	Local pad:String
+	Local Log:TTestLogger = logs[0]
+	Local count:Int
+	For Local s:String = EachIn Log.LINES
+		If s.length > widest Then
+			widest = s.length
+		End If
+		count :+ 1
+	Next
+	For Local i:Int = 0 Until widest
+		pad :+ " "
+	Next
+	
+	For Local i:Int = 0 Until count
+		Local s:String
+		For Local n:Int = 0 Until logs.length
+			Local txt:String = String(logs[n].LINES.ValueAtIndex(i))
+			s :+ (txt + pad)[..widest + 2] 
+		Next
+		Print s
+	Next
+End Function

+ 476 - 0
objectlist.mod/objectlist.bmx

@@ -0,0 +1,476 @@
+SuperStrict
+
+Rem
+bbdoc: Data structures/Array-backed Object Lists
+End Rem
+Module BRL.ObjectList
+
+ModuleInfo "Version: 1.00"
+ModuleInfo "Author: Bruce A Henderson"
+ModuleInfo "License: zlib/libpng"
+
+ModuleInfo "History: 1.00"
+ModuleInfo "History: Initial Release."
+
+Rem
+bbdoc: Array-backed Object List 
+End Rem
+Type TObjectList
+
+	Field version:Int
+
+	Field data:Object[16]
+	Field size:Int
+	
+	Field dirty:Int
+	
+	Method _ensureCapacity(newSize:Int)
+		If newSize >= data.length Then
+			data = data[.. newSize * 3 / 2 + 1]
+		End If
+	End Method
+
+	Rem
+	bbdoc: Clears the list.
+	about: Removes all objects from list.
+	End Rem
+	Method Clear()
+		For Local i:Int = 0 Until size
+			data[i] = Null
+		Next
+		size = 0
+		version :+ 1
+		dirty = False
+	End Method
+
+	Rem
+	bbdoc: Checks if the list is empty.
+	returns: #True if the list is empty, else #False
+	End Rem
+	Method IsEmpty:Int()
+		Return size = 0
+	End Method
+	
+	Rem
+	bbdoc: Adds an object to the start of the list
+	End Rem
+	Method AddFirst(value:Object)
+		Compact()
+		
+		If size Then
+			_ensureCapacity(size + 1)
+			ArrayCopy(data, 0, data, 1, size)
+		End If
+
+		data[0] = value
+		size :+ 1
+		version :+ 1
+	End Method
+	
+	Rem
+	bbdoc: Adds an object to the end of the list
+	End Rem
+	Method AddLast(value:Object)
+		Compact()
+		
+		_ensureCapacity(size + 1)
+		
+		data[size] = value
+		size :+ 1
+		version :+ 1
+	End Method
+	
+	Rem
+	bbdoc: Checks if the list contains a value
+	returns: #True if the list contains @obj, else #False
+	End Rem
+	Method Contains:Int(obj:Object)
+		For Local i:Int = 0 Until size
+			If data[i] = obj Then
+				Return True
+			End If
+		Next
+	End Method
+
+	Rem
+	bbdoc: Returns the first object in the list
+	about: Returns #Null if the list is empty.
+	End Rem
+	Method First:Object()
+		If size Then
+			Compact()
+
+			Return data[0]
+		End If
+	End Method
+	
+	Rem
+	bbdoc: Returns the last object in the list
+	about: Returns #Null if the list is empty.
+	End Rem
+	Method Last:Object()
+		If size Then
+			Compact()
+
+			Return data[size - 1]
+		End If
+	End Method
+	
+	Rem
+	bbdoc: Removes and returns the first object in the list.
+	about: Returns #Null if the list is empty.
+	End Rem
+	Method RemoveFirst:Object()
+		If size Then
+			Compact()
+
+			If size Then
+				Local value:Object = data[0]
+				ArrayCopy(data, 1, data, 0, size - 1)
+				size :- 1
+				data[size] = Null
+				version :+ 1
+				Return value
+			End If
+		End If
+	End Method
+	
+	Rem
+	bbdoc: Removes and returns the last object in the list.
+	about: Returns #Null if the list is empty.
+	End Rem
+	Method RemoveLast:Object()
+		If size Then
+			Compact()
+			
+			If size Then
+				Local value:Object = data[size - 1]
+				size :- 1
+				data[size] = Null
+				version :+ 1
+				Return value
+			End If
+		End If
+	End Method
+
+	Rem
+	bbdoc: Returns the object at the given index.
+	about: Throws an exception if the index is out of range (must be 0..list.Count()-1 inclusive).
+	End Rem
+	Method ValueAtIndex:Object(index:Int)
+		Compact()
+
+		Assert index>=0 Else "Object index must be positive"
+		If index >= size Then RuntimeError "List index out of range"
+		
+		Return data[index]
+	End Method
+	
+	Rem
+	bbdoc: Counts the list length
+	returns: The numbers of objects in the list.
+	End Rem
+	Method Count:Int()
+		Compact()
+
+		Return size
+	End Method
+	
+	Rem
+	bbdoc: Removes an object from the list.
+	about: Remove scans the list for the specified value and removes it.
+	By default, only the first found object is removed.	Enabling @removeAll will result in all instances of @value being removed from the list.
+	By default, the list is compacted on each remove. This can be inefficient if removing several objects from a list. Disabling @compactOnRemove
+	will skip compaction until either #Compact() is called, or the current enumerator completes, or a different list method is called.
+	This mechanism allows for removal of elements during an enumeration.
+	End Rem
+	Method Remove:Int(value:Object, removeAll:Int = False, compactOnRemove:Int = True)
+		If size Then
+			Local modified:Int
+			For Local i:Int = 0 Until size
+				If data[i] = value Then
+					data[i] = Null
+					modified = True
+					If Not removeAll Then
+						Exit
+					End If
+				End If
+			Next
+			
+			If modified Then
+				dirty = True
+				If compactOnRemove Then
+					Compact()
+				End If
+
+				version :+ 1
+			End If
+			
+			Return modified
+		End If
+	
+	End Method
+	
+	Rem
+	bbdoc: Compacts the list.
+	about: Use with #Remove() and @compactOnRemove = #False.
+	End Rem
+	Method Compact()
+		If dirty Then
+			Local offset:Int
+			For Local i:Int = 0 Until size
+				Local value:Object = data[i]
+				
+				If value Then
+					data[offset] = value
+					offset :+ 1
+				End If
+			Next
+			size = offset
+			dirty = False
+			version :+ 1
+		End If
+	End Method
+	
+	Method Swap(list:TObjectList)
+		
+	End Method
+
+	Rem
+	bbdoc: Creates an identical copy of the list.
+	End Rem
+	Method Copy:TObjectList()
+		Compact()
+		
+		Local list:TObjectList = New TObjectList()
+		
+		For Local i:Int = 0 Until size
+			list.AddLast(data[i])
+		Next
+		
+		Return list
+	End Method
+	
+	Rem
+	bbdoc: Reverses the order of the list.
+	End Rem
+	Method Reverse()
+		Compact()
+		
+		If size Then
+			Local leftOffset:Int
+			Local rightOffset:Int = size - 1
+			
+			While leftOffset < rightOffset
+				Local temp:Object = data[leftOffset]
+				data[leftOffset] = data[rightOffset]
+				data[rightOffset] = temp
+			
+				leftOffset :+ 1
+				rightOffset :- 1
+			Wend
+		
+		End If
+	End Method
+	
+	Rem
+	bbdoc: Creates a new list that is the reversed version of this list.
+	End Rem
+	Method Reversed:TObjectList()
+		Compact()
+		
+		Local list:TObjectList = New TObjectList()
+		
+		Local i:Int = size - 1
+		
+		While i >= 0
+			list.AddLast(data[i])
+			i :- 1
+		Wend
+		
+		Return list
+	End Method
+	
+	Method _removeAt(index:Int)
+		data[index] = Null
+		dirty = True
+		version :+ 1
+	End Method
+	
+	Method ObjectEnumerator:TObjectListEnumerator()
+		Local enumeration:TObjectListEnumerator=New TObjectListEnumerator
+		enumeration.list = Self
+		Return enumeration
+	End Method
+
+	Method ReverseEnumerator:TObjectListReverseEnumerator()
+		Local enumeration:TObjectListReverseEnumerator = New TObjectListReverseEnumerator
+		enumeration.list = Self
+		enumeration.index = size - 1
+		Return enumeration
+	End Method
+
+	Rem
+	bbdoc: Converts the list to an array
+	returns: An array of objects
+	End Rem
+	Method ToArray:Object[]()
+		Compact()
+		
+		Local arr:Object[] = New Object[size]
+		If size Then
+			ArrayCopy data, 0, arr, 0, size
+		End If
+		Return arr
+	End Method
+	
+	Rem
+	bbdoc: Creates a list from an array
+	returns: A new object list
+	End Rem
+	Function FromArray:TObjectList(arr:Object[])
+		Local list:TObjectList = New TObjectList
+		For Local i:Int = 0 Until arr.length
+			list.AddLast arr[i]
+		Next
+		Return list
+	End Function
+	
+	Rem
+	bbdoc: Sort the list in either ascending (default) or decending order.
+	about: User types should implement a Compare method in order to be sorted.
+	End Rem
+	Method Sort(ascending:Int=True, compareFunc:Int( o1:Object,o2:Object )=_CompareObjects)
+		If size < 2 Then
+			Return
+		End If
+
+		Local ccsgn:Int = -1
+		If ascending Then
+			ccsgn = 1
+		End If
+	
+		Compact()
+		
+		_sort(data, 0, size - 1, compareFunc, ccsgn)
+		
+	End Method
+	
+	Function _sort(data:Object[], low:Int, high:Int, compareFunc:Int( o1:Object,o2:Object ), ccsgn:Int)
+		If low < high Then
+			Local index:Int = _partition(data, low, high, compareFunc, ccsgn)
+			_sort(data, low, index - 1, compareFunc, ccsgn)
+			_sort(data, index + 1, high, compareFunc, ccsgn)
+		End If
+	End Function
+	
+	Function _partition:Int(data:Object[], low:Int, high:Int, compareFunc:Int( o1:Object,o2:Object ), ccsgn:Int)
+		Local pivot:Object = data[high]
+		Local index:Int = low - 1
+		
+		For Local n:Int = low Until high
+			If compareFunc(data[n], pivot) * ccsgn < 0 Then
+				index :+ 1
+				Local tmp:Object = data[index]
+				data[index] = data[n]
+				data[n] = tmp
+			End If
+		Next
+
+		Local tmp:Object = data[index + 1]
+		data[index + 1] = data[high]
+		data[high] = tmp
+		
+		Return index + 1
+	End Function
+	
+	Function _CompareObjects:Int( o1:Object,o2:Object )
+		Return o1.Compare( o2 )
+	End Function
+
+End Type
+
+Rem
+bbdoc: Enumerator Object used by #TObjectList in order to implement #Eachin support. 
+End Rem
+Type TObjectListEnumerator
+
+	Field list:TObjectList
+	Field index:Int = 0
+	Field lastVersion:Int
+
+	Method HasNext:Int()
+		Local result:Int = index < list.size
+		
+		' reached the end of the iteration
+		If Not result Then
+			list.Compact()
+		End If
+		
+		Return result
+	End Method
+
+	Method NextObject:Object()
+		Local value:Object = list.data[index]
+		index :+ 1
+		lastVersion = list.version
+		Return value
+	End Method
+
+	Method Remove()
+		list._removeAt(index - 1)
+		lastVersion = list.version
+	End Method
+
+	Method Delete()
+		If lastVersion = list.version Then
+			list.Compact()
+		End If
+	End Method
+	
+End Type
+
+Rem
+bbdoc: Enumerator Object used by #TObjectList in order to implement #Eachin support.
+about: This enumerator traverses the list in reverse (last to first).
+End Rem
+Type TObjectListReverseEnumerator
+
+	Field list:TObjectList
+	Field index:Int
+	Field lastVersion:Int
+	
+	Method HasNext:Int()
+		Local result:Int = index >= 0
+		
+		' reached the end of the iteration
+		If Not result Then
+			list.Compact()
+		End If
+		
+		Return result
+	End Method
+
+	Method NextObject:Object()
+		Local value:Object = list.data[index]
+		index :- 1
+		lastVersion = list.version
+		Return value
+	End Method
+
+	Method Remove()
+		list._removeAt(index + 1)
+		lastVersion = list.version
+	End Method
+
+	Method Delete()
+		If lastVersion = list.version Then
+			list.Compact()
+		End If
+	End Method
+	
+	Method ObjectEnumerator:TObjectListReverseEnumerator()
+		Return Self
+	End Method
+	
+End Type