Skip to content
Snippets Groups Projects
Commit 65febcd9 authored by Benjamin Wenger's avatar Benjamin Wenger
Browse files

new bins are done, this just needs some tests to be fixed

parent b7f65d49
Branches
Tags
3 merge requests!18Release,!17Dev,!14New regions
......@@ -23,15 +23,20 @@ func TestGeoBin_String(t *testing.T) {
bin GeoBin
expected string
}{
{Americas, "Americas"},
{NorthAmerica, "NorthAmerica"},
{SouthAndCentralAmerica, "SouthAndCentralAmerica"},
{WesternEurope, "WesternEurope"},
{CentralEurope, "CentralEurope"},
{EasternEurope, "EasternEurope"},
{MiddleEast, "MiddleEast"},
{Africa, "Africa"},
{NorthernAfrica, "NorthernAfrica"},
{SouthernAfrica, "SouthernAfrica"},
{Russia, "Russia"},
{Asia, "Asia"},
{Asia + 1, "INVALID BIN " + strconv.Itoa(int(Asia+1))},
{EasternAsia, "EasternAsia"},
{WesternAsia, "WesternAsia"},
{Oceania, "Oceania"},
{Oceania + 1, "INVALID BIN " + strconv.Itoa(int(Oceania+1))},
}
for i, val := range testValues {
......@@ -48,14 +53,18 @@ func TestGetRegion(t *testing.T) {
region string
expected GeoBin
}{
{"Americas", Americas},
{"NorthAmerica", NorthAmerica},
{"SouthAndCentralAmerica", SouthAndCentralAmerica},
{"WesternEurope", WesternEurope},
{"CentralEurope", CentralEurope},
{"EasternEurope", EasternEurope},
{"MiddleEast", MiddleEast},
{"Africa", Africa},
{"NorthernAfrica", NorthernAfrica},
{"SouthernAfrica", SouthernAfrica},
{"Russia", Russia},
{"Asia", Asia},
{"EasternAsia", EasternAsia},
{"WesternAsia", WesternAsia},
{"Oceania", Oceania},
}
for i, val := range testValues {
......@@ -90,7 +99,7 @@ func TestGetRegion_InvalidRegionError(t *testing.T) {
// Tests that a GeoBin can be JSON marshalled and unmarshalled.
func TestGeoBin_JsonMarshalUnmarshal(t *testing.T) {
bin := Americas
bin := NorthAmerica
data, err := json.Marshal(bin)
if err != nil {
......@@ -111,7 +120,7 @@ func TestGeoBin_JsonMarshalUnmarshal(t *testing.T) {
// Unit test of GeoBin.Bytes.
func TestGeoBin_Bytes(t *testing.T) {
bin := Americas
bin := NorthAmerica
expected := []byte{byte(bin)}
if !bytes.Equal(expected, bin.Bytes()) {
......
......@@ -45,4 +45,251 @@ func CountryLen() int {
var countryBins = map[string]GeoBin{
"AQ": NorthAmerica,
"MX": NorthAmerica,
"CA": NorthAmerica,
"US": NorthAmerica,
"UM": NorthAmerica,
"AI": SouthAndCentralAmerica,
"AG": SouthAndCentralAmerica,
"AR": SouthAndCentralAmerica,
"BS": SouthAndCentralAmerica,
"UY": SouthAndCentralAmerica,
"BB": SouthAndCentralAmerica,
"BR": SouthAndCentralAmerica,
"PE": SouthAndCentralAmerica,
"BZ": SouthAndCentralAmerica,
"CL": SouthAndCentralAmerica,
"BM": SouthAndCentralAmerica,
"EC": SouthAndCentralAmerica,
"BO": SouthAndCentralAmerica,
"GY": SouthAndCentralAmerica,
"BQ": SouthAndCentralAmerica,
"GF": SouthAndCentralAmerica,
"SR": SouthAndCentralAmerica,
"PY": SouthAndCentralAmerica,
"KY": SouthAndCentralAmerica,
"CO": SouthAndCentralAmerica,
"NI": SouthAndCentralAmerica,
"PR": SouthAndCentralAmerica,
"CR": SouthAndCentralAmerica,
"CU": SouthAndCentralAmerica,
"CW": SouthAndCentralAmerica,
"DM": SouthAndCentralAmerica,
"DO": SouthAndCentralAmerica,
"SV": SouthAndCentralAmerica,
"GT": SouthAndCentralAmerica,
"FK": SouthAndCentralAmerica,
"JM": SouthAndCentralAmerica,
"PA": SouthAndCentralAmerica,
"VE": SouthAndCentralAmerica,
"VG": SouthAndCentralAmerica,
"VI": SouthAndCentralAmerica,
"HT": SouthAndCentralAmerica,
"HN": SouthAndCentralAmerica,
"GL": SouthAndCentralAmerica,
"GD": SouthAndCentralAmerica,
"TC": SouthAndCentralAmerica,
"TT": SouthAndCentralAmerica,
"GS": SouthAndCentralAmerica,
"MS": SouthAndCentralAmerica,
"SX": SouthAndCentralAmerica,
"GP": SouthAndCentralAmerica,
"MQ": SouthAndCentralAmerica,
"LC": SouthAndCentralAmerica,
"KN": SouthAndCentralAmerica,
"PM": SouthAndCentralAmerica,
"BL": SouthAndCentralAmerica,
"VC": SouthAndCentralAmerica,
"AW": SouthAndCentralAmerica,
"MF": SouthAndCentralAmerica,
"AD": WesternEurope,
"GB": WesternEurope,
"ES": WesternEurope,
"PT": WesternEurope,
"NL": WesternEurope,
"FR": WesternEurope,
"IE": WesternEurope,
"FO": WesternEurope,
"IS": WesternEurope,
"GI": WesternEurope,
"MC": WesternEurope,
"GG": WesternEurope,
"JE": WesternEurope,
"IM": WesternEurope,
"BE": WesternEurope,
"LU": WesternEurope,
"AT": CentralEurope,
"PL": CentralEurope,
"NO": CentralEurope,
"MT": CentralEurope,
"DK": CentralEurope,
"SE": CentralEurope,
"CH": CentralEurope,
"SK": CentralEurope,
"SI": CentralEurope,
"IT": CentralEurope,
"DE": CentralEurope,
"HU": CentralEurope,
"LI": CentralEurope,
"SJ": CentralEurope,
"SM": CentralEurope,
"VA": CentralEurope,
"CZ": CentralEurope,
"AX": EasternEurope,
"AL": EasternEurope,
"BY": EasternEurope,
"UA": EasternEurope,
"BA": EasternEurope,
"LT": EasternEurope,
"LV": EasternEurope,
"BG": EasternEurope,
"MD": EasternEurope,
"HR": EasternEurope,
"FI": EasternEurope,
"CY": EasternEurope,
"RO": EasternEurope,
"EE": EasternEurope,
"GR": EasternEurope,
"ME": EasternEurope,
"RS": EasternEurope,
"MK": EasternEurope,
"GE": EasternEurope,
"AM": MiddleEast,
"AZ": MiddleEast,
"BH": MiddleEast,
"TR": MiddleEast,
"SA": MiddleEast,
"AE": MiddleEast,
"IQ": MiddleEast,
"QA": MiddleEast,
"YE": MiddleEast,
"IL": MiddleEast,
"JO": MiddleEast,
"KW": MiddleEast,
"PS": MiddleEast,
"SY": MiddleEast,
"IR": MiddleEast,
"LB": MiddleEast,
"OM": MiddleEast,
"DZ": NorthernAfrica,
"BJ": NorthernAfrica,
"BF": NorthernAfrica,
"CV": NorthernAfrica,
"TD": NorthernAfrica,
"SN": NorthernAfrica,
"CI": NorthernAfrica,
"ML": NorthernAfrica,
"NE": NorthernAfrica,
"DJ": NorthernAfrica,
"EG": NorthernAfrica,
"ET": NorthernAfrica,
"GM": NorthernAfrica,
"GH": NorthernAfrica,
"ER": NorthernAfrica,
"LR": NorthernAfrica,
"LY": NorthernAfrica,
"SL": NorthernAfrica,
"TN": NorthernAfrica,
"TG": NorthernAfrica,
"EH": NorthernAfrica,
"SD": NorthernAfrica,
"MA": NorthernAfrica,
"MR": NorthernAfrica,
"GN": NorthernAfrica,
"GW": NorthernAfrica,
"AO": SouthernAfrica,
"ZA": SouthernAfrica,
"CM": SouthernAfrica,
"RW": SouthernAfrica,
"CG": SouthernAfrica,
"CD": SouthernAfrica,
"SO": SouthernAfrica,
"ZM": SouthernAfrica,
"ZW": SouthernAfrica,
"UG": SouthernAfrica,
"GA": SouthernAfrica,
"GQ": SouthernAfrica,
"SZ": SouthernAfrica,
"BW": SouthernAfrica,
"MG": SouthernAfrica,
"TZ": SouthernAfrica,
"NA": SouthernAfrica,
"SS": SouthernAfrica,
"MW": SouthernAfrica,
"BI": SouthernAfrica,
"MZ": SouthernAfrica,
"KE": SouthernAfrica,
"BV": SouthernAfrica,
"CF": SouthernAfrica,
"KM": SouthernAfrica,
"NG": SouthernAfrica,
"MU": SouthernAfrica,
"SC": SouthernAfrica,
"LS": SouthernAfrica,
"YT": SouthernAfrica,
"ST": SouthernAfrica,
"RE": SouthernAfrica,
"SH": SouthernAfrica,
"RU": Russia,
"BD": EasternAsia,
"TH": EasternAsia,
"BT": EasternAsia,
"JP": EasternAsia,
"KH": EasternAsia,
"CN": EasternAsia,
"MY": EasternAsia,
"VN": EasternAsia,
"NP": EasternAsia,
"KP": EasternAsia,
"KR": EasternAsia,
"TW": EasternAsia,
"SG": EasternAsia,
"MM": EasternAsia,
"MN": EasternAsia,
"HK": EasternAsia,
"MO": EasternAsia,
"LA": EasternAsia,
"KZ": WesternAsia,
"AF": WesternAsia,
"TM": WesternAsia,
"UZ": WesternAsia,
"PK": WesternAsia,
"KG": WesternAsia,
"TJ": WesternAsia,
"IN": WesternAsia,
"LK": WesternAsia,
"IO": WesternAsia,
"MV": WesternAsia,
"AU": Oceania,
"PG": Oceania,
"FJ": Oceania,
"TL": Oceania,
"ID": Oceania,
"PH": Oceania,
"SB": Oceania,
"NC": Oceania,
"NZ": Oceania,
"VU": Oceania,
"TV": Oceania,
"TK": Oceania,
"AS": Oceania,
"WF": Oceania,
"TO": Oceania,
"NU": Oceania,
"CK": Oceania,
"PF": Oceania,
"PN": Oceania,
"WS": Oceania,
"PW": Oceania,
"GU": Oceania,
"NR": Oceania,
"KI": Oceania,
"CX": Oceania,
"CC": Oceania,
"NF": Oceania,
"MH": Oceania,
"FM": Oceania,
"MP": Oceania,
"HM": Oceania,
"TF": Oceania,
"BN": Oceania,
}
package region
import (
"gitlab.com/xx_network/primitives/id"
"math/rand"
"testing"
"time"
)
func TestCreateLatencyTable(t *testing.T) {
o := CreateLinkTable()
for i := 0; i < len(o); i++ {
for j := 0; j < len(o); j++ {
if o[i][j] != o[j][i] {
t.Errorf("orders of %s and %s did not have the "+
"same distance (%s - %d vs %s - %d)", GeoBin(i), GeoBin(j), GeoBin(i), o[i][j], GeoBin(j), o[j][i])
}
}
}
}
// Test that a team of 8 nodes, each in a different region
// is assembled into a round with an efficient order
func TestCreateRound_EfficientTeam_AllRegions(t *testing.T) {
const teamsize = 9
// Build the nodes
nodeList := make([]*id.ID, teamsize)
rng := rand.New(rand.NewSource(42))
// Craft regions for nodes
countries := []string{"CA", "BZ", "GB", "DE", "UA", "IL", "EG", "ZA", "RU", "BD", "IN", "AU"}
countryMap := make(map[id.ID]string)
for i := uint64(0); i < uint64(len(nodeList)); i++ {
nid := id.NewIdFromUInt(i, id.Node, t)
nodeList[i] = nid
countryMap[*nid] = countries[i]
}
start := time.Now()
latencyTable := CreateSetLatencyTableWeights(CreateLinkTable())
bestOrdering, weight, err := OrderNodeTeam(nodeList, countryMap, GetCountryBins(), latencyTable, rng)
duration := time.Now().Sub(start)
t.Logf("CreateRound took: %v\n", duration)
if err != nil {
t.Fatalf("Failed to get best ordering: %+v", err)
}
expectedDuration := 60 * time.Millisecond
if duration > expectedDuration {
t.Errorf("Warning, creating round for a team of %d took longer than expected."+
"\n\tExpected: ~%s"+
"\n\tReceived: %s", teamsize, expectedDuration, duration)
}
var regionOrder []GeoBin
var regionOrderStr []string
for _, n := range bestOrdering {
order, _ := GetCountryBin(countryMap[*n])
regionOrder = append(regionOrder, order)
regionOrderStr = append(regionOrderStr, order.String())
}
t.Logf("Team order outputted by CreateRound with weight %d: %v", weight, regionOrderStr)
// Go though the regions, checking for any long jumps
validRegionTransitions := newTransitions()
longTransitions := uint32(0)
for i, thisRegion := range regionOrder {
// Get the next region to see if it's a long distant jump
nextRegion := regionOrder[(i+1)%len(regionOrder)]
if !validRegionTransitions.isValidTransition(thisRegion, nextRegion) {
longTransitions++
}
}
t.Logf("Amount of long distant jumps: %v", longTransitions)
// Check that the long jumps does not exceed over half the jumps
if longTransitions > teamsize/2+1 {
t.Errorf("Number of long distant transitions beyond acceptable amount!"+
"\n\tAcceptable long distance transitions: %v"+
"\n\tReceived long distance transitions: %v", teamsize/2+1, longTransitions)
}
}
// Test that a team of 8 nodes, each in a different region
// is assembled into a round with an efficient order
func TestCreateRound_EfficientTeam_CloseAndFar(t *testing.T) {
const teamsize = 5
// Build the nodes
nodeList := make([]*id.ID, teamsize)
rng := rand.New(rand.NewSource(42))
// Craft regions for nodes
countries := []string{"AX", "RU", "KZ", "CN", "AU"}
countryMap := make(map[id.ID]string)
for i := uint64(0); i < uint64(len(nodeList)); i++ {
nid := id.NewIdFromUInt(i, id.Node, t)
nodeList[i] = nid
countryMap[*nid] = countries[i]
}
start := time.Now()
latencyTable := CreateSetLatencyTableWeights(CreateLinkTable())
bestOrdering, weight, err := OrderNodeTeam(nodeList, countryMap, GetCountryBins(), latencyTable, rng)
duration := time.Now().Sub(start)
t.Logf("CreateRound took: %v\n", duration)
if err != nil {
t.Fatalf("Failed to get best ordering: %+v", err)
}
expectedDuration := 60 * time.Millisecond
if duration > expectedDuration {
t.Errorf("Warning, creating round for a team of %d took longer than expected."+
"\n\tExpected: ~%s"+
"\n\tReceived: %s", teamsize, expectedDuration, duration)
}
for i := 0; i < len(bestOrdering); i++ {
}
t.Logf("Amount of long distant jumps: %v", longTransitions)
// Check that the long jumps does not exceed over half the jumps
if longTransitions > teamsize/2+1 {
t.Errorf("Number of long distant transitions beyond acceptable amount!"+
"\n\tAcceptable long distance transitions: %v"+
"\n\tReceived long distance transitions: %v", teamsize/2+1, longTransitions)
}
}
/*
// Test that a team of 8 nodes from random regions,
// is assembled into a round with an efficient order
func TestCreateRound_EfficientTeam_RandomRegions(t *testing.T) {
testpool := NewWaitingPool()
// Build scheduling params
testParams := Params{
TeamSize: 8,
BatchSize: 32,
Threshold: 2,
NodeCleanUpInterval: 3,
}
// Build network state
privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins())
if err != nil {
t.Errorf("Failed to create test state: %v", err)
t.FailNow()
}
// Build the nodes
nodeList := make([]*id.ID, testParams.TeamSize*2)
nodeStateList := make([]*node.State, testParams.TeamSize*2)
// Craft regions for nodes
regions := []string{"CR", "GB", "SK",
"HR", "IQ", "BF", "RU", "CX"}
// Populate the pool with 2x the team size
for i := uint64(0); i < uint64(len(nodeList)); i++ {
// Randomize the regions of the nodes
index := mathRand.Intn(8)
// Generate a test id
nid := id.NewIdFromUInt(i, id.Node, t)
nodeList[i] = nid
// Add the node to that node map
// Place the node in a random region
err := testState.GetNodeMap().AddNode(nodeList[i], regions[index], "", "", 0)
if err != nil {
t.Errorf("Couldn't add node: %v", err)
t.FailNow()
}
// Add the node to the pool
nodeState := testState.GetNodeMap().GetNode(nid)
nodeStateList[i] = nodeState
testpool.Add(nodeState)
}
roundID, err := testState.IncrementRoundID()
if err != nil {
t.Errorf("IncrementRoundID() failed: %+v", err)
}
// Create the protoround
start := time.Now()
testProtoRound, err := createSecureRound(testParams, testpool, roundID, testState)
if err != nil {
t.Errorf("Error in happy path: %v", err)
}
duration := time.Now().Sub(start)
expectedDuration := int64(45)
// Check that it did not take an excessive amount of time
// to create the round
if duration.Milliseconds() > expectedDuration {
t.Errorf("Warning, creating round for a team of 8 took longer than expected."+
"\n\tExpected: ~%v ms"+
"\n\tReceived: %v ms", expectedDuration, duration)
}
// Parse the order of the regions
// one for testing and one for logging
var regionOrder []region.GeoBin
var regionOrderStr []string
for _, n := range testProtoRound.NodeStateList {
order, _ := region.GetCountryBin(n.GetOrdering())
regionOrder = append(regionOrder, order)
regionOrderStr = append(regionOrderStr, order.String())
}
// Output the teaming order to the log in human readable format
t.Log("Team order outputted by CreateRound: ", regionOrderStr)
// Measure the amount of longer than necessary jumps
validRegionTransitions := newTransitions()
longTransitions := uint32(0)
for i, thisRegion := range regionOrder {
// Get the next region to see if it's a long distant jump
nextRegion := regionOrder[(i+1)%len(regionOrder)]
if !validRegionTransitions.isValidTransition(thisRegion, nextRegion) {
longTransitions++
}
}
t.Logf("Amount of long distant jumps: %v", longTransitions)
// Check that the long distant jumps do not exceed half the jumps
if longTransitions > testParams.TeamSize/2+1 {
t.Errorf("Number of long distant transitions beyond acceptable amount!"+
"\n\tAcceptable long distance transitions: %v"+
"\n\tReceived long distance transitions: %v", testParams.TeamSize/2+1, longTransitions)
}
}*/
// Based on the control state logic used for rounds. Based on the map
// discerned from internet cable maps
type regionTransition [12]regionTransitionValidation
// Transitional information used for each region
type regionTransitionValidation struct {
from [12]bool
}
// Create the valid jumps for each region
func newRegionTransitionValidation(from ...GeoBin) regionTransitionValidation {
tv := regionTransitionValidation{}
for _, f := range from {
tv.from[f] = true
}
return tv
}
// Valid transitions are defined as region jumps that are not long distant
// long distant is defined by internet cable maps. It was defined
// in a undirected graph of what are good internet connections
func newTransitions() regionTransition {
t := regionTransition{}
latencyTable := CreateLinkTable()
for i := 0; i < len(latencyTable); i++ {
acceptable := make([]GeoBin, 0)
for j := 0; j < len(latencyTable[i]); j++ {
if latencyTable[i][j] <= 3 {
acceptable = append(acceptable, GeoBin(j))
}
}
t[i] = newRegionTransitionValidation(acceptable...)
}
return t
}
// IsValidTransition checks the transitionValidation to see if
// the attempted transition is valid
func (r regionTransition) isValidTransition(from, to GeoBin) bool {
return r[to].from[from]
}
package region
import (
"encoding/binary"
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/xx_network/primitives/id"
"io"
"math"
)
func OrderNodeTeam(nodes []*id.ID, countries map[id.ID]string, countryToBins map[string]GeoBin, distanceLatency [12][12]int, rng io.Reader) ([]*id.ID, int, error) {
// Make all permutations of nodePermutation
permutations := Permute(nodes)
jww.DEBUG.Printf("Looking for most efficient teaming order")
optimalLatency := math.MaxInt32
var optimalTeams [][]*id.ID
// TODO: consider a way to do this more efficiently? As of now,
// for larger teams of 10 or greater it takes >2 seconds for round creation
// but it runs in the microsecond range with 4 nodePermutation.
// Since our use case is smaller teams, we deem this sufficient for now
for np := range permutations {
nodePermutation := permutations[np]
totalLatency := 0
for i := range nodePermutation {
thisNode := nodePermutation[i]
// Get the ordering for the current node
thisCounty, ok := countries[*thisNode]
if !ok {
return nil, 0, errors.Errorf("Unable to locate country for node %s: %v", thisNode, countries)
}
thisRegion, ok := countryToBins[thisCounty]
if !ok {
return nil, 0, errors.Errorf("Unable to locate bin for node %s at country %s", thisNode, thisCounty)
}
// Get the ordering of the next node, circling back if at the last node
nextNode := nodePermutation[(i+1)%len(nodePermutation)]
nextCounty, ok := countries[*nextNode]
if !ok {
return nil, 0, errors.Errorf("Unable to locate country for node %s: %v", nextCounty, countries)
}
nextRegion, ok := countryToBins[nextCounty]
if !ok {
return nil, 0, errors.Errorf("Unable to locate bin for node %s at country %s", nextNode, nextCounty)
}
// Calculate the distance and pull the latency from the table
totalLatency += distanceLatency[thisRegion][nextRegion]
}
// Replace with the best time and order found thus far
if totalLatency < optimalLatency {
optimalTeams = make([][]*id.ID, 0)
optimalTeams = append(optimalTeams, nodePermutation)
optimalLatency = totalLatency
} else if totalLatency == optimalLatency {
optimalTeams = append(optimalTeams, nodePermutation)
}
}
numBytes := make([]byte, 8)
_, err := rng.Read(numBytes)
if err != nil {
return nil, 0, errors.WithMessagef(err, "failed to generate ordering")
}
index := binary.BigEndian.Uint64(numBytes) % uint64(len(optimalTeams))
return optimalTeams[index], optimalLatency, nil
}
// Creates a latency table which maps different regions latencies to all
// other defined regions. Latency is derived through educated guesses right now
// without any real world data.
// todo: table needs better real-world accuracy. Once data is collected
// this table can be updated for better accuracy and selection
func CreateLinkTable() (distanceLatency [12][12]int) {
// number of hops on the graph from region.Americas to other regions
distanceLatency[NorthAmerica][NorthAmerica] = 0
distanceLatency[NorthAmerica][SouthAndCentralAmerica] = 1
distanceLatency[NorthAmerica][WesternEurope] = 2
distanceLatency[NorthAmerica][CentralEurope] = 3
distanceLatency[NorthAmerica][EasternEurope] = 4
distanceLatency[NorthAmerica][MiddleEast] = 4
distanceLatency[NorthAmerica][SouthernAfrica] = 3
distanceLatency[NorthAmerica][NorthernAfrica] = 3
distanceLatency[NorthAmerica][Russia] = 4
distanceLatency[NorthAmerica][EasternAsia] = 2
distanceLatency[NorthAmerica][WesternAsia] = 3
distanceLatency[NorthAmerica][Oceania] = 2
distanceLatency[SouthAndCentralAmerica][NorthAmerica] = 1
distanceLatency[SouthAndCentralAmerica][SouthAndCentralAmerica] = 0
distanceLatency[SouthAndCentralAmerica][WesternEurope] = 3
distanceLatency[SouthAndCentralAmerica][CentralEurope] = 4
distanceLatency[SouthAndCentralAmerica][EasternEurope] = 5
distanceLatency[SouthAndCentralAmerica][MiddleEast] = 5
distanceLatency[SouthAndCentralAmerica][SouthernAfrica] = 4
distanceLatency[SouthAndCentralAmerica][NorthernAfrica] = 4
distanceLatency[SouthAndCentralAmerica][Russia] = 5
distanceLatency[SouthAndCentralAmerica][EasternAsia] = 3
distanceLatency[SouthAndCentralAmerica][WesternAsia] = 4
distanceLatency[SouthAndCentralAmerica][Oceania] = 3
distanceLatency[WesternEurope][NorthAmerica] = 2
distanceLatency[WesternEurope][SouthAndCentralAmerica] = 3
distanceLatency[WesternEurope][WesternEurope] = 0
distanceLatency[WesternEurope][CentralEurope] = 1
distanceLatency[WesternEurope][EasternEurope] = 2
distanceLatency[WesternEurope][MiddleEast] = 2
distanceLatency[WesternEurope][SouthernAfrica] = 1
distanceLatency[WesternEurope][NorthernAfrica] = 1
distanceLatency[WesternEurope][Russia] = 3
distanceLatency[WesternEurope][EasternAsia] = 4
distanceLatency[WesternEurope][WesternAsia] = 3
distanceLatency[WesternEurope][Oceania] = 4
distanceLatency[CentralEurope][NorthAmerica] = 3
distanceLatency[CentralEurope][SouthAndCentralAmerica] = 4
distanceLatency[CentralEurope][WesternEurope] = 1
distanceLatency[CentralEurope][CentralEurope] = 0
distanceLatency[CentralEurope][EasternEurope] = 1
distanceLatency[CentralEurope][MiddleEast] = 1
distanceLatency[CentralEurope][SouthernAfrica] = 1
distanceLatency[CentralEurope][NorthernAfrica] = 1
distanceLatency[CentralEurope][Russia] = 2
distanceLatency[CentralEurope][EasternAsia] = 3
distanceLatency[CentralEurope][WesternAsia] = 2
distanceLatency[CentralEurope][Oceania] = 4
distanceLatency[EasternEurope][NorthAmerica] = 4
distanceLatency[EasternEurope][SouthAndCentralAmerica] = 5
distanceLatency[EasternEurope][WesternEurope] = 2
distanceLatency[EasternEurope][CentralEurope] = 1
distanceLatency[EasternEurope][EasternEurope] = 0
distanceLatency[EasternEurope][MiddleEast] = 1
distanceLatency[EasternEurope][SouthernAfrica] = 2
distanceLatency[EasternEurope][NorthernAfrica] = 2
distanceLatency[EasternEurope][Russia] = 1
distanceLatency[EasternEurope][EasternAsia] = 3
distanceLatency[EasternEurope][WesternAsia] = 2
distanceLatency[EasternEurope][Oceania] = 4
distanceLatency[MiddleEast][NorthAmerica] = 4
distanceLatency[MiddleEast][SouthAndCentralAmerica] = 5
distanceLatency[MiddleEast][WesternEurope] = 2
distanceLatency[MiddleEast][CentralEurope] = 1
distanceLatency[MiddleEast][EasternEurope] = 1
distanceLatency[MiddleEast][MiddleEast] = 0
distanceLatency[MiddleEast][SouthernAfrica] = 2
distanceLatency[MiddleEast][NorthernAfrica] = 2
distanceLatency[MiddleEast][Russia] = 2
distanceLatency[MiddleEast][EasternAsia] = 2
distanceLatency[MiddleEast][WesternAsia] = 1
distanceLatency[MiddleEast][Oceania] = 3
distanceLatency[NorthernAfrica][NorthAmerica] = 3
distanceLatency[NorthernAfrica][SouthAndCentralAmerica] = 4
distanceLatency[NorthernAfrica][WesternEurope] = 1
distanceLatency[NorthernAfrica][CentralEurope] = 1
distanceLatency[NorthernAfrica][EasternEurope] = 2
distanceLatency[NorthernAfrica][MiddleEast] = 2
distanceLatency[NorthernAfrica][SouthernAfrica] = 2
distanceLatency[NorthernAfrica][NorthernAfrica] = 0
distanceLatency[NorthernAfrica][Russia] = 3
distanceLatency[NorthernAfrica][EasternAsia] = 4
distanceLatency[NorthernAfrica][WesternAsia] = 3
distanceLatency[NorthernAfrica][Oceania] = 5
distanceLatency[SouthernAfrica][NorthAmerica] = 3
distanceLatency[SouthernAfrica][SouthAndCentralAmerica] = 4
distanceLatency[SouthernAfrica][WesternEurope] = 1
distanceLatency[SouthernAfrica][CentralEurope] = 1
distanceLatency[SouthernAfrica][EasternEurope] = 2
distanceLatency[SouthernAfrica][MiddleEast] = 2
distanceLatency[SouthernAfrica][SouthernAfrica] = 0
distanceLatency[SouthernAfrica][NorthernAfrica] = 2
distanceLatency[SouthernAfrica][Russia] = 3
distanceLatency[SouthernAfrica][EasternAsia] = 4
distanceLatency[SouthernAfrica][WesternAsia] = 3
distanceLatency[SouthernAfrica][Oceania] = 5
distanceLatency[Russia][NorthAmerica] = 4
distanceLatency[Russia][SouthAndCentralAmerica] = 5
distanceLatency[Russia][WesternEurope] = 3
distanceLatency[Russia][CentralEurope] = 2
distanceLatency[Russia][EasternEurope] = 1
distanceLatency[Russia][MiddleEast] = 2
distanceLatency[Russia][SouthernAfrica] = 3
distanceLatency[Russia][NorthernAfrica] = 3
distanceLatency[Russia][Russia] = 0
distanceLatency[Russia][EasternAsia] = 2
distanceLatency[Russia][WesternAsia] = 1
distanceLatency[Russia][Oceania] = 3
distanceLatency[EasternAsia][NorthAmerica] = 2
distanceLatency[EasternAsia][SouthAndCentralAmerica] = 3
distanceLatency[EasternAsia][WesternEurope] = 4
distanceLatency[EasternAsia][CentralEurope] = 3
distanceLatency[EasternAsia][EasternEurope] = 3
distanceLatency[EasternAsia][MiddleEast] = 2
distanceLatency[EasternAsia][SouthernAfrica] = 4
distanceLatency[EasternAsia][NorthernAfrica] = 4
distanceLatency[EasternAsia][Russia] = 2
distanceLatency[EasternAsia][EasternAsia] = 0
distanceLatency[EasternAsia][WesternAsia] = 1
distanceLatency[EasternAsia][Oceania] = 1
distanceLatency[WesternAsia][NorthAmerica] = 3
distanceLatency[WesternAsia][SouthAndCentralAmerica] = 4
distanceLatency[WesternAsia][WesternEurope] = 3
distanceLatency[WesternAsia][CentralEurope] = 2
distanceLatency[WesternAsia][EasternEurope] = 2
distanceLatency[WesternAsia][MiddleEast] = 1
distanceLatency[WesternAsia][SouthernAfrica] = 3
distanceLatency[WesternAsia][NorthernAfrica] = 3
distanceLatency[WesternAsia][Russia] = 1
distanceLatency[WesternAsia][EasternAsia] = 1
distanceLatency[WesternAsia][WesternAsia] = 0
distanceLatency[WesternAsia][Oceania] = 2
distanceLatency[Oceania][NorthAmerica] = 2
distanceLatency[Oceania][SouthAndCentralAmerica] = 3
distanceLatency[Oceania][WesternEurope] = 4
distanceLatency[Oceania][CentralEurope] = 4
distanceLatency[Oceania][EasternEurope] = 4
distanceLatency[Oceania][MiddleEast] = 3
distanceLatency[Oceania][SouthernAfrica] = 5
distanceLatency[Oceania][NorthernAfrica] = 5
distanceLatency[Oceania][Russia] = 3
distanceLatency[Oceania][EasternAsia] = 1
distanceLatency[Oceania][WesternAsia] = 2
distanceLatency[Oceania][Oceania] = 0
return
}
func CreateSetLatencyTableWeights(distanceLatency [12][12]int) [12][12]int {
weights := make([]int, len(distanceLatency))
weight := 1
for i := 0; i < len(weights); i++ {
weights[i] = weight
weight += 2
}
for i := 0; i < len(distanceLatency); i++ {
for j := 0; j < len(distanceLatency); j++ {
distanceLatency[i][j] = weights[distanceLatency[i][j]]
}
}
return distanceLatency
}
package region
import "gitlab.com/xx_network/primitives/id"
// permute.go contains the implementation of Heap's algorithm, used to generate all
// possible permutations of n objects
// Based off of Heap's algorithm found here: https://en.wikipedia.org/wiki/Heap%27s_algorithm.
// Runs n! time, but in place in terms of space. As of writing, we use this for permuting all
// orders of a team, of which team size is small, justifying the high complexity
func Permute(items []*id.ID) [][]*id.ID {
var helper func([]*id.ID, int)
var output [][]*id.ID
// Place inline to make appending output easier
helper = func(items []*id.ID, numItems int) {
if numItems == 1 {
// Create a copy and append the copy to the output
ourCopy := make([]*id.ID, len(items))
copy(ourCopy, items)
output = append(output, ourCopy)
} else {
for i := 0; i < numItems; i++ {
helper(items, numItems-1)
// Swap choice dependent on parity of k (even or odd)
if numItems%2 == 1 {
// Swap the values
items[i], items[numItems-1] = items[numItems-1], items[i]
} else {
// Swap the values
items[0], items[numItems-1] = items[numItems-1], items[0]
}
}
}
}
// Initialize recursive function
helper(items, len(items))
return output
}
package region
import (
"fmt"
"gitlab.com/xx_network/primitives/id"
"math/rand"
"testing"
)
// Happy path
func TestPermute(t *testing.T) {
const totalNodes = 3
nodeList := make([]*id.ID, totalNodes)
prng := rand.New(rand.NewSource(42))
// Build node states with unique ordering
for i := 0; i < totalNodes; i++ {
// Make a node state
newNode, _ := id.NewRandomID(prng, id.Node)
// Place new node in list
nodeList[i] = newNode
}
// Permute the nodes
permutations := Permute(nodeList)
expectedLen := factorial(totalNodes)
// Verify that the amount of permutations is
// factorial of the original amount of nodes
if len(permutations) != expectedLen {
t.Errorf("Permutations did not produce the expected amount of permutations "+
"(factorial of amount of nodes)!"+
"\n\tExpected: %d"+
"\n\tReceived: %d", expectedLen, len(permutations))
}
expectedPermutations := make(map[string]bool)
// Iterate through all the permutations to ensure uniqueness between orderings
for _, permutation := range permutations {
var concatenatedOrdering string
// Concatenate orderings into a single string
for _, ourNode := range permutation {
concatenatedOrdering += ourNode.String()
}
// If that ordering has been encountered before, error
if expectedPermutations[concatenatedOrdering] {
t.Errorf("Permutation %s has occurred more than once!", concatenatedOrdering)
}
// Mark permutation as seen
expectedPermutations[concatenatedOrdering] = true
}
}
func factorial(n int) int {
factVal := 1
if n < 0 {
fmt.Println("Factorial of negative number doesn't exist.")
} else {
for i := 1; i <= n; i++ {
factVal *= i
}
}
return factVal
}
package region
import (
"fmt"
"testing"
)
func TestWeights(t *testing.T) {
to := 12
weights := make([]int, to+1)
weights[1] = 1
for current := 2; current < to; current++ {
var items []int
for i := 1; i < current; i++ {
for j := 0; j < (current+i-1)/i; j++ {
items = append(items, i)
}
}
permutations := All(items)
fmt.Printf("Permuted %d got %d results\n", current, len(permutations))
maxWeight := 0
for _, permutation := range permutations {
total := 0
weight := 0
for _, element := range permutation {
total += element
weight += weights[element]
}
if total != current {
continue
}
if weight > maxWeight {
maxWeight = weight
}
}
weights[current] = maxWeight + 1
fmt.Printf("Current:%d, weight:%d\n", current, weights[current])
}
fmt.Printf("FinalList:%v,", weights)
}
// All returns all combinations for a given string array.
// This is essentially a powerset of the given set except that the empty set is disregarded.
func All(set []int) (subsets [][]int) {
length := uint(len(set))
// Go through all possible combinations of objects
// from 1 (only first object in subset) to 2^length (all objects in subset)
for subsetBits := 1; subsetBits < (1 << length); subsetBits++ {
var subset []int
for object := uint(0); object < length; object++ {
// checks if object is contained in subset
// by checking if bit 'object' is set in subsetBits
if (subsetBits>>object)&1 == 1 {
// add object to subset
subset = append(subset, set[object])
}
}
// add subset to subsets
subsets = append(subsets, subset)
}
return subsets
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment