Skip to content

Commit

Permalink
Merge pull request #1 from yourbasic/tip
Browse files Browse the repository at this point in the history
Tip
  • Loading branch information
korthaj authored Jun 14, 2017
2 parents 4ddd6b2 + e7004df commit a3ad84c
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 6 deletions.
21 changes: 21 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package radix_test

import (
"fmt"
"github.com/yourbasic/radix"
)

func ExampleSortSlice() {
people := []struct {
Name string
Age int
}{
{"Gopher", 7},
{"Alice", 55},
{"Vera", 24},
{"Bob", 75},
}
radix.SortSlice(people, func(i int) string { return people[i].Name })
fmt.Println(people)
// Output: [{Alice 55} {Bob 75} {Gopher 7} {Vera 24}]
}
158 changes: 158 additions & 0 deletions slice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package radix

import (
"bufio"
"log"
"os"
"reflect"
"sort"
"strconv"
"testing"
)

func TestSortSlice(t *testing.T) {
data := [...]string{"", "Hello", "foo", "fo", "xb", "xa", "bar", "foo", "f00", "%*&^*&^&", "***"}
sorted := data[0:]
sort.Strings(sorted)

a := data[0:]
SortSlice(a, func(i int) string { return a[i] })
if !reflect.DeepEqual(a, sorted) {
t.Errorf(" got %v", a)
t.Errorf("want %v", sorted)
}

SortSlice(nil, func(i int) string { return a[i] })
a = []string{}
SortSlice(a, func(i int) string { return a[i] })
if !reflect.DeepEqual(a, []string{}) {
t.Errorf(" got %v", a)
t.Errorf("want %v", []string{})
}
a = []string{""}
SortSlice(a, func(i int) string { return a[i] })
if !reflect.DeepEqual(a, []string{""}) {
t.Errorf(" got %v", a)
t.Errorf("want %v", []string{""})
}
}

func TestSortSlice1k(t *testing.T) {
data := make([]string, 1<<10)
for i := range data {
data[i] = strconv.Itoa(i ^ 0x2cc)
}

sorted := make([]string, len(data))
copy(sorted, data)
sort.Strings(sorted)

SortSlice(data, func(i int) string { return data[i] })
if !reflect.DeepEqual(data, sorted) {
t.Errorf(" got %v", data)
t.Errorf("want %v", sorted)
}
}

func TestSortSliceBible(t *testing.T) {
var data []string
f, err := os.Open("res/bible.txt")
if err != nil {
log.Fatal(err)
}
for sc := bufio.NewScanner(f); sc.Scan(); {
data = append(data, sc.Text())
}

sorted := make([]string, len(data))
copy(sorted, data)
sort.Strings(sorted)

SortSlice(data, func(i int) string { return data[i] })
if !reflect.DeepEqual(data, sorted) {
for i, s := range data {
if s != sorted[i] {
t.Errorf("%v got: %v", i, s)
t.Errorf("%v want: %v", i, sorted[i])
}
}
}
}

func BenchmarkRadixSortSliceBible(b *testing.B) {
b.StopTimer()
var data []string
f, err := os.Open("res/bible.txt")
if err != nil {
log.Fatal(err)
}
for sc := bufio.NewScanner(f); sc.Scan(); {
data = append(data, sc.Text())
}

a := make([]string, len(data))
for i := 0; i < b.N; i++ {
copy(a, data)
b.StartTimer()
SortSlice(a, func(i int) string { return a[i] })
b.StopTimer()
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}

func BenchmarkSortSliceBible(b *testing.B) {
b.StopTimer()
var data []string
f, err := os.Open("res/bible.txt")
if err != nil {
log.Fatal(err)
}
for sc := bufio.NewScanner(f); sc.Scan(); {
data = append(data, sc.Text())
}

a := make([]string, len(data))
for i := 0; i < b.N; i++ {
copy(a, data)
b.StartTimer()
sort.Slice(a, func(i, j int) bool { return a[i] < a[j] })
b.StopTimer()
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}

func BenchmarkRadixSortSlice1k(b *testing.B) {
b.StopTimer()
data := make([]string, 1<<10)
for i := range data {
data[i] = strconv.Itoa(i ^ 0x2cc)
}

a := make([]string, len(data))
for i := 0; i < b.N; i++ {
copy(a, data)
b.StartTimer()
SortSlice(a, func(i int) string { return a[i] })
b.StopTimer()
}
}

func BenchmarkSortSlice1k(b *testing.B) {
b.StopTimer()
data := make([]string, 1<<10)
for i := range data {
data[i] = strconv.Itoa(i ^ 0x2cc)
}

a := make([]string, len(data))
for i := 0; i < b.N; i++ {
copy(a, data)
b.StartTimer()
sort.Slice(a, func(i, j int) bool { return a[i] < a[j] })
b.StopTimer()
}
}
47 changes: 45 additions & 2 deletions sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@
// This is an optimized sorting algorithm equivalent to sort.Strings.
// For string sorting, a carefully implemented radix sort can be considerably
// faster than Quicksort, sometimes more than twice as fast.
//
package radix

import (
"reflect"
"unsafe"
)

// Sort sorts a slice of strings in increasing byte-wise lexicographic order.
//
// The function is equivalent to sort.Strings in the standard library.
func Sort(a []string) {
n := len(a)
if n < 2 {
return
}
mem := make([]list, n) // Put elements into a linked list.
// Put elements into a linked list.
mem := make([]list, n)
for i, s := range a {
mem[i].str = s
if i < n-1 {
Expand All @@ -27,6 +33,43 @@ func Sort(a []string) {
}
}

// SortSlice sorts a slice according to the strings returned by str.
//
// The function panics if the provided interface is not a slice.
func SortSlice(slice interface{}, str func(i int) string) {
if slice == nil {
return
}
n := reflect.ValueOf(slice).Len()
if n < 2 {
return
}
// Put elements into a linked list.
mem := make([]list, n)
for i := 0; i < n; i++ {
mem[i].str = str(i)
if i < n-1 {
mem[i].next = &mem[i+1]
}
}
res := msdRadixSort(&mem[0], n)
// Create a permutation that will sort the slice.
perm := make([]int, n)
const size = unsafe.Sizeof(list{})
base := uintptr(unsafe.Pointer(&mem[0]))
for i := 0; i < n; i++ {
perm[(uintptr(unsafe.Pointer(res))-base)/size] = i
res = res.next
}
// Apply permutation by swapping.
swap := reflect.Swapper(slice)
for i := 0; i < n; i++ {
for j := perm[i]; j != i; perm[j], j = j, perm[j] {
swap(i, j)
}
}
}

const insertBreak = 16

type list struct {
Expand Down
8 changes: 4 additions & 4 deletions sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ func TestSortBible(t *testing.T) {
for i, s := range data {
if s != sorted[i] {
t.Errorf("%v got: %v", i, s)
t.Errorf("%v want: %v\n\n", i, sorted[i])
t.Errorf("%v want: %v", i, sorted[i])
}
}
}
}

func BenchmarkSortMsdBible(b *testing.B) {
func BenchmarkRadixSortBible(b *testing.B) {
b.StopTimer()
var data []string
f, err := os.Open("res/bible.txt")
Expand Down Expand Up @@ -125,7 +125,7 @@ func BenchmarkSortStringsBible(b *testing.B) {
}
}

func BenchmarkSortMsd1K(b *testing.B) {
func BenchmarkRadixSort1k(b *testing.B) {
b.StopTimer()
data := make([]string, 1<<10)
for i := range data {
Expand All @@ -141,7 +141,7 @@ func BenchmarkSortMsd1K(b *testing.B) {
}
}

func BenchmarkSortStrings1K(b *testing.B) {
func BenchmarkSortStrings1k(b *testing.B) {
b.StopTimer()
data := make([]string, 1<<10)
for i := range data {
Expand Down

0 comments on commit a3ad84c

Please sign in to comment.