NetPath VirtualLinkRouter::searchPathDejkstra(VirtualLink * virtualLink, Network * network, SearchPathAlgorithm algorithm)
{
    if ( virtualLink->getFirst() == virtualLink->getSecond() )
        return NetPath();

    // local variables
    std::set<ElementWeight, WeightCompare> elementsToParse;
    std::map<Element * , Link*> incomingEdge;
    std::map<Element * , std::vector<Link *> > elementLinks;

    // initializing parameters
    Links::iterator it = network->getLinks().begin();
    Links::iterator itEnd = network->getLinks().end();
    std::vector<Link *> * vecLinks = NULL;
    for ( ; it != itEnd; ++it )
    {
        if ((*it)->getFirst() != virtualLink->getFirst()) elementsToParse.insert(ElementWeight((*it)->getFirst(), LONG_MAX)); // LONG_MAX equals to inf
        if ((*it)->getSecond() != virtualLink->getFirst()) elementsToParse.insert(ElementWeight((*it)->getSecond(), LONG_MAX));

        vecLinks = &elementLinks[(*it)->getFirst()];
        if (vecLinks->capacity() < NLinks) vecLinks->reserve(NLinks);
        vecLinks->push_back(*it);
        vecLinks = &elementLinks[(*it)->getSecond()];
        if (vecLinks->capacity() < NLinks) vecLinks->reserve(NLinks);
        vecLinks->push_back(*it);
    }
    elementsToParse.insert(ElementWeight(virtualLink->getFirst(), 0));
    elementsToParse.insert(ElementWeight(virtualLink->getSecond(), LONG_MAX));
    Element * currentElement = virtualLink->getFirst();

    Link edge("dijkstra edge", 0);
    ElementWeight temp(NULL, -LONG_MAX);
    std::set<ElementWeight, WeightCompare>::iterator tempIter;
    long curWeight = 0;
    // algorithm itself
    while ( currentElement != NULL && currentElement != virtualLink->getSecond() )
    {
        temp.element = currentElement;
        tempIter = elementsToParse.find(temp);
        assert(tempIter != elementsToParse.end());
        curWeight = tempIter->weight;
//        std::cerr << "currentElement = " << currentElement << ", tempIter->element = " << tempIter->element << ", weight = " << tempIter->weight << '\n';
        elementsToParse.erase(tempIter);

        // going through all neighbors of current element,
        // parsing their weight and choosing the element with the
        // lowest weight
        if ( elementLinks.find(currentElement) == elementLinks.end() )
        {
            return NetPath(); // No links assosiated with element
        }

        std::vector<Link *>& curLinks = elementLinks[currentElement];
        unsigned int sz = curLinks.size();
        for(unsigned int index = 0; index < sz; ++ index)
        {
            Link * cur = curLinks[index];
            Element * other = cur->getFirst() == currentElement ?
                cur->getSecond() : cur->getFirst();
            temp.element = other;
            temp.weight = LONG_MAX;
            tempIter = elementsToParse.find(temp);
            if ( tempIter != elementsToParse.end() )
            {
                edge.setCapacity(cur->getCapacity());
                edge.bindElements(currentElement, other);

                // weight of reaching the next element from current element
                long weight = getEdgeWeigth(edge, network, algorithm) + curWeight;
                if ( weight < tempIter->weight )
                {
                    // have to erase and reinsert because weight is a part of the key
                    temp.element = other;
                    temp.weight = weight;
                    elementsToParse.erase(tempIter);
                    elementsToParse.insert(temp);
                    incomingEdge[other] = cur;
                }
            }
            temp.weight = -LONG_MAX;
        }

        if (elementsToParse.begin()->weight != LONG_MAX) currentElement = elementsToParse.begin()->element;
        else return NetPath(); // if an infinite weight is the minimum, it is a fail
    }

    if ( currentElement != virtualLink->getSecond() )
        return NetPath(); // no way from one element to another

    // retrieving the way
    NetPath answer;

    Element * other = currentElement; // for parsing results with just one edge
    while ( incomingEdge[currentElement]->getFirst() != virtualLink->getFirst() 
        && incomingEdge[currentElement]->getSecond() != virtualLink->getFirst() )
    {
        answer.push_back(incomingEdge[currentElement]);
        other = incomingEdge[currentElement]->getFirst() == currentElement ?
            incomingEdge[currentElement]->getSecond() : incomingEdge[currentElement]->getFirst();
        answer.push_back(static_cast<NetworkingElement *>(other));
        currentElement = other;
    }
    answer.push_back(incomingEdge[other]);
    
    // this step may be skiped, but doing for sure
    std::reverse(answer.begin(), answer.end());
    return answer;
}
NetPath DijkstraRouter::search()
{
    if ( !validateInput() )
    {
        printf("[RD]Wrong input\n");
        return NetPath();
    }

    if ( link->getFirst() == link->getSecond() )
        return NetPath();

    std::set<ElementWeight, WeightCompare> elementsToParse;
    std::map<Element * , Link *> incomingEdge;
    std::map<Element * , std::vector<Link *> > elementLinks;

    Links & links = network->getLinks();
    for ( Links::iterator i = links.begin(); i != links.end(); i++ )
    {
        Link * l = *i;
        if ( l->getFirst() != link->getFirst() )
            elementsToParse.insert(ElementWeight(l->getFirst(), LONG_MAX)); 
        if ( l->getSecond() != link->getFirst() )
            elementsToParse.insert(ElementWeight(l->getSecond(), LONG_MAX));

        elementLinks[l->getFirst()].push_back(l);
        elementLinks[l->getSecond()].push_back(l);
    }
    elementsToParse.insert(ElementWeight(link->getFirst(), 0));
    elementsToParse.insert(ElementWeight(link->getSecond(), LONG_MAX));
    Element * currentElement = link->getFirst();

    Link edge("dijkstraedge", 0);
    ElementWeight temp(0, -LONG_MAX);
    std::set<ElementWeight, WeightCompare>::iterator tempIter;
    long curWeight = 0;
    while ( currentElement != 0 && currentElement != link->getSecond() )
    {
        temp.element = currentElement;
        tempIter = elementsToParse.find(temp);
        assert(tempIter != elementsToParse.end());
        curWeight = tempIter->weight;
        elementsToParse.erase(tempIter);

        if ( elementLinks.find(currentElement) == elementLinks.end() )
            return NetPath();

        std::vector<Link *>& curLinks = elementLinks[currentElement];
        unsigned int size = curLinks.size();
        for(unsigned int index = 0; index < size; index++)
        {
            Link * cur = curLinks[index];
            Element * other = cur->getFirst() == currentElement ?
                cur->getSecond() : cur->getFirst();
            temp.element = other;
            temp.weight = LONG_MAX;
            tempIter = elementsToParse.find(temp);
            if ( tempIter != elementsToParse.end() )
            {
                edge.setCapacity(cur->getCapacity());
                edge.bindElements(currentElement, other);

                long weight = getEdgeWeight(&edge) + curWeight;
                if ( weight < tempIter->weight )
                {
                    temp.element = other;
                    temp.weight = weight;
                    elementsToParse.erase(tempIter);
                    elementsToParse.insert(temp);
                    incomingEdge[other] = cur;
                }
            }
            temp.weight = -LONG_MAX;
        }

        if (elementsToParse.begin()->weight != LONG_MAX) 
            currentElement = elementsToParse.begin()->element;
        else
            return NetPath();
    }

    if ( currentElement != link->getSecond() )
        return NetPath();

    Element * other = currentElement;
    NetPath result;
    while ( incomingEdge[currentElement]->getFirst() != link->getFirst() 
            && incomingEdge[currentElement]->getSecond() != link->getFirst() )
    {
        result.push_back(incomingEdge[currentElement]);
        other = incomingEdge[currentElement]->getFirst() == currentElement ?
            incomingEdge[currentElement]->getSecond() : incomingEdge[currentElement]->getFirst();
        result.push_back((NetworkingElement *)other);
        currentElement = other;
    }
    result.push_back(incomingEdge[other]);
    
    std::reverse(result.begin(), result.end());
    return result;
}