Skip to content

Instantly share code, notes, and snippets.

@hooman
Last active August 29, 2015 14:05
Show Gist options
  • Save hooman/640477d2f0fc8047fe95 to your computer and use it in GitHub Desktop.
Save hooman/640477d2f0fc8047fe95 to your computer and use it in GitHub Desktop.
This is obsolete, please see this one: https://github.com/hooman/ArrayMultiD
// NOTE: THIS IS NOT CURRENT
// The current version is here: https://github.com/hooman/ArrayMultiD
// Playground - noun: a place where people can play
//
// ArrayMultiD.swift
// ArrayMultiD
//
// Created by Hooman Mehr (hooman@mac.com) on 8/17/14.
// Copyright (c) 2014 Hooman Mehr. New BSD License.
//
// WARNING: This is a sample code to study and to demostrate some Swift capabilities
// and limitations. Use it at your own risk.
//
/// Multi-dimensional index type for use with multi-dimensional array ArrayMultiD
/// Use ArrayMultiD index() method. The constructor is private.
public struct IndexMultiD: RandomAccessIndexType, Printable, DebugPrintable {
public typealias Distance = Int
let multipliers: [Int]
let count: Int
let offset = 0
// Internally mutable to improve performance of calcOffset:
private var lastIndexes: [Int]
private var lastDeltas: [Int]
public var description: String { get { return "\(index)" } }
public var debugDescription: String { get { return "Index(\(index), multiplier: \(multipliers), offset: \(offset), count:\(count))" } }
var index: [Int] {
get {
return calcIndexes(offset)
}
}
var counts: [Int] {
get {
return calcIndexes(count.predecessor()).map { $0.successor() }
}
}
public func distanceTo(other: IndexMultiD) -> Distance { return offset - other.offset }
public func advancedBy(dist: Distance) -> IndexMultiD
{ return IndexMultiD(offset: offset + dist, prototype: self) }
public func successor() -> IndexMultiD { return advancedBy(1) }
public func predecessor() -> IndexMultiD { return advancedBy(-1) }
public func comparableTo(other: IndexMultiD) -> Bool
{ return multipliers == other.multipliers && count == other.count }
public func with(#offset: Int) -> IndexMultiD {
return IndexMultiD(offset: offset, prototype: self)
}
public func with(#indexes: [Int]) -> IndexMultiD {
return IndexMultiD(indexes: indexes, prototype: self)
}
public func calcIndexes(offset: Int) -> [Int] {
assert(0 <= offset && offset <= count, "IndexMultiD.calcIndexes: Offset out of bounds.")
var remainder = (offset == count) ? offset.predecessor() : offset
var index: [Int] = multipliers.map { multiplier in
let i = remainder / multiplier
remainder %= multiplier
return i
}
index.append((offset == count) ? remainder.successor() : remainder)
return index
}
/// When using ArrayMultiD with traditional indexing ([i,j,k,etc]), this call becomes performance critical.
/// For this reason, I cam caching the last index delta multiplications, since we normally scan them in
/// order, hence the same upper index is used repeatedly.
public mutating func calcOffset(indexes: [Int]) -> Int {
assert(indexes.count == multipliers.count.successor(), "IndexMultiD.calcOffset: Incorrect number of dimensions")
var offset = indexes.last!
for i in 0 ..< multipliers.count {
let index = indexes[i]
let delta = (lastIndexes[i] == index) ? lastDeltas[i] : (index * multipliers[i])
lastIndexes[i] = index
lastDeltas[i] = delta
offset += delta
}
return offset
}
private init(offset: Int, var counts: [Int]) {
let end = counts.endIndex
let last = end.predecessor()
var count = counts[0]
if last > 0 {
for i in 0 ..< last {
counts[i] = counts[i + 1 ..< end].reduce(1, combine: * )
}
count *= counts[0]
counts.removeLast()
self.init(offset: offset, count: count, multipliers: counts)
} else {
self.init(offset: offset, count: count, multipliers: [])
}
}
private init(offset: Int, count: Int, multipliers: [Int]) {
assert(0 <= offset && offset <= count, "IndexMultiD.init: offset out of bounds")
self.offset = offset
self.count = count
self.multipliers = multipliers
self.lastIndexes = Array<Int>(count: multipliers.count, repeatedValue: 0)
self.lastDeltas = lastIndexes
}
private init(offset: Int, prototype: IndexMultiD) {
self = prototype
self.offset = offset
}
private init(indexes: [Int], prototype: IndexMultiD) {
self = prototype
self.offset = calcOffset(indexes)
}
}
/// Multidimensional array implementation for Swift. It is a first step and currently lacks
/// Sliceable support. It behaves like traditional arrays: You can't resize it. But it is
/// much more efficient than array of array of array implementations.
/// See example usage at the bottom.
public struct ArrayMultiD<E>: MutableCollectionType {
public typealias Generator = GeneratorOf<E>
public typealias Index = IndexMultiD
typealias Element = E
// Internally mutable by calcOffset last result caching:
public private(set) var startIndex: Index
public var endIndex: Index { get { return Index(offset: startIndex.count, prototype: startIndex) } }
var counts: [Int]
var storage: [E]
public subscript (i: Index) -> Element
{ get { return storage[i.offset] } set { storage[i.offset] = newValue } }
public subscript (i: Int...) -> Element
{ mutating get { return storage[startIndex.calcOffset(i)] } set { storage[startIndex.calcOffset(i)] = newValue } }
public subscript (i: [Int]) -> Element
{ mutating get { return storage[startIndex.calcOffset(i)] } set { storage[startIndex.calcOffset(i)] = newValue } }
public func index(i: Int...) -> Index { return index(i) }
public func index(i: [Int]) -> Index {
assert(i.count == counts.count, "MultiDimArray: Incorrect number of dimensions.")
return Index(indexes: i, prototype: startIndex)
}
public func generate() -> Generator {
var currentOffset = 0
let count = self.startIndex.count
return GeneratorOf<E> { currentOffset < count ? self.storage[currentOffset++] : nil }
}
public init(repeatedValue: E, counts: [Int]) {
self.counts = counts
self.startIndex = Index(offset: 0, counts: counts )
self.storage = Array<E>(count: startIndex.count, repeatedValue: repeatedValue)
}
public init(repeatedValue: E, counts: Int...) {
self.init(repeatedValue: repeatedValue, counts: counts)
}
}
public func <(lhs: IndexMultiD, rhs: IndexMultiD) -> Bool {
assert(lhs.comparableTo(rhs), "MultiDimIndexes are not comparable.")
return lhs.offset < rhs.offset
}
public func ==(lhs: IndexMultiD, rhs: IndexMultiD) -> Bool { return lhs.offset == rhs.offset && lhs.comparableTo(rhs) }
// Convenience operators for multidimensional array index type (IndexMultiD)
public postfix func ++(inout lhs: IndexMultiD) -> IndexMultiD {
let initialVal = lhs
lhs = lhs.successor()
return initialVal
}
public postfix func --(inout lhs: IndexMultiD) -> IndexMultiD {
let initialVal = lhs
lhs = lhs.predecessor()
return initialVal
}
public prefix func ++(inout rhs: IndexMultiD) -> IndexMultiD {
return rhs.successor()
}
public prefix func --(inout rhs: IndexMultiD) -> IndexMultiD {
return rhs.predecessor()
}
public func += (inout lhs: IndexMultiD, rhs: IndexMultiD.Distance) {
lhs = lhs.advancedBy(rhs)
}
public func -= (inout lhs: IndexMultiD, rhs: IndexMultiD.Distance) {
lhs = lhs.advancedBy(-rhs)
}
public func + (lhs: IndexMultiD, rhs: IndexMultiD.Distance) -> IndexMultiD {
return lhs.advancedBy(rhs)
}
public func - (lhs: IndexMultiD, rhs: IndexMultiD.Distance) -> IndexMultiD {
return lhs.advancedBy(-rhs)
}
// Basic usage sample:
var a0 = ArrayMultiD(repeatedValue: 0, counts: 0)
var a0d = ArrayMultiD(repeatedValue: 0, counts: 1)
var a1d = ArrayMultiD(repeatedValue: 0, counts: 2)
var a2d = ArrayMultiD(repeatedValue: 0, counts: 2, 3)
var a3d = ArrayMultiD(repeatedValue: 0, counts: 2, 3, 4)
var a4d = ArrayMultiD(repeatedValue: 0, counts: 2, 3, 4, 5)
var val = 0
for i in 0 ..< 2 {
a1d[i] = ++val
for j in 0 ..< 3 {
a2d[i,j] = ++val
for k in 0 ..< 4 {
a3d[i,j,k] = ++val
for l in 0 ..< 5 {
a4d[i,j,k,l] = ++val
}
}
}
}
a4d.endIndex.index
a4d.startIndex
a4d.endIndex.counts
println("Array 1D:")
for e in a1d {
println(e)
}
println("Array 2D:")
for e in a2d {
println(e)
}
println("Array 3D:")
for e in a3d {
println(e)
}
println("Array 4D:")
for e in a4d {
println(e)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment