this is hard
almost complete, without testing