namespace AlistairMilne_ElectronicArtsMontreal_submission2

{

    /*

    Partition and subdivide into the list the same way a qsort would, but

    switch axis each time we descend a level to build a kind of axis aligned

    BSP.  When any given node in the tree is below a certain maximum size,

    then stop recursing there and just brute-force those points.  Unlike

    qsort a third step is necessary after recursing into the left and right

    branches of a node; we also then have to then repartition the node's

    list and get the points which lie in the overlapping region across the

    partition and recurse into that list too.

    */

 

    __forceinline float compVal(const PointC& point, int treeDepth)

    {

        return ((float*)(&point))[(treeDepth) % 3];

    }

 

    __forceinline void SWAP(PointC& p1, PointC& p2)

    {

        PointC temp = p1;

        p1 = p2;

        p2 = temp;

    }

 

    __forceinline int Partition(PointC* pBuffer, int leftIdx, int rightIdx, int treeDepth)

    {

        int pivotIdx = (leftIdx + rightIdx) >> 1;

        SWAP(pBuffer[pivotIdx], pBuffer[leftIdx+1]);

        if (compVal(pBuffer[leftIdx+1], treeDepth) > compVal(pBuffer[rightIdx], treeDepth))

        {

            SWAP(pBuffer[leftIdx+1], pBuffer[rightIdx]);

        }

        if (compVal(pBuffer[leftIdx], treeDepth) > compVal(pBuffer[rightIdx], treeDepth))

        {

            SWAP(pBuffer[leftIdx], pBuffer[rightIdx]);

        }

        if (compVal(pBuffer[leftIdx+1], treeDepth) > compVal(pBuffer[leftIdx], treeDepth))

        {

            SWAP(pBuffer[leftIdx+1], pBuffer[leftIdx]);

        }

 

        int i = leftIdx+1;

        int j = rightIdx;

        float pivotValue = compVal(pBuffer[leftIdx], treeDepth);

        for (;;)

        {

            do ++i; while (compVal(pBuffer[i], treeDepth) < pivotValue);

            do --j; while (compVal(pBuffer[j], treeDepth) > pivotValue);

            if (j < i)

                break;

            SWAP(pBuffer[i], pBuffer[j]);

        }

        SWAP(pBuffer[leftIdx], pBuffer[j]);

        return j;

    }

 

    __forceinline int Repartition(PointC* pBuffer, int leftIdx, int rightIdx,

        int treeDepth, float leftExtent, float rightExtent)

    {

        int pivotIdx = leftIdx;

        for (int i=leftIdx; i<=rightIdx; ++i)

        {

            float iVal = compVal(pBuffer[i], treeDepth);

            if ((iVal > leftExtent) && (iVal < rightExtent))

            {

                SWAP(pBuffer[pivotIdx], pBuffer[i]);

                ++pivotIdx;

            }

        }

        return pivotIdx;

    }

 

    float GetMinDist(PointC* pBuffer, int leftIdx, int rightIdx, int treeDepth)

    {

        static float bestDist;

 

        // reset

        if (treeDepth == 0)

            bestDist = sqrtf(FLT_MAX);

 

        const int MAX_LEAF_SIZE = 12;

        if (rightIdx - leftIdx < MAX_LEAF_SIZE)

        {

            // brute force when we get down to a small enough number of points

            PointC* p1 = pBuffer + leftIdx;

            PointC* pEnd = pBuffer + rightIdx + 1;

            float bestSqDist = bestDist*bestDist;

            for (; p1 < pEnd; ++p1)

            {

                register float p1x = p1->x;

                register float p1y = p1->y;

                register float p1z = p1->z;

                for (PointC *p2 = p1+1; p2 < pEnd; ++p2)

                {

                    float sub = p1x - p2->x;

                    float currSqDist = sub*sub;

                    sub = p1y - p2->y;

                    currSqDist = currSqDist + sub*sub;

                    sub = p1z - p2->z;

                    currSqDist = currSqDist + sub*sub;

                    if (currSqDist < bestSqDist)

                        bestSqDist = currSqDist;

                }

            }

            if (bestSqDist < bestDist*bestDist)

                bestDist = sqrtf(bestSqDist);

            return bestDist;

        }

        else

        {

            // partition, recurse left, recurse right (just like qsort)

            int pivotIdx = Partition(pBuffer, leftIdx, rightIdx, treeDepth);

            float pivotValue = compVal(pBuffer[pivotIdx], treeDepth);          

 

            // each time we descend left, output the details from the stack

            GetMinDist(pBuffer, leftIdx, pivotIdx-1, treeDepth+1);

            GetMinDist(pBuffer, pivotIdx, rightIdx, treeDepth+1);

 

            // repartition, then recurse into the overlap between left & right

            pivotIdx = Repartition(pBuffer, leftIdx, rightIdx, treeDepth,

                pivotValue-bestDist, pivotValue+bestDist);

            if (pivotIdx-leftIdx > 1)

            {

                GetMinDist(pBuffer, leftIdx, pivotIdx-1, treeDepth+1);

            }

            return bestDist;

        }

    }

 

    float GetMinDist(CloudC* pCloud)

    {

        return GetMinDist( pCloud->GetPointBuffer(), 0, pCloud->GetNumPoints() - 1, 0);

    }

}