record badrun(count,fromdate,todate)  # record to track bad runs

procedure main()
return mungetask1("readings1-input.txt","readings1-output.txt")
end

procedure mungetask1(fin,fout)

fin  := open(fin)      | stop("Unable to open input file ",fin)
fout := open(fout,"w") | stop("Unable to open output file ",fout)

F_tot := F_acc := F_rej := 0                       # data set totals
rejmax := badrun(-1)                               # longest reject runs
rejcur := badrun(0)                                # current reject runs

while line := read(fin) do {
   line ? {
      ldate := tab(many(&digits ++ '-'))           # date (poorly checked)
      fields := tot := rej := 0                    # record counters & totals

      while tab(many(' \t')) do {                  # whitespace before every pair
         value := real(tab(many(&digits++'-.')))   | stop("Bad value in ",ldate)
         tab(many(' \t'))
         flag := integer(tab(many(&digits++'-')))  | stop("Bad flag in ",ldate)
         fields +:= 1

         if flag > 0 then {                        # good data, ends a bad run
            if rejcur.count > rejmax.count then rejmax := rejcur
            rejcur := badrun(0)
            tot +:= value
            }
         else {                                    # bad (flagged) data
            if rejcur.count = 0 then rejcur.fromdate := ldate
            rejcur.todate := ldate
            rejcur.count +:= 1
            rej +:= 1
            }
         }
      }
   F_tot +:= tot
   F_acc +:= acc := fields - rej
   F_rej +:= rej
   write(fout,"Line: ",ldate," Reject: ", rej," Accept: ", acc," Line_tot: ",tot," Line_avg: ", if acc > 0 then tot / acc else 0)
   }

write(fout,"\nTotal    = ",F_tot,"\nReadings = ",F_acc,"\nRejects  = ",F_rej,"\nAverage  = ",F_tot / F_acc)
if rejmax.count > 0 then
   write(fout,"Maximum run of bad data was ",rejmax.count," readings from ",rejmax.fromdate," to ",rejmax.todate)
else
   write(fout,"No bad runs of data")
end


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

include builtins\timedate.e

integer all_good = 0

string fmt = "%d-%d-%d\t"&join(repeat("%f",48),'\t')
sequence extset = sq_mul(tagset(24),2), -- {2,4,6,..48}
         curr, last

for i=1 to length(lines) do
    string li = lines[i]
    sequence r = scanf(li,fmt)
    if length(r)!=1 then
        printf(1,"bad line [%d]:%s\n",{i,li})
    else
        curr = r[1][1..3]
        if i>1 and curr=last then
            printf(1,"duplicate line for %04d/%02d/%02d\n",last)
        end if
        last = curr
        all_good += sum(sq_le(extract(r[1][4..$],extset),0))=0
    end if
end for

printf(1,"Valid records %d of %d total\n",{all_good, length(lines)})

?"done"
{} = wait_key()


$handle = fopen("readings.txt", "rb");
$missformcount = 0;
$totalcount = 0;
$dates = array();
while (!feof($handle)) {
    $buffer = fgets($handle);
	$line = preg_replace('/\s+/',' ',$buffer);
	$line = explode(' ',trim($line));
	$datepattern = '/^\d{4}-\d{2}-\d{2}$/';
	$valpattern = '/^\d+\.{1}\d{3}$/';
	$flagpattern = '/^[1-9]{1}$/';

	if(count($line) != 49) $missformcount++;
	if(!preg_match($datepattern,$line[0],$check)) $missformcount++;
	else $dates[$totalcount+1] = $check[0];

	$errcount = 0;
	for($i=1;$i<count($line);$i++){
		if($i%2!=0){
			if(!preg_match($valpattern,$line[$i],$check)) $errcount++;
		}else{
			if(!preg_match($flagpattern,$line[$i],$check)) $errcount++;
		}
	}
	if($errcount != 0) $missformcount++;
	$totalcount++;
}
fclose ($handle);
$good = $totalcount - $missformcount;
$duplicates = array_diff_key( $dates , array_unique( $dates ));
echo 'Valid records ' . $good . ' of ' . $totalcount . ' total
';
echo 'Duplicates :
';
foreach ($duplicates as $key => $val){
	echo $val . ' at Line : ' . $key . '
';
}


(defpackage isaac
  (:use cl))

(in-package isaac)

(deftype uint32 () '(unsigned-byte 32))
(deftype arru32 () '(simple-array uint32))

(defstruct state
  (randrsl (make-array 256 :element-type 'uint32) :type arru32)
  (randcnt 0 :type uint32)
  (mm (make-array 256 :element-type 'uint32) :type arru32)
  (aa 0 :type uint32)
  (bb 0 :type uint32)
  (cc 0 :type uint32))

(defparameter *global-state* (make-state))

;; Some helper functions to force 32-bit arithmetic.
;; COERCE32 will be used to ensure the 32-bit results from
;; the given operations.
(declaim (inline lsh32 rsh32 add32 mod32 xor32))

(defmacro coerce32 (thing)
  `(ldb (byte 32 0) ,thing))

;; ASH is split into lsh32 and rsh32 to satisfy the compiler and
;; allow inlining.
(declaim (ftype (function (uint32 (unsigned-byte 6)) uint32) lsh32))
(defun lsh32 (integer count)
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0)))
  (coerce32 (ash integer count)))

(declaim (ftype (function (uint32 uint32) uint32) rsh32 add32 mod32 xor32))
(defun rsh32 (integer count)
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0)))
  (coerce32 (ash integer (- count))))

(defun add32 (x y)
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0)))
  (coerce32 (+ x y)))

(defun mod32 (number divisor)
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0)))
  (coerce32 (mod number divisor)))

(defun xor32 (x y)
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0)))
  (coerce32 (logxor x y)))

(defmacro incf32 (place &optional (delta 1))
  `(setf ,place (add32 ,place ,delta)))

(defun isaac (&optional (state *global-state*))
  "The ISAAC function."
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0))
           (type state state))
  (with-slots (randrsl randcnt mm aa bb cc) state
    (incf32 cc)
    (incf32 bb cc)
    (dotimes (i 256)
      (let ((x (aref mm i)))
        (setf aa (add32 (aref mm (mod32 (add32 i 128) 256))
                        (xor32 aa
                               (ecase (mod32 i 4)
                                 (0 (lsh32 aa 13))
                                 (1 (rsh32 aa 6))
                                 (2 (lsh32 aa 2))
                                 (3 (rsh32 aa 16))))))
        (let ((y (add32 (aref mm (mod32 (rsh32 x 2) 256))
                        (add32 aa
                               bb))))
          (setf (aref mm i) y)
          (setf bb (add32 (aref mm (mod32 (rsh32 y 10) 256))
                          x))
          (setf (aref randrsl i) bb))))
    (setf randcnt 0)
    (values)))

(defmacro mix (&rest places)
  "The magic mixer that spits out code to mix the given places."
  (let ((len (length places))
        (kernel '#0=(11 -2 8 -16 10 -4 8 -9 . #0#)))
    (rplacd (last places) places)
    `(progn
       ,@(loop
            for i from 0
            for n in kernel
            until (= i len)
            append
              (destructuring-bind (a b c d . rest) places
                (declare (ignore rest))
                (pop places)
                `((setf ,a (xor32 ,a ,(if (> n 0) `(lsh32 ,b ,n) `(rsh32 ,b ,(- n)))))
                  (incf32 ,d ,a)
                  (incf32 ,b ,c)))))))

(defun replace-tree (value replacement tree)
  "Replace all of the values in the given expression with the replacement."
  (if (atom tree)
      (if (equal tree value)
          replacement
          tree)
      (cons (replace-tree value replacement (car tree))
            (if (null (cdr tree))
                nil
                (replace-tree value replacement (cdr tree))))))

(defmacro unroller (index-name place-name places &body body)
  "A helper for unrolling a section of a loop's index with the given places."
  `(progn ,@(loop
               for place in places
               for i from 0 below (length places) append
                 `(,@(if (= i 0)
                         (replace-tree place-name place body)
                         (replace-tree index-name `(add32 ,index-name ,i)
                                       (replace-tree place-name place body)))))))

(defun randinit (flag &optional (state *global-state*))
  "Initialize the given state."
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0))
           (type state state))
  (with-slots (randrsl randcnt mm aa bb cc) state
    (let* ((a #x9e3779b9) (b a) (c a) (d a) (e a) (f a) (g a) (h a))
      (setf aa 0)
      (setf bb 0)
      (setf cc 0)
      (loop repeat 4 do
           (mix a b c d e f g h))
      (loop for idx from 0 below 256 by 8 do
           (when flag
             (unroller idx place (a b c d e f g h)
               (incf32 place (aref randrsl idx))))
           (mix a b c d e f g h)
           (unroller idx place (a b c d e f g h)
             (setf (aref mm idx) place)))
      (when flag
        (loop for idx from 0 below 256 by 8 do
             (unroller idx place (a b c d e f g h)
               (incf32 place (aref mm idx)))
             (mix a b c d e f g h)
             (unroller idx place (a b c d e f g h)
               (setf (aref mm idx) place)))))
    (isaac state)
    (setf randcnt 0)
    (values)))

(defun i-random (&optional (state *global-state*))
  "Get a random integer from the given state."
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0))
           (type state state))
  (with-slots (randrsl randcnt) state
    (prog1 (aref randrsl randcnt)
      (incf32 randcnt)
      (when (> randcnt 255)
        (isaac state)
        (setf randcnt 0)))))

(defun i-rand-a (&optional (state *global-state*))
  "Get a random printable character from the given state."
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0))
           (type state state))
  (add32 (mod32 (i-random state) 95) 32))

(defun i-seed (seed flag &optional (state *global-state*))
  "Seed the given state with a string of up to 256 characters."
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0))
           (type state state)
           (type string seed))
  (with-slots (randrsl mm) state
    (dotimes (i 256)
      (setf (aref mm i) 0))
    (let ((m (length seed)))
      (dotimes (i 256)
        (setf (aref randrsl i)
              (if (>= i m)
                  0
                  (char-code (char seed i))))))
    (randinit flag state)
    (values)))

(defun vernam (msg &optional (state *global-state*))
  "Vernam encode MSG with STATE."
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0))
           (type state state)
           (type string msg))
  (let* ((l (length msg))
         (v (make-string l)))
    (dotimes (i l)
      (setf (aref v i) (code-char (logxor (i-rand-a state) (char-code (char msg i))))))
    v))

;; Cipher modes: encipher, decipher, none
(defconstant +mod+ 95)
(defconstant +start+ 32)

(defun caesar (mode char shift modulo start)
  "Caesar encode the given character."
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0))
           (type uint32 char shift modulo start))
  (when (eq mode 'decipher)
    (setf shift (- shift)))
  (let ((n (mod (+ (- char start) shift) modulo)))
    (when (< n 0)
      (incf n modulo))
    (+ start n)))

(defun caesar-str (mode msg modulo start &optional (state *global-state*))
  "Caesar encode or decode MSG with STATE."
  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0))
           (type string msg)
           (type fixnum modulo start)
           (type state state))
  (let* ((l (length msg))
         (c (make-string l)))
    (dotimes (i l)
      (setf (aref c i) (code-char (caesar mode (char-code (char msg i)) (i-rand-a state) modulo start))))
    c))

(defun print-hex (string)
  (loop for c across string do (format t "~2,'0x" (char-code c))))

(defun main-test ()
  (let ((state (make-state))
        (msg "a Top Secret secret")
        (key "this is my secret key"))
    (i-seed key t state)
    (let ((vctx (vernam msg state))
          (cctx (caesar-str 'encipher msg +mod+ +start+ state)))
      (i-seed key t state)
      (let ((vptx (vernam vctx state))
            (cptx (caesar-str 'decipher cctx +mod+ +start+ state)))
        (format t "Message: ~a~%" msg)
        (format t "Key    : ~a~%" key)
        (format t "XOR    : ")
        (print-hex vctx)
        (terpri)
        (format t "XOR dcr: ~a~%" vptx)
        (format t "MOD    : ")
        (print-hex cctx)
        (terpri)
        (format t "MOD dcr: ~a~%" cptx))))
  (values))


  load 'files'
  parseLine=: 10&({. ,&< (_99&".;._1)@:}.)  NB. custom parser
  summarize=: # , +/ , +/ % #               NB. count,sum,mean
  filter=: #~ 0&<                           NB. keep valid measurements

  'Dates dat'=: |: parseLine;._2 CR -.~ fread jpath '~temp/readings.txt'
  Vals=:  (+: i.24){"1 dat
  Flags=: (>: +: i.24){"1 dat
  DailySummary=: Vals [email protected]"1 Flags
  RunLengths=: ([: #(;.1) 0 , }. *. }:) , 0 >: Flags
   ]MaxRun=: >./ RunLengths
589
   ]StartDates=: Dates {~ (>:@[email protected]&MaxRun (24 <[email protected]%~ +/)@{. ]) RunLengths
1993-03-05


import java.io.File;
import java.util.*;
import static java.lang.System.out;

public class TextProcessing1 {

    public static void main(String[] args) throws Exception {
        Locale.setDefault(new Locale("en", "US"));
        Metrics metrics = new Metrics();

        int dataGap = 0;
        String gapBeginDate = null;
        try (Scanner lines = new Scanner(new File("readings.txt"))) {
            while (lines.hasNextLine()) {

                double lineTotal = 0.0;
                int linePairs = 0;
                int lineInvalid = 0;
                String lineDate;

                try (Scanner line = new Scanner(lines.nextLine())) {

                    lineDate = line.next();

                    while (line.hasNext()) {
                        final double value = line.nextDouble();
                        if (line.nextInt() <= 0) {
                            if (dataGap == 0)
                                gapBeginDate = lineDate;
                            dataGap++;
                            lineInvalid++;
                            continue;
                        }
                        lineTotal += value;
                        linePairs++;

                        metrics.addDataGap(dataGap, gapBeginDate, lineDate);
                        dataGap = 0;
                    }
                }
                metrics.addLine(lineTotal, linePairs);
                metrics.lineResult(lineDate, lineInvalid, linePairs, lineTotal);
            }
            metrics.report();
        }
    }

    private static class Metrics {
        private List<String[]> gapDates;
        private int maxDataGap = -1;
        private double total;
        private int pairs;
        private int lineResultCount;

        void addLine(double tot, double prs) {
            total += tot;
            pairs += prs;
        }

        void addDataGap(int gap, String begin, String end) {
            if (gap > 0 && gap >= maxDataGap) {
                if (gap > maxDataGap) {
                    maxDataGap = gap;
                    gapDates = new ArrayList<>();
                }
                gapDates.add(new String[]{begin, end});
            }
        }

        void lineResult(String date, int invalid, int prs, double tot) {
            if (lineResultCount >= 3)
                return;
            out.printf("%10s  out: %2d  in: %2d  tot: %10.3f  avg: %10.3f%n",
                    date, invalid, prs, tot, (prs > 0) ? tot / prs : 0.0);
            lineResultCount++;
        }

        void report() {
            out.printf("%ntotal    = %10.3f%n", total);
            out.printf("readings = %6d%n", pairs);
            out.printf("average  = %010.3f%n", total / pairs);
            out.printf("%nmaximum run(s) of %d invalid measurements: %n",
                    maxDataGap);
            for (String[] dates : gapDates)
                out.printf("begins at %s and ends at %s%n", dates[0], dates[1]);

        }
    }
}


import util.

go =>
  Readings = [split(Record) : Record in read_file_lines("readings.txt")],
  DateStamps = new_map(),
  GoodReadings = 0,
  foreach({Rec,Id} in zip(Readings,1..Readings.length))
    if Rec.length != 49 then printf("Entry %d has bad_length %d\n", Id, Rec.length) end,
    Date = Rec[1],
    if DateStamps.has_key(Date) then
      printf("Entry %d (date %w) is a duplicate of entry %w\n", Id, Date, DateStamps.get(Date))
    else
      if sum([1: I in 3..2..49, check_field(Rec[I])]) == 0 then
         GoodReadings := GoodReadings + 1
      end
    end,
    DateStamps.put(Date, Id)
  end,
  nl,
  printf("Total readings: %d\n",Readings.len),
  printf("Good readings: %d\n",GoodReadings),
  nl.

check_field(Field) =>
  Field == "-2" ; Field == "-1" ; Field == "0".


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

(load "@lib/misc.l")

(in (opt)
   (until (eof)
      (let Lst (split (line) "^I")
         (unless
            (and
               (= 49 (length Lst))     # Check total length
               ($dat (car Lst) "-")    # Check for valid date
               (fully                  # Check data format
                  '((L F)
                     (if F                         # Alternating:
                        (format L 3)               # Number
                        (>= 9 (format L) -9) ) )   # or flag
                  (cdr Lst)
                  '(T NIL .) ) )
            (prinl "Bad line format: " (glue " " Lst))
            (bye 1) ) ) ) )

(bye)


/* To process readings produced by automatic reading stations. */

check: procedure options (main);
   declare 1 date, 2 (yy, mm, dd) character (2),
           (j1, j2) character (1);
   declare old_date character (6);
   declare line character (330) varying;
   declare R(24) fixed decimal, Machine(24) fixed binary;
   declare (i, k, n, faulty static initial (0)) fixed binary;
   declare input file;

   open file (input) title ('/READINGS.TXT,TYPE(CRLF),RECSIZE(300)');

   on endfile (input) go to done;

   old_date = '';
   k = 0;
   do forever;
      k = k + 1;

      get file (input) edit (line) (L);
      get string(line) edit (yy, j1, mm, j2, dd) (a(4), a(1), a(2), a(1), a(2));

      line = substr(line, 11);

      do i = 1 to length(line);
         if substr(line, i, 1) = '09'x then substr(line, i, 1) = ' ';
      end;
      line = trim(line);
      n = tally(line, ' ') - tally (line, '  ') + 1;

      if n ^= 48 then
         do;
            put skip list ('There are ' || n || ' readings in line ' || k);
         end;

      n = n/2;
      line = line || ' ';

      get string(line) list ((R(i), Machine(i) do i = 1 to n));

      if any(Machine < 1) ^= '0'B then
         faulty = faulty + 1;
      if old_date ^= ' ' then if old_date = string(date) then
         put skip list ('Dates are the same at line' || k);
      old_date = string(date);
   end;
done:
   put skip list ('There were ' || k || ' sets of readings');
   put skip list ('There were ' || faulty || ' faulty readings' );
   put skip list ('There were ' || k-faulty || ' good readings' );
end check;


import std.algorithm: min;
import std.algorithm: copy;
import std.typetuple: TypeTuple;
import std.typecons: staticIota;

struct ISAAC {
    // External results.
    private uint[mm.length] randResult;
    private uint randCount;

    // Internal state.
    private uint[256] mm;
    private uint aa, bb, cc;

    private void isaac() pure nothrow @safe @nogc {
        cc++;         // cc just gets incremented once per mm.length results.
        bb = bb + cc; // Then combined with bb.

        foreach (immutable i, ref mmi; mm) {
            immutable x = mm[i];
            final switch (i % 4) { // Not enforced final switch.
                case 0: aa ^= (aa << 13); break;
                case 1: aa ^= (aa >>  6); break;
                case 2: aa ^= (aa <<  2); break;
                case 3: aa ^= (aa >> 16); break;
            }

            aa = mm[(i + 128) % $] + aa;
            immutable y = mm[(x >> 2) % $] + aa + bb;
            bb = mm[(y >> 10) % $] + x;
            randResult[i] = bb;
        }

        randCount = 0;
    }

    // If flag is true then use the contents of randResult to initialize mm.
    private pure nothrow @safe @nogc static void mix(ref uint[8] a)  {
        alias shifts = TypeTuple!(11, 2, 8, 16, 10, 4, 8, 9);
        /*static*/ foreach (immutable i, immutable sh; shifts) {
            static if (i % 2 == 0)
                a[i] ^= a[(i + 1) % $] << sh;
            else
                a[i] ^= a[(i + 1) % $] >> sh;
            a[(i + 3) % $] += a[i];
            a[(i + 1) % $] += a[(i + 2) % $];
        }
    }

    private void randInit(bool flag)() pure nothrow @safe @nogc {
        uint[8] a = 0x9E37_79B9; // The Golden Ratio.
        aa = bb = cc = 0;

        // Scramble it.
        /*static*/ foreach (immutable i; staticIota!(0, 4))
            mix(a);

        // Fill in mm with messy stuff. Use all the information in the seed.
        for (size_t i = 0; i < mm.length; i += 8) {
            static if (flag)
                a[] += randResult[i .. i + 8];
            mix(a);
            mm[i .. i + 8] = a[];
        }

        // Do a second pass to make all of the seed affect all of mm.
        static if (flag) {
            for (size_t i = 0; i < mm.length; i += 8) {
                a[] += mm[i .. i + 8];
                mix(a);
                mm[i .. i + 8] = a[];
            }
        }

        isaac();       // Fill in the first set of results.
        randCount = 0; // Prepare to use the first set of results.
    }

    /// Seed ISAAC with a string.
    /// Uses only the first randResult.length ubytes.
    public void iSeed(bool flag)(in ubyte[] seed) pure nothrow @safe @nogc {
        mm[] = 0;
        randResult[] = 0;

        immutable n = min(randResult.length, seed.length);
        copy(seed[0 .. n], randResult[0 .. n]);

        randInit!flag(); // Initialize ISAAC with seed.
    }

    /// Get a random uint.
    private uint iRandom() pure nothrow @safe @nogc {
        immutable result = randResult[randCount];

        randCount++;
        if (randCount > (randResult.length - 1)) {
            isaac();
            randCount = 0;
        }

        return result;
    }

    /// Get a random character in printable ASCII range.
    private ubyte iRandA() pure nothrow @safe @nogc {
        return iRandom() % 95 + 32;
    }

    /// XOR encrypt on random stream.
    /// buffer must be as large as message or larger.
    public ubyte[] vernam(in ubyte[] message, ubyte[] buffer)
    pure nothrow @safe @nogc
    in {
        assert(buffer.length >= message.length);
    } out(result) {
        assert(result.length == message.length);
    } body {
        auto v = buffer[0 .. message.length];

        // XOR message.
        foreach (immutable i, immutable msgi; message)
            v[i] = (iRandA() ^ msgi);
        return v;
    }

    /// XOR encrypt on random stream.
    public ubyte[] vernam(in ubyte[] message) pure nothrow @safe {
        return vernam(message, new ubyte[message.length]);
    }
}

void main() {
    import std.stdio, std.string;

    immutable message = "a Top Secret secret";
    immutable key = "this is my secret key";

    writeln("Message  : ", message);
    writeln("Key      : ", key);

    ISAAC cipher;

    // Encrypt.
    // iSeed uses only the first ISAAC.randResult.length ubytes.
    cipher.iSeed!true(key.representation);
    const encrypted = cipher.vernam(message.representation);

    // Output ciphertext as a string of hexadecimal digits.
    writefln("Encrypted: %(%02X%)", encrypted);

    // Decrypt.
    cipher.iSeed!true(key.representation);
    const decrypted = cipher.vernam(encrypted);

    writeln("Decrypted: ", decrypted.assumeUTF);
}


var filename = 'readings.txt';
var show_lines = 5;
var file_stats = {
    'num_readings': 0,
    'total': 0,
    'reject_run': 0,
    'reject_run_max': 0,
    'reject_run_date': ''
};

var fh = new ActiveXObject("Scripting.FileSystemObject").openTextFile(filename, 1); // 1 = for reading
while ( ! fh.atEndOfStream) {
    var line = fh.ReadLine();
    line_stats(line, (show_lines-- > 0));
}
fh.close();

WScript.echo(
    "\nFile(s)  = " + filename + "\n" +
    "Total    = " + dec3(file_stats.total) + "\n" +
    "Readings = " + file_stats.num_readings + "\n" +
    "Average  = " + dec3(file_stats.total / file_stats.num_readings) + "\n\n" +
    "Maximum run of " + file_stats.reject_run_max +
    " consecutive false readings ends at " + file_stats.reject_run_date
);

function line_stats(line, print_line) {
    var readings = 0;
    var rejects = 0;
    var total = 0;
    var fields = line.split('\t');
    var date = fields.shift();

    while (fields.length > 0) {
        var value = parseFloat(fields.shift());
        var flag = parseInt(fields.shift(), 10);
        readings++;
        if (flag <= 0) {
            rejects++;
            file_stats.reject_run++;
        }
        else {
            total += value;
            if (file_stats.reject_run > file_stats.reject_run_max) {
                file_stats.reject_run_max = file_stats.reject_run;
                file_stats.reject_run_date = date;
            }
            file_stats.reject_run = 0;
        }
    }

    file_stats.num_readings += readings - rejects;
    file_stats.total += total;

    if (print_line) {
        WScript.echo(
            "Line: " + date + "\t" +
            "Reject: " + rejects + "\t" +
            "Accept: " + (readings - rejects) + "\t" +
            "Line_tot: " + dec3(total) + "\t" +
            "Line_avg: " + ((readings == rejects) ? "0.0" : dec3(total / (readings - rejects)))
        );
    }
}

// round a number to 3 decimal places
function dec3(value) {
    return Math.round(value * 1e3) / 1e3;
}


$dateHash = @{}
$goodLineCount = 0
get-content c:\temp\readings.txt |
    ForEach-Object {
        $line = $_.split(" |`t",2)
        if ($dateHash.containskey($line[0])) {
            $line[0] + " is duplicated"
        } else {
            $dateHash.add($line[0], $line[1])
        }
        $readings = $line[1].split()
        $goodLine = $true
        if ($readings.count -ne 48) { $goodLine = $false; "incorrect line length : $line[0]"  }
        for ($i=0; $i -lt $readings.count; $i++) {
            if ($i % 2 -ne 0) {
                if ([int]$readings[$i] -lt 1) {
                    $goodLine = $false
                }
            }
        }
        if ($goodLine) { $goodLineCount++ }
    }
[string]$goodLineCount + " good lines"


Define filename.s = "readings.txt"
#instrumentCount = 24

Enumeration
  #exp_date
  #exp_instruments
  #exp_instrumentStatus
EndEnumeration

Structure duplicate
  date.s
  firstLine.i
  line.i
EndStructure

NewMap dates() ;records line date occurs first
NewList duplicated.duplicate()
NewList syntaxError()
Define goodRecordCount, totalLines, line.s, i
Dim inputDate.s(0)
Dim instruments.s(0)

If ReadFile(0, filename)
  CreateRegularExpression(#exp_date, "\d+-\d+-\d+")
  CreateRegularExpression(#exp_instruments, "(\t|\x20)+(\d+\.\d+)(\t|\x20)+\-?\d")
  CreateRegularExpression(#exp_instrumentStatus, "(\t|\x20)+(\d+\.\d+)(\t|\x20)+")
  Repeat
    line = ReadString(0, #PB_Ascii)
    If line = "": Break: EndIf
    totalLines + 1

    ExtractRegularExpression(#exp_date, line, inputDate())
    If FindMapElement(dates(), inputDate(0))
      AddElement(duplicated())
      duplicated()\date = inputDate(0)
      duplicated()\firstLine = dates()
      duplicated()\line = totalLines
    Else
      dates(inputDate(0)) = totalLines
    EndIf

    ExtractRegularExpression(#exp_instruments, Mid(line, Len(inputDate(0)) + 1), instruments())
    Define pairsCount = ArraySize(instruments()), containsBadValues = #False
    For i =  0 To pairsCount
      If Val(ReplaceRegularExpression(#exp_instrumentStatus, instruments(i), "")) < 1
        containsBadValues = #True
        Break
      EndIf
    Next

    If pairsCount <> #instrumentCount - 1
      AddElement(syntaxError()): syntaxError() = totalLines
    EndIf
    If Not containsBadValues
      goodRecordCount + 1
    EndIf
  ForEver
  CloseFile(0)

  If OpenConsole()
    ForEach duplicated()
      PrintN("Duplicate date: " + duplicated()\date + " occurs on lines " + Str(duplicated()\line) + " and " + Str(duplicated()\firstLine) + ".")
    Next
    ForEach syntaxError()
      PrintN( "Syntax error in line " + Str(syntaxError()))
    Next
    PrintN(#CRLF$ + Str(goodRecordCount) + " of " + Str(totalLines) + " lines read were valid records.")

    Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
    CloseConsole()
  EndIf
EndIf


{$apptype console}
PROGRAM RosettaIsaac;
USES SysUtils;

// TASK globals
VAR msg : STRING = 'a Top Secret secret';
VAR key : STRING = 'this is my secret key';
VAR xctx: STRING = ''; // XOR ciphertext
VAR mctx: STRING = ''; // MOD ciphertext

// ISAAC globals
// external results
VAR randrsl: ARRAY[0..256] OF CARDINAL;
VAR randcnt: cardinal;
// internal state
VAR mm: ARRAY[0..256] OF CARDINAL;
VAR aa: CARDINAL=0; bb: CARDINAL=0; cc: CARDINAL=0;

PROCEDURE Isaac;
VAR i,x,y: CARDINAL;
BEGIN
   cc := cc + 1;    // cc just gets incremented once per 256 results
   bb := bb + cc;   // then combined with bb

   FOR i := 0 TO 255 DO BEGIN
     x := mm[i];
     CASE (i mod 4) OF
		0: aa := aa xor (aa shl 13);
		1: aa := aa xor (aa shr 6);
		2: aa := aa xor (aa shl 2);
		3: aa := aa xor (aa shr 16);
     END;
     aa := mm[(i+128) mod 256] + aa;
	 y  := mm[(x shr 2) mod 256] + aa + bb;
     mm[i] := y;
     bb := mm[(y shr 10) mod 256] + x;
     randrsl[i]:= bb;
   END;
   // this reset was not in original readable.c!
   randcnt:=0;  // prepare to use the first set of results
END; {Isaac}

// if (flag==TRUE), then use the contents of randrsl[] to initialize mm[].
PROCEDURE mix(VAR a,b,c,d,e,f,g,h: CARDINAL);
BEGIN
	a := a xor b shl 11; d:=d+a; b:=b+c;
	b := b xor c shr  2; e:=e+b; c:=c+d;
	c := c xor d shl  8; f:=f+c; d:=d+e;
	d := d xor e shr 16; g:=g+d; e:=e+f;
	e := e xor f shl 10; h:=h+e; f:=f+g;
	f := f xor g shr  4; a:=a+f; g:=g+h;
	g := g xor h shl  8; b:=b+g; h:=h+a;
	h := h xor a shr  9; c:=c+h; a:=a+b;
END; {mix}

PROCEDURE iRandInit(flag: BOOLEAN);
VAR i,a,b,c,d,e,f,g,h: CARDINAL;
BEGIN
   aa:=0; bb:=0; cc:=0;
   a:=$9e3779b9; 	// the golden ratio

   b:=a; c:=a; d:=a; e:=a; f:=a; g:=a; h:=a;

   FOR i := 0 TO 3 DO          // scramble it
        mix(a,b,c,d,e,f,g,h);

   i:=0;
   REPEAT  // fill in mm[] with messy stuff
	IF flag THEN BEGIN     // use all the information in the seed
       a:=a+randrsl[i  ]; b:=b+randrsl[i+1]; c:=c+randrsl[i+2]; d:=d+randrsl[i+3];
       e:=e+randrsl[i+4]; f:=f+randrsl[i+5]; g:=g+randrsl[i+6]; h:=h+randrsl[i+7];
    END;

    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;
	i:=i+8;
   UNTIL i>255;

   IF (flag) THEN BEGIN
   // do a second pass to make all of the seed affect all of mm
     i:=0;
     REPEAT
      a:=a+mm[i  ]; b:=b+mm[i+1]; c:=c+mm[i+2]; d:=d+mm[i+3];
      e:=e+mm[i+4]; f:=f+mm[i+5]; g:=g+mm[i+6]; h:=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;
      i:=i+8;
     UNTIL i>255;
   END;
   isaac();           // fill in the first set of results
   randcnt:=0;       // prepare to use the first set of results
END; {randinit}

{ Seed ISAAC with a given string.
  The string can be any size. The first 256 values will be used.}
PROCEDURE iSeed(seed: STRING; flag: BOOLEAN);
VAR i,m: CARDINAL;
BEGIN
	FOR i:= 0 TO 255 DO mm[i]:=0;
	m := Length(seed)-1;
	FOR i:= 0 TO 255 DO BEGIN
	// in case seed has less than 256 elements
        IF i>m THEN randrsl[i]:=0
			// Pascal strings are 1-based
			ELSE randrsl[i]:=ord(seed[i+1]);
	END;
	// initialize ISAAC with seed
	iRandInit(flag);
END; {iSeed}

{ Get a random 32-bit value 0..MAXINT }
FUNCTION iRandom : Cardinal;
BEGIN
	result := randrsl[randcnt];
	inc(randcnt);
	IF (randcnt >255) THEN BEGIN
		Isaac();
		randcnt := 0;
	END;
END; {iRandom}

{ Get a random character in printable ASCII range }
FUNCTION iRandA: BYTE;
	BEGIN
		result := iRandom mod 95 + 32;
	END;

{ convert an ASCII string to a hexadecimal string }
FUNCTION ascii2hex(s: STRING): STRING;
	VAR i,l: CARDINAL;
	BEGIN
		result := '';
			l := Length(s);
			FOR i := 1 TO l DO
				result := result + IntToHex(ord(s[i]),2);
	END;

{ XOR encrypt on random stream. Output: string of hex chars }
FUNCTION Vernam(msg: STRING): STRING;
	VAR	i: CARDINAL;
	BEGIN
		result := '';
		FOR i := 1 to length(msg) DO
			result := result + chr(iRandA xor ord(msg[i]));
	result := ascii2hex(result);
	END;

{ Get position of the letter in chosen alphabet }
FUNCTION letternum(letter, start: CHAR): byte;
	BEGIN
		result := (ord(letter)-ord(start));
	END;

{ Caesar-shift a character <shift> places: Generalized Vigenere }
FUNCTION Caesar(ch: CHAR; shift, modulo: INTEGER; start: CHAR): CHAR;
	VAR n: INTEGER;
	BEGIN
		n := letternum(ch,start) + shift;
		n := n MOD modulo;
		result := chr(ord(start)+n);
	END;

{ Vigenere mod 95 encryption. Output: string of hex chars }
FUNCTION Vigenere(msg: STRING): STRING;
	VAR i: CARDINAL;
	BEGIN
		result := '';
		FOR i := 1 to length(msg) DO
			result := result + Caesar(msg[i],iRandA,95,' ');
		result := ascii2hex(result);
	END;

BEGIN
	// 1) seed ISAAC with the key
	iSeed(key,true);
	// 2) Vernam XOR encryption
	xctx := Vernam(msg);
	// 3) MOD encryption
	mctx := Vigenere(msg);
	// program output
	Writeln('Message: ',msg);
	Writeln('Key    : ',key);
	Writeln('XOR    : ',xctx);
	Writeln('MOD    : ',mctx);
END.


foreach STREAM as $row ( INITIAL; EXPRESSION; VALUE ).


using DataFrames

function mungdata(filename)
    lines = readlines(filename)
    numlines = length(lines)
    dates = Array{DateTime, 1}(numlines)
    means = zeros(Float64, numlines)
    numvalid = zeros(Int, numlines)
    invalidlength = zeros(Int, numlines)
    invalidpos = zeros(Int, numlines)
    datamatrix = Array{Float64,2}(numlines, 24)
    datamatrix .= NaN
    totalsum = 0.0
    totalgood = 0
    for (linenum,line) in enumerate(lines)
        data = split(line)
        validcount = badlength = 0
        validsum = 0.0
        for i in 2:2:length(data)-1
            if parse(Int, data[i+1]) >= 0
                validsum += (datamatrix[linenum, Int(i/2)] = parse(Float64, data[i]))
                validcount += 1
                badlength = 0
            else
                badlength += 1
                if badlength > invalidlength[linenum]
                    invalidlength[linenum] = badlength
                    invalidpos[linenum] = Int(i/2) - invalidlength[linenum] + 1
                end
            end
        end
        dates[linenum] = DateTime(data[1], "y-m-d")
        means[linenum] = validsum / validcount
        numvalid[linenum] = validcount
        totalsum += validsum
        totalgood += validcount
    end
    dt = DataFrame(Date = dates, Mean = means, ValidValues = numvalid,
                   MaximumGap = invalidlength, GapPosition = invalidpos)
    for i in 1:size(datamatrix)[2]
        dt[Symbol("$(i-1):00")] = datamatrix[:,i]
    end
    dt, totalsum/totalgood
end

datafilename = "data.txt" # this is taken from the example listed on the task, since the actual text file is not available
df, dmean = mungdata(datafilename)
println(df)
println("The overall mean is $dmean")
maxbadline = indmax(df[:MaximumGap])
maxbadval = df[:MaximumGap][maxbadline]
maxbadtime = df[:GapPosition][maxbadline] - 1
maxbaddate = replace("$(df[:Date][maxbadline])", r"T.+$", "")
println("The largest run of bad values is $(maxbadval), on $(maxbaddate) beginning at $(maxbadtime):00 hours.")


// version 1.2.31

import java.io.File

fun main(args: Array<String>) {
    val rx = Regex("""\s+""")
    val file = File("readings.txt")
    val fmt = "Line:  %s  Reject: %2d  Accept: %2d  Line_tot: %7.3f  Line_avg: %7.3f"
    var grandTotal = 0.0
    var readings = 0
    var date = ""
    var run = 0
    var maxRun = -1
    var finishLine = ""
    file.forEachLine { line ->
        val fields = line.split(rx)
        date = fields[0]
        if (fields.size == 49) {
            var accept = 0
            var total = 0.0
            for (i in 1 until fields.size step 2) {
                if (fields[i + 1].toInt() >= 1) {
                    accept++
                    total += fields[i].toDouble()
                    if (run > maxRun) {
                        maxRun = run
                        finishLine = date
                    }
                    run = 0
                }
                else run++
            }
            grandTotal += total
            readings += accept
            println(fmt.format(date, 24 - accept, accept, total, total / accept))
        }
        else println("Line:  $date does not have 49 fields and has been ignored")
    }

    if (run > maxRun) {
        maxRun = run
        finishLine = date
    }
    val average = grandTotal / readings
    println("\nFile     = ${file.name}")
    println("Total    = ${"%7.3f".format(grandTotal)}")
    println("Readings = $readings")
    println("Average  = ${"%-7.3f".format(average)}")
    println("\nMaximum run of $maxRun consecutive false readings")
    println("ends at line starting with date: $finishLine")
}


import re
import zipfile
import StringIO

def munge2(readings):

   datePat = re.compile(r'\d{4}-\d{2}-\d{2}')
   valuPat = re.compile(r'[-+]?\d+\.\d+')
   statPat = re.compile(r'-?\d+')
   allOk, totalLines = 0, 0
   datestamps = set([])
   for line in readings:
      totalLines += 1
      fields = line.split('\t')
      date = fields[0]
      pairs = [(fields[i],fields[i+1]) for i in range(1,len(fields),2)]

      lineFormatOk = datePat.match(date) and \
         all( valuPat.match(p[0]) for p in pairs ) and \
         all( statPat.match(p[1]) for p in pairs )
      if not lineFormatOk:
         print 'Bad formatting', line
         continue

      if len(pairs)!=24 or any( int(p[1]) < 1 for p in pairs ):
         print 'Missing values', line
         continue

      if date in datestamps:
         print 'Duplicate datestamp', line
         continue
      datestamps.add(date)
      allOk += 1

   print 'Lines with all readings: ', allOk
   print 'Total records: ', totalLines

#zfs = zipfile.ZipFile('readings.zip','r')
#readings = StringIO.StringIO(zfs.read('readings.txt'))
readings = open('readings.txt','r')
munge2(readings)


# Read in data from file
dfr <- read.delim("d:/readings.txt", colClasses=c("character", rep(c("numeric", "integer"), 24)))
dates <- strptime(dfr[,1], "%Y-%m-%d")

# Any bad values?
dfr[which(is.na(dfr))]

# Any duplicated dates
dates[duplicated(dates)]

# Number of rows with no bad values
flags <- as.matrix(dfr[,seq(3,49,2)])>0
sum(apply(flags, 1, all))


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

file_sum, file_cnt_data, file_lines = 0, 0, 0
max_rejected, n_rejected = 0, 0
max_rejected_date, rejected_date = "", ""

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

    date = string.match( data, "%d+%-%d+%-%d+" )
    if date == nil then break end

    val = {}
    for w in string.gmatch( data, "%s%-*%d+[%.%d]*" ) do
        val[#val+1] = tonumber(w)
    end

    sum, cnt = 0, 0
    for i = 1, #val, 2 do
    	if val[i+1] > 0 then
    	    sum = sum + val[i]
    	    cnt = cnt + 1
    	    n_rejected = 0
    	else
	    if n_rejected == 0 then
	        rejected_date = date
  	    end
    	    n_rejected = n_rejected + 1
    	    if n_rejected > max_rejected then
    	        max_rejected = n_rejected
    	        max_rejected_date = rejected_date
    	    end
    	end
    end

    file_sum = file_sum + sum
    file_cnt_data = file_cnt_data + cnt
    file_lines = file_lines + 1

    print( string.format( "%s:\tRejected: %d\tAccepted: %d\tLine_total: %f\tLine_average: %f", date, #val/2-cnt, cnt, sum, sum/cnt ) )
end

print( string.format( "\nFile:\t  %s", filename ) )
print( string.format( "Total:\t  %f", file_sum ) )
print( string.format( "Readings: %d", file_lines ) )
print( string.format( "Average:  %f", file_sum/file_cnt_data ) )
print( string.format( "Maximum %d consecutive false readings starting at %s.", max_rejected, max_rejected_date ) )


#lang racket
(read-decimal-as-inexact #f)
;; files to read is a sequence, so it could be either a list or vector of files
(define (text-processing/2 files-to-read)
  (define seen-datestamps (make-hash))
  (define (datestamp-seen? ds) (hash-ref seen-datestamps ds #f))
  (define (datestamp-seen! ds pos) (hash-set! seen-datestamps ds pos))

  (define (fold-into-pairs l (acc null))
    (match l ['() (reverse acc)]
      [(list _) (reverse (cons l acc))]
      [(list-rest a b tl) (fold-into-pairs tl (cons (list a b) acc))]))

  (define (match-valid-field line pos)
    (match (string-split line)
      ;; if we don't hit an error, then the file is valid
      ((list-rest (not (pregexp #px"[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}")) _)
       (error 'match-valid-field "invalid format non-datestamp at head: ~a~%" line))

      ;; check for duplicates
      ((list-rest (? datestamp-seen? ds) _)
       (printf "duplicate datestamp: ~a at line: ~a (first seen at: ~a)~%"
               ds pos (datestamp-seen? ds))
       #f)

      ;; register the datestamp as seen, then move on to rest of match
      ((list-rest ds _) (=> next-match-rule) (datestamp-seen! ds pos) (next-match-rule))

      ((list-rest
        _
        (app fold-into-pairs
             (list (list (app string->number (and (? number?) vs))
                         (app string->number (and (? integer?) statuss)))
                   ...)))
       (=> next-match-rule)
       (unless (= (length vs) 24) (next-match-rule))
       (not (for/first ((s statuss) #:unless (positive? s)) #t)))

      ;; if we don't hit an error, then the file is valid
      (else (error 'match-valid-field "bad field format: ~a~%" line))))

  (define (sub-t-p/1)
    (for/sum ((line (in-lines))
              (line-number (in-naturals 1)))
      (if (match-valid-field line line-number) 1 0)))
  (for/sum ((file-name files-to-read))
    (with-input-from-file file-name sub-t-p/1)))

(printf "~a records have good readings for all instruments~%"
        (text-processing/2 (current-command-line-arguments)))


my $good-records;
my $line;
my %dates;

for lines() {
    $line++;
    / ^
    (\d ** 4 '-' \d\d '-' \d\d)
    [ \h+ \d+'.'\d+ \h+ ('-'?\d+) ] ** 24
    $ /
        or note "Bad format at line $line" and next;
    %dates.push: $0 => $line;
    $good-records++ if $1.all >= 1;
}

say "$good-records good records out of $line total";

say 'Repeated timestamps (with line numbers):';
.say for sort %dates.pairs.grep: *.value.elems > 1;


' version 03-11-2016
' compile with: fbc -s console

Dim Shared As UInteger<32> randrsl(256), randcnt
Static Shared As UInteger<32> mm(256)
Static Shared As UInteger<32> aa, bb ,cc

Sub ISAAC()

    Dim As UInteger<32> i, x, y

    cc = cc + 1
    bb = bb + cc

    For i = 0 To 256 -1
        x = mm(i)
        Select Case (i Mod 4)
            Case 0 : aa = aa Xor (aa Shl 13)
            Case 1 : aa = aa Xor (aa Shr 6)
            Case 2 : aa = aa Xor (aa Shl 2)
            Case 3 : aa = aa Xor (aa Shr 16)
        End Select
        aa = mm((i+128) Mod 256) + aa
        y = mm((x Shr 2) Mod 256) + aa + bb : mm(i) = y
        bb = mm((y Shr 10) Mod 256) + x : randrsl(i) = bb
    Next

    randcnt = 0

End Sub

#Macro mix(a, b, c, d, e, f, g, h)

    a Xor= b Shl 11 : d += a : b += c
    b Xor= c Shr 2  : e += b : c += d
    c Xor= d Shl 8  : f += c : d += e
    d Xor= e Shr 16 : g += d : e += f
    e Xor= f Shl 10 : h += e : f += g
    f Xor= g Shr 4  : a += f : g += h
    g Xor= h Shl 8  : b += g : h += a
    h Xor= a Shr 9  : c += h : a += b

#EndMacro

Sub randinit(flag As Long)

    Dim As Long i
    Dim As UInteger<32> a = &H9e3779b9   '/* the golden ratio *
    Dim As UInteger<32> b = &H9e3779b9
    Dim As UInteger<32> c = &H9e3779b9
    Dim As UInteger<32> d = &H9e3779b9
    Dim As UInteger<32> e = &H9e3779b9
    Dim As UInteger<32> f = &H9e3779b9
    Dim As UInteger<32> g = &H9e3779b9
    Dim As UInteger<32> h = &H9e3779b9
    aa = 0 : bb = 0 : cc = 0

    For i = 0 To 3
        mix(a, b, c, d, e, f, g, h)
    Next

    For i = 0 To 255 Step 8
        If flag = 1 Then
            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
        End If
    Next

    If flag = 1 Then
        For i = 0 To 255 Step 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
        Next
    End If

    ISAAC()
    randcnt = 0

End Sub

' // Get a random 32-bit value 0..MAXINT
Function iRandom() As UInteger<32>

Dim As UInteger<32> r = randrsl(randcnt)
randcnt += 1
If randcnt > 255 Then
    ISAAC()
    randcnt = 0
End If

Return r

End Function

' // Get a random character in printable ASCII range
Function iRandA() As UByte

Return iRandom() Mod 95 +32

End Function

' // Seed ISAAC with a string
Sub iSeed(seed As String, flag As Long)

Dim As ULong i, m = Len(seed) -1

For i = 0 To 255
    mm(i) = 0
Next

For i = 0 To 255

    If i > m Then
        randrsl(i) = 0
    Else
        randrsl(i) = seed[i]
    End If

Next

randinit(flag)

End Sub

' // maximum length of message
'#define MAXMSG 4096
#Define _MOD_ 95      ' mod is FreeBASIC keyword
#Define _START_ 32    ' start is used as variable name

' // cipher modes for Caesar
Enum ciphermode
    mEncipher
    mDecipher
    mNone
End Enum

' // XOR cipher on random stream. Output: ASCII string
' no maximum lenght for input and output string
Function Vernam(msg As String) As String

Dim As ULong i
Dim As String v

For i = 0 To Len(msg) -1
    v += Chr(iRandA() Xor msg[i])
Next

Return v

End Function

' // Caesar-shift a printable character
Function Ceasar(m As ciphermode, ch As UByte, shift As UByte, modulo As UByte, _
                                                     start As UByte) As UByte

' FreeBASIC Mod does not handle negative numbers correctly
' also there is litte problem with shift (declared UByte)
' the IIF() statement helps with shift
' to avoid a negative n a 8 times modulo is added
' modulo * 8 get translateted by FreeBASIC to modulo shl 3
Dim As Long n = (ch - start) + IIf(m = mDecipher, -shift, shift) + modulo * 8
n = n Mod modulo
Return start + n

End Function

' // Caesar-shift a string on a pseudo-random stream
Function CeasarStr(m As ciphermode, msg As String, modulo As UByte, _
                                                    start As UByte) As String

Dim As Long i
Dim As String v

For i = 0 To Len(msg) -1
    v += Chr(Ceasar(m, msg[i], iRandA(), modulo, start))
Next

Return v

End Function

' ------=< MAIN >=------

Dim As Long n, l
Dim As String msg = "a Top Secret secret"
Dim As String key = "this is my secret key"

Dim As String vctx, vptx
Dim As String cctx, cptx

l = Len(msg)
' // Encrypt: Vernam XOR
iSeed(key, 1)
vctx = Vernam(msg)
' // Encrypt: Caesar
cctx = CeasarStr(mEncipher, msg, _mod_, _start_)
' // Decrypt: Vernam XOR
iSeed(key, 1)
vptx = Vernam(vctx)
' // Decrypt: Caesar
cptx = CeasarStr(mDecipher, cctx, _mod_, _start_)
Print "message: "; msg
Print "    key: "; key
Print "    XOR: ";
' // Output Vernam ciphertext as a string of hex digits
For n = 0 To l -1
    Print Hex(vctx[n], 2);
Next
Print
' // Output Vernam decrypted plaintext
Print "XOR dcr: "; vptx
' // Caesar
Print "    MOD: ";
' // Output Caesar ciphertext as a string of hex digits
For n= 0 To l -1
    Print Hex(cctx[n], 2);
Next
Print
' // Output Caesar decrypted plaintext
Print "MOD dcr: " ; cptx

' empty keyboard buffer
While InKey <> "" : Wend
Print : Print "hit any key to end program"
Sleep
End


FileName = "Readings.txt"; data = Import[FileName,"TSV"];
Scan[(a=Position[#[[3;;All;;2]],1];
Print["Line:",#[[1]] ,"\tReject:", 24 - Length[a], "\t Accept:", Length[a], "\tLine_tot:",
[email protected][#, Flatten[2*a]] , "\tLine_avg:", [email protected][#, Flatten[2*a]]/Length[a]])&, data]

GlobalSum = Nb = Running = MaxRunRecorded =  0; MaxRunTime = {};
Scan[ For[i = 3, i < 50, i = i + 2,
   If[#[[i]] == 1,
    Running=0; GlobalSum += #[[i-1]]; Nb++;,
    Running ++;  If[MaxRunRecorded < Running, MaxRunRecorded = Running;MaxRunTime={ #[[1]]}; ];
   ]] &, data ]

Print["\nFile(s) : ",FileName,"\nTotal : ",[email protected],"\nReadings : ",Nb,
"\nAverage : ",GlobalSum/Nb,"\n\nMaximum run(s) of ",MaxRunRecorded,
" consecutive false readings ends at line starting with date(s):",MaxRunTime]


import os, sequtils, strutils, strformat

var
  nodata = 0
  nodataMax = -1
  nodataMaxLine: seq[string]

  totFile = 0.0
  numFile = 0

for filename in commandLineParams():
  for line in filename.lines:
    var
      totLine = 0.0
      numLine = 0
      data: seq[float]
      flags: seq[int]

    let fields = line.split()
    let date = fields[0]

    for i, field in fields[1..^1]:
      if i mod 2 == 0: data.add parseFloat(field)
      else: flags.add parseInt(field)

    for datum, flag in zip(data, flags).items:
      if flag < 1:
        inc nodata
      else:
        if nodataMax == nodata and nodata > 0:
          nodataMaxLine.add date
        if nodataMax < nodata and nodata > 0:
          nodataMax = nodata
          nodataMaxLine = @[date]
        nodata = 0
        totLine += datum
        inc numLine

    totFile += totLine
    numFile += numLine

    let average = if numLine > 0: totLine / float(numLine) else: 0.0
    echo &"Line: {date}  Reject: {data.len - numLine:2}  Accept: {numLine:2}  ",
         &"LineTot: {totLine:6.2f} LineAvg: {average:4.2f}"

echo()
echo &"""File(s)   = {commandLineParams().join(" ")}"""
echo &"Total     = {totFile:.2f}"
echo &"Readings  = {numFile}"
echo &"Average   = {totFile / float(numFile):.2f}"
echo ""
echo &"Maximum run(s) of {nodataMax} consecutive false readings ",
     &"""ends at line starting with date(s): {nodataMaxLine.join(" ")}."""


/*REXX program to process  instrument data  from a  data file.                */
numeric digits 20                      /*allow for bigger numbers.            */
ifid='READINGS.TXT'                    /*name of the   input  file.           */
ofid='READINGS.OUT'                    /*  "   "  "   output    "             */
grandSum=0                             /*grand sum of the whole file.         */
grandFlg=0                             /*grand number of flagged data.        */
grandOKs=0
Lflag=0                                /*longest period of flagged data.      */
Cflag=0                                /*longest continuous flagged data.     */
oldDate =0                             /*placeholder of penultimate date.     */
w       =16                            /*width of fields when displayed.      */
dupDates=0                             /*count of duplicated timestamps.      */
badFlags=0                             /*count of bad flags  (not integer).   */
badDates=0                             /*count of bad dates  (bad format).    */
badData =0                             /*count of bad data   (not numeric).   */
ignoredR=0                             /*count of ignored records, bad records*/
maxInstruments=24                      /*maximum number of instruments.       */
yyyyCurr=right(date(),4)               /*get the current year (today).        */
monDD.  =31                            /*number of days in every month.       */
                                       /*# days in Feb. is figured on the fly.*/
monDD.4 =30
monDD.6 =30
monDD.9 =30
monDD.11=30

  do records=1  while lines(ifid)\==0  /*read until finished.                 */
  rec=linein(ifid)                     /*read the next record (line).         */
  parse var rec datestamp Idata        /*pick off the the dateStamp and data. */
  if datestamp==oldDate  then do       /*found a duplicate timestamp.         */
                              dupDates=dupDates+1   /*bump the dupDate counter*/
                              call sy datestamp copies('~',30),
                                       'is a duplicate of the',
                                       "previous datestamp."
                              ignoredR=ignoredR+1     /*bump # of ignoredRecs.*/
                              iterate  /*ignore this duplicate record.        */
                              end

  parse var datestamp yyyy '-' mm '-' dd   /*obtain YYYY, MM, and the DD.     */
  monDD.2=28+leapyear(yyyy)            /*how long is February in year  YYYY ? */
                                       /*check for various bad formats.       */
  if verify(yyyy||mm||dd,1234567890)\==0 |,
     length(datestamp)\==10   |,
     length(yyyy)\==4         |,
     length(mm  )\==2         |,
     length(dd  )\==2         |,
     yyyy<1970                |,
     yyyy>yyyyCurr            |,
     mm=0  | dd=0             |,
     mm>12 | dd>monDD.mm  then do
                               badDates=badDates+1
                               call sy datestamp copies('~'),
                                                 'has an illegal format.'
                               ignoredR=ignoredR+1  /*bump number ignoredRecs.*/
                               iterate              /*ignore this bad record. */
                               end
  oldDate=datestamp                    /*save datestamp for the next read.    */
  sum=0
  flg=0
  OKs=0

    do j=1  until Idata=''             /*process the instrument data.         */
    parse var Idata data.j flag.j Idata

    if pos('.',flag.j)\==0 |,          /*does flag have a decimal point  -or- */
       \datatype(flag.j,'W')  then do  /* ··· is the flag not a whole number? */
                                   badFlags=badFlags+1 /*bump badFlags counter*/
                                   call sy datestamp copies('~'),
                                           'instrument' j "has a bad flag:",
                                           flag.j
                                   iterate       /*ignore it and it's data.   */
                                   end

    if \datatype(data.j,'N')  then do  /*is the flag not a whole number?*/
                                   badData=badData+1      /*bump counter.*/
                                   call sy datestamp copies('~'),
                                           'instrument' j "has bad data:",
                                           data.j
                                   iterate       /*ignore it & it's flag.*/
                                   end

    if flag.j>0  then do               /*if good data, ~~~                    */
                      OKs=OKs+1
                      sum=sum+data.j
                      if Cflag>Lflag  then do
                                           Ldate=datestamp
                                           Lflag=Cflag
                                           end
                      Cflag=0
                      end
                 else do               /*flagged data ~~~                     */
                      flg=flg+1
                      Cflag=Cflag+1
                      end
    end   /*j*/

  if j>maxInstruments then do
                           badData=badData+1       /*bump the badData counter.*/
                           call sy datestamp copies('~'),
                                   'too many instrument datum'
                           end

  if OKs\==0  then avg=format(sum/OKs,,3)
              else avg='[n/a]'
  grandOKs=grandOKs+OKs
  _=right(commas(avg),w)
  grandSum=grandSum+sum
  grandFlg=grandFlg+flg
  if flg==0  then  call sy datestamp ' average='_
             else  call sy datestamp ' average='_ '  flagged='right(flg,2)
  end   /*records*/

records=records-1                      /*adjust for reading the  end─of─file. */
if grandOKs\==0  then grandAvg=format(grandsum/grandOKs,,3)
                 else grandAvg='[n/a]'
call sy
call sy copies('=',60)
call sy '      records read:'  right(commas(records ),w)
call sy '   records ignored:'  right(commas(ignoredR),w)
call sy '     grand     sum:'  right(commas(grandSum),w+4)
call sy '     grand average:'  right(commas(grandAvg),w+4)
call sy '     grand OK data:'  right(commas(grandOKs),w)
call sy '     grand flagged:'  right(commas(grandFlg),w)
call sy '   duplicate dates:'  right(commas(dupDates),w)
call sy '         bad dates:'  right(commas(badDates),w)
call sy '         bad  data:'  right(commas(badData ),w)
call sy '         bad flags:'  right(commas(badFlags),w)
if Lflag\==0 then call sy '   longest flagged:' right(commas(LFlag),w) " ending at " Ldate
call sy copies('=',60)
exit                                   /*stick a fork in it,  we're all  done.*/
/*────────────────────────────────────────────────────────────────────────────*/
commas: procedure;  parse arg _;   n=_'.9';    #=123456789;    b=verify(n,#,"M")
        e=verify(n,#'0',,verify(n,#"0.",'M'))-4
           do j=e  to b  by -3;   _=insert(',',_,j);    end  /*j*/;     return _
/*────────────────────────────────────────────────────────────────────────────*/
leapyear: procedure; arg y             /*year could be:  Y,  YY,  YYY, or YYYY*/
if length(y)==2 then y=left(right(date(),4),2)y      /*adjust for   YY   year.*/
if y//4\==0     then return 0          /* not divisible by 4?   Not a leapyear*/
return y//100\==0 | y//400==0          /*apply the 100  and the 400 year rule.*/
/*────────────────────────────────────────────────────────────────────────────*/
sy:     say arg(1);               call lineout ofid,arg(1);             return


package main

import "fmt"

const (
    msg = "a Top Secret secret"
    key = "this is my secret key"
)

func main() {
    var z state
    z.seed(key)
    fmt.Println("Message: ", msg)
    fmt.Println("Key    : ", key)
    fmt.Println("XOR    : ", z.vernam(msg))
}

type state struct {
    aa, bb, cc uint32
    mm         [256]uint32
    randrsl    [256]uint32
    randcnt    int
}

func (z *state) isaac() {
    z.cc++
    z.bb += z.cc
    for i, x := range z.mm {
        switch i % 4 {
        case 0:
            z.aa = z.aa ^ z.aa<<13
        case 1:
            z.aa = z.aa ^ z.aa>>6
        case 2:
            z.aa = z.aa ^ z.aa<<2
        case 3:
            z.aa = z.aa ^ z.aa>>16
        }
        z.aa += z.mm[(i+128)%256]
        y := z.mm[x>>2%256] + z.aa + z.bb
        z.mm[i] = y
        z.bb = z.mm[y>>10%256] + x
        z.randrsl[i] = z.bb
    }
}

func (z *state) randInit() {
    const gold = uint32(0x9e3779b9)
    a := [8]uint32{gold, gold, gold, gold, gold, gold, gold, gold}
    mix1 := func(i int, v uint32) {
        a[i] ^= v
        a[(i+3)%8] += a[i]
        a[(i+1)%8] += a[(i+2)%8]
    }
    mix := func() {
        mix1(0, a[1]<<11)
        mix1(1, a[2]>>2)
        mix1(2, a[3]<<8)
        mix1(3, a[4]>>16)
        mix1(4, a[5]<<10)
        mix1(5, a[6]>>4)
        mix1(6, a[7]<<8)
        mix1(7, a[0]>>9)
    }
    for i := 0; i < 4; i++ {
        mix()
    }
    for i := 0; i < 256; i += 8 {
        for j, rj := range z.randrsl[i : i+8] {
            a[j] += rj
        }
        mix()
        for j, aj := range a {
            z.mm[i+j] = aj
        }
    }
    for i := 0; i < 256; i += 8 {
        for j, mj := range z.mm[i : i+8] {
            a[j] += mj
        }
        mix()
        for j, aj := range a {
            z.mm[i+j] = aj
        }
    }
    z.isaac()
}

func (z *state) seed(seed string) {
    for i, r := range seed {
        if i == 256 {
            break
        }
        z.randrsl[i] = uint32(r)
    }
    z.randInit()
}

func (z *state) random() (r uint32) {
    r = z.randrsl[z.randcnt]
    z.randcnt++
    if z.randcnt == 256 {
        z.isaac()
        z.randcnt = 0
    }
    return
}

func (z *state) randA() byte {
    return byte(z.random()%95 + 32)
}

func (z *state) vernam(msg string) string {
    b := []byte(msg)
    for i := range b {
        b[i] ^= z.randA()
    }
    return fmt.Sprintf("%X", b)
}


let input_line ic =
  try Some(input_line ic)
  with End_of_file -> None

let fold_input f ini ic =
  let rec fold ac =
    match input_line ic with
    | Some line -> fold (f ac line)
    | None -> ac
  in
  fold ini

let ic = open_in "readings.txt"

let scan line =
  Scanf.sscanf line "%s\
    \t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\
    \t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\
    \t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\
    \t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d\t%f\t%d"
    (fun date
         v1  f1  v2  f2  v3  f3  v4  f4  v5  f5  v6  f6
         v7  f7  v8  f8  v9  f9  v10 f10 v11 f11 v12 f12
         v13 f13 v14 f14 v15 f15 v16 f16 v17 f17 v18 f18
         v19 f19 v20 f20 v21 f21 v22 f22 v23 f23 v24 f24 ->
      (date),
      [ (v1,  f1 ); (v2,  f2 ); (v3,  f3 ); (v4,  f4 ); (v5,  f5 ); (v6,  f6 );
        (v7,  f7 ); (v8,  f8 ); (v9,  f9 ); (v10, f10); (v11, f11); (v12, f12);
        (v13, f13); (v14, f14); (v15, f15); (v16, f16); (v17, f17); (v18, f18);
        (v19, f19); (v20, f20); (v21, f21); (v22, f22); (v23, f23); (v24, f24); ])

let tot_file, num_file, _, nodata_max, nodata_maxline =
  fold_input
    (fun (tot_file, num_file, nodata, nodata_max, nodata_maxline) line ->
       let date, datas = scan line in
       let _datas = List.filter (fun (_, flag) -> flag > 0) datas in
       let ok = List.length _datas in
       let tot = List.fold_left (fun ac (value, _) -> ac +. value) 0.0 _datas in
       let nodata, nodata_max, nodata_maxline =
         List.fold_left
             (fun (nodata, nodata_max, nodata_maxline) (_, flag) ->
                if flag <= 0
                then (succ nodata, nodata_max, nodata_maxline)
                else
                  if nodata_max = nodata && nodata > 0
                  then (0, nodata_max, date::nodata_maxline)
                  else if nodata_max < nodata && nodata > 0
                  then (0, nodata, [date])
                  else (0, nodata_max, nodata_maxline)
             )
             (nodata, nodata_max, nodata_maxline) datas in
       Printf.printf "Line: %s" date;
       Printf.printf "  Reject: %2d  Accept: %2d" (24 - ok) ok;
       Printf.printf "\tLine_tot: %8.3f" tot;
       Printf.printf "\tLine_avg: %8.3f\n" (tot /. float ok);
       (tot_file +. tot, num_file + ok, nodata, nodata_max, nodata_maxline))
    (0.0, 0, 0, 0, [])
    ic ;;

close_in ic ;;

Printf.printf "Total    = %f\n" tot_file;
Printf.printf "Readings = %d\n" num_file;
Printf.printf "Average  = %f\n" (tot_file /. float num_file);
Printf.printf "Maximum run(s) of %d consecutive false readings \
               ends at line starting with date(s): %s\n"
               nodata_max (String.concat ", " nodata_maxline);


require 'set'

def munge2(readings, debug=false)
   datePat = /^\d{4}-\d{2}-\d{2}/
   valuPat = /^[-+]?\d+\.\d+/
   statPat = /^-?\d+/
   totalLines = 0
   dupdate, badform, badlen, badreading = Set[], Set[], Set[], 0
   datestamps = Set[[]]
   for line in readings
      totalLines += 1
      fields = line.split(/\t/)
      date = fields.shift
      pairs = fields.enum_slice(2).to_a

      lineFormatOk = date =~ datePat &&
        pairs.all? { |x,y| x =~ valuPat && y =~ statPat }
      if !lineFormatOk
         puts 'Bad formatting ' + line if debug
         badform << date
      end

      if pairs.length != 24 ||
           pairs.any? { |x,y| y.to_i < 1 }
         puts 'Missing values ' + line if debug
      end
      if pairs.length != 24
         badlen << date
      end
      if pairs.any? { |x,y| y.to_i < 1 }
         badreading += 1
      end

      if datestamps.include?(date)
         puts 'Duplicate datestamp ' + line if debug
         dupdate << date
      end

      datestamps << date
   end

   puts 'Duplicate dates:', dupdate.sort.map { |x| '  ' + x }
   puts 'Bad format:', badform.sort.map { |x| '  ' + x }
   puts 'Bad number of fields:', badlen.sort.map { |x| '  ' + x }
   puts 'Records with good readings: %i = %5.2f%%' % [
      totalLines-badreading, (totalLines-badreading)/totalLines.to_f*100 ]
   puts
   puts 'Total records:  %d' % totalLines
end

open('readings.txt','r') do |readings|
   munge2(readings)
end


import Data.Array (Array, (!), (//), array, elems)
import Data.Word (Word, Word32)
import Data.Bits (shift, xor)
import Data.Char (toUpper)
import Data.List (unfoldr)
import Numeric (showHex)

type IArray = Array Word32 Word32

data IsaacState = IState
  { randrsl :: IArray
  , randcnt :: Word32
  , mm :: IArray
  , aa :: Word32
  , bb :: Word32
  , cc :: Word32
  }

instance Show IsaacState where
  show (IState _ cnt _ a b c) =
    show cnt ++ " " ++ show a ++ " " ++ show b ++ " " ++ show c

toHex :: Char -> String
toHex c = showHex (fromEnum c) ""

hexify :: String -> String
hexify = map toUpper . concatMap toHex

toNum :: Char -> Word32
toNum = fromIntegral . fromEnum

toChar :: Word32 -> Char
toChar = toEnum . fromIntegral

golden :: Word32
golden = 0x9e3779b9

-- Mix up an ordering of words.
mix :: [Word32] -> [Word32]
mix set = foldl aux set [11, -2, 8, -16, 10, -4, 8, -9]
  where
    aux [a, b, c, d, e, f, g, h] x = [b + c, c, d + a_, e, f, g, h, a_]
      where
        a_ = a `xor` (b `shift` x)

-- Generate the next 256 words.
isaac :: IsaacState -> IsaacState
isaac (IState rsl _ m a b c) = IState rsl_ 0 m_ a_ b_ c_
  where
    c_ = c + 1
    (rsl_, m_, a_, b_) =
      foldl aux (rsl, m, a, b) $ zip [0 .. 255] $ cycle [13, -6, 2, -16]
    aux (rsl, m, a, b) (i, s) = (rsl_, m_, a_, b_)
      where
        x = m ! i
        a_ = (a `xor` (a `shift` s)) + m ! ((i + 128) `mod` 256)
        y = a_ + b + m ! ((x `shift` (-2)) `mod` 256)
        m_ = m // [(i, y)]
        b_ = x + m_ ! ((y `shift` (-10)) `mod` 256)
        rsl_ = rsl // [(i, b_)]

-- Given a seed value in randrsl, initialize/mixup the state.
randinit :: IsaacState -> Bool -> IsaacState
randinit state flag = isaac (IState randrsl_ 0 m 0 0 0)
  where
    firstSet = iterate mix (replicate 8 golden) !! 4
    iter _ _ [] = []
    iter flag set rsl =
      let (rslH, rslT) = splitAt 8 rsl
          set_ =
            mix $
            if flag
              then zipWith (+) set rslH
              else set
      in set_ ++ iter flag set_ rslT
    randrsl_ = randrsl state
    firstPass = iter flag firstSet $ elems randrsl_
    set_ = drop (256 - 8) firstPass
    secondPass =
      if flag
        then iter True set_ firstPass
        else firstPass
    m = array (0, 255) $ zip [0 ..] secondPass

-- Given a string seed, optionaly use it to generate a new state.
seed :: String -> Bool -> IsaacState
seed key flag =
  let m = array (0, 255) $ zip [0 .. 255] $ repeat 0
      rsl = m // zip [0 ..] (map toNum key)
      state = IState rsl 0 m 0 0 0
  in randinit state flag

-- Produce a random word and the next state from the given state.
random :: IsaacState -> (Word32, IsaacState)
random state@(IState rsl cnt m a b c) =
  let r = rsl ! cnt
      state_ =
        if cnt + 1 > 255
          then isaac $ IState rsl 0 m a b c
          else IState rsl (cnt + 1) m a b c
  in (r, state_)

-- Produce a stream of random words from the given state.
randoms :: IsaacState -> [Word32]
randoms = unfoldr $ Just . random

-- Produce a random printable/typable character in the ascii range
-- and the next state from the given state.
randA :: IsaacState -> (Char, IsaacState)
randA state =
  let (r, state_) = random state
  in (toEnum $ fromIntegral $ (r `mod` 95) + 32, state_)

-- Produce a stream of printable characters from the given state.
randAs :: IsaacState -> String
randAs = unfoldr $ Just . randA

-- Vernam encode/decode a string with the given state.
vernam :: IsaacState -> String -> String
vernam state msg = map toChar $ zipWith xor msg_ randAs_
  where
    msg_ = map toNum msg
    randAs_ = map toNum $ randAs state

main :: IO ()
main = do
  let msg = "a Top Secret secret"
      key = "this is my secret key"
      st = seed key True
      ver = vernam st msg
      unver = vernam st ver
  putStrLn $ "Message: " ++ msg
  putStrLn $ "Key    : " ++ key
  putStrLn $ "XOR    : " ++ hexify ver
  putStrLn $ "XOR dcr: " ++ unver


use strict;
use warnings;

my $nodata = 0;               # Current run of consecutive flags<0 in lines of file
my $nodata_max = -1;          # Max consecutive flags<0 in lines of file
my $nodata_maxline = "!";     # ... and line number(s) where it occurs

my $infiles = join ", ", @ARGV;

my $tot_file = 0;
my $num_file = 0;

while (<>) {
  chomp;
  my $tot_line = 0;             # sum of line data
  my $num_line = 0;             # number of line data items with flag>0
  my $rejects  = 0;

  # extract field info, skipping initial date field
  my ($date, @fields) = split;
  while (@fields and my ($datum, $flag) = splice @fields, 0, 2) {
    if ($flag+1 < 2) {
      $nodata++;
      $rejects++;
      next;
    }

    # check run of data-absent fields
    if($nodata_max == $nodata and $nodata > 0){
      $nodata_maxline = "$nodata_maxline, $date";
    }
    if($nodata_max < $nodata and $nodata > 0){
      $nodata_max = $nodata;
      $nodata_maxline = $date;
    }
    # 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",
         $date, $rejects, $num_line, $tot_line, ($num_line>0)? $tot_line/$num_line: 0;

}

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;


object DataMunging2 {
  import scala.io.Source
  import scala.collection.immutable.{TreeMap => Map}

  val pattern = """^(\d+-\d+-\d+)""" + """\s+(\d+\.\d+)\s+(-?\d+)""" * 24 + "$" r;

  def main(args: Array[String]) {
    val files = args map (new java.io.File(_)) filter (file => file.isFile && file.canRead)
    val (numFormatErrors, numValidRecords, dateMap) =
      files.iterator.flatMap(file => Source fromFile file getLines ()).
        foldLeft((0, 0, new Map[String, Int] withDefaultValue 0)) {
        case ((nFE, nVR, dM), line) => pattern findFirstMatchIn line map (_.subgroups) match {
          case Some(List(date, rawData @ _*)) =>
            val allValid = (rawData map (_ toDouble) iterator) grouped 2 forall (_.last > 0)
            (nFE, nVR + (if (allValid) 1 else 0), dM(date) += 1)
          case None => (nFE + 1, nVR, dM)
        }
      }

    dateMap foreach {
      case (date, repetitions) if repetitions > 1 => println(date+": "+repetitions+" repetitions")
      case _ =>
    }

    println("""|
               |Valid records: %d
               |Duplicated dates: %d
               |Duplicated records: %d
               |Data format errors: %d
               |Invalid data records: %d
               |Total records: %d""".stripMargin format (
              numValidRecords,
              dateMap filter { case (_, repetitions) => repetitions > 1 } size,
              dateMap.valuesIterable filter (_ > 1) map (_ - 1) sum,
              numFormatErrors,
              dateMap.valuesIterable.sum - numValidRecords,
              dateMap.valuesIterable.sum))
  }
}


package src ;
import haxe.Int32;
import haxe.macro.Expr;
import haxe.ds.Vector;

typedef Ub4 = Int32;

enum Ciphermode {
	mEncipher;
	mDecipher;
	mNone;
}

class Isaac
{
	public var randrsl = new Vector<Ub4>(256);
	public var randcnt:Ub4;

	var mm = new Vector<Ub4>(256);
	var aa:Ub4 = 0;
	var bb:Ub4 = 0;
	var cc:Ub4 = 0;

	public function isaac():Void {
		var x, y;
		cc++;
		bb += cc;
		for (i in 0...256) {
			x = mm[i];
			aa ^= switch (i % 4) {//Haxe unification
				case 0: aa << 13;
				case 1: aa >>> 6;
				case 2: aa << 2;
				case 3: aa >>> 16;
				default: 0;//never happens
			}
			aa              = mm[(i + 128) % 256] + aa;
			mm[i]      = y  = mm[(x >>> 2) % 256] + aa + bb;
			randrsl[i] = bb = mm[(y >>> 10) % 256] + x;
		}
	}

	macro static function mix(a:ExprOf<Ub4>, b:ExprOf<Ub4>, c:ExprOf<Ub4>, d:ExprOf<Ub4>,
	                          e:ExprOf<Ub4>, f:ExprOf<Ub4>, g:ExprOf<Ub4>, h:ExprOf<Ub4>) {
		return macro {
			$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;
		};
	}

	public function randinit(flag:Bool):Void {
		var a, b, c, d, e, f, g, h, i;
		aa = bb = cc = (0:Ub4);
		a = b = c = d = e = f = g = h = (0x9e3779b9:Ub4); /* the golden ratio */
		for (i in 0...4) mix(a, b, c, d, e, f, g, h); /* scramble it */
		i = 0;
		while (i < 256) { /* 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;
			i += 8;
		}
		if (flag) { /* do a second pass to make all of the seed affect all of mm */
			i = 0;
			while (i<256) {
				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;
				i += 8;
			}
		}
		isaac();
		randcnt = 0;
	}

	public function iRandom():Ub4 {
		var r = randrsl[randcnt];
		++randcnt;
		if (randcnt > 255) {
			isaac();
			randcnt = 0;
		}
		return r;
	}

	public function iRandA():Int32 {
		return cast(cast(iRandom(),UInt) % 95 + 32,Int32);
	}

	public function iSeed(seed:String, flag:Bool):Void {
		var m=seed.length-1;
		for (i in 0...256) mm[i] = 0;
		for (i in 0...256) if (i > m) randrsl[i] = 0; else randrsl[i] = seed.charCodeAt(i);
		randinit(flag);
	}

	inline static var modC = 95;
	inline static var startC = 32;

	public function vernam (msg:String):String {
		var v="";
		for (i in 0...msg.length) v += String.fromCharCode(iRandA() ^ msg.charCodeAt(i));
		return v;
	}

	public function caesar(m:Ciphermode, ch:Int32, shift:Int32,
	                       modulo:Int32, start:Int32):String {
		var n:Int32;
		if (m == mDecipher) n = ch - start - cast(shift,Int32);
		else n = ch - start + cast(shift,Int32);
		n %= modulo;
		if (n < 0) n += modulo;
		return String.fromCharCode(start + cast(n,Ub4));
	}

	public function caesarStr(m:Ciphermode, msg:String, modulo:Int32, start:Int32):String {
		var c = "";
		for (i in 0...msg.length)
			c += caesar(m,msg.charCodeAt(i),iRandA(),modulo,start);
		return c;
	}

	static public function main():Void {
		var msg = "a Top Secret secret";
		var key = "this is my secret key";
		var cIsaac = new Isaac();
		var vctx, vptx, cctx, cptx;
		cIsaac.iSeed(key, true);
		vctx = cIsaac.vernam(msg);
		cctx = cIsaac.caesarStr(mEncipher, msg, modC, startC);

		cIsaac.iSeed(key, true);
		vptx = cIsaac.vernam(vctx);
		cptx = cIsaac.caesarStr(mDecipher, cctx, modC, startC);

		Sys.println("Message: " + msg);
		Sys.println("Key    : " + key);
		var hex = "";
		for (i in 0...vctx.length) hex += StringTools.hex(vctx.charCodeAt(i), 2);
		Sys.println("XOR    : " + hex);
		Sys.println("XOR dcr: " + vptx);
		hex = "";
		for (i in 0...cctx.length) hex += StringTools.hex(cctx.charCodeAt(i), 2);
		Sys.println("MOD    : " + hex);
		Sys.println("MOD dcr: " + cptx);
	}
}


import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Random;

public class IsaacRandom extends Random {

	private static final long serialVersionUID = 1L;

	private final int[] randResult = new int[256];    // output of last generation
	private int valuesUsed;                           // the number of values already used up from randResult

	// internal generator state
	private final int[] mm = new int[256];
	private int aa, bb, cc;

	public IsaacRandom() {
		super(0);
		init(null);
	}

	public IsaacRandom(int[] seed) {
		super(0);
		setSeed(seed);
	}

	public IsaacRandom(String seed) {
		super(0);
		setSeed(seed);
	}

	private void generateMoreResults() {
		cc++;
		bb += cc;

		for (int i=0; i<256; i++) {
			int 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] + aa;
			int y = mm[i] = mm[(x>>>2) & 0xFF] + aa + bb;
			randResult[i] = bb = mm[(y>>>10) & 0xFF] + x;
		}

		valuesUsed = 0;
	}

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

	private void init(int[] seed) {
		if (seed != null && seed.length != 256) {
			seed = Arrays.copyOf(seed, 256);
		}
		aa = bb = cc = 0;
		int[] initState = new int[8];
		Arrays.fill(initState, 0x9e3779b9);	// the golden ratio

		for (int i=0; i<4; i++) {
			mix(initState);
		}

		for (int i=0; i<256; i+=8) {
			if (seed != null) {
				for (int j=0; j<8; j++) {
					initState[j] += seed[i+j];
				}
			}
			mix(initState);
			for (int j=0; j<8; j++) {
				mm[i+j] = initState[j];
			}
		}

		if (seed != null) {
			for (int i=0; i<256; i+=8) {
				for (int j=0; j<8; j++) {
					initState[j] += mm[i+j];
				}

				mix(initState);

				for (int j=0; j<8; j++) {
					mm[i+j] = initState[j];
				}
			}
		}

		valuesUsed = 256;	// Make sure generateMoreResults() will be called by the next next() call.
	}

	@Override
	protected int next(int bits) {
		if (valuesUsed == 256) {
			generateMoreResults();
			assert(valuesUsed == 0);
		}
		int value = randResult[valuesUsed];
		valuesUsed++;
		return value >>> (32-bits);
	}

	@Override
	public synchronized void setSeed(long seed) {
		super.setSeed(0);
		if (mm == null) {
			// We're being called from the superclass constructor. We don't have our
			// state arrays instantiated yet, and we're going to do proper initialization
			// later in our own constructor anyway, so just ignore this call.
			return;
		}
		int[] arraySeed = new int[256];
		arraySeed[0] = (int) (seed & 0xFFFFFFFF);
		arraySeed[1] = (int) (seed >>> 32);
		init(arraySeed);
	}

	public synchronized void setSeed(int[] seed) {
		super.setSeed(0);
		init(seed);
	}

	public synchronized void setSeed(String seed) {
		super.setSeed(0);
		char[] charSeed = seed.toCharArray();
		int[] intSeed = new int[charSeed.length];
		for (int i=0; i<charSeed.length; i++) {
			intSeed[i] = charSeed[i];
		}
		init(intSeed);
	}

	public int randomChar() {
		long unsignedNext = nextInt() & 0xFFFFFFFFL;	// The only way to force unsigned modulo behavior in Java is to convert to a long and mask off the copies of the sign bit.
		return (int) (unsignedNext % 95 + 32);		    // nextInt(95) + 32 would yield a more equal distribution, but then we would be incompatible with the original C code
	}

	public enum CipherMode { ENCIPHER, DECIPHER, NONE };

	public byte[] vernamCipher(byte[] input) {
		byte[] result = new byte[input.length];
		for (int i=0; i<input.length; i++) {
			result[i] = (byte) (randomChar() ^ input[i]);
		}
		return result;
	}

	private static byte caesarShift(CipherMode mode, byte ch, int shift, byte modulo, byte start) {
		if (mode == CipherMode.DECIPHER) {
			shift = -shift;
		}
		int n = (ch-start) + shift;
		n %= modulo;
		if (n<0) {
			n += modulo;
		}
		return (byte) (start + n);
	}

	public byte[] caesarCipher(CipherMode mode, byte[] input, byte modulo, byte start) {
		byte[] result = new byte[input.length];
		for (int i=0; i<input.length; i++) {
			result[i] = caesarShift(mode, input[i], randomChar(), modulo, start);
		}
		return result;
	}

	private static String toHexString(byte[] input) {
		// NOTE: This method prefers simplicity over performance.
		StringBuilder sb = new StringBuilder(input.length*2);
		for (byte b : input) {
			sb.append(String.format("%02X", b));
		}
		return sb.toString();
	}

	public static void main(String[] args) {
		final byte MOD = 95;
		final byte START = 32;

		String secret = "a Top Secret secret";
		String key = "this is my secret key";

		IsaacRandom random = new IsaacRandom(key);
		byte[] vernamResult;
		byte[] caesarResult;
		String vernamDecrypted;
		String caesarDecrypted;
		try {
			vernamResult = random.vernamCipher(secret.getBytes("ASCII"));
			caesarResult = random.caesarCipher(CipherMode.ENCIPHER, secret.getBytes("ASCII"), MOD, START);
			random.setSeed(key);
			vernamDecrypted = new String(random.vernamCipher(vernamResult), "ASCII");
			caesarDecrypted = new String(random.caesarCipher(CipherMode.DECIPHER, caesarResult, MOD, START), "ASCII");
		} catch (UnsupportedEncodingException e) {
			throw new InternalError("JVM isn't conforming - ASCII encoding isn't available");
		}
		System.out.printf("Message: %s\n", secret);
		System.out.printf("Key    : %s\n", key);
		System.out.printf("XOR    : %s\n", toHexString(vernamResult));
		System.out.printf("XOR dcr: %s\n", vernamDecrypted);
		System.out.printf("MOD    : %s\n", toHexString(caesarResult));
		System.out.printf("MOD dcr: %s\n", caesarDecrypted);
	}
}


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

include builtins\timedate.e

integer count = 0,
        max_count = 0,
        ntot = 0
atom readtot = 0
timedate run_start, max_start

procedure end_bad_run()
    if count then
        if count>max_count then
            max_count = count
            max_start = run_start
        end if
        count = 0
    end if
end procedure

for i=1 to length(lines) do
    sequence oneline = split(lines[i],'\t'), r
    if length(oneline)!=49 then
        ?"bad line (length!=49)"
    else
        r = parse_date_string(oneline[1],{"YYYY-MM-DD"})
        if not timedate(r) then
            ?{"bad date",oneline[1]}
        else
            timedate td = r
            integer rejects=0, accepts=0
            atom readsum = 0
            for j=2 to 48 by 2 do
                r = scanf(oneline[j],"%f")
                if length(r)!=1 then
                    ?{"error scanning",oneline[j]}
                    rejects += 1
                else
                    atom reading = r[1][1]
                    r = scanf(oneline[j+1],"%d")
                    if length(r)!=1 then
                        ?{"error scanning",oneline[j+1]}
                        rejects += 1
                    else
                        integer flag = r[1][1]
                        if flag<=0 then
                            if count=0 then
                                run_start = td
                            end if
                            count += 1
                            rejects += 1
                        else
                            end_bad_run()
                            accepts += 1
                            readsum += reading
                        end if
                    end if
                end if
            end for
            readtot += readsum
            ntot += accepts
            if i>=length(lines)-2 then
                string average = iff(accepts=0?"N/A":sprintf("%6.3f",readsum/accepts))
                printf(1,"Date: %s, Rejects: %2d, Accepts: %2d, Line total: %7.3f, Average %s\n",
                       {format_timedate(td,"DD/MM/YYYY"),rejects, accepts, readsum, average})
            end if
        end if
    end if
end for

printf(1,"Average: %.3f (of %d readings)\n",{readtot/ntot,ntot})
end_bad_run()
if max_count then
    printf(1,"Maximum run of %d consecutive false readings starting: %s\n",
            {max_count,format_timedate(max_start,"DD/MM/YYYY")})
end if

?"done"
{} = wait_key()


var good_records = 0;
var dates = Hash();

ARGF.each { |line|
    var m = /^(\d\d\d\d-\d\d-\d\d)((?:\h+\d+\.\d+\h+-?\d+){24})\s*$/.match(line);
    m || (warn "Bad format at line #{$.}"; next);
    dates{m[0]} := 0 ++;
    var i = 0;
    m[1].words.all{|n| i++.is_even || (n.to_num >= 1) } && ++good_records;
}

say "#{good_records} good records out of #{$.} total";
say 'Repeated timestamps:';
say dates.to_a.grep{ .value > 1 }.map { .key }.sort.join("\n");


* Read text/2

	v = array(24)
	f = array(24)
	tos = char(9) " " ;* break characters are both tab and space
	pat1 = break(tos) . dstamp
	pat2 = span(tos) break(tos) . *v[i] span(tos) (break(tos) | (len(1) rem)) . *f[i]
	rowcount = 0
	hold_dstamp = ""
	num_bad_rows = 0
	num_invalid_rows = 0

in0
	row = input :f(endinput)
	rowcount = rowcount + 1
	row ? pat1 = :f(invalid_row)

* duplicated datestamp?
* if dstamp = hold_dstamp then duplicated
	hold_dstamp = differ(hold_dstamp,dstamp) dstamp :s(nodup)
	output = dstamp ": datestamp at row " rowcount " duplicates datestamp at " rowcount - 1
nodup

	i = 1
in1
	row ? pat2 = :f(invalid_row)
	i = lt(i,24) i + 1 :s(in1)

* Is this a goodrow?
* if any flag is < 1 then row has bad data
	c = 0
goodrow
	c = lt(c,24) c + 1 :f(goodrow2)
	num_bad_rows = lt(f[c],1) num_bad_rows + 1 :s(goodrow2)f(goodrow)
goodrow2

	:(in0)

invalid_row
	num_invalid_rows = num_invalid_rows + 1
	:(in0)

endinput
	output =
	output = "Total number of rows                    : " rowcount
	output = "Total number of rows with invalid format: " num_invalid_rows
	output = "Total number of rows with bad data      : " num_bad_rows
	output = "Total number of good rows               : " rowcount - num_invalid_rows - num_bad_rows

end


set data [lrange [split [read [open "readings.txt" "r"]] "\n"] 0 end-1]
set total [llength $data]
set correct $total
set datestamps {}

foreach line $data {
    set formatOk true
    set hasAllMeasurements true

    set date [lindex $line 0]
    if {[llength $line] != 49} { set formatOk false }
    if {![regexp {\d{4}-\d{2}-\d{2}} $date]} { set formatOk false }
    if {[lsearch $datestamps $date] != -1} { puts "Duplicate datestamp: $date" } {lappend datestamps $date}

    foreach {value flag} [lrange $line 1 end] {
        if {$flag < 1} { set hasAllMeasurements false }

        if {![regexp -- {[-+]?\d+\.\d+} $value] || ![regexp -- {-?\d+} $flag]} {set formatOk false}
    }
    if {!$hasAllMeasurements} { incr correct -1 }
    if {!$formatOk} { puts "line \"$line\" has wrong format" }
}

puts "$correct records with good readings = [expr $correct * 100.0 / $total]%"
puts "Total records: $total"


"""
Julia translation of code from the following:
------------------------------------------------------------------------------
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.
------------------------------------------------------------------------------
"""
# maximum length of message here is set to 4096
const MAXMSG = 4096

# cipher modes for encryption versus decryption modes
@enum CipherMode mEncipher mDecipher mNone

# external results
mutable struct IState
    randrsl::Array{UInt32, 1}
    randcnt::UInt32
    mm::Array{UInt32, 1}
    aa::UInt32
    bb::UInt32
    cc::UInt32
    function IState()
        this = new()
        this.randrsl = zeros(UInt32, 256)
        this.randcnt = UInt32(0)
        this.mm = zeros(UInt32, 256)
        this.aa = this.bb = this.cc = UInt32(0)
        this
    end
end

"""
    isaac
Randomize the pool
"""
function isaac(istate)
    istate.cc +=  1             # cc gets incremented once per 256 results
    istate.bb += istate.cc      # then combined with bb

    for (j, c) in enumerate(istate.mm)   # Julia NB: indexing ahead so use i for c indexing
        i = j - 1
        xmod4 = i % 4
        if(xmod4 == 0)
            istate.aa ⊻= istate.aa << 13
        elseif(xmod4 == 1)
            istate.aa ⊻= istate.aa >> 6
        elseif(xmod4 == 2)
            istate.aa ⊻= istate.aa << 2
        else
            istate.aa ⊻= istate.aa >> 16
        end
        istate.aa += istate.mm[(i + 128) % 256 + 1]
        y = istate.mm[(c >> 2) % 256 + 1] + istate.aa + istate.bb
        istate.mm[j] = y
        istate.bb = istate.mm[(y >> 10) % 256 + 1] + c
        istate.randrsl[j] = istate.bb
    end
    # not in original readable.c
    istate.randcnt = 0
end

"""
    mix
Mix the bytes in a reversible way.
"""
function mix(arr)              # Julia NB: use E for e in c code here
   (a,b,c,d,E,f,g,h) = arr
   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;
   (a,b,c,d,E,f,g,h)
end

"""
    randinit
Make a random UInt32 array.
If flag is true, use the contents of randrsl[] to initialize mm[].
"""
function randinit(istate, flag::Bool)
    istate.aa = istate.bb = istate.cc = 0
    mixer = Array{UInt32,1}(8)
    fill!(mixer, 0x9e3779b9)             # the golden ratio
    for i in 1:4                         # scramble it
        mixer = mix(mixer)
    end
    for i in 0:8:255                     # fill in mm[] with messy stuff
        if(flag)                         # use all the information in the seed
            mixer = [mixer[j] + istate.randrsl[i+j] for j in 1:8]
        end
        mixer = mix(mixer)
        istate.mm[i+1:i+8] .= mixer
    end
    if(flag)                             # do a second pass to seed all of mm
        for i in 0:8:255
            mixer = [mixer[j] + istate.mm[i+j] for j in 1:8]
            mixer = mix(mixer)
            istate.mm[i+1:i+8] .= mixer
        end
    end
    isaac(istate)                        # fill in the first set of results
    istate.randcnt = 0
end

"""
    Get a random 32-bit value 0..MAXINT
"""
function irandom(istate)
    retval::UInt32 = istate.randrsl[istate.randcnt+1]
    istate.randcnt += 1
    if(istate.randcnt > 255)
        isaac(istate)
        istate.randcnt = 0
    end
    retval
end

"""
    Get a random character in printable ASCII range
"""
iranda(istate) = UInt8(irandom(istate) % 95 + 32)

"""
    Do XOR cipher on random stream.
    Output: UInt8 array
"""
vernam(istate, msg) = [UInt8(iranda(istate) ⊻ c) for c in msg]

"""
    Seed ISAAC with a string
"""
function iseed(istate, seed, flag)
    fill!(istate.mm, 0)
    fill!(istate.randrsl, 0)
    len = min(length(seed), length(istate.randrsl))
    istate.randrsl[1:len] .= seed[1:len]
    randinit(istate, flag)   # initialize ISAAC with seed
end

tohexstring(arr::Array{UInt8,1}) = join([hex(i, 2) for i in arr])

function test(istate, msg, key)
    # Vernam ciphertext & plaintext
    vctx = zeros(UInt8, MAXMSG)
    vptx = zeros(UInt8, MAXMSG)
    # Encrypt: Vernam XOR
    iseed(istate, Vector{UInt8}(key), true)
    vctx = vernam(istate, Vector{UInt8}(msg))
    # Decrypt: Vernam XOR
    iseed(istate, Vector{UInt8}(key), true)
    vptx = vernam(istate, vctx)
    # Program output
    println("Message: $msg")
    println("Key    : $key")
    println("XOR    : $(tohexstring(vctx))")
    # Output Vernam decrypted plaintext
    println("XOR dcr: $(join(map(c -> Char(c), vptx)))")
    0
end

"""
Test the above.
"""
const msg = "a Top Secret secret"
const key = "this is my secret key"
test(IState(), msg, key)


// version 1.1.3

/* external results */
val randrsl = IntArray(256)
var randcnt = 0

/* internal state */
val mm = IntArray(256)
var aa = 0
var bb = 0
var cc = 0

const val GOLDEN_RATIO = 0x9e3779b9.toInt()

fun isaac() {
    cc++       // cc just gets incremented once per 256 results
    bb += cc   // then combined with bb
    for (i in 0..255) {
        val x = mm[i]
        when (i % 4) {
            0 -> aa = aa xor (aa shl 13)
            1 -> aa = aa xor (aa ushr 6)
            2 -> aa = aa xor (aa shl 2)
            3 -> aa = aa xor (aa ushr 16)
        }
        aa += mm[(i + 128) % 256]
        val y = mm[(x ushr 2) % 256] + aa + bb
        mm[i] = y
        bb = mm[(y ushr 10) % 256] + x
        randrsl[i] = bb
    }
    randcnt = 0
}

/* if (flag == true), then use the contents of randrsl to initialize mm. */
fun mix(n: IntArray) {
    n[0] = n[0] xor (n[1]  shl 11); n[3] += n[0]; n[1] += n[2]
    n[1] = n[1] xor (n[2] ushr  2); n[4] += n[1]; n[2] += n[3]
    n[2] = n[2] xor (n[3]  shl  8); n[5] += n[2]; n[3] += n[4]
    n[3] = n[3] xor (n[4] ushr 16); n[6] += n[3]; n[4] += n[5]
    n[4] = n[4] xor (n[5]  shl 10); n[7] += n[4]; n[5] += n[6]
    n[5] = n[5] xor (n[6] ushr  4); n[0] += n[5]; n[6] += n[7]
    n[6] = n[6] xor (n[7]  shl  8); n[1] += n[6]; n[7] += n[0]
    n[7] = n[7] xor (n[0] ushr  9); n[2] += n[7]; n[0] += n[1]
}

fun randinit(flag: Boolean) {
    aa = 0
    bb = 0
    cc = 0
    val n = IntArray(8) { GOLDEN_RATIO }
    for (i in 0..3) mix(n)      // scramble the array

    for (i in 0..255 step 8) {  // fill in mm with messy stuff
        if (flag) {             // use all the information in the seed
           for (j in 0..7) n[j] += randrsl[i + j]
        }
        mix(n)
        for (j in 0..7) mm[i + j] = n[j]
    }

    if (flag) {
        /* do a second pass to make all of the seed affect all of mm */
        for (i in 0..255 step 8) {
            for (j in 0..7) n[j] += mm[i + j]
            mix(n)
            for (j in 0..7) mm[i + j] = n[j]
        }
    }

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

/* As Kotlin doesn't (yet) support unsigned types, we need to use
   Long here to get a random value in the range of a UInt */
fun iRandom(): Long {
    val r = randrsl[randcnt++]
    if (randcnt > 255) {
        isaac()
        randcnt = 0
    }
    return r.toLong() and 0xFFFFFFFFL
}

/* Get a random character (as Int) in printable ASCII range */
fun iRandA() = (iRandom() % 95 + 32).toInt()

/* Seed ISAAC with a string */
fun iSeed(seed: String, flag: Boolean) {
    for (i in 0..255) mm[i] = 0
    val m = seed.length
    for (i in 0..255) {
        /* in case seed has less than 256 elements */
        randrsl[i] = if (i >= m) 0 else seed[i].toInt()
    }
    /* initialize ISAAC with seed */
    randinit(flag)
}

/* XOR cipher on random stream. Output: ASCII string */
fun vernam(msg: String) : String {
    val len = msg.length
    val v = ByteArray(len)
    for (i in 0 until len) {
        v[i] = (iRandA() xor msg[i].toInt()).toByte()
    }
    return v.toString(charset("ASCII"))
}

/* constants for Caesar */
const val MOD = 95
const val START = 32

/* cipher modes for Caesar */
enum class CipherMode {
    ENCIPHER, DECIPHER, NONE
}

/* Caesar-shift a printable character */
fun caesar(m: CipherMode, ch: Int, shift: Int, modulo: Int, start: Int): Char {
    val sh = if (m == CipherMode.DECIPHER) -shift else shift
    var n = (ch - start) + sh
    n %= modulo
    if (n < 0) n += modulo
    return (start + n).toChar()
}

/* Caesar-shift a string on a pseudo-random stream */
fun caesarStr(m: CipherMode, msg: String, modulo: Int, start: Int): String {
    val sb = StringBuilder(msg.length)
    /* Caesar-shift message */
    for (c in msg) {
        sb.append(caesar(m, c.toInt(), iRandA(), modulo, start))
    }
    return sb.toString()
}

fun String.toHexByteString() =
    this.map { "%02X".format(it.toInt()) }.joinToString("")

fun main(args: Array<String>) {
    val msg = "a Top Secret secret"
    val key = "this is my secret key"

    // Vernam & Caesar ciphertext
    iSeed(key, true)
    val vctx = vernam(msg)
    val cctx = caesarStr(CipherMode.ENCIPHER, msg,  MOD, START)

    // Vernam & Caesar plaintext
    iSeed(key, true)
    val vptx = vernam(vctx)
    val cptx = caesarStr(CipherMode.DECIPHER, cctx, MOD, START)

    // Program output
    println("Message : $msg")
    println("Key     : $key")
    println("XOR     : ${vctx.toHexByteString()}")
    println("XOR dcr : $vptx")
    println("MOD     : ${cctx.toHexByteString()}")
    println("MOD dcr : $cptx")
}


go  =>
  File = "readings.txt",
  Total = new_map([num_readings=0,num_good_readings=0,sum_readings=0.0]),
  InvalidCount = 0,
  MaxInvalidCount = 0,
  InvalidRunEnd = "",

  Id = 0,
  foreach(Line in read_file_lines(File))
    Id := Id + 1,
    NumReadings = 0,
    NumGoodReadings = 0,
    SumReadings = 0,

    Fields = Line.split,
    Rec = Fields.tail.map(to_float),
    foreach([Reading,Flag] in chunks_of(Rec,2))
      NumReadings := NumReadings + 1,
      if Flag > 0 then
        NumGoodReadings := NumGoodReadings + 1,
        SumReadings := SumReadings + Reading,
        InvalidCount := 0
      else
        InvalidCount := InvalidCount + 1,
        if InvalidCount > MaxInvalidCount then
          MaxInvalidCount := InvalidCount,
          InvalidRunEnd := Fields[1]
        end
      end
    end,

    Total.put(num_readings,Total.get(num_readings) + NumReadings),
    Total.put(num_good_readings,Total.get(num_good_readings) + NumGoodReadings),
    Total.put(sum_readings,Total.get(sum_readings) + SumReadings),
    if Id <= 3 then
      printf("date:%w accept:%w reject:%w sum:%w\n", Fields[1],NumGoodReadings,
                                                     NumReadings-NumGoodReadings,
                                                     SumReadings)
    end
  end,
  nl,
  printf("readings: %d good readings: %d sum: %0.3f avg: %0.3f\n",Total.get(num_readings),
                                                       Total.get(num_good_readings),
                                                       Total.get(sum_readings),
                                                       Total.get(sum_readings) / Total.get(num_good_readings)),
  nl,
  println(maxInvalidCount=MaxInvalidCount),
  println(invalidRunEnd=InvalidRunEnd),

  nl.