diff --git a/ComonadSheet.cabal b/ComonadSheet.cabal
new file mode 100644
--- /dev/null
+++ b/ComonadSheet.cabal
@@ -0,0 +1,58 @@
+name:                ComonadSheet
+version:             0.3.0.0
+stability:           experimental
+homepage:            https://github.com/kwf/ComonadSheet
+synopsis:            A library for expressing spreadsheet-like computations as the fixed-points of comonads.
+description:         @ComonadSheet@ is a library for expressing spreadsheet-like computations with absolute and relative references, using fixed-points of n-dimensional comonads. For examples of use, see the <https://github.com/kwf/ComonadSheet GitHub page> for the library.
+license:             BSD3
+license-file:        LICENSE
+author:              Kenneth Foner
+maintainer:          kenneth.foner@gmail.com
+bug-reports:         https://github.com/kwf/ComonadSheet/issues
+copyright:           Copyright: (c) 2013-2014 Kenneth W. Foner
+category:            Control
+build-type:          Simple
+cabal-version:       >=1.10
+
+library
+
+  exposed-modules:
+      Control.Comonad.Sheet.Indexed,
+      Control.Comonad.Sheet.Names,
+      Control.Comonad.Sheet.Reference,
+      Control.Comonad.Sheet.Manipulate,
+      Control.Comonad.Sheet.Examples,
+      Control.Comonad.Sheet
+
+  other-extensions:
+      FlexibleContexts,
+      FlexibleInstances,
+      GADTs,
+      MultiParamTypeClasses,
+      UndecidableInstances,
+      LambdaCase,
+      RankNTypes,
+      StandaloneDeriving,
+      ConstraintKinds,
+      DataKinds,
+      ScopedTypeVariables,
+      TypeFamilies,
+      TypeOperators,
+      PolyKinds,
+      DeriveFunctor,
+      TupleSections
+
+  build-depends:
+      base >=4.7 && < 4.8,
+      comonad >=4.0,
+      distributive >=0.1,
+      transformers >=0.3,
+      applicative-numbers >= 0.1.3,
+      Stream >=0.4,
+      NestedFunctor >= 0.2,
+      PeanoWitnesses >= 0.1,
+      IndexedList >= 0.1,
+      Tape >= 0.4,
+      containers >=0.5
+
+  default-language:    Haskell2010
diff --git a/Control/Comonad/Sheet.hs b/Control/Comonad/Sheet.hs
new file mode 100644
--- /dev/null
+++ b/Control/Comonad/Sheet.hs
@@ -0,0 +1,120 @@
+{- |
+Module      :  Control.Comonad.Sheet
+Description :  A library for expressing "spreadsheet-like" computations with absolute and relative references, using fixed-points of n-dimensional comonads.
+Copyright   :  Copyright (c) 2014 Kenneth Foner
+
+Maintainer  :  kenneth.foner@gmail.com
+Stability   :  experimental
+Portability :  non-portable
+
+@ComonadSheet@ is a library for expressing spreadsheet-like computations with absolute and relative references, using fixed-points of n-dimensional comonads. A sheet is an n-dimensionally nested 'Tape', which is a stream infinite in both left and right directions, with a focus element. For instance, @type Sheet1 a = Nested (Flat Tape) a@, which is isomorphic to @Tape a@. Nested @Tape@s describe multi-dimensional grid-like spaces, which I will refer to, rather leadingly, as /sheets/ made up of /cells/.
+
+While a conventional spreadsheet combines the construction and evaluation of a space of formulae into one process for the user, these steps are distinct in the @ComonadSheet@ library. To create a self-referencing spreadsheet-like computation, first construct a multi-dimensional space of functions which take as input a /space of values/ and return a /single value/. Then, take its fixed point using the @evaluate@ function, resulting in a /space of values/. In other words:
+
+> evaluate :: (ComonadApply w) => w (w a -> a) -> w a
+
+For examples of use, see the <https://github.com/kwf/ComonadSheet GitHub page> for the library.
+
+=Creating Sheets
+
+Usually, the best way to create a sheet is using the 'sheet' function, or using the 'pure' method of the 'Applicative' interface. The @sheet@ function takes a default element value, and a structure containing more values, and inserts those values into a space initially filled with the default value. For instance, @sheet 0 [[1]] :: Sheet2 Int@ makes a two- dimensional sheet which is 0 everywhere except the focus, which is 1. Note that because of overloading on @sheet@'s operands, it is usually necessary to give a type signature somewhere. This is generally not a problem because GHC can almost always infer the type you wanted if you give it so much as a top-level signature.
+
+=References and Manipulation
+
+References to sheets are represented as quasi-heterogeneous lists of absolute and relative references. (In the 'Names' module, I've provided names for referring to dimensions up to 4.) A reference which talks about some dimension /n/ can be used to refer to that same relative or absolute location in any sheet of dimension /n/ or greater.
+
+For instance, @rightBy 5@ is a relative reference in the first dimension. If I let @x = sheet 0 [1..] :: Sheet1 Int@, then @extract (go (rightBy 5) x) == 6@. Notice that I used the 'extract' method from the sheet's 'Comonad' instance to pull out the focus element. Another way to express the same thing would be to say @cell (rightBy 5) x@ -- the @cell@ function is the composition of 'extract' and 'go'. In addition to moving around in sheets, I can use references to slice out pieces of them. For instance, @take (rightBy 5) x == [1,2,3,4,5,6]@. (Note that references used in extracting ranges are treated as inclusive.) I can also use a reference to point in a direction and extract an infinite stream (or stream- of-stream-of- streams...) pointed in that direction. For instance, @view right x == [1..]@.
+
+References can be relative or absolute. An absolute reference can only be used to refer to an `Indexed` sheet, as this is the only kind of sheet with a notion of absolute position.
+
+References can be combined using the @(&)@ operator. For example, @columnAt 5 & aboveBy 10@ represents a reference to a location above the current focus position by 10 cells, and at column 5, regardless of the current column position. Relative references may be combined with one another, and absolute and relative references may be combined, but combining two absolute references is a type error.
+
+=A Simple Example
+
+A one-dimensional sheet which is zero left of the origin and lists the natural numbers right of the origin:
+
+> naturals :: Sheet3 Integer
+> naturals = evaluate $ sheet 0 (repeat (cell left + 1))
+
+> take (rightBy 10) naturals == [1,2,3,4,5,6,7,8,9,10,11]
+
+For more examples, including Pascal's triangle, Fibonacci numbers, and Conway's Game of Life, see the <https://github.com/kwf/ComonadSheet GitHub page> for the library.
+-}
+
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ConstraintKinds  #-}
+{-# LANGUAGE TypeFamilies     #-}
+
+module Control.Comonad.Sheet
+   ( evaluate
+   , cell , cells
+   , sheet , indexedSheet
+     -- Names for the relevant aspects of some smaller dimensions.
+   , module Control.Comonad.Sheet.Names
+     -- Generic functions for manipulating multi-dimensional comonadic spreadsheets.
+   , module Control.Comonad.Sheet.Manipulate
+     -- Adds absolute position to n-dimensional comonadic spreadsheets.
+   , module Control.Comonad.Sheet.Indexed
+     -- Relative and absolute references to locations in arbitrary-dimensional sheets.
+   , module Control.Comonad.Sheet.Reference
+
+     -- The 'Nested' type enables us to abstract over dimensionality. For instance, a 2-dimensional sheet of integers
+     -- is represented by a @Nested (Nest (Flat Tape) Tape) Int@.
+   , module Data.Functor.Nested
+     -- Counted and conic lists, used for representing references.
+   , module Data.List.Indexed
+     -- The 'Tape' is the base type we use to construct sheets. A 'Tape' is a both-ways-infinite stream, like a Turing
+     -- machine's tape.
+   , module Data.Stream.Tape
+     -- Peano numerals linked to type-level indices.
+   , module Data.Numeric.Witness.Peano
+
+     -- Comonads form the basis of sheet evaluation.
+   , module Control.Comonad
+     -- Distributivity enables composition of comonads.
+   , module Data.Distributive
+     -- Numeric instances for functions gives us, for functions @f, g :: Num b => a -> b@, e.g.
+     -- @f + g == \x -> f x + g x@. This enables concise syntax for specifying numeric cells in sheets.
+   , module Data.Numeric.Function
+   ) where
+
+import Control.Comonad.Sheet.Names
+import Control.Comonad.Sheet.Manipulate
+import Control.Comonad.Sheet.Indexed
+import Control.Comonad.Sheet.Reference
+
+import Data.Functor.Nested
+import Data.List.Indexed
+import Data.Stream.Tape
+import Data.Numeric.Witness.Peano
+
+import Control.Comonad
+import Control.Applicative
+import Data.Distributive
+import Data.Traversable
+import Data.Numeric.Function
+import Data.Function
+
+-- | Take a container of functions referencing containers of values and return the fixed-point: a container of values.
+evaluate :: (ComonadApply w) => w (w a -> a) -> w a
+evaluate fs = fix $ (fs <@>) . duplicate
+
+-- | Given a relative or absolute position, extract from a sheet the value at that location.
+cell :: (Comonad w, Go r w) => RefList r -> w a -> a
+cell = (extract .) . go
+
+-- | Given a list of relative or absolute positions, extract from a sheet the values at those locations.
+cells :: (Traversable t, Comonad w, Go r w) => t (RefList r) -> w a -> t a
+cells = traverse cell
+
+-- | Given a default value and an insertable container of values, construct a sheet containing those values.
+sheet :: ( InsertNested l (Nested ts) , Applicative (Nested ts)
+         , DimensionalAs x (Nested ts a) , AsDimensionalAs x (Nested ts a) ~ l a )
+         => a -> x -> Nested ts a
+sheet background functions = insert functions (pure background)
+
+-- | Given an origin index, a default value, and an insertable container of values, construct an indexed sheet.
+indexedSheet :: ( InsertNested l (Nested ts) , Applicative (Nested ts)
+                , DimensionalAs x (Nested ts a) , AsDimensionalAs x (Nested ts a) ~ l a)
+                => Coordinate (NestedCount ts) -> a -> x -> Indexed ts a
+indexedSheet i = (Indexed i .) . sheet
diff --git a/Control/Comonad/Sheet/Examples.hs b/Control/Comonad/Sheet/Examples.hs
new file mode 100644
--- /dev/null
+++ b/Control/Comonad/Sheet/Examples.hs
@@ -0,0 +1,77 @@
+module Control.Comonad.Sheet.Examples where
+
+import Control.Comonad.Sheet
+
+import Control.Applicative ( (<$>), (<*>) )
+import Data.List ( intersperse )
+import Data.Bool ( bool )
+
+import Data.Stream ( Stream , repeat , (<:>) )
+
+import qualified Prelude as P
+import Prelude hiding ( repeat , take )
+
+pascal :: Sheet2 Integer
+pascal = evaluate . sheet 0 $
+  repeat 1 <:> repeat (1 <:> pascalRow)
+  where pascalRow = repeat $ cell above + cell left
+
+diagonalize :: Sheet2 a -> [[a]]
+diagonalize = 
+   zipWith P.take [1..]
+   . map (map extract . P.iterate (go (above & right)))
+   . P.iterate (go below)
+
+fibLike :: Sheet3 Integer
+fibLike = evaluate $ sheet 0 $
+   fibSheetFrom 1 1 <:> repeat (fibSheetFrom (cell inward + 1) (cell inward))
+   where fibSheetFrom a b = (a          <:> b                <:> fibRow) <:>
+                     repeat (cell above <:> (1 + cell above) <:> fibRow)
+         fibRow = repeat $ cell (leftBy 1) + cell (leftBy 2)
+
+data Cell = X | O deriving ( Eq , Show )
+type Universe = Sheet3 Cell
+type Ruleset = ([Int],[Int]) -- list of numbers of neighbors to trigger
+                             -- being born, and staying alive, respectively
+
+life :: Ruleset -> [[Cell]] -> Universe
+life ruleset seed = evaluate $ insert [map (map const) seed] blank
+   where blank = sheet (const X) (repeat . tapeOf . tapeOf $ rule)
+         rule place  = case (neighbors place `elem`) `onBoth` ruleset of
+                            (True,_)  -> O
+                            (_,True)  -> cell inward place
+                            _         -> X
+         neighbors   = length . filter (O ==) . cells bordering
+         bordering   = map (inward &) (diagonals ++ verticals ++ horizontals)
+         diagonals   = (&) <$> horizontals <*> verticals
+         verticals   =        [above, below]
+         horizontals = map d2 [right, left]
+
+onBoth :: (a -> b) -> (a,a) -> (b,b)
+f `onBoth` (x,y) = (f x,f y)
+
+conway :: [[Cell]] -> Universe
+conway = life ([3],[2,3])
+
+printLife :: Int -> Int -> Int -> Universe -> IO ()
+printLife c r t = mapM_ putStr
+   .            ([separator '┌' '─' '┐'] ++)
+   .         (++ [separator '└' '─' '┘']) 
+   . intersperse (separator '├' '─' '┤')
+   . map (unlines . map (("│ " ++) . (++ " │")) . frame)
+   . take (rightBy c & belowBy r & outwardBy t)
+   where
+      separator x y z = [x] ++ P.replicate (1 + (1 + c) * 2) y ++ [z] ++ "\n"
+      frame = map $ intersperse ' ' . map (bool ' ' '●' . (O ==))
+
+glider :: Universe
+glider = conway [[X,X,O],
+                 [O,X,O],
+                 [X,O,O]]
+
+spaceship :: Universe
+spaceship = conway [[X,X,X,X,X],
+                    [X,O,O,O,O],
+                    [O,X,X,X,O],
+                    [X,X,X,X,O],
+                    [O,X,X,O,X]]
diff --git a/Control/Comonad/Sheet/Indexed.hs b/Control/Comonad/Sheet/Indexed.hs
new file mode 100644
--- /dev/null
+++ b/Control/Comonad/Sheet/Indexed.hs
@@ -0,0 +1,87 @@
+{- |
+Module      :  Control.Comonad.Sheet.Indexed
+Description :  Adds absolute position to n-dimensional comonadic spreadsheets.
+Copyright   :  Copyright (c) 2014 Kenneth Foner
+
+Maintainer  :  kenneth.foner@gmail.com
+Stability   :  experimental
+Portability :  non-portable
+
+This module defines the @Indexed@ type, which bolts an absolute coordinate onto a normal nested structure, allowing
+you to talk about absolute position as well as relative position.
+-}
+
+{-# LANGUAGE ConstraintKinds       #-}
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE PolyKinds             #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE UndecidableInstances  #-}
+
+module Control.Comonad.Sheet.Indexed where
+
+import Control.Comonad
+import Control.Applicative
+import Data.Functor.Identity
+import Data.Functor.Compose
+
+import Data.Numeric.Witness.Peano
+import Data.Stream.Tape
+import Control.Comonad.Sheet.Reference
+import Data.Functor.Nested
+import Data.List.Indexed
+
+-- | An n-dimensional coordinate is a list of length n of absolute references.
+type Coordinate n = CountedList n (Ref Absolute)
+
+-- | An indexed sheet is an n-dimensionally nested 'Tape' paired with an n-dimensional coordinate which
+--   represents the absolute position of the current focus in the sheet.
+data Indexed ts a =
+  Indexed { index     :: Coordinate (NestedCount ts)
+          , unindexed :: Nested ts a }
+
+instance (Functor (Nested ts)) => Functor (Indexed ts) where
+   fmap f (Indexed i t) = Indexed i (fmap f t)
+
+-- | For a sheet to be Indexable, it needs to consist of n-dimensionally nested 'Tape's, such that we can take the
+--   cross product of all n tapes to generate a tape of indices.
+type Indexable ts = ( Cross (NestedCount ts) Tape , ts ~ NestedNTimes (NestedCount ts) Tape )
+
+instance (ComonadApply (Nested ts), Indexable ts) => Comonad (Indexed ts) where
+   extract      = extract . unindexed
+   duplicate it = Indexed (index it) $
+                     Indexed <$> indices (index it)
+                             <@> duplicate (unindexed it)
+
+instance (ComonadApply (Nested ts), Indexable ts) => ComonadApply (Indexed ts) where
+   (Indexed i fs) <@> (Indexed _ xs) = Indexed i (fs <@> xs)
+
+-- | Takes an n-coordinate and generates an n-dimensional enumerated space of coordinates.
+indices :: (Cross n Tape) => Coordinate n -> Nested (NestedNTimes n Tape) (Coordinate n)
+indices = cross . fmap enumerate
+
+-- | The cross product of an n-length counted list of @(t a)@ is an n-nested @t@ of counted lists of @a@.
+class Cross n t where
+   cross :: CountedList n (t a) -> Nested (NestedNTimes n t) (CountedList n a)
+
+instance (Functor t) => Cross (Succ Zero) t where
+   cross (t ::: _) =
+      Flat $ (::: CountedNil) <$> t
+
+instance ( Cross (Succ n) t , Functor t
+         , Functor (Nested (NestedNTimes (Succ n) t)) )
+         => Cross (Succ (Succ n)) t where
+   cross (t ::: ts) =
+      Nest $ (\xs -> (::: xs) <$> t) <$> cross ts
+
+-- | Counts how deeply a 'Nested' thing is nested.
+type family NestedCount x where
+   NestedCount (Flat f)   = Succ Zero
+   NestedCount (Nest f g) = Succ (NestedCount f)
+
+-- | Computes the type of an n-deep nested structure (similar to replicate for 'Nested').
+type family NestedNTimes n f where
+   NestedNTimes (Succ Zero) f = Flat f
+   NestedNTimes (Succ n)    f = Nest (NestedNTimes n f) f
diff --git a/Control/Comonad/Sheet/Manipulate.hs b/Control/Comonad/Sheet/Manipulate.hs
new file mode 100644
--- /dev/null
+++ b/Control/Comonad/Sheet/Manipulate.hs
@@ -0,0 +1,234 @@
+{- |
+Module      :  Control.Comonad.Sheet.Manipulate
+Description :  Generic functions for manipulating multi-dimensional comonadic spreadsheets.
+Copyright   :  Copyright (c) 2014 Kenneth Foner
+
+Maintainer  :  kenneth.foner@gmail.com
+Stability   :  experimental
+Portability :  non-portable
+
+This module defines the 'take', 'view', 'go', and 'insert' functions generically for any dimensionality of sheet. These
+constitute the preferred way of manipulating sheets, providing an interface to: take finite slices ('take'), infinite
+slices ('view'), move to locations ('go'), and insert finite or infinite structures ('insert').
+-}
+
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE UndecidableInstances  #-}
+
+module Control.Comonad.Sheet.Manipulate where
+
+import Data.Stream.Tape
+import Control.Comonad.Sheet.Indexed
+import Data.Numeric.Witness.Peano
+import Data.Functor.Nested
+import Control.Comonad.Sheet.Reference
+import Data.List.Indexed hiding ( replicate )
+
+import Data.Stream ( Stream(..) , (<:>) )
+import qualified Data.Stream as S
+
+import Control.Applicative
+import Prelude hiding ( take )
+
+class Take r t where
+   -- | The type of an n-dimensional list extracted from an n-dimensional sheet. For instance:
+   --
+   -- > ListFrom Sheet2 a == [[a]]
+   type ListFrom t a
+   -- | Given a 'RefList' and an n-dimensional sheet, return an n-dimensional list corresponding to taking items from
+   --   the space until reaching the (relative or absolute) coordinates specified.
+   take :: RefList r -> t a -> ListFrom t a
+
+class View r t where
+   -- | The type of an n-dimensional stream extracted from an n-dimensional sheet. For instance:
+   --
+   -- > StreamFrom Sheet2 a == Stream (Stream a)
+   type StreamFrom t a
+   -- | Given a 'RefList' and an n-dimensional sheet, return an n-dimensional stream corresponding to the "view" in the
+   --   direction specified by the sign of each of the coordinates. The direction implied by an absolute coordinate is
+   --   the direction from the current focus to that location.
+   view :: RefList r -> t a -> StreamFrom t a
+
+class Go r t where
+   -- | Given a 'RefList' and an n-dimensional sheet, move to the location specified by the @RefList@ given.
+   go :: RefList r -> t a -> t a
+
+-- | Combination of 'go' and 'take': moves to the location specified by the first argument, then takes the amount
+--   specified by the second argument.
+slice :: (Take r' t, Go r t) => RefList r -> RefList r' -> t a -> ListFrom t a
+slice r r' = take r' . go r
+
+-- | Use this to insert a (possibly nested) list-like structure into a (possibly many-dimensional) sheet.
+--   Note that the depth of nesting of the structure being inserted must match the number of dimensions of the sheet
+--   into which it is being inserted. Note also that the structure being inserted need not be a @Nested@ type; it
+--   need only have enough levels of structure (i.e. number of nested lists) to match the dimensionality of the sheet.
+insert :: (DimensionalAs x (t a), InsertNested l t, AsDimensionalAs x (t a) ~ l a) => x -> t a -> t a
+insert l t = insertNested (l `asDimensionalAs` t) t
+
+-- | Take (n + 1) things from a 'Tape', either in the rightward or leftward directions, depending on the sign of the
+--   reference given. If the reference is @(Rel 0)@, return the empty list.
+tapeTake :: Ref Relative -> Tape a -> [a]
+tapeTake (Rel r) t | r > 0 = focus t : S.take      r  (viewR t)
+tapeTake (Rel r) t | r < 0 = focus t : S.take (abs r) (viewL t)
+tapeTake _ _ = []
+
+instance Take Nil (Nested (Flat Tape)) where
+   type ListFrom (Nested (Flat Tape)) a = [a]
+   take _ _ = []
+
+instance (Take Nil (Nested ts), Functor (Nested ts)) => Take Nil (Nested (Nest ts Tape)) where
+   type ListFrom (Nested (Nest ts Tape)) a = ListFrom (Nested ts) [a]
+   take _ = take (Rel 0 :-: ConicNil)
+
+instance Take (Relative :-: Nil) (Nested (Flat Tape)) where
+   type ListFrom (Nested (Flat Tape)) a = [a]
+   take (r :-: _) (Flat t) = tapeTake r t
+
+instance ( Functor (Nested ts), Take rs (Nested ts) )
+         => Take (Relative :-: rs) (Nested (Nest ts Tape)) where
+   type ListFrom (Nested (Nest ts Tape)) a = ListFrom (Nested ts) [a]
+   take (r :-: rs) (Nest t) = take rs . fmap (tapeTake r) $ t
+
+instance ( Take (Replicate (NestedCount ts) Relative) (Nested ts)
+         , Length r <= NestedCount ts
+         , ((NestedCount ts - Length r) + Length r) ~ NestedCount ts
+         ) => Take r (Indexed ts) where
+   type ListFrom (Indexed ts) a = ListFrom (Nested ts) a
+   take r (Indexed i t) = take (heterogenize id (getMovement r i)) t
+
+-- | Given a relative reference, either return the rightward-pointing stream or the leftward one, depending on the
+--   sign of the reference. @(Rel 0)@ defaults to rightward.
+tapeView :: Ref Relative -> Tape a -> Stream a
+tapeView (Rel r) t | r >= 0    = focus t <:> viewR t
+tapeView (Rel r) t | otherwise = focus t <:> viewL t
+
+instance View Nil (Nested (Flat Tape)) where
+   type StreamFrom (Nested (Flat Tape)) a = Stream a
+   view _ (Flat t) = tapeView (Rel 0) t
+
+instance (View Nil (Nested ts), Functor (Nested ts)) => View Nil (Nested (Nest ts Tape)) where
+   type StreamFrom (Nested (Nest ts Tape)) a = StreamFrom (Nested ts) (Stream a)
+   view _ = view (Rel 0 :-: ConicNil)
+
+instance View (Relative :-: Nil) (Nested (Flat Tape)) where
+   type StreamFrom (Nested (Flat Tape)) a = (Stream a)
+   view (r :-: _) (Flat t) = tapeView r t
+
+instance ( Functor (Nested ts), View rs (Nested ts) )
+         => View (Relative :-: rs) (Nested (Nest ts Tape)) where
+   type StreamFrom (Nested (Nest ts Tape)) a = StreamFrom (Nested ts) (Stream a)
+   view (r :-: rs) (Nest t) = view rs . fmap (tapeView r) $ t
+
+instance ( View (Replicate (NestedCount ts) Relative) (Nested ts)
+         , Length r <= NestedCount ts
+         , ((NestedCount ts - Length r) + Length r) ~ NestedCount ts
+         ) => View r (Indexed ts) where
+   type StreamFrom (Indexed ts) a = StreamFrom (Nested ts) a
+   view r (Indexed i t) = view (heterogenize id (getMovement r i)) t
+
+-- | Given a relative reference, move that much in a 'Tape', either rightward or leftward depending on sign.
+tapeGo :: Ref Relative -> Tape a -> Tape a
+tapeGo (Rel r) = fpow (abs r) (if r > 0 then moveR else moveL)
+   where fpow n = foldr (.) id . replicate n -- iterate a function n times
+
+instance Go (Relative :-: Nil) (Nested (Flat Tape)) where
+   go (r :-: _) (Flat t) = Flat $ tapeGo r t
+
+instance Go Nil (Nested ts) where go _ = id
+
+instance (Go rs (Nested ts), Functor (Nested ts)) => Go (Relative :-: rs) (Nested (Nest ts Tape)) where
+   go (r :-: rs) (Nest t) = Nest . go rs . fmap (tapeGo r) $ t
+
+instance ( Go (Replicate (NestedCount ts) Relative) (Nested ts)
+         , Length r <= NestedCount ts
+         , ((NestedCount ts - Length r) + Length r) ~ NestedCount ts
+         , ReifyNatural (NestedCount ts) )
+         => Go r (Indexed ts) where
+   go r (Indexed i t) =
+      let move = getMovement r i
+      in  Indexed (merge move i) (go (heterogenize id move) t)
+
+-- | A @(Signed f a)@ is an @(f a)@ annotated with a sign: either @Positive@ or @Negative@. This is a useful type for
+--   specifying the directionality of insertions into sheets. By wrapping a list or stream in a @Negative@ and then
+--   inserting it into a sheet, you insert it in the opposite direction to the usual one: leftward, upward, inward...
+data Signed f a = Positive (f a)
+                | Negative (f a)
+                deriving ( Eq , Ord , Show )
+
+-- | In order to insert an n-dimensional list-like structure @(l a)@ into an n-dimensional @Tape@, it's only necessary
+--   to define how to insert a 1-dimensional @(l a)@ into a 1-dimensional @Tape@. Add instances of this class if you
+--   want to be able to insert custom types into a sheet.
+class InsertBase l where
+   insertBase :: l a -> Tape a -> Tape a
+
+-- | Inserting a @Tape@ into another @Tape@ replaces the latter with the former completely.
+instance InsertBase Tape where
+   insertBase t _ = t
+
+-- | Inserting a @Stream@ into a @Tape@ replaces the focus and right side of the @Tape@ with the contents of the stream.
+instance InsertBase Stream where
+   insertBase (Cons x xs) (Tape ls _ _) = Tape ls x xs
+
+-- | Inserting a @Signed Stream@ into a @Tape@ either behaves just like inserting a regular @Stream@, or (in the @Negative@ case) inserts the stream to the left.
+instance InsertBase (Signed Stream) where
+   insertBase (Positive (Cons x xs)) (Tape ls _ _) = Tape ls x xs
+   insertBase (Negative (Cons x xs)) (Tape _ _ rs) = Tape xs x rs
+
+-- | Inserting a list into a @Tape@ prepends the contents of the list rightwards in the @Tape@, pushing the old focus
+--   element rightward (i.e. the head of the list becomes the new focus).
+instance InsertBase [] where
+   insertBase [] t = t
+   insertBase (x : xs) (Tape ls c rs) =
+      Tape ls x (S.prefix xs (Cons c rs))
+
+-- | Inserting a @Signed []@ into a @Tape@ either behaves just like inserting a regular list, or (in the @Negative@ case) inserts the list to the left.
+instance InsertBase (Signed []) where
+   insertBase (Positive []) t = t
+   insertBase (Negative []) t = t
+   insertBase (Positive (x : xs)) (Tape ls c rs) =
+      Tape ls x (S.prefix xs (Cons c rs))
+   insertBase (Negative (x : xs)) (Tape ls c rs) =
+      Tape (S.prefix xs (Cons c ls)) x rs
+
+-- | This typeclass is the inductive definition for inserting things into higher-dimensional spaces. To make new types
+--   insertable, add instances of 'InsertBase', not @InsertNested@.
+class InsertNested l t where
+   insertNested :: l a -> t a -> t a
+
+instance (InsertBase l) => InsertNested (Nested (Flat l)) (Nested (Flat Tape)) where
+   insertNested (Flat l) (Flat t) = Flat $ insertBase l t
+
+instance ( InsertBase l , InsertNested (Nested ls) (Nested ts)
+         , Functor (Nested ls) , Applicative (Nested ts) )
+         => InsertNested (Nested (Nest ls l)) (Nested (Nest ts Tape)) where
+   insertNested (Nest l) (Nest t) =
+      Nest $ insertNested (insertBase <$> l) (pure id) <*> t
+
+instance (InsertNested l (Nested ts)) => InsertNested l (Indexed ts) where
+   insertNested l (Indexed i t) = Indexed i (insertNested l t)
+
+-- | @DimensionalAs@ provides a mechanism to "lift" an n-deep nested structure into an explicit @Nested@ type. This
+--   is the way in which raw lists-of-lists-of-lists, etc. can be inserted (without manual annotation of nesting depth)
+--   into a sheet.
+class DimensionalAs x y where
+   type AsDimensionalAs x y
+   -- | @x `asDimensionalAs` y@ applies the appropriate constructors for 'Nested' to @x@ a number of times equal to
+   --   the number of dimensions of @y@. For instance:
+   --
+   --   > [['x']] `asDimensionalAs` Nest (Flat [['y']]) == Nest (Flat [['x']])
+   asDimensionalAs :: x -> y -> x `AsDimensionalAs` y
+
+-- | In the case of a @Nested@ structure, @asDimensionalAs@ defaults to @asNestedAs@.
+instance (NestedAs x (Nested ts y), AsDimensionalAs x (Nested ts y) ~ AsNestedAs x (Nested ts y)) => DimensionalAs x (Nested ts y) where
+   type x `AsDimensionalAs` (Nested ts a) = x `AsNestedAs` (Nested ts a)
+   asDimensionalAs = asNestedAs
+
+-- | @DimensionalAs@ also knows the dimensionality of an 'Indexed' sheet as well as regular @Nested@ structures.
+instance (NestedAs x (Nested ts y)) => DimensionalAs x (Indexed ts y) where
+   type x `AsDimensionalAs` (Indexed ts a) = x `AsNestedAs` (Nested ts a)
+   x `asDimensionalAs` (Indexed i t)       = x `asNestedAs` t
diff --git a/Control/Comonad/Sheet/Names.hs b/Control/Comonad/Sheet/Names.hs
new file mode 100644
--- /dev/null
+++ b/Control/Comonad/Sheet/Names.hs
@@ -0,0 +1,212 @@
+{- |
+Module      :  Control.Comonad.Sheet.Names
+Description :  Names for the relevant aspects of some smaller dimensions (currently up to 4).
+Copyright   :  Copyright (c) 2014 Kenneth Foner
+
+Maintainer  :  kenneth.foner@gmail.com
+Stability   :  experimental
+Portability :  non-portable
+
+This module defines names to be used manipulating n-dimensional sheets. Currently, names are defined for dimensions
+of 4 and fewer. Below is a summary of the names currently defined in this module. Template Haskell to define these names for new dimension numbers is coming soon!
+
+=Dimension 1:
+
+   * 'Sheet1' is the type of a 1-dimensional sheet
+
+   * 'left' (negative) and 'right' (positive) are directions
+
+   * 'leftBy' and 'rightBy' define relative position by some integer argument
+
+   * 'columnAt' defines absolute position at a given column
+
+   * 'column' retrieves the current column index
+
+   * 'd1' coerces a 1-or-fewer-dimensional reference to a 1-dimensional reference
+
+=Dimension 2:
+   
+   * 'Sheet2' is the type of a 2-dimensional sheet
+
+   * 'above' (negative) and 'below' (positive) are directions
+
+   * 'aboveBy' and 'belowBy' define relative position by some integer argument
+
+   * 'rowAt' defines absolute position at a given row
+
+   * 'row' retrieves the current row index
+
+   * 'd2' coerces a 2-or-fewer-dimensional reference to a 2-dimensional reference
+
+=Dimension 3:
+
+   * 'Sheet3' is the type of a 3-dimensional sheet
+
+   * 'inward' (negative) and 'outward' (positive) are directions
+
+   * 'inwardBy' and 'outwardBy' define relative position by some integer argument
+
+   * 'levelAt' defines absolute position at a given level
+
+   * 'level' retrieves the current level index
+
+   * 'd3' coerces a 3-or-fewer-dimensional reference to a 3-dimensional reference
+
+=Dimension 4:
+
+   * 'Sheet4' is the type of a 4-dimensional sheet
+
+   * 'ana' (negative) and 'kata' (positive) are directions
+
+   * 'anaBy' and 'kataBy' define relative position by some integer argument
+
+   * 'spaceAt' defines absolute position at a given space
+
+   * 'space' retrieves the current space index
+
+   * 'd4' coerces a 4-or-fewer-dimensional reference to a 4-dimensional reference
+
+-}
+
+{-# LANGUAGE DataKinds        #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies     #-}
+{-# LANGUAGE TypeOperators    #-}
+
+module Control.Comonad.Sheet.Names
+   ( Sheet1 , here1 , d1 , columnAt , column , rightBy , leftBy , right , left
+   , Sheet2 , here2 , d2 , rowAt , row , aboveBy , belowBy , above , below
+   , Sheet3 , here3 , d3 , levelAt , level , inwardBy , outwardBy , inward , outward
+   , Sheet4 , here4 , d4 , spaceAt , space , anaBy , kataBy , ana , kata
+   ) where
+
+import Control.Comonad.Sheet.Reference
+import Data.Numeric.Witness.Peano
+import Data.Stream.Tape
+import Control.Comonad.Sheet.Indexed
+import Data.Functor.Nested
+import Data.List.Indexed
+
+-- One dimension...
+
+type Rel1 = Relative :-: Nil
+type Nat1 = Succ Zero
+
+nat1 :: Natural Nat1
+nat1 = reifyNatural
+
+type Sheet1  = Nested  (NestedNTimes Nat1 Tape)
+type ISheet1 = Indexed (NestedNTimes Nat1 Tape)
+
+here1 :: RefList Rel1
+here1 = Rel 0 :-: ConicNil
+
+d1 :: (CombineRefLists Rel1 x) => RefList x -> RefList (Rel1 & x)
+d1 = (here1 &)
+
+columnAt :: Int -> RefList (Absolute :-: Nil)
+columnAt = dimensional nat1 . Abs
+
+column :: (Zero < NestedCount ts) => Indexed ts x -> Int
+column = getRef . nth Zero . index
+
+rightBy, leftBy :: Int -> RefList Rel1
+rightBy = dimensional nat1 . Rel
+leftBy = rightBy . negate
+
+right, left :: RefList Rel1
+right = rightBy 1
+left  = leftBy  1
+
+-- Two dimensions...
+
+type Rel2 = Relative :-: Rel1
+type Nat2 = Succ Nat1
+
+nat2 :: Natural Nat2
+nat2 = reifyNatural
+
+type Sheet2  = Nested  (NestedNTimes Nat2 Tape)
+type ISheet2 = Indexed (NestedNTimes Nat2 Tape)
+
+here2 :: RefList Rel2
+here2 = Rel 0 :-: here1
+
+d2 :: (CombineRefLists Rel2 x) => RefList x -> RefList (Rel2 & x)
+d2 = (here2 &)
+
+rowAt :: Int -> RefList (Tack Absolute Rel1)
+rowAt = dimensional nat2 . Abs
+
+row :: (Nat1 < NestedCount ts) => Indexed ts x -> Int
+row = getRef . nth nat1 . index
+
+belowBy, aboveBy :: Int -> RefList Rel2
+belowBy = dimensional nat2 . Rel
+aboveBy = belowBy . negate
+
+below, above :: RefList Rel2
+below = belowBy 1
+above = aboveBy 1
+
+-- Three dimensions...
+
+type Rel3 = Relative :-: Rel2
+type Nat3 = Succ Nat2
+
+nat3 :: Natural Nat3
+nat3 = reifyNatural
+
+type Sheet3  = Nested  (NestedNTimes Nat3 Tape)
+type ISheet3 = Indexed (NestedNTimes Nat3 Tape)
+
+here3 :: RefList Rel3
+here3 = Rel 0 :-: here2
+
+d3 :: (CombineRefLists Rel3 x) => RefList x -> RefList (Rel3 & x)
+d3 = (here3 &)
+
+levelAt :: Int -> RefList (Tack Absolute Rel2)
+levelAt = dimensional nat3 . Abs
+
+level :: (Nat2 < NestedCount ts) => Indexed ts x -> Int
+level = getRef . nth nat2 . index
+
+outwardBy, inwardBy :: Int -> RefList Rel3
+outwardBy = dimensional nat3 . Rel
+inwardBy  = outwardBy . negate
+
+outward, inward :: RefList Rel3
+outward = outwardBy 1
+inward  = inwardBy  1
+
+-- Four dimensions...
+
+type Rel4 = Relative :-: Rel3
+type Nat4 = Succ Nat3
+
+nat4 :: Natural Nat4
+nat4 = reifyNatural
+
+type Sheet4  = Nested  (NestedNTimes Nat4 Tape)
+type ISheet4 = Indexed (NestedNTimes Nat4 Tape)
+
+here4 :: RefList Rel4
+here4 = Rel 0 :-: here3
+
+d4 :: (CombineRefLists Rel4 x) => RefList x -> RefList (Rel4 & x)
+d4 = (here4 &)
+
+spaceAt :: Int -> RefList (Tack Absolute Rel3)
+spaceAt = dimensional nat4 . Abs
+
+space :: (Nat3 < NestedCount ts) => Indexed ts x -> Int
+space = getRef . nth nat3 . index
+
+anaBy, kataBy :: Int -> RefList Rel4
+anaBy  = dimensional nat4 . Rel
+kataBy = anaBy . negate
+
+ana, kata :: RefList Rel4
+ana  = anaBy  1
+kata = kataBy 1
diff --git a/Control/Comonad/Sheet/Reference.hs b/Control/Comonad/Sheet/Reference.hs
new file mode 100644
--- /dev/null
+++ b/Control/Comonad/Sheet/Reference.hs
@@ -0,0 +1,139 @@
+{- |
+Module      :  Control.Comonad.Sheet.Reference
+Description :  Relative and absolute references to locations in arbitrary-dimensional sheets.
+Copyright   :  Copyright (c) 2014 Kenneth Foner
+
+Maintainer  :  kenneth.foner@gmail.com
+Stability   :  experimental
+Portability :  non-portable
+
+This module defines the types needed to construct and manipulate references to locations in n-dimensional sheets.
+All sheets support relative references, but only 'Indexed' sheets support absolute references; the type system prevents
+the use of absolute references for sheets without an index.
+-}
+
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE PolyKinds             #-}
+{-# LANGUAGE RankNTypes            #-}
+{-# LANGUAGE StandaloneDeriving    #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+
+module Control.Comonad.Sheet.Reference where
+
+import Data.Numeric.Witness.Peano
+import Data.List.Indexed
+
+import Control.Applicative
+import Data.List ( intercalate )
+
+import Prelude hiding ( replicate , length )
+
+-- | A 'Ref' is either absolute or relative. This type exists solely to be lifted to the type/kind level.
+data RefType = Relative | Absolute
+
+-- | A @Ref@ is either absolute or relative. Either holds an @Int@, but their semantics differ.
+data Ref (t :: RefType) where
+   Rel :: Int -> Ref Relative
+   Abs :: Int -> Ref Absolute
+deriving instance Show (Ref t)
+
+-- | Extract the raw @Int@ from a @Ref@. Use this sparingly, as every time you do, you risk accidentally converting
+--   to the other variety of @Ref@ if you're not careful.
+getRef :: Ref t -> Int
+getRef (Abs x) = x
+getRef (Rel x) = x
+
+instance Enum (Ref Relative) where
+   fromEnum (Rel r) = r
+   toEnum = Rel
+
+instance Enum (Ref Absolute) where
+   fromEnum (Abs r) = r
+   toEnum = Abs
+
+-- | Two absolute references cannot be combined into a single reference, but relative and absolute references can be
+--   combined to form an absolute reference, and two relative references can be combined into one relative reference.
+type family Combine a b where
+   Combine Relative Absolute = Absolute
+   Combine Absolute Relative = Absolute
+   Combine Relative Relative = Relative
+
+-- | Combine @Ref@s which can be meaningfully combined. Note the absence of the absolute-absolute case; there's no
+--   good meaning for combining two absolute references, so it is statically prohibited.
+class CombineRefs a b where
+   combine :: Ref a -> Ref b -> Ref (Combine a b)
+instance CombineRefs Absolute Relative where
+   combine (Abs a) (Rel b) = Abs (a + b)
+instance CombineRefs Relative Absolute where
+   combine (Rel a) (Abs b) = Abs (a + b)
+instance CombineRefs Relative Relative where
+   combine (Rel a) (Rel b) = Rel (a + b)
+
+-- | A @RefList@ is a list of @Ref@s, each of which may be individually relative or absolute.
+type RefList = ConicList Ref
+
+infixr 4 &
+type family a & b where
+   (a :-: as) & (b :-: bs) = Combine a b :-: (as & bs)
+   Nil        & bs         = bs
+   as         & Nil        = as
+
+-- | We can combine lists of references if their corresponding elements can be combined. When combining two lists of
+--   references, any trailing elements from the longer list will be preserved at the end; this is /unlike/ the behavior
+--   of, e.g., @zip@.
+class CombineRefLists as bs where
+   (&) :: RefList as -> RefList bs -> RefList (as & bs)
+instance (CombineRefs a b, CombineRefLists as bs)
+      => CombineRefLists (a :-: as) (b :-: bs) where (a :-: as) & (b :-: bs) = combine a b :-: (as & bs)
+instance CombineRefLists Nil        (b :-: bs) where ConicNil & bs           = bs
+instance CombineRefLists (a :-: as) Nil        where as       & ConicNil     = as
+instance CombineRefLists Nil        Nil        where ConicNil & ConicNil     = ConicNil
+
+-- | Given a homogeneous list of length n containing relative references, we can merge those relative positions with a
+--   homogeneous list of absolute references. This yields another list of absolute references.
+merge :: (ReifyNatural n)
+      => CountedList n (Ref Relative)
+      -> CountedList n (Ref Absolute)
+      -> CountedList n (Ref Absolute)
+merge rs as = (\(Rel r) (Abs a) -> Abs (r + a)) <$> rs <*> as
+
+-- | Finds the relative movement necessary to move from a given absolute coordinate to the location specified by a
+--   list of relative and absolute coordinates.
+diff :: CountedList n (Either (Ref Relative) (Ref Absolute))
+     -> CountedList n (Ref Absolute)
+     -> CountedList n (Ref Relative)
+diff (Left  (Rel r) ::: rs) (Abs i ::: is) = Rel  r      ::: diff rs is
+diff (Right (Abs r) ::: rs) (Abs i ::: is) = Rel (r - i) ::: diff rs is
+diff CountedNil _  = CountedNil
+diff _  CountedNil = CountedNil
+
+-- | Given a list of relative and absolute references (an n-dimensional reference) and an n-dimensional coordinate,
+--   we can obtain the relative movement necessary to get from the coordinate to the location specified by the 
+--   references given.
+getMovement :: (Length ts <= n, ((n - Length ts) + Length ts) ~ n)
+            => RefList ts -> CountedList n (Ref Absolute) -> CountedList n (Ref Relative)
+getMovement refs coords =
+   padTo (count coords) (Left (Rel 0)) (homogenize eitherFromRef refs) `diff` coords
+
+-- | Given a @Ref@, forget the type-level information about whether it's absolute or relative by casting it into an
+--   @Either@ type where the @Left@ branch holds a relative reference or the @Right@ holds an absolute one.
+eitherFromRef :: Ref t -> Either (Ref Relative) (Ref Absolute)
+eitherFromRef (Rel r) = Left  (Rel r)
+eitherFromRef (Abs a) = Right (Abs a)
+
+-- | Given a number /n/ greater than zero and some reference, prepend (n - 1) relative references of value zero to the
+--   reference given, thus creating an n-dimensional reference where the original reference refers to the nth dimension.
+dimensional :: Natural (Succ n) -> Ref t -> RefList (Tack t (Replicate n Relative))
+dimensional (Succ n) a = tack a (heterogenize id (replicate n (Rel 0)))
+
+instance Show (RefList ts) where
+   showsPrec p xs = showParen (p > 10) $
+      (showString $ ( intercalate " :-: "
+                    $ map (either show show)
+                    $ unCount
+                    $ homogenize eitherFromRef xs ) ++ " :-: ConicNil")
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2014, Kenneth Foner
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Kenneth Foner nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
