more general implementation