; exercise 7

(define (read-line . port)
  (define (eat p c)
    (if (and (not (eof-object? (peek-char p)))
             (char=? (peek-char p) c))
        (read-char p)))
  (let ((p (if (null? port) (current-input-port) (car port))))
    (let loop ((c (read-char p)) (line '()))
      (cond ((eof-object? c) (if (null? line) c (list->string (reverse line))))
            ((char=? #\newline c) (eat p #\return) (list->string (reverse line)))
            ((char=? #\return c) (eat p #\newline) (list->string (reverse line)))
            (else (loop (read-char p) (cons c line)))))))

(define (string-split sep str)
  (define (f cs xs) (cons (list->string (reverse cs)) xs))
  (let loop ((ss (string->list str)) (cs '()) (xs '()))
    (cond ((null? ss) (reverse (if (null? cs) xs (f cs xs))))
          ((char=? (car ss) sep) (loop (cdr ss) '() (f cs xs)))
          (else (loop (cdr ss) (cons (car ss) cs) xs)))))

(define (read-input)
  (let loop ((line (read-line)) (lines (list)))
    (if (eof-object? line) lines
      (let* ((fields (string-split #\| line))
             (student-number (string->number (car fields)))
             (class-name (cadr fields))
             (grade (string->number (caddr fields)))
             (fields (list student-number class-name grade)))
        (loop (read-line) (cons fields lines))))))

(define (lt? a b)
  (or (string<? (cadr a) (cadr b))
      (and (not (string<? (cadr b) (cadr a)))
           (< (car a) (car b)))))

(define (write-output lines)
  (let loop ((lines (sort lines lt?)) (prev ""))
    (when (pair? lines)
      (when (not (string=? prev (cadar lines)))
        (display (cadar lines)) (display #\tab)
        (display (caddar lines)) (newline))
      (loop (cdr lines) (cadar lines)))))

(define (exercise7)
  (write-output (read-input)))

(exercise7)