Last active
August 29, 2015 14:05
-
-
Save hooman/640477d2f0fc8047fe95 to your computer and use it in GitHub Desktop.
This is obsolete, please see this one: https://github.com/hooman/ArrayMultiD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 ([email protected]) 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