Automatically generated documentation

out2.mtr
Turn raw cell wall thicknesses and lumen diameters into
mean tracheidogram chronologies.


Association lists for measurements
Association list utilities
f: restore
f: update-assoc
f: a-merge
f: process-tag
f: sort-map
Data input
f: extract-item
f: merge-obs

Cell and cell wall sizes from lumen and double cell wall measurements
f: makevec
f: corr-measures
f: correct-walls

Standardizing cell measurements to mean cell-file lengths
Mean cell-file lengths
f: total-cell-counts
Deriving mean tracheidograms
f: standadize-measurements
f: take-means
f: chron-build
f: make-mean-chronologies

Tabulating chronologies of mean tracheidograms
Helper functions for tabulation
f: pad-years
f: tabulate-nf
f: tabulate-trach
Tabulate the data for a single site
f: tabulate-mean-chronology

Write the .mtr files a set of mean tracheidogram chronologies.
a: require-extension
f: write-mtr-file
f: write-mtr
a: require-extension
f: process-args
a: process-args

Association lists for measurements

We represent the data hierarchy by a collection of nested association lists.
In the raw data at the topmost level is the site list, following the pattern
((site-A-ID site-A-DATA) (site-B-ID site-B-DATA) (site-C-ID site-C-DATA)...)
with the various site IDs being literal tags and the site data being further
association lists, each of the pattern
((sample-X sample-X-DATA) (sample-Y sample-Y-DATA) ... )
where sample-X, sample-Y etc. are the sample identifiers and the
corresponding sample data are more association lists, each of the form
((year-L year-L-DATA) (year-M year-M-DATA) (year-N year-N-DATA) ... )
with year-L, year-M, year-N... being the dates, as integers on an arbitrary
timescale, and the data are replicate measurement association lists
((cell-file-P cell-file-P-DATA) (cell-file-Q cell-file-Q-DATA) ... )
where cell-file-P, cell-file-Q etc. are the identification numbers for the
particular files of cells, and the cell file data are association lists
of the form
((cell-N (wall-N lumen-N)) (cell-N-1 (wall-N-1 lumen-N-1) ... ).
Later stages of processing build nested lists with different data at the
lower levels, but keep the same kind of structure at the higher levels
of the hierarchy.

Association list utilities

Since we make extensive use of association lists, it makes sense to define
some of the common operations.

restore

(define restore (lambda (st lis)
... Full Code ... )
Append the list <lis> to a reversed version of the list <st>.
More or less equivalent to (append (reverse st) lis).


update-assoc

(define update-assoc (lambda (item a-list update-op)
... Full Code ... )
Generate a new updated version of the association list <a-list>.
If (assoc item a-list) would recover the a pairing of <item> with some data,
we replace this with a new pairing of <item> and the results of applying
the function <update-op> to those data, but otherwise prepend a new entry
associating <item> with the results of applying <update-op> to the empty
list.


a-merge

(define a-merge (lambda (a-item a-list merge-op)
... Full Code ... )
Merge new data with an existing association list <a-list>.
Given the pair <a-item>, already a possible association list entry, use the
<merge-op> function to merge it with any matching entry in the list, or
otherwise add it.


process-tag

(define process-tag (lambda (a-item proc-op)
... Full Code ... )
Generate a modified version of the association list entry <a-item>.
Use the function <proc-op> to transform the data in the cdr of the existing
entry, keeping the car field unchanged.


sort-map

(define sort-map (lambda (a-list rel-op? proc-op)
... Full Code ... )
Process the association list <a-list>, sorting the modified entries.
Apply a processing function <proc-op> to the data in the cdr field of all
the entries in the old list, maintaining the car field of the entries,
but using this as a sort key to determine the order in the new list,
through the comparison predicate <rel-op?>.



Data input

The files holding the cell size measurements required to poulate the
nested association lists with data have a uniform stucture, simply
a repeated sequence of five whitespace-separated values
1. a numeric sample code
2. a year and cell file code, consisting of
2.1. the date on an arbitrary timescale as an integer
2.2. the replicate measurement number, padded to three digits with zeros
3. the number giving the cell position within the measurement series
4. the double cell wall thickness measurement
5. the cell lumen diameter measurement.
Negative values for the measurements denote missing observations, such as
the double cell wall thickness for the first cells in the series, or the
lumen diameter for the last cells in the series: by convention each series
begins after the common cell wall of the last latewood cell of the previous
ring and ends with the thickness of the common cell wall of the last latewood
cell of the current ring and the first earlywood cell of the next ring.
The original log files from the cell measurement add-ons to the NIH Image
program also add repeated headers, the sequence of five labels
"Sample            File          N         Wall           Lumen".

extract-item

(define extract-item (lambda (in-obj)
... Full Code ... )
Read a sequence of five values from a data file.
Given <in-obj>, a pair consisting of a list of previously read data values
and an input port, generate an updated version with the next sequence of five
values read from the file and prepended to the list.  A premature end-of-file
condition may leave the sequence incomplete.


merge-obs

(define merge-obs (lambda (top-a-list site-id fname)
... Full Code ... )
Read measurements from the file named by the string <fname>.
The new data are merged with any already present in the collection of
nested association lists, <top-a-list>, using the site identification
literal <site-id>.  Missing measurements get recoded from negative numbers
to a literal value: missing.



Cell and cell wall sizes from lumen and double cell wall measurements

Ideally for each cell we would have a lumen diameter and two cell wall
measurements, from the lumen to the middle lamella between it and the
previous cell along the radial file, and the corresponding width to the
middle lamella dividing it from the next cell; however in practice the
middle lamellae are often obscure, so we have to make do with measurements
of the distance between lumena instead: the doule cell wall thickness.
Because this single measurement includes contributions from two distinct
cells, we apply some crude heuristics to partition it into two components
for the purposes of computing cell sizes.  With some exceptions, a cell
size will depend on the observed lumen diameter, and a wall thickness
that depends on a partial contribution from the preceding and following
double cell wall measurements.  For convenience with subsequent processing,
as soon as we have derived the lists of cell sizes and wall thicknesses
we turn them into vectors.

makevec

(define makevec (lambda (wall cell)
... Full Code ... )
Turn lists of wall measurements <wall> and cell sizes <cell> into vectors. 


corr-measures

(define corr-measures (lambda (raw prev wall cell)
... Full Code ... )
Turn raw lumen diameters and double cell wall thicknesses into cell sizes.
Given <raw> a list of unprocessed raw measurements of the form
((current-cell-wall current-lumen) (next-cell-wall next-lumen) ...)
<prev>, the previously processed raw measurement as the list
(previous-double-cell-wall previous-lumen)
<wall>, a stack of accumulated corrected wall thicknesses and
<cell>, a stack of accumulated cell sizes, we recurse down the list
of of unprocessed measurements, maintaining the corrected measurement
lists, and finally return them as a list of two vectors.  In the simplest
case the corrected cell size would be simply the sum of the lumen
diameter and the mean of the preceding and following double cell wall
thicknesses, but missing values and end cases require a case analysis
and some crude approximations. 


correct-walls

(define correct-walls (lambda (obs-a-list)
... Full Code ... )
Build a collection nested association lists holding cell and wall sizes.
From <obs-a-list>, the nested association lists containing the raw
observed lumen diameters and double cell wall thicknesses, we construct
the corresponding collection of lists holding vectors of corrected
cell size and wall dimensions.  



Standardizing cell measurements to mean cell-file lengths

For a given year at a particular site the rings in different samples, and
even the replicate measurements within a ring, will generally have different
numbers of cells within the radial files of tracheids.  To produce a single
set of measurements summarizing the pattern of cell sizes for that ring
at that site, the mean tracheidogram, we must coerce the measurement
series to all have the same length.  Here we use the same simple weighted
averaging technique that the Institue of Forest have used for many years.

Mean cell-file lengths


total-cell-counts

(define total-cell-counts (lambda (size-a-list)
... Full Code ... )
Build a collection of site-specific annual cell number totals.
Given <size-a-list>, the collection of nested association lists that contains
the corrected cell size and wall thickness vectors, we generate a new
association list pairing site IDs with chronologies of cell file totals.
These chronologies are unsorted association lists where each entry is
of the form
(date total-number-of-cell-files . total-number-of-cells)
with the totals accumulated over all the replicated cell files for all the
samples at a site.  Trivially, the ratio of the totals gives the mean number
of cells in the radial file of tracheids for a particular date at a
particular site.



Deriving mean tracheidograms


standadize-measurements

(define standadize-measurements (lambda (c n)
... Full Code ... )
Make a list of length <n> from an arbitrary-length measurement vector <c>.
In real conifer wood the number of cells in a radial file of tracheids will
vary, not only from tree to tree and ring to ring, but also between different
files of cells within a ring.  We must account for these variations in length
to turn multiple series of observed cell measurements into summaries, and the
simplest way to do this, long used at the Institute of Forest, uses weighted
means to map the observations onto a fixed-length vector.


take-means

(define take-means (lambda (n yr-date measurements)
... Full Code ... )
Generate the mean tracheidogram for a particular year.
Given a cell-file length, <n>, a number giving the date of the ring
on whatever arbitrary time-scale is in use, <yr-date>, and nested
association lists holding the corrected cell wall and total cell sizes,
<measurements>, we extract all the size data from all the samples
and replicated cell files for that date, standardize each individual
cell file to have a length of <n>, accumulate them into a running totals
of standardized cell measurements, and finally divide by the total number
of cell files for the mean tracheidograms.  Two lists represent the
tracheidograms, one of standardized cell wall thicknesses and the other of
standardized total cell sizes, and we redundantly associate them with
the length used in the standardization.


chron-build

(define chron-build (lambda (yrlist measurelist)
... Full Code ... )
Build a chronology of mean tracheidograms for a single site.
The association list <yrlist> must already contain entries pairing the
date of each year in the chronology with the total number of cell
files measured for that year at that site and the sum of these cell
file lengths; <measurelist> must contain the corresponding cell size
and wall thickness data within nested association lists (cell-file
replicates within years within samples).  We build up a sequence of
mean tracheidograms for each year, each standardized to the mean
cell-file length for that year, keeping it sorted by the dates for the
years.


make-mean-chronologies

(define make-mean-chronologies (lambda (measurements)
... Full Code ... )
Make chronologies of mean tracheidograms for all sites.
The collection nested association lists, <measurements>, should contain
corrected cell size and wall thickness data, with measurements of
replicated cell files nested within dated rings, in turn nested within
samples, which are nested within the site list entries.  The list of
chronologies is another nested association list, associating each
year with a mean cell file length, and two lists of cell size and wall
thickness values, both standardized to this length, then pairing the
sorted list of years with the relevant site ID.



Tabulating chronologies of mean tracheidograms



Helper functions for tabulation


pad-years

(define pad-years (lambda (chron-yrs ex-yrs)
... Full Code ... )
Insert placeholders for missing years.  The association list
<chron-yrs> represents part of a mean tracheidogram chronology, each
entry pairing dates with the mean cell file lengths and lists of mean
values; the association list <ex-yrs> is the stack of already
processed results.  If the next chronology entry does not have the
date immediately following that of the entry at the top of the stack,
we insert placeholders for the missing years, with zero lengths and
null lists of mean values.


tabulate-nf

(define tabulate-nf (lambda (todo tabulated buffer)
... Full Code ... )
Tabulate the mean cell file lengths.
Given a list of mean cell file lengths, <todo>, a stack of previously
tabulated rows, <tabulated>, and stack of partially completed column
values, <buffer>, we push the next length onto the row buffer and
update the stacks when the row and table lengths are reached.


tabulate-trach

(define tabulate-trach (lambda (seen unseen table row valid)
... Full Code ... )
Tabulate the mean cell dimension values.
We build up the table from a list of value lists, one list of values
for each year.  The <seen> list holds the stack of lists that have
already been processed for the current table row, <unseen> those yet
to be processed, <table> the stack of already assembled table rows,
<row> the stack of current row values, and <valid> a count of the
number of valid values encountered so far in this row.



Tabulate the data for a single site


tabulate-mean-chronology

(define tabulate-mean-chronology (lambda (chron)
... Full Code ... )
Tabulate the start year, number of years, and mean values for one site.
The association list <chron> holds the mean chronology data, pairing
dates with mean tracheidograms represented as lists of mean wall thickness
and cell size values.  We ignore the wall thicknesses, and only tabulate
the cell sizes at the moment.



Write the .mtr files a set of mean tracheidogram chronologies.


require-extension

... Source Code ...
The formatting needs specific field sizes, hence a Common Lisp-style format.


write-mtr-file

(define write-mtr-file (lambda (site-id site-chron)
... Full Code ... )
Write a single site to a single .mtr file.


write-mtr

(define write-mtr (lambda (meanchron)
... Full Code ... )
Write all the sites to their individual .mtr files.


require-extension

... Source Code ...


process-args

(define process-args (lambda (arglist raw-chron)
... Full Code ... )
Read all the input files, write all the output files.


process-args

... Source Code ...


Code

makevec

Index
Turn lists of wall measurements <wall> and cell sizes <cell> into vectors. 
(define makevec
  (lambda (wall cell)
    (list (list->vector (reverse wall))
          (list->vector (reverse cell)))))

corr-measures

Index
Turn raw lumen diameters and double cell wall thicknesses into cell sizes.
Given <raw> a list of unprocessed raw measurements of the form
((current-cell-wall current-lumen) (next-cell-wall next-lumen) ...)
<prev>, the previously processed raw measurement as the list
(previous-double-cell-wall previous-lumen)
<wall>, a stack of accumulated corrected wall thicknesses and
<cell>, a stack of accumulated cell sizes, we recurse down the list
of of unprocessed measurements, maintaining the corrected measurement
lists, and finally return them as a list of two vectors.  In the simplest
case the corrected cell size would be simply the sum of the lumen
diameter and the mean of the preceding and following double cell wall
thicknesses, but missing values and end cases require a case analysis
and some crude approximations. 
(define corr-measures
  (lambda (raw prev wall cell)
    (cond ((null? raw)
           (if (or (null? prev)
                   (eq? (car prev) 'missing)
                   (eq? (cadr prev) 'missing))
               (makevec wall cell)
               (makevec (cons (car prev) wall)
                        (cons (+ (car prev) (cadr prev)) cell))))
          ((or (null? prev) (eq? (cadr prev) 'missing))  ; No previous lumen
           (corr-measures (cdr raw) (car raw) wall cell))
          ((eq? (car prev) 'missing)    ; No previous wall
           (if (eq? (caar raw) 'missing)  ; No current wall
               (corr-measures (cdr raw) (car raw) wall cell)
               (corr-measures (cdr raw)     ; Use current wall
                              (car raw)
                              (cons (caar raw) wall)
                              (cons (+ (caar raw) (cadr prev)) cell))))
          ((eq? (caar raw) 'missing)    ; No current wall
           (corr-measures (cdr raw)
                          (car raw)
                          (cons (car prev) wall)
                          (cons (+ (car prev) (cadr prev)) cell)))
          (else
           (let ((avg-wall
                  (if (eq? (cadar raw) 'missing)   ; No current lumen
                      (+ (/ (car prev) 2) (caar raw))
                      (/ (+ (car prev) (caar raw)) 2))))
             (corr-measures (cdr raw)
                            (car raw)
                            (cons avg-wall wall)
                            (cons (+ avg-wall (cadr prev)) cell)))))))

correct-walls

Index
Build a collection nested association lists holding cell and wall sizes.
From <obs-a-list>, the nested association lists containing the raw
observed lumen diameters and double cell wall thicknesses, we construct
the corresponding collection of lists holding vectors of corrected
cell size and wall dimensions.  
(define correct-walls
  (lambda (obs-a-list)
    (letrec
        ((corr-sites
          (lambda (done todo)
            (cond ((null? todo) done)
                  ((eq? (caar todo) 'err)
                   (corr-sites (cons (car todo) done) (cdr todo)))
                  (else (corr-sites (cons (process-tag (car todo) corr-samp)
                                          done)
                                    (cdr todo))))))
         (corr-samp
          (lambda (samp-list)
            (sort-map samp-list < corr-year)))
         (corr-year
          (lambda (year-list)
            (sort-map year-list < corr-cfile)))
         (corr-cfile
          (lambda (cfile-list)
            (sort-map cfile-list < corr-cell)))
         (corr-cell
          (lambda (measures)
            (corr-measures (map cdr (sort-map measures < car)) '() '() '()))))
      (corr-sites '() obs-a-list))))

require-extension
Index
The formatting needs specific field sizes, hence a Common Lisp-style format.
(require-extension format)

write-mtr-file

Index
Write a single site to a single .mtr file.
(define write-mtr-file
  (lambda (site-id site-chron)
    (with-output-to-file (string-append site-id ".mtr")
      (lambda ()
        (format #t
                "~{~{~4D~}~%~{~6{~12D~}~&~}~{~{~6,1F      ~}~&~}~}"
                (tabulate-mean-chronology site-chron))))))

write-mtr

Index
Write all the sites to their individual .mtr files.
(define write-mtr
  (lambda (meanchron)
    (for-each (lambda (site-entry)
                (write-mtr-file (car site-entry) (cdr site-entry)))
              meanchron)))

require-extension
Index
(require-extension utils)

process-args

Index
Read all the input files, write all the output files.
(define process-args
  (lambda (arglist raw-chron)
    (if (null? arglist)
        (write-mtr (make-mean-chronologies (correct-walls raw-chron)))
        (let*
            ((arg (car arglist))
             (chronid (call-with-values 
                        (lambda () (decompose-pathname arg))
                        (lambda (dir base ext) base))))
          (if (not chronid)
              (process-args (cdr arglist)
                            raw-chrons)
              (process-args (cdr arglist)
                            (merge-obs raw-chron chronid arg)))))))

process-args
Index
(process-args (cdr (argv)) '())

restore

Index
Append the list <lis> to a reversed version of the list <st>.
More or less equivalent to (append (reverse st) lis).
(define restore
  (lambda (st lis)
    (if (null? st)
        lis
        (restore (cdr st) (cons (car st) lis)))))

update-assoc

Index
Generate a new updated version of the association list <a-list>.
If (assoc item a-list) would recover the a pairing of <item> with some data,
we replace this with a new pairing of <item> and the results of applying
the function <update-op> to those data, but otherwise prepend a new entry
associating <item> with the results of applying <update-op> to the empty
list.
(define update-assoc
  (lambda (item a-list update-op)
    (letrec
        ((update-cycle
          (lambda (a-nonmatch a-residue)
            (cond ((null? a-residue)
                   (cons (cons item (update-op '())) (reverse a-nonmatch)))
                  ((equal? item (caar a-residue))
                   (cons (cons item (update-op (cdar a-residue)))
                         (restore a-nonmatch (cdr a-residue))))
                  (else
                   (update-cycle (cons (car a-residue) a-nonmatch) (cdr a-residue)))))))
      (update-cycle '() a-list))))

a-merge

Index
Merge new data with an existing association list <a-list>.
Given the pair <a-item>, already a possible association list entry, use the
<merge-op> function to merge it with any matching entry in the list, or
otherwise add it.
(define a-merge
  (lambda (a-item a-list merge-op)
    (update-assoc (car a-item)
                  a-list
                  (lambda (info) (merge-op (cdr a-item) info)))))

process-tag

Index
Generate a modified version of the association list entry <a-item>.
Use the function <proc-op> to transform the data in the cdr of the existing
entry, keeping the car field unchanged.
(define process-tag
  (lambda (a-item proc-op)
    (cons (car a-item) (proc-op (cdr a-item)))))

sort-map

Index
Process the association list <a-list>, sorting the modified entries.
Apply a processing function <proc-op> to the data in the cdr field of all
the entries in the old list, maintaining the car field of the entries,
but using this as a sort key to determine the order in the new list,
through the comparison predicate <rel-op?>.
(define sort-map
  (lambda (a-list rel-op? proc-op)
    (letrec
        ((insert-sort
          (lambda (item dest)
            (letrec
                ((insert-help
                  (lambda (before after)
                    (cond ((null? before)
                           (restore after
                                    (list (process-tag item proc-op))))
                          ((rel-op? (caar before) (car item))
                           (insert-help (cdr before)
                                        (cons (car before) after)))
                          (else
                           (restore after
                                    (cons (process-tag item proc-op)
                                          before)))))))
              (insert-help dest '()))))
         (map-help
          (lambda (todo done)
            (if (null? todo)
                done
                (map-help (cdr todo) (insert-sort (car todo) done))))))
      (map-help a-list '()))))

extract-item

Index
Read a sequence of five values from a data file.
Given <in-obj>, a pair consisting of a list of previously read data values
and an input port, generate an updated version with the next sequence of five
values read from the file and prepended to the list.  A premature end-of-file
condition may leave the sequence incomplete.
(define extract-item
  (lambda (in-obj)
    (letrec
        ((give-me-n
          (lambda (st n fport)
            (if (= n 0)
                (cons (reverse st) fport)
                (let ((nth (read fport)))
                  (if (eof-object? nth)
                      (cons nth (close-input-port fport))
                      (give-me-n (cons nth st) (- n 1) fport)))))))
      (give-me-n '() 5 (cdr in-obj)))))

merge-obs

Index
Read measurements from the file named by the string <fname>.
The new data are merged with any already present in the collection of
nested association lists, <top-a-list>, using the site identification
literal <site-id>.  Missing measurements get recoded from negative numbers
to a literal value: missing.
(define merge-obs
  (lambda (top-a-list site-id fname)
    (letrec
        ((extract-input
          (lambda (in-obj)
            (let
                ((new-input (extract-item in-obj)))
              (cond ((eof-object? (car new-input)) new-input)
                    ((equal? (car new-input) '(Sample File N Wall Lumen))
                     (extract-input new-input))
                    (else
                     (cons (decode-item (car new-input)) (cdr new-input)))))))
         (decode-item
          (lambda (item)
            (if (and (list? item) (= (length item) 5))
                (let ((year-file (cadr item))
                      (cell-num (caddr item))
                      (raw-wall (cadddr item))
                      (raw-lumen (car (cddddr item))))
                  (if (and (integer? year-file)
                           (integer? cell-num)
                           (real? raw-wall)
                           (real? raw-lumen))
                      (list
                       (car item)
                       (quotient year-file 1000)
                       (remainder year-file 1000)
                       cell-num
                       (if (>= raw-wall 0) raw-wall 'missing)
                       (if (>= raw-lumen 0) raw-lumen 'missing))
                      (cons 'err (cons 'in-values (cons fname item)))))
                (cons 'err (cons 'in-format (cons fname item))))))
         (merge-cell
          (lambda (measure cellinfo)
            (a-merge measure cellinfo cons)))
         (merge-cfile
          (lambda (cell cfileinfo)
            (a-merge cell cfileinfo merge-cell)))
         (merge-year
          (lambda (cfile yearinfo)
            (a-merge cfile yearinfo merge-cfile)))
         (merge-input
          (lambda (in-obj sampinfo)
            (let ((next-input (extract-input in-obj)))
              (if (eof-object? (car next-input))
                  sampinfo
                  (merge-input
                   next-input
                   (a-merge (car next-input) sampinfo merge-year)))))))
      (update-assoc site-id
                    top-a-list
                    (lambda (sampinfo)
                      (merge-input (cons '() (open-input-file fname))
                                   sampinfo))))))

total-cell-counts

Index
Build a collection of site-specific annual cell number totals.
Given <size-a-list>, the collection of nested association lists that contains
the corrected cell size and wall thickness vectors, we generate a new
association list pairing site IDs with chronologies of cell file totals.
These chronologies are unsorted association lists where each entry is
of the form
(date total-number-of-cell-files . total-number-of-cells)
with the totals accumulated over all the replicated cell files for all the
samples at a site.  Trivially, the ratio of the totals gives the mean number
of cells in the radial file of tracheids for a particular date at a
particular site.
(define total-cell-counts
  (lambda (size-a-list)
    (letrec
        ((total-cfile-n
          (lambda (n-totals cfile-list)
            (if (null? cfile-list)
                n-totals
                (total-cfile-n (cons (+ 1 (car n-totals))
                                     (+ (vector-length (caddar cfile-list))
                                        (cdr n-totals)))
                               (cdr cfile-list)))))
         (total-annual-n
          (lambda (yr-totals sample-yr)
            (if (null? sample-yr)
                yr-totals
                (let
                    ((cfile-totals (total-cfile-n '(0 . 0) (cdr sample-yr))))
                  (update-assoc (car sample-yr)
                                yr-totals
                                (lambda (old-total)
                                  (if (null? old-total)
                                      cfile-totals
                                      (cons (+ (car cfile-totals)
                                               (car old-total))
                                            (+ (cdr cfile-totals)
                                               (cdr old-total))))))))))
         (total-sample-n
          (lambda (yr-totals yr-list)
            (if (null? yr-list)
                yr-totals
                (total-sample-n (total-annual-n yr-totals (car yr-list))
                                (cdr yr-list)))))
         (total-site-n
          (lambda (yr-totals samples)
            (if (null? samples)
                yr-totals
                (total-site-n (total-sample-n yr-totals (cdar samples))
                              (cdr samples)))))
         (total-site-yr-n
          (lambda (sample-list)
            (total-site-n '() sample-list)))
         )
      (sort-map size-a-list string<? total-site-yr-n))))

standadize-measurements

Index
Make a list of length <n> from an arbitrary-length measurement vector <c>.
In real conifer wood the number of cells in a radial file of tracheids will
vary, not only from tree to tree and ring to ring, but also between different
files of cells within a ring.  We must account for these variations in length
to turn multiple series of observed cell measurements into summaries, and the
simplest way to do this, long used at the Institute of Forest, uses weighted
means to map the observations onto a fixed-length vector.
(define standadize-measurements
  (lambda (c n)
    (letrec
        ((nc (vector-length c))
         (interp
          (lambda (t i j)
            (letrec
                ((interp-help
                  (lambda (m j s)
                    (if (<= m (* n (+ j 1)))
                        (interp (cons (/ (+ s
                                            (* (vector-ref c j)
                                               (- m (* n j))))
                                         nc)
                                      t)
                                (+ i 1)
                                (+ j 1))
                        (interp-help m
                                     (+ j 1)
                                     (+ s (* n (vector-ref c j))))))))
              (cond ((>= i n) (reverse t))
                    ((<= (* n j) (* nc i))
                     (interp t i (+ j 1)))
                    ((>= (* n j) (* nc (+ i 1)))
                     (interp (cons (vector-ref c (- j 1)) t) (+ i 1) j))
                    (else
                     (interp-help (* nc (+ i 1))
                                  j
                                  (* (vector-ref c (- j 1))
                                     (- (* n j) (* nc i))))))))))
      (interp '() 0 0))))

take-means

Index
Generate the mean tracheidogram for a particular year.
Given a cell-file length, <n>, a number giving the date of the ring
on whatever arbitrary time-scale is in use, <yr-date>, and nested
association lists holding the corrected cell wall and total cell sizes,
<measurements>, we extract all the size data from all the samples
and replicated cell files for that date, standardize each individual
cell file to have a length of <n>, accumulate them into a running totals
of standardized cell measurements, and finally divide by the total number
of cell files for the mean tracheidograms.  Two lists represent the
tracheidograms, one of standardized cell wall thicknesses and the other of
standardized total cell sizes, and we redundantly associate them with
the length used in the standardization.
(define take-means
  (lambda (n yr-date measurements)
    (letrec
        ((init-totals
          (lambda (i initlist)
            (if (zero? i)
                initlist
                (init-totals (- i 1) (cons 0 initlist)))))
         (initial-totals
          (list 0 (init-totals n '()) (init-totals n '())))
         (update-totals
          (lambda (cfile runningtotals)
            (let*
                ((running-count (car runningtotals))
                 (running-walls (cadr runningtotals))
                 (running-sizes (caddr runningtotals))
                 (std-walls (standadize-measurements (car cfile) n))
                 (std-sizes (standadize-measurements (cadr cfile) n)))
              (list (+ 1 running-count)
                    (map + std-walls running-walls)
                    (map + std-sizes running-sizes)))))
         (cfile-iterate
          (lambda (cfile-list accum-total)
            (if (null? cfile-list)
                       accum-total
                       (cfile-iterate (cdr cfile-list)
                                      (update-totals (cdar cfile-list)
                                                     accum-total)))))
         (samp-iterate
          (lambda (samp-list accum-total)
            (if (null? samp-list)
                accum-total
                (samp-iterate (cdr samp-list)
                              (cfile-iterate (cdr (assoc yr-date
                                                         (cdar samp-list)))
                                             accum-total)))))
         (final-totals (samp-iterate measurements initial-totals)))
      (cons yr-date
            (let*
                ((final-count (car final-totals))
                 (final-walls (cadr final-totals))
                 (final-sizes (caddr final-totals))
                 (total-to-mean
                  (lambda (tot)
                    (/ tot final-count))))
              (list n
                    (map total-to-mean final-walls)
                    (map total-to-mean final-sizes)))))))

chron-build

Index
Build a chronology of mean tracheidograms for a single site.
The association list <yrlist> must already contain entries pairing the
date of each year in the chronology with the total number of cell
files measured for that year at that site and the sum of these cell
file lengths; <measurelist> must contain the corresponding cell size
and wall thickness data within nested association lists (cell-file
replicates within years within samples).  We build up a sequence of
mean tracheidograms for each year, each standardized to the mean
cell-file length for that year, keeping it sorted by the dates for the
years.
(define chron-build
  (lambda (yrlist measurelist)
    (letrec
        ((insert-yr
          (lambda (yr-date yr-cftotals dest)
            (letrec
                ((means
                  (take-means (inexact->exact (round (/ (cdr yr-cftotals)
                                                        (car yr-cftotals))))
                              yr-date
                              measurelist))
                 (insert-iterate
                  (lambda (before after)
                    (cond ((null? before)
                           (restore after (list means)))
                          ((< (caar before) yr-date)
                           (insert-iterate (cdr before)
                                           (cons (car before) after)))
                          (else
                           (restore after
                                    (cons means before)))))))
              (insert-iterate dest '()))))
         (yr-iterate
          (lambda (todo done)
            (if (null? todo)
                done
                (yr-iterate (cdr todo)
                            (insert-yr (caar todo) (cdar todo) done))))))
      (yr-iterate yrlist '()))))

make-mean-chronologies

Index
Make chronologies of mean tracheidograms for all sites.
The collection nested association lists, <measurements>, should contain
corrected cell size and wall thickness data, with measurements of
replicated cell files nested within dated rings, in turn nested within
samples, which are nested within the site list entries.  The list of
chronologies is another nested association list, associating each
year with a mean cell file length, and two lists of cell size and wall
thickness values, both standardized to this length, then pairing the
sorted list of years with the relevant site ID.
(define make-mean-chronologies
  (lambda (measurements)
    (letrec
        ((totals (total-cell-counts measurements))
         (site-mean
          (lambda (sitelist chronologies)
            (if (null? sitelist)
                (reverse chronologies)
                (letrec
                    ((site-id (caar sitelist))
                     (site-yrs (cdar sitelist))
                     (measurement-yrs (cdr (assoc site-id measurements)))
                     (mean-yrs (chron-build site-yrs measurement-yrs)))
                  (site-mean (cdr sitelist)
                             (cons (cons site-id mean-yrs)
                                   chronologies)))))))
      (site-mean totals '()))))

pad-years

Index
Insert placeholders for missing years.  The association list
<chron-yrs> represents part of a mean tracheidogram chronology, each
entry pairing dates with the mean cell file lengths and lists of mean
values; the association list <ex-yrs> is the stack of already
processed results.  If the next chronology entry does not have the
date immediately following that of the entry at the top of the stack,
we insert placeholders for the missing years, with zero lengths and
null lists of mean values.
(define pad-years
  (lambda (chron-yrs ex-yrs)
    (cond ((null? chron-yrs)
           (reverse ex-yrs))
          ((or (null? ex-yrs) (= 1 (- (caar chron-yrs) (caar ex-yrs))))
           (pad-years (cdr chron-yrs)
                      (cons (car chron-yrs) ex-yrs)))
          (else
           (pad-years chron-yrs
                      (cons (list (+ 1 (caar ex-yrs)) 0 '() '())
                            ex-yrs))))))

tabulate-nf

Index
Tabulate the mean cell file lengths.
Given a list of mean cell file lengths, <todo>, a stack of previously
tabulated rows, <tabulated>, and stack of partially completed column
values, <buffer>, we push the next length onto the row buffer and
update the stacks when the row and table lengths are reached.
(define tabulate-nf
  (lambda (todo tabulated buffer)
    (cond
     ((null? todo)
      (reverse (if (null? buffer)
                   tabulated
                   (cons (reverse buffer) tabulated))))
     ((= (length buffer) 5)
      (tabulate-nf (cdr todo)
                   (cons (reverse (cons (cadar todo) buffer))
                         tabulated)
                   '()))
     (else
      (tabulate-nf (cdr todo)
                   tabulated
                   (cons (cadar todo) buffer))))))

tabulate-trach

Index
Tabulate the mean cell dimension values.
We build up the table from a list of value lists, one list of values
for each year.  The <seen> list holds the stack of lists that have
already been processed for the current table row, <unseen> those yet
to be processed, <table> the stack of already assembled table rows,
<row> the stack of current row values, and <valid> a count of the
number of valid values encountered so far in this row.
(define tabulate-trach
  (lambda (seen unseen table row valid)
    (cond
     ((null? unseen) 
      (if (= 0 valid)
          (reverse table)
          (tabulate-trach '()
                          (reverse seen)
                          (cons (reverse row) table)
                          '()
                          0)))
     ((null? (car unseen))
      (tabulate-trach (cons '() seen)
                      (cdr unseen)
                      table
                      (cons 0 row)
                      valid))
     (else
      (tabulate-trach (cons (cdar unseen) seen)
                      (cdr unseen)
                      table
                      (cons (caar unseen) row)
                      (+ 1 valid))))))

tabulate-mean-chronology

Index
Tabulate the start year, number of years, and mean values for one site.
The association list <chron> holds the mean chronology data, pairing
dates with mean tracheidograms represented as lists of mean wall thickness
and cell size values.  We ignore the wall thicknesses, and only tabulate
the cell sizes at the moment.
(define tabulate-mean-chronology
  (lambda (chron)
    (let*
        ((padded-chron (pad-years chron '()))
         (nf (tabulate-nf padded-chron '() '()))
         (trach (tabulate-trach '() (map cadddr padded-chron) '() '() 0)))
      (list (list (caar padded-chron) (length  padded-chron))
            nf
            trach))))