Rewritten from scratch
add tests