blob: 196173dbee8d62dccc0e7e89e337dff844ab5550 [file] [log] [blame]
// 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
}