sortedtableextras/src/RedBlackTbl.mg


 Created on Tue Nov 18 17:23:12 PST 1997 by heydon       
 Last modified on Sat Nov 22 13:22:27 PST 1997 by heydon 
 Copyright (C) 1997, Digital Equipment Corporation       

GENERIC MODULE RedBlackTbl(Key, Value, Tbl, SortedTbl);

TYPE
  Color = { Red, Black };
  Node = REF RECORD
    k: Key.T;
    v: Value.T;
    color := Color.Red;
    p, l, r: Node := NIL; (* parent, left child, right child *)
  END;

REVEAL
  T = Public BRANDED Brand OBJECT
    nil: Node := NIL;   (* sentinal at leaves of tree *)
    root: Node;         (* tree root node *)
    num: CARDINAL := 0; (* number of elements in the table *)
  OVERRIDES
    keyCompare := KeyCompare;
    init := Init;
    size := Size;
    get := Get;
    put := Put;
    delete := Delete;
    iterate := Iterate;
    iterateOrdered := IterateOrdered;
  END;

CONST
  IterBrand = "(Iterator " & Brand & ")";

REVEAL
  Iterator = IteratorPublic BRANDED IterBrand OBJECT
    t: T;       (* corresponding tree *)
    curr: Node; (* current node in iteration *)
  END;

TYPE
  IteratorUp = Iterator OBJECT OVERRIDES
    reset := ResetUp;
    next := NextUp;
    seek := SeekUp;
  END;
  IteratorDown = Iterator OBJECT OVERRIDES
    reset := ResetDown;
    next := NextDown;
    seek := SeekDown;
  END;
Utility procedures ------------------------------------------------------

PROCEDURE Min(t: T; curr: Node): Node =
Return the minimum element of the tree rooted at curr. Requires curr # t.nil.
  VAR prev: Node; BEGIN
    <* ASSERT curr # t.nil *>
    REPEAT
      prev := curr;
      curr := curr.l
    UNTIL curr = t.nil;
    RETURN prev
  END Min;

PROCEDURE Max(t: T; curr: Node): Node =
Return the maximum element of the tree rooted at curr. Requires curr # t.nil.
  VAR prev: Node; BEGIN
    <* ASSERT curr # t.nil *>
    REPEAT
      prev := curr;
      curr := curr.r
    UNTIL curr = t.nil;
    RETURN prev
  END Max;

PROCEDURE Successor(t: T; n: Node): Node =
  VAR res: Node; BEGIN
    IF n.r # t.nil THEN
      res := Min(t, n.r)
    ELSE
      WHILE n.p # t.nil AND n.p.r = n DO
        n := n.p
      END;
      res := n.p
    END;
    RETURN res
  END Successor;

PROCEDURE Predecessor(t: T; n: Node): Node =
  VAR res: Node; BEGIN
    IF n.l # t.nil THEN
      res := Max(t, n.l)
    ELSE
      WHILE n.p # t.nil AND n.p.l = n DO
        n := n.p
      END;
      res := n.p
    END;
    RETURN res
  END Predecessor;

PROCEDURE LeftRotate(t: T; p, ch: Node) =
Requires that ch is the right child of p. Do a left-rotation about those two nodes.
  BEGIN
    <* ASSERT ch # t.nil AND ch = p.r *>
    (* make "p"'s right child "ch"'s left child *)
    p.r := ch.l;
    p.r.p := p;

    (* adjust "ch"'s parent *)
    ch.p := p.p;
    IF p.p = t.nil THEN
      (* "p" was the tree root; make "ch" the new root *)
      t.root := ch
    ELSE
      IF p = p.p.l
        THEN p.p.l := ch
        ELSE p.p.r := ch
      END
    END;

    (* make "p" "ch"'s new left child *)
    ch.l := p;
    p.p := ch;
  END LeftRotate;

PROCEDURE RightRotate(t: T; p, ch: Node) =
Requires that ch is the left child of p. Do a right-rotation about those two nodes.
  BEGIN
    <* ASSERT ch # t.nil AND ch = p.l *>
    (* make "p"'s left child "ch"'s right child *)
    p.l := ch.r;
    p.l.p := p;

    (* adjust "ch"'s parent *)
    ch.p := p.p;
    IF p.p = t.nil THEN
      (* "p" was the tree root; make "ch" the new root *)
      t.root := ch
    ELSE
      IF p = p.p.l
        THEN p.p.l := ch
        ELSE p.p.r := ch
      END
    END;

    (* make "p" "ch"'s new right child *)
    ch.r := p;
    p.p := ch;
  END RightRotate;
Tree method implementations ---------------------------------------------

PROCEDURE KeyCompare(<*UNUSED*> t: T; READONLY k1, k2: Key.T): [-1..1] =
  BEGIN RETURN Key.Compare(k1, k2) END KeyCompare;

PROCEDURE Init(t: T): T =
  BEGIN
    IF t.nil = NIL THEN
      (* initialization *)
      t.nil := NEW(Node, color := Color.Black)
    ELSE
      (* clear existing table *)
      t.num := 0
    END;
    t.root := t.nil;
    RETURN t
  END Init;

PROCEDURE Size(t: T): CARDINAL =
  BEGIN RETURN t.num END Size;

PROCEDURE Get(t: T; READONLY k: Key.T; VAR (*OUT*) v: Value.T): BOOLEAN =
  VAR curr := t.root; BEGIN
    WHILE curr # t.nil DO
      CASE t.keyCompare(k, curr.k) OF
      | -1 => curr := curr.l
      |  1 => curr := curr.r
      |  0 => v := curr.v; RETURN TRUE
      END
    END;
    RETURN FALSE
  END Get;

PROCEDURE Put(t: T; READONLY k: Key.T; READONLY v: Value.T): BOOLEAN =
  BEGIN
    IF t.root = t.nil THEN
      (* empty tree *)
      t.root := NEW(Node, k := k, v := v, p := t.nil,
        l := t.nil, r := t.nil, color := Color.Black);
    ELSE
      VAR prev: Node; cmp: [-1..1]; curr := t.root; BEGIN
        (* insert new element or return if "k" already in table *)
        REPEAT
          prev := curr;
          cmp := t.keyCompare(k, curr.k);
          CASE cmp OF
          | -1 => curr := curr.l
          |  1 => curr := curr.r
          |  0 => curr.v := v; RETURN TRUE
          END
        UNTIL curr = t.nil;
        curr := NEW(Node, k := k, v := v, color := Color.Red,
          p := prev, l := t.nil, r := t.nil);
        IF cmp < 0
          THEN prev.l := curr
          ELSE prev.r := curr
        END;

        (* rebalance if necessary *)
        WHILE prev # t.root AND prev.color = Color.Red DO
          (* Note: The root of the tree is always colored black,
             so "prev.p" is guaranteed to be non-NIL. *)
          <* ASSERT prev.p # t.nil *>
          IF prev = prev.p.l THEN
            (* "prev" is a left child *)
            VAR uncle := prev.p.r; BEGIN
              IF uncle.color = Color.Red THEN
                prev.color := Color.Black;
                uncle.color := Color.Black;
                curr := prev.p;
                prev := curr.p;
                curr.color := Color.Red;
              ELSE
                IF curr = prev.r THEN
                  LeftRotate(t, prev, curr);
                  curr := prev; prev := curr.p;
                END;
                prev.color := Color.Black;
                prev.p.color := Color.Red;
                RightRotate(t, prev.p, prev);
              END
            END
          ELSE
            (* "prev" is a right child *)
            VAR uncle := prev.p.l; BEGIN
              IF uncle.color = Color.Red THEN
                prev.color := Color.Black;
                uncle.color := Color.Black;
                curr := prev.p;
                prev := curr.p;
                curr.color := Color.Red;
              ELSE
                IF curr = prev.l THEN
                  RightRotate(t, prev, curr);
                  curr := prev; prev := curr.p;
                END;
                prev.color := Color.Black;
                prev.p.color := Color.Red;
                LeftRotate(t, prev.p, prev);
              END
            END
          END
        END
      END;
      t.root.color := Color.Black
    END;
    INC(t.num);
    RETURN FALSE;
  END Put;

PROCEDURE Delete(t: T; READONLY k: Key.T; VAR (*OUT*) v: Value.T): BOOLEAN =
  VAR curr := t.root; rep, repCh: Node; BEGIN
    (* find the node to delete (if any *)
    WHILE curr # t.nil DO
      CASE t.keyCompare(k, curr.k) OF
      | -1 => curr := curr.l
      |  1 => curr := curr.r
      |  0 => EXIT
      END
    END;
    IF curr = t.nil THEN RETURN FALSE END;

    (* locate replacement node and one of its children *)
    IF curr.l = t.nil OR curr.r = t.nil
      THEN rep := curr
      ELSE rep := Successor(t, curr)
    END;
    IF rep.l # t.nil
      THEN repCh := rep.l
      ELSE repCh := rep.r
    END;

    (* splice out "rep" node *)
    repCh.p := rep.p;
    IF rep.p = t.nil THEN
      t.root := repCh
    ELSE
      IF rep = rep.p.l
        THEN repCh.p.l := repCh
        ELSE repCh.p.r := repCh
      END
    END;

    (* save value of node to be deleted *)
    v := curr.v;

    (* copy "rep" fields into "curr" if they are different *)
    IF rep # curr THEN
      curr.k := rep.k;
      curr.v := rep.v
    END;

    (* rebalance tree if necessary *)
    IF rep.color = Color.Black THEN
      DeleteFixup(t, repCh)
    END;
    DEC(t.num);
    RETURN TRUE
  END Delete;

PROCEDURE DeleteFixup(t: T; ch: Node) =
  VAR p, sib: Node; BEGIN
    WHILE ch # t.root AND ch.color = Color.Black DO
      p := ch.p;
      IF ch = p.l THEN
        (* "ch" is the left child of "p" *)
        sib := p.r;
        <* ASSERT sib # t.nil *>
        IF sib.color = Color.Red THEN
          (* case 1 *)
          sib.color := Color.Black;
          p.color := Color.Red;
          LeftRotate(t, p, sib);
          sib := p.r
        END;
        <* ASSERT sib.color = Color.Black AND sib # t.nil *>
        IF sib.l.color = Color.Black AND sib.r.color = Color.Black THEN
          (* case 2 *)
          sib.color := Color.Red;
          ch := p
        ELSE
          IF sib.r.color = Color.Black THEN
            (* case 3 *)
            <* ASSERT sib.l.color = Color.Red *>
            sib.l.color := Color.Black;
            sib.color := Color.Red;
            RightRotate(t, sib, sib.l);
            sib := p.r
          END;
          <* ASSERT sib.r.color = Color.Red *>
          (* case 4 *)
          sib.color := p.color;
          p.color := Color.Black;
          sib.r.color := Color.Black;
          LeftRotate(t, p, sib);
          ch := t.root
        END
      ELSE
        (* "ch" is the right child of "p" *)
        sib := p.l;
        <* ASSERT sib # t.nil *>
        IF sib.color = Color.Red THEN
          (* case 1 *)
          sib.color := Color.Black;
          p.color := Color.Red;
          RightRotate(t, p, sib);
          sib := p.l
        END;
        <* ASSERT sib.color = Color.Black AND sib # t.nil *>
        IF sib.r.color = Color.Black AND sib.l.color = Color.Black THEN
          (* case 2 *)
          sib.color := Color.Red;
          ch := p
        ELSE
          IF sib.l.color = Color.Black THEN
            (* case 3 *)
            <* ASSERT sib.r.color = Color.Red *>
            sib.r.color := Color.Black;
            sib.color := Color.Red;
            LeftRotate(t, sib, sib.r);
            sib := p.l
          END;
          <* ASSERT sib.l.color = Color.Red *>
          (* case 4 *)
          sib.color := p.color;
          p.color := Color.Black;
          sib.l.color := Color.Black;
          RightRotate(t, p, sib);
          ch := t.root
        END
      END
    END;
    ch.color := Color.Black
  END DeleteFixup;

PROCEDURE Iterate(t: T): Tbl.Iterator =
  BEGIN RETURN IterateOrdered(t, TRUE) END Iterate;

PROCEDURE IterateOrdered(t: T; up: BOOLEAN): SortedTbl.Iterator =
  VAR res: Iterator; BEGIN
    IF up
      THEN res := NEW(IteratorUp);
      ELSE res := NEW(IteratorDown);
    END;
    res.t := t;
    res.reset();
    RETURN res
  END IterateOrdered;
Iterator method implementations ----------------------------------------

PROCEDURE ResetUp(it: Iterator) =
  VAR t := it.t; BEGIN
    IF t.root = t.nil
      THEN it.curr := t.nil
      ELSE it.curr := Min(t, t.root)
    END
  END ResetUp;

PROCEDURE ResetDown(it: Iterator) =
  VAR t := it.t; BEGIN
    IF t.root = t.nil
      THEN it.curr := t.nil
      ELSE it.curr := Max(t, t.root)
    END
  END ResetDown;

PROCEDURE NextUp(it: Iterator; VAR (*OUT*) k: Key.T; VAR (*OUT*) v: Value.T):
    BOOLEAN =
  VAR curr := it.curr; BEGIN
    (* handle empty iterator *)
    IF curr = it.t.nil THEN RETURN FALSE END;

    (* save key and value in current node *)
    k := curr.k; v := curr.v;

    (* advance "it.curr" to next node in order *)
    it.curr := Successor(it.t, curr);
    RETURN TRUE
  END NextUp;

PROCEDURE NextDown(it: Iterator; VAR (*OUT*) k: Key.T; VAR (*OUT*) v: Value.T):
    BOOLEAN =
  VAR curr := it.curr; BEGIN
    (* handle empty iterator *)
    IF curr = it.t.nil THEN RETURN FALSE END;

    (* save key and value in current node *)
    k := curr.k; v := curr.v;

    (* advance "it.curr" to next node in order *)
    it.curr := Predecessor(it.t, curr);
    RETURN TRUE
  END NextDown;

PROCEDURE SeekUp(it: Iterator; READONLY key: Key.T) =
  VAR t := it.t; curr := t.root; prev, anc := t.nil; cmp: [-1..1] := -1; BEGIN
    WHILE curr # t.nil DO
      prev := curr;
      cmp := t.keyCompare(key, curr.k);
      CASE cmp OF
      | -1 => anc := curr; curr := curr.l
      |  1 => curr := curr.r
      |  0 => it.curr := curr; RETURN
      END
    END;
    IF cmp < 0
      THEN it.curr := prev
      ELSE it.curr := anc
    END
  END SeekUp;

PROCEDURE SeekDown(it: Iterator; READONLY key: Key.T) =
  VAR t := it.t; curr := t.root; prev, anc := t.nil; cmp: [-1..1] := 1; BEGIN
    WHILE curr # t.nil DO
      prev := curr;
      cmp := t.keyCompare(key, curr.k);
      CASE cmp OF
      | -1 => curr := curr.l
      |  1 => anc := curr; curr := curr.r
      |  0 => it.curr := curr; RETURN
      END
    END;
    IF cmp > 0
      THEN it.curr := prev
      ELSE it.curr := anc
    END
  END SeekDown;

BEGIN
END RedBlackTbl.