ported from asimov.js