1.1--- a/lisp/lib/cry/crc64.lisp Sat Mar 23 21:28:11 2024 -0400
1.2+++ b/lisp/lib/cry/crc64.lisp Sat Mar 23 23:02:31 2024 -0400
1.3@@ -36,12 +36,14 @@
1.4 ;; Lookup tables.
1.5 ;; We store high and low order bytes separately to benefit from
1.6 ;; 32 bit arithmentic performance.
1.7+
1.8 (declaim (type (array (unsigned-byte 32) (256)) *crc-table-h* *crc-table-l*))
1.9 (defvar *crc-table-h* (make-array 256 :element-type '(unsigned-byte 32)))
1.10 (defvar *crc-table-l* (make-array 256 :element-type '(unsigned-byte 32)))
1.11
1.12 (defun init-crc64 (polynomial)
1.13- "Computes lookup tables of CRC values for byte values 0 thru 255"
1.14+ "Computes lookup tables of CRC values for byte values 0 thru 255. Don't
1.15+forget to call this before calling the library functions."
1.16 (dotimes (i 256)
1.17 (let ((part i))
1.18 (dotimes (j 8)
2.1--- a/lisp/lib/cry/cry.asd Sat Mar 23 21:28:11 2024 -0400
2.2+++ b/lisp/lib/cry/cry.asd Sat Mar 23 23:02:31 2024 -0400
2.3@@ -1,4 +1,4 @@
2.4-(defsystem :crypto
2.5+(defsystem :cry
2.6 :version "0.1.0"
2.7 :maintainer "ellis <ellis@rwest.io>"
2.8 :bug-tracker "https://vc.compiler.company/comp/core/issues"
3.1--- a/lisp/lib/cry/hotp.lisp Sat Mar 23 21:28:11 2024 -0400
3.2+++ b/lisp/lib/cry/hotp.lisp Sat Mar 23 23:02:31 2024 -0400
3.3@@ -1,6 +1,43 @@
3.4 ;;; crypto/hotp.lisp --- HMAC-Based One-Time Passwords
3.5
3.6+;; see https://github.com/bhyde/cl-one-time-passwords/hotp.lisp
3.7+
3.8 ;; RFC 4226
3.9
3.10 ;;; Code:
3.11-(in-package :crypto/hotp)
3.12+(in-package :cry/hotp)
3.13+
3.14+(defvar *digits* 6)
3.15+
3.16+(defvar *hmac-sha-mode* :sha1)
3.17+
3.18+(defun hotp (key-string counter)
3.19+ (hotp-truncate (hmac-sha-n key-string counter)))
3.20+
3.21+(defun hotp-truncate (20-bytes)
3.22+ (flet ((dt (ht)
3.23+ (let* ((byte19 (aref ht 19))
3.24+ (byte-offset (ldb (byte 4 0) byte19))
3.25+ (result 0))
3.26+ (setf (ldb (byte 7 24) result) (aref ht byte-offset))
3.27+ (setf (ldb (byte 8 16) result) (aref ht (+ 1 byte-offset)))
3.28+ (setf (ldb (byte 8 8) result) (aref ht (+ 2 byte-offset)))
3.29+ (setf (ldb (byte 8 0) result) (aref ht (+ 3 byte-offset)))
3.30+ result)))
3.31+ (let ((sbits (dt 20-bytes)))
3.32+ (mod sbits
3.33+ (svref #(1 10 100 1000 10000 100000 1000000 10000000 100000000)
3.34+ *digits*)))))
3.35+
3.36+(defun hmac-sha-n (key-string counter)
3.37+ (loop
3.38+ with counter-bytes = (make-array 8 :element-type '(unsigned-byte 8))
3.39+ with hmac = (ironclad:make-hmac
3.40+ (ironclad:hex-string-to-byte-array key-string)
3.41+ *hmac-sha-mode*)
3.42+ finally
3.43+ (ironclad:update-hmac hmac counter-bytes)
3.44+ (return (ironclad:hmac-digest hmac))
3.45+ for i from 7 downto 0
3.46+ for offset from 0 by 8
3.47+ do (setf (aref counter-bytes i) (ldb (byte 8 offset) counter))))
4.1--- a/lisp/lib/cry/pkg.lisp Sat Mar 23 21:28:11 2024 -0400
4.2+++ b/lisp/lib/cry/pkg.lisp Sat Mar 23 23:02:31 2024 -0400
4.3@@ -10,11 +10,17 @@
4.4
4.5 (defpackage :cry/hotp
4.6 (:nicknames :hotp)
4.7- (:use :cl :std :cry))
4.8+ (:use :cl :std :cry)
4.9+ (:export *digits*
4.10+ *hmac-sha-mode*
4.11+ hotp))
4.12
4.13 (defpackage :cry/totp
4.14 (:nicknames :totp)
4.15- (:use :cl :std :cry))
4.16+ (:use :cl :std :cry/hotp)
4.17+ (:export *time-zero*
4.18+ *time-step-in-seconds*
4.19+ totp))
4.20
4.21 (defpackage :cry/crc64
4.22 (:use :cl)
4.23@@ -54,6 +60,8 @@
4.24 (defclass password () ())
4.25 (defclass password-db (database) ())
4.26 (defclass password-store () ())
4.27+
4.28+;;; Proto
4.29 (defgeneric register-user (user &key store password deadline)
4.30 (:documentation "Register user identified by TOKEN in store specified by STORE. Returns
4.31 the user object and an optionally a confirmation token."))
5.1--- a/lisp/lib/cry/totp.lisp Sat Mar 23 21:28:11 2024 -0400
5.2+++ b/lisp/lib/cry/totp.lisp Sat Mar 23 23:02:31 2024 -0400
5.3@@ -1,6 +1,22 @@
5.4 ;;; crypto/totp.lisp --- Time-Based One-Time Passwords
5.5
5.6+;; see https://github.com/bhyde/cl-one-time-passwords/totp.lisp
5.7+
5.8 ;; RFC 6238
5.9
5.10 ;;; Code:
5.11-(in-package :cry
5.12+(in-package :cry/totp)
5.13+
5.14+(defconstant .unix-epoch-zero. 2208988800)
5.15+ ;; 00:00:00 UTC on 1 January 1970
5.16+ ;; (encode-universal-time 0 0 0 1 1 1970 0)
5.17+ ;; --> 2208988800
5.18+
5.19+(defvar *time-zero* 0) ; aka the unix epoch zero
5.20+(defvar *time-step-in-seconds* 30)
5.21+
5.22+(defmacro time-step (unix-time)
5.23+ `(floor (- ,unix-time *time-zero*) *time-step-in-seconds*))
5.24+
5.25+(defun totp (key-hexstring &optional (offset 0) (time (- (get-universal-time) .unix-epoch-zero. offset)))
5.26+ (hotp key-hexstring (time-step time)))
6.1--- a/lisp/lib/dat/dat.asd Sat Mar 23 21:28:11 2024 -0400
6.2+++ b/lisp/lib/dat/dat.asd Sat Mar 23 23:02:31 2024 -0400
6.3@@ -1,6 +1,6 @@
6.4 (defsystem :dat
6.5 :description "Data formats"
6.6- :depends-on (:cl-ppcre :std :obj)
6.7+ :depends-on (:cl-ppcre :std :obj :png)
6.8 :version "0.1.0"
6.9 :serial t
6.10 :components ((:file "pkg")
8.1--- a/lisp/lib/dat/pkg.lisp Sat Mar 23 21:28:11 2024 -0400
8.2+++ b/lisp/lib/dat/pkg.lisp Sat Mar 23 23:02:31 2024 -0400
8.3@@ -123,5 +123,21 @@
8.4 #:header #:header-type
8.5 #:unknown-event #:status #:data-byte #:dd #:bb #:cc #:nn))
8.6
8.7+(defpackage :dat/qrcode
8.8+ (:nicknames :qrcode)
8.9+ (:use :cl :std :dat/proto)
8.10+ (:export
8.11+ :encode-symbol
8.12+ ;; QR code representation
8.13+ ;; this should be enough to write another backend for QR symbol
8.14+ :qr-symbol
8.15+ :matrix
8.16+ :modules
8.17+ :dark-module-p
8.18+ :read-file-content))
8.19+
8.20+(defpackage :dat/png
8.21+ (:use :cl :std :dat/proto :dat/qrcode :png))
8.22+
8.23 (uiop:define-package :dat
8.24 (:use-reexport :dat/proto :dat/csv :dat/arff :dat/toml :dat/json :dat/sxp :dat/xml :dat/bencode))
9.1--- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2+++ b/lisp/lib/dat/png.lisp Sat Mar 23 23:02:31 2024 -0400
9.3@@ -0,0 +1,57 @@
9.4+;;; dat/png.lisp --- PNG image format
9.5+
9.6+;;
9.7+
9.8+;;; Code:
9.9+(in-package :dat/png)
9.10+
9.11+;;;; Copyright (c) 2011-2014 jnjcc, Yste.org. All rights reserved.
9.12+;;;;
9.13+;;;; png backend for QR code symbol
9.14+
9.15+;; (defun set-color (pngarray x y color)
9.16+;; (setf (aref pngarray x y 0) color)
9.17+;; (setf (aref pngarray x y 1) color)
9.18+;; (setf (aref pngarray x y 2) color))
9.19+
9.20+;; (defun symbol->png (symbol pixsize margin)
9.21+;; "return the qr symbol written into a zpng:png object with PIXSIZE
9.22+;; pixels for each module, and MARGIN pixels on all four sides"
9.23+;; (with-slots (matrix modules) symbol
9.24+;; (let* ((size (+ (* modules pixsize) (* margin 2)))
9.25+;; (qrpng (make-instance 'zpng:png :width size :height size))
9.26+;; (qrarray (zpng:data-array qrpng)))
9.27+;; (dotimes (x size)
9.28+;; (dotimes (y size)
9.29+;; (if (and (<= margin x (- size margin 1))
9.30+;; (<= margin y (- size margin 1)))
9.31+;; (let ((i (floor (- x margin) pixsize))
9.32+;; (j (floor (- y margin) pixsize)))
9.33+;; (if (dark-module-p matrix i j)
9.34+;; (set-color qrarray x y 0)
9.35+;; (set-color qrarray x y 255)))
9.36+;; ;; quiet zone
9.37+;; (set-color qrarray x y 255))))
9.38+;; qrpng)))
9.39+
9.40+;; (defun encode-png (text &key (fpath "qrcode.png") (version 1) (level :level-m)
9.41+;; (mode nil) (pixsize 9) (margin 8))
9.42+;; (let ((symbol (encode-symbol text :version version :level level :mode mode)))
9.43+;; (zpng:write-png (symbol->png symbol pixsize margin) fpath)))
9.44+
9.45+;; (defun encode-png-stream (text stream &key (version 1) (level :level-m)
9.46+;; (mode nil) (pixsize 9) (margin 8))
9.47+;; (let ((symbol (encode-symbol text :version version :level level :mode mode)))
9.48+;; (zpng:write-png-stream (symbol->png symbol pixsize margin) stream)))
9.49+
9.50+;; (defun encode-png-bytes (bytes &key (fpath "kanji.png") (version 1)
9.51+;; (level :level-m) (mode nil) (pixsize 9) (margin 8))
9.52+;; (let ((symbol (encode-symbol-bytes bytes :version version :level level
9.53+;; :mode mode)))
9.54+;; (zpng:write-png (symbol->png symbol pixsize margin) fpath)))
9.55+
9.56+;; (defun encode-png-bytes-stream (bytes stream &key (version 1) (level :level-m)
9.57+;; (mode nil) (pixsize 9) (margin 8))
9.58+;; (let ((symbol (encode-symbol-bytes bytes :version version :level level
9.59+;; :mode mode)))
9.60+;; (zpng:write-png-stream (symbol->png symbol pixsize margin) stream)))
10.1--- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2+++ b/lisp/lib/dat/qrcode.lisp Sat Mar 23 23:02:31 2024 -0400
10.3@@ -0,0 +1,1628 @@
10.4+;;; dat/qrcode.lisp --- QR Code formats
10.5+
10.6+;; see https://github.com/jnjcc/cl-qrencode
10.7+
10.8+;;; Code:
10.9+(in-package :dat/qrcode)
10.10+
10.11+;;;; Copyright (c) 2011-2014 jnjcc, Yste.org. All rights reserved.
10.12+;;;;
10.13+(defun read-file-content (fpath)
10.14+ (with-open-file (fp fpath)
10.15+ (let ((content (make-string (file-length fp))))
10.16+ (read-sequence content fp)
10.17+ content)))
10.18+
10.19+;;;; Galois Field with primitive element 2, as used by Reed-Solomon code
10.20+
10.21+(defclass galois ()
10.22+ ((power :initform nil :initarg :power :reader gf-power
10.23+ :documentation "Galois Field GF(2^POWER)")
10.24+ (prime-poly :initform nil :initarg :ppoly :reader prime-poly
10.25+ :documentation "prime polynomial")
10.26+ (order :initform nil :reader gf-order)
10.27+ (exp-table :initform nil)
10.28+ (log-table :initform nil)))
10.29+
10.30+(defmethod initialize-instance :after ((gf galois) &rest args)
10.31+ (declare (ignore args))
10.32+ (setf (slot-value gf 'order) (ash 1 (slot-value gf 'power)))
10.33+ (let* ((order (gf-order gf))
10.34+ (ppoly (prime-poly gf))
10.35+ ;; 2^0 = 1 && (log 0) = -1
10.36+ (exptab (make-array order :initial-element 1))
10.37+ (logtab (make-array order :initial-element -1)))
10.38+ (do ((i 1 (1+ i)))
10.39+ ((>= i order))
10.40+ (setf (aref exptab i) (* (aref exptab (- i 1)) 2))
10.41+ (when (>= (aref exptab i) order)
10.42+ (setf (aref exptab i)
10.43+ (boole boole-and (- order 1)
10.44+ (boole boole-xor (aref exptab i) ppoly))))
10.45+ (setf (aref logtab (aref exptab i)) i))
10.46+ (setf (aref logtab 1) 0)
10.47+ (setf (slot-value gf 'exp-table) exptab)
10.48+ (setf (slot-value gf 'log-table) logtab)))
10.49+
10.50+;;; value accessor
10.51+(defgeneric gf-exp (gf pow)
10.52+ (:documentation "2^POW under Galois Field GF"))
10.53+(defgeneric gf-log (gf value)
10.54+ (:documentation "VALUE should be within range [0, 2^POW - 1]"))
10.55+
10.56+(defmethod gf-exp ((gf galois) pow)
10.57+ (let* ((sz (- (gf-order gf) 1))
10.58+ (idx (mod pow sz)))
10.59+ (aref (slot-value gf 'exp-table) idx)))
10.60+
10.61+(defmethod gf-log ((gf galois) value)
10.62+ (let* ((sz (gf-order gf))
10.63+ (idx (mod value sz)))
10.64+ (aref (slot-value gf 'log-table) idx)))
10.65+
10.66+;;; Galois Field arithmetic
10.67+(defgeneric gf-add (gf a b))
10.68+(defgeneric gf-subtract (gf a b))
10.69+(defgeneric gf-multiply (gf a b))
10.70+(defgeneric gf-divide (gf a b))
10.71+
10.72+(defmethod gf-add ((gf galois) a b)
10.73+ (boole boole-xor a b))
10.74+
10.75+(defmethod gf-subtract ((gf galois) a b)
10.76+ (boole boole-xor a b))
10.77+
10.78+(defmethod gf-multiply ((gf galois) a b)
10.79+ (let ((sum (+ (gf-log gf a) (gf-log gf b))))
10.80+ (gf-exp gf sum)))
10.81+
10.82+(defmethod gf-divide ((gf galois) a b)
10.83+ (when (= b 0)
10.84+ (error "divide by zero"))
10.85+ (if (= a 0)
10.86+ 0
10.87+ (let ((sub (- (gf-log gf a) (gf-log gf b))))
10.88+ (gf-exp gf sub))))
10.89+
10.90+;;; open-paren at beg of line confuses `slime-compile-defun` which uses
10.91+;;; elisp function `beginning-of-defun`, which in turn involves
10.92+;;; backward-searching open-paren at beg of line
10.93+;;; there seems to be no easy way to fix this problem
10.94+;; with an extra leading '\', docstring is kind of ulgy now, though
10.95+(defmacro with-gf-accessors (accessors gf &body body)
10.96+ "shortcuts for gf-exp & gf-log, usage:
10.97+\(with-gf-accessors ((gfexp gf-exp)) *gf-instance* ...)"
10.98+ `(labels ,(mapcar (lambda (acc-entry)
10.99+ (let ((acc-name (car acc-entry))
10.100+ (method-name (cadr acc-entry)))
10.101+ `(,acc-name (a)
10.102+ (,method-name ,gf a))))
10.103+ accessors)
10.104+ ,@body))
10.105+
10.106+(defmacro with-gf-arithmetics (ariths gf &body body)
10.107+ "shortcuts for gf-add, gf-subtract, gf-multiply & gf-divide, usage:
10.108+\(with-gf-arithmetics ((gf+ gf-add)) *gf-instance* ...)"
10.109+ `(labels ,(mapcar (lambda (arith-entry)
10.110+ (let ((arith-name (car arith-entry))
10.111+ (method-name (cadr arith-entry)))
10.112+ `(,arith-name (a b)
10.113+ (,method-name ,gf a b))))
10.114+ ariths)
10.115+ ,@body))
10.116+
10.117+(defmacro with-gf-shortcuts (accessors ariths gf &body body)
10.118+ "combined with-gf-accessors & with-gf-arithmetics, usage:
10.119+\(with-gf-shortcuts ((gflog gf-log)) ((gf* gf-multiply)) *gf-instance* ...)"
10.120+ `(labels ,(append
10.121+ (mapcar (lambda (acc-entry)
10.122+ (let ((acc-name (car acc-entry))
10.123+ (method-name (cadr acc-entry)))
10.124+ `(,acc-name (a)
10.125+ (,method-name ,gf a))))
10.126+ accessors)
10.127+ (mapcar (lambda (arith-entry)
10.128+ (let ((arith-name (car arith-entry))
10.129+ (method-name (cadr arith-entry)))
10.130+ `(,arith-name (a b)
10.131+ (,method-name ,gf a b))))
10.132+ ariths))
10.133+ ,@body))
10.134+
10.135+;;;; Bose-Chaudhuri-Hocquenghem (BCH) error correction code
10.136+
10.137+;;; Polynomial (using list) arithmetics
10.138+;;; by polynomial list (3 2 1), we mean 3*x^2 + 2*x + 1
10.139+(defun poly-ash (poly s)
10.140+ "shift left POLY by S"
10.141+ (declare (type list poly))
10.142+ (append poly (make-list s :initial-element 0)))
10.143+(defun poly-multiply (poly b &optional (op #'*))
10.144+ "multiply B on every element of POLY using OP"
10.145+ (labels ((mult (elem)
10.146+ (funcall op elem b)))
10.147+ (mapcar #'mult poly)))
10.148+(defun poly-substract (lhs rhs &optional (op #'-))
10.149+ (labels ((sub (elem1 elem2)
10.150+ (funcall op elem1 elem2)))
10.151+ (mapcar #'sub lhs rhs)))
10.152+(defun poly-mod (msg gen rem &optional (sub #'poly-substract) (mul #'poly-multiply))
10.153+ "MSG % GEN, with REM remainders"
10.154+ (labels ((cdrnzero (msg rem)
10.155+ (do ((head msg (cdr head)))
10.156+ ((or (null head) (<= (length head) rem) (/= (car head) 0)) head)
10.157+ head)))
10.158+ (do ((m (poly-ash msg rem) (cdrnzero m rem)))
10.159+ ((<= (length m) rem) m)
10.160+ (let* ((glen (length gen))
10.161+ (sft (- (length m) glen))
10.162+ ;; LEAD coffiecient of message polynomial
10.163+ (lead (car m)))
10.164+ (setf m (funcall sub m (poly-ash (funcall mul gen lead) sft)))))))
10.165+
10.166+(defclass bch-ecc ()
10.167+ ((k :initform nil :initarg :k
10.168+ :documentation "# of data codewords")
10.169+ (ec :initform nil :initarg :ec
10.170+ :documentation "# of error correction codewords")))
10.171+
10.172+(defun bch* (poly b)
10.173+ (poly-multiply poly b))
10.174+(defun bch- (lhs rhs)
10.175+ (labels ((xor (a b)
10.176+ (boole boole-xor a b)))
10.177+ (poly-substract lhs rhs #'xor)))
10.178+(defun bch-xor (lhs rhs)
10.179+ (labels ((xor (a b)
10.180+ (boole boole-xor a b)))
10.181+ (mapcar #'xor lhs rhs)))
10.182+(defun bch% (msg gen rem)
10.183+ (poly-mod msg gen rem #'bch- #'bch*))
10.184+
10.185+(defgeneric bch-ecc (bch msgpoly genpoly)
10.186+ (:documentation "do bch error correction under BCH(K+EC, K)"))
10.187+
10.188+(defmethod bch-ecc ((bch bch-ecc) msg gen)
10.189+ (with-slots (k ec) bch
10.190+ (unless (= (length msg) k)
10.191+ (error "wrong msg length, expect: ~A; got: ~A~%" k (length msg)))
10.192+ (bch% msg gen ec)))
10.193+
10.194+;;; As used by format information ecc & version information ecc respectively
10.195+;;; BCH(15, 5) & BCH(18, 6)
10.196+(let ((fi-ecc (make-instance 'bch-ecc :k 5 :ec 10))
10.197+ ;; format information generator polynomial
10.198+ ;; x^10 + x^8 + x^5 + x^4 + x^2 + x + 1
10.199+ (fi-gpoly '(1 0 1 0 0 1 1 0 1 1 1))
10.200+ (fi-xor '(1 0 1 0 1 0 0 0 0 0 1 0 0 1 0)))
10.201+ (defun format-ecc (level mask-ind)
10.202+ (let ((seq (append (level-indicator level)
10.203+ (mask-pattern-ref mask-ind))))
10.204+ (bch-xor (append seq (bch-ecc fi-ecc seq fi-gpoly))
10.205+ fi-xor))))
10.206+
10.207+(let ((vi-ecc (make-instance 'bch-ecc :k 6 :ec 12))
10.208+ ;; version information generator polynomial
10.209+ ;; x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
10.210+ (vi-gpoly '(1 1 1 1 1 0 0 1 0 0 1 0 1)))
10.211+ (defun version-ecc (version)
10.212+ (let ((seq (decimal->bstream version 6)))
10.213+ (append seq (bch-ecc vi-ecc seq vi-gpoly)))))
10.214+
10.215+(defclass rs-ecc ()
10.216+ ((k :initform nil :initarg :k
10.217+ :documentation "# of data codewords")
10.218+ (ec :initform nil :initarg :ec
10.219+ :documentation "# of error correction codewords")
10.220+ (gpoly :initform nil :reader gpoly
10.221+ :documentation "with EC, we calculate generator poly immediately")))
10.222+
10.223+;;; Reed-Solomon code uses GF(2^8) with prime polynomial 285,
10.224+;;; or 1,0001,1101, or (x^8 + x^4 + x^3 + x^2 + 1)
10.225+(let ((gf256 (make-instance 'galois :power 8 :ppoly 285)))
10.226+ ;; Polynomial arithmetics under GF(2^8), as used by Reed-Solomon ecc
10.227+ (defun rs* (poly b)
10.228+ "multiply B on every element of POLY under GF(2^8)"
10.229+ (with-gf-arithmetics ((gf* gf-multiply)) gf256
10.230+ (poly-multiply poly b #'gf*)))
10.231+ (defun rs- (lhs rhs)
10.232+ (with-gf-arithmetics ((gf- gf-subtract)) gf256
10.233+ (poly-substract lhs rhs #'gf-)))
10.234+ (defun rs% (msg gen rem)
10.235+ (poly-mod msg gen rem #'rs- #'rs*))
10.236+
10.237+ (defmethod initialize-instance :after ((rs rs-ecc) &rest args)
10.238+ (declare (ignore args))
10.239+ (setf (slot-value rs 'gpoly) (gen-poly rs)))
10.240+
10.241+ (defgeneric gen-poly (rs))
10.242+ (defmethod gen-poly ((rs rs-ecc))
10.243+ "Generator Polynomial: (x-a^0) * (x-a^1) * ... * (x-a^(ec-1))"
10.244+ (with-slots (ec) rs
10.245+ (let* ((size (+ ec 1))
10.246+ (poly (make-list size :initial-element nil)))
10.247+ (with-gf-shortcuts ((gfexp gf-exp)) ((gf+ gf-add) (gf* gf-multiply)) gf256
10.248+ (setf (nth 0 poly) 1
10.249+ (nth 1 poly) 1)
10.250+ (do ((i 2 (1+ i)))
10.251+ ((> i ec) poly)
10.252+ (setf (nth i poly) 1)
10.253+ (do ((j (- i 1) (1- j)))
10.254+ ((<= j 0))
10.255+ (if (not (= (nth j poly) 0))
10.256+ (setf (nth j poly)
10.257+ (gf+ (nth (- j 1) poly)
10.258+ (gf* (nth j poly) (gfexp (- i 1)))))
10.259+ (setf (nth j poly) (nth (- j 1) poly))))
10.260+ (setf (nth 0 poly) (gf* (nth 0 poly) (gfexp (- i 1))))))
10.261+ (reverse poly))))
10.262+
10.263+ (defgeneric gen-poly-gflog (rs))
10.264+ (defgeneric ecc-poly (rs msg))
10.265+
10.266+ (defmethod gen-poly-gflog ((rs rs-ecc))
10.267+ (with-gf-accessors ((gflog gf-log)) gf256
10.268+ ;; GPOLY already calculated when making new instance
10.269+ (mapcar #'gflog (gpoly rs))))
10.270+
10.271+ (defmethod ecc-poly ((rs rs-ecc) msg-poly)
10.272+ "Error Correction codewords Polynomial for MSG-POLY"
10.273+ (with-slots (k ec gpoly) rs
10.274+ (unless (= (length msg-poly) k)
10.275+ (error "wrong msg-poly length, expect: ~A~%" k))
10.276+ (rs% msg-poly gpoly ec))))
10.277+
10.278+(deftype qr-mode ()
10.279+ '(member :unknown
10.280+ :numeric :alnum :byte :kanji
10.281+ ;; Extended Channel Interpretation, Structured Append, FNC1
10.282+ :eci :structured :fnc1))
10.283+
10.284+(defun mode-indicator (mode)
10.285+ (declare (type qr-mode mode))
10.286+ (case mode
10.287+ (:numeric '(0 0 0 1)) ; "0001"
10.288+ (:alnum '(0 0 1 0)) ; "0010"
10.289+ (:byte '(0 1 0 0)) ; "0100"
10.290+ (:kanji '(1 0 0 0)) ; "1000"
10.291+ (:eci '(0 1 1 1)) ; "0111"
10.292+ (:structured '(0 0 1 1)) ; "0011"
10.293+ (:fnc1 '(0 1 0 1)))) ; FIXME: "0101" & "1001"
10.294+
10.295+(defun terminator (bstream version level)
10.296+ "End of message"
10.297+ (let* ((nbits (length bstream))
10.298+ (diff (- (* (data-words-capacity version level) 8)
10.299+ nbits)))
10.300+ (cond
10.301+ ((< diff 0) (error "you serious about this?!"))
10.302+ ((<= diff 4) (make-list diff :initial-element 0))
10.303+ (t (make-list 4 :initial-element 0)))))
10.304+
10.305+(defun byte-value (mode byte)
10.306+ "BYTE value under MODE"
10.307+ (declare (type qr-mode mode))
10.308+ (case mode
10.309+ (:numeric
10.310+ (and (<= #x30 byte #x39)
10.311+ (- byte #x30)))
10.312+ (:alnum
10.313+ (cond
10.314+ ((<= #x30 byte #x39) (- byte #x30)) ; 0-9
10.315+ ((<= #x41 byte #x5A) (+ (- byte #x41) 10)) ; A-Z
10.316+ ((= byte #x20) 36) ; SP
10.317+ ((= byte #x24) 37) ; $
10.318+ ((= byte #x25) 38) ; %
10.319+ ((= byte #x2A) 39) ; *
10.320+ ((= byte #x2B) 40) ; +
10.321+ ((= byte #x2D) 41) ; -
10.322+ ((= byte #x2E) 42) ; .
10.323+ ((= byte #x2F) 43) ; /
10.324+ ((= byte #x3A) 44) ; :
10.325+ (t nil)))
10.326+ ((:byte :kanji) byte)))
10.327+
10.328+(defun kanji-word-p (word)
10.329+ "(kanji-p, kanji-range: {0, 1})"
10.330+ (cond
10.331+ ((<= #x8140 word #x9ffc) (values t 0))
10.332+ ((<= #xe040 word #xebbf) (values t 1))
10.333+ (t (values nil nil))))
10.334+
10.335+(defun starts-kanji-p (bytes)
10.336+ "(BYTES starts with kanji-p, kanji word value, kanji-range: {0, 1})"
10.337+ (declare (type list bytes))
10.338+ (let* ((first (car bytes))
10.339+ (second (cadr bytes))
10.340+ (word (and second (+ (ash first 8) second))))
10.341+ (if (and first second)
10.342+ (multiple-value-bind (kanji-p range)
10.343+ (kanji-word-p word)
10.344+ (values kanji-p word range))
10.345+ (values nil nil nil))))
10.346+
10.347+(defun xor-subset-of (bytes)
10.348+ "exclusive subset of first unit of BYTES.
10.349+as for unit, one byte for :numeric, :alnum; two bytes for :kanji"
10.350+ (declare (type list bytes))
10.351+ (let* ((first (car bytes)))
10.352+ (cond
10.353+ ((null first) :unknown)
10.354+ ((byte-value :numeric first) :numeric)
10.355+ ((byte-value :alnum first) :alnum)
10.356+ ;; excluding reserved values 80-9F & E0-FF
10.357+ ((and (not (<= #x80 first #x9F))
10.358+ (not (<= #xE0 first #xFF)))
10.359+ :byte)
10.360+ ((starts-kanji-p bytes)
10.361+ :kanji))))
10.362+
10.363+(defclass qr-input ()
10.364+ ((bytes
10.365+ :initform nil :initarg :bytes :reader bytes :type list
10.366+ :documentation "list of bytes to be encoded")
10.367+ (version
10.368+ :initform 1 :initarg :version :reader version
10.369+ :documentation "version of qr symbol, adapted according to BYTES")
10.370+ (ec-level ; cannot be NIL
10.371+ :initform :level-m :initarg :ec-level :reader level :type ecc-level)
10.372+ (mode
10.373+ :initform nil :initarg :mode :reader mode :type (or null qr-mode)
10.374+ :documentation "if supplied, we force all BYTES to be under MODE,
10.375+therefore, unless you know exactly what you are doing, leave this NIL")
10.376+ (cur-byte
10.377+ :initform 0 :accessor cur-byte
10.378+ :documentation "index of BYTES during data analysis")
10.379+ (segments
10.380+ :initform nil :accessor segments :type list
10.381+ :documentation
10.382+ "list of list, of the form ((:mode1 byte ...) (:mode2 byte ...) ...)")
10.383+ (bstream
10.384+ :initform nil :reader bstream :type list
10.385+ :documentation "list of 0-1 values after encoding SEGMENTS")
10.386+ (blocks
10.387+ :initform nil :reader blocks :type list
10.388+ :documentation "list of list, of the form ((codeword ...) (codeword ...) ...)
10.389+after converting BSTREAM to codewords")
10.390+ (ecc-blocks ; error correction blocks
10.391+ :initform nil :reader ecc-blocks :type list
10.392+ :documentation "list of list, ec codewords corresponding to BLOCKS")
10.393+ (msg-codewords
10.394+ :initform nil :reader message :type list
10.395+ :documentation "list of codewords from BLOCKS & ECC-BLOCKS,
10.396+interleaving if neccessary")
10.397+ (matrix
10.398+ :initform nil :accessor matrix
10.399+ :documentation "raw QR code symbol (without masking) as matrix")))
10.400+
10.401+(defmethod initialize-instance :after ((input qr-input) &rest args)
10.402+ (declare (ignore args))
10.403+ (validate-and-analysis input))
10.404+
10.405+;;; 0) Data analysis
10.406+(defgeneric validate-and-analysis (input)
10.407+ (:documentation "adapt VERSION according to BYTES, and fill SEGMENTS slot"))
10.408+;;; 1) Data encoding
10.409+(defgeneric data-encoding (input)
10.410+ (:documentation "encode SEGMENTS into BSTREAM slot"))
10.411+;;; 2) Error correction coding
10.412+(defgeneric ec-coding (input)
10.413+ (:documentation "split BSTREAM into BLOCKS, do rs-ecc, and fill ECC-BLOCKS"))
10.414+;;; 3) Structure final message
10.415+(defgeneric structure-message (input)
10.416+ (:documentation "interleaving BLOCKS and ECC-BLOCKS into MSG-CODEWORDS"))
10.417+;;; 4) Codeword placement in matrix, a.k.a, raw QR code symbol
10.418+(defgeneric module-placement (input)
10.419+ (:documentation "write MSG-CODEWORDS into the raw (without masking) MATRIX"))
10.420+;;; 5) Data masking & Format information
10.421+(defgeneric data-masking (input)
10.422+ (:documentation "mask MATRIX with best pattern, generate the final symbol"))
10.423+
10.424+(defgeneric data-analysis (input)
10.425+ (:documentation "BYTES -> SEGMETS, switch bewteen modes as necessary to
10.426+achieve the most efficient conversion of data"))
10.427+(defgeneric redo-data-analysis (input)
10.428+ (:documentation "VERSION changed, reset CUR-BYTE and redo data analysis"))
10.429+(defgeneric analyse-byte-mode (input &optional seg))
10.430+(defgeneric analyse-alnum-mode (input &optional seg))
10.431+(defgeneric analyse-numeric-mode (input &optional seg))
10.432+(defgeneric analyse-kanji-mode (input &optional seg))
10.433+(defgeneric append-cur-byte (input &optional seg)
10.434+ (:documentation "append CUR-BYTE of BYTES into SEGMENTS"))
10.435+(defun mode-analyse-func (mode)
10.436+ "put CUR-BYTE into MODE, and then look at following BYTES for new segment"
10.437+ (case mode
10.438+ (:byte #'analyse-byte-mode)
10.439+ (:alnum #'analyse-alnum-mode)
10.440+ (:numeric #'analyse-numeric-mode)
10.441+ (:kanji #'analyse-kanji-mode)))
10.442+
10.443+(defmethod data-analysis ((input qr-input))
10.444+ (with-slots (mode cur-byte segments) input
10.445+ (when mode ; MODE supplied
10.446+ (let ((seg (append (list mode) (bytes input))))
10.447+ (setf cur-byte (length (bytes input)))
10.448+ (setf segments (append segments (list seg))))
10.449+ (return-from data-analysis)))
10.450+ (with-slots (bytes version segments) input
10.451+ (let ((init-mode (select-init-mode bytes version)))
10.452+ (funcall (mode-analyse-func init-mode) input))))
10.453+
10.454+(defmethod redo-data-analysis ((input qr-input))
10.455+ (with-slots (cur-byte segments) input
10.456+ (setf cur-byte 0)
10.457+ (setf segments nil)
10.458+ (data-analysis input)))
10.459+
10.460+(defun select-init-mode (bytes version)
10.461+ "optimization of bitstream length: select initial mode"
10.462+ (declare (type list bytes))
10.463+ (let ((init-xor (xor-subset-of bytes)))
10.464+ (case init-xor
10.465+ (:byte :byte)
10.466+ (:kanji
10.467+ (case (xor-subset-of (nthcdr 2 bytes))
10.468+ ((:numeric :alnum) :kanji)
10.469+ (:byte
10.470+ (let ((nunits (ecase (version-range version)
10.471+ ((0 1) 5)
10.472+ (2 6))))
10.473+ (if (every-unit-matches (nthcdr 3 bytes) 2 nunits :kanji)
10.474+ :byte
10.475+ :kanji)))
10.476+ (otherwise :kanji)))
10.477+ (:alnum
10.478+ (let ((nunits (ecase (version-range version)
10.479+ (0 6) (1 7) (2 8))))
10.480+ ;; number of units (characters) match :alnum, followed by a :byte unit
10.481+ (multiple-value-bind (n last-mode) (nunits-matches (cdr bytes) :alnum)
10.482+ (if (and (< n nunits) (eq last-mode :byte))
10.483+ :byte
10.484+ :alnum))))
10.485+ (:numeric
10.486+ (let ((nbunits (ecase (version-range version)
10.487+ ((0 1) 4) (2 5)))
10.488+ (naunits (ecase (version-range version)
10.489+ (0 7) (1 8) (2 9))))
10.490+ (multiple-value-bind (n last-mode) (nunits-matches (cdr bytes) :numeric)
10.491+ (if (and (< n nbunits) (eq last-mode :byte))
10.492+ :byte
10.493+ (if (and (< n naunits) (eq last-mode :alnum))
10.494+ :alnum
10.495+ :numeric))))))))
10.496+
10.497+;;; UNIT: character under a certain mode,
10.498+;;; a byte under :numeric :alnum & :byte, or a byte-pair under :kanji
10.499+(defun every-unit-matches (bytes usize nunits mode)
10.500+ "if every unit of USZIE bytes (at most NUNITS unit) within BYTES matches MODE"
10.501+ (declare (type list bytes) (type qr-mode mode))
10.502+ (when (>= (length bytes) (* usize nunits))
10.503+ (dotimes (i nunits)
10.504+ (let ((b (nthcdr (* usize i) bytes)))
10.505+ (unless (eq (xor-subset-of b) mode)
10.506+ (return-from every-unit-matches nil))))
10.507+ (return-from every-unit-matches t)))
10.508+
10.509+(defun nunits-matches (bytes mode)
10.510+ "(number of units that matches MODE, and mode for the first unmatched unit)"
10.511+ (declare (type list bytes) (type qr-mode mode))
10.512+ (let ((usize (ecase mode
10.513+ ((:byte :alnum :numeric) 1)
10.514+ ;; as for :kanji, 2 bytes forms a single unit
10.515+ (:kanji 2)))
10.516+ (nunits 0))
10.517+ (do ((b bytes (nthcdr usize b)))
10.518+ ((or (null b)
10.519+ (not (eq (xor-subset-of b) mode)))
10.520+ (values nunits (xor-subset-of b)))
10.521+ (incf nunits))))
10.522+
10.523+(defmethod analyse-byte-mode ((input qr-input) &optional (seg '(:byte)))
10.524+ (declare (type list seg))
10.525+ (setf seg (append-cur-byte input seg))
10.526+ (unless seg
10.527+ (return-from analyse-byte-mode))
10.528+ (with-slots (bytes cur-byte version segments) input
10.529+ (let* ((range (version-range version))
10.530+ (nkunits (ecase range ; number of :kanji units before more :byte
10.531+ (0 9) (1 12) (2 13)))
10.532+ (nanuits (ecase range ; number of :alnum units before more :byte
10.533+ (0 11) (1 15) (2 16)))
10.534+ (nmunits1 (ecase range ; number of :numeric units before more :byte
10.535+ (0 6) (1 8) (2 9)))
10.536+ (nmunits2 (ecase range ; number of :numeric units before more :alnum
10.537+ (0 6) (1 7) (2 8)))
10.538+ (switch-mode nil))
10.539+ (multiple-value-bind (nmatches last-mode)
10.540+ (nunits-matches (nthcdr cur-byte bytes) :kanji)
10.541+ (and (>= nmatches nkunits) (eq last-mode :byte)
10.542+ (setf switch-mode :kanji)))
10.543+ (unless switch-mode
10.544+ (multiple-value-bind (nmatches last-mode)
10.545+ (nunits-matches (nthcdr cur-byte bytes) :alnum)
10.546+ (and (>= nmatches nanuits) (eq last-mode :byte)
10.547+ (setf switch-mode :alnum))))
10.548+ (unless switch-mode
10.549+ (multiple-value-bind (nmatches last-mode)
10.550+ (nunits-matches (nthcdr cur-byte bytes) :numeric)
10.551+ (case last-mode
10.552+ (:byte (and (>= nmatches nmunits1)
10.553+ (setf switch-mode :numeric)))
10.554+ (:alnum (and (>= nmatches nmunits2)
10.555+ (setf switch-mode :numeric))))))
10.556+ (if switch-mode
10.557+ (progn
10.558+ ;; current segment finished, add a new SWITCH-MODE segment
10.559+ (setf segments (append segments (list seg)))
10.560+ (setf seg (list switch-mode)))
10.561+ (setf switch-mode :byte))
10.562+ (funcall (mode-analyse-func switch-mode) input seg))))
10.563+
10.564+(defmethod analyse-alnum-mode ((input qr-input) &optional (seg '(:alnum)))
10.565+ (declare (type list seg))
10.566+ (setf seg (append-cur-byte input seg))
10.567+ (unless seg
10.568+ (return-from analyse-alnum-mode))
10.569+ (with-slots (bytes cur-byte version segments) input
10.570+ (let ((nmunits (ecase (version-range version)
10.571+ (0 13) (1 15) (2 17)))
10.572+ (switch-mode nil))
10.573+ (when (>= (nunits-matches (nthcdr cur-byte bytes) :kanji) 1)
10.574+ (setf switch-mode :kanji))
10.575+ (unless switch-mode
10.576+ (when (>= (nunits-matches (nthcdr cur-byte bytes) :byte) 1)
10.577+ (setf switch-mode :byte)))
10.578+ (unless switch-mode
10.579+ (multiple-value-bind (nmatches last-mode)
10.580+ (nunits-matches (nthcdr cur-byte bytes) :numeric)
10.581+ (and (>= nmatches nmunits) (eq last-mode :alnum)
10.582+ (setf switch-mode :numeric))))
10.583+ (if switch-mode
10.584+ (progn
10.585+ (setf segments (append segments (list seg)))
10.586+ (setf seg (list switch-mode)))
10.587+ (setf switch-mode :alnum))
10.588+ (funcall (mode-analyse-func switch-mode) input seg))))
10.589+
10.590+(defmethod analyse-numeric-mode ((input qr-input) &optional (seg '(:numeric)))
10.591+ (declare (type list seg))
10.592+ (setf seg (append-cur-byte input seg))
10.593+ (unless seg
10.594+ (return-from analyse-numeric-mode))
10.595+ (with-slots (bytes cur-byte version segments) input
10.596+ (let ((switch-mode nil))
10.597+ (when (>= (nunits-matches (nthcdr cur-byte bytes) :kanji) 1)
10.598+ (setf switch-mode :kanji))
10.599+ (unless switch-mode
10.600+ (when (>= (nunits-matches (nthcdr cur-byte bytes) :byte) 1)
10.601+ (setf switch-mode :byte)))
10.602+ (unless switch-mode
10.603+ (when (>= (nunits-matches (nthcdr cur-byte bytes) :alnum) 1)
10.604+ (setf switch-mode :alnum)))
10.605+ (if switch-mode
10.606+ (progn
10.607+ (setf segments (append segments (list seg)))
10.608+ (setf seg (list switch-mode)))
10.609+ (setf switch-mode :numeric))
10.610+ (funcall (mode-analyse-func switch-mode) input seg))))
10.611+
10.612+(defmethod append-cur-byte ((input qr-input) &optional seg)
10.613+ "if CUR-BYTE is the last byte, return nil"
10.614+ (declare (type list seg))
10.615+ (with-slots (bytes cur-byte segments) input
10.616+ (setf seg (append seg (list (nth cur-byte bytes))))
10.617+ (incf cur-byte)
10.618+ (when (>= cur-byte (length bytes))
10.619+ (setf segments (append segments (list seg)))
10.620+ (setf seg nil))
10.621+ (return-from append-cur-byte seg)))
10.622+
10.623+(defmethod analyse-kanji-mode ((input qr-input) &optional (seg '(:kanji)))
10.624+ (declare (type list seg))
10.625+ (with-slots (bytes cur-byte segments) input
10.626+ (setf seg (append seg (nthcdr cur-byte bytes)))
10.627+ (setf cur-byte (length bytes))
10.628+ (setf segments (append segments (list seg)))))
10.629+
10.630+(defmethod validate-and-analysis ((input qr-input))
10.631+ (with-slots ((level ec-level) segments) input
10.632+ (unless (<= 1 (version input) 40)
10.633+ (error "version ~A out of bounds" (version input)))
10.634+ (do ((prev -1))
10.635+ ((<= (version input) prev))
10.636+ (setf prev (version input))
10.637+ (redo-data-analysis input)
10.638+ (labels ((seg-bstream-len (seg)
10.639+ (segment-bstream-length seg (version input))))
10.640+ (let* ((blen (reduce #'+ (mapcar #'seg-bstream-len segments)
10.641+ :initial-value 0))
10.642+ (min-v (minimum-version prev (ceiling blen 8) level)))
10.643+ (if min-v
10.644+ (setf (slot-value input 'version) min-v)
10.645+ (error "no version to hold ~A bytes" (ceiling blen 8))))))))
10.646+
10.647+(defmethod data-encoding ((input qr-input))
10.648+ (with-slots (version (level ec-level) segments) input
10.649+ (labels ((seg->bstream (seg)
10.650+ (segment->bstream seg version)))
10.651+ (let* ((bs (reduce #'append (mapcar #'seg->bstream segments)
10.652+ :initial-value nil))
10.653+ (tt (terminator bs version level))
10.654+ ;; connect bit streams in all segment, with terminator appended
10.655+ (bstream (append bs tt)))
10.656+ ;; add padding bits
10.657+ (setf bstream (append bstream (padding-bits bstream)))
10.658+ ;; add pad codewords, finishes data encoding
10.659+ (setf (slot-value input 'bstream)
10.660+ (append bstream
10.661+ (pad-codewords bstream version level)))))))
10.662+
10.663+(defmethod ec-coding ((input qr-input))
10.664+ (with-slots (version (level ec-level) bstream) input
10.665+ (let ((codewords (bstream->codewords bstream))
10.666+ (blocks nil)
10.667+ (ecc-blocks nil)
10.668+ ;; RS error correction obj for blk1 & blk2
10.669+ (rs1 nil)
10.670+ (rs2 nil))
10.671+ (multiple-value-bind (ecc-num blk1 data1 blk2 data2)
10.672+ (ecc-block-nums version level)
10.673+ (when (> blk1 0)
10.674+ (setf rs1 (make-instance 'rs-ecc :k data1 :ec ecc-num)))
10.675+ (when (> blk2 0)
10.676+ (setf rs2 (make-instance 'rs-ecc :k data2 :ec ecc-num)))
10.677+ (dotimes (i blk1)
10.678+ (setf blocks
10.679+ (append blocks (list (subseq codewords 0 data1))))
10.680+ (setf codewords (nthcdr data1 codewords)))
10.681+ (dotimes (i blk2)
10.682+ (setf blocks
10.683+ (append blocks (list (subseq codewords 0 data2))))
10.684+ (setf codewords (nthcdr data2 codewords)))
10.685+ (dotimes (i blk1)
10.686+ (setf ecc-blocks
10.687+ (append ecc-blocks (list (ecc-poly rs1 (nth i blocks))))))
10.688+ (dotimes (i blk2)
10.689+ (setf ecc-blocks
10.690+ (append ecc-blocks (list (ecc-poly rs2 (nth (+ i blk1) blocks))))))
10.691+ (setf (slot-value input 'blocks) blocks)
10.692+ (setf (slot-value input 'ecc-blocks) ecc-blocks)))))
10.693+
10.694+(defmethod structure-message ((input qr-input))
10.695+ (with-slots (version (level ec-level) blocks ecc-blocks) input
10.696+ (let ((final nil))
10.697+ (multiple-value-bind (ecc-num blk1 data1 blk2 data2)
10.698+ (ecc-block-nums version level)
10.699+ (declare (ignore ecc-num))
10.700+ (setf (slot-value input 'msg-codewords)
10.701+ (append final
10.702+ ;; interleave data blocks, data blocks may differ in length
10.703+ (take-data-in-turn blocks blk1 data1 blk2 data2)
10.704+ ;; we know error correction blocks are of the same length
10.705+ (take-in-turn ecc-blocks)))))))
10.706+
10.707+(defmethod module-placement ((input qr-input))
10.708+ (setf (matrix input) (make-matrix (version input)))
10.709+ (with-slots (version msg-codewords matrix) input
10.710+ ;; Function pattern placement
10.711+ (function-patterns matrix version)
10.712+ ;; Symbol character placement
10.713+ (let ((rbits (remainder-bits version))
10.714+ (bstream nil))
10.715+ (labels ((dec->byte (codeword)
10.716+ (decimal->bstream codeword 8)))
10.717+ (setf bstream (append (reduce #'append (mapcar #'dec->byte msg-codewords))
10.718+ ;; data capacity of _symbol_ does not divide by 8
10.719+ (make-list rbits :initial-element 0))))
10.720+ (symbol-character bstream matrix version))))
10.721+
10.722+(defmethod data-masking ((input qr-input))
10.723+ "(masked matrix, mask pattern reference)"
10.724+ (with-slots (version (level ec-level) matrix) input
10.725+ (let ((modules (matrix-modules version)))
10.726+ (multiple-value-bind (masked indicator)
10.727+ (choose-masking matrix modules level)
10.728+ (values masked (mask-pattern-ref indicator))))))
10.729+
10.730+(defun decimal->bstream (dec nbits)
10.731+ "using NBITS bits to encode decimal DEC"
10.732+ (let ((bstream nil))
10.733+ (dotimes (i nbits)
10.734+ (if (logbitp i dec)
10.735+ (push 1 bstream)
10.736+ (push 0 bstream)))
10.737+ bstream))
10.738+(defun bstream->decimal (bstream nbits)
10.739+ (declare (type list bstream))
10.740+ (let ((nbits (min nbits (length bstream)))
10.741+ (dec 0))
10.742+ (dotimes (i nbits)
10.743+ (setf dec (+ (* dec 2) (nth i bstream))))
10.744+ dec))
10.745+
10.746+;;; :numeric mode
10.747+(defun group->decimal (values ndigits)
10.748+ "digit groups of length NDIGITS (1, 2 or 3) to decimal"
10.749+ (declare (type list values))
10.750+ (case ndigits
10.751+ (1 (nth 0 values))
10.752+ (2 (+ (* (nth 0 values) 10) (nth 1 values)))
10.753+ (3 (+ (* (nth 0 values) 100) (* (nth 1 values) 10) (nth 2 values)))))
10.754+(defun final-digit-bits (n)
10.755+ "the final one or two digits are converted to 4 or 7 bits respectively"
10.756+ (case n
10.757+ (0 0) (1 4) (2 7)))
10.758+(defun numeric->bstream (bytes)
10.759+ (declare (type list bytes))
10.760+ (labels ((num-value (byte)
10.761+ (byte-value :numeric byte)))
10.762+ (let ((values (mapcar #'num-value bytes))
10.763+ (bstream nil))
10.764+ (do ((v values (nthcdr 3 v)))
10.765+ ((null v) bstream)
10.766+ (case (length v)
10.767+ (1 ; only 1 digits left
10.768+ (setf bstream
10.769+ (append bstream (decimal->bstream (group->decimal v 1)
10.770+ (final-digit-bits 1)))))
10.771+ (2 ; only 2 digits left
10.772+ (setf bstream
10.773+ (append bstream (decimal->bstream (group->decimal v 2)
10.774+ (final-digit-bits 2)))))
10.775+ (otherwise ; at least 3 digits left
10.776+ (setf bstream
10.777+ (append bstream
10.778+ (decimal->bstream (group->decimal v 3) 10)))))))))
10.779+
10.780+;;; :alnum mode
10.781+(defun pair->decimal (values num)
10.782+ "alnum pairs of length NUM (1 or 2) to decimal"
10.783+ (declare (type list values))
10.784+ (case num
10.785+ (1 (nth 0 values))
10.786+ (2 (+ (* (nth 0 values) 45) (nth 1 values)))))
10.787+(defun alnum->bstream (bytes)
10.788+ (declare (type list bytes))
10.789+ (labels ((alnum-value (byte)
10.790+ (byte-value :alnum byte)))
10.791+ (let ((values (mapcar #'alnum-value bytes))
10.792+ (bstream nil))
10.793+ (do ((v values (nthcdr 2 v)))
10.794+ ((null v) bstream)
10.795+ (case (length v)
10.796+ (1 ; only 1 alnum left
10.797+ (setf bstream
10.798+ (append bstream
10.799+ (decimal->bstream (pair->decimal v 1) 6))))
10.800+ (otherwise ; at least 2 alnum left
10.801+ (setf bstream
10.802+ (append bstream
10.803+ (decimal->bstream (pair->decimal v 2) 11)))))))))
10.804+
10.805+;;; :byte mode
10.806+(defun byte->bstream (bytes)
10.807+ (declare (type list bytes))
10.808+ (labels ((join (prev cur)
10.809+ (append prev (decimal->bstream (byte-value :byte cur) 8))))
10.810+ (reduce #'join bytes :initial-value nil)))
10.811+
10.812+;;; :kanji mode
10.813+(defun kanji->decimal (word range)
10.814+ (let ((subtractor (ecase range
10.815+ (0 #x8140)
10.816+ (1 #xc140))))
10.817+ (decf word subtractor)
10.818+ (setf word (+ (* (ash word -8) #xc0)
10.819+ (boole boole-and word #xff)))))
10.820+(defun kanji->bstream (bytes)
10.821+ (declare (type list bytes))
10.822+ (labels ((kanji-value (byte)
10.823+ (byte-value :kanji byte)))
10.824+ (let ((values (mapcar #'kanji-value bytes))
10.825+ (delta 1)
10.826+ (bstream nil))
10.827+ (do ((v values (nthcdr delta v)))
10.828+ ((null v) bstream)
10.829+ (case (length v)
10.830+ (1 ; only 1 byte left
10.831+ (setf bstream
10.832+ (append bstream (decimal->bstream (car v) 13)))
10.833+ (setf delta 1))
10.834+ (otherwise ; at least 2 bytes left
10.835+ (multiple-value-bind (kanji-p word range) (starts-kanji-p v)
10.836+ (if kanji-p
10.837+ (progn
10.838+ (setf bstream
10.839+ (append bstream
10.840+ (decimal->bstream (kanji->decimal word range)
10.841+ 13)))
10.842+ (setf delta 2))
10.843+ (progn
10.844+ (setf bstream
10.845+ (append bstream (decimal->bstream (car v) 13)))
10.846+ (setf delta 1))))))))))
10.847+
10.848+;;; :eci mode
10.849+(defun eci->bstream (bytes)
10.850+ "TODO"
10.851+ (declare (ignore bytes))
10.852+ (error "eci->bstream: TODO..."))
10.853+
10.854+(defun bstream-trans-func (mode)
10.855+ (case mode
10.856+ (:numeric #'numeric->bstream)
10.857+ (:alnum #'alnum->bstream)
10.858+ (:byte #'byte->bstream)
10.859+ (:kanji #'kanji->bstream)))
10.860+
10.861+(defun kanji-bytes-length (bytes)
10.862+ (declare (type list bytes))
10.863+ (let ((step 1)
10.864+ (len 0))
10.865+ (do ((b bytes (nthcdr step b)))
10.866+ ((null b) len)
10.867+ (if (starts-kanji-p b)
10.868+ (setf step 2)
10.869+ (setf step 1))
10.870+ (incf len))))
10.871+
10.872+(defun bytes-length (bytes mode)
10.873+ "number of data characters under MODE"
10.874+ (declare (type list bytes) (type qr-mode mode))
10.875+ (case mode
10.876+ ((:numeric :alnum :byte) (length bytes))
10.877+ (:kanji (kanji-bytes-length bytes))))
10.878+
10.879+(defun segment-bstream-length (segment version)
10.880+ "bit stream length of SEGMENT (:mode b0 b1 ...) under VERSION"
10.881+ (declare (type list segment))
10.882+ (let* ((mode (car segment))
10.883+ (bytes (cdr segment))
10.884+ (m 4)
10.885+ (c (char-count-bits version mode))
10.886+ (d (bytes-length bytes mode))
10.887+ (r 0))
10.888+ ;; M = number of bits in mode indicator
10.889+ ;; C = number of bits in character count indicator
10.890+ ;; D = number of input data characters
10.891+ (case mode
10.892+ (:numeric
10.893+ (setf r (final-digit-bits (mod d 3)))
10.894+ ;; B = M + C + 10 * (D / 3) + R
10.895+ (+ m c (* 10 (floor d 3)) r))
10.896+ (:alnum
10.897+ (setf r (mod d 2))
10.898+ ;; B = M + C + 11 * (D / 2) + 6 * (D % 2)
10.899+ (+ m c (* 11 (floor d 2)) (* 6 r)))
10.900+ (:byte
10.901+ ;; B = M + C + 8 * D
10.902+ (+ m c (* 8 d)))
10.903+ (:kanji
10.904+ ;; B = M + C + 13 * D
10.905+ (+ m c (* 13 d))))))
10.906+
10.907+(defun segment->bstream (segment version)
10.908+ "SEGMENT (:mode b0 b1 ...) to bit stream under VERSION"
10.909+ (declare (type list segment))
10.910+ (let* ((mode (car segment))
10.911+ (bytes (cdr segment))
10.912+ (len (bytes-length bytes mode))
10.913+ (n (char-count-bits version mode))
10.914+ (bstream nil))
10.915+ (append bstream (mode-indicator mode)
10.916+ (decimal->bstream len n) ; character count indicator
10.917+ (funcall (bstream-trans-func mode) bytes))))
10.918+
10.919+(defun padding-bits (bstream)
10.920+ "add padding bits so that BSTREAM ends at a codeword boundary"
10.921+ (multiple-value-bind (quot rem) (ceiling (length bstream) 8)
10.922+ (declare (ignore quot))
10.923+ (make-list (- rem) :initial-element 0)))
10.924+
10.925+(defun pad-codewords (bstream version level)
10.926+ "add pad codewords (after adding padding-bits) to fill data codeword capacity"
10.927+ (let ((pad-words '((1 1 1 0 1 1 0 0)
10.928+ (0 0 0 1 0 0 0 1)))
10.929+ (pad-len (- (data-words-capacity version level)
10.930+ (/ (length bstream) 8)))
10.931+ (ret nil))
10.932+ (dotimes (i pad-len)
10.933+ (setf ret (append ret (nth (mod i 2) pad-words))))
10.934+ ret))
10.935+
10.936+(defun bstream->codewords (bstream)
10.937+ "convert bstream into codewords, as coefficients of the terms of a polynomial"
10.938+ (do ((b bstream (nthcdr 8 b))
10.939+ (codewords nil))
10.940+ ((null b) codewords)
10.941+ (setf codewords (append codewords (list (bstream->decimal b 8))))))
10.942+
10.943+(defun take-in-turn (blks)
10.944+ "taking codewords from each block (bound by minimum length) in turn"
10.945+ (reduce #'append (apply #'mapcar #'list blks)))
10.946+
10.947+(defun take-data-in-turn (blocks blk1 data1 blk2 data2)
10.948+ "taking data words from each block (might have different length) in turn"
10.949+ (let ((data-final nil)
10.950+ (left-blks nil))
10.951+ (setf data-final (take-in-turn blocks))
10.952+ (cond
10.953+ ((or (= blk1 0) (= blk2 0))
10.954+ ;; only one kind of block exists
10.955+ (setf left-blks nil))
10.956+ ((> data1 data2)
10.957+ ;; block 1 has more elements left
10.958+ (setf left-blks (mapcar #'(lambda (blk)
10.959+ (nthcdr data2 blk))
10.960+ (subseq blocks 0 blk1))))
10.961+ ((> data2 data1)
10.962+ ;; block 2 has more elements left
10.963+ (setf left-blks (mapcar #'(lambda (blk)
10.964+ (nthcdr data1 blk))
10.965+ (subseq blocks blk1 (+ blk1 blk2))))))
10.966+ (if left-blks
10.967+ (append data-final (take-in-turn left-blks))
10.968+ data-final)))
10.969+
10.970+(deftype module-color ()
10.971+ ":RAW, nothing has been done to this module; :RESERVE, format info reserve module
10.972+:FLIGHT/:FDARK, function pattern light/dark module; :LIGHT/:DARK, data modules"
10.973+ '(member :raw :flight :fdark :reserve :light :dark))
10.974+
10.975+(defun same-color-p (color1 color2)
10.976+ "during QR symbol evaluation, :fdark & :dark are considered to be same"
10.977+ (case color1
10.978+ ((:flight :light) (or (eq color2 :flight) (eq color2 :light)))
10.979+ ((:fdark :dark) (or (eq color2 :fdark) (eq color2 :fdark)))
10.980+ (otherwise (eq color1 color2))))
10.981+
10.982+(defun raw-module-p (matrix i j)
10.983+ "nothing has been done to MATRIX[I, J]"
10.984+ (eq (aref matrix i j) :raw))
10.985+
10.986+(defun make-modules-matrix (modules &optional (init :raw))
10.987+ "make a raw matrix with MODULES * MODULES elements"
10.988+ (make-array `(,modules ,modules) :initial-element init))
10.989+
10.990+(defun make-matrix (version &optional (init :raw))
10.991+ "make a raw matrix according to VERSION"
10.992+ (let ((n (matrix-modules version)))
10.993+ (make-modules-matrix n init)))
10.994+
10.995+(defun paint-square (matrix x y n &optional (color :fdark))
10.996+ "Paint a square of size N*N starting from upleft (X, Y) in MATRIX to COLOR"
10.997+ (let ((maxx (+ x n -1))
10.998+ (maxy (+ y n -1)))
10.999+ (loop for i from x to maxx do
10.1000+ (loop for j from y to maxy do
10.1001+ (setf (aref matrix i j) color))))
10.1002+ matrix)
10.1003+
10.1004+;;; Function Patterns
10.1005+(defun function-patterns (matrix version)
10.1006+ (let ((modules (matrix-modules version)))
10.1007+ (finder-patterns matrix modules)
10.1008+ (separator matrix modules)
10.1009+ (timing-patterns matrix modules)
10.1010+ (alignment-patterns matrix version))
10.1011+ matrix)
10.1012+;; a) Finder Patterns: fixed position in matrix
10.1013+(defun one-finder-pattern (matrix x y)
10.1014+ "Paint one finder pattern starting from upleft (X, Y)"
10.1015+ (paint-square matrix x y 7 :fdark)
10.1016+ (paint-square matrix (+ x 1) (+ y 1) 5 :flight)
10.1017+ (paint-square matrix (+ x 2) (+ y 2) 3 :fdark))
10.1018+(defun finder-patterns (matrix modules)
10.1019+ ;; top-left finder pattern
10.1020+ (one-finder-pattern matrix 0 0)
10.1021+ ;; top-right finder pattern
10.1022+ (one-finder-pattern matrix (- modules 7) 0)
10.1023+ ;; bottom-left finder pattern
10.1024+ (one-finder-pattern matrix 0 (- modules 7)))
10.1025+
10.1026+;; b) Separator: fixed position in matrix
10.1027+(defun separator (matrix modules)
10.1028+ (dotimes (j 8)
10.1029+ ;; top-left horizontal separator
10.1030+ (setf (aref matrix 7 j) :flight)
10.1031+ ;; top-right horizontal separator
10.1032+ (setf (aref matrix 7 (- modules j 1)) :flight)
10.1033+ ;; bottom-left horizontal separator
10.1034+ (setf (aref matrix (- modules 8) j) :flight))
10.1035+ (dotimes (i 8)
10.1036+ ;; top-left vertical separator
10.1037+ (setf (aref matrix i 7) :flight)
10.1038+ ;; bottom-left vertical separator
10.1039+ (setf (aref matrix (- modules i 1) 7) :flight)
10.1040+ ;; top-right vertical separator
10.1041+ (setf (aref matrix i (- modules 8)) :flight))
10.1042+ matrix)
10.1043+
10.1044+;; c) Timing patterns
10.1045+(defun timing-patterns (matrix modules)
10.1046+ (let ((color :fdark))
10.1047+ (loop for idx from 8 to (- modules 9) do
10.1048+ (if (evenp idx)
10.1049+ (setf color :fdark)
10.1050+ (setf color :flight))
10.1051+ ;; Horizontal
10.1052+ (setf (aref matrix 6 idx) color)
10.1053+ ;; Vertical
10.1054+ (setf (aref matrix idx 6) color)))
10.1055+ matrix)
10.1056+
10.1057+;; d) Alignment Patterns: varies between versions
10.1058+;; may overlap timing patterns, modules coincide with that of timing patterns
10.1059+(defun one-align-pattern (matrix x y)
10.1060+ "Paint one alignment pattern centered at (X, Y)"
10.1061+ (paint-square matrix (- x 2) (- y 2) 5 :fdark)
10.1062+ (paint-square matrix (- x 1) (- y 1) 3 :flight)
10.1063+ (paint-square matrix x y 1 :fdark))
10.1064+(defun alignment-patterns (matrix version)
10.1065+ (dolist (center (align-centers version) matrix)
10.1066+ (one-align-pattern matrix (first center) (second center))))
10.1067+
10.1068+;;; Encoding Region
10.1069+(defun symbol-character (bstream matrix version)
10.1070+ (let ((modules (matrix-modules version)))
10.1071+ (reserve-information matrix version)
10.1072+ (bstream-placement bstream matrix modules))
10.1073+ matrix)
10.1074+;; reserve format information & version information
10.1075+(defun reserve-information (matrix version)
10.1076+ (let ((modules (matrix-modules version)))
10.1077+ ;; format information...
10.1078+ ;; top-left & top-right horizontal
10.1079+ (dotimes (j 8)
10.1080+ (when (raw-module-p matrix 8 j)
10.1081+ (setf (aref matrix 8 j) :reserve))
10.1082+ (setf (aref matrix 8 (- modules j 1)) :reserve))
10.1083+ (setf (aref matrix 8 8) :reserve)
10.1084+ ;; top-left & bottom-left vertical
10.1085+ (dotimes (i 8)
10.1086+ (when (raw-module-p matrix i 8)
10.1087+ (setf (aref matrix i 8) :reserve))
10.1088+ (setf (aref matrix (- modules i 1) 8) :reserve))
10.1089+ ;; dark module...
10.1090+ (setf (aref matrix (- modules 8) 8) :fdark)
10.1091+
10.1092+ ;; version information for version 7-40
10.1093+ (when (>= version 7)
10.1094+ (version-information matrix modules version))))
10.1095+
10.1096+(defun paint-fcolor-bit (matrix i j bit)
10.1097+ "Paint function pattern color for MATRIX[I, J] according to BIT of {0, 1}"
10.1098+ (setf (aref matrix i j) (case bit
10.1099+ (0 :flight) (1 :fdark))))
10.1100+(defun version-information (matrix modules version)
10.1101+ "version information placement on two blocks of modules:
10.1102+bottom-left 3*6 block: [modules-11, modules-9] * [0, 5]
10.1103+top-right 6*3 block: [0, 5] * [modules-11, modules-9]"
10.1104+ (assert (>= version 7))
10.1105+ (let ((vib (version-ecc version))
10.1106+ (i (- modules 9))
10.1107+ (start (- modules 9))
10.1108+ (bound (- modules 11))
10.1109+ (j 5))
10.1110+ (dolist (bit vib matrix)
10.1111+ (paint-fcolor-bit matrix i j bit)
10.1112+ (paint-fcolor-bit matrix j i bit)
10.1113+ (if (>= (- i 1) bound)
10.1114+ (decf i)
10.1115+ (progn
10.1116+ (decf j)
10.1117+ (setf i start))))))
10.1118+
10.1119+;; Symbol character placement
10.1120+(defun paint-color-bit (matrix i j bit)
10.1121+ "Paint data color for MATRIX[I, J] according to BIT of {0, 1}"
10.1122+ (setf (aref matrix i j) (case bit
10.1123+ (0 :light) (1 :dark))))
10.1124+(defun bstream-placement (bstream matrix modules)
10.1125+ "2X4 module block for a regular symbol character. Regard the interleaved
10.1126+codeword sequence as a single bit stream, which is placed in the two module
10.1127+wide columns, alternately in the right and left modules, moving upwards or
10.1128+downwards according to DIRECTION, skipping function patterns, changing DIRECTION
10.1129+at the top or bottom of the symbol. The only exception is that no block should
10.1130+ever overlap the vertical timing pattern."
10.1131+ (let ((i (- modules 1))
10.1132+ (j (- modules 1))
10.1133+ ;; -1: upwards, +1: downwards
10.1134+ (direction -1)
10.1135+ (len (length bstream)))
10.1136+ (do ((idx 0))
10.1137+ ((>= idx len) matrix)
10.1138+ (when (raw-module-p matrix i j)
10.1139+ (paint-color-bit matrix i j (nth idx bstream))
10.1140+ (incf idx))
10.1141+ (when (and (>= (- j 1) 0)
10.1142+ (raw-module-p matrix i (- j 1)))
10.1143+ ;; try left module
10.1144+ (paint-color-bit matrix i (- j 1) (nth idx bstream))
10.1145+ (incf idx))
10.1146+ (if (< -1 (+ i direction) modules)
10.1147+ (incf i direction)
10.1148+ (progn
10.1149+ ;; reverse direction
10.1150+ (setf direction (- direction))
10.1151+ (if (= j 8)
10.1152+ ;; vertical timing pattern reached, the next block starts
10.1153+ ;; to the left of it
10.1154+ (decf j 3)
10.1155+ (decf j 2)))))))
10.1156+
10.1157+;;; format information, during and after masking
10.1158+(defun format-information (matrix modules level mask-ind)
10.1159+ ;; format information bistream
10.1160+ (let ((fib (format-ecc level mask-ind))
10.1161+ (darks 0)
10.1162+ (idx 0)
10.1163+ (idx2 0))
10.1164+ (setf darks (count-if #'(lambda (elem) (= elem 1)) fib))
10.1165+ ;; horizontal 14 ~ 8
10.1166+ (loop for j from 0 to 7 do
10.1167+ (when (eq (aref matrix 8 j) :reserve)
10.1168+ (paint-fcolor-bit matrix 8 j (nth idx fib))
10.1169+ (incf idx)))
10.1170+ ;; vertical 14 ~ 8
10.1171+ (loop for i from (- modules 1) downto (- modules 7) do
10.1172+ (paint-fcolor-bit matrix i 8 (nth idx2 fib))
10.1173+ (incf idx2))
10.1174+ ;; horizontal 7 - 0
10.1175+ (loop for j from (- modules 8) to (- modules 1) do
10.1176+ (paint-fcolor-bit matrix 8 j (nth idx fib))
10.1177+ (incf idx))
10.1178+ ;; vertical 7 - 0
10.1179+ (loop for i from 8 downto 0 do
10.1180+ (when (eq (aref matrix i 8) :reserve)
10.1181+ (paint-fcolor-bit matrix i 8 (nth idx2 fib))
10.1182+ (incf idx2)))
10.1183+ (values matrix darks)))
10.1184+
10.1185+;;; only encoding region modules (excluding format information) are masked
10.1186+(defun encoding-module-p (matrix i j)
10.1187+ "modules belong to encoding region, excluding format & version information"
10.1188+ (or (eq (aref matrix i j) :light)
10.1189+ (eq (aref matrix i j) :dark)))
10.1190+(defun non-mask-module-p (matrix i j)
10.1191+ (not (encoding-module-p matrix i j)))
10.1192+(defun reverse-module-color (matrix i j)
10.1193+ (case (aref matrix i j)
10.1194+ (:dark :light) (:light :dark)))
10.1195+
10.1196+;;; all modules are evaluated:
10.1197+;;; there should be only :dark :light :fdark :flight modules left by now
10.1198+(defun dark-module-p (matrix i j)
10.1199+ (or (eq (aref matrix i j) :fdark)
10.1200+ (eq (aref matrix i j) :dark)))
10.1201+
10.1202+(defun copy-and-mask (matrix modules level mask-ind)
10.1203+ "make a new matrix and mask using MASK-IND for later evaluation"
10.1204+ (let ((ret (make-modules-matrix modules))
10.1205+ (mask-p (mask-condition mask-ind))
10.1206+ (darks 0))
10.1207+ (dotimes (i modules)
10.1208+ (dotimes (j modules)
10.1209+ (cond
10.1210+ ((non-mask-module-p matrix i j)
10.1211+ (setf (aref ret i j) (aref matrix i j)))
10.1212+ ((funcall mask-p i j) ; need mask
10.1213+ (setf (aref ret i j) (reverse-module-color matrix i j)))
10.1214+ (t
10.1215+ (setf (aref ret i j) (aref matrix i j))))
10.1216+ (when (dark-module-p ret i j)
10.1217+ (incf darks))))
10.1218+ (multiple-value-bind (dummy fi-darks)
10.1219+ (format-information ret modules level mask-ind)
10.1220+ (declare (ignore dummy))
10.1221+ ;; add format information dark modules
10.1222+ (values ret (+ darks fi-darks)))))
10.1223+
10.1224+(defun mask-matrix (matrix modules level mask-ind)
10.1225+ "do not evaluate, just go ahead and mask MATRIX using MASK-IND mask pattern"
10.1226+ (let ((mask-p (mask-condition mask-ind)))
10.1227+ (dotimes (i modules)
10.1228+ (dotimes (j modules)
10.1229+ (and (encoding-module-p matrix i j)
10.1230+ (funcall mask-p i j)
10.1231+ (setf (aref matrix i j) (reverse-module-color matrix i j)))))
10.1232+ ;; paint format information
10.1233+ (format-information matrix modules level mask-ind)
10.1234+ matrix))
10.1235+
10.1236+(defun choose-masking (matrix modules level)
10.1237+ "mask and evaluate using each mask pattern, choose the best mask result"
10.1238+ (let ((n4 10)
10.1239+ (best-matrix nil)
10.1240+ (mask-indicator nil)
10.1241+ (min-penalty nil)
10.1242+ (square (* modules modules))
10.1243+ (cur-penalty 0))
10.1244+ (dotimes (i *mask-pattern-num*)
10.1245+ (multiple-value-bind (cur-matrix darks)
10.1246+ (copy-and-mask matrix modules level i)
10.1247+ ;; feature 4: proportion of dark modules in entire symbol
10.1248+ (let ((bratio (/ (+ (* darks 200) square) square 2)))
10.1249+ (setf cur-penalty (* (/ (abs (- bratio 50)) 5) n4)))
10.1250+ (incf cur-penalty (evaluate-feature-123 cur-matrix modules))
10.1251+ (when (or (null min-penalty)
10.1252+ (< cur-penalty min-penalty))
10.1253+ (setf min-penalty cur-penalty
10.1254+ mask-indicator i
10.1255+ best-matrix cur-matrix))))
10.1256+ (values best-matrix mask-indicator)))
10.1257+
10.1258+;;; feature 1 & 2 & 3
10.1259+(defun evaluate-feature-123 (matrix modules)
10.1260+ (let ((penalty 0))
10.1261+ (incf penalty (evaluate-feature-2 matrix modules))
10.1262+ (dotimes (col modules)
10.1263+ (let ((rlength (calc-run-length matrix modules col)))
10.1264+ (incf penalty (evaluate-feature-1 rlength))
10.1265+ (incf penalty (evaluate-feature-3 rlength))))
10.1266+ (dotimes (row modules)
10.1267+ (let ((rlength (calc-run-length matrix modules row :col)))
10.1268+ (incf penalty (evaluate-feature-1 rlength))
10.1269+ (incf penalty (evaluate-feature-3 rlength))))
10.1270+ penalty))
10.1271+
10.1272+(defun calc-run-length (matrix modules num &optional (direction :row))
10.1273+ "list of number of adjacent modules in same color"
10.1274+ (let ((rlength nil)
10.1275+ (ridx 0))
10.1276+ (labels ((get-elem (idx)
10.1277+ (case direction
10.1278+ (:row (aref matrix num idx))
10.1279+ (:col (aref matrix idx num))))
10.1280+ (add-to-list (list elem)
10.1281+ (append list (list elem))))
10.1282+ ;; we make sure (NTH 1 rlength) is for dark module
10.1283+ (when (same-color-p (get-elem 0) :dark)
10.1284+ (setf rlength (add-to-list rlength -1)
10.1285+ ridx 1))
10.1286+ (setf rlength (add-to-list rlength 1))
10.1287+
10.1288+ (loop for i from 1 to (- modules 1) do
10.1289+ (if (same-color-p (get-elem i) (get-elem (- i 1)))
10.1290+ (incf (nth ridx rlength))
10.1291+ (progn
10.1292+ (incf ridx)
10.1293+ (setf rlength (add-to-list rlength 1)))))
10.1294+ rlength)))
10.1295+
10.1296+(defun evaluate-feature-1 (rlength)
10.1297+ "(5 + i) adjacent modules in row/column in same color. (N1 + i) points, N1 = 3"
10.1298+ (let ((n1 3)
10.1299+ (penalty 0))
10.1300+ (dolist (sz rlength penalty)
10.1301+ (when (> sz 5)
10.1302+ (incf penalty (+ n1 sz -5))))))
10.1303+
10.1304+(defun evaluate-feature-3 (rlength)
10.1305+ "1:1:3:1:1 ration (dark:light:dark:light:dark) pattern in row/column,
10.1306+preceded or followed by light area 4 modules wide. N3 points, N3 = 40"
10.1307+ (let ((n3 40)
10.1308+ (len (length rlength))
10.1309+ (penalty 0))
10.1310+ (do ((i 3 (+ i 2)))
10.1311+ ((>= i (- len 2)) penalty)
10.1312+ (when (and (= (mod i 2) 1) ; for dark module
10.1313+ (= (mod (nth i rlength) 3) 0)
10.1314+ (let ((fact (floor (nth i rlength) 3)))
10.1315+ ;; 1:1:3:1:1
10.1316+ (when (= fact
10.1317+ (nth (- i 2) rlength)
10.1318+ (nth (- i 1) rlength)
10.1319+ (nth (+ i 1) rlength)
10.1320+ (nth (+ i 2) rlength))
10.1321+ (cond
10.1322+ ((<= (- i 3) 0) (incf penalty n3))
10.1323+ ((>= (+ i 4) len) (incf penalty n3))
10.1324+ ((>= (nth (- i 3) rlength) (* 4 fact)) (incf penalty n3))
10.1325+ ((>= (nth (+ i 3) rlength) (* 4 fact)) (incf penalty n3))))))))))
10.1326+
10.1327+(defun evaluate-feature-2 (matrix modules)
10.1328+ "block m * n of modules in same color. N2 * (m-1) * (n-1) points, N2=3"
10.1329+ (let ((n2 3)
10.1330+ (penalty 0)
10.1331+ (bcount 0))
10.1332+ (dotimes (i (- modules 1) penalty)
10.1333+ (dotimes (j (- modules 1))
10.1334+ (when (dark-module-p matrix i j)
10.1335+ (incf bcount))
10.1336+ (when (dark-module-p matrix (+ i 1) j)
10.1337+ (incf bcount))
10.1338+ (when (dark-module-p matrix i (+ j 1))
10.1339+ (incf bcount))
10.1340+ (when (dark-module-p matrix (+ i 1) (+ j 1))
10.1341+ (incf bcount))
10.1342+ (when (or (= bcount 0) (= bcount 4))
10.1343+ (incf penalty n2))))))
10.1344+
10.1345+(defclass qr-symbol ()
10.1346+ ((matrix :initform nil :initarg :matrix :reader matrix
10.1347+ :documentation "qr code symbol as matrix")
10.1348+ (modules :initform nil :initarg :modules :reader modules
10.1349+ :documentation "qr code symbol modules")))
10.1350+
10.1351+(defmethod print-object ((symbol qr-symbol) stream)
10.1352+ (fresh-line stream)
10.1353+ (with-slots (matrix modules) symbol
10.1354+ (format stream "qr symbol ~A x ~A:~%" modules modules)
10.1355+ (dotimes (i modules)
10.1356+ (dotimes (j modules)
10.1357+ (if (dark-module-p matrix i j)
10.1358+ (format stream "1 ")
10.1359+ (format stream "0 ")))
10.1360+ (format stream "~%"))))
10.1361+
10.1362+;;; FIXME: other encodings???
10.1363+(defun ascii->bytes (text)
10.1364+ (map 'list #'char-code text))
10.1365+
10.1366+(defun bytes->input (bytes version level mode)
10.1367+ (setf version (min (max version 1) 40))
10.1368+ (let ((input (make-instance 'qr-input :bytes bytes :version version
10.1369+ :ec-level level :mode mode)))
10.1370+ (data-encoding input)
10.1371+ (ec-coding input)
10.1372+ (structure-message input)
10.1373+ (module-placement input)
10.1374+ input))
10.1375+
10.1376+(defun input->symbol (input)
10.1377+ "encode qr symbol from a qr-input"
10.1378+ (multiple-value-bind (matrix mask-ref)
10.1379+ (data-masking input)
10.1380+ (declare (ignore mask-ref))
10.1381+ (let ((modules (matrix-modules (version input))))
10.1382+ (make-instance 'qr-symbol :matrix matrix :modules modules))))
10.1383+
10.1384+(defun encode-symbol-bytes (bytes &key (version 1) (level :level-m) (mode nil))
10.1385+ "encode final qr symbol from BYTES list"
10.1386+ (let ((input (bytes->input bytes version level mode)))
10.1387+ (log:debug! (format nil "version: ~A; segments: ~A~%" (version input)
10.1388+ (segments input)))
10.1389+ (input->symbol input)))
10.1390+
10.1391+;;;-----------------------------------------------------------------------------
10.1392+;;; One Ring to Rule Them All, One Ring to Find Them,
10.1393+;;; One Ring to Bring Them All and In the Darkness Blind Them:
10.1394+;;; This function wraps all we need.
10.1395+;;;-----------------------------------------------------------------------------
10.1396+;; (sdebug :dbg-input)
10.1397+(defun encode-symbol (text &key (version 1) (level :level-m) (mode nil))
10.1398+ "encode final qr symbol, unless you know what you are doing, leave MODE NIL"
10.1399+ (let ((bytes (ascii->bytes text)))
10.1400+ (encode-symbol-bytes bytes :version version :level level :mode mode)))
10.1401+
10.1402+;;; Table 1 - Codeword capacity of all versions of QR Code 2005
10.1403+;;; excluding Micro QR Code, varies between version
10.1404+(defvar *codeword-capacity-table*
10.1405+ #2A((-1 -1 -1 -1 -1 -1) ; 0, no such version
10.1406+ (21 202 31 208 26 0) (25 235 31 359 44 7)
10.1407+ (29 243 31 567 70 7) (33 251 31 807 100 7)
10.1408+ (37 259 31 1079 134 7) (41 267 31 1383 172 7)
10.1409+ (45 390 67 1568 196 0) (49 398 67 1936 242 0)
10.1410+ (53 406 67 2336 292 0) (57 414 67 2768 346 0) ; Version 10
10.1411+ (61 422 67 3232 404 0) (65 430 67 3728 466 0)
10.1412+ (69 438 67 4256 532 0) (73 611 67 4651 581 3)
10.1413+ (77 619 67 5243 655 3) (81 627 67 5867 733 3)
10.1414+ (85 635 67 6523 815 3) (89 643 67 7211 901 3)
10.1415+ (93 651 67 7931 991 3) (97 659 67 8683 1085 3) ; Version 20
10.1416+ (101 882 67 9252 1156 4) (105 890 67 10068 1258 4)
10.1417+ (109 898 67 10916 1364 4) (113 906 67 11796 1474 4)
10.1418+ (117 914 67 12708 1588 4) (121 922 67 13652 1706 4)
10.1419+ (125 930 67 14628 1828 4) (129 1203 67 15371 1921 3)
10.1420+ (133 1211 67 16411 2051 3) (137 1219 67 17483 2185 3) ; Version 30
10.1421+ (141 1227 67 18587 2323 3) (145 1235 67 19723 2465 3)
10.1422+ (149 1243 67 20891 2611 3) (153 1251 67 22091 2761 3)
10.1423+ (157 1574 67 23008 2876 0) (161 1582 67 24272 3034 0)
10.1424+ (165 1590 67 25568 3196 0) (169 1598 67 26896 3362 0)
10.1425+ (173 1606 67 28256 3532 0) (177 1614 67 29648 3706 0)) ; Version 40
10.1426+ "Number of modules (as version increases, 4 modules added) A | Function pattern
10.1427+modules B | Format and Version information modules C | Data modules (A^2-B-C) |
10.1428+Data capacity codewords (bytes, including ecc codewords) | Remainder bits.")
10.1429+(defun codeword-capacity (version)
10.1430+ "codeword: data word + ecc word"
10.1431+ (aref *codeword-capacity-table* version 4))
10.1432+(defun matrix-modules (version)
10.1433+ (aref *codeword-capacity-table* version 0))
10.1434+(defun remainder-bits (version)
10.1435+ (aref *codeword-capacity-table* version 5))
10.1436+
10.1437+(defun mode->index (mode)
10.1438+ (case mode
10.1439+ (:numeric 0)
10.1440+ (:alnum 1)
10.1441+ (:byte 2)
10.1442+ (:kanji 3)))
10.1443+
10.1444+(deftype ecc-level ()
10.1445+ '(member :level-l :level-m :level-q :level-h))
10.1446+(defun level->index (level)
10.1447+ (case level
10.1448+ (:level-l 0)
10.1449+ (:level-m 1)
10.1450+ (:level-q 2)
10.1451+ (:level-h 3)))
10.1452+
10.1453+;;; (Part I of) Table 9 - Number of Error Correction Codewords (bytes)
10.1454+;;; varies between version and level
10.1455+(defvar *ecc-codewords-table*
10.1456+ ;; (:level-l :level-m :level-q :level-h)
10.1457+ #2A((-1 -1 -1 -1) ;; 0, no such version
10.1458+ (7 10 13 17) (10 16 22 28) (15 26 36 44)
10.1459+ (20 36 52 64) (26 48 72 88) (36 64 96 112)
10.1460+ (40 72 108 130) (48 88 132 156) (60 110 160 192)
10.1461+ (72 130 192 224) (80 150 224 264) (96 176 260 308)
10.1462+ (104 198 288 352) (120 216 320 384) (132 240 360 432)
10.1463+ (144 280 408 480) (168 308 448 532) (180 338 504 588)
10.1464+ (196 364 546 650) (224 416 600 700) (224 442 644 750)
10.1465+ (252 476 690 816) (270 504 750 900) (300 560 810 960)
10.1466+ (312 588 870 1050) (336 644 952 1110) (360 700 1020 1200)
10.1467+ (390 728 1050 1260) (420 784 1140 1350) (450 812 1200 1440)
10.1468+ (480 868 1290 1530) (510 924 1350 1620) (540 980 1440 1710)
10.1469+ (570 1036 1530 1800) (570 1064 1590 1890) (600 1120 1680 1980)
10.1470+ (630 1204 1770 2100) (660 1260 1860 2220) (720 1316 1950 2310)
10.1471+ (750 1372 2040 2430))) ;; version 1 ~ 40
10.1472+(defun ecc-words-capacity (version level)
10.1473+ (aref *ecc-codewords-table* version (level->index level)))
10.1474+(defun data-words-capacity (version level)
10.1475+ (- (codeword-capacity version) (ecc-words-capacity version level)))
10.1476+
10.1477+;;; (Part II of) Table 9 - Error Correction blocks
10.1478+;;; varies between version and level
10.1479+(defvar *ecc-blocks*
10.1480+ ;; (version, level) =>
10.1481+ ;; (# of ec codewords for each blk, # of blk 1, # of data words for blk 1,
10.1482+ ;; # of blk 2, # of data words for blk 2)
10.1483+ ;; :level-l :level-m :level-q :level-h
10.1484+ #3A(((0 0 0 0 0) (0 0 0 0 0) (0 0 0 0 0) (0 0 0 0 0)) ; no such version
10.1485+ ((7 1 19 0 0) (10 1 16 0 0) (13 1 13 0 0) (17 1 9 0 0)) ; Version 1
10.1486+ ((10 1 34 0 0) (16 1 28 0 0) (22 1 22 0 0) (28 1 16 0 0))
10.1487+ ((15 1 55 0 0) (26 1 44 0 0) (18 2 17 0 0) (22 2 13 0 0))
10.1488+ ((20 1 80 0 0) (18 2 32 0 0) (26 2 24 0 0) (16 4 9 0 0))
10.1489+ ((26 1 108 0 0) (24 2 43 0 0) (18 2 15 2 16) (22 2 11 2 12)) ; Version 5
10.1490+ ((18 2 68 0 0) (16 4 27 0 0) (24 4 19 0 0) (28 4 15 0 0))
10.1491+ ((20 2 78 0 0) (18 4 31 0 0) (18 2 14 4 15) (26 4 13 1 14))
10.1492+ ((24 2 97 0 0) (22 2 38 2 39) (22 4 18 2 19) (26 4 14 2 15))
10.1493+ ((30 2 116 0 0) (22 3 36 2 37) (20 4 16 4 17) (24 4 12 4 13))
10.1494+ ((18 2 68 2 69) (26 4 43 1 44) (24 6 19 2 20) (28 6 15 2 16)) ; Version 10
10.1495+ ((20 4 81 0 0) (30 1 50 4 51) (28 4 22 4 23) (24 3 12 8 13))
10.1496+ ((24 2 92 2 93) (22 6 36 2 37) (26 4 20 6 21) (28 7 14 4 15))
10.1497+ ((26 4 107 0 0) (22 8 37 1 38) (24 8 20 4 21) (22 12 11 4 12))
10.1498+ ((30 3 115 1 116) (24 4 40 5 41) (20 11 16 5 17) (24 11 12 5 13))
10.1499+ ((22 5 87 1 88) (24 5 41 5 42) (30 5 24 7 25) (24 11 12 7 13)) ; Version 15
10.1500+ ((24 5 98 1 99) (28 7 45 3 46) (24 15 19 2 20) (30 3 15 13 16))
10.1501+ ((28 1 107 5 108) (28 10 46 1 47) (28 1 22 15 23) (28 2 14 17 15))
10.1502+ ((30 5 120 1 121) (26 9 43 4 44) (28 17 22 1 23) (28 2 14 19 15))
10.1503+ ((28 3 113 4 114) (26 3 44 11 45) (26 17 21 4 22) (26 9 13 16 14))
10.1504+ ((28 3 107 5 108) (26 3 41 13 42) (30 15 24 5 25) (28 15 15 10 16)) ; Version 20
10.1505+ ((28 4 116 4 117) (26 17 42 0 0) (28 17 22 6 23) (30 19 16 6 17))
10.1506+ ((28 2 111 7 112) (28 17 46 0 0) (30 7 24 16 25) (24 34 13 0 0))
10.1507+ ((30 4 121 5 122) (28 4 47 14 48) (30 11 24 14 25) (30 16 15 14 16))
10.1508+ ((30 6 117 4 118) (28 6 45 14 46) (30 11 24 16 25) (30 30 16 2 17))
10.1509+ ((26 8 106 4 107) (28 8 47 13 48) (30 7 24 22 25) (30 22 15 13 16)) ; Version 25
10.1510+ ((28 10 114 2 115) (28 19 46 4 47) (28 28 22 6 23) (30 33 16 4 17))
10.1511+ ((30 8 122 4 123) (28 22 45 3 46) (30 8 23 26 24) (30 12 15 28 16))
10.1512+ ((30 3 117 10 118) (28 3 45 23 46) (30 4 24 31 25) (30 11 15 31 16))
10.1513+ ((30 7 116 7 117) (28 21 45 7 46) (30 1 23 37 24) (30 19 15 26 16))
10.1514+ ((30 5 115 10 116) (28 19 47 10 48) (30 15 24 25 25) (30 23 15 25 16)) ; Version 30
10.1515+ ((30 13 115 3 116) (28 2 46 29 47) (30 42 24 1 25) (30 23 15 28 16))
10.1516+ ((30 17 115 0 0) (28 10 46 23 47) (30 10 24 35 25) (30 19 15 35 16))
10.1517+ ((30 17 115 1 116) (28 14 46 21 47) (30 29 24 19 25) (30 11 15 46 16))
10.1518+ ((30 13 115 6 116) (28 14 46 23 47) (30 44 24 7 25) (30 59 16 1 17))
10.1519+ ((30 12 121 7 122) (28 12 47 26 48) (30 39 24 14 25) (30 22 15 41 16)) ; Version 35
10.1520+ ((30 6 121 14 122) (28 6 47 34 48) (30 46 24 10 25) (30 2 15 64 16))
10.1521+ ((30 17 122 4 123) (28 29 46 14 47) (30 49 24 10 25) (30 24 15 46 16))
10.1522+ ((30 4 122 18 123) (28 13 46 32 47) (30 48 24 14 25) (30 42 15 32 16))
10.1523+ ((30 20 117 4 118) (28 40 47 7 48) (30 43 24 22 25) (30 10 15 67 16))
10.1524+ ((30 19 118 6 119) (28 18 47 31 48) (30 34 24 34 25) (30 20 15 61 16)) ; Version 40
10.1525+ ))
10.1526+(defun ecc-block-nums (version level)
10.1527+ "# of ec codewords for each blk, # of blk 1, # of data words for blk 1, ..."
10.1528+ (let ((lidx (level->index level)))
10.1529+ (values (aref *ecc-blocks* version lidx 0)
10.1530+ (aref *ecc-blocks* version lidx 1)
10.1531+ (aref *ecc-blocks* version lidx 2)
10.1532+ (aref *ecc-blocks* version lidx 3)
10.1533+ (aref *ecc-blocks* version lidx 4))))
10.1534+
10.1535+(defun minimum-version (init-version nbytes level)
10.1536+ "minimum version that can hold NBYTES data words, or INIT-VERSION if bigger"
10.1537+ (do ((v init-version (1+ v)))
10.1538+ ((> v 40) nil)
10.1539+ (when (>= (data-words-capacity v level) nbytes)
10.1540+ (return-from minimum-version v))))
10.1541+
10.1542+(defun version-range (version)
10.1543+ (cond
10.1544+ ((<= 1 version 9) 0)
10.1545+ ((<= 10 version 26) 1)
10.1546+ ((<= 27 version 40) 2)))
10.1547+
10.1548+;;; Table 3 - Number of bits in character count indicator for QR Code 2005
10.1549+(defvar *char-count-indicator*
10.1550+ ;; :numeric :alnum :byte :kanji
10.1551+ #2A((10 9 8 8) ; version-range 0
10.1552+ (12 11 16 10) ; version-range 1
10.1553+ (14 13 16 12))) ; version-range 2
10.1554+(defun char-count-bits (version mode)
10.1555+ (let ((i (version-range version))
10.1556+ (j (mode->index mode)))
10.1557+ (aref *char-count-indicator* i j)))
10.1558+
10.1559+;;; Table E.1 - Row/column coordinates of center modules of alignment patterns
10.1560+;;; varies between versions
10.1561+(defvar *align-coord-table*
10.1562+ #2A((0 ()) ; 0, no such version
10.1563+ (0 ()) (1 (6 18)) (1 (6 22))
10.1564+ (1 (6 26)) (1 (6 30)) (1 (6 34))
10.1565+ (6 (6 22 38)) (6 (6 24 42)) (6 (6 26 46))
10.1566+ (6 (6 28 50)) (6 (6 30 54)) (6 (6 32 58))
10.1567+ (6 (6 34 62)) (13 (6 26 46 66)) (13 (6 26 48 70))
10.1568+ (13 (6 26 50 74)) (13 (6 30 54 78)) (13 (6 30 56 82))
10.1569+ (13 (6 30 58 86)) (13 (6 34 62 90)) (22 (6 28 50 72 94))
10.1570+ (22 (6 26 50 74 98)) (22 (6 30 54 78 102)) (22 (6 28 54 80 106))
10.1571+ (22 (6 32 58 84 110)) (22 (6 30 58 86 114)) (22 (6 34 62 90 118))
10.1572+ (33 (6 26 50 74 98 122)) (33 (6 30 54 78 102 126)) (33 (6 26 52 78 104 130))
10.1573+ (33 (6 30 56 82 108 134)) (33 (6 34 60 86 112 138)) (33 (6 30 58 86 114 142))
10.1574+ (33 (6 34 62 90 118 146)) (46 (6 30 54 78 102 126 150)) (46 (6 24 50 76 102 128 154))
10.1575+ (46 (6 28 54 80 106 132 158)) (46 (6 32 58 84 110 136 162)) (46 (6 26 54 82 110 138 166))
10.1576+ (46 (6 30 58 86 114 142 170)))
10.1577+ "# of Alignment Patterns, row/column coordinates of center modules.")
10.1578+(defun valid-center-p (x y modules)
10.1579+ "The alignment center module is not in Finder Patterns."
10.1580+ (not (or (and (<= 0 x 8) (<= 0 y 8)) ; upleft finder pattern
10.1581+ (and (<= 0 x 8)
10.1582+ (<= (- modules 8) y (- modules 1))) ; upright finder pattern
10.1583+ (and (<= (- modules 8) x (- modules 1))
10.1584+ (<= 0 y 8)))))
10.1585+(defun align-centers (version)
10.1586+ "list of all valid alignment pattern center modules under VERSION"
10.1587+ (let* ((modules (matrix-modules version))
10.1588+ (coords (aref *align-coord-table* version 1))
10.1589+ (len (length coords))
10.1590+ (centers nil))
10.1591+ (dotimes (i len)
10.1592+ (loop for j from i to (- len 1) do
10.1593+ (let ((x (nth i coords))
10.1594+ (y (nth j coords)))
10.1595+ (when (valid-center-p x y modules)
10.1596+ (push (list x y) centers))
10.1597+ (unless (= x y)
10.1598+ (when (valid-center-p y x modules)
10.1599+ (push (list y x) centers))))))
10.1600+ centers))
10.1601+
10.1602+(defvar *mask-pattern-num* 8)
10.1603+(defun mask-condition (indicator)
10.1604+ (lambda (i j)
10.1605+ (case indicator
10.1606+ ;; (i + j) mod 2 == 0
10.1607+ (0 (= (mod (+ i j) 2) 0))
10.1608+ ;; i mod 2 == 0
10.1609+ (1 (= (mod i 2) 0))
10.1610+ ;; j mod 3 == 0
10.1611+ (2 (= (mod j 3) 0))
10.1612+ ;; (i + j) mod 3 == 0
10.1613+ (3 (= (mod (+ i j) 3) 0))
10.1614+ ;; ((i/2) + (j/3)) mod 2 == 0
10.1615+ (4 (= (mod (+ (floor i 2) (floor j 3)) 2) 0))
10.1616+ ;; (i*j) mod 2 + (i*j) mod 3 == 0
10.1617+ (5 (= (+ (mod (* i j) 2) (mod (* i j) 3)) 0))
10.1618+ ;; ((i*j) mod 2 + (i*j) mod 3)) mod 2 == 0
10.1619+ (6 (= (mod (+ (mod (* i j) 2) (mod (* i j) 3)) 2) 0))
10.1620+ ;; ((i+j) mod 2 + (i*j) mod 3)) mod 2 == 0
10.1621+ (7 (= (mod (+ (mod (+ i j) 2) (mod (* i j) 3)) 2) 0)))))
10.1622+
10.1623+(defvar *ecc-level-indicator* #((0 1) (0 0) (1 1) (1 0))
10.1624+ ":level-l :level-m :level-q :level-h")
10.1625+(defun level-indicator (level)
10.1626+ (aref *ecc-level-indicator* (level->index level)))
10.1627+(defvar *mask-pattern-reference*
10.1628+ #((0 0 0) (0 0 1) (0 1 0) (0 1 1)
10.1629+ (1 0 0) (1 0 1) (1 1 0) (1 1 1)))
10.1630+(defun mask-pattern-ref (ind)
10.1631+ (aref *mask-pattern-reference* ind))