/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { RedBlackTree } from "./collections";
import { MergeTree } from "./mergeTree";
import { CollaborationWindow, IMergeBlock, ISegment } from "./mergeTreeNodes";
interface IOverlapClient {
    clientId: number;
    seglen: number;
}
/**
 * Tracks length information for a part of a MergeTree (block) at a given time (seq).
 * These objects are associated with internal nodes (i.e. blocks).
 */
export interface PartialSequenceLength {
    /**
     * Sequence number
     */
    seq: number;
    /**
     * The length of the associated block.
     */
    len: number;
    /**
     * The delta between the current length of the associated block and its length at the previous seq number.
     */
    seglen: number;
    /**
     * clientId for the client that submitted the op with sequence number `seq`.
     */
    clientId?: number;
    /**
     * If this partial length obliterated remote segments, this is the length of
     * those segments
     */
    remoteObliteratedLen?: number;
    /**
     * This field maps each client to the size of the intersection between segments deleted at this seq
     * and segments concurrently deleted by that client.
     *
     * For example, this PartialSequenceLength:
     * ```typescript
     * {
     *     seq: 5,
     *     len: 100,
     *     seglen: -10,
     *     clientId: 0,
     *     overlapRemoveClients: <RedBlack tree with key-values expressed by>{
     *         1: { clientId: 1, seglen: -5 },
     *         3: { clientId: 3, seglen: -10 }
     *     }
     * }
     * ```
     *
     * corresponds to an op submitted by client 0 which:
     * - reduces the length of this block by 10 (it may have deleted a single segment of length 10,
     *     several segments totalling length 10, or even delete and add content for a total reduction of 10 length)
     * - was concurrent to one or more ops submitted by client 1 that also removed some of the same segments,
     *     whose length totalled 5
     * - was concurrent to one or more ops submitted by client 3 that removed some of the same segments,
     *     whose length totalled 10
     */
    overlapRemoveClients?: RedBlackTree<number, IOverlapClient>;
    /**
     * This field is the same as `overlapRemoveClients`, except that it tracks
     * overlapping obliterates rather than removes.
     */
    overlapObliterateClients?: RedBlackTree<number, IOverlapClient>;
}
export interface PartialSequenceLengthsOptions {
    verifier?: (partialLengths: PartialSequenceLengths) => void;
    verifyExpected?: (mergeTree: MergeTree, node: IMergeBlock, refSeq: number, clientId: number, localSeq?: number) => void;
    zamboni: boolean;
}
/**
 * Keeps track of partial sums of segment lengths for all sequence numbers in the current collaboration window.
 * Only used during active collaboration.
 *
 * This class is associated with an internal node (block) of a MergeTree. It efficiently answers queries of the form
 * "What is the length of `block` from the perspective of some particular seq and clientId?".
 *
 * It also supports incremental updating of state for newly-sequenced ops that don't affect the structure of the
 * MergeTree.
 *
 * To answer these queries, it pre-builds several lists which track the length of the block at a per-sequence-number
 * level. These lists are:
 *
 * 1. (`partialLengths`): Stores the total length of the block.
 * 2. (`clientSeqNumbers[clientId]`): Stores only the total lengths of segments submitted by `clientId`. [see footnote]
 *
 * The reason both lists are necessary is that resolving the length of the block from the perspective of
 * (clientId, refSeq) requires including both of the following types of segments:
 * 1. Segments sequenced before `refSeq`
 * 2. Segments submitted by `clientId`
 *
 * This is possible with the above bookkeeping, using:
 *
 * (length of the block at the minimum sequence number)
 * + (partialLengths total length at refSeq)
 * + (clientSeqNumbers total length at most recent op)
 * - (clientSeqNumbers total length at refSeq)
 *
 * where the subtraction avoids double-counting segments submitted by clientId sequenced within the collab window.
 *
 * To enable reconnect, if constructed with `computeLocalPartials === true` it also supports querying for the length of
 * the block from the perspective of the local client at a particular `refSeq` and `localSeq`. This computation is
 * similar to the above:
 *
 * (length of the block at the minimum sequence number)
 * + (partialLengths total length at refSeq)
 * + (unsequenced edits' total length submitted before localSeq)
 * - (overlapping remove of the unsequenced edits' total length at refSeq)
 *
 * This algorithm scales roughly linearly with number of editing clients and the size of the collab window.
 * (certain unlikely sequences of operations may introduce log factors on those variables)
 *
 * Note: there is some slight complication with clientSeqNumbers resulting from the possibility of different clients
 * concurrently removing the same segment. See the field's documentation for more details.
 */
export declare class PartialSequenceLengths {
    /**
     * The minimumSequenceNumber as defined by the collab window used in the last call to `update`,
     * or if no such calls have been made, the one used on construction.
     */
    minSeq: number;
    static options: PartialSequenceLengthsOptions;
    /**
     * Combine the partial lengths of block's children
     * @param block - an interior node. If `recur` is false, it is assumed that each interior node child of this block
     * has its partials up to date.
     * @param collabWindow - segment window of the segment tree containing `block`.
     * @param recur - whether to recursively compute partial lengths for internal children of `block`.
     * This incurs more work, but gives correct bookkeeping in the case that a descendant in the merge tree has been
     * modified without bubbling up the resulting partial length change to this block's partials.
     * @param computeLocalPartials - whether to compute partial length information about local unsequenced ops.
     * This enables querying for the length of the block at a given localSeq, but incurs extra work.
     * Local partial information doesn't support `update`.
     */
    static combine(block: IMergeBlock, collabWindow: CollaborationWindow, recur?: boolean, computeLocalPartials?: boolean): PartialSequenceLengths;
    /**
     * @returns a PartialSequenceLengths structure which tracks only lengths of leaf children of the provided
     * IMergeBlock.
     */
    private static fromLeaves;
    private static getOverlapClients;
    private static accumulateRemoveClientOverlap;
    private static accumulateMoveClientOverlap;
    /**
     * Coalesce overlapping move lengths for a partial length entry that already
     * exists
     *
     * @param segmentLen - Length of segment with overlapping moves
     * @param segment - Segment with overlapping moves
     * @param firstGte - Existing partial length entry
     * @param clientIds - Ids of clients that concurrently obliterated this segment
     */
    static accumulateMoveOverlapForExisting(segmentLen: number, segment: ISegment, firstGte: PartialSequenceLength, clientIds: number[]): void;
    /**
     * @param obliterateOverlapLen - Length of segment with overlap
     * @param clientIds - Ids of clients that have concurrently obliterated this
     * segment
     */
    private static getMoveOverlapForExisting;
    private static updatePartialsAfterInsertion;
    /**
     * Inserts length information about the insertion of `segment` into
     * `combinedPartialLengths.partialLengths`.
     *
     * Does not update the clientSeqNumbers field to account for this segment.
     *
     * If `removalInfo` or `moveInfo` are defined, this operation updates the
     * bookkeeping to account for the (re)moval of this segment at the (re)movedSeq
     * instead.
     *
     * When the insertion or (re)moval of the segment is un-acked and
     * `combinedPartialLengths` is meant to compute such records, this does the
     * analogous addition to the bookkeeping for the local segment in
     * `combinedPartialLengths.unsequencedRecords`.
     */
    private static insertSegment;
    private static addSeq;
    /**
     * Length of the block this PartialSequenceLength corresponds to when viewed at `minSeq`.
     */
    private minLength;
    /**
     * Total number of segments in the subtree rooted at the block this PartialSequenceLength corresponds to.
     */
    private segmentCount;
    /**
     * List of PartialSequenceLength objects--ordered by increasing seq--giving length information about
     * the block associated with this PartialSequenceLengths object.
     *
     * `partialLengths[i].len` contains the length of this block considering only sequenced segments with
     * `sequenceNumber <= partialLengths[i].seq`.
     */
    private readonly partialLengths;
    /**
     * clientSeqNumbers[clientId] is a list of partial lengths for sequenced ops which either:
     * - were submitted by `clientId`.
     * - deleted a range containing segments that were concurrently deleted by `clientId`
     *
     * The second case is referred to as the "overlapping delete" case. It is necessary to avoid double-counting
     * the removal of those segments in queries including clientId.
     */
    private readonly clientSeqNumbers;
    /**
     * Contains information required to answer queries for the length of this segment from the perspective of
     * the local client but not including all local segments (i.e., `localSeq !== collabWindow.localSeq`).
     * This field is only computed if requested in the constructor (i.e. `computeLocalPartials === true`).
     */
    private unsequencedRecords;
    constructor(
    /**
     * The minimumSequenceNumber as defined by the collab window used in the last call to `update`,
     * or if no such calls have been made, the one used on construction.
     */
    minSeq: number, computeLocalPartials: boolean);
    update(node: IMergeBlock, seq: number, clientId: number, collabWindow: CollaborationWindow): void;
    /**
     * Returns the length of this block as viewed from the perspective of `clientId` at `refSeq`.
     * This is the total length of all segments sequenced at or before refSeq OR submitted by `clientId`.
     * If `clientId` is the local client, `localSeq` can also be provided. In that case, it is the total
     * length of all segments submitted at or before `refSeq` in addition to any local, unacked segments
     * with `segment.localSeq <= localSeq`.
     *
     * Note: the local case (where `localSeq !== undefined`) is only supported on a PartialSequenceLength object
     * constructed with `computeLocalPartials` set to true and not subsequently updated with `update`.
     */
    getPartialLength(refSeq: number, clientId: number, localSeq?: number): number;
    /**
     * Computes the seglen for the double-counted removed overlap at (refSeq, localSeq). This logic is equivalent
     * to the following:
     *
     * ```typescript
     *   let total = 0;
     *   for (const partialLength of this.unsequencedRecords!.overlappingRemoves) {
     *       if (partialLength.seq > refSeq) {
     *           break;
     *       }
     *
     *      if (partialLength.localSeq <= localSeq) {
     *          total += partialLength.seglen;
     *      }
     *   }
     *
     *   return total;
     * ```
     *
     * Reconnect happens to only need to compute these lengths for two refSeq values: before and
     * after the rebase. Since these lists potentially scale with O(collab window * number of local edits)
     * and potentially need to be queried for each local op that gets rebased,
     * we cache the results for a given refSeq in `this.unsequencedRecords.cachedOverlappingByRefSeq` so
     * that they can be binary-searched the same way the usual partialLengths lists are.
     */
    private computeOverlappingLocalRemoves;
    toString(glc?: (id: number) => string, indentCount?: number): string;
    private zamboni;
    private addClientSeqNumber;
    private addClientSeqNumberFromPartial;
    private cliLatestLEQ;
    private cliLatest;
}
export declare function verifyExpected(mergeTree: MergeTree, node: IMergeBlock, refSeq: number, clientId: number, localSeq?: number): void;
export declare function verify(partialSeqLengths: PartialSequenceLengths): void;
/**
 * Combines the `overlapRemoveClients` and `overlapObliterateClients` fields of
 * two `PartialSequenceLength` objects, modifying the first PartialSequenceLength's
 * bookkeeping in-place.
 *
 * Combination is performed additively on `seglen` on a per-client basis.
 */
export declare function combineOverlapClients(a: PartialSequenceLength, b: PartialSequenceLength): void;
export {};
//# sourceMappingURL=partialLengths.d.ts.map