; "phil-spc.scm": Peano-Hilbert space filling mapping
; Copyright (c) 2003 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 warrantee 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 'logical)

;;@code{(require 'hilbert-fill)}
;;@ftindex hilbert-fill
;;
;;@noindent
;;@cindex Peano
;;@cindex Hilbert
;;@cindex Space-Filling
;;The @dfn{Peano-Hilbert Space-Filling Curve} is a one-to-one mapping
;;between a unit line segment and an @var{n}-dimensional unit cube.
;;
;;@noindent
;;The integer procedures map the non-negative integers to an
;;arbitrarily large @var{n}-dimensional cube with its corner at the
;;origin and all coordinates are non-negative.
;;
;;@noindent
;;For any exact nonnegative integers @var{scalar} and @var{rank},
;;
;;@example
;;(= @var{scalar} (hilbert-coordinates->integer
;;           (integer->hilbert-coordinates @var{scalar} @var{rank})))
;;                                       @result{} #t
;;@end example

;;@body
;;Returns a list of @2 integer coordinates corresponding to exact
;;non-negative integer @1.  The lists returned by @0 for @1 arguments
;;0 and 1 will differ in the first element.
(define (integer->hilbert-coordinates scalar rank)
  (define ndones (logical:ones rank))
  (define rank*nbits
    (let ((rank^2 (* rank rank)))
      (* (quotient (+ -1 rank^2 (integer-length scalar)) rank^2)
	 rank^2)))
  (let ((nthbits (quotient (logical:ones rank*nbits) ndones)))
    (define igry (logxor (integer->gray-code scalar) (ash nthbits -1)))
    (do ((bdxn (- rank rank*nbits) (+ rank bdxn))
	 (chnk (logand (ash igry (- rank rank*nbits)) ndones)
	       (logand (ash igry (+ rank bdxn)) ndones))
	 (rotation 0 (modulo (+ (integer-length (logand (- chnk) chnk))
				1 rotation)
			     rank))
	 (flipbit 0 (ash 1 rotation))
	 (bignum 0 (+ (logxor flipbit (logical:rotate chnk rotation rank))
		      (ash bignum rank))))
	((positive? bdxn)
	 (map gray-code->integer (bitwise:delaminate rank bignum))))))

;;@body
;;Returns an exact non-negative integer corresponding to @1, a list
;;of non-negative integer coordinates.
(define (hilbert-coordinates->integer coords)
  (define rank (length coords))
  (define bignum (apply bitwise:laminate (map integer->gray-code coords)))
  (let ((rank*nbits
	 (* (quotient (+ -1 rank (integer-length (apply max coords))) rank)
	    rank rank))
	(ndones (logical:ones rank)))
    (define nthbits (quotient (logical:ones rank*nbits) ndones))
    (define (loop bdxn rotation flipbit scalar)
      (if (positive? bdxn)
	  (gray-code->integer (logxor scalar (ash nthbits -1)))
	  (let ((chnk (logical:rotate
		       (logxor flipbit (logand ndones (ash bignum bdxn)))
		       (- rotation)
		       rank)))
	    (loop (+ rank bdxn)
		  (modulo (+ (integer-length (logand (- chnk) chnk))
			     1 rotation)
			  rank)
		  (ash 1 rotation)
		  (+ chnk (ash scalar rank))))))
    (loop (- rank rank*nbits) 0 0 0)))
