Improved, generalised version.