refactor quantum gates into separate classes

instead of having a single gate class, make each gate its own
class

force gates to implement various methods using the gate interface
fix broken tests after gate refactor