diff --git a/.gitignore b/.gitignore index fa07443..3cfd4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -83,9 +84,10 @@ celerybeat-schedule .env # virtualenv -.venv -venv/ -ENV/ +bin/ +include/ +lib/ +share/ # Spyder project settings .spyderproject @@ -93,3 +95,8 @@ ENV/ # Rope project settings .ropeproject +# Compiled files +astar_native_c_code/*.o +astar_native_c_code/test_main + + diff --git a/.travis.yml b/.travis.yml index e5e1a5d..643e62e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,17 @@ language: python python: - - 3.3 + - 3.6 - nightly - - pypy3 - - "2.7" - - pypy before_install: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors -install: - - pip install . - - pip install nose coverage - - pip install coveralls +install: + - pip install coverage coveralls # # command to run tests, e.g. python setup.py test script: - - python setup.py nosetests --with-coverage --cover-package astar + - python setup.py build + - python setup.py install + - coverage run all_tests.py + - coveralls -after_sucess: - coveralls diff --git a/LICENSE b/LICENSE index 6b1d0ba..4a8a96f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2017, Julien Rialland +Copyright (c) 2012-2019, Julien Rialland All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/all_tests.py b/all_tests.py new file mode 100755 index 0000000..bdc9c89 --- /dev/null +++ b/all_tests.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + + +if __name__ == "__main__": + import sys + import unittest + import tests + runner = unittest.TextTestRunner() + sys.exit(runner.run(tests.suite())) diff --git a/src/astar/__init__.py b/astar/__init__.py similarity index 58% rename from src/astar/__init__.py rename to astar/__init__.py index 1a0193a..751588e 100644 --- a/src/astar/__init__.py +++ b/astar/__init__.py @@ -1,23 +1,36 @@ # -*- coding: utf-8 -*- """ generic A-Star path searching algorithm """ +import logging from abc import ABCMeta, abstractmethod from heapq import heappush, heappop +#------------------------------------------------------------------------------- +# module metada + __author__ = "Julien Rialland" -__copyright__ = "Copyright 2012-2017, J.Rialland" +__copyright__ = "Copyright 2012-2019, J.Rialland" __license__ = "BSD" -__version__ = "0.9" +__version__ = "0.95" __maintainer__ = __author__ __email__ = ''.join(map(chr, [106, 117, 108, 105, 101, 110, 46, 114, 105, 97, 108, 108, 97, 110, 100, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109])) __status__ = "Production" +#------------------------------------------------------------------------------- +# module constants Infinite = float('inf') +# module symbols (defined afterwards) +AStar = None +find_path = None + +#------------------------------------------------------------------------------- +# implementation +class AStar_Py: + """pure-python implementation of the algorithm""" -class AStar: __metaclass__ = ABCMeta __slots__ = () @@ -76,37 +89,52 @@ def _gen(): return reversed(list(_gen())) def astar(self, start, goal, reversePath=False): + if self.is_goal_reached(start, goal): return [start] + searchNodes = AStar.SearchNodeDict() + startNode = searchNodes[start] = AStar.SearchNode( start, gscore=.0, fscore=self.heuristic_cost_estimate(start, goal)) + openSet = [] heappush(openSet, startNode) + while openSet: + current = heappop(openSet) + if self.is_goal_reached(current.data, goal): return self.reconstruct_path(current, reversePath) + current.out_openset = True current.closed = True + for neighbor in [searchNodes[n] for n in self.neighbors(current.data)]: + if neighbor.closed: continue + tentative_gscore = current.gscore + \ self.distance_between(current.data, neighbor.data) + if tentative_gscore >= neighbor.gscore: continue + neighbor.came_from = current neighbor.gscore = tentative_gscore neighbor.fscore = tentative_gscore + \ - self.heuristic_cost_estimate(neighbor.data, goal) + self.heuristic_cost_estimate(neighbor.data, goal) + if neighbor.out_openset: neighbor.out_openset = False heappush(openSet, neighbor) return None -def find_path(start, goal, neighbors_fnct, reversePath=False, heuristic_cost_estimate_fnct=lambda a, b: Infinite, distance_between_fnct=lambda a, b: 1.0, is_goal_reached_fnct=lambda a, b: a == b): + +def find_path_Py(start, goal, neighbors_fnct, reversePath=False, heuristic_cost_estimate_fnct=lambda a, b: Infinite, distance_between_fnct=lambda a, b: 1.0, is_goal_reached_fnct=lambda a, b: a == b): """A non-class version of the path finding algorithm""" class FindPath(AStar): @@ -123,5 +151,69 @@ def is_goal_reached(self, current, goal): return is_goal_reached_fnct(current, goal) return FindPath().astar(start, goal, reversePath) +#define the 'Astar' class as being the pure-python implementation by default +AStar = AStar_Py +find_path = find_path_Py + +#------------------------------------------------------------------------------- +# native implementation if available + +#try to load the native module +try: + import astar_native + + class Astar_Native_Param: + """wraps the parameters that are needed by the C implementation""" + def __init__(self, start, goal, neighbors_fn, reverse_path, heuristic_cost_estimate_fn, distance_between_fn, is_goal_reached_fn): + self.start = start + self.goal = goal + self.neighbors_fn = neighbors_fn + self.reverse_path = reverse_path + self.heuristic_cost_estimate_fn = heuristic_cost_estimate_fn + self.distance_between_fn = distance_between_fn + self.is_goal_reached_fn = is_goal_reached_fn + + class Astar_Native(AStar_Py): + """uses the C implementation""" + def astar(self, start, goal, reversePath=False): + param = Astar_Native_Param( + start, + goal, + lambda a : self.neighbors(a), + reversePath, + lambda a,b : self.heuristic_cost_estimate(a, b), + lambda a,b : self.distance_between(a, b), + lambda a,b : self.is_goal_reached(a, b) + ) + return astar_native.astar(param) + + def find_path_Native( + start, + goal, + neighbors_fnct, + reversePath=False, + heuristic_cost_estimate_fnct=lambda a, b: Infinite, + distance_between_fnct=lambda a, b: 1.0, is_goal_reached_fnct=lambda a, b: a == b + ): + param = Astar_Native_Param( + start, + goal, + neighbors_fnct, + reversePath, + heuristic_cost_estimate_fnct, + distance_between_fnct, + is_goal_reached_fnct + ) + return astar_native.astar(param) + + #redefine the exported symbols + AStar = Astar_Native + find_path = find_path_Native + logging.info('Using astar_native implementation') + +except Exception: + logging.exception('The astar_native module is not available - Pure python implementation will be used instead') + pass __all__ = ['AStar', 'find_path'] + diff --git a/astar_native_c_code/Makefile b/astar_native_c_code/Makefile new file mode 100644 index 0000000..0853ebc --- /dev/null +++ b/astar_native_c_code/Makefile @@ -0,0 +1,21 @@ + +CC=gcc +CCFLAGS=-I. -I /usr/include/python3.6 -O0 -ggdb -Wfatal-errors +LDFLAGS=-lpthread -lpython3.6m + +all: test_main + +.PHONY: clean + +test_main : mempool.o rbtree.o priorityqueue.o astar_impl.o test_main.o + $(CC) $^ $(LDFLAGS) -o $@ + +%.o: %.c + $(CC) $< -c $(CCFLAGS) -o $@ + +test: test_main + @gdb -quiet -x gdbinit $< + +clean: + @rm -f *.o + diff --git a/astar_native_c_code/astar_impl.c b/astar_native_c_code/astar_impl.c new file mode 100644 index 0000000..a8a9e09 --- /dev/null +++ b/astar_native_c_code/astar_impl.c @@ -0,0 +1,188 @@ + +#include +#include "astar_impl.h" +#include "rbtree.h" +#include "priorityqueue.h" + +#define ASTAR_ALLOC_INCREMENT 1 + +typedef struct _searchnode { + void *data; + struct _searchnode *came_from; + double gscore; + double fscore; + bool closed; + bool out_openset; +} searchnode_t; + +searchnode_t *searchnode_new(void *data) { + searchnode_t *n = malloc(sizeof(searchnode_t)); + n->data = data; + n->came_from = NULL; + n->gscore = INFINITY; + n->fscore = INFINITY; + n->closed = false; + n->out_openset = true; + return n; +} + +void searchnode_free(searchnode_t *n) { + free(n); +} + +int searchnode_pq_cmp(const void *a, const void* b) { + float fa = (*(searchnode_t **)a)->fscore; + float fb = (*(searchnode_t **)b)->fscore; + int result = fa == fb ? 0 : ( fa < fb ? -1 : 1); + printf("%f %f %d\n", fa, fb, result); + return result; +} + +static void +astar_impl_feed_result( + searchnode_t *current, + searchnode_t *goal, + int reverse_path, + astar_result_t *result +) { + int capacity = 2; + result->size = 0; + result->path = malloc(capacity * sizeof(void*)); + result->path[result->size++] = current->data; + + while(current->came_from != NULL) { + current = current->came_from; + if(result->size == capacity) { + capacity += ASTAR_ALLOC_INCREMENT; + result->path = realloc(result->path, capacity * sizeof(void*)); + } + result->path[result->size++] = current->data; + } + + result->path = realloc(result->path, result->size * sizeof(void*)); + + if(!reverse_path) { + int i = result->size -1; + int j = 0; + while(i > j) { + void *tmp = result->path[i]; + result->path[i--] = result->path[j]; + result->path[j++] = tmp; + } + } +} + +#if __SIZEOF_POINTER__ <= __SIZEOF_LONG__ +static long default_hash_fn(void *p) { + return (long)p; +} + +int voidptr_key_compare(void *n1, void *n2, void *opt) { + return (long)n1 - (long)n2; +} + +#else + #error "Will no be able to convert pointers to longs because sizeof(void*) > sizeof(long) !" +#endif + + +void searchnode_free_entry(void *key, void*value, void* opt) { + searchnode_free((searchnode_t*)value); +} + +void astar_impl(astar_param_t* param, astar_result_t *result) { + + // provide a dummy hash function if missing + if(param->hash_fn == NULL) { + param->hash_fn= default_hash_fn; + } + + //default result if not found + result->path = NULL; + result->size = 0; + + //if goal is reached on step 1, return immediately + if(param->is_goal_reached_fn(param->invocation_ctx, param->start, param->goal)) { + result->path = malloc(sizeof(void*)); + result->path[0] = param->start; + result->size = 1; + return; + } + + searchnode_t *start_node = searchnode_new(param->start); + searchnode_t *goal_node = searchnode_new(param->goal); + + // create a map of all search node + rbtree_tree *nodes = rbtree_create(voidptr_key_compare, NULL); + + rbtree_insert(nodes, (void*)param->hash_fn(start_node->data), start_node); + rbtree_insert(nodes, (void*)param->hash_fn(goal_node->data), goal_node); + + //create the priority queue + pq_t *openset = pq_new(searchnode_pq_cmp); + + start_node->gscore = .0; + start_node->fscore = param->heuristic_cost_estimate_fn(param->invocation_ctx, start_node->data, goal_node->data); + pq_push(openset, start_node); + start_node->out_openset = false; + + //while openset is not empty + while(pq_has_len(openset)) { + + searchnode_t * current = pq_pop(openset); + + //test if the goal is reached + if(param->is_goal_reached_fn(param->invocation_ctx, current->data, goal_node->data)) { + astar_impl_feed_result(current, goal_node, param->reverse_path, result); + break; + } + + current->out_openset = true; + current->closed = true; + + //for each neighbor of the current node + void *neighbor_data; + void *iterator = param->neighbors_fn(param->invocation_ctx, current->data); + + while( (neighbor_data = param->iter_next_fn(param->invocation_ctx, iterator)) != NULL) { + + //get the search node associated with the neighbor + long hash = param->hash_fn(neighbor_data); + searchnode_t * neighbor = rbtree_lookup(nodes, (void*)hash); + if(neighbor == NULL) { + neighbor = searchnode_new(neighbor_data); + rbtree_insert(nodes, (void*)hash, neighbor); + } + + if(neighbor->closed) { + continue; + } + + double tentative_g_score = current->gscore + param->distance_between_fn(param->invocation_ctx, current->data, neighbor->data); + + if(tentative_g_score >= neighbor->gscore) { + continue; + } + + neighbor->came_from = current; + neighbor->gscore = tentative_g_score; + neighbor->fscore = tentative_g_score + param->heuristic_cost_estimate_fn(param->invocation_ctx, neighbor->data, goal_node->data); + + if(neighbor->out_openset) { + neighbor->out_openset = false; + pq_push(openset, neighbor); + } + } + + //dealloc the iterator + param->iter_free_fn(param->invocation_ctx, iterator); + } + + //dealloc all nodes + rbtree_foreach(nodes, searchnode_free_entry, NULL); + rbtree_delete(nodes); + + //dealloc the priority queue + pq_free(openset); +} + diff --git a/astar_native_c_code/astar_impl.h b/astar_native_c_code/astar_impl.h new file mode 100644 index 0000000..2ddc4cd --- /dev/null +++ b/astar_native_c_code/astar_impl.h @@ -0,0 +1,35 @@ +#ifndef ASTAR_IMPL_H_ +#define ASTAR_IMPL_H_ + +#include + +typedef long (*hash_fn_t)(void*); +typedef void* (*neighbors_fn_t)(void *invocation_ctx, void *node); +typedef void* (*iter_next_fn_t)(void *invocation_ctx, void *iterator); +typedef void (*iter_free_fn_t)(void *invocation_ctx, void *iterator); +typedef double (*heuristic_cost_estimate_fn_t)(void *invocation_ctx, void *n, void *goal); +typedef double (*distance_between_fn_t)(void *invocation_ctx, void *n1, void *n2); +typedef int (*is_goal_reached_fn_t)(void *invocation_ctx, void *n, void *goal); + +typedef struct _astar_param { + void *start; + void *goal; + int reverse_path; + void * invocation_ctx; + hash_fn_t hash_fn; + heuristic_cost_estimate_fn_t heuristic_cost_estimate_fn; + distance_between_fn_t distance_between_fn; + is_goal_reached_fn_t is_goal_reached_fn; + neighbors_fn_t neighbors_fn; + iter_next_fn_t iter_next_fn; + iter_free_fn_t iter_free_fn; +} astar_param_t; + +typedef struct _astar_result { + void ** path; + size_t size; +} astar_result_t; + +void astar_impl(astar_param_t* params, astar_result_t *result); + +#endif/*ASTAR_IMPL_H_*/ diff --git a/astar_native_c_code/astar_native_module.c b/astar_native_c_code/astar_native_module.c new file mode 100644 index 0000000..e4517b5 --- /dev/null +++ b/astar_native_c_code/astar_native_module.c @@ -0,0 +1,133 @@ + +#define PY_SSIZE_T_CLEAN + +#include "astar_impl.h" +#include + +typedef struct _py_astar_call_ctx { + PyObject *neighbors_fn; + PyObject *heuristic_cost_estimate_fn; + PyObject *distance_between_fn; + PyObject *is_goal_reached_fn; +} py_astar_call_ctx_t; + +static long py_hash_fn(void *obj) { + return (long)PyObject_Hash((PyObject*)obj); +} + +static void* py_neighbors_fn(void *invocation_ctx, void *node) { + py_astar_call_ctx_t *ctx = (py_astar_call_ctx_t*)invocation_ctx; + PyObject *l = PyObject_CallFunctionObjArgs(ctx->neighbors_fn, (PyObject*)node, NULL); + PyObject * iter = PyObject_GetIter(l); + Py_DECREF(l); + return iter; +} + +static void* py_iter_next_fn(void *invocation_ctx, void *iter) { + PyObject* next = PyIter_Next((PyObject*)iter); + return next; +} + +static void py_iter_free_fn(void *invocation_ctx, void *iter) { + Py_DECREF((PyObject*)iter); +} + +static double py_heuristic_cost_estimate_fn(void *invocation_ctx, void *n, void *goal) { + py_astar_call_ctx_t *ctx = (py_astar_call_ctx_t*)invocation_ctx; + PyObject *result = PyObject_CallFunctionObjArgs(ctx->heuristic_cost_estimate_fn, (PyObject*)n, (PyObject*)goal, NULL); + double d = PyFloat_AsDouble(result); + Py_DECREF(result); + return d; +} + +static double py_distance_between_fn(void *invocation_ctx, void *n1, void *n2) { + py_astar_call_ctx_t *ctx = (py_astar_call_ctx_t*)invocation_ctx; + PyObject *result = PyObject_CallFunctionObjArgs(ctx->distance_between_fn, (PyObject*)n1, (PyObject*)n2, NULL); + double d = PyFloat_AsDouble(result); + Py_DECREF(result); + return d; +} + +static int py_is_goal_reached_fn(void *invocation_ctx, void *n, void *goal) { + py_astar_call_ctx_t *ctx = (py_astar_call_ctx_t*)invocation_ctx; + PyObject *result = PyObject_CallFunctionObjArgs(ctx->is_goal_reached_fn, (PyObject*)n, (PyObject*)goal, NULL); + int isTrue = PyObject_IsTrue(result); + Py_DECREF(result); + return isTrue; +} + +static PyObject* astar(PyObject *self, PyObject *args) { + + py_astar_call_ctx_t invocation_ctx; + astar_param_t call_params; + + call_params.invocation_ctx = &invocation_ctx; + call_params.hash_fn = py_hash_fn; + call_params.heuristic_cost_estimate_fn = py_heuristic_cost_estimate_fn; + call_params.distance_between_fn = py_distance_between_fn; + call_params.is_goal_reached_fn = py_is_goal_reached_fn; + call_params.neighbors_fn = py_neighbors_fn; + call_params.iter_next_fn = py_iter_next_fn; + call_params.iter_free_fn = py_iter_free_fn; + + astar_result_t astar_result; + + PyObject *py_param; + if(!PyArg_ParseTuple(args, "O", &py_param)) { + return NULL; + } + + PyObject *start = PyObject_GetAttrString(py_param, "start"); + call_params.start = start; + + PyObject *goal = PyObject_GetAttrString(py_param, "goal"); + call_params.goal = goal; + + invocation_ctx.neighbors_fn = PyObject_GetAttrString(py_param, "neighbors_fn"); + + invocation_ctx.heuristic_cost_estimate_fn = PyObject_GetAttrString(py_param, "heuristic_cost_estimate_fn"); + + invocation_ctx.distance_between_fn = PyObject_GetAttrString(py_param, "distance_between_fn"); + + invocation_ctx.is_goal_reached_fn = PyObject_GetAttrString(py_param, "is_goal_reached_fn"); + + PyObject *oReversePath = PyObject_GetAttrString(py_param, "reverse_path"); + call_params.reverse_path = PyObject_IsTrue(oReversePath); + Py_DECREF(oReversePath); + Py_DECREF(py_param); + + astar_impl(&call_params, &astar_result); + + PyObject * lReturned = PyList_New(astar_result.size); + size_t i; + for(i=0; i < astar_result.size; i++) { + PyList_SetItem(lReturned, i, astar_result.path[i]); + }; + + if(astar_result.path != NULL) { + free(astar_result.path); + } + + return lReturned; +} + +/*----------------------------------------------------------------------------*/ +FILE *log_file = NULL; + +static PyMethodDef astart_native_methods[] = { + {"astar", astar, METH_VARARGS, "native astar implementation"}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef astar_native_moduledef = { + PyModuleDef_HEAD_INIT, + "astar_native", + "plain C implementation of the astar algorithm", + -1, + astart_native_methods +}; + +PyMODINIT_FUNC +PyInit_astar_native(void) { + return PyModule_Create(&astar_native_moduledef); +} diff --git a/astar_native_c_code/gdbinit b/astar_native_c_code/gdbinit new file mode 100644 index 0000000..1b1c1f1 --- /dev/null +++ b/astar_native_c_code/gdbinit @@ -0,0 +1,17 @@ +set confirm off +handle SIGINT pass nostop noprint +handle SIGQUIT pass nostop noprint +handle SIGUSR1 pass nostop noprint +handle SIGUSR2 pass nostop noprint + + +set $_exitcode = -1 +run +if $_exitcode != -1 + quit +else + backtrace + quit +end + + diff --git a/astar_native_c_code/mempool.c b/astar_native_c_code/mempool.c new file mode 100644 index 0000000..a4f54a2 --- /dev/null +++ b/astar_native_c_code/mempool.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 1999-2006 Christophe Fillot. + * E-mail: cf@utc.fr + * + * mempool.c: Simple Memory Pools. + */ + +#include +#include +#include +#include +#include +#include "mempool.h" + +/* + * Internal function used to allocate a memory block, and do basic operations + * on it. It does not manipulate pools, so no mutex is needed. + */ +static inline memblock_t *memblock_alloc(size_t size,int zeroed) +{ + memblock_t *block; + size_t total_size; + + total_size = size + sizeof(memblock_t); + if (!(block = malloc(total_size))) + return NULL; + + if (zeroed) + memset(block,0,total_size); + + block->tag = MEMBLOCK_TAG; + block->block_size = size; + block->prev = block->next = NULL; + return block; +} + +/* Insert block in linked list */ +static inline void memblock_insert(mempool_t *pool,memblock_t *block) +{ + MEMPOOL_LOCK(pool); + + pool->nr_blocks++; + pool->total_size += block->block_size; + + block->prev = NULL; + block->next = pool->block_list; + + if (block->next) + block->next->prev = block; + + pool->block_list = block; + + MEMPOOL_UNLOCK(pool); +} + +/* Remove block from linked list */ +static inline void memblock_delete(mempool_t *pool,memblock_t *block) +{ + MEMPOOL_LOCK(pool); + + pool->nr_blocks--; + pool->total_size -= block->block_size; + + if (!block->prev) + pool->block_list = block->next; + else + block->prev->next = block->next; + + if (block->next) + block->next->prev = block->prev; + + block->next = block->prev = NULL; + MEMPOOL_UNLOCK(pool); +} + +/* Allocate a new block in specified pool (internal function) */ +static inline void *mp_alloc_inline(mempool_t *pool,size_t size,int zeroed) +{ + memblock_t *block; + + if (!(block = memblock_alloc(size,zeroed))) + return NULL; + + block->pool = pool; + memblock_insert(pool,block); + return(block->data); +} + +/* Allocate a new block in specified pool */ +void *mp_alloc(mempool_t *pool,size_t size) +{ + return(mp_alloc_inline(pool,size, true)); +} + +/* Allocate a new block which will not be zeroed */ +void *mp_alloc_n0(mempool_t *pool,size_t size) +{ + return(mp_alloc_inline(pool,size, false)); +} + +/* Reallocate a block */ +void *mp_realloc(void *addr,size_t new_size) +{ + memblock_t *ptr,*block = (memblock_t *)addr - 1; + mempool_t *pool; + size_t total_size; + + assert(block->tag == MEMBLOCK_TAG); + pool = block->pool; + + /* remove this block from list */ + memblock_delete(pool,block); + + /* reallocate block with specified size */ + total_size = new_size + sizeof(memblock_t); + + if (!(ptr = realloc(block,total_size))) { + memblock_insert(pool,block); + return NULL; + } + + ptr->block_size = new_size; + memblock_insert(pool,ptr); + return ptr->data; +} + +/* Allocate a new memory block and copy data into it */ +void *mp_dup(mempool_t *pool,void *data,size_t size) +{ + void *p; + + if ((p = mp_alloc_n0(pool,size))) + memcpy(p,data,size); + + return p; +} + +/* Duplicate specified string and insert it in a memory pool */ +char *mp_strdup(mempool_t *pool,char *str) +{ + char *new_str; + + if ((new_str = mp_alloc(pool,strlen(str)+1)) == NULL) + return NULL; + + strcpy(new_str,str); + return new_str; +} + +/* Free block at specified address */ +int mp_free(void *addr) +{ + memblock_t *block = (memblock_t *)addr - 1; + mempool_t *pool; + + if (addr != NULL) { + assert(block->tag == MEMBLOCK_TAG); + pool = block->pool; + + memblock_delete(pool,block); + memset(block,0,sizeof(memblock_t)); + free(block); + } + + return(0); +} + +/* Free block at specified address and clean pointer */ +int mp_free_ptr(void *addr) +{ + void *p; + + assert(addr != NULL); + p = *(void **)addr; + *(void **)addr = NULL; + mp_free(p); + return(0); +} + +/* Free all blocks of specified pool */ +void mp_free_all_blocks(mempool_t *pool) +{ + memblock_t *block,*next; + + MEMPOOL_LOCK(pool); + + for(block=pool->block_list;block;block=next) { + next = block->next; + free(block); + } + + pool->block_list = NULL; + pool->nr_blocks = 0; + pool->total_size = 0; + + MEMPOOL_UNLOCK(pool); +} + +/* Free specified memory pool */ +void mp_free_pool(mempool_t *pool) +{ + mp_free_all_blocks(pool); + + if (!(pool->flags & MEMPOOL_FIXED)) + free(pool); +} + +/* Create a new pool in a fixed memory area */ +mempool_t *mp_create_fixed_pool(mempool_t *mp,char *name) +{ + memset(mp,0,sizeof(*mp)); + + if (pthread_mutex_init(&mp->lock,NULL) != 0) + return NULL; + + mp->name = name; + mp->block_list = NULL; + mp->flags = MEMPOOL_FIXED; + return mp; +} + +/* Create a new pool */ +mempool_t *mp_create_pool(char *name) +{ + mempool_t *mp = malloc(sizeof(*mp)); + + if (!mp || !mp_create_fixed_pool(mp,name)) { + free(mp); + return NULL; + } + + mp->flags = 0; /* clear "FIXED" flag */ + return mp; +} + diff --git a/astar_native_c_code/mempool.h b/astar_native_c_code/mempool.h new file mode 100644 index 0000000..b0376e7 --- /dev/null +++ b/astar_native_c_code/mempool.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1999-2006 Christophe Fillot. + * E-mail: cf@utc.fr + * + * mempool.h: Simple Memory Pools. + */ + +#ifndef __MEMPOOL_H__ +#define __MEMPOOL_H__ 1 + +#include +#include +#include +#include + +/* Memory Pool "Fixed" Flag */ +#define MEMPOOL_FIXED 1 + +/* Dummy value used to check if a memory block is invalid */ +#define MEMBLOCK_TAG 0xdeadbeef + +typedef struct memblock memblock_t; +typedef struct mempool mempool_t; + +/* Memory block */ +struct memblock { + int tag; /* MEMBLOCK_TAG if block is valid */ + size_t block_size; /* Block size (without header) */ + memblock_t *next,*prev; /* Double linked list pointers */ + mempool_t *pool; /* Pool which contains this block */ + uint64_t data[0]; /* Memory block itself */ +}; + +/* Memory Pool */ +struct mempool { + memblock_t *block_list; /* Double-linked block list */ + pthread_mutex_t lock; /* Mutex for managing pool */ + char *name; /* Name of this pool */ + int flags; /* Flags */ + int nr_blocks; /* Number of blocks in this pool */ + size_t total_size; /* Total bytes allocated */ + size_t max_size; /* Maximum memory */ +}; + +/* Lock and unlock access to a memory pool */ +#define MEMPOOL_LOCK(mp) pthread_mutex_lock(&(mp)->lock) +#define MEMPOOL_UNLOCK(mp) pthread_mutex_unlock(&(mp)->lock) + +/* Callback function for use with mp_foreach */ +typedef void (*mp_foreach_cbk)(memblock_t *block,void *user_arg); + +/* Execute an action for each block in specified pool */ +static inline void mp_foreach(mempool_t *pool,mp_foreach_cbk cbk,void *arg) +{ + memblock_t *mb; + + for(mb=pool->block_list;mb;mb=mb->next) + cbk(mb,arg); +} + +/* Allocate a new block in specified pool */ +void *mp_alloc(mempool_t *pool,size_t size); + +/* Allocate a new block which will not be zeroed */ +void *mp_alloc_n0(mempool_t *pool,size_t size); + +/* Reallocate a block */ +void *mp_realloc(void *addr,size_t new_size); + +/* Allocate a new memory block and copy data into it */ +void *mp_dup(mempool_t *pool,void *data,size_t size); + +/* Duplicate specified string and insert it in a memory pool */ +char *mp_strdup(mempool_t *pool,char *str); + +/* Free block at specified address */ +int mp_free(void *addr); + +/* Free block at specified address and clean pointer */ +int mp_free_ptr(void *addr); + +/* Free all blocks of specified pool */ +void mp_free_all_blocks(mempool_t *pool); + +/* Free specified memory pool */ +void mp_free_pool(mempool_t *pool); + +/* Create a new pool in a fixed memory area */ +mempool_t *mp_create_fixed_pool(mempool_t *mp,char *name); + +/* Create a new pool */ +mempool_t *mp_create_pool(char *name); + +#endif + diff --git a/astar_native_c_code/priorityqueue.c b/astar_native_c_code/priorityqueue.c new file mode 100644 index 0000000..2718d68 --- /dev/null +++ b/astar_native_c_code/priorityqueue.c @@ -0,0 +1,55 @@ + +#include +#include "priorityqueue.h" + +pq_t *pq_new(pq_cmp_fn_t compare_fn) { + pq_t *pq = malloc(sizeof(pq_t)); + mp_create_fixed_pool(&pq->mp,"Priority Queue"); + pq->compare_fn = compare_fn; + pq->first = NULL; + return pq; +} + +void pq_free(pq_t *pq) { + mp_free_pool(&pq->mp); + free(pq); +} + +void pq_push(pq_t *pq, void *data) { + pq_cell_t *inserted = mp_alloc_n0(&pq->mp,sizeof(pq_cell_t)); + inserted->data = data; + inserted->next = NULL; + + if(pq->first == NULL) { + pq->first = inserted; + return; + } + + pq_cell_t *current = pq->first, *prec = NULL; + while(current != NULL && pq->compare_fn(&data, &(current->data)) > 0) { + prec = current; + current = current->next; + } + inserted->next = current; + if(prec != NULL) { + prec->next = inserted; + } else { + pq->first = inserted; + } +} + +void *pq_pop(pq_t *pq) { + if(pq->first == NULL) { + return NULL; + } + pq_cell_t *current = pq->first; + void *data = current->data; + pq->first = current->next; + mp_free(current); + return data; +} + +bool pq_has_len(pq_t *pq) { + return pq->first != NULL; +} + diff --git a/astar_native_c_code/priorityqueue.h b/astar_native_c_code/priorityqueue.h new file mode 100644 index 0000000..6c1414c --- /dev/null +++ b/astar_native_c_code/priorityqueue.h @@ -0,0 +1,32 @@ +#ifndef PRIORITYQUEUE_H_ +#define PRIORITYQUEUE_H_ + +#include +#include +#include "mempool.h" + +typedef int (*pq_cmp_fn_t)(const void*, const void*); + +typedef struct _pq_cell { + void * data; + struct _pq_cell *next; +} pq_cell_t; + +typedef struct _pq { + pq_cmp_fn_t compare_fn; + mempool_t mp; + pq_cell_t *first; +} pq_t; + +pq_t *pq_new(pq_cmp_fn_t compare_fn); + +void pq_free(pq_t *pq); + +void pq_push(pq_t *pq, void *data); + +void *pq_pop(pq_t *pq); + +bool pq_has_len(pq_t *pq); + + +#endif/*PRIORITYQUEUE_H_*/ diff --git a/astar_native_c_code/rbtree.c b/astar_native_c_code/rbtree.c new file mode 100644 index 0000000..14734cc --- /dev/null +++ b/astar_native_c_code/rbtree.c @@ -0,0 +1,523 @@ +/* + * Dynamips + * Copyright (c) 2005 Christophe Fillot. + * E-mail: cf@utc.fr + * + * rbtree.c: Red/Black Trees. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rbtree.h" + +#define rbtree_nil(tree) (&(tree)->nil) +#define NIL(tree,x) (((x) == rbtree_nil(tree)) || !x) + +/* Allocate memory for a new node */ +static rbtree_node *rbtree_node_alloc(rbtree_tree *tree,void *key,void *value) +{ + rbtree_node *node; + + if (!(node = mp_alloc_n0(&tree->mp,sizeof(*node)))) + return NULL; + + node->key = key; + node->value = value; + node->left = rbtree_nil(tree); + node->right = rbtree_nil(tree); + node->parent = rbtree_nil(tree); + node->color = -1; + return node; +} + +/* Free memory used by a node */ +static inline void rbtree_node_free(rbtree_tree *tree,rbtree_node *node) +{ + mp_free(node); +} + +/* Returns the node which represents the minimum value */ +static inline rbtree_node *rbtree_min(rbtree_tree *tree,rbtree_node *x) +{ + while(!NIL(tree,x->left)) + x = x->left; + + return(x); +} + +/* Returns the node which represents the maximum value */ +static inline rbtree_node *rbtree_max(rbtree_tree *tree,rbtree_node *x) +{ + while(!NIL(tree,x->right)) + x = x->right; + + return(x); +} + +/* Returns the successor of a node */ +static inline rbtree_node *rbtree_successor(rbtree_tree *tree,rbtree_node *x) +{ + rbtree_node *y; + + if (!NIL(tree,x->right)) + return(rbtree_min(tree,x->right)); + + y = x->parent; + while(!NIL(tree,y) && (x == y->right)) { + x = y; + y = y->parent; + } + + return(y); +} + +/* Left rotation */ +static inline void rbtree_left_rotate(rbtree_tree *tree,rbtree_node *x) +{ + rbtree_node *y; + + y = x->right; + x->right = y->left; + + if (!NIL(tree,x->right)) + x->right->parent = x; + + y->parent = x->parent; + + if (NIL(tree,x->parent)) + tree->root = y; + else { + if (x == x->parent->left) + x->parent->left = y; + else + x->parent->right = y; + } + + y->left = x; + x->parent = y; +} + +/* Right rotation */ +static inline void rbtree_right_rotate(rbtree_tree *tree,rbtree_node *y) +{ + rbtree_node *x; + + x = y->left; + y->left = x->right; + + if (!NIL(tree,y->left)) + y->left->parent = y; + + x->parent = y->parent; + + if (NIL(tree,y->parent)) + tree->root = x; + else { + if (y->parent->left == y) + y->parent->left = x; + else + y->parent->right = x; + } + + x->right = y; + y->parent = x; +} + +/* insert a new node */ +static rbtree_node *rbtree_insert_new(rbtree_tree *tree,void *key,void *value, + int *exists) +{ + rbtree_node *parent,*node,*new_node,**nodeplace; + int comp; + + nodeplace = &tree->root; + parent = NULL; + *exists = false; + + for(;;) + { + node = *nodeplace; + + if (NIL(tree,node)) + break; + + comp = tree->key_cmp(key,node->key,tree->opt_data); + + if (!comp) { + *exists = true; + node->value = value; + return node; + } + + parent = node; + nodeplace = (comp > 0) ? &node->right : &node->left; + } + + /* create a new node */ + if (!(new_node = rbtree_node_alloc(tree,key,value))) + return NULL; + + *nodeplace = new_node; + new_node->parent = parent; + tree->node_count++; + return new_node; +} + +/* Insert a node in a Red/Black Tree */ +int rbtree_insert(rbtree_tree *tree,void *key,void *value) +{ + rbtree_node *x,*y; + int exists; + + /* insert a new node (if necessary) */ + x = rbtree_insert_new(tree,key,value,&exists); + + if (exists) return(0); + if (!x) return(-1); + + tree->node_count++; + + /* maintains red-black properties */ + x->color = RBTREE_RED; + + while((x != tree->root) && (x->parent->color == RBTREE_RED)) + { + if (x->parent == x->parent->parent->left) + { + y = x->parent->parent->right; + + if (y->color == RBTREE_RED) + { + x->parent->color = RBTREE_BLACK; + y->color = RBTREE_BLACK; + x->parent->parent->color = RBTREE_RED; + x = x->parent->parent; + } + else + { + if (x == x->parent->right) { + x = x->parent; + rbtree_left_rotate(tree,x); + } + + x->parent->color = RBTREE_BLACK; + x->parent->parent->color = RBTREE_RED; + rbtree_right_rotate(tree,x->parent->parent); + } + } + else + { + y = x->parent->parent->left; + + if (y->color == RBTREE_RED) + { + x->parent->color = RBTREE_BLACK; + y->color = RBTREE_BLACK; + x->parent->parent->color = RBTREE_RED; + x = x->parent->parent; + } + else + { + if (x == x->parent->left) { + x = x->parent; + rbtree_right_rotate(tree,x); + } + + x->parent->color = RBTREE_BLACK; + x->parent->parent->color = RBTREE_RED; + rbtree_left_rotate(tree,x->parent->parent); + } + } + } + + tree->root->color = RBTREE_BLACK; + return(0); +} + +/* Lookup for a node corresponding to "key" */ +static inline rbtree_node *rbtree_lookup_node(rbtree_tree *tree,void *key) +{ + rbtree_node *node; + int comp; + + node = tree->root; + + for (;;) { + if (NIL(tree,node)) /* key not found */ + break; + + if (!(comp = tree->key_cmp(key,node->key,tree->opt_data))) + break; /* exact match */ + + node = (comp > 0) ? node->right : node->left; + } + + return(node); +} + +/* + * Lookup for a node corresponding to "key". If node does not exist, + * function returns null pointer. + */ +void *rbtree_lookup(rbtree_tree *tree,void *key) +{ + return(rbtree_lookup_node(tree,key)->value); +} + +/* Restore Red/black tree properties after a removal */ +static void rbtree_removal_fixup(rbtree_tree *tree,rbtree_node *x) +{ + rbtree_node *w; + + while((x != tree->root) && (x->color == RBTREE_BLACK)) + { + if (x == x->parent->left) + { + w = x->parent->right; + + if (w->color == RBTREE_RED) { + w->color = RBTREE_BLACK; + x->parent->color = RBTREE_RED; + rbtree_left_rotate(tree,x->parent); + w = x->parent->right; + } + + if ((w->left->color == RBTREE_BLACK) && + (w->right->color == RBTREE_BLACK)) + { + w->color = RBTREE_RED; + x = x->parent; + } + else + { + if (w->right->color == RBTREE_BLACK) { + w->left->color = RBTREE_BLACK; + w->color = RBTREE_RED; + rbtree_right_rotate(tree,w); + w = x->parent->right; + } + + w->color = x->parent->color; + x->parent->color = RBTREE_BLACK; + w->right->color = RBTREE_BLACK; + rbtree_left_rotate(tree,x->parent); + x = tree->root; + } + } + else + { + w = x->parent->left; + + if (w->color == RBTREE_RED) { + w->color = RBTREE_BLACK; + x->parent->color = RBTREE_RED; + rbtree_right_rotate(tree,x->parent); + w = x->parent->left; + } + + if ((w->right->color == RBTREE_BLACK) && + (w->left->color == RBTREE_BLACK)) + { + w->color = RBTREE_RED; + x = x->parent; + } + else + { + if (w->left->color == RBTREE_BLACK) { + w->right->color = RBTREE_BLACK; + w->color = RBTREE_RED; + rbtree_left_rotate(tree,w); + w = x->parent->left; + } + + w->color = x->parent->color; + x->parent->color = RBTREE_BLACK; + w->left->color = RBTREE_BLACK; + rbtree_right_rotate(tree,x->parent); + x = tree->root; + } + } + } + + x->color = RBTREE_BLACK; +} + +/* Removes a node out of a tree */ +void *rbtree_remove(rbtree_tree *tree,void *key) +{ + rbtree_node *z = rbtree_lookup_node(tree,key); + rbtree_node *x,*y; + void *value; + + if (NIL(tree,z)) + return NULL; + + value = z->value; + + if (NIL(tree,z->left) || NIL(tree,z->right)) + y = z; + else + y = rbtree_successor(tree,z); + + if (!NIL(tree,y->left)) + x = y->left; + else + x = y->right; + + x->parent = y->parent; + + if (NIL(tree,y->parent)) + tree->root = x; + else { + if (y == y->parent->left) + y->parent->left = x; + else + y->parent->right = x; + } + + if (y != z) { + z->key = y->key; + z->value = y->value; + } + + if (y->color == RBTREE_BLACK) + rbtree_removal_fixup(tree,x); + + rbtree_node_free(tree,y); + tree->node_count++; + return(value); +} + +static void rbtree_foreach_node(rbtree_tree *tree,rbtree_node *node, + tree_fforeach user_fn,void *opt) +{ + if (!NIL(tree,node)) { + rbtree_foreach_node(tree,node->left,user_fn,opt); + user_fn(node->key,node->value,opt); + rbtree_foreach_node(tree,node->right,user_fn,opt); + } +} + +/* Call the specified function for each node */ +int rbtree_foreach(rbtree_tree *tree,tree_fforeach user_fn,void *opt) +{ + if (!tree) return(-1); + + rbtree_foreach_node(tree,tree->root,user_fn,opt); + return(0); +} + +/* Returns the maximum height of the right and left sub-trees */ +static int rbtree_height_node(rbtree_tree *tree,rbtree_node *node) +{ + int lh,rh; + + lh = (!NIL(tree,node->left)) ? rbtree_height_node(tree,node->left) : 0; + rh = (!NIL(tree,node->right)) ? rbtree_height_node(tree,node->right) : 0; + return(1 + (lh > rh ? lh : rh)); +} + +/* Compute the height of a Red/Black tree */ +int rbtree_height(rbtree_tree *tree) +{ + return(!NIL(tree,tree->root) ? rbtree_height_node(tree,tree->root) : 0); +} + +/* Returns the number of nodes */ +int rbtree_node_count(rbtree_tree *tree) +{ + return(tree->node_count); +} + +/* Purge all nodes */ +void rbtree_purge(rbtree_tree *tree) +{ + mp_free_all_blocks(&tree->mp); + tree->node_count = 0; + + /* just in case */ + memset(rbtree_nil(tree),0,sizeof(rbtree_node)); + rbtree_nil(tree)->color = RBTREE_BLACK; + + /* reset root */ + tree->root = rbtree_nil(tree); +} + +/* Check a node */ +static int rbtree_check_node(rbtree_tree *tree,rbtree_node *node) +{ + if (!NIL(tree,node)) return(0); + + if (!NIL(tree,node->left)) { + if (tree->key_cmp(node->key,node->left->key,tree->opt_data) <= 0) + return(-1); + + if (rbtree_check_node(tree,node->left) == -1) + return(-1); + } + + if (!NIL(tree,node->right)) { + if (tree->key_cmp(node->key,node->right->key,tree->opt_data) >= 0) + return(-1); + + if (rbtree_check_node(tree,node->right) == -1) + return(-1); + } + + return(0); +} + +/* Check tree consistency */ +int rbtree_check(rbtree_tree *tree) +{ + return(rbtree_check_node(tree,tree->root)); +} + +/* Create a new Red/Black tree */ +rbtree_tree *rbtree_create(tree_fcompare key_cmp,void *opt_data) +{ + rbtree_tree *tree; + + if (!(tree = malloc(sizeof(*tree)))) + return NULL; + + memset(tree,0,sizeof(*tree)); + + /* initialize the memory pool */ + if (!mp_create_fixed_pool(&tree->mp,"Red-Black Tree")) { + free(tree); + return NULL; + } + + /* initialize the "nil" pointer */ + memset(rbtree_nil(tree),0,sizeof(rbtree_node)); + rbtree_nil(tree)->color = RBTREE_BLACK; + + tree->key_cmp = key_cmp; + tree->opt_data = opt_data; + tree->root = rbtree_nil(tree); + return tree; +} + +/* Delete a Red/Black tree */ +void rbtree_delete(rbtree_tree *tree) +{ + if (tree) { + mp_free_pool(&tree->mp); + free(tree); + } +} + + + + + diff --git a/astar_native_c_code/rbtree.h b/astar_native_c_code/rbtree.h new file mode 100644 index 0000000..45b4e9e --- /dev/null +++ b/astar_native_c_code/rbtree.h @@ -0,0 +1,97 @@ +/* + * IPFlow Collector + * Copyright (c) 2004 Christophe Fillot. + * E-mail: cf@utc.fr + * + * rbtree.c: Red/Black Trees. + */ + +#ifndef __RBTREE_H__ +#define __RBTREE_H__ 1 + +static const char rcsid_rbtree[] = "$Id$"; + +#include +#include "mempool.h" + +/* Comparison function for 2 keys */ +typedef int (*tree_fcompare)(void *key1,void *key2,void *opt); + +/* User function to call when using rbtree_foreach */ +typedef void (*tree_fforeach)(void *key,void *value,void *opt); + +/* Node colors */ +enum { + RBTREE_RED = 0, + RBTREE_BLACK, +}; + +/* + * Description of a node in a Red/Black tree. To be more efficient, keys are + * stored with a void * pointer, allowing to use different type keys. + */ +typedef struct rbtree_node rbtree_node; +struct rbtree_node { + /* Key and Value */ + void *key,*value; + + /* Left and right nodes */ + rbtree_node *left,*right; + + /* Parent node */ + rbtree_node *parent; + + /* Node color */ + short color; +}; + +/* + * Description of a Red/Black tree. For commodity, a name can be given to the + * tree. "rbtree_comp" is a pointer to a function, defined by user, which + * compares keys during node operations. + */ +typedef struct rbtree_tree rbtree_tree; +struct rbtree_tree { + int node_count; /* Number of Nodes */ + mempool_t mp; /* Memory pool */ + rbtree_node nil; /* Sentinel */ + rbtree_node *root; /* Root node */ + tree_fcompare key_cmp; /* Key comparison function */ + void *opt_data; /* Optional data for comparison */ +}; + +/* Insert a node in an Red/Black tree */ +int rbtree_insert(rbtree_tree *tree,void *key,void *value); + +/* Removes a node out of a tree */ +void *rbtree_remove(rbtree_tree *tree,void *key); + +/* + * Lookup for a node corresponding to "key". If node does not exist, + * function returns null pointer. + */ +void *rbtree_lookup(rbtree_tree *tree,void *key); + +/* Call the specified function for each node */ +int rbtree_foreach(rbtree_tree *tree,tree_fforeach user_fn,void *opt); + +/* Compute the height of a Red/Black tree */ +int rbtree_height(rbtree_tree *tree); + +/* Returns the number of nodes */ +int rbtree_node_count(rbtree_tree *tree); + +/* Purge all nodes */ +void rbtree_purge(rbtree_tree *tree); + +/* Check tree consistency */ +int rbtree_check(rbtree_tree *tree); + +/* Create a new Red/Black tree */ +rbtree_tree *rbtree_create(tree_fcompare key_cmp,void *opt_data); + +/* Delete an Red/Black tree */ +void rbtree_delete(rbtree_tree *tree); + +#endif + diff --git a/astar_native_c_code/test_main.c b/astar_native_c_code/test_main.c new file mode 100644 index 0000000..43b1f48 --- /dev/null +++ b/astar_native_c_code/test_main.c @@ -0,0 +1,71 @@ + +#include +#include + +#include "priorityqueue.h" + +int int_cmp(const void *pa, const void *pb) { + int a = *((int*)pa); + int b = *((int*)pb); + int result = a == b ? 0 : (a < b ? -1 : 1); + return result; +} + +void test_pq_1() { + + pq_t *pq = pq_new(int_cmp); + + pq_push(pq, (void*)3); + pq_push(pq, (void*)1); + pq_push(pq, (void*)2); + pq_push(pq, (void*)0); + pq_push(pq, (void*)4); + + + assert(0 == (int)pq_pop(pq)); + assert(1 == (int)pq_pop(pq)); + assert(2 == (int)pq_pop(pq)); + assert(3 == (int)pq_pop(pq)); + assert(4 == (int)pq_pop(pq)); + assert(false == pq_has_len(pq)); + pq_free(pq); +} + +void test_pq_2() { + int size = 512; + pq_t *pq = pq_new(int_cmp); + + int *array1 = malloc(size* sizeof(int)); + int *array2 = malloc(size * sizeof(int)); + + + for(int i=0; i B with a distance of 100 the shortest path is A -> C -> D -> B with a distance of 60 diff --git a/src/test/london/London_Underground_Overground_DLR_Crossrail_map.svg b/tests/london/London_Underground_Overground_DLR_Crossrail_map.svg similarity index 100% rename from src/test/london/London_Underground_Overground_DLR_Crossrail_map.svg rename to tests/london/London_Underground_Overground_DLR_Crossrail_map.svg diff --git a/src/test/london/london_underground.py b/tests/london/__init__.py similarity index 76% rename from src/test/london/london_underground.py rename to tests/london/__init__.py index b63c9cb..ddde3cc 100644 --- a/src/test/london/london_underground.py +++ b/tests/london/__init__.py @@ -13,13 +13,13 @@ def __init__(self, id, name, position): self.name = name self.position = position self.links = [] + def __repr__(self): + return '<' + self.name + '>' - -def build_data(): +def build_data(cwd = os.path.dirname(__file__)): """builds the 'map' by reading the data files""" stations = {} - rootdir = os.path.dirname(os.path.abspath(sys.argv[0])) - r = csv.reader(open(os.path.join(rootdir, 'underground_stations.csv'))) + r = csv.reader(open(os.path.join(cwd, 'underground_stations.csv'))) next(r) # jump the first line for record in r: id = int(record[0]) @@ -28,7 +28,7 @@ def build_data(): name = record[3] stations[id] = Station(id, name, (lat, lon)) - r = csv.reader(open(os.path.join(rootdir, 'underground_routes.csv'))) + r = csv.reader(open(os.path.join(cwd, 'underground_routes.csv'))) next(r) # jump the first line for id1, id2, lineNumber in r: id1 = int(id1) @@ -37,8 +37,9 @@ def build_data(): stations[id2].links.append(stations[id1]) return stations +stations = build_data() -def get_station_by_name(stations, name): +def get_station_by_name(name): """lookup by name, the name does not have to be exact.""" name = name.lower() ratios = [(SequenceMatcher(None, name, v.name.lower()).ratio(), v) @@ -67,6 +68,13 @@ def distance(n1, n2): return astar.find_path(s1, s2, neighbors_fnct=lambda s: s.links, heuristic_cost_estimate_fnct=distance, distance_between_fnct=distance) +import unittest +class LondonTests(unittest.TestCase): + def runTest(self): + get_path(get_station_by_name("Chesham"), get_station_by_name("Beckton")) + get_path(get_station_by_name("Edgware"), get_station_by_name("New Addington")) + + if __name__ == '__main__': if len(sys.argv) != 3: @@ -74,11 +82,9 @@ def distance(n1, n2): 'Usage : {script} '.format(script=sys.argv[0])) sys.exit(1) - stations = build_data() - - station1 = get_station_by_name(stations, sys.argv[1]) + station1 = get_station_by_name(sys.argv[1]) print('Station 1 : ' + station1.name) - station2 = get_station_by_name(stations, sys.argv[2]) + station2 = get_station_by_name(sys.argv[2]) print('Station 2 : ' + station2.name) print('-' * 80) path = get_path(station1, station2) diff --git a/src/test/london/underground_lines.csv b/tests/london/underground_lines.csv similarity index 100% rename from src/test/london/underground_lines.csv rename to tests/london/underground_lines.csv diff --git a/src/test/london/underground_routes.csv b/tests/london/underground_routes.csv similarity index 100% rename from src/test/london/underground_routes.csv rename to tests/london/underground_routes.csv diff --git a/src/test/london/underground_stations.csv b/tests/london/underground_stations.csv similarity index 100% rename from src/test/london/underground_stations.csv rename to tests/london/underground_stations.csv diff --git a/src/test/maze/maze.py b/tests/maze/__init__.py similarity index 84% rename from src/test/maze/maze.py rename to tests/maze/__init__.py index a1a9126..815ae1e 100644 --- a/src/test/maze/maze.py +++ b/tests/maze/__init__.py @@ -2,6 +2,7 @@ from astar import AStar import sys import math +import unittest def make_maze(w=30, h=30): @@ -81,19 +82,21 @@ def neighbors(self, node): x, y = node return[(nx, ny) for nx, ny in[(x, y - 1), (x, y + 1), (x - 1, y), (x + 1, y)]if 0 <= nx < self.width and 0 <= ny < self.height and self.lines[ny][nx] == ' '] -# generate an ascii maze -size = 20 -m = make_maze(size, size) -# what is the size of it? -w = len(m.split('\n')[0]) -h = len(m.split('\n')) +def solve_maze(size = 20): + m = make_maze(size, size) + w = len(m.split('\n')[0]) + h = len(m.split('\n')) + start = (1, 1) # we choose to start at the upper left corner + goal = (w - 2, h - 2) # we want to reach the lower right corner + return list(MazeSolver(m).astar(start, goal)) + -start = (1, 1) # we choose to start at the upper left corner -goal = (w - 2, h - 2) # we want to reach the lower right corner +class TestMaze(unittest.TestCase): + def runTest(self): + foundPath = solve_maze() + self.assertTrue(len(foundPath) > 0) -# let's solve it -foundPath = list(MazeSolver(m).astar(start, goal)) +if __name__ == '__main__': + unittest.main() -# print the solution -print(drawmaze(m, list(foundPath))) diff --git a/src/test/tan_network/tan_network_5.py b/tests/tan_network/__init__.py similarity index 93% rename from src/test/tan_network/tan_network_5.py rename to tests/tan_network/__init__.py index 549f1b6..7f21e55 100644 --- a/src/test/tan_network/tan_network_5.py +++ b/tests/tan_network/__init__.py @@ -62,11 +62,11 @@ def solve_tan(in_stream): return ''.join([station.name + '\n' for station in path]) -class TanFinderTest(unittest.TestCase): +class TanFinderTests(unittest.TestCase): - def test_solveTan(self): + def runTest(self): + rootdir = os.path.dirname(__file__) self.maxDiff = None - rootdir = os.path.dirname(os.path.abspath(sys.argv[0])) with open(os.path.join(rootdir, 'tan_network_5.in.txt')) as inputFile: with open(os.path.join(rootdir, 'tan_network_5.out.txt')) as outputFile: output_data = outputFile.read() diff --git a/src/test/tan_network/tan_network_5.in.txt b/tests/tan_network/tan_network_5.in.txt similarity index 100% rename from src/test/tan_network/tan_network_5.in.txt rename to tests/tan_network/tan_network_5.in.txt diff --git a/src/test/tan_network/tan_network_5.out.txt b/tests/tan_network/tan_network_5.out.txt similarity index 100% rename from src/test/tan_network/tan_network_5.out.txt rename to tests/tan_network/tan_network_5.out.txt