diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..600d2d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode \ No newline at end of file diff --git a/debug.go b/debug.go new file mode 100644 index 0000000..8ecde8e --- /dev/null +++ b/debug.go @@ -0,0 +1,21 @@ +package main + +import "fmt" + +// just prints the skiplist +func skiplist_debug(a *skiplist) { + level := a.n_levels - 1 + finger := a.head + + for ; level >= 0; level-- { + counter := 0 + if finger.next[level] == nil { + fmt.Print(nil) + } + for node := finger.next[level]; node != nil; node = node.next[level] { + counter++ + fmt.Print(" ", node.value) + } + fmt.Println(" nil", counter) + } +} diff --git a/rand_func.go b/rand_func.go new file mode 100644 index 0000000..64ed648 --- /dev/null +++ b/rand_func.go @@ -0,0 +1,30 @@ +package main + +import ( + "math/rand" + "time" +) + +const Mask = ((1 << SKIPLIST_MAX_LEVEL) - 1) + +func coin_tosses(prob float64, max_levels int) (counter int) { + t1 := time.Now() + + counter = 1 + + res_mask := rand.Uint64() & Mask + // find first zero in float representation + for ; res_mask&1 == 0; res_mask >>= 1 { + counter++ + } + + //res := rand.Float64() + //for res < prob { + // res = rand.Float64() + // counter++ + //} + + randtime += time.Now().Sub(t1) + return counter + +} diff --git a/skiplist b/skiplist index 2898af7..a07c751 100755 Binary files a/skiplist and b/skiplist differ diff --git a/skiplist.go b/skiplist.go index 0d12422..3e5e34f 100644 --- a/skiplist.go +++ b/skiplist.go @@ -2,8 +2,6 @@ package main import ( "fmt" - "math/rand" - "sync" "time" ) @@ -30,7 +28,6 @@ func (initial *skiplist) Init_skiplist(prob float64, max_levels int) { initial.max_levels = max_levels var head *skiplist_node = new(skiplist_node) - head.value = -1 head.fully_linked = true head.marked = false @@ -56,11 +53,11 @@ func debug(a skiplist) { } } -func (list *skiplist) ToSortedArray() []int { +func (list *skiplist) ToSortedArray() []interface{} { /* make a sorted array out of the skiplist returns the lowest level */ - arr := make([]int, list.n_elements, list.n_elements) - fmt.Println(list.n_elements) + arr := make([]interface{}, list.n_elements, list.n_elements) + //fmt.Println(list.n_elements) counter := 0 for current_node := list.head.next[0]; current_node != nil; current_node = current_node.next[0] { arr[counter] = current_node.value @@ -74,16 +71,18 @@ func (list *skiplist) ToSortedArray() []int { } -func (head *skiplist) Find(val int, prev, next []*skiplist_node) (found_level int) { +func (head *skiplist) Find(val interface{}, prev, next []*skiplist_node) (found_level int) { /* Find where the element should be and return the first level where it was found and next and previous elements for every level. - Returns -1 when not found */ + Returns the first level where it was found or + -1 when not found */ // could be modified by inserts head.lock.Lock() level := head.n_levels - 1 head.lock.Unlock() + // much faster than starting at max pred := head.head found_level = -1 @@ -93,13 +92,13 @@ func (head *skiplist) Find(val int, prev, next []*skiplist_node) (found_level in for ; level >= 0; level-- { // horizontally curr = pred.next[level] - for curr != nil && curr.value < val { + for curr != nil && Less(curr.value, val) { pred = curr curr = pred.next[level] } // next of where it should be - if curr != nil && curr.value == val && found_level == -1 { + if curr != nil && Equals(curr.value, val) && found_level == -1 { found_level = level } @@ -112,7 +111,7 @@ func (head *skiplist) Find(val int, prev, next []*skiplist_node) (found_level in return found_level } -func (head *skiplist) Contains(val int) bool { +func (head *skiplist) Contains(val interface{}) bool { /* same function as find but returns as soon as item is found, ignoring below levels and does not return prev,next */ @@ -127,14 +126,14 @@ func (head *skiplist) Contains(val int) bool { for ; level >= 0; level-- { // horizontally curr = pred.next[level] - for curr != nil && curr.value < val { + for curr != nil && Less(curr.value, val) { pred = curr curr = pred.next[level] } //found something or have to go down // is the next element what I seek - if curr != nil && curr.value == val { + if curr != nil && Equals(curr.value, val) { node := curr return node.fully_linked && !node.marked } @@ -143,7 +142,7 @@ func (head *skiplist) Contains(val int) bool { return false } -func (head *skiplist) Insert(v int) bool { +func (head *skiplist) Insert(v interface{}) bool { // highest level of insertion top_level := coin_tosses(head.prob, head.max_levels) @@ -155,11 +154,12 @@ func (head *skiplist) Insert(v int) bool { } head.lock.Unlock() - for { + // buffers to store prev and next pointers + var prev, next []*skiplist_node + prev = make([]*skiplist_node, SKIPLIST_MAX_LEVEL) + next = make([]*skiplist_node, SKIPLIST_MAX_LEVEL) - var prev, next []*skiplist_node - prev = make([]*skiplist_node, SKIPLIST_MAX_LEVEL) - next = make([]*skiplist_node, SKIPLIST_MAX_LEVEL) + for { // find insertion point and previous and next nodes found_level := head.Find(v, prev, next) @@ -206,19 +206,20 @@ func (head *skiplist) Insert(v int) bool { } // can the insertion proceed + // node is locked so we can check next valid = !pred.marked && (succ == nil || !succ.marked) && pred.next[level] == succ } // cannot add if !valid { // unlock to try again - var _prevPred *skiplist_node = nil + prevPred = nil for i := highest_locked; i >= 0; i-- { //fmt.Println("Unlocking", i, prev[i].value) - if _prevPred != prev[i] { + if prevPred != prev[i] { prev[i].mux.Unlock() } - _prevPred = prev[i] + prevPred = prev[i] } // restart attempt @@ -238,12 +239,10 @@ func (head *skiplist) Insert(v int) bool { } // new node is ok newNode.fully_linked = true - //fmt.Println("highest locked", highest_locked) //unlock prevPred = nil for i := highest_locked; i >= 0; i-- { - //fmt.Println("Unlocking", i, prev[i].value) if prevPred != prev[i] { prev[i].mux.Unlock() } @@ -251,8 +250,6 @@ func (head *skiplist) Insert(v int) bool { } - //fmt.Println(head.Len()) - head.lock.Lock() head.n_elements = head.n_elements + 1 head.lock.Unlock() @@ -262,7 +259,7 @@ func (head *skiplist) Insert(v int) bool { } -func (head *skiplist) Remove(val int) bool { +func (head *skiplist) Remove(val interface{}) bool { /* remove node */ var nodeToDelete *skiplist_node = nil @@ -272,7 +269,6 @@ func (head *skiplist) Remove(val int) bool { var prev, next [SKIPLIST_MAX_LEVEL]*skiplist_node for { - //fmt.Println("looping") // try to find node found_level := head.Find(val, prev[:], next[:]) //fmt.Println("level", found_level) @@ -323,6 +319,17 @@ func (head *skiplist) Remove(val int) bool { // can't delete try again if !valid { + + // unlock to try again + prevPred = nil + for i := highest_locked; i >= 0; i-- { + //fmt.Println("Unlocking", i, prev[i].value) + if prevPred != prev[i] { + prev[i].mux.Unlock() + } + prevPred = prev[i] + } + continue } // actually delete node @@ -354,90 +361,10 @@ func (head *skiplist) Remove(val int) bool { } } -// func CanDelete(candidate *skiplist_node, found_level int) bool { - fmt.Println("aaa", candidate.fully_linked, candidate.top_level, found_level, candidate.marked) + //fmt.Println("aaa", candidate.fully_linked, candidate.top_level, found_level, candidate.marked) return candidate.fully_linked && candidate.top_level == found_level && !candidate.marked } -func coin_tosses(prob float64, max_levels int) int { - t1 := time.Now() - - var counter int = 1 - - /* - res_mask := math.Float64bits(rand.Float64()) & ((1 << SKIPLIST_MAX_LEVEL) - 1) - // find first zero in float representation - for ; res_mask&1 == 0; res_mask >>= 1 { - counter++ - } - */ - - res := rand.Float64() - for res < prob { - res = rand.Float64() - counter++ - } - - randtime += time.Now().Sub(t1) - return counter - -} - -func eval_sort(arr []int) { - if len(arr) == 0 { - return - } - prev := arr[0] - for index := 1; index < len(arr); index++ { - if arr[index] < prev { - fmt.Println(prev, arr[index], index) - } - prev = arr[index] - } - - fmt.Println(len(arr)) -} - -func (head *skiplist) Inserter(v int, wg *sync.WaitGroup) { - for index := v; index < (v+1)*2500000; index++ { - head.Insert(index) - } - defer wg.Done() - -} - -func (head *skiplist) Remover(v int, wg *sync.WaitGroup) { - head.Remove(v) - defer wg.Done() - -} - func main() { - start := time.Now() - rand.Seed(time.Now().UTC().UnixNano()) - - var head *skiplist = new(skiplist) - head.Init_skiplist(0.5, 20) - - var arr []int = make([]int, 10000000) - var wg sync.WaitGroup - wg.Add(1) - for index := 0; index < 1; index++ { - go head.Inserter(index, &wg) - arr[index] = index - } - - wg.Wait() - - sorted := head.ToSortedArray() - eval_sort(sorted) - - t := time.Now() - elapsed := t.Sub(start) - fmt.Println(elapsed) - fmt.Println(randtime) - //debug(*head) - - //debug(head) } diff --git a/skiplist_copy.go.bak b/skiplist_copy.go.bak deleted file mode 100644 index 61b248b..0000000 --- a/skiplist_copy.go.bak +++ /dev/null @@ -1,245 +0,0 @@ -package main - -import ( - "fmt" - "math/rand" - "time" -) - -type skiplist_node struct { - value int - next *skiplist_node - prev *skiplist_node - down *skiplist_node -} -type skiplist struct { - n_levels uint32 - head *skiplist_node - n_elements int - prob float64 - max_levels uint32 -} - -func (head *skiplist) Height() uint32 { - return head.n_levels -} - -func (head *skiplist) Len() int { - return head.n_elements -} - -type NotFoundErr string - -func (err NotFoundErr) Error() string { - return "Element not found" -} -func Init_skiplist(prob float64, max_levels uint32) skiplist { - var initial skiplist - initial.n_levels = 1 - initial.prob = prob - initial.max_levels = max_levels - - var head skiplist_node - head.down = nil - head.next = nil - head.value = -1 - - initial.n_elements = 0 - - initial.head = &head - - return initial -} - -func debug(a skiplist) { - ins := a.head - - for ; ins != nil; ins = ins.down { - counter := 0 - if ins.next == nil { - fmt.Print(nil) - } - for node := ins.next; node != nil; node = node.next { - counter++ - fmt.Print(" ", node.value) - } - fmt.Println(" nil", counter) - } -} - -func (head *skiplist) ToSortedArray() []int { - arr := make([]int, head.n_elements, head.n_elements) - curr_level := head.head - for levels_down := head.n_levels; levels_down != 1; curr_level, levels_down = curr_level.down, levels_down-1 { - } - - current_node := curr_level.next - counter := 0 - for ; current_node != nil; current_node = current_node.next { - arr[counter] = current_node.value - //fmt.Print(arr[counter], current_node.value, " ") - counter++ - } - - //fmt.Println(arr) - - return arr - -} - -func (head *skiplist) Find(val int) *skiplist_node { - curr := head.head - // vertically - for ; curr != nil; curr = curr.down { - // horizontally - for ; curr.next != nil && curr.next.value < val; curr = curr.next { - } - //found something or have to go down - - // is the next element what I seek - if curr.next != nil && curr.next.value == val { - return curr.next - } - } - // not found - return nil -} - -func (head *skiplist) Remove(val int) error { - - node := head.Find(val) - - if node == nil { - // never found - return new(NotFoundErr) // error - } else { - // traverse down and delete nodes - for node != nil { - - node.prev.next = node.next - if node.next != nil { - node.next.prev = node.prev - } - node = node.down - } - - // removed properly - head.n_elements-- - return nil - - } - -} - -func (head *skiplist) Insert(val int) int { - - head.n_elements++ - - levels := coin_tosses(head.prob, head.max_levels) - //fmt.Println("element ", val, levels) - ins_point := head.head - - if levels > head.n_levels { - var i uint32 = 0 - for ; i < levels-head.n_levels; i++ { - var new_node skiplist_node - new_node.down = head.head - new_node.value = -1 - head.head = &new_node - } - - //fmt.Println("entered") - head.n_levels = levels // new # of levels - } - - ins_point = head.head - - var current_node *skiplist_node - var prev *skiplist_node - var above *skiplist_node - - above = nil - var new_val *skiplist_node - //fmt.Println("insertion point", ins_point) - level := head.n_levels - for ; ins_point != nil; ins_point, level = ins_point.down, level-1 { - if level <= levels { - new_val = new(skiplist_node) - new_val.value = val // to be added - } - - // connect above level - if above != nil { - above.down = new_val - } - - prev = ins_point - // traverse horizontally to find insertion point - for current_node = ins_point.next; current_node != nil && current_node.value < val; prev, current_node = current_node, current_node.next { - } - - // don't insert at current level if - // too high - if level > levels { - ins_point = prev - continue - } - - // insert at the end - prev.next = new_val - new_val.next = current_node - new_val.prev = prev - above = new_val - - // don't start from the beginning - ins_point = prev - - } - return 0 - -} - -func coin_tosses(prob float64, max_levels uint32) uint32 { - var counter uint32 = 1 - res := rand.Float64() - for res > prob && counter < max_levels { - counter++ - res = rand.Float64() - } - - return counter - -} - -func eval_sort(arr []int) { - prev := arr[0] - for index := 1; index < len(arr); index++ { - if arr[index] < prev { - fmt.Println(prev, arr[index], index) - } - prev = arr[index] - } - - fmt.Println(len(arr)) -} - -func main() { - rand.Seed(time.Now().UTC().UnixNano()) - - var head skiplist - head = Init_skiplist(0.5, 20) - - var arr []int = make([]int, 10000000) - for index := 0; index < 1000000; index++ { - head.Insert(rand.Intn(1234500)) - arr[index] = index - } - - //sort.Ints(arr) - - sorted := head.ToSortedArray() - eval_sort(sorted) - fmt.Println(head.Len()) - - //debug(head) -} diff --git a/skiplist_struct.go b/skiplist_struct.go index 32d2945..df7d7cb 100644 --- a/skiplist_struct.go +++ b/skiplist_struct.go @@ -5,9 +5,8 @@ import "sync" const SKIPLIST_MAX_LEVEL = 30 type skiplist_node struct { - value int + value interface{} next [SKIPLIST_MAX_LEVEL]*skiplist_node - prev [SKIPLIST_MAX_LEVEL]*skiplist_node marked bool fully_linked bool mux sync.Mutex diff --git a/skiplist_test.go b/skiplist_test.go new file mode 100644 index 0000000..dde26af --- /dev/null +++ b/skiplist_test.go @@ -0,0 +1,346 @@ +package main + +import ( + "fmt" + "math/rand" + "sync" + "testing" + "time" +) + +const AMOUNT = 1000000 // amount of elements to insert +const N_ROUTINES = 100 // routines to start for concurrent tests + +// helpers +func eval_sort(arr []interface{}) bool { + if len(arr) == 0 { + return true + } + prev := arr[0] + for index := 1; index < len(arr); index++ { + if Less(arr[index], prev) { + fmt.Println("Items out of order:", arr[index], prev) + return false + } + prev = arr[index] + } + return true +} + +func (head *skiplist) Inserter(v int, wg *sync.WaitGroup) bool { + defer wg.Done() + for index := v * (AMOUNT / N_ROUTINES); index < (v+1)*(AMOUNT/N_ROUTINES); index++ { + if !head.Insert(interface{}(index)) { + return false + } + } + return true +} + +func (head *skiplist) Remover(v int, wg *sync.WaitGroup) bool { + + defer wg.Done() + for index := v * (AMOUNT / N_ROUTINES); index < (v+1)*(AMOUNT/N_ROUTINES); index++ { + if !head.Remove(interface{}(index)) { + return false + } + } + return true + +} + +// basic functionality tests +func TestInsert(t *testing.T) { + + fmt.Println("---------------------------------------") + fmt.Println("Sequential integer add and test order") + fmt.Println("----------------------------------------") + + rand.Seed(time.Now().UTC().UnixNano()) + + var head *skiplist = new(skiplist) + head.Init_skiplist(0.5, 20) + + //var wg sync.WaitGroup + + fmt.Println("Inserting numbers from 0 to", AMOUNT-1) + for index := 0; index < AMOUNT; index++ { + if !head.Insert(interface{}(index)) { + t.Errorf("Could not insert item %d", index) + } + } + + for index := 0; index < AMOUNT; index++ { + if !head.Contains((interface{}(index))) { + t.Errorf("Inserted number %d but not contained in skiplist", index) + } + } + + sorted := head.ToSortedArray() + ok := eval_sort(sorted) + + if !ok { + t.Errorf("Items out of order") + } + + if head.n_elements != AMOUNT { + t.Errorf("Skiplist should contain %d items but contains %d", AMOUNT, head.n_elements) + } + + fmt.Println("OK!") + fmt.Println("----------------------------------------") + +} + +func TestRemove(t *testing.T) { + + fmt.Println("---------------------------------------") + fmt.Println("Sequential integer add, check and remove") + fmt.Println("----------------------------------------") + + rand.Seed(time.Now().UTC().UnixNano()) + + var head *skiplist = new(skiplist) + head.Init_skiplist(0.5, 20) + + //var wg sync.WaitGroup + + fmt.Println("Inserting numbers from 0 to", AMOUNT-1) + for index := 0; index < AMOUNT; index++ { + if !head.Insert(interface{}(index)) { + + } + } + + for index := 0; index < AMOUNT; index++ { + if !head.Contains((interface{}(index))) { + t.Errorf("Inserted number %d but not contained in skiplist", index) + } + } + + if head.n_elements != AMOUNT { + t.Errorf("Skiplist should contain %d items but contains %d", AMOUNT, head.n_elements) + } + + fmt.Println("Removing numbers from 0 to", AMOUNT-1) + amount_removed := 0 + for index := 0; index < AMOUNT; index++ { + if !head.Remove((interface{}(index))) { + t.Errorf("Inserted number %d but not contained in skiplist", index) + } else { + amount_removed++ + } + + if !(AMOUNT-amount_removed == head.n_elements) { + t.Errorf("Item %d reported removed but item count not updated", index) + } + } + + if !(head.n_elements == 0) { + t.Errorf("Skiplist should be empty") + } + + fmt.Println("OK!") + fmt.Println("----------------------------------------") + +} + +func TestRandOperation(t *testing.T) { + fmt.Println("------------------------------------") + fmt.Println("Random integer add, check and remove") + fmt.Println("------------------------------------") + rand.Seed(time.Now().UTC().UnixNano()) + + var head *skiplist = new(skiplist) + head.Init_skiplist(0.5, 20) + + //var wg sync.WaitGroup + + added := 0 + fmt.Println("Inserting", AMOUNT, "random numbers") + for index := 0; index < AMOUNT; index++ { + if head.Insert(interface{}(rand.Intn(AMOUNT))) { + added++ + } + } + + amount_removed := 0 + for index := 0; index < AMOUNT; index++ { + if head.Contains((interface{}(index))) { + if !head.Remove((interface{}(index))) { + t.Errorf("Number %d exists but could not be removed from skiplist ", index) + } + + amount_removed++ + } + // check order for some removes + if index%(AMOUNT/100) == 0 { + sorted := head.ToSortedArray() + ok := eval_sort(sorted) + + if !ok { + t.Errorf("Items out of order after remove") + } + } + + if !(added-amount_removed == head.n_elements) { + t.Errorf("Item %d reported removed but item count not updated", index) + } + } + + if !(head.n_elements == 0) { + t.Errorf("Skiplist should be empty") + } + + fmt.Println("OK!") + fmt.Println("----------------------------------------") + +} + +func TestConcurrentInsertAndOrder(t *testing.T) { + + fmt.Println("--------------------------------------") + fmt.Println("Sequential integer add and test order") + fmt.Println("-------------------------------------") + + rand.Seed(time.Now().UTC().UnixNano()) + + var head *skiplist = new(skiplist) + head.Init_skiplist(0.5, 20) + + var wg sync.WaitGroup + + wg.Add(N_ROUTINES) + fmt.Println("Spawing", N_ROUTINES, "coroutines to insert ", AMOUNT, "elements") + for index := 0; index < N_ROUTINES; index++ { + if !head.Inserter(index, &wg) { + t.Errorf("Could not insert item %d", index) + } + } + + wg.Wait() + + // lockless contains doesn't matter if run on one + // or many coroutines + for index := 0; index < AMOUNT; index++ { + if !head.Contains((interface{}(index))) { + t.Errorf("Inserted number %d but not contained in skiplist", index) + } + } + + sorted := head.ToSortedArray() + ok := eval_sort(sorted) + + if !ok { + t.Errorf("Items out of order") + } + + if head.n_elements != AMOUNT { + t.Errorf("Skiplist should contain %d items but contains %d", AMOUNT, head.n_elements) + } + + fmt.Println("OK!") + fmt.Println("----------------------------------------") + +} + +func TestConcurrentInsertRemove(t *testing.T) { + + fmt.Println("--------------------------------------------") + fmt.Println("Concurrent Sequential integer add and remove") + fmt.Println("--------------------------------------------") + + rand.Seed(time.Now().UTC().UnixNano()) + + var head *skiplist = new(skiplist) + head.Init_skiplist(0.5, 20) + + var wg sync.WaitGroup + + wg.Add(N_ROUTINES) + fmt.Println("Spawing", N_ROUTINES, "coroutines to insert ", AMOUNT, "elements") + for index := 0; index < N_ROUTINES; index++ { + if !head.Inserter(index, &wg) { + t.Errorf("Could not insert item %d", index) + } + } + + wg.Wait() + + // lockless contains doesn't matter if run on one + // or many coroutines + for index := 0; index < AMOUNT; index++ { + if !head.Contains((interface{}(index))) { + t.Errorf("Inserted number %d but not contained in skiplist", index) + } + } + + wg.Add(N_ROUTINES) + fmt.Println("Spawing", N_ROUTINES, "coroutines to remove ", AMOUNT, "elements") + for index := 0; index < N_ROUTINES; index++ { + if !head.Remover(index, &wg) { + t.Errorf("Could not insert item %d", index) + } + } + + wg.Wait() + + if head.n_elements != 0 { + t.Errorf("Skiplist should be empty but contains %d elements", head.n_elements) + } + + fmt.Println("OK!") + fmt.Println("----------------------------------------") + +} + +func TestConcurrentMixed(t *testing.T) { + + fmt.Println("---------------------------------------") + fmt.Println("Mixed add and remove") + fmt.Println("----------------------------------------") + + rand.Seed(time.Now().UTC().UnixNano()) + + var head *skiplist = new(skiplist) + head.Init_skiplist(0.5, 20) + + var wg sync.WaitGroup + + wg.Add(N_ROUTINES) + fmt.Println("Spawing", N_ROUTINES, "coroutines to insert ", AMOUNT, "elements from 0 to", AMOUNT) + fmt.Println("and spawing", N_ROUTINES/2, "coroutines to remove elements from 0 to", AMOUNT/2) + for index := 0; index < N_ROUTINES; index++ { + if !head.Inserter(index, &wg) { + t.Errorf("Could not insert item %d", index) + } + } + + wg.Wait() + + wg.Add(N_ROUTINES / 2) + fmt.Println("Spawing", N_ROUTINES, "coroutines to remove ", AMOUNT, "elements") + for index := 0; index < N_ROUTINES/2; index++ { + if !head.Remover(index, &wg) { + t.Errorf("Could not insert item %d", index) + } + } + + wg.Wait() + + // lockless contains doesn't matter if run on one + // or many coroutines + for index := 0; index < AMOUNT/2; index++ { + if head.Contains((interface{}(index))) { + t.Errorf("%d should not be contained in skiplist", index) + } + } + + if head.n_elements != AMOUNT/2 { + t.Errorf("Skiplist should have %d elements but has %d", AMOUNT/2, head.n_elements) + } + + fmt.Println("OK!") + fmt.Println("----------------------------------------") + +} diff --git a/user_structs.go b/user_structs.go new file mode 100644 index 0000000..7496279 --- /dev/null +++ b/user_structs.go @@ -0,0 +1,40 @@ +package main + +// example struct setup for insertion to +// skiplist + +type mystr struct { + a int + b bool +} + +// Required + +// do not delete int case, +// used for testing +func Less(a, b interface{}) bool { + switch a.(type) { + case mystr: + a := a.(mystr) + b := b.(mystr) + return a.a < b.a + case int: + return a.(int) < b.(int) + default: + return false + } +} + +func Equals(a, b interface{}) bool { + switch a.(type) { + case mystr: + a := a.(mystr) + b := b.(mystr) + return a.a == b.a + case int: + return a.(int) == b.(int) + default: + return false + + } +}