Skip to content

Commit

Permalink
strategic async calls enabled, more conformance to sendable
Browse files Browse the repository at this point in the history
  • Loading branch information
heckj committed Apr 12, 2024
1 parent 3e819e1 commit 2476bb2
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 41 deletions.
8 changes: 4 additions & 4 deletions Sources/Lindenmayer/LSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public enum LSystem: Sendable {
/// - Parameters:
/// - axiom: An initial module that represents the initial state of the Lindenmayer system..
/// - prng: An optional psuedo-random number generator to use for randomness in rule productions.
public static func create<RNGType>(_ axiom: Module, with prng: RNGType?) -> RandomContextualLSystem<RNGType> {
public static func create<RNGType: Sendable>(_ axiom: Module, with prng: RNGType?) -> RandomContextualLSystem<RNGType> {
if let prng {
return RandomContextualLSystem(axiom: [axiom], state: nil, newStateIndicators: nil, prng: RNGWrapper(prng))
}
Expand All @@ -66,7 +66,7 @@ public enum LSystem: Sendable {
/// - Parameters:
/// - axiom: A sequence of modules that represents the initial state of the Lindenmayer system..
/// - prng: An optional psuedo-random number generator to use for for randomness in rule productions.
public static func create<RNGType>(_ axiom: [Module], with prng: RNGType?) -> RandomContextualLSystem<RNGType> {
public static func create<RNGType: Sendable>(_ axiom: [Module], with prng: RNGType?) -> RandomContextualLSystem<RNGType> {
if let prng {
return RandomContextualLSystem(axiom: axiom, state: nil, newStateIndicators: nil, prng: RNGWrapper(prng))
}
Expand All @@ -78,7 +78,7 @@ public enum LSystem: Sendable {
/// - axiom: An initial module that represents the initial state of the Lindenmayer system.
/// - prng: An optional psuedo-random number generator to use for for randomness in rule productions.
/// - parameters: An instance of type you provide that the L-system provides to the rules you create for use as parameters.
public static func create<PType, RNGType>(_ axiom: Module, with prng: RNGType?, using parameters: PType) -> ParameterizedRandomContextualLSystem<PType, RNGType> {
public static func create<PType: Sendable, RNGType: Sendable>(_ axiom: Module, with prng: RNGType?, using parameters: PType) -> ParameterizedRandomContextualLSystem<PType, RNGType> {
if let prng {
return ParameterizedRandomContextualLSystem(axiom: [axiom], state: nil, newStateIndicators: nil, parameters: parameters, prng: RNGWrapper(prng), rules: [])
}
Expand All @@ -90,7 +90,7 @@ public enum LSystem: Sendable {
/// - axiom: A sequence of modules that represents the initial state of the Lindenmayer system..
/// - prng: An optional psuedo-random number generator to use for for randomness in rule productions.
/// - parameters: An instance of type you provide that the L-system provides to the rules you create for use as parameters.
public static func create<PType, RNGType>(_ axiom: [Module], with prng: RNGType?, using parameters: PType) -> ParameterizedRandomContextualLSystem<PType, RNGType> {
public static func create<PType: Sendable, RNGType: Sendable>(_ axiom: [Module], with prng: RNGType?, using parameters: PType) -> ParameterizedRandomContextualLSystem<PType, RNGType> {
if let prng {
return ParameterizedRandomContextualLSystem(axiom: axiom, state: nil, newStateIndicators: nil, parameters: parameters, prng: RNGWrapper(prng), rules: [])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,17 @@ public struct ParameterizedRandomContextualLSystem<PType, PRNG>: LindenmayerSyst
ParameterizedRandomContextualLSystem<PType, PRNG>(axiom: axiom, state: state, newStateIndicators: newItemIndicators, parameters: parameters, prng: prng, rules: rules)
}

public func reset() -> Self {
prng.resetRNG(seed: prng.seed)
public func reset() async -> Self {
await prng.resetRNG(seed: prng.seed)
return ParameterizedRandomContextualLSystem<PType, PRNG>(axiom: axiom, state: nil, newStateIndicators: nil, parameters: parameters, prng: prng, rules: rules)
}

/// Sets the seed for the pseudo-random number generator to the value you provide.
/// - Parameter seed: The seed value to set within the pseudo-random generator.
/// - Returns: The L-system with the seed value updated.
@discardableResult
public func setSeed(seed: UInt64) -> Self {
prng.resetRNG(seed: seed)
public func setSeed(seed: UInt64) async -> Self {
await prng.resetRNG(seed: seed)
return self
}

Expand All @@ -181,8 +181,8 @@ public struct ParameterizedRandomContextualLSystem<PType, PRNG>: LindenmayerSyst
/// - params: The updated value for the parameter type of the L-system.
/// - Returns: The L-system with the seed value and parameters value updated.
@discardableResult
public func set(seed: UInt64) -> Self {
prng.resetRNG(seed: seed)
public func set(seed: UInt64) async -> Self {
await prng.resetRNG(seed: seed)
return self
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,17 +128,17 @@ public struct RandomContextualLSystem<PRNG>: LindenmayerSystem where PRNG: Seede

/// Resets the L-system to it's initial state, wiping out an existing state while keeping the rules.
/// - Returns: A new L-system with it's state reset to the initial state you set when you created the L-system.
public func reset() -> Self {
prng.resetRNG(seed: prng.seed)
public func reset() async -> Self {
await prng.resetRNG(seed: prng.seed)
return RandomContextualLSystem(axiom: axiom, state: nil, newStateIndicators: nil, prng: prng, rules: rules)
}

/// Sets the seed for the pseudo-random number generator to the value you provide.
/// - Parameter seed: The seed value to set within the pseudo-random generator.
/// - Returns: The L-system with the seed value updated.
@discardableResult
public func setSeed(seed: UInt64) -> Self {
prng.resetRNG(seed: seed)
public func setSeed(seed: UInt64) async -> Self {
await prng.resetRNG(seed: seed)
return self
}
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/Lindenmayer/LindenmayerSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public protocol LindenmayerSystem: Sendable {
/// The L-system evolved by a number of iterations you provide.
/// - Parameter iterations: The number of times to evolve the L-system.
/// - Returns: The updated L-system from the number of evolutions you provided.
func evolved(iterations: Int) -> Self
func evolved(iterations: Int) async -> Self

/// Returns a new L-system after processing the current state against the rules to generate a new state sequence.
func evolve() -> Self
func evolve() async -> Self

/// Returns a new L-system reset to its original state.
func reset() -> Self
func reset() async -> Self

/// Returns a set of modules around the index location you provide.
/// - Parameter atIndex: The index location of the state of the current L-system.
Expand Down Expand Up @@ -92,7 +92,7 @@ public extension LindenmayerSystem {

var rightInstance: (any Module)? = nil
if state.count > atIndex + 1 {
let rightInstance = state[atIndex + 1]
rightInstance = state[atIndex + 1]
}

return ModuleSet(leftInstance: leftInstance, directInstance: strict, rightInstance: rightInstance)
Expand Down
10 changes: 7 additions & 3 deletions Sources/Lindenmayer/PRNG/RNGWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Foundation
///
/// - ``RNGWrapper/resetRNG(seed:)``
///
public final class RNGWrapper<PRNG> where PRNG: SeededRandomNumberGenerator {
public actor RNGWrapper<PRNG> where PRNG: SeededRandomNumberGenerator {
private var _prng: PRNG
#if DEBUG
var _invokeCount: UInt64 = 0
Expand All @@ -48,8 +48,12 @@ public final class RNGWrapper<PRNG> where PRNG: SeededRandomNumberGenerator {

/// Creates a new random number generator wrapper class with the random number generator you provide.
/// - Parameter prng: A random number generator.
public init(_ prng: PRNG) {
_prng = prng
public init(_ prng: PRNG, seed: UInt64? = nil) {
if let seedValue = seed {
_prng = PRNG(seed: seedValue)
} else {
_prng = prng
}
}

/// Returns a random float value within the range you provide.
Expand Down
58 changes: 38 additions & 20 deletions Tests/LindenmayerTests/PRNGWrapperTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,46 +53,64 @@ final class PRNGWrapperTests: XCTestCase {
XCTAssertEqual(firstResults, secondResults)
}

func testCheckingRNGReferenceType() throws {
func testCheckingRNGReferenceType() async throws {
// requires `@testable import Lindenmayer` to get to the DetailedExamples struct
let start = Examples3D.randomBush

XCTAssertEqual(start.prng.seed, 42)
XCTAssertEqual(start.prng._invokeCount, 0)
var seedValue = await start.prng.seed
var _invokeCount = await start.prng._invokeCount
XCTAssertEqual(seedValue, 42)
XCTAssertEqual(_invokeCount, 0)

let oneEv = start.evolve()
XCTAssertEqual(oneEv.prng.seed, 42)
XCTAssertEqual(oneEv.prng._invokeCount, 2)
seedValue = await start.prng.seed
_invokeCount = await start.prng._invokeCount
XCTAssertEqual(seedValue, 42)
XCTAssertEqual(_invokeCount, 2)
// print(oneEv.prng.position)

let sideTest = RNGWrapper(Xoshiro(seed: 42))
_ = sideTest.p()
_ = sideTest.p()
XCTAssertEqual(sideTest._invokeCount, 2)
XCTAssertEqual(sideTest.position, oneEv.prng.position)
_ = await sideTest.p()
_ = await sideTest.p()
_invokeCount = await sideTest._invokeCount
let sideTestPosition = await sideTest.position
let oneEvPosition = await sideTest.position
XCTAssertEqual(_invokeCount, 2)
XCTAssertEqual(sideTestPosition, oneEvPosition)

let twoEv = oneEv.evolve()
// Even though evolve is returning a new LSystem, the underlying reference to the RNG should be the same - so it
// continues to move forward as new evolutions are invoked.
XCTAssertEqual(twoEv.prng._invokeCount, 4)
start.prng.resetRNG(seed: start.prng.seed)
_invokeCount = await twoEv.prng._invokeCount
XCTAssertEqual(_invokeCount, 4)
await start.prng.resetRNG(seed: start.prng.seed)
_invokeCount = await start.prng._invokeCount
XCTAssertEqual(_invokeCount, 0)
}

func testResettingPRNG() throws {
func testResettingPRNG() async throws {
// requires `@testable import Lindenmayer` to get to the DetailedExamples struct
let start = Examples3D.randomBush

XCTAssertEqual(start.prng.seed, 42)
XCTAssertEqual(start.prng._invokeCount, 0)
var seed = await start.prng.seed
var _invokeCount = await start.prng._invokeCount
XCTAssertEqual(seed, 42)
XCTAssertEqual(_invokeCount, 0)

let oneEv = start.evolve()

XCTAssertEqual(oneEv.prng.seed, 42)
XCTAssertEqual(oneEv.prng._invokeCount, 2)
seed = await oneEv.prng.seed
_invokeCount = await oneEv.prng._invokeCount

start.prng.resetRNG(seed: start.prng.seed)
XCTAssertEqual(oneEv.prng.seed, 42)
XCTAssertEqual(oneEv.prng.position, 0)
XCTAssertEqual(oneEv.prng._invokeCount, 0)
XCTAssertEqual(seed, 42)
XCTAssertEqual(_invokeCount, 2)

await start.prng.resetRNG(seed: start.prng.seed)
let position = await oneEv.prng.position
seed = await oneEv.prng.seed
_invokeCount = await oneEv.prng._invokeCount
XCTAssertEqual(seed, 42)
XCTAssertEqual(position, 0)
XCTAssertEqual(_invokeCount, 0)
}
}

0 comments on commit 2476bb2

Please sign in to comment.