flatDependencies :: GenericPackageDescription -> [Dependency]
flatDependencies =
      sortOn (\(Dependency pkgname _) -> map toLower (display pkgname))
    . pkgDeps
  where
    pkgDeps :: GenericPackageDescription -> [Dependency]
    pkgDeps pkg = fromMap $ Map.unionsWith intersectVersions $
                      map condTreeDeps (maybeToList $ condLibrary pkg)
                   ++ map (condTreeDeps . snd) (condExecutables pkg)
      where
        fromMap = map fromPair . Map.toList
        fromPair (pkgname, Versions _ ver) =
            Dependency pkgname $ fromVersionIntervals ver

    condTreeDeps :: CondTree v [Dependency] a -> PackageVersions
    condTreeDeps (CondNode _ ds comps) =
        Map.unionsWith intersectVersions $
          toMap ds : map fromComponent comps
      where
        fromComponent (_, then_part, else_part) =
            unionDeps (condTreeDeps then_part)
                      (maybe Map.empty condTreeDeps else_part)
        toMap = Map.fromListWith intersectVersions . map toPair
        toPair (Dependency pkgname ver) =
            (pkgname, Versions All $ toVersionIntervals ver)

    unionDeps :: PackageVersions -> PackageVersions -> PackageVersions
    unionDeps ds1 ds2 = Map.unionWith unionVersions
                        (Map.union ds1 defaults) (Map.union ds2 defaults)
      where
        defaults = Map.map (const notSpecified) $ Map.union ds1 ds2
        notSpecified = Versions Some $ toVersionIntervals noVersion

-- | Version intervals for a dependency that also indicate whether the
-- dependency has been specified on all branches. For example, package x's
-- version intervals use 'All' while package y's version intervals use
-- 'Some':
--
-- > if flag(f)
-- >   build-depends: x < 1, y < 1
-- > else
-- >   build-depends: x >= 1
--
-- This distinction affects the intersection of intervals.
flatDependencies :: GenericPackageDescription -> [Dependency]
flatDependencies =
      sortOn (\(Dependency pkgname _) -> map toLower (display pkgname))
    . pkgDeps
  where
    pkgDeps :: GenericPackageDescription -> [Dependency]
    pkgDeps pkg = fromMap $ Map.unionsWith intersectVersions $
                      map condTreeDeps (maybeToList $ condLibrary pkg)
                   ++ map (condTreeDeps . snd) (condExecutables pkg)
      where
        fromMap = map fromPair . Map.toList
        fromPair (pkgname, Versions _ ver) =
            Dependency pkgname $ fromVersionIntervals ver

    condTreeDeps :: CondTree v [Dependency] a -> PackageVersions
    condTreeDeps (CondNode _ ds comps) =
        Map.unionsWith intersectVersions $
          toMap ds : map fromComponent comps
      where
        fromComponent (_, then_part, else_part) =
            unionDeps (condTreeDeps then_part)
                      (maybe Map.empty condTreeDeps else_part)
        toMap = Map.fromListWith intersectVersions . map toPair
        toPair (Dependency pkgname ver) =
            (pkgname, Versions All $ toVersionIntervals ver)

    unionDeps :: PackageVersions -> PackageVersions -> PackageVersions
    unionDeps ds1 ds2 = Map.unionWith unionVersions
                        (Map.union ds1 defaults) (Map.union ds2 defaults)
      where
        defaults = Map.map (const notSpecified) $ Map.union ds1 ds2
        notSpecified = Versions Some $ toVersionIntervals noVersion

-- | Version intervals for a dependency that also indicate whether the
-- dependency has been specified on all branches. For example, package x's
-- version intervals use 'All' while package y's version intervals use
-- 'Some':
--
-- > if flag(f)
-- >   build-depends: x < 1, y < 1
-- > else
-- >   build-depends: x >= 1
--
-- This distinction affects the intersection of intervals.
flatDependencies =
      sortOn (\(Dependency pkgname _) -> map toLower (display pkgname))
    . pkgDeps
  where
    pkgDeps :: GenericPackageDescription -> [Dependency]
    pkgDeps pkg = fromMap $ Map.unionsWith intersectVersions $
                      map condTreeDeps (maybeToList $ condLibrary pkg)
                   ++ map (condTreeDeps . snd) (condExecutables pkg)
      where
        fromMap = map fromPair . Map.toList
        fromPair (pkgname, Versions _ ver) =
            Dependency pkgname $ fromVersionIntervals ver

    condTreeDeps :: CondTree v [Dependency] a -> PackageVersions
    condTreeDeps (CondNode _ ds comps) =
        Map.unionsWith intersectVersions $
          toMap ds : map fromComponent comps
      where
        fromComponent (_, then_part, else_part) =
            unionDeps (condTreeDeps then_part)
                      (maybe Map.empty condTreeDeps else_part)
        toMap = Map.fromListWith intersectVersions . map toPair
        toPair (Dependency pkgname ver) =
            (pkgname, Versions All $ toVersionIntervals ver)

    unionDeps :: PackageVersions -> PackageVersions -> PackageVersions
    unionDeps ds1 ds2 = Map.unionWith unionVersions
                        (Map.union ds1 defaults) (Map.union ds2 defaults)
      where
        defaults = Map.map (const notSpecified) $ Map.union ds1 ds2
        notSpecified = Versions Some $ toVersionIntervals noVersion

-- | Version intervals for a dependency that also indicate whether the
-- dependency has been specified on all branches. For example, package x's
-- version intervals use 'All' while package y's version intervals use
-- 'Some':
--
-- > if flag(f)
-- >   build-depends: x < 1, y < 1
-- > else
-- >   build-depends: x >= 1
--
-- This distinction affects the intersection of intervals.