IMPLEMENTATION MODULE Profile;

FROM Comparable IMPORT Comparable;
FROM Modules IMPORT Module, ModuleDictionary;
FROM ArrayList IMPORT ArrayList;
FROM Streams IMPORT OutputStream, StdOut;
IMPORT String;
IMPORT SYSTEM;

INLINE
  % extern int yafl_profile;
  END;

  ONCE CLASS Profile;

    REDEFINE METHOD CREATE;
      BEGIN
      BASE;
      END CREATE;

    METHOD StartProfile;
      BEGIN
      ResetProfile;
      ResumeProfile;
      END StartProfile;

    METHOD ResetProfile;
      BEGIN
      ModuleDictionary.PrepareMaps;
      END ResetProfile;

    METHOD StopProfile;
      BEGIN
      INLINE
        % yafl_profile = 0;
        END;
      END StopProfile;

    METHOD ResumeProfile;
      BEGIN
      INLINE
        % yafl_profile = 1;
        END;
      END ResumeProfile;

    METHOD ModuleReport (ModuleName: ARRAY OF CHAR);
      BEGIN
      END ModuleReport;

    METHOD AllModulesReport;
      BEGIN
      END AllModulesReport;

    METHOD FrequencyReport (OutputFName: ARRAY OF CHAR;
                            HeadSize,
                            Threshold: INTEGER);
      VAR
        LineVisitList: ArrayList(Visit);      
        ModVisitList: ArrayList(Visit);      
        k, VisitCount, 
        Minimum, Total: INTEGER;
        Curr, Prev, TheVisit: Visit;
        Output: OutputStream;
        LMap: ARRAY OF INTEGER;
      BEGIN
      ModVisitList.CREATE;
      Minimum := Threshold;
      FOR m IN ModuleDictionary DO
        k := 0;
        FOR x IN m.LineMap DO
          k := k + x;
          END;
	 IF k > 0 THEN
          TheVisit.CREATE (m, 0, k);
          ModVisitList.Append (TheVisit);
          Total := Total + k;
          END;
        END;
      ModVisitList.Sort;
      Output.CREATE;
      Output.Create (OutputFName, Output.WriteAccess);
      Output.WriteLine ("Module report");
      Output.WriteLine ("-------------");
      FOR i := ModVisitList.Size - 1 TO 0 BY -1 DO
        ModVisitList.Get(i).Report (Output, Total);
        END;
      Output.WriteString (" Total: ");
      Output.WriteInt (Total, 12);
      Output.WriteLn;
      Output.WriteLn;
      Total := 0;
      Output.WriteLine ("Detailed report");
      Output.WriteLine ("---------------");
      LineVisitList.CREATE;
      Minimum := Threshold;
      FOR Mod IN ModuleDictionary DO
        LMap := Mod.LineMap;
        IF LMap <> VOID THEN
          FOR j := 0 TO LMap.SIZE - 1 DO
            VisitCount := LMap[j];
            Total := Total + VisitCount;
            IF VisitCount > Minimum THEN
              TheVisit.CREATE (Mod, j, VisitCount);
              LineVisitList.Append (TheVisit);
              IF LineVisitList.Size > HeadSize THEN
                LineVisitList.Sort;
                LineVisitList.Delete(0);
                Minimum := LineVisitList.Get(0).VisitCount;
                END;
              END;
            END;
          END;
        END;
      LineVisitList.Sort;
      FOR i := LineVisitList.Size - 1 TO 1 BY - 1 DO
        Curr := LineVisitList.Get(i);
        Prev := LineVisitList.Get(i-1);
        IF Prev.Mergeable (Curr) THEN
          Prev.Merge (Curr);
          LineVisitList.Delete (i);
          END;
        END;
      LineVisitList.Sort;
      FOR i := LineVisitList.Size - 1 TO 0 BY -1 DO
        LineVisitList.Get(i).Report (Output, Total);
        END;
      Output.WriteString (" Total: ");
      Output.WriteInt (Total, 12);
      Output.WriteLn;
      Output.Close;
      END FrequencyReport;

  END Profile;
---------------------------------------------------------------
  CLASS Visit;
    INHERITS Comparable;

    VAR
      TheFromLineNr, 
      TheToLineNr,
      TheVisitCount: INTEGER;
      TheMod: Module;
      
    METHOD Mergeable (Other: Visit): BOOLEAN;
      BEGIN
      IF (Other.TheMod = TheMod) AND
         (Other.TheVisitCount = TheVisitCount) THEN
        RESULT := (TheToLineNr + 1 = Other.TheFromLineNr) OR
                  (TheFromLineNr = Other.TheToLineNr + 1);
        END;   
      END Mergeable;
      
    METHOD Merge (Other: Visit);
      BEGIN
      ASSERT Mergeable (Other);
      TheFromLineNr := SYSTEM.MIN (TheFromLineNr, Other.TheFromLineNr);
      TheToLineNr := SYSTEM.MAX (TheToLineNr, Other.TheToLineNr);
      END Merge;
      
    METHOD VisitCount: INTEGER;
      BEGIN
      RESULT := TheVisitCount * (TheToLineNr - TheFromLineNr + 1);
      END VisitCount;

    REDEFINE METHOD IsGreater (Other: Comparable): BOOLEAN;
      VAR
        Code: INTEGER;
      BEGIN
      WHAT Other OF
        IN Visit:
          IF TAG.VisitCount < VisitCount THEN
            RESULT := TRUE;
           ELSIF TAG.VisitCount = VisitCount THEN
            Code := String.Compare (TAG.TheMod.Name, TheMod.Name);
            IF Code < 0 THEN
              RESULT := TRUE;
             ELSIF Code = 0 THEN
              RESULT := TAG.TheFromLineNr > TheFromLineNr;
              END;
            END;
          END;
        END;
      END IsGreater;

    REDEFINE METHOD CREATE (Mod: Module;
                            LineNr,
                            Count: INTEGER);
      BEGIN
      BASE;
      TheMod := Mod;
      TheFromLineNr := LineNr;
      TheToLineNr := LineNr;
      TheVisitCount := Count;
      END CREATE;

    METHOD Report (Output: OutputStream;
                   Total: INTEGER);
      BEGIN
      Output.WriteInt (TheVisitCount, 10);
      IF Total <> 0 THEN
        Output.WriteString (" (");
        Output.WriteReal ((100.0 * SYSTEM.FLOAT(TheVisitCount)) / 
                                SYSTEM.FLOAT(Total), 5, 2);
        Output.WriteString ("%)");
       ELSE 
        Output.WriteString ("         ");
        END;                        
      IF TheFromLineNr > 0 THEN
        Output.WriteInt (TheFromLineNr, 6);
        IF TheToLineNr <> TheFromLineNr THEN
          Output.WriteString ('-');
          Output.WriteInt (TheToLineNr, 0);
          END;
        END;
      Output.WriteChar (' ');
      Output.WriteLine (TheMod.Name);
      END Report;

  END Visit;

END Profile;
