19.7 Elementary interval transformations

The major algorithms are ordered compositions of the following linear list scans. Advancing i follows nextI; advancing x follows nextX.

Dual IT of concurrent insertion lists

DualITii(i1,i2) requires context-equivalent concurrent insertion lists.

template <class T1, class T2>
void DualIT_ii(T1* i1, T2* i2)
{
    int s1 = 0;
    int s2 = 0;
    while (i1 && i2)
    {
        assert(i1->opid.id != i2->opid.id);
        int d = (s1 + i2->iq) - (s2 + i1->iq);
        if (d < 0 || (d == 0 && i2->opid.id < i1->opid.id))
        {
            i2->iq += s1;
            s2 += i2->size();
            i2 = i2->nextI;
        }
        else
        {
            i1->iq += s2;
            s1 += i1->size();
            i1 = i1->nextI;
        }
    }
    while (i2) { i2->iq += s1; i2 = i2->nextI; }
    while (i1) { i1->iq += s2; i1 = i1->nextI; }
}

The site comparison orders coincident concurrent insertions.

IT2 of serialised insertion lists

IT2ii(i1,i2) requires mutually exclusive insertions with i1 >> i2.

template <class T1, class T2>
void IT2_ii(T1* i1, const T2* i2)
{
    int s = 0;
    while (i1 && i2)
    {
        int d = i2->iq - (s + i1->iq);
        if (d <= 0)
        {
            s += i2->size();
            i2 = i2->nextI;
        }
        else
        {
            if (d < i1->size()) SplitInterval(i1, d);
            i1->iq += s;
            i1 = i1->nextI;
        }
    }
    while (i1) { i1->iq += s; i1 = i1->nextI; }
}

The split case is:

[111111)
    ^
    [22222)        split i1 at the insertion point

IT of extraction past insertion

ITxi(x,i) requires context-equivalent, mutually exclusive lists.

template <class T1, class T2>
void IT_xi(T2* x, const T1* i)
{
    int s = 0;
    while (i && x)
    {
        int d = i->iq - (s + x->xq);
        if (d <= 0)
        {
            s += i->size();
            i = i->nextI;
        }
        else
        {
            if (d < x->size()) SplitInterval(x, d, false);
            x->xq += s;
            x = x->nextX;
        }
    }
    while (x) { x->xq += s; x = x->nextX; }
}
    [iiiiiii)       insertion is on the left: accumulate its length
   x

[xxxxxxxx)         insertion is inside: split the extraction
    i

ET2 of convergent insertion lists

ET2ii(i2,i1) requires mutually exclusive insertions already expressed in their common post-insertion coordinates.

template <class T1, class T2>
void ET2_ii(T2* i2, const T1* i1)
{
    int s = 0;
    while (i1 && i2)
    {
        if (i1->iq + i1->size() <= i2->iq)
        {
            s += i1->size();
            i1 = i1->nextI;
        }
        else if (i2->iq + i2->size() <= i1->iq)
        {
            i2->iq -= s;
            i2 = i2->nextI;
        }
        else
        {
            assert(0); // The insertion lists must be mutually exclusive.
        }
    }
    while (i2) { i2->iq -= s; i2 = i2->nextI; }
}

ET of extraction past insertion

ETxi(x,i) has the same scan as ET2ii, using xq for extraction positions. Each insertion ending before x contributes to s; subtract s when processing x. Any overlap violates the required mutual exclusion.

template <class T1, class T2>
void ET_xi(T1* x, const T2* i)
{
    int s = 0;
    while (i && x)
    {
        if (i->iq + i->size() <= x->xq)
        {
            s += i->size();
            i = i->nextI;
        }
        else if (x->xq + x->size() <= i->iq)
        {
            x->xq -= s;
            x = x->nextX;
        }
        else
        {
            assert(0); // The lists must be mutually exclusive.
        }
    }
    while (x) { x->xq -= s; x = x->nextX; }
}

Transpose of insertion lists

Transposeii(i1,i2) requires the context-serialised insertion sequence [i1,i2]. It changes both lists in place to [i2',i1'], where i2' >> i1', and applying either sequence has the same result. The equality case places i2 before i1, as required when reversing their serial order.

template <class T1, class T2>
void Transposeii(T1* i1, T2* i2)
{
    int s1 = 0;
    int s2 = 0;
    while (i1 && i2)
    {
        int d = i2->iq - (s2 + i1->iq);
        if (d <= 0)
        {
            i2->iq -= s1;
            s2 += i2->size();
            i2 = i2->nextI;
        }
        else
        {
            if (d < i1->size()) SplitInterval(i1, d);
            i1->iq += s2;
            s1 += i1->size();
            i1 = i1->nextI;
        }
    }
    while (i2) { i2->iq -= s1; i2 = i2->nextI; }
    while (i1) { i1->iq += s2; i1 = i1->nextI; }
}

Deletes against move extractions

ITdx(d,x,N) scans extraction intervals without position shifts.

[ddddddddddddd)             split d; continue with its right part
      [xxxxxxx)

[xxxxxxxxxxxxx)             split x; continue with its right part
      [ddddddd)

[dddddddddddd)              split d after common span
[xxxxxx)

[dddddd)                    split x after common span
[xxxxxxxxxxxx)

After splitting, both current spans are equal. If the head of the move alias group has e=0, append a command to track the complete deletion alias group to the move's (idoc,iq). Advance past both complete alias groups. Commands are applied after all document scans.

void IT_dx(
    DeleteInterval* d,
    MoveInterval* x,
    std::vector<SetNewExtractionPosCommand<DeleteInterval> >& N)
{
    while (d && x)
    {
        if (d->xq + d->size() <= x->xq)
        {
            d = d->nextX;
        }
        else if (x->xq + x->size() <= d->xq)
        {
            x = x->nextX;
        }
        else
        {
            if (d->xq < x->xq)
                d = SplitInterval(d, x->xq - d->xq, false);
            else if (x->xq < d->xq)
                x = SplitInterval(x, d->xq - x->xq, false);

            assert(x->xq == d->xq);
            if (x->size() < d->size())
                SplitInterval(d, x->size(), false);
            else if (d->size() < x->size())
                SplitInterval(x, d->size(), false);

            assert(d->size() == x->size());
            if (x->e == 0)
                N.push_back(SetNewExtractionPosCommand<DeleteInterval>(
                    d, x->idoc, x->iq));

            int xq = d->xq;
            do d = d->nextX; while (d && d->xq == xq);
            do x = x->nextX; while (x && x->xq == xq);
        }
    }
}

Wrappers

void IT(Operation& O1, const Operation& O2)
{
    Operation copyO2 = O2;
    DualIT(O1, copyO2);
}

void ET(Operation& O1, const Operation& O2)
{
    Operation copyO2 = O2;
    Transpose(copyO2, O1);
}

Deep copying must recreate every move once and join that new object into both copied lists; copying the insertion and extraction lists independently would violate shared identity.