#Importing all the necessary libraries
!pip install qiskit matplotlib

import qiskit
import matplotlib

from qiskit import QuantumCircuit, assemble, Aer, circuit, QuantumRegister, ClassicalRegister
from qiskit.visualization import plot_histogram
from qiskit.quantum_info.operators import Operator
from qiskit.extensions import UnitaryGate

hash_gate = Operator([
    [0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0],
    [0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0],
    [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0],
    [0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
])

hash_gate.name = "Hash Gate"
hash_dagger = hash_gate.conjugate().transpose()
hash_gate.name = "Hash Dagger"

#Testing input 0110 -> should get 1000
n_qubits = 4
controls = QuantumRegister(n_qubits)
outputs = ClassicalRegister(n_qubits)
circuit = QuantumCircuit(controls, outputs)

circuit.x(controls[1:3])
circuit.draw()

circuit.append(hash_gate, [0,1,2,3])
circuit.barrier(controls)

circuit.measure(controls, outputs)
circuit.draw()

sim = Aer.get_backend('aer_simulator') 
result = sim.run(circuit).result()
counts = result.get_counts()
plot_histogram(counts)

n_qubits = 4
controls = QuantumRegister(n_qubits)
outputs = ClassicalRegister(n_qubits)
circuit = QuantumCircuit(controls, outputs)

circuit.x(controls[1:3])
circuit.draw()

circuit.append(hash_gate, [0,1,2,3])
circuit.append(hash_dagger, [0,1,2,3])
circuit.barrier(controls)

circuit.measure(controls, outputs)
circuit.draw()

# We begin by declaring a simulator for our circuit to run on
sim = Aer.get_backend('aer_simulator') 
# We run the simulator with sim.run(QUANTUM CIRCUIT),
# And we get the resulting values with .result()
result = sim.run(circuit).result()
# We then collect the results using .get_counts()
counts = result.get_counts()
# Visualization
plot_histogram(counts)