Almost final version. Small bugs may exist.