/***************************************************************************
   Copyright 2007 David Nolden <david.nolden.kdevelop@art-master.de>
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "setrepository.h"
#include <debug.h>
#include <list>
#include <util/kdevvarlengtharray.h>
#include <iostream>
#include <limits>
#include <serialization/itemrepository.h>
#include <serialization/indexedstring.h>
#include <QString>
#include <QMutex>
#include <algorithm>

//#define DEBUG_SETREPOSITORY

#ifdef DEBUG_SETREPOSITORY
#define ifDebug(X) X
#else
#define ifDebug(x)
#undef Q_ASSERT
#define Q_ASSERT(x)
#endif

#ifndef DEBUG_SETREPOSITORY
#define CHECK_SPLIT_POSITION(Node)
#else
#define CHECK_SPLIT_POSITION(node) Q_ASSERT(!(node).leftNode || (getLeftNode(&node)->end() <= splitPositionForRange((node).start, (node).end) && getRightNode(&node)->start() >= splitPositionForRange((node).start, (node).end)))
#endif

namespace Utils {

/**
 * To achieve a maximum re-usage of nodes, we make sure that sub-nodes of a node always split at specific boundaries.
 * For each range we can compute a position where that range should be split into its child-nodes.
 * When creating a new node with 2 sub-nodes, we re-create those child-nodes if their boundaries don't represent those split-positions.
 *
 * We pick the split-positions deterministically, they are in order of priority:
 * ((1<<31)*n, n = [0,...]
 * ((1<<30)*n, n = [0,...]
 * ((1<<29)*n, n = [0,...]
 * ((1<<...)*n, n = [0,...]
 * ...
 * */


typedef BasicSetRepository::Index Index;

///The returned split position shall be the end of the first sub-range, and the start of the second
///@param splitBit should be initialized with 31, unless you know better. The value can then be used on while computing child split positions.
///In the end, it will contain the bit used to split the range. It will also contain zero if no split-position exists(length 1)
uint splitPositionForRange(uint start, uint end, uchar& splitBit) {

  if(end-start == 1) {
    splitBit = 0;
    return 0;
  }

  while(true) {
    uint position = ((end-1) >> splitBit) << splitBit; //Round to the split-position in this interval that is smaller than end
    if(position > start && position < end)
      return position;
    Q_ASSERT(splitBit != 0);
    --splitBit;
  }

  return 0;
}

uint splitPositionForRange(uint start, uint end) {
  uchar splitBit = 31;
  return splitPositionForRange(start, end, splitBit);
}

class SetNodeDataRequest;

    #define getLeftNode(node) repository.itemFromIndex(node->leftNode())
    #define getRightNode(node) repository.itemFromIndex(node->rightNode())
    #define nodeFromIndex(index) repository.itemFromIndex(index)
struct SetRepositoryAlgorithms {
  SetRepositoryAlgorithms(SetDataRepository& _repository, BasicSetRepository* _setRepository) : repository(_repository), setRepository(_setRepository) {
  }

  ///Expensive
  Index count(const SetNodeData* node) const;

  void localCheck(const SetNodeData* node);

  void check(uint node);

  void check(const SetNodeData* node);

  QString shortLabel(const SetNodeData& node) const;

  uint set_union(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31);
  uint createSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left = nullptr, const SetNodeData* right = nullptr);
  uint computeSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right, uchar splitBit);
  uint set_intersect(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31);
  bool set_contains(const SetNodeData* node, Index index);
  uint set_subtract(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit = 31);

  //Required both nodes to be split correctly
  bool set_equals(const SetNodeData* lhs, const SetNodeData* rhs);

  QString dumpDotGraph(uint node) const;

    ///Finds or inserts the given ranges into the repository, and returns the set-index that represents them
    uint setForIndices(std::vector<uint>::const_iterator begin, std::vector<uint>::const_iterator end, uchar splitBit = 31) {
        Q_ASSERT(begin != end);

        uint startIndex = *begin;
        uint endIndex = *(end-1)+1;

        if(endIndex == startIndex+1) {
            SetNodeData data(startIndex, endIndex);

            return repository.index( SetNodeDataRequest(&data, repository, setRepository) );
        }

        uint split = splitPositionForRange(startIndex, endIndex, splitBit);
        Q_ASSERT(split);

        std::vector<uint>::const_iterator splitIterator = std::lower_bound(begin, end, split);
        Q_ASSERT(*splitIterator >= split);
        Q_ASSERT(splitIterator > begin);
        Q_ASSERT(*(splitIterator-1) < split);

        return createSetFromNodes(setForIndices(begin, splitIterator, splitBit), setForIndices(splitIterator, end, splitBit));
    }


private:
  QString dumpDotGraphInternal(uint node, bool master=false) const;

  SetDataRepository& repository;
  BasicSetRepository* setRepository;
};

void SetNodeDataRequest::destroy(SetNodeData* data, KDevelop::AbstractItemRepository& _repository) {
    SetDataRepository& repository(static_cast<SetDataRepository&>(_repository));

    if(repository.setRepository->delayedDeletion()) {

        if(data->leftNode()){
            SetDataRepositoryBase::MyDynamicItem left = repository.dynamicItemFromIndex(data->leftNode());
            SetDataRepositoryBase::MyDynamicItem right = repository.dynamicItemFromIndex(data->rightNode());
            Q_ASSERT(left->m_refCount > 0);
            --left->m_refCount;
            Q_ASSERT(right->m_refCount > 0);
            --right->m_refCount;
        }else {
            //Deleting a leaf
            Q_ASSERT(data->end() - data->start() == 1);
            repository.setRepository->itemRemovedFromSets(data->start());
        }
    }
}

SetNodeDataRequest::SetNodeDataRequest(const SetNodeData* _data, SetDataRepository& _repository, BasicSetRepository* _setRepository) : data(*_data), m_hash(_data->hash()), repository(_repository), setRepository(_setRepository), m_created(false) {
    ifDebug( SetRepositoryAlgorithms alg(repository); alg.check(_data) );
}

SetNodeDataRequest::~SetNodeDataRequest() {
    //Eventually increase the reference-count of direct children
    if(m_created) {
        if(data.leftNode())
        ++repository.dynamicItemFromIndex(data.leftNode())->m_refCount;
        if(data.rightNode())
        ++repository.dynamicItemFromIndex(data.rightNode())->m_refCount;
    }
}

//Should create an item where the information of the requested item is permanently stored. The pointer
//@param item equals an allocated range with the size of itemSize().
void SetNodeDataRequest::createItem(SetNodeData* item) const {
    Q_ASSERT((data.rightNode() && data.leftNode()) || (!data.rightNode() && !data.leftNode()));

    m_created = true;

    *item = data;

    Q_ASSERT((item->rightNode() && item->leftNode()) || (!item->rightNode() && !item->leftNode()));

#ifdef DEBUG_SETREPOSITORY
    //Make sure we split at the correct split position
    if(item->hasSlaves()) {
        uint split = splitPositionForRange(data.start, data.end);
        const SetNodeData* left = repository.itemFromIndex(item->leftNode());
        const SetNodeData* right = repository.itemFromIndex(item->rightNode());
        Q_ASSERT(split >= left->end() && split <= right->start());
    }
#endif
    if(!data.leftNode() && setRepository) {
        for(uint a = item->start(); a < item->end(); ++a)
            setRepository->itemAddedToSets(a);
    }
}

bool SetNodeDataRequest::equals(const SetNodeData* item) const {
    Q_ASSERT((item->rightNode() && item->leftNode()) || (!item->rightNode() && !item->leftNode()));
    //Just compare child nodes, since data must be correctly split, this is perfectly ok
    //Since this happens in very tight loops, we don't call an additional function here, but just do the check.
    return item->leftNode() == data.leftNode() && item->rightNode() == data.rightNode() && item->start() == data.start() && item->end() == data.end();
}

Set::Set() : m_tree(0), m_repository(nullptr) {
}

Set::~Set() {
}

unsigned int Set::count() const {
  if(!m_repository || !m_tree)
    return 0;
  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
  return alg.count(m_repository->dataRepository.itemFromIndex(m_tree));
}

Set::Set(uint treeNode, BasicSetRepository* repository) : m_tree(treeNode), m_repository(repository) {
}


Set::Set(const Set& rhs) {
  m_repository = rhs.m_repository;
  m_tree = rhs.m_tree;
}

Set& Set::operator=(const Set& rhs) {
  m_repository = rhs.m_repository;
  m_tree = rhs.m_tree;
  return *this;
}

QString Set::dumpDotGraph() const {
  if(!m_repository || !m_tree)
    return QString();

  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
  return alg.dumpDotGraph(m_tree);
}

Index SetRepositoryAlgorithms::count(const SetNodeData* node) const {
  if(node->leftNode() && node->rightNode())
    return count(getLeftNode(node)) + count(getRightNode(node));
  else
    return node->end() - node->start();
}

void SetRepositoryAlgorithms::localCheck(const SetNodeData* ifDebug(node) ) {
//   Q_ASSERT(node->start() > 0);
  Q_ASSERT(node->start() < node->end());
  Q_ASSERT((node->leftNode() && node->rightNode()) || (!node->leftNode() && !node->rightNode()));
  Q_ASSERT(!node->leftNode() || (getLeftNode(node())->start() == node->start() && getRightNode(node)->end() == node->end()));
  Q_ASSERT(!node->leftNode() || (getLeftNode(node())->end() <= getRightNode(node)->start()));
}

void SetRepositoryAlgorithms::check(uint node) {
  if(!node)
    return;

  check(nodeFromIndex(node));
}

void SetRepositoryAlgorithms::check(const SetNodeData* node) {
  localCheck(node);
  if(node->leftNode())
    check(getLeftNode(node));
  if(node->rightNode())
    check(getRightNode(node));
//  CHECK_SPLIT_POSITION(*node); Re-enable this
}

QString SetRepositoryAlgorithms::shortLabel(const SetNodeData& node) const {
  return QStringLiteral("n%1_%2").arg(node.start()).arg(node.end());
}

QString SetRepositoryAlgorithms::dumpDotGraphInternal(uint nodeIndex, bool master) const {
  if(!nodeIndex)
    return QStringLiteral("empty node");

  const SetNodeData& node(*repository.itemFromIndex(nodeIndex));

  QString color = QStringLiteral("blue");
  if(master)
    color = QStringLiteral("red");

  QString label = QStringLiteral("%1 -> %2").arg(node.start()).arg(node.end());
  if(!node.contiguous())
    label += QLatin1String(", with gaps");

  QString ret = QStringLiteral("%1[label=\"%2\", color=\"%3\"];\n").arg(shortLabel(node), label, color);

  if(node.leftNode()) {
    const SetNodeData& left(*repository.itemFromIndex(node.leftNode()));
    const SetNodeData& right(*repository.itemFromIndex(node.rightNode()));
    Q_ASSERT(node.rightNode());
    ret += QStringLiteral("%1 -> %2;\n").arg(shortLabel(node), shortLabel(left));
    ret += QStringLiteral("%1 -> %2;\n").arg(shortLabel(node), shortLabel(right));
    ret += dumpDotGraphInternal(node.leftNode());
    ret += dumpDotGraphInternal(node.rightNode());
  }

  return ret;
}

QString SetRepositoryAlgorithms::dumpDotGraph(uint nodeIndex) const {
  QString ret = QStringLiteral("digraph Repository {\n");
  ret += dumpDotGraphInternal(nodeIndex, true);
  ret += QLatin1String("}\n");
  return ret;
}

const int nodeStackAlloc = 500;

class SetIteratorPrivate
{
public:
  SetIteratorPrivate()
  {
    nodeStackData.resize(nodeStackAlloc);
    nodeStack = nodeStackData.data();
  }

  SetIteratorPrivate(const SetIteratorPrivate& rhs)
    : nodeStackData(rhs.nodeStackData)
    , nodeStackSize(rhs.nodeStackSize)
    , currentIndex(rhs.currentIndex)
    , repository(rhs.repository)
  {
    nodeStack = nodeStackData.data();
  }

  SetIteratorPrivate& operator=(const SetIteratorPrivate& rhs)
  {
    nodeStackData = rhs.nodeStackData;
    nodeStackSize = rhs.nodeStackSize;
    currentIndex = rhs.currentIndex;
    repository = rhs.repository;
    nodeStack = nodeStackData.data();

    return *this;
  }

  void resizeNodeStack() {
    nodeStackData.resize(nodeStackSize + 1);
    nodeStack = nodeStackData.data();
  }

  KDevVarLengthArray<const SetNodeData*, nodeStackAlloc> nodeStackData;
  const SetNodeData** nodeStack;
  int nodeStackSize = 0;
  Index currentIndex = 0;
  BasicSetRepository* repository = nullptr;

  /**
   * Pushes the noed on top of the stack, changes currentIndex, and goes as deep as necessary for iteration.
   * */
  void startAtNode(const SetNodeData* node) {
    Q_ASSERT(node->start() != node->end());
    currentIndex = node->start();

    do {
      nodeStack[nodeStackSize++] = node;

      if(nodeStackSize >= nodeStackAlloc)
          resizeNodeStack();

      if(node->contiguous())
        break; //We need no finer granularity, because the range is contiguous
      node = Set::Iterator::getDataRepository(repository).itemFromIndex(node->leftNode());
    } while(node);
    Q_ASSERT(currentIndex >= nodeStack[0]->start());
  }
};


std::set<Index> Set::stdSet() const
{
  Set::Iterator it = iterator();
  std::set<Index> ret;

  while(it) {
    Q_ASSERT(ret.find(*it) == ret.end());
    ret.insert(*it);
    ++it;
  }

  return ret;
}

Set::Iterator::Iterator(const Iterator& rhs)
    : d(new SetIteratorPrivate(*rhs.d))
{
}

Set::Iterator& Set::Iterator::operator=(const Iterator& rhs) {
  *d = *rhs.d;
  return *this;
}

Set::Iterator::Iterator()
    : d(new SetIteratorPrivate)
{
}

Set::Iterator::~Iterator() = default;

Set::Iterator::operator bool() const {
  return d->nodeStackSize;
}

Set::Iterator& Set::Iterator::operator++() {

  Q_ASSERT(d->nodeStackSize);

  if(d->repository->m_mutex)
    d->repository->m_mutex->lock();

  ++d->currentIndex;

  //const SetNodeData** currentNode = &d->nodeStack[d->nodeStackSize - 1];
  if(d->currentIndex >= d->nodeStack[d->nodeStackSize - 1]->end()) {
    //Advance to the next node
    while(d->nodeStackSize && d->currentIndex >= d->nodeStack[d->nodeStackSize - 1]->end()) {
      --d->nodeStackSize;
    }

    if(!d->nodeStackSize) {
      //ready
    }else{
      //++d->nodeStackSize;
      //We were iterating the left slave of the node, now continue with the right.
      ifDebug( const SetNodeData& left = *d->repository->dataRepository.itemFromIndex(d->nodeStack[d->nodeStackSize - 1]->leftNode()); Q_ASSERT(left.end == d->currentIndex); )

      const SetNodeData& right = *d->repository->dataRepository.itemFromIndex(d->nodeStack[d->nodeStackSize - 1]->rightNode());

      d->startAtNode(&right);
    }
  }

  Q_ASSERT(d->nodeStackSize == 0 || d->currentIndex < d->nodeStack[0]->end());

  if(d->repository->m_mutex)
    d->repository->m_mutex->unlock();

  return *this;
}

BasicSetRepository::Index Set::Iterator::operator*() const {
  return d->currentIndex;
}

Set::Iterator Set::iterator() const {
  if(!m_tree || !m_repository)
    return Iterator();

  QMutexLocker lock(m_repository->m_mutex);

  Iterator ret;
  ret.d->repository = m_repository;

  if(m_tree)
    ret.d->startAtNode(m_repository->dataRepository.itemFromIndex(m_tree));
  return ret;
}

//Creates a set item with the given children., they must be valid, and they must be split around their split-position.
uint SetRepositoryAlgorithms::createSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right) {

    if(!left)
      left = nodeFromIndex(leftNode);
    if(!right)
      right = nodeFromIndex(rightNode);

    Q_ASSERT(left->end() <= right->start());

    SetNodeData set(left->start(), right->end(), leftNode, rightNode);

    Q_ASSERT(set.start() < set.end());

    uint ret = repository.index(SetNodeDataRequest(&set, repository, setRepository));
    Q_ASSERT(set.leftNode() >= 0x10000);
    Q_ASSERT(set.rightNode() >= 0x10000);
    Q_ASSERT(ret == repository.findIndex(SetNodeDataRequest(&set, repository, setRepository)));
    ifDebug( check(ret) );
    return ret;
}

//Constructs a set node from the given two sub-nodes. Those must be valid, they must not intersect, and they must have a correct split-hierarchy.
//The do not need to be split around their computed split-position.
uint SetRepositoryAlgorithms::computeSetFromNodes(uint leftNode, uint rightNode, const SetNodeData* left, const SetNodeData* right, uchar splitBit)
{
  Q_ASSERT(left->end() <= right->start());
  uint splitPosition = splitPositionForRange(left->start(), right->end(), splitBit);

  Q_ASSERT(splitPosition);

  if(splitPosition < left->end()) {
    //The split-position intersects the left node
    uint leftLeftNode = left->leftNode();
    uint leftRightNode = left->rightNode();

    const SetNodeData* leftLeft = this->getLeftNode(left);
    const SetNodeData* leftRight = this->getRightNode(left);

    Q_ASSERT(splitPosition >= leftLeft->end() && splitPosition <= leftRight->start());

    //Create a new set from leftLeft, and from leftRight + right. That set will have the correct split-position.
    uint newRightNode = computeSetFromNodes(leftRightNode, rightNode, leftRight, right, splitBit);

    return createSetFromNodes(leftLeftNode, newRightNode, leftLeft);

  }else if(splitPosition > right->start()) {
    //The split-position intersects the right node
    uint rightLeftNode = right->leftNode();
    uint rightRightNode = right->rightNode();

    const SetNodeData* rightLeft = this->getLeftNode(right);
    const SetNodeData* rightRight = this->getRightNode(right);

    Q_ASSERT(splitPosition >= rightLeft->end() && splitPosition <= rightRight->start());

    //Create a new set from left + rightLeft, and from rightRight. That set will have the correct split-position.
    uint newLeftNode = computeSetFromNodes(leftNode, rightLeftNode, left, rightLeft, splitBit);

    return createSetFromNodes(newLeftNode, rightRightNode, nullptr, rightRight);
  }else{
    return createSetFromNodes(leftNode, rightNode, left, right);
  }
}

uint SetRepositoryAlgorithms::set_union(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit)
{
  if(firstNode == secondNode)
    return firstNode;

  uint firstStart = first->start(), secondEnd = second->end();

  if(firstStart >= secondEnd)
    return computeSetFromNodes(secondNode, firstNode, second, first, splitBit);

  uint firstEnd = first->end(), secondStart = second->start();

  if(secondStart >= firstEnd)
    return computeSetFromNodes(firstNode, secondNode, first, second, splitBit);

  //The ranges of first and second do intersect

  uint newStart = firstStart < secondStart ? firstStart : secondStart;
  uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd;

  //Compute the split-position for the resulting merged node
  uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit);

  //Since the ranges overlap, we can be sure that either first or second contain splitPosition.
  //The node that contains it, will also be split by it.

  if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) {
    //The split-position intersect with both first and second. Continue the union on both sides of the split-position, and merge it.

    uint firstLeftNode = first->leftNode();
    uint firstRightNode = first->rightNode();
    uint secondLeftNode = second->leftNode();
    uint secondRightNode = second->rightNode();

    const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
    const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
    const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
    const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);

    Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
    Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());

    return createSetFromNodes( set_union(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit), set_union(firstRightNode, secondRightNode, firstRight, secondRight, splitBit) );

  }else if(splitPosition > firstStart && splitPosition < firstEnd) {

    uint firstLeftNode = first->leftNode();
    uint firstRightNode = first->rightNode();

    const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
    const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);

    Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());

    //splitPosition does not intersect second. That means that second is completely on one side of it.
    //So we only need to union that side of first with second.

    if(secondEnd <= splitPosition) {
      return createSetFromNodes( set_union(firstLeftNode, secondNode, firstLeft, second, splitBit), firstRightNode, nullptr, firstRight );
    }else{
      Q_ASSERT(secondStart >= splitPosition);
      return createSetFromNodes( firstLeftNode, set_union(firstRightNode, secondNode, firstRight, second, splitBit), firstLeft );
    }

  }else if(splitPosition > secondStart && splitPosition < secondEnd) {

    uint secondLeftNode = second->leftNode();
    uint secondRightNode = second->rightNode();

    const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
    const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);

    Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());

    if(firstEnd <= splitPosition) {
      return createSetFromNodes( set_union(secondLeftNode, firstNode, secondLeft, first, splitBit), secondRightNode, nullptr, secondRight );
    }else{
      Q_ASSERT(firstStart >= splitPosition);
      return createSetFromNodes( secondLeftNode, set_union(secondRightNode, firstNode, secondRight, first, splitBit), secondLeft );
    }

  }else{
    //We would have stopped earlier of first and second don't intersect
    ifDebug( uint test = repository.findIndex(SetNodeDataRequest(first, repository, setRepository)); qCDebug(LANGUAGE) << "found index:" << test; )
    Q_ASSERT(0);
    return 0;
  }
}

bool SetRepositoryAlgorithms::set_equals(const SetNodeData* lhs, const SetNodeData* rhs)
{
  if(lhs->leftNode() != rhs->leftNode() || lhs->rightNode() != rhs->rightNode())
    return false;
  else
    return true;
}

uint SetRepositoryAlgorithms::set_intersect(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit)
{
  if(firstNode == secondNode)
    return firstNode;

  if(first->start() >= second->end())
    return 0;

  if(second->start() >= first->end())
    return 0;

  //The ranges of first and second do intersect
  uint firstStart = first->start(), firstEnd = first->end(), secondStart = second->start(), secondEnd = second->end();

  uint newStart = firstStart < secondStart ? firstStart : secondStart;
  uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd;

  //Compute the split-position for the resulting merged node
  uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit);

  //Since the ranges overlap, we can be sure that either first or second contain splitPosition.
  //The node that contains it, will also be split by it.


  if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) {
    //The split-position intersect with both first and second. Continue the intersection on both sides

    uint firstLeftNode = first->leftNode();
    uint firstRightNode = first->rightNode();

    uint secondLeftNode = second->leftNode();
    uint secondRightNode = second->rightNode();

    const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
    const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
    const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
    const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);

    Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
    Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());

    uint newLeftNode = set_intersect(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit);
    uint newRightNode = set_intersect(firstRightNode, secondRightNode, firstRight, secondRight, splitBit);

    if(newLeftNode && newRightNode)
      return createSetFromNodes( newLeftNode, newRightNode );
    else if(newLeftNode)
      return newLeftNode;
    else
      return newRightNode;

  }else if(splitPosition > firstStart && splitPosition < firstEnd) {

    uint firstLeftNode = first->leftNode();
    uint firstRightNode = first->rightNode();

    const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
    const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);

    Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());

    //splitPosition does not intersect second. That means that second is completely on one side of it.
    //So we can completely ignore the other side of first.

    if(secondEnd <= splitPosition) {
      return set_intersect(firstLeftNode, secondNode, firstLeft, second, splitBit);
    }else{
      Q_ASSERT(secondStart >= splitPosition);
      return set_intersect(firstRightNode, secondNode, firstRight, second, splitBit);
    }
  }else if(splitPosition > secondStart && splitPosition < secondEnd) {

    uint secondLeftNode = second->leftNode();
    uint secondRightNode = second->rightNode();

    const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
    const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);

    Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());

    if(firstEnd <= splitPosition) {
      return set_intersect(secondLeftNode, firstNode, secondLeft, first, splitBit);
    }else{
      Q_ASSERT(firstStart >= splitPosition);
      return set_intersect(secondRightNode, firstNode, secondRight, first, splitBit);
    }
  }else{
    //We would have stopped earlier of first and second don't intersect
    Q_ASSERT(0);
    return 0;
  }
  Q_ASSERT(0);
}

bool SetRepositoryAlgorithms::set_contains(const SetNodeData* node, Index index)
{
  while(true) {
    if(node->start() > index || node->end() <= index)
      return false;

    if(node->contiguous())
      return true;

    const SetNodeData* leftNode = nodeFromIndex(node->leftNode());

    if(index < leftNode->end())
      node = leftNode;
    else {
      const SetNodeData* rightNode = nodeFromIndex(node->rightNode());
      node = rightNode;
    }
  }

  return false;
}

uint SetRepositoryAlgorithms::set_subtract(uint firstNode, uint secondNode, const SetNodeData* first, const SetNodeData* second, uchar splitBit)
{
  if(firstNode == secondNode)
    return 0;

  if(first->start() >= second->end() || second->start() >= first->end())
    return firstNode;

  //The ranges of first and second do intersect
  uint firstStart = first->start(), firstEnd = first->end(), secondStart = second->start(), secondEnd = second->end();

  uint newStart = firstStart < secondStart ? firstStart : secondStart;
  uint newEnd = firstEnd > secondEnd ? firstEnd : secondEnd;

  //Compute the split-position for the resulting merged node
  uint splitPosition = splitPositionForRange(newStart, newEnd, splitBit);

  //Since the ranges overlap, we can be sure that either first or second contain splitPosition.
  //The node that contains it, will also be split by it.

  if(splitPosition > firstStart && splitPosition < firstEnd && splitPosition > secondStart && splitPosition < secondEnd) {
    //The split-position intersect with both first and second. Continue the subtract on both sides of the split-position, and merge it.

    uint firstLeftNode = first->leftNode();
    uint firstRightNode = first->rightNode();

    uint secondLeftNode = second->leftNode();
    uint secondRightNode = second->rightNode();

    const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
    const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);
    const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
    const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);


    Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());
    Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());

    uint newLeftNode = set_subtract(firstLeftNode, secondLeftNode, firstLeft, secondLeft, splitBit);
    uint newRightNode = set_subtract(firstRightNode, secondRightNode, firstRight, secondRight, splitBit);

    if(newLeftNode && newRightNode)
      return createSetFromNodes(newLeftNode, newRightNode);
    else if(newLeftNode)
      return newLeftNode;
    else
      return newRightNode;

  }else if(splitPosition > firstStart && splitPosition < firstEnd) {

//    Q_ASSERT(splitPosition >= firstLeft->end() && splitPosition <= firstRight->start());

    uint firstLeftNode = first->leftNode();
    uint firstRightNode = first->rightNode();

    const SetNodeData* firstLeft = repository.itemFromIndex(firstLeftNode);
    const SetNodeData* firstRight = repository.itemFromIndex(firstRightNode);

    //splitPosition does not intersect second. That means that second is completely on one side of it.
    //So we only need to subtract that side of first with second.

    uint newLeftNode = firstLeftNode, newRightNode = firstRightNode;

    if(secondEnd <= splitPosition) {
      newLeftNode = set_subtract(firstLeftNode, secondNode, firstLeft, second, splitBit);
    }else{
      Q_ASSERT(secondStart >= splitPosition);
      newRightNode = set_subtract(firstRightNode, secondNode, firstRight, second, splitBit);
    }

    if(newLeftNode && newRightNode)
      return createSetFromNodes(newLeftNode, newRightNode);
    else if(newLeftNode)
      return newLeftNode;
    else
      return newRightNode;

    }else if(splitPosition > secondStart && splitPosition < secondEnd) {

    uint secondLeftNode = second->leftNode();
    uint secondRightNode = second->rightNode();

    const SetNodeData* secondLeft = repository.itemFromIndex(secondLeftNode);
    const SetNodeData* secondRight = repository.itemFromIndex(secondRightNode);

    Q_ASSERT(splitPosition >= secondLeft->end() && splitPosition <= secondRight->start());

    if(firstEnd <= splitPosition) {
      return set_subtract(firstNode, secondLeftNode, first, secondLeft, splitBit);
    }else{
      Q_ASSERT(firstStart >= splitPosition);
      return set_subtract(firstNode, secondRightNode, first, secondRight, splitBit);
    }

  }else{
    //We would have stopped earlier of first and second don't intersect
    Q_ASSERT(0);
    return 0;
  }
  Q_ASSERT(0);
}

Set BasicSetRepository::createSetFromIndices(const std::vector<Index>& indices) {

  QMutexLocker lock(m_mutex);

  if(indices.empty())
    return Set();

  SetRepositoryAlgorithms alg(dataRepository, this);

  return Set(alg.setForIndices(indices.begin(), indices.end()), this);
}

Set BasicSetRepository::createSet(Index i) {
    QMutexLocker lock(m_mutex);
    SetNodeData data(i, i+1);

    return Set(dataRepository.index( SetNodeDataRequest(&data, dataRepository, this) ), this);
}

Set BasicSetRepository::createSet(const std::set<Index>& indices) {

  if(indices.empty())
      return Set();

  QMutexLocker lock(m_mutex);

  std::vector<Index> indicesVector;
  indicesVector.reserve(indices.size());

  for( std::set<Index>::const_iterator it = indices.begin(); it != indices.end(); ++it )
    indicesVector.push_back(*it);

  return createSetFromIndices(indicesVector);
}

BasicSetRepository::BasicSetRepository(const QString& name, KDevelop::ItemRepositoryRegistry* registry, bool delayedDeletion)
    : dataRepository(this, name, registry)
    , m_mutex(nullptr)
    , m_delayedDeletion(delayedDeletion)
{
    m_mutex = dataRepository.mutex();
}

struct StatisticsVisitor {
  explicit StatisticsVisitor(const SetDataRepository& _rep) : nodeCount(0), badSplitNodeCount(0), zeroRefCountNodes(0), rep(_rep) {
  }
  bool operator() (const SetNodeData* item) {
    if(item->m_refCount == 0)
        ++zeroRefCountNodes;
    ++nodeCount;
    uint split = splitPositionForRange(item->start(), item->end());
    if(item->hasSlaves())
      if(split < rep.itemFromIndex(item->leftNode())->end() || split > rep.itemFromIndex(item->rightNode())->start())
        ++badSplitNodeCount;
    return true;
  }
  uint nodeCount;
  uint badSplitNodeCount;
  uint zeroRefCountNodes;
  const SetDataRepository& rep;
};

void BasicSetRepository::printStatistics() const {

  StatisticsVisitor stats(dataRepository);
  dataRepository.visitAllItems<StatisticsVisitor>(stats);
  qCDebug(LANGUAGE) << "count of nodes:" << stats.nodeCount << "count of nodes with bad split:" << stats.badSplitNodeCount << "count of nodes with zero reference-count:" << stats.zeroRefCountNodes;
}

BasicSetRepository::~BasicSetRepository() = default;

void BasicSetRepository::itemRemovedFromSets(uint /*index*/) {
}

void BasicSetRepository::itemAddedToSets(uint /*index*/) {
}

////////////Set convenience functions//////////////////

bool Set::contains(Index index) const
{
  if(!m_tree || !m_repository)
    return false;

  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);
  return alg.set_contains(m_repository->dataRepository.itemFromIndex(m_tree), index);
}

Set Set::operator +(const Set& first) const
{
  if(!first.m_tree)
    return *this;
  else if(!m_tree || !m_repository)
    return first;

  Q_ASSERT(m_repository == first.m_repository);

  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);

  uint retNode = alg.set_union(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree));

  ifDebug(alg.check(retNode));

  return Set(retNode, m_repository);
}

Set& Set::operator +=(const Set& first) {
  if(!first.m_tree)
    return *this;
  else if(!m_tree || !m_repository) {
    m_tree = first.m_tree;
    m_repository = first.m_repository;
    return *this;
  }

  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);

  m_tree = alg.set_union(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree));

  ifDebug(alg.check(m_tree));
  return *this;
}

Set Set::operator &(const Set& first) const {
  if(!first.m_tree || !m_tree)
    return Set();

  Q_ASSERT(m_repository);

  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);

  Set ret( alg.set_intersect(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree)), m_repository );

  ifDebug(alg.check(ret.m_tree));

  return ret;
}

Set& Set::operator &=(const Set& first) {
  if(!first.m_tree || !m_tree) {
    m_tree = 0;
    return *this;
  }

  Q_ASSERT(m_repository);

  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);

  m_tree = alg.set_intersect(m_tree, first.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(first.m_tree));
  ifDebug(alg.check(m_tree));
  return *this;
}

Set Set::operator -(const Set& rhs) const {
  if(!m_tree || !rhs.m_tree)
    return *this;

  Q_ASSERT(m_repository);

  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);

  Set ret( alg.set_subtract(m_tree, rhs.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(rhs.m_tree)), m_repository );
  ifDebug( alg.check(ret.m_tree) );
  return ret;
}

Set& Set::operator -=(const Set& rhs) {
  if(!m_tree || !rhs.m_tree)
    return *this;

  Q_ASSERT(m_repository);

  QMutexLocker lock(m_repository->m_mutex);

  SetRepositoryAlgorithms alg(m_repository->dataRepository, m_repository);

  m_tree = alg.set_subtract(m_tree, rhs.m_tree, m_repository->dataRepository.itemFromIndex(m_tree), m_repository->dataRepository.itemFromIndex(rhs.m_tree));

  ifDebug(alg.check(m_tree));
  return *this;
}

BasicSetRepository* Set::repository() const {
  return m_repository;
}

void Set::staticRef() {
  if(!m_tree)
    return;

  QMutexLocker lock(m_repository->m_mutex);
  SetNodeData* data = m_repository->dataRepository.dynamicItemFromIndexSimple(m_tree);
  ++data->m_refCount;
}

///Mutex must be locked
void Set::unrefNode(uint current) {
    SetNodeData* data = m_repository->dataRepository.dynamicItemFromIndexSimple(current);
    Q_ASSERT(data->m_refCount);
    --data->m_refCount;
    if(!m_repository->delayedDeletion()) {
        if(data->m_refCount == 0) {

            if(data->leftNode()){
                Q_ASSERT(data->rightNode());
                unrefNode(data->rightNode());
                unrefNode(data->leftNode());
            }else {
                //Deleting a leaf
                Q_ASSERT(data->end() - data->start() == 1);
                m_repository->itemRemovedFromSets(data->start());
            }

        m_repository->dataRepository.deleteItem(current);
        }
    }
}

///Decrease the static reference-count of this set by one. This set must have a reference-count > 1.
///If this set reaches the reference-count zero, it will be deleted, and all sub-nodes that also reach the reference-count zero
///will be deleted as well. @warning Either protect ALL your sets by using reference-counting, or don't use it at all.
void Set::staticUnref() {
  if(!m_tree)
    return;

  QMutexLocker lock(m_repository->m_mutex);

  unrefNode(m_tree);
}

StringSetRepository::StringSetRepository(const QString& name) : Utils::BasicSetRepository(name) {
}

void StringSetRepository::itemRemovedFromSets(uint index) {
    ///Call the IndexedString destructor with enabled reference-counting
    KDevelop::IndexedString string = KDevelop::IndexedString::fromIndex(index);

    KDevelop::enableDUChainReferenceCounting(&string, sizeof(KDevelop::IndexedString));
    string.~IndexedString(); //Call destructor with enabled reference-counting
    KDevelop::disableDUChainReferenceCounting(&string);
}

void StringSetRepository::itemAddedToSets(uint index) {
    ///Call the IndexedString constructor with enabled reference-counting

    KDevelop::IndexedString string = KDevelop::IndexedString::fromIndex(index);

    char data[sizeof(KDevelop::IndexedString)];

    KDevelop::enableDUChainReferenceCounting(data, sizeof(KDevelop::IndexedString));
    new (data) KDevelop::IndexedString(string); //Call constructor with enabled reference-counting
    KDevelop::disableDUChainReferenceCounting(data);
}

}

