Fix major flaws in SparseSet
This commit is contained in:
parent
8174315fd3
commit
d8ee72569e
|
|
@ -47,7 +47,7 @@ extension Nexus {
|
|||
if componentIdsByEntity[entityIdx] == nil {
|
||||
componentIdsByEntity[entityIdx] = SparseComponentIdentifierSet()
|
||||
}
|
||||
componentIdsByEntity[entityIdx]?.add(componentId, at: componentId.hashValue)
|
||||
componentIdsByEntity[entityIdx]?.insert(componentId, at: componentId.hashValue)
|
||||
|
||||
update(familyMembership: entityId)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ extension Nexus {
|
|||
let newEntityIdentifier: EntityIdentifier = newEntityIndex.identifier
|
||||
|
||||
let newEntity: Entity = Entity(nexus: self, id: newEntityIdentifier, name: name)
|
||||
entityStorage.add(newEntity, at: newEntityIndex)
|
||||
entityStorage.insert(newEntity, at: newEntityIndex)
|
||||
notify(EntityCreated(entityId: newEntityIdentifier))
|
||||
return newEntity
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ extension Nexus {
|
|||
}
|
||||
|
||||
public func has(entity entityId: EntityIdentifier) -> Bool {
|
||||
return entityStorage.has(entityId.index)
|
||||
return entityStorage.contains(entityId.index)
|
||||
}
|
||||
|
||||
public func get(entity entityId: EntityIdentifier) -> Entity? {
|
||||
|
|
@ -42,7 +42,7 @@ extension Nexus {
|
|||
let entityId: EntityIdentifier = entity.identifier
|
||||
let entityIdx: EntityIndex = entityId.index
|
||||
|
||||
guard entityStorage.remove(at: entityIdx) else {
|
||||
guard entityStorage.remove(at: entityIdx) != nil else {
|
||||
report("EntityRemove failure: no entity \(entityId) to remove")
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public extension Nexus {
|
|||
guard let members: UniformEntityIdentifiers = members(of: traits) else {
|
||||
return false
|
||||
}
|
||||
return members.has(entityId.index)
|
||||
return members.contains(entityId.index)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ private extension Nexus {
|
|||
|
||||
func add(to traits: FamilyTraitSet, entityId: EntityIdentifier, entityIdx: EntityIndex) {
|
||||
createTraitsIfNeccessary(traits: traits)
|
||||
familyMembersByTraits[traits]?.add(entityId, at: entityIdx)
|
||||
familyMembersByTraits[traits]?.insert(entityId, at: entityIdx)
|
||||
}
|
||||
|
||||
func remove(from traits: FamilyTraitSet, entityId: EntityIdentifier, entityIdx: EntityIndex) {
|
||||
|
|
|
|||
|
|
@ -55,24 +55,27 @@ public class Nexus: Equatable {
|
|||
familyMembersByTraits = [:]
|
||||
}
|
||||
|
||||
public final func clear() {
|
||||
for entity: Entity in entityStorage {
|
||||
destroy(entity: entity)
|
||||
}
|
||||
|
||||
entityStorage.clear()
|
||||
freeEntities.removeAll()
|
||||
|
||||
assert(entityStorage.isEmpty)
|
||||
assert(componentsByType.values.reduce(0) { $0 + $1.count } == 0)
|
||||
assert(componentIdsByEntity.values.reduce(0) { $0 + $1.count } == 0)
|
||||
assert(freeEntities.isEmpty)
|
||||
assert(familyMembersByTraits.values.reduce(0) { $0 + $1.count } == 0)
|
||||
|
||||
componentsByType.removeAll()
|
||||
componentIdsByEntity.removeAll()
|
||||
familyMembersByTraits.removeAll()
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
for entity: Entity in entityStorage {
|
||||
destroy(entity: entity)
|
||||
}
|
||||
|
||||
entityStorage.clear()
|
||||
freeEntities.removeAll()
|
||||
|
||||
assert(entityStorage.isEmpty)
|
||||
assert(componentsByType.values.reduce(0) { $0 + $1.count } == 0)
|
||||
assert(componentIdsByEntity.values.reduce(0) { $0 + $1.count } == 0)
|
||||
assert(freeEntities.isEmpty)
|
||||
assert(familyMembersByTraits.values.reduce(0) { $0 + $1.count } == 0)
|
||||
|
||||
componentsByType.removeAll()
|
||||
componentIdsByEntity.removeAll()
|
||||
familyMembersByTraits.removeAll()
|
||||
clear()
|
||||
}
|
||||
|
||||
// MARK: Equatable
|
||||
|
|
|
|||
|
|
@ -5,119 +5,171 @@
|
|||
// Created by Christian Treffs on 30.10.17.
|
||||
//
|
||||
|
||||
public class SparseSet<Element>: UniformStorage, Sequence {
|
||||
public typealias Index = Int
|
||||
private typealias DenseIndex = Int
|
||||
private var size: Int = 0
|
||||
private var denseIndices: ContiguousArray<Index>
|
||||
private var denseData: ContiguousArray<Element>
|
||||
private var sparse: [Index: DenseIndex]
|
||||
public class SparseSet<Element>: Sequence {
|
||||
public typealias Index = Int
|
||||
public typealias Key = Int
|
||||
|
||||
public struct Entry {
|
||||
let key: Key
|
||||
let element: Element
|
||||
}
|
||||
|
||||
private(set) var dense: ContiguousArray<Entry>
|
||||
private(set) var sparse: [Index: Key]
|
||||
|
||||
// TODO: implement
|
||||
// a) RandomAccessCollection conformance
|
||||
// b) subscript
|
||||
|
||||
public init() {
|
||||
denseIndices = ContiguousArray<Index>()
|
||||
denseData = ContiguousArray<Element>()
|
||||
sparse = [Index: DenseIndex]()
|
||||
}
|
||||
public init() {
|
||||
sparse = [Index: Key]()
|
||||
dense = ContiguousArray<Entry>()
|
||||
}
|
||||
|
||||
deinit {
|
||||
clear()
|
||||
}
|
||||
deinit {
|
||||
clear()
|
||||
}
|
||||
|
||||
public var count: Int { return size }
|
||||
public var isEmpty: Bool { return size == 0 }
|
||||
public var count: Int { return dense.count }
|
||||
public var isEmpty: Bool { return dense.isEmpty }
|
||||
public var capacity: Int { return sparse.count }
|
||||
|
||||
public func has(_ index: Index) -> Bool {
|
||||
return sparse[index] ?? Int.max < count /*&&
|
||||
denseIndices[safe: sparse[index]!] != nil*/
|
||||
}
|
||||
public func contains(_ key: Key) -> Bool {
|
||||
return find(at: key) != nil
|
||||
}
|
||||
|
||||
public func add(_ element: Element, at index: Index) {
|
||||
if has(index) {
|
||||
return
|
||||
}
|
||||
sparse[index] = count
|
||||
denseIndices.append(index)
|
||||
denseData.append(element)
|
||||
size += 1
|
||||
}
|
||||
/// Inset an element for a given key into the set in O(1).
|
||||
/// Elements at previously set keys will be replaced.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - element: the element
|
||||
/// - key: the key
|
||||
/// - Returns: true if new, false if replaced.
|
||||
@discardableResult
|
||||
public func insert(_ element: Element, at key: Key) -> Bool {
|
||||
if let (denseIndex, _) = find(at: key) {
|
||||
dense[denseIndex] = Entry(key: key, element: element)
|
||||
return false
|
||||
}
|
||||
|
||||
public func get(at index: Index) -> Element? {
|
||||
guard let sIdx: Index = sparse[index] else {
|
||||
return nil
|
||||
}
|
||||
return denseData[sIdx]
|
||||
}
|
||||
let nIndex = dense.count
|
||||
dense.append(Entry(key: key, element: element))
|
||||
sparse[key] = nIndex
|
||||
return true
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func remove(at index: Index) -> Bool {
|
||||
guard has(index), let removeIdx: DenseIndex = sparse[index] else {
|
||||
return false
|
||||
}
|
||||
/// Get the element for the given key in O(1).
|
||||
///
|
||||
/// - Parameter key: the key
|
||||
/// - Returns: the element or nil of key not found.
|
||||
public func get(at key: Key) -> Element? {
|
||||
guard let (_, element) = find(at: key) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let lastIdx: DenseIndex = count - 1
|
||||
denseIndices.swapAt(removeIdx, lastIdx)
|
||||
denseData.swapAt(removeIdx, lastIdx)
|
||||
sparse[index] = nil
|
||||
let swappedIndex: Index = denseIndices[removeIdx]
|
||||
sparse[swappedIndex] = removeIdx
|
||||
denseIndices.removeLast()
|
||||
denseData.removeLast()
|
||||
size -= 1
|
||||
if size == 0 {
|
||||
clear(keepingCapacity: false)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
public func clear(keepingCapacity: Bool = false) {
|
||||
size = 0
|
||||
denseIndices.removeAll(keepingCapacity: keepingCapacity)
|
||||
denseData.removeAll(keepingCapacity: keepingCapacity)
|
||||
sparse.removeAll(keepingCapacity: keepingCapacity)
|
||||
}
|
||||
/// Removes the element entry for given key in O(1).
|
||||
///
|
||||
/// - Parameter key: the key
|
||||
/// - Returns: removed value or nil if key not found.
|
||||
@discardableResult
|
||||
public func remove(at key: Key) -> Entry? {
|
||||
guard let (denseIndex, _) = find(at: key) else {
|
||||
|
||||
public func makeIterator() -> SparseSetIterator<Element> {
|
||||
return SparseSetIterator<Element>(self)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - SparseIterator
|
||||
public struct SparseSetIterator<Element>: IteratorProtocol {
|
||||
private let sparseSet: SparseSet<Element>
|
||||
private var iterator: IndexingIterator<ContiguousArray<Element>>
|
||||
let removed = swapRemove(at: denseIndex)
|
||||
if !dense.isEmpty && denseIndex < dense.count {
|
||||
let swappedElement = dense[denseIndex]
|
||||
sparse[swappedElement.key] = denseIndex
|
||||
}
|
||||
sparse[key] = nil
|
||||
return removed
|
||||
}
|
||||
|
||||
init(_ sparseSet: SparseSet<Element>) {
|
||||
self.sparseSet = sparseSet
|
||||
self.iterator = sparseSet.denseData.makeIterator()
|
||||
}
|
||||
public func clear(keepingCapacity: Bool = false) {
|
||||
sparse.removeAll(keepingCapacity: keepingCapacity)
|
||||
dense.removeAll(keepingCapacity: keepingCapacity)
|
||||
}
|
||||
|
||||
mutating public func next() -> Element? {
|
||||
return iterator.next()
|
||||
}
|
||||
public func makeIterator() -> SparseSetIterator<Element> {
|
||||
return SparseSetIterator<Element>(self)
|
||||
}
|
||||
|
||||
}
|
||||
/// Removes an element from the set and retuns it in O(1).
|
||||
/// The removed element is replaced with the last element of the set.
|
||||
///
|
||||
/// - Parameter denseIndex: the dense index
|
||||
/// - Returns: the element entry
|
||||
private func swapRemove(at denseIndex: Int) -> Entry {
|
||||
dense.swapAt(denseIndex, dense.count - 1)
|
||||
return dense.removeLast()
|
||||
}
|
||||
|
||||
private func find(at key: Key) -> (Int, Element)? {
|
||||
guard let denseIndex = sparse[key], denseIndex < count else {
|
||||
return nil
|
||||
}
|
||||
let entry = self.dense[denseIndex]
|
||||
guard entry.key == key else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (denseIndex, entry.element)
|
||||
}
|
||||
|
||||
// MARK: - SparseIterator
|
||||
public struct SparseSetIterator<Element>: IteratorProtocol {
|
||||
private let sparseSet: SparseSet<Element>
|
||||
private var sortedSparseIterator: IndexingIterator<[(key: SparseSet.Index, value: SparseSet.Key)]>
|
||||
|
||||
init(_ sparseSet: SparseSet<Element>) {
|
||||
self.sparseSet = sparseSet
|
||||
|
||||
let sortedSparse = sparseSet.sparse.sorted { first, next -> Bool in
|
||||
first.key < next.key
|
||||
}
|
||||
|
||||
sortedSparseIterator = sortedSparse.makeIterator()
|
||||
}
|
||||
|
||||
mutating public func next() -> Element? {
|
||||
guard let (key, _) = sortedSparseIterator.next() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return sparseSet.get(at: key)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension SparseSet.Entry: Equatable where SparseSet.Element: Equatable {
|
||||
public static func == (lhs: SparseSet.Entry, rhs: SparseSet.Entry) -> Bool {
|
||||
return lhs.element == rhs.element && lhs.key == rhs.key
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
extension SparseSet: Equatable where SparseSet.Element: Equatable {
|
||||
public static func == (lhs: SparseSet<Element>, rhs: SparseSet<Element>) -> Bool {
|
||||
return lhs.denseIndices == rhs.denseIndices &&
|
||||
lhs.denseData == rhs.denseData &&
|
||||
lhs.sparse == rhs.sparse
|
||||
return lhs.dense == rhs.dense &&
|
||||
lhs.sparse == rhs.sparse
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - specialized sparse sets
|
||||
|
||||
public class SparseEntitySet: SparseSet<Entity> {
|
||||
public typealias Index = EntityIndex
|
||||
public typealias Index = EntityIndex
|
||||
}
|
||||
|
||||
public class SparseEntityIdentifierSet: SparseSet<EntityIdentifier> {
|
||||
public typealias Index = EntityIndex
|
||||
public typealias Index = EntityIndex
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,36 +10,60 @@ import XCTest
|
|||
|
||||
class NexusTests: XCTestCase {
|
||||
|
||||
var nexus: Nexus!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
nexus = Nexus()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
nexus = nil
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testCreateEntity() {
|
||||
let nexus: Nexus = Nexus()
|
||||
XCTAssert(nexus.numEntities == 0)
|
||||
func testEntityCreate() {
|
||||
XCTAssertEqual(nexus.numEntities, 0)
|
||||
|
||||
let e0 = nexus.create()
|
||||
XCTAssert(e0.identifier.index == 0)
|
||||
XCTAssert(nexus.numEntities == 1)
|
||||
|
||||
XCTAssertEqual(e0.identifier.index, 0)
|
||||
XCTAssertEqual(nexus.numEntities, 1)
|
||||
|
||||
let e1 = nexus.create(entity: "Entity 1")
|
||||
|
||||
let e1 = nexus.create(entity: "Named e1")
|
||||
XCTAssert(e1.identifier.index == 1)
|
||||
XCTAssert(nexus.numEntities == 2)
|
||||
|
||||
XCTAssert(e0.name == nil)
|
||||
XCTAssert(e1.name == "Named e1")
|
||||
|
||||
let rE0 = nexus.get(entity: e0.identifier)!
|
||||
XCTAssert(rE0.name == e0.name)
|
||||
XCTAssert(rE0.identifier == e0.identifier)
|
||||
XCTAssert(e1.identifier.index == 1)
|
||||
XCTAssert(nexus.numEntities == 2)
|
||||
|
||||
XCTAssertNil(e0.name)
|
||||
XCTAssertEqual(e1.name, "Entity 1")
|
||||
}
|
||||
|
||||
func testEntityDestroy() {
|
||||
testEntityCreate()
|
||||
XCTAssertEqual(nexus.numEntities, 2)
|
||||
|
||||
let e1: Entity = nexus.get(entity: 1)!
|
||||
XCTAssertEqual(e1.identifier.index, 1)
|
||||
|
||||
XCTAssertTrue(nexus.destroy(entity: e1))
|
||||
XCTAssertFalse(nexus.destroy(entity: e1))
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, 1)
|
||||
|
||||
let e1Again: Entity? = nexus.get(entity: 1)
|
||||
XCTAssertNil(e1Again)
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, 1)
|
||||
|
||||
nexus.clear()
|
||||
|
||||
XCTAssertEqual(nexus.numEntities, 0)
|
||||
}
|
||||
|
||||
|
||||
func testComponentCreation() {
|
||||
let nexus: Nexus = Nexus()
|
||||
|
||||
XCTAssert(nexus.numEntities == 0)
|
||||
|
||||
let e0: Entity = nexus.create(entity: "e0")
|
||||
|
|
@ -58,7 +82,7 @@ class NexusTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testComponentDeletion() {
|
||||
let nexus = Nexus()
|
||||
|
||||
let identifier: EntityIdentifier = nexus.create(entity: "e0").identifier
|
||||
|
||||
let e0 = nexus.get(entity: identifier)!
|
||||
|
|
@ -106,7 +130,6 @@ class NexusTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testComponentUniqueness() {
|
||||
let nexus = Nexus()
|
||||
let a = nexus.create()
|
||||
let b = nexus.create()
|
||||
let c = nexus.create()
|
||||
|
|
|
|||
|
|
@ -10,13 +10,25 @@ import XCTest
|
|||
|
||||
class SparseSetTests: XCTestCase {
|
||||
|
||||
var set: SparseSet<Position>!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
set = SparseSet<Position>()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
set = nil
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testSparseSetAdd() {
|
||||
let set = SparseSet<Position>()
|
||||
|
||||
let num: Int = 100
|
||||
|
||||
for idx in 0..<num {
|
||||
let pos = Position(x: idx, y: idx)
|
||||
set.add(pos, at: idx)
|
||||
set.insert(pos, at: idx)
|
||||
}
|
||||
|
||||
XCTAssertEqual(set.count, num)
|
||||
|
|
@ -34,75 +46,355 @@ class SparseSetTests: XCTestCase {
|
|||
XCTAssertEqual(set.get(at: 100)?.x, nil)
|
||||
}
|
||||
|
||||
func testSparseSetAddNoReplace() {
|
||||
let set = SparseSet<Position>()
|
||||
func testSparseSetAddAndReplace() {
|
||||
|
||||
let p1 = Position(x: 1, y: 1)
|
||||
let p2 = Position(x: 2, y: 2)
|
||||
|
||||
set.add(p1, at: 10)
|
||||
XCTAssertTrue(set.insert(p1, at: 10))
|
||||
|
||||
XCTAssertEqual(set.get(at: 10)?.x, p1.x)
|
||||
XCTAssertEqual(set.count, 1)
|
||||
|
||||
set.add(p2, at: 10)
|
||||
XCTAssertFalse(set.insert(p2, at: 10))
|
||||
|
||||
XCTAssertEqual(set.get(at: 10)?.x, p1.x)
|
||||
XCTAssertEqual(set.get(at: 10)?.x, p2.x)
|
||||
XCTAssertEqual(set.count, 1)
|
||||
|
||||
}
|
||||
|
||||
func testSparseSetGet() {
|
||||
|
||||
let p1 = Position(x: 1, y: 1)
|
||||
|
||||
set.insert(p1, at: 10)
|
||||
|
||||
XCTAssertEqual(set.get(at: 10)?.x, p1.x)
|
||||
|
||||
XCTAssertNil(set.get(at: 33))
|
||||
|
||||
XCTAssertNotNil(set.remove(at: 10))
|
||||
|
||||
XCTAssertNil(set.get(at: 10))
|
||||
}
|
||||
|
||||
func testSparseSetRemove() {
|
||||
let set = SparseSet<Position>()
|
||||
let num: Int = 100
|
||||
|
||||
let num: Int = 7
|
||||
|
||||
for idx in 0..<num {
|
||||
let pos = Position(x: idx, y: idx)
|
||||
set.add(pos, at: idx)
|
||||
set.insert(pos, at: idx)
|
||||
XCTAssertEqual(set.sparse[idx], idx)
|
||||
XCTAssertEqual(set.dense.count, idx+1)
|
||||
}
|
||||
|
||||
XCTAssertEqual(set.count, num)
|
||||
set.remove(at: 33)
|
||||
XCTAssertEqual(set.count, num-1)
|
||||
set.remove(at: 54)
|
||||
XCTAssertEqual(set.count, num-2)
|
||||
XCTAssertEqual(set.sparse.count, num)
|
||||
XCTAssertEqual(set.dense.count, num)
|
||||
|
||||
for idx in 0..<num {
|
||||
set.remove(at: idx)
|
||||
}
|
||||
XCTAssertEqual(set.get(at: 0)?.x, 0)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, 1)
|
||||
XCTAssertEqual(set.get(at: 2)?.x, 2)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, 3)
|
||||
XCTAssertEqual(set.get(at: 4)?.x, 4)
|
||||
XCTAssertEqual(set.get(at: 5)?.x, 5)
|
||||
XCTAssertEqual(set.get(at: 6)?.x, 6)
|
||||
XCTAssertEqual(set.get(at: 7)?.x, nil)
|
||||
|
||||
XCTAssertEqual(set.sparse[0], 0)
|
||||
XCTAssertEqual(set.sparse[1], 1)
|
||||
XCTAssertEqual(set.sparse[2], 2)
|
||||
XCTAssertEqual(set.sparse[3], 3)
|
||||
XCTAssertEqual(set.sparse[4], 4)
|
||||
XCTAssertEqual(set.sparse[5], 5)
|
||||
XCTAssertEqual(set.sparse[6], 6)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
|
||||
// ---------------------------------------------
|
||||
set.remove(at: 3)
|
||||
|
||||
XCTAssertEqual(set.count, num-1)
|
||||
XCTAssertEqual(set.sparse.count, num-1)
|
||||
XCTAssertEqual(set.dense.count, num-1)
|
||||
|
||||
|
||||
XCTAssertEqual(set.get(at: 0)?.x, 0)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, 1)
|
||||
XCTAssertEqual(set.get(at: 2)?.x, 2)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 4)?.x, 4)
|
||||
XCTAssertEqual(set.get(at: 5)?.x, 5)
|
||||
XCTAssertEqual(set.get(at: 6)?.x, 6)
|
||||
XCTAssertEqual(set.get(at: 7)?.x, nil)
|
||||
|
||||
XCTAssertEqual(set.sparse[0], 0)
|
||||
XCTAssertEqual(set.sparse[1], 1)
|
||||
XCTAssertEqual(set.sparse[2], 2)
|
||||
XCTAssertEqual(set.sparse[3], nil)
|
||||
XCTAssertEqual(set.sparse[4], 4)
|
||||
XCTAssertEqual(set.sparse[5], 5)
|
||||
XCTAssertEqual(set.sparse[6], 3)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------
|
||||
set.remove(at: 2)
|
||||
|
||||
XCTAssertEqual(set.count, num-2)
|
||||
XCTAssertEqual(set.sparse.count, num-2)
|
||||
XCTAssertEqual(set.dense.count, num-2)
|
||||
|
||||
|
||||
XCTAssertEqual(set.get(at: 0)?.x, 0)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, 1)
|
||||
XCTAssertEqual(set.get(at: 2)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 4)?.x, 4)
|
||||
XCTAssertEqual(set.get(at: 5)?.x, 5)
|
||||
XCTAssertEqual(set.get(at: 6)?.x, 6)
|
||||
XCTAssertEqual(set.get(at: 7)?.x, nil)
|
||||
|
||||
XCTAssertEqual(set.sparse[0], 0)
|
||||
XCTAssertEqual(set.sparse[1], 1)
|
||||
XCTAssertEqual(set.sparse[2], nil)
|
||||
XCTAssertEqual(set.sparse[3], nil)
|
||||
XCTAssertEqual(set.sparse[4], 4)
|
||||
XCTAssertEqual(set.sparse[5], 2)
|
||||
XCTAssertEqual(set.sparse[6], 3)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------
|
||||
set.remove(at: 0)
|
||||
|
||||
XCTAssertEqual(set.count, num-3)
|
||||
XCTAssertEqual(set.sparse.count, num-3)
|
||||
XCTAssertEqual(set.dense.count, num-3)
|
||||
|
||||
|
||||
XCTAssertEqual(set.get(at: 0)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, 1)
|
||||
XCTAssertEqual(set.get(at: 2)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 4)?.x, 4)
|
||||
XCTAssertEqual(set.get(at: 5)?.x, 5)
|
||||
XCTAssertEqual(set.get(at: 6)?.x, 6)
|
||||
XCTAssertEqual(set.get(at: 7)?.x, nil)
|
||||
|
||||
XCTAssertEqual(set.sparse[0], nil)
|
||||
XCTAssertEqual(set.sparse[1], 1)
|
||||
XCTAssertEqual(set.sparse[2], nil)
|
||||
XCTAssertEqual(set.sparse[3], nil)
|
||||
XCTAssertEqual(set.sparse[4], 0)
|
||||
XCTAssertEqual(set.sparse[5], 2)
|
||||
XCTAssertEqual(set.sparse[6], 3)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------
|
||||
set.remove(at: 1)
|
||||
|
||||
XCTAssertEqual(set.count, num-4)
|
||||
XCTAssertEqual(set.sparse.count, num-4)
|
||||
XCTAssertEqual(set.dense.count, num-4)
|
||||
|
||||
XCTAssertEqual(set.get(at: 0)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 2)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 4)?.x, 4)
|
||||
XCTAssertEqual(set.get(at: 5)?.x, 5)
|
||||
XCTAssertEqual(set.get(at: 6)?.x, 6)
|
||||
XCTAssertEqual(set.get(at: 7)?.x, nil)
|
||||
|
||||
XCTAssertEqual(set.sparse[0], nil)
|
||||
XCTAssertEqual(set.sparse[1], nil)
|
||||
XCTAssertEqual(set.sparse[2], nil)
|
||||
XCTAssertEqual(set.sparse[3], nil)
|
||||
XCTAssertEqual(set.sparse[4], 0)
|
||||
XCTAssertEqual(set.sparse[5], 2)
|
||||
XCTAssertEqual(set.sparse[6], 1)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
// ---------------------------------------------
|
||||
set.remove(at: 6)
|
||||
|
||||
XCTAssertEqual(set.count, num-5)
|
||||
XCTAssertEqual(set.sparse.count, num-5)
|
||||
XCTAssertEqual(set.dense.count, num-5)
|
||||
|
||||
XCTAssertEqual(set.get(at: 0)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 2)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 4)?.x, 4)
|
||||
XCTAssertEqual(set.get(at: 5)?.x, 5)
|
||||
XCTAssertEqual(set.get(at: 6)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 7)?.x, nil)
|
||||
|
||||
XCTAssertEqual(set.sparse[0], nil)
|
||||
XCTAssertEqual(set.sparse[1], nil)
|
||||
XCTAssertEqual(set.sparse[2], nil)
|
||||
XCTAssertEqual(set.sparse[3], nil)
|
||||
XCTAssertEqual(set.sparse[4], 0)
|
||||
XCTAssertEqual(set.sparse[5], 1)
|
||||
XCTAssertEqual(set.sparse[6], nil)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
// ---------------------------------------------
|
||||
set.remove(at: 5)
|
||||
|
||||
XCTAssertEqual(set.count, num-6)
|
||||
XCTAssertEqual(set.sparse.count, num-6)
|
||||
XCTAssertEqual(set.dense.count, num-6)
|
||||
|
||||
XCTAssertEqual(set.get(at: 0)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 2)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 4)?.x, 4)
|
||||
XCTAssertEqual(set.get(at: 5)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 6)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 7)?.x, nil)
|
||||
|
||||
XCTAssertEqual(set.sparse[0], nil)
|
||||
XCTAssertEqual(set.sparse[1], nil)
|
||||
XCTAssertEqual(set.sparse[2], nil)
|
||||
XCTAssertEqual(set.sparse[3], nil)
|
||||
XCTAssertEqual(set.sparse[4], 0)
|
||||
XCTAssertEqual(set.sparse[5], nil)
|
||||
XCTAssertEqual(set.sparse[6], nil)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
// ---------------------------------------------
|
||||
set.remove(at: 4)
|
||||
|
||||
XCTAssertEqual(set.count, 0)
|
||||
XCTAssertEqual(set.sparse.count, 0)
|
||||
XCTAssertEqual(set.dense.count, 0)
|
||||
XCTAssertTrue(set.isEmpty)
|
||||
|
||||
XCTAssertEqual(set.get(at: 0)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 2)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 4)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 5)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 6)?.x, nil)
|
||||
XCTAssertEqual(set.get(at: 7)?.x, nil)
|
||||
|
||||
XCTAssertEqual(set.sparse[0], nil)
|
||||
XCTAssertEqual(set.sparse[1], nil)
|
||||
XCTAssertEqual(set.sparse[2], nil)
|
||||
XCTAssertEqual(set.sparse[3], nil)
|
||||
XCTAssertEqual(set.sparse[4], nil)
|
||||
XCTAssertEqual(set.sparse[5], nil)
|
||||
XCTAssertEqual(set.sparse[6], nil)
|
||||
XCTAssertEqual(set.sparse[7], nil)
|
||||
|
||||
}
|
||||
|
||||
func testSparseSetRemoveAndAdd() {
|
||||
testSparseSetRemove()
|
||||
|
||||
let indices: Set<Int> = [0, 30, 1, 21, 78, 56, 99, 3]
|
||||
|
||||
for idx in indices {
|
||||
let pos = Position(x: idx, y: idx)
|
||||
set.insert(pos, at: idx)
|
||||
}
|
||||
|
||||
XCTAssertEqual(set.count, indices.count)
|
||||
XCTAssertEqual(set.sparse.count, indices.count)
|
||||
XCTAssertEqual(set.dense.count, indices.count)
|
||||
XCTAssertFalse(set.isEmpty)
|
||||
|
||||
XCTAssertEqual(set.get(at: 0)?.x, 0)
|
||||
XCTAssertEqual(set.get(at: 30)?.x, 30)
|
||||
XCTAssertEqual(set.get(at: 1)?.x, 1)
|
||||
XCTAssertEqual(set.get(at: 21)?.x, 21)
|
||||
XCTAssertEqual(set.get(at: 78)?.x, 78)
|
||||
XCTAssertEqual(set.get(at: 56)?.x, 56)
|
||||
XCTAssertEqual(set.get(at: 99)?.x, 99)
|
||||
XCTAssertEqual(set.get(at: 3)?.x, 3)
|
||||
|
||||
|
||||
}
|
||||
|
||||
func testSparseSetRemoveNonPresent() {
|
||||
let set = SparseSet<Position>()
|
||||
|
||||
XCTAssertTrue(set.isEmpty)
|
||||
XCTAssertFalse(set.remove(at: 100))
|
||||
XCTAssertNil(set.remove(at: 100))
|
||||
XCTAssertTrue(set.isEmpty)
|
||||
}
|
||||
|
||||
func testSparseSetDoubleRemove() {
|
||||
class AClass { }
|
||||
let set = SparseSet<AClass>()
|
||||
let a = AClass()
|
||||
let b = AClass()
|
||||
set.insert(a, at: 0)
|
||||
set.insert(b, at: 1)
|
||||
|
||||
XCTAssertEqual(set.sparse.count, 2)
|
||||
XCTAssertEqual(set.dense.count, 2)
|
||||
|
||||
XCTAssertEqual(set.count, 2)
|
||||
|
||||
XCTAssertTrue(set.get(at: 0) === a)
|
||||
XCTAssertTrue(set.get(at: 1) === b)
|
||||
|
||||
XCTAssertEqual(set.count, 2)
|
||||
|
||||
XCTAssertNotNil(set.remove(at: 1))
|
||||
|
||||
XCTAssertEqual(set.count, 1)
|
||||
XCTAssertEqual(set.sparse.count, 1)
|
||||
XCTAssertEqual(set.dense.count, 1)
|
||||
|
||||
|
||||
XCTAssertNil(set.remove(at: 1))
|
||||
|
||||
XCTAssertEqual(set.count, 1)
|
||||
XCTAssertEqual(set.sparse.count, 1)
|
||||
XCTAssertEqual(set.dense.count, 1)
|
||||
|
||||
|
||||
XCTAssertTrue(set.get(at: 0) === a)
|
||||
|
||||
XCTAssertEqual(set.count, 1)
|
||||
|
||||
}
|
||||
|
||||
func testSparseSetNonCongiuousData() {
|
||||
let set = SparseSet<Position>()
|
||||
|
||||
|
||||
var indices: Set<Int> = [0, 30, 1, 21, 78, 56, 99, 3]
|
||||
|
||||
for idx in indices {
|
||||
let pos = Position(x: idx, y: idx)
|
||||
set.add(pos, at: idx)
|
||||
set.insert(pos, at: idx)
|
||||
}
|
||||
|
||||
XCTAssertEqual(set.count, indices.count)
|
||||
|
||||
|
||||
func recurseValueTest() {
|
||||
for idx in indices {
|
||||
XCTAssertEqual(set.get(at: idx)?.x, idx)
|
||||
XCTAssertEqual(set.get(at: idx)?.y, idx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
recurseValueTest()
|
||||
|
||||
while let idx = indices.popFirst() {
|
||||
set.remove(at: idx)
|
||||
let entry = set.remove(at: idx)!
|
||||
XCTAssertEqual(entry.key, idx)
|
||||
recurseValueTest()
|
||||
XCTAssertEqual(set.count, indices.count)
|
||||
}
|
||||
|
|
@ -112,7 +404,7 @@ class SparseSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSparseSetClear() {
|
||||
let set = SparseSet<Position>()
|
||||
|
||||
let num: Int = 100
|
||||
|
||||
XCTAssertEqual(set.count, 0)
|
||||
|
|
@ -120,7 +412,7 @@ class SparseSetTests: XCTestCase {
|
|||
|
||||
for idx in 0..<num {
|
||||
let pos = Position(x: idx, y: idx)
|
||||
set.add(pos, at: idx)
|
||||
set.insert(pos, at: idx)
|
||||
}
|
||||
|
||||
XCTAssertEqual(set.count, num)
|
||||
|
|
@ -136,17 +428,21 @@ class SparseSetTests: XCTestCase {
|
|||
let characters = SparseSet<Character>()
|
||||
|
||||
|
||||
characters.add("H", at: 4)
|
||||
characters.add("e", at: 13)
|
||||
characters.add("l", at: 34)
|
||||
characters.add("l", at: 44)
|
||||
characters.add("o", at: 55)
|
||||
characters.add(" ", at: 66)
|
||||
characters.add("W", at: 77)
|
||||
characters.add("o", at: 89)
|
||||
characters.add("r", at: 90)
|
||||
characters.add("l", at: 123)
|
||||
characters.add("d", at: 140)
|
||||
characters.insert("H", at: 4)
|
||||
characters.insert("e", at: 13)
|
||||
|
||||
characters.insert("l", at: 44)
|
||||
characters.insert("o", at: 89)
|
||||
|
||||
characters.insert(" ", at: 66)
|
||||
characters.insert("d", at: 140)
|
||||
characters.insert("W", at: 77)
|
||||
|
||||
characters.insert("r", at: 90)
|
||||
characters.insert("l", at: 123)
|
||||
characters.insert("o", at: 55)
|
||||
characters.insert("l", at: 34)
|
||||
|
||||
|
||||
XCTAssertEqual(characters.count, 11)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue