rewritten from scratch, fixed infinite loop bug, added tests