                          "I am a scalar and I have no dimensions!")
             precondition(newValue.base.elementShape == elementShape,
                          "Element shape mismatch")
-            items[itemSubrange(from: bounds)] =
-                newValue.base.items[newValue.base.itemSubrange(from: newValue.baseRange)]
-        }
-    }
-
-}
-
-// MARK: - RandomAccessCollection
+            units[unitSubrange(from: bounds)] =
+                newValue.base.units[newValue.base.unitSubrange(from: newValue.baseRange)]
+        }
+    }
+
+}
+
 extension Tensor : RandomAccessCollection {
     public typealias Index = Int
 
     /// Access a sub-tensor at index
-    public subscript(index: TensorIndex) -> Tensor<ItemType> {
+    public subscript(index: TensorIndex) -> Tensor<UnitType> {
         get {
             precondition(!isScalar || index.isEmpty, "I am a scalar and I have no dimensions!")
             let newShape = shape.dropFirst(index.count)
             let contiguousIndex = index.contiguousIndex(in: shape)
             let range = contiguousIndex..<contiguousIndex+newShape.contiguousSize as Range
-            return Tensor(shape: newShape, items: items[range])
+            return Tensor(shape: newShape, units: units[range])
         }
         set {
             precondition(!isScalar || index.isEmpty, "I am a scalar and I have no dimensions!")

    /// - parameter shape: tensor shape
    /// - parameter elements: sequence of elements in row-major order
    /// - parameter vacancySupplier
    public init<S: Sequence>(shape: TensorShape, items: S,
                             vacancySupplier supplier: (() -> ItemType)? = nil)
        where S.Iterator.Element == ItemType, S.SubSequence : Sequence,
              S.SubSequence.Iterator.Element == ItemType {
        let contiguousSize = shape.contiguousSize
        var slice = ContiguousArray(items.prefix(contiguousSize))
        /// If elements fewer than required by the shape and supplier is provided
        /// generate new elements using the supplier until vacancy is filled
        if slice.count < contiguousSize, let supplier = supplier {
            slice.reserveCapacity(shape.contiguousSize)
            slice.append(contentsOf: (0..<contiguousSize).map { _ in supplier() })
        }
        self.init(shape: shape, items: slice)
    }

    /// Allocate and initialize a tensor to a repeated value
    /// - parameter shape: tensor shape
    /// - parameter repeating: repeated value
    public init(shape: TensorShape, repeating repeatedValue: ItemType) {
        let items = ContiguousArray(repeating: repeatedValue, count: shape.contiguousSize)
        self.init(shape: shape, items: items)
    }

    /// Allocate and initialize a tensor using the factory function
    /// - parameter shape: tensor shape
    /// - parameter supplier: factory function providing values lazily
    public init(shape: TensorShape, supplier: () -> ItemType) {
        let items = ContiguousArray((0..<shape.contiguousSize).map { _ in supplier() })
        self.init(shape: shape, items: items)
    }

}

/// - TODO: Add conditional expressibility conformance in Swift 4

// MARK: - Element equality and item equality
public extension Tensor where ItemType : Equatable {

    public static func ==(lhs: Tensor<ItemType>, rhs: Tensor<ItemType>) -> Bool {
        return lhs.elementsEqual(rhs)
    }

    public func elementsEqual(_ other: Tensor<ItemType>) -> Bool {
        guard shape == other.shape else { return false }
        return shape.elementsEqual(other.shape) && items.elementsEqual(other.items)
    }

    public func itemsEqual(_ other: Tensor<ItemType>) -> Bool {
        return items.elementsEqual(other.items)
    }

}

// MARK: - Isomorphism
public extension TensorProtocol where IndexDistance == Int {

    public func isSimilar(to other: Self) -> Bool {

    }
}

public typealias TensorSlice<UnitType> =
    RangeReplaceableRandomAccessSlice<Tensor<UnitType>>

extension Tensor : RangeReplaceableCollection {

    public typealias SubSequence = TensorSlice<UnitType>

    public mutating func append(_ newElement: Tensor<UnitType>) {
        precondition(newElement.shape == elementShape, "Element shape mismatch")
        reserveCapacity(count + 1)
        units.append(contentsOf: newElement.units)
    }

    public mutating func reserveCapacity(_ minimumCapacity: Int) {
        let cap = Swift.max(units.capacity, unitCountPerElement * minimumCapacity)
        units.reserveCapacity(cap)
    }

    @discardableResult
    public mutating func remove(at index: Int) -> Tensor<UnitType> {
        precondition(indices.contains(index), "Index out of range")
        let range = unitSubrange(from: index..<index+1)
        defer { units.removeSubrange(range) }
        return Tensor(self[range])
    }

    public mutating func append<S : Sequence>(contentsOf newElements: S)
        where S.Iterator.Element == Tensor<UnitType> {
        for element in newElements {
            append(element)
        }
    }

    public mutating func append<C : Collection>(contentsOf newElements: C)
        where C.Iterator.Element == Tensor<UnitType>, C.IndexDistance == Int {
        reserveCapacity(count + newElements.count)
        for element in newElements {
            append(element)
        }
    }

    public mutating func append(contentsOf newElements: Tensor<UnitType>) {
        precondition(newElements.elementShape == elementShape, "Element shape mismatch")
        reserveCapacity(count + newElements.count)
        units.append(contentsOf: newElements.units)
    }

    public mutating func replaceSubrange<C : Collection>
        (_ subrange: Range<Int>, with newElements: C) where C.Iterator.Element == Tensor<UnitType> {
        let range = unitSubrange(from: subrange)
        let elemShape = elementShape
        units.replaceSubrange(range, with: newElements.lazy.flatMap { elem -> ContiguousArray<UnitType> in
            precondition(elemShape == elem.shape, "Element shape mismatch")
            return elem.units
        })
    }

    public subscript(bounds: Range<Int>) -> TensorSlice<UnitType> {
        get {
            precondition(isScalar,
                         "I am a scalar and I have no dimensions!")
