refactor for more general usage; add a few tests