Go fountain code library
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/binary.go b/binary.go
new file mode 100644
index 0000000..c752837
--- /dev/null
+++ b/binary.go
@@ -0,0 +1,116 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math/rand"
+)
+
+// Random binary fountain code. In this code, the constituent source blocks in
+// a code block are selected randomly and independently.
+
+// BinaryCodec contains the codec information for the random binary fountain
+// encoder and decoder.
+// Implements fountain.Codec
+type binaryCodec struct {
+	// numSourceBlocks is the number of source blocks (N) the source message is split into.
+	numSourceBlocks int
+}
+
+// NewBinaryCodec returns a codec implementing the binary fountain code,
+// where source blocks composing each LT block are chosen randomly and independently.
+func NewBinaryCodec(numSourceBlocks int) Codec {
+	return &binaryCodec{numSourceBlocks: numSourceBlocks}
+}
+
+// SourceBlocks returns the number of source blocks used in the codec.
+func (c *binaryCodec) SourceBlocks() int {
+	return c.numSourceBlocks
+}
+
+// PickIndices finds the source indices for a code block given an ID and
+// a random seed. Uses the Mersenne Twister internally.
+func (c *binaryCodec) PickIndices(codeBlockIndex int64) []int {
+	random := rand.New(NewMersenneTwister(codeBlockIndex))
+
+	var indices []int
+	for b := 0; b < c.SourceBlocks(); b++ {
+		if random.Intn(2) == 1 {
+			indices = append(indices, b)
+		}
+	}
+
+	return indices
+}
+
+// GenerateIntermediateBlocks simply returns the partition of the input message
+// into source blocks. It does not perform any additional precoding.
+func (c *binaryCodec) GenerateIntermediateBlocks(message []byte, numBlocks int) []block {
+	long, short := partitionBytes(message, c.numSourceBlocks)
+	source := equalizeBlockLengths(long, short)
+
+	return source
+}
+
+// NewDecoder creates a new binary fountain code decoder
+func (c *binaryCodec) NewDecoder(messageLength int) Decoder {
+	return newBinaryDecoder(c, messageLength)
+}
+
+// binaryDecoder is the state required to decode a combinatoric fountain
+// code message.
+type binaryDecoder struct {
+	codec         binaryCodec
+	messageLength int
+
+	// The sparse equation matrix used for decoding.
+	matrix sparseMatrix
+}
+
+// newBinaryDecoder creates a new decoder for a particular message.
+// The codec parameters used to create the original encoding blocks must be provided.
+// The decoder is only valid for decoding code blocks for a particular message.
+func newBinaryDecoder(c *binaryCodec, length int) *binaryDecoder {
+	return &binaryDecoder{
+		codec:         *c,
+		messageLength: length,
+		matrix: sparseMatrix{
+			coeff: make([][]int, c.numSourceBlocks),
+			v:     make([]block, c.numSourceBlocks),
+		}}
+}
+
+// AddBlocks adds a set of encoded blocks to the decoder. Returns true if the
+// message can be fully decoded. False if there is insufficient information.
+func (d *binaryDecoder) AddBlocks(blocks []LTBlock) bool {
+	for i := range blocks {
+		d.matrix.addEquation(d.codec.PickIndices(blocks[i].BlockCode),
+			block{data: blocks[i].Data})
+	}
+	return d.matrix.determined()
+}
+
+// Decode extracts the decoded message from the decoder. If the decoder does
+// not have sufficient information to produce an output, returns a nil slice.
+func (d *binaryDecoder) Decode() []byte {
+	if !d.matrix.determined() {
+		return nil
+	}
+
+	d.matrix.reduce()
+
+	lenLong, lenShort, numLong, numShort := partition(d.messageLength, d.codec.numSourceBlocks)
+	return d.matrix.reconstruct(d.messageLength, lenLong, lenShort, numLong, numShort)
+}
diff --git a/binary_test.go b/binary_test.go
new file mode 100644
index 0000000..1f60b90
--- /dev/null
+++ b/binary_test.go
@@ -0,0 +1,122 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math/rand"
+	"reflect"
+	"testing"
+)
+
+// TestBinaryDecoder ensures that the binary fountain produces a round-trip
+// decodeable message using the corresponding decoder.
+func TestBinaryDecoder(t *testing.T) {
+	c := NewBinaryCodec(13)
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+	ids := make([]int64, 45)
+	random := rand.New(rand.NewSource(8923489))
+	for i := range ids {
+		ids[i] = int64(random.Intn(100000))
+	}
+
+	blocks := EncodeLTBlocks(message, ids, c)
+	t.Log("blocks =", blocks)
+
+	d := newBinaryDecoder(c.(*binaryCodec), len(message))
+
+	for i := 0; i < 16; i++ {
+		d.AddBlocks([]LTBlock{blocks[i]})
+		if testing.Verbose() {
+			printMatrix(d.matrix, t)
+		}
+	}
+
+	d.matrix.reduce()
+	t.Log("REDUCE")
+	printMatrix(d.matrix, t)
+
+	decoded := d.Decode()
+	printMatrix(d.matrix, t)
+	if !reflect.DeepEqual(decoded, message) {
+		t.Errorf("Decoded message doesn't match original. Got %v, want %v", decoded, message)
+	}
+}
+
+// TestbinaryDecoderBlockTable tests many combinations of fountain block ID
+// combinations to ensure that the codec has the expected reconstruction
+// properties.
+func TestBinaryDecoderBlockTable(t *testing.T) {
+	c := NewBinaryCodec(13)
+
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+	random := rand.New(rand.NewSource(8234923))
+
+	moreBlocksNeeded := 0
+	for i := 0; i < 100; i++ {
+		r := rand.New(rand.NewSource(random.Int63()))
+		ids := make([]int64, 45)
+		for i := range ids {
+			ids[i] = int64(r.Intn(100000))
+		}
+		blocks := EncodeLTBlocks(message, ids, c)
+
+		d := newBinaryDecoder(c.(*binaryCodec), len(message))
+		d.AddBlocks(blocks[0:30])
+		if !d.matrix.determined() {
+			moreBlocksNeeded++
+			d.AddBlocks(blocks[31:46])
+		}
+		decoded := d.Decode()
+		if !reflect.DeepEqual(decoded, message) {
+			t.Errorf("Decoded message doesn't match original. Got %v, want %v", decoded, message)
+		}
+	}
+
+	if moreBlocksNeeded > 2 {
+		t.Errorf("Needed too many high-block-count decoding sequences: %d", moreBlocksNeeded)
+	}
+}
+
+// TestBinaryDecodeMessageTable tests a large number of source messages to make
+// sure they are all reconstructed accurately. This provides assurance that the
+// decoder is functioning accurately.
+func TestBinaryDecodeMessageTable(t *testing.T) {
+	c := NewBinaryCodec(10)
+	random := rand.New(rand.NewSource(8234982))
+	for i := 0; i < 100; i++ {
+		r := rand.New(rand.NewSource(random.Int63()))
+		messageLen := r.Intn(1000) + 1000
+		message := make([]byte, messageLen)
+		for j := 0; j < len(message); j++ {
+			message[j] = byte(r.Intn(200))
+		}
+		ids := make([]int64, 50)
+		for i := range ids {
+			ids[i] = int64(r.Intn(100000))
+		}
+		blocks := EncodeLTBlocks(message, ids, c)
+
+		d := newBinaryDecoder(c.(*binaryCodec), len(message))
+		d.AddBlocks(blocks[0:25])
+		if !d.matrix.determined() {
+			t.Errorf("Message should be determined after 25 blocks")
+		} else {
+			decoded := d.Decode()
+			if !reflect.DeepEqual(decoded, message) {
+				t.Errorf("Incorrect message decode. Length=%d, message=%v", len(message), message)
+			}
+		}
+	}
+}
diff --git a/block.go b/block.go
new file mode 100644
index 0000000..e8af62c
--- /dev/null
+++ b/block.go
@@ -0,0 +1,248 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+// A block represents a contiguous range of data being encoded or decoded,
+// or a block of coded data. Details of how the source text is split into blocks
+// is governed by the particular fountain code used.
+type block struct {
+	// Data content of this source or code block.
+	data []byte
+
+	// How many padding bytes this block has at the end.
+	padding int
+}
+
+// newBlock creates a new block with a given length. The block will initially be
+// all padding.
+func newBlock(len int) *block {
+	return &block{padding: len}
+}
+
+// length returns the length of the block in bytes. Counts data bytes as well
+// as any padding.
+func (b *block) length() int {
+	return len(b.data) + b.padding
+}
+
+func (b *block) empty() bool {
+	return b.length() == 0
+}
+
+// A common operation is to XOR entire code blocks together with other blocks.
+// When this is done, padding bytes count as 0 (that is XOR identity), and the
+// destination block will be modified so that its data is large enough to
+// contain the result of the XOR.
+func (b *block) xor(a block) {
+	if len(b.data) < len(a.data) {
+		var inc = len(a.data) - len(b.data)
+		b.data = append(b.data, make([]byte, inc)...)
+		if b.padding > inc {
+			b.padding -= inc
+		} else {
+			b.padding = 0
+		}
+	}
+
+	for i := 0; i < len(a.data); i++ {
+		b.data[i] ^= a.data[i]
+	}
+}
+
+// partitionBytes partitions an input text into a sequence of p blocks. The
+// sizes of the blocks will be given by the partition() function. The last
+// block may have padding.
+// Return values: the slice of longer blocks, the slice of shorter blocks.
+// Within each block slice, all will have uniform lengths.
+func partitionBytes(in []byte, p int) ([]block, []block) {
+	sliceIntoBlocks := func(in []byte, num, length int) ([]block, []byte) {
+		blocks := make([]block, num)
+		for i := range blocks {
+			if len(in) > length {
+				blocks[i].data, in = in[:length], in[length:]
+			} else {
+				blocks[i].data, in = in, []byte{}
+			}
+			if len(blocks[i].data) < length {
+				blocks[i].padding = length - len(blocks[i].data)
+			}
+		}
+		return blocks, in
+	}
+
+	lenLong, lenShort, numLong, numShort := partition(len(in), p)
+	long, in := sliceIntoBlocks(in, numLong, lenLong)
+	short, _ := sliceIntoBlocks(in, numShort, lenShort)
+	return long, short
+}
+
+// equalizeBlockLengths adds padding to all short blocks to make them equal in
+// size to the long blocks. The caller should ensure that all the longBlocks
+// have the same length.
+// Returns a block slice containing all the long and short blocks.
+func equalizeBlockLengths(longBlocks, shortBlocks []block) []block {
+	if len(longBlocks) == 0 {
+		return shortBlocks
+	}
+	if len(shortBlocks) == 0 {
+		return longBlocks
+	}
+
+	for i := range shortBlocks {
+		shortBlocks[i].padding += longBlocks[0].length() - shortBlocks[i].length()
+	}
+
+	blocks := make([]block, len(longBlocks)+len(shortBlocks))
+	copy(blocks, longBlocks)
+	copy(blocks[len(longBlocks):], shortBlocks)
+	return blocks
+}
+
+// sparseMatrix is the block decoding data structure. It is a sparse matrix of
+// XOR equations. The coefficients are the indices of the source blocks which
+// are XORed together to produce the values. So if equation _i_ of the matrix is
+// block_0 ^ block_2 ^ block_3 ^ block_9 = [0xD2, 0x38]
+// that would be represented as coeff[i] = [0, 2, 3, 9], v[i].data = [0xD2, 0x38]
+// Example: The sparse coefficient matrix
+// | 0 1 1 0 |
+// | 0 1 0 0 |
+// | 0 0 1 1 |
+// | 0 0 0 1 |
+// would be represented as
+// [ [ 1, 2],
+//   [ 1 ],
+//   [ 2, 3],
+//   [ 3 ]]
+// Every row has M[i][0] >= i. If we added components [2] to this matrix, it
+// would replace the M[2] row ([2, 3]), and then the resulting component vector
+// after cancellation against that row ([3]) would be used instead of the
+// original equation. In this case, M[3] is already populated, so the new
+// equation is redundant (and could be used for ECC, theoretically), but if we
+// didn't have an entry in M[3], it would be placed there.
+// The values were omitted from this discussion, but they follow along by doing
+// XOR operations as the components are reduced during insertion.
+type sparseMatrix struct {
+	coeff [][]int
+	v     []block
+}
+
+// xorRow performs a reduction of the given candidate equation (indices, b)
+// with the specified matrix row (index s). It does so by XORing the values,
+// and then taking the symmetric difference of the coefficients of that matrix
+// row and the provided indices. (That is, the "set XOR".) Assumes both
+// coefficient slices are sorted.
+func (m *sparseMatrix) xorRow(s int, indices []int, b block) ([]int, block) {
+	b.xor(m.v[s])
+
+	var newIndices []int
+	coeffs := m.coeff[s]
+	var i, j int
+	for i < len(coeffs) && j < len(indices) {
+		index := indices[j]
+		if coeffs[i] == index {
+			i++
+			j++
+		} else if coeffs[i] < index {
+			newIndices = append(newIndices, coeffs[i])
+			i++
+		} else {
+			newIndices = append(newIndices, index)
+			j++
+		}
+	}
+
+	newIndices = append(newIndices, coeffs[i:]...)
+	newIndices = append(newIndices, indices[j:]...)
+	return newIndices, b
+}
+
+// addEquation adds an XOR equation to the decode matrix. The online decode
+// strategy is a variant of that of Bioglio, Grangetto, and Gaeta
+// (http://www.di.unito.it/~bioglio/Papers/CL2009-lt.pdf) It maintains the
+// invariant that either coeff[i][0] == i or len(coeff[i]) == 0. That is, while
+// adding an equation to the matrix, it ensures that the decode matrix remains
+// triangular.
+func (m *sparseMatrix) addEquation(components []int, b block) {
+	// This loop reduces the incoming equation by XOR until it either fits into
+	// an empty row in the decode matrix or is discarded as redundant.
+	for len(components) > 0 && len(m.coeff[components[0]]) > 0 {
+		s := components[0]
+		if len(components) >= len(m.coeff[s]) {
+			components, b = m.xorRow(s, components, b)
+		} else {
+			// Swap the existing row for the new one, reduce the existing one and
+			// see if it fits elsewhere.
+			components, m.coeff[s] = m.coeff[s], components
+			b, m.v[s] = m.v[s], b
+		}
+	}
+
+	if len(components) > 0 {
+		m.coeff[components[0]] = components
+		m.v[components[0]] = b
+	}
+}
+
+// Check to see if the decode matrix is fully specified. This is true when
+// all rows have non-empty coefficient slices.
+// TODO(gbillock): is there a weakness here if an auxiliary block is unpopulated?
+func (m *sparseMatrix) determined() bool {
+	for _, r := range m.coeff {
+		if len(r) == 0 {
+			return false
+		}
+	}
+	return true
+}
+
+// reduce performs Gaussian Elimination over the whole matrix. Presumes
+// the matrix is triangular, and that the method is not called unless there is
+// enough data for a solution.
+// TODO(gbillock): Could profitably do this online as well?
+func (m *sparseMatrix) reduce() {
+	for i := len(m.coeff) - 1; i >= 0; i-- {
+		for j := 0; j < i; j++ {
+			ci, cj := m.coeff[i], m.coeff[j]
+			for k := 1; k < len(cj); k++ {
+				if cj[k] == ci[0] {
+					m.v[j].xor(m.v[i])
+					continue
+				}
+			}
+		}
+		// All but the leading coefficient in the rows have been reduced out.
+		m.coeff[i] = m.coeff[i][0:1]
+	}
+}
+
+// reconstruct pastes the fully reduced values in the sparse matrix result column
+// into a new byte array and returns it. The length/number parameters are typically
+// those given by partition().
+// lenLong is how many long blocks there are.
+// lenShort is how many short blocks there are (following the long blocks).
+// numLong is how many bytes are in the long blocks.
+// numShort is how many bytes the short blocks are.
+func (m *sparseMatrix) reconstruct(totalLength, lenLong, lenShort, numLong, numShort int) []byte {
+	out := make([]byte, totalLength)
+	out = out[0:0]
+	for i := 0; i < numLong; i++ {
+		out = append(out, m.v[i].data[0:lenLong]...)
+	}
+	for i := numLong; i < numLong+numShort; i++ {
+		out = append(out, m.v[i].data[0:lenShort]...)
+	}
+
+	return out
+}
diff --git a/block_test.go b/block_test.go
new file mode 100644
index 0000000..0b2f7de
--- /dev/null
+++ b/block_test.go
@@ -0,0 +1,280 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"bytes"
+	"reflect"
+	"testing"
+)
+
+func TestBlockLength(t *testing.T) {
+	var lengthTests = []struct {
+		b   block
+		len int
+	}{
+		{block{}, 0},
+		{block{[]byte{1, 0, 1}, 0}, 3},
+		{block{[]byte{1, 0, 1}, 1}, 4},
+	}
+
+	for _, i := range lengthTests {
+		if i.b.length() != i.len {
+			t.Errorf("Length of b is %d, should be %d", i.b.length(), i.len)
+		}
+		if (i.len == 0) != i.b.empty() {
+			t.Errorf("Emptiness check error. Got %v, want %v", i.b.empty(), i.len == 0)
+		}
+	}
+}
+
+func TestBlockXor(t *testing.T) {
+	var xorTests = []struct {
+		a   block
+		b   block
+		out block
+	}{
+		{block{[]byte{1, 0, 1}, 0}, block{[]byte{1, 1, 1}, 0}, block{[]byte{0, 1, 0}, 0}},
+		{block{[]byte{1}, 0}, block{[]byte{0, 14, 6}, 0}, block{[]byte{1, 14, 6}, 0}},
+		{block{}, block{[]byte{100, 200}, 0}, block{[]byte{100, 200}, 0}},
+		{block{[]byte{}, 5}, block{[]byte{0, 1, 0}, 0}, block{[]byte{0, 1, 0}, 2}},
+		{block{[]byte{}, 5}, block{[]byte{0, 1, 0, 2, 3}, 0}, block{[]byte{0, 1, 0, 2, 3}, 0}},
+		{block{[]byte{}, 5}, block{[]byte{0, 1, 0, 2, 3, 7}, 0}, block{[]byte{0, 1, 0, 2, 3, 7}, 0}},
+		{block{[]byte{1}, 4}, block{[]byte{0, 1, 0, 2, 3, 7}, 0}, block{[]byte{1, 1, 0, 2, 3, 7}, 0}},
+	}
+
+	for _, i := range xorTests {
+		t.Logf("...Testing %v XOR %v", i.a, i.b)
+		originalLength := i.a.length()
+		i.a.xor(i.b)
+		if i.a.length() < originalLength {
+			t.Errorf("Length shrunk. Got %d, want length >= %d", i.a.length(), originalLength)
+		}
+		if len(i.a.data) != len(i.b.data) {
+			t.Errorf("a and b data should be same length after xor. a len=%d, b len=%d", len(i.a.data), len(i.b.data))
+		}
+
+		if !bytes.Equal(i.a.data, i.out.data) {
+			t.Errorf("XOR value is %v : should be %v", i.a.data, i.out.data)
+		}
+	}
+}
+
+func TestPartitionBytes(t *testing.T) {
+	a := make([]byte, 100)
+	for i := 0; i < len(a); i++ {
+		a[i] = byte(i)
+	}
+
+	var partitionTests = []struct {
+		numPartitions     int
+		lenLong, lenShort int
+	}{
+		{11, 1, 10},
+		{3, 1, 2},
+	}
+
+	for _, i := range partitionTests {
+		t.Logf("Partitioning %v into %d", a, i.numPartitions)
+		long, short := partitionBytes(a, i.numPartitions)
+		if len(long) != i.lenLong {
+			t.Errorf("Got %d long blocks, should have %d", len(long), i.lenLong)
+		}
+		if len(short) != i.lenShort {
+			t.Errorf("Got %d short blocks, should have %d", len(short), i.lenShort)
+		}
+		if short[len(short)-1].padding != 0 {
+			t.Errorf("Should fit blocks exactly, have last padding %d", short[len(short)-1].padding)
+		}
+		if long[0].data[0] != 0 {
+			t.Errorf("Long block should be first. First value is %v", long[0].data)
+		}
+	}
+}
+
+func TestEqualizeBlockLengths(t *testing.T) {
+	b := []byte("abcdefghijklmnopq")
+	var equalizeTests = []struct {
+		numPartitions int
+		length        int
+		padding       int
+	}{
+		{1, 17, 0},
+		{2, 9, 1},
+		{3, 6, 1},
+		{4, 5, 1},
+		{5, 4, 1},
+		{6, 3, 1},
+		{7, 3, 1},
+		{8, 3, 1},
+		{9, 2, 1},
+		{10, 2, 1},
+		{16, 2, 1},
+		{17, 1, 0},
+	}
+
+	for _, i := range equalizeTests {
+		long, short := partitionBytes(b, i.numPartitions)
+		blocks := equalizeBlockLengths(long, short)
+		if len(blocks) != i.numPartitions {
+			t.Errorf("Got %d blocks, should have %d", len(blocks), i.numPartitions)
+		}
+		for k := range blocks {
+			if blocks[k].length() != i.length {
+				t.Errorf("Got block length %d for block %d, should be %d",
+					blocks[0].length(), k, i.length)
+			}
+		}
+		if blocks[len(blocks)-1].padding != i.padding {
+			t.Errorf("Padding of last block is %d, should be %d",
+				blocks[len(blocks)-1].padding, i.padding)
+		}
+	}
+}
+
+func printMatrix(m sparseMatrix, t *testing.T) {
+	t.Log("------- matrix -----------")
+	for i := range m.coeff {
+		t.Logf("%v = %v\n", m.coeff[i], m.v[i].data)
+	}
+}
+
+func TestMatrixXorRow(t *testing.T) {
+	var xorRowTests = []struct {
+		arow   []int
+		r      []int
+		result []int
+	}{
+		{[]int{0, 1}, []int{2, 3}, []int{0, 1, 2, 3}},
+		{[]int{0, 1}, []int{1, 2, 3}, []int{0, 2, 3}},
+		{[]int{}, []int{1, 2, 3}, []int{1, 2, 3}},
+		{[]int{1, 2, 3}, []int{}, []int{1, 2, 3}},
+		{[]int{1}, []int{2}, []int{1, 2}},
+		{[]int{1}, []int{1}, []int{}},
+		{[]int{1, 2}, []int{1, 2, 3, 4}, []int{3, 4}},
+		{[]int{3, 4}, []int{1, 2, 3, 4}, []int{1, 2}},
+		{[]int{1, 2, 3, 4}, []int{1, 2}, []int{3, 4}},
+		{[]int{0, 1, 2, 3, 4}, []int{1, 2}, []int{0, 3, 4}},
+		{[]int{3, 4}, []int{1, 2, 3, 4, 5}, []int{1, 2, 5}},
+		{[]int{3, 4, 8}, []int{1, 2, 3, 4, 5}, []int{1, 2, 5, 8}},
+	}
+
+	for _, test := range xorRowTests {
+		m := sparseMatrix{coeff: [][]int{test.arow}, v: []block{block{[]byte{1}, 0}}}
+
+		testb := block{[]byte{2}, 0}
+		test.r, testb = m.xorRow(0, test.r, testb)
+
+		// Needed since under DeepEqual the nil and the empty slice are not equal.
+		if test.r == nil {
+			test.r = make([]int, 0)
+		}
+		if !reflect.DeepEqual(test.r, test.result) {
+			t.Errorf("XOR row result got %v, should be %v", test.r, test.result)
+		}
+		if !reflect.DeepEqual(testb, block{[]byte{3}, 0}) {
+			t.Errorf("XOR row block got %v, should be %v", testb, block{[]byte{3}, 0})
+		}
+	}
+}
+
+func TestMatrixBasic(t *testing.T) {
+	m := sparseMatrix{coeff: [][]int{{}, {}}, v: []block{block{}, block{}}}
+
+	m.addEquation([]int{0}, block{data: []byte{1}})
+	if m.determined() {
+		t.Errorf("2-row matrix should not be determined after 1 equation")
+		printMatrix(m, t)
+	}
+
+	m.addEquation([]int{0, 1}, block{data: []byte{2}})
+	if !m.determined() {
+		t.Errorf("2-row matrix should be determined after 2 equations")
+		printMatrix(m, t)
+	}
+
+	printMatrix(m, t)
+
+	if !reflect.DeepEqual(m.coeff[0], []int{0}) ||
+		!reflect.DeepEqual(m.v[0].data, []byte{1}) {
+		t.Errorf("Equation 0 got (%v = %v), want ([0] = [1])", m.coeff[0], m.v[0].data)
+	}
+	if !reflect.DeepEqual(m.coeff[1], []int{1}) ||
+		!reflect.DeepEqual(m.v[1].data, []byte{3}) {
+		t.Errorf("Equation 1 got (%v = %v), want ([1] = [3])", m.coeff[0], m.v[0].data)
+	}
+
+	m.reduce()
+	if !reflect.DeepEqual(m.coeff[0], []int{0}) ||
+		!reflect.DeepEqual(m.v[0].data, []byte{1}) {
+		t.Errorf("Equation 0 got (%v = %v), want ([0] = [1])", m.coeff[0], m.v[0].data)
+	}
+	if !reflect.DeepEqual(m.coeff[1], []int{1}) ||
+		!reflect.DeepEqual(m.v[1].data, []byte{3}) {
+		t.Errorf("Equation 1 got (%v = %v), want ([1] = [3])", m.coeff[0], m.v[0].data)
+	}
+}
+
+func TestMatrixLarge(t *testing.T) {
+	m := sparseMatrix{coeff: make([][]int, 4), v: make([]block, 4)}
+
+	m.addEquation([]int{2, 3}, block{data: []byte{1}})
+	m.addEquation([]int{2}, block{data: []byte{2}})
+	if m.determined() {
+		t.Errorf("4-row matrix should not be determined after 2 equations")
+		printMatrix(m, t)
+	}
+	printMatrix(m, t)
+
+	// Should have triangular entries in {2} and {3} now.
+	if len(m.coeff[2]) != 1 || m.v[2].data[0] != 2 {
+		t.Errorf("Equation 2 got %v = %v, should be [2] = [2]", m.coeff[2], m.v[2])
+	}
+	if len(m.coeff[3]) != 1 || m.v[3].data[0] != 3 {
+		t.Errorf("Equation 3 got %v = %v, should be [3] = [3]", m.coeff[3], m.v[3])
+	}
+	if len(m.coeff[0]) != 0 || len(m.coeff[1]) != 0 {
+		t.Errorf("Equations 0 and 1 should be empty")
+		printMatrix(m, t)
+	}
+
+	m.addEquation([]int{0, 1, 2, 3}, block{data: []byte{4}})
+	if m.determined() {
+		t.Errorf("4-row matrix should not be determined after 3 equations")
+		printMatrix(m, t)
+	}
+
+	m.addEquation([]int{3}, block{data: []byte{3}})
+	if m.determined() {
+		t.Errorf("4-row matrix should not be determined after redundant equation")
+		printMatrix(m, t)
+	}
+
+	m.addEquation([]int{0, 2}, block{data: []byte{8}})
+	if !m.determined() {
+		t.Errorf("4-row matrix should be determined after 4 equations")
+		printMatrix(m, t)
+	}
+
+	// The matrix should now have entries in rows 0 and 1, but not equal to the
+	// original equations.
+	printMatrix(m, t)
+	if !reflect.DeepEqual(m.coeff[0], []int{0, 2}) {
+		t.Errorf("Got %v for coeff[0], expect [0, 2]", m.coeff[0])
+	}
+	if !reflect.DeepEqual(m.coeff[1], []int{1, 3}) {
+		t.Errorf("Got %v for coeff[1], expect [1, 3]", m.coeff[1])
+	}
+}
diff --git a/constants.go b/constants.go
new file mode 100644
index 0000000..01e2049
--- /dev/null
+++ b/constants.go
@@ -0,0 +1,722 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+var smallPrimes = []int{
+	2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
+	31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109,
+	113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
+	199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
+	293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389,
+	397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487,
+	491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599,
+	601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691,
+	701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811,
+	821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919,
+	929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021,
+	1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103,
+	1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
+	1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297,
+	1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423,
+	1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489,
+	1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583,
+	1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669,
+	1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783,
+	1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879,
+	1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999}
+
+// The V0 table from RFC 5053 Section 5.6.1
+var v0table = [256]uint32{
+	251291136, 3952231631, 3370958628, 4070167936, 123631495, 3351110283,
+	3218676425, 2011642291, 774603218, 2402805061, 1004366930,
+	1843948209, 428891132, 3746331984, 1591258008, 3067016507,
+	1433388735, 504005498, 2032657933, 3419319784, 2805686246,
+	3102436986, 3808671154, 2501582075, 3978944421, 246043949,
+	4016898363, 649743608, 1974987508, 2651273766, 2357956801, 689605112,
+	715807172, 2722736134, 191939188, 3535520147, 3277019569, 1470435941,
+	3763101702, 3232409631, 122701163, 3920852693, 782246947, 372121310,
+	2995604341, 2045698575, 2332962102, 4005368743, 218596347,
+	3415381967, 4207612806, 861117671, 3676575285, 2581671944,
+	3312220480, 681232419, 307306866, 4112503940, 1158111502, 709227802,
+	2724140433, 4201101115, 4215970289, 4048876515, 3031661061,
+	1909085522, 510985033, 1361682810, 129243379, 3142379587, 2569842483,
+	3033268270, 1658118006, 932109358, 1982290045, 2983082771,
+	3007670818, 3448104768, 683749698, 778296777, 1399125101, 1939403708,
+	1692176003, 3868299200, 1422476658, 593093658, 1878973865,
+	2526292949, 1591602827, 3986158854, 3964389521, 2695031039,
+	1942050155, 424618399, 1347204291, 2669179716, 2434425874,
+	2540801947, 1384069776, 4123580443, 1523670218, 2708475297,
+	1046771089, 2229796016, 1255426612, 4213663089, 1521339547,
+	3041843489, 420130494, 10677091, 515623176, 3457502702, 2115821274,
+	2720124766, 3242576090, 854310108, 425973987, 325832382, 1796851292,
+	2462744411, 1976681690, 1408671665, 1228817808, 3917210003,
+	263976645, 2593736473, 2471651269, 4291353919, 650792940, 1191583883,
+	3046561335, 2466530435, 2545983082, 969168436, 2019348792,
+	2268075521, 1169345068, 3250240009, 3963499681, 2560755113,
+	911182396, 760842409, 3569308693, 2687243553, 381854665, 2613828404,
+	2761078866, 1456668111, 883760091, 3294951678, 1604598575,
+	1985308198, 1014570543, 2724959607, 3062518035, 3115293053,
+	138853680, 4160398285, 3322241130, 2068983570, 2247491078,
+	3669524410, 1575146607, 828029864, 3732001371, 3422026452,
+	3370954177, 4006626915, 543812220, 1243116171, 3928372514,
+	2791443445, 4081325272, 2280435605, 885616073, 616452097, 3188863436,
+	2780382310, 2340014831, 1208439576, 258356309, 3837963200,
+	2075009450, 3214181212, 3303882142, 880813252, 1355575717, 207231484,
+	2420803184, 358923368, 1617557768, 3272161958, 1771154147,
+	2842106362, 1751209208, 1421030790, 658316681, 194065839, 3241510581,
+	38625260, 301875395, 4176141739, 297312930, 2137802113, 1502984205,
+	3669376622, 3728477036, 234652930, 2213589897, 2734638932,
+	1129721478, 3187422815, 2859178611, 3284308411, 3819792700,
+	3557526733, 451874476, 1740576081, 3592838701, 1709429513,
+	3702918379, 3533351328, 1641660745, 179350258, 2380520112,
+	3936163904, 3685256204, 3156252216, 1854258901, 2861641019,
+	3176611298, 834787554, 331353807, 517858103, 3010168884, 4012642001,
+	2217188075, 3756943137, 3077882590, 2054995199, 3081443129,
+	3895398812, 1141097543, 2376261053, 2626898255, 2554703076,
+	401233789, 1460049922, 678083952, 1064990737, 940909784, 1673396780,
+	528881783, 1712547446, 3629685652, 1358307511}
+
+// The V1 table from RFC 5053 Section 5.6.2
+var v1table = [256]uint32{
+	807385413, 2043073223, 3336749796, 1302105833, 2278607931, 541015020,
+	1684564270, 372709334, 3508252125, 1768346005, 1270451292,
+	2603029534, 2049387273, 3891424859, 2152948345, 4114760273,
+	915180310, 3754787998, 700503826, 2131559305, 1308908630, 224437350,
+	4065424007, 3638665944, 1679385496, 3431345226, 1779595665,
+	3068494238, 1424062773, 1033448464, 4050396853, 3302235057,
+	420600373, 2868446243, 311689386, 259047959, 4057180909, 1575367248,
+	4151214153, 110249784, 3006865921, 4293710613, 3501256572, 998007483,
+	499288295, 1205710710, 2997199489, 640417429, 3044194711, 486690751,
+	2686640734, 2394526209, 2521660077, 49993987, 3843885867, 4201106668,
+	415906198, 19296841, 2402488407, 2137119134, 1744097284, 579965637,
+	2037662632, 852173610, 2681403713, 1047144830, 2982173936, 910285038,
+	4187576520, 2589870048, 989448887, 3292758024, 506322719, 176010738,
+	1865471968, 2619324712, 564829442, 1996870325, 339697593, 4071072948,
+	3618966336, 2111320126, 1093955153, 957978696, 892010560, 1854601078,
+	1873407527, 2498544695, 2694156259, 1927339682, 1650555729,
+	183933047, 3061444337, 2067387204, 228962564, 3904109414, 1595995433,
+	1780701372, 2463145963, 307281463, 3237929991, 3852995239,
+	2398693510, 3754138664, 522074127, 146352474, 4104915256, 3029415884,
+	3545667983, 332038910, 976628269, 3123492423, 3041418372, 2258059298,
+	2139377204, 3243642973, 3226247917, 3674004636, 2698992189,
+	3453843574, 1963216666, 3509855005, 2358481858, 747331248,
+	1957348676, 1097574450, 2435697214, 3870972145, 1888833893,
+	2914085525, 4161315584, 1273113343, 3269644828, 3681293816,
+	412536684, 1156034077, 3823026442, 1066971017, 3598330293,
+	1979273937, 2079029895, 1195045909, 1071986421, 2712821515,
+	3377754595, 2184151095, 750918864, 2585729879, 4249895712,
+	1832579367, 1192240192, 946734366, 31230688, 3174399083, 3549375728,
+	1642430184, 1904857554, 861877404, 3277825584, 4267074718,
+	3122860549, 666423581, 644189126, 226475395, 307789415, 1196105631,
+	3191691839, 782852669, 1608507813, 1847685900, 4069766876,
+	3931548641, 2526471011, 766865139, 2115084288, 4259411376,
+	3323683436, 568512177, 3736601419, 1800276898, 4012458395, 1823982,
+	27980198, 2023839966, 869505096, 431161506, 1024804023, 1853869307,
+	3393537983, 1500703614, 3019471560, 1351086955, 3096933631,
+	3034634988, 2544598006, 1230942551, 3362230798, 159984793, 491590373,
+	3993872886, 3681855622, 903593547, 3535062472, 1799803217, 772984149,
+	895863112, 1899036275, 4187322100, 101856048, 234650315, 3183125617,
+	3190039692, 525584357, 1286834489, 455810374, 1869181575, 922673938,
+	3877430102, 3422391938, 1414347295, 1971054608, 3061798054,
+	830555096, 2822905141, 167033190, 1079139428, 4210126723, 3593797804,
+	429192890, 372093950, 1779187770, 3312189287, 204349348, 452421568,
+	2800540462, 3733109044, 1235082423, 1765319556, 3174729780,
+	3762994475, 3171962488, 442160826, 198349622, 45942637, 1324086311,
+	2901868599, 678860040, 3812229107, 19936821, 1119590141, 3640121682,
+	3545931032, 2102949142, 2828208598, 3603378023, 4135048896}
+
+// The systematic indices table from RFC 5053 Section 5.7
+var systematicIndextable = [8193]uint16{0, 0, 0, 0,
+	18, 14, 61, 46, 14, 22, 20, 40, 48, 1, 29, 40, 43, 46, 18, 8, 20, 2,
+	61, 26, 13, 29, 36, 19, 58, 5, 58, 0, 54, 56, 24, 14, 5, 67, 39, 31,
+	25, 29, 24, 19, 14, 56, 49, 49, 63, 30, 4, 39, 2, 1, 20, 19, 61, 4,
+	54, 70, 25, 52, 9, 26, 55, 69, 27, 68, 75, 19, 64, 57, 45, 3, 37, 31,
+	100, 41, 25, 41, 53, 23, 9, 31, 26, 30, 30, 46, 90, 50, 13, 90, 77,
+	61, 31, 54, 54, 3, 21, 66, 21, 11, 23, 11, 29, 21, 7, 1, 27, 4, 34,
+	17, 85, 69, 17, 75, 93, 57, 0, 53, 71, 88, 119, 88, 90, 22, 0, 58,
+	41, 22, 96, 26, 79, 118, 19, 3, 81, 72, 50, 0, 32, 79, 28, 25, 12,
+	25, 29, 3, 37, 30, 30, 41, 84, 32, 31, 61, 32, 61, 7, 56, 54, 39, 33,
+	66, 29, 3, 14, 75, 75, 78, 84, 75, 84, 25, 54, 25, 25, 107, 78, 27,
+	73, 0, 49, 96, 53, 50, 21, 10, 73, 58, 65, 27, 3, 27, 18, 54, 45, 69,
+	29, 3, 65, 31, 71, 76, 56, 54, 76, 54, 13, 5, 18, 142, 17, 3, 37,
+	114, 41, 25, 56, 0, 23, 3, 41, 22, 22, 31, 18, 48, 31, 58, 37, 75,
+	88, 3, 56, 1, 95, 19, 73, 52, 52, 4, 75, 26, 1, 25, 10, 1, 70, 31,
+	31, 12, 10, 54, 46, 11, 74, 84, 74, 8, 58, 23, 74, 8, 36, 11, 16, 94,
+	76, 14, 57, 65, 8, 22, 10, 36, 36, 96, 62, 103, 6, 75, 103, 58, 10,
+	15, 41, 75, 125, 58, 15, 10, 34, 29, 34, 4, 16, 29, 18, 18, 28, 71,
+	28, 43, 77, 18, 41, 41, 41, 62, 29, 96, 15, 106, 43, 15, 3, 43, 61,
+	3, 18, 103, 77, 29, 103, 19, 58, 84, 58, 1, 146, 32, 3, 70, 52, 54,
+	29, 70, 69, 124, 62, 1, 26, 38, 26, 3, 16, 26, 5, 51, 120, 41, 16, 1,
+	43, 34, 34, 29, 37, 56, 29, 96, 86, 54, 25, 84, 50, 34, 34, 93, 84,
+	96, 29, 29, 50, 50, 6, 1, 105, 78, 15, 37, 19, 50, 71, 36, 6, 54, 8,
+	28, 54, 75, 75, 16, 75, 131, 5, 25, 16, 69, 17, 69, 6, 96, 53, 96,
+	41, 119, 6, 6, 88, 50, 88, 52, 37, 0, 124, 73, 73, 7, 14, 36, 69, 79,
+	6, 114, 40, 79, 17, 77, 24, 44, 37, 69, 27, 37, 29, 33, 37, 50, 31,
+	69, 29, 101, 7, 61, 45, 17, 73, 37, 34, 18, 94, 22, 22, 63, 3, 25,
+	25, 17, 3, 90, 34, 34, 41, 34, 41, 54, 41, 54, 41, 41, 41, 163, 143,
+	96, 18, 32, 39, 86, 104, 11, 17, 17, 11, 86, 104, 78, 70, 52, 78, 17,
+	73, 91, 62, 7, 128, 50, 124, 18, 101, 46, 10, 75, 104, 73, 58, 132,
+	34, 13, 4, 95, 88, 33, 76, 74, 54, 62, 113, 114, 103, 32, 103, 69,
+	54, 53, 3, 11, 72, 31, 53, 102, 37, 53, 11, 81, 41, 10, 164, 10, 41,
+	31, 36, 113, 82, 3, 125, 62, 16, 4, 41, 41, 4, 128, 49, 138, 128, 74,
+	103, 0, 6, 101, 41, 142, 171, 39, 105, 121, 81, 62, 41, 81, 37, 3,
+	81, 69, 62, 3, 69, 70, 21, 29, 4, 91, 87, 37, 79, 36, 21, 71, 37, 41,
+	75, 128, 128, 15, 25, 3, 108, 73, 91, 62, 114, 62, 62, 36, 36, 15,
+	58, 114, 61, 114, 58, 105, 114, 41, 61, 176, 145, 46, 37, 30, 220,
+	77, 138, 15, 1, 128, 53, 50, 50, 58, 8, 91, 114, 105, 63, 91, 37, 37,
+	13, 169, 51, 102, 6, 102, 23, 105, 23, 58, 6, 29, 29, 19, 82, 29, 13,
+	36, 27, 29, 61, 12, 18, 127, 127, 12, 44, 102, 18, 4, 15, 206, 53,
+	127, 53, 17, 69, 69, 69, 29, 29, 109, 25, 102, 25, 53, 62, 99, 62,
+	62, 29, 62, 62, 45, 91, 125, 29, 29, 29, 4, 117, 72, 4, 30, 71, 71,
+	95, 79, 179, 71, 30, 53, 32, 32, 49, 25, 91, 25, 26, 26, 103, 123,
+	26, 41, 162, 78, 52, 103, 25, 6, 142, 94, 45, 45, 94, 127, 94, 94,
+	94, 47, 209, 138, 39, 39, 19, 154, 73, 67, 91, 27, 91, 84, 4, 84, 91,
+	12, 14, 165, 142, 54, 69, 192, 157, 185, 8, 95, 25, 62, 103, 103, 95,
+	71, 97, 62, 128, 0, 29, 51, 16, 94, 16, 16, 51, 0, 29, 85, 10, 105,
+	16, 29, 29, 13, 29, 4, 4, 132, 23, 95, 25, 54, 41, 29, 50, 70, 58,
+	142, 72, 70, 15, 72, 54, 29, 22, 145, 29, 127, 29, 85, 58, 101, 34,
+	165, 91, 46, 46, 25, 185, 25, 77, 128, 46, 128, 46, 188, 114, 46, 25,
+	45, 45, 114, 145, 114, 15, 102, 142, 8, 73, 31, 139, 157, 13, 79, 13,
+	114, 150, 8, 90, 91, 123, 69, 82, 132, 8, 18, 10, 102, 103, 114, 103,
+	8, 103, 13, 115, 55, 62, 3, 8, 154, 114, 99, 19, 8, 31, 73, 19, 99,
+	10, 6, 121, 32, 13, 32, 119, 32, 29, 145, 30, 13, 13, 114, 145, 32,
+	1, 123, 39, 29, 31, 69, 31, 140, 72, 72, 25, 25, 123, 25, 123, 8, 4,
+	85, 8, 25, 39, 25, 39, 85, 138, 25, 138, 25, 33, 102, 70, 25, 25, 31,
+	25, 25, 192, 69, 69, 114, 145, 120, 120, 8, 33, 98, 15, 212, 155, 8,
+	101, 8, 8, 98, 68, 155, 102, 132, 120, 30, 25, 123, 123, 101, 25,
+	123, 32, 24, 94, 145, 32, 24, 94, 118, 145, 101, 53, 53, 25, 128,
+	173, 142, 81, 81, 69, 33, 33, 125, 4, 1, 17, 27, 4, 17, 102, 27, 13,
+	25, 128, 71, 13, 39, 53, 13, 53, 47, 39, 23, 128, 53, 39, 47, 39,
+	135, 158, 136, 36, 36, 27, 157, 47, 76, 213, 47, 156, 25, 25, 53, 25,
+	53, 25, 86, 27, 159, 25, 62, 79, 39, 79, 25, 145, 49, 25, 143, 13,
+	114, 150, 130, 94, 102, 39, 4, 39, 61, 77, 228, 22, 25, 47, 119, 205,
+	122, 119, 205, 119, 22, 119, 258, 143, 22, 81, 179, 22, 22, 143, 25,
+	65, 53, 168, 36, 79, 175, 37, 79, 70, 79, 103, 70, 25, 175, 4, 96,
+	96, 49, 128, 138, 96, 22, 62, 47, 95, 105, 95, 62, 95, 62, 142, 103,
+	69, 103, 30, 103, 34, 173, 127, 70, 127, 132, 18, 85, 22, 71, 18,
+	206, 206, 18, 128, 145, 70, 193, 188, 8, 125, 114, 70, 128, 114, 145,
+	102, 25, 12, 108, 102, 94, 10, 102, 1, 102, 124, 22, 22, 118, 132,
+	22, 116, 75, 41, 63, 41, 189, 208, 55, 85, 69, 8, 71, 53, 71, 69,
+	102, 165, 41, 99, 69, 33, 33, 29, 156, 102, 13, 251, 102, 25, 13,
+	109, 102, 164, 102, 164, 102, 25, 29, 228, 29, 259, 179, 222, 95, 94,
+	30, 30, 30, 142, 55, 142, 72, 55, 102, 128, 17, 69, 164, 165, 3, 164,
+	36, 165, 27, 27, 45, 21, 21, 237, 113, 83, 231, 106, 13, 154, 13,
+	154, 128, 154, 148, 258, 25, 154, 128, 3, 27, 10, 145, 145, 21, 146,
+	25, 1, 185, 121, 0, 1, 95, 55, 95, 95, 30, 0, 27, 95, 0, 95, 8, 222,
+	27, 121, 30, 95, 121, 0, 98, 94, 131, 55, 95, 95, 30, 98, 30, 0, 91,
+	145, 66, 179, 66, 58, 175, 29, 0, 31, 173, 146, 160, 39, 53, 28, 123,
+	199, 123, 175, 146, 156, 54, 54, 149, 25, 70, 178, 128, 25, 70, 70,
+	94, 224, 54, 4, 54, 54, 25, 228, 160, 206, 165, 143, 206, 108, 220,
+	234, 160, 13, 169, 103, 103, 103, 91, 213, 222, 91, 103, 91, 103, 31,
+	30, 123, 13, 62, 103, 50, 106, 42, 13, 145, 114, 220, 65, 8, 8, 175,
+	11, 104, 94, 118, 132, 27, 118, 193, 27, 128, 127, 127, 183, 33, 30,
+	29, 103, 128, 61, 234, 165, 41, 29, 193, 33, 207, 41, 165, 165, 55,
+	81, 157, 157, 8, 81, 11, 27, 8, 8, 98, 96, 142, 145, 41, 179, 112,
+	62, 180, 206, 206, 165, 39, 241, 45, 151, 26, 197, 102, 192, 125,
+	128, 67, 128, 69, 128, 197, 33, 125, 102, 13, 103, 25, 30, 12, 30,
+	12, 30, 25, 77, 12, 25, 180, 27, 10, 69, 235, 228, 343, 118, 69, 41,
+	8, 69, 175, 25, 69, 25, 125, 41, 25, 41, 8, 155, 146, 155, 146, 155,
+	206, 168, 128, 157, 27, 273, 211, 211, 168, 11, 173, 154, 77, 173,
+	77, 102, 102, 102, 8, 85, 95, 102, 157, 28, 122, 234, 122, 157, 235,
+	222, 241, 10, 91, 179, 25, 13, 25, 41, 25, 206, 41, 6, 41, 158, 206,
+	206, 33, 296, 296, 33, 228, 69, 8, 114, 148, 33, 29, 66, 27, 27, 30,
+	233, 54, 173, 108, 106, 108, 108, 53, 103, 33, 33, 33, 176, 27, 27,
+	205, 164, 105, 237, 41, 27, 72, 165, 29, 29, 259, 132, 132, 132, 364,
+	71, 71, 27, 94, 160, 127, 51, 234, 55, 27, 95, 94, 165, 55, 55, 41,
+	0, 41, 128, 4, 123, 173, 6, 164, 157, 121, 121, 154, 86, 164, 164,
+	25, 93, 164, 25, 164, 210, 284, 62, 93, 30, 25, 25, 30, 30, 260, 130,
+	25, 125, 57, 53, 166, 166, 166, 185, 166, 158, 94, 113, 215, 159, 62,
+	99, 21, 172, 99, 184, 62, 259, 4, 21, 21, 77, 62, 173, 41, 146, 6,
+	41, 128, 121, 41, 11, 121, 103, 159, 164, 175, 206, 91, 103, 164, 72,
+	25, 129, 72, 206, 129, 33, 103, 102, 102, 29, 13, 11, 251, 234, 135,
+	31, 8, 123, 65, 91, 121, 129, 65, 243, 10, 91, 8, 65, 70, 228, 220,
+	243, 91, 10, 10, 30, 178, 91, 178, 33, 21, 25, 235, 165, 11, 161,
+	158, 27, 27, 30, 128, 75, 36, 30, 36, 36, 173, 25, 33, 178, 112, 162,
+	112, 112, 112, 162, 33, 33, 178, 123, 123, 39, 106, 91, 106, 106,
+	158, 106, 106, 284, 39, 230, 21, 228, 11, 21, 228, 159, 241, 62, 10,
+	62, 10, 68, 234, 39, 39, 138, 62, 22, 27, 183, 22, 215, 10, 175, 175,
+	353, 228, 42, 193, 175, 175, 27, 98, 27, 193, 150, 27, 173, 17, 233,
+	233, 25, 102, 123, 152, 242, 108, 4, 94, 176, 13, 41, 219, 17, 151,
+	22, 103, 103, 53, 128, 233, 284, 25, 265, 128, 39, 39, 138, 42, 39,
+	21, 86, 95, 127, 29, 91, 46, 103, 103, 215, 25, 123, 123, 230, 25,
+	193, 180, 30, 60, 30, 242, 136, 180, 193, 30, 206, 180, 60, 165, 206,
+	193, 165, 123, 164, 103, 68, 25, 70, 91, 25, 82, 53, 82, 186, 53, 82,
+	53, 25, 30, 282, 91, 13, 234, 160, 160, 126, 149, 36, 36, 160, 149,
+	178, 160, 39, 294, 149, 149, 160, 39, 95, 221, 186, 106, 178, 316,
+	267, 53, 53, 164, 159, 164, 165, 94, 228, 53, 52, 178, 183, 53, 294,
+	128, 55, 140, 294, 25, 95, 366, 15, 304, 13, 183, 77, 230, 6, 136,
+	235, 121, 311, 273, 36, 158, 235, 230, 98, 201, 165, 165, 165, 91,
+	175, 248, 39, 185, 128, 39, 39, 128, 313, 91, 36, 219, 130, 25, 130,
+	234, 234, 130, 234, 121, 205, 304, 94, 77, 64, 259, 60, 60, 60, 77,
+	242, 60, 145, 95, 270, 18, 91, 199, 159, 91, 235, 58, 249, 26, 123,
+	114, 29, 15, 191, 15, 30, 55, 55, 347, 4, 29, 15, 4, 341, 93, 7, 30,
+	23, 7, 121, 266, 178, 261, 70, 169, 25, 25, 158, 169, 25, 169, 270,
+	270, 13, 128, 327, 103, 55, 128, 103, 136, 159, 103, 327, 41, 32,
+	111, 111, 114, 173, 215, 173, 25, 173, 180, 114, 173, 173, 98, 93,
+	25, 160, 157, 159, 160, 159, 159, 160, 320, 35, 193, 221, 33, 36,
+	136, 248, 91, 215, 125, 215, 156, 68, 125, 125, 1, 287, 123, 94, 30,
+	184, 13, 30, 94, 123, 206, 12, 206, 289, 128, 122, 184, 128, 289,
+	178, 29, 26, 206, 178, 65, 206, 128, 192, 102, 197, 36, 94, 94, 155,
+	10, 36, 121, 280, 121, 368, 192, 121, 121, 179, 121, 36, 54, 192,
+	121, 192, 197, 118, 123, 224, 118, 10, 192, 10, 91, 269, 91, 49, 206,
+	184, 185, 62, 8, 49, 289, 30, 5, 55, 30, 42, 39, 220, 298, 42, 347,
+	42, 234, 42, 70, 42, 55, 321, 129, 172, 173, 172, 13, 98, 129, 325,
+	235, 284, 362, 129, 233, 345, 175, 261, 175, 60, 261, 58, 289, 99,
+	99, 99, 206, 99, 36, 175, 29, 25, 432, 125, 264, 168, 173, 69, 158,
+	273, 179, 164, 69, 158, 69, 8, 95, 192, 30, 164, 101, 44, 53, 273,
+	335, 273, 53, 45, 128, 45, 234, 123, 105, 103, 103, 224, 36, 90, 211,
+	282, 264, 91, 228, 91, 166, 264, 228, 398, 50, 101, 91, 264, 73, 36,
+	25, 73, 50, 50, 242, 36, 36, 58, 165, 204, 353, 165, 125, 320, 128,
+	298, 298, 180, 128, 60, 102, 30, 30, 53, 179, 234, 325, 234, 175, 21,
+	250, 215, 103, 21, 21, 250, 91, 211, 91, 313, 301, 323, 215, 228,
+	160, 29, 29, 81, 53, 180, 146, 248, 66, 159, 39, 98, 323, 98, 36, 95,
+	218, 234, 39, 82, 82, 230, 62, 13, 62, 230, 13, 30, 98, 0, 8, 98, 8,
+	98, 91, 267, 121, 197, 30, 78, 27, 78, 102, 27, 298, 160, 103, 264,
+	264, 264, 175, 17, 273, 273, 165, 31, 160, 17, 99, 17, 99, 234, 31,
+	17, 99, 36, 26, 128, 29, 214, 353, 264, 102, 36, 102, 264, 264, 273,
+	273, 4, 16, 138, 138, 264, 128, 313, 25, 420, 60, 10, 280, 264, 60,
+	60, 103, 178, 125, 178, 29, 327, 29, 36, 30, 36, 4, 52, 183, 183,
+	173, 52, 31, 173, 31, 158, 31, 158, 31, 9, 31, 31, 353, 31, 353, 173,
+	415, 9, 17, 222, 31, 103, 31, 165, 27, 31, 31, 165, 27, 27, 206, 31,
+	31, 4, 4, 30, 4, 4, 264, 185, 159, 310, 273, 310, 173, 40, 4, 173, 4,
+	173, 4, 250, 250, 62, 188, 119, 250, 233, 62, 121, 105, 105, 54, 103,
+	111, 291, 236, 236, 103, 297, 36, 26, 316, 69, 183, 158, 206, 129,
+	160, 129, 184, 55, 179, 279, 11, 179, 347, 160, 184, 129, 179, 351,
+	179, 353, 179, 129, 129, 351, 11, 111, 93, 93, 235, 103, 173, 53, 93,
+	50, 111, 86, 123, 94, 36, 183, 60, 55, 55, 178, 219, 253, 321, 178,
+	235, 235, 183, 183, 204, 321, 219, 160, 193, 335, 121, 70, 69, 295,
+	159, 297, 231, 121, 231, 136, 353, 136, 121, 279, 215, 366, 215, 353,
+	159, 353, 353, 103, 31, 31, 298, 298, 30, 30, 165, 273, 25, 219, 35,
+	165, 259, 54, 36, 54, 54, 165, 71, 250, 327, 13, 289, 165, 196, 165,
+	165, 94, 233, 165, 94, 60, 165, 96, 220, 166, 271, 158, 397, 122, 53,
+	53, 137, 280, 272, 62, 30, 30, 30, 105, 102, 67, 140, 8, 67, 21, 270,
+	298, 69, 173, 298, 91, 179, 327, 86, 179, 88, 179, 179, 55, 123, 220,
+	233, 94, 94, 175, 13, 53, 13, 154, 191, 74, 83, 83, 325, 207, 83, 74,
+	83, 325, 74, 316, 388, 55, 55, 364, 55, 183, 434, 273, 273, 273, 164,
+	213, 11, 213, 327, 321, 21, 352, 185, 103, 13, 13, 55, 30, 323, 123,
+	178, 435, 178, 30, 175, 175, 30, 481, 527, 175, 125, 232, 306, 232,
+	206, 306, 364, 206, 270, 206, 232, 10, 30, 130, 160, 130, 347, 240,
+	30, 136, 130, 347, 136, 279, 298, 206, 30, 103, 273, 241, 70, 206,
+	306, 434, 206, 94, 94, 156, 161, 321, 321, 64, 161, 13, 183, 183, 83,
+	161, 13, 169, 13, 159, 36, 173, 159, 36, 36, 230, 235, 235, 159, 159,
+	335, 312, 42, 342, 264, 39, 39, 39, 34, 298, 36, 36, 252, 164, 29,
+	493, 29, 387, 387, 435, 493, 132, 273, 105, 132, 74, 73, 206, 234,
+	273, 206, 95, 15, 280, 280, 280, 280, 397, 273, 273, 242, 397, 280,
+	397, 397, 397, 273, 397, 280, 230, 137, 353, 67, 81, 137, 137, 353,
+	259, 312, 114, 164, 164, 25, 77, 21, 77, 165, 30, 30, 231, 234, 121,
+	234, 312, 121, 364, 136, 123, 123, 136, 123, 136, 150, 264, 285, 30,
+	166, 93, 30, 39, 224, 136, 39, 355, 355, 397, 67, 67, 25, 67, 25,
+	298, 11, 67, 264, 374, 99, 150, 321, 67, 70, 67, 295, 150, 29, 321,
+	150, 70, 29, 142, 355, 311, 173, 13, 253, 103, 114, 114, 70, 192, 22,
+	128, 128, 183, 184, 70, 77, 215, 102, 292, 30, 123, 279, 292, 142,
+	33, 215, 102, 468, 123, 468, 473, 30, 292, 215, 30, 213, 443, 473,
+	215, 234, 279, 279, 279, 279, 265, 443, 206, 66, 313, 34, 30, 206,
+	30, 51, 15, 206, 41, 434, 41, 398, 67, 30, 301, 67, 36, 3, 285, 437,
+	136, 136, 22, 136, 145, 365, 323, 323, 145, 136, 22, 453, 99, 323,
+	353, 9, 258, 323, 231, 128, 231, 382, 150, 420, 39, 94, 29, 29, 353,
+	22, 22, 347, 353, 39, 29, 22, 183, 8, 284, 355, 388, 284, 60, 64, 99,
+	60, 64, 150, 95, 150, 364, 150, 95, 150, 6, 236, 383, 544, 81, 206,
+	388, 206, 58, 159, 99, 231, 228, 363, 363, 121, 99, 121, 121, 99,
+	422, 544, 273, 173, 121, 427, 102, 121, 235, 284, 179, 25, 197, 25,
+	179, 511, 70, 368, 70, 25, 388, 123, 368, 159, 213, 410, 159, 236,
+	127, 159, 21, 373, 184, 424, 327, 250, 176, 176, 175, 284, 316, 176,
+	284, 327, 111, 250, 284, 175, 175, 264, 111, 176, 219, 111, 427, 427,
+	176, 284, 427, 353, 428, 55, 184, 493, 158, 136, 99, 287, 264, 334,
+	264, 213, 213, 292, 481, 93, 264, 292, 295, 295, 6, 367, 279, 173,
+	308, 285, 158, 308, 335, 299, 137, 137, 572, 41, 137, 137, 41, 94,
+	335, 220, 36, 224, 420, 36, 265, 265, 91, 91, 71, 123, 264, 91, 91,
+	123, 107, 30, 22, 292, 35, 241, 356, 298, 14, 298, 441, 35, 121, 71,
+	63, 130, 63, 488, 363, 71, 63, 307, 194, 71, 71, 220, 121, 125, 71,
+	220, 71, 71, 71, 71, 235, 265, 353, 128, 155, 128, 420, 400, 130,
+	173, 183, 183, 184, 130, 173, 183, 13, 183, 130, 130, 183, 183, 353,
+	353, 183, 242, 183, 183, 306, 324, 324, 321, 306, 321, 6, 6, 128,
+	306, 242, 242, 306, 183, 183, 6, 183, 321, 486, 183, 164, 30, 78,
+	138, 158, 138, 34, 206, 362, 55, 70, 67, 21, 375, 136, 298, 81, 298,
+	298, 298, 230, 121, 30, 230, 311, 240, 311, 311, 158, 204, 136, 136,
+	184, 136, 264, 311, 311, 312, 312, 72, 311, 175, 264, 91, 175, 264,
+	121, 461, 312, 312, 238, 475, 350, 512, 350, 312, 313, 350, 312, 366,
+	294, 30, 253, 253, 253, 388, 158, 388, 22, 388, 22, 388, 103, 321,
+	321, 253, 7, 437, 103, 114, 242, 114, 114, 242, 114, 114, 242, 242,
+	242, 306, 242, 114, 7, 353, 335, 27, 241, 299, 312, 364, 506, 409,
+	94, 462, 230, 462, 243, 230, 175, 175, 462, 461, 230, 428, 426, 175,
+	175, 165, 175, 175, 372, 183, 572, 102, 85, 102, 538, 206, 376, 85,
+	85, 284, 85, 85, 284, 398, 83, 160, 265, 308, 398, 310, 583, 289,
+	279, 273, 285, 490, 490, 211, 292, 292, 158, 398, 30, 220, 169, 368,
+	368, 368, 169, 159, 368, 93, 368, 368, 93, 169, 368, 368, 443, 368,
+	298, 443, 368, 298, 538, 345, 345, 311, 178, 54, 311, 215, 178, 175,
+	222, 264, 475, 264, 264, 475, 478, 289, 63, 236, 63, 299, 231, 296,
+	397, 299, 158, 36, 164, 164, 21, 492, 21, 164, 21, 164, 403, 26, 26,
+	588, 179, 234, 169, 465, 295, 67, 41, 353, 295, 538, 161, 185, 306,
+	323, 68, 420, 323, 82, 241, 241, 36, 53, 493, 301, 292, 241, 250, 63,
+	63, 103, 442, 353, 185, 353, 321, 353, 185, 353, 353, 185, 409, 353,
+	589, 34, 271, 271, 34, 86, 34, 34, 353, 353, 39, 414, 4, 95, 95, 4,
+	225, 95, 4, 121, 30, 552, 136, 159, 159, 514, 159, 159, 54, 514, 206,
+	136, 206, 159, 74, 235, 235, 312, 54, 312, 42, 156, 422, 629, 54,
+	465, 265, 165, 250, 35, 165, 175, 659, 175, 175, 8, 8, 8, 8, 206,
+	206, 206, 50, 435, 206, 432, 230, 230, 234, 230, 94, 299, 299, 285,
+	184, 41, 93, 299, 299, 285, 41, 285, 158, 285, 206, 299, 41, 36, 396,
+	364, 364, 120, 396, 514, 91, 382, 538, 807, 717, 22, 93, 412, 54,
+	215, 54, 298, 308, 148, 298, 148, 298, 308, 102, 656, 6, 148, 745,
+	128, 298, 64, 407, 273, 41, 172, 64, 234, 250, 398, 181, 445, 95,
+	236, 441, 477, 504, 102, 196, 137, 364, 60, 453, 137, 364, 367, 334,
+	364, 299, 196, 397, 630, 589, 589, 196, 646, 337, 235, 128, 128, 343,
+	289, 235, 324, 427, 324, 58, 215, 215, 461, 425, 461, 387, 440, 285,
+	440, 440, 285, 387, 632, 325, 325, 440, 461, 425, 425, 387, 627, 191,
+	285, 440, 308, 55, 219, 280, 308, 265, 538, 183, 121, 30, 236, 206,
+	30, 455, 236, 30, 30, 705, 83, 228, 280, 468, 132, 8, 132, 132, 128,
+	409, 173, 353, 132, 409, 35, 128, 450, 137, 398, 67, 432, 423, 235,
+	235, 388, 306, 93, 93, 452, 300, 190, 13, 452, 388, 30, 452, 13, 30,
+	13, 30, 306, 362, 234, 721, 635, 809, 784, 67, 498, 498, 67, 353,
+	635, 67, 183, 159, 445, 285, 183, 53, 183, 445, 265, 432, 57, 420,
+	432, 420, 477, 327, 55, 60, 105, 183, 218, 104, 104, 475, 239, 582,
+	151, 239, 104, 732, 41, 26, 784, 86, 300, 215, 36, 64, 86, 86, 675,
+	294, 64, 86, 528, 550, 493, 565, 298, 230, 312, 295, 538, 298, 295,
+	230, 54, 374, 516, 441, 54, 54, 323, 401, 401, 382, 159, 837, 159,
+	54, 401, 592, 159, 401, 417, 610, 264, 150, 323, 452, 185, 323, 323,
+	185, 403, 185, 423, 165, 425, 219, 407, 270, 231, 99, 93, 231, 631,
+	756, 71, 364, 434, 213, 86, 102, 434, 102, 86, 23, 71, 335, 164, 323,
+	409, 381, 4, 124, 41, 424, 206, 41, 124, 41, 41, 703, 635, 124, 493,
+	41, 41, 487, 492, 124, 175, 124, 261, 600, 488, 261, 488, 261, 206,
+	677, 261, 308, 723, 908, 704, 691, 723, 488, 488, 441, 136, 476, 312,
+	136, 550, 572, 728, 550, 22, 312, 312, 22, 55, 413, 183, 280, 593,
+	191, 36, 36, 427, 36, 695, 592, 19, 544, 13, 468, 13, 544, 72, 437,
+	321, 266, 461, 266, 441, 230, 409, 93, 521, 521, 345, 235, 22, 142,
+	150, 102, 569, 235, 264, 91, 521, 264, 7, 102, 7, 498, 521, 235, 537,
+	235, 6, 241, 420, 420, 631, 41, 527, 103, 67, 337, 62, 264, 527, 131,
+	67, 174, 263, 264, 36, 36, 263, 581, 253, 465, 160, 286, 91, 160, 55,
+	4, 4, 631, 631, 608, 365, 465, 294, 427, 427, 335, 669, 669, 129, 93,
+	93, 93, 93, 74, 66, 758, 504, 347, 130, 505, 504, 143, 505, 550, 222,
+	13, 352, 529, 291, 538, 50, 68, 269, 130, 295, 130, 511, 295, 295,
+	130, 486, 132, 61, 206, 185, 368, 669, 22, 175, 492, 207, 373, 452,
+	432, 327, 89, 550, 496, 611, 527, 89, 527, 496, 550, 516, 516, 91,
+	136, 538, 264, 264, 124, 264, 264, 264, 264, 264, 535, 264, 150, 285,
+	398, 285, 582, 398, 475, 81, 694, 694, 64, 81, 694, 234, 607, 723,
+	513, 234, 64, 581, 64, 124, 64, 607, 234, 723, 717, 367, 64, 513,
+	607, 488, 183, 488, 450, 183, 550, 286, 183, 363, 286, 414, 67, 449,
+	449, 366, 215, 235, 95, 295, 295, 41, 335, 21, 445, 225, 21, 295,
+	372, 749, 461, 53, 481, 397, 427, 427, 427, 714, 481, 714, 427, 717,
+	165, 245, 486, 415, 245, 415, 486, 274, 415, 441, 456, 300, 548, 300,
+	422, 422, 757, 11, 74, 430, 430, 136, 409, 430, 749, 191, 819, 592,
+	136, 364, 465, 231, 231, 918, 160, 589, 160, 160, 465, 465, 231, 157,
+	538, 538, 259, 538, 326, 22, 22, 22, 179, 22, 22, 550, 179, 287, 287,
+	417, 327, 498, 498, 287, 488, 327, 538, 488, 583, 488, 287, 335, 287,
+	335, 287, 41, 287, 335, 287, 327, 441, 335, 287, 488, 538, 327, 498,
+	8, 8, 374, 8, 64, 427, 8, 374, 417, 760, 409, 373, 160, 423, 206,
+	160, 106, 499, 160, 271, 235, 160, 590, 353, 695, 478, 619, 590, 353,
+	13, 63, 189, 420, 605, 427, 643, 121, 280, 415, 121, 415, 595, 417,
+	121, 398, 55, 330, 463, 463, 123, 353, 330, 582, 309, 582, 582, 405,
+	330, 550, 405, 582, 353, 309, 308, 60, 353, 7, 60, 71, 353, 189, 183,
+	183, 183, 582, 755, 189, 437, 287, 189, 183, 668, 481, 384, 384, 481,
+	481, 481, 477, 582, 582, 499, 650, 481, 121, 461, 231, 36, 235, 36,
+	413, 235, 209, 36, 689, 114, 353, 353, 235, 592, 36, 353, 413, 209,
+	70, 308, 70, 699, 308, 70, 213, 292, 86, 689, 465, 55, 508, 128, 452,
+	29, 41, 681, 573, 352, 21, 21, 648, 648, 69, 509, 409, 21, 264, 21,
+	509, 514, 514, 409, 21, 264, 443, 443, 427, 160, 433, 663, 433, 231,
+	646, 185, 482, 646, 433, 13, 398, 172, 234, 42, 491, 172, 234, 234,
+	832, 775, 172, 196, 335, 822, 461, 298, 461, 364, 1120, 537, 169,
+	169, 364, 694, 219, 612, 231, 740, 42, 235, 321, 279, 960, 279, 353,
+	492, 159, 572, 321, 159, 287, 353, 287, 287, 206, 206, 321, 287, 159,
+	321, 492, 159, 55, 572, 600, 270, 492, 784, 173, 91, 91, 443, 443,
+	582, 261, 497, 572, 91, 555, 352, 206, 261, 555, 285, 91, 555, 497,
+	83, 91, 619, 353, 488, 112, 4, 592, 295, 295, 488, 235, 231, 769,
+	568, 581, 671, 451, 451, 483, 299, 1011, 432, 422, 207, 106, 701,
+	508, 555, 508, 555, 125, 870, 555, 589, 508, 125, 749, 482, 125, 125,
+	130, 544, 643, 643, 544, 488, 22, 643, 130, 335, 544, 22, 130, 544,
+	544, 488, 426, 426, 4, 180, 4, 695, 35, 54, 433, 500, 592, 433, 262,
+	94, 401, 401, 106, 216, 216, 106, 521, 102, 462, 518, 271, 475, 365,
+	193, 648, 206, 424, 206, 193, 206, 206, 424, 299, 590, 590, 364, 621,
+	67, 538, 488, 567, 51, 51, 513, 194, 81, 488, 486, 289, 567, 563,
+	749, 563, 338, 338, 502, 563, 822, 338, 563, 338, 502, 201, 230, 201,
+	533, 445, 175, 201, 175, 13, 85, 960, 103, 85, 175, 30, 445, 445,
+	175, 573, 196, 877, 287, 356, 678, 235, 489, 312, 572, 264, 717, 138,
+	295, 6, 295, 523, 55, 165, 165, 295, 138, 663, 6, 295, 6, 353, 138,
+	6, 138, 169, 129, 784, 12, 129, 194, 605, 784, 445, 234, 627, 563,
+	689, 627, 647, 570, 627, 570, 647, 206, 234, 215, 234, 816, 627, 816,
+	234, 627, 215, 234, 627, 264, 427, 427, 30, 424, 161, 161, 916, 740,
+	180, 616, 481, 514, 383, 265, 481, 164, 650, 121, 582, 689, 420, 669,
+	589, 420, 788, 549, 165, 734, 280, 224, 146, 681, 788, 184, 398, 784,
+	4, 398, 417, 417, 398, 636, 784, 417, 81, 398, 417, 81, 185, 827,
+	420, 241, 420, 41, 185, 185, 718, 241, 101, 185, 185, 241, 241, 241,
+	241, 241, 185, 324, 420, 420, 1011, 420, 827, 241, 184, 563, 241,
+	183, 285, 529, 285, 808, 822, 891, 822, 488, 285, 486, 619, 55, 869,
+	39, 567, 39, 289, 203, 158, 289, 710, 818, 158, 818, 355, 29, 409,
+	203, 308, 648, 792, 308, 308, 91, 308, 6, 592, 792, 106, 106, 308,
+	41, 178, 91, 751, 91, 259, 734, 166, 36, 327, 166, 230, 205, 205,
+	172, 128, 230, 432, 623, 838, 623, 432, 278, 432, 42, 916, 432, 694,
+	623, 352, 452, 93, 314, 93, 93, 641, 88, 970, 914, 230, 61, 159, 270,
+	159, 493, 159, 755, 159, 409, 30, 30, 836, 128, 241, 99, 102, 984,
+	538, 102, 102, 273, 639, 838, 102, 102, 136, 637, 508, 627, 285, 465,
+	327, 327, 21, 749, 327, 749, 21, 845, 21, 21, 409, 749, 1367, 806,
+	616, 714, 253, 616, 714, 714, 112, 375, 21, 112, 375, 375, 51, 51,
+	51, 51, 393, 206, 870, 713, 193, 802, 21, 1061, 42, 382, 42, 543,
+	876, 42, 876, 382, 696, 543, 635, 490, 353, 353, 417, 64, 1257, 271,
+	64, 377, 127, 127, 537, 417, 905, 353, 538, 465, 605, 876, 427, 324,
+	514, 852, 427, 53, 427, 557, 173, 173, 7, 1274, 563, 31, 31, 31, 745,
+	392, 289, 230, 230, 230, 91, 218, 327, 420, 420, 128, 901, 552, 420,
+	230, 608, 552, 476, 347, 476, 231, 159, 137, 716, 648, 716, 627, 740,
+	718, 679, 679, 6, 718, 740, 6, 189, 679, 125, 159, 757, 1191, 409,
+	175, 250, 409, 67, 324, 681, 605, 550, 398, 550, 931, 478, 174, 21,
+	316, 91, 316, 654, 409, 425, 425, 699, 61, 699, 321, 698, 321, 698,
+	61, 425, 699, 321, 409, 699, 299, 335, 321, 335, 61, 698, 699, 654,
+	698, 299, 425, 231, 14, 121, 515, 121, 14, 165, 81, 409, 189, 81,
+	373, 465, 463, 1055, 507, 81, 81, 189, 1246, 321, 409, 886, 104, 842,
+	689, 300, 740, 380, 656, 656, 832, 656, 380, 300, 300, 206, 187, 175,
+	142, 465, 206, 271, 468, 215, 560, 83, 215, 83, 215, 215, 83, 175,
+	215, 83, 83, 111, 206, 756, 559, 756, 1367, 206, 559, 1015, 559, 559,
+	946, 1015, 548, 559, 756, 1043, 756, 698, 159, 414, 308, 458, 997,
+	663, 663, 347, 39, 755, 838, 323, 755, 323, 159, 159, 717, 159, 21,
+	41, 128, 516, 159, 717, 71, 870, 755, 159, 740, 717, 374, 516, 740,
+	51, 148, 335, 148, 335, 791, 120, 364, 335, 335, 51, 120, 251, 538,
+	251, 971, 1395, 538, 78, 178, 538, 538, 918, 129, 918, 129, 538, 538,
+	656, 129, 538, 538, 129, 538, 1051, 538, 128, 838, 931, 998, 823,
+	1095, 334, 870, 334, 367, 550, 1061, 498, 745, 832, 498, 745, 716,
+	498, 498, 128, 997, 832, 716, 832, 130, 642, 616, 497, 432, 432, 432,
+	432, 642, 159, 432, 46, 230, 788, 160, 230, 478, 46, 693, 103, 920,
+	230, 589, 643, 160, 616, 432, 165, 165, 583, 592, 838, 784, 583, 710,
+	6, 583, 583, 6, 35, 230, 838, 592, 710, 6, 589, 230, 838, 30, 592,
+	583, 6, 583, 6, 6, 583, 30, 30, 6, 375, 375, 99, 36, 1158, 425, 662,
+	417, 681, 364, 375, 1025, 538, 822, 669, 893, 538, 538, 450, 409,
+	632, 527, 632, 563, 632, 527, 550, 71, 698, 550, 39, 550, 514, 537,
+	514, 537, 111, 41, 173, 592, 173, 648, 173, 173, 173, 1011, 514, 173,
+	173, 514, 166, 648, 355, 161, 166, 648, 497, 327, 327, 550, 650, 21,
+	425, 605, 555, 103, 425, 605, 842, 836, 1011, 636, 138, 756, 836,
+	756, 756, 353, 1011, 636, 636, 1158, 741, 741, 842, 756, 741, 1011,
+	677, 1011, 770, 366, 306, 488, 920, 920, 665, 775, 502, 500, 775,
+	775, 648, 364, 833, 207, 13, 93, 500, 364, 500, 665, 500, 93, 295,
+	183, 1293, 313, 272, 313, 279, 303, 93, 516, 93, 1013, 381, 6, 93,
+	93, 303, 259, 643, 168, 673, 230, 1261, 230, 230, 673, 1060, 1079,
+	1079, 550, 741, 741, 590, 527, 741, 741, 442, 741, 442, 848, 741,
+	590, 925, 219, 527, 925, 335, 442, 590, 239, 590, 590, 590, 239, 527,
+	239, 1033, 230, 734, 241, 741, 230, 549, 548, 1015, 1015, 32, 36,
+	433, 465, 724, 465, 73, 73, 73, 465, 808, 73, 592, 1430, 250, 154,
+	154, 250, 538, 353, 353, 353, 353, 353, 175, 194, 206, 538, 632,
+	1163, 960, 175, 175, 538, 452, 632, 1163, 175, 538, 960, 194, 175,
+	194, 632, 960, 632, 94, 632, 461, 960, 1163, 1163, 461, 632, 960,
+	755, 707, 105, 382, 625, 382, 382, 784, 707, 871, 559, 387, 387, 871,
+	784, 559, 784, 88, 36, 570, 314, 1028, 975, 335, 335, 398, 573, 573,
+	573, 21, 215, 562, 738, 612, 424, 21, 103, 788, 870, 912, 23, 186,
+	757, 73, 818, 23, 73, 563, 952, 262, 563, 137, 262, 1022, 952, 137,
+	1273, 442, 952, 604, 137, 308, 384, 913, 235, 325, 695, 398, 95, 668,
+	776, 713, 309, 691, 22, 10, 364, 682, 682, 578, 481, 1252, 1072,
+	1252, 825, 578, 825, 1072, 1149, 592, 273, 387, 273, 427, 155, 1204,
+	50, 452, 50, 1142, 50, 367, 452, 1142, 611, 367, 50, 50, 367, 50,
+	1675, 99, 367, 50, 1501, 1099, 830, 681, 689, 917, 1089, 453, 425,
+	235, 918, 538, 550, 335, 161, 387, 859, 324, 21, 838, 859, 1123, 21,
+	723, 21, 335, 335, 206, 21, 364, 1426, 21, 838, 838, 335, 364, 21,
+	21, 859, 920, 838, 838, 397, 81, 639, 397, 397, 588, 933, 933, 784,
+	222, 830, 36, 36, 222, 1251, 266, 36, 146, 266, 366, 581, 605, 366,
+	22, 966, 681, 681, 433, 730, 1013, 550, 21, 21, 938, 488, 516, 21,
+	21, 656, 420, 323, 323, 323, 327, 323, 918, 581, 581, 830, 361, 830,
+	364, 259, 364, 496, 496, 364, 691, 705, 691, 475, 427, 1145, 600,
+	179, 427, 527, 749, 869, 689, 335, 347, 220, 298, 689, 1426, 183,
+	554, 55, 832, 550, 550, 165, 770, 957, 67, 1386, 219, 683, 683, 355,
+	683, 355, 355, 738, 355, 842, 931, 266, 325, 349, 256, 1113, 256,
+	423, 960, 554, 554, 325, 554, 508, 22, 142, 22, 508, 916, 767, 55,
+	1529, 767, 55, 1286, 93, 972, 550, 931, 1286, 1286, 972, 93, 1286,
+	1392, 890, 93, 1286, 93, 1286, 972, 374, 931, 890, 808, 779, 975,
+	975, 175, 173, 4, 681, 383, 1367, 173, 383, 1367, 383, 173, 175, 69,
+	238, 146, 238, 36, 148, 888, 238, 173, 238, 148, 238, 888, 185, 925,
+	925, 797, 925, 815, 925, 469, 784, 289, 784, 925, 797, 925, 925,
+	1093, 925, 925, 925, 1163, 797, 797, 815, 925, 1093, 784, 636, 663,
+	925, 187, 922, 316, 1380, 709, 916, 916, 187, 355, 948, 916, 187,
+	916, 916, 948, 948, 916, 355, 316, 316, 334, 300, 1461, 36, 583,
+	1179, 699, 235, 858, 583, 699, 858, 699, 1189, 1256, 1189, 699, 797,
+	699, 699, 699, 699, 427, 488, 427, 488, 175, 815, 656, 656, 150, 322,
+	465, 322, 870, 465, 1099, 582, 665, 767, 749, 635, 749, 600, 1448,
+	36, 502, 235, 502, 355, 502, 355, 355, 355, 172, 355, 355, 95, 866,
+	425, 393, 1165, 42, 42, 42, 393, 939, 909, 909, 836, 552, 424, 1333,
+	852, 897, 1426, 1333, 1446, 1426, 997, 1011, 852, 1198, 55, 32, 239,
+	588, 681, 681, 239, 1401, 32, 588, 239, 462, 286, 1260, 984, 1160,
+	960, 960, 486, 828, 462, 960, 1199, 581, 850, 663, 581, 751, 581,
+	581, 1571, 252, 252, 1283, 264, 430, 264, 430, 430, 842, 252, 745,
+	21, 307, 681, 1592, 488, 857, 857, 1161, 857, 857, 857, 138, 374,
+	374, 1196, 374, 1903, 1782, 1626, 414, 112, 1477, 1040, 356, 775,
+	414, 414, 112, 356, 775, 435, 338, 1066, 689, 689, 1501, 689, 1249,
+	205, 689, 765, 220, 308, 917, 308, 308, 220, 327, 387, 838, 917, 917,
+	917, 220, 662, 308, 220, 387, 387, 220, 220, 308, 308, 308, 387,
+	1009, 1745, 822, 279, 554, 1129, 543, 383, 870, 1425, 241, 870, 241,
+	383, 716, 592, 21, 21, 592, 425, 550, 550, 550, 427, 230, 57, 483,
+	784, 860, 57, 308, 57, 486, 870, 447, 486, 433, 433, 870, 433, 997,
+	486, 443, 433, 433, 997, 486, 1292, 47, 708, 81, 895, 394, 81, 935,
+	81, 81, 81, 374, 986, 916, 1103, 1095, 465, 495, 916, 667, 1745, 518,
+	220, 1338, 220, 734, 1294, 741, 166, 828, 741, 741, 1165, 1371, 1371,
+	471, 1371, 647, 1142, 1878, 1878, 1371, 1371, 822, 66, 327, 158, 427,
+	427, 465, 465, 676, 676, 30, 30, 676, 676, 893, 1592, 93, 455, 308,
+	582, 695, 582, 629, 582, 85, 1179, 85, 85, 1592, 1179, 280, 1027,
+	681, 398, 1027, 398, 295, 784, 740, 509, 425, 968, 509, 46, 833, 842,
+	401, 184, 401, 464, 6, 1501, 1501, 550, 538, 883, 538, 883, 883, 883,
+	1129, 550, 550, 333, 689, 948, 21, 21, 241, 2557, 2094, 273, 308, 58,
+	863, 893, 1086, 409, 136, 1086, 592, 592, 830, 830, 883, 830, 277,
+	68, 689, 902, 277, 453, 507, 129, 689, 630, 664, 550, 128, 1626,
+	1626, 128, 902, 312, 589, 755, 755, 589, 755, 407, 1782, 589, 784,
+	1516, 1118, 407, 407, 1447, 589, 235, 755, 1191, 235, 235, 407, 128,
+	589, 1118, 21, 383, 1331, 691, 481, 383, 1129, 1129, 1261, 1104,
+	1378, 1129, 784, 1129, 1261, 1129, 947, 1129, 784, 784, 1129, 1129,
+	35, 1104, 35, 866, 1129, 1129, 64, 481, 730, 1260, 481, 970, 481,
+	481, 481, 481, 863, 481, 681, 699, 863, 486, 681, 481, 481, 55, 55,
+	235, 1364, 944, 632, 822, 401, 822, 952, 822, 822, 99, 550, 2240,
+	550, 70, 891, 860, 860, 550, 550, 916, 1176, 1530, 425, 1530, 916,
+	628, 1583, 916, 628, 916, 916, 628, 628, 425, 916, 1062, 1265, 916,
+	916, 916, 280, 461, 916, 916, 1583, 628, 1062, 916, 916, 677, 1297,
+	924, 1260, 83, 1260, 482, 433, 234, 462, 323, 1656, 997, 323, 323,
+	931, 838, 931, 1933, 1391, 367, 323, 931, 1391, 1391, 103, 1116,
+	1116, 1116, 769, 1195, 1218, 312, 791, 312, 741, 791, 997, 312, 334,
+	334, 312, 287, 287, 633, 1397, 1426, 605, 1431, 327, 592, 705, 1194,
+	592, 1097, 1118, 1503, 1267, 1267, 1267, 618, 1229, 734, 1089, 785,
+	1089, 1129, 1148, 1148, 1089, 915, 1148, 1129, 1148, 1011, 1011,
+	1229, 871, 1560, 1560, 1560, 563, 1537, 1009, 1560, 632, 985, 592,
+	1308, 592, 882, 145, 145, 397, 837, 383, 592, 592, 832, 36, 2714,
+	2107, 1588, 1347, 36, 36, 1443, 1453, 334, 2230, 1588, 1169, 650,
+	1169, 2107, 425, 425, 891, 891, 425, 2532, 679, 274, 274, 274, 325,
+	274, 1297, 194, 1297, 627, 314, 917, 314, 314, 1501, 414, 1490, 1036,
+	592, 1036, 1025, 901, 1218, 1025, 901, 280, 592, 592, 901, 1461, 159,
+	159, 159, 2076, 1066, 1176, 1176, 516, 327, 516, 1179, 1176, 899,
+	1176, 1176, 323, 1187, 1229, 663, 1229, 504, 1229, 916, 1229, 916,
+	1661, 41, 36, 278, 1027, 648, 648, 648, 1626, 648, 646, 1179, 1580,
+	1061, 1514, 1008, 1741, 2076, 1514, 1008, 952, 1089, 427, 952, 427,
+	1083, 425, 427, 1089, 1083, 425, 427, 425, 230, 920, 1678, 920, 1678,
+	189, 189, 953, 189, 133, 189, 1075, 189, 189, 133, 1264, 725, 189,
+	1629, 189, 808, 230, 230, 2179, 770, 230, 770, 230, 21, 21, 784,
+	1118, 230, 230, 230, 770, 1118, 986, 808, 916, 30, 327, 918, 679,
+	414, 916, 1165, 1355, 916, 755, 733, 433, 1490, 433, 433, 433, 605,
+	433, 433, 433, 1446, 679, 206, 433, 21, 2452, 206, 206, 433, 1894,
+	206, 822, 206, 2073, 206, 206, 21, 822, 21, 206, 206, 21, 383, 1513,
+	375, 1347, 432, 1589, 172, 954, 242, 1256, 1256, 1248, 1256, 1256,
+	1248, 1248, 1256, 842, 13, 592, 13, 842, 1291, 592, 21, 175, 13, 592,
+	13, 13, 1426, 13, 1541, 445, 808, 808, 863, 647, 219, 1592, 1029,
+	1225, 917, 1963, 1129, 555, 1313, 550, 660, 550, 220, 660, 552, 663,
+	220, 533, 220, 383, 550, 1278, 1495, 636, 842, 1036, 425, 842, 425,
+	1537, 1278, 842, 554, 1508, 636, 554, 301, 842, 792, 1392, 1021, 284,
+	1172, 997, 1021, 103, 1316, 308, 1210, 848, 848, 1089, 1089, 848,
+	848, 67, 1029, 827, 1029, 2078, 827, 1312, 1029, 827, 590, 872, 1312,
+	427, 67, 67, 67, 67, 872, 827, 872, 2126, 1436, 26, 2126, 67, 1072,
+	2126, 1610, 872, 1620, 883, 883, 1397, 1189, 555, 555, 563, 1189,
+	555, 640, 555, 640, 1089, 1089, 610, 610, 1585, 610, 1355, 610, 1015,
+	616, 925, 1015, 482, 230, 707, 231, 888, 1355, 589, 1379, 151, 931,
+	1486, 1486, 393, 235, 960, 590, 235, 960, 422, 142, 285, 285, 327,
+	327, 442, 2009, 822, 445, 822, 567, 888, 2611, 1537, 323, 55, 1537,
+	323, 888, 2611, 323, 1537, 323, 58, 445, 593, 2045, 593, 58, 47, 770,
+	842, 47, 47, 842, 842, 648, 2557, 173, 689, 2291, 1446, 2085, 2557,
+	2557, 2291, 1780, 1535, 2291, 2391, 808, 691, 1295, 1165, 983, 948,
+	2000, 948, 983, 983, 2225, 2000, 983, 983, 705, 948, 2000, 1795,
+	1592, 478, 592, 1795, 1795, 663, 478, 1790, 478, 592, 1592, 173, 901,
+	312, 4, 1606, 173, 838, 754, 754, 128, 550, 1166, 551, 1480, 550,
+	550, 1875, 1957, 1166, 902, 1875, 550, 550, 551, 2632, 551, 1875,
+	1875, 551, 2891, 2159, 2632, 3231, 551, 815, 150, 1654, 1059, 1059,
+	734, 770, 555, 1592, 555, 2059, 770, 770, 1803, 627, 627, 627, 2059,
+	931, 1272, 427, 1606, 1272, 1606, 1187, 1204, 397, 822, 21, 1645,
+	263, 263, 822, 263, 1645, 280, 263, 605, 1645, 2014, 21, 21, 1029,
+	263, 1916, 2291, 397, 397, 496, 270, 270, 1319, 264, 1638, 264, 986,
+	1278, 1397, 1278, 1191, 409, 1191, 740, 1191, 754, 754, 387, 63, 948,
+	666, 666, 1198, 548, 63, 1248, 285, 1248, 169, 1248, 1248, 285, 918,
+	224, 285, 1426, 1671, 514, 514, 717, 514, 51, 1521, 1745, 51, 605,
+	1191, 51, 128, 1191, 51, 51, 1521, 267, 513, 952, 966, 1671, 897, 51,
+	71, 592, 986, 986, 1121, 592, 280, 2000, 2000, 1165, 1165, 1165,
+	1818, 222, 1818, 1165, 1252, 506, 327, 443, 432, 1291, 1291, 2755,
+	1413, 520, 1318, 227, 1047, 828, 520, 347, 1364, 136, 136, 452, 457,
+	457, 132, 457, 488, 1087, 1013, 2225, 32, 1571, 2009, 483, 67, 483,
+	740, 740, 1013, 2854, 866, 32, 2861, 866, 887, 32, 2444, 740, 32, 32,
+	866, 2225, 866, 32, 1571, 2627, 32, 850, 1675, 569, 1158, 32, 1158,
+	1797, 2641, 1565, 1158, 569, 1797, 1158, 1797, 55, 1703, 42, 55,
+	2562, 675, 1703, 42, 55, 749, 488, 488, 347, 1206, 1286, 1286, 488,
+	488, 1206, 1286, 1206, 1286, 550, 550, 1790, 860, 550, 2452, 550,
+	550, 2765, 1089, 1633, 797, 2244, 1313, 194, 2129, 194, 194, 194,
+	818, 32, 194, 450, 1313, 2387, 194, 1227, 2387, 308, 2232, 526, 476,
+	278, 830, 830, 194, 830, 194, 278, 194, 714, 476, 830, 714, 830, 278,
+	830, 2532, 1218, 1759, 1446, 960, 1747, 187, 1446, 1759, 960, 105,
+	1446, 1446, 1271, 1446, 960, 960, 1218, 1446, 1446, 105, 1446, 960,
+	488, 1446, 427, 534, 842, 1969, 2460, 1969, 842, 842, 1969, 427, 941,
+	2160, 427, 230, 938, 2075, 1675, 1675, 895, 1675, 34, 129, 1811, 239,
+	749, 1957, 2271, 749, 1908, 129, 239, 239, 129, 129, 2271, 2426,
+	1355, 1756, 194, 1583, 194, 194, 1583, 194, 1355, 194, 1628, 2221,
+	1269, 2425, 1756, 1355, 1355, 1583, 1033, 427, 582, 30, 582, 582,
+	935, 1444, 1962, 915, 733, 915, 938, 1962, 767, 353, 1630, 1962,
+	1962, 563, 733, 563, 733, 353, 822, 1630, 740, 2076, 2076, 2076, 589,
+	589, 2636, 866, 589, 947, 1528, 125, 273, 1058, 1058, 1161, 1635,
+	1355, 1161, 1161, 1355, 1355, 650, 1206, 1206, 784, 784, 784, 784,
+	784, 412, 461, 412, 2240, 412, 679, 891, 461, 679, 679, 189, 189,
+	1933, 1651, 2515, 189, 1386, 538, 1386, 1386, 1187, 1386, 2423, 2601,
+	2285, 175, 175, 2331, 194, 3079, 384, 538, 2365, 2294, 538, 2166,
+	1841, 3326, 1256, 3923, 976, 85, 550, 550, 1295, 863, 863, 550, 1249,
+	550, 1759, 146, 1069, 920, 2633, 885, 885, 1514, 1489, 166, 1514,
+	2041, 885, 2456, 885, 2041, 1081, 1948, 362, 550, 94, 324, 2308, 94,
+	2386, 94, 550, 874, 1329, 1759, 2280, 1487, 493, 493, 2099, 2599,
+	1431, 1086, 1514, 1086, 2099, 1858, 368, 1330, 2599, 1858, 2846,
+	2846, 2907, 2846, 713, 713, 1854, 1123, 713, 713, 3010, 1123, 3010,
+	538, 713, 1123, 447, 822, 555, 2011, 493, 508, 2292, 555, 1736, 2135,
+	2704, 555, 2814, 555, 2000, 555, 555, 822, 914, 327, 679, 327, 648,
+	537, 2263, 931, 1496, 537, 1296, 1745, 1592, 1658, 1795, 650, 1592,
+	1745, 1745, 1658, 1592, 1745, 1592, 1745, 1658, 1338, 2124, 1592,
+	1745, 1745, 1745, 837, 1726, 2897, 1118, 1118, 230, 1118, 1118, 1118,
+	1388, 1748, 514, 128, 1165, 931, 514, 2974, 2041, 2387, 2041, 979,
+	185, 36, 1269, 550, 173, 812, 36, 1165, 2676, 2562, 1473, 2885, 1982,
+	1578, 1578, 383, 383, 2360, 383, 1578, 2360, 1584, 1982, 1578, 1578,
+	1578, 2019, 1036, 355, 724, 2023, 205, 303, 355, 1036, 1966, 355,
+	1036, 401, 401, 401, 830, 401, 849, 578, 401, 849, 849, 578, 1776,
+	1123, 552, 2632, 808, 1446, 1120, 373, 1529, 1483, 1057, 893, 1284,
+	1430, 1529, 1529, 2632, 1352, 2063, 1606, 1352, 1606, 2291, 3079,
+	2291, 1529, 506, 838, 1606, 1606, 1352, 1529, 1529, 1483, 1529, 1606,
+	1529, 259, 902, 259, 902, 612, 612, 284, 398, 2991, 1534, 1118, 1118,
+	1118, 1118, 1118, 734, 284, 2224, 398, 734, 284, 734, 398, 3031, 398,
+	734, 1707, 2643, 1344, 1477, 475, 1818, 194, 1894, 691, 1528, 1184,
+	1207, 1501, 6, 2069, 871, 2069, 3548, 1443, 2069, 2685, 3265, 1350,
+	3265, 2069, 2069, 128, 1313, 128, 663, 414, 1313, 414, 2000, 128,
+	2000, 663, 1313, 699, 1797, 550, 327, 550, 1526, 699, 327, 1797,
+	1526, 550, 550, 327, 550, 1426, 1426, 1426, 2285, 1123, 890, 728,
+	1707, 728, 728, 327, 253, 1187, 1281, 1364, 1571, 2170, 755, 3232,
+	925, 1496, 2170, 2170, 1125, 443, 902, 902, 925, 755, 2078, 2457,
+	902, 2059, 2170, 1643, 1129, 902, 902, 1643, 1129, 606, 36, 103, 338,
+	338, 1089, 338, 338, 338, 1089, 338, 36, 340, 1206, 1176, 2041, 833,
+	1854, 1916, 1916, 1501, 2132, 1736, 3065, 367, 1934, 833, 833, 833,
+	2041, 3017, 2147, 818, 1397, 828, 2147, 398, 828, 818, 1158, 818,
+	689, 327, 36, 1745, 2132, 582, 1475, 189, 582, 2132, 1191, 582, 2132,
+	1176, 1176, 516, 2610, 2230, 2230, 64, 1501, 537, 1501, 173, 2230,
+	2988, 1501, 2694, 2694, 537, 537, 173, 173, 1501, 537, 64, 173, 173,
+	64, 2230, 537, 2230, 537, 2230, 2230, 2069, 3142, 1645, 689, 1165,
+	1165, 1963, 514, 488, 1963, 1145, 235, 1145, 1078, 1145, 231, 2405,
+	552, 21, 57, 57, 57, 1297, 1455, 1988, 2310, 1885, 2854, 2014, 734,
+	1705, 734, 2854, 734, 677, 1988, 1660, 734, 677, 734, 677, 677, 734,
+	2854, 1355, 677, 1397, 2947, 2386, 1698, 128, 1698, 3028, 2386, 2437,
+	2947, 2386, 2643, 2386, 2804, 1188, 335, 746, 1187, 1187, 861, 2519,
+	1917, 2842, 1917, 675, 1308, 234, 1917, 314, 314, 2339, 2339, 2592,
+	2576, 902, 916, 2339, 916, 2339, 916, 2339, 916, 1089, 1089, 2644,
+	1221, 1221, 2446, 308, 308, 2225, 2225, 3192, 2225, 555, 1592, 1592,
+	555, 893, 555, 550, 770, 3622, 2291, 2291, 3419, 465, 250, 2842,
+	2291, 2291, 2291, 935, 160, 1271, 308, 325, 935, 1799, 1799, 1891,
+	2227, 1799, 1598, 112, 1415, 1840, 2014, 1822, 2014, 677, 1822, 1415,
+	1415, 1822, 2014, 2386, 2159, 1822, 1415, 1822, 179, 1976, 1033, 179,
+	1840, 2014, 1415, 1970, 1970, 1501, 563, 563, 563, 462, 563, 1970,
+	1158, 563, 563, 1541, 1238, 383, 235, 1158, 383, 1278, 383, 1898,
+	2938, 21, 2938, 1313, 2201, 2059, 423, 2059, 1313, 872, 1313, 2044,
+	89, 173, 3327, 1660, 2044, 1623, 173, 1114, 1114, 1592, 1868, 1651,
+	1811, 383, 3469, 1811, 1651, 869, 383, 383, 1651, 1651, 3223, 2166,
+	3469, 767, 383, 1811, 767, 2323, 3355, 1457, 3341, 2640, 2976, 2323,
+	3341, 2323, 2640, 103, 103, 1161, 1080, 2429, 370, 2018, 2854, 2429,
+	2166, 2429, 2094, 2207, 871, 1963, 1963, 2023, 2023, 2336, 663, 2893,
+	1580, 691, 663, 705, 2046, 2599, 409, 2295, 1118, 2494, 1118, 1950,
+	549, 2494, 2453, 2046, 2494, 2453, 2046, 2453, 2046, 409, 1118, 4952,
+	2291, 2225, 1894, 1423, 2498, 567, 4129, 1475, 1501, 795, 463, 2084,
+	828, 828, 232, 828, 232, 232, 1818, 1818, 666, 463, 232, 220, 220,
+	2162, 2162, 833, 4336, 913, 35, 913, 21, 2927, 886, 3037, 383, 886,
+	876, 1747, 383, 916, 916, 916, 2927, 916, 1747, 837, 1894, 717, 423,
+	481, 1894, 1059, 2262, 3206, 4700, 1059, 3304, 2262, 871, 1831, 871,
+	3304, 1059, 1158, 1934, 1158, 756, 1511, 41, 978, 1934, 2603, 720,
+	41, 756, 41, 325, 2611, 1158, 173, 1123, 1934, 1934, 1511, 2045,
+	2045, 2045, 1423, 3206, 3691, 2512, 3206, 2512, 2000, 1811, 2504,
+	2504, 2611, 2437, 2437, 2437, 1455, 893, 150, 2665, 1966, 605, 398,
+	2331, 1177, 516, 1962, 4241, 94, 1252, 760, 1292, 1962, 1373, 2000,
+	1990, 3684, 42, 1868, 3779, 1811, 1811, 2041, 3010, 5436, 1780, 2041,
+	1868, 1811, 1780, 1811, 1868, 1811, 2041, 1868, 1811, 5627, 4274,
+	1811, 1868, 4602, 1811, 1811, 1474, 2665, 235, 1474, 2665}
diff --git a/luby.go b/luby.go
new file mode 100644
index 0000000..cefdc23
--- /dev/null
+++ b/luby.go
@@ -0,0 +1,231 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*
+Package fountain includes implementations of several fountain codes.
+
+Fountain codes have the property that a very large (more or less unlimited)
+number of code blocks can be generated from a fixed number of source
+blocks. The original message can be recovered from any subset of sufficient
+size of these code blocks, so even if some code blocks are lost, the message
+can still be reconstructed once a sufficient number have been received.
+So in a transmission system, the receiver need not notify the
+transmitter about every code block, it only need notify the transmitter
+when the source message has been fully reconstructed.
+
+The overall approach used by this package is that there are various codec
+implementations which follow the same overall algorithm -- splitting a source
+message into source blocks, manipulating those source blocks to produce a
+set of precode blocks, then for each code block to be produced, picking
+constituent precode blocks to use to create the code block, and then using
+an LT (Luby Transform) process to produce the code blocks.
+*/
+package fountain
+
+import (
+	"math/rand"
+)
+
+// Codec is an interface for fountain codes which follow the general
+// scheme of preparing intermediate encoding representations based on the input
+// message and picking LT composition indices given an integer code block number.
+type Codec interface {
+	// SourceBlocks returns the number of source blocks to be used in the
+	// codec. Note that this may not be the same number as the number of intermediate
+	// code blocks. It is the minimum number of encoded blocks necessary to
+	// reconstruct the original message.
+	SourceBlocks() int
+
+	// GenerateIntermediateBlocks prepares a set of precode blocks given the input
+	// message. The precode blocks may just be identically the blocks in the
+	// original message, or it may be a transformation on those source blocks
+	// derived from a codec-specific relationship.
+	// For example, in Online codes, this consists of adding auxiliary blocks.
+	// In a Raptor code, the entire set of source blocks is transformed into a
+	// different set of precode blocks.
+	GenerateIntermediateBlocks(message []byte, numBlocks int) []block
+
+	// PickIndices asks the codec to select the (non-strict subset of the) precode
+	// blocks to be used in the LT composition of a particular code block. These
+	// blocks will then be XORed to produce the code block.
+	PickIndices(codeBlockIndex int64) []int
+
+	// NewDecoder creates a decoder suitable for use with blocks encoded using this
+	// codec for a known message size (in bytes). The decoder will be initialized
+	// and ready to receive incoming blocks for decoding.
+	NewDecoder(messageLength int) Decoder
+}
+
+// LTBlock is an encoded block structure representing a block created using
+// the LT transform.
+type LTBlock struct {
+	// BlockCode is the ID used to construct the encoded block.
+	// The way in which the ID is mapped to the choice of blocks will vary by
+	// codec.
+	BlockCode int64
+
+	// Data is the contents of the encoded block.
+	Data []byte
+}
+
+// Decoder is an interface allowing decoding of fountain-code-encoded messages
+// as the blocks are received.
+type Decoder interface {
+	// AddBlocks adds a set of encoded blocks to the decoder. Returns true if the
+	// message can be fully decoded. False if there is insufficient information.
+	AddBlocks(blocks []LTBlock) bool
+
+	// Decode extracts the decoded message from the decoder. If the decoder does
+	// not have sufficient information to produce an output, returns a nil slice.
+	Decode() []byte
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation of Luby Transform codes.
+// The Luby Transform (LT) converts a source text split into a number of source
+// blocks into an unbounded set of code blocks, each of which is formed of an
+// XOR operation of some subset of the source blocks.
+// See "LT Codes" -- M. Luby (2002)
+
+// lubyCodec contains the codec information for the Luby Transform encoder
+// and decoder.
+// Implements fountain.Codec.
+type lubyCodec struct {
+	// sourceBlocks is the number of source blocks (N) the source message is split into.
+	sourceBlocks int
+
+	// random is a source of randomness for sampling the degree distribution
+	// and the source blocks when composing a code block.
+	random *rand.Rand
+
+	// degreeCDF is the degree distribution function from which encoding block
+	// compositions are chosen.
+	degreeCDF []float64
+}
+
+// NewLubyCodec creates a new Codec using the provided number of source blocks,
+// PRNG, and degree distribution function.
+// The intermediate blocks will be a roughly-equal-sized partition of the source
+// message padded so that all blocks have equal size. The indices will be picked
+// using the provided PRNG seeded with the BlockCode ID of the LTBlock
+// to be created, according to the degree CDF provided.
+func NewLubyCodec(sourceBlocks int, random *rand.Rand, degreeCDF []float64) Codec {
+	return &lubyCodec{
+		sourceBlocks: sourceBlocks,
+		random:       random,
+		degreeCDF:    degreeCDF}
+}
+
+// SourceBlocks retrieves the number of source blocks the codec is configured to use.
+func (c *lubyCodec) SourceBlocks() int {
+	return c.sourceBlocks
+}
+
+// PickIndices uses the provided PRNG to select a random number of source
+// blocks with degree d, given by a random selection in the degreeCDF parameter.
+// The degree distribution is how likely the encoder is to pick code blocks composed
+// of d source blocks.
+func (c *lubyCodec) PickIndices(codeBlockIndex int64) []int {
+	c.random.Seed(codeBlockIndex)
+	d := pickDegree(c.random, c.degreeCDF)
+	return sampleUniform(c.random, d, c.sourceBlocks)
+}
+
+// GenerateIntermediateEncoding for the LubyCodec simply splits the source message
+// into numBlocks blocks of roughly equal size, padding shorter ones so that all
+// blocks are the same length.
+func (c *lubyCodec) GenerateIntermediateBlocks(message []byte, numBlocks int) []block {
+	long, short := partitionBytes(message, c.sourceBlocks)
+	return equalizeBlockLengths(long, short)
+}
+
+// generateLubyTransformBlock generates a single code block from the set of
+// source blocks, given the composition indices, by XORing the source blocks
+// together.
+func generateLubyTransformBlock(source []block, indices []int) block {
+	var symbol block
+
+	for _, i := range indices {
+		if i < len(source) {
+			symbol.xor(source[i])
+		}
+	}
+
+	return symbol
+}
+
+// EncodeLTBlocks encodes a sequence of LT-encoded code blocks from the given message
+// and the block IDs. Suitable for use with any fountain.Codec.
+// Note: This method is destructive to the message array.
+func EncodeLTBlocks(message []byte, encodedBlockIDs []int64, c Codec) []LTBlock {
+	source := c.GenerateIntermediateBlocks(message, c.SourceBlocks())
+
+	ltBlocks := make([]LTBlock, len(encodedBlockIDs))
+	for i := range encodedBlockIDs {
+		indices := c.PickIndices(encodedBlockIDs[i])
+		ltBlocks[i].BlockCode = encodedBlockIDs[i]
+		b := generateLubyTransformBlock(source, indices)
+		ltBlocks[i].Data = make([]byte, b.length())
+		copy(ltBlocks[i].Data, b.data)
+	}
+	return ltBlocks
+}
+
+// NewDecoder creates a luby transform decoder
+func (c *lubyCodec) NewDecoder(messageLength int) Decoder {
+	return newLubyDecoder(c, messageLength)
+}
+
+// lubyDecoder is the state required to decode a Luby Transform message.
+type lubyDecoder struct {
+	codec         *lubyCodec
+	messageLength int
+
+	// The sparse equation matrix used for decoding.
+	matrix sparseMatrix
+}
+
+// newLubyDecoder creates a new decoder for a particular Luby Transform message.
+// The codec parameters used to create the original encoding blocks must be provided.
+// The decoder is only valid for decoding code blocks for a particular message.
+func newLubyDecoder(c *lubyCodec, length int) *lubyDecoder {
+	d := &lubyDecoder{codec: c, messageLength: length}
+	d.matrix.coeff = make([][]int, c.SourceBlocks())
+	d.matrix.v = make([]block, c.SourceBlocks())
+
+	return d
+}
+
+// AddBlocks adds a set of encoded blocks to the decoder. Returns true if the
+// message can be fully decoded. False if there is insufficient information.
+func (d *lubyDecoder) AddBlocks(blocks []LTBlock) bool {
+	for i := range blocks {
+		indices := d.codec.PickIndices(blocks[i].BlockCode)
+		d.matrix.addEquation(indices, block{data: blocks[i].Data})
+	}
+	return d.matrix.determined()
+}
+
+// Decode extracts the decoded message from the decoder. If the decoder does
+// not have sufficient information to produce an output, returns a nil slice.
+func (d *lubyDecoder) Decode() []byte {
+	if !d.matrix.determined() {
+		return nil
+	}
+
+	d.matrix.reduce()
+
+	lenLong, lenShort, numLong, numShort := partition(d.messageLength, d.codec.SourceBlocks())
+	return d.matrix.reconstruct(d.messageLength, lenLong, lenShort, numLong, numShort)
+}
diff --git a/luby_test.go b/luby_test.go
new file mode 100644
index 0000000..44bcba1
--- /dev/null
+++ b/luby_test.go
@@ -0,0 +1,95 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math/rand"
+	"reflect"
+	"testing"
+)
+
+func TestLubyTransformBlockGenerator(t *testing.T) {
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+	codec := NewLubyCodec(4, rand.New(NewMersenneTwister(200)), solitonDistribution(4))
+
+	wantIndices := [][]int{
+		{0},
+		{1},
+		{3},
+		{0, 1},
+		{1, 2, 3},
+	}
+
+	// These magic seeds are chosen such that they will generate the block compositions
+	// in wantIndices given the PRNG with which we initialized the codec above (the
+	// Mersenne Twister with seed=200).
+	encodeBlocks := []int64{7, 34, 5, 31, 25}
+	for i := range wantIndices {
+		indices := codec.PickIndices(encodeBlocks[i])
+		if !reflect.DeepEqual(indices, wantIndices[i]) {
+			t.Logf("Got %v indices for %d, want %v", indices, encodeBlocks[i], wantIndices[i])
+		}
+	}
+
+	source := codec.GenerateIntermediateBlocks(message, codec.SourceBlocks())
+	lubyBlocks := make([]LTBlock, len(encodeBlocks))
+	for i := range encodeBlocks {
+		b := generateLubyTransformBlock(source, wantIndices[i])
+		lubyBlocks[i].BlockCode = encodeBlocks[i]
+		lubyBlocks[i].Data = make([]byte, b.length())
+		copy(lubyBlocks[i].Data, b.data)
+	}
+
+	if len(source) != codec.SourceBlocks() {
+		t.Logf("Got %d encoded blocks, want %d", len(source), codec.SourceBlocks())
+	}
+
+	if string(lubyBlocks[0].Data) != "abcdefg" {
+		t.Errorf("Data for {0} block is %v, should be 'abcdefg'", string(lubyBlocks[0].Data))
+	}
+	if string(lubyBlocks[1].Data) != "hijklmn" {
+		t.Errorf("Data for {1} block is %v, should be 'hijklmn'", string(lubyBlocks[1].Data))
+	}
+	if string(lubyBlocks[2].Data) != "uvwxyz" {
+		t.Errorf("Data for {1} block is %v, should be 'uvwxyz'", string(lubyBlocks[2].Data))
+	}
+	if lubyBlocks[3].Data[0] != 'a'^'h' {
+		t.Errorf("Data[0] for {0, 1} block is %d, should be 'a'^'h' (%d)",
+			lubyBlocks[3].Data[0], 'a'^'h')
+	}
+	if lubyBlocks[4].Data[0] != 'h'^'o'^'u' {
+		t.Errorf("Data[0] for {1,2,3} block is %d, should be 'h'^'o'^'u' (%d)",
+			lubyBlocks[3].Data[0], 'h'^'o'^'u')
+	}
+}
+
+func TestLubyDecoder(t *testing.T) {
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+	codec := NewLubyCodec(4, rand.New(NewMersenneTwister(200)), solitonDistribution(4))
+
+	encodeBlocks := []int64{7, 34, 5, 31, 25}
+	lubyBlocks := EncodeLTBlocks(message, encodeBlocks, codec)
+
+	decoder := codec.NewDecoder(len(message))
+	determined := decoder.AddBlocks(lubyBlocks)
+	if !determined {
+		t.Errorf("After adding code blocks, decoder is still undetermined.")
+	}
+	decoded := decoder.Decode()
+	if !reflect.DeepEqual(decoded, message) {
+		t.Errorf("Decoded luby transform message is %v, expected %v", decoded, message)
+		t.Logf("String value = %v", string(decoded))
+	}
+}
diff --git a/mersenne.go b/mersenne.go
new file mode 100644
index 0000000..24da7fc
--- /dev/null
+++ b/mersenne.go
@@ -0,0 +1,200 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math"
+	"math/rand"
+)
+
+// MersenneTwister is an implementation of the MT19937 PRNG of Matsumoto and Nishimura.
+// Following http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/mt.pdf
+// Uses the 32-bit version of the algorithm.
+// Satisfies math/rand.Source
+type MersenneTwister struct {
+	mt          [624]uint32
+	index       int
+	initialized bool
+}
+
+// NewMersenneTwister creates a new MT19937 PRNG with the given seed. The seed
+// is converted to a 32-bit seed by XORing the high and low halves.
+func NewMersenneTwister(seed int64) rand.Source {
+	t := &MersenneTwister{}
+	t.Seed(seed)
+
+	return t
+}
+
+func (t *MersenneTwister) Seed(seed int64) {
+	t.initialize(uint32(((seed >> 32) ^ seed) & math.MaxUint32))
+}
+
+// Int63 produces a new int64 value between 0 and 2^63-1 by combining the bits
+// of two Uint32 values.
+func (t *MersenneTwister) Int63() int64 {
+	a := t.Uint32()
+	b := t.Uint32()
+	return (int64(a) << 31) ^ int64(b)
+}
+
+func (t *MersenneTwister) Uint32() uint32 {
+	if !t.initialized {
+		t.initialize(4357) // value from original paper; lets the twister do something reasonable when empty
+	}
+
+	// Every 624 calls, revolve the untempered seed matrix
+	if t.index == 0 {
+		t.generateUntempered()
+	}
+
+	y := t.mt[t.index]
+	t.index++
+	if t.index >= len(t.mt) {
+		t.index = 0
+	}
+	y ^= y >> 11
+	y ^= (y << 7) & 0x9d2c5680
+	y ^= (y << 15) & 0xefc60000
+	y ^= y >> 18
+
+	return y
+}
+
+func (t *MersenneTwister) initialize(seed uint32) {
+	t.index = 0
+	t.mt[0] = seed
+
+	for i := 1; i < len(t.mt); i++ {
+		// Improved initialization: not subject to the same runs of correlated numbers.
+		t.mt[i] = (1812433253*(t.mt[i-1]^(t.mt[i-1]>>30)) + uint32(i)) & math.MaxUint32
+		// Original paper did this
+		// t.mt[i] = uint32(69069 * int64(t.mt[i-1]) & math.MaxUint32)
+	}
+	t.initialized = true
+}
+
+func (t *MersenneTwister) generateUntempered() {
+	mag01 := [2]uint32{0x0, 0x9908b0df}
+	for i := 0; i < len(t.mt); i++ {
+		y := (t.mt[i] & 0x80000000) | (t.mt[(i+1)%len(t.mt)] & 0x7fffffff)
+		t.mt[i] = (t.mt[(i+397)%len(t.mt)] ^ (y >> 1)) ^ mag01[y&0x01]
+	}
+}
+
+// MersenneTwister64 is a 64-bit MT19937 PRNG after Nishimura.
+// See http://dl.acm.org/citation.cfm?id=369540&dl=ACM&coll=DL&CFID=261426526&CFTOKEN=25107569
+// Satisfies math/rand.Source
+type MersenneTwister64 struct {
+	mt          [312]uint64
+	index       int
+	initialized bool
+}
+
+// NewMersenneTwister64 creates a new 64-bit version of the MT19937 PRNG.
+func NewMersenneTwister64(seed int64) rand.Source {
+	t := &MersenneTwister64{}
+	t.Seed(seed)
+
+	return t
+}
+
+// Seed initializes the state of the PRNG with the given seed value.
+func (t *MersenneTwister64) Seed(seed int64) {
+	t.initialize(uint64(seed))
+}
+
+// Int63 returns the next value from the PRNG. This value is the low 63 bits
+// of the Uint64 value.
+func (t *MersenneTwister64) Int63() int64 {
+	return int64(t.Uint64() & math.MaxInt64)
+}
+
+func (t *MersenneTwister64) initialize(seed uint64) {
+	t.index = 0
+	t.mt[0] = seed
+
+	for i := 1; i < len(t.mt); i++ {
+		t.mt[i] = 6364136223846793005*(t.mt[i-1]^(t.mt[i-1]>>62)) + uint64(i)
+	}
+	t.initialized = true
+}
+
+// SeedSlice allows the twister to be initialized with a slice of seed values.
+func (t *MersenneTwister64) SeedSlice(seed []uint64) {
+	t.initialize(19650218)
+
+	length := len(seed)
+	if len(t.mt) > length {
+		length = len(t.mt)
+	}
+
+	i, j := 1, 0
+	for k := 0; k < length; k++ {
+		t.mt[i] = (t.mt[i] ^ ((t.mt[i-1] ^ (t.mt[i-1] >> 62)) * 3935559000370003845)) + seed[j] + uint64(j)
+		i++
+		j++
+		if i >= len(t.mt) {
+			t.mt[0] = t.mt[len(t.mt)-1]
+			i = 1
+		}
+		if j >= len(seed) {
+			j = 0
+		}
+	}
+
+	for k := 0; k < len(t.mt)-1; k++ {
+		t.mt[i] = (t.mt[i] ^ ((t.mt[i-1] ^ (t.mt[i-1] >> 62)) * 2862933555777941757)) - uint64(i)
+		i++
+		if i >= len(t.mt) {
+			t.mt[0] = t.mt[len(t.mt)-1]
+			i = 1
+		}
+	}
+
+	t.mt[0] = 1 << 63
+}
+
+// Uint64 returns the next pseudo-random value from the twister.
+func (t *MersenneTwister64) Uint64() uint64 {
+	if !t.initialized {
+		t.initialize(5489)
+	}
+
+	// Every 312 calls, revolve the untempered seed matrix
+	if t.index == 0 {
+		t.generateUntempered()
+	}
+
+	y := t.mt[t.index]
+	t.index++
+	if t.index >= len(t.mt) {
+		t.index = 0
+	}
+	y ^= (y >> 29) & 0x5555555555555555
+	y ^= (y << 17) & 0x71d67fffeda60000
+	y ^= (y << 37) & 0xfff7eee000000000
+	y ^= y >> 43
+
+	return y
+}
+
+func (t *MersenneTwister64) generateUntempered() {
+	mag01 := [2]uint64{0x0, 0xb5026f5aa96619e9}
+	for i := 0; i < len(t.mt); i++ {
+		y := (t.mt[i] & 0xffffffff80000000) | (t.mt[(i+1)%len(t.mt)] & 0x7fffffff)
+		t.mt[i] = (t.mt[(i+156)%len(t.mt)] ^ (y >> 1)) ^ mag01[y&0x01]
+	}
+}
diff --git a/mersenne_test.go b/mersenne_test.go
new file mode 100644
index 0000000..7ba15f9
--- /dev/null
+++ b/mersenne_test.go
@@ -0,0 +1,596 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math"
+	"math/big"
+	"math/rand"
+	"reflect"
+	"testing"
+)
+
+func NewTwister(seed int64) *MersenneTwister {
+	t := &MersenneTwister{}
+	t.Seed(seed)
+
+	return t
+}
+
+func TestMersenne(t *testing.T) {
+	var mt MersenneTwister
+
+	mt.Uint32()
+	if !mt.initialized {
+		t.Error("MersenneTwister should be initialized after Uint32 call.")
+	}
+
+	var mt2 MersenneTwister
+	mt2.Int63()
+	if !mt2.initialized {
+		t.Error("MersenneTwister should be initialized after Int63 call.")
+	}
+
+	var mt3 MersenneTwister
+	mt3.Seed(1)
+	if !mt3.initialized {
+		t.Error("MersenneTwister should be initialized after Seed call.")
+	}
+
+}
+
+func TestMersenneGen(t *testing.T) {
+	mt := NewTwister(1)
+
+	// See implementation at github.com/cslarsen/mersenne-twister
+	seed1Expected := []uint32{
+		1791095845, 4282876139, 3093770124, 4005303368, 491263,
+		550290313, 1298508491, 4290846341, 630311759, 1013994432,
+		396591248, 1703301249, 799981516, 1666063943, 1484172013,
+		2876537340, 1704103302, 4018109721, 2314200242, 3634877716,
+		1800426750, 1345499493, 2942995346, 2252917204, 878115723,
+		1904615676, 3771485674, 986026652, 117628829, 2295290254,
+		2879636018, 3925436996, 1792310487, 1963679703, 2399554537,
+		1849836273, 602957303, 4033523166, 850839392, 3343156310,
+		3439171725, 3075069929, 4158651785, 3447817223, 1346146623,
+		398576445, 2973502998, 2225448249, 3764062721, 3715233664,
+		3842306364, 3561158865, 365262088, 3563119320, 167739021,
+		1172740723, 729416111, 254447594, 3771593337, 2879896008,
+		422396446, 2547196999, 1808643459, 2884732358, 4114104213,
+		1768615473, 2289927481, 848474627, 2971589572, 1243949848,
+		1355129329, 610401323, 2948499020, 3364310042, 3584689972,
+		1771840848, 78547565, 146764659, 3221845289, 2680188370,
+		4247126031, 2837408832, 3213347012, 1282027545, 1204497775,
+		1916133090, 3389928919, 954017671, 443352346, 315096729,
+		1923688040, 2015364118, 3902387977, 413056707, 1261063143,
+		3879945342, 1235985687, 513207677, 558468452, 2253996187,
+		83180453, 359158073, 2915576403, 3937889446, 908935816,
+		3910346016, 1140514210, 1283895050, 2111290647, 2509932175,
+		229190383, 2430573655, 2465816345, 2636844999, 630194419,
+		4108289372, 2531048010, 1120896190, 3005439278, 992203680,
+		439523032, 2291143831, 1778356919, 4079953217, 2982425969,
+		2117674829, 1778886403, 2321861504, 214548472, 3287733501,
+		2301657549, 194758406, 2850976308, 601149909, 2211431878,
+		3403347458, 4057003596, 127995867, 2519234709, 3792995019,
+		3880081671, 2322667597, 590449352, 1924060235, 598187340,
+		3831694379, 3467719188, 1621712414, 1708008996, 2312516455,
+		710190855, 2801602349, 3983619012, 1551604281, 1493642992,
+		2452463100, 3224713426, 2739486816, 3118137613, 542518282,
+		3793770775, 2964406140, 2678651729, 2782062471, 3225273209,
+		1520156824, 1498506954, 3278061020, 1159331476, 1531292064,
+		3847801996, 3233201345, 1838637662, 3785334332, 4143956457,
+		50118808, 2849459538, 2139362163, 2670162785, 316934274,
+		492830188, 3379930844, 4078025319, 275167074, 1932357898,
+		1526046390, 2484164448, 4045158889, 1752934226, 1631242710,
+		1018023110, 3276716738, 3879985479, 3313975271, 2463934640,
+		1294333494, 12327951, 3318889349, 2650617233, 656828586}
+
+	got := make([]uint32, len(seed1Expected))
+
+	for i := range got {
+		got[i] = mt.Uint32()
+	}
+
+	if !reflect.DeepEqual(got, seed1Expected) {
+		t.Errorf("Got bad sequence for seed=1. Got %v, want %v", got, seed1Expected)
+	}
+
+	mt = NewTwister(1)
+
+	u := mt.Uint32()
+	if u != 1791095845 {
+		t.Errorf("Seed=1 value is %d, should be 1791095845", u)
+	}
+}
+
+func countBits(v uint32) int {
+	c := 0
+	for i := 0; i < 32; i++ {
+		if v&0x1 == 0x1 {
+			c++
+		}
+		v >>= 1
+	}
+	return c
+}
+
+func TestMersenneDifference(t *testing.T) {
+	// Checks that very similar seeds produce different outputs.
+	mt1 := NewTwister(123456)
+	mt2 := NewTwister(123457)
+	v1 := mt1.Uint32()
+	v2 := mt2.Uint32()
+	if math.Abs(float64(v1-v2)) < 0xF000 {
+		t.Errorf("PRNG values unseemly close for two close initial seeds. Got %d, %d", v1, v2)
+	}
+	if countBits(v1^v2) != 16 {
+		t.Errorf("Got common bits %d between random values from two close seeds. Want 16.", countBits(v1^v2))
+	}
+}
+
+// calculateBitStatistics performs about the simplest DieHard randomness test --
+// checks the bit count statistics of random numbers to make sure they match
+// a binomial distribution.
+// Returns the sum-squared-difference metric of the observed distribution and the
+// binomial distribution, normalized to the number of trials.
+func calculateBitStatistics(mt *MersenneTwister, trials int) float64 {
+	var bitsSet [33]int64
+	for i := 0; i < trials; i++ {
+		bitsSet[countBits(mt.Uint32())]++
+	}
+
+	// bitsSet should follow a binomial distribution pretty closely
+	var ssd int64
+	sum := 4294967296 // Sum of the binomial distribution from 0-32.
+	for i := 0; i < 33; i++ {
+		z := big.Int{}
+		binomial := int64(z.Binomial(32, int64(i)).Uint64()) * int64(trials) / int64(sum)
+		ssd += (bitsSet[i] - binomial) * (bitsSet[i] - binomial)
+	}
+	return math.Sqrt(float64(ssd)) / float64(trials)
+}
+
+func TestMersenneStatistics(t *testing.T) {
+	mt := NewTwister(892349823)
+	for i := 0; i < 25; i++ {
+		seed := mt.Uint32()
+		mt = NewTwister(int64(seed))
+		if calculateBitStatistics(mt, 10000) > 0.03 {
+			t.Errorf("Trial bits-set distribution not binomial within 3%% for seed %d", seed)
+		}
+	}
+}
+
+func TestMersenneSource(t *testing.T) {
+	r := rand.New(NewMersenneTwister(8189023))
+	count := 0
+	for i := 0; i < 100; i++ {
+		if r.Intn(2) == 1 {
+			count++
+		}
+	}
+	if math.Abs(float64(count)-50) > 3 {
+		t.Errorf("Expect coin toss about 50-50, was %d", count)
+	}
+}
+
+// Initial run on x86 hardware: 17.8ns/op
+func BenchmarkMersenne(b *testing.B) {
+	mt := NewTwister(903490345)
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		mt.Uint32()
+	}
+}
+
+func NewTwister64(seed uint64) *MersenneTwister64 {
+	t := &MersenneTwister64{}
+	t.initialize(seed)
+
+	return t
+}
+
+func TestMersenne64(t *testing.T) {
+	mt := &MersenneTwister64{}
+	seed := []uint64{0x12345, 0x23456, 0x34567, 0x45678}
+	mt.SeedSlice(seed)
+	if !mt.initialized {
+		t.Errorf("Twister should be initialized after SeedSlice")
+	}
+	r := mt.Uint64()
+	if r != 7266447313870364031 {
+		t.Errorf("MersenneTwister64(initialized with %v).Uint64() = %d, want %d",
+			seed, r, 7266447313870364031)
+	}
+}
+
+func TestMersenne64Gen(t *testing.T) {
+	// See http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt64.html
+	var expected64 = []uint64{
+		7266447313870364031, 4946485549665804864, 16945909448695747420,
+		16394063075524226720, 4873882236456199058, 14877448043947020171,
+		6740343660852211943, 13857871200353263164, 5249110015610582907,
+		10205081126064480383, 1235879089597390050, 17320312680810499042,
+		16489141110565194782, 8942268601720066061, 13520575722002588570,
+		14226945236717732373, 9383926873555417063, 15690281668532552105,
+		11510704754157191257, 15864264574919463609, 6489677788245343319,
+		5112602299894754389, 10828930062652518694, 15942305434158995996,
+		15445717675088218264, 4764500002345775851, 14673753115101942098,
+		236502320419669032, 13670483975188204088, 14931360615268175698,
+		8904234204977263924, 12836915408046564963, 12120302420213647524,
+		15755110976537356441, 5405758943702519480, 10951858968426898805,
+		17251681303478610375, 4144140664012008120, 18286145806977825275,
+		13075804672185204371, 10831805955733617705, 6172975950399619139,
+		12837097014497293886, 12903857913610213846, 560691676108914154,
+		1074659097419704618, 14266121283820281686, 11696403736022963346,
+		13383246710985227247, 7132746073714321322, 10608108217231874211,
+		9027884570906061560, 12893913769120703138, 15675160838921962454,
+		2511068401785704737, 14483183001716371453, 3774730664208216065,
+		5083371700846102796, 9583498264570933637, 17119870085051257224,
+		5217910858257235075, 10612176809475689857, 1924700483125896976,
+		7171619684536160599, 10949279256701751503, 15596196964072664893,
+		14097948002655599357, 615821766635933047, 5636498760852923045,
+		17618792803942051220, 580805356741162327, 425267967796817241,
+		8381470634608387938, 13212228678420887626, 16993060308636741960,
+		957923366004347591, 6210242862396777185, 1012818702180800310,
+		15299383925974515757, 17501832009465945633, 17453794942891241229,
+		15807805462076484491, 8407189590930420827, 974125122787311712,
+		1861591264068118966, 997568339582634050, 18046771844467391493,
+		17981867688435687790, 3809841506498447207, 9460108917638135678,
+		16172980638639374310, 958022432077424298, 4393365126459778813,
+		13408683141069553686, 13900005529547645957, 15773550354402817866,
+		16475327524349230602, 6260298154874769264, 12224576659776460914,
+		6405294864092763507, 7585484664713203306, 5187641382818981381,
+		12435998400285353380, 13554353441017344755, 646091557254529188,
+		11393747116974949255, 16797249248413342857, 15713519023537495495,
+		12823504709579858843, 4738086532119935073, 4429068783387643752,
+		585582692562183870, 1048280754023674130, 6788940719869959076,
+		11670856244972073775, 2488756775360218862, 2061695363573180185,
+		6884655301895085032, 3566345954323888697, 12784319933059041817,
+		4772468691551857254, 6864898938209826895, 7198730565322227090,
+		2452224231472687253, 13424792606032445807, 10827695224855383989,
+		11016608897122070904, 14683280565151378358, 7077866519618824360,
+		17487079941198422333, 3956319990205097495, 5804870313319323478,
+		8017203611194497730, 3310931575584983808, 5009341981771541845,
+		6930001938490791874, 14415278059151389495, 11001114762641844083,
+		6715939435439735925, 411419160297131328, 4522402260441335284,
+		3381955501804126859, 15935778656111987797, 4345051260540166684,
+		13978444093099579683, 9219789505504949817, 9245142924137529075,
+		11628184459157386459, 7242398879359936370, 8511401943157540109,
+		11948130810477009827, 6865450671488705049, 13965005347172621081,
+		15956599226522058336, 7737868921014130584, 2107342503741411693,
+		15818996300425101108, 16399939197527488760, 13971145494081508107,
+		3910681448359868691, 4249175367970221090, 9735751321242454020,
+		12418107929362160460, 241792245481991138, 5806488997649497146,
+		10724207982663648949, 1121862814449214435, 1326996977123564236,
+		4902706567834759475, 12782714623891689967, 7306216312942796257,
+		15681656478863766664, 957364844878149318, 5651946387216554503,
+		8197027112357634782, 6302075516351125977, 13454588464089597862,
+		15638309200463515550, 10116604639722073476, 12052913535387714920,
+		2889379661594013754, 15383926144832314187, 7841953313015471731,
+		17310575136995821873, 9820021961316981626, 15319619724109527290,
+		15349724127275899898, 10511508162402504492, 6289553862380300393,
+		15046218882019267110, 11772020174577005930, 3537640779967351792,
+		6801855569284252424, 17687268231192623388, 12968358613633237218,
+		1429775571144180123, 10427377732172208413, 12155566091986788996,
+		16465954421598296115, 12710429690464359999, 9547226351541565595,
+		12156624891403410342, 2985938688676214686, 18066917785985010959,
+		5975570403614438776, 11541343163022500560, 11115388652389704592,
+		9499328389494710074, 9247163036769651820, 3688303938005101774,
+		2210483654336887556, 15458161910089693228, 6558785204455557683,
+		1288373156735958118, 18433986059948829624, 3435082195390932486,
+		16822351800343061990, 3120532877336962310, 16681785111062885568,
+		7835551710041302304, 2612798015018627203, 15083279177152657491,
+		6591467229462292195, 10592706450534565444, 7438147750787157163,
+		323186165595851698, 7444710627467609883, 8473714411329896576,
+		2782675857700189492, 3383567662400128329, 3200233909833521327,
+		12897601280285604448, 3612068790453735040, 8324209243736219497,
+		15789570356497723463, 1083312926512215996, 4797349136059339390,
+		5556729349871544986, 18266943104929747076, 1620389818516182276,
+		172225355691600141, 3034352936522087096, 1266779576738385285,
+		3906668377244742888, 6961783143042492788, 17159706887321247572,
+		4676208075243319061, 10315634697142985816, 13435140047933251189,
+		716076639492622016, 13847954035438697558, 7195811275139178570,
+		10815312636510328870, 6214164734784158515, 16412194511839921544,
+		3862249798930641332, 1005482699535576005, 4644542796609371301,
+		17600091057367987283, 4209958422564632034, 5419285945389823940,
+		11453701547564354601, 9951588026679380114, 7425168333159839689,
+		8436306210125134906, 11216615872596820107, 3681345096403933680,
+		5770016989916553752, 11102855936150871733, 11187980892339693935,
+		396336430216428875, 6384853777489155236, 7551613839184151117,
+		16527062023276943109, 13429850429024956898, 9901753960477271766,
+		9731501992702612259, 5217575797614661659, 10311708346636548706,
+		15111747519735330483, 4353415295139137513, 1845293119018433391,
+		11952006873430493561, 3531972641585683893, 16852246477648409827,
+		15956854822143321380, 12314609993579474774, 16763911684844598963,
+		16392145690385382634, 1545507136970403756, 17771199061862790062,
+		12121348462972638971, 12613068545148305776, 954203144844315208,
+		1257976447679270605, 3664184785462160180, 2747964788443845091,
+		15895917007470512307, 15552935765724302120, 16366915862261682626,
+		8385468783684865323, 10745343827145102946, 2485742734157099909,
+		916246281077683950, 15214206653637466707, 12895483149474345798,
+		1079510114301747843, 10718876134480663664, 1259990987526807294,
+		8326303777037206221, 14104661172014248293, 15531278677382192198,
+		3874303698666230242, 3611366553819264523, 1358753803061653874,
+		1552102816982246938, 14492630642488100979, 15001394966632908727,
+		2273140352787320862, 17843678642369606172, 2903980458593894032,
+		16971437123015263604, 12969653681729206264, 3593636458822318001,
+		9719758956915223015, 7437601263394568346, 3327758049015164431,
+		17851524109089292731, 14769614194455139039, 8017093497335662337,
+		12026985381690317404, 739616144640253634, 15535375191850690266,
+		2418267053891303448, 15314073759564095878, 10333316143274529509,
+		16565481511572123421, 16317667579273275294, 13991958187675987741,
+		3753596784796798785, 9078249094693663275, 8459506356724650587,
+		12579909555010529099, 7827737296967050903, 5489801927693999341,
+		10995988997350541459, 14721747867313883304, 7915884580303296560,
+		4105766302083365910, 12455549072515054554, 13602111324515032467,
+		5205971628932290989, 5034622965420036444, 9134927878875794005,
+		11319873529597990213, 14815445109496752058, 2266601052460299470,
+		5696993487088103383, 6540200741841280242, 6631495948031875490,
+		5328340585170897740, 17897267040961463930, 9030000260502624168,
+		14285709137129830926, 12854071997824681544, 15408328651008978682,
+		1063314403033437073, 13765209628446252802, 242013711116865605,
+		4772374239432528212, 2515855479965038648, 5872624715703151235,
+		14237704570091006662, 678604024776645862, 12329607334079533339,
+		17570877682732917020, 2695443415284373666, 4312672841405514468,
+		6454343485137106900, 8425658828390111343, 16335501385875554899,
+		5551095603809016713, 11781094401885925035, 9395557946368382509,
+		9765123360948816956, 18107191819981188154, 16049267500594757404,
+		16349966108299794199, 1040405303135858246, 2366386386131378192,
+		223761048139910454, 15375217587047847934, 15231693398695187454,
+		12916726640254571028, 8878036960829635584, 1626201782473074365,
+		5758998126998248293, 18077917959300292758, 10585588923088536745,
+		15072345664541731497, 3559348759319842667, 12744591691872202375,
+		2388494115860283059, 6414691845696331748, 3069528498807764495,
+		8737958486926519702, 18059264986425101074, 3139684427605102737,
+		12378931902986734693, 410666675039477949, 12139894855769838924,
+		5780722552400398675, 7039346665375142557, 3020733445712569008,
+		2612305843503943561, 13651771214166527665, 16478681918975800939,
+		566088527565499576, 4715785502295754870, 6957318344287196220,
+		11645756868405128885, 13139951104358618000, 17650948583490040612,
+		18168787973649736637, 5486282999836125542, 6122201977153895166,
+		17324241605502052782, 10063523107521105867, 17537430712468011382,
+		10828407533637104262, 10294139354198325113, 12557151830240236401,
+		16673044307512640231, 10918020421896090419, 11077531235278014145,
+		5499571814940871256, 2334252435740638702, 18177461912527387031,
+		2000007376901262542, 7968425560071444214, 1472650787501520648,
+		3115849849651526279, 7980970700139577536, 12153253535907642097,
+		8109716914843248719, 3154976533165008908, 5553369513523832559,
+		10345792701798576501, 3677445364544507875, 10637177623943913351,
+		7380255087060498096, 14479400372337014801, 15381362583330700960,
+		204531043189704802, 13699106540959723942, 3817903465872254783,
+		10972364467110284934, 2701394334530963810, 2931625600749229147,
+		16428252083632828910, 11873166501966812913, 5566810080537233762,
+		7840617383807795056, 10699413880206684652, 18259119259617231436,
+		10332714341486317526, 10137911902863059694, 669146221352346842,
+		8373571610024623455, 10620002450820868661, 12220730820779815970,
+		5902974968095412898, 7931010481705150841, 16413777368097063650,
+		11273457888324769727, 13719113891065284171, 8327795098009702553,
+		10333342364827584837, 6202832891413866653, 9137034567886143162,
+		14514450826524340059, 473610156015331016, 813689571029117640,
+		13776316799690285717, 10429708855338427756, 8995290140880620858,
+		2320123852041754384, 8082864073645003641, 6961777411740398590,
+		10008644283003991179, 3239064015890722333, 16762634970725218787,
+		16467281536733948427, 10563290046315192938, 5108560603794851559,
+		15121667220761532906, 14155440077372845941, 10050536352394623377,
+		15474881667376037792, 3448088038819200619, 3692020001240358871,
+		6444847992258394902, 8687650838094264665, 3028124591188972359,
+		16945232313401161629, 15547830510283682816, 3982930188609442149,
+		14270781928849894661, 13768475593433447867, 13815150225221307677,
+		8502397232429564693, 718377350715476994, 7459266877697905475,
+		8353375565171101521, 7807281661994435472, 16924127046922196149,
+		10157812396471387805, 2519858716882670232, 7384148884750265792,
+		8077153156180046901, 3499231286164597752, 2700106282881469611,
+		14679824700835879737, 14188324938219126828, 3016120398601032793,
+		10858152824243889420, 9412371965669250534, 4857522662584941069,
+		984331743838900386, 4094160040294753142, 2368635764350388458,
+		15101240511397838657, 15584415763303953578, 7831857200208015446,
+		1952643641639729063, 4184323302594028609, 16795120381104846695,
+		3541559381538365280, 15408472870896842474, 5628362450757896366,
+		16277348886873708846, 12437047172652330846, 10172715019035948149,
+		1999700669649752791, 6217957085626135027, 11220551167830336823,
+		16478747645632411810, 5437280487207382147, 11382378739613087836,
+		15866932785489521505, 5502694314775516684, 16440179278067648435,
+		15510104554374162846, 15722061259110909195, 10760687291786964354,
+		10736868329920212671, 4166148127664495614, 14303518358120527892,
+		9122250801678898571, 10028508179936801946, 216630713752669403,
+		10655207865433859491, 4041437116174699233, 6280982262534375348,
+		297501356638818866, 13976146806363377485, 13752396481560145603,
+		11472199956603637419, 16393728429143900496, 14752844047515986640,
+		1524477318846038424, 6596889774254235440, 1591982099532234960,
+		8065146456116391065, 3964696017750868345, 17040425970526664920,
+		11511165586176539991, 3443401252003315103, 16314977947073778249,
+		16860120454903458341, 5370503221561340846, 15362920279125264094,
+		2822458124714999779, 14575378304387898337, 9689406052675046032,
+		2872149351415175149, 13019620945255883050, 14929026760148695825,
+		8503417349692327218, 9677798905341573754, 828949921821462483,
+		16110482368362750196, 15794218816553655671, 14942910774764855088,
+		12026350906243760195, 13610867176871462505, 18324536557697872582,
+		2658962269666727629, 327225403251576027, 9207535177029277544,
+		8744129291351887858, 6129603385168921503, 18385497655031085907,
+		13024478718952333892, 14547683159720717167, 5932119629366981711,
+		325385464632594563, 3559879386019806291, 6629264948665231298,
+		14358245326238118181, 15662449672706340765, 13975503159145803297,
+		3609534220891499022, 4224273587485638227, 9274084767162416370,
+		13156843921244091998, 18284750575626858789, 14664767920489118779,
+		11292057742031803221, 13919998707305829132, 14473305049457001422,
+		9696877879685767807, 1406758246007973837, 2429517644459056881,
+		14361215588101587430, 11386164476149757528, 10474116023593331839,
+		2921165656527786564, 15604610369733358953, 12955027028676000544,
+		10314281035410779907, 3167047178514709947, 1088721329408346700,
+		17930425515478182741, 7466411836095405617, 15534027454610690575,
+		10879629128927506091, 11502219301371200635, 13915106894453889418,
+		4226784327815861027, 12335222183627106346, 3648499746356007767,
+		18441388887898023393, 18117929843327093625, 4237736098094830438,
+		14229123019768296655, 3930112058127932690, 12663879236019645778,
+		9281161952002617309, 4978473890680876319, 845759387067546611,
+		1386164484606776333, 8008554770639925512, 11159581016793288971,
+		18065390393740782906, 17647985458967631018, 9092379465737744314,
+		2914678236848656327, 4376066698447630270, 16057186499919087528,
+		3031333261848790078, 2926746602873431597, 7931945763526885287,
+		147649915388326849, 15801792398814946230, 5265900391686545347,
+		16173686275871890830, 7562781050481886043, 5853506575839330404,
+		14957980734704564792, 10944286556353523404, 1783009880614150597,
+		9529762028588888983, 822992871011696119, 2130074274744257510,
+		8000279549284809219, 3514744284158856431, 128770032569293263,
+		3737367602618100572, 16364836605077998543, 783266423471782696,
+		4569418252658970391, 11093950688157406886, 14888808512267628166,
+		4217786261273670948, 17047486076688645713, 14133826721458860485,
+		17539744882220127106, 12394675039129853905, 5757634999463277090,
+		9621947619435861331, 1182210208559436772, 14603391040490913939,
+		17481976703660945893, 14063388816234683976, 2046622692581829572,
+		8294969799792017441, 5293778434844788058, 17976364049306763808,
+		399482430848083948, 16495545010129798933, 15241340958282367519,
+		989828753826900814, 17616558773874893537, 2471817920909589004,
+		11764082277667899978, 9618755269550400950, 1240014743757147125,
+		1887649378641563002, 1842982574728131416, 13243531042427194002,
+		7688268125537013927, 3080422097287486736, 2562894809975407783,
+		12428984115620094788, 1355581933694478148, 9895969242586224966,
+		8628445623963160889, 4298916726468199239, 12773165416305557280,
+		5240726258301567487, 4975412836403427561, 1842172398579595303,
+		7812151462958058676, 17974510987263071769, 14980707022065991200,
+		18294903201142729875, 12911672684850242753, 8979482998667235743,
+		16808468362384462073, 5981317232108359798, 12373702800369335100,
+		16119707581920094765, 2782738549717633602, 15454155188515389391,
+		16495638000603654629, 16348757069342790497, 7769562861984504567,
+		17504300515449231559, 5557710032938318996, 11846125204788401203,
+		13957316349928882624, 2738350683717432043, 15738068448047700954,
+		6224714837294524999, 6081930777706411111, 11366312928059597928,
+		4355315799925031482, 12393324728734964015, 15277140291994338591,
+		1406052433297386355, 15859448364509213398, 1672805458341158435,
+		2926095111610982994, 11056431822276774455, 12083767323511977430,
+		3296968762229741153, 12312076899982286460, 17769284994682227273,
+		15349428916826953443, 1056147296359223910, 18305757538706977431,
+		6214378374180465222, 14279648441175008454, 17791306410319136644,
+		956593013486324072, 2921235772936241950, 10002890515925652606,
+		10399654693663712506, 6446247931049971441, 6380465770144534958,
+		11439178472613251620, 10131486500045494660, 3692642123868351947,
+		10972816599561388940, 4931112976348785580, 8213967169213816566,
+		15336469859637867841, 15026830342847689383, 7524668622380765825,
+		17309937346758783807, 372780684412666438, 5642417144539399955,
+		18303842993081194577, 11085303253831702827, 15658163165983586950,
+		8517521928922081563, 16091186344159989860, 17614656488010863910,
+		4736067146481515156, 13449945221374241354, 17755469346196579408,
+		13300502638545717375, 6611828134763118043, 14177591906740276597,
+		9340430243077460347, 7499765399826404087, 3409518087967832469,
+		9013253864026602045, 4444307427984430192, 3729283608700519712,
+		13642048880719588383, 16486557958022946240, 2996465014991157904,
+		10020049344596426576, 12302485648009883778, 8492591321344423126,
+		17407986443716172520, 10530482934957373052, 15740662350540828750,
+		1790629986901049436, 6305948377669917188, 15092985352503125323,
+		928505047232899787, 14404651977039851607, 7564177565277805597,
+		3411236815351677870, 7752718145953236134, 12315979971311483798,
+		12477729506691004724, 14654956300924793305, 6689803038918974388,
+		1540738812233000153, 13508351811701989957, 15864432023192136053,
+		7990997967273843917, 7424300239290765161, 39585249496300263,
+		3877436595063283319, 10710642254398044448, 4653804418844456375,
+		1232267496410380283, 3690525514009038824, 15459770765077428485,
+		13240346522153894145, 5674964360688390624, 16973644653010587289,
+		15924280764204855206, 15196708627253442662, 17596174821341373274,
+		16196745023027393691, 6980050627399795351, 17582264380857746637,
+		18170372407506856324, 12108126025631005514, 15687749089493373169,
+		5814107289258228434, 9381977959648494876, 15895601183088112734,
+		16267869075651604263, 15228381979765852785, 11949618678312581999,
+		4545324791131029438, 582725409406225185, 15282520250746126790,
+		14758446535973412711, 7605613563088071833, 1111140641057375915,
+		5364843095234852245, 218335432181198977, 4891472444796201742,
+		4564628942836375772, 15500501278323817088, 4913946328556108657,
+		2684786251736694229, 12090498456116310122, 5310885782157038567,
+		5032788439854011923, 12627401038822728242, 11869662610126430929,
+		17650156853043540226, 12126672500118808436, 10437658933435653256,
+		13133995470637873311, 4601324715591152820, 1874350460376708372,
+		5808688626286061164, 13777088437302430376, 5018451954762213522,
+		2588296738534474754, 5503414509154170711, 5230497186769951796,
+		13261090710400573914, 8515217303152165705, 11074538219737365303,
+		15481562385740613213, 12705484409881007350, 14221931471178549498,
+		12905633420087112297, 17337759164357146506, 14081997515778175224,
+		17384320185513122939, 7131793076779216692, 17483217190312403109,
+		900692047897995877, 14723287313048560400, 6132094372965340305,
+		7572797575350925726, 12725160700431903514, 380860122911632449,
+		1900504978569024571, 8423729759529914138, 7305587201606052334,
+		12446871355267313320, 4615812356515386206, 3361817115406652303,
+		17690418922000878428, 14632214537567910559, 2709702289926174775,
+		3459675155951086144, 7788364399926538150, 16043992474431955950,
+		15830963823784930267, 4216893617835797954, 538159724689093771,
+		16029152738918251363, 14444848757576686696, 12941757045272633696,
+		10900480525147953314, 12547307449905859302, 16001571796892398181,
+		407942194622690676, 13873235372903944444, 18071603799493008777,
+		1015646077646778622, 9387605808959554815, 11566702442022019410,
+		7061722181092883183, 2629032108249254109, 5271820053177594520,
+		12640880742139693547, 10098688629735675775, 5716304472850923064,
+		3312674502353063071, 7295926377425759633, 833281439103466115,
+		16316743519466861667, 9912050326606348167, 11651133878100804242,
+		18026798122431692459, 6157758321723692663, 4856021830695749349,
+		7074321707293278978, 10748097797809573561, 2949954440753264783,
+		9813922580940661152, 9949237950172138336, 15643982711269455885,
+		16078663425810239127, 12508044395364228880, 12920301578340189344,
+		15368071871011048915, 1610400750626363239, 11994736084146033126,
+		6042574085746186088, 4154587549267685807, 15915752367312946034,
+		1191196620621769193, 467437822242538360, 2836463788873877488,
+		10476401302029164984, 1716169985450737419, 5327734953288310341,
+		3994170067185955262, 884431883768190063, 11019001754831208284,
+		14322807384384895215, 161011537360955545, 1466223959660131656,
+		5227048585229497539, 12410731857504225031, 2142243279080761103,
+		17682826799106851430, 1792612570704179953, 14727410295243056025,
+		1459567192481221274, 5669760721687603135, 17507918443756456845,
+		10354471145847018200, 10362475129248202288, 13143844410150939443,
+		6861184673150072028, 18396524361124732580, 543906666394301875,
+		12476817828199026728, 11853496871128122868, 12747674713108891748,
+		7986179867749890282, 9158195177777627533, 2217320706811118570,
+		8631389005200569973, 5538133061362648855, 3369942850878700758,
+		7813559982698427184, 509051590411815948, 10197035660403006684,
+		13004818533162292132, 9831652587047067687, 7619315254749630976,
+		994412663058993407}
+
+	mt := &MersenneTwister64{}
+	mt.SeedSlice([]uint64{0x12345, 0x23456, 0x34567, 0x45678})
+	out := []uint64{}
+	for _ = range expected64 {
+		out = append(out, mt.Uint64())
+	}
+
+	if !reflect.DeepEqual(out, expected64) {
+		t.Errorf("Mersenne Twister 64-bit expected output incorrect")
+		for i := range out {
+			if out[i] != expected64[i] {
+				t.Errorf("Bad at %d: Got %d, want %d", i, out[i], expected64[i])
+			}
+		}
+	}
+}
+
+func TestMersenne64Source(t *testing.T) {
+	r := rand.New(NewMersenneTwister64(8189023))
+	count := 0
+	for i := 0; i < 100; i++ {
+		if r.Intn(2) == 1 {
+			count++
+		}
+	}
+	if math.Abs(float64(count)-50) > 3 {
+		t.Errorf("Expect coin toss about 50-50, was %d", count)
+	}
+}
+
+// Initial run on x86 hardware: 16ns/op
+func BenchmarkMersenne64(b *testing.B) {
+	mt := &MersenneTwister64{}
+	mt.SeedSlice([]uint64{0x12345, 0x23456, 0x34567, 0x45678})
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		mt.Uint64()
+	}
+}
diff --git a/online.go b/online.go
new file mode 100644
index 0000000..d78fa93
--- /dev/null
+++ b/online.go
@@ -0,0 +1,281 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math"
+	"math/rand"
+)
+
+// Implemention of Online Codes. See
+// http://cs.nyu.edu/web/Research/TechReports/TR2002-833/TR2002-833.pdf
+// After Maymounkov and Mazieres
+//
+// The input message is a sequence of bytes which is split into N blocks.
+//
+// The constraints on the codec parameters are that 0.55*e*q*N >= q, probably
+// like q*2, which implies that 0.55*e*N >= 1 or e*N >= 1.82.
+//
+// Taking epsilon small (i.e. 0.01), this means N > 200 or so. A more
+// reasonable approach gives N > 400 or so for e=0.01.
+//
+// What this means for small texts is that e should be fairly big, like say
+// 0.3 or something. That means 0.3*N > 4 or so, meaning N is like 12-15.
+// This still allows us to get good probability of recovery. It means source
+// symbols are small, but (e/2)^(q+1) can still be 10^-12 with e=0.3 if
+// q is large enough (like 15).
+//
+// The number of code blocks expected to provide almost certain message
+// recovery is (1+epsilon)(1+0.55*Q*epsilon)N where N is the number of source blocks.
+//
+// For large texts, the primary concern is picking N such that the packet
+// size is convenient. Ideally epsilon is quite small, meaning not much extra
+// data transmission required, and since there are many blocks, that can be
+// done without increasing q very much.
+
+// onlineCodec contains the parameters for application of an online code, typically
+// for a particular known message.
+// Recommended parameters for large NumSourceBlocks: Epsilon=0.01, Quality=3.
+// Note that it requires quite large numbers (that is, thousands) of input blocks
+// to approach optimality. For example, NumSourceBlocks=1000 requires about 3%
+// overhead at these settings to achieve recovery error rate of 1e-8.
+// Implements fountain.Codec
+type onlineCodec struct {
+	// epsilon is the suboptimality parameter. ("Efficiency" or "e")
+	// A message of N blocks can be decoded with high probability
+	// from (1+3*epsilon)*numSourceBlocks received blocks.
+	epsilon float64
+
+	// quality is the decoder quality factor ("q"). This parameter influences the
+	// failure rate of the decoder.
+	// Given (1+3*epsilon)*N blocks, the algorithm will fail with probability
+	// (epsilon/2)^(quality+1)
+	quality int
+
+	// numSourceBlocks is the number of source blocks ("N") to construct from the
+	// input message. This parameter interacts with the message length to set the
+	// packet size, so should be picked with that in mind.
+	numSourceBlocks int
+
+	// randomSeed is a source of randomness for selecting auxiliary encoding blocks.
+	// This seeds a psuedorandom source identically for both encoding and decoding.
+	randomSeed int64
+
+	// cdf is the cumulative distribution function of the degree distribution.
+	cdf []float64
+}
+
+// NewOnlineCodec creates a new encoder for an Online code.
+// epsilon is the suboptimality parameter. ("Efficiency" or "e")
+// A message of N blocks can be decoded with high probability
+// from (1+3*epsilon)*numSourceBlocks received blocks.
+// quality is the decoder quality factor ("q"). This parameter influences the
+// failure rate of the decoder.
+// Given (1+3*epsilon)*N blocks, the algorithm will fail with probability
+// (epsilon/2)^(quality+1)
+// seed is the random seed used to pick auxiliary encoding blocks.
+func NewOnlineCodec(sourceBlocks int, epsilon float64, quality int, seed int64) Codec {
+	return &onlineCodec{
+		epsilon:         epsilon,
+		quality:         quality,
+		numSourceBlocks: sourceBlocks,
+		randomSeed:      seed,
+		cdf:             onlineSolitonDistribution(epsilon)}
+}
+
+// SourceBlocks returns the number of source blocks into which the codec will
+// partition an input message.
+func (c *onlineCodec) SourceBlocks() int {
+	return c.numSourceBlocks
+}
+
+// numAuxBlocks returns the number of auxiliary blocks to create for the outer
+// encoding.
+func (c onlineCodec) numAuxBlocks() int {
+	// Note: equation is from the paper.
+	return int(math.Ceil(0.55 * float64(c.quality) * c.epsilon * float64(c.numSourceBlocks)))
+}
+
+// estimateDecodeBlocksNeeded returns a rough lower bound on the number of decode
+// blocks likely needed to successfully decode a message. This number is about
+// (1+epsilon)(NumSourceBlocks + numAuxBlocks)
+func (c onlineCodec) estimateDecodeBlocksNeeded() int {
+	return int(math.Ceil((1 + c.epsilon) * float64(c.numSourceBlocks+c.numAuxBlocks())))
+}
+
+// GenerateIntermediateBlocks finds a set of auxiliary encoding blocks using an
+// LT process, which it then appends to the original set of message blocks.
+func (c *onlineCodec) GenerateIntermediateBlocks(message []byte, numBlocks int) []block {
+	src, aux := generateOuterEncoding(message, *c)
+	intermediate := make([]block, len(src), len(src)+len(aux))
+	copy(intermediate, src)
+	intermediate = append(intermediate, aux...)
+	return intermediate
+}
+
+// generateOuterEncoding creates the source and auxiliary blocks after section
+// 3.1 of http://pdos.csail.mit.edu/~petar/papers/maymounkov-bigdown-lncs.ps
+// Returns two slices of blocks: the original source blocks and the auxiliary
+// blocks.
+// Basic idea: the auxiliary blocks are randomly composed of the source blocks
+// and then used to generate code blocks. This makes recovery of the full
+// original message from code blocks more robust.
+func generateOuterEncoding(message []byte, codec onlineCodec) ([]block, []block) {
+	numAuxBlocks := codec.numAuxBlocks()
+	long, short := partitionBytes(message, codec.numSourceBlocks)
+	source := equalizeBlockLengths(long, short)
+
+	aux := make([]block, numAuxBlocks)
+	// Ensure all aux blocks have the same length as the source blocks,
+	// even if they don't happen to get loaded with data.
+	for i := range aux {
+		aux[i].padding = source[0].length()
+	}
+
+	random := rand.New(NewMersenneTwister(codec.randomSeed))
+	for i := 0; i < codec.numSourceBlocks; i++ {
+		touchAuxBlocks := sampleUniform(random, codec.quality, numAuxBlocks)
+		for _, j := range touchAuxBlocks {
+			aux[j].xor(source[i])
+		}
+	}
+
+	return source, aux
+}
+
+// generateCodeBlock creates a new code symbol, which is the XOR of
+// outer blocks [b_k1, b_k2, b_k3, ... b_kd]
+// Where the sequence k1, k2, k3, ..., kd is provided in the indices.
+func generateCodeBlock(source []block, aux []block, indices []int) block {
+	var symbol block
+
+	for _, i := range indices {
+		if i < len(source) {
+			symbol.xor(source[i])
+		} else {
+			symbol.xor(aux[i-len(source)])
+		}
+	}
+
+	return symbol
+}
+
+// PickIndices finds the source indices for a code block given an ID using
+// the CDF for the online degree distribution.
+func (c *onlineCodec) PickIndices(codeBlockIndex int64) []int {
+	random := rand.New(NewMersenneTwister(codeBlockIndex))
+
+	degree := pickDegree(random, c.cdf)
+	// Pick blocks from the augmented set of original+aux blocks produced
+	// by GenerateIntermediateBlocks.
+	s := sampleUniform(random, degree, c.SourceBlocks()+c.numAuxBlocks())
+	return s
+}
+
+// encodeOnlineBlocks creates a set of online code blocks given the ids provided.
+// An easy way to generate the ids is to pick a pseudo-random sequence and then
+// just grab the first M members of the sequence.
+// The characteristic of an online code is that this method may be called
+// repeatedly with different ids, generating different code blocks for the same
+// message, all of which can then be used interchangeably by the decoder.
+// For each code block, we pick a random set of outer-encoding blocks to XOR
+// to compose it.
+func encodeOnlineBlocks(message []byte, ids []int64, codec onlineCodec) []LTBlock {
+	source, aux := generateOuterEncoding(message, codec)
+	blocks := make([]LTBlock, len(ids))
+	for i := range blocks {
+		indices := codec.PickIndices(ids[i])
+		block := generateCodeBlock(source, aux, indices)
+		blocks[i].BlockCode = ids[i]
+		blocks[i].Data = make([]byte, source[0].length())
+		copy(blocks[i].Data, block.data)
+	}
+	return blocks
+}
+
+// onlineDecoder is the state required for decoding a particular message prepared
+// with the onlineCodec. It must be initialized with the same parameters
+// used for encoding, as well as the expected message length.
+// Implements fountain.Decoder
+type onlineDecoder struct {
+	codec         *onlineCodec
+	messageLength int
+
+	// The sparse equation matrix used for decoding.
+	matrix sparseMatrix
+}
+
+// NewDecoder creates an online transform decoder
+func (c *onlineCodec) NewDecoder(messageLength int) Decoder {
+	return newOnlineDecoder(c, messageLength)
+}
+
+// newOnlineDecoder creates a new decoder for a particular message. The codec
+// parameters as well as the original message length must be provided. The
+// decoder is only valid for decoding blocks for a particular source message.
+func newOnlineDecoder(c *onlineCodec, length int) *onlineDecoder {
+	d := &onlineDecoder{codec: c, messageLength: length}
+
+	numAuxBlocks := c.numAuxBlocks()
+	d.matrix.coeff = make([][]int, c.numSourceBlocks+numAuxBlocks)
+	d.matrix.v = make([]block, c.numSourceBlocks+numAuxBlocks)
+
+	// Now we add the initial auxiliary equations into the decode matrix.
+	// These come in as synthetic decode blocks, which have value 0 and
+	// coefficient bits set indicating their constituent outer blocks.
+	auxBlockComposition := make([][]int, numAuxBlocks)
+	random := rand.New(NewMersenneTwister(c.randomSeed))
+	for i := 0; i < c.numSourceBlocks; i++ {
+		touchAuxBlocks := sampleUniform(random, c.quality, numAuxBlocks)
+		for _, j := range touchAuxBlocks {
+			auxBlockComposition[j] = append(auxBlockComposition[j], i)
+		}
+	}
+	for i := range auxBlockComposition {
+		auxBlockComposition[i] = append(auxBlockComposition[i], i+c.numSourceBlocks)
+	}
+	// Note: these composition slices are guaranteed sorted since we added constituent
+	// source blocks in order, followed by the aux block index. So we can now just
+	// add them to the equation matrix as if they were received.
+
+	for i := range auxBlockComposition {
+		d.matrix.addEquation(auxBlockComposition[i], block{})
+	}
+
+	return d
+}
+
+// AddBlocks adds a set of encoded blocks to the decoder. Returns true if the
+// message can be fully decoded. False if there is insufficient information.
+func (d *onlineDecoder) AddBlocks(blocks []LTBlock) bool {
+	for i := range blocks {
+		indices := d.codec.PickIndices(blocks[i].BlockCode)
+		d.matrix.addEquation(indices, block{data: blocks[i].Data})
+	}
+	return d.matrix.determined()
+}
+
+// Decode extracts the decoded message from the decoder. If the decoder does
+// not have sufficient information to produce an output, returns a nil slice.
+func (d *onlineDecoder) Decode() []byte {
+	if !d.matrix.determined() {
+		return nil
+	}
+
+	d.matrix.reduce()
+
+	lenLong, lenShort, numLong, numShort := partition(d.messageLength, d.codec.numSourceBlocks)
+	return d.matrix.reconstruct(d.messageLength, lenLong, lenShort, numLong, numShort)
+}
diff --git a/online_test.go b/online_test.go
new file mode 100644
index 0000000..d2ecded
--- /dev/null
+++ b/online_test.go
@@ -0,0 +1,175 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math/rand"
+	"reflect"
+	"testing"
+)
+
+func printEncoding(source []block, aux []block, t *testing.T) {
+	t.Log("Outer Encoding Blocks")
+	t.Log("---------------------")
+	kb := 0
+	for s := range source {
+		t.Log("src", kb, source[s].data)
+		kb++
+	}
+	for a := range aux {
+		t.Log("aux", kb, aux[a].data)
+		kb++
+	}
+}
+
+func TestOnlineBlocks(t *testing.T) {
+	c := onlineCodec{epsilon: 0.01, quality: 5, numSourceBlocks: 6, randomSeed: 200}
+	if c.numAuxBlocks() != 1 {
+		t.Errorf("Got %d aux blocks, want 1", c.numAuxBlocks())
+	}
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+
+	source, aux := generateOuterEncoding(message, c)
+	printEncoding(source, aux, t)
+
+	if source[0].data[0] != 'a' {
+		t.Errorf("Source data should start with message beginning. Got %s", source[0].data)
+	}
+
+	block := generateCodeBlock(source, aux, []int{4})
+	if !reflect.DeepEqual(block.data, source[4].data) {
+		t.Errorf("Single data block is %v, should be %v", block.data, source[4].data)
+	}
+	block = generateCodeBlock(source, aux, []int{2, 5, 6})
+	if block.data[0] != 107^119^7 {
+		t.Errorf("XOR data block got %v, should be %v", block.data[0], 107^119^7)
+	}
+	t.Log("block =", block)
+
+	codec := NewOnlineCodec(6, 0.01, 5, 200)
+	ltblocks := EncodeLTBlocks(message, []int64{252}, codec)
+	indices := codec.PickIndices(252)
+	if !reflect.DeepEqual(indices, []int{4}) {
+		t.Errorf("Indices for 252 are %v, should be [4]", indices)
+	}
+	if !reflect.DeepEqual(ltblocks[0].Data, source[4].data) {
+		t.Errorf("Single data block is %v, should be %v", ltblocks[0].Data, source[4].data)
+	}
+	t.Log("block =", ltblocks[0])
+}
+
+func TestDecoder(t *testing.T) {
+	c := NewOnlineCodec(13, 0.3, 10, 200).(*onlineCodec)
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+	ids := make([]int64, 45)
+	random := rand.New(rand.NewSource(8923489))
+	for i := range ids {
+		ids[i] = int64(random.Intn(100000))
+	}
+	source, aux := generateOuterEncoding(message, *c)
+	printEncoding(source, aux, t)
+
+	blocks := encodeOnlineBlocks(message, ids, *c)
+	t.Log("blocks =", blocks)
+
+	d := newOnlineDecoder(c, len(message))
+
+	for i := 0; i < 16; i++ {
+		d.AddBlocks([]LTBlock{blocks[i]})
+		if testing.Verbose() {
+			printMatrix(d.matrix, t)
+		}
+	}
+
+	d.matrix.reduce()
+	t.Log("REDUCE")
+	printMatrix(d.matrix, t)
+
+	decoded := d.Decode()
+	printMatrix(d.matrix, t)
+	if !reflect.DeepEqual(decoded, message) {
+		t.Errorf("Got %v, want %v", decoded, message)
+	}
+}
+
+func TestDecoderBlockTable(t *testing.T) {
+	c := NewOnlineCodec(13, 0.3, 10, 0).(*onlineCodec)
+	if c.numAuxBlocks() != 22 {
+		t.Errorf("Got %d aux blocks, want 22", c.numAuxBlocks())
+	}
+	needed := c.estimateDecodeBlocksNeeded()
+	if needed != 46 {
+		t.Errorf("Got %d blocks expected to be needed, want 17", needed)
+	}
+
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+	random := rand.New(rand.NewSource(8234923))
+
+	moreBlocksNeeded := 0
+	for i := 0; i < 100; i++ {
+		c.randomSeed = random.Int63()
+		r := rand.New(rand.NewSource(random.Int63()))
+		ids := make([]int64, 45)
+		for i := range ids {
+			ids[i] = int64(r.Intn(100000))
+		}
+		blocks := encodeOnlineBlocks(message, ids, *c)
+
+		d := newOnlineDecoder(c, len(message))
+		d.AddBlocks(blocks[0:30])
+		if !d.matrix.determined() {
+			moreBlocksNeeded++
+			d.AddBlocks(blocks[31:46])
+		}
+		decoded := d.Decode()
+		if !reflect.DeepEqual(decoded, message) {
+			t.Errorf("Got %v, want %v", decoded, message)
+		}
+	}
+
+	if moreBlocksNeeded > 2 {
+		t.Errorf("Needed too many high-block-count decoding sequences: %d", moreBlocksNeeded)
+	}
+}
+
+func TestDecodeMessageTable(t *testing.T) {
+	c := NewOnlineCodec(10, 0.2, 7, 0).(*onlineCodec)
+	random := rand.New(rand.NewSource(8234982))
+	for i := 0; i < 100; i++ {
+		c.randomSeed = random.Int63()
+		r := rand.New(rand.NewSource(random.Int63()))
+		messageLen := r.Intn(1000) + 1000
+		message := make([]byte, messageLen)
+		for j := 0; j < len(message); j++ {
+			message[j] = byte(r.Intn(200))
+		}
+		ids := make([]int64, 50)
+		for i := range ids {
+			ids[i] = int64(r.Intn(100000))
+		}
+		blocks := encodeOnlineBlocks(message, ids, *c)
+
+		d := newOnlineDecoder(c, len(message))
+		d.AddBlocks(blocks[0:25])
+		if !d.matrix.determined() {
+			t.Errorf("Message should be determined after 25 blocks")
+		} else {
+			decoded := d.Decode()
+			if !reflect.DeepEqual(decoded, message) {
+				t.Errorf("Incorrect message decode. Length=%d", len(message))
+			}
+		}
+	}
+}
diff --git a/raptor.go b/raptor.go
new file mode 100644
index 0000000..3dfa3a4
--- /dev/null
+++ b/raptor.go
@@ -0,0 +1,370 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math"
+	"sort"
+)
+
+// The Raptor fountain code (also called the R10 code) from RFC 5053.
+// This code nearly matches the performance of the random binary fountain, but
+// does so with a hard cap on the degree distribution on code blocks, meaning
+// that the decode process becomes linear instead of quadratic.
+//
+// In addition, it is a systematic code, meaning that the original source blocks
+// are part of the encoding. This enables the source blocks to be sent simply,
+// and then repair blocks constructed as needed using the code.
+//
+// A limitation is that the code supports a maximum of 8192 source blocks,
+// thus requiring very large source messages to be split up into sub-messages if
+// smaller packet sizes are a goal. Performance varies from the random fountain
+// the most for higher loss rates and smaller numbers of source blocks. A reasonable
+// expectation is that the encoding overhead due to using the code is a few percent.
+//
+// When encoding the message, the codec takes a list of block IDs and returns a
+// set of raptor coded blocks generated according to the RFC. The contract in the
+// RFC is that ids from 0 to K (codec.NumSourceSymbols) are identical to the source
+// symbols. (That's what makes it a systematic code.) Such symbols should not be in
+// the same packet as repair symbols with id >= K.
+//
+// A typical usage in a transmission system might be to just split the message and
+// send the first K symbols normally. Then create the codec and generate repair
+// symbols using random ESI values >= K until the message is reconstructed by
+// the receiver.
+//
+// The BlockCode in the resulting LTBlocks will be a uint16-compatible value.
+//
+// IMPORTANT NOTE: encoding is destructive to the input message.
+
+// raptorCodec describes the parameters needed to construct a raptor code. The codec
+// governs the production of an unbounded set of LTBlocks from a given source message.
+// If the total transfer size is such that the application wants to split the message
+// into sub-blocks before transfer, that should be done independently of this codec
+// in accordance with the instructions in RFC 5053. For many uses, there will be a
+// value of NumSourceSymbols and SymbolAlignmentSize such that the resulting LTBlock
+// size is of an acceptable length. Note: the RFC provides a way to pack multiple
+// symbols per packet. If packet erasure is the loss model for the code, this
+// ends up behaving like choosing a smaller NumSourceSymbols, in which case it's
+// simpler to pass just one code symbol per packet.
+// Implements fountain.Codec
+type raptorCodec struct {
+	// SymbolAlignmentSize = Al is the size of each symbol in the source message in bytes.
+	// Usually 4. This is the XOR granularity in bytes. On 32-byte machines 4-byte XORs
+	// will be most efficient. On the other hand, the code will perform with less overhead
+	// with larger numbers of source blocks.
+	SymbolAlignmentSize int
+
+	// NumSourceSymbols = K. Must be in the range [4, 8192] (inclusive). This is
+	// how many source symbols the input message will be divided into. If NumSourceSymbols
+	// doesn't evenly divide the length of the message in units of SymbolAlignmentSize,
+	// there will be null padding applied to the block.
+	NumSourceSymbols int
+}
+
+// NewRaptorCodec creates a new R10 raptor codec using the provided number of
+// source blocks and alignment size.
+func NewRaptorCodec(sourceBlocks int, alignmentSize int) Codec {
+	return &raptorCodec{
+		NumSourceSymbols:    sourceBlocks,
+		SymbolAlignmentSize: alignmentSize}
+}
+
+// SourceBlocks returns the number of source symbols used by the codec.
+func (c *raptorCodec) SourceBlocks() int {
+	return c.NumSourceSymbols
+}
+
+// RAND function from section 5.4.4.1
+// x, i should be non-negative, m positive.
+// Produces a pseudo-random value in the range [0, m-1]
+func raptorRand(x, i, m uint32) uint32 {
+	v0 := v0table[(x+i)%256]
+	v1 := v1table[((x/256)+i)%256]
+	return (v0 ^ v1) % m
+}
+
+// Deg function from section 5.4.4.2
+// deg calculates the degree to be used in code block generation.
+func deg(v uint32) int {
+	f := [...]uint32{0, 10241, 491582, 712794, 831695, 948446, 1032189, 1048576}
+	d := [...]int{0, 1, 2, 3, 4, 10, 11, 40}
+
+	for j := 1; j < len(f)-1; j++ {
+		if v < f[j] {
+			return d[j]
+		}
+	}
+
+	return d[len(d)-1]
+}
+
+// From RFC section 5.4.2.3 This function computes L, S, and H from K.
+// K is the number of source symbols (limited to 2**16).
+// The return values are:
+// L is the number of intermediate symbols desired (K+S+H), followed by
+// S, the number of LDPC symbols, followed by
+// H, the number of half-symbols.
+func intermediateSymbols(k int) (int, int, int) {
+	// X is the smallest positive integer such that X*(X-1) >= 2*K
+	x := int(math.Floor(math.Sqrt(2 * float64(k))))
+	if x < 1 {
+		x = 1
+	}
+
+	for (x * (x - 1)) < (2 * k) {
+		x++
+	}
+
+	// S is the smallest prime such that S >= ceil(0.01*K) + X
+	s := int(math.Ceil(0.01*float64(k))) + x
+	s = smallestPrimeGreaterOrEqual(s)
+
+	// H is the smallest integer such that choose(H, ceil(H/2)) >= K + S
+	// choose(h, h/2) <= 4^(h/2), so begin with
+	// h/2 ln(4) = ln K+S
+	// h = ln(K+S)/ln(4)
+	h := int(math.Floor(math.Log(float64(s)+float64(k)) / math.Log(4)))
+	for centerBinomial(h) < k+s {
+		h++
+	}
+
+	return k + s + h, s, h
+}
+
+// Triple generator from RFC section 5.4.4.4
+// k is the number of source symbols.
+// x is the (random) code symbol ID.
+// The generator creates values (d, a, b) to be used in constructing intermediate blocks.
+func tripleGenerator(k int, x uint16) (int, uint32, uint32) {
+	l, _, _ := intermediateSymbols(k)
+	lprime := smallestPrimeGreaterOrEqual(l)
+	q := uint32(65521) // largest prime < 2^16
+	jk := uint32(systematicIndextable[k])
+
+	a := uint32((53591 + (uint64(jk) * 997)) % uint64(q))
+	b := (10267 * (jk + 1)) % q
+	y := uint32((uint64(b) + (uint64(x) * uint64(a))) % uint64(q))
+	v := raptorRand(y, 0, 1048576) // 1048576 == 2^20
+	d := deg(v)
+	a = 1 + raptorRand(y, 1, uint32(lprime-1))
+	b = raptorRand(y, 2, uint32(lprime))
+
+	return d, a, b
+}
+
+// findLTIndices discovers the composition of the ESI=x LT code block for a
+// raptor code. k is the number of source blocks.
+func findLTIndices(k int, x uint16) []int {
+	l, _, _ := intermediateSymbols(k)
+	lprime := uint32(smallestPrimeGreaterOrEqual(l))
+	d, a, b := tripleGenerator(k, x)
+
+	if d > l {
+		d = l
+	}
+
+	indices := make([]int, 0)
+	for b >= uint32(l) {
+		b = (b + a) % lprime
+	}
+	indices = append(indices, int(b))
+
+	for j := 1; j < d; j++ {
+		b = (b + a) % lprime
+		for b >= uint32(l) {
+			b = (b + a) % lprime
+		}
+		indices = append(indices, int(b))
+	}
+
+	sort.Ints(indices)
+	return indices
+}
+
+// ltEncode is the LT encoding function. RFC section 5.4.4.3
+// c is the intermediate symbol vector, k is the number of source symbols.
+// x is the symbol ID we are generating.
+// The output is an code block containing the bytes of that symbol.
+func ltEncode(k int, x uint16, c []block) block {
+	indices := findLTIndices(k, x)
+
+	result := block{}
+	for _, i := range indices {
+		result.xor(c[i])
+	}
+
+	return result
+}
+
+// raptorIntermediateBlocks takes the source blocks and follows the algorithm
+// described in the RFC to generate the intermediate encoding which is then used
+// as the LT source encoding for the systematic code. The properties of the intermediate
+// encoding is that we will create L resulting blocks from K source blocks.
+// The first K intermediate blocks have an LT relationship with the K source blocks --
+// we basically use an unsystematic LT code using the tripleGenerator values where
+// the X "ESI" is simply the source block index. We then generate S+H additional
+// intermediate blocks.
+//
+// The S blocks are a low density parity check group of blocks which have contributions
+// from three of the first K intermediate blocks arranged in successive clusters.
+// This low density arrangement cycles column-wise in the decode matrix, meaning
+// that there is extra decode-friendly information in the decode matrix to make the
+// expected decoding operations use a sparse enough matrix so that it is efficient.
+//
+// The H blocks are composed in the decode matrix of many (half) of the first K
+// intermediate blocks. The coding is taken from a gray code, and is intended to
+// be more-or-less equivalent to a random binary fountain from the coding
+// perspective, so when these blocks are chosen in the coded blocks, it ensures
+// good performance by making sure there are no unrepresented source blocks.
+//
+// We then decode this -- the J(K) systematic index values are chosen such that
+// the first L=K+S+H members of this set will produce an invertible decode matrix.
+// The guarantees that the re-encoding of the first K code symbols are equal to
+// the source symbols (ensuring a systematic code), and that the analysis of the
+// overall code will follow the performance of the random binary fountain closely.
+//
+// This method is destructive to the source blocks.
+func raptorIntermediateBlocks(source []block) []block {
+	ltdecoder := newRaptorDecoder(&raptorCodec{SymbolAlignmentSize: 1,
+		NumSourceSymbols: len(source)}, 1)
+	for i := 0; i < len(source); i++ {
+		indices := findLTIndices(len(source), uint16(i))
+		ltdecoder.matrix.addEquation(indices, source[i])
+	}
+
+	ltdecoder.matrix.reduce()
+
+	// panics if ~ltdecoder.determined. The J(K) selection should ensure that
+	// never happens.
+	intermediate := ltdecoder.matrix.v
+	return intermediate
+}
+
+// GenerateIntermediateBlocks creates the pre-code representation given the
+// message argument blocks. For the raptor code, this pre-code is generated by
+// a reverse-coding process which ensures that for BlockCode=0, the 0th block of
+// the incoming message is produced, and so on up to the 'len(message)-1'th BlockCode.
+func (c *raptorCodec) GenerateIntermediateBlocks(message []byte, numBlocks int) []block {
+	sourceLong, sourceShort := partitionBytes(message, numBlocks)
+	source := equalizeBlockLengths(sourceLong, sourceShort)
+	return raptorIntermediateBlocks(source)
+}
+
+// PickIndices chooses a set of indices for the provided CodeBlock index value
+// which are used to compose an LTBlock. It functions by
+func (c *raptorCodec) PickIndices(codeBlockIndex int64) []int {
+	return findLTIndices(int(c.SourceBlocks()), uint16(codeBlockIndex))
+}
+
+// NewDecoder creates a new raptor decoder
+func (c *raptorCodec) NewDecoder(messageLength int) Decoder {
+	return newRaptorDecoder(c, messageLength)
+}
+
+// raptorDecoder is the state required for decoding a particular message prepared
+// with the Raptor code. It must be initialized with the same raptorCodec parameters
+// used for encoding, as well as the expected message length.
+type raptorDecoder struct {
+	codec         raptorCodec
+	messageLength int
+
+	// The sparse equation matrix used for decoding.
+	matrix sparseMatrix
+}
+
+// newRaptorDecoder creates a new raptor decoder for a given message. The
+// codec supplied must be the same one as the message was encoded with.
+func newRaptorDecoder(c *raptorCodec, length int) *raptorDecoder {
+	d := &raptorDecoder{codec: *c, messageLength: length}
+
+	l, s, h := intermediateSymbols(c.NumSourceSymbols)
+
+	// Add the S + H intermediate symbol composition equations.
+	d.matrix.coeff = make([][]int, l)
+	d.matrix.v = make([]block, l)
+
+	k := c.NumSourceSymbols
+	compositions := make([][]int, s)
+
+	for i := 0; i < k; i++ {
+		a := 1 + (int(math.Floor(float64(i)/float64(s))) % (s - 1))
+		b := i % s
+		compositions[b] = append(compositions[b], i)
+		b = (b + a) % s
+		compositions[b] = append(compositions[b], i)
+		b = (b + a) % s
+		compositions[b] = append(compositions[b], i)
+	}
+	for i := 0; i < s; i++ {
+		compositions[i] = append(compositions[i], k+i)
+		d.matrix.addEquation(compositions[i], block{})
+	}
+
+	compositions = make([][]int, h)
+
+	hprime := int(math.Ceil(float64(h) / 2))
+	m := buildGraySequence(k+s, hprime)
+	for i := 0; i < h; i++ {
+		for j := 0; j < k+s; j++ {
+			if bitSet(uint(m[j]), uint(i)) {
+				compositions[i] = append(compositions[i], j)
+			}
+		}
+		compositions[i] = append(compositions[i], k+s+i)
+		d.matrix.addEquation(compositions[i], block{})
+	}
+
+	return d
+}
+
+// AddBlocks adds a set of encoded blocks to the decoder. Returns true if the
+// message can be fully decoded. False if there is insufficient information.
+func (d *raptorDecoder) AddBlocks(blocks []LTBlock) bool {
+	for i := range blocks {
+		indices := findLTIndices(d.codec.NumSourceSymbols, uint16(blocks[i].BlockCode))
+		d.matrix.addEquation(indices, block{data: blocks[i].Data})
+	}
+	return d.matrix.determined()
+}
+
+// Decode extracts the decoded message from the decoder. If the decoder does
+// not have sufficient information to produce an output, returns a nil slice.
+func (d *raptorDecoder) Decode() []byte {
+	if !d.matrix.determined() {
+		return nil
+	}
+
+	d.matrix.reduce()
+
+	// Now the intermediate blocks are held in d.matrix.v. Use the encoder function
+	// to recover the source blocks.
+	intermediate := d.matrix.v
+	source := make([]block, d.codec.NumSourceSymbols)
+	for i := 0; i < d.codec.NumSourceSymbols; i++ {
+		source[i] = ltEncode(d.codec.NumSourceSymbols, uint16(i), intermediate)
+	}
+
+	lenLong, lenShort, numLong, numShort := partition(d.messageLength, d.codec.NumSourceSymbols)
+	out := make([]byte, d.messageLength)
+	out = out[0:0]
+	for i := 0; i < numLong; i++ {
+		out = append(out, source[i].data[0:lenLong]...)
+	}
+	for i := numLong; i < numLong+numShort; i++ {
+		out = append(out, source[i].data[0:lenShort]...)
+	}
+	return out
+}
diff --git a/raptor_test.go b/raptor_test.go
new file mode 100644
index 0000000..5858f4f
--- /dev/null
+++ b/raptor_test.go
@@ -0,0 +1,323 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math/rand"
+	"reflect"
+	"testing"
+)
+
+func TestRaptorRand(t *testing.T) {
+	var randTests = []struct {
+		x uint32
+		i uint32
+		m uint32
+		r uint32
+	}{
+		{1, 4, 150, 50},
+		{20005, 19, 25, 6},
+		{2180, 11, 1383483, 1166141},
+	}
+
+	for _, test := range randTests {
+		if test.r != raptorRand(test.x, test.i, test.m) {
+			t.Errorf("raptorRand(%d, %d, %d) = %d, should be %d",
+				test.x, test.i, test.m, raptorRand(test.x, test.i, test.m), test.r)
+		}
+	}
+}
+
+func TestDeg(t *testing.T) {
+	var degreeTests = []struct {
+		x uint32
+		d int
+	}{
+		{0, 1},
+		{10000, 1},
+		{10240, 1},
+		{10241, 2},
+		{10242, 2},
+		{715000, 4},
+		{1000000, 11},
+		{1034300, 40},
+		{1048575, 40},
+		{1048576, 40},
+	}
+
+	for _, test := range degreeTests {
+		if test.d != deg(test.x) {
+			t.Errorf("deg(%d) = %d, should be %d", test.x, deg(test.x), test.d)
+		}
+	}
+}
+
+func TestIntermediateSymbols(t *testing.T) {
+	var intermediateTests = []struct {
+		k int
+		l int
+		s int
+		h int
+	}{
+		{0, 4, 2, 2},
+		{1, 8, 3, 4},
+		{10, 23, 7, 6}, // from a Luby, Shokrollahi paper
+		{13, 26, 7, 6},
+		{14, 28, 7, 7},
+		{500, 553, 41, 12},
+		{5000, 5166, 151, 15},
+	}
+
+	for _, test := range intermediateTests {
+		l, s, h := intermediateSymbols(test.k)
+		if l != test.l || s != test.s || h != test.h {
+			t.Errorf("intermediateSymbols(%d) = (%d, %d, %d), should be %d, %d, %d",
+				test.k, l, s, h, test.l, test.s, test.h)
+		}
+	}
+}
+
+func TestTripleGenerator(t *testing.T) {
+	var tripleTests = []struct {
+		k int
+		x uint16
+		d int
+		a uint32
+		b uint32
+	}{
+		{0, 3, 2, 4, 3},
+		{1, 4, 4, 2, 5},
+		{4, 0, 10, 13, 1},
+		{4, 4, 4, 6, 2},
+		{500, 514, 2, 107, 279},
+		{1000, 52918, 3, 1070, 121},
+	}
+
+	for _, test := range tripleTests {
+		d, a, b := tripleGenerator(test.k, test.x)
+		if d != test.d || a != test.a || b != test.b {
+			t.Errorf("tripleGenerator(%d, %d) = (%d, %d, %d), should be %d, %d, %d",
+				test.k, test.x, d, a, b, test.d, test.a, test.b)
+		}
+	}
+}
+
+func TestSystematicIndices(t *testing.T) {
+	if systematicIndextable[4] != 18 {
+		t.Errorf("Systematic index for 4 was %d, must be 18", systematicIndextable[4])
+	}
+	if systematicIndextable[21] != 2 {
+		t.Errorf("Systematic index for 4 was %d, must be 2", systematicIndextable[4])
+	}
+	if systematicIndextable[8192] != 2665 {
+		t.Errorf("Systematic index for 4 was %d, must be 2665", systematicIndextable[4])
+	}
+}
+
+func TestLTIndices(t *testing.T) {
+	var ltIndexTests = []struct {
+		k       int
+		x       uint16
+		indices []int
+	}{
+		{4, 0, []int{1, 2, 3, 4, 6, 7, 8, 10, 11, 12}},
+		{4, 4, []int{2, 3, 8, 9}},
+		{100, 1, []int{51, 104}},
+		{1000, 727, []int{306, 687, 1040}},
+		{10, 57279, []int{19, 20, 21, 22}},
+	}
+
+	for _, test := range ltIndexTests {
+		indices := findLTIndices(test.k, test.x)
+		if !reflect.DeepEqual(indices, test.indices) {
+			t.Errorf("findLTIndices(%d, %d) = %v, should be %v",
+				test.k, test.x, indices, test.indices)
+		}
+	}
+}
+
+func TestRaptorDecoderConstruction(t *testing.T) {
+	decoder := newRaptorDecoder(&raptorCodec{SymbolAlignmentSize: 1,
+		NumSourceSymbols: 10}, 1)
+	printMatrix(decoder.matrix, t)
+	// From the first row of the constraint matrix. Test vectors from a paper by
+	// Luby and Shokrollahi.
+	if !reflect.DeepEqual(decoder.matrix.coeff[0], []int{0, 5, 6, 7, 10}) {
+		t.Errorf("First matrix equation was %v, should be {0, 5, 6, 7, 10}",
+			decoder.matrix.coeff[0])
+	}
+	// Fourth row
+	if !reflect.DeepEqual(decoder.matrix.coeff[1], []int{1, 2, 3, 8, 13}) {
+		t.Errorf("Second matrix equation was %v, should be {1, 2, 3, 8, 13}",
+			decoder.matrix.coeff[0])
+	}
+	// Fifth row
+	if !reflect.DeepEqual(decoder.matrix.coeff[2], []int{2, 3, 4, 7, 9, 14}) {
+		t.Errorf("Third matrix equation was %v, should be {2, 3, 4, 7, 9, 14}",
+			decoder.matrix.coeff[0])
+	}
+}
+
+func printIntermediateEncoding(intermediate []block, t *testing.T) {
+	t.Log("Intermediate Encoding Blocks")
+	t.Log("----------------------------")
+	kb := 0
+	for s := range intermediate {
+		t.Log("intermediate", kb, intermediate[s].data)
+		kb++
+	}
+}
+
+func TestIntermediateBlocks(t *testing.T) {
+	blocks := [4]block{
+		{data: []byte{0, 0, 0, 1}},
+		{data: []byte{0, 0, 1, 0}},
+		{data: []byte{0, 1, 0, 0}},
+		{data: []byte{1, 0, 0, 0}},
+	}
+
+	srcBlocks := make([]block, len(blocks))
+	for i := range blocks {
+		srcBlocks[i].xor(blocks[i])
+	}
+	intermediate := raptorIntermediateBlocks(srcBlocks) // destructive to srcBlocks
+	if len(intermediate) != 14 {
+		t.Errorf("Length of intermediate blocks is %d, should be 14", len(intermediate))
+	}
+	printIntermediateEncoding(intermediate, t)
+
+	t.Log("Finding intermediate equations")
+	// test that intermediate ltEnc equations hold
+	for i := 0; i < 4; i++ {
+		block := ltEncode(4, uint16(i), intermediate)
+		if !reflect.DeepEqual(block, blocks[i]) {
+			t.Errorf("The result of LT encoding on the intermediate blocks for "+
+				"block %d is %v, should be the source blocks %v", i,
+				block.data, blocks[i].data)
+		}
+	}
+}
+
+func TestSystematicRaptorCode(t *testing.T) {
+	c := NewRaptorCodec(13, 2)
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+	blocks := c.GenerateIntermediateBlocks(message, c.SourceBlocks())
+
+	messageCopy := []byte("abcdefghijklmnopqrstuvwxyz")
+	sourceLong, sourceShort := partitionBytes(messageCopy, c.SourceBlocks())
+	sourceCopy := equalizeBlockLengths(sourceLong, sourceShort)
+
+	for _, testIndex := range []int64{0, 1, 2, 3, 4, 5} {
+		t.Logf("Testing %d", testIndex)
+		b := ltEncode(13, uint16(testIndex), blocks)
+		if !reflect.DeepEqual(b.data, sourceCopy[testIndex].data) {
+			t.Errorf("LT encoding of CodeBlock=%d was (%v), should be the %d'th source block (%v)",
+				testIndex, b.data, testIndex, sourceCopy[testIndex].data)
+		}
+	}
+}
+
+func TestIntermediateBlocks13(t *testing.T) {
+	blocks := make([]block, 13)
+	for i := range blocks {
+		blocks[i].data = make([]byte, 13)
+		blocks[i].data[i] = 1
+	}
+
+	srcBlocks := make([]block, 13)
+	for i := range blocks {
+		srcBlocks[i].xor(blocks[i])
+	}
+	intermediate := raptorIntermediateBlocks(srcBlocks) // destructive to srcBlocks
+	if len(intermediate) != 26 {
+		t.Errorf("Length of intermediate blocks is %d, should be 26", len(intermediate))
+	}
+	printIntermediateEncoding(intermediate, t)
+
+	t.Log("Finding intermediate equations")
+	// test that intermediate ltEnc equations hold
+	for i := 0; i < 13; i++ {
+		block := ltEncode(13, uint16(i), intermediate)
+		if !reflect.DeepEqual(block, blocks[i]) {
+			t.Errorf("The result of LT encoding on the intermediate blocks for "+
+				"block %d is %v, should be the source blocks %v", i,
+				block.data, blocks[i].data)
+		}
+	}
+
+	ids := make([]int64, 45)
+	random := rand.New(rand.NewSource(8923489))
+	for i := range ids {
+		ids[i] = int64(random.Intn(60000))
+	}
+	c := raptorCodec{NumSourceSymbols: 13, SymbolAlignmentSize: 13}
+	srcBlocks = make([]block, 13)
+	for i := range blocks {
+		srcBlocks[i].xor(blocks[i])
+	}
+
+	message := make([]byte, 0)
+	for i := range blocks {
+		message = append(message, blocks[i].data...)
+	}
+	codeBlocks := EncodeLTBlocks(message, ids, &c) // destructive to srcBlocks
+
+	t.Log("DECODE--------")
+	decoder := newRaptorDecoder(&c, len(message))
+	for i := 0; i < 17; i++ {
+		decoder.AddBlocks([]LTBlock{codeBlocks[i]})
+	}
+
+	message = make([]byte, 0)
+	for i := range blocks {
+		message = append(message, blocks[i].data...)
+	}
+
+	if decoder.matrix.determined() {
+		t.Log("Recovered:\n", decoder.matrix.v)
+		out := decoder.Decode()
+		if !reflect.DeepEqual(message, out) {
+			t.Errorf("Decoding result must equal %v, got %v", message, out)
+		}
+	}
+}
+
+func TestRaptorCodec(t *testing.T) {
+	c := NewRaptorCodec(13, 2)
+	message := []byte("abcdefghijklmnopqrstuvwxyz")
+	ids := make([]int64, 45)
+	random := rand.New(rand.NewSource(8923489))
+	for i := range ids {
+		ids[i] = int64(random.Intn(60000))
+	}
+
+	messageCopy := make([]byte, len(message))
+	copy(messageCopy, message)
+
+	codeBlocks := EncodeLTBlocks(messageCopy, ids, c)
+
+	t.Log("DECODE--------")
+	decoder := newRaptorDecoder(c.(*raptorCodec), len(message))
+	for i := 0; i < 17; i++ {
+		decoder.AddBlocks([]LTBlock{codeBlocks[i]})
+	}
+	if decoder.matrix.determined() {
+		t.Log("Recovered:\n", decoder.matrix.v)
+		out := decoder.Decode()
+		if !reflect.DeepEqual(message, out) {
+			t.Errorf("Decoding result must equal %s, got %s", string(message), string(out))
+		}
+	}
+}
diff --git a/ru10.go b/ru10.go
new file mode 100644
index 0000000..84e13c8
--- /dev/null
+++ b/ru10.go
@@ -0,0 +1,210 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math"
+  "math/rand"
+  "sort"
+)
+
+// The RU10 fountain is an unsystematic(*) fountain code which uses a degree
+// distribution and intermediate block generation scheme similar to the
+// R10 (Raptor) code. The unsystematic code has much lower independent block
+// generation setup cost -- it requires only generating the auxiliary blocks,
+// and does not require the full decode run of the algorithm to generate the
+// intermediate encoding. This makes it cheaper in two ways for parallel
+// execution. If all receivers can be expected to have the decoder in place,
+// there is no decoding advantage to requiring the source blocks to appear in the
+// code block set. But more to the point, if multiple encoders are working from
+// the same source blocks, they can generate code blocks independently without
+// having to worry if they are re-transmitting source blocks. Since the encoder
+// state is cheaper and code blocks interchangeable, we sacrifice a little
+// performance for that parallelizability and statelessness. We also gain a little
+// in that this encoder variant needn't rely on the systematic number table
+// of the R10 codec, and without that constraint, the number of possible code block
+// ESI IDs is basically unlimited.
+//
+// (*) Well, not by design at least.
+
+// This triple generator uses the Mersenne Twister to generate random seeds.
+// k is the number of source symbols.
+// x is the (random) code symbol ID.
+// The generator creates values (d, a, b) to be used in constructing intermediate blocks.
+func ru10TripleGenerator(k int, x int64) (int, uint32, uint32) {
+	l, _, _ := intermediateSymbols(k)
+	lprime := smallestPrimeGreaterOrEqual(l)
+
+	// TODO(gbillock): nudge x as a function of k to get better overhead-failure curve?
+	rand := rand.New(NewMersenneTwister64(x))
+
+	v := uint32(rand.Int63() % 1048576)
+	a := uint32(1 + (rand.Int63() % int64(lprime - 1)))
+	b := uint32(rand.Int63() % int64(lprime))
+	d := deg(v)
+
+	return d, a, b
+}
+
+// ru10Codec implements the Raptor-alike fountain code.
+// Implements fountain.Codec.
+type ru10Codec struct {
+	numSourceSymbols int
+
+  symbolAlignmentSize int
+}
+
+// NewRU10Codec creates an unsystematic raptor-like fountain codec which uses an
+// intermediate block generation algorithm similar to the Raptor R10 codec.
+func NewRU10Codec(numSourceSymbols int, symbolAlignmentSize int) Codec {
+  return &ru10Codec{
+    numSourceSymbols: numSourceSymbols,
+    symbolAlignmentSize: symbolAlignmentSize}
+}
+
+// SourceBlocks returns the number of source blocks the codec uses in the
+// source message plus intermediate blocks added.
+func (c *ru10Codec) SourceBlocks() int {
+	return c.numSourceSymbols
+}
+
+// PickIndices uses the R10 distribution function to pick indices. It gets
+// numbers from the triple generator.
+func (c *ru10Codec) PickIndices(codeBlockIndex int64) []int {
+	d, a, b := ru10TripleGenerator(c.numSourceSymbols, codeBlockIndex)
+	l, _, _ := intermediateSymbols(c.numSourceSymbols)
+	lprime := uint32(smallestPrimeGreaterOrEqual(l))
+
+	if d > l {
+		d = l
+	}
+
+	indices := make([]int, 0)
+	for b >= uint32(l) {
+		b = (b + a) % lprime
+	}
+	indices = append(indices, int(b))
+
+	for j := 1; j < d; j++ {
+		b = (b + a) % lprime
+		for b >= uint32(l) {
+			b = (b + a) % lprime
+		}
+		indices = append(indices, int(b))
+	}
+
+	sort.Ints(indices)
+	return indices
+}
+
+// RU10 intermediate encoding consists of the source symbols plus additional
+// intermediate symbols consisting of exactly the S and H blocks the R10 code
+// uses. The difference is that the code is unsystematic -- the source blocks
+// aren't necessarily going to be represented at the output -- so when we do
+// the decode we don't need to translate from the intermediate symbols back to
+// the source symbols: the source symbols are just the first K intermediate symbols.
+func (c *ru10Codec) GenerateIntermediateBlocks(message []byte, numBlocks int) []block {
+	sourceLong, sourceShort := partitionBytes(message, c.numSourceSymbols)
+	source := equalizeBlockLengths(sourceLong, sourceShort)
+
+	_, s, h := intermediateSymbols(c.numSourceSymbols)
+
+	k := c.numSourceSymbols
+	compositions := make([][]int, s)
+
+	for i := 0; i < k; i++ {
+		a := 1 + (int(math.Floor(float64(i)/float64(s))) % (s - 1))
+		b := i % s
+		compositions[b] = append(compositions[b], i)
+		b = (b + a) % s
+		compositions[b] = append(compositions[b], i)
+		b = (b + a) % s
+		compositions[b] = append(compositions[b], i)
+	}
+	for i := 0; i < s; i++ {
+		b := generateLubyTransformBlock(source, compositions[i])
+		source = append(source, b)
+	}
+
+	hprime := int(math.Ceil(float64(h) / 2))
+	m := buildGraySequence(k+s, hprime)
+	for i := 0; i < h; i++ {
+		hcomposition := make([]int, 0)
+		for j := 0; j < k+s; j++ {
+			if bitSet(uint(m[j]), uint(i)) {
+				hcomposition = append(hcomposition, j)
+			}
+		}
+		b := generateLubyTransformBlock(source, hcomposition)
+		source = append(source, b)
+	}
+	return source
+}
+
+// NewDecoder creates a new RU10 decoder
+func (c *ru10Codec) NewDecoder(messageLength int) Decoder {
+  return newRU10Decoder(c, messageLength)
+}
+
+// ru10Decoder is the corresponding decoder for fountain codes using the RU10 encoder.
+type ru10Decoder struct {
+	decoder *raptorDecoder
+}
+
+// newRU10Decoder creates a new raptor decoder for a given message. The
+// codec supplied must be the same one as the message was encoded with.
+func newRU10Decoder(c *ru10Codec, length int) *ru10Decoder {
+	return &ru10Decoder{
+		decoder: newRaptorDecoder(&raptorCodec{
+      SymbolAlignmentSize: c.symbolAlignmentSize,
+			NumSourceSymbols: c.numSourceSymbols},
+			length),
+	}
+}
+
+func (d *ru10Decoder) AddBlocks(blocks []LTBlock) bool {
+	c := ru10Codec{
+    symbolAlignmentSize: d.decoder.codec.SymbolAlignmentSize,
+		numSourceSymbols: d.decoder.codec.NumSourceSymbols}
+	for i := range blocks {
+		indices := c.PickIndices(blocks[i].BlockCode)
+		d.decoder.matrix.addEquation(indices, block{data: blocks[i].Data})
+	}
+	return d.decoder.matrix.determined()
+}
+
+func (d *ru10Decoder) Decode() []byte {
+	if !d.decoder.matrix.determined() {
+		return nil
+	}
+
+	d.decoder.matrix.reduce()
+
+	// Now the intermediate blocks are held in d.decoder.matrix.v. The source
+	// blocks are the first K intermediate blocks.
+	intermediate := d.decoder.matrix.v
+
+	lenLong, lenShort, numLong, numShort :=
+		partition(d.decoder.messageLength, d.decoder.codec.NumSourceSymbols)
+	out := make([]byte, d.decoder.messageLength)
+	out = out[0:0]
+	for i := 0; i < numLong; i++ {
+		out = append(out, intermediate[i].data[0:lenLong]...)
+	}
+	for i := numLong; i < numLong+numShort; i++ {
+		out = append(out, intermediate[i].data[0:lenShort]...)
+	}
+	return out
+}
diff --git a/util.go b/util.go
new file mode 100644
index 0000000..196173d
--- /dev/null
+++ b/util.go
@@ -0,0 +1,291 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math"
+	"math/rand"
+	"sort"
+)
+
+// Note that these CDFs (cumulative distribution function) will be used for
+// selecting source blocks for code block generation. A typical algorithm
+// is to choose a number from a distribution, then pick uniformly that many
+// source blocks to XOR into a code block. To use CDF mapping values, pick a
+// random number r (0 <= r < 1) and then find the smallest i such that
+// CDF[i] >= r.
+
+// solitonDistribution returns a CDF mapping for the soliton distribution.
+// N (the number of elements in the CDF) cannot be less than 1
+// The CDF is one-based: the probability of picking 1 from the distribution
+// is CDF[1].
+func solitonDistribution(n int) []float64 {
+	cdf := make([]float64, n+1)
+	cdf[1] = 1 / float64(n)
+	for i := 2; i < len(cdf); i++ {
+		cdf[i] = cdf[i-1] + (1 / (float64(i) * float64(i-1)))
+	}
+	return cdf
+}
+
+// robustSolitonDistribution returns a CDF mapping for the robust solition
+// distribution.
+// This is an addition to the soliton distribution with three parameters,
+// N, M, and delta.
+// Before normalization, the correction pdf(i) = 1/i*M, for i=1..M-1,
+// pdf(M) = ln(N/(M*delta))/M
+// pdf(i) = 0 for i = M+1..N
+// These values are added to the ideal soliton distribution, and then the
+// result normalized.
+// The CDF is one-based: the probability of picking 1 from the distribution
+// is CDF[1].
+func robustSolitonDistribution(n int, m int, delta float64) []float64 {
+	pdf := make([]float64, n+1)
+
+	pdf[1] = 1/float64(n) + 1/float64(m)
+	total := pdf[1]
+	for i := 2; i < len(pdf); i++ {
+		pdf[i] = (1 / (float64(i) * float64(i-1)))
+		if i < m {
+			pdf[i] = 1 / (float64(i) * float64(m))
+		}
+		if i == m {
+			pdf[i] += math.Log(float64(n)/(float64(m)*delta)) / float64(m)
+		}
+		total += pdf[i]
+	}
+
+	cdf := make([]float64, n+1)
+	for i := 1; i < len(pdf); i++ {
+		pdf[i] /= total
+		cdf[i] = cdf[i-1] + pdf[i]
+	}
+	return cdf
+}
+
+// onlineSolitionDistribution returns a soliton-like distribution for
+// Online Codes
+// See http://pdos.csail.mit.edu/~petar/papers/maymounkov-bigdown-lncs.ps
+// 'Rateless Codes and Big Downloads' by Maymounkov and Mazieres
+// The distribution is described by a parameter epsilon, which is the
+// the "overage factor" required to reconstruct the source message in an
+// Online Code.
+// F = ciel(ln(eps^2/4 / ln(1 - eps/2))
+// and the pdf is pdf[1] = 1 - (1 + 1/F)/(1 + eps)
+// pdf[i] = ((1 - pdf[1])F) / ((F-1)i(i-1)) for 2 <= i <= F
+func onlineSolitonDistribution(eps float64) []float64 {
+	f := math.Ceil(math.Log(eps*eps/4) / math.Log(1-(eps/2)))
+
+	cdf := make([]float64, int(f+1))
+
+	// First coefficient is 1 - ( (1 + 1/f) / (1+e) )
+	rho := 1 - ((1 + (1 / f)) / (1 + eps))
+	cdf[1] = rho
+
+	// Subsequent i'th coefficient is (1-rho)*F / (F-1)i*(i-1)
+	for i := 2; i <= int(f); i++ {
+		rhoI := ((1 - rho) * f) / ((f - 1) * float64(i-1) * float64(i))
+		cdf[i] = cdf[i-1] + rhoI
+	}
+
+	return cdf
+}
+
+// pickDegree returns the smallest index i such that cdf[i] > r
+// (r a random number from the random generator)
+// cdf must be sorted in ascending order.
+func pickDegree(random *rand.Rand, cdf []float64) int {
+	r := random.Float64()
+	d := sort.SearchFloat64s(cdf, r)
+	if cdf[d] > r {
+		return d
+	}
+
+	if d < len(cdf)-1 {
+		return d + 1
+	} else {
+		return len(cdf) - 1
+	}
+}
+
+// sampleUniform picks num numbers from [0,max) uniformly.
+// There will be no duplicates.
+// If num >= max, simply returns a slice with all indices from 0 to max-1
+// without touching the random number generator.
+// The returned slice is sorted.
+func sampleUniform(random *rand.Rand, num, max int) []int {
+	if num >= max {
+		picks := make([]int, max)
+		for i := 0; i < max; i++ {
+			picks[i] = i
+		}
+		return picks
+	}
+
+	picks := make([]int, num)
+	seen := make(map[int]bool)
+	for i := 0; i < num; i++ {
+		p := random.Intn(max)
+		for seen[p] {
+			p = random.Intn(max)
+		}
+		picks[i] = p
+		seen[p] = true
+	}
+	sort.Ints(picks)
+	return picks
+}
+
+// partition is the block partitioning function from RFC 5053 S.5.3.1.2
+// See http://tools.ietf.org/html/rfc5053
+// Partitions a number i (a size) into j semi-equal pieces. The details are
+// in the return values: there are jl longer pieces of size il, and js shorter
+// pieces of size is.
+func partition(i, j int) (il int, is int, jl int, js int) {
+	il = int(math.Ceil(float64(i) / float64(j)))
+	is = int(math.Floor(float64(i) / float64(j)))
+	jl = i - (is * j)
+	js = j - jl
+
+	if jl == 0 {
+		il = 0
+	}
+	if js == 0 {
+		is = 0
+	}
+
+	return
+}
+
+// factorial calculates the factorial (x!) of the input argument.
+func factorial(x int) int {
+	f := 1
+	for i := 1; i <= x; i++ {
+		f *= i
+	}
+	return f
+}
+
+// centerBinomial calculates choose(x, ceil(x/2)) = x!/(x/2)!(x-(x/2)m!)
+func centerBinomial(x int) int {
+	return choose(x, x/2)
+}
+
+// choose calculates (n k) or n choose k. Tolerant of quite large n/k.
+func choose(n int, k int) int {
+	if k > n/2 {
+		k = n - k
+	}
+	numerator := make([]int, n-k)
+	denominator := make([]int, n-k)
+	for i, j := k+1, 1; i <= n; i, j = i+1, j+1 {
+		numerator[j-1] = int(i)
+		denominator[j-1] = j
+	}
+
+	if len(denominator) > 0 {
+		// find the first member of numerator not in denominator
+		z := sort.SearchInts(numerator, denominator[len(denominator)-1])
+		if z > 0 {
+			numerator = numerator[z+1:]
+			denominator = denominator[0 : len(denominator)-z-1]
+		}
+	}
+
+	for j := len(denominator) - 1; j > 0; j-- {
+		for i := len(numerator) - 1; i >= 0; i-- {
+			if numerator[i]%denominator[j] == 0 {
+				numerator[i] /= denominator[j]
+				denominator[j] = 1
+				break
+			}
+		}
+	}
+	f := 1
+	for _, i := range numerator {
+		f *= i
+	}
+	return f
+}
+
+// bitSet returns true if x has the b'th bit set
+func bitSet(x uint, b uint) bool {
+	return (x>>b)&1 == 1
+}
+
+// bitsSet returns how many bits in x are set.
+// This algorithm basically uses shifts and ANDs to sum up the bits in
+// a tree fashion.
+func bitsSet(x uint64) int {
+	x -= (x >> 1) & 0x5555555555555555
+	x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333)
+	x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f
+	return int((x * 0x0101010101010101) >> 56)
+}
+
+// grayCode calculates the gray code representation of the input argument
+// The Gray code is a binary representation in which successive values differ
+// by exactly one bit. See http://en.wikipedia.org/wiki/Gray_code
+func grayCode(x uint64) uint64 {
+	return (x >> 1) ^ x
+}
+
+// buildGraySequence returns a sequence (in ascending order) of "length" Gray numbers,
+// all of which have exactly "b" bits set.
+func buildGraySequence(length int, b int) []int {
+	s := make([]int, length)
+	i := 0
+	for x := uint64(0); ; x++ {
+		g := grayCode(x)
+		if bitsSet(g) == b {
+			s[i] = int(g)
+			i++
+			if i >= length {
+				break
+			}
+		}
+	}
+	return s
+}
+
+// isPrime tests x for primality. Works on numbers less than the square of
+// the largest smallPrimes array.
+func isPrime(x int) bool {
+	for _, p := range smallPrimes {
+		if p*p > x {
+			return true
+		}
+		if x%p == 0 {
+			return false
+		}
+	}
+	// Well, not really, but we don't know any higher primes for sure.
+	return true
+}
+
+// smallestPrimeGreaterOrEqual returns the smallest prime greater than or equal to x
+// TODO(gbillock): should handle up to 70000 or so
+func smallestPrimeGreaterOrEqual(x int) int {
+	if x <= smallPrimes[len(smallPrimes)-1] {
+		p := sort.Search(len(smallPrimes), func(i int) bool {
+			return smallPrimes[i] >= x
+		})
+		return smallPrimes[p]
+	}
+	for !isPrime(x) {
+		x++
+	}
+	return x
+}
diff --git a/util_test.go b/util_test.go
new file mode 100644
index 0000000..f979d9b
--- /dev/null
+++ b/util_test.go
@@ -0,0 +1,408 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fountain
+
+import (
+	"math"
+	"math/rand"
+	"reflect"
+	"testing"
+)
+
+func almostEqual(a, b float64) bool {
+	return math.Abs(a-b) < 1e-5
+}
+
+func TestSolitonDistribution(t *testing.T) {
+	// Test several randomly chosen values of n.
+	tests := make([]int, 100)
+	for i := range tests {
+		tests[i] = rand.Intn(1e3) + 1 // Add 1 so that n will never be 0.
+	}
+	// Plus a couple of specific ones.
+	tests = append(tests, 1, 10)
+
+	for _, n := range tests {
+		cdf := solitonDistribution(n)
+		if len(cdf) != n+1 {
+			t.Errorf("n=%d: Wrong length CDF: %d", n, len(cdf))
+			t.Log("CDF=", cdf)
+		}
+
+		if !almostEqual(cdf[n], 1) {
+			t.Errorf("n=%d: CDF[max] = %f, should be 1", n, cdf[n])
+			t.Log("CDF=", cdf)
+		}
+
+		if !almostEqual(cdf[0], 0) {
+			t.Errorf("n=%d: CDF[0] = %f, should be 0.0", n, cdf[0])
+			t.Log("CDF=", cdf)
+		}
+
+		if !almostEqual(cdf[1], 1/float64(n)) {
+			t.Errorf("n=%d: CDF[1] = %f, should be 1/n (=%f)", n, cdf[1], 1/float64(n))
+			t.Log("CDF=", cdf)
+		}
+	}
+}
+
+func TestRobustSolitonDistribution(t *testing.T) {
+	cdf := robustSolitonDistribution(10, 8, 0.1)
+	if len(cdf) != 11 {
+		t.Errorf("Wrong length CDF: %d, should be 11", len(cdf))
+		t.Log("CDF=", cdf)
+	}
+
+	if !almostEqual(cdf[0], 0) {
+		t.Errorf("CDF[0] = %f, should be 0.0", cdf[0])
+		t.Log("CDF=", cdf)
+	}
+
+	if !almostEqual(cdf[1], .287474) {
+		t.Errorf("CDF[1] = %f, should be 0.287474", cdf[1])
+		t.Log("CDF=", cdf)
+	}
+
+	if !almostEqual(cdf[len(cdf)-1], 1) {
+		t.Errorf("CDF[max] = %f, should be very nearly 1", cdf[9])
+		t.Log("CDF=", cdf)
+	}
+
+	if (cdf[8]-cdf[7] < cdf[7]-cdf[6]) ||
+		(cdf[8]-cdf[7] < cdf[9]-cdf[8]) {
+		t.Errorf("CDF must have mode at M position(8): %v", cdf)
+		t.Logf("PDF[7, 8, 9] = %v, %v, %v", cdf[7]-cdf[6], cdf[8]-cdf[7], cdf[9]-cdf[8])
+		t.Log("CDF=", cdf)
+	}
+}
+
+func TestOnlineSolitonDistribution(t *testing.T) {
+	cdf := onlineSolitonDistribution(0.1)
+	if len(cdf) != 118 {
+		t.Errorf("Wrong length CDF: %d. Should be 118", len(cdf))
+		t.Log("CDF=", cdf)
+	}
+
+	if (cdf[2] - cdf[1]) < (cdf[1] - cdf[0]) {
+		t.Errorf("CDF should have mode in second position: %v", cdf[0:5])
+		t.Log("CDF=", cdf)
+	}
+
+	if !almostEqual(cdf[0], 0) {
+		t.Errorf("CDF[0] = %f, should be 0.0", cdf[0])
+		t.Log("CDF=", cdf)
+	}
+
+	if !almostEqual(cdf[len(cdf)-1], 1) {
+		t.Errorf("CDF[max] = %f, should be very nearly 1", cdf[len(cdf)-1])
+		t.Log("CDF=", cdf)
+	}
+
+	cdf = onlineSolitonDistribution(0.01)
+	if len(cdf) != 2116 {
+		t.Errorf("Wrong length CDF for 0.0: %d, should be 2116", len(cdf))
+	}
+}
+
+func TestPickDegree(t *testing.T) {
+	cdf := onlineSolitonDistribution(0.25)
+	random := rand.New(rand.NewSource(25))
+	var numLessThanFive int
+	for i := 0; i < 100; i++ {
+		d := pickDegree(random, cdf)
+		if d < 1 || d > len(cdf)-1 {
+			t.Errorf("Degree out of bounds: %d", d)
+		}
+		if d < 5 {
+			numLessThanFive++
+		}
+	}
+	if numLessThanFive < 80 {
+		t.Errorf("Too many large degrees: %d, should be < 80", numLessThanFive)
+	}
+}
+
+func TestSampleUniform(t *testing.T) {
+	random := rand.New(rand.NewSource(256))
+
+	var sampleTests = []struct {
+		num             int
+		length          int
+		expectedSamples []int
+	}{
+		{2, 5, []int{0, 4}},
+		{2, 2, []int{0, 1}},
+		{12, 2, []int{0, 1}},
+	}
+
+	for _, i := range sampleTests {
+		out := sampleUniform(random, i.num, i.length)
+		if !reflect.DeepEqual(out, i.expectedSamples) {
+			t.Errorf("Bad sample. Got %v, want %v", out, i.expectedSamples)
+		}
+	}
+}
+
+func TestPartition(t *testing.T) {
+	var partitionTests = []struct {
+		totalSize                            int
+		numPartitions                        int
+		numLong, numShort, lenLong, lenShort int
+	}{
+		{100, 10, 0, 10, 0, 10},
+		{100, 9, 1, 8, 12, 11},
+		{100, 11, 1, 10, 10, 9},
+	}
+
+	for _, i := range partitionTests {
+		il, is, jl, js := partition(i.totalSize, i.numPartitions)
+		if jl+js != i.numPartitions {
+			t.Errorf("Total blocks = %d, must be %d", il+jl, i.numPartitions)
+		}
+		if il*jl+is*js != i.totalSize {
+			t.Errorf("Total sampled size = %d, must be %d", il*jl+is*js, i.totalSize)
+		}
+		if jl != i.numLong {
+			t.Errorf("Bad number of long blocks. got %d, want %d", jl, i.numLong)
+		}
+		if js != i.numShort {
+			t.Errorf("Bad number of short blocks. got %d, want %d", js, i.numShort)
+		}
+		if il != i.lenLong {
+			t.Errorf("Bad long block length. got %d, want %d", il, i.lenLong)
+		}
+		if is != i.lenShort {
+			t.Errorf("Bad short block length. got %d, want %d", is, i.lenShort)
+		}
+	}
+}
+
+func TestFactorial(t *testing.T) {
+	var factorialTests = []struct {
+		x int
+		f int
+	}{
+		{0, 1},
+		{1, 1},
+		{5, 120},
+		{10, 3628800},
+		{14, 87178291200},
+	}
+
+	for _, test := range factorialTests {
+		if test.f != factorial(test.x) {
+			t.Errorf("%d! = %d, should be %d", test.x, factorial(test.x), test.f)
+		}
+	}
+}
+
+func TestBinomial(t *testing.T) {
+	var binomialTests = []struct {
+		x int
+		b int
+	}{
+		{2, 2},
+		{6, 20},
+		{7, 35},
+		{11, 462},
+		{12, 924},
+	}
+
+	for _, test := range binomialTests {
+		if test.b != centerBinomial(test.x) {
+			t.Errorf("(%d, %d/2) = %d, should be %d", test.x, test.x, centerBinomial(test.x), test.b)
+		}
+	}
+}
+
+func TestChoose(t *testing.T) {
+	var chooseTests = []struct {
+		n    int
+		k    int
+		comb int
+	}{
+		{0, 0, 1},
+		{1, 0, 1},
+		{2, 1, 2},
+		{7, 2, factorial(7) / (factorial(5) * factorial(2))},
+		{5, 3, factorial(5) / (factorial(2) * factorial(3))},
+		{12, 7, factorial(12) / (factorial(5) * factorial(7))},
+		{12, 1, 12},
+		{12, 2, 66},
+		{52, 5, 2598960},
+		{52, 1, 52},
+		{52, 52, 1},
+		{52, 0, 1},
+	}
+	for _, test := range chooseTests {
+		if choose(test.n, test.k) != test.comb {
+			t.Errorf("choose(%d, %d) = %d, should be %d",
+				test.n, test.k, choose(test.n, test.k), test.comb)
+		}
+	}
+}
+
+func TestBitSet(t *testing.T) {
+	var bitTests = []struct {
+		x     uint
+		b     uint
+		equal bool
+	}{
+		{0, 0, false},
+		{0, 1, false},
+		{1, 0, true},
+		{7, 1, true},
+		{16, 3, false},
+		{16, 4, true},
+		{16, 5, false},
+		{0x1000, 12, true},
+		{0x4000, 14, true},
+		{0x4000, 15, false},
+	}
+
+	for _, test := range bitTests {
+		if bitSet(test.x, test.b) != test.equal {
+			t.Errorf("%d bit set in %d = %t, should be %t", test.b, test.x, bitSet(test.x, test.b), test.equal)
+		}
+	}
+}
+
+// simpleBitsSet returns how many bits in x are set.
+func simpleBitsSet(x uint64) int {
+	var count int
+	var i uint64
+	s := uint64(1)
+	for i = 0; i < 64; i++ {
+		if (x & s) != 0 {
+			count++
+		}
+		s <<= 1
+	}
+	return count
+}
+
+func TestBitsSet(t *testing.T) {
+	var bitsSetTests = []struct {
+		x uint64
+		b int
+	}{
+		{0, 0},
+		{1, 1},
+		{2, 1},
+		{4, 1},
+		{6, 2},
+		{7, 3},
+		{11, 3},
+		{12, 2},
+		{1<<63 | 1<<62, 2},
+		{0x66666666, 16},
+		{0x46464646, 4*1 + 4*2},
+		{0xffdd4411, 2*4 + 2*3 + 2*1 + 2*1},
+	}
+
+	for _, test := range bitsSetTests {
+		if test.b != bitsSet(test.x) {
+			t.Errorf("bits set in %d = %d, should be %d", test.x, bitsSet(test.x), test.b)
+		}
+	}
+
+	for i := 0; i < 1000; i++ {
+		x := uint64(rand.Int63())
+		if simpleBitsSet(x) != bitsSet(x) {
+			t.Errorf("bits set in %d = %d, should be %d", x, bitsSet(x), simpleBitsSet(x))
+		}
+	}
+}
+
+func TestGrayCode(t *testing.T) {
+	var grayTests = []struct {
+		x uint64
+		g uint64
+	}{
+		{0, 0},
+		{1, 1},
+		{2, 3},
+		{5, 7},
+		{6, 5},
+		{9, 13},
+	}
+
+	for _, test := range grayTests {
+		if test.g != grayCode(test.x) {
+			t.Errorf("grayCode(%d) = %d, should be %d", test.x, grayCode(test.x), test.g)
+		}
+	}
+}
+
+func TestGraySequence(t *testing.T) {
+	var grayTests = []struct {
+		length int
+		b      int
+		seq    []int
+	}{
+		{4, 3, []int{7, 13, 14, 11}},
+		{6, 2, []int{3, 6, 5, 12, 10, 9}},
+	}
+
+	for _, test := range grayTests {
+		if !reflect.DeepEqual(buildGraySequence(test.length, test.b), test.seq) {
+			t.Errorf("gray sequence for %d = %v, should be %v", test.b, buildGraySequence(test.length, test.b), test.seq)
+		}
+	}
+}
+
+func TestSmallestPrime(t *testing.T) {
+	var primeTests = []struct {
+		x int
+		p int
+	}{
+		{0, 2},
+		{1, 2},
+		{2, 2},
+		{20, 23},
+		{1426, 1427},
+		{1427, 1427},
+		{1427, 1427},
+		{1997, 1997},
+		{1998, 1999},
+		{1999, 1999},
+		{3301, 3301},
+		{8522, 8527},
+	}
+
+	for _, test := range primeTests {
+		if test.p != smallestPrimeGreaterOrEqual(test.x) {
+			t.Errorf("smallestPrimeGreaterOrEqual(%d) = %d, should be %d", test.x, smallestPrimeGreaterOrEqual(test.x), test.p)
+		}
+	}
+}
+
+func TestIsPrime(t *testing.T) {
+	var primeTests = []struct {
+		x     int
+		prime bool
+	}{
+		{2000, false},
+		{2099, true},
+		{2607, false},
+		{9007, true},
+	}
+
+	for _, test := range primeTests {
+		if test.prime != isPrime(test.x) {
+			t.Errorf("isPrime(%d) = %t, should be %t", test.x, isPrime(test.x), test.prime)
+		}
+	}
+}