{-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GADTs #-} #if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Trustworthy #-} {-# LANGUAGE DeriveGeneric #-} #endif #if __GLASGOW_HASKELL__ >= 707 {-# LANGUAGE DataKinds #-} #endif #ifndef MIN_VERSION_vector #define MIN_VERSION_vector(x,y,z) 1 #endif #ifndef MIN_VERSION_transformers #define MIN_VERSION_transformers(x,y,z) 1 #endif ----------------------------------------------------------------------------- -- | -- Copyright : (C) 2012-2015 Edward Kmett -- License : BSD-style (see the file LICENSE) -- -- Maintainer : Edward Kmett -- Stability : experimental -- Portability : non-portable -- -- Plücker coordinates for lines in 3d homogeneous space. ---------------------------------------------------------------------------- module Linear.Plucker ( Plucker(..) , squaredError , isotropic , (><) , plucker , plucker3D -- * Operations on lines , parallel , intersects , LinePass(..) , passes , quadranceToOrigin , closestToOrigin , isLine , coincides , coincides' -- * Basis elements , p01, p02, p03 , p10, p12, p13 , p20, p21, p23 , p30, p31, p32 , e01, e02, e03, e12, e31, e23 ) where import Control.Applicative import Control.DeepSeq (NFData(rnf)) import Control.Monad (liftM) import Control.Monad.Fix import Control.Monad.Zip import Control.Lens hiding (index, (<.>)) import Data.Binary as Binary import Data.Bytes.Serial import Data.Distributive import Data.Foldable as Foldable import Data.Functor.Bind import Data.Functor.Classes import Data.Functor.Rep import Data.Hashable import Data.Semigroup import Data.Semigroup.Foldable import Data.Serialize as Cereal #if __GLASGOW_HASKELL__ >= 707 import qualified Data.Vector as V #endif import qualified Data.Vector.Generic.Mutable as M import qualified Data.Vector.Generic as G import qualified Data.Vector.Unboxed.Base as U import Foreign.Ptr (castPtr) import Foreign.Storable (Storable(..)) import GHC.Arr (Ix(..)) #if __GLASGOW_HASKELL__ >= 702 import GHC.Generics (Generic) #endif #if __GLASGOW_HASKELL__ >= 706 import GHC.Generics (Generic1) #endif import Linear.Epsilon import Linear.Metric #if __GLASGOW_HASKELL__ >= 707 import Linear.V #endif import Linear.V2 import Linear.V3 import Linear.V4 import Linear.Vector {-# ANN module "HLint: ignore Reduce duplication" #-} -- | Plücker coordinates for lines in a 3-dimensional space. data Plucker a = Plucker !a !a !a !a !a !a deriving (Eq,Ord,Show,Read #if __GLASGOW_HASKELL__ >= 702 ,Generic #endif #if __GLASGOW_HASKELL__ >= 706 ,Generic1 #endif ) #if __GLASGOW_HASKELL__ >= 707 instance Finite Plucker where type Size Plucker = 6 toV (Plucker a b c d e f) = V (V.fromListN 6 [a,b,c,d,e,f]) fromV (V v) = Plucker (v V.! 0) (v V.! 1) (v V.! 2) (v V.! 3) (v V.! 4) (v V.! 5) #endif instance Functor Plucker where fmap g (Plucker a b c d e f) = Plucker (g a) (g b) (g c) (g d) (g e) (g f) {-# INLINE fmap #-} instance Apply Plucker where Plucker a b c d e f <.> Plucker g h i j k l = Plucker (a g) (b h) (c i) (d j) (e k) (f l) {-# INLINE (<.>) #-} instance Applicative Plucker where pure a = Plucker a a a a a a {-# INLINE pure #-} Plucker a b c d e f <*> Plucker g h i j k l = Plucker (a g) (b h) (c i) (d j) (e k) (f l) {-# INLINE (<*>) #-} instance Additive Plucker where zero = pure 0 {-# INLINE zero #-} liftU2 = liftA2 {-# INLINE liftU2 #-} liftI2 = liftA2 {-# INLINE liftI2 #-} instance Bind Plucker where Plucker a b c d e f >>- g = Plucker a' b' c' d' e' f' where Plucker a' _ _ _ _ _ = g a Plucker _ b' _ _ _ _ = g b Plucker _ _ c' _ _ _ = g c Plucker _ _ _ d' _ _ = g d Plucker _ _ _ _ e' _ = g e Plucker _ _ _ _ _ f' = g f {-# INLINE (>>-) #-} instance Monad Plucker where return a = Plucker a a a a a a {-# INLINE return #-} Plucker a b c d e f >>= g = Plucker a' b' c' d' e' f' where Plucker a' _ _ _ _ _ = g a Plucker _ b' _ _ _ _ = g b Plucker _ _ c' _ _ _ = g c Plucker _ _ _ d' _ _ = g d Plucker _ _ _ _ e' _ = g e Plucker _ _ _ _ _ f' = g f {-# INLINE (>>=) #-} instance Distributive Plucker where distribute f = Plucker (fmap (\(Plucker x _ _ _ _ _) -> x) f) (fmap (\(Plucker _ x _ _ _ _) -> x) f) (fmap (\(Plucker _ _ x _ _ _) -> x) f) (fmap (\(Plucker _ _ _ x _ _) -> x) f) (fmap (\(Plucker _ _ _ _ x _) -> x) f) (fmap (\(Plucker _ _ _ _ _ x) -> x) f) {-# INLINE distribute #-} instance Representable Plucker where type Rep Plucker = E Plucker tabulate f = Plucker (f e01) (f e02) (f e03) (f e23) (f e31) (f e12) {-# INLINE tabulate #-} index xs (E l) = view l xs {-# INLINE index #-} instance Foldable Plucker where foldMap g (Plucker a b c d e f) = g a `mappend` g b `mappend` g c `mappend` g d `mappend` g e `mappend` g f {-# INLINE foldMap #-} instance Traversable Plucker where traverse g (Plucker a b c d e f) = Plucker <$> g a <*> g b <*> g c <*> g d <*> g e <*> g f {-# INLINE traverse #-} instance Foldable1 Plucker where foldMap1 g (Plucker a b c d e f) = g a <> g b <> g c <> g d <> g e <> g f {-# INLINE foldMap1 #-} instance Traversable1 Plucker where traverse1 g (Plucker a b c d e f) = Plucker <$> g a <.> g b <.> g c <.> g d <.> g e <.> g f {-# INLINE traverse1 #-} instance Ix a => Ix (Plucker a) where range (Plucker l1 l2 l3 l4 l5 l6,Plucker u1 u2 u3 u4 u5 u6) = [Plucker i1 i2 i3 i4 i5 i6 | i1 <- range (l1,u1) , i2 <- range (l2,u2) , i3 <- range (l3,u3) , i4 <- range (l4,u4) , i5 <- range (l5,u5) , i6 <- range (l6,u6) ] {-# INLINE range #-} unsafeIndex (Plucker l1 l2 l3 l4 l5 l6,Plucker u1 u2 u3 u4 u5 u6) (Plucker i1 i2 i3 i4 i5 i6) = unsafeIndex (l6,u6) i6 + unsafeRangeSize (l6,u6) * ( unsafeIndex (l5,u5) i5 + unsafeRangeSize (l5,u5) * ( unsafeIndex (l4,u4) i4 + unsafeRangeSize (l4,u4) * ( unsafeIndex (l3,u3) i3 + unsafeRangeSize (l3,u3) * ( unsafeIndex (l2,u2) i2 + unsafeRangeSize (l2,u2) * unsafeIndex (l1,u1) i1)))) {-# INLINE unsafeIndex #-} inRange (Plucker l1 l2 l3 l4 l5 l6,Plucker u1 u2 u3 u4 u5 u6) (Plucker i1 i2 i3 i4 i5 i6) = inRange (l1,u1) i1 && inRange (l2,u2) i2 && inRange (l3,u3) i3 && inRange (l4,u4) i4 && inRange (l5,u5) i5 && inRange (l6,u6) i6 {-# INLINE inRange #-} instance Num a => Num (Plucker a) where (+) = liftA2 (+) {-# INLINE (+) #-} (-) = liftA2 (-) {-# INLINE (-) #-} (*) = liftA2 (*) {-# INLINE (*) #-} negate = fmap negate {-# INLINE negate #-} abs = fmap abs {-# INLINE abs #-} signum = fmap signum {-# INLINE signum #-} fromInteger = pure . fromInteger {-# INLINE fromInteger #-} instance Fractional a => Fractional (Plucker a) where recip = fmap recip {-# INLINE recip #-} (/) = liftA2 (/) {-# INLINE (/) #-} fromRational = pure . fromRational {-# INLINE fromRational #-} instance Floating a => Floating (Plucker a) where pi = pure pi {-# INLINE pi #-} exp = fmap exp {-# INLINE exp #-} sqrt = fmap sqrt {-# INLINE sqrt #-} log = fmap log {-# INLINE log #-} (**) = liftA2 (**) {-# INLINE (**) #-} logBase = liftA2 logBase {-# INLINE logBase #-} sin = fmap sin {-# INLINE sin #-} tan = fmap tan {-# INLINE tan #-} cos = fmap cos {-# INLINE cos #-} asin = fmap asin {-# INLINE asin #-} atan = fmap atan {-# INLINE atan #-} acos = fmap acos {-# INLINE acos #-} sinh = fmap sinh {-# INLINE sinh #-} tanh = fmap tanh {-# INLINE tanh #-} cosh = fmap cosh {-# INLINE cosh #-} asinh = fmap asinh {-# INLINE asinh #-} atanh = fmap atanh {-# INLINE atanh #-} acosh = fmap acosh {-# INLINE acosh #-} instance Hashable a => Hashable (Plucker a) where hashWithSalt s (Plucker a b c d e f) = s `hashWithSalt` a `hashWithSalt` b `hashWithSalt` c `hashWithSalt` d `hashWithSalt` e `hashWithSalt` f {-# INLINE hashWithSalt #-} instance Storable a => Storable (Plucker a) where sizeOf _ = 6 * sizeOf (undefined::a) {-# INLINE sizeOf #-} alignment _ = alignment (undefined::a) {-# INLINE alignment #-} poke ptr (Plucker a b c d e f) = do poke ptr' a pokeElemOff ptr' 1 b pokeElemOff ptr' 2 c pokeElemOff ptr' 3 d pokeElemOff ptr' 4 e pokeElemOff ptr' 5 f where ptr' = castPtr ptr {-# INLINE poke #-} peek ptr = Plucker <$> peek ptr' <*> peekElemOff ptr' 1 <*> peekElemOff ptr' 2 <*> peekElemOff ptr' 3 <*> peekElemOff ptr' 4 <*> peekElemOff ptr' 5 where ptr' = castPtr ptr {-# INLINE peek #-} instance Metric Plucker where dot (Plucker a b c d e f) (Plucker g h i j k l) = a*g+b*h+c*i+d*j+e*k+f*l {-# INLINE dot #-} instance Epsilon a => Epsilon (Plucker a) where nearZero = nearZero . quadrance {-# INLINE nearZero #-} -- | Given a pair of points represented by homogeneous coordinates -- generate Plücker coordinates for the line through them, directed -- from the second towards the first. plucker :: Num a => V4 a -> V4 a -> Plucker a plucker (V4 a b c d) (V4 e f g h) = Plucker (a*f-b*e) (a*g-c*e) (b*g-c*f) (a*h-d*e) (b*h-d*f) (c*h-d*g) {-# INLINE plucker #-} -- | Given a pair of 3D points, generate Plücker coordinates for the -- line through them, directed from the second towards the first. plucker3D :: Num a => V3 a -> V3 a -> Plucker a plucker3D p q = Plucker a b c d e f where V3 a b c = p - q V3 d e f = p `cross` q -- | These elements form a basis for the Plücker space, or the Grassmanian manifold @Gr(2,V4)@. -- -- @ -- 'p01' :: 'Lens'' ('Plucker' a) a -- 'p02' :: 'Lens'' ('Plucker' a) a -- 'p03' :: 'Lens'' ('Plucker' a) a -- 'p23' :: 'Lens'' ('Plucker' a) a -- 'p31' :: 'Lens'' ('Plucker' a) a -- 'p12' :: 'Lens'' ('Plucker' a) a -- @ p01, p02, p03, p23, p31, p12 :: Lens' (Plucker a) a p01 g (Plucker a b c d e f) = (\a' -> Plucker a' b c d e f) <$> g a p02 g (Plucker a b c d e f) = (\b' -> Plucker a b' c d e f) <$> g b p03 g (Plucker a b c d e f) = (\c' -> Plucker a b c' d e f) <$> g c p23 g (Plucker a b c d e f) = (\d' -> Plucker a b c d' e f) <$> g d p31 g (Plucker a b c d e f) = (\e' -> Plucker a b c d e' f) <$> g e p12 g (Plucker a b c d e f) = Plucker a b c d e <$> g f {-# INLINE p01 #-} {-# INLINE p02 #-} {-# INLINE p03 #-} {-# INLINE p23 #-} {-# INLINE p31 #-} {-# INLINE p12 #-} -- | These elements form an alternate basis for the Plücker space, or the Grassmanian manifold @Gr(2,V4)@. -- -- @ -- 'p10' :: 'Num' a => 'Lens'' ('Plucker' a) a -- 'p20' :: 'Num' a => 'Lens'' ('Plucker' a) a -- 'p30' :: 'Num' a => 'Lens'' ('Plucker' a) a -- 'p32' :: 'Num' a => 'Lens'' ('Plucker' a) a -- 'p13' :: 'Num' a => 'Lens'' ('Plucker' a) a -- 'p21' :: 'Num' a => 'Lens'' ('Plucker' a) a -- @ p10, p20, p30, p32, p13, p21 :: (Functor f, Num a) => (a -> f a) -> Plucker a -> f (Plucker a) p10 = anti p01 p20 = anti p02 p30 = anti p03 p32 = anti p23 p13 = anti p31 p21 = anti p21 {-# INLINE p10 #-} {-# INLINE p20 #-} {-# INLINE p30 #-} {-# INLINE p32 #-} {-# INLINE p13 #-} {-# INLINE p21 #-} anti :: (Functor f, Num a) => ((a -> f a) -> r) -> (a -> f a) -> r anti k f = k (fmap negate . f . negate) e01, e02, e03, e23, e31, e12 :: E Plucker e01 = E p01 e02 = E p02 e03 = E p03 e23 = E p23 e31 = E p31 e12 = E p12 instance FunctorWithIndex (E Plucker) Plucker where imap f (Plucker a b c d e g) = Plucker (f e01 a) (f e02 b) (f e03 c) (f e23 d) (f e31 e) (f e12 g) {-# INLINE imap #-} instance FoldableWithIndex (E Plucker) Plucker where ifoldMap f (Plucker a b c d e g) = f e01 a `mappend` f e02 b `mappend` f e03 c `mappend` f e23 d `mappend` f e31 e `mappend` f e12 g {-# INLINE ifoldMap #-} instance TraversableWithIndex (E Plucker) Plucker where itraverse f (Plucker a b c d e g) = Plucker <$> f e01 a <*> f e02 b <*> f e03 c <*> f e23 d <*> f e31 e <*> f e12 g {-# INLINE itraverse #-} type instance Index (Plucker a) = E Plucker type instance IxValue (Plucker a) = a instance Ixed (Plucker a) where ix = el {-# INLINE ix #-} instance Each (Plucker a) (Plucker b) a b where each = traverse {-# INLINE each #-} -- | Valid Plücker coordinates @p@ will have @'squaredError' p '==' 0@ -- -- That said, floating point makes a mockery of this claim, so you may want to use 'nearZero'. squaredError :: Num a => Plucker a -> a squaredError v = v >< v {-# INLINE squaredError #-} -- | This isn't th actual metric because this bilinear form gives rise to an isotropic quadratic space infixl 5 >< (><) :: Num a => Plucker a -> Plucker a -> a Plucker a b c d e f >< Plucker g h i j k l = a*l-b*k+c*j+d*i-e*h+f*g {-# INLINE (><) #-} -- | Checks if the line is near-isotropic (isotropic vectors in this -- quadratic space represent lines in real 3d space). isotropic :: Epsilon a => Plucker a -> Bool isotropic a = nearZero (a >< a) {-# INLINE isotropic #-} -- | Checks if two lines intersect (or nearly intersect). intersects :: (Epsilon a, Ord a) => Plucker a -> Plucker a -> Bool intersects a b = not (a `parallel` b) && passes a b == Coplanar -- intersects :: Epsilon a => Plucker a -> Plucker a -> Bool -- intersects a b = nearZero (a >< b) {-# INLINE intersects #-} -- | Describe how two lines pass each other. data LinePass = Coplanar -- ^ The lines are coplanar (parallel or intersecting). | Clockwise -- ^ The lines pass each other clockwise (right-handed -- screw) | Counterclockwise -- ^ The lines pass each other counterclockwise -- (left-handed screw). deriving (Eq, Show #if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702 ,Generic #endif ) -- | Check how two lines pass each other. @passes l1 l2@ describes -- @l2@ when looking down @l1@. passes :: (Epsilon a, Ord a) => Plucker a -> Plucker a -> LinePass passes a b | nearZero s = Coplanar | s > 0 = Counterclockwise | otherwise = Clockwise where s = (u1 `dot` v2) + (u2 `dot` v1) V2 u1 v1 = toUV a V2 u2 v2 = toUV b {-# INLINE passes #-} -- | Checks if two lines are parallel. parallel :: Epsilon a => Plucker a -> Plucker a -> Bool parallel a b = nearZero $ u1 `cross` u2 where V2 u1 _ = toUV a V2 u2 _ = toUV b {-# INLINE parallel #-} -- | Represent a Plücker coordinate as a pair of 3-tuples, typically -- denoted U and V. toUV :: Plucker a -> V2 (V3 a) toUV (Plucker a b c d e f) = V2 (V3 a b c) (V3 d e f) -- | Checks if two lines coincide in space. In other words, undirected equality. coincides :: (Epsilon a, Fractional a) => Plucker a -> Plucker a -> Bool coincides p1 p2 = Foldable.all nearZero $ (s *^ p2) - p1 where s = maybe 1 getFirst . getOption . fold $ saveDiv <$> p1 <*> p2 saveDiv x y | nearZero y = Option Nothing | otherwise = Option . Just $ First (x / y) {-# INLINABLE coincides #-} -- | Checks if two lines coincide in space, and have the same -- orientation. coincides' :: (Epsilon a, Fractional a, Ord a) => Plucker a -> Plucker a -> Bool coincides' p1 p2 = Foldable.all nearZero ((s *^ p2) - p1) && s > 0 where s = maybe 1 getFirst . getOption . fold $ saveDiv <$> p1 <*> p2 saveDiv x y | nearZero y = Option Nothing | otherwise = Option . Just $ First (x / y) {-# INLINABLE coincides' #-} -- | The minimum squared distance of a line from the origin. quadranceToOrigin :: Fractional a => Plucker a -> a quadranceToOrigin p = (v `dot` v) / (u `dot` u) where V2 u v = toUV p {-# INLINE quadranceToOrigin #-} -- | The point where a line is closest to the origin. closestToOrigin :: Fractional a => Plucker a -> V3 a closestToOrigin p = normalizePoint $ V4 x y z (u `dot` u) where V2 u v = toUV p V3 x y z = v `cross` u {-# INLINE closestToOrigin #-} -- | Not all 6-dimensional points correspond to a line in 3D. This -- predicate tests that a Plücker coordinate lies on the Grassmann -- manifold, and does indeed represent a 3D line. isLine :: Epsilon a => Plucker a -> Bool isLine p = nearZero $ u `dot` v where V2 u v = toUV p {-# INLINE isLine #-} -- TODO: drag some more stuff out of my thesis data instance U.Vector (Plucker a) = V_Plucker !Int (U.Vector a) data instance U.MVector s (Plucker a) = MV_Plucker !Int (U.MVector s a) instance U.Unbox a => U.Unbox (Plucker a) instance U.Unbox a => M.MVector U.MVector (Plucker a) where basicLength (MV_Plucker n _) = n basicUnsafeSlice m n (MV_Plucker _ v) = MV_Plucker n (M.basicUnsafeSlice (6*m) (6*n) v) basicOverlaps (MV_Plucker _ v) (MV_Plucker _ u) = M.basicOverlaps v u basicUnsafeNew n = liftM (MV_Plucker n) (M.basicUnsafeNew (6*n)) basicUnsafeRead (MV_Plucker _ a) i = do let o = 6*i x <- M.basicUnsafeRead a o y <- M.basicUnsafeRead a (o+1) z <- M.basicUnsafeRead a (o+2) w <- M.basicUnsafeRead a (o+3) v <- M.basicUnsafeRead a (o+4) u <- M.basicUnsafeRead a (o+5) return (Plucker x y z w v u) basicUnsafeWrite (MV_Plucker _ a) i (Plucker x y z w v u) = do let o = 6*i M.basicUnsafeWrite a o x M.basicUnsafeWrite a (o+1) y M.basicUnsafeWrite a (o+2) z M.basicUnsafeWrite a (o+3) w M.basicUnsafeWrite a (o+4) v M.basicUnsafeWrite a (o+5) u #if MIN_VERSION_vector(0,11,0) basicInitialize (MV_Plucker _ v) = M.basicInitialize v #endif instance U.Unbox a => G.Vector U.Vector (Plucker a) where basicUnsafeFreeze (MV_Plucker n v) = liftM ( V_Plucker n) (G.basicUnsafeFreeze v) basicUnsafeThaw ( V_Plucker n v) = liftM (MV_Plucker n) (G.basicUnsafeThaw v) basicLength ( V_Plucker n _) = n basicUnsafeSlice m n (V_Plucker _ v) = V_Plucker n (G.basicUnsafeSlice (6*m) (6*n) v) basicUnsafeIndexM (V_Plucker _ a) i = do let o = 6*i x <- G.basicUnsafeIndexM a o y <- G.basicUnsafeIndexM a (o+1) z <- G.basicUnsafeIndexM a (o+2) w <- G.basicUnsafeIndexM a (o+3) v <- G.basicUnsafeIndexM a (o+4) u <- G.basicUnsafeIndexM a (o+5) return (Plucker x y z w v u) instance MonadZip Plucker where mzipWith = liftA2 instance MonadFix Plucker where mfix f = Plucker (let Plucker a _ _ _ _ _ = f a in a) (let Plucker _ a _ _ _ _ = f a in a) (let Plucker _ _ a _ _ _ = f a in a) (let Plucker _ _ _ a _ _ = f a in a) (let Plucker _ _ _ _ a _ = f a in a) (let Plucker _ _ _ _ _ a = f a in a) instance NFData a => NFData (Plucker a) where rnf (Plucker a b c d e f) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d `seq` rnf e `seq` rnf f instance Serial1 Plucker where serializeWith = traverse_ deserializeWith k = Plucker <$> k <*> k <*> k <*> k <*> k <*> k instance Serial a => Serial (Plucker a) where serialize = serializeWith serialize deserialize = deserializeWith deserialize instance Binary a => Binary (Plucker a) where put = serializeWith Binary.put get = deserializeWith Binary.get instance Serialize a => Serialize (Plucker a) where put = serializeWith Cereal.put get = deserializeWith Cereal.get #if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0)) instance Eq1 Plucker where liftEq k (Plucker a1 b1 c1 d1 e1 f1) (Plucker a2 b2 c2 d2 e2 f2) = k a1 a2 && k b1 b2 && k c1 c2 && k d1 d2 && k e1 e2 && k f1 f2 instance Ord1 Plucker where liftCompare k (Plucker a1 b1 c1 d1 e1 f1) (Plucker a2 b2 c2 d2 e2 f2) = k a1 a2 `mappend` k b1 b2 `mappend` k c1 c2 `mappend` k d1 d2 `mappend` k e1 e2 `mappend` k f1 f2 instance Read1 Plucker where liftReadsPrec k _ z = readParen (z > 10) $ \r -> [ (Plucker a b c d e f, r7) | ("Plucker",r1) <- lex r , (a,r2) <- k 11 r1 , (b,r3) <- k 11 r2 , (c,r4) <- k 11 r3 , (d,r5) <- k 11 r4 , (e,r6) <- k 11 r5 , (f,r7) <- k 11 r6 ] instance Show1 Plucker where liftShowsPrec k _ z (Plucker a b c d e f) = showParen (z > 10) $ showString "Plucker " . k 11 a . showChar ' ' . k 11 b . showChar ' ' . k 11 c . showChar ' ' . k 11 d . showChar ' ' . k 11 e . showChar ' ' . k 11 f #else instance Eq1 Plucker where eq1 = (==) instance Ord1 Plucker where compare1 = compare instance Show1 Plucker where showsPrec1 = showsPrec instance Read1 Plucker where readsPrec1 = readsPrec #endif