Futures version of reading ArrayBasedDirectedGraph

ArrayBasedDirectedGraph was not very clear with `apply()` method on almost 200 lines. I've cut it into submethods and rewrite it to futures instead of ExecutorUtil's parallel works.
@szymonm No need to pass future pool around. FuturePool.unboundedPool is a lazy val on an object. i.e. it is constructed lazily, and there is only one instance. So you can unambiguously refer to it.

This is only a suggestion, but it is easier to reason if you only specify parallelism at the top level, indicating how many parallel *futures* you want to run. The number of threads is not controlled by you. For example, if the futures are doing IO, it might be that you can multiplex multiple futures onto a single thread.

So for example, if you have two functions with different concurrency requirements. The following would be the cleaner approach.

class Foo(concurrency: Int) {
val sem = new AsyncSemaphore(concurrency)
def asyncFoo() = {
  sem.acquireAndRun { FuturePool.unboundedPool { *your code here* } }
}
}

val foo1 = new Foo(10)
val foo2 = new Foo(20)

foo1 and foo2 will now reuse threads between them, while you retain control over their concurrency.