Reverse lookup dict of tuples, or use a different data structure? - python

I currently have a dictionary of tuples like this:
d = {
0: ('f', 'farm'),
1: ('m', 'mountain'),
2: ('h', 'house'),
3: ('t', 'forest'),
4: ('d', 'desert')
}
It's been working fine until I realized that I need to be able to do a reverse lookup, so given 'f' return 0, or given 'm' return 1
I know that's possible here by creating lists of the keys and values in the dict and cross-referencing them to find the position of the key, but that seems counter productive. I was wondering if there's a different data structure that would be better suited.
All the relationships here are one-to-one. 0 will always map to f, and f will always map to 0

There are already similar questions that you could use as a starting point.
How to implement an efficient bidirectional hash table?
Reverse / invert a dictionary mapping
In your specific case, the dict in its current form may be pointless. Dicts with integer keys starting from zero are just inefficient lists, but lists already have a reverse lookup method called index.
So what you could do is:
places_order = ['f', 'm', 'h', 't', 'd']
and then use places_order.index(some_letter) to get the corresponding integer.
This is an O(n) operation, so I'm assuming you don't need to perform these lookups millions of times with high performance. (Otherwise, especially if the real places_order is a long list, consider dict(zip(places_order, range(len(places_order)))).)
In addition, you could keep a map for the abbreviations of place names.
abbreviations = {
'f': 'farm',
'm': 'mountain',
'h': 'house',
't': 'forest',
'd': 'desert'
}
It's hard to say more without knowing any specifics about what you are trying to achieve.

A most straightforward search would be:
def search(dictionary, word):
for key,value in dictionary.items():
if word in value:
return key
This could then be used as:
>>> search(d, 'h')
2

Not sure I fully understand the use-case, but if you are not looking for a built-in python what about pandas.Series:
import pandas as pd
d = pd.Series(['farm', 'mountain', 'house', 'forest', 'desert'],index = ['f', 'm', 'h', 't', 'd'])
so both d[0] and d['f'] will output 'farm', d.index[0] will give 'f' and d.index.get_loc('f') will give 0.
For the case of built-ins see #timgeb's answer or consider a sort of namedtuple.

Here is another approach:
d = {
0: ('f', 'farm'),
1: ('m', 'mountain'),
2: ('h', 'house'),
3: ('t', 'forest'),
4: ('d', 'desert')
}
for key, value in d.items():
if 'h' in value:
print (key)
break

You can use a dict like this:
abs = {
'f': 'farm',
'm': 'mountain',
'h': 'house',
't': 'forest',
'd': 'desert'
}
when you need to reverse loop:
for i, (key, value) in enumerate(abs.items()):
print(i, key, value) # 0 f farm
when you want items by index:
list(abs.items())[i] # ('f', 'farm')

Related

Replace symbols in a string with conflicting keys in a dictionary

I need a translator, that have a dictionary with keys like
's': 'd'
and
'sch': 'b'
.
That's a rough example, but the point is, when i have an input word like "schto", it needs to replace it as "bkr", substitute 'sch' to 'b'. BUT there are the key 's', thus it translates the word as "dnokr", leave out and never lookup for 'sch', because there the key with the symbol 's' and it translates it first before 'sch'. What is a workaround here to replace the input word with the key 'sch' first, not with separate 's', 'c', and 'h'?
Here is the example of the code.
newdict = {'sch': 'b', 'sh': 'q', 'ch': 'w', 's': 'd', 'c': 'n', 'h': 'o', 't': 'k', 'o': 'r'}
code = input("Type: ")
code = "".join([newdict[w] for w in code])
print(code)
Regular expressions are greedy by default. If you're using a version of Python in which the insertion-order of key-value pairs in a dictionary are guaranteed, and you insert the key-value pairs in such a way that the longer ones come first, something like this should work for you - re.sub takes either a string with which to replace a match, or it takes a callable (function/lambda/whatever), which accepts the current match as an argument, and must return a string with which to replace it:
import re
lookup = {
"sch": "b",
"sh": "q",
"s": "d"
}
def replace(match):
return lookup[match.group()]
pattern = "|".join(lookup)
print(re.sub(pattern, replace, "schush swim"))
Output:
buq dwim
>>>
If you are using Python version 3.4+, then dictionary maintain the insertions order of keys. And hence you can achieve this using str.replace() while iterating over dict.items().
It'll recursively update the strings based on mapping. For example, if 'h' is replaced by 'o', then 'o' will be replaced by 'r'.
newdict = {'sch': 'b', 'sh': 'q', 'ch': 'w', 's': 'd', 'c': 'n', 'h': 'o', 't': 'k', 'o': 'r'}
my_word = "schto"
for k, v in newdict.items():
my_word = my_word.replace(k, v)
where my_word will give you your desired string as 'bkr'.
Here, since the dict.items() maintains the insertion order, keys which are defined first will be executed first during the iteration. Hence, you can define the priority of your rules by defining the keys you want to give precedence by declaring them before the other keys.

Python: How do I consistently find the next index after another index in a list with duplicates?

Sorry if the question wasn't clear enough, I am very new to python. I also apologize in advance if there are any typos in my code.
Say I have a list
list = [a,b,c,a,x,y,b,m,a,z]
And I want to get the index value of the element after each 'a' using a for loop and store it in a dict. (This assumes dict = {} already exists)
for store in list:
if dict.has_key(store) == False:
if list.index(store) != len(list)-1:
dict[store] = []
dict[store].append(list[list.index(store)+1])
else:
if list.index(store) != len(list)-1:
dict[store].append(list[list.index(store)+1])
Now ideally, I would want my dict to be
dict = {'a':['b','x','z'], 'b':['c','m'], 'c':['a']....etc.}
Instead, I get
dict = {'a':['b','b','b'], 'b':['c','c'], 'c':['a']...etc.}
I realized this is because index only finds the first occurrence of variable store. How would I structure my code so that for every value of store I can find the next index of that specific value instead of only the first one?
Also, I want to know how to do this only using a for loop; no recursions or while, etc (if statements are fine obviously).
I apologize again if my question isn't clear or if my code is messy.
You can do it like that:
l = ['a','b','c','a','x','y','b','m','a','z']
d={}
for i in range(len(l)-1):
if not l[i] in d:
d[l[i]] = []
d[l[i]].append(l[i+1])
Then d is
{'a': ['b', 'x', 'z'],
'b': ['c', 'm'],
'c': ['a'],
'm': ['a'],
'x': ['y'],
'y': ['b']}
Regarding your code, there is no need to use index, as you already enumerating over the list, so you do not need to search for the place of the current element. Also, you can just enumerate until len(l)-1, which simplifies the code. The problem in your code was that list.index(store) always finds the first appearance of store in list.
This looks like a job for defaultdict. Also, you should avoid using list and dict as variables since they are reserved words.
from collections import defaultdict
# create a dictionary that has default value of an empty list
# for any new key
d = defaultdict(list)
# create the list
my_list = 'a,b,c,a,x,y,b,m,a,z'.split(',')
# create tuples of each item with its following item
for k,v in zip(my_list, my_list[1:]):
d[k].append(v)
d
# returns:
defaultdict(list,
{'a': ['b', 'x', 'z'],
'b': ['c', 'm'],
'c': ['a'],
'm': ['a'],
'x': ['y'],
'y': ['b']})

How can i define a switch-case with several variables being the same? [duplicate]

In Python, I need a dictionary object which looks like:
{'a': 10, 'b': 20, 'c': 10, 'd': 10, 'e': 20}
I've been able to get this successfully by combining the dict.update() and dict.fromkeys() functions like so:
myDict = {}
myDict.update(dict.fromkeys(['a', 'c', 'd'], 10))
myDict.update(dict.fromkeys(['b', 'e'], 20))
However, because the code is being written for novice users who may need to make add keys/values on occasion, I'd prefer a simple bare-bones (Perl-like) syntax such as:
myDict = {}
myDict['a', 'c', 'd'] = 10
myDict['b', 'e'] = 20
This, however, gives me:
myDict = {('a', 'c', 'd'): 10, ('b', 'e'): 20}
Is there a way I can simplify my first example (using dict.update() and dict.fromkeys()) further, and get the dict object I'm looking for?
Or, alternatively, if I have a dict with tuples as in my second example, is there an easy way for me to do a lookup such as myDict['c'] or myDict.get('c') and get the value 10?
I would say what you have is very simple, you could slightly improve it to be:
my_dict = dict.fromkeys(['a', 'c', 'd'], 10)
my_dict.update(dict.fromkeys(['b', 'e'], 20))
If your keys are tuple you could do:
>>> my_dict = {('a', 'c', 'd'): 10, ('b', 'e'): 20}
>>> next(v for k, v in my_dict.items() if 'c' in k) # use .iteritems() python-2.x
10
This is, of course, will return first encountered value, key for which contains given element.
Similar to #SilentGhost but a more declarative syntax (with Python 3.5+) I prefer:
myDict = {
**dict.fromkeys(['a', 'c', 'd'], 10),
**dict.fromkeys(['b', 'e'], 20)
}
Your first example can be simplified using a loop:
myDict = {}
for key in ['a', 'c', 'd']:
myDict[key] = 10
for key in ['b', 'e']:
myDict[key] = 20
No specialized syntax or trickery, and I can't think of anything which would be easier to understand.
Regarding your second question, there is no simple and efficient way to do the lookup as in your second example. I can only think of iterating over the keys (tuples) and checking whether the key is in any of them, which isn't what you're looking for. Stick to using a straightforward dict with the keys you want.
In general, if you are aiming for code that can be understood by novices, stick to the basics such as if conditions and for/while loops.
Dict union (3.9+)
Now with Python 3.9, you can do this:
myDict = dict.fromkeys(['a', 'c', 'd'], 10) | dict.fromkeys(['b', 'e'], 20)
Although personally, I'm not sure I would use this, since it's hard to read.
Dict comprehension
myDict = {
k: v
for keys, v in [(['a', 'c', 'd'], 10), (['b', 'e'], 20)]
for k in keys
}
This is also hard to read, but I'm mentioning it for the sake of completeness.
reference
You could inherit from dict to implement a sort of "update from keys":
class LazyDict(dict):
def keylist(self, keys, value):
for key in keys:
self[key] = value
>>> d = LazyDict()
>>> d.keylist(('a', 'b', 'c'), 10)
>>> d
{'a': 10, 'c': 10, 'b': 10}
but I prefer loop solution
Method:
def multi_key_dict_get(d, k):
for keys, v in d.items():
if k in keys:
return v
return None
Usage:
my_dict = {
('a', 'b', 'c'): 10,
('p', 'q', 'r'): 50
}
value = multi_key_dict_get(my_dict, 'a')
While #SilentGhost's answer works pretty fine with single length of keys, it won't work correctly for those looking for a "multiple character length of keys" solution, and so I've thought of the below solution [...]
let's say that we have the above dict and keys we are looking for:
my_dict = {
'key1':'KEY_1',
('tk1', 'tk2','tk3'):'TK_1_2_3',
'key2':'KEY_2'
}
my_keys = ['key2','ke', 'tk2','k','key','exception'] # key's I'm looking for
the example & SOLUTION below:
for key in my_keys:
print(next((v for k, v in my_dict.items() if (key == k) or (isinstance(k,tuple) and key in k)),None))
CORRECTLY outputs:
KEY_2
None
TK_1_2_3
None
None
None
While with (a slightly modified solution [so it won't throw StopIteration exception] of) #SilentGhost's answer
for key in my_keys:
print(next((v for k, v in my_dict.items() if key in k),None)) # added ((...),None)
the results are WRONG because [...]2 if not a StopIteration exception:
KEY_2
KEY_1
TK_1_2_3
KEY_1
KEY_1
None
While personally I wouldn't really recommend it from a perspective of speed efficiency (at least not for all use cases), it is indeed a way of solving this issue and so I decided to post it.
class MyDict(dict):
def __setitem__(self,keys,value):
if type(keys)!=tuple:keys=(keys,)
for key in keys:super().__setitem__(key,value)
myDict = MyDict()
myDict['a', 'c', 'd'] = 10
myDict['b', 'e'] = 20
print(myDict) # {'a': 10, 'c': 10, 'd': 10, 'b': 20, 'e': 20}

Python - using dictionary to count keys and values

I am a student in a python course where we created a list of tuples (containing 2 elements) that we're trying to manipulate in various ways. In addition, we are to convert those tuple elements into a dictionary and re-create the manipulations using the dictionary and avoiding for loops. The task I'm stuck on is that given a specific id (which could be a key OR value in the dictionary) the function returns all the other keys/values that are found in that dictionary.
It doesn't seem efficient to use a dictionary for this, but that's the section we are on in the course and is specifically asked by the assignment. Also no for loops (if that is possible?). Recall that the id can be either a key or a value in the dictionary.
example_dictionary = {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'}
def get_interactions(example_dictionary, id):
output = ''
for j,k in example_dictionary.items():
if j == id:
output = output + k + ' '
if k == id:
output = output + j + ' '
return output
This code works just fine, however it 1) has a for loop (no good) and 2) isn't very pythonic (kind of an eyesore)! How could I use the dictionary more efficiently and condense down my lines? I am in Python 3, Thank you!
Expected result
Having one dictionary and value named wanted, you want to create another dict being copy of
original one with removed all items not having key or value equal to wanted value.
It can be expressed in form of pytest test case with couple of scenarios.
import pytest
scenarios = [
[
# dct
{'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
# wanted
"A",
# expected (result)
{'A': 'C', 'D': 'A'},
],
[
# dct
{'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
# wanted
"E",
# expected (result)
{'R': 'E'},
],
[
# dct
{'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
# wanted
"D",
# expected (result)
{'D': 'A', 'C': 'D'},
],
[
# dct
{'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
# wanted
"nothere",
# expected (result)
{},
],
[
# dct
{'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
# wanted
"A",
# expected (result)
{'A': 'C', 'D': 'A'},
],
]
# replace with real implementation
def get_key_or_val_itms(dct, wanted):
# something comes here
return result
#pytest.mark.parametrize("scenario", scenarios)
def test_it(scenario):
dct, wanted, expected = scenario
assert get_key_or_val_itms(dct, wanted) == expected
Do not bother with anything apart from scenarios. It lists couple of test scenarios with input
dictionary, value for wanted and expected result.
Building stones for the solution
dict.items() - dict to list of tuples
>>> dct = {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'}
>>> dct.items()
[('A', 'C'), ('R', 'E'), ('D', 'A'), ('L', 'R'), ('C', 'D')]
testing membership of a value in a tuple/list
>>> 'A' in ('A', 'C')
True
>>> 'A' in ('R', 'E')
False
Lambda function testing, if wanted is present in a tuple
lambda allows "in place" function definition. It is often used in places,
where some functions expects reference to a function.
First, create named function tuple_wanted
>>> wanted = "A"
>>> def tuple_wanted(tpl):
... return wanted in tpl
and test it (note, that wanted has now value "A"):
>>> tuple_wanted(('A', 'C'))
True
>>> tuple_wanted(('R', 'E'))
False
Now create the function. To play with it, we store the result of lambda in fun:
>>> fun = lambda tpl: wanted in tpl
It can be used in the same manner a tuple_wanted before:
>>> fun(('A', 'C'))
True
>>> fun(('R', 'E'))
False
Later on we will use the result of lambda directly (see filter) without
storing it into any variable.
filter removing all list items not passing some test
filter gets test function and iterable (e.g. list of items) to test by it.
Result of calling filter is list of items from the iterable, which passed the test.
In our case, we want to pass only the tuples, containing wanted value (e.g. "A")
>>> filter(tuple_wanted, dct.items())
[('A', 'C'), ('D', 'A')]
>>> filter(fun, dct.items())
[('A', 'C'), ('D', 'A')]
>>> filter(lambda tpl: wanted in tpl, dct.items())
[('A', 'C'), ('D', 'A')]
Convert list of tuples with 2 items into dictionary
>>> tpllst = [('A', 'C'), ('D', 'A')]
>>> dict(tpllst)
{'A': 'C', 'D': 'A'}
Function doing the work
Long version
This version is here to explain what is going on step by step:
def get_key_or_val_itms(dct, wanted):
# dict as [(key, val), (key2, val2), ...]
tpldct = dct.items()
# find tuples, where either key or val equals `wanted` value
# first make function, which detects, the tuple we search for
def tuple_wanted(tpl):
return wanted in tpl
# now use it to filter only what we search for
restpldct = filter(tuple_wanted, tpldct)
# finally, turn the result into dict
return dict(restpldct)
Short version
def get_key_or_val_itms(dct, wanted):
return dict(filter(lambda tpl: wanted in tpl, dct.items()))
Conclusions
It works (with either long or short version of the function):
>>> dct = {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'}
>>> wanted = "A"
>>> get_key_or_val_itms(dct, wanted)
{'A': 'C', 'D': 'A'}
If you put the function into file with test suite, calling $ py.test -sv the_file.py shall output:
$ py.test -sv the_file.py
py.test================================ test session starts =========================
=======
platform linux2 -- Python 2.7.9, pytest-2.8.7, py-1.4.31, pluggy-0.3.1 -- /home/javl/
.virtualenvs/stack/bin/python2
cachedir: .cache
rootdir: /home/javl/sandbox/stack/dict, inifile:
collected 5 items
countdict.py::test_it[scenario0] PASSED
countdict.py::test_it[scenario1] PASSED
countdict.py::test_it[scenario2] PASSED
countdict.py::test_it[scenario3] PASSED
countdict.py::test_it[scenario4] PASSED
============================= 5 passed in 0.01 seconds ==============================
As can be seen, all the scenarios are passing.
Explanation how py.test works is out of scope of this answer, to learn more about it, see http://pytest.org/latest/
I wouldn't know how to avoid using a for loop, other than making your own for loop, similar to the following:
i = 0
def func(tup, id) {
if i < len(dictionary_items):
output = False
if tup[0] == id or tup[1] == id:
output = id + ' '
i += 1
return output
}
dictionary_items = dictionary.items()
func(dictionary_items[0], id)
func(dictionary_items[1], id)
func(dictionary_items[2], id)
And so on. However, that would be ugly and extremely non-pythonic.
As for making your code more pythonic, you can change the lines output = output + k + ' ' to output += k + ' ' or output = k + ' ' (You're concatenating strings k and ' ' to an empty string, output, which changes nothing about the strings k and ' ').
Furthermore, you could check if j == id or k == id rather than two seperate if statements, then saying output = id + ' ',since if j or k are equal to id, it doesn't matter if you return whichever of j and k is equal to the id or if you return the id itself.
You have to check all the keys and values, so there is always going to be some type of loop. Python has many ways to iterate (ie. loop) through items without explicit use of for.
One good way to iterate through items without for is with the filter, map, and reduce built-in functions along with the lambda syntax for creating small, anonymous functions.
from itertools import chain
# Get values for matching keys and vice versa
values = map(lambda x: x[1] if x[0] == id else None, dct.items())
keys = map(lambda x: x[0] if x[1] == id else None, dct.items())
# Then you filter out the None values
# itertools.chain allows us to conveniently do this in one line
matches = filter(lambda x: x is not None, chain(keys, values))
If you can't use itertools.chain, you'll just need a few extra steps
keys = filter(lambda x: x is not None, keys)
values = filter(lambda x: x is not None, values)
matches = keys + values
If you need a space separated output of values:
output = ' '.join(matches)
You could use list comprehensions, although one could argue that it is a kind of for loop:
example_dictionary = {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'}
def get_interactions(dic, id):
output =[v for k, v in dic.items() if k == id] + [k for k,v in dic.items() if v == id]
return output

Making a dictionary from a list of lists

I have been unable to figure this out, I think the problem might be in the way I am making the list of lists. Can anyone help out? Thanks!
My desired outcome is
codondict = {'A': ['GCT','GCC','GCA','GCG'], 'C': ['TGT','TGC'], &c
but what i get is:
{'A': 'A', 'C': 'C', &c.
Here's my terminal:
A=['GCT','GCC','GCA','GCG']
C=['TGT','TGC']
D=['GAT','GAC']
E=['GAA','GAG']
F=['TTT','TTC']
G=['GGT','GGC','GGA','GGG']
H=['CAT','CAC']
I=['ATT','ATC','ATA']
K=['AAA','AAG']
L=['TTA','TTG','CTT','CTC','CTA','CTG']
M=['ATG']
N=['AAT','AAC']
P=['CCT','CCC','CCA','CCG']
Q=['CAA','CAG']
R=['CGT','CGC','CGA','CGG','AGA','AGG']
S=['TCT','TCC','TCA','TCG','AGT','AGC']
T=['ACT','ACC','ACA','ACG']
V=['GTT','GTC','GTA','GTG']
W=['TGG']
Y=['TAT','TAC']
aminoacids=['A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y']
from collections import defaultdict
codondict=defaultdict(list)
for i in aminoacids:
... for j in i:(ALSO TRIED for j in list(i))
... ... codondict[i]=j
...
codondict
defaultdict(, {'A': 'A', 'C': 'C', 'E': 'E', 'D': 'D', 'G': 'G', 'F': 'F', 'I': 'I', 'H': 'H', 'K': 'K', 'M': 'M', 'L': 'L', 'N': 'N', 'Q': 'Q', 'P': 'P', 'S': 'S', 'R': 'R', 'T': 'T', 'W': 'W', 'V': 'V', 'Y': 'Y'})
You can try this:
condondict= dict(A=['GCT','GCC','GCA','GCG'],
C=['TGT','TGC'],
D=['GAT','GAC'],
E=['GAA','GAG'],
F=['TTT','TTC'],
G=['GGT','GGC','GGA','GGG'],
H=['CAT','CAC'],
I=['ATT','ATC','ATA'],
K=['AAA','AAG'],
L=['TTA','TTG','CTT','CTC','CTA','CTG'],
M=['ATG'],
N=['AAT','AAC'],
P=['CCT','CCC','CCA','CCG'],
Q=['CAA','CAG'],
R=['CGT','CGC','CGA','CGG','AGA','AGG'],
S=['TCT','TCC','TCA','TCG','AGT','AGC'],
T=['ACT','ACC','ACA','ACG'],
V=['GTT','GTC','GTA','GTG'],
W=['TGG'],
Y=['TAT','TAC'])
The reason to use defaultdict() is to allow access/creation of dictionary values without causing a KeyError, or by-pass using the form:
if key not in mydict.keys():
mydict[key] = []
mydict[key].append(something)
If your not creating new keys dynamically, you don't really need to use defaultdict().
Also if your keys already represent the aminoacids, you and just iterate over the keys themselves.
for aminoacid, sequence in condondict.iteritems():
# do stuff with with data...
Another way to do what you need is using the locals() function, which returns a dictionary containing the whole set of variables of the local scope, with the variable names as the keys and its contents as values.
for i in aminoacids:
codondict[i] = locals()[i]
So, you could get the A list, for example, using: locals()['A'].
That's kind of verbose, and is confusing the name of a variable 'A' with its value A. Keeping to what you've got:
aminoacids = { 'A': A, 'C': C, 'D': D ... }
should get you the dictionary you ask for:
{ 'A' : ['GCT', 'GCC', 'GCA', 'GCG'], 'C' : ['TGT', 'TGC'], ... }
where the order of keys 'A' and 'C' may not be what you get back because dictionaries are not ordered.
You can use globals() built-in too, and dict comprehension:
codondict = {k:globals()[k] for k in aminoacids}
it's better to rely on locals() instead of globals(), like stummjr's solution, but you can't do so with dict comprehension directly
codondict = dict([(k,locals()[k]) for k in aminoacids])
However you can do this:
loc = locals()
codondict = {k:loc[k] for k in aminoacids}
If you change dinamically your aminoacids list or the aminoacids assignments, it's better to use something lazier, like:
codondict = lambda: {k:globals()[k] for k in aminoacids}
with this last you can always use the updated dictionary, but it's now a callable, so use codondict()[x] instead of codondict[x] to get an actual dict. This way you can store the entire dict like hist = codondict() in case you need to compare different historical versions of codondict. That's small enough to be useful in interactive modes, but not recommended in bigger codes, though.

Categories

Resources