class MultiList
This class provides a generic n-dimensional multi-list containing any other type.
The shape of a MultiList is a list of ints specifying the length of each dimension.
Dimensions are called axes.
The count is the total number of elements in the ML.

For efficiency reasons, the MultiList class avoids copying its underlying
data as much as possible. A ML can be a read-only view that shares
data with an owner ML. The view can have
a different shape, count, and/or order of axes than its owner.
One way to construct a view is to call ml.slice

Methods that perform operation in place return "this" which allows for method chaining.
For example,
If you want to perform operations on a view instead of the owner, call

MultiList supports indexing, for example
element = ml[i,j,k]
ml[i,j,k] = element
There are plans to support syntactical multidimensional slicing, such as
ml2 = ml[a:b, c:d]
However, at the moment, you should use .slice instead

Inspiration for MultiList comes from similar libraries such as
Boost.MultiArray --
Numpy.Array --
Ruby NArray --

For further discussion on MultiLists, see the forum
or browse the source

        const maxCount = 2_100_000_000 (as int)
        const maxDimRank = 10_000_000 (as int)
        const minDimRank = 1 (as int)
    var _count as int
    var _data as T[]
    var _dimOrder as IList<of int>
        Maps the order of the axes stored internally to
        the external permuted order
    var _inverseDimOrder as IList<of int>
    var _isPermuted as bool
    var _isReadOnly as bool
    var _isReferred as bool
        If a referrer is GC'ed, _isReferred will still be true
    var _numDims as int
    var _owner as MultiList<of T>?
    var _ranges as IList<of Pair<of int>>
        For readonly multilists (aka views), _ranges is the range of elements per axis
        included in the view.
    var _shape as IList<of int>
        Shape is the size of each axis in the view
    var _strides as IList<of int>
        A stride is the number of places in _data separating
        two adjacent elements of a particular axes.
        Elements are stored in row major order.
        for equation, see
        A slice has the same strides as its owner
    var _viewCount as int?
    cue init(shape as vari int)
    cue init(shape as IList<of int>)
        Constructor for a multilists that "owns" its data
    cue init(original as MultiList<of T>, ranges as IList<of Pair<of int>>)
        The readonly view constructor returned from a slice
    cue init(shape as IList<of int>, data as T*)
        Length of data can be less than the count but not more
    get count as int
    get isPermuted as bool
    get isReadOnly as bool
    get isReferred as bool
    get numDims as int
    get owner as MultiList<of T>?
        In ml2 = ml1.slice, ml1 is the owner
        and ml2 is a view
    get shape as IList<of int>
    get toList as IList<of T>
    get view as MultiList<of T>
    pro [indices as vari int] as T
    def clone
        Returns a shallow copy
    def enumerate
    def equals(obj as Object?)
    def equals(m as MultiList<of T>)
        Equal if shapes are the same, and
        elements are in the same order.
        Ignores .isReadOnly, .isPermuted, .isReferred
    def fill(data as T*)
    def fill(start as int, data as T*)
        Throw IndexOutOfRangeException if the length of data + start exceeds .count
    def getHashCode
        As a completely mutable object, MultiList does not
        support getHashCode
    def permute(order as IList<of int>)
        Permutes the axes in place
    def reshape(shape as IList<of int>)
        Same as reshape(shape, false, false)
    def reshape(shape as IList<of int>, noCopy as bool, unsafe as bool)
        Reshapes the axes in place,
        unsafe == true will reshape the MultiList even if isReferred == true
        and the count of the shape is different than .count
        Will cause a data copy if necessary.
        If a view is copied, it's owner will be nil and .isReadOnly will be false
    def slice(ranges as vari Pair<of int>)
        Returns a readonly view of the ML.
        If ranges are provided for the first few axes,
        then the remaining ranges default to the whole axis.
        to-do: implement syntactic slicing as a property.
        This method is a placeholder.
    def toString
    def transpose
        Reverses the order of axes in place