greedyAddSink :: (Monad m, Coin c)
              => Word64          -- ^ Target to reach
              -> (Int -> Word64) -- ^ Coin count to fee function
              -> Bool            -- ^ Try to find better solutions
              -> Sink c m (Maybe ([c], Word64)) -- (Selected coins, change)
greedyAddSink target fee continue =
    go [] 0 [] 0
  where
    -- The goal is the value we must reach (including the fee) for a certain
    -- amount of selected coins.
    goal c = target + fee c
    go acc aTot ps pTot = await >>= \coinM -> case coinM of
        -- A coin is available in the stream
        Just coin -> do
            let val = coinValue coin
            -- We have reached the goal using this coin
            if val + aTot >= (goal $ length acc + 1)
                -- If we want to continue searching for better solutions
                then if continue
                    -- This solution is the first one or
                    -- This solution is better than the previous one
                    then if pTot == 0 || val + aTot < pTot
                        -- Continue searching for better solutions in the stream
                        then go [] 0 (coin:acc) (val + aTot)
                        -- Otherwise, we stop here and return the previous
                        -- solution
                        else return $ Just (ps, pTot - (goal $ length ps))
                    -- Otherwise, return this solution
                    else return $
                        Just (coin:acc, val + aTot - (goal $ length acc + 1))
                -- We have not yet reached the goal. Add the coin to the
                -- accumulator
                else go (coin:acc) (val + aTot) ps pTot
        -- We reached the end of the stream
        Nothing ->
            return $ if null ps
                -- If no solution was found, return Nothing
                then Nothing
                -- If we have a solution, return it
                else Just (ps, pTot - (goal $ length ps))
greedyAddSink :: (Monad m, Coin c)
              => Word64          -- ^ Target to reach
              -> (Int -> Word64) -- ^ Coin count to fee function
              -> Bool            -- ^ Try to find better solutions
              -> Sink c m (Maybe ([c], Word64))
greedyAddSink target fee continue =
    go [] 0 [] 0
  where
    -- The goal is the value we must reach (including the fee) for a certain
    -- amount of selected coins.
    goal c = target + fee c
    go acc aTot ps pTot = await >>= \coinM -> case coinM of
        -- A coin is available in the stream
        Just coin -> do
            let val = coinValue coin
            -- We have reached the goal using this coin
            if val + aTot >= (goal $ length acc + 1)
                -- If we want to continue searching for better solutions
                then if continue
                    -- This solution is the first one or
                    -- This solution is better than the previous one
                    then if pTot == 0 || val + aTot < pTot
                        -- Continue searching for better solutions in the stream
                        then go [] 0 (coin:acc) (val + aTot)
                        -- Otherwise, we stop here and return the previous
                        -- solution
                        else return $ Just (ps, pTot - (goal $ length ps))
                    -- Otherwise, return this solution
                    else return $
                        Just (coin:acc, val + aTot - (goal $ length acc + 1))
                -- We have not yet reached the goal. Add the coin to the
                -- accumulator
                else go (coin:acc) (val + aTot) ps pTot
        -- We reached the end of the stream
        Nothing ->
            return $ if null ps
                -- If no solution was found, return Nothing
                then Nothing
                -- If we have a solution, return it
                else Just (ps, pTot - (goal $ length ps))
greedyAddSink target fee continue =
    go [] 0 [] 0
  where
    -- The goal is the value we must reach (including the fee) for a certain
    -- amount of selected coins.
    goal c = target + fee c
    go acc aTot ps pTot = await >>= \coinM -> case coinM of
        -- A coin is available in the stream
        Just coin -> do
            let val = coinValue coin
            -- We have reached the goal using this coin
            if val + aTot >= (goal $ length acc + 1)
                -- If we want to continue searching for better solutions
                then if continue
                    -- This solution is the first one or
                    -- This solution is better than the previous one
                    then if pTot == 0 || val + aTot < pTot
                        -- Continue searching for better solutions in the stream
                        then go [] 0 (coin:acc) (val + aTot)
                        -- Otherwise, we stop here and return the previous
                        -- solution
                        else return $ Just (ps, pTot - (goal $ length ps))
                    -- Otherwise, return this solution
                    else return $
                        Just (coin:acc, val + aTot - (goal $ length acc + 1))
                -- We have not yet reached the goal. Add the coin to the
                -- accumulator
                else go (coin:acc) (val + aTot) ps pTot
        -- We reached the end of the stream
        Nothing ->
            return $ if null ps
                -- If no solution was found, return Nothing
                then Nothing
                -- If we have a solution, return it
                else Just (ps, pTot - (goal $ length ps))