;Coded by UEZ build 2023-04-07
#include <Array.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
_GDIPlus_Startup()

Const $pow25To7 = 6103515625.0, $16d116 = 16 / 116, $1d3 = 1 / 3, $1d24 = 1 / 2.4
Const $PI = ACos(-1)
Const $deg360InRad = deg2Rad(360.0), $deg180InRad = deg2Rad(180.0)

Global $hGUI = GUICreate("GDI+ Color Sorting / Grouping Test", 850, 650)
GUISetState()
Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
Global $hPen = _GDIPlus_PenCreate(0)
_GDIPlus_GraphicsClear($hGraphic)


SRandom(80)
Global $aColortable[511], $i, $y = 0
For $i = 0 To UBound($aColortable) - 1
    $aColortable[$i] = Int(Random() * 0x1000000)
Next

;~ For $i = 0 To UBound($aColortable) - 1
;~     Do
;~         $iColor = Int(Random() * 0xFFFFFF)
;~         $a = RGB2HSV($iColor)
;~     Until $a[2] > 0.98 And $a[1] > 0.98
;~     $aColortable[$i] = $iColor
;~ Next

DrawColors($y, "Random")

_ArraySort($aColortable)
$y += 40
DrawColors($y, "Default Array Sort")

SortByCMYK($aColortable)
$y += 40
DrawColors($y, "CMYK")

SortByLum($aColortable)
$y += 40
DrawColors($y, "Luminosity")

SortByBrightColor($aColortable)
$y += 40
DrawColors($y, "BrightColor")

SortByDistance($aColortable, 0, 1)
$y += 40
DrawColors($y, "Distance 1")

SortByHSV($aColortable)
$y += 40
DrawColors($y, "HSV by V")

SortByHSL($aColortable, 3)
$y += 40
DrawColors($y, "HSL by L")

SortByHilbert($aColortable, 0)
$y += 40
DrawColors($y, "Hilbert")

SortByHSL($aColortable)
$y += 40
DrawColors($y, "HSL / HSV by H")

SortByHSL2($aColortable)
$y += 40
DrawColors($y, "HSV 2")

SortByLAB($aColortable)
$y += 40
DrawColors($y, "LAB")

SortByGroupingHSL($aColortable, 2, 2, 1)
$y += 40
DrawColors($y, "Grouping HSL by dist. 1")


SortByGroupingHSL($aColortable, 2, 3)
$y += 40
DrawColors($y, "Grouping HSL by Hilbert")

SortByHUEGroup_LABLightness_V2($aColortable)
$y += 40
DrawColors($y, "Grouping HUE LAB Lightness")

SortByGroupingHSV($aColortable, 2, 3)
$y += 40
DrawColors($y, "Grouping HSV by LAB")


While GUIGetMsg() <> $GUI_EVENT_CLOSE
    Sleep(10)
WEnd

_GDIPlus_PenDispose($hPen)
_GDIPlus_GraphicsDispose($hGraphic)
_GDIPlus_Shutdown()
GUIDelete()
Exit

Func DrawColors($y, $txt = 0, $array = $aColortable)
	If UBound($array, 2) Then
		For $i = 0 To UBound($array) - 1
			_GDIPlus_PenSetColor($hPen, BitOR(0xFF000000, $array[$i][0]))
			_GDIPlus_GraphicsDrawLine($hGraphic, $i, $y, $i, $y + 32, $hPen)
		Next
	Else
		For $i = 0 To UBound($array) - 1
			_GDIPlus_PenSetColor($hPen, BitOR(0xFF000000, $array[$i]))
			_GDIPlus_GraphicsDrawLine($hGraphic, $i, $y, $i, $y + 32, $hPen)
		Next
	EndIf
    If $txt Then _GDIPlus_GraphicsDrawString($hGraphic, $txt, $i + 8, $y + 4, "Arial", 16, Default, 0xFFFFFFFF)
EndFunc

Func SortByBrightColor(ByRef $List) ; by fee
	Local Const $High = UBound($List)
	Local $Sort[$High][$High], $Redd, $Gren, $Blue, $Mini, $Maxi, $Maxi, $Luma, $a, $b, $c
	For $a = 0 To $High - 1
		$Redd = BitAnd(BitShift($List[$a], 16), 0xFF)
		$Gren = BitAnd(BitShift($List[$a],  8), 0xFF)
		$Blue = BitAnd(         $List[$a],      0xFF)
		$Mini = Min3($Redd, $Gren, $Blue)
		$Maxi = Max3($Redd, $Gren, $Blue)
		$Dist = $Maxi - $Mini
		$Luma = Round(($Redd * 0.299) + ($Gren * 0.587) + ($Blue * 0.114))
		$Sort[$Luma][$Dist] = $List[$a]
	Next
	$c = 0
	For $a = 0 To $High - 1
		For $b = 0 To $High - 1
			If $Sort[$a][$b] = '' Then ContinueLoop
			$List[$c] = $Sort[$a][$b]
			$c += 1
		Next
	Next
EndFunc   ;==>SortByBrightColor

Func SortByHilbert(ByRef $aColortable, $dir = 0)
	; Compute the Hilbert index for each coordinate
	Local $i, $ub = UBound($aColortable), $aCoords[$ub][3], $aColortable2[$ub][3], $aHilbertIndices[$ub]
	If UBound($aColortable, 2) Then
		For $i = 0 To $ub - 1
			$aCoords[$i][0] = BitShift(BitAND($aColortable[$i][0], 0xFF0000), 16) ; red component
			$aCoords[$i][1] = BitShift(BitAND($aColortable[$i][0], 0xFF00), 8) ; green component
			$aCoords[$i][2] = BitAND($aColortable[$i][0], 0xFF) ; blue component
			$aColortable2[$i][0] = $aCoords[$i][0]
			$aColortable2[$i][1] = $aCoords[$i][1]
			$aColortable2[$i][2] = $aCoords[$i][2]
			$aHilbertIndices[$i] = _HilbertIndex($aCoords[$i][0], $aCoords[$i][1], $aCoords[$i][2])
		Next
	Else
		For $i = 0 To $ub - 1
			$aCoords[$i][0] = BitShift(BitAND($aColortable[$i], 0xFF0000), 16) ; red component
			$aCoords[$i][1] = BitShift(BitAND($aColortable[$i], 0xFF00), 8) ; green component
			$aCoords[$i][2] = BitAND($aColortable[$i], 0xFF) ; blue component
			$aColortable2[$i][0] = $aCoords[$i][0]
			$aColortable2[$i][1] = $aCoords[$i][1]
			$aColortable2[$i][2] = $aCoords[$i][2]
			$aHilbertIndices[$i] = _HilbertIndex($aCoords[$i][0], $aCoords[$i][1], $aCoords[$i][2])
		Next
	EndIf
	_GlobalArraySort($aHilbertIndices, $aColortable2)
	If UBound($aColortable, 2) Then
		If $dir = 0 Then
			For $i = 0 To $ub - 1
				$aColortable[$i][0] = BitOR(BitShift($aColortable2[$i][0], -16), BitShift($aColortable2[$i][1], -8), $aColortable2[$i][2])
			Next
		Else
			For $i = 0 To $ub - 1
				$aColortable[$ub - 1 - $i][0] = BitOR(BitShift($aColortable2[$i][0], -16), BitShift($aColortable2[$i][1], -8), $aColortable2[$i][2])
			Next
		EndIf
	Else
		If $dir = 0 Then
			For $i = 0 To $ub - 1
				$aColortable[$i] = BitOR(BitShift($aColortable2[$i][0], -16), BitShift($aColortable2[$i][1], -8), $aColortable2[$i][2])
			Next
		Else
			For $i = 0 To $ub - 1
				$aColortable[$ub - 1 - $i] = BitOR(BitShift($aColortable2[$i][0], -16), BitShift($aColortable2[$i][1], -8), $aColortable2[$i][2])
			Next
		EndIf
	EndIf
	Dim $aHilbertIndices[0]
	Dim $aColortable2[0]
	Dim $aCoords[0]
EndFunc

Func SortByCMYK(ByRef $aColortable) ;http://www.easyrgb.com/en/math.php
    Local $aColortable2[UBound($aColortable)][6]
    Local $a, $i
    For $i = 0 To UBound($aColortable) - 1
        $a = RGA2CMYK($aColortable[$i])
        $aColortable2[$i][0] = $aColortable[$i]
        $aColortable2[$i][1] = $a[0] ;C
        $aColortable2[$i][2] = $a[1] ;M
        $aColortable2[$i][3] = $a[2] ;Y
        $aColortable2[$i][4] = $a[3] ;K
        $aColortable2[$i][5] = Sqrt($a[0] ^ 2 + $a[1] ^ 2 + $a[2] ^ 2 + $a[3] ^ 2)
    Next
	Local $aSort[] = [5, 3]
	_ArraySort_MultiColumn($aColortable2, $aSort, 1, 0)
    For $i = 0 To UBound($aColortable2) - 1
        $aColortable[$i] = $aColortable2[$i][0]
    Next
    Dim $aColortable2[0]
EndFunc

Func SortByLum(ByRef $aColortable)
	Local $aColortable2[UBound($aColortable)][4], $a, $i, $r, $g, $b, $lum, $h2, $lum2, $v2, $repetitions = 1
    For $i = 0 To UBound($aColortable) - 1
		$r = BitShift(BitAND($aColortable[$i], 0xFF0000), 16)
		$g = BitShift(BitAND($aColortable[$i], 0xFF00), 8)
		$b = BitAND($aColortable[$i], 0xFF)
		$lum = Sqrt(0.241 * $r + 0.691 * $g + 0.068 * $b)
        $a = RGB2HSV($aColortable[$i])
		$h2 = Int($a[0] * $repetitions)
		$lum2 = Int($lum * $repetitions)
		$v2 = Int($a[2] * $repetitions)
;~ 		If Mod($h2, 2) = 1 Then
;~ 			$v2 = $repetitions - $v2
;~ 			$lum2 = $repetitions - $lum
;~ 		EndIf
        $aColortable2[$i][0] = $aColortable[$i]
        $aColortable2[$i][1] = $h2 ;H
        $aColortable2[$i][2] = $lum ;S
        $aColortable2[$i][3] = $v2 ;V
	Next
;~ 	Local $aSort[] = [2, 3]
;~ 	_ArraySort_MultiColumn($aColortable2, $aSort, 0, 0)
;~ 	DrawColors(96, "Test", $aColortable2)
	Local $aSort2[][] = [[2, 0], [3, 0]]
	_ArrayMultiColSort($aColortable2, $aSort2)
    For $i = 0 To UBound($aColortable2) - 1
        $aColortable[$i] = $aColortable2[$i][0]
    Next
    Dim $aColortable2[0]
EndFunc

Func SortByLAB(ByRef $aColortable, $iStartRGB = 0x000000) ;by eukalyptus
    Local $iDim = UBound($aColortable), $iDim2 = UBound($aColortable, 2)
    Local $aLAB[$iDim][2], $i

    If $iDim2 Then
        For $i = 0 To $iDim - 1
            $aLAB[$i][0] = _RGB2LAB($aColortable[$i][0])
        Next
    Else
        For $i = 0 To $iDim - 1
            $aLAB[$i][0] = _RGB2LAB($aColortable[$i])
        Next
    EndIf

    Local $aLastLAB = _RGB2LAB($iStartRGB)

    Local $aSort[$iDim], $fD, $fMin, $iIdx
    For $i = 0 To $iDim - 1
        $fMin = 2 ^ 32
        $iIdx = -1
        For $j = 0 To $iDim - 1
            If $aLAB[$j][1] Then ContinueLoop
            ;$fD = Sqrt((($aLAB[$j][0])[0] - $aLastLAB[0]) ^ 2 + (($aLAB[$j][0])[1] - $aLastLAB[1]) ^ 2 + (($aLAB[$j][0])[2] - $aLastLAB[2]) ^ 2) ;Euklid
            $fD = Sqrt((($aLAB[$j][0])[1] - $aLastLAB[1]) ^ 2 + (($aLAB[$j][0])[2] - $aLastLAB[2]) ^ 2) + Abs(($aLAB[$j][0])[0] - $aLastLAB[0]) ;Manhatten Sqrt($da^2 + $db^2) + Abs($L2 - $L1)
            If $fD < $fMin Then
                $fMin = $fD
                $iIdx = $j
            EndIf
        Next
        $aSort[$i] = $iIdx
        $aLastLAB = $aLAB[$iIdx][0]
        $aLAB[$iIdx][1] = True
    Next

    $aTmp = $aColortable
    For $i = 0 To $iDim - 1
        If $iDim2 Then
            $aColortable[$i][0] = $aTmp[$aSort[$i]][0]
        Else
            $aColortable[$i] = $aTmp[$aSort[$i]]
        EndIf
    Next
EndFunc

Func _RGB2LAB($iRGB) ;by eukalyptus
    Local $aRGB[3], $i
    $aRGB[0] = BitAND(BitShift($iRGB, 16), 0xFF) / 255
    $aRGB[1] = BitAND(BitShift($iRGB, 8), 0xFF) / 255
    $aRGB[2] = BitAND($iRGB, 0xFF) / 255

    For $i = 0 To 2
        If $aRGB[$i] > 0.04045 Then
            $aRGB[$i] = (($aRGB[$i] + 0.055) / 1.055) ^ 2.4
        Else
            $aRGB[$i] = $aRGB[$i] / 12.92
        EndIf
        $aRGB[$i] *= 100
    Next

    Local $aXYZ[3]
    $aXYZ[0] = ($aRGB[0] * 0.4124 + $aRGB[1] * 0.3576 + $aRGB[2] * 0.1805) / 95.047
    $aXYZ[1] = ($aRGB[0] * 0.2126 + $aRGB[1] * 0.7152 + $aRGB[2] * 0.0722) / 100.0
    $aXYZ[2] = ($aRGB[0] * 0.0193 + $aRGB[1] * 0.1192 + $aRGB[2] * 0.9505) / 108.883

    For $i = 0 To 2
        If $aXYZ[$i] > 0.008856 Then
            $aXYZ[$i] = $aXYZ[$i] ^ 0.3333333333333333
        Else
            $aXYZ[$i] = (7.787 * $aXYZ[$i]) + (16 / 116)
        EndIf
    Next

    Local $aLAB[3]
    $aLAB[0] = (116 * $aXYZ[1]) - 16
    $aLAB[1] = 500 * ($aXYZ[0] - $aXYZ[1])
    $aLAB[2] = 200 * ($aXYZ[1] - $aXYZ[2])

    Return $aLAB
EndFunc

Func SortByDistance(ByRef $aColortable, $iDir = 0, $iDistance = 1)
	Local $ub = UBound($aColortable), $aTmp[2], $swapped
	Local $i, $distances[$ub][$ub], $sums[$ub][2], $sortedColors[$ub]
	If UBound($aColortable, 2) Then
		If $iDistance = 1 Then
			If $iDir  = 0 Then
				Do ;bubblesort -> slow!
					$swapped = 0
					For $i = 0 To $ub - 2
						If Distance($aColortable[$i][0], $aColortable[$i + 1][0], 1) Then
							$aTmp[0] = $aColortable[$i][0]
							$aColortable[$i][0] = $aColortable[$i + 1][0]
							$aColortable[$i + 1][0] = $aTmp[0]
							$swapped = 1
						EndIf
					Next
				Until $swapped = 0
			Else
				Do ;bubblesort -> slow!
					$swapped = 0
					For $i = $ub - 1 To 1 Step -1
						If Distance($aColortable[$i][0], $aColortable[$i - 1][0], 1) Then
							$aTmp[0] = $aColortable[$i][0]
							$aColortable[$i][0] = $aColortable[$i - 1][0]
							$aColortable[$i - 1][0] = $aTmp[0]
							$swapped = 1
						EndIf
					Next
				Until $swapped = 0
			EndIf
		Else
			For $i = 0 To $ub - 2
				For $j = $i + 1 To $ub - 1
					$distances[$i][$j] = Distance($aColortable[$i][0], $aColortable[$j][0], $iDistance)
					$distances[$j][$i] = $distances[$i][$j]
				Next
			Next

			For $i = 0 To $ub - 1
				$sums[$i][0] = 0
				$sums[$i][1] = $aColortable[$i][0]
				For $j = 0 To $ub - 1
					If $i <> $j Then
						$sums[$i][0] += $distances[$i][$j]
					EndIf
				Next
			Next
			_ArraySort($sums, $iDir)
			For $i = 0 To $ub - 1
				$aColortable[$i][0] = $sums[$i][1]
			Next
		EndIf
	Else
		If $iDistance = 1 Then
			If $iDir  = 0 Then
				Do ;bubblesort -> slow!
					$swapped = 0
					For $i = 0 To $ub - 2
						If Distance($aColortable[$i], $aColortable[$i + 1], 1) Then
							$aTmp[0] = $aColortable[$i]
							$aColortable[$i] = $aColortable[$i + 1]
							$aColortable[$i + 1] = $aTmp[0]
							$swapped = 1
						EndIf
					Next
				Until $swapped = 0
			Else
	            Do ;bubblesort -> slow!
					$swapped = 0
					For $i = $ub - 1 To 1 Step -1
						If Distance($aColortable[$i], $aColortable[$i - 1], 1) Then
							$aTmp[0] = $aColortable[$i]
							$aColortable[$i] = $aColortable[$i - 1]
							$aColortable[$i - 1] = $aTmp[0]
							$swapped = 1
						EndIf
					Next
				Until $swapped = 0
			EndIf
		Else
			For $i = 0 To $ub - 2
				For $j = $i + 1 To $ub - 1
					$distances[$i][$j] = Distance($aColortable[$i], $aColortable[$j], $iDistance)
					$distances[$j][$i] = $distances[$i][$j]
				Next
			Next

			For $i = 0 To $ub - 1
				$sums[$i][0] = 0
				$sums[$i][1] = $aColortable[$i]
				For $j = 0 To $ub - 1
					If $i <> $j Then
						$sums[$i][0] += $distances[$i][$j]
					EndIf
				Next
			Next
			_ArraySort($sums, $iDir)
			For $i = 0 To $ub - 1
				$aColortable[$i] = $sums[$i][1]
			Next
		EndIf
	EndIf
EndFunc

Func SortByHSL(ByRef $aColortable, $sc = 1)
    Local $aColortable2[UBound($aColortable)][5]
    Local $a, $i
    For $i = 0 To UBound($aColortable) - 1
        $a = RGB2HSL($aColortable[$i])
        $aColortable2[$i][0] = $aColortable[$i]
        $aColortable2[$i][1] = $a[0] ;H
        $aColortable2[$i][2] = $a[1] ;S
        $aColortable2[$i][3] = $a[2] ;L
        $aColortable2[$i][4] = Sqrt($a[0] ^ 2 + $a[1] ^ 4 + $a[2] ^ 8) ;Sqrt(($a[0] / 360) ^ 2 + $a[1] ^ 2 + $a[2] ^ 2)
    Next
    $sc = Max(Min($sc, 4), 1)
    _ArraySort($aColortable2, 0, 0, 0, $sc)

;~ 	Local $aSort2[][] = [[1, 0], [3, 0], [2, 0]]
;~ 	_ArrayMultiColSort($aColortable2, $aSort2)

    For $i = 0 To UBound($aColortable2) - 1
        $aColortable[$i] = $aColortable2[$i][0]
    Next
    Dim $aColortable2[0]
EndFunc

Func SortByHSV(ByRef $aColortable, $sc = 3)
    Local $aColortable2[UBound($aColortable)][4]
    Local $a, $i
    For $i = 0 To UBound($aColortable) - 1
        $a = RGB2HSV($aColortable[$i])
        $aColortable2[$i][0] = $aColortable[$i]
        $aColortable2[$i][1] = $a[0]
        $aColortable2[$i][2] = $a[1]
        $aColortable2[$i][3] = $a[2]
    Next
    $sc = Max(Min($sc, 3), 1)
    _ArraySort($aColortable2, 0, 0, 0, $sc)
    For $i = 0 To UBound($aColortable2) - 1
        $aColortable[$i] = $aColortable2[$i][0]
    Next
    Dim $aColortable2[0]
EndFunc

Func SortByGroupingHSL(ByRef $aColortable, $sc = 2, $sortmode = 2, $iDistance = 1)
    Local $red[0][2], $orange[0][2], $yellow[0][2], $lime[0][2], $green[0][2], $turqoise[0][2], $cyan[0][2], $cobalt[0][2], $blue[0][2], $violet[0][2], $magenta[0][2], $crimson[0][2], $grey[0][2], $i, $j, $col
    $sc = Max(Min($sc, 2), 0)
    For $i = 0 To UBound($aColortable) - 1
        $col = $aColortable[$i]
        Switch RGBtoColorGroup($col)
            Case "red"
                _ArrayAdd($red, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "orange"
                _ArrayAdd($orange, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "yellow"
                _ArrayAdd($yellow, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "lime"
                _ArrayAdd($lime, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "green"
                _ArrayAdd($green, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "turqoise"
                _ArrayAdd($turqoise, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "cyan"
                _ArrayAdd($cyan, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "cobalt"
                _ArrayAdd($cobalt, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "blue"
                _ArrayAdd($blue, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "violet"
                _ArrayAdd($violet, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "magenta"
                _ArrayAdd($magenta, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "crimson"
                _ArrayAdd($crimson, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
            Case "grey"
                _ArrayAdd($grey, $aColortable[$i] & "|" & RGB2HSL($col)[$sc])
        EndSwitch
    Next
    $sortmode = Max(Min($sortmode, 3), 1)
    Switch $sortmode
        Case 1
            _ArraySort($red, 0, 0, 0, 1)
            _ArraySort($orange, 0, 0, 0, 1)
            _ArraySort($yellow, 0, 0, 0, 1)
            _ArraySort($lime, 0, 0, 0, 1)
            _ArraySort($green, 0, 0, 0, 1)
            _ArraySort($turqoise, 0, 0, 0, 1)
            _ArraySort($cyan, 0, 0, 0, 1)
            _ArraySort($cobalt, 0, 0, 0, 1)
            _ArraySort($blue, 0, 0, 0, 1)
            _ArraySort($violet, 0, 0, 0, 1)
            _ArraySort($magenta, 0, 0, 0, 1)
            _ArraySort($crimson, 0, 0, 0, 1)
            _ArraySort($grey, 0, 0, 0, 1)
        Case 2
            SortByDistance($red, 1, $iDistance)
            SortByDistance($orange, 0, $iDistance)
            SortByDistance($yellow, 1, $iDistance)
            SortByDistance($lime, 0, $iDistance)
            SortByDistance($green, 1, $iDistance)
            SortByDistance($turqoise, 0, $iDistance)
            SortByDistance($cyan, 1, $iDistance)
            SortByDistance($cobalt, 0, $iDistance)
            SortByDistance($blue, 1, $iDistance)
            SortByDistance($violet, 0, $iDistance)
            SortByDistance($magenta, 1, $iDistance)
            SortByDistance($crimson, 0, $iDistance)
            SortByDistance($grey, 1, $iDistance)
		Case 3
			SortByHilbert($red, 1)
			SortByHilbert($orange, 0)
			SortByHilbert($yellow, 1)
			SortByHilbert($lime, 0)
			SortByHilbert($green, 1)
			SortByHilbert($turqoise, 0)
			SortByHilbert($cyan, 1)
			SortByHilbert($cobalt, 0)
			SortByHilbert($blue, 1)
			SortByHilbert($violet, 0)
			SortByHilbert($magenta, 1)
			SortByHilbert($crimson, 0)
			SortByHilbert($grey, 1)
	EndSwitch
    $i = 0
    For $j = 0 To UBound($crimson) - 1
        $aColortable[$i] = $crimson[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($red) - 1
        $aColortable[$i] = $red[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($orange) - 1
        $aColortable[$i] = $orange[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($yellow) - 1
        $aColortable[$i] = $yellow[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($lime) - 1
        $aColortable[$i] = $lime[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($green) - 1
        $aColortable[$i] = $green[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($turqoise) - 1
        $aColortable[$i] = $turqoise[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($cyan) - 1
        $aColortable[$i] = $cyan[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($cobalt) - 1
        $aColortable[$i] = $cobalt[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($blue) - 1
        $aColortable[$i] = $blue[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($violet) - 1
        $aColortable[$i] = $violet[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($magenta) - 1
        $aColortable[$i] = $magenta[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($grey) - 1
        $aColortable[$i] = $grey[$j][0]
        $i += 1
    Next
EndFunc

Func SortByGroupingHSV(ByRef $aColortable, $sc = 2, $sortmode = 3)
    Local $red[0][2], $orange[0][2], $yellow[0][2], $lime[0][2], $green[0][2], $turqoise[0][2], $cyan[0][2], $cobalt[0][2], $blue[0][2], $violet[0][2], $magenta[0][2], $crimson[0][2], $grey[0][2], $i, $j, $col
    $sc = Max(Min($sc, 2), 0)
    For $i = 0 To UBound($aColortable) - 1
        $col = $aColortable[$i]
        Switch RGBtoColorGroup($col)
            Case "red"
                _ArrayAdd($red, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "orange"
                _ArrayAdd($orange, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "yellow"
                _ArrayAdd($yellow, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "lime"
                _ArrayAdd($lime, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "green"
                _ArrayAdd($green, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "turqoise"
                _ArrayAdd($turqoise, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "cyan"
                _ArrayAdd($cyan, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "cobalt"
                _ArrayAdd($cobalt, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "blue"
                _ArrayAdd($blue, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "violet"
                _ArrayAdd($violet, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "magenta"
                _ArrayAdd($magenta, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "crimson"
                _ArrayAdd($crimson, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
            Case "grey"
                _ArrayAdd($grey, $aColortable[$i] & "|" & RGB2HSV($col)[$sc])
        EndSwitch
    Next
    $sortmode = Max(Min($sortmode, 4), 1)
    Switch $sortmode
        Case 1
            _ArraySort($red, 0, 0, 0, 1)
            _ArraySort($orange, 1, 0, 0, 1)
            _ArraySort($yellow, 0, 0, 0, 1)
            _ArraySort($lime, 1, 0, 0, 1)
            _ArraySort($green, 0, 0, 0, 1)
            _ArraySort($turqoise, 1, 0, 0, 1)
            _ArraySort($cyan, 0, 0, 0, 1)
            _ArraySort($cobalt, 1, 0, 0, 1)
            _ArraySort($blue, 0, 0, 0, 1)
            _ArraySort($violet, 1, 0, 0, 1)
            _ArraySort($magenta, 0, 0, 0, 1)
            _ArraySort($crimson, 1, 0, 0, 1)
            _ArraySort($grey, 1, 0, 0, 1)
        Case 2
            SortByDistance($red, 1)
            SortByDistance($orange)
            SortByDistance($yellow, 1)
            SortByDistance($lime)
            SortByDistance($green, 1)
            SortByDistance($turqoise)
            SortByDistance($cyan, 1)
            SortByDistance($cobalt)
            SortByDistance($blue, 1)
            SortByDistance($violet)
            SortByDistance($magenta, 1)
            SortByDistance($crimson)
            SortByDistance($grey, 1)
        Case 3
            SortByLAB($red)
            SortByLAB($orange)
            SortByLAB($yellow)
            SortByLAB($lime)
            SortByLAB($green)
            SortByLAB($turqoise)
            SortByLAB($cyan)
            SortByLAB($cobalt)
            SortByLAB($blue)
            SortByLAB($violet)
            SortByLAB($magenta)
            SortByLAB($crimson)
            SortByLAB($grey)
		Case 4
			SortByHilbert($red)
            SortByHilbert($orange)
            SortByHilbert($yellow)
            SortByHilbert($lime)
            SortByHilbert($green)
            SortByHilbert($turqoise)
            SortByHilbert($cyan)
            SortByHilbert($cobalt)
            SortByHilbert($blue)
            SortByHilbert($violet)
            SortByHilbert($magenta)
            SortByHilbert($crimson)
            SortByHilbert($grey)
    EndSwitch
    $i = 0
    For $j = 0 To UBound($red) - 1
        $aColortable[$i] = $red[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($orange) - 1
        $aColortable[$i] = $orange[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($yellow) - 1
        $aColortable[$i] = $yellow[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($lime) - 1
        $aColortable[$i] = $lime[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($green) - 1
        $aColortable[$i] = $green[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($turqoise) - 1
        $aColortable[$i] = $turqoise[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($cyan) - 1
        $aColortable[$i] = $cyan[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($cobalt) - 1
        $aColortable[$i] = $cobalt[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($blue) - 1
        $aColortable[$i] = $blue[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($violet) - 1
        $aColortable[$i] = $violet[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($magenta) - 1
        $aColortable[$i] = $magenta[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($crimson) - 1
        $aColortable[$i] = $crimson[$j][0]
        $i += 1
    Next
    For $j = 0 To UBound($grey) - 1
        $aColortable[$i] = $grey[$j][0]
        $i += 1
    Next
EndFunc

Func SortByHUEGroup_LABLightness_V2(ByRef $aColortable) ;by eukalyptus
    Local $iDim = UBound($aColortable)

    Local $aHSL, $iIdx, $aGroup[12], $aTmp[0][2]
    For $i = 0 To 11
        $aGroup[$i] = $aTmp
    Next

    Local $aLAB
    For $i = 0 To $iDim - 1
        $aLAB = _RGB2LAB($aColortable[$i])
        $aHSL = RGB2HSL($aColortable[$i])
        $iIdx = Floor(Mod($aHSL[0], 360) / 30)
        _ArrayAdd($aGroup[$iIdx], $aLAB[0] & "|" & $aColortable[$i])
    Next

    $iIdx = 0
    For $i = 0 To 11
        $aTmp = $aGroup[$i]
        For $j = 0 To UBound($aTmp)-1
            $aTmp[$j][0] = Number($aTmp[$j][0])
        Next
        _ArraySort($aTmp, Mod($i, 2))
        $aGroup[$i] = $aTmp

        For $j = 0 To UBound($aTmp)-1
            $aColortable[$iIdx] = $aTmp[$j][1]
            $iIdx += 1
        Next
    Next

EndFunc

Func SortByHSL2(ByRef $aColortable) ;by Mars
    Local $aColortable2[UBound($aColortable)][9]
    Local $a, $i, $hsl_
    For $i = 0 To UBound($aColortable) - 1
        $a = _RGB2LAB($aColortable[$i])
        $hsl_ = RGB2HSL($aColortable[$i])
        $aColortable2[$i][0] = $aColortable[$i]
        $aColortable2[$i][1] = $a[0] ; LAB -> L
        $aColortable2[$i][2] = Sqrt($a[1]^2 + $a[2]^2) ; chroma
        $aColortable2[$i][3] = Abs($a[0] - 50) ; L umformen um "hell und dunkel" rauszufiltern
        $aColortable2[$i][4] = $hsl_[0] ; HSL -> H (könnte man via Farbwinkel aus LAB auch machen, hab ich ausprobiert, sah nicht so schön aus)
    Next
    Local $10 = Int(UBound($aColortable)/10)
    _ArraySort($aColortable2, 0, 0, 0, 2) ; chroma -> ziemlich "unbunte" farben herausfiltern (10%)
    _ArraySort($aColortable2, 1, 1 * $10 + 1, 0 * $10, 3) ; abs(L-50) -> sehr helle und sehr dunkle Farben herausfiltern
    _ArraySort($aColortable2, 0, 1 * $10 + 1, 5 * $10, 2) ; chroma -> von den hellen und dunklen Farben die unbuntesten raussuchen (nochmal 10%)
    _ArraySort($aColortable2, 0, 0 * $10 + 0, 2 * $10, 1) ; Die packt man links an das erste 5tel als eine Art "Pseudograustufenliste" (darin sind 20% aller Farben)
    _ArraySort($aColortable2, 0, 2 * $10 + 1, 0 * $10, 1) ; L -> sortiere nach Helligkeit
    _ArraySort($aColortable2, 0, 2 * $10 + 1, 6 * $10, 4) ; H -> Sortiere nach Hue (1x für niedrige Helligkeit)
    _ArraySort($aColortable2, 1, 6 * $10 + 1, 0 * $10, 4) ; H -> Sortiere nach Hue (1x für hohe Helligkeit)
    For $i = 0 To UBound($aColortable2) - 1
        $aColortable[$i] = $aColortable2[$i][0]
    Next
    Dim $aColortable2[0]
EndFunc

Func RGBtoColorGroup($iColor)
    Local $HSL = RGB2HSL($iColor), $H, $r
    If $HSL[0] = 0 And $HSL[1] = 0 Then
        $H = -1
    Else
        $H = $HSL[0]
    EndIf
    Switch Round($H, 0)
        Case 0 To 14, 345 To 359
            Return "red"
        Case 15 To 44
            Return "orange"
        Case 45 To 74
            Return "yellow"
        Case 75 To 104
            Return "lime"
        Case 105 To 134
            Return "green"
        Case 135 To 164
            Return "turqoise"
        Case 165 To 194
            Return "cyan"
        Case 195 To 224
            Return "cobalt"
        Case 225 To 254
            Return "blue"
        Case 255 To 284
            Return "violet"
        Case 285 To 314
            Return "magenta"
        Case 315 To 344
            Return "crimson"
        Case -1
            Return "grey"
    EndSwitch
EndFunc

Func Distance($rgb1, $rgb2, $m = 1)
    Local $r1 = BitShift(BitAND($rgb1, 0xFF0000), 16), $g1 = BitShift(BitAND($rgb1, 0xFF00), 8), $b1 = BitAND($rgb1, 0xFF)
    Local $r2 = BitShift(BitAND($rgb2, 0xFF0000), 16), $g2 = BitShift(BitAND($rgb2, 0xFF00), 8), $b2 = BitAND($rgb2, 0xFF)
    Local $dr = $r2 - $r1, $dg = $g2 - $g1, $db =  $b2 - $b1
	$m = Max(Min($m, 8), 1)
	Switch $m
		Case 1
			Return ($dr + $dg + $db) < 0
		Case 2
			Return Sqrt($dr * $dr + $dg * $dg + $db * $db) ;Euclidean distance
		Case 3
			Return Abs($r1 - $r2) + Abs($g1 - $g2) + Abs($b1 - $b2) ;Taxicab (Manhattan) distance
		Case 4
			Local $r_ = 0.5 * ($r1 + $r2)
			Return Sqrt((2 + ($r_ / 256)) * $dr * $dr + 4 * $dg * $dg + (2 + (255 - $r_) / 256) * $db * $db) ;redmean approx.
		Case 5
			Local $lab1 = _RGB2LAB($rgb1), $lab2 = _RGB2LAB($rgb2)
			Return Sqrt(($lab2[0] - $lab1[0])^2 + ($lab2[1] - $lab1[1])^2  + ($lab2[2] - $lab1[2])^2) ;L*a*b scale (CIE76) -> slow!
		Case 6
			Local $lab1 = _RGB2LAB($rgb1), $lab2 = _RGB2LAB($rgb2)
			Return Sqrt(($lab2[1] - $lab1[1])^2 + ($lab2[2] - $lab1[2])^2) + Abs($lab2[0] - $lab1[0]) ;L*a*b -> hybrid approach where a taxicab distance is used between the lightness and the chroma plane
		Case 7
			Local $lab1 = _RGB2LAB($rgb1), $lab2 = _RGB2LAB($rgb2), $L1 = $lab1[0], $L2 = $lab2[0], $a1 = $lab1[1], $b1 = $lab1[2], $a2 = $lab2[1], $b2 = $lab2[2]
			Local $dL = $L2 - $L1, $C1 = Sqrt($a1 * $a1 + $b1 * $b1), $C2 = Sqrt($a2 * $a2 + $b2 * $b2)
			Local $dEab = Sqrt($dL * $dL + ($a2 - $a1)^2  + ($b2 - $b1)^2)
			Local $dCab = $C1 - $C2, $dHab = Sqrt($dEab * $dEab - $dL * $dL - $dCab * $dCab)
			Local $K1 = 0.045, $K2 = 0.015, $kL = 1, $kC = 1, $kH = 1, $sL = 1, $sC = 1 + $K1 * $C1, $sH = 1 + $K2  * $C1
			Return Sqrt(($dL /($kL * $sL))^2 + ($dCab / ($kC * $sC))^2 + ($dHab / ($kH * $sH))^2) ;CIE94
		Case 8
			Local $lab1 = _RGB2LAB($rgb1), $lab2 = _RGB2LAB($rgb2)
			Return CIEDE2000($lab1, $lab2)
	EndSwitch
EndFunc

Func RGB2HSL($iColor) ;http://www.easyrgb.com/en/math.php
    Local $var_r = BitAND(BitShift($iColor, 16), 255) / 255, $var_g = BitAND(BitShift($iColor, 8), 255) / 255, $var_b = BitAND($iColor, 255) / 255
    Local $H, $S, $L, $var_Min, $var_Max, $del_Max, $del_Max2, $del_R, $del_G, $del_B

    $var_Min = Min3($var_r, $var_g, $var_b)
    $var_Max = Max3($var_r, $var_g, $var_b)
    $del_Max = $var_Max - $var_Min
    $L = ($var_Max + $var_Min) / 2
    If $del_Max = 0 Then
        $H = 0
        $S = 0
    Else
        $S = $L < 0.5 ? $del_Max / ($var_Max + $var_Min) : $del_Max / (2 - $var_Max - $var_Min)
        $del_Max2 = $del_Max / 2
        $del_R = ((($var_Max - $var_R) / 6) + $del_Max2) / $del_Max
        $del_G = ((($var_Max - $var_G) / 6) + $del_Max2) / $del_Max
        $del_B = ((($var_Max - $var_B) / 6) + $del_Max2) / $del_Max
        If ($var_R = $var_Max) Then
            $H = $del_B - $del_G
        Elseif $var_G = $var_Max Then
            $H = 0.333333333333333 + $del_R - $del_B
        Elseif $var_B = $var_Max Then
            $H = 0.666666666666667 + $del_G - $del_R
        EndIf
        If $H < 0 Then $H += 1
        If $H > 1 Then $H -= 1
    EndIf
    Local $aHSL[3] = [$H * 360, $S, $L]
    Return $aHSL
EndFunc

Func RGB2HSV($iColor) ;http://www.easyrgb.com/en/math.php
    Local $var_r = BitAND(BitShift($iColor, 16), 255) / 255, $var_g = BitAND(BitShift($iColor, 8), 255) / 255, $var_b = BitAND($iColor, 255) / 255
    Local $H, $S, $V, $var_Min, $var_Max, $del_Max, $del_Max2, $del_R, $del_G, $del_B

    $var_Min = Min3($var_r, $var_g, $var_b)
    $var_Max = Max3($var_r, $var_g, $var_b)
    $del_Max = $var_Max - $var_Min
    $V = $var_Max
    If $del_Max = 0 Then
        $H = 0
        $S = 0
    Else
        $S = $del_Max / $var_Max
        $del_Max2 = $del_Max / 2
        $del_R = ((($var_Max - $var_R) / 6) + $del_Max2) / $del_Max
        $del_G = ((($var_Max - $var_G) / 6) + $del_Max2) / $del_Max
        $del_B = ((($var_Max - $var_B) / 6) + $del_Max2) / $del_Max
        If $var_R = $var_Max Then
            $H = $del_B - $del_G
        ElseIf $var_G = $var_Max Then
            $H = 0.333333333333333 + $del_R - $del_B
        ElseIf $var_B = $var_Max Then
            $H = 0.666666666666667 + $del_G - $del_R
        EndIf
        If $H < 0 Then $H += 1
        If $H > 1 Then $H -= 1
    EndIf
    Local $aHSV[3] = [$H * 360, $S, $V]
    Return $aHSV
EndFunc

Func RGA2CMYK($iColor)
	Local $r = BitAND(BitShift($iColor, 16), 255), $g = BitAND(BitShift($iColor, 8), 255), $b = BitAND($iColor, 255)
	Local $C = 1 - ($r / 255), $M = 1 - ($G / 255), $Y = 1 - ($B / 255), $K = 1
	If ($C < $K) Then $K = $C
	If ($M < $K) Then $K = $M
	If ($Y < $K) Then $K = $Y
	If ($K = 1) Then ;Black only
		$C = 0
		$M = 0
		$Y = 0
	Else
		$C = ($C - $K) / (1 - $K)
		$M = ($M - $K) / (1 - $K)
		$Y = ($Y - $K) / (1 - $K)
	EndIf
	Local $aCMYK[] = [$C, $M, $Y, $K]
	Return $aCMYK
EndFunc

Func LAB2RGB($lab)
	Local $y = ($lab[0] + 16) / 116, $x = $lab[1] / 500 + $y, $z = $y - $lab[2] / 200, $r, $g, $b
	$x = 0.95047 * (($x * $x * $x > 0.008856) ? $x * $x * $x : ($x - $16d116) / 7.787)
	$y = 1.00000 * (($y * $y * $y > 0.008856) ? $y * $y * $y : ($y - $16d116) / 7.787)
	$z = 1.08883 * (($z * $z * $z > 0.008856) ? $z * $z * $z : ($z - $16d116) / 7.787)

	$r = $x *  3.2406 + $y * -1.5372 + $z * -0.4986
	$g = $x * -0.9689 + $y *  1.8758 + $z *  0.0415
	$b = $x *  0.0557 + $y * -0.2040 + $z *  1.0570

	$r = ($r > 0.0031308) ? (1.055 * Pow($r, $1d24) - 0.055) : 12.92 * $r
	$g = ($g > 0.0031308) ? (1.055 * Pow($g, $1d24) - 0.055) : 12.92 * $g
	$b = ($b > 0.0031308) ? (1.055 * Pow($b, $1d24) - 0.055) : 12.92 * $b
	Return _ColorRGB(Max(0, Min(1, $r)) * 255, Max(0, Min(1, $g)) * 255, Max(0, Min(1, $b)) * 255)
EndFunc

Func _ColorRGB($R, $G, $B)
	Return BitOR(0xFF000000, BitShift($R, -16), BitShift($G, -8), $b)
EndFunc


; Function to compute the Hilbert index for a 3-dimensional coordinate
Func _HilbertIndex($r, $g, $b)
    Local $iBits = 8 ; number of bits per dimension (must be a power of 2)
    Local $iMask = BitShift(1, -$iBits) - 1 ; bitmask for extracting the lower $iBits bits
    Local $iIndex = 0 ; initialize the Hilbert index
	Local $iXBit, $iYBit, $iYBit, $iMortonCode
    For $i = $iBits - 1 To 0 Step -1
        $iXBit = BitShift($r, $i) And $iMask ; extract the $i-th bit of $r
        $iYBit = BitShift($g, $i) And $iMask ; extract the $i-th bit of $g
        $iZBit = BitShift($b, $i) And $iMask ; extract the $i-th bit of $b
        $iMortonCode = $iZBit * 4 + $iYBit * 2 + $iXBit ; compute the Morton code for this 3-bit pattern
        $iIndex = BitOR($iIndex, BitShift($iMortonCode, -$i * 3)) ; add the Morton code to the Hilbert index
    Next
    Return $iIndex
EndFunc

Func _HilbertIndex2($r, $g, $b, $num_bits = 8)
    ; Calculate the Hilbert Index for a given RGB color.
    Local $index = 0
    For $i = 0 To $num_bits - 1
        Local $mask = BitShift(1, -$i)
        Local $bit_r = BitShift(BitAND($r, $mask), $i)
        Local $bit_g = BitShift(BitAND($g, $mask), $i)
        Local $bit_b = BitShift(BitAND($b, $mask), $i)
        $index = BitOR($index, BitShift($bit_r, -(2 * $i + 2)) + BitShift($bit_g, -(2 * $i + 1)) + BitShift($bit_b, -(2 * $i)))
    Next
    Return $index
EndFunc


; Function to sort a 2D array based on a 1D index array
Func _GlobalArraySort(ByRef $aIndexArray, ByRef $aSortArray, $iStart = 0, $iEnd = -1)
    ;Local $aTempArray[UBound($aSortArray)][$iFlag] ; create a temporary array for the sorted data
    If $iEnd = -1 Then $iEnd = UBound($aSortArray) - 1
    If $iStart >= $iEnd Then Return
    Local $iLeft = $iStart, $iRight = $iEnd
    Local $iPivot = $aIndexArray[($iStart + $iEnd) / 2]
    While ($iLeft <= $iRight)
        While ($aIndexArray[$iLeft] < $iPivot And $iLeft < $iEnd)
            $iLeft += 1
        WEnd
        While ($aIndexArray[$iRight] > $iPivot And $iRight > $iStart)
            $iRight -= 1
        WEnd
        If ($iLeft <= $iRight) Then
            Local $aTemp[UBound($aSortArray, 2)]
            For $j = 0 To UBound($aSortArray, 2) - 1
                $aTemp[$j] = $aSortArray[$iLeft][$j]
                $aSortArray[$iLeft][$j] = $aSortArray[$iRight][$j]
                $aSortArray[$iRight][$j] = $aTemp[$j]
            Next
            Local $iTemp = $aIndexArray[$iLeft]
            $aIndexArray[$iLeft] = $aIndexArray[$iRight]
            $aIndexArray[$iRight] = $iTemp
            $iLeft += 1
            $iRight -= 1
        EndIf
    WEnd
    If ($iStart < $iRight) Then _GlobalArraySort($aIndexArray, $aSortArray, $iStart, $iRight)
    If ($iLeft < $iEnd) Then _GlobalArraySort($aIndexArray, $aSortArray, $iLeft, $iEnd)
EndFunc

Func CIEDE2000($Lab1, $Lab2) ;http://en.wikipedia.org/wiki/Color_difference#CIEDE2000)
	Local $kL = 1.0, $kC = 1.0, $kH = 1.0, _
		  $lBarPrime = 0.5 * ($Lab1[0] + $Lab2[0]), $c1 = Sqrt($Lab1[1] * $Lab1[1] + $Lab1[2] * $Lab1[2]), $c2 = Sqrt($Lab2[1] * $Lab2[1] + $Lab2[2] * $Lab2[2]), $cBar = 0.5 * ($c1 + $c2), _
				  $cBar7 = $cBar * $cBar * $cBar * $cBar * $cBar * $cBar * $cBar, $g = 0.5 * (1.0 - Sqrt($cBar7 / ($cBar7 + 6103515625))), $a1Prime = $Lab1[1] * (1.0 + $g), $a2Prime = $Lab2[1] * (1.0 + $g), _
				  $c1Prime = Sqrt($a1Prime * $a1Prime + $Lab1[2] * $Lab1[2]), $c2Prime = Sqrt($a2Prime * $a2Prime + $Lab2[2] * $Lab2[2]), $cBarPrime = 0.5 * ($c1Prime + $c2Prime), $h1Prime = (Atan2($Lab1[2], $a1Prime) * 180.0) / $PI, _
				  $h2Prime = (Atan2($Lab2[2], $a2Prime) * 180.0) / $PI, $hBarPrime, $t, $dLPrime, $dCPrime, $dHPrime, $dH2Prime, $sL, $sC, $sH, $dTheta, $cBarPrime7, $rC, $rT

	If $h1Prime < 0.0 Then $h1Prime += 360.0
	If $h2Prime < 0.0 Then $h2Prime += 360.0
	$hBarPrime = Abs($h1Prime - $h2Prime) > 180.0 ? (0.5 * ($h1Prime + $h2Prime + 360.0)) : 0.5 * ($h1Prime + $h2Prime)
	$t = 1.0 - _
			0.17 * Cos($PI * (      $hBarPrime - 30.0) / 180.0) + _
			0.24 * Cos($PI * (2.0 * $hBarPrime       ) / 180.0) + _
			0.32 * Cos($PI * (3.0 * $hBarPrime +  6.0) / 180.0) - _
			0.20 * Cos($PI * (4.0 * $hBarPrime - 63.0) / 180.0)
	If (Abs($h2Prime - $h1Prime) <= 180.0) Then
		$dhPrime = $h2Prime - $h1Prime
	Else
		$dhPrime = $h2Prime <= $h1Prime ? $h2Prime - $h1Prime + 360.0 : $h2Prime - $h1Prime - 360.0
	Endif
	$dLPrime = $Lab2[0] - $Lab1[0]
	$dCPrime = $c2Prime - $c1Prime
	$dH2Prime = 2.0 * Sqrt($c1Prime * $c2Prime) * Sin($PI * (0.5 * $dhPrime) / 180.0)
	$sL = 1.0 + ((0.015 * ($lBarPrime - 50.0) * ($lBarPrime - 50.0)) / Sqrt(20.0 + ($lBarPrime - 50.0) * ($lBarPrime - 50.0)))
	$sC = 1.0 + 0.045 * $cBarPrime
	$sH = 1.0 + 0.015 * $cBarPrime * $t
	$dTheta = 30.0 * Exp(-(($hBarPrime - 275.0) / 25.0) * (($hBarPrime - 275.0) / 25.0))
	$cBarPrime7 = $cBarPrime * $cBarPrime * $cBarPrime * $cBarPrime * $cBarPrime * $cBarPrime * $cBarPrime
	$rC = Sqrt($cBarPrime7 / ($cBarPrime7 + 6103515625.0))
	$rT = -2.0 * $rC * Sin($PI * (2.0 * $dTheta) / 180.0)
	Return Sqrt(($dLPrime  / ($kL * $sL)) * ($dLPrime  / ($kL * $sL)) + _
				($dCPrime  / ($kC * $sC)) * ($dCPrime  / ($kC * $sC)) + _
				($dH2Prime / ($kH * $sH)) * ($dH2Prime / ($kH * $sH)) + _
				($dCPrime  / ($kC * $sC)) * ($dH2Prime / ($kH * $sH)) * $rT)
EndFunc

Func Min($a, $b)
    Return ($a < $b) ? $a : $b
EndFunc

Func Max($a, $b)
    Return ($a > $b) ? $a : $b
EndFunc

Func Min3($a, $b, $c)
    Return Min(Min($a, $b), $C)
EndFunc

Func Max3($a, $b, $c)
    Return Max(Max($a, $b), $C)
EndFunc

Func Pow($a, $b)
	Return $a ^ $b
EndFunc

Func deg2Rad(Const $deg)
	Return ($deg * ($PI / 180.0))
EndFunc

Func rad2Deg(Const $rad)
	Return ((180.0 / $PI) * $rad)
EndFunc

Func ATan2($y, $x)
	Local $result = Atan($y / $x)
	If $x < 0 Then
		$result += $PI
	ElseIf $y < 0 Then
		$result += 2 * $PI
	EndIf
	Return $result
EndFunc


; #FUNCTION# =============================================================================
; Name.............:	_ArraySort_MultiColumn
; Description ...:  	sorts an array at given colums (multi colum sort)
; Syntax...........:	_ArraySort_MultiColumn(ByRef $aSort, ByRef $aIndices)
; Parameters ...:		$aSort - array to sort
;						$aIndices - array with colum indices which should be sorted in specified order - zero based
;						$oDir/$iDir - sort direction - if set to 1, sort descending else ascending. $oDir is for the 1st row,
;													  $iDir for the remaining rows
;						$iStart - where to start to sort - 0 = 0 based array, 1 = 1 based array
; Author .........:		UEZ
; Version ........:		v0.80 build 2019-01-26 Beta
; =========================================================================================
Func _ArraySort_MultiColumn(ByRef $aSort, ByRef $aIndices, $oDir = 0, $iDir = 0, $iStart = 0)
	If Not IsArray($aIndices) Or Not IsArray($aSort) Then Return SetError(1, 0, 0) ;checks if $aIndices is an array
	If UBound($aSort, 2) = 0 Then Return SetError(4, 0, 0) ;array is 1D only
	If UBound($aIndices) > UBound($aSort, 2) Then Return SetError(2, 0, 0) ;check if $aIndices array is greater the $aSort array
	Local $1st, $2nd, $x, $j, $k, $l = 0
	For $x = 0 To UBound($aIndices) - 1 ;check if array content makes sense
		If Not IsInt($aIndices[$x]) Then Return SetError(3, 0, 0) ;array content is not numeric
	Next
	$iStart = Int($iStart) < 0 ? 0 : Int($iStart) > 1 ? 1 : Int($iStart)
	If UBound($aIndices) = 1 Then Return _ArraySort($aSort, $oDir, $iStart, 0, $aIndices[0]) ;check if only one index is given
	_ArraySort($aSort, $oDir, $iStart, 0, $aIndices[0])
	Do
		$1st = $aIndices[$l]
		$2nd = $aIndices[$l + 1]
		$j = 0
		$k = 1
		While $k < UBound($aSort)
			If $aSort[$j][$1st] <> $aSort[$k][$1st] Then
				If $k - $j > 1 Then
					_ArraySort($aSort, $iDir, $j, $k - 1, $2nd)
					$j = $k
				Else
					$j = $k
				EndIf
			EndIf
			$k += 1
		WEnd
		If $k - $j > 1 Then _ArraySort($aSort, $iDir, $j, $k, $2nd)
		$l += 1
	Until $l = UBound($aIndices) - 1
	Return 1
EndFunc   ;==>_ArraySort_MultiColumn

; #FUNCTION# ====================================================================================================================
; Name...........: _ArrayMultiColSort
; Description ...: Sort 2D arrays on several columns
; Syntax.........: _ArrayMultiColSort(ByRef $aArray, $aSortData[, $iStart = 0[, $iEnd = 0]])
; Parameters ....: $aArray    - The 2D array to be sorted
;                  $aSortData - 2D array holding details of the sort format
;                               Format: [Column to be sorted, Sort order]
;                                   Sort order can be either numeric (0/1 = ascending/descending) or a ordered string of items
;                                   Any elements not matched in string are left unsorted after all sorted elements
;                  $iStart    - Element of array at which sort starts (default = 0)
;                  $iEnd      - Element of array at which sort endd (default = 0 - converted to end of array)
; Requirement(s).: v3.3.8.1 or higher
; Return values .: Success: No error
;                  Failure: @error set as follows
;                            @error = 1 with @extended set as follows (all refer to $sIn_Date):
;                                1 = Array to be sorted not 2D
;                                2 = Sort data array not 2D
;                                3 = More data rows in $aSortData than columns in $aArray
;                                4 = Start beyond end of array
;                                5 = Start beyond End
;                            @error = 2 with @extended set as follows:
;                                1 = Invalid string parameter in $aSortData
;                                2 = Invalid sort direction parameter in $aSortData
;                                3 = Invalid column index in $aSortData
; Author ........: Melba23
; Remarks .......: Columns can be sorted in any order
; Example .......; Yes
; ===============================================================================================================================
Func _ArrayMultiColSort(ByRef $aArray, $aSortData, $iStart = 0, $iEnd = 0)

	; Errorchecking
	; 2D array to be sorted
	If UBound($aArray, 2) = 0 Then
		Return SetError(1, 1, "")
	EndIf
	; 2D sort data
	If UBound($aSortData, 2) <> 2 Then
		Return SetError(1, 2, "")
	EndIf
	If UBound($aSortData) > UBound($aArray) Then
		Return SetError(1, 3)
	EndIf
	For $i = 0 To UBound($aSortData) - 1
		If $aSortData[$i][0] < 0 Or $aSortData[$i][0] > UBound($aArray, 2) -1 Then
			Return SetError(2, 3, "")
		EndIf
	Next
	; Start element
	If $iStart < 0 Then
		$iStart = 0
	EndIf
	If $iStart >= UBound($aArray) - 1 Then
		Return SetError(1, 4, "")
	EndIf
	; End element
	If $iEnd <= 0 Or $iEnd >= UBound($aArray) - 1 Then
		$iEnd = UBound($aArray) - 1
	EndIf
	; Sanity check
	If $iEnd <= $iStart Then
		Return SetError(1, 5, "")
	EndIf

	Local $iCurrCol, $iChunk_Start, $iMatchCol

	; Sort first column
	__AMCS_SortChunk($aArray, $aSortData, 0, $aSortData[0][0], $iStart, $iEnd)
	If @error Then
		Return SetError(2, @extended, "")
	EndIf
	; Now sort within other columns
	For $iSortData_Row = 1 To UBound($aSortData) - 1
		; Determine column to sort
		$iCurrCol = $aSortData[$iSortData_Row][0]
		; Create arrays to hold data from previous columns
		Local $aBaseValue[$iSortData_Row]
		; Set base values
		For $i = 0 To $iSortData_Row - 1
			$aBaseValue[$i] = $aArray[$iStart][$aSortData[$i][0]]
		Next
		; Set start of this chunk
		$iChunk_Start = $iStart
		; Now work down through array
		For $iRow = $iStart + 1 To $iEnd
			; Match each column
			For $k = 0 To $iSortData_Row - 1
				$iMatchCol = $aSortData[$k][0]
				; See if value in each has changed
				If $aArray[$iRow][$iMatchCol] <> $aBaseValue[$k] Then
					; If so and row has advanced
					If $iChunk_Start < $iRow - 1 Then
						; Sort this chunk
						__AMCS_SortChunk($aArray, $aSortData, $iSortData_Row, $iCurrCol, $iChunk_Start, $iRow - 1)
						If @error Then
							Return SetError(2, @extended, "")
						EndIf
					EndIf
					; Set new base value
					$aBaseValue[$k] = $aArray[$iRow][$iMatchCol]
					; Set new chunk start
					$iChunk_Start = $iRow
				EndIf
			Next
		Next
		; Sort final section
		If $iChunk_Start < $iRow - 1 Then
			__AMCS_SortChunk($aArray, $aSortData, $iSortData_Row, $iCurrCol, $iChunk_Start, $iRow - 1)
			If @error Then
				Return SetError(2, @extended, "")
			EndIf
		EndIf
	Next

EndFunc   ;==>_ArrayMultiColSort

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __AMCS_SortChunk
; Description ...: Sorts array section
; Author ........: Melba23
; Remarks .......:
; ===============================================================================================================================
Func __AMCS_SortChunk(ByRef $aArray, $aSortData, $iRow, $iColumn, $iChunkStart, $iChunkEnd)

	Local $aSortOrder

	; Set default sort direction
	Local $iSortDirn = 1
	; Need to prefix elements?
	If IsString($aSortData[$iRow][1]) Then
		; Split elements
		$aSortOrder = StringSplit($aSortData[$iRow][1], ",")
		If @error Then
			Return SetError(1, 1, "")
		EndIf
		; Add prefix to each element
		For $i = $iChunkStart To $iChunkEnd
			For $j = 1 To $aSortOrder[0]
				If $aArray[$i][$iColumn] = $aSortOrder[$j] Then
					$aArray[$i][$iColumn] = StringFormat("%02i-", $j) & $aArray[$i][$iColumn]
					ExitLoop
				EndIf
			Next
			; Deal with anything that does not match
			If $j > $aSortOrder[0] Then
				$aArray[$i][$iColumn] = StringFormat("%02i-", $j) & $aArray[$i][$iColumn]
			EndIf
		Next
	Else
		Switch $aSortData[$iRow][1]
			Case 0, 1
				; Set required sort direction if no list
				If $aSortData[$iRow][1] Then
					$iSortDirn = -1
				Else
					$iSortDirn = 1
				EndIf
			Case Else
				Return SetError(1, 2, "")
		EndSwitch
	EndIf

	; Sort the chunk
	Local $iSubMax = UBound($aArray, 2) - 1
	__ArrayQuickSort2D($aArray, $iSortDirn, $iChunkStart, $iChunkEnd, $iColumn, $iSubMax)

	; Remove any prefixes
	If IsString($aSortData[$iRow][1]) Then
		For $i = $iChunkStart To $iChunkEnd
			$aArray[$i][$iColumn] = StringTrimLeft($aArray[$i][$iColumn], 3)
		Next
	EndIf

EndFunc   ;==>__AMCS_SortChunk