Skip to content

Commit

Permalink
Merge pull request #27 from YOCKOW/development
Browse files Browse the repository at this point in the history
Refactor memoizables.
  • Loading branch information
YOCKOW committed Apr 3, 2020
2 parents 5cae9e4 + e01da0d commit 7ed1f1c
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 34 deletions.
21 changes: 19 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,32 @@ jobs:
uses: actions/cache@v1
with:
path: .build
key: build-${{ github.workspace }}-${{ runner.os }}-${{ matrix.swift-compat-ver }}-${{ hashFiles('Package.*') }}
key: build-${{ github.workspace }}-${{ runner.os }}-${{ matrix.swift-compat-ver }}-${{ hashFiles('Package.*') }}-${{ hashFiles('Sources/**/*.swift') }}
restore-keys: |
build-${{ github.workspace }}-${{ runner.os }}-${{ matrix.swift-compat-ver }}-${{ hashFiles('Package.*') }}-
build-${{ github.workspace }}-${{ runner.os }}-${{ matrix.swift-compat-ver }}-
build-${{ github.workspace }}-${{ runner.os }}-
build-${{ github.workspace }}-
- uses: YOCKOW/Action-setup-swift@master
with:
swift-version: '5.1.4'
swift-version: '5.2'
- name: Try to build products with debug mode.
run: |
swift build --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
if [ $? != 0 ]; then
echo "Failed to build products with debug mode."
rm -rf $(cd .build/debug && pwd -P)
fi
continue-on-error: true
- name: Test with debug mode.
run: swift test --configuration debug -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
- name: Try to build products with release mode.
run: |
swift build --configuration release -Xswiftc -enable-testing -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
if [ $? != 0 ]; then
echo "Failed to build products with release mode."
rm -rf $(cd .build/release && pwd -P)
fi
continue-on-error: true
- name: Test with release mode.
run: swift test --configuration release -Xswiftc -enable-testing -Xswiftc -swift-version -Xswiftc ${{ matrix.swift-compat-ver }}
80 changes: 52 additions & 28 deletions Sources/Ranges/Memoizables.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,76 @@
Licensed under MIT License.
See "LICENSE.txt" for more information.
************************************************************************************************ */

/// Immutable multiple ranges.
/// Results can be memoized.
public final class MemoizableMultipleRanges<Bound> where Bound: Comparable & Hashable {
private var _memoized: [Bound: Bool] = [:]
private var _multipleRanges: MultipleRanges<Bound>

public init(_ ranges: MultipleRanges<Bound>) {
self._multipleRanges = ranges
}

public func contains(_ value: Bound) -> Bool {
func _contains(_ value: Bound) -> Bool {
let result = self._multipleRanges.contains(value)
self._memoized[value] = result
return result
}
return self._memoized[value] ?? _contains(value)
}
}


/// Immutable range-dictionary.
/// Results can be memoized.
public final class MemoizableRangeDictionary<Bound, Value> where Bound: Comparable & Hashable {
private var _memoized: [Bound: Value?] = [:]
private var _recentPairs: ArraySlice<RangeDictionary<Bound, Value>._Pair> = []
private var _rangeDictionary: RangeDictionary<Bound, Value>

public init(_ rangeDictionary: RangeDictionary<Bound, Value>) {
self._rangeDictionary = rangeDictionary
}

public subscript(_ element: Bound) -> Value? {
func _value() -> Value? {
let result = self._rangeDictionary[element]
self._memoized[element] = result
return result
private func _memoizedValue(for bound: Bound) -> Value?? {
return self._memoized[bound]
}

private func _recentValue(for bound: Bound) -> Value? {
for pair in self._recentPairs {
if pair.range.contains(bound) { return pair.value }
}

return nil
}

private func _appendRecentPair(_ pair: RangeDictionary<Bound, Value>._Pair) {
self._recentPairs.append(pair)
self._recentPairs = self._recentPairs.suffix(3)
}

private func _valueWithMemoizing(for bound: Bound) -> Value? {
if let index = self._rangeDictionary._index(whereRangeContains: bound) {
let pair = self._rangeDictionary._rangesAndValues[index]
self._memoized[bound] = pair.value
self._appendRecentPair(pair)
return pair.value
} else {
self._memoized[bound] = Optional<Value>.none
return nil
}
}

public subscript(_ element: Bound) -> Value? {
switch self._memoized[element] {
case Optional<Value?>.none:
return _value()
if let value = self._recentValue(for: element) {
self._memoized[element] = value
return value
}
return _valueWithMemoizing(for: element)
case Optional<Value?>.some(.none):
return nil
case Optional<Value?>.some(.some(let value)):
return value
}
}
}



/// Immutable multiple ranges.
/// Results can be memoized.
public final class MemoizableMultipleRanges<Bound> where Bound: Comparable & Hashable {
private var _memoized: MemoizableRangeDictionary<Bound, Void>

public init(_ ranges: MultipleRanges<Bound>) {
self._memoized = .init(ranges._rangeDictionary)
}

public func contains(_ value: Bound) -> Bool {
return self._memoized[value] != nil
}
}


2 changes: 1 addition & 1 deletion Sources/Ranges/MultipleRanges.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ private func _bitCastArrayNoneToVoid<T>(_ array: Array<AnyRange<T>>) -> Array<(A

/// Represents multiple ranges.
public struct MultipleRanges<Bound> where Bound: Comparable {
private var _rangeDictionary: RangeDictionary<Bound, Void>
internal var _rangeDictionary: RangeDictionary<Bound, Void>

private init(_ rangeDictionary: RangeDictionary<Bound, Void>) {
self._rangeDictionary = rangeDictionary
Expand Down
6 changes: 3 additions & 3 deletions Sources/Ranges/RangeDictionary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
*/
public struct RangeDictionary<Bound, Value> where Bound: Comparable {
fileprivate typealias _Pair = (range: AnyRange<Bound>, value: Value)
internal typealias _Pair = (range: AnyRange<Bound>, value: Value)

/// Must be always sorted with the ranges.
fileprivate private(set) var _rangesAndValues: [_Pair]
internal private(set) var _rangesAndValues: [_Pair]
private func _twoRanges(from index: Int) -> (AnyRange<Bound>, AnyRange<Bound>) {
let (pair0, pair1) = self._rangesAndValues._twoElements(from: index)
return (pair0.range, pair1.range)
Expand Down Expand Up @@ -82,7 +82,7 @@ public struct RangeDictionary<Bound, Value> where Bound: Comparable {
}
}

private func _index(whereRangeContains element: Bound) -> Int? {
internal func _index(whereRangeContains element: Bound) -> Int? {
func _binarySearch<C>(_ collection: C, _ element: Bound) -> Int?
where C: Collection, C.Index == Int, C.Element == _Pair
{
Expand Down
2 changes: 2 additions & 0 deletions Tests/RangesTests/MemoizablesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ final class MemoizablesTests: XCTestCase {
XCTAssertTrue(ranges.contains(555))
XCTAssertFalse(ranges.contains(5555))
XCTAssertTrue(ranges.contains(5)) // again
XCTAssertTrue(ranges.contains(7)) // near
}

func test_dictionary() {
Expand All @@ -30,5 +31,6 @@ final class MemoizablesTests: XCTestCase {
XCTAssertEqual(dictionary[25], "20")
XCTAssertEqual(dictionary[10000], nil)
XCTAssertEqual(dictionary[5], "0") // again
XCTAssertEqual(dictionary[7], "0") // near
}
}

0 comments on commit 7ed1f1c

Please sign in to comment.