From 63a27119e35568c57cd023522a71aa60246c9b99 Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 09:19:23 +0200 Subject: [PATCH 01/10] Add SortSlice outline and example --- example_test.go | 21 +++++++++++++++++++++ sort.go | 25 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 example_test.go diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..4806ccf --- /dev/null +++ b/example_test.go @@ -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}] +} diff --git a/sort.go b/sort.go index b9dcfea..075d16b 100644 --- a/sort.go +++ b/sort.go @@ -6,6 +6,31 @@ // package radix +import "reflect" + +// TODO: 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) { + rv := reflect.ValueOf(slice) + swap := reflect.Swapper(slice) + n := rv.Len() + if n < 2 { + return + } + mem := make([]list, n) // Put elements into a linked list. + for i := 0; i < n; i++ { + mem[i].str = str(i) + if i < n-1 { + mem[i].next = &mem[i+1] + } + } + _ = msdRadixSort(&mem[0], n) + swap(0, 1) + swap(2, 3) + swap(1, 2) +} + // 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) { From bea97b64808d07a42a409806a2787bbaf1de076f Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 14:12:50 +0200 Subject: [PATCH 02/10] Add permutation with swap --- sort.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sort.go b/sort.go index 075d16b..f93d6b6 100644 --- a/sort.go +++ b/sort.go @@ -26,9 +26,14 @@ func SortSlice(slice interface{}, str func(i int) string) { } } _ = msdRadixSort(&mem[0], n) - swap(0, 1) - swap(2, 3) - swap(1, 2) + perm := []int{2, 0, 3, 1} + for i := 0; i < len(perm); i++ { + j := perm[i] + for j != i { + swap(i, j) + perm[j], j = j, perm[j] + } + } } // Sort sorts a slice of strings in increasing byte-wise lexicographic order. From bf22a14e8011697c584785a5517716cd26e06f6f Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 14:26:22 +0200 Subject: [PATCH 03/10] Add benchmark for sort.Slice --- sort_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sort_test.go b/sort_test.go index c090b6c..93aeed4 100644 --- a/sort_test.go +++ b/sort_test.go @@ -125,6 +125,29 @@ func BenchmarkSortStringsBible(b *testing.B) { } } +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 BenchmarkSortMsd1K(b *testing.B) { b.StopTimer() data := make([]string, 1<<10) @@ -156,3 +179,19 @@ func BenchmarkSortStrings1K(b *testing.B) { 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() + } +} From b1e7206a396ebe40201ea34d9d9d2ecc59305662 Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 14:53:08 +0200 Subject: [PATCH 04/10] Add quick-and-dirty SortSlice implemenation --- sort.go | 18 ++++++++--- sort_test.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/sort.go b/sort.go index f93d6b6..f20a8d7 100644 --- a/sort.go +++ b/sort.go @@ -12,6 +12,9 @@ import "reflect" // // The function panics if the provided interface is not a slice. func SortSlice(slice interface{}, str func(i int) string) { + if slice == nil { + return + } rv := reflect.ValueOf(slice) swap := reflect.Swapper(slice) n := rv.Len() @@ -21,12 +24,18 @@ func SortSlice(slice interface{}, str func(i int) string) { mem := make([]list, n) // Put elements into a linked list. for i := 0; i < n; i++ { mem[i].str = str(i) + mem[i].index = i if i < n-1 { mem[i].next = &mem[i+1] } } - _ = msdRadixSort(&mem[0], n) - perm := []int{2, 0, 3, 1} + res := msdRadixSort(&mem[0], n) + perm := make([]int, n) + for i := 0; i < n; i++ { + perm[res.index] = i + res = res.next + } + //perm := []int{2, 0, 3, 1} for i := 0; i < len(perm); i++ { j := perm[i] for j != i { @@ -60,8 +69,9 @@ func Sort(a []string) { const insertBreak = 16 type list struct { - str string - next *list + index int + str string + next *list } type bucket struct { diff --git a/sort_test.go b/sort_test.go index 93aeed4..1cffa8b 100644 --- a/sort_test.go +++ b/sort_test.go @@ -37,6 +37,34 @@ func TestSort(t *testing.T) { } } +func TestSortSlice(t *testing.T) { + data := [...]string{"", "Hello", "foo", "fo", "xb", "xa", "bar", "foo", "f00", "%*&^*&^&", "***"} + sorted := data[0:] + sort.Strings(sorted) + + a := data[0:] + str := func(i int) string { return a[i] } + SortSlice(a, str) + if !reflect.DeepEqual(a, sorted) { + t.Errorf(" got %v", a) + t.Errorf("want %v", sorted) + } + + SortSlice(nil, str) + a = []string{} + SortSlice(a, str) + if !reflect.DeepEqual(a, []string{}) { + t.Errorf(" got %v", a) + t.Errorf("want %v", []string{}) + } + a = []string{""} + SortSlice(a, str) + if !reflect.DeepEqual(a, []string{""}) { + t.Errorf(" got %v", a) + t.Errorf("want %v", []string{""}) + } +} + func TestSort1k(t *testing.T) { data := make([]string, 1<<10) for i := range data { @@ -47,6 +75,24 @@ func TestSort1k(t *testing.T) { copy(sorted, data) sort.Strings(sorted) + str := func(i int) string { return data[i] } + SortSlice(data, str) + if !reflect.DeepEqual(data, sorted) { + t.Errorf(" got %v", data) + t.Errorf("want %v", sorted) + } +} + +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) + Sort(data) if !reflect.DeepEqual(data, sorted) { t.Errorf(" got %v", data) @@ -102,6 +148,29 @@ func BenchmarkSortMsdBible(b *testing.B) { } } +func BenchmarkSortSliceMsdBible(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 BenchmarkSortStringsBible(b *testing.B) { b.StopTimer() var data []string @@ -164,6 +233,22 @@ func BenchmarkSortMsd1K(b *testing.B) { } } +func BenchmarkSortSliceMsd1K(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 BenchmarkSortStrings1K(b *testing.B) { b.StopTimer() data := make([]string, 1<<10) From 2025b93fecd7d7d51a185c06daca2d1c94d2f207 Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 14:53:43 +0200 Subject: [PATCH 05/10] Lint --- sort.go | 1 - 1 file changed, 1 deletion(-) diff --git a/sort.go b/sort.go index f20a8d7..55a17eb 100644 --- a/sort.go +++ b/sort.go @@ -35,7 +35,6 @@ func SortSlice(slice interface{}, str func(i int) string) { perm[res.index] = i res = res.next } - //perm := []int{2, 0, 3, 1} for i := 0; i < len(perm); i++ { j := perm[i] for j != i { From 24f55b890c33dc64165eea2d271dcc81b5f7a34a Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 17:50:00 +0200 Subject: [PATCH 06/10] Fix test func names --- sort_test.go | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/sort_test.go b/sort_test.go index 1cffa8b..669ca52 100644 --- a/sort_test.go +++ b/sort_test.go @@ -10,12 +10,13 @@ import ( "testing" ) +var text = [...]string{"", "Hello", "foo", "fo", "xb", "xa", "bar", "foo", "f00", "%*&^*&^&", "***"} + func TestSort(t *testing.T) { - data := [...]string{"", "Hello", "foo", "fo", "xb", "xa", "bar", "foo", "f00", "%*&^*&^&", "***"} - sorted := data[0:] + sorted := text[0:] sort.Strings(sorted) - a := data[0:] + a := text[0:] Sort(a) if !reflect.DeepEqual(a, sorted) { t.Errorf(" got %v", a) @@ -38,11 +39,10 @@ func TestSort(t *testing.T) { } func TestSortSlice(t *testing.T) { - data := [...]string{"", "Hello", "foo", "fo", "xb", "xa", "bar", "foo", "f00", "%*&^*&^&", "***"} - sorted := data[0:] + sorted := text[0:] sort.Strings(sorted) - a := data[0:] + a := text[0:] str := func(i int) string { return a[i] } SortSlice(a, str) if !reflect.DeepEqual(a, sorted) { @@ -75,8 +75,7 @@ func TestSort1k(t *testing.T) { copy(sorted, data) sort.Strings(sorted) - str := func(i int) string { return data[i] } - SortSlice(data, str) + Sort(data) if !reflect.DeepEqual(data, sorted) { t.Errorf(" got %v", data) t.Errorf("want %v", sorted) @@ -93,7 +92,7 @@ func TestSortSlice1k(t *testing.T) { copy(sorted, data) sort.Strings(sorted) - Sort(data) + SortSlice(data, func(i int) string { return data[i] }) if !reflect.DeepEqual(data, sorted) { t.Errorf(" got %v", data) t.Errorf("want %v", sorted) @@ -125,7 +124,7 @@ func TestSortBible(t *testing.T) { } } -func BenchmarkSortMsdBible(b *testing.B) { +func BenchmarkRadixSortBible(b *testing.B) { b.StopTimer() var data []string f, err := os.Open("res/bible.txt") @@ -148,7 +147,7 @@ func BenchmarkSortMsdBible(b *testing.B) { } } -func BenchmarkSortSliceMsdBible(b *testing.B) { +func BenchmarkRadixSortSliceBible(b *testing.B) { b.StopTimer() var data []string f, err := os.Open("res/bible.txt") @@ -217,7 +216,7 @@ func BenchmarkSortSliceBible(b *testing.B) { } } -func BenchmarkSortMsd1K(b *testing.B) { +func BenchmarkRadixSort1k(b *testing.B) { b.StopTimer() data := make([]string, 1<<10) for i := range data { @@ -233,7 +232,7 @@ func BenchmarkSortMsd1K(b *testing.B) { } } -func BenchmarkSortSliceMsd1K(b *testing.B) { +func BenchmarkRadixSortSlice1k(b *testing.B) { b.StopTimer() data := make([]string, 1<<10) for i := range data { @@ -249,7 +248,7 @@ func BenchmarkSortSliceMsd1K(b *testing.B) { } } -func BenchmarkSortStrings1K(b *testing.B) { +func BenchmarkSortStrings1k(b *testing.B) { b.StopTimer() data := make([]string, 1<<10) for i := range data { @@ -265,7 +264,7 @@ func BenchmarkSortStrings1K(b *testing.B) { } } -func BenchmarkSortSlice1K(b *testing.B) { +func BenchmarkSortSlice1k(b *testing.B) { b.StopTimer() data := make([]string, 1<<10) for i := range data { From 160fb74c0ab60319a577455414b4bd41bcf443bf Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 18:02:21 +0200 Subject: [PATCH 07/10] Split tests into two files --- slice_test.go | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++ sort.go | 46 +++++++++--------- sort_test.go | 129 ++---------------------------------------------- 3 files changed, 158 insertions(+), 149 deletions(-) create mode 100644 slice_test.go diff --git a/slice_test.go b/slice_test.go new file mode 100644 index 0000000..489f5f3 --- /dev/null +++ b/slice_test.go @@ -0,0 +1,132 @@ +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:] + 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 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() + } +} diff --git a/sort.go b/sort.go index 55a17eb..fa511b2 100644 --- a/sort.go +++ b/sort.go @@ -3,12 +3,33 @@ // 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" -// TODO: SortSlice sorts a slice according to the strings returned by str. +// 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. + for i, s := range a { + mem[i].str = s + if i < n-1 { + mem[i].next = &mem[i+1] + } + } + res := msdRadixSort(&mem[0], n) + for i := range a { + a[i] = res.str + res = res.next + } +} + +// 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) { @@ -44,27 +65,6 @@ func SortSlice(slice interface{}, str func(i int) string) { } } -// 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. - for i, s := range a { - mem[i].str = s - if i < n-1 { - mem[i].next = &mem[i+1] - } - } - res := msdRadixSort(&mem[0], n) - for i := range a { - a[i] = res.str - res = res.next - } -} - const insertBreak = 16 type list struct { diff --git a/sort_test.go b/sort_test.go index 669ca52..ab7a808 100644 --- a/sort_test.go +++ b/sort_test.go @@ -10,13 +10,12 @@ import ( "testing" ) -var text = [...]string{"", "Hello", "foo", "fo", "xb", "xa", "bar", "foo", "f00", "%*&^*&^&", "***"} - func TestSort(t *testing.T) { - sorted := text[0:] + data := [...]string{"", "Hello", "foo", "fo", "xb", "xa", "bar", "foo", "f00", "%*&^*&^&", "***"} + sorted := data[0:] sort.Strings(sorted) - a := text[0:] + a := data[0:] Sort(a) if !reflect.DeepEqual(a, sorted) { t.Errorf(" got %v", a) @@ -38,33 +37,6 @@ func TestSort(t *testing.T) { } } -func TestSortSlice(t *testing.T) { - sorted := text[0:] - sort.Strings(sorted) - - a := text[0:] - str := func(i int) string { return a[i] } - SortSlice(a, str) - if !reflect.DeepEqual(a, sorted) { - t.Errorf(" got %v", a) - t.Errorf("want %v", sorted) - } - - SortSlice(nil, str) - a = []string{} - SortSlice(a, str) - if !reflect.DeepEqual(a, []string{}) { - t.Errorf(" got %v", a) - t.Errorf("want %v", []string{}) - } - a = []string{""} - SortSlice(a, str) - if !reflect.DeepEqual(a, []string{""}) { - t.Errorf(" got %v", a) - t.Errorf("want %v", []string{""}) - } -} - func TestSort1k(t *testing.T) { data := make([]string, 1<<10) for i := range data { @@ -82,23 +54,6 @@ func TestSort1k(t *testing.T) { } } -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 TestSortBible(t *testing.T) { var data []string f, err := os.Open("res/bible.txt") @@ -147,29 +102,6 @@ func BenchmarkRadixSortBible(b *testing.B) { } } -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 BenchmarkSortStringsBible(b *testing.B) { b.StopTimer() var data []string @@ -193,29 +125,6 @@ func BenchmarkSortStringsBible(b *testing.B) { } } -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 BenchmarkRadixSort1k(b *testing.B) { b.StopTimer() data := make([]string, 1<<10) @@ -232,22 +141,6 @@ func BenchmarkRadixSort1k(b *testing.B) { } } -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 BenchmarkSortStrings1k(b *testing.B) { b.StopTimer() data := make([]string, 1<<10) @@ -263,19 +156,3 @@ func BenchmarkSortStrings1k(b *testing.B) { 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() - } -} From 0fc821665215c4768c23f343f92a74e96b92d45b Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 18:57:10 +0200 Subject: [PATCH 08/10] Add SortSlice function --- sort.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/sort.go b/sort.go index fa511b2..c8e209a 100644 --- a/sort.go +++ b/sort.go @@ -5,7 +5,10 @@ // faster than Quicksort, sometimes more than twice as fast. package radix -import "reflect" +import ( + "reflect" + "unsafe" +) // Sort sorts a slice of strings in increasing byte-wise lexicographic order. // @@ -15,7 +18,8 @@ func Sort(a []string) { 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 { @@ -36,31 +40,32 @@ func SortSlice(slice interface{}, str func(i int) string) { if slice == nil { return } - rv := reflect.ValueOf(slice) - swap := reflect.Swapper(slice) - n := rv.Len() + n := reflect.ValueOf(slice).Len() 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 := 0; i < n; i++ { mem[i].str = str(i) - mem[i].index = 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[res.index] = i + perm[(uintptr(unsafe.Pointer(res))-base)/size] = i res = res.next } - for i := 0; i < len(perm); i++ { - j := perm[i] - for j != i { + // 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) - perm[j], j = j, perm[j] } } } @@ -68,9 +73,8 @@ func SortSlice(slice interface{}, str func(i int) string) { const insertBreak = 16 type list struct { - index int - str string - next *list + str string + next *list } type bucket struct { From e66eb161f4af1d0bd0109a52575a2e38155aaef5 Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 19:40:36 +0200 Subject: [PATCH 09/10] Update slice test --- slice_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/slice_test.go b/slice_test.go index 489f5f3..ddfb56e 100644 --- a/slice_test.go +++ b/slice_test.go @@ -16,6 +16,7 @@ func TestSortSlice(t *testing.T) { 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) @@ -53,6 +54,31 @@ func TestSortSlice1k(t *testing.T) { } } +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\n\n", i, sorted[i]) + } + } + } +} + func BenchmarkRadixSortSliceBible(b *testing.B) { b.StopTimer() var data []string From e7004df1b8aa844dbfa99286f95d5f8b955600d6 Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 14 Jun 2017 20:37:45 +0200 Subject: [PATCH 10/10] Lint --- slice_test.go | 2 +- sort_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/slice_test.go b/slice_test.go index ddfb56e..a44de9a 100644 --- a/slice_test.go +++ b/slice_test.go @@ -73,7 +73,7 @@ func TestSortSliceBible(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]) } } } diff --git a/sort_test.go b/sort_test.go index ab7a808..20d0363 100644 --- a/sort_test.go +++ b/sort_test.go @@ -73,7 +73,7 @@ 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]) } } }