final version with tests