diff --git a/hrw.go b/hrw.go index c9a7eeb..2effa3a 100644 --- a/hrw.go +++ b/hrw.go @@ -87,10 +87,7 @@ func SortSliceByValue(slice interface{}, hash uint64) { func SortHasherSliceByValue[T Hasher](slice []T, hash uint64) { rule := prepareHasherRule(slice) if rule != nil { - swap := func(i, j int) { - slice[i], slice[j] = slice[j], slice[i] - } - sortByDistance(len(rule), false, rule, hash, swap) + sortHasherByDistance(slice, false, rule, hash) } } @@ -107,13 +104,59 @@ func SortSliceByWeightValue(slice interface{}, weights []float64, hash uint64) { func SortHasherSliceByWeightValue[T Hasher](slice []T, weights []float64, hash uint64) { rule := prepareHasherRule(slice) if rule != nil { - swap := func(i, j int) { - slice[i], slice[j] = slice[j], slice[i] - } - sortByWeight(len(slice), false, rule, weights, hash, swap) + sortHasherByWeight(slice, false, rule, weights, hash) } } +// sortHasherByDistance is similar to sortByDistance but accepts slice directly. +func sortHasherByDistance[T Hasher](slice []T, byIndex bool, nodes []uint64, hash uint64) { + dist := make([]uint64, len(slice)) + for i := range dist { + dist[i] = getDistance(byIndex, i, nodes, hash) + } + + s := &sorter{ + l: len(slice), + swap: func(i, j int) { + slice[i], slice[j] = slice[j], slice[i] + dist[i], dist[j] = dist[j], dist[i] + }, + less: func(i, j int) bool { + return dist[i] < dist[j] + }, + } + sort.Sort(s) +} + +// sortHasherByWeight is similar to sortByWeight but accepts slice directly. +func sortHasherByWeight[T Hasher](slice []T, byIndex bool, nodes []uint64, weights []float64, hash uint64) { + // if all nodes have the same distance then sort uniformly + if allSameF64(weights) { + sortHasherByDistance(slice, byIndex, nodes, hash) + return + } + + dist := make([]float64, len(slice)) + for i := range dist { + d := getDistance(byIndex, i, nodes, hash) + // `maxUint64 - distance` makes the shorter distance more valuable + // it is necessary for operation with normalized values + dist[i] = float64(^uint64(0)-d) * weights[i] + } + + s := &sorter{ + l: len(slice), + swap: func(i, j int) { + slice[i], slice[j] = slice[j], slice[i] + dist[i], dist[j] = dist[j], dist[i] + }, + less: func(i, j int) bool { + return dist[i] > dist[j] // higher distance must be placed lower to be first + }, + } + sort.Sort(s) +} + // SortSliceByIndex received []T and hash to sort by index-distance func SortSliceByIndex(slice interface{}, hash uint64) { length := reflect.ValueOf(slice).Len()