split into two implementations (vertical and horizontal) to avoid bloat