From 04fa77e30d337fe64b9adb541d579d92158afb1a Mon Sep 17 00:00:00 2001 From: "Chaitanya.Narukulla" Date: Tue, 31 Oct 2017 16:47:34 -0700 Subject: [PATCH 01/24] Done with binheap --- binheap.py | 36 ++++++++++++++++++++++++++++++++++++ test_binheap.py | 0 2 files changed, 36 insertions(+) create mode 100644 binheap.py create mode 100644 test_binheap.py diff --git a/binheap.py b/binheap.py new file mode 100644 index 0000000..22e5da7 --- /dev/null +++ b/binheap.py @@ -0,0 +1,36 @@ +"""Binary heap.""" + + +class BinHeap(object): + """ini the class node.""" + + def __init__(self): + """Create a new min heap.""" + self.heaplist = [0] + self._size = 0 + + def push(self, val): + """.""" + self.heaplist.append(val) + self._size += 1 + idx = self._size + while idx // 2 > 0: + if self.heaplist[idx] > self.heaplist[idx // 2]: + temp_val = self.heaplist[idx] + self.heaplist[idx] = self.heaplist[idx // 2] + self.heaplist[idx // 2] = temp_val + idx = idx // 2 + + def pop(self): + """.""" + self._size -= 1 + pop_val = self.heaplist[1] + self.heaplist[1] = self.heaplist.pop() + idx = 1 + while idx * 2 < self._size: + if self.heaplist[idx * 2] > self.heaplist[idx * 2 + 1]: + temp_val = self.heaplist[idx * 2 + 1] + self.heaplist[idx * 2 + 1] = self.heaplist[idx] + self.heaplist[idx] = temp_val + idx = idx * 2 + return pop_val diff --git a/test_binheap.py b/test_binheap.py new file mode 100644 index 0000000..e69de29 From 3f009ca20d6a09b4aebd49db0a4c755bed0e8ccb Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Wed, 1 Nov 2017 17:16:36 -0700 Subject: [PATCH 02/24] reworked push and working on pop, also wrote some tests --- binheap.py | 36 -------------------- src/binheap.py | 82 +++++++++++++++++++++++++++++++++++++++++++++ src/conftest.py | 9 +++++ src/test_binheap.py | 65 +++++++++++++++++++++++++++++++++++ test_binheap.py | 0 5 files changed, 156 insertions(+), 36 deletions(-) delete mode 100644 binheap.py create mode 100644 src/binheap.py create mode 100644 src/conftest.py create mode 100644 src/test_binheap.py delete mode 100644 test_binheap.py diff --git a/binheap.py b/binheap.py deleted file mode 100644 index 22e5da7..0000000 --- a/binheap.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Binary heap.""" - - -class BinHeap(object): - """ini the class node.""" - - def __init__(self): - """Create a new min heap.""" - self.heaplist = [0] - self._size = 0 - - def push(self, val): - """.""" - self.heaplist.append(val) - self._size += 1 - idx = self._size - while idx // 2 > 0: - if self.heaplist[idx] > self.heaplist[idx // 2]: - temp_val = self.heaplist[idx] - self.heaplist[idx] = self.heaplist[idx // 2] - self.heaplist[idx // 2] = temp_val - idx = idx // 2 - - def pop(self): - """.""" - self._size -= 1 - pop_val = self.heaplist[1] - self.heaplist[1] = self.heaplist.pop() - idx = 1 - while idx * 2 < self._size: - if self.heaplist[idx * 2] > self.heaplist[idx * 2 + 1]: - temp_val = self.heaplist[idx * 2 + 1] - self.heaplist[idx * 2 + 1] = self.heaplist[idx] - self.heaplist[idx] = temp_val - idx = idx * 2 - return pop_val diff --git a/src/binheap.py b/src/binheap.py new file mode 100644 index 0000000..077dba0 --- /dev/null +++ b/src/binheap.py @@ -0,0 +1,82 @@ +"""Binary heap.""" + + +class Binheap(object): + """Iniitialize the class node.""" + + def __init__(self, iterable=None): + """Create a new min heap.""" + self.heaplist = [] + self._size = 0 + if isinstance(iterable, (list, tuple)): + for item in iterable: + self.push(item) + + def push(self, val): + """Push a value to the end of heap and sort up.""" + if type(val) == int or type(val) == float: + self.heaplist.append(val) + self._size += 1 + if self._size > 1: + sort_up = True + idx = self._size - 1 + while sort_up: + if idx % 2 == 0: + parent = (idx - 2) // 2 + else: + parent = (idx - 1) // 2 + if val < self.heaplist[parent]: + self.heaplist[idx] = self.heaplist[parent] + self.heaplist[parent] = val + idx = parent + else: + sort_up = False + else: + raise ValueError('You must input numbers only.') + + def pop(self): + """Remove first value in heap and sort down.""" + if self._size == 0: + raise IndexError('There are no values to pop.') + elif self._size == 1: + self._size -= 1 + return self.heaplist.pop() + elif self._size > 1: + pop_val = self.heaplist[0] + self.heaplist[0] = self.heaplist.pop() + self._size -= 1 + idx = 0 + sort_down = True + while sort_down: + l_child = idx * 2 + 1 + r_child = idx * 2 + 2 + if self.heaplist[l_child] and self.heaplist[l_child]: + if self.heaplist[l_child] > self.heaplist[r_child]: + if self.heaplist[r_child] < self.heaplist[idx]: + temp_val = self.heaplist[idx] + self.heaplist[idx] = self.heaplist[r_child] + self.heaplist[r_child] = temp_val + idx = r_child + else: + sort_down = False + else: + if self.heaplist[l_child] < self.heaplist[r_child]: + temp_val = self.heaplist[idx] + self.heaplist[idx] = self.heaplist[l_child] + self.heaplist[l_child] = temp_val + idx = l_child + else: + sort_down = False + elif self.heaplist[l_child] and not self.heaplist[r_child]: + if self.heaplist[l_child] < self.heaplist[r_child]: + temp_val = self.heaplist[idx] + self.heaplist[idx] = self.heaplist[l_child] + self.heaplist[l_child] = temp_val + sort_down = False + elif self.heaplist[r_child] and not self.heaplist[l_child]: + if self.heaplist[r_child] < self.heaplist[idx]: + temp_val = self.heaplist[idx] + self.heaplist[idx] = self.heaplist[r_child] + self.heaplist[r_child] = temp_val + sort_down = False + return pop_val diff --git a/src/conftest.py b/src/conftest.py new file mode 100644 index 0000000..870d234 --- /dev/null +++ b/src/conftest.py @@ -0,0 +1,9 @@ +"""Fixutres for test_binheap.py.""" +import pytest + + +@pytest.fixture +def ebh(): + """Initialize an empty binary heap.""" + from binheap import Binheap + return Binheap() diff --git a/src/test_binheap.py b/src/test_binheap.py new file mode 100644 index 0000000..864db40 --- /dev/null +++ b/src/test_binheap.py @@ -0,0 +1,65 @@ +"""Test binheap.py.""" +import pytest + + +def test_initialize_empty_heap(ebh): + """Test if init binary is empty.""" + assert ebh + + +def test_push_one_value(ebh): + """Test size after push one value into heap.""" + ebh.push(9) + assert ebh._size == 1 + + +def test_push_mulitp_value(ebh): + """Test size after push multiple values into heap.""" + for i in range(20): + ebh.push(i) + assert ebh._size == 20 + + +def test_push_one_non_num(ebh): + """Test for erron if pushing non numerical value into heap.""" + with pytest.raises(ValueError): + ebh.push('NOOOOOOOO!!!!!!!!!') + + +def test_push_iterable(): + """Test if push successfully takes iterable.""" + from binheap import Binheap + heap = Binheap([1, 2, 3, 4, 5, 6, 7, 8]) + assert heap._size == 8 + + +def test_pop_on_empty_heap_raises_indexerror(ebh): + """Test for index error trying to pop from empty heap.""" + with pytest.raises(IndexError): + ebh.pop() + + +def test_pop_on_heap_with_one_item(ebh): + """Test pop returns item when only one item in heap.""" + ebh.push(1) + assert ebh.pop() == 1 + + +def test_pop_twice_on_heap_with_one_item(ebh): + """Test pop on fully popped heap.""" + ebh.push(1) + ebh.pop() + with pytest.raises(IndexError): + ebh.pop() + + +def test_pop_returns_sorted_values(ebh): + """Test pop entire bin returns sorted values.""" + from binheap import Binheap + import random + rand_nums = list(set([random.randint(0, 1000) + for i in range(20)])) + heap = Binheap(rand_nums) + # import pdb; pdb.set_trace() + all_popped = [heap.pop() for i in range(heap._size)] + assert all_popped == sorted(rand_nums) diff --git a/test_binheap.py b/test_binheap.py deleted file mode 100644 index e69de29..0000000 From 1d1137a85e92ff0b415acedc01b322be45e68b70 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Thu, 2 Nov 2017 14:27:13 -0700 Subject: [PATCH 03/24] completed readme, added setup, added tox, fixed push and pop, finished tests, all passing. --- README.md | 171 +++++++++++++++++++++++++++++++++++++++++++- setup.py | 16 +++++ src/binheap.py | 52 +++++++------- src/test_binheap.py | 15 +++- tox.ini | 9 +++ 5 files changed, 233 insertions(+), 30 deletions(-) create mode 100644 setup.py create mode 100644 tox.ini diff --git a/README.md b/README.md index 87c500a..ab9af5c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,169 @@ -# data-structures -Data-Structures +# Data-Structures + +Where a variety of data-structures found in python are being explored, such as: +* A simple class LinkedList for building linked lists as well as a subclass Stack to implement a stack. + +## Authors + +ChaiChaitanya Narukulla and Mark Reynoso created this code base as a project for Python 401 at Code Fellows Seattle. + +### Requirements for testing and development + +In order to create a development and testing environment, you will need the following: + +``` +python2, python3, ipython, pytest, pytest-watch, pytest-cov, tox +``` + +``` +To install environment: + +pip install -e .[testing][development] +``` + +### Linkded List Class Usage + +``` +To create an instance of a the LinkedList() class contained in package, from python: + +new = LinkedList() *you may choose and optional parameter of a single value or an iterable of one of the following: a tuple, a list, or a string.* + +LinkedList() contains the following methods: +* _push(val) (O1)_ - will insert the value ‘val’ at the head of the list. +* _pop() (O1)_ - will pop the first value off the head of the list and return it. Raises an exception with an appropriate message if there are no values to return. +* _size() (01)_ - will return the length of the list. +* _search(val) (On)_ - will return the node containing ‘val’ in the list, if present, else None. +* _remove(node) (On)_ - will remove the given node from the list, wherever it might be (node must be an item in the list). If the node is not in the list, it will raise an exception with an appropriate message. +* _display() (O1)_ - will return a unicode string representing the list as if it were a Python tuple literal: “(12, ‘sam’, 37, ‘tango’)” + +To access any contained methods: +new.push(val) +new.pop() +new.size() +new.search(val) +new.remove(node) +new.display() + +Additionally, LinkedList() will interact with these built-in Python functions: + +* _len(new)_ - returns the size of the list. +* _print(new)_ - returns what the display() method returns. + +``` + +### Stack Class Usage + +``` +To create an instance of a Stack() class contained in package, from python: + +new = Stack() *you may choose and optional parameter of a single value or an iterable of one of the following: a tuple, a list, or a string.* + +Stack() contains the following methods: +* _push(val) (O1)_ - will insert the value ‘val’ at the head of the stack. +* _pop() (O1)_ - will pop the first value off the head of the stack and return it. Raises an exception with an appropriate message if there are no values to return. + +To access any contained methods: +new.push(val) +new.pop() + +Additionally, Stack() will interact with these built-in Python functions: + +* _len(new)_ - returns the size of the list. + +``` + +### Doubly Linked List Class Usage + +``` +To create an instance of a Dll() class contained in package, from python: + +new = Dll() *you may not call Dll() with any parameters.* + +Dll() contains the following methods: +* _push(val) (O1)_ - will insert the value ‘val’ at the head of the list. +* _append(val) (O1)_ - will insert the value ‘val’ at the tail of the list. +* _pop() (O1)_ - will pop the first value off the head of the stack and return it. Raises an exception with an appropriate message if there are no values to return. +* _shift() (O1)_ - will remove the last value off the tail of the list and return it. Raises an exception with an appropriate message if there are no values to return. +* _remove(node) (On)_ - will remove the given node from the list, wherever it might be (node must be an item in the list). If the node is not in the list, it will raise an exception with an appropriate message. + +To access any contained methods: +new.push(val) +new.append(val) +new.pop() +new.shift() +new.remove() + +Additionally, Dll() will interact with these built-in Python functions: + +* _len(new)_ - returns the size of the list. + +``` + +### Queue Class Usage + +``` +To create an instance of a Queue() class contained in package, from python: + +new = Queue() *you may not call Queue() with any parameters.* + +Queue() contains the following methods: +* _enqueue(val) (O1)_ - will insert the value ‘val’ at the end of the queue. +* _dequeue() (O1)_ - will remove the last value off the front of the queue and return it. Raises an exception with an appropriate message if there are no values to return. +* _peek() (O1)_ - shows the value of the node at the end of the queue. +* _size() (O1)_ - displays the number of node in the queue. + +To access any contained methods: +new.enqueue(val) +new.dequeue() +new.peek() +new.size() + +Additionally, Queue will interact with these built-in Python functions: + +* _len(new)_ - returns the size of the list. + +``` + +### Deque Class Usage + +``` +To create an instance of a Deque() class contained in package, from python: + +new = Deque() *you may not call Deque() with any parameters.* + +Deque() contains the following methods: +* _append(val) (O1)_ - will insert the value ‘val’ at the end of the deque. +* _appendleft() (O1)_ - will insert a value to the front of the deque. +*_pop() (01)_ - removes the first val from the end of the deque. +*_popleft() (01)_ - removes the first val from the front of the deque. +* _peek() (O1)_ - shows the value of the item at the end of the deque. +* _peekleft() (O1)_ - shows the value of the item at the font of the deque. +* _size() (O1)_ - displays the number items in the deque. + +To access any contained methods: +new.append(val) +new.appendleft(val) +new.pop() +new.popleft() +new.peek() +new.peekleft() +new.size() + +``` + +### Binaary Heap (Binheap) Class Usage + +``` +To create an instance of a Binheap() class contained in package, from python: + +new = Binheap() *you may call Binheap() with any uniqe number or iterable of unique numbers.* + +Binheap() contains the following methods: +* _push(val) (Olog(n))_ - will insert the value ‘val’ at the end of the heap. +*_pop() (0log(n))_ - removes the first val from the end of the heap. + +To access any contained methods: +new.push(val) +new.pop() + +``` \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..8011f7b --- /dev/null +++ b/setup.py @@ -0,0 +1,16 @@ +from setuptools import setup + +setup( + name='data-structures', + description=('A package for building and ' + 'running the data-structures module'), + package_dir={'': 'src'}, + author='Mark and chai', + author_email='chaitanyanarukulla@gmail.com mreynoso@spu.edu', + py_modules=['binheap'], + install_requires=[], + extras_require={ + 'testing': ['pytest', 'pytest-cov', 'pytest-watch', 'tox'], + 'development': ['ipython'] + }, +) diff --git a/src/binheap.py b/src/binheap.py index 077dba0..2bc2ae6 100644 --- a/src/binheap.py +++ b/src/binheap.py @@ -1,4 +1,4 @@ -"""Binary heap.""" +"""Binary heap class.""" class Binheap(object): @@ -17,10 +17,10 @@ def push(self, val): if type(val) == int or type(val) == float: self.heaplist.append(val) self._size += 1 - if self._size > 1: - sort_up = True - idx = self._size - 1 - while sort_up: + sort_up = True + idx = self._size - 1 + while sort_up: + if idx > 0: if idx % 2 == 0: parent = (idx - 2) // 2 else: @@ -31,6 +31,8 @@ def push(self, val): idx = parent else: sort_up = False + else: + sort_up = False else: raise ValueError('You must input numbers only.') @@ -50,33 +52,29 @@ def pop(self): while sort_down: l_child = idx * 2 + 1 r_child = idx * 2 + 2 - if self.heaplist[l_child] and self.heaplist[l_child]: + if r_child <= self._size - 1 and l_child <= self._size - 1: if self.heaplist[l_child] > self.heaplist[r_child]: if self.heaplist[r_child] < self.heaplist[idx]: - temp_val = self.heaplist[idx] - self.heaplist[idx] = self.heaplist[r_child] - self.heaplist[r_child] = temp_val + self.heaplist[r_child], self.heaplist[idx] =\ + self.heaplist[idx], self.heaplist[r_child] idx = r_child else: sort_down = False else: if self.heaplist[l_child] < self.heaplist[r_child]: - temp_val = self.heaplist[idx] - self.heaplist[idx] = self.heaplist[l_child] - self.heaplist[l_child] = temp_val - idx = l_child - else: - sort_down = False - elif self.heaplist[l_child] and not self.heaplist[r_child]: - if self.heaplist[l_child] < self.heaplist[r_child]: - temp_val = self.heaplist[idx] - self.heaplist[idx] = self.heaplist[l_child] - self.heaplist[l_child] = temp_val - sort_down = False - elif self.heaplist[r_child] and not self.heaplist[l_child]: - if self.heaplist[r_child] < self.heaplist[idx]: - temp_val = self.heaplist[idx] - self.heaplist[idx] = self.heaplist[r_child] - self.heaplist[r_child] = temp_val - sort_down = False + if self.heaplist[l_child] < self.heaplist[idx]: + self.heaplist[idx], self.heaplist[l_child] =\ + self.heaplist[l_child], self.heaplist[idx] + idx = l_child + else: + sort_down = False + elif l_child == self._size - 1: + if self.heaplist[l_child] < self.heaplist[idx]: + self.heaplist[idx], self.heaplist[l_child] =\ + self.heaplist[l_child], self.heaplist[idx] + sort_down = False + else: + sort_down = False + else: + sort_down = False return pop_val diff --git a/src/test_binheap.py b/src/test_binheap.py index 864db40..7b4ac0f 100644 --- a/src/test_binheap.py +++ b/src/test_binheap.py @@ -53,7 +53,7 @@ def test_pop_twice_on_heap_with_one_item(ebh): ebh.pop() -def test_pop_returns_sorted_values(ebh): +def test_pop_returns_sorted_values(): """Test pop entire bin returns sorted values.""" from binheap import Binheap import random @@ -63,3 +63,16 @@ def test_pop_returns_sorted_values(ebh): # import pdb; pdb.set_trace() all_popped = [heap.pop() for i in range(heap._size)] assert all_popped == sorted(rand_nums) + + +def test_pop_returns_sorted_values_limited(ebh): + """Test pop in controlled environment.""" + ebh.push(23), + ebh.push(2), + ebh.push(30), + ebh.push(50), + ebh.push(6), + ebh.push(17), + ebh.push(29) + all_popped = [ebh.pop() for i in range(7)] + assert all_popped == [2, 6, 17, 23, 29, 30, 50] diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..06c5283 --- /dev/null +++ b/tox.ini @@ -0,0 +1,9 @@ +[tox] + +envlist = py27, py36 + +[testenv] +commands = py.test src --cov=src --cov-report term-missing +deps = + pytest + pytest-cov \ No newline at end of file From d36c2458d6cf54a5823a3fd2c76192e6721766d0 Mon Sep 17 00:00:00 2001 From: "Chaitanya.Narukulla" Date: Fri, 3 Nov 2017 14:12:12 -0700 Subject: [PATCH 04/24] initial setup --- src/priorityq.py | 0 src/test_priorityq.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/priorityq.py create mode 100644 src/test_priorityq.py diff --git a/src/priorityq.py b/src/priorityq.py new file mode 100644 index 0000000..e69de29 diff --git a/src/test_priorityq.py b/src/test_priorityq.py new file mode 100644 index 0000000..e69de29 From 9380413915ae8fd0991e008b37c88a7e5160d1a5 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Fri, 3 Nov 2017 15:03:23 -0700 Subject: [PATCH 05/24] untested priority heap written. works great in theory. --- src/priorityq.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/priorityq.py b/src/priorityq.py index e69de29..386b105 100644 --- a/src/priorityq.py +++ b/src/priorityq.py @@ -0,0 +1,34 @@ +"""Create a priority queue instance.""" + + +class Priorityq(object): + """Create a que with priority attributes for each value.""" + + def __init__(self): + """Initialize a priority queue instance.""" + self._que = {} + self._highest = 0 + + def insert(self, val, priority): + """Insert a new value into the queue.""" + if priority: + if priority < self._highest: + self._highest = priority + if self._que[priority]: + self._que[priority].append(val) + else: + self._que[priority] = [val] + else: + lowest = 0 + for low_priority in self._que: + if low_priority > self._highest: + lowest = low_priority + self._que[lowest].append(val) + + def pop(self): + """Remove the highest prioty value from queue.""" + return self._que[self._highest].pop(0) + + def peek(self): + """View the hightest priority item.""" + return self._que[self._highest][0] From 0881f6c022ed1ac04d71ccddc4c759e0b88de109 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Fri, 3 Nov 2017 16:52:36 -0700 Subject: [PATCH 06/24] fixed insert and pop methods and wrote a few tests, all passing --- src/conftest.py | 7 +++++++ src/priorityq.py | 41 +++++++++++++++++++++++++++-------------- src/test_priorityq.py | 24 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 870d234..5f89810 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -7,3 +7,10 @@ def ebh(): """Initialize an empty binary heap.""" from binheap import Binheap return Binheap() + + +@pytest.fixture +def pq(): + """Initialize a empty pq.""" + from priorityq import Priorityq + return Priorityq() diff --git a/src/priorityq.py b/src/priorityq.py index 386b105..476cb9d 100644 --- a/src/priorityq.py +++ b/src/priorityq.py @@ -8,26 +8,39 @@ def __init__(self): """Initialize a priority queue instance.""" self._que = {} self._highest = 0 + self._lowest = 0 - def insert(self, val, priority): + def insert(self, val, priority=0): """Insert a new value into the queue.""" - if priority: - if priority < self._highest: - self._highest = priority - if self._que[priority]: - self._que[priority].append(val) - else: - self._que[priority] = [val] + if priority <= self._highest: + self._highest = priority + if priority >= self._lowest: + self._lowest = priority + if priority in self._que: + self._que[priority].append(val) else: - lowest = 0 - for low_priority in self._que: - if low_priority > self._highest: - lowest = low_priority - self._que[lowest].append(val) + self._que[priority] = [val] def pop(self): """Remove the highest prioty value from queue.""" - return self._que[self._highest].pop(0) + if len(self._que) == 0: + raise IndexError('There are no value to pop.') + else: + self._que[self._highest].pop(0) + if self._que[self._highest] == []: + self._que.pop(self._highest) + if len(self._que) == 0: + self._highest = 0 + self._lowest = 0 + high = max(self._highest) + self._highest = high + if self._que[self._lowest] == []: + self._que.pop(self._lowest) + if len(self._que) == 0: + self._lowest = 0 + self._lowest = 0 + high = max(self._lowest) + self._highest = high def peek(self): """View the hightest priority item.""" diff --git a/src/test_priorityq.py b/src/test_priorityq.py index e69de29..4f31593 100644 --- a/src/test_priorityq.py +++ b/src/test_priorityq.py @@ -0,0 +1,24 @@ +"""Test priorityq.py.""" +import pytest + + +def test_priorityq_iinitialize_empty_que(pq): + """Test initialize an empty pq.""" + assert pq + + +def test_priorityq_insert_one_val_w_priority(pq): + """Test insert one value to pq with priority.""" + pq.insert('hi', 0) + assert pq._highest == 0 + + +def test_insert_n_values_returns_n_length(pq): + """Test that the number of values inserted is the que's length.""" + for i in range(20): + pq.insert(i) + total_length = 0 + for key in pq._que: + total_length += len(pq._que[key]) + assert total_length == 20 + From f09487392a7dc676578dbf57f746f62c97fbfd0f Mon Sep 17 00:00:00 2001 From: "Chaitanya.Narukulla" Date: Sun, 5 Nov 2017 12:09:42 -0800 Subject: [PATCH 07/24] Test written and passing all test --- README.md | 19 +++++++++ src/priorityq.py | 15 +++---- src/test_priorityq.py | 98 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ab9af5c..45cc5eb 100644 --- a/README.md +++ b/README.md @@ -166,4 +166,23 @@ To access any contained methods: new.push(val) new.pop() +``` + +### Priorityq (Priorityq) Class Usage + +``` +To create an instance of a Priorityq() class contained in package, from python: + +new = Priorityq() *you may call Priorityq() with any uniqe number or iterable of unique numbers.* + +Priorityq() contains the following methods: +*_insert(val,priority) (O1)_ - will insert the value ‘val’ at the end of the heap. +*_pop() (01)_ - removes the higest priority and returns the higest priority. +*_peek() (01)_ - returns the higest priority. + +To access any contained methods: +new.insert(val) +new.pop() +new.peek() + ``` \ No newline at end of file diff --git a/src/priorityq.py b/src/priorityq.py index 476cb9d..04fa8a3 100644 --- a/src/priorityq.py +++ b/src/priorityq.py @@ -26,21 +26,16 @@ def pop(self): if len(self._que) == 0: raise IndexError('There are no value to pop.') else: - self._que[self._highest].pop(0) + val = self._que[self._highest].pop(0) if self._que[self._highest] == []: self._que.pop(self._highest) if len(self._que) == 0: self._highest = 0 self._lowest = 0 - high = max(self._highest) - self._highest = high - if self._que[self._lowest] == []: - self._que.pop(self._lowest) - if len(self._que) == 0: - self._lowest = 0 - self._lowest = 0 - high = max(self._lowest) - self._highest = high + else: + high = min(self._que) + self._highest = high + return val def peek(self): """View the hightest priority item.""" diff --git a/src/test_priorityq.py b/src/test_priorityq.py index 4f31593..8ff64fb 100644 --- a/src/test_priorityq.py +++ b/src/test_priorityq.py @@ -22,3 +22,101 @@ def test_insert_n_values_returns_n_length(pq): total_length += len(pq._que[key]) assert total_length == 20 + +def test_priorityq_insert_with_many_priority_returns_higest(pq): + """Test ipriorityq insert with many priority returns higest pirority.""" + pq.insert('hi', -10) + pq.insert('one', 1) + pq.insert('four', 4) + pq.insert('eight', 8) + assert pq._highest == -10 + + +def test_priorityq_insert_with_many_priority_returns_lowest(pq): + """Test ipriorityq insert with many priority returns lowest pirority.""" + pq.insert('hi', -10) + pq.insert('one', 1) + pq.insert('four', 4) + pq.insert('eight', 8) + assert pq._lowest == 8 + + +def test_priorityq_insert_with_same_priority_returns_list_of_val(pq): + """Test if inserted with same priority returns list of val in pirority.""" + pq.insert('hi', -10) + pq.insert('one', -10) + pq.insert('four', 4) + pq.insert('eight', 4) + assert pq._que[-10] == ['hi', 'one'] + + +def test_if_pop_raises_indexerror_on_empty_prorityq(pq): + """Test if pop reises indexerroe.""" + with pytest.raises(IndexError): + pq.pop() + + +def test_priorityq_pop_function_pops(pq): + """Test ipriorityq if it pops a val.""" + pq.insert('hi', -10) + pq.insert('one', 1) + pq.pop() + pq.pop() + assert pq._que == {} + + +def test_priorityq_pop_function_returns_val(pq): + """Test ipriorityq if it pops and returns the right val.""" + pq.insert('hi', -10) + pq.insert('one', 1) + assert pq.pop() == 'hi' + + +def test_pop_if_more_poped_then_inserted_raises_error(pq): + """Test if inserted with same priority returns list of val in pirority.""" + pq.insert('hi', -10) + pq.insert('one', -10) + pq.insert('four', 4) + pq.pop() + pq.pop() + pq.pop() + with pytest.raises(IndexError): + pq.pop() + + +def test_pop_if_returns_in_prioety_order(pq): + """Test if inserted with priority returns in priority order.""" + pq.insert('hi', -10) + pq.insert('one', -1) + pq.insert('four', 4) + pop1 = pq.pop() + pop2 = pq.pop() + pop3 = pq.pop() + assert pop1 == 'hi' + assert pop2 == 'one' + assert pop3 == 'four' + + +def test_pop_if_returns_in_prioety_order_pop_in_and_out(pq): + """Test if inserted val with priority returns in priority order.""" + pq.insert('hi', -10) + pq.insert('four', 4) + pq.insert('one', -1) + pop1 = pq.pop() + pop2 = pq.pop() + pq.insert('first', -20) + pop3 = pq.pop() + pq.insert('last', 20) + pop4 = pq.pop() + assert pop1 == 'hi' + assert pop2 == 'one' + assert pop3 == 'first' + assert pop4 == 'four' + + +def test_peek_returns_the_higest_priority_val(pq): + """Test if peek returns the val in line to be poped.""" + pq.insert('hi', -10) + pq.insert('one', 1) + pq.insert('four', 4) + assert pq.peek() == 'hi' From ac42137bf264f9c69894b85df59c321a73cf435e Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Sun, 5 Nov 2017 13:51:57 -0800 Subject: [PATCH 08/24] created graph --- src/graph.py | 86 +++++++++++++++++++++++++++++++++++++++++++++++ src/test_graph.py | 0 2 files changed, 86 insertions(+) create mode 100644 src/graph.py create mode 100644 src/test_graph.py diff --git a/src/graph.py b/src/graph.py new file mode 100644 index 0000000..3407833 --- /dev/null +++ b/src/graph.py @@ -0,0 +1,86 @@ +"""Implement a class for a graph data structure.""" + + +class Graph(object): + """Graph class.""" + + def __init__(self): + """Initialize a graph.""" + self._graph = {} + + def nodes(self): + """Return a list of all nodes in graph.""" + return self._graph.keys() + + def edges(self): + """Return a list of edges in graph.""" + edges = [] + for key in self._graph: + for i in key: + edges.append((key, i)) + return edges + + def add_node(self, val): + """Add a node with value of val to graph.""" + self._graph[val] + + def add_edge(self, val1, val2): + """Add a new edge to graph between val1 & val2 as well as add vals.""" + if val1 in self._graph and val2 in self._graph: + if val2 not in self._graph[val1]: + self._graph[val1].append(val2) + if val1 not in self._graph[val2]: + self._graph[val2].append(val1) + if val1 in self._graph and val2 not in self._graph: + self._graph[val1].append(val2) + self._graph[val2] = [val1] + if val2 in self._graph and val1 not in self._graph: + self._graph[val2].append(val1) + self._graph[val1] = [val2] + else: + self._graph[val1] = [val2] + self._graph[val2] = [val1] + + def del_node(self, val): + """Delete node w/val from graph, raises exception if not exist.""" + if self._graph[val]: + self._graph.remove(val) + for key in self._graph: + for i in self._graph: + if i == val: + i.remove(val) + raise ValueError('There is no node of that value in the graph.') + + def del_edge(self, val1, val2): + """Delete edge between val1 & val2 from graph.""" + if self._graph[val1] and self._graph[val2]: + for edge in self._graph[val1]: + if edge == val2: + edge.remove(val2) + for edge in self._graph[val2]: + if edge == val1: + edge.remove(val1) + raise ValueError('These edges do not exist.') + + def has_node(self, val): + """Return true or false if node has value.""" + if self._graph[val]: + return True + else: + return False + + def neighbors(self, val): + """Return list of nodes connected node(val).""" + return self._graph[val] + + def adjacent(self, val1, val2): + """Return true if edge between vals, else false, & error if no val.""" + if self._graph[val1] and self._graph[val2]: + for edge in self._graph[val1]: + if edge == val2: + return True + for edge in self._graph[val2]: + if edge == val1: + return True + return False + raise ValueError('These edges do not exist.') diff --git a/src/test_graph.py b/src/test_graph.py new file mode 100644 index 0000000..e69de29 From 03b0444fb9b1f1eca60c5ea287fafffd3913b4a2 Mon Sep 17 00:00:00 2001 From: "Chaitanya.Narukulla" Date: Sun, 5 Nov 2017 16:21:48 -0800 Subject: [PATCH 09/24] Writing test --- src/conftest.py | 7 +++++ src/graph.py | 19 +++++++------- src/test_graph.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 5f89810..81d0af3 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -14,3 +14,10 @@ def pq(): """Initialize a empty pq.""" from priorityq import Priorityq return Priorityq() + + +@pytest.fixture +def g(): + """Initialize a empty pq.""" + from graph import Graph + return Graph() diff --git a/src/graph.py b/src/graph.py index 3407833..d993b2a 100644 --- a/src/graph.py +++ b/src/graph.py @@ -10,36 +10,37 @@ def __init__(self): def nodes(self): """Return a list of all nodes in graph.""" - return self._graph.keys() + return list(self._graph.keys()) def edges(self): """Return a list of edges in graph.""" edges = [] for key in self._graph: - for i in key: + print(edges) + for i in self._graph[key]: edges.append((key, i)) return edges def add_node(self, val): """Add a node with value of val to graph.""" - self._graph[val] + self._graph[val] = [] def add_edge(self, val1, val2): """Add a new edge to graph between val1 & val2 as well as add vals.""" if val1 in self._graph and val2 in self._graph: if val2 not in self._graph[val1]: self._graph[val1].append(val2) - if val1 not in self._graph[val2]: - self._graph[val2].append(val1) + print(self._graph[val1]) if val1 in self._graph and val2 not in self._graph: + self._graph[val2] = [] self._graph[val1].append(val2) - self._graph[val2] = [val1] if val2 in self._graph and val1 not in self._graph: - self._graph[val2].append(val1) - self._graph[val1] = [val2] + self._graph[val1] = [] + self._graph[val1].append(val2) else: self._graph[val1] = [val2] - self._graph[val2] = [val1] + self._graph[val2] = [] + def del_node(self, val): """Delete node w/val from graph, raises exception if not exist.""" diff --git a/src/test_graph.py b/src/test_graph.py index e69de29..61c6b66 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -0,0 +1,66 @@ +"""Testing graph.py.""" +# import pytest + + +def test_graph_iinitialize_empty_dict(g): + """Test initialize an empty pq.""" + assert g._graph == {} + + +def test_add_node(g): + """Test it adds note.""" + g.add_node(5) + assert g._graph[5] == [] + + +def test_node_returns_list_of_all_nodes(g): + """Test if nodes return list of all nodes.""" + g.add_node(5) + g.add_node(7) + g.add_node(8) + assert g.nodes() == [5, 7, 8] + + +def test_edges_return_list_of_all_nodes_edges(g): + """Test it adds note.""" + g.add_node(5) + g.add_node(7) + g.edges() + assert g.edges() == [] + +def test_add_nodel_adds_node(g): + """Test if add_node add nodes.""" + g.add_node(5) + g.add_node(7) + g.add_node(8) + assert g.nodes() == [5, 7, 8] + + + +def test_add_edges_adds_edges(g): + """Test if edges are added if 2 val are given.""" + g.add_node(5) + g.add_node(7) + g.add_edge(5, 7) + assert g.edges() == [(5, 7)] + + +def test_add_edges_with_e(g): + """Test if edges are added if 2 val are given.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.add_edge(5, 7) + g.add_edge(7, 9) + assert g.edges() == [(5, 7), (7, 9)] + +def test_add_edges_will_add_mutipul_edges_to_a_node_(g): + """Test if edges are added if 2 val are given.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.add_node(10) + g.add_edge(5, 7) + g.add_edge(5, 9) + g.add_edge(5, 10) + assert g.edges() == [(5, 7), (5, 9), (5, 10)] From 2c6e481e7833f7b27b7ffdb13cc733edc945972c Mon Sep 17 00:00:00 2001 From: "Chaitanya.Narukulla" Date: Sun, 5 Nov 2017 18:17:39 -0800 Subject: [PATCH 10/24] test are passing --- src/graph.py | 65 +++++++++++++++------------ src/test_graph.py | 112 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 31 deletions(-) diff --git a/src/graph.py b/src/graph.py index d993b2a..fcb81d0 100644 --- a/src/graph.py +++ b/src/graph.py @@ -16,7 +16,6 @@ def edges(self): """Return a list of edges in graph.""" edges = [] for key in self._graph: - print(edges) for i in self._graph[key]: edges.append((key, i)) return edges @@ -30,58 +29,66 @@ def add_edge(self, val1, val2): if val1 in self._graph and val2 in self._graph: if val2 not in self._graph[val1]: self._graph[val1].append(val2) - print(self._graph[val1]) - if val1 in self._graph and val2 not in self._graph: + elif val1 in self._graph and val2 not in self._graph: self._graph[val2] = [] self._graph[val1].append(val2) - if val2 in self._graph and val1 not in self._graph: + elif val2 in self._graph and val1 not in self._graph: self._graph[val1] = [] self._graph[val1].append(val2) else: self._graph[val1] = [val2] self._graph[val2] = [] - def del_node(self, val): """Delete node w/val from graph, raises exception if not exist.""" - if self._graph[val]: - self._graph.remove(val) + if val in self._graph: + del self._graph[val] for key in self._graph: - for i in self._graph: + for i in self._graph[key]: if i == val: i.remove(val) - raise ValueError('There is no node of that value in the graph.') + else: + raise ValueError('There is no node of that value in the graph.') def del_edge(self, val1, val2): """Delete edge between val1 & val2 from graph.""" - if self._graph[val1] and self._graph[val2]: - for edge in self._graph[val1]: - if edge == val2: - edge.remove(val2) - for edge in self._graph[val2]: - if edge == val1: - edge.remove(val1) - raise ValueError('These edges do not exist.') + try: + if val2 in self._graph[val1]: + self._graph[val1].remove(val2) + else: + raise ValueError('These edges do not exist.') + except KeyError: + raise ValueError('These edges do not exist.') def has_node(self, val): """Return true or false if node has value.""" - if self._graph[val]: - return True - else: + try: + if val in self._graph: + return True + else: + return False + except KeyError: return False def neighbors(self, val): """Return list of nodes connected node(val).""" - return self._graph[val] + try: + neighbors = self._graph[val] + for key in self._graph: + if val in self._graph[key]: + if self._graph[key] not in neighbors: + neighbors.append(self._graph[key]) + return neighbors + except KeyError: + raise ValueError('This node dosent exit') def adjacent(self, val1, val2): """Return true if edge between vals, else false, & error if no val.""" - if self._graph[val1] and self._graph[val2]: - for edge in self._graph[val1]: - if edge == val2: - return True - for edge in self._graph[val2]: - if edge == val1: - return True + if val1 in self._graph or val2 in self._graph: + if val1 in self._graph[val2]: + return True + if val2 in self._graph[val1]: + return True return False - raise ValueError('These edges do not exist.') + else: + raise ValueError('These edges do not exist.') diff --git a/src/test_graph.py b/src/test_graph.py index 61c6b66..05dec78 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -1,5 +1,5 @@ """Testing graph.py.""" -# import pytest +import pytest def test_graph_iinitialize_empty_dict(g): @@ -28,6 +28,7 @@ def test_edges_return_list_of_all_nodes_edges(g): g.edges() assert g.edges() == [] + def test_add_nodel_adds_node(g): """Test if add_node add nodes.""" g.add_node(5) @@ -36,7 +37,6 @@ def test_add_nodel_adds_node(g): assert g.nodes() == [5, 7, 8] - def test_add_edges_adds_edges(g): """Test if edges are added if 2 val are given.""" g.add_node(5) @@ -54,6 +54,7 @@ def test_add_edges_with_e(g): g.add_edge(7, 9) assert g.edges() == [(5, 7), (7, 9)] + def test_add_edges_will_add_mutipul_edges_to_a_node_(g): """Test if edges are added if 2 val are given.""" g.add_node(5) @@ -64,3 +65,110 @@ def test_add_edges_will_add_mutipul_edges_to_a_node_(g): g.add_edge(5, 9) g.add_edge(5, 10) assert g.edges() == [(5, 7), (5, 9), (5, 10)] + + +def test_del_node_delets_the_node(g): + """Test del node delets the node.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.del_node(7) + assert g.nodes() == [5, 9] + + +def test_del_node_raises_valueerroe(g): + """Test del node delets the node.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.del_node(5) + g.del_node(7) + g.del_node(9) + with pytest.raises(ValueError): + g.del_node(5) + + +def test_del_edge_delets_edges(g): + """Test if del_edges delets edges.""" + g.add_node(5) + g.add_node(7) + g.add_edge(5, 7) + g.del_edge(5, 7) + assert g.edges() == [] + + +def test_del_edge_raises_value_error(g): + """Test if del_edges raises value error.""" + g.add_node(5) + g.add_node(7) + g.add_edge(5, 7) + g.del_edge(5, 7) + with pytest.raises(ValueError): + g.del_edge(5, 7) + + +def test_del_edge_raises_key_error(g): + """Test if del_edges raises value error.""" + g.add_node(5) + g.add_node(7) + g.add_edge(5, 7) + g.del_edge(5, 7) + with pytest.raises(ValueError): + g.del_edge(9, 0) + + +def test_has_node_has_no_node(g): + """Test false if there is no node.""" + assert g.has_node(2) is False + + +def test_has_node_has_node(g): + """Test false if there is no node.""" + g.add_node(5) + assert g.has_node(5) is True + +def test_neighbors_return_list_of_nodes_connected(g): + """Test neighbor retutns the list of nodes connected.""" + g.add_node(5) + g.add_node(7) + g.add_edge(5, 7) + g.add_node(8) + g.add_node(9) + g.add_edge(5, 9) + g.add_edge(5, 8) + assert g.neighbors(5) == [7, 9, 8] + + +def test_neighbors_raises_valueerror(g): + """Test neighbor retutns the list of nodes connected.""" + g.add_node(5) + g.add_node(7) + g.add_edge(5, 7) + g.add_node(8) + g.add_node(9) + g.add_edge(5, 9) + with pytest.raises(ValueError): + g.neighbors(20) + + +def test_adjacent_has_any_edges(g): + """Test adjacent has edges returns true.""" + g.add_node(5) + g.add_node(7) + g.add_edge(5, 7) + assert g.adjacent(5, 7) is True + + +def test_adjacent_has_no_edges(g): + """Test adjacent has no edges returns false.""" + g.add_node(5) + g.add_node(7) + assert g.adjacent(5, 7) is False + + +def test_adjacent_raises_valueerror(g): + """Test adjacent has no edges returns false.""" + g.add_node(5) + g.add_node(7) + with pytest.raises(ValueError): + g.adjacent(9, 0) From bc22f461ac746b9791b5f50569a097defd7ed2ee Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Sun, 5 Nov 2017 19:04:37 -0800 Subject: [PATCH 11/24] tests passing and cleaned up code --- README.md | 31 +++++++++++++ src/graph.py | 11 ++--- src/test_graph.py | 113 +++++++++++++++++++++++++++++++--------------- 3 files changed, 111 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 45cc5eb..1e3fa74 100644 --- a/README.md +++ b/README.md @@ -185,4 +185,35 @@ new.insert(val) new.pop() new.peek() +``` + +### Graph Class Usage + +``` +To create an instance of a Graph() class contained in package, from python: + +new = Graph() *you may not call Graph() with any parameters.* + +Graph() contains the following methods: +* _nodes() (O1)_ - will return a list of all nodes in the graph. +* _edges() (On2)_ - will return a list of edges in graph. +*_add_node(val) (01)_ - adds a new node to the graph. +*_add_edge(val1, val2) (0n)_ - add a new edge to nodes in the graph. +* _del_node(val) (On2)_ - delete node from graph. +* _del_edge(val1, val2) (On)_ - delete edge between two nodes. +* _has_node(val) (O1)_ - returns true if node exists and false if not. +* _neighbors(val) (On2)_ - return a list of nodes with edges to input node. +* _adjacent(val1, val2) (On)_ - return true if edge exists between two inputs, otherwise false. + +To access any contained methods: +new.nodes() +new.edges() +new.add_node(val) +new.add_edge(val1, val2) +new.del_node(val) +new.del_edge(val1, val2) +new.has_node(val) +new.neighbors(val) +new.adjacent(val1, val2) + ``` \ No newline at end of file diff --git a/src/graph.py b/src/graph.py index fcb81d0..2608f18 100644 --- a/src/graph.py +++ b/src/graph.py @@ -46,7 +46,7 @@ def del_node(self, val): for key in self._graph: for i in self._graph[key]: if i == val: - i.remove(val) + self._graph[key].remove(val) else: raise ValueError('There is no node of that value in the graph.') @@ -62,12 +62,9 @@ def del_edge(self, val1, val2): def has_node(self, val): """Return true or false if node has value.""" - try: - if val in self._graph: - return True - else: - return False - except KeyError: + if val in self._graph: + return True + else: return False def neighbors(self, val): diff --git a/src/test_graph.py b/src/test_graph.py index 05dec78..6f09fd9 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -13,31 +13,27 @@ def test_add_node(g): assert g._graph[5] == [] -def test_node_returns_list_of_all_nodes(g): - """Test if nodes return list of all nodes.""" +def test_add_node_returns_list_of_all_nodes(g): + """Test if add node return list of all nodes.""" g.add_node(5) g.add_node(7) g.add_node(8) - assert g.nodes() == [5, 7, 8] + assert 5 in g.nodes() + assert 7 in g.nodes() + assert 8 in g.nodes() + assert type(g.nodes()) == list -def test_edges_return_list_of_all_nodes_edges(g): - """Test it adds note.""" + +def test_edges_return_list_of_tuples_of_all_edges(g): + """Test if edges returns list of tuples with pairings of each edge.""" g.add_node(5) g.add_node(7) g.edges() assert g.edges() == [] -def test_add_nodel_adds_node(g): - """Test if add_node add nodes.""" - g.add_node(5) - g.add_node(7) - g.add_node(8) - assert g.nodes() == [5, 7, 8] - - -def test_add_edges_adds_edges(g): +def test_add_edges_returns_list_of_edges(g): """Test if edges are added if 2 val are given.""" g.add_node(5) g.add_node(7) @@ -45,7 +41,7 @@ def test_add_edges_adds_edges(g): assert g.edges() == [(5, 7)] -def test_add_edges_with_e(g): +def test_add_two_edges(g): """Test if edges are added if 2 val are given.""" g.add_node(5) g.add_node(7) @@ -55,7 +51,7 @@ def test_add_edges_with_e(g): assert g.edges() == [(5, 7), (7, 9)] -def test_add_edges_will_add_mutipul_edges_to_a_node_(g): +def test_add_edges_will_add_mutipule_edges_to_a_node(g): """Test if edges are added if 2 val are given.""" g.add_node(5) g.add_node(7) @@ -67,19 +63,61 @@ def test_add_edges_will_add_mutipul_edges_to_a_node_(g): assert g.edges() == [(5, 7), (5, 9), (5, 10)] -def test_del_node_delets_the_node(g): +def test_add_edges_with_one_val_in_graph_add_second_val(g): + """Test if edges are added if 2 val are given.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.add_node(10) + g.add_edge(5, 7) + g.add_edge(5, 9) + g.add_edge(5, 15) + assert g.edges() == [(5, 7), (5, 9), (5, 15)] + + +def test_add_edges_with_first_val_new_and_second_val_in_graph(g): + """Test if edges are added if 2 val are given.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.add_node(10) + g.add_edge(5, 7) + g.add_edge(5, 9) + g.add_edge(15, 5) + assert (15, 5) in g.edges() + assert (5, 7) in g.edges() + assert (5, 9) in g.edges() + + +def test_add_edges_with_two_new_nodes(g): + """Test add edges when both nodes are new to graph.""" + g.add_edge(19, 100) + assert g.edges() == [(19, 100)] + + +def test_del_node_deletes_the_node_given(g): """Test del node delets the node.""" g.add_node(5) g.add_node(7) g.add_node(9) g.del_node(7) - assert g.nodes() == [5, 9] + assert 7 not in g.nodes() -def test_del_node_raises_valueerroe(g): +def test_del_node_deletes_the_node_given(g): """Test del node delets the node.""" g.add_node(5) g.add_node(7) + g.add_edge(5, 7) + g.add_node(9) + g.del_node(7) + assert 7 not in g.nodes() + + +def test_del_node_raises_value_error(g): + """Test del node raises value error if node has been deleted.""" + g.add_node(5) + g.add_node(7) g.add_node(9) g.del_node(5) g.del_node(7) @@ -88,8 +126,8 @@ def test_del_node_raises_valueerroe(g): g.del_node(5) -def test_del_edge_delets_edges(g): - """Test if del_edges delets edges.""" +def test_del_edge_deletes_all_edges(g): + """Test if del_edges deletes all edges.""" g.add_node(5) g.add_node(7) g.add_edge(5, 7) @@ -97,7 +135,7 @@ def test_del_edge_delets_edges(g): assert g.edges() == [] -def test_del_edge_raises_value_error(g): +def test_del_edge_raises_value_error_if_too_many_edges_deleted(g): """Test if del_edges raises value error.""" g.add_node(5) g.add_node(7) @@ -107,8 +145,8 @@ def test_del_edge_raises_value_error(g): g.del_edge(5, 7) -def test_del_edge_raises_key_error(g): - """Test if del_edges raises value error.""" +def test_del_edge_raises_value_error_if_values_not_in_dict(g): + """Test if del_edges raises value error for values not in dict.""" g.add_node(5) g.add_node(7) g.add_edge(5, 7) @@ -117,18 +155,19 @@ def test_del_edge_raises_key_error(g): g.del_edge(9, 0) -def test_has_node_has_no_node(g): - """Test false if there is no node.""" +def test_has_node_is_false_on_empty_graph(g): + """Test false if there are no nodes in graph.""" assert g.has_node(2) is False -def test_has_node_has_node(g): - """Test false if there is no node.""" +def test_has_node_if_value_in_graph(g): + """Test false if node does not exist.""" g.add_node(5) assert g.has_node(5) is True -def test_neighbors_return_list_of_nodes_connected(g): - """Test neighbor retutns the list of nodes connected.""" + +def test_neighbors_return_list_of_nodes_connected_to_input(g): + """Test neighbor retutns the list of nodes connected input value.""" g.add_node(5) g.add_node(7) g.add_edge(5, 7) @@ -139,8 +178,8 @@ def test_neighbors_return_list_of_nodes_connected(g): assert g.neighbors(5) == [7, 9, 8] -def test_neighbors_raises_valueerror(g): - """Test neighbor retutns the list of nodes connected.""" +def test_neighbors_raises_valueerror_if_node_not_exist(g): + """Test neighbor raises value error if input not a node.""" g.add_node(5) g.add_node(7) g.add_edge(5, 7) @@ -151,23 +190,23 @@ def test_neighbors_raises_valueerror(g): g.neighbors(20) -def test_adjacent_has_any_edges(g): - """Test adjacent has edges returns true.""" +def test_adjacent_if_two_nodes_have_edge_is_true(g): + """Test adjacent has edges returns true when edge exists.""" g.add_node(5) g.add_node(7) g.add_edge(5, 7) assert g.adjacent(5, 7) is True -def test_adjacent_has_no_edges(g): +def test_adjacent_when_edge_when_no_edges(g): """Test adjacent has no edges returns false.""" g.add_node(5) g.add_node(7) assert g.adjacent(5, 7) is False -def test_adjacent_raises_valueerror(g): - """Test adjacent has no edges returns false.""" +def test_adjacent_raises_valueerror_if_input_not_node(g): + """Test adjacent raises value error if node does not exist.""" g.add_node(5) g.add_node(7) with pytest.raises(ValueError): From b244da29bfc3dcc4f60e993ef32b2fcc51ea7913 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Tue, 7 Nov 2017 14:36:18 -0800 Subject: [PATCH 12/24] depth first tested and finished --- src/graph.py | 18 +++++++++++++ src/test_graph.py | 64 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/graph.py b/src/graph.py index 2608f18..db62fe0 100644 --- a/src/graph.py +++ b/src/graph.py @@ -89,3 +89,21 @@ def adjacent(self, val1, val2): return False else: raise ValueError('These edges do not exist.') + + def depth_first_traversal(self, start_val): + """Traverse the graph from first edge of each node until ultimate.""" + if start_val in self._graph: + depth_traversal = [] + path = [start_val] + while path: + val = path.pop() + if val not in depth_traversal: + depth_traversal.append(val) + path = path + self._graph[val] + return depth_traversal + else: + raise ValueError('Value is not in graph.') + + def breadth_first_traversal(self, start_val): + """Traverse the graph by node's edges before moving to next node.""" + pass diff --git a/src/test_graph.py b/src/test_graph.py index 6f09fd9..e2d0d95 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -211,3 +211,67 @@ def test_adjacent_raises_valueerror_if_input_not_node(g): g.add_node(7) with pytest.raises(ValueError): g.adjacent(9, 0) + + +def test_depth_first_one_node_many_edges(g): + """Test depth first traversal returns all paths from one node.""" + g.add_node(5) + g.add_node(6) + g.add_node(7) + g.add_node(8) + g.add_node(9) + g.add_edge(5, 6) + g.add_edge(5, 7) + g.add_edge(5, 8) + g.add_edge(5, 9) + assert g.depth_first_traversal(5) == [5, 9, 8, 7, 6] + + +def test_depth_first_node_no_edge_return_only_self(g): + """Test depth first traversal returns start val if no edges.""" + g.add_node(8) + g.add_node(9) + g.add_node(10) + g.add_edge(10, 8) + assert g.depth_first_traversal(8) == [8] + + +def test_depth_first_one_edge_per_node(g): + """Test depth first traversal returns all paths from one node.""" + g.add_node(5) + g.add_node(6) + g.add_node(7) + g.add_node(8) + g.add_node(9) + g.add_edge(5, 6) + g.add_edge(6, 7) + g.add_edge(7, 8) + g.add_edge(8, 9) + assert g.depth_first_traversal(5) == [5, 6, 7, 8, 9] + + +def test_df_raises_value_error_node_not_exist(g): + """Test df traversal raises value error if val not in graph.""" + with pytest.raises(ValueError): + g.depth_first_traversal('blerg') + + +def test_depth_first_many_node_many_edges(g): + """Test depth first traversal returns all paths from one node.""" + g.add_node(5) + g.add_node(6) + g.add_node(7) + g.add_node(8) + g.add_node(9) + g.add_edge(5, 9) + g.add_edge(6, 9) + g.add_edge(7, 8) + g.add_edge(8, 5) + g.add_edge(9, 5) + g.add_edge(9, 6) + g.add_edge(9, 8) + g.add_edge(6, 5) + g.add_edge(7, 5) + g.add_edge(8, 9) + g.add_edge(6, 8) + assert g.depth_first_traversal(8) == [8, 9, 6, 5] From 69d06dafdf7eb92201759c06de531ec7c5ccbb98 Mon Sep 17 00:00:00 2001 From: "Chaitanya.Narukulla" Date: Tue, 7 Nov 2017 15:05:13 -0800 Subject: [PATCH 13/24] implemented breadth_first_traversal and depth_first_traversal function and written test --- src/graph.py | 12 +++++++- src/test_graph.py | 75 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/graph.py b/src/graph.py index db62fe0..62c5711 100644 --- a/src/graph.py +++ b/src/graph.py @@ -106,4 +106,14 @@ def depth_first_traversal(self, start_val): def breadth_first_traversal(self, start_val): """Traverse the graph by node's edges before moving to next node.""" - pass + if start_val in self._graph: + depth_traversal = [] + path = [start_val] + while path: + val = path.pop(0) + if val not in depth_traversal: + depth_traversal.append(val) + path = path + self._graph[val] + return depth_traversal + else: + raise ValueError('Value is not in graph.') diff --git a/src/test_graph.py b/src/test_graph.py index e2d0d95..f9bbd80 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -24,7 +24,6 @@ def test_add_node_returns_list_of_all_nodes(g): assert type(g.nodes()) == list - def test_edges_return_list_of_tuples_of_all_edges(g): """Test if edges returns list of tuples with pairings of each edge.""" g.add_node(5) @@ -104,16 +103,6 @@ def test_del_node_deletes_the_node_given(g): assert 7 not in g.nodes() -def test_del_node_deletes_the_node_given(g): - """Test del node delets the node.""" - g.add_node(5) - g.add_node(7) - g.add_edge(5, 7) - g.add_node(9) - g.del_node(7) - assert 7 not in g.nodes() - - def test_del_node_raises_value_error(g): """Test del node raises value error if node has been deleted.""" g.add_node(5) @@ -275,3 +264,67 @@ def test_depth_first_many_node_many_edges(g): g.add_edge(8, 9) g.add_edge(6, 8) assert g.depth_first_traversal(8) == [8, 9, 6, 5] + + +def test_breadth_first_one_node_many_edges(g): + """Test breadth first traversal returns all paths from one node.""" + g.add_node(5) + g.add_node(6) + g.add_node(7) + g.add_node(8) + g.add_node(9) + g.add_edge(5, 6) + g.add_edge(5, 7) + g.add_edge(5, 8) + g.add_edge(5, 9) + assert g.breadth_first_traversal(5) == [5, 6, 7, 8, 9] + + +def test_breadth_first_node_no_edge_return_only_self(g): + """Test breadth first traversal returns start val if no edges.""" + g.add_node(8) + g.add_node(9) + g.add_node(10) + g.add_edge(10, 8) + assert g.depth_first_traversal(8) == [8] + + +def test_breadth_first_one_edge_per_node(g): + """Test breadth first traversal returns all paths from one node.""" + g.add_node(5) + g.add_node(6) + g.add_node(7) + g.add_node(8) + g.add_node(9) + g.add_edge(5, 6) + g.add_edge(6, 7) + g.add_edge(7, 8) + g.add_edge(8, 9) + assert g.depth_first_traversal(5) == [5, 6, 7, 8, 9] + + +def test_bf_raises_value_error_node_not_exist(g): + """Test bf traversal raises value error if val not in graph.""" + with pytest.raises(ValueError): + g.depth_first_traversal('blerg') + + +def test_bf_first_many_node_many_edges(g): + """Test bf first traversal returns all paths from one node.""" + g.add_node(5) + g.add_node(6) + g.add_node(7) + g.add_node(8) + g.add_node(9) + g.add_edge(5, 9) + g.add_edge(6, 9) + g.add_edge(7, 8) + g.add_edge(8, 5) + g.add_edge(9, 5) + g.add_edge(9, 6) + g.add_edge(9, 8) + g.add_edge(6, 5) + g.add_edge(7, 5) + g.add_edge(8, 9) + g.add_edge(6, 8) + assert g.depth_first_traversal(8) == [8, 9, 6, 5] From 9b64dc231311d9c1f49ad57a464b9aa22e383486 Mon Sep 17 00:00:00 2001 From: "Chaitanya.Narukulla" Date: Tue, 7 Nov 2017 15:17:04 -0800 Subject: [PATCH 14/24] tested for cyclic graphs --- README.md | 7 ++++++- src/test_graph.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e3fa74..fd6a44f 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,10 @@ Graph() contains the following methods: * _del_edge(val1, val2) (On)_ - delete edge between two nodes. * _has_node(val) (O1)_ - returns true if node exists and false if not. * _neighbors(val) (On2)_ - return a list of nodes with edges to input node. -* _adjacent(val1, val2) (On)_ - return true if edge exists between two inputs, otherwise false. +* _adjacent(val1, val2) (On)_ - return true if edge exists between two inputs, + otherwise false. +* _depth_first_traversal(start_val) (Ologn)_ - will return full visited path of traversal completed. +* _breadth_first_traversal(start_val)(Ologn)_ - will return full visited path of traversal completed. To access any contained methods: new.nodes() @@ -215,5 +218,7 @@ new.del_edge(val1, val2) new.has_node(val) new.neighbors(val) new.adjacent(val1, val2) +new.depth_first_traversal(start_val) +new.breadth_first_traversal(start_val) ``` \ No newline at end of file diff --git a/src/test_graph.py b/src/test_graph.py index f9bbd80..80c2760 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -328,3 +328,21 @@ def test_bf_first_many_node_many_edges(g): g.add_edge(8, 9) g.add_edge(6, 8) assert g.depth_first_traversal(8) == [8, 9, 6, 5] + + +def test_breadth_first_works_on_cyclic_graph(g): + """Test breadth first traversal returns all paths from one node.""" + g.add_node(5) + g.add_node(6) + g.add_edge(5, 6) + g.add_edge(6, 5) + assert g.breadth_first_traversal(5) == [5, 6] + + +def test_depth_first_works_on_cyclic_graph(g): + """Test breadth first traversal returns all paths from one node.""" + g.add_node(5) + g.add_node(6) + g.add_edge(5, 6) + g.add_edge(6, 5) + assert g.breadth_first_traversal(5) == [5, 6] From a73a4b173be04d9ea8a2673b2b215fe3447c3176 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Wed, 8 Nov 2017 14:36:16 -0800 Subject: [PATCH 15/24] converted graph to dictionaries and added weight to edges --- src/graph.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/graph.py b/src/graph.py index 62c5711..3f1d921 100644 --- a/src/graph.py +++ b/src/graph.py @@ -14,30 +14,30 @@ def nodes(self): def edges(self): """Return a list of edges in graph.""" - edges = [] + edges = {} for key in self._graph: for i in self._graph[key]: - edges.append((key, i)) + edges[(key, i)] = self._graph[key][i] return edges def add_node(self, val): """Add a node with value of val to graph.""" - self._graph[val] = [] + self._graph[val] = {} - def add_edge(self, val1, val2): + def add_edge(self, val1, val2, weight=0): """Add a new edge to graph between val1 & val2 as well as add vals.""" if val1 in self._graph and val2 in self._graph: if val2 not in self._graph[val1]: - self._graph[val1].append(val2) + self._graph[val1][val2] = weight elif val1 in self._graph and val2 not in self._graph: - self._graph[val2] = [] - self._graph[val1].append(val2) + self._graph[val2] = {} + self._graph[val1][val2] = weight elif val2 in self._graph and val1 not in self._graph: - self._graph[val1] = [] - self._graph[val1].append(val2) + self._graph[val1] = {} + self._graph[val1][val2] = weight else: - self._graph[val1] = [val2] - self._graph[val2] = [] + self._graph[val1] = {val2: weight} + self._graph[val2] = {} def del_node(self, val): """Delete node w/val from graph, raises exception if not exist.""" @@ -46,7 +46,7 @@ def del_node(self, val): for key in self._graph: for i in self._graph[key]: if i == val: - self._graph[key].remove(val) + del self._graph[key] else: raise ValueError('There is no node of that value in the graph.') @@ -54,12 +54,13 @@ def del_edge(self, val1, val2): """Delete edge between val1 & val2 from graph.""" try: if val2 in self._graph[val1]: - self._graph[val1].remove(val2) + del self._graph[val1] else: raise ValueError('These edges do not exist.') except KeyError: raise ValueError('These edges do not exist.') + def has_node(self, val): """Return true or false if node has value.""" if val in self._graph: @@ -70,7 +71,7 @@ def has_node(self, val): def neighbors(self, val): """Return list of nodes connected node(val).""" try: - neighbors = self._graph[val] + neighbors = [] for key in self._graph: if val in self._graph[key]: if self._graph[key] not in neighbors: @@ -99,7 +100,7 @@ def depth_first_traversal(self, start_val): val = path.pop() if val not in depth_traversal: depth_traversal.append(val) - path = path + self._graph[val] + path = path + list(self._graph[val].keys()) return depth_traversal else: raise ValueError('Value is not in graph.') @@ -113,7 +114,7 @@ def breadth_first_traversal(self, start_val): val = path.pop(0) if val not in depth_traversal: depth_traversal.append(val) - path = path + self._graph[val] + path = path + list(self._graph[val].keys()) return depth_traversal else: raise ValueError('Value is not in graph.') From 12f72b6f7a0cb909747bd60a975e12a594487fdd Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Wed, 8 Nov 2017 15:29:55 -0800 Subject: [PATCH 16/24] refactored tests for python 2 --- src/graph.py | 9 +++---- src/test_graph.py | 61 +++++++++++++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/graph.py b/src/graph.py index 3f1d921..cd8b85d 100644 --- a/src/graph.py +++ b/src/graph.py @@ -60,7 +60,6 @@ def del_edge(self, val1, val2): except KeyError: raise ValueError('These edges do not exist.') - def has_node(self, val): """Return true or false if node has value.""" if val in self._graph: @@ -70,12 +69,10 @@ def has_node(self, val): def neighbors(self, val): """Return list of nodes connected node(val).""" + neighbors = [] try: - neighbors = [] - for key in self._graph: - if val in self._graph[key]: - if self._graph[key] not in neighbors: - neighbors.append(self._graph[key]) + for key in self._graph[val]: + neighbors.append(key) return neighbors except KeyError: raise ValueError('This node dosent exit') diff --git a/src/test_graph.py b/src/test_graph.py index 80c2760..e269442 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -10,11 +10,11 @@ def test_graph_iinitialize_empty_dict(g): def test_add_node(g): """Test it adds note.""" g.add_node(5) - assert g._graph[5] == [] + assert g._graph[5] == {} -def test_add_node_returns_list_of_all_nodes(g): - """Test if add node return list of all nodes.""" +def test_nodes_returns_list_of_all_nodes(g): + """Test nodes return list of all nodes.""" g.add_node(5) g.add_node(7) g.add_node(8) @@ -24,20 +24,20 @@ def test_add_node_returns_list_of_all_nodes(g): assert type(g.nodes()) == list -def test_edges_return_list_of_tuples_of_all_edges(g): - """Test if edges returns list of tuples with pairings of each edge.""" +def test_edges_return_dict_of_tuples_of_all_edge(g): + """Test if edges returns dict of tuples as edge and weight as value.""" g.add_node(5) g.add_node(7) g.edges() - assert g.edges() == [] + assert g.edges() == {} -def test_add_edges_returns_list_of_edges(g): - """Test if edges are added if 2 val are given.""" +def test_add_edges_returns_dict_of_edges_as_tuple_keys(g): + """Test if edges are added if 2 val are given with default weight.""" g.add_node(5) g.add_node(7) g.add_edge(5, 7) - assert g.edges() == [(5, 7)] + assert g.edges() == {(5, 7): 0} def test_add_two_edges(g): @@ -47,7 +47,7 @@ def test_add_two_edges(g): g.add_node(9) g.add_edge(5, 7) g.add_edge(7, 9) - assert g.edges() == [(5, 7), (7, 9)] + assert g.edges() == {(5, 7): 0, (7, 9): 0} def test_add_edges_will_add_mutipule_edges_to_a_node(g): @@ -59,7 +59,7 @@ def test_add_edges_will_add_mutipule_edges_to_a_node(g): g.add_edge(5, 7) g.add_edge(5, 9) g.add_edge(5, 10) - assert g.edges() == [(5, 7), (5, 9), (5, 10)] + assert g.edges() == {(5, 7): 0, (5, 9): 0, (5, 10): 0} def test_add_edges_with_one_val_in_graph_add_second_val(g): @@ -71,7 +71,7 @@ def test_add_edges_with_one_val_in_graph_add_second_val(g): g.add_edge(5, 7) g.add_edge(5, 9) g.add_edge(5, 15) - assert g.edges() == [(5, 7), (5, 9), (5, 15)] + assert g.edges() == {(5, 7): 0, (5, 9): 0, (5, 15): 0} def test_add_edges_with_first_val_new_and_second_val_in_graph(g): @@ -91,7 +91,7 @@ def test_add_edges_with_first_val_new_and_second_val_in_graph(g): def test_add_edges_with_two_new_nodes(g): """Test add edges when both nodes are new to graph.""" g.add_edge(19, 100) - assert g.edges() == [(19, 100)] + assert g.edges() == {(19, 100): 0} def test_del_node_deletes_the_node_given(g): @@ -121,7 +121,7 @@ def test_del_edge_deletes_all_edges(g): g.add_node(7) g.add_edge(5, 7) g.del_edge(5, 7) - assert g.edges() == [] + assert g.edges() == {} def test_del_edge_raises_value_error_if_too_many_edges_deleted(g): @@ -164,7 +164,7 @@ def test_neighbors_return_list_of_nodes_connected_to_input(g): g.add_node(9) g.add_edge(5, 9) g.add_edge(5, 8) - assert g.neighbors(5) == [7, 9, 8] + assert sorted(g.neighbors(5)) == [7, 8, 9] def test_neighbors_raises_valueerror_if_node_not_exist(g): @@ -213,7 +213,12 @@ def test_depth_first_one_node_many_edges(g): g.add_edge(5, 7) g.add_edge(5, 8) g.add_edge(5, 9) - assert g.depth_first_traversal(5) == [5, 9, 8, 7, 6] + assert 5 in g.depth_first_traversal(5) + assert 9 in g.depth_first_traversal(5) + assert 6 in g.depth_first_traversal(5) + assert 7 in g.depth_first_traversal(5) + assert 8 in g.depth_first_traversal(5) + assert type(g.depth_first_traversal(5)) == list def test_depth_first_node_no_edge_return_only_self(g): @@ -252,18 +257,19 @@ def test_depth_first_many_node_many_edges(g): g.add_node(7) g.add_node(8) g.add_node(9) + g.add_edge(8, 9) + g.add_edge(8, 5) g.add_edge(5, 9) g.add_edge(6, 9) g.add_edge(7, 8) - g.add_edge(8, 5) g.add_edge(9, 5) g.add_edge(9, 6) g.add_edge(9, 8) g.add_edge(6, 5) g.add_edge(7, 5) - g.add_edge(8, 9) g.add_edge(6, 8) - assert g.depth_first_traversal(8) == [8, 9, 6, 5] + dt = g.depth_first_traversal(8) + assert dt == [8, 5, 9, 6] or dt == [8, 9, 6 , 5] or dt == [8, 9, 5, 6] def test_breadth_first_one_node_many_edges(g): @@ -277,7 +283,12 @@ def test_breadth_first_one_node_many_edges(g): g.add_edge(5, 7) g.add_edge(5, 8) g.add_edge(5, 9) - assert g.breadth_first_traversal(5) == [5, 6, 7, 8, 9] + assert 5 in g.breadth_first_traversal(5) + assert 9 in g.breadth_first_traversal(5) + assert 6 in g.breadth_first_traversal(5) + assert 7 in g.breadth_first_traversal(5) + assert 8 in g.breadth_first_traversal(5) + assert type(g.breadth_first_traversal(5)) == list def test_breadth_first_node_no_edge_return_only_self(g): @@ -289,8 +300,8 @@ def test_breadth_first_node_no_edge_return_only_self(g): assert g.depth_first_traversal(8) == [8] -def test_breadth_first_one_edge_per_node(g): - """Test breadth first traversal returns all paths from one node.""" +def test_depth_first_one_edge_per_node(g): + """Test depth first traversal returns all paths from one node.""" g.add_node(5) g.add_node(6) g.add_node(7) @@ -327,7 +338,11 @@ def test_bf_first_many_node_many_edges(g): g.add_edge(7, 5) g.add_edge(8, 9) g.add_edge(6, 8) - assert g.depth_first_traversal(8) == [8, 9, 6, 5] + assert 5 in g.depth_first_traversal(8) + assert 9 in g.depth_first_traversal(8) + assert 6 in g.depth_first_traversal(8) + assert 5 in g.depth_first_traversal(8) + # assert g.depth_first_traversal(8) == [8, 9, 6, 5] def test_breadth_first_works_on_cyclic_graph(g): From 67c69e53b94ef3d13d5e5bdfe00d6db01f8a03aa Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Wed, 8 Nov 2017 16:30:08 -0800 Subject: [PATCH 17/24] added testing for weights of edges --- src/test_graph.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/test_graph.py b/src/test_graph.py index e269442..ea9aae0 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -361,3 +361,65 @@ def test_depth_first_works_on_cyclic_graph(g): g.add_edge(5, 6) g.add_edge(6, 5) assert g.breadth_first_traversal(5) == [5, 6] + + +def test_add_edges_w_weights_returns_dict_of_edges_as_tuple_keys(g): + """Test if edges are added if 2 val are given with default weight.""" + g.add_node(5) + g.add_node(7) + g.add_edge(5, 7, 6) + assert g.edges() == {(5, 7): 6} + + +def test_add_two_edges_w_weights_(g): + """Test if edges are added if 2 val are given with weights returns dict.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.add_edge(5, 7, 100) + g.add_edge(7, 9, 1) + assert g.edges() == {(5, 7): 100, (7, 9): 1} + + +def test_add_edges_w_weights_will_add_mutipule_edges_to_a_node(g): + """Test if edges are added if 2 val are given with weights.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.add_node(10) + g.add_edge(5, 7, 4) + g.add_edge(5, 9) + g.add_edge(5, 10, 12039) + assert g.edges() == {(5, 7): 4, (5, 9): 0, (5, 10): 12039} + + +def test_add_edges_with_weights_with_one_val_in_graph_add_second_val(g): + """Test if edges are added if 2 val are given.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.add_node(10) + g.add_edge(5, 7, 0) + g.add_edge(5, 9, 3) + g.add_edge(5, 15, 6) + assert g.edges() == {(5, 7): 0, (5, 9): 3, (5, 15): 6} + + +def test_add_edges_w_weights_with_first_val_new_and_second_val_in_graph(g): + """Test if edges are added if 2 val are given.""" + g.add_node(5) + g.add_node(7) + g.add_node(9) + g.add_node(10) + g.add_edge(5, 7, 4) + g.add_edge(5, 9, 7) + g.add_edge(15, 5, 9) + assert (15, 5) in g.edges() + assert (5, 7) in g.edges() + assert (5, 9) in g.edges() + + +def test_add_edges_with_weights_with_two_new_nodes(g): + """Test add edges when both nodes are new to graph.""" + g.add_edge(19, 100, 1000) + assert g.edges() == {(19, 100): 1000} From 0700ebc81aa3088383925607b850b28169bca804 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Wed, 8 Nov 2017 16:32:55 -0800 Subject: [PATCH 18/24] updated readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fd6a44f..c8dd3d7 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ new.peek() ``` -### Graph Class Usage +### Graph Class (weighted) Usage ``` To create an instance of a Graph() class contained in package, from python: @@ -196,9 +196,9 @@ new = Graph() *you may not call Graph() with any parameters.* Graph() contains the following methods: * _nodes() (O1)_ - will return a list of all nodes in the graph. -* _edges() (On2)_ - will return a list of edges in graph. +* _edges() (On2)_ - will return a dictionary of edges in graph with weights. *_add_node(val) (01)_ - adds a new node to the graph. -*_add_edge(val1, val2) (0n)_ - add a new edge to nodes in the graph. +*_add_edge(val1, val2, weight) (0n)_ - add a new edge to nodes in the graph. * _del_node(val) (On2)_ - delete node from graph. * _del_edge(val1, val2) (On)_ - delete edge between two nodes. * _has_node(val) (O1)_ - returns true if node exists and false if not. @@ -212,7 +212,7 @@ To access any contained methods: new.nodes() new.edges() new.add_node(val) -new.add_edge(val1, val2) +new.add_edge(val1, val2, weight) new.del_node(val) new.del_edge(val1, val2) new.has_node(val) From b9c8b6f083fe2f1a2bdf343663717d1f6d7007a0 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Thu, 9 Nov 2017 10:53:15 -0800 Subject: [PATCH 19/24] added if name=main and adjusted for new dictionary setup --- src/graph.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/graph.py b/src/graph.py index cd8b85d..89dde44 100644 --- a/src/graph.py +++ b/src/graph.py @@ -115,3 +115,42 @@ def breadth_first_traversal(self, start_val): return depth_traversal else: raise ValueError('Value is not in graph.') + + +if __name__ == '__main__': + graph_data = { + 'A': {'B': 0, 'D': 0}, + 'B': {'A': 0, 'E': 0, 'D': 0}, + 'C': {'A': 0, 'B': 0}, + 'D': {}, + 'E': {'D': 0, 'C': 0, 'B': 0, 'A': 0} + } + g = Graph() + g._graph = graph_data + print('Using the following graph:\n\n{}\n\nthe lists below show a ' + 'depth_first_traversal followed by a ' + 'breadth_first_traversal:'.format(graph_data)) + print('\nDepth First Traversal:') + print(g.depth_first_traversal('A')) + print('\nBreadth First Traversal:') + print(g.breadth_first_traversal('A')) + + graph_data = { + 'George': {'Steve': 0, 'Jane': 0, 'Phil': 0}, + 'Anne': {'Abe': 0, 'Uma': 0, 'George': 0}, + 'George': {'Abe': 0, 'Steve': 0}, + 'Steve': {'Anne': 0}, + 'Abe': {'George': 0, 'Uma': 0, 'Steve': 0, 'Phil': 0}, + 'Uma': {'Steve': 0}, + 'Phil': {'Uma': 0, 'George': 0, 'Phil': 0}, + 'Jane': {'Anne': 0}, + } + g2 = Graph() + g2._graph = graph_data + print('\n\nHere is another example on the following graph:\n\n{}\n\nthe ' + 'lists below, again, show a depth_first_traversal followed by a ' + 'breadth_first_traversal:'.format(graph_data)) + print('\nDepth First Traversal:') + print(g2.depth_first_traversal('Abe')) + print('\nBreadth First Traversal:') + print(g2.breadth_first_traversal('Abe')) From efa6675b48592aa21b47cdfe74b1c3ac2995b93f Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Tue, 5 Dec 2017 21:04:38 -0800 Subject: [PATCH 20/24] updated graph --- src/graph.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/graph.py b/src/graph.py index 89dde44..264608d 100644 --- a/src/graph.py +++ b/src/graph.py @@ -14,10 +14,10 @@ def nodes(self): def edges(self): """Return a list of edges in graph.""" - edges = {} + edges = [] for key in self._graph: for i in self._graph[key]: - edges[(key, i)] = self._graph[key][i] + edges.append((key, i, self._graph[key][i])) return edges def add_node(self, val): @@ -46,7 +46,7 @@ def del_node(self, val): for key in self._graph: for i in self._graph[key]: if i == val: - del self._graph[key] + del self._graph[key][val] else: raise ValueError('There is no node of that value in the graph.') @@ -54,7 +54,7 @@ def del_edge(self, val1, val2): """Delete edge between val1 & val2 from graph.""" try: if val2 in self._graph[val1]: - del self._graph[val1] + del self._graph[val1][val2] else: raise ValueError('These edges do not exist.') except KeyError: @@ -69,22 +69,28 @@ def has_node(self, val): def neighbors(self, val): """Return list of nodes connected node(val).""" - neighbors = [] try: + self._graph[val] + neighbors = [] for key in self._graph[val]: - neighbors.append(key) - return neighbors + if key not in neighbors: + neighbors.append(key) + for key in self._graph: + for child in self._graph[key]: + if child == val and key not in neighbors: + neighbors.append(key) + else: + return neighbors except KeyError: raise ValueError('This node dosent exit') def adjacent(self, val1, val2): """Return true if edge between vals, else false, & error if no val.""" - if val1 in self._graph or val2 in self._graph: + if val1 in self._graph and val2 in self._graph: if val1 in self._graph[val2]: return True - if val2 in self._graph[val1]: - return True - return False + else: + return False else: raise ValueError('These edges do not exist.') From e16933506f94a107341489e013ab05a02b663c95 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Wed, 6 Dec 2017 11:14:04 -0800 Subject: [PATCH 21/24] fixed tests --- src/graph.py | 2 +- src/test_graph.py | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/graph.py b/src/graph.py index 264608d..3dbd295 100644 --- a/src/graph.py +++ b/src/graph.py @@ -87,7 +87,7 @@ def neighbors(self, val): def adjacent(self, val1, val2): """Return true if edge between vals, else false, & error if no val.""" if val1 in self._graph and val2 in self._graph: - if val1 in self._graph[val2]: + if val2 in self._graph[val1]: return True else: return False diff --git a/src/test_graph.py b/src/test_graph.py index ea9aae0..692bc32 100644 --- a/src/test_graph.py +++ b/src/test_graph.py @@ -24,12 +24,12 @@ def test_nodes_returns_list_of_all_nodes(g): assert type(g.nodes()) == list -def test_edges_return_dict_of_tuples_of_all_edge(g): +def test_edges_return_lis_of_tuples_of_all_edge(g): """Test if edges returns dict of tuples as edge and weight as value.""" g.add_node(5) g.add_node(7) g.edges() - assert g.edges() == {} + assert g.edges() == [] def test_add_edges_returns_dict_of_edges_as_tuple_keys(g): @@ -37,7 +37,7 @@ def test_add_edges_returns_dict_of_edges_as_tuple_keys(g): g.add_node(5) g.add_node(7) g.add_edge(5, 7) - assert g.edges() == {(5, 7): 0} + assert g.edges() == [(5, 7, 0)] def test_add_two_edges(g): @@ -47,7 +47,7 @@ def test_add_two_edges(g): g.add_node(9) g.add_edge(5, 7) g.add_edge(7, 9) - assert g.edges() == {(5, 7): 0, (7, 9): 0} + assert g.edges() == [(5, 7, 0), (7, 9, 0)] def test_add_edges_will_add_mutipule_edges_to_a_node(g): @@ -59,7 +59,7 @@ def test_add_edges_will_add_mutipule_edges_to_a_node(g): g.add_edge(5, 7) g.add_edge(5, 9) g.add_edge(5, 10) - assert g.edges() == {(5, 7): 0, (5, 9): 0, (5, 10): 0} + assert g.edges() == [(5, 7, 0), (5, 9, 0), (5, 10, 0)] def test_add_edges_with_one_val_in_graph_add_second_val(g): @@ -71,7 +71,7 @@ def test_add_edges_with_one_val_in_graph_add_second_val(g): g.add_edge(5, 7) g.add_edge(5, 9) g.add_edge(5, 15) - assert g.edges() == {(5, 7): 0, (5, 9): 0, (5, 15): 0} + assert g.edges() == [(5, 7, 0), (5, 9, 0), (5, 15, 0)] def test_add_edges_with_first_val_new_and_second_val_in_graph(g): @@ -83,15 +83,15 @@ def test_add_edges_with_first_val_new_and_second_val_in_graph(g): g.add_edge(5, 7) g.add_edge(5, 9) g.add_edge(15, 5) - assert (15, 5) in g.edges() - assert (5, 7) in g.edges() - assert (5, 9) in g.edges() + assert (15, 5, 0) in g.edges() + assert (5, 7, 0) in g.edges() + assert (5, 9, 0) in g.edges() def test_add_edges_with_two_new_nodes(g): """Test add edges when both nodes are new to graph.""" g.add_edge(19, 100) - assert g.edges() == {(19, 100): 0} + assert g.edges() == [(19, 100, 0)] def test_del_node_deletes_the_node_given(g): @@ -121,7 +121,7 @@ def test_del_edge_deletes_all_edges(g): g.add_node(7) g.add_edge(5, 7) g.del_edge(5, 7) - assert g.edges() == {} + assert g.edges() == [] def test_del_edge_raises_value_error_if_too_many_edges_deleted(g): @@ -269,7 +269,7 @@ def test_depth_first_many_node_many_edges(g): g.add_edge(7, 5) g.add_edge(6, 8) dt = g.depth_first_traversal(8) - assert dt == [8, 5, 9, 6] or dt == [8, 9, 6 , 5] or dt == [8, 9, 5, 6] + assert dt == [8, 5, 9, 6] or dt == [8, 9, 6, 5] or dt == [8, 9, 5, 6] def test_breadth_first_one_node_many_edges(g): @@ -368,7 +368,7 @@ def test_add_edges_w_weights_returns_dict_of_edges_as_tuple_keys(g): g.add_node(5) g.add_node(7) g.add_edge(5, 7, 6) - assert g.edges() == {(5, 7): 6} + assert g.edges() == [(5, 7, 6)] def test_add_two_edges_w_weights_(g): @@ -378,7 +378,7 @@ def test_add_two_edges_w_weights_(g): g.add_node(9) g.add_edge(5, 7, 100) g.add_edge(7, 9, 1) - assert g.edges() == {(5, 7): 100, (7, 9): 1} + assert g.edges() == [(5, 7, 100), (7, 9, 1)] def test_add_edges_w_weights_will_add_mutipule_edges_to_a_node(g): @@ -390,7 +390,7 @@ def test_add_edges_w_weights_will_add_mutipule_edges_to_a_node(g): g.add_edge(5, 7, 4) g.add_edge(5, 9) g.add_edge(5, 10, 12039) - assert g.edges() == {(5, 7): 4, (5, 9): 0, (5, 10): 12039} + assert g.edges() == [(5, 7, 4), (5, 9, 0), (5, 10, 12039)] def test_add_edges_with_weights_with_one_val_in_graph_add_second_val(g): @@ -402,7 +402,7 @@ def test_add_edges_with_weights_with_one_val_in_graph_add_second_val(g): g.add_edge(5, 7, 0) g.add_edge(5, 9, 3) g.add_edge(5, 15, 6) - assert g.edges() == {(5, 7): 0, (5, 9): 3, (5, 15): 6} + assert g.edges() == [(5, 7, 0), (5, 9, 3), (5, 15, 6)] def test_add_edges_w_weights_with_first_val_new_and_second_val_in_graph(g): @@ -414,12 +414,12 @@ def test_add_edges_w_weights_with_first_val_new_and_second_val_in_graph(g): g.add_edge(5, 7, 4) g.add_edge(5, 9, 7) g.add_edge(15, 5, 9) - assert (15, 5) in g.edges() - assert (5, 7) in g.edges() - assert (5, 9) in g.edges() + assert (15, 5, 9) in g.edges() + assert (5, 7, 4) in g.edges() + assert (5, 9, 7) in g.edges() def test_add_edges_with_weights_with_two_new_nodes(g): """Test add edges when both nodes are new to graph.""" g.add_edge(19, 100, 1000) - assert g.edges() == {(19, 100): 1000} + assert g.edges() == [(19, 100, 1000)] From 82a87c2657e1988c4084dabf4499c31bc99569f8 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Fri, 8 Dec 2017 15:03:15 -0800 Subject: [PATCH 22/24] cleaned up sloppy spots --- src/graph.py | 50 +++++++++++++++++--------------------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/src/graph.py b/src/graph.py index 3dbd295..ce6a2e0 100644 --- a/src/graph.py +++ b/src/graph.py @@ -1,4 +1,4 @@ -"""Implement a class for a graph data structure.""" +"""Implement a class for graph data structure with weighted edges.""" class Graph(object): @@ -16,37 +16,27 @@ def edges(self): """Return a list of edges in graph.""" edges = [] for key in self._graph: - for i in self._graph[key]: - edges.append((key, i, self._graph[key][i])) + for child in self._graph[key]: + edges.append((key, child, self._graph[key][child])) return edges def add_node(self, val): """Add a node with value of val to graph.""" - self._graph[val] = {} + self._graph.setdefault(val, {}) def add_edge(self, val1, val2, weight=0): """Add a new edge to graph between val1 & val2 as well as add vals.""" - if val1 in self._graph and val2 in self._graph: - if val2 not in self._graph[val1]: - self._graph[val1][val2] = weight - elif val1 in self._graph and val2 not in self._graph: - self._graph[val2] = {} - self._graph[val1][val2] = weight - elif val2 in self._graph and val1 not in self._graph: - self._graph[val1] = {} - self._graph[val1][val2] = weight - else: - self._graph[val1] = {val2: weight} - self._graph[val2] = {} + self.add_node(val1) + self.add_node(val2) + self._graph[val1][val2] = weight def del_node(self, val): """Delete node w/val from graph, raises exception if not exist.""" if val in self._graph: del self._graph[val] for key in self._graph: - for i in self._graph[key]: - if i == val: - del self._graph[key][val] + if val in self._graph[key]: + del self._graph[key][val] else: raise ValueError('There is no node of that value in the graph.') @@ -58,14 +48,11 @@ def del_edge(self, val1, val2): else: raise ValueError('These edges do not exist.') except KeyError: - raise ValueError('These edges do not exist.') + raise ValueError('This edge does not exist.') def has_node(self, val): """Return true or false if node has value.""" - if val in self._graph: - return True - else: - return False + return val in self._graph def neighbors(self, val): """Return list of nodes connected node(val).""" @@ -82,17 +69,14 @@ def neighbors(self, val): else: return neighbors except KeyError: - raise ValueError('This node dosent exit') + raise ValueError('This node does not exit') def adjacent(self, val1, val2): """Return true if edge between vals, else false, & error if no val.""" - if val1 in self._graph and val2 in self._graph: - if val2 in self._graph[val1]: - return True - else: - return False - else: - raise ValueError('These edges do not exist.') + try: + return val2 in self._graph[val1] + except KeyError: + raise ValueError('{} is not a node in this graph'.format(val1)) def depth_first_traversal(self, start_val): """Traverse the graph from first edge of each node until ultimate.""" @@ -103,7 +87,7 @@ def depth_first_traversal(self, start_val): val = path.pop() if val not in depth_traversal: depth_traversal.append(val) - path = path + list(self._graph[val].keys()) + path += list(self._graph[val].keys()) return depth_traversal else: raise ValueError('Value is not in graph.') From 4afd7442c092be16eb0a800ee7a0f0d63169ad32 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Sun, 10 Dec 2017 14:45:57 -0800 Subject: [PATCH 23/24] updating errors --- src/graph.py | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/src/graph.py b/src/graph.py index ce6a2e0..44ffc0c 100644 --- a/src/graph.py +++ b/src/graph.py @@ -105,42 +105,3 @@ def breadth_first_traversal(self, start_val): return depth_traversal else: raise ValueError('Value is not in graph.') - - -if __name__ == '__main__': - graph_data = { - 'A': {'B': 0, 'D': 0}, - 'B': {'A': 0, 'E': 0, 'D': 0}, - 'C': {'A': 0, 'B': 0}, - 'D': {}, - 'E': {'D': 0, 'C': 0, 'B': 0, 'A': 0} - } - g = Graph() - g._graph = graph_data - print('Using the following graph:\n\n{}\n\nthe lists below show a ' - 'depth_first_traversal followed by a ' - 'breadth_first_traversal:'.format(graph_data)) - print('\nDepth First Traversal:') - print(g.depth_first_traversal('A')) - print('\nBreadth First Traversal:') - print(g.breadth_first_traversal('A')) - - graph_data = { - 'George': {'Steve': 0, 'Jane': 0, 'Phil': 0}, - 'Anne': {'Abe': 0, 'Uma': 0, 'George': 0}, - 'George': {'Abe': 0, 'Steve': 0}, - 'Steve': {'Anne': 0}, - 'Abe': {'George': 0, 'Uma': 0, 'Steve': 0, 'Phil': 0}, - 'Uma': {'Steve': 0}, - 'Phil': {'Uma': 0, 'George': 0, 'Phil': 0}, - 'Jane': {'Anne': 0}, - } - g2 = Graph() - g2._graph = graph_data - print('\n\nHere is another example on the following graph:\n\n{}\n\nthe ' - 'lists below, again, show a depth_first_traversal followed by a ' - 'breadth_first_traversal:'.format(graph_data)) - print('\nDepth First Traversal:') - print(g2.depth_first_traversal('Abe')) - print('\nBreadth First Traversal:') - print(g2.breadth_first_traversal('Abe')) From fb86f547e8439296a3946f5cb6e4eee36cd27403 Mon Sep 17 00:00:00 2001 From: Mark Reynoso Date: Tue, 12 Dec 2017 17:30:47 -0800 Subject: [PATCH 24/24] fixed adjacent --- src/graph.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/graph.py b/src/graph.py index 44ffc0c..8a5fdd4 100644 --- a/src/graph.py +++ b/src/graph.py @@ -57,26 +57,21 @@ def has_node(self, val): def neighbors(self, val): """Return list of nodes connected node(val).""" try: - self._graph[val] - neighbors = [] - for key in self._graph[val]: - if key not in neighbors: - neighbors.append(key) - for key in self._graph: - for child in self._graph[key]: - if child == val and key not in neighbors: - neighbors.append(key) - else: - return neighbors + return list(self._graph[val].keys()) except KeyError: - raise ValueError('This node does not exit') + raise ValueError('This node does not exist') def adjacent(self, val1, val2): """Return true if edge between vals, else false, & error if no val.""" try: - return val2 in self._graph[val1] + self._graph[val1] + self._graph[val2] + if val2 in self._graph[val1]: + return True + else: + return False except KeyError: - raise ValueError('{} is not a node in this graph'.format(val1)) + raise ValueError('Node is not a node in this graph') def depth_first_traversal(self, start_val): """Traverse the graph from first edge of each node until ultimate."""