Saturday, 12 November 2016

BTS - Find the Longest Sequence

1. Problem Description
Find the longest sequence in a binary tree

2. Data Structure and Algorithm
Pseudo-code:
    - Generate the depth map for each node
        * maximal depth of the left child
        * maximal depth of the right child
    - Go through then map and spot the node, Nx, with maximal sum of left and right depths
    - Find the deepest path, Pl of the left child of Nx
    - Find the deepest path, Pr of the right child of Nx
    - Revert Pl and join Pl, Nx and Pr as the longest sequence

Find the deepest path check details: BTS - Find the Deepest Path

3. C++ Implementation
Find the TreeNode definition, utility functions here: BST - Find Next In-Order Value and BTS - Print the First and Last Nodes of Each Level.

// ********************************************************************************
// Implementation
// ********************************************************************************
struct TreeNodeDepth{
    TreeNodeDepth()
        : depthOfLeft(0)
        , depthOfRight(0)
    {}
    TreeNodeDepth(const size_t left, const size_t right)
        : depthOfLeft(left)
        , depthOfRight(right)
    {}

    size_t Total() const {
        return depthOfLeft + depthOfRight;
    }
    // depth of left and right children
    size_t depthOfLeft;
    size_t depthOfRight;
};

template <typename T>
using DepthMap = std::unordered_map<TreeNode<T>*, TreeNodeDepth>;

template <typename T>
size_t GenerateDepthMap(TreeNode<T> *root, DepthMap<T> &depth) {
    if (!root) {
        // special case - NULL pointer
        return 0;
    }

    // depth of this node and save into the map
    size_t leftDepth = GenerateDepthMap(root->left, depth);
    size_t rightDepth = GenerateDepthMap(root->right, depth);
    depth.insert(std::make_pair(root, TreeNodeDepth(leftDepth, rightDepth)));

    // return the maximal depth of left and right path
    return leftDepth >= rightDepth ? (leftDepth + 1) : (rightDepth + 1);
}

template <typename T>
std::list<TreeNode<T>*> FindTheLongestSequence(TreeNode<T> *root)
{
    if (!root) {
        // special case - NULL pointer
        return std::list<TreeNode<T>*>();
    }

    // Generate the depth map
    DepthMap<T> depth;
    GenerateDepthMap(root, depth);
    size_t totalLength = 0;
    TreeNode<T>* nodeWithTheLongestSeq = NULL;
    auto itEnd = depth.rend();
    // Loop through the depth map and find the longest sequence
    // Spot the node with largest sum of depth of left and right children
    for (auto it = depth.rbegin(); it != itEnd; ++it) {
        if (it->second.Total() > totalLength) {
            nodeWithTheLongestSeq = it->first;
            totalLength = it->second.Total();
        }
    }
   
    // Find the deepest path of the left child
    std::list<TreeNode<T>*> leftBranchPath = 
        FindTheDeepestPath(nodeWithTheLongestSeq->left);
    // Find the deepest path of the right child
    std::list<TreeNode<T>*> rightBranchPath =
        FindTheDeepestPath(nodeWithTheLongestSeq->right);
    // Join them to form the sequence
    leftBranchPath.reverse();
    leftBranchPath.push_back(nodeWithTheLongestSeq);
    leftBranchPath.splice(leftBranchPath.end(), rightBranchPath);
    return leftBranchPath;
}

// ********************************************************************************
// Test
// ********************************************************************************
void TestFindTheLongestSeq()
{
    std::vector<double> input = { 1.0, 2.0, 3.0, 4.0, 5.0 };
    TreeNode<double> *root = NULL;
    auto path = FindTheLongestSequence(root);
    assert(path.empty() == true);
    //              3
    //      1               4
    //          2               5
    root = ConstructTreeOnSortedValueBFS(input);
    path = FindTheLongestSequence(root);
    TestResult<double>(path, { 2.0, 1.0, 3.0, 4.0, 5.0 });
    
    input = { 6.0, 7.0, 8.0, 9.0 };
    TreeNode<double> *subTree1 = ConstructTreeOnSortedValueDFS(input);
    input = { 10.0, 11.0, 12.0, 13.0 , 14.0 };
    TreeNode<double> *subTree2 = ConstructTreeRecursive(input);
    input = { 15.0, 16.0, 17.0, 18.0 };
    TreeNode<double> *subTree3 = ConstructTreeOnSortedValueDFS(input);
    input = { 19.0, 20.0, 21.0, 22.0 , 23.0 };
    TreeNode<double> *subTree4 = ConstructTreeRecursive(input);

    TreeNode<double> *leftestNode = FindLeftestNode(root);
    assert(leftestNode != NULL);
    leftestNode->left = subTree1;
    //              3
    //         1        4
    //      8     2        5
    //    6   9
    //  7
    path = FindTheLongestSequence(root);
    TestResult<double>(path, { 7.0, 6.0, 8.0, 1.0, 3.0, 4.0, 5.0 });

    TreeNode<double> *rightestNode = FindRightestNode(root);
    assert(rightestNode != NULL);
    rightestNode->left = subTree2;
    //              3
    //         1        4
    //      8     2         5
    //    6   9          12
    //  7            10      13
    //                    11      14
    path = FindTheLongestSequence(root);
    TestResult<double>(path, { 7.0, 6.0, 8.0, 1.0, 3.0, 4.0, 5.0, 12.0, 10.0, 11.0 });
    
    TreeNode<double> *firstLeftRighestNode = FindRightestNode(root->left);
    assert(firstLeftRighestNode != NULL);
    firstLeftRighestNode->right = subTree3;
    //                    3
    //         1              4
    //      8     2                   5
    //    6   9     17          12
    //  7         15   18   10       13
    //                16          11      14
    path = FindTheLongestSequence(root);
    TestResult<double>(path, { 16.0, 15.0, 17.0, 2.0, 1.0, 3.0,
                               4.0, 5.0, 12.0, 10.0, 11.0 });
    
    TreeNode<double> *firstRightLeftestNodeOfS2 = FindRightestNode(subTree2->left);
    assert(firstRightLeftestNodeOfS2 != NULL);
    firstRightLeftestNodeOfS2->left = subTree4;
    //                    3
    //         1              4
    //      8     2                   5
    //    6   9     17          12
    //  7         15   18   10       13
    //               16           11      14
    //                         21
    //                    19     22
    //                        20       23
    path = FindTheLongestSequence(root);
    TestResult<double>(path, { 16.0, 15.0, 17.0, 2.0, 1.0, 3.0,
        4.0, 5.0, 12.0, 10.0, 11.0, 21.0, 19.0, 20.0 });
    
    TreeNode<double> *rightestNodeOfS2 = FindRightestNode(subTree2);
    assert(rightestNodeOfS2 != NULL);
    for (size_t i = 0; i < 8; ++i) {
        rightestNodeOfS2->right = new TreeNode<double>(100.0 + i);
        rightestNodeOfS2 = rightestNodeOfS2->right;
    }
    TreeNode<double> *rightestNodeOfS4 = FindRightestNode(subTree4);
    assert(rightestNodeOfS4 != NULL);
    for (size_t i = 0; i < 4; ++i) {
        rightestNodeOfS4->left = new TreeNode<double>(50.0 + i);
        rightestNodeOfS4 = rightestNodeOfS4->left;
    }
    //                    3
    //         1              4
    //      8     2                   5
    //    6   9     17          12
    //  7         15   18   10       13
    //                16          11      14
    //                         21              100
    //                    19     22             101
    //                      20       23            102
    //                             50                   103
    //                           51                       104
    //                         52                            105
    //                       53                                106
    //                                                              107
    path = FindTheLongestSequence(root);
    TestResult<double>(path, { 53.0, 52.0, 51.0, 50.0, 23.0, 22.0, 21.0,
        11.0, 10.0, 12.0, 13.0, 14.0, 100.0, 101.0, 102.0, 103.0, 104.0,
        105.0, 106.0, 107.0 });
    // clean up
    DeleteTree_TD(&root);
}


BTS - Find the Deepest Path

1. Problem Description
Find the deepest path from root to its leaf node.

2. Data Structure and Algorithm
Recursive implementation pseudo-code:
    - Get the deepest path of the path of the left child
    - Get the deepest path of the path of the right child
    - Return the deepest path of the left child if it is longer than the right
        * Return the deepest path of the right child if it is longer than the left
    - Special case: Return null path if this node is empty

Loop implementation pseudo-code:
    - Generate a stack map
        * In the order of each level
        * Each level is separated by a NULL pointer
        * Start from the root until the leaf nodes of the lowest level
    - Take the stack map from backwards
        * Take the first node of the lowest level (one of deepest path), Pn
        * Populate out node of each level (use separator of NULL pointer)
        * Find the parent of Pn -- Pn-1
        * Loop the stack map until the root

3. C++ Implementation
Find the TreeNode definition, utility functions here: BST - Find Next In-Order Value and BTS - Print the First and Last Nodes of Each Level.

// ********************************************************************************
// Implementation
// ********************************************************************************
// Recursive implementation
template <typename T>
std::list<TreeNode<T>*> FindTheDeepestPath_R(TreeNode<T> *root)
{
    if (!root) {
        return std::list<TreeNode<T>*>();
    }
    // left path
    std::list<TreeNode<T>*> leftPath = FindTheDeepestPath_R(root->left);
    leftPath.emplace_front(root);
    // right path
    std::list<TreeNode<T>*> rightPath = FindTheDeepestPath_R(root->right);
    rightPath.emplace_front(root);

    return leftPath.size() >= rightPath.size() ? leftPath : rightPath;
}

// Loop implementation
template <typename T>
std::list<TreeNode<T>*> FindTheDeepestPath(TreeNode<T> *root)
{
    if (!root) {
        return std::list<TreeNode<T>*>();
    }

    std::stack<TreeNode<T>*> nodesInLevel;
    std::queue<TreeNode<T>*> searchQueue;
    searchQueue.push(root);
    searchQueue.push(NULL);
    TreeNode<T> * curNode = NULL; // empty node as separater
    while (!searchQueue.empty()) {
        curNode = searchQueue.front();
        searchQueue.pop();
        if (curNode) {
            if (curNode->left) {
                searchQueue.push(curNode->left);
            }
            if (curNode->right) {
                searchQueue.push(curNode->right);
            }
        }
        else if (!searchQueue.empty()) {
            searchQueue.push(curNode);
        }

        nodesInLevel.push(curNode);
    }

    // nodeInLevel should have all nodes
    // each level is separated by empty nodes
    // start with root and end with empty nodes
    std::list<TreeNode<T>*> path;
    std::list<TreeNode<T>*> nodesInSameLevel;
    while (!nodesInLevel.empty()) {
        curNode = nodesInLevel.top();
        nodesInLevel.pop();
        if (!curNode) {
            // found a level
            if (!nodesInSameLevel.empty()) {
                if (path.empty()) {
                    // it might have multipile path
                    // take the first one avaialbel
                    path.push_back(*nodesInSameLevel.rbegin());
                }
                else {
                    auto itEnd = nodesInSameLevel.cend();
                    for (auto it = nodesInSameLevel.cbegin(); it != itEnd; ++it) {
                        if ((*it)->left == path.back() || (*it)->right == path.back()) {
                            // find its parent node
                            path.push_back(*it);
                            break;
                        }
                    }
                }
            }
            nodesInSameLevel.clear();
        }
        else {
            nodesInSameLevel.push_back(curNode);
        }
    }
    path.push_back(curNode);
    path.reverse();  
    return path;
}

// ********************************************************************************
// Test
// ********************************************************************************
void TestFindTheDeepestPath()
{
    std::vector<double> input = { 1.0, 2.0, 3.0, 4.0, 5.0};
    TreeNode<double> *root = NULL;
    auto path = FindTheDeepestPath(root);
    assert(path.empty() == true);
    path = FindTheDeepestPath_R(root);
    assert(path.empty() == true);
    //              3
    //      1               4
    //          2               5
    root = ConstructTreeOnSortedValueBFS(input);
    path = FindTheDeepestPath(root);
    TestResult<double>(path, {3.0, 1.0, 2.0});
    path = FindTheDeepestPath_R(root);
    TestResult<double>(path, { 3.0, 1.0, 2.0 });

    input = { 6.0, 7.0, 8.0, 9.0 };
    TreeNode<double> *subTree1 = ConstructTreeOnSortedValueDFS(input);
    input = { 10.0, 11.0, 12.0, 13.0 , 14.0};
    TreeNode<double> *subTree2 = ConstructTreeRecursive(input);
    input = { 15.0, 16.0, 17.0, 18.0 };
    TreeNode<double> *subTree3 = ConstructTreeOnSortedValueDFS(input);
    input = { 19.0, 20.0, 21.0, 22.0 , 23.0 };
    TreeNode<double> *subTree4 = ConstructTreeRecursive(input);

    TreeNode<double> *leftestNode = FindLeftestNode(root);
    assert(leftestNode != NULL);
    leftestNode->left = subTree1;
    //              3
    //         1        4
    //      8     2        5
    //    6   9
    //  7
    path = FindTheDeepestPath(root);
    TestResult<double>(path, { 3.0, 1.0, 8.0, 6.0, 7.0 });
    path = FindTheDeepestPath_R(root);
    TestResult<double>(path, { 3.0, 1.0, 8.0, 6.0, 7.0 });

    TreeNode<double> *rightestNode = FindRightestNode(root);
    assert(rightestNode != NULL);
    rightestNode->left = subTree2;
    //              3
    //         1        4
    //      8     2         5
    //    6   9          12
    //  7            10      13
    //                  11      14
    path = FindTheDeepestPath(root);
    TestResult<double>(path, { 3.0, 4.0, 5.0, 12.0, 10.0, 11.0 });
    path = FindTheDeepestPath_R(root);
    TestResult<double>(path, { 3.0, 4.0, 5.0, 12.0, 10.0, 11.0 });

    TreeNode<double> *firstLeftRighestNode = FindRightestNode(root->left);
    assert(firstLeftRighestNode != NULL);
    firstLeftRighestNode->right = subTree3;
    //                    3
    //         1              4
    //      8     2                   5
    //    6   9     17          12
    //  7         15   18   10       13
    //               16            11      14
    path = FindTheDeepestPath(root);
    TestResult<double>(path, { 3.0, 1.0, 2.0, 17.0, 15.0, 16.0 });
    path = FindTheDeepestPath_R(root);
    TestResult<double>(path, { 3.0, 1.0, 2.0, 17.0, 15.0, 16.0 });

    TreeNode<double> *firstRightLeftestNodeOfS2 = FindRightestNode(subTree2->left);
    assert(firstRightLeftestNodeOfS2 != NULL);
    firstRightLeftestNodeOfS2->left = subTree4;
    //                    3
    //         1              4
    //      8     2                   5
    //    6   9     17          12
    //  7         15   18   10       13
    //               16           11      14
    //                         21
    //                    19     22
    //                       20       23
    path = FindTheDeepestPath(root);
    TestResult<double>(path, { 3.0, 4.0, 5.0, 12.0, 10.0, 11.0, 21.0, 19.0, 20.0 });
    path = FindTheDeepestPath_R(root);
    TestResult<double>(path, { 3.0, 4.0, 5.0, 12.0, 10.0, 11.0, 21.0, 19.0, 20.0 });

    TreeNode<double> *rightestNodeOfS2 = FindRightestNode(subTree2);
    assert(rightestNodeOfS2 != NULL);
    for (size_t i = 0; i < 8; ++i) {
        rightestNodeOfS2->right = new TreeNode<double>(100.0 + i);
        rightestNodeOfS2 = rightestNodeOfS2->right;
    }
    //                    3
    //         1              4
    //      8     2                   5
    //    6   9     17          12
    //  7         15   18   10     13
    //              16           11      14
    //                        21              100
    //                    19     22            101
    //                      20       23          102
    //                                                  103
    //                                                     104
    //                                                        105
    //                                                          106
    //                                                            107
    // clean up
    path = FindTheDeepestPath(root);
    TestResult<double>(path, { 3.0, 4.0, 5.0, 12.0, 13.0, 14.0, 100.0, 101.0,
                               102.0, 103.0, 104.0, 105.0, 106.0, 107.0 });
    path = FindTheDeepestPath_R(root);
    TestResult<double>(path, { 3.0, 4.0, 5.0, 12.0, 13.0, 14.0, 100.0, 101.0,
                               102.0, 103.0, 104.0, 105.0, 106.0, 107.0 });

   // clean up
    DeleteTree_TD(&root);
}

Monday, 31 October 2016

Data Structure and Algorithm - Detect Missing Numbers in a Large Set

1. Problem Description
This is an Amazon interview question for senior software development engineer from careercup. Here is the original thread,
"
Given an array of 1 billion numbers with just a few 100 missing, find all the missing numbers. you have only enough memory to store only 10k elements
PS October 07, 2016 in United States Report Duplicate | Flag ".


2. Data Structure and Algorithm
Assuming that 1 billion numbers are given starting from 1. The 10K memory is in unit of C++ 32-bit integer.

The first approach:
The idea is very simple. Simply go through the number stream and detect numbers from 1, 2, ..., until 1 billion. 10K integer and use each bit to represent one number, therefore total range is 32K
    - Detect number 1, 2, ..., 32K
    - Detect number 32K+1, 32K+2, ..., 64K
    - Detect number 64K+1, 64K+2, ..., 96K
    - Keep until reach 1 billion

The computation complexity is O(N*(N/M), where
    -  N is the size of number stream and
     - M is the size of memory

The second approach:
Comparing with the first approach, instead of searching numbers one by one in the number stream this approach is to narrow down the range in missing number. Until the range is not greater than the given memory, then linear search the number stream to find out the missing number in the range.
    - Take the mid number and missing number in the first and second half.
         Set MID = (LowerBound+UpperBound)/2
         Start with (1+1000000000)/2 = 500000000
    - Find out count of number <= MID and count of number > MID
        [LowerBound, MID] and [MID+1, UpperBound]
    - Discard the range, if there is no missing number in this range
        If count of number in [LowerBound, MID] is equal to MID - LowerBound + 1
        If count of number in [MID+1, UpperBound] is equal to UpperBound - MID
    - Keep above 3 steps until the range is under the mem size
        In this case until the range is under 32K
    - Go through each range to find out the missing number.

The computation complexity is O(N(P + log(N/M)), where
    - N is the size of number stream
    - M is the size of memory
    - P is the size of missing number
Of course this approach takes extra memory to save the P ranges.

The third approach:
This is an improvement of the second approach. Instead of dividing the stream into 2 parts this approach is to directly divided the number stream into N/(M << 5) parts.
The second approach guarantees that the range is under mem size (32K), however the third approach guarantee that the range is equal to mem size. Therefore reduce the number of range and then reduce the times of going through the number stream linearly to find out the missing number.

The computation complexity is O(N*P), where,
    - N is the size of number stream
    - P is the size of missing number.

Section 3 shows the C++ implementation. And a Python implementation can be found here, Data Structure and Algorithm - Detect Missing Numbers in a Large Set.

3. C++ Implementation
An interface called "NumberStream" is implemented to act as the 1 billion numbers. A derived class called "NumberGenerator" is implemented to return the numbers with range of 1 billion with missing numbers.

A loop and recursive solution are implemented for Approach 2. A loop implementation is implemented for Approach 3. The test is based on a smaller scale, with 1 million number with 100 missing and 10000/100000/1000000 memory available.

// ********************************************************************************
// Implementation
// ********************************************************************************
// NumberStream.h  -- interface to get numbers
#pragma once

#include <unordered_set>

class NumberStream
{
public:
    using MissingNumberCollection = std::unordered_set<size_t>;

    NumberStream(const size_t upperBound, const size_t missing)
        : m_UpperBound(upperBound)
        , m_Missing(missing)
    {}
    virtual ~NumberStream() {}
    virtual bool HasNext() const = 0;
    virtual size_t GetNext() const = 0;
    virtual const MissingNumberCollection& GetMissingCollection() const = 0;
    virtual void ResetToHead() const = 0;

    size_t GetUpperBound() const {
        return m_UpperBound;
    }

    size_t GetMissing() const {
        return m_Missing;
    }

protected:
    const size_t m_UpperBound;
    const size_t m_Missing;
};

// NumberGenerator -- derived class
#pragma once

#include "NumberStream.h"

#include <unordered_set>

#include <ctime>

class NumberGenerator : public NumberStream
{
public:
    NumberGenerator(const size_t upperBound, const size_t missing);
    ~NumberGenerator();

    const MissingNumberCollection& GetMissingCollection() const {
        return m_MissingNumbers;
    }

    size_t GetNext() const;
    bool HasNext() const;
    void ResetToHead() const;
    
private:
    MissingNumberCollection m_MissingNumbers;
    mutable size_t m_MissingNumberDetected;
    mutable size_t m_CurVal;
};

// NumberGenerator.cpp - implementation
#include "stdafx.h"
#include "NumberGenerator.h"

#include <random>
#include <ctime>

NumberGenerator::NumberGenerator(const size_t upperBound, const size_t missing)
    : NumberStream(upperBound, missing)
    , m_MissingNumberDetected(0)
    , m_CurVal(0)
{
    // generate random numbers as the missing number
    std::mt19937_64 mtRand(time(NULL));
    std::uniform_int_distribution<size_t> gen(1, upperBound);
    for (size_t idx = 0; idx < m_Missing;) {
        const size_t temp = gen(mtRand);
        if (m_MissingNumbers.find(temp) != m_MissingNumbers.end()) {
            continue;
        }
        m_MissingNumbers.insert(temp);
        //m_MissingNumbers.insert(idx + 1);
        ++idx;
    }
}

NumberGenerator::~NumberGenerator()
{
}

size_t NumberGenerator::GetNext() const
{
    while (++m_CurVal < m_UpperBound) {
        if (m_MissingNumbers.find(m_CurVal) != m_MissingNumbers.end()) {
            ++m_MissingNumberDetected;
        }
        else {
            break;
        }
    }
    return m_CurVal;
}

bool NumberGenerator::HasNext() const {
    return m_CurVal < m_UpperBound
        && (m_UpperBound - m_CurVal) > (m_Missing - m_MissingNumberDetected);
}

void NumberGenerator::ResetToHead() const
{
    m_CurVal = 0;
    m_MissingNumberDetected = 0;
}

// DetectMissingNumbers_Amazon.cpp
#include "NumberStream.h"
#include "NumberGenerator.h"

#include <map>
#include <queue>
#include <tuple>
#include <unordered_map>
#include <vector>

// Given range is not greater than mem size
// Linearly search number stream to find the missing number
void FindMisingNumber(const NumberStream &ns,
    const std::tuple<size_t, size_t> &missNumbers,
    const size_t MemSizeInInt,
    NumberStream::MissingNumberCollection &results)
{
    size_t lowerBound, upperBound;
    std::tie(lowerBound, upperBound) = missNumbers;
    std::vector<bool> hashTable(upperBound + 1 - lowerBound);
    ns.ResetToHead();
    size_t tmp;
    while (ns.HasNext()) {
        tmp = ns.GetNext();
        if (tmp >= lowerBound && tmp <= upperBound) {
            hashTable[tmp - lowerBound] = true;
        }
    }

    tmp = 0;
    for (auto it = hashTable.cbegin(); it != hashTable.end(); ++it, ++tmp) {
        if (*it == false) {
            results.insert(tmp + lowerBound);
        }
    }
}

// Approach 2: recursive implementation
void DetectMissingNumber_R(const NumberStream &ns,
                        const size_t MemSizeInInt,
                        const size_t lowerBound,
                        const size_t upperBound,
                        NumberStream::MissingNumberCollection &results)
{
    const size_t TotalLinearDetectSize = MemSizeInInt << 5; // int has 32 bits
    if ((upperBound - lowerBound) <= TotalLinearDetectSize) {
        // find the missing number
        FindMisingNumber(ns, 
                        std::make_tuple(lowerBound, upperBound),
                        MemSizeInInt,
                        results);
        return;
    }

    // keep divide into two
    const size_t mid = (lowerBound + upperBound) >> 1;
    ns.ResetToHead();
    size_t countOfLowerHalf = 0; // [lowerBound, mid]
    size_t countOfUpperHalf = 0; // [mid + 1, upperBound]
    size_t temp;
    while (ns.HasNext()) {
        temp = ns.GetNext();
        if (temp >= lowerBound && temp <= upperBound) {
            temp > mid ? ++countOfUpperHalf : ++countOfLowerHalf;
        }
    }

    if (countOfLowerHalf < (mid + 1 - lowerBound)) {
        /* sub problem f(lowerBound,
                         mid,
                         mid + 1 - lowerBound - countOfLowerHalf)
        */
        DetectMissingNumber_R(
            ns,
            MemSizeInInt,
            lowerBound,
            mid,
            results);
    }
    if (countOfUpperHalf < (upperBound - mid)) {
        /* sub problem f(mid + 1,
                         upperBound,
                         upperBound - mid - countOfUpperHalf)
        */
        DetectMissingNumber_R(
            ns,
            MemSizeInInt,
            mid + 1,
            upperBound,
            results);
    }
}

// Approach 2: recursive implementation entry-point
NumberStream::MissingNumberCollection DetectMissingNumber_R(
    const NumberStream & ns,
    const size_t MemSizeInInt)
{
    const size_t missing = ns.GetMissing();
    const size_t upperBound = ns.GetUpperBound();
    NumberStream::MissingNumberCollection results;
    if (missing) {
        DetectMissingNumber_R(ns, MemSizeInInt, 1, upperBound, results);
    }
    return results;
}

// Approach 2: loop implementation
NumberStream::MissingNumberCollection DetectMissingNumber(
                                                          const NumberStream & ns,
                                                          const size_t MemSizeInInt)
{
    const size_t Missing = ns.GetMissing();
    const size_t UpperBound = ns.GetUpperBound();
    const size_t TotalLinearDetectSize = MemSizeInInt << 5; // int has 32 bits
    NumberStream::MissingNumberCollection results;
    if (!Missing) {
        return results;
    }

    using SearchQueue = std::queue<std::tuple<size_t, size_t>>;
    SearchQueue sq;
    // lowerBound and upperBound inclusive [lowerBound, upperBound]
    sq.push(std::make_tuple(1, UpperBound));
    size_t lowerBound, upperBound;
    size_t tryCount = 0;
    while (!sq.empty()) {
        ++tryCount;
        std::tie(lowerBound, upperBound) = sq.front();
        if ((upperBound - lowerBound) <= TotalLinearDetectSize) {
            // find the missing number for range under mem size
            FindMisingNumber(ns, sq.front(), MemSizeInInt, results);
        }
        else {
            // keep divide into two
            const size_t mid = (lowerBound + upperBound) >> 1;
            ns.ResetToHead();
            size_t countOfLowerHalf = 0; // [lowerBound, mid]
            size_t countOfUpperHalf = 0; // [mid + 1, upperBound]
            size_t temp;
            while (ns.HasNext()) {
                temp = ns.GetNext();
                if (temp >= lowerBound && temp <= upperBound) {
                    temp > mid ? ++countOfUpperHalf : ++countOfLowerHalf;
                }
            }
                
            if (countOfLowerHalf < (mid + 1 - lowerBound)) {
                // push [lowerBound, mid]
                sq.push(std::make_tuple(lowerBound,
                    mid));
            }
            if (countOfUpperHalf < (upperBound - mid)) {
                // push [mid+1, upperBound]
                sq.push(std::make_tuple(mid + 1,
                    upperBound));
            }
        }
        sq.pop();
    }

    return results;
}

// Approach 3: divide into rang (size equal to Mem << 5)
void DivideRange(const NumberStream &ns,
    const size_t MemSizeInInt,
    const size_t lowerBound,
    const size_t upperBound,
    std::vector<std::tuple<size_t, size_t>> &divides)
{
    const size_t TotalLinearDetectSize = MemSizeInInt << 5; // int has 32 bits    
    const size_t divisions = (upperBound - lowerBound + 1) / TotalLinearDetectSize;
    std::map<size_t, size_t> ladders;
    ladders.insert(std::make_pair(lowerBound, 0));
    for (size_t idx = 1; idx < divisions; ++idx) {
        ladders.insert(
            std::make_pair(lowerBound + TotalLinearDetectSize * idx, 0));
    }
    if (divisions * TotalLinearDetectSize + lowerBound < upperBound) {
        ladders.insert(
            std::make_pair(lowerBound + TotalLinearDetectSize * divisions, 0));
    }
    
    ns.ResetToHead();
    size_t temp;
    while (ns.HasNext()) {
        temp = ns.GetNext();
        if (temp < lowerBound || temp > upperBound) {
            continue;
        }
        auto er = ladders.equal_range(temp);
        er.first != er.second ?
            ++(er.first->second) : ++((--er.first)->second);
    }

    auto it = ladders.cbegin();
    auto itEnd = ladders.cend();
    auto itNext = ladders.cbegin();
    ++itNext;
    size_t totalMissing = 0;
    for (; itNext != itEnd; ++it, ++itNext) {
        if (it->second < (itNext->first - it->first)) {
            divides.push_back(
                std::make_tuple(it->first, itNext->first - 1));
        }
        totalMissing += itNext->first - it->first - it->second;
    }
    auto itR = ladders.rbegin();
    if (itR->second < (upperBound - itR->first + 1)) {
        divides.push_back(
            std::make_tuple(itR->first, upperBound));
    }
    totalMissing += upperBound - itR->first + 1 - itR->second;
}

// Approach 3 - entry point
NumberStream::MissingNumberCollection DetectMissingNumber_Div(const NumberStream & ns,
    const size_t MemSizeInInt)
{
    const size_t Missing = ns.GetMissing();
    const size_t UpperBound = ns.GetUpperBound();
    const size_t TotalLinearDetectSize = MemSizeInInt << 5; // int has 32 bits
    NumberStream::MissingNumberCollection results;
    if (!Missing) {
        return results;
    }

    using SearchQueue = std::queue<std::tuple<size_t, size_t>>;
    SearchQueue sq;
    // lowerBound and upperBound inclusive [lowerBound, upperBound]
    sq.push(std::make_tuple(1, UpperBound));
    size_t lowerBound, upperBound;
    size_t tryCount = 0;
    while (!sq.empty()) {
        ++tryCount;
        std::tie(lowerBound, upperBound) = sq.front();
        if ((upperBound - lowerBound) <= TotalLinearDetectSize) {
            // find the missing number
            FindMisingNumber(ns, sq.front(), MemSizeInInt, results);
        }
        else {
            // keep divide into MemSizeInInt
            std::vector<std::tuple<size_t, size_t>> divides;
            DivideRange(ns, MemSizeInInt, lowerBound, upperBound, divides);
            for (auto it = divides.cbegin(); it != divides.end(); ++it) {
                std::tie(lowerBound, upperBound) = *it;
                sq.push(std::make_tuple(lowerBound, upperBound));
            }
        }
        sq.pop();
    }

    return results;
}

// ********************************************************************************
// Test
// ********************************************************************************
#include <cassert>
void Test_DetectMissingNumbers()
{
    NumberGenerator ns(1000000, 100);
    NumberGenerator::MissingNumberCollection results;

    results = DetectMissingNumber(ns, 10000);
    assert(results == ns.GetMissingCollection());

    results = DetectMissingNumber(ns, 100000);
    assert(results == ns.GetMissingCollection());

    results = DetectMissingNumber(ns, 1000000);
    assert(results == ns.GetMissingCollection());
}

void Test_DetectMissingNumbers_R()
{
    NumberGenerator ns(1000000, 100);
    NumberGenerator::MissingNumberCollection results;

    results = DetectMissingNumber_R(ns, 10000);
    assert(results == ns.GetMissingCollection());

    results = DetectMissingNumber_R(ns, 100000);
    assert(results == ns.GetMissingCollection());

    results = DetectMissingNumber_R(ns, 1000000);
    assert(results == ns.GetMissingCollection());
}

void Test_DetectMissingNumbers_Div()
{
    NumberGenerator ns(1000000, 100);
    NumberGenerator::MissingNumberCollection results;

    results = DetectMissingNumber_Div(ns, 10000);
    assert(results == ns.GetMissingCollection());

    results = DetectMissingNumber_Div(ns, 100000);
    assert(results == ns.GetMissingCollection());

    results = DetectMissingNumber_Div(ns, 1000000);
    assert(results == ns.GetMissingCollection());
}

Thursday, 29 September 2016

Data Structure and Algorithm - Detect Word Permutation

1. Problem Description
This is a Microsoft interview question for software develop engineer from careercup. Here is the original thread,
"
A list of words is given and a bigger string given how can we find whether the string is a permutation of the smaller strings.
eg- s= badactorgoodacting dict[]={'actor','bad','act','good'] FALSE
eg- s= badactorgoodacting dict[]={'actor','bad','acting','good'] TRUE
The smaller words themselves don't need to be permuted. The question is whether we can find a ordering of the smaller strings such that if concatenated in that order it gives the larger string
One more constraint - some words from dict[] may also be left over unused
novicedhunnu September 15, 2016 in India Report Duplicate | Flag 
".

2. Data Structure and Algorithm
The solution is based on Trie data structure and algorithm. Details refer to my C++ post - Data Structure and Algorithm - Trie.

Pseudo-code:
    1. Save all word into Trie strucutre
    2. Initialize an empty queue/stack (breadth/depth-first search)
    3. Push starting index 0 into queue/stack (search from start of input string in Trie)
    4. Do until queue/stack is empty
        * Take the top value of queue/stack as idx and pop it out
        * Search input string starting from idx in Trie strucuture
            - Push ending index of any valid word starting from idx into queue/stack
            - Return TRUE, if 
                ~ reaching the end of input and 
                ~ having a valid word - [idx, endOfInputString)  in Trie
    5. Return FALSE otherwise

I have implemented this solution in both C++ and Python. The C++ implementation is provided in next section and the Python solution can be found on my Python post, Data Structure and Algorithm - Detect Word Permutation. Trie is a node-based data structure and algorithm. Therefore a recursive and non-recursive solution are implemented.

3. C++ Implementation
// ********************************************************************************
// Implementation
// ********************************************************************************
// PermutationDetector.h
#pragma once

#include <queue>
#include <string>
#include <vector>

struct DictionaryTrie;

class PermutationDetector
{
public:
    PermutationDetector();
    PermutationDetector(const std::vector<std::string> &words);
    ~PermutationDetector();

    void ConstructDict(const std::vector<std::string> &words, const bool appendMode);

    // recursive implementation
    bool IsPermutation_R(const std::string &word) const;
    // non-recursive implementation
    bool IsPermutation(const std::string &word) const;
private:
    bool IsPermutation_R(const std::string &word, std::queue<size_t> &nextSearch) const;
    DictionaryTrie *m_DictRoot;

};

// PermutationDetector.cpp
#include "PermutationDetector.h"
#include "DictionaryTrie.h"

#include <queue>

#include <cassert>

PermutationDetector::PermutationDetector()
    : m_DictRoot(nullptr)
{
}

PermutationDetector::PermutationDetector(const std::vector<std::string> &words)
    : m_DictRoot(nullptr)
{
    ConstructDict(words, false);
}

PermutationDetector::~PermutationDetector()
{
    if (m_DictRoot) {
        DestoryDictionaryTrie(m_DictRoot);
        m_DictRoot = nullptr;
    }
}

void PermutationDetector::ConstructDict(const std::vector<std::string> &words,
                                        const bool appendMode)
{
    if (!appendMode && m_DictRoot) {
        DestoryDictionaryTrie(m_DictRoot);
        m_DictRoot = nullptr;
    }
    if (!m_DictRoot) {
        m_DictRoot = new DictionaryTrie();
    }

    assert(m_DictRoot != nullptr);

    auto&& itEnd = words.rend();
    for (auto&& it = words.rbegin(); it != itEnd; ++it) {
        AddWord(m_DictRoot, it->c_str());
    }
}

bool PermutationDetector::IsPermutation_R(const std::string &word,
                                        std::queue<size_t> &nextSearch) const
{
    if (nextSearch.empty()) {
        return false;
    }

    const size_t startSearchIndex = nextSearch.front();
    const char *startSearchPtr = word.c_str() + startSearchIndex;
    nextSearch.pop();

    DictionaryTrie *tempPtr = m_DictRoot;
    size_t searchedLen = 1;
    while (*startSearchPtr != '\0' && tempPtr) {
        tempPtr = tempPtr->children[*startSearchPtr - 'a'];
        if (tempPtr && tempPtr->str) {
            // found a valid word/permutation and candidate for next search
            nextSearch.push(startSearchIndex + searchedLen);
        }
        ++searchedLen;
        ++startSearchPtr;
    }

    if (*startSearchPtr == '\0' && tempPtr && tempPtr->str) {
        return true;
    }

    // tail recursion
    return IsPermutation_R(word, nextSearch);
}

bool PermutationDetector::IsPermutation_R(const std::string &word) const
{
    if (word.empty()) {
        return false;
    }

    // switch queue/stack for breadth/depth-first search
    std::queue<size_t> nextSearch;
    nextSearch.push(0);
    return IsPermutation_R(word, nextSearch);
}

bool PermutationDetector::IsPermutation(const std::string &word) const
{
    if (word.empty()) {
        return false;
    }

    // switch queue/stack for breadth/depth-first search
    std::queue<size_t> nextSearch;
    nextSearch.push(0);
    while (!nextSearch.empty()) {
        size_t startSearchIndex = nextSearch.front();
        nextSearch.pop();
        const char *searchStartPtr = word.c_str() + startSearchIndex;
        DictionaryTrie *tempPtr = m_DictRoot;
        ++startSearchIndex;
        while (*searchStartPtr != '\0' && tempPtr) {
            tempPtr = tempPtr->children[*searchStartPtr - 'a'];
            if (tempPtr && tempPtr->str) {
                // found a valid word/permutation and candidate for next search
                nextSearch.push(startSearchIndex);
            }
            ++startSearchIndex;
            ++searchStartPtr;
        }
     
        if (*searchStartPtr == '\0' && tempPtr && tempPtr->str) {
            return true;
        }
    }

    return false;
}

// ********************************************************************************
// Test
// ********************************************************************************
void Test_PermutationDetector()
{
    PermutationDetector detector({"actor", "bad", "act", "good"});
    assert(detector.IsPermutation("badactorgoodacting") == false);
    assert(detector.IsPermutation_R("badactorgoodacting") == false);

    detector.ConstructDict({"actor", "bad", "acting", "good"}, false);
    assert(detector.IsPermutation("badactorgoodacting") == true);
    assert(detector.IsPermutation_R("badactorgoodacting") == true);
}

Sunday, 18 September 2016

Data Structure and Algorithm - Find Path between Sensors

1. Problem Description
This is a Google interview question for software develop engineer from careercup. Here is the original thread,
"
Given a rectangle with top-left(a,b) and bottom-right(c,d) coordinates. Also given some coordinates (m,n) of sensors inside the rectangle. All sensors can sense in a circular region of radius r about their centre (m,n). Total N sensors are given. A player has to reach from left side of rectangle to its right side (i.e. he can start his journey from any point whose y coordinate is b and x coordinate is a<=x<=c. He has to end his journey to any point whose y coordinate is d and x coordinate is a<=x<=c).

Write an algorithm to find path (possibly shortest but not necessary) from start to end as described above.

Note: all coordinates are real numbers

(a,b)
|----------------------------------------------|
|.............................................................|end
|.............................................................|
|start......................................................|
|.............................................................|
|----------------------------------------------|(c,d)

Edit: You have to avoid sensors.
Also u can move in any direction any time.
- ankit3600 June 14, 2016 in India | Report Duplicate | Flag 
".

2. Data Structure and Algorithm
Determine if path exists:
Figure 1 and Figure 2 are talking about vertical path. Figure 1 shows a case of having a vertical path and Figure 2 shows a case of not having a vertical path.

Figure 1: Have a Vertical Path

Figure 2: Does Not Have a Vertical Path
The sensors are merged into sensor ranges if they are crossed with each other. Given a rectangle and sensors in it, there is no vertical path if there exists any merged sensor range that crosses both left and right bounds of the rectangle. And there is no horizontal path if there exists any merged sensor range that crosses both top and bottom bounds of the rectangle.
In Figure 2 sensors, S1, S2, S3, S4, S5, S6 and S7, are merged together and cross both left and right bounds of the rectangle, therefore there is not vertical path. However in Figure 1 there does not exists such sensor range, therefore there are paths cross top and bottom boundary.

Merge sensors as sensor range if they are crossed:
Two sensors are crossed if the distance between their center is not greater than sum of their radius. If two sensors are crossed, they will be merged as a sensor range.
Data structure of Sensor range:
    - A list of sensors which they are crossed
    - Four crosses - each with one boundary of the rectangle (left, right, top and bottom)
        * The crosses are tracked and updated when a new sensor joins
        * If both left and right crosses exist, then there is no horizontal path
        * If both top and bottom crosses, exist, then there is no vertical path

How a new sensor is added:
One sensor and one sensor range are crossed if this sensor is crossed with any sensor consisting of this sensor range.
Check if this new sensor is crossed with any existing sensor ranges.
    - Add a new sensor range (consisting of this single sensor), if not crossed with
       any other sensor range
    - Merge this sensor with all crossed sensor ranges to form a new sensor range
      The new sensor range includes:
          * A list of sensors: this sensor and all sensors consisting of crossed sensor ranges
          * Merge the crosses with rectangle of this sensor and of crossed sensor ranges

Figure 3: Merge Sensors
For example in Figure 3,
    - SR1 (sensor range 1) has one sensor S1
    - SR3 (sensor range 3) has one sensor S3
Now add S2, then S2 crosses with two sensor ranges SR1 and SR3. Then S1, S2 and S3 forms a new sensor range.
And merge the cross of SR1 and SR2 and S2 together,
    - SR1 has top cross [P1, P2]
    - SR3 has top cross [P3, P4]
    - S2 does not cross with rectangle
Then the merged cross is [P1, P4]
    - Lower bound = min(all low bounds)
    - Upper bound = max(all upper bounds)

Corner Case of Merge Crosses:
Figure 4 shows the corner case when detecting vertical path. In this example in Figure 4, SR1 consists of 4 sensors - S1, S2, S3 and S4. SR1 has a merged top cross with rectangle as [P1, P2]. The corner case here is that SR1 also has a valid left cross with rectangle.

Figure 4: Merge Crosses - Corner Case
Therefore any point in the range of [Ps, P1) is not a valid starting point of a vertical path. So SR1 should have a top cross with rectangle of [Ps, P2]. The same as the sensor range at the right bottom corner with a bottom cross of [P1, Pe].

The Path returned:
After senors are merged into sensor ranges, the complement ranges of sensor ranges 4 crosses on rectangle boundaries are populated. For example in Figure 5, P1 and P2 are in complement ranges of top boundary, and P3 and P4 are in the complement ranges of bottom boundary. And combinations of point from complement ranges of top and bottom can form a valid vertical path.

Figure 5: Cross Path
In this implementation it is to find the first complement range of pair (top cross, bottom cross) in vertical path search or (left cross, right cross) in horizontal path search. And then return the mid point of the range.

I have implemented the solution in two languages. The C++ implementation is provided in the next section and the Python implementation is on my Python post.

3. C++ Implementation
// ********************************************************************************
// Implementation
// ********************************************************************************
#include <array>
#include <limits>
#include <set>
#include <vector>
#include <cassert>
#include <cmath>
struct Location
{
    Location()
        : x(-1.0)
        , y(-1.0)
    {}
    Location(double x_, double y_)
        : x(x_)
        , y(y_)
    {}
    double x;
    double y;
    static double GetDistance(const Location &l1, const Location &l2) {
        const double deltaX = l1.x - l2.x;
        const double deltaY = l1.y - l2.y;
        return sqrt(deltaX*deltaX + deltaY*deltaY);
    }
    bool operator==(const Location &rhs) const {
        return x == rhs.x && y == rhs.y;
    }
};
struct EdgeCross
{
    EdgeCross()
    {}
    EdgeCross(double lb, double ub)
        : lowerBound(lb)
        , upperBound(ub)
    {}
    inline void SetBounds(double lb, double ub)
    {
        lowerBound = lb;
        upperBound = ub;
    }
    inline void SetLowerBound(double lb)
    {
        lowerBound = lb;
    }
    inline void SetUpperBound(double ub)
    {
        upperBound = ub;
    }
    bool IsValid() const {
        return lowerBound != std::numeric_limits<double>::max() &&
               upperBound != std::numeric_limits<double>::min();
    }
    double lowerBound = std::numeric_limits<double>::max();
    double upperBound = std::numeric_limits<double>::min();
    void MergeBounds(const EdgeCross &rhs)
    {
        SetBounds(std::min(lowerBound, rhs.lowerBound),
                  std::max(upperBound, rhs.upperBound));
    }
    enum EDGE
    {
        LEFT = 0UL,
        TOP,
        RIGHT,
        BOTTOM,
        MAX
    };
    using EdgeCrosses = std::array<EdgeCross, EDGE::MAX>;
};
struct EdgeCrossCmpLess
{
    bool operator() (const EdgeCross *lhs, const EdgeCross *rhs) const {
        return lhs->upperBound < rhs->lowerBound;
    }
};
struct Sensor
{
    Sensor()
    {}
    Sensor(const Location &cnter, double rds)
        : center(cnter)
        , radius(rds)
    {
        assert(radius >= 0.0);
    }
    Location center;
    double radius = 0.0;
    bool Crossed(const Sensor &rhs) const {
        double dist = Location::GetDistance(this->center, rhs.center);
        return dist <= fabs(this->radius + rhs.radius);
    }
    bool CrossedWithHLine(const double Y, EdgeCross& ec) const {
        if (Y >= (center.y - radius) && Y <= (center.y + radius)) {
            const double deltaY = center.y - Y;
            const double deltaX = sqrt(radius*radius - deltaY*deltaY);
            ec.SetBounds(center.x - deltaX, center.x + deltaX);
            return true;
        }
        return false;
    }
    bool CrossedWithVLine(const double X, EdgeCross& ec) const {
        if (X >= (center.x - radius) && X <= (center.x + radius)) {
            const double deltaX = center.x - X;
            const double deltaY = sqrt(radius*radius - deltaX*deltaX);
            ec.SetBounds(center.y - deltaY, center.y + deltaY);
            return true;
        }
        return false;
    }
};
struct SensorRange
{
    SensorRange()
    {}
    EdgeCross::EdgeCrosses edgeCrosses;
    std::vector<const Sensor *> sensors;
    void MergeEdgeCross(const EdgeCross::EdgeCrosses &ecs) {
        for (size_t idx = 0; idx < ecs.size(); ++idx) {
            edgeCrosses[idx].MergeBounds(ecs[idx]);
        }
    }
    void Merge(const SensorRange &sr) {
        MergeEdgeCross(sr.edgeCrosses);
        sensors.insert(sensors.end(), sr.sensors.begin(), sr.sensors.end());
    }
};
struct Rectangle {
    Rectangle()
    {}
    Rectangle(const double l, const double r, const double t, const double b)
    {
        assert(l < r);
        assert(b < t);
        edges[EdgeCross::LEFT] = l;
        edges[EdgeCross::RIGHT] = r;
        edges[EdgeCross::TOP] = t;
        edges[EdgeCross::BOTTOM] = b;
    }
    std::array<double, EdgeCross::MAX> edges;
};
void FindCrossBetweenSensorAndRect(const Sensor &sensor,
                                   const Rectangle &rect,
                                   EdgeCross::EdgeCrosses &edgeCrosses)
{
    for (size_t edge = EdgeCross::LEFT; edge < EdgeCross::MAX; ++edge) {
        if (edge & 1UL) {
            sensor.CrossedWithHLine(rect.edges[edge], edgeCrosses[edge]);
        }
        else {
            sensor.CrossedWithVLine(rect.edges[edge], edgeCrosses[edge]);
        }
    }
}

// Merge sensors into sensor ranges in Figure 3
bool GenerateSenorRangeMap(const std::vector<Sensor> &sensors,
                           const Rectangle &rect,
                           const bool breakIfNoPath,
                           const bool vPath,
                           std::vector<SensorRange> &sensorRanges)
{
    auto itEnd = sensors.cend();
    std::vector<size_t> toMerge;
    for (auto it = sensors.cbegin(); it != itEnd; ++it) {
        const size_t CUR_SIZE = sensorRanges.size();
        toMerge.clear();
        toMerge.reserve(CUR_SIZE);
        // populate all sensor ranges crossed with this sensor
        for (size_t idx = 0; idx < CUR_SIZE; ++idx) {
            auto&& sRange = sensorRanges[idx];
            auto itSenEnd = sRange.sensors.cend();
            for (auto itSen = sRange.sensors.cbegin(); itSen != itSenEnd; ++itSen) {
                if ((*itSen)->Crossed(*it)) {
                    toMerge.push_back(idx);
                    break;
                }
            }
        }
        const SensorRange *workingSR = NULL;
        if (toMerge.empty()) {
            // create a new sensor range if not crossed with any sensor ranges
            SensorRange sr;
            sr.sensors.emplace_back(&(*it));
            FindCrossBetweenSensorAndRect(*it, rect, sr.edgeCrosses);
            sensorRanges.push_back(sr);
            workingSR = &sensorRanges.back();
        }
        else {
            // merge all existing sensor range
            // 1. merge the rest into the first senror range
            // 2. erase the sensor ranges except the very first
            auto itMergeREnd = toMerge.crend() - 1;
            for (auto itMergeR = toMerge.crbegin(); itMergeR != itMergeREnd; ++itMergeR) {
                auto&& sRangeToKeep = sensorRanges[*toMerge.begin()];
                auto&& sRangeToMerge = sensorRanges[*itMergeR];
                sRangeToKeep.Merge(sRangeToMerge);
                sensorRanges.erase(sensorRanges.begin() + *itMergeR);
            }
            // merge with the sensor
            EdgeCross::EdgeCrosses edgeCrosses;
            FindCrossBetweenSensorAndRect(*it, rect, edgeCrosses);
            auto&& sRangeToKeep = sensorRanges[*toMerge.begin()];
            sRangeToKeep.MergeEdgeCross(edgeCrosses);
            sRangeToKeep.sensors.push_back(&(*it));
            workingSR = &sRangeToKeep;
        }
        if (workingSR && breakIfNoPath) {
            if ((vPath &&
                 workingSR->edgeCrosses[EdgeCross::LEFT].IsValid() &&
                 workingSR->edgeCrosses[EdgeCross::RIGHT].IsValid()) ||
                (!vPath &&
                 workingSR->edgeCrosses[EdgeCross::TOP].IsValid() &&
                 workingSR->edgeCrosses[EdgeCross::BOTTOM].IsValid())) {
                    return false;
            }
        }
    }
    return true;
}

// Process the corner case in Figure 4
bool ProcessCornerCase(const bool vPath,
                       const EdgeCross::EdgeCrosses &edgeCrosses,
                       const Rectangle &rect,
                       EdgeCross &edge)
{
    if (edge.IsValid()) {
        if (vPath) {
            // left corner
            if (edgeCrosses[EdgeCross::LEFT].IsValid()) {
                edge.SetLowerBound(rect.edges[EdgeCross::LEFT]);
            }
            // right corner
            if (edgeCrosses[EdgeCross::RIGHT].IsValid()) {
                edge.SetUpperBound(rect.edges[EdgeCross::RIGHT]);
            }
        }
        else {
            // top corner
            if (edgeCrosses[EdgeCross::TOP].IsValid()) {
                edge.SetUpperBound(rect.edges[EdgeCross::TOP]);
            }
            // bottom corner
            if (edgeCrosses[EdgeCross::BOTTOM].IsValid()) {
                edge.SetLowerBound(rect.edges[EdgeCross::BOTTOM]);
            }
        }
        return true;
    }
    return false;
}

using Path = std::array<Location, 2>;
using EdgeCrossSet = std::set<const EdgeCross*, EdgeCrossCmpLess>;
// Get the mid point in Figure 5
bool GetMidPointOfFirstGap(const EdgeCrossSet &ecs,
                           const Rectangle &rect,
                           const bool vPath,
                           double &val)
{
    const double lowerBound = vPath ?
        rect.edges[EdgeCross::LEFT] : rect.edges[EdgeCross::BOTTOM];
    const double upperBound = vPath ?
        rect.edges[EdgeCross::RIGHT] : rect.edges[EdgeCross::TOP];
    if (ecs.empty()) {
        val = (lowerBound + upperBound) / 2;
        return true;
    }
    auto itFirst = ecs.cbegin();
    if ((*itFirst)->lowerBound > lowerBound) {
        val = (lowerBound + (*itFirst)->lowerBound) / 2;
        return true;
    }
    auto itSecond = ++ecs.cbegin();
    if (itSecond == ecs.cend()) {
        if ((*itFirst)->upperBound < upperBound) {
            val = ((*itFirst)->upperBound + upperBound) / 2;
            return true;
        }
        return false;
    }
    if ((*itFirst)->upperBound < (*itSecond)->lowerBound) {
        val = ((*itFirst)->upperBound + (*itSecond)->lowerBound) / 2;
        return true;
    }
    return false;
}

bool FindPath(const std::vector<Sensor> &sensors,
              const Rectangle &rect,
              const bool vPath,
              Path &path)
{
    std::vector<SensorRange> sensorRanges;
    if (!sensors.empty()) {
        // Merge sensors into sensor ranges
        if (!GenerateSenorRangeMap(sensors, rect, true, vPath, sensorRanges)) {
            return false;
        }
    }

    // Check if path exist
    if (sensorRanges.empty()) {
        if (vPath) {
            path[0] = Location{ rect.edges[EdgeCross::LEFT],
                                             rect.edges[EdgeCross::TOP] };
            path[1] = Location{ rect.edges[EdgeCross::LEFT],
                                             rect.edges[EdgeCross::BOTTOM] };
        }
        else {
            path[0] = Location{ rect.edges[EdgeCross::LEFT],
                                             rect.edges[EdgeCross::TOP] };
            path[1] = Location{ rect.edges[EdgeCross::RIGHT],
                                             rect.edges[EdgeCross::TOP] };
        }
        return true;
    }
    EdgeCrossSet ecsFrom;
    EdgeCrossSet ecsTo;
    auto itEnd = sensorRanges.end();
    for (auto it = sensorRanges.begin(); it != itEnd; ++it) {
        // Process the corner case shown in Figure 4
        // And populate the first complement range
        if (vPath && ProcessCornerCase(vPath,
                                       it->edgeCrosses,
                                       rect,
                                       it->edgeCrosses[EdgeCross::TOP])) {
            ecsFrom.insert(&(it->edgeCrosses[EdgeCross::TOP]));
        }
        if (vPath && ProcessCornerCase(vPath,
                                       it->edgeCrosses,
                                       rect,
                                       it->edgeCrosses[EdgeCross::BOTTOM])) {
            ecsTo.insert(&(it->edgeCrosses[EdgeCross::BOTTOM]));
        }
        if (!vPath && ProcessCornerCase(vPath,
                                        it->edgeCrosses,
                                        rect,
                                        it->edgeCrosses[EdgeCross::LEFT])) {
            ecsFrom.insert(&(it->edgeCrosses[EdgeCross::LEFT]));
        }
        if (!vPath && ProcessCornerCase(vPath,
                                        it->edgeCrosses,
                                        rect,
                                        it->edgeCrosses[EdgeCross::RIGHT])) {
            ecsTo.insert(&(it->edgeCrosses[EdgeCross::RIGHT]));
        }
    }
    double valFrom, valTo;
    // Return the path (the mid point of the first complement range pair)
    if (GetMidPointOfFirstGap(ecsFrom, rect, vPath, valFrom) &&
        GetMidPointOfFirstGap(ecsTo, rect, vPath, valTo)) {
        path[0] = vPath ? Location(valFrom, rect.edges[EdgeCross::TOP]) :
                          Location(rect.edges[EdgeCross::LEFT], valFrom);
        path[1] = vPath ? Location(valTo, rect.edges[EdgeCross::BOTTOM]) :
                          Location(rect.edges[EdgeCross::RIGHT], valTo);
        return true;
    }
    return false;
}
// ********************************************************************************
// Test
// ********************************************************************************
void TestFindPathBetweenSensors()
{
    Path path;
    const Rectangle rect = { 1.0, 9.0, 8.0, 1.0 };
    {
        const std::vector<Sensor> sensors{ { { 2.0, 3.0 }, 1.0 },
                                           { { 4.0, 3.0 }, 1.0 },
                                           { { 6.0, 3.0 }, 1.0 },
                                           { { 8.0, 3.0 }, 1.0 }
                                         };
        assert(FindPath(sensors, rect, true, path) == false);
        assert(FindPath(sensors, rect, false, path) == true);
        assert(path.at(0) == Location(1.0, 2.0) && path.at(1) == Location(9.0, 2.0));
    }
    {
        const std::vector<Sensor> sensors{ { { 2.0, 3.0 }, 1.0 },
                                           { { 4.0, 3.0 }, 1.0 },
                                           { { 6.0, 3.0 }, 1.0 },
                                           { { 8.0, 3.0 }, 1.0 },
                                           { { 8.0, 8.0 }, 1.0 },
                                           { { 7.0, 7.0 }, 1.0 }
                                         };
        assert(FindPath(sensors, rect, true, path) == false);
        assert(FindPath(sensors, rect, false, path) == true);
        assert(path.at(0) == Location(1.0, 2.0) && path.at(1) == Location(9.0, 2.0));
    }
    {
        const std::vector<Sensor> sensors{ { { 2.0, 7.2 }, 1.0 },
                                           { { 3.0, 6.0 }, 1.0 },
                                           { { 4.0, 4.5 }, 1.0 },
                                           { { 8.0, 7.2 }, 1.0 },
                                           { { 7.0, 6.2 }, 1.0 },
                                           { { 2.0, 3.0 }, 1.0 },
                                           { { 3.0, 4.0 }, 1.0 },
                                           { { 9.0, 1.0 }, 1.0 }
                                         };
        assert(FindPath(sensors, rect, true, path) == true);
        assert(path.at(0) == Location(5.0, 8.0) && path.at(1) == Location(4.5, 1.0));
        assert(FindPath(sensors, rect, false, path) == true);
        assert(path.at(0) == Location(1.0, 2.0) && path.at(1) == Location(9.0, 4.6));
    }
}