Text processing/Max licenses in use

From Rosetta Code
Task
Text processing/Max licenses in use
You are encouraged to solve this task according to the task description, using any language you may know.

A company currently pays a fixed sum for the use of a particular licensed software package.   In determining if it has a good deal it decides to calculate its maximum use of the software from its license management log file.

Assume the software's licensing daemon faithfully records a checkout event when a copy of the software starts and a checkin event when the software finishes to its log file.

An example of checkout and checkin events are:

 License OUT @ 2008/10/03_23:51:05 for job 4974
 ...
 License IN  @ 2008/10/04_00:18:22 for job 4974


Task

Save the 10,000 line log file from   here   into a local file, then write a program to scan the file extracting both the maximum licenses that were out at any time, and the time(s) at which this occurs.

Mirror of log file available as a zip here (offsite mirror).

11l

Translation of: Python
V out = 0
V max_out = -1
[String] max_times
L(job) File(‘mlijobs.txt’).read_lines()
   out += I ‘OUT’ C job {1} E -1
   I out > max_out
      max_out = out
      max_times.clear()
   I out == max_out
      max_times.append(job.split(‘ ’)[3])

print(‘Maximum simultaneous license use is #. at the following times:’.format(max_out))
print(‘  ’max_times.join("\n  "))
Output:
Maximum simultaneous license use is 99 at the following times:
  2008/10/03_08:39:34
  2008/10/03_08:40:40

Ada

-- licenselist.adb --
-- run under GPS 4.3-5 (Sidux/Debian)
-- process rosetta.org text_processing/3 example
-- uses linked-list to hold times
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Strings.Unbounded,
     Ada.Strings.Unbounded.Text_IO,
     Ada.Containers.Doubly_Linked_Lists;
use Ada.Text_IO, Ada.Integer_Text_IO,
    Ada.Strings.Unbounded, Ada.Strings.Unbounded.Text_IO,
    Ada.Containers;

procedure licenselist is

 type logrec is record  -- define a record 'logrec' to place in a list
    logtext : String(1..19);
 end record;

 package dblist is new Doubly_Linked_Lists(logrec);
 use dblist;
       -- declare dblist as a list of logrec's
 licenselog : list;
 logtime    : logrec;
  -- to record the time of max OUT licenses

 infile : File_Type;           -- file handle
 str    : Unbounded_String;        -- input string buffer of unknown length
 outcnt, maxoutcnt : integer := 0;
 infilename : string := "license.log";

  procedure trace_times  is
  -- loop thru times list and print
     pntr : cursor := licenselog.first;
  -- pntr is of system type cursor reference to local list 'licenselog'
  begin
       new_line;
       while has_element(pntr) loop
        put(element(pntr).logtext); new_line;
        next(pntr);
       end loop;
  end trace_times;

begin -- main program --
   open ( infile,
         mode=> in_file,
         name=> infilename );

  loop
    exit when End_of_file ( infile );
    str := get_line( infile );
    if index( str, "OUT" ) > 0 then -- test if OUT record
      outcnt := outcnt +1;
    else                            -- else assume IN record
      outcnt := outcnt -1;
    end if;
    if outcnt > maxoutcnt then
         maxoutcnt := outcnt;
     logtime.logtext := slice(str,15,33); -- date_time field
         licenselog.clear;             -- reset list for new time(s)
         licenselog.append (logtime);  -- put current time into list
    elsif outcnt = maxoutcnt then
         logtime.logtext := slice(str,15,33);  -- date_time field
         licenselog.append (logtime);   -- add current time into list
    end if;  -- have to account for possibility of equal number of OUT's
  end loop;
   put("The max. number of licenses OUT is ");put(maxoutcnt,5); new_line;
   put(" at these times ");

  trace_times;
  close ( infile );
end licenselist;

Output:

The max. number of licenses out is    99
 at these times 
2008/10/03_08:39:34
2008/10/03_08:40:40
[2010-06-06 01:06:07] process terminated successfully (elapsed time: 00.25s)

ALGOL 68

Translation of: C

note: This specimen retains the original C coding style.

Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny
PROC report = (REF FILE file in)INT: (

  MODE TIME = [19]CHAR;
  STRUCT ([3]CHAR inout, TIME time, INT jobnum) record;
  FORMAT record fmt = $"License "g" @ "g" for job "g(0)l$;

  FLEX[1]TIME max time;

  INT lic out := 0, max out := LWB max time-1, max count := LWB max time-1;
  BOOL file in ended := FALSE;
  on logical file end(file in, (REF FILE file in)BOOL: file in ended := TRUE);
  WHILE
    getf(file in, (record fmt, record));
# WHILE # NOT file in ended DO
    IF inout OF record = "OUT" THEN
      lic out +:= 1
    ELIF lic out > 0 THEN # incase license already "OUT" #
      lic out -:= 1
    FI;

    IF lic out > max out THEN
      max out := lic out;
      max count := LWB max time-1
    FI;
    IF lic out = max out THEN
      IF max count = UPB max time THEN
        [UPB max time*2]TIME new max time;
        new max time[:UPB max time] := max time;
        max time := new max time
        # ;putf(stand error, ($"increasing UPB max time (now it is "g(0)")"l$, UPB max time)); #
      FI;
      max time[max count +:= 1] := time OF record
    FI
  OD;

  printf(($"Maximum simultaneous license use is "g(0)" at the following times:"l$, max out));
  FOR lic out FROM LWB max time TO max count DO
    printf(($gl$, max time[lic out]))
  OD;

  0 EXIT
  exit report error: errno
);

INT errno;

COMMENT
  Usage:
    a68g Text_processing_3.a68 --exit Text_processing_3.dat
    a68g Text_processing_3.a68 < Text_processing_3.dat
END COMMENT

main:
(
  INT argv1 := 4;
  IF argc >= argv1 THEN
    FOR i FROM argv1 TO argc DO
      FILE file in;
      errno := open(file in, argv(i), stand in channel);
      IF errno /= 0 THEN
        putf(stand error, ($"cannot read "gl$, argv(1)));
        exit main error
      ELSE
        report(file in)
      FI;
      close(file in)
    OD
  ELSE
    report(stand in)
  FI;
  exit main error: SKIP
)

Output:

Maximum simultaneous license use is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

APL

Works with: APL2
Translation of: J
⍝  Copy/paste file's contents into TXT (easiest), or TXT ← ⎕NREAD
       I    TXT[;8+⎕IO]
       D    TXT[;⎕IO+14+⍳19]
       lu   +\ ¯1 * 'OI'  I
       mx   (⎕IO+⍳⍴lu)/⍨lu= max  / lu
           'Maximum simultaneous license use is ' , ' at the following times:' , max  D[mx;]
Maximum simultaneous license use is 99 at the following times:
 2008/10/03_08:39:34
 2008/10/03_08:40:40

AutoHotkey

Translation of: Python
IfNotExist, mlijobs.txt
  UrlDownloadToFile, http://rosettacode.org/mlijobs.txt, mlijobs.txt

out := 0, max_out := -1, max_times := ""

Loop, Read, mlijobs.txt
{
  If InStr(A_LoopReadLine, "OUT")
    out++
  Else
    out--
  If (out > max_out)
    max_out := out, max_times := ""
  If (out = max_out)
  {
    StringSplit, lineArr, A_LoopReadLine, %A_Space%
    max_times .= lineArr4 . "`n"
  }
}

MsgBox Maximum use is %max_out% at:`n`n%max_times%
Maximum use is 99 at:

2008/10/03_08:39:34
2008/10/03_08:40:40

AWK

to be called with awk -f licenses.awk ./mlijobs.txt

$2=="OUT" { 
    count = count + 1 
    time = $4 
    if ( count > maxcount ) {
        maxcount = count 
        maxtimes = time
    } else { 
        if ( count == maxcount ) {
            maxtimes = maxtimes " and " time 
        }
    }
}
$2=="IN" { count = count - 1 }
END {print "The biggest number of licenses is " maxcount " at " maxtimes " !"}

Sample output

The biggest number of licenses is 99 at 2008/10/03_08:39:34 and 2008/10/03_08:40:40 !

On a 2.53MHz machine, these timings were obtained using GNU Awk 4.0.2:

user   0m0.015s
sys    0m0.008s

BBC BASIC

      max% = 0
      nlicence% = 0
      file% = OPENIN("mlijobs.txt")
      
      WHILE NOT EOF#file%
        a$ = GET$#file%
        stamp$ = MID$(a$, 15, 19)
        IF INSTR(a$, "OUT") THEN
          nlicence% += 1
          IF nlicence% > max% THEN
            max% = nlicence%
            start$ = stamp$
          ENDIF
        ENDIF
        IF INSTR(a$, "IN") THEN
          IF nlicence% = max% THEN
            finish$ = previous$
          ENDIF
          nlicence% -= 1
        ENDIF
        previous$ = stamp$
      ENDWHILE
      CLOSE #file%
      
      PRINT "Maximum licences checked out = " ; max%
      PRINT "From " start$ " to " finish$
      END

Output:

Maximum licences checked out = 99
From 2008/10/03_08:39:34 to 2008/10/03_08:40:40

Bracmat

(   0:?N:?n
  & :?Ts
  & @( get$("mlijobs.txt",STR)
     :   ?
         ( "e " ?OI " @ " ?T " " ?
         &   (   !OI:OUT
               &   !n+1
                 : ( >!N:?N&!T:?Ts
                   | !N&!Ts !T:?Ts
                   | ?
                   )
             | !n+-1
             )
           : ?n
         & ~
         )
     )
| out$(!N !Ts)
);

Output:

99 2008/10/03_08:39:34 2008/10/03_08:40:40

C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#define INOUT_LEN 4
#define TIME_LEN 20
#define MAX_MAXOUT 1000

char inout[INOUT_LEN];
char time[TIME_LEN];
uint jobnum;

char maxtime[MAX_MAXOUT][TIME_LEN];

int main(int argc, char **argv)
{
  FILE *in = NULL;
  int l_out = 0, maxout=-1, maxcount=0;

  if ( argc > 1 ) {
    in = fopen(argv[1], "r");
    if ( in == NULL ) {
      fprintf(stderr, "cannot read %s\n", argv[1]);
      exit(1);
    }
  } else {
    in = stdin;
  }

  while( fscanf(in, "License %s @ %s for job %u\n", inout, time, &jobnum) != EOF ) {

    if ( strcmp(inout, "OUT") == 0 )
      l_out++;
    else
      l_out--;

    if ( l_out > maxout ) {
      maxout = l_out;
      maxcount=0; maxtime[0][0] = '\0';
    }
    if ( l_out == maxout ) {
      if ( maxcount < MAX_MAXOUT ) {
    strncpy(maxtime[maxcount], time, TIME_LEN);
    maxcount++;
      } else {
    fprintf(stderr, "increase MAX_MAXOUT (now it is %u)\n", MAX_MAXOUT);
    exit(1);
      }
    }
  }

  printf("Maximum simultaneous license use is %d at the following times:\n", maxout);
  for(l_out=0; l_out < maxcount; l_out++) {
    printf("%s\n", maxtime[l_out]);
  }

  if ( in != stdin ) fclose(in);
  exit(0);
}

Using mmap, no extra storage (think this as a search and replace):

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <err.h>
#include <string.h>

int main()
{
    struct stat s;
    int fd = open("mlijobs.txt", O_RDONLY);
    int cnt, max_cnt, occur;
    char *buf, *ptr;

    if (fd == -1) err(1, "open");
    fstat(fd, &s);
    ptr = buf = mmap(0, s.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);

    cnt = max_cnt = 0;
    while(ptr - buf < s.st_size - 33) {
        if (!strncmp(ptr, "License OUT", 11) && ++cnt >= max_cnt) {
            if (cnt > max_cnt) {
                max_cnt = cnt;
                occur = 0;
            }
            /* can't sprintf time stamp: might overlap */
            memmove(buf + 26 * occur, ptr + 14, 19);
            sprintf(buf + 26 * occur + 19, "%6d\n", cnt);
            occur++;
        } else if (!strncmp(ptr, "License IN ", 11)) cnt --;

        while (ptr < buf + s.st_size && *ptr++ != '\n');
    }

    printf(buf);
    munmap(buf, s.st_size);
    return close(fd);
}

output

2008/10/03_08:39:34    99
2008/10/03_08:40:40    99

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace TextProc3
{
    class Program
    {
        static void Main(string[] args)
        {            
            string line;
            int count = 0, maxcount = 0;
            List<string> times = new List<string>();
            System.IO.StreamReader file = new StreamReader("mlijobs.txt");
            while ((line = file.ReadLine()) != null)
            {
                string[] lineelements = line.Split(' ');                
                switch (lineelements[1])
                {
                    case "IN":
                        count--;
                        break;
                    case "OUT":
                        count++;
                        if (count > maxcount)
                        {
                            maxcount = count;
                            times.Clear();
                            times.Add(lineelements[3]);
                        }else if(count == maxcount){
                            times.Add(lineelements[3]);
                        }
                        break;
                }                
            }
            file.Close();
            Console.WriteLine(maxcount);
            foreach (string time in times)
            {
                Console.WriteLine(time);
            }
        }
    }
}
99
2008/10/03_08:39:34
2008/10/03_08:40:40

C++

#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

int main()
{
    const char logfilename[] = "mlijobs.txt";
    std::ifstream logfile(logfilename);

    if (!logfile.is_open())
    {
        std::cerr << "Error opening: " << logfilename << "\n";
        return -1;
    }

    int license = 0, max_license = 0;
    std::vector<std::string> max_timestamp;

    for (std::string logline; std::getline(logfile, logline); )
    {
        std::string action(logline.substr(8,3));

        if (action == "OUT")
        {
            if (++license >= max_license)
            {
                if (license > max_license)
                {
                    max_license = license;
                    max_timestamp.clear();
                }
                max_timestamp.push_back(logline.substr(14, 19));
            }
        }
        else if (action == "IN ")
        {
            --license;
        }
    }

    std::cout << "License count at log end: " << license
        << "\nMaximum simultaneous license: " << max_license
        << "\nMaximum license time(s):\n";

    std::copy(max_timestamp.begin(), max_timestamp.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"));
}

Output:

License count at log end: 0
Maximum simultaneous licenses: 99
Maximum license time(s):
2008/10/03_08:39:34
2008/10/03_08:40:40

Clojure

(defn delta [entry]
  (case (second (re-find #"\ (.*)\ @" entry))
    "IN " -1
    "OUT" 1
    (throw (Exception. (str "Invalid entry:" entry)))))

(defn t [entry]
  (second (re-find #"@\ (.*)\ f" entry)))

(let [entries (clojure.string/split (slurp "mlijobs.txt") #"\n")
      in-use (reductions + (map delta entries))
      m (apply max in-use)
      times (map #(nth (map t entries) %)
                 (keep-indexed #(when (= m %2) %1) in-use))]
  (println "Maximum simultaneous license use is" m "at the following times:")
  (map println times))
Maximum simultaneous license use is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

COBOL

Works with: OpenCOBOL
       IDENTIFICATION DIVISION.
       PROGRAM-ID. max-licenses-in-use.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT license-file ASSIGN "mlijobs.txt"
               ORGANIZATION LINE SEQUENTIAL
               FILE STATUS file-status.

       DATA DIVISION.
       FILE SECTION.
       FD  license-file.
       01  license-record.
           03  FILLER                   PIC X(8).
           03  action                   PIC X(3).
               88  license-out          VALUE "OUT".
           03  FILLER                   PIC X(3).
           03  license-timestamp        PIC X(19).
           03  FILLER                   PIC X(13).

       WORKING-STORAGE SECTION.
       01  file-status                  PIC XX.
           88  file-ok                  VALUE "00".

       01  max-licenses-out             PIC 9(6).
       01  num-max-times                PIC 99.
       01  max-license-times-area.
           03  max-timestamps           PIC X(19) OCCURS 1 TO 50 TIMES
               DEPENDING ON num-max-times.
       01  current-licenses-out         PIC 9(6).
       
       01  i                            PIC 99.

       PROCEDURE DIVISION.
       DECLARATIVES.
       license-file-error SECTION.
           USE AFTER ERROR ON license-file.

           DISPLAY "An unexpected error has occurred. Error "
               file-status ". The program will close."
           GOBACK
           .
       END DECLARATIVES.

       main-line.
           OPEN INPUT license-file
           IF NOT file-ok
               DISPLAY "File could not be opened. Error " file-status
                   "."
               GOBACK
           END-IF
           
           PERFORM FOREVER
               READ license-file
                   AT END
                       EXIT PERFORM
               END-READ

               IF license-out
                   ADD 1 TO current-licenses-out

                   EVALUATE TRUE
                       WHEN current-licenses-out > max-licenses-out
                           MOVE 1 TO num-max-times
                           MOVE current-licenses-out TO max-licenses-out
                           MOVE license-timestamp
                               TO max-timestamps (num-max-times)

                       WHEN current-licenses-out = max-licenses-out
                           ADD 1 TO num-max-times
                           MOVE license-timestamp
                               TO max-timestamps (num-max-times)
                   END-EVALUATE
               ELSE
                   SUBTRACT 1 FROM current-licenses-out
               END-IF
           END-PERFORM

           CLOSE license-file

           DISPLAY "License count at log end: " current-licenses-out
           DISPLAY "Maximum simulataneous licenses: " max-licenses-out
           DISPLAY "Time(s):"
           PERFORM VARYING i FROM 1 BY 1 UNTIL num-max-times < i
               DISPLAY max-timestamps (i)
           END-PERFORM

           GOBACK
           .

Common Lisp

Library: CL-PPCRE
(defun max-licenses (&optional (logfile "mlijobs.txt"))
  (with-open-file (log logfile :direction :input)
    (do ((current-logs 0) (max-logs 0) (max-log-times '())
         (line #1=(read-line log nil nil) #1#))
        ((null line)
         (format t "~&Maximum simultaneous license use is ~w at the ~
                     following time~p: ~{~%  ~a~}."
                 max-logs (length max-log-times) (nreverse max-log-times)))
      (cl-ppcre:register-groups-bind (op time)
          ("License (\\b.*\\b)[ ]{1,2}@ (\\b.*\\b)" line)
        (cond ((string= "OUT" op) (incf current-logs))
              ((string= "IN"  op) (decf current-logs))
              (t (cerror "Ignore it." "Malformed entry ~s." line)))
        (cond ((> current-logs max-logs)
               (setf max-logs current-logs
                     max-log-times (list time)))
              ((= current-logs max-logs)
               (push time max-log-times)))))))
> (max-licenses)
Maximum simultaneous license use is 99 at the following times: 
  2008/10/03_08:39:34
  2008/10/03_08:40:40.
NIL

D

void main() {
    import std.stdio;
    int nOut, maxOut = -1;
    string[] maxTimes;

    foreach (string job; lines(File("mlijobs.txt"))) {
        nOut += (job[8] == 'O') ? 1 : -1;
        if (nOut > maxOut) {
            maxOut = nOut;
            maxTimes = null;
        }
        if (nOut == maxOut)
            maxTimes ~= job[14 .. 33];
    }

    writefln("Maximum simultaneous license use is %d at" ~
             " the following times:\n%( %s\n%)", maxOut, maxTimes);
}
Output:
Maximum simultaneous license use is 99 at the following times:
 "2008/10/03_08:39:34"
 "2008/10/03_08:40:40"

E

Translation of: Python
var out := 0
var maxOut := 0
var maxTimes := []

def events := ["OUT " => 1, "IN  " => -1]

for line in <file:mlijobs.txt> {
    def `License @{via (events.fetch) delta}@@ @time for job @num$\n` := line

    out += delta
    if (out > maxOut) {
        maxOut := out
        maxTimes := []
    }
    if (out == maxOut) {
        maxTimes with= time
    }
}

println(`Maximum simultaneous license use is $maxOut at the following times:`)
for time in maxTimes {
    println(` $time`)
}

Eiffel

class
    APPLICATION

create
    make

feature

    make
            -- Max Licences used.
        local
            count: INTEGER
            max_count: INTEGER
            date: STRING
        do
            read_list
            create date.make_empty
            across
                data as d
            loop
                if d.item.has_substring ("OUT") then
                    count := count + 1
                    if count > max_count then
                        max_count := count
                        date := d.item
                    end
                elseif d.item.has_substring ("IN") then
                    count := count - 1
                end
            end
            io.put_string ("Max Licences OUT: " + max_count.out)
            io.new_line
            io.put_string ("Date: " + date.substring (15, 33))
        end

    original_list: STRING = "mlijobs.txt"

feature {NONE}

    read_list
            -- Data read into 'data.
        local
            l_file: PLAIN_TEXT_FILE
        do
            create l_file.make_open_read_write (original_list)
            l_file.read_stream (l_file.count)
            data := l_file.last_string.split ('%N')
            l_file.close
        end

    data: LIST [STRING]

end
Output:
Max Licences OUT: 99
Date: 2008/10/03_08:39:34

Erlang

-module( text_processing_max_licenses ).

-export( [out_dates_from_file/1, task/0] ).

out_dates_from_file( Name ) ->
    {ok, Binary} = file:read_file( Name ),
    Lines = binary:split( Binary, <<"\n">>, [global] ),
    {_N, _Date, Dict} = lists:foldl( fun out_dates/2, {0, "", dict:new()}, Lines ),
    [{X, dict:fetch(X, Dict)} || X <- dict:fetch_keys( Dict )].

task() ->
    [{Max, Dates} | _T] = lists:reverse(lists:sort(out_dates_from_file("mlijobs.txt")) ),
    io:fwrite( "Max licenses was ~p at ~p~n", [Max, Dates] ).



out_dates( <<>>, Acc ) -> Acc;
out_dates( Line, {N, Date, Dict} ) ->
    [_License, Direction, <<"@">>, New_date | _T] = [X || X <- binary:split(Line, <<" ">>, [global]), X =/= <<>>],
    New_n = out_dates_n( N, Direction ),
    New_dict = out_dates_dict( N, New_n, Date, Dict ),
    {New_n, New_date, New_dict}.

out_dates_dict( N, New_n, Date, Dict ) when N > New_n -> dict:append( N, Date, Dict );
out_dates_dict( _N, _New_n, _Date, Dict ) -> Dict. 

out_dates_n( N, <<"OUT">> ) -> N + 1;
out_dates_n( N, <<"IN">> ) -> N - 1.
Output:
12> text_processing_max_licenses:task().
Max licenses was 99 at [<<"2008/10/03_08:39:34">>,<<"2008/10/03_08:40:40">>]

Euphoria

function split(sequence s, integer c)
    sequence out
    integer first, delim
    out = {}
    first = 1
    while first <= length(s) do
        delim = find_from(c, s, first)
        if delim = 0 then
            delim = length(s) + 1
        end if
        out = append(out, s[first..delim-1])
        first = delim + 1
    end while
    return out
end function

include get.e
function val(sequence s)
    sequence v
    v = value(s)
    return v[2] - v[1] * v[1]
end function

constant fn = open("mlijobs.txt", "r")
integer maxout
sequence jobs, line, maxtime
object x
jobs = {}
maxout = 0
while 1 do
    x = gets(fn)
    if atom(x) then
        exit
    end if
    line = split(x, ' ')
    line[$] = val(line[$])
    if equal(line[2], "OUT") then
        jobs &= line[$]
        if length(jobs) > maxout then
            maxout = length(jobs)
            maxtime = {line[4]}
        elsif length(jobs) = maxout then
            maxtime = append(maxtime, line[4])
        end if
    else
        jobs[find(line[$],jobs)] = jobs[$]
        jobs = jobs[1..$-1]
    end if
end while
close(fn)

printf(1, "Maximum simultaneous license use is %d at the following times:\n", maxout)
for i = 1 to length(maxtime) do
    printf(1, "%s\n", {maxtime[i]})
end for

Output:

Maximum simultaneous license use is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

Factor

Placing the file in resource:work/mlijobs.txt:

USING: kernel sequences splitting math accessors io.encodings.ascii
io.files math.parser io ;
IN: maxlicenses

TUPLE: maxlicense max-count current-count times ;

<PRIVATE

: <maxlicense> ( -- max ) -1 0 V{ } clone \ maxlicense boa ; inline

: out? ( line -- ? ) [ "OUT" ] dip subseq? ; inline

: line-time ( line -- time ) " " split harvest fourth ; inline

: update-max-count ( max -- max' )
    dup [ current-count>> ] [ max-count>> ] bi >
    [ dup current-count>> >>max-count V{ } clone >>times ] when ;

: (inc-current-count) ( max ? -- max' )
    [ [ 1 + ] change-current-count ]
    [ [ 1 - ] change-current-count ]
    if
    update-max-count ; inline

: inc-current-count ( max ? time -- max' time )
    [ (inc-current-count) ] dip ;

: current-max-equal? ( max -- max ? )
    dup [ current-count>> ] [ max-count>> ] bi = ;

: update-time ( max time -- max' )
    [ current-max-equal? ] dip
    swap
    [ [ suffix ] curry change-times ] [ drop ] if ;
    
: split-line ( line -- ? time ) [ out? ] [ line-time ] bi ;

: process ( max line -- max ) split-line inc-current-count update-time ;

PRIVATE>

: find-max-licenses ( -- max )
    "resource:work/mlijobs.txt" ascii file-lines
    <maxlicense> [ process ] reduce ;

: print-max-licenses ( max -- )
    [ times>> ] [ max-count>> ] bi
    "Maximum simultaneous license use is " write
    number>string write
    " at the following times: " print
    [ print ] each ;
( scratchpad ) [ find-max-licenses print-max-licenses ] time
Maximum simultaneous license use is 99 at the following times: 
2008/10/03_08:39:34
2008/10/03_08:40:40
Running time: 0.16164423 seconds

Forth

20 constant date-size
create max-dates date-size 100 * allot
variable max-out
variable counter

stdin value input

: process ( addr len -- )
  8 /string
  over 3 s" OUT" compare 0= if
    1 counter +!
    counter @ max-out @ > if
      counter @ max-out !
      drop 5 + date-size max-dates  place
    else counter @ max-out @ = if
      drop 5 + date-size max-dates +place
    else 2drop then then
  else drop 2 s" IN" compare 0= if
    -1 counter +!
  then then ;

: main
  0 max-out !
  0 counter !
  s" mlijobs.txt" r/o open-file throw to input
  begin  pad 80 input read-line throw
  while  pad swap process
  repeat drop
  input close-file throw
  max-out @ . ." max licenses in use @"
  max-dates count type cr ;

main bye

Fortran

Works with: Fortran version 90 and later
 PROGRAM MAX_LICENSES
   IMPLICIT NONE
 
   INTEGER :: out=0, maxout=0, maxcount=0, err
   CHARACTER(50) :: line
   CHARACTER(19) :: maxtime(100)
 
   OPEN (UNIT=5, FILE="Licenses.txt", STATUS="OLD", IOSTAT=err)
   IF (err > 0) THEN
     WRITE(*,*) "Error opening file Licenses.txt"
     STOP
   END IF
 
   DO 
     READ(5, "(A)", IOSTAT=err) line
     IF (err == -1) EXIT          ! EOF detected
     IF (line(9:9) == "O") THEN
       out = out + 1
     ELSE IF (line(9:9) == "I") THEN
       out = out - 1
     END IF
     IF (out > maxout ) THEN
       maxout = maxout + 1
       maxcount = 1
       maxtime(maxcount) = line(15:33)
     ELSE IF (out == maxout) THEN
       maxcount = maxcount + 1
       maxtime(maxcount) = line(15:33)
     END IF
   END DO
  
   CLOSE(5)
  
   WRITE(*,"(A,I4,A)") "Maximum simultaneous license use is", maxout, " at the following times:"
   WRITE(*,"(A)") maxtime(1:maxcount)
  
 END PROGRAM MAX_LICENSES

Output

 Maximum simultaneous license use is  99 at the following times:
 2008/10/03_08:39:34                                           
 2008/10/03_08:40:40


FreeBASIC

Translation of: PureBasic
Const CRLF = Chr(13) & Chr(10)
Dim As String currline, maxtime
Dim As Integer counter = 0, max = 0
Dim As String filename = "mlijobs.txt"

Open filename For Input As #1
While Not Eof(1)
    Line Input #1, currline
    
    If Mid(currline,9,3) = "OUT" Then
        counter += 1
    Else
        counter -= 1
    End If
    If counter > max Then
        max = counter
        maxtime = Mid(currline,15,19)
    Elseif counter = max Then
        maxtime &= CRLF & Mid(currline,15,19)
    End If
Wend
Print Str(max); " license(s) used at ;"; CRLF; maxtime
Close #1
Print !"\nPress ENTER to exit"
Sleep
Output:
Igual que la entrada de PureBasic.


Gema

Start with gema -f licenses.gema mlijobs.txt

@set{count;0};@set{max;0}

License OUT \@ * *\n=@incr{count}@testmax{${count},*}
License IN  \@ * *\n=@decr{count}
\Z=@report{${max},${times${max}}}

testmax:*,*=@cmpn{${max};$1;@set{max;$1};;}@append{times${count};$2\n}

report:*,*=Maximum simultaneous license use is * at\n*

Output:

Maximum simultaneous license use is 99 at
2008/10/03_08:39:34
2008/10/03_08:40:40

Go

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "log"
    "os"
)

const (
    filename   = "mlijobs.txt"
    inoutField = 1
    timeField  = 3
    numFields  = 7
)

func main() {
    file, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    var ml, out int
    var mlTimes []string
    in := []byte("IN")
    s := bufio.NewScanner(file)
    for s.Scan() {
        f := bytes.Fields(s.Bytes())
        if len(f) != numFields {
            log.Fatal("unexpected format,", len(f), "fields.")
        }
        if bytes.Equal(f[inoutField], in) {
            out--
            if out < 0 {
                log.Fatalf("negative license use at %s", f[timeField])
            }
            continue
        }
        out++
        if out < ml {
            continue
        }

        if out > ml {
            ml = out
            mlTimes = mlTimes[:0]
        }
        mlTimes = append(mlTimes, string(f[timeField]))
    }
    if err = s.Err(); err != nil {
        log.Fatal(err)
    }

    fmt.Println("max licenses:", ml)
    fmt.Println("at:")
    for _, t := range mlTimes {
        fmt.Println(" ", t)
    }
}
Output:
max licenses: 99
at:
  2008/10/03_08:39:34
  2008/10/03_08:40:40

Groovy

def max = 0
def dates = []
def licenses = [:]
new File('licenseFile.txt').eachLine { line ->
    (line =~ /License (\w+)\s+@ ([\d\/_:]+) for job (\d+)/).each { matcher, action, date, job ->
        switch (action) {
        case 'IN':
            assert licenses[job] != null : "License has not been checked out for $job"
            licenses.remove job
            break
        case 'OUT':
            assert licenses[job] == null : "License has already been checked out for $job"
            licenses[job] = date
            def count = licenses.keySet().size()
            if (count > max) {
                max = count
                dates = [ date ]
            } else if (count == max) {
                dates << date
            }
            break
        default:
            throw new IllegalArgumentException("Unsupported license action $action")
        }
    }
}

println "Maximum Licenses $max"
dates.each { date -> println "  $date" }

Output:

Maximum Licenses 99
  2008/10/03_08:39:34
  2008/10/03_08:40:40

Haskell

import Data.List

main = do
  f <- readFile "./../Puzzels/Rosetta/inout.txt"
  let (ioo,dt) = unzip. map ((\(_:io:_:t:_)-> (io,t)). words) . lines $ f
      cio = drop 1 . scanl (\c io -> if io == "IN" then pred c else succ c) 0 $ ioo 
      mo = maximum cio
  putStrLn $ "Maximum simultaneous license use is " ++ show mo ++ " at:"
  mapM_ (putStrLn . (dt!!)) . elemIndices mo $ cio

HicEst

This example is incorrect. It does not accomplish the given task. Please fix the code and remove this message.

We open Licenses.txt in MatrixExplorer mode with 3 columns: IN/OUT, date_time, ID_nr. This allows to adress single file elements by Licenses(row, column).

CHARACTER Licenses="Licenses.txt"
REAL :: counts(1), Top10(10)

OPEN(FIle=Licenses, fmt='8x,A3,3x,A19,Nb ,', LENgth=lines)

ALLOCATE(counts, lines)
counts(1) = 1
DO line = 2, lines
   counts(line) = counts(line-1) + 1 - 2*(Licenses(line,1)=='IN')
ENDDO

SORT(Vector=counts, Descending=1, Index=Top10)

DO i = 1, LEN(Top10)
  WRITE() counts(Top10(i)), Licenses(Top10(i), 2)
ENDDO

END
99 2008/10/03_08:40:40
99 2008/10/03_08:39:34
98 2008/10/03_08:40:47
98 2008/10/03_08:40:11
98 2008/10/03_08:39:46
98 2008/10/03_08:39:45
98 2008/10/03_08:39:30
97 2008/10/03_20:44:58
97 2008/10/03_08:41:36
97 2008/10/03_08:40:53

Icon and Unicon

The following solution works in both languages:

procedure main(A)
    maxCount := count := 0

    every !&input ?  case tab(upto('@')) of {
        "License OUT ": {
             maxTime := (maxCount <:= (count +:= 1), [])
             put(maxTime, (maxCount = count, ="@ ", tab(find(" for "))))
             }
        "License IN  ": count -:= (count > 0, 1)     # Error check
        }

    write("There were ",maxCount," licenses out at:")
    every write("\t",!maxTime)
end

And a run of the program:

->ml <mlijobs.txt
There were 99 licenses out at:
        2008/10/03_08:39:34
        2008/10/03_08:40:40
->


J

   require 'files'
   'I D' =: (8 ; 14+i.19) {"1 &.> <'m' fread 'licenses.txt' NB.  read file as matrix, select columns
   lu    =:  +/\ _1 ^ 'OI' i. I                             NB.  Number of licenses in use at any given time
   mx    =:  (I.@:= >./) lu                                 NB.  Indicies of maxima

   NB.  Output results
   (mx { D) ,~ 'Maximum simultaneous license use is ' , ' at the following times:' ,~ ": {. ,mx { lu
 Maximum simultaneous license use is 99 at the following times:
 2008/10/03_08:39:34                                           
 2008/10/03_08:40:40

Java

Works with: Java version 1.5+
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedList;

public class License {
  public static void main(String[] args) throws FileNotFoundException, IOException{
    BufferedReader in = new BufferedReader(new FileReader(args[0]));
    int max = Integer.MIN_VALUE;
    LinkedList<String> dates = new LinkedList<String>();
    String line;
    int count = 0;
    while((line = in.readLine()) != null){
      if(line.startsWith("License OUT ")) count++;
      if(line.startsWith("License IN ")) count--;
      if(count > max){
        max = count;
        String date = line.split(" ")[3];
        dates.clear();
        dates.add(date);
      }else if(count == max){
        String date = line.split(" ")[3];
        dates.add(date);
      }
    }
    System.out.println("Max licenses out: "+max);
    System.out.println("At time(s): "+dates);
  }
}
Max licenses out: 99
At time(s): [2008/10/03_08:39:34, 2008/10/03_08:40:40]

JavaScript

Works with: JScript

for the file i/o

var file_system = new ActiveXObject("Scripting.FileSystemObject");
var fh = file_system.openTextFile('mlijobs.txt', 1); // 1 == open for reading
var in_use = 0, max_in_use = -1, max_in_use_at = [];

while ( ! fh.atEndOfStream) {
    var line = fh.readline();
    if (line.substr(8,3) == "OUT") {
        in_use++;
        if (in_use > max_in_use) {
            max_in_use = in_use;
            max_in_use_at = [ line.split(' ')[3] ];
        }
        else if (in_use == max_in_use)
            max_in_use_at.push( line.split(' ')[3] );
    }
    else if (line.substr(8,2) == "IN") 
        in_use--;
}

fh.close();

WScript.echo("Max licenses out: " + max_in_use + "\n  " + max_in_use_at.join('\n  '));

output:

Max licenses out: 99
  2008/10/03_08:39:34
  2008/10/03_08:40:40

jq

Works with: jq version 1.4
# Input: an array of strings
def max_licenses_in_use:
  # state: [in_use = 0, max_in_use = -1, max_in_use_at = [] ]
  reduce .[] as $line
    ([0,  -1, [] ];
     ($line|split(" ")) as $line
     | if $line[1] == "OUT" then 
         .[0] += 1                          # in_use++;
         | if   .[0] > .[1]                 # (in_use > max_in_use)
           then .[1] = .[0]                 # max_in_use = in_use
             |  .[2] = [$line[3]]           # max_in_use_at = [$line[3]]
           elif .[0] == .[1]                # (in_use == max_in_use)
             then .[2] += [$line[3]]        # max_in_use_at << $line[3]
           else .
           end
       elif $line[1] == "IN" then .[0] -= 1 # in_use--
       else .
       end )
   | "Max licenses out: \(.[1]) at:\n \(.[2]|join("\n "))" ;

# The file is read in as a single string and so must be split at newlines:
split("\n") | max_licenses_in_use
Output:
$ time /usr/local/bin/jq -M -R -r -s -f Max_licences_in_use.jq mlijobs.txt
Max licenses out: 99 at:
 2008/10/03_08:39:34
 2008/10/03_08:40:40

real   0m0.163s
user   0m0.154s
sys    0m0.005s

Julia

Works with: Julia version 0.6
Translation of: Python
function maximumsimultlicenses(io::IO)
    out, maxout, maxtimes = 0, -1, String[]
    for job in readlines(io)
        out += ifelse(occursin("OUT", job), 1, -1)
        if out > maxout
            maxout = out
            empty!(maxtimes)
        end
        if out == maxout
            push!(maxtimes, split(job)[4])
        end
    end
    return maxout, maxtimes
end

let (maxout, maxtimes) = open(maximumsimultlicenses, "data/mlijobs.txt")
    println("Maximum simultaneous license use is $maxout at the following times: \n - ", join(maxtimes, "\n - "))
end
Output:
Maximum simultaneous license use is 99 at the following times:
 - 2008/10/03_08:39:34
 - 2008/10/03_08:40:40

K

  r:m@&a=x:|/a:+\{:[x[8+!3]~"OUT";1;-1]}'m:0:"mlijobs.txt";
  `0:,/"Maximum simultaneous license use is ",$x;
  `0:" at the following times:\n";`0:r[;14+!19]

Output:

Maximum simultaneous license use is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

Kotlin

The link for downloading the file, mlijobs.txt, is currently broken so I've created a small file myself to test the program:

// version 1.1.51

import java.io.File

fun main(args: Array<String>) {
    val filePath = "mlijobs.txt"
    var licenses = 0
    var maxLicenses = 0
    val dates = mutableListOf<String>()
    val f = File(filePath) 

    f.forEachLine { line ->
        if (line.startsWith("License OUT")) {
            licenses++
            if (licenses > maxLicenses) {
                maxLicenses = licenses
                dates.clear() 
                dates.add(line.substring(14, 33))
            }
            else if(licenses == maxLicenses) {
                dates.add(line.substring(14, 33))
            }
        }
        else if (line.startsWith("License IN")) {
            licenses--
        }
    }
    println("Maximum simultaneous license use is $maxLicenses at the following time(s):")
    println(dates.map { "  $it" }.joinToString("\n"))               
}

The file used for testing:

License OUT @ 2008/10/03_23:51:05 for job 4974
License OUT @ 2008/10/03_23:57:00 for job 4975
License IN  @ 2008/10/04_00:00:06 for job 4975
License OUT @ 2008/10/04_00:07:19 for job 4976
License IN  @ 2008/10/04_00:11:32 for job 4976
License IN  @ 2008/10/04_00:18:22 for job 4974
Output:
Maximum simultaneous license use is 2 at the following time(s):
  2008/10/03_23:57:00
  2008/10/04_00:07:19

Lua

filename = "mlijobs.txt"
io.input( filename )

max_out, n_out = 0, 0
occurr_dates = {}

while true do
    line = io.read( "*line" )
    if line == nil then break end

    if string.find( line, "OUT" ) ~= nil then
        n_out = n_out + 1
        if n_out > max_out then
            max_out = n_out
            occurr_dates = {}
            occurr_dates[#occurr_dates+1] = string.match( line, "@ ([%d+%p]+)" )
        elseif n_out == max_out then
            occurr_dates[#occurr_dates+1] = string.match( line, "@ ([%d+%p]+)" )
        end
    else
        n_out = n_out - 1
    end
end

print( "Maximum licenses in use:", max_out )
print( "Occurrences:" )
for i = 1, #occurr_dates do
    print( "", occurr_dates[i] )
end

Output:

Maximum licenses in use: 99
Occurrences:
    2008/10/03_08:39:34
    2008/10/03_08:40:40

M2000 Interpreter

Load.Doc load txt in any format of UTF-16LE, UTF16-BE, UTF-8, ANSI, in any line separator CRLF, CR, LF and place text in paragraphs in a document object. A document object return a string containing text (as utf16le using CRLF), but for some specific functions and statements treated as object. So changing paragraphs done with Paragraph$(a$, (m)) where m is by reference with a special syntax for this function (using without m we place order number, but m is a valid paragraph id, which always point to same paragraph until removing it).

In Wine 1.8 can't download.

Module Checkit {
      Document a$, max_time$
      Load.doc a$, "mlijobs.txt"
      const dl$=" ", nl$={
      }
      Def long m, out, max_out=-1
      m=Paragraph(a$, 0) 
      If Forward(a$,m) then {
            While m {
                  job$=Paragraph$(a$,(m))
                  out+=If(Piece$(job$,dl$,2)="OUT"->1&, -1&)
                  If out>max_out then max_out=out : Clear max_time$
                  If out=max_out then max_time$=Piece$(job$,dl$,4)+nl$
            }
      }
      Report Format$("Maximum simultaneous license use is {0} at the following times:",max_out)
      Print "    ";  ' left margin
      Report max_time$
}
Checkit

Output

Maximum simultaneous license use is 99 at the following times:
    2008/10/03_08:39:34
    2008/10/03_08:40:40

M4

divert(-1)
define(`current',0)
define(`max',0)
define(`OUT',`define(`current',incr(current))`'ifelse(eval(current>max),1,
   `define(`max',current)`'divert(-1)`'undivert(1)`'divert(1)',
   `ifelse(current,max,`divert(1)undivert(1)')')')
define(`IN',`define(`current',decr(current))')
define(`for',`divert(-1)')
include(mlijobs.txt))
divert
max
undivert(1)

Output:

99
 @ 2008/10/03_08:39:34  @ 2008/10/03_08:40:40

Mathematica /Wolfram Language

LC = 0; LCMax = 0; Scan[
   If[MemberQ[#, "OUT"], LC++; 
      If[LCMax < LC, LCMax = LC; LCMaxtimes = {};]; 
      If[LCMax == LC, AppendTo[LCMaxtimes, #[[4]]]],
   LC--;] &, Import["mlijobs.txt", "Table"]]
Print["The maximum number of licenses used was ", LCMax, ", at ", LCMaxtimes]
Output:
-> The maximum number of licenses used was 99, at {2008/10/03_08:39:34,2008/10/03_08:40:40}

MAXScript

fn licencesInUse =
(
    local logFile = openFile "mlijobs.txt"
    local out = 0
    local maxOut = -1
    local maxTimes = #()
    
    while not EOF logFile do
    (
        line = readLine logFile

        if findString line "OUT" != undefined then
        (
            out += 1
        )
        else
        (
            out -= 1
        )

        if out > maxOut then
        (
            maxOut = out
            maxTimes = #()
        )

        if out == maxOut then
        (
            append maxTimes (filterString line " ")[4]
        )
    )
    format "Maximum simultaneous license use is % at the following times:\n" maxOut 

    for time in maxTimes do
    (
        format "%\n" time
    )

    close logFile
)

licencesInUse()

Output

Maximum simultaneous license use is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

newLISP

(define (foo  , INOUT Time mxcnt cnt line times tokens a)
  (let (handle (open "mlijobs.txt" "read"))
    (while (setq line (read-line handle))
      (setq tokens (parse line {[ ]+} 0))
      ;; Match the 7 tokens.
      (when (setq a (unify '(_ INOUT _ Time _ _ _) tokens))
        (bind a)  ;; Assign to variables INOUT and Time.
        (case INOUT
          ("OUT"
            (when (> (++ cnt) mxcnt) (setq mxcnt cnt) (setq times '()))
            (if (= cnt mxcnt) (push Time times)))
          ("IN" (-- cnt)))))
    (close handle)
    (list mxcnt times)))

(foo)

(99 ("2008/10/03_08:40:40" "2008/10/03_08:39:34"))

A shorter solution.

(define (foo  , mxcnt cnt times)
  (let (handle (open "mlijobs.txt" "read"))
    (while (search handle { (IN|OUT) +@ +(\S+)} true 0)
      (case $1
        ("OUT"
          (when (> (++ cnt) mxcnt) (setq mxcnt cnt) (setq times '()))
          (if (= cnt mxcnt) (push $2 times)))
        ("IN" (-- cnt))))
    (close handle)
    (list mxcnt times)))

Nim

Translation of: Python
import strutils

var
  curOut = 0
  maxOut = -1
  maxTimes = newSeq[string]()

for job in lines "mlijobs.txt":
  if "OUT" in job: inc curOut else: dec curOut
  if curOut > maxOut:
    maxOut = curOut
    maxTimes.setLen(0)
  if curOut == maxOut:
    maxTimes.add job.split[3]

echo "Maximum simultaneous license use is ", maxOut, " at the following times:"
for i in maxTimes: echo "  ", i
Output:
Maximum simultaneous license use is 99 at the following times:
  2008/10/03_08:39:34
  2008/10/03_08:40:40

OCaml

let () =
  let out = ref 0 in
  let max_out = ref(-1) in
  let max_times = ref [] in

  let ic = open_in "mlijobs.txt" in
  try while true do
    let line = input_line ic in
    let io, date, n =
      Scanf.sscanf line
        "License %3[IN OUT] %_c %19[0-9/:_] for job %d"
        (fun io date n -> (io, date, n))
    in
    if io = "OUT" then incr out else decr out;
    if !out > !max_out then
    ( max_out := !out;
      max_times := [date]; )
    else if !out = !max_out then
      max_times := date :: !max_times;
  done
  with End_of_file ->
    close_in ic;
    Printf.printf
      "Maximum simultaneous license use is %d \
       at the following times:\n" !max_out;
    List.iter print_endline !max_times;
;;

Oz

Translation of: Python
declare
  fun {MaxLicenses Filename ?Times}
     InUse = {NewCell 0}
     MaxInUse = {NewCell 0}
     MaxTimes = {NewCell nil}
  in
     for Job in {ReadLines Filename} do
        case {List.take Job 11} of "License OUT" then
     InUse := @InUse + 1
     if @InUse > @MaxInUse then
        MaxInUse := @InUse
        MaxTimes := nil
     end
     if @InUse == @MaxInUse then
        JobTime = {Nth {String.tokens Job & } 4}
     in
        MaxTimes := JobTime|@MaxTimes
     end
        [] "License IN " then
     InUse := @InUse - 1
        end
     end
     Times = {Reverse @MaxTimes}
     @MaxInUse
  end

  %% Helper.
  %% Returns a lazy list. So we don't keep the whole logfile in memory...
  fun {ReadLines Filename}
     F = {New class $ from Open.file Open.text end init(name:Filename)}
     fun lazy {ReadNext}
        case {F getS($)} of
           false then nil
        [] Line then
           Line|{ReadNext}
        end
     end
  in
     %% close file when handle becomes unreachable
     {Finalize.register F proc {$ F} {F close} end}
     {ReadNext}
  end

  Times
  MaxInUse = {MaxLicenses "mlijobs.txt" ?Times}
in
  {System.showInfo
   "Maximum simultaneous license use is "#MaxInUse#" at the following times:"}
  {ForAll Times System.showInfo}

Output:

Maximum simultaneous license use is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

PARI/GP

license()={
    my(v=externstr("type mlijobs.txt"),u,cur,rec,t);
    for(i=1,#v,
        u=Vec(v[i]);
        if(#u>9 && u[9] == "O",
            if(cur++>rec,
                rec=cur;
                t=[v[i]]
            ,
                if(cur == rec,t=concat(t,[v[i]]))
            )
        ,
            cur--
        )
    );
    print(apply(s->concat(vecextract(Vec(s),"15..33")), t));
    rec
};
["2008/10/03_08:39:34", "2008/10/03_08:40:40"]
%1 = 99

Perl

#!/usr/bin/perl -w
use strict;

my $out = 0;
my $max_out = -1;
my @max_times;

open FH, '<mlijobs.txt' or die "Can't open file: $!";
while (<FH>) {
    chomp;
    if (/OUT/) {
        $out++;
    } else {
        $out--;
    }
    if ($out > $max_out) {
        $max_out = $out;
        @max_times = ();
    }
    if ($out == $max_out) {
        push @max_times, (split)[3];
    }
}
close FH;

print "Maximum simultaneous license use is $max_out at the following times:\n";
print "  $_\n" foreach @max_times;

Example output:

Maximum simultaneous license use is 99 at the following times:
  2008/10/03_08:39:34
  2008/10/03_08:40:40

Phix

-- demo\rosetta\Max_licences.exw
with javascript_semantics -- (include version/first of next three lines only)
include mlijobs.e -- global constant lines, or:
--assert(write_lines("mlijobs.txt",lines)!=-1) -- first run, or get from link above, then:
--constant lines = read_lines("mlijobs.txt")

integer maxout = 0, jobnumber
sequence jobs = {}, maxtime, scanres
string inout, jobtime
for i=1 to length(lines) do
    string line = lines[i]
    scanres = scanf(line,"License %s @ %s for job %d")
    if length(scanres)!=1 then
        printf(1,"error scanning line: %s\n",{line})
        {} = wait_key()
        abort(0)
    end if
    {{inout,jobtime,jobnumber}} = scanres
    if inout="OUT" then
        jobs &= jobnumber
        if length(jobs)>maxout then
            maxout = length(jobs)
            maxtime = {jobtime}
        elsif length(jobs)=maxout then
            maxtime = append(maxtime, jobtime)
        end if
    else
        jobs[find(jobnumber,jobs)] = jobs[$]
        jobs = jobs[1..$-1]
    end if
end for
 
printf(1, "Maximum simultaneous license use is %d at the following times:\n", maxout)
for i = 1 to length(maxtime) do
    printf(1, "%s\n", {maxtime[i]})
end for

?"done"
{} = wait_key()
Output:
Maximum simultaneous license use is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

Note the version of mlijobs.e shipped in the distribution is cropped at 20 lines (not all 457K) and will need reconstructing from the above download to get matching results

PHP

$handle = fopen ("mlijobs.txt", "rb");
$maxcount = 0;
$count = 0;
$times = array();
while (!feof($handle)) {
    $buffer = fgets($handle);
    $op = trim(substr($buffer,8,3));
    switch ($op){
        case 'IN':
            $count--;
        break;
        case 'OUT':
            $count++;
            preg_match('/([\d|\/|_|:]+)/',$buffer,$time);
            if($count>$maxcount){
                $maxcount = $count;
                $times = Array($time[0]);
            }elseif($count == $maxcount){
                $times[] = $time[0];
            }
        break;
    }   
}
fclose ($handle);

echo $maxcount . '<br>';
for($i=0;$i<count($times);$i++){
    echo $times[$i] . '<br>';
}
99
2008/10/03_08:39:34
2008/10/03_08:40:40

Picat

import util.

go =>
  Jobs = read_file_lines("mlijobs.txt"),
  Counts = asum(Jobs).to_array, % convert to array for speed
  Max = max(Counts),
  MaxIxs = [I : I in 1..Counts.length, Counts[I] == Max],
  printf("Max number of licenses is %d.\n", Max),
  printf("Interval:\n%w\n", [Jobs[J,15..33] : J in MaxIxs].join("\n")),
  nl.

% Accumulative sum
asum(List) = Asum =>
  asum(List,[],Asum).
asum([],Asum0,Asum) => 
  Asum = Asum0.reverse().
asum([H|T],[],Asum) => 
  C = cond(slice(H,9,11) == "OUT", 1, -1),
  asum(T,[C],Asum).
asum([H|T],Asum0,Asum) =>
  Asum0 = [Last|_],
  C = cond(slice(H,9,11) == "OUT", 1, -1),
  asum(T,[Last+C|Asum0],Asum).
Output:
Max number of licenses is 99.
Interval:
2008/10/03_08:39:34
2008/10/03_08:40:40


PicoLisp

Translation of: AWK

Put the following into an executable file "licenses":

#!/usr/bin/picolisp /usr/lib/picolisp/lib.l

(zero Count MaxCount)

(in (opt)
   (while (split (line) " ")
      (case (pack (cadr (setq Line @)))
         (IN
            (dec 'Count) )
         (OUT
            (let Time (cadddr Line)
               (cond
                  ((> (inc 'Count) MaxCount)
                     (setq MaxCount Count  MaxTimes Time) )
                  ((= Count MaxCount)
                     (setq MaxTimes (pack MaxTimes " and " Time)) ) ) ) ) ) ) )

(prinl "The biggest number of licenses is " MaxCount " at " MaxTimes " !")
(bye)

Then it can be called as

$ ./licenses mlijobs.txt
The biggest number of licenses is 99 at 2008/10/03_08:39:34 and 2008/10/03_08:40:40 !

PL/I

text3: procedure options (main); /* 19 November 2011 */
   declare line character (80) varying;
   declare (nout, max_nout) fixed;
   declare saveline character (80) varying controlled;
   declare k fixed binary;
   declare in file input;

   open file (in) title ('/TEXT-MAX.DAT,TYPE(TEXT),RECSIZE(80)' );

   on endfile (in) go to finish_up;

   max_nout, nout = 0;
   do forever;
      get file (in) edit (line) (L);
      if substr(line, 9, 4) = 'OUT' then nout = nout+1;
      else if substr(line, 9, 3) = 'IN' then
         nout = nout-1;
      if nout = max_nout then
         do; allocate saveline; saveline = line; end;
      if nout > max_nout then
         do;
            do while (allocation(saveline) > 0); free saveline; end;
            max_nout = nout;
            allocate saveline;
            saveline = line;
         end;
   end;

finish_up:
   put skip list ('The maximum number of licences taken out = ' || max_nout);
   do while (allocation(saveline) > 0);
      k = index(saveline, '@');
      if k > 0 then put skip list ('It occurred at ' || substr(saveline, k+1) );
      free saveline;
   end;
end text3;

OUTPUT:

The maximum number of licences taken out =       99 
It occurred at  2008/10/03_08:40:40 for job 1837 
It occurred at  2008/10/03_08:39:34 for job 1833 

PowerShell

[int]$count = 0
[int]$maxCount = 0
[datetime[]]$times = @()

$jobs = Get-Content -Path ".\mlijobs.txt" | ForEach-Object {
    [string[]]$fields   = $_.Split(" ",[StringSplitOptions]::RemoveEmptyEntries)
    [datetime]$datetime = Get-Date $fields[3].Replace("_"," ")
    [PSCustomObject]@{
        State = $fields[1]
        Date  = $datetime
        Job   = $fields[6]
    }
}

foreach ($job in $jobs)
{
    switch ($job.State)
    {
        "IN"
        {
            $count--
        }
        "OUT"
        {
            $count++

            if ($count -gt $maxCount)
            {
                $maxCount = $count
                $times = @()
                $times+= $job.Date
            }
            elseif ($count -eq $maxCount)
            {
                $times+= $job.Date
            }
        }
    }
}

[PSCustomObject]@{
    LicensesOut = $maxCount
    StartTime   = $times[0]
    EndTime     = $times[1]
}
Output:
LicensesOut StartTime            EndTime             
----------- ---------            -------             
         99 10/3/2008 8:39:34 AM 10/3/2008 8:40:40 AM

PureBasic

OpenConsole()

If ReadFile(0, OpenFileRequester("Text processing/3","mlijobs.txt","All files",1))  
  While Not Eof(0)
    currline$=ReadString(0)
    If StringField(currline$,2," ")="OUT"
      counter+1
    Else
      counter-1
    EndIf
    If counter>max  
      max=counter
      maxtime$=StringField(currline$,4," ")
    ElseIf counter=max
      maxtime$+#CRLF$+StringField(currline$,4," ")
    EndIf
  Wend
  PrintN(Str(max)+" license(s) used at ;"+#CRLF$+maxtime$)
  CloseFile(0)            
Else
  PrintN("Failed to open the file.")
EndIf

PrintN(#CRLF$+"Press ENTER to exit"): Input()
CloseConsole()
99 license(s) used at ;
2008/10/03_08:39:34
2008/10/03_08:40:40

Press ENTER to exit

Python

out, max_out, max_times = 0, -1, []
for job in open('mlijobs.txt'):
    out += 1 if "OUT" in job else -1
    if out > max_out:
        max_out, max_times = out, []
    if out == max_out:
        max_times.append(job.split()[3])
        
print("Maximum simultaneous license use is %i at the following times:" % max_out)
print('  ' + '\n  '.join(max_times))
Output:
Maximum simultaneous license use is 99 at the following times:
  2008/10/03_08:39:34
  2008/10/03_08:40:40

R

# Read in data, discard useless bits
dfr <- read.table("mlijobs.txt")
dfr <- dfr[,c(2,4)]
# Find most concurrent licences, and when
n.checked.out <- cumsum(ifelse(dfr$V2=="OUT", 1, -1))
times <- strptime(dfr$V4, "%Y/%m/%d_%H:%M:%S")
most.checked.out <- max(n.checked.out)
when.most.checked.out <- times[which(n.checked.out==most.checked.out)]
# As a bonus, plot license use
plot(times, n.checked.out, type="s")

Racket

#lang racket
;;; reads a licence file on standard input
;;; returns max licences used and list of times this occurred
(define (count-licences)
  (let inner ((ln (read-line)) (in-use 0) (max-in-use 0) (times-list null))
    (if (eof-object? ln) 
        (values max-in-use (reverse times-list))
        (let ((mtch (regexp-match #px"License (IN |OUT) @ (.*) for job.*" ln)))
          (cond
            [(string=? "OUT" (second mtch))
             (let ((in-use+1 (add1 in-use)))
               (cond
                 [(> in-use+1 max-in-use)
                  (inner (read-line) in-use+1 in-use+1 (list (third mtch)))]
                 [(= in-use+1 max-in-use)
                  (inner (read-line) in-use+1 max-in-use (cons (third mtch) times-list))]
                 [else (inner (read-line) in-use+1 max-in-use times-list)]))]
            [(string=? "IN " (second mtch))
             (inner (read-line) (sub1 in-use) max-in-use times-list)]
            [else (inner (read-line) in-use max-in-use times-list)])))))

(define-values (max-used max-used-when)
(with-input-from-file "mlijobs.txt" count-licences))
(printf "Maximum licences in simultaneously used is ~a at the following times:~%"
        max-used)
(for-each displayln max-used-when)

Output:

Maximum licences in simultaneously used is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

The following takes advantage of a combination of for/fold and (in-lines) (and is possible more in the Racket idiom, rather than just using (read-line) and (regexp-match):

#lang racket
;;; reads a licence file on standard input
;;; returns max licences used and list of times this occurred
(define (count-licences)
  (define (sub-count-licences)
    (for/fold ((in-use 0) (max-in-use 0) (times-list null))
      ((ln (in-lines)))
      (let ((mtch (regexp-match #px"License (IN |OUT) @ (.*) for job.*" ln)))
        (cond
          [(string=? "OUT" (second mtch))
           (let ((in-use+1 (add1 in-use)))
             (cond
               [(> in-use+1 max-in-use) (values in-use+1 in-use+1 (list (third mtch)))]
               [(= in-use+1 max-in-use) (values in-use+1 max-in-use (cons (third mtch) times-list))]
               [else (values in-use+1 max-in-use times-list)]))]
          [(string=? "IN " (second mtch)) (values (sub1 in-use) max-in-use times-list)]
          [else (values in-use max-in-use times-list)]))))
  (let-values (((in-use max-in-use times-list) (sub-count-licences)))
    (values max-in-use (reverse times-list))))

(define-values (max-used max-used-when) (with-input-from-file "mlijobs.txt" count-licences))
(printf "Maximum licences in simultaneously used is ~a at the following times:~%" max-used)
(for-each displayln max-used-when)

(Same output)

Raku

(formerly Perl 6) Add some error trapping as recommended by Dijkstra. Not particularly necessary for this specific example but it doesn't hurt to be proactive.


Redirecting the mlijobs.txt file to STDIN:

my %licenses;

%licenses<count max> = 0,0;

for $*IN.lines -> $line {
    my ( $license, $date_time );
    ( *, $license, *, $date_time ) = split /\s+/, $line;
    if $license eq 'OUT' {
        %licenses<count>++;
        if %licenses<count> > %licenses<max> {
            %licenses<max>   = %licenses<count>;
            %licenses<times> = [$date_time];
        }
        elsif %licenses<count> == %licenses<max> {
            %licenses<times>.push($date_time);
        }
    }
    elsif $license eq 'IN' {
        if %licenses<count> == %licenses<max> {
            %licenses<times>[*-1] ~= " through " ~ $date_time;
        }
        %licenses<count>--;
    }
    else {
        # Not a licence OUT or IN event, do nothing
    }
};

my $plural = %licenses<times>.elems == 1 ?? '' !! 's';

say "Maximum concurrent licenses in use: {%licenses<max>}, in the time period{$plural}:";
say join ",\n", %licenses<times>.list;

Example output:

Maximum concurrent licenses in use: 99, in the time periods:
2008/10/03_08:39:34 through 2008/10/03_08:39:45,
2008/10/03_08:40:40 through 2008/10/03_08:40:47

REXX

version 1

/*REXX program processes instrument data as read from a time sorted data file.*/
iFID= 'LICENSE.LOG'                    /*the fileID of the   input  file.     */
high=0                                 /*highest number of licenses (so far). */
#=0                                    /*the count of number of licenses out. */
n=0                                    /*the number of  highest licenses out. */
    do recs=0  while lines(iFID)\==0   /* [↓]  read file  until  end─of─file. */
    parse value linein(iFID) with . ? . $       /*get IN│OUT status, job info.*/
    if ?=='IN'  then                   #=#-1    /*decrement the license count.*/
                else if ?=='OUT'  then #=#+1    /*increment  "     "      "   */
    if # >high then do;  n=1;    job.1=$;  end  /*the job info for highest cnt*/
    if #==high then do;  n=n+1;  job.n=$;  end  /* "   "    "   "   equal   " */
    high=max(high,#)                            /*calculate max license count.*/
    end   /*while ···*/

say recs  'records read from the input file: '  iFID
say 'The maximum number of licenses out is '    high        " at:"
say
     do j=1  for n                     /*show what/when max licenses occurred.*/
     say left('',20)  job.j            /*indent the information displayed.    */
     end   /*j*/                       /*stick a fork in it,  we're all done. */

output   when using the default input file:

10000 records read from the input file:  LICENSE.LOG
The maximum number of licenses out is  99  at:

                     2008/10/03_08:39:34 for job 1833
                     2008/10/03_08:40:40 for job 1837

Version 2 dual-coded for PC and TSO

It should be noted that almost every REXX interpreter returns a different string for   parse source   under Microsoft Windows:

  •   MSDOS     for   BREXX
  •   PCDOS     for   PC/REXX   and   Personal Rexx for DOS
  •   WIN           for   Personal Rexx for Windows
  •   WIN32       for   Regina
  •   Win32       for   R4   and   ROO


It is unknown which classic REXX interpreter can be used (under Windows) below, it fails for the above mentioned seven REXX interpreters.
I am sure it has worked for Regina. Maybe this is now correct for all the WIN (and xxDOS) situations (pun intended)

/* REXX **************************************************************
* 19.11.2012 Walter Pachl transcribed from PL/I
*                         and dual-coded (for PC (Windows) and TSO)
**********************************************************************/
Parse Source source
call time 'R'
Say source
Parse Upper source system .
If left(system,3)='WIN' |,  /* changed from 'Windows' (I see WIN64 in source) */
   wordpos(system,'MSDOS PCDOS')>0 Then Do  
  fid='mlijobs.txt'
  Do i=1 By 1 While lines(fid)>0
    l.i=linein(fid)
    End
  l.0=i-1
  End
Else Do
  "ALLOC FI(IN) DA(MLIJOBS.TEXT) SHR REUSE"
  'EXECIO * DISKR IN (STEM L. FINIS'
  'FREE FI(IN)'
  End
store.0=0
max_nout=0
nout    =0
cnt=0
cnt.=0
do i=1 To l.0
  line=l.i
  Parse Var line 9 inout +3
  Select
    When inout='OUT' then nout = nout+1;
    When inout='IN'  then nout = nout-1;
    Otherwise Iterate
    End
  cnt.nout=cnt.nout+1
  cnt=cnt+1
  if nout = max_nout then
    Call store line
  if nout > max_nout then Do
    max_nout=nout
    drop store.
    store.0=0
    Call store
    end;
  end;

Say 'The maximum number of licences taken out = ' max_nout
Do i=1 to store.0
  k=pos('@',store.i)
  Say 'It occurred at 'substr(store.i,k+1)
  End
limit=5
Do nout=0 To max_nout
  If nout=limit+1 Then Say '.........'
  If nout<=limit | nout>=max_nout-limit Then
    Say right(cnt.nout,5) right(nout,3)
  End
Say right(cnt,5) 'Jobs'
Say time('E') 'seconds (elapsed)'
Exit

store:
  z=store.0+1
  store.z=line
  store.0=z
  Return
Output:

on Windows

D:\>rexx maxl
WindowsNT COMMAND D:\maxl.rex
The maximum number of licences taken out =  99
It occurred at  2008/10/03_08:39:34 for job 1833
It occurred at  2008/10/03_08:40:40 for job 1837
    1   0
    2   1
    2   2
    2   3
    2   4
    2   5
.........
   24  94
   22  95
   15  96
    7  97
    5  98
    2  99
10000 Jobs
0.234000 seconds (elapsed) ooRexx

D:\>regina maxl
WIN64 COMMAND D:\maxl.rex
...
10000 Jobs
.177000 seconds (elapsed) Regina
Output:

on TSO

TSO COMMAND LIC SYS00059 N555555.PRIV.CLIST ? TSO ISPF ?
The maximum number of licences taken out =  99
It occurred at  2008/10/03_08:39:34 for job 1833
It occurred at  2008/10/03_08:40:40 for job 1837
    1   0
    2   1
    2   2
    2   3
    2   4
    2   5
.........
   24  94
   22  95
   15  96
    7  97
    5  98
    2  99
10000 Jobs
0.384154 seconds (elapsed)

Ruby

out = 0
max_out = -1
max_times = []
 
File.foreach('mlijobs.txt') do |line|
  out += line.include?("OUT") ? 1 : -1
  if out > max_out
    max_out = out
    max_times = []
  end
  max_times << line.split[3]  if out == max_out
end
 
puts "Maximum simultaneous license use is #{max_out} at the following times:"
max_times.each {|time| puts "  #{time}"}

Example output:

Maximum simultaneous license use is 99 at the following times:
  2008/10/03_08:39:34
  2008/10/03_08:40:40

Run BASIC

open "c:\data\temp\logFile.txt" for input as #f
while not(eof(#f))
  line input #f, a$
  if word$(a$,2," ") = "IN" then count = count - 1 else count = count + 1
  maxCount = max(maxCount,count)
wend
open "c:\data\temp\logFile.txt" for input as #f
while not(eof(#f))
  line input #f, a$
  if word$(a$,2," ") = "IN" then count = count - 1 else count = count + 1
  if count = maxCount then theDate$ = theDate$ + " | " + word$(a$,4," ") + " Job:";word$(a$,7," ")
wend
print maxCount;" ";theDate$

Rust

type Timestamp = String;

fn compute_usage<R, S, E>(lines: R) -> Result<(u32, Vec<Timestamp>), E>
where
    S: AsRef<str>,
    R: Iterator<Item = Result<S, E>>,
{
    let mut timestamps = Vec::new();
    let mut current = 0;
    let mut maximum = 0;

    for line in lines {
        let line = line?;
        let line = line.as_ref();

        if line.starts_with("License IN") {
            current -= 1;
        } else if line.starts_with("License OUT") {
            current += 1;

            if maximum <= current {
                let date = line.split_whitespace().nth(3).unwrap().to_string();

                if maximum < current {
                    maximum = current;
                    timestamps.clear();
                }

                timestamps.push(date);
            }
        }
    }

    Ok((maximum, timestamps))
}

fn main() -> std::io::Result<()> {
    use std::io::{BufRead, BufReader};
    let file = std::fs::OpenOptions::new().read(true).open("mlijobs.txt")?;
    let (max, timestamps) = compute_usage(BufReader::new(file).lines())?;
    println!("Maximum licenses out: {}", max);
    println!("At time(s): {:?}", timestamps);
    Ok(())
}

Scala

Translated imperative version

Can be seen running in your browser Scastie (remote JVM).

import java.io.{BufferedReader, InputStreamReader}
import java.net.URL

object License0 extends App {
  val url = new URL("https://raw.githubusercontent.com/def-/nim-unsorted/master/mlijobs.txt")
  val in = new BufferedReader(new InputStreamReader(url.openStream()))

  val dates = new collection.mutable.ListBuffer[String]
  var (count: Int, max: Int) = (0, Int.MinValue)
  var line: String = _

  while ( {line = in.readLine; line} != null) {
    if (line.startsWith("License OUT ")) count += 1
    if (line.startsWith("License IN ")) count -= 1 // Redundant test when "OUT" 
    if (count > max) { // Fruitless execution when "License IN " 
      max = count
      val date = line.split(" ")(3)
      dates.clear()
      dates += date
    } else if (count == max) {
      val date = line.split(" ")(3)
      dates += date
    }
  }

  println("Max licenses out: " + max)
  println("At time(s): " + dates.mkString(", "))

}

Smart Imperative Scala with dirty side effects

Output:

Best seen running in your browser Scastie (remote JVM).

import scala.collection.mutable.ListBuffer

object License1 extends App {
  val src = io.Source.fromURL("https://raw.githubusercontent.com/def-/nim-unsorted/master/mlijobs.txt")

  val dates = new ListBuffer[String]
  var (max, count) = (Int.MinValue, 0)

  src.getLines.foreach { line =>
    def date = line.split(" ")(3)

    if (line.startsWith("License OUT ")) {
      count += 1
      if (count > max) {
        max = count
        dates.clear
      }
      if (count == max) dates += date
    } else if (line.startsWith("License IN ")) count -= 1
  }

  println("Max licenses out: " + max)
  println("At time(s): " + dates.mkString(", "))

}

Clean coded FP with (tail) recursion, no side effects

Output:

Best seen running in your browser Scastie (remote JVM).

import scala.annotation.tailrec

object License2 extends App {
  type resultTuple = (Int /*max*/, Int /*count*/, List[String] /*dates*/ )

  val src = io.Source.fromURL(
    "https://raw.githubusercontent.com/def-/nim-unsorted/master/mlijobs.txt")
  val iter = src.getLines()
  val (max, count, dates) = loop(Int.MinValue, 0, Nil)

  def lineToResult(tuple: (resultTuple, String)): resultTuple = {
    val ((max, count, dates), line) = tuple

    def date = line.split(" ")(3)

    if (line.startsWith("License OUT ")) {
      if (count + 1 > max) (count + 1, count + 1, List(date))
      else if (count + 1 == max) (max, max, dates :+ date)
      else (max, count + 1, dates)
    } else if (line.startsWith("License IN ")) tuple._1.copy(_2 = count - 1)
    else tuple._1
  }

  @tailrec
  private def loop(tuple: resultTuple): resultTuple = {
    def lineToResult(tuple: (resultTuple, String)): resultTuple = {
      val ((max, count, dates), line) = tuple

      def date = line.split(" ")(3)

      if (line.startsWith("License OUT ")) {
        if (count + 1 > max) (count + 1, count + 1, List(date))
        else if (count + 1 == max) (max, max, dates :+ date)
        else (max, count + 1, dates)
      } else if (line.startsWith("License IN ")) tuple._1.copy(_2 = count - 1)
      else tuple._1
    }

    if (iter.hasNext)
      loop(lineToResult(tuple, iter.next()))
    else tuple
  }

  println("Max licenses out: " + max)
  println("At time(s): " + dates.mkString(", "))

}

Totally FP with foldLeft

Output:

Best seen running in your browser Scastie (remote JVM).

object License3 extends App {
  type resultTuple = (Int /*max*/, Int /*count*/, List[String] /*dates*/ )

  val src = io.Source.fromURL("https://raw.githubusercontent.com/def-/nim-unsorted/master/mlijobs.txt")

  val (max, count, dates): resultTuple =
    src.getLines().foldLeft(Int.MinValue, 0, Nil: List[String]) {
      case ((max: Int, count: Int, dates: List[String]), line: String)
        if line.startsWith("License OUT ") =>
        def date = line.split(" ")(3)

        if (count + 1 > max) (count + 1, count + 1, List(date))
        else if (count + 1 == max) (max, max, dates :+ date)
        else (max, count + 1, dates)

      case (resultPart: resultTuple, line: String)
        if line.startsWith("License IN ") =>
        resultPart.copy(_2 = resultPart._2 - 1)

      case (resultPart, _) => resultPart
    }

  println("Max licenses out: " + max)
  println("At time(s): " + dates.mkString(", "))

}

Seed7

$ include "seed7_05.s7i";

const proc: main is func
  local
    var file: inFile is STD_NULL;
    var string: line is "";
    var integer: currLicenses is 0;
    var integer: maxLicenses is 0;
    var array string: maxLicenseTimes is 0 times "";
    var string: eventTime is "";
  begin
    inFile := open("mlijobs.txt", "r");
    while hasNext(inFile) do
      line := getln(inFile);
      if line[9 len 3] = "OUT" then
        incr(currLicenses);
        if currLicenses >= maxLicenses then
          if currLicenses > maxLicenses then
            maxLicenses := currLicenses;
            maxLicenseTimes := 0 times "";
          end if;
          maxLicenseTimes &:= line[15 len 19];
        end if;
      elsif currLicenses > 0 then
        decr(currLicenses);
      end if;
    end while;
    close(inFile);
    writeln("Maximum simultaneous license use is " <& maxLicenses <& " at the following times:");
    for eventTime range maxLicenseTimes do
      writeln(eventTime);
    end for;
  end func;

Output:

Maximum simultaneous license use is 99 at the following times:
2008/10/03_08:39:34
2008/10/03_08:40:40

Sidef

Translation of: Perl
var out = 0
var max_out = -1
var max_times = []

ARGF.each { |line|
    out += (line ~~ /OUT/ ? 1 : -1)
    if (out > max_out) {
        max_out = out
        max_times = []
    }
    if (out == max_out) {
        max_times << line.split(' ')[3]
    }
}

say "Maximum simultaneous license use is #{max_out} at the following times:"
max_times.each {|t| say "  #{t}" }
Output:
$ sidef max_licenses.sf < mlijobs.txt
Maximum simultaneous license use is 99 at the following times:
  2008/10/03_08:39:34
  2008/10/03_08:40:40

Tcl

Translation of: Python
 set out 0
 set max_out -1
 set max_times {}
 
 foreach job [split [read [open "mlijobs.txt" "r"]] "\n"] {
     if {[lindex $job 1] == "OUT"} {
         incr out 
     } { 
         incr out -1
     }   
     if {$out > $max_out} {
         set max_out $out
         set max_times {}
     }   
     if {$out == $max_out} {
         lappend max_times [lindex $job 3]
     }   
 }
 
 puts "Maximum simultaneous license use is $max_out at the following times:"
 foreach t $max_times {
     puts "  $t"
 }

Output matches Python

TUSCRIPT

$$ MODE TUSCRIPT
joblog="mlijobs.txt",jobnrout=0
log=FILE (joblog)
DICT jobnrout CREATE
LOOP l=log
jobout=EXTRACT (l,":License :"|,": :")
 IF (jobout=="out") THEN
  time=EXTRACT (l,":@ :"|,": :"), jobnrout=jobnrout+1
  DICT jobnrout APPEND/QUIET jobnrout,num,cnt,time;" "
 ELSE
  jobnrout=jobnrout-1
 ENDIF
ENDLOOP
DICT jobnrout UNLOAD jobnrout,num,cnt,time
DICT jobnrout SIZE maxlicout
times=SELECT (time,#maxlicout)
PRINT "The max. number of licences out is ", maxlicout
PRINT "at these times: ", times

Output:

The max. number of licences out is 99
at these times: 2008/10/03_08:39:34 2008/10/03_08:40:40

TXR

Working with Version 266.

@(bind *times* #H((:eql-based) nil))
@(bind *licenses-out* 0)
@(bind *maximum-licenses-out* 0)
@(collect)
License @statuses @@ @dateTimes for job @jobNumbers
@(end)
@(do (each ((status statuses)
             (dateTime dateTimes)
             (jobNumber jobNumbers))
       (set *licenses-out*
         (if (equal status "OUT")
           (progn 
             (when (>= (+ *licenses-out* 1) *maximum-licenses-out*)
               (set *maximum-licenses-out* (+ *licenses-out* 1))
               (pushhash *times* *maximum-licenses-out* dateTime))
             (+ *licenses-out* 1))
           (+ *licenses-out* -1)))))
@(output)
Maximum # of licenses out: @{*maximum-licenses-out*}
Peak time(s): @{(reverse (gethash *times* *maximum-licenses-out*))}
@(end)

Output:

Maximum # of licenses out: 99
Peak time(s): 2008/10/03_08:39:34 2008/10/03_08:40:40

Ursala

Four functions are defined. Log lexes the log file, which can be accessed as a pre-declared constant without explicit I/O by being given as a compile-time command line parameter. Scan accumulates running totals of licenses in use. Search identifies the maxima, and format transforms the results to human readable form.

#import std
#import nat

log    = ^(~&hh==`O,~&tth)*FtPS sep` *F mlijobs_dot_txt
scan   = @NiX ~&ar^& ^C/~&alrhr2X ~&arlh?/~&faNlCrtPXPR ~&fabt2R
search = @lSzyCrSPp leql$^&hl@lK2; ^/length@hl ~&rS
format = ^|C\~& --' licenses in use at'@h+ %nP

#show+

main = format search scan log

output:

99 licenses in use at
2008/10/03_08:39:34
2008/10/03_08:40:40

Vedit macro language

File_Open("|(PATH_ONLY)\data\mlijobs.txt", BROWSE)

#1 = 0          // Number of licenses active
#2 = 0          // Max number of active licenses found

Repeat(ALL) {
    Search("|{OUT,IN}|W@", ADVANCE+ERRBREAK)
    if (Match_Item == 1) {                      // "OUT"
        #1++
        if (#1 > #2) {                          // new high value
            #2 = #1
            Reg_Empty(10)                       // empty the time list
        }
        if (#1 == #2) {                         // same as high value
            Reg_Copy(10, 1, APPEND)             // store time
        }
    } else {                                    // "IN"
        #1--
    }
}

Message("Maximum simultaneous license use is ")
Num_Type(#2, LEFT+NOCR)
Message(" at the following times:\n")
Reg_Type(10)

Buf_Quit(OK)

Output:

Maximum simultaneous license use is 99 at the following times:
 2008/10/03_08:39:34 for job 1833
 2008/10/03_08:40:40 for job 1837

Wren

import "io" for File

var lines = File.read("mlijobs.txt").replace("\r", "").split("\n")
var out = 0
var max = 0
var times = []
for (line in lines) {
    if (line.startsWith("License OUT")) {
        out = out + 1
        if (out >= max) {
            var sp = line.split(" ")
            if (out > max) {
                max = out
                times.clear()
            }
            times.add(sp[3])
        }
    } else if (line.startsWith("License IN")) {
        out = out - 1
    }
}

System.print("The maximum licenses that were out = %(max) at time(s):")
System.print("  " + times.join("\n  "))
Output:
The maximum licenses that were out = 99 at time(s):
  2008/10/03_08:39:34
  2008/10/03_08:40:40

XPL0

To run: maxlic < mlijobs.txt

string  0;              \use zero-terminated strings
char TimeMax(100, 25);
int  Out, OutMax, I, Ch, ListInx;
[Out:= 0;  OutMax:= 0;  ListInx:= 0;
loop    [for I:= 0 to 9-1 do
            [Ch:= ChIn(1);
            if Ch = $1A \EOF\ then quit;
            ];
        if Ch = ^O then
            [Out:= Out+1;
            if Out >= OutMax then
                [if Out > OutMax then ListInx:= 0;
                OutMax:= Out;
                for I:= 0 to 5-1 do Ch:= ChIn(1);
                for I:= 0 to 19-1 do
                    TimeMax(ListInx, I):= ChIn(1);
                TimeMax(ListInx, I):= 0;
                ListInx:= ListInx+1;
                ];
            ]
        else    \Ch = ^I as in "IN"
            Out:= Out-1;
        repeat Ch:= ChIn(1) until Ch = $0A; \LF
        ];
Text(0, "Maximum simultaneous license use is ");
IntOut(0, OutMax);
Text(0, " at the following time(s):^m^j");
for I:= 0 to ListInx-1 do
    [Text(0, @TimeMax(I,0));  CrLf(0)];
]
Output:
Maximum simultaneous license use is 99 at the following time(s):
2008/10/03_08:39:34
2008/10/03_08:40:40

zkl

Translation of: D

The referenced log file is no longer available, so, using the example log:

nOut,maxOut,maxTimes:=0,-1,List();
foreach job in (File("mlijobs.txt")){
   _,status,_,date:=job.split();
   nOut+=( if(status.toUpper()=="OUT") 1 else -1 );
   if(nOut>maxOut){ maxOut=nOut; maxTimes.clear(); }
   if(nOut==maxOut) maxTimes.append(date);
}
println(("Maximum simultaneous license use is %d at"
         " the following times:\n %s").fmt(maxOut,maxTimes.concat("\n")));
Output:
Maximum simultaneous license use is 1 at the following times:
 2008/10/03_23:51:05