Wiki

Ticket #288: MultiListUpdate1.patch

File MultiListUpdate1.patch, 20.1 KB (added by jaegs, 12 years ago)
  • Source/Cobra.Core/MultiList.cobra

     
    1 namespace Cobra.CoreX 
     1namespace Cobra.Core 
    22 
    3     class MultiList<of T> 
     3    class MultiList<of A> 
    44        """ 
    5         This class provides an n-dimensional multi-list container. 
     5        This class provides a generic n-dimensional multi-list containing any other type. 
     6        The shape of a MultiList is a list of ints specifying the length of each dimension. 
     7        Dimensions are called axes. 
     8        The count is the total number of elements in the ML. 
    69 
    7         All methods that return MultiList<of T> are in place except for slice  
    8         which returns a readonly view and clone which returns a shallow copy. 
     10        For efficiency reasons, the MultiList class avoids copying its underlying  
     11        data as much as possible. A ML can be a read-only view that shares 
     12        data with an owner ML. The view can have 
     13        a different shape, count, and/or order of axes than its owner. 
     14        One way to construct a view is to call ml.slice 
    915 
    10         It is inspired by similar libraries such as  
     16        Methods that perform operation in place return "this" which allows for method chaining. 
     17        For example, 
     18            ml.permute(order).reshape(shape).transpose 
     19        If you want to perform operations on a view instead of the owner, call 
     20            ml.view.permute(order).reshape(shape).transpose 
     21 
     22        MultiList supports indexing, for example 
     23            element = ml[i,j,k] 
     24            ml[i,j,k] = element 
     25        There are plans to support syntactical multidimensional slicing, such as 
     26            ml2 = ml[a:b, c:d] 
     27        However, at the moment, you should use .slice instead 
     28         
     29        Inspiration for MultiList comes from similar libraries such as  
    1130        Boost.MultiArray -- http://www.boost.org/doc/libs/1_50_0/libs/multi_array/doc/reference.html 
    1231        Numpy.Array -- http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html 
    1332        Ruby NArray -- http://narray.rubyforge.org/SPEC.en 
     33        MATLAB -- http://www.mathworks.com/products/matlab/ 
    1434 
    15         Implementing more methods would for the most part not affect existing code 
    16         Some methods that would have changes are 
     35        For further discussion on MultiLists, see the forum 
     36        http://cobra-language.com/forums/viewtopic.php?f=4&t=974 
     37        """ 
     38        /# 
     39        --Notes-- 
     40        to-do: 
     41        (1) Implement syntactic multidimensional slicing as a property. 
     42        (3) Could potentially support ml[a:b:c, d:e:f] 
     43         
     44        Implementing more methods would for the most part not affect existing code. 
     45        The ability to write generic methods will open up a lot of possibilities 
     46        Some methods that would have changes, however, are 
    1747            Reversing an axis -- http://docs.scipy.org/doc/numpy/reference/generated/numpy.flipud.html#numpy.flipud 
    18             Removing single length dimensions -- http://docs.scipy.org/doc/numpy/reference/generated/numpy.squeeze.html#numpy.squeeze 
     48            Removing single length axes -- http://docs.scipy.org/doc/numpy/reference/generated/numpy.squeeze.html#numpy.squeeze 
    1949            Roll -- http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html#numpy.roll 
    20         """ 
    2150 
     51        Another thing to consider is making a Matrix<of A> sublcass that is a 2-dim MultiList. 
     52        #/ 
     53 
    2254        const minDimRank = 1 
    2355        const maxDimRank = 10_000_000 
    2456        const maxCount = 2_100_000_000 
    2557 
    26         var _owner as MultiList<of T>? 
    27         var _data as T[] 
     58        var _owner as MultiList<of A>? 
     59        var _data as A[] 
    2860        var _shape as IList<of int> 
    29             """ Shape is the size of each dimension in the view """ 
     61            """ Shape is the size of each axis in the view """ 
    3062 
    3163        var _dimOrder as IList<of int> 
    3264            """ 
    33             Maps the order of the dimensions stored internally to 
     65            Maps the order of the axes stored internally to 
    3466            the external permuted order 
    3567            """ 
    3668        var _inverseDimOrder as IList<of int> 
    3769        var _strides as IList<of int> 
    3870            """ 
    3971            A stride is the number of places in _data separating 
    40             two adjacent elements of a particular dimension. 
     72            two adjacent elements of a particular axes. 
     73            Elements are stored in row major order. 
     74            for equation, see 
     75            http://en.wikipedia.org/wiki/Row-major_order#Generalization_to_higher_dimensions      
    4176            A slice has the same strides as its owner 
    4277            """ 
    43  
    4478        var _ranges as IList<of Pair<of int>> 
    4579            """ 
    46             For readonly multilists (aka views), _ranges is the range of elements per dimension 
     80            For readonly multilists (aka views), _ranges is the range of elements per axis 
    4781            included in the view. 
    4882            """ 
    4983 
    5084        var _isReadOnly = false 
    5185        var _isPermuted = false 
    5286        var _isReferred = false  
    53             """ If a referrer is GC'ed, _isReferred will still be true """ 
     87            """If a referrer is GC'ed, _isReferred will still be true""" 
    5488 
    5589        var _count = 1 
    5690 
     
    69103                base.init 
    70104                _shape = shape.clone 
    71105                _computeFields 
    72                 _dimOrder = for i in _numDims get i  
    73                 _inverseDimOrder = _dimOrder.clone 
    74                 _data = T[](_count) 
     106                _data = A[](_count) 
    75107 
    76108        cue init(shape as vari int) 
    77109            body 
    78110                .init(shape.toList) 
    79111 
    80         cue init(shape as IList<of int>, data as T*) 
     112        cue init(shape as IList<of int>, data as A*) 
     113            """ Length of data can be less than the count but not more""" 
    81114            body 
    82115                .init(shape) 
    83116                .fill(data) 
    84117 
    85         cue init(owner as MultiList<of T>, ranges as IList<of Pair<of int>>) is private 
     118        cue init(original as MultiList<of A>, ranges as IList<of Pair<of int>>) is private 
    86119            """ 
    87120            The readonly view constructor returned from a slice 
    88121            """      
    89122            require 
    90                 ranges.count == owner._numDims 
     123                ranges.count == original._numDims 
    91124                all for r in ranges.count get _ 
    92125                    ranges[r][0] >= 0 _ 
    93126                    and ranges[r][1] > ranges[r][0] _ 
    94                     and ranges[r][1] - ranges[r][0] <= owner._shape[r] 
     127                    and ranges[r][1] - ranges[r][0] <= original._shape[r] 
     128            ensure 
     129                not _owner.isReadOnly 
    95130            body 
    96131                base.init 
    97                 _owner = owner 
    98                 _data = owner._data 
     132                _owner = original.owner 
     133                _data = original._data 
    99134                _isReadOnly = true 
    100                 _isPermuted = owner._isPermuted 
     135                _isPermuted = original._isPermuted 
    101136                _shape = for range in ranges get range[1] - range[0] 
    102                 _dimOrder = owner._dimOrder.clone 
    103                 _inverseDimOrder = owner._inverseDimOrder.clone 
    104                 _strides = owner._strides 
     137                _dimOrder = original._dimOrder.clone 
     138                _inverseDimOrder = original._inverseDimOrder.clone 
     139                _strides = original._strides 
    105140                _numDims = _shape.count 
    106141                _ranges = ranges.clone 
    107                 _count = owner._count 
     142                _count = original._count 
    108143                _viewCount = 1 
    109144                for s in _shape, _viewCount *= s 
    110145 
    111         cue enumerate as T* 
     146        cue enumerate as A* 
    112147            for index in _generateIndices 
    113148                yield this[index] 
    114149 
     
    126161 
    127162        get isReferred from var 
    128163 
    129         get owner as MultiList<of T>? 
     164        get view as MultiList<of A> 
     165            """Syntactic placeholder for this[:] """ 
     166            return .slice 
     167 
     168        get owner as MultiList<of A>? 
     169            """ 
     170            In ml2 = ml1.slice, ml1 is the owner  
     171            and ml2 is a view 
     172            """ 
    130173            return _owner ? this 
    131174 
    132         pro [indices as vari int] as T 
     175        get toList as IList<of A> 
     176            return .enumerate.toList 
     177 
     178        pro [indices as vari int] as A 
    133179            get 
    134180                require 
    135181                    indices.length == .numDims 
     
    142188                body 
    143189                    _data[_address(indices)] = value 
    144190         
    145         def fill(start as int, data as T*) as MultiList<of T> 
     191        def fill(start as int, data as A*) as MultiList<of A> 
    146192            """ 
    147193            Throw IndexOutOfRangeException if the length of data + start exceeds .count 
    148194            """ 
     
    160206                assert index > 0 
    161207                return this 
    162208 
    163         def fill(data as T*) as MultiList<of T> 
     209        def fill(data as A*) as MultiList<of A> 
    164210            return .fill(0, data) 
    165211 
    166         def _computeStrides 
     212             
     213        def _computeFields 
    167214            """ 
    168             Elements are stored in row major order. 
    169             For equation, see 
    170             http://en.wikipedia.org/wiki/Row-major_order#Generalization_to_higher_dimensions 
     215            Computes/Recomputes _numDims, _strides, _count, ranges, 
     216            _dimOrder, and _inverseDimOrder for 
     217            non-sliced, non-permuted ML 
    171218            """ 
    172             require not _isReadOnly 
    173             _strides = [1] 
    174             for s in _numDims -1 : 0 : -1 
    175                 _strides.insert(0, _strides[0] * _shape[s]) 
     219            require 
     220                not .isPermuted 
     221            ensure 
     222                _count < .maxCount 
     223                _count >= 1 
     224            body 
     225                _numDims = _shape.count 
    176226 
    177         def _computeRanges 
    178             """ 
    179             This computes ranges for owners where the range is the whole dimension  
    180             """ 
    181             require not _isReadOnly 
    182             _ranges = for s in _shape get Pair<of int>(0, s) 
     227                _strides = [1] 
     228                for s in _numDims -1 : 0 : -1 
     229                    _strides.insert(0, _strides[0] * _shape[s]) 
     230                     
     231                for s in _shape, _count *= s 
     232                #ranges - Each range is the whole axis  
     233                _ranges = for s in _shape get Pair<of int>(0, s) 
     234                _dimOrder = for i in _numDims get i  
     235                _inverseDimOrder = _dimOrder.clone 
    183236 
    184         def _computeCount 
    185             require not _isReadOnly 
    186             for s in _shape, _count *= s 
    187  
    188         def _computeFields 
    189             _numDims = _shape.count 
    190             _computeStrides 
    191             _computeRanges 
    192             _computeCount 
    193  
    194237        def _inversePermuteList(lst as IList<of int>) as IList<of int> 
    195238            """ 
    196             Reorders the elements in a list according to _dimOrder 
     239            Reorders the elements in a list according to _inverseDimOrder 
    197240            For example the indices the users gives are in permuted order 
    198241            But the data is stored in the original order. 
    199242            """ 
     
    225268                    if index < 0 or index >= _shape[i] 
    226269                        dim = _dimOrder[i] 
    227270                        throw IndexOutOfRangeException( 
    228                             'Array index [index] is out of range 0 : [_shape[dim]] for dimension [dim].') 
     271                            'Array index [index] is out of range 0 : [_shape[dim]] for axis [dim].') 
    229272                    addr += (index + _ranges[i][0]) * _strides[i] 
    230273                assert addr >= 0 and addr < _count 
    231274                return addr 
     
    253296                            indices[pos] = index 
    254297                            yield indices 
    255298 
    256         def clone as MultiList<of T> 
    257             return .clone(_shape) 
     299        def clone as MultiList<of A> 
     300            """ Returns a shallow copy """ 
     301            return MultiList<of A>(_shape, .enumerate) 
    258302 
    259         def clone(shape as IList<of int>) as MultiList<of T> 
    260             """ Same as ml2 = MultiList<of T>(shape, ml1.enumerate) """ 
    261             return MultiList<of T>(shape, .enumerate) 
    262  
    263         def slice(ranges as vari Pair<of int>) as MultiList<of T> 
     303        def slice(ranges as vari Pair<of int>) as MultiList<of A> 
    264304            """ 
    265305            Returns a readonly view of the ML. 
    266             If ranges are provided for the first few dimensions, 
    267             then the remaining ranges default to the whole dimension.  
     306            If ranges are provided for the first few axes, 
     307            then the remaining ranges default to the whole axis.  
    268308            ml.slice() 
     309            to-do: implement syntactic slicing as a property. 
     310            This method is a placeholder. 
    269311            """ 
    270312            _isReferred = true 
    271313            completedRanges = _ranges.clone 
     
    276318                lowerBound = completedRanges[r][0] + range[0] 
    277319                upperBound = completedRanges[r][0] + range[1] 
    278320                completedRanges[r] = Pair<of int>(lowerBound, upperBound) 
    279             return MultiList<of T>(this, completedRanges) 
     321            return MultiList<of A>(this, completedRanges) 
    280322 
    281         def permute(order as IList<of int>) as MultiList<of T> 
     323        def permute(order as IList<of int>) as MultiList<of A> 
    282324            """ 
    283             Permutes the dimensions in place 
     325            Permutes the axes in place 
    284326            """ 
    285327            _isPermuted = true 
    286328            _dimOrder = for o in order.count get _dimOrder[order[o]]  
    287329            _inverseDimOrder = for d in _numDims get _dimOrder.indexOf(d) 
    288330            return this 
    289331 
    290         def transpose as MultiList<of T> 
    291             """ Reverses the dimensions in place""" 
     332        def transpose as MultiList<of A> 
     333            """ Reverses the order of axes in place""" 
    292334            return .permute(for d in _numDims get _numDims - d - 1) 
    293335             
    294336        def toString as String is override 
     
    310352            sb.append(String(c']', _numDims)) 
    311353            return sb.toString 
    312354 
    313         def reshape(shape as IList<of int>) as MultiList<of T> 
    314             """ Same as reshape(shape, false) """ 
    315             return .reshape(shape, false) 
    316              
    317         def reshape(shape as IList<of int>, unsafe as bool) as MultiList<of T> 
     355        def reshape(shape as IList<of int>) as MultiList<of A> 
     356            """ Same as reshape(shape, false, false) """ 
     357            return .reshape(shape, false, false) 
     358 
     359        def _truncate(stream as A*, length) as A* 
     360            count = 0 
     361            for e in stream 
     362                if count == length, break 
     363                count += 1 
     364                yield e 
     365                 
     366        def reshape(shape as IList<of int>, noCopy as bool, unsafe as bool) as MultiList<of A> 
    318367            """ 
    319             Reshapes the dimension in place, 
     368            Reshapes the axes in place, 
    320369            unsafe == true will reshape the MultiList even if isReferred == true 
    321             Throws InvalidOperationException if size of new shape differs from  
    322             current shape. 
     370            and the count of the shape is different than .count 
     371            Will cause a data copy if necessary. 
     372            If a view is copied, it's owner will be nil and .isReadOnly will be false 
    323373            """ 
    324374            require 
    325                 not .isReadOnly 
    326                 unsafe or not .isReferred 
    327375                all for s in shape get s >= .minDimRank and s <= .maxDimRank 
    328376            body 
    329                 # The order in which things are done in this method 
    330                 # is very important 
    331377                newCount = 1 
    332378                for s in shape, newCount *= s 
    333                 if newCount <> .count 
     379                if newCount <> .owner.count and .isReferred and not unsafe 
    334380                    throw InvalidOperationException( 
    335                         "New shape = [shape] with count [newCount] must have same count [.count] as previous shape [.shape]") 
    336                 mustCopy = .isPermuted 
    337                 if mustCopy 
    338                     # Construct a ML with the new shape, 
    339                     # and copy its fields 
     381                    ".isReferred == true. To reshape, new shape = [shape] with count [newCount] " + _ 
     382                    "must have same count [.count] as previous shape [.shape]. " + _ 
     383                    "Call .reshape(shape, true) to bypass this restriction.")        
     384                else if newCount <> .owner.count and .isReadOnly 
     385                    throw InvalidOperationException( 
     386                    ".isReadOnly == true. To reshape, new shape = [shape] with count [newCount] " + _ 
     387                    "must have same count [.owner.count] as owner with shape = [.owner.shape].")             
     388                else if newCount == .owner.count and not .isPermuted 
     389                    _shape = shape 
     390                    _computeFields 
     391                    _isPermuted = false 
     392                else if noCopy 
     393                    throw InvalidOperationException( 
     394                    "To reshape to from [.shape] to [shape], " + _ 
     395                    "data must be copied but noCopy == true. " + _ 
     396                    "Call .reshape(shape, true, [unsafe]) to permit copy.") 
     397                else 
     398                    # Construct a ML with the new shape, and copy its fields 
    340399                    # This procedure reuses a lot of code 
    341                     copy = .clone(shape) 
     400                    # The constructor expects a stream with size <= .count 
     401                    copy = MultiList<of A>(shape, _truncate(.enumerate, newCount)) 
    342402                    _shape = shape 
    343403                    _numDims = _shape.count 
    344404                    _data = copy._data 
    345405                    _isPermuted = false 
     406                    _owner = nil 
    346407                    _dimOrder = copy._dimOrder.clone 
    347408                    _inverseDimOrder = copy._inverseDimOrder.clone 
    348409                    _strides = copy._strides 
    349410                    _ranges = copy._ranges.clone 
    350411                    _count = copy._count 
    351                 else 
    352                     _shape = shape 
    353                     _computeFields 
    354412                return this 
    355413 
    356414        def getHashCode as int is override 
     
    360418            """ 
    361419            throw InvalidOperationException() 
    362420 
    363         def equals(m as MultiList<of T>) as bool 
     421        def equals(m as MultiList<of A>) as bool 
    364422            """  
    365423            Equal if shapes are the same, and 
    366424            elements are in the same order. 
     
    369427            if m.count <> .count, return false 
    370428            if m._shape <> _shape, return false 
    371429            for indices in _generateIndices 
    372                 if not this[indices] == m[indices], return false 
     430                if this[indices] <> m[indices], return false 
    373431            return true 
    374432 
    375433        def equals(obj as Object?) as bool is override 
    376             if obj inherits MultiList<of T>, return .equals(obj) 
     434            if obj inherits MultiList<of A>, return .equals(obj) 
    377435            return false 
    378436 
    379437 
     
    421479 
    422480        test 
    423481            # count, slice, permute, toString 
    424             ml = MultiList<of int>([3,4,2], for i in 24 get i+3) 
     482            ml = MultiList<of int>([3, 4, 2], for i in 24 get i+3) 
    425483            assert ml.toString == _ 
    426484                r"[[[3, 4], [5, 6], [7, 8], [9, 10]], [[11, 12], [13, 14], [15, 16], [17, 18]], [[19, 20], [21, 22], [23, 24], [25, 26]]]" 
    427485 
    428486            ml2 = ml.slice(@[Pair<of int>(0,2), Pair<of int>(1,3)]) 
    429487            assert ml2.toString == r'[[[5, 6], [7, 8]], [[13, 14], [15, 16]]]' 
    430488 
    431             ml2.permute([1,0,2]) 
     489            ml2.permute([1, 0, 2]) 
    432490            assert ml2.toString == r'[[[5, 6], [13, 14]], [[7, 8], [15, 16]]]' 
    433491            assert ml2.count == 8 
    434492             
    435             ml3 = ml.slice(@[Pair<of int>(0,2), Pair<of int>(1,3)]) 
    436             assert ml3.owner == ml 
     493            ml3 = ml.slice(@[Pair<of int>(0, 2), Pair<of int>(1, 3)]) 
    437494 
    438             ml3.permute([0,2,1]) 
    439             ml3.permute([2,1,0]) 
    440             assert ml3.slice(Pair<of int>(0,1), Pair<of int>(0,1)).toString == r'[[[5, 13]]]' 
    441             assert ml3.slice(Pair<of int>(0,1), Pair<of int>(0,1)).shape == [1,1,2] 
    442 # 2012-07-31 Could not install Cobra without commenting out the following line, which failed 
    443 # issue is that installation builds Cobra.Core with -turbo 
    444 # see discussion at http://cobra-language.com/forums/viewtopic.php?f=4&t=974&p=4966#p4966 
    445 #           expect AssertException, ml3[1,1,1] = 5 
     495            ml3.permute([0, 2, 1]) 
     496            ml3.permute([2, 1, 0]) 
     497            assert ml3.slice(Pair<of int>(0, 1), Pair<of int>(0, 1)).toString == r'[[[5, 13]]]' 
     498            assert ml3.slice(Pair<of int>(0, 1), Pair<of int>(0, 1)).shape == [1, 1, 2] 
     499# 2012-07-31 Could not install Cobra without commenting out the following line, which failed  
     500# issue is that installation builds Cobra.Core with -turbo  
     501# see discussion at http://cobra-language.com/forums/viewtopic.php?f=4&t=974&p=4966#p4966  
     502#           expect AssertException, ml3[1,1,1] = 5           
    446503 
    447504            assert ml2.equals(ml2) 
    448             assert ml2.clone.equals(ml.slice(Pair<of int>(0,2), Pair<of int>(1,3)).permute([1,0,2])) 
     505            assert ml2.clone.equals(ml.slice(Pair<of int>(0, 2), Pair<of int>(1, 3)).permute([1, 0, 2])) 
    449506            assert ml3.clone.equals(ml3) 
    450507            assert not ml2.equals(ml3) 
    451508 
    452509        test 
    453510            # fill 
    454             ml = MultiList<of int>(4,3,2).fill(5, [1,2,3,4,5,6,7,8]) 
     511            ml = MultiList<of int>(4, 3, 2).fill(5, [1, 2, 3, 4, 5, 6, 7, 8]) 
    455512            assert ml.toString == _ 
    456513                r'[[[0, 0], [0, 0], [0, 1]], [[2, 3], [4, 5], [6, 7]], [[8, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]]]' 
    457             ml1 = MultiList<of Pair<of int>>([2,2]).fill([Pair<of int>(0,2), Pair<of int>(1,3)]) 
    458             ml2 = MultiList<of Pair<of int>>([2,2], [Pair<of int>(0,2), Pair<of int>(1,3)]) 
     514            ml1 = MultiList<of Pair<of int>>([2, 2]).fill([Pair<of int>(0, 2), Pair<of int>(1, 3)]) 
     515            ml2 = MultiList<of Pair<of int>>([2, 2], [Pair<of int>(0, 2), Pair<of int>(1, 3)]) 
    459516            assert ml1.equals(ml2) 
    460517 
    461518        test 
    462519            # clone, transpose 
    463             ml = MultiList<of int>([3,4,2], for i in 24 get i+3) 
    464             assert ml.clone.transpose.equals(ml.permute([2,1,0])) 
     520            ml = MultiList<of int>([3, 4, 2], for i in 24 get i+3) 
     521            assert ml.clone.transpose.equals(ml.permute([2, 1, 0])) 
    465522 
    466523        test 
    467524            # owner  
    468             ml1 = MultiList<of int>([3,4,2], for i in 24 get i+3) 
    469             assert ml1.owner == ml1 
    470             ml3 = ml1.slice(@[Pair<of int>(0,2), Pair<of int>(1,3)]) 
    471             assert ml3.owner == ml1 
     525            # This test must check for physical equality  
     526            ml1 = MultiList<of int>([3, 4, 2], for i in 24 get i+3) 
     527            assert ml1.owner is ml1 
     528            ml3 = ml1.slice(@[Pair<of int>(0, 2), Pair<of int>(1, 3)]) 
     529            assert ml3.owner is ml1 
     530            ml4 = ml3.slice 
     531            assert ml4.owner is ml1 
     532            ml4 = ml4 #suppress warning 
    472533 
    473534        test 
    474535            # reshape 
    475             ml = MultiList<of int>([3,4,2], for i in 24 get i+3)     
    476             ml.reshape([2,4,3]) 
     536            ml = MultiList<of int>([3, 4, 2], for i in 24 get i+3)   
     537            ml.reshape([2, 4, 3]) 
    477538            assert ml.toString == _ 
    478539                r'[[[3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]], [[15, 16, 17], [18, 19, 20], [21, 22, 23], [24, 25, 26]]]' 
    479540 
    480             ml2 = MultiList<of int>([3,4,2], for i in 24 get i+3)    
    481             expect InvalidOperationException, ml2.transpose.reshape([3,4]) 
     541            ml2 = MultiList<of int>([3, 4, 2], for i in 24 get i+3).transpose.reshape([3, 4]) 
     542            assert ml2.toString == _ 
     543                r'[[3, 11, 19, 5], [13, 21, 7, 15], [23, 9, 17, 25]]' 
    482544 
    483             ml = MultiList<of int>([3,4,2], for i in 24 get i+3)     
    484             assert ml.transpose.reshape([4,3,2]).toString == _ 
     545            ml3 = MultiList<of int>([3, 4, 2], for i in 24 get i+3)  
     546            assert ml3.transpose.reshape([4, 3, 2]).toString == _ 
    485547                r'[[[3, 11], [19, 5], [13, 21]], [[7, 15], [23, 9], [17, 25]], [[4, 12], [20, 6], [14, 22]], [[8, 16], [24, 10], [18, 26]]]' 
     548 
     549            ml4 = MultiList<of int>([3, 4, 2], for i in 24 get i+3).view.reshape([2, 4, 3]) 
     550            assert ml4.toString == _     
     551                r'[[[3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]], [[15, 16, 17], [18, 19, 20], [21, 22, 23], [24, 25, 26]]]' 
     552 
     553            expect InvalidOperationException, MultiList<of int>([3, 4, 2], for i in 24 get i+3).view.transpose.reshape([2, 4, 3], true, false) 
     554 
     555            expect InvalidOperationException, MultiList<of int>([3, 4, 2]).view.reshape([1, 2]) 
     556            ml5 = MultiList<of int>([3, 4, 2]) 
     557            ml6 = ml5.view #make .isReferred = true 
     558            ml6 = ml6 #suppresss warning 
     559            expect InvalidOperationException, ml5.reshape([1, 2])