How to recursively add nodes to n-ary tree in Python - python

So I have an n-ary tree with a root node that I have manually added 5 child nodes too. Each of these children nodes represents a state of its parent that has had some calculations done to its data.
For example:
child1.value = root.value -1, child2.value = root.value +2, child3.value = root.value +1, etc.
Essentially, I want to create a method (recursive generator) that will go to each child, and for each child will create 5 more children for that child with the same calculations done to it, and so on for multiple levels of the tree until one of the children contains data in a desired state.
**Edit to add desired state and calculations**
Basically I am trying to solve the sheep and wolves crossing a river problem.
My values are:
leftsheep = 3
leftwolves = 3
rightsheep = 0
rightwolves = 0
boatposition = 0 (is always 0 when on left side of river)
The desired state should look like so:
leftsheep = 0
leftwolves = 0
rightsheep = 3
rightwolves = 3
boatposition = 1 (is on right side of river, and all animals are moved)
The catch is that when the animals are left alone (the boat is on the opposite side of the river), the number of wolves can never outnumber the number of sheep (or they will eat them).
So for each node, I generate 5 possible nodes from it.
1. leftsheep-1 # 1 sheep is moved from left to right
2. leftwolves-1 # 1 wolf is moved from left to right
3. leftsheep-2 # 2 sheep are moved from left to right
4. leftwolves-2 # 2 wolves are moved from left to right
5. leftsheep-1 and leftwolf-1 # 1 sheep and 1 wolf are moved to the right
and boat+1 # Boat is always 1 when on the right side, 0 on left
Then once the desired node is found, I plan to use a search algorithm (BFS, DFS, etc.) to find the path from that node back to the root.
So far, I have created my Tree like this:
class Node:
def __init__(self, value1, value2, value3, value4, value5):
self.children = []
self.value1 = value1
self.value2 = value2
self.value3 = value3
self.value4 = value4
self.value5 = value5
self.parent = None
And I have a function def build_initial_tree() to build the tree with root node and 5 children. These 5 children are instances of the root with calculations performed on some of their data.
Finally, my recursive method looks like this:
def build_tree(self):
#first check if initial state is goal state:
if self.value4 == initial_value1 and self.value5 == initial_value2:
return self
#If current node has children
elif len(self.children) > 0:
#For each child that node has
for child in self.children:
#add 5 children to each node
child.add_child(child)
#rebuild the tree with each child added
child.build_tree()
#print the tree
return self.print_tree()
#if node has no children, it is a leaf node so just return the tree
else:
return self
It is the for loop in this method that is giving me trouble. If I run as is, the line child.add_child(child) gives me a maximum recursion error (I know why it does this but I am not sure how to fix it or what this line should be). I have tried looking at many examples but they almost all apply to Binary Trees so I am getting confused. I just want to build the tree by adding children to it (with calculations applied to the data) until one of the nodes reaches a desired state (value4 = initial_value1 and value5 = initial_value2).
Should I do the calculations at the same time I add them to the tree? (Checking against constraints and removing/not adding nodes that break these constraints once the calculation is performed), or should I add all calculated nodes first, and then remove unwanted ones during my search algorithm function?

There should not be a recursion depth problem. There can be several reasons why your code that determines the children leads to this problem:
The number of sheep/wolves is allowed to go negative, leading to an infinite repetition of subtraction of animals
A state that was already moved away from is encountered again, but the search continues, and leads to "running in a circle".
...
It would make sense to try to find a shortest path to the solution (fewest boat moves), and then it is more appropriate to use a BFS traversal. Note that you don't actually need the graph as you can perform a BFS immediately. When the target node is found, you can derive the path from that, by walking backwards to the source state.
Here is an implementation:
from collections import namedtuple
# sheep & wolves are the animal counts at the side opposite to the boat:
Node = namedtuple("Node", "sheep, wolves, boat")
Move = namedtuple("Move", "sheep, wolves")
moves = (
Move(1, 0),
Move(2, 0),
Move(0, 1),
Move(0, 2),
Move(1, 1)
)
# Apply a move: the boat travels to the other side taking the given number of animals
# If the move turns out to be invalid: return None
# Else return the new state
def apply(state, move):
newstate = Node(3 - state.sheep - move.sheep, 3 - state.wolves - move.wolves, 1 - state.boat)
if 0 <= newstate.wolves <= (newstate.sheep or 3):
return newstate
def bfs(source, target):
frontier = [source]
camefrom = { source: None }
while frontier:
nextfrontier = []
for state in frontier:
for move in moves:
newstate = apply(state, move)
if newstate is not None and newstate not in camefrom:
if newstate == target:
path = [target]
while state:
path.append(state)
state = camefrom[state]
return path[::-1]
nextfrontier.append(newstate)
camefrom[newstate] = state
frontier = nextfrontier
def stringify(state):
a = f"({state.sheep} sheep, {state.wolves} wolves)"
b = f"({3-state.sheep} sheep, {3-state.wolves} wolves)"
return f"{a}------boat{b}" if state.boat else f"{b}boat------{a}"
path = bfs(Node(0, 0, 0), Node(0, 0, 1))
for state in path:
print(stringify(state))
This outputs this solution:
(3 sheep, 3 wolves)boat------(0 sheep, 0 wolves)
(3 sheep, 1 wolves)------boat(0 sheep, 2 wolves)
(3 sheep, 2 wolves)boat------(0 sheep, 1 wolves)
(3 sheep, 0 wolves)------boat(0 sheep, 3 wolves)
(3 sheep, 1 wolves)boat------(0 sheep, 2 wolves)
(1 sheep, 1 wolves)------boat(2 sheep, 2 wolves)
(1 sheep, 2 wolves)boat------(2 sheep, 1 wolves)
(1 sheep, 0 wolves)------boat(2 sheep, 3 wolves)
(1 sheep, 1 wolves)boat------(2 sheep, 2 wolves)
(0 sheep, 0 wolves)------boat(3 sheep, 3 wolves)
Note that I kept the list as moves as you suggest, but I think there is another move that you should consider: just moving the boat without taking any animals.
So then the list of moves is:
moves = (
Move(0, 0),
Move(1, 0),
Move(2, 0),
Move(0, 1),
Move(0, 2),
Move(1, 1)
)
And the output shows that this gives an advantage. Now the solution needs fewer moves:
(3 sheep, 3 wolves)boat------(0 sheep, 0 wolves)
(3 sheep, 1 wolves)------boat(0 sheep, 2 wolves)
(3 sheep, 1 wolves)boat------(0 sheep, 2 wolves)
(1 sheep, 1 wolves)------boat(2 sheep, 2 wolves)
(1 sheep, 1 wolves)boat------(2 sheep, 2 wolves)
(0 sheep, 0 wolves)------boat(3 sheep, 3 wolves)

Related

Python list comprehension with rockets

I need to print out a list of rockets with their own positions (x and y) using list comprehension (need to use list comprehension for homework).
I have an issue currently when I print the list, it first prints (0,0) for 9 times before it prints the assigned x and y values for each rocket. I am very new to python, so please bear with me.
class Rocket:
## Fill in the rest of the class definition
def __init__ (self, x=0,y=0):
self.x = x
self.y = y
def moveup(self):
self.y += 1
rocket = Rocket(0,0)
print("Starting position is " , rocket.x, rocket.y)
direction = input("What direction do you want to go?")
if direction == "up":
print("you went up")
rocket.moveup()
print("Position: ", rocket.x, rocket.y)
else:
print("Nothing happened")
#-------------------------------------------------------------------------------------
rockets = [Rocket() for x in range(0,10)]
rockets.append(Rocket(0,0))
rockets.append(Rocket(1,1))
rockets.append(Rocket(2,2))
rockets.append(Rocket(3,3))
rockets.append(Rocket(4,4))
rockets.append(Rocket(5,5))
rockets.append(Rocket(6,6))
rockets.append(Rocket(7,7))
rockets.append(Rocket(8,8))
rockets.append(Rocket(9,9))
for rocket in rockets:
print(rocket.x,rocket.y)
my output is :
Starting position is 0 0
What direction do you want to go?up
you went up
Position: 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 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
I think I partially know what is going on, when I first make the list it makes, each rocket 0,0 and when I append, it creates a new rocket instead of editing them. I guess my main question is how do I edit an index of the array to correct this?
You don't need the list comprehension and the appends. Just initialize the list with the rockets you want in the list comprehension
rockets = [Rocket(i, i) for i in range(10)]
please observe that when calling
rockets = [Rocket() for x in range(0,10)]
you create 10 instances of class Rocket and put them on the list rockets. Call is supplied with no parameters an thus default values are used as defined in constructor (Rocket: ... def __init__ (self, x=0,y=0)).
This is equivalent to calling rockets.append(Rocket(0, 0)) 10 times. Then you create the remaining 10 by hand
rockets.append(Rocket(0,0))
...
rockets.append(Rocket(9,9))
so the output is justified
and when I append, it creates a new rocket instead of editing them
That`s exactly what it does .You may want to use array subscription to get the object from an array to use its methods or edit its properties
e.g.
for r in rockets:
r.moveup()
for r in rockets:
r.x = r.y*r.y

Recursive function return value without allocating extra space?

So I'm stuck at trying to optimize this LeetCode problem called Frog Jump. Here's the basic description of the problem:
Given a list of stones' positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit.
If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction.
e.g: [0,1,3,5,6,8,12,17]
There are a total of 8 stones. The first stone at the 0th unit, second
stone at the 1st unit, third stone at the 3rd unit, and so on... The
last stone at the 17th unit.
Return true. The frog can jump to the last stone by jumping 1 unit
to the 2nd stone, then 2 units to the 3rd stone, then 2 units to the
4th stone, then 3 units to the 6th stone, 4 units to the 7th stone,
and 5 units to the 8th stone.
Here's my solution which works. What I need help with is how to output the same boolean without allocating the extra res array which stores the logical OR of all the explored paths using DFS.
class Solution:
def canCross(self, stones: List[int]) -> bool:
if stones[1] != 1:
return False
res = []
memo = {}
def dfs(curr_stone, last_stone, last_jump):
if curr_stone == last_stone:
res.append(True)
return
if curr_stone > last_stone:
res.append(False)
return
for i in range(-1,2):
next_stone = curr_stone + (last_jump + i)
if next_stone in stones and next_stone > curr_stone and (next_stone, last_stone, last_jump+i) not in memo:
memo[(next_stone, last_stone, last_jump+i)] = 1
dfs(next_stone, last_stone, last_jump+i)
dfs(1, stones[-1], 1)
return any(res)
Can someone help me with how to approach these questions? I always struggle a lot with these sort of questions and end up storing values in an array; however, ideally I would like the result of the recursive code to be the same without allocating the extra res array space.
Since the entire purpose of the function seems to boil down to return any(res), it seems like you should return True/False from the recursive function instead of appending them, then exit from all recursive calls once a single True value is found, and not bother saving every found value.
That will involve checking what was returned from the recursive call dfs(next_stone, last_stone, last_jump+i), and if it's true, simply returning that True:
from typing import List
class Solution:
def canCross(self, stones: List[int]) -> bool:
if stones[1] != 1:
return False
memo = {}
def dfs(curr_stone, last_stone, last_jump):
if curr_stone == last_stone:
return True # Return the results instead of appending them to a list
if curr_stone > last_stone:
return False
for i in range(-1, 2):
next_stone = curr_stone + (last_jump + i)
if next_stone in stones and next_stone > curr_stone and (next_stone, last_stone, last_jump + i) not in memo:
memo[(next_stone, last_stone, last_jump + i)] = 1
rec_result = dfs(next_stone, last_stone, last_jump + i)
if rec_result: # Then check the recursive results at the call-site
return True
return dfs(1, stones[-1], 1)
I'll note, I haven't done extensive testing on this, but from some quick "head interpretation", it seems to be equivalent.

Can someone explain how the root function works in this quick union implementation?

I am trying to implement Quick Union algorithm where ids of the nodes are their root nodes.
id is implemented like this:
self.id = []
for i in range(N)
self.id.append(i)
Which stores the index of the nodes 0 to N - 1
This is the code that I can't understand:
def root(self, i):
while(i != self.id[i]):
i = self.id[i]
return i
What is happening? what is "i"? how does it point to the root node?
I understand the union method:
def union(self, p, q):
i = self.root(p)
j = self.root(q)
self.id[i] = j
Which means something like: change the id, where the value is root of p, to root of q, right?
id[i] points to the parent of i-th element. If this element is not a root (root id points to itself), then code moves to the parent, then to the parent of parent and so on until reaches a root.
Example: we want to find a root of 0-th element
i 0 1 2 3
\/ ----->
id 2 1 1 3
id[0] is not equal to 0, so this element is leaf, we make a step to it's parent 2
i 0 1 2 3
<---\/
id 2 1 1 3
id[2] is not equal to 2, so this element is leaf, we make a step to it's parent 1
i 0 1 2 3
\/
id 2 1 1 3
id[1] is equal to 1, so we have found root fo the 0-th element
Trees stored in that array:
1 3
/
2
\
0

Kosaraju's Algorithm for SCCs, non-recursive

I have an implementation of Kosaraju's algorithm for finding SCCs in Python. The code below contains a recursive (fine on the small test cases) version and a non-recursive one (which I ultimately need because of the size of the real dataset).
I have run both the recursive and non-recursive version on a few test datasets and get the correct answer. However running it on the much larger dataset that I ultimately need to use, produces the wrong result. Going through the real data is not really an option because it contains nearly a million nodes.
My problem is that I don't know how to proceed from here. My suspision is that I either forgot a certain case of graph constellation in my test cases, or that I have a more fundamental misunderstanding about how this algo is supposed to work.
#!/usr/bin/env python3
import heapq
class Node():
"""A class to represent nodes in a DirectedGraph. It has attributes for
performing DFS."""
def __init__(self, i):
self.id = i
self.edges = []
self.rev_edges = []
self.explored = False
self.fin_time = 0
self.leader = 0
def add_edge(self, edge_id):
self.edges.append(edge_id)
def add_rev_edge(self, edge_id):
self.rev_edges.append(edge_id)
def mark_explored(self):
self.explored = True
def set_leader(self, leader_id):
self.leader = leader_id
def set_fin_time(self, fin_time):
self.fin_time = fin_time
class DirectedGraph():
"""A class to represent directed graphs via the adjacency list approach.
Each dictionary entry is a Node."""
def __init__(self, length, list_of_edges):
self.nodes = {}
self.nodes_by_fin_time = {}
self.length = length
self.fin_time = 1 # counter for the finishing time
self.leader_count = 0 # counter for the size of leader nodes
self.scc_heapq = [] # heapq to store the ssc by size
self.sccs_computed = False
for n in range(1, length + 1):
self.nodes[str(n)] = Node(str(n))
for n in list_of_edges:
ns = n[0].split(' ')
self.nodes[ns[0]].add_edge(ns[1])
self.nodes[ns[1]].add_rev_edge(ns[0])
def n_largest_sccs(self, n):
if not self.sccs_computed:
self.compute_sccs()
return heapq.nlargest(n, self.scc_heapq)
def compute_sccs(self):
"""First compute the finishing times and the resulting order of nodes
via a DFS loop. Second use that new order to compute the SCCs and order
them by their size."""
# Go through the given graph in reverse order, computing the finishing
# times of each node, and create a second graph that uses the finishing
# times as the IDs.
i = self.length
while i > 0:
node = self.nodes[str(i)]
if not node.explored:
self.dfs_fin_times(str(i))
i -= 1
# Populate the edges of the nodes_by_fin_time
for n in self.nodes.values():
for e in n.edges:
e_head_fin_time = self.nodes[e].fin_time
self.nodes_by_fin_time[n.fin_time].add_edge(e_head_fin_time)
# Use the nodes ordered by finishing times to calculate the SCCs.
i = self.length
while i > 0:
self.leader_count = 0
node = self.nodes_by_fin_time[str(i)]
if not node.explored:
self.dfs_leaders(str(i))
heapq.heappush(self.scc_heapq, (self.leader_count, node.id))
i -= 1
self.sccs_computed = True
def dfs_fin_times(self, start_node_id):
stack = [self.nodes[start_node_id]]
# Perform depth-first search along the reversed edges of a directed
# graph. While doing this populate the finishing times of the nodes
# and create a new graph from those nodes that uses the finishing times
# for indexing instead of the original IDs.
while len(stack) > 0:
curr_node = stack[-1]
explored_rev_edges = 0
curr_node.mark_explored()
for e in curr_node.rev_edges:
rev_edge_head = self.nodes[e]
# If the head of the rev_edge has already been explored, ignore
if rev_edge_head.explored:
explored_rev_edges += 1
continue
else:
stack.append(rev_edge_head)
# If the current node has no valid, unexplored outgoing reverse
# edges, pop it from the stack, populate the fin time, and add it
# to the new graph.
if len(curr_node.rev_edges) - explored_rev_edges == 0:
sink_node = stack.pop()
# The fin time is 0 if that node has not received a fin time.
# Prevents dealing with the same node twice here.
if sink_node and sink_node.fin_time == 0:
sink_node.set_fin_time(str(self.fin_time))
self.nodes_by_fin_time[str(self.fin_time)] = \
Node(str(self.fin_time))
self.fin_time += 1
def dfs_leaders(self, start_node_id):
stack = [self.nodes_by_fin_time[start_node_id]]
while len(stack) > 0:
curr_node = stack.pop()
curr_node.mark_explored()
self.leader_count += 1
for e in curr_node.edges:
if not self.nodes_by_fin_time[e].explored:
stack.append(self.nodes_by_fin_time[e])
###### Recursive verions below ###################################
def dfs_fin_times_rec(self, start_node_id):
curr_node = self.nodes[start_node_id]
curr_node.mark_explored()
for e in curr_node.rev_edges:
if not self.nodes[e].explored:
self.dfs_fin_times_rec(e)
curr_node.set_fin_time(str(self.fin_time))
self.nodes_by_fin_time[str(self.fin_time)] = Node(str(self.fin_time))
self.fin_time += 1
def dfs_leaders_rec(self, start_node_id):
curr_node = self.nodes_by_fin_time[start_node_id]
curr_node.mark_explored()
for e in curr_node.edges:
if not self.nodes_by_fin_time[e].explored:
self.dfs_leaders_rec(e)
self.leader_count += 1
To run:
#!/usr/bin/env python3
import utils
from graphs import scc_computation
# data = utils.load_tab_delimited_file('data/SCC.txt')
data = utils.load_tab_delimited_file('data/SCC_5.txt')
# g = scc_computation.DirectedGraph(875714, data)
g = scc_computation.DirectedGraph(11, data)
g.compute_sccs()
# for e, v in g.nodes.items():
# print(e, v.fin_time)
# for e, v in g.nodes_by_fin_time.items():
# print(e, v.edges)
print(g.n_largest_sccs(20))
Most complex test case (SCC_5.txt):
1 5
1 4
2 3
2 11
2 6
3 7
4 2
4 8
4 10
5 7
5 5
5 3
6 8
6 11
7 9
8 2
8 8
9 3
10 1
11 9
11 6
Drawing of that test case: https://imgur.com/a/LA3ObpN
This produces 4 SCCs:
Bottom: Size 4, nodes 2, 8, 6, 11
Left: Size 3, nodes 1, 10, 4
Top: Size 1, node 5
Right: Size 3, nodes 7, 3, 9
Ok, I figured out the missing cases. The algorithm wasn't performing correctly on very strongly connected graphs and duplicated edges. Here is an adjusted version of the test case I posted above with a duplicated edge and more edges to turn the whole graph into one big SCC.
1 5
1 4
2 3
2 6
2 11
3 2
3 7
4 2
4 8
4 10
5 1
5 3
5 5
5 7
6 8
7 9
8 2
8 2
8 4
8 8
9 3
10 1
11 9
11 6

Tower of Hanoi recursive algorithm for four towers in Python

I'm having a decent amount of trouble implementing the optimal algorithm for the tower of hanoi game for four stools in python.
First, in class we solved the tower of hanoi algorithm for three towers, for which we were given the following algorithm:
Move all but the bottom disk from source to intermediate tower
Move the bottom disk form the source to the destination
Move all but the bottom disk from intermediate stool to destination tower
The actual code for this algorithm (with model being of class Model which has towers and disks, with a move method that moves disks from one tower to another) yields:
def move_disks(n, source, intermediate, destination):
"""Move n disks from source to destination
#param int n:
#param int source:
#param int intermediate:
#param int destination:
#rtype: None
"""
if n > 1:
move_disks(n - 1, source, destination, intermediate)
move_disks(1, source, intermediate, destination)
move_disks(n - 1, intermediate, source, destination)
else:
model.move(source, destination)
Now for four stools, I'm given the following:
Move n- i disks to intermediate stool using all four towers
Move i disks from the original tower to the destination tower, using only three available towers
Move n-i smallest disks from intermediate tower to destination tower
Manually playing around with disks and towers I got n-i = 2 if n>=3 and i = 1 if n =2. Since there are 4 possible sources and destinations, in my own function I have 5 arguments instead of 4:
def move_disks(n, source, intermediate, intermediate2, destination):
"""Move n disks from source to destination
#param int n:
#param int source:
#param int intermediate:
#param int intermediate2:
#param int destination:
#rtype: None
"""
if n > 1:
move_disks(n - i, source, intermediate 2 destination, intermediate)
move_disks(1, source, intermediate, intermediate2, destination)
move_disks(n - i, intermediate, intermediate2, source, destination)
else:
print("{} -> {}".format(source,destination)) # used to explicitly follow output in the console
#model.move(source, destination) --> to be implemented when the function returns the right output
When I run it for n=3 I get:
1 -> 3
1 -> 2
3 -> 2
1 -> 4
2 -> 3
2 -> 4
3 -> 4
which gives the same number as moves as the solution for three towers.
The optimal solution for four stools should yield:
1 -> 3
1 -> 2
1 -> 4
2 -> 4
3 -> 4
The problem is definitely coming from my understanding of the algorithm so I've tried tracing function calls on a dry erasable board for a few hours to no avail.
Any tips or tricks for what else I should be doing or looking for to solve this algorithm? I'm honestly lost and a little bit discouraged.
I see two issues with your code.
The first is that you're using a variable i without defining it in the function. It probably has a global value in your environment, but perhaps not an appropriate one for the given n. You should probably have your function figure out what i should be and assign it as a local variable.
The second issue is that you're always recursing using the same four-tower function, while the algorithm you describe is supposed to use only three towers in the middle step. This probably means you should keep around your original function (in the first code block), and use a different name for your four-tower function.
If we name your first function move_disks3 and the second move_disks4, we can make it work:
def move_disks3(n, source, intermediate, destination):
"""Move n disks from source to destination
#param int n:
#param int source:
#param int intermediate:
#param int destination:
#rtype: None
"""
if n > 1:
move_disks3(n - 1, source, destination, intermediate)
move_disks3(1, source, intermediate, destination)
move_disks3(n - 1, intermediate, source, destination)
else:
print("{} -> {}".format(source,destination))
def move_disks4(n, source, intermediate, intermediate2, destination):
"""Move n disks from source to destination
#param int n:
#param int source:
#param int intermediate:
#param int intermediate2:
#param int destination:
#rtype: None
"""
if n > 1:
if n > 2: # I'm not sure this picks the optimal i in all cases, but it does for n=3
i = 2
else:
i = 1
move_disks4(n - i, source, intermediate2, destination, intermediate)
move_disks3(i, source, intermediate2, destination)
move_disks4(n - i, intermediate, intermediate2, source, destination)
else:
print("{} -> {}".format(source,destination))
I'm not certain I understood your statements about the optimal i value, so you might need to make a few changes if I got something wrong there. But this code does work, and does give the desired results for n = 3 (and plausible results for a few higher n values I tested):
>>> move_disks4(3, 1, 2, 3, 4)
1 -> 2
1 -> 3
1 -> 4
3 -> 4
2 -> 4
def move4Poles(begin, end, temp1, temp2, disks):
if disks == 1:
print(begin, "to", end)
return
if disks == 2:
move4Poles(begin, temp1, '_', '_', 1)
move4Poles(begin, end, '_', '_', 1)
move4Poles(temp1, end, '_', '_', 1)
return
if disks >= 3:
move4Poles(begin, temp2, temp1, end, disks - 2)
move4Poles(begin, end, temp1, '_', 2)
move4Poles(temp2, end, temp1, begin, disks - 2)
move4Poles('A', 'D', 'B', 'C', 4)
A to B
A to C
B to C
A to B
A to D
B to D
C to B
C to D
B to D

Categories

Resources