content: import Data.List (find, delete, (\\))
  import Control.Applicative ((<|>))
  
  ------------------------------------------------------------
  
  newtype Perimeter a = Perimeter [a]
    deriving Show
  
  instance Eq a => Eq (Perimeter a) where
    Perimeter p1 == Perimeter p2 =
      null (p1 \\ p2)
      && ((p1 `elem` zipWith const (iterate rotate p2) p1)
          || Perimeter p1 == Perimeter (reverse p2))
  
  rotate lst = zipWith const (tail (cycle lst)) lst
  
  toEdges :: Ord a => Perimeter a -> Maybe (Edges a)
  toEdges (Perimeter ps)
    | allDifferent ps = Just . Edges $ zipWith ord ps (tail (cycle ps))
    | otherwise = Nothing
    where
      ord a b = if a < b then (a, b) else (b, a)
  
  allDifferent [] = True
  allDifferent (x:xs) = all (x /=) xs && allDifferent xs
  
  ------------------------------------------------------------
  
  newtype Edges a = Edges [(a, a)]
    deriving Show
  
  instance Eq a => Eq (Edges a) where
    e1 == e2 = toPerimeter e1 == toPerimeter e2
  
  toPerimeter :: Eq a => Edges a -> Maybe (Perimeter a)
  toPerimeter (Edges ((a, b):es)) = Perimeter . (a :) <$> go b es
    where
      go x rs
        | x == a = return []
        | otherwise = do
            p <- find ((x ==) . fst) rs <|> find ((x ==) . snd) rs
            let next = if fst p == x then snd p else fst p
            (x :) <$> go next (delete p rs)
  