Skip to content

Commit

Permalink
Add _stdlib_random for more platforms (#1)
Browse files Browse the repository at this point in the history
* Remove refs to Countable ranges

* Add `_stdlib_random` for more platforms

* Use `getrandom` (if available) for Android, Cygwin

* Reorder the `_stdlib_random` functions

* Also include <features.h> on Linux

* Add `#error TODO` in `_stdlib_random` for Windows

* Colon after Fatal Error

Performance improvement for Random

gybify ranges

Fix typo in 'basic random numbers'
Add _stdlib_random as a testable method

Switch to generic constraints

Hopefully link against bcrypt
  • Loading branch information
benrimmington authored and Azoy committed Mar 25, 2018
1 parent a53bec5 commit 8fd12eb
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 158 deletions.
16 changes: 14 additions & 2 deletions stdlib/public/core/Bool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,23 @@ public struct Bool {
/// random Boolean.
/// - Returns: A random Boolean.
@_inlineable
public static func random(
using generator: RandomNumberGenerator = Random.default
public static func random<T: RandomNumberGenerator>(
using generator: T
) -> Bool {
return generator.next() % 2 == 0
}

/// Returns a random Boolean
///
/// - Parameter generator: The random number generator to use when getting a
/// random Boolean.
/// - Returns: A random Boolean.
///
/// This uses the standard library's default random number generator.
@_inlineable
public static func random() -> Bool {
return Bool.random(using: Random.default)
}
}

extension Bool : _ExpressibleByBuiltinBooleanLiteral, ExpressibleByBooleanLiteral {
Expand Down
4 changes: 4 additions & 0 deletions stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
${EXECINFO_LIBRARY})
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
list(APPEND swift_core_link_flags "$ENV{SystemRoot}/system32/bcrypt.dll")
endif()

option(SWIFT_CHECK_ESSENTIAL_STDLIB
"Check core standard library layering by linking its essential subset"
FALSE)
Expand Down
51 changes: 0 additions & 51 deletions stdlib/public/core/ClosedRange.swift
Original file line number Diff line number Diff line change
Expand Up @@ -459,57 +459,6 @@ extension ClosedRange where Bound: Strideable, Bound.Stride : SignedInteger {
}
}

extension ClosedRange
where Bound : FixedWidthInteger,
Bound.Magnitude : UnsignedInteger {

/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
@_inlineable
public func random(
using generator: RandomNumberGenerator = Random.default
) -> Element? {
let isLowerNegative = Bound.isSigned && lowerBound < 0
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
var delta: Bound.Magnitude
if isLowerNegative {
delta = sameSign
? lowerBound.magnitude - upperBound.magnitude
: lowerBound.magnitude + upperBound.magnitude
} else {
delta = upperBound.magnitude - lowerBound.magnitude
}
if delta == Bound.Magnitude.max {
return Bound(truncatingIfNeeded: generator.next() as Bound.Magnitude)
}
delta += 1
let randomMagnitude = generator.next(upperBound: delta)
if sameSign {
return lowerBound + Bound(randomMagnitude)
} else {
return Bound.isSigned && randomMagnitude <= upperBound.magnitude
? Bound(randomMagnitude)
: 0 - Bound(randomMagnitude - upperBound.magnitude)
}
}
}

extension ClosedRange {
@_inlineable
public func overlaps(_ other: ClosedRange<Bound>) -> Bool {
Expand Down
35 changes: 30 additions & 5 deletions stdlib/public/core/Collection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ public protocol Collection: Sequence where SubSequence: Collection {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
func random(using generator: RandomNumberGenerator) -> Element?
func random<T: RandomNumberGenerator>(using generator: T) -> Element?

@available(*, deprecated, message: "all index distances are now of type Int")
typealias IndexDistance = Int
Expand Down Expand Up @@ -1029,18 +1029,43 @@ extension Collection {
/// }
/// // Could print "20", perhaps
@_inlineable
public func random(
using generator: RandomNumberGenerator = Random.default
public func random<T: RandomNumberGenerator>(
using generator: T
) -> Element? {
guard !isEmpty else { return nil }
let random = generator.next(upperBound: UInt(self.count))
let random = generator.next(upperBound: UInt(count))
let index = self.index(
self.startIndex,
startIndex,
offsetBy: numericCast(random)
)
return self[index]
}

/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
///
/// This uses the standard library's default random number generator.
@_inlineable
public func random() -> Element? {
return random(using: Random.default)
}

/// Do not use this method directly; call advanced(by: n) instead.
@_inlineable
@_versioned
Expand Down
33 changes: 28 additions & 5 deletions stdlib/public/core/CollectionAlgorithms.swift
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ extension MutableCollection where Self : BidirectionalCollection {
}

//===----------------------------------------------------------------------===//
// shuffled()
// shuffled()/shuffle()
//===----------------------------------------------------------------------===//

extension Sequence {
Expand All @@ -271,13 +271,25 @@ extension Sequence {
/// the sequence.
/// - Returns: A shuffled array of this sequence's elements.
@_inlineable
public func shuffled(
using generator: RandomNumberGenerator = Random.default
public func shuffled<T: RandomNumberGenerator>(
using generator: T
) -> [Element] {
var result = ContiguousArray(self)
result.shuffle(using: generator)
return Array(result)
}

/// Returns the elements of the sequence, shuffled.
///
/// - Parameter generator: The random number generator to use when shuffling
/// the sequence.
/// - Returns: A shuffled array of this sequence's elements.
///
/// This uses the standard library's default random number generator.
@_inlineable
public func shuffled() -> [Element] {
return shuffled(using: Random.default)
}
}

extension MutableCollection {
Expand All @@ -286,8 +298,8 @@ extension MutableCollection {
/// - Parameter generator: The random number generator to use when shuffling
/// the collection.
@_inlineable
public mutating func shuffle(
using generator: RandomNumberGenerator = Random.default
public mutating func shuffle<T: RandomNumberGenerator>(
using generator: T
) {
guard count > 1 else { return }
var amount = count
Expand All @@ -302,6 +314,17 @@ extension MutableCollection {
formIndex(after: &currentIndex)
}
}

/// Shuffles the collection in place.
///
/// - Parameter generator: The random number generator to use when shuffling
/// the collection.
///
/// This uses the standard library's default random number generator.
@_inlineable
public mutating func shuffle() {
shuffle(using: Random.default)
}
}

//===----------------------------------------------------------------------===//
Expand Down
17 changes: 15 additions & 2 deletions stdlib/public/core/FloatingPoint.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -2390,9 +2390,9 @@ where Self.RawSignificand : FixedWidthInteger,
/// the random floating point.
/// - Returns: A random representation of this floating point.
@_inlineable
public static func random(
public static func random<T: RandomNumberGenerator>(
in range: ${Range}<Self>,
using generator: RandomNumberGenerator = Random.default
using generator: T
) -> Self {
% if 'Closed' not in Range:
_precondition(
Expand All @@ -2418,6 +2418,19 @@ where Self.RawSignificand : FixedWidthInteger,
return delta * unitRandom + range.lowerBound
}

/// Returns a random representation of this floating point within the range.
///
/// - Parameter range: A ${Range} to determine the bounds to get a random value
/// from.
/// - Parameter generator: The random number generator to use when getting
/// the random floating point.
/// - Returns: A random representation of this floating point.
///
/// This uses the standard library's default random number generator.
@_inlineable
public static func random(in range: ${Range}<Self>) -> Self {
return Self.random(in: range, using: Random.default)
}
}

% end
Expand Down
111 changes: 109 additions & 2 deletions stdlib/public/core/Integers.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -2346,6 +2346,99 @@ ${assignmentOperatorComment(x.operator, False)}

% for Range in ['Range', 'ClosedRange']:

extension ${Range}
where Bound: FixedWidthInteger,
Bound.Magnitude: UnsignedInteger {

/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
@_inlineable
public func random<T: RandomNumberGenerator>(
using generator: T
) -> Element? {
% if 'Closed' not in Range:
guard lowerBound != upperBound else {
return nil
}
% end
let isLowerNegative = Bound.isSigned && lowerBound < 0
let sameSign = !Bound.isSigned || isLowerNegative == (upperBound < 0)
% if 'Closed' not in Range:
let delta: Bound.Magnitude
% else:
var delta: Bound.Magnitude
% end
if isLowerNegative {
delta = sameSign
? lowerBound.magnitude - upperBound.magnitude
: lowerBound.magnitude + upperBound.magnitude
} else {
delta = upperBound.magnitude - lowerBound.magnitude
}
% if 'Closed' in Range:
if delta == Bound.Magnitude.max {
return Bound(truncatingIfNeeded: generator.next() as Bound.Magnitude)
}
delta += 1
% end
let randomMagnitude = generator.next(upperBound: delta)
if sameSign {
return lowerBound + Bound(randomMagnitude)
} else {
% if 'Closed' not in Range:
return randomMagnitude < upperBound.magnitude
? Bound(randomMagnitude)
: -1 - Bound(randomMagnitude - upperBound.magnitude)
% else:
return Bound.isSigned && randomMagnitude <= upperBound.magnitude
? Bound(randomMagnitude)
: 0 - Bound(randomMagnitude - upperBound.magnitude)
% end
}
}

/// Returns a random element from this collection.
///
/// - Parameter generator: The random number generator to use when getting
/// a random element.
/// - Returns: A random element from this collection.
///
/// A good example of this is getting a random greeting from an array:
///
/// let greetings = ["hi", "hey", "hello", "hola"]
/// let randomGreeting = greetings.random()
///
/// If the collection is empty, the value of this function is `nil`.
///
/// let numbers = [10, 20, 30, 40, 50]
/// if let randomNumber = numbers.random() {
/// print(randomNumber)
/// }
/// // Could print "20", perhaps
///
/// This uses the standard library's default random number generator.
@_inlineable
public func random() -> Element? {
return self.random(using: Random.default)
}
}

extension FixedWidthInteger
where Self.Stride : SignedInteger,
Self.Magnitude : UnsignedInteger {
Expand All @@ -2358,9 +2451,9 @@ where Self.Stride : SignedInteger,
/// the random integer.
/// - Returns: A random representation of this integer.
@_inlineable
public static func random(
public static func random<T: RandomNumberGenerator>(
in range: ${Range}<Self>,
using generator: RandomNumberGenerator = Random.default
using generator: T
) -> Self {
% if 'Closed' not in Range:
_precondition(
Expand All @@ -2370,6 +2463,20 @@ where Self.Stride : SignedInteger,
% end
return range.random(using: generator)!
}

/// Returns a random representation of this integer within the range.
///
/// - Parameter range: A ${Range} to determine the bounds to get a random value
/// from.
/// - Parameter generator: The random number generator to use when getting
/// the random integer.
/// - Returns: A random representation of this integer.
///
/// This uses the standard library's default random number generator.
@_inlineable
public static func random(in range: ${Range}<Self>) -> Self {
return Self.random(in: range, using: Random.default)
}
}

% end
Expand Down

0 comments on commit 8fd12eb

Please sign in to comment.