algorithm is a little complicated, but it works