public FordFulkerson(FlowNetwork G, int s, int t) {
        if (s < 0 || s >= G.V()) {
            throw new IndexOutOfBoundsException("Source s is invalid: " + s);
        }
        if (t < 0 || t >= G.V()) {
            throw new IndexOutOfBoundsException("Sink t is invalid: " + t);
        }
        if (s == t) {
            throw new IllegalArgumentException("Source equals sink");
        }
        value = excess(G, t);
        if (!isFeasible(G, s, t)) {
            throw new IllegalArgumentException("Initial flow is infeasible");
        }

        // while there exists an augmenting path, use it
        while (hasAugmentingPath(G, s, t)) {

            // compute bottleneck capacity
            double bottle = Double.POSITIVE_INFINITY;
            for (int v = t; v != s; v = edgeTo[v].other(v)) {
                bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v));
            }

            // augment flow
            for (int v = t; v != s; v = edgeTo[v].other(v)) {
                edgeTo[v].addResidualFlowTo(v, bottle); 
            }

            value += bottle;
        }

        // check optimality conditions
        assert check(G, s, t);
    }