             return internalIndices
         }
         
-        let subarrays = internalIndicesList.map { self[$0] }
-        return ComputedArray(DenseArray(shape: newShape, baseArray: subarrays))
+        return ComputedArray(shape: newShape, linear: { ComputedArray(self[internalIndicesList[$0]]) })
+    }
+    
+    /// Reverse the order of Elements along the last axis (columns).
+    public func reverse() -> ComputedArray<Element> {
+        return self.vectorMap(byRows: true, transform: { $0.reverse() } )
     }
     
     /// Reverse the order of Elements along the first axis.
     public func flip() -> ComputedArray<Element> {
-        return ComputedArray(DenseArray(collection: sequenceFirst.reverse()))
-    }
-    
-    /// Reverse the order of Elements along the last axis (columns).
-    public func reverse() -> ComputedArray<Element> {
-        return ComputedArray(DenseArray(collectionOnLastAxis: sequenceLast.reverse())) // Pretty sure one could do this much more efficiently with linear indexing.
+        return self.vectorMap(byRows: false, transform: { $0.reverse() } )
     }
     
     /// Returns a ComputedArray whose dimensions are reversed.
     public func transpose() -> ComputedArray<Element> {
-        let indexGenerator = makeColumnMajorIndexGenerator(shape)
-        let transposedSeq = AnySequence(AnyGenerator { () -> Element? in
-            guard let indices = indexGenerator.next() else { return nil }
-            return self[indices]
-            })
-        return transposedSeq.map { $0 } .reshape(shape.reverse())
+        return ComputedArray(shape: shape.reverse(), cartesian: {indices in
+            self[indices.reverse()]
+        })
     }
     
     /// Returns a DenseArray with the contents of additionalItems appended to the last axis of the Array.

/// The outer product is the result of  all elements of `left` and `right` being `transform`'d.
public func outer<X, Y, A: Array, B: Array where A.Element == X, B.Element == X>
    (left: A, _ right: B, product: ((X, X) -> Y)) -> ComputedArray<Y> {
        var baseArray = [Y]()
        baseArray.reserveCapacity(Int(left.allElements.count * right.allElements.count))
        
        for l in left.allElements {
            for r in right.allElements {
                baseArray.append(product(l, r))
            }
        }
        
        return ComputedArray(DenseArray(shape: left.shape + right.shape, baseArray: baseArray))
}

/// Returns the inner product of `left` and `right`, fused with `transform` and reduced by `combine`.
/// For example the dot product of A & B is defined as `inner(A, B, *, +)`.
public func inner<A: Array, B: Array, X, Y where A.Element == X, B.Element == X>
    (left: A, _ right: B, product: (ArraySlice<X>, ArraySlice<X>) -> ComputedArray<Y>, sum: (Y, Y) -> Y) -> ComputedArray<Y> {
        let enclosedA = left.enclose(left.rank - 1)
        let enclosedB = right.enclose(0)
        
        let outerProduct = outer(enclosedA, enclosedB, product: product)
        let baseArray = outerProduct.allElements.map{ $0.reduce(sum).allElements.map {$0} }.flatMap {$0}
        return ComputedArray(DenseArray(shape: outerProduct.shape, baseArray: baseArray))
}

/// Returns the inner product of `left` and `right`, fused with `transform` and reduced by `combine`.
/// For example the dot product of A & B is defined as `inner(A, B, *, 0, +)`.
public func inner<A: Array, B: Array, X, Y, Z where A.Element == X, B.Element == X>
    (left: A, _ right: B, product: (ArraySlice<X>, ArraySlice<X>) -> ComputedArray<Y>, sum: (Z, Y) -> Z, initialSum: Z) -> ComputedArray<Z> {
        let enclosedA = left.enclose(left.rank - 1)
        let enclosedB = right.enclose(0)
        
        let outerProduct = outer(enclosedA, enclosedB, product: product)
        let baseArray = outerProduct.allElements.map{ $0.reduce(initialSum, combine: sum).allElements.map {$0} }.flatMap {$0}
        return ComputedArray(DenseArray(shape: outerProduct.shape, baseArray: baseArray))
}

// MARK: - Multi-Map

/// Returns an Array with the same shape of the inputs, whose elements are the output of the transform applied to pairs of left's & right's elements.
public func zipMap<A: Array, B: Array, X, Y where A.Element == X, B.Element == X>
    (left: A, _ right: B, transform: (X, X) throws -> Y) rethrows -> ComputedArray<Y> {
        precondition(left.shape == right.shape, "Arrays must have the same shape to map a function element-wise")
    
        return try ComputedArray(DenseArray(shape: left.shape, baseArray: zip(left.allElements, right.allElements).map(transform)))
}

// Currently not exposed as part of the public API. Not sure it's useful for much else.
/// Returns an Array with a rank equal to left's and a shape equal to the sums of the shapes (offset by one if byRows = false), whose (row or highest-dimensional) vectors are the output of the transform applied to pairs of left's & right's (row or highest-dimensional) vectors.
func zipVectorMap<A: Array, B: Array, X, Y where A.Element == X, B.Element == X>(left: A, _ right: B, byRows rowVector: Bool = true, transform: ([X], [X]) throws -> [Y]) rethrows -> ComputedArray<Y> {
    if rowVector {
        guard (left.rank == right.rank || left.rank == right.rank + 1) &&
            !zip(left.shape.dropLast(), right.shape).contains(!=) else {
                fatalError("Shape of additionalItems must match the base array in all but the last dimension")
        }
    } else {
        guard (left.rank == right.rank && !zip(left.shape.dropFirst(), right.shape.dropFirst()).contains(!=)) ||
            (left.rank == right.rank + 1 && !zip(left.shape.dropFirst(), right.shape).contains(!=)) else {
                fatalError("Shape of additionalItems must match the base array in all but the first dimension")
        }
    }
    
    func internalZipVectorMap<A: Array, B: Array, X, Y where A.Element == X, B.Element == X>(left: A, _ right: B, byRows rowVector: Bool = true, transform: ([X], [X]) throws -> [Y]) rethrows -> ComputedArray<Y> {
        if let s = left.scalar, r = right.scalar {
            return try transform([s], [r]).ravel()
        }
        
        let slice = rowVector ? left.sequenceFirst : left.sequenceLast
        if let first = slice.first where first.isScalar { // Slice is a [ArraySlice<Element>], we need to know if it's constituent Arrays are themselves scalar.

    ///    1 2 | 5 6 --> 1 2 5 6
    ///    3 4 | 7 8 --> 3 4 7 8
    public func append<A: Array where A.Element == Element>(additionalItems: A) -> ComputedArray<Element> {
        return zipVectorMap(self, additionalItems, byRows: true, transform: { $0 + $1 } )
        //        guard rank == additionalItems.rank && shape.dropLast().elementsEqual(additionalItems.shape.dropLast()) ||
        //            rank == additionalItems.rank + 1 && shape.dropLast().elementsEqual(additionalItems.shape) else {
        //               fatalError("Shape of additionalItems must match the base array in all but the last dimension")
        //        }
        //
        //        var newShape = shape
        //        newShape[newShape.count - 1] += rank == additionalItems.rank ? additionalItems.shape.last! : 1
        //        return ComputedArray(shape: newShape, cartesian: { indices in
        //            if indices.last! < self.shape.last! {
        //                return self[indices]
        //            }
        //            var viewIndices = [Int](indices.dropLast()) + [indices.last! - self.shape.last!] as [Int]
        //            if self.rank != additionalItems.rank {
        //                viewIndices = [Int](viewIndices.dropLast())
        //            }
        //            return additionalItems[viewIndices]
        //        })
    }
    
    /// Returns a DenseArray with the contents of additionalItems concatenated to the first axis of the Array.
