Browse Source

Blindly ran 2to3 against all python files.

master
Pete Shadbolt 7 years ago
parent
commit
c9121351c6
15 changed files with 83 additions and 82 deletions
  1. +9
    -9
      abp/build_tables.py
  2. +5
    -5
      abp/clifford.py
  3. +12
    -12
      abp/graphstate.py
  4. +6
    -6
      abp/nxgraphstate.py
  5. +3
    -3
      abp/qi.py
  6. +4
    -3
      abp/stabilizer.py
  7. +5
    -5
      abp/vizclient.py
  8. +8
    -8
      tests/mock.py
  9. +12
    -12
      tests/test_against_anders_and_briegel.py
  10. +3
    -3
      tests/test_clifford.py
  11. +7
    -7
      tests/test_graphstate.py
  12. +1
    -1
      tests/test_measurement.py
  13. +1
    -1
      tests/test_mercedes.py
  14. +3
    -3
      tests/test_qi.py
  15. +4
    -4
      tests/test_stabilizer.py

+ 9
- 9
abp/build_tables.py View File

@@ -9,7 +9,7 @@ import itertools as it
from functools import reduce
from os.path import dirname, join, split
import json
import qi, clifford
from . import qi, clifford


DECOMPOSITIONS = (
@@ -52,8 +52,8 @@ def find_cz(bond, c1, c2, commuters, state_table):
target = qi.normalize_global_phase(target)

# Choose the sets to search over
s1 = commuters if c1 in commuters else xrange(24)
s2 = commuters if c2 in commuters else xrange(24)
s1 = commuters if c1 in commuters else range(24)
s2 = commuters if c2 in commuters else range(24)

# Find a match
for bondp, c1p, c2p in it.product([0, 1], s1, s2):
@@ -79,9 +79,9 @@ def get_unitaries():
def get_by_name(unitaries, conjugation_table):
""" Get a lookup table of cliffords by name """
a = {name: find_clifford(u, unitaries)
for name, u in qi.by_name.items()}
for name, u in list(qi.by_name.items())}
a.update({key + "_h": conjugation_table[value]
for key, value in a.items()})
for key, value in list(a.items())})
a.update({clifford.get_name(i): i for i in range(24)})
a.update({i: i for i in range(24)})
return a
@@ -101,7 +101,7 @@ def get_times_table(unitaries):
def get_state_table(unitaries):
""" Cache a table of state to speed up a little bit """
state_table = np.zeros((2, 24, 24, 4), dtype=complex)
params = list(it.product([0, 1], range(24), range(24)))
params = list(it.product([0, 1], list(range(24)), list(range(24))))
for bond, i, j in tqdm(params, desc="Building state table"):
state = qi.bond if bond else qi.nobond
kp = np.kron(unitaries[i], unitaries[j])
@@ -137,7 +137,7 @@ def get_measurement_table():
This is pretty unintelligible right now, we should probably compute the phase from unitaries instead
"""
measurement_table = np.zeros((4, 24, 2), dtype=int)
for operator, unitary in it.product(range(4), range(24)):
for operator, unitary in it.product(list(range(4)), list(range(24))):
measurement_table[operator, unitary] = get_measurement_entry(
operator, unitary)
return measurement_table
@@ -158,7 +158,7 @@ def get_cz_table(unitaries):
# And now build the CZ table
cz_table = np.zeros((2, 24, 24, 3), dtype=int)
rows = list(
it.product([0, 1], it.combinations_with_replacement(range(24), 2)))
it.product([0, 1], it.combinations_with_replacement(list(range(24)), 2)))
# CZ is symmetric so we only need combinations
for bond, (c1, c2) in tqdm(rows, desc="Building CZ table"):
newbond, c1p, c2p = find_cz(
@@ -174,7 +174,7 @@ def get_display_table(unitaries):
c = qi.CircuitModel(1)
c.act_local_rotation(0, u)
state = c.state.round(2)
print "{:.2f}, {:.2f}".format(state[0][0], state[1][0])
print("{:.2f}, {:.2f}".format(state[0][0], state[1][0]))


def compute_everything():


+ 5
- 5
abp/clifford.py View File

@@ -40,7 +40,7 @@ The complete set of aliases for single-qubit Cliffords is as follows:

"""

from tables import *
from .tables import *

# Aliases
identity = by_name["identity"]
@@ -58,7 +58,7 @@ def conjugate(operator, unitary):
def use_old_cz():
""" Use the CZ lookup table from A&B's code, rather than our own. Useful for testing. """
global cz_table
from anders_cz import cz_table
from .anders_cz import cz_table

def get_name(i):
""" Get the name of this clifford """
@@ -66,7 +66,7 @@ def get_name(i):

def human_name(i):
""" Get the human-readable name of this clifford - slow """
choices = sorted((key for key, value in by_name.items() if value == i), key=len)
choices = sorted((key for key, value in list(by_name.items()) if value == i), key=len)
return choices[-1]

def is_diagonal(v):
@@ -78,9 +78,9 @@ if __name__ == '__main__':
from itertools import groupby

for i in range(24):
members = [key for key, value in by_name.items() if value == i and str(key)!=str(i)]
members = [key for key, value in list(by_name.items()) if value == i and str(key)!=str(i)]
members = sorted(members, key=len)
print "* {}: {}".format(i, ", ".join(members))
print("* {}: {}".format(i, ", ".join(members)))



+ 12
- 12
abp/graphstate.py View File

@@ -6,9 +6,9 @@ This module implements Anders and Briegel's method for fast simulation of Cliffo

import itertools as it
import json, random
import qi, clifford, util
from . import qi, clifford, util
import abp
from stabilizer import Stabilizer
from .stabilizer import Stabilizer


class GraphState(object):
@@ -30,7 +30,7 @@ class GraphState(object):
# Cloning from a networkx graph
self.adj = data.adj.copy()
self.node = data.node.copy()
for key, value in self.node.items():
for key, value in list(self.node.items()):
self.node[key]["vop"] = data.node[
key].get("vop", clifford.identity)
except AttributeError:
@@ -66,7 +66,7 @@ class GraphState(object):
By default, nodes are initialized with ``vop=``:math:`I`, i.e. they are in the :math:`|+\\rangle` state.
"""
if node in self.node:
print "Warning: node {} already exists".format(node)
print("Warning: node {} already exists".format(node))
return

default = kwargs.get("default", "identity")
@@ -141,7 +141,7 @@ class GraphState(object):
def edgelist(self):
""" Describe a graph as an edgelist # TODO: inefficient """
edges = set(tuple(sorted((i, n)))
for i, v in self.adj.items()
for i, v in list(self.adj.items())
for n in v)
return tuple(edges)

@@ -305,7 +305,7 @@ class GraphState(object):
"""
forces = forces if forces != None else [
random.choice([0, 1]) for i in range(len(measurements))]
measurements = zip(measurements, forces)
measurements = list(zip(measurements, forces))
results = []
for (node, basis), force in measurements:
result = self.measure(node, basis, force, detail)
@@ -333,9 +333,9 @@ class GraphState(object):
if abp.DETERMINISTIC:
friend = sorted(self.adj[node].keys())[0]
else:
friend = next(self.adj[node].iterkeys())
friend = next(iter(self.adj[node].keys()))
else:
assert friend in self.adj[node].keys() # TODO: unnecessary assert
assert friend in list(self.adj[node].keys()) # TODO: unnecessary assert

# Update the VOPs. TODO: pretty ugly
if result:
@@ -427,9 +427,9 @@ class GraphState(object):

"""
if stringify:
node = {str(key): value for key, value in self.node.items()}
adj = {str(key): {str(key): value for key, value in ngbh.items()}
for key, ngbh in self.adj.items()}
node = {str(key): value for key, value in list(self.node.items())}
adj = {str(key): {str(key): value for key, value in list(ngbh.items())}
for key, ngbh in list(self.adj.items())}
return {"node": node, "adj": adj}
else:
return {"node": self.node, "adj": self.adj}
@@ -460,7 +460,7 @@ class GraphState(object):
state.act_hadamard(mapping[n])
for i, j in self.edgelist():
state.act_cz(mapping[i], mapping[j])
for i, n in self.node.items():
for i, n in list(self.node.items()):
state.act_local_rotation(mapping[i], clifford.unitaries[n["vop"]])
return state



+ 6
- 6
abp/nxgraphstate.py View File

@@ -1,8 +1,8 @@
import networkx as nx
import numpy as np
import graphstate
import clifford
import util
from . import graphstate
from . import clifford
from . import util

class NXGraphState(graphstate.GraphState, nx.Graph):
""" This is GraphState with NetworkX-like abilities """
@@ -12,8 +12,8 @@ class NXGraphState(graphstate.GraphState, nx.Graph):
def layout(self):
""" Automatically lay out the graph """
pos = nx.spring_layout(self, dim=3, scale=np.sqrt(self.order()))
middle = np.average(pos.values(), axis=0)
pos = {key: value - middle for key, value in pos.items()}
for key, (x, y, z) in pos.items():
middle = np.average(list(pos.values()), axis=0)
pos = {key: value - middle for key, value in list(pos.items())}
for key, (x, y, z) in list(pos.items()):
self.node[key]["position"] = util.xyz(x, y, z)


+ 3
- 3
abp/qi.py View File

@@ -52,7 +52,7 @@ nobond = np.kron(plus, plus)
# Labelling stuff
common_us = id, px, py, pz, ha, ph, sqz, msqz, sqy, msqy, sqx, msqx
names = "identity", "px", "py", "pz", "hadamard", "phase", "sqz", "msqz", "sqy", "msqy", "sqx", "msqx"
by_name = dict(zip(names, common_us))
by_name = dict(list(zip(names, common_us)))

paulis = px, py, pz
operators = id, px, py, pz
@@ -60,7 +60,7 @@ operators = id, px, py, pz

def normalize_global_phase(m):
""" Normalize the global phase of a matrix """
v = (x for x in m.flatten() if np.abs(x) > 0.001).next()
v = next((x for x in m.flatten() if np.abs(x) > 0.001))
phase = np.arctan2(v.imag, v.real) % np.pi
rot = np.exp(-1j * phase)
return rot * m if rot * v > 0 else -rot * m
@@ -78,7 +78,7 @@ class CircuitModel(object):
""" Act a CU somewhere. """
control = 1 << control
target = 1 << target
for i in xrange(self.d):
for i in range(self.d):
if (i & control) and (i & target):
self.state[i, 0] *= -1



+ 4
- 3
abp/stabilizer.py View File

@@ -31,15 +31,16 @@ class Stabilizer(object):
""" For comparison with old A&B code """
m = {1: 0, 1j:1, -1: 2, -1j: 3}
return {"paulis": self.tableau,
"phases": {key: m[value] for key, value in self.phases.items()}}
"phases": {key: m[value] for key, value in list(self.phases.items())}}

def __getitem__(self, (i, j)):
def __getitem__(self, xxx_todo_changeme):
"""" Pass straight through to the dictionary """
(i, j) = xxx_todo_changeme
return self.tableau[i][j]

def __str__(self):
""" Represent as a string """
keys = map(str, self.tableau.keys())
keys = list(map(str, list(self.tableau.keys())))
w = max(len(k) for k in keys)
keys = [k.ljust(w) for k in keys]
s = " {}\n".format(" ".join(map(str, keys)))


+ 5
- 5
abp/vizclient.py View File

@@ -3,9 +3,9 @@ import networkx as nx
import numpy as np
import websocket
from socket import error as socket_error
import clifford
import util
import nxgraphstate
from . import clifford
from . import util
from . import nxgraphstate

class VizClient(object):
def __init__(self, uri = "ws://localhost:5000"):
@@ -21,7 +21,7 @@ class VizClient(object):
g = nxgraphstate.NXGraphState(graph)

# Automatically perform layout if position is not provided
if not all(("position" in node) for node in g.node.values()):
if not all(("position" in node) for node in list(g.node.values())):
g.layout()

# Send data to browser and rate-limit
@@ -29,7 +29,7 @@ class VizClient(object):
self.ws.send(json.dumps(g.to_json(stringify=True)))
self.ws.recv()
except websocket._exceptions.WebSocketTimeoutException:
print "Timed out ... you might be pushing a bit hard"
print("Timed out ... you might be pushing a bit hard")
time.sleep(delay)



+ 8
- 8
tests/mock.py View File

@@ -21,7 +21,7 @@ class AndersWrapper(graphsim.GraphRegister):
""" A wrapper for A&B to make the interface identical and enable equality testing """

def __init__(self, nodes):
assert list(nodes) == range(len(nodes))
assert list(nodes) == list(range(len(nodes)))
super(AndersWrapper, self).__init__(len(nodes))

def act_local_rotation(self, qubit, operation):
@@ -58,7 +58,7 @@ class ABPWrapper(GraphState):
super(ABPWrapper, self).__init__(nodes, vop="hadamard")

def print_stabilizer(self):
print self.to_stabilizer()
print(self.to_stabilizer())

def __eq__(self, other):
return self.to_json() == other.to_json()
@@ -67,7 +67,7 @@ class ABPWrapper(GraphState):
class CircuitModelWrapper(qi.CircuitModel):

def __init__(self, nodes=[]):
assert list(nodes) == range(len(nodes))
assert list(nodes) == list(range(len(nodes)))
super(CircuitModelWrapper, self).__init__(len(nodes))

def act_circuit(self, circuit):
@@ -82,19 +82,19 @@ class CircuitModelWrapper(qi.CircuitModel):

def random_pair(n):
""" Helper function to get random pairs"""
return tuple(random.choice(range(n), 2, replace=False))
return tuple(random.choice(list(range(n)), 2, replace=False))


def random_graph_circuit(n=10, depth=100):
""" A random Graph state. """
return [(i, "hadamard") for i in xrange(n)] + \
[(random_pair(n), "cz") for i in xrange(depth)]
return [(i, "hadamard") for i in range(n)] + \
[(random_pair(n), "cz") for i in range(depth)]


def random_stabilizer_circuit(n=10, depth=100):
""" Generate a random stabilizer state, without any VOPs """
return random_graph_circuit(n, depth) + \
[(i, random.choice(range(24))) for i in range(n)]
[(i, random.choice(list(range(24)))) for i in range(n)]


def bell_pair():
@@ -122,7 +122,7 @@ def simple_graph():

def circuit_to_state(Base, n, circuit):
""" Convert a circuit to a state, given a base class """
g = Base(range(n))
g = Base(list(range(n)))
g.act_circuit(circuit)
return g



+ 12
- 12
tests/test_against_anders_and_briegel.py View File

@@ -17,48 +17,48 @@ def test_hadamard():

def test_local_rotations():
""" Test local rotations """
for i in tqdm(range(REPEATS), "Testing local rotations"):
circuit = [(0, random.choice(range(24))) for j in range(DEPTH)]
for i in tqdm(list(range(REPEATS)), "Testing local rotations"):
circuit = [(0, random.choice(list(range(24)))) for j in range(DEPTH)]
mock.test_circuit(circuit, 1)


def test_times_table():
""" Test times table """
for i, j in it.product(range(24), range(24)):
for i, j in it.product(list(range(24)), list(range(24))):
circuit = [(0, i), (0, j)]
mock.test_circuit(circuit, 1)


def test_cz_table():
""" Test the CZ table """
for i, j in it.product(range(24), range(24)):
for i, j in it.product(list(range(24)), list(range(24))):
circuit = [(0, i), (1, j), ((0, 1), "cz")]
mock.test_circuit(circuit, 2)


def test_cz_hadamard(n=10):
""" Test CZs and Hadamards at random """
for i in tqdm(range(REPEATS), desc="Testing CZ and Hadamard against A&B"):
for i in tqdm(list(range(REPEATS)), desc="Testing CZ and Hadamard against A&B"):
circuit = random.choice(["cz", "hadamard"], DEPTH)
circuit = [(mock.random_pair(n), gate) if gate == "cz"
else (random.choice(range(n)), gate)
else (random.choice(list(range(n))), gate)
for gate in circuit]
mock.test_circuit(circuit, n)


def test_all(n=10):
""" Test everything """
for i in tqdm(range(REPEATS), desc="Testing CZ and Hadamard against A&B"):
circuit = random.choice(["cz"] * 10 + range(24), DEPTH)
for i in tqdm(list(range(REPEATS)), desc="Testing CZ and Hadamard against A&B"):
circuit = random.choice(["cz"] * 10 + list(range(24)), DEPTH)
circuit = [(mock.random_pair(n), gate) if gate == "cz"
else (random.choice(range(n)), gate)
else (random.choice(list(range(n))), gate)
for gate in circuit]
mock.test_circuit(circuit, n)


def test_single_qubit_measurement():
""" Determinstic test of all single-qubit situations """
space = it.product(range(24), PAULIS, (0, 1))
space = it.product(list(range(24)), PAULIS, (0, 1))
for rotation, measurement, outcome in tqdm(space, "Testing single qubit measurements"):
a = mock.circuit_to_state(mock.ABPWrapper, 1, [(0, rotation)])
b = mock.circuit_to_state(mock.AndersWrapper, 1, [(0, rotation)])
@@ -79,7 +79,7 @@ def test_two_qubit_measurement():
def test_graph_state_measurement(n = 10):
""" Measuring random graph states """
space = list(it.product(range(REPEATS), PAULIS, (0, 1)))
space = list(it.product(list(range(REPEATS)), PAULIS, (0, 1)))
for i, measurement, outcome in tqdm(space, "Measuring random graph states"):
circuit = mock.random_graph_circuit(n, DEPTH)
a = mock.circuit_to_state(mock.ABPWrapper, n, circuit)
@@ -90,7 +90,7 @@ def test_graph_state_measurement(n = 10):

def test_stabilizer_state_measurement(n = 10):
""" Measuring random stabilizer states """
space = list(it.product(range(REPEATS), PAULIS, (0, 1)))
space = list(it.product(list(range(REPEATS)), PAULIS, (0, 1)))
for i, measurement, outcome in tqdm(space, "Measuring random stabilizer states"):
circuit = mock.random_stabilizer_circuit(n, DEPTH)
a = mock.circuit_to_state(mock.ABPWrapper, n, circuit)


+ 3
- 3
tests/test_clifford.py View File

@@ -45,7 +45,7 @@ def test_we_have_24_matrices():

def test_we_have_all_useful_gates():
""" Check that all the interesting gates are included up to a global phase """
for name, u in qi.by_name.items():
for name, u in list(qi.by_name.items()):
build_tables.find_clifford(u, clifford.unitaries)


@@ -85,7 +85,7 @@ def test_conjugation():
raise nose.SkipTest("Original C++ is not available, skipping test")

for operation_index, transform_index in it.product(range(4), range(24)):
for operation_index, transform_index in it.product(list(range(4)), list(range(24))):
transform = graphsim.LocCliffOp(transform_index)
operation = graphsim.LocCliffOp(operation_index)

@@ -103,7 +103,7 @@ def test_cz_table():
""" Does the CZ code work good? """
state_table = build_tables.get_state_table(clifford.unitaries)

rows = it.product([0, 1], it.combinations_with_replacement(range(24), 2))
rows = it.product([0, 1], it.combinations_with_replacement(list(range(24)), 2))

for bond, (c1, c2) in rows:



+ 7
- 7
tests/test_graphstate.py View File

@@ -65,9 +65,9 @@ def test_edgelist():
def test_stress(n=int(1e5)):
""" Testing that making a graph of ten thousand qubits takes less than half a second"""
import time
g = GraphState(range(n + 1), vop="hadamard")
g = GraphState(list(range(n + 1)), vop="hadamard")
t = time.clock()
for i in xrange(n):
for i in range(n):
g._add_edge(i, i + 1)
assert time.clock() - t < .5

@@ -93,7 +93,7 @@ def test_czs():
def test_local_complementation():
""" Test that local complementation works okay """
pairs = (0, 1), (0, 3), (1, 3), (1, 2),
psi = GraphState(range(4), vop="hadamard")
psi = GraphState(list(range(4)), vop="hadamard")
psi.act_circuit([("hadamard", i) for i in psi.node])
psi.act_circuit([("cz", pair) for pair in pairs])
old_edges = psi.edgelist()
@@ -105,8 +105,8 @@ def test_local_complementation():

def test_single_qubit():
""" A multi qubit test with Hadamards only"""
for repeat in tqdm(range(REPEATS), desc="Single qubit rotations against CircuitModel"):
circuit = [(0, random.choice(range(24))) for i in range(DEPTH)]
for repeat in tqdm(list(range(REPEATS)), desc="Single qubit rotations against CircuitModel"):
circuit = [(0, random.choice(list(range(24)))) for i in range(DEPTH)]
a = mock.circuit_to_state(mock.ABPWrapper, 1, circuit)
b = mock.circuit_to_state(mock.CircuitModelWrapper, 1, circuit)
assert a.to_state_vector() == b
@@ -114,7 +114,7 @@ def test_single_qubit():

def test_graph_state_multiqubit(n=6):
""" A multi qubit test with Hadamards only"""
for repeat in tqdm(range(REPEATS), desc="Random graph states against the CircuitModel"):
for repeat in tqdm(list(range(REPEATS)), desc="Random graph states against the CircuitModel"):
circuit = mock.random_graph_circuit(n)
a = mock.circuit_to_state(mock.ABPWrapper, n, circuit)
b = mock.circuit_to_state(mock.CircuitModelWrapper, n, circuit)
@@ -123,7 +123,7 @@ def test_graph_state_multiqubit(n=6):

def test_stabilizer_state_multiqubit(n=6):
""" A multi qubit test with arbitrary local rotations """
for repeat in tqdm(range(REPEATS), desc="Random Clifford circuits against the CircuitModel"):
for repeat in tqdm(list(range(REPEATS)), desc="Random Clifford circuits against the CircuitModel"):
circuit = mock.random_stabilizer_circuit(n)
a = mock.circuit_to_state(mock.ABPWrapper, n, circuit)
b = mock.circuit_to_state(mock.CircuitModelWrapper, n, circuit)


+ 1
- 1
tests/test_measurement.py View File

@@ -29,7 +29,7 @@ def test_single_qubit_measurements():

def test_type():
""" Test that the output is always an int """
for r, m, f in it.product(range(24), ("px", "py", "pz"), (0, 1)):
for r, m, f in it.product(list(range(24)), ("px", "py", "pz"), (0, 1)):
g = GraphState([0], vop="hadamard")
g.act_local_rotation(0, r)
assert str(g.measure(0, m)) in "01"


+ 1
- 1
tests/test_mercedes.py View File

@@ -3,7 +3,7 @@ from abp.util import xyz
from mock import simple_graph

def linear_cluster(n):
g = GraphState(range(n), vop="hadamard")
g = GraphState(list(range(n)), vop="hadamard")
g.act_circuit([("hadamard", i) for i in range(n)])
g.act_circuit([("cz", (i, i+1)) for i in range(n-1)])
return g


+ 3
- 3
tests/test_qi.py View File

@@ -135,7 +135,7 @@ def test_against_chp(n=5):
ket = chp.get_ket()
nonzero = np.sqrt(len(ket))
output.state[0, 0] = 0
for key, phase in ket.items():
for key, phase in list(ket.items()):
output.state[key] = np.exp(1j * phase * np.pi / 2) / nonzero
return output

@@ -151,7 +151,7 @@ def test_against_chp(n=5):
# Run a random circuit
chp.init(n)
psi = qi.CircuitModel(n)
for i in tqdm(range(DEPTH), "Testing CircuitModel against CHP"):
for i in tqdm(list(range(DEPTH)), "Testing CircuitModel against CHP"):
if np.random.rand() > .5:
a = np.random.randint(0, n - 1)
chp.act_hadamard(a)
@@ -166,7 +166,7 @@ def test_against_chp(n=5):
def test_sqrt_notation(n=2):
""" Test that SQRT notation looks nice """
c = mock.random_stabilizer_circuit(n)
g = GraphState(range(n))
g = GraphState(list(range(n)))
g.act_circuit(c)

def test_indexint():


+ 4
- 4
tests/test_stabilizer.py View File

@@ -7,13 +7,13 @@ REPEATS = 1000
def test_stabilizers_against_anders_and_briegel(n=10):
""" Test that stabilizers are line-for-line equivalent """
for i in tqdm(range(REPEATS), "Stabilizer representation VS A&B"):
for i in tqdm(list(range(REPEATS)), "Stabilizer representation VS A&B"):
c = mock.random_stabilizer_circuit(n)
g = mock.AndersWrapper(range(n))
g = mock.AndersWrapper(list(range(n)))
g.act_circuit(c)
da = g.get_full_stabilizer().to_dictionary()

g = mock.ABPWrapper(range(n))
g = mock.ABPWrapper(list(range(n)))
g.act_circuit(c)
db = g.to_stabilizer().to_dictionary()

@@ -21,4 +21,4 @@ def test_stabilizers_against_anders_and_briegel(n=10):

def test_stabilizer_access():
g = GraphState(3)
print g.to_stabilizer()[0, 0]
print(g.to_stabilizer()[0, 0])

Loading…
Cancel
Save