private/bits.rkt
#lang racket/base

(require racket/contract
         racket/list)

(provide
 (contract-out
  [bits->bytes (-> (listof boolean?) bytes?)]
  [bits->integer (-> (listof boolean?) exact-nonnegative-integer?)]
  [bits->string (-> (listof boolean?) string?)]
  [bytes->bits (-> bytes? (listof boolean?))]
  [integer->bits (->* (exact-nonnegative-integer?)
                      ((or/c #f exact-nonnegative-integer?))
                      (listof boolean?))]))

(define (bits->bytes bits)
  (list->bytes
   (let loop ([n (length bits)]
              [bits bits])
     (cond
       [(zero? n) '()]
       [(< n 8) (list (arithmetic-shift (bits->integer bits) (- 8 n)))]
       [else (let-values ([(byte bits) (split-at bits 8)])
               (cons (bits->integer byte)
                     (loop (- n 8) bits)))]))))

(define (bits->integer bits)
  (for/fold ([a 0])
            ([b (in-list bits)])
    (bitwise-ior (arithmetic-shift a 1)
                 (if b 1 0))))

(define (bits->string bits)
  (define (convert bits n)
    (build-string n
                  (lambda (i)
                    (if (list-ref bits i) #\1 #\0))))
  (let loop ([n (length bits)]
             [bits bits]
             [s ""])
    (if (<= n 8)
        (string-append s (convert bits n))
        (loop (- n 8)
              (drop bits 8)
              (string-append s (convert bits 8) " ")))))
   
(define (bytes->bits bytes)
  (append*
   (map (lambda (b)
          (integer->bits b 8))
        (bytes->list bytes))))

(define (integer->bits m [length #f])
  (let ([n (or length
               (integer-length m))])
    (build-list n
                (lambda (b)
                  (bitwise-bit-set? m (- n b 1))))))