-- FeaturePSTImpl.mesa

-- A generic package to organize list elements
-- in a priority search tree by the intervals they cover,
-- and to search by interval.

-- last modified by E. McCreight, November 8, 1982  2:34 PM
-- written by E. McCreight, November 3, 1981  11:08 AM

DIRECTORY
  InlineDefs,
  MiscDefs,
  FeaturePST;

FeaturePSTImpl: PROGRAM
  IMPORTS InlineDefs, MiscDefs, FeaturePST
  EXPORTS FeaturePST SHARES FeaturePST =
  BEGIN OPEN FeaturePST;

  -- Note: An Interval is [min..max).


  NewFeaturePST: PUBLIC PROCEDURE[zone: UNCOUNTED ZONE]
    RETURNS[PSTHandle] =
    {RETURN[zone.NEW[PSTObject ← [
      zone: zone, root: zone.NEW[PSTNode ← [nil[]]]]]]};


  DestroyFeaturePST: PUBLIC PROCEDURE[p: PSTHandle]
    RETURNS[PSTHandle] =
    BEGIN

    InternalDestroyPST: PROCEDURE[p: PSTNodePtr] =
      BEGIN
      WITH d: p SELECT FROM
        nil => NULL;
        leaf => p↑ ← [nil[]];
        internal =>
          BEGIN
          pst: PSTBinNodePtr ← d.pst;
          InternalDestroyPST[@pst.halves[lower]];
          InternalDestroyPST[@pst.halves[upper]];
          zone.FREE[@pst]; p↑ ← [nil[]];
          END;
        ENDCASE;
      END;

    zone: UNCOUNTED ZONE ← p.zone;
    pi: PSTNodePtr ← p.root;
    InternalDestroyPST[pi];
    zone.FREE[@p.root];
    WHILE p.freeNodes#NIL DO
      node: PSTBinNodePtr ← NewNode[p];
      zone.FREE[@node];
      ENDLOOP;
    zone.FREE[@p];
    RETURN[NIL];
    END; -- of DestroyPST

  InsertFeaturePST: PUBLIC PROCEDURE[p: PSTHandle,
    item: Item] =
    BEGIN
    -- There can be many items with the same
    -- .y value.  For now we shall simply let these
    -- form degenerate trees upward, so as not to
    -- overflow our stacks.

    pi: PSTNodePtr ← p.root;
--  IF checkTree THEN CheckPST[p];
    DO WITH f: pi SELECT FROM
      nil => {pi↑ ← [leaf[item: item]]; EXIT};
      leaf => pi↑ ← [internal[pst: NewNode[p: p,
        init: [vMin: f.item,
          uLen: CommonLen[f.item, item, pt]]]]];
      internal =>
        BEGIN OPEN InlineDefs;
        son: PSTBinNodePtr ← f.pst;
        newULen: RadixIndex ← CommonLen[item, son.vMin, pt];
        IF newULen<son.uLen THEN
          BEGIN -- a new node for lp must be put
            -- between father and son;
          new: PSTBinNodePtr ← NewNode[p: p,
            init:
            (IF TreeHalf[item, newULen]=lower THEN
              [uLen: newULen, halves:
                [lower: [leaf[item]], upper: [internal[son]]]]
            ELSE
              [uLen: newULen, halves:
                [lower: [internal[son]], upper: [leaf[item]]]])];
          pi↑ ← [internal[new]];
          ExtractVMin[pi, p]; -- sets the vMin field
          EXIT;
          END
        ELSE -- item.lp and son.vMin.lp match in first son.uLen bits
          BEGIN
          IF Shallower[item, son.vMin] THEN
            BEGIN -- exchange item with son.vMin
            t: Item ← son.vMin; son.vMin ← item; item ← t;
            END;
          pi ← @son.halves[TreeHalf[item, son.uLen]];
          END;
        END; -- internal
      ENDCASE;
      ENDLOOP;
--  IF checkTree THEN CheckPST[p];
    END; -- of InsertFeaturePST

  CouldntDeleteFeaturePST: PUBLIC SIGNAL = CODE;

  DeleteFeaturePST: PUBLIC PROCEDURE[p: PSTHandle, item: Item] =
    BEGIN
    pi: PSTNodePtr ← p.root;
--  IF checkTree THEN CheckPST[p];
    DO WITH f: pi SELECT FROM
      nil => SIGNAL CouldntDeleteFeaturePST;
      leaf => IF item=f.item THEN {pi↑ ← [nil[]]; EXIT} ELSE
        SIGNAL CouldntDeleteFeaturePST;
      internal =>
        BEGIN
        son: PSTBinNodePtr ← f.pst;
        IF item=son.vMin THEN {ExtractVMin[pi, p]; EXIT}
        ELSE
          pi ← @son.halves[TreeHalf[item, son.uLen]];
        END;
      ENDCASE;
      ENDLOOP;
--  IF checkTree THEN CheckPST[p];
    END; -- of DeleteFeaturePST

  ExtractVMin: PROCEDURE[pi: PSTNodePtr, p: PSTHandle] =
    BEGIN
    DO WITH f: pi SELECT FROM
      nil => MiscDefs.CallDebugger[NIL];
      leaf => {pi↑ ← [nil[]]; RETURN};
      internal =>
        BEGIN
        t: PSTBinNodePtr ← f.pst;
        WITH l: t.halves[lower] SELECT FROM
          nil =>
            WITH u: t.halves[upper] SELECT FROM
              nil =>
                {FreeNode[p, t]; pi↑ ←[nil[]]; RETURN};
              leaf =>
                BEGIN
                pi↑ ← [leaf[item: u.item]];
                FreeNode[p, t];
                RETURN;
                END;
              internal => {t.vMin ← u.pst.vMin; pi ← @u};
              ENDCASE; -- u
          leaf =>
            WITH u: t.halves[upper] SELECT FROM
              nil =>
                BEGIN
                pi↑ ← [leaf[item: l.item]];
                FreeNode[p, t];
                RETURN;
                END;
              leaf =>
                IF Shallower[l.item, u.item] THEN
                  {t.vMin ← l.item; pi ← @l}
                ELSE {t.vMin ← u.item; pi ← @u};
              internal =>
                IF Shallower[l.item, u.pst.vMin] THEN
                  {t.vMin ← l.item; pi ← @l}
                ELSE {t.vMin ← u.pst.vMin; pi ← @u};
              ENDCASE; -- u
          internal =>
            WITH u: t.halves[upper] SELECT FROM
              nil => {t.vMin ← l.pst.vMin; pi ← @l};
              leaf =>
                IF Shallower[l.pst.vMin, u.item] THEN
                  {t.vMin ← l.pst.vMin; pi ← @l}
                ELSE {t.vMin ← u.item; pi ← @u};
              internal =>
                IF Shallower[l.pst.vMin, u.pst.vMin] THEN
                  {t.vMin ← l.pst.vMin; pi ← @l}
                ELSE {t.vMin ← u.pst.vMin; pi ← @u};
              ENDCASE; -- u
          ENDCASE; -- l
        END;
      ENDCASE; -- f
      ENDLOOP;
    END; -- of ExtractVMin

  NewNode: PROCEDURE[p: PSTHandle,
    init: PSTBinNode ← [uLen: 0]]
    RETURNS[new: PSTBinNodePtr] = INLINE
    BEGIN
    IF (new ← p.freeNodes)#NIL THEN
      BEGIN
      WITH n: new.halves[lower] SELECT FROM
        internal => p.freeNodes ← n.pst;
        nil => p.freeNodes ← NIL;
        ENDCASE => ERROR; -- bad free list linkage
      new↑ ← init;
      END
    ELSE
      new ← p.zone.NEW[PSTBinNode ← init];
    END; -- of NewNode


  FreeNode: PROCEDURE[p: PSTHandle,
    old: PSTBinNodePtr] = INLINE
    BEGIN
    old.halves[lower] ← [internal[pst: p.freeNodes]];
    p.freeNodes ← old;
    END; -- of FreeNode

  pqLim: INTEGER = 200;
  PSTPQEnt: TYPE = RECORD[min: Domain, p: PSTNodePtr];
  PSTPQArray: TYPE = ARRAY [1..pqLim) OF PSTPQEnt;
  PSTPQPtr: TYPE = LONG POINTER TO PSTPQArray ← NIL;
  PSTPQs: ARRAY [0..10) OF PSTPQPtr;
  recursionDepth: [0..10) ← 0;

  SearchFeaturePST: PUBLIC PROCEDURE[p: PSTHandle,
    int: Interval, touch: PROCEDURE[item: Item]] =
    BEGIN -- enumerates in increasing order by Min[item]

    MakePSTPQEnt: PROCEDURE[p: PSTNodePtr]
      RETURNS[PSTPQEnt] = INLINE
      BEGIN
      WITH dp: p SELECT FROM
        internal => RETURN[[min: Min[dp.pst.vMin], p: p]];
        leaf => RETURN[[min: Min[dp.item], p: p]];
        ENDCASE => RETURN[[min: FIRST[Domain], p: p]];
      END;

    yMin: Domain ← int.min;
    yMax: Domain ← int.max; -- yMax not in interval

    pqSize: [0..pqLim) ← 1;
    PSTPQ: PSTPQPtr ← IF PSTPQs[recursionDepth]#NIL
      THEN PSTPQs[recursionDepth]
      ELSE (PSTPQs[recursionDepth] ← p.zone.NEW[PSTPQArray]);
    recursionDepth ← recursionDepth+1;
    PSTPQ[1] ← MakePSTPQEnt[p.root];

    WHILE 1<=pqSize DO

      RippleUp: PROCEDURE[] = INLINE
        BEGIN
        p: PSTPQEnt ← PSTPQ[hole];
        FOR nextHole ← hole/2, hole/2
          WHILE 1<=nextHole AND p.min<PSTPQ[nextHole].min DO
          PSTPQ[hole] ← PSTPQ[nextHole];
          hole ← nextHole;
          ENDLOOP;
        PSTPQ[hole] ← p;
        END; -- of RippleUp

      FillHole: PROCEDURE[] = INLINE
        BEGIN
        IF hole<pqSize THEN
          {PSTPQ[hole] ← PSTPQ[pqSize]; RippleUp[]};
        pqSize ← pqSize-1;
        END; -- of FillHole

      NewPSTPQ: PROCEDURE[p: PSTNodePtr] = INLINE
        BEGIN
        hole ← pqSize ← pqSize+1;
        PSTPQ[pqSize] ← MakePSTPQEnt[p];
        RippleUp[];
        END; -- of NewPSTPQ

      next: PSTPQEnt ← PSTPQ[1];

      hole: [1..pqLim) ← 1;
      nextHole: [0..2*pqLim);
      FOR nextHole ← 2, 2*hole WHILE nextHole<=pqSize DO
        IF nextHole<pqSize AND
          PSTPQ[nextHole+1].min<PSTPQ[nextHole].min THEN
          nextHole ← nextHole+1;
        PSTPQ[hole] ← PSTPQ[nextHole];
        hole ← nextHole;
        ENDLOOP;

      WITH f: next.p SELECT FROM
        nil => FillHole[];
        leaf =>
          BEGIN OPEN f;
          IF next.min<yMax AND yMin<Max[item] THEN
            touch[item
              ! UNWIND => recursionDepth ← recursionDepth-1];
          FillHole[];
          END;
        internal =>
          BEGIN OPEN InlineDefs;
          son: PSTBinNodePtr ← f.pst;
          IF next.min<=yMax THEN
            -- interval could touch this subtree
            BEGIN OPEN son;
            IF next.min<yMax AND yMin<Max[vMin] THEN
              touch[vMin
                ! UNWIND => recursionDepth ← recursionDepth-1];
            PSTPQ[hole] ← MakePSTPQEnt[@halves[upper]];
            RippleUp[];
            WITH fLower: halves[lower] SELECT FROM
              nil => NULL;
              leaf => NewPSTPQ[@fLower];
              internal =>
                IF NOT Above[yMin, fLower.pst] THEN
                  -- interval could touch lower subtree as well
                  NewPSTPQ[@fLower];
              ENDCASE;
            END
          ELSE FillHole[];
          END;
        ENDCASE;
      ENDLOOP;
    recursionDepth ← recursionDepth-1;
    END; -- of SearchFeaturePST

  ClassifyFeaturePSTInterval: PUBLIC PROCEDURE[p: PSTHandle,
    int: Interval,
    covered: PROCEDURE[int: Interval, repItem: Item],
    gap: PROCEDURE[int: Interval]] =
    BEGIN OPEN MiscDefs;

    DoGap: PROCEDURE[int: Interval] = INLINE
      {IF gap#NullFeatureGap THEN gap[int]};

    DoCovered: PROCEDURE[int: Interval, repItem: Item] = INLINE
      {IF covered#NullFeatureCovered THEN covered[int, repItem]};

    Classify: PROCEDURE[pi: PSTNodePtr, w: Interval]
      RETURNS[r: Interval, minItem, maxItem: Item ← nullItem] =
      BEGIN

        -- The window w is not empty, so w.min<w.max.

        -- w is always surrounded by a pair of
        -- covered intervals. Classify is responsible for
        -- finding the largest sub-interval r of w which does not
        -- intersect a cover contained in the subtree pi
        -- containing either endpoint of v. Thus
        -- if r is non-empty, it starts and ends with a gap.
        -- If r is empty, then r.min>w.min and r.max<w.max.

        -- Classify is
        -- also responsible for calling "covered" and "gap" on
        -- a collection of sub-intervals of w that exhaust r,
        -- and for providing two Items, minItem and maxItem,
        -- that are part of the covers of w.min and w.max,
        -- respectively, if these points are not part of r.

      item, item1, item2, rtItem: Item;
      i, i2, w2, r2: Interval;
      lSon, rSon: PSTNodePtr;

      WITH f: pi SELECT FROM
        nil => 
          BEGIN
          DoGap[w];
          r ← w;
          RETURN
          END;

        leaf =>
          BEGIN
          item ← f.item;
          i ← [min: Min[item], max: Max[item]];
          IF i.max<=w.min OR w.max<=i.min THEN
            {DoGap[w]; r ← w; RETURN}; -- no coverage
          i2 ← [w.max, w.max]; -- empty above w
          lSon ← NIL;
          END; -- leaf

        internal =>
          BEGIN
          fint: PSTBinNodePtr ← f.pst;
          item ← fint.vMin;
          i ← [min: Min[item], max: Max[item]];
          IF w.max<=i.min OR Above[w.min, fint] THEN
            {DoGap[w]; r ← w; RETURN};
            -- no coverage possible
          lSon ← @fint.halves[lower];
          rSon ← @fint.halves[upper];

          WITH s: fint.halves[upper] SELECT FROM
            nil =>
              BEGIN
              i2 ← [w.max, w.max]; -- empty above w
              END;
            leaf =>
              BEGIN
              rtItem ← s.item;
              i2 ← [min: Min[rtItem],  max: Max[rtItem]];
              END;
            internal =>
              BEGIN
              rtItem ← s.pst.vMin;
              i2 ← [min: Min[rtItem],  max: Max[rtItem]];
              END;
            ENDCASE;
          END; -- internal
        ENDCASE;

      -- Here i.min<w.max.

      SELECT w.min FROM
        IN [i.min..i.max) => minItem ← item;
        IN [i2.min..i2.max) => minItem ← rtItem;
        ENDCASE => NULL;

      SELECT w.max FROM
        <=i.max => maxItem ← item; -- greatest element of w IN i
        IN (i2.min..i2.max] => maxItem ← rtItem;
          -- greatest element of w IN i2
        ENDCASE => NULL;

      -- If i and i2 touch, we can extend i and i2 to
      -- cover each other's upper endpoints
      -- without altering their joint coverage.
      IF i2.min<=i.max THEN -- i.min<=i2.min by PST invariant
        BEGIN
        i.max ← i2.max ← MAX[i.max, i2.max];
        END;

      IF -- i.max<= -- i2.max<=w.min THEN
        {[r, minItem, maxItem] ← Classify[rSon, w]; RETURN}
        -- w is beyond the effects of i, i2, and lSon
      ELSE IF -- i.max<= -- i2.max<w.max THEN
        BEGIN -- Classify the gap between i2 and the top of w
        w2 ← [min: i2.max, max: w.max]; -- w.min<i2.max
        [r2, , maxItem] ← Classify[rSon, w2];
        IF r2.max<=r2.min THEN -- rSon covers w2
          i2.max ← w.max
        ELSE
          {i2.max ← r2.min; r.max ← r2.max};
        END;

      -- We have now reported [i2.max..w.max) clipped to w.

      IF i.max<i2.min THEN
        BEGIN -- i<<i2
        IF w.min<i2.min AND i.max<w.max THEN
          BEGIN -- at least some of the gap between i and i2 is in w
          w2 ← [min: MAX[w.min, i.max],
            max: MIN[w.max, i2.min]];
          IF lSon#NIL THEN
            [r2, item1, item2] ← Classify[lSon, w2]
          ELSE {DoGap[w2]; r2 ← w2};
          IF w2.min=w.min THEN
            BEGIN
            r.min ← r2.min;
            IF w2.min<r2.min THEN minItem ← item1;
            END;
          IF w.max=w2.max THEN
            BEGIN
            r.max ← r2.max;
            IF r2.max<w2.max THEN maxItem ← item2;
            END;
          IF r2.max<=r2.min THEN
            i.max ← i2.max -- extend i to cover i2
          ELSE {i.max ← r2.min; i2.min ← r2.max};

          -- If i<<i2 still, and i2 is properly contained in w, then
          -- we must call covered on i2.
          IF i.max<i2.min AND i2.max<w.max THEN
            DoCovered[i2, rtItem];
          END;
        END
      ELSE i.max ← i2.max; -- i2 may have grown in the step above

      -- We have now reported [i.max..w.max) clipped to w.

      IF w.min<i.min THEN
        BEGIN
        r.min ← w.min;
        DoGap[[w.min, i.min]];
        IF i.max<w.max THEN DoCovered[i,item]
        END;

      -- We have now explained all of w.
      -- We must now compute r in
      -- those cases where i or i2 contain the endpoints of w.

      SELECT w.min FROM
        IN [i.min..i.max) => r.min ← MIN[w.max, i.max];
        IN [i2.min..i2.max) => r.min ← MIN[w.max, i2.max];
        ENDCASE => NULL;

      SELECT w.max FROM
        <=i.max => r.max ← MAX[w.min, i.min];
          -- greatest element of w IN i
        IN (i2.min..i2.max] => r.max ← MAX[w.min, i2.min];
          -- greatest element of w IN i2
        ENDCASE => NULL;

      END; -- of Classify

    pi: PSTNodePtr ← p.root;
    IF int.min<int.max THEN
      BEGIN -- test interval is non-empty
      r: Interval;
      minItem, maxItem: Item;

      [r, minItem, maxItem] ← Classify[pi, int];

      IF r.max<=r.min THEN
        DoCovered[int, minItem] -- the whole interval is covered
      ELSE
        BEGIN
        IF int.min<r.min THEN
          DoCovered[[int.min, r.min], minItem];
           -- a prefix is covered
        IF r.max<int.max THEN
          DoCovered[[r.max, int.max], maxItem];
           -- a suffix is covered
        END;

      END;
    END; -- of ClassifyPSTInterval


  NullFeatureCovered: PUBLIC PROCEDURE[int: Interval,
    repItem: Item] =
    {NULL};

  NullFeatureGap: PUBLIC PROCEDURE[int: Interval] =
    {NULL};



  prefixTable: PrefixTable;
  pt: PrefixTablePtr ← @prefixTable;

  InitPrefixTable: PROCEDURE[] =
    BEGIN
    i: [0..8] ← 0;
    k: [0..256) ← 255;
    FOR j: [0..256) ← 128, j/2 WHILE j>0 DO
      FOR k ← k, k-1 WHILE j<=k DO
        prefixTable[k] ← i;
        ENDLOOP;
      i ← i+1;
      ENDLOOP;
    prefixTable[0] ← 8; -- nobody should use this entry...
    END;

  InitPrefixTable[];
  END. -- of FeaturePSTImpl