mapPreservingGraph :: (HasUnitId ipkg,
                       HasUnitId srcpkg,
                       HasUnitId ipkg',   PackageFixedDeps ipkg',
                       HasUnitId srcpkg', PackageFixedDeps srcpkg')
                   => (  (UnitId -> UnitId)
                      -> GenericPlanPackage ipkg  srcpkg  iresult  ifailure
                      -> GenericPlanPackage ipkg' srcpkg' iresult' ifailure')
                   -> GenericInstallPlan ipkg  srcpkg  iresult  ifailure
                   -> GenericInstallPlan ipkg' srcpkg' iresult' ifailure'
mapPreservingGraph f plan =
    mkInstallPlan (PackageIndex.fromList pkgs')
                  (planIndepGoals plan)
  where
    -- The package mapping function may change the UnitId. So we
    -- walk over the packages in dependency order keeping track of these
    -- package id changes and use it to supply the correct set of package
    -- dependencies as an extra input to the package mapping function.

    (_, pkgs') = foldl' f' (Map.empty, []) (reverseTopologicalOrder plan)

    f' (ipkgidMap, pkgs) pkg = (ipkgidMap', pkg' : pkgs)
      where
       pkg' = f (mapDep ipkgidMap) pkg

       ipkgidMap'
         | ipkgid /= ipkgid' = Map.insert ipkgid ipkgid' ipkgidMap
         | otherwise         =                           ipkgidMap
         where
           ipkgid  = installedUnitId pkg
           ipkgid' = installedUnitId pkg'

    mapDep ipkgidMap ipkgid = Map.findWithDefault ipkgid ipkgid ipkgidMap


-- ------------------------------------------------------------
-- * Checking validity of plans
-- ------------------------------------------------------------

-- | A valid installation plan is a set of packages that is 'acyclic',
-- 'closed' and 'consistent'. Also, every 'ConfiguredPackage' in the
-- plan has to have a valid configuration (see 'configuredPackageValid').
--
-- * if the result is @False@ use 'problems' to get a detailed list.
--
mapPreservingGraph :: (HasUnitId ipkg,
                       HasUnitId srcpkg,
                       HasUnitId ipkg',   PackageFixedDeps ipkg',
                       HasUnitId srcpkg', PackageFixedDeps srcpkg')
                   => (  (UnitId -> UnitId)
                      -> GenericPlanPackage ipkg  srcpkg  iresult  ifailure
                      -> GenericPlanPackage ipkg' srcpkg' iresult' ifailure')
                   -> GenericInstallPlan ipkg  srcpkg  iresult  ifailure
                   -> GenericInstallPlan ipkg' srcpkg' iresult' ifailure'
mapPreservingGraph f plan =
    mkInstallPlan (PackageIndex.fromList pkgs')
                  (planIndepGoals plan)
  where
    -- The package mapping function may change the UnitId. So we
    -- walk over the packages in dependency order keeping track of these
    -- package id changes and use it to supply the correct set of package
    -- dependencies as an extra input to the package mapping function.

    (_, pkgs') = foldl' f' (Map.empty, []) (reverseTopologicalOrder plan)

    f' (ipkgidMap, pkgs) pkg = (ipkgidMap', pkg' : pkgs)
      where
       pkg' = f (mapDep ipkgidMap) pkg

       ipkgidMap'
         | ipkgid /= ipkgid' = Map.insert ipkgid ipkgid' ipkgidMap
         | otherwise         =                           ipkgidMap
         where
           ipkgid  = installedUnitId pkg
           ipkgid' = installedUnitId pkg'

    mapDep ipkgidMap ipkgid = Map.findWithDefault ipkgid ipkgid ipkgidMap


-- ------------------------------------------------------------
-- * Checking validity of plans
-- ------------------------------------------------------------

-- | A valid installation plan is a set of packages that is 'acyclic',
-- 'closed' and 'consistent'. Also, every 'ConfiguredPackage' in the
-- plan has to have a valid configuration (see 'configuredPackageValid').
--
-- * if the result is @False@ use 'problems' to get a detailed list.
--
mapPreservingGraph f plan =
    mkInstallPlan (PackageIndex.fromList pkgs')
                  (planIndepGoals plan)
  where
    -- The package mapping function may change the UnitId. So we
    -- walk over the packages in dependency order keeping track of these
    -- package id changes and use it to supply the correct set of package
    -- dependencies as an extra input to the package mapping function.

    (_, pkgs') = foldl' f' (Map.empty, []) (reverseTopologicalOrder plan)

    f' (ipkgidMap, pkgs) pkg = (ipkgidMap', pkg' : pkgs)
      where
       pkg' = f (mapDep ipkgidMap) pkg

       ipkgidMap'
         | ipkgid /= ipkgid' = Map.insert ipkgid ipkgid' ipkgidMap
         | otherwise         =                           ipkgidMap
         where
           ipkgid  = installedUnitId pkg
           ipkgid' = installedUnitId pkg'

    mapDep ipkgidMap ipkgid = Map.findWithDefault ipkgid ipkgid ipkgidMap


-- ------------------------------------------------------------
-- * Checking validity of plans
-- ------------------------------------------------------------

-- | A valid installation plan is a set of packages that is 'acyclic',
-- 'closed' and 'consistent'. Also, every 'ConfiguredPackage' in the
-- plan has to have a valid configuration (see 'configuredPackageValid').
--
-- * if the result is @False@ use 'problems' to get a detailed list.
--