Implement memory ranges

To accommodate memory ranges, the weight balancing algorithm is
completely overhauled.

Each way in which a memory size may be specified is equivalent to a
range. Omitting a memory size is equivalent to specifying the range
`0..`. Specifying a single value is equivalent to specifying the
corresponding degenerate range.

In the four dimensional space of memory types (heap, permgen/maxspace,
stack, and native), the Cartesian product of memory ranges is a (four
dimensional) box B, all of whose edges start at a non-negative memory
size but may extend to plus infinity in any dimension if the
corresponding range is unbounded.

The total available memory ($MEMORY_LIMIT) defines a hyperplane T in
which each point has memory size coordinates which sum to the total
available memory. T separates the whole space into two half spaces.
The half space containing the origin consists of points which consume
less than the total available memory. The other half space consists of
points which consume more than the available memory.

The memory weightings describe a line W which intersects T at a single
point D (for default), which is the Cartesian product of the default
memory sizes (in the absence of any memory ranges).

In non-error cases, T intersects B in a "hypersegment" S of points
which lie within all the memory ranges and have total memory equal to
the total available memory. Note that S is convex.

The weight balancing algorithm aims to find the closest point of S to
the point D. If D lies within S, then the closest point is D itself.
If D lies outside S, then since S is convex, there is a unique point
in S which is closest to D.

The weight balancing algorithm works as follows. It first sets a
search point x to the point D. If x lies within S, the algorithm
terminates. If x lies outside S, the algorithm determines in which
dimensions x lies outside S. x is moved by constraining x to B in
those dimensions. This effectively drops a perpendicular onto B in
those dimensions. These dimensions of x are now fixed for the
remainder of the algorithm and the corresponding memory sizes are
deemed to have been allocated. If there are no remaining dimensions,
the algorithm terminates. Otherwise, the remaining dimensions of x are
recalculated by balancing the remaining memory among the remaining
dimensions. x now either lies within S in those dimensions or it does
not, in which case the above steps are repeated.

It remains to be proved that this algorithm always determines the
unique point in S which is closest to D.

Native memory is allowed to have an upper bound. Even though this
isn't particularly useful, there is no need to prohibit it and this
simplifies the documentation.

Stack size handling is improved. If a range of stack sizes is
specified and the floor of the range is non-zero, the floor is used as
the default stack size. Otherwise, the JVM default stack size is used.
Also, the number of threads, determined from the weighted proportion
of total available memory for the stack divided by the default stack
size, is rounded and, if necessary, adjusted upwards to one to behave
better in edge cases. Working in terms of a speculative number of
threads is not ideal, but it does allow stack ranges and sizes to be
converted back and forth between individual stack size and total stack
memory.

Memory weightings are generalised so that they need no longer add up
to 1.

The MemorySize type is extended to cope with a zero memory size with
no unit.

[#58060090]