19.10 Transpose
The supporting move-tracking algorithms used below are specified in
Move tracking and transpose helpers.
This subchapter specifies the complete Transpose(Operation&,Operation&) function that
orders and combines them with the elementary interval transformations.
Contract
Given the context-serialised sequence [O1,O2], Transpose(O1,O2) changes
both operations in place to [O2',O1'] with the same effect. The operation identifiers
must differ. Both input operations must satisfy all representation invariants; both output
operations must satisfy them as well.
Write each operation as its four families of intervals:
O1 = [ C1 D1 I1 X1 ] O2 = [ C2 D2 I2 X2 ]
The complete transpose consists of the sixteen pairwise interactions in this matrix. The axes use
the coordinate order C,D,I,X: a cell ab is the interaction between the column
family a from O1 and the row family b from O2. Processing
begins at the bottom right and progresses towards the top left. The order is significant because
completed cells establish the compatible coordinates required at the moving frontier; the phase
diagrams therefore describe the dependency structure of the algorithm as well as its progress.
The phase diagrams use the symbols defined in
Dual inclusion transformation, oriented for this
matrix: O1 proceeds upwards and O2 proceeds from right to left. A combined
symbol therefore has two arrows emanating from the bottom-right corner of its cell. The calls shown
beneath each diagram are normative.
The Transpose function
The following seven subsections are consecutive parts of this function body. The opening listing gives the function signature and validates its inputs; the closing listing after step 7 gives the common epilogue and closes the function.
void Transpose(Operation& O1, Operation& O2)
{
assert(O1.GetId() != O2.GetId());
O1.AssertValid();
O2.AssertValid();
Step 1: transpose creations from O2
For every document occurring in both operations, in this exact order:
{
for (auto& [doc, di2] : O2.documents)
{
auto m1 = O1.documents.find(doc);
if (m1 != O1.documents.end())
{
DocIntervals& di1 = m1->second;
IT_xi(di1.x, di2.c); // x1 = IT(x1,c2)
Transposeii(di1.i, di2.c);
IT_xi(di1.d, di2.c); // d1 = IT(d1,c2)
Transposeii(di1.c, di2.c);
}
}
}
This moves C2 to the left of every family in O1. The extraction transforms
split intervals when a creation falls inside them; the insertion transposes use their accumulated
left and right shifts.
Step 2: track deletions from O2 back through moves of O1
After step 1, D2 and I1 are both in post-C1,I1,C2
coordinates. An overlap therefore means that D2 deletes characters moved by
O1. For every common document call TrackETxm(D2,I1,N2). Only after all
documents have been scanned, apply every command in N2 to O2.
{
std::vector<SetNewExtractionPosCommand<DeleteInterval> > N2;
for (auto& [doc, di2] : O2.documents)
{
auto m1 = O1.documents.find(doc);
if (m1 != O1.documents.end())
{
DocIntervals& di1 = m1->second;
Track_ETxm(di2.d, di1.i, N2);
}
}
ApplyNewExtractionPosCommands(O2, N2);
}
Step 3: process D2 and transform I1 and X1 past I2
{
for (auto& [doc, di2] : O2.documents)
{
auto m1 = O1.documents.find(doc);
if (m1 != O1.documents.end())
{
DocIntervals& di1 = m1->second;
ET_xi(di2.d, di1.i);
ET_xi(di2.d, di1.c);
IT_xi(di1.x, di2.i);
IT2_ii(di1.i, di2.i);
}
}
}
The tracking in step 2 made D2 and I1 mutually exclusive, satisfying the
first call's precondition. Concurrency makes D2 and C1 mutually exclusive.
After the final two calls, I1,X1,I2,X2 all use the context containing both sets of move
insertions; the move conflicts can now be compared directly.
Step 4: transpose competing moves
Create empty command lists N1 and N2. For every common document call
TransposeXX1(X1,X2,N1), followed by
TransposeXX2(I1,X2,N1,N2). After every document has been scanned, apply
N1 to O1 and N2 to O2. No extraction list may be
relocated before both scans have finished.
{
std::vector<SetNewExtractionPosCommand<MoveInterval> > N1;
std::vector<SetNewExtractionPosCommand<MoveInterval> > N2;
for (auto& [doc, di2] : O2.documents)
{
auto m1 = O1.documents.find(doc);
if (m1 != O1.documents.end())
{
DocIntervals& di1 = m1->second;
TransposeConcurrent_xx_1(di1.x, di2.x, N1);
TransposeConcurrent_xx_2(di1.i, di2.x, N1, N2);
}
}
ApplyNewExtractionPosCommands(O1, N1);
ApplyNewExtractionPosCommands(O2, N2);
}
Step 5: exclusion-transform I2 and X2 past I1
{
for (auto& [doc, di2] : O2.documents)
{
auto m1 = O1.documents.find(doc);
if (m1 != O1.documents.end())
{
DocIntervals& di1 = m1->second;
ET_xi(di2.x, di1.i);
ET2_ii(di2.i, di1.i);
IT_xi(di1.d, di2.i);
}
}
}
Step 4 made X2 and I1 mutually exclusive. The first two calls remove the
effect of I1 from both parts of the moves in O2. The third begins transforming
the deletions of O1 through the moves of O2 by first accounting for their
insertions.
Step 6: track D1 through enabled moves of O2
For every common document run ITdx(D1,X2,ND1). It aligns overlapping spans by
splitting. When an X2 alias group begins with e=0, append a command to move
the complete overlapping D1 alias group to (X2.idoc,X2.iq). Apply
ND1 to O1 only after all documents have been scanned.
{
std::vector<SetNewExtractionPosCommand<DeleteInterval> > ND1;
for (auto& [doc, di2] : O2.documents)
{
auto m1 = O1.documents.find(doc);
if (m1 != O1.documents.end())
{
DocIntervals& di1 = m1->second;
IT_dx(di1.d, di2.x, ND1);
}
}
ApplyNewExtractionPosCommands(O1, ND1);
}
Step 7: finish C1 against the moves of O2
{
for (auto& [doc, di2] : O2.documents)
{
auto m1 = O1.documents.find(doc);
if (m1 != O1.documents.end())
{
DocIntervals& di1 = m1->second;
Transposeii(di1.c, di2.i);
ET_xi(di2.x, di1.c);
}
}
}
Concurrency guarantees that X2 and C1 are mutually exclusive. These calls
complete the remaining position changes. Finally coalesce adjacent compatible intervals in both
operations and check every invariant.
O1.CoalesceAdjacentIntervals();
O2.CoalesceAdjacentIntervals();
O1.AssertValid();
O2.AssertValid();
}
Implementation correspondence
The seven steps above occur in the same order as Transpose(Operation&,Operation&).
The preceding helper subchapter specifies Track_ETxm, Transpose_ee,
TransposeConcurrent_xx_1 and TransposeConcurrent_xx_2. Together, the two
subchapters correspond directly to Transpose.cpp.