
(defparameter *n* 0)

(defun calc-counts (total-weight keys &aux results)
  (labels ((calc (left-weight keys-count keys)
             (incf *n*)
             (cond ((= left-weight 0)
                    (push keys-count results))
                   ((and (> left-weight 0) (consp keys))
                    (destructuring-bind (key &rest rest-keys) keys
                      (calc (- left-weight (key-weight key))
                            (add-key keys-count key)
                            keys)
                      (calc left-weight keys-count rest-keys))))))
    (calc total-weight (make-counts keys) keys)
    results))

(defun make-counts (keys)
  (loop :for key :in keys
        :collect (cons key 0)))

(defun add-key (counts k)
  (loop :for (key . count) :in counts
        :collect (cons key (if (eq key k) (1+ count) count))))

(defun get-key-count (counts k)
  (cdr (assoc k counts)))

(defun key-weight (k)
  (get k :weight))

(defun (setf key-weight) (w k)
  (setf (get k :weight) w))

; --------

(defconstant cake&tea-weight 50)
(defconstant    apple-weight 65)

(setf (key-weight 'sour)      (+ apple-weight (* 3 cake&tea-weight)))
(setf (key-weight 'half-sour) (+ apple-weight (* 2 cake&tea-weight)))
(setf (key-weight 'sweet)     (+ apple-weight      cake&tea-weight) )

(defparameter *fmt*
  "~%Result #~d
  Sour      apples: ~4d
  Half-sour apples: ~4d
  Sweet     apples: ~4d
  Total apples: ~d, cakes: ~d, tea drinks: ~d
")

(defun main ()
  (let ((results (calc-counts 1435 '(sour half-sour sweet))))
    (loop :for i :upfrom 1
          :for result :in results :do
          (let* ((sour   (get-key-count result 'sour))
                 (half   (get-key-count result 'half-sour))
                 (sweet  (get-key-count result 'sweet))
                 (apples (+ sour half sweet))
                 (c&t    (+ (* 3 sour) (* 2 half) sweet)))
            (format t *fmt* i sour half sweet apples c&t c&t)))
    (format t "~%~a cases~%" *n*)))

(main)