% first line Data.PcSets
% This file is part of gpcsets: Pitch Class Sets for Haskell
% Copyright 2009 by Bruce H. McCosar.
% Distributed under a BSD3 license; see the file 'LICENSE' for details.
\chapter{Data.PcSets}
\section{Introduction}
\subsection{The Module Export List}
\begin{code}
module Data.PcSets
(
PcSet (modulus,elements,pMap)
, Selective (complement)
, Inclusive (reconcile)
, GenSet
, StdSet
, GenRow
, StdRow
, genset
, stdset
, genrow
, stdrow
, transpose
, invert
, invertXY
, zero
, retrograde
, rotate
, sort
, normal
, reduced
, prime
, cardinality
, binaryValue
, avec
, cvec
, ivec
, rowP
, rowR
, rowI
, rowRI
)
where
\end{code}
\subsection{The Module Import List}
\begin{code}
import qualified Data.List (nub,sort,sortBy,elemIndices)
\end{code}
\section{Classes}
\subsection{PcSet}
\begin{code}
class PcSet a where
modulus :: a -> Int
elements :: a -> [Int]
pMap :: ([Int] -> [Int]) -> a -> a
\end{code}
\subsection{Selective PcSets (Pitch Class Sets)}
\begin{code}
class PcSet a => Selective a where
complement :: a -> a
\end{code}
\subsection{Inclusive PcSets (Tone Rows)}
\begin{code}
class PcSet a => Inclusive a where
reconcile :: Int -> a -> a
reconcile n ps = transpose r ps
where
firstElement = head . elements $ ps
r = n firstElement
\end{code}
\section{Types}
\subsection{GenSet: General Pitch Class Sets}
\begin{code}
data GenSet = GenSet Int [Int]
deriving (Eq,Ord,Show)
\end{code}
text
\begin{code}
instance PcSet GenSet where
modulus (GenSet m _ ) = m
elements (GenSet _ es) = es
pMap f (GenSet m es) = genset m . f $ es
\end{code}
text
\begin{code}
instance Selective GenSet where
complement (GenSet 0 _ ) = GenSet 0 []
complement (GenSet m es) = GenSet m cs
where cs = filter (`notElem` es) [0..(m1)]
\end{code}
\subsection{StdSet: Standard Pitch Class Sets}
\begin{code}
data StdSet = StdSet [Int]
deriving (Eq,Ord,Show)
\end{code}
text
\begin{code}
instance PcSet StdSet where
modulus (StdSet _ ) = 12
elements (StdSet es) = es
pMap f (StdSet es) = stdset . f $ es
\end{code}
text
\begin{code}
instance Selective StdSet where
complement (StdSet es) = StdSet cs
where cs = filter (`notElem` es) [0..11]
\end{code}
\subsection{GenRow: General Tone Rows}
\begin{code}
data GenRow = GenRow [Int]
deriving (Eq,Ord,Show)
\end{code}
text
\begin{code}
instance PcSet GenRow where
modulus (GenRow es) = length es
elements (GenRow es) = es
pMap f (GenRow es) = genrow (length es) . f $ es
\end{code}
text
\begin{code}
instance Inclusive GenRow
\end{code}
\subsection{StdRow: Standard Tone Rows}
\begin{code}
data StdRow = StdRow [Int]
deriving (Eq,Ord,Show)
\end{code}
text
\begin{code}
instance PcSet StdRow where
modulus (StdRow _ ) = 12
elements (StdRow es) = es
pMap f (StdRow es) = stdrow . f $ es
\end{code}
text
\begin{code}
instance Inclusive StdRow
\end{code}
\section{Constructors}
\subsection{genset}
\begin{code}
genset :: Int -> [Int] -> GenSet
genset 0 _ = GenSet 0 []
genset m_in es = GenSet m (f es)
where
m = abs m_in
f = Data.List.nub . map (`mod` m)
\end{code}
\subsection{stdset}
\begin{code}
stdset :: [Int] -> StdSet
stdset es = StdSet ps
where ps = elements $ genset 12 es
\end{code}
\subsection{genrow}
\begin{code}
genrow :: Int -> [Int] -> GenRow
genrow m es = GenRow (os ++ cs)
where
ps = genset m es
os = elements ps
cs = elements $ complement ps
\end{code}
\subsection{stdrow}
\begin{code}
stdrow :: [Int] -> StdRow
stdrow es = StdRow ts
where ts = elements $ genrow 12 es
\end{code}
\section{General Operations (All Sets)}
\subsection{Transformations}
\subsubsection{transpose}
\begin{code}
transpose :: PcSet a => Int -> a -> a
transpose = pMap . map . (+)
\end{code}
\subsubsection{invert}
\begin{code}
invert :: PcSet a => a -> a
invert ps = pMap (map (m )) ps
where m = modulus ps
\end{code}
\subsubsection{invertXY}
\begin{code}
invertXY :: PcSet a => Int -> Int -> a -> a
invertXY x y = transpose (x + y) . invert
\end{code}
\subsubsection{zero}
\begin{code}
zero :: PcSet a => a -> a
zero ps = transpose (n) ps
where n = head . elements $ ps
\end{code}
\subsection{Permutations}
\subsubsection{retrograde}
\begin{code}
retrograde :: PcSet a => a -> a
retrograde = pMap reverse
\end{code}
\subsubsection{rotate}
\begin{code}
rotate :: PcSet a => Int -> a -> a
rotate n ps = pMap nShift ps
where
nShift = take sameLength . drop offset . cycle
sameLength = (length . elements) ps
offset = n `mod` sameLength
\end{code}
\section{Selective Set Operations}
\subsection{Systematically Equivalent Forms}
\subsubsection{sort}
\begin{code}
sort :: (PcSet a, Selective a) => a -> a
sort = pMap Data.List.sort
\end{code}
\subsubsection{normal}
\begin{code}
normal :: (PcSet a, Selective a) => a -> a
normal = nform . bestPack . pcsArrangements
\end{code}
\subsubsection{reduced}
\begin{code}
reduced :: (PcSet a, Selective a) => a -> a
reduced = rform . bestPack . pcsArrangements
\end{code}
\subsubsection{prime}
\begin{code}
prime :: (PcSet a, Selective a) => a -> a
prime ps = if i_val < o_val then inversion else original
where
original = reduced ps
inversion = reduced $ invert ps
o_val = binaryValue original
i_val = binaryValue inversion
\end{code}
\subsection{Scalar Quantities}
\subsubsection{cardinality}
\begin{code}
cardinality :: (PcSet a, Selective a) => a -> Int
cardinality = length . elements
\end{code}
\subsubsection{binaryValue}
\begin{code}
binaryValue :: (PcSet a, Selective a) => a -> Integer
binaryValue = sum . map (2 ^) . elements
\end{code}
\subsection{Vector Quantities}
\subsubsection{avec}
\begin{code}
avec :: (PcSet a, Selective a) => a -> [Int]
avec ps = map (`mod` m) $ zipWith () rs os
where
m = modulus ps
os = elements ps
rs = elements . rotate 1 $ ps
\end{code}
\subsubsection{cvec}
\begin{code}
cvec :: (PcSet a, Selective a) => a -> [Int]
cvec ps = count . concatMap f $ es
where
m = modulus ps
es = elements ps
count cs = map (\n ->
length (Data.List.elemIndices n cs)) [0..(m1)]
f x = map (\y -> (x + y) `mod` m) es
\end{code}
\subsubsection{ivec}
\begin{code}
ivec :: (PcSet a, Selective a) => a -> [Int]
ivec ps = if m == 0 then []
else pivotguard . spacefold . count . intervals . elements $ ps
where
m = modulus ps
pivotguard es = if odd m then es
else init es ++ [last es `div` 2]
spacefold = take (m `div` 2) . flipSum
flipSum es = zipWith (+) es (reverse es)
count ivs = map (g ivs) [1..(m1)]
g ivs n = length (Data.List.elemIndices n ivs)
intervals [] = []
intervals (e:es) = diffs e es ++ intervals es
diffs = map . f
f a b = (b a) `mod` m
\end{code}
\section{Inclusive Set (Tone Row) Operations}
\subsection{Permutation-Transformations}
\subsubsection{rowP}
\begin{code}
rowP :: (PcSet a, Inclusive a) => Int -> a -> a
rowP = reconcile
\end{code}
\subsubsection{rowR}
\begin{code}
rowR :: (PcSet a, Inclusive a) => Int -> a -> a
rowR = (. retrograde) . reconcile
\end{code}
\subsubsection{rowI}
\begin{code}
rowI :: (PcSet a, Inclusive a) => Int -> a -> a
rowI = (. invert) . reconcile
\end{code}
\subsubsection{rowRI}
\begin{code}
rowRI :: (PcSet a, Inclusive a) => Int -> a -> a
rowRI = (. (invert . retrograde)) . reconcile
\end{code}
\section{Not Exported}
\subsection{Related to Normal, Reduced, and Prime}
\begin{code}
data (PcSet a, Selective a) => Candidate a = Candidate
{
idx :: Integer,
nform :: a,
rform :: a
}
\end{code}
\begin{code}
interview :: (PcSet a, Selective a) => a -> Candidate a
interview ps = Candidate
{
idx = binaryValue zs,
nform = ps,
rform = zs
}
where zs = zero ps
\end{code}
\begin{code}
sortFunction :: (PcSet a, Selective a) =>
Candidate a -> Candidate a -> Ordering
sortFunction a b = compare (idx a) (idx b)
\end{code}
\begin{code}
bestPack :: (PcSet a, Selective a) => [a] -> Candidate a
bestPack arrs = head (Data.List.sortBy sortFunction candidates)
where candidates = [interview ps | ps <- arrs]
\end{code}
\begin{code}
pcsArrangements :: (PcSet a, Selective a) => a -> [a]
pcsArrangements ps = if n == 0
then [ps]
else take n $ iterate f (sort ps)
where
n = cardinality ps
f = rotate 1
\end{code}
% last line Data.PcSets