anchor.diff:  (in-package :e-pluribus-hugo)
   
   (defparameter *category-alist*
  -  '(("1" . "Novel") ("2" . "Novella") ("3" . "Novelette") ("4" . "Short story")
  -    ("5" . "Non-Fiction Book") ("6" . "Dramatic Presentation")
  -    ("7" . "Professional Editor") ("8" . "Professional Artist")
  -    ("9" . "Semiprozine") ("10" . "Fanzine") ("11" . "Fan Writer")
  -    ("12" . "Fan Artist") ("13" . "John W. Campbell Award (Not a Hugo)")))
  +  '((1 . "Novel") (2 . "Novella") (3 . "Novelette") (4 . "Short story")
  +    (5 . "Non-Fiction Book") (6 . "Dramatic Presentation")
  +    (7 . "Professional Editor") (8 . "Professional Artist")
  +    (9 . "Semiprozine") (10 . "Fanzine") (11 . "Fan Writer")
  +    (12 . "Fan Artist") (13 . "John W. Campbell Award (Not a Hugo)")))
   
   (defun categories ()
     (mapcar #'car *category-alist*))
   
  -(defun category->description (category)
  +(defun category-description (category)
     "Return the full description of the CATEGORY."
  -  (cdr (assoc category *category-alist* :test 'string=)))
  -
  +  (cdr (assoc category *category-alist*)))
  +
  +;; Each voter has one ballot.  Each ballot has multiple categories.
  +;; There is a list of nominated works or authors for each category on a
  +;; ballot.
  +;;
  +;; An individual ballot is modeled as an association of the voter ID
  +;; and a hash table with the categories as the keys.  Each value in
  +;; the hash table is a list of titles or authors.  The full set of
  +;; ballots is simply a list of individual ballots.
  +
  +(defun make-ballot (voter)
  +  "Create an association list element representing a ballot."
  +  (cons voter (make-hash-table)))
  +
  +(defun add-nomination (ballot category title)
  +  "Add a nominated work (title or author) to the BALLOT.
  +  No work can be listed more than once on the same ballot."
  +  (let ((nominations (cdr ballot)))
  +    (if (null (gethash category nominations))
  +        (setf (gethash category nominations) (list title))
  +        (pushnew title (gethash category nominations) :test 'string=))))
  +
  +;; The field names and column ranges in the sample data file.
   (defparameter *fields-alist*
     '(('voter . (0 5))
       ('category . (5 8))
  -    ('title . (27 74))
  -    ('author . (77 112))))
  +    ('title . (27 74))))
   
   (defun parse-fields (line)
  -  "Get the voter ID, category, title, and author from a ballot file line."
  +  "Get the voter ID, category, and title from a ballot file LINE."
     (let ((fields (list)))
       (dolist (field *fields-alist* (nreverse fields))
         (let ((start (first (cdr field)))
  
positive.code:               (fraction->string (second result))
                (third result)))))
  
  (defconstant +maximum-results+ 5)
  
  (defun eph-rule-results (ballots)
  
negative.code:   ;; TODO:  in write up, write this first then the functions
    (dolist (category (categories))
      (let ((contenders (category-titles ballots category))
            (category-ballots (category-ballots ballots category)))
        (while (> (length contenders) +maximum-results+)
          (let* ((scores (score contenders category-ballots))
                 (least-popular (selection-phase scores))
                 (eliminated (elimination-phase least-popular scores)))
            (setf contenders (set-difference contenders eliminated))
            (setf category-ballots (eliminate eliminated category-ballots))
            (unless (> (length contenders) +maximum-results+)
              (category-eph-results category
                                    (score contenders category-ballots))))))))
  
  ;; To generate the results under the E Pluribus Hugo rules, evaluate
  ;;   (eph-rule-results *ballots*)
  