-----------------------------------------------------------------------------
-- |
-- Module      :  Codec.Gray
-- Copyright   :  (c) Amy de Buitléir 2011-2012
-- License     :  BSD-style
-- Maintainer  :  amy@nualeargais.ie
-- Stability   :  experimental
-- Portability :  portable
--
-- Gray encoding schemes. A Gray code is a list of values such that two
-- successive values differ in only one digit. Usually the term /Gray code/
-- refers to the Binary Reflected Gray code (BRGC), but non-binary Gray codes
-- have also been discovered. Some Gray codes are also /cyclic/: the last and
-- first values differ in only one digit.
--
-----------------------------------------------------------------------------
{-# LANGUAGE UnicodeSyntax #-}

module Codec.Gray 
  (
    grayCodes,
    naryGrayCodes
  ) where

import Data.List (foldl')

-- | @'grayCodes' k@ generates the list of Binary Reflected Gray Code (BRGC)
--   numbers of length k. This code is cyclic.
grayCodes  Int  [[Bool]]
grayCodes 0 = [[]]
grayCodes k = 
  let xs = grayCodes (k-1) in map (False:) xs ++ map (True:) (reverse xs)

-- | @'naryGrayCodes' xs k@ generates a non-Boolean (or n-ary) Gray code of
--   length @k@ using the elements of @x@ as "digits". This code is cyclic.
--
--   Ex: @'naryGrayCodes' "012" 4@ generates a ternary Gray code that is 
--   four digits long.
naryGrayCodes  [a]  Int  [[a]]
naryGrayCodes xs 1 = map (\x  [x]) xs
naryGrayCodes xs k = snd $ foldl' prefixAndShift (ys,[]) xs'
  where ys = naryGrayCodes xs 1
        xs' = naryGrayCodes xs (k-1)

-- | Shift elements right.
shift  [a]  [a]
shift as = last as : init as

prefixAndShift  ([[a]],[[a]])  [a]  ([[a]],[[a]])
prefixAndShift (ys,zs) xs = (shift ys, zs ++ (map (xs++) ys))