# 7c082c0289d5

`Problem 22`
author Steve Losh Mon, 20 Feb 2017 17:14:25 +0000 5effe1bc7876 6e9dc46857b5 (none) data/22-names.txt src/euler.lisp vendor/make-quickutils.lisp vendor/quickutils.lisp

## Changes

```--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/22-names.txt	Mon Feb 20 17:14:25 2017 +0000
@@ -0,0 +1,1 @@
\ No newline at end of file```
```--- a/src/euler.lisp	Fri Feb 17 13:07:45 2017 +0000
+++ b/src/euler.lisp	Mon Feb 20 17:14:25 2017 +0000
@@ -576,6 +576,31 @@
(= n (sum-of-divisors other))))))
(sum (remove-if-not #'amicablep (range 1 10000)))))

+(defun problem-22 ()
+  ;; Using names.txt, a 46K text file containing over five-thousand first names,
+  ;; begin by sorting it into alphabetical order. Then working out the
+  ;; alphabetical value for each name, multiply this value by its alphabetical
+  ;; position in the list to obtain a name score.
+  ;;
+  ;; For example, when the list is sorted into alphabetical order, COLIN, which
+  ;; is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So,
+  ;; COLIN would obtain a score of 938 × 53 = 49714.
+  ;;
+  ;; What is the total of all the name scores in the file?
+             (-<> "data/22-names.txt"
+               (substitute #\Space #\, <>)
+               (sort <> #'string<)))
+           (letter-score (letter)
+             (1+ (- (char-code letter) (char-code #\A))))
+           (name-score (name)
+             (sum name :key #'letter-score)))
+    (iterate (for (position . name) :in
+             (sum (* position (name-score name))))))
+

;;;; Tests --------------------------------------------------------------------
(def-suite :euler)
@@ -602,6 +627,7 @@
(test p19 (is (= 171 (problem-19))))
(test p20 (is (= 648 (problem-20))))
(test p21 (is (= 31626 (problem-21))))
+(test p22 (is (= 871198282 (problem-22))))

;; (run! :euler)```
```--- a/vendor/make-quickutils.lisp	Fri Feb 17 13:07:45 2017 +0000
+++ b/vendor/make-quickutils.lisp	Mon Feb 20 17:14:25 2017 +0000
@@ -7,6 +7,7 @@
:curry
:define-constant
:ensure-boolean
:n-grams
:range
:rcurry```
```--- a/vendor/quickutils.lisp	Fri Feb 17 13:07:45 2017 +0000
+++ b/vendor/quickutils.lisp	Mon Feb 20 17:14:25 2017 +0000
@@ -2,7 +2,7 @@
;;;; See http://quickutil.org for details.

;;;; To regenerate:
-;;;; (qtlc:save-utils-as "quickutils.lisp" :utilities '(:CURRY :DEFINE-CONSTANT :ENSURE-BOOLEAN :N-GRAMS :RANGE :RCURRY :SWITCH :WITH-GENSYMS) :ensure-package T :package "EULER.QUICKUTILS")
+;;;; (qtlc:save-utils-as "quickutils.lisp" :utilities '(:CURRY :DEFINE-CONSTANT :ENSURE-BOOLEAN :READ-FILE-INTO-STRING :N-GRAMS :RANGE :RCURRY :SWITCH :WITH-GENSYMS) :ensure-package T :package "EULER.QUICKUTILS")

(unless (find-package "EULER.QUICKUTILS")
@@ -15,8 +15,10 @@
(when (boundp '*utilities*)
(setf *utilities* (union *utilities* '(:MAKE-GENSYM-LIST :ENSURE-FUNCTION
:CURRY :DEFINE-CONSTANT
-                                         :ENSURE-BOOLEAN :TAKE :N-GRAMS :RANGE
-                                         :RCURRY :STRING-DESIGNATOR
+                                         :ENSURE-BOOLEAN :ONCE-ONLY
+                                         :WITH-OPEN-FILE* :WITH-INPUT-FROM-FILE
+                                         :RANGE :RCURRY :STRING-DESIGNATOR
:WITH-GENSYMS :EXTRACT-FUNCTION-NAME
:SWITCH))))
@@ -105,6 +107,97 @@
(and x t))

+  (defmacro once-only (specs &body forms)
+    "Evaluates `forms` with symbols specified in `specs` rebound to temporary
+variables, ensuring that each initform is evaluated only once.
+
+Each of `specs` must either be a symbol naming the variable to be rebound, or of
+the form:
+
+    (symbol initform)
+
+Bare symbols in `specs` are equivalent to
+
+    (symbol symbol)
+
+Example:
+
+    (defmacro cons1 (x) (once-only (x) `(cons ,x ,x)))
+      (let ((y 0)) (cons1 (incf y))) => (1 . 1)"
+    (let ((gensyms (make-gensym-list (length specs) "ONCE-ONLY"))
+          (names-and-forms (mapcar (lambda (spec)
+                                     (etypecase spec
+                                       (list
+                                        (destructuring-bind (name form) spec
+                                          (cons name form)))
+                                       (symbol
+                                        (cons spec spec))))
+                                   specs)))
+      ;; bind in user-macro
+      `(let ,(mapcar (lambda (g n) (list g `(gensym ,(string (car n)))))
+              gensyms names-and-forms)
+         ;; bind in final expansion
+         `(let (,,@(mapcar (lambda (g n)
+                             ``(,,g ,,(cdr n)))
+                           gensyms names-and-forms))
+            ;; bind in user-macro
+            ,(let ,(mapcar (lambda (n g) (list (car n) g))
+                    names-and-forms gensyms)
+               ,@forms)))))
+
+
+  (defmacro with-open-file* ((stream filespec &key direction element-type
+                                                   if-exists if-does-not-exist external-format)
+                             &body body)
+    "Just like `with-open-file`, but `nil` values in the keyword arguments mean to use
+the default value specified for `open`."
+    (once-only (direction element-type if-exists if-does-not-exist external-format)
+      `(with-open-stream
+           (,stream (apply #'open ,filespec
+                           (append
+                            (when ,direction
+                              (list :direction ,direction))
+                            (when ,element-type
+                              (list :element-type ,element-type))
+                            (when ,if-exists
+                              (list :if-exists ,if-exists))
+                            (when ,if-does-not-exist
+                              (list :if-does-not-exist ,if-does-not-exist))
+                            (when ,external-format
+                              (list :external-format ,external-format)))))
+         ,@body)))
+
+
+  (defmacro with-input-from-file ((stream-name file-name &rest args
+                                                         &key (direction nil direction-p)
+                                                         &allow-other-keys)
+                                  &body body)
+    "Evaluate `body` with `stream-name` to an input stream on the file
+`file-name`. `args` is sent as is to the call to `open` except `external-format`,
+which is only sent to `with-open-file` when it's not `nil`."
+    (declare (ignore direction))
+    (when direction-p
+      (error "Can't specifiy :DIRECTION for WITH-INPUT-FROM-FILE."))
+    `(with-open-file* (,stream-name ,file-name :direction :input ,@args)
+       ,@body))
+
+
+  (defun read-file-into-string (pathname &key (buffer-size 4096) external-format)
+    "Return the contents of the file denoted by `pathname` as a fresh string.
+
+The `external-format` parameter will be passed directly to `with-open-file`
+unless it's `nil`, which means the system default."
+    (with-input-from-file
+        (file-stream pathname :external-format external-format)
+      (let ((*print-pretty* nil))
+        (with-output-to-string (datum)
+          (let ((buffer (make-array buffer-size :element-type 'character)))
+            (loop
+              :do (write-sequence buffer datum :start 0 :end bytes-read)
+
+
(defun take (n sequence)
"Take the first `n` elements from `sequence`."
(subseq sequence 0 n))
@@ -238,7 +331,7 @@
(generate-switch-body whole object clauses test key '(cerror "Return NIL from CSWITCH.")))