       IDENTIFICATION DIVISION.
       PROGRAM-ID. text-processing-2.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT readings ASSIGN Input-File-Path
               ORGANIZATION LINE SEQUENTIAL
               FILE STATUS file-status.

       DATA DIVISION.
       FILE SECTION.
       FD  readings.
       01  reading-record.
           03  date-stamp          PIC X(10).
           03  FILLER              PIC X.
           03  input-data          PIC X(300).

       LOCAL-STORAGE SECTION.
       78  Input-File-Path         VALUE "readings.txt".
       78  Num-Data-Points         VALUE 48.

       01  file-status             PIC XX.

       01  current-line            PIC 9(5).

       01  num-date-stamps-read    PIC 9(5).
       01  read-date-stamps-area.
           03  read-date-stamps    PIC X(10) OCCURS 1 TO 10000 TIMES
                                   DEPENDING ON num-date-stamps-read
                                   INDEXED BY date-stamp-idx.

       01  offset                  PIC 999.
       01  data-len                PIC 999.
       01  data-flag               PIC X.
           88  data-not-found      VALUE "N".

       01  data-field              PIC X(25).

       01  i                       PIC 99.

       01  num-good-readings       PIC 9(5).

       01  reading-flag            PIC X.
           88 bad-reading          VALUE "B".

       01  delim                   PIC X.

       PROCEDURE DIVISION.
       DECLARATIVES.
       readings-error SECTION.
           USE AFTER ERROR ON readings

           DISPLAY "An error occurred while using " Input-File-Path
           DISPLAY "Error code " file-status
           DISPLAY "The program will terminate."

           CLOSE readings
           GOBACK
           .
       END DECLARATIVES.

       main-line.
           OPEN INPUT readings

           *> Process each line of the file.
           PERFORM FOREVER
               READ readings
                   AT END
                       EXIT PERFORM
               END-READ

               ADD 1 TO current-line

               IF reading-record = SPACES
                   DISPLAY "Line " current-line " is blank."
                   EXIT PERFORM CYCLE
               END-IF

               PERFORM check-duplicate-date-stamp

               *> Check there are 24 data pairs and see if all the
               *> readings are ok.
               INITIALIZE offset, reading-flag, data-flag
               PERFORM VARYING i FROM 1 BY 1 UNTIL Num-Data-Points < i
                   PERFORM get-next-field
                   IF data-not-found
                       DISPLAY "Line " current-line " has missing "
                           "fields."
                       SET bad-reading TO TRUE
                       EXIT PERFORM
                   END-IF

                   *> Every other data field is the instrument flag.
                   IF FUNCTION MOD(i, 2) = 0 AND NOT bad-reading
                       IF FUNCTION NUMVAL(data-field) <= 0
                           SET bad-reading TO TRUE
                       END-IF
                   END-IF

                   ADD data-len TO offset
               END-PERFORM

               IF NOT bad-reading
                   ADD 1 TO num-good-readings
               END-IF
           END-PERFORM

           CLOSE readings

           *> Display results.
           DISPLAY SPACE
           DISPLAY current-line " lines read."
           DISPLAY num-good-readings " have good readings for all "
               "instruments."

           GOBACK
           .
       check-duplicate-date-stamp.
           SEARCH read-date-stamps
               AT END
                   ADD 1 TO num-date-stamps-read
                   MOVE date-stamp
                       TO read-date-stamps (num-date-stamps-read)

               WHEN read-date-stamps (date-stamp-idx) = date-stamp
                   DISPLAY "Date " date-stamp " is duplicated at "
                       "line " current-line "."
           END-SEARCH
           .
       get-next-field.
           INSPECT input-data (offset:) TALLYING offset
               FOR LEADING X"09"

           *> The fields are normally delimited by a tab.
           MOVE X"09" TO delim
           PERFORM find-num-chars-before-delim

           *> If the delimiter was not found...
           IF FUNCTION SUM(data-len, offset) > 300
               *> The data may be delimited by a space if it is at the
               *> end of the line.
               MOVE SPACE TO delim
               PERFORM find-num-chars-before-delim

               IF FUNCTION SUM(data-len, offset) > 300
                   SET data-not-found TO TRUE
                   EXIT PARAGRAPH
               END-IF
           END-IF

           IF data-len = 0
               SET data-not-found TO TRUE
               EXIT PARAGRAPH
           END-IF

           MOVE input-data (offset:data-len) TO data-field
           .
       find-num-chars-before-delim.
           INITIALIZE data-len
           INSPECT input-data (offset:) TALLYING data-len
               FOR CHARACTERS BEFORE delim
           .


real R;
[Format(20, 20);
repeat  R:= RlIn(0);
        RlOut(0, R);
        Text(0, if R = float(fix(R)) then " is integer"
                else " is not integer");
        CrLf(0);
until   R = 0.;
]


INT no data := 0;               # Current run of consecutive flags<0 in lines of file #
INT no data max := -1;          # Max consecutive flags<0 in lines of file #
FLEX[0]STRING no data max line; # ... and line number(s) where it occurs #

REAL tot file := 0;             # Sum of file data #
INT num file := 0;              # Number of file data items with flag>0 #

# CHAR fs = " "; #
INT nf = 24;

INT upb list := nf;
FORMAT list repr = $n(upb list-1)(g", ")g$;

PROC exception = ([]STRING args)VOID:(
  putf(stand error, ($"Exception"$, $", "g$, args, $l$));
  stop
);

PROC raise io error = (STRING message)VOID:exception(("io error", message));

OP +:= = (REF FLEX []STRING rhs, STRING append)REF FLEX[]STRING: (
  HEAP [UPB rhs+1]STRING out rhs;
  out rhs[:UPB rhs] := rhs;
  out rhs[UPB rhs+1] := append;
  rhs := out rhs;
  out rhs
);

INT upb opts = 3; # these are "a68g" "./Data_Munging.a68" & "-" #
[argc - upb opts]STRING in files;
FOR arg TO UPB in files DO in files[arg] := argv(upb opts + arg) OD;

MODE FIELD = STRUCT(REAL data, INT flag);
FORMAT field repr = $2(g)$;

FOR index file TO UPB in files DO
  STRING file name = in files[index file], FILE file;
  IF open(file, file name, stand in channel) NE 0 THEN
    raise io error("Cannot open """+file name+"""") FI;
  on logical file end(file, (REF FILE f)BOOL: logical file end done);
  REAL tot line, INT num line;
  # make term(file, ", ") for CSV data #
  STRING date;
  DO
    tot line := 0;             # sum of line data #
    num line := 0;             # number of line data items with flag>0 #
    # extract field info #
    [nf]FIELD data;
    getf(file, ($10a$, date, field repr, data, $l$));

    FOR key TO UPB data DO
      FIELD field = data[key];
      IF flag OF field<1 THEN
        no data +:= 1
      ELSE
        # check run of data-absent data #
        IF no data max = no data AND no data>0 THEN
          no data max line +:= date FI;
        IF no data max<no data AND no data>0 THEN
          no data max := no data;
          no data max line := date FI;
        # re-initialise run of no data counter #
        no data := 0;
        # gather values for averaging #
        tot line +:= data OF field;
        num line +:= 1
      FI
    OD;

    # totals for the file so far #
    tot file +:= tot line;
    num file +:= num line;

    printf(($"Line: "g"  Reject: "g(-2)"  Accept: "g(-2)"  Line tot: "g(-14, 3)"  Line avg: "g(-14, 3)l$,
          date,
          UPB(data) -num line,
          num line, tot line,
          IF num line>0 THEN tot line/num line ELSE 0 FI))
  OD;
  logical file end done:
    close(file)
OD;

FORMAT plural = $b(" ", "s")$,
       p = $b("", "s")$;

upb list := UPB in files;
printf(($l"File"f(plural)"    = "$, upb list = 1, list repr, in files, $l$,
        $"Total    = "g(-0, 3)l$, tot file,
        $"Readings = "g(-0)l$, num file,
        $"Average  = "g(-0, 3)l$, tot file / num file));

upb list := UPB no data max line;
printf(($l"Maximum run"f(p)" of "g(-0)" consecutive false reading"f(p)" ends at line starting with date"f(p)": "$,
    upb list = 1, no data max, no data max = 0, upb list = 1, list repr, no data max line, $l$))


# ptest.t
use strict;
use warnings;

use Test;

my %tests;
BEGIN {
    # plan tests before loading Palindrome.pm
    %tests = (
        'A man, a plan, a canal: Panama.'           => 1,
        'My dog has fleas'                          => 0,
        "Madam, I'm Adam."                          => 1,
        '1 on 1'                                    => 0,
        'In girum imus nocte et consumimur igni'    => 1,
        ''                                          => 1,
    );

    # plan 4 tests per string
    plan tests => (keys(%tests) * 4);
}

use Palindrome;

for my $key (keys %tests) {
    $_ = lc $key;  # convert to lowercase
    s/[\W_]//g;    # keep only alphanumeric characters

    my $expect = $tests{$key};
    my $note = ("\"$key\" should " . ($expect ? '' : 'not ') .
                "be a palindrome.");

    ok palindrome == $expect, 1, "palindrome: $note";
    ok palindrome_c == $expect, 1, "palindrome_c: $note";
    ok palindrome_r == $expect, 1, "palindrome_r: $note";
    ok palindrome_e == $expect, 1, "palindrome_e: $note";
}


with javascript_semantics
requires("0.8.2")

function is_palindrome(sequence s)
    return s==reverse(s)
end function

--set_test_verbosity(TEST_QUIET)                -- default, no output when third call removed
--set_test_verbosity(TEST_SUMMARY)              -- first and last line only [w or w/o ""]
--set_test_verbosity(TEST_SHOW_FAILED)          -- first and last two lines only
set_test_verbosity(TEST_SHOW_ALL)               -- as shown in last two cases below

--set_test_abort(TEST_ABORT)                    -- abort(1) on failure, after showing the summary
--set_test_abort(TEST_QUIET)                    -- quietly carry on, the default
--set_test_abort(TEST_CRASH)                    -- abort immmediately on failure (w/o summary)

--set_test_pause(TEST_PAUSE_FAIL)               -- pause on failure, the default
--set_test_pause(TEST_QUIET)                    -- disable pause on failure
--set_test_pause(TEST_PAUSE)                    -- always pause

set_test_module("palindromes")                  -- optional, w/o first line is omitted

test_true(is_palindrome("abba"),"abba")
test_true(is_palindrome("abba"))                -- no desc makes success hidden...
                                                -- ...and failure somewhat laconic
test_false(is_palindrome("abc"),"not abc")
test_true(is_palindrome("failure"),"failure")

test_summary()


(de palindrome? (S)
   (= (setq S (chop S)) (reverse S)) )

(test T (palindrome? "racecar"))
(test NIL (palindrome? "ferrari"))


void main() {
    import std.stdio, std.array, std.string, std.regex, std.conv,
           std.algorithm;

    auto rxDate = `^\d\d\d\d-\d\d-\d\d$`.regex;
    // Works but eats lot of RAM in DMD 2.064.
    // auto rxDate = ctRegex!(`^\d\d\d\d-\d\d-\d\d$`);

    int[string] repeatedDates;
    int goodReadings;
    foreach (string line; "readings.txt".File.lines) {
        try {
            auto parts = line.split;
            if (parts.length != 49)
                throw new Exception("Wrong column count");
            if (parts[0].match(rxDate).empty)
                throw new Exception("Date is wrong");
            repeatedDates[parts[0]]++;
            bool noProblem = true;
            for (int i = 1; i < 48; i += 2) {
                if (parts[i + 1].to!int < 1)
                    // don't break loop because it's validation too.
                    noProblem = false;
                if (!parts[i].isNumeric)
                    throw new Exception("Reading is wrong: "~parts[i]);
            }
            if (noProblem)
                goodReadings++;
        } catch(Exception ex) {
            writefln(`Problem in line "%s": %s`, line, ex);
        }
    }

    writefln("Duplicated timestamps: %-(%s, %)",
            repeatedDates.byKey.filter!(k => repeatedDates[k] > 1));
    writeln("Good reading records: ", goodReadings);
}


# Author AlephX Aug 17 2011

SetFormat, float, 4.2
SetFormat, FloatFast, 4.2

data = %A_scriptdir%\readings.txt
result = %A_scriptdir%\results.txt
totvalid := 0
totsum := 0
totavg:= 0

Loop, Read, %data%, %result%
	{
	sum := 0
	Valid := 0
	Couples := 0
	Lines := A_Index
    Loop, parse, A_LoopReadLine, %A_Tab%
		{
        ;MsgBox, Field number %A_Index% is %A_LoopField%
		if A_index = 1
			{
			Date := A_LoopField
			Counter := 0
			}
		else
			{
			Counter++
			couples := Couples + 0.5
			if Counter = 1
				{
				value := A_LoopField / 1
				}
			else
				{
				if A_loopfield > 0
					{
					Sum := Sum + value
					Valid++

					if (wrong > maxwrong)
						{
						maxwrong := wrong
						lastwrongdate := currwrongdate
						startwrongdate := firstwrongdate
						startoccurrence := firstoccurrence
						lastoccurrence := curroccurrence
						}
					wrong := 0
					}
				else
					{
					wrong++
					currwrongdate := date
					curroccurrence := (A_index-1) / 2
					if (wrong = 1)
						{
						firstwrongdate := date
						firstoccurrence := curroccurrence
						}
					}
				Counter := 0
				}
			}
		}
	avg := sum / valid
	TotValid := Totvalid+valid
	TotSum := Totsum+sum
	FileAppend, Day: %date% sum: %sum% avg: %avg% Readings: %valid%/%couples%`n
	}

Totavg := TotSum / TotValid
FileAppend, `n`nDays %Lines%`nMaximal wrong readings: %maxwrong% from %startwrongdate% at %startoccurrence% to %lastwrongdate% at %lastoccurrence%`n`n, %result%
FileAppend, Valid readings: %TotValid%`nTotal Value: %TotSUm%`nAverage: %TotAvg%, %result%


palindrome(Word) :- name(Word,List), reverse(List,List).

:- begin_tests(palindrome).

test(valid_palindrome) :- palindrome('ingirumimusnocteetconsumimurigni').
test(invalid_palindrome, [fail]) :- palindrome('this is not a palindrome').

:- end_tests(palindrome).


Macro DoubleQuote
  ; Needed for the Assert-Macro below
  "                             ; " second dlbquote to prevent Rosettas misshighlighting of following code. Remove comment before execution!
EndMacro
Macro Assert(TEST,MSG="")
  CompilerIf #PB_Compiler_Debugger
    If Not (TEST)
      If MSG<>"": Debug MSG: EndIf
      Temp$="Fail: "+DoubleQuote#TEST#DoubleQuote
      Debug Temp$+", Line="+Str(#PB_Compiler_Line)+" in "+#PB_Compiler_File
      CallDebugger
    EndIf
  CompilerEndIf
EndMacro

Procedure IsPalindrome(StringToTest.s)
  If StringToTest=ReverseString(StringToTest)
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

text1$="racecar"
text2$="wisconsin"
Assert(IsPalindrome(text1$), "Catching this would be a fail")
Assert(IsPalindrome(text2$), "Catching this is correct")


def is_palindrome(s):
    '''
        >>> is_palindrome('')
        True
        >>> is_palindrome('a')
        True
        >>> is_palindrome('aa')
        True
        >>> is_palindrome('baa')
        False
        >>> is_palindrome('baab')
        True
        >>> is_palindrome('ba_ab')
        True
        >>> is_palindrome('ba_ ab')
        False
        >>> is_palindrome('ba _ ab')
        True
        >>> is_palindrome('ab'*2)
        False
        >>> x = 'ab' *2**15
        >>> len(x)
        65536
        >>> xreversed = x[::-1]
        >>> is_palindrome(x+xreversed)
        True
        >>> len(x+xreversed)
        131072
        >>>
    '''
    return s == s[::-1]

def _test():
    import doctest
    doctest.testmod()
    #doctest.testmod(verbose=True)

if __name__ == "__main__":
    _test()


checkTrue(palindroc("aba"))  # TRUE
checkTrue(!palindroc("ab"))  # TRUE
checkException(palindroc())  # TRUE
checkTrue(palindroc(""))     # Error.  Uh-oh, there's a bug in the function


class
	APPLICATION

create
	make

feature

	make
			-- Finds double date stamps and wrong formats.
		local
			found: INTEGER
			double: STRING
		do
			read_wordlist
			fill_hash_table
			across
				hash as h
			loop
				if h.key.has_substring ("_double") then
					io.put_string ("Double date stamp: %N")
					double := h.key
					double.remove_tail (7)
					io.put_string (double)
					io.new_line
				end
				if h.item.count /= 24 then
					io.put_string (h.key.out + " has the wrong format. %N")
					found := found + 1
				end
			end
			io.put_string (found.out + " records have not 24 readings.%N")
			good_records
		end

	good_records
			-- Number of records that have flag values > 0 for all readings.
		local
			count, total: INTEGER
			end_date: STRING
		do
			create end_date.make_empty
			across
				hash as h
			loop
				count := 0
				across
					h.item as d
				loop
					if d.item.flag > 0 then
						count := count + 1
					end
				end
				if count = 24 then
					total := total + 1
				end
			end
			io.put_string ("%NGood records: " + total.out + ". %N")
		end

	original_list: STRING = "readings.txt"

	read_wordlist
			--Preprocesses data in '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]

	fill_hash_table
			--Fills 'hash' using the date as key.
		local
			by_dates: LIST [STRING]
			date: STRING
			data_tup: TUPLE [val: REAL; flag: INTEGER]
			data_arr: ARRAY [TUPLE [val: REAL; flag: INTEGER]]
			i: INTEGER
		do
			create hash.make (data.count)
			across
				data as d
			loop
				if not d.item.is_empty then
					by_dates := d.item.split ('%T')
					date := by_dates [1]
					by_dates.prune (date)
					create data_tup
					create data_arr.make_empty
					from
						i := 1
					until
						i > by_dates.count - 1
					loop
						data_tup := [by_dates [i].to_real, by_dates [i + 1].to_integer]
						data_arr.force (data_tup, data_arr.count + 1)
						i := i + 2
					end
					hash.put (data_arr, date)
					if not hash.inserted then
						date.append ("_double")
						hash.put (data_arr, date)
					end
				end
			end
		end

	hash: HASH_TABLE [ARRAY [TUPLE [val: REAL; flag: INTEGER]], STRING]

end


BEGIN{
  nodata = 0;             # Current run of consecutive flags<0 in lines of file
  nodata_max=-1;          # Max consecutive flags<0 in lines of file
  nodata_maxline="!";     # ... and line number(s) where it occurs
}
FNR==1 {
  # Accumulate input file names
  if(infiles){
    infiles = infiles "," infiles
  } else {
    infiles = FILENAME
  }
}
{
  tot_line=0;             # sum of line data
  num_line=0;             # number of line data items with flag>0

  # extract field info, skipping initial date field
  for(field=2; field<=NF; field+=2){
    datum=$field;
    flag=$(field+1);
    if(flag<1){
      nodata++
    }else{
      # check run of data-absent fields
      if(nodata_max==nodata && (nodata>0)){
        nodata_maxline=nodata_maxline ", " $1
      }
      if(nodata_max<nodata && (nodata>0)){
        nodata_max=nodata
        nodata_maxline=$1
      }
      # re-initialise run of nodata counter
      nodata=0;
      # gather values for averaging
      tot_line+=datum
      num_line++;
    }
  }

  # totals for the file so far
  tot_file += tot_line
  num_file += num_line

  printf "Line: %11s  Reject: %2i  Accept: %2i  Line_tot: %10.3f  Line_avg: %10.3f\n", \
         $1, ((NF -1)/2) -num_line, num_line, tot_line, (num_line>0)? tot_line/num_line: 0

  # debug prints of original data plus some of the computed values
  #printf "%s  %15.3g  %4i\n", $0, tot_line, num_line
  #printf "%s\n  %15.3f  %4i  %4i  %4i  %s\n", $0, tot_line, num_line,  nodata, nodata_max, nodata_maxline

}

END{
  printf "\n"
  printf "File(s)  = %s\n", infiles
  printf "Total    = %10.3f\n", tot_file
  printf "Readings = %6i\n", num_file
  printf "Average  = %10.3f\n", tot_file / num_file

  printf "\nMaximum run(s) of %i consecutive false readings ends at line starting with date(s): %s\n", nodata_max, nodata_maxline
}


@echo off
setlocal ENABLEDELAYEDEXPANSION
set maxrun=    0
set maxstart=
set maxend=
set notok=0
set inputfile=%1
for /F "tokens=1,*" %%i in (%inputfile%) do (
    set date=%%i
    call :processline %%j
)

echo\
echo max false: %maxrun%  from %maxstart% until %maxend%

goto :EOF

:processline
set sum=0000
set count=0
set hour=1
:loop
if "%1"=="" goto :result
set num=%1
if "%2"=="1" (
    if "%notok%" NEQ "0" (
        set notok=     !notok!
        if /I "!notok:~-5!" GTR "%maxrun%" (
            set maxrun=!notok:~-5!
            set maxstart=%nok0date% %nok0hour%
            set maxend=%nok1date% %nok1hour%
        )
        set notok=0
    )
    set /a sum+=%num:.=%
    set /a count+=1
) else (
    if "%notok%" EQU "0" (
        set nok0date=%date%
        set nok0hour=%hour%
    ) else (
        set nok1date=%date%
        set nok1hour=%hour%
    )
    set /a notok+=1
)
shift
shift
set /a hour+=1
goto :loop

:result
if "%count%"=="0" (
    set mean=0
) else (
    set /a mean=%sum%/%count%
)
if "%mean%"=="0" set mean=0000
if "%sum%"=="0" set sum=0000
set mean=%mean:~0,-3%.%mean:~-3%
set sum=%sum:~0,-3%.%sum:~-3%
set count=   %count%
set sum=    %sum%
set mean=    %mean%
echo Line: %date% Accept: %count:~-3%  tot: %sum:~-8%  avg: %mean:~-8%

goto :EOF


#lang racket
(module+ test (require rackunit))

;; from the Palindrome entry
(define (palindromb str)
  (let* ([lst (string->list (string-downcase str))]
         [slst (remove* '(#\space) lst)])
    (string=? (list->string (reverse slst)) (list->string slst))))

;; this test module is not loaded unless it is
;; specifically requested for testing, allowing internal
;; unit test specification
(module+ test
  (check-true (palindromb "racecar"))
  (check-true (palindromb "avoova"))
  (check-false (palindromb "potato")))


use Test;

sub palin( Str $string) { so $string.lc.comb(/\w/) eq  $string.flip.lc.comb(/\w/) }

my %tests =
    'A man, a plan, a canal: Panama.'           => True,
    'My dog has fleas'                          => False,
    "Madam, I'm Adam."                          => True,
    '1 on 1'                                    => False,
    'In girum imus nocte et consumimur igni'    => True,
    ''                                          => True,
    ;

plan %tests.elems;

for %tests.kv -> $test, $expected-result {
    is palin($test), $expected-result,
        "\"$test\" is {$expected-result??''!!'not '}a palindrome.";
}


needs assertion'
needs hash'

: palindrome? ( $-f ) dup ^hash'hash [ ^strings'reverse ^hash'hash ] dip = ;

with assertion'
: t0 ( - ) "hello" palindrome? 0 assert=  ; assertion
: t1 ( - ) "ingirumimusnocteetconsumimurigni" palindrome? -1 assert=  ; assertion
: test ( - ) t0 t1 ;
test


-module( text_processing2 ).

-export( [task/0] ).

task() ->
	Name = "priv/readings.txt",
	try
	File_contents = text_processing:file_contents( Name ),
	[correct_field_format(X) || X<- File_contents],
	{_Previous, Duplicates} = lists:foldl( fun date_duplicates/2, {"", []}, File_contents ),
	io:fwrite( "Duplicates: ~p~n", [Duplicates] ),
	Good = [X || X <- File_contents, is_all_good_readings(X)],
	io:fwrite( "Good readings: ~p~n", [erlang:length(Good)] )

	catch
	_:Error ->
		io:fwrite( "Error: Failed when checking ~s: ~p~n", [Name, Error] )
	end.

correct_field_format( {_Date, Value_flags} ) ->
	Corret_number = value_flag_records(),
	{correct_field_format, Corret_number} = {correct_field_format, erlang:length(Value_flags)}.

date_duplicates( {Date, _Value_flags}, {Date, Acc} ) -> {Date, [Date | Acc]};
date_duplicates( {Date, _Value_flags}, {_Other, Acc} ) -> {Date, Acc}.

is_all_good_readings( {_Date, Value_flags} ) -> value_flag_records() =:= erlang:length( [ok || {_Value, ok} <-  Value_flags] ).

value_flag_records() -> 24.


      file% = OPENIN("readings.txt")
      IF file% = 0 THEN PRINT "Could not open test data file" : END

      Total = 0
      Count% = 0
      BadMax% = 0
      bad% = 0
      WHILE NOT EOF#file%
        text$ = GET$#file%
        IF text$<>"" THEN
          tab% = INSTR(text$, CHR$(9))
          date$ = LEFT$(text$, tab% - 1)
          acc = 0
          cnt% = 0
          FOR field% = 1 TO 24
            dval = VALMID$(text$, tab%+1)
            tab% = INSTR(text$, CHR$(9), tab%+1)
            flag% = VALMID$(text$, tab%+1)
            tab% = INSTR(text$, CHR$(9), tab%+1)
            IF flag% > 0 THEN
              acc += dval
              cnt% += 1
              bad% = 0
            ELSE
              bad% += 1
              IF bad% > BadMax% BadMax% = bad% : BadDate$ = date$
            ENDIF
          NEXT field%
          @% = &90A
          PRINT "Date: " date$ "  Good = "; cnt%, "  Bad = "; 24-cnt%, ;
          @% = &20308
          IF cnt% THEN PRINT "  Total = " acc "  Mean = " acc / cnt% ;
          PRINT
          Total += acc
          Count% += cnt%
        ENDIF
      ENDWHILE
      CLOSE #file%
      PRINT ' "Grand total = " ; Total
      PRINT "Number of valid readings = " ; STR$(Count%)
      PRINT "Overall mean = " ; Total / Count%
      @% = &90A
      PRINT '"Longest run of bad readings = " ; BadMax% " ending " BadDate$


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int badHrs, maxBadHrs;

static double hrsTot = 0.0;
static int rdgsTot = 0;
char bhEndDate[40];

int mungeLine( char *line, int lno, FILE *fout )
{
    char date[40], *tkn;
    int   dHrs, flag, hrs2, hrs;
    double hrsSum;
    int   hrsCnt = 0;
    double avg;

    tkn = strtok(line, ".");
    if (tkn) {
        int n = sscanf(tkn, "%s %d", &date, &hrs2);
        if (n<2) {
            printf("badly formated line - %d %s\n", lno, tkn);
            return 0;
        }
        hrsSum = 0.0;
        while( tkn= strtok(NULL, ".")) {
            n = sscanf(tkn,"%d %d %d", &dHrs, &flag, &hrs);
            if (n>=2) {
                if (flag > 0) {
                    hrsSum += 1.0*hrs2 + .001*dHrs;
                    hrsCnt += 1;
                    if (maxBadHrs < badHrs) {
                        maxBadHrs = badHrs;
                        strcpy(bhEndDate, date);
                    }
                    badHrs = 0;
                }
                else {
                    badHrs += 1;
                }
                hrs2 = hrs;
            }
            else {
                printf("bad file syntax line %d: %s\n",lno, tkn);
            }
        }
        avg = (hrsCnt > 0)? hrsSum/hrsCnt : 0.0;
        fprintf(fout, "%s  Reject: %2d  Accept: %2d  Average: %7.3f\n",
                date, 24-hrsCnt, hrsCnt, hrsSum/hrsCnt);
        hrsTot += hrsSum;
        rdgsTot += hrsCnt;
    }
    return 1;
}

int main()
{
    FILE *infile, *outfile;
    int lineNo = 0;
    char line[512];
    const char *ifilename = "readings.txt";
    outfile = fopen("V0.txt", "w");

    infile = fopen(ifilename, "rb");
    if (!infile) {
        printf("Can't open %s\n", ifilename);
        exit(1);
    }
    while (NULL != fgets(line, 512, infile)) {
        lineNo += 1;
        if (0 == mungeLine(line, lineNo, outfile))
            printf("Bad line at %d",lineNo);
    }
    fclose(infile);

    fprintf(outfile, "File:     %s\n", ifilename);
    fprintf(outfile, "Total:    %.3f\n", hrsTot);
    fprintf(outfile, "Readings: %d\n", rdgsTot);
    fprintf(outfile, "Average:  %.3f\n", hrsTot/rdgsTot);
    fprintf(outfile, "\nMaximum number of consecutive bad readings is %d\n", maxBadHrs);
    fprintf(outfile, "Ends on date %s\n", bhEndDate);
    fclose(outfile);
    return 0;
}


/*REXX program stresses various REXX functions (BIFs), many BIFs are used as variables. */
signal=(interpret=value); value=(interpret<parse); do upper=value to value; end
exit=upper*upper*upper*upper-value-upper; say=' '; return=say say say; with.=signal
do then=value to exit; pull=''; do otherwise= upper to then-,
value; select=otherwise-value; if.otherwise=with.otherwise+with.select; end
if.value=value; if.then=value; do otherwise=value to exit-then; pull=pull,
say''say; end; do otherwise=value to then; pull=pull center(if.otherwise,,
length(return)); end; say pull; do otherwise=value to exit; with.otherwise=,
if.otherwise; end; end; exit 0                   /*stick a fork in it,  we're all done. */


  assert(IsPalindrome("racecar"))
  assert(IsPalindrome("alice"))


def palindrome?(s)
  s == s.reverse
end

require 'test/unit'
class MyTests < Test::Unit::TestCase
  def test_palindrome_ok
    assert(palindrome? "aba")
  end

  def test_palindrome_nok
    assert_equal(false, palindrome?("ab"))
  end

  def test_object_without_reverse
    assert_raise(NoMethodError) {palindrome? 42}
  end

  def test_wrong_number_args
    assert_raise(ArgumentError) {palindrome? "a", "b"}
  end

  def test_show_failing_test
    assert(palindrome?("ab"), "this test case fails on purpose")
  end
end


let file = @"readings.txt"

let dates = HashSet(HashIdentity.Structural)
let mutable ok = 0

do
  for line in System.IO.File.ReadAllLines file do
    match String.split [' '; '\t'] line with
    | [] -> ()
    | date::xys ->
        if dates.Contains date then
          printf "Date %s is duplicated\n" date
        else
          dates.Add date
        let f (b, t) h = not b, if b then int h::t else t
        let _, states = Seq.fold f (false, []) xys
        if Seq.forall (fun s -> s >= 1) states then
          ok <- ok + 1
  printf "%d records were ok\n" ok


USING: io io.encodings.ascii io.files kernel math math.parser
prettyprint sequences sequences.extras sets splitting ;

: check-format ( seq -- )
    [ " \t" split length 49 = ] all?
    "Format okay." "Format not okay." ? print ;

"readings.txt" ascii file-lines [ check-format ] keep
[ "Duplicates:" print [ "\t" split1 drop ] map duplicates . ]
[ [ " \t" split rest <odds> [ string>number 0 <= ] none? ] count ]
bi pprint " records were good." print


#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iomanip>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>

using std::cout;
using std::endl;
const int NumFlags = 24;

int main()
{
    std::fstream file("readings.txt");

    int badCount = 0;
    std::string badDate;
    int badCountMax = 0;
    while(true)
    {
        std::string line;
        getline(file, line);
        if(!file.good())
            break;

        std::vector<std::string> tokens;
        boost::algorithm::split(tokens, line, boost::is_space());

        if(tokens.size() != NumFlags * 2 + 1)
        {
            cout << "Bad input file." << endl;
            return 0;
        }

        double total = 0.0;
        int accepted = 0;
        for(size_t i = 1; i < tokens.size(); i += 2)
        {
            double val = boost::lexical_cast<double>(tokens[i]);
            int flag = boost::lexical_cast<int>(tokens[i+1]);
            if(flag > 0)
            {
                total += val;
                ++accepted;
                badCount = 0;
            }
            else
            {
                ++badCount;
                if(badCount > badCountMax)
                {
                    badCountMax = badCount;
                    badDate = tokens[0];
                }
            }
        }

        cout << tokens[0];
        cout << "  Reject: " << std::setw(2) << (NumFlags - accepted);
        cout << "  Accept: " << std::setw(2) << accepted;
        cout << "  Average: " << std::setprecision(5) << total / accepted << endl;
    }
    cout << endl;
    cout << "Maximum number of consecutive bad readings is " << badCountMax << endl;
    cout << "Ends on date " << badDate << endl;
}


/// Tests if the given string slice is a palindrome (with the respect to
/// codepoints, not graphemes).
///
/// # Examples
///
/// ```
/// # use playground::palindrome::is_palindrome;
/// assert!(is_palindrome("abba"));
/// assert!(!is_palindrome("baa"));
/// ```
pub fn is_palindrome(s: &str) -> bool {
    let half = s.len();
    s.chars().take(half).eq(s.chars().rev().take(half))
}

#[cfg(test)]
mod tests {

    use super::is_palindrome;

    #[test]
    fn test_is_palindrome() {
        assert!(is_palindrome("abba"));
    }
}


import org.scalacheck._
import Prop._
import Gen._

object PalindromeCheck extends Properties("Palindrome") {
  property("A string concatenated with its reverse is a palindrome") =
    forAll { s: String => isPalindrome(s + s.reverse) }

  property("A string concatenated with any character and its reverse is a palindrome") =
    forAll { (s: String, c: Char) => isPalindrome(s + c + s.reverse) }

  property("If the first half of a string is equal to the reverse of its second half, it is a palindrome") =
    forAll { (s: String) => s.take(s.length / 2) != s.drop((s.length + 1) / 2).reverse || isPalindrome(s) }

  property("If the first half of a string is different than the reverse of its second half, it isn't a palindrome") =
    forAll { (s: String) => s.take(s.length / 2) == s.drop((s.length + 1) / 2).reverse || !isPalindrome(s) }

}


(import (srfi 64))
(test-begin "palindrome-tests")
(test-assert (palindrome? "ingirumimusnocteetconsumimurigni"))
(test-assert (not (palindrome? "This is not a palindrome")))
(test-equal #t (palindrome? "ingirumimusnocteetconsumimurigni")) ; another of several test functions
(test-end)


CREATE OR REPLACE PROCEDURE TEST_MY_TEST()
  BEGIN
    DECLARE EXPECTED INTEGER;
    DECLARE ACTUAL INTEGER;
    CALL DB2UNIT.REGISTER_MESSAGE('My first test');
    SET EXPECTED = 2;
    SET ACTUAL = 1+1;
    CALL DB2UNIT.ASSERT_INT_EQUALS('Same value', EXPECTED, ACTUAL);
  END @


Crunches a set of hourly data. Starts with a date, then 24 pairs of value,indicator for that day, on one line.
      INTEGER Y,M,D		!Year, month, and day.
      INTEGER GOOD(24,2)	!The indicators.
      REAL*8     V(24,2)	!The grist.
      CHARACTER*10 DATE(2)	!Along with the starting date.
      INTEGER IT,TI		!A flipper and its antiflipper.
      INTEGER NV		!Number of entirely good records.
      INTEGER I,NREC,HIC	!Some counters.
      LOGICAL INGOOD		!State flipper for the runs of data.
      INTEGER IN,MSG		!I/O mnemonics.
      CHARACTER*666 ACARD	!Scratchpad, of sufficient length for all expectation.
      IN = 10		!Unit number for the input file.
      MSG = 6		!Output.
      OPEN (IN,FILE="Readings1.txt", FORM="FORMATTED",	!This should be a function.
     1 STATUS ="OLD",ACTION="READ")			!Returning success, or failure.
      NV = 0		!No pure records seen.
      NREC = 0		!No records read.
      HIC = 0		!Provoking no complaints.
      DATE = "snargle"	!No date should look like this!
      IT = 2		!Syncopation for the 1-2 flip flop.
Chew into the file.
   10 READ (IN,11,END=100,ERR=666) L,ACARD(1:MIN(L,LEN(ACARD)))	!With some protection.
      NREC = NREC + 1		!So, a record has been read.
   11 FORMAT (Q,A)		!Obviously, Q ascertains the length of the record being read.
      READ (ACARD,12,END=600,ERR=601) Y,M,D	!The date part is trouble, as always.
   12 FORMAT (I4,2(1X,I2))				!Because there are no delimiters between the parts.
      TI = IT			!Thus finger the previous value.
      IT = 3 - IT		!Flip between 1 and 2.
      DATE(IT) = ACARD(1:10)	!Save the date field.
      READ (ACARD(11:L),*,END=600,ERR=601) (V(I,IT),GOOD(I,IT),I = 1,24)	!But after the date, delimiters abound.
Comparisons. Should really convert the date to a daynumber, check it by reversion, and then check for + 1 day only.
   20 IF (DATE(IT).EQ.DATE(TI)) THEN	!Same date?
        IF (ALL(V(:,IT)   .EQ.V(:,TI)) .AND.	!Yes. What about the data?
     1      ALL(GOOD(:,IT).EQ.GOOD(:,TI))) THEN	!This disregards details of the spacing of the data.
          WRITE (MSG,21) NREC,DATE(IT),"same."	!Also trailing zeroes, spurious + signs, blah blah.
   21     FORMAT ("Record",I8," Duplicate date field (",A,"), data ",A)	!Say it.
         ELSE				!But if they're not all equal,
          WRITE (MSG,21) NREC,DATE(IT),"different!"	!They're different!
        END IF					!So much for comparing the data.
      END IF				!So much for just comparing the date's text.
      IF (ALL(GOOD(:,IT).GT.0)) NV = NV + 1	!A fully healthy record, either way?
      GO TO 10		!More! More! I want more!!

Complaints. Should really distinguish between trouble in the date part and in the data part.
  600 WRITE (MSG,*) '"END" declared - insufficient data?'	!Not enough numbers, presumably.
      GO TO 602				!Reveal the record.
  601 WRITE (MSG,*) '"ERR" declared - improper number format?'	!Ah, but which number?
  602 WRITE (MSG,603) NREC,L,ACARD(1:L)	!Anyway, reveal the uninterpreted record.
  603 FORMAT("Record",I8,", length ",I0," reads ",A)	!Just so.
      HIC = HIC + 1			!This may grow into a habit.
      IF (HIC.LE.12) GO TO 10		!But if not yet, try the next record.
      STOP "Enough distaste."		!Or, give up.
  666 WRITE (MSG,101) NREC,"format error!"	!For A-style data? Should never happen!
      GO TO 900				!But if it does, give up!

Closedown.
  100 WRITE (MSG,101) NREC,"then end-of-file"	!Discovered on the next attempt.
  101 FORMAT ("Record",I8,": ",A)		!A record number plus a remark.
      WRITE (MSG,102) NV	!The overall results.
  102 FORMAT ("  with",I8," having all values good.")	!This should do.
  900 CLOSE(IN)		!Done.
      END	!Spaghetti rules.


(ns rosettacode.textprocessing1
  (:require [clojure.string :as str]))

(defn parse-line [s]
  (let [[date & data-toks] (str/split s #"\s+")]
    {:date date
     :hour-vals (for [[v flag] (partition 2 data-toks)]
                  {:val  (Double. v)
                   :flag (Long. flag)})}))

(defn analyze-line [m]
  (let [valid?  (fn [rec] (pos? (:flag rec)))
        data    (->> (filter valid? (:hour-vals m))
                     (map :val))
        n-vals  (count data)
        sum     (reduce + data)]
    {:date   (:date m)
     :n-vals n-vals
     :sum    (double sum)
     :avg    (if (zero? n-vals) 0.0 (/ sum n-vals))
     :gaps   (for [hr (:hour-vals m)]
               {:gap? (not (valid? hr)) :date (:date m)})}))

(defn print-line [m]
  (println (format "%s: %d valid, sum: %7.3f, mean: %6.3f"
                   (:date   m)
                   (:n-vals m)
                   (:sum m)
                   (:avg m))))

(defn process-line [s]
  (let [m         (parse-line s)
        line-info (analyze-line m)]
    (print-line line-info)
    line-info))

(defn update-file-stats [file-m line-m]
  (let [append (fn [a b] (reduce conj a b))]
    (-> file-m
        (update-in [:sum]    + (:sum line-m))
        (update-in [:n-vals] + (:n-vals line-m))
        (update-in [:gap-recs] append (:gaps line-m)))))

(defn process-file [path]
  (let [file-lines (->> (slurp path)
                        str/split-lines)
        summary (reduce (fn [res line]
                          (update-file-stats res (process-line line)))
                        {:sum 0
                         :n-vals 0
                         :gap-recs []}
                        file-lines)
        max-gap (->> (partition-by :gap? (:gap-recs summary))
                     (filter #(:gap? (first %)))
                     (sort-by count >)
                     first)]
    (println (format "Sum: %f\n# Values: %d\nAvg: %f"
                     (:sum summary)
                     (:n-vals summary)
                     (/ (:sum summary) (:n-vals summary))))
    (println (format "Max gap of %d recs started on %s"
                     (count max-gap)
                     (:date (first max-gap))))))


import Cocoa
import XCTest

class PalindromTests: XCTestCase {

    override func setUp() {
        super.setUp()

    }

    override func tearDown() {
        super.tearDown()
    }

    func testPalindrome() {
        // This is an example of a functional test case.
        XCTAssert(isPalindrome("abcba"), "Pass")
        XCTAssert(isPalindrome("aa"), "Pass")
        XCTAssert(isPalindrome("a"), "Pass")
        XCTAssert(isPalindrome(""), "Pass")
        XCTAssert(isPalindrome("ab"), "Pass") // Fail
        XCTAssert(isPalindrome("aa"), "Pass")
        XCTAssert(isPalindrome("abcdba"), "Pass") // Fail
    }

    func testPalindromePerformance() {
        // This is an example of a performance test case.
        self.measureBlock() {
            var _is = isPalindrome("abcba")
        }
    }
}


templates palindrome
  [$...] -> #
  when <=$(last..first:-1)> do '$...;' !
end palindrome

test 'palindrome filter'
  assert 'rotor' -> palindrome <='rotor'> 'rotor is a palindrome'
  assert ['rosetta' -> palindrome] <=[]> 'rosetta is not a palindrome'
end 'palindrome filter'


package require tcltest 2
source palindrome.tcl; # Assume that this is what loads the implementation of ‘palindrome’

tcltest::test palindrome-1 {check for palindromicity} -body {
    palindrome abcdedcba
} -result 1
tcltest::test palindrome-2 {check for non-palindromicity} -body {
    palindrome abcdef
} -result 0
tcltest::test palindrome-3 {check for palindrome error} -body {
    palindrome
} -returnCodes error -result "wrong # args: should be \"palindrome s\""

tcltest::cleanupTests


#!/bin/bash

is_palindrome() {
  local s1=$1
  local s2=$(echo $1 | tr -d "[ ,!:;.'\"]" | tr '[A-Z]' '[a-z]')

  if [[ $s2 = $(echo $s2 | rev) ]]
  then
     echo "[$s1] is a palindrome"
  else
     echo "[$s1] is NOT a palindrome"
  fi
}


Option Explicit

Sub Test_a_function()
Dim a, i&
    a = Array("abba", "mom", "dennis sinned", "Un roc lamina l animal cornu", "palindrome", "ba _ ab", "racecars", "racecar", "wombat", "in girum imus nocte et consumimur igni")
    For i = 0 To UBound(a)
        Debug.Print a(i) & " is a palidrome ? " & IsPalindrome(CStr(a(i)))
    Next
End Sub

Function IsPalindrome(txt As String) As Boolean
Dim tempTxt As String
    tempTxt = LCase(Replace(txt, " ", ""))
    IsPalindrome = (tempTxt = StrReverse(tempTxt))
End Function


package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

const (
	filename   = "readings.txt"
	readings   = 24             // per line
	fields     = readings*2 + 1 // per line
	dateFormat = "2006-01-02"
)

func main() {
	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	var allGood, uniqueGood int
	// map records not only dates seen, but also if an all-good record was
	// seen for the key date.
	m := make(map[time.Time]bool)
	s := bufio.NewScanner(file)
	for s.Scan() {
		f := strings.Fields(s.Text())
		if len(f) != fields {
			log.Fatal("unexpected format,", len(f), "fields.")
		}
		ts, err := time.Parse(dateFormat, f[0])
		if err != nil {
			log.Fatal(err)
		}
		good := true
		for i := 1; i < fields; i += 2 {
			flag, err := strconv.Atoi(f[i+1])
			if err != nil {
				log.Fatal(err)
			}
			if flag > 0 { // value is good
				_, err := strconv.ParseFloat(f[i], 64)
				if err != nil {
					log.Fatal(err)
				}
			} else { // value is bad
				good = false
			}
		}
		if good {
			allGood++
		}
		previouslyGood, seen := m[ts]
		if seen {
			fmt.Println("Duplicate datestamp:", f[0])
		}
		m[ts] = previouslyGood || good
		if !previouslyGood && good {
			uniqueGood++
		}
	}
	if err := s.Err(); err != nil {
		log.Fatal(err)
	}

	fmt.Println("\nData format valid.")
	fmt.Println(allGood, "records with good readings for all instruments.")
	fmt.Println(uniqueGood,
		"unique dates with good readings for all instruments.")
}


       IDENTIFICATION DIVISION.
       PROGRAM-ID. data-munging.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT input-file ASSIGN TO INPUT-FILE-PATH
               ORGANIZATION LINE SEQUENTIAL
               FILE STATUS file-status.

       DATA DIVISION.
       FILE SECTION.
       FD  input-file.
       01  input-record.
           03  date-stamp          PIC X(10).
           03  FILLER              PIC X.
           *> Curse whoever decided to use tabs and variable length
           *> data in the file!
           03  input-data-pairs    PIC X(300).

       WORKING-STORAGE SECTION.
       78  INPUT-FILE-PATH         VALUE "readings.txt".

       01  file-status             PIC 99.
           88 file-is-ok           VALUE 0.
           88 end-of-file          VALUE 10.

       01  data-pair.
           03  val                 PIC 9(3)V9(3).
           03  flag                PIC S9.
               88  invalid-flag    VALUE -9 THRU 0.

       01  val-length              PIC 9.
       01  flag-length             PIC 9.
       01  offset                  PIC 99.

       01  day-total               PIC 9(5)V9(3).
       01  grand-total             PIC 9(8)V9(3).
       01  mean-val                PIC 9(8)V9(3).

       01  day-rejected            PIC 9(5).
       01  day-accepted            PIC 9(5).

       01  total-rejected          PIC 9(8).
       01  total-accepted          PIC 9(8).

       01  current-data-gap        PIC 9(8).
       01  max-data-gap            PIC 9(8).
       01  max-data-gap-end        PIC X(10).

       PROCEDURE DIVISION.
       DECLARATIVES.
       *> Terminate the program if an error occurs on input-file.
       input-file-error SECTION.
           USE AFTER STANDARD ERROR ON input-file.

           DISPLAY
               "An error occurred while reading input.txt. "
               "File error: " file-status
               ". The program will terminate."
           END-DISPLAY

           GOBACK
           .

       END DECLARATIVES.

       main-line.
           *> Terminate the program if the file cannot be opened.
           OPEN INPUT input-file
           IF NOT file-is-ok
               DISPLAY "File could not be opened. The program will "
                   "terminate."
               GOBACK
           END-IF

           *> Process the data in the file.
           PERFORM FOREVER
               *> Stop processing if at the end of the file.
               READ input-file
                   AT END
                       EXIT PERFORM
               END-READ

               *> Split the data up and process the value-flag pairs.
               PERFORM UNTIL input-data-pairs = SPACES
                   *> Split off the value-flag pair at the front of the
                   *> record.
                   UNSTRING input-data-pairs DELIMITED BY X"09"
                       INTO val COUNT val-length, flag COUNT flag-length

                   COMPUTE offset = val-length + flag-length + 3
                   MOVE input-data-pairs (offset:) TO input-data-pairs

                   *> Process according to flag.
                   IF NOT invalid-flag
                       ADD val TO day-total, grand-total

                       ADD 1 TO day-accepted, total-accepted

                       IF max-data-gap < current-data-gap
                           MOVE current-data-gap TO max-data-gap
                           MOVE date-stamp TO max-data-gap-end
                       END-IF

                       MOVE ZERO TO current-data-gap
                   ELSE
                       ADD 1 TO current-data-gap, day-rejected,
                           total-rejected
                   END-IF
               END-PERFORM

               *> Display day stats.
               DIVIDE day-total BY day-accepted GIVING mean-val
               DISPLAY
                   date-stamp
                   " Reject: " day-rejected
                   " Accept: " day-accepted
                   " Average: " mean-val
               END-DISPLAY

               INITIALIZE day-rejected, day-accepted, mean-val,
                   day-total
           END-PERFORM

           CLOSE input-file

           *> Display overall stats.
           DISPLAY SPACE
           DISPLAY "File:         " INPUT-FILE-PATH
           DISPLAY "Total:        " grand-total
           DISPLAY "Readings:     " total-accepted

           DIVIDE grand-total BY total-accepted GIVING mean-val
           DISPLAY "Average:      " mean-val

           DISPLAY SPACE
           DISPLAY "Bad readings: " total-rejected
           DISPLAY "Maximum number of consecutive bad readings is "
               max-data-gap
           DISPLAY "Ends on date " max-data-gap-end

           GOBACK
           .


import "/module" for Expect, Suite, ConsoleReporter

var isPal = Fn.new { |word| word == ((word.count > 0) ? word[-1..0] : "") }

var words = ["rotor", "rosetta", "step on no pets", "été", "wren", "🦊😀🦊"]
var expected = [true, false, true, true, false, true]

var TestPal = Suite.new("Pal") { |it|
    it.suite("'isPal' function:") { |it|
        for (i in 0...words.count) {
            it.should("return '%(expected[i])' for '%(words[i])' is palindrome") {
                Expect.call(isPal.call(words[i])).toEqual(expected[i])
            }
        }
    }
}

var reporter = ConsoleReporter.new()
TestPal.run(reporter)
reporter.epilogue()


fcn pali(text){
   if (text.len()<2) return(False);
   text==text.reverse();
}


import Data.List (nub, (\\))

data Record = Record {date :: String, recs :: [(Double, Int)]}

duplicatedDates rs = rs \\ nub rs

goodRecords = filter ((== 24) . length . filter ((>= 1) . snd) . recs)

parseLine l = let ws = words l in Record (head ws) (mapRecords (tail ws))

mapRecords [] = []
mapRecords [_] = error "invalid data"
mapRecords (value:flag:tail) = (read value, read flag) : mapRecords tail

main = do
  inputs <- (map parseLine . lines) `fmap` readFile "readings.txt"
  putStr (unlines ("duplicated dates:": duplicatedDates (map date inputs)))
  putStrLn ("number of good records: " ++ show (length $ goodRecords inputs))


procedure main(A)
    dups := set()
    goodRecords := 0
    lastDate := badFile := &null
    f := A[1] | "readings.txt"
    fin := open(f) | stop("Cannot open file '",f,"'")

    while (fields := 0, badReading := &null, line := read(fin)) do {
        line ? {
            ldate := tab(many(&digits ++ '-')) | (badFile := "yes", next)
            if \lastDate == ldate then insert(dups, ldate)
            lastDate := ldate
            while tab(many(' \t')) do {
                (value := real(tab(many(&digits++'-.'))),
                 tab(many(' \t')),
                 flag := integer(tab(many(&digits++'-'))),
                 fields +:= 1) | (badFile := "yes")
                if flag < 1 then badReading := "yes"
                }
            }
        if fields = 24 then goodRecords +:= (/badReading, 1)
        else badFile := "yes"
        }

    if (\badFile) then write(f," has field format issues.")
    write("There are ",goodRecords," records with all good readings.")
    if *dups > 0 then {
        write("The following dates have multiple records:")
        every writes(" ",!sort(dups))
        write()
        }

end


(defvar *invalid-count*)
(defvar *max-invalid*)
(defvar *max-invalid-date*)
(defvar *total-sum*)
(defvar *total-valid*)

(defun read-flag (stream date)
  (let ((flag (read stream)))
    (if (plusp flag)
        (setf *invalid-count* 0)
        (when (< *max-invalid* (incf *invalid-count*))
          (setf *max-invalid* *invalid-count*)
          (setf *max-invalid-date* date)))
    flag))

(defun parse-line (line)
  (with-input-from-string (s line)
    (let ((date (make-string 10)))
      (read-sequence date s)
      (cons date (loop repeat 24 collect (list (read s)
                                               (read-flag s date)))))))

(defun analyze-line (line)
  (destructuring-bind (date &rest rest) line
    (let* ((valid (remove-if-not #'plusp rest :key #'second))
           (n (length valid))
           (sum (apply #'+ (mapcar #'rationalize (mapcar #'first valid))))
           (avg (if valid (/ sum n) 0)))
      (incf *total-valid* n)
      (incf *total-sum* sum)
      (format t "Line: ~a  Reject: ~2d  Accept: ~2d  ~
                 Line_tot: ~8,3f  Line_avg: ~7,3f~%"
              date (- 24 n) n sum avg))))

(defun process (pathname)
  (let ((*invalid-count* 0) (*max-invalid* 0) *max-invalid-date*
        (*total-sum* 0) (*total-valid* 0))
    (with-open-file (f pathname)
      (loop for line = (read-line f nil nil)
            while line
            do (analyze-line (parse-line line))))
    (format t "~%File     = ~a" pathname)
    (format t "~&Total    = ~f" *total-sum*)
    (format t "~&Readings = ~a" *total-valid*)
    (format t "~&Average  = ~10,3f~%" (/ *total-sum* *total-valid*))
    (format t "~%Maximum run(s) of ~a consecutive false readings ends at ~
               line starting with date(s): ~a~%"
            *max-invalid* *max-invalid-date*)))


   require 'tables/dsv dates'
   dat=: TAB readdsv jpath '~temp/readings.txt'
   Dates=: getdate"1 >{."1 dat
   Vals=:  _99 ". >(1 + +: i.24){"1 dat
   Flags=: _99 ". >(2 + +: i.24){"1 dat

   # Dates                      NB. Total # lines
5471
   +/ *./"1 ] 0 = Dates         NB. # lines with invalid date formats
0
   +/ _99 e."1 Vals,.Flags      NB. # lines with invalid value or flag formats
0
   +/ *./"1   [0 < Flags        NB. # lines with only valid flags
5017
   ~. (#~ (i.~ ~: i:~)) Dates   NB. Duplicate dates
1990 3 25
1991 3 31
1992 3 29
1993 3 28
1995 3 26


import java.util.*;
import java.util.regex.*;
import java.io.*;

public class DataMunging2 {

    public static final Pattern e = Pattern.compile("\\s+");

    public static void main(String[] args) {
        try {
            BufferedReader infile = new BufferedReader(new FileReader(args[0]));
            List<String> duplicates = new ArrayList<String>();
            Set<String> datestamps = new HashSet<String>(); //for the datestamps

            String eingabe;
            int all_ok = 0;//all_ok for lines in the given pattern e
            while ((eingabe = infile.readLine()) != null) {
                String[] fields = e.split(eingabe); //we tokenize on empty fields
                if (fields.length != 49) //we expect 49 fields in a record
                    System.out.println("Format not ok!");
                if (datestamps.add(fields[0])) { //not duplicated
                    int howoften = (fields.length - 1) / 2 ; //number of measurement
                                                             //devices and values
                    for (int n = 1; Integer.parseInt(fields[2*n]) >= 1; n++) {
                        if (n == howoften) {
                            all_ok++ ;
                            break ;
                        }
                    }
                } else {
                    duplicates.add(fields[0]); //first field holds datestamp
                }
            }
            infile.close();
            System.out.println("The following " + duplicates.size() + " datestamps were duplicated:");
            for (String x : duplicates)
                System.out.println(x);
            System.out.println(all_ok + " records were complete and ok!");
        } catch (IOException e) {
            System.err.println("Can't open file " + args[0]);
            System.exit(1);
        }
    }
}


void main(in string[] args) {
    import std.stdio, std.conv, std.string;

    const fileNames = (args.length == 1) ? ["readings.txt"] :
                                           args[1 .. $];

    int noData, noDataMax = -1;
    string[] noDataMaxLine;

    double fileTotal = 0.0;
    int fileValues;

    foreach (const fileName; fileNames) {
        foreach (char[] line; fileName.File.byLine) {
            double lineTotal = 0.0;
            int lineValues;

            // Extract field info.
            const parts = line.split;
            const date = parts[0];
            const fields = parts[1 .. $];
            assert(fields.length % 2 == 0,
                   format("Expected even number of fields, not %d.",
                          fields.length));

            for (int i; i < fields.length; i += 2) {
                immutable value = fields[i].to!double;
                immutable flag = fields[i + 1].to!int;

                if (flag < 1) {
                    noData++;
                    continue;
                }

                // Check run of data-absent fields.
                if (noDataMax == noData && noData > 0)
                    noDataMaxLine ~= date.idup;

                if (noDataMax < noData && noData > 0) {
                    noDataMax = noData;
                    noDataMaxLine.length = 1;
                    noDataMaxLine[0] = date.idup;
                }

                // Re-initialise run of noData counter.
                noData = 0;

                // Gather values for averaging.
                lineTotal += value;
                lineValues++;
            }

            // Totals for the file so far.
            fileTotal += lineTotal;
            fileValues += lineValues;

            writefln("Line: %11s  Reject: %2d  Accept: %2d" ~
                     "  Line_tot: %10.3f  Line_avg: %10.3f",
                     date,
                     fields.length / 2 - lineValues,
                     lineValues,
                     lineTotal,
                     (lineValues > 0) ? lineTotal / lineValues : 0.0);
        }
    }

    writefln("\nFile(s)  = %-(%s, %)", fileNames);
    writefln("Total    = %10.3f", fileTotal);
    writefln("Readings = %6d", fileValues);
    writefln("Average  = %10.3f", fileTotal / fileValues);

    writefln("\nMaximum run(s) of %d consecutive false " ~
             "readings ends at line starting with date(s): %-(%s, %)",
             noDataMax, noDataMaxLine);
}


// wrap up the counter variables in a closure.
function analyze_func(filename) {
    var dates_seen = {};
    var format_bad = 0;
    var records_all = 0;
    var records_good = 0;
    return function() {
        var fh = new ActiveXObject("Scripting.FileSystemObject").openTextFile(filename, 1); // 1 = for reading
        while ( ! fh.atEndOfStream) {
            records_all ++;
            var allOK = true;
            var line = fh.ReadLine();
            var fields = line.split('\t');
            if (fields.length != 49) {
                format_bad ++;
                continue;
            }

            var date = fields.shift();
            if (has_property(dates_seen, date))
                WScript.echo("duplicate date: " + date);
            else
                dates_seen[date] = 1;

            while (fields.length > 0) {
                var value = parseFloat(fields.shift());
                var flag = parseInt(fields.shift(), 10);
                if (isNaN(value) || isNaN(flag)) {
                    format_bad ++;
                }
                else if (flag <= 0) {
                    allOK = false;
                }
            }
            if (allOK)
                records_good ++;
        }
        fh.close();
        WScript.echo("total records: " + records_all);
        WScript.echo("Wrong format: " + format_bad);
        WScript.echo("records with no bad readings: " + records_good);
    }
}

function has_property(obj, propname) {
    return typeof(obj[propname]) == "undefined" ? false : true;
}

var analyze = analyze_func('readings.txt');
analyze();


class
	APPLICATION

create
	make

feature

	make
			-- Summary statistics for 'hash'.
		local
			reject, accept, reading_total: INTEGER
			total, average, file_total: REAL
		do
			read_wordlist
			across
				hash as h
			loop
				io.put_string (h.key + "%T")
				reject := 0
				accept := 0
				total := 0
				across
					h.item as data
				loop
					if data.item.flag > 0 then
						accept := accept + 1
						total := total + data.item.val
					else
						reject := reject + 1
					end
				end
				file_total := file_total + total
				reading_total := reading_total + accept
				io.put_string ("accept: " + accept.out + "%Treject: " + reject.out + "%Ttotal: " + total.out + "%T")
				average := total / accept.to_real
				io.put_string ("average: " + average.out + "%N")
			end
			io.put_string ("File total: " + file_total.out + "%N")
			io.put_string ("Readings total: " + reading_total.out + "%N")
			find_longest_gap
		end

	find_longest_gap
			-- Longest gap (flag values <= 0).
		local
			count: INTEGER
			longest_gap: INTEGER
			end_date: STRING
		do
			create end_date.make_empty
			across
				hash as h
			loop
				across
					h.item as data
				loop
					if data.item.flag <= 0 then
						count := count + 1
					else
						if count > longest_gap then
							longest_gap := count
							end_date := h.key
						end
						count := 0
					end
				end
			end
			io.put_string ("%NThe longest gap is " + longest_gap.out + ". It ends at the date stamp " + end_date + ". %N")
		end

	original_list: STRING = "readings.txt"

	read_wordlist
			-- Preprocessed wordlist in 'hash'.
		local
			l_file: PLAIN_TEXT_FILE
			data: LIST [STRING]
			by_dates: LIST [STRING]
			date: STRING
			data_tup: TUPLE [val: REAL; flag: INTEGER]
			data_arr: ARRAY [TUPLE [val: REAL; flag: INTEGER]]
			i: INTEGER
		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
			create hash.make (data.count)
			across
				data as d
			loop
				if not d.item.is_empty then
					by_dates := d.item.split ('%T')
					date := by_dates [1]
					by_dates.prune (date)
					create data_tup
					create data_arr.make_empty
					from
						i := 1
					until
						i > by_dates.count - 1
					loop
						data_tup := [by_dates [i].to_real, by_dates [i + 1].to_integer]
						data_arr.force (data_tup, data_arr.count + 1)
						i := i + 2
					end
					hash.put (data_arr, date)
					if not hash.inserted then
						date.append ("_double_date_stamp")
						hash.put (data_arr, date)
					end
				end
			end
		end

	hash: HASH_TABLE [ARRAY [TUPLE [val: REAL; flag: INTEGER]], STRING]

end


$ jq -R '[splits("[ \t]+")]' Text_processing_2.txt


dupdate = df[nonunique(df[:,[:Date]]),:][:Date]
println("The following rows have duplicate DATESTAMP:")
println(df[df[:Date] .== dupdate,:])
println("All values good in these rows:")
println(df[df[:ValidValues] .== 24,:])


// version 1.2.31

import java.io.File

fun main(args: Array<String>) {
    val rx = Regex("""\s+""")
    val file = File("readings.txt")
    var count = 0
    var invalid = 0
    var allGood = 0
    var map = mutableMapOf<String, Int>()
    file.forEachLine { line ->
        count++
        val fields = line.split(rx)
        val date = fields[0]
        if (fields.size == 49) {
            if (map.containsKey(date))
                map[date] = map[date]!! + 1
            else
                map.put(date, 1)
            var good = 0
            for (i in 2 until fields.size step 2) {
                if (fields[i].toInt() >= 1) {
                    good++
                }
            }
            if (good == 24) allGood++
        }
        else invalid++
    }

    println("File = ${file.name}")
    println("\nDuplicated dates:")
    for ((k,v) in map) {
        if (v > 1) println("  $k ($v times)")
    }
    println("\nTotal number of records   : $count")
    var percent = invalid.toDouble() / count * 100.0
    println("Number of invalid records : $invalid (${"%5.2f".format(percent)}%)")
    percent = allGood.toDouble() / count * 100.0
    println("Number which are all good : $allGood (${"%5.2f".format(percent)}%)")
}


-module( text_processing ).

-export( [file_contents/1, main/1] ).

-record( acc, {failed={"", 0, 0}, files=[], ok=0, total=0} ).

file_contents( Name ) ->
	{ok, Binary} = file:read_file( Name ),
	[line_contents(X) || X <- binary:split(Binary, <<"\r\n">>, [global]), X =/= <<>>].

main( Files ) ->
      Acc = lists:foldl( fun file/2, #acc{}, Files ),
      {Failed_date, Failed, _Continuation} = Acc#acc.failed,
      io:fwrite( "~nFile(s)=~p~nTotal=~.2f~nReadings=~p~nAverage=~.2f~n~nMaximum run(s) of ~p consecutive false readings ends at line starting with date(s): ~p~n",
        [lists:reverse(Acc#acc.files), Acc#acc.total, Acc#acc.ok, Acc#acc.total / Acc#acc.ok, Failed, Failed_date] ).

file( Name, #acc{files=Files}=Acc ) ->
	try
	Line_contents = file_contents( Name ),
	lists:foldl( fun file_content_line/2, Acc#acc{files=[Name | Files]}, Line_contents )

	catch
	_:Error ->
		io:fwrite( "Error: Failed to read ~s: ~p~n", [Name, Error] ),
		Acc
	end.

file_content_line( {Date, Value_flags}, #acc{failed=Failed, ok=Ok, total=Total}=Acc ) ->
	New_failed = file_content_line_failed( Value_flags, Date, Failed ),
	{Sum, Oks, Average} = file_content_line_oks_0( [X || {X, ok} <- Value_flags] ),
	io:fwrite( "Line=~p\tRejected=~p\tAccepted=~p\tLine total=~.2f\tLine average=~.2f~n", [Date, erlang:length(Value_flags) - Oks, Oks, Sum, Average] ),
	Acc#acc{failed=New_failed, ok=Ok + Oks, total=Total + Sum}.

file_content_line_failed( [], Date, {_Failed_date, Failed, Acc} ) when Acc > Failed ->
        {Date, Acc, Acc};
file_content_line_failed( [], _Date, Failed ) ->
        Failed;
file_content_line_failed( [{_V, error} | T], Date, {Failed_date, Failed, Acc} ) ->
        file_content_line_failed( T,  Date, {Failed_date, Failed, Acc + 1} );
file_content_line_failed( [_H | T],  Date, {_Failed_date, Failed, Acc} ) when Acc > Failed ->
        file_content_line_failed( T, Date, {Date, Acc, 0} );
file_content_line_failed( [_H | T], Date, {Failed_date, Failed, _Acc} ) ->
        file_content_line_failed( T, Date, {Failed_date, Failed, 0} ).

file_content_line_flag( N ) when N > 0 -> ok;
file_content_line_flag( _N ) -> error.

file_content_line_oks_0( [] ) -> {0.0, 0, 0.0};
file_content_line_oks_0( Ok_value_flags ) ->
        Sum = lists:sum( Ok_value_flags ),
        Oks = erlang:length( Ok_value_flags ),
        {Sum, Oks, Sum / Oks}.

file_content_line_value_flag( Binary, {[], Acc} ) ->
        Flag = file_content_line_flag( erlang:list_to_integer(binary:bin_to_list(Binary)) ),
        {[Flag], Acc};
file_content_line_value_flag( Binary, {[Flag], Acc} ) ->
        Value = erlang:list_to_float( binary:bin_to_list(Binary) ),
        {[], [{Value, Flag} | Acc]}.

line_contents( Line ) ->
	[Date_binary | Rest] = binary:split( Line, <<"\t">>, [global] ),
	{_Previous, Value_flags} = lists:foldr( fun file_content_line_value_flag/2, {[], []}, Rest ), % Preserve order
	{binary:bin_to_list( Date_binary ), Value_flags}.


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

dates = {}
duplicated, bad_format = {}, {}
num_good_records, lines_total = 0, 0

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

    lines_total = lines_total + 1

    date = string.match( line, "%d+%-%d+%-%d+" )
    if dates[date] ~= nil then
        duplicated[#duplicated+1] = date
    end
    dates[date] = 1

    count_pairs, bad_values = 0, false
    for v, w in string.gmatch( line, "%s(%d+[%.%d+]*)%s(%-?%d)" ) do
        count_pairs = count_pairs + 1
        if tonumber(w) <= 0 then
            bad_values = true
        end
    end
    if count_pairs ~= 24 then
        bad_format[#bad_format+1] = date
    end
    if not bad_values then
        num_good_records = num_good_records + 1
    end
end

print( "Lines read:", lines_total )
print( "Valid records: ", num_good_records )
print( "Duplicate dates:" )
for i = 1, #duplicated do
    print( "   ", duplicated[i] )
end
print( "Bad format:" )
for i = 1, #bad_format do
    print( "   ", bad_format[i] )
end


Module TestThis {
	Document a$, exp$
	\\ automatic find the enconding and the line break
	Load.doc a$, "readings.txt"
	m=0
	n=doc.par(a$)
	k=list
	nl$={
	}
	l=0
	exp$=format$("Records: {0}", n)+nl$
	For i=1 to n
		b$=paragraph$(a$, i)
		If exist(k,Left$(b$, 10)) then
			m++ : where=eval(k)
			exp$=format$("Duplicate for {0} at {1}",where, i)+nl$
		Else
			Append k, Left$(b$, 10):=i
		End if
		Stack New {
			Stack  Mid$(Replace$(chr$(9)," ", b$), 11)
			while not empty {
				Read a, b
				if b<=0 then l++ : exit
			}
		}
	Next
	exp$= format$("Duplicates {0}",m)+nl$
	exp$= format$("Valid Records {0}",n-l)+nl$
	clipboard exp$
	report exp$
}
TestThis


\ data munging

\ 1991-03-30[\t10.000\t[-]1]*24

\ 1. mean of valid (flag > 0) values per day and overall
\ 2. length of longest run of invalid values, and when it happened

fvariable day-sum
variable day-n

fvariable total-sum
variable total-n

10 constant date-size    \ yyyy-mm-dd
create cur-date date-size allot

create bad-date date-size allot
variable bad-n

create worst-date date-size allot
variable worst-n

: split ( buf len char -- buf' l2  buf l1 )  \ where buf'[0] = char, l1 = len-l2
  >r 2dup r> scan
  2swap 2 pick - ;

: next-sample ( buf len -- buf' len' fvalue flag )
  #tab split >float   drop    1 /string
  #tab split snumber? drop >r 1 /string r> ;

: ok?  0> ;

: add-sample ( value -- )
  day-sum [email protected] f+ day-sum f!
  1 day-n +! ;

: add-day
  day-sum [email protected] total-sum [email protected] f+ total-sum f!
  day-n @ total-n +! ;

: add-bad-run
  bad-n @ 0= if
    cur-date bad-date date-size move
  then
  1 bad-n +! ;

: check-worst-run
  bad-n @ worst-n @ > if
    bad-n @ worst-n !
    bad-date worst-date date-size move
  then
  0 bad-n ! ;

: hour ( buf len -- buf' len' )
  next-sample ok? if
    add-sample
    check-worst-run
  else
    fdrop
    add-bad-run
  then ;

: .mean ( sum count -- ) 0 d>f f/ f. ;

: day ( line len -- )
  2dup + #tab swap c! 1+			\ append tab for parsing
  #tab split cur-date swap move 1 /string	\ skip date
  0e day-sum f!
  0  day-n !
  24 0 do hour loop 2drop
  cur-date date-size type ."  mean = "
  day-sum [email protected] day-n @ .mean cr
  add-day ;

stdin value input

: main
  s" input.txt" r/o open-file throw to input
  0e total-sum f!
  0 total-n !
  0 worst-n !
  begin  pad 512 input read-line throw
  while  pad swap day
  repeat
  input close-file throw
  worst-n @ if
    ."  Longest interruption: " worst-n @ .
    ." hours starting " worst-date date-size type cr
  then
  ."  Total mean = "
  total-sum [email protected] total-n @ .mean cr ;

main bye


Crunches a set of hourly data. Starts with a date, then 24 pairs of value,indicator for that day, on one line.
      INTEGER Y,M,D		!Year, month, and day.
      INTEGER GOOD(24)		!The indicators.
      REAL*8 V(24),VTOT,T	!The grist.
      INTEGER NV,N,NB		!Number of good values overall, and in a day.
      INTEGER I,NREC,HIC	!Some counters.
      INTEGER BI,BN,BBI,BBN	!Stuff to locate the longest run of bad data,
      CHARACTER*10 BDATE,BBDATE	!Along with the starting date.
      LOGICAL INGOOD		!State flipper for the runs of data.
      INTEGER IN,MSG		!I/O mnemonics.
      CHARACTER*666 ACARD	!Scratchpad, of sufficient length for all expectation.
      IN = 10		!Unit number for the input file.
      MSG = 6		!Output.
      OPEN (IN,FILE="Readings1.txt", FORM="FORMATTED",	!This should be a function.
     1 STATUS ="OLD",ACTION="READ")			!Returning success, or failure.
      NB = 0		!No bad values read.
      NV = 0		!Nor good values read.
      VTOT = 0		!Their average is to come.
      NREC = 0		!No records read.
      HIC = 0		!Provoking no complaints.
      INGOOD = .TRUE.	!I start in hope.
      BBN = 0		!And the longest previous bad run is short.
Chew into the file.
   10 READ (IN,11,END=100,ERR=666) L,ACARD(1:MIN(L,LEN(ACARD)))	!With some protection.
      NREC = NREC + 1		!So, a record has been read.
   11 FORMAT (Q,A)		!Obviously, Q ascertains the length of the record being read.
      READ (ACARD,12,END=600,ERR=601) Y,M,D	!The date part is trouble, as always.
   12 FORMAT (I4,2(1X,I2))				!Because there are no delimiters between the parts.
      READ (ACARD(11:L),*,END=600,ERR=601) (V(I),GOOD(I),I = 1,24)	!But after the date, delimiters abound.
Calculations. Could use COUNT(array) and SUM(array), but each requires its own pass through the array.
   20 T = 0		!Start on the day's statistics.
      N = 0		!No values yet.
      DO I = 1,24	!So, scan the cargo and do all the twiddling in one pass..
        IF (GOOD(I).GT.0) THEN	!A good value?
          N = N + 1		!Yes. Count it in.
          T = T + V(I)		!And augment for the average.
          IF (.NOT.INGOOD) THEN	!Had we been ungood?
            INGOOD = .TRUE.	!Yes. But now it changes.
            IF (BN.GT.BBN) THEN	!The run just ending: is it longer?
              BBN = BN		!Yes. Make it the new baddest.
              BBI = BI		!Recalling its start index,
              BBDATE = BDATE	!And its start date.
            END IF		!So much for bigger badness.
          END IF		!Now we're in good data.
         ELSE		!Otherwise, a bad value is upon us.
          IF (INGOOD) THEN	!Were we good?
            INGOOD = .FALSE.	!No longer. A new bad run is starting.
            BDATE = ACARD(1:10)	!Recall the date for this starter.
            BI = I		!And its index.
            BN = 0		!Start the run-length counter.
          END IF		!So much for a fall.
          BN = BN + 1	!Count another bad value.
        END IF		!Good or bad, so much for that value.
      END DO		!On to the next.
Commentary for the day's data..
      IF (N.LE.0) THEN	!I prefer to avoid dividing by zero.
        WRITE (MSG,21) NREC,ACARD(1:10)	!So, no average to report.
   21   FORMAT ("Record",I8," (",A,") has no good data!")	!Just a remark.
       ELSE			!But otherwise,
        WRITE(MSG,22) NREC,ACARD(1:10),N,T/N	!An average is possible.
   22   FORMAT("Record",I8," (",A,")",I3," good, average",F9.3)	!So here it is.
        NB = NB + 24 - N	!Count the bad by implication.
        NV = NV + N		!Count the good directly.
        VTOT = VTOT + T		!Should really sum deviations from a working average.
      END IF			!So much for that line.
      GO TO 10		!More! More! I want more!!

Complaints. Should really distinguish between trouble in the date part and in the data part.
  600 WRITE (MSG,*) '"END" declared - insufficient data?'	!Not enough numbers, presumably.
      GO TO 602				!Reveal the record.
  601 WRITE (MSG,*) '"ERR" declared - improper number format?'	!Ah, but which number?
  602 WRITE (MSG,603) NREC,L,ACARD(1:L)	!Anyway, reveal the uninterpreted record.
  603 FORMAT(" Record ",I0,", length ",I0," reads ",A)	!Just so.
      HIC = HIC + 1			!This may grow into a habit.
      IF (HIC.LE.12) GO TO 10		!But if not yet, try the next record.
      STOP "Enough distaste."		!Or, give up.
  666 WRITE (MSG,101) NREC,"format error!"	!For A-style data? Should never happen!
      GO TO 900				!But if it does, give up!

Closedown.
  100 WRITE (MSG,101) NREC,"then end-of-file"	!Discovered on the next attempt.
  101 FORMAT (" Record ",I0,": ",A)		!A record number plus a remark.
      WRITE (MSG,102) NV,NB,VTOT/NV		!The overall results.
  102 FORMAT (I8," values, ",I0," bad. Average",F9.4)	!This should do.
      IF (BBN.LE.0) THEN		!Now for a special report.
        WRITE (MSG,*) "No bad value presented, so no longest run."	!Unneeded!
       ELSE				!But actually, the example data has some bad values.
        WRITE (MSG,103) BBN,BBI,BBDATE	!And this is for the longest encountered.
  103   FORMAT ("Longest bad run: ",I0,", starting hour ",I0," on ",A)	!Just so.
      END IF			!Enough remarks.
  900 CLOSE(IN)		!Done.
      END	!Spaghetti rules.


data = Import["Readings.txt","TSV"]; Print["duplicated dates: "];
Select[[email protected][[;;,1]], #[[2]]>1&][[;;,1]]//Column
Print["number of good records: ", Count[([email protected]@#[[3;;All;;2]])& /@ data, 1],
" (out of a total of ", Length[data], ")"]


function [val,count] = readdat(configfile)
% READDAT reads readings.txt file
%
% The value of boolean parameters can be tested with
%    exist(parameter,'var')

if nargin<1,
   filename = 'readings.txt';
end;

fid = fopen(filename);
if fid<0, error('cannot open file %s\n',a); end;
[val,count] = fscanf(fid,'%04d-%02d-%02d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d %f %d \n');
fclose(fid);

count = count/51;

if (count<1) || count~=floor(count),
     error('file has incorrect format\n')
end;

val = reshape(val,51,count)';   % make matrix with 51 rows and count columns, then transpose it.

d = datenum(val(:,1:3));	% compute timestamps

printf('The following records are followed by a duplicate:');
dix = find(diff(d)==0)		% check for to consequtive timestamps with zero difference

printf('number of valid records: %i\n ', sum( all( val(:,5:2:end) >= 1, 2) ) );


/* Known to compile and work with tcc in win32 & gcc on Linux (with warnings)
------------------------------------------------------------------------------
readable.c: My random number generator, ISAAC.
(c) Bob Jenkins, March 1996, Public Domain
You may use this code in any way you wish, and it is free.  No warrantee.
------------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#ifdef _MSC_VER
  typedef unsigned __int32 uint32_t;
#else
  #include <stdint.h>
#endif

/* a ub4 is an unsigned 4-byte quantity */
typedef  uint32_t  ub4;

/* external results */
ub4 randrsl[256], randcnt;

/* internal state */
static    ub4 mm[256];
static    ub4 aa=0, bb=0, cc=0;

void isaac()
{
   register ub4 i,x,y;

   cc = cc + 1;    /* cc just gets incremented once per 256 results */
   bb = bb + cc;   /* then combined with bb */

   for (i=0; i<256; ++i)
   {
     x = mm[i];
     switch (i%4)
     {
     case 0: aa = aa^(aa<<13); break;
     case 1: aa = aa^(aa>>6); break;
     case 2: aa = aa^(aa<<2); break;
     case 3: aa = aa^(aa>>16); break;
     }
     aa              = mm[(i+128)%256] + aa;
     mm[i]      = y  = mm[(x>>2)%256] + aa + bb;
     randrsl[i] = bb = mm[(y>>10)%256] + x;
   }
   // not in original readable.c
   randcnt = 0;
}

/* if (flag!=0), then use the contents of randrsl[] to initialize mm[]. */
#define mix(a,b,c,d,e,f,g,h) \
{ \
   a^=b<<11; d+=a; b+=c; \
   b^=c>>2;  e+=b; c+=d; \
   c^=d<<8;  f+=c; d+=e; \
   d^=e>>16; g+=d; e+=f; \
   e^=f<<10; h+=e; f+=g; \
   f^=g>>4;  a+=f; g+=h; \
   g^=h<<8;  b+=g; h+=a; \
   h^=a>>9;  c+=h; a+=b; \
}

void randinit(int flag)
{
   register int i;
   ub4 a,b,c,d,e,f,g,h;
   aa=bb=cc=0;
   a=b=c=d=e=f=g=h=0x9e3779b9;  /* the golden ratio */

   for (i=0; i<4; ++i)          /* scramble it */
   {
     mix(a,b,c,d,e,f,g,h);
   }

   for (i=0; i<256; i+=8)   /* fill in mm[] with messy stuff */
   {
     if (flag)                  /* use all the information in the seed */
	 {
       a+=randrsl[i  ]; b+=randrsl[i+1]; c+=randrsl[i+2]; d+=randrsl[i+3];
       e+=randrsl[i+4]; f+=randrsl[i+5]; g+=randrsl[i+6]; h+=randrsl[i+7];
     }
     mix(a,b,c,d,e,f,g,h);
     mm[i  ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
     mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
   }

   if (flag)
   {        /* do a second pass to make all of the seed affect all of mm */
	 for (i=0; i<256; i+=8)
     {
       a+=mm[i  ]; b+=mm[i+1]; c+=mm[i+2]; d+=mm[i+3];
       e+=mm[i+4]; f+=mm[i+5]; g+=mm[i+6]; h+=mm[i+7];
       mix(a,b,c,d,e,f,g,h);
       mm[i  ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
       mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
     }
   }

   isaac();            /* fill in the first set of results */
   randcnt=0;        /* prepare to use the first set of results */
}

// Get a random 32-bit value 0..MAXINT
ub4 iRandom()
{
	ub4 r = randrsl[randcnt];
	++randcnt;
	if (randcnt >255) {
		isaac();
		randcnt = 0;
	}
	return r;
}

// Get a random character in printable ASCII range
char iRandA()
{
	return iRandom() % 95 + 32;
}

// Seed ISAAC with a string
void iSeed(char *seed, int flag)
{
	register ub4 i,m;
	for (i=0; i<256; i++) mm[i]=0;
	m = strlen(seed);
	for (i=0; i<256; i++)
	{
	// in case seed has less than 256 elements
        if (i>m) randrsl[i]=0;  else randrsl[i] = seed[i];
	}
	// initialize ISAAC with seed
	randinit(flag);
}

// maximum length of message
#define MAXMSG 4096
#define MOD 95
#define START 32
// cipher modes for Caesar
enum ciphermode {
	mEncipher, mDecipher, mNone
};

// XOR cipher on random stream. Output: ASCII string
char v[MAXMSG];
char* Vernam(char *msg)
	{
		register ub4 i,l;
		l = strlen(msg);
		// zeroise v
		memset(v,'\0',l+1);
		// XOR message
		for (i=0; i<l; i++)
			v[i] = iRandA() ^ msg[i];
		return v;
	}

// Caesar-shift a printable character
char Caesar(enum ciphermode m, char ch, char shift, char modulo, char start)
	{
		register int n;
		if (m == mDecipher) shift = -shift;
		n = (ch-start) + shift;
		n = n % modulo;
		if (n<0) n += modulo;
		return start+n;
	}

// Caesar-shift a string on a pseudo-random stream
char c[MAXMSG];
char* CaesarStr(enum ciphermode m, char *msg, char modulo, char start)
	{
		register ub4 i,l;
		l = strlen(msg);
		// zeroise c
		memset(c,'\0',l+1);
		// Caesar-shift message
		for (i=0; i<l; i++)
			c[i] = Caesar(m, msg[i], iRandA(), modulo, start);
		return c;
	}

int main()
{
	register ub4 n,l;
	// input: message and key
	char *msg = "a Top Secret secret";
	char *key = "this is my secret key";
	// Vernam ciphertext & plaintext
	char vctx[MAXMSG], vptx[MAXMSG];
	// Caesar ciphertext & plaintext
	char cctx[MAXMSG], cptx[MAXMSG];
	l = strlen(msg);
	// Encrypt: Vernam XOR
	iSeed(key,1);
	strcpy(vctx, Vernam(msg));
	// Encrypt: Caesar
	strcpy(cctx, CaesarStr(mEncipher, msg, MOD, START));
	// Decrypt: Vernam XOR
	iSeed(key,1);
	strcpy(vptx, Vernam(vctx));
	// Decrypt: Caesar
	strcpy(cptx, CaesarStr(mDecipher,cctx, MOD, START));
	// Program output
	printf("Message: %s\n",msg);
	printf("Key    : %s\n",key);
	printf("XOR    : ");
	// Output Vernam ciphertext as a string of hex digits
	for (n=0; n<l; n++) printf("%02X",vctx[n]);
	printf("\n");
	// Output Vernam decrypted plaintext
	printf("XOR dcr: %s\n",vptx);
	// Caesar
	printf("MOD    : ");
	// Output Caesar ciphertext as a string of hex digits
	for (n=0; n<l; n++) printf("%02X",cctx[n]);
	printf("\n");
	// Output Caesar decrypted plaintext
	printf("MOD dcr: %s\n",cptx);
	return 0;
}


package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

const (
	filename = "readings.txt"
	readings = 24             // per line
	fields   = readings*2 + 1 // per line
)

func main() {
	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	var (
		badRun, maxRun   int
		badDate, maxDate string
		fileSum          float64
		fileAccept       int
	)
	endBadRun := func() {
		if badRun > maxRun {
			maxRun = badRun
			maxDate = badDate
		}
		badRun = 0
	}
	s := bufio.NewScanner(file)
	for s.Scan() {
		f := strings.Fields(s.Text())
		if len(f) != fields {
			log.Fatal("unexpected format,", len(f), "fields.")
		}
		var accept int
		var sum float64
		for i := 1; i < fields; i += 2 {
			flag, err := strconv.Atoi(f[i+1])
			if err != nil {
				log.Fatal(err)
			}
			if flag <= 0 { // value is bad
				if badRun++; badRun == 1 {
					badDate = f[0]
				}
			} else { // value is good
				endBadRun()
				value, err := strconv.ParseFloat(f[i], 64)
				if err != nil {
					log.Fatal(err)
				}
				sum += value
				accept++
			}
		}
		fmt.Printf("Line: %s  Reject %2d  Accept: %2d  Line_tot:%9.3f",
			f[0], readings-accept, accept, sum)
		if accept > 0 {
			fmt.Printf("  Line_avg:%8.3f\n", sum/float64(accept))
		} else {
			fmt.Println()
		}
		fileSum += sum
		fileAccept += accept
	}
	if err := s.Err(); err != nil {
		log.Fatal(err)
	}
	endBadRun()

	fmt.Println("\nFile     =", filename)
	fmt.Printf("Total    = %.3f\n", fileSum)
	fmt.Println("Readings = ", fileAccept)
	if fileAccept > 0 {
		fmt.Printf("Average  =  %.3f\n", fileSum/float64(fileAccept))
	}
	if maxRun == 0 {
		fmt.Println("\nAll data valid.")
	} else {
		fmt.Printf("\nMax data gap = %d, beginning on line %s.\n",
			maxRun, maxDate)
	}
}


import strutils, tables

const NumFields = 49
const DateField = 0
const FlagGoodValue = 1

var badRecords: int       # Number of records that have invalid formatted values.
var totalRecords: int     # Total number of records in the file.
var badInstruments: int   # Total number of records that have at least one instrument showing error.
var seenDates: Table[string, bool]  # Table to keep track of what dates we have seen.

proc checkFloats(floats: seq[string]): bool =
  ## Ensure we can parse all records as floats (except the date stamp).
  for index in 1..<NumFields:
    try:
      # We're assuming all instrument flags are floats not integers.
      discard parseFloat(floats[index])
    except ValueError:
      return false
  true

proc areAllFlagsOk(instruments: seq[string]): bool =
  ## Ensure that all sensor flags are ok.

  # Flags start at index 2, and occur every 2 fields.
  for index in countup(2, NumFields, 2):
    # We're assuming all instrument flags are floats not integers
    var flag = parseFloat(instruments[index])
    if flag < FlagGoodValue: return false
  true

# Note: we're not checking the format of the date stamp.

# Main.

var currentLine = 0
for line in "readings.txt".lines:
  currentLine.inc
  if line.len == 0: continue    # Empty lines don't count as records.

  var tokens = line.split({' ', '\t'})
  totalRecords.inc

  if tokens.len != NumFields:
    badRecords.inc
    continue

  if not checkFloats(tokens):
    badRecords.inc
    continue

  if not areAllFlagsOk(tokens):
    badInstruments.inc

  if seenDates.hasKeyOrPut(tokens[DateField], true):
    echo tokens[DateField], " duplicated on line ", currentLine

let goodRecords = totalRecords - badRecords
let goodInstruments = goodRecords - badInstruments

echo "Total Records: ", totalRecords
echo "Records with wrong format: ", badRecords
echo "Records where all instruments were OK: ", goodInstruments


#load "str.cma"
open Str

let strip_cr str =
  let last = pred (String.length str) in
  if str.[last] <> '\r' then str else String.sub str 0 last

let map_records =
  let rec aux acc = function
    | value::flag::tail ->
        let e = (float_of_string value, int_of_string flag) in
        aux (e::acc) tail
    | [_] -> invalid_arg "invalid data"
    | [] -> List.rev acc
  in
  aux [] ;;

let duplicated_dates =
  let same_date (d1,_) (d2,_) = (d1 = d2) in
  let date (d,_) = d in
  let rec aux acc = function
    | a::b::tl when same_date a b ->
        aux (date a::acc) tl
    | _::tl ->
        aux acc tl
    | [] ->
        List.rev acc
  in
  aux [] ;;

let record_ok (_,record) =
  let is_ok (_,v) = v >= 1 in
  let sum_ok =
    List.fold_left (fun sum this ->
      if is_ok this then succ sum else sum) 0 record
  in
  sum_ok = 24

let num_good_records =
  List.fold_left  (fun sum record ->
    if record_ok record then succ sum else sum) 0 ;;

let parse_line line =
  let li = split (regexp "[ \t]+") line in
  let records = map_records (List.tl li)
  and date = List.hd li in
  (date, records)

let () =
  let ic = open_in "readings.txt" in
  let rec read_loop acc =
    let line_opt = try Some (strip_cr (input_line ic))
                   with End_of_file -> None
    in
    match line_opt with
      None -> close_in ic; List.rev acc
    | Some line -> read_loop (parse_line line :: acc)
  in
  let inputs = read_loop [] in

  Printf.printf "%d total lines\n" (List.length inputs);

  Printf.printf "duplicated dates:\n";
  let dups = duplicated_dates inputs in
  List.iter print_endline dups;

  Printf.printf "number of good records: %d\n" (num_good_records inputs);
;;


using System;

namespace cipher {

static class Cipher {

// external results
static uint[] randrsl = new uint[256];
static uint randcnt;
// internal state
static uint[] mm = new uint[256];
static uint aa=0, bb=0, cc=0;

static void isaac() {
   uint i,x,y;
   cc++;    // cc just gets incremented once per 256 results
   bb+=cc;   // then combined with bb

   for (i=0; i<=255; i++) {
     x = mm[i];
     switch (i & 3) {
      case 0: aa = aa ^ (aa << 13); break;
      case 1: aa = aa ^ (aa >> 6); break;
      case 2: aa = aa ^ (aa << 2); break;
      case 3: aa = aa ^ (aa >> 16); break;
     }
     aa = mm[(i+128) & 255] + aa;
     y  = mm[(x >> 2) & 255] + aa + bb;
     mm[i] = y;
     bb = mm[(y >> 10) & 255] + x;
     randrsl[i]= bb;
   }
}

// if (flag==TRUE), then use the contents of randrsl[] to initialize mm[].
static void mix(ref uint a, ref uint b, ref uint c, ref uint d, ref uint e, ref uint f, ref uint g, ref uint h) {
   a = a ^ b << 11; d+=a; b+=c;
   b = b ^ c >> 2;  e+=b; c+=d;
   c = c ^ d << 8;  f+=c; d+=e;
   d = d ^ e >> 16; g+=d; e+=f;
   e = e ^ f << 10; h+=e; f+=g;
   f = f ^ g >> 4;  a+=f; g+=h;
   g = g ^ h << 8;  b+=g; h+=a;
   h = h ^ a >> 9;  c+=h; a+=b;
}

static void Init(bool flag) {
  short i; uint a,b,c,d,e,f,g,h;

   aa=0; bb=0; cc=0;
   a=0x9e3779b9; b=a; c=a; d=a;
   e=a; f=a; g=a; h=a;

   for (i=0; i<=3; i++)           // scramble it
        mix(ref a,ref b,ref c,ref d,ref e,ref f,ref g,ref h);

   i=0;
   do  { // fill in mm[] with messy stuff
          if (flag) {     // use all the information in the seed
            a+=randrsl[i  ]; b+=randrsl[i+1]; c+=randrsl[i+2]; d+=randrsl[i+3];
            e+=randrsl[i+4]; f+=randrsl[i+5]; g+=randrsl[i+6]; h+=randrsl[i+7];
          } // if flag

      mix(ref a,ref b,ref c,ref d,ref e,ref f,ref g,ref h);
      mm[i  ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
      mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
      i+=8;
      }
   while (i<255);

   if (flag) {
   // do a second pass to make all of the seed affect all of mm
     i=0;
     do {
      a+=mm[i  ]; b+=mm[i+1]; c+=mm[i+2]; d+=mm[i+3];
      e+=mm[i+4]; f+=mm[i+5]; g+=mm[i+6]; h+=mm[i+7];
      mix(ref a,ref b,ref c,ref d,ref e,ref f,ref g,ref h);
      mm[i  ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
      mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
      i+=8;
        }
     while (i<255);
   }
   isaac();           // fill in the first set of results
   randcnt=0;       // prepare to use the first set of results
}

// Seed ISAAC with a string
static void Seed(string seed, bool flag) {
	for (int i=0; i<256; i++) mm[i]=0;
	for (int i=0; i<256; i++) randrsl[i]=0;
	int m = seed.Length;
	for (int i=0; i<m; i++) {
        randrsl[i] = seed[i];
	}
	// initialize ISAAC with seed
	Init(flag);
}

// Get a random 32-bit value
static uint Random() {
    uint result = randrsl[randcnt];
    randcnt++;
    if (randcnt>255) {
         isaac(); randcnt=0;
    }
    return result;
}

// Get a random character in printable ASCII range
static byte RandA() {
	return (byte)(Random() % 95 + 32);
}

// XOR encrypt on random stream. Output: ASCII byte array
static byte[] Vernam(string msg)
	{
		int n,l;
		byte[] v = new byte[msg.Length];
		l = msg.Length;
		// XOR message
		for (n=0; n<l; n++) {
			v[n] = (byte) (RandA() ^ (byte)msg[n]);
		}
		return v;
	}

	public static void Main() {
		string msg = "a Top Secret secret";
		string key = "this is my secret key";
		byte[] xctx= new byte[msg.Length];
		byte[] xptx= new byte[msg.Length];
		string xtcx= "*******************";
		string xtpx= "*******************";
		Seed(key,true);
		// XOR encrypt
		xctx = Vernam(msg);
		xtcx = System.Text.Encoding.ASCII.GetString(xctx);
		// XOR decrypt
		Seed(key,true);
		xptx = Vernam(xtcx);
		xtpx = System.Text.Encoding.ASCII.GetString(xptx);
		Console.WriteLine("Message: "+msg);
		Console.WriteLine("Key    : "+key);
		Console.Write    ("XOR    : ");
		// output ciphertext as a string of hexadecimal digits
		for (int n=0; n<xctx.Length; n++) Console.Write("{0:X2}", xctx[n]);
		Console.WriteLine("\nXOR dcr: "+xtpx);
	}
}
}


import Data.List
import Numeric
import Control.Arrow
import Control.Monad
import Text.Printf
import System.Environment
import Data.Function

type Date = String
type Value = Double
type Flag = Bool

readFlg :: String -> Flag
readFlg = (> 0).read

readNum :: String -> Value
readNum = fst.head.readFloat

take2 = takeWhile(not.null).unfoldr (Just.splitAt 2)

parseData :: [String] -> (Date,[(Value,Flag)])
parseData = head &&& map(readNum.head &&& readFlg.last).take2.tail

sumAccs :: (Date,[(Value,Flag)]) -> (Date, ((Value,Int),[Flag]))
sumAccs = second (((sum &&& length).concat.uncurry(zipWith(\v f -> [v|f])) &&& snd).unzip)

maxNAseq :: [Flag] -> [(Int,Int)]
maxNAseq = head.groupBy((==) `on` fst).sortBy(flip compare)
           . concat.uncurry(zipWith(\i (r,b)->[(r,i)|not b]))
           . first(init.scanl(+)0). unzip
           . map ((fst &&& id).(length &&& head)). group

main = do
    file:_ <- getArgs
    f <- readFile file
    let dat :: [(Date,((Value,Int),[Flag]))]
        dat      = map (sumAccs. parseData. words).lines $ f
        summ     = ((sum *** sum). unzip *** maxNAseq.concat). unzip $ map snd dat
        totalFmt = "\nSummary\t\t accept: %d\t total: %.3f \taverage: %6.3f\n\n"
        lineFmt  = "%8s\t accept: %2d\t total: %11.3f \taverage: %6.3f\n"
        maxFmt   =  "Maximum of %d consecutive false readings, starting on line /%s/ and ending on line /%s/\n"
-- output statistics
    putStrLn "\nSome lines:\n"
    mapM_ (\(d,((v,n),_)) -> printf lineFmt d n v (v/fromIntegral n)) $ take 4 $ drop 2200 dat
    (\(t,n) -> printf totalFmt  n t (t/fromIntegral n)) $ fst summ
    mapM_ ((\(l, d1,d2) -> printf maxFmt l d1 d2)
              . (\(a,b)-> (a,(fst.(dat!!).(`div`24))b,(fst.(dat!!).(`div`24))(a+b)))) $ snd summ


use List::MoreUtils 'natatime';
use constant FIELDS => 49;

binmode STDIN, ':crlf';
  # Read the newlines properly even if we're not running on
  # Windows.

my ($line, $good_records, %dates) = (0, 0);
while (<>)
   {++$line;
    my @fs = split /\s+/;
    @fs == FIELDS or die "$line: Bad number of fields.\n";
    for (shift @fs)
       {/\d{4}-\d{2}-\d{2}/ or die "$line: Bad date format.\n";
        ++$dates{$_};}
    my $iterator = natatime 2, @fs;
    my $all_flags_okay = 1;
    while ( my ($val, $flag) = $iterator->() )
       {$val =~ /\d+\.\d+/ or die "$line: Bad value format.\n";
        $flag =~ /\A-?\d+/ or die "$line: Bad flag format.\n";
        $flag < 1 and $all_flags_okay = 0;}
    $all_flags_okay and ++$good_records;}

print "Good records: $good_records\n",
   "Repeated timestamps:\n",
   map {"  $_\n"}
   grep {$dates{$_} > 1}
   sort keys %dates;


#include <iomanip>
#include <iostream>
#include <sstream>
using namespace std;

enum CipherMode {ENCRYPT, DECRYPT};

// External results
uint32_t randRsl[256];
uint32_t randCnt;

// Internal state
uint32_t mm[256];
uint32_t aa = 0, bb = 0, cc = 0;

void isaac()
{
    ++cc;  // cc just gets incremented once per 256 results
    bb += cc; // then combined with bb

    for (uint32_t i = 0; i < 256; ++i)
    {
        uint32_t x, y;

        x = mm[i];
        switch (i % 4)
        {
            case 0:
                aa = aa ^ (aa << 13);
                break;
            case 1:
                aa = aa ^ (aa >> 6);
                break;
            case 2:
                aa = aa ^ (aa << 2);
                break;
            case 3:
                aa = aa ^ (aa >> 16);
                break;
        }
        aa = mm[(i + 128) % 256] + aa;
        y = mm[(x >> 2) % 256] + aa + bb;
        mm[i] = y;
        bb = mm[(y >> 10) % 256] + x;
        randRsl[i] = bb;
    }
    randCnt = 0; // Prepare to use the first set of results.
}

void mix(uint32_t a[])
{
    a[0] = a[0] ^ a[1] << 11; a[3] += a[0]; a[1] += a[2];
    a[1] = a[1] ^ a[2] >>  2; a[4] += a[1]; a[2] += a[3];
    a[2] = a[2] ^ a[3] <<  8; a[5] += a[2]; a[3] += a[4];
    a[3] = a[3] ^ a[4] >> 16; a[6] += a[3]; a[4] += a[5];
    a[4] = a[4] ^ a[5] << 10; a[7] += a[4]; a[5] += a[6];
    a[5] = a[5] ^ a[6] >>  4; a[0] += a[5]; a[6] += a[7];
    a[6] = a[6] ^ a[7] <<  8; a[1] += a[6]; a[7] += a[0];
    a[7] = a[7] ^ a[0] >>  9; a[2] += a[7]; a[0] += a[1];
}

void randInit(bool flag)
{
    uint32_t a[8];
    aa = bb = cc = 0;

    a[0] = 2654435769UL; // 0x9e3779b9: the golden ratio
    for (uint32_t j = 1; j < 8; ++j)
        a[j] = a[0];

    for (uint32_t i = 0; i < 4; ++i) // Scramble it.
        mix(a);
    for (uint32_t i = 0; i < 256; i += 8) // Fill in mm[] with messy stuff.
    {
        if (flag) // Use all the information in the seed.
            for (uint32_t j = 0; j < 8; ++j)
                a[j] += randRsl[i + j];
        mix(a);
        for (uint32_t j = 0; j < 8; ++j)
            mm[i + j] = a[j];
    }

    if (flag)
    {   // Do a second pass to make all of the seed affect all of mm.
        for (uint32_t i = 0; i < 256; i += 8)
        {
            for (uint32_t j = 0; j < 8; ++j)
                a[j] += mm[i + j];
            mix(a);
            for (uint32_t j = 0; j < 8; ++j)
                mm[i + j] = a[j];
        }
    }
    isaac(); // Fill in the first set of results.
    randCnt = 0; // Prepare to use the first set of results.
}

// Seed ISAAC with a given string.
// The string can be any size. The first 256 values will be used.
void seedIsaac(string seed, bool flag)
{
    uint32_t seedLength = seed.length();
    for (uint32_t i = 0; i < 256; i++)
        mm[i] = 0;
    for (uint32_t i = 0; i < 256; i++)
        // In case seed has less than 256 elements
        randRsl[i] = i > seedLength ? 0 : seed[i];
    // Initialize ISAAC with seed
    randInit(flag);
}

// Get a random 32-bit value 0..MAXINT
uint32_t getRandom32Bit()
{
    uint32_t result = randRsl[randCnt];
    ++randCnt;
    if (randCnt > 255)
    {
        isaac();
        randCnt = 0;
    }
    return result;
}

// Get a random character in printable ASCII range
char getRandomChar()
{
    return getRandom32Bit() % 95 + 32;
}

// Convert an ASCII string to a hexadecimal string.
string ascii2hex(string source)
{
    uint32_t sourceLength = source.length();
    stringstream ss;
    for (uint32_t i = 0; i < sourceLength; i++)
        ss << setfill ('0') << setw(2) << hex << (int) source[i];
    return ss.str();
}

// XOR encrypt on random stream.
string vernam(string msg)
{
    uint32_t msgLength = msg.length();
    string destination = msg;
    for (uint32_t i = 0; i < msgLength; i++)
        destination[i] = getRandomChar() ^ msg[i];
    return destination;
}

// Caesar-shift a character <shift> places: Generalized Vigenere
char caesar(CipherMode m, char ch, char shift, char modulo, char start)
{
    int n;
    if (m == DECRYPT)
        shift = -shift;
    n = (ch - start) + shift;
    n %= modulo;
    if (n < 0)
        n += modulo;
    return start + n;
}

// Vigenere mod 95 encryption & decryption.
string vigenere(string msg, CipherMode m)
{
    uint32_t msgLength = msg.length();
    string destination = msg;
    // Caesar-shift message
    for (uint32_t i = 0; i < msgLength; ++i)
        destination[i] = caesar(m, msg[i], getRandomChar(), 95, ' ');
    return destination;
}

int main()
{
    // TASK globals
    string msg = "a Top Secret secret";
    string key = "this is my secret key";
    string xorCipherText, modCipherText, xorPlainText, modPlainText;

    // (1) Seed ISAAC with the key
    seedIsaac(key, true);
    // (2) Encryption
    // (a) XOR (Vernam)
    xorCipherText = vernam(msg);
    // (b) MOD (Vigenere)
    modCipherText = vigenere(msg, ENCRYPT);
    // (3) Decryption
    seedIsaac(key, true);
    // (a) XOR (Vernam)
    xorPlainText = vernam(xorCipherText);
    // (b) MOD (Vigenere)
    modPlainText = vigenere(modCipherText, DECRYPT);
    // Program output
    cout << "Message: " << msg << endl;
    cout << "Key    : " << key << endl;
    cout << "XOR    : " << ascii2hex(xorCipherText) << endl;
    cout << "MOD    : " << ascii2hex(modCipherText) << endl;
    cout << "XOR dcr: " << xorPlainText << endl;
    cout << "MOD dcr: " << modPlainText << endl;
}