diff options
Diffstat (limited to 'rmdsff.scm')
-rw-r--r-- | rmdsff.scm | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/rmdsff.scm b/rmdsff.scm new file mode 100644 index 0000000..b9ee7eb --- /dev/null +++ b/rmdsff.scm @@ -0,0 +1,317 @@ +;;;; "rmdsff.scm" Space-filling functions and their inverses. +;;; Copyright (C) 2013, 2014 Aubrey Jaffer +; +;Permission to copy this software, to modify it, to redistribute it, +;to distribute modified versions, and to use it for any purpose is +;granted, subject to the following restrictions and understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warranty or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'array) + +;;@code{(require 'space-filling)} +;;@ftindex space-filling + +;;@ The algorithms and cell properties are described in +;;@url{http://people.csail.mit.edu/jaffer/Geometry/RMDSFF.pdf} + +;;; A cell is an object encapsulating the information about a +;;; Hamiltonian path on a rectangular grid of side^rank nodes. +;;; Here are the accessors for a cell: +(define cell-type caar) +(define cell-side cadar) +(define cell-rank caddar) +(define (cell-index cell crds) (apply array-ref (cadadr cell) crds)) +(define (cell-coords cell t) (vector-ref (caadr cell) t)) +(define (cell-entry cell t) (vector-ref (caddr cell) t)) +(define (cell-exit cell t) (vector-ref (cadddr cell) t)) +(define (cell-rotation cell t) (vector-ref (cadddr (cdr cell)) t)) + +;;@args type rank side precession +;;@args type rank side +;;@args type rank +;; +;;@1 must be the symbol @code{diagonal}, @code{adjacent}, or +;;@code{centered}. @2 must be an integer larger than 1. @3, if +;;present, must be an even integer larger than 1 if @1 is +;;@code{adjacent} or an odd integer larger than 2 otherwise; @3 +;;defaults to the smallest value. @4, if present, must be an integer +;;between 0 and @3^@2-1; it is relevant only when @1 is +;;@code{diagonal} or @code{centered}. +;; +;;@args Hamiltonian-path-vector precession +;;@args Hamiltonian-path-vector +;; +;;@1 must be a vector of @var{side}^@var{rank} lists of @var{rank} of +;;integers encoding the coordinate positions of a Hamiltonian path on +;;the @var{rank}-dimensional grid of points starting and ending on +;;corners of the grid. The starting corner must be the origin +;;(all-zero coordinates). If the side-length is even, then the ending +;;corner must be non-zero in only one coordinate; otherwise, the +;;ending corner must be the furthest diagonally opposite corner from +;;the origin. +;; +;;@code{make-cell} returns a data object suitable for passing as the +;;first argument to @code{integer->coordinates} or +;;@code{coordinates->integer}. +(define (make-cell arg1 . args) + (define (make-serpentine-path rank s) + (let loop ((path '(())) (rnk (+ -1 rank))) + (if (negative? rnk) path + (loop (let iloop ((seq '()) (sc (+ -1 s))) + (if (negative? sc) + seq + (iloop (append (map (lambda (coords) (cons sc coords)) + (if (odd? sc) (reverse path) path)) + seq) + (+ -1 sc)))) + (+ -1 rnk))))) + (if (list? arg1) (set! arg1 (list->vector arg1))) + (cond + ((> (length args) 3) + (slib:error 'make-cell 'extra 'arguments 'not 'handled args)) + ((vector? arg1) + (let ((path arg1) + (precession (and (not (null? args)) (car args)))) + (define frst (vector-ref path 0)) + (define len (vector-length path)) + (define s-1 (apply max (apply append (vector->list path)))) + (let* ((len-1 (+ -1 len)) + (last (vector-ref path len-1)) + (d (length frst))) + ;; returns index of first non-zero in LST + (define (first-non-zero lst) + (define pos (lambda (n lst) + (cond ((zero? (car lst)) (pos (+ 1 n) (cdr lst))) + (else n)))) + (pos 0 lst)) + ;; returns the traversal direction of the sub-path. + (define (U_e N t) + (if (= t len-1) + (map - (vector-ref path t) (vector-ref path (- t 1))) + (let ((dH_i+1 (map - (vector-ref path (+ t 1)) (vector-ref path t)))) + (define dotpr (apply + (map * N dH_i+1))) + (define csum (apply + dH_i+1)) + (if (or (and (zero? dotpr) (= 1 csum)) (= dotpr csum -1)) + dH_i+1 + (map - (vector-ref path t) (vector-ref path (- t 1))) + )))) + (define (flip-direction dir cdir) + (map (lambda (px c) (modulo (+ px c) 2)) dir cdir)) + (define (path-diag? path) + (define prev frst) + (for-each (lambda (lst) + (if (not (= d (length lst))) + (slib:error 'non-uniform 'ranks frst lst))) + (vector->list path)) + (for-each (lambda (cs) + (if (not (= 1 (apply + (map abs (map - prev cs))))) + (slib:error 'bad 'step prev cs)) + (set! prev cs)) + (cdr (vector->list path))) + (cond ((not (zero? (apply + frst))) (slib:error 'strange 'start frst)) + ((not (= d (length last))) (slib:error 'non-uniform 'lengths path)) + ((apply = s-1 last) #t) + ((and (= s-1 (apply + last))) #f) + (else (slib:error 'strange 'net-travel frst last)))) + (define diag? (path-diag? path)) + (define entries (make-vector len (vector-ref path 0))) + (define exits (make-vector + len (if diag? + (map (lambda (c) (quotient c s-1)) last) + (vector-ref path 1)))) + (define rotations (make-vector len 0)) + (define ipath (apply make-array + (A:fixZ32b -1) + (vector->list (make-vector d (+ 1 s-1))))) + (define ord 0) + (for-each (lambda (coords) + (apply array-set! ipath ord coords) + (set! ord (+ 1 ord))) + (vector->list path)) + (let lp ((t 1) + (prev-X (if diag? + (map (lambda (c) 1) (vector-ref entries 0)) + (vector-ref path 1)))) + (cond ((> t len-1) + (if (not (equal? (vector-ref exits len-1) + (map (lambda (c) (quotient c s-1)) last))) + (slib:warn (list (if diag? 'diagonal 'adjacent) + (+ 1 s-1) d precession) + 'bad 'last 'exit + (vector-ref exits len-1) 'should 'be + (map (lambda (c) (quotient c s-1)) last))) + (let ((ord 0) + (h (first-non-zero last))) + (for-each + (lambda (coords) + (vector-set! rotations + ord + (modulo + (cond ((not diag?) + (- h (first-non-zero + (map - + (vector-ref exits ord) + (vector-ref entries ord))))) + (precession (+ precession ord)) + (else 0)) + d)) + (set! ord (+ 1 ord))) + (vector->list path))) + (list (list (if diag? 'diagonal 'adjacent) + (+ 1 s-1) + d + precession) + (list path ipath) + entries + exits + rotations + )) + (else + (let ((N (flip-direction + prev-X + (map - (vector-ref path t) + (vector-ref path (- t 1)))))) + (define X (if diag? + (map (lambda (tn) (- 1 tn)) N) + (flip-direction N (U_e N t)))) + (vector-set! entries t N) + (vector-set! exits t X) + (lp (+ 1 t) X)))))))) + ((< (car args) 2) + (slib:error 'make-cell 'rank 'too 'small (car args))) + (else + (case arg1 + ((center centered) + (let ((cell (make-cell (make-serpentine-path + (car args) + (if (null? (cdr args)) 3 (cadr args))) + (if (= 3 (length args)) (caddr args) #f)))) + (if (not (eq? 'diagonal (cell-type cell))) + (slib:error 'make-cell 'centered 'must 'be 'diagonal (car cell))) + (set-car! (car cell) 'centered) + cell)) + ((diagonal opposite) + (make-cell (make-serpentine-path + (car args) + (if (null? (cdr args)) 3 (cadr args))) + (if (= 3 (length args)) (caddr args) #f))) + ((adjacent) + (make-cell (make-serpentine-path + (car args) + (if (null? (cdr args)) 2 (cadr args))) + (if (= 3 (length args)) (caddr args) #f))) + (else + (slib:error 'make-cell 'unknown 'cell 'type arg1)))))) + +;;@ Hilbert, Peano, and centered Peano cells are generated +;;respectively by: +;;@example +;;(make-cell 'adjacent @var{rank} 2) ; Hilbert +;;(make-cell 'diagonal @var{rank} 3) ; Peano +;;(make-cell 'centered @var{rank} 3) ; centered Peano +;;@end example + +;; Positive k rotates left +(define (rotate-list lst k) + (define len (length lst)) + (cond ((<= len 1) lst) + (else + (set! k (modulo k len)) + (if (zero? k) + lst + (let ((ans (list (car lst)))) + (do ((ls (cdr lst) (cdr ls)) + (tail ans (cdr tail)) + (k (+ -1 k) (+ -1 k))) + ((<= k 0) + (append ls ans)) + (set-cdr! tail (list (car ls))))))))) + +;;@ In the conversion procedures, if the cell is @code{diagonal} or +;;@code{adjacent}, then the coordinates and scalar must be nonnegative +;;integers. If @code{centered}, then the integers can be negative. + +;;@body +;;@0 converts the integer @2 to a list of coordinates according to @1. +(define (integer->coordinates cell u) + (define umag (case (cell-type cell) + ((centered) (* (abs u) 2)) + (else u))) + (define d (cell-rank cell)) + (define s (cell-side cell)) + (let* ((s^d (expt s d)) + (s^d^2 (expt s^d d))) + (define (align V t sde) + (map (lambda (Vj Nj) (if (zero? Nj) Vj (- sde Vj))) + (rotate-list V (cell-rotation cell t)) + (cell-entry cell t))) + (define (rec u m w) + (if (positive? w) + (let ((t (quotient u m))) + (map + + (map (lambda (y) (* y w)) (cell-coords cell t)) + (align (rec (modulo u m) (quotient m s^d) (quotient w s)) + t + (- w 1)))) + (cell-coords cell 0))) + (do ((uscl 1 (* uscl s^d^2)) + (cscl 1 (* cscl s^d))) + ((> uscl umag) + (case (cell-type cell) + ((centered) + (let ((cscl/2 (quotient cscl 2))) + (map (lambda (c) (- c cscl/2)) + (rec (+ u (quotient uscl 2)) + (quotient uscl s^d) + (quotient cscl s))))) + (else (rec u (quotient uscl s^d) (quotient cscl s)))))))) + +;;@body +;;@0 converts the list of coordinates @2 to an integer according to @1. +(define (coordinates->integer cell V) + (define maxc (case (cell-type cell) + ((centered) (* 2 (apply max (map abs V)))) + (else (apply max V)))) + (define d (cell-rank cell)) + (define s (cell-side cell)) + (let* ((s^d (expt s d)) + (s^d^2 (expt s^d d))) + (define (align^-1 V t sde) + (rotate-list (map (lambda (Vj Nj) (if (zero? Nj) Vj (- sde Vj))) + V + (cell-entry cell t)) + (- (cell-rotation cell t)))) + (define (rec u V w) + (if (positive? w) + (let ((dig (cell-index cell (map (lambda (c) (quotient c w)) V)))) + (rec (+ dig (* s^d u)) + (align^-1 (map (lambda (cx) (modulo cx w)) V) + dig + (- w 1)) + (quotient w s))) + u)) + (do ((uscl 1 (* uscl s^d^2)) + (cscl 1 (* cscl s^d))) + ((> cscl maxc) + (case (cell-type cell) + ((centered) + (let ((cscl/2 (quotient cscl 2))) + (- (rec 0 + (map (lambda (c) (+ c cscl/2)) V) + (quotient cscl s)) + (quotient uscl 2)))) + (else (rec 0 V (quotient cscl s)))))))) + +;;@var{coordinates->integer} and @var{integer->coordinates} are +;;inverse functions when passed the same @var{cell} argument. |