generalized and cleaned up