gothon wrote:
This example is based on variable sized data referenced using the 'Any Ptr' type rather than macros, so it will not generate any significant bloat if it is reused on many different kinds of array.
I made a working version of my example by implementing HeapSort,
https://en.wikipedia.org/wiki/Heapsort
This example is in 4 files, but if you prefer not to build 'libBinaryHeap.a', you can glue them together into 1 file and delete the '#include / #inclib' lines.
BinaryHeap.bi
Code: Select all
#Inclib "BinaryHeap"
Type ArrayRange
FirstElement As Any Ptr
ElementSize As Integer
LastElementIdx As Integer
End Type
Type BinaryHeap
AR As ArrayRange
LessComp As Function(L As Any Ptr, R As Any Ptr) As Integer
Private:
Declare Sub SiftDown(I As Integer)
Declare Sub BuildHeap
Public:
Declare Constructor(R As ArrayRange, LC As Function(L As Any Ptr, R As Any Ptr) As Integer)
Declare Sub Insert
Declare Sub ExtractMax
End Type
#Define ElementArray(ELEMENT, Length) Type<ArrayRange>(@(ELEMENT), SizeOf(ELEMENT), (Length) - 1)
#Define SubArray(ARRAY, LB, UB) Type<ArrayRange>(@ARRAY(LB), SizeOf(ARRAY(LBound(ARRAY))), (UB) - (LB))
#Define WholeArray(ARRAY) SubArray(ARRAY, LBound(ARRAY), UBound(ARRAY))
Declare Sub HeapSort(Range As ArrayRange, LessComp As Function(L As Any Ptr, R As Any Ptr) As Integer)
NumberCompare.bi
Code: Select all
#Macro DefineCompareNumeric(VARTYPE)
Function VARTYPE##_AscendingCompare(L As Any Ptr, R As Any Ptr) As Integer
Return *CPtr(VARTYPE Ptr, L) < *CPtr(VARTYPE Ptr, R)
End Function
Function VARTYPE##_DescendingCompare(L As Any Ptr, R As Any Ptr) As Integer
Return *CPtr(VARTYPE Ptr, L) > *CPtr(VARTYPE Ptr, R)
End Function
#EndMacro
#Macro HeapSortNumeric(ARRAYRANGE, ARRAY, DIRECTION)
#If TypeOf(ARRAY) = Byte
HeapSort ARRAYRANGE, @Byte_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = UByte
HeapSort ARRAYRANGE, @UByte_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = Short
HeapSort ARRAYRANGE, @Short_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = UShort
HeapSort ARRAYRANGE, @UShort_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = Integer
HeapSort ARRAYRANGE, @Integer_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = UInteger
HeapSort ARRAYRANGE, @UInteger_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = Long
HeapSort ARRAYRANGE, @Long_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = ULong
HeapSort ARRAYRANGE, @ULong_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = LongInt
HeapSort ARRAYRANGE, @LongInt_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = ULongInt
HeapSort ARRAYRANGE, @ULongInt_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = Single
HeapSort ARRAYRANGE, @Single_##DIRECTION##Compare
#ElseIf TypeOf(ARRAY) = Double
HeapSort ARRAYRANGE, @Double_##DIRECTION##Compare
#Else
#Print Warning: ARRAY is not of a numeric type and will not be sorted!
#EndIf
#EndMacro
#Define HeapSortNumericElement(ELEMENT, LENGTH, DIRECTION) HeapSortNumeric(ElementArray(ELEMENT, LENGTH), ELEMENT, DIRECTION)
#Define HeapSortNumericSub(ARRAY, LB, UB, DIRECTION) HeapSortNumeric(SubArray(ARRAY, LB, UB), ARRAY, DIRECTION)
#Define HeapSortNumericWhole(ARRAY, DIRECTION) HeapSortNumeric(WholeArray(ARRAY), ARRAY, DIRECTION)
'Should probably Declare functions here and put defines in a seperate .bas file
DefineCompareNumeric(Byte)
DefineCompareNumeric(UByte)
DefineCompareNumeric(Short)
DefineCompareNumeric(UShort)
DefineCompareNumeric(Integer)
DefineCompareNumeric(UInteger)
DefineCompareNumeric(Long)
DefineCompareNumeric(ULong)
DefineCompareNumeric(LongInt)
DefineCompareNumeric(ULongInt)
DefineCompareNumeric(Single)
DefineCompareNumeric(Double)
Sorting arrays of intrinsic numeric types is good for making demos and tests. I can't think of any other applications however.
BinaryHeap.bas - compile to 'libBinaryHeap.a' with 'fbc -lib BinaryHeap.bas'
Code: Select all
#Include "BinaryHeap.bi"
#Define Element(I) (AR.FirstElement + (I) * AR.ElementSize)
#Define MemSwap(A, B) fb_MemSwap *CPtr(UByte Ptr, A), *CPtr(UByte Ptr, B), AR.ElementSize
#Define Parent(I) ((I) \ 2) 'One Based Heap Array
#Define LChild(I) ((I) * 2)
#Define RChild(I) ((I) * 2 + 1)
'Restore the heap property downward at node I
Sub BinaryHeap.SiftDown(I As Integer)
Do While LChild(I) <= AR.LastElementIdx
Var J = I
Var LeftChild = Element(LChild(I))
If LessComp(Element(J), LeftChild) Then J = LChild(I)
If RChild(I) <= AR.LastElementIdx AndAlso LessComp(Element(J), LeftChild + AR.ElementSize) Then J = RChild(I)
If J = I Then Exit Sub
MemSwap(Element(I), Element(J))
I = J
Loop
End Sub
'Make the unordered elements satisfy the heap property in O(n) time
Sub BinaryHeap.BuildHeap
For I As Integer = Parent(AR.LastElementIdx) To 1 Step -1
SiftDown I
Next I
End Sub
'Take an ArrayRange and a comparison and make it into a BinaryHeap
Constructor BinaryHeap(R As ArrayRange, LC As Function(L As Any Ptr, R As Any Ptr) As Integer)
AR.FirstElement = R.FirstElement - R.ElementSize 'Now the Heap Array is 1 Based
AR.ElementSize = R.ElementSize
AR.LastElementIdx = R.LastElementIdx + 1
LessComp = LC
BuildHeap
End Constructor
'Decrement the size of the heap by swaping out the max element in O(Log(n)) time
Sub BinaryHeap.ExtractMax
Assert (AR.LastElementIdx > 0)
MemSwap(Element(1), Element(AR.LastElementIdx))
AR.LastElementIdx -= 1
SiftDown 1
End Sub
'Increment the size of the heap by sifting in the next array element O(1) average time, O(Log(n)) worst case
Sub BinaryHeap.Insert
'ToDo:
'AR.LastElementIdx += 1
'SiftUp AR.LastElementIdx
End Sub
Sub HeapSort(R As ArrayRange, LessComp As Function(L As Any Ptr, R As Any Ptr) As Integer)
Dim Heap As BinaryHeap = BinaryHeap(R, LessComp)
Do While Heap.AR.LastElementIdx > 1
Heap.ExtractMax
Loop
End Sub
ToDo Note: BinaryHeap.Insert() is not implemented, but if it were, you could also use this as a Priority Queue! :-)
TestHeap.bas - main module compile with 'fbc TestHeap.bas'
Code: Select all
#Include "BinaryHeap.bi"
#Include "NumberCompare.bi"
Type MyUDT
Key As Double
Value As String
End Type
Function MyUDT_LessComp(L As Any Ptr, R As Any Ptr) As Integer
Return CPtr(MyUDT Ptr, L)->Key < CPtr(MyUDT Ptr, R)->Key
End Function
Dim MyArray(3) As MyUDT
MyArray(0).Key = 10
MyArray(0).Value = "For I As Integer = 0 To UBound(MyArray)"
MyArray(1).Key = 20
MyArray(1).Value = " Var J = I + Int(Rnd * (UBound(MyArray) + 1 - I))"
MyArray(2).Key = 30
MyArray(2).Value = " Swap MyArray(I), MyArray(J)"
MyArray(3).Key = 40
MyArray(3).Value = "Next I"
'Make a random permutation
For I As Integer = 0 To UBound(MyArray)
Var J = I + Int(Rnd * (UBound(MyArray) + 1 - I))
Swap MyArray(I), MyArray(J)
Next I
For I As Integer = 0 To UBound(MyArray)
Print MyArray(I).Key & " " & MyArray(I).Value
Next I
Print
HeapSort WholeArray(MyArray), @MyUDT_LessComp
For I As Integer = 0 To UBound(MyArray)
Print MyArray(I).Key & " " & MyArray(I).Value
Next I
Print
Dim Nums(20) As Byte
Print "Input Nums Array"
For I As Integer = 0 To UBound(Nums)
Nums(I) = Int(Rnd * 90) + 10
Print Nums(I);
Next I
Print
Print
HeapSortNumericWhole(Nums, Descending)
Print "Descending Nums Array"
For I As Integer = 0 To UBound(Nums)
Print Nums(I);
Next I
Print
Print
HeapSortNumericSub(Nums, 1, UBound(Nums) - 2, Ascending)
Print "Ascending Nums Sub Array"
For I As Integer = 0 To UBound(Nums)
Print Nums(I);
Next I
Print
Print
Dim pNum As Single Ptr = CAllocate(10, SizeOf(Single))
Print "Manually allocated Array"
For I As Integer = 0 To 9
pNum[I] = Int(Rnd * 50) / 10
Print pNum[I];
Next I
Print
Print
HeapSortNumericElement(pNum[0], 10, Ascending)
Print "Sorted manually allocated Array"
For I As Integer = 0 To 9
Print pNum[I];
Next I
DeAllocate(pNum) 'Remember to free memory
Sleep
'This prints a warning when compiled:
'HeapSortNumericWhole(MyArray, Ascending)