This repository has been archived by the owner on Jul 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathutil.go
291 lines (261 loc) · 7.81 KB
/
util.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fountain
import (
"math"
"math/rand"
"sort"
)
// Note that these CDFs (cumulative distribution function) will be used for
// selecting source blocks for code block generation. A typical algorithm
// is to choose a number from a distribution, then pick uniformly that many
// source blocks to XOR into a code block. To use CDF mapping values, pick a
// random number r (0 <= r < 1) and then find the smallest i such that
// CDF[i] >= r.
// solitonDistribution returns a CDF mapping for the soliton distribution.
// N (the number of elements in the CDF) cannot be less than 1
// The CDF is one-based: the probability of picking 1 from the distribution
// is CDF[1].
func solitonDistribution(n int) []float64 {
cdf := make([]float64, n+1)
cdf[1] = 1 / float64(n)
for i := 2; i < len(cdf); i++ {
cdf[i] = cdf[i-1] + (1 / (float64(i) * float64(i-1)))
}
return cdf
}
// robustSolitonDistribution returns a CDF mapping for the robust solition
// distribution.
// This is an addition to the soliton distribution with three parameters,
// N, M, and delta.
// Before normalization, the correction pdf(i) = 1/i*M, for i=1..M-1,
// pdf(M) = ln(N/(M*delta))/M
// pdf(i) = 0 for i = M+1..N
// These values are added to the ideal soliton distribution, and then the
// result normalized.
// The CDF is one-based: the probability of picking 1 from the distribution
// is CDF[1].
func robustSolitonDistribution(n int, m int, delta float64) []float64 {
pdf := make([]float64, n+1)
pdf[1] = 1/float64(n) + 1/float64(m)
total := pdf[1]
for i := 2; i < len(pdf); i++ {
pdf[i] = (1 / (float64(i) * float64(i-1)))
if i < m {
pdf[i] += 1 / (float64(i) * float64(m))
}
if i == m {
pdf[i] += math.Log(float64(n)/(float64(m)*delta)) / float64(m)
}
total += pdf[i]
}
cdf := make([]float64, n+1)
for i := 1; i < len(pdf); i++ {
pdf[i] /= total
cdf[i] = cdf[i-1] + pdf[i]
}
return cdf
}
// onlineSolitionDistribution returns a soliton-like distribution for
// Online Codes
// See http://pdos.csail.mit.edu/~petar/papers/maymounkov-bigdown-lncs.ps
// 'Rateless Codes and Big Downloads' by Maymounkov and Mazieres
// The distribution is described by a parameter epsilon, which is the
// the "overage factor" required to reconstruct the source message in an
// Online Code.
// F = ciel(ln(eps^2/4 / ln(1 - eps/2))
// and the pdf is pdf[1] = 1 - (1 + 1/F)/(1 + eps)
// pdf[i] = ((1 - pdf[1])F) / ((F-1)i(i-1)) for 2 <= i <= F
func onlineSolitonDistribution(eps float64) []float64 {
f := math.Ceil(math.Log(eps*eps/4) / math.Log(1-(eps/2)))
cdf := make([]float64, int(f+1))
// First coefficient is 1 - ( (1 + 1/f) / (1+e) )
rho := 1 - ((1 + (1 / f)) / (1 + eps))
cdf[1] = rho
// Subsequent i'th coefficient is (1-rho)*F / (F-1)i*(i-1)
for i := 2; i <= int(f); i++ {
rhoI := ((1 - rho) * f) / ((f - 1) * float64(i-1) * float64(i))
cdf[i] = cdf[i-1] + rhoI
}
return cdf
}
// pickDegree returns the smallest index i such that cdf[i] > r
// (r a random number from the random generator)
// cdf must be sorted in ascending order.
func pickDegree(random *rand.Rand, cdf []float64) int {
r := random.Float64()
d := sort.SearchFloat64s(cdf, r)
if cdf[d] > r {
return d
}
if d < len(cdf)-1 {
return d + 1
} else {
return len(cdf) - 1
}
}
// sampleUniform picks num numbers from [0,max) uniformly.
// There will be no duplicates.
// If num >= max, simply returns a slice with all indices from 0 to max-1
// without touching the random number generator.
// The returned slice is sorted.
func sampleUniform(random *rand.Rand, num, max int) []int {
if num >= max {
picks := make([]int, max)
for i := 0; i < max; i++ {
picks[i] = i
}
return picks
}
picks := make([]int, num)
seen := make(map[int]bool)
for i := 0; i < num; i++ {
p := random.Intn(max)
for seen[p] {
p = random.Intn(max)
}
picks[i] = p
seen[p] = true
}
sort.Ints(picks)
return picks
}
// partition is the block partitioning function from RFC 5053 S.5.3.1.2
// See http://tools.ietf.org/html/rfc5053
// Partitions a number i (a size) into j semi-equal pieces. The details are
// in the return values: there are jl longer pieces of size il, and js shorter
// pieces of size is.
func partition(i, j int) (il int, is int, jl int, js int) {
il = int(math.Ceil(float64(i) / float64(j)))
is = int(math.Floor(float64(i) / float64(j)))
jl = i - (is * j)
js = j - jl
if jl == 0 {
il = 0
}
if js == 0 {
is = 0
}
return
}
// factorial calculates the factorial (x!) of the input argument.
func factorial(x int) int {
f := 1
for i := 1; i <= x; i++ {
f *= i
}
return f
}
// centerBinomial calculates choose(x, ceil(x/2)) = x!/(x/2)!(x-(x/2)m!)
func centerBinomial(x int) int {
return choose(x, x/2)
}
// choose calculates (n k) or n choose k. Tolerant of quite large n/k.
func choose(n int, k int) int {
if k > n/2 {
k = n - k
}
numerator := make([]int, n-k)
denominator := make([]int, n-k)
for i, j := k+1, 1; i <= n; i, j = i+1, j+1 {
numerator[j-1] = int(i)
denominator[j-1] = j
}
if len(denominator) > 0 {
// find the first member of numerator not in denominator
z := sort.SearchInts(numerator, denominator[len(denominator)-1])
if z > 0 {
numerator = numerator[z+1:]
denominator = denominator[0 : len(denominator)-z-1]
}
}
for j := len(denominator) - 1; j > 0; j-- {
for i := len(numerator) - 1; i >= 0; i-- {
if numerator[i]%denominator[j] == 0 {
numerator[i] /= denominator[j]
denominator[j] = 1
break
}
}
}
f := 1
for _, i := range numerator {
f *= i
}
return f
}
// bitSet returns true if x has the b'th bit set
func bitSet(x uint, b uint) bool {
return (x>>b)&1 == 1
}
// bitsSet returns how many bits in x are set.
// This algorithm basically uses shifts and ANDs to sum up the bits in
// a tree fashion.
func bitsSet(x uint64) int {
x -= (x >> 1) & 0x5555555555555555
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333)
x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f
return int((x * 0x0101010101010101) >> 56)
}
// grayCode calculates the gray code representation of the input argument
// The Gray code is a binary representation in which successive values differ
// by exactly one bit. See http://en.wikipedia.org/wiki/Gray_code
func grayCode(x uint64) uint64 {
return (x >> 1) ^ x
}
// buildGraySequence returns a sequence (in ascending order) of "length" Gray numbers,
// all of which have exactly "b" bits set.
func buildGraySequence(length int, b int) []int {
s := make([]int, length)
i := 0
for x := uint64(0); ; x++ {
g := grayCode(x)
if bitsSet(g) == b {
s[i] = int(g)
i++
if i >= length {
break
}
}
}
return s
}
// isPrime tests x for primality. Works on numbers less than the square of
// the largest smallPrimes array.
func isPrime(x int) bool {
for _, p := range smallPrimes {
if p*p > x {
return true
}
if x%p == 0 {
return false
}
}
// Well, not really, but we don't know any higher primes for sure.
return true
}
// smallestPrimeGreaterOrEqual returns the smallest prime greater than or equal to x
// TODO(gbillock): should handle up to 70000 or so
func smallestPrimeGreaterOrEqual(x int) int {
if x <= smallPrimes[len(smallPrimes)-1] {
p := sort.Search(len(smallPrimes), func(i int) bool {
return smallPrimes[i] >= x
})
return smallPrimes[p]
}
for !isPrime(x) {
x++
}
return x
}