Skip to content

Instantly share code, notes, and snippets.

@ohookins
Created April 24, 2018 19:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ohookins/89d59f121a28eed05fa924724f1d8515 to your computer and use it in GitHub Desktop.
Save ohookins/89d59f121a28eed05fa924724f1d8515 to your computer and use it in GitHub Desktop.
package main
import (
"math/big"
"strings"
)
const (
idLength = 12
lookupString = "0123456789abcdefghijklmnopqrstuvwxyz"
)
func partition(start, end string, partitions int) []string {
startNum := base36ToBig(start)
endNum := base36ToBig(end)
// Sanity check inputs - end must be greater than start
if startNum.Cmp(endNum) >= 0 {
return []string{}
}
// Calculate the keyspace included in each partition
difference := big.NewInt(0).Sub(endNum, startNum)
partitionSize := big.NewInt(0).Div(difference, big.NewInt(int64(partitions)))
// Generate readable strings representing the start of each partition
var partitionStrings []string
for i := 0; i < partitions; i++ {
partitionAdditive := big.NewInt(0).Mul(partitionSize, big.NewInt(int64(i)))
partitionStart := big.NewInt(0).Add(startNum, partitionAdditive)
partitionStrings = append(partitionStrings, bigToBase36(partitionStart))
}
// We assume that the caller will re-use the end string themselves,
// so we don't need to include it in the partition slice.
return partitionStrings
}
func base36ToBig(input string) *big.Int {
// Boundary check to make sure we have a string of idLength length
if len(input) < idLength {
input += strings.Repeat("0", idLength-len(input))
}
if len(input) > idLength {
input = input[:12]
}
num := big.NewInt(0)
for i := 0; i < idLength; i++ {
// Turn the current character in the string to its numerical value
// from the lookup table.
c := charToIndex(input[i])
// Create the multiplier for the current input string position.
// It is basically 36^position.
mul := big.NewInt(0).Exp(big.NewInt(36), big.NewInt(int64(idLength-i)), nil)
// Calculate the current character position's magnitude in the final
// result number.
tmp := big.NewInt(0).Mul(mul, big.NewInt(int64(c)))
num.Add(num, tmp)
}
return num
}
func bigToBase36(input *big.Int) string {
result := make([]byte, idLength)
for i := 0; i < idLength; i++ {
divisor := big.NewInt(0).Exp(big.NewInt(36), big.NewInt(int64(idLength-i)), nil)
// Dividing the remaining input number by 36^i should yield an index
// into our lookup string.
mod := big.NewInt(0)
posIndex, _ := big.NewInt(0).DivMod(input, divisor, mod)
result[i] = indexToChar(int(posIndex.Int64()))
// Set the remainder of the division for the next loop
input = mod
}
return string(result)
}
func charToIndex(c byte) int {
return strings.IndexByte(lookupString, c)
}
func indexToChar(index int) byte {
return lookupString[index]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment