Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Neel/linkedlist #12

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 34 additions & 34 deletions pkg/linkedlist/linkedlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,82 +6,82 @@ package linkedlist

import (
"bytes"
"fmt"

"github.com/neelkshah/pandora/config"
)

// LinkedList is the data structure used for the hash table.
type LinkedList struct {
head *linkedListNode
tail *linkedListNode
head *Node
tail *Node
count int
}

// linkedListNode is the data structure that forms the LinkedList.
type linkedListNode struct {
values []valueNode
nextNode *linkedListNode
// Node is the data structure that forms the LinkedList.
type Node struct {
Values []valueNode
nextNode *Node
}

// valueNode contains a single value to be stored in the LinkedList.
// ValueNode contains a single value to be stored in the LinkedList.
type valueNode struct {
key []byte
value []byte
Key []byte
Value []byte
}

// CreateLinkedList returns a pointer to a new empty linked list instance.
func CreateLinkedList() *LinkedList {
// createImpl returns a pointer to a new empty linked list instance.
func createImpl() *LinkedList {
linkedList := LinkedList{head: nil, tail: nil, count: 0}
return &linkedList
}

// Append appends a given value to the end of the referenced LinkedList instance.
func (linkedList *LinkedList) Append(key []byte, value []byte) {
var newValue = valueNode{key: key, value: value}
// AppendImpl appends a given value to the end of the referenced LinkedList instance.
func (linkedList *LinkedList) appendImpl(key []byte, value []byte) {
var newValue = valueNode{Key: key, Value: value}
if linkedList.count == 0 {
var newNode = linkedListNode{values: []valueNode{newValue}, nextNode: nil}
var newNode = Node{Values: []valueNode{newValue}, nextNode: nil}
linkedList.head = &newNode
linkedList.tail = &newNode
linkedList.count = 1
return
}
var tailNode = linkedList.tail
if len(tailNode.values) < config.NODEFATNESS {
tailNode.values = append(tailNode.values, newValue)
if len(tailNode.Values) < config.NODEFATNESS {
tailNode.Values = append(tailNode.Values, newValue)
} else {
var newNode = linkedListNode{values: []valueNode{newValue}, nextNode: nil}
var newNode = Node{Values: []valueNode{newValue}, nextNode: nil}
tailNode.nextNode = &newNode
linkedList.tail = &newNode
}
linkedList.count++
}

// Get returns the values associate with the key.
func (linkedList *LinkedList) Get(key []byte) ([][]byte, bool) {
// GetImpl returns the values associate with the key.
func (linkedList *LinkedList) getImpl(key []byte) ([][]byte, bool) {
if linkedList == nil || linkedList.head == nil {
return nil, true
}
var currentNode = linkedList.head
var result = make([][]byte, 0)
for {
if currentNode == nil {
break
return result, len(result) != 0
}
for _, vnode := range (*currentNode).values {
if bytes.Equal(vnode.key, key) {
result = append(result, vnode.value)
for _, vnode := range (*currentNode).Values {
if bytes.Equal(vnode.Key, key) {
result = append(result, vnode.Value)
}
}
currentNode = currentNode.nextNode
}
return result, false
}

// Delete deletes all key-value pairs having the key passed as parameter.
// It returns the number of deleted pairs and a bool indicating occurrence of an error.
func (linkedList *LinkedList) Delete(key []byte) (int, bool) {
// DeleteImpl deletes all key-value pairs having the key passed as parameter.
// It returns the number of deleted pairs and any error.
func (linkedList *LinkedList) deleteImpl(key []byte) (int, error) {
if linkedList == nil || linkedList.head == nil {
return 0, true
return 0, fmt.Errorf("The linked list is empty")
}
var currentNode = linkedList.head
var count = 0
Expand All @@ -90,16 +90,16 @@ func (linkedList *LinkedList) Delete(key []byte) (int, bool) {
if currentNode == nil {
break
}
for _, vnode := range (*currentNode).values {
if !bytes.Equal(vnode.key, key) {
(*currentNode).values[k] = vnode
for _, vnode := range (*currentNode).Values {
if !bytes.Equal(vnode.Key, key) {
(*currentNode).Values[k] = vnode
k++
continue
}
count++
}
(*currentNode).values = (*currentNode).values[:k]
(*currentNode).Values = (*currentNode).Values[:k]
currentNode = currentNode.nextNode
}
return count, false
return count, nil
}
60 changes: 60 additions & 0 deletions pkg/linkedlist/linkedlist_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Author: Neel Shah, 2020
// linkedlist_api.go provides the API for the CRUD methods for the linked-list ADT
// to be used as part of the hash table ADT.

package linkedlist

// CreateLinkedList returns a pointer to a new empty linked list instance.
func CreateLinkedList() *LinkedList {
return createImpl()
}

// Append appends a key value pair to the linked list after checking their respective types.
func (linkedlist *LinkedList) Append(iKey interface{}, iValue interface{}) error {

pair, conversionError := convert(iKey, iValue)

if conversionError != nil {
return conversionError
}

linkedlist.appendImpl(pair[0], pair[1])
return nil
}

// Get gets the value array of values associated with the passed key.
func (linkedlist *LinkedList) Get(iKey interface{}) ([][]byte, bool, error) {

key, conversionError := convert(iKey)

if conversionError != nil {
return nil, false, conversionError
}

valueArray, found := linkedlist.getImpl(key[0])

return valueArray, found, nil
}

// Delete deletes the key-value pairs associated with the passed key. It returns the count of deleted pairs and any error.
func (linkedlist *LinkedList) Delete(iKey interface{}) (int, error) {
key, conversionError := convert(iKey)

if conversionError != nil {
return 0, conversionError
}

deletedCount, deletionError := linkedlist.deleteImpl(key[0])

return deletedCount, deletionError
}

// Head returns the head node of the linked list.
func (linkedlist *LinkedList) Head() *Node {
return linkedlist.head
}

// Next returns the next node in the linked list.
func (node *Node) Next() *Node {
return node.nextNode
}
80 changes: 47 additions & 33 deletions pkg/linkedlist/linkedlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,89 +25,103 @@ func TestCreateLinkedList(t *testing.T) {
// Test append function for single value append.
func TestAppend(t *testing.T) {
var response = *CreateLinkedList()
response.Append([]byte("a"), helper.IntToByte(5))
if response.head == nil ||
appendError := response.Append("a", 5)
if appendError != nil ||
response.head == nil ||
response.tail == nil ||
response.count != 1 ||
response.head != response.tail ||
helper.ByteToInt(response.head.values[0].value) != 5 ||
len(response.head.values) != 1 {
t.Fail()
helper.ByteToInt(response.head.Values[0].Value) != 5 ||
len(response.head.Values) != 1 {
t.Fatalf("Failed to append single key-value pair to empty linked list.")
}
}

// Test whether fatness is maintained during append.
func TestFatness(t *testing.T) {
var response = CreateLinkedList()
var list = CreateLinkedList()
var N = 3 * config.NODEFATNESS
for i := 1; i <= N; i++ {
var key = helper.IntToByte(rand.Uint64())
var value = helper.IntToByte(rand.Uint64())
response.Append(key, value)
var key = rand.Uint64()
var value = rand.Uint64()
if appendError := list.Append(key, value); appendError != nil {
t.Fatalf("Error in appending key-value pair %v, %v to linkedlist.\nInner error: %v", key, value, appendError)
}
}
var responselist = *response
var responselist = *list
if (responselist).count != 3*config.NODEFATNESS ||
responselist.head == responselist.tail ||
responselist.head == nil ||
responselist.tail == nil {
t.Fail()
t.Fatalf("Error in appending to fat nodes.")
}
var currentNode = *responselist.head
var i = 0
for {
if len(currentNode.values) != config.NODEFATNESS {
t.Fail()
if len(currentNode.Values) != config.NODEFATNESS {
t.Fatalf("Error in individual node fatness.")
}
i++
if currentNode.nextNode == nil {
return
break
}
currentNode = *currentNode.nextNode
}
if i != 3 {
t.Fatalf("Overall fatness not being maintained. Number of fat nodes != 3")
}
}

// Test get
func TestGet(t *testing.T) {
var response = CreateLinkedList()
response.Append(helper.IntToByte(5), helper.IntToByte(7))
if result, isEmpty := response.Get(helper.IntToByte(5)); isEmpty == true || !bytes.Equal(result[0], helper.IntToByte(7)) {
t.Fail()
var list = CreateLinkedList()
if appendError := list.Append(5, 7); appendError != nil {
t.Fatalf("Error in appending key-value pair to linkedlist.\nInner error: %v", appendError)
}
if result, isEmpty := response.Get(helper.IntToByte(7)); isEmpty == true || len(result) != 0 {
t.Fail()
if result, found, _ := list.Get(5); !found || !bytes.Equal(result[0], helper.IntToByte(7)) {
t.Fatalf("Value present in linkedlist, but not found.")
}
if result, found, _ := list.Get(helper.IntToByte(7)); found || len(result) != 0 {
t.Fatalf("Value not present in linkedlist, returned as found.")
}
}

// Test get behaviour for an empty LinkedList instance
func TestGetNilValidation(t *testing.T) {
var response = CreateLinkedList()
if result, isEmpty := response.Get(helper.IntToByte(5)); isEmpty == false || result != nil {
t.Fail()
if result, found, _ := response.Get(helper.IntToByte(5)); found || result != nil {
t.Fatalf("Getting values from empty linkedlist.")
}
}

// Test delete behaviour for an empty LinkedList instance
func TestDeleteNilValidation(t *testing.T) {
var response = CreateLinkedList()
if count, isEmpty := response.Delete(helper.IntToByte(5)); isEmpty == false || count != 0 {
t.Fail()
if count, errorValue := response.Delete(helper.IntToByte(5)); errorValue == nil || count != 0 {
t.Fatalf("Deleting values from empty linkedlist.")
}
}

// Test deletion
func TestDelete(t *testing.T) {
var response = CreateLinkedList()
for i := 1; i <= 5; i++ {
var key = helper.IntToByte(rand.Uint64())
var value = helper.IntToByte(rand.Uint64())
response.Append(key, value)
var key = rand.Uint64()
var value = rand.Uint64()
if appendResponse := response.Append(key, value); appendResponse != nil {
t.Fatalf("Error in appending key-value pair to linkedlist.")
}
}
response.Append(helper.IntToByte(5), helper.IntToByte(7))
response.Append(5, 7)
for i := 1; i <= 5; i++ {
var key = helper.IntToByte(rand.Uint64())
var value = helper.IntToByte(rand.Uint64())
response.Append(key, value)
var key = rand.Uint64()
var value = rand.Uint64()
if appendResponse := response.Append(key, value); appendResponse != nil {
t.Fatalf("Error in appending key-value pair to linkedlist.")
}
}

if count, status := response.Delete(helper.IntToByte(5)); count < 1 || status == true {
t.Fail()
if count, errorValue := response.Delete(5); count < 1 || errorValue != nil {
t.Fatalf("Error in deleting key-value pair.")
}
}
30 changes: 27 additions & 3 deletions pkg/linkedlist/utils.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package linkedlist

import "fmt"
import (
"fmt"

"github.com/neelkshah/pandora/pkg/helper"
)

// Print prints the contents of a LinkedList instance.
func (linkedList *LinkedList) Print() {
fmt.Printf("Count of elements: %v\n", linkedList.count)
var currentNode = *linkedList.head
for {
for _, element := range currentNode.values {
fmt.Printf("%v, %v\t", element.key, element.value)
for _, element := range currentNode.Values {
fmt.Printf("%v, %v\t", element.Key, element.Value)
}
if currentNode.nextNode == nil {
return
Expand All @@ -17,3 +21,23 @@ func (linkedList *LinkedList) Print() {
fmt.Println()
}
}

// convert converts the given slice of values into a slice of byte slices.
func convert(originalValues ...interface{}) ([][]byte, error) {
var values [][]byte

for _, originalValue := range originalValues {
switch keyType := originalValue.(type) {
case int:
values = append(values, helper.IntToByte(uint64(originalValue.(int))))
case uint64:
values = append(values, helper.IntToByte(originalValue.(uint64)))
case string:
values = append(values, []byte(originalValue.(string)))
default:
return nil, fmt.Errorf("%v is of invalid type %v", originalValue, keyType)
}
}

return values, nil
}
Loading