diff --git a/docs/API.md b/docs/API.md
index 3069ff7b..cd15d330 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -32,19 +32,22 @@ This section of the Cyclone API is based on the [R7RS Scheme Specific
Cyclone supports the following [Scheme Requests for Implementation (SRFI)](http://srfi.schemers.org/) libraries. Detailed information is available in the linked SRFI page as well as the provided Cyclone API:
-- [`srfi 1`](api/srfi/1.md) - [List Library](http://srfi.schemers.org/srfi-1/srfi-1.html)
+- [`srfi 1`](api/srfi/1.md) - [List library](http://srfi.schemers.org/srfi-1/srfi-1.html)
- [`srfi 2`](api/srfi/2.md) - [`and-let*`](http://srfi.schemers.org/srfi-2/srfi-2.html)
- [`srfi 8`](api/srfi/8.md) - [`receive`: Binding to multiple values](http://srfi.schemers.org/srfi-8/srfi-8.html) - Included as part of `scheme base`.
- [`srfi 18`](api/srfi/18.md) - [Multithreading support](http://srfi.schemers.org/srfi-18/srfi-18.html)
- [`srfi 27`](api/srfi/27.md) - [Sources of random bits](http://srfi.schemers.org/srfi-27/srfi-27.html)
+- [`srfi 28`](api/srfi/28.md) - [Basic format strings](http://srfi.schemers.org/srfi-28/srfi-28.html)
+- [`srfi 60`](api/srfi/60.md) - [Integers as bits](http://srfi.schemers.org/srfi-60/srfi-60.html)
- [`srfi 69`](api/srfi/69.md) - [Basic hash tables](http://srfi.schemers.org/srfi-69/srfi-69.html)
- [`srfi 106`](api/srfi/106.md) - [Basic socket interface](http://srfi.schemers.org/srfi-106/srfi-106.html)
- [`srfi 111`](api/srfi/111.md) - [Boxes](http://srfi.schemers.org/srfi-111/srfi-111.html)
- [`srfi 113`](api/srfi/113.md) - [Sets and bags](http://srfi.schemers.org/srfi-113/srfi-113.html)
- [`srfi 117`](api/srfi/117.md) - [Mutable queues](http://srfi.schemers.org/srfi-117/srfi-117.html)
+- [`srfi 121`](api/srfi/121.md) - [Generators](http://srfi.schemers.org/srfi-121/srfi-121.html)
- [`srfi 128`](api/srfi/128.md) - [Comparators](http://srfi.schemers.org/srfi-128/srfi-128.html)
-- [`srfi 132`](api/srfi/132.md) - [Sort Libraries](http://srfi.schemers.org/srfi-132/srfi-132.html)
-- [`srfi 133`](api/srfi/133.md) - [Vector Library (R7RS-compatible)](http://srfi.schemers.org/srfi-133/srfi-133.html)
+- [`srfi 132`](api/srfi/132.md) - [Sort libraries](http://srfi.schemers.org/srfi-132/srfi-132.html)
+- [`srfi 133`](api/srfi/133.md) - [Vector library (R7RS-compatible)](http://srfi.schemers.org/srfi-133/srfi-133.html)
# Cyclone Libraries
diff --git a/docs/api/srfi/121.md b/docs/api/srfi/121.md
new file mode 100644
index 00000000..e1843743
--- /dev/null
+++ b/docs/api/srfi/121.md
@@ -0,0 +1,458 @@
+# SRFI 121 - Generators
+
+Defines utility procedures that create, transform and consume generators. A
+'generator' is a procedure with no arguments (a 'thunk') that works as a source
+of values. Every time a generator is called, it yields a value.
+
+Generators may be finite or infinite; a finite generator returns an end-of-file
+object to indicate that it is exhausted (has no more values to give). For
+example, ``read-char``, ``read-line`` and ``read`` are generators that produce
+characters, lines and objects from the current input port.
+
+This library is designed to provide lightweight laziness.
+
+See the [SRFI document][1] for more information.
+
+## Definitions
+
+Generators can be divided into two classes: *finite* and *infinite*. Both kinds
+of generator can be invoked an indefinite number of times. After a finite
+generator has produced all of its values, it will return an end-of-file object
+for all subsequent calls. A generator is said to be *exhausted* if calling it
+will return an end-of-file object. By definition, an infinite generator can
+never be exhausted.
+
+A generator is said to be in an *undefined state* if it cannot be determined how
+many values it has produced. This arises because it is impossible to tell by
+inspection whether a generator is exhausted or not. For example,
+``(generator-fold + 0 (generator 1 2 3) (generator 1 2))`` will compute 0 + 1 +
+1 + 2 + 2 = 6, at which time the second generator will be exhausted. If the
+first generator is invoked, however, it may return either 3 or an end-of-file
+object, depending on whether the implementation of ``generator-fold`` invoked it
+or not. Therefore, the first generator is said to be in an undefined state.
+
+Functions provided under [generator operations](#generator-operations) do not
+consume elements from their input generators. In general, they produce finite
+generators if their inputs are finite.
+
+Functions provided udner [consuming generated
+values](#consuming-generated-values) consume all values from any generator
+passed to them, and will not return if any of their arguments are infinite.
+
+## Generator constructors
+[`generator`](#generator)
+[`make-iota-generator`](#make-iota-generator)
+[`make-range-generator`](#make-range-generator)
+[`make-coroutine-generator`](#make-coroutine-generator)
+[`list->generator'](#list-generator)
+[`vector->generator'](#vector-generator)
+[`reverse-vector->generator'](#reverse-vector-generator)
+[`string->generator`](#string-generator)
+[`bytevector->generator`](#bytevector-generator)
+[`make-for-each-generator`](#make-for-each-generator)
+[`make-unfold-generator`](#make-unfold-generator)
+
+## Generator operations
+[`gcons*`](#gcons)
+[`gappend`](#gappend)
+[`gcombine`](#gcombine)
+[`gfilter`](#gfilter)
+[`gremove`](#gremove)
+[`gtake`](#gtake)
+[`gdrop`](#gdrop)
+[`gtake-while`](#gtake-while)
+[`gdrop-while`](#gdrop-while)
+[`gdelete`](#gdelete)
+[`gdelete-neighbor-dups`](#gdelete-neighbor-dups)
+[`gindex`](#gindex)
+[`gselect`](#gselect)
+
+## Consuming generated values
+[`generator->list`](#generator-list)
+[`generator->reverse-list`](#generator-reverse-list)
+[`generator->vector`](#generator-vector)
+[`generator->vector!`](#generator-vector!)
+[`generator->string`](#generator-string)
+[`generator-fold`](#generator-fold)
+[`generator-for-each`](#generator-for-each)
+[`generator-find`](#generator-find)
+[`generator-count`](#generator-count)
+[`generator-any`](#generator-any)
+[`generator-every`](#generator-every)
+[`generator-unfold`](#generator-unfold)
+
+# generator
+
+ (generator arg ...)
+
+Returns a generator which produces each of this function's arguments in turn.
+When given no arguments, returns an empty generator which provides no values.
+
+# make-iota-generator
+
+ (make-iota-generator count)
+ (make-iota-generator count start)
+ (make-iota-generator count start step)
+
+Returns a finite generator which produces a sequence of ``count`` numbers. The
+sequence begins with ``start`` (default 0) and increases by ``step`` (default
+1). If both ``start`` and ``step`` are exact, the generator produces exact
+values; otherwise, it produces inexact ones. The exactness of ``count`` does not
+affect the exactness of results.
+
+Example: ``(generator->list (make-iota-generator 3 8))`` => (8 9 10)``
+
+# make-range-generator
+
+ (make-range-generator start)
+ (make-range-generator start end)
+ (make-range-generator start end step)
+
+Returns a generator which produces a sequence of numbers. The sequence begins
+with ``start``, increases by ``step`` (default 1), and continues while the
+number is less than ``end``, or forever if ``end`` is not provided. If both
+``start`` and ``step`` are exact, the generator produces exact values;
+otherwise, it produces inexact ones. The exactness of ``end`` does not affect
+the exactness of the results.
+
+Example: ``(generator->list (make-range-generator 3) 4) => (3 4 5 6)``
+
+# make-coroutine-generator
+
+ (make-coroutine-generator proc)
+
+Creates a generator from a coroutine. The ``proc`` argument should be a
+procedure that takes a single argument ``yield``. When called,
+``make-coroutine-generator`` immediately returns a generator ``g``. When ``g``
+is called, ``proc`` runs until it calls ``yield``. Calling ``yield`` causes the
+execution of ``proc`` to be suspended, and ``g`` returns the value passed to
+``yield``.
+
+Whether ``g`` is finite or infinite depends on the behaviour of ``proc``: if
+``proc`` returns, it is the end of the sequence, and ``g`` will return an
+end-of-file object from then on. The return value of ``proc`` is ignored.
+
+# list->generator
+
+ (list->generator lis)
+
+Returns a generator that produces each element of the list ``lis`` in turn.
+Mutating ``lis`` will affect the results of the generator.
+
+``list->generator`` and ``generator->list`` (when given no arguments) are
+inverses up to ``equal?``; thus, for any list ``x``,
+``(equal? x (generator->list (list->generator x))) => #t``.
+
+# vector->generator
+
+ (vector->generator vec)
+ (vector->generator vec start)
+ (vector->generator vec start end)
+
+Returns a generator that produces elements of ``vec``, in turn, from the index
+``start`` (inclusive, default 0) to ``end`` (exclusive, default
+``(vector-length vec)``). Mutating ``vec`` will affect the results of the
+generator.
+
+When given no arguments, ``vector->generator`` and ``generator->vector`` are
+inverses up to ``equal?``; thus, for any vector ``x``, ``(equal? x
+(generator->vector (vector->generator x))) => #t``.
+
+# reverse-vector->generator
+
+ (reverse-vector->generator vec)
+ (reverse-vector->generator vec start)
+ (reverse-vector->generator vec start end)
+
+Returns a generator that produces elements of ``vec``, in turn, from ``end``
+(exclusive, default ``(vector-length vec)``) to ``start`` (inclusive, default
+0), in reverse order of indices. Mutating ``vec`` will affect the results of the
+generator.
+
+# string->generator
+
+ (string->generator str)
+ (string->generator str start)
+ (string->generator str start end)
+
+Returns a generator that produces characters of ``str``, in turn, from ``start``
+(inclusive, default 0) to ``end`` (exclusive, default ``(string-length str)``).
+Mutating ``str`` will affect the results of the generator.
+
+When given no arguments, ``string->generator`` and ``generator->string`` are
+inverses up to ``string=?``; thus, for any string ``s``, ``(string=? s
+(generator->string (string->generator s))) => #t``.
+
+# bytevector->generator
+
+ (bytevector->generator bv)
+ (bytevector->generator bv start)
+ (bytevector->generator bv start end)
+
+Returns a generator that produces bytes of ``bv``, in turn, from ``start``
+(inclusive, default 0) to ``end`` (exclusive, default ``(bytevector-length
+bv)``). Mutating ``bv`` will affect the results of the generator.
+
+# make-for-each-generator
+
+ (make-for-each-generator for-each obj)
+
+Constructs a generator over any collection ``obj``, using a ``for-each``
+procedure appropriate to ``obj``. This must be a procedure that, when called as
+``(for-each proc obj)`` calls ``proc`` on each element of ``obj``. Examples of
+such procedures are ``for-each``, ``string-for-each`` and ``vector-for-each``.
+The value returned by ``for-each`` is ignored. The generator is finite if the
+collection is finite.
+
+``obj`` need not be a conventional one (such as a string, list, etc), as long as
+``for-each`` can invoke a procedure on everything that counts as a member.
+
+# make-unfold-generator
+
+ (make-unfold-generator stop? mapper successor seed)
+
+A generator similar to [SRFI 1][2]'s ``unfold``.
+
+The ``stop?`` predicate takes a seed value and determines whether to stop. The
+``mapper`` procedure calculates a value to be returned by the generator from a
+seed value. The ``successor`` procedure calculates the next seed value from
+the current seed value.
+
+For each call of the resulting generator, ``stop?`` is called with the current
+seed value. If it returns true, then the generator returns an end-of-file
+object. Otherwise, it applies ``mapper`` to the current seed value to get the
+value to return, and uses ``successor`` to update the seed value.
+
+The generator is finite unless ``stop?`` is a constant function returning
+``#f``.
+
+# gcons\*
+
+ (gcons\* item ... gen)
+
+Returns a generator that adds ``item ...`` in front of ``gen``. Once each of
+``item ...`` has been produced, the generator is guaranteed to tail-call
+``gen``.
+
+# gappend
+
+ (gappend gen ...)
+
+Returns a generator that yields the items from the first argument generator, and
+once it is exhausted, the second generator, and so forth.
+
+If any of the argument generators are infinite, subsequent argument generators
+will never be asked to produce any values.
+
+# gcombine
+
+ (gcombine proc seed gen gen2 ...)
+
+Returns a generator for mapping with state. It produces a sequence of sub-folds
+over ``proc``.
+
+The ``proc`` argument is a procedure that takes as many arguments as there are
+argument generators, plus one. It is called as ``(proc v1 v2 ... seed)``, where
+``v1 v2 ...`` are the values produced by the argument generators, and ``seed``
+is the current seed value. It must return two values: the produced value and the
+next seed. The generator is exhausted when any of its argument generators are
+exhausted, at which time, the remaining argument generators are in an undefined
+state.
+
+# gfilter
+
+ (gfilter pred gen)
+
+Returns a generator that produces items from ``gen``, except those for which
+``pred`` would return ``#f``.
+
+# gremove
+
+ (gremove pred gen)
+
+Equivalent to ``(gfilter (lambda (x) (not (pred x))) gen)``.
+
+# gtake
+
+ (gtake gen k)
+ (gtake gen k padding)
+
+A generator analogue to [SRFI 1][2]'s ``take``. Returns a generator that
+produces (at most) the first ``k`` items of ``gen``. If ``gen`` is exhausted
+before it can produce ``k`` items, the rest will be made up by producing the
+``padding`` value.
+
+# gdrop
+
+ (gdrop gen k)
+
+A generator analogue to [SRFI 1][2]'s ``drop``. Returns a generator that
+skips the first (at most) ``k`` items of ``gen``, then produces the rest. If
+``k`` is greater than, or equal to, the total number of items ``gen`` could
+produce, an empty generator is produced instead.
+
+# gtake-while
+
+ (gtake-while pred gen)
+
+A generator analogue to [SRFI 1][2]'s ``take-while``. Returns a generator that
+produces values from ``gen`` until ``pred`` returns ``#f`` on a value.
+
+# gdrop-while
+
+ (gdrop-while pred gen)
+
+A generator analogue to [SRFI 1][2]'s ``drop-while``. Returns a generator that
+discards values from ``gen`` until ``pred`` returns ``#t`` for a value, and then
+produces values from ``gen``.
+
+# gdelete
+
+ (gdelete item gen)
+ (gdelete item gen =)
+
+Returns a generator which produces the same items as ``gen``, except any items
+that are the same as ``item`` up to ``=``, which defaults to ``equal?``. ``=``
+is passed exactly two arguments, one of which is the value produced by ``gen``.
+
+# gdelete-neighbor-dups
+
+ (gdelete-neighbor-dups gen)
+ (gdelete-neighbor-dups gen =)
+
+Returns a generator which produces the same items as ``gen``, except any items
+that are the same as the preceding items up to ``=``, which defaults to
+``equal?``. ``=`` is passed exactly two arguments; the first of which is
+produced by ``gen`` before the second.
+
+# gindex
+
+ (gindex value-gen index-gen)
+
+Returns a generator that produces elements of ``value-gen`` specified by the
+indices (non-negative exact integers) produced by ``index-gen``. It is an error
+if the indices are not strictly increasing, or if any index exceeds the number
+of elements produced by ``value-gen``. The generator is exhausted when either of
+``value-gen`` or ``index-gen`` is exhausted, at which time the other is in an
+undefined state.
+
+# gselect
+
+ (gselect value-gen truth-gen)
+
+Returns a generator that produces elements of ``value-gen`` that correspond to
+the values produced by ``truth-gen``. If the current value of ``truth-gen`` is
+true, the current value of ``value-gen`` is produced; otherwise, the value is
+skipped. The generator is exhausted when either of ``value-gen`` or
+``truth-gen`` is exhausted, at which time the other is in an undefined state.
+
+# generator->list
+
+ (generator->list gen)
+ (generator->list gen k)
+
+Calls ``gen`` repeatedly to produce its values, then collects them into a list
+and returns them. If ``k`` is omitted, ``gen`` will be asked to produce values
+until it is exhausted; otherwise, only at most ``k``-many values will be
+requested. It is an error for ``k`` to be anything other than a non-negative
+integer.
+
+# generator->reverse-list
+
+ (generator->reverse-list gen)
+ (generator->reverse-list gen k)
+
+As ``generator->list``, but the returned list is in reverse order.
+
+# generator->vector
+
+ (generator->vector gen)
+ (generator->vector gen k)
+
+As ``generator->list``, but the returned result is a vector.
+
+# generator->vector!
+
+ (generator->vector! vec at gen)
+
+Calls ``gen`` repeatedly to produce its values, and puts them into ``vec``,
+starting at index ``at``, until ``vec`` is full or ``gen`` is exhausted. Returns
+the number of elements produced from ``gen``.
+
+# generator->string
+
+ (generator->string gen)
+ (generator->string gen k)
+
+Calls ``gen`` repeatedly to produce characters, and returns a newly-allocated
+string of them. It is an error if ``gen`` does not produce only characters. If
+``k`` is omitted, the generator will be asked to produce characters until it is
+exhausted; otherwise, at most ``k`` characters will be requested. It is an error
+for ``k`` to be anything other than a non-negative integer.
+
+# generator-fold
+
+ (generator-fold proc seed gen1 gen2 ...)
+
+An analogue of [SRFI 1][2]'s ``fold`` on values produced by the generator
+arguments.
+
+When one generator argument ``gen`` is given, for each value ``v`` produced by
+``gen``, ``proc`` is called as ``(proc v r)``, where ``r`` is the current
+accumulated result; the initial value of ``r`` is ``seed``, and the return value
+from ``proc`` becomes the new accumulated result. When ``gen`` is exhausted, the
+accumulated result at the time is returned.
+
+When more than one generator argument is given, ``proc`` is called on all the
+values produced by all the generator arguments, followed by the current
+accumulated result. The procedure returns when any of the generator arguments is
+exhausted, at which time the others are in an undefined state.
+
+# generator-for-each
+
+ (generator-for-each proc gen1 gen2 ...)
+
+A generator analogue of ``for-each`` that consumes produced values with side
+effects. ``proc`` is repeatedly applied to values produced by all the generator
+arguments, until any of them is exhausted. The values returned by ``proc`` are
+discarded. The procedure terminates when any of the argument generators is
+exhausted, at which time the others are in an undefined state.
+
+# generator-find
+
+ (generator-find pred gen)
+
+Returns the first value produced by ``gen`` that satisfies the predicate
+``pred``, or ``#f`` if no such value exists. If ``gen`` is infinite, this
+procedure will not return if it cannot find an appropriate item.
+
+# generator-count
+
+ (generator-count pred gen)
+
+Returns the number of values that gen can produce which satisfy the predicate
+``pred``. This procedure will not return if ``gen`` is infinite.
+
+# generator-any
+
+ (generator-any pred gen)
+
+Applies ``pred`` to each item produced by ``gen``. As soon as ``pred`` returns a
+true value, the value is returned without consuming the rest of ``gen``. If
+``gen`` is exhausted, returns ``#f``.
+
+# generator-every
+
+ (generator-every pred gen)
+
+Equivalent to ``(not (generator-any (lambda (x) (not (pred x))) gen))``.
+
+# generator-unfold
+
+ (generator-unfold gen unfold arg ...)
+
+Equivalent to ``(unfold eof-object? (lambda (x) x) (lambda (x) (gen)) arg
+...)``, where ``unfold`` is the [SRFI 1][2] procedure of the same name.
+
+[1]: http://srfi.schemers.org/srfi-121/srfi-121.html
+[2]: http://srfi.schemers.org/srfi-1/srfi-1.html
diff --git a/docs/api/srfi/18.md b/docs/api/srfi/18.md
index 28bda540..7d81b319 100644
--- a/docs/api/srfi/18.md
+++ b/docs/api/srfi/18.md
@@ -4,6 +4,12 @@ The `(srfi 18)` library provides multithreading support.
See the [Multithreading support SRFI documentation](http://srfi.schemers.org/srfi-18/srfi-18.html) for more information.
+## Limitations
+
+Currently, ``thread-join!`` is not provided. While this is not an essential
+primitive and can be worked around, code that relies on ``thread-join!`` being
+present in this implementation will fail to compile.
+
- [`thread?`](#thread)
- [`make-thread`](#make-thread)
- [`thread-name`](#thread-name)
diff --git a/docs/api/srfi/28.md b/docs/api/srfi/28.md
new file mode 100644
index 00000000..7ba65952
--- /dev/null
+++ b/docs/api/srfi/28.md
@@ -0,0 +1,47 @@
+# SRFI 28 - Basic format strings
+
+Specifies a method of interpreting a Scheme string which contains a number of
+escape sequences, into which other data is interpolated according to the
+semantics of each sequence.
+
+See the [SRFI document][1] for more information.
+
+## Limitations
+
+Currently, this translates newline escape sequences into LF. This may cause
+issues if this is ever used on Windows (which expects CRLF instead). Given that
+Cyclone does not currently support Windows, this issue should not arise in
+practice.
+
+## Interface
+
+# format
+
+ (format format-string [obj ...])
+
+Processes ``format-string``, replacing any escape sequences in order with one or
+more characters. These characters depend on the semantics of the escape
+sequence.
+
+An 'escape sequence' is a two-character sequence in ``format-string``, where the
+first character is a tilde ('~'). The following are all of the valid escape
+codes, as well as their semantics:
+
+- ``~a``: The corresponding value is inserted into the string as if printed by
+ ``display``.
+- ``~s``: The corresponding value is inserted into the string as if printed by
+ ``write``.
+- ``~%``: A newline is inserted.
+- ``~~``: A literal tilde is inserted.
+
+``~a`` and ``~s``, when encountered, require a corresponding Scheme value to be
+present as an argument to ``format``. The values provided in ``obj ...`` are
+used by the escape sequences in order. It is an error if fewer values are
+provided than escape sequences which require them. ``~%`` and ``~~`` do not need
+a corresponding value.
+
+Example: The call ``(format "This is the ~ast example: ~s~%" 1 '(foo bar 17))``
+would produce the string ``"This is the 1st example: (foo bar 17)
+"`` (note the newline).
+
+[1]: http://srfi.schemers.org/srfi-28/srfi-28.html
diff --git a/docs/api/srfi/60.md b/docs/api/srfi/60.md
new file mode 100644
index 00000000..24b44ae6
--- /dev/null
+++ b/docs/api/srfi/60.md
@@ -0,0 +1,247 @@
+# SRFI 60 - Integers as bits
+
+Various operations designed to work on integers as strings of bits efficiently.
+
+See the [SRFI document][1] for more information.
+
+## Bitwise operations
+[`logand`](#logand)
+[`logior`](#logior)
+[`logxor`](#logxor)
+[`lognot`](#lognot)
+[`logtest`](#logtest)
+[`bitwise-and`](#bitwise-and)
+[`bitwise-ior`](#bitwise-ior)
+[`bitwise-xor`](#bitwise-xor)
+[`bitwise-not`](#bitwise-not)
+[`bitwise-if`](#bitwise-if)
+[`bitwise-merge`](#bitwise-merge)
+[`any-bits-set?`](#any-bits-set)
+
+## Integer properties
+[`logcount`](#logcount)
+[`log2-binary-factors`](#log2-binary-factors)
+[`bit-count`](#bit-count)
+[`integer-length`](#integer-length)
+[`first-set-bit`](#first-set-bit)
+
+## Bit within word
+[`logbit?`](#logbit)
+[`bit-set?`](#bit-set)
+[`copy-bit`](#copy-bit)
+
+## Field of bits
+[`bit-field`](#bit-field)
+[`copy-bit-field`](#copy-bit-field)
+[`ash`](#ash)
+[`arithmetic-shift`](#ash)
+[`rotate-bit-field`](#rotate-bit-field)
+
+## Bits as booleans
+[`integer->list`](#integer-list)
+[`list->integer`](#list-integer)
+[`booleans->integer`](#booleans-integer)
+
+# logand
+
+ (logand n1 ...)
+
+Returns the integer which is the bitwise-AND of the arguments.
+
+Example: ``(number->string (logand #b1100 #b1010) 2) => "1000"``
+
+# bitwise-and
+
+Synonym for ``logand``.
+
+# logior
+
+ (logior n1 ...)
+
+Returns the integer which is the bitwise-OR of the arguments.
+
+Example: ``(number->string (logior #b1100 #b1010) 2) => "1110"``
+
+# bitwise-ior
+
+Synonym for ``logior``.
+
+# logxor
+
+ (logxor n1 ...)
+
+Returns the integer which is the bitwise-XOR of the arguments.
+
+Example: ``(number->string (logxor #b1100 #b1010) 2) => "110"``
+
+# bitwise-xor
+
+Synonym for ``logxor``.
+
+# lognot
+
+ (lognot n)
+
+Returns the integer which is the one's-complement of the argument.
+
+# bitwise-not
+
+Synonym for ``lognot``.
+
+# bitwise-if
+
+ (bitwise-if mask n0 n1)
+
+Returns an integer composed of some bits from ``n0`` and some bits from ``n1``.
+A bit of the result is taken from ``n0`` if the corresponding bit of ``mask`` is
+1, and from ``n1`` otherwise.
+
+# bitwise-merge
+
+Synonym for ``bitwise-if``.
+
+# logtest
+
+ (logtest j k)
+
+Equivalent to ``(not (zero? (logand j k)))``.
+
+Example: ``(logtest #b0100 #b1011) => #f``
+
+# any-bits-set?
+
+Synonym for ``logtest``.
+
+# logcount
+
+ (logcount n)
+
+Returns the number of bits in ``n``. If ``n`` is positive, the 1-bits in its
+binary representation are counted. If negative, the 0-bits in its
+two's-complement binary representation are counted. If 0, then 0 is returned.
+
+Example: ``(logcount #b10101010) => 4``
+
+# bit-count
+
+Synonym for ``logcount``.
+
+# integer-length
+
+ (integer-length n)
+
+Returns the number of bits necessary to represent ``n``.
+
+Example: ``(integer-length #b1011) => 4``
+
+# log2-binary-factors
+
+ (log2-binary-factors n)
+
+Returns the bit-index of the least-significant 1-bit in ``n``. If ``n`` contains
+no 1-bits, -1 is returned.
+
+Example: ``(log2-binary-factors 4) => 2``
+
+# first-set-bit
+
+Synonym for ``log2-binary-factors``
+
+# logbit?
+
+ (logbit? index n)
+
+Equivalent to ``(logtest (exact (expt 2 index)) n)``.
+
+Example: ``(logbit? 1 #b1101) => #f``
+
+# bit-set?
+
+Synonym for ``logbit?``.
+
+# copy-bit
+
+ (copy-bit index from bit)
+
+Returns ``from``, except in the ``index``th bit, which is 1 if ``bit`` is #t,
+and 0 if ``bit`` is #f.
+
+Example: ``(number->string (copy-bit 2 #b1111 #f) 2) => "1011"``
+
+# bit-field
+
+ (bit-field n start end)
+
+Returns the integer composed of the ``start`` (inclusive) through ``end``
+(exclusive) bits of ``n``. The ``start``th bit becomes the 0th bit in the
+result.
+
+Example: ``(number->string (bit-field #b1101101010 0 4) 2) => "1010"``
+
+# copy-bit-field
+
+ (copy-bit-field to from start end)
+
+Returns ``to``, except possibly in the ``start`` (inclusive) through ``end``
+(exclusive) bits, which are the same as those of ``from``. The 0th bit of
+``from`` becomes the ``start``th bit of the result.
+
+Example: ``(number->string (copy-bit-field #b1101101010 0 0 4) 2) =>
+"1101100000"``
+
+# ash
+
+ (ash n count)
+
+Equivalent to ``(exact (floor (* n (expt 2 count))))``.
+
+Example: ``(number->string (ash #b1010 -1) 2) => "101"``
+
+# arithmetic-shift
+
+Synonym for ``ash``.
+
+# rotate-bit-field
+
+ (rotate-bit-field n count start end)
+
+Returns ``n`` with the bit-field from ``start`` to ``end`` cyclically permuted
+by ``count`` bits towards high-order.
+
+Example: ``(number->string (rotate-bit-field #b0100 3 0 4) 2) => "10"``
+
+# reverse-bit-field
+
+ (reverse-bit-field n start end)
+
+Returns ``n`` with the order of bits ``start`` to ``end`` reversed.
+
+Example: ``(number->string (reverse-bit-field #b10100111 0 8) 2) =>
+"11100101")``
+
+# integer->list
+
+ (integer->list k)
+
+ (integer->list k len)
+
+Returns a list of ``len`` booleans corresponding to each bit of the non-negative
+integer ``k``. ``#t`` is coded for each 1; ``#f`` for each 0. ``len`` defaults
+to ``(integer-length k)``.
+
+# list->integer
+
+ (list->integer list)
+
+Returns an integer formed from the booleans in ``list``, which must consist only
+of booleans. A 1 bit is coded for each ``#t``; a 0 bit for each ``#f``.
+``integer->list`` and ``list->integer`` are inverses up to ``equal?``: thus, for
+any ``x``, ``(equal x (integer->list (list->integer x))) => #t``.
+
+# booleans->integer
+
+ (booleans->integer b1 ...)
+
+Equivalent to ``(list->integer (list b1 ...))``.
+
+[1]: http://srfi.schemers.org/srfi-60/srfi-60.html
diff --git a/docs/api/srfi/69.md b/docs/api/srfi/69.md
index 93ce8201..b530139a 100644
--- a/docs/api/srfi/69.md
+++ b/docs/api/srfi/69.md
@@ -11,6 +11,18 @@ Hash tables are widely recognised as a fundamental data structure for a wide var
See the [SRFI document](http://srfi.schemers.org/srfi-69/srfi-69.html) for more information.
+## Limitations
+
+Hash table size must be strictly less than 536,870,909.
+
+The default implementation of record hashing is severely suboptimal - if you
+want to store records in a hash table, use a custom hashing function.
+
+This implementation does not distinguish ``hash`` and ``hash-by-identity``.
+Additionally, symbol hashing is done by string-hashing the result of
+``symbol->string`` on the symbol, making symbols identical to strings for the
+purpose of hash table key performance.
+
## Type constructors and predicate
[`make-hash-table`](#make-hash-table)
[`hash-table?`](#hash-table)
diff --git a/srfi/60.scm b/srfi/60.scm
index 49713776..80fe92d7 100644
--- a/srfi/60.scm
+++ b/srfi/60.scm
@@ -1,23 +1,32 @@
-;;; "60.scm", bit access and operations for integers for Scheme
-;;; Copyright (C) 1991, 1993, 2001, 2003, 2005 Aubrey Jaffer
-;;; Copyright (C) 2017 Koz Ross
-;
-;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.
-
+#|
+ | Copyright (c) 1991, 1993, 2001, 2003, 2005 Aubrey Jaffer
+ | Copyright (c) 2017, Koz Ross
+ |
+ | All rights reserved.
+ |
+ | Redistribution and use in source and binary forms, with or without
+ | modification, are permitted provided that the following conditions are met:
+ | * Redistributions of source code must retain the above copyright
+ | notice, this list of conditions and the following disclaimer.
+ | * Redistributions in binary form must reproduce the above copyright
+ | notice, this list of conditions and the following disclaimer in the
+ | documentation and/or other materials provided with the distribution.
+ | * Neither the name of Cyclone nor the
+ | names of its contributors may be used to endorse or promote products
+ | derived from this software without specific prior written permission.
+ |
+ | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
+ | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ |#
+
(define-c raw-logand
"(void* data, int argc, closure _, object k, object x, object y)"
"Cyc_check_int(data, x);
@@ -99,16 +108,24 @@
(define bit-count logcount)
-(define (integer-length x)
- (exact (ceiling (log (+ x 1) 2))))
+(define-c integer-length
+ "(void* data, int argc, closure _, object k, object x)"
+ "Cyc_check_int(data, x);
+ int input = (int)unbox_number(x);
+ int res = 0;
+ while (input) {
+ res++;
+ input >>= 1;
+ };
+ return_closcall1(data, k, obj_int2obj(res));")
(define (log2-binary-factors n)
- (- (integer-length (logand n (- n))) 1))
+ (- (integer-length (raw-logand n (- n))) 1))
(define first-set-bit log2-binary-factors)
(define (logbit? index n)
- (logtest (exact (expt 2 index)) n))
+ (logtest (ash 1 index) n))
(define bit-set? logbit?)
@@ -126,8 +143,22 @@
(ash from start)
to))
-(define (ash x y)
- (exact (floor (* x (expt 2 y)))))
+(define-c ash
+ "(void* data, int argc, closure _, object k, object x, object y)"
+ "Cyc_check_int(data, x);
+ Cyc_check_int(data,y);
+ int bf = (int)unbox_number(x);
+ int shift = (int)unbox_number(y);
+ if (shift > 0) {
+ for (int i = 0; i < shift; i++) {
+ bf *= 2;
+ }
+ } else {
+ for (int i = 0; i < abs(shift); i++) {
+ bf /= 2;
+ }
+ }
+ return_closcall1(data, k, obj_int2obj(bf))")
(define arithmetic-shift ash)
diff --git a/srfi/60.sld b/srfi/60.sld
index 8ab61128..52f018a4 100644
--- a/srfi/60.sld
+++ b/srfi/60.sld
@@ -10,8 +10,8 @@
;;;; doesn't have them yet. This will need to be modified accordingly once
;;;; this support is provided.
(define-library (srfi 60)
- (import (scheme base)
- (scheme inexact))
+ (import
+ (scheme base))
(export
any-bits-set? arithmetic-shift ash
bit-count bit-field bit-set? bitwise-and bitwise-if bitwise-ior