#!/bin/sh
#| -*- scheme -*-
exec racket "$0" "$@"
|#

#lang racket/base

(require racket/cmdline racket/port)

(define read-lines
  (case-lambda
    [() (for/list ([line (in-lines)]) line)]
    [(n) (for/list ([line (in-lines)] [i (in-range n)]) line)]))

(define-struct ring (vec [len #:mutable]))
(define (list->ring l)
  (let ([v (list->vector l)]) (make-ring v (vector-length v))))
(define (ring-empty? r)
  (zero? (ring-len r)))
(define (ring-randpop! r)
  (let ([len (ring-len r)])
    (if (zero? len)
      (error 'ring-randpop! "random ring is empty")
      (let* ([i (random len)]
             [v (ring-vec r)]
             [x (vector-ref v i)])
        (vector-set! v i (vector-ref v (sub1 len)))
        (set-ring-len! r (sub1 len))
        x))))
(define (ring-randset! r x)
  (vector-set! (ring-vec r) (random (ring-len r)) x))
(define (ring-randdisplay r)
  (let loop ()
    (unless (ring-empty? r) (display (ring-randpop! r)) (newline) (loop))))

(define (output-n-random-lines n)
  (define r (list->ring (read-lines n)))
  (let loop ([m n])
    (let ([line (read-line)])
      (if (eof-object? line)
        (ring-randdisplay r)
        (let ([m (add1 m)])
          (when (< (random m) n) (ring-randset! r line))
          (loop m))))))

(define (output-randomized-lines)
  (ring-randdisplay (list->ring (read-lines))))

(define (run)
  (if num (output-n-random-lines num) (output-randomized-lines)))

(define num #f)

(command-line
 #:once-any
 [("-n") n "show n random line"
  (set! num (or (string->number n) (error 'random-lines "bad number: ~s" n)))]
 #:args files
 (cond [(null? files) (run)]
       [(null? (cdr files)) (with-input-from-file (car files) run)]
       [else (let-values ([(in out) (make-pipe 4096)])
               (thread (lambda ()
                         (for ([file files])
                           (with-input-from-file file
                             (lambda () (copy-port (current-input-port) out))))
                         (close-output-port out)))
               (parameterize ([current-input-port in]) (run)))]))
