-- File: HistCommands.mesa   last edited by
-- Sandman on September 4, 1980  10:15 AM
-- Karlton on Jun 23, 1980 3:17 PM

DIRECTORY
  Inline USING [BITSHIFT, COPY, LongNumber],
  MsgSW USING [Post],
  PerfCommonOps USING [logSW, msgSW, Number, WriteLongNumber],
  PerfOps USING [
    GetBase, GetBuckets, GetHistBase, GetHistLeg, GetHistNode, GetLegTable,
    GetNodeTable, GetPCR, GetScale, histClass, histType, PutMessage],
  PerfPrivate USING [
    HistBase, HistIndex, Histogram, HistType, LegTab, MaxLegs, MaxNodes, NodeTab,
    NullHist, NullNode, Number, PCR],
  Put USING [CR, Decimal, Line, Number, Text],
  String USING [AppendChar, AppendDecimal],
  UserInput USING [ResetUserAbort, userAbort],
  Window USING [Handle];

HistCommands: PROGRAM
  IMPORTS Inline, PerfOps, UserInput, String, Put, MsgSW, PerfCommonOps
  EXPORTS PerfOps =PUBLIC

  BEGIN OPEN PerfCommonOps, PerfOps, PerfPrivate;

  handle: Window.Handle ← PerfCommonOps.logSW;

  AddHistogram: PUBLIC PROCEDURE =
    BEGIN
    hIndex: HistIndex;
    hist: POINTER TO Histogram;
    index: CARDINAL;
    size, scale, limit: CARDINAL;
    type: HistType ← PerfOps.histType;
    histBase: HistBase;
    nodeTab: POINTER TO NodeTab ← GetNodeTable[read];
    legTab: POINTER TO LegTab ← GetLegTable[read];
    pCR: PCR ← GetPCR[read];
    SELECT type FROM
      node =>
	BEGIN
	index ← GetHistNode[];
	IF index ~IN [0..pCR.nextNode) THEN GOTO invalidNode;
	IF (hIndex ← nodeTab[index].hist) # NullHist THEN
	  BEGIN PutHistError[alreadyOne]; DeleteHist[hIndex]; END;
	END;
      leg =>
	BEGIN
	index ← GetHistLeg[];
	IF index ~IN [0..pCR.nextLeg) THEN GOTO invalidLeg;
	IF (hIndex ← legTab[index].hist) # NullHist THEN
	  BEGIN PutHistError[alreadyOne]; DeleteHist[hIndex]; END;
	END;
      ENDCASE => ERROR;
    limit ← FindAvailableSpace[pCR.histFree];
    IF histClass = log THEN
      limit ← IF type = node THEN MIN[16, limit] ELSE MIN[32, limit];
    size ← CheckBuckets[limit ! Confused => GOTO return];
    scale ← CheckScale[type ! Confused => GOTO return];
    hIndex ← GrabHistogramSlot[size];
    Put.Text[handle, "Added Histogram for "L];
    IF histType = node THEN
      BEGIN
      [] ← GetNodeTable[write];
      nodeTab[index].hist ← hIndex;
      Put.Text[handle, "Node "L];
      END
    ELSE
      BEGIN
      [] ← GetLegTable[write];
      legTab[index].hist ← hIndex;
      Put.Text[handle, "Leg "L];
      END;
    Put.Decimal[handle, index];
    Put.CR[handle];
    histBase ← GetHistBase[write];
    hist ← @histBase[hIndex];
    Zero[hist, SIZE[Histogram] + size];
    hist.scale ← scale;
    hist.type ← type;
    hist.class ← histClass;
    hist.nBuckets ← size;
    hist.base ← GetBase[];
    EXITS
      return => NULL;
      invalidNode => PutHistError[invalidNode];
      invalidLeg => PutHistError[invalidLeg];
    END;

  Zero: PROC [p: POINTER, l: CARDINAL] = {
    IF l # 0 THEN {p↑ ← 0; Inline.COPY[from: p, to: p + 1, nwords: l - 1]}};

  ValidateIndex: PROCEDURE [type: HistType, index: CARDINAL, pCR: PCR]
    RETURNS [hIndex: HistIndex] =
    BEGIN
    SELECT type FROM
      node =>
	BEGIN
	nodeTab: POINTER TO NodeTab ← GetNodeTable[read];
	IF index ~IN [0..pCR.nextNode) THEN SIGNAL Invalid;
	IF (hIndex ← nodeTab[index].hist) = NullHist THEN SIGNAL Confused;
	END;
      leg =>
	BEGIN
	legTab: POINTER TO LegTab ← GetLegTable[read];
	IF index ~IN [0..pCR.nextLeg) THEN SIGNAL Invalid;
	IF (hIndex ← legTab[index].hist) = NullHist THEN SIGNAL Confused;
	END;
      ENDCASE;
    RETURN
    END;

  Confused: SIGNAL = CODE;
  Invalid: SIGNAL = CODE;

  DeleteHistogram: PROCEDURE =
    BEGIN
    hIndex: HistIndex;
    type: HistType ← histType;
    index: CARDINAL;
    nodeTab: POINTER TO NodeTab ← GetNodeTable[read];
    legTab: POINTER TO LegTab ← GetLegTable[read];
    pCR: PCR ← GetPCR[read];
    index ← IF type = node THEN GetHistNode[] ELSE GetHistLeg[];
    hIndex ← ValidateIndex[
      type, index, pCR !
      Invalid => IF type = node THEN GOTO invalidNode ELSE GOTO invalidLeg;
      Confused => GOTO return];
    Put.Text[handle, "Deleted Histogram for "L];
    SELECT type FROM
      node =>
	BEGIN
	[] ← GetNodeTable[write];
	DeleteHist[hIndex];
	nodeTab[index].hist ← NullHist;
	Put.Text[handle, "Node "L];
	END;
      leg =>
	BEGIN
	[] ← GetLegTable[write];
	DeleteHist[hIndex];
	legTab[index].hist ← NullHist;
	Put.Text[handle, "Leg "L];
	END;
      ENDCASE;
    Put.Decimal[handle, index];
    Put.CR[handle];
    EXITS
      invalidNode => PutHistError[invalidNode];
      invalidLeg => PutHistError[invalidLeg];
      return => PutHistError[none];
    END;

  DeleteHist: PROCEDURE [hIndex: HistIndex] =
    BEGIN
    histBase: HistBase ← GetHistBase[read];
    nodeTab: POINTER TO NodeTab ← GetNodeTable[read];
    legTab: POINTER TO LegTab ← GetLegTable[read];
    pCR: PCR ← GetPCR[write];
    count, i: CARDINAL;
    from, to: HistIndex;
    to ← hIndex;
    from ← hIndex + SIZE[Histogram] + histBase[hIndex].nBuckets;
    UNTIL from = pCR.histFree DO
      FOR i IN [0..MaxNodes) DO
	IF nodeTab[i].hist = from THEN
	  BEGIN [] ← GetNodeTable[write]; nodeTab[i].hist ← to END;
	ENDLOOP;
      FOR i IN [0..MaxLegs) DO
	IF legTab[i].hist = from THEN
	  BEGIN [] ← GetLegTable[write]; legTab[i].hist ← to END;
	ENDLOOP;
      [] ← GetHistBase[write];
      count ← SIZE[Histogram] + histBase[from].nBuckets;
      Inline.COPY[from: @histBase[from], to: @histBase[to], nwords: count];
      from ← from + count;
      to ← to + SIZE[Histogram] + histBase[to].nBuckets;
      ENDLOOP;
    pCR.histFree ← to;
    END;

  PrintHistogram: PROCEDURE =
    BEGIN
    type: HistType ← PerfOps.histType;
    hIndex: HistIndex;
    index: CARDINAL ← IF type = node THEN GetHistNode[] ELSE GetHistLeg[];
    nodeTab: POINTER TO NodeTab ← GetNodeTable[read];
    legTab: POINTER TO LegTab ← GetLegTable[read];
    pCR: PCR ← GetPCR[read];
    Put.CR[handle];
    hIndex ← ValidateIndex[
      type, index, pCR !
      Invalid => IF type = node THEN GOTO invalidNode ELSE GOTO invalidLeg;
      Confused => GOTO return];
    ListHist[hIndex, index, type];
    EXITS
      invalidNode => PutHistError[invalidNode];
      invalidLeg => PutHistError[invalidLeg];
      return => PutHistError[none];
    END;

  ListHist: PROCEDURE [hIndex: HistIndex, index: CARDINAL, type: HistType] =
    BEGIN
    i: CARDINAL;
    histBase: HistBase ← GetHistBase[read];
    hist: POINTER TO Histogram ← @histBase[hIndex];
    Put.CR[handle];
    Put.Text[handle, "Histogram for "L];
    Put.Text[handle, IF type = node THEN "Node "L ELSE "Leg "L];
    Put.Decimal[handle, index];
    Put.CR[handle];
    Put.Text[handle, "Number of References  "L];
    WriteLongNumber[hist.count, 15];
    Put.CR[handle];
    Put.Text[handle, "Sum of Values         "L];
    WriteLongNumber[hist.sum, 15];
    Put.CR[handle];
    Put.Text[handle, "Average Value         "L];
    WriteLongNumber[IF hist.count = 0 THEN 0 ELSE hist.sum/hist.count, 15];
    Put.CR[handle];
    Put.Text[handle, "Scale Factor"L];
    Put.Text[handle, IF type = leg THEN " (2↑n)"L ELSE "      "L];
    Put.Text[handle, "    "L];
    WriteLongNumber[hist.scale, 15];
    Put.CR[handle];
    IF hist.class = linear THEN
      BEGIN
      Put.Text[handle, "Base                  "L];
      WriteLongNumber[hist.base, 15];
      Put.CR[handle];
      END;
    Put.Line[handle, "     Value       Count"L];
    Put.Line[handle, " -------------- -------"L];
    IF hist.class = linear AND hist.base # 0 THEN
      BEGIN
      Put.Text[handle, "      Underflow"L];
      WriteLongNumber[hist.underflow, 8];
      Put.CR[handle];
      END;
    FOR i IN [0..hist.nBuckets) DO
      IF UserInput.userAbort THEN
	BEGIN PutMessage[aborted]; UserInput.ResetUserAbort[]; RETURN END;
      WriteLongNumber[ScaleBucket[i, hist], 15];
      WriteLongNumber[hist.buckets[i], 8];
      Put.CR[handle];
      ENDLOOP;
    Put.Text[handle, "       Overflow"L];
    WriteLongNumber[hist.overflow, 8];
    Put.CR[handle];
    END;

  ScaleBucket: PROCEDURE [bucket: CARDINAL, hist: POINTER TO Histogram]
    RETURNS [val: Number] =
    BEGIN
    SELECT hist.type FROM
      node =>
	SELECT hist.class FROM
	  linear => val ← LONG[bucket]*hist.scale;
	  ENDCASE => val ← TimesTwoToN[hist.scale, bucket];
      leg =>
	SELECT hist.class FROM
	  linear => val ← TimesTwoToN[bucket, hist.scale];
	  ENDCASE => val ← TimesTwoToN[1, bucket*(hist.scale + 1)];
      ENDCASE;
    IF hist.class = linear THEN val ← val + hist.base;
    END;

  TimesTwoToN: PROCEDURE [z: Number, n: CARDINAL] RETURNS [Number] =
    BEGIN OPEN Inline;
    y, x: LongNumber;
    x.lc ← z;
    IF n = 0 THEN RETURN[z];
    IF n > 31 THEN RETURN[0];
    IF n > 15 THEN
      BEGIN
      y.lowbits ← 0;
      y.highbits ← BITSHIFT[x.lowbits, n - 16];
      RETURN[y.lc]
      END;
    y.lowbits ← BITSHIFT[x.lowbits, n];
    y.highbits ← BITSHIFT[x.lowbits, n - 16] + BITSHIFT[x.highbits, n];
    RETURN[y.lc]
    END;

  LegLegal: PROCEDURE [index: CARDINAL] RETURNS [BOOLEAN] =
    BEGIN
    legTab: POINTER TO LegTab ← GetLegTable[read];
    pCR: PCR ← GetPCR[read];
    RETURN[index IN [0..pCR.nextLeg) AND legTab[index].from # NullNode];
    END;

  FindAvailableSpace: PROCEDURE [free: HistIndex] RETURNS [limit: CARDINAL] =
    BEGIN
    limit ← NullHist - free;
    RETURN[IF limit > SIZE[Histogram] THEN limit - SIZE[Histogram] ELSE 0];
    END;

  GrabHistogramSlot: PROCEDURE [size: CARDINAL] RETURNS [hist: HistIndex] =
    BEGIN
    pCR: PCR ← GetPCR[write];
    hist ← pCR.histFree;
    pCR.histFree ← hist + size + SIZE[Histogram];
    RETURN
    END;

  CheckScale: PROCEDURE [type: HistType] RETURNS [n: CARDINAL] =
    BEGIN
    n ← GetScale[];
    SELECT TRUE FROM
      type = leg AND n IN [0..31] => RETURN;
      type = node AND n # 0 => RETURN;
      ENDCASE;
    MsgSW.Post[
      sw: PerfCommonOps.msgSW, string: "Scale must be"L, endOfMsg: FALSE];
    MsgSW.Post[
      PerfCommonOps.msgSW,
      IF type = leg THEN " (2↑n) [0..31]"L ELSE " [1..65,535]"L];
    ERROR Confused;
    END;

  CheckBuckets: PROCEDURE [top: CARDINAL] RETURNS [n: CARDINAL] =
    BEGIN OPEN String;
    s: STRING ← [5];
    n ← GetBuckets[];
    IF n IN [1..top] THEN RETURN;
    MsgSW.Post[
      sw: PerfCommonOps.msgSW, string: "Number of Buckets must be [1.."L,
      endOfMsg: FALSE];
    AppendDecimal[s, LOOPHOLE[top]];
    AppendChar[s, ']];
    MsgSW.Post[PerfCommonOps.msgSW, s];
    ERROR Confused;
    END;

  HistError: TYPE = {invalidLeg, invalidNode, alreadyOne, none};

  PutHistError: PROCEDURE [message: HistError] =
    BEGIN
    MsgSW.Post[
      PerfCommonOps.msgSW,
      SELECT message FROM
	invalidLeg => "!Invalid Leg"L,
	invalidNode => "!Invalid Node"L,
	alreadyOne => "Old Histogram deleted!"L,
	none => "No Histogram exists!"L,
	ENDCASE => "?"L];
    RETURN
    END;

  END.