This was an absolute mess to port from the Haskell version, but it should be equivalent to the original code.