Skip to content

Commit

Permalink
Minor review of the documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
jfsmig committed May 24, 2022
1 parent a2a1d2f commit 444a639
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 39 deletions.
3 changes: 3 additions & 0 deletions .codacy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
exclude_paths:
- "*_test.go"
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)

Sorted array provide a complexity profile which make it suitable for collection with significantly more lookups that modifying operations:
* a compact memory footprint
* an efficient lookup complexity in O(log N)
* an efficient scan complexity since it depends on the lookup followed by a sequential scan of the array
* an insertion in O(N * log N) which is rather inefficient but remains acceptable if the operation is rather rare
* a compact memory footprint
* an efficient lookup complexity in O(log N)
* an efficient scan complexity since it depends on the lookup followed by a sequential scan of the array
* an insertion in O(N * log N) which is rather inefficient but remains acceptable if the operation is rather rare

3 flavors of generic sorted arrays for efficient lookup and paginated scans.
11 changes: 7 additions & 4 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import (
)

const (
minSliceSize = 1
maxSliceSize = 1000
// MinSliceSize is the absolute minimal size of a slice return by the Slice methods.
MinSliceSize = 1

// MaxSliceSize is the absolute upper limit to the number of elements returned by the Slice methods.
MaxSliceSize = 1000
)

var (
ErrUnsorted = errors.New("unsorted")
ErrDuplicates = errors.New("duplicates")
errUnsorted = errors.New("unsorted")
errDuplicates = errors.New("duplicates")
)
17 changes: 13 additions & 4 deletions sorted_cmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ type WithCompare[T any] interface {
Compare(other T) int
}

// Len implements a method of the sort.Interface
func (s SortedCmp[T]) Len() int { return len(s) }

// Swap implements a method of the sort.Interface
func (s SortedCmp[T]) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// Less implements a method of the sort.Interface
func (s SortedCmp[T]) Less(i, j int) bool { return s[i].Compare(s[j]) < 0 }

// Add introduces a new item in the sorted array, regardless the presence of the same item,
// and preserves the ordering of the array.
func (s *SortedCmp[T]) Add(a T) {
*s = append(*s, a)
switch nb := len(*s); nb {
Expand All @@ -42,16 +47,18 @@ func (s *SortedCmp[T]) Add(a T) {
}
}

// Append introduces several items in the sorted array, regardless the presence of identical items
// and preserves the ordering of the array.
func (s *SortedCmp[T]) Append(a ...T) {
*s = append(*s, a...)
sort.Sort(s)
}

func (s SortedCmp[T]) Slice(marker T, max uint32) []T {
if max < minSliceSize {
max = minSliceSize
} else if max > maxSliceSize {
max = maxSliceSize
if max < MinSliceSize {
max = MinSliceSize
} else if max > MaxSliceSize {
max = MaxSliceSize
}
start := sort.Search(len(s), func(i int) bool {
return marker.Compare(s[i]) < 0
Expand All @@ -66,6 +73,8 @@ func (s SortedCmp[T]) Slice(marker T, max uint32) []T {
return s[start : uint32(start)+remaining]
}

// GetIndex returns the position of the first items that matches (Compare returns 0) to the given other item,
// or -1 in case of no match.
func (s SortedCmp[T]) GetIndex(id T) int {
i := sort.Search(len(s), func(i int) bool {
return id.Compare(s[i]) <= 0
Expand Down
12 changes: 6 additions & 6 deletions sorted_cmp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,19 @@ func TestCmp_Slice(T *testing.T) {
testSlice(bag[0], 2, bag[1], bag[2])
testSlice(bag[0]-1, 2, bag[0], bag[1])
testSlice(bag[len(bag)-1], 1)
testSlice(bag[0]-1, minSliceSize-1, bag[:minSliceSize]...)
testSlice(bag[0]-1, maxSliceSize+1, bag...)
testSlice(bag[0]-1, MinSliceSize-1, bag[:MinSliceSize]...)
testSlice(bag[0]-1, MaxSliceSize+1, bag...)
}

func TestCmp_SliceLarge(T *testing.T) {
const total = 3 * maxSliceSize
const total = 3 * MaxSliceSize
var bag SortedCmp[CmpInt]
for i := 0; i < total; i++ {
bag.Add(CmpInt(i))
}
bag.Assert()
slice := bag.Slice(bag[0], total)
if len(slice) < minSliceSize || len(slice) > maxSliceSize {
if len(slice) < MinSliceSize || len(slice) > MaxSliceSize {
T.Fatal()
}
}
Expand Down Expand Up @@ -179,10 +179,10 @@ func (s SortedCmp[T]) Assert() {
// Check validates the ordering and the unicity of the elements in the array
func (s SortedCmp[T]) Check() error {
if !sort.IsSorted(s) {
return ErrUnsorted
return errUnsorted
}
if !s.areItemsUnique() {
return ErrDuplicates
return errDuplicates
}
return nil
}
Expand Down
12 changes: 7 additions & 5 deletions sorted_obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ func (s *SortedObj[PkType, T]) Append(a ...T) {
}

func (s SortedObj[PkType, T]) Slice(marker PkType, max uint32) []T {
if max < minSliceSize {
max = minSliceSize
} else if max > maxSliceSize {
max = maxSliceSize
if max < MinSliceSize {
max = MinSliceSize
} else if max > MaxSliceSize {
max = MaxSliceSize
}
start := sort.Search(len(s), func(i int) bool {
return s[i].PK() > marker
Expand All @@ -73,6 +73,8 @@ func (s SortedObj[PkType, T]) Slice(marker PkType, max uint32) []T {
return s[start : uint32(start)+remaining]
}

// GetIndex returns -1 if no item of the array has the given PRIMARY KEY, or the position of the first
// element with that PRIMARY KEY
func (s SortedObj[PkType, T]) GetIndex(id PkType) int {
i := sort.Search(len(s), func(i int) bool {
return s[i].PK() >= id
Expand All @@ -93,7 +95,7 @@ func (s SortedObj[PkType, T]) Get(id PkType) (out T, ok bool) {

func (s SortedObj[PkType, T]) Has(id PkType) bool { return s.GetIndex(id) >= 0 }

// RemovePK identifies the position of the element with the given primary key
// Remove identifies the position of the element with the given PRIMARY KEY
// and then removes it and restores the sorting of the set.
func (s *SortedObj[PkType, T]) Remove(pk PkType) {
idx := s.GetIndex(pk)
Expand Down
12 changes: 6 additions & 6 deletions sorted_obj_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,19 @@ func TestObj_Slice(T *testing.T) {
testSlice(bag[0].PK(), 2, bag[1], bag[2])
testSlice(bag[0].PK()-1, 2, bag[0], bag[1])
testSlice(bag[len(bag)-1].PK(), 1)
testSlice(bag[0].PK()-1, minSliceSize-1, bag[:minSliceSize]...)
testSlice(bag[0].PK()-1, maxSliceSize+1, bag...)
testSlice(bag[0].PK()-1, MinSliceSize-1, bag[:MinSliceSize]...)
testSlice(bag[0].PK()-1, MaxSliceSize+1, bag...)
}

func TestObj_SliceLarge(T *testing.T) {
const total = 3 * maxSliceSize
const total = 3 * MaxSliceSize
var bag SortedObj[int64, *Obj]
for i := int64(0); i < total; i++ {
bag.Add(&Obj{i})
}
bag.Assert()
slice := bag.Slice(bag[0].PK(), total)
if len(slice) < minSliceSize || len(slice) > maxSliceSize {
if len(slice) < MinSliceSize || len(slice) > MaxSliceSize {
T.Fatal()
}
}
Expand Down Expand Up @@ -165,10 +165,10 @@ func (s SortedObj[int64, T]) Assert() {
// Check validates the ordering and the unicity of the elements in the array
func (s SortedObj[int64, T]) Check() error {
if !sort.IsSorted(s) {
return ErrUnsorted
return errUnsorted
}
if !s.areItemsUnique() {
return ErrDuplicates
return errDuplicates
}
return nil
}
Expand Down
10 changes: 6 additions & 4 deletions sorted_raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ func (s *SortedRaw[T]) Append(a ...T) {
}

func (s SortedRaw[T]) Slice(marker T, max uint32) []T {
if max < minSliceSize {
max = minSliceSize
} else if max > maxSliceSize {
max = maxSliceSize
if max < MinSliceSize {
max = MinSliceSize
} else if max > MaxSliceSize {
max = MaxSliceSize
}
start := sort.Search(len(s), func(i int) bool {
return s[i] > marker
Expand All @@ -62,6 +62,8 @@ func (s SortedRaw[T]) Slice(marker T, max uint32) []T {
return s[start : uint32(start)+remaining]
}

// GetIndex returns -1 if no item of the array is identical to the given value, or the position
// of the first element.
func (s SortedRaw[T]) GetIndex(id T) int {
i := sort.Search(len(s), func(i int) bool {
return s[i] >= id
Expand Down
12 changes: 6 additions & 6 deletions sorted_raw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,19 @@ func TestRaw_Slice(T *testing.T) {
testSlice(bag[0], 2, bag[1], bag[2])
testSlice(bag[0]-1, 2, bag[0], bag[1])
testSlice(bag[len(bag)-1], 1)
testSlice(bag[0]-1, minSliceSize-1, bag[:minSliceSize]...)
testSlice(bag[0]-1, maxSliceSize+1, bag...)
testSlice(bag[0]-1, MinSliceSize-1, bag[:MinSliceSize]...)
testSlice(bag[0]-1, MaxSliceSize+1, bag...)
}

func TestRaw_SliceLarge(T *testing.T) {
const total = 3 * maxSliceSize
const total = 3 * MaxSliceSize
var bag SortedRaw[int]
for i := 0; i < total; i++ {
bag.Add(i)
}
bag.Assert()
slice := bag.Slice(bag[0], total)
if len(slice) < minSliceSize || len(slice) > maxSliceSize {
if len(slice) < MinSliceSize || len(slice) > MaxSliceSize {
T.Fatal()
}
}
Expand Down Expand Up @@ -173,10 +173,10 @@ func (s SortedRaw[int]) Assert() {
// Check validates the ordering and the unicity of the elements in the array
func (s SortedRaw[int]) Check() error {
if !sort.IsSorted(s) {
return ErrUnsorted
return errUnsorted
}
if !s.areItemsUnique() {
return ErrDuplicates
return errDuplicates
}
return nil
}
Expand Down

0 comments on commit 444a639

Please sign in to comment.