Skip to content

Commit eb555d0

Browse files
committed
Update README and playground
1 parent 39d6b38 commit eb555d0

File tree

6 files changed

+244
-217
lines changed

6 files changed

+244
-217
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# emacs
22
*~
33

4+
# macOS noise
5+
.DS_Store
6+
47
# Xcode
58
#
69
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

EntropyString.playground/Pages/Basic Usage.xcplaygroundpage/Contents.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
//: [Previous](@previous)
22
//:## Basic Usage
3-
3+
//: Calculate the bits of entropy necessary to cover a *risk* of a **1 in a billion** chance
4+
//: of a repeat in a *total* of **1 million** strings and generate a example string using a
5+
//: set of 32 characters:
46
import EntropyString
57

6-
//: Calculate bits of entropy to cover **1 million strings** with a repeat *risk* of
7-
//: **1 in a billion** and generate a string using a set of 32 characters:
88
let bits = Entropy.bits(for: .ten06, risk: .ten09)
99
var string = RandomString.entropy(of: bits, using: .charSet32)
1010
print(string)
1111
//: * callout(string): 9Pp7MDDm7b9Dhb
1212
//:
13-
//: Generate a string of the same entropy using set of 16 characters (hexadecimal)
13+
//: Generate a string of the same entropy using set of 16 characters (hexadecimal):
1414
string = RandomString.entropy(of: bits, using: .charSet16)
1515
print(string)
1616
//: * callout(string): d33fa62f572c4cc9c8

EntropyString.playground/Pages/Custom Bytes.xcplaygroundpage/Contents.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
//: may have a need to provide your own btyes, say for deterministic testing or to use a
77
//: specialized byte genterator. The `RandomString.entropy(of:using:bytes)` function allows
88
//: passing in your own bytes to create a string.
9+
//:
10+
//: Suppose we want a string capable of 30 bits of entropy using 32 characters. We pass in 4 bytes
11+
//: (to cover the 30 bits):
912
import EntropyString
1013

1114
let bytes: RandomString.Bytes = [250, 200, 150, 100]
@@ -25,4 +28,10 @@ catch {
2528
}
2629
//: * callout(error): tooFewBytes
2730
//:
31+
//: Note how the number of bytes needed is dependent on the number of characters in our set.
32+
//: In using a string to represent entropy, we can only generate entropy in multiples of the
33+
//: bits of entropy per character used. So in the example above, to get at least 32 bits of
34+
//: entropy using a character set of 32 characters (5 bits per char), we'll need enough bytes
35+
//: to cover 35 bits, not 32, so a `tooFewBytes` error is thrown.
36+
//:
2837
//: [TOC](Table%20of%20Contents)

README.md

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,16 @@ The [Swift Package Manager](https://swift.org/package-manager/) is a decentraliz
9696
9797
[TOC](#TOC)
9898
99+
----
100+
101+
The remainer of this README is included in the project as a Swift playground for interactive exploration.
102+
103+
----
104+
105+
99106
## <a name="BasicUsage"></a>Basic Usage
100107
101-
Calculate *bits* of entropy to cover **1 million strings** with a repeat *risk* of **1 in a billion** and generate a *string* using a set of **32 characters**:
108+
Calculate the bits of entropy necessary to cover a *risk* of a **1 in a billion** chance of a repeat in a *total* of **1 million** strings and generate a example string using a set of 32 characters:
102109
103110
```swift
104111
import EntropyString
@@ -109,7 +116,7 @@ Calculate *bits* of entropy to cover **1 million strings** with a repeat *risk*
109116

110117
> 9Pp7MDDm7b9Dhb
111118
112-
Generate a *string* of the same *entropy* using a set of **16 characters** (hexadecimal):
119+
Generate a string of the same entropy using a set of 16 characters (hexadecimal):
113120

114121
```swift
115122
string = RandomString.entropy(of: bits, using: .charSet16)
@@ -135,20 +142,19 @@ We'll begin investigating `EntropyString` by considering our [Real Need](Read%20
135142

136143
## <a name="RealNeed"></a>Real Need
137144

138-
Let's start by reflecting on a common statement of need of developers, who might say:
145+
Let's start by reflecting on a common statement of need for developers, who might say:
139146

140147
*I need random strings 16 characters long.*
141148

142-
Okay. There are libraries available that address that exact need. But first, there are some
143-
questions that arise from the need as stated, such as:
149+
Okay. There are libraries available that address that exact need. But first, there are some questions that arise from the need as stated, such as:
144150

145151
1. What characters do you want to use?
146152
2. How many of these strings do you need?
147153
3. Why do you need these strings?
148154

149155
The available libraries often let you specify the characters to use. So we can assume for now that question 1 is answered with:
150156

151-
*Hexadecimal IDs will do fine*.
157+
*Hexadecimal will do fine*.
152158

153159
As for question 2, the developer might respond:
154160

@@ -168,13 +174,15 @@ Probabilistic uniqueness contains risk. That's the price we pay for giving up on
168174

169175
*I guess I can live with a 1 in a million chance of a repeat*.
170176

171-
So now we've gotten to the real need:
177+
So now we've gotten to the developer's real need:
172178

173179
*I need 10,000 random hexadecimal IDs with less than 1 in a million chance of any repeats*.
174180

175-
How do you address this need using a library designed to generate strings of specified length? Well, you don't directly, because that library was designed to answer the originally stated need, not the real need we've uncovered. We need a library that deals with probabilistic uniqueness of a total number of some strings. And that's exactly what `EntropyString` does.
181+
Not only is this statement more specific, note there is no mention of string length. The developer needs probabilistic uniqueness, and strings are to be used to capture randomness for this purpose. As such, the length of the string is simply a by-product of the encoding used to represent the required uniqueness as a string.
176182

177-
Let's use `EntropyString` to help this developer:
183+
So how do you address this need using a library designed to generate strings of specified length? Well, you don't directly, because that library was designed to answer the originally stated need, not the real need we've uncovered. We need a library that deals with probabilistic uniqueness of a total number of some strings. And that's exactly what `EntropyString` does.
184+
185+
Let's use `EntropyString` to help this developer by generating 5 IDs:
178186

179187
```swift
180188
import EntropyString
@@ -196,13 +204,13 @@ To generate the IDs, we first use
196204
let bits = Entropy.bits(total: 10000, risk: .ten06)
197205
```
198206

199-
to determine the bits of entropy needed to satisfy our probabilistic uniqueness of **10,000** strings with a **1 in a million** (ten to the sixth power) risk of repeat. We didn't print the result, but if you did you'd see it's about **45.51**. Then inside a loop we used
207+
to determine how much entropy is needed to satisfy the probabilistic uniqueness of a **1 in a million** (ten to the sixth power) risk of repeat in a total of **10,000** strings. We didn't print the result, but if you did you'd see it's about **45.51** bits. Then inside a loop we used
200208

201209
```swift
202210
let string = RandomString.entropy(of: bits, using: .charSet16)
203211
```
204212

205-
to actually generate random strings using hexadecimal (charSet16) characters. Looking at the IDs, we can see each is 12 characters long. Again, the string length is a by-product of the characters used to represent the entropy we needed. And it seems the developer didn't really need 16 characters after all.
213+
to actually generate a random string of the specified entropy using hexadecimal (charSet16) characters. Looking at the IDs, we can see each is 12 characters long. Again, the string length is a by-product of the characters used to represent the entropy we needed. And it seems the developer didn't really need 16 characters after all.
206214

207215
Finally, given that the strings are 12 hexadecimals long, each string actually has an information carrying capacity of 12 * 4 = 48 bits of entropy (a hexadecimal character carries 4 bits). That's fine. Assuming all characters are equally probable, a string can only carry entropy equal to a multiple of the amount of entropy represented per character. `EntropyString` produces the smallest strings that *exceed* the specified entropy.
208216

@@ -477,6 +485,8 @@ Rather than have `EntropyString` generate bytes automatically, you can provide y
477485

478486
As described in [Secure Bytes](#SecureBytes), `EntropyString` automatically generates random bytes using either `SecRandomCopyBuf` or `arc4random_buf`. These functions are fine, but you may have a need to provide your own btyes, say for deterministic testing or to use a specialized byte genterator. The `RandomString.entropy(of:using:bytes)` function allows passing in your own bytes to create a string.
479487

488+
Suppose we want a string capable of 30 bits of entropy using 32 characters. We pass in 4 bytes (to cover the 30 bits):
489+
480490
```swift
481491
import EntropyString
482492

@@ -500,4 +510,6 @@ The __bytes__ provided can come from any source. However, the number of bytes mu
500510

501511
> error: tooFewBytes
502512
513+
Note how the number of bytes needed is dependent on the number of characters in our set. In using a string to represent entropy, we can only generate entropy in multiples of the bits of entropy per character used. So in the example above, to get at least 32 bits of entropy using a character set of 32 characters (5 bits per char), we'll need enough bytes to cover 35 bits, not 32, so a `tooFewBytes` error is thrown.
514+
503515
[TOC](#TOC)

Sources/Entropy.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@ public struct Entropy {
7272
//
7373
/// Calculates bits of entropy
7474
///
75-
/// - parameter total: Number of items in the universal set express as a `UInt`
75+
/// - parameter total: Number of total items
7676
/// - parameter risk: Accepted probability expressed as 1 in *risk* chance of repeat
7777
///
7878
/// - return: Bits of entropy required to cover the *risk* of repeat in *total* generated items.
7979
public static func bits(for total: UInt, risk: Power) -> Float {
8080
let tPower = log10(Float(total))
8181
var N: Float
8282
if UInt(tPower) < Power.ten09.rawValue {
83-
N = log2(Float(total) * Float(total-1)) + (Float(risk.rawValue) * log2(10)) - 1
83+
N = log2(Float(total)) + log2(Float(total-1)) + (Float(risk.rawValue) * log2(10)) - 1
8484
}
8585
else {
8686
let n = 2 * tPower + Float(risk.rawValue)
@@ -91,7 +91,7 @@ public struct Entropy {
9191

9292
/// Calculates bits of entropy
9393
///
94-
/// - parameter total: Number of items in the universal set expressed a power of ten
94+
/// - parameter total: Number of total items
9595
/// - parameter risk: Accepted probability expressed as 1 in *risk* chance of repeat
9696
///
9797
/// - return: Bits of entropy required to cover the *risk* of repeat in *total* generated items.

0 commit comments

Comments
 (0)