UP | HOME
Jump to my Home Page Send me a message Check out stuff on GitHub Check out my photography on Instagram Check out my profile on LinkedIn Check out my profile on reddit Check me out on Facebook

Code

Author: Mitch Richling
Updated: 2023-10-14

Copyright 2023 Mitch Richling. All rights reserved.

Table of Contents

1. Code Library

This is code used by other org-mode files to generate content.

These need reevaluated when the content of the tables data.org:pfdat or data.org:loc2id change.

;; ltable -- a list of lists data structure used to represent an org-table.
(defun BWS-ltable-to-org-table-string (ltable)
  (mapconcat (lambda (x) (concat "|" (string-join (if (listp x) x (list x)) "|") "|"))
             ltable
             "\n"))
(defun BWS-ltable-sort (ltable col-num lessthan-test)
  (cl-sort ltable (lambda (x y) (funcall lessthan-test (elt x col-num) (elt y col-num)))))
(defun BWS-ltable-search (ltable col-num item equal-test)
  (cl-remove item
             ltable
             :test (lambda (x y) (not (funcall equal-test x (elt y col-num))))))
(defun BWS-ltable-get-col (ltable col-num)
  (mapcar (lambda (x) (elt x col-num)) ltable))
(defun BWS-ltable-search-cell (ltable key-col-num val-col-num key-val)
  "Search for a row with a string-equal test on data in column key-col-num and the key-val, and return value in column val-col-num"
  (or (elt (cl-find key-val
                    ltable
                    :test (lambda (x y) (string-equal x (elt y key-col-num))))
           val-col-num)
      ""))
(defun BWS-ltable-stringafy (ltable)
  "Convert elements in an ltable to strings -- org-mode has a habit of converting numeric looking things to lisp numbers"
  (mapcar (lambda (x) (mapcar (lambda (y) (format "%s" y)) x)) ltable))
;; Utility functions
(defun BWS-factor-alist (in-table factor-col data-col)
  (cl-loop for key in (cl-remove-duplicates (BWS-ltable-get-col in-table factor-col)
                                            :test #'string-equal)
           collect (cons key (BWS-ltable-get-col (BWS-ltable-search in-table factor-col key #'string-equal) data-col))))
(defun BWS-string-for-table-cell-from-list (in-list break-count)
  (string-join (cl-loop with n = (length in-list)
                        for e in in-list
                        for i from 1
                        for p = (format "%s" e)
                        collect (if (and (not (= n i)) (zerop (mod i break-count))) (concat p " {{{NLTBC}}}") p))
               " "))
(defun BWS-id-to-id-type(in-id)
  (replace-regexp-in-string "[0-9]+$" "" in-id))
(defun BWS-id-match-type (in-type in-id )
  (string-equal in-type (replace-regexp-in-string "[0-9]+$" "" in-id)))
(defun BWS-ids-same-typep (in-id1 in-id2)
  (string-equal (BWS-id-to-id-type in-id1) (BWS-id-to-id-type in-id2)))
(defun BWS-pos-to-link (n sheet-fmt)
  (let ((pos (if (numberp n) n (string-to-number n)))
        (sno (if (= sheet-fmt 150) "w" "u"))
        (n   (format "%s" n)))
    (if (zerop pos)
        n
        (if (string-match "\\*" n)
            (replace-regexp-in-string "\\*$" "" n)
            (format "[[file:sheet%s001.html#bwIDsheet%s001p%03d][%d]]" sno sno pos pos)))))
(defun BWS-stamp-loc-to-file-base (stamp-location)
  (if (string-equal "C" (substring stamp-location 0 1))
      "album"
      "stock"))
(defun BWS-stamp-id-to-file-base (stamp-id)
  (BWS-stamp-loc-to-file-base (BWS-id2loc stamp-id)))
(defun BWS-stamp-id-to-link (stamp-id)
  (format "[[file:%s.html#%s][%s]]" (BWS-stamp-id-to-file-base stamp-id) stamp-id stamp-id))
(defun BWS-stamp-id-to-thm-local-link (stamp-id)
  (if (string-equal (substring stamp-id 0 7) "bwIDp2s")
      (format "[[#%s][file:%s-X-front-back_90dpi.png]]" stamp-id stamp-id)
      (format "[[#%s][file:%s-1_90dpi.png]]"            stamp-id stamp-id)))
(defun BWS-stamp-id-to-thm-link (stamp-id)
  (if (string-equal (substring stamp-id 0 7) "bwIDp2s")
      (format "[[file:%s.html#%s][file:%s-X-front-back_90dpi.png]]" (BWS-stamp-id-to-file-base stamp-id) stamp-id stamp-id)
      (format "[[file:%s.html#%s][file:%s-1_90dpi.png]]"            (BWS-stamp-id-to-file-base stamp-id) stamp-id stamp-id)))
(defun BWS-loc-to-parts (in-loc)
  (if (stringp in-loc)
      (cons (substring in-loc 0 1)
            (mapcar #'string-to-number (split-string (substring in-loc 2) "[prc]" 't)))))
(defun BWS-loc-lessp (in-loc1 in-loc2)
  (if (BWS-loc-same-book in-loc1 in-loc2)
      (cl-loop for c1 in (cdr (BWS-loc-to-parts in-loc1))
               for c2 in (cdr (BWS-loc-to-parts in-loc2))
               when (< c1 c2)
               return t
               when (> c1 c2)
               return nil
               finally return nil)
      (string-lessp in-loc1 in-loc2)))
(defun BWS-loc-same-book (in-loc1 in-loc2)
  (string-equal (substring in-loc1 0 1) (substring in-loc2 0 1)))
(defun BWS-thk-to-m-pm (measures-string)
  "Compute mean & standard deviation, and produce a nice LaTeX string."
  (let ((measures (mapcar #'string-to-number (split-string measures-string nil 't))))
    (let ((n  (length measures))
          (s1 (float (apply #'+ measures)))
          (s2 (float (apply #'+ (mapcar (lambda (x) (* x x)) measures)))))
    (format "\\(%0.1f\\pm %0.2f\\,\\mathrm{microns}\\)" (/ (* 1.0 s1) n) (sqrt (/ (- (* n s2) (* s1 s1)) (* n (1- n))))))))
(defun BWS-perf2cperf (h v)
  "Round to nearest 1/4 perf value, and produce a nice LaTeX string."
  (interactive "nHorizontal: \nnVertical: ")
  (let ((h (if (numberp h) h (string-to-number h)))
        (v (if (numberp v) v (string-to-number v))))
    (cl-flet ((r2q (x) (let* ((wp (truncate x))
                              (tf (round (- (* 4 x) (* wp 4))))
                              (bf 4))
                         (cond ((= tf 0) (format "%d"           wp))
                               ((= tf 2) (if (zerop wp)
                                             (format "\\frac{%d}{%d}"              (/ tf 2) 2)
                                             (format "%d \\frac{%d}{%d}" wp        (/ tf 2) 2)))
                               ((= tf 4) (format "%d"           (1+ wp)))
                               ('t       (if (zerop wp)
                                             (format "\\frac{%d}{%d}"              tf       4)
                                             (format "%d \\frac{%d}{%d}" wp        tf       4)))))))
      (if (< h 0.125)
          (if (< v 0.125)
              (format "Imperforate")
              (format "\\(%s\\) Vertically" (r2q v)))
          (if (< v 0.125)
              (format "\\(%s\\) Horizontally" (r2q h))
              (let ((hs (r2q h))
                    (vs (r2q v)))
                (if (string-equal hs vs)
                    (format "\\(%s\\)" hs)
                    (format "\\(%s \\times %s\\)" hs vs))))))))
(defun BWS-pfid2pretty (pfid)
  (format "=%s=: %s" pfid (BWS-ltable-search-cell pfdat 0 1 pfid)))
(defun BWS-ppfid2pretty (ppfid)
  (format "=%s=: (POTENTIAL) %s" ppfid (BWS-ltable-search-cell ppfdat 0 1 ppfid)))
(defun BWS-id2loc (sid)
  (BWS-ltable-search-cell loc2id 1 0 sid))
;; Burelage functions
(defun BWS-find-burelage-match (pat)
  "Given a burelage pattern, produce a list of matches.  
Each match is a list with three values: univeral position, position on 150, position on 168.
 Example patterns:
   - A block of four ......... '((1 2) 
                                 (3 4))
   - A horizontal stip of 5 .. '((1 2 3 4 5))
   - A vertical stip of 3 .... '((1) 
                                 (2) 
                                 (3))
   - An irregular block ...... '((1 2 0)      
                                 (0 3 4))
     Note the use of 0 as a wild card -- it effectively matches any type
   - Ambiguous block ......... '((13 2)      
                                 (3  4))     
     The use of 13 here indicates the upper left position may be a 1 OR 3.  One might use 13 if
     all they can tell about the background is that the 'triangle' is at the top of the stamp.
     In general, the individual digits are taken as allowable types to match.  For example, 
     123 means to match 1, or 2, or 3."
  (cl-labels ((getbt (x y)      (elt (elt burl150168 y) x))
              (i2rt  (ty)       (if (numberp btype)
                                    (cond ((= 1 btype) "I")
                                          ((= 2 btype) "II")
                                          ((= 3 btype) "III")
                                          ((= 4 btype) "IV")
                                          (t           ""))
                                    ""))
              (patmat (x y pat) (cl-loop for ptl in pat
                                         for py from 0
                                         when (not (cl-loop for ptv in ptl
                                                            for px from 0
                                                            for bt = (getbt (+ x px) (+ y py))

                                                            ;; when (or (null bt) (not (or (zerop   ptv)
                                                            ;;                             (and (< ptv 5) (equal bt ptv))
                                                            ;;                             (cl-some (lambda (x) (equal bt (- x 48))) (number-to-string ptv)))))

                                                            when (or (null bt) (not (cl-some (lambda (x) (let ((v (- x 48)))
                                                                                                           (or (zerop v)
                                                                                                               (equal bt v))))
                                                                                             (number-to-string ptv))))

                                                            return nil
                                                            finally (cl-return t)))
                                         return nil
                                         finally (cl-return t))))
    (let ((pat-wid (apply #'max (mapcar #'length pat)))
          (pat     (if nil ;; Set to 't to rotate pattern 180 degrees
                       (reverse (mapcar #'reverse pat))
                       pat)))
      (cl-loop for y from 0 upto (1- 15)
               append (cl-loop for x from 0 upto (1- 12)
                               for p    = (+ 1 (* y 12) x)
                               for p168 = (if (> y 0)              (+ 1 (* (1- y) 12) x) "-")
                               for p150 = (if (< (+ pat-wid x) 11) (+ 1 (* y      10) x) "-")
                               for mat = (patmat x y pat)
                               when mat
                               collect (list p p150 p168))))))
(defun BWS-find-all-burelage-multiples (multiple-width) ;; Shape: 2=2x2, 4=4x1, 1=1x4
  "Find all multiples of 4 (2x2, 1x4, 4x1) with all 4 burelage types"
  (cl-labels ((perms (in-list) (if (null in-list)
                                   '(())
                                   (mapcan (lambda (e) (mapcar (lambda (p) (cons e p))
                                                               (perms (cl-remove e in-list :count 1 :test #'eq))))
                                           in-list)))
              (l2pat (in-list) (cl-case multiple-width
                                 (4 (list in-list))
                                 (1 (mapcar #'list in-list))
                                 (2 (list (list (elt in-list 0) (elt in-list 1))
                                          (list (elt in-list 2) (elt in-list 3)))))))    
    (let ((all-blocks (perms '(1 2 3 4))))
      (sort (mapcan (lambda (x) (mapcar #'car (BWS-find-burelage-match (l2pat x)))) all-blocks) #'<))))
(defun BWS-make-burelage-table (color-function)
  "Draw a colorfull burelage table"
  (concat "#+ATTR_HTML: :rules all solid #ccc :frame all :align center :cellpadding 0 :border-spacing 0\n"
          "| /150/ | *1* | *2* | *3* | *4* | *5* | *6* | *7* | *8* | *9* | *10* |     |     |     |\n"
          "|  <c>  | <c> | <c> | <c> | <c> | <c> | <c> | <c> | <c> | <c> | <c>  | <c> | <c> | <c> |\n"
          (BWS-ltable-to-org-table-string (cl-loop for row in burl150168
                                                   for y from 0
                                                   collect (append (list (format "*%d*" (1+ y)))
                                                                   (cl-loop for btype-num in row
                                                                            for x from 0
                                                                            for p    = (+ 1 (* y 12) x)
                                                                            for btype-str = (if (numberp btype-num)
                                                                                                (cl-case btype-num
                                                                                                  (1  "I")
                                                                                                  (2 "II")
                                                                                                  (3 "III")
                                                                                                  (4 "IV"))
                                                                                                " ")
                                                                            collect (if (and (> x 9) (= y 0))
                                                                                        " "
                                                                                        (format "{{{%s(%s)}}}" 
                                                                                                (concat "BCB" (funcall color-function p))
                                                                                                btype-str)))
                                                                   (list (if (= y 0)
                                                                             " "
                                                                             (format "*%d*" y))))))
          "\n"
          "|       | *1* | *2* | *3* | *4* | *5* | *6* | *7* | *8* | *9* | *10* | *11* | *12* | /168/ |\n"))
(defun BWS-make-burelage-hits-table (pat-shape hit-list)  
  (let ((bloc (cl-loop for mul-pos in hit-list
                       append (cl-loop for tst-pos from 1 upto 180
                                       for tst-pos-x = (mod (1- tst-pos) 12)
                                       for tst-pos-y = (truncate (1- tst-pos) 12)
                                       for mul-pos-x = (mod (1- mul-pos) 12)
                                       for mul-pos-y = (truncate (1- mul-pos) 12)
                                       when (not (cond ((< tst-pos 1)                                                                     "off sheet<")
                                                       ((> tst-pos 180)                                                                   "off sheet>")
                                                       ((= tst-pos 11)                                                                    "off sheet 11")
                                                       ((= tst-pos 12)                                                                    "off sheet 12")
                                                       ((< tst-pos-y mul-pos-y)                                                           "above block")
                                                       ((< tst-pos-x mul-pos-x)                                                           "left of block")
                                                       ((> tst-pos-y (1- (+ mul-pos-y (length pat-shape))))                               "below block")
                                                       ((> tst-pos-x (+ mul-pos-x (1- (length (elt pat-shape (- mul-pos-y tst-pos-y)))))) "right of block")))
                                       collect tst-pos))))
    (BWS-make-burelage-table (lambda (p) (cond ((member p hit-list) "P")
                                               ((member p bloc)     "Y")
                                               ('t                  "W"))))))
(defun BWS-make-burelage-report (multiple-width)  
  (let ((bta (list "-" "I" "II" "III" "IV")))
    (concat "#+ATTR_HTML: :border 2 solid #ccc :frame hsides :align center\n"
            "|   150    |          |          |          |   168    |          |          |          | Types |     |     |     |\n"
            "| $P_{LU}$ | $P_{RU}$ | $P_{LL}$ | $P_{RL}$ | $P_{LU}$ | $P_{RU}$ | $P_{LL}$ | $P_{RL}$ |  LU   | RU  | LL  | RL  |\n"
            "|----------+----------+----------+----------+----------+----------+----------+----------+-------+-----+-----+-----|\n"
            "|   <c>    |   <c>    |   <c>    |   <c>    |   <c>    |   <c>    |   <c>    |   <c>    |  <c>  | <c> | <c> | <c> |\n"
            "|    /     |          |          |    >     |    <     |          |          |    >     |   <   |     |     |  >  |\n"
            (BWS-ltable-to-org-table-string (BWS-ltable-stringafy (cl-loop for pos in (BWS-find-all-burelage-multiples multiple-width)
                                                                           for pos-x = (mod (1- pos) 12)
                                                                           for pos-y = (truncate (1- pos) 12)
                                                                           for blk-pos = (cl-loop for yd from 0 upto (1- (/ 4 multiple-width))
                                                                                                  append (cl-loop for xd from 0 upto (1- multiple-width)
                                                                                                                  for x = (+ pos-x xd)
                                                                                                                  for y = (+ pos-y yd)
                                                                                                                  for p    = (+ 1 (* y 12) x)
                                                                                                                  for p168 = (if (> y 0)  (+ 1 (* (1- y) 12) x) 0)
                                                                                                                  for p150 = (if (< x 10) (+ 1 (* y      10) x) 0)
                                                                                                                  for bt   = (elt (elt burl150168 y) x)
                                                                                                                  collect (list p150 p168 bt)))
                                                                           for blk-pos150 = (let ((tmp (mapcar #'cl-first  blk-pos))) (if (cl-some #'zerop tmp) (list "*-*" "*-*" "*-*" "*-*") tmp))
                                                                           for blk-pos168 = (let ((tmp (mapcar #'cl-second blk-pos))) (if (cl-some #'zerop tmp) (list "*-*" "*-*" "*-*" "*-*") tmp))
                                                                           for blk-type   = (mapcar (lambda (x) (elt bta (cl-third x))) blk-pos)
                                                                           collect (append blk-pos150 blk-pos168 blk-type)))))))
(defun BWS-make-sheet-grid-table (sheet cell-function)  
  (let ((snx (if (= sheet 150) 10 12))
        (sny (if (= sheet 150) 15 14)))
    (concat "#+ATTR_HTML: :rules all solid #ccc :frame all :align center\n"
            (BWS-ltable-to-org-table-string (append (list (cl-loop for x from 0 upto snx
                                                                   collect "<c>"))                                                  
                                                    (if (= sheet 150) (list (append (list "/150/")
                                                                                    (cl-loop for x from 0 upto (1- snx)
                                                                                             collect (format "*%02d*" (1+ x))))))
                                                    (cl-loop for y from 0 upto (1- sny)
                                                             collect (append (if (= sheet 150) (list (format "*%02d*" (1+ y))))
                                                                             (cl-loop for x from 0 upto (1- snx)
                                                                                      for p = (+ 1 (* y snx) x)
                                                                                      collect (funcall cell-function p))
                                                                             (if (= sheet 168) (list (format "*%02d*" (1+ y))))))
                                                    (if (= sheet 168) (list (append (cl-loop for x from 0 upto (1- snx)
                                                                                             collect (format "*%02d*" (1+ x)))
                                                                                    (list "/168/")))))))))

2. Code Snippets

2.1. Burelage Types

2.1.1. The BWS-find-burelage-match Function

When faced with a block of stamps it is frequently possible to plate the block using the block's burelage types. Searching manually for matching blocks is tedious, and so I have a little lisp function for that:

(documentation 'BWS-find-burelage-match)
"Given a burelage pattern, produce a list of matches.  
Each match is a list with three values: univeral position, position on 150, position on 168.
 Example patterns:
   - A block of four ......... ’((1 2) 
                                 (3 4))
   - A horizontal stip of 5 .. ’((1 2 3 4 5))
   - A vertical stip of 3 .... ’((1) 
                                 (2) 
                                 (3))
   - An irregular block ...... ’((1 2 0)      
                                 (0 3 4))
     Note the use of 0 as a wild card -- it effectively matches any type
   - Ambiguous block ......... ’((13 2)      
                                 (3  4))     
     The use of 13 here indicates the upper left position may be a 1 OR 3.  One might use 13 if
     all they can tell about the background is that the ’triangle’ is at the top of the stamp.
     In general, the individual digits are taken as allowable types to match.  For example, 
     123 means to match 1, or 2, or 3."

2.1.2. Presenting results as a table

(concat "#+ATTR_HTML: :align center\n"
        "| Universal Position | 150 Position | 168 Position |\n"
        "|--------------------+--------------+--------------|\n"
        "|        <c>         |     <c>      |     <c>      |\n"
        ;; (BWS-ltable-to-org-table-string (BWS-ltable-stringafy (BWS-find-burelage-match '((24 0 13 13 13)
        ;;                                                                                  (13 0 24 24 13))))))
        ;; (BWS-ltable-to-org-table-string (BWS-ltable-stringafy (BWS-find-burelage-match '((24 13 13 24 13)
        ;;                                                                                  (24 13 24 24 13)
        ;;                                                                                  (0  0  13 24 24))))))
        (BWS-ltable-to-org-table-string (BWS-ltable-stringafy (BWS-find-burelage-match '((3 1)
                                                                                         (0 1))))))

Universal Position 150 Position 168 Position
85 71 73

2.1.3. Presenting results as a sheet diagram

(let* ((pat '((24 13 24)
              (13 13 24)
              (13 13 24)))
       (locs (BWS-ltable-get-col (BWS-find-burelage-match pat) 0)))
  (BWS-make-burelage-table (lambda (p) (if (member p locs)
                                           "P"
                                           "W"))))
150 1 2 3 4 5 6 7 8 9 10      
1
II
IV
IV
IV
I
I
IV
I
II
III
     
2
I
IV
IV
II
I
I
II
IV
III
II
II
IV
1
3
III
I
I
IV
III
I
I
IV
III
I
III
IV
2
4
III
II
III
IV
II
II
I
IV
I
IV
IV
III
3
5
IV
IV
I
I
I
II
IV
III
II
I
II
IV
4
6
I
I
I
I
II
I
I
I
II
II
I
III
5
7
II
I
I
IV
II
IV
IV
I
I
I
IV
IV
6
8
III
I
III
III
II
IV
I
IV
I
III
II
IV
7
9
III
I
II
IV
I
I
III
III
IV
IV
I
I
8
10
I
II
III
III
IV
II
IV
I
II
III
IV
III
9
11
III
I
II
III
II
III
I
I
IV
I
IV
III
10
12
III
IV
III
II
II
III
III
III
II
II
IV
II
11
13
III
III
III
II
II
IV
III
II
II
II
II
IV
12
14
IV
IV
III
II
I
IV
III
IV
IV
II
II
IV
13
15
IV
III
III
I
III
III
IV
IV
IV
III
III
IV
14
  1 2 3 4 5 6 7 8 9 10 11 12 168

2.2. Largest Inventory ID number by type

(concat "#+ATTR_HTML: :align center\n"
        "| Largest ID |\n"
        "|------------|\n"
        (BWS-ltable-to-org-table-string (cl-remove-duplicates (sort (BWS-ltable-get-col loc2id 1)
                                                                         #'string-lessp)
                                                                   :test #'BWS-ids-same-typep)))
Largest ID
bwIDbh001
bwIDdbl006
bwIDdrkb003
bwIDdrkw004
bwIDesy002
bwIDidbl003
bwIDimp006
bwIDimph008
bwIDimps003
bwIDimpv004
bwIDmisbg006
bwIDmisfg004
bwIDovrc001
bwIDp2s019
bwIDpdue002
bwIDpf008
bwIDpfrk001
bwIDps001
bwIDqbu006
bwIDqbw003
bwIDrepb002
bwIDruf004
bwIDsamp010
bwIDsgl078
bwIDshade015
bwIDsheetu001
bwIDsheetw001

2.3. Number of items in each book

(apply #'concat 
       "#+ATTR_HTML: :align center\n"
       "| Book | Count |\n"
       "|------+-------|\n"
       (let ((dat (BWS-ltable-get-col loc2id 0)))
         (mapcar (lambda (book) (format "|%s|%d|\n" book (cl-count-if (lambda (x) (BWS-loc-same-book book x)) dat))) 
                 (cl-remove-duplicates (mapcar (lambda (x) (substring x 0 1)) dat) :test #'string-equal))))
Book Count
C 88
S 64